Thought I would post back with my solution - which, while inelegant, at least works without any hacks, custom snippets, or resort to Wayfinder.
First, I wrapped the whole result set in a row div. Then, I added a special tpl_n4, which consists of the usual item tpl code (actually a reference to a chunk so I don't repeat myself) and then the following code:
So after every 4th item, I close one row and open another. But we're not quite home; what if there are 8 items? With the closing div tag of that initial opening row (which has been made into many rows - I know it's confusing), I end up with an empty row at the end. While this isn't necessarily a deal-killer, I found a simple way to fix it. I set tplLast to the same chunk as the default item tpl, NOT the special one with the row opening at the end. Now the default closing div finishes up my last row no matter how long it is, and I never end up with an empty extra row at the end.
This is such a kludge I can barely believe it works - but it does, and I'm not complaining.