OK, scratch that theory. I just found an old ngscopeclient dataset from my previous experiments with the DP83867.
I had MDIO working successfully using this same controller on it. In that particular case I was running at LVCMOS25 levels rather than the LVCMOS18 I'm using here, but it was the same FPGA IP.
So clearly the DP83867 *is* able to work with my controller. Which makes me lean back towards a hardware issue again.
But I'm still at a loss as to what could make MDIO fail but literally everything else work.