395a322adc
Mirrors handle_double_click for the touch pipeline. A double-tap on a face-up card fires MoveRequestEvent to the best legal destination using the same priority order (foundation first, tableau second; stack move as priority 2 when the tapped card is a stack base). Implementation: - handle_double_tap reads TouchPhase::Ended events. When drag.active_touch_id is set and drag.committed is false, the touch ended without crossing the drag threshold = pure tap. The top card ID from drag.cards is used as the tracking key. - DOUBLE_TAP_WINDOW = 0.5s (wider than DOUBLE_CLICK_WINDOW = 0.35s; touch screens have higher input latency; pinned by a const-assert test). - System is inserted between touch_follow_drag and touch_end_drag in the .chain() so drag state is readable before touch_end_drag clears it. - touch_end_drag's uncommitted-tap cleanup path still fires after handle_double_tap — the drag.clear() + StateChangedEvent are harmless in sequence with a MoveRequestEvent already queued. 1 new test (1283 total): double_tap_window_is_wider_than_double_click_window (compile-time const assert). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>