OK - Managed to get this working without using any NAT of IP or MAC addresses. Instead I'm using 'ip unnumbered' interfaces on the routerboard or what some call /32 routing in combination with proxy-arp.
Let's say we have 220.127.116.11/24 as a network where .1 is the RB IP and the gateway for the virtual firewalls. I assign that IP to a bridge interface (i.e. a Loopback interface) with the full /24 subnet - nothing fancy there. For each VLAN interface that I want to be on the same /24, I then assign the same IP (18.104.22.168) but using a /32 mask. The special bit however, is instead of leaving the network address as automatically calculated, I set this to be the IP of the virtual firewall's interface on that VLAN (e.g. 22.214.171.124, 126.96.36.199, 188.8.131.52 etc). This as I understand is much the same as using "ip unnumbered" in the Cisco world. The other special bit is to enable proxy arp on the RB's VLAN interface so that it replies to ARPs for other IPs on the same network that may be on different physical or VLAN interfaces. Even though the MAC addresses of the virtual firewall VLAN interfaces (FortiGate VDOMs) are the same and they're all on the same IP subnet, due to the fact that they're on different VLAN interfaces, this doesn't cause a problem in the MAC or ARP tables as they are no longer bridged at L2.
If I want to give a customer another IP to use, I simply put in a route to send traffic to that particular IP out of that customer's VLAN interface. It doesn't need to be an adjacent IP either.
This creates what I might call a layer 3 bridge of sorts. Traffic between interfaces is routed by the RB even though they are on the same subnet. While this might seem like unnecessary overhead for the RB, it does open up a number of useful options (in my use case at least) such as:
1. Preventing two customers from trying to use the same IP address - Unless an IP is specifically routed to that customer's VLAN interface, it will not work and more importantly, it will not affect traffic to and from the legitimate VLAN for that IP even though they're on the same IP subnet.
2. Full L3 queue functionality can be assigned per customer that also controls traffic between customers despite it appearing that they're all on the same network.
3. Reduction in broadcast domains - Each customer gets their own L2 broadcast domain on their VLAN. Only arp broadcasts are proxied and as these are also cached by the RB, there's a further reduction in broadcast traffic between customers.
Overall, I'm actually quite pleased that this issue arose as I now have a much better solution that the original one I had planned.
For a different use case of a similar configuration, check out Brian Horn's presentation at MUM US '14: https://www.youtube.com/watch?v=OTx1o8AajDY