Flatten a Stack Cats program Stack Cats is a reversible, stack-based language. Its reversible nature makes for somewhat weird loops. This challenge is about the conditional loop (...). When these loops are nested in certain ways, it's possible to transform the code to reduce the nesting depth. Here are the rules (where A and B stand for an arbitrary snippets):

When one loop starts with another loop, we can extract the inner loop to the front: ((A)B) becomes (A)(B).
When one loop ends with another loop, we can extract the inner loop to the end: (B(A)) becomes (B)(A).
Empty loops, (), can be removed from the program entirely. As a corollary (in conjunction with the other rules), ((A)) is equivalent to (A).

The only nested loops that will remain are of the form (A(B)C), where A, B and C are non-empty.
The Challenge
You're given a valid Stack Cats program and your task is to reduce the nesting level of loops as much as possible, leaving no empty loops, using the above transformations.
A valid Stack Cats program...

... consists only of the characters ()/\&lt;&gt;[]{}!&quot;*+-:=ITX^_|.
... has mirror symmetry (e.g. \(]{}!{}[)/ is a valid program, but /|/ isn't).
... has correctly matched and nested () and {} ([], &lt;&gt; and \/ don't necessarily need to be matched as usual, although they'll appear in pairs due to the mirror symmetry requirement).

You can take either a string or a list of characters as input, but output needs to be presented in the same format.
You may write a program or a function and use any of our standard methods of receiving input and providing output. Note that these loopholes are forbidden by default.
This is code-golf, so the shortest valid answer – measured in bytes – wins.
Test Cases
Test cases are two lines each (input and output), separated by empty lines. Note that one output is empty. You also need to support the empty input (which should result in empty output).
(((=+|+=)))
(=+|+=)

({(=+|+=)})
({(=+|+=)})

((\)/)I(\(/))
(\)(/)I(\)(/)

(()()(())()())


((&lt;|&gt;((X((T)))[_]))\^/(([_](((T))X))&lt;|&gt;))
(&lt;|&gt;)(X)(T)([_])(\^/)([_])(T)(X)(&lt;|&gt;)
Retina 0.8.2, 113 107 67 66 bytes

+`\(\)|(\()?(\(((\()|(?&lt;-4&gt;\))|[^()])*(?(4)@)\))(?(1)|(\)))
$5$2$1


Try it online! Includes 3 4 byte saving thanks to @MartinEnder. Explanation:

+`


Repeatedly apply the substitution until there are no matches.

\(\)|


Match an empty loop (in which case nothing is captured so the substitution simply deletes it) or:

(\()?


Optionally match a (. This is captured in group 1 if it matched, but not if it didn't.

(\(


Capture the main body of the match in group 2 and match a (.

(
 (\()
|
 (&lt;-4&gt;\))
|
 [^()]
)*


Repeatedly match either a (, capturing it in group 4, or a ), removing a capture from group 4 (failing if there isn't one), or something else.

(?(4)@)


Ensure that there are no spare captures left in group 4.

\))


End capture group 2 with another ).

(?(1)|(\)))


If capture group 1 was empty, then capture a ) in capture group 5. (So exactly one of those two groups will have a capture).

$5$2$1


Move the bracket captured in either group 1 or group 5 to the other side of group 2. This has the effect of moving the inner loop to the front or the end of the outer loop depending on which side it matched.