This issue delivers pansy's core vision: click into a bed, plop garlic in one corner and basil in another, zoom out, and still see what's planted where. It builds focus mode, plop placement/manipulation, and semantic zoom on top of the field editor (#11), the PlantPicker (#13), and the plantings API (#14).
Scope
Focus mode: click/tap a plantable object (that isn't being dragged) → animate zoomToFit(object), set focusedObjectId + ?focus= URL param, dim/desaturate everything else. Escape / tap outside / a breadcrumb button exits (animates back to full-garden view). Same canvas, no separate route.
Placing: in focus mode, a "plant" action opens PlantPicker (#13); after choosing, click/tap inside the object drops a plop there — default radius max(1.5 × spacing_cm, 15cm), converted into the object's local frame before POSTing. Repeat-place stays armed until Escape/done.
Manipulating: select a plop → drag to move; a radius handle resizes; inspector (reuse #11's panel/sheet) shows plant (changeable via picker), radius, count (shows derived value greyed; typing sets the override; clearing restores derived), label, planted date, "remove" (sets removed_at, distinct from hard delete). Optimistic sync with the same one-PATCH-per-gesture + 409-rollback pattern as #11.
PlopMarker semantic zoom (web/src/editor/PlopMarker.tsx), three bands on viewport scale (start at <0.75 / 0.75–3 / >3 px per cm; tune by feel and keep thresholds as constants):
far: flat circle in the plant's color, no text — plus, on plantable objects, a name label + dominant-plant tint so beds read at a glance;
mid: colored circle + emoji icon;
near: circle + emoji + plant name + count (derived or override).
Rotation correctness: plops render inside the object's rotated group so they track the object; verify moving/rotating a bed in field view carries its plops (no planting writes occur).
Out of scope
Read-only mode (#17). Clear-bed bulk action (#18). Region fills (#19). Plop overlap rules (none by design).
The DESIGN.md scenario end-to-end on desktop AND a phone: create a bed, focus into it, place a few garlic plops in one corner, basil in another, beans along a side; exit focus; zoom out — colors/labels identify the plantings; zoom mid — icons appear; zoom in — names + counts. Reload: identical.
Rotate the bed 45° in field view — plops stay in their corners.
Resizing a plop updates its displayed derived count live; setting an override sticks.
"Remove" hides the plop everywhere and survives reload (row kept with removed_at).
Part of epic #20 · Design: [DESIGN.md](https://gitea.stevedudenhoeffer.com/steve/pansy/src/branch/main/DESIGN.md)
## Context
**This issue delivers pansy's core vision**: click into a bed, plop garlic in one corner and basil in another, zoom out, and still see what's planted where. It builds focus mode, plop placement/manipulation, and semantic zoom on top of the field editor (#11), the PlantPicker (#13), and the plantings API (#14).
## Scope
- [ ] **Focus mode**: click/tap a plantable object (that isn't being dragged) → animate `zoomToFit(object)`, set `focusedObjectId` + `?focus=` URL param, dim/desaturate everything else. Escape / tap outside / a breadcrumb button exits (animates back to full-garden view). Same canvas, no separate route.
- [ ] **Placing**: in focus mode, a "plant" action opens `PlantPicker` (#13); after choosing, click/tap inside the object drops a plop there — default radius `max(1.5 × spacing_cm, 15cm)`, converted into the object's **local frame** before POSTing. Repeat-place stays armed until Escape/done.
- [ ] **Manipulating**: select a plop → drag to move; a radius handle resizes; inspector (reuse #11's panel/sheet) shows plant (changeable via picker), radius, count (shows derived value greyed; typing sets the override; clearing restores derived), label, planted date, "remove" (sets `removed_at`, distinct from hard delete). Optimistic sync with the same one-PATCH-per-gesture + 409-rollback pattern as #11.
- [ ] **`PlopMarker` semantic zoom** (`web/src/editor/PlopMarker.tsx`), three bands on viewport scale (start at <0.75 / 0.75–3 / >3 px per cm; tune by feel and keep thresholds as constants):
- far: flat circle in the plant's color, no text — plus, on plantable objects, a name label + dominant-plant tint so beds read at a glance;
- mid: colored circle + emoji icon;
- near: circle + emoji + plant name + count (derived or override).
- [ ] Rotation correctness: plops render inside the object's rotated group so they track the object; verify moving/rotating a bed in field view carries its plops (no planting writes occur).
## Out of scope
- Read-only mode (#17). Clear-bed bulk action (#18). Region fills (#19). Plop overlap rules (none by design).
## Key files & patterns
- Local-frame transforms from `web/src/lib/geometry.ts` (#9); semantic-zoom bands: `DESIGN.md` § Editor / rendering.
## Dependencies
Blocked by #11, #13, #14.
## Acceptance criteria
- **The DESIGN.md scenario end-to-end on desktop AND a phone**: create a bed, focus into it, place a few garlic plops in one corner, basil in another, beans along a side; exit focus; zoom out — colors/labels identify the plantings; zoom mid — icons appear; zoom in — names + counts. Reload: identical.
- Rotate the bed 45° in field view — plops stay in their corners.
- Resizing a plop updates its displayed derived count live; setting an override sticks.
- "Remove" hides the plop everywhere and survives reload (row kept with `removed_at`).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Part of epic #20 · Design: DESIGN.md
Context
This issue delivers pansy's core vision: click into a bed, plop garlic in one corner and basil in another, zoom out, and still see what's planted where. It builds focus mode, plop placement/manipulation, and semantic zoom on top of the field editor (#11), the PlantPicker (#13), and the plantings API (#14).
Scope
zoomToFit(object), setfocusedObjectId+?focus=URL param, dim/desaturate everything else. Escape / tap outside / a breadcrumb button exits (animates back to full-garden view). Same canvas, no separate route.PlantPicker(#13); after choosing, click/tap inside the object drops a plop there — default radiusmax(1.5 × spacing_cm, 15cm), converted into the object's local frame before POSTing. Repeat-place stays armed until Escape/done.removed_at, distinct from hard delete). Optimistic sync with the same one-PATCH-per-gesture + 409-rollback pattern as #11.PlopMarkersemantic zoom (web/src/editor/PlopMarker.tsx), three bands on viewport scale (start at <0.75 / 0.75–3 / >3 px per cm; tune by feel and keep thresholds as constants):Out of scope
Key files & patterns
web/src/lib/geometry.ts(#9); semantic-zoom bands:DESIGN.md§ Editor / rendering.Dependencies
Blocked by #11, #13, #14.
Acceptance criteria
removed_at).