@the-inclusionist/engine 6.36.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +107 -0
  3. package/app/css/style.css +622 -0
  4. package/app/public/vendor/fonts/andika-400-ext.woff2 +0 -0
  5. package/app/public/vendor/fonts/andika-400.woff2 +0 -0
  6. package/app/public/vendor/fonts/andika-700-ext.woff2 +0 -0
  7. package/app/public/vendor/fonts/andika-700.woff2 +0 -0
  8. package/app/public/vendor/fonts/atkinson-400-ext.woff2 +0 -0
  9. package/app/public/vendor/fonts/atkinson-400.woff2 +0 -0
  10. package/app/public/vendor/fonts/atkinson-700-ext.woff2 +0 -0
  11. package/app/public/vendor/fonts/atkinson-700.woff2 +0 -0
  12. package/app/public/vendor/fonts/atkinson-mono-400-ext.woff2 +0 -0
  13. package/app/public/vendor/fonts/atkinson-mono-400.woff2 +0 -0
  14. package/app/public/vendor/fonts/comicneue-400.woff2 +0 -0
  15. package/app/public/vendor/fonts/comicneue-700.woff2 +0 -0
  16. package/app/public/vendor/fonts/greatvibes-400.woff2 +0 -0
  17. package/app/public/vendor/fonts/inter-400.woff2 +0 -0
  18. package/app/public/vendor/fonts/inter-700.woff2 +0 -0
  19. package/app/public/vendor/fonts/lato-400.woff2 +0 -0
  20. package/app/public/vendor/fonts/lato-700.woff2 +0 -0
  21. package/app/public/vendor/fonts/lexend-400-ext.woff2 +0 -0
  22. package/app/public/vendor/fonts/lexend-400.woff2 +0 -0
  23. package/app/public/vendor/fonts/lexend-700-ext.woff2 +0 -0
  24. package/app/public/vendor/fonts/lexend-700.woff2 +0 -0
  25. package/app/public/vendor/fonts/literata-400.woff2 +0 -0
  26. package/app/public/vendor/fonts/literata-700.woff2 +0 -0
  27. package/app/public/vendor/fonts/newsreader-400.woff2 +0 -0
  28. package/app/public/vendor/fonts/newsreader-700.woff2 +0 -0
  29. package/app/public/vendor/fonts/opensans-400.woff2 +0 -0
  30. package/app/public/vendor/fonts/opensans-700.woff2 +0 -0
  31. package/app/public/vendor/fonts/pinyon-400.woff2 +0 -0
  32. package/app/public/vendor/fonts/quattro-400.woff2 +0 -0
  33. package/app/public/vendor/fonts/quattro-700.woff2 +0 -0
  34. package/app/public/vendor/fonts/sourcesans-400.woff2 +0 -0
  35. package/app/public/vendor/fonts/sourcesans-700.woff2 +0 -0
  36. package/app/public/vendor/fonts/sourceserif-400.woff2 +0 -0
  37. package/app/public/vendor/fonts/sourceserif-700.woff2 +0 -0
  38. package/app/public/vendor/fonts/ufcook-700.woff2 +0 -0
  39. package/app/public/vendor/fonts/ufmag-400.woff2 +0 -0
  40. package/app/public/vendor/fonts.css +88 -0
  41. package/dist-pkg/boot/create-game.d.ts +159 -0
  42. package/dist-pkg/boot/create-game.js +266 -0
  43. package/dist-pkg/core/a11y-sr.d.ts +4 -0
  44. package/dist-pkg/core/a11y-sr.js +20 -0
  45. package/dist-pkg/core/actions.d.ts +100 -0
  46. package/dist-pkg/core/actions.js +149 -0
  47. package/dist-pkg/core/anel.d.ts +17 -0
  48. package/dist-pkg/core/anel.js +31 -0
  49. package/dist-pkg/core/collision.d.ts +21 -0
  50. package/dist-pkg/core/collision.js +62 -0
  51. package/dist-pkg/core/constants.d.ts +84 -0
  52. package/dist-pkg/core/constants.js +64 -0
  53. package/dist-pkg/core/contract.d.ts +227 -0
  54. package/dist-pkg/core/contract.js +184 -0
  55. package/dist-pkg/core/dom-query.d.ts +9 -0
  56. package/dist-pkg/core/dom-query.js +25 -0
  57. package/dist-pkg/core/entity.d.ts +150 -0
  58. package/dist-pkg/core/entity.js +38 -0
  59. package/dist-pkg/core/escape-html.d.ts +16 -0
  60. package/dist-pkg/core/escape-html.js +32 -0
  61. package/dist-pkg/core/i18n.d.ts +71 -0
  62. package/dist-pkg/core/i18n.js +236 -0
  63. package/dist-pkg/core/layers.d.ts +39 -0
  64. package/dist-pkg/core/layers.js +71 -0
  65. package/dist-pkg/core/letter-grid.d.ts +55 -0
  66. package/dist-pkg/core/letter-grid.js +116 -0
  67. package/dist-pkg/core/loop.d.ts +17 -0
  68. package/dist-pkg/core/loop.js +36 -0
  69. package/dist-pkg/core/password.d.ts +34 -0
  70. package/dist-pkg/core/password.js +205 -0
  71. package/dist-pkg/core/rng.d.ts +20 -0
  72. package/dist-pkg/core/rng.js +61 -0
  73. package/dist-pkg/core/rotulo-acessivel.d.ts +17 -0
  74. package/dist-pkg/core/rotulo-acessivel.js +41 -0
  75. package/dist-pkg/core/run-state.d.ts +115 -0
  76. package/dist-pkg/core/run-state.js +40 -0
  77. package/dist-pkg/core/scenes.d.ts +68 -0
  78. package/dist-pkg/core/scenes.js +64 -0
  79. package/dist-pkg/core/screens.d.ts +16 -0
  80. package/dist-pkg/core/screens.js +28 -0
  81. package/dist-pkg/core/state.d.ts +138 -0
  82. package/dist-pkg/core/state.js +290 -0
  83. package/dist-pkg/core/tiles.d.ts +12 -0
  84. package/dist-pkg/core/tiles.js +61 -0
  85. package/dist-pkg/core/world.d.ts +4 -0
  86. package/dist-pkg/core/world.js +58 -0
  87. package/dist-pkg/educational/activities-registry.d.ts +47 -0
  88. package/dist-pkg/educational/activities-registry.js +95 -0
  89. package/dist-pkg/i18n/en.d.ts +3 -0
  90. package/dist-pkg/i18n/en.js +560 -0
  91. package/dist-pkg/i18n/es.d.ts +3 -0
  92. package/dist-pkg/i18n/es.js +558 -0
  93. package/dist-pkg/i18n/pt.d.ts +3 -0
  94. package/dist-pkg/i18n/pt.js +618 -0
  95. package/dist-pkg/input/default-bindings.d.ts +24 -0
  96. package/dist-pkg/input/default-bindings.js +122 -0
  97. package/dist-pkg/input/devices.d.ts +15 -0
  98. package/dist-pkg/input/devices.js +31 -0
  99. package/dist-pkg/input/edges.d.ts +58 -0
  100. package/dist-pkg/input/edges.js +49 -0
  101. package/dist-pkg/input/gamepad.d.ts +203 -0
  102. package/dist-pkg/input/gamepad.js +531 -0
  103. package/dist-pkg/input/keyboard-runtime.d.ts +61 -0
  104. package/dist-pkg/input/keyboard-runtime.js +83 -0
  105. package/dist-pkg/input/keyboard.d.ts +45 -0
  106. package/dist-pkg/input/keyboard.js +80 -0
  107. package/dist-pkg/input/keydown.d.ts +270 -0
  108. package/dist-pkg/input/keydown.js +381 -0
  109. package/dist-pkg/input/latch.d.ts +30 -0
  110. package/dist-pkg/input/latch.js +63 -0
  111. package/dist-pkg/input/pointer-space.d.ts +40 -0
  112. package/dist-pkg/input/pointer-space.js +51 -0
  113. package/dist-pkg/input/state.d.ts +11 -0
  114. package/dist-pkg/input/state.js +12 -0
  115. package/dist-pkg/input/touch-bindings.d.ts +210 -0
  116. package/dist-pkg/input/touch-bindings.js +365 -0
  117. package/dist-pkg/input/touch.d.ts +173 -0
  118. package/dist-pkg/input/touch.js +314 -0
  119. package/dist-pkg/input/transports.d.ts +90 -0
  120. package/dist-pkg/input/transports.js +96 -0
  121. package/dist-pkg/input/vocabulary-migration.d.ts +58 -0
  122. package/dist-pkg/input/vocabulary-migration.js +125 -0
  123. package/dist-pkg/platform/audio-ambient.d.ts +27 -0
  124. package/dist-pkg/platform/audio-ambient.js +89 -0
  125. package/dist-pkg/platform/audio-earcons.d.ts +24 -0
  126. package/dist-pkg/platform/audio-earcons.js +62 -0
  127. package/dist-pkg/platform/audio-jingles.d.ts +17 -0
  128. package/dist-pkg/platform/audio-jingles.js +54 -0
  129. package/dist-pkg/platform/audio-mixer.d.ts +14 -0
  130. package/dist-pkg/platform/audio-mixer.js +62 -0
  131. package/dist-pkg/platform/audio-nav.d.ts +51 -0
  132. package/dist-pkg/platform/audio-nav.js +99 -0
  133. package/dist-pkg/platform/audio-sonar.d.ts +71 -0
  134. package/dist-pkg/platform/audio-sonar.js +173 -0
  135. package/dist-pkg/platform/audio.d.ts +32 -0
  136. package/dist-pkg/platform/audio.js +191 -0
  137. package/dist-pkg/platform/interruptible-speech.d.ts +19 -0
  138. package/dist-pkg/platform/interruptible-speech.js +79 -0
  139. package/dist-pkg/platform/speech.d.ts +2 -0
  140. package/dist-pkg/platform/speech.js +35 -0
  141. package/dist-pkg/platform/storage.d.ts +87 -0
  142. package/dist-pkg/platform/storage.js +148 -0
  143. package/dist-pkg/platform/tts.d.ts +33 -0
  144. package/dist-pkg/platform/tts.js +142 -0
  145. package/dist-pkg/render/camera.d.ts +58 -0
  146. package/dist-pkg/render/camera.js +105 -0
  147. package/dist-pkg/render/canvas.d.ts +13 -0
  148. package/dist-pkg/render/canvas.js +26 -0
  149. package/dist-pkg/render/cenario-data.d.ts +127 -0
  150. package/dist-pkg/render/cenario-data.js +145 -0
  151. package/dist-pkg/render/city-tex.d.ts +63 -0
  152. package/dist-pkg/render/city-tex.js +228 -0
  153. package/dist-pkg/render/city-tiles.d.ts +11 -0
  154. package/dist-pkg/render/city-tiles.js +140 -0
  155. package/dist-pkg/render/crt.d.ts +19 -0
  156. package/dist-pkg/render/crt.js +92 -0
  157. package/dist-pkg/render/cvd-matrices.d.ts +35 -0
  158. package/dist-pkg/render/cvd-matrices.js +91 -0
  159. package/dist-pkg/render/draw.d.ts +149 -0
  160. package/dist-pkg/render/draw.js +221 -0
  161. package/dist-pkg/render/fx.d.ts +63 -0
  162. package/dist-pkg/render/fx.js +123 -0
  163. package/dist-pkg/render/hc-role-data.d.ts +14 -0
  164. package/dist-pkg/render/hc-role-data.js +24 -0
  165. package/dist-pkg/render/high-contrast.d.ts +109 -0
  166. package/dist-pkg/render/high-contrast.js +254 -0
  167. package/dist-pkg/render/lq-filter.d.ts +37 -0
  168. package/dist-pkg/render/lq-filter.js +94 -0
  169. package/dist-pkg/render/minimap.d.ts +11 -0
  170. package/dist-pkg/render/minimap.js +94 -0
  171. package/dist-pkg/render/parallax.d.ts +85 -0
  172. package/dist-pkg/render/parallax.js +158 -0
  173. package/dist-pkg/render/player-anim.d.ts +71 -0
  174. package/dist-pkg/render/player-anim.js +152 -0
  175. package/dist-pkg/render/port.d.ts +143 -0
  176. package/dist-pkg/render/port.js +29 -0
  177. package/dist-pkg/render/recycling-tex.d.ts +48 -0
  178. package/dist-pkg/render/recycling-tex.js +164 -0
  179. package/dist-pkg/render/scene-city.d.ts +77 -0
  180. package/dist-pkg/render/scene-city.js +181 -0
  181. package/dist-pkg/render/scene-parallax.d.ts +72 -0
  182. package/dist-pkg/render/scene-parallax.js +426 -0
  183. package/dist-pkg/render/scene-sky.d.ts +126 -0
  184. package/dist-pkg/render/scene-sky.js +295 -0
  185. package/dist-pkg/render/screen-pipeline.d.ts +134 -0
  186. package/dist-pkg/render/screen-pipeline.js +177 -0
  187. package/dist-pkg/render/set-cenario.d.ts +54 -0
  188. package/dist-pkg/render/set-cenario.js +103 -0
  189. package/dist-pkg/render/sprite-fx.d.ts +3 -0
  190. package/dist-pkg/render/sprite-fx.js +40 -0
  191. package/dist-pkg/render/textures.d.ts +75 -0
  192. package/dist-pkg/render/textures.js +240 -0
  193. package/dist-pkg/render/title-scene.d.ts +46 -0
  194. package/dist-pkg/render/title-scene.js +75 -0
  195. package/dist-pkg/render/viewports.d.ts +52 -0
  196. package/dist-pkg/render/viewports.js +185 -0
  197. package/dist-pkg/render/viz-axes.d.ts +98 -0
  198. package/dist-pkg/render/viz-axes.js +184 -0
  199. package/dist-pkg/render/viz-modes.d.ts +61 -0
  200. package/dist-pkg/render/viz-modes.js +74 -0
  201. package/dist-pkg/render/viz-setters.d.ts +130 -0
  202. package/dist-pkg/render/viz-setters.js +244 -0
  203. package/dist-pkg/render/weather.d.ts +96 -0
  204. package/dist-pkg/render/weather.js +171 -0
  205. package/dist-pkg/render/wheelchair-sprites.d.ts +24 -0
  206. package/dist-pkg/render/wheelchair-sprites.js +63 -0
  207. package/dist-pkg/render/world-tex.d.ts +18 -0
  208. package/dist-pkg/render/world-tex.js +87 -0
  209. package/dist-pkg/ui/activities-menu.d.ts +258 -0
  210. package/dist-pkg/ui/activities-menu.js +648 -0
  211. package/dist-pkg/ui/caa-sets.d.ts +39 -0
  212. package/dist-pkg/ui/caa-sets.js +65 -0
  213. package/dist-pkg/ui/changed-mark.d.ts +15 -0
  214. package/dist-pkg/ui/changed-mark.js +96 -0
  215. package/dist-pkg/ui/debug-panel.d.ts +67 -0
  216. package/dist-pkg/ui/debug-panel.js +173 -0
  217. package/dist-pkg/ui/dom.d.ts +24 -0
  218. package/dist-pkg/ui/dom.js +47 -0
  219. package/dist-pkg/ui/focus-trap.d.ts +71 -0
  220. package/dist-pkg/ui/focus-trap.js +99 -0
  221. package/dist-pkg/ui/fonts.d.ts +41 -0
  222. package/dist-pkg/ui/fonts.js +59 -0
  223. package/dist-pkg/ui/hud.d.ts +137 -0
  224. package/dist-pkg/ui/hud.js +229 -0
  225. package/dist-pkg/ui/item-announcement.d.ts +19 -0
  226. package/dist-pkg/ui/item-announcement.js +36 -0
  227. package/dist-pkg/ui/layout.d.ts +6 -0
  228. package/dist-pkg/ui/layout.js +54 -0
  229. package/dist-pkg/ui/loop-crash.d.ts +19 -0
  230. package/dist-pkg/ui/loop-crash.js +60 -0
  231. package/dist-pkg/ui/map-hub.d.ts +56 -0
  232. package/dist-pkg/ui/map-hub.js +138 -0
  233. package/dist-pkg/ui/menu-nav.d.ts +141 -0
  234. package/dist-pkg/ui/menu-nav.js +390 -0
  235. package/dist-pkg/ui/pause-icons.d.ts +327 -0
  236. package/dist-pkg/ui/pause-icons.js +620 -0
  237. package/dist-pkg/ui/reach-notice.d.ts +34 -0
  238. package/dist-pkg/ui/reach-notice.js +71 -0
  239. package/dist-pkg/ui/settings-audio.d.ts +139 -0
  240. package/dist-pkg/ui/settings-audio.js +544 -0
  241. package/dist-pkg/ui/settings-caa.d.ts +45 -0
  242. package/dist-pkg/ui/settings-caa.js +137 -0
  243. package/dist-pkg/ui/settings-controls.d.ts +100 -0
  244. package/dist-pkg/ui/settings-controls.js +152 -0
  245. package/dist-pkg/ui/settings-empathy.d.ts +62 -0
  246. package/dist-pkg/ui/settings-empathy.js +137 -0
  247. package/dist-pkg/ui/settings-motion.d.ts +95 -0
  248. package/dist-pkg/ui/settings-motion.js +282 -0
  249. package/dist-pkg/ui/settings-motor.d.ts +77 -0
  250. package/dist-pkg/ui/settings-motor.js +215 -0
  251. package/dist-pkg/ui/settings-panel.d.ts +76 -0
  252. package/dist-pkg/ui/settings-panel.js +187 -0
  253. package/dist-pkg/ui/settings-typo.d.ts +74 -0
  254. package/dist-pkg/ui/settings-typo.js +150 -0
  255. package/dist-pkg/ui/settings-visual.d.ts +111 -0
  256. package/dist-pkg/ui/settings-visual.js +252 -0
  257. package/dist-pkg/ui/shell.d.ts +239 -0
  258. package/dist-pkg/ui/shell.js +430 -0
  259. package/dist-pkg/ui/title.d.ts +36 -0
  260. package/dist-pkg/ui/title.js +60 -0
  261. package/dist-pkg/ui/vlibras.d.ts +11 -0
  262. package/dist-pkg/ui/vlibras.js +87 -0
  263. package/dist-pkg/ui/webcam.d.ts +7 -0
  264. package/dist-pkg/ui/webcam.js +88 -0
  265. package/docs/CREDITS.md +64 -0
  266. package/docs/LICENSES.md +132 -0
  267. package/package.json +115 -0
@@ -0,0 +1,622 @@
1
+ /* SPDX-License-Identifier: AGPL-3.0-or-later */
2
+ :root{
3
+ --bg:#0b1020; --panel:#1b2440; --ink:#f5f8ff; --ink-soft:#cdd6f2;
4
+ --accent:#ffd23f; --accent-ink:#1a1400; --focus:#ffd23f; --good:#34e29b;
5
+ --changed:#fff; /* ADR-0029: moldura de quem saiu do padrão */
6
+ /* Alto contraste no DOM (issue #83): o véu do painel sem transparência, e o fundo de botão. Ficam aqui,
7
+ junto dos outros tokens, para o teste de contraste poder lê-los do CSS em vez de os copiar. */
8
+ --bg-solid:#04070f; --panel-btn:#1a2740;
9
+ --maxw:1000px; --tap:44px;
10
+ --ui-fs:16px; /* fonte-base da UI (<p>): 16px = MÍNIMO a 640×360; o JS (layout) aumenta conforme o canvas cresce */
11
+ /* ===== Tipografia oficial EdSP ===== */
12
+ /* 'Noto Color Emoji' no fim do stack → emoji consistentes (nativo no Chromebook/Android; woff2 abaixo p/ Windows) */
13
+ --font-fallback:system-ui,"Segoe UI",Roboto,Arial,sans-serif,'Noto Color Emoji';
14
+ --font:'Atkinson Hyperlegible',var(--font-fallback); /* PADRÃO */
15
+ --font-math:'Atkinson Hyperlegible Mono',ui-monospace,"Cascadia Mono",Consolas,monospace; /* matemática */
16
+ /* Espaçamento padrão do projeto (≥ recomendações WCAG 2.2 §1.4.12) */
17
+ --ls:0.12em; /* letter-spacing */
18
+ --ws:0.16em; /* word-spacing */
19
+ --lh:1.5; /* line-height */
20
+
21
+ /* ===== ORDEM-Z CANÔNICA — a faixa de OVERLAY do `core/layers.ts` (ADR-0020) =====
22
+ Estes valores são a MESMA fonte que o mundo usa em PIXI, trazida para o CSS. Eles existem antes do
23
+ primeiro consumidor DE PROPÓSITO, e o motivo é o mesmo que a Fase C deu ao barramento tipado: o contrato
24
+ precisa nascer certo em vez de ser retipado depois. `tests/z-order-css.node.test.js` compara este bloco,
25
+ valor a valor, com o `Z` do TypeScript — divergir fica vermelho.
26
+
27
+ ⚠️ NENHUM SELETOR AINDA OS USA, e isso NÃO é esquecimento. Adotá-los inverte três ordens que hoje valem,
28
+ e inverter ordem é decisão de produto:
29
+
30
+ · `.caption` (8) está ABAIXO de `#touch-controls` (14). Canonicamente CAPTIONS(26000) fica ACIMA de
31
+ TOUCH_CONTROLS(25000). A inversão CONSERTA um defeito — legenda é recurso de surdez, e controle de
32
+ toque cobrindo legenda é a coisa errada —, mas muda a tela.
33
+ · `.quiz` (12) está ABAIXO de `#touch-controls` (14); canonicamente DIALOGUE(27000) fica acima.
34
+ · `.skip-link` (100) está ACIMA dos modais (60). O comentário do `Z` põe o skip-link em CAPTIONS
35
+ (26000), o que o deixaria ABAIXO de MENU(30000) — e um "pular para o conteúdo" embaixo de um modal
36
+ não é alcançável. Aqui o comentário do `Z` é que está errado, não o CSS.
37
+
38
+ E DUAS COISAS NÃO TÊM SLOT: os dois pseudo-elementos do CRT (`crt-vig-1::before` em 5 e
39
+ `crt-scan-1::after` em 500). Pelo ADR-0020 o CRT é PÓS-PROCESSO, não camada — inventar um número para
40
+ ele repetiria o erro de categoria que a emenda de 2026-08-26 já registrou no `app.stage`.
41
+
42
+ ⚠️ E `z-index` de CSS NÃO É PLANO. `#game-hud` tem `z-index:4` e CRIA CONTEXTO DE EMPILHAMENTO, então o
43
+ `.screen-pause` (6) compete só lá dentro — o menu de pausa vive efetivamente em "4" na raiz. Uma lista
44
+ plana de valores globais não expressa isso. Adotar exige decidir, por elemento, em que contexto ele vive. */
45
+ --z-hud:24000;
46
+ --z-touch-controls:25000;
47
+ --z-captions:26000;
48
+ --z-dialogue:27000;
49
+ --z-game-msg:28000;
50
+ --z-modal-scrim:29000;
51
+ --z-menu:30000;
52
+ --z-menu-max:39999;
53
+ --z-transition:40000;
54
+ --z-debug:90000;
55
+ }
56
+ /* Emoji: usa o Noto Color Emoji do sistema (Chromebook/Android/Linux) e, se ausente, o woff2 embutido
57
+ (offline). Se o arquivo não estiver presente, o navegador cai no emoji do SO — sem quebrar. */
58
+ @font-face{ font-family:'Noto Color Emoji'; src:local('Noto Color Emoji'); /* só a fonte do sistema; sem download (o .woff2 nunca existiu) */ font-display:swap; }
59
+ /* Modo ALFABETIZAÇÃO → Andika */
60
+ :root[data-fonte="alfabetizacao"]{ --font:'Andika',var(--font-fallback); }
61
+ /* Modo DISLEXIA/TDAH → Lexend + espaçamento da British Dyslexia Association
62
+ (palavra ≥ 3,5× letra: letter 0.18em / word 0.63em) */
63
+ :root[data-fonte="dislexia"]{ --font:'Lexend',var(--font-fallback); --ls:0.18em; --ws:0.63em; }
64
+ /* Tipografia: qualquer fonte do catálogo (menu próprio na pausa); --font-custom traz a pilha com fallback */
65
+ :root[data-fonte="custom"]{ --font:var(--font-custom,'Atkinson Hyperlegible'),var(--font-fallback); }
66
+ /* Menu Tipografia: caixa branca de pré-visualização (pangrama), escrita escura */
67
+ .typo-preview{background:#fff;color:#14141f;border:2px solid var(--ink-soft);border-radius:8px;padding:.65rem .9rem;margin:.2rem 0 .6rem;font-size:1.3em;line-height:1.3;text-align:center}
68
+ /* Tela de título (visual da v3): overlay TRANSPARENTE — a cena (céu/nuvens/grama) vem do canvas */
69
+ #title-overlay{background:transparent;font-size:var(--ui-fs,16px)} /* UI do splash escala com o k inteiro do canvas (igual pausa/diálogos) — NÃO fica fixa em 16px */
70
+ body.at-title #game-hud,body.at-title .start-tips,body.at-title .hud{display:none} /* HUD do jogo não vaza no menu inicial (display: filhos não conseguem re-exibir) */
71
+ .title-wrap{display:flex;flex-direction:column;align-items:center;gap:12px;max-height:96%;}
72
+ .game-title{color:#fff;font-size:2em;line-height:1.2;margin:0;text-align:center;letter-spacing:1px;text-shadow:2px 2px 0 #0d0d1a,-1px -1px 0 #0d0d1a}
73
+ .title-block{display:inline-block}
74
+ .game-byline{color:#f6f5f0;font-size:.8em;text-align:right;margin-top:2px;text-shadow:1px 1px 0 #0d0d1a}
75
+ .game-version{color:#9999b3;font-size:.6em;text-align:right;letter-spacing:1px;margin-top:2px;text-shadow:1px 1px 0 #0d0d1a}
76
+ .game-subtitle{color:#f6f5f0;font-size:.8em;margin:0 0 4px;text-shadow:1px 1px 0 #0d0d1a;text-align:center}
77
+ /* Splash DENTRO do canvas, SEM rolagem. GESTALT (José): a11y no CABEÇALHO · menu no ESPAÇO DE TRABALHO · legenda no RODAPÉ */
78
+ .title-wrap{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;max-height:none}
79
+ .title-legend{position:absolute;left:0;right:0;bottom:8px} /* RODAPÉ, como a legenda da pausa */
80
+ .title-menu{display:flex;flex-direction:column;gap:.35em;align-items:stretch;padding:2px;width:13em;margin:0 auto} /* largura fixa ENXUTA */
81
+ .title-menu[hidden]{display:none}
82
+ /* O menu principal com o DOBRO da largura base (13em → 26em): a 13em os rótulos quebravam em DUAS linhas, e
83
+ um botão de duas linhas muda de altura conforme o idioma — a mesma tela com uma geometria em pt-BR e outra
84
+ em espanhol. Quem se orienta por alinhamento perde a referência; quem escuta nem percebe que ela existia. */
85
+ #tm-main{width:26em}
86
+ /* ===================== SUBMENUS DA ABERTURA: LISTA VERTICAL COM ROLAGEM (ADR-0044) =====================
87
+ Eram GRADES de duas colunas, e a seta anda em ordem de DOM: “para baixo” pulava para a coluna da direita,
88
+ atravessando a tela. O anel do item 1 já estava certo no código (`nextTitleIndex` dá a volta) — era o
89
+ LAYOUT que mentia sobre ele. Em lista, a ordem que se VÊ é a ordem em que se ANDA.
90
+
91
+ Onze frações não cabem no quadro, então a lista ROLA — foi o que o Dev pediu, e o preço é barato:
92
+ `focus()` traz o item para dentro da janela sozinho, de modo que quem anda por teclado ou controle nunca
93
+ perde o foco fora da vista.
94
+
95
+ O painel é OPACO pelo mesmo motivo do véu da pausa: por trás da abertura corre o modo de atração, e
96
+ texto sobre quadro variável tem a razão de contraste que o quadro permitir — média de sorte, não garantia. */
97
+ #tm-alf,#tm-mat,#tm-tab,#tm-fr,#tm-cen{width:26em;max-height:72%;overflow-y:auto;overflow-x:hidden;background:var(--bg-solid);border-radius:10px;padding:.5em .7em}
98
+ /* O título do submenu fica GRUDADO no topo: numa lista que rola, um cabeçalho que sobe leva embora a única
99
+ pista de “onde estou” que quem lê por resto de visão tem. */
100
+ #tm-alf .tm-title,#tm-mat .tm-title,#tm-tab .tm-title,#tm-fr .tm-title,#tm-cen .tm-title{position:sticky;top:-.5em;z-index:1;background:var(--bg-solid);padding:.35em 0 .25em;margin:0}
101
+ /* …e por isso os itens precisam de MARGEM DE ROLAGEM: um cabeçalho grudado cobre o item que o navegador acaba
102
+ de "trazer para a vista", porque para ele o item já estava dentro do quadro. MEDIDO no navegador antes de
103
+ existir esta linha: dando a volta do último para o primeiro, o primeiro botão parava com o topo em 143px e
104
+ o cabeçalho terminava em 155 — 12px encobertos, e a criança via um item cortado no lugar do foco. O valor
105
+ cobre a altura do cabeçalho (34px ≈ 2,1em) com folga. */
106
+ #tm-alf .title-btn,#tm-mat .title-btn,#tm-tab .title-btn,#tm-fr .title-btn,#tm-cen .title-btn{scroll-margin-top:2.4em;scroll-margin-bottom:.5em}
107
+ /* As notações de fração continuam LADO A LADO: são cinco alternadores de UM grupo, e não itens da lista —
108
+ virar coluna aqui alongaria a rolagem sem ganhar ordem nenhuma. */
109
+ #tm-fr .frac-nots{display:flex;flex-direction:column;gap:.25em}
110
+ /* AS CINCO NOTAÇÕES SÃO AJUSTE, NÃO ITEM DE MENU (pedido do Dev). Elas usavam `.title-btn` — a mesma classe
111
+ dos botões que ABREM uma atividade — e ficavam empilhadas logo acima deles: cinco coisas com cara de
112
+ "entrar" que na verdade ligam e desligam. A moldura (`fieldset`/`legend`) é a fronteira, e a marca ☑/☐ é o
113
+ estado sem depender de cor (a cor sozinha reprova a WCAG 1.4.1). */
114
+ #tm-fr .frac-nots{border:2px solid var(--ink-soft);border-radius:10px;padding:.3em .5em .5em;margin:0 0 .45em}
115
+ #tm-fr .frac-nots legend{padding:0 .4em;font-size:.8em;font-weight:700;color:var(--ink-soft)}
116
+ #tm-fr .fnot-opt{display:flex;align-items:center;gap:.5em;width:100%;min-height:44px;padding:.2em .4em;background:transparent;color:#fff;border:0;border-radius:8px;font:inherit;cursor:pointer;text-align:left}
117
+ #tm-fr .fnot-opt:hover,#tm-fr .fnot-opt:focus-visible{background:#16233f}
118
+ #tm-fr .fnot-marca{font-size:1.15em;line-height:1;min-width:1.1em}
119
+ #tm-fr .fnot-opt[aria-checked="true"] .fnot-marca{color:var(--accent)}
120
+ #tm-fr .fnot-sym{font-size:1em}
121
+ #tm-fr .fnot-opt .fv{font-size:.8em}
122
+ .tm-title{color:#fff;font-size:1em;margin:0 0 .1em;text-align:center;text-shadow:1px 1px 0 #0d0d1a} /* título do submenu */
123
+ .title-btn{background:var(--accent);color:var(--accent-ink);border:3px solid #0d0d1a;font-family:inherit;font-weight:bold;font-size:.95em;padding:.4em .6em;border-radius:6px;cursor:pointer;min-width:0;width:100%;min-height:2.3em} /* contorno PRETO grosso (3px) = mesma cor do texto (José) */
124
+ .title-btn.ghost{background:#e8862b;color:#0d0d1a;border:2px solid #0d0d1a} /* Voltar = preenchimento LARANJA */
125
+ .act-sub{display:block;font-size:.72em;font-weight:600;opacity:.7;letter-spacing:.02em;margin-top:.06em;font-family:var(--font-math,inherit);white-space:nowrap} /* subtítulo de exemplo (2ª linha, NUNCA quebra — botão largo o bastante) */
126
+ /* Seletor VERMELHO fixo (piscar removido a pedido do José). Especificidade via #title-overlay. */
127
+ #title-overlay .title-btn:focus-visible,#title-overlay .title-btn:focus{outline:4px solid #ff3b30;outline-offset:2px}
128
+ #title-overlay .title-btn.act-fx{animation:titleAct .22s ease} /* efeito de ATIVAÇÃO (antes de trocar de tela) */
129
+ @keyframes titleAct{0%{transform:scale(.92);filter:brightness(1.6)}60%{transform:scale(1.03)}100%{transform:scale(1)}}
130
+ @media (prefers-reduced-motion: reduce){ #title-overlay .title-btn.act-fx{animation:none;filter:brightness(1.4)} }
131
+ .title-btn.tab-on{box-shadow:inset 0 0 0 3px #14141f}
132
+ /* As fileiras da tabuada eram DUAS linhas de números lado a lado, e o Dev cobrou de novo depois de ver o
133
+ resultado: menu "misto" é menu onde "para baixo" às vezes anda para o lado. Viraram coluna, como todo o
134
+ resto da abertura. O seletor ganhou o `#tm-tab` na frente de propósito — é o que deixa o gate de layout
135
+ enxergar esta regra como sendo DE um submenu da abertura, em vez de uma classe solta que ninguém vigia. */
136
+ #tm-tab .tab-row{display:flex;flex-direction:column;gap:.35em}
137
+ #tm-tab .tab-row .title-btn{width:100%;padding:.4em .5em}
138
+ /* RODAPÉ (mesmo estilo do menu de pausa): descrição no fundo do canvas, NÃO colada ao grupo de botões (José).
139
+ Fundo fumê PRETO → contraste ≥7:1 entre o texto claro (#f6f5f0) e o fundo (≈#0a0a12). */
140
+ .tm-desc:not(:empty){position:absolute;left:50%;transform:translateX(-50%);bottom:10px;max-width:88%;color:#f6f5f0;font-size:.8em;text-align:center;background:rgba(8,8,16,.92);padding:.35em .7em;border-radius:8px;line-height:1.25}
141
+ .tm-desc:empty{position:absolute;left:0;bottom:10px}
142
+ /* Legenda de controles: mesmo visual/posição da legenda do menu de pausa (chips), em 2 LINHAS */
143
+ .title-legend{display:flex;flex-direction:column;gap:.25em;align-items:center;margin:.5em 0 .1em;font-size:.82em;color:#c3cfe0}
144
+ .title-legend .lg-row{display:flex;gap:1.1em;justify-content:center;flex-wrap:wrap}
145
+ .title-legend .lg{display:inline-flex;align-items:center;gap:.35em}
146
+ .title-legend .lg-ico{display:inline-flex;align-items:center;justify-content:center;min-width:1.35em;height:1.35em;padding:0 .2em;border-radius:.68em;font-weight:800;font-size:.9em;color:#fff;background:rgba(13,13,26,.85);text-shadow:0 1px 1px rgba(0,0,0,.55)}
147
+ #title-icons{position:absolute;top:10px;left:50%;transform:translateX(-50%);display:flex;gap:.3em;justify-content:center;flex-wrap:nowrap;white-space:nowrap} /* CABEÇALHO: 10px do topo, UMA fileira */
148
+ /* Seletor de jogadores = UM botão só ("◀ Nº de jogadores: X ▶"); o lado clicado decide +1/−1 */
149
+ #np-btn{letter-spacing:.02em}
150
+ #np-btn #np-n{display:inline-block;min-width:1.1em;text-align:center}
151
+ /* Fração VERTICAL: pilha número/traço/número em HTML puro (zero dependência) */
152
+ .fv{display:inline-flex;flex-direction:column;align-items:center;vertical-align:middle;line-height:1.05;margin:0 .1em}
153
+ .fv b{font-weight:800;padding:0 .25em}
154
+ .fv b:first-child{border-bottom:2px solid currentColor}
155
+ /* Fração como FIGURA (pizza/quadrado): SVG inline, 2-6 partes iguais; imprópria = várias unidades lado a lado */
156
+ :root{--frac-fill:#f2a03d} /* laranja: alto contraste sobre branco + contorno preto (não depende só da cor — WCAG 1.4.1) */
157
+ .frac-fig{display:inline-flex;align-items:center;gap:.25em;vertical-align:middle}
158
+ .frac-shape{display:inline-flex;align-items:center;gap:.15em} /* círculo + quadrado da mesma fração, lado a lado */
159
+ .frac-op{display:inline-flex;flex-direction:column;align-items:center;gap:.15em;vertical-align:middle} /* operando da conta: número EM CIMA, gráfico embaixo */
160
+ .frac-svg{width:2.4em;height:2.4em;display:inline-block;vertical-align:middle}
161
+ .quiz-choice .frac-svg{width:2.6em;height:2.6em}
162
+ .quiz-prob .frac-svg{width:2.6em;height:2.6em}
163
+ .quiz-dots{font-size:1.3em;letter-spacing:.15em;line-height:1.3;margin:.3rem 0;max-width:12em}
164
+ @media (prefers-contrast: more){
165
+ /* Aqui `--ink-soft` JÁ é branco: a moldura de "alterado" tem que deixar de ser branca, ou marcado e
166
+ não-marcado viram a mesma moldura — justamente para a criança cujos ajustes têm mais chance de estar
167
+ fora do padrão. Uma marca cuja visibilidade depende do tema não é uma marca (ADR-0029). */
168
+ :root{ --bg:#000; --panel:#000; --ink:#fff; --ink-soft:#fff; --accent:#ffe600; --changed:#ffe600; }
169
+ }
170
+ *{box-sizing:border-box}
171
+ html,body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--font);font-size:16px;line-height:var(--lh);letter-spacing:var(--ls);word-spacing:var(--ws)}
172
+ /* matemática em mono tabular (algarismos alinhados/legíveis) */
173
+ .quiz-box--math .quiz-prob,.quiz-box--math .quiz-choice,#hud-coins,#hud-total{font-family:var(--font-math);font-variant-numeric:tabular-nums}
174
+ /* espaçamento de parágrafo ≥2× só em blocos de leitura (não quebra HUD/menus) */
175
+ .overlay__card p + p{margin-top:2em}
176
+ /* A página NUNCA rola (José): altura travada no viewport e overflow escondido — o excedente do canvas
177
+ (tolerância de 5px lógicos/lado) é cortado pelo .stage-wrap, e os menus rolam DENTRO dos cards. */
178
+ body{display:flex;flex-direction:column;height:100vh;height:100dvh;overflow:hidden}
179
+ .sr-only{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
180
+ .skip-link{position:absolute;left:8px;top:-60px;background:var(--accent);color:var(--accent-ink);padding:.6em 1em;border-radius:0 0 10px 10px;font-weight:700;z-index:100;transition:top .15s}
181
+ .skip-link:focus{top:0}
182
+ :focus-visible{outline:4px solid var(--focus);outline-offset:2px;border-radius:6px}
183
+
184
+ .topbar{max-width:var(--maxw);margin:0 auto;padding:.6rem 1rem;display:flex;align-items:center;justify-content:space-between;gap:1rem;flex-wrap:wrap}
185
+ body:not(.dbg) .topbar{display:none} /* título da PÁGINA só em ?debug=true (o jogo já mostra no splash); recupera a vertical */
186
+ .title{font-size:1.3rem;margin:0}
187
+ .ver{color:var(--ink-soft);font-weight:400;font-size:.9rem}
188
+ .badge{margin:0;color:var(--accent-ink);background:var(--accent);border-radius:999px;padding:.2em .8em;font-weight:700;font-size:.8rem}
189
+
190
+ .hud{max-width:var(--maxw);margin:0 auto;padding:0 1rem .5rem;display:flex;gap:1.5rem;flex-wrap:wrap}
191
+ .hud__item{margin:0;font-size:1.05rem}
192
+ /* moedas/missão/poder migraram para dentro da tela de cada jogador; ficam sr-only (leitor de tela) */
193
+ .hud__item--sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
194
+ /* HUD por jogador SOBRE o canvas (DOM = alta definição, não pixela). Um por viewport; moedas na 1ª coluna, poder na 2ª. */
195
+ #game-hud{position:absolute;inset:0;pointer-events:none;z-index:4;font-family:var(--font);font-size:var(--hud-fs,14px);font-weight:800}
196
+ /* "Tela do jogador": um contêiner por jogador (posicionado na fatia do viewport dele). Hospeda HUD, e nas
197
+ próximas etapas a pausa e os menus daquele jogador. O render segue compartilhado (um contexto WebGL). */
198
+ .player-screen{position:absolute;pointer-events:none}
199
+ .vphud{position:absolute;top:0;left:0;width:100%;display:flex;align-items:flex-start;gap:0;padding:.4em .55em;box-sizing:border-box;color:#fff;text-shadow:0 1px 2px #000,0 0 3px #000,0 0 6px #000;line-height:1.15}
200
+ .vphud-obj,.vphud-power{flex:1 1 50%;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
201
+ .vphud-ico{font-weight:400}
202
+ /* jogador que saiu (MP): viewport preto com "Jogo abandonado" no centro; os outros seguem */
203
+ .vphud-quit{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:#000;color:#cdd6f2;font-family:var(--font);font-weight:800;font-size:var(--hud-fs,14px);text-align:center}
204
+ .vphud-quit[hidden]{display:none}
205
+ /* PAUSA dentro do canvas (tela = canvas) — sobreposta ao #game-region, não à página */
206
+ .pause-incanvas{position:absolute;inset:0;z-index:6;display:flex;align-items:center;justify-content:center;background:var(--bg-solid);font-family:var(--font)}
207
+ .pause-incanvas[hidden]{display:none}
208
+ /* Etapa 2: menu de pausa POR TELA (dentro de cada .player-screen). Cada jogador vê e navega o seu. */
209
+ .screen-pause{position:absolute;inset:0;z-index:6;display:flex;align-items:center;justify-content:center;background:var(--bg-solid);font-family:var(--font);font-size:var(--ui-fs,16px);pointer-events:auto} /* base = --ui-fs (16 no mínimo, escala com o canvas) */
210
+ .screen-pause[hidden]{display:none}
211
+ .screen-pause .pause-card{max-width:94%;max-height:98%;padding:.5rem .7rem}
212
+ .screen-pause .pause-card h2{font-size:1.2em;margin:.05rem 0 .28rem}
213
+ .screen-pause .pause-menu{gap:.3rem}
214
+ /* O quadro por jogador aperta o PADDING, nunca a altura mínima: o quadro apertado é o do jogo com quatro
215
+ crianças, onde os dedos disputam espaço — o pior lugar possível para encolher um alvo de toque. O cartão
216
+ tem `overflow:auto` e rola; o botão não diminui. */
217
+ .screen-pause .pm-btn{padding:.3rem .7rem}
218
+ .screen-pause .pause-legend{margin:.28rem 0 0}
219
+ /* Atalhos de a11y = botões do menu em forma de ícone, DENTRO do card (sem barra/sombra própria).
220
+ Navegáveis por teclado (cima a partir de Continuar) e por toque/clique. */
221
+ .pause-icons{display:flex;flex-wrap:wrap;gap:.4rem;justify-content:center;align-items:center;margin:.1rem 0 .35rem}
222
+ /* ===================== A BARRA RÁPIDA NO HUD (ADR-0044, item 7) =====================
223
+ Ela saiu do cartão de pausa e passou a viver na tela do jogador, disponível DURANTE a partida — que é
224
+ quando uma criança precisa mudar um ajuste que está a atrapalhando, e não depois de pausar.
225
+
226
+ É IRMÃ da `.screen-exp` e não filha, e a razão é o #82: `filter` de CSS desce para os descendentes e um
227
+ filho não consegue cancelá-lo. Dentro da experiência, o modo empatia degradaria justamente o que existe
228
+ para DAR acesso — e simulação não é acessibilidade, é o contrário dela.
229
+
230
+ `pointer-events:auto` porque a `.player-screen` inteira é `none` (o jogo recebe o toque); os dez ícones
231
+ são a exceção, como o cartão de pausa já era. */
232
+ /* A ALTURA DA BARRA, num token: é ela que empurra o HUD para baixo. Sem isto os dois ficavam ancorados no
233
+ topo e se sobrepunham — o HUD lido por cima dos ícones, que foi o que o Dev viu e chamou de péssimo. */
234
+ .player-screen{--a11y-h:calc(var(--tap) + .5rem)}
235
+ .screen-a11y{position:absolute;left:0;right:0;top:0;height:var(--a11y-h);z-index:5;pointer-events:auto;font-family:var(--font);font-size:var(--ui-fs,16px)}
236
+ .screen-a11y .pause-icons{flex-wrap:nowrap;white-space:nowrap;gap:.25rem;margin:0;height:100%}
237
+ /* SÓ OS BOTÕES OCUPAM ESPAÇO. A explicação é ABSOLUTA e fica fora do fluxo — ela aparece por cima do jogo ao
238
+ passar o mouse (ou quando o cursor do modo `accessibility` pousa num ícone) e some sem deixar buraco. Uma
239
+ faixa de texto reservando altura na tela de jogo é o contrário do que uma barra de atalho serve. */
240
+ .screen-a11y .pause-icons-cap{position:absolute;left:50%;transform:translateX(-50%);top:calc(var(--a11y-h) + .1rem);margin:0;max-width:92%;text-align:center;font-size:.8em;color:#f6f5f0;pointer-events:none}
241
+ .screen-a11y .pause-icons-cap:not(:empty){background:rgba(8,8,16,.92);border-radius:8px;padding:.2em .6em}
242
+ /* O HUD desce para DEBAIXO da barra. É a mesma conta, e é por isso que ela mora num token: as duas medidas
243
+ não podem divergir sem que uma tape a outra. */
244
+ .screen-exp .vphud{top:var(--a11y-h)}
245
+ .pi-btn{width:var(--tap);height:var(--tap);min-width:var(--tap);min-height:var(--tap);border-radius:10px;border:2px solid var(--ink-soft);background:#1a2740;font-size:1.25em;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center}
246
+ /* Wizard de gamepad: demonstração com ANIMAÇÃO REAL (frames do jogo trocados via JS) + deslocamento CSS */
247
+ #padwiz-demo{height:86px;display:flex;align-items:flex-end;justify-content:center;position:relative;margin:.15rem 0 .4rem;overflow:hidden}
248
+ #padwiz-demo-img{height:64px;image-rendering:pixelated}
249
+ #padwiz-demo-fx{position:absolute;top:2px;font-size:1.5em;line-height:1}
250
+ #padwiz-demo.pw-left #padwiz-demo-img{animation:pwSlideL 1.4s linear infinite}
251
+ #padwiz-demo.pw-right #padwiz-demo-img{animation:pwSlideR 1.4s linear infinite}
252
+ #padwiz-demo.pw-up #padwiz-demo-img{animation:pwClimbUp 1.4s linear infinite}
253
+ #padwiz-demo.pw-down #padwiz-demo-img{animation:pwClimbDown 1.4s linear infinite}
254
+ #padwiz-demo.pw-jump #padwiz-demo-img{animation:pwJump .9s cubic-bezier(.3,0,.7,1) infinite}
255
+ #padwiz-demo.pw-run #padwiz-demo-img{animation:pwSlideR .8s linear infinite}
256
+ #padwiz-demo.pw-swap,#padwiz-demo.pw-start{align-items:center}
257
+ #padwiz-demo.pw-swap #padwiz-demo-fx{position:static;white-space:nowrap;font-size:1.7em;animation:pwIcons 4s linear infinite}
258
+ #padwiz-demo.pw-especial #padwiz-demo-fx{animation:pwPulse .8s ease-in-out infinite}
259
+ #padwiz-demo.pw-start #padwiz-demo-fx{position:static;font-weight:800;font-size:1.9em;letter-spacing:.1em;animation:pwBlink 1s steps(2,end) infinite}
260
+ @keyframes pwSlideL{0%{translate:34px 0}100%{translate:-34px 0}}
261
+ @keyframes pwSlideR{0%{translate:-34px 0}100%{translate:34px 0}}
262
+ @keyframes pwClimbUp{0%{translate:0 22px}100%{translate:0 -22px}}
263
+ @keyframes pwClimbDown{0%{translate:0 -22px}100%{translate:0 22px}}
264
+ @keyframes pwJump{0%,100%{translate:0 0}35%{translate:0 -26px}55%{translate:0 -22px}}
265
+ @keyframes pwIcons{0%{translate:60px 0}100%{translate:-60px 0}}
266
+ @keyframes pwPulse{0%,100%{scale:1;opacity:.7}50%{scale:1.5;opacity:1}}
267
+ @keyframes pwBlink{0%,100%{opacity:1}50%{opacity:.25}}
268
+ @media (prefers-reduced-motion: reduce){ #padwiz-demo *{animation:none !important} }
269
+ .pi-btn.pi-on{background:var(--accent);border-color:var(--accent)}
270
+ /* Modo TEA (🧩): pi-on preenchido de amarelo = silencioso · fundo branco = calmo · base (azul) = off.
271
+ (fundo, não borda — a borda é reservada ao realce de seleção :focus/.pi-sel e sobreporia o sinal.) */
272
+ .pi-btn.pi-calm{background:#fff;border-color:var(--ink-soft)}
273
+ /* Correção de daltonismo (🚥): fundo bicolor "diagnóstico" — pares escolhidos por busca (simulação Machado
274
+ 2009, ΔE Lab) p/ que quem TEM a deficiência-alvo veja o maior contraste e as outras duas o menor. Tritan
275
+ (eixo azul-amarelo, isolado) separa limpo com vermelho/verde; protan e deutan compartilham o eixo
276
+ vermelho-verde → separação só PARCIAL (limite físico). off = base (azul, borda cinza); borda clara = ativo. */
277
+ .pi-btn.pi-cvd-protan{background:linear-gradient(90deg,#b3671b 0 50%,#b31b41 50% 100%);border-color:var(--ink)}
278
+ .pi-btn.pi-cvd-deuter{background:linear-gradient(90deg,#0000f2 0 50%,#aa61f2 50% 100%);border-color:var(--ink)}
279
+ .pi-btn.pi-cvd-tritan{background:linear-gradient(90deg,#f23d00 0 50%,#1bb341 50% 100%);border-color:var(--ink)}
280
+ .pi-btn.pi-dis{opacity:.35;cursor:not-allowed}
281
+ .pi-btn.pi-soon{opacity:.55}
282
+ .pi-btn.pi-sel,.pi-btn:focus,.pi-btn:focus-visible{outline:2px solid var(--accent);outline-offset:1px;border-color:var(--accent)}
283
+ .pause-icons-cap{text-align:center;margin:0 0 .3rem;font-size:.82em;color:var(--ink-soft);min-height:1.1em}
284
+ /* `scrollbar-gutter: stable both-edges` — MEDIDO no navegador depois do item 6: com a lista em 44 px o
285
+ cartão passa a rolar, a barra de rolagem come largura de UM lado só, e a lista centrada saía 7,5 px do
286
+ centro. Reservar a calha nos dois lados devolve a simetria, role ou não. */
287
+ .pause-card{background:#0e1626;border:2px solid #ffd23f;border-radius:14px;padding:.8rem 1rem;max-width:88%;max-height:92%;overflow:auto;scrollbar-gutter:stable both-edges;text-align:center;box-shadow:0 8px 30px rgba(0,0,0,.5)}
288
+ .pause-card h2{margin:.1rem 0 .6rem;color:#ffd23f}
289
+ /* ===================== A LISTA DE PAUSA (ADR-0044, item 6) =====================
290
+ Era `grid-template-columns:1fr 1fr`, e a grade contava a mesma mentira dos submenus da abertura: a seta
291
+ anda em ordem de DOM, então "para baixo" pulava para a coluna da direita. Uma coluna, e o que se vê volta
292
+ a ser a ordem em que se anda.
293
+ CENTRADA E MAIS LARGA: 26rem contra os 262 px medidos antes — o número está no gate, não na opinião.
294
+ `width` e não `min(26rem,100%)`: com a barra de ícones fora do cartão (item 7), o cartão passou a se
295
+ encolher ao conteúdo, e um `100%` medido contra ele virava circular — a lista caiu para 182 px. Quem
296
+ declara a largura é a LISTA; o cartão a acompanha até o `max-width` dele. */
297
+ .pause-menu{display:flex;flex-direction:column;gap:.45rem;width:26rem;max-width:100%;margin:0 auto}
298
+ /* A lista ESCONDIDA tem de sumir de verdade: `display:grid` de autor vence o `[hidden]{display:none}` do
299
+ navegador, e sem esta linha as duas listas do cartão de pausa apareceriam empilhadas (ADR-0044, item 5). */
300
+ .pause-menu[hidden]{display:none}
301
+ /* 44 px NÃO é o mínimo da WCAG — o 2.5.8 pede 24×24, e o cartão já passava nisso com folga. 44 é a
302
+ recomendação da Apple (HIG), e este projeto trata piso como piso. `min-height` e não `padding` porque a
303
+ altura de antes (35,6 px) não estava declarada em lugar nenhum: ela RESULTAVA da soma de padding, fonte e
304
+ entrelinha. Um alvo que ninguém escreveu é um alvo que ninguém defende. */
305
+ .pm-btn{background:#1a2740;color:#eaf2f8;border:1px solid #3a4a6a;border-radius:10px;padding:.5rem .8rem;min-height:44px;display:flex;align-items:center;justify-content:center;font:inherit;font-weight:700;cursor:pointer;white-space:nowrap}
306
+ .pm-btn:hover,.pm-btn:focus,.pm-btn.pm-sel{background:#2a3a5e;border-color:#ffd23f;outline:2px solid #ffd23f;outline-offset:1px}
307
+ /* Legenda sim/não: ícone do botão (conforme layout do controle) + a palavra. Sony/Nintendo invertem 0↔1. */
308
+ .pause-legend{display:flex;gap:1.1rem;justify-content:center;margin:.7rem 0 .1rem;font-size:.82em;color:#c3cfe0}
309
+ .pause-legend .lg{display:inline-flex;align-items:center;gap:.35rem}
310
+ .pause-legend .lg-ico{display:inline-flex;align-items:center;justify-content:center;width:1.35rem;height:1.35rem;border-radius:50%;font-weight:800;font-size:.8rem;color:#fff;text-shadow:0 1px 1px rgba(0,0,0,.55)}
311
+ .hud__item strong{font-variant-numeric:tabular-nums}
312
+ .hud__fps{color:var(--ink-soft)}
313
+
314
+ main{flex:1;display:flex;flex-direction:column;width:100%;margin:0 auto;padding:0 .25rem .25rem;min-height:0;overflow:hidden}
315
+ /* tela = prioridade: ocupa todo o espaço; o excedente (até 5px×k por lado) é cortado sem rolagem */
316
+ .stage-wrap{flex:1;display:flex;align-items:center;justify-content:center;min-height:0;padding:0;overflow:hidden}
317
+ .stage{position:relative;display:flex;align-items:center}
318
+ /* ===== A CAMADA DOM (issue #82) =====
319
+ Tudo o que não é a canvas. Existe para o filtro de acessibilidade poder cair numa superfície que contém os
320
+ MENUS e não contém o mundo — filtro de PIXI não alcança DOM, e filtrar o `#game-region` inteiro aplicaria
321
+ duas vezes na canvas.
322
+
323
+ `pointer-events:none` porque o invólucro cobre a região inteira e não pode engolir clique; os quatro filhos
324
+ que precisam de clique e não o declaravam (herdavam do `#game-region`) ganham `auto` logo abaixo.
325
+
326
+ `z-index:1`: acima da canvas, ABAIXO dos dois pseudo-elementos do CRT (vinheta em 5, scanline em 500), que
327
+ ficam no `#game-region`. O CRT é efeito de tela e passa por cima de tudo — o que a scanline já fazia por
328
+ decisão do Dev, e a vinheta agora acompanha. */
329
+ #dom-layer{position:absolute;inset:0;z-index:1;pointer-events:none}
330
+
331
+ /* ===== ALTO CONTRASTE NO DOM (issue #83) =====
332
+ O alto contraste NÃO é filtro: ele é Renderização Direta, e repinta as TEXTURAS da canvas. No DOM não há
333
+ textura para repintar — há que escrever o equivalente, e é isto.
334
+
335
+ MEDIDO, não estimado (WCAG 1.4.3, razão sobre sRGB linearizado). O que falhava, e só no nível 7:1:
336
+
337
+ · o VÉU do painel de pausa é `rgba(4,7,15,.72)` — translúcido. A razão do texto sobre ele DEPENDE do
338
+ jogo por trás: 19,17:1 sobre um quadro preto e 8,07:1 sobre um branco. Com `--ink-soft`, `--accent` e
339
+ `--good` a conta cai a 5,92 / 5,94 / 5,11:1 — AA, não AAA. Nos níveis 3:1 e 4,5:1 nada falhava.
340
+ · o botão SELECIONADO é `#2a3a5e`, e `--good` sobre ele dá 6,70:1.
341
+
342
+ Duas mudanças, e nenhuma inventa cor:
343
+
344
+ 1. O VÉU FICA OPACO. É o que "alto contraste" quer dizer: sai o meio-tom, e com ele a dependência do que
345
+ está atrás. Sozinha, esta já leva tudo a AAA menos um caso.
346
+ 2. O CURSOR INVERTE — fundo `--accent`, texto `--accent-ink`, 12,71:1. Escurecer o selecionado o
347
+ aproximaria do não-selecionado (`#1a2740`) e a criança PERDERIA O CURSOR; inverter resolve as duas
348
+ coisas de uma vez, e é o padrão de alto contraste de qualquer sistema operacional.
349
+
350
+ Resultado: o pior par do menu vai de 5,11:1 para 10,29:1 (`--ink-soft` sobre botão). Margem de +47% sobre
351
+ o 7:1. `tests/contraste-menu.node.test.js` afirma isso a cada rodada, com as razões calculadas do próprio
352
+ CSS — não copiadas para dentro do teste.
353
+
354
+ ⚠️ VALE PARA OS TRÊS NÍVEIS, e não só para o 7:1: nos outros dois o menu já passava, mas passava POR SORTE
355
+ do que estivesse na tela. Determinismo é ganho em todos. */
356
+ /* (A regra do véu SAIU daqui em 2026-08-26: ele passou a ser opaco NA BASE, por decisão do Dev — os menus
357
+ de título e de pausa têm de estar em 7:1 DESDE O INÍCIO, porque é justamente neles que a pessoa com
358
+ deficiência ajusta os controles para si. Um contraste que só chega depois de achar o ajuste chega tarde.
359
+ Sobra aqui o que é específico do modo: o cursor invertido.) */
360
+ #dom-layer.hc .pm-btn{background:var(--panel-btn)}
361
+ #dom-layer.hc .pm-btn:hover,
362
+ #dom-layer.hc .pm-btn:focus,
363
+ #dom-layer.hc .pm-btn.pm-sel{background:var(--accent);color:var(--accent-ink);border-color:var(--accent-ink)}
364
+ #dom-layer #viz-indicator,#dom-layer .overlay,#dom-layer .quiz,#dom-layer .pause-incanvas{pointer-events:auto}
365
+
366
+ .game-region{position:relative;background:#05070f;box-shadow:0 0 0 3px var(--ink-soft);border-radius:8px;overflow:hidden;flex:none}
367
+ .libras-slot{flex:none;width:0;height:0;background:#0b1020;border-radius:8px;overflow:hidden}
368
+ .game-region:focus-visible{outline:4px solid var(--focus);outline-offset:3px}
369
+ /* ===== O AVISO DE ALCANCE (issue #112) =====
370
+ Reaproveita `.overlay` e `.overlay__card` inteiros — o cartão nasce com a mesma casca dos outros diálogos,
371
+ e é isso que o faz parecer parte do jogo em vez de um erro do navegador. Só o destaque do título é próprio. */
372
+ #reach-notice .reach-notice__titulo{font-weight:700;margin:0 0 .5rem}
373
+ #reach-notice p{margin:0 0 .5rem}
374
+ #reach-notice button{margin-top:.75rem}
375
+ /* ===== O LAÇO PAROU (ADR-0054, issue #109) =====
376
+ O TEXTO VEM DO ATRIBUTO, e é esse o ponto: `ui/loop-crash` escreve a frase JÁ TRADUZIDA em
377
+ `data-incl-parou`, e o CSS só a mostra. Um aviso cravado aqui estaria em inglês para uma criança
378
+ brasileira — o defeito que o pilar 3 existe para impedir.
379
+ ⚠️ E AS CORES SÃO CRUAS, DE PROPÓSITO: nada de `var(--…)`. Este aviso aparece justamente quando alguma
380
+ coisa quebrou, e a máquina de temas é candidata plausível. Branco sobre vinho dá 9,5:1, acima do 7:1 do
381
+ 1.4.6, e não depende de nada ter sobrevivido. */
382
+ .game-region[data-incl-parou]::after{
383
+ content:attr(data-incl-parou);
384
+ position:absolute;inset:auto 0 0 0;z-index:60;
385
+ padding:14px 18px;
386
+ background:#7f1d1d;color:#fff;border-top:3px solid #fff;
387
+ font:700 15px/1.35 system-ui,-apple-system,"Segoe UI",sans-serif;
388
+ text-align:center;text-wrap:balance
389
+ }
390
+ .pixi-mount,.pixi-mount canvas{display:block;width:100%;height:100%}
391
+ /* pixel-art nítida ao escalar */
392
+ .pixi-mount canvas{image-rendering:pixelated;image-rendering:crisp-edges}
393
+ /* L2: Estética CRT (menu Sensibilidade visual) em 3 níveis: -1=pequeno, -2=grande. z-index 5 = sobre o
394
+ canvas/HUD do jogo, ABAIXO da pausa in-canvas (6) e dos diálogos (60) → menus nunca perdem legibilidade. */
395
+ .game-region.crt-scan-1::after,
396
+ .game-region.crt-vig-1::before{content:'';position:absolute;inset:0;z-index:5;pointer-events:none}
397
+ /* Scanlines ALINHADAS: período = 1 pixel LÓGICO do jogo (k px), arredondado p/ pixel FÍSICO (dpr) e
398
+ linha = 1 pixel físico — o JS (layout/applyCrt) calcula --scan-per/--scan-line; sem isso o gradiente
399
+ 1px/3px em px CSS caía fora da grade (linhas desiguais em telas com escala do Windows). */
400
+ .game-region.crt-scan-1::after{z-index:500;background:repeating-linear-gradient(to bottom, rgba(0,0,0,.16) 0 var(--scan-line,1px), transparent var(--scan-line,1px) var(--scan-per,3px))} /* z-index alto = scanlines POR CIMA de tudo (canvas E menus DOM), pedido do José */
401
+ .game-region.crt-vig-1::before{background:radial-gradient(ellipse at center, transparent 60%, rgba(0,0,0,.34) 100%)}
402
+ .game-region.crt-round-0{border-radius:0} /* tela quadrada */
403
+ .game-region.crt-round-2{border-radius:24px} /* nível 1 (pequeno) = o padrão de 8px, sem classe */
404
+ @media (prefers-reduced-transparency: reduce){ .game-region.crt-scan-1::after,.game-region.crt-vig-1::before{display:none} }
405
+
406
+ /* dicas de início sobre o canvas */
407
+ .start-tips{position:absolute;left:50%;top:14%;transform:translateX(-50%);
408
+ display:flex;flex-direction:column;gap:.5rem;align-items:center;pointer-events:none;
409
+ transition:opacity .5s ease;z-index:6;max-width:92%}
410
+ .start-tips p{margin:0;background:rgba(5,7,15,.85);color:#fff;border:2px solid rgba(255,255,255,.45);
411
+ border-radius:10px;padding:.4em .9em;font-size:1.05rem;font-weight:600;text-align:center}
412
+ .start-tips kbd{background:var(--accent);color:var(--accent-ink);border-radius:6px;padding:.05em .5em;font-weight:800}
413
+ .start-tips.hide{opacity:0}
414
+ @media (prefers-reduced-motion: reduce){ .start-tips{transition:none} }
415
+
416
+ /* seletor de modo */
417
+ .modes{max-width:var(--maxw);margin:0 auto .5rem;padding:0 1rem;display:flex;gap:.5rem;flex-wrap:wrap}
418
+ .mode-btn{min-height:var(--tap);padding:.4em .9em;font:inherit;font-weight:700;color:var(--ink);background:var(--panel);border:2px solid var(--ink-soft);border-radius:999px;cursor:pointer}
419
+ .mode-btn.is-on{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
420
+ /* TOGGLE (switch): substitui os botões "ícone + Ligado/Desligado". Estado = trilho + manopla que desliza.
421
+ Semântica de botão de alternância via aria-pressed (o texto interno fica oculto por font-size:0). */
422
+ .mode-btn.switch{--sw-w:52px;--sw-h:28px;position:relative;width:var(--sw-w);min-width:var(--sw-w);height:var(--sw-h);min-height:var(--sw-h);padding:0;flex:0 0 auto;border-radius:999px;border:2px solid var(--ink-soft);background:#0e1326;color:transparent;font-size:0;transition:background .18s,border-color .18s}
423
+ .mode-btn.switch::after{content:"";position:absolute;top:50%;left:3px;transform:translateY(-50%);width:calc(var(--sw-h) - 10px);height:calc(var(--sw-h) - 10px);border-radius:50%;background:var(--ink-soft);transition:left .18s,background .18s}
424
+ .mode-btn.switch[aria-pressed="true"]{background:var(--accent);border-color:var(--accent)}
425
+ .mode-btn.switch[aria-pressed="true"]::after{left:calc(var(--sw-w) - (var(--sw-h) - 10px) - 5px);background:var(--accent-ink)}
426
+ .mode-btn.switch:focus-visible,.mode-btn.switch:focus{outline:3px solid var(--accent);outline-offset:2px}
427
+ @media (prefers-reduced-motion: reduce){ .mode-btn.switch,.mode-btn.switch::after{transition:none} }
428
+
429
+ /* desafio Soma-Sub (overlay sobre o canvas) */
430
+ .quiz{position:absolute;inset:0;z-index:12;display:grid;place-items:center;background:rgba(0,0,0,.55)}
431
+ .quiz-box{position:relative;background:#14141f;color:var(--ink);border:3px solid var(--accent);border-radius:12px;padding:1rem 1.1rem;max-width:90%;text-align:center}
432
+ /* 3 luzes de progresso (canto sup. direito): apagada = cinza; acesa = amarela com luz acesa em volta */
433
+ .quiz-wins{position:absolute;top:.5rem;right:.6rem;display:flex;gap:.4rem}
434
+ .qw-dot{width:.95rem;height:.95rem;border-radius:50%;background:#3a4152;border:2px solid #0d0d1a}
435
+ .qw-dot.on{background:var(--accent);border-color:#8a6d00;box-shadow:0 0 .55rem .12rem var(--accent)} /* iluminação em volta (luz acesa) */
436
+ .quiz-wins.celebrate .qw-dot.on{animation:qwPop .6s ease}
437
+ @keyframes qwPop{0%{transform:scale(1)}45%{transform:scale(1.4);box-shadow:0 0 .9rem .3rem var(--accent)}100%{transform:scale(1)}}
438
+ @media (prefers-reduced-motion:reduce){ .quiz-wins.celebrate .qw-dot.on{animation:none} }
439
+ .quiz-shape{color:var(--accent);font-weight:800;text-transform:capitalize}
440
+ .quiz-prob{font-size:1.6rem;font-weight:800;margin:.4rem 0 .8rem}
441
+ .quiz-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:.4rem}
442
+ .quiz-choice{min-height:var(--tap);min-width:var(--tap);font:inherit;font-size:1.2rem;font-weight:700;color:var(--ink);background:#131a30;border:2px solid var(--ink-soft);border-radius:8px;cursor:pointer}
443
+ .quiz-choice.sel{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
444
+ .quiz-choice.reveal{outline:3px solid var(--good);outline-offset:2px}
445
+ .quiz-hint{font-size:.85rem;margin-top:.6rem;color:var(--ink-soft)}
446
+ .quiz-emoji{font-size:2.4rem;line-height:1}
447
+ /* Palavra do topo = emoji SELECIONÁVEL (pular sobre ela repete a fala / VLibras gesticula) */
448
+ .quiz-word{font-size:2.4rem;line-height:1;background:transparent;border:2px solid transparent;border-radius:12px;padding:.05em .2em;cursor:pointer}
449
+ .quiz-word.sel{border-color:var(--accent);background:rgba(255,230,0,.12)}
450
+ .quiz-letter{font-size:.8rem;color:var(--ink-soft);margin:.1rem 0 .4rem}
451
+ .silaba-boxes{display:flex;gap:.4rem;justify-content:center;margin-bottom:.5rem}
452
+ .silaba-box{min-width:34px;min-height:28px;border:2px dashed var(--ink-soft);border-radius:6px;display:flex;align-items:center;justify-content:center;font-weight:800;font-size:1.15rem}
453
+ .silaba-box.filled{border-style:solid;border-color:var(--accent);background:rgba(255,255,255,.08)}
454
+ .silaba-actions{display:flex;gap:.4rem;justify-content:center;margin-top:.4rem}
455
+ /* E8: cela Braille */
456
+ .bcells{display:flex;gap:.5rem;justify-content:center;flex-wrap:wrap;margin:.4rem 0}
457
+ .bcell{display:flex;flex-direction:column;align-items:center;gap:.2rem}
458
+ .bcell-grid{display:grid;grid-template-columns:repeat(2,10px);grid-auto-rows:10px;gap:3px;padding:4px;background:#0e1326;border:1px solid var(--ink-soft);border-radius:4px}
459
+ .bdot{width:10px;height:10px;border-radius:50%;background:#2a2f45}
460
+ .bdot.on{background:var(--accent)}
461
+ .bcell-l{font-weight:800}
462
+ @media (prefers-contrast: more){ .quiz-box{background:#000} .quiz-choice{background:#000} .quiz-choice.sel{background:#fff;color:#000} }
463
+
464
+ .opts{max-width:var(--maxw);margin:0 auto .5rem;padding:0 1rem;display:flex;gap:.4rem;flex-wrap:wrap;justify-content:center}
465
+ /* barra de controles compacta → cabe em UMA fileira em telas comuns (não afeta botões de overlay) */
466
+ .opts .mode-btn{padding:.35em .65em;font-size:.92rem;white-space:nowrap}
467
+ .caption{position:absolute;left:50%;bottom:8px;transform:translateX(-50%);background:rgba(0,0,0,.82);color:#fff;
468
+ border:2px solid rgba(255,255,255,.5);border-radius:8px;padding:.25em .7em;font-weight:700;font-size:.95rem;
469
+ z-index:8;pointer-events:none;max-width:90%;opacity:0;transition:opacity .15s}
470
+ .caption.show{opacity:1}
471
+ @media (prefers-reduced-motion: reduce){ .caption{transition:none} }
472
+
473
+ .help{max-width:var(--maxw);margin:1rem auto 0;color:var(--ink-soft)}
474
+ .help summary{cursor:pointer;font-weight:700;color:var(--ink);min-height:var(--tap);display:flex;align-items:center}
475
+ .help ul{padding-left:1.1rem}
476
+ .help li{margin:.3rem 0}
477
+
478
+ .btn{min-height:var(--tap);min-width:var(--tap);padding:.5em 1em;font:inherit;font-weight:600;color:var(--ink);background:var(--panel);border:2px solid var(--ink-soft);border-radius:10px;cursor:pointer}
479
+ .btn--primary{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
480
+
481
+ .overlay{position:fixed;inset:0;z-index:50;display:grid;place-items:center;background:rgba(0,0,0,.7);padding:1rem}
482
+ /* "tela = canvas": os diálogos de a11y são reparentados para dentro do #game-region (JS) e ficam
483
+ presos aos limites do canvas; o último aberto sobe (z-index via JS). Só afeta os que estão dentro. */
484
+ #game-region .overlay{position:absolute;z-index:60;padding:.6rem}
485
+ #game-region .overlay__card{max-height:94%;max-width:96%}
486
+ /* Rodapé de explicação: branco sobre preto translúcido 0,82 → contraste do branco >7:1 mesmo sobre 100% branco
487
+ (composição: 1-0,82=0,18 → L≈0,027 → ~13:1), sem ser 100% opaco. Fixo no rodapé do card. */
488
+ .opt-explain{position:sticky;bottom:calc(-1 * var(--card-pad,1.5rem));margin:.8rem calc(-1 * var(--card-pad,1.5rem)) calc(-1 * var(--card-pad,1.5rem));padding:.6rem .9rem;min-height:2.8rem;display:flex;align-items:center;text-align:left;
489
+ background:rgba(0,0,0,.82);color:#fff;font-size:.9em;line-height:1.35;border-top:1px solid rgba(255,255,255,.18);border-radius:0 0 9px 9px;backdrop-filter:blur(1px)}
490
+ #game-region .overlay__actions{position:static;background:transparent;padding:.5rem 0 0;margin:0}
491
+ #visual{background:rgba(0,0,0,.28);align-items:start;padding-top:3.5rem} /* cor: fundo translúcido p/ ver o efeito imediato no jogo */
492
+
493
+ /* Indicador de simulação (canto sup. dir.): branca=cegueira, verde=baixa visão. Duplo toque volta ao normal. */
494
+ #viz-indicator{position:absolute;top:6px;right:6px;z-index:20;width:16px;height:16px;border-radius:50%;border:2px solid rgba(0,0,0,.6);padding:0;cursor:pointer;box-shadow:0 1px 4px rgba(0,0,0,.7)} /* DENTRO do canvas (game-region) — funciona como botão */
495
+ #viz-indicator.blind{background:#fff}
496
+ #viz-indicator.low{background:#36d36a}
497
+ /* Baixa visão — 5 simulações (desfoque vem do filter na canvas; aqui só os overlays) */
498
+ #viz-overlay{position:absolute;inset:0;z-index:8;pointer-events:none}
499
+ #viz-overlay.lv-blur{background:none} /* miopia/astigmatismo: só desfoque */
500
+ #viz-overlay.lv-haze{background:rgba(244,246,250,.42)} /* catarata: película esbranquiçada */
501
+ #viz-overlay.lv-tunnel{background:radial-gradient(circle at 50% 50%, transparent 0 15%, rgba(0,0,0,.55) 27%, rgba(0,0,0,.99) 42%)} /* glaucoma: visão de túnel */
502
+ #viz-overlay.lv-macular{background:radial-gradient(circle at 50% 50%, rgba(12,12,15,.95) 0 10%, rgba(12,12,15,.55) 17%, transparent 28%)} /* degeneração macular: borrão central */
503
+ #viz-overlay.lv-diabetic{background: /* retinopatia diabética: manchas dispersas */
504
+ radial-gradient(circle at 22% 30%, rgba(10,10,14,.95) 0 4%, transparent 11%),
505
+ radial-gradient(circle at 64% 22%, rgba(10,10,14,.9) 0 3%, transparent 9%),
506
+ radial-gradient(circle at 80% 58%, rgba(10,10,14,.95) 0 5%, transparent 12%),
507
+ radial-gradient(circle at 40% 70%, rgba(10,10,14,.9) 0 4%, transparent 10%),
508
+ radial-gradient(circle at 16% 80%, rgba(10,10,14,.85) 0 3%, transparent 9%),
509
+ radial-gradient(circle at 54% 48%, rgba(10,10,14,.8) 0 3%, transparent 8%)}
510
+ /* Cegueira total: a canvas vira preta (filter brightness(0)); escondemos HUD e controles na tela */
511
+ body.blind-mode .hud, body.blind-mode #touch-controls, body.blind-mode #start-tips, body.blind-mode #caption{visibility:hidden}
512
+ [hidden]{display:none!important}
513
+ /* CAIXA ALTA (menu de CAA, ADR-0028). O jogo tem DOIS caminhos de texto e por isso dois mecanismos, que não
514
+ são redundância: o `disp()` do main.js cobre o que a PIXI desenha na canvas, e esta regra cobre o que o
515
+ navegador desenha no DOM — menus, HUD, legendas. O botão só ligava o primeiro, então a criança escolhia
516
+ "maiúsculas" e os menus continuavam mistos.
517
+ `text-transform` e NÃO transformar o texto: o conteúdo acessível fica intacto, e um leitor de tela não passa
518
+ a soletrar letra por letra, que é o que ALL CAPS de verdade provoca em NVDA/JAWS. */
519
+ /* Alternativas do quiz (segundo consumidor): altura mínima em MILÍMETROS REAIS, calculada por
520
+ `input/touch.padPxPerMm` — WCAG 2.5.5 / GAG. O valor chega em `--quiz-alt-min`. */
521
+ .quiz-alt{min-height:var(--quiz-alt-min,44px);display:block;width:100%;margin:.25rem 0}
522
+ .quiz-alts{display:flex;flex-direction:column;gap:.25rem}
523
+ :root[data-letras="upper"] body,
524
+ :root[data-letras="upper"] button,
525
+ :root[data-letras="upper"] select,
526
+ :root[data-letras="upper"] option,
527
+ :root[data-letras="upper"] input,
528
+ :root[data-letras="upper"] textarea{text-transform:uppercase}
529
+ /* Os controles de formulário estão na lista porque a folha do NAVEGADOR lhes impõe `text-transform:none`, e
530
+ `none` numa regra do UA vence a herança. Sem eles a caixa alta pegava no texto das telas e falhava em cada
531
+ BOTÃO — que é justamente onde os rótulos dos menus moram. Foi o que o Dev viu: "não torna todas as letras
532
+ do menu maiúsculas". */
533
+ /* menus nunca ultrapassam a tela: limitados à altura do viewport e roláveis (rodapé de ações fica fixo embaixo) */
534
+ .overlay__card{background:var(--panel);border:3px solid var(--accent);border-radius:12px;padding:1.5rem;max-width:420px;width:100%;text-align:center;font-size:var(--ui-fs,16px);
535
+ max-height:92dvh;overflow-y:auto;overscroll-behavior:contain} /* base = --ui-fs (16 mín, escala com o canvas); internos em em */
536
+ .overlay__card h2{margin-top:0}
537
+ .topbar__right{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap}
538
+ .title-card{text-align:center}
539
+ .title-big{font-size:2rem;margin:.2rem 0;color:var(--accent)}
540
+ .title-by{color:var(--ink-soft);margin:.2rem 0 1rem}
541
+ .title-card .btn{margin-top:.4rem;font-size:1.1rem;padding:.6em 1.6em}
542
+ .overlay__actions{display:flex;gap:.6rem;flex-wrap:wrap;margin-top:1rem;justify-content:flex-end;
543
+ position:sticky;bottom:-1.5rem;background:var(--panel);padding:.6rem 0 1.5rem;margin-bottom:-1.5rem}
544
+ .opt-hint{color:var(--ink-soft);font-size:.9em;margin:.2rem 0 .8rem}
545
+ .panel-sub{text-align:left;font-size:1rem;margin:1rem 0 .4rem;border-bottom:1px solid var(--accent);padding-bottom:.2rem}
546
+ .panel-sub:first-of-type{margin-top:.4rem}
547
+ .panel-sub__tag{color:var(--ink-soft);font-size:.78em;font-weight:400}
548
+ .vol{width:140px;max-width:45%;accent-color:var(--accent);height:1.5rem;cursor:pointer} /* barra de volume (range) */
549
+ /* Caixas de seleção com a IDENTIDADE do jogo: fonte escolhida, cores e tamanho (não o estilo do SO) */
550
+ select.vol, .overlay__card select, .ctrl-col select{
551
+ font-family:var(--font);font-size:.95em;font-weight:600;color:var(--ink);
552
+ width:auto;max-width:100%;height:auto;min-height:var(--tap);
553
+ background:#0e1326;border:2px solid var(--ink-soft);border-radius:8px;
554
+ padding:.35em 2em .35em .7em;cursor:pointer;
555
+ -webkit-appearance:none;-moz-appearance:none;appearance:none;
556
+ background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path d='M1 1l5 5 5-5' fill='none' stroke='%23ffd23f' stroke-width='2' stroke-linecap='round'/></svg>");
557
+ background-repeat:no-repeat;background-position:right .7em center}
558
+ select.vol:focus, .overlay__card select:focus{outline:3px solid var(--accent);outline-offset:1px;border-color:var(--accent)}
559
+ .overlay__card select option{background:#0e1326;color:var(--ink);font-weight:600}
560
+ .ctrl-players{display:flex;gap:.4rem;flex-wrap:wrap;justify-content:center;margin-bottom:.7rem}
561
+ .ctrl-players .mode-btn{padding:.3em .7em;font-size:.95rem}
562
+ .ctrl-list{display:flex;flex-direction:column;gap:.45rem;text-align:left}
563
+ .ctrl-row{display:flex;align-items:center;justify-content:space-between;gap:.6rem;background:#0e1326;border:2px solid var(--ink-soft);border-radius:8px;padding:.4rem .6rem}
564
+ /* ADR-0029 — SAIU DO PADRÃO. Dois canais visuais no mesmo lugar: a COR da moldura e a FORMA, que é uma
565
+ moldura DUPLA (borda, respiro, anel). O anel transparente de 2px revela o fundo do próprio elemento e é o
566
+ que cria o respiro; sem ele os dois anéis colariam e viraria só uma borda mais grossa — uma diferença de
567
+ espessura que ninguém percebe sem ter o não-marcado ao lado para comparar. A contagem de anéis sobrevive a
568
+ escala de cinza, a daltonismo e a toda paleta do jogo, porque não é uma matiz. */
569
+ .is-changed{border-color:var(--changed)!important;box-shadow:inset 0 0 0 2px transparent,inset 0 0 0 4px var(--changed)}
570
+ /* A marca CEDE ao foco: perder o lugar é pior que perder uma dica. */
571
+ .is-changed:focus-visible,.is-changed:focus{box-shadow:none}
572
+ .ctrl-row kbd{background:var(--ink);color:var(--bg);border-radius:5px;padding:.05em .45em;font-weight:700}
573
+ .ctrl-row.row-off{opacity:.42} /* item desabilitado (ex.: modo de teclado indisponível) = só cinza claro, sem texto extra */
574
+ .ctrl-row .mode-btn[disabled]{cursor:not-allowed;opacity:.7}
575
+ .ctrl-row.ctrl-col{flex-direction:column;align-items:stretch;gap:.4rem}
576
+ .ctrl-row.ctrl-col label{font-weight:700}
577
+ .pad-presets{display:flex;gap:.5rem;flex-wrap:wrap}
578
+ .pad-tag{font-size:.8rem;font-weight:700;padding:.05em .5em;border-radius:999px;background:#20304e;color:#bcd}
579
+ .pad-tag[data-who="crianca"]{background:#2a4a2e;color:#bfe9c2}
580
+ .pad-tag[data-who="adulto"]{background:#2a3a5e;color:#c3d4f0}
581
+ .pad-warn{font-size:.85rem;color:#ffdf9e;background:#3a2e12;border:1px solid #6a5220;border-radius:8px;padding:.45rem .6rem}
582
+ .ctrl-col input[type=range]{width:100%;min-height:var(--tap);accent-color:var(--accent)}
583
+
584
+ /* E13: controles de toque (mobile) — overlay nas bordas inferiores, não cobre o centro */
585
+ .touch{position:absolute;inset:auto 0 0 0;z-index:14;display:flex;justify-content:space-between;align-items:flex-end;gap:8px;padding:6px 8px;pointer-events:none}
586
+ .touch-pad{display:flex;gap:8px;pointer-events:auto}
587
+ .touch-btn{width:56px;height:56px;min-width:var(--tap);min-height:var(--tap);border-radius:12px;border:2px solid rgba(255,255,255,.75);background:rgba(13,17,32,.92);color:#fff;font-size:1.4rem;font-weight:800;line-height:1;display:flex;align-items:center;justify-content:center;touch-action:none;-webkit-user-select:none;user-select:none;cursor:pointer}
588
+ .touch-btn:active{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
589
+ .touch-jump{background:rgba(18,74,52,.94)}
590
+ /* Botões de ação em LOSANGO (4 bolhas: 0 baixo, 1 direita, 2 esquerda, 3 cima) + START pílula */
591
+ .touch-actions{align-items:center}
592
+ /* Tamanho FÍSICO (mm), não px: --pad-btn e --pad-diam são calculados no JS a partir de mm reais
593
+ (referência: iPhone 16 ocupando a tela toda). Config em A12e motora. Fallback antes do JS rodar. */
594
+ .pad-diamond{position:relative;width:var(--pad-diam,182px);height:var(--pad-diam,182px);pointer-events:none}
595
+ .pad-b{position:absolute;width:var(--pad-btn,66px);height:var(--pad-btn,66px);border-radius:50%;font-size:calc(var(--pad-btn,66px)*.42);font-weight:800;pointer-events:auto;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.6);display:flex;align-items:center;justify-content:center}
596
+ .pad-top{top:0;left:50%;transform:translateX(-50%)}
597
+ .pad-bottom{bottom:0;left:50%;transform:translateX(-50%)}
598
+ .pad-left{left:0;top:50%;transform:translateY(-50%)}
599
+ .pad-right{right:0;top:50%;transform:translateY(-50%)}
600
+ .touch-start{position:absolute;left:50%;bottom:6px;transform:translateX(-50%);min-height:calc(var(--pad-btn,66px)*.7);box-sizing:border-box;display:inline-flex;align-items:center;justify-content:center;pointer-events:auto;border-radius:999px;padding:0 22px;font-weight:800;font-size:.82rem;letter-spacing:.08em;background:rgba(13,17,32,.92);color:#fff;border:2px solid rgba(255,255,255,.75);cursor:pointer;z-index:15}
601
+ .touch-start:active{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
602
+ /* joystick digital: base (círculo maior) + manopla (círculo menor) */
603
+ /* Direcional (analógico/"alavanca"): base = superfície + deslocamento; manopla = superfície de contato. Ambos em mm (JS). */
604
+ .touch-stick{position:relative;width:var(--stick-base,124px);height:var(--stick-base,124px);border-radius:50%;border:3px solid rgba(255,255,255,.7);background:rgba(13,17,32,.85);touch-action:none;pointer-events:auto;-webkit-user-select:none;user-select:none}
605
+ .touch-knob{position:absolute;left:50%;top:50%;width:var(--stick-knob,58px);height:var(--stick-knob,58px);margin:calc(var(--stick-knob,58px)/-2) 0 0 calc(var(--stick-knob,58px)/-2);border-radius:50%;background:rgba(255,255,255,.88);box-shadow:0 0 0 2px rgba(0,0,0,.45);transition:transform .04s linear;pointer-events:none}
606
+ /* D-pad em cruz (estilo alternativo): 4 braços + miolo; tamanho do braço em mm (JS → --dpad-arm-l/w, --dpad-span) */
607
+ .touch-cross{position:relative;width:var(--dpad-span,120px);height:var(--dpad-span,120px);touch-action:none;pointer-events:auto;-webkit-user-select:none;user-select:none}
608
+ .touch-cross[hidden]{display:none}
609
+ .dpad-arm{position:absolute;display:flex;align-items:center;justify-content:center;background:rgba(13,17,32,.92);border:2px solid rgba(255,255,255,.75);color:rgba(255,255,255,.85);font-size:calc(var(--dpad-arm-w,34px)*.5);line-height:1;pointer-events:none}
610
+ .dpad-arm.on{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
611
+ .dpad-up,.dpad-down{width:var(--dpad-arm-w,34px);height:var(--dpad-arm-l,42px);left:50%;transform:translateX(-50%)}
612
+ .dpad-left,.dpad-right{height:var(--dpad-arm-w,34px);width:var(--dpad-arm-l,42px);top:50%;transform:translateY(-50%)}
613
+ .dpad-up{top:0;border-radius:8px 8px 0 0;border-bottom:none}
614
+ .dpad-down{bottom:0;border-radius:0 0 8px 8px;border-top:none}
615
+ .dpad-left{left:0;border-radius:8px 0 0 8px;border-right:none}
616
+ .dpad-right{right:0;border-radius:0 8px 8px 0;border-left:none}
617
+ .dpad-center{position:absolute;left:50%;top:50%;width:var(--dpad-arm-w,34px);height:var(--dpad-arm-w,34px);margin:calc(var(--dpad-arm-w,34px)/-2) 0 0 calc(var(--dpad-arm-w,34px)/-2);background:rgba(13,17,32,.92);border:2px solid rgba(255,255,255,.4);pointer-events:none}
618
+ @media (prefers-contrast: more){ .touch-cross .dpad-arm,.dpad-center{background:#000;border-color:#fff} .dpad-arm.on{background:#fff;color:#000} }
619
+ @media (prefers-reduced-motion: reduce){ .touch-knob{transition:none} }
620
+ @media (prefers-contrast: more){ .touch-btn{background:#000;border-color:#fff} .touch-btn:active{background:#fff;color:#000} .touch-stick{background:#000;border-color:#fff} .touch-knob{background:#fff} }
621
+
622
+ @media (max-width:560px){.title{font-size:1.1rem}.hud{gap:1rem}}