# Content Authoring Spec

This site is a no-build static site. Public content lives in Markdown under `content/` and is rendered by browser-side modules.

## Home Content

Home content lives in `content/home/`.

- `content/home/index.json` is the ordered manifest for visible home sections and optional section tags.
- `content/home/meta.md` controls the page title, social links, and call-to-action link.
- `content/home/sections/*.md` contains one visible home section per file.
- To edit a section, change only that section file.
- To add a section, add a Markdown file under `content/home/sections/` and add its relative path to `content/home/index.json`.
- Section manifest entries can be plain paths or objects with `path` and `tags`. Use 2 essential tags by default and at most 4 when broader context helps the left rail.

Home section manifest format:

```json
{
  "path": "sections/about.md",
  "tags": ["Independent", "Interfaces", "Systems"]
}
```

Home section format:

```markdown
# Section title

Section summary or opening paragraph.

## Revealable note

Note body.

- Optional list item
- Optional list item
```

The first heading in a section file becomes the visible section title. Deeper headings become revealable notes inside that section. Duplicate section IDs are suffixed automatically.

## Blog Posts

Blog posts live in `content/blog/posts/`.

- Add a post by dropping a `.md` file into `content/blog/posts/`.
- File names should start with a publish date: `YYYY-MM-DD-slug.md`.
- The blog sorts newest first by `date`.
- The post slug comes from `slug:` front matter when present, otherwise from the filename with the leading date removed.
- Duplicate post slugs are suffixed automatically, for example `post`, `post-2`, `post-3`.

Blog post format:

```markdown
---
title: Post title
date: 2026-06-08
topic: Site notes
summary: One short sentence for the post header.
---

Post body starts here.

## Section heading

- Bullet item
- Another bullet item
```

Front matter is intentionally simple `key: value` text. Supported keys are `title`, `date`, `topic`, `summary`, `description`, and `slug`.

## Markdown Support

The renderer supports a small Markdown subset:

- Paragraphs separated by blank lines.
- Unordered list items starting with `- `.
- Inline links like `[Label](https://example.com)`.
- Inline code using backticks.
- Bold text using `**text**`.
- Blog body headings: `##` and `###`.
- Single-line Vimeo iframe embeds from `https://player.vimeo.com/video/...`.

Links are sanitized before rendering. Only `http:`, `https:`, and `mailto:` URLs become anchors. Unsupported or unsafe link URLs render as plain label text.

Vimeo embeds are also sanitized. Only `https://player.vimeo.com/video/{numeric-id}` iframe `src` values render as embedded media.

## Discovery And Deployment

Home uses an explicit manifest because section order matters. Blog posts are discovered automatically:

- Local development reads the directory listing for `/content/blog/posts/`.
- Production reads generated same-page archive data from `blog/index.html`, with generated Markdown alternate links as a static fallback.

Discovery surfaces and embedded page-data blocks are generated projections of public content, not separate source-of-truth content. The home page embeds only first-layer shell data and lazy-loads section bodies from Markdown when opened; the blog embeds its post archive for static production rendering. Edit Markdown sources and manifests first, then regenerate discovery.

When adding or removing public Markdown sources, run:

```bash
node tools/update-discovery.mjs
```

This updates these discovery surfaces in the same change:

- `sitemap.xml`
- `llms.txt`
- `llms-full.txt`
- `agents.txt`
- Static discovery and embedded page-data blocks in `index.html` and `blog/index.html`

Generated HTML blocks are marked with comments such as:

```html
<!-- discovery:home-alternates:start -->
<!-- discovery:home-alternates:end -->
```

Do not edit inside generated discovery blocks by hand. Update `content/home/index.json`, files under `content/home/`, or files under `content/blog/posts/`, then rerun the generator.

Use this command to fail on drift without rewriting files:

```bash
node tools/update-discovery.mjs --check
```

When adding a public page or changing the authoring contract, also update:

- `README.md`
- `AGENTS.md`
- `CONTEXT.md`

## Verification

Run from the repository root:

```bash
node tests/content-modules.test.mjs
python -m http.server 5173
```

To check discovery drift without rewriting files:

```bash
node tools/update-discovery.mjs --check
```

Then check:

- `http://localhost:5173/`
- `http://localhost:5173/blog/`
- `http://localhost:5173/sitemap.xml`
- `http://localhost:5173/llms.txt`

The site has no package manager, build command, bundler, or server runtime.
