That's because Ctrl+DCtrl+D is a hack.
Deep down, Ctrl+D Ctrl+D (despite being called the eof character) doesn't actually mean end-of-file: it means “send the pending input to the application now”. This is actually close to the meaning of Ctrl+M Ctrl+M (eol), which sends the pending input plus a newline.
When you press Ctrl+DCtrl+D immediately after a Ctrl+M Ctrl+M (i.e. at the beginning of a line) or after another Ctrl+DCtrl+D, the pending input is empty. Thus the application receives 0 bytes of input. In a read call, reading 0 bytes signals the end of the file.
When you press Ctrl+ZCtrl+Z, the pending input is discarded. Thus only what had already been sent to the application (which is cat) by entering a newline or Ctrl+DCtrl+D before pressing Ctrl+ZCtrl+Z is processed.