diff --git a/.husky/pre-commit b/.husky/pre-commit
index 9e972a9..2ffefef 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -73,8 +73,9 @@ fi
# 6. Web checks — only when web/ files are staged.
if [ -n "$(staged_match '^web/')" ]; then
- echo "→ web: prettier --check"
- ( cd web && pnpm run format:check )
+ echo "→ web: prettier --write"
+ ( cd web && pnpm run format )
+ git add $(git diff --cached --name-only --diff-filter=ACMR | grep '^web/' | tr '\n' ' ')
echo "→ web: eslint"
( cd web && pnpm run lint )
diff --git a/backend/internal/domain/market/handler.go b/backend/internal/domain/market/handler.go
index f958be6..fa7a423 100644
--- a/backend/internal/domain/market/handler.go
+++ b/backend/internal/domain/market/handler.go
@@ -2,6 +2,7 @@ package market
import (
"errors"
+ "log/slog"
"net/http"
"strconv"
@@ -30,6 +31,7 @@ func (h *Handler) Search(c *gin.Context) { //nolint:dupl // similar structure to
markets, total, err := h.service.Search(c.Request.Context(), params)
if err != nil {
+ slog.ErrorContext(c.Request.Context(), "market search failed", "error", err)
apiErr := apierror.Internal("failed to search markets")
c.JSON(apiErr.Status, apierror.NewResponse(apiErr))
return
@@ -77,6 +79,7 @@ func (h *Handler) GetBySlug(c *gin.Context) {
c.JSON(apiErr.Status, apierror.NewResponse(apiErr))
return
}
+ slog.ErrorContext(c.Request.Context(), "market get by slug failed", "slug", slug, "error", err)
apiErr := apierror.Internal("failed to get market")
c.JSON(apiErr.Status, apierror.NewResponse(apiErr))
return
diff --git a/web/docs/design-system.md b/web/docs/design-system.md
new file mode 100644
index 0000000..8b52f57
--- /dev/null
+++ b/web/docs/design-system.md
@@ -0,0 +1,172 @@
+# Marktvogt Design System — Burgund
+
+Established May 2026 via Claude Design handoff (bundle: `marktvogt-de.tar.gz`).
+
+## Identity
+
+**Burgund** is the locked visual identity for Marktvogt. The name comes from the deep sealing-wax red at its core.
+
+Design principles chosen in the original session:
+
+- **Editorial-classical** — Cormorant Garamond display, EB Garamond body, editorial proportions
+- **Mid-density** — enough whitespace to breathe, enough content to inform
+- **Minimal decoration** — one ornament glyph (✦), drop-caps, double rules only. No rounded corners, no gradient badges, no color-coded semantic variants.
+- **Body-lead hierarchy** — lead paragraph at 22px italic, then 16/15/14px body; display headlines at 76/56/44px
+
+## Tokens
+
+Source of truth: `web/src/app.css` (`@theme` block and `:root.dark` overrides).
+
+| Token | Light | Dark |
+| --------------------- | -------------------------------- | ------------------------ |
+| `--color-bg` | `#f5efe4` (warm parchment) | `#0f0c0a` (deep ink) |
+| `--color-surface` | `#ffffff` | `#191411` |
+| `--color-surface-alt` | `#ece4d4` | `#241c17` |
+| `--color-ink` | `#181410` | `#f0e6d2` |
+| `--color-ink-soft` | `#3a322a` | `#c0b094` |
+| `--color-ink-muted` | `#6e6253` | `#74644f` |
+| `--color-rule` | `#181410` | `#3a2e22` |
+| `--color-rule-soft` | `#c9b58c` | `#2a221d` |
+| `--color-accent` | `#9a1e2c` (sealing-wax burgundy) | `#d86268` (halbton rose) |
+| `--color-accent-soft` | `#c84858` | `#8a2a32` |
+| `--color-on-accent` | `#f5efe4` | `#0f0c0a` |
+
+The dark accent is the "Halbton" step chosen from the design session — midway between the original loud `#e84a5e` and the subdued `#c87a7a`. The dark Submit-CTA "Bordeau block" uses `surface-alt #241c17` with cream `ink` foreground.
+
+## Typography
+
+| Role | Font | Usage |
+| -------------- | ------------------ | ------------------------------------------------------------------- |
+| `font-display` | Cormorant Garamond | Headlines, section titles, drop-caps |
+| `font-serif` | EB Garamond | Body text, nav links, buttons, table content |
+| `font-sans` | Inter | Reserved; currently unused in UI — available for UI forms if needed |
+| `font-mono` | JetBrains Mono | CAPS labels, tags, mono counters |
+
+All fonts self-hosted under `web/static/fonts/`. Variable fonts covering the declared weight ranges.
+
+### Type scale (derived from design files)
+
+| Use | Size | Weight | Font |
+| --------------- | ----------- | ------- | --------------------------------------- |
+| Display hero | 76–88px | 500 | display |
+| Display large | 56px | 500 | display |
+| Display medium | 44px | 500 | display |
+| Display small | 24px | 400–500 | display |
+| Lead / intro | 22px italic | 400 | serif |
+| Body large | 19px | 400 | serif |
+| Body | 16px | 400 | serif |
+| Body small | 15px | 400 | serif |
+| UI text | 14px | 400 | serif |
+| Caption | 13px | 400 | serif |
+| Mono caps large | 11px | 400 | mono + uppercase + tracking 0.18em |
+| Mono caps | 10px | 400 | mono + uppercase + tracking 0.12–0.15em |
+| Mono caps small | 9px | 400 | mono + uppercase + tracking 0.1em |
+
+## Atoms
+
+Source files: `web/src/lib/components/atoms/`
+
+### `MarktvogtMark`
+
+The shield-M logo. Props: `size` (default 32). Uses `currentColor` — inherits from parent element color.
+
+```svelte
+