An NFT is the same machinery with one field changed. Read Solmate's ERC721 beside the ERC20 you already know and the whole of non-fungibility turns out to be one mapping pointing the other way.
You have read three fungible tokens now, and all three kept the same shape: an address, and how much of something it holds. This file is the other standard. Same author as the last one, same house style, same banner comments, and two hundred and thirty one lines.
The whole difference is visible in the state block. Not in the function bodies, not in the second contract waiting at the bottom. One mapping points the other way, and everything else in the file follows from that single flip.
Here is the sentence this whole lesson rests on. The last file wrote `mapping(address => uint256) public balanceOf`: hand it an address, get back an amount. Line 31 writes `mapping(uint256 => address) internal _ownerOf`: hand it a token id, get back an address.
Read left to right and the key type changed from an address to a number, and the value type changed from a number to an address. That is non-fungibility, in one line. A thing with an identity of its own, and exactly one holder for it.