hide() did not invalidate in-flight search requests. Clearing
the input or picking a contact while a request was pending let
the old response re-render and pop the suggestion list back
open. Bump requestSeq in hide() so every dismissal retires
pending responses.
Add a Vitest + jsdom harness with black-box tests for the
invitee widget, covering the stale-response race both on
field clear and on contact pick.
The static datalist only carried the first 50 address book
contacts, so typed fragments could not match anything beyond
that. A new read-only GET /contacts/search endpoint queries
the full address book per keystroke, and a small vanilla JS
combobox shows the results and replaces only the fragment
after the last separator, so comma-separated lists keep
working. The server-rendered datalist remains as the no-JS
fallback; the JS removes the list attribute at runtime so
the native popup does not double the custom one.