Usually dual APIs are offered when they do distinctly different things. Async calls on Socket go through epoll/kqueue via SocketAsyncEngine, which has observably different behavior to regular sockets. There is no such duality in high-level APIs like HttpClient's `.GetStringAsync`.
You are not free from having to worry about it with virtual threads either. If you block a carrier thread with such a low-level API call, the runtime will have a bad time. Or you will always end up paying for a context switch (if you maintain a separate pool of block-able threads).
Green threading UX is better for "regular" code that never bothers to take advantage of concurrency features, but if you do want them, you are quickly back to usually more ceremonious structured concurrency abstractions which, lo and behold, can reimplement the same task system with a worker pool but on top of virtual threads.
If you start to care about cancellation, suddenly you are back to coloring problem regardless. I agree with you that having to write many await's in quick succession is tiresome and unnecessary. It's partially a fault of library authors who name methods "GetSomeLongNameAsync" instead of "Get" or "Query". But other than this it's not as invasive as people make it out to be, and encourages writing highly concurrent code - reducing latency 2x can require moving one or two awaits around. Not so much in Java/Erlang/Go.