Q:

Can you ever revert a commit other than the very last without creating a conflict?

Note: stackoverflow's bot thinks the question is subjective but it is very factual - I am not asking for opinions here!
Let's say that I have one branch only, and the history of my commits are:
A - B - C - D
If I click on C in a GUI (like GitKraken or Git for Windows) and do "revert commit", I get a file conflict message.
Two questions:

Is this because the revert "undoes" the changes made in C, and GIT
is now stuck with B and D which modify the file in different,
incompatible ways? is that the reason?
If yes, does this mean that you can only revert the very last
commit? Can you ever revert a commit other than the very last
without creating a conflict?

UPDATE: editing the question after the very helpful clarifications from @RomainValeri. I think I was making too much confusion between revert and reset. In his example below, doing
git revert B

causes the branch to go from
A-B-C-D

to
A-B-C-D-E

If B was the only commit which changed file2.txt, and B didn't make any other changes to other files, then the new commit E created after the revert will retain all the changes made in C and D but NOT those made in B. Is this correct?
This is because, technically, reverting means cancelling out, undoing any changes of a commit - is this correct?
Also: let's say that I have only one file in my working directory. If B is the only commit which changes the function fun1() in my file, while all the other commits change other functions within the same file, then reverting B will most likely cause a conflict, because git thinks in terms of lines in a file, not in terms of functions within a file. Is this correct?
So let's say that B changes fun1(), C changes fun2() and D changes fun3(). I then realise that the changes made at B in fun1() are wrong and I need to undo them. If all these functions are in the same file, is there a way to undo the changes in B while retaining those in C and D?
If, instead, each of these 3 functions is in a separate file, then it is much simpler to undo the changes of one commit without affecting the others, right?
I imagine this is, of course, one of the many reasons why a single file should never be too large nor contain too many functions doing different things, right?

A:

Short answer : Yes. You could very well revert a given commit (even an old one) and have no conflict.

Why is it so?
git revert creates a new commit on top of the current tip of the tree, it does not delete the given commit, nor modifies it in any way.
You have a conflict when something is different in the merge-base between the commit you're creating with the revert and HEAD.

Try it out for yourself with this
Trivial example
Given the repo tree,
Folder1
  file1.txt
  file2.txt
Folder2
  file3.txt

and following history,
A---B---C---D << HEAD

where
A creates all files with their contents.
B modifies file2.txt
C modifies file1.txt and file3.txt
D further modifies file1.txt
then the command
git revert B

would not create any conflict, just a fresh new commit E with new changes for file2.txt.
A---B---C---D---E << HEAD

Contents of the new commit created by git revert (added after comments)
The changes in E are the exact reverse changes that B introduced.
With B like this :
$ git show B
commit f3ba29d98c0998297126d686d03d33794cbc0f73
Author: Pythonista <some.name@some.isp>
Date:   Sun Mar 24 20:41:49 2019 +0100

    Some change

diff --git a/javascript/functions.js b/javascript/functions.js
index 543c9cb..5f166d3 100644
--- a/javascript/functions.js
+++ b/javascript/functions.js
@@ -3710,5 +3710,6 @@ function dumpLists(isMixted) {
 // end of process

 createTrigger(window, 'load', START_EVERYTHING);
+doStuff(param);

You would have an E commit containing :
$ git show E
commit 2ff723970e81d64f3776c081a1f96242589774b6 (HEAD -> master)
Author: Pythonista <some.name@some.isp>
Date:   Sun Mar 24 20:42:33 2019 +0100

    Revert "Some change"

    This reverts commit f3ba29d98c0998297126d686d03d33794cbc0f73.

diff --git a/javascript/functions.js b/javascript/functions.js
index 5f166d3..543c9cb 100644
--- a/javascript/functions.js
+++ b/javascript/functions.js
@@ -3710,6 +3710,5 @@ function dumpLists(isMixted) {
 // end of process

 createTrigger(window, 'load', START_EVERYTHING);
-doStuff(param);

Mind the + or - in front of the last diff line, meaning "addition" (+) and "deletion" (-).

Finally, to say a word about the concerns at the end of your post (which I mostly missed, for some reason) :
I don't think you should base architecture choices for your application upon source control worries. Git is quite a great tool, and it's smart enough to prevent a big part of the frequent trouble we would inevitably have without. Go for the best way to group your code along its own logic, regardless of git.
And by the way, conflict detection happens at chunk level, not file. Meaning that the same file could have been modified by two sources without provoking a conflict if they happen in different non-contiguous parts. I couldn't exactly say where's the threshold, but it's irrelevant to the point.

