* perf(web): eliminate sidebar close lag with many tasks
Closing the app sidebar unmounted the entire task list in the same frame
the width animation started, so close lag scaled with task count.
- Keep the Tasks grow-section mounted but CSS-hidden in rail mode:
collapse is now O(1) and reopening is instant (no remount).
- Memoize onToggleGroup so TaskSwitcher's memo() holds; an inline arrow
re-rendered every task row on each sidebar render.
- Animate only width (transition-[width]) instead of transition-all.
- content-visibility:auto on task blocks skips layout/paint for rows
outside the sidebar scrollport in long lists.
* test(web): add round-trip collapse/expand assertion to sidebar section test
* feat(web): drag-reorder subtasks within a parent task
Adds per-parent subtask ordering in the sidebar. Drag a subtask under its
parent to reorder; the chosen order is independent of the global sort and
persists per browser via localStorage.
- New `sidebarTaskPrefs.subtaskOrderByParentId` slice field and matching
`setSubtaskOrder(parentId, ids)` action, persisted to localStorage under
`kandev.sidebar.subtaskOrderByParentId`.
- `applyView` applies the per-parent override after `separateSubtasks`, so
it never touches root order or the active sort spec.
- TaskSwitcher exposes `onReorderSubtasks(parentId, ids)`; each parent's
subtask list lives in its own nested DndContext / SortableContext using
the existing dnd-kit setup.
- Task-deletion cleanup strips the deleted id as both parent key and
subtask entry from `subtaskOrderByParentId` in store + localStorage.
* refactor(web): address PR review feedback on subtask reordering
- Extract `pruneSubtaskOrder(map, taskId)` helper in `local-storage.ts`;
use it in both `cleanupTaskStorage` and `removeTaskFromSidebarPrefs`
to keep the two cleanup paths in lockstep.
- Move `SortableSubtaskRow`, `SortableSubtaskList`, and `useSubtaskDnd`
into a new sibling file `task-switcher-subtask-dnd.tsx` so the
switcher stays under the 600-line soft limit.
- `SortableSubtaskList` now short-circuits to a plain row map when
`onReorderSubtasks` is undefined — read-only callers no longer get a
misleading grab cursor or unused DnD scaffolding.
* fix(web): add stable keys to non-DnD subtask render path
When `onReorderSubtasks` is undefined, the early-return branch in
`SortableSubtaskList` mapped subtasks without keys, falling back to
React's index-based reconciliation. Wrap each child in a keyed
`Fragment` so reorders or size changes reconcile correctly.
* refactor(web): single-source DRAG_ACTIVATION_DISTANCE
Export the constant from `task-switcher-subtask-dnd.tsx` and import it
in `task-switcher.tsx` instead of declaring the same value twice.
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>