I would change each "nthScreen" function to manage its own processes. So you call the nthScreen() function every single time through the loop. It then looks at what state it's in: has it just entered that screen? Is it time for a blink? Keep a few local variables inside the function as static so that it can remember what went on before.
For example...
void firstScreen(bool hasChanged)
{
// call this hundreds of times per second.
//hasChanged tells us that the overall screen state has changed, so we need to re-display the whole screen
static unsigned long localLastMillis;
static bool blinkStatus=true;
const unsigned long blinkInterval = 1000; //milliseconds, interval between blinks
if(hasChanged) {
//Draw the full screen
lcd.clear();
lcd.setCursor(0, 0); // Column, line
lcd.print(" MY INTRO ");
lcd.setCursor(0, 2);
lcd.print(" SPLASH ");
lcd.setCursor(0, 3);
lcd.print(" SCREEN ");
}
if((millis()-localLastMillis > blinkInterval && blinkStatus) || hasChanged) {
//either the timer expired or we've just changed the screen and we need to show the blinking text
lcd.setCursor(0, 1);
lcd.print(" BLINKING ");
blinkStatus = false;
localLastMillis = millis();
}
if(millis()-localLastMillis > blinkInterval && !blinkStatus) {
//time to blink the text off
lcd.setCursor(0, 1);
lcd.print(" ");
blinkStatus = true;
localLastMillis = millis();
}
}
Since these functions are being called all the time, you may even wish to put the button-handling code inside them. That way the same button can do different things depending on what screen you are on. (Do the debounce outside the screen functions.)