Brain-fade with non-blocking timing loop

OK, stared at this for 20-minutes and cannot see why the 60-second delay is being ignored.

Can someone please make me slap my forehead!

uint32_t procDelay = 60000;  // used Globally
uint16_t minsLeft = 0;  // used Globally

void progWork(uint16_t mins) {
  minsLeft = mins;
  while (minsLeft > 0) {
    --minsLeft;
    Serial.println(minsLeft);
    uint32_t ctr = millis();
    if (millis() - ctr > procDelay) {  // This is supposed to wait 1-minute
      ctr = millis();                         // but it scoots straight through
      showCountDown();
      if (ctr % 10 == 0) {
        cardCheck();
      }
    }
  }
}

I am old, I sometimes do this. :D

Because you decrement minsLeft every pass through the loop instead of just when you detect that a whole minute has passed.

Personally I wouldn't do it this way. This is a blocking function. Yes, it uses millis() and does stuff while it's blocking but it's still only letting the cardCheck() run once per minute and no other code outside this function can work at all.

Additionally, changing the global minsLeft inside this function is a "side effect". Why did you pass in the mins when you could have set minsLeft before calling this function and then let it count down? The better way would be to keep the mins parameter and make minsLeft local.