Some checks failed
Test and deploy staging / staging (push) Has been cancelled
182 lines
6.0 KiB
Svelte
Executable File
182 lines
6.0 KiB
Svelte
Executable File
<script lang="ts">
|
||
import { resolve } from '$app/paths';
|
||
import portrait from '$lib/assets/stan.jpg?url';
|
||
import ProgressBar from '$lib/components/ProgressBar.svelte';
|
||
import type { PageProps } from './$types';
|
||
|
||
let { data }: PageProps = $props();
|
||
|
||
const projects = [
|
||
{
|
||
name: 'vash esports',
|
||
subtitle: 'the best way to play osu!',
|
||
href: 'https://esports.vash.software',
|
||
external: true
|
||
},
|
||
{
|
||
name: 'catchemall',
|
||
subtitle: 'catch 1025 pokemon across 53 games',
|
||
href: '/projects/catch-em-all',
|
||
external: false
|
||
},
|
||
{
|
||
name: 'animaldex',
|
||
subtitle: 'add neighborhood pets to the world map 😼',
|
||
href: '/projects/animaldex',
|
||
external: false
|
||
},
|
||
{
|
||
name: 'osu! map finder',
|
||
subtitle: 'find osu! maps for Spotify songs/albums/playlists',
|
||
href: '/projects/spotify-beatmap-finder',
|
||
external: false
|
||
},
|
||
{
|
||
name: 'derby lens',
|
||
subtitle: 'estimate odds for horse betting',
|
||
href: '/projects/derby',
|
||
external: false
|
||
}
|
||
] as const;
|
||
const experience = [
|
||
{ when: '2026–now', role: 'associate software engineer', place: 'Cargill' },
|
||
{
|
||
when: '2021–2026',
|
||
role: 'BSc computer science · software engineering',
|
||
place: 'The Hague University of Applied Sciences'
|
||
},
|
||
{ when: '2026', role: 'software engineering intern', place: '3webapps' },
|
||
{ when: '2023', role: 'software engineering intern', place: 'h5mag' },
|
||
{ when: '2021–2023', role: 'junior IT specialist', place: 'Finance Plus' }
|
||
] as const;
|
||
</script>
|
||
|
||
<svelte:head>
|
||
<link rel="preload" as="image" href={portrait} fetchpriority="high" />
|
||
</svelte:head>
|
||
|
||
<div class="mx-auto max-w-6xl px-4 sm:px-6">
|
||
<section
|
||
class="grid items-center gap-8 pt-7 pb-12 sm:py-20 md:grid-cols-[minmax(0,1fr)_minmax(0,320px)] md:gap-16"
|
||
>
|
||
<div class="min-w-0 text-center md:text-left">
|
||
<h1 class="text-5xl font-semibold tracking-[-0.055em] sm:text-6xl lg:text-7xl">stan runge</h1>
|
||
<nav
|
||
class="mt-7 grid grid-cols-2 justify-items-center gap-x-4 gap-y-3 text-base text-[var(--site-muted)] sm:flex sm:flex-wrap sm:justify-center sm:gap-x-6 md:justify-start"
|
||
aria-label="Find me online"
|
||
>
|
||
<a
|
||
href="https://git.stanrunge.dev/stan"
|
||
rel="external"
|
||
class="hover:text-[var(--site-text)]">git ↗</a
|
||
>
|
||
<a href="https://x.com/stanrunge" rel="external" class="hover:text-[var(--site-text)]"
|
||
>twitter ↗</a
|
||
>
|
||
<a href="https://esports.vash.software" rel="external" class="hover:text-[var(--site-text)]"
|
||
>vash esports ↗</a
|
||
>
|
||
<a href="mailto:stan@stanrunge.dev" class="hover:text-[var(--site-text)]">email ↗</a>
|
||
</nav>
|
||
</div>
|
||
<img
|
||
src={portrait}
|
||
alt="Stan at his desk"
|
||
width="760"
|
||
height="746"
|
||
loading="eager"
|
||
decoding="sync"
|
||
fetchpriority="high"
|
||
class="aspect-square w-full max-w-80 justify-self-center rounded-md bg-[var(--site-surface)] object-cover md:justify-self-end"
|
||
/>
|
||
</section>
|
||
|
||
<section aria-labelledby="projects-heading" class="border-t border-[var(--site-border)] pt-8">
|
||
<h2 id="projects-heading" class="mb-4 text-2xl font-semibold tracking-tight">
|
||
<a href={resolve('/projects')} class="hover:text-[var(--site-muted)]">projects ↗</a>
|
||
</h2>
|
||
<ul>
|
||
{#each projects as project (project.name)}
|
||
<li class="border-b border-[var(--site-border)] text-lg">
|
||
{#if project.external}
|
||
<a
|
||
href={project.href}
|
||
rel="external"
|
||
class="flex items-center justify-between gap-4 py-4 hover:text-[var(--site-muted)]"
|
||
>
|
||
<span class="min-w-0">
|
||
<span class="block">{project.name}</span>
|
||
<span class="mt-1 block text-base text-[var(--site-muted)]">{project.subtitle}</span
|
||
>
|
||
</span><span aria-hidden="true" class="shrink-0 text-[var(--site-muted)]">↗</span>
|
||
</a>
|
||
{:else}
|
||
<a
|
||
href={resolve(project.href)}
|
||
class="flex items-center justify-between gap-4 py-4 hover:text-[var(--site-muted)]"
|
||
>
|
||
<span class="min-w-0">
|
||
<span class="block">{project.name}</span>
|
||
<span class="mt-1 block text-base text-[var(--site-muted)]">{project.subtitle}</span
|
||
>
|
||
</span><span aria-hidden="true" class="shrink-0 text-[var(--site-muted)]">↗</span>
|
||
</a>
|
||
{/if}
|
||
</li>
|
||
{/each}
|
||
</ul>
|
||
</section>
|
||
|
||
<section aria-labelledby="lists-heading" class="pt-14 sm:pt-20">
|
||
<h2 id="lists-heading" class="mb-4 text-2xl font-semibold tracking-tight">
|
||
<a href={resolve('/lists')} class="hover:text-[var(--site-muted)]">lists ↗</a>
|
||
</h2>
|
||
<ul>
|
||
{#each data.lists as list (list.id)}
|
||
<li class="border-b border-[var(--site-border)] text-lg">
|
||
<a
|
||
href={resolve('/lists/[slug]', { slug: list.slug })}
|
||
class="block py-4 hover:text-[var(--site-muted)]"
|
||
>
|
||
<span class="flex flex-wrap items-center justify-between gap-x-6 gap-y-1">
|
||
<span>{list.name}</span>
|
||
<span class="text-base text-[var(--site-muted)]">
|
||
{#if list.isProgress && data.user}
|
||
{list.total ? Math.round((list.checked / list.total) * 100) : 0}% · {list.checked}/{list.total}
|
||
{:else}
|
||
{list.total} {list.total === 1 ? 'item' : 'items'}
|
||
{/if} ↗
|
||
</span>
|
||
</span>
|
||
{#if list.isProgress}<span class="mt-3 block"
|
||
><ProgressBar
|
||
value={list.checked}
|
||
max={list.total}
|
||
label={`Progress on ${list.name}`}
|
||
small
|
||
/></span
|
||
>{/if}
|
||
</a>
|
||
</li>
|
||
{/each}
|
||
</ul>
|
||
</section>
|
||
|
||
<section aria-labelledby="experience-heading" class="pt-14 sm:pt-20">
|
||
<h2 id="experience-heading" class="mb-4 text-2xl font-semibold tracking-tight">experience</h2>
|
||
<ol>
|
||
{#each experience as item (item.when + item.place)}
|
||
<li
|
||
class="grid gap-1 border-t border-[var(--site-border)] py-4 sm:grid-cols-[minmax(0,1fr)_130px] sm:gap-6"
|
||
>
|
||
<div class="min-w-0">
|
||
<h3 class="font-medium">{item.role}</h3>
|
||
<p class="mt-1 text-base text-[var(--site-muted)]">{item.place}</p>
|
||
</div>
|
||
<span class="text-base text-[var(--site-muted)] sm:text-right">{item.when}</span>
|
||
</li>
|
||
{/each}
|
||
</ol>
|
||
</section>
|
||
</div>
|