Public HTML in the clone
The docs plugin owns the data layer: the doc_pages table, the Doc model, the DocResource, and the Arrange Docs screen. It does not ship a themed docs chrome, a sidebar Blade file, or a landing page. Public HTML lives in the clone, under resources/views/site/*. This is the split recorded in ADR 0008: the plugin is look-neutral, the clone is the product.
The recipe
In the clone, add a route for /docs/{path} to a PublicPageController. The controller loads the published Doc by path, renders the clone's docs view, and passes the cached outline so the sidebar partial can render without re-querying.
Route::get('/docs/{path}', [PublicPageController::class, 'docs'])
->where('path', '.+');
The controller resolves the doc, checks publication, and falls back to a 404 for guests on drafts. Staff preview is the same route: a logged-in editor or super_admin (or the row's author) sees the draft; a guest gets 404 on anything unpublished.
Public path
Doc::publicPath() returns /docs/{path} by default. If the product uses another prefix (for example /handbook/{path}), override it in the clone rather than forking the plugin. See ticket 06 for the planned Plugin::make()->publicPrefix(...) config; until then, a one-line subclass in the clone is enough.
Sidebar
The sidebar partial renders the cached outline recursively, with no depth limit. The outline is produced by Arrange Docs and cached; the public page reads the cache, never the live tree. The index page indents three levels; deeper pages still appear, just not with extra indent on the index.
Request flow
flowchart LR
A["GET /docs/{path}"] --> B["PublicPageController"]
B --> C{"Doc by path\npublished?"}
C -- yes --> D["Clone docs view\n+ sidebar partial"]
C -- draft + staff --> D
C -- draft + guest --> E["404"]
What stays in the plugin
The doc_pages migration, the Doc model, DocResource, and the Arrange Docs action stay in filament-docs. The clone never re-declares those. It only adds routes, controllers, and Blade for its own look.