@the-inclusionist/engine 9.0.0 → 11.0.0
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.
- package/README.md +120 -78
- package/app/css/style.css +761 -377
- package/app/public/vendor/fonts/playwrite-cu.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-es-deco.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-es.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-gb-j.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-gb-s.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-pe.woff2 +0 -0
- package/app/public/vendor/fonts/playwrite-pt.woff2 +0 -0
- package/app/public/vendor/fonts-licences/NOTICE.txt +166 -0
- package/app/public/vendor/fonts-licences/OFL-1.1.txt +86 -0
- package/app/public/vendor/fonts.css +36 -84
- package/dist-pkg/boot/create-game.d.ts +499 -219
- package/dist-pkg/boot/create-game.js +3944 -562
- package/dist-pkg/core/a11y-sr.d.ts +25 -3
- package/dist-pkg/core/a11y-sr.js +38 -19
- package/dist-pkg/core/accessible-label.d.ts +17 -0
- package/dist-pkg/core/accessible-label.js +37 -0
- package/dist-pkg/core/accommodation-subjects.d.ts +26 -0
- package/dist-pkg/core/accommodation-subjects.js +32 -0
- package/dist-pkg/core/accommodations.d.ts +125 -0
- package/dist-pkg/core/accommodations.js +195 -0
- package/dist-pkg/core/actions.d.ts +108 -55
- package/dist-pkg/core/actions.js +122 -71
- package/dist-pkg/core/calm-mode.d.ts +31 -0
- package/dist-pkg/core/calm-mode.js +47 -0
- package/dist-pkg/core/camera-cycle.d.ts +15 -0
- package/dist-pkg/core/camera-cycle.js +7 -0
- package/dist-pkg/core/caption-duration.d.ts +15 -0
- package/dist-pkg/core/caption-duration.js +19 -0
- package/dist-pkg/core/cartridge-problems.d.ts +34 -0
- package/dist-pkg/core/cartridge-problems.js +53 -0
- package/dist-pkg/core/constants.d.ts +0 -54
- package/dist-pkg/core/constants.js +9 -76
- package/dist-pkg/core/contract.d.ts +142 -164
- package/dist-pkg/core/contract.js +222 -208
- package/dist-pkg/core/dom-query.d.ts +4 -4
- package/dist-pkg/core/dom-query.js +12 -20
- package/dist-pkg/core/entity.d.ts +62 -77
- package/dist-pkg/core/entity.js +18 -32
- package/dist-pkg/core/escape-html.d.ts +9 -10
- package/dist-pkg/core/escape-html.js +16 -20
- package/dist-pkg/core/flash-threshold.d.ts +17 -0
- package/dist-pkg/core/flash-threshold.js +113 -0
- package/dist-pkg/core/game-speed.d.ts +11 -0
- package/dist-pkg/core/game-speed.js +16 -0
- package/dist-pkg/core/genres.d.ts +20 -0
- package/dist-pkg/core/genres.js +44 -0
- package/dist-pkg/core/i18n.d.ts +71 -60
- package/dist-pkg/core/i18n.js +186 -200
- package/dist-pkg/core/loop.d.ts +17 -6
- package/dist-pkg/core/loop.js +26 -26
- package/dist-pkg/core/pause-icon-catalogue.d.ts +15 -0
- package/dist-pkg/core/pause-icon-catalogue.js +54 -0
- package/dist-pkg/core/ring.d.ts +17 -0
- package/dist-pkg/core/ring.js +29 -0
- package/dist-pkg/core/rng.d.ts +7 -12
- package/dist-pkg/core/rng.js +23 -53
- package/dist-pkg/core/route.d.ts +26 -26
- package/dist-pkg/core/route.js +126 -121
- package/dist-pkg/core/scenes.d.ts +37 -40
- package/dist-pkg/core/scenes.js +41 -43
- package/dist-pkg/core/screens.d.ts +4 -4
- package/dist-pkg/core/screens.js +11 -16
- package/dist-pkg/core/session-clock.d.ts +23 -0
- package/dist-pkg/core/session-clock.js +37 -0
- package/dist-pkg/core/setting-defaults.d.ts +61 -0
- package/dist-pkg/core/setting-defaults.js +66 -0
- package/dist-pkg/core/speech-rate.d.ts +29 -0
- package/dist-pkg/core/speech-rate.js +47 -0
- package/dist-pkg/core/state.d.ts +118 -118
- package/dist-pkg/core/state.js +322 -297
- package/dist-pkg/core/visual-cycles.d.ts +30 -0
- package/dist-pkg/core/visual-cycles.js +52 -0
- package/dist-pkg/core/visual-state.d.ts +13 -0
- package/dist-pkg/core/visual-state.js +6 -0
- package/dist-pkg/educational/activities-registry.d.ts +1 -1
- package/dist-pkg/educational/activities-registry.js +9 -8
- package/dist-pkg/educational/adaptive-engine.d.ts +11 -11
- package/dist-pkg/educational/adaptive-engine.js +17 -17
- package/dist-pkg/educational/segment-bar.d.ts +12 -12
- package/dist-pkg/educational/segment-bar.js +9 -9
- package/dist-pkg/i18n/en.js +324 -223
- package/dist-pkg/i18n/es.js +323 -223
- package/dist-pkg/i18n/pt.js +348 -226
- package/dist-pkg/input/default-bindings.d.ts +37 -38
- package/dist-pkg/input/default-bindings.js +127 -144
- package/dist-pkg/input/devices.d.ts +7 -7
- package/dist-pkg/input/devices.js +17 -16
- package/dist-pkg/input/edges.d.ts +23 -32
- package/dist-pkg/input/edges.js +23 -28
- package/dist-pkg/input/empathy-filter.d.ts +22 -0
- package/dist-pkg/input/empathy-filter.js +34 -0
- package/dist-pkg/input/face-map.d.ts +96 -0
- package/dist-pkg/input/face-map.js +255 -0
- package/dist-pkg/input/face-signals.d.ts +40 -0
- package/dist-pkg/input/face-signals.js +48 -0
- package/dist-pkg/input/gamepad.d.ts +195 -145
- package/dist-pkg/input/gamepad.js +383 -556
- package/dist-pkg/input/gaze-cycle.d.ts +64 -0
- package/dist-pkg/input/gaze-cycle.js +133 -0
- package/dist-pkg/input/gaze-relative.d.ts +68 -0
- package/dist-pkg/input/gaze-relative.js +142 -0
- package/dist-pkg/input/hand-map.d.ts +28 -0
- package/dist-pkg/input/hand-map.js +142 -0
- package/dist-pkg/input/input-cooldown.d.ts +17 -0
- package/dist-pkg/input/input-cooldown.js +39 -0
- package/dist-pkg/input/key-default.d.ts +30 -0
- package/dist-pkg/input/key-default.js +60 -0
- package/dist-pkg/input/keyboard-runtime.d.ts +15 -21
- package/dist-pkg/input/keyboard-runtime.js +23 -27
- package/dist-pkg/input/keyboard.d.ts +68 -63
- package/dist-pkg/input/keyboard.js +83 -105
- package/dist-pkg/input/keydown.d.ts +123 -137
- package/dist-pkg/input/keydown.js +284 -289
- package/dist-pkg/input/latch-edge.d.ts +33 -21
- package/dist-pkg/input/latch-edge.js +34 -35
- package/dist-pkg/input/latch-scope.d.ts +51 -50
- package/dist-pkg/input/latch-scope.js +61 -82
- package/dist-pkg/input/latch-store.d.ts +26 -30
- package/dist-pkg/input/latch-store.js +42 -50
- package/dist-pkg/input/latch-sync.d.ts +29 -29
- package/dist-pkg/input/latch-sync.js +25 -25
- package/dist-pkg/input/latch.d.ts +14 -17
- package/dist-pkg/input/latch.js +29 -37
- package/dist-pkg/input/pad-defaults.d.ts +13 -16
- package/dist-pkg/input/pad-defaults.js +21 -28
- package/dist-pkg/input/pad-reading.d.ts +76 -0
- package/dist-pkg/input/pad-reading.js +169 -0
- package/dist-pkg/input/pad-wizard.d.ts +99 -0
- package/dist-pkg/input/pad-wizard.js +256 -0
- package/dist-pkg/input/pointer-space.d.ts +19 -20
- package/dist-pkg/input/pointer-space.js +27 -32
- package/dist-pkg/input/pointer.d.ts +39 -40
- package/dist-pkg/input/pointer.js +42 -45
- package/dist-pkg/input/state.d.ts +101 -89
- package/dist-pkg/input/state.js +54 -134
- package/dist-pkg/input/switch-scan.d.ts +59 -0
- package/dist-pkg/input/switch-scan.js +67 -0
- package/dist-pkg/input/synthetic-source.d.ts +42 -0
- package/dist-pkg/input/synthetic-source.js +57 -0
- package/dist-pkg/input/touch-bindings.d.ts +127 -116
- package/dist-pkg/input/touch-bindings.js +158 -173
- package/dist-pkg/input/touch.d.ts +147 -72
- package/dist-pkg/input/touch.js +362 -126
- package/dist-pkg/input/transport-in-use.d.ts +107 -0
- package/dist-pkg/input/transport-in-use.js +130 -0
- package/dist-pkg/input/transports.d.ts +90 -117
- package/dist-pkg/input/transports.js +104 -106
- package/dist-pkg/input/virtual-controller.d.ts +82 -0
- package/dist-pkg/input/virtual-controller.js +69 -0
- package/dist-pkg/input/vocabulary-migration.d.ts +50 -49
- package/dist-pkg/input/vocabulary-migration.js +80 -79
- package/dist-pkg/input/voice-map.d.ts +59 -0
- package/dist-pkg/input/voice-map.js +123 -0
- package/dist-pkg/platform/audio-ambient.d.ts +13 -2
- package/dist-pkg/platform/audio-ambient.js +14 -14
- package/dist-pkg/platform/audio-earcons.d.ts +20 -19
- package/dist-pkg/platform/audio-earcons.js +51 -42
- package/dist-pkg/platform/audio-jingles.js +9 -10
- package/dist-pkg/platform/audio-mixer.d.ts +14 -2
- package/dist-pkg/platform/audio-mixer.js +31 -39
- package/dist-pkg/platform/audio-sonar.d.ts +49 -152
- package/dist-pkg/platform/audio-sonar.js +124 -302
- package/dist-pkg/platform/audio.d.ts +62 -19
- package/dist-pkg/platform/audio.js +193 -168
- package/dist-pkg/platform/choose-by-voice.d.ts +70 -0
- package/dist-pkg/platform/choose-by-voice.js +157 -0
- package/dist-pkg/platform/flash-sampler.d.ts +19 -0
- package/dist-pkg/platform/flash-sampler.js +79 -0
- package/dist-pkg/platform/font-library.d.ts +77 -0
- package/dist-pkg/platform/font-library.js +98 -0
- package/dist-pkg/platform/font-library.json +721 -0
- package/dist-pkg/platform/heavy-catalogue.d.ts +77 -0
- package/dist-pkg/platform/heavy-catalogue.js +256 -0
- package/dist-pkg/platform/heavy-mirror.d.ts +20 -0
- package/dist-pkg/platform/heavy-mirror.js +73 -0
- package/dist-pkg/platform/heavy.d.ts +136 -0
- package/dist-pkg/platform/heavy.js +324 -0
- package/dist-pkg/platform/interruptible-speech.d.ts +23 -16
- package/dist-pkg/platform/interruptible-speech.js +77 -54
- package/dist-pkg/platform/kokoro-port.d.ts +27 -0
- package/dist-pkg/platform/kokoro-port.js +50 -0
- package/dist-pkg/platform/kokoro-runtime.d.ts +20 -0
- package/dist-pkg/platform/kokoro-runtime.js +52 -0
- package/dist-pkg/platform/kokoro.d.ts +70 -0
- package/dist-pkg/platform/kokoro.js +133 -0
- package/dist-pkg/platform/listener-scope.d.ts +12 -0
- package/dist-pkg/platform/listener-scope.js +90 -0
- package/dist-pkg/platform/locale-host.d.ts +14 -0
- package/dist-pkg/platform/locale-host.js +45 -0
- package/dist-pkg/platform/microphone.d.ts +51 -0
- package/dist-pkg/platform/microphone.js +118 -0
- package/dist-pkg/platform/onnx-runtime.d.ts +34 -0
- package/dist-pkg/platform/onnx-runtime.js +42 -0
- package/dist-pkg/platform/reading-in-worker.d.ts +64 -0
- package/dist-pkg/platform/reading-in-worker.js +125 -0
- package/dist-pkg/platform/reading-model.d.ts +94 -0
- package/dist-pkg/platform/reading-model.js +272 -0
- package/dist-pkg/platform/reading-runtime.d.ts +24 -0
- package/dist-pkg/platform/reading-runtime.js +164 -0
- package/dist-pkg/platform/reading-worker.d.ts +41 -0
- package/dist-pkg/platform/reading-worker.js +58 -0
- package/dist-pkg/platform/reading.d.ts +80 -0
- package/dist-pkg/platform/reading.js +136 -0
- package/dist-pkg/platform/speech-recognition.d.ts +60 -0
- package/dist-pkg/platform/speech-recognition.js +104 -0
- package/dist-pkg/platform/speech.d.ts +23 -1
- package/dist-pkg/platform/speech.js +29 -18
- package/dist-pkg/platform/storage-keys.d.ts +96 -0
- package/dist-pkg/platform/storage-keys.js +106 -0
- package/dist-pkg/platform/storage.d.ts +46 -111
- package/dist-pkg/platform/storage.js +104 -169
- package/dist-pkg/platform/tts.d.ts +85 -39
- package/dist-pkg/platform/tts.js +447 -127
- package/dist-pkg/platform/vision-loop.d.ts +23 -0
- package/dist-pkg/platform/vision-loop.js +56 -0
- package/dist-pkg/platform/vision.d.ts +132 -0
- package/dist-pkg/platform/vision.js +117 -0
- package/dist-pkg/platform/voice-listener.d.ts +40 -0
- package/dist-pkg/platform/voice-listener.js +88 -0
- package/dist-pkg/platform/voice-plan.d.ts +15 -98
- package/dist-pkg/platform/voice-plan.js +56 -143
- package/dist-pkg/platform/vosk-runtime.d.ts +96 -0
- package/dist-pkg/platform/vosk-runtime.js +151 -0
- package/dist-pkg/platform/vosk-vocabulary.d.ts +6 -0
- package/dist-pkg/platform/vosk-vocabulary.js +178 -0
- package/dist-pkg/render/canvas.d.ts +13 -7
- package/dist-pkg/render/canvas.js +13 -13
- package/dist-pkg/render/crt.d.ts +48 -12
- package/dist-pkg/render/crt.js +78 -80
- package/dist-pkg/render/cvd-matrices.d.ts +22 -18
- package/dist-pkg/render/cvd-matrices.js +37 -38
- package/dist-pkg/render/hc-role-data.d.ts +7 -7
- package/dist-pkg/render/hc-role-data.js +14 -14
- package/dist-pkg/render/high-contrast.d.ts +67 -56
- package/dist-pkg/render/high-contrast.js +226 -217
- package/dist-pkg/render/low-vision-drawing.d.ts +5 -0
- package/dist-pkg/render/low-vision-drawing.js +46 -0
- package/dist-pkg/render/lq-filter.d.ts +32 -20
- package/dist-pkg/render/lq-filter.js +59 -65
- package/dist-pkg/render/port.d.ts +86 -88
- package/dist-pkg/render/port.js +17 -23
- package/dist-pkg/render/screen-pipeline.d.ts +42 -42
- package/dist-pkg/render/screen-pipeline.js +80 -87
- package/dist-pkg/render/sprite-fx.d.ts +2 -1
- package/dist-pkg/render/sprite-fx.js +16 -14
- package/dist-pkg/render/viewports.d.ts +20 -15
- package/dist-pkg/render/viewports.js +83 -114
- package/dist-pkg/render/viz-axes-labels.d.ts +47 -0
- package/dist-pkg/render/viz-axes-labels.js +88 -0
- package/dist-pkg/render/viz-axes.d.ts +89 -86
- package/dist-pkg/render/viz-axes.js +128 -116
- package/dist-pkg/render/viz-modes.d.ts +28 -29
- package/dist-pkg/render/viz-modes.js +40 -45
- package/dist-pkg/render/viz-refusal.d.ts +32 -0
- package/dist-pkg/render/viz-refusal.js +57 -0
- package/dist-pkg/render/viz-setters.d.ts +104 -66
- package/dist-pkg/render/viz-setters.js +182 -191
- package/dist-pkg/ui/aac-sets.d.ts +45 -0
- package/dist-pkg/ui/aac-sets.js +41 -0
- package/dist-pkg/ui/audio-choices.d.ts +68 -0
- package/dist-pkg/ui/audio-choices.js +98 -0
- package/dist-pkg/ui/audio-rows-that-apply.d.ts +12 -0
- package/dist-pkg/ui/audio-rows-that-apply.js +43 -0
- package/dist-pkg/ui/camera-control.d.ts +53 -0
- package/dist-pkg/ui/camera-control.js +48 -0
- package/dist-pkg/ui/changed-mark.d.ts +9 -8
- package/dist-pkg/ui/changed-mark.js +22 -53
- package/dist-pkg/ui/control-choices.d.ts +37 -0
- package/dist-pkg/ui/control-choices.js +84 -0
- package/dist-pkg/ui/debug-panel.d.ts +59 -43
- package/dist-pkg/ui/debug-panel.js +68 -69
- package/dist-pkg/ui/declared-words.d.ts +21 -0
- package/dist-pkg/ui/declared-words.js +66 -0
- package/dist-pkg/ui/dom.d.ts +10 -14
- package/dist-pkg/ui/dom.js +10 -47
- package/dist-pkg/ui/drawing-problems.d.ts +25 -0
- package/dist-pkg/ui/drawing-problems.js +94 -0
- package/dist-pkg/ui/eye-control.d.ts +36 -0
- package/dist-pkg/ui/eye-control.js +178 -0
- package/dist-pkg/ui/face-control.d.ts +25 -0
- package/dist-pkg/ui/face-control.js +183 -0
- package/dist-pkg/ui/focus-trap.d.ts +39 -49
- package/dist-pkg/ui/focus-trap.js +56 -65
- package/dist-pkg/ui/fonts.d.ts +157 -68
- package/dist-pkg/ui/fonts.js +305 -115
- package/dist-pkg/ui/footer-scroll-driver.d.ts +14 -0
- package/dist-pkg/ui/footer-scroll-driver.js +201 -0
- package/dist-pkg/ui/footer-scroll.d.ts +38 -0
- package/dist-pkg/ui/footer-scroll.js +55 -0
- package/dist-pkg/ui/game-options.d.ts +47 -0
- package/dist-pkg/ui/game-options.js +158 -0
- package/dist-pkg/ui/gaze-overlay.d.ts +64 -0
- package/dist-pkg/ui/gaze-overlay.js +165 -0
- package/dist-pkg/ui/hand-control.d.ts +25 -0
- package/dist-pkg/ui/hand-control.js +143 -0
- package/dist-pkg/ui/help-panel.d.ts +87 -0
- package/dist-pkg/ui/help-panel.js +242 -0
- package/dist-pkg/ui/hud-bands.d.ts +62 -0
- package/dist-pkg/ui/hud-bands.js +146 -0
- package/dist-pkg/ui/hud-row.d.ts +34 -0
- package/dist-pkg/ui/hud-row.js +80 -0
- package/dist-pkg/ui/hud.d.ts +73 -73
- package/dist-pkg/ui/hud.js +94 -98
- package/dist-pkg/ui/item-announcement.d.ts +13 -12
- package/dist-pkg/ui/item-announcement.js +25 -26
- package/dist-pkg/ui/layout.d.ts +103 -28
- package/dist-pkg/ui/layout.js +117 -99
- package/dist-pkg/ui/libras-avatar-clip.d.ts +30 -0
- package/dist-pkg/ui/libras-avatar-clip.js +86 -0
- package/dist-pkg/ui/libras-avatar-load.d.ts +52 -0
- package/dist-pkg/ui/libras-avatar-load.js +134 -0
- package/dist-pkg/ui/libras-avatar-plan.d.ts +102 -0
- package/dist-pkg/ui/libras-avatar-plan.js +211 -0
- package/dist-pkg/ui/libras-avatar-player.d.ts +28 -0
- package/dist-pkg/ui/libras-avatar-player.js +226 -0
- package/dist-pkg/ui/libras-avatar-stage.d.ts +37 -0
- package/dist-pkg/ui/libras-avatar-stage.js +208 -0
- package/dist-pkg/ui/libras-glosses.d.ts +28 -0
- package/dist-pkg/ui/libras-glosses.js +161 -0
- package/dist-pkg/ui/locale-flags.d.ts +16 -0
- package/dist-pkg/ui/locale-flags.js +30 -0
- package/dist-pkg/ui/loop-crash.d.ts +17 -17
- package/dist-pkg/ui/loop-crash.js +33 -62
- package/dist-pkg/ui/menu-intent.d.ts +61 -0
- package/dist-pkg/ui/menu-intent.js +88 -0
- package/dist-pkg/ui/menu-items.d.ts +20 -0
- package/dist-pkg/ui/menu-items.js +51 -0
- package/dist-pkg/ui/menu-nav.d.ts +98 -92
- package/dist-pkg/ui/menu-nav.js +398 -286
- package/dist-pkg/ui/mobility-choices.d.ts +26 -0
- package/dist-pkg/ui/mobility-choices.js +41 -0
- package/dist-pkg/ui/motion-choices.d.ts +31 -0
- package/dist-pkg/ui/motion-choices.js +68 -0
- package/dist-pkg/ui/motion-scene.d.ts +24 -19
- package/dist-pkg/ui/motion-scene.js +29 -61
- package/dist-pkg/ui/mount-panel.d.ts +81 -0
- package/dist-pkg/ui/mount-panel.js +61 -0
- package/dist-pkg/ui/panel-shell.d.ts +65 -31
- package/dist-pkg/ui/panel-shell.js +92 -66
- package/dist-pkg/ui/panel-widgets.d.ts +150 -0
- package/dist-pkg/ui/panel-widgets.js +294 -0
- package/dist-pkg/ui/pause-buttons.d.ts +35 -0
- package/dist-pkg/ui/pause-buttons.js +66 -0
- package/dist-pkg/ui/pause-icons.d.ts +261 -347
- package/dist-pkg/ui/pause-icons.js +671 -601
- package/dist-pkg/ui/pause-markup.d.ts +86 -0
- package/dist-pkg/ui/pause-markup.js +125 -0
- package/dist-pkg/ui/reach-notice.d.ts +22 -23
- package/dist-pkg/ui/reach-notice.js +55 -58
- package/dist-pkg/ui/scan-overlay.d.ts +23 -0
- package/dist-pkg/ui/scan-overlay.js +50 -0
- package/dist-pkg/ui/screen-text.d.ts +25 -0
- package/dist-pkg/ui/screen-text.js +152 -0
- package/dist-pkg/ui/session-clock.d.ts +32 -0
- package/dist-pkg/ui/session-clock.js +67 -0
- package/dist-pkg/ui/settings-aac.d.ts +70 -0
- package/dist-pkg/ui/settings-aac.js +208 -0
- package/dist-pkg/ui/settings-audio.d.ts +97 -114
- package/dist-pkg/ui/settings-audio.js +493 -425
- package/dist-pkg/ui/settings-controls.d.ts +53 -96
- package/dist-pkg/ui/settings-controls.js +143 -215
- package/dist-pkg/ui/settings-empathy.d.ts +39 -30
- package/dist-pkg/ui/settings-empathy.js +53 -60
- package/dist-pkg/ui/settings-mobility.d.ts +192 -0
- package/dist-pkg/ui/settings-mobility.js +332 -0
- package/dist-pkg/ui/settings-motion.d.ts +91 -70
- package/dist-pkg/ui/settings-motion.js +287 -198
- package/dist-pkg/ui/settings-panel.d.ts +58 -45
- package/dist-pkg/ui/settings-panel.js +189 -94
- package/dist-pkg/ui/settings-typo.d.ts +29 -78
- package/dist-pkg/ui/settings-typo.js +158 -129
- package/dist-pkg/ui/settings-visual.d.ts +29 -87
- package/dist-pkg/ui/settings-visual.js +240 -193
- package/dist-pkg/ui/shell.d.ts +112 -135
- package/dist-pkg/ui/shell.js +148 -217
- package/dist-pkg/ui/simulation-list.d.ts +27 -0
- package/dist-pkg/ui/simulation-list.js +76 -0
- package/dist-pkg/ui/simulation-over-the-world.d.ts +23 -0
- package/dist-pkg/ui/simulation-over-the-world.js +131 -0
- package/dist-pkg/ui/simulation-refusal.d.ts +5 -31
- package/dist-pkg/ui/simulation-refusal.js +8 -56
- package/dist-pkg/ui/switchable-control.d.ts +11 -0
- package/dist-pkg/ui/switchable-control.js +19 -0
- package/dist-pkg/ui/title.d.ts +3 -12
- package/dist-pkg/ui/title.js +49 -18
- package/dist-pkg/ui/top-band.d.ts +26 -0
- package/dist-pkg/ui/top-band.js +96 -0
- package/dist-pkg/ui/typo-choices.d.ts +71 -0
- package/dist-pkg/ui/typo-choices.js +85 -0
- package/dist-pkg/ui/visual-axes-panel.d.ts +11 -47
- package/dist-pkg/ui/visual-axes-panel.js +12 -94
- package/dist-pkg/ui/visual-choices.d.ts +77 -0
- package/dist-pkg/ui/visual-choices.js +107 -0
- package/dist-pkg/ui/vlibras.d.ts +77 -10
- package/dist-pkg/ui/vlibras.js +130 -81
- package/dist-pkg/ui/voice-control.d.ts +68 -0
- package/dist-pkg/ui/voice-control.js +276 -0
- package/dist-pkg/ui/voice-settings.d.ts +95 -0
- package/dist-pkg/ui/voice-settings.js +352 -0
- package/dist-pkg/ui/where-the-child-is.d.ts +31 -0
- package/dist-pkg/ui/where-the-child-is.js +70 -0
- package/docs/CREDITS.md +300 -44
- package/docs/LICENSES.md +189 -145
- package/package.json +51 -27
- package/scripts/check-cartridge.mjs +78 -0
- package/scripts/game-build.d.mts +25 -0
- package/scripts/game-build.mjs +144 -0
- package/scripts/heavy-into-the-delivery.mjs +388 -0
- package/scripts/libras-avatar.json +680 -0
- package/scripts/libras-avatar.mjs +246 -0
- package/scripts/libras-glosses/gloss.py +95 -0
- package/scripts/libras-glosses/pyproject.toml +32 -0
- package/scripts/libras-glosses/uv.lock +781 -0
- package/scripts/libras-glosses.mjs +351 -0
- package/scripts/licences/Apache-2.0.txt +176 -0
- package/scripts/licences/GPL-3.0.txt +674 -0
- package/scripts/licences/MIT.txt +17 -0
- package/scripts/licences/OFL-1.1.txt +86 -0
- package/scripts/licences/UFL-1.0.txt +96 -0
- package/scripts/licences/fonts.mjs +389 -0
- package/scripts/licences/third-party.mjs +301 -0
- package/scripts/licences/vosk-browser.NOTICE.txt +268 -0
- package/app/public/vendor/fonts/comicneue-400.woff2 +0 -0
- package/app/public/vendor/fonts/comicneue-700.woff2 +0 -0
- package/app/public/vendor/fonts/fondamento-400-ext.woff2 +0 -0
- package/app/public/vendor/fonts/fondamento-400.woff2 +0 -0
- package/app/public/vendor/fonts/inter-400.woff2 +0 -0
- package/app/public/vendor/fonts/inter-700.woff2 +0 -0
- package/app/public/vendor/fonts/lato-400.woff2 +0 -0
- package/app/public/vendor/fonts/lato-700.woff2 +0 -0
- package/app/public/vendor/fonts/literata-400.woff2 +0 -0
- package/app/public/vendor/fonts/literata-700.woff2 +0 -0
- package/app/public/vendor/fonts/newsreader-400.woff2 +0 -0
- package/app/public/vendor/fonts/newsreader-700.woff2 +0 -0
- package/app/public/vendor/fonts/opendyslexic-400.woff2 +0 -0
- package/app/public/vendor/fonts/opensans-400.woff2 +0 -0
- package/app/public/vendor/fonts/opensans-700.woff2 +0 -0
- package/app/public/vendor/fonts/pinyon-400.woff2 +0 -0
- package/app/public/vendor/fonts/pressstart-400.woff2 +0 -0
- package/app/public/vendor/fonts/quattro-400.woff2 +0 -0
- package/app/public/vendor/fonts/quattro-700.woff2 +0 -0
- package/app/public/vendor/fonts/sourcesans-400.woff2 +0 -0
- package/app/public/vendor/fonts/sourcesans-700.woff2 +0 -0
- package/app/public/vendor/fonts/sourceserif-400.woff2 +0 -0
- package/app/public/vendor/fonts/sourceserif-700.woff2 +0 -0
- package/app/public/vendor/fonts/ufmag-400.woff2 +0 -0
- package/dist-pkg/core/anel.d.ts +0 -17
- package/dist-pkg/core/anel.js +0 -31
- package/dist-pkg/core/collision.d.ts +0 -21
- package/dist-pkg/core/collision.js +0 -62
- package/dist-pkg/core/layers.d.ts +0 -63
- package/dist-pkg/core/layers.js +0 -95
- package/dist-pkg/core/letter-grid.d.ts +0 -55
- package/dist-pkg/core/letter-grid.js +0 -116
- package/dist-pkg/core/password.d.ts +0 -34
- package/dist-pkg/core/password.js +0 -205
- package/dist-pkg/core/rotulo-acessivel.d.ts +0 -17
- package/dist-pkg/core/rotulo-acessivel.js +0 -41
- package/dist-pkg/core/run-state.d.ts +0 -115
- package/dist-pkg/core/run-state.js +0 -40
- package/dist-pkg/core/tiles.d.ts +0 -12
- package/dist-pkg/core/tiles.js +0 -61
- package/dist-pkg/core/world.d.ts +0 -4
- package/dist-pkg/core/world.js +0 -58
- package/dist-pkg/input/origem-sintetica.d.ts +0 -44
- package/dist-pkg/input/origem-sintetica.js +0 -60
- package/dist-pkg/input/transporte-em-uso.d.ts +0 -101
- package/dist-pkg/input/transporte-em-uso.js +0 -130
- package/dist-pkg/platform/audio-nav.d.ts +0 -51
- package/dist-pkg/platform/audio-nav.js +0 -99
- package/dist-pkg/platform/guide-intensity.d.ts +0 -40
- package/dist-pkg/platform/guide-intensity.js +0 -73
- package/dist-pkg/platform/pesados-catalogo.d.ts +0 -14
- package/dist-pkg/platform/pesados-catalogo.js +0 -177
- package/dist-pkg/platform/pesados.d.ts +0 -31
- package/dist-pkg/platform/pesados.js +0 -75
- package/dist-pkg/platform/vozes-prontas.d.ts +0 -28
- package/dist-pkg/platform/vozes-prontas.js +0 -61
- package/dist-pkg/render/camera.d.ts +0 -58
- package/dist-pkg/render/camera.js +0 -105
- package/dist-pkg/render/cenario-data.d.ts +0 -127
- package/dist-pkg/render/cenario-data.js +0 -145
- package/dist-pkg/render/city-tex.d.ts +0 -63
- package/dist-pkg/render/city-tex.js +0 -228
- package/dist-pkg/render/city-tiles.d.ts +0 -11
- package/dist-pkg/render/city-tiles.js +0 -140
- package/dist-pkg/render/draw.d.ts +0 -149
- package/dist-pkg/render/draw.js +0 -231
- package/dist-pkg/render/fx.d.ts +0 -63
- package/dist-pkg/render/fx.js +0 -123
- package/dist-pkg/render/minimap.d.ts +0 -11
- package/dist-pkg/render/minimap.js +0 -94
- package/dist-pkg/render/parallax.d.ts +0 -85
- package/dist-pkg/render/parallax.js +0 -158
- package/dist-pkg/render/player-anim.d.ts +0 -71
- package/dist-pkg/render/player-anim.js +0 -152
- package/dist-pkg/render/recycling-tex.d.ts +0 -48
- package/dist-pkg/render/recycling-tex.js +0 -164
- package/dist-pkg/render/scene-city.d.ts +0 -77
- package/dist-pkg/render/scene-city.js +0 -181
- package/dist-pkg/render/scene-parallax.d.ts +0 -72
- package/dist-pkg/render/scene-parallax.js +0 -426
- package/dist-pkg/render/scene-sky.d.ts +0 -126
- package/dist-pkg/render/scene-sky.js +0 -295
- package/dist-pkg/render/set-cenario.d.ts +0 -54
- package/dist-pkg/render/set-cenario.js +0 -103
- package/dist-pkg/render/textures.d.ts +0 -75
- package/dist-pkg/render/textures.js +0 -240
- package/dist-pkg/render/title-scene.d.ts +0 -46
- package/dist-pkg/render/title-scene.js +0 -75
- package/dist-pkg/render/weather.d.ts +0 -96
- package/dist-pkg/render/weather.js +0 -171
- package/dist-pkg/render/wheelchair-sprites.d.ts +0 -32
- package/dist-pkg/render/wheelchair-sprites.js +0 -69
- package/dist-pkg/render/world-tex.d.ts +0 -18
- package/dist-pkg/render/world-tex.js +0 -87
- package/dist-pkg/ui/activities-menu.d.ts +0 -258
- package/dist-pkg/ui/activities-menu.js +0 -648
- package/dist-pkg/ui/caa-sets.d.ts +0 -39
- package/dist-pkg/ui/caa-sets.js +0 -65
- package/dist-pkg/ui/latch-refusal.d.ts +0 -32
- package/dist-pkg/ui/latch-refusal.js +0 -60
- package/dist-pkg/ui/map-hub.d.ts +0 -56
- package/dist-pkg/ui/map-hub.js +0 -138
- package/dist-pkg/ui/settings-caa.d.ts +0 -45
- package/dist-pkg/ui/settings-caa.js +0 -137
- package/dist-pkg/ui/settings-motor.d.ts +0 -175
- package/dist-pkg/ui/settings-motor.js +0 -342
- package/dist-pkg/ui/webcam.d.ts +0 -7
- package/dist-pkg/ui/webcam.js +0 -93
package/app/css/style.css
CHANGED
|
@@ -1,47 +1,60 @@
|
|
|
1
1
|
/* SPDX-License-Identifier: AGPL-3.0-or-later */
|
|
2
2
|
:root{
|
|
3
|
+
/* The engine stylesheet's sentinel: `createGame` reads it to say in `problems` when a page did not link this file. */
|
|
4
|
+
--incl-engine-stylesheet:1;
|
|
3
5
|
--bg:#0b1020; --panel:#1b2440; --ink:#f5f8ff; --ink-soft:#cdd6f2;
|
|
4
6
|
--accent:#ffd23f; --accent-ink:#1a1400; --focus:#ffd23f; --good:#34e29b;
|
|
5
|
-
--changed:#fff; /* ADR-0029:
|
|
6
|
-
/*
|
|
7
|
-
|
|
7
|
+
--changed:#fff; /* ADR-0029: the frame of a setting that left its default */
|
|
8
|
+
/* High contrast in the DOM (issue #83): the panel veil without transparency, and the button background. They live here,
|
|
9
|
+
beside the other tokens, so the contrast test can read them from the CSS instead of copying them. */
|
|
8
10
|
--bg-solid:#04070f; --panel-btn:#1a2740;
|
|
9
11
|
--maxw:1000px; --tap:44px;
|
|
10
|
-
--ui-fs:16px; /*
|
|
11
|
-
/* =====
|
|
12
|
-
/* 'Noto Color Emoji'
|
|
12
|
+
--ui-fs:16px; /* the UI's base font size (<p>): 16px = the MINIMUM at 640×360; `ui/layout` raises it as the canvas grows */
|
|
13
|
+
/* ===== The engine's typography ===== */
|
|
14
|
+
/* 'Noto Color Emoji' at the end of the stack → consistent emoji (native on Chromebook/Android; the system face elsewhere) */
|
|
13
15
|
--font-fallback:system-ui,"Segoe UI",Roboto,Arial,sans-serif,'Noto Color Emoji';
|
|
14
|
-
--font:'Atkinson Hyperlegible',var(--font-fallback); /*
|
|
15
|
-
--font-math:'Atkinson Hyperlegible Mono',ui-monospace,"Cascadia Mono",Consolas,monospace; /*
|
|
16
|
-
/*
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
--font:'Atkinson Hyperlegible',var(--font-fallback); /* DEFAULT */
|
|
17
|
+
--font-math:'Atkinson Hyperlegible Mono',ui-monospace,"Cascadia Mono",Consolas,monospace; /* mathematics */
|
|
18
|
+
/* ===== SPACING: THE DEFAULT ROSE FROM THE WCAG FLOOR TO THE BDA'S (ADR-0149 §2) =====
|
|
19
|
+
It was 0.12em/0.16em — the floor of WCAG 2.2 §1.4.12 —, and it is now 0.18em/0.63em, the British Dyslexia
|
|
20
|
+
Association's recommendation: word ≥ 3.5× letter. The Dev's decision on 2026-09-12: «somente fontes
|
|
21
|
+
cursivas não precisam seguir; ao usar todas as outras fontes, é obrigatório seguir».
|
|
22
|
+
|
|
23
|
+
⚠️ THIS USED TO BE THE EXCEPTION OF ONE FACE. The BDA numbers lived only in `[data-fonte="dislexia"]` (Lexend),
|
|
24
|
+
and the DEFAULT face — Atkinson, which every game draws — sat on the WCAG floor. It was not «no spacing at all»,
|
|
25
|
+
as a first version of the record said; it was the lower of the two floors, applied because of a LABEL instead of
|
|
26
|
+
because of a rule about faces.
|
|
27
|
+
|
|
28
|
+
🔴 AND THE REACH OF THIS IS EVERY GAME: changing the default face's spacing changes the metrics of every screen at
|
|
29
|
+
once. It is the right rule, and it is a layout change in every game that uses the engine. */
|
|
30
|
+
--ls:0.18em; /* letter-spacing — BDA */
|
|
31
|
+
--ws:0.63em; /* word-spacing — BDA (≥ 3.5× the letter) */
|
|
19
32
|
--lh:1.5; /* line-height */
|
|
20
33
|
|
|
21
|
-
/* =====
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
⚠️
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
/* ===== THE Z-ORDER SLOTS — the engine's canonical stacking order (ADR-0020, ADR-0102) =====
|
|
35
|
+
These `--z-*` variables are where the z-order lives now. They were born as the OVERLAY band of the `Z` table in
|
|
36
|
+
`core/layers.ts`; that table was the platformer's world order and left with the tile world (ADR-0228), so the
|
|
37
|
+
slots below are the only copy. `tests/the-skip-link-outranks-the-transition.node.test.js` holds what the engine
|
|
38
|
+
needs of them: the skip link sits on its slot, above the transition and every other slot except the loop-crash
|
|
39
|
+
notice and the debug panel, and no literal `z-index` in this file reaches it.
|
|
40
|
+
|
|
41
|
+
⚠️ MOST SELECTORS STILL CARRY LITERALS, and that is not forgetfulness. Only `.skip-link`, `#incl-parou` and the
|
|
42
|
+
touch pad over an open panel read a slot. Adopting the slots everywhere inverts three orders that hold today, and
|
|
43
|
+
inverting an order is a product decision:
|
|
44
|
+
|
|
45
|
+
· `.caption` (8) is BELOW the touch pad (14). In the slots, captions (26000) are ABOVE touch controls (25000).
|
|
46
|
+
The inversion FIXES a defect — the caption serves deaf players, and a touch control covering it is the wrong
|
|
47
|
+
way round —, but it changes the screen.
|
|
48
|
+
· `.quiz` (12) is BELOW the touch pad (14); in the slots, dialogue (27000) is above.
|
|
49
|
+
· the skip link was already corrected: it sits on `--z-skip-link`, above the modals and the transition.
|
|
50
|
+
|
|
51
|
+
AND TWO THINGS HAVE NO SLOT: the two CRT pseudo-elements (`crt-vig-1::before` at 5 and `crt-scan-1::after` at
|
|
52
|
+
500). Under ADR-0020 the CRT is POST-PROCESSING, not a layer — inventing a number for it would repeat the category
|
|
53
|
+
error the record already names.
|
|
54
|
+
|
|
55
|
+
⚠️ AND CSS `z-index` IS NOT FLAT. `#game-hud` has `z-index:4` and CREATES A STACKING CONTEXT, so `.screen-pause`
|
|
56
|
+
(6) competes only inside it — the pause menu effectively lives at "4" at the root. A flat list of global values
|
|
57
|
+
cannot express that. Adopting the slots means deciding, per element, which context it lives in. */
|
|
45
58
|
--z-hud:24000;
|
|
46
59
|
--z-touch-controls:25000;
|
|
47
60
|
--z-captions:26000;
|
|
@@ -55,326 +68,362 @@
|
|
|
55
68
|
--z-loop-crash:45000;
|
|
56
69
|
--z-debug:90000;
|
|
57
70
|
}
|
|
58
|
-
/* Emoji:
|
|
59
|
-
|
|
60
|
-
@font-face{ font-family:'Noto Color Emoji'; src:local('Noto Color Emoji'); /*
|
|
61
|
-
/*
|
|
71
|
+
/* Emoji: the system's Noto Color Emoji (Chromebook/Android/Linux). No file is shipped for it, so where the system
|
|
72
|
+
lacks it the browser falls back to the OS emoji — without breaking. */
|
|
73
|
+
@font-face{ font-family:'Noto Color Emoji'; src:local('Noto Color Emoji'); /* the system face only; no download (the .woff2 never existed) */ font-display:swap; }
|
|
74
|
+
/* LITERACY mode → Andika */
|
|
62
75
|
:root[data-fonte="alfabetizacao"]{ --font:'Andika',var(--font-fallback); }
|
|
63
|
-
/*
|
|
64
|
-
|
|
65
|
-
:root[data-fonte="dislexia"]{ --font:'Lexend',var(--font-fallback);
|
|
66
|
-
/*
|
|
76
|
+
/* DYSLEXIA/ADHD mode → Lexend. 📌 The BDA spacing is not here: it is the document's default (see `:root`, ADR-0149 §2),
|
|
77
|
+
not this face's exception. The rule keeps only the FAMILY, which is all it ever had of its own. */
|
|
78
|
+
:root[data-fonte="dislexia"]{ --font:'Lexend',var(--font-fallback); }
|
|
79
|
+
/* ===== THE CURSIVE EXCEPTION (ADR-0149 §3) =====
|
|
80
|
+
🔴 Letter spacing on a JOINED face breaks it exactly at the joins that make it cursive: what is meant to help the
|
|
81
|
+
reading would destroy the thing being read. The Dev's words: «para manter os conectores».
|
|
82
|
+
It is not a concession — it is the same rule applied to a face where it inverts.
|
|
83
|
+
⚠️ `normal` and not `0`: `word-spacing:0` is still a spacing declaration; `normal` gives back the metrics the face's
|
|
84
|
+
designer chose, which is precisely what is meant to be preserved. */
|
|
85
|
+
:root[data-cursiva="1"]{ --ls:normal; --ws:normal; }
|
|
86
|
+
/* A COLOUR-VISION-SAFE PALETTE FOR THE MENUS AND THE HUD (ADR-0151) — Okabe-Ito.
|
|
87
|
+
📏 The interface's meaning-by-colour sits in two tokens: `--accent` (selection, «on», choice) and `--good` (the right
|
|
88
|
+
answer revealed). The usual pair, yellow and green, is the one protans and deutans confuse: CIE76 ΔE of 48 and 55
|
|
89
|
+
after Machado's (2009) simulation. Orange and sky blue give 101, 111 and 87 (tritan) — the largest worst case among
|
|
90
|
+
the Okabe-Ito pairs measured — and every contrast stays ≥ 4.5:1 (the tightest is `--good` on the selected button,
|
|
91
|
+
4.88:1). The gate is `tests/okabe-ito-palette.node.test.js`. */
|
|
92
|
+
:root[data-paleta="okabe-ito"]{ --accent:#E69F00; --focus:#E69F00; --good:#56B4E9; }
|
|
93
|
+
/* Typography: any face of the catalogue (its own menu in the pause); --font-custom brings the stack with its fallback */
|
|
67
94
|
:root[data-fonte="custom"]{ --font:var(--font-custom,'Atkinson Hyperlegible'),var(--font-fallback); }
|
|
68
|
-
/*
|
|
95
|
+
/* Typography menu: a white preview box (pangram), dark text */
|
|
69
96
|
.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}
|
|
70
|
-
/*
|
|
71
|
-
#title-overlay{background:transparent;font-size:var(--ui-fs,16px)} /* UI
|
|
72
|
-
body.at-title #game-hud,body.at-title .start-tips,body.at-title .hud{display:none} /* HUD
|
|
97
|
+
/* Title screen: a TRANSPARENT overlay — the scene behind it comes from the canvas */
|
|
98
|
+
#title-overlay{background:transparent;font-size:var(--ui-fs,16px)} /* the splash UI scales with the canvas's integer k (like the pause and the dialogs) — it does NOT stay at 16px */
|
|
99
|
+
body.at-title #game-hud,body.at-title .start-tips,body.at-title .hud{display:none} /* the game's HUD does not leak into the start menu (display: children cannot show themselves again) */
|
|
73
100
|
.title-wrap{display:flex;flex-direction:column;align-items:center;gap:12px;max-height:96%;}
|
|
74
|
-
.game-title{color:#fff;font-size:2em;line-height:1.2;margin:0;text-align:center;letter-spacing:
|
|
101
|
+
.game-title{color:#fff;font-size:2em;line-height:1.2;margin:0;text-align:center;letter-spacing:var(--ls);text-shadow:2px 2px 0 #0d0d1a,-1px -1px 0 #0d0d1a}
|
|
75
102
|
.title-block{display:inline-block}
|
|
76
|
-
.game-byline{color:#f6f5f0;font-size
|
|
77
|
-
.game-version{color:#9999b3;font-size
|
|
78
|
-
.game-subtitle{color:#f6f5f0;font-size
|
|
79
|
-
/*
|
|
103
|
+
.game-byline{color:#f6f5f0;font-size:1em;text-align:right;margin-top:2px;text-shadow:1px 1px 0 #0d0d1a}
|
|
104
|
+
.game-version{color:#9999b3;font-size:1em;text-align:right;letter-spacing:var(--ls);margin-top:2px;text-shadow:1px 1px 0 #0d0d1a}
|
|
105
|
+
.game-subtitle{color:#f6f5f0;font-size:1em;margin:0 0 4px;text-shadow:1px 1px 0 #0d0d1a;text-align:center}
|
|
106
|
+
/* The splash INSIDE the canvas, with NO scrolling. GESTALT (the Dev): a11y in the HEADER · menu in the WORKSPACE · legend in the FOOTER */
|
|
80
107
|
.title-wrap{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;max-height:none}
|
|
81
|
-
.title-legend{position:absolute;left:0;right:0;bottom:8px} /*
|
|
82
|
-
.title-menu{display:flex;flex-direction:column;gap:.35em;align-items:stretch;padding:2px;width:13em;margin:0 auto} /*
|
|
108
|
+
.title-legend{position:absolute;left:0;right:0;bottom:8px} /* FOOTER, like the pause legend */
|
|
109
|
+
.title-menu{display:flex;flex-direction:column;gap:.35em;align-items:stretch;padding:2px;width:13em;margin:0 auto} /* a fixed, LEAN width */
|
|
110
|
+
/* NO MENU LEAVES THE CANVAS (ADR-0130 rule 5): every opening menu scrolls when its items outgrow the frame — a RULE of the
|
|
111
|
+
class, so a menu a game adds inherits it. The main menu had no scroll: with twenty items it ran 208 px above and 288 px
|
|
112
|
+
below a 360 px stage, and its first and last items could not be reached. */
|
|
113
|
+
.title-menu{max-height:72%;overflow-y:auto;overflow-x:hidden}
|
|
83
114
|
.title-menu[hidden]{display:none}
|
|
84
|
-
/*
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
115
|
+
/* THE WIDTH FOLLOWS THE WIDEST ITEM (ADR-0130 rule 6, issue #134). A fixed width is a translation defect that only
|
|
116
|
+
shows after translation: a label that fits in Portuguese wraps onto two lines, or is cut, in a longer language — and a
|
|
117
|
+
two-line button changes height with the language. `fit-content` is the widest label's own width, capped by the room
|
|
118
|
+
there is, so a label wraps only when the frame itself is narrower than it and the menu never leaves the canvas. */
|
|
119
|
+
#tm-main{width:fit-content}
|
|
120
|
+
/* ===================== THE OPENING SUBMENUS: A VERTICAL LIST THAT SCROLLS (ADR-0044) =====================
|
|
121
|
+
They were two-column GRIDS, and the arrow moves in DOM order: “down” jumped to the right-hand column, across the
|
|
122
|
+
screen. The ring of item 1 was already right in the code (`nextTitleIndex` wraps around) — it was the LAYOUT that
|
|
123
|
+
lied about it. In a list, the order you SEE is the order you MOVE in.
|
|
124
|
+
|
|
125
|
+
Eleven fractions do not fit the frame, so the list SCROLLS — which is what the Dev asked for, and the price is low:
|
|
126
|
+
`focus()` brings the item into view by itself, so whoever moves by keyboard or controller never loses the focus
|
|
127
|
+
out of sight.
|
|
128
|
+
|
|
129
|
+
The panel is OPAQUE for the same reason as the pause veil: the attract mode runs behind the opening, and text over a
|
|
130
|
+
changing frame has whatever contrast the frame allows — an average of luck, not a guarantee. */
|
|
131
|
+
#tm-alf,#tm-mat,#tm-tab,#tm-fr,#tm-cen{width:fit-content;max-height:72%;overflow-y:auto;overflow-x:hidden;background:var(--bg-solid);border-radius:10px;padding:.5em .7em}
|
|
132
|
+
/* The submenu's title STICKS to the top: in a scrolling list, a heading that scrolls away takes with it the only
|
|
133
|
+
“where am I” cue that someone reading with residual vision has. */
|
|
102
134
|
#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}
|
|
103
|
-
/* …
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
135
|
+
/* …and that is why the items need a SCROLL MARGIN: a sticky heading covers the item the browser has just "brought into
|
|
136
|
+
view", because for the browser the item was already inside the frame. MEASURED in the browser before this line
|
|
137
|
+
existed: wrapping from the last item to the first, the first button stopped with its top at 143px while the heading
|
|
138
|
+
ended at 155 — 12px covered, and the child saw a cut-off item where the focus was. The value covers the heading's
|
|
139
|
+
height (34px ≈ 2.1em) with room to spare. */
|
|
108
140
|
#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}
|
|
109
|
-
/*
|
|
110
|
-
|
|
141
|
+
/* The fraction notations stay SIDE BY SIDE: they are five toggles of ONE group, not list items — turning them into a
|
|
142
|
+
column here would lengthen the scroll without adding any order. */
|
|
111
143
|
#tm-fr .frac-nots{display:flex;flex-direction:column;gap:.25em}
|
|
112
|
-
/*
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
144
|
+
/* THE FIVE NOTATIONS ARE SETTINGS, NOT MENU ITEMS (the Dev's request). They used `.title-btn` — the same class as the
|
|
145
|
+
buttons that OPEN an activity — and were stacked right above them: five things that looked like "enter" and in fact
|
|
146
|
+
switch on and off. The frame (`fieldset`/`legend`) is the boundary, and the ☑/☐ mark is the state without relying
|
|
147
|
+
on colour (colour alone fails WCAG 1.4.1). */
|
|
116
148
|
#tm-fr .frac-nots{border:2px solid var(--ink-soft);border-radius:10px;padding:.3em .5em .5em;margin:0 0 .45em}
|
|
117
|
-
#tm-fr .frac-nots legend{padding:0 .4em;font-size
|
|
149
|
+
#tm-fr .frac-nots legend{padding:0 .4em;font-size:1em;font-weight:700;color:var(--ink-soft)}
|
|
118
150
|
#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}
|
|
119
151
|
#tm-fr .fnot-opt:hover,#tm-fr .fnot-opt:focus-visible{background:#16233f}
|
|
120
152
|
#tm-fr .fnot-marca{font-size:1.15em;line-height:1;min-width:1.1em}
|
|
121
153
|
#tm-fr .fnot-opt[aria-checked="true"] .fnot-marca{color:var(--accent)}
|
|
122
154
|
#tm-fr .fnot-sym{font-size:1em}
|
|
123
|
-
#tm-fr .fnot-opt .fv{font-size
|
|
124
|
-
.tm-title{color:#fff;font-size:1em;margin:0 0 .1em;text-align:center;text-shadow:1px 1px 0 #0d0d1a} /*
|
|
125
|
-
.title-btn{background:var(--accent);color:var(--accent-ink);border:3px solid #0d0d1a;font-family:inherit;font-weight:bold;font-size
|
|
126
|
-
.title-btn.ghost{background:#e8862b;color:#0d0d1a;border:2px solid #0d0d1a} /*
|
|
127
|
-
.act-sub{display:block;font-size
|
|
128
|
-
/*
|
|
155
|
+
#tm-fr .fnot-opt .fv{font-size:1em}
|
|
156
|
+
.tm-title{color:#fff;font-size:1em;margin:0 0 .1em;text-align:center;text-shadow:1px 1px 0 #0d0d1a} /* the submenu's title */
|
|
157
|
+
.title-btn{background:var(--accent);color:var(--accent-ink);border:3px solid #0d0d1a;font-family:inherit;font-weight:bold;font-size:1em;padding:.4em .6em;border-radius:6px;cursor:pointer;min-width:0;width:100%;min-height:2.3em} /* a thick BLACK outline (3px) = the same colour as the text (the Dev) */
|
|
158
|
+
.title-btn.ghost{background:#e8862b;color:#0d0d1a;border:2px solid #0d0d1a} /* Back = ORANGE fill */
|
|
159
|
+
.act-sub{display:block;font-size:1em;font-weight:600;opacity:.7;letter-spacing:var(--ls);margin-top:.06em;font-family:var(--font-math,inherit);white-space:nowrap} /* the example subtitle (2nd line, NEVER wraps — the button is wide enough) */
|
|
160
|
+
/* A fixed RED selector (blinking removed at the Dev's request). Specificity through #title-overlay. */
|
|
129
161
|
#title-overlay .title-btn:focus-visible,#title-overlay .title-btn:focus{outline:4px solid #ff3b30;outline-offset:2px}
|
|
130
|
-
#title-overlay .title-btn.act-fx{animation:titleAct .22s ease} /*
|
|
162
|
+
#title-overlay .title-btn.act-fx{animation:titleAct .22s ease} /* the ACTIVATION effect (before the screen changes) */
|
|
131
163
|
@keyframes titleAct{0%{transform:scale(.92);filter:brightness(1.6)}60%{transform:scale(1.03)}100%{transform:scale(1)}}
|
|
132
164
|
@media (prefers-reduced-motion: reduce){ #title-overlay .title-btn.act-fx{animation:none;filter:brightness(1.4)} }
|
|
133
165
|
.title-btn.tab-on{box-shadow:inset 0 0 0 3px #14141f}
|
|
134
|
-
/*
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
166
|
+
/* The times-table rows were TWO lines of numbers side by side, and the Dev objected again after seeing the result: a
|
|
167
|
+
"mixed" menu is a menu where "down" sometimes moves sideways. They became a column, like the rest of the opening.
|
|
168
|
+
The selector carries `#tm-tab` in front on purpose — that is what lets the layout gate see this rule as belonging TO
|
|
169
|
+
an opening submenu, instead of a loose class nobody watches. */
|
|
138
170
|
#tm-tab .tab-row{display:flex;flex-direction:column;gap:.35em}
|
|
139
171
|
#tm-tab .tab-row .title-btn{width:100%;padding:.4em .5em}
|
|
140
|
-
/*
|
|
141
|
-
|
|
142
|
-
.tm-desc:not(:empty){position:absolute;left:50%;transform:translateX(-50%);bottom:10px;max-width:88%;color:#f6f5f0;font-size
|
|
172
|
+
/* FOOTER (the same style as the pause menu): the description at the bottom of the canvas, NOT glued to the button group (the Dev).
|
|
173
|
+
A smoked BLACK background → contrast ≥7:1 between the light text (#f6f5f0) and the background (≈#0a0a12). */
|
|
174
|
+
.tm-desc:not(:empty){position:absolute;left:50%;transform:translateX(-50%);bottom:10px;max-width:88%;color:#f6f5f0;font-size:1em;text-align:center;background:rgba(8,8,16,.92);padding:.35em .7em;border-radius:8px;line-height:1.25}
|
|
143
175
|
.tm-desc:empty{position:absolute;left:0;bottom:10px}
|
|
144
|
-
/*
|
|
145
|
-
.title-legend{display:flex;flex-direction:column;gap:.25em;align-items:center;margin:.5em 0 .1em;font-size
|
|
176
|
+
/* Controls legend: the same look and place as the pause menu's legend (chips), on 2 LINES */
|
|
177
|
+
.title-legend{display:flex;flex-direction:column;gap:.25em;align-items:center;margin:.5em 0 .1em;font-size:1em;color:#c3cfe0}
|
|
146
178
|
.title-legend .lg-row{display:flex;gap:1.1em;justify-content:center;flex-wrap:wrap}
|
|
147
179
|
.title-legend .lg{display:inline-flex;align-items:center;gap:.35em}
|
|
148
|
-
.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
|
|
149
|
-
#title-icons{position:absolute;top:
|
|
150
|
-
|
|
151
|
-
|
|
180
|
+
.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:1em;color:#fff;background:rgba(13,13,26,.85);text-shadow:0 1px 1px rgba(0,0,0,.55)}
|
|
181
|
+
#title-icons{position:absolute;top:0;left:50%;transform:translateX(-50%);display:flex;gap:0;justify-content:center;flex-wrap:nowrap;white-space:nowrap} /* header: on the screen's top edge, giving that room to the game (interface log 2026-09-13), ONE row */
|
|
182
|
+
#title-icons.pause-icons{margin:0 0 calc(.35rem * var(--espaco-fixo,1))}
|
|
183
|
+
/* The NAME of the pointed icon, just below the row. Absolute, so the bar's height — the band the game must not draw
|
|
184
|
+
over (ADR-0148) — does not grow when a name appears; the explanation goes to the footer, not here. */
|
|
185
|
+
#title-icons .pause-icons-cap{position:absolute;top:100%;line-height:1.25;left:50%;transform:translateX(-50%);margin:0;text-align:center;font-size:1em;color:#f6f5f0;pointer-events:none;white-space:nowrap;min-height:0;padding:calc(var(--ui-fs,16px) * .2) calc(var(--ui-fs,16px) * .6)}
|
|
186
|
+
/* the padding stays when empty: the room `createGame` reserves under the bar reads it (issue #160) */
|
|
187
|
+
#title-icons .pause-icons-cap:not(:empty){background:rgba(8,8,16,.92);border-radius:8px}
|
|
188
|
+
/* While a name shows, the bar rises over the HUD so the name covers the mission under it, not the other way round (ADR-0239
|
|
189
|
+
erratum: the name is momentary, and it is what the child is pointing at). Only then: at rest the bar keeps its layer. */
|
|
190
|
+
#title-icons:has(.pause-icons-cap:not(:empty)),.screen-a11y:has(.pause-icons-cap:not(:empty)){z-index:13}
|
|
191
|
+
/* Player-count selector = ONE button ("◀ No. of players: X ▶"); the side clicked decides +1/−1 */
|
|
192
|
+
#np-btn{letter-spacing:var(--ls)}
|
|
152
193
|
#np-btn #np-n{display:inline-block;min-width:1.1em;text-align:center}
|
|
153
|
-
/*
|
|
194
|
+
/* VERTICAL fraction: a number/bar/number stack in plain HTML (zero dependencies) */
|
|
154
195
|
.fv{display:inline-flex;flex-direction:column;align-items:center;vertical-align:middle;line-height:1.05;margin:0 .1em}
|
|
155
196
|
.fv b{font-weight:800;padding:0 .25em}
|
|
156
197
|
.fv b:first-child{border-bottom:2px solid currentColor}
|
|
157
|
-
/*
|
|
158
|
-
:root{--frac-fill:#f2a03d} /*
|
|
198
|
+
/* A fraction as a FIGURE (pie/square): inline SVG, 2-6 equal parts; an improper one = several units side by side */
|
|
199
|
+
:root{--frac-fill:#f2a03d} /* orange: high contrast on white + a black outline (not colour alone — WCAG 1.4.1) */
|
|
159
200
|
.frac-fig{display:inline-flex;align-items:center;gap:.25em;vertical-align:middle}
|
|
160
|
-
.frac-shape{display:inline-flex;align-items:center;gap:.15em} /*
|
|
161
|
-
.frac-op{display:inline-flex;flex-direction:column;align-items:center;gap:.15em;vertical-align:middle} /*
|
|
201
|
+
.frac-shape{display:inline-flex;align-items:center;gap:.15em} /* circle + square of the same fraction, side by side */
|
|
202
|
+
.frac-op{display:inline-flex;flex-direction:column;align-items:center;gap:.15em;vertical-align:middle} /* an operand of the sum: the number ON TOP, the figure below */
|
|
162
203
|
.frac-svg{width:2.4em;height:2.4em;display:inline-block;vertical-align:middle}
|
|
163
204
|
.quiz-choice .frac-svg{width:2.6em;height:2.6em}
|
|
164
205
|
.quiz-prob .frac-svg{width:2.6em;height:2.6em}
|
|
165
206
|
.quiz-dots{font-size:1.3em;letter-spacing:.15em;line-height:1.3;margin:.3rem 0;max-width:12em}
|
|
166
207
|
@media (prefers-contrast: more){
|
|
167
|
-
/*
|
|
168
|
-
|
|
169
|
-
|
|
208
|
+
/* Here `--ink-soft` is ALREADY white: the "changed" frame has to stop being white, or marked and unmarked become the
|
|
209
|
+
same frame — precisely for the child whose settings are most likely to be off the default. A mark whose
|
|
210
|
+
visibility depends on the theme is not a mark (ADR-0029). */
|
|
170
211
|
:root{ --bg:#000; --panel:#000; --ink:#fff; --ink-soft:#fff; --accent:#ffe600; --changed:#ffe600; }
|
|
171
212
|
}
|
|
172
213
|
*{box-sizing:border-box}
|
|
214
|
+
/* A face with a floor above 16 px (`--fonte-escala`, written by the typography cycle and menu; ADR-0176 rule 4) enlarges
|
|
215
|
+
ITS OWN TEXT and not the whole document (interface log 2026-09-13, issue #172): the scale lives on `#game-region`'s
|
|
216
|
+
`font-size`, spacing bound to the text is written in `--ui-fs`, and the root stays 16 px so `rem` does not grow.
|
|
217
|
+
`--espaco-fixo` is the factor of the spaces that do NOT follow the scale — the ones the Dev saw shrink under a 25% browser
|
|
218
|
+
zoom while the text held «mantendo o design muito bom»: 1 at the base, down to that quarter at the 20 px floor. */
|
|
219
|
+
:root{--espaco-fixo:max(.25, 4 - 3 * var(--fonte-escala,1))}
|
|
173
220
|
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)}
|
|
174
|
-
/*
|
|
221
|
+
/* THE SPACING IS THE TEXT'S, WHEREVER THE TEXT IS (ADR-0149 erratum, issue #187): «Se dentro de um botão tem texto, as regras de
|
|
222
|
+
espaçamento valem para este texto, mesmo que seja uma única palavra.» The browser's own stylesheet gives controls
|
|
223
|
+
`letter-spacing: normal`, which beat the inheritance — measured: pause items and quiz options had none. Zero specificity, so a
|
|
224
|
+
rule that spaces a control on purpose still decides. */
|
|
225
|
+
:where(button,select,option,input,textarea){letter-spacing:inherit;word-spacing:inherit}
|
|
226
|
+
/* mathematics in tabular mono (aligned, legible digits) */
|
|
175
227
|
.quiz-box--math .quiz-prob,.quiz-box--math .quiz-choice,#hud-coins,#hud-total{font-family:var(--font-math);font-variant-numeric:tabular-nums}
|
|
176
|
-
/*
|
|
228
|
+
/* paragraph spacing ≥2× only in reading blocks (it does not break the HUD or the menus) */
|
|
177
229
|
.overlay__card p + p{margin-top:2em}
|
|
178
|
-
/*
|
|
179
|
-
|
|
230
|
+
/* The page NEVER scrolls (the Dev): height locked to the viewport and overflow hidden — the canvas's excess (a tolerance
|
|
231
|
+
of 5 logical px per side) is cut by .stage-wrap, and the menus scroll INSIDE their cards. */
|
|
180
232
|
body{display:flex;flex-direction:column;height:100vh;height:100dvh;overflow:hidden}
|
|
181
233
|
.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}
|
|
182
|
-
/*
|
|
183
|
-
|
|
234
|
+
/* The skip link sits on its SLOT, `--z-skip-link`, and not on a literal: above the modals and the transition, below only
|
|
235
|
+
the loop-crash notice and the debug panel. `tests/the-skip-link-outranks-the-transition.node.test.js` holds it (ADR-0102). */
|
|
184
236
|
.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:var(--z-skip-link);transition:top .15s}
|
|
185
237
|
.skip-link:focus{top:0}
|
|
186
238
|
:focus-visible{outline:4px solid var(--focus);outline-offset:2px;border-radius:6px}
|
|
187
239
|
|
|
188
240
|
.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}
|
|
189
|
-
body:not(.dbg) .topbar{display:none} /*
|
|
190
|
-
.title{font-size:1.
|
|
191
|
-
.ver{color:var(--ink-soft);font-weight:400;font-size
|
|
192
|
-
.badge{margin:0;color:var(--accent-ink);background:var(--accent);border-radius:999px;padding:.2em .8em;font-weight:700;font-size
|
|
241
|
+
body:not(.dbg) .topbar{display:none} /* the PAGE title only under ?debug=true (the game already shows it on the splash); gives back the vertical room */
|
|
242
|
+
.title{font-size:1.3em;margin:0}
|
|
243
|
+
.ver{color:var(--ink-soft);font-weight:400;font-size:1em}
|
|
244
|
+
.badge{margin:0;color:var(--accent-ink);background:var(--accent);border-radius:999px;padding:.2em .8em;font-weight:700;font-size:1em}
|
|
193
245
|
|
|
194
246
|
.hud{max-width:var(--maxw);margin:0 auto;padding:0 1rem .5rem;display:flex;gap:1.5rem;flex-wrap:wrap}
|
|
195
|
-
.hud__item{margin:0;font-size:1.
|
|
196
|
-
/*
|
|
247
|
+
.hud__item{margin:0;font-size:1.05em}
|
|
248
|
+
/* coins/mission/power moved into each player's screen; they stay sr-only (for the screen reader) */
|
|
197
249
|
.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}
|
|
198
|
-
/* HUD
|
|
199
|
-
#game-hud{position:absolute;inset:0;pointer-events:none;z-index:4;font-family:var(--font);font-size:var(--hud-fs,
|
|
200
|
-
/* "
|
|
201
|
-
|
|
250
|
+
/* Per-player HUD OVER the canvas (DOM = high definition, no pixelation). One per viewport; coins in the 1st column, power in the 2nd. */
|
|
251
|
+
#game-hud{position:absolute;inset:0;pointer-events:none;z-index:4;font-family:var(--font);font-size:var(--hud-fs,16px);font-weight:800}
|
|
252
|
+
/* "Player screen": one container per player (placed on that player's slice of the viewport). It hosts the HUD, and the
|
|
253
|
+
pause and menus of that player. The render stays shared (one WebGL context). */
|
|
202
254
|
.player-screen{position:absolute;pointer-events:none}
|
|
203
255
|
.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}
|
|
204
256
|
.vphud-obj,.vphud-power{flex:1 1 50%;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
205
257
|
.vphud-ico{font-weight:400}
|
|
206
|
-
/*
|
|
207
|
-
.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,
|
|
258
|
+
/* a player who left (multiplayer): a black viewport with "Game abandoned" in the middle; the others carry on */
|
|
259
|
+
.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,16px);text-align:center}
|
|
208
260
|
.vphud-quit[hidden]{display:none}
|
|
209
|
-
/*
|
|
261
|
+
/* PAUSE inside the canvas (screen = canvas) — laid over #game-region, not over the page */
|
|
210
262
|
.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)}
|
|
211
263
|
.pause-incanvas[hidden]{display:none}
|
|
212
|
-
/*
|
|
213
|
-
.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
|
|
264
|
+
/* The pause menu PER SCREEN (inside each .player-screen). Each player sees and moves through their own. */
|
|
265
|
+
.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 at least, scales with the canvas) */
|
|
214
266
|
.screen-pause[hidden]{display:none}
|
|
215
|
-
|
|
216
|
-
|
|
267
|
+
/* ADR-0163: seven 44 px items are 308 of the 360 px at 640×360 (measured). The title keeps the 16 px text floor with a tight
|
|
268
|
+
line (20 px, not 29) and the card may use the whole height. */
|
|
269
|
+
.screen-pause .pause-card{max-width:94%;max-height:100%;padding:.25rem .7rem}
|
|
270
|
+
/* ONE HEIGHT for every card of the menus (interface log, 2026-09-13): the root, its submenu, the panels and the help. The root's
|
|
271
|
+
six items fitted and its card shrank to them, 46 px shorter than the submenu at 640×360 (measured); it now holds the whole
|
|
272
|
+
height too, its items from the top, as a panel's rows are. */
|
|
273
|
+
.screen-pause .pause-card{height:100%;box-sizing:border-box}
|
|
274
|
+
/* THE EDGE MARGIN (interface log 2026-09-26): the card's CONTENT starts at the margin — its 2 px border just above it, no
|
|
275
|
+
padding under the border — so neither its title nor an item scrolled up under its top edge comes nearer the region's edge
|
|
276
|
+
than the margin (a padding inside the card would not do: a scrolled list shows through its own padding). 📏 The card had
|
|
277
|
+
3 px to spare at 640×360 (pause-target-44px); the border and the old .25rem padding give back what the margin takes. */
|
|
278
|
+
.screen-pause{padding-top:calc(var(--margem-borda,8px) - 2px)}
|
|
279
|
+
.screen-pause .pause-card{padding-top:0}
|
|
280
|
+
/* THE CARD RUNS UNDER THE BUTTON LEGEND AT THE FOOT OF THE SCREEN, as a panel runs under its explanation (issue #134): the
|
|
281
|
+
room at its end and the scroll padding let its LAST item, and the one under the cursor, come to rest above the band —
|
|
282
|
+
and above the HUD row under it (`--rodape-h`), which is drawn over the card too. Measured at 640×360 before: «Sair do jogo»
|
|
283
|
+
at 288–332 px under a legend at 313–342, a third of it covered. */
|
|
284
|
+
.screen-pause .pause-card{scroll-padding-bottom:calc(var(--rodape-h,0px) + var(--footer-band-h,50px))}
|
|
285
|
+
.screen-pause .pause-card::after{content:"";display:block;height:calc(var(--rodape-h,0px) + var(--footer-band-h,50px))}
|
|
286
|
+
.screen-pause .pause-card h2{font-size:1em;line-height:1.25;margin:0 0 .1rem}
|
|
217
287
|
.screen-pause .pause-menu{gap:.3rem}
|
|
218
|
-
/*
|
|
219
|
-
|
|
220
|
-
|
|
288
|
+
/* The per-player frame tightens the PADDING, never the minimum height: the tight frame is the one of a game with four
|
|
289
|
+
children, where fingers compete for room — the worst possible place to shrink a touch target. The card has
|
|
290
|
+
`overflow:auto` and scrolls; the button does not shrink. */
|
|
221
291
|
.screen-pause .pm-btn{padding:.3rem .7rem}
|
|
222
|
-
.screen-pause .pause-legend{margin:.
|
|
223
|
-
/*
|
|
224
|
-
|
|
225
|
-
.pause-
|
|
226
|
-
/*
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
292
|
+
.screen-pause .pause-legend{margin:.1rem 0 0}
|
|
293
|
+
/* ADR-0163: with 42 px items the seven-item submenu overflowed 640×360 by 28 px (measured). The gap goes, not the target:
|
|
294
|
+
centres stay 44 px apart, far above the 24 px the WCAG 2.5.8 spacing exception protects. */
|
|
295
|
+
.screen-pause .pause-menu{gap:2px}
|
|
296
|
+
/* a11y shortcuts = menu buttons shaped as icons, INSIDE the card (no bar or shadow of their own).
|
|
297
|
+
Reachable by keyboard (up from Continue) and by touch/click. */
|
|
298
|
+
.pause-icons{display:flex;flex-wrap:wrap;gap:0;justify-content:center;align-items:center;margin:.1rem 0 .35rem}
|
|
299
|
+
/* ===================== THE QUICK BAR IN THE HUD (ADR-0044, item 7) =====================
|
|
300
|
+
It lives on the player's screen, available DURING play — which is when a child needs to change a setting that is
|
|
301
|
+
getting in the way, and not after pausing.
|
|
302
|
+
|
|
303
|
+
It is a SIBLING of `.screen-exp` and not a child, and the reason is #82: a CSS `filter` reaches every descendant and a
|
|
304
|
+
child cannot cancel it. Inside the experience, the empathy mode would degrade exactly what exists to GIVE access — and
|
|
305
|
+
simulation is not accessibility, it is its opposite.
|
|
306
|
+
|
|
307
|
+
`pointer-events:auto` because the whole `.player-screen` is `none` (the game receives the touch); the icons are the
|
|
308
|
+
exception, as the pause card already was. */
|
|
309
|
+
/* THE BAR'S HEIGHT, in one token: it is what pushes the HUD down. Without it both were anchored to the top and overlapped
|
|
310
|
+
— the HUD read over the icons, which is what the Dev saw and called terrible. */
|
|
311
|
+
.player-screen{--a11y-h:calc(var(--alvo-min,44px) + .5rem)}
|
|
239
312
|
.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)}
|
|
240
|
-
.screen-a11y .pause-icons{flex-wrap:nowrap;white-space:nowrap;gap
|
|
241
|
-
/*
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
.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
|
|
313
|
+
.screen-a11y .pause-icons{flex-wrap:nowrap;white-space:nowrap;gap:0;margin:0;height:100%}
|
|
314
|
+
/* ONLY THE BUTTONS TAKE ROOM. The explanation is ABSOLUTE and stays out of the flow — it appears over the game on mouse
|
|
315
|
+
hover (or when the `accessibility` mode's cursor rests on an icon) and goes without leaving a hole. A strip of text
|
|
316
|
+
reserving height on the game screen is the opposite of what a shortcut bar is for. */
|
|
317
|
+
.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:1em;color:#f6f5f0;pointer-events:none}
|
|
245
318
|
.screen-a11y .pause-icons-cap:not(:empty){background:rgba(8,8,16,.92);border-radius:8px;padding:.2em .6em}
|
|
246
|
-
/*
|
|
247
|
-
|
|
319
|
+
/* The HUD moves BELOW the bar. It is the same sum, and that is why it lives in a token: the two measures cannot
|
|
320
|
+
diverge without one covering the other. */
|
|
248
321
|
.screen-exp .vphud{top:var(--a11y-h)}
|
|
249
|
-
.pi-btn{width:var(--
|
|
250
|
-
/*
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
#padwiz-demo.pw-right #padwiz-demo-img{animation:pwSlideR 1.4s linear infinite}
|
|
256
|
-
#padwiz-demo.pw-up #padwiz-demo-img{animation:pwClimbUp 1.4s linear infinite}
|
|
257
|
-
#padwiz-demo.pw-down #padwiz-demo-img{animation:pwClimbDown 1.4s linear infinite}
|
|
258
|
-
#padwiz-demo.pw-jump #padwiz-demo-img{animation:pwJump .9s cubic-bezier(.3,0,.7,1) infinite}
|
|
259
|
-
#padwiz-demo.pw-run #padwiz-demo-img{animation:pwSlideR .8s linear infinite}
|
|
260
|
-
#padwiz-demo.pw-swap,#padwiz-demo.pw-start{align-items:center}
|
|
261
|
-
#padwiz-demo.pw-swap #padwiz-demo-fx{position:static;white-space:nowrap;font-size:1.7em;animation:pwIcons 4s linear infinite}
|
|
262
|
-
#padwiz-demo.pw-especial #padwiz-demo-fx{animation:pwPulse .8s ease-in-out infinite}
|
|
263
|
-
#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}
|
|
264
|
-
@keyframes pwSlideL{0%{translate:34px 0}100%{translate:-34px 0}}
|
|
265
|
-
@keyframes pwSlideR{0%{translate:-34px 0}100%{translate:34px 0}}
|
|
266
|
-
@keyframes pwClimbUp{0%{translate:0 22px}100%{translate:0 -22px}}
|
|
267
|
-
@keyframes pwClimbDown{0%{translate:0 -22px}100%{translate:0 22px}}
|
|
268
|
-
@keyframes pwJump{0%,100%{translate:0 0}35%{translate:0 -26px}55%{translate:0 -22px}}
|
|
269
|
-
@keyframes pwIcons{0%{translate:60px 0}100%{translate:-60px 0}}
|
|
270
|
-
@keyframes pwPulse{0%,100%{scale:1;opacity:.7}50%{scale:1.5;opacity:1}}
|
|
271
|
-
@keyframes pwBlink{0%,100%{opacity:1}50%{opacity:.25}}
|
|
272
|
-
@media (prefers-reduced-motion: reduce){ #padwiz-demo *{animation:none !important} }
|
|
273
|
-
.pi-btn.pi-on{background:var(--accent);border-color:var(--accent)}
|
|
274
|
-
/* Modo TEA (🧩): pi-on preenchido de amarelo = silencioso · fundo branco = calmo · base (azul) = off.
|
|
275
|
-
(fundo, não borda — a borda é reservada ao realce de seleção :focus/.pi-sel e sobreporia o sinal.) */
|
|
322
|
+
.pi-btn{width:var(--alvo-min,44px);height:var(--alvo-min,44px);min-width:var(--alvo-min,44px);min-height:var(--alvo-min,44px);border-radius:10px;border:2px solid var(--ink-soft);background:#1a2740;color:#f6f5f0;font-size:1.25em;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center}
|
|
323
|
+
/* Gamepad wizard: a demonstration with REAL ANIMATION (game frames swapped by JS) + a CSS offset */
|
|
324
|
+
/* ADR-0159 rule 10 / WCAG 1.4.1: «on» is not colour alone — a thick stripe along the base appears with it. */
|
|
325
|
+
.pi-btn.pi-on{background:var(--accent);border-color:var(--accent);box-shadow:inset 0 -5px 0 var(--accent-ink)}
|
|
326
|
+
/* ASD mode (🧩): pi-on filled yellow = silent · white background = calm · base (blue) = off.
|
|
327
|
+
(the background, not the border — the border is kept for the selection highlight :focus/.pi-sel and would hide the signal.) */
|
|
276
328
|
.pi-btn.pi-calm{background:#fff;border-color:var(--ink-soft)}
|
|
277
|
-
/*
|
|
278
|
-
|
|
279
|
-
(
|
|
280
|
-
|
|
329
|
+
/* Colour-vision correction (🚥): a two-colour "diagnostic" background — pairs chosen by search (Machado's 2009 simulation,
|
|
330
|
+
Lab ΔE) so that whoever HAS the target deficiency sees the largest contrast and the other two the smallest. Tritan
|
|
331
|
+
(the blue-yellow axis, on its own) separates cleanly with red/green; protan and deutan share the red-green axis
|
|
332
|
+
→ only a PARTIAL separation (a physical limit). off = base (blue, grey border); a light border = active. */
|
|
281
333
|
.pi-btn.pi-cvd-protan{background:linear-gradient(90deg,#b3671b 0 50%,#b31b41 50% 100%);border-color:var(--ink)}
|
|
282
334
|
.pi-btn.pi-cvd-deuter{background:linear-gradient(90deg,#0000f2 0 50%,#aa61f2 50% 100%);border-color:var(--ink)}
|
|
283
335
|
.pi-btn.pi-cvd-tritan{background:linear-gradient(90deg,#f23d00 0 50%,#1bb341 50% 100%);border-color:var(--ink)}
|
|
284
336
|
.pi-btn.pi-dis{opacity:.35;cursor:not-allowed}
|
|
285
|
-
.pi-btn.pi-soon{opacity:.55}
|
|
286
337
|
.pi-btn.pi-sel,.pi-btn:focus,.pi-btn:focus-visible{outline:2px solid var(--accent);outline-offset:1px;border-color:var(--accent)}
|
|
287
|
-
.pause-icons-cap{text-align:center;margin:0 0 .3rem;font-size
|
|
288
|
-
/* `scrollbar-gutter: stable both-edges` —
|
|
289
|
-
|
|
290
|
-
|
|
338
|
+
.pause-icons-cap{text-align:center;margin:0 0 .3rem;font-size:1em;color:var(--ink-soft);min-height:1.1em}
|
|
339
|
+
/* `scrollbar-gutter: stable both-edges` — MEASURED in the browser after item 6: with the list at 44 px the card starts
|
|
340
|
+
scrolling, the scrollbar eats width on ONE side only, and the centred list sat 7.5 px off centre. Reserving the gutter
|
|
341
|
+
on both sides gives the symmetry back, scrolling or not. */
|
|
291
342
|
.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)}
|
|
292
343
|
.pause-card h2{margin:.1rem 0 .6rem;color:#ffd23f}
|
|
293
|
-
/* =====================
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
344
|
+
/* ===================== THE PAUSE LIST (ADR-0044, item 6) =====================
|
|
345
|
+
It was `grid-template-columns:1fr 1fr`, and the grid told the same lie as the opening submenus: the arrow moves in
|
|
346
|
+
DOM order, so "down" jumped to the right-hand column. One column, and what you see is again the order you move in.
|
|
347
|
+
CENTRED AND WIDER: 26 letters against the 262 px measured before — the number is in the gate, not in an opinion.
|
|
348
|
+
⚠️ IN `em`, NOT `rem` (pointed out by the Dev): the menus' letters grow with the region (`--ui-fs`) and `rem` stays at the
|
|
349
|
+
root's 16 px — measured at 1920×1080, 48 px letters in a 416 px list, and every item wrapped onto two lines.
|
|
350
|
+
`width` and not `min(26em,100%)`: with the icon bar out of the card (item 7), the card shrinks to its content, and a
|
|
351
|
+
`100%` measured against it became circular — the list fell to 182 px. The LIST declares the width; the card follows
|
|
352
|
+
it up to its own `max-width`. */
|
|
353
|
+
.pause-menu{display:flex;flex-direction:column;gap:.45rem;width:26em;max-width:100%;margin:0 auto}
|
|
354
|
+
/* The HIDDEN list has to really disappear: an author's `display:grid` beats the browser's `[hidden]{display:none}`, and
|
|
355
|
+
without this line the two lists of the pause card would show stacked (ADR-0044, item 5). */
|
|
304
356
|
.pause-menu[hidden]{display:none}
|
|
305
|
-
/* 44 px
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
/*
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
⚠️ E ELE USA `--alvo-min` E NÃO `--tap`, que são coisas diferentes: `--tap` é o tamanho PREFERIDO e
|
|
318
|
-
cresce com a escala do canvas (22 × k, nunca menos de 44); `--alvo-min` é o CHÃO por altura. Um botão
|
|
319
|
-
isolado pode ser o preferido; um item de LISTA, que tem de caber inteiro, usa o chão.
|
|
320
|
-
|
|
321
|
-
O `44px` do fallback é o comportamento de antes, para o caso de `layout()` não ter corrido — que era
|
|
322
|
-
exatamente o que acontecia no `quiz.html` até 07/09. */
|
|
323
|
-
.pm-btn{background:#1a2740;color:#eaf2f8;border:1px solid #3a4a6a;border-radius:10px;padding:.5rem .8rem;min-height:var(--alvo-min,44px);display:flex;align-items:center;justify-content:center;font:inherit;font-weight:700;cursor:pointer;white-space:nowrap}
|
|
357
|
+
/* 44 px is NOT the WCAG minimum — 2.5.8 asks for 24×24, and the card already passed that with room to spare. 44 is
|
|
358
|
+
Apple's recommendation (HIG). `min-height` and not `padding` because the earlier height (35.6 px) was declared
|
|
359
|
+
nowhere: it RESULTED from the sum of padding, font and line height. A target nobody wrote down is a target nobody
|
|
360
|
+
defends. */
|
|
361
|
+
/* The menu item's target is `--alvo-min`, never a literal. ADR-0163 superseded ADR-0095's height ruler (24/34/44 px):
|
|
362
|
+
the engine forces the resolution, no screen is under 640×360, and the floor is 22 × k — 44 px there, growing with the
|
|
363
|
+
scale. `applyScale` (`ui/layout`) writes it on the game region; the 44 px fallback is that same floor at k = 2. */
|
|
364
|
+
/* ADR-0159 rule 8 / WCAG 1.4.11: the item's border is its boundary against the card (#0e1626). #3a4a6a measured 2.04:1;
|
|
365
|
+
#6a7a96 is 4.2:1. */
|
|
366
|
+
.pm-btn{background:#1a2740;color:#eaf2f8;border:1px solid #6a7a96;border-radius:10px;padding:.5rem .8rem;min-height:var(--alvo-min,44px);display:flex;align-items:center;justify-content:center;font:inherit;font-weight:700;cursor:pointer;white-space:nowrap}
|
|
367
|
+
/* ADR-0161: a locked item stays in the list, visibly muted; the cursor still lands on it and it says its reason. */
|
|
368
|
+
.pm-btn[aria-disabled="true"]{color:#9aa6b8;border-style:dashed;cursor:not-allowed}
|
|
324
369
|
.pm-btn:hover,.pm-btn:focus,.pm-btn.pm-sel{background:#2a3a5e;border-color:#ffd23f;outline:2px solid #ffd23f;outline-offset:1px}
|
|
325
|
-
/*
|
|
326
|
-
|
|
370
|
+
/* ADR-0167: no item shows a position number — the place is SPOKEN after the name («…, 2 de 6»), never drawn.
|
|
371
|
+
ADR-0159 rule 12: a pause item's glyph is drawn with empty alternative text, out of the name. */
|
|
372
|
+
.pm-btn[data-glifo]::before{content:attr(data-glifo) / "";margin-inline-end:.5em}
|
|
373
|
+
/* Yes/no legend: the button's glyph (from the pad's design) + the word. By POSITION on every design — yes is the south button
|
|
374
|
+
(A · ✕ · B), no is the east one (B · ◯ · A); no design swaps them (the Dev's association, ADR-0013 erratum). */
|
|
375
|
+
.pause-legend{display:flex;gap:1.1rem;justify-content:center;margin:.7rem 0 .1rem;font-size:1em;color:#c3cfe0}
|
|
327
376
|
.pause-legend .lg{display:inline-flex;align-items:center;gap:.35rem}
|
|
328
|
-
.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
|
|
377
|
+
.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:1em;color:#fff;text-shadow:0 1px 1px rgba(0,0,0,.55)}
|
|
329
378
|
.hud__item strong{font-variant-numeric:tabular-nums}
|
|
330
379
|
.hud__fps{color:var(--ink-soft)}
|
|
331
380
|
|
|
332
381
|
main{flex:1;display:flex;flex-direction:column;width:100%;margin:0 auto;padding:0 .25rem .25rem;min-height:0;overflow:hidden}
|
|
333
|
-
/*
|
|
382
|
+
/* screen = priority: it takes all the room; the excess (up to 5px×k per side) is cut without scrolling */
|
|
334
383
|
.stage-wrap{flex:1;display:flex;align-items:center;justify-content:center;min-height:0;padding:0;overflow:hidden}
|
|
335
384
|
.stage{position:relative;display:flex;align-items:center}
|
|
336
|
-
/* =====
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
385
|
+
/* ===== THE DOM LAYER (issue #82) =====
|
|
386
|
+
Everything that is not the canvas. It exists so the accessibility filter can land on a surface that holds the MENUS
|
|
387
|
+
and not the world — a PIXI filter does not reach the DOM, and filtering the whole `#game-region` would apply twice
|
|
388
|
+
on the canvas.
|
|
340
389
|
|
|
341
|
-
`pointer-events:none`
|
|
342
|
-
|
|
390
|
+
`pointer-events:none` because the wrapper covers the whole region and must not swallow clicks; the four children
|
|
391
|
+
that need clicks and did not declare it (they inherited it from `#game-region`) get `auto` just below.
|
|
343
392
|
|
|
344
|
-
`z-index:1`:
|
|
345
|
-
|
|
346
|
-
|
|
393
|
+
`z-index:1`: above the canvas, BELOW the two CRT pseudo-elements (vignette at 5, scanline at 500), which sit on
|
|
394
|
+
`#game-region`. The CRT is a screen effect and goes over everything — which the scanline already did by the Dev's
|
|
395
|
+
decision, and the vignette now follows. */
|
|
347
396
|
#dom-layer{position:absolute;inset:0;z-index:1;pointer-events:none}
|
|
348
397
|
|
|
349
|
-
/* =====
|
|
350
|
-
|
|
351
|
-
|
|
398
|
+
/* ===== HIGH CONTRAST IN THE DOM (issue #83) =====
|
|
399
|
+
High contrast is NOT a filter: it is Direct Rendering, and it repaints the canvas's TEXTURES. The DOM has no texture
|
|
400
|
+
to repaint — the equivalent has to be written, and this is it.
|
|
352
401
|
|
|
353
|
-
|
|
402
|
+
MEASURED, not estimated (WCAG 1.4.3, the ratio over linearised sRGB). What failed, and only at the 7:1 level:
|
|
354
403
|
|
|
355
|
-
·
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
·
|
|
404
|
+
· the pause panel's VEIL was `rgba(4,7,15,.72)` — translucent. The text's ratio over it DEPENDED on the game behind:
|
|
405
|
+
19.17:1 over a black frame and 8.07:1 over a white one. With `--ink-soft`, `--accent` and `--good` the sum fell to
|
|
406
|
+
5.92 / 5.94 / 5.11:1 — AA, not AAA. At the 3:1 and 4.5:1 levels nothing failed.
|
|
407
|
+
· the SELECTED button is `#2a3a5e`, and `--good` on it gives 6.70:1.
|
|
359
408
|
|
|
360
|
-
|
|
409
|
+
Two changes, and neither invents a colour:
|
|
361
410
|
|
|
362
|
-
1.
|
|
363
|
-
|
|
364
|
-
2.
|
|
365
|
-
|
|
366
|
-
|
|
411
|
+
1. THE VEIL IS OPAQUE. That is what "high contrast" means: the half-tone goes, and with it the dependence on what is
|
|
412
|
+
behind. On its own this takes everything to AAA but one case.
|
|
413
|
+
2. THE CURSOR INVERTS — `--accent` background, `--accent-ink` text, 12.71:1. Darkening the selected item would bring
|
|
414
|
+
it closer to the unselected one (`#1a2740`) and the child would LOSE THE CURSOR; inverting solves both at once, and
|
|
415
|
+
it is the high-contrast pattern of every operating system.
|
|
367
416
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
417
|
+
Result: the worst pair of the menu goes from 5.11:1 to 10.29:1 (`--ink-soft` on a button). A margin of +47% over
|
|
418
|
+
7:1. `tests/menu-contrast-measured.node.test.js` asserts it on every run, with the ratios computed from this CSS
|
|
419
|
+
itself — not copied into the test.
|
|
371
420
|
|
|
372
|
-
⚠️
|
|
373
|
-
|
|
374
|
-
/* (
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
421
|
+
⚠️ IT HOLDS FOR ALL THREE LEVELS, not only 7:1: at the other two the menu already passed, but it passed BY THE LUCK
|
|
422
|
+
of whatever was on screen. Determinism is a gain at every level. */
|
|
423
|
+
/* (The veil's rule is not here: the veil is opaque AT THE BASE, by the Dev's decision — the title and pause menus have to
|
|
424
|
+
be at 7:1 FROM THE START, because they are precisely where a person with a disability adjusts the controls for
|
|
425
|
+
themselves. A contrast that only arrives after finding the setting arrives late.
|
|
426
|
+
What is left here is what is specific to the mode: the inverted cursor.) */
|
|
378
427
|
#dom-layer.hc .pm-btn{background:var(--panel-btn)}
|
|
379
428
|
#dom-layer.hc .pm-btn:hover,
|
|
380
429
|
#dom-layer.hc .pm-btn:focus,
|
|
@@ -384,63 +433,76 @@ main{flex:1;display:flex;flex-direction:column;width:100%;margin:0 auto;padding:
|
|
|
384
433
|
.game-region{position:relative;background:#05070f;box-shadow:0 0 0 3px var(--ink-soft);border-radius:8px;overflow:hidden;flex:none}
|
|
385
434
|
.libras-slot{flex:none;width:0;height:0;background:#0b1020;border-radius:8px;overflow:hidden}
|
|
386
435
|
.game-region:focus-visible{outline:4px solid var(--focus);outline-offset:3px}
|
|
387
|
-
/* =====
|
|
388
|
-
|
|
389
|
-
|
|
436
|
+
/* ===== THE REACH NOTICE (issue #112) =====
|
|
437
|
+
It reuses `.overlay` and `.overlay__card` whole — the card is born with the same shell as the other dialogs, and
|
|
438
|
+
that is what makes it look like part of the game instead of a browser error. Only the title's emphasis is its own. */
|
|
390
439
|
#reach-notice .reach-notice__titulo{font-weight:700;margin:0 0 .5rem}
|
|
391
440
|
#reach-notice p{margin:0 0 .5rem}
|
|
392
441
|
#reach-notice button{margin-top:.75rem}
|
|
393
|
-
/* =====
|
|
394
|
-
|
|
395
|
-
`
|
|
396
|
-
|
|
397
|
-
⚠️
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
/* ⚠️
|
|
401
|
-
`.game-region.crt-scan-1::after`
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
442
|
+
/* ===== THE LOOP STOPPED (ADR-0054, issue #109) =====
|
|
443
|
+
THE TEXT COMES FROM THE SCRIPT, and that is the point: `ui/loop-crash` writes the sentence ALREADY TRANSLATED (from
|
|
444
|
+
`t()`, through `textContent`), and the CSS only shows it. A notice hard-coded here would be in English for a Brazilian
|
|
445
|
+
child — the defect pillar 3 exists to prevent.
|
|
446
|
+
⚠️ AND THE COLOURS ARE RAW, ON PURPOSE: no `var(--…)`. This notice appears exactly when something broke, and the
|
|
447
|
+
theming machinery is a plausible suspect. White on wine gives 9.5:1, above the 7:1 of 1.4.6, and does not depend on
|
|
448
|
+
anything having survived. */
|
|
449
|
+
/* ⚠️ IT IS AN ELEMENT, NOT A `::after` WITH `content: attr(...)` — that version would never show. The
|
|
450
|
+
`.game-region.crt-scan-1::after` just below is the CRT scanline, on by default — and an element has ONE `::after`
|
|
451
|
+
only. The two rules do not stack; the CRT's comes later and wins. The `::before` is taken by the vignette. An
|
|
452
|
+
ELEMENT competes with nobody for its place — and its text enters the accessibility tree, which `content` does not
|
|
453
|
+
guarantee. Its slot, `--z-loop-crash`, is above the scanline's 500 on purpose. */
|
|
405
454
|
#incl-parou{
|
|
406
455
|
position:absolute;inset:auto 0 0 0;z-index:var(--z-loop-crash);
|
|
407
456
|
padding:14px 18px;
|
|
408
457
|
background:#7f1d1d;color:#fff;border-top:3px solid #fff;
|
|
409
|
-
font:700
|
|
458
|
+
font:700 1em/1.35 system-ui,-apple-system,"Segoe UI",sans-serif;
|
|
410
459
|
text-align:center;text-wrap:balance
|
|
411
460
|
}
|
|
412
461
|
.pixi-mount,.pixi-mount canvas{display:block;width:100%;height:100%}
|
|
413
|
-
/* pixel
|
|
462
|
+
/* crisp pixel art when scaling */
|
|
414
463
|
.pixi-mount canvas{image-rendering:pixelated;image-rendering:crisp-edges}
|
|
415
|
-
/*
|
|
416
|
-
|
|
464
|
+
/* The CRT look (visual sensitivity menu) in 3 levels: -1=small, -2=large. z-index 5 = over the game's canvas/HUD, BELOW the
|
|
465
|
+
in-canvas pause (6) and the dialogs (60) → the menus never lose legibility. */
|
|
417
466
|
.game-region.crt-scan-1::after,
|
|
418
467
|
.game-region.crt-vig-1::before{content:'';position:absolute;inset:0;z-index:5;pointer-events:none}
|
|
419
|
-
/*
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
.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
|
|
468
|
+
/* ALIGNED scanlines: period = 1 LOGICAL game pixel (k px), rounded to a PHYSICAL pixel (dpr), and line = 1 physical
|
|
469
|
+
pixel — the JS (`render/crt`, applyCrt) computes --scan-per/--scan-line; without it the 1px/3px gradient in CSS px
|
|
470
|
+
fell off the grid (uneven lines on screens with Windows scaling). */
|
|
471
|
+
.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))} /* a high z-index = scanlines OVER everything (canvas AND DOM menus), the Dev's request */
|
|
423
472
|
.game-region.crt-vig-1::before{background:radial-gradient(ellipse at center, transparent 60%, rgba(0,0,0,.34) 100%)}
|
|
424
|
-
.game-region.crt-round-0{border-radius:0} /*
|
|
425
|
-
.game-region.crt-round-2{border-radius:24px} /*
|
|
473
|
+
.game-region.crt-round-0{border-radius:0} /* square screen */
|
|
474
|
+
.game-region.crt-round-2{border-radius:24px} /* level 1 (small) = the 8px default, no class */
|
|
426
475
|
@media (prefers-reduced-transparency: reduce){ .game-region.crt-scan-1::after,.game-region.crt-vig-1::before{display:none} }
|
|
427
476
|
|
|
428
|
-
/*
|
|
477
|
+
/* start tips over the canvas */
|
|
429
478
|
.start-tips{position:absolute;left:50%;top:14%;transform:translateX(-50%);
|
|
430
479
|
display:flex;flex-direction:column;gap:.5rem;align-items:center;pointer-events:none;
|
|
431
480
|
transition:opacity .5s ease;z-index:6;max-width:92%}
|
|
432
481
|
.start-tips p{margin:0;background:rgba(5,7,15,.85);color:#fff;border:2px solid rgba(255,255,255,.45);
|
|
433
|
-
border-radius:10px;padding:.4em .9em;font-size:1.
|
|
482
|
+
border-radius:10px;padding:.4em .9em;font-size:1.05em;font-weight:600;text-align:center}
|
|
434
483
|
.start-tips kbd{background:var(--accent);color:var(--accent-ink);border-radius:6px;padding:.05em .5em;font-weight:800}
|
|
435
484
|
.start-tips.hide{opacity:0}
|
|
436
485
|
@media (prefers-reduced-motion: reduce){ .start-tips{transition:none} }
|
|
437
486
|
|
|
438
|
-
/*
|
|
487
|
+
/* mode selector */
|
|
439
488
|
.modes{max-width:var(--maxw);margin:0 auto .5rem;padding:0 1rem;display:flex;gap:.5rem;flex-wrap:wrap}
|
|
440
|
-
.mode-btn{min-height:var(--
|
|
489
|
+
.mode-btn{min-height:var(--alvo-min,44px);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}
|
|
441
490
|
.mode-btn.is-on{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
|
|
442
|
-
/*
|
|
443
|
-
|
|
491
|
+
/* STEPS ⯇ ⯈ (ADR-0151): choosing between positions with left and right. The arrows are FINGER targets and follow the
|
|
492
|
+
target ruler (`--alvo-min`, ADR-0163); the focus belongs to the whole control, and the end that cannot move is dimmed. */
|
|
493
|
+
.passos{display:inline-flex;align-items:center;gap:.25rem;min-height:var(--alvo-min,44px);border:2px solid var(--ink-soft);border-radius:8px;padding:0 .15rem;background:var(--panel-btn);color:var(--ink);font-weight:700;user-select:none;-webkit-user-select:none}
|
|
494
|
+
.passos:focus,.passos:focus-visible{outline:3px solid var(--focus);outline-offset:2px}
|
|
495
|
+
.passo-seta{display:inline-flex;align-items:center;justify-content:center;min-width:var(--alvo-min,44px);min-height:var(--alvo-min,44px);cursor:pointer;font-size:1.1em;touch-action:manipulation}
|
|
496
|
+
.passo-seta.no-limite{opacity:.35;cursor:default}
|
|
497
|
+
.passo-valor{min-width:6ch;text-align:center;flex:1}
|
|
498
|
+
/* «◀ Label: value ▶» takes the whole ROW (ADR-0130 erratum): there is no label on the left competing for the room. */
|
|
499
|
+
/* 🔴 AND NO BOX INSIDE THE BOX: the ROW is the item. The first version kept `.passos`'s border and background, and the
|
|
500
|
+
text wrapped onto four lines inside a box beside the hint — the Dev: «Por que continua desenhando os botões de seleção
|
|
501
|
+
de <=5 itens do jeito errado?!». On one line, `◀ Label: value ▶`, with no wrapping. */
|
|
502
|
+
.ctrl-row--passos .passos{flex:1;justify-content:space-between;border:0;background:transparent;padding:0;white-space:nowrap}
|
|
503
|
+
.ctrl-row--passos .passo-valor{white-space:nowrap}
|
|
504
|
+
/* TOGGLE (switch): replaces the "icon + On/Off" buttons. State = a track + a knob that slides.
|
|
505
|
+
Toggle-button semantics through aria-pressed (the inner text is hidden by font-size:0). */
|
|
444
506
|
.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}
|
|
445
507
|
.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}
|
|
446
508
|
.mode-btn.switch[aria-pressed="true"]{background:var(--accent);border-color:var(--accent)}
|
|
@@ -448,33 +510,33 @@ main{flex:1;display:flex;flex-direction:column;width:100%;margin:0 auto;padding:
|
|
|
448
510
|
.mode-btn.switch:focus-visible,.mode-btn.switch:focus{outline:3px solid var(--accent);outline-offset:2px}
|
|
449
511
|
@media (prefers-reduced-motion: reduce){ .mode-btn.switch,.mode-btn.switch::after{transition:none} }
|
|
450
512
|
|
|
451
|
-
/*
|
|
513
|
+
/* the quiz challenge (an overlay over the canvas) */
|
|
452
514
|
.quiz{position:absolute;inset:0;z-index:12;display:grid;place-items:center;background:rgba(0,0,0,.55)}
|
|
453
515
|
.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}
|
|
454
|
-
/* 3
|
|
516
|
+
/* 3 progress lights (top right corner): off = grey; on = yellow with a glow around it */
|
|
455
517
|
.quiz-wins{position:absolute;top:.5rem;right:.6rem;display:flex;gap:.4rem}
|
|
456
518
|
.qw-dot{width:.95rem;height:.95rem;border-radius:50%;background:#3a4152;border:2px solid #0d0d1a}
|
|
457
|
-
.qw-dot.on{background:var(--accent);border-color:#8a6d00;box-shadow:0 0 .55rem .12rem var(--accent)} /*
|
|
519
|
+
.qw-dot.on{background:var(--accent);border-color:#8a6d00;box-shadow:0 0 .55rem .12rem var(--accent)} /* the glow around it (a light that is on) */
|
|
458
520
|
.quiz-wins.celebrate .qw-dot.on{animation:qwPop .6s ease}
|
|
459
521
|
@keyframes qwPop{0%{transform:scale(1)}45%{transform:scale(1.4);box-shadow:0 0 .9rem .3rem var(--accent)}100%{transform:scale(1)}}
|
|
460
522
|
@media (prefers-reduced-motion:reduce){ .quiz-wins.celebrate .qw-dot.on{animation:none} }
|
|
461
523
|
.quiz-shape{color:var(--accent);font-weight:800;text-transform:capitalize}
|
|
462
|
-
.quiz-prob{font-size:1.
|
|
524
|
+
.quiz-prob{font-size:1.6em;font-weight:800;margin:.4rem 0 .8rem}
|
|
463
525
|
.quiz-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:.4rem}
|
|
464
|
-
.quiz-choice{min-height:var(--
|
|
526
|
+
.quiz-choice{min-height:var(--alvo-min,44px);min-width:var(--alvo-min,44px);font:inherit;font-size:1.2em;font-weight:700;color:var(--ink);background:#131a30;border:2px solid var(--ink-soft);border-radius:8px;cursor:pointer}
|
|
465
527
|
.quiz-choice.sel{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
|
|
466
528
|
.quiz-choice.reveal{outline:3px solid var(--good);outline-offset:2px}
|
|
467
|
-
.quiz-hint{font-size
|
|
468
|
-
.quiz-emoji{font-size:2.
|
|
469
|
-
/*
|
|
470
|
-
.quiz-word{font-size:2.
|
|
529
|
+
.quiz-hint{font-size:1em;margin-top:.6rem;color:var(--ink-soft)}
|
|
530
|
+
.quiz-emoji{font-size:2.4em;line-height:1}
|
|
531
|
+
/* The top word = a SELECTABLE emoji (jumping on it repeats the speech / VLibras signs it) */
|
|
532
|
+
.quiz-word{font-size:2.4em;line-height:1;background:transparent;border:2px solid transparent;border-radius:12px;padding:.05em .2em;cursor:pointer}
|
|
471
533
|
.quiz-word.sel{border-color:var(--accent);background:rgba(255,230,0,.12)}
|
|
472
|
-
.quiz-letter{font-size
|
|
534
|
+
.quiz-letter{font-size:1em;color:var(--ink-soft);margin:.1rem 0 .4rem}
|
|
473
535
|
.silaba-boxes{display:flex;gap:.4rem;justify-content:center;margin-bottom:.5rem}
|
|
474
|
-
.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.
|
|
536
|
+
.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.15em}
|
|
475
537
|
.silaba-box.filled{border-style:solid;border-color:var(--accent);background:rgba(255,255,255,.08)}
|
|
476
538
|
.silaba-actions{display:flex;gap:.4rem;justify-content:center;margin-top:.4rem}
|
|
477
|
-
/*
|
|
539
|
+
/* a Braille cell */
|
|
478
540
|
.bcells{display:flex;gap:.5rem;justify-content:center;flex-wrap:wrap;margin:.4rem 0}
|
|
479
541
|
.bcell{display:flex;flex-direction:column;align-items:center;gap:.2rem}
|
|
480
542
|
.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}
|
|
@@ -484,94 +546,394 @@ main{flex:1;display:flex;flex-direction:column;width:100%;margin:0 auto;padding:
|
|
|
484
546
|
@media (prefers-contrast: more){ .quiz-box{background:#000} .quiz-choice{background:#000} .quiz-choice.sel{background:#fff;color:#000} }
|
|
485
547
|
|
|
486
548
|
.opts{max-width:var(--maxw);margin:0 auto .5rem;padding:0 1rem;display:flex;gap:.4rem;flex-wrap:wrap;justify-content:center}
|
|
487
|
-
/*
|
|
488
|
-
.opts .mode-btn{padding:.35em .65em;font-size
|
|
549
|
+
/* a compact controls bar → fits on ONE row on common screens (does not affect overlay buttons) */
|
|
550
|
+
.opts .mode-btn{padding:.35em .65em;font-size:1em;white-space:nowrap}
|
|
489
551
|
.caption{position:absolute;left:50%;bottom:8px;transform:translateX(-50%);background:rgba(0,0,0,.82);color:#fff;
|
|
490
|
-
border:2px solid rgba(255,255,255,.5);border-radius:8px;padding:.25em .7em;font-weight:700;font-size
|
|
552
|
+
border:2px solid rgba(255,255,255,.5);border-radius:8px;padding:.25em .7em;font-weight:700;font-size:1em;
|
|
491
553
|
z-index:8;pointer-events:none;max-width:90%;opacity:0;transition:opacity .15s}
|
|
492
554
|
.caption.show{opacity:1}
|
|
493
555
|
@media (prefers-reduced-motion: reduce){ .caption{transition:none} }
|
|
494
556
|
|
|
495
557
|
.help{max-width:var(--maxw);margin:1rem auto 0;color:var(--ink-soft)}
|
|
496
|
-
.help summary{cursor:pointer;font-weight:700;color:var(--ink);min-height:var(--
|
|
558
|
+
.help summary{cursor:pointer;font-weight:700;color:var(--ink);min-height:var(--alvo-min,44px);display:flex;align-items:center}
|
|
497
559
|
.help ul{padding-left:1.1rem}
|
|
498
560
|
.help li{margin:.3rem 0}
|
|
499
561
|
|
|
500
|
-
.btn{min-height:var(--
|
|
562
|
+
.btn{min-height:var(--alvo-min,44px);min-width:var(--alvo-min,44px);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}
|
|
501
563
|
.btn--primary{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
|
|
502
564
|
|
|
503
565
|
.overlay{position:fixed;inset:0;z-index:50;display:grid;place-items:center;background:rgba(0,0,0,.7);padding:1rem}
|
|
504
|
-
/* "
|
|
505
|
-
|
|
566
|
+
/* "screen = canvas": the a11y dialogs are re-parented into #game-region (JS) and stay held within the canvas's bounds;
|
|
567
|
+
the last one opened goes on top (z-index by JS). Only affects the ones inside. */
|
|
506
568
|
#game-region .overlay{position:absolute;z-index:60;padding:.6rem}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
.
|
|
511
|
-
|
|
569
|
+
/* THE QUIZ STARTS BELOW THE QUICK BAR (ADR-0148). The engine declares `--barra-a11y-h` on `#game-region`; the bar sits
|
|
570
|
+
10 px from the top (`#title-icons`), and the statement was drawn behind it — measured on a screenshot of the Dev's. */
|
|
571
|
+
/* Since issue #160 the variable is the WHOLE room (the bar's offset, the bar, the icon name line and a light gap): read as is. */
|
|
572
|
+
.quiz-app{padding-top:var(--barra-a11y-h,0px);padding-bottom:var(--rodape-h,0px)} /* the HUD row is always there; the footer bands overlay (ADR-0239 erratum) */
|
|
573
|
+
/* THE EDGE MARGIN (interface log 2026-09-26; `--margem-borda`, written by `ui/layout` from k): the Dev, «os textos estão tocando
|
|
574
|
+
na borda do jogo». The sides get it here; the top is the quick bar's room and the bottom the HUD row's, which keeps its own. */
|
|
575
|
+
.quiz-app{padding-left:var(--margem-borda,8px);padding-right:var(--margem-borda,8px)}
|
|
576
|
+
/* ADR-0163: at 640×360 the bar, the statement, four 44 px options and the footer band had 404 px to fit in 360 (measured).
|
|
577
|
+
The spacing goes, not the targets or the 16 px floor: the statement keeps its size on a 1.25 line and no outer margins. */
|
|
578
|
+
.quiz-pergunta{margin:0 0 calc(.25rem * var(--espaco-fixo,1));line-height:1.25}
|
|
579
|
+
/* The quiz's MENU button (ADR-0166 erratum), in the top-left corner the bar leaves free: the engine's target size. */
|
|
580
|
+
/* THE QUICK PAUSE (ADR-0155): PAUSED in the middle of the frozen screen — THE WORD ONLY, white, with a BLACK OUTLINE ON
|
|
581
|
+
THE LETTERS and no box: «Borda nas letras, não numa caixa» (the Dev, the record's erratum). The outline is what keeps it
|
|
582
|
+
legible over a light scene; `paint-order` puts the stroke UNDER the fill, so the letter does not get thinner, and the
|
|
583
|
+
shadow in eight directions covers browsers without `-webkit-text-stroke`. z-index 12, the same literal as the quiz
|
|
584
|
+
overlay (`.quiz`), not yet a slot: over the content by document order, under the pad (14) and the panels (60). The
|
|
585
|
+
right slot is `--z-game-msg`, and adopting it alone would put PAUSED over a panel. No pointer events. */
|
|
586
|
+
.pausa-rapida{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);z-index:12;pointer-events:none;
|
|
587
|
+
color:#fff;-webkit-text-stroke:.08em #000;paint-order:stroke fill;
|
|
588
|
+
text-shadow:2px 0 #000,-2px 0 #000,0 2px #000,0 -2px #000,2px 2px #000,-2px 2px #000,2px -2px #000,-2px -2px #000;
|
|
589
|
+
font-family:var(--font);font-weight:800;font-size:calc(var(--ui-fs,16px)*2);letter-spacing:var(--ls);white-space:nowrap}
|
|
590
|
+
.pausa-rapida[hidden]{display:none}
|
|
591
|
+
/* WHAT THE SCAN IS OFFERING (ADR-0218, #201): with «um botão só», the child does not choose — they wait, and what they
|
|
592
|
+
wait for has to be in view. The SAME outlined white letters as the quick pause, no box, at the TOP of the region,
|
|
593
|
+
below the bar's band (`--barra-a11y-h`), which is the zone no game uses. No pointer events: it is a notice, not a
|
|
594
|
+
control — what fires it is its single button, wherever that is. */
|
|
595
|
+
/* ⚠️ `--scan-top` and NOT `--barra-a11y-h`: this label makes the band grow, and placing it by the band made a loop — it
|
|
596
|
+
pushed the band, the band pushed it, and it went down the screen on every measure (caught by a test case). `--scan-top`
|
|
597
|
+
is where the bar ends, and does not depend on it (`ui/top-band` writes both). */
|
|
598
|
+
.scan-now{position:absolute;left:50%;top:var(--scan-top,0px);transform:translateX(-50%);z-index:12;
|
|
599
|
+
pointer-events:none;color:#fff;-webkit-text-stroke:.06em #000;paint-order:stroke fill;
|
|
600
|
+
text-shadow:2px 0 #000,-2px 0 #000,0 2px #000,0 -2px #000,2px 2px #000,-2px 2px #000,2px -2px #000,-2px -2px #000;
|
|
601
|
+
font-family:var(--font);font-weight:800;font-size:calc(var(--ui-fs,16px)*1.5);letter-spacing:var(--ls);white-space:nowrap}
|
|
602
|
+
.scan-now[hidden]{display:none}
|
|
603
|
+
/* THE FOOTER LEGEND of the quick pause (ADR-0155 erratum): the same outlined white letters, no box, right ABOVE the pad's
|
|
604
|
+
START pill — which stays in view, because it is the way out for someone who only has a finger. ⚠️ `left`/`right` and
|
|
605
|
+
not `left:50%` + `translate`: with that, the box shrank to HALF of the region (measured: 271 px, three lines, over
|
|
606
|
+
PAUSED). */
|
|
607
|
+
/* 🔴 The footer is ONE column (2026-09-12): the explanation of the pointed bar icon above the quick-pause legend. Two
|
|
608
|
+
absolute strips covered each other as soon as one wrapped. */
|
|
609
|
+
/* 🔴 ADR-0164 (2026-09-12): the footer is at the LOWEST edge of the region, over a darkened band, and holds at most TWO
|
|
610
|
+
lines of text. The Dev saw a three-line explanation in the middle of the screen, over a quiz option, with outlined
|
|
611
|
+
letters and no background. White on black at .82 over pure white composes to ~13:1 (the panel footer's arithmetic).
|
|
612
|
+
With the quick-pause legend also showing, each keeps ONE line, so the band never grows past two.
|
|
613
|
+
`--footer-band-h` is the room those two lines take: the engine's panels keep it free under their last row.
|
|
614
|
+
🔴 `--rodape-h` is what a GAME leaves free at the bottom, and since ADR-0239's erratum it is the HUD ROW's room only — its
|
|
615
|
+
height plus how far the pad lifts it. The caption, the legend and the explanation are MOMENTARY and overlay the workspace
|
|
616
|
+
while they show: «a legenda é momentânea» (the Dev). Counting them made a band that is off most of the time take the
|
|
617
|
+
game's floor for good (the demo quiz lost its last option at 640×360). */
|
|
618
|
+
#game-region{--footer-band-h:calc(var(--ui-fs,16px) * 2.6 + 4px + var(--margem-borda,8px));--rodape-h:calc(var(--hud-row-bottom,0px) + var(--hud-row-h,0px))}
|
|
619
|
+
/* ADR-0163 rule 3: the region's text grows with the scale the engine forces — 16 px at 640×360, 32 at 1280×720 — times the
|
|
620
|
+
communication cycle's type scale, which the root size also carries. Without it everything in the region read the ROOT
|
|
621
|
+
16 px: measured at 1280×720, quiz options at 16 px and the bar's emoji at 20 px under a `--ui-fs` of 32. */
|
|
622
|
+
#game-region{font-size:calc(var(--ui-fs,16px) * var(--fonte-escala,1))}
|
|
623
|
+
/* ADR-0164 as the Dev corrected it: the EXPLANATION stands on a full-width dark band at the lowest edge (rule 2, rule 4);
|
|
624
|
+
the BUTTON LEGEND runs the full width with NO band, a dark background only behind each «name: function» (rule 3), and
|
|
625
|
+
sits above the explanation. The column itself paints nothing. Padding adds to 8 px, so two lines fit `--footer-band-h`. */
|
|
626
|
+
/* THE PAD'S SHOULDER COLUMNS, as what else stands on the region must keep clear of them: while the pad shows them they own the
|
|
627
|
+
TOP CORNERS (ADR-0160; drawn only when the game names them, ADR-0162), and they are placed for REACH — a thumb finds a corner
|
|
628
|
+
without looking — so where they and anything else meet, the other thing moves. A shoulder is a `.touch-btn`, 56 px with the
|
|
629
|
+
target floor under it; a column holds one or two, 6 px apart. `--shoulders-*-reach` is how far down a column reaches, the edge
|
|
630
|
+
margin under it counted: 0 while that corner holds none. */
|
|
631
|
+
#game-region,.game-region{--shoulder-size:max(56px,var(--alvo-min,44px));--shoulders-left-reach:0px;--shoulders-right-reach:0px}
|
|
632
|
+
#game-region:has(#touch-controls:not([hidden]) > .touch-ombros--esq),.game-region:has(#touch-controls:not([hidden]) > .touch-ombros--esq){
|
|
633
|
+
--shoulders-left-reach:calc(var(--margem-borda,8px) * 2 + var(--shoulder-size))}
|
|
634
|
+
#game-region:has(#touch-controls:not([hidden]) > .touch-ombros--esq > :nth-child(2)),.game-region:has(#touch-controls:not([hidden]) > .touch-ombros--esq > :nth-child(2)){
|
|
635
|
+
--shoulders-left-reach:calc(var(--margem-borda,8px) * 2 + var(--shoulder-size) * 2 + 6px)}
|
|
636
|
+
#game-region:has(#touch-controls:not([hidden]) > .touch-ombros--dir),.game-region:has(#touch-controls:not([hidden]) > .touch-ombros--dir){
|
|
637
|
+
--shoulders-right-reach:calc(var(--margem-borda,8px) * 2 + var(--shoulder-size))}
|
|
638
|
+
#game-region:has(#touch-controls:not([hidden]) > .touch-ombros--dir > :nth-child(2)),.game-region:has(#touch-controls:not([hidden]) > .touch-ombros--dir > :nth-child(2)){
|
|
639
|
+
--shoulders-right-reach:calc(var(--margem-borda,8px) * 2 + var(--shoulder-size) * 2 + 6px)}
|
|
640
|
+
/* THE HUD the engine mounts from a cartridge's `hud` (ADR-0168; issue #162): the mission at the top centre, just under the quick
|
|
641
|
+
bar, and the rest in the HUD row at the bottom (ADR-0239 and its erratum; the row's rules are below). Where a caller mounts the
|
|
642
|
+
bands with no row they stand on the region: the points and the power down the right edge, the learning bars at the bottom
|
|
643
|
+
centre. State on dark chips, like the icon's name; no pointer events, so a finger passes through to the game.
|
|
644
|
+
📌 What it keeps clear of: the quick bar, which owns the top (ADR-0239's driver: «the top belongs to the quick accessibility
|
|
645
|
+
bar; the HUD no longer shares it») — `--barra-a11y-h` is the band `ui/top-band` measures —, the pad's shoulder columns (above),
|
|
646
|
+
and the pad itself, which the row stands above (ADR-0239 §6). */
|
|
647
|
+
/* Where the bands have no row, the right edge's stack (the points, the power under them) starts under the bar's band and under
|
|
648
|
+
the right shoulders — whichever reaches lower — and never nearer the top than the edge margin. */
|
|
649
|
+
#game-region,.game-region{--hud-corner-top:max(var(--margem-borda,8px),var(--barra-a11y-h,0px),var(--shoulders-right-reach))}
|
|
650
|
+
.hud-faixa{position:absolute;z-index:12;margin:0;pointer-events:none;font-size:var(--ui-fs,16px);line-height:1.25;display:flex;gap:2px}
|
|
651
|
+
.hud-faixa[hidden]{display:none}
|
|
652
|
+
/* The MISSION sits at the TOP CENTRE, just under the quick bar (ADR-0239 erratum): `ui/top-band` writes its `top` from the
|
|
653
|
+
bar's box. The class keeps its old name — a page-surface name is not renamed in passing. The bar's momentary name line is
|
|
654
|
+
drawn over it while a name shows (the Dev: «a legenda é momentânea, a escrita da missão aparece o tempo todo»). */
|
|
655
|
+
/* THE EDGE MARGIN (interface log 2026-09-26) where a caller mounts the bands on the region, with no row: `--margem-borda` from
|
|
656
|
+
the side, and from the top the same place under the bar the root's mission takes (`--hud-mission-top`, which `ui/top-band`
|
|
657
|
+
writes; 0 with no bar) — at the margin it stood behind the bar's icons. Where the root mounts the mission, `ui/top-band`
|
|
658
|
+
writes that `top` on the element itself. */
|
|
659
|
+
.hud-esquerda{top:max(var(--margem-borda,8px),var(--hud-mission-top,0px));left:50%;transform:translateX(-50%);max-width:calc(100% - 2 * var(--margem-borda,8px));flex-direction:column;align-items:center;text-align:center}
|
|
660
|
+
.hud-direita{top:var(--hud-corner-top,8px);right:var(--margem-borda,8px);flex-direction:column;align-items:flex-end;text-align:right}
|
|
661
|
+
/* The learning bars (ADR-0049 §5): one ten-segment bar per skill, one skill a line as in the row (ADR-0239 §1), centred at the
|
|
662
|
+
bottom — under the footer column, which comes later in the region on the same layer and covers them. Okabe-Ito colours, and
|
|
663
|
+
a second cue beside each, so no state is told by colour alone (WCAG 1.4.1): green carries a dot, red stripes, an empty slot
|
|
664
|
+
only its outline; a purple bar a ▲, an orange one a ▼. 📏 Side by side (ADR-0168 §3's footer, superseded), three bars were
|
|
665
|
+
501 px wide at 640×360 and, lifted above the pad, ran into the points and the power under the right shoulders. */
|
|
666
|
+
.hud-aprendizagem{left:50%;bottom:var(--margem-borda,8px);transform:translateX(-50%);flex-direction:column;align-items:center;gap:2px}
|
|
667
|
+
/* …and with no row, ABOVE the on-screen pad while it shows, as the row would stand (ADR-0239 §6): `--hud-row-bottom` is how high
|
|
668
|
+
the pad's lower parts reach (`ui/hud-row`). 📏 At the lower edge they stood under the START and SELECT pills. */
|
|
669
|
+
.game-region > .hud-aprendizagem,#game-region > .hud-aprendizagem{bottom:calc(var(--hud-row-bottom,0px) + var(--margem-borda,8px))}
|
|
670
|
+
/* The explanation band is 82% opaque: the bars showed through under its words. While it shows they are not drawn at all —
|
|
671
|
+
the explanation has priority (ADR-0168 §3), and a segment behind a letter is noise for the child reading it. */
|
|
672
|
+
.game-region:has(.barra-explicacao:not([hidden])) .hud-aprendizagem,#game-region:has(.barra-explicacao:not([hidden])) .hud-aprendizagem{visibility:hidden}
|
|
673
|
+
.hud-barra{display:flex;align-items:center;gap:.15em;padding:.2em .3em;background:rgba(8,8,16,.92);border-radius:8px}
|
|
674
|
+
.hud-seg{display:block;width:.8em;height:1em;border-radius:2px;box-sizing:border-box}
|
|
675
|
+
.hud-seg[data-seg="vazio"]{border:1px solid #8a8f98}
|
|
676
|
+
.hud-seg[data-seg="azul"]{background:#0072B2}
|
|
677
|
+
.hud-seg[data-seg="verde"]{background:radial-gradient(circle,#08080f 0 .14em,transparent .16em),#009E73}
|
|
678
|
+
.hud-seg[data-seg="vermelho"]{background:repeating-linear-gradient(45deg,#08080f 0 .12em,transparent .12em .3em),#D55E00}
|
|
679
|
+
.hud-barra[data-cor="roxa"] .hud-seg:not([data-seg="vazio"]){background:#CC79A7}
|
|
680
|
+
.hud-barra[data-cor="laranja"] .hud-seg:not([data-seg="vazio"]){background:#E69F00}
|
|
681
|
+
.hud-barra[data-cor="roxa"]::after,.hud-barra[data-cor="laranja"]::after{color:#f6f5f0;font-size:1em;line-height:1;margin-left:.15em}
|
|
682
|
+
.hud-barra[data-cor="roxa"]::after{content:"▲"}
|
|
683
|
+
.hud-barra[data-cor="laranja"]::after{content:"▼"}
|
|
684
|
+
.hud-numero{margin:0;padding:0 .3em;color:#f6f5f0;background:rgba(8,8,16,.92);border-radius:8px;max-width:100%;overflow-wrap:anywhere}
|
|
685
|
+
/* ===================== THE HUD ROW AT THE BOTTOM (ADR-0239; issue #94) =====================
|
|
686
|
+
One row along the lower edge, left to right: the learning bars · the session clock, centred · the power over the score ·
|
|
687
|
+
the game's map. `ui/hud-row` writes where it stands (`--hud-row-bottom`: above the on-screen pad while it shows) and its
|
|
688
|
+
height (`--hud-row-h`, which the footer and `--rodape-h` read — the row is all a game must leave free). Three columns of which the outer two are equal, so the
|
|
689
|
+
clock is at the true centre; `minmax(0,…)` so a long cell never pushes it off. No pointer events: a finger passes through
|
|
690
|
+
to the game. Empty — no clock, no band, no map — it is not drawn. */
|
|
691
|
+
.hud-row{position:absolute;left:0;right:0;bottom:var(--hud-row-bottom,0px);z-index:12;pointer-events:none;box-sizing:border-box;
|
|
692
|
+
display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);align-items:end;column-gap:8px;
|
|
693
|
+
padding:0 var(--margem-borda,8px) var(--margem-borda,8px); /* the edge margin (interface log 2026-09-26) */
|
|
694
|
+
font-family:var(--font);font-size:var(--ui-fs,16px)}
|
|
695
|
+
/* …and while the pad shows a shoulder column on a side, the row's cells stand clear of it on that side: lifted above the pad at
|
|
696
|
+
640×360 the row reaches up to the shoulders (the pad's directional is 179 px tall), and they keep their corner for reach
|
|
697
|
+
(ADR-0160). 📏 The power over the score stood 5 px into R1. */
|
|
698
|
+
#game-region:has(#touch-controls:not([hidden]) > .touch-ombros--esq) > .hud-row,.game-region:has(#touch-controls:not([hidden]) > .touch-ombros--esq) > .hud-row{
|
|
699
|
+
padding-left:calc(var(--margem-borda,8px) * 2 + var(--shoulder-size))}
|
|
700
|
+
#game-region:has(#touch-controls:not([hidden]) > .touch-ombros--dir) > .hud-row,.game-region:has(#touch-controls:not([hidden]) > .touch-ombros--dir) > .hud-row{
|
|
701
|
+
padding-right:calc(var(--margem-borda,8px) * 2 + var(--shoulder-size))}
|
|
702
|
+
.hud-row:not(:has(.session-clock, .hud-faixa:not([hidden]), .hud-map > *)){display:none}
|
|
703
|
+
.hud-row-learning{justify-self:start;min-width:0}
|
|
704
|
+
.hud-row-clock{justify-self:center}
|
|
705
|
+
.hud-row-end{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:end;column-gap:8px;min-width:0}
|
|
706
|
+
.hud-score{justify-self:center;display:flex;flex-direction:column;align-items:center;gap:2px;min-width:0}
|
|
707
|
+
.hud-map{display:flex;align-items:flex-end} /* empty, it is 0 px wide: nothing is drawn until a game mounts its map */
|
|
708
|
+
/* In the row the bands are laid out by their cell, not placed on the region; the learning bars stack, one skill a line,
|
|
709
|
+
because three side by side do not fit a third of 640 px. */
|
|
710
|
+
.hud-row .hud-faixa{position:static;transform:none}
|
|
711
|
+
.hud-row .hud-aprendizagem{flex-direction:column;align-items:flex-start;gap:2px}
|
|
712
|
+
.hud-row .hud-direita{align-items:center;text-align:center}
|
|
713
|
+
/* The explanation band covers the row while it shows (a menu is open, the game is paused): the row is not drawn under its
|
|
714
|
+
translucent band, and the band sits where the row stands. The caption and the legend otherwise stand ABOVE the row. */
|
|
715
|
+
.game-region:has(.barra-explicacao:not([hidden])) .hud-row,#game-region:has(.barra-explicacao:not([hidden])) .hud-row{visibility:hidden}
|
|
716
|
+
/* THE LOOK OF THE CLOCK AND THE SCORE: a small label over bold digits, white with a dark outline — the reference is Super
|
|
717
|
+
Mario World's HUD, and nothing of its art or face is taken: the engine's own faces, the mathematics one with tabular
|
|
718
|
+
figures for the digits, so they never change width. Text at the 16 px floor and above (ADR-0163); white on its black
|
|
719
|
+
outline, so it holds its contrast over any scene. */
|
|
720
|
+
.session-clock-text,.hud-points .hud-numero{color:#fff;font-weight:800;-webkit-text-stroke:.06em #000;paint-order:stroke fill;
|
|
721
|
+
text-shadow:2px 0 #000,-2px 0 #000,0 2px #000,0 -2px #000,2px 2px #000,-2px 2px #000,2px -2px #000,-2px -2px #000}
|
|
722
|
+
.session-clock-digits,.hud-points .hud-numero{font-family:var(--font-math);font-variant-numeric:tabular-nums;
|
|
723
|
+
font-size:calc(var(--ui-fs,16px) * 1.25);line-height:1.1;white-space:nowrap}
|
|
724
|
+
.hud-points .hud-numero{background:none;padding:0}
|
|
725
|
+
.hud-points{top:var(--hud-corner-top,8px);right:var(--margem-borda,8px);flex-direction:column;align-items:flex-end} /* only where a caller mounts the bands with no row; `--hud-corner-top` above */
|
|
726
|
+
/* …and there, at the top edge, the digits get a line that holds their glyphs: at 1.1 the face's box rose 1 px over the line
|
|
727
|
+
(measured: 24 px of glyph box on a 22 px line at 640×360, 46 on 44 at 1280×720), into the edge margin */
|
|
728
|
+
.game-region > .hud-points .hud-numero,#game-region > .hud-points .hud-numero{line-height:1.2}
|
|
729
|
+
/* …and the POWER, which in the row stands over the points (ADR-0239 §3), has no room over them there: the same pair reads
|
|
730
|
+
top-down, the points first and the power directly under them, on the same right edge — both at one `top` drew one box over
|
|
731
|
+
the other. The points' line is one line of digits, 1.25 × the text size on a 1.2 line (above); 2 px apart, as in the row's
|
|
732
|
+
cell. With no points shown the power is the stack's first line. */
|
|
733
|
+
.game-region:has(> .hud-points:not([hidden]) > .hud-numero:not([hidden])) > .hud-direita,
|
|
734
|
+
#game-region:has(> .hud-points:not([hidden]) > .hud-numero:not([hidden])) > .hud-direita{top:calc(var(--hud-corner-top,8px) + var(--ui-fs,16px) * 1.25 * 1.2 + 2px)}
|
|
735
|
+
/* THE SESSION CLOCK (ADR-0236, ADR-0239): the label over the time left, and the Time Timer pie to their right, as tall as a
|
|
736
|
+
target. The pie is the share still to go (`--left`, written by `ui/session-clock`), green on a white face (ADR-0050 §3)
|
|
737
|
+
inside a dark rim and a white ring, drawn inside its box. At the end the WHOLE pie turns red — an almost empty face
|
|
738
|
+
becoming a full one, so the change is not told by colour alone (WCAG 1.4.1). Okabe-Ito green and vermillion. */
|
|
739
|
+
.session-clock{display:flex;align-items:center;gap:6px;pointer-events:none}
|
|
740
|
+
.session-clock-text{display:flex;flex-direction:column;align-items:center;line-height:1.1}
|
|
741
|
+
/* The label stays at the 16 px text floor (ADR-0163) and loses weight instead, and the digits are an eighth over the floor:
|
|
742
|
+
the Dev found «TEMPO» too big and wants the label-and-digits block no wider than two and a half pies (ADR-0239 errata). */
|
|
743
|
+
.session-clock-label{font-size:var(--ui-fs,16px);letter-spacing:var(--ls);font-weight:600}
|
|
744
|
+
.session-clock-digits{font-size:calc(var(--ui-fs,16px) * 1.125)}
|
|
745
|
+
.session-clock-pie{flex:none;box-sizing:border-box;width:var(--alvo-min,44px);height:var(--alvo-min,44px);border-radius:50%;
|
|
746
|
+
border:2px solid #08080f;box-shadow:inset 0 0 0 2px #f6f5f0;
|
|
747
|
+
background:conic-gradient(#f6f5f0 0 calc((1 - var(--left,1)) * 1turn),#009E73 0)}
|
|
748
|
+
/* The spent share is the face from twelve o'clock CLOCKWISE, and the green is what is left up to twelve: the edge moves
|
|
749
|
+
clockwise as time passes, like a Time Timer (the Dev, 2026-09-25: it was emptying anticlockwise). */
|
|
750
|
+
/* The end is red and still — no pulse, no lock (ADR-0240). */
|
|
751
|
+
.session-clock[data-look="red"] .session-clock-pie{background:#D55E00}
|
|
752
|
+
.rodape-da-tela{position:absolute;left:0;right:0;bottom:0;z-index:12;display:flex;flex-direction:column;gap:0;pointer-events:none}
|
|
753
|
+
/* WITH THE HUD ROW (ADR-0239 point 6): the caption and the legend stand ABOVE the row; while the explanation shows, the column
|
|
754
|
+
comes down to where the row stands, and the explanation covers it. */
|
|
755
|
+
#game-region:has(> .hud-row) .rodape-da-tela{bottom:calc(var(--hud-row-bottom,0px) + var(--hud-row-h,0px))}
|
|
756
|
+
#game-region:has(> .hud-row) .rodape-da-tela:has(> .barra-explicacao:not([hidden])){bottom:var(--hud-row-bottom,0px)}
|
|
757
|
+
.rodape-da-tela:not(:has(> :not([hidden]))){display:none}
|
|
758
|
+
/* `--footer-lines` is how many lines a footer text shows (ADR-0164): the clamp reads it, and so do the scrolling box below and
|
|
759
|
+
`ui/footer-scroll-driver`, which must know the view it scrolls a longer text through (ADR-0245). */
|
|
760
|
+
.pausa-legenda,.barra-explicacao{color:#fff;font-family:var(--font);font-weight:700;font-size:var(--ui-fs,16px);--footer-lines:2;
|
|
761
|
+
line-height:1.3;text-align:center;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:var(--footer-lines);overflow:hidden}
|
|
762
|
+
/* The band reaches the edges; its WORDS stand the edge margin off them (interface log 2026-09-26) — `--footer-band-h` counts it.
|
|
763
|
+
At the sides .6em is already more than the margin (4.8·k against 4·k); under them the margin is a transparent BORDER the
|
|
764
|
+
band's colour shows through, not padding: the two-line clamp clips at the padding box, and a padding there would show the
|
|
765
|
+
top of a third line. */
|
|
766
|
+
.barra-explicacao{order:2;padding:4px .6em 0;border-bottom:var(--margem-borda,8px) solid transparent;background:rgba(0,0,0,.82)}
|
|
767
|
+
.pausa-legenda{order:1;padding:0 .6em}
|
|
768
|
+
.rodape-da-tela:not(:has(> .barra-explicacao:not([hidden]))) > .pausa-legenda{padding-bottom:var(--margem-borda,8px)}
|
|
769
|
+
.pausa-legenda .lg-nome{background:rgba(0,0,0,.82);padding:0 .35em;border-radius:4px;-webkit-box-decoration-break:clone;box-decoration-break:clone}
|
|
770
|
+
/* The SOUND CAPTION (study item D3) tops the column — ADR-0164 rule 4: «the sound caption above, the explanation below
|
|
771
|
+
it» — on a dark background behind its words only, two lines at most (rule 5). */
|
|
772
|
+
.legenda-de-som{order:0;align-self:center;max-width:100%;box-sizing:border-box;margin:0 0 4px;padding:2px .6em;border-radius:6px;
|
|
773
|
+
background:rgba(0,0,0,.82);color:#fff;font-family:var(--font);font-weight:700;font-size:var(--ui-fs,16px);line-height:1.3;
|
|
774
|
+
text-align:center;display:-webkit-box;-webkit-box-orient:vertical;--footer-lines:2;-webkit-line-clamp:var(--footer-lines);overflow:hidden}
|
|
775
|
+
/* the caption as the column's lowest line (no legend, no explanation under it): the edge margin under it, not 4 px */
|
|
776
|
+
.rodape-da-tela:not(:has(> .barra-explicacao:not([hidden]), > .pausa-legenda:not([hidden]))) > .legenda-de-som{margin-bottom:var(--margem-borda,8px)}
|
|
777
|
+
/* two or more lines of the footer showing at once: one line each, so the footer does not climb into the workspace */
|
|
778
|
+
.rodape-da-tela:has(> :not([hidden]) ~ :not([hidden])) > *{--footer-lines:1}
|
|
779
|
+
.pausa-legenda[hidden],.barra-explicacao[hidden],.legenda-de-som[hidden]{display:none}
|
|
780
|
+
/* ADR-0245: AN EXPLANATION LONGER THAN ITS LINES MOVES INSIDE THEM. `ui/footer-scroll-driver` marks it `data-scroll`: a glide
|
|
781
|
+
translates the words' `.footer-scroll-text` block (a Web Animation, sub-pixel — a transform needs a block, an inline span
|
|
782
|
+
ignores it), reduced motion turns pages by `scrollTop`, at once. The box stays the clamped one to the pixel: a block of
|
|
783
|
+
exactly its lines (`1lh` each, no clamp and so no ellipsis), with the 4 px over the words as a transparent BORDER instead of
|
|
784
|
+
padding — the view clips at the padding box, and a padding would show a slice of the next line. Pointer events come back to
|
|
785
|
+
the band while it moves, so a pointer over it can hold it (WCAG 2.2.2). */
|
|
786
|
+
.barra-explicacao[data-scroll],#game-region .opt-explain[data-scroll]{display:block;-webkit-line-clamp:none;box-sizing:content-box;
|
|
787
|
+
height:calc(var(--footer-lines,2) * 1lh);padding-top:0;border-top:4px solid transparent;pointer-events:auto}
|
|
788
|
+
.barra-explicacao[data-scroll] > .footer-scroll-text,#game-region .opt-explain[data-scroll] > .footer-scroll-text{display:block}
|
|
789
|
+
/* A PANEL WEARS THE PAUSE CARD (interface log, 2026-09-13). The Dev: the settings submenu «mantém a mesma altura e identidade
|
|
790
|
+
visual», and every panel it opens had «outra identidade visual, com alturas menores, título maior e cores diferentes».
|
|
791
|
+
So a panel in the region takes `.screen-pause .pause-card` as it is: the whole height, the same dark card and gold border,
|
|
792
|
+
the gold title at the text size, items of `.pm-btn`'s colour, border and radius with the same 2 px gaps. It is at least
|
|
793
|
+
the pause card's width and grows only when a row needs it — a steps row never wraps (ADR-0130 erratum), and with the BDA
|
|
794
|
+
spacing (ADR-0149) «◀ Realce de contraste: desligado ▶» is wider than the pause list's 26em. */
|
|
795
|
+
#game-region .overlay__card{background:#0e1626;border:2px solid #ffd23f;border-radius:14px;padding:.25rem .7rem;width:auto;
|
|
796
|
+
max-width:94%;height:100%;max-height:100%;box-sizing:border-box;
|
|
797
|
+
scrollbar-gutter:stable both-edges;box-shadow:0 8px 30px rgba(0,0,0,.5);scroll-padding-bottom:var(--footer-band-h,50px)}
|
|
798
|
+
#game-region .overlay__card h2{font-size:1em;line-height:1.25;margin:0 0 .1rem;color:#ffd23f}
|
|
799
|
+
#game-region .overlay__back{width:100%;margin:0 0 2px;justify-content:center;background:#1a2740;border:1px solid #6a7a96;border-radius:10px;color:#eaf2f8}
|
|
800
|
+
#game-region .overlay__back:hover,#game-region .overlay__back:focus{background:#2a3a5e;border-color:#ffd23f;outline:2px solid #ffd23f;outline-offset:1px}
|
|
801
|
+
#game-region .ctrl-list{gap:2px}
|
|
802
|
+
/* the pause list's width is the floor, as `.pause-menu` declares it: the card follows with the same padding and scrollbar gutters */
|
|
803
|
+
#game-region .overlay__back,#game-region .ctrl-list{min-width:26em;max-width:100%} /* em: grows with the menu's text, like the pause list */
|
|
804
|
+
#game-region .ctrl-row{box-sizing:border-box;min-height:var(--alvo-min,44px);padding:.3rem .7rem;background:#1a2740;border:1px solid #6a7a96;border-radius:10px;color:#eaf2f8;font-weight:700}
|
|
805
|
+
#game-region .overlay__card > .ctrl-row{margin-top:2px}
|
|
806
|
+
#game-region .ctrl-row:hover,#game-region .ctrl-row:focus-within{background:#2a3a5e;border-color:#ffd23f}
|
|
807
|
+
/* The card runs under the explanation band, as the pause card runs under its legend: the room at the end of the card and
|
|
808
|
+
the scroll padding let the last row, and the focused one, come to rest above the band. */
|
|
809
|
+
#game-region .overlay__card::after{content:"";display:block;height:var(--footer-band-h,50px)}
|
|
810
|
+
/* HELP IS A SLIDE SHOW (interface log, 2026-09-13; `ui/help-panel`): «textos, figuras e no máximo animações», not a menu. One stop
|
|
811
|
+
with the page arrows at its sides; the slide holds the child's key drawn as a key cap, the game's word and its sentence, and
|
|
812
|
+
dots that say where the child is by fill and not by colour alone. The key cap presses itself now and then — the one animation,
|
|
813
|
+
gone under reduced motion. */
|
|
814
|
+
/* a slide explains itself: no footer band telling the child to point at an option, and no room kept for it */
|
|
815
|
+
#help .opt-explain,#help .overlay__card::after{display:none}
|
|
816
|
+
.slides{display:flex;align-items:stretch;gap:.25rem;min-height:11em;border:1px solid #6a7a96;border-radius:10px;background:#1a2740;color:#eaf2f8}
|
|
817
|
+
.slides:focus,.slides:focus-visible{outline:2px solid #ffd23f;outline-offset:1px;border-color:#ffd23f}
|
|
818
|
+
.slides .passo-seta{align-self:center;color:#ffd23f;font-size:1.3em}
|
|
819
|
+
.slide{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.45em;padding:.5em .2em;text-align:center}
|
|
820
|
+
.slide-tecla{display:inline-flex;align-items:center;justify-content:center;min-width:3em;min-height:2em;padding:.15em .6em;font:inherit;font-size:1.4em;
|
|
821
|
+
font-weight:800;color:#0e1626;background:#eaf2f8;border-radius:10px;box-shadow:0 .22em 0 #6a7a96;animation:tecla-aperta 2.4s ease-in-out infinite}
|
|
822
|
+
.slide-tecla[data-sem-tecla]{color:#c3cfe0;background:transparent;border:2px dashed #6a7a96;box-shadow:none;animation:none}
|
|
823
|
+
@keyframes tecla-aperta{0%,70%,100%{transform:translateY(0);box-shadow:0 .22em 0 #6a7a96}82%{transform:translateY(.16em);box-shadow:0 .06em 0 #6a7a96}}
|
|
824
|
+
@media (prefers-reduced-motion: reduce){ .slide-tecla{animation:none} }
|
|
825
|
+
/* a cartridge's «how to play» figure (ADR-0195): the surface the game draws on, crisp at the pixel scale */
|
|
826
|
+
.slide-figura{display:block;width:min(100%,16em);height:7em;image-rendering:pixelated}
|
|
827
|
+
.slide-palavra{margin:0;font-size:1.2em;font-weight:800;color:#ffd23f}
|
|
828
|
+
.slide-texto{margin:0;max-width:28em;line-height:1.35}
|
|
829
|
+
.slide-pontos{display:flex;gap:.45em;justify-content:center;margin-top:.2em}
|
|
830
|
+
.slide-ponto{width:.6em;height:.6em;border-radius:50%;border:1px solid #eaf2f8;box-sizing:border-box}
|
|
831
|
+
.slide-ponto.is-on{background:#ffd23f;border-color:#ffd23f}
|
|
832
|
+
/* Explanation footer: white on 0.82 translucent black → the white's contrast is >7:1 even over 100% white
|
|
833
|
+
(composition: 1-0.82=0.18 → L≈0.027 → ~13:1), without being 100% opaque. Fixed at the card's foot. */
|
|
834
|
+
.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;text-align:left;
|
|
835
|
+
background:rgba(0,0,0,.82);color:#fff;font-size:1em;line-height:1.35;border-top:1px solid rgba(255,255,255,.18);border-radius:0 0 9px 9px;backdrop-filter:blur(1px);
|
|
836
|
+
/* ADR-0164: two lines at most — in the region a longer explanation scrolls through them (ADR-0245, below) */
|
|
837
|
+
display:-webkit-box;-webkit-box-orient:vertical;--footer-lines:2;-webkit-line-clamp:var(--footer-lines);overflow:hidden}
|
|
838
|
+
/* ADR-0164 rule 1: a panel's explanation is a footer of the SCREEN, not of the card — «está ajustado ao menu, não ao
|
|
839
|
+
canvas», the Dev on a screenshot. The card sets no position, so the overlay (the region's size) is the containing block:
|
|
840
|
+
the band lands at the region's lowest edge, side to side, and does not scroll with the card. The card runs under it,
|
|
841
|
+
the pause card's height (see the panel rules above). */
|
|
842
|
+
#game-region .opt-explain{position:absolute;left:0;right:0;bottom:0;z-index:1;margin:0;padding:4px .6em 0;min-height:0;
|
|
843
|
+
border:0;border-bottom:var(--margem-borda,8px) solid transparent;border-radius:0;text-align:center;line-height:1.3;font-size:var(--ui-fs,16px)}
|
|
844
|
+
/* ↑ the transparent bottom border is the edge margin under the words, as the footer band's (interface log 2026-09-26) */
|
|
845
|
+
/* the card's content from the edge margin down, as the pause card's (interface log 2026-09-26) */
|
|
846
|
+
#game-region .overlay{padding:calc(var(--margem-borda,8px) - 2px) 0 0}
|
|
847
|
+
#game-region .overlay__card{padding-top:0}
|
|
512
848
|
#game-region .overlay__actions{position:static;background:transparent;padding:.5rem 0 0;margin:0}
|
|
513
|
-
#visual{background:rgba(0,0,0,.28)
|
|
849
|
+
#visual{background:rgba(0,0,0,.28)} /* colour: a translucent background, to see the effect on the game at once */
|
|
514
850
|
|
|
515
|
-
/*
|
|
516
|
-
|
|
851
|
+
/* Simulation indicator (TOP LEFT corner): white=blindness, green=low vision. A double tap returns to normal. The top right is
|
|
852
|
+
the right shoulders' (ADR-0160) and, with no HUD row, the points'; nothing of the HUD sits top left, so the dot is alone
|
|
853
|
+
there — under the left shoulders while the pad shows them (`--shoulders-left-reach`, with the HUD's rules). */
|
|
854
|
+
#viz-indicator{position:absolute;top:max(var(--margem-borda,8px),var(--shoulders-left-reach,0px));left:var(--margem-borda,8px);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)} /* INSIDE the canvas (game-region) — it works as a button */
|
|
517
855
|
#viz-indicator.blind{background:#fff}
|
|
518
856
|
#viz-indicator.low{background:#36d36a}
|
|
519
|
-
/*
|
|
857
|
+
/* Low vision — 5 simulations (the blur comes from the filter on the canvas; only the overlays are here) */
|
|
520
858
|
#viz-overlay{position:absolute;inset:0;z-index:8;pointer-events:none}
|
|
521
|
-
#viz-overlay.lv-blur{background:none} /*
|
|
522
|
-
#viz-overlay.lv-haze{background:rgba(244,246,250,.42)} /*
|
|
523
|
-
#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:
|
|
524
|
-
#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%)} /*
|
|
525
|
-
#viz-overlay.lv-diabetic{background: /*
|
|
859
|
+
#viz-overlay.lv-blur{background:none} /* myopia/astigmatism: blur only */
|
|
860
|
+
#viz-overlay.lv-haze{background:rgba(244,246,250,.42)} /* cataract: a whitish film */
|
|
861
|
+
#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: tunnel vision */
|
|
862
|
+
#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%)} /* macular degeneration: a central blur */
|
|
863
|
+
#viz-overlay.lv-diabetic{background: /* diabetic retinopathy: scattered spots */
|
|
526
864
|
radial-gradient(circle at 22% 30%, rgba(10,10,14,.95) 0 4%, transparent 11%),
|
|
527
865
|
radial-gradient(circle at 64% 22%, rgba(10,10,14,.9) 0 3%, transparent 9%),
|
|
528
866
|
radial-gradient(circle at 80% 58%, rgba(10,10,14,.95) 0 5%, transparent 12%),
|
|
529
867
|
radial-gradient(circle at 40% 70%, rgba(10,10,14,.9) 0 4%, transparent 10%),
|
|
530
868
|
radial-gradient(circle at 16% 80%, rgba(10,10,14,.85) 0 3%, transparent 9%),
|
|
531
869
|
radial-gradient(circle at 54% 48%, rgba(10,10,14,.8) 0 3%, transparent 8%)}
|
|
532
|
-
/*
|
|
870
|
+
/* Total blindness: the canvas turns black (filter brightness(0)); we hide the HUD and the on-screen controls */
|
|
533
871
|
body.blind-mode .hud, body.blind-mode #touch-controls, body.blind-mode #start-tips, body.blind-mode #caption{visibility:hidden}
|
|
534
872
|
[hidden]{display:none!important}
|
|
535
|
-
/*
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
/*
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
873
|
+
/* CAPITAL LETTERS (the AAC menu, ADR-0028). What a game draws on the canvas is the game's to capitalise; this rule covers
|
|
874
|
+
what the browser draws in the DOM — menus, HUD, captions. `createGame` writes `data-letras` on the root (ADR-0149 §1).
|
|
875
|
+
`text-transform` and NOT transforming the text: the accessible content stays intact, and a screen reader does not start
|
|
876
|
+
spelling letter by letter, which is what real ALL CAPS causes in NVDA/JAWS. */
|
|
877
|
+
/* Quiz options take the ENGINE's target floor (ADR-0163 rule 2): 44 px at 640×360, growing with the scale. They had
|
|
878
|
+
9 mm computed by the quiz (54 px on a desktop), and four of them pushed the last one out of a 360 px region. */
|
|
879
|
+
.quiz-alt{min-height:var(--alvo-min,44px);display:block;width:100%;margin:0;padding:calc(var(--ui-fs,16px) * .25) calc(var(--ui-fs,16px) * .5)} /* the list gap spaces them (#160); padding in `--ui-fs`, so a larger face grows the text only (#172) */
|
|
880
|
+
/* 📏 .25/.5 and not .4/.9 since the edge margin (interface log 2026-09-26): with 16 px less width and a HUD row 4 px taller, the
|
|
881
|
+
quiz's own questions ended under the row at 640×360 (quiz-bench-layout); the 44 px floor is `min-height`, untouched. */
|
|
882
|
+
/* the marked option is not colour alone either (ADR-0159 rule 10): the same base stripe as an icon switched on */
|
|
883
|
+
.quiz-alt.is-on{box-shadow:inset 0 0 0 3px var(--accent-ink)} /* a ring, not a bottom strip: the strip left the fill looking shifted up (#166) */
|
|
884
|
+
/* FIVE OPTIONS IN THREE COLUMNS — «1 2 3» over «4 5», read left to right (`quiz-round.OPTION_COLUMNS`, written as `--quiz-cols`).
|
|
885
|
+
📏 In one column the fifth ended under the HUD row at 640×360, and in two a two-line statement with its explanation did. */
|
|
886
|
+
.quiz-alts{display:grid;grid-template-columns:repeat(var(--quiz-cols,3),minmax(0,1fr));gap:calc(.25rem * var(--espaco-fixo,1))}
|
|
887
|
+
/* An option already tried is out: struck through as well as dimmed, so it is not told by colour alone (WCAG 1.4.1); an inactive
|
|
888
|
+
control is outside the contrast minimum (WCAG 1.4.3). The answer to COPY (ADR-0049 §6) carries a mark before its words. */
|
|
889
|
+
.quiz-alt.is-off{opacity:.55;text-decoration:line-through;cursor:default}
|
|
890
|
+
.quiz-alt.is-answer{outline:3px solid var(--accent);outline-offset:2px}
|
|
891
|
+
.quiz-alt.is-answer::before{content:"➜ "}
|
|
892
|
+
/* The explanation after the third wrong attempt, and the copy line: under the statement, at the text size, not bold. While it
|
|
893
|
+
shows, the statement comes down to the text size too: 📏 a two-line statement at its heading size, a three-line explanation
|
|
894
|
+
and two rows of options ended under the HUD row at 640×360 (quiz-bench-layout). */
|
|
895
|
+
.quiz-note{margin:0 0 calc(.25rem * var(--espaco-fixo,1));line-height:1.25;font-weight:400}
|
|
896
|
+
.quiz-app:has(.quiz-note) .quiz-pergunta{font-size:1em}
|
|
897
|
+
/* THE START SCREEN (the Dev: «Tela inicial para escolher a habilidade via sigla da BNCC»): each stage's name over a grid of
|
|
898
|
+
`--quiz-cols` codes (`quiz-round.SKILL_COLUMNS`), the engine's target size; a code never overflows its button. */
|
|
899
|
+
.quiz-stage{margin:0}
|
|
900
|
+
.quiz-stage-name{margin:0;font-size:1em;line-height:1.25}
|
|
901
|
+
.quiz-skills{display:grid;grid-template-columns:repeat(var(--quiz-cols,5),minmax(0,1fr));gap:calc(.25rem * var(--espaco-fixo,1));margin:0 0 calc(.25rem * var(--espaco-fixo,1))}
|
|
902
|
+
.quiz-skill{min-height:var(--alvo-min,44px);margin:0;padding:calc(var(--ui-fs,16px) * .3);overflow-wrap:anywhere;line-height:1.15}
|
|
903
|
+
.quiz-skill.is-on{box-shadow:inset 0 0 0 3px var(--accent-ink)}
|
|
545
904
|
:root[data-letras="upper"] body,
|
|
546
905
|
:root[data-letras="upper"] button,
|
|
547
906
|
:root[data-letras="upper"] select,
|
|
548
907
|
:root[data-letras="upper"] option,
|
|
549
908
|
:root[data-letras="upper"] input,
|
|
550
909
|
:root[data-letras="upper"] textarea{text-transform:uppercase}
|
|
551
|
-
/*
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
/* menus nunca ultrapassam a tela: limitados à altura do viewport e roláveis (rodapé de ações fica fixo embaixo) */
|
|
910
|
+
/* The form controls are on the list because the BROWSER's stylesheet imposes `text-transform:none` on them, and `none`
|
|
911
|
+
in a UA rule beats inheritance. Without them the capitals took the screens' text and failed on every BUTTON — which
|
|
912
|
+
is exactly where the menus' labels live. It is what the Dev saw: «não torna todas as letras do menu maiúsculas». */
|
|
913
|
+
/* menus never overflow the screen: limited to the viewport's height and scrollable (the actions footer stays fixed at the bottom) */
|
|
556
914
|
.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);
|
|
557
|
-
max-height:92dvh;overflow-y:auto;overscroll-behavior:contain} /* base = --ui-fs (16
|
|
915
|
+
max-height:92dvh;overflow-y:auto;overscroll-behavior:contain} /* base = --ui-fs (16 min, scales with the canvas); inner sizes in em */
|
|
558
916
|
.overlay__card h2{margin-top:0}
|
|
917
|
+
/* ADR-0158: «Voltar» is the panel's item 1 — a full-width row above the list, not a pill at the bottom. */
|
|
918
|
+
.overlay__back{display:flex;align-items:center;width:100%;margin:0 0 .45rem;text-align:left;border-radius:8px}
|
|
919
|
+
/* ADR-0167: a panel's stops draw no number; «Voltar»'s glyph stays, out of the accessible name (ADR-0159 rule 12). */
|
|
920
|
+
.overlay__card button[data-glifo]::before{content:attr(data-glifo) / "";margin-inline-end:.5em}
|
|
559
921
|
.topbar__right{display:flex;align-items:center;gap:.6rem;flex-wrap:wrap}
|
|
560
922
|
.title-card{text-align:center}
|
|
561
|
-
.title-big{font-size:
|
|
923
|
+
.title-big{font-size:2em;margin:.2rem 0;color:var(--accent)}
|
|
562
924
|
.title-by{color:var(--ink-soft);margin:.2rem 0 1rem}
|
|
563
|
-
.title-card .btn{margin-top:.4rem;font-size:1.
|
|
925
|
+
.title-card .btn{margin-top:.4rem;font-size:1.1em;padding:.6em 1.6em}
|
|
564
926
|
.overlay__actions{display:flex;gap:.6rem;flex-wrap:wrap;margin-top:1rem;justify-content:flex-end;
|
|
565
927
|
position:sticky;bottom:-1.5rem;background:var(--panel);padding:.6rem 0 1.5rem;margin-bottom:-1.5rem}
|
|
566
|
-
.opt-hint{color:var(--ink-soft);font-size
|
|
567
|
-
.panel-sub{text-align:left;font-size:
|
|
928
|
+
.opt-hint{color:var(--ink-soft);font-size:1em;margin:.2rem 0 .8rem}
|
|
929
|
+
.panel-sub{text-align:left;font-size:1em;margin:1rem 0 .4rem;border-bottom:1px solid var(--accent);padding-bottom:.2rem}
|
|
568
930
|
.panel-sub:first-of-type{margin-top:.4rem}
|
|
569
|
-
.panel-sub__tag{color:var(--ink-soft);font-size
|
|
570
|
-
.vol{width:140px;max-width:45%;accent-color:var(--accent);height:1.5rem;cursor:pointer} /*
|
|
571
|
-
/*
|
|
931
|
+
.panel-sub__tag{color:var(--ink-soft);font-size:1em;font-weight:400}
|
|
932
|
+
.vol{width:140px;max-width:45%;accent-color:var(--accent);height:1.5rem;cursor:pointer} /* the volume bar (range) */
|
|
933
|
+
/* Select boxes with the game's IDENTITY: the chosen face, colours and size (not the OS style) */
|
|
572
934
|
select.vol, .overlay__card select, .ctrl-col select{
|
|
573
|
-
font-family:var(--font);font-size
|
|
574
|
-
width:auto;max-width:100%;height:auto;min-height:var(--
|
|
935
|
+
font-family:var(--font);font-size:1em;font-weight:600;color:var(--ink);
|
|
936
|
+
width:auto;max-width:100%;height:auto;min-height:var(--alvo-min,44px);
|
|
575
937
|
background:#0e1326;border:2px solid var(--ink-soft);border-radius:8px;
|
|
576
938
|
padding:.35em 2em .35em .7em;cursor:pointer;
|
|
577
939
|
-webkit-appearance:none;-moz-appearance:none;appearance:none;
|
|
@@ -580,52 +942,74 @@ select.vol, .overlay__card select, .ctrl-col select{
|
|
|
580
942
|
select.vol:focus, .overlay__card select:focus{outline:3px solid var(--accent);outline-offset:1px;border-color:var(--accent)}
|
|
581
943
|
.overlay__card select option{background:#0e1326;color:var(--ink);font-weight:600}
|
|
582
944
|
.ctrl-players{display:flex;gap:.4rem;flex-wrap:wrap;justify-content:center;margin-bottom:.7rem}
|
|
583
|
-
.ctrl-players .mode-btn{padding:.3em .7em;font-size
|
|
945
|
+
.ctrl-players .mode-btn{padding:.3em .7em;font-size:1em}
|
|
584
946
|
.ctrl-list{display:flex;flex-direction:column;gap:.45rem;text-align:left}
|
|
585
947
|
.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}
|
|
586
|
-
/* ADR-0029 —
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
948
|
+
/* ADR-0029 — LEFT THE DEFAULT. Two visual channels in the same place: the frame's COLOUR and its SHAPE, which is a
|
|
949
|
+
DOUBLE frame (border, gap, ring). The transparent 2px ring reveals the element's own background and is what makes
|
|
950
|
+
the gap; without it the two rings would touch and become just a thicker border — a difference in thickness nobody
|
|
951
|
+
notices without the unmarked one beside it for comparison. The ring count survives greyscale, colour-vision
|
|
952
|
+
deficiency and every palette of the game, because it is not a hue. */
|
|
591
953
|
.is-changed{border-color:var(--changed)!important;box-shadow:inset 0 0 0 2px transparent,inset 0 0 0 4px var(--changed)}
|
|
592
|
-
/*
|
|
954
|
+
/* The mark YIELDS to the focus: losing your place is worse than losing a hint. */
|
|
593
955
|
.is-changed:focus-visible,.is-changed:focus{box-shadow:none}
|
|
594
956
|
.ctrl-row kbd{background:var(--ink);color:var(--bg);border-radius:5px;padding:.05em .45em;font-weight:700}
|
|
595
|
-
.ctrl-row.row-off{opacity:.42} /* item
|
|
957
|
+
.ctrl-row.row-off{opacity:.42} /* a disabled item (e.g. an unavailable keyboard mode) = light grey only, no extra text */
|
|
596
958
|
.ctrl-row .mode-btn[disabled]{cursor:not-allowed;opacity:.7}
|
|
597
959
|
.ctrl-row.ctrl-col{flex-direction:column;align-items:stretch;gap:.4rem}
|
|
598
960
|
.ctrl-row.ctrl-col label{font-weight:700}
|
|
599
961
|
.pad-presets{display:flex;gap:.5rem;flex-wrap:wrap}
|
|
600
|
-
.pad-tag{font-size
|
|
962
|
+
.pad-tag{font-size:1em;font-weight:700;padding:.05em .5em;border-radius:999px;background:#20304e;color:#bcd}
|
|
601
963
|
.pad-tag[data-who="crianca"]{background:#2a4a2e;color:#bfe9c2}
|
|
602
964
|
.pad-tag[data-who="adulto"]{background:#2a3a5e;color:#c3d4f0}
|
|
603
|
-
.pad-warn{font-size
|
|
604
|
-
.ctrl-col input[type=range]{width:100%;min-height:var(--
|
|
965
|
+
.pad-warn{font-size:1em;color:#ffdf9e;background:#3a2e12;border:1px solid #6a5220;border-radius:8px;padding:.45rem .6rem}
|
|
966
|
+
.ctrl-col input[type=range]{width:100%;min-height:var(--alvo-min,44px);accent-color:var(--accent)}
|
|
605
967
|
|
|
606
|
-
/*
|
|
607
|
-
.touch{position:absolute;inset:
|
|
608
|
-
.touch
|
|
609
|
-
|
|
968
|
+
/* touch controls (mobile) — an overlay on the bottom edges, not covering the centre */
|
|
969
|
+
.touch{position:absolute;inset:0;z-index:14;display:flex;justify-content:space-between;align-items:flex-end;gap:8px;padding:var(--margem-borda,8px);pointer-events:none}
|
|
970
|
+
/* ADR-0160: the shoulders in the TOP corners of the region, the trigger (2) above the bumper (1). `.touch` covers the whole
|
|
971
|
+
region since then (`inset:0`, still `pointer-events:none`) so a corner is the region's corner, not the pad strip's. */
|
|
972
|
+
.touch-ombros{position:absolute;top:var(--margem-borda,8px);display:flex;flex-direction:column;gap:6px;pointer-events:auto}
|
|
973
|
+
.touch-ombros--esq{left:var(--margem-borda,8px)}
|
|
974
|
+
.touch-ombros--dir{right:var(--margem-borda,8px)}
|
|
975
|
+
/* ADR-0160: the action buttons sit by ACTION NUMBER — 1 and 4 on top, 2 and 3 below — whatever slot fires them.
|
|
976
|
+
`margin-left:auto` keeps the block on the right when the game names no direction (ADR-0162). */
|
|
977
|
+
.touch-pad{display:grid;grid-template-areas:"a1 a4" "a2 a3";gap:8px;margin-left:auto;pointer-events:auto}
|
|
978
|
+
.touch-btn[data-acao="action1"]{grid-area:a1}
|
|
979
|
+
.touch-btn[data-acao="action4"]{grid-area:a4}
|
|
980
|
+
.touch-btn[data-acao="action2"]{grid-area:a2}
|
|
981
|
+
.touch-btn[data-acao="action3"]{grid-area:a3}
|
|
982
|
+
.touch-btn{width:56px;height:56px;min-width:var(--alvo-min,44px);min-height:var(--alvo-min,44px);border-radius:12px;border:2px solid rgba(255,255,255,.75);background:rgba(13,17,32,.92);color:#fff;font-size:1.4em;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}
|
|
610
983
|
.touch-btn:active{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
|
|
611
984
|
.touch-jump{background:rgba(18,74,52,.94)}
|
|
612
|
-
/*
|
|
985
|
+
/* Action buttons in a DIAMOND (4 bubbles: 0 bottom, 1 right, 2 left, 3 top) + a START pill */
|
|
613
986
|
.touch-actions{align-items:center}
|
|
614
|
-
/*
|
|
615
|
-
(
|
|
987
|
+
/* PHYSICAL size (mm), not px: --pad-btn and --pad-diam are computed by `input/touch` from real millimetres
|
|
988
|
+
(reference: an iPhone 16 filling the screen). These are the fallbacks before the JS runs. */
|
|
616
989
|
.pad-diamond{position:relative;width:var(--pad-diam,182px);height:var(--pad-diam,182px);pointer-events:none}
|
|
617
990
|
.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}
|
|
618
991
|
.pad-top{top:0;left:50%;transform:translateX(-50%)}
|
|
619
992
|
.pad-bottom{bottom:0;left:50%;transform:translateX(-50%)}
|
|
620
993
|
.pad-left{left:0;top:50%;transform:translateY(-50%)}
|
|
621
994
|
.pad-right{right:0;top:50%;transform:translateY(-50%)}
|
|
622
|
-
|
|
995
|
+
/* `width:auto`: the pill is also a `.touch-btn`, whose fixed 56px cut the word — measured in the dist, «START» with 66px
|
|
996
|
+
of text in a 56px button at the cursive hand's 1.25 scale (ADR-0149). The pill grows with the word, and the floor is the target. */
|
|
997
|
+
/* The two system pills, SELECT and START, in a container at the centre (ADR-0155). Inside it they no longer place
|
|
998
|
+
themselves: the container puts them side by side, and each keeps the width of its text. */
|
|
999
|
+
/* 🔴 WITH A PANEL OPEN, THE PAD SITS ABOVE IT (ADR-0157): its directional is what moves in the panel. The panel's veil
|
|
1000
|
+
(`#game-region .overlay`, 60) covered the pad (14), and a tap on an arrow landed on the veil. `--z-menu-max` is the top
|
|
1001
|
+
of the menu band, and that is the pad's role while a menu is open — a slot and not a new literal. */
|
|
1002
|
+
#game-region:has(.overlay:not([hidden])) .touch{z-index:var(--z-menu-max)}
|
|
1003
|
+
/* the two pills stand the edge margin off the bottom (interface log 2026-09-26) — alone or side by side */
|
|
1004
|
+
.touch-sistema{position:absolute;left:50%;bottom:var(--margem-borda,8px);transform:translateX(-50%);display:flex;gap:10px;pointer-events:none}
|
|
1005
|
+
.touch-sistema .touch-start,.touch-sistema .touch-select{position:static;transform:none}
|
|
1006
|
+
.touch-start,.touch-select{position:absolute;left:50%;bottom:var(--margem-borda,8px);transform:translateX(-50%);width:auto;min-width:var(--alvo-min,44px);white-space:nowrap;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:1em;letter-spacing:var(--ls);background:rgba(13,17,32,.92);color:#fff;border:2px solid rgba(255,255,255,.75);cursor:pointer;z-index:15}
|
|
623
1007
|
.touch-start:active{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
|
|
624
|
-
/* joystick
|
|
625
|
-
/*
|
|
1008
|
+
/* digital joystick: base (larger circle) + knob (smaller circle) */
|
|
1009
|
+
/* Directional (analogue/"lever"): base = surface + travel; knob = contact surface. Both in mm (JS). */
|
|
626
1010
|
.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}
|
|
627
1011
|
.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}
|
|
628
|
-
/* D-pad
|
|
1012
|
+
/* Cross D-pad (an alternative style): 4 arms + a core; the arm's size in mm (JS → --dpad-arm-l/w, --dpad-span) */
|
|
629
1013
|
.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}
|
|
630
1014
|
.touch-cross[hidden]{display:none}
|
|
631
1015
|
.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}
|
|
@@ -641,4 +1025,4 @@ select.vol:focus, .overlay__card select:focus{outline:3px solid var(--accent);ou
|
|
|
641
1025
|
@media (prefers-reduced-motion: reduce){ .touch-knob{transition:none} }
|
|
642
1026
|
@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} }
|
|
643
1027
|
|
|
644
|
-
@media (max-width:560px){.title{font-size:1.
|
|
1028
|
+
@media (max-width:560px){.title{font-size:1.1em}.hud{gap:1rem}}
|