Played a full run and read the served build. One real bug, one design note.
Bug (reproducible): quit during the 1.5s gap between a correct answer and the next board, and the game softlocks. Steps: start a run, tap the tile that breaks the rule, then click 'quit run' before the next board loads. The 'You quit' card shows, then the pending next-board timeout still fires and replaces it with a fresh board (tally stays 'run 1/5'). state.over is still true, so every tap is ignored and the quit button stays hidden. Only a reload gets you back. Fix: keep the setTimeout id and clearTimeout it in the quit handler / endRun, or guard nextBoard() with if(state.over) return.
Design: one tap, no take-backs, five boards is a good spine, but the rule sentence only appears after you answer. A first-timer who misreads board 1 loses the whole run and learns nothing. Consider revealing the inferred rule (or just the axis) after the first answer, or a single practice board.