I thought that was why you had the ä there.
Just running on guesswork and making stuff up as I go along here, now (my usual approach to this kind of problem, I'm afraid).
I agree with your original post: a lone chr(150) should not come out of utf8_encode(); either that function is broken (and I know of no bugs in that regard), or something is converting it back before it goes in the page.
Something like this:
The text contains an "en-dash"
Which has been encoded according to ISO-8859-1
In other words, chr(150)
That byte goes into utf8_encode
Which assumes it's ISO-8859-1
And outputs two bytes chr(194) chr(150)
Which is the UTF-8 encoding for "en-dash"
Something weird happens (this is the important bit and this is where I'm not being much help)
And that "en-dash" is back to being encoded as chr(150)
Which is rendered according to UTF-8
And the renderer says a rude thing.
Why should the ä come through intact, though? If that text was already in UTF-8 when it was stored, then utf8_encode (which assumes its input is in ISO-8859-1) would convert it again (outputting bytes representing "A-tilde currency-sign"). Then the weirdness happens and those bytes get turned back into the UTF-8 encoding for "a-umlaut" and is rendered without any trouble.
I reckon that what you have stored in the database is encoded according to ISO-8859-1 (hence the need for utf8_encode in the first place); but UTF-8-encoded text has been put in it (valid UTF-8 is also valid ISO-8859-1).
Another possibility: incoming text is UTF-8 for the most part, but there are some invalid characters in there (say, a text file originally encoded as UTF-8, but later edits used ISO-8859-1 and the writer didn't notice things like "ä" instead of "ä"). That would be messy, and would probably require sentient intervention to tidy up. Definitely time for a ๐.
Inconsistently-encoded input would explain inconsistently-encoded output. But it doesn't explain why the effect is the exact opposite of what one'd expect.