Support clarified what was happening here, maybe this will help others.
The request I posted was doing two things: Asking for authorization for the https://localhost audience, and requesting an id token. The audience parameter appears unnecessary in this case. The id token scopes were being ignored because I had a rule running that restricted access token scopes. It turns out that in order to request e.g. the “email” scope for the id token, rules must allow that scope on access tokens as well.
I updated my rule to allow the OpenID scopes (email, profile, address, phone), and now the id token contains the expected claims.