How are variables stored in and retrieved from the program stack? Apologies in advance for the naivety of this question. I am a 50 year old artist trying to properly understand computers really for the first time. So here goes.

I have been trying to understand how data types and variables are handled by a compiler (in a very general sense, I know there is a lot to it). I'm missing something in my understanding of the relationship between storage in "the stack" and value types, and storage on "the heap" and reference types (the quotation marks are meant to signify that I understand that these terms are abstractions and not to be taken too literally in such a simplified context as the way I am framing this question). Anyway, my simplistic idea is that types like Booleans and integers go on "the stack" because they can, because they are known entities in terms of storage space, and their scope is easily controlled accordingly. 

But what I don't get is how variables on the stack are then read by an application- if I declare and assign x as an integer, say x = 3, and storage is reserved on the stack and then its value of 3 is stored there, and then in the same function I declare and assign y as, say 4, and then following that I then use x in another expression, (say z = 5 + x) how can the program read x in order to evaluate z when it is below y on the stack? I am clearly missing something. Is it that the location on the stack is only about the lifetime/ scope of the variable, and that the whole stack is actually accessible to the program all the time? If so, does that imply there is some other index that holds the addresses only of the variables on the stack to allow the values to be retrieved? But then I thought the whole point of the stack was that values were stored in the same place as the variable address? In my puny mind it seems that if there is this other index, then we are talking about something more like a heap? I'm clearly very confused, and I'm just hoping there is a simple answer to my simplistic question.

Thanks for reading this far.
Storing local variables on a stack is an implementation detail – basically an optimization. You can think of it this way. When entering a function, space for all local variables is allocated somewhere. You can then access all variables, since you know their location somehow (this is part of the process of allocation). When leaving a function, the space is deallocated (freed).

The stack is one way of implementing this process – you can think of it as a kind of "fast heap" which has limited size and so is only appropriate for small variables. As an additional optimization, all local variables are stored in one block. Since each local variable has known size, you know the offset of each variable in the block, and that is how you access it. This is in contrast to variables allocated on the heap, whose addresses are themselves stored in other variables.

You can think of the stack as very similar to the classical stack data structure, with one crucial difference: you are allowed to access items below the top-of-stack. Indeed, you can access the $k$th item from the top. This is how you can access all your local variables with pushing and popping. The only pushing being done is upon entering the function, and the only popping upon leaving the function.

Finally, let me mention that in practice, some of the local variables are stored in registers. This is since access to registers is faster than access to the stack. This is another way of implementing a space for local variables. Once again, we know exactly where a variable is stored (this time not via offset, but via the name of a register), and this kind of storage is only appropriate for small data.