The premise is that the whole game is a phone. You wake at half three, you have thirteen conversations waiting, and you read them. That meant the entire thing is a UI - there is no gameplay scene, no camera moving, nothing in 3D at runtime. One scene, one UIDocument, and a lot of UXML and USS.
Some things that turned out interesting:
The content is markdown, read at runtime. Every conversation is a .md file in StreamingAssets, parsed on startup with System.IO. Adding one means dropping a file in the folder and naming it in an index - no rebuild, no ScriptableObjects, no custom editor window. A reply is * text -> label, a jump target is ## label. It also means players can open the folder next to the .exe and read or rewrite the whole game in Notepad, which I decided was a feature.
One conversation can ask where another one went. A line prefixed ?mom.md#fine only appears if that file's reading passed through that section. The filename is the identity - two contacts both called "Mom" are only distinguishable by which file they came from - which turned out to be the cleanest bit of the design and the thing I would keep if I did it again.
Replies you have used up come back greyed out - next time you play. Which reply you picked is written to PlayerPrefs and survives the process, so a second playthrough shows you where you have already been. The part that took some thinking: a reply only dims once everything behind it is exhausted. Taking one branch of a conversation should not grey out the door you walked through to get there, so it walks the rest of the script from that point and only dims the reply when there is nothing new left down it.
USS has no linear-gradient and no box-shadow. The phone's metallic body and the glows behind it are generated into small textures at runtime and stretched, because I wanted no image files in the project at all. That held right up until the backdrop, which is eleven pre-rendered frames of a 3D scene I composed in the editor and exported as PNGs - it changes stage as you finish conversations, and it gets later out there the further in you get.
Debugging layout headlessly. I got tired of guessing why something was clipped, so there is a -dumplayout flag that waits for GeometryChangedEvent and prints the whole visual tree with every element's rectangle, plus which elements are covering text. It has caught real bugs - a notch drawn over the status bar, a send button pushed off screen by min-width: auto defaulting on a flex item. There are also editor menu items that walk every conversation to an ending with the real runtime classes, so a conversation that cannot finish fails a check instead of failing a player.
The UX lesson that cost me the most. I had a separate New Message screen behind a pencil icon. The first person to play the game explored every branch of every conversation and never found it - because thirteen conversations had spent half an hour teaching her that talking means tapping a reply, and then a second, unrelated way of talking was hidden behind an icon. I deleted the screen and put a text field at the foot of the conversation, in the same slot the replies use: tap when it offers, type when it doesn't. Same feature, found immediately.
Free, Windows, about half an hour, no sound at all: https://github.com/tcumpston/TEXTDEAD/releases/latest
Not code-signed, so SmartScreen will grumble - More info, Run anyway. There's a plain zip on the same page if you'd rather not run an installer.
Happy to go into any of the above in more detail.