diff --git a/docs/bugs-and-fixes.md b/docs/bugs-and-fixes.md
deleted file mode 100644
index d93b94e..0000000
--- a/docs/bugs-and-fixes.md
+++ /dev/null
@@ -1,136 +0,0 @@
-# Bugs & Fixes
-
-Backlog of confirmed bugs with root cause analysis and implementation spec for the fix.
-
----
-
-## BUG-001 — New entry not visible after form submission
-
-**Status:** fixed 2026-06-18
-**Reported:** 2026-06-18
-
-### Symptom
-
-After submitting a new post via `/post`, the entry page file is created correctly on disk but does not appear in the `/tracker` feed or in the Grav Admin panel until the cache is manually flushed.
-
-### Root cause
-
-Grav's page-tree cache (`cache/doctrine/`) is not invalidated when `add-page-by-form` writes a new page to disk. The tracker template uses `page.children`, which Grav serves from cache — so the new child page is invisible until the cache is cleared.
-
-### Workaround (manual)
-
-Run in terminal after each submission:
-
-```bash
-docker exec intotheeast_grav bash -c "cd /app/www/public && php bin/grav clearcache"
-```
-
-### Fix spec
-
-Wire cache-clear into the form process so it happens automatically on every successful submission.
-
-**Approach — custom Grav plugin event hook:**
-
-1. Create a small plugin `user/plugins/cache-on-save/` with one event listener:
- - Listen on `onFormProcessed`
- - When the form name is `new-entry`, call `$this->grav['cache']->deleteAll()` (note: `clear()` does not exist on `Grav\Common\Cache` in Grav 1.7)
-2. Enable the plugin in `user/config/plugins/cache-on-save.yaml`
-
-This is the cleanest approach: it fires exactly once per successful submission, requires no changes to `post-form.md`, and works for any future forms too.
-
-**Alternative — disable page cache entirely:**
-
-Set `cache: { enabled: false }` in `system.yaml`. Simpler but degrades frontend performance; not recommended for production.
-
-### Files to create/modify
-
-| File | Change |
-|------|--------|
-| `user/plugins/cache-on-save/cache-on-save.php` | New plugin, ~30 lines |
-| `user/plugins/cache-on-save/cache-on-save.yaml` | Plugin manifest, enabled: true |
-| `user/config/plugins/cache-on-save.yaml` | Runtime config, enabled: true |
-
-### Acceptance criteria
-
-1. Submit a new post via `/post`
-2. Navigate to `/tracker` — the new entry is visible immediately, no manual cache flush needed
-3. Grav Admin also shows the new page immediately
-
----
-
-## BUG-002 — Stale Twig cache after theme file changes
-
-**Status:** fixed 2026-06-18
-**Reported:** 2026-06-18
-
-### Symptom
-
-After theme template files are added or modified (e.g., creating `partials/base.html.twig`), Grav's Twig compiled-template cache still holds the old compiled version. Pages that extend the changed file throw 500 errors like "Template partials/base.html.twig is not defined" even though the file exists on disk.
-
-### Root cause
-
-Grav caches compiled Twig templates in `cache/twig/`. When a new file is added, existing templates that reference it don't know to recompile — their cache entries are still valid from their own mtime perspective.
-
-### Workaround (manual)
-
-Run after any theme file is added or changed:
-
-```bash
-docker exec intotheeast_grav bash -c "cd /app/www/public && php bin/grav clearcache"
-```
-
-### Fix spec
-
-Disable Twig template caching in development via `user/config/system.yaml`:
-
-```yaml
-twig:
- cache: false
-```
-
-Acceptable for a single-user dev setup — eliminates both BUG-001's side-effect and this bug entirely. Performance cost is negligible at one-user scale. On production, leave Twig cache enabled (it's fine there because template files don't change at runtime).
-
-**Files to change:**
-
-| File | Change |
-|------|--------|
-| `user/config/system.yaml` | Add `twig: { cache: false }` under development section |
-
-### Acceptance criteria
-
-1. Add a new theme template file
-2. Reload any page — no 500 error, template works immediately without manual cache flush
-
----
-
-## BUG-003 — One post per day limit; silent failure on duplicate date
-
-**Status:** fixed 2026-06-18
-**Reported:** 2026-06-18
-
-### Symptom
-
-Submitting a second post with the same date as an existing entry shows "Entry posted successfully!" but creates no file. The user's post is silently discarded.
-
-### Root cause
-
-The `add-page-by-form` plugin built the page slug from date only (`Y-m-d`), producing folder names like `2026-06-18.entry`. With `overwrite_mode: false`, if that folder already exists the plugin skips page creation but does not abort — the `message` process step runs regardless, showing a false success.
-
-### Fix
-
-Change the slug template in `user/pages/02.post/post-form.md` to include time and title:
-
-```twig
-{{ form.value.date|date('Y-m-d-Hi') }}-{{ form.value.title|lower|regex_replace('/[^a-z0-9]+/', '-')|trim('-') }}
-```
-
-Example: title "Arrived in Tokyo" at 14:30 on 2026-06-18 → `2026-06-18-1430-arrived-in-tokyo`
-
-The slug is locked at creation time. Renaming the title afterwards does not change the URL.
-
-### Acceptance criteria
-
-1. Submit two posts on the same day with different times or titles — both appear in `/tracker` as separate entries
-2. Renaming a post's title in the frontmatter does not break its URL
-
----
diff --git a/docs/design/design-spec.md b/docs/design/design-spec.md
deleted file mode 100644
index b6519d9..0000000
--- a/docs/design/design-spec.md
+++ /dev/null
@@ -1,368 +0,0 @@
-# Into the East — Design Spec
-
-**Date:** 2026-06-18
-**Status:** Approved for implementation
-
----
-
-## 1. Direction
-
-**The brief:** A personal travel journal, sole author, trip to East Asia. Three weeks to implement before departure. Audience is both friends/family and the occasional curious stranger.
-
-**The position:** Neither Polarsteps nor FindPenguins. Both optimize for social sharing of travel data. This site optimizes for **the story** — and should feel like reading a well-edited travel journal, not using an app.
-
-**What we steal from each:**
-- Polarsteps: photography-first hierarchy, airy whitespace, map as the emotional spine of the trip
-- FindPenguins: typography as brand identity, stats as trophy case, hierarchical trip → entry structure
-
-**What we do better than both:**
-- Web-native: fast, linkable, no install, works on any browser
-- Single author = pure editorial voice, no social noise
-- Full CSS control = real typographic identity, not generic app chrome
-- Editorial feel: more travel magazine, less productivity dashboard
-
-**Aesthetic direction:** Field notes. The kind of journal a thoughtful traveler would carry — clean, direct, lets the photography speak. Sophisticated without effort.
-
-**The one aesthetic risk:** Full-bleed hero photography with a translucent date+location overlay at the bottom of each card. The photo IS the entry card — not a thumbnail beside text. This is the single element that distinguishes this design from both reference apps and from typical blog layouts.
-
----
-
-## 2. Color System
-
-### Palette
-
-| Token | Hex | Usage |
-|---|---|---|
-| `--color-ink` | `#17171A` | Primary text (near-black with cool undertone, like ink) |
-| `--color-ink-2` | `#4A4850` | Secondary text, body paragraphs |
-| `--color-ink-muted` | `#9896A0` | Labels, timestamps, captions, placeholder text |
-| `--color-paper` | `#F7F5F2` | Page background (warm paper white, not blue-white) |
-| `--color-canvas` | `#FFFFFF` | Card backgrounds, modals, form surfaces |
-| `--color-border` | `#E8E6E3` | Standard dividers, card borders |
-| `--color-border-soft` | `#F0EDEA` | Subtle section dividers |
-| `--color-accent` | `#1F6B5A` | Deep teal — brand color, links, CTAs, active states |
-| `--color-accent-hover` | `#185647` | Darkened accent for hover/pressed states |
-| `--color-accent-light` | `#EBF5F2` | Pale teal for highlight backgrounds |
-| `--color-accent-on` | `#FFFFFF` | Text on accent-colored surfaces |
-
-### Rationale for accent color
-
-Deep teal `#1F6B5A` was chosen over:
-- Blue (#0066cc current): too generic, too tech
-- Orange/saffron: clichéd for "Asia" travel design
-- Terracotta/cream: the most common default for lifestyle/travel blogs
-
-Teal evokes bamboo, celadon porcelain, ancient jade, the color of temple gardens — all without being literal or kitsch. It works cleanly against both the warm paper background and white card surfaces.
-
----
-
-## 3. Typography
-
-### Fonts
-
-| Role | Family | Fallback | Source |
-|---|---|---|---|
-| Display / Headings | DM Serif Display | Georgia, serif | Google Fonts |
-| UI / Body / Labels | DM Sans | -apple-system, BlinkMacSystemFont, sans-serif | Google Fonts |
-
-**Google Fonts URL:**
-```
-https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,400;0,9..40,500;0,9..40,600;1,9..40,400&family=DM+Serif+Display:ital@0;1&display=swap
-```
-
-**Why this pairing:**
-DM Serif Display has a calligraphic quality — slightly editorial, authoritative but not stiff. Paired with DM Sans (its designed companion) the system is cohesive. DM Sans is neutral and highly legible at all sizes. Both are under-used relative to Inter/Lato/Playfair, so the combination has a distinctive voice without being trendy.
-
-### Type Scale
-
-| Token | Size | Line Height | Usage |
-|---|---|---|---|
-| `--text-xs` | 0.75rem (12px) | 1.5 | Badges, captions |
-| `--text-sm` | 0.875rem (14px) | 1.5 | Meta, timestamps, labels |
-| `--text-base` | 1rem (16px) | 1.65 | Body paragraphs |
-| `--text-md` | 1.125rem (18px) | 1.55 | Lead text, intro paragraphs |
-| `--text-lg` | 1.375rem (22px) | 1.35 | Subheadings, card titles (mobile) |
-| `--text-xl` | 1.75rem (28px) | 1.25 | Entry card titles |
-| `--text-2xl` | 2.25rem (36px) | 1.2 | Page headings, entry titles (desktop) |
-| `--text-3xl` | 3rem (48px) | 1.1 | Hero entry title |
-
-### Usage rules
-
-- Entry titles: `--font-display`, `--text-xl` (mobile) / `--text-2xl` (desktop)
-- Site title in header: `--font-display`, `--text-lg`
-- All other UI text: `--font-ui`
-- Body paragraphs: `--font-ui`, `--text-base`, `--leading-normal`
-- Timestamps/badges: `--font-ui`, `--text-xs`, uppercase, `letter-spacing: 0.07em`
-
----
-
-## 4. Spacing & Layout
-
-### Spacing scale (4px base unit)
-
-| Token | Value |
-|---|---|
-| `--space-1` | 0.25rem (4px) |
-| `--space-2` | 0.5rem (8px) |
-| `--space-3` | 0.75rem (12px) |
-| `--space-4` | 1rem (16px) |
-| `--space-5` | 1.25rem (20px) |
-| `--space-6` | 1.5rem (24px) |
-| `--space-8` | 2rem (32px) |
-| `--space-10` | 2.5rem (40px) |
-| `--space-12` | 3rem (48px) |
-| `--space-16` | 4rem (64px) |
-
-### Layout
-
-- Content max-width: `720px` (comfortable reading at any font size)
-- Page horizontal padding: `1.25rem` (mobile), `1.5rem` (desktop ≥520px)
-- Header height: `60px` (fixed, for JS offset calculations)
-- Map page: full viewport, no content max-width constraint
-
-### Border radius
-
-| Token | Value | Usage |
-|---|---|---|
-| `--radius-sm` | 4px | Photo corners, small chips |
-| `--radius-md` | 8px | Cards, buttons, inputs |
-| `--radius-lg` | 12px | Large cards, modals |
-| `--radius-full` | 9999px | Pills, badges |
-
-### Shadows
-
-| Token | Value | Usage |
-|---|---|---|
-| `--shadow-sm` | `0 1px 3px rgba(0,0,0,0.08)` | Stat blocks, subtle elevation |
-| `--shadow-md` | `0 4px 12px rgba(0,0,0,0.10)` | Cards on hover, dropdowns |
-| `--shadow-lg` | `0 8px 24px rgba(0,0,0,0.14)` | Lightbox, modals |
-
----
-
-## 5. Component Inventory
-
-### 5.1 Site Header
-
-```
-[ into the east ] [ Journal Map Stats ]
-← accent bar across top (3px) ───────────────────────────────
-```
-
-- Top border: `3px solid var(--color-accent)` — thin accent bar signals the brand color without decorating
-- Site title: DM Serif Display, `--text-lg`, no decoration
-- Nav links: DM Sans, `--text-sm`, weight 500, `--color-ink-2`
-- Active nav link: `--color-accent`, weight 600
-- Mobile: same layout, title slightly smaller, nav links compact
-- Background: `--color-canvas` (white), bottom border `1px solid var(--color-border)`
-
-### 5.2 Entry Feed Card — With Photo
-
-```
-┌─────────────────────────────────────┐
-│ │
-│ [photo] │ ← full-width, 16:9, rounded corners
-│ │
-│ 18 JUN · 📍 Kyoto, Japan │ ← overlaid at bottom, gradient mask
-└─────────────────────────────────────┘
- Arrived in Tokyo ← DM Serif Display, --text-xl
- After 14 hours of flying I finally ← body excerpt, --color-ink-2
- set foot on Japanese soil...
- Read entry → ← --color-accent, --text-sm
-```
-
-- Photo: `aspect-ratio: 16/9`, `object-fit: cover`, `border-radius: var(--radius-md)`
-- Photo has a `linear-gradient(to top, rgba(0,0,0,0.55), transparent)` overlay at the bottom 40%
-- Date + location sit on top of gradient in white text (`rgba(255,255,255,0.92)`)
-- On hover: photo scales to 1.03 (subtle zoom, 0.4s ease)
-- Title below photo: DM Serif Display, hover turns `--color-accent`
-- Card separation: `padding-bottom: var(--space-12)` + `border-bottom: 1px solid var(--color-border)`
-
-### 5.3 Entry Feed Card — No Photo
-
-When no photo is available, fall back to a text-only layout:
-
-```
- 18 JUN 2026 · 📍 Kyoto, Japan ← meta row, --text-sm, --color-ink-muted
-
- Arrived in Tokyo ← DM Serif Display, --text-xl
- After 14 hours of flying...
- Read entry →
-```
-
-- No photo container
-- Meta (date + location) on one line above title, small + muted
-
-### 5.4 Single Entry Page
-
-```
- Wednesday, 18 June 2026 ← --text-sm, --color-ink-muted, uppercase
- 📍 Kyoto, Japan · ⛅ Partly cloudy · 22°C
-
- Arrived in Tokyo ← DM Serif Display, --text-2xl / --text-3xl
- ─────────────────────────────────────
- Body text content... ← --font-ui, --text-base/md
-
- [Photo gallery — 2 or 3 col grid]
-
- ← Back to journal
-```
-
-- The entry title uses `--font-display` at largest scale
-- A thin `--color-border` rule separates the header from the body
-- Body text is `--text-md` (18px) for comfortable long-form reading
-- Full-bleed hero option: if a `hero_image` is set, it spans the full content width with a bottom margin
-
-### 5.5 Post Form (Author View)
-
-```
- New Entry
-
- Title * [________________________]
- Date & Time [2026-06-18 14:30 ]
- What happened [ ]
- today? [ ]
- [ ]
-
- Photos [ + Add photos (max 4) ]
-
- City [________________________]
- Country [________________________]
-
- [ 📍 Get Location ] [ 🌤 Get Weather ]
- ✓ Location captured: Kyoto, Japan ← status line
-
- [ Post Entry ]
-```
-
-UX changes from current:
-- Lat/lng inputs **hidden from the UI** (remain in the form as `display:none` for data capture, filled by JS)
-- Location status shows captured city/country + coordinates in a single line (not separate status paragraphs)
-- Photo upload area: larger touch target, visual indication of count
-- "Post Entry" button: `--color-accent` background, full-width on mobile, `min-height: 52px`
-- Form fields: `--radius-md` corners, `--color-border` border, focus ring in `--color-accent`
-- Section spacing: generous vertical rhythm on mobile
-
-### 5.6 Stats Page
-
-```
- ┌────────────┐ ┌────────────┐
- │ 42 │ │ 18 │
- │ days on │ │ entries │
- │ the road │ │ posted │
- └────────────┘ └────────────┘
- ┌────────────┐ ┌────────────┐
- │ 6 │ │ ~14,200 │
- │ countries │ │ km │
- │ visited │ │ traveled │
- └────────────┘ └────────────┘
-
- Countries visited
- Japan · South Korea · Mongolia · Russia · Finland · Estonia
-```
-
-- Numbers: `--font-display`, `--text-3xl`, `--color-accent`
-- Labels: `--font-ui`, `--text-xs`, uppercase, `--color-ink-muted`
-- Cards: white, `--shadow-sm`, `--radius-md`, centered
-
-### 5.7 Map Page
-
-Minimal changes — the map itself is good. Style improvements:
-- Leaflet popups: match the new design (DM Sans, `--radius-md`, `--shadow-md`)
-- Markers: keep current circle style, update color to `--color-accent`
-- Feed mini-map wrapper: match `--radius-md`, `--border`
-
----
-
-## 6. UX Flows
-
-### 6.1 Reader — First Visit
-
-1. Land on `/tracker` (journal feed)
-2. See mini-map above fold (if entries exist) — route tells the geographic story at a glance
-3. First entry card: full-bleed hero photo with date/location overlay — immediate emotional pull
-4. Scroll through chronological entries
-5. Tap/click entry → entry detail page
-6. Navigate back via "← Back to journal"
-
-**Key principle:** The reader should understand the journey spatially (mini-map) and emotionally (hero photo) before reading a single word.
-
-### 6.2 Reader — Navigation
-
-- Journal: primary destination, the feed
-- Map: geographic exploration mode
-- Stats: quick numbers, satisfying progress indicator
-- No account required, no social friction, no login prompt for readers
-
-### 6.3 Author — Posting from Mobile
-
-1. Navigate to `/post` (bookmark on home screen)
-2. Already logged in (Grav session persists) — form loads directly
-3. **Title**: tap → type (autofocused)
-4. **Date & Time**: auto-filled to now, adjust if needed
-5. **Content**: write what happened
-6. **Photos**: tap "Add photos" → camera or gallery → select up to 4
-7. **Location**: tap "📍 Get Location" → GPS fires → status shows "Kyoto, Japan · 34.985, 135.758" in one line
-8. **Weather**: tap "🌤 Get Weather" (works only if location was captured) → status shows "Partly cloudy · 22°C"
-9. **City/Country**: auto-populated from GPS is a nice-to-have for v2; in v1 type manually if needed
-10. Tap "Post Entry" → success message → 2-second pause → redirect to /tracker (new entry visible at top)
-
-**Key principles:**
-- One-thumb operation for all critical actions on mobile
-- Location/weather are conveniences, not blockers — can skip both
-- Visual feedback is immediate (status line updates on GPS response)
-- After submit: don't leave author on a success message page; redirect to see their new post
-
----
-
-## 7. Mobile Specifics
-
-### Touch targets
-- All interactive elements: `min-height: 44px`, `min-width: 44px` (Apple HIG standard)
-- Form buttons: `min-height: 52px` on the post form (primary CTA)
-- Nav links: `padding: 0.5rem 0.75rem`
-
-### Viewport concerns
-- Map page: `height: calc(100vh - 60px)`, `touch-action: none` on map container — prevents scroll trap
-- Photo lightbox: full viewport overlay, swipe-friendly (keyboard + click already implemented)
-- Form on mobile: single-column, generous input padding `0.875rem 1rem`, `font-size: 1rem` (prevents iOS zoom on focus)
-
-### Performance
-- Google Fonts: loaded with `preconnect` hints
-- Images: `loading="lazy"` on all non-above-fold images (already in place)
-- Leaflet: loaded from CDN, only on pages that need it
-- No new JS frameworks — vanilla JS throughout
-
----
-
-## 8. Tech Stack Decision
-
-**Keep Grav CMS.** With a 3-week timeline, replacing it would consume all available time on migration rather than design improvements.
-
-| Layer | Decision | Rationale |
-|---|---|---|
-| Backend | Grav CMS (PHP, Twig) — unchanged | Works, flat-file, no DB |
-| CSS | Vanilla CSS + custom properties (design tokens) | No build step, full control, ships as one file |
-| JS | Vanilla JS — unchanged | Current JS is well-structured, scope doesn't justify a framework |
-| Icons | Unicode + emoji (current) | No dependency, works everywhere |
-| Fonts | Google Fonts via CDN | Two fonts, display-swap, negligible impact |
-| Maps | Leaflet.js (current) | Already in use, no reason to change |
-| Build | None — no build pipeline | Grav's asset pipeline handles minification if needed |
-
-**No Alpine.js, no TypeScript, no Tailwind.** The site has clean vanilla JS and CSS today; a redesign is about visual quality, not framework migration. Introducing a build pipeline on a 3-week timeline is a distraction.
-
----
-
-## 9. What Changes From Current Design
-
-| Area | Current | New |
-|---|---|---|
-| Typography | System sans-serif only | DM Serif Display for headings + DM Sans for UI |
-| Accent color | `#0066cc` (generic blue) | `#1F6B5A` (deep teal) |
-| Background | `#ffffff` (pure white) | `#F7F5F2` (warm paper) |
-| Entry cards | Thumbnail + text below | Full-bleed 16:9 photo with overlay |
-| Header | No visual identity | Accent top-border, typographic title |
-| Design tokens | Hardcoded values throughout | CSS custom properties throughout |
-| Post form | Lat/lng visible inputs | Lat/lng hidden, single status line |
-| Font loading | None | Google Fonts DM pairing |
-| Hover states | Minimal | Photo zoom, title color change |
-| Stat numbers | `#0066cc` | `--color-accent` (#1F6B5A) |
diff --git a/docs/milestone-1-spec.md b/docs/milestone-1-spec.md
deleted file mode 100644
index 6413fff..0000000
--- a/docs/milestone-1-spec.md
+++ /dev/null
@@ -1,193 +0,0 @@
-# Milestone 1 Spec — Entry Enrichment
-
-**Goal:** Every entry is richer out of the box — location name shown, weather auto-captured, photos in a proper gallery, hero image visible on the feed.
-
----
-
-## User Stories
-
-- As a traveler (Mischa), when I submit the post form, I want my current weather conditions auto-filled so I don't have to look them up manually.
-- As a traveler, I want to type my city and country once and have it appear on the entry and in the feed card, so readers know where I am without reading the whole post.
-- As a reader, when I scan the feed, I want to see a thumbnail photo and location for each entry so I can quickly get a sense of where Mischa is and whether to read the full entry.
-- As a reader, when I open an entry, I want to see all uploaded photos in a gallery I can browse, not a wall of raw images.
-- As a traveler, when I submit a form without photos, the entry should still display cleanly with no broken image placeholders.
-
----
-
-## Feature Details
-
-### 1.1 — Location Name Field on Post Form
-
-**What:** Add two text fields to the post form: `location_city` and `location_country`.
-
-**Behavior:**
-- Both are optional (GPS coordinates are also optional)
-- Placeholder text: "e.g. Kyoto" and "e.g. Japan"
-- Displayed below the lat/lng fields
-- On submit, stored in entry frontmatter as `location_city` and `location_country`
-- On the form, shown as a single labeled group "Location Name" with two side-by-side inputs on desktop, stacked on mobile
-
-**Edge cases:**
-- If left blank: entry shows no location badge. No error, no broken UI.
-- Long city names (e.g. "Ulaanbaatar") must not overflow card layout.
-- Special characters (accents, non-Latin) must render correctly.
-
-**Mobile behavior:** Both fields full-width, stacked, 44px min touch targets.
-
----
-
-### 1.2 — Weather Auto-Fetch on Post Form
-
-**What:** A "Get Weather" button on the post form that calls the Open-Meteo free API (no API key) using the lat/lng already entered, and fills hidden weather fields.
-
-**Fields to fetch and store:**
-- `weather_temp_c` — temperature in Celsius (integer)
-- `weather_desc` — short description: one of: Sunny, Partly cloudy, Cloudy, Foggy, Drizzle, Rain, Snow, Thunderstorm (derived from WMO weather code)
-
-**WMO code mapping (Open-Meteo uses WMO codes):**
-- 0 → Sunny
-- 1,2 → Partly cloudy
-- 3 → Cloudy
-- 45,48 → Foggy
-- 51,53,55,56,57 → Drizzle
-- 61,63,65,66,67,80,81,82 → Rain
-- 71,73,75,77,85,86 → Snow
-- 95,96,99 → Thunderstorm
-
-**API call:**
-```
-https://api.open-meteo.com/v1/forecast?latitude={lat}&longitude={lng}¤t=temperature_2m,weather_code&temperature_unit=celsius
-```
-
-**UX flow:**
-1. User fills in lat/lng (manually or via "Get Location" button)
-2. User taps "Get Weather" button
-3. Button shows "Fetching…" while loading
-4. On success: fills temp and desc fields (visible, editable text inputs)
-5. On failure (no network, no lat/lng): shows inline error "Could not fetch weather — enter manually"
-
-**Edge cases:**
-- If lat/lng not filled when button tapped: show inline error "Enter coordinates first"
-- Weather fields are always editable manually (auto-fill is a convenience, not mandatory)
-- If weather fields left blank: entry shows no weather badge. No broken UI.
-- Open-Meteo returns current conditions, not historical — this is fine for posting in real time
-
-**Mobile behavior:** "Get Weather" button is full-width, 44px height, placed immediately below the lat/lng + location name fields.
-
----
-
-### 1.3 — Weather Display on Entry Page
-
-**What:** If `weather_temp_c` or `weather_desc` is present in frontmatter, display a weather badge on the entry page.
-
-**Display format:** `☀️ Sunny · 28°C` (icon + description + temperature)
-- Icon chosen from a small set based on `weather_desc`:
- - Sunny → ☀️
- - Partly cloudy → ⛅
- - Cloudy → ☁️
- - Foggy → 🌫️
- - Drizzle → 🌦️
- - Rain → 🌧️
- - Snow → ❄️
- - Thunderstorm → ⛈️
-
-**Placement:** In the entry header, between the date and the body text. Same line as GPS coordinates if those are shown.
-
-**Edge cases:**
-- Only temp, no desc → show temp only
-- Only desc, no temp → show desc only
-- Neither → hide weather section entirely
-- Temperature should always be integer (round if float)
-
----
-
-### 1.4 — Location Badge on Feed Cards and Entry Page
-
-**What:** Display `location_city, location_country` as a small badge on tracker feed cards and at the top of entry pages.
-
-**Feed card:** Below the date, above the excerpt. Format: `📍 Kyoto, Japan`
-
-**Entry page:** In the header below the date, above the content. Format: `📍 Kyoto, Japan`
-
-**Edge cases:**
-- Only city, no country → `📍 Kyoto`
-- Only country, no city → `📍 Japan`
-- Neither → location badge hidden entirely
-- Long location names: truncate with ellipsis at 30 chars on cards (full text on entry page)
-
----
-
-### 1.5 — Photo Gallery on Entry Page
-
-**What:** Photos uploaded to an entry should display in a responsive grid gallery with lightbox (click to enlarge).
-
-**Implementation approach:** Use Grav's native media collection for the entry page. Each `.entry` folder contains its photos. Render them in a grid in `entry.html.twig`. Use a minimal vanilla JS lightbox — no external framework.
-
-**Gallery behavior:**
-- Photos displayed in a 2-column grid on mobile, 3-column on desktop
-- Each thumbnail is square-cropped, 150px on mobile
-- Clicking/tapping a thumbnail opens a lightbox overlay
-- Lightbox: dark overlay, full-size image centered, tap/click outside or press Escape to close
-- Left/right navigation arrows in lightbox (swipe on mobile)
-- No captions needed for v1
-
-**Edge cases:**
-- 0 photos: gallery section hidden entirely
-- 1 photo: still uses grid (single item), lightbox works
-- Many photos (>10): gallery still renders (no hard limit on display)
-- Non-image files in the media folder: skip them (only render jpg, jpeg, png, webp, gif)
-
----
-
-### 1.6 — Hero Image on Tracker Feed Cards
-
-**What:** If an entry has photos, the first photo (or the one named in `hero_image` frontmatter) appears as a thumbnail on the tracker feed card.
-
-**Implementation:** In `tracker.html.twig`, for each entry:
-1. If `entry.header.hero_image` is set, use `entry.media[entry.header.hero_image]`
-2. Else, use the first image in `entry.media` sorted by name
-3. Render as a 16:9 aspect-ratio thumbnail, full width of card, above the title
-
-**Edge cases:**
-- No photos: card shows no image, just text. No broken `` tag.
-- `hero_image` set but file missing: fall back to first media file, or no image
-- Very tall/wide images: CSS `object-fit: cover` maintains card aspect ratio
-
----
-
-## Out of Scope (Milestone 1)
-
-- Map features (Milestone 2)
-- Statistics page (Milestone 3)
-- Video support
-- Comments or reactions
-- Automated reverse geocoding (city name comes from form input, not auto-detected)
-- Altitude display (data may not be present)
-- Historical weather (Open-Meteo current endpoint only)
-
----
-
-## Acceptance Criteria
-
-1. Post form has `location_city` and `location_country` fields that save to entry frontmatter
-2. Post form has "Get Weather" button that fills `weather_temp_c` and `weather_desc` via Open-Meteo when lat/lng are provided
-3. Entry page shows weather badge when weather fields are present; hidden when absent
-4. Entry page shows location badge `📍 City, Country` when location fields are present; hidden when absent
-5. Tracker feed card shows location badge when present
-6. Tracker feed card shows a hero image when photos exist for an entry
-7. Entry page shows a 2-col (mobile) / 3-col (desktop) photo grid
-8. Clicking any photo opens a full-screen lightbox with prev/next navigation
-9. Pressing Escape or clicking outside lightbox closes it
-10. All fields are optional — empty values produce no broken UI elements
-11. All interactive elements meet 44px minimum touch target on mobile
-12. Form submits correctly with all new fields populated or all blank
-
----
-
-## Design Notes
-
-- Weather and location badges should be subtle — small text, muted color, not the visual focus
-- Use emoji icons for weather — universal, no icon font dependency
-- Gallery grid: `gap: 4px` between thumbs, no borders, square crops
-- Lightbox: `background: rgba(0,0,0,0.92)`, image centered with `max-height: 90vh`
-- Feed card image: `aspect-ratio: 16/9`, `object-fit: cover`, rounded top corners matching card
diff --git a/docs/milestone-2-spec.md b/docs/milestone-2-spec.md
deleted file mode 100644
index 2e7337b..0000000
--- a/docs/milestone-2-spec.md
+++ /dev/null
@@ -1,166 +0,0 @@
-# Milestone 2 Spec — Interactive Map
-
-**Goal:** A `/map` page shows all entries as markers on an interactive Leaflet.js map, connected by a chronological route line, with popups linking to entries.
-
----
-
-## User Stories
-
-- As a reader, I want to see a world map showing where Mischa has been so I can understand the journey at a glance without reading every entry.
-- As a reader, I want to click a map marker and see the entry date, title, and a thumbnail — and be able to click through to the full entry.
-- As a reader on mobile, I want to pan and pinch-zoom the map with my fingers without the page scrolling underneath.
-- As a traveler (Mischa), I want the map to automatically include every entry that has lat/lng data — I should not need to do any manual map maintenance.
-- As a reader, I want the map to show the route line connecting stops in the order they were visited, so the journey makes narrative sense.
-
----
-
-## Feature Details
-
-### 2.1 — Map Page
-
-**Route:** `/map`
-
-**Template:** `map.html.twig` — extends `partials/base.html.twig`
-
-**Page file:** `user/pages/03.map/map.md`
-
-**Content:**
-- Full-viewport-height map container below the site header
-- Leaflet.js loaded from CDN (jsDelivr): `https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.min.js`
-- Leaflet CSS from same CDN
-- Tile layer: OpenStreetMap (free, no API key): `https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png`
-- Attribution: "© OpenStreetMap contributors"
-
-**Map initialization:**
-- Default zoom: auto-fit to bounds of all markers (use `map.fitBounds()`)
-- If no entries with GPS data: show world view, zoom 2, centered at 0,0 with a message "No locations yet"
-- Min zoom: 2, Max zoom: 18
-
----
-
-### 2.2 — Entry Data Serialization
-
-**How entries reach the map JS:**
-
-In `map.html.twig`, Grav's Twig will iterate all published entries under `/tracker` and serialize them to a JSON array embedded in a `
-{% endblock %}
-```
-
-- [ ] **Step 3: Replace the Post form CSS section**
-
-Find `/* ── Post form ──` in style.css. Replace it entirely with:
-
-```css
-/* ── Post form ──────────────────────────────────────────────────────────────── */
-
-.post-form-wrap h1 {
- font-family: var(--font-display);
- font-size: var(--text-xl);
- font-weight: 400;
- margin-bottom: var(--space-6);
- color: var(--color-ink);
-}
-
-/* Hide GPS coordinate fields — filled by JS, not user-facing */
-.gps-hidden-field { display: none !important; }
-
-/* Grav form field wrappers */
-.form-field { margin-bottom: var(--space-5); }
-.form-label label {
- display: block;
- font-size: var(--text-sm);
- font-weight: 600;
- color: var(--color-ink);
- margin-bottom: var(--space-2);
-}
-
-.form-field input[type="text"],
-.form-field input[type="email"],
-.form-field input[type="datetime-local"],
-.form-field textarea {
- width: 100%;
- font-family: var(--font-ui);
- font-size: var(--text-base);
- padding: 0.875rem 1rem;
- border: 1px solid var(--color-border);
- border-radius: var(--radius-md);
- background: var(--color-canvas);
- color: var(--color-ink);
- min-height: 44px;
- transition: border-color 0.15s;
- -webkit-appearance: none;
-}
-
-.form-field input:focus,
-.form-field textarea:focus {
- outline: 2px solid var(--color-accent);
- outline-offset: 1px;
- border-color: var(--color-accent);
-}
-
-.form-field textarea { resize: vertical; min-height: 160px; line-height: var(--leading-normal); }
-
-/* Submit button — Grav renders it as .btn or input[type=submit] */
-.form-actions input[type="submit"],
-.form-actions .btn,
-.form-actions button[type="submit"] {
- display: block;
- width: 100%;
- padding: 1rem;
- min-height: 52px;
- background: var(--color-accent);
- color: var(--color-accent-on);
- border: none;
- border-radius: var(--radius-md);
- font-family: var(--font-ui);
- font-size: var(--text-base);
- font-weight: 600;
- cursor: pointer;
- transition: background 0.15s;
- margin-top: var(--space-6);
-}
-
-.form-actions input[type="submit"]:hover,
-.form-actions button[type="submit"]:hover { background: var(--color-accent-hover); }
-
-/* Action buttons row (Get Location, Get Weather) */
-.form-action-row {
- display: flex;
- gap: var(--space-3);
- margin-top: var(--space-5);
-}
-
-.btn-action {
- flex: 1;
- padding: 0.75rem var(--space-3);
- min-height: 44px;
- background: var(--color-canvas);
- border: 1px solid var(--color-border);
- border-radius: var(--radius-md);
- font-family: var(--font-ui);
- font-size: var(--text-sm);
- font-weight: 500;
- cursor: pointer;
- color: var(--color-ink);
- transition: background 0.15s, border-color 0.15s;
-}
-
-.btn-action:hover { background: var(--color-paper); border-color: var(--color-accent); }
-
-/* Status feedback lines */
-.form-status {
- font-size: var(--text-sm);
- color: var(--color-ink-muted);
- margin-top: var(--space-2);
- min-height: 1.4em;
-}
-
-.form-status--ok { color: var(--color-accent); }
-.form-status--err { color: #B44A2A; }
-```
-
-- [ ] **Step 4: Verify post form**
-
-Open `http://100.96.115.96:8081/post` (logged in). Verify:
-- Lat/lng inputs not visible (`.gps-hidden-field` hidden via CSS)
-- Inputs have rounded corners, proper padding, focus ring in teal
-- "Get Location" and "Get Weather" buttons side by side, same width
-- "Post Entry" (or whatever the submit label is) in teal, full-width
-- Tap "Get Location" → status line shows "✓ Location captured · lat, lng" in teal
-- Mobile at 375px: all inputs and buttons are thumb-friendly
-
-- [ ] **Step 5: Commit**
-
-```bash
-cd /home/mischa/Nextcloud/Projects/travel-blog-intotheeast/user
-git add pages/02.post/post-form.md themes/intotheeast/templates/post-form.html.twig themes/intotheeast/css/style.css
-git commit -m "feat: redesign post form — hide GPS fields, teal CTA, better mobile UX"
-```
-
----
-
-### Task 7: Stats + map + mini-map styling
-
-**Files:**
-- Modify: `user/themes/intotheeast/templates/stats.html.twig`
-- Modify: `user/themes/intotheeast/css/style.css` (Map, Stats, Mini-map sections)
-
-**Interfaces:**
-- Produces: styled stats page and map page using design tokens
-
-- [ ] **Step 1: Update stats page heading**
-
-In `user/themes/intotheeast/templates/stats.html.twig`, replace the `