The answer can be found in the termios(3) man page:
VEOF (004, EOT, Ctrl-D) End-of-file character (EOF). More precisely:
this character causes the pending tty buffer to be sent to the
waiting user program without waiting for end-of-line. If it is
the first character of the line, the read(2) in the user program
returns 0, which signifies end-of-file. Recognized when ICANON
is set, and then not passed as input.
The first ^D you press causes the line you have typed to be delivered to the cat, so it gets a read(2) result of a (one character, no EOL char). The second ^D causes read(2) to return 0, which signifies EOF to cat.