In the spirit of OOP and code modularity, I did at least build the page originally so that all the reports had the same output page that I could use over and over again. The new request, however, only wanted half of the data that the previous reports did. Ack!
The first step was to add a LOT more documentation -- and more helpful documentation than "--start HTML code--" "--end HTML code--". The second step was to pull all the stored procedures out of the main page, shove them off to an include file where they could all be seen in one place, and change from an "if/then/else" structure to a "switch/case" structure.
An interim step was to pull out code that tested on the value of "form.submit" -- because there is a bug in IE 6 (I think it is) that doesn't send the value of "form.submit" if the user hits the enter key rather than clicking on the button. (We discovered that one through a lot of head-banging and hand-wringing, believe you me!) Instead, we'll use a hidden variable ('requesttype') to determine which report to run, thankyouverymuch.
The final step, and this was the neat bit I thought, was for each request type to set up a list of which columns to include in the output, and in the output code to test on whether that column is in the list. So now this report is infinitely extensible -- if they add a new type of report that tracks a new data point, all we have to do is put a new column in the list.
It's all pretty basic stuff, really, but it was stuff I hadn't learned (or at least really grocked) yet when I first built the report. As time goes by and new changes to older sites come in (as they inevitably do -- the older and more forgotten a site is, the more likely somebody will shriek "OMG WE GOTTA MAKE THIS CHANGE RIGHT NOW!!!" at me when I come ambling in some morning), I plan to keep doing this pruning-and-updating process. Regardless of how I may feel about a given contract, I can at least take pride in my craftsmanship about it.