Where the randomness comes from
Every character comes from crypto.getRandomValues, the browser’s own cryptographic generator. Math.random is fast and fine for shuffling a list; it is not fine for anything somebody might try to guess.
The draw is rejection-sampled rather than folded with a remainder, so no character in the set is fractionally more likely than another.