# Personal Blog Rewrite Plan

## Goal

Replace the existing portfolio with a statically generated personal blog that still helps with hiring, but earns that trust through writing, project essays, and technical evidence instead of a conventional portfolio timeline.

The site should feel current, minimal, and carefully made. It should not imitate old plain-HTML blogs, but it should learn from why many respected technical blogs work: durable URLs, direct navigation, readable typography, fast pages, RSS, and content that carries the authority.

This plan incorporates the rendered review of 100 CS/technical blogs and a 10-lens review pass covering content, visual design, mobile ergonomics, accessibility, performance, information architecture, implementation risk, hiring signal, typography, and content migration.

## Inputs

### Existing Repo

The current repo is a custom TypeScript/Webpack single-page portfolio. The strongest reusable material is in:

- `src/data/projects/*.ts`: project titles, date strings, summaries, longer paragraphs, media references, external links
- `src/data/media`: screenshots, posters, videos, CV, BSc/MSc thesis PDFs
- `static/no-change`: favicons, `robots.txt`, 404 page, Open Graph image

The current implementation should be replaced rather than evolved. It depends on JavaScript for core content and is organized around timeline cards, expanded details, image viewers, theme controls, and decorative UI. The new site should ship little to no JavaScript and render normal pages as static HTML.

### Rendered Blog Research

I installed Playwright and rendered 100 technical/personal CS blogs at desktop and phone widths. The artifacts are in:

- `/tmp/codex-blog-design-pass/index.html`
- `/tmp/codex-blog-design-pass/summary.json`
- `/tmp/codex-blog-design-pass/summary.csv`

Useful metrics from the pass:

- Median desktop paragraph width: about `662px`
- Median mobile paragraph width: about `350px`
- Median paragraph font size: `16px`
- 73 of 100 exposed RSS or Atom
- Most strong personal blogs used very few card-like UI elements
- Several famous blogs had mobile overflow, which this site should explicitly avoid
- The best visual references used media as explanation, not decoration

Representative references:

- Writing-first archive: [Simon Willison](https://simonwillison.net/), [Marc Brooker](https://brooker.co.za/blog/), [Brandur](https://brandur.org/articles), [Armin Ronacher](https://lucumr.pocoo.org/), [Phil Eaton](https://notes.eatonphil.com/)
- Minimal but current personal sites: [Tom MacWright](https://macwright.com/), [Thorsten Ball](https://thorstenball.com/), [matklad](https://matklad.github.io/), [BurntSushi](https://burntsushi.net/)
- Technical visual writing: [Red Blob Games](https://www.redblobgames.com/), [Distill](https://distill.pub/), [Bret Victor](https://worrydream.com/), [Jay Alammar](https://jalammar.github.io/), [fasterthanli.me](https://fasterthanli.me/)
- Strong code-heavy writing: [Ned Batchelder](https://nedbatchelder.com/blog/), [Dave Cheney](https://dave.cheney.net/), [Eli Bendersky](https://eli.thegreenplace.net/), [Laurence Tratt](https://tratt.net/laurie/blog/)

## Product Direction

The site should be a blog first and a hiring surface second.

The homepage should not say "hire me" first. It should establish a serious technical writer and builder through the quality of the archive. Recruiters should still quickly find the About page, CV, GitHub, LinkedIn, contact details, and the most relevant project essays.

The tone should be:

- Text-first
- Calm
- Precise
- Modern
- Functional
- Slightly editorial
- Never flashy
- Never intentionally dated

Avoid:

- Purple or blue-purple gradients
- Glowing blobs
- Timeline cards
- Hero illustrations
- Glassmorphism
- Decorative animations
- Dense nav chrome
- Flat boxes pretending to be a "retro" blog
- Landing-page copy
- Skill grids, logo walls, metric blocks, or resume-card layouts

## Key Decisions

- Canonical essays live under `/writing/[slug]/`, including project essays.
- `/projects/` is a complete text index of built work with stable anchors; it is not a second set of duplicate project detail pages.
- Tags are Phase 1 metadata but tag pages can wait.
- The visual system uses a refined sans-serif voice, not Times New Roman and not browser defaults.
- Normal pages ship no client JavaScript. Any exception must be post-specific and justified.
- Images that need optimization live in `src/assets` and render through Astro image tooling. `public/` is for stable raw files such as PDFs, favicons, legacy assets, and videos/downloads.
- The deployment plan must match the existing Forgejo workflow that builds `dist/` and rsyncs it to `/pages/schmelczer-dev`.

## Design Principles

### Function Includes Reading

Minimal design is not an excuse for weak typography. The site should feel better on a phone than most personal blogs.

Core reading requirements:

- No horizontal scrolling at `320px`, `390px`, or `430px`
- Prose width around `36rem-42rem` on desktop
- Mobile content width: `min(100% - 2 * var(--gutter), var(--measure))`
- Mobile gutter: `clamp(16px, 5vw, 24px)`
- Body font size: `17px` on phones, `17px-18px` on larger screens
- Prose line height: `1.62-1.68`
- Paragraph spacing tuned for long reading, not marketing skimming
- Code blocks scroll horizontally only inside the code block
- Figures, tables, long links, and code never force page overflow
- Header nav, footer links, RSS/contact links, project links, footnotes, backlinks, and pagination use a `44px` minimum hit area or equivalent padding

### Typography

Use a refined sans-serif as the main voice. Do not use Times New Roman. Do not make the site look like default browser HTML.

Recommended direction:

- Body and UI: `Source Sans 3` first, then `Inter`, then the system sans stack
- Code: `IBM Plex Mono`, `JetBrains Mono`, or a careful system monospace stack
- No custom display font
- No negative letter-spacing
- Use font weights sparingly: regular, medium, semibold

Default recommendation:

```css
font-family:
  'Source Sans 3',
  Inter,
  ui-sans-serif,
  system-ui,
  -apple-system,
  BlinkMacSystemFont,
  'Segoe UI',
  sans-serif;
```

Font loading policy:

- Start with the system stack or one self-hosted variable WOFF2 for `Source Sans 3`
- Use only regular-to-semibold weights
- Use `font-display: swap`
- Preload only the single above-fold face if measurement proves it helps
- Avoid third-party font requests

Editorial type targets:

- Body: `17px`, line-height `1.65`
- Article `h1`: about `2rem` on phones, restrained on desktop, line-height `1.15-1.2`
- `h2`: line-height `1.2-1.25`, with generous space above
- Metadata and captions: `14px-15px`, line-height `1.35-1.45`
- Code: about `.88em`, line-height `1.55`

### Color

Use a quiet near-white background and near-black text. Let links and small metadata carry the color.

Candidate palette:

- Background: `#fbfaf7`
- Text: `#181817`
- Muted text: `#6b6860`
- Rule/border: `#d9d5ca`
- Strong rule/focus: `#7f7668`
- Link: `#285f74`
- Link hover: `#8a4b2f`
- Code background: `#efede6`
- Callout background: `#f4f1e8`

The palette should feel warm and literary without becoming beige-heavy. The teal link is the only real accent. Purple is out.

Low-contrast tokens such as `--rule`, `--code-bg`, and `--callout-bg` must not be the only focus, active, or error indicator.

### Link Treatment

Prose links should look like links.

```css
.prose a {
  color: var(--link);
  text-decoration-line: underline;
  text-decoration-thickness: 0.08em;
  text-underline-offset: 0.18em;
}
```

Hover can shift color. Keyboard focus must use a visible `2px+` outline with offset. Do not remove outlines.

### Layout

Use a simple centered layout with a narrow content column and a slightly wider media column.

Desktop:

- Page max width around `72rem`
- Main article column around `40rem`
- Figures can break out to `56rem-64rem` when useful
- Header aligned with content, not full-width heavy navigation
- Footer minimal

Mobile:

- Single column
- Header wraps naturally; no menu JavaScript
- Nav uses `flex-wrap: wrap`, small gaps, and no pill labels that can force overflow
- Site title and nav may become two short rows
- Article title should not dominate the whole first viewport
- First paragraph should appear without excessive scrolling
- Metadata should be visible but quiet

### UI Shape

Use rules, whitespace, indentation, and typographic hierarchy instead of cards.

Default primitives:

- Thin horizontal rules
- Muted metadata
- Hanging dates on desktop where appropriate
- Indented summaries
- Captions
- Narrow dividers
- Text rows for archive/project entries

Acceptable framed elements:

- Code blocks
- Tables
- Figures with captions
- Callouts for short notes
- A small "At a glance" metadata block inside project essays when it is content, not decoration

Avoid using cards for every post preview. Archive entries should mostly be text rows with date, title, tags, and one sentence.

### Figures, Tables, and Code

Code blocks:

- `overflow-x: auto`
- `max-width: 100%`
- `-webkit-overflow-scrolling: touch`
- No wrapping by default
- Mobile code font around `13px-14px`
- `padding: 1rem`
- `border: 1px solid var(--rule)`
- `border-radius: 6px`
- No box shadow

Tables:

- Small tables stay full width
- Wide tables live inside an overflow container
- Use tabular numerals where useful
- Use subtle row rules
- Avoid zebra striping unless the data genuinely needs it

Figures:

- `margin-block: 2rem`
- Captions in muted text at `14px-15px`
- `img`, `video`, `canvas`, and `svg` use `max-width: 100%; height: auto`
- Optional `border: 1px solid var(--rule)` for screenshots
- Wide figures only above tablet widths
- Avoid `width: 100vw`; use `max-inline-size: min(100%, var(--measure-wide))`

### Media

Media should be evidence.

Good uses:

- A screenshot showing the UI state being discussed
- A short video demonstrating an engine, simulation, or interaction
- A diagram that explains architecture or data flow
- A small inline image that makes a project concrete

Bad uses:

- Decorative thumbnails for every post
- Hero images
- Cropped atmospheric images
- Large videos above the fold
- Autoplay

For project posts, use one strong media item near the top only when it immediately clarifies the subject. Prefer "evidence figure" or "lead figure" language, not "hero."

Videos:

- Never load videos in archive/listing pages
- Use posters
- Set explicit dimensions
- Use `preload="metadata"` or `preload="none"`
- Provide captions, transcript, or a text summary when the video carries meaning
- Compress large MP4/WebM files before publishing
- Set a per-page media budget before launch

## Information Architecture

### Routes

```text
/
/writing/
/writing/[slug]/
/projects/
/about/
/rss.xml
/sitemap-index.xml
```

Optional later:

```text
/notes/
/tags/[tag]/
/now/
```

Project essays are canonical writing posts. The project index links to those posts and to demos/source/packages/papers. Do not create duplicate `/projects/[slug]/` pages in the first version; use stable anchors such as `/projects/#ad-astra` for smaller items if needed.

### Navigation

Persistent header:

- `Writing`
- `Projects`
- `About`
- `RSS`

Footer:

- Email
- CV
- GitHub
- LinkedIn
- RSS

Keep the header compact and text-only. Put dense hiring links in the About page and footer rather than in the primary nav.

### Homepage

Purpose: establish an editorial surface first, then make the best built work easy to find.

Structure:

1. Compact intro
2. Latest writing
3. Selected project essays
4. Selected technical work
5. Footer with RSS/contact links

Example intro direction:

> I'm Andras Schmelczer. I write about building software systems, AI deployment, graphics, simulations, and tools.

No large hero. No headshot on the homepage unless it is tiny and secondary. Avoid badges, thumbnails, big feature blocks, and ranking language. "Selected" should be a quiet editorial choice, not a portfolio showcase.

### Writing Index

Chronological archive with tasteful grouping.

Rules:

- Group by year
- Newest first
- Show all posts
- Tags are inline metadata
- Tag pages can wait, but tags must exist in frontmatter from Phase 1
- Use "selected" markers very sparingly, if at all

Each entry:

- Date
- Title
- One-sentence summary
- Tags

Avoid card grids. A reader should be able to scan the archive quickly on a phone.

### Project Index

This is not the old portfolio timeline. It is an index of things built.

Groups:

- Selected projects
- Older and smaller projects

Each row:

- Name
- Problem, constraint, or essay angle
- Year or period
- Quiet technology metadata
- Primary link to essay when one exists
- Secondary links: demo, source, package, paper, video

Use technologies as evidence, not as the lead. Every legacy project should be accounted for either as a row or as part of a merged row, even when it does not deserve a full essay.

### About

This is the hiring-focused page.

Sections:

- Short bio
- Quick facts: target role/domain, location/remote preference, availability if relevant, email, CV, GitHub, LinkedIn
- Best starting points: 3-5 strongest essays
- What I work on
- Selected technical work
- Technical strengths
- Experience/education summary

This page can be more direct:

- MSc in Computer Science
- Professional software engineering experience
- AI/ML systems, large-scale architecture, graphics, visualization, embedded projects
- Preference for complex, multidisciplinary systems

Keep it typographic and compact. No skill grids, logo walls, metric blocks, or resume cards.

## Content Plan

### Editorial Standard

The first version should not read like project cards converted to Markdown. The project material should become technical essays, some of which happen to come from past projects.

Every substantial post should answer:

- Does this teach something to a technical reader who is not evaluating me?
- Does the title make a specific promise?
- Is there a thesis, reader takeaway, and evidence?
- Is there at least one concrete constraint, number, diagram, code excerpt, or failure?
- Is the project age clearly framed?
- Are technologies evidence rather than the point?
- Would the post still be worth reading if my name and CV links disappeared?

### Initial Essays

Use essay angles rather than project labels:

1. `Designing an ML deployment API around best practices` from GreatAI
2. `Tile-based optimization for 2D SDF ray tracing` from SDF-2D
3. `Shared simulation code in a mobile multiplayer browser game` from decla.red
4. `A 50 FPS game engine on an ATtiny85` from ad_astra
5. `Syncing state with immutable tries` from life-towers
6. `Graph models for a real-time cooling simulation` from the nuclear simulator and graph editor

Best recruiter starting set:

- GreatAI
- SDF-2D
- life-towers or nuclear-simulation

Secondary project rows or shorter notes:

- City simulation in Unity
- Forex prediction experiment
- My Notes Android app
- Platform game in C/SDL
- LED music visualizer
- Photo site generator
- Avoid
- Photo colour grader

Add 2-3 non-project technical notes shortly after launch so the site does not feel like a portfolio in blog clothing. Good candidates:

- Lessons from deploying ML systems
- Notes on mobile graphics performance
- What makes old side projects still worth writing about
- A debugging or architecture retrospective from recent work

### Date Framing

Separate project dates from publication dates.

- `date`: publication date for RSS and archive ordering
- `updated`: material revision date
- `projectPeriod`: human text such as `Autumn-Winter 2020`
- `sortDate`: approximate ISO date for project ordering

Old projects should be framed as current reflection on past work, not as current products.

### Post Template

Each project essay should follow a consistent loose structure:

```markdown
---
title:
description:
date:
updated:
projectPeriod:
tags:
project:
role:
stack:
scale:
outcome:
links:
  - label:
    type:
    url:
media:
  - type:
    src:
    poster:
    mp4:
    webm:
    alt:
    caption:
    transcript:
    role: evidence
---

Intro: the thesis, why this is worth reading, and what the reader will learn.

## At a glance

Role, timeframe, stack, scale, outcome, links.

## The Problem

## Constraints

## Design

## What Worked

## What I Would Change

## Links
```

This should not become rigid. Some posts need a story; others need an architecture walkthrough.

### Writing Style

Prefer:

- Specific constraints
- Numbers
- Concrete tradeoffs
- Architecture sketches
- What shipped
- What improved
- Who used it, if known
- What was measured
- What I personally owned
- What failed
- What changed your mind
- Short code excerpts when useful

Avoid:

- "I was passionate about..."
- Generic technology lists
- Resume phrasing
- Overexplaining old projects as if they are current products
- Hiding rough edges

Rough edges are useful if framed as engineering judgment.

## Migration Plan

### Migration Manifest

Create a migration manifest before rewriting content. It should map executable TS project records into static content.

Fields:

```ts
{
  sourceProjectId: string;
  sourceFile: string;
  slug: string;
  target: "essay" | "project-row" | "merged-project-row";
  canonicalPath: string;
  legacyAnchor?: string;
  title: string;
  essayTitle?: string;
  publishedDate?: string;
  projectPeriod: string;
  sortDate: string;
  bodySources: Array<"description" | "more">;
  links: Array<{ label: string; type: "source" | "demo" | "package" | "paper" | "video" | "site"; url: string; download?: boolean }>;
  media: Array<{ type: "image" | "video" | "preview"; src?: string; poster?: string; mp4?: string; webm?: string; externalUrl?: string; alt: string; caption: string }>;
  downloads: Array<{ label: string; url: string }>;
}
```

Initial mapping:

| Source                             | Target                            | Notes                                                      |
| ---------------------------------- | --------------------------------- | ---------------------------------------------------------- |
| `great-ai.ts`                      | Essay                             | Use `great-ai.png`, MSc thesis PDF, PyPI/site links        |
| `sdf2d.ts`                         | Essay                             | Use `sdf2d.png`, NPM, YouTube, demo; cross-link BSc thesis |
| `declared.ts`                      | Essay                             | Use `decla-red.png`, GitHub, demo, BSc thesis              |
| `ad-astra.ts`                      | Essay                             | Use poster and MP4/WebM; GitHub link                       |
| `towers.ts`                        | Essay or selected project row     | Use `towers.png`, GitHub, demo; focus on trie sync         |
| `nuclear.ts` + `nuclear-editor.ts` | One essay or selected project row | Merge simulator/editor into one story with two screenshots |
| `city-simulation.ts`               | Project row or short note         | Use video only inside detail writing if kept               |
| `forex.ts`                         | Project row or short note         | Frame carefully as experiment, not financial claim         |
| `my-notes.ts`                      | Older project row                 | Android/Markdown note app                                  |
| `platform-game.ts`                 | Older project row                 | Early C/SDL learning story                                 |
| `leds.ts`                          | Older project row                 | Raspberry Pi, FFT, LEDs                                    |
| `photos.ts`                        | Older project row                 | Static photo site generator                                |
| `avoid.ts`                         | Older project row                 | First web game, external demo                              |
| `colors.ts`                        | Older project row                 | Color-grader proof of concept                              |

Migration copyedit rules:

- Decode HTML entities such as `&mdash;` and `&amp;`
- Replace generic icon/helper labels with semantic labels
- Preserve external URLs
- Replace generic video alt text with project-specific alt/captions
- Keep BSc/MSc PDFs with explicit labels
- Cross-link shared thesis context between SDF-2D and decla.red

### Link Labels

Normalize links to reader-facing labels:

- `Source`
- `Demo`
- `Package`
- `Paper`
- `Thesis`
- `Video`
- `Project site`

Do not use helper-derived labels such as "Open in new tab" in content.

## Technical Plan

### Framework

Use Astro.

Reasons:

- Static generation by default
- Markdown content collections
- Little-to-no client JavaScript
- Good TypeScript fit for this repo
- Straightforward RSS/sitemap support
- Easy to preserve a content-first architecture

Do not use Typst as the primary web writing format for now. Typst's HTML export is still not the right foundation for this production blog. Use Markdown for posts. Typst can be revisited later for PDFs or long-form downloadable documents.

### JavaScript Policy

Default: ship no client JavaScript.

Allowed later only if it materially improves a specific post:

- A small interactive diagram
- A progressive enhancement for a specific demo
- A tiny script for theme preference if dark mode is added

Hard rules:

- No global app shell
- No client-side routing
- No required JS for reading
- No `client:*` islands in normal pages
- No global view transitions
- No global prefetching
- No analytics script unless explicitly accepted as a measured exception

QA gates:

- Run Playwright with JavaScript disabled and verify reading, nav, RSS discovery, media access, archive scanning, and project links
- Fail CI if normal pages emit unexpected `_astro/*.js` or `<script type="module">`
- Grep built HTML for unexpected scripts

### Proposed File Structure

```text
astro.config.mjs
src/
  assets/
    projects/
      great-ai/
      sdf-2d/
      declared/
      ad-astra/
  content/
    config.ts
    posts/
      greatai-ai-deployment-api.md
      sdf-2d-ray-tracing.md
      declared-shared-simulation-code.md
      ad-astra-attiny85-game-engine.md
      life-towers-immutable-tries.md
      nuclear-cooling-simulation.md
    projects/
      greatai.md
      sdf-2d.md
      declared.md
      ad-astra.md
      towers.md
      nuclear-simulation.md
  layouts/
    Base.astro
    Post.astro
    Page.astro
  components/
    Header.astro
    Footer.astro
    ArticleList.astro
    Figure.astro
    ProjectLinks.astro
    AtAGlance.astro
  pages/
    index.astro
    writing/
      index.astro
      [slug].astro
    projects/
      index.astro
    about.astro
    rss.xml.ts
styles/
  global.css
public/
  favicon.ico
  robots.txt
  og-image.jpg
  media/
    downloads/
    video/
    legacy/
```

Use `public/` only for stable raw files and downloads. Optimized post images should live in `src/assets` or beside content and render through Astro image tooling.

### Content Collections

Define typed schemas for posts and projects in `src/content/config.ts`.

Controlled tags:

```ts
['ai', 'systems', 'graphics', 'simulation', 'embedded', 'web', 'tools', 'games'];
```

Post frontmatter:

```ts
{
  title: string;
  description: string;
  date: Date;
  updated?: Date;
  draft?: boolean;
  tags: string[];
  selected?: boolean;
  featuredOrder?: number;
  project?: string;
  projectPeriod?: string;
  role?: string;
  stack?: string[];
  scale?: string;
  outcome?: string;
  audience?: "general" | "technical" | "recruiter-relevant";
  links?: Array<{ label: string; type: string; url: string }>;
  media?: Array<{
    type: "image" | "video" | "diagram";
    src?: string;
    poster?: string;
    mp4?: string;
    webm?: string;
    alt?: string;
    decorative?: boolean;
    caption?: string;
    transcript?: string;
    role?: "evidence" | "og" | "inline";
  }>;
}
```

Project frontmatter:

```ts
{
  sourceProjectId: string;
  title: string;
  description: string;
  period: string;
  sortDate: Date;
  status?: string;
  technologies: string[];
  selected?: boolean;
  essay?: string;
  legacyAnchor?: string;
  links: Array<{ label: string; type: string; url: string; download?: boolean }>;
  media?: Array<{ type: string; src?: string; poster?: string; alt: string; caption: string }>;
  downloads?: Array<{ label: string; url: string }>;
}
```

Schema rules:

- Meaningful media must have `alt` and `caption`
- `decorative: true` is allowed only with empty `alt`
- Videos with meaningful content should include a transcript or text summary
- Draft posts never appear in archives, RSS, sitemap, or selected lists

### CSS Architecture

Use one global CSS file with carefully named custom properties. No Tailwind. No CSS framework.

CSS tokens:

```css
:root {
  color-scheme: light;

  --font-sans: 'Source Sans 3', Inter, ui-sans-serif, system-ui, sans-serif;
  --font-mono: 'IBM Plex Mono', 'JetBrains Mono', ui-monospace, monospace;

  --bg: #fbfaf7;
  --text: #181817;
  --muted: #6b6860;
  --link: #285f74;
  --link-hover: #8a4b2f;
  --rule: #d9d5ca;
  --rule-strong: #7f7668;
  --code-bg: #efede6;
  --callout-bg: #f4f1e8;

  --measure: 40rem;
  --measure-wide: 62rem;
  --gutter: clamp(16px, 5vw, 24px);

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-6: 1.5rem;
  --space-8: 2rem;
  --space-12: 3rem;

  --text-caption: 0.875rem;
  --text-body: 1.0625rem;
  --text-title-mobile: 2rem;
  --leading-prose: 1.65;
  --leading-heading: 1.18;
}
```

Mobile CSS invariants:

```css
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  overflow-x: hidden;
}

body {
  margin: 0;
  font-size: var(--text-body);
  line-height: var(--leading-prose);
}

img,
video,
canvas,
svg {
  max-width: 100%;
  height: auto;
}

pre,
table {
  max-width: 100%;
  overflow-x: auto;
}

.prose {
  max-inline-size: var(--measure);
  padding-inline: var(--gutter);
  margin-inline: auto;
}

.site-header,
.project-links,
.post-tags {
  min-width: 0;
  flex-wrap: wrap;
}
```

Use modern CSS, but conservatively:

- `clamp()` for spacing and gutters, not viewport-scaled prose
- `:focus-visible`
- `text-wrap: balance` only on short headings if supported
- `overflow-wrap: anywhere` for dangerous long links
- `@media (prefers-reduced-motion)` even though there should be little motion

### Accessibility

Baseline requirements:

- `html lang="en"`
- Semantic landmarks: `header`, `nav`, `main`, `footer`
- One `h1` per page
- Ordered heading hierarchy
- `article` for posts
- `time datetime` for dates
- Real list markup for archives/projects
- `aria-current` in nav
- Skip link visible on focus
- Persistent `2px+` `:focus-visible` outline with offset
- Underlined links in prose
- Sufficient contrast
- Proper image alt text
- `figure` and `figcaption` for explanatory media
- Captions or transcripts for meaningful video
- `controls` on video
- No autoplay, parallax, or animated scroll by default
- `prefers-reduced-motion` disables transitions and scroll behavior
- No content hidden behind hover
- No JS-required navigation
- No horizontal page overflow at common phone widths
- Descriptive link text

QA should include keyboard-only testing, 200% zoom, forced-colors/high-contrast mode, mobile viewport checks, and a screen-reader landmark/heading smoke test.

### Performance

Targets:

- No client JS on normal pages
- CSS under 20KB uncompressed if practical
- Optimized responsive images through Astro image tooling
- Lazy-load non-critical images
- Do not autoplay videos
- Keep homepage light
- Avoid third-party fonts
- Prefer local assets over embeds

Media handling:

- Put optimized post images in `src/assets`
- Keep PDFs, favicons, legacy stable files, and raw videos/downloads in `public/`
- Prefer still images over videos in listing pages
- Use videos only inside article bodies
- Compress the existing MP4/WebM files before publishing if they remain large
- Add page weight reports for homepage, article, about, and projects pages
- Keep old PDFs as first-class downloads with labels such as `MSc thesis` and `BSc thesis`

### SEO and Feeds

Include:

- Page titles
- Meta descriptions
- Canonical URLs
- Open Graph metadata
- `<link rel="alternate" type="application/rss+xml">`
- Visible footer RSS link
- RSS feed at `/rss.xml`
- Sitemap via `@astrojs/sitemap` default output, expected as `/sitemap-index.xml` unless a custom `/sitemap.xml` endpoint is added
- `robots.txt`
- `humans.txt` optional

RSS rules:

- Newest first
- Exclude drafts
- Use absolute URLs
- Use stable GUIDs
- Include `updated` dates when present
- Include useful summaries at minimum
- If full-content RSS is used, normalize relative links and images

### Deployment

The repo currently deploys through `.forgejo/workflows/deploy.yml`, not a generic GitHub Pages action. The workflow runs `npm ci`, `npm run lint`, `npm run build`, then rsyncs `dist/` to `/pages/schmelczer-dev`.

Deployment requirements:

- `astro.config.mjs` sets `site: "https://schmelczer.dev"`
- No SSR adapter
- Build output remains `dist/`
- Decide `trailingSlash` explicitly
- Keep `base` empty for the root domain
- Ensure Node version in `.nvmrc` is compatible with Astro and the deployed workflow
- Update lint/format scripts for `.astro`, `.md`, config files, and TypeScript
- Remove obsolete Webpack loaders and scripts during implementation
- Keep the lockfile committed

Recommended scripts:

```json
{
  "dev": "astro dev",
  "lint": "astro check && prettier --check .",
  "format": "prettier --write .",
  "build": "astro check && astro build",
  "preview": "astro preview"
}
```

### Legacy URLs

Add a legacy URL and asset map before cutover.

Cover:

- Old hash anchors generated from project titles
- `/avoid`
- Existing root favicons
- `robots.txt`
- `og-image.jpg`
- Old PDF locations under `static/`
- Any old project/demo URLs that should remain external rather than canonical

Hash URLs cannot be server-redirected reliably, so preserve old IDs on the project index where practical.

## Implementation Phases

### Phase 1: Scaffold

- Replace Webpack setup with Astro
- Add `astro.config.mjs`
- Add `src/content/config.ts`
- Add content collections
- Add global CSS tokens and mobile invariants before templates harden
- Add homepage, writing index, post page, projects index, about page
- Add RSS and sitemap integration
- Add initial no-JS checks
- Set up asset folders: optimized images in `src/assets`, raw downloads/video in `public`

### Phase 2: Migration Manifest

- Inventory every `src/data/projects/*.ts` file
- Extract title, date string, description, `more`, media, PDFs, and links
- Normalize links into semantic labels
- Decode HTML entities
- Decide essay vs project row vs merged project row
- Preserve fuzzy project dates separately from publication dates
- Add legacy anchors

### Phase 3: Content Migration

- Convert flagship project data into Markdown essays
- Create project collection rows for all legacy projects
- Preserve external links and downloads
- Add captions, alt text, transcripts or summaries where needed
- Choose one or two media items per flagship post
- Write the About page from scratch
- Add at least 2-3 non-project technical notes soon after the first project essay batch

### Phase 4: Design Pass

- Tune typography on mobile first
- Test `320px`, `390px`, `430px`, tablet, and desktop
- Check first viewport of article pages
- Check code block behavior
- Check long URLs and long headings
- Check captions and figure sizing
- Remove any layout that feels card-heavy, retro-default, or portfolio-template-like

### Phase 5: QA

- Run build
- Run accessibility checks with axe or pa11y if available
- Run Playwright screenshots for homepage, article, about, and projects
- Run Playwright assertion: `document.documentElement.scrollWidth <= window.innerWidth` at `320`, `390`, and `430`
- Run Playwright with JavaScript disabled
- Verify no unintended JS bundles ship on static pages
- Inspect `dist/`
- Verify static HTML routes exist
- Validate canonical/RSS/sitemap URLs
- Validate internal links
- Check no drafts appear in RSS or sitemap
- Check image alt coverage
- Check 404 behavior
- Report page weight for homepage, representative article, about, and projects

### Phase 6: Recruiter Flow QA

- Can a recruiter find email and CV within 10 seconds on mobile?
- Can they identify the top three strongest technical pieces within two clicks?
- Does each flagship post establish role, depth, and outcome near the top?
- Does the About page communicate competence without feeling like a resume template?
- Does the homepage still feel like a blog rather than a hiring funnel?

## Final Design Decision

Build a modern, quiet, sans-serif publishing site.

It should borrow the seriousness and durability of old technical blogs without copying their dated defaults. It should borrow the polish of modern personal sites without becoming a portfolio template. The experience should be: open on a phone, start reading immediately, trust the author more with every paragraph.
