diff --git a/CHANGELOG.md b/CHANGELOG.md
index 69a7e4e..03fd25f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,6 +37,9 @@ turns the section for that version into the release notes shown in the app.
fix still corrects spelling with macOS's own spell checker, leaving names
and your taught words alone.
+### Changed
+- The icon's bars are a little heavier, to match the wordmark (GDR-0021).
+
### Fixed
- Fix, shorten and plain no longer touch Slack mentions and channels, links,
email addresses, dates, times, units, versions or abbreviations like
diff --git a/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_1024.png b/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_1024.png
index 40eafe3..5b94f41 100644
Binary files a/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_1024.png and b/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_1024.png differ
diff --git a/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_128.png b/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_128.png
index 579ec62..4533224 100644
Binary files a/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_128.png and b/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_128.png differ
diff --git a/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_16.png b/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_16.png
index 0d42cbe..78ed7f5 100644
Binary files a/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_16.png and b/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_16.png differ
diff --git a/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_256.png b/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_256.png
index 18482fa..ae58bdb 100644
Binary files a/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_256.png and b/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_256.png differ
diff --git a/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_32.png b/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_32.png
index 1e38371..d88e01d 100644
Binary files a/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_32.png and b/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_32.png differ
diff --git a/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_512.png b/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_512.png
index 6a03be9..955fabe 100644
Binary files a/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_512.png and b/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_512.png differ
diff --git a/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_64.png b/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_64.png
index f0caca5..f289928 100644
Binary files a/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_64.png and b/apps/Timbre/Sources/Assets.xcassets/AppIcon.appiconset/icon_64.png differ
diff --git a/docs/decisions/README.md b/docs/decisions/README.md
index 0a8ee05..0f24eb3 100644
--- a/docs/decisions/README.md
+++ b/docs/decisions/README.md
@@ -65,3 +65,5 @@ Two kinds of records live here. The boundary rule:
| [0018](gdr/0018-words-appear-as-you-speak.md) | Words appear as you speak; the cleanup replaces them on release | Accepted |
| [0019](gdr/0019-outfit-and-a-simpler-drawing.md) | Outfit, served by the site; the keyboard goes; keys are flat (amends 0017) | Accepted |
| [0020](gdr/0020-nunito.md) | Nunito: the rounded face (amends 0019) | Accepted |
+| [0021](gdr/0021-the-wordmark.md) | The wordmark: bars beside "timbre", stroke 3; an underlined secondary (amends 0019, 0017) | Accepted |
+| [0022](gdr/0022-nunito-sans-for-text.md) | Nunito Sans for text; Nunito keeps the headlines (amends 0020) | Accepted |
diff --git a/docs/decisions/gdr/0021-the-wordmark.md b/docs/decisions/gdr/0021-the-wordmark.md
new file mode 100644
index 0000000..cef0802
--- /dev/null
+++ b/docs/decisions/gdr/0021-the-wordmark.md
@@ -0,0 +1,23 @@
+# 0021 — The wordmark: the bare bars beside "timbre", stroke 3, with an underlined secondary
+
+- **Status:** Accepted
+- **Date:** 2026-10-14
+- **Amends:** [GDR-0019](0019-outfit-and-a-simpler-drawing.md), point 5; [GDR-0017](0017-retro-identity-one-colour-per-gesture.md), point 3 (where the rainbow sits)
+
+## Context
+
+With Nunito chosen (GDR-0020), the wordmark was laid out on the Brand room in every arrangement that was still open: title case and lowercase, the mark tiled and bare, the rounded rainbow under the word, beside it, or absent, and the bars at three weights. Each round narrowed by looking: lowercase first, then the tile dropped beside the word ("if we show the icon with the wordmark we shouldn't show it with a background"), then the doubled rainbow dropped, then the weight.
+
+## Decision
+
+1. **The primary wordmark is the bare bars beside "timbre"**, lowercase, Nunito at its heaviest, no underline. The rainbow appears once in a lockup: in the bars. **The secondary is "timbre" with the rounded rainbow beneath it**, no bars — for the places the tiled icon already stands (an App Store listing, a window with the icon in its title) or where the bars would repeat. Never both rainbows in one lockup.
+2. **The bars are drawn at stroke 3 of the 32-unit grid**, up from 2.6, everywhere they appear: the favicon, the app icon, the header, the share image. Heavy enough to sit beside Nunito's extra-bold; light enough that the sixteen-pixel icon keeps its gaps.
+3. **The tile stays for the icon alone** — the favicon, the Dock, the update prompt — where a box is wanted. Beside the word the tile would be a second box.
+4. **The name in prose stays "Timbre."** Lowercase is the drawing, not the spelling.
+5. The rounded rainbow underline keeps its place in the headline, under one word, and the stripe keeps its places between sections. The primary wordmark does not carry it; the secondary is where it belongs.
+
+## Consequences
+
+- One lockup, drawn once (`Mark.svelte` with `tile={false}`), used by the site header and footer, the Backstage sidebar, the login card and the share image. The secondary is the same word with the `rainbow` bar beneath; the Brand room shows both.
+- The app icon set and the favicon are regenerated from the same stroke; `tools/app-icon.swift` and `tools/og-image.swift` carry the number.
+- The Brand room keeps every round so the next question starts from the comparison.
diff --git a/docs/decisions/gdr/0022-nunito-sans-for-text.md b/docs/decisions/gdr/0022-nunito-sans-for-text.md
new file mode 100644
index 0000000..e180808
--- /dev/null
+++ b/docs/decisions/gdr/0022-nunito-sans-for-text.md
@@ -0,0 +1,21 @@
+# 0022 — Nunito Sans for text; Nunito keeps the headlines
+
+- **Status:** Accepted
+- **Date:** 2026-10-14
+- **Amends:** [GDR-0020](0020-nunito.md), point 1
+
+## Context
+
+Nunito everywhere was friendly and, at paragraph size, a little tiring: rounded terminals on every letter of a long line. Six pairings went on the Brand room, Nunito over a text face each, with the wordmark, a headline, a paragraph and a button. Nunito Sans was chosen, with Inter named as also good.
+
+## Decision
+
+1. **Headlines and the wordmark stay Nunito**, at its heaviest, as GDR-0020 and GDR-0021 have it. They are `--font-display`.
+2. **Text is Nunito Sans**: regular for paragraphs, semibold for emphasis and buttons. It was drawn as Nunito's companion — the same proportions, square terminals — so the two read as one voice at two sizes. It is `--font-sans`.
+3. **Inter is the recorded runner-up.** If Nunito Sans ever reads wrong, Inter is the next thing tried, not another round.
+4. Both are served by the site, under the OFL, like everything else in `web/static/fonts`.
+
+## Consequences
+
+- Two faces, one family, no decorative third. Labels stay IBM Plex Mono.
+- The Brand room marks the pairing in use and keeps the other five.
diff --git a/tools/app-icon.swift b/tools/app-icon.swift
index 0bd4234..b825982 100644
--- a/tools/app-icon.swift
+++ b/tools/app-icon.swift
@@ -56,7 +56,7 @@ NSBezierPath(roundedRect: NSRect(x: inset, y: inset, width: tileSide, height: ti
let grid = tileSide / 32
let centres: [Double] = [8, 13.3, 18.6, 24]
let heights: [Double] = [6, 14, 9, 3]
-let width = 2.6 * grid
+let width = 3.0 * grid
for (i, cx) in centres.enumerated() {
let h = heights[i] * grid + width // round caps extend a stroke by half its width each end
let x = inset + cx * grid - width / 2
diff --git a/tools/og-image.swift b/tools/og-image.swift
index c6b6554..e04458c 100644
--- a/tools/og-image.swift
+++ b/tools/og-image.swift
@@ -72,7 +72,7 @@ func drawMark(x: Double, y: Double, side: Double) {
caseColor.setFill()
NSBezierPath(roundedRect: NSRect(x: x, y: y, width: side, height: side), xRadius: side * 7 / 32, yRadius: side * 7 / 32).fill()
let grid = side / 32
- let barWidth = 2.6 * grid
+ let barWidth = 3.0 * grid
for (i, cx) in [8.0, 13.3, 18.6, 24.0].enumerated() {
let h = [6.0, 14.0, 9.0, 3.0][i] * grid + barWidth
[rainbow[0], rainbow[2], rainbow[3], rainbow[4]][i].setFill()
@@ -81,9 +81,19 @@ func drawMark(x: Double, y: Double, side: Double) {
}
drawMark(x: 880, y: 200, side: 260)
-// Wordmark, with the small mark beside it, as the header shows it.
-drawMark(x: 80, y: 446, side: 64)
-draw("Timbre", at: NSPoint(x: 160, y: 436), font: nunito(72, weight: 800), color: ink, kern: -1)
+// The tiled icon already stands on this card, so the wordmark is the
+// secondary: the word with the rounded rainbow beneath, no bars (GDR-0021).
+let wordmark = nunito(76, weight: 800)
+draw("timbre", at: NSPoint(x: 80, y: 430), font: wordmark, color: ink, kern: -1.5)
+let wordWidth = size(of: "timbre", font: wordmark, kern: -1.5).width
+let wordBand = wordWidth / Double(rainbow.count)
+NSGraphicsContext.saveGraphicsState()
+NSBezierPath(roundedRect: NSRect(x: 82, y: 420, width: wordWidth - 4, height: 9), xRadius: 4.5, yRadius: 4.5).addClip()
+for (i, color) in rainbow.enumerated() {
+ color.setFill()
+ NSRect(x: 82 + Double(i) * wordBand, y: 420, width: wordBand + 0.5, height: 9).fill()
+}
+NSGraphicsContext.restoreGraphicsState()
// The claim, wrapped by hand, with the rainbow under "never".
let headline = nunito(58, weight: 800)
@@ -91,10 +101,13 @@ draw("Dictation that", at: NSPoint(x: 80, y: 318), font: headline, color: ink, k
draw("never leaves your Mac.", at: NSPoint(x: 80, y: 250), font: headline, color: ink, kern: -1)
let neverWidth = size(of: "never", font: headline, kern: -1).width
let band = neverWidth / Double(rainbow.count)
+NSGraphicsContext.saveGraphicsState()
+NSBezierPath(roundedRect: NSRect(x: 80, y: 242, width: neverWidth, height: 8), xRadius: 4, yRadius: 4).addClip()
for (i, color) in rainbow.enumerated() {
color.setFill()
- NSRect(x: 80 + Double(i) * band, y: 246, width: band + 0.5, height: 6).fill()
+ NSRect(x: 80 + Double(i) * band, y: 242, width: band + 0.5, height: 8).fill()
}
+NSGraphicsContext.restoreGraphicsState()
draw("Zero network requests* · No account · Free · Open source", at: NSPoint(x: 82, y: 170), font: mono, color: muted)
draw("timbre.hugopretorius.dev", at: NSPoint(x: 82, y: 124), font: mono, color: ink)
diff --git a/web/src/app.css b/web/src/app.css
index 3dfc8de..a8aba45 100644
--- a/web/src/app.css
+++ b/web/src/app.css
@@ -156,7 +156,8 @@
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
- --font-sans: "Nunito", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ --font-sans: "Nunito Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ --font-display: "Nunito", "Nunito Sans", -apple-system, BlinkMacSystemFont, sans-serif;
--font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
}
@@ -169,12 +170,13 @@
-webkit-font-smoothing: antialiased;
}
/* Headlines: Nunito at its heaviest, rounded terminals and all — the
- friendliness Hugo asked for, in the same family as the text so the
- page has one voice (GDR-0020). Rounded faces want less tightening. */
+ friendliness Hugo asked for (GDR-0020). Text is Nunito Sans, its own
+ companion: the same bones, square terminals, easier at small sizes
+ (GDR-0022). Rounded faces want less tightening. */
h1,
h2,
h3 {
- font-family: var(--font-sans);
+ font-family: var(--font-display);
font-weight: 800;
letter-spacing: -0.015em;
line-height: 1.08;
@@ -187,7 +189,7 @@
/* The headline cut, for anything that is not a heading but should carry it. */
@utility display {
- font-family: var(--font-sans);
+ font-family: var(--font-display);
font-weight: 800;
letter-spacing: -0.015em;
line-height: 1.08;
@@ -215,21 +217,32 @@
var(--retro-violet) 83.33% 100%
);
}
-/* A word underlined with the rainbow: the one emphasis the headline gets. */
-@utility underline-rainbow {
- background-image: linear-gradient(
- 90deg,
- var(--retro-red) 0 16.66%,
- var(--retro-orange) 16.66% 33.33%,
- var(--retro-yellow) 33.33% 50%,
- var(--retro-green) 50% 66.66%,
- var(--retro-blue) 66.66% 83.33%,
- var(--retro-violet) 83.33% 100%
- );
- background-repeat: no-repeat;
- background-size: 100% 0.14em;
- background-position: 0 92%;
- padding-bottom: 0.04em;
+/* A word underlined with the rainbow: the one emphasis the headline gets.
+ A rounded bar beneath the word (GDR-0020's rounded face wants rounded
+ ends everywhere the rainbow sits). */
+@layer components {
+ .underline-rainbow {
+ position: relative;
+ display: inline-block;
+ }
+ .underline-rainbow::after {
+ content: "";
+ position: absolute;
+ left: 0.02em;
+ right: 0.02em;
+ bottom: -0.06em;
+ height: 0.14em;
+ border-radius: 999px;
+ background-image: linear-gradient(
+ 90deg,
+ var(--retro-red) 0 16.66%,
+ var(--retro-orange) 16.66% 33.33%,
+ var(--retro-yellow) 33.33% 50%,
+ var(--retro-green) 50% 66.66%,
+ var(--retro-blue) 66.66% 83.33%,
+ var(--retro-violet) 83.33% 100%
+ );
+ }
}
@utility rainbow-text {
background-image: linear-gradient(
diff --git a/web/src/lib/assets/favicon.svg b/web/src/lib/assets/favicon.svg
index adf76b6..6ac7a4e 100644
--- a/web/src/lib/assets/favicon.svg
+++ b/web/src/lib/assets/favicon.svg
@@ -3,7 +3,7 @@
mark as the app icon (tools/app-icon.swift): charcoal case, four bars
in the retro accents (GDR-0017). -->
-
+
diff --git a/web/src/lib/components/marketing/Mark.svelte b/web/src/lib/components/marketing/Mark.svelte
index 13a5e7d..60412f9 100644
--- a/web/src/lib/components/marketing/Mark.svelte
+++ b/web/src/lib/components/marketing/Mark.svelte
@@ -1,20 +1,25 @@
-