Recipe cards were rendering full directions, all nutrition chips,
prep notes, swap candidates, and grocery links inline in the grid —
making each card tall enough to push the second row below the fold at
3-column widths. Cards now show title, match/complexity/time badges,
up to 4 pantry ingredient chips, missing count, and calorie hint.
Full detail remains in RecipeDetailPanel on "Make this".
ElementClassifier.classify_batch() was issuing N separate DB queries
(one per pantry item). Replaced with a single WHERE name IN (...)
query + heuristic fallback for misses — same result, one round-trip.
Cloud mode: attach shared read-only corpus DB (RECIPE_DB_PATH env var)
as "corpus" schema so per-user SQLite DBs can access 3.19M recipes.
All corpus table references now use self._cp prefix ("corpus." in cloud,
"" in local). FTS5 pseudo-column kept unqualified per SQLite spec.
compose.cloud.yml: bind-mount /Library/Assets/kiwi/kiwi.db read-only.
Also fix batch of audit issues:
- #101: OCR approval used source="receipt_ocr" for inventory_items — use "receipt"
- #89/#100: Shopping confirm-purchase used source="shopping_list" — use "manual"
- #103: Frontend inventory filter sent ?status= but API expects ?item_status=
- #104: InventoryItemUpdate schema missing purchase_date field; store.py allowed set also missing it
- #105: Guest cookie Secure flag tied to CLOUD_MODE instead of X-Forwarded-Proto; broke HTTP direct-port access