diff --git a/Justfile b/Justfile
index c92ba01..a2d4bdd 100644
--- a/Justfile
+++ b/Justfile
@@ -10,3 +10,9 @@ lint-ci: check-planning
test:
uv run pytest
+
+# Regenerate the brand kit and copy the subset the site serves into docs/assets.
+sync-assets:
+ uv run python -m brand.build.render
+ cp brand/org/favicon.svg brand/org/mark.svg brand/org/wordmark.svg brand/org/wordmark-dark.svg docs/assets/
+ cp brand/org/social-card-green.png docs/assets/
diff --git a/architecture/site-branding.md b/architecture/site-branding.md
new file mode 100644
index 0000000..f9d337d
--- /dev/null
+++ b/architecture/site-branding.md
@@ -0,0 +1,49 @@
+# Site branding assets
+
+How the org site (`modern-python.org`) gets its logo, favicon, hero wordmark, and
+social card. The canonical brand kit is generated by `brand/build/`; the site
+serves a copied subset under `docs/assets/`.
+
+## Pipeline
+
+1. `brand/build/` is the source of truth. `uv run python -m brand.build.render`
+ draws every asset from `brand/build/geometry.py` (vector geometry; text is
+ outlined to paths via the bundled Jost TTF, so nothing depends on a font at
+ serve time) into `brand/org/`.
+2. `just sync-assets` runs the render, then copies the site subset into
+ `docs/assets/`: `favicon.svg`, `mark.svg`, `wordmark.svg`, `wordmark-dark.svg`,
+ and `social-card-green.png`. (The site can only serve files under `docs/`, so
+ the copies are intentional duplication, not drift — regenerate via the recipe.)
+3. MkDocs Material consumes them (see wiring below).
+
+## Geometry building blocks (`brand/build/geometry.py`)
+
+- `icon()` — full-bleed square mark (favicon/avatar/apple-touch), with background.
+- `mark()` — the same chevron mark with **no background**, for the site header.
+- `lockup_body()` — the `MODERN`/`PYTHON` crop-mark wordmark, drawn in a 540x250
+ space, returned as bare markup for embedding.
+- `wordmark()` — `lockup_body` wrapped in a tight transparent viewBox
+ (`118 32 304 184`) for standalone use in the hero.
+- `social_card()` / `social_square()` — the wordmark composited onto a background.
+
+Two color roles flow through every function: `struct` (the cream/green-ink
+structure) and `gold` (the accent). The site uses two wordmark variants:
+green-ink + gold-light (`wordmark.svg`, light pages) and cream + gold-dark
+(`wordmark-dark.svg`, dark pages and the green header).
+
+## Site wiring
+
+- **Header** (`mkdocs.yml` `theme.logo: assets/mark.svg`) — the chevron mark, with
+ Material's default "Modern Python" site title (Roboto) and search beside it.
+- **Favicon** (`mkdocs.yml` `theme.favicon: assets/favicon.svg`).
+- **Hero** (`docs/index.md`) — both wordmark variants sit inside the page `
`
+ (`.mp-wordmark`); `extra.css` shows the light variant by default and swaps to
+ the dark variant under `[data-md-color-scheme="slate"]`. The `
` is load-
+ bearing: Material injects a fallback `
{{ page.title }}
` (which read
+ "Home" on the index) only when the rendered content has no `
`, so wrapping
+ the wordmark in one suppresses it.
+- **Social card** (`overrides/main.html`) — the `og:image` / `twitter:image`
+ meta points at `assets/social-card-green.png` (the green card).
+
+The brand palette and token roles live in `brand/build/tokens.py` and
+`brand/README.md`.
diff --git a/brand/build/geometry.py b/brand/build/geometry.py
index 7c4f48f..0801926 100644
--- a/brand/build/geometry.py
+++ b/brand/build/geometry.py
@@ -52,6 +52,24 @@ def lockup_body(*, struct: str, gold: str) -> str:
return crops + modern + python
+def wordmark(*, struct: str, gold: str) -> str:
+ """Standalone two-color MODERN/PYTHON wordmark for the site hero. Wraps
+ `lockup_body` (drawn in a 540x250 space) in a tight viewBox centered on the
+ content, with no background — transparent so it sits on any page surface."""
+ return (
+ '"
+ )
+
+
+def mark(*, struct: str, gold: str) -> str:
+ """The chevron mark on its own (no background) for the site header logo —
+ same glyph as `icon`, minus the full-bleed background rect."""
+ return _SVG_OPEN.format(w=100, h=100) + _icon_mark(struct, gold) + ""
+
+
def social_card(*, bg: str, struct: str, gold: str, url_color: str) -> str:
body = lockup_body(struct=struct, gold=gold)
url, _ = outline_text("modern-python.org", 34, x=640, baseline_y=575,
diff --git a/brand/build/render.py b/brand/build/render.py
index da60416..29244b7 100644
--- a/brand/build/render.py
+++ b/brand/build/render.py
@@ -51,6 +51,13 @@ def render() -> None:
g.icon_circle(bg=t.GREEN_SURFACE, struct=t.CREAM, gold=t.GOLD_DARK))
export_png(ORG / "avatar-circle.svg", ORG / "avatar-circle-1024.png", width=1024, height=1024)
+ # Site logos — transparent, no background.
+ # wordmark (hero): two-color lockup, light + dark variants
+ # mark (header): chevron mark in cream/gold-dark for the green header bar
+ _write(ORG / "wordmark.svg", g.wordmark(struct=t.GREEN_INK, gold=t.GOLD_LIGHT))
+ _write(ORG / "wordmark-dark.svg", g.wordmark(struct=t.CREAM, gold=t.GOLD_DARK))
+ _write(ORG / "mark.svg", g.mark(struct=t.CREAM, gold=t.GOLD_DARK))
+
# Social cards — cream (primary) + green (alternate).
_write(ORG / "social-card.svg",
g.social_card(bg=t.CREAM, struct=t.GREEN_INK, gold=t.GOLD_LIGHT, url_color=t.GOLD_LIGHT))
diff --git a/brand/org/mark.svg b/brand/org/mark.svg
new file mode 100644
index 0000000..c31e2ff
--- /dev/null
+++ b/brand/org/mark.svg
@@ -0,0 +1 @@
+
diff --git a/brand/org/wordmark-dark.svg b/brand/org/wordmark-dark.svg
new file mode 100644
index 0000000..495d9f5
--- /dev/null
+++ b/brand/org/wordmark-dark.svg
@@ -0,0 +1 @@
+
diff --git a/brand/org/wordmark.svg b/brand/org/wordmark.svg
new file mode 100644
index 0000000..d3c2846
--- /dev/null
+++ b/brand/org/wordmark.svg
@@ -0,0 +1 @@
+
diff --git a/docs/assets/modern-python-favicon.svg b/docs/assets/favicon.svg
similarity index 100%
rename from docs/assets/modern-python-favicon.svg
rename to docs/assets/favicon.svg
diff --git a/docs/assets/mark.svg b/docs/assets/mark.svg
new file mode 100644
index 0000000..c31e2ff
--- /dev/null
+++ b/docs/assets/mark.svg
@@ -0,0 +1 @@
+
diff --git a/docs/assets/modern-python-mark.svg b/docs/assets/modern-python-mark.svg
deleted file mode 100644
index e441c2a..0000000
--- a/docs/assets/modern-python-mark.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/docs/assets/modern-python-white.svg b/docs/assets/modern-python-white.svg
deleted file mode 100644
index 77b32d8..0000000
--- a/docs/assets/modern-python-white.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/docs/assets/modern-python.png b/docs/assets/modern-python.png
deleted file mode 100644
index d7201c9..0000000
Binary files a/docs/assets/modern-python.png and /dev/null differ
diff --git a/docs/assets/modern-python.svg b/docs/assets/modern-python.svg
deleted file mode 100644
index 7078a37..0000000
--- a/docs/assets/modern-python.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/docs/assets/social-card-green.png b/docs/assets/social-card-green.png
new file mode 100644
index 0000000..37eef6c
Binary files /dev/null and b/docs/assets/social-card-green.png differ
diff --git a/docs/assets/social-card.png b/docs/assets/social-card.png
deleted file mode 100644
index 5b64619..0000000
Binary files a/docs/assets/social-card.png and /dev/null differ
diff --git a/docs/assets/wordmark-dark.svg b/docs/assets/wordmark-dark.svg
new file mode 100644
index 0000000..495d9f5
--- /dev/null
+++ b/docs/assets/wordmark-dark.svg
@@ -0,0 +1 @@
+
diff --git a/docs/assets/wordmark.svg b/docs/assets/wordmark.svg
new file mode 100644
index 0000000..d3c2846
--- /dev/null
+++ b/docs/assets/wordmark.svg
@@ -0,0 +1 @@
+
diff --git a/docs/index.md b/docs/index.md
index 922c04f..1c5d487 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,4 +1,5 @@
---
+title: Modern Python
hide:
- navigation
- toc
@@ -6,16 +7,15 @@ hide:
-
+
+
+
+
Open-source templates and libraries for building production-ready Python
applications — web services, microservices, and the dependency injection that
wires them together.
-
@@ -116,3 +116,7 @@ catalog below.
- [`db-retry`](https://github.com/modern-python/db-retry) — retry helpers for database operations.
- [`eof-fixer`](https://github.com/modern-python/eof-fixer) — automatically fix newlines at the end of files.
- [`semvertag`](https://github.com/modern-python/semvertag) — auto-tag your GitHub/GitLab repo with semantic version tags from CI.
+
+