Let's see.
> JVM bytecodes for dynamic languages
You're talkiing about invokedynamic - bytecode instruction added in Java 7, specifically to make dynamic language dispatch efficient, right? Explained simply: JVM was designed for static types - method calls resolved at compile time. In dynamic langs you don't know the type of something until runtime had to hack around, typically by boxing everything and doing manual type checks. This was slow and awkward. JRuby/Groovy adopted it eagerly. Clojure's dispatch model though is different. Most calls are either: direct interop (already statically typed), or calls through a Var (is a reference to a function value, not a dynamic method lookup). The Var indirection is a different shape of problem that invokedynamic doesn't solve as cleanly. It's not that it's useless, just that the fit isn't as natural.
> virtual threads was still "being considered"
That is an outdated info. Clojure 1.12.0 shipped two years ago with virtual thread support, but the integration with core.async's thread pool model was not there (so you were not completely incorrect). However, core.async later reimplemented go blocks using virtual threads when available. The improvements are still underway https://clojure.org/news/2025/10/01/async_virtual_threads
> take into account the host differences
Okay, this one is genuinely not that straightforward. The #? reader conditional in .cljc files is a clean, minimal mechanism. I don't really know any other language that can target completely different platforms from a single namespace as cleanly - even in Nodejs you can't in practice do it as nicely. Kotlin Multiplatform is probably the closest competitor - but its `expect/actual` mechanism requires separate source sets, separate files, and considerably more boilerplate. You're not writing in the same namespace; you're wiring together parallel declarations. Scala.js and GHCJS are essentially separate compilation targets with thinner sharing stories. But yes, it still can get complicated - different hosts have meaningfully different concurrency and I/O models, so it's rather "shared logic, host-specific edges" rather than "write once run anywhere". I still think Clojure handles this all far more elegantly than alternatives.
So pragmatically speaking, you're pointing at complexity at the implementation/runtime layer, while Clojure's complexity reduction happens at a different layer entirely - data model, immutability by default, simpler concurrency reasoning, REPL workflow. Those layers mostly don't interfere with each other. You mentioned real concerns at the platform engineering level, but they in practice don't touch what Clojure is actually trying to simplify. Someone writing Clojure code never experiences invokedynamic problems one way or the other.