/* The page is set in the same pixels the board is drawn in: the letters are
   bitmaps on the same 5x7 grid as every sprite, built into a real TrueType
   file by tools/pixel/font.py and embedded here by `just sprites`. No font
   service, no asset fetch — file:// keeps working.

   An em is 8 glyph pixels, so a font-size that is a multiple of 8 lands every
   pixel on a whole screen pixel: 8px draws 1:1, 16px at 2x, 24px at 3x. That
   is why the sizes below are 8, 16 and 24 and almost nothing in between. */
/* FONT:BEGIN */
@font-face {
  font-family: 'Frostwaste';
  font-style: normal; font-weight: 400; font-display: block;
  src: url(data:font/ttf;base64,AAEAAAAKAIAAAwAgT1MvMmKieqgAAACsAAAAYGNtYXC42EkOAAABDAAAAJRnbHlmjGSlKwAAAaAAACqoaGVhZGELQUoAACxIAAAANmhoZWEETgGSAAAsgAAAACRobXR4AlgAAAAALKQAAADQbG9jYQAIqgQAAC10AAABoG1heHAAbwAeAAAvFAAAACBuYW1lDw4jIgAALzQAAADOcG9zdAADAAAAADAEAAAAIAAEAlgBkAAFAAABLAEsAAABLAEsASwAAAEsAGQAyAAAAgAGCQMAAAAAAAAAAAEAAAAAAAAAAAAAAABGUlNUAEAAICdkAlj/OAAAAlgAyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAwABAAAADAAEAIgAAAAeABAAAwAOACMAXQBfAH0AsAC3ANcgFCAmIZIiEiaUJ0QnZP//AAAAIAAlAF8AYQCwALcA1yATICYhkiISJpQnRCdk////4f/g/9//3v+s/6b/h+BM4Dve0N5R2dDZIdkCAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgDIAAABLAH0AAMABwAAAQEBAQEBAQEAyABkAAD/nAAAAGQAAP+cAfQAAP7UAAD/nAAA/5wAAAACAGQBLAGQAfQAAwAHAAABAQEBAQEBAQBkAGQAAP+cAMgAZAAA/5wB9AAA/zgAAADIAAD/OAAAAAgAAAAAAfQB9AADAAcACwAPABMAFwAbAB8AAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAGQAZAAA/5wAyABkAAD/nP7UAfQAAP4MAGQAZAAA/5wAyABkAAD/nP7UAfQAAP4MAGQAZAAA/5wAyABkAAD/nAH0AAD/nAAAAGQAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAGQAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAGQAAP+cAAAABwAAAAAB9AH0AAMABwALAA8AEwAXABsAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAABkAAD/nAGQAGQAAP+c/5wAZAAA/5z/nABkAAD/nP+cAGQAAP+c/5wAZAAA/5wBkABkAAD/nAH0AAD/nAAAAGQAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAAA/5wAAABkAAD/nAAAAAAACQAAAAAB9AH0AAMABwALAA8AEwAXABsAHwAjAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAZADIAAD/OP+cAGQAAP+cASwAZAAA/5z/OADIAAD/OAEsAGQAAP+c/nAAZAAA/5wBLABkAAD/nP84AMgAAP84ASwAZAAA/5wB9AAA/5wAAAAAAAD/nAAAAGQAAP+cAAAAAAAA/5wAAABkAAD/nAAAAAAAAP+cAAAAZAAA/5wAAAAAAAD/nAAAAGQAAP+cAAAAAAABAMgBLAEsAfQAAwAAAQEBAQDIAGQAAP+cAfQAAP84AAAAAAADAGQAAAGQAfQAAwAHAAsAAAEBAQEBAQEBAQEBAQDIAMgAAP84/5wAZAAA/5wAZADIAAD/OAH0AAD/nAAAAAAAAP7UAAAAAAAA/5wAAAAAAAMAZAAAAZAB9AADAAcACwAAAQEBAQEBAQEBAQEBAGQAyAAA/zgAyABkAAD/nP84AMgAAP84AfQAAP+cAAAAAAAA/tQAAAAAAAD/nAAAAAAABQAAAMgBLAH0AAMABwALAA8AEwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAABkAAD/nADIAGQAAP+c/5wAZAAA/5z/nABkAAD/nADIAGQAAP+cAfQAAP+cAAAAZAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAZAAA/5wAAAAAAAMAZABkAZABkAADAAcACwAAAQEBAQEBAQEBAQEBAMgAZAAA/5z/nAEsAAD+1ABkAGQAAP+cAZAAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAAAgAA/5wAyADIAAMABwAAAQEBAQEBAQEAZABkAAD/nP+cAGQAAP+cAMgAAP84AAAAAAAA/5wAAAABAGQAyAGQASwAAwAAAQEBAQBkASwAAP7UASwAAP+cAAAAAAABAGQAAADIAGQAAwAAAQEBAQBkAGQAAP+cAGQAAP+cAAAAAAAFAAAAAAH0AfQAAwAHAAsADwATAAABAQEBAQEBAQEBAQEBAQEBAQEBAQGQAGQAAP+c/5wAZAAA/5z/nABkAAD/nP+cAGQAAP+c/5wAZAAA/5wB9AAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAABwAAAAAB9AH0AAMABwALAA8AEwAXABsAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAZAEsAAD+1P+cAGQAAP+cASwAyAAA/zj/nABkAAD/nADIAGQAAP+c/nAAyAAA/zgAZAEsAAD+1AH0AAD/nAAAAAAAAP84AAAAyAAA/5wAAAAAAAD/nAAAAGQAAP84AAAAZAAA/5wAAAAAAAD/nAAAAAAABABkAAABkAH0AAMABwALAA8AAAEBAQEBAQEBAQEBAQEBAQEAyABkAAD/nP+cAMgAAP84AGQAZAAA/5z/nAEsAAD+1AH0AAD/nAAAAAAAAP+cAAAAAAAA/zgAAAAAAAD/nAAAAAYAAAAAAfQB9AADAAcACwAPABMAFwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAGQBLAAA/tT/nABkAAD/nAGQAGQAAP+c/zgAyAAA/zj/nABkAAD/nP+cAfQAAP4MAfQAAP+cAAAAAAAA/5wAAABkAAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAUAAAAAAfQB9AADAAcACwAPABMAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAABkAAA/nABkABkAAD/nP7UASwAAP7UASwAZAAA/5z+cAGQAAD+cAH0AAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAAEAAAAAAH0AfQAAwAHAAsADwAAAQEBAQEBAQEBAQEBAQEBAQAAAGQAAP+cASwAZAAA/5z+1AH0AAD+DAEsAGQAAP+cAfQAAP84AAAAyAAA/zgAAAAAAAD/nAAAAAAAAP84AAAABQAAAAAB9AH0AAMABwALAA8AEwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAH0AAD+DAAAAGQAAP+cAAABkAAA/nABkABkAAD/nP5wAZAAAP5wAfQAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAYAAAAAAfQB9AADAAcACwAPABMAFwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAGQBLAAA/tT/nABkAAD/nAAAAZAAAP5wAAAAZAAA/5wBkABkAAD/nP7UASwAAP7UAfQAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAZAAA/5wAAAAAAAD/nAAAAAQAAAAAAfQB9AADAAcACwAPAAABAQEBAQEBAQEBAQEBAQEBAAAB9AAA/gwBkABkAAD/nP+cAGQAAP+c/5wAZAAA/5wB9AAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAAA/zgAAAAHAAAAAAH0AfQAAwAHAAsADwATABcAGwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQBkASwAAP7U/5wAZAAA/5wBkABkAAD/nP7UASwAAP7U/5wAZAAA/5wBkABkAAD/nP7UASwAAP7UAfQAAP+cAAAAAAAA/5wAAABkAAD/nAAAAAAAAP+cAAAAAAAA/5wAAABkAAD/nAAAAAAAAP+cAAAAAAAGAAAAAAH0AfQAAwAHAAsADwATABcAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQBkASwAAP7U/5wAZAAA/5wBkABkAAD/nP7UAZAAAP5wASwAZAAA/5z+1AEsAAD+1AH0AAD/nAAAAAAAAP+cAAAAZAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAAA/5wAAAACAGQAZADIAZAAAwAHAAABAQEBAQEBAQBkAGQAAP+cAAAAZAAA/5wBkAAA/5wAAP+cAAD/nAAAAAMAAP+cAMgBkAADAAcACwAAAQEBAQEBAQEBAQEBAGQAZAAA/5wAAABkAAD/nP+cAGQAAP+cAZAAAP+cAAD/nAAA/zgAAAAAAAD/nAAAAAAABQBkAAABkAH0AAMABwALAA8AEwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBLABkAAD/nP+cAGQAAP+c/5wAZAAA/5wAZABkAAD/nABkAGQAAP+cAfQAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAIAZABkAZABkAADAAcAAAEBAQEBAQEBAGQBLAAA/tQAAAEsAAD+1AGQAAD/nAAA/5wAAP+cAAAABQBkAAABkAH0AAMABwALAA8AEwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAZABkAAD/nABkAGQAAP+cAGQAZAAA/5z/nABkAAD/nP+cAGQAAP+cAfQAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAUAAAAAAfQB9AADAAcACwAPABMAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAGQBLAAA/tT/nABkAAD/nAGQAGQAAP+c/zgAyAAA/zgAAABkAAD/nAH0AAD/nAAAAAAAAP+cAAAAZAAA/5wAAAAAAAD/nAAA/5wAAP+cAAAAAAAFAAAAAAH0AfQAAwAHAAsADwATAAABAQEBAQEBAQEBAQEBAQEBAQEBAQBkASwAAP7U/5wAZAAA/5wBLADIAAD/OP+cASwAAP7U/5wBLAAA/tQB9AAA/5wAAAAAAAD+1AAAASwAAP+cAAAAAAAA/5wAAP+cAAD/nAAAAAAABgAAAAAB9AH0AAMABwALAA8AEwAXAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAZAEsAAD+1P+cAGQAAP+cAZAAZAAA/5z+cAH0AAD+DAAAAGQAAP+cAZAAZAAA/5wB9AAA/5wAAAAAAAD/nAAAAGQAAP+cAAAAAAAA/5wAAAAAAAD/OAAAAMgAAP84AAAABwAAAAAB9AH0AAMABwALAA8AEwAXABsAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAGQAAD+cAAAAGQAAP+cAZAAZAAA/5z+cAGQAAD+cAAAAGQAAP+cAZAAZAAA/5z+cAGQAAD+cAH0AAD/nAAAAAAAAP+cAAAAZAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAZAAA/5wAAAAAAAD/nAAAAAAAAwAAAAAB9AH0AAMABwALAAABAQEBAQEBAQEBAQEAZAGQAAD+cP+cAGQAAP+cAGQBkAAA/nAB9AAA/5wAAAAAAAD+1AAAAAAAAP+cAAAAAAAEAAAAAAH0AfQAAwAHAAsADwAAAQEBAQEBAQEBAQEBAQEBAQAAAZAAAP5wAAAAZAAA/5wBkABkAAD/nP5wAZAAAP5wAfQAAP+cAAAAAAAA/tQAAAEsAAD+1AAAAAAAAP+cAAAABQAAAAAB9AH0AAMABwALAA8AEwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAH0AAD+DAAAAGQAAP+cAAABkAAA/nAAAABkAAD/nAAAAfQAAP4MAfQAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAQAAAAAAfQB9AADAAcACwAPAAABAQEBAQEBAQEBAQEBAQEBAAAB9AAA/gwAAABkAAD/nAAAAZAAAP5wAAAAZAAA/5wB9AAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAAA/zgAAAAFAAAAAAH0AfQAAwAHAAsADwATAAABAQEBAQEBAQEBAQEBAQEBAQEBAQBkAZAAAP5w/5wAZAAA/5wBLADIAAD/OABkAGQAAP+c/tQBLAAA/tQB9AAA/5wAAAAAAAD+1AAAAMgAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAABQAAAAAB9AH0AAMABwALAA8AEwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAABkAAD/nAGQAGQAAP+c/nAB9AAA/gwAAABkAAD/nAGQAGQAAP+cAfQAAP84AAAAyAAA/zgAAAAAAAD/nAAAAAAAAP84AAAAyAAA/zgAAAAAAAMAAAAAAfQB9AADAAcACwAAAQEBAQEBAQEBAQEBAAAB9AAA/gwAyABkAAD/nP84AfQAAP4MAfQAAP+cAAAAAAAA/tQAAAAAAAD/nAAAAAAAAwAAAAAB9AH0AAMABwALAAABAQEBAQEBAQEBAQEBkABkAAD/nP5wAGQAAP+cAGQBLAAA/tQB9AAA/nAAAABkAAD/nAAAAAAAAP+cAAAAAAAHAAAAAAH0AfQAAwAHAAsADwATABcAGwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAGQAAP+cAZAAZAAA/5z/nABkAAD/nP7UASwAAP7UAAAAZAAA/5wBLABkAAD/nABkAGQAAP+cAfQAAP84AAAAyAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAAA/zgAAADIAAD/nAAAAAAAAP+cAAAAAAACAAAAAAH0AfQAAwAHAAABAQEBAQEBAQAAAGQAAP+cAAAB9AAA/gwB9AAA/nAAAAAAAAD/nAAAAAcAAAAAAfQB9AADAAcACwAPABMAFwAbAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAZAAA/5wBkABkAAD/nP5wAMgAAP84ASwAyAAA/zj+1ABkAAD/nADIAGQAAP+cAMgAZAAA/5wB9AAA/5wAAABkAAD/nAAAAAAAAP+cAAAAZAAA/5wAAAAAAAD+1AAAASwAAP+cAAAAZAAA/tQAAAAAAAcAAAAAAfQB9AADAAcACwAPABMAFwAbAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAZAAA/5wBkABkAAD/nP5wAMgAAP84AAAAZAAA/5wAyABkAAD/nABkAMgAAP84AGQAZAAA/5wB9AAA/5wAAABkAAD+1AAAAMgAAP+cAAAAAAAA/tQAAAEsAAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAQAAAAAAfQB9AADAAcACwAPAAABAQEBAQEBAQEBAQEBAQEBAGQBLAAA/tT/nABkAAD/nAGQAGQAAP+c/tQBLAAA/tQB9AAA/5wAAAAAAAD+1AAAASwAAP7UAAAAAAAA/5wAAAAFAAAAAAH0AfQAAwAHAAsADwATAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAZAAAP5wAAAAZAAA/5wBkABkAAD/nP5wAZAAAP5wAAAAZAAA/5wB9AAA/5wAAAAAAAD/nAAAAGQAAP+cAAAAAAAA/5wAAAAAAAD/OAAAAAAABgAAAAAB9AH0AAMABwALAA8AEwAXAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAZAEsAAD+1P+cAGQAAP+cAZAAZAAA/5z/nABkAAD/nP84AMgAAP84ASwAZAAA/5wB9AAA/5wAAAAAAAD+1AAAASwAAP84AAAAAAAA/5wAAAAAAAD/nAAAAGQAAP+cAAAABwAAAAAB9AH0AAMABwALAA8AEwAXABsAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAGQAAD+cAAAAGQAAP+cAZAAZAAA/5z+cAGQAAD+cAAAAGQAAP+cASwAZAAA/5wAZABkAAD/nAH0AAD/nAAAAAAAAP+cAAAAZAAA/5wAAAAAAAD/nAAAAAAAAP84AAAAyAAA/5wAAAAAAAD/nAAAAAAABQAAAAAB9AH0AAMABwALAA8AEwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAZAGQAAD+cP+cAGQAAP+cAGQBLAAA/tQBLABkAAD/nP5wAZAAAP5wAfQAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAIAAAAAAfQB9AADAAcAAAEBAQEBAQEBAAAB9AAA/gwAyABkAAD/nAH0AAD/nAAAAAAAAP5wAAAAAwAAAAAB9AH0AAMABwALAAABAQEBAQEBAQEBAQEAAABkAAD/nAGQAGQAAP+c/tQBLAAA/tQB9AAA/nAAAAGQAAD+cAAAAAAAAP+cAAAAAAAFAAAAAAH0AfQAAwAHAAsADwATAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAGQAAP+cAZAAZAAA/5z+1ABkAAD/nADIAGQAAP+c/5wAZAAA/5wB9AAA/tQAAAEsAAD+1AAAAAAAAP+cAAAAZAAA/5wAAAAAAAD/nAAAAAAABwAAAAAB9AH0AAMABwALAA8AEwAXABsAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAABkAAD/nAGQAGQAAP+c/zgAZAAA/5z/OADIAAD/OAEsAMgAAP84/tQAZAAA/5wBkABkAAD/nAH0AAD+1AAAASwAAP7UAAAAZAAA/5wAAAAAAAD/nAAAAGQAAP+cAAAAAAAA/5wAAABkAAD/nAAAAAAACQAAAAAB9AH0AAMABwALAA8AEwAXABsAHwAjAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAABkAAD/nAGQAGQAAP+c/tQAZAAA/5wAyABkAAD/nP+cAGQAAP+c/5wAZAAA/5wAyABkAAD/nP7UAGQAAP+cAZAAZAAA/5wB9AAA/5wAAABkAAD/nAAAAAAAAP+cAAAAZAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAZAAA/5wAAAAAAAD/nAAAAGQAAP+cAAAAAAAFAAAAAAH0AfQAAwAHAAsADwATAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAGQAAP+cAZAAZAAA/5z+1ABkAAD/nADIAGQAAP+c/5wAZAAA/5wB9AAA/5wAAABkAAD/nAAAAAAAAP+cAAAAZAAA/5wAAAAAAAD+1AAAAAAABQAAAAAB9AH0AAMABwALAA8AEwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAH0AAD+DAEsAGQAAP+c/5wAZAAA/5z/nABkAAD/nP+cAfQAAP4MAfQAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAMAZAAAAZAB9AADAAcACwAAAQEBAQEBAQEBAQEBAGQBLAAA/tQAAABkAAD/nAAAASwAAP7UAfQAAP+cAAAAAAAA/tQAAAAAAAD/nAAAAAAABQAAAAAB9AH0AAMABwALAA8AEwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAABkAAD/nABkAGQAAP+cAGQAZAAA/5wAZABkAAD/nABkAGQAAP+cAfQAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAMAZAAAAZAB9AADAAcACwAAAQEBAQEBAQEBAQEBAGQBLAAA/tQAyABkAAD/nP84ASwAAP7UAfQAAP+cAAAAAAAA/tQAAAAAAAD/nAAAAAAAAQAAAAAB9ABkAAMAAAEBAQEAAAH0AAD+DABkAAD/nAAAAAAABAAAAAABkAGQAAMABwALAA8AAAEBAQEBAQEBAQEBAQEBAQEAZADIAAD/OP+cAGQAAP+cASwAZAAA/5z/OAEsAAD+1AGQAAD/nAAAAAAAAP84AAAAyAAA/zgAAAAAAAD/nAAAAAUAAAAAAZAB9AADAAcACwAPABMAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAZAAA/5wAAAEsAAD+1AAAAGQAAP+cASwAZAAA/5z+1AEsAAD+1AH0AAD/OAAAAAAAAP+cAAAAAAAA/5wAAABkAAD/nAAAAAAAAP+cAAAAAAADAAAAAAGQAZAAAwAHAAsAAAEBAQEBAQEBAQEBAQBkASwAAP7U/5wAZAAA/5wAZAEsAAD+1AGQAAD/nAAAAAAAAP84AAAAAAAA/5wAAAAAAAUAAAAAAZAB9AADAAcACwAPABMAAAEBAQEBAQEBAQEBAQEBAQEBAQEBASwAZAAA/5z/OAEsAAD+1P+cAGQAAP+cASwAZAAA/5z/OAEsAAD+1AH0AAD/OAAAAAAAAP+cAAAAAAAA/5wAAABkAAD/nAAAAAAAAP+cAAAAAAAFAAAAAAGQAZAAAwAHAAsADwATAAABAQEBAQEBAQEBAQEBAQEBAQEBAQBkAMgAAP84/5wAZAAA/5wBLABkAAD/nP7UASwAAP7UAGQBLAAA/tQBkAAA/5wAAAAAAAD/nAAAAGQAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAABAAAAAABkAH0AAMABwALAA8AAAEBAQEBAQEBAQEBAQEBAQEAyADIAAD/OP+cAGQAAP+c/5wBLAAA/tQAZABkAAD/nAH0AAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAD/OAAAAAYAAP84AZABkAADAAcACwAPABMAFwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAGQBLAAA/tT/nABkAAD/nAEsAGQAAP+c/zgBLAAA/tQAyABkAAD/nP7UASwAAP7UAZAAAP+cAAAAAAAA/zgAAADIAAD/OAAAAAAAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAQAAAAAAZAB9AADAAcACwAPAAABAQEBAQEBAQEBAQEBAQEBAAAAZAAA/5wAAAEsAAD+1AAAAGQAAP+cASwAZAAA/5wB9AAA/zgAAAAAAAD/nAAAAAAAAP84AAAAyAAA/zgAAAACAGQAAADIAfQAAwAHAAABAQEBAQEBAQBkAGQAAP+cAAAAZAAA/5wB9AAA/5wAAP+cAAD+1AAAAAMAAP84ASwB9AADAAcACwAAAQEBAQEBAQEBAQEBAMgAZAAA/5wAAABkAAD/nP84AMgAAP84AfQAAP+cAAD/nAAA/nAAAAAAAAD/nAAAAAAABgAAAAABkAH0AAMABwALAA8AEwAXAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAABkAAD/nAEsAGQAAP+c/tQAyAAA/zgAAABkAAD/nADIAGQAAP+cAGQAZAAA/5wB9AAA/zgAAABkAAD/nAAAAAAAAP+cAAAAAAAA/zgAAADIAAD/nAAAAAAAAP+cAAAAAwAAAAABkAH0AAMABwALAAABAQEBAQEBAQEBAQEAAADIAAD/OABkAGQAAP+cAAABLAAA/tQB9AAA/5wAAAAAAAD+1AAAAAAAAP+cAAAAAAAEAAAAAAH0AZAAAwAHAAsADwAAAQEBAQEBAQEBAQEBAQEBAQAAAfQAAP4MAAAAZAAA/5wAyABkAAD/nADIAGQAAP+cAZAAAP+cAAAAAAAA/tQAAAEsAAD+1AAAASwAAP7UAAAAAwAAAAABkAGQAAMABwALAAABAQEBAQEBAQEBAQEAAAEsAAD+1AAAAGQAAP+cASwAZAAA/5wBkAAA/5wAAAAAAAD+1AAAASwAAP7UAAAAAAAEAAAAAAGQAZAAAwAHAAsADwAAAQEBAQEBAQEBAQEBAQEBAQBkAMgAAP84/5wAZAAA/5wBLABkAAD/nP84AMgAAP84AZAAAP+cAAAAAAAA/zgAAADIAAD/OAAAAAAAAP+cAAAABQAA/zgBkAGQAAMABwALAA8AEwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAEsAAD+1AAAAGQAAP+cASwAZAAA/5z+1AEsAAD+1AAAAGQAAP+cAZAAAP+cAAAAAAAA/zgAAADIAAD/OAAAAAAAAP+cAAAAAAAA/zgAAAAAAAUAAP84AZABkAADAAcACwAPABMAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAGQBLAAA/tT/nABkAAD/nAEsAGQAAP+c/zgBLAAA/tQAyABkAAD/nAGQAAD/nAAAAAAAAP84AAAAyAAA/zgAAAAAAAD/nAAAAAAAAP84AAAAAAAEAAAAAAGQAZAAAwAHAAsADwAAAQEBAQEBAQEBAQEBAQEBAQAAAGQAAP+cAMgAyAAA/zj/OADIAAD/OAAAAGQAAP+cAZAAAP+cAAAAZAAA/5wAAAAAAAD/nAAAAAAAAP84AAAABAAAAAABkAGQAAMABwALAA8AAAEBAQEBAQEBAQEBAQEBAQEAZAEsAAD+1P+cAMgAAP84AMgAyAAA/zj/OAEsAAD+1AGQAAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAQAAAAAAZAB9AADAAcACwAPAAABAQEBAQEBAQEBAQEBAQEBAGQAZAAA/5z/nAEsAAD+1ABkAGQAAP+cAGQAyAAA/zgB9AAA/5wAAAAAAAD/nAAAAAAAAP84AAAAAAAA/5wAAAADAAAAAAGQAZAAAwAHAAsAAAEBAQEBAQEBAQEBAQAAAGQAAP+cASwAZAAA/5z/OAEsAAD+1AGQAAD+1AAAASwAAP7UAAAAAAAA/5wAAAAAAAMAAAAAAZABkAADAAcACwAAAQEBAQEBAQEBAQEBAAAAZAAA/5wBLABkAAD/nP84AMgAAP84AZAAAP7UAAABLAAA/tQAAAAAAAD/nAAAAAAABQAAAAAB9AGQAAMABwALAA8AEwAAAQEBAQEBAQEBAQEBAQEBAQEBAQEAAABkAAD/nADIAGQAAP+cAMgAZAAA/5z+1ABkAAD/nADIAGQAAP+cAZAAAP7UAAABLAAA/tQAAAEsAAD+1AAAAAAAAP+cAAAAZAAA/5wAAAAAAAUAAAAAAZABkAADAAcACwAPABMAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAZAAA/5wBLABkAAD/nP84AMgAAP84/5wAZAAA/5wBLABkAAD/nAGQAAD/nAAAAGQAAP+cAAAAAAAA/zgAAAAAAAD/nAAAAGQAAP+cAAAAAAAFAAD/OAGQAZAAAwAHAAsADwATAAABAQEBAQEBAQEBAQEBAQEBAQEBAQAAAGQAAP+cASwAZAAA/5z/OAEsAAD+1ADIAGQAAP+c/tQBLAAA/tQBkAAA/tQAAAEsAAD+1AAAAAAAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAABAAAAAABkAGQAAMABwALAA8AAAEBAQEBAQEBAQEBAQEBAQEAAAGQAAD+cADIAGQAAP+c/5wAZAAA/5z/nAGQAAD+cAGQAAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAUAZAAAAZAB9AADAAcACwAPABMAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAMgAyAAA/zgAAABkAAD/nP+cAMgAAP84AGQAZAAA/5wAAADIAAD/OAH0AAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAABAMgAAAEsAfQAAwAAAQEBAQDIAGQAAP+cAfQAAP4MAAAAAAAFAGQAAAGQAfQAAwAHAAsADwATAAABAQEBAQEBAQEBAQEBAQEBAQEBAQBkAMgAAP84AGQAZAAA/5wAAADIAAD/OAAAAGQAAP+c/5wAyAAA/zgB9AAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAABAAAAMgBkAH0AAMABwALAA8AAAEBAQEBAQEBAQEBAQEBAQEAZADIAAD/OP+cAGQAAP+cASwAZAAA/5z/OADIAAD/OAH0AAD/nAAAAAAAAP+cAAAAZAAA/5wAAAAAAAD/nAAAAAEAyADIASwBLAADAAABAQEBAMgAZAAA/5wBLAAA/5wAAAAAAAUAAABkAfQBkAADAAcACwAPABMAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAZAAA/5wBkABkAAD/nP7UASwAAP7U/5wAZAAA/5wBkABkAAD/nAGQAAD/nAAAAGQAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAGQAAP+cAAAAAAABAAAAyAH0ASwAAwAAAQEBAQAAAfQAAP4MASwAAP+cAAAAAAABAAAAyAH0ASwAAwAAAQEBAQAAAfQAAP4MASwAAP+cAAAAAAADAAAAAAH0AGQAAwAHAAsAAAEBAQEBAQEBAQEBAQAAAGQAAP+cAMgAZAAA/5wAyABkAAD/nABkAAD/nAAAAGQAAP+cAAAAZAAA/5wAAAAAAAMAAABkAfQBkAADAAcACwAAAQEBAQEBAQEBAQEBAMgAZAAA/5z/OAH0AAD+DADIAGQAAP+cAZAAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAAAQBkAMgBkAEsAAMAAAEBAQEAZAEsAAD+1AEsAAD/nAAAAAAABwAAAAAB9AH0AAMABwALAA8AEwAXABsAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAABkAAD/nAGQAGQAAP+c/tQAZAAA/5wAyABkAAD/nP+cAGQAAP+c/5wBLAAA/tQAZABkAAD/nAH0AAD/nAAAAGQAAP+cAAAAAAAA/5wAAABkAAD/nAAAAAAAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAACQAAAAAB9AH0AAMABwALAA8AEwAXABsAHwAjAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAABkAAD/nADIAGQAAP+cAMgAZAAA/5z+1AEsAAD+1P+cAfQAAP4MAGQBLAAA/tT/nABkAAD/nADIAGQAAP+cAMgAZAAA/5wB9AAA/5wAAABkAAD/nAAAAGQAAP+cAAAAAAAA/5wAAAAAAAD/nAAAAAAAAP+cAAAAAAAA/5wAAABkAAD/nAAAAGQAAP+cAAAAAAAFAAAAAAH0AfQAAwAHAAsADwATAAABAQEBAQEBAQEBAQEBAQEBAQEBAQBkAGQAAP+cAMgAZAAA/5z+1AH0AAD+DABkASwAAP7UAGQAZAAA/5wB9AAA/5wAAABkAAD/nAAAAAAAAP84AAAAAAAA/5wAAAAAAAD/nAAAAAAAAQAAAAEAAGdkRzFfDzz1AAMDIAAAAAAAAAAAAAAAAAAAAAAAAP84AfQB9AAAAAgAAgABAAAAAAABAAACWP84AAACWAAAAAAB9AABAAAAAAAAAAAAAAAAAAAAAQJYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4AAAAcAAAASwAAAHUAAACqAAAAswAAAMcAAADbAAAA+gAAAQ4AAAEcAAABJQAAAS4AAAFNAAABdwAAAZAAAAG0AAAB0wAAAewAAAILAAACLwAAAkgAAAJyAAAClgAAAqQAAAK4AAAC1wAAAuUAAAMEAAADIwAAA0IAAANmAAADkAAAA6QAAAO9AAAD3AAAA/UAAAQUAAAEMwAABEcAAARbAAAEhQAABJMAAAS9AAAE5wAABQAAAAUfAAAFQwAABW0AAAWMAAAFmgAABa4AAAXNAAAF9wAABiwAAAZLAAAGagAABn4AAAadAAAGsQAABroAAAbTAAAG8gAABwYAAAclAAAHRAAAB10AAAeBAAAHmgAAB6gAAAe8AAAH4AAAB/QAAAgNAAAIIQAACDoAAAhZAAAIeAAACJEAAAiqAAAIwwAACNcAAAjrAAAJCgAACSkAAAlIAAAJYQAACYAAAAmJAAAJqAAACcEAAAnKAAAJ6QAACfIAAAn7AAAKDwAACiMAAAosAAAKVgAACosAAAqqAABAAAAZwAcAAcAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAABQBCAAMAAQQJAAEAIAAAAAMAAQQJAAIADgAgAAMAAQQJAAMAIAAuAAMAAQQJAAQAIABOAAMAAQQJAAYAHgBuAEYAcgBvAHMAdAB3AGEAcwB0AGUAIABQAGkAeABlAGwAUgBlAGcAdQBsAGEAcgBGAHIAbwBzAHQAdwBhAHMAdABlACAAUABpAHgAZQBsAEYAcgBvAHMAdAB3AGEAcwB0AGUAIABQAGkAeABlAGwARgByAG8AcwB0AHcAYQBzAHQAZQBQAGkAeABlAGwAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) format('truetype');
}
/* FONT:END */

/* Frostwaste is a district at night, and the only light in it is on fire.
   Everything dark here is blue-violet rather than grey — a neutral shadow is
   what makes pixel art look like clip art — and the only saturated warm
   colours are flame, fuel and blood. */
:root {
  --bg: #080b14;
  --panel: #101724;
  --panel-edge: #24314a;
  --ground: #3c5474;        /* snow, by moonlight. the floor is drift */
  --ground-alt: #354b69;
  --warm: #d9a45c;
  --wall: #0f1724;
  --board-bg: #05070c;
  --text: #dbe6f4;
  --dim: #7e91ab;
  --amber: #f0ad46;
  --rust: #cf6535;
  --shimmer: #66d8fb;
  --void: #b79aff;
  --hp: #e2573c;
  --hp-track: #2c1a1c;
  --steel: #7389a5;

  /* Two faces, and the split is the whole point.

     The bitmap face has an 8-pixel em, so it only renders cleanly at 8, 16,
     24… — and at 8 a glyph is five screen pixels tall. Setting the whole page
     in it put 73 of 97 text elements at that size, which is how a pixel-art
     pass made the UI harder to use than it was before. It is also monospace
     with a wide advance, so prose in it reads a letter at a time.

     So: DISPLAY is the bitmap face, for the things that want to look like the
     game — headings, stat numbers, button labels, keycaps — and never below
     16px. TYPE is a plain readable stack for everything you have to *read*.
     Dead Cells does exactly this; the pixels belong to the art. */
  --display: 'Frostwaste', ui-monospace, "Cascadia Mono", Menlo, monospace;
  --type: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;

  --r-common: #cfd6dd;
  --r-uncommon: #6ecf5f;
  --r-rare: #4fa3e8;
  --r-epic: #b07ae8;
  --r-valuable: #e8a33c;
}

* { box-sizing: border-box; }

/* ---------- where the bitmap face is allowed ----------
   Everything the eye takes in as a *shape* — a title, a number, a key on a
   button — wears the game's own typeface, and never under 16px. Everything
   you have to read a word at a time is left in --type. */
/* Chrome buttons carry words you read — SURVIVORS, HOW TO PLAY — so they
   stay in --type with the log and the legend. The display face is for the
   things you take in as a shape. */
#topbar h1, #date-label,
#hp-num, #frost-num,
.ab-key, .ab-lab, .dpad-btn i,
.streak-big, .arch-day b, .day-pip,
.inv-cell .inv-held, .inv-cell .inv-stat, .inv-cell .inv-count,
#class-name, .class-opt-name, #overlay-card h2, .modal-card h2,
#side h3, .modal h3 {
  font-family: var(--display);
}
/* The 8-pixel em only lands on whole screen pixels at multiples of 8, so the
   display face has exactly three sizes: 16, 24, 32. */
.ab-key, .dpad-btn i, .day-pip, #side h3,
.inv-cell .inv-held, .inv-cell .inv-stat, .inv-cell .inv-count {
  font-size: 16px;
}

html, body {
  margin: 0;
  min-height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--type);
}

/* ---------- top bar ---------- */
#topbar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px 14px;
  padding: 14px 18px;
  border-bottom: 2px solid var(--panel-edge);
  position: relative;
  flex-wrap: wrap;
}
#topbar-meta { display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
#logo { width: 32px; height: 32px; flex: none; }
#topbar h1 {
  margin: 0;
  min-width: 0;
  display: flex; align-items: center;
  font-family: var(--type);
  color: var(--amber);
}
#wordmark {
  /* The wordmark is a 7-row bitmap: it renders cleanly at 21px (3x) or 28px
     (4x) and at nothing between. It was 22, which is 3.14 screen pixels per
     glyph pixel — the title of the game, resampled. */
  height: 21px; width: auto; display: block;
  shape-rendering: crispEdges;
  filter: drop-shadow(2px 2px 0 rgba(0, 0, 0, .55));
}
#logo { shape-rendering: crispEdges; }
#date-label { color: var(--dim); font-size: 16px; }
#mode-label {
  font-size: 12px; letter-spacing: 1px; text-transform: uppercase;
  color: var(--rust); border: 2px solid var(--rust); border-radius: 0; padding: 1px 6px;
}
#mode-label:empty { display: none; }
/* every button is a pixel block: 2px hard border, and it *moves* when pressed
   instead of fading */
#share-btn, #sound-btn, #board-actions button, .hint-btn, .dpad-btn, .tbtn, #map-toggle {
  border-width: 2px; border-style: solid; border-color: var(--panel-edge);
  box-shadow: 2px 2px 0 rgba(0, 0, 0, .5);
  font-family: var(--type);
}
#share-btn:active, #sound-btn:active, #board-actions button:active, .hint-btn:active,
.dpad-btn:active, .tbtn:active, #map-toggle:active {
  transform: translate(2px, 2px); box-shadow: none;
}
#topbar-btns {
  position: absolute; right: 18px;
  display: inline-flex; gap: 8px; align-items: center;
}
#share-btn, #sound-btn {
  flex: none; white-space: nowrap;
  background: var(--panel); color: var(--amber);
  padding: 5px 13px; font-size: 16px; letter-spacing: 1px;
  text-transform: uppercase; cursor: pointer;
}
#share-btn:hover, #sound-btn:hover { background: var(--panel-edge); }
/* muted is a state you should be able to see without reading it */
#sound-btn.off { color: var(--dim); border-color: #2a333d; }

/* ---------- layout ---------- */
#layout {
  display: flex;
  gap: 18px;
  align-items: flex-start;
  justify-content: center;
  padding: 18px;
  flex-wrap: wrap;
  max-width: 1400px;
  margin: 0 auto;
}
/* 640 = the board at 2x. Capping the column there means the board, the
   controller and the action row are all one width on a desk, and the 80px
   that used to sit as margin beside the board goes to the side panel. */
#board-wrap { flex: 0 1 640px; max-width: 640px; }
/* Below this the two columns stack, and the panel lines up under the board
   instead of sitting narrow and centred beside nothing. */
@media (max-width: 1000px) { #side { max-width: 640px; } }
#side { flex: 1 1 300px; max-width: 400px; }

#game {
  width: 100%;
  display: block;
  border-width: 2px;
  /* pixel art: never let the renderer smooth an edge */
  shape-rendering: crispEdges;
  image-rendering: pixelated;
  background: var(--board-bg);
  border: 2px solid var(--panel-edge);
  border-radius: 0;
}

#board-actions { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }
#board-actions button {
  flex: 1 1 0; min-width: 0;
  background: var(--panel); color: var(--text);
  border: 2px solid var(--panel-edge); border-radius: 0;
  padding: 7px 4px; font-size: 12px; letter-spacing: .6px;
  text-transform: uppercase; cursor: pointer; white-space: nowrap;
}
#board-actions button:hover { background: var(--panel-edge); color: var(--amber); }
/* five buttons do not fit one row on a phone: three and two */
@media (max-width: 480px) { #board-actions button { flex: 1 1 30%; } }

/* ---------- board ---------- */
/* The ground is settled snow under a night sky — four blue shades mottle it so
   it does not read as a checkerboard, and a grain layer catches the moon. */
.tile { fill: var(--ground); transition: fill .35s ease; }
.tile.drift-0 { fill: #3c5474; }
.tile.drift-1 { fill: #354b69; }
.tile.drift-2 { fill: #435c7e; }
.tile.drift-3 { fill: #385071; }
/* Ground texture in the same 2-unit blocks the sprites are drawn in — and as
   light and shade rather than as colour, so one set of marks works on cold
   drift and on ground a fire has turned warm. A blue fleck on warm snow is a
   hole in it. */
.grain, .drift-edge { pointer-events: none; }
.raise { fill: #ffffff; opacity: .07; }
.sink  { fill: #000814; opacity: .16; }

/* boot prints pressed into the snow: a bright rim of pushed-up snow, a shaded
   sole where the drift is compacted */
.bootprint { pointer-events: none; }
.print-rim { fill: #9dbdd8; }
.print-sole { fill: #16202f; }

/* Light pools. A fire is not a sprite you stand beside, it is the only thing
   lighting this district — so the drift around it is *lit*, in two steps, and
   you read the map by where the light falls. */
/* what the light spills past the tile it was painted on */
.spill-warm { fill: #7a5f3a; }
.spill-glow { fill: #4a5668; }
.spill-hot  { fill: #96703f; }
/* Light falls off; it does not change colour in one step. The outer ring used
   to be an olive of almost exactly the drift's brightness, so instead of
   reading as the edge of a fire's light it read as a khaki stain across five
   tiles. It is the drift with a little warmth in it now, and the heat is
   carried by the two rings inside it. */
.tile.glow { fill: #4a5668; }
.tile.warm { fill: #7a5f3a; }
.tile.warm.hot { fill: #96703f; }


.wall { fill: var(--wall); }
/* the lit face and the shaded one — light comes from the upper left */
.wall-lit { fill: #35507a; }
.wall-dark { fill: #05070e; }

.route-hint { display: none; }
.route-hint { fill: none; stroke: var(--shimmer); stroke-width: 2; stroke-dasharray: 3 5; opacity: .35; }

/* Remembered ground has to stay ground. At .62 over an already-dark board it
   crushed 57% of the tiles into one value and the district read as a smear. */
.fog { fill: #060a12; transition: opacity .25s ease; }
.fog.explored { opacity: .4; }
.fog.clear { opacity: 0; }

.sprite-sheet { position: absolute; width: 0; height: 0; overflow: hidden; }

/* The true edge of the district. Zoomed in you cannot see the whole map, so the
   wall has to announce itself. */
.map-edge-glow { fill: none; stroke: #0a1016; stroke-width: 14; }
.map-edge { fill: none; stroke: #33465a; stroke-width: 7; }
.map-edge-inner {
  fill: none; stroke: var(--shimmer); stroke-width: 2;
  stroke-dasharray: 14 10; opacity: .75;
  animation: edge-drift 6s steps(12, end) infinite;
}
@keyframes edge-drift { to { stroke-dashoffset: -48; } }

/* The legend is laid UNDER the art, never as a filter over it: a slab means
   this ground is taken (solid also stops your sightline), a soft pad means this
   is worth standing on. Sprites then only ever cast a shadow. */
.pad { pointer-events: none; }
/* The plate under an obstacle was a pale slab, drawn back when the board was
   a white snowfield at midday. On a night district it became the brightest
   thing on screen — brighter than the survivor — which is the contrast
   hierarchy upside down. Blocked ground is a hole in the drift now: darker
   than the snow, with a lit edge so it still reads as a thing and not as a
   gap. Solid keeps the heavier edge; low cover keeps the dashes. */
.pad-solid { fill: #16203a; stroke: #4a6288; stroke-width: 2; }
.pad-low { fill: rgba(22, 32, 58, .55); stroke: rgba(74, 98, 136, .7); stroke-width: 2; stroke-dasharray: 4 4; }
.pad-loot, .pad-goal { animation: pad-pulse 2.4s steps(3, end) infinite; }
/* three held levels, not a ramp — the board dims in steps like a bad bulb */
@keyframes pad-pulse { 0%, 100% { opacity: .62; } 34% { opacity: .82; } 67% { opacity: 1; } }

.sprite { transition: transform .16s steps(4, end); }
/* ---------- the idle ----------
   Motion Twin were blunt about the trade they made: "movement is love,
   movement is life", and they accepted that "the disappointing level of
   details is and always will be an issue" in exchange for it. A figure that
   holds perfectly still between turns is a sticker on a board, however well
   it is lit — so everything alive breathes.

   It animates `translate`, not `transform`: the transform attribute is where
   the tile position lives, and the two compose instead of fighting. Two held
   positions on a slow beat, because a smooth bob is sub-pixel motion, and the
   phase is desynced per sprite — a row of enemies breathing in lockstep is
   worse than a row of enemies not breathing at all. */
@keyframes idle-bob { 0%, 55% { translate: 0 0; } 56%, 100% { translate: 0 -1px; } }
.sprite.hero, .sprite.en, .sprite.sentry {
  animation: idle-bob 1.5s steps(1, end) infinite;
}
/* the corpse is the one figure on the board that does not breathe */
.sprite.dead { animation: none; }
/* loot on the snow catches the eye by moving, not by glowing harder */
@keyframes drop-bob { 0%, 49% { translate: 0 0; } 50%, 100% { translate: 0 -1px; } }
.drop { animation: drop-bob 1.1s steps(1, end) infinite; }

/* ---------- the light on a figure ----------
   Dead Cells does not hand-shade its sprites: normal maps and a light system
   do it, and the rim "finds the outmost pixels and calculates which light they
   receive". The rim here is baked into the art, but its COLOUR is not — it is
   a custom property, which is the one thing that inherits through a <use>
   shadow tree, so one sprite can wear two different lights.

   Cold by default: this district is lit by a moon on snow. Warm the moment a
   figure steps inside a fire's pool, because at that point the fire is the
   brighter source — and that is the game's own rule made visible, since the
   fire is also the only thing that stops the frostbite meter. */
/* The rim path ships with no fill of its own, so it inherits this one. `fill`
   is an inherited property and inheritance crosses into a <use> shadow tree in
   every browser — which a custom property in a presentation attribute does
   not: Chrome resolves those and Firefox does not. */
.sprite { fill: #9ec6ef; }
.sprite.lit { fill: #ffcc7d; }
.sprite.en { fill: #b7d2ee; }
.sprite.en.lit { fill: #ffb95e; }

/* a hard contact shadow, offset down-right away from the light. A blurred halo
   under blocky art reads as a rendering mistake rather than as ground. */
.sprite.en, .sprite.hero { filter: drop-shadow(2px 3px 0 rgba(6, 10, 20, .55)); }

/* Scenery sits further from the light than anything that acts. Dead Cells is
   explicit about the order — navigation reads first, interactive things
   second, and enemies are "saturated, bright, immediately identifiable", while
   backgrounds stay "muted and blended, no discernible rupture". Raising the
   palette's saturation made a steel wreck the brightest object on the board,
   which is that rule broken. Cover is pushed back down here rather than
   re-authored, so scenery and actors keep sharing one palette. */
.sprite.cover { filter: saturate(.72) brightness(.62); }
.sprite.cover { filter: drop-shadow(2px 2px 0 rgba(45, 68, 90, .28)); }
.sprite.unaware { opacity: .6; }
/* flash-frozen by the rimekin: iced over until its turn comes back */
.sprite.en.frozen { filter: sepia(1) hue-rotate(160deg) saturate(2.6) brightness(1.25) drop-shadow(2px 2px 0 rgba(45, 68, 90, .38)); }
/* a flamekeeper's burn is a weapon, and its glow says so */
.burn-glow { animation-duration: 1.2s; }
.sprite.dying { opacity: 0; transition: opacity .2s ease; }
#hero-use { transition: transform .16s steps(4, end); }
#hero-weapon { transition: transform .16s steps(4, end); }
#hero-weapon { pointer-events: none; }

.fire-glow { pointer-events: none; animation: fire-flicker 2.6s steps(4, end) infinite; }
@keyframes fire-flicker { 0%, 100% { opacity: .75; } 45% { opacity: 1; } 70% { opacity: .68; } }

.crawler { animation: crawler-pulse 1.8s steps(3, end) infinite; transform-origin: center; }
@keyframes crawler-pulse { 0%, 100% { opacity: .8; } 50% { opacity: 1; } }

/* reticle over the enemy `F` would hit */
.reticle {
  fill: none; stroke: var(--rust); stroke-width: 2.6;
  pointer-events: none;
  stroke-linecap: butt;
  animation: reticle-snap 1s steps(2, end) infinite;
  transform-box: fill-box; transform-origin: center;
}
/* brackets that close and open on the beat. They used to rotate, which is
   smooth sub-pixel motion and the one animation that read as not-pixel-art. */
@keyframes reticle-snap { 0%, 49% { transform: scale(1); } 50%, 100% { transform: scale(.86); } }
.reticle-dot { fill: var(--rust); stroke: none; }

.tracer { stroke: #ffe3a0; stroke-width: 2; opacity: .9; animation: tracer-fade .22s ease-out forwards; }
.tracer.enemy { stroke: #ff8a6a; }
.tracer.sentry { stroke: #7fd0e0; }
@keyframes tracer-fade { to { opacity: 0; } }

.dmg-num, .tag text, .sentry-life, .cover-num { font-family: var(--display); }
.dmg-num {
  font: 700 16px/1 var(--type);
  fill: var(--amber); text-anchor: middle;
  animation: dmg-float 1s ease-out forwards; pointer-events: none;
}
.dmg-num.dmg-hero { fill: #ff6a52; }
.dmg-num.dmg-crit { font-size: 16px; fill: #ffd24a; }
.miss-num, .cover-num {
  font: 700 16px/1 var(--type); text-anchor: middle;
  fill: var(--dim); animation: dmg-float .9s ease-out forwards; pointer-events: none;
}
.cover-num { fill: #7fd0e0; }
@keyframes dmg-float {
  0% { opacity: 0; transform: translateY(4px); }
  20% { opacity: 1; }
  100% { opacity: 0; transform: translateY(-16px); }
}

.particle { pointer-events: none; }
.kill-ring {
  fill: none; stroke: #cfd6dd; stroke-width: 2;
  animation: ring-out .5s steps(4, end) forwards; pointer-events: none;
  transform-box: fill-box; transform-origin: center;
}
/* four held sizes: a shockwave that grows continuously slides between pixels */
@keyframes ring-out { to { transform: scale(2.1); opacity: 0; } }
.burst-ring {
  fill: none; stroke: var(--shimmer); stroke-width: 3;
  animation: ring-out .6s steps(4, end) forwards; pointer-events: none;
  transform-box: fill-box; transform-origin: center;
}

.sentry-life {
  font: 700 8px/1 var(--type); fill: #7fd0e0;
  text-anchor: middle; pointer-events: none;
}

/* Struck: the body itself flashes red where it stands, so a number floating
   off the top of the screen is never the only evidence a hit landed. */
/* three flashes, not one: a single 300ms blink is easy to miss under a
   floating number and the board shake */
.sprite.struck { animation: struck .54s steps(1, end); }
@keyframes struck {
  0%, 33%, 66% { filter: drop-shadow(0 0 0 #ff4a34) brightness(2.6) saturate(3) hue-rotate(-25deg); }
  17%, 50%, 84%, 100% { filter: none; }
}
.dmg-num.dmg-none { fill: var(--dim); font-size: 16px; }
/* the good numbers: a patch, a kit, a thaw */
.heal-num { font: 700 16px/1 var(--type); fill: #7fe08f; text-anchor: middle; animation: dmg-float 1s ease-out forwards; pointer-events: none; }
.thaw-num { font: 700 16px/1 var(--type); fill: var(--shimmer); text-anchor: middle; animation: dmg-float 1s ease-out forwards; pointer-events: none; }

/* On one or two HP the board itself says so: a red pulse at the edges, the
   frostbite vignette's twin. */
#board-wrap.low-hp #board-frame { animation: low-hp 1.1s steps(4, end) infinite; }
@keyframes low-hp {
  0%, 100% { box-shadow: inset 0 0 0 0 rgba(255, 74, 52, 0); }
  50% { box-shadow: inset 0 0 0 6px rgba(255, 74, 52, .55); }
}

/* the round in flight, and the flash it left the barrel with */
.slug { fill: #fff3c4; }
.slug.enemy { fill: #ff8a6a; }
.slug.sentry { fill: #7fd0e0; }
.muzzle rect { fill: #fff3c4; }
.muzzle { animation: muzzle-out .11s steps(2, end) forwards; pointer-events: none; }
@keyframes muzzle-out { to { opacity: 0; } }
.slash rect { fill: #ffffff; }
.slash { animation: slash-out .26s ease-out forwards; pointer-events: none; }
@keyframes slash-out {
  0% { opacity: 0; }
  25% { opacity: .95; }
  100% { opacity: 0; }
}

/* loot leaving the snow for your kit */
.loot-fly {
  position: fixed; z-index: 40; width: 32px; height: 32px;
  pointer-events: none;
}
.loot-fly svg { width: 32px; height: 32px; shape-rendering: crispEdges; }

/* what is lying on the ground, waiting to be walked onto */
.drop-mat { fill: rgba(20, 34, 46, .3); }
.drop-mat.r-uncommon { fill: rgba(110, 207, 95, .55); }
.drop-mat.r-rare { fill: rgba(79, 163, 232, .6); }
.drop-mat.r-epic { fill: rgba(176, 122, 232, .65); }

/* the numbers that belong to a body, drawn on the body */
.tag { pointer-events: none; transition: transform .16s steps(4, end); }
.tag-bg { fill: rgba(12, 20, 28, .72); }
.tag-hp { fill: var(--hp); }
.tag-frz { fill: var(--shimmer); }
.tag-en-hp { fill: #e8604a; }
.tag-text {
  font: 700 8px/1 var(--type); fill: #eef5fb; text-anchor: middle;
  paint-order: stroke; stroke: rgba(10, 18, 26, .85); stroke-width: 2.5;
}
.tag-dmg { fill: #eef5fb; text-anchor: start; }
.tag-ammo { fill: var(--amber); text-anchor: end; }
.tag-kind rect { fill: #eef5fb; }
.tag-kind.ranged rect { fill: #ffd24a; }

#board-wrap.shake { animation: board-shake .22s steps(3, end); }
#board-wrap.shake-2 { animation: board-shake .28s steps(4, end); }
#board-wrap.shake-3 { animation: board-shake-hard .32s steps(5, end); }
@keyframes board-shake {
  0%, 100% { transform: translate(0, 0); }
  25% { transform: translate(-3px, 1px); }
  75% { transform: translate(3px, -1px); }
}
@keyframes board-shake-hard {
  0%, 100% { transform: translate(0, 0); }
  20% { transform: translate(-6px, 3px); }
  50% { transform: translate(5px, -3px); }
  80% { transform: translate(-3px, 2px); }
}
@media (prefers-reduced-motion: reduce) {
  #board-wrap.shake, #board-wrap.shake-2, #board-wrap.shake-3 { animation: none; }
}

/* ---------- side panel ---------- */
#side { display: flex; flex-direction: column; gap: 12px; }

#class-box {
  background: var(--panel);
  border: 2px solid var(--panel-edge);
  border-radius: 0;
  padding: 12px 14px;
}
#class-head { display: flex; gap: 10px; align-items: center; }
#class-icon { width: 32px; height: 32px; flex: none; }
#class-name {
  font-family: var(--type); font-size: 16px;
  letter-spacing: 2px; text-transform: uppercase; color: var(--amber);
}
#class-power { font-size: 16px; color: var(--shimmer); }
#class-blurb { font-size: 16px; line-height: 1.45; color: var(--dim); margin-top: 8px; }
/* Bullets, not a paragraph: a player checks this with something walking at
   them. Same list style in the class picker and the bestiary. */
.points { margin: 6px 0 0; padding-left: 14px; }
.points li { margin: 0 0 4px; color: var(--dim); line-height: 1.4; }
.points li::marker { color: var(--amber); }
.class-opt-lead { display: block; font-size: 16px; color: var(--shimmer); margin: 2px 0 4px; }
.bestiary-lead { color: var(--text); margin-top: 3px; }
.bestiary-row .points { font-size: 12px; }

/* the legend's verdict on each thing, coloured the way the board colours it */
.legend-tag {
  display: inline-block; margin-left: 8px; padding: 1px 6px;
  font-size: 12px; font-weight: 700; letter-spacing: 1px; vertical-align: 1px;
  border: 2px solid currentColor;
}
.tag-goal { color: var(--amber); }
.tag-loot { color: #ffd24a; }
.tag-safe { color: var(--shimmer); }
.tag-danger { color: #ff6a52; }
.tag-block { color: var(--dim); }
.tag-cover { color: #a9bdd0; }

/* One column. Two columns of label/value pairs fitted on a desktop and turned
   the panel into a crossword on a phone, which is where this is played. */
#stats {
  width: 100%; border-collapse: collapse;
  border: 2px solid var(--panel-edge); background: var(--panel);
  font-size: 16px;
}
#stats th {
  width: 34%; text-align: left; padding: 5px 8px;
  color: var(--dim); font-weight: 400; text-transform: uppercase;
  letter-spacing: .8px; font-size: 12px; white-space: nowrap;
}
#stats td { padding: 5px 8px; color: var(--text); font-weight: 700; }
#stats tr + tr th, #stats tr + tr td { border-top: 2px solid var(--panel-edge); }
#stats .warn { color: var(--rust); }
#stats .good { color: var(--shimmer); }

/* ---------- the record ----------
   What the streak is actually made of: one ranked run a day, and the seven
   behind it. Practice never appears here, which is the whole point of it. */
#streak-box {
  border: 2px solid var(--panel-edge); background: var(--panel); padding: 10px;
}
#streak-box h3 {
  margin: 0 0 8px; font-size: 16px; text-transform: uppercase;
  letter-spacing: 1.4px; color: var(--dim); font-weight: 400;
}
#streak-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.streak-big { font-size: 16px; font-weight: 700; color: var(--amber); }
.streak-note { font-size: 12px; color: var(--dim); }
#streak-days { display: flex; gap: 4px; margin-top: 9px; flex-wrap: wrap; }
.day-pip {
  flex: 1 1 0; min-width: 26px; text-align: center;
  font-size: 16px; padding: 4px 0;
  border: 2px solid var(--panel-edge); background: #101820; color: var(--dim);
}
.day-pip.win { border-color: #3f7d52; background: #16281c; color: #8fdb9f; }
.day-pip.loss { border-color: #7d3f3f; background: #281618; color: #db8f8f; }

/* ---------- the archive ---------- */
#archive-list {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 6px;
}
.arch-day {
  display: flex; flex-direction: column; gap: 2px;
  padding: 7px 9px; text-decoration: none;
  border: 2px solid var(--panel-edge); background: #101820; color: var(--text);
  font-size: 12px;
}
.arch-day b { font-size: 16px; letter-spacing: .5px; }
.arch-day span { color: var(--dim); font-size: 12px; }
.arch-day:hover { border-color: var(--amber); }
.arch-day.today { border-color: var(--amber); background: #1d1810; }
.arch-day.win b { color: #8fdb9f; }
.arch-day.loss b { color: #db8f8f; }
.arch-day.unplayed b { color: var(--dim); }

#bars {
  background: var(--panel); border: 2px solid var(--panel-edge);
  border-radius: 0; padding: 10px 12px;
  display: flex; flex-direction: column; gap: 8px;
}
.bar-row { display: flex; align-items: center; gap: 8px; }
.bar-label {
  width: 34px; font-size: 12px; letter-spacing: 1px;
  text-transform: uppercase; color: var(--dim);
}
#hp-track { flex: 1; display: flex; gap: 2px; height: 13px; }
.hp-seg { flex: 1; background: var(--hp-track); }
.hp-seg.on { background: var(--hp); }
.hp-seg.lost { background: #241a18; opacity: .55; }
/* blocks, not a bar: frostbite is spent in steps, so it has to be countable at
   a glance the way HP is */
#frost-track {
  flex: 1; height: 14px; display: flex; gap: 2px;
}
/* Empty is safe. Each block is 10 frostbite going in, and a fire takes the
   whole column back out. */
.frost-seg { flex: 1; background: #1b2733; transition: background .2s ease; }
.frost-seg.on { background: var(--shimmer); }
.frost-seg.on.critical { background: #ff6a52; }
#hp-num, #frost-num {
  width: 56px; text-align: right; font-size: 16px;
  font-variant-numeric: tabular-nums; color: var(--dim);
}

#inventory {
  background: var(--panel); border: 2px solid var(--panel-edge);
  border-radius: 0; padding: 10px 12px;
}
#inventory h3 {
  margin: 0 0 8px; font-size: 16px; letter-spacing: 1.6px;
  text-transform: uppercase; color: var(--dim); font-weight: 600;
}
.inv-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 6px; }
.inv-cell {
  position: relative; aspect-ratio: 1;
  display: flex; align-items: center; justify-content: center;
  background: #141b23; border: 2px solid #3a4654;
  cursor: help; overflow: hidden;
}
/* Sprites are 32 units of art. Off the board they were being drawn at 24, 30,
   34 and 40px — 0.75x, 0.94x, 1.06x and 1.25x — so every panel in the game was
   resampling the art it was showing, while the board itself was held to whole
   pixels. One art pixel, one screen pixel, everywhere. */
.inv-cell svg { width: 32px; height: 32px; shape-rendering: crispEdges; }

/* quality reads as a corner ribbon; the frame is left to mean "equipped" */
.inv-rib {
  position: absolute; top: 0; right: 0; width: 12px; height: 12px;
  /* a stepped corner, so the quality flag is pixels like everything else */
  clip-path: polygon(100% 0, 100% 100%, 66% 100%, 66% 66%, 33% 66%, 33% 33%, 0 33%, 0 0);
  background: var(--r-common);
}
.inv-cell.uncommon .inv-rib { background: var(--r-uncommon); }
.inv-cell.rare     .inv-rib { background: var(--r-rare); }
.inv-cell.epic     .inv-rib { background: var(--r-epic); }
.inv-cell.valuable .inv-rib { background: var(--r-valuable); }
.inv-cell.epic { box-shadow: inset 0 0 0 2px rgba(176, 122, 232, .55); }

.inv-cell.held { border-color: var(--amber); background: #241f14; }
.inv-cell .inv-held {
  position: absolute; left: 2px; bottom: 1px;
  font-size: 16px; font-weight: 400; letter-spacing: 0; color: var(--amber);
}
.inv-cell .inv-stat {
  position: absolute; top: 0; left: 2px;
  font-family: var(--type); font-size: 12px; color: var(--amber); font-weight: 700;
}
.inv-cell .inv-kind {
  position: absolute; left: 12px; right: 13px; top: 0; text-align: left;
  font-family: var(--type);
  font-size: 12px; letter-spacing: .5px; text-transform: uppercase; color: var(--dim);
  white-space: nowrap; overflow: hidden; pointer-events: none;
}
.inv-cell .inv-count {
  position: absolute; right: 2px; bottom: 0;
  font-size: 16px; color: var(--text); font-weight: 400;
}
.inv-cell.disabled { opacity: .5; }
.inv-cell.disabled svg { filter: grayscale(1); opacity: .75; }
.inv-cell.disabled .inv-rib { filter: grayscale(1); opacity: .6; }
.inv-cell.disabled::after {
  content: ''; position: absolute; inset: 5px;
  background:
    linear-gradient(to bottom right, transparent calc(50% - .6px), #8a97a5 calc(50% - .6px), #8a97a5 calc(50% + .6px), transparent calc(50% + .6px)),
    linear-gradient(to bottom left,  transparent calc(50% - .6px), #8a97a5 calc(50% - .6px), #8a97a5 calc(50% + .6px), transparent calc(50% + .6px));
  opacity: .65;
}

/* the day's objective, and the fires that let you reach it */
#frost-track.warm { box-shadow: 0 0 0 2px rgba(232, 163, 60, .85); }
#frost-num.warn { color: #e8735c; font-weight: 700; }
#frost-num.warming { color: var(--amber); }

#log {
  background: var(--panel); border: 2px solid var(--panel-edge);
  border-radius: 0; padding: 10px 12px;
  font-size: 16px; line-height: 1.6; color: var(--dim);
  min-height: 82px;
}
#log div:last-child { color: var(--text); }

/* ---------- off-screen target ---------- */
.offscreen-target circle { fill: rgba(232, 163, 60, .22); stroke: var(--amber); stroke-width: 1.5; }
.offscreen-arrow { fill: var(--amber); }

/* ---------- minimap ---------- */
/* The district map is a corner toggle, not a standing panel: on a phone it was
   taking a third of the screen for something you consult now and then. */
#map-toggle {
  position: absolute; top: 8px; right: 8px; z-index: 6;
  width: 34px; height: 34px; line-height: 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(22, 34, 46, .82); color: var(--text);
  border: 2px solid rgba(120, 150, 175, .5); border-radius: 0;
  font-size: 16px; cursor: pointer; padding: 0;
}
#map-toggle:hover, #map-toggle.on { background: rgba(30, 48, 64, .95); color: var(--amber); }
#map-box {
  position: absolute; top: 48px; right: 8px; z-index: 6;
  background: rgba(16, 26, 36, .96); border: 2px solid var(--panel-edge);
  border-radius: 0; padding: 10px; line-height: 1.4;
  box-shadow: 4px 4px 0 rgba(0, 0, 0, .5);
}
#map-box.hidden { display: none; }
#minimap {
  width: 190px; height: 190px; display: block;
  background: var(--board-bg); shape-rendering: crispEdges;
}
@media (max-width: 480px) {
  #map-box { left: 8px; right: 8px; }
  #minimap { width: 100%; height: auto; }
}
.mm-cell { fill: #0b1218; }
.mm-cell.known { fill: #23303c; }
.mm-cell.seen { fill: #3c4c5b; }
.mm-cell.known.solid, .mm-cell.seen.solid { fill: #55606b; }
.mm-cell.known.low, .mm-cell.seen.low { fill: #46525e; }
.mm-frame { fill: none; stroke: #33465a; stroke-width: 2; }
.mm-view { fill: none; stroke: var(--amber); stroke-width: 1.5; opacity: .85; }
.mm-hero { fill: #ffe3a0; stroke: #12202c; stroke-width: 1; }
.mm-crawler { fill: var(--shimmer); stroke: #12202c; stroke-width: 1; }
#map-key {
  display: flex; flex-wrap: wrap; gap: 4px 10px; margin-top: 8px;
  font-size: 12px; color: var(--dim); text-transform: uppercase; letter-spacing: .5px;
}
#map-key span { display: inline-flex; align-items: center; gap: 4px; }
.mk { width: 8px; height: 8px; display: inline-block; }
.mk.hero { background: #ffe3a0; }
.mk.crawler { background: var(--shimmer); }
.mk.view { background: none; border: 1.5px solid var(--amber); border-radius: 0; }

/* ---------- the controller ----------
   One strip, on every screen. Not a phone fallback and not a key list — the
   same buttons a finger taps are the ones a desk player reads their keys off,
   so the two control schemes teach each other.

   Sizing is off finger targets, not off text: 52px pad keys and 54px action
   buttons at desk size, 60/62 on a phone where the strip gets the full width.
   Everything below 44px is a missed tap. */
#controls {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 14px; margin-top: 10px; padding: 10px;
  background: var(--panel); border: 2px solid var(--panel-edge);
}

/* the pad: a real cross, three columns wide, with the key under the arrow */
#dpad {
  flex: none;
  display: grid; grid-template-columns: repeat(3, 52px); grid-template-rows: repeat(2, 52px);
  gap: 6px;
}
.dpad-btn:nth-child(1) { grid-column: 2; grid-row: 1; }
.dpad-btn:nth-child(2) { grid-column: 1; grid-row: 2; }
.dpad-btn:nth-child(3) { grid-column: 2; grid-row: 2; }
.dpad-btn:nth-child(4) { grid-column: 3; grid-row: 2; }
.dpad-btn {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  background: #101820; color: var(--text);
  border: 2px solid var(--panel-edge); border-radius: 0;
  cursor: pointer; padding: 0;
  touch-action: manipulation; user-select: none; -webkit-tap-highlight-color: transparent;
}
.dpad-btn b { font-size: 16px; line-height: 0; display: flex; justify-content: center; }
.dpad-btn i {
  font-style: normal; font-size: 16px; letter-spacing: .6px; color: var(--dim);
}
.dpad-btn:hover { border-color: var(--amber); }
.dpad-btn:active { background: var(--panel-edge); color: var(--amber); }

/* the actions: four across, wrapping to two rows */
#actions {
  flex: 1 1 auto;
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px;
  max-width: 460px;
}
.abtn {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
  min-height: 54px; padding: 6px 3px;
  background: #101820; color: var(--text);
  border: 2px solid var(--panel-edge); border-radius: 0;
  cursor: pointer; font-family: var(--display);
  touch-action: manipulation; user-select: none; -webkit-tap-highlight-color: transparent;
}
.abtn:hover:not(.off) { border-color: var(--amber); }
.abtn:active { background: var(--panel-edge); }
/* the keyboard shortcut, in the corner the eye checks last */
.ab-key {
  position: absolute; top: 2px; left: 3px;
  font-size: 16px; line-height: 1; letter-spacing: 0;
  color: var(--dim); border: 1px solid var(--panel-edge); padding: 1px 3px;
  pointer-events: none;
}
/* The pictograms used to be emoji: full-colour vector glyphs, rendered
   smooth and differently by every OS, on a page whose every other mark is a
   pixel. They are sprites now, and 16px is two whole screen pixels per art
   pixel on the 16x16 grids they are drawn on. */
.ab-ico { font-size: 16px; line-height: 0; }
.ab-ico svg, .dpad-btn b svg, #map-toggle svg, .ico {
  display: block; width: 16px; height: 16px;
  shape-rendering: crispEdges;
}
/* wherever an icon sits in a line of text */
.ico { display: inline-block; vertical-align: -2px; flex: none; }
.ab-lab {
  font-size: 16px; text-transform: none; letter-spacing: 0;
  color: var(--text); line-height: 1.1;
}
.ab-sub {
  font-family: var(--type); font-size: 12px; color: var(--amber); line-height: 1.1;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* not possible right now: no ammo, nothing in the reticle, no charges left */
.abtn.off { opacity: .38; cursor: default; }
.abtn.off .ab-sub { color: var(--dim); }

/* A phone gives the strip the whole width, so the buttons get bigger, not
   smaller — the opposite of what a naive shrink would do. */
@media (pointer: coarse), (max-width: 760px) {
  #controls { gap: 10px; padding: 8px; }
  #dpad { grid-template-columns: repeat(3, 60px); grid-template-rows: repeat(2, 60px); gap: 5px; }
  .dpad-btn b { font-size: 24px; }
  #actions { grid-template-columns: repeat(4, 1fr); gap: 5px; max-width: none; }
  .abtn { min-height: 62px; }
  .ab-ico { font-size: 16px; }
}

/* Narrow phones: the pad and the actions stop competing for one row. The pad
   centres, the actions take the full width underneath in four columns. */
@media (max-width: 520px) {
  #controls { flex-direction: column; align-items: center; gap: 8px; }
  #dpad { grid-template-columns: repeat(3, 66px); grid-template-rows: repeat(2, 58px); }
  #actions { width: 100%; }
}

/* ---------- modals & overlay ---------- */
.modal {
  position: fixed; inset: 0; z-index: 20;
  background: rgba(8, 7, 6, .82);
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
.modal.hidden, #overlay.hidden { display: none; }
.modal-card {
  position: relative;
  box-shadow: 4px 4px 0 rgba(0, 0, 0, .55);
  background: var(--panel); border: 2px solid var(--panel-edge);
  border-radius: 0; padding: 20px 22px;
  max-width: 620px; width: 100%; max-height: 86vh; overflow: auto;
}
.modal-card h3 {
  margin: 0 0 6px; font-family: var(--type); font-size: 16px;
  letter-spacing: 2px; text-transform: uppercase; color: var(--amber);
}
.modal-note { margin: 0 0 14px; font-size: 16px; color: var(--dim); }
.modal-close {
  position: absolute; top: 10px; right: 12px;
  background: none; border: none; color: var(--dim);
  font-size: 24px; line-height: 1; cursor: pointer;
}
.modal-close:hover { color: var(--text); }

.class-opt {
  display: flex; gap: 12px; width: 100%; text-align: left;
  background: #17150f; border: 2px solid var(--panel-edge);
  border-radius: 0; padding: 11px 13px; margin-bottom: 8px;
  color: var(--text); cursor: pointer;
  font-family: var(--type);   /* a <button> does not inherit the page face */
}
.class-opt:hover { border-color: var(--amber); background: #221d14; }
/* the ranked run is played as the day's survivor; the others are for practice */
.class-opt:disabled { opacity: .5; cursor: not-allowed; border-color: var(--panel-edge); background: none; }
.class-opt > svg { width: 32px; height: 32px; flex: none; }
.class-opt-text { flex: 1; min-width: 0; }
.class-opt-name {
  display: block;
  font-family: var(--type); letter-spacing: 1.4px;
  text-transform: uppercase; color: var(--amber); font-size: 16px;
}
.class-opt-stats { display: block; font-size: 12px; color: var(--shimmer); margin: 3px 0; }
.class-opt-desc { display: block; font-size: 16px; color: var(--dim); line-height: 1.45; margin-top: 4px; }

.bestiary-row {
  display: flex; gap: 12px; padding: 9px 0;
  border-bottom: 2px solid var(--panel-edge);
}
.bestiary-row svg { width: 32px; height: 32px; flex: none; }
.bestiary-text { flex: 1; font-size: 16px; line-height: 1.45; }
.bestiary-name {
  font-family: var(--type); letter-spacing: 1.2px;
  text-transform: uppercase; color: var(--text);
}
.bestiary-stats { color: var(--shimmer); font-size: 12px; }
.bestiary-desc { color: var(--dim); margin-top: 3px; }

.help-body { font-size: 16px; line-height: 1.6; color: var(--dim); }
.help-body b { color: var(--text); }
.keys { width: 100%; border-collapse: collapse; margin: 12px 0; }
.keys th {
  text-align: left; padding: 4px 12px 4px 0; white-space: nowrap;
  color: var(--amber); font-size: 16px; font-weight: 600;
}
.keys td { padding: 4px 0; }
.help-body ul { padding-left: 18px; margin: 10px 0 0; }
.help-body li { margin-bottom: 6px; }

#overlay {
  position: fixed; inset: 0; z-index: 30;
  background: rgba(8, 7, 6, .88);
  display: flex; align-items: center; justify-content: center; padding: 20px;
}
#overlay-card {
  background: var(--panel); border: 2px solid var(--panel-edge);
  box-shadow: 4px 4px 0 rgba(0, 0, 0, .55);
  border-radius: 0; padding: 24px 26px;
  max-width: 460px; width: 100%; text-align: center; max-height: 90vh; overflow: auto;
}
#overlay-card h2 {
  margin: 0 0 12px; font-family: var(--display); font-size: 24px;
  letter-spacing: 3px; text-transform: uppercase; color: var(--amber);
}
#overlay-body .big { font-size: 16px; margin: 6px 0; }
#overlay-body .best { color: var(--shimmer); font-weight: 700; }
#overlay-body .hint { color: var(--dim); font-size: 16px; }
#share-snippet {
  background: #17150f; border: 2px solid var(--panel-edge);
  border-radius: 0; padding: 10px; text-align: left;
  font-size: 16px; line-height: 1.5; white-space: pre-wrap;
  /* the last line is this page's URL — one unbroken word, and on a domain of
     any length it ran past the box and pushed the card out with it. Breaking
     it is a wrap, not an edit: what gets copied is still the whole line. */
  overflow-wrap: anywhere; word-break: break-word;
  color: var(--text); margin: 14px 0 10px;
}
.hint-btn {
  background: var(--panel); color: var(--amber);
  border: 2px solid var(--panel-edge); border-radius: 0;
  padding: 7px 14px; font-size: 16px; letter-spacing: 1px;
  text-transform: uppercase; cursor: pointer; margin: 0 4px;
}
.hint-btn:hover { background: var(--panel-edge); }

#credits {
  border-top: 2px solid var(--panel-edge);
  padding: 14px 18px 22px; text-align: center;
  color: var(--dim); font-size: 12px; line-height: 1.6;
}
#credits a { color: var(--amber); text-decoration: none; }
#credits b { color: var(--text); }

/* ---------- narrow screens ----------
   The layout was a two-column flex with a centred header and an absolutely
   positioned Share button, and nothing below 760px had ever been sized: the
   title, the date chip and the button all landed on the same pixels. */
@media (max-width: 760px) {
  #topbar {
    justify-content: flex-start;
    gap: 8px 10px;
    padding: 10px 12px;
  }
  /* 26 is 0.81x of the 32-unit mark. The mark has two legal sizes and 26 is
     not one of them; on a phone it drops to half. */
  #logo { width: 16px; height: 16px; }
  #topbar h1 { font-size: 16px; letter-spacing: 2.4px; }
  #topbar-meta { flex: 1 1 auto; justify-content: flex-start; }
  #date-label { font-size: 16px; }
  #share-btn {
    position: static;
    margin-left: auto;
    padding: 5px 11px; font-size: 12px;
  }

  #layout { padding: 10px; gap: 12px; }
  #board-wrap, #side { flex: 1 1 100%; max-width: 100%; }
  #board-actions button { padding: 9px 8px; font-size: 12px; }

  /* the board is the game: give it the width and put the panels underneath */
  #board-wrap { order: 1; }
  #side { order: 2; }
  #map-box { max-width: none; }
  #minimap { max-width: 260px; }

  #stats { font-size: 16px; }
  #stats th, #stats td { padding: 3px 4px; }
  .inv-grid { grid-template-columns: repeat(8, 1fr); }
  #credits { padding: 12px 14px 18px; font-size: 12px; }
  .modal { padding: 10px; }
  .modal-card { padding: 16px 15px; max-height: 90vh; }
  #overlay { padding: 12px; }
  #overlay-card { padding: 18px 16px; }
  #overlay-card h2 { font-size: 16px; letter-spacing: 2px; }
  #share-snippet { font-size: 12px; }
}

/* Two buttons no longer fit beside the date on a phone, so below this width
   they stop being pinned to the corner and wrap onto their own line. */
@media (max-width: 620px) {
  #topbar { gap: 8px 10px; padding: 10px 12px; }
  #topbar-btns { position: static; }
}

@media (max-width: 420px) {
  #topbar h1 { font-size: 16px; letter-spacing: 1.6px; }
  #mode-label { display: none; }
  #dpad { grid-template-columns: repeat(3, 46px); grid-template-rows: repeat(2, 46px); }
  #touch-buttons { grid-template-columns: repeat(3, minmax(58px, 1fr)); }
  .inv-grid { grid-template-columns: repeat(6, 1fr); }
}

/* ---------- snowfall ----------
   A canvas, after three CSS attempts. The last of those put a position and a
   size inside `background-image`, which is not legal there, so the browser
   dropped the whole declaration and the board had no snow on it at all. The
   flakes are square and land on whole pixels; render.js drives them. */
/* render.js gives this an explicit width: a whole multiple of the 320 units
   of art, so every art pixel is a whole number of screen pixels. The slack
   that leaves goes evenly into the margin. */
#board-frame { position: relative; line-height: 0; margin: 0 auto; max-width: 100%; }
#snow {
  position: absolute; inset: 0; z-index: 4;
  width: 100%; height: 100%;
  pointer-events: none;
  image-rendering: pixelated;
}

/* ---------- the board icing over ----------
   The frostbite meter has a twin on the board itself: rime creeps in from the
   edges at five blocks, closes in at eight, and at ten the district has you.
   render.js draws it into this canvas in 2-unit blocks, only when the meter
   moves; scaled up here without smoothing, so the ice is pixels like the rest. */
#frost-vig {
  position: absolute; inset: 0; z-index: 5;
  width: 100%; height: 100%;
  pointer-events: none;
  image-rendering: pixelated;
}

/* the errand on the overview */
.mm-canister { fill: #ffc861; stroke: #12202c; stroke-width: .6; }
.mm-canister.taken { fill: none; stroke: #6b7a86; stroke-width: 1; }
.mk.canister { background: #ffc861; border-radius: 0; }

/* ---------- fire ----------
   A flame that does not move is a lamp. The flame is its own sprite over the
   barrel (render.js splits it off), and it flickers in held frames: a pixel
   hop and a dip in the light, on the <use> itself. Animating the paths inside
   every barrel's shadow tree repainted the whole board sixty times a second. */
.fire-flame { animation: flame-flick 1.3s steps(1, end) infinite; }
@keyframes flame-flick {
  0%, 27%   { translate: 0 0;    opacity: 1; }
  28%, 49%  { translate: 0 -1px; opacity: .86; }
  50%, 76%  { translate: 0 0;    opacity: .95; }
  77%, 100% { translate: 0 -1px; opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .fire-flame, .sprite.hero, .sprite.en, .sprite.sentry, .drop { animation: none; }
}

/* sprites rendered outside the board — kit grid, bestiary, class picker — are
   the same pixel art and must not be smoothed either */
.bestiary-row svg, .class-opt > svg, #class-icon, .tbtn svg {
  shape-rendering: crispEdges;
}
