diff --git a/site/index.html b/site/index.html
index b5f8c92..55afdd4 100644
--- a/site/index.html
+++ b/site/index.html
@@ -28,6 +28,7 @@
Generic Scorecard - 4 Players
Mahjong Scorecard
Finska Scorecard
+ Mental Arithmatic Game
Neon Snake: vibe-coded by Google Gemini
diff --git a/site/mental-arithmatic/index.html b/site/mental-arithmatic/index.html
new file mode 100644
index 0000000..31e8a6c
--- /dev/null
+++ b/site/mental-arithmatic/index.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+ Mental Arithmatic - Tools
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/site/mental-arithmatic/scripts/game.js b/site/mental-arithmatic/scripts/game.js
new file mode 100644
index 0000000..e583a19
--- /dev/null
+++ b/site/mental-arithmatic/scripts/game.js
@@ -0,0 +1,104 @@
+import { Controller } from "https://unpkg.com/@hotwired/stimulus/dist/stimulus.js";
+
+export class GameController extends Controller {
+ static targets = [
+ "clock",
+ "score",
+ "problem",
+ "indicators",
+ "answer",
+ ];
+
+ start() {
+ this.startGame();
+ this.element.classList.remove('hidden');
+ }
+
+ submitAnswer(ev) {
+ ev.preventDefault();
+
+ this._checkAnswer();
+ }
+
+ startGame() {
+ this._clock = 120;
+ this._score = 0;
+
+ this._generateProblem();
+ this._startClock();
+
+ this._updateClock();
+ }
+
+ _startClock() {
+ this._clockInterval = window.setInterval(() => {
+ if (this._clock <= 0) {
+ this._gameOver();
+ return;
+ }
+
+ this._clock -= 1;
+ this._updateClock();
+ }, 1000);
+ }
+
+ _gameOver() {
+ window.clearInterval(this._clockInterval);
+
+ this.element.classList.add('hidden');
+ window.dispatchEvent(new CustomEvent("endGame"));
+ }
+
+ _generateProblem() {
+ const num1 = Math.floor(Math.random() * 99) + 1;
+ const num2 = Math.floor(Math.random() * 99) + 1;
+
+ this._problem = [num1, num2];
+ this._answer = num1 + num2;
+
+ this.indicatorsTarget.classList.remove('right', 'wrong');
+ this.problemTarget.innerHTML = '';
+
+ for (let i = 0; i < this._problem.length; i++) {
+ const div = document.createElement('div');
+
+ if (i === this._problem.length - 1) {
+ div.textContent = '+ ' + this._problem[i];
+ } else {
+ div.textContent = this._problem[i];
+ }
+ this.problemTarget.appendChild(div);
+ }
+
+ this.answerTarget.disabled = false;
+ this.answerTarget.value = "";
+ this.answerTarget.focus();
+ }
+
+ _checkAnswer() {
+ let isRight = parseInt(this.answerTarget.value) === this._answer;
+
+ this.answerTarget.disabled = true;
+
+ let delay = 500;
+
+ if (isRight) {
+ this._score += 1;
+ this.indicatorsTarget.classList.add('right');
+ } else {
+ this.indicatorsTarget.classList.add('wrong');
+ this.answerTarget.value = this._answer;
+ delay = 800;
+ }
+
+ this.scoreTarget.textContent = this._score;
+
+ window.setTimeout(() => { this._generateProblem(); }, delay);
+ }
+
+ _updateClock() {
+ let m = Math.floor(this._clock / 60);
+ let s = this._clock % 60;
+ this.clockTarget.textContent = `${m}:${s < 10 ? '0' : ''}${s}`;
+ }
+}
\ No newline at end of file
diff --git a/site/mental-arithmatic/scripts/main.js b/site/mental-arithmatic/scripts/main.js
new file mode 100644
index 0000000..c15b905
--- /dev/null
+++ b/site/mental-arithmatic/scripts/main.js
@@ -0,0 +1,8 @@
+import { Application } from "https://unpkg.com/@hotwired/stimulus/dist/stimulus.js";
+import { WelcomeController } from "./welcome.js"
+import { GameController } from "./game.js"
+
+window.Stimulus = Application.start();
+
+window.Stimulus.register('welcome', WelcomeController);
+window.Stimulus.register('game', GameController);
\ No newline at end of file
diff --git a/site/mental-arithmatic/scripts/welcome.js b/site/mental-arithmatic/scripts/welcome.js
new file mode 100644
index 0000000..c4ce76f
--- /dev/null
+++ b/site/mental-arithmatic/scripts/welcome.js
@@ -0,0 +1,14 @@
+import { Controller } from "https://unpkg.com/@hotwired/stimulus/dist/stimulus.js";
+
+export class WelcomeController extends Controller {
+ startGame(ev) {
+ ev.preventDefault();
+
+ this.element.classList.add('hidden');
+ window.dispatchEvent(new CustomEvent("startGame"));
+ }
+
+ gameEnded(ev) {
+ this.element.classList.remove('hidden');
+ }
+}
\ No newline at end of file
diff --git a/site/mental-arithmatic/style.css b/site/mental-arithmatic/style.css
new file mode 100644
index 0000000..58d4173
--- /dev/null
+++ b/site/mental-arithmatic/style.css
@@ -0,0 +1,99 @@
+.hidden {
+ display: none !important;
+}
+
+body {
+ min-height: 100dvh;
+ --pico-form-element-disabled-opacity: 1.0;
+}
+
+.game-card {
+ display: flex;
+ flex-direction: column;
+ min-height: 80dvh;
+}
+
+.game-card header,
+.game-card footer {
+ display: flex;
+ justify-content: space-between;
+
+ font-size: 2em;
+ margin-block: 12px;
+ margin-inline: 20px;
+}
+
+.game-card main {
+ flex-grow: 1;
+ flex-shrink: 1;
+
+ display: flex;
+ align-items: center;
+}
+
+.game-card .question {
+ margin: auto;
+ width: 60vw;
+
+ text-align: right;
+ font-size: 3em;
+}
+
+.game-card .answer {
+ border-block: 2px solid black;
+ padding-block: 2px;
+ position: relative;
+}
+
+.game-card .answer.right {
+ border-color: green;
+ color: green;
+}
+
+.game-card .answer.wrong {
+ border-color: red;
+ color: red;
+}
+
+.game-card .answer .indicator {
+ display: none;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+}
+
+.game-card .answer.right .indicator.right {
+ display: block;
+}
+
+.game-card .answer.wrong .indicator.wrong {
+ display: block;
+}
+
+.game-card .question input {
+ border-inline: none;
+ border-radius: 0;
+ box-shadow: none;
+
+ border: none;
+
+ margin: 0;
+ padding: 0;
+
+ text-align: right;
+ font-size: 1em;
+ height: 1em;
+}
+
+.game-card .question input:focus {
+ border-none: none;
+}
+
+.game-card .answer.right input {
+ color: green;
+}
+
+.game-card .answer.wrong input {
+ color: red;
+}
\ No newline at end of file