Theming

# Theming

The default theme owns presentation for public pages and may override
shared layout and component templates. Page templates themselves are not
themed.

## Logical assets

PHP requests built entries by logical name (`theme`, `islands`). Templates
never point at producer source paths. Production HTML references `/build/`
URLs from the Vite manifest.

## Markdown document island

`/docs/` pages insert the Markdown document component.

- `data-g5nx-island="markdown-document"`
- one `data-g5nx-island-fallback` wrapper around the static `h1` and the
  escaped `pre` (so a successful React mount does not leave a duplicate
  heading)
- empty mount sibling with `data-g5nx-island-root`
- escaped scalars `data-source-url` and `data-title`

Without JavaScript the escaped heading and Markdown remain visible.
`g5nx-diagram` fences are id-only contract markers. No-JS keeps them as
escaped fenced code. With JavaScript, a valid one-line id lazy-loads the
first-party diagram renderer inside the Markdown island.

When the islands entry loads, the renderer:

- fetches `data-source-url` as same-origin JSON only
- reads `data.slug`, `data.title`, `data.description`, `data.content`
- renders GitHub-flavored Markdown with fenced-code highlighting
- skips raw HTML in the Markdown source
- leaves the native fallback in place if the dynamic import or mount fails
- once React mounts, a Markdown fetch failure displays the accessible
  error and Retry UI; it does not restore the raw fallback

```php
$this->requireEntry('islands');
```

The shared native component (no island) still outputs an `h1` and an
escaped `pre`.