As a newbie in just about every aspect of this project I resorted to Google and found the 74LVC series of parts. These are 3.3V logic gates and buffers that are 5V tolerant. E.g. they'll translate 5V -> 3.3V but not the other way.
I'm using the 74LVC245 bi-directional buffer to connect address and data lines and regular AND/OR/etc gates for the various control pins.
I also briefly tried the 74LVC4245 which does up translation (3.3V -> 5V) as well.
However, I've hit all manner of little niggly issues using the LVC series. Essentially they're too fast. Their transitions between low and high cause bouncing with other parts on the same PCB. The '4245 was the worst, each time it drove the data bus other chips would toggle their state with apparent ground bounce like issues (all this is new to me so I may well have this wrong!).
The other downside is most of the parts are only available in surface mount packages.
In the last couple of days I've been trying the 74AHC series instead on the same PCB. These parts support 2-6V (e.g. are happy at 3.3V), have 5V tolerant inputs, and operate at about half the speed. My initial tinkering suggests they're working much better.
As an example of an issue, I'm using a 74LVC139 to decode IORQ and M1 into "IORQ and not M1", "M1 and not IORQ", and "INT_ACK" (interrupt acknowledge, IORQ and M1).
It turns out at the end of an interrupt request cycle IORQ and M1 don't return to high at quite the same time, there is maybe a 4ns gap between them. Annoyingly this is enough for the '139 to switch outputs which in turn triggers the Pico into thinking an I/O request is underway.
The analog traces at the top show M1 and IORQ while the digital traces (circled) show INT_ACK and IORQ_and_not_M1.
The ACH part doesn't appear to be fast enough to react to the lag.
Full schematic of my current prototype attached.