The first two examples don't actually have anything to do with the relationship between Num and Integral.
Take a look at the type of gin and fni. Let's do it together:
> :t gin
gin :: Integer
> :t fni
fni :: Integer
What's going on? This is called "type defaulting".
Technically speaking, any numeric literal like 3 or 5 or 42 in Haskell has type Num a => a. So if you wanted it to just be an integer number dammit, you'd have to always write 42 :: Integer instead of just 42. This is mighty inconvenient.
So to work around that, Haskell has certain rules that in certain special cases prescribe concrete types to be substituted when the type comes out generic. And in case of both Num and Integral the default type is Integer.
So when the compiler sees 3, and it's used as a parameter for gi, the compiler defaults to Integer. That's it. Your additional constraint of Num a has no further effect, because Integer is, in fact, already an instance of Num.
With the last two examples, on the other hand, the difference is that you explicitly specified the type signature. You didn't just leave it to the compiler to decide, no! You specifically said that n :: Num a => a. So the compiler can't decide that n :: Integer anymore. It has to be generic.
And since it's generic, and constrained to be Num, an Integral type doesn't work, because, as you have correctly noted, Num is not a subclass of Integral.
You can verify this by giving fni a type signature:
-- no longer works
fni :: Num a => a
fni = fn (3 :: Integral a => a)
Wait, but shouldn't n still work? After all, in OO this would work just fine. Take C#:
class Num {}
class Integral : Num {}
class Integer : Integral {}
Num a = (Integer)3
// ^ this is valid (modulo pseudocode), because `Integer` is a subclass of `Num`
Ah, but this is not a generic type! In the above example, a is a value of a concrete type Num, whereas in your Haskell code a is itself a type, but constrained to be Num. This is more like a C# interface than a C# class.
And generic types (whether in Haskell or not) actually work the other way around! Take a value like this:
x :: a
x = ...
What this type signature says is that "Whoever has a need of x, come and take it! But first name a type a. Then the value x will be of that type. Whichever type you name, that's what x will be"
Or, in plainer terms, it's the caller of a function (or consumer of a value) that chooses generic types, not the implementer.
And so, if you say that n :: Num a => a, it means that value n must be able to "morph" into any type a whatsoever, as long as that type has a Num instance. Whoever will use n in their computation - that person will choose what a is. You, the implementer of n, don't get to choose that.
And since you don't get to choose what a is, you don't get to narrow it down to be not just any Num, but an Integral. Because, you know, there are some Nums that are not Integrals, and so what are you going to do if whoever uses n chooses one of those non-Integral types to be a?
In case of i this works fine, because every Integral must also be Num, and so whatever the consumer of i chooses for a, you know for sure that it's going to be Num.