Vaya sí, igual me extendí demasiado jeje. Intenté resumirlo lo mejor posible y aun así me quedó bastante largo.
Al final lo solucioné usando utf8_encode() en los sitios adecuados. No acabo de entender todo lo que he hecho... pero de momento funciona. Cuando tenga más tiempo reviso de nuevo el código.
Muchas gracias!