Add flat, many-to-many categories to Weiro. Categories are managed via a dedicated admin page and assigned to posts on the post edit form. On the published static site, categories appear as labels on posts, archive pages per category, a category index page, and per-category RSS/JSON feeds. Categories with no published posts are hidden from the published site.
- A multi-select checkbox list of all available categories (sorted alphabetically by name), displayed in a **right sidebar** alongside the main title/body editing area on the left.
- Full Markdown description rendered below the heading
- List of published posts in the category, ordered by `published_at` descending
### Post Pages
Each post page displays its category names as clickable links to the corresponding category archive pages.
### Feeds
Per-category feeds:
-`/categories/<slug>/feed.xml` (RSS)
-`/categories/<slug>/feed.json` (JSON Feed)
Main site feeds (`/feed.xml`, `/feed.json`) gain category metadata on each post entry.
### Empty Category Handling
Categories with no published posts are hidden from the published site: no index entry, no archive page, no feed generated. They remain visible and manageable in the admin UI.
## SQL Queries
New file: `sql/queries/categories.sql`
-`SelectCategoriesOfSite` — all categories for a site, ordered by name
-`SelectCategory` — single category by ID
-`SelectCategoryByGUID` — single category by GUID
-`SelectCategoriesOfPost` — categories for a given post (via join table)
-`CreateCategory(ctx, params) (*Category, error)` — auto-generates slug from name. If the slug collides with an existing one for the same site, return a validation error.
-`UpdateCategory(ctx, params) (*Category, error)` — same slug collision check on update.
-`UpdatePost` — after saving the post, deletes existing `post_categories` rows and re-inserts for the selected category IDs. The post save and category reassignment must run within a single database transaction to ensure atomicity.
Saving or deleting a category queues a site rebuild, same as post state changes.
## DB Provider
`providers/db/` gains wrapper methods for all new sqlc queries, following the same pattern as existing post methods (e.g. `SaveCategory`, `SelectCategoriesOfPost`, etc.).
- **Hard delete for categories** — unlike posts which use soft-delete, categories are hard-deleted. They are simpler entities and don't need a trash/restore workflow.
- **No sort_order column** — categories are sorted alphabetically by name. Manual ordering can be added later if needed.
- **Existing microblog-crosspost feed** — kept as-is. Per-category feeds are a separate, additive feature.