diff --git a/README.md b/README.md
index 98ae0b4..6e8a5be 100644
--- a/README.md
+++ b/README.md
@@ -117,3 +117,18 @@ Quando ativo, após ações de cadastro/edição, exclusão, importação e expe
- totais expedidos por SKU.
> Importante: por segurança do navegador, não é possível editar automaticamente o mesmo arquivo Excel já aberto no seu computador. O que o sistema faz é gerar uma nova versão atualizada da planilha.
+
+## Demo de jogo mobile de resgate
+
+Adicionado um protótipo jogável em:
+
+- `resgate-game.html`
+
+Tema da demo: salvar personagens em várias fases (princesa, rei, camponesa + criança etc.), com controle por botões touch e setas do teclado.
+
+Para rodar:
+
+```bash
+python3 -m http.server 4173
+# abrir http://localhost:4173/resgate-game.html
+```
diff --git a/resgate-game.css b/resgate-game.css
new file mode 100644
index 0000000..fe93262
--- /dev/null
+++ b/resgate-game.css
@@ -0,0 +1,101 @@
+:root {
+ --bg: #101728;
+ --card: #172239;
+ --text: #f4f7ff;
+ --accent: #ffca3a;
+ --danger: #ff595e;
+ --ok: #34d399;
+}
+
+* { box-sizing: border-box; }
+
+body {
+ margin: 0;
+ font-family: Inter, system-ui, sans-serif;
+ background: radial-gradient(circle at top, #1f2f52, var(--bg));
+ color: var(--text);
+}
+
+.game-shell {
+ min-height: 100vh;
+ max-width: 540px;
+ margin: 0 auto;
+ padding: 1rem;
+ display: grid;
+ gap: 0.9rem;
+}
+
+.hud {
+ background: var(--card);
+ border-radius: 14px;
+ padding: 0.8rem;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ text-align: center;
+}
+
+.label {
+ margin: 0;
+ font-size: 0.75rem;
+ opacity: 0.8;
+}
+
+h1, h2 { margin: 0.2rem 0 0; font-size: 1.2rem; }
+
+.arena-wrap {
+ background: rgba(255, 255, 255, 0.06);
+ border-radius: 14px;
+ padding: 0.7rem;
+}
+
+.arena {
+ position: relative;
+ width: 100%;
+ aspect-ratio: 1/1;
+ background: #0b1220;
+ border-radius: 10px;
+ border: 2px solid rgba(255,255,255,0.15);
+ overflow: hidden;
+}
+
+.hero, .npc, .enemy {
+ position: absolute;
+ width: 24px;
+ height: 24px;
+ border-radius: 6px;
+}
+
+.hero { background: var(--accent); box-shadow: 0 0 14px rgba(255,202,58,.6); }
+.npc { background: var(--ok); }
+.enemy { background: var(--danger); }
+
+.controls {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 0.5rem;
+}
+
+button {
+ border: 0;
+ border-radius: 10px;
+ padding: 0.75rem;
+ font-size: 1rem;
+ font-weight: 700;
+ background: #2a3f67;
+ color: white;
+}
+
+.actions {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 0.5rem;
+}
+
+#nextPhaseBtn { background: #3c7d3f; }
+.secondary { background: #5f6678; }
+
+.message {
+ min-height: 1.8rem;
+ text-align: center;
+ margin: 0;
+}
diff --git a/resgate-game.html b/resgate-game.html
new file mode 100644
index 0000000..f498bff
--- /dev/null
+++ b/resgate-game.html
@@ -0,0 +1,49 @@
+
+
+
+
+
+ Resgate Real - Demo Mobile
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Chegue no refém evitando os inimigos.
+
+
+
+
+
diff --git a/resgate-game.js b/resgate-game.js
new file mode 100644
index 0000000..e54fd39
--- /dev/null
+++ b/resgate-game.js
@@ -0,0 +1,166 @@
+const arena = document.getElementById('arena');
+const heroEl = document.getElementById('hero');
+const phaseTitle = document.getElementById('phaseTitle');
+const livesEl = document.getElementById('lives');
+const goalEl = document.getElementById('goal');
+const messageEl = document.getElementById('message');
+
+const PHASES = [
+ { target: 'Princesa', enemies: 2 },
+ { target: 'Rei', enemies: 3 },
+ { target: 'Camponesa + Criança', enemies: 4 },
+ { target: 'Rainha', enemies: 5 },
+ { target: 'Vilarejo', enemies: 6 }
+];
+
+const state = {
+ phase: 0,
+ lives: 3,
+ hero: { x: 16, y: 16 },
+ npc: null,
+ enemies: [],
+ enemyEls: []
+};
+
+function clamp(value, min, max) {
+ return Math.min(max, Math.max(min, value));
+}
+
+function place(el, x, y) {
+ el.style.left = `${x}px`;
+ el.style.top = `${y}px`;
+}
+
+function makeEnemy() {
+ const el = document.createElement('div');
+ el.className = 'enemy';
+ arena.appendChild(el);
+ return {
+ el,
+ x: Math.random() * 280 + 20,
+ y: Math.random() * 280 + 20,
+ vx: Math.random() > 0.5 ? 2 : -2,
+ vy: Math.random() > 0.5 ? 2 : -2
+ };
+}
+
+function resetPhase() {
+ state.enemyEls.forEach((el) => el.remove());
+ state.enemyEls = [];
+ state.enemies = [];
+
+ const phase = PHASES[state.phase];
+ phaseTitle.textContent = String(state.phase + 1);
+ goalEl.textContent = phase.target;
+ livesEl.textContent = String(state.lives);
+ state.hero = { x: 16, y: 16 };
+
+ document.querySelector('.npc')?.remove();
+ const npcEl = document.createElement('div');
+ npcEl.className = 'npc';
+ arena.appendChild(npcEl);
+ state.npc = {
+ el: npcEl,
+ x: Math.random() * 260 + 40,
+ y: Math.random() * 260 + 40
+ };
+
+ for (let i = 0; i < phase.enemies; i += 1) {
+ const enemy = makeEnemy();
+ state.enemies.push(enemy);
+ state.enemyEls.push(enemy.el);
+ }
+
+ messageEl.textContent = `Resgate: ${phase.target}`;
+ render();
+}
+
+function distance(a, b) {
+ return Math.hypot(a.x - b.x, a.y - b.y);
+}
+
+function moveHero(dx, dy) {
+ const max = arena.clientWidth - 24;
+ state.hero.x = clamp(state.hero.x + dx, 0, max);
+ state.hero.y = clamp(state.hero.y + dy, 0, max);
+ render();
+ checkCollisions();
+}
+
+function checkCollisions() {
+ if (distance(state.hero, state.npc) < 20) {
+ messageEl.textContent = `Você salvou ${PHASES[state.phase].target}!`;
+ return;
+ }
+
+ const hit = state.enemies.some((enemy) => distance(state.hero, enemy) < 18);
+ if (hit) {
+ state.lives -= 1;
+ livesEl.textContent = String(state.lives);
+ if (state.lives <= 0) {
+ messageEl.textContent = 'Game over! Reinicie para tentar novamente.';
+ state.lives = 0;
+ return;
+ }
+ state.hero = { x: 16, y: 16 };
+ messageEl.textContent = 'Você foi atingido! Volte ao início da fase.';
+ render();
+ }
+}
+
+function animateEnemies() {
+ const max = arena.clientWidth - 24;
+ state.enemies.forEach((enemy) => {
+ enemy.x += enemy.vx;
+ enemy.y += enemy.vy;
+ if (enemy.x <= 0 || enemy.x >= max) enemy.vx *= -1;
+ if (enemy.y <= 0 || enemy.y >= max) enemy.vy *= -1;
+ });
+ render();
+ checkCollisions();
+ requestAnimationFrame(animateEnemies);
+}
+
+function render() {
+ place(heroEl, state.hero.x, state.hero.y);
+ place(state.npc.el, state.npc.x, state.npc.y);
+ state.enemies.forEach((enemy) => place(enemy.el, enemy.x, enemy.y));
+}
+
+function nextPhase() {
+ if (state.phase === PHASES.length - 1) {
+ messageEl.textContent = 'Parabéns! Você concluiu todas as fases desta demo.';
+ return;
+ }
+ state.phase += 1;
+ resetPhase();
+}
+
+document.querySelectorAll('[data-dir]').forEach((btn) => {
+ btn.addEventListener('click', () => {
+ if (state.lives <= 0) return;
+ const dir = btn.dataset.dir;
+ if (dir === 'left') moveHero(-18, 0);
+ if (dir === 'right') moveHero(18, 0);
+ if (dir === 'up') moveHero(0, -18);
+ if (dir === 'down') moveHero(0, 18);
+ });
+});
+
+document.getElementById('nextPhaseBtn').addEventListener('click', nextPhase);
+document.getElementById('restartBtn').addEventListener('click', () => {
+ state.phase = 0;
+ state.lives = 3;
+ resetPhase();
+});
+
+window.addEventListener('keydown', (event) => {
+ if (state.lives <= 0) return;
+ if (event.key === 'ArrowLeft') moveHero(-16, 0);
+ if (event.key === 'ArrowRight') moveHero(16, 0);
+ if (event.key === 'ArrowUp') moveHero(0, -16);
+ if (event.key === 'ArrowDown') moveHero(0, 16);
+});
+
+resetPhase();
+requestAnimationFrame(animateEnemies);