Stop chat text from being read as color markup master
AC's write() renders an inline \color\text\reset\ markup, and the chat highlighter emits it deliberately. But raw message text was spliced into the same string with nothing escaping backslashes, so anyone who typed \red\hi\reset\ into chat dyed their message red in every client — user input interpreted as rendering instructions. lib/color-codes.mjs is now the one scanner both sides share. Producers escape text they did not author (\\ is a literal backslash) and move their element offsets onto the escaped copy; the renderer walks codes instead of running a global regex, so an unterminated \ is a character rather than a swallowed line. Escaping rather than stripping, so a backslash somebody legitimately typed still arrives instead of being silently eaten. Covers every consumer: chat.mjs splices per wrapped line, and applyColorCodes covers hotlink.mjs, moods, and the prompt MOTD (a user's mood, so the same hole). The shadow pass walks codes too — a regex read the two halves of an escaped backslash as a delimiter. Width measurement now shares the renderer's scanner, so a measured width still matches a drawn width.