Landing Pagexiaomi/mimo-v2.6-proverified 20/20partial cross-check
Echo
A rotating sonar sweep lights phosphor contacts over expanding ping rings on canvas
The exact prompt
A. Build one complete valid index.html for a single-viewport landing page for a marine-robotics company named Echo. Document title: `Echo — Sonar Autonomy for the Deep`. Set `<html lang="en">`. ONE file only: plain HTML, inline CSS, one small inline script at the end of body. No build step, no framework, no bundler. Google Fonts is the only permitted network request (system fallback stack required). Every visible pixel comes from CSS gradients, inline SVG, text, and one `<canvas>`. Desktop is one frame with no scroll at `>= 901px`: `html, body { height:100%; overflow:hidden }` and `.page { height:100dvh; overflow:hidden }`. Width `<= 900px` may scroll vertically but must never overflow horizontally. Accessibility is mandatory: real `header`, `nav`, `main`, `footer`; exactly one `h1`; every visible control a named button or anchor; a visible `:focus-visible` ring; readable contrast. The runtime telemetry hook must be exactly `window.__DEMO__ = { name: 'echo', ready: false, frames: 0, effects: [], errors: [] };` and the script must set `ready` true after the first painted sonar frame, increment `frames` once per animation frame inside the requestAnimationFrame loop, push each implemented effect name into `effects`, and attach a window `'error'` listener that pushes into `errors`. Zero console output, no uncaught exceptions, finished index.html under 90 KB. Do NOT build: no video, no WebGL, no Three.js, no Lottie, no GSAP, no jQuery, no Tailwind, no React, no images (remote or base64), no audio, no external scripts or third-party CDN URLs, no forms or inputs, no pricing tables, no testimonials, no world maps, no dashboards, no extra sections beyond header / hero / footer, no footer links beyond the three specified, no cookie banner, no scroll indicator, no hamburger menu above 900px. The single visual idea: a full-bleed canvas sonar scope — concentric ping rings expanding from a fixed origin, a rotating sweep line trailing a fading phosphor wedge, contact blips igniting as the sweep crosses them and decaying over seconds — behind quiet marine-instrument typography.
---
## B. THE BACKDROP — procedural sonar scope (the ONLY visual effect besides grain)
A single `<canvas id="sonar">` fills the viewport: `position:fixed; inset:0; width:100%; height:100%; z-index:0; display:block; pointer-events:none`, repainted every `requestAnimationFrame` tick and NEVER a static image.
**Geometry (canvas 2D, no libraries):**
- Origin `(cx, cy)`, `>= 901px`: `cx = w * 0.70`, `cy = h * 0.52` (scope right of the copy column). `<= 900px`: `cx = w * 0.50`, `cy = h * 0.40`.
- Scope radius `R = min(w*0.30, h*0.50)` at `>= 901px` (432px at 1440×900); `R = min(w*0.46, h*0.34)` at `<= 900px`.
- Paint order per frame, back to front: navy fill, graticule blit, 4 ping rings, 8 trail buckets, leading edge, 30 contact blips, edge-fade gradient, vignette.
**Graticule (drawn ONCE per resize into an offscreen canvas, blitted once per frame):**
- 6 concentric range rings at `R * k / 6` for `k = 1..6`, `lineWidth 1`, stroke `rgba(92,255,157,0.13)`.
- 36 bearing ticks every 10°: 1px segments `R → R + 8px` at alpha 0.16; every third tick (30°) runs `R → R + 14px` at alpha 0.26.
- Four mono bearing labels, drawn once at radius `R + 26px`, 10px, fill `rgba(92,255,157,0.30)`: `000` top, `090` right, `180` bottom, `270` left.
- The offscreen canvas matches the main canvas backing size; per-frame cost is exactly one `drawImage`.
**Ping rings — 4 expanding waves:**
- Wave count `P = 4`, ping period `5.5s`. Wave `k` has phase `pk = ((t / 5.5) + k / 4) % 1` for `k = 0..3` (a new ring every 1.375s).
- Radius `r_k = R * (0.06 + 0.94 * pk^0.82)` — the exponent front-loads expansion so rings decelerate like a real wavefront.
- Stroke: `lineWidth 1` (`2` when `pk < 0.12`, the newborn ring), colour `rgba(92,255,157, alpha)` with `alpha = 0.42 * (1 - pk)^1.5 + 0.02`; one `beginPath` per ring, 4 strokes per frame.
**Sweep and its fading trail:**
- Leading angle `a = (t / 6.4) * TAU - PI/2` (`TAU = Math.PI * 2`; bearing 000 at top, clockwise; one revolution every 6.4s).
- The trail is a wedge spanning `56°` BEHIND the leading edge, split into `8` alpha buckets of `7°` each. Bucket `j = 0..7` covers `[a - (j+1)*7°, a - j*7°]` and fills `rgba(92,255,157, alpha_j)` with `alpha_j = 0.14 * (1 - j/8)^1.7 + 0.012`. One `moveTo; arc; closePath; fill` per bucket — 8 fills per frame, no `filter`/`shadowBlur`.
- Leading edge: one 1px line origin → `R + 10px` at `rgba(92,255,157,0.55)` plus a 2.5px tip dot at alpha 0.9.
**Contact blips — 30 targets (22 at `<= 900px`, 16 at `<= 560px`):**
- Generated ONCE at startup by mulberry32 (seed `20260214`) for identical loads: `ang` in `[0, TAU)`, `rad = 0.10 + rng() * 0.84` of R, `size = 0.8 + rng() * 0.6`, stored `{ang, rad, life, size}`.
- Ignition: track the previous sweep angle; when it crosses a contact's `ang` (wrap-aware), set `life = 1`.
- Decay: `life = max(0, life - dt / 2.6)` — 2.6s fade, so one 6.4s revolution leaves ~12 lit contacts.
- Per contact, TWO primitives: glow — the 64×64 sprite (stops `rgba(92,255,157,0.85)` 0%, `rgba(92,255,157,0.28)` 35%, `rgba(92,255,157,0)` 70%) via `drawImage` in a `(14 + 26 * life) * size` square at `globalAlpha = 0.55 * life^1.8`; then core — filled circle radius `(2.0 + 1.5 * life) * size`, `fillStyle rgba(92,255,157, 0.30 + 0.65 * life^1.4)`. Reset `globalAlpha` after each glow.
**Edge fade and vignette (2 gradients per frame):** radial gradient centred `(cx, cy)`: `rgba(3,12,22,0)` at 0 and at `R * 0.86` → `rgba(3,12,22,0.55)` at `R * 1.06` → `#030c16` at `R * 1.34`; then a full-frame vignette transparent at 45% radius to `rgba(3,12,22,0.52)` at the corners.
**Grain (CSS, separate element):** `div.grain`, `position:fixed; inset:-50%; width:200%; height:200%; pointer-events:none; opacity:0.05; z-index:5`, background = inline SVG `feTurbulence` data-URI (`fractalNoise`, `baseFrequency="0.8"`, `numOctaves="2"`, `stitchTiles="stitch"`, then `feColorMatrix saturate 0`), tile 160px, `grain 0.9s steps(4) infinite` jittering `translate` among `(0,0) (-2%,1%) (1%,-2%) (-1%,2%)`.
**Per-frame cost budget:** 4 ring strokes + 8 trail fills + 2 leading-edge primitives + 30 × 2 blip primitives + 1 graticule blit + 2 gradient fills + 1 clear = **≤ 78 draw primitives per frame**, ≤ 3 gradient objects per frame, **zero `ctx.filter` / `ctx.shadowBlur` calls ever** (glow is a pre-rendered sprite). Resolution trick: cap backing-store DPR at `Math.min(devicePixelRatio, 2)` desktop, `1.5` at `<= 900px`; `canvas.width = innerWidth * dpr`, `canvas.height = innerHeight * dpr`, `ctx.setTransform(dpr, 0, 0, dpr, 0, 0)`, graticule rebuilt at that size. Read `innerWidth/innerHeight` only on resize — NEVER `getBoundingClientRect` in the loop. Cap `dt` at 0.05s. While `document.hidden`, increment `frames` and skip the paint.
---
## C. LAYER STACK (back → front)
1. `html, body` — deep navy `#030c16`, forced twice (see Failure Guards).
2. `canvas#sonar` — z-index 0, the scope: graticule, pings, sweep, contacts, edge fade, vignette.
3. `.scrim` — z-index 1, fixed `linear-gradient(180deg, rgba(3,12,22,0.78) 0%, rgba(3,12,22,0.22) 36%, rgba(3,12,22,0.18) 62%, rgba(3,12,22,0.86) 100%)` guaranteeing text contrast top and bottom.
4. `.page` — z-index 2, `position:relative; display:grid; grid-template-rows: auto minmax(0,1fr) auto; min-height:100dvh` (height 100dvh inside the desktop lock).
5. `.grain` — z-index 5, above all content.
---
## D. TOKEN TABLE (defaults at ~1440px)
```
--bg: #030c16
--bg-2: #061524
--text: #e7f3ef
--text-soft: #c3d7d4
--muted: #8ba7ae
--dim: #55727c
--accent: #5cff9d
--accent-dim: rgba(92,255,157,0.55)
--accent-line: rgba(92,255,157,0.28)
--accent-fill: rgba(92,255,157,0.06)
--border: rgba(180,225,214,0.14)
--border-soft: rgba(180,225,214,0.09)
--logo: 15px
--logo-mark: 20px
--nav: 13.5px
--nav-h: 38px
--btn: 13.5px
--btn-h: 42px
--chip: 11.5px
--chip-h: 28px
--h1: 60px
--lead: 16.5px
--readout: 12.5px
--foot: 12.5px
--header-y: 24px
--header-x: 40px
--hero-x: 40px
--hero-y: 40px
--footer-y: 22px
--copy-max: 560px
--lead-max: 520px
--h1-gap: 20px
--lead-gap: 20px
--action-gap: 28px
--readout-gap: 26px
--sans: "Space Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif
--mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace
--ease-out: cubic-bezier(0.16,1,0.3,1)
--enter-dur: 0.95s
```
Fonts (Google Fonts ONLY, one `<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>` plus one stylesheet link with `display=swap`): `Space+Grotesk:wght@400;500;700` and `IBM+Plex+Mono:wght@400;500`.
---
## E. THE COMPOSITION — exact build order
Body: `margin:0; min-width:320px; font-family:var(--sans); -webkit-font-smoothing:antialiased` (navy-first rule per I.1).
**HEADER** — `padding: var(--header-y) var(--header-x); display:flex; align-items:center; justify-content:space-between; gap:24px`.
- Left: `a.brand` (href `#`, accessible name "Echo home") — inline SVG mark 20×20: `<circle cx="10" cy="10" r="1.9" fill="#5cff9d"/>` plus two concentric arcs `<path d="M14.4 5.6a6.2 6.2 0 0 1 0 8.8" fill="none" stroke="#5cff9d" stroke-width="1.8" stroke-linecap="round"/>` and `<path d="M17.2 2.8a10 10 0 0 1 0 14.4" fill="none" stroke="#5cff9d" stroke-width="1.4" stroke-linecap="round" opacity="0.55"/>`. Wordmark `echo` in `var(--mono)`, `font-size: var(--logo); font-weight:500; color:var(--text); letter-spacing:0.14em`; trailing `_` cursor in `var(--accent)`, `blink 1.06s steps(1) infinite` (`0%,49% {opacity:1} 50%,100% {opacity:0}`).
- Centre: `nav` with exactly 3 links: `platform`, `fleet`, `science` — all href `#`, mono, `font-size: var(--nav); color: var(--muted)`, `display:flex; align-items:center; gap:30px; height:var(--nav-h)`. Hover: `color: var(--accent)` over 0.18s ease. `platform` (current bearing) is preceded by a 5×5px square `background: var(--accent); box-shadow: 0 0 8px var(--accent-dim); display:inline-block; margin-right:8px`.
- Right: `a.cta` (href `#`, class `btn btn--ghost btn--sm`) text `Request a sea trial`, height `var(--nav-h)`, padding 0 16px.
**MAIN (hero)** — `display:flex; flex-direction:column; justify-content:center; align-items:flex-start; padding: var(--hero-y) var(--hero-x); width:100%; max-width: var(--copy-max); margin-right:auto; min-width:0`. Left-anchored, vertically centred in the 1fr row; the disc owns the right 55%.
1. `.chip` — inline-flex, `height: var(--chip-h); padding: 0 12px; border:1px solid var(--accent-line); border-radius:999px; background: var(--accent-fill)`, mono `font-size: var(--chip); letter-spacing:0.18em; color: var(--accent)`. Exact copy: `DEEP-CLASS SONAR AUTONOMY`. Preceded by a 6px dot (`border-radius:50%; background:var(--accent)`) pulsing per Motion.
2. `h1` — EXACTLY ONE `<h1>`, `var(--sans); font-weight:700; font-size: var(--h1); letter-spacing:-0.045em; line-height:1.02; color:var(--text); margin: var(--h1-gap) 0 0`. Two masked lines (see Motion):
Line 1: `Sound sees`
Line 2: `what light cannot.`
Line 1 wraps `Sound` in `<em>` styled `font-family: var(--mono); font-style:normal; font-weight:400; font-size:0.88em; color: var(--accent); letter-spacing:-0.02em`.
3. `p.lead` — `margin: var(--lead-gap) 0 0; max-width: var(--lead-max); font-size: var(--lead); line-height:1.6; letter-spacing:-0.015em; color: var(--text-soft)`. Exact copy: `Echo builds autonomous sonar vehicles that map, classify and track everything that lives and moves beneath 200 metres — where cameras go blind and light gives up.`
4. `.actions` — `display:flex; gap:12px; flex-wrap:wrap; margin-top: var(--action-gap)`. `a.btn.btn--primary` text `Request a sea trial` (href `#`); `a.btn.btn--ghost` text `Read the tech brief` (href `#`).
5. `.readout` — mono `font-size: var(--readout); color: var(--dim); margin-top: var(--readout-gap); white-space:nowrap`. Exact copy: `CONTACTS 07 · SWEEP 042° · PING 5.5 s · DEPTH 6 000 m`. The four values `07`, `042°`, `5.5`, `6 000` are `color: var(--accent)`. The trailing `· DEPTH 6 000 m` sits in `<span class="opt">` (hidden `<=560px`).
**FOOTER** — `padding: var(--footer-y) var(--header-x); display:flex; align-items:center; justify-content:space-between; gap:20px; border-top:1px solid var(--border-soft)`.
- Left: mono `var(--foot)`, `color: var(--dim)`: `© 2026 Echo Marine Systems`.
- Centre: mono `var(--foot)`, `color: var(--dim)`: `beacon: transmitting` with the same 7px status square (`margin-right:8px`) as the nav marker.
- Right: exactly 3 links `research`, `voyages`, `contact` — mono `var(--foot)`, `color: var(--muted)`, `gap:22px`, href `#`, hover `color: var(--text)`.
---
## F. THE UI LANGUAGE
One button family, two weights. `.btn` is `inline-flex; align-items:center; justify-content:center; height:var(--btn-h); padding:0 20px; border-radius:6px; font-family:var(--mono); font-size:var(--btn); letter-spacing:0.02em; text-decoration:none; transition: transform, background, border-color, box-shadow, color .2s var(--ease-out)`. `.btn--sm` is `height:var(--nav-h); padding:0 16px`. `.btn--primary`: `color:#04140c; font-weight:500; border:1px solid rgba(92,255,157,0.6); background:linear-gradient(180deg, #7dffb4 0%, #5cff9d 52%, #33d97f 100%); box-shadow: 0 14px 40px rgba(92,255,157,0.18), inset 0 1px 0 rgba(255,255,255,0.5)`. Hover: `transform: translateY(-1px); background:linear-gradient(180deg, #a4ffcb 0%, #7dffb4 48%, #46e88d 100%); box-shadow: 0 18px 48px rgba(92,255,157,0.28), inset 0 1px 0 rgba(255,255,255,0.5)`. `.btn--ghost`: `color:var(--text); border:1px solid var(--border); background:rgba(180,225,214,0.04)`. Hover: `transform: translateY(-1px); border-color: var(--accent-line); color: var(--accent); background: rgba(92,255,157,0.07)`. Both `:active` → `transform: translateY(0)`. Nav and footer links are plain mono text, `text-decoration:none`, colour-only hover — no pills, backgrounds, or underlines. Every `a` and `button` gets `:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px }`. Selection: `::selection { background: rgba(92,255,157,0.26); color: #fff }`. No default blue focus ring and no text underline anywhere.
---
## G. MOTION — entrance stagger + named keyframes
All entrance animations: `animation-duration: var(--enter-dur); animation-fill-mode: both; animation-timing-function: var(--ease-out); animation-delay: var(--d, 0.08s)`. Each animated element carries `.appear` plus a modifier and an inline `--d`.
Stagger table (element → modifier → --d):
```
brand appear--fade 0.08s
nav platform appear--fade 0.16s
nav fleet appear--fade 0.24s
nav science appear--fade 0.32s
header cta appear--chip 0.40s
chip appear--pop 0.52s
h1 line 1 appear--mask 0.66s
h1 line 2 appear--mask 0.80s
lead appear--soft 0.98s
primary button appear--chip 1.14s
ghost button appear--chip 1.22s
readout line appear--read 1.34s (duration 1.1s)
footer left appear--fade 1.46s
footer centre appear--fade 1.54s
footer right appear--fade 1.62s
```
Named keyframes (all end at opacity 1 / identity transform):
```
in-fade from { opacity: 0 } to { opacity: 1 }
in-soft from { opacity: 0; transform: translateY(14px); filter: blur(4px) } to { opacity: 1; transform: none; filter: blur(0) }
in-mask from { opacity: 0; transform: translateY(104%) } to { opacity: 1; transform: none }
in-chip from { opacity: 0; transform: translateY(10px) scale(0.96) } to { opacity: 1; transform: none }
in-pop 0% { opacity:0; transform: scale(0.9) } 70% { opacity:1; transform: scale(1.03) } 100% { opacity:1; transform:none }
in-read from { opacity: 0; clip-path: inset(0 100% 0 0) } to { opacity: 1; clip-path: inset(0 -4% 0 0) }
```
H1 masking: each line is `<span class="line"><span class="line-inner appear appear--mask">…</span></span>` with `.line { display:block; overflow:hidden; padding: 0.04em 0.1em 0.10em }` and `.line-inner { display:inline-block }` — type surfaces out of a hard edge like a seabed return. `in-read` wipes the readout left-to-right, echoing the sweep.
Continuous keyframes: `blink` (brand cursor, 1.06s steps(1) infinite); `pulse` on the chip dot — `0%,100% { opacity:.35; transform:scale(.8) } 50% { opacity:1; transform:scale(1) }`, `2.4s ease-in-out infinite`; `grain` (0.9s steps(4)). Canvas sweep, pings, contact decay and grain run continuously, independent of the stagger.
---
## H. RESPONSIVE TABLE — token overrides per breakpoint (exact)
```
>= 1600px:
--h1: 72px --logo: 16px --nav: 14.5px --nav-h: 42px --btn: 14.5px
--btn-h: 46px --lead: 17.5px --readout: 13px --foot: 13px --chip: 12px
--header-y: 28px --header-x: 56px --hero-x: 56px --copy-max: 620px --lead-max: 560px
>= 1920px:
--h1: 84px --header-x: 72px --hero-x: 72px --copy-max: 680px
--lead: 18.5px --readout: 13.5px --lead-max: 600px
1280px – 1599px:
--h1: 64px --header-x: 48px --hero-x: 48px --copy-max: 540px
901px – 1279px:
--h1: 48px --nav-h: 34px --header-x: 32px --hero-x: 32px
--copy-max: 460px --lead: 15.5px --readout: 12px
>= 901px AND max-height 850px:
--h1: 44px --hero-y: 24px --header-y: 18px --chip-h: 26px --lead: 15px
--h1-gap: 14px --lead-gap: 16px --action-gap: 18px --readout-gap: 16px
>= 901px AND max-height 720px:
--h1: 38px --hero-y: 14px --header-y: 12px --lead: 14px --readout: 11.5px
--btn-h: 38px --chip-h: 24px --footer-y: 14px --h1-gap: 12px --lead-gap: 12px
--action-gap: 14px --readout-gap: 12px
>= 901px (desktop lock, all heights):
html, body { height:100%; overflow:hidden } .page { height:100dvh; overflow:hidden }
<= 900px:
no vh lock; page may scroll; nav becomes a full-viewport overlay menu:
- header keeps brand + burger button (<button class="burger" aria-label="Menu" aria-expanded="false">, three 18×1.5px bars in var(--text), gap 5px)
- .menu { position:fixed; inset:0; z-index:10; background: rgba(3,12,22,0.92); backdrop-filter: blur(14px); display:flex; flex-direction:column; align-items:flex-start; justify-content:center; padding: 0 32px; gap: 26px; transform: translateY(-100%); transition: transform 0.45s var(--ease-out) }
- .menu.open { transform: none }
- menu shows the 3 nav links stacked at --nav: 22px plus the header CTA chip; Escape key, link click, and resize >900px all close it
--h1: 40px --header-x: 24px --hero-x: 24px --copy-max: 100% --lead-max: 520px
--hero-y: 28px --readout: 11.5px
.scrim mid stops rise to rgba(3,12,22,0.42) at 36% and rgba(3,12,22,0.38) at 62% (copy sits over the centred scope)
<= 560px:
--h1: 32px --lead: 14.5px --readout: 11px --header-x: 20px --hero-x: 20px
footer becomes flex-direction:column; align-items:flex-start; gap:10px
header CTA is hidden (it lives inside the menu on mobile)
the readout drops the DEPTH segment via span.opt { display:none }
```
Canvas constants scale too: at `<= 900px` origin `(w*0.50, h*0.40)`, `R = min(w*0.46, h*0.34)`, contact count 22, DPR cap 1.5; at `<= 560px` contact count 16 and graticule labels drop to `000`/`180` only. Sweep 6.4s and ping 5.5s stay constant at every size.
---
## I. FAILURE GUARDS — the page is never blank, never broken
1. **Navy-first:** the FIRST CSS rule is `html, body { background:#030c16 !important; color:#e7f3ef }`; the body tag carries `style="background:#030c16;color:#e7f3ef"`; a later rule restates `html, body { background: var(--bg, #030c16) }`.
2. **Resting opacity is 1:** every `.appear` element's un-animated computed style is its final state (opacity 1, no transform/blur/clip); `fill-mode: both` holds the FROM state only during the delay. If CSS animation or JS fails, the page renders complete and readable.
3. **Two-rAF entrance fallback:** after two nested `requestAnimationFrame` callbacks, inspect every `.appear` with `el.getAnimations({ subtree:false })` (or unconditionally if missing); if none reports `playState` "running"/"finished", add `.is-in` to ALL `.appear` — `.is-in { animation:none !important; opacity:1 !important; transform:none !important; filter:none !important; clip-path:none !important }`.
4. **Canvas fallback:** if the canvas or its 2D context is missing, add `.no-canvas` to `<html>`, push a short string into `window.__DEMO__.errors`, and paint a static CSS scope: `.no-canvas body { background-image: repeating-radial-gradient(circle at 70% 52%, rgba(0,0,0,0) 0 58px, rgba(92,255,157,0.13) 58px 59px) }` so the page still reads sonar.
5. **Reduced motion:** inside `@media (prefers-reduced-motion: reduce)`: `*, *::before, *::after { transition:none !important; animation:none !important }`; force `.appear, .line-inner, .readout` to `opacity:1 !important; transform:none !important; filter:none !important; clip-path:none !important`; brand cursor and chip dot freeze at `opacity:1`; `.grain { display:none }`. The canvas paints exactly ONE static frame — sweep parked at bearing `042°` (drawn with its full trail), ping phases frozen at `pk = 0.18, 0.42, 0.66, 0.90`, every contact at `life = 0.55` — sets `ready = true`, sets `frames = 1`, and schedules no further frames.
6. **No console output** in the happy path: no `console.log`, no thrown exceptions; every canvas call null-guarded.
---
## J. THE EXACT JS (only this, vanilla, one IIFE at end of body)
1. **Telemetry first:**
`window.__DEMO__ = { name: 'echo', ready: false, frames: 0, effects: [], errors: [] };`
Attach `window.addEventListener('error', e => window.__DEMO__.errors.push(String(e.message || e)))` immediately, then push into `effects` once, in order: `'sonar-sweep'`, `'ping-rings'`, `'contact-blips'`, `'range-graticule'`, `'grain-texture'`.
2. **Setup:** cache `document.documentElement`, `canvas#sonar`, its 2D context (guard null → `.no-canvas` path from I.4 and stop), burger, menu panel, menu links, and `matchMedia('(prefers-reduced-motion: reduce)')`. Constants: `TAU`, sweep 6.4s, ping 5.5s, trail 56°, 8 buckets, decay 2.6s, seed 20260214. Implement `mulberry32(seed)` (3 lines, `Math.imul`); build the contacts ONCE with the breakpoint count (30 / 22 / 16) and the 64×64 glow sprite ONCE, both per section B.
3. **resize()** (on load + resize, via boolean `resizeQueued` + `requestAnimationFrame`, never a timer): read `innerWidth`/`innerHeight` (NOT `getBoundingClientRect`), apply the DPR caps and `setTransform` of section B, recompute `cx, cy, R`, rebuild the offscreen graticule (6 rings, 36 ticks, 4 labels).
4. **drawFrame(t, dt):** execute section B in order — navy clear, one graticule `drawImage`, the 4 ping rings (`pk = ((t/5.5) + k/4) % 1`), the 8 trail buckets and leading edge, contact updates (wrap-aware crossing detection vs the previous sweep angle, `life = Math.max(0, life - dt/2.6)`) with glow + core, edge fade, vignette. Set `window.__DEMO__.ready = true` after the first completed paint; increment `window.__DEMO__.frames` by exactly 1 per rAF tick — even while `document.hidden` — skipping only the paint while hidden.
5. **Loop:** one `requestAnimationFrame` loop, `dt = Math.min(0.05, (now - last) / 1000)`, `last = now`. Reduced motion: run `drawFrame(2.4, 0)` once (frozen per I.5), set `frames = 1`, `ready = true`, schedule nothing more. In the loop: no `getBoundingClientRect`, DOM animation via `transform`/`opacity` only, no `ctx.filter`/`ctx.shadowBlur`.
6. **Entrance fallback:** two nested rAFs after DOMContentLoaded, then the `.is-in` sweep of section I.3.
7. **Menu (mobile only):** burger click toggles `.menu.open` and `aria-expanded`; Escape, any menu link click, or resize to `>900px` closes it.
No other JS. No libraries. No timers. No console calls.
---
TECHNICAL CONTRACT (non-negotiable):
1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,
no framework, no package install. It must open correctly by double-clicking the file
and also over http://.
2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.
Plain HTML + CSS + modern JavaScript in <style> and <script> tags.
3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of
photos. Every visual must be generated procedurally with CSS gradients, SVG, or
<canvas>. This keeps the page free to host and impossible to break with a dead link.
4. Google Fonts is the ONLY permitted network request, and it must be declared with a
<link rel="preconnect"> plus a system-font fallback stack so the page still looks
intentional offline. If you prefer, skip it and use a pure system stack.
5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).
No horizontal scrollbar at any of those widths. Nothing may overflow its container.
6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every
interactive element is a <button> or <a> with an accessible name; visible :focus-visible
ring; body text contrast ratio >= 4.5:1 against its background.
7. Motion safety: wrap all continuous motion in
`@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or
near-static state. The page must remain fully readable and usable with motion off.
8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single
requestAnimationFrame loop, use transform/opacity for animation, and never allocate
inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.
9. Runtime telemetry hook (used by the automated verifier — implement it exactly):
at the top of your script define
window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };
then set `ready = true` after the first paint of your main effect, increment
`frames` once per animation frame inside your rAF loop, and push the name of each
implemented effect into `effects`. Add a `window.addEventListener('error', ...)`
handler that pushes messages into `errors`. Do not rename these fields.
10. Zero console output in the happy path. No console.log left behind. No uncaught
exceptions, no failed network requests.
RESPONSE FORMAT:
Return ONLY the raw HTML inside a single ```html fenced code block. No explanation, no
preamble, no commentary before or after the fence. The file must be complete and
immediately runnable — never truncate, never write "// rest of code here".Acceptance criteria (15)
- exactly one <h1> split into exactly 2 masked line elements (.line > .line-inner) with text 'Sound sees' and 'what light cannot.', plus semantic header, nav, main, and footer landmarks
- canvas#sonar paints non-blank content and keeps repainting (pixel hash changes across frames) at 1440x900, and the generated script never assigns ctx.filter or ctx.shadowBlur
- window.__DEMO__ is initialized with name 'echo'; ready becomes true after the first painted frame; frames increments once per requestAnimationFrame tick; effects contains 'sonar-sweep', 'ping-rings', 'contact-blips', and 'range-graticule'; errors stays empty
- exactly 3 nav links and 3 footer links exist with accessible names; no horizontal overflow at 390px, 768px, 1440px, or 1920px; at >=901px body has overflow:hidden and .page height is 100dvh
- the document performs no network requests except Google Fonts, contains no external media or framework scripts, includes a prefers-reduced-motion reduce block leaving all content at opacity 1, and every anchor or button shows a visible :focus-visible ring
- window.__DEMO__.ready becomes true within 5s
- window.__DEMO__.frames advances (>= 20 frames in 1.2s)
- window.__DEMO__.errors is empty
- zero console errors and zero page errors
- zero failed network requests
- exactly one <h1> with non-empty text
- header, main, footer landmarks present
- no horizontal overflow at 390 / 768 / 1440 widths
- no horizontal overflow after 2s of animation
- prefers-reduced-motion: reduce leaves the page usable and errors empty