It turned out doing software UART was a terrible idea. The processor is way too slow to support a reasonable baud rate. I did figure out how to use a comparator though: the key phrase I was missing was “rail-to-rail.” That means that inputs can be in the full voltage range from ground to Vcc. Another handy phrase is “push-pull,” which means that the comparator can output 0 and 1; in contrast, an “open collector” comparator can only output 0, and needs an external resistor to pull the output to 1.
I bought a rail-to-rail push-pull comparator, the MCP6541, and tried it with the receiver circuit, and sure enough it increased the maximum range significantly. Unfortunately it also increased the minimum range significantly.