Given how close those plan estimates are to each other, I would say your query was very fragile under 14.6, and it was just a matter of luck of how the statistics were computed that you got the better plan on the older version. As opposed to some important coding changes that happened between versions. To verify that, Can you force each version to choose the other plan, for example by fiddling with enable_nestedloop on one and enable_mergjoin on the other?
Disabling mergejoin on 14.6 and disabling nestedloop on 15.2 causes both to use hashjoin where it runs for 37ms in 14.6 and 208ms in 15.2.
So please also disable hashjoin on each (as well as what you already disabled) and see if that forces at least one of them to switch to using the other one's plan. Then once you get at least one version to show both plans, it is a question of whether there was just a small difference in cost estimates which was still large enough to change the rank order or the plans, or was it a large difference.
The difference in performance of the hashjoin plans is also interesting and probably worth investigating, but it is not obviously related to the original performance difference. So how many different things are you willing to investigate, and in what order?
If you could offer up a dataset which reproduces the problem but can be shared without confidentiality problems, that could help. Especially if you can provide a generator for the data which uses random() and generate_series(), rather than the data itself.