- lib/shares.ts: zod Share shape + hooks (useShares/useAddShare/ useUpdateShareRole/useRemoveShare over /gardens/:id/shares). - lib/gardens.ts: myRole on the garden schema + canEditRole/isOwnerRole helpers (role always comes from the server's my_role, never guessed). - ShareGardenModal (owner only): invite an existing user by email as viewer/ editor, change a share's role inline, remove; surfaces the backend's friendly "no account with that email". Reachable from the garden card and the editor. - GardenCard: "shared · viewer/editor" badge on non-owned gardens; owner gets Share/Edit/Delete, a recipient gets Leave (LeaveGardenModal removes their own share). - Read-only viewer mode in the editor, gated on a single canEdit flag: viewers get no palette, no select/move/resize/rotate handles, no plop overlay, no placement, and read-only inspectors (a disabled <fieldset> makes every field read-only in one shot; Change/Delete/Remove/Plant-here hidden), plus a "View only" indicator. Defense in depth: the canvas create/place handlers also check canEdit, so a missed gate can't fire a mutation that would 403. tsc --noEmit clean; 24/24 vitest; production build green. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01JdQpdYYsTgtkJBxbcpAszi