By the shell or by the kernel’s terminal discipline or by the terminal emulator? AFAIU the shell is basically out of the picture while `less` is running.
> I can <C-z> while less is running to background that process using the shell, so the shell is clearly not completely gone.
The shell isn’t gone, but it isn’t active either from what I understand. The function of converting the user’s typing ^Z on a terminal (or a ^Z arriving on the master end of a pseudoterminal) into a SIGTSTP signal to the terminal’s foreground process group is[1] a built-in function of the kernel, much like for ^C and SIGINT or ^\ and SIGQUIT. (The use of ^Z resp. ^C or ^\ specifically, as well as the function being active at all, is configurable via a TTY ioctl wrapped by termios wrapped in turn by `stty susp` resp. `stty intr` or `stty quit`.) So is the default signal action of stopping (i.e. suspending) the process in response to that signal. The shell just sees its waitpid() syscall return and handles the possibility of that having happened due to the process stopping rather than dying (by updating its job bookkeeping, making itself the foreground process group again, and reëntering the REPL).
I am not saying that doing job control by filtering the child’s input would be a bad design in the abstract, and it is how terminal multiplexers work for instance. I admit the idea of kernel-side support for shell job control is pretty silly, it’s just how it’s traditionally done in a Unix system.
Whew! Advanced Unix system programming level stuff. I've dabbled a bit in that field, in C, on Unix, some older versions on PCs. It was fun. Any recommendation for a tutorial style book or site or blog on the subject, other than man pages and the Kerrisk book (TLPI, which is more of a reference), for Linux?