How to transition from a complex branching reality to a single-branch model?
Because you mention waterfall, I understand that the numerous branches you are alluding to are feature-branches rather than maintenance-branches.

In this setup, I also assume that these branches are created according to a waterfall plan that tries to minimise conflicts.  This implies that the goal of the development is to produce several distinct products.  When using a single-branch development model, it is important to also work on a single product. If several products are simultaneously developed in a single-branch development model, it effectively “glues” together versions of theses products, so that we might have in version a of the repository a healthy product X and a buggy product Y, while in version b the product X has a regression and Y a bug fix, but we have no version where both X and Y are healthy.  Such a situation would force us to consider X and Y as being developed in distinct repositories, which is a hint that they should be.

Therefore, the first steps should perform a repository split:


Arrange the repository so that it is easy to split it into several small repositories. For instance, rearrange the current repository so that each top-level directory corresponds to a repository you want to create in the future. Doing so, you can continue to use the branch-spaghetti discipline everybody is familiar with.
When step 1 is completed, refine the branch-spaghetti discipline by requiring that any single branch can only touch files in one top-level directory.
When each branch complies with step 2, perform the split. Developers can easily convert their pending changes to patch a single repository, just by removing the first-level of the path.


Now that the split has been performed, you can start working on the branch discipline itself.


Introduce programming techniques helping the development of short-lived branches. Branches being short-lived is a crucial aspect of all single-branch methodologies. One of their goals is to reduce the time spent on merging and debugging long-lived branches. A popular technique is the introduction of “feature-flags” where a “factory” uses a configuration flag to either produce the historic version of an object or the new, initially partially developed, version of that object.
By now, you have zillions of repositories with only a few branches in each, and you can turn the “we globally adopt the trunk-based development discipline” button, without seeing the original branch-spaghetti mountain collapsing on the trunk.


The actual split of repositories might be optional, but you would then have to adopt policies that cleanly delineate the allowed scope of each submitted patch (to limit the risk of conflicts when merging changes in the main branch). Reducing the overhead bound to conflicts is one of the goals of single-branch model methodologies, so I assume this is relevant in your context.