Progress report:
I decided to go with #2.
I've added the following columns to w3t_Banned:
B_X_NoEditPost
B_X_NoEditProfile
B_X_NoMessage
B_X_NoPost
B_X_NoRate
And I've changed user::check_ban(), and all calls to it, to check these privileges. For example, if there's a row in w3t_Banned with B_Username='XYZ', B_X_NoPost='0' and B_X_NoMessage='1', then user XYZ can post, but not send private messages.
I wanted to add a B_X_NoLogin flag as well, but I had problems with that, as described
here.