Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ hugo.linux
# Other
.DS_Store
.*
!.okf
!.claude
.claude/**/*.json
.claude/**/*.sh
Expand Down
29 changes: 29 additions & 0 deletions .okf/build/hugo-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
type: Build Pipeline
title: Hugo build (bin/hugo-build)
description: Canonical build + validation entry point; runs Hugo plus the course validators including the banned-strings ratchet.
resource: bin/hugo-build
tags: [build, hugo, validation]
timestamp: 2026-07-13T00:00:00Z
---

`bin/hugo-build` builds the site into `public/` and runs the validation
suite. Zero build breaks is a hard rule - all content changes must pass it
before commit.

# Key facts

- Course pages get extra validators, including the banned-strings ratchet
driven by [data/course_banned_strings.yaml](/content/banned-strings-ratchet.md).
- The ratchet only sees rendered HTML text. It CANNOT see inside SVG artwork
or mermaid diagram labels - defects there need the
[visual scroll gate](/workflows/render-verification.md).
Comment on lines +18 to +20

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Correct the banned-string ratchet scope.

lib/course_validators.rb:349-367 reads each course chapter source file with File.read(path). The ratchet can therefore see Mermaid labels written in Markdown; only text baked into external SVG artwork is outside this check.

Proposed wording
- The ratchet only sees rendered HTML text. It CANNOT see inside SVG artwork
- or mermaid diagram labels - defects there need the
+ The ratchet scans course chapter source files, so it can catch Mermaid labels
+ written in Markdown, but it cannot inspect text baked into SVG artwork;
+ defects in rendered visuals still need the
  [visual scroll gate](/workflows/render-verification.md).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- The ratchet only sees rendered HTML text. It CANNOT see inside SVG artwork
or mermaid diagram labels - defects there need the
[visual scroll gate](/workflows/render-verification.md).
- The ratchet scans course chapter source files, so it can catch Mermaid labels
written in Markdown, but it cannot inspect text baked into SVG artwork;
defects in rendered visuals still need the
[visual scroll gate](/workflows/render-verification.md).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.okf/build/hugo-build.md around lines 18 - 20, Update the ratchet scope
description in the rendered HTML guidance to state that it can detect banned
strings in Mermaid labels and other Markdown source text read by the validator,
while text baked into external SVG artwork remains outside the check and
requires the visual scroll gate.

- Build twice as a control before blaming an edit for output flicker:
Hugo stats/PurgeCSS interactions can produce nondeterministic diffs
(term-casing races, header partialCached race).

# Examples

```bash
bin/hugo-build # build + validate
```
5 changes: 5 additions & 0 deletions .okf/build/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Build & Test

* [Hugo build pipeline](hugo-build.md) - bin/hugo-build with the 8 course validators
* [Test gates](test-gates.md) - the three suites and when each is a commit blocker
* [Template PDFs](pdf-templates.md) - regenerating the downloadable course PDFs
20 changes: 20 additions & 0 deletions .okf/build/pdf-templates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
type: Build Pipeline
title: Course template PDFs
description: bin/generate-template-pdfs regenerates the downloadable course PDFs from built pages; run it after editing any template chapter.
resource: bin/generate-template-pdfs
tags: [build, pdf, course]
timestamp: 2026-07-13T00:00:00Z
---

The course offers downloadable PDF versions of its template chapters
(worksheet, ownership checklist, and siblings). They are generated FROM the
built HTML pages, so any content change to a template chapter silently
stales its PDF.

# Steps

1. Edit the template chapter markdown.
2. `bin/hugo-build`
3. `bin/generate-template-pdfs`
4. Commit the regenerated PDFs together with the content change.
29 changes: 29 additions & 0 deletions .okf/build/test-gates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
type: Playbook
title: Test gates and when they block commits
description: bin/rake test:critical always; bin/test AND bin/dtest are both mandatory for any themes/, layouts/, or CSS change.
tags: [testing, visual-regression, gates]
timestamp: 2026-07-13T00:00:00Z
---

# The three suites

| Command | What it is | When required |
|---|---|---|
| `bin/rake test:critical` | Critical Minitest suite (46 runs / 84 screenshots) | After every change, before every commit |
| `bin/test` | Visual regression on macOS host (baselines in `test/fixtures/screenshots/macos/`) | Any edit to `themes/`, `layouts/`, `*.css`, or post body HTML |
| `bin/dtest` | Same suite in Linux/Docker (baselines in `linux/`) - CI runs Linux | Same trigger as bin/test; skipping it ships green-locally / red-in-CI |
Comment on lines +13 to +15

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not present the command matrix as the complete validation gate.

The table and “need only” wording omit mandatory path-specific checks: HTML/CSS/JS changes require Chrome DevTools validation, Markdown/SVG/image changes require desktop/mobile visual review, and .okf/ changes require /okf:validate .okf --strict. Scope this document to command suites or link the additional gates explicitly.

As per coding guidelines, these path-specific validation requirements are mandatory for the affected file types.

Also applies to: 25-26

🧰 Tools
🪛 LanguageTool

[uncategorized] ~14-~14: The operating system from Apple is written “macOS”.
Context: ... regression on macOS host (baselines in test/fixtures/screenshots/macos/) | Any edit to themes/, layouts/,...

(MAC_OS)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.okf/build/test-gates.md around lines 13 - 15, Update the validation
guidance in the command matrix and surrounding “need only” wording so it is not
presented as the complete gate. Explicitly link or include the mandatory
path-specific checks: Chrome DevTools validation for HTML/CSS/JS changes,
desktop/mobile visual review for Markdown/SVG/image changes, and `/okf:validate
.okf --strict` for `.okf/` changes.

Source: Coding guidelines


# Hard-won caveats

- The snapshot tool REWRITES baselines when a run passes. Never edit CSS
while a suite is running - a raced run once saved a corrupt baseline
missing its hero image. Catch with pixel-compare, restore via `git checkout`.
- Visual failures are commit blockers, not warnings. Either fix the
regression or update BOTH baseline dirs (macos/ and linux/) in the same
commit with the intentional change.
- Content-only + standalone-SVG waves need only
[bin/hugo-build](/build/hugo-build.md) + `test:critical`.
- Docker runs via Colima; fresh worktrees need `bun install` first.
- Tests must assert behavior shape (`q=\d+`, has `<picture>`), never tunable
config values (exact quality/width numbers).
25 changes: 25 additions & 0 deletions .okf/content/banned-strings-ratchet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
type: Validator
title: Banned-strings ratchet
description: data/course_banned_strings.yaml pins every fixed prose defect so it cannot silently regress; entries can be scoped to a single page.
resource: data/course_banned_strings.yaml
tags: [validation, course, regression]
timestamp: 2026-07-13T00:00:00Z
---

When a review sprint fixes a prose defect (a wrong tool name, a banned
phrase, a stale threshold), the exact defective string is added to
`data/course_banned_strings.yaml`. [bin/hugo-build](/build/hugo-build.md)
fails if it reappears in rendered course HTML.
Comment on lines +12 to +13

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Describe the actual scan target.

lib/course_validators.rb#check_banned_strings reads content/course/**/index.md and checks body.include?; it does not inspect rendered HTML. Mermaid source text in those Markdown files is scanned, while standalone SVG artwork is not. The current wording creates false confidence about the validator’s coverage.

Suggested wording
-[bin/hugo-build](/build/hugo-build.md)
-fails if it reappears in rendered course HTML.
+[bin/hugo-build](/build/hugo-build.md)
+fails if it reappears in source `content/course/**/index.md` files.
...
-- The ratchet reads rendered text only - it is blind to text inside SVG
-  artwork and mermaid labels.
+- The ratchet scans source Markdown, including Mermaid source text, but is
+  blind to text inside standalone SVG artwork.

Also applies to: 20-22

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.okf/content/banned-strings-ratchet.md around lines 12 - 13, Update the
banned-strings ratchet documentation around the data/course_banned_strings.yaml
and bin/hugo-build references to accurately state that
lib/course_validators.rb#check_banned_strings scans content/course/**/index.md
Markdown bodies, including Mermaid source, rather than rendered course HTML;
clarify that standalone SVG artwork is not inspected.


# Rules

- Entries support per-page scoping (e.g. "Airtable" is banned only on the
operating-kit page where it was a fabricated attribution) and exemptions
(rescue framing is allowed on Going Further rescue chapters).
Comment on lines +17 to +19

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove or implement the claimed exemption contract.

The supplied validator supports optional scope filtering, but no exemption field or rescue-framing exception. This documentation would cause maintainers to configure behavior the validator ignores.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.okf/content/banned-strings-ratchet.md around lines 17 - 19, Update the
banned-strings ratchet documentation to remove the unsupported
exemption/rescue-framing claim, or implement the corresponding exemption
behavior in the validator and document its configuration. Keep the documented
per-page scope behavior aligned with the validator’s existing scope filtering,
and do not describe configuration fields the validator ignores.

- The ratchet reads rendered text only - it is blind to text inside SVG
artwork and mermaid labels. Artwork defects are caught by the
[render-verification playbook](/workflows/render-verification.md) instead.
- After fixing any prose defect, grep your own replacement text for the
exact pattern you just removed - re-introducing the defect being fixed is
a blocking failure.
33 changes: 33 additions & 0 deletions .okf/content/course-canon.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
type: Reference
title: Course canon numbers
description: The single source of truth for every recurring number in the course; any chapter that contradicts these is defective.
tags: [course, canon, consistency]
timestamp: 2026-07-13T00:00:00Z
---

# Canonical values

| Fact | Canon |
|---|---|
| Founding Hypothesis score | ≥14/20 across four lenses |
| Smoke test traffic | 300 cold visitors |
| Interviews | 10 (from a 30-name list, expanded if replies run thin) |
| Build / pivot / kill gate | 7+ strong signals build · 4-6 pivot · under 4 kill |
| Prototype test subjects | 5 interview subjects |
| Sean Ellis must-have test | 40% - directional at ≥10 respondents, useful at 20+, sliceable at 30+ |
| Cold reply bands | 3-8% realistic; <5% stop and diagnose · 5-10% continue · >10% accelerate |
| Paid pilot deposit | $500+ refundable, via Stripe |
| Tracking tool | Google Sheet - never Airtable |
| AI token pass-through | disciplined $80-$120/dev/mo · undisciplined $300-$500; industry range $80-$300 |
| Slopsquatting source | Lasso Security research March 2025; "slopsquatting" coined by Infosecurity Magazine April 2025 |
Comment on lines +13 to +23

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Add citations to the canonical claims.

This source-of-truth table contains externally verifiable thresholds, pricing ranges, and research attributions without citations. Add an inline citation or authoritative internal reference for each applicable row, especially the Sean Ellis, pricing, and slopsquatting entries.

As per coding guidelines, Markdown assertions must have citations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.okf/content/course-canon.md around lines 13 - 23, Add inline citations or
authoritative internal references to every externally verifiable claim in the
canonical table, including thresholds, pricing ranges, research attributions,
and especially the Sean Ellis must-have test, AI token pass-through, paid pilot
deposit, and slopsquatting source rows. Preserve the existing table values while
making each applicable assertion traceable.

Source: Coding guidelines


# Recurring cast (keep consistent across chapters)

- **Mia** - the walkthrough protagonist (TutorMatch).
- **Marcos** (author) and **Priya** (reviewer) - the PR-review example pair.
Spelling is "Marcos", never "Marcus". PR #847 = the refund-branch review
story; the weekly-report admin-search example uses PR #843.

Structural home: [course structure](/content/course-structure.md).
Enforcement: [banned-strings ratchet](/content/banned-strings-ratchet.md).
34 changes: 34 additions & 0 deletions .okf/content/course-structure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
type: Content Architecture
title: Course structure (From Idea to First Paying Customer)
description: 5-module linear spine defined in data/course_sequence.yaml, plus a non-linear Going Further set, per-module Mia walkthroughs, and template chapters.
resource: content/course/tech-for-non-technical-founders-2026/
tags: [course, structure, hugo]
timestamp: 2026-07-13T00:00:00Z
---

# The spine

The reading order lives in `data/course_sequence.yaml` - Chapter 0
(how-this-course-works) then Modules 1-5 (hypothesis → smoke test →
interviews → brief/build → first paying customer). The prev/next strip,
branch-aware forks, and the `course-stat` shortcode all derive from that
file. Never hardcode exact chapter counts in prose; use near numbers
("20+ chapters").

# Outside the spine

- **Going Further set** (linked from the 5.7 closing callout): continuation
chapters (churn triage, pivot-or-persevere), the hire-track reference,
demoted management chapters (engineering org chart, friday demo rule,
three questions standup, weekly dev report), and the AI-in-production trio
(agency AI questions → AI token bill → slopsquatting, badged Step 1-3 of 3).
- **Mia walkthroughs**: one per module (`module-N-walkthrough-mia`).
- **Templates**: worksheet/checklist/script chapters with
[generated PDFs](/build/pdf-templates.md).
- **Reference deep-dives** under `reference/` (hypothesis-sprint-full,
smoke-test-channel-guide, stripe-price-test-full).
- **Floating glossary**: five-tech-words-stop-nodding-at, reachable from every
chapter, not in the linear order.

All chapter facts must agree with the [course canon](/content/course-canon.md).
6 changes: 6 additions & 0 deletions .okf/content/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Content

* [Course structure](course-structure.md) - the 5-module spine, Going Further set, and course_sequence.yaml
* [Course canon](course-canon.md) - the numbers every chapter must agree on
* [Voice rules](voice-rules.md) - Sam voice, banned patterns, and the em-dash rule
* [Banned-strings ratchet](banned-strings-ratchet.md) - how fixed prose defects stay fixed
42 changes: 42 additions & 0 deletions .okf/content/voice-rules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
type: Style Guide
title: Course voice rules (write for Sam)
description: The reader is Sam, an idea-stage non-technical first-time founder; plain-words glossing, dash style, and the banned structural patterns.
tags: [voice, style, course]
timestamp: 2026-07-13T00:00:00Z
---

Full guides: `docs/90-99-content-strategy/strategy-analysis/90.11-voice-guide.md`
and the course-specific rules in `CLAUDE.md`. This concept records the
rules that recur in review sprints.

# Core rules

- **Write for Sam, not Paul.** Sam is an idea-stage non-technical founder.
Course bodies never use rescue/trauma framing (that ICP is the website's
"Alex", not the course's Sam); the Going Further rescue chapters are the
exemption.
- **Gloss at first mention** - every acronym/tool/term gets a plain-words
parenthetical the first time it appears in the reading order (SOW, MSA,
SLA, FTE, PLG, ARPU, MAU, RAG-status, 0day...).
- **Dashes**: always "-", never "—" - in prose AND inside SVG/mermaid artwork.
- **Progressive disclosure**: orientation blocks orient; thresholds and
mechanics belong where the reader acts on them.
- **Callout rhythm**: no two adjacent same-form callouts.

# Banned structural patterns (reject on sight)

Slogany reveal-twist flips ("X wasn't Y - it was Z"), cinematic time-cut
narration, anonymous-founder-vignette openers repeated 3+ chapters in a row,
aphoristic flourish closers, fabricated cohort stats without a source,
`## Why this matters` headings, "Founders who / Most founders / Founders we
worked with", dual-source statistical openers, sustained staccato.

Sentence-level slop scoring is necessary but NOT sufficient - shape-tell
review (essay arc, pivot sentences, cloned scaffolding) runs alongside it.

Regression sweep before any handback:

```bash
grep -rn "## Why this matters\|Founders who\|Most founders\|Founders we worked with" content/course/
```
34 changes: 34 additions & 0 deletions .okf/design/course-typography.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
type: Design System
title: Course reading typography
description: Course pages read at 20px/1.65 body scale (blog keeps 22.4px); overrides must mirror the full single-post.css selector chain or they silently lose.
resource: themes/beaver/assets/css/style.css
tags: [css, typography, course]
timestamp: 2026-07-13T00:00:00Z
---

# The scale (course pages only)

| Element | Value |
|---|---|
| Body p / li | 20px / 1.65 |
| li margin | 12px |
| Blockquote/callout p | 19px / 1.6 |
| Worksheet card p, ws-checks li | 17px / 1.55 |
| ws-route p | 16.5px |

The blog keeps its thoughtbot-anchored 22.4px scale - the course override is
scoped with a `.section-course` prefix.

# The specificity trap

`single-post.css` sets body type via
`.blog article.single-content .fl-rich-text p` (specificity 0,3,2). Any
course override weaker than that silently loses - a plain
`.section-course .blog p` (0,2,1) does nothing. Mirror the FULL chain:
`.section-course .blog article.single-content .fl-rich-text p`.

Also: broad element rules (e.g. the 20px li rule) can invert component
hierarchies - the worksheet-card checklist items got bumped to 20px and had
to be re-pinned to 17px. After any type change, run both visual suites per
[test gates](/build/test-gates.md).
29 changes: 29 additions & 0 deletions .okf/design/cover-pipeline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
type: Playbook
title: Course cover rebuild pipeline
description: Covers are rebuilt as full HTML per .stitch/design.md rendered headless at 2400x1260; chip-composite for single-fact edits.
resource: .stitch/design.md
tags: [covers, design, stitch]
timestamp: 2026-07-13T00:00:00Z
---

Original covers were Stitch-made and their downloads are auth-gated, so the
working rebuild path is: author a full HTML page implementing the spec and
screenshot it headless at 2400x1260.

# Spec essentials (.stitch/design.md)

- Titles: Space Grotesk 800, letter-spacing -0.03em
- Chip labels: Inter 600, 22px, letter-spacing 0.14em
- Chip values: Space Grotesk 700
- Status line: JetBrains Mono
- Palette: JetVelocity - obsidian dark, Ruby red #cc342d, neon purple #a855f7

# Techniques

- Full rebuild: HTML file in scratchpad → headless Chrome screenshot →
`cover.png` (update the `cover_image_alt` frontmatter to match).
- Single-fact edit (one chip's wording): chip-composite - render just the
corrected chip and composite it over the existing PNG with magick.
- Watch sub-glyph grazes on chips ("%", trailing "E"); fix with explicit
`<br/>` splits and `&nbsp;` slack, verify with zoomed re-renders.
32 changes: 32 additions & 0 deletions .okf/design/house-visual-spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
type: Design System
title: House in-post visual spec
description: Hand-drawn SVG house style - paper tones, 2-2.5px strokes, semantic colors (green = money only), labels inside shapes.
resource: .stitch/prompts/course-inpost-visual-prompt.md
tags: [svg, design, course]
timestamp: 2026-07-13T00:00:00Z
---

# The spec in one table

| Element | Rule |
|---|---|
| Backgrounds | Paper tones: #fff5f5 (red-tint), #faf7f2 (cream), #f0f9f0 (green), #fbe9ff (purple) |
| Strokes | 2-2.5px, hand-drawn feel |
| Red | Action / anti-pattern |
| Purple | Alternate path |
| Green | ONLY money/success outcomes |
| Amber | Warnings |
| Typography | Caveat / Patrick Hand / Comic Sans MS cursive stack |
| Labels | INSIDE shapes (Sweller split-attention rule); never bare diamonds |
| Dashes | "-" only, including inside artwork |

# Exemplars

`invoice-loop.svg` (ai-token-bill) and `network-buckets.svg` are the
canonical compact hand-drawn exemplars - numbered step cards, outcome cards,
dashed loop, header + italic subtitle, ~960x470.

Diagrams taller than 2x viewport read as a wall, not a hook - replace with a
compact SVG or table. Verify every new visual with the
[render-verification playbook](/workflows/render-verification.md).
6 changes: 6 additions & 0 deletions .okf/design/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Design

* [Mermaid theme](mermaid-theme.md) - the Caveat webfont root-cause fix and the theming gotchas
* [House visual spec](house-visual-spec.md) - paper tones, semantic colors, hand-drawn identity
* [Cover pipeline](cover-pipeline.md) - rebuilding course covers from the design spec
* [Course typography](course-typography.md) - the course-scoped reading scale and the specificity trap
40 changes: 40 additions & 0 deletions .okf/design/mermaid-theme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
---
type: Design System
title: Mermaid house theme and the Caveat webfont fix
description: Mermaid renders in Caveat handwritten cursive, loaded explicitly and awaited via document.fonts.ready; label metrics gotchas documented.
resource: themes/beaver/layouts/baseof.html
tags: [mermaid, fonts, design]
timestamp: 2026-07-13T00:00:00Z
---

# Root cause worth remembering

The Caveat webfont was historically NEVER loaded - mermaid fell back to the
platform's generic cursive (Apple Chancery on macOS) and measured labels
with different metrics than it rendered, causing a whole class of recurring
last-character clipping. The fix in `baseof.html`:

1. Google Fonts Caveat `<link>` (+ preconnects), gated on `features.mermaid`.
2. `startOnLoad: false` and `document.fonts.ready.then(() => mermaid.run())`.
3. Font stack `'Caveat', 'Patrick Hand', 'Comic Sans MS', cursive` - mirrors
the in-post SVGs' fallback.

# Theming gotchas

- NEVER set font-size/padding on `.edgeLabel` via `themeCSS` - it applies
AFTER measurement and reintroduces the measure/render mismatch.
- Labels ending in wide glyphs (%) clip; append `&nbsp;` inside the label or
keep edge labels to 1-2 words.
- Bare diamond decision nodes are banned - use rects.
- Flowchart spacing: `nodeSpacing: 45, rankSpacing: 55, padding: 14`.
- Mermaid code fences render via
`themes/beaver/layouts/_markup/render-codeblock-mermaid.html`, which sets
`features.mermaid`.

# Brand rule

Handwritten typography in diagrams is a non-negotiable brand identity -
the owner rejected a clean-sans mermaid theme on sight the same day he had
approved it in the abstract. Fix legibility structurally (compact SVG,
table) rather than by changing the font. See the
[house visual spec](/design/house-visual-spec.md).
Loading
Loading