Branches are the wrong solution here. Branches are for when the code
(temporarily) needs to change from the common base.
That is misleading. Feature branches (temporary branches) are only one of the many uses for branches. Client-branches could be long-living branches, that just have a common ancestor and that could periodically benefit from the ancestor updates.
The problem with branches is that changes made to one branch stay on
that one branch until you explicitly merge them onto another branch.
This effectively means that if you have a change in the common code,
you need to apply that change to each 'client-branch' individually,
which is exactly what you don't want.
The suggestion of using a submodule (with common code) also requires that you explicitly updates the submodule of each customer. With the disadvantage that when using submodules you cannot cherry pick (choose which changes you want to incorporate), unless you also branch the submodule - which would be the worst of both worlds. Additionally, using submodules will force you to keep all common-code inside a single subfolder, which may be a problem if you need to keep some files (like HTML, ASPX, JSP, etc) inside root folder or into some specific folder. If you are using client-branches, you can have both common-code and client-specific code in the same folders (and as long as you keep them in separated files, merging will always be trivial).
One could easily have one branch for each customer, and merge the product enhancements into each branch as needed (or even rebase the client-branches, which would lead to cleaner history, although that could lead to some inconsistent states in history). This answer explains the client-specific branches. The main challenge here is to keep the core funcionality isolated from the customizations. That could be achieved either using inheritance, partial classes or events.
A simple example: master branch could have base classes, which would host most of the code, and nearly-empty derived classes which would host basically the customizations. Each customer would have it's own branch, and would only change those derived classes. When new features are added to the master branch (product baseline) they can be merged into the customer branches. Customer extensions could break (if using strongly typed language) because of contract changes on the base class - and that's good, that's a sign that the customization should be reviewed.
It's important to isolate the common code from the customer-specific code, so that the merges are straightforward. It's a good idea to keep them into separated files, either being different classes (customization inheriting from the common code) or just being partial classes. The base funcionality and the customizations could even be completely unrelated classes, as long as you plug them in some startup code (like subscribing events or something like that).
By correctly designing the methods (and/or the extension events), you can achieve a quite solid extensible product, and avoid maintenance hell.