# Voluntary Product Accessibility Template (VPAT) 2.4 — Pip's Arcade

**Product:** WiseBoard — Pip's Arcade (student game suite)
**Version evaluated:** mywiseboardclassroom.com (April 2026 release)
**Date:** May 6, 2026 (most recent refresh; initial publication April 25, 2026)
**Author:** Chaz Landry, Operator
**Contact:** info@mywiseboardclassroom.com

> Companion document to the main WiseBoard VPAT 2.4 (`vpat-2.4.md`). Audits the
> student game canvases that were intentionally out of scope for the April 2026
> main VPAT. Based on the **VPAT 2.4 INT** template by ITI.

## Why a separate VPAT

Pip's Arcade is a suite of student-facing classroom games projected to the
front of the room and played on student devices. Each game uses bespoke
animated canvases (CSS-animated DOM, R3F/Three.js scenes, or hybrid grids)
with game-specific keyboard mappings — different in nature from the
Dashboard / Lessons / Performance Tracker surfaces evaluated in the main VPAT.

District procurement teams asking "Can a screen-reader student play in
Arcade?" or "Can a teacher with vestibular disorders run a Horse Racing
session safely?" deserve an answer that is specific to these surfaces, not
buried in the broader product VPAT.

## Applicable Standards/Guidelines

| Standard / Guideline | Included In Report |
|---|---|
| Web Content Accessibility Guidelines 2.2, Level A and AA | Yes |
| Revised Section 508 standards (36 C.F.R. § 1194 Appx. A & C) | Yes (web only) |
| EN 301 549 v3.2.1 (2021-03) | Partially — web app criteria |

## Terms

- **Supports** — meets the criterion without exception across the audited surfaces.
- **Partially Supports** — meets the criterion with documented exceptions tracked in the remediation backlog.
- **Does Not Support** — does not meet the criterion.
- **Not Applicable** — criterion does not apply to the functionality.

## Scope of evaluation

Audited surfaces (April 2026):

| Game | Files audited | Rendering |
|---|---|---|
| Pip's Railroad | `artifacts/classroom/src/pages/arcade/railroad.tsx` | DOM + SVG (USMap) |
| Pocket Owl | `artifacts/classroom/src/pages/arcade/pocket-owl.tsx`, `_pocket-owl-portrait.tsx` | DOM + SVG portrait |
| Quest | `artifacts/classroom/src/pages/arcade/quest.tsx`, `quest-3d-preview.tsx` | DOM grid + R3F preview |
| Boss Battle | `artifacts/classroom/src/pages/boss-battle/{play,display,BossScene}.tsx` | DOM controls + R3F display |
| Jeopardy | `artifacts/classroom/src/pages/jeopardy/{game,board-3d,cinematic-3d}.tsx` | DOM controls + R3F board |
| Escape Room | `artifacts/classroom/src/pages/escape-room/game.tsx` | DOM dashboard |
| Two-Minute Drill | `artifacts/classroom/src/pages/two-minute-drill/game.tsx` | DOM gridiron |
| Horse Racing | `artifacts/classroom/src/pages/horse-racing/game.tsx` | DOM track |
| Capture the Flag | `artifacts/classroom/src/pages/capture-flag/index.tsx` | DOM |
| Survivor | `artifacts/classroom/src/pages/survivor/{game,tribal-3d,cinematic-3d}.tsx` | DOM + R3F cinematics |
| WiseUp | `artifacts/classroom/src/pages/wise-up/scenes-3d.tsx` | DOM controls + R3F scenes |

Out of scope: SDK-level changes to React Three Fiber, three.js, or Drei
(third-party rendering libraries that backstop our 3D scenes).

## Evaluation methodology

- **Automated:** `eslint-plugin-jsx-a11y` in CI; manual axe DevTools sweeps on
  representative surfaces (Horse Racing, Boss Battle display, Escape Room,
  Two-Minute Drill, Jeopardy game).
- **Manual:** keyboard-only walkthroughs of the teacher control surfaces;
  visual review of color-contrast on player-facing text; verification of
  reduced-motion behavior with the OS preference toggled on macOS Safari and
  Chrome via DevTools "Emulate CSS prefers-reduced-motion: reduce".
- **Screen-reader spot checks:** VoiceOver on macOS Safari for the teacher
  control surfaces of Boss Battle, Horse Racing, and Escape Room (the
  surfaces a non-sighted educator is most likely to operate live).

## Table 1 — WCAG 2.2 Level A & AA

| Criterion | Conformance | Remarks |
|---|---|---|
| 1.1.1 Non-text Content | Supports | Pocket Owl portrait (`_pocket-owl-portrait.tsx`) has `role="img"` + descriptive `aria-label`. Decorative emoji in Two-Minute Drill / Escape Room banners marked `aria-hidden`. R3F `<Canvas>` in Boss Battle is `aria-hidden="true"`/`role="presentation"` because boss name + HP + roster are mirrored in accessible DOM. R3F `<Canvas>` in Quest 3D preview (`quest-3d-preview.tsx`) is also `aria-hidden="true"` with an SR-only equivalent text description of the scene. **April 29, 2026 update:** Jeopardy R3F scenes now also `aria-hidden="true"` + `role="presentation"` — `board-3d.tsx` (the host clue grid; the keyboard overlay carries the accessible representation), and all four `cinematic-3d.tsx` scenes (Lobby, Daily Double splash, Clue spotlight, Game-over winner; the host page's leaderboard sidebar, room-code header, clue panel, and Daily Double DOM banner each carry the accessible mirror). **April 30, 2026 update — Survivor closed:** the Survivor `TribalCouncil3D` (`tribal-3d.tsx`) and the three `cinematic-3d.tsx` scenes (Campfire Lobby, Vote Reveal, Sole Survivor) are now wrapped in `aria-hidden="true"` + `role="presentation"` — the host page's standings list, room-code header, vote tally, and "tribe has spoken" overlay each carry the accessible mirror. Regression coverage: `__tests__/survivor-cinematic-a11y.test.tsx` (4 cases). All R3F surfaces in Pip's Arcade now ship the same invariant; promoted from "Partially Supports" → "Supports". **May 6, 2026 update — WiseUp closed (Task #299):** the four 3D WiseUp scenes (`pages/wise-up/scenes-3d.tsx` — `QuestionStage3D`, `PodiumStage3D`, `LobbyScene3D`, `MidLeaderboard3D`) now pass non-empty `ariaLabel` + `srOnlyDescription` props to the shared `SceneShell`, completing the WCAG 1.1.1 equivalent-text story for every R3F surface in Pip's Arcade. The descriptions mirror the visible state (answer-tile letters and revealed correct answer; podium ranks, names, and scores; lobby room code and joined... |
| 1.2.x Audio/Video | Not Applicable | Arcade has no prerecorded audio/video; in-game sound effects are decorative and toggleable via `SoundToggle`. |
| 1.3.1 Info and Relationships | Partially Supports | Headings, labels, and roster lists use semantic markup. **Gap:** the Escape Room progress grid and Two-Minute Drill scoreboard use nested `div`s instead of `<ul>` / `<table>`; assistive tech reads them as a flat blob. |
| 1.3.2 Meaningful Sequence | Supports | Reading order matches DOM order on every audited surface. |
| 1.3.3 Sensory Characteristics | Supports | Game state never relies on color alone — leader is also labeled "Leader: {name}" via the new `aria-live` region in Horse Racing; touchdown event also announced "Touchdown by {name}!" |
| 1.3.4 Orientation | Supports | All games adapt to portrait and landscape. |
| 1.3.5 Identify Input Purpose | Not Applicable | Game canvases collect no PII inputs (room code is the only input; no autocomplete category applies). |
| 1.4.1 Use of Color | Supports | Jockey colors / category colors are paired with name labels. |
| 1.4.2 Audio Control | Supports | All in-game audio is mute-able via the in-game `SoundToggle` and respects user preference across sessions. |
| 1.4.3 Contrast (Minimum) | Supports | Body text + score panels verified ≥ 4.5:1 against game backgrounds. **April 30, 2026 update — Capture-the-Flag closed:** spot check found the question-set empty-state body copy at 25% white (~2.0:1 against the near-black page) and the "NO QUESTION SETS YET" header at 35% white (~2.7:1) — both below the 4.5:1 / 3:1 minimums. The room-code modal close-button icon was at 45% white (~3.6:1), borderline for normal text. Bumped to 60% / 70% / 75% white respectively, which clears 4.5:1 normal-text and 3:1 non-text-UI thresholds against the dark background. The decorative flag emoji is now also explicitly `aria-hidden="true"`. The disabled-state launch button intentionally retains its low-opacity color — WCAG 1.4.3 explicitly exempts inactive UI components. CTF only ships one accent theme (green `#22c55e`); the prior "varies by theme" framing is obsolete. Promoted from "Partially Supports" → "Supports". |
| 1.4.4 Resize Text | Supports | 200% browser zoom does not break layout; absolute-positioned game elements (horses, dust) stay relative to the track. |
| 1.4.5 Images of Text | Supports | All text rendered as text. |
| 1.4.10 Reflow | Partially Supports | DOM-rendered surfaces reflow at 320 CSS px. **Gap:** R3F canvases in Jeopardy and Survivor target 1280-wide projector viewports and may overflow on small windows; this is by design (projector use case) but is documented for districts using small touchscreens. |
| 1.4.11 Non-text Contrast | Supports | Two-Minute Drill yard-line background pattern bumped from 6% → 12% opacity in this audit pass to clear 3:1 against the dark BG. **April 30, 2026 update — Horse Racing & Escape Room focus rings closed:** Horse Racing "End Race" and Escape Room "End Game" buttons now ship the explicit `focus:outline-none focus-visible:ring-2 focus-visible:ring-amber-300 focus-visible:ring-offset-2 focus-visible:ring-offset-black` upgrade (amber `#fcd34d` measures ~8.8:1 against the dark button backgrounds, well above the 3:1 minimum). `focus-visible` (not plain `:focus`) avoids drawing the ring on mouse activation, matching the pattern used elsewhere in the app. Promoted from "Partially Supports" → "Supports". |
| 1.4.12 Text Spacing | Supports | No fixed line-height or letter-spacing that breaks at user override. |
| 1.4.13 Content on Hover or Focus | Supports | No hover-revealed tooltips inside game canvases (game state is always on-screen). |
| 2.1.1 Keyboard | Supports | Quest supports full WASD/Arrow movement (`quest.tsx` lines 489-509). Railroad has Esc-to-exit-fullscreen. Teacher control surfaces (Start, End, Copy Code) are standard buttons reachable by Tab. **Closed in this audit pass (A6):** the 3D Jeopardy board now ships a DOM accessibility overlay (`board-3d.tsx` `JeopardyBoardKeyboardOverlay`) — each cell is reachable via Tab in left-to-right / top-to-bottom order, Enter / Space picks the focused clue (same code path as the mouse click), played cells use the native `disabled` attribute so the browser skips them in tab order, and the focus ring is a 3px amber `focus-visible` outline (≥ 3:1 against the dark blue tiles, see 1.4.11). Active-clue panel buttons (Correct, Wrong, Skip) are standard `<button>`s reachable by Tab without a focus trap. The Survivor immunity-challenge cinematic (`survivor/cinematic-3d.tsx`) is non-interactive playback with no operable controls and is therefore not in scope for this criterion. |
| 2.1.2 No Keyboard Trap | Supports | No focus traps anywhere in Arcade. Esc closes fullscreen overlays. |
| 2.1.4 Character Key Shortcuts | Supports | Quest's WASD movement is only active inside the game canvas; no global single-character shortcuts. |
| 2.2.1 Timing Adjustable | Partially Supports | Two-Minute Drill, Boss Battle, and Horse Racing are by design time-based games; teachers can end the round early via "End Game" / "End Race". **Gap:** there is no per-round extension UI for accommodations (extended-time IEPs); a teacher today must either skip these games or end and restart. |
| 2.2.2 Pause, Stop, Hide | Supports | Most games have a visible "End" button. Pip's Railroad weather overlay (rain particles, dust) honors `prefers-reduced-motion` via the shared `ParticleLayer` canvas (`src/components/game-fx/ParticleLayer.tsx`) — when the OS preference is set at page load, the particle render loop is never started and the daily weather is shown as a static tint only. (Note: `ParticleLayer` reads the preference once at mount; toggling the OS setting mid-session requires a page reload to take effect on weather particles. Live response is tracked as a non-blocking refactor.) |
| 2.3.1 Three Flashes or Below Threshold | Supports | Verified: no game flashes more than 3 times per second. Two-Minute Drill TD-flash overlay has been audited at ~0.5 Hz peak. |
| 2.3.3 Animation from Interactions (AAA — voluntarily applied) | Supports | This audit added `prefers-reduced-motion` gating to: Horse Racing gallop / dust / yard-float / lane-pulse keyframes; Two-Minute Drill TD-in / TD-out / TD-flash / fade-in / pulse keyframes; Escape Room er-pop / er-out keyframes. Pocket Owl portrait already honored the preference. **Closed in this audit pass (A1, A2):** Pip's Railroad weather effects (rain particles + dust) honor the preference via the shared `ParticleLayer`; the Railroad `ScreenShake` wrapper (`railroad.tsx`) skips the shake transform when the preference is on; and Boss Battle's R3F `useFrame` callbacks branch on the preference — the boss creature locks to a still neutral pose (no idle bob, rage tilt, defeat tumble, ring spin, limb sway, or aura pulse), the camera holds its base pose (no orbit, hit-shake, or victory/defeat zoom), the `<Float>` wrapper is set to zero intensity, and the `<Sparkles>` particles are frozen. Hit-flash emissive decay is preserved as a discrete state change in response to a player action. **Also closed in this audit pass (A3-style — same pattern applied to the remaining 3D arcade canvases):** Jeopardy 3D board (`board-3d.tsx` `ClueTile` flip / lift / scale animations snap to the open / closed target with no easing), Jeopardy cinematic (`jeopardy/cinematic-3d.tsx` lobby `BevelledTile` bob, lobby `Player3DCard` bob, Daily Double dollar-sign rotation + scale pulse, Gold Shower falling particles, Clue Spotlight fly-in / idle bob, Winner `Fireworks3D` / `Confetti3D` / `StagePodium` rise & sway), Quest 3D preview (`quest-3d-preview.tsx` `PipBillboard` bob, `Windmill` blade rotation, `Fountain` water-opacity shimmer, ambient `Sparkles`, `Stars`, drei `Float` chests — also replaces a duplicated inline reduced-motion hook with the shared `@/hooks/use-prefers-reduced-motion`), Survivor tribal council (`survivor/tribal-3d.tsx` `Torch` flame particles / tilt / point-light pulse), Survivor cinematic (`survivor/c... |
| 2.4.1 Bypass Blocks | Supports | App-shell skip-to-main link covers the arcade pages. |
| 2.4.2 Page Titled | Supports | Each game route sets a `<title>` via `PageMeta`. |
| 2.4.3 Focus Order | Supports | Tab order matches visual order on teacher control surfaces. |
| 2.4.4 Link Purpose (In Context) | Supports | "Back to Races" / "Back to Rooms" buttons name their destination. |
| 2.4.6 Headings and Labels | Supports | Each game page has an `<h1>` naming the game; sub-sections use `<h2>`/`<h3>`. |
| 2.4.7 Focus Visible | Supports | Default Tailwind focus ring preserved on all standard buttons. See 1.4.11 for game-specific exceptions. |
| 2.4.11 Focus Not Obscured (Minimum) — WCAG 2.2 | Supports | No sticky overlays cover focused controls. |
| 2.5.1 Pointer Gestures | Supports | All game interactions are single-tap / single-click. No pinch-zoom required. |
| 2.5.2 Pointer Cancellation | Supports | Up-event activation throughout (standard buttons). |
| 2.5.3 Label in Name | Supports | Visible button labels match accessible names. |
| 2.5.4 Motion Actuation | Not Applicable | No motion-actuated functionality. |
| 2.5.7 Dragging Movements — WCAG 2.2 | Not Applicable | No drag-and-drop in any audited game (Quest movement is keyboard / tap, not drag). |
| 2.5.8 Target Size (Minimum) — WCAG 2.2 | Supports | Teacher control buttons ≥ 44×44 CSS px; student answer buttons in Boss Battle / Two-Minute Drill / Jeopardy ≥ 44×44. |
| 3.1.1 Language of Page | Supports | Inherits `<html lang="en">` from app shell. |
| 3.1.2 Language of Parts | Not Applicable | Arcade game canvases render English-only labels. Multilingual parent / family content lives outside the Arcade scope (covered by the main product VPAT, row 3.1.2). |
| 3.2.x On Focus / Input / Consistent | Supports | No context changes on focus or input. |
| 3.3.1 Error Identification | Not Applicable | No data-entry forms inside game canvases (room-code entry is on the join page, covered by main VPAT). |
| 3.3.7 Redundant Entry — WCAG 2.2 | Supports | Player name carried from join page. |
| 3.3.8 Accessible Authentication (Minimum) — WCAG 2.2 | Not Applicable | Students enter Arcade via room code, not auth. |
| 4.1.2 Name, Role, Value | Partially Supports | Standard buttons + Radix primitives expose correct roles/states. **Gap:** the Pocket Owl breed-selection tabs and Class Aviary list (per audit) could expose `aria-selected` for tab semantics. |
| 4.1.3 Status Messages | Supports | This audit added `role="status" aria-live="polite"` regions for Horse Racing leader / winner, Two-Minute Drill TOUCHDOWN events, and Escape Room ESCAPED events. **April 29, 2026 update:** Jeopardy now ships a single SR-only polite live region (`buzz-announcer.tsx`, mounted in `pages/jeopardy/game.tsx`) that announces both buzz-ins ("Maya buzzed in") *and* the teacher's adjudication outcome ("Correct!" / "Wrong" / "Skipped"). The host page resets the adjudication state ~1.5 s after each set so a second identical adjudication still re-speaks. **May 3, 2026 update — Jeopardy clue reveal closed (Task #178):** the same file now also exports `JeopardyClueAnnouncer`, a sibling polite SR-only region mounted next to the buzz announcer in `pages/jeopardy/game.tsx`. The moment `game.currentClueId` transitions from null to a clue (alongside the existing `sound.play("reveal")` / `sound.play("critical")` calls), the announcer speaks "{category}, ${points}. {clue text}" — and prefixes "Daily Double — " when the picked tile is a Daily Double, so the SR-only teacher hears the stakes before reading the clue aloud. The host clears the reveal state when the clue closes (currentClueId → null) so the next opening can fire a fresh announcement; the wrapper is keyed on a monotonic nonce so two consecutive openings whose announcement text would be byte-identical (e.g. accidentally re-picking the same dollar value, or two clues that share text across categories) both re-speak. Regression coverage: `__tests__/jeopardy-buzz-announcer.test.tsx` adds 7 cases for the new component (empty, basic reveal, clear-on-close, Daily Double prefix, nonce-keyed double-speak, axe pass with content, axe pass when empty). **April 30, 2026 update — Boss Battle & Survivor closed:** `pages/boss-battle/event-announcer.tsx` ships a polite SR-only region announcing "First hit by {player}!" (once per session), "Critical hit by {player}!" (every crit), and "Boss defeated!" (on the ...

## Table 2 — Section 508 (Revised) — Chapter 5 (Software)

Web app conforms to Chapter 5 by way of WCAG 2.2 conformance above. No
native (compiled) software is in scope.

## Table 3 — EN 301 549 (selected)

WCAG 2.2 mapping covers EN 301 549 §§ 9.1–9.4. Functional performance
statements (§ 4.2): the keyboard, screen-reader, and zoom support documented
above is partial for Arcade — districts whose AT-using students play Arcade
should review the gaps below before adoption.

## Remediation backlog (open items)

| # | Item | Severity | Target | Notes |
|---|---|---|---|---|
| A1 | Add `prefers-reduced-motion` branching to Pip's Railroad weather effects (rain, dust) and `useScreenShake` hook | Medium | Q3 2026 | **Done — shipped April 27, 2026.** Weather particles are gated inside the shared `ParticleLayer` (`src/components/game-fx/ParticleLayer.tsx`) — the canvas mounts but the render loop never starts when the OS preference is on, leaving the daily weather as a static color tint only. The Railroad `ScreenShake` wrapper (`src/pages/arcade/railroad.tsx`) consumes `usePrefersReducedMotion` and skips the 0.45 s shake transform when the preference is on. Status promoted to "Supports" in the 2.2.2 and 2.3.3 rows above. **April 29, 2026 follow-up — Task #91 (live toggle):** `ParticleLayer` previously read `matchMedia(...).matches` once inside a mount-only effect, so toggling macOS "Reduce motion" while the Railroad map was already open had no effect until a full page reload. The component now consumes the shared `usePrefersReducedMotion` hook and re-runs its animation effect when the preference flips: turning the preference on cancels the in-flight RAF loop and clears the canvas immediately, turning it back off rebuilds the loop with a fresh particle pool. The hook subscribes to `change` events and the effect cleanup tears down both the RAF and the `ResizeObserver` on each transition, so no observers or frames leak. Today the only `ParticleLayer` consumer is the Railroad map; the fix lives in the shared component so any future consumer inherits the live-toggle behavior automatically. Regression coverage: `__tests__/particle-layer-reduced-motion.test.tsx` drives the media query directly and asserts both the freeze and resume transitions schedule / cancel RAF without any reload. **April 29, 2026 — Task #92 (`ScreenShake` regression coverage):** added `__tests__/railroad-screen-shake.test.tsx` (3 cases) which mounts the now-exported `ScreenShake` component, mocks `usePrefersReducedMotion`, and asserts (a) with reduced-motion ON the `animate-[rr... |
| A2 | Add `prefers-reduced-motion` branching to `BossScene.tsx` `useFrame` animations (idle bob, rage spin, defeat tumble) | Medium | Q3 2026 | **Done — shipped April 27, 2026.** `src/pages/boss-battle/BossScene.tsx` consumes `usePrefersReducedMotion` and threads `reduced` into `BossCreature` and `CameraRig`. When the preference is on: the boss locks to a still neutral pose (no idle bob, rage tilt, defeat tumble, ring spin, limb sway, or aura pulse), the camera holds its base pose (no orbit, hit-shake, or victory/defeat zoom), the `<Float>` wrapper is set to zero intensity, and the `<Sparkles>` particles are frozen. Hit-flash emissive decay is preserved as a discrete state change in response to a player action. Status promoted to "Supports" in the 2.3.3 row above. **April 29, 2026 follow-up — Task #92 (regression coverage):** added `__tests__/boss-scene-reduced-motion.test.tsx` (5 cases) which mocks `@react-three/fiber`, `@react-three/drei`, `@react-three/postprocessing`, and `three` so happy-dom can mount the scene and asserts `<Float>` ships `speed=0` / `floatIntensity=0` / `rotationIntensity=0` and `<Sparkles>` ships `speed=0` whenever the OS preference is on, plus the matching non-zero values when off, plus the `aria-hidden="true"`+`role="presentation"` invariant for the outer wrapper. A future refactor that re-introduces the idle bob, ring spin, sparkle drift, or camera orbit will now trip CI. The Railroad's `ScreenShake` wrapper (item A1) gets matching coverage in `__tests__/railroad-screen-shake.test.tsx` (3 cases) — see that row for details. |
| A3 | Add `aria-hidden="true"` to R3F `<Canvas>` in `jeopardy/board-3d.tsx`, `jeopardy/cinematic-3d.tsx`, `arcade/quest-3d-preview.tsx`, `survivor/tribal-3d.tsx`, `survivor/cinematic-3d.tsx` | Medium | Q3 2026 | Same pattern as `BossScene.tsx`. **Partially closed — `prefers-reduced-motion` portion shipped April 28, 2026.** All five files now consume `usePrefersReducedMotion` from `@/hooks/use-prefers-reduced-motion` and thread `reduced` through every `useFrame` callback (including the eight shared `game-3d/*` components, which now accept an optional `reduced` prop). The Quest preview's previously-duplicated inline hook was deleted in favor of the shared one so all arcade surfaces track the same source of truth. See the 2.3.3 row above for the full per-scene inventory. **April 29, 2026 update — Jeopardy `aria-hidden` portion closed (Task #88):** `board-3d.tsx` and the four `cinematic-3d.tsx` scenes (Lobby, Daily Double splash, Clue spotlight, Game-over winner) now wrap their `SceneShell` in `aria-hidden="true"` + `role="presentation"`. Regression coverage: `__tests__/jeopardy-keyboard-overlay.test.tsx` (board) and `__tests__/jeopardy-cinematic-a11y.test.tsx` (the four cinematic scenes). **April 30, 2026 — fully closed:** the same wrappers shipped on the Survivor `TribalCouncil3D` (`tribal-3d.tsx`) and the three `cinematic-3d.tsx` scenes (Campfire Lobby, Vote Reveal, Sole Survivor). Regression coverage: `__tests__/survivor-cinematic-a11y.test.tsx` (4 cases). Status promoted to "Supports" in the 1.1.1 row above. **May 6, 2026 follow-up — WiseUp closed (Task #299):** the four 3D WiseUp scenes (`pages/wise-up/scenes-3d.tsx`) — previously omitted from the May 2 sweep — now pass non-empty `ariaLabel` + `srOnlyDescription` to `SceneShell`. Regression coverage: `__tests__/wise-up-cinematic-a11y.test.tsx` (12 cases). All R3F surfaces in Pip's Arcade now ship the equivalent-text alternative. **May 2, 2026 follow-up — equivalent text alternative shipped (Task #137):** the orig...
