{"source":"d1","total":332,"count":50,"limit":50,"offset":0,"prompts":[{"slug":"liquid-glass-voyage","title":"Liquid Glass Voyage","category":"Hero Section","effect":"Liquid-glass morphism floating on a procedurally animated cinematic film backdrop","platform":"workbuddy","platform_label":"Workbuddy proxy","model":"hy4-preview-f","model_label":"Hy4 preview (hy4-preview-f)","demo":"/demos/liquid-glass-voyage/index.html","doc":"/prompts/liquid-glass-voyage.md","prompt_text":"DESIGN BRIEF — \"Liquid Glass Voyage\"\n\nBuild a full-viewport cinematic hero for a fictional deep-sea research studio called\n\"Abyssal\". The feeling is expensive, quiet and slightly unreal: a slow-moving film of\nlight and water behind a single pane of liquid glass.\n\nA. THE BACKDROP (\"the film\")\n   - A fixed, full-bleed <canvas> layer that renders an endless, slowly drifting\n     underwater light field. Do NOT draw literal water. Draw the *light*: 3-5 large,\n     very soft radial gradients in deep teal (#031a1f), abyssal blue (#062b3a) and\n     cyan (#1e6f8c) that drift on independent Lissajous paths, plus one warm\n     highlight (#c8a45c) that passes across the frame every ~24 seconds.\n   - Layer on top, in CSS: a fine animated grain (SVG feTurbulence as a repeating\n     background-image, animated by shifting background-position), a 1.5px caustic\n     band pattern at 4% opacity, and a bottom-to-top vignette so the centre stays\n     legible.\n   - The whole film must run at 60fps. Render at devicePixelRatio capped to 2, and\n     downscale the canvas backing store to 0.6x for the soft gradients (they are\n     blurry by nature) then CSS-scale it up — this is the performance trick.\n   - A \"film loop\" behaviour, implemented in JS exactly like this: every 18 seconds the\n     film performs a 900ms cross-fade to the next palette (teal → indigo → slate →\n     teal), implemented with requestAnimationFrame interpolation of a palette weight\n     array, NOT a CSS transition. Any fade in progress is cancelled and restarted\n     from the current opacity rather than snapping.\n\nB. THE GLASS\n   - Implement a reusable `.liquid-glass` class with: background rgba(255,255,255,0.012)\n     plus background-blend-mode: luminosity; backdrop-filter: blur(14px) saturate(1.4)\n     with the -webkit- prefix; box-shadow: inset 0 1px 1px rgba(255,255,255,0.10);\n     position: relative; overflow: hidden; isolation: isolate.\n   - The signature 1.4px glass edge is drawn with a ::before pseudo-element:\n     inset 0; border-radius: inherit; padding: 1.4px; background:\n     linear-gradient(180deg, rgba(255,255,255,.55) 0%, rgba(255,255,255,.18) 18%,\n     rgba(255,255,255,0) 42%, rgba(255,255,255,0) 58%, rgba(255,255,255,.18) 82%,\n     rgba(255,255,255,.55) 100%); and the border-only mask trick\n     (-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);\n     -webkit-mask-composite: xor; mask-composite: exclude; pointer-events: none).\n   - Add a slow specular sweep: a ::after with a 120deg white gradient at 6% opacity\n     that translates across the pane over 9s, paused (animation-play-state: paused)\n     until the hero has entered.\n\nC. LAYOUT (single column, everything centred, all inside <main>)\n   - <header> nav bar: a liquid-glass pill, max-width 1080px, centred, containing on the\n     left a small inline-SVG mark (a stylised wave inside a circle) + the wordmark\n     \"ABYSSAL\" in 600 weight with 0.18em letter-spacing; centre nav links \"Vessel\",\n     \"Research\", \"Expeditions\" (hidden below 768px); right side a plain text \"Sign in\"\n     link and a liquid-glass pill button \"Request access\".\n   - Hero block, optically centred but translated up by 6vh:\n       * Eyebrow: a liquid-glass pill containing a 6px pulsing dot and the text\n         \"EXPEDITION 14 · LIVE FROM 4,120 M\" in 11px, 0.22em letter-spacing, uppercase.\n       * <h1> in \"Instrument Serif\" (Google Fonts, italic available) at\n         clamp(2.75rem, 8vw, 6.5rem), line-height 0.98, tracking -0.02em, colour #f4f7f8:\n         \"We chart the light / that never reaches shore.\" — the second line in italic\n         and at 0.86em size, coloured #9fd8ea.\n       * A max-width 560px paragraph, 17px/1.65, rgba(244,247,248,0.72): \"Abyssal maps\n         the twilight zone with autonomous gliders — 41 vehicles, 900 days of continuous\n         observation, one open dataset.\"\n       * A liquid-glass email capture bar: rounded-full, padding 8px 8px 8px 22px,\n         containing a transparent email input (placeholder \"you@institute.edu\",\n         aria-label \"Email address\") and a solid #eaf6fa circular submit button with a\n         black arrow-right SVG. On submit, prevent default and morph the pill into a\n         confirmation state reading \"You're on the manifest.\" with a checkmark, then\n         fade back after 3.2s.\n   - A stat strip of three liquid-glass cards (grid: 3 cols → 1 col under 640px) showing\n     animated counters: \"4,120 m  max depth\", \"41  gliders\", \"900  days at sea\". Counters\n     animate from 0 with an easeOutExpo over 1.6s when the strip first enters the\n     viewport (IntersectionObserver, fire once).\n   - <footer> a single row: \"© 2026 Abyssal Research\" on the left, three liquid-glass\n     circular social icon buttons (inline SVG: instagram, x, globe) on the right, each\n     with aria-label.\n\nD. TYPE + COLOUR SYSTEM\n   - Display: 'Instrument Serif', ui-serif, Georgia, serif.\n   - UI: system stack (-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif).\n   - Palette: ink #f4f7f8, muted rgba(244,247,248,.72), cyan #9fd8ea, gold #c8a45c,\n     base #031a1f.\n\nE. MICRO-INTERACTION\n   - Buttons lift 1px and gain a 1.04 specular boost on hover; the glass edge brightens\n     to 0.75 alpha. Use transform + custom properties only.\n   - On pointer move over the hero, the glass panes tilt at most 1.2deg toward the\n     pointer (perspective 1200px), damped with a 0.08 lerp in the rAF loop.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["canvas backdrop element exists and is painted (non-blank pixels)","exactly one element with class \"liquid-glass\" has a non-none backdrop-filter","h1 font-family resolves to a serif family","three stat counters exist and end at 4120 / 41 / 900","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"],"usage":{"build_tokens":33814,"build_prompt_tokens":2339,"build_completion_tokens":31475,"build_reasoning_tokens":23753,"build_calls":1,"build_attempts":3,"build_latency_ms":285951,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":28353,"total_tokens":62167},"verification":{"passed":true,"passed_checks":21,"total_checks":21,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"22545 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"13 frames, effects: canvas-film, palette-crossfade, glass-tilt, stat-counters"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"48 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 46.39, stdev 13.48, coverage 1"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 2041299372 -> 2163609095"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"We chart the light\nthat never reaches shore.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Abyssal — Liquid Glass Voyage"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"[prompt] liquid-glass element has real backdrop-filter","pass":true,"detail":"blur(14px) saturate(1.4)"},{"name":"[prompt] stat counters reach 4120 / 41 / 900","pass":true,"detail":"found all three targets"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=332 pageErrors=0"}]},"crosscheck":{"slug":"liquid-glass-voyage","reviewer_model":"gpt-5.5","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":6,"requirements_met":["Abyssal-specific copy is present for eyebrow, headline, paragraph and stats","Header pill contains mark, wordmark, nav links, sign-in link and request-access button","Display type uses Instrument Serif with italic second headline line","Reusable .liquid-glass class includes the requested blur, saturation, inset shadow, isolation and masked 1.4px edge","Canvas backdrop is procedural and uses multiple soft gradients plus a warm highlight","DPR is capped and the canvas backing store is downscaled to 0.6x","CSS grain, caustic-line overlay and bottom vignette are present","Stats cards and one-shot animated counters are implemented"],"requirements_missed":["The visible composition at 1024x640 does not include the requested footer row","The backdrop in the screenshot reads as a flat dark teal field, not a cinematic underwater light film with visible drifting pools","Email confirmation does not morph or fade; it hard-swaps display states","Pointer tilt is document-wide and globally applied, not scoped to pointer movement over the hero with panes tilting individually toward the pointer","In-progress palette fade cancellation/restart is not implemented","Specular sweep is started by a fixed timeout, not by the hero entering","Header/nav/footer are not all inside main, contrary to the layout wording"],"issues":[{"issue":"Footer is missing from the rendered viewport","severity":"medium","evidence":"Screenshot at 1024x640 ends at the stat cards; the \"© 2026 Abyssal Research\" row and social icons are not visible. In the HTML, <footer> is after </main> and main has `padding: 120px 24px 48px`."},{"issue":"Backdrop is visually too flat for the requested film of underwater light","severity":"medium","evidence":"Screenshot shows an almost uniform dark teal background with no discernible large cyan/blue radial light pools, warm pass, grain or caustic texture."},{"issue":"Email capture confirmation is an abrupt content swap, not a morph/fade interaction","severity":"medium","evidence":"Submit handler uses `input.style.display = 'none'; btn.style.display = 'none'; success.style.display = 'flex';` and later reverses it after `3200`; there is no opacity transition or morph animation."},{"issue":"Pointer tilt behavior is coupled incorrectly","severity":"medium","evidence":"Code listens on `document.addEventListener('pointermove', ...)` and writes global `--rotX` / `--rotY` on `document.documentElement`, so all `.tilt-pane` elements rotate together rather than panes tilting toward the pointer over the hero."},{"issue":"Palette cross-fade does not satisfy the requested cancellation behavior","severity":"low","evidence":"Fade trigger is guarded by `&& !fadeActive`, so an active fade is never cancelled and restarted from current weights."},{"issue":"Specular sweep is not tied to actual hero entry","severity":"low","evidence":"The page runs `setTimeout(() => { document.body.classList.add('hero-entered'); }, 100);` instead of detecting when the hero has entered."},{"issue":"Header center navigation is only space-betweened, not truly centered","severity":"low","evidence":"`header { justify-content: space-between; }` with unequal brand/action widths; in the screenshot the nav cluster sits left of the pill's visual center."},{"issue":"Button hover scales the whole control instead of only lifting and boosting specular","severity":"low","evidence":"`button.liquid-glass:hover, a.liquid-glass:hover { transform: translateY(-1px) scale(1.04); }`; the brief asked for a 1px lift and specular boost, not control scaling."}],"summary":"This is a recognizable implementation of the brief, but it lands as a clean generic glass hero rather than the requested cinematic liquid-light composition. The main visual failures are the flat backdrop, missing footer in the shown viewport, and several motion interactions implemented as simple shortcuts.","usage":{"input":11121,"output":4104,"total":15225,"reasoning":3106,"cached":0},"latency_ms":50025}},{"slug":"scroll-atlas","title":"Scroll Atlas","category":"Landing Page","effect":"Pinned scroll-scrubbed cinematic narrative with a progress rail","platform":"cmdcode","platform_label":"cmd-code","model":"MiniMaxAI/MiniMax-M3","model_label":"MiniMax-M3 (MiniMaxAI/MiniMax-M3)","demo":"/demos/scroll-atlas/index.html","doc":"/prompts/scroll-atlas.md","prompt_text":"DESIGN BRIEF — \"Scroll Atlas\"\n\nBuild a one-page scroll-driven narrative for a fictional cartography studio called\n\"Atlas Nine\". The page must feel like scrubbing a film timeline: the user scrolls, and a\npinned stage performs choreographed, reversible motion. Nothing here should be a\n\"fade in on scroll\" — every transform must be *driven* by scroll progress.\n\nA. THE PINNED STAGE\n   - Structure: a `<section class=\"stage\">` that is 420vh tall, containing a\n     `position: sticky; top: 0; height: 100vh` inner viewport. Do NOT use\n     position: fixed. The sticky inner viewport holds the canvas, the type, and the rail.\n   - Progress p = clamp(scrollTop_of_section / (sectionHeight - viewportHeight), 0, 1),\n     computed in the rAF loop from a cached rect (recompute the rect only on resize and\n     on a 250ms debounce of scroll — never call getBoundingClientRect inside rAF).\n   - The stage is divided into 4 beats at p = 0.00, 0.25, 0.50, 0.75. Each beat has a\n     title, a body line and a colour. Cross-fade beats with a 0.08-wide smoothstep\n     window so there is never a hard cut.\n\nB. THE VISUAL — a drawn world, not an image\n   - A full-viewport <canvas> renders an abstract \"atlas\": a slowly rotating globe of\n     latitude/longitude arcs (stroke rgba(255,255,255,.14), 1px) drawn with simple\n     3D→2D projection maths you write yourself (no library). Only the front hemisphere\n     is drawn; back-facing segments are skipped.\n   - As p advances, the camera dollies in (radius 1.0 → 0.42), the globe tilts\n     (tilt 0 → -0.6 rad), and a set of 12 \"survey pins\" (small filled circles with a\n     1.5px halo) fade in one by one between p = 0.25 and 0.75, each with a thin arc\n     connecting it to its predecessor — a route being drawn.\n   - The globe stroke colour lerps between the four beat colours. Add a soft radial\n     bloom behind the globe whose radius is driven by p.\n   - Performance: precompute the sphere point set once (lat rings of 18, lon rings of 36)\n     into typed arrays; per frame only rotate + project. Target 60fps at 1440x900.\n\nC. THE RAIL\n   - A vertical progress rail fixed to the right edge, 2px wide, 34vh tall, centred:\n     track rgba(255,255,255,.12), fill a gradient that grows with p. Four 6px tick\n     marks sit at 0/25/50/75% and the active tick grows to 10px and turns solid white.\n     The rail is aria-hidden and purely decorative, but the *text* beats must be real\n     content in the DOM.\n\nD. TEXT\n   - Beat 1 — \"Nine degrees of nowhere\" / \"We began with a blank plate and a\n     suspicion that the coastline was lying.\"\n   - Beat 2 — \"Survey, then argue\" / \"Twelve soundings, four disagreements, one\n     corrected map.\"\n   - Beat 3 — \"The route writes itself\" / \"Each pin is a decision. The arc is the\n     consequence.\"\n   - Beat 4 — \"Atlas Nine\" / \"Maps for places that are still deciding what they are.\"\n   - Beats are absolutely stacked in the same grid cell; opacity and a 24px translateY\n     are driven by p (no CSS transitions on them). Text must remain selectable and\n     readable when p is 0.\n\nE. FRAME\n   - <header> a fixed, translucent, blurred top bar (background rgba(8,8,12,.55),\n     backdrop-filter blur(12px), 1px bottom hairline) with the wordmark \"ATLAS NINE\",\n     links \"Work\", \"Studio\", \"Journal\" and a bordered pill button \"Start a map\".\n   - <main> holds the pinned stage, then one closing section (min-height 70vh) with an\n     <h2> \"Ready to redraw something?\" and a large ghost button.\n   - <footer> three columns: wordmark + one-line description, \"Studio\" link list,\n     \"Contact\" link list; bottom row with the copyright.\n   - Palette: base #08080c, ink #f2f2f5, muted rgba(242,242,245,.6), accent #c084fc,\n     secondary #67e8f9. UI font: system stack. Display font: \"Instrument Serif\"\n     (Google Fonts) for the h1/h2 only.\n\nF. MOTION SAFETY\n   - With prefers-reduced-motion: reduce, the section collapses to 100vh (via CSS), the\n     sticky behaviour is removed, all four beats are laid out sequentially as static\n     blocks, and the globe renders at a fixed camera with no rotation.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the pinned stage section is taller than 200vh","a sticky element with height ~100vh exists","canvas renders non-blank pixels","all four beat texts are present in the DOM","scroll progress drives a transform: a value changes measurably after scrolling 50% of the stage","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"],"usage":{"build_tokens":10955,"build_prompt_tokens":1855,"build_completion_tokens":9100,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":12,"build_latency_ms":92643,"wasted_tokens":160565,"repair_tokens":19828,"crosscheck_tokens":45815,"total_tokens":76598},"verification":{"passed":true,"passed_checks":23,"total_checks":23,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"25627 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"25 frames, effects: pinned-stage, scroll-progress, globe-3d, globe-pins, globe-route, rail-progress, beat-crossfade, color-lerp, radial-bloom"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"65 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 60.63, stdev 75.4, coverage 0.4004"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 1099744252 -> 2371913888"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Nine degrees of nowhere\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Atlas Nine — Scroll Atlas"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"[prompt] scroll stage taller than 2x viewport","pass":true,"detail":"true"},{"name":"[prompt] sticky pinned element present","pass":true,"detail":"true"},{"name":"[prompt] all 4 beat texts in DOM","pass":true,"detail":"4/4"},{"name":"[prompt] canvas repaints after scroll","pass":true,"detail":"669830 -> 1413358"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=768 pageErrors=0"}]},"crosscheck":{"slug":"scroll-atlas","reviewer_model":"gpt-5.4","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":6,"requirements_met":["Fixed translucent blurred top bar with correct nav items and pill CTA","Pinned full-viewport stage with canvas, stacked text beats, and right-side progress rail","All four requested beat titles and body lines are present as real DOM text","Canvas visual is procedurally drawn rather than image-based","Closing section includes the requested h2 and ghost-style CTA","Footer is arranged as three columns with a bottom copyright row","Dark palette and serif display treatment broadly match the brief"],"requirements_missed":["Globe is not presented clearly as a readable rotating sphere in the shown composition","Beat cross-fade window is not the requested 0.08 width","Survey pins are not rendered as small filled circles with a 1.5px halo","Precomputation does not use typed arrays as specified","Display font is applied beyond h1/h2 despite the brief limiting it","There is continuous pulsing UI animation in the stage chrome, which conflicts with the film-scrub-only motion direction","Route connection logic can break when a predecessor pin is back-facing instead of consistently connecting each new pin to its predecessor"],"issues":[{"issue":"The main visual reads as an oversized clipped grid with a horizontal divider, not a clear globe of front-hemisphere arcs.","severity":"high","evidence":"In the screenshot the circular form is so enlarged that only partial arcs are visible and a single straight purple line spans the full viewport width, dominating the composition."},{"issue":"Beat crossfade timing is wider than specified.","severity":"medium","evidence":"Code sets `const win = 0.08` but then uses `smoothstep(0, win*2, d)`, creating a 0.16-wide opacity/translate window instead of the requested 0.08."},{"issue":"Pin styling does not match the brief's small filled circles with a 1.5px halo.","severity":"medium","evidence":"Pins are drawn with three circles: `arc(..., 5.5)`, `arc(..., 3.2)` and `arc(..., 1.8)`, which produces a much larger marker system than requested."},{"issue":"Required typed-array precomputation technique is not implemented.","severity":"medium","evidence":"Sphere data is built into nested normal arrays via `pts.push([ ... ])`; no `Float32Array` or other typed arrays are used."},{"issue":"The wordmark uses the display serif font even though the brief reserves it for h1/h2 only.","severity":"low","evidence":"`.word{ font-family: var(--display); }` applies Instrument Serif to header and footer branding."},{"issue":"There is extra autonomous motion unrelated to scroll scrubbing.","severity":"low","evidence":"`.stage__topright .pulse` runs `animation:pulse 1.8s ease-in-out infinite;` even though the brief says the page should feel like scroll-driven film scrubbing and not generic animated chrome."},{"issue":"Route drawing can drop continuity instead of always connecting a pin to its predecessor.","severity":"medium","evidence":"Inside the pin loop, `if(q[2] > 0.02){ prevProj = null; continue; }` resets the predecessor whenever a pin is back-facing."}],"summary":"The page hits the broad structure and copy, but the hero composition is off: the globe is overblown and cropped until it stops reading as the requested object. Several brief-specific implementation details are also wrong, especially the crossfade timing, pin styling, and required typed-array technique.","usage":{"input":11651,"output":1417,"total":13068,"reasoning":534,"cached":0},"latency_ms":17488}},{"slug":"magnetic-orbit","title":"Magnetic Orbit","category":"Hero Section","effect":"Cursor-reactive magnetic hero with spotlight, tilt and a custom cursor","platform":"workbuddy","platform_label":"Workbuddy proxy","model":"glm-5.3","model_label":"GLM-5.3 (glm-5.3)","demo":"/demos/magnetic-orbit/index.html","doc":"/prompts/magnetic-orbit.md","prompt_text":"DESIGN BRIEF — \"Magnetic Orbit\"\n\nBuild a cursor-reactive hero for a fictional creative-technology studio called \"FIELD\".\nThe page should feel like it is physically aware of the pointer: light, weight and\nmagnetism. Everything reacts; nothing jitters.\n\nA. CUSTOM CURSOR (desktop pointer only)\n   - Hide the native cursor inside the hero (`cursor: none`) and draw a two-part\n     cursor: a 7px solid dot that tracks the pointer at 1.0 lerp (essentially instant),\n     and a 40px ring that follows with a 0.14 lerp so it trails with visible weight.\n   - The ring scales to 1.9x and its border thins from 1.5px to 1px when hovering any\n     interactive element; it scales to 3.2x and fills with rgba(249,115,22,.12) when\n     hovering a `[data-magnetic]` element.\n   - Do NOT hide the native cursor on coarse pointers — guard with\n     `window.matchMedia('(pointer: fine)')` and only then enable.\n   - The cursor must be aria-hidden and pointer-events: none.\n\nB. THE SPOTLIGHT\n   - A fixed radial spotlight (radial-gradient, 520px radius, rgba(249,115,22,.14) →\n     transparent) follows the pointer across the hero with a 0.10 lerp, blended with\n     mix-blend-mode: screen. Behind it, a second, larger, colder spotlight\n     (rgba(56,189,248,.10), 900px) follows with a 0.04 lerp, creating depth.\n   - Both spotlights are driven from the single rAF loop using cached pointer\n     coordinates; never attach a mousemove handler that writes layout.\n\nC. MAGNETISM\n   - Any element with `data-magnetic` translates toward the pointer when it is within\n     140px of the element centre, with strength falling off as\n     `(1 - d/140)^2 * 14px`, capped at 14px. On pointer leave it springs back using a\n     critically-damped spring (stiffness 0.12, damping 0.78), not a CSS transition.\n   - Apply this to: the primary CTA button, the three orbit nodes, and the logo mark.\n\nD. COMPOSITION\n   - <header>: a thin top bar with the wordmark \"FIELD\" (tracking 0.3em, 12px, 700) on\n     the left, and on the right \"Index\" / \"Contact\" plus a bordered pill \"Book a call\".\n   - <main> hero, min-height 100vh, centred:\n       * <h1> at clamp(3rem, 9vw, 7rem), line-height 0.94, letter-spacing -0.035em,\n         system sans, weight 500: \"Design that / pulls back.\" with \"pulls back.\"\n         in rgba(242,242,245,.45).\n       * Under it, max-width 480px, 16.5px/1.7, rgba(242,242,245,.62): \"FIELD is a\n         three-person studio building interfaces that answer to the hand that moves them.\"\n       * A row: primary CTA \"Start a project\" (solid #f97316, black text, rounded-full)\n         and a ghost \"See the index\" (1px border rgba(255,255,255,.18)).\n       * ORBIT NODES: three absolutely positioned circular glass nodes (86px, 108px,\n         72px) scattered around the headline at pre-set coordinates, each containing a\n         2-3 word label (\"Motion\", \"Systems\", \"Brand\"). They drift on slow independent\n         sine paths (±8px, 11-17s periods), are `data-magnetic`, and tilt up to 10deg\n         toward the pointer with perspective 900px. On hover each node scales to 1.08\n         and its label brightens.\n   - A thin marquee strip below the hero (a single row, 1px top and bottom hairlines,\n     12px uppercase, 0.24em tracking, opacity .5) reading\n     \"MOTION · SYSTEMS · BRAND · INTERFACES · SOUND ·\" repeated — translated\n     continuously by the rAF loop at 26px/s, wrapping seamlessly using two duplicated\n     spans and a modulo offset.\n   - <footer>: one row, \"© 2026 FIELD Studio\" and an email link.\n\nE. SYSTEM\n   - Palette: base #0a0a0b, ink #f2f2f5, muted rgba(242,242,245,.62), accent #f97316,\n     cold #38bdf8.\n   - Fonts: system sans for everything (no webfont) — this page must be 100% offline.\n   - A faint 48px grid overlay at 3% opacity behind the hero, masked with a radial\n     gradient so it fades out at the edges.\n\nF. TOUCH DEVICES\n   - On coarse pointers: no custom cursor, no spotlight (render a static centred glow),\n     nodes become a normal 3-up grid, magnetism is disabled. The page must look\n     deliberate, not broken.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["a custom cursor element exists and is pointer-events:none","at least 3 elements carry data-magnetic","a marquee strip exists and its transform changes over time","a spotlight element with a radial-gradient exists","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"],"usage":{"build_tokens":7209,"build_prompt_tokens":1862,"build_completion_tokens":5347,"build_reasoning_tokens":5347,"build_calls":1,"build_attempts":7,"build_latency_ms":74179,"wasted_tokens":135448,"repair_tokens":13250,"crosscheck_tokens":10614,"total_tokens":31073},"verification":{"passed":true,"passed_checks":21,"total_checks":21,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"17283 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"3 frames, effects: custom-cursor, spotlight-warm, spotlight-cold, magnetism, node-drift, node-tilt, marquee"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"34 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Design that\npulls back.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"FIELD — Design that pulls back"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"[prompt] custom cursor (fixed, pointer-events:none) present","pass":true,"detail":"true"},{"name":"[prompt] >= 3 data-magnetic elements","pass":true,"detail":"6"},{"name":"[prompt] radial-gradient spotlight present","pass":true,"detail":"true"},{"name":"[prompt] marquee transform advances over time","pass":true,"detail":"none | matrix(1, 0, 0, 1, -135.2, 0) -> none | matrix(1, 0, 0, 1, -159.03, 0)"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=378 pageErrors=0"}]},"crosscheck":{"slug":"magnetic-orbit","reviewer_model":"MiniMaxAI/MiniMax-M3","reviewer_platform":"cmdcode","verdict":"partial","confidence":"high","aesthetic_score":7,"requirements_met":["Wordmark FIELD in header with correct tracking/weight","Index, Contact, Book a call pill in header","H1 copy 'Design that pulls back.' with second line dimmed","Lede paragraph with correct copy and styling","Primary CTA solid orange with rounded-full","Ghost CTA with 1px translucent border","Three orbit nodes with Motion/Systems/Brand labels","Magnetic nodes drift on slow independent sine paths","Marquee strip with MOTION · SYSTEMS · BRAND · ... repeated","Footer with copyright and email","Custom cursor dot and ring with lerp","Two spotlights (warm + cold) with mix-blend-mode: screen","Magnetism applied to CTA, nodes, and logo","Critically-damped spring back on pointer leave","Grid overlay with radial mask","System fonts only, no external resources","Touch fallback: static glow when no fine pointer","Reduced motion respected"],"requirements_missed":["Cursor ring is sized at 40px but element has 40px width/height so 3.2x scale makes it 128px — does not match the 1px border thinning on interactive hover (border remains at calculated 1px only when explicitly set, not the 1.5→1 transition described)","Magnetic offset direction: brief says 'translates toward the pointer' but code applies sign-inverted offset (multiplied by -1), pushing elements AWAY from the pointer","Tilt direction looks reversed: code uses (-dy) for rotateX and (dx) for rotateY which tilts nodes away from pointer in a non-intuitive way, and clamp is ±10 but values can hit ±10 abruptly","Spotlight translation subtracts 520/900 from coords (intended to center radial gradient on pointer) — this is correct only if radial gradient is centered at 50% 50%; the spot element is 1040×1040 with 520px gradient at center, so subtract 520 is correct, but spotCold is 1800×1800 with 900px gradient, so subtract 900 is correct — actually this is fine","marqueeTrack: halfWidth measured only on first child span, and modulo uses that — but with two duplicated spans, after first wrap the visible content shifts; the implementation may not wrap seamlessly if the two spans have different measured widths (they should match but it's fragile)","Cursor ring uses 'transform-origin: center' only via class but transform string also includes translate3d — the scale origin will be at the translated position's center which is correct","Node hover detection via mouseenter/mouseleave sets --hs but never reads it; instead the rAF loop uses m.hovering which is set by pointerover delegation — there is a code path that sets both, indicating confusion","The hero 'min-height: 100vh' plus large padding plus 3 absolutely positioned nodes plus orbit-stage with z-index means nodes can sit on top of or behind text depending on z-stacking — node-3 is at left:22% bottom:20% which is fine, but at smaller viewports the orbit-stage becomes static and nodes appear below the CTA — this is acceptable per brief for touch devices only, but the breakpoint is 900px which is mid-desktop","Brief says custom cursor should be 'inside the hero' only; code applies it body-wide via body.fine-pointer class","Eyebrow text 'A studio of three — everywhere at once' is present in code but not described in brief — extra content not required","Brief specifies 'logo mark' should be data-magnetic — wordmark is data-magnetic but is text, not a mark"],"issues":[{"issue":"Magnetic elements move AWAY from pointer instead of toward it","severity":"high","evidence":"if (d > 0.001){ ox += (dx / d) * s * -1; oy += (dy / d) * s * -1; } — the * -1 inverts the direction. Brief C explicitly says 'translates toward the pointer'."},{"issue":"Ring border 'thinning' on interactive hover not implemented as described","severity":"low","evidence":"var bw = magneticHover ? 1 : (interactiveHover ? 1 : 1.5); — border is 1px on BOTH magnetic and interactive, so the 1.5→1 transition between interactive and default works, but magnetic should keep 1px which it does; the transition CSS uses 'background .2s ease, border-color .2s ease' but border-width is set inline each frame so no transition occurs"},{"issue":"Custom cursor not scoped to hero only","severity":"low","evidence":"body.fine-pointer .hero{cursor:none} applies to hero, but cursor-dot/ring are fixed positioned and visible everywhere; brief A says 'Hide the native cursor inside the hero'"},{"issue":"Node tilt direction appears reversed/intuitive-fail","severity":"medium","evidence":"tiltY = (dx / m.w) * 10; tiltX = (-dy / m.h) * 10; — when pointer is to the right of node, dx>0 so tiltY is positive (rotateY positive), which rotates node's right edge away — counterintuitive; brief says 'tilt up to 10deg toward the pointer'"},{"issue":"Orbit nodes break into 3-up grid at <=900px (tablet portrait and below)","severity":"medium","evidence":"@media (max-width:900px){ .orbit-stage{position:static; ... display:flex; ...} } — at 768px tablet width, nodes become static grid below CTA, which is the 'coarse pointer' fallback but 900px is broader than touch-only"},{"issue":"Orbit-stage and hero-inner have overlapping z-index with nodes potentially rendering over text","severity":"low","evidence":"orbit-stage z-index:4, hero-inner z-index:5, nodes absolutely positioned at left:9% top:22% etc. — at 1024x640 the node-1 left:9% top:22% places it at roughly 92px x 141px which is clear of text but tight"},{"issue":"Screenshot shows the page renders correctly overall but node-3 (Brand) overlaps the orange CTA button","severity":"medium","evidence":"node-3 {left:22%; bottom:20%} at 1024x640 puts the 72px node at approximately (225, 512) which visually overlaps the 'Start a project' button in the screenshot"},{"issue":"H1 has <br> instead of the two-line layout the brief implies","severity":"low","evidence":"<h1>Design that<br><span class=\"dim\">pulls back.</span></h1> — uses literal <br> rather than two block elements; visually identical so acceptable"},{"issue":"Marquee uses 'padding-right:0' and a trailing &nbsp; in spans; seamless wrap depends on duplicated spans being identical — fragile but works","severity":"low","evidence":"<span>MOTION · SYSTEMS · BRAND · INTERFACES · SOUND ·&nbsp;</span><span>...</span>"}],"summary":"Composition, copy, palette and structure match the brief closely, but the magnetic offset is sign-inverted so elements repel rather than attract the pointer, the cursor ring isn't scoped to the hero, and the Brand node visually collides with the CTA in the 1024x640 screenshot. Aesthetically it reads as a deliberate, premium studio page rather than a default AI template.","usage":{"input":8842,"output":1772,"total":10614,"reasoning":0,"cached":0},"latency_ms":28021}},{"slug":"neural-drift","title":"Neural Drift","category":"Landing Page","effect":"Procedural canvas constellation field with depth parallax and pointer repulsion","platform":"workbuddy","platform_label":"Workbuddy proxy","model":"kimi-k3","model_label":"Kimi-K3 (kimi-k3)","demo":"/demos/neural-drift/index.html","doc":"/prompts/neural-drift.md","prompt_text":"DESIGN BRIEF — \"Neural Drift\"\n\nBuild a landing page for a fictional ML infrastructure company called \"Cortex\" whose hero\nis a living constellation field rendered on canvas. The field is the product metaphor:\nthousands of small signals, a few strong connections.\n\nA. THE FIELD (canvas, full hero)\n   - 3 depth layers of particles: 90 far (radius 0.6px, alpha .18), 55 mid (1.1px, .34),\n     26 near (1.9px, .6). Total 171 nodes — enough to look dense, cheap enough to stay\n     at 60fps.\n   - Each particle has a position, a velocity (0.06–0.22 px/frame, scaled by layer depth)\n     and wraps around the edges. Nearer layers move faster and are brighter — that is the\n     parallax.\n   - Connections: for every pair within 118px, draw a 1px line whose alpha is\n     `(1 - d/118)^1.6 * 0.30 * layerAlpha`. Use a spatial hash grid of 118px cells so\n     this stays O(n) — do NOT write a naive O(n²) loop.\n   - Pointer repulsion: nodes within 150px of the pointer are pushed away with force\n     `(1 - d/150)^2 * 1.9`, integrated with a 0.06 lerp back to their base velocity so\n     they settle rather than explode.\n   - Add 3 \"signal pulses\": every 4-7 seconds a pulse travels from a random near-node\n     outward along its connections, brightening each edge to alpha .85 for 400ms and\n     briefly enlarging the nodes it passes. Implement with a queue, not recursion.\n   - Bloom: after drawing, composite a scaled-down copy of the canvas onto itself with\n     `globalAlpha = 0.28` and `filter: blur(6px)` via an offscreen canvas — a cheap\n     two-pass bloom. Cap devicePixelRatio at 2.\n\nB. THE PAGE\n   - <header>: sticky, blurred, rgba(6,7,9,.6) + backdrop-filter blur(14px). Wordmark\n     \"CORTEX\" with a small canvas-free SVG glyph (three connected dots). Nav: \"Platform\",\n     \"Docs\", \"Pricing\", \"Careers\". Right: \"Sign in\" + a solid #34d399 pill \"Deploy\".\n   - Hero, over the field, centred, with a subtle dark scrim so text always wins:\n       * Eyebrow pill: \"SOC 2 TYPE II · 99.99% UPTIME\"\n       * <h1> clamp(2.6rem, 7.5vw, 5.6rem), weight 600, line-height 1.0,\n         letter-spacing -0.03em: \"Every signal, / finally connected.\"\n       * Paragraph (max-width 540px): \"Cortex ingests 40 billion events a day and turns\n         them into the two or three that actually matter.\"\n       * Two buttons: solid \"Start free\" and bordered \"Read the docs →\".\n       * A live-looking telemetry strip: three monospace figures in a glass card that\n         update every 1.4s with plausible jitter — \"INGEST  41.2k/s\",\n         \"P99  18ms\", \"NODES  1,284\" — each value ticking with a 200ms count-up.\n         Mark this card aria-live=\"off\" so it does not spam screen readers.\n   - A features section (3 cards, grid → 1 col under 720px), each with an inline SVG\n     icon drawn with strokes only: \"Streaming ingest\", \"Sub-20ms queries\",\n     \"Zero-copy pipelines\". Cards lift 2px and gain a 1px #34d399/40 border on hover.\n   - A closing CTA band with an <h2> \"Ship the pipeline, not the plumbing.\" and one\n     button.\n   - <footer>: four columns (Product / Developers / Company / Legal) plus a bottom row\n     with \"© 2026 Cortex Systems, Inc.\" and a status pill with a pulsing green dot\n     reading \"All systems normal\".\n\nC. SYSTEM\n   - Palette: base #060709, surface #0d1013, ink #eef2f4, muted rgba(238,242,244,.6),\n     accent #34d399, line rgba(255,255,255,.09).\n   - Fonts: system sans for UI; \"JetBrains Mono\" via Google Fonts for the telemetry\n     strip ONLY, with ui-monospace fallback.\n\nD. REDUCED MOTION\n   - Render the field once, statically (no rAF loop, no pulses, no repulsion), and\n     disable the telemetry ticking. The page must not look empty.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["canvas renders non-blank pixels","a telemetry strip with monospace font exists","at least 3 feature cards exist","canvas pixel content changes between two samples 800ms apart","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"],"usage":{"build_tokens":32693,"build_prompt_tokens":1839,"build_completion_tokens":30854,"build_reasoning_tokens":21772,"build_calls":1,"build_attempts":2,"build_latency_ms":751249,"wasted_tokens":33839,"repair_tokens":0,"crosscheck_tokens":12520,"total_tokens":45213},"verification":{"passed":true,"passed_checks":21,"total_checks":21,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"27426 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"19 frames, effects: constellation-field, depth-parallax, spatial-hash-connections, hub-nodes, pointer-repulsion, signal-pulses, telemetry-ticker, two-pass-bloom"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"40 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 6.19, stdev 2.86, coverage 1"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 2449348892 -> 3318836846"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Every signal,\nfinally connected.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Cortex — Every signal, finally connected"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"[prompt] monospace telemetry strip present","pass":true,"detail":"true"},{"name":"[prompt] three feature cards present","pass":true,"detail":"3/3"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=1132 pageErrors=0"}]},"crosscheck":{"slug":"neural-drift","reviewer_model":"glm-5.2","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":8,"requirements_met":["3 depth layers with correct counts (90/55/26), radii, and alphas","Spatial hash grid used for connections (O(n) neighbor scan via cell bucketing)","Pointer repulsion with correct force formula and 0.06 lerp back to base velocity","Signal pulses via BFS queue (no recursion), 4-7s interval, 400ms duration","Two-pass bloom with offscreen canvas, globalAlpha 0.28, blur(6px)","devicePixelRatio capped at 2","Sticky blurred header with correct rgba and blur(14px)","SVG glyph with three connected dots","Correct nav items, Sign in, and Deploy pill","Hero scrim for text legibility","Eyebrow pill, h1 with correct clamp/font/letter-spacing, paragraph with max-width 540px","Telemetry card with correct labels, aria-live=off, 1.4s update interval, 200ms count-up","Features section with 3 cards, stroke-only SVG icons, 2px lift + #34d399/40 border on hover, 720px breakpoint","Closing CTA band with correct h2 copy and one button","Footer with four columns and bottom row with copyright and pulsing green dot status pill","Correct palette and font usage (JetBrains Mono for telemetry only)","Reduced motion renders field statically and disables telemetry ticking","window.__DEMO__ telemetry hook with all required fields"],"requirements_missed":["Hub nodes are not the specified 'near-nodes'; pulses originate from random near-nodes but 7 hub nodes are drawn from indices 90-170 (mid+far), contradicting 'a random near-node' for pulse origin while also adding an unrequested 'hub-nodes' visual effect with amplified edges and green tinting"],"issues":[{"issue":"Pulse spawning selects from NEAR array (correct), but an unrequested 'hubs' system creates 7 visually-distinct green nodes from indices 90+ (mid and far layers), which is not in the brief and visually deviates from the pure constellation metaphor","severity":"medium","evidence":"var picked = 0; while (picked < 7) { var c = 90 + ((Math.random() * (N - 90)) | 0); ... hubs[c] = 1; } and drawNodes fills hubs with 'rgba(52,211,153,0.8)' at 1.5x radius"},{"issue":"Connection alpha formula multiplies by 1.9 for hub-hub pairs, an unrequested modification that overrides the brief's specified alpha formula","severity":"low","evidence":"if (hubs[i] && hubs[j]) a *= 1.9;"},{"issue":"Telemetry format for INGEST displays '41.2k/s' but the brief specified 'INGEST  41.2k/s' as the displayed string; the implementation splits label and value into separate spans losing the inline monospace figure format","severity":"low","evidence":"<span class=\"t-label\">INGEST</span><span class=\"t-value\" id=\"tIngest\">41.2k/s</span>"},{"issue":"Reduced-motion path sets D.frames=1 but does not push 'signal-pulses' or 'pointer-repulsion' into effects array (effects are pushed conditionally before reduced check); the 'telemetry-ticker' effect is only pushed in the non-reduced branch which is correct, but the effects list naming is slightly inconsistent with the brief's described effects","severity":"low","evidence":"if (!reduced) D.effects.push('pointer-repulsion', 'signal-pulses', 'telemetry-ticker');"}],"summary":"The field, motion choreography, and page structure faithfully implement nearly every specification in the brief with correct particle counts, spatial hashing, BFS-based pulses, and two-pass bloom. The only real deviation is an unrequested 'hub nodes' system that introduces 7 green-accented nodes from mid/far layers and amplifies certain edges, which is not part of the specified constellation metaphor.","usage":{"input":11543,"output":977,"total":12520,"reasoning":0,"cached":0},"latency_ms":16671}},{"slug":"kinetic-type","title":"Kinetic Type","category":"Hero Section","effect":"Split-text mask reveal, velocity-skewed marquee bands and scroll-linked type scale","platform":"workbuddy","platform_label":"Workbuddy proxy","model":"gemini-3.5-flash","model_label":"Gemini-3.5-Flash (gemini-3.5-flash)","demo":"/demos/kinetic-type/index.html","doc":"/prompts/kinetic-type.md","prompt_text":"DESIGN BRIEF — \"Kinetic Type\"\n\nBuild an editorial, type-first hero for a fictional independent magazine called \"OFFCUT\".\nTypography IS the interface here. No illustrations, no photography — only letters, rules,\nand motion.\n\nA. THE SPLIT-TEXT REVEAL (on load, once)\n   - Split the <h1> into per-word <span class=\"w\"> wrappers, each containing a\n     <span class=\"wi\"> inner. The outer has overflow: hidden; the inner starts at\n     translateY(105%) and animates to 0 with a cubic-bezier(.16,1,.3,1) ease over 900ms,\n     staggered 65ms per word, driven by the shared rAF loop (compute progress from a\n     start timestamp — do not use setTimeout chains).\n   - Respect prefers-reduced-motion: skip the reveal entirely and render final state.\n\nB. THE MARQUEE BANDS (three of them)\n   - Band 1 (top, 13px, uppercase, 0.3em tracking, opacity .55): slow left drift,\n     \"OFFCUT — ISSUE 14 — SPRING — \" repeated.\n   - Band 2 (behind the h1, ~11vw, weight 800, colour transparent with a 1px\n     rgba(255,255,255,.13) -webkit-text-stroke, uppercase): fast right drift reading\n     \"PRINT IS NOT DEAD IT JUST GOT FASTER\".\n   - Band 3 (below, 15px, weight 600): left drift with the words\n     \"TYPE / GRID / INK / PAPER / TYPE / GRID / INK / PAPER\".\n   - All three are driven by ONE rAF loop. Crucially, the drift speed must be modulated\n     by scroll velocity: compute `v = (scrollY - lastScrollY)`, smooth it with a 0.12\n     lerp, and offset the bands by `baseSpeed + clamp(v * 0.35, -14, 14)` px/frame. The\n     bands should visibly surge when the user scrolls fast and settle when they stop.\n   - Additionally apply a velocity skew: each band's transform includes\n     `skewX(clamp(v * -0.06, -4, 4)deg)` so the type leans into the motion.\n   - Seamless wrap: duplicate the band content exactly twice and use a modulo offset of\n     half the scroll width. Measure the width once per resize, never per frame.\n\nC. SCROLL-LINKED TYPE SCALE\n   - The <h1> scales from 1.0 to 0.82 and its letter-spacing tightens from -0.01em to\n     -0.045em as the user scrolls the first 60vh, driven by rAF from a cached scroll\n     value. It must feel like the type is being squeezed by the scroll.\n\nD. THE PAGE\n   - <header>: a 3-part grid bar — left \"OFFCUT\" (900 weight, tracking -0.02em),\n     centre the issue meta \"ISSUE 14 / SPRING\", right \"Subscribe\" as an underlined link\n     plus a solid #facc15 pill \"Get the print edition\" (black text). Hairline rules\n     above and below the header.\n   - <main>:\n       * Hero with the h1: \"Slow down and / read the thing.\" — second line in italic\n         serif. Below it a 2-column block (stacked under 720px): a 15px/1.7 paragraph\n         and a pull-quote with a 3px #facc15 left border.\n       * An \"index\" section: a numbered list of 5 article titles (01–05) with hairline\n         separators; each row is an <a> that on hover slides its title 8px right and\n         reveals a small \"→\". Implement hover motion with CSS transform only.\n       * A closing subscribe block: <h2> \"One issue. Every quarter. No feed.\" plus an\n         inline email form that validates non-empty and shows an inline success message.\n   - <footer>: masthead, three link columns (Read / About / Legal), and a bottom row\n     \"© 2026 OFFCUT Press — printed in Rotterdam\".\n\nE. SYSTEM\n   - Palette: paper #f4f1ea, ink #14140f, muted rgba(20,20,15,.6), accent #facc15.\n     This page is LIGHT — the only light page in the set. Use dark text on paper.\n   - Fonts: \"Instrument Serif\" (display + italic pull-quotes) and\n     \"Inter\" (UI, 400/600/900) via Google Fonts, with Georgia and system-ui fallbacks.\n   - Everything sits on a strict 12-column grid with 24px gutters; hairline rules are\n     1px rgba(20,20,15,.16).\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["h1 is split into per-word spans (>= 4 elements with class \"w\")","at least 3 marquee bands exist","a band transform changes over time","page background is light (luminance > 0.6)","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"],"usage":{"build_tokens":11429,"build_prompt_tokens":1879,"build_completion_tokens":9550,"build_reasoning_tokens":1995,"build_calls":1,"build_attempts":5,"build_latency_ms":40809,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":12540,"total_tokens":23969},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"23208 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"37 frames, effects: split-text-reveal, velocity-marquees, scroll-scale"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"73 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"SLOW DOWN AND\nread the thing.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"OFFCUT — Independent Magazine"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"[prompt] h1 split into >= 4 word spans","pass":true,"detail":"6 .w elements"},{"name":"[prompt] >= 3 oversized nowrap marquee bands","pass":true,"detail":"6"},{"name":"[prompt] light page (background luminance > 0.6)","pass":true,"detail":"0.945"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=1732 pageErrors=0"}]},"crosscheck":{"slug":"kinetic-type","reviewer_model":"moonshotai/Kimi-K2.7-Code","reviewer_platform":"cmdcode","verdict":"partial","confidence":"high","aesthetic_score":7,"requirements_met":["single valid HTML file","header 3-part grid with logo, meta, actions","hero h1 split into per-word w/wi spans","three marquee bands present and driven by one rAF loop","scroll-linked h1 scale and letter-spacing","two-column hero content stacks below 720px","index list with five numbered rows and CSS hover transforms","subscribe form with inline success message","footer masthead + link columns + bottom row structure","light paper palette with Instrument Serif/Inter fonts","prefers-reduced-motion safeguard"],"requirements_missed":["header hairline rule above the header","band 2 white 1px text-stroke color","marquee seamless wrap: duplicate twice + half-width modulo","exact footer bottom copy","hero paragraph size 15px/1.7","split reveal cubic-bezier(.16,1,.3,1) easing"],"issues":[{"issue":"Band 2 text-stroke is dark ink instead of the specified rgba(255,255,255,.13), making the 'PRINT IS NOT DEAD...' background band appear muddy or nearly invisible against the paper background instead of a subtle white outline.","severity":"medium","evidence":"CSS: `.band-2 span { -webkit-text-stroke: 1px rgba(20, 20, 15, 0.13); }` — brief requires `rgba(255,255,255,.13)`."},{"issue":"Marquee seamless-wrap implementation does not follow the brief's algorithm: it duplicates content by a calculated multiplier rather than exactly twice, and wraps using single-text-width modulo instead of half the scroll width.","severity":"medium","evidence":"JS: `const multiplier = Math.max(3, ...);` and `offset = baseOffsets[idx] % singleWidth;` instead of duplicate twice and half-width modulo."},{"issue":"Header lacks the required hairline rule above it; only a bottom border is present.","severity":"low","evidence":"HTML: `<header class=\"border-bottom\">` only; no `.border-top` or top border rule applied."},{"issue":"Footer bottom text does not match the brief: it reads '© 2026 OFFCUT Press — Rotterdam' and adds an extra 'Printed on FSC Certified 120gsm paper' line instead of '© 2026 OFFCUT Press — printed in Rotterdam'.","severity":"low","evidence":"HTML: `<span>© 2026 OFFCUT Press — Rotterdam</span><span>Printed on FSC Certified 120gsm paper</span>`."},{"issue":"Hero paragraph font size is 16px, not the requested 15px.","severity":"low","evidence":"CSS: `.hero-desc { font-size: 16px; line-height: 1.7; ... }`."},{"issue":"Split-text reveal easing is a quartic ease-out approximation, not the specified cubic-bezier(.16,1,.3,1).","severity":"low","evidence":"JS: `const ease = 1 - Math.pow(1 - t, 4); // Quartic approximation of easeOut`."}],"summary":"The page looks intentionally editorial and type-driven, but several concrete spec mismatches remain: the background stroke band is the wrong color, the header top rule is missing, the marquee wrap algorithm is off, and the footer copy is wrong. It feels premium at first glance, but a hostile review finds the brief was not followed to the letter.","usage":{"input":9694,"output":2846,"total":12540,"reasoning":0,"cached":0},"latency_ms":44487}},{"slug":"stack-deck","title":"Stack Deck","category":"Landing Page","effect":"Pinned 3D card deck that stacks and expands on scroll, with animated stat counters","platform":"workbuddy","platform_label":"Workbuddy proxy","model":"gpt-5.5","model_label":"GPT-5.5 (gpt-5.5)","demo":"/demos/stack-deck/index.html","doc":"/prompts/stack-deck.md","prompt_text":"DESIGN BRIEF — \"Stack Deck\"\n\nBuild a product landing page for a fictional release-engineering platform called\n\"Harbour\". The centrepiece is a pinned deck of five cards that physically stack as you\nscroll, then fan out into a bento grid at the end.\n\nA. THE DECK\n   - Structure: `<section class=\"deck-wrap\">` at 500vh containing a\n     `position: sticky; top: 0; height: 100vh` inner stage. Progress\n     p = clamp(scrolled / (wrapHeight - viewportHeight), 0, 1).\n   - Five cards, each 460x300 (max-width 88vw), absolutely centred, with\n     `transform-style: preserve-3d` and `perspective: 1400px` on the stage.\n   - Choreography, driven by p, all via transform (never top/left):\n       * For card i (0-indexed), its \"arrival\" happens in the window\n         [i*0.16, i*0.16 + 0.16]. Before arrival it sits at translateY(60vh) scale(.92)\n         rotateX(18deg) with opacity 0. On arrival it eases to translateY(-i*14px)\n         scale(1 - i*0.012) rotateX(0) opacity 1 — so each new card lands slightly\n         above and slightly smaller than the one before, forming a visible deck edge.\n       * Cards already landed recede: their brightness drops by 5% per card stacked on\n         top of them (use filter: brightness()).\n       * Between p = 0.82 and 1.0 the deck \"deals\": the five cards fan out to a bento\n         layout of preset final positions (a 3+2 arrangement) with a 2deg alternating\n         rotation, using a smoothstep on the last window.\n   - Each card has: a coloured top hairline, a small index label \"01\"–\"05\", an <h3>,\n     two lines of body copy, and a distinct inline-SVG diagram drawn with strokes only\n     (a pipeline, a branch tree, a gauge, a timeline, a shield). Cards are glass:\n     rgba(255,255,255,.045), 1px rgba(255,255,255,.09) border, border-radius 18px,\n     backdrop-filter blur(10px), and a soft 40px shadow.\n   - Add a small \"1 / 5\" progress readout pinned to the bottom of the stage whose number\n     is the index of the most recently landed card.\n\nB. STAT COUNTERS\n   - Below the deck, a 4-up stat row: \"12,400  deploys a day\", \"38s  median release\",\n     \"99.98%  success rate\", \"0  rollback surprises\". Counters animate 0 → target with\n     easeOutExpo over 1.8s on first intersection, formatted with thousands separators.\n     The \"%\" and \"s\" suffixes are separate <span>s so the number can animate cleanly.\n\nC. THE REST OF THE PAGE\n   - <header>: sticky blurred bar. Wordmark \"HARBOUR\" with an inline SVG anchor glyph.\n     Nav \"Product\", \"Pricing\", \"Changelog\", \"Docs\". Right: \"Sign in\" + solid #6366f1\n     pill \"Start free\".\n   - Hero above the deck: <h1> clamp(2.5rem, 7vw, 5.2rem) weight 650 letter-spacing\n     -0.03em: \"Ship on Friday. / Sleep on Saturday.\" with the second line in\n     rgba(237,238,244,.42). Paragraph max-width 560px: \"Harbour turns your release\n     process into a pipeline you can actually read.\" Two buttons: solid \"Start free\",\n     bordered \"Watch the 2-min tour\".\n   - A closing CTA band: <h2> \"Your next release is already written.\" and one button.\n   - <footer>: wordmark + description, three link columns, a bottom row with\n     \"© 2026 Harbour Labs\" and a status pill \"All systems operational\" with a green dot.\n\nD. SYSTEM\n   - Palette: base #08090f, surface #101223, ink #edeef4, muted rgba(237,238,244,.58),\n     accent #6366f1, secondary #22d3ee, plus five card hues\n     (#6366f1, #22d3ee, #f472b6, #facc15, #34d399).\n   - Fonts: system sans for UI; \"Instrument Serif\" via Google Fonts for the h1 second\n     line and the h2s only.\n\nE. ROBUSTNESS\n   - Recompute the deck rect on resize (debounced 150ms) and on orientationchange.\n   - With prefers-reduced-motion: reduce: the deck-wrap collapses to auto height, the\n     sticky stage becomes static, all five cards render in a plain vertical stack at\n     their final fan-out positions, and the counters jump straight to their targets.\n   - Guard the whole scroll engine so that if IntersectionObserver is unavailable the\n     page still renders everything in final state.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["exactly 5 deck cards exist","a sticky stage exists inside a section taller than 300vh","card transforms change after scrolling into the deck","four stat counters exist and end at 12400 / 38 / 99.98 / 0","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"],"usage":{"build_tokens":7752,"build_prompt_tokens":1837,"build_completion_tokens":5915,"build_reasoning_tokens":18,"build_calls":1,"build_attempts":5,"build_latency_ms":43980,"wasted_tokens":0,"repair_tokens":14646,"crosscheck_tokens":31794,"total_tokens":54192},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"18218 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"49 frames, effects: scroll-stacked-deck, fan-out-bento, stat-counters, reduced-motion-fallback"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"73 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Ship on Friday.\nSleep on Saturday.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Harbour — Stack Deck"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"[prompt] card elements present","pass":true,"detail":"5 elements matching [class*=card]"},{"name":"[prompt] stat counters reached targets after scroll","pass":true,"detail":"4/4 markers"},{"name":"[prompt] card transform changes on scroll","pass":true,"detail":"matrix3d(0.92, 0, 0, 0, 0, 0.874972, 0.3 -> matrix(1, 0, 0, 1, 0, 0)"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=1529 pageErrors=0"}]},"crosscheck":{"slug":"stack-deck","reviewer_model":"kimi-k2.8-preview","reviewer_platform":"workbuddy","verdict":"confirmed","confidence":"high","aesthetic_score":8,"requirements_met":["500vh deck-wrap with sticky 100vh stage and perspective: 1400px","five 460x300 glass cards (rgba white fill, .09 border, 18px radius, blur(10px), 40px shadow) with hue hairline, 01-05 index, h3, two copy lines, and five distinct stroke-only SVGs (pipeline, branch, gauge, timeline, shield) in the specified hue order","arrival window [i*0.16, i*0.16+0.16] driving translateY(60vh)->-i*14px, scale .92->1-i*.012, rotateX 18->0, opacity 0->1","brightness drops 5% per card stacked on landed cards","smoothstep deal between p=.82 and 1.0 into a 3+2 bento with 2deg alternating rotateZ","1 / 5 readout pinned to stage bottom tracking the most recently landed card","four stat counters animating 0->target with easeOutExpo over 1.8s, toLocaleString separators, s/% in separate spans","sticky blurred header with anchor glyph, exact nav labels, Sign in + solid #6366f1 Start free","hero clamp(2.5rem,7vw,5.2rem) weight 650 -0.03em h1 with muted Instrument Serif italic second line, 560px paragraph, both buttons","CTA band h2 and one button; footer with wordmark, description, three link columns, (c) 2026 Harbour Labs, green-dot status pill","resize debounce 150ms + orientationchange rect recompute; reduced-motion collapses deck and jumps counters","copy is real, specific, and on-theme throughout"],"requirements_missed":["IntersectionObserver-unavailable guard does not render the deck in final state: it calls renderDeck() once with wrapTop=0/travel=1, giving p=0 and all cards at opacity 0 (invisible), not the dealt bento the brief demands"],"issues":[{"issue":"Bento bottom row overflows short viewports: finalY=230 for cards 04/05 but deck height is min(620px,72vh), so at 640px-tall stages the dealt cards extend ~60px past the stage bottom and are clipped by overflow:hidden (diagrams and card bottoms cut off, readout overlaps)","severity":"medium","evidence":"var finalY=[-92,-92,-92,230,230]; .deck{height:min(620px,72vh)}; .deck-stage{...;overflow:hidden}"},{"issue":"No-IO fallback leaves deck invisible instead of final state","severity":"medium","evidence":"if(!('IntersectionObserver' in window)||reduce){setCountsFinal();renderDeck();...;return} — renderDeck computes p from wrapTop=0, travel=1 => p=0, opacity=t=0 for all cards"},{"issue":"Brightness dimming persists after the deal even though cards are no longer stacked (card 01 sits at 0.80 in the final bento), leaving the grid permanently uneven","severity":"low","evidence":"var stacked=Math.max(0,landed-1-i), bright=1-stacked*.05 — no reset when deal>0"},{"issue":"Muted ink deviates from the stated palette: --muted is rgba(237,238,244,.68) while brief specifies .58 (--soft is defined at .58 but barely used)","severity":"low","evidence":"--muted:rgba(237,238,244,.68)"},{"issue":"Card fill is a linear-gradient (.07 -> .035) rather than the specified flat rgba(255,255,255,.045)","severity":"low","evidence":".card{background:linear-gradient(145deg,rgba(255,255,255,.07),rgba(255,255,255,.035))}"}],"summary":"The screenshot's hero is clean and premium — strong clamp type, correct serif italic second line, tidy header — and the code faithfully implements the stacking, dimming, smoothstep deal, and counter choreography. It is docked for a no-IntersectionObserver path that renders an invisible deck instead of the final bento, and a dealt-state bottom row that gets clipped on short viewports.","usage":{"input":9333,"output":6001,"total":15334,"reasoning":4957,"cached":0},"latency_ms":98435}},{"slug":"aurora-freight","title":"Aurora Freight","category":"Landing Page","effect":"A procedural aurora light-field behind a bottom-anchored hero and liquid-metal nav","platform":"workbuddy","platform_label":"Workbuddy proxy","model":"hy4-preview-f","model_label":"Hy4 preview (hy4-preview-f)","demo":"/demos/aurora-freight/index.html","doc":"/prompts/aurora-freight.md","prompt_text":"\nDESIGN BRIEF — \"Aurora Freight\"\n\nA. WHAT IS BEING BUILT\n\nBuild ONE complete, valid, self-contained `index.html`: a single-viewport dark landing page for\na fictional logistics-AI company called **Aurora Freight**. Document title (exact):\n`Aurora Freight — Freight Intelligence, Orchestrated`. `<html lang=\"en\">`.\n\nThe page is: pure black (`#000000`) ground; a full-bleed PROCEDURAL aurora rendered on a\n<canvas> as the cinematic backdrop; a fixed header with a liquid-metal pill nav; a\nbottom-anchored (NOT vertically centred) hero with a two-line masked <h1>; and a three-item\nstats footer. That is the whole page. One frame. No scroll on desktop.\n\nDO NOT BUILD — every one of these is a failure:\n- No video, no WebGL, no Three.js, no Lottie, no GSAP, no jQuery, no React, no Tailwind CDN.\n- No remote images, no .mp4/.webm/.gif, no base64 photo blobs, no third-party CDN of any kind.\n  Google Fonts (preconnect + stylesheet) is the ONLY network request.\n- No extra sections, no feature cards, no logo strip, no pricing table, no testimonial, no FAQ,\n  no form, no newsletter input, no cookie banner, no footer beyond the three stats.\n- No scroll indicator, no mouse-follow parallax, no cursor blob, no custom scrollbar.\n- No vertical scrolling on desktop: at >=901px `html, body { height:100%; overflow:hidden }`\n  and `.page { height:100dvh; overflow:hidden }`. Scroll is permitted ONLY at <=900px.\n- No text-shadow glows, no neon outlines on type, no drop shadows under the headline.\n- No animated counters, no typing effect, no marquee.\n\nB. THE BACKDROP — PROCEDURAL AURORA LIGHT FIELD\n\nB.1 The canvas element\n  <canvas id=\"aurora\" class=\"aurora-canvas\" aria-hidden=\"true\"></canvas>\n  CSS: position:fixed; inset:0; width:100%; height:100%; display:block; z-index:0;\n  pointer-events:none; background:#000;\n  It is fixed, not absolute, so it never moves during layout.\n\nB.2 The resolution trick (implement exactly)\n  const DPR = Math.min(window.devicePixelRatio || 1, 2);   // cap DPR at 2\n  const SCALE = 0.5;                                        // render at half resolution\n  canvas.width  = Math.max(1, Math.round(innerWidth  * DPR * SCALE));\n  canvas.height = Math.max(1, Math.round(innerHeight * DPR * SCALE));\n  CSS keeps it at 100% x 100%, so the browser upscales the soft field with bilinear smoothing.\n  Because the content is pure blur, 0.5x is visually identical to 1x and costs 4x less fill.\n  Also set once after sizing: ctx.imageSmoothingEnabled = true;\n  ctx.imageSmoothingQuality = 'low';  (cheap, and smoothing is what we want).\n  Recompute on resize with a debounce of 150ms, and on orientationchange. Clamp: never let\n  canvas.width * canvas.height exceed 2,600,000 device pixels; if it does, lower SCALE to fit\n  (SCALE = Math.min(SCALE, Math.sqrt(2600000 / (w*h*DPR*DPR)))).\n\nB.3 The sprite approach — ZERO allocation in the hot loop\n  Do NOT call createRadialGradient inside the rAF loop. Instead pre-render sprites once:\n  For each of the 3 palettes (18 sprites total, 6 bands x 3 palettes), create an offscreen\n  canvas SPRITE = 256 x 256 (document.createElement('canvas')) and paint into it ONE radial\n  gradient centred at (128,128) with radius 128, stops:\n      0.00 -> rgba(r,g,b,1.00)\n      0.35 -> rgba(r,g,b,0.42)\n      0.62 -> rgba(r,g,b,0.14)\n      0.82 -> rgba(r,g,b,0.04)\n      1.00 -> rgba(r,g,b,0.00)\n  Store in a flat array `sprites[paletteIndex * 6 + bandIndex]`. 18 x 256 x 256 x 4 bytes =\n  4.7 MB of offscreen memory — acceptable, allocated once at boot.\n  Per frame the only draw call is ctx.drawImage(sprite, dx, dy, dw, dh) — no gradient object,\n  no array, no string, no closure is created per frame.\n\nB.4 The six bands — exact parameters\n  Geometry helpers (w,h = canvas backing-store size in device px):\n    maxDim = Math.max(w, h)\n    cx_i = 0.50 * w + ampX_i * w * Math.sin(t * fx_i + phaseX_i)\n    cy_i = 0.46 * h + ampY_i * h * Math.sin(t * fy_i + phaseY_i)\n    r_i  = radius_i * maxDim                      // sprite half-width\n    dw   = 2 * r_i\n    dh   = 2 * r_i * aspect_i                     // squashed vertically -> a band, not a ball\n  These are independent Lissajous paths (different x and y frequencies per band => the paths\n  never repeat inside a session).\n\n  i | role      | radius_i | aspect_i | ampX_i | ampY_i | fx_i (rad/s) | fy_i (rad/s) | phX | phY | alpha\n  0 | cyan core | 0.62     | 0.58     | 0.30   | 0.20   | 0.0532       | 0.0411       | 0.0 | 1.7 | 0.62\n  1 | teal      | 0.54     | 0.64     | 0.26   | 0.24   | 0.0648       | 0.0480       | 2.1 | 0.4 | 0.55\n  2 | violet    | 0.70     | 0.50     | 0.34   | 0.18   | 0.0439       | 0.0861       | 4.0 | 3.2 | 0.40\n  3 | blue      | 0.48     | 0.70     | 0.22   | 0.28   | 0.0757       | 0.0576       | 5.6 | 2.6 | 0.34\n  4 | magenta   | 0.66     | 0.54     | 0.36   | 0.16   | 0.0376       | 0.1030       | 1.2 | 5.0 | 0.28\n  (fx 0.0532 rad/s = a 118 s period; fy 0.1030 rad/s = a 61 s period. Slow and cinematic.)\n\n  5 | AMBER SWEEP — a warm band that crosses the frame every ~30 s. It is NOT a Lissajous drift;\n  it is a scripted traversal, so it reads as an event:\n      cycle   = 30 s\n      p       = (t % cycle) / cycle                     // 0..1\n      window  = p in [0.10, 0.62]                       // visible for 15.6 s of the 30 s\n      u       = clamp((p - 0.10) / 0.52, 0, 1)\n      ease    = 0.5 - 0.5 * Math.cos(Math.PI * u)       // easeInOutSine: no velocity jump\n      ax      = (-0.35 + 1.70 * ease) * w               // enters left, exits right\n      ay      = (0.62 - 0.10 * Math.sin(Math.PI * u)) * h\n      aEnv    = Math.pow(Math.sin(Math.PI * u), 0.8)    // fade in/out at the edges\n      alpha   = 0.42 * aEnv\n      radius_5 = 0.40, aspect_5 = 0.46\n\nB.5 Compositing order inside one frame (all in canvas, no CSS filters)\n  1. ctx.globalCompositeOperation = 'source-over'; ctx.globalAlpha = 1;\n     ctx.fillStyle = '#000000'; ctx.fillRect(0, 0, w, h);        // hard black ground each frame\n  2. ctx.globalCompositeOperation = 'screen';                     // light adds, never subtracts\n  3. for each band i = 0..5:\n       draw palette A sprite with globalAlpha = alpha_i * aEnv_i * (1 - mix)\n       draw palette B sprite with globalAlpha = alpha_i * aEnv_i * mix\n       (aEnv_i is 1 for bands 0-4, and the envelope from B.4 for band 5)\n     Order 0,1,2,3,4,5 — largest, softest first is not required because 'screen' is commutative.\n  4. ctx.globalCompositeOperation = 'source-over'; ctx.globalAlpha = 1;\n  Note: 'screen' on black means the field can never darken the page below #000000, and no band\n  can ever clip to white — the sum stays under ~0.78 luminance because the peak alphas are low.\n\nB.6 Palette cross-fade every 20 s (rAF, cancellable, never snapping)\n  Three palettes, index cycled 0 -> 1 -> 2 -> 0:\n    P0 Nordic      : #22d3ee, #12a89a, #7c5cff, #2a6bd8, #a855f7, #f0a24a\n    P1 Ion         : #35e0d0, #0f8fbf, #5b6cff, #1b4fd8, #c678f7, #ffb066\n    P2 Ember drift : #4de3c1, #17997f, #8b5cf6, #2563eb, #d946ef, #f59e0b\n  State: { fromA, fromB, mix, fading:false, fadeT0:0, fadeDur:2600 }\n  Every 20.0 s of accumulated time (timer measured in the same rAF, using the clamped dt), start\n  a fade: set toB = next palette, fadeT0 = now, fading = true.\n  Each frame while fading: mix goes from the value it had when the fade started (mixStart) to 1\n  using easeInOutCubic over fadeDur; when done: fromA = fromB; fromB = next(fromB); mix = 0;\n  fading = false.\n  CANCELLATION RULE (the important part): if a new fade is requested while `fading === true`,\n  do NOT restart from 0. Snapshot the CURRENT blended colour as the new starting point:\n    fromA = currentBlendRgb (computed by lerping P[fromA] and P[fromB] by the live mix,\n            per channel, rounded to integers, then used as a palette slot of its own),\n    mixStart = 0, fadeT0 = now, fromB = requested target.\n  This makes interruptions continuous — the field never snaps.\n\nB.7 Frame budget and dt clamp\n  - dt = Math.min((now - last) / 1000, 0.05);   // clamp so a tab switch cannot teleport bands\n  - Draw at most ~60 fps: if (now - lastDraw < 15.5) { rAF again; return; }\n  - Target: the whole frame must cost under 4 ms on a 2019 laptop. With 12 drawImage calls on a\n    0.5x backing store this is easily met. If `document.hidden` is true, cancel the rAF and\n    restart it on visibilitychange; never draw while hidden.\n  - Every rAF tick increments window.__DEMO__.frames (see J) — including when a draw is skipped.\n\nB.8 Grain: SVG feTurbulence at 4%, static (zero per-frame cost)\n  Inline (NOT a data URI, NOT remote):\n    <svg class=\"grain-svg\" aria-hidden=\"true\" focusable=\"false\">\n      <filter id=\"auroraGrain\" x=\"0\" y=\"0\" width=\"100%\" height=\"100%\">\n        <feTurbulence type=\"fractalNoise\" baseFrequency=\"0.85\" numOctaves=\"3\"\n                      stitchTiles=\"stitch\" seed=\"7\" result=\"n\"/>\n        <feColorMatrix type=\"saturate\" values=\"0\"/>\n      </filter>\n      <rect width=\"100%\" height=\"100%\" filter=\"url(#auroraGrain)\"/>\n    </svg>\n  CSS: .grain-svg { position:fixed; inset:-4%; width:108%; height:108%; z-index:100;\n       opacity:0.04; mix-blend-mode:soft-light; pointer-events:none; }\n  A wrapper drift so it does not look like a static plate — compositor-only, 6 discrete steps:\n    .grain-svg { animation: grain-drift 6s steps(6, end) infinite; will-change: transform; }\n    @keyframes grain-drift {\n      0%   { transform: translate3d(0,0,0) }      16.6% { transform: translate3d(-1.2%, 0.8%, 0) }\n      33.3%{ transform: translate3d(0.9%, -1.1%, 0) } 50% { transform: translate3d(-0.7%, -0.6%, 0) }\n      66.6%{ transform: translate3d(1.1%, 0.5%, 0) }  83.3%{ transform: translate3d(-0.5%, 1.2%, 0) }\n      100% { transform: translate3d(0,0,0) } }\n\nB.9 Vignette + scrim (CSS, above canvas, below content)\n  .aurora-scrim is a fixed div at z-index:1 with pointer-events:none and:\n    background:\n      radial-gradient(120% 90% at 50% 62%, rgba(0,0,0,0) 0%, rgba(0,0,0,0.35) 46%,\n                      rgba(0,0,0,0.78) 78%, rgba(0,0,0,0.94) 100%),\n      linear-gradient(to top, rgba(0,0,0,0.92) 0%, rgba(0,0,0,0.62) 22%,\n                      rgba(0,0,0,0.18) 46%, rgba(0,0,0,0.32) 72%, rgba(0,0,0,0.72) 100%);\n  The bottom-to-top ramp is the load-bearing part: the bottom 22% of the viewport (where the\n  hero copy sits) is knocked down to ~92% black, so the headline and lede ALWAYS win, no matter\n  how bright the aurora is. The top ramp protects the header. Values are not adjustable upward.\n\nC. THE LAYER STACK (back -> front), with z-index\n\n  z 0    html, body            background:#000000 (see failure guards)\n  z 0    #aurora               fixed canvas, the light field\n  z 1    .aurora-scrim         fixed vignette/scrim gradient\n  z 2    .page                 position:relative; display:grid; grid-template-rows:auto 1fr auto;\n                               min-height:100dvh; (desktop lock: height:100dvh; overflow:hidden)\n  auto   header / main / footer  children of .page, no z-index of their own\n  z 100  .grain-svg            feTurbulence grain, 4%, soft-light\n  Nothing else may create a stacking context above 2. The mobile menu overlay, when open, sits at\n  z-index:90 (below grain, above .page).\n\nD. TOKEN TABLE (declare all of these on :root; default values are the ~1440px design)\n\n  Colour\n    --bg: #000000\n    --text: #ffffff\n    --muted: #9a9a9a              (7.5:1 on #000 — safe for the lede)\n    --stat: #d8d8d8\n    --accent: #22d3ee\n    --border: rgba(255,255,255,0.16)\n    --border-soft: rgba(255,255,255,0.12)\n    --pill-line: rgba(198,198,198,0.55)\n    --pill-line-hover: rgba(235,235,235,0.9)\n    --grain-opacity: 0.04\n    --focus: rgba(120,231,255,0.9)\n\n  Type\n    --logo: 15.5px\n    --logo-mark: 22px\n    --nav: 14px\n    --nav-h: 40px\n    --btn: 13.5px\n    --btn-h: 40px\n    --h1: 48px\n    --lede: 15.5px\n    --badge: 12.5px\n    --stat-size: 13.5px\n\n  Space / geometry\n    --header-y: 22px\n    --header-x: 40px\n    --stats-x: 72px\n    --stats-y: 36px\n    --hero-gap: 85px\n    --copy-max: 860px\n    --lede-max: 470px\n    --pill-radius: 7px\n    --pill-pad: 18px\n    --nav-gap: 10px\n    --cta-gap: 14px\n    --hero-btn-gap: 12px\n    --stat-gap: 36px\n\n  Fonts\n    --font-sans: \"Inter\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto,\n                 Helvetica, Arial, sans-serif\n    --font-serif: \"Instrument Serif\", \"Iowan Old Style\", \"Palatino Linotype\", Palatino,\n                  Georgia, serif\n  Load with <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">,\n  <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>, and one\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500&family=Instrument+Serif:ital@1&display=swap\">.\n  If the request fails the system fallbacks above must still look intentional.\n\nE. THE COMPOSITION — build order\n\nE.1 <body style=\"background:#000;color:#fff\"> (inline attribute, belt and braces).\n\nE.2 <header class=\"site-header\">\n    padding: var(--header-y) var(--header-x); display:flex; align-items:center; gap:16px;\n    .brand { display:inline-flex; align-items:center; gap:10px; color:#fff; text-decoration:none }\n      .brand-mark { width:var(--logo-mark); height:var(--logo-mark); flex:0 0 auto }\n        An INLINE SVG: a 22x22 viewBox=\"0 0 22 22\" aurora mark — three stacked arcs\n        (path d=\"M2 15 C6 7, 10 19, 14 8 S18 12, 20 6\" style curves) stroked with\n        stroke=\"url(#markGrad)\" stroke-width=\"1.6\" fill=\"none\" stroke-linecap=\"round\", where\n        #markGrad is a linearGradient from #22d3ee to #a855f7, plus a 2.2r circle at (20,6)\n        filled #f0a24a. aria-hidden=\"true\" (the wordmark carries the name).\n      .brand-word { font-size:var(--logo); letter-spacing:0.14em; text-transform:uppercase;\n                    font-weight:500; white-space:nowrap }   -> text \"Aurora Freight\"\n    nav.nav-desktop { margin-left:auto; display:flex; align-items:center; gap:var(--nav-gap) }\n      ul { display:flex; align-items:center; gap:var(--nav-gap); list-style:none; margin:0; padding:0 }\n      Four <a class=\"pill appear\"> links, in this order and with these EXACT strings:\n        \"Platform\"  \"Network\"  \"Pricing\"  \"Docs\"\n      Then <a class=\"btn btn-ghost btn-sm appear\"> with the string \"Request access\", separated\n      from the last pill by var(--cta-gap).\n    button.burger (hidden at >=901px): 40x40, aria-label=\"Open menu\", aria-expanded=\"false\",\n      aria-controls=\"mobile-menu\", border:1px solid var(--border), border-radius:var(--pill-radius),\n      background:transparent; three 1px x 16px white bars (spans) at 6px vertical spacing.\n\nE.3 <main class=\"hero\">\n    CSS: .hero { display:flex; align-items:flex-end; justify-content:center;\n                 padding:8px 24px var(--hero-gap); min-height:0 }\n    .hero-inner { width:100%; max-width:var(--copy-max); margin:0 auto; text-align:center }\n    Children, in order:\n      1. p.badge.appear\n           inline-flex; align-items:center; gap:8px; margin:0 0 26px;\n           font-size:var(--badge); letter-spacing:0.08em; text-transform:uppercase;\n           color:var(--muted);\n           .badge-dot { width:5px; height:5px; border-radius:50%; background:var(--accent);\n                        box-shadow:0 0 10px rgba(34,211,238,0.75) }\n           Copy (exact): \"New — the Aurora Route Graph\"\n      2. h1.hero-title  (the ONLY h1 on the page)\n           font-size:var(--h1); font-weight:500; letter-spacing:-0.045em; line-height:1.12;\n           margin:0 0 22px; color:var(--text);\n           Exactly TWO <span class=\"headline-line\"> children, each display:block;\n           overflow:hidden; padding:0.06em 0.15em 0.14em; (the padding stops the italic serif\n           and the descenders from being clipped by the mask):\n             Line 1:  Move <em>freight</em> with an AI that\n             Line 2:  already knows the road.\n           em { font-family:var(--font-serif); font-style:italic; font-weight:400;\n                font-size:1.08em; letter-spacing:-0.03em; color:var(--muted) }   <-- NOT white\n      3. p.lede.appear\n           max-width:var(--lede-max); margin:0 auto 30px; font-size:var(--lede);\n           line-height:1.62; color:var(--muted);\n           Copy (exact): \"Aurora plans, books and tracks every load across 340+ carriers — learning your lanes and your rates in weeks, not quarters.\"\n      4. div.hero-actions { display:flex; gap:var(--hero-btn-gap); justify-content:center;\n                            align-items:center; flex-wrap:wrap }\n           a.btn.btn-solid.appear -> \"Request access\"\n           a.btn.btn-line.appear  -> \"Watch the 2-min tour\"\n\nE.4 <footer class=\"stats\">\n    padding:0 var(--stats-x) var(--stats-y);\n    ul { display:flex; align-items:center; justify-content:center; gap:var(--stat-gap);\n         flex-wrap:wrap; list-style:none; margin:0; padding:0 }\n    Exactly THREE <li class=\"stat appear\"> items:\n      <b>340+</b> carriers connected\n      <b>1.2M</b> loads routed monthly\n      <b>18%</b> lower freight spend\n    .stat { font-size:var(--stat-size); color:var(--stat); letter-spacing:0.01em;\n            display:inline-flex; gap:7px; align-items:baseline; white-space:nowrap }\n    .stat b { color:#ffffff; font-weight:500; font-variant-numeric:tabular-nums }\n\nE.5 Mobile menu (<=900px only)\n    div#mobile-menu.mobile-menu[hidden] — position:fixed; inset:0; z-index:90;\n    background:rgba(0,0,0,0.72); backdrop-filter:blur(18px) saturate(1.2);\n    -webkit-backdrop-filter:blur(18px) saturate(1.2);\n    display:flex; flex-direction:column; align-items:center; justify-content:center; gap:18px;\n    Contains the same four links at font-size:22px plus \"Request access\" as a solid button.\n    Toggled by .burger; closed by: clicking any link, pressing Escape, or a resize to >=901px.\n    When open: body gets .menu-open { overflow:hidden } and aria-expanded flips to \"true\".\n\nF. THE UI LANGUAGE\n\nF.1 Liquid-metal pill (.pill) — the nav links\n    display:inline-flex; align-items:center; justify-content:center;\n    height:var(--nav-h); padding:0 var(--pill-pad); border-radius:var(--pill-radius);\n    overflow:hidden; position:relative; isolation:isolate;\n    border:1px solid var(--pill-line);\n    background:linear-gradient(105deg, #050505 0%, #2a2a2a 48%, #4a4a4a 100%);\n    color:#e9e9e9; font-size:var(--nav); font-weight:500; letter-spacing:0.005em;\n    text-decoration:none; transition:border-color .35s ease, background .35s ease,\n                                  box-shadow .35s ease, color .35s ease;\n    .pill::before { content:\"\"; position:absolute; inset:-1px; z-index:-1;\n      background:linear-gradient(115deg, transparent 30%, rgba(255,255,255,0.16) 50%,\n                                 transparent 70%);\n      transform:translateX(-120%); transition:transform .6s ease }\n    .pill:hover::before { transform:translateX(120%) }\n    .pill:hover { border-color:var(--pill-line-hover); color:#ffffff;\n      background:linear-gradient(105deg, #111111 0%, #3a3a3a 45%, #6a6a6a 100%);\n      box-shadow:0 0 18px rgba(200,210,230,0.18) }\n    .pill:focus-visible { outline:2px solid var(--focus); outline-offset:3px }\n\nF.2 Buttons (.btn)\n    display:inline-flex; align-items:center; justify-content:center;\n    height:var(--btn-h); padding:0 20px; border-radius:var(--pill-radius);\n    font-size:var(--btn); font-weight:500; letter-spacing:0.01em;\n    text-decoration:none; cursor:pointer; border:1px solid transparent;\n    transition:transform .3s ease, background .3s ease, border-color .3s ease,\n               box-shadow .3s ease, color .3s ease;\n    .btn-solid { background:linear-gradient(105deg, #e8ecf2 0%, #ffffff 52%, #d3d9e2 100%);\n                 color:#06070a; border-color:rgba(255,255,255,0.5) }\n    .btn-solid:hover { transform:translateY(-1px);\n                 box-shadow:0 8px 26px rgba(160,190,220,0.20) }\n    .btn-line  { background:rgba(255,255,255,0.02); color:#ffffff;\n                 border-color:var(--border) }\n    .btn-line:hover { border-color:rgba(255,255,255,0.42);\n                 background:rgba(255,255,255,0.06) }\n    .btn-ghost { background:linear-gradient(105deg, rgba(255,255,255,0.06) 0%,\n                 rgba(255,255,255,0.02) 55%, rgba(255,255,255,0.10) 100%);\n                 color:#ffffff; border-color:var(--pill-line) }   // header CTA, matches the pills\n    .btn-sm { height:var(--btn-h); padding:0 16px; font-size:var(--btn) }\n    Hover lift is translateY(-1px) ONLY — nothing bounces, nothing scales on hover.\n\nF.3 Focus ring, global\n    :focus-visible { outline:2px solid var(--focus); outline-offset:3px; border-radius:inherit }\n    Never remove outlines; never set outline:0 without a replacement.\n\nF.4 Anti-flash rule (first three rules of the stylesheet, in this order)\n    1) html, body { background:#000000 !important; color:#ffffff; }\n    2) (the inline body attribute from E.1)\n    3) html, body { background:#000000; background:var(--bg, #000000); margin:0; padding:0;\n                    font-family:var(--font-sans); -webkit-font-smoothing:antialiased;\n                    text-rendering:optimizeLegibility; }\n\nG. MOTION\n\nG.1 Entrance system\n    .appear { opacity:1 }                        // RESTING STATE IS VISIBLE — see I\n    .appear { animation-duration:1.05s; animation-fill-mode:both;\n              animation-timing-function:cubic-bezier(0.16,1,0.3,1);\n              animation-delay:var(--d, 0.08s); }\n    .is-in { animation:none !important; opacity:1 !important; transform:none !important;\n             clip-path:none !important; filter:none !important; }\n\nG.2 Stagger table (element -> animation -> --d)\n    brand            in-scale   0.08s\n    nav pill 1       in-scale   0.16s\n    nav pill 2       in-soft    0.28s\n    nav pill 3       in-scale   0.40s\n    nav pill 4       in-soft    0.52s\n    header CTA       in-scale   0.34s\n    badge            in-pop     0.22s\n    h1 line 1        in-mask    0.42s\n    h1 line 2        in-mask    0.62s\n    lede             in-soft    0.82s   (animation-duration:1.25s)\n    solid CTA        in-btn     0.96s\n    line CTA         in-side    1.10s\n    stat 1           in-stat    1.12s\n    stat 2           in-stat    1.28s\n    stat 3           in-stat    1.44s\n    (the canvas has no entrance animation; it fades in once via .aurora-canvas\n     { animation:in-fade 2.2s ease both } with --d 0s)\n\nG.3 Keyframes — every one ends at opacity 1 and the identity transform\n    @keyframes in-fade { from { opacity:0 } to { opacity:1 } }\n    @keyframes in-scale{ from { opacity:0; transform:scale(0.84) }\n                         to   { opacity:1; transform:scale(1) } }\n    @keyframes in-soft { from { opacity:0; transform:translateY(14px) }\n                         to   { opacity:1; transform:translateY(0) } }\n    @keyframes in-mask { from { opacity:0; transform:translateY(40%) }\n                         to   { opacity:1; transform:translateY(0) } }   // clipped by .headline-line\n    @keyframes in-pop  { 0%   { opacity:0; transform:scale(0.90) }\n                         70%  { opacity:1; transform:scale(1.03) }\n                         100% { opacity:1; transform:scale(1) } }\n    @keyframes in-btn  { from { opacity:0; transform:translateY(18px) scale(0.94) }\n                         to   { opacity:1; transform:translateY(0) scale(1) } }\n    @keyframes in-side { from { opacity:0; transform:translateX(22px) }\n                         to   { opacity:1; transform:translateX(0) } }\n    @keyframes in-stat { from { opacity:0; transform:translateY(20px) }\n                         to   { opacity:1; transform:translateY(0) } }\n\nH. RESPONSIVE TABLE — every breakpoint is a token override table\n\n  >=1600px\n    --logo:17px; --logo-mark:24px; --nav:15px; --nav-h:44px; --btn:15px; --btn-h:44px;\n    --hero-btn-h:48px; --h1:64px; --lede:18px; --badge:13.5px; --stat-size:15px;\n    --header-y:28px; --header-x:64px; --stats-x:96px; --stats-y:44px;\n    --copy-max:980px; --lede-max:540px\n\n  >=1920px\n    --h1:76px; --lede:19px; --badge:14px; --stat-size:15.5px; --header-y:30px; --header-x:72px;\n    --stats-x:120px; --stats-y:52px; --copy-max:1060px; --lede-max:580px; --hero-gap:96px\n\n  1280px – 1599px\n    --h1:54px; --header-x:48px; --copy-max:900px\n\n  901px – 1279px\n    --h1:42px; --nav-h:36px; --hero-gap:64px; --copy-max:760px; --lede-max:440px;\n    --stats-x:48px; --stat-gap:28px\n\n  >=901px AND (max-height:850px)\n    --h1:40px; --hero-gap:48px; --header-y:18px; --stats-y:28px\n\n  >=901px AND (max-height:720px)\n    --h1:34px; --hero-gap:32px; --lede:15px; --header-y:14px; --stats-y:22px; --stat-gap:24px\n\n  >=901px DESKTOP LOCK (apply in the same block)\n    html, body { height:100%; overflow:hidden }\n    .page { height:100dvh; overflow:hidden }\n    ONE FRAME, NO SCROLL.\n\n  <=900px  (phone + small tablet — scrolling IS allowed here)\n    Remove the vh lock: html, body { height:auto; overflow:auto } .page { height:auto; min-height:100dvh }\n    .nav-desktop { display:none }  .burger { display:inline-flex }\n    --h1:40px; --header-y:18px; --header-x:24px; --hero-gap:56px; --stats-x:24px;\n    --stats-y:32px; --stat-gap:18px; --copy-max:640px; --lede-max:420px\n    .hero { padding:16px 20px var(--hero-gap) }\n    .stats ul { justify-content:center }\n    Menu overlay per E.5.\n\n  <=560px\n    --h1:34px; --lede:15px; --badge:12px; --stat-size:12.5px; --hero-gap:44px;\n    --header-x:18px; --stats-x:18px\n    .hero-actions { flex-direction:column; align-items:stretch; width:100% }\n    .hero-actions .btn { width:100% }\n    .headline-line { padding:0.06em 0.10em 0.14em }\n    .stats ul { flex-direction:column; align-items:center; gap:10px }   // three stacked lines\n  No horizontal overflow at 390px, 768px, 1440px or 1920px: never set a width wider than\n  100vw, keep --copy-max inside a 20px gutter, and let .headline-line shrink (it is a block,\n  so it wraps rather than overflows).\n\nI. FAILURE GUARDS\n\n  I.1 Resting opacity 1. `.appear { opacity:1 }` is the base rule; the animation only ever\n      animates FROM 0. If CSS animations are unsupported, disabled, or interrupted, every\n      element is already in its final state. The page is NEVER blank.\n  I.2 animationend settlement. On each element's own `animationend`, add `.is-in` so the\n      element drops the animation and keeps its end state (prevents any post-animation\n      repaint from re-running the keyframe, and frees compositor layers).\n  I.3 Two-rAF fallback. After two nested requestAnimationFrames from DOMContentLoaded, for\n      every `.appear`: if `el.getAnimations().some(a => a.playState === 'running' ||\n      a.playState === 'finished')` is false, add `.is-in`. Guard with\n      `typeof el.getAnimations === 'function'` — if absent, add `.is-in` to all.\n  I.4 Canvas fallback. If `canvas.getContext('2d')` returns null, or sprite creation throws,\n      hide #aurora, keep the black ground + scrim, set `__DEMO__.ready = true`, push\n      'aurora-canvas-fallback' into effects, and push nothing into errors. The page must still\n      look finished (it is a black page with a gradient scrim — acceptable).\n  I.5 prefers-reduced-motion: reduce\n      @media (prefers-reduced-motion: reduce) {\n        *, *::before, *::after { transition:none !important; animation:none !important; }\n        .appear, .headline-line, .badge-dot, h1 em { opacity:1 !important; transform:none !important;\n          clip-path:none !important; filter:none !important; }\n        .grain-svg { animation:none !important }\n      }\n      Canvas under reduced motion: build the sprites, draw ONE static frame at t = 0 with the\n      amber band parked off-frame, then keep the rAF loop running ONLY to increment\n      __DEMO__.frames (no dt advance, no draw). The visual result is a completely static,\n      fully readable page; telemetry still advances.\n\nJ. THE JAVASCRIPT — exactly this much, nothing more\n\n  (function () {\n    'use strict';\n    window.__DEMO__ = { name: 'aurora-freight', ready: false, frames: 0, effects: [], errors: [] };\n    var D = window.__DEMO__;\n    window.addEventListener('error', function (e) {\n      D.errors.push(String((e && e.message) || 'error'));\n    });\n\n    // --- 1. entrance settlement + fallback (I.2, I.3) ---\n    var appear = Array.prototype.slice.call(document.querySelectorAll('.appear'));\n    appear.forEach(function (el) {\n      el.addEventListener('animationend', function () { el.classList.add('is-in'); });\n    });\n    requestAnimationFrame(function () {\n      requestAnimationFrame(function () {\n        appear.forEach(function (el) {\n          var running = false;\n          if (typeof el.getAnimations === 'function') {\n            running = el.getAnimations().some(function (a) {\n              return a.playState === 'running' || a.playState === 'finished';\n            });\n          }\n          if (!running) { el.classList.add('is-in'); }\n        });\n      });\n    });\n\n    // --- 2. mobile menu (E.5) ---\n    var burger = document.querySelector('.burger');\n    var menu = document.getElementById('mobile-menu');\n    function setMenu(open) {\n      if (!burger || !menu) { return; }\n      menu.hidden = !open;\n      burger.setAttribute('aria-expanded', open ? 'true' : 'false');\n      document.body.classList.toggle('menu-open', open);\n    }\n    if (burger && menu) {\n      burger.addEventListener('click', function () {\n        setMenu(menu.hidden === true);\n      });\n      menu.addEventListener('click', function (e) {\n        if (e.target.closest('a')) { setMenu(false); }\n      });\n      document.addEventListener('keydown', function (e) {\n        if (e.key === 'Escape') { setMenu(false); }\n      });\n      window.addEventListener('resize', function () {\n        if (window.innerWidth >= 901) { setMenu(false); }\n      });\n    }\n\n    // --- 3. aurora canvas (B) ---\n    var reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n    var canvas = document.getElementById('aurora');\n    var ctx = canvas && canvas.getContext ? canvas.getContext('2d') : null;\n\n    if (!ctx) {                                  // I.4\n      if (canvas) { canvas.style.display = 'none'; }\n      D.ready = true; D.effects.push('aurora-canvas-fallback');\n      return;\n    }\n\n    var PALETTES = [\n      [[34,211,238],[18,168,154],[124,92,255],[42,107,216],[168,85,247],[240,162,74]],\n      [[53,224,208],[15,143,191],[91,108,255],[27,79,216],[198,120,247],[255,176,102]],\n      [[77,227,193],[23,153,127],[139,92,246],[37,99,235],[217,70,239],[245,158,11]]\n    ];\n    var BANDS = [\n      { r:0.62, a:0.58, ax:0.30, ay:0.20, fx:0.0532, fy:0.0411, px:0.0, py:1.7, al:0.62 },\n      { r:0.54, a:0.64, ax:0.26, ay:0.24, fx:0.0648, fy:0.0480, px:2.1, py:0.4, al:0.55 },\n      { r:0.70, a:0.50, ax:0.34, ay:0.18, fx:0.0439, fy:0.0861, px:4.0, py:3.2, al:0.40 },\n      { r:0.48, a:0.70, ax:0.22, ay:0.28, fx:0.0757, fy:0.0576, px:5.6, py:2.6, al:0.34 },\n      { r:0.66, a:0.54, ax:0.36, ay:0.16, fx:0.0376, fy:0.1030, px:1.2, py:5.0, al:0.28 }\n    ];\n    var SPRITE = 256, sprites = [];\n    function buildSprites() {\n      for (var p = 0; p < PALETTES.length; p++) {\n        for (var i = 0; i < 6; i++) {\n          var c = PALETTES[p][i];\n          var cv = document.createElement('canvas');\n          cv.width = SPRITE; cv.height = SPRITE;\n          var g = cv.getContext('2d');\n          var grd = g.createRadialGradient(128, 128, 0, 128, 128, 128);\n          var rgb = c[0] + ',' + c[1] + ',' + c[2];\n          grd.addColorStop(0.00, 'rgba(' + rgb + ',1)');\n          grd.addColorStop(0.35, 'rgba(' + rgb + ',0.42)');\n          grd.addColorStop(0.62, 'rgba(' + rgb + ',0.14)');\n          grd.addColorStop(0.82, 'rgba(' + rgb + ',0.04)');\n          grd.addColorStop(1.00, 'rgba(' + rgb + ',0)');\n          g.fillStyle = grd; g.fillRect(0, 0, SPRITE, SPRITE);\n          sprites.push(cv);\n        }\n      }\n    }\n    buildSprites();\n\n    var DPR = Math.min(window.devicePixelRatio || 1, 2), SCALE = 0.5;\n    var W = 0, H = 0, maxDim = 0;\n    function resize() {\n      W = Math.max(1, Math.round(window.innerWidth  * DPR * SCALE));\n      H = Math.max(1, Math.round(window.innerHeight * DPR * SCALE));\n      var cap = Math.sqrt(2600000 / (W * H));\n      if (cap < 1) { W = Math.round(W * cap); H = Math.round(H * cap); }\n      canvas.width = W; canvas.height = H;\n      maxDim = Math.max(W, H);\n      ctx.imageSmoothingEnabled = true; ctx.imageSmoothingQuality = 'low';\n      draw(0);                                    // repaint immediately after a resize\n    }\n\n    var pa = 0, pb = 1, mix = 0, fading = false, fadeT0 = 0, mixStart = 0;\n    var FADE_EVERY = 20000, FADE_DUR = 2600, sinceFade = 0;\n    var CYCLE = 30;\n\n    function startFade() {\n      mixStart = mix; fading = true; fadeT0 = now;         // B.6: never restart from 0\n    }\n    function tickFade(now, dt) {\n      sinceFade += dt * 1000;\n      if (!fading && sinceFade >= FADE_EVERY) { sinceFade = 0; pb = (pb + 1) % 3; startFade(); }\n      if (fading) {\n        var k = Math.min(1, (now - fadeT0) / FADE_DUR);\n        var e = k < 0.5 ? 4 * k * k * k : 1 - Math.pow(-2 * k + 2, 3) / 2;   // easeInOutCubic\n        mix = mixStart + (1 - mixStart) * e;\n        if (k >= 1) { pa = pb; pb = (pb + 1) % 3; mix = 0; fading = false; }\n      }\n    }\n\n    function draw(t) {\n      ctx.globalCompositeOperation = 'source-over'; ctx.globalAlpha = 1;\n      ctx.fillStyle = '#000000'; ctx.fillRect(0, 0, W, H);\n      ctx.globalCompositeOperation = 'screen';\n      for (var i = 0; i < 5; i++) {\n        var b = BANDS[i];\n        var cx = 0.50 * W + b.ax * W * Math.sin(t * b.fx + b.px);\n        var cy = 0.46 * H + b.ay * H * Math.sin(t * b.fy + b.py);\n        var rr = b.r * maxDim, dw = 2 * rr, dh = 2 * rr * b.a;\n        var dx = cx - rr, dy = cy - dh / 2;\n        var al = b.al;\n        ctx.globalAlpha = al * (1 - mix);\n        ctx.drawImage(sprites[pa * 6 + i], dx, dy, dw, dh);\n        ctx.globalAlpha = al * mix;\n        ctx.drawImage(sprites[pb * 6 + i], dx, dy, dw, dh);\n      }\n      // amber sweep, band 5 (B.4)\n      var p = (t % CYCLE) / CYCLE;\n      if (p >= 0.10 && p <= 0.62) {\n        var u = (p - 0.10) / 0.52;\n        var ease = 0.5 - 0.5 * Math.cos(Math.PI * u);\n        var ax = (-0.35 + 1.70 * ease) * W;\n        var ay = (0.62 - 0.10 * Math.sin(Math.PI * u)) * H;\n        var env = Math.pow(Math.sin(Math.PI * u), 0.8);\n        var rr5 = 0.40 * maxDim, dw5 = 2 * rr5, dh5 = 2 * rr5 * 0.46;\n        var al5 = 0.42 * env;\n        ctx.globalAlpha = al5 * (1 - mix);\n        ctx.drawImage(sprites[pa * 6 + 5], ax - rr5, ay - dh5 / 2, dw5, dh5);\n        ctx.globalAlpha = al5 * mix;\n        ctx.drawImage(sprites[pb * 6 + 5], ax - rr5, ay - dh5 / 2, dw5, dh5);\n      }\n      ctx.globalCompositeOperation = 'source-over'; ctx.globalAlpha = 1;\n    }\n\n    var now = 0, last = 0, lastDraw = 0, t = 0, raf = 0;\n    function frame(ts) {\n      raf = requestAnimationFrame(frame);\n      D.frames++;\n      if (!last) { last = ts; lastDraw = ts; now = ts; return; }\n      var dt = Math.min((ts - last) / 1000, 0.05);\n      last = ts; now = ts;\n      if (reduce) { return; }                       // I.5: static image, frames still count\n      t += dt;\n      tickFade(ts, dt);\n      if (ts - lastDraw < 15.5) { return; }\n      lastDraw = ts;\n      draw(t);\n      if (!D.ready) { D.ready = true; }\n    }\n\n    function boot() { resize(); draw(0); D.ready = true; raf = requestAnimationFrame(frame); }\n    var rt = 0;\n    window.addEventListener('resize', function () {\n      clearTimeout(rt); rt = setTimeout(resize, 150);\n    });\n    window.addEventListener('orientationchange', function () { resize(); });\n    document.addEventListener('visibilitychange', function () {\n      if (document.hidden) { cancelAnimationFrame(raf); raf = 0; }\n      else if (!raf) { last = 0; raf = requestAnimationFrame(frame); }\n    });\n\n    boot();\n    D.effects.push('aurora-canvas', 'palette-crossfade', 'amber-sweep', 'grain', 'entrance-stagger', 'pill-shine');\n  })();\n\n  No console.log anywhere. No unhandled promise. No network request other than Google Fonts.\n  File size target: under 90 KB (this page should land around 22-30 KB).\n\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the <h1> is split into exactly 2 .headline-line elements, each with overflow:hidden","a #aurora canvas exists with non-zero width and paints non-blank pixels, and __DEMO__.frames advances by >= 20 in 1.2s","the footer contains exactly 3 .stat items and the page contains exactly one <h1>","no element overflows the viewport horizontally at 390, 768, 1440 and 1920 widths","__DEMO__.effects includes 'aurora-canvas' and 'palette-crossfade', and __DEMO__.errors is empty","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"],"usage":{"build_tokens":47432,"build_prompt_tokens":12305,"build_completion_tokens":35127,"build_reasoning_tokens":26572,"build_calls":1,"build_attempts":1,"build_latency_ms":322387,"wasted_tokens":0,"repair_tokens":43735,"crosscheck_tokens":57094,"total_tokens":148261},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"24632 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"16 frames, effects: aurora-canvas, palette-crossfade, amber-sweep, grain, entrance-stagger, pill-shine"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"22 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 43.55, stdev 30.5, coverage 0.9851"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 3102098699 -> 3604924717"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Move freight with an AI that\nalready knows the road.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Aurora Freight — Freight Intelligence, Orchestrated"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=379 pageErrors=0"}]},"crosscheck":{"slug":"aurora-freight","reviewer_model":"hy3","reviewer_platform":"workbuddy","verdict":"confirmed","confidence":"high","aesthetic_score":8,"requirements_met":["document title exactly 'Aurora Freight — Freight Intelligence, Orchestrated'","lang='en' on html","pure black ground (body inline + html,body !important)","fixed canvas #aurora with required CSS (z:0, pointer-events:none, #000 background)","DPR capped at 2, SCALE 0.5, 2,600,000 device-pixel clamp implemented","18 pre-rendered radial sprite canvases (256x256, 5-stop gradient)","screen composite mode, hard-black ground each frame","5 Lissajous bands + amber sweep band 5 with 30s cycle, easeInOutSine envelope","palette cross-fade every 20s with cancellation snapshot rule (B.6)","dt clamp at 0.05, ~60fps draw budget (15.5ms), visibilitychange handling","SVG feTurbulence grain at 4% with 6-step drift animation (compositor-only)","scrim: fixed, z:1, pointer-events:none, correct radial + linear gradient stops","layer stack z-indices (0/1/2/90/100) match spec","all required CSS tokens declared on :root at default values","header with inline SVG brand-mark (3-arc path + 2.2r amber circle + linearGradient markGrad)","four pills in exact order Platform/Network/Pricing/Docs + Request access ghost","burger 40x40 with 3x 1x16 white bars, aria-label/expanded/controls","hero bottom-anchored via align-items:flex-end, padding per spec","two-line masked h1 with em 'freight' in italic Instrument Serif, color --muted","lede copy exact, max-width var(--lede-max)","two hero CTAs: btn-solid 'Request access' and btn-line 'Watch the 2-min tour'","footer stats with exactly three li items, tabular-nums on b","mobile menu overlay z:90 with backdrop-filter blur+saturate, link/Escape/resize close","anti-flash rules in correct order at top of stylesheet",".appear resting state opacity:1, .is-in settlement, two-rAF fallback implemented","all 7 entrance keyframes defined with correct end states","stagger table matches element-to-animation-to-delay mapping","lede override animation-duration:1.25s applied","all 8 responsive breakpoints present and ordered (1600/1920/1280-1599/901-1279/<=850/<=720/<=900/<=560)","desktop lock (>=901px) sets html/body height:100% overflow:hidden and .page 100dvh overflow:hidden","<=900px disables desktop lock, hides nav-desktop, shows burger","prefers-reduced-motion media query with full overrides + canvas still frames-counter","window.__DEMO__ telemetry (ready/frames/effects/errors/name) implemented","window.error handler pushes to D.errors","no console.log, no third-party JS, no remote media, only Google Fonts request"],"requirements_missed":[],"issues":[{"issue":"Aurora field reads as very subdued in the 1024x640 screenshot — only a faint right-edge glow is visible","severity":"low","evidence":"Spec deliberately caps luminance <0.78 to protect readability (B.5 note) and uses peak alphas 0.28–0.62, so this is by design, but at the captured moment the amber sweep isn't on-screen either, leaving the field quiet."},{"issue":"Anti-flash rules list order differs from brief ordering — brief specifies inline body style as item 2 between two html,body rules, generated file only has two html,body rules + body attribute (acceptable collapse)","severity":"low","evidence":"CSS lines 41–42 ('html, body { background:#000000 !important; color:#ffffff; }' then 'html, body { background:#000000; background: var(--bg…)') plus inline body attribute — the effect is preserved."}],"summary":"A faithful, end-to-end implementation of the Aurora Freight brief — copy is exact, tokens, breakpoints, layer stack, sprite/aurora math, palette cross-fade, amber sweep, grain, entrance stagger, mobile menu, reduced-motion and __DEMO__ telemetry are all correctly wired and the screenshot is free of clipping or mis-positioned elements. The aurora field is on the quiet end at 1024x640 because the spec deliberately caps luminance; nothing in the rendering is broken.","usage":{"input":22061,"output":4972,"total":27033,"reasoning":3896,"cached":22060},"latency_ms":49249}},{"slug":"basalt","title":"Basalt","category":"Landing Page","effect":"A light sweep recalculates the three faces of sixty SVG basalt columns, creating a rippling geothermal field.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/basalt/index.html","doc":"/prompts/basalt.md","prompt_text":"WHAT IS BEING BUILT\n\nBuild one complete standalone index.html for a single-viewport cinematic landing page titled “Basalt — Geothermal Energy From Deep Ground.” The document language is en. The document title is “Basalt — Geothermal Energy From Deep Ground.” Include one <header>, one <nav> with an accessible name, one <main>, and one <footer>. Include exactly one <h1> reading “POWER, BUILT FROM THE EARTH.” Do not build a second heading, article, application shell, form, cookie panel, modal, card carousel, dashboard, pricing section, login flow, video, image gallery, or additional page.\n\nUse semantic HTML, plain CSS, and vanilla JavaScript only. Do not use React, Tailwind, GSAP, Three.js, Lottie, a framework, a bundler, a build step, external JavaScript, external images, remote fonts other than Google Fonts, MP4, WebM, GIF, base64 media, or any third-party CDN URL. Every visual pixel must be produced by HTML, CSS gradients, inline SVG, or CSS transforms. Google Fonts is the only permitted network request. Load Inter from https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap and provide the exact system fallback stack.\n\nAt viewport widths of 901px and above, set html, body, and the page shell to height: 100dvh and overflow: hidden. The page itself does not scroll. At widths of 900px and below, allow vertical document scrolling while preventing horizontal scrolling. The composition must fit without clipping at 390x844, 768x1024, 1440x900, and 1920x1080. The page must remain readable with JavaScript disabled except for the decorative light sweep.\n\nTHE BACKDROP\n\nCreate a full-viewport geothermal scene using one inline SVG field containing exactly 60 hexagonal prisms. The field is not a flat honeycomb: every prism has a top rhombus/hex face, one lower-left side face, and one lower-right side face. Use 60 <g class=\"column\"> elements and exactly 180 child face paths: 60 .top, 60 .left, and 60 .right. Generate the complete SVG in JavaScript at startup from numeric geometry; do not include 180 handwritten path elements.\n\nUse a flat-top hex grid with horizontal spacing of 92 SVG units and row staggering of 78 SVG units. Start at x=220, y=250. The field occupies an SVG viewBox of 0 0 1600 1000. Generate rows 0 through 8 with row counts of 6, 7, 7, 7, 7, 7, 7, 7, and 5, totalling 60. Offset every odd row by 46 SVG units. Each top hexagon has radius 46 SVG units, with vertices at angles 0, 60, 120, 180, 240, and 300 degrees. The top face uses points at x±46, y+23, and x, y−39 around its center. Extrude each column downward by a height from 34 to 214 SVG units according to depth. The left face connects the top’s lower-left, bottom, and lower-right vertices to their translated counterparts. The right face uses the lower-right, bottom, and translated lower-center geometry. Use fill-rule=\"evenodd\" where required, but keep all three faces individually addressable.\n\nMake nearer columns taller. Compute depth with a fixed column index-based formula: minimumDepth = 34, maximumDepth = 214, and depth = 214 − round(((row / 8) * 108) + ((columnIndex % 5) * 12)). Add no randomness. Apply a perspective scale from 0.58 in the rear to 1.08 in the front, centered at x=800,y=565. Apply a 6 degree skewX(-6) to the SVG field. Do not use SVG filters on individual columns.\n\nThe background is a CSS radial gradient centered at 50% 58%: #17252a at 0%, #0d171b at 48%, #070d10 at 100%. Overlay one linear-gradient(115deg, rgba(16,35,39,.92) 0%, rgba(5,12,15,.18) 44%, rgba(18,31,35,.74) 100%). Add one absolutely positioned .grain layer using a repeating-radial-gradient at 0.34px radius, 7px spacing, rgba(226,232,240,.025) 0, rgba(226,232,240,0) 1px. The grain must be static and pointer-events:none.\n\nAdd a moving .light-sweep element across the field. It is a 720px-wide, 1400px-high rotated rectangle with opacity 0, background linear-gradient(90deg, rgba(226,232,240,0) 0%, rgba(226,232,240,.025) 35%, rgba(226,232,240,.10) 50%, rgba(226,232,240,.02) 67%, rgba(226,232,240,0) 100%), transform translate3d(-900px,-80px,0) rotate(18deg), and will-change: transform,opacity. The light is a visual only; it must not intercept pointer events.\n\nThe SVG is rendered once with width:100%; height:100%; display:block. Do not use canvas. No canvas resolution is required; state this explicitly in implementation: canvas resolution is not applicable, and there is no canvas element. The per-frame budget is 60 columns, 180 fill updates maximum, one transform update, one opacity update, and zero layout reads. Update only SVG fill values and opacity or CSS custom properties. Do not call getBoundingClientRect, getComputedStyle, querySelector, querySelectorAll, classList manipulation, or DOM creation during an animation frame. Cache all nodes, geometry, colors, and previous values before the rAF loop. Precompute the three face RGB values for 24 light-angle samples in JavaScript before animation begins.\n\nUse no per-item SVG filter, no blur filter on the moving light, and no shadowBlur equivalent. Add foreground haze with two static CSS pseudo-elements: .haze-near is a 760px by 280px radial ellipse at 12% 104% with blur(34px), opacity .22; .haze-far is a 1100px by 260px radial ellipse at 92% 70% with blur(58px), opacity .14. These are the only blur radii used by the backdrop.\n\nTHE LAYER STACK\n\nBuild the visual stack back to front with these exact z-index values. The fixed page shell has z-index:0 and position:relative; isolation:isolate. The .backdrop has z-index:-5 and position:absolute; inset:0. The .backdrop-gradient has z-index:-4. The .grain has z-index:-3. The #field has z-index:-2. The .haze-far has z-index:-1. The .light-sweep has z-index:0. The .vignette has z-index:1. The header has z-index:10. The main has z-index:5. The footer has z-index:10. The focus treatment must remain above every layer, use outline rather than z-index, and be visible on all controls. No stacking context may put the hero text behind the field.\n\nTHE TOKEN TABLE\n\nDefine these exact custom properties on :root and use them rather than untracked sizes or colors:\n\n--page-bg: #070d10\n--ink: #f4f7f8\n--muted: #9aa9ad\n--accent: #e2e8f0\n--accent-soft: rgba(226,232,240,.14)\n--line: rgba(226,232,240,.22)\n--panel: rgba(12,23,27,.72)\n--panel-solid: #0c171b\n--black: #05090b\n--teal-deep: #17252a\n--teal-mid: #0d171b\n--teal-low: #070d10\n--font-sans: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif\n--page-pad-x: clamp(24px, 5.55vw, 120px)\n--page-pad-y: 28px\n--header-height: 48px\n--header-row-height: 32px\n--nav-gap: 26px\n--hero-max-width: 720px\n--hero-top: clamp(104px, 15vh, 160px)\n--hero-grid-gap: 26px\n--eyebrow-size: 11px\n--eyebrow-tracking: .18em\n--h1-size: clamp(44px, 5.25vw, 86px)\n--h1-line-height: .96\n--h1-weight: 600\n--body-size: 15px\n--body-line-height: 1.6\n--body-max-width: 430px\n--body-gap: 24px\n--action-row-gap: 18px\n--button-height: 46px\n--button-padding-x: 22px\n--button-radius: 2px\n--button-font-size: 12px\n--button-tracking: .12em\n--footer-height: 42px\n--footer-gap: 24px\n--field-opacity: .92\n--field-blur-far: 0px\n--field-blur-near: 0px\n--vignette-alpha: .34\n--sweep-duration: 12s\n--sweep-delay: -2.4s\n--ease-out: cubic-bezier(.16,1,.3,1)\n--ease-in-out: cubic-bezier(.65,0,.35,1)\n--ease-sweep: cubic-bezier(.45,0,.55,1)\n--duration-page: 900ms\n--duration-stagger: 760ms\n--duration-sweep: 12000ms\n\nSet box-sizing:border-box on every element. Set body margin:0, min-width:320px, color:var(--ink), background:var(--page-bg), font-family:var(--font-sans), and text-rendering:optimizeLegibility.\n\nTHE COMPOSITION\n\nHeader is a 100% width flex row with padding:var(--page-pad-y) var(--page-pad-x), height:var(--header-height), align-items:center, justify-content:space-between, and border-bottom:1px solid rgba(226,232,240,.11). The brand is an anchor with aria-label=\"Basalt home\", display:flex, align-items:center, gap:12px, color:var(--ink), text-decoration:none, font-size:14px, font-weight:700, letter-spacing:.2em. Its mark is a 24px square inline SVG with a 14px basalt-colored hexagon and a 2px accent diagonal line. The navigation contains exactly three links: “Approach”, “Systems”, and “Contact”. Each link has a visible accessible name, color:var(--muted), text-decoration:none, font-size:11px, font-weight:600, letter-spacing:.14em, text-transform:uppercase, and a hover color of var(--accent). The right-side header status is a 9px dot plus the exact text “GEOTHERMAL / ACTIVE”, with dot color #8ba8a1.\n\nMain is positioned absolute with inset:var(--header-height) var(--page-pad-x) var(--footer-height), display:grid, align-items:center, pointer-events:none. Main content is pointer-events:auto. The hero is a 7-column grid with the copy block spanning columns 1 through 5 and a 2-column technical readout occupying columns 6 through 7. The hero has max-width:1440px, width:100%, margin:0 auto, transform:translateY(calc(var(--hero-top) * -.16)), and gap:var(--hero-grid-gap).\n\nThe hero copy is stacked with gap:var(--body-gap). The eyebrow is one line reading “BASALT / DEEP-HEAT INFRASTRUCTURE”, color:var(--accent), font-size:var(--eyebrow-size), font-weight:600, letter-spacing:var(--eyebrow-tracking), line-height:1.2, text-transform:uppercase. Place a 24px horizontal accent rule before it. The h1 is max-width:720px, color:var(--ink), font-size:var(--h1-size), font-weight:var(--h1-weight), line-height:var(--h1-line-height), letter-spacing:-.055em, text-wrap:balance, and contains one highlighted span using color:var(--accent) and font-weight:400. The exact h1 content is “POWER, BUILT FROM THE EARTH.”\n\nThe body paragraph is max-width:var(--body-max-width), margin:0, color:var(--muted), font-size:var(--body-size), line-height:var(--body-line-height), and contains exactly: “Basalt turns the earth’s stable heat into precise, continuous power. We build the systems that make deep geothermal energy dependable at grid scale.” The action row contains two visible controls. The primary anchor text is “EXPLORE THE FIELD” and href=\"#field\". The secondary anchor text is “OUR APPROACH” and href=\"#approach\". Both have height:var(--button-height), padding:0 var(--button-padding-x), border-radius:var(--button-radius), font-size:var(--button-font-size), font-weight:700, letter-spacing:var(--button-tracking), line-height:var(--button-height), text-decoration:none, text-transform:uppercase.\n\nThe technical readout is a 2-column grid, align-self:end, padding:18px 0 18px 24px, border-left:1px solid var(--line), gap:18px. It contains four exact labels and values: “DEPTH / 4.8 KM”, “OUTPUT / 120 MW”, “UPTIME / 99.97%”, and “STATUS / COMMISSIONED”. Labels use 9px, 600 weight, .15em tracking, uppercase, and var(--muted). Values use 12px, 600 weight, .06em tracking, and var(--ink). The readout is decorative information, not a control.\n\nFooter is a flex row with padding:0 var(--page-pad-x), height:var(--footer-height), align-items:center, justify-content:space-between, border-top:1px solid rgba(226,232,240,.11), color:var(--muted), font-size:10px, font-weight:500, letter-spacing:.12em, text-transform:uppercase. The left text is “BASALT ENERGY SYSTEMS”. The center text is “BUILT BELOW / DISTRIBUTED EVERYWHERE”. The right text is “© 2025 BASALT”. Each text item has a visible focus state only when interactive; these footer items are not interactive.\n\nTHE UI LANGUAGE\n\nThe primary control uses background:linear-gradient(135deg,#f4f7f8 0%,#cbd5d9 100%), color:#071015, border:1px solid #f4f7f8, box-shadow:0 8px 24px rgba(0,0,0,.18). Its resting transform is translateY(0), opacity:1. On hover, background:linear-gradient(135deg,#ffffff 0%,#dce5e8 100%), transform:translateY(-2px), box-shadow:0 12px 28px rgba(0,0,0,.26). On active, transform:translateY(0), box-shadow:0 5px 14px rgba(0,0,0,.2). The secondary control uses background:rgba(226,232,240,.04), color:var(--accent), border:1px solid rgba(226,232,240,.42). Its resting transform is translateY(0), opacity:1. On hover, background:rgba(226,232,240,.11), border-color:var(--accent), transform:translateY(-2px). On active, transform:translateY(0). Every visible anchor has a focus-visible outline:2px solid #ffffff, outline-offset:4px, border-radius:2px. The brand and navigation links use a 1px transparent underline at rest and a 1px var(--accent) underline on hover, with focus-visible outline as specified.\n\nMOTION\n\nUse these named keyframes:\n\n@keyframes basalt-rise { from { opacity:0; transform:translate3d(0,22px,0); } to { opacity:1; transform:translate3d(0,0,0); } }\n@keyframes basalt-fade { from { opacity:0; } to { opacity:1; } }\n@keyframes basalt-scan { 0% { opacity:0; transform:translate3d(-900px,-80px,0) rotate(18deg); } 16% { opacity:.82; } 68% { opacity:.5; } 100% { opacity:0; transform:translate3d(2350px,-80px,0) rotate(18deg); } }\n@keyframes basalt-pulse { 0%,100% { opacity:.45; } 50% { opacity:1; } }\n\nApply the initial page sequence with these exact delays. Header: basalt-fade, duration 760ms, delay 0ms. Brand: basalt-fade, duration 620ms, delay 80ms. Nav links: basalt-fade, duration 620ms, delay 140ms, 170ms, 200ms for Approach, Systems, Contact. Eyebrow: basalt-rise, duration 760ms, delay 260ms. h1: basalt-rise, duration 900ms, delay 340ms. Paragraph: basalt-rise, duration 760ms, delay 460ms. Primary button: basalt-rise, duration 760ms, delay 560ms. Secondary button: basalt-rise, duration 760ms, delay 620ms. Technical readout: basalt-fade, duration 820ms, delay 700ms. Footer: basalt-fade, duration 760ms, delay 820ms. The status dot uses basalt-pulse, duration 1800ms, delay 900ms, iteration-count:infinite.\n\nAnimate the light sweep with basalt-scan, duration 12s, delay -2.4s, iteration-count:infinite, timing-function:cubic-bezier(.45,0,.55,1). The field is static in layout; its face colors change through the JavaScript light-angle loop. The loop is the only continuous color animation. In JavaScript, calculate normalizedTime as ((now - startTime) / 12000) % 1, angle as -72 + normalizedTime * 144 degrees, and sampleIndex as round(((angle + 72) / 6) % 24). Interpolate between sample 0 and sample 1 with t equal to the fractional sample position. Set each top, left, and right fill from the cached sample arrays. Set column opacity from a cached depth lookup: rear .42, middle .68, front .92. Do not animate SVG geometry, transform, or filter in the rAF loop.\n\nRESPONSIVE TABLE\n\nApply the following exact overrides:\n\nAt 1920px and wider: --page-pad-x:120px; --page-pad-y:32px; --h1-size:86px; --hero-top:160px; --body-size:15px; --field-opacity:.94.\n\nAt 1440px and wider: --page-pad-x:80px; --page-pad-y:28px; --h1-size:76px; --hero-top:136px; --body-size:15px; --field-opacity:.92.\n\nAt 901px through 1439px: --page-pad-x:56px; --page-pad-y:24px; --h1-size:64px; --hero-top:112px; --body-size:14px; --field-opacity:.86.\n\nAt 768px through 900px: set height:auto, min-height:100dvh, overflow-x:hidden; --page-pad-x:32px; --page-pad-y:20px; --header-height:44px; --footer-height:38px; --h1-size:52px; --hero-top:0px; --hero-grid-template:1fr; --field-opacity:.72; --vignette-alpha:.24. Main is position:relative, inset:auto, min-height:calc(100dvh - 82px), padding:64px 0 48px, align-items:start. The hero is one column, gap:36px. The technical readout becomes a horizontal 4-column row with padding:16px 0 0, border-left:0, border-top:1px solid var(--line), gap:16px. The nav gap is 18px. The footer center text is hidden only through a semantic visually-hidden class, not display:none, so the accessible footer structure remains intact.\n\nAt 390px through 767px: set height:auto, min-height:100dvh, overflow-x:hidden; --page-pad-x:22px; --page-pad-y:18px; --header-height:42px; --footer-height:36px; --h1-size:44px; --hero-top:0px; --body-size:14px; --body-line-height:1.55; --field-opacity:.58; --vignette-alpha:.2; --nav-gap:12px; --button-height:44px; --button-padding-x:16px; --button-font-size:10px. Hide the header navigation links and the header status text from layout with a 1px visually-hidden utility, preserving their accessible names and keyboard order; do not use display:none or visibility:hidden. Main has padding:52px 0 36px, min-height:calc(100dvh - 78px). The hero is one column. The technical readout is a 2-column grid with padding-top:18px and border-top:1px solid var(--line). The footer displays only the left and right text with gap:10px; center text is visually hidden. The field remains 1600x1000 viewBox with width:160%; height:100%; left:-30%; top:0%; opacity:var(--field-opacity). The light sweep remains 720px wide and is clipped by overflow:hidden.\n\nAt height 760px and below, including 1440x760: --page-pad-y:20px; --header-height:42px; --footer-height:36px; --hero-top:76px; --h1-size:54px; --body-size:13px; --body-line-height:1.45; --body-gap:18px; --action-row-gap:12px. Reduce technical readout padding-top to 12px and padding-bottom to 12px. At height 680px and below, including 390x680: --page-pad-y:16px; --header-height:38px; --footer-height:32px; --h1-size:40px; --hero-top:56px; --body-size:13px; --body-gap:14px; --button-height:40px; --button-padding-x:14px; --footer-font-size:9px. The h1 may wrap to three lines; the hero must not overlap the footer.\n\nFAILURE GUARDS\n\nSet every animated foreground element to opacity:1 and transform:none as its resting CSS state. JavaScript must not make copy disappear. If JavaScript does not execute, the inline SVG field, background, copy, and controls remain visible and static. Initialize telemetry before any other script work with exactly:\n\nwindow.__DEMO__ = { name: 'basalt', ready: false, frames: 0, effects: [], errors: [] };\n\nRegister one window error listener that pushes String(event.message || 'window error') into window.__DEMO__.errors. Do not call console.log, console.warn, console.error, alert, or debuggers. After the first paint, use requestAnimationFrame(() => { window.__DEMO__.ready = true; }); Mark ready only after the first frame has been composited.\n\nUse a two-rAF fallback before starting continuous animation. The first rAF validates the viewport and caches measurements; the second rAF sets ready and starts the loop. If the second rAF is unavailable, set ready true on the first rAF and use a 50ms setTimeout to start the loop. If requestAnimationFrame is missing, set ready true and do not run a continuous loop; retain the static composition. If an individual face update throws, catch the error once outside the loop, push the message to telemetry.errors, stop the loop, and set all face fills to their cached fallback values. Never throw asynchronously.\n\nUnder prefers-reduced-motion: reduce, remove the sweep animation, set .light-sweep to display:none, remove all page entrance animations, set all animated foreground elements to opacity:1 and transform:none, set #field opacity:var(--field-opacity), and use one static light angle of 0 degrees for all faces. The reduced-motion static state must be readable, have no continuous JavaScript loop, and retain telemetry ready=true after first paint. The reduced-motion CSS block must include all entrance selectors and the light-sweep selector.\n\nTHE EXACT JS\n\nUse this JavaScript structure, preserving the named constants, cached references, geometry, telemetry, guards, and loop semantics:\n\n(() => {\n  'use strict';\n  window.__DEMO__ = { name: 'basalt', ready: false, frames: 0, effects: ['basalt-rise','basalt-fade','basalt-scan','basalt-pulse','face-light'], errors: [] };\n  window.addEventListener('error', event => window.__DEMO__.errors.push(String(event.message || 'window error')));\n  const reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n  const svg = document.getElementById('field');\n  const namespace = 'http://www.w3.org/2000/svg';\n  if (!svg) return;\n  const columns = [];\n  const faces = { top: [], left: [], right: [] };\n  const rowCounts = [6,7,7,7,7,7,7,7,5];\n  const hex = radius => Array.from({length:6}, (_, i) => {\n    const angle = Math.PI * i / 3;\n    return [radius * Math.cos(angle), radius * Math.sin(angle)];\n  });\n  const make = (tag, className, points) => {\n    const node = document.createElementNS(namespace, tag);\n    node.setAttribute('class', className);\n    node.setAttribute('d', points.map((p, i) => (i ? 'L' : 'M') + p[0].toFixed(2) + ' ' + p[1].toFixed(2)).join(' ') + ' Z');\n    return node;\n  };\n  let index = 0;\n  rowCounts.forEach((count, row) => {\n    for (let col = 0; col < count; col++) {\n      const x = 220 + col * 92 + (row % 2 ? 46 : 0);\n      const y = 250 + row * 78;\n      const depth = 214 - Math.round((row / 8) * 108 + (col % 5) * 12);\n      const radius = 46;\n      const vertices = hex(radius).map(p => [x + p[0], y + p[1]]);\n      const bottom = vertices.map(p => [p[0], p[1] + depth]);\n      const group = document.createElementNS(namespace, 'g');\n      group.setAttribute('class', 'column');\n      const leftPoints = [vertices[4], vertices[5], bottom[5], bottom[4], bottom[3]];\n      const rightPoints = [vertices[5], vertices[0], bottom[0], bottom[5]];\n      const top = make('path', 'top', vertices);\n      const left = make('path', 'left', leftPoints);\n      const right = make('path', 'right', rightPoints);\n      group.append(top, left, right);\n      svg.appendChild(group);\n      const opacity = .42 + (row / 8) * .50;\n      group.setAttribute('opacity', opacity.toFixed(3));\n      columns.push(group);\n      faces.top.push(top); faces.left.push(left); faces.right.push(right);\n      index++;\n    }\n  });\n  const samples = Array.from({length:25}, (_, i) => {\n    const angle = -72 + (i / 24) * 144;\n    const radians = angle * Math.PI / 180;\n    const lightX = Math.cos(radians);\n    const lightY = Math.sin(radians);\n    const top = [94 + Math.round(24 * (lightY + 1)), 113 + Math.round(25 * (lightY + 1)), 119 + Math.round(27 * (lightY + 1))];\n    const left = [35 + Math.round(21 * (1 - lightX)), 56 + Math.round(24 * (1 - lightX)), 61 + Math.round(26 * (1 - lightX))];\n    const right = [55 + Math.round(26 * (1 + lightX)), 78 + Math.round(29 * (1 + lightX)), 83 + Math.round(31 * (1 + lightX))];\n    return {top,left,right};\n  });\n  const applySample = sample => {\n    faces.top.forEach(node => node.setAttribute('fill', 'rgb(' + sample.top.join(',') + ')'));\n    faces.left.forEach(node => node.setAttribute('fill', 'rgb(' + sample.left.join(',') + ')'));\n    faces.right.forEach(node => node.setAttribute('fill', 'rgb(' + sample.right.join(',') + ')'));\n  };\n  if (reduced) {\n    applySample(samples[12]);\n    requestAnimationFrame(() => { window.__DEMO__.ready = true; });\n    return;\n  }\n  const start = performance.now();\n  let lastIndex = -1;\n  const render = now => {\n    try {\n      window.__DEMO__.frames += 1;\n      const time = ((now - start) / 12000) % 1;\n      const position = time * 24;\n      const current = Math.floor(position);\n      const next = Math.min(24, current + 1);\n      const amount = position - current;\n      if (current !== lastIndex) {\n        const a = samples[current], b = samples[next];\n        const mix = (x,y) => Math.round(x + (y - x) * amount);\n        const sample = {top:a.top.map((v,i)=>mix(v,b.top[i])),left:a.left.map((v,i)=>mix(v,b.left[i])),right:a.right.map((v,i)=>mix(v,b.right[i]))};\n        applySample(sample);\n        lastIndex = current;\n      }\n      requestAnimationFrame(render);\n    } catch (error) {\n      window.__DEMO__.errors.push(String(error.message || error));\n    }\n  };\n  const first = requestAnimationFrame(() => {\n    const second = requestAnimationFrame(() => {\n      window.__DEMO__.ready = true;\n      startLoop();\n    });\n    if (!second) {\n      window.__DEMO__.ready = true;\n      startLoop();\n    }\n  });\n  function startLoop() {\n    if (typeof requestAnimationFrame === 'function') requestAnimationFrame(render);\n  }\n})();\n\nDo not use any other JavaScript API in the animation loop. Do not add a resize observer, pointer listener, scroll listener, canvas, or media-query listener. The implementation must remain below 90 KB, produce zero console output, have no uncaught exceptions, sustain at least 20 frames per 1.2 seconds at 1440x900, and expose the exact telemetry object.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["The generated document is one index.html with exactly one h1, semantic header/nav/main/footer elements, no external media, and no framework or bundler dependency.","At 1440x900, the page has height 100dvh with overflow hidden, no horizontal overflow, and the visible copy reads “POWER, BUILT FROM THE EARTH.”","The inline field contains exactly 60 column groups and 180 face paths, each column has one top, one left, and one right face, and the background uses no canvas or external images.","window.__DEMO__ exists with name basalt, ready true after first paint, a frames counter incremented once per animation frame, the five listed effect names, and an errors array; console output remains empty.","At prefers-reduced-motion: reduce, the sweep is absent, all animated content is readable and static, no continuous rAF loop runs, and the page still exposes ready true with no uncaught exceptions.","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"],"usage":{"build_tokens":51075,"build_prompt_tokens":7665,"build_completion_tokens":43410,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":3,"build_latency_ms":786564,"wasted_tokens":79330,"repair_tokens":0,"crosscheck_tokens":34229,"total_tokens":85304},"verification":{"passed":true,"passed_checks":18,"total_checks":18,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"28816 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"27 frames, effects: basalt-rise, basalt-fade, basalt-scan, basalt-pulse, face-light"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"115 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"POWER, BUILT FROM THE EARTH.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Basalt — Geothermal Energy From Deep Ground."},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=438 pageErrors=0"}]},"crosscheck":{"slug":"basalt","reviewer_model":"moonshotai/Kimi-K2.7-Code","reviewer_platform":"cmdcode","verdict":"partial","confidence":"high","aesthetic_score":6,"requirements_met":["Single valid index.html with standalone HTML/CSS/JS","Semantic landmarks: header, nav with aria-label, main, footer","Exactly one h1 with required text content","Document language and title present","Custom properties defined on :root and used throughout","Backdrop radial/linear gradients implemented","Grain layer present with pointer-events none","SVG field generated in JS with 60 columns and 180 face paths","Flat-top hex geometry, depth formula, and skewX(-6deg) applied","Light sweep element, animation keyframes, and timing values","Header, hero copy, buttons, readout, and footer content match the brief","Button states and focus-visible outline implemented","Named keyframes and entrance animation delays applied","Responsive overrides for 1920, 1440, 901-1439, 768-900, 390-767, and short heights","Reduced-motion static state supported","Telemetry object and error listener implemented"],"requirements_missed":["Page readable with JavaScript disabled (field is empty without JS)","Exact two-rAF startup fallback with 50 ms setTimeout","Error fallback that restores face fills to cached values","Global layer stack with z-index values as direct siblings","Per-frame fractional interpolation between light-angle samples","Cached depth opacity lookup values (.42/.68/.92)","Haze implemented as CSS pseudo-elements","Exact document title punctuation"],"issues":[{"issue":"SVG field is empty in the HTML; it is drawn entirely by JavaScript, so the page fails the brief's no-JS readability requirement.","severity":"high","evidence":"<svg id=\"field\" viewBox=\"0 0 1600 1000\" ...></svg> contains no static content."},{"issue":"The animation loop only recomputes face fills when the sample index changes, not each frame with fractional interpolation.","severity":"medium","evidence":"render() wraps applySample() inside `if (current !== lastIndex)`."},{"issue":"The two-rAF fallback is not implemented as specified; the 50 ms setTimeout path is missing and the fallback check `if (!second)` is invalid because requestAnimationFrame always returns a positive ID.","severity":"medium","evidence":"const second = requestAnimationFrame(() => { ... }); if (!second) { ... }"},{"issue":"The error catch block stops the loop but never resets face fills to cached fallback values.","severity":"medium","evidence":"catch block only pushes `error.message` and returns."},{"issue":"The backdrop layer stack is nested inside .backdrop instead of direct siblings, so the specified global z-index ordering is not literally applied.","severity":"medium","evidence":"<div class=\"backdrop\"> contains .backdrop-gradient, .grain, #field, .haze-far, .haze-near, .light-sweep, .vignette."},{"issue":"Column opacity is computed by linear row interpolation rather than the specified rear/middle/front lookup.","severity":"low","evidence":"const opacity = .42 + (row / 8) * .50;"},{"issue":"Haze is implemented as real <div> elements, not CSS pseudo-elements as described.","severity":"low","evidence":"<div class=\"haze-far\"></div><div class=\"haze-near\"></div>"},{"issue":"Document title has an extra trailing period compared to the brief.","severity":"low","evidence":"<title>Basalt — Geothermal Energy From Deep Ground.</title>"},{"issue":"Top face geometry uses a six-vertex trigonometric hex, not the brief's x±46, y+23 / x, y−39 construction.","severity":"low","evidence":"const hex = radius => Array.from({length:6}, (_, i) => { const angle = Math.PI * i / 3; ... });"},{"issue":"Grain background adds extra stops and a fixed background-size that differ from the specified repeating-radial-gradient.","severity":"low","evidence":"repeating-radial-gradient(...) includes .34px, 1px, transparent 1px, transparent 7px with background-size: 7px 7px."},{"issue":"No fill-rule=\"evenodd\" is applied to the face paths despite the brief's instruction.","severity":"low","evidence":"make() sets class and d but not fill-rule."}],"summary":"It mostly looks like the specified landing page, but the field is built only with JavaScript, the startup and interpolation logic deviate from the brief, and the layer stack is nested rather than flat. Premium feel is undermined by these reliability and precision gaps.","usage":{"input":17381,"output":16848,"total":34229,"reasoning":0,"cached":0},"latency_ms":211797}},{"slug":"bracket","title":"Bracket","category":"Landing Page","effect":"A measured editorial landing page whose headings are progressively marked by hand-drawn dashed brackets, corner dots, and one looping red correction.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/bracket/index.html","doc":"/prompts/bracket.md","prompt_text":"A. WHAT IS BEING BUILT\n\nBuild a single-file, single-viewport desktop landing page for Marginalia, a research-capture product whose primary action is marking up source material. The page must feel like a clean research document that has acquired four intelligent annotations: a product promise, three workflow headings, a testimonial, and one final call to action. It must not feel like a conventional SaaS hero with a centered headline, floating glass cards, decorative blobs, or a dashboard mock-up.\n\nCreate one complete index.html document with lang=\"en\", charset UTF-8, an exact document title of \"Marginalia — Keep the useful part\", and a responsive viewport declaration of width=device-width, initial-scale=1. The semantic page structure must contain one nav, one header, one main, and one footer. There must be exactly one h1: \"Keep the useful part.\" All other section titles are h2. Every visible control must be a native button or anchor with explicit accessible text. Use a visible 2px focus-visible outline in #C2452D with a 3px offset.\n\nAt widths of 901px and above, html, body, and the .page shell must be height: 100dvh; body must use overflow:hidden and the page must fit without vertical scrolling. At widths through 900px, the page may scroll vertically. No horizontal overflow is allowed at any width. The content column is flush-left, its desktop left gutter is exactly 72px, and its desktop text measure is 68ch. Annotation geometry may extend into the right margin but must remain inside the viewport.\n\nDo NOT build a split hero, centered hero, card grid, pricing table, feature icon row, testimonial carousel, dashboard screenshot, map, video, image, photograph, illustration unrelated to annotation, floating blobs, grain image, glassmorphism, neon gradient, dark theme, 3D object, cursor follower, text scramble, parallax, or second looping animation. Do not use external media, remote images, third-party CDNs, base64 assets, framework code, React, Tailwind, GSAP, Three.js, or Lottie. Google Fonts is the only permitted network request. Use IBM Plex Mono at weights 400, 500, and 600; if the font request fails, use ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, and monospace. Load the permitted font stylesheet with preconnect links to fonts.googleapis.com and fonts.gstatic.com. Keep the complete file under 90 KB.\n\nB. THE BACKDROP\n\nThe backdrop is a flat #F4F1EA bone field, not a canvas and not a video. Create 5 absolutely positioned ambient layers inside a .backdrop element with pointer-events:none:\n\n1. One 1180px by 820px radial gradient at left:-180px, top:-240px, using rgba(26,24,21,0.035) at 0%, rgba(26,24,21,0.012) at 48%, and transparent at 72%.\n2. One 900px by 640px radial gradient at right:-260px, bottom:-300px, using rgba(176,169,158,0.12) at 0% and transparent at 68%.\n3. One 1px vertical line at left:71px, top:0, bottom:0, color rgba(26,24,21,0.08).\n4. One 1px vertical line at right:71px, top:0, bottom:0, color rgba(26,24,21,0.06).\n5. One 26px by 26px repeating-linear-gradient cross-paper texture positioned at right:104px, top:42px, with 1px lines in rgba(26,24,21,0.035), opacity:0.5, and background-size:13px 13px.\n\nThe two margin boxes are semantic decorative spans, not controls. Place .margin-note.margin-note--one at left:18px, top:220px, width:24px, height:116px, border:1px dashed rgba(176,169,158,0.7), and opacity:0.06. Place .margin-note.margin-note--two at right:18px, bottom:118px, width:24px, height:84px, the same border, and opacity:0.06. Both are empty, aria-hidden=\"true\", contain no text, and exist visually as quote placeholders.\n\nEvery visible annotation is inline SVG with viewBox=\"0 0 200 95\" and preserveAspectRatio=\"none\". The base path is exactly M5 5 L195 5 L195 88 L5 72 Z, fill=\"none\", stroke=\"#B0A99E\", stroke-width=\"2\", vector-effect=\"non-scaling-stroke\", stroke-linecap=\"round\", stroke-linejoin=\"round\", and stroke-dasharray=\"6 4\". Place four corner circles at (5,5), (195,5), (195,88), and (5,72) with r=\"3.5\"; place four midpoint circles at (100,5), (195,46.5), (100,80), and (5,38.5) with r=\"3\"; all eight circles use fill=\"#B0A99E\" and vector-effect=\"non-scaling-stroke\". Wrap the path and circles in a group with transform-origin:50% 50% and transform:rotate(-0.5deg).\n\nPosition each annotation overlay as position:absolute around the measured bounds of its target heading. On initial layout and on every ResizeObserver delivery, use the target heading getBoundingClientRect() and the document or shell origin to calculate x,y,width,height. Expand the SVG by 9px horizontally and 17px vertically with the left edge at target.left - 9, top at target.top - 8, width equal to target.width + 18, and height equal to target.height + 16. This single initial/resize measurement is permitted. Never call getBoundingClientRect() inside the rAF loop. The SVG is aria-hidden=\"true\", focusable=\"false\", and pointer-events:none.\n\nUse a transparent SVG line of length 240 from 0,0 to 240,0 as a pathLength source for dash drawing. Animate the visible path from stroke-dashoffset:240 to 0 over 640ms with a custom cubic-bezier(0.16,1,0.3,1). The dots begin after 360ms. Dot scale progresses 0 to 1 in 200ms each with a 40ms stagger, using transform-box:fill-box and transform-origin:center.\n\nThe one red correction is a position:absolute 2px-high #C2452D line crossing the word \"useful\" in the h1. Place it from 4px below the h1’s top to the measured width of the inline text range for \"useful\" plus 5px. Its transform-origin:left center. Animate scaleX from 0 to 1 over 320ms using cubic-bezier(0.2,0.8,0.2,1), hold for 5.68s, then reset instantly at the end of a 6s cycle. It must be paused under reduced motion and displayed at scaleX:1.\n\nThere is no canvas. Therefore there is no canvas resolution trick, device-pixel-ratio handling, pixel buffer, filter, or shadow operation. The rAF work budget is no more than 6 style writes per frame, no more than 2 class toggles, no layout reads, and no allocation of DOM nodes. A frame updates only a cached document timestamp, the global frames counter, and the correction element’s CSS custom property --strike-progress. Pause the loop with document.hidden. Restart it on visibilitychange.\n\nC. THE LAYER STACK\n\nUse this exact front-to-back z-index contract:\n\n1. body and page base: z-index:auto.\n2. .backdrop gradients and guide lines: z-index:0.\n3. .margin-note placeholders: z-index:1.\n4. nav: z-index:5.\n5. main: z-index:10.\n6. inline heading annotation SVGs: z-index:11.\n7. the h1 red correction line: z-index:12.\n8. footer: z-index:20.\n9. .sr-only utility: z-index:100, but it remains 1px by 1px and visually hidden.\n\nThe .page element is position:relative; min-height:100dvh; isolation:isolate. All positioned annotation SVG elements are children of their associated .marked section, not of body. Each .marked section is position:relative and must not create a stacking context. Each annotation overlay’s parent section can receive z-index:11 only if it is also position:relative; the SVG itself must remain z-index:11. Header, main, and footer must never overlap at desktop.\n\nD. TOKEN TABLE\n\nDefine every recurring size and color in :root. Do not replace these decisions with inline repeated values:\n\n--bone: #F4F1EA\n--ink: #1A1815\n--ink-soft: #4A463F\n--pencil: #B0A99E\n--red-pencil: #C2452D\n--paper-line: rgba(26,24,21,0.10)\n--paper-rule: rgba(176,169,158,0.34)\n--focus: #C2452D\n--font-mono: \"IBM Plex Mono\", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace\n--page-gutter: 72px\n--content-max: 1120px\n--measure: 68ch\n--nav-height: 76px\n--footer-height: 72px\n--header-space: 52px\n--hero-space: 68px\n--section-gap: 28px\n--section-rule: 1px\n--annotation-x-inset: 9px\n--annotation-y-inset: 8px\n--annotation-x-expand: 18px\n--annotation-y-expand: 16px\n--annotation-stroke: 2px\n--annotation-rotation: -0.5deg\n--dash-array: 6 4\n--dash-length: 240\n--draw-duration: 640ms\n--dot-duration: 200ms\n--dot-stagger: 40ms\n--strike-duration: 320ms\n--strike-period: 6000ms\n--ease-draw: cubic-bezier(0.16,1,0.3,1)\n--ease-strike: cubic-bezier(0.2,0.8,0.2,1)\n--radius-pill: 999px\n--radius-card: 2px\n--button-height: 44px\n--button-padding-x: 20px\n--focus-ring: 2px\n--focus-offset: 3px\n--desktop-line: 0.72\n--small-line: 0.82\n--body-size: 15px\n--small-size: 11px\n--micro-size: 10px\n--nav-size: 12px\n--hero-size: clamp(56px,5.2vw,92px)\n--h2-size: clamp(24px,2.2vw,34px)\n--testimonial-size: clamp(20px,1.8vw,27px)\n\nE. THE COMPOSITION\n\nThe nav uses height:var(--nav-height), padding:0 var(--page-gutter), display:grid, grid-template-columns:1fr auto 1fr, align-items:center. The left link contains the wordmark \"MARGINALIA / 01\". The center navigation contains two links, \"How it works\" and \"Field notes\". The right action is an anchor button with exact copy \"Start marking\". On widths through 760px, the center navigation is display:none; the grid remains 1fr auto and the right action remains visible.\n\nThe header sits directly below nav with padding:52px 72px 0. Its left edge matches the main column. Place a 10px uppercase eyebrow with exact copy \"RESEARCH CAPTURE / BETA\". Under it, place the only h1 with exact copy \"Keep the useful part.\" The h1 uses font-size:var(--hero-size), line-height:0.96, font-weight:500, letter-spacing:-0.055em, max-width:68ch, and color:var(--ink). The phrase \"useful\" must be wrapped in span#useful-word. Beneath the h1, place a 2-column grid with grid-template-columns:minmax(0,68ch) 180px, column-gap:48px, align-items:end. The left paragraph exact copy is \"Marginalia turns research into a marked-up record. Capture the line, add the reason, and return later to the thought that changed your work.\" The right meta block uses exact copy \"NO / LOGIN REQUIRED\" and \"BUILT FOR / CLOSE READING\". The paragraph is font-size:15px, line-height:1.7, max-width:68ch.\n\nMain contains a workflow region with padding:52px 72px 0. Its top rule is 1px solid var(--paper-rule). Use a 3-row single-column grid, gap:20px, and each row is min-height:82px. Each marked row contains a numbered mono label and an h2. Exact row copy is:\n\n01 / CAPTURE\n\"Mark the sentence, not the screen.\"\n\n02 / INTERRUPT\n\"Add the objection while it is sharp.\"\n\n03 / RETURN\n\"Leave a trail your future self can follow.\"\n\nOnly every third heading is wrapped by an SVG overlay. Therefore annotate row 03 only: the heading \"Leave a trail your future self can follow.\" gets the standard bracket overlay. Rows 01 and 02 remain unmarked, creating a deliberate cadence rather than making every heading decorative.\n\nThe main also contains a 2-column evidence band with margin-top:38px, padding-top:22px, border-top:1px solid var(--paper-rule), and grid-template-columns:minmax(0,1.7fr) minmax(220px,0.8fr), gap:54px. The left block is a blockquote with exact copy: “The best notes are not transcripts. They are arguments with the source.” The attribution below is \"ANIKA SEN / INDEPENDENT RESEARCHER\". The right block uses exact copy \"01 SOURCE / 02 REASON / 03 CONSEQUENCE\" with 13px line-height:1.75 and color var(--ink-soft). This is text, not a card.\n\nThe footer uses height:72px, padding:0 72px, display:grid, grid-template-columns:1fr auto 1fr, align-items:center, border-top:1px solid var(--paper-rule). Left copy is \"MARGINALIA / RESEARCH TOOL\". Center link exact copy is \"hello@marginalia.work\". Right copy is \"© 2025 / BETA 01\". The center and right items are right-aligned. The footer is real and visible; it is not a fixed overlay.\n\nF. THE UI LANGUAGE\n\nPrimary button and all action links use height:44px, min-width:132px, padding:0 20px, border:1px solid var(--ink), border-radius:999px, background:linear-gradient(135deg, #1A1815 0%, #1A1815 72%, #302C27 100%), color:#F4F1EA, font:500 12px/1 var(--font-mono), letter-spacing:0.02em, display:inline-flex, align-items:center, justify-content:center, gap:10px, cursor:pointer, transition:transform 180ms var(--ease-draw), background 180ms ease, color 180ms ease. Add a 6px by 6px red-pencil dot using a pseudo-element. On hover, transform:translateY(-2px), background:linear-gradient(135deg, #C2452D 0%, #A93825 100%), and the dot changes to #F4F1EA. On active, transform:translateY(0) scale(0.98). On focus-visible, outline:2px solid var(--focus), outline-offset:3px.\n\nSecondary text links have no button fill, no underline, and color:var(--ink). Their hover state changes color to var(--red-pencil). Focus-visible receives the same ring. The nav center links use 12px uppercase mono text with 24px gap. Do not add hover animation to body text or headings.\n\nG. MOTION\n\nUse CSS custom-property delays on annotation SVGs. The stroke keyframes are bracket-draw: from stroke-dashoffset:240 to 0 over 640ms. The dot keyframes are bracket-dot: 0% opacity:0; transform:scale(0); 100% opacity:1; transform:scale(1). The correction keyframes are correction-strike: 0% transform:scaleX(0); 5.33% transform:scaleX(1); 100% transform:scaleX(1), with animation-duration:6000ms. At animation iteration boundary, the next iteration begins at 0. The red line is the only looping animation.\n\nStagger table:\n\n- header eyebrow: fade-up, 0ms delay, 420ms.\n- h1: fade-up, 80ms delay, 620ms.\n- hero paragraph and meta: fade-up, 180ms delay, 520ms.\n- workflow row 01: fade-up, 240ms delay, 460ms.\n- workflow row 02: fade-up, 300ms delay, 460ms.\n- workflow row 03 text: fade-up, 360ms delay, 460ms.\n- workflow row 03 annotation path: bracket-draw, IntersectionObserver activation, 640ms.\n- workflow row 03 corner dot 1: bracket-dot, activation +360ms, 200ms.\n- workflow row 03 corner dot 2: bracket-dot, activation +400ms, 200ms.\n- workflow row 03 corner dot 3: bracket-dot, activation +440ms, 200ms.\n- workflow row 03 corner dot 4: bracket-dot, activation +480ms, 200ms.\n- workflow row 03 midpoint dot 1: bracket-dot, activation +520ms, 200ms.\n- workflow row 03 midpoint dot 2: bracket-dot, activation +560ms, 200ms.\n- workflow row 03 midpoint dot 3: bracket-dot, activation +600ms, 200ms.\n- workflow row 03 midpoint dot 4: bracket-dot, activation +640ms, 200ms.\n- evidence block: fade-up, 520ms delay, 520ms.\n- footer: fade-in, 620ms delay, 420ms.\n\nUse opacity and transform only for non-SVG text motion. Define .reveal with opacity:0, transform:translateY(14px), and .reveal.is-visible with opacity:1, transform:translateY(0). The final resting state of all text is opacity:1, not opacity:0. Do not animate SVG x, y, width, or height in rAF.\n\nIntersectionObserver uses threshold:0.2 and rootMargin:\"0px 0px 0px 0px\". It adds .is-visible to .reveal elements and .is-drawn to annotation SVG elements. An annotation is drawn once. Disconnect each observed element after activation. If IntersectionObserver is unavailable, add the active classes synchronously after first paint.\n\nH. RESPONSIVE TABLE\n\nDesktop, minimum width:901px: --page-gutter:72px; --nav-height:76px; --footer-height:72px; --header-space:52px; --hero-space:68px; --section-gap:28px; body overflow:hidden; html, body, .page height:100dvh; nav, header, main, footer use the 72px horizontal gutter; content max-width:1120px; the workflow has 3 rows; annotation overlays remain 18px wider and 16px taller than their target.\n\nTablet, 901px through 1199px: --page-gutter:48px; --nav-height:68px; --footer-height:64px; --header-space:38px; --hero-space:48px; --hero-size:clamp(52px,6.4vw,72px); body overflow:hidden; nav, header, main, footer use 48px gutters; evidence gap:36px; annotation rotation remains -0.5deg.\n\nSmall, 601px through 900px: --page-gutter:32px; --nav-height:64px; --footer-height:auto; --header-space:32px; --hero-space:40px; --section-gap:24px; body overflow-y:auto; .page min-height:100dvh and height:auto; nav, header, main, footer use 32px gutters; header’s 2-column hero grid becomes one column; evidence band becomes one column with gap:24px; workflow rows use min-height:96px; margin notes remain in viewport edges at 12px.\n\nPhone, 390px through 600px: --page-gutter:20px; --nav-height:60px; --footer-height:auto; --header-space:24px; --hero-space:34px; --hero-size:clamp(42px,12vw,58px); --h2-size:25px; --body-size:14px; --small-size:10px; body overflow-y:auto; nav padding:0 20px; nav center links display:none; header, main, footer padding-left:20px and padding-right:20px; nav wordmark is 10px; primary button is 40px high and min-width:112px; hero meta moves below the paragraph with 16px gap; workflow grid uses 1fr; row number occupies 72px; row h2 max-width:calc(100vw - 112px); evidence block uses 1fr; footer uses 1fr auto and places copyright on a second line; .margin-note--one is 7px from the left, and .margin-note--two is 7px from the right.\n\nShort-height breakpoint, max-height:760px and min-width:901px: --header-space:28px; --hero-space:28px; --section-gap:14px; --nav-height:64px; --footer-height:56px; workflow row min-height:58px; evidence margin-top:20px; footer line remains 1px.\n\nShort-height mobile breakpoint, max-height:700px and max-width:900px: --header-space:20px; --hero-space:26px; --section-gap:16px; workflow row min-height:76px; evidence margin-top:22px. These two max-height rules must not disable scroll.\n\nAt every breakpoint, clamp annotation SVG widths to calc(100vw - 32px) on small screens and keep its left coordinate at or above 8px. Never allow the annotation SVG to create horizontal scroll.\n\nI. FAILURE GUARDS\n\nAll content has a resting opacity of 1. If JavaScript does not run, remove the .js class from html and the reveal selectors must default to visible; initialize reveal styles only after the first successful script setup. All annotation paths begin fully drawn only if their class is never added? No: the visual resting state must be readable, so SVG path default stroke-dashoffset:0 and dot default opacity:1. When JavaScript is active and the annotation has not been observed, add .is-armed, which sets stroke-dashoffset:240 and dot opacity:0 with transform:scale(0). On activation, .is-drawn restores the final state. The red correction default is visible at scaleX:1 and only becomes animated after the two-rAF fallback has completed.\n\nUse a two-rAF fallback before animation: requestAnimationFrame(() => requestAnimationFrame(() => { document.documentElement.classList.add(\"motion-ready\"); syncAllAnnotations(); setReady(); })). If the second frame does not execute within 1200ms, a setTimeout fallback marks all reveal elements visible, marks all annotations drawn, marks motion-ready, and sets ready:true. This fallback is cleared after either path completes.\n\nUnder prefers-reduced-motion:reduce, html, body, and every component must be static and readable. Disable reveal transforms, SVG stroke drawing, dot scaling, and the correction loop. Set .reveal to opacity:1 and transform:none; set annotation path stroke-dashoffset:0; set every dot opacity:1 and transform:scale(1); set the correction to scaleX(1). Do not start the rAF loop. Mark all annotation overlays visible immediately and set ready:true after first paint.\n\nJ. THE EXACT JS\n\nUse one inline script after the closing footer. Create this exact telemetry object first:\n\nwindow.__DEMO__ = { name: 'bracket', ready: false, frames: 0, effects: [], errors: [] };\n\nRegister window error and unhandledrejection listeners. Each error handler pushes the string form of event.reason or event.message into window.__DEMO__.errors, without console output. Register a resize listener only when ResizeObserver is unavailable; otherwise observe every .marked and the .page shell. Cache all elements once in arrays: reveals, annotations, annotationPaths, annotationDots, strike, and strikeWord. Cache document.documentElement and document.body references.\n\nDefine measureAnnotation(annotation) with these operations: read annotation.dataset.target, resolve that target, read target.getBoundingClientRect(), read page.getBoundingClientRect() once, compute left as target.left - page.left - 9, top as target.top - page.top - 8, width as target.width + 18, height as target.height + 16, clamp left to 8 and width to the page clientWidth minus left minus 8, then assign annotation.style.transform and annotation.style.width and annotation.style.height. Set the SVG viewBox, preserveAspectRatio, path, and circles in the HTML; JS only positions the overlay. The path must use pathLength=\"240\" and style stroke-dasharray:6 4, stroke-dashoffset:240 when armed.\n\nDefine syncStrike once: read usefulWord.getBoundingClientRect(), read h1.getBoundingClientRect(), cache strikeLeft, strikeTop, and strikeWidth, then set strike.style.transformOrigin=\"left center\". The first measurement is outside rAF and is repeated only on resize and ResizeObserver delivery. The rAF loop never reads layout.\n\nThe rAF callback is function frame(now) { if (document.hidden) { rafId = 0; return; } window.__DEMO__.frames += 1; const phase = ((now - startedAt) % 6000) / 6000; const progress = phase < 0.0533333333 ? phase / 0.0533333333 : 1; strike.style.setProperty(\"--strike-progress\", String(progress)); rafId = requestAnimationFrame(frame); }. It writes only the cached strike custom property and increments telemetry. Do not toggle classes or measure anything in this callback. Start it only after motion-ready and only when !prefersReduce. On visibilitychange, cancel the current rAF when hidden and restart it with a fresh startedAt when visible. Push each initialized effect name exactly once into effects as \"box-draw\", \"corner-dots\", and \"red-correction\".\n\nAfter first paint, set ready:true, begin the rAF loop, and disconnect the 1200ms fallback. All event handlers use passive listeners where applicable. The script must contain no console calls, no throw statements, no async dependencies, and no external module imports.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["At 1440x900, the document has one nav, one header, one main, one footer, exactly one h1, and no horizontal or vertical page overflow; at 390x844 it has no horizontal overflow and body vertical scrolling is enabled.","The page contains one visible Marginalia navigation, the exact h1 text “Keep the useful part.”, the four workflow rows, the exact evidence quote, and the exact footer copy; every visible button or link has a non-empty accessible name.","The workflow contains one annotation SVG with viewBox=\"0 0 200 95\", preserveAspectRatio=\"none\", path data beginning M5 5 L195 5 L195 88 L5 72 Z, stroke-dasharray 6 4, eight annotation circles, and a group rotation of -0.5deg.","window.__DEMO__ exists with name \"bracket\", ready becomes true after first paint, frames increases during a 1.2 second desktop run, effects includes box-draw, corner-dots, and red-correction, and errors remains empty during normal interaction.","With prefers-reduced-motion:reduce, reveal text, annotation strokes, dots, and the red correction are visible and static, the rAF loop does not run, and the page remains readable at 390x844, 768x1024, 1440x900, and 1920x1080.","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"],"usage":{"build_tokens":58247,"build_prompt_tokens":6702,"build_completion_tokens":51545,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":679679,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":32956,"total_tokens":91203},"verification":{"passed":true,"passed_checks":18,"total_checks":18,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"35570 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"55 frames, effects: box-draw, corner-dots, red-correction"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"75 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Keep the useful part.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Marginalia — Keep the useful part"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=671 pageErrors=0"}]},"crosscheck":{"slug":"bracket","reviewer_model":"moonshotai/Kimi-K2.7-Code","reviewer_platform":"cmdcode","verdict":"partial","confidence":"high","aesthetic_score":6,"requirements_met":["Single-file document structure with one nav, header, main, footer and single h1","Exact title, lang, charset, and viewport declaration","Five-layer backdrop, guide lines, cross-paper texture, and margin notes","Navigation copy and 1fr auto 1fr layout","Hero eyebrow, h1, span#useful-word, and two-column hero grid copy","Workflow row copy and annotation of row 03 only","Evidence band testimonial and index copy","Footer copy, layout, and visible border","IBM Plex Mono with preconnect and system fallback","Visible #C2452D 2px/3px focus-visible outline","Responsive breakpoints and no horizontal overflow","window.__DEMO__ telemetry, error handlers, and live rAF loop","prefers-reduced-motion static fallback"],"requirements_missed":["Bracket annotation visible in final render","Correct annotation dot geometry (eight total: four corner + four midpoint)","Red strikethrough positioned 4px below h1 top","Red-line animation driven solely by rAF --strike-progress",".sr-only utility layer in z-index contract"],"issues":[{"issue":"Row 03 bracket annotation is not visible in the rendered screenshot","severity":"high","evidence":"No bracket overlay appears around the heading 'Leave a trail your future self can follow.', which is the only element the brief requires to be annotated."},{"issue":"Annotation SVG contains duplicate midpoint circles","severity":"medium","evidence":"The SVG defines four corner circles plus eight r=3 midpoint-positioned circles, with the last four duplicating the coordinates of the previous four, instead of the required eight total circles."},{"issue":"Red strikethrough is vertically misplaced","severity":"medium","evidence":"syncStrike sets strikeTop = usefulBounds.height * 0.52 instead of 4 px below the h1's top."},{"issue":"Red-line animation is driven by CSS keyframes rather than the rAF custom property","severity":"medium","evidence":".motion-ready .strike-line { animation: correction-strike ... } overrides the transform: scaleX(var(--strike-progress)) that the rAF loop writes every frame."},{"issue":"Missing .sr-only utility layer from the z-index contract","severity":"low","evidence":"No .sr-only rule is present in the stylesheet."}],"summary":"Most of the requested document-like composition is present, but the rendered page omits the row-03 bracket annotation, duplicates annotation dots, and misplaces the red strikethrough. These deviations make the output look like an incomplete implementation rather than a faithful build.","usage":{"input":17757,"output":15199,"total":32956,"reasoning":0,"cached":0},"latency_ms":170502}},{"slug":"cadence","title":"Cadence","category":"Landing Page","effect":"A metronome pendulum sweeping a tick-marked arc while a hairline beat grid pulses and concentric rings burst from the pivot on every beat","platform":"cmdcode","platform_label":"cmd-code","model":"xiaomi/mimo-v2.6-pro","model_label":"Xiaomi MiMo v2.6 Pro (xiaomi/mimo-v2.6-pro)","demo":"/demos/cadence/index.html","doc":"/prompts/cadence.md","prompt_text":"## A. WHAT IS BEING BUILT\n\nOne complete, valid index.html — single-viewport cinematic landing page for **Cadence**, a live-music tech company locking stage lighting, LED walls and cameras to a band's live tempo. Document title: \"Cadence — Every Surface on the Beat\". html lang=\"en\". ONE file: plain HTML + one style block + one vanilla script at end of body. No build step, no framework, no bundler.\n\n**Viewport rule:** at width >= 901px the page is exactly one frame: html, body { height:100%; overflow:hidden } and .page { height:100dvh; overflow:hidden }. NO SCROLL on desktop. At width <= 900px it may scroll vertically. No horizontal overflow at 390, 768, 1440, 1920.\n\n**Do NOT build:** no video, mp4/webm/gif, remote images, base64 photo blobs, third-party CDN URLs, WebGL, Three.js, GSAP, Lottie, jQuery, Tailwind, React, audio, forms, pricing, testimonials, blog, cookie banner, scroll indicator, modals, waveform galleries, nothing beyond header / hero / meter / stats footer, desktop hamburger (burger <= 900px only). Google Fonts is the ONLY network request.\n\n**The single visual idea:** a black rehearsal room, one machine — a room-sized metronome. A weighted rod sweeps a smooth arc from a pivot near the top of frame, ticking at each extreme. Behind it a hairline beat grid pulses per beat and concentric rings emit from the pivot (heavier downbeat). Bottom-right: a monospace BPM readout (96.0 BPM, 4/4) with a four-bar beat meter. Nearly monochrome — graphite, bone white — one warm accent (#ff8a3d) for the beat only: rings, measure lines, active meter bar, pivot core.\n\n**Beat clock (one clock, all of it obeys):** BPM = 96. BEAT = 60/96 = 0.625 s. BAR (4/4) = 2.5 s. Pendulum sweep T = 2 beats = 1.25 s. Beats fire at the swing extremes (tick at the ends); the downbeat is the right extreme.\n\n---\n\n## B. THE BACKDROP — the metronome stage (counts, gradient stops, blur radii, drift periods)\n\nOne fixed .backdrop (inset:0; overflow:hidden; pointer-events:none; z-index:0) holds:\n\n1. **.bg** (z 0): radial-gradient(circle at 50% 7.5%, rgba(255,138,61,0.13) 0%, rgba(255,138,61,0.05) 26%, transparent 52%); linear-gradient(180deg, #0a0a0b 0%, #101013 44%, #0c0c0e 74%, #08080a 100%).\n2. **.glow** (z 1) pivot halo: left:50%; top:7.5%; 720x720px; translate(-50%,-50%); radial-gradient(circle, rgba(255,170,96,0.22) 0%, rgba(255,138,61,0.09) 34%, transparent 68%); filter:blur(44px); opacity:0.8; animation glow-breathe 11s ease-in-out infinite (opacity 0.62 <-> 0.92, scale 1 <-> 1.06).\n3. **canvas#stage** (z 2) fixed inset:0; width:100%; height:100%. Repainted EVERY frame, never static; draws back to front: beat grid, pivot rings, pendulum.\n4. **.scrim** (z 3) fixed inset:0: linear-gradient(180deg, rgba(8,8,10,0.58) 0%, rgba(8,8,10,0.10) 26%, rgba(8,8,10,0.52) 64%, rgba(8,8,10,0.90) 100%) — also fades the grid hairlines at top and bottom.\n5. **.grain** (z 30) fixed inset:-50%; 200%x200%; opacity:0.05; SVG feTurbulence data-URI (fractalNoise, baseFrequency 0.8, numOctaves 2, stitchTiles, then saturate 0), tile 160px; animation grain 0.9s steps(4) infinite, translating among (0,0) (-2%,1%) (1%,-2%) (-1%,2%).\n\n**Geometry (canvas 2D, no libraries).** Cached { w, h } read once per resize. Pivot P = (w * 0.5, h * 0.075; h * 0.06 at <=900). Rod L = h * 0.52 (>=1600) / h * 0.50 (1280-1599) / h * 0.48 (901-1279) / h * 0.44 (max-height 850) / h * 0.38 (max-height 720) / min(h * 0.34, 300px) (<=900) / min(h * 0.28, 220px) (<=560). Weight 44x64px default; 48x70 (>=1600), 52x76 (>=1920), 38x54 (max-height 850), 32x46 (max-height 720), 34x50 (<=900), 28x40 (<=560). Amplitude A = 26deg (24deg <=900, 22deg at max-height 720). theta(t) = A * cos(2 * PI * t / 1.25); at t = 0.625 * k the angle is exactly +/- A — the beat tick.\n\n**1. Beat grid:** hairlines x_i = i * PITCH, i = 0..ceil(w/PITCH). PITCH = 30 (>=1600) / 26 (1280-1599) / 22 (901-1279) / 18 (<=900) / 14 (<=560) px — 56 lines at 1440x900. Weak lines (i % 8 !== 0): rgba(226,226,222, a_i), 1px. Measure lines (i % 8 === 0; 7 at 1440x900): rgba(255,138,61, a_i * 0.85), 1.4px. Full height (.scrim fades the ends). Alpha:\n  - p = (t / 0.625) mod 1; beatEnv = exp(-5.2 * p) (1.000 at the tick, 0.006 at the next).\n  - shimmer = 0.05 * (0.5 + 0.5 * sin(i * 0.45 - t * 1.7)).\n  - a_i = 0.045 + 0.22 * beatEnv + shimmer + (i % 8 === 0 ? 0.10 : 0).\n  Batch into 6 alpha buckets, ONE beginPath() per bucket — 6 draw calls for 56 lines.\n\n**2. Pivot rings:** 4 pre-allocated slots (NO allocation in the loop), slot beatIndex % 4; on each beat boundary store born = t, down = (beatIndex % 4 === 0). Age a = t - born <= 1.6s: r = 14 + 300 * a px (494px at death); stroke rgba(255,138,61, 0.30 * pow(1 - a / 1.6, 1.7) * (down ? 1.4 : 1)); lineWidth = down ? 2 : 1.2. Housing circle at P: r = 22px, rgba(226,226,222,0.14), 1px.\n\n**3. Pendulum:** pivot cap: filled r=7px #0a0a0b, 1.5px rgba(226,226,222,0.55) ring, accent core dot r=2.5px rgba(255,138,61,0.95). Rod: 3px line (2px <=900) P -> tip at L, rgba(232,232,228,0.92), round caps. Weight: rounded rect centred at 0.70 L, rotated with the rod — fill #d9d9d5, 1px rgba(255,138,61,0.55) stroke, radius 5px, one 1px rgba(10,10,11,0.5) groove. Tip cap r=5px rgba(255,138,61,0.9). Trail: 4 ghost rods at t - 0.024 / 0.048 / 0.072 / 0.096 s, 2px, alphas 0.07 / 0.05 / 0.035 / 0.02. Scale arc: 11 ticks -A..+A (step 5.2deg), radius L*1.02 -> L*1.06, 1px rgba(226,226,222,0.22), extremes rgba(255,138,61,0.55); hairline arc at L*1.02 across 52deg, rgba(226,226,222,0.12).\n\n**Resolution trick:** dpr = Math.min(window.devicePixelRatio || 1, 2); canvas.width = cssW * dpr; canvas.height = cssH * dpr; ctx.setTransform(dpr, 0, 0, dpr, 0, 0). getBoundingClientRect only inside resizeCanvas(). Resize: one boolean resizeQueued + requestAnimationFrame.\n\n**Per-frame cost budget (1440x900):** 1 clearRect + 6 batched grid paths (56 strokes) + 3 ring arcs + 5 rods + 11 ticks + 1 arc + 5 fills = 12 draw calls. ZERO ctx.filter / ctx.shadowBlur anywhere, ZERO DOM writes. Under 2 ms/frame; >= 20 frames per 1.2s headless. document.hidden: keep loop and counter, skip the paint.\n\n**Drift periods (non-harmonic on purpose):** pendulum 1.25s, beat 0.625s, bar 2.5s, glow-breathe 11s, grain 0.9s.\n\n---\n\n## C. THE LAYER STACK (back -> front, z-index)\n\n1. html, body — #0a0a0b, forced twice (Failure Guards).\n2. .backdrop — z 0, fixed inset:0, pointer-events:none, overflow:hidden. Inside: .bg 0, .glow 1, canvas#stage 2, .scrim 3, .grain 30.\n3. .page — z 40; position:relative; display:grid; grid-template-rows:auto minmax(0,1fr) auto; min-height:100dvh (height:100dvh inside the desktop lock).\n4. main.hero — z 50.\n5. header.site-header, footer.stats-footer — z 60.\n6. .menu (mobile, open) — fixed inset:0, z 80.\n\n---\n\n## D. TOKEN TABLE (defaults at 1440x900)\n\n```\n--bg:            #0a0a0b\n--bg-2:          #101013\n--text:          #f2f2f0\n--text-soft:     #d6d6d2\n--muted:         #9a9a96\n--dim:           #6a6a66\n--accent:        #ff8a3d\n--accent-hot:    #ffc38a\n--accent-soft:   rgba(255,138,61,0.55)\n--accent-line:   rgba(255,138,61,0.32)\n--grid-line:     rgba(226,226,222,0.22)\n--border:        rgba(242,242,240,0.14)\n--border-soft:   rgba(242,242,240,0.08)\n--focus:         #ffc38a\n--sans:          \"Inter\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif\n--mono:          \"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace\n--logo:          16px\n--logo-track:    0.20em\n--nav:           13.5px\n--nav-h:         38px\n--btn:           13.5px\n--btn-h:         44px\n--chip-h:        30px\n--eyebrow:       11.5px\n--h1:            76px\n--lead:          18px\n--bpm:           64px\n--bpm-unit:      13px\n--meter-label:   11px\n--meter-h:       26px\n--stat-val:      26px\n--stat-label:    12px\n--header-y:      26px\n--header-x:      44px\n--hero-x:        44px\n--hero-bottom:   56px\n--copy-max:      700px\n--meter-w:       300px\n--radius-pill:   999px\n--radius-sm:     5px\n--focus-w:       2px\n--focus-offset:  3px\n--grain-op:      0.05\n--glow-blur:     44px\n--ease-out:      cubic-bezier(0.16,1,0.3,1)\n--enter-dur:     0.95s\n```\n\nFonts: Google Fonts ONLY — Inter 400;500;600, JetBrains Mono 400;500 — one preconnect to https://fonts.gstatic.com (crossorigin) + one stylesheet link with display=swap, fallbacks as above. BPM digits use font-variant-numeric: tabular-nums.\n\n---\n\n## E. THE COMPOSITION — exact build order\n\n**.page** holds <header class=\"site-header\">, <main class=\"hero\">, <footer class=\"stats-footer\"> in grid rows auto / 1fr / auto.\n\n**HEADER** — display:flex; align-items:center; justify-content:space-between; gap:24px; padding:var(--header-y) var(--header-x).\n\n- a.brand (href \"#\", accessible name \"Cadence home\"): inline SVG mark 22x22 — metronome glyph <path d=\"M11 2.5 L19 19.5 L3 19.5 Z\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linejoin=\"round\"/> plus rod <path d=\"M11 17 L15.4 7.2\" stroke=\"#ff8a3d\" stroke-width=\"1.6\" stroke-linecap=\"round\"/> — then \"CADENCE\" (var(--sans), var(--logo), weight 600, letter-spacing var(--logo-track), color var(--text)) and a 6px square .beat-dot (background var(--accent); radius 1px; margin-left:10px), opacity written per beat by JS (1 -> 0.25).\n- nav, exactly 4 links — \"Engine\", \"Rigs\", \"Pricing\", \"Docs\" — href \"#\", var(--sans), var(--nav), weight 500, color var(--muted), flex; gap:30px; height:var(--nav-h). Hover: color var(--text), 0.18s.\n- a.cta (href \"#\", \"Get the SDK\"): height var(--btn-h); padding 0 20px; inline-flex; radius var(--radius-pill); var(--btn) weight 600; color var(--accent-hot); border 1px solid var(--accent-line); background linear-gradient(180deg, rgba(255,138,61,0.16) 0%, rgba(255,138,61,0.05) 100%).\n\n**MAIN (hero, bottom-anchored)** — display:flex; align-items:flex-end; justify-content:space-between; gap:48px; padding:0 var(--hero-x) var(--hero-bottom). Left .hero-copy (max-width:var(--copy-max); flex column; align-items:flex-start); right aside.meter (width:var(--meter-w); flex:0 0 auto). Copy hugs the LEFT padding.\n\n1. .chip (a <p>, not interactive): height var(--chip-h); padding 0 14px; inline-flex; align-items:center; radius var(--radius-pill); border 1px solid var(--border); background rgba(255,138,61,0.06); var(--mono) at var(--eyebrow), letter-spacing 0.18em, uppercase, color var(--accent). Copy: \"LIVE TEMPO SYNC ENGINE\".\n2. h1 — EXACTLY ONE <h1>, var(--sans), weight 600, var(--h1), line-height 0.98, letter-spacing -0.045em, color var(--text), margin: 20px 0 0. Two masked lines (Motion): \"Every surface\" / \"on the beat.\" with <em>beat</em> in var(--mono), font-style normal, weight 500, font-size 0.94em, letter-spacing -0.03em, color var(--accent-hot).\n3. .lead — var(--sans), var(--lead), line-height 1.55, letter-spacing -0.012em, color var(--text-soft), max-width 560px, margin: 22px 0 0. Copy: \"Cadence locks lighting, LED walls and cameras to the band's live tempo — one shared clock, drifting less than a quarter of a millisecond across a three-hour set.\"\n4. .actions — flex; gap:12px; flex-wrap:wrap; margin-top:30px. .btn--primary \"#\" \"Book a demo\"; .btn--ghost \"#\" \"Explore the engine\".\n\n**aside.meter (BPM instrument panel, bottom-right)** — flex column; gap:14px; padding:20px 22px; border:1px solid var(--border-soft); radius:var(--radius-sm); background rgba(10,10,11,0.42).\n\n- .meter-top — flex; justify-content:space-between; align-items:baseline: <span class=\"meter-label\">TEMPO</span> left, \"4 / 4\" right (var(--mono), var(--meter-label), letter-spacing 0.22em, color var(--dim)).\n- .bpm-line — flex; align-items:baseline; gap:8px: <span class=\"bpm-val\">96.0</span> (var(--mono), weight 500, var(--bpm), letter-spacing -0.04em, tabular-nums, color var(--text)) + <span class=\"bpm-unit\">BPM</span> (var(--mono), var(--bpm-unit), letter-spacing 0.16em, color var(--dim)).\n- .beat-row — flex; align-items:flex-end; gap:8px. Exactly 4 <span class=\"beat-seg\"> bars: flex:1; height:var(--meter-h); radius:2px; background var(--accent-line); transform-origin:50% 100%; transform:scaleY(0.22); opacity:0.28; transition transform/opacity 220ms var(--ease-out). .on: background var(--accent); transform:scaleY(1); opacity:1. Then <span class=\"beat-count\">BEAT <span class=\"beat-num\">1</span> / 4</span> (var(--mono), var(--meter-label), letter-spacing 0.16em, color var(--dim), margin-left:6px).\n\n**FOOTER (three-item stats)** — display:grid; grid-template-columns:repeat(3, 1fr); padding: 22px var(--header-x) 28px; border-top:1px solid var(--border-soft). Exactly one <ul>, three <li>. Each li: display:grid; gap:6px; padding:0 30px; first-child padding-left:0; last-child padding-right:0; li + li border-left:1px solid var(--border-soft); 2nd centred, 3rd right. Each li: <strong class=\"stat-val\"> (var(--mono), var(--stat-val), weight 500, letter-spacing -0.02em, color var(--text)) + <span class=\"stat-label\"> (var(--mono), var(--stat-label), letter-spacing 0.14em, uppercase, color var(--dim)). Pairs: \"0.24 ms\" / \"peak drift over 3 h\"; \"64\" / \"DMX universes driven\"; \"3 h 12 m\" / \"longest locked set\".\n\n---\n\n## F. THE UI LANGUAGE\n\n- **.btn base:** inline-flex; centred; height var(--btn-h); padding 0 22px; radius var(--radius-pill); var(--sans) var(--btn) weight 600; letter-spacing 0.01em; no underline; relative; overflow:hidden; transition transform/border-color/background/color/box-shadow 220ms var(--ease-out).\n- **.btn--primary:** color #1c0d02; border 1px solid rgba(255,195,138,0.65); background linear-gradient(180deg, #ffc38a 0%, #ff8a3d 52%, #d96a1e 100%); box-shadow 0 14px 44px rgba(255,138,61,0.20), inset 0 1px 0 rgba(255,255,255,0.55). Hover: background linear-gradient(180deg, #ffd7ae 0%, #ff9a52 48%, #e67526 100%), translateY(-1px), box-shadow 0 18px 54px rgba(255,138,61,0.30).\n- **.btn--ghost:** color var(--text-soft); border 1px solid var(--border); background rgba(242,242,240,0.035). Hover: color var(--text), border-color var(--accent-line), background rgba(255,138,61,0.08), translateY(-1px).\n- **Tick glint** on .btn and .cta: content \"\"; absolute; inset:-1px; background linear-gradient(112deg, transparent 0% 36%, rgba(255,195,138,0.20) 50%, transparent 64% 100%); translateX(-140%); transition 680ms var(--ease-out); hover -> translateX(140%).\n- **.cta** = .btn base at padding 0 20px with section E's hairline look; hover: border-color var(--accent-soft), background linear-gradient(180deg, rgba(255,138,61,0.24) 0%, rgba(255,138,61,0.08) 100%), box-shadow 0 0 20px rgba(255,138,61,0.18).\n- **Nav links** plain, no pills/underline; hover var(--text). **.chip** and **aside.meter** never react — readouts.\n- **Focus:** a/button :focus-visible { outline: var(--focus-w) solid var(--focus); outline-offset: var(--focus-offset); border-radius: var(--radius-pill) }. ::selection { background: rgba(255,138,61,0.30); color: #140903 }. No default blue ring, no underline anywhere.\n\n---\n\n## G. MOTION — entrance stagger and named keyframes\n\nEntrance animations: duration var(--enter-dur); fill-mode both; timing var(--ease-out); delay var(--d, 0.08s); each element .appear + one modifier + inline --d (resting state = final state).\n\nStagger table (element -> modifier = class .appear--X -> --d):\n```\nbrand          fade    0.08s\nnav 1          fade    0.16s\nnav 2          fade    0.24s\nnav 3          fade    0.32s\nnav 4          fade    0.40s\nheader cta     chip    0.48s\neyebrow chip   pop     0.60s\nh1 line 1      mask    0.74s\nh1 line 2      mask    0.90s\nlead           soft    1.08s\nprimary btn    chip    1.22s\nghost btn      chip    1.30s\nmeter panel    meter   1.42s   (duration 1.1s)\nstat 1         stat    1.54s\nstat 2         stat    1.62s\nstat 3         stat    1.70s\n```\n\nEntrance keyframes (end at opacity 1 / identity):\n```\nin-fade   opacity 0 -> 1\nin-soft   opacity 0, translateY(14px), blur(4px) -> 1, none, blur(0)\nin-mask   opacity 0, translateY(108%) -> 1, none\nin-chip   opacity 0, translateY(10px) scale(0.96) -> 1, none\nin-pop    0% 0 scale(0.94) / 68% 1 scale(1.03) / 100% 1, none\nin-meter  opacity 0, translateY(16px) scaleY(0.9), origin 50% 100% -> 1, none\nin-stat   opacity 0, translateY(12px) -> 1, none\n```\n\nContinuous keyframes: glow-breathe 11s ease-in-out infinite — 0%,100% { opacity: 0.62; transform: translate(-50%,-50%) scale(1) }, 50% { opacity: 0.92; transform: translate(-50%,-50%) scale(1.06) }; grain 0.9s steps(4) infinite (offsets per B.5).\n\nH1 masking: .line { display:block; overflow:hidden; padding: 0.06em 0.06em 0.12em } > .line-inner { display:inline-block } carrying .appear--mask — type rising from a hard edge.\n\nMusical periods (pendulum 1.25s, grid 0.625s, rings 1.6s, meter 220ms) obey the ONE JS clock of section J — never CSS — so nothing drifts; glow-breathe (11s) and grain (0.9s) alone are CSS, non-harmonic.\n\n---\n\n## H. RESPONSIVE TABLE — exact token overrides per breakpoint\n\n```\n>= 1600px:\n  --h1: 90px  --bpm: 74px  --lead: 19px  --logo: 17px  --nav: 14px  --nav-h: 40px\n  --btn: 14px  --btn-h: 46px  --stat-val: 30px  --meter-w: 340px  --meter-h: 30px\n  --header-y: 30px  --header-x: 56px  --hero-x: 56px  --hero-bottom: 64px  --copy-max: 800px\n\n>= 1920px:\n  --h1: 104px  --bpm: 84px  --lead: 20px  --stat-val: 34px  --stat-label: 12.5px\n  --header-x: 72px  --hero-x: 72px  --hero-bottom: 72px  --copy-max: 880px  --meter-w: 380px\n\n1280px – 1599px:\n  --h1: 72px  --bpm: 60px  --copy-max: 660px  --meter-w: 280px\n\n901px – 1279px:\n  --h1: 56px  --bpm: 52px  --lead: 16px  --stat-val: 24px  --nav: 12.5px  --btn: 12.5px\n  --header-x: 30px  --hero-x: 30px  --hero-bottom: 44px  --copy-max: 560px  --meter-w: 250px\n\n>= 901px AND max-height 850px:\n  --h1: 54px  --bpm: 48px  --lead: 16px  --stat-val: 22px  --chip-h: 28px  --meter-h: 22px\n  --header-y: 18px  --hero-bottom: 36px  (h1 margin-top 14px, lead 16px, actions 22px)\n\n>= 901px AND max-height 720px:\n  --h1: 44px  --bpm: 42px  --lead: 15px  --eyebrow: 11px  --stat-val: 20px  --stat-label: 11px\n  --chip-h: 26px  --btn-h: 38px  --meter-h: 18px  --header-y: 14px  --hero-bottom: 26px\n  (footer padding 14px top, 18px bottom)\n\n>= 901px (desktop lock, all heights):\n  html, body { height:100%; overflow:hidden }  .page { height:100dvh; overflow:hidden }\n\n<= 900px:\n  no viewport lock; vertical scroll; nav + CTA move into a burger menu:\n  - header keeps .brand + <button class=\"burger\" aria-label=\"Open navigation\" aria-expanded=\"false\">, three 18x1.5px bars, var(--text), gap 5px\n  - .menu { position:fixed; inset:0; z-index:80; background: rgba(10,10,11,0.94); backdrop-filter: blur(14px); flex 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 }\n  - menu: the 4 nav links at 22px + the .cta chip; Escape, link click, resize > 900px close it\n  - main.hero stacks: flex-direction:column; gap:32px; aside.meter { width:100% }\n  --h1: 46px  --bpm: 54px  --lead: 16px  --stat-val: 22px  --meter-w: 100%  --meter-h: 24px\n  --header-x: 24px  --hero-x: 24px  --hero-bottom: 40px  --copy-max: 100%\n\n<= 560px:\n  --h1: 36px  --bpm: 46px  --lead: 15px  --eyebrow: 11px  --stat-val: 19px  --stat-label: 10.5px\n  --header-x: 20px  --hero-x: 20px  --hero-bottom: 32px  --meter-h: 20px\n  stats footer stacks: grid-template-columns:1fr; li { padding: 12px 0; text-align:left !important }; li + li { border-left:0; border-top:1px solid var(--border-soft) }\n  .actions { width:100% }  .btn { flex: 1 1 100% }\n  (canvas L, PITCH, weight and A per section B)\n```\n\n---\n\n## I. FAILURE GUARDS — the page is never blank, never broken\n\n1. **Black-first:** the FIRST CSS rule is html, body { background:#0a0a0b !important; color:#f2f2f0 }; body carries style=\"background:#0a0a0b;color:#f2f2f0\"; a later rule restates html, body { background:#0a0a0b; background: var(--bg, #0a0a0b) }.\n2. **Resting opacity is 1:** every .appear element's un-animated style is its final state (opacity 1, no transform, no filter); keyframes own the FROM states; fill-mode:both holds them only during delay. Broken CSS or missing JS leaves the page complete.\n3. **Two-rAF entrance fallback:** after two nested requestAnimationFrame callbacks, per .appear run el.getAnimations({ subtree:false }); if none reports playState \"running\"/\"finished\", add .is-in to ALL .appear. No getAnimations: sweep unconditionally after the two rAFs. .is-in forces animation:none !important; opacity:1 !important; transform:none !important; filter:none !important; clip-path:none !important.\n4. **Canvas fallback:** missing canvas or getContext(\"2d\") -> .no-canvas on <html>, push \"canvas-unavailable\" into errors, static stage on .backdrop::after: repeating-linear-gradient(90deg, var(--grid-line) 0 1px, transparent 1px 26px) grid; two 1px rgba(255,138,61,0.30) circles (border-radius:50%, 220px and 380px, centred at 50% 7.5%) as frozen rings; one static 3px bone-white rod rotated 14deg from P. Type, meter, footer stay visible.\n5. **Reduced motion:** @media (prefers-reduced-motion: reduce): *, *::before, *::after { transition:none !important; animation:none !important }, .appear/.is-in forced to opacity:1 !important; transform:none !important; filter:none !important; clip-path:none !important. Canvas paints ONE frozen frame at t = 0.22s (theta = 14.3deg, beatEnv 0.32, static ring arcs aged 0.22 / 0.85 / 1.48s, meter bar 1 .on, 2-4 off, .beat-num \"1\"), then no further rAF: ready = true, frames = 1. .glow rests opacity 0.8 scale 1; .grain display:none. Nothing moves.\n6. **Contrast floor:** h1/bpm-val #f2f2f0 on #0a0a0b, lead #d6d6d2 over the scrim, labels #6a6a66 11px uppercase above 4.5:1; text opacity never below 0.86.\n7. **Zero console output:** no console calls, no throws, every DOM and canvas call guarded.\n\n---\n\n## J. THE EXACT JS (only this, vanilla, one IIFE at end of body)\n\n1. **Telemetry first:** window.__DEMO__ = { name: 'cadence', ready: false, frames: 0, effects: [], errors: [] }; push once, in order: 'metronome-arc', 'beat-grid', 'pivot-rings', 'beat-meter', 'film-grain'; then window.addEventListener('error', function (e) { window.__DEMO__.errors.push(String(e.message || e)); }).\n2. **Clock constants:** BPM = 96, BEAT = 0.625, T_SWING = 1.25, A = 26 (deg), RING_R0 = 14, RING_V = 300, RING_LIFE = 1.6. theta(t), beatEnv, a_i exactly as section B; beatIndex = Math.floor(t / BEAT); p = t / BEAT - beatIndex; beatInBar = beatIndex % 4.\n3. **Canvas stage loop:** cache documentElement, canvas#stage, the 2D context (null -> .no-canvas path, push \"canvas-unavailable\"), cached { w, h } + geometry from resizeCanvas(), and 4 pre-allocated ring slots { born, down } (born = -10). drawStage(t): clearRect; beat grid via B.1 batching; on a NEW beatIndex (vs lastBeat) write slot lastBeat % 4 = { born: t, down: beatInBar === 0 } and move the meter DOM (4 .on moves, .beat-num textContent = String(beatInBar + 1), .beat-dot style.opacity = '1'); rings by B.2; rods, weight, caps, 11-tick arc by B.3. Each rAF tick increments window.__DEMO__.frames by exactly 1 then paints; FIRST completed paint sets ready = true. NEVER ctx.filter, ctx.shadowBlur, getBoundingClientRect in the loop; zero DOM reads beyond cached handles. document.hidden: skip paint, keep loop and counter.\n4. **Resize:** resizeCanvas() reads canvas.getBoundingClientRect() ONCE (the only permitted call), recomputes { w, h } + geometry, dpr = Math.min(window.devicePixelRatio || 1, 2), canvas.width = w * dpr, canvas.height = h * dpr, ctx.setTransform(dpr, 0, 0, dpr, 0, 0), repaints. One boolean resizeQueued + requestAnimationFrame — no timers.\n5. **Entrance fallback:** Guard 3's two nested rAFs + getAnimations .is-in sweep.\n6. **Mobile menu (<= 900px):** burger toggles .menu.open + aria-expanded; Escape, link click, resize > 900px close.\n7. **prefers-reduced-motion:** matchMedia('(prefers-reduced-motion: reduce)') once; if matched: drawStage(0.22) once (Guard 5), meter beat 1, ready on that paint, frames = 1, no further rAF.\n\nNo other JS. No libraries. No timers. No console calls.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the document contains exactly one h1 built from exactly two .line > .line-inner masked lines with the visible copy 'Every surface' and 'on the beat.', semantic header, nav, main and footer landmarks, exactly 4 nav links, and exactly 3 footer stat items","canvas#stage paints non-blank pixel content that changes across frames at 1440x900 (the pendulum sweeps), and the script never calls ctx.filter, ctx.shadowBlur or getBoundingClientRect inside the animation loop","window.__DEMO__ is initialized with name 'cadence', ready becomes true after the first painted frame, frames increments once per requestAnimationFrame tick (>= 20 per 1.2s at 1440x900), effects contains metronome-arc, beat-grid, pivot-rings, beat-meter and film-grain, and errors stays empty in the happy path","the meter shows '96.0' and 'BPM' in tabular monospace with exactly 4 .beat-seg bars whose .on class rotates once per 625ms and a .beat-num cycling 1-4, matching the 1.25s pendulum sweep drawn on the canvas","no horizontal overflow at 390px, 768px, 1440px or 1920px; at >= 901px body has overflow:hidden and .page height is 100dvh; a prefers-reduced-motion: reduce block paints exactly one static frame and leaves every text element at opacity 1","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"],"usage":{"build_tokens":31903,"build_prompt_tokens":26,"build_completion_tokens":31877,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":10,"build_latency_ms":344330,"wasted_tokens":234038,"repair_tokens":0,"crosscheck_tokens":24202,"total_tokens":56105},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"36424 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"24 frames, effects: metronome-arc, beat-grid, pivot-rings, beat-meter, film-grain"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"37 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 21.41, stdev 64.65, coverage 0.1006"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 3286108806 -> 2911043296"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Every surface\non the beat.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Cadence — Every Surface on the Beat"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=403 pageErrors=0"}]},"crosscheck":{"slug":"cadence","reviewer_model":"kimi-k2.8-preview","reviewer_platform":"workbuddy","verdict":"confirmed","confidence":"high","aesthetic_score":8,"requirements_met":["Composition matches brief: header / bottom-anchored hero / right-side meter / 3-stat footer at one frame","Copy is exact: chip, h1 two masked lines with mono accent 'beat', lead paragraph, stat pairs all verbatim","Beat clock constants correct (96 BPM, 0.625s beat, 1.25s swing, theta = A*cos(2*pi*t/1.25), downbeat at right extreme)","Canvas geometry tiers, pitch, weight sizes and amplitude breakpoints implemented per section B","Grid alpha formula, 6-bucket batching, measure-line accent override present","4 pre-allocated ring slots, born/down on new beatIndex, r = 14 + 300a, pow(1.7) decay, downbeat 1.4x heavier","Pendulum details present: ghost trails at 4 offsets, 3px rod, rotated rounded weight with groove, pivot cap layers, accent tip, 11-tick scale arc with accent extremes","Token table, breakpoint overrides, and desktop viewport lock implemented as specified","Entrance stagger table with per-element --d delays and all seven named keyframes; resting state = final state","Failure guards: black-first CSS, two-rAF getAnimations fallback, no-canvas static stage, reduced-motion frozen frame at t=0.22","dpr cap of 2, single getBoundingClientRect in resize, rAF-queued resize, no ctx.filter/shadowBlur, no in-loop allocation","Telemetry hook exact shape with five effects pushed in order"],"requirements_missed":["Beat-dot decay is done via CSS animation classes (is-a/is-b) instead of JS writing opacity 1 -> 0.25 each beat as briefed","Mobile menu wraps the 4 links in a nested <nav> rather than direct flex children, a structural deviation from the specified .menu layout"],"issues":[{"issue":"Pivot sits inside the header band and the housing circle visually collides with the 'Rigs' nav link","severity":"low","evidence":"P = (w*0.5, h*0.075) is mandated, so the r=22 housing circle and rings render behind the centered nav at 1024x640; nav text stays legible on top, so this is spec-conformant but compositionally crowded in the screenshot"},{"issue":"Beat-dot opacity decay driven by CSS keyframes, not the JS opacity writes the brief specifies","severity":"low","evidence":"updateBeat sets style.opacity='1' then toggles .is-a/.is-b classes with dot-tick keyframes; brief: 'opacity written per beat by JS (1 -> 0.25)'"}],"summary":"The page renders as the brief's room-sized metronome scene with correct geometry, palette, copy and choreography, and nothing in the screenshot is broken, clipped or misplaced. Residual gaps are minor: the beat-dot decay is CSS-driven rather than JS-driven, and the mandated pivot position crowds the center nav link slightly.","usage":{"input":22660,"output":1542,"total":24202,"reasoning":805,"cached":0},"latency_ms":37499}},{"slug":"camber","title":"Camber","category":"Landing Page","effect":"Two colliding typographic planes sweep with a moving gilt specular while a restrained paper field rotates behind them.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/camber/index.html","doc":"/prompts/camber.md","prompt_text":"A. WHAT IS BEING BUILT\n\nBuild one self-contained, single-file landing page for Ledger & Vane, a private bank serving families with inherited holdings. The page is titled “Ledger & Vane — Private Banking for Inherited Holdings”. Set the HTML language to lang=\"en\", include a UTF-8 charset, and include the responsive viewport declaration width=device-width, initial-scale=1. The document must use semantic header, nav, main, section, and footer elements.\n\nAt viewport widths of 901px and wider, html, body, and the .page shell must be exactly 100dvh high, body must use overflow:hidden, and the layout must not scroll. At widths of 900px and narrower, use the normal document flow, retain overflow-x:hidden, and allow vertical scrolling. The desktop composition must remain visible inside 100dvh at 1440x900 and 1920x1080. Do not create a second page, route, modal, card carousel, dashboard, account login, onboarding sequence, or multi-step application.\n\nUse exactly one h1. Its accessible text must read “Holdings, held in balance.” The h1 must contain two span.line elements: the first reads “Holdings,” and the second reads “held in balance.” Do not create additional h1 elements in the header, footer, hidden templates, or fallback markup.\n\nDo NOT build:\n- A centred hero, centred navigation, centred balance column, or centred footer.\n- A conventional card grid, floating glass panels, gradient blobs, neon glow, glassmorphism, or a generic fintech dashboard.\n- A video, image, GIF, icon font, third-party icon package, Lottie animation, WebGL scene, framework, bundler, build tool, or external media.\n- More than the specified two typographic headline planes.\n- A second CTA, “Book a demo” control, hamburger menu, cookie panel, chat widget, newsletter form, account portal, or decorative control.\n- A horizontal page scrollbar, viewport-scaled body text, simulated cursor movement, scroll hijacking, pointer-following elements, or layout measurement inside the animation frame.\n- Any visible text not explicitly specified in section E.\n- Any animation that changes layout dimensions, top, left, right, bottom, width, height, filter, box-shadow, or colour during the rAF loop.\n\nThe only permitted network request is an optional Google Fonts stylesheet for Inter and Manrope. Use font-display:swap through the Google Fonts request. The CSS fallback stack must be Inter, “Helvetica Neue”, Helvetica, Arial, sans-serif for all text. If the request fails or is offline, the system fallback must render the complete page without reflow that causes horizontal overflow.\n\nB. THE BACKDROP\n\nCreate three backdrop layers inside a fixed .backdrop element with position:fixed, inset:0, z-index:0, pointer-events:none, and overflow:hidden.\n\nLayer one is a solid #EFEDE8 field. Layer two is one .orbit element, centred at 50% 50%, with width:82vmax, height:82vmax, border-radius:50%, opacity:0.08, mix-blend-mode:multiply, and transform-origin:50% 50%. Its background must be conic-gradient(from 22deg at 50% 50%, #0B0C0E 0deg, rgba(11,12,14,0) 46deg, #8A8F98 92deg, rgba(11,12,14,0) 164deg, #0B0C0E 224deg, rgba(11,12,14,0) 304deg, #0B0C0E 360deg). Animate orbit from rotate(0deg) to rotate(360deg) over 36000ms linear infinite. This is exactly one degree per second. Do not scale this layer.\n\nLayer three is one canvas#grain with CSS width:100%, height:100%, opacity:0.17, and mix-blend-mode:multiply. Generate a 96x96 procedural paper tile on an OffscreenCanvas when available; otherwise use a detached HTMLCanvasElement. Draw 1,152 fixed marks from a seeded PRNG: 72% use rgba(11,12,14,0.035), 20% use rgba(138,143,152,0.030), and 8% use rgba(11,12,14,0.018). Each mark is a 1px horizontal or vertical line with length from 1px to 3px. Do not use ctx.filter, ctx.shadowBlur, globalCompositeOperation changes, gradients, or image smoothing. The canvas export is 96x96. The displayed CSS layer stretches that 96x96 export across the viewport, which is the canvas resolution trick: one low-resolution source avoids a full-screen backing store while preserving a paper-grain texture.\n\nFor a second, wider grain response, create a second 96x96 tile with the same 1,152-mark recipe and a different deterministic seed. Maintain exactly two tile objects in JavaScript. The rAF loop alternates tiles every 1400ms, applies integer translation offsets from the fixed sequence 0,0; 7,5; 13,2; 19,8; 25,4; 31,10; 37,6; 43,11, then repeats. Reset the index to zero after 37,11. Draw the active tile twice per axis at offsets -96, 0 and 0, 96 so translation never reveals an uncovered edge. Clear with setTransform(1,0,0,1,0,0), fill the 192x192 working canvas with rgba(239,237,232,0), and draw four tile copies. Do not scale the context.\n\nThe canvas backing store is exactly 192x192, independent of viewport size. Do not multiply by devicePixelRatio. Set the canvas element’s intrinsic width and height attributes to 192. The paper background and all typography remain fully readable if canvas rendering is blocked.\n\nThe CSS .grain-sheen element is optional only if it uses opacity and transform. If present, make it position:absolute, inset:-12%, background:radial-gradient(ellipse at 30% 20%, rgba(255,255,255,0.34), rgba(255,255,255,0) 42%), and animate translate3d(-1.5%, 0.8%, 0) to translate3d(1.5%, -0.8%, 0) over 18000ms ease-in-out infinite alternate. It must not use blur, blend mode, or a filter.\n\nThe combined per-frame cost budget is: zero DOM reads, zero getBoundingClientRect calls, zero computed-style reads, zero canvas allocation, zero gradient creation, zero text measurement, one 192x192 clearRect, and at most four 96x96 drawImage calls only when the 1400ms grain interval expires. On other rAF ticks, perform only the telemetry frame increment. CSS animations run on the compositor. At 1440x900, this design must sustain at least 20 rAF frames per 1.2 seconds and target 55 or more frames per 1.2 seconds on the reference headless browser.\n\nC. THE LAYER STACK\n\nBuild the visual stack back to front with these exact z-index values:\n\n- body background: the default stacking context, colour #EFEDE8.\n- .backdrop: position:fixed, z-index:0.\n- .orbit: z-index:1 within .backdrop.\n- canvas#grain: z-index:2 within .backdrop.\n- .grain-sheen: z-index:3 within .backdrop, if used.\n- .page: position:relative, z-index:10.\n- header.site-header: z-index:30.\n- nav.site-nav: position:relative, z-index:31.\n- .nav-rule: z-index:32.\n- main#main: z-index:20.\n- section.hero: z-index:20.\n- .eyebrow: z-index:21.\n- h1#camber-title: z-index:22.\n- .balance-rail: z-index:23.\n- .hero-actions: z-index:24.\n- .proof-line: z-index:25.\n- footer.site-footer: z-index:40.\n\nThe headline must physically sit above the rotating conic field. Do not apply mix-blend-mode to either headline line. Only .orbit may use mix-blend-mode:multiply. The canvas may also use mix-blend-mode:multiply, but it must remain behind .page.\n\nD. TOKEN TABLE\n\nDefine every recurring size, colour, spacing, font, line-height, duration, and easing value in :root. Use these exact tokens:\n\n--paper: #EFEDE8;\n--ink: #0B0C0E;\n--graphite: #8A8F98;\n--gilt: #C9A227;\n--white: #FFFFFF;\n--line: rgba(11,12,14,0.18);\n--muted-line: rgba(11,12,14,0.10);\n--focus: #8A6D0B;\n--font-sans: Inter, \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n--gutter: 64px;\n--header-top: 28px;\n--header-bottom: 24px;\n--nav-height: 44px;\n--hero-top: 24px;\n--hero-bottom: 30px;\n--footer-top: 22px;\n--footer-bottom: 24px;\n--nav-rule: 1px;\n--type-display: clamp(72px, 11vw, 168px);\n--display-leading: 0.82;\n--display-overlap: -0.14em;\n--display-weight: 700;\n--display-tracking: -0.065em;\n--body-size: 15px;\n--body-leading: 1.5;\n--label-size: 11px;\n--label-leading: 1.2;\n--label-tracking: 0.16em;\n--balance-width: 240px;\n--headline-max: 1180px;\n--button-height: 46px;\n--button-padding-x: 22px;\n--button-radius: 999px;\n--header-rule-duration: 900ms;\n--header-rule-delay: 500ms;\n--line-one-duration: 500ms;\n--line-one-delay: 0ms;\n--line-two-duration: 700ms;\n--line-two-delay: 150ms;\n--sweep-duration: 9000ms;\n--ease-rise: cubic-bezier(.25,.1,.25,1);\n--ease-ui: cubic-bezier(.2,.8,.2,1);\n--orbit-duration: 36000ms;\n--grain-interval: 1400ms;\n--motion-distance: 24px;\n--mobile-gutter: 20px;\n--tablet-gutter: 36px;\n--narrow-desktop-gutter: 48px;\n--short-heading-size: 86px;\n--short-balance-width: 190px;\n--short-footer-top: 14px;\n--short-footer-bottom: 14px;\n\nUse box-sizing:border-box on html, body, and every descendant. Set body margin:0, min-width:320px, overflow-x:hidden, background:var(--paper), color:var(--ink), font-family:var(--font-sans), font-size:var(--body-size), line-height:var(--body-leading), text-rendering:optimizeLegibility, and -webkit-font-smoothing:antialiased. Set button and a font:inherit.\n\nE. THE COMPOSITION\n\nThe .page element is a CSS grid with grid-template-rows:96px minmax(0,1fr) 72px, width:100%, height:100dvh, padding:0 var(--gutter), and overflow:hidden at widths above 900px.\n\nBuild the header first. header.site-header is display:grid, grid-template-columns:minmax(220px,1fr) auto, align-items:center, height:96px, padding:var(--header-top) 0 var(--header-bottom), border-bottom:var(--nav-rule) solid transparent, and position:relative. Place the wordmark link at grid-column:1 and the navigation at grid-column:2. The wordmark exact copy is “LEDGER & VANE”. It must be an anchor with href=\"#main\", aria-label=\"Ledger & Vane home\", color:var(--ink), font-size:13px, font-weight:800, letter-spacing:0.18em, text-decoration:none, and white-space:nowrap.\n\nnav.site-nav must use display:flex, align-items:center, gap:28px, height:var(--nav-height), margin:0, padding:0. It contains exactly three visible links in this order: “Our approach” with href=\"#approach”, “Stewardship” with href=\"#stewardship”, and “Contact” with href=\"#contact”. Each link uses color:var(--ink), font-size:12px, font-weight:600, letter-spacing:0.02em, text-decoration:none. The links must not be buttons styled as pills. Add a 1px vertical divider using border-left:1px solid var(--muted-line) to the “Contact” link and padding-left:28px. The nav-rule is a separate .nav-rule element positioned at the bottom of the header: left:0, right:0, bottom:0, height:var(--nav-rule), background:var(--ink), transform:scaleX(0), transform-origin:left center. It must animate to scaleX(1) over 900ms with a 500ms delay.\n\nBuild main#main as the second grid row, min-height:0, position:relative, display:grid, place-items:stretch. section.hero is the only child, position:relative, display:grid, grid-template-columns:minmax(0,1fr) var(--balance-width), grid-template-rows:1fr auto auto, column-gap:44px, row-gap:18px, padding:var(--hero-top) 0 var(--hero-bottom), min-height:0, overflow:hidden.\n\nPlace .eyebrow in row 1, column 1, align-self:start, margin:0. Its exact copy is “PRIVATE BANKING / FAMILY CAPITAL / EST. 1998”. Use color:var(--graphite), font-size:var(--label-size), line-height:var(--label-leading), letter-spacing:var(--label-tracking), font-weight:700, text-transform:uppercase.\n\nPlace h1#camber-title in row 2, column 1, align-self:end, width:min(100%,var(--headline-max)), margin:0 0 0 calc(var(--gutter) * -0.10), font-size:var(--type-display), line-height:var(--display-leading), font-weight:var(--display-weight), letter-spacing:var(--display-tracking), text-transform:none, white-space:nowrap. The h1 is position:relative. The first .line display:block, color:var(--graphite), transform:translateY(0), opacity:1 at rest. The first line exact copy is “Holdings,”. The second .line display:block, color:var(--ink), margin-top:var(--display-overlap), transform:translateY(0), opacity:1 at rest. The second line exact copy is “held in balance.”. Apply the second-line gradient with background-image:linear-gradient(100deg, #0B0C0E 42%, #C9A227 50%, #0B0C0E 58%), background-size:300% 100%, background-position:0% 0, background-repeat:no-repeat, -webkit-background-clip:text, background-clip:text, -webkit-text-fill-color:transparent, and color:transparent only to .line.two. The gilt must appear only in this moving sweep and in no other visible element. Animate background-position from 0% 0 to 300% 0 over 9s linear infinite. Do not animate the gradient angle.\n\nPlace .balance-rail in row 2, column 2, align-self:end, justify-self:end, width:var(--balance-width), padding:0 0 2px, border-top:1px solid var(--ink), color:var(--ink), text-align:right. It must contain a definition-style list with three rows. The exact visible copy is:\n“Assets under stewardship”\n“$4.8B”\n“Across 126 family offices”\n“Relationship-led since 1998”\n“London · Geneva · Singapore”\nUse dl, dt, and dd elements. The dt copy is 10px, uppercase, line-height:1.25, letter-spacing:0.12em, and color:var(--graphite). The dd copy is 12px, line-height:1.4, font-weight:650, and color:var(--ink). The first dd uses font-size:24px, line-height:1.05, letter-spacing:-0.03em, and margin-top:7px. Set list rows to margin:0 0 17px. The last row has margin-bottom:0.\n\nPlace .hero-actions in row 3, column 1, display:flex, align-items:center, gap:16px, margin:0, align-self:end. The primary visible control is an anchor with href=\"#contact\", exact copy “Arrange a private conversation”, class .button-primary, and accessible name supplied by its visible text. Place a non-interactive .proof-line after it with exact copy “Independent advice. One accountable relationship.” Use color:var(--graphite), font-size:12px, line-height:1.4, letter-spacing:0.01em.\n\nDo not place any control in .balance-rail. The balance column is informational, right-aligned, and aligned to the same bottom edge as the h1.\n\nBuild footer.site-footer as the third grid row, display:grid, grid-template-columns:1fr auto 1fr, align-items:center, height:72px, padding:var(--footer-top) 0 var(--footer-bottom), border-top:1px solid var(--muted-line), color:var(--graphite), font-size:10px, line-height:1.3, letter-spacing:0.10em, text-transform:uppercase. The left footer item is “Ledger & Vane / Private bank”. The centre item is “Stewardship, measured in generations.” and must be text-align:center. The right item is an anchor with href=\"#legal\", exact visible copy “Legal & privacy”, and aria-label “Ledger & Vane legal and privacy information”. The right item must be text-align:right and color:var(--graphite). No footer element may be centred vertically as a page-wide CTA; only the centre footer copy has text-align:center.\n\nF. THE UI LANGUAGE\n\nThe primary control is a pill with height:var(--button-height), padding:0 var(--button-padding-x), border:1px solid var(--ink), border-radius:var(--button-radius), display:inline-flex, align-items:center, justify-content:center, color:var(--paper), background:linear-gradient(100deg, #0B0C0E 0%, #0B0C0E 68%, #2A2515 100%), font-size:12px, font-weight:700, letter-spacing:0.02em, text-decoration:none, white-space:nowrap, transition:transform 180ms var(--ease-ui), background 180ms var(--ease-ui), color 180ms var(--ease-ui), box-shadow 180ms var(--ease-ui). Its resting box-shadow is 0 0 0 rgba(201,162,39,0). On hover, transform:translateY(-2px), background:linear-gradient(100deg, #1B1C1F 0%, #0B0C0E 72%, #4A3B0E 100%), color:var(--white), box-shadow:0 6px 0 rgba(11,12,14,0.10). On active, transform:translateY(0) and box-shadow:0 2px 0 rgba(11,12,14,0.10). Do not use a gilt fill behind the button.\n\nNavigation links have no pill and no background. On hover, color:var(--graphite). The wordmark changes from var(--ink) to var(--graphite) on hover without changing position. Footer links use a 1px underline with text-underline-offset:3px on hover.\n\nEvery visible link, button, and anchor receives a visible :focus-visible ring using outline:2px solid var(--focus), outline-offset:4px, border-radius:inherit. The ring must remain visible over both paper and ink. Do not remove browser focus outlines.\n\nG. MOTION\n\nAll motion begins after the two-rAF startup guard. The initial paint must be readable, with line one opacity:1, line two opacity:1, line two transform:translateY(0), and the nav rule at scaleX(1) as the resting state. The entrance animations are one-shot CSS animations that use opacity and transform only.\n\nThe exact stagger is:\n\n- header wordmark: opacity 0 to 1 and translateY(8px) to translateY(0), 500ms, ease-out, delay 80ms.\n- nav links: opacity 0 to 1 and translateY(8px) to translateY(0), 500ms, ease-out, delays 120ms, 160ms, 200ms respectively.\n- .nav-rule: scaleX(0) to scaleX(1), 900ms, cubic-bezier(.2,.8,.2,1), delay 500ms.\n- .eyebrow: opacity 0 to 1 and translateY(12px) to translateY(0), 500ms, ease-out, delay 220ms.\n- h1 line one: opacity 0 to 1, 500ms, ease-out, delay 0ms.\n- h1 line two: opacity 0 to 1 and translateY(24px) to translateY(0), 700ms, cubic-bezier(.25,.1,.25,1), delay 150ms.\n- .balance-rail: opacity 0 to 1 and translateY(16px) to translateY(0), 650ms, cubic-bezier(.25,.1,.25,1), delay 300ms.\n- .button-primary: opacity 0 to 1 and translateY(12px) to translateY(0), 500ms, ease-out, delay 420ms.\n- .proof-line: opacity 0 to 1, 500ms, ease-out, delay 520ms.\n- footer items: opacity 0 to 1, 450ms, ease-out, delays 620ms, 680ms, 740ms respectively.\n\nUse these named keyframes exactly or implement equivalent named animations with identical properties:\n\n@keyframes camber-fade {\n  from { opacity: 0; }\n  to { opacity: 1; }\n}\n\n@keyframes camber-rise-line {\n  from { opacity: 0; transform: translateY(24px); }\n  to { opacity: 1; transform: translateY(0); }\n}\n\n@keyframes camber-rise-small {\n  from { opacity: 0; transform: translateY(12px); }\n  to { opacity: 1; transform: translateY(0); }\n}\n\n@keyframes camber-rail {\n  from { opacity: 0; transform: translateY(16px); }\n  to { opacity: 1; transform: translateY(0); }\n}\n\n@keyframes camber-rule {\n  from { transform: scaleX(0); }\n  to { transform: scaleX(1); }\n}\n\n@keyframes camber-sweep {\n  from { background-position: 0% 0; }\n  to { background-position: 300% 0; }\n}\n\n@keyframes camber-orbit {\n  from { transform: rotate(0deg); }\n  to { transform: rotate(360deg); }\n}\n\nThe h1 line two sweep is the only continuously moving text effect. It must run for 9 seconds, linear, infinite, and must not pause on hover. The conic orbit runs for 36 seconds, linear, infinite. The grain translation changes every 1400ms. No infinite entrance animation may repeat.\n\nH. THE RESPONSIVE TABLE\n\nAt the base desktop layout, use --gutter:64px, --type-display:clamp(72px, 11vw, 168px), --balance-width:240px, .page grid rows 96px minmax(0,1fr) 72px, and .hero column-gap:44px.\n\nAt 1920x1080 or wider, set --gutter:80px, --headline-max:1320px, --balance-width:280px, --hero-top:30px, --hero-bottom:34px, and .hero column-gap:56px. Keep the headline left anchored and the balance rail right aligned. Do not increase the display type above 168px.\n\nAt 1440x900, retain --gutter:64px, --type-display:clamp(72px, 11vw, 168px), --balance-width:240px, and --hero column-gap:44px. The h1 width must not exceed 1180px.\n\nAt 1200px and below, set --gutter:var(--narrow-desktop-gutter), --balance-width:210px, and .hero column-gap:28px. Set --type-display:clamp(64px, 10.2vw, 124px). Keep one horizontal hero row until 901px.\n\nAt 900px and below, change .page to min-height:100dvh, height:auto, overflow:visible; set body overflow-y:auto; set .page grid-template-rows:auto auto auto; set --gutter:var(--tablet-gutter); set --header-top:24px; set --header-bottom:20px; set header.site-header height:auto; set nav.site-nav gap:18px; set .hero grid-template-columns:1fr; set .hero grid-template-rows:auto auto auto auto; set .hero padding-top:36px; set .hero padding-bottom:36px; set .balance-rail justify-self:start; width:100%; max-width:360px; text-align:left; set h1#camber-title white-space:normal; set --type-display:clamp(58px, 10vw, 96px); set --display-overlap:-0.10em; set h1#camber-title margin-left:0; set .hero-actions flex-wrap:wrap; set footer.site-footer grid-template-columns:1fr 1fr; row-gap:12px; height:auto; padding-top:22px; padding-bottom:22px; set footer centre item text-align:right; set footer right item grid-column:2; set footer centre item grid-column:1; set footer right item grid-row:1; set footer centre item grid-row:2. Do not use horizontal scrolling to preserve the original balance-rail position.\n\nAt 600px and below, set --gutter:var(--mobile-gutter); set --header-top:20px; set --header-bottom:18px; set .site-header grid-template-columns:1fr; row-gap:16px; set nav.site-nav justify-content:space-between; width:100%; gap:12px; set nav.site-nav a font-size:11px; set nav.site-nav a:last-child padding-left:12px; set .hero padding-top:30px; set .hero padding-bottom:30px; set .eyebrow max-width:280px; line-height:1.45; set --type-display:clamp(54px, 15.3vw, 86px); set --display-leading:0.84; set --display-overlap:-0.08em; set --display-tracking:-0.06em; set .hero-actions align-items:flex-start; flex-direction:column; gap:12px; set .button-primary width:100%; set .proof-line max-width:260px; set .balance-rail max-width:none; set footer.site-footer font-size:9px; letter-spacing:0.08em; set .grain-sheen inset:-20%.\n\nAt 480px and below, set --type-display:clamp(50px, 14.5vw, 70px), set .eyebrow font-size:10px, set .balance-rail dd:first-of-type font-size:22px, and set .button-primary font-size:11px, padding-left:16px, padding-right:16px.\n\nTwo short-height desktop guards are mandatory. At min-width:901px and max-height:780px, set --hero-top:12px; set --hero-bottom:14px; set --footer-top:10px; set --footer-bottom:10px; set --display-size:var(--short-heading-size); set --type-display:86px; set --balance-width:var(--short-balance-width); set .hero row-gap:10px; set .hero padding-top:12px; set .hero padding-bottom:14px; set .page grid-template-rows:82px minmax(0,1fr) 52px; set header.site-header height:82px; set footer.site-footer height:52px. At min-width:901px and max-height:620px, set --gutter:48px; set --header-top:14px; set --header-bottom:12px; set --hero-top:8px; set --hero-bottom:8px; set --footer-top:8px; set --footer-bottom:8px; set --type-display:72px; set --balance-width:170px; set .balance-rail dt font-size:9px; set .balance-rail dd font-size:10px; set .balance-rail dd:first-of-type font-size:20px; set .hero row-gap:6px; set .page grid-template-rows:68px minmax(0,1fr) 42px; set header.site-header height:68px; set footer.site-footer height:42px. Short-height rules never apply below 901px.\n\nI. FAILURE GUARDS\n\nEvery animated element must declare its resting state first: opacity:1, transform:none for normal rise elements, transform:scaleX(1) for .nav-rule, and transform:translateY(0) for the two headline lines. Animation classes are applied by a .motion-ready class on document.documentElement only after two nested requestAnimationFrame callbacks. If JavaScript is disabled, the page must remain fully readable with all content visible, the rule visible, and the second headline in its final position. The CSS animation fallback is allowed to run from the initial paint only when the JavaScript startup guard is unavailable.\n\nIf OffscreenCanvas is unavailable, create a normal canvas with width=192 and height=192. If either 2D context creation fails, remove the canvas from the DOM and keep the paper background, orbit, typography, and all content. If canvas drawing throws, remove the canvas, push the error text into window.__DEMO__.errors, disable the rAF loop after recording the error, and set ready=true. Never show a visible error message.\n\nImplement the two-rAF fallback exactly: call requestAnimationFrame(function firstFrame(){ requestAnimationFrame(function secondFrame(){ document.documentElement.classList.add('motion-ready'); startLoop(); window.__DEMO__.ready = true; }); });. If the first rAF is unavailable, use setTimeout(startAndReady, 32). If the second rAF is unavailable, use setTimeout(startAndReady, 32) from inside the first callback. A delayed start must not prevent the page from being readable.\n\nCreate window.__DEMO__ before registering the window error listener and before the startup callbacks. The object must use the exact name, properties, initial types, and spelling specified by the contract. Set ready=true only after the first paint and after the two-rAF guard. The error listener must push String(event.message || 'window error') into errors, prevent duplicate insertion of the same message within 1000ms, and never call console methods.\n\nIn @media (prefers-reduced-motion: reduce), set *, *::before, and *::after to animation-duration:0.001ms, animation-iteration-count:1, transition-duration:0.001ms, scroll-behavior:auto, and transform:none where a transform is not required for layout. Set .orbit animation:none; set .line.two background-position:50% 0; set canvas#grain opacity:0.11; set the rAF loop to increment frames but do not redraw grain. Keep the rAF loop active for telemetry, with no work other than frames increment. All content must be visible and static.\n\nJ. THE EXACT JS\n\nPlace this script at the end of body as plain JavaScript. It must not use imports, modules, external libraries, console output, async functions, storage, cookies, service workers, or network requests.\n\nconst demo = {\n  name: 'camber',\n  ready: false,\n  frames: 0,\n  effects: [],\n  errors: []\n};\nwindow.__DEMO__ = demo;\ndemo.effects.push('line-one-fade', 'line-two-rise', 'specular-sweep', 'nav-rule-scale', 'conic-orbit', 'paper-grain-drift');\n\nconst errorTimes = new Map();\nwindow.addEventListener('error', function (event) {\n  const message = String(event && event.message ? event.message : 'window error');\n  const now = Date.now();\n  const previous = errorTimes.get(message);\n  if (typeof previous !== 'number' || now - previous > 1000) {\n    demo.errors.push(message);\n    errorTimes.set(message, now);\n  }\n}, { passive: true });\n\nconst canvas = document.getElementById('grain');\nlet context = null;\nlet tileA = null;\nlet tileB = null;\nlet workingSize = 192;\nlet grainIndex = 0;\nlet lastGrainSwap = 0;\nlet reducedMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\nlet stopped = false;\n\nfunction makeTile(seed) {\n  const tile = typeof OffscreenCanvas === 'function' ? new OffscreenCanvas(96, 96) : document.createElement('canvas');\n  tile.width = 96;\n  tile.height = 96;\n  const tileContext = tile.getContext('2d');\n  if (!tileContext) return null;\n  let state = seed >>> 0;\n  const random = function () {\n    state = (1664525 * state + 1013904223) >>> 0;\n    return state / 4294967296;\n  };\n  for (let i = 0; i < 1152; i += 1) {\n    const x = Math.floor(random() * 96);\n    const y = Math.floor(random() * 96);\n    const length = 1 + Math.floor(random() * 3);\n    const vertical = random() > 0.5;\n    tileContext.fillStyle = i % 10 < 7 ? 'rgba(11,12,14,0.035)' : (i % 10 < 9 ? 'rgba(138,143,152,0.030)' : 'rgba(11,12,14,0.018)');\n    if (vertical) tileContext.fillRect(x, y, 1, length);\n    else tileContext.fillRect(x, y, length, 1);\n  }\n  return tile;\n}\n\nfunction removeCanvas() {\n  stopped = true;\n  if (canvas && canvas.parentNode) canvas.parentNode.removeChild(canvas);\n}\n\nfunction drawGrain() {\n  if (!context || reducedMotion || !tileA || !tileB) return;\n  const offsets = [[0,0],[7,5],[13,2],[19,8],[25,4],[31,10],[37,6],[43,11]];\n  const offset = offsets[grainIndex % offsets.length];\n  const tile = grainIndex % 2 === 0 ? tileA : tileB;\n  context.clearRect(0, 0, workingSize, workingSize);\n  context.drawImage(tile, -96 + offset[0], -96 + offset[1]);\n  context.drawImage(tile, offset[0], -96 + offset[1]);\n  context.drawImage(tile, -96 + offset[0], offset[1]);\n  context.drawImage(tile, offset[0], offset[1]);\n}\n\nfunction frame(timestamp) {\n  if (stopped) return;\n  demo.frames += 1;\n  if (!reducedMotion && timestamp - lastGrainSwap >= 1400) {\n    lastGrainSwap = timestamp;\n    grainIndex = (grainIndex + 1) % 8;\n    drawGrain();\n  }\n  window.requestAnimationFrame(frame);\n}\n\nfunction startAndReady() {\n  if (canvas) {\n    try {\n      context = canvas.getContext('2d');\n      tileA = makeTile(1978);\n      tileB = makeTile(2025);\n      if (!context || !tileA || !tileB) removeCanvas();\n    } catch (error) {\n      demo.errors.push(String(error && error.message ? error.message : 'canvas setup failed'));\n      removeCanvas();\n    }\n  }\n  document.documentElement.classList.add('motion-ready');\n  if (!stopped) window.requestAnimationFrame(frame);\n  demo.ready = true;\n}\n\nif (typeof window.requestAnimationFrame === 'function') {\n  window.requestAnimationFrame(function firstFrame() {\n    if (typeof window.requestAnimationFrame === 'function') {\n      window.requestAnimationFrame(function secondFrame() {\n        startAndReady();\n      });\n    } else {\n      window.setTimeout(startAndReady, 32);\n    }\n  });\n} else {\n  window.setTimeout(startAndReady, 32);\n}\n\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["At 1440x900, the document has one h1, one header, one main, one footer, and one nav; document.documentElement.scrollHeight is no greater than document.documentElement.clientHeight, and document.documentElement.scrollWidth is no greater than document.documentElement.clientWidth.","The h1 contains the exact visible strings “Holdings,” and “held in balance.”, the second line uses a 100deg linear-gradient with the gilt stop at 50%, and the rendered second line has a negative top overlap relative to the first line.","window.__DEMO__ exists with name “camber”, ready is true after 1200ms, frames increases by at least 20 over a 1200ms measurement window, effects contains “specular-sweep”, and errors has length 0 after load.","At 390x844, 768x1024, 1440x900, and 1920x1080, document.documentElement.scrollWidth is no greater than document.documentElement.clientWidth; the primary control and all three navigation links have non-empty accessible names.","With prefers-reduced-motion set to reduce, the orbit animation is absent, the second headline line is visible, the navigation rule is visible at full opacity, and the page remains readable without horizontal overflow.","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"],"usage":{"build_tokens":42702,"build_prompt_tokens":8856,"build_completion_tokens":33846,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":532247,"wasted_tokens":0,"repair_tokens":29087,"crosscheck_tokens":20332,"total_tokens":92121},"verification":{"passed":false,"passed_checks":19,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"26801 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"2 frames, effects: line-one-fade, line-two-rise, specular-sweep, nav-rule-scale, conic-orbit, paper-grain-drift"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"23 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 2.08, stdev 14.7, coverage 0.0236"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":false,"detail":"hash 1760415996 -> 1760415996"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Holdings,\nheld in balance.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Ledger & Vane — Private Banking for Inherited Holdings"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=399 pageErrors=0"}]},"crosscheck":{"slug":"camber","reviewer_model":"kimi-k2.8-preview","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":5,"requirements_met":["exact specified copy throughout (wordmark, eyebrow, nav, button, proof, footer)","header/nav/rule structure with Contact divider matches brief","balance rail right-aligned to h1 bottom edge with specified figures","conic orbit backdrop and paper grain canvas present behind page","single pill CTA with specified gradient and hover/active states","footer three-column grid with centre-centred copy","staggered entrance animation timings and named keyframes present","no second CTA, hamburger, or extraneous controls"],"requirements_missed":["second h1 line 'held in balance.' not visible in the rendered screenshot","last balance row uses an empty <dd></dd> with 'London · Geneva · Singapore' as a dt","drawGrain omits the specified rgba(239,237,232,0) fill of the 192x192 working canvas","reduced-motion selector .line.two does not match the actual .line-two class names"],"issues":[{"issue":"The second headline line 'held in balance.' is entirely absent from the rendered page; only the graphite 'Holdings,' line is visible above the CTA, leaving a large blank band where the primary ink headline should sit.","severity":"high","evidence":"Screenshot: only 'Holdings,' renders; no second line below it despite h1 containing span.line.line-two with the gradient text treatment"},{"issue":"Empty dd element after final dt leaves an invalid/empty definition row in the balance rail.","severity":"low","evidence":"<dt>London · Geneva · Singapore</dt><dd></dd>"},{"issue":"Reduced-motion rule for the sweep uses selector .line.two which matches nothing (elements use .line-two), so background-position:50% 0 is never applied under reduced motion.","severity":"low","evidence":"CSS: `.line.two { background-position: 50% 0 !important; }` vs HTML `class=\"line line-two\"`"},{"issue":"drawGrain deviates from the exact JS: no fill of the 192x192 working canvas with rgba(239,237,232,0) after clearRect, and an extra initial drawGrain() call was added in startAndReady.","severity":"low","evidence":"drawGrain() only calls context.clearRect then four drawImage calls"}],"summary":"The structural composition, copy, and motion spec are faithfully implemented, but the rendered screenshot is missing the entire second headline line 'held in balance.', which is the page's primary statement and gut-punches the design. Until that line renders reliably, the page reads as an unfinished hero with a floating CTA.","usage":{"input":17880,"output":2452,"total":20332,"reasoning":1786,"cached":0},"latency_ms":37376}},{"slug":"cant","title":"Cant","category":"Landing Page","effect":"A signal-red freight field where clipped-corner geometry, canted tonnage bars, and a left-to-right loading scan turn operational data into one industrial motion system.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/cant/index.html","doc":"/prompts/cant.md","prompt_text":"A. WHAT IS BEING BUILT\n\nBuild one complete, framework-free landing page for Ferrolane, a freight forwarder operating rail and short-sea legs across Northern Europe. The page is a single-screen desktop logistics manifesto and route-manifest viewer, not a scrolling marketing site. Use semantic HTML, plain CSS, and plain JavaScript in one index.html. Set the document title to “Ferrolane — Northern Europe, on the cant” and set the document language to en. The visual thesis is “the cant”: one 14px diagonal chamfer appears on the top-right and bottom-left corners of every rectangular surface, turning a standard logistics interface into a forward-leaning industrial system.\n\nThe HTML viewport rule is width=device-width, initial-scale=1. The root is 100dvh. At 901px width and wider, html and body must be exactly 100dvh with overflow:hidden; the main region must fit without internal scrolling. At 900px width and below, the document may scroll vertically, but it must never scroll horizontally. Support 390x844, 768x1024, 1440x900, and 1920x1080. Use one Google Fonts request only, loading Archivo at weights 500, 600, and 700, with the fallback stack Arial, Helvetica, sans-serif. If the font request fails or is blocked, the page must remain correct.\n\nDo NOT build a video, animated GIF, image element, remote asset, base64 asset, SVG illustration, WebGL scene, 3D chart, map, globe, loader screen, cookie panel, authentication form, dashboard sidebar, multi-page navigation, modal, carousel, horizontal scroller, parallax image, stock photograph, or second primary CTA. Do NOT use React, Tailwind, GSAP, Three.js, Lottie, a framework, bundler, build step, icon CDN, analytics service, or any network request other than Google Fonts. Do NOT add border-radius. Do not add box-shadow, text-shadow, drop-shadow, gradients inside text, or decorative pills. Do not use opacity below 0.72 for required text. Do not use JavaScript layout reads, getBoundingClientRect, getComputedStyle, scroll listeners, interval timers, or Web Animations API. Do not make manifest rows keyboard controls unless they perform a real navigation. Do not include more than one h1.\n\nB. THE BACKDROP\n\nThe entire viewport is one flat #E03127 signal-red field. There is one body pseudo-element backdrop. It uses a fixed linear gradient from #E03127 at 0% to #E03127 at 100%; the identical stops prevent an unintended visual gradient. Its size is 100% 100%, its position is fixed at inset:0, its z-index is 0, and it has pointer-events:none. Add one fixed 2px vertical near-black registration line at x=48px from top to bottom using a body::after pseudo-element with opacity:0.18. Do not add a second decorative background layer.\n\nCreate 126 bar elements in one chart field: 9 visual columns multiplied by 14 vertical positions. Each bar is an empty div with class “tonnage-bar”. The chart uses CSS Grid with 9 equal columns and 14 equal rows. The 126 heights are not random and are not derived at runtime from viewport dimensions. Hard-set every bar using the exact 14-row value array below, repeated in the listed order for each of 9 columns:\n\nColumn 1: 34, 46, 58, 42, 66, 52, 74, 61, 78, 69, 83, 72, 88, 76.\nColumn 2: 38, 50, 62, 47, 71, 56, 79, 66, 84, 73, 87, 76, 91, 81.\nColumn 3: 42, 55, 65, 51, 74, 60, 82, 70, 87, 77, 90, 80, 94, 84.\nColumn 4: 46, 59, 68, 55, 77, 64, 85, 73, 89, 80, 92, 83, 96, 87.\nColumn 5: 50, 63, 71, 59, 80, 68, 88, 77, 91, 83, 95, 86, 98, 90.\nColumn 6: 54, 66, 74, 63, 82, 72, 90, 80, 93, 86, 97, 89, 100, 93.\nColumn 7: 58, 70, 77, 67, 85, 76, 93, 84, 95, 89, 99, 92, 100, 96.\nColumn 8: 62, 73, 80, 71, 87, 79, 95, 87, 97, 92, 100, 95, 100, 98.\nColumn 9: 66, 77, 84, 75, 90, 83, 98, 91, 99, 95, 100, 98, 100, 100.\n\nRender each value as a percentage height inside a fixed chart cell. The bars use #E03127 as their own fill against the same red field and are separated only by 1px rgba(0,0,0,0.18) inset strokes. Their base cell has the global cut polygon. Each bar is rotated 8deg around its bottom center and skewed -6deg on the X axis, preserving the leaning direction of the cut. The chart occupies the lower two-thirds and extends 72px beyond the right viewport edge; clip the chart wrapper, not the page.\n\nCanvas count is zero. Therefore there is no canvas element, no canvas resolution trick, no getContext call, no ctx.filter, no ctx.shadowBlur, and no per-item canvas compositing. The 126 bars must be transformed by the compositor. A representative 1440x900 performance budget is no more than 1.42 milliseconds of scripting per animation frame, no more than 3.2 milliseconds of style recalculation per frame during the 3.8-second entrance sequence, and no layout-triggering property changes. The post-entrance rAF loop performs only a numeric counter increment and an effect array lookup. Transform and opacity are the only animated CSS properties.\n\nUse three decorative data rules behind the foreground copy. One rule extends from x=0 to 100%, sits at y=67%, and is 1px rgba(0,0,0,0.18). One rule sits at x=48px and spans 38% of viewport height. One rule sits at x=calc(50vw + 12px) and spans 64% of viewport height. Their drift periods are 0 seconds because they remain static. The only continuous background movement is a 7.4-second opacity drift on a 180px soft radial highlight centered at 78% 28%, ranging from opacity:0.035 to opacity:0.07; the highlight never changes position and never resembles a second subject.\n\nC. THE LAYER STACK\n\nUse this back-to-front z-index stack exactly:\n\nz-index:0: body and the fixed flat red backdrop.\nz-index:1: the two vertical registration rules and one horizontal data rule in .atmosphere.\nz-index:2: .header, the clipped 64px navigation bar.\nz-index:3: .manifest, the clipped 3-column route table.\nz-index:4: .hero, including eyebrow, h1, deck, CTA, service status, and 9x14 bar field.\nz-index:5: the one-time 90-degree white clip sweep.\nz-index:6: .footer and its clipped 1px footer rule.\nz-index:10: the focus-visible outline layer, created by native :focus-visible with position:relative and z-index:10; never remove it.\n\nThe header, manifest, hero, and footer are siblings inside a positioned .stage. The bar field is contained by .chart-window with overflow:hidden. Do not rely on source order to create overlap: set every listed z-index explicitly.\n\nD. TOKEN TABLE\n\nDefine these exact custom properties on :root and consume them rather than introducing undocumented sizes:\n\n--red: #E03127;\n--ink: #0E0E0E;\n--white: #FFFFFF;\n--cool: #B9C4CB;\n--rule: rgba(0,0,0,0.18);\n--paper-line: rgba(255,255,255,0.64);\n--field-x: 48px;\n--field-y-top: 32px;\n--grid-gap: 24px;\n--grid-columns: repeat(12,minmax(0,1fr));\n--cut: 14px;\n--cut-path: polygon(0 0,calc(100% - 14px) 0,100% 14px,100% 100%,14px 100%,0 calc(100% - 14px));\n--stroke: 1px;\n--header-h: 64px;\n--footer-h: 44px;\n--type-display: clamp(54px,6.2vw,118px);\n--type-title: 17px;\n--type-body: 14px;\n--type-label: 11px;\n--type-data: 12px;\n--line-tight: 0.88;\n--line-title: 1.15;\n--line-body: 1.35;\n--weight-medium: 500;\n--weight-bold: 700;\n--letter-label: 0.12em;\n--letter-display: -0.055em;\n--ease-cant: cubic-bezier(.2,.8,.2,1);\n--duration-bars: 520ms;\n--duration-sweep: 900ms;\n--duration-row: 140ms;\n--bar-stagger: 26ms;\n--bar-rotation: 8deg;\n--bar-shear: -6deg;\n--chart-bleed: 72px;\n--focus-ring: #FFFFFF;\n--focus-offset: 4px;\n--status-green: #FFFFFF;\n--reduced-duration: 1ms.\n\nAll values in the implementation must come from these tokens or from a declared component rule. Do not use a token for border-radius because every computed border-radius must be 0.\n\nE. THE COMPOSITION\n\nThe body uses padding:0. The .stage is position:relative; width:100%; height:100dvh; min-height:680px; overflow:hidden; background:#E03127; color:#0E0E0E; font-family:Archivo,Arial,Helvetica,sans-serif.\n\nThe header is a semantic nav inside header.header. It is 64px high and starts at top:0 with left:0; width:100%. It has 48px left padding, 48px right padding, border-bottom:var(--stroke) solid var(--rule), and the exact --cut-path. Inside it, use a 12-column grid with 24px gutters. The brand occupies columns 1 through 3, the route statement columns 5 through 9, and the contact link columns 11 through 12. The brand text is “FERROLANE” in 17px, 700 weight, line-height:1, letter-spacing:-0.025em. The route statement is “RAIL / SHORT SEA / NORTHERN EUROPE” in 11px, 600 weight, letter-spacing:0.12em. The contact link is “ROUTE DESK ↗” in 12px, 700 weight and is right aligned. All three are real visible text; the contact link is an anchor with href=\"mailto:routes@ferrolane.example\" and accessible name “Email the Ferrolane route desk”. The nav has aria-label=\"Primary navigation”.\n\nThe main element is .hero and is positioned absolute with top:96px; left:48px; right:48px; bottom:76px. Use a 12-column grid with 24px gutters, grid-template-rows:112px 1fr 92px, and align-items:start. Place .eyebrow at grid-column:1 / 5; grid-row:1. Place the h1 at grid-column:1 / 10; grid-row:2; align-self:start. Place .hero-copy at grid-column:1 / 5; grid-row:3. Place .chart-window at grid-column:5 / 13; grid-row:2 / 4; margin-top:22px; margin-right:-72px. Place .manifest at grid-column:10 / 13; grid-row:1 / 3; align-self:start; margin-top:20px.\n\nThe eyebrow exact copy is “WEEK 29 / NORTH SEA CORRIDOR”. It is 11px, 700 weight, line-height:1, letter-spacing:0.12em, and includes a 32px white horizontal line after the text. Use a 7px square, not a circle, as the status marker. The h1 exact copy is “MOVE HEAVY. / KEEP LEANING.” It uses var(--type-display), weight 700, line-height:0.88, letter-spacing:-0.055em, and max-width:980px. It is the only h1. The hero copy exact copy is “Scheduled rail and short-sea capacity from Malmö to the North Sea ports. One operating window. 11,840 tonnes committed.” It is 14px, weight 500, line-height:1.35, max-width:360px. The CTA is an anchor with href=\"mailto:routes@ferrolane.example?subject=Capacity%20request\" and exact copy “REQUEST CAPACITY ↗”. It is the page’s only inverted white CTA and has an accessible name “Request Ferrolane capacity”. A small status block below it reads “NEXT DEPARTURE / 04 SEP / 06:40” in 11px, 600 weight, letter-spacing:0.12em.\n\nThe .chart-window is 100% width, 100% height, position:relative, overflow:hidden, and has the exact --cut-path. It has a 1px solid rgba(0,0,0,0.18) stroke through an inset box-shadow? Shadows are forbidden, so use border:var(--stroke) solid var(--rule) and then apply the cut. Inside, .bars is a 9-column by 14-row grid with 1px gaps, width:100%, height:100%, padding:18px, transform-origin:bottom left. Each .bar-cell is a div with position:relative; min-width:0; min-height:0; overflow:hidden; border:var(--stroke) solid var(--rule); and the exact --cut-path. Each .tonnage-bar is position:absolute; inset:auto 0 0; width:100%; height:var(--bar-height); background:#E03127; border-top:var(--stroke) solid rgba(255,255,255,0.64); transform-origin:bottom center; transform:rotate(8deg) skewX(-6deg) scaleY(0). Do not use shadows.\n\nThe .manifest is a real section with aria-labelledby=\"manifest-title\". It has a 1px border, the exact cut path, background:transparent, and padding:18px 18px 16px. Its heading is “LIVE MANIFEST” in 11px, 700 weight, letter-spacing:0.12em, and is not an h2 because the document has one h1; use a p with id=\"manifest-title\". The three exact route rows are “MALMÖ → HELSINGBORG”, “GOTHENBURG → OSLO”, and “AARHUS → ESBJERG”. Each row is a 3-column CSS grid: 1fr 74px 58px, 13px row gap, and 16px vertical padding. The route name is 12px, 700 weight. The departure is 11px in --cool. The load is 12px, 700 weight, right aligned. The exact data strings are “04 SEP / 06:40” with “84%”; “05 SEP / 11:15” with “72%”; and “06 SEP / 07:05” with “91%”. Add a 1px rule between rows. The final footer line in the manifest reads “UPDATED 04:12 UTC” in 10px, 600 weight, letter-spacing:0.08em.\n\nThe footer is a semantic footer fixed at bottom:0; left:0; right:0; height:44px. It uses --cut-path, border-top:var(--stroke) solid var(--rule), 48px horizontal padding, and a 12-column grid. The left copy in columns 1 through 5 is “FERROLANE FREIGHT FORWARDING / 55.6050° N, 12.9940° E”. The right copy in columns 10 through 12 is “NORTH SEA / 2025”. Use 10px, 600 weight, letter-spacing:0.1em. The page must contain no text that overflows the 12-column grid at 1440px or 1920px.\n\nF. THE UI LANGUAGE\n\nEvery rectangular surface with a visible boundary uses the exact --cut-path. This includes header, manifest, chart window, CTA, footer, bar cells, and the footer rule container. Every border-radius declaration is 0. No box-shadow or text-shadow is allowed anywhere. Separation is limited to 1px rgba(0,0,0,0.18) borders and 1px rgba(255,255,255,0.64) bar top rules.\n\nThe CTA is a solid #FFFFFF block with #0E0E0E type, no border, 152px minimum width and 42px height, padding:0 18px, display:inline-flex, align-items:center, justify-content:space-between, font-size:12px, font-weight:700, letter-spacing:0.08em, and the exact cut path. Its default background is #FFFFFF. Its hover background is #B9C4CB. Its hover transform is translate(2px,-2px). Its active transform is translate(0,0). The navigation and manifest hover states do not use color inversion; they use a red inset block sliding from left to right, because the CTA is the one inverted control.\n\nEvery visible anchor and button must have a visible :focus-visible ring using outline:3px solid var(--focus-ring); outline-offset:var(--focus-offset); border-radius:0. The focus ring is not clipped by an ancestor with overflow:hidden; if a parent must clip visuals, reserve 8px around focusable content. The manifest rows are not links or buttons, so they must not receive a false control treatment.\n\nG. MOTION\n\nEntrance motion is CSS-only for the visual elements. JavaScript only creates the hard-set bar heights and starts the reveal class. The bar sequence uses the following stagger table:\n\n.eyebrow: opacity 0 to 1 and translateY(10px) to translateY(0), duration 420ms, ease var(--ease-cant), delay 80ms.\nh1: opacity 0 to 1 and translateY(18px) to translateY(0), duration 620ms, ease var(--ease-cant), delay 140ms.\n.hero-copy: opacity 0 to 1 and translateX(-12px) to translateX(0), duration 480ms, ease var(--ease-cant), delay 260ms.\nCTA: opacity 0 to 1 and translateY(8px) to translateY(0), duration 420ms, ease var(--ease-cant), delay 360ms.\n.manifest: opacity 0 to 1 and translateX(16px) to translateX(0), duration 520ms, ease var(--ease-cant), delay 240ms.\nBar 1: scaleY(0) to scaleY(1), duration 520ms, ease var(--ease-cant), delay 0ms.\nEach subsequent bar adds 26ms. Bar 126 therefore begins at 3,250ms and completes at 3,770ms.\n.clip-sweep: translateX(-120%) rotate(90deg) to translateX(120%) rotate(90deg), duration 900ms, ease cubic-bezier(.2,.8,.2,1), delay 500ms, iteration-count 1.\nManifest row hover: a ::before block starts at translateX(-100%) opacity:0.18 and ends at translateX(0) opacity:0.18 over 140ms with ease-out.\n\nName the keyframes exactly “cantBarsGrow”, “cantCopyIn”, “cantSweep”, “cantRowIn”, and “cantDrift”. The .reveal class applies cantCopyIn to non-bars and cantBarsGrow to bars. The sweep has pointer-events:none and is clipped to the stage. The bar transforms must remain composited; never animate width, height, top, left, margin, padding, filter, clip-path, or background-position. The row highlight uses transform and opacity only.\n\nH. THE RESPONSIVE TABLE\n\nAt 1440x900, use the desktop tokens exactly: --field-x:48px; --field-y-top:32px; --header-h:64px; --footer-h:44px; --type-display:96px; --type-title:17px; --type-body:14px; --grid-gap:24px. Keep the hero top at 96px, bottom at 76px, and 12 columns. The h1 uses 96px maximum, the chart bleeds 72px, and the manifest is 318px wide.\n\nAt 1920x1080, override --field-x:64px; --field-y-top:40px; --header-h:72px; --footer-h:48px; --type-display:118px; --grid-gap:32px. Use top:112px; bottom:80px; left:64px; right:64px. The chart margin-right is 96px and the manifest is 360px wide. The grid remains 12 columns, 32px gutters, and zero optical centring.\n\nAt 768x1024, switch the page to scrollable document flow. Override --field-x:28px; --field-y-top:20px; --header-h:60px; --footer-h:42px; --type-display:64px; --type-body:13px; --grid-gap:18px. Set body overflow-x:hidden and body overflow-y:auto. Set .stage min-height:1280px and height:auto. Set .header position:relative. Set .hero position:relative; inset:auto; display:grid; grid-template-columns:repeat(8,minmax(0,1fr)); grid-template-rows:auto auto auto 420px; row-gap:24px; margin:0 28px. Put eyebrow in columns 1 / 7, h1 in columns 1 / 9, copy in columns 1 / 5, CTA in columns 1 / 5, and chart-window in columns 2 / 9. Put manifest in columns 1 / 9 below the chart, with a fixed height of 246px. Footer remains relative after the main.\n\nAt 390x844, override --field-x:18px; --field-y-top:16px; --header-h:58px; --footer-h:40px; --type-display:46px; --type-title:14px; --type-body:12px; --type-label:10px; --grid-gap:12px. Use 4 columns in the header and 4 columns in the hero. The header hides the route statement visually with display:none and retains the brand in columns 1 / 3 and the route desk link in columns 3 / 5. The hero uses row-gap:18px and a minimum height of 1460px. The h1 spans all 4 columns. The copy and CTA span all 4 columns. The chart-window spans all 4 columns, is 390px high, and has margin-right:-18px. The manifest follows the chart, uses padding:14px, and has a height of 270px. Route rows use grid-template-columns:1fr 68px 48px. The footer uses two rows of 20px and does not overlap the chart.\n\nAdd a max-height:760px breakpoint. At desktop widths, override --header-h:56px; --footer-h:40px; --type-display:clamp(52px,5.2vw,82px); --type-label:10px; --type-body:12px. Set .hero top:76px; bottom:58px; grid-template-rows:88px 1fr 74px. Reduce chart margin-top to 8px and manifest margin-top to 10px. Do not reduce the bar count.\n\nAdd a max-height:640px breakpoint. At desktop widths, override --header-h:52px; --footer-h:36px; --type-display:clamp(44px,4.4vw,68px); --type-label:9px; --type-data:10px. Set .hero top:64px; bottom:48px; grid-template-rows:72px 1fr 62px. Set the hero copy line-height:1.2, CTA height:36px, and chart margin-top:4px. The page remains clipped and readable with no vertical scroll at >=901px.\n\nI. FAILURE GUARDS\n\nThe resting state of all readable text, links, manifest content, and footer content must be opacity:1. Add a .js-ready class to body only after DOM construction. The initial CSS state uses opacity:0 only for elements that have an .enter class, and the first rAF adds .reveal. If JavaScript does not run, a 1.8s CSS fallback reveals all non-bar copy and displays a 2-column static tonnage sample. The two-rAF fallback is mandatory: requestAnimationFrame is called once to schedule a first paint, then a second requestAnimationFrame is called before adding .reveal and setting ready=true. Never leave the page permanently invisible if either rAF callback is delayed.\n\nUnder prefers-reduced-motion:reduce, set animation-duration:1ms, animation-delay:0ms, transition-duration:1ms, transition-delay:0ms, and transform:none on .enter, .tonnage-bar, .clip-sweep, .manifest-row::before, and .atmosphere-glow. Keep opacity:1 for all readable content. Do not run the visual entrance sequence in reduced-motion mode. The telemetry rAF loop may continue at a low-cost counter cadence of one callback per animation frame, but it must not cause layout or paint. All numeric values, labels, and controls must remain readable.\n\nJ. THE EXACT JS\n\nPlace this JavaScript at the end of body, before the closing body tag, without modules, imports, external scripts, or a framework. It must execute after the complete DOM exists:\n\n(() => {\n  \"use strict\";\n\n  const telemetry = window.__DEMO__ = {\n    name: \"cant\",\n    ready: false,\n    frames: 0,\n    effects: [],\n    errors: []\n  };\n\n  window.addEventListener(\"error\", event => {\n    telemetry.errors.push(String(event.message || \"window error\"));\n  });\n\n  const columns = [\n    [34,46,58,42,66,52,74,61,78,69,83,72,88,76],\n    [38,50,62,47,71,56,79,66,84,73,87,76,91,81],\n    [42,55,65,51,74,60,82,70,87,77,90,80,94,84],\n    [46,59,68,55,77,64,85,73,89,80,92,83,96,87],\n    [50,63,71,59,80,68,88,77,91,83,95,86,98,90],\n    [54,66,74,63,82,72,90,80,93,86,97,89,100,93],\n    [58,70,77,67,85,76,93,84,95,89,99,92,100,96],\n    [62,73,80,71,87,79,95,87,97,92,100,95,100,98],\n    [66,77,84,75,90,83,98,91,99,95,100,98,100,100]\n  ];\n\n  const chart = document.querySelector(\".bars\");\n  if (chart) {\n    const fragment = document.createDocumentFragment();\n    columns.forEach(column => {\n      column.forEach(value => {\n        const cell = document.createElement(\"div\");\n        cell.className = \"bar-cell\";\n        const bar = document.createElement(\"div\");\n        bar.className = \"tonnage-bar enter\";\n        bar.style.setProperty(\"--bar-height\", value + \"%\");\n        bar.style.setProperty(\"--bar-delay\", (fragment.childNodes.length * 26) + \"ms\");\n        cell.appendChild(bar);\n        fragment.appendChild(cell);\n      });\n    });\n    chart.appendChild(fragment);\n  }\n\n  const effects = [\"bar-grow\", \"clip-sweep\", \"row-highlight\"];\n  effects.forEach(effect => telemetry.effects.push(effect));\n\n  const reduced = window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n  const stage = document.querySelector(\".stage\");\n  const body = document.body;\n\n  const first = window.requestAnimationFrame(() => {\n    window.requestAnimationFrame(() => {\n      body.classList.add(\"js-ready\");\n      if (stage) stage.classList.add(reduced ? \"is-static\" : \"is-active\");\n      telemetry.ready = true;\n    });\n  });\n\n  function frame() {\n    telemetry.frames += 1;\n    window.requestAnimationFrame(frame);\n  }\n  window.requestAnimationFrame(frame);\n})();\n\nThe JavaScript must not write telemetry repeatedly to the DOM, create a timer, inspect geometry, or alter the bar array. The only dynamic style writes are the 126 CSS custom properties --bar-height and --bar-delay assigned while constructing bars. Keep the effect names exactly “bar-grow”, “clip-sweep”, and “row-highlight”.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["A headless browser at 1440x900 reports document.body.scrollWidth equal to document.documentElement.clientWidth, document.documentElement.scrollHeight equal to 900, and window.__DEMO__.name equal to \"cant\" with ready true after two animation frames.","A headless browser counts exactly one h1, one header, one main, one nav, one footer, 126 .tonnage-bar elements, and zero img, video, canvas, iframe, or external script elements.","At 1440x900, every visible rectangular component using the cut treatment has computed border-radius equal to \"0px\", no computed box-shadow, and a clip-path beginning with the specified 14px chamfer polygon.","The CTA has an accessible name, the route-desk link has an accessible name, both anchors are keyboard focusable with a visible :focus-visible outline, and the reduced-motion media query leaves the h1, CTA, manifest, and footer readable with computed opacity 1.","The performance check observes at least 20 telemetry.frames increments during a 1.2 second headless interval at 1440x900, with window.__DEMO__.errors.length equal to 0 and no getBoundingClientRect or getComputedStyle calls in the source.","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"],"usage":{"build_tokens":57801,"build_prompt_tokens":7348,"build_completion_tokens":50453,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":2,"build_latency_ms":695674,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":36630,"total_tokens":94431},"verification":{"passed":true,"passed_checks":18,"total_checks":18,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"30520 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"37 frames, effects: bar-grow, clip-sweep, row-highlight"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"75 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"MOVE HEAVY.\nKEEP LEANING.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Ferrolane — Northern Europe, on the cant"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=505 pageErrors=0"}]},"crosscheck":{"slug":"cant","reviewer_model":"glm-5.2","reviewer_platform":"workbuddy","verdict":"unparsed","confidence":"low","summary":"","issues":[],"usage":{"input":16630,"output":20000,"total":36630,"reasoning":20000,"cached":15133},"raw":""}},{"slug":"carbon","title":"Carbon","category":"Landing Page","effect":"A dual-direction carbon-fibre weave is traversed by a slow radial light while a graphite wordmark and electric-blue structural seam hold the composition.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/carbon/index.html","doc":"/prompts/carbon.md","prompt_text":"A. WHAT IS BEING BUILT\n\nBuild one complete, standalone index.html for a cinematic single-viewport landing page named “CARBON”. The document language is lang=\"en\", the document title is exactly “CARBON — Woven Composites”, and the page is a real document containing one header, one main landmark, one footer, one nav landmark, exactly one h1, and no duplicate page-level headings. The visible product claim is: “Engineered fibre. Quietly radical.” The company is a fictional composites manufacturer; do not add product checkout, shopping-cart behaviour, customer accounts, external links, cookie controls, or secondary pages.\n\nAt viewport widths of 901px and above, html and body must use width: 100%; height: 100dvh; margin: 0; overflow: hidden. The stage must use width: 100%; height: 100dvh; min-height: 680px; overflow: hidden. At widths of 900px and below, the document may scroll vertically, but it must retain width: 100% and must never scroll horizontally. Support 390x844, 768x1024, 1440x900, and 1920x1080 without clipped controls or horizontal overflow.\n\nDo NOT build a video background, GIF, raster image, remote image, base64 image, Lottie file, WebGL scene, SVG illustration, framework, component library, utility framework, animation library, or third-party CDN asset. Do not build a dashboard, configurator, ecommerce flow, multi-page navigation, modal, cookie banner, or mobile drawer. Do not use more than one h1. Do not use text generated inside canvas. Do not add horizontal overflow. Do not run continuous layout measurement. Do not animate background-position, background-size, width, height, top, left, filter, box-shadow, or colour values. Do not call getBoundingClientRect inside the animation loop.\n\nB. THE BACKDROP\n\nBuild one full-stage backdrop with these elements:\n\n1. One .backdrop fixed to the stage at inset: 0, z-index: 0.\n2. One .backdrop-base at inset: 0. Its background colour is #080a0d.\n3. Two pseudo-elements on .backdrop-base, ::before and ::after, each absolute at inset: -80px. Together they form the carbon-fibre weave. The element counts are two pseudo-elements for the base weave and one separate highlight layer.\n4. The warp uses repeating-linear-gradient(90deg, #11151a 0, #11151a 12px, #050608 12px, #050608 24px, #1c2229 24px, #1c2229 36px, #080a0d 36px, #080a0d 48px). The fill uses repeating-linear-gradient(0deg, rgba(255,255,255,0) 0, rgba(255,255,255,0) 14px, rgba(0,0,0,0.48) 14px, rgba(0,0,0,0.48) 24px, rgba(255,255,255,0.035) 24px, rgba(255,255,255,0.035) 34px, rgba(0,0,0,0.18) 34px, rgba(0,0,0,0.18) 44px). Offset the horizontal fill by 12px and the vertical warp by -12px so their 12px half-period offset creates an over/under impression. Keep the gradients stationary; animate only transforms and opacity.\n5. One .backdrop-vignette at inset: 0 with background: radial-gradient(ellipse 78% 72% at 50% 46%, rgba(8,10,13,0) 0%, rgba(8,10,13,0.16) 48%, rgba(8,10,13,0.76) 100%). It darkens all four corners and protects text contrast.\n6. One .backdrop-light at inset: -180px; z-index: 2. Its background is rgba(226,232,240,0.13). Apply mask-image: radial-gradient(circle 360px at 50% 50%, #000 0%, rgba(0,0,0,0.94) 20%, rgba(0,0,0,0.62) 43%, rgba(0,0,0,0.18) 67%, transparent 82%); add -webkit-mask-image with the identical value. The mask reveals a light veil; it does not regenerate or repaint the weave gradients. The layer is 150% by 150%, translated through six keyframe positions, and uses will-change: transform, opacity.\n7. One .blue-seam at left: 63%; top: -12%; width: 1px; height: 124%; z-index: 3. Its linear-gradient is transparent 0%, rgba(59,130,246,0.12) 13%, #60a5fa 43%, #93c5fd 55%, rgba(59,130,246,0.16) 87%, transparent 100%. Add box-shadow: 0 0 12px rgba(96,165,250,0.16). Keep the seam visible over the weave but below all typography.\n8. One .grain at inset: 0; z-index: 4; opacity: 0.13; pointer-events: none. Construct it only from a 4px by 4px conic-gradient with alternating transparent and rgba(255,255,255,0.045) quadrants. Do not load image data.\n\nThe drift periods are 18s for the weave highlight, 26s for the seam pulse, and 7.5s for the grain translation. The highlight and seam use compositor-only transform animation. The grain may translate by 4px increments, but it must not animate opacity. No canvas is used, so no canvas-resolution trick, context filter, shadow blur, or per-item compositing cost applies. The per-frame budget is fewer than 3 active animated layers, zero geometry reads, zero geometry writes, zero style writes from JavaScript, and a target of 20 or more animation frames during any 1.2-second sample at 1440x900. The main rAF callback only increments window.__DEMO__.frames.\n\nC. THE LAYER STACK\n\nBuild the stage in this exact back-to-front order:\n\n1. .backdrop, position: fixed/absolute stage child, z-index: 0.\n2. .backdrop-base and its two gradient pseudo-elements, z-index: 1 within the backdrop stacking context.\n3. .backdrop-light, z-index: 2 within the backdrop stacking context.\n4. .blue-seam, z-index: 3 within the backdrop stacking context.\n5. .grain, z-index: 4 within the backdrop stacking context.\n6. header.site-header, z-index: 20.\n7. main.hero, z-index: 30.\n8. div.hero-seam-label, z-index: 35.\n9. footer.site-footer, z-index: 20.\n10. div.edge-index at z-index: 40 for non-interactive edition metadata.\n\nThe stage establishes position: relative and isolation: isolate. All full-bleed background layers use pointer-events: none. The header, main, footer, and metadata remain keyboard reachable. Do not place a full-size transparent element over the page because it would block controls.\n\nD. TOKEN TABLE\n\nDefine every recurring size and colour in :root with these exact values:\n\n--font-sans: ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif;\n--font-mono: \"SFMono-Regular\", Consolas, \"Liberation Mono\", monospace;\n--page-x: 48px;\n--page-y: 34px;\n--header-height: 46px;\n--footer-height: 42px;\n--content-width: 1280px;\n--content-max: 1680px;\n--hero-top: 50%;\n--hero-width: min(1120px, 78vw);\n--h1-size: clamp(92px, 15.4vw, 296px);\n--h1-weight: 680;\n--h1-letter: -0.078em;\n--h1-line: 0.78;\n--eyebrow-size: 11px;\n--eyebrow-letter: 0.22em;\n--claim-size: clamp(20px, 1.8vw, 32px);\n--claim-line: 1.18;\n--body-size: 14px;\n--body-line: 1.55;\n--meta-size: 10px;\n--meta-letter: 0.18em;\n--nav-size: 11px;\n--nav-letter: 0.16em;\n--button-height: 46px;\n--button-pad-x: 22px;\n--button-radius: 999px;\n--button-border: 1px;\n--rule: 1px;\n--seam-width: 1px;\n--backdrop-grain-size: 4px;\n--backdrop-weave-unit: 48px;\n--motion-fast: 420ms;\n--motion-medium: 820ms;\n--motion-slow: 1200ms;\n--ease-out: cubic-bezier(0.16, 1, 0.3, 1);\n--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);\n--color-void: #080a0d;\n--color-carbon: #11151a;\n--color-carbon-light: #1c2229;\n--color-white: #f8fafc;\n--color-slate: #94a3b8;\n--color-slate-dark: #64748b;\n--color-line: rgba(148,163,184,0.24);\n--color-line-strong: rgba(148,163,184,0.46);\n--color-accent: #60a5fa;\n--color-accent-soft: rgba(96,165,250,0.16);\n--focus-ring: #93c5fd;\n--text-shadow: 0 8px 32px rgba(0,0,0,0.72), 0 1px 2px rgba(0,0,0,0.92);\n--z-backdrop: 0;\n--z-header: 20;\n--z-main: 30;\n--z-label: 35;\n--z-footer: 20;\n--z-meta: 40;\n\nE. THE COMPOSITION\n\nHeader: position: absolute; top: 0; left: 0; right: 0; height: 46px; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; padding: 0 48px; border-bottom: 1px solid rgba(148,163,184,0.12); z-index: 20. The left brand is an anchor with href=\"#top\", accessible name “CARBON home”, and exact visible copy “CARBON / 01”. Style it as 11px monospace, weight 600, letter-spacing 0.16em, color #f8fafc. The center nav contains exactly two anchors, “Materials” and “Process”, styled 11px uppercase with 0.16em tracking and 28px gap. The right header action is an anchor with href=\"#contact\", exact copy “Start a project”, height 46px, and display: inline-flex; align-items: center; justify-self: end.\n\nHero: main.hero has id=\"top\", position: absolute; inset: 46px 0 42px; display: grid; grid-template-columns: 1fr 1120px 1fr; align-items: center; padding: 0 48px; z-index: 30. The central .hero-copy has width: min(1120px, 78vw); justify-self: center. The .eyebrow reads exactly “COMPOSITE SYSTEMS / EST. 2016”. The only h1 reads exactly “CARBON” and uses the tokenized fluid type. Beneath it, insert a 1px rule with width: min(720px, 64vw), margin: 34px 0 26px, and background: linear-gradient(90deg, rgba(148,163,184,0.58), rgba(148,163,184,0.06)). The claim reads exactly “Engineered fibre. Quietly radical.” The body reads exactly “We build high-performance composite structures where low weight, exceptional stiffness, and precise finishing meet.” Place one primary anchor after the body: href=\"#contact\", exact copy “Explore the material”, with a right-arrow glyph “→” inside an aria-hidden span. Place one secondary text link beside it: href=\"#process\", exact copy “Our process”. The body content is constrained to 480px; no text may extend behind the 63% seam without the vignette preserving contrast.\n\nA vertical .hero-seam-label is position: absolute; left: calc(63% + 18px); top: 50%; transform: translateY(-50%); writing-mode: vertical-rl; text-orientation: mixed; color: #93c5fd; font: 10px/1 var(--font-mono); letter-spacing: 0.18em; opacity: 0.74. Its exact copy is “TOW 6K / 2x2 TWILL / 220 GSM”.\n\nFooter: position: absolute; left: 0; right: 0; bottom: 0; height: 42px; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; padding: 0 48px; border-top: 1px solid rgba(148,163,184,0.12); color: #94a3b8; z-index: 20. The left copy is exactly “PRECISION IN EVERY LAYER”. The center coordinates read exactly “51.5072° N / 0.1276° W”. The right copy is exactly “ISO 9001 / MATERIAL 06”, justify-self: end.\n\n.edge-index is position: absolute; right: 17px; top: 50%; transform: translateY(-50%) rotate(180deg); writing-mode: vertical-rl; font: 9px/1 var(--font-mono); letter-spacing: 0.22em; color: rgba(148,163,184,0.5). Its exact copy is “MONOFILAMENT STUDY / 04”. It is non-interactive.\n\nThe anchor targets #contact and #process must resolve to existing visible or programmatically focusable page landmarks. Give main id=\"process\". Put id=\"contact\" on the primary action wrapper and tabindex=\"-1\" so the header target remains valid without introducing a duplicate control.\n\nF. THE UI LANGUAGE\n\nPrimary button treatment: height 46px; padding 0 22px; border: 1px solid rgba(248,250,252,0.22); border-radius: 999px; background: linear-gradient(135deg, #f8fafc 0%, #cbd5e1 100%); color: #080a0d; display: inline-flex; align-items: center; gap: 18px; font-size: 11px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase. On hover, transform: translateY(-2px); background: linear-gradient(135deg, #ffffff 0%, #94a3b8 100%); box-shadow: 0 10px 30px rgba(0,0,0,0.28). The arrow span is font-size: 16px; line-height: 1; transition: transform 420ms var(--ease-out). On hover the arrow translates 4px on the x-axis.\n\nSecondary link: height 46px; padding: 0 4px; color: #cbd5e1; border-bottom: 1px solid rgba(148,163,184,0.42); display inline-flex; align-items: center; font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase. On hover, color: #f8fafc; border-color: #60a5fa.\n\nHeader brand and nav links have no permanent pill. Their hover colour is #f8fafc, and their underline pseudo-element scales from 0 to 1 on the x-axis. Every visible control has a 2px solid #93c5fd focus-visible outline with 4px outline offset and border-radius inherited from the control. Do not suppress focus rings. Ensure minimum target dimensions of 44px by 44px for the project link, nav links, primary action, and secondary link.\n\nG. MOTION\n\nUse CSS only for entrance animation. The wordmark starts at opacity 0, transform: translate3d(0, 34px, 0), and runs for 1100ms with cubic-bezier(0.16,1,0.3,1). The eyebrow, rule, claim, body, and actions use 760ms, 860ms, 920ms, 980ms, and 1040ms respectively. Do not leave any content below opacity 1.\n\nExact stagger table:\n\n.site-header -> opacity 0 to 1 and translate3d(0,-12px,0) to 0 -> 0ms, duration 700ms.\n.hero .eyebrow -> opacity 0 to 1 and translate3d(0,14px,0) to 0 -> 180ms, duration 680ms.\n.hero h1 -> opacity 0 to 1, scale(0.985), and translate3d(0,34px,0) to 0 -> 300ms, duration 1100ms.\n.hero-rule -> opacity 0 to 1 and scaleX(0.2) to 1 -> 560ms, duration 860ms.\n.hero .claim -> opacity 0 to 1 and translate3d(0,18px,0) to 0 -> 640ms, duration 780ms.\n.hero .body-copy -> opacity 0 to 1 and translate3d(0,14px,0) to 0 -> 740ms, duration 760ms.\n.hero-actions -> opacity 0 to 1 and translate3d(0,12px,0) to 0 -> 840ms, duration 740ms.\n.site-footer -> opacity 0 to 1 and translate3d(0,10px,0) to 0 -> 920ms, duration 700ms.\n.edge-index -> opacity 0 to 1 -> 1100ms, duration 700ms.\n.backdrop-light -> lightSweep -> 0ms, duration 18000ms, iteration-count infinite.\n.blue-seam -> seamPulse -> 0ms, duration 26000ms, iteration-count infinite.\n.grain -> grainShift -> 0ms, duration 7500ms, iteration-count infinite.\n\nNamed keyframes:\n\n@keyframes reveal-up { from { opacity: 0; transform: translate3d(0, var(--enter-y, 18px), 0); } to { opacity: 1; transform: translate3d(0,0,0); } }\n@keyframes reveal-title { from { opacity: 0; transform: translate3d(0,34px,0) scale(0.985); } to { opacity: 1; transform: translate3d(0,0,0) scale(1); } }\n@keyframes reveal-rule { from { opacity: 0; transform: scaleX(0.2); } to { opacity: 1; transform: scaleX(1); } }\n@keyframes lightSweep { 0% { transform: translate3d(-26%, -10%, 0); opacity: 0.46; } 24% { opacity: 0.82; } 50% { transform: translate3d(22%, 4%, 0); opacity: 0.64; } 76% { opacity: 0.88; } 100% { transform: translate3d(30%, 18%, 0); opacity: 0.46; } }\n@keyframes seamPulse { 0%,100% { transform: translate3d(0,-7px,0); opacity: 0.28; } 50% { transform: translate3d(0,7px,0); opacity: 0.76; } }\n@keyframes grainShift { 0% { transform: translate3d(0,0,0); } 25% { transform: translate3d(-4px,4px,0); } 50% { transform: translate3d(4px,-4px,0); } 75% { transform: translate3d(-2px,-2px,0); } 100% { transform: translate3d(0,0,0); } }\n\nSet animation-fill-mode: both for entrance animations. Set will-change: transform, opacity only on the three continuously animated backdrop layers. Never animate box-shadow or filter.\n\nH. RESPONSIVE TABLE\n\nAt 1440x900, retain the base tokens: 48px page padding, 1120px hero width, 15.4vw h1 size, 46px header, 42px footer. At 1920x1080, override --page-x: 72px; --page-y: 46px; --content-max: 1680px; --hero-width: min(1240px, 72vw); --h1-size: 296px; --claim-size: 32px. The seam remains at 63% and the h1 remains optically left of centre through the central grid column.\n\nAt 768x1024, set --page-x: 32px; --page-y: 26px; --header-height: 58px; --footer-height: 52px; --hero-width: min(680px, 82vw); --h1-size: clamp(112px, 21vw, 176px); --claim-size: 24px; --body-size: 14px. Use body height: auto; min-height: 100dvh; overflow-x: hidden. Stage min-height: 100dvh; height: auto; overflow: visible. Header becomes sticky at top with z-index 20. Hero becomes position: relative; inset: auto; min-height: calc(100dvh - 58px - 52px); padding: 112px 32px 120px; display: grid; grid-template-columns: 1fr; align-content: center. Footer remains relative. Hide the center header nav below 901px. Keep the blue seam fixed at 63%. Set the vertical seam label to display: none only in the compact layout because its 10px text is not a control and would crowd the body. The edge-index remains visible.\n\nAt 390x844, set --page-x: 20px; --page-y: 18px; --header-height: 58px; --footer-height: 64px; --hero-width: 100%; --h1-size: clamp(72px, 25vw, 104px); --h1-letter: -0.072em; --claim-size: 22px; --body-size: 13px; --button-height: 48px; --button-pad-x: 18px. Header grid becomes 1fr auto. Hero padding is 92px 20px 112px. Footer grid becomes 1fr auto; the center coordinates are display:none. Hero actions use flex-direction: column; align-items: flex-start; gap: 12px. The primary action remains 48px high. The claim max-width is 330px and body max-width is 340px. Hide the edge-index. Keep the seam at 58% on mobile so it passes through the right side of the h1. The seam label is already display:none.\n\nAt max-height: 760px and min-width: 901px, set --page-y: 20px; --header-height: 42px; --footer-height: 36px; --h1-size: clamp(84px, 14vw, 188px); --claim-size: 19px; --body-size: 12px. Hero inset becomes 42px 0 36px. The body copy max-width is 430px. Hero rule margins become 22px 0 18px.\n\nAt max-height: 700px and min-width: 901px, set --page-x: 32px; --header-height: 38px; --footer-height: 34px; --h1-size: clamp(72px, 12.5vw, 152px); --claim-size: 17px; --body-size: 11px. Hero inset becomes 38px 0 34px. The h1 line-height becomes 0.8. The rule width becomes 560px and margins become 16px 0 14px. Header project link height becomes 38px. Footer height becomes 34px.\n\nI. FAILURE GUARDS\n\nAll animated content must have opacity: 1 and transform: none as its resting state in CSS. Animation-delay may hold an element at opacity 0 for at most 1100ms. The script must not overwrite opacity or transform on content. Use two requestAnimationFrame callbacks: the first schedules the second; the second marks telemetry ready after the browser has completed one paint opportunity. Add a 1200ms timeout fallback that also marks ready once, so a throttled background tab cannot leave ready false. The runtime must never emit console output. Catch script errors through a window error listener and append error.message strings to telemetry.\n\nUnder prefers-reduced-motion: reduce, stop entrance animation, lightSweep, seamPulse, and grainShift with animation: none !important. Keep all foreground elements at opacity: 1 and transform: none. Keep .backdrop-light at a static transform: translate3d(-8%, 0, 0) and opacity: 0.66. Keep the seam at opacity: 0.56. Do not disable the telemetry rAF counter because telemetry readiness is a runtime requirement; its callback must still perform only frames += 1. Do not use matchMedia listeners that change document structure.\n\nJ. THE EXACT JS\n\nPlace this script as the final direct child of body. It performs no DOM measurement and no style mutation:\n\n    <script>\n    (function () {\n      var telemetry = {\n        name: 'carbon',\n        ready: false,\n        frames: 0,\n        effects: [\n          'header-reveal',\n          'eyebrow-reveal',\n          'title-reveal',\n          'rule-reveal',\n          'claim-reveal',\n          'body-reveal',\n          'actions-reveal',\n          'footer-reveal',\n          'metadata-reveal',\n          'backdrop-light',\n          'blue-seam',\n          'film-grain'\n        ],\n        errors: []\n      };\n      window.__DEMO__ = telemetry;\n\n      window.addEventListener('error', function (event) {\n        telemetry.errors.push(String(event.message || 'Unknown runtime error'));\n      });\n\n      var readyMarked = false;\n      function markReady() {\n        if (readyMarked) return;\n        readyMarked = true;\n        telemetry.ready = true;\n      }\n\n      requestAnimationFrame(function () {\n        requestAnimationFrame(markReady);\n      });\n\n      window.setTimeout(markReady, 1200);\n\n      function countFrame() {\n        telemetry.frames += 1;\n        requestAnimationFrame(countFrame);\n      }\n      requestAnimationFrame(countFrame);\n    }());\n    </script>\n\nThe full HTML must use semantic landmarks exactly once: header.site-header containing nav, main#top.hero, and footer.site-footer. Use aria-label=\"Primary navigation\" on nav. The visible arrow span inside the primary action must be aria-hidden=\"true\". Do not add aria-live regions. Preserve normal document order. Set background: #080a0d on html. Set body margin: 0. Set *, *::before, and *::after with box-sizing: border-box. Set button, a with font: inherit where applicable. Ensure document.documentElement.scrollWidth is not greater than document.documentElement.clientWidth at all four target viewport sizes. Keep the complete file below 90 KB and contain no console statement.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["Loads the standalone page and leaves document.documentElement.scrollWidth equal to document.documentElement.clientWidth at 390x844, 768x1024, 1440x900, and 1920x1080.","Contains exactly one h1 with the visible text CARBON plus semantic header, nav, main, and footer landmarks, and every visible link has a non-empty accessible name with a visible focus-visible outline.","At a viewport width of 1440 and height of 900, document.body scrollHeight is no greater than 900 and document.documentElement overflow is hidden; the document reports exactly one html element with lang=en and title CARBON — Woven Composites.","Within 1200ms window.__DEMO__.ready is true, its name is carbon, effects contains all 12 required effect names, frames increases by at least 20 over a 1200ms sample, and errors remains an array.","With prefers-reduced-motion: reduce, all animated foreground elements compute to opacity 1 and the continuously animated backdrop layers compute to animation-name none, with no console messages and no uncaught page errors.","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"],"usage":{"build_tokens":53910,"build_prompt_tokens":6909,"build_completion_tokens":47001,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":3,"build_latency_ms":797482,"wasted_tokens":77818,"repair_tokens":0,"crosscheck_tokens":23679,"total_tokens":77589},"verification":{"passed":true,"passed_checks":18,"total_checks":18,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"26108 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"3 frames, effects: header-reveal, eyebrow-reveal, title-reveal, rule-reveal, claim-reveal, body-reveal, actions-reveal, footer-reveal, metadata-reveal, backdrop-light, blue-seam, film-grain"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"71 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"CARBON\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"CARBON — Woven Composites"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=397 pageErrors=0"}]},"crosscheck":{"slug":"carbon","reviewer_model":"MiniMaxAI/MiniMax-M3","reviewer_platform":"cmdcode","verdict":"partial","confidence":"high","aesthetic_score":6,"requirements_met":["Document title and lang attribute exact","Single h1 present with exact copy CARBON","Token table complete with all specified values","Backdrop weave uses exact specified gradients with correct offsets","Blue seam at 63% with correct linear-gradient and animation","Backdrop light veil with correct mask-image (both webkit and standard)","Grain layer uses 4px conic-gradient with correct opacity","Header three-column grid with brand/nav/action","Footer three-column grid with exact copy","Edge-index with vertical-rl writing mode and correct copy","Hero seam label with exact copy and correct positioning","Primary action button with arrow glyph in aria-hidden span","Secondary link beside primary","Reduced motion media query stops all animations and sets static states","Telemetry script matches brief with frames counter and error listener","No external resources, no canvas, no images","Motion choreography matches specified durations and delays","Named keyframes (reveal-up, reveal-title, reveal-rule, lightSweep, seamPulse, grainShift) implemented correctly"],"requirements_missed":["Main element has id=\"top\" instead of id=\"process\" as explicitly required","Grid template uses minmax(0, var(--hero-width)) instead of literal 1fr 1120px 1fr","h1 rendering appears severely clipped at 1024px viewport showing only partial text","Footer is not visible in the 1024x640 screenshot, likely clipped by overflow","Unspecified .wordmark-viewport and .wordmark-form wrapper elements added without brief authorization"],"issues":[{"issue":"h1 wordmark severely clipped at 1024x640 viewport, only 'ON' or last portion visible at left edge instead of full 'CARBON' centered in hero","severity":"high","evidence":"Screenshot shows large white letters 'ON' at left edge of viewport with 'CARB' missing; .wordmark-form width:max-content inside .wordmark-viewport overflow:hidden width:100% clips incorrectly when hero grid middle column is minmax(0,798px) at 1024px"},{"issue":"Footer not visible in 1024x640 screenshot","severity":"medium","evidence":"Screenshot shows only backdrop, header, partial hero, and seam-label; no footer strip at bottom edge"},{"issue":"Main landmark has id='top' but brief explicitly states 'Give main id=process'","severity":"medium","evidence":"<main class=\"hero\" id=\"top\"> vs brief: 'Give main id=\"process\". Put id=\"contact\" on the primary action wrapper'"},{"issue":"Hero grid template deviates from brief literal specification","severity":"low","evidence":"Generated: grid-template-columns: minmax(0, 1fr) minmax(0, var(--hero-width)) minmax(0, 1fr); Brief: grid-template-columns: 1fr 1120px 1fr"},{"issue":"Unspecified wordmark wrapper elements add rendering risk","severity":"low","evidence":".wordmark-viewport and .wordmark-form classes are not mentioned in the brief but are inserted around the h1 text"},{"issue":"backdrop-base lacks explicit z-index: 1 while its pseudo-elements have z-index: 1","severity":"low","evidence":".backdrop-base has no z-index property; .backdrop-base::before and ::after have z-index: 1"},{"issue":"primary-action transition only covers transform, not background/box-shadow specified in brief hover state","severity":"low","evidence":"CSS: transition: transform var(--motion-fast) var(--ease-out); on .primary-action"}],"summary":"The page implements the brief's token system, backdrop layers, motion choreography, and copy faithfully, but the hero h1 rendering is broken at the 1024px viewport — the wordmark is severely clipped showing only a fragment, and the footer is not visible. The main landmark also has the wrong id (top instead of process), and the grid template deviates from the literal spec. Visually premium in concept but the screenshot reveals a critical layout failure at this resolution.","usage":{"input":15979,"output":7700,"total":23679,"reasoning":0,"cached":0},"latency_ms":74848}},{"slug":"cardinal","title":"Cardinal","category":"Landing Page","effect":"A precision compass rose with 72 tick marks seeking a wandering bearing with spring damping, behind three pulsing magnetic-field rings.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/cardinal/index.html","doc":"/prompts/cardinal.md","prompt_text":"A. WHAT IS BEING BUILT\nThis document specifies the complete build order for the \"CARDINAL\" single-viewport cinematic landing page. The page is a high-precision, dark-mode navigation interface. The document title must be exactly `<title>CARDINAL — Precision Navigation</title>`. The HTML tag must be `<html lang=\"en\">`. The viewport rule is `<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">`. The body must enforce a single viewport on desktop: `height: 100dvh; overflow: hidden;` for screens >=901px, and allow native scrolling (`overflow-y: auto; overflow-x: hidden;`) for screens <=900px. \n\nDo NOT build: any multi-page routing; any WebGL, Three.js, or canvas-based 3D rendering; any React, Vue, Svelte, or framework code; any Tailwind or CSS frameworks; any GSAP, Lottie, or anime.js; any external raster images (jpg/png/gif), external SVGs, video files, or audio files; any base64 encoded media blobs; any custom font files (woff/woff2/ttf); any horizontal scrollbars at 390px, 768px, 1440px, or 1920px; any scroll-jacking or smooth-scroll libraries.\n\nB. THE BACKDROP\nThe base background is a deep black: `--bg: #050505`. A subtle radial glow sits at z-index 0: `radial-gradient(circle at 50% 50%, rgba(248, 113, 113, 0.08) 0%, rgba(5, 5, 5, 1) 60%)` applied to a pseudo-element covering 100vw and 100vh. \nThe magnetic field rings consist of 3 SVG `<circle>` elements inside an SVG with a `0 0 100 100` viewBox. The radii are 30, 40, and 50 user units (which map to 30vmin, 40vmin, 50vmin when the SVG is scaled). The stroke is `var(--accent)` at `0.3` user units wide, with `fill=\"none\"`. They pulse outward using the `pulse-ring` keyframe: scale from 0.8 to 1.2, opacity from 0.8 to 0. Durations are 4s, 5s, and 6s. Delays are 0s, 1.3s, and 2.6s. The entire ring container drifts with a `drift` animation: `transform: translate(-50%, -50%) rotate(360deg)` over 180s linear infinite.\nThe canvas resolution trick (`canvas.width = canvas.offsetWidth * devicePixelRatio`) is explicitly bypassed because we are using SVG and CSS gradients instead of `<canvas>`. This guarantees the per-frame cost budget with zero rasterization overhead. The per-frame cost budget is strictly 2 DOM writes: the needle's `transform` and the readout's `textContent`. Zero `getBoundingClientRect` calls are made inside the loop. Zero `ctx.filter` or `ctx.shadowBlur` operations exist.\n\nC. THE LAYER STACK\nz-index 0: `.bg-glow` (radial gradient pseudo-element).\nz-index 1: `#rings` (SVG container for the 3 pulsing magnetic rings).\nz-index 2: `#rose-container` (SVG container for the 72 ticks, 4 labels, and 1 needle).\nz-index 3: `header` (logo and navigation).\nz-index 4: `.hero` (the H1 and the live bearing readout).\nz-index 5: `footer` (status indicator and action buttons).\n\nD. TOKEN TABLE\n--bg: #050505;\n--accent: #f87171;\n--accent-dim: rgba(248, 113, 113, 0.2);\n--text-main: #f3f4f6;\n--text-muted: #9ca3af;\n--font-mono: 'Space Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n--font-sans: 'Inter', system-ui, -apple-system, sans-serif;\n--rose-size: 70vmin;\n--tick-length-cardinal: 2.5;\n--tick-length-ordinal: 1.5;\n--tick-length-minor: 0.8;\n--needle-length: 60;\n--h1-size: clamp(2.5rem, 6vw, 5rem);\n--readout-size: clamp(1rem, 2vw, 1.5rem);\n--header-footer-pad: 2rem 3rem;\n\nE. THE COMPOSITION\nHeader: `display: grid; grid-template-columns: 1fr auto; padding: var(--header-footer-pad); position: absolute; top: 0; width: 100%; box-sizing: border-box; z-index: 3;`. Left side contains the text \"CARDINAL\" (`font-family: var(--font-sans); font-weight: 600; letter-spacing: 0.5em; font-size: 1rem; color: var(--text-main);`). Right side contains `<nav>` with three links: \"TECHNOLOGY\", \"FLEET\", \"CONTACT\" (`display: flex; gap: 2rem; font-family: var(--font-mono); font-size: 0.75rem; text-transform: uppercase; color: var(--text-muted); text-decoration: none;`).\n\nHero: `position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; z-index: 4; pointer-events: none;`. Contains exactly one `<h1>` with the text \"NAVIGATE WITH ABSOLUTE CERTAINTY\" (`font-size: var(--h1-size); font-weight: 600; letter-spacing: -0.02em; margin: 0; text-transform: uppercase;`). Below it is a `<div id=\"readout\">` showing \"BRG 000°\" (`font-family: var(--font-mono); font-size: var(--readout-size); color: var(--accent); margin-top: 1rem; font-weight: 700;`).\n\nFooter: `position: absolute; bottom: 0; width: 100%; box-sizing: border-box; padding: var(--header-footer-pad); display: grid; grid-template-columns: 1fr auto; align-items: center; z-index: 5;`. Left side: \"STATUS: ONLINE\" (`font-family: var(--font-mono); font-size: 0.75rem; color: var(--text-muted); display: flex; align-items: center; gap: 0.5rem;`) followed by an 8px dot (`width: 8px; height: 8px; background: var(--accent); border-radius: 50%; animation: blink 2s infinite;`). Right side: `.btn-group { display: flex; gap: 1rem; }` containing two buttons.\n\nF. THE UI LANGUAGE\nPrimary Button: `.btn-primary { background: linear-gradient(135deg, #f87171 0%, #dc2626 100%); color: #ffffff; border: none; padding: 0.75rem 1.5rem; border-radius: 999px; font-family: var(--font-mono); font-size: 0.875rem; text-transform: uppercase; letter-spacing: 0.1em; cursor: pointer; transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.2s; }`. Hover state: `.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(248, 113, 113, 0.3); }`.\nSecondary Button: `.btn-secondary { background: transparent; color: var(--text-main); border: 1px solid rgba(248, 113, 113, 0.3); padding: 0.75rem 1.5rem; border-radius: 999px; font-family: var(--font-mono); font-size: 0.875rem; text-transform: uppercase; letter-spacing: 0.1em; cursor: pointer; transition: background 0.2s, border-color 0.2s; }`. Hover state: `.btn-secondary:hover { border-color: var(--accent); background: rgba(248, 113, 113, 0.05); }`.\nFocus visible: `:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }`.\n\nG. MOTION\nStagger table:\n- `header`: `fade-down`, 0.6s, delay 0.1s.\n- `line` (ticks): `fade-in`, 0.5s, delay `calc(var(--i) * 0.01s)`.\n- `h1`: `fade-up`, 0.8s, delay 0.2s.\n- `#readout`: `fade-up`, 0.8s, delay 0.4s.\n- `footer`: `fade-up`, 0.6s, delay 0.6s.\n- `circle` (rings): `pulse-ring`, 4s/5s/6s, delay 0s/1.3s/2.6s.\n- `#needle`: Spring damping via JS (continuous).\n\nNamed keyframes:\n- `@keyframes pulse-ring { 0% { transform: scale(0.8); opacity: 0.8; } 100% { transform: scale(1.2); opacity: 0; } }`\n- `@keyframes drift { from { transform: translate(-50%, -50%) rotate(0deg); } to { transform: translate(-50%, -50%) rotate(360deg); } }`\n- `@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }`\n- `@keyframes fade-up { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }`\n- `@keyframes fade-down { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } }`\n- `@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }`\n\nH. RESPONSIVE TABLE\nBase (1440x900): `--rose-size: 70vmin;` `--header-footer-pad: 2rem 3rem;`\n@media (max-width: 900px): `body { overflow-y: auto; overflow-x: hidden; display: flex; flex-direction: column; min-height: 100dvh; }`, `#rose-container { position: relative; top: auto; left: auto; transform: none; margin: 10vh auto; }`, `.hero { position: relative; top: auto; left: auto; transform: none; margin-bottom: 10vh; }`, `footer { position: relative; }`.\n@media (max-width: 768px): `--rose-size: 80vmin;` `--h1-size: 3rem;`, `header { grid-template-columns: 1fr; gap: 1rem; text-align: center; }`, `nav { justify-content: center; }`.\n@media (max-width: 390px): `--rose-size: 90vmin;` `--h1-size: 2rem;`, `footer { grid-template-columns: 1fr; gap: 1.5rem; text-align: center; }`, `.btn-group { justify-content: center; }`.\n@media (max-height: 700px): `--rose-size: 50vmin;`, `header, footer { padding: 1rem 2rem; }`.\n@media (max-height: 500px): `--rose-size: 40vmin;`, `--h1-size: 1.5rem;`, `header, footer { padding: 0.5rem 1rem; }`.\n\nI. FAILURE GUARDS\nResting opacity: All base CSS sets `opacity: 1` for text and ticks. The `fade-in` and `fade-up` animations end at `opacity: 1` with `forwards`. If JS fails to execute, CSS animations still play and leave all elements perfectly visible.\nNeedle resting: Initial CSS `transform: rotate(0deg)` is set on `#needle`. If JS fails, the needle remains visibly rendered at 0 degrees (North).\nTwo-rAF fallback: The JS loop uses `requestAnimationFrame`. If the tab is backgrounded, rAF naturally pauses and `frames` stops incrementing, which is correct. No custom polyfill is required.\nReduced-motion: The CSS includes `@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important; scroll-behavior: auto !important; } }`. The JS checks `window.matchMedia('(prefers-reduced-motion: reduce)').matches`. If true, it sets `currentBearing = 45`, updates the DOM exactly once, sets `ready = true`, and deliberately does NOT start the rAF loop or the `setInterval`. The navigation settles instantly and the page remains completely readable and static.\n\nJ. THE EXACT JS\n```javascript\nwindow.__DEMO__ = { name: 'cardinal', ready: false, frames: 0, effects: [], errors: [] };\nwindow.addEventListener('error', (e) => window.__DEMO__.errors.push(e.message));\n\nconst ticksGroup = document.getElementById('ticks');\nconst ringsGroup = document.getElementById('rings');\nconst needle = document.getElementById('needle');\nconst readout = document.getElementById('readout');\nconst prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n\n// 1. Generate 72 ticks\nfor (let i = 0; i < 72; i++) {\n  const tick = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n  const angle = (i * 5) * (Math.PI / 180);\n  const isCardinal = i % 18 === 0;\n  const isOrdinal = i % 9 === 0 && !isCardinal;\n  const length = isCardinal ? 2.5 : (isOrdinal ? 1.5 : 0.8);\n  const strokeWidth = isCardinal ? 0.6 : 0.3;\n  const rOuter = 48;\n  const rInner = rOuter - length;\n  tick.setAttribute('x1', (50 + rOuter * Math.sin(angle)).toString());\n  tick.setAttribute('y1', (50 - rOuter * Math.cos(angle)).toString());\n  tick.setAttribute('x2', (50 + rInner * Math.sin(angle)).toString());\n  tick.setAttribute('y2', (50 - rInner * Math.cos(angle)).toString());\n  tick.setAttribute('stroke', 'var(--accent)');\n  tick.setAttribute('stroke-width', strokeWidth.toString());\n  tick.setAttribute('opacity', isCardinal ? '1' : (isOrdinal ? '0.6' : '0.3'));\n  tick.style.setProperty('--i', i);\n  if (!prefersReducedMotion) {\n    tick.style.animation = 'fade-in 0.5s ease-out forwards';\n    tick.style.animationDelay = `calc(var(--i) * 0.01s)`;\n  }\n  ticksGroup.appendChild(tick);\n}\n\n// 2. Generate 4 cardinal labels\nconst labels = [\n  { text: 'N', angle: 0 },\n  { text: 'E', angle: 90 },\n  { text: 'S', angle: 180 },\n  { text: 'W', angle: 270 }\n];\nlabels.forEach(l => {\n  const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n  const angle = l.angle * (Math.PI / 180);\n  const r = 42;\n  text.setAttribute('x', (50 + r * Math.sin(angle)).toString());\n  text.setAttribute('y', (50 - r * Math.cos(angle) + 1.5).toString());\n  text.setAttribute('fill', 'var(--text-main)');\n  text.setAttribute('font-size', '4');\n  text.setAttribute('font-family', 'var(--font-mono)');\n  text.setAttribute('text-anchor', 'middle');\n  text.textContent = l.text;\n  ticksGroup.appendChild(text);\n});\n\n// 3. Generate 3 magnetic rings\nfor (let i = 0; i < 3; i++) {\n  const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n  circle.setAttribute('cx', '50');\n  circle.setAttribute('cy', '50');\n  circle.setAttribute('r', (30 + i * 10).toString());\n  circle.setAttribute('fill', 'none');\n  circle.setAttribute('stroke', 'var(--accent)');\n  circle.setAttribute('stroke-width', '0.3');\n  if (!prefersReducedMotion) {\n    circle.style.animation = `pulse-ring ${4 + i}s ease-out infinite`;\n    circle.style.animationDelay = `${i * 1.3}s`;\n  }\n  ringsGroup.appendChild(circle);\n}\n\n// 4. Spring physics for the needle\nlet currentBearing = 0;\nlet targetBearing = 0;\nlet velocity = 0;\nconst stiffness = 0.08;\nconst damping = 0.85;\n\nif (prefersReducedMotion) {\n  currentBearing = 45;\n  targetBearing = 45;\n  needle.style.transform = `rotate(${currentBearing}deg)`;\n  readout.textContent = `BRG ${Math.round(currentBearing).toString().padStart(3, '0')}°`;\n  window.__DEMO__.ready = true;\n} else {\n  window.__DEMO__.effects.push('spring-needle');\n  window.__DEMO__.effects.push('magnetic-pulse');\n  \n  setInterval(() => {\n    targetBearing = Math.random() * 360;\n  }, 3500);\n\n  function loop() {\n    window.__DEMO__.frames++;\n    let diff = targetBearing - currentBearing;\n    // Normalize difference to the shortest path (-180 to 180)\n    diff = ((diff + 180) % 360 + 360) % 360 - 180;\n    const force = diff * stiffness;\n    velocity = (velocity + force) * damping;\n    currentBearing = (currentBearing + velocity + 360) % 360;\n\n    needle.style.transform = `rotate(${currentBearing}deg)`;\n    readout.textContent = `BRG ${Math.round(currentBearing).toString().padStart(3, '0')}°`;\n    \n    requestAnimationFrame(loop);\n  }\n  requestAnimationFrame(loop);\n  window.__DEMO__.ready = true;\n}\n```\n\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["document.querySelectorAll('h1').length === 1 && document.querySelector('h1').textContent.trim() === 'NAVIGATE WITH ABSOLUTE CERTAINTY'","window.__DEMO__.frames > 20 after 1200ms of runtime","getComputedStyle(document.getElementById('needle')).transform !== 'none' and its value differs between two samples 100ms apart","document.documentElement.scrollWidth <= document.documentElement.clientWidth at 390px, 768px, and 1440px viewport widths","window.__DEMO__.effects.includes('spring-needle') && window.__DEMO__.effects.includes('magnetic-pulse')","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"],"usage":{"build_tokens":34133,"build_prompt_tokens":5007,"build_completion_tokens":29126,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":424930,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":16330,"total_tokens":50463},"verification":{"passed":true,"passed_checks":18,"total_checks":18,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"15179 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"3 frames, effects: spring-needle, magnetic-pulse"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"69 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"NAVIGATE WITH ABSOLUTE CERTAINTY\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"CARDINAL — Precision Navigation"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=117 pageErrors=0"}]},"crosscheck":{"slug":"cardinal","reviewer_model":"deepseek-v4.1-flash","reviewer_platform":"workbuddy","verdict":"confirmed","confidence":"high","aesthetic_score":7,"requirements_met":["Layer stack z0 bg-glow / z1 #rings / z2 #rose-container / z3 header / z4 .hero / z5 footer all present and ordered correctly","72 ticks generated at 5° intervals with cardinal/ordinal/minor length and stroke-width differentiation, rOuter 48","4 cardinal labels N/E/S/W at r=42, fill var(--text-main), font-size 4, mono family","3 magnetic rings r=30/40/50, fill none, stroke var(--accent) 0.3 units, pulse-ring 4s/5s/6s with 0/1.3/2.6s delays","Ring container drift animation: translate(-50%,-50%) rotate(360deg) over 180s linear infinite","Exact radial glow: circle at 50% 50%, rgba(248,113,113,0.08) 0% -> rgba(5,5,5,1) 60% on a full-viewport pseudo-element at z-index 0","Header grid 1fr auto, CARDINAL with 0.5em letter-spacing, nav with TECHNOLOGY/FLEET/CONTACT in mono uppercase","Hero absolute 50%/50% translate(-50%,-50%), single h1 'NAVIGATE WITH ABSOLUTE CERTAINTY' + #readout 'BRG 000°' in accent mono","Footer grid 1fr auto with STATUS: ONLINE, 8px blinking accent dot, and pill button group","Stagger table honoured exactly: header fade-down 0.6s/0.1s, h1 fade-up 0.8s/0.2s, readout fade-up 0.8s/0.4s, footer fade-up 0.6s/0.6s","All six named keyframes (pulse-ring, drift, fade-in, fade-up, fade-down, blink) present with brief-specified values","Spring needle JS verbatim from brief: stiffness 0.08, damping 0.85, shortest-path diff normalization, 2 DOM writes per frame","Reduced-motion guard in CSS and JS (bearing pinned to 45°, no rAF, no setInterval)","Responsive table reproduced at 900/768/390px and 700/500px height breakpoints with matching token overrides","Focus-visible 2px accent outline with 2px offset on all interactive elements"],"requirements_missed":[],"issues":[{"issue":"Compass rose is visually subordinated: the h1 sits dead-centre over the dial and completely hides the E and W cardinal labels","severity":"low","evidence":"Screenshot shows 'NAVIGATE WITH ABSOLUTE CERTAINTY' covering the rose centre; only N and S labels are legible. This is the brief's own z-index 2 vs 4 stacking, so it is faithful, but the rose reads as texture rather than an instrument."},{"issue":"Nav anchors point at unrelated landmark IDs that are not navigation destinations","severity":"low","evidence":"<a href=\"#technology\"> resolves to <main id=\"technology\">, #fleet to the button group, #contact to <footer>. Clicking 'FLEET' scrolls to the footer buttons."},{"issue":"Three declared design tokens are dead code; tick and needle geometry is hardcoded instead","severity":"low","evidence":"--tick-length-cardinal/ordinal/minor and --needle-length are declared in :root but never referenced; JS hardcodes 2.5/1.5/0.8 and the needle uses y2=\"-10\"."}],"summary":"The build is a near-verbatim transcription of the brief — tokens, layer stack, stagger table, keyframes and the spring-needle loop are all present and correct, and nothing in the screenshot is clipped, mis-sized or misplaced. It reads as a competent, restrained dark instrument page rather than a default AI template, though the centred H1 burying the E/W bearings makes the rose decorative instead of functional.","usage":{"input":10592,"output":5738,"total":16330,"reasoning":4768,"cached":0},"latency_ms":26791}},{"slug":"cascade","title":"Cascade","category":"Landing Page","effect":"A monospace telemetry waterfall sends fourteen streams of compact log tokens through a cool-grey field, with isolated green hot rows and a five-second vertical scan.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/cascade/index.html","doc":"/prompts/cascade.md","prompt_text":"<THE FULL BUILD ORDER, 2200-3200 words>\n\nA. WHAT IS BEING BUILT\n\nBuild one self-contained index.html for a fictional log-analytics company named CASCADE. The page is a single-screen cinematic product landing page whose central visual is an instrumented data waterfall: fourteen vertical streams of short operational tokens descend behind a precise, restrained interface. It must feel like observability software under a calibrated microscope, not a science-fiction terminal.\n\nUse document language lang=\"en\". Set the document title to \"CASCADE — Log intelligence in motion\". Set the viewport meta to width=device-width, initial-scale=1. The application is one semantic document with one real header, one main, one real footer, and one real nav. There must be exactly one h1: “CASCADE”. The h1 text may be visually split with spans, but the accessible name must remain CASCADE.\n\nAt viewport widths of 901px and greater, set html, body, and the page shell to height: 100dvh and overflow: hidden. At widths of 900px and below, permit normal vertical document scrolling, keep the header non-sticky, and size the canvas composition to 390x844, 768x1024, 1440x900, and 1920x1080 without horizontal overflow. Never scale the whole page with transform.\n\nDo NOT build a hero carousel, terminal emulator, Matrix rain, animated typing demo, product dashboard mockup, pricing table, blog feed, login form, cookie panel, modal, video, image gallery, 3D scene, particle nebula, katakana, binary glyph field, chart library, or multi-page navigation. Do not use React, Tailwind, GSAP, Three.js, Lottie, a framework, a bundler, a build step, external images, remote fonts other than Google Fonts, mp4, webm, gif, SVG icon assets from a CDN, base64 media, or third-party APIs. Do not put content behind an interaction gate. Do not add more than one h1. Do not use a neon green field, pure black background, glow overload, or persistent scanning overlay that harms legibility.\n\nB. THE BACKDROP\n\nCreate one full-bleed canvas element with id=\"cascade-canvas\", CSS width:100% and height:100%, positioned absolute at inset:0. It sits behind the header, hero, and footer. The canvas is decorative with aria-hidden=\"true\". The page background beneath it must be #0b0e12.\n\nUse the canvas resolution trick: set its internal drawing buffer to round(cssWidth * min(devicePixelRatio, 1.5)) by round(cssHeight * min(devicePixelRatio, 1.5)), capped at a maximum width of 2880 and maximum height of 1620. Apply the same cap to the offscreen trail buffer. On resize, preserve normalized column and row state, create new offscreen canvases, clear both buffers to transparent, and restart animation only if motion is permitted. Do not recreate geometry or gradients per frame.\n\nRender fourteen fixed logical columns across the field. Give each column a normalized x center, speed, phase, token interval, width, and opacity. Logical column width is 8.25% of the canvas width and gutters consume the remaining 15.5%. At 1440px, the approximate center coordinates are 67, 186, 305, 424, 543, 662, 781, 900, 1019, 1138, 1257, 1376, 1495, and 1614px after proportional width calculation; the actual coordinates are always derived from column index and canvas width, never hard-coded for one viewport. Map canvas x to the right side of the composition so the 68%-wide hero copy remains on a controlled dark veil.\n\nEach column contains a stack of 9 logical rows. Every row is a short token, not a character field. Alternate among “trace.7f3”, “ingest 142ms”, “GET /v1/logs”, “idx 84.2ms”, “P95 18.7ms”, “batch 064”, “status 204”, “queue 03”, “shard eu-2”, “merge 4.1ms”, “replay 0007”, “evt 91a4”, “edge 11ms”, and “checksum ok”. Use deterministic hash functions for token selection, phase, and hot-row designation; do not call Math.random inside the animation loop.\n\nEach row travels downward at a per-column speed from 0.54 to 0.94 canvas heights per second. Rows begin with a phase offset from 0.00 to 0.96 of the cycle. New tokens are placed above the top edge at cycle intervals from 760ms to 1320ms. Each token has a 3px-radius line cap and a tail of 42px. A token is not a full-width line: its width is 56% to 86% of its column width. Draw the tail as a vertical linear gradient from transparent at its top to rgba(148,163,184,0.28) at its lower end, then draw the 2px token head in rgba(203,213,225,0.72). Every 29th token is hot. Hot tokens are only 7px high, width 62% of their column, and use #4ade80 at opacity 0.92. A hot row has a 76x14px background wash using rgba(74,222,128,0.08), with no blur and no shadow. Exactly one hot row may be visible in each column at a time.\n\nBehind the tokens, fill the canvas with a vertical linear gradient: stop 0 is #0b0e12, stop 0.48 is #11161d, stop 1 is #090b0f. Add one static horizontal rule at y=52% using rgba(148,163,184,0.055) and one at y=78% using rgba(148,163,184,0.035). Do not add star points, rain lines, glyph noise, or glowing edges.\n\nThe fading trail is produced on an offscreen canvas, not by clearing the visible canvas completely each frame. Every frame, fill the trail buffer with rgba(11,14,18,0.085) using source-over. Draw the newly active token segments onto that buffer, then draw the trail buffer onto the visible canvas with globalAlpha 0.92. This is a fixed-cost two-canvas composite. Avoid ctx.filter and ctx.shadowBlur entirely. Avoid large path counts: use one path per gradient tail and one short line or rounded rectangle per visible token. The per-frame budget is 14 column updates, 126 row visibility checks, 126 maximum token draws, 2 buffer composites, and 1 scan-line draw. It must sustain at least 20 frames per 1.2 seconds on a 1440x900 headless viewport.\n\nThe scan line is a 1px vertical line with a 1px adjacent translucent line. Its gradient runs from transparent at y=0, through rgba(203,213,225,0.10) at y=46%, to rgba(74,222,128,0.34) at y=54%, then transparent at y=100%. Sweep from x=0 to canvas width over 5000ms with a 900ms pause after each pass. It is positioned behind all foreground UI and must not cross text at opacity above 0.10 over the hero copy. Cache all canvas dimensions in the loop; do not call getBoundingClientRect, clientWidth, clientHeight, getComputedStyle, or window.innerWidth inside the rAF loop.\n\nC. THE LAYER STACK\n\nUse these layers from back to front. The body background is z-index:0. The full-bleed canvas is z-index:1. A static vignette pseudo-element on the hero is z-index:2, with pointer-events:none. The primary left-side content veil is z-index:3, using a horizontal gradient from rgba(11,14,18,0.96) to rgba(11,14,18,0.60) and width:70%. The header is z-index:10. The main hero content is z-index:11. The footer is z-index:10. The accessibility skip link is z-index:100 only when focused. No layer may create horizontal overflow.\n\nThe header has a 1px bottom border in rgba(148,163,184,0.12), height:76px, and padding:0 48px. The logo lockup is on the left. The nav is on the right. The hero uses a max-width:1440px shell centered with 48px side padding and 64px top and bottom space. The footer sits at the bottom edge in the same shell with 24px top padding and 28px bottom padding.\n\nD. TOKEN TABLE\n\nDefine every visual token in :root and use only these variables for sizes and colors.\n\n--page-bg: #0b0e12\n--panel-ink: #f1f5f9\n--body-ink: #94a3b8\n--muted-ink: #64748b\n--line: rgba(148,163,184,0.12)\n--line-soft: rgba(148,163,184,0.07)\n--accent: #4ade80\n--accent-soft: rgba(74,222,128,0.12)\n--accent-border: rgba(74,222,128,0.42)\n--canvas-cap: 2880px\n--canvas-height-cap: 1620px\n--shell-max: 1440px\n--header-height: 76px\n--header-pad-x: 48px\n--hero-pad-x: 48px\n--hero-pad-y: 64px\n--footer-pad-x: 48px\n--footer-pad-y: 28px\n--footer-top: 24px\n--nav-gap: 32px\n--eyebrow-size: 11px\n--eyebrow-track: 0.16em\n--h1-size: clamp(48px, 6.5vw, 104px)\n--h1-line: 0.92\n--h1-track: -0.07em\n--lede-size: 17px\n--lede-line: 1.55\n--lede-measure: 470px\n--action-gap: 12px\n--button-height: 46px\n--button-pad-x: 20px\n--button-radius: 999px\n--pill-height: 34px\n--pill-pad-x: 12px\n--pill-gap: 8px\n--footer-text-size: 12px\n--footer-gap: 18px\n--focus-ring: 2px\n--focus-offset: 4px\n--scan-duration: 5000ms\n--scan-pause: 900ms\n--trail-opacity: 0.085\n--token-font: \"IBM Plex Mono\", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", monospace\n--display-font: \"Space Grotesk\", system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif\n\nLoad Google Fonts only for Space Grotesk weights 400, 500, 600 and IBM Plex Mono weights 400, 500, 600. Include preconnect links to fonts.googleapis.com and fonts.gstatic.com, but all fallback stacks must render correctly if the request is blocked.\n\nE. THE COMPOSITION\n\nHeader markup is real header.header > a.brand[href=\"#top\"] and nav aria-label=\"Primary navigation\". The brand has a 10px 10px 10px 4px accent square and the text “CASCADE”. Its accessible name must be “CASCADE home”. Nav contains three links: “Product”, “Signals”, and “Company”, each linking to an in-page section or top target that exists. Add a header action anchor with exact text “Request access”; it links to #request-access and is focusable.\n\nMain id=\"top\" has section.hero aria-labelledby=\"hero-title\". Place a left content column with width:min(100%, 620px) and position relative. The eyebrow exact copy is “LOG INTELLIGENCE / 01”. It uses an 8px green status dot followed by that text. The h1 exact text is “CASCADE”. Under it, a two-line statement uses exact copy: “See the signal.” on the first line and “Keep the context.” on the second. The supporting paragraph exact copy is “CASCADE turns fragmented events into one calm operational surface. Search, compare, and act on the trail before it disappears.” Set its width to 470px and max-width:100%.\n\nPlace two controls below the paragraph. The primary anchor exact text is “Start a quiet shift”; secondary anchor exact text is “Read the field notes”. The first links to #request-access, the second to #signals. Add a small proof row below them containing “14 streams” and “99.98% ingest uptime” separated by a 1px vertical rule. The footer exact left copy is “Operational clarity, without the noise.” The footer right copy is “© 2025 CASCADE SYSTEMS”. A tiny footer status group displays a 6px green dot and “ALL SYSTEMS OBSERVABLE”.\n\nCreate one non-interactive signal legend inside the hero at right:100px; bottom:24px; with three items: “TRACE”, “BATCH”, and “HOT ROW”. The first two are 5px grey dots, the last is a 5px green dot. The legend must not intercept pointer events.\n\nThe composition is a 12-column conceptual grid: left content occupies columns 1 through 6; empty breathing space occupies columns 7 through 8; the canvas is visible through columns 5 through 12. At desktop, set hero grid-template-columns: repeat(12, minmax(0, 1fr)); gap:24px. The left content starts at column 1 and spans 6 columns. The right visual is a transparent overlay with no border, no card, and no label, allowing the canvas to be the product image. Do not place dashboard rectangles, charts, or fake controls over the waterfall.\n\nF. THE UI LANGUAGE\n\nPrimary button: display:inline-flex; align-items:center; justify-content:center; min-height:46px; padding:0 20px; border:1px solid var(--accent-border); border-radius:999px; background:linear-gradient(135deg, #4ade80 0%, #22c55e 100%); color:#07110b; font:600 13px/1 var(--display-font); letter-spacing:-0.01em; text-decoration:none. Hover changes transform:translateY(-2px), background:linear-gradient(135deg, #6ee7a0 0%, #4ade80 100%), and border-color:#86efac. Active changes transform:translateY(0). Do not add a glow shadow.\n\nSecondary button: min-height:46px; padding:0 20px; border:1px solid rgba(203,213,225,0.24); border-radius:999px; background:rgba(15,23,42,0.36); color:#e2e8f0; font:500 13px/1 var(--display-font). Hover border-color:rgba(74,222,128,0.55), background:rgba(30,41,59,0.64), and transform:translateY(-2px). Both buttons have a visible 2px accent focus ring with 4px offset. Do not use outline:none without a replacement.\n\nPills are 34px high with 12px horizontal padding, 1px solid var(--line), background:rgba(15,23,42,0.54), color:#cbd5e1, font:500 10px/1 var(--token-font), letter-spacing:0.08em, and border-radius:999px. Hover border-color:var(--accent-border), color:var(--accent), and background:var(--accent-soft). Every visible link and control receives an accessible name, including the status and brand links.\n\nG. MOTION\n\nInitial page elements enter once. The animation is staggered from the eyebrow to the proof row. Use only opacity and transform.\n\nStagger table:\n- header: fade-down 480ms cubic-bezier(0.22,1,0.36,1), delay 0ms.\n- eyebrow: fade-up 500ms cubic-bezier(0.22,1,0.36,1), delay 100ms.\n- h1: fade-up 700ms cubic-bezier(0.22,1,0.36,1), delay 170ms.\n- statement: fade-up 600ms cubic-bezier(0.22,1,0.36,1), delay 280ms.\n- lede: fade-up 600ms cubic-bezier(0.22,1,0.36,1), delay 360ms.\n- actions: fade-up 500ms cubic-bezier(0.22,1,0.36,1), delay 440ms.\n- proof row: fade-up 500ms cubic-bezier(0.22,1,0.36,1), delay 520ms.\n- signal legend: fade-in 700ms ease-out, delay 800ms.\n- footer: fade-up 500ms cubic-bezier(0.22,1,0.36,1), delay 600ms.\n\nDefine @keyframes fade-up as from opacity:0; transform:translateY(18px); to opacity:1; transform:translateY(0). Define @keyframes fade-down as from opacity:0; transform:translateY(-10px); to opacity:1; transform:translateY(0). Define @keyframes fade-in as from opacity:0 to opacity:1. Set the canvas to opacity:0 and animate it to opacity:1 over 900ms with delay 0ms. Do not animate width, height, top, left, box-shadow, or color.\n\nThe canvas loop has effect names “cascade.tokens”, “cascade.trails”, “cascade.scan”. Push each name exactly once into window.__DEMO__.effects. The scan CSS animation is scan-sweep 5900ms linear infinite, with 0% transform:translateX(-100%), 15% transform:translateX(0%), 84% transform:translateX(100%), 100% transform:translateX(100%). The timing includes the 5000ms sweep and 900ms pause. The line is a child pseudo-element or an absolutely positioned div with transform only.\n\nH. RESPONSIVE TABLE\n\nAt 1920x1080: --shell-max:1680px; --header-pad-x:72px; --hero-pad-x:72px; --footer-pad-x:72px; --hero-pad-y:92px; --h1-size:clamp(72px, 7.2vw, 112px). Keep the left veil width:68%.\n\nAt 1440x900: --shell-max:1440px; --header-pad-x:48px; --hero-pad-x:48px; --footer-pad-x:48px; --hero-pad-y:64px; --h1-size:clamp(58px, 7vw, 96px). Keep one line for each h1 text span and the lede width at 470px.\n\nAt 901px and below: hide the primary action label only if necessary by reducing --hero-pad-x to:28px; set --header-pad-x:28px; --footer-pad-x:28px; --nav-gap:18px; --lede-measure:430px. Keep desktop height:100dvh only at 901px and above.\n\nAt max-width:900px: body overflow-y:auto; html,body overflow-x:hidden; .page-shell min-height:100dvh; header height:68px; hero min-height:760px; height:auto; padding-top:56px; padding-bottom:44px; grid-template-columns:1fr; gap:40px. Position the canvas across the full hero at top:0; height:100%; opacity:0.88. The content veil becomes vertical:linear-gradient(180deg,rgba(11,14,18,0.98) 0%,rgba(11,14,18,0.90) 48%,rgba(11,14,18,0.62) 100%). Keep the signal legend at right:24px; bottom:20px. At 390x844, set --header-pad-x:20px; --hero-pad-x:20px; --footer-pad-x:20px; --h1-size:52px; --lede-size:15px; --lede-measure:100%; --action-gap:10px; --button-height:44px; --button-pad-x:16px; --nav-gap:12px; hide the word “Company” from the primary nav but retain a visibly available 44px link named “More” only if the header still needs three destinations. Prefer a two-link nav on mobile: “Product” and “Signals”; no hidden focusable links.\n\nAt max-height:820px and min-width:901px: --header-height:68px; --hero-pad-y:34px; --footer-pad-y:18px; --footer-top:14px; --h1-size:clamp(48px, 6.2vw, 82px); --lede-size:15px; --lede-line:1.4. At max-height:680px and min-width:901px: --header-height:60px; --hero-pad-y:22px; --footer-pad-y:12px; --footer-top:10px; --h1-size:48px; --lede-size:14px; --statement-size:20px. Never reduce text below 12px. No horizontal overflow at 390, 768, 1440, or 1920 widths.\n\nI. FAILURE GUARDS\n\nSet every animated foreground element to opacity:1 at rest and use animation-fill-mode:both only for the entrance animation. If JavaScript fails, the page must remain readable, the canvas may show its static CSS background, and controls must still work as anchors. Add a two-rAF fallback: after the first rAF request, request a second rAF; if the first frame callback has not updated window.__DEMO__.frames, set canvas opacity:1, remove all entrance classes, set all animated foreground opacity:1, and cancel the animation class rather than leaving hidden content. Set window.__DEMO__.ready=true only after the first paint using requestAnimationFrame(() => requestAnimationFrame(() => { window.__DEMO__.ready = true; })). The exact telemetry object must be initialized before any resize or animation work.\n\nThe error listener is window.addEventListener(\"error\", function(event) { window.__DEMO__.errors.push(String(event.message || \"window error\")); });. Do not call console.log, console.warn, console.error, or debug. Avoid uncaught exceptions by wrapping setup and resize in try/catch and pushing a concise error string if initialization fails.\n\nIn @media (prefers-reduced-motion: reduce), remove all CSS animation and transition declarations with animation:none; transition:none; scroll-behavior:auto; set canvas opacity:1; set every animated foreground opacity:1; preserve the static gradient and token positions. The JavaScript must check matchMedia(\"(prefers-reduced-motion: reduce)\").matches. If true, draw one deterministic frame, set ready after two rAFs, and do not start the continuous loop. If the preference changes, cancel any loop and either start it or stop it accordingly. Canvas movement is decorative, so this static frame must still show fourteen columns and at least four visible token heads.\n\nJ. THE EXACT JS\n\nUse this exact initialization and animation structure, with the HTML canvas referenced by id \"cascade-canvas\". The DOM reads happen only in setup or resize, never in frame().\n\nconst canvas = document.getElementById(\"cascade-canvas\");\nconst ctx = canvas.getContext(\"2d\", { alpha: false });\nconst trail = document.createElement(\"canvas\");\nconst trailCtx = trail.getContext(\"2d\");\nconst shell = document.querySelector(\".page-shell\");\nconst reduced = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\nwindow.__DEMO__ = { name: \"cascade\", ready: false, frames: 0, effects: [], errors: [] };\nwindow.addEventListener(\"error\", function(event) {\n  window.__DEMO__.errors.push(String(event.message || \"window error\"));\n});\n\nconst words = [\n  \"trace.7f3\", \"ingest 142ms\", \"GET /v1/logs\", \"idx 84.2ms\",\n  \"P95 18.7ms\", \"batch 064\", \"status 204\", \"queue 03\",\n  \"shard eu-2\", \"merge 4.1ms\", \"replay 0007\", \"evt 91a4\",\n  \"edge 11ms\", \"checksum ok\"\n];\nconst columns = Array.from({ length: 14 }, (_, index) => ({\n  index,\n  phase: (index * 0.071 + 0.13) % 1,\n  speed: 0.54 + ((index * 0.037) % 0.40),\n  interval: 760 + ((index * 83) % 560),\n  width: 0.56 + ((index * 0.013) % 0.30)\n}));\nlet width = 0;\nlet height = 0;\nlet dpr = 1;\nlet last = 0;\nlet raf = 0;\nlet running = false;\nlet fallbackPending = true;\nlet scanStart = 0;\n\nfunction resizeCanvas() {\n  const rect = shell.getBoundingClientRect();\n  width = Math.max(1, Math.round(rect.width));\n  height = Math.max(1, Math.round(rect.height));\n  dpr = Math.min(window.devicePixelRatio || 1, 1.5);\n  const targetWidth = Math.min(2880, Math.round(width * dpr));\n  const targetHeight = Math.min(1620, Math.round(height * dpr));\n  canvas.width = targetWidth;\n  canvas.height = targetHeight;\n  trail.width = targetWidth;\n  trail.height = targetHeight;\n  canvas.style.width = \"100%\";\n  canvas.style.height = \"100%\";\n  trailCtx.clearRect(0, 0, targetWidth, targetHeight);\n}\n\nfunction hash(value) {\n  const n = Math.sin(value * 12.9898) * 43758.5453;\n  return n - Math.floor(n);\n}\n\nfunction drawStatic() {\n  if (!ctx) return;\n  ctx.clearRect(0, 0, width, height);\n  const base = ctx.createLinearGradient(0, 0, 0, height);\n  base.addColorStop(0, \"#0b0e12\");\n  base.addColorStop(0.48, \"#11161d\");\n  base.addColorStop(1, \"#090b0f\");\n  ctx.fillStyle = base;\n  ctx.fillRect(0, 0, width, height);\n  ctx.strokeStyle = \"rgba(148,163,184,0.055)\";\n  ctx.lineWidth = 1;\n  ctx.beginPath();\n  ctx.moveTo(0, height * 0.52);\n  ctx.lineTo(width, height * 0.52);\n  ctx.moveTo(0, height * 0.78);\n  ctx.lineTo(width, height * 0.78);\n  ctx.stroke();\n  columns.forEach((column, columnIndex) => {\n    const x = width * (0.045 + columnIndex * 0.0825);\n    for (let row = 0; row < 9; row += 1) {\n      const y = ((row / 9 + column.phase) % 1) * height;\n      const hot = (row + columnIndex * 5) % 29 === 0;\n      ctx.fillStyle = hot ? \"rgba(74,222,128,0.92)\" : \"rgba(203,213,225,0.66)\";\n      ctx.fillRect(x, y, Math.max(5, width * 0.82 * column.width * 0.055), hot ? 7 : 3);\n    }\n  });\n}\n\nfunction frame(time) {\n  if (!running || !ctx) return;\n  if (!last) last = time;\n  const delta = Math.min(40, time - last);\n  last = time;\n  const scaleX = width;\n  const scaleY = height;\n  trailCtx.globalCompositeOperation = \"source-over\";\n  trailCtx.fillStyle = \"rgba(11,14,18,0.085)\";\n  trailCtx.fillRect(0, 0, scaleX, scaleY);\n  trailCtx.globalCompositeOperation = \"source-over\";\n  columns.forEach((column, columnIndex) => {\n    const x = scaleX * (0.045 + columnIndex * 0.0825);\n    const cycle = (column.interval / 1000) * column.speed;\n    const offset = ((time / 1000 + column.phase) % cycle) / cycle;\n    for (let row = 0; row < 9; row += 1) {\n      const y = (offset + row / 9) % 1 * scaleY;\n      if (y < -48 || y > scaleY + 48) continue;\n      const hot = (row + columnIndex * 5) % 29 === 0;\n      const tokenWidth = Math.max(5, scaleX * 0.82 * column.width * 0.055);\n      const tail = trailCtx.createLinearGradient(x, y - 42, x, y);\n      tail.addColorStop(0, \"rgba(148,163,184,0)\");\n      tail.addColorStop(1, hot ? \"rgba(74,222,128,0.38)\" : \"rgba(148,163,184,0.28)\");\n      trailCtx.strokeStyle = tail;\n      trailCtx.lineWidth = hot ? 3 : 2;\n      trailCtx.beginPath();\n      trailCtx.moveTo(x, y - 42);\n      trailCtx.lineTo(x, y);\n      trailCtx.stroke();\n      trailCtx.fillStyle = hot ? \"rgba(74,222,128,0.92)\" : \"rgba(203,213,225,0.72)\";\n      trailCtx.fillRect(x - tokenWidth * 0.5, y - 2, tokenWidth, hot ? 7 : 3);\n    }\n  });\n  ctx.drawImage(trail, 0, 0, scaleX, scaleY);\n  const scanProgress = (time - scanStart) % 5900;\n  if (scanProgress <= 5000) {\n    const x = scaleX * (scanProgress / 5000);\n    const scan = ctx.createLinearGradient(0, 0, 0, scaleY);\n    scan.addColorStop(0, \"rgba(203,213,225,0)\");\n    scan.addColorStop(0.46, \"rgba(203,213,225,0.10)\");\n    scan.addColorStop(0.54, \"rgba(74,222,128,0.34)\");\n    scan.addColorStop(1, \"rgba(74,222,128,0)\");\n    ctx.fillStyle = scan;\n    ctx.fillRect(x, 0, 1, scaleY);\n  }\n  window.__DEMO__.frames += 1;\n  fallbackPending = false;\n  raf = requestAnimationFrame(frame);\n}\n\nfunction start() {\n  if (reduced.matches || running || !ctx) return;\n  running = true;\n  last = 0;\n  scanStart = performance.now();\n  window.__DEMO__.effects.push(\"cascade.tokens\", \"cascade.trails\", \"cascade.scan\");\n  raf = requestAnimationFrame(frame);\n}\n\nfunction stop() {\n  running = false;\n  if (raf) cancelAnimationFrame(raf);\n  raf = 0;\n}\n\nwindow.addEventListener(\"resize\", function() {\n  resizeCanvas();\n  if (reduced.matches) drawStatic();\n});\n\nreduced.addEventListener(\"change\", function() {\n  if (reduced.matches) {\n    stop();\n    drawStatic();\n  } else {\n    start();\n  }\n});\n\nresizeCanvas();\ndrawStatic();\nrequestAnimationFrame(() => requestAnimationFrame(() => {\n  window.__DEMO__.ready = true;\n  if (reduced.matches) return;\n  if (fallbackPending) {\n    canvas.style.opacity = \"1\";\n    document.querySelectorAll(\"[data-animate]\").forEach(function(element) {\n      element.style.opacity = \"1\";\n      element.style.transform = \"none\";\n    });\n  }\n  start();\n}));\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["At 1440x900, the document has exactly one visible h1 with text CASCADE, a semantic header, nav, main, and footer, and document.documentElement.scrollHeight is no greater than 900px.","The canvas backing buffer is capped at 2880x1620, window.__DEMO__.name equals cascade, ready becomes true within 2 seconds, frames is greater than 20 within 1.2 seconds, and effects includes cascade.tokens, cascade.trails, and cascade.scan.","At 390x844, 768x1024, 1440x900, and 1920x1080, document.documentElement.scrollWidth is no greater than innerWidth, every visible interactive element has an accessible name, and no element has a focus-visible outline thinner than 2px.","The page contains no img, video, audio, iframe, canvas-adjacent external asset, React, Tailwind, GSAP, Three.js, Lottie, or console output; all visible marks are generated by CSS, SVG, or canvas.","With prefers-reduced-motion: reduce, the canvas and foreground content are readable at opacity 1, no CSS entrance or scan animation is running, the JavaScript rAF loop does not continuously increment frames, and the telemetry object remains initialized with the exact required fields.","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"],"usage":{"build_tokens":58236,"build_prompt_tokens":7860,"build_completion_tokens":50376,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":3,"build_latency_ms":834920,"wasted_tokens":79720,"repair_tokens":0,"crosscheck_tokens":22570,"total_tokens":80806},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"36106 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"11 frames, effects: cascade.tokens, cascade.trails, cascade.scan"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"72 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 26.71, stdev 32.42, coverage 0.9999"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 4293200450 -> 3353705147"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"CASCADE\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"CASCADE — Log intelligence in motion"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=442 pageErrors=0"}]},"crosscheck":{"slug":"cascade","reviewer_model":"kimi-k2.8-preview","reviewer_platform":"workbuddy","verdict":"confirmed","confidence":"high","aesthetic_score":7,"requirements_met":["14-column token waterfall rendered behind UI with dark base gradient","Hero copy block on left with exact eyebrow, h1 CASCADE, statement, lede, buttons, proof row","Header with brand lockup, three nav links, Request access pill","Footer with exact left copy, centered ALL SYSTEMS OBSERVABLE status, copyright","Signal legend TRACE/BATCH/HOT ROW positioned bottom-right with correct dots","Hot rows rendered green with 76x14 wash visible in the field","Content veil, vignette, and layer stacking present; hero text sits on controlled dark veil","Token heads are short horizontal segments (56-86% column width), not full-width lines or Matrix rain","Two horizontal rules and restrained palette; no neon field, no glow overload","Staggered entrance keyframes (fade-up/fade-down/fade-in) with the specified durations and delays"],"requirements_missed":["Single scan line implementation: both a CSS .scan-runner (scan-sweep) and a JS-drawn scan sprite exist, producing two independently timed sweeps","Brief's token table is extended with non-specified variables (--header-bg-top, --vignette-edge, etc.) and header/footer/vignette use colors not routed through the mandated gradient tokens"],"issues":[{"issue":"Duplicate scan line: CSS-animated div and canvas-drawn scan sweep run simultaneously with different clocks, so two scan lines can be visible at once instead of one","severity":"medium","evidence":".scan-runner{animation:scan-sweep 5900ms linear infinite} plus frame(){ctx.drawImage(scanSprite, x, 0, 2, scaleY)}"},{"issue":"Signal legend text sits over the brightest token columns and its small grey mono labels have poor contrast against the dense white token field","severity":"low","evidence":"legend at right:100px bottom:24px renders as tiny dim text over the right-side comb of tokens in the screenshot"},{"issue":"Rightmost canvas region shows a dead gap: columns are squeezed by the 0.82 field factor, leaving the far-right edge emptier than the composition implies","severity":"low","evidence":"columnX uses fieldWidth * 0.82 * (0.045 + i * 0.0825)"},{"issue":"Hot-row uniqueness per column not guaranteed by the ordinal scheme; several columns show clusters of green rows while the spec says exactly one hot row visible per column","severity":"low","evidence":"plan.hot = ordinal % 29 === 0 with ordinal = columnIndex * 29 + residue"}],"summary":"The rendered page matches the brief's composition: restrained observability aesthetic, exact copy, left-anchored hero over a veiled 14-stream waterfall, correct chrome and footer. Main defects are a doubled scan-line implementation and a legend whose legibility drowns against the brightest token columns.","usage":{"input":19677,"output":2893,"total":22570,"reasoning":2197,"cached":0},"latency_ms":41147}},{"slug":"chrome-bloom","title":"Chrome Bloom","category":"Landing Page","effect":"A liquid-chrome metaball bloom drifts behind a conic-sheen italic wordmark","platform":"cmdcode","platform_label":"cmd-code","model":"MiniMaxAI/MiniMax-M3","model_label":"MiniMax-M3 (MiniMaxAI/MiniMax-M3)","demo":"/demos/chrome-bloom/index.html","doc":"/prompts/chrome-bloom.md","prompt_text":"Build one complete, self-contained `index.html` for **Bloom** — a generative brand studio landing page. Document title: `Bloom — Generative Brand Studio`. `<html lang=\"en\">`. ONE file only: plain HTML + CSS + JS in `<style>`/`<script>`. No frameworks, no bundlers, no external media of any kind (no images, no video, no fonts other than Google Fonts, no CDN scripts). Every visual pixel is generated by CSS gradients, inline SVG, and absolutely-positioned CSS circles composited through a `filter: blur() contrast()` chain. No `<canvas>`, no WebGL, no Three.js.\n\n**Viewport rule:** on viewports `>= 901px` wide the page is a single frame: `html, body { height:100%; overflow:hidden }` and `.page { height:100dvh; overflow:hidden }`. NO SCROLL on desktop. On viewports `<= 900px` the page may scroll vertically. No horizontal overflow at any width.\n\n**Do NOT build:** no `<canvas>`, no WebGL, no Three.js, no Lottie, no GSAP, no jQuery, no Tailwind, no React, no images (remote or base64), no audio, no forms, no pricing tables, no testimonials, no extra sections beyond the specified header / hero / footer, no more than 3 nav links, no more than 3 footer items, no cookie banner, no scroll indicator, no hamburger menu on desktop (menu only on `<=900px`).\n\n---\n\n## A. WHAT IS BEING BUILT\n\nA single-viewport landing page for a generative brand studio called **Bloom**. Behind a large italic serif wordmark sits a liquid-chrome form: exactly **6 circles** on independent orbital paths, blurred and composited through a single `filter: blur(28px) contrast(18)` chain so they read as ONE metaball mass rather than 6 separate blobs. The wordmark itself is a serif italic at large size with a conic-gradient chrome sheen applied via `background-clip: text`. A fine SVG grain overlay sits on top. A slow ~40-second hue drift rotates the chrome's tint. The frame is moody, expensive, liquid-metal — the studio calls itself a brand foundry and the page should feel like one. Header: small wordmark + 3 nav links. Hero: eyebrow chip, italic wordmark, single muted lede, one CTA. Footer: a three-item strip.\n\n---\n\n## B. THE BACKDROP — the chrome bloom (the ONLY animated visual besides the grain jitter)\n\nThe chrome bloom is a fixed-size composited layer, NOT a full-viewport element. It is one `<div class=\"bloom\">` at `position:fixed; left:50%; top:50%; width: min(82vw, 1180px); height: min(82vw, 1180px); transform: translate(-50%, -50%); z-index: 1; pointer-events: none; filter: blur(28px) contrast(18); mix-blend-mode: screen; will-change: transform; contain: paint;`. Its transform is animated by JS; the **filter is set once in CSS and never re-applied per frame**. Because the bloom sits over a near-black field, the `contrast(18)` amplifies blurred edges into hard chrome rims and the adjacent circles merge into one body.\n\n**The 6 circles.** Inside `.bloom`, six `<span class=\"orb orb--N\">` elements where N = 1..6. Each is `position:absolute; left:50%; top:50%; width: var(--orb-size, 38%); height: var(--orb-size, 38%); margin-left: calc(var(--orb-size)/-2); margin-top: calc(var(--orb-size)/-2); border-radius: 50%;`. Each circle is filled with a **radial-gradient chrome body** so even before compositing it looks like a polished bead. The gradient (the same family for every orb, different stop offsets) is:\n\n```\nbackground:\n  radial-gradient(circle at 32% 30%,\n    #ffffff 0%,\n    #f6f0e1 8%,\n    #e9d8a6 18%,\n    #b08a3a 38%,\n    #2a1d0a 62%,\n    #0a0703 88%,\n    #000000 100%);\n```\n\nThe 6 orbs have these sizes and starting offsets (orb-size is a percentage of the bloom container; x/y are the centerpoint offset as a percentage of container width/height; the JS orbit path will then add to these):\n\n```\norb--1  size 42%  x -18%  y  -8%   (large, upper-left mass)\norb--2  size 34%  x  22%  y -14%   (mid, upper-right)\norb--3  size 30%  x  10%  y  18%   (mid, lower-centre)\norb--4  size 26%  x -24%  y  14%   (small, lower-left)\norb--5  size 22%  x  26%  y   6%   (small, right)\norb--6  size 18%  x  -6%  y -22%   (tiny, top-centre)\n```\n\n**Orbits.** JS reads `--t` (seconds since start) and sets each orb's `transform` to `translate(calc(baseX + r * cos(theta)), calc(baseY + r * sin(theta)))` once per animation frame. The baseX / baseY are the offsets above; the radius `r` is a per-orb percentage of container size (so the path is responsive). The angle `theta = omega * t + phase`. Six named per-orb motion rules, each fully independent:\n\n```\norb--1  r 9%   omega 0.105 rad/s   phase 0.00\norb--2  r 7%   omega 0.146 rad/s   phase 1.10\norb--3  r 6%   omega 0.182 rad/s   phase 2.31\norb--4  r 5%   omega 0.218 rad/s   phase 3.47\norb--5  r 4.5% omega 0.260 rad/s   phase 4.62\norb--6  r 4%   omega 0.314 rad/s   phase 5.78\n```\n\nFrequencies are deliberately non-commensurate so the bloom never visibly repeats. Each orb is also given a faint `filter: brightness(0.92..1.08)` via its inline `--bri` custom property, set once in CSS and varied per orb to break perfect symmetry.\n\n**Performance budget for the filter.** The `filter: blur(28px) contrast(18)` chain is expensive; therefore the bloom container is:\n- a fixed pixel size that does NOT re-flow on scroll or resize-recalc,\n- promoted to its own composited layer via `will-change: transform`,\n- has `contain: paint`,\n- the filter is declared in CSS **once** — the rAF loop animates `transform` on the `.bloom` container (a slow 40s hue-rotation drift) and per-orb `transform` (the orbit positions). Filter is never re-applied per frame.\n- on `<= 900px` the bloom is shrunk: `width: min(120vw, 720px); height: min(120vw, 720px);` and `filter: blur(22px) contrast(16)` to keep mobile paint under budget.\n\n**Hue drift.** A second slow animation is applied to the `.bloom` container itself: `animation: hue-drift 40s linear infinite` where `hue-drift` keyframes go `0% { filter: blur(28px) contrast(18) hue-rotate(0deg); } 50% { filter: blur(28px) contrast(18) hue-rotate(-14deg); } 100% { filter: blur(28px) contrast(18) hue-rotate(0deg); }`. The hue-rotate sits at the end of the filter chain so the chrome mass subtly tints warmer / cooler without changing position. The filter is set in keyframes, not per frame, so the compositor can keep caching it.\n\n**Grain overlay.** A `.grain` element: `position:fixed; inset:-50%; width:200%; height:200%; pointer-events:none; z-index:5; opacity:0.07; mix-blend-mode: overlay;`. Background is an inline SVG `feTurbulence` rendered as a data-URI: `<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.92' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>`, `background-size: 180px 180px;`. Animation: `grain 1.1s steps(4) infinite` with keyframes jittering `transform: translate` among `(0,0) (-2%,1%) (1.5%,-2%) (-1%,2%)`. This is the second effect layer.\n\n---\n\n## C. LAYER STACK (back → front)\n\n1. `html, body` — near-black `#0a0a0c`, forced twice (see Failure Guards).\n2. `.vignette` — z-index 0, `position:fixed; inset:0; pointer-events:none;`, `background: radial-gradient(ellipse at 50% 50%, rgba(10,10,12,0) 0%, rgba(10,10,12,0) 55%, rgba(0,0,0,0.55) 90%, rgba(0,0,0,0.85) 100%);` — a deep edge falloff so the chrome bloom appears to float in a void.\n3. `.bloom` — z-index 1, the 6-orb liquid-chrome composited layer (per Section B).\n4. `.scrim` — z-index 2, a fixed `linear-gradient(180deg, rgba(10,10,12,0.55) 0%, rgba(10,10,12,0.12) 28%, rgba(10,10,12,0.10) 58%, rgba(10,10,12,0.78) 100%)` guaranteeing text contrast at the top and bottom of the frame.\n5. `.page` — z-index 3, `position:relative; display:grid; grid-template-rows: auto 1fr auto; min-height:100dvh` (height:100dvh inside the desktop lock).\n6. `.wordmark` sheen — z-index 4, the conic-gradient chrome sheen applied via `background-clip: text` on the h1 (described in section E).\n7. `.grain` — z-index 5, the SVG feTurbulence overlay.\n\n---\n\n## D. TOKEN TABLE (defaults at ~1440px)\n\n```\n--bg:            #0a0a0c\n--bg-2:          #14141a\n--text:          #f4ecdc\n--text-soft:     #d6c9a8\n--muted:         #8b8474\n--dim:           #56524a\n--accent:        #e9d8a6\n--accent-warm:   #f6e2a0\n--accent-deep:   #a07a2a\n--line:          rgba(244,236,220,0.16)\n--line-soft:     rgba(244,236,220,0.08)\n--chrome-hi:     #ffffff\n--chrome-mid:    #e9d8a6\n--chrome-lo:     #2a1d0a\n--logo:          15px\n--nav:           13.5px\n--nav-h:         36px\n--btn:           13.5px\n--btn-h:         44px\n--eyebrow:       12px\n--h1:            clamp(64px, 9.4vw, 156px)\n--h1-min:        44px\n--h1-max:        168px\n--lede:          17px\n--foot:          12.5px\n--header-y:      26px\n--header-x:      44px\n--hero-x:        44px\n--hero-y:        48px\n--copy-max:      1080px\n--lede-max:      560px\n--radius-pill:   999px\n--radius-soft:   14px\n--focus-w:       2px\n--focus-offset:  4px\n--bloom-blur:    28px\n--bloom-contrast:18\n--orb-1:         42%\n--orb-2:         34%\n--orb-3:         30%\n--orb-4:         26%\n--orb-5:         22%\n--orb-6:         18%\n--ease-out:      cubic-bezier(0.16,1,0.3,1)\n--enter-dur:     0.95s\n--soft-dur:      1.15s\n--drift-dur:     40s\n--grain-dur:     1.1s\n--serif:         \"Cormorant Garamond\", \"EB Garamond\", Georgia, \"Times New Roman\", serif\n--sans:          \"Inter\", system-ui, -apple-system, \"Segoe UI\", sans-serif\n```\n\nFonts (Google Fonts ONLY, with the fallbacks listed): `Cormorant+Garamond:ital,wght@1,400;1,500;1,600` and `Inter:wght@400;500;600`, loaded with one `<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>` and one stylesheet link with `display=swap`.\n\n---\n\n## E. THE COMPOSITION — exact build order\n\n**.page** is the grid container with three rows: `<header>`, `<main>`, `<footer>`.\n\n**HEADER** — `padding: var(--header-y) var(--header-x) 0; display:flex; align-items:center; justify-content:space-between; gap:24px`.\n\n- Left: `a.brand` (href `#`, accessible name \"Bloom home\"). It contains an inline SVG monogram 22×22 — a small chrome droplet: `<svg viewBox=\"0 0 22 22\" width=\"22\" height=\"22\" aria-hidden=\"true\"><defs><radialGradient id=\"bm\" cx=\"35%\" cy=\"30%\" r=\"70%\"><stop offset=\"0%\" stop-color=\"#ffffff\"/><stop offset=\"22%\" stop-color=\"#f6e2a0\"/><stop offset=\"55%\" stop-color=\"#a07a2a\"/><stop offset=\"100%\" stop-color=\"#0a0703\"/></radialGradient></defs><circle cx=\"11\" cy=\"11\" r=\"9\" fill=\"url(#bm)\"/></svg>` — followed by the wordmark text `bloom` in `var(--serif)`, italic, `font-size: var(--logo); font-weight:500; color: var(--text); letter-spacing:0.01em; font-style: italic`.\n- Centre: `nav` with exactly 3 links: `work`, `studio`, `journal`. Each `href=\"#\"`, `var(--sans)`, `font-size: var(--nav); color: var(--muted); font-weight:500; letter-spacing: 0.02em;`. Height `var(--nav-h)`, `display:flex; align-items:center; gap: 32px;`. Hover: `color: var(--text); transition: color 0.22s var(--ease-out);`.\n- Right: `a.cta` (href `#`, accessible name \"Start a project\"), text `Start a project`. Style: `display:inline-flex; align-items:center; height: var(--btn-h); padding: 0 22px; border-radius: var(--radius-pill); font-family: var(--sans); font-size: var(--btn); font-weight: 500; letter-spacing: 0.01em; color: #14110a; background: linear-gradient(180deg, #fbf2cf 0%, var(--accent) 48%, var(--accent-deep) 100%); border: 1px solid rgba(255,255,255,0.18); box-shadow: 0 18px 44px rgba(233,216,166,0.18), inset 0 1px 0 rgba(255,255,255,0.55);`. Hover: `transform: translateY(-1px); box-shadow: 0 22px 56px rgba(233,216,166,0.26), inset 0 1px 0 rgba(255,255,255,0.7); background: linear-gradient(180deg, #ffffff 0%, #f6e2a0 50%, #c89a3a 100%);` over 0.24s `var(--ease-out)`.\n\n**MAIN (hero)** — `display:flex; flex-direction:column; justify-content:center; align-items:center; padding: var(--hero-y) var(--hero-x) calc(var(--hero-y) + 24px); width:100%; max-width: var(--copy-max); margin: 0 auto; text-align:center; min-width:0;`. Vertically centred within the 1fr row.\n\n1. `.eyebrow` — `var(--sans)`, `font-size: var(--eyebrow); color: var(--accent); letter-spacing: 0.22em; text-transform: uppercase; font-weight: 500; margin: 0;`. Exact copy: `Generative Brand Foundry · Est. 2024`. The middle dot is `color: var(--dim)`.\n2. `h1` — EXACTLY ONE `<h1>`, `var(--serif)`, italic, `font-weight: 500; font-size: var(--h1); line-height: 0.96; letter-spacing: -0.025em; color: var(--text); margin: 24px 0 0; text-wrap: balance;`. The conic-gradient chrome sheen is applied to the h1 element via `background: conic-gradient(from 220deg at 50% 50%, #fbf2cf 0deg, #e9d8a6 70deg, #b08a3a 140deg, #f6e2a0 220deg, #ffffff 290deg, #a07a2a 340deg, #fbf2cf 360deg); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: transparent;`. The text is also given `text-shadow: 0 1px 0 rgba(0,0,0,0.35);` for legibility on bright bloom moments. The conic gradient is slowly rotated via `@keyframes sheen { from { background-position: 50% 50%; } to { background-position: 50% 50%; } }` — the sheen does NOT visibly animate (the wordmark stays reading as a single chrome surface); rotation is implicit because the conic is drawn around the text bounding box, not the viewport. Exact copy, a single line, max 2 lines on narrow screens: `Brands, in liquid metal.`\n3. `.lede` — `var(--sans)`, single paragraph, `font-size: var(--lede); line-height: 1.55; color: var(--text-soft); margin: 22px auto 0; max-width: var(--lede-max); font-weight: 400; letter-spacing: 0.002em;`. Exact copy: `Bloom designs living identity systems — marks, type, motion and chrome — for studios that ship faster than the brand book can keep up.`\n\n**FOOTER** — `padding: 18px var(--header-x) 22px; display:flex; align-items:center; justify-content:space-between; gap: 20px; border-top: 1px solid var(--line-soft); background: linear-gradient(180deg, rgba(10,10,12,0) 0%, rgba(10,10,12,0.55) 100%);`.\n\n- Left: mono-styled `var(--foot)`, `color: var(--muted); font-family: var(--sans); font-weight: 500; letter-spacing: 0.01em;`. Exact copy: `© 2026 Bloom Studio`.\n- Centre: a single `span.foot-mark` — `color: var(--muted); font-size: var(--foot); font-family: var(--sans); font-weight: 500;` with a 6×6 `display:inline-block; border-radius: 50%; background: var(--accent); box-shadow: 0 0 10px rgba(233,216,166,0.55); margin-right: 8px; vertical-align: 1px;` and the text `Now booking · Q2 2026`.\n- Right: exactly 3 links `dribbble`, `instagram`, `are.na`. `var(--sans)`, `var(--foot)`, `color: var(--muted); font-weight: 500; letter-spacing: 0.01em;`, `gap: 22px`, `href=\"#\"`. Hover: `color: var(--text); transition: color 0.22s var(--ease-out);`.\n\n---\n\n## F. THE UI LANGUAGE\n\nThere is exactly ONE button style in the page (the header CTA) — the chrome-pill: 999px radius, 1px `rgba(255,255,255,0.18)` border, a 3-stop linear gradient `#fbf2cf → #e9d8a6 → #a07a2a`, dark text `#14110a`, outer accent glow, inner top highlight. Hover: lift -1px, swap to a brighter gradient, deepen the glow over 0.24s `var(--ease-out)`. Nav links are plain sans text — no pills, no backgrounds, no underline ever; hover swaps colour to `var(--text)`. Footer links: same treatment in `var(--foot)`. Eyebrow is a tracking-heavy small-caps accent line. Selection: `::selection { background: rgba(233,216,166,0.30); color: #14110a; }`. Every interactive element gets `:focus-visible { outline: var(--focus-w) solid var(--accent); outline-offset: var(--focus-offset); border-radius: 6px; }`. No element on the page ever shows a default blue focus ring or a text underline.\n\n---\n\n## G. MOTION — entrance stagger + named keyframes\n\nAll entrance animations: `animation-duration: var(--enter-dur); animation-fill-mode: both; animation-timing-function: var(--ease-out); animation-delay: var(--d, 0.08s)`. The `.appear` class is applied to each animated element with a modifier class selecting keyframes and an inline `--d`.\n\nStagger table (element → modifier → --d):\n```\nbrand             appear--fade   0.08s\nnav link 1 work   appear--fade   0.16s\nnav link 2 studio appear--fade   0.22s\nnav link 3 journal appear--fade  0.28s\nheader cta        appear--lift   0.34s\neyebrow           appear--soft   0.50s\nh1                appear--bloom  0.66s   (duration 1.20s)\nlede              appear--soft   0.96s   (duration 1.10s)\nfooter left       appear--fade   1.18s\nfooter centre     appear--fade   1.24s\nfooter right      appear--fade   1.30s\n```\n\nNamed keyframes (all end at opacity 1 / identity transform):\n```\nin-fade   from { opacity: 0 }                                            to { opacity: 1 }\nin-soft   from { opacity: 0; transform: translateY(12px) }               to { opacity: 1; transform: none }\nin-lift   from { opacity: 0; transform: translateY(10px) scale(0.97) }   to { opacity: 1; transform: none }\nin-bloom  from { opacity: 0; transform: translateY(18px) scale(0.985); filter: blur(6px); }\n           to  { opacity: 1; transform: none; filter: blur(0); }\n```\n\nThe H1 `in-bloom` rise mirrors the way a molten form settles — the type enters with a faint blur that resolves into the chrome sheen. The CTA `in-lift` carries the only transform:translateY + scale on a button.\n\nContinuous keyframes:\n- `hue-drift` — see Section B. 40s, linear, infinite. Sits on `.bloom`. The filter is declared in keyframes (so it is cached by the compositor), not re-applied by JS.\n- `grain` — see Section B. 1.1s, steps(4), infinite. Sits on `.grain`. Only `transform` is animated.\n- `sheen` — declared on the h1 so the conic gradient's `background-position` is animated; in practice the conic is fixed relative to the text bounding box so the visible effect is a stable chrome surface with a barely-perceptible highlight drift. `0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } ` over 18s linear infinite. If this risks distracting from the wordmark it may be removed; the wordmark still reads as chrome without it.\n\nThe orb orbits, the hue-drift, the grain, and the sheen run continuously, independent of the entrance stagger. The orb orbit positions are written by JS per rAF tick (see Section J).\n\n---\n\n## H. RESPONSIVE TABLE — exact token overrides per breakpoint\n\n```\n>= 1600px:\n  --h1-max: 188px  --logo: 16px  --nav: 14px  --nav-h: 38px\n  --btn: 14px  --btn-h: 46px  --lede: 18px  --foot: 13px\n  --header-y: 32px  --header-x: 60px  --hero-x: 60px\n  --copy-max: 1240px  --lede-max: 620px\n\n>= 1920px:\n  --h1-max: 220px  --header-x: 76px  --hero-x: 76px\n  --copy-max: 1380px  --header-y: 38px  --lede: 19px\n\n1280px – 1599px:\n  --h1-max: 168px  --header-x: 48px  --hero-x: 48px\n  --copy-max: 1120px\n\n901px – 1279px:\n  --h1-min: 56px  --h1-max: 132px  --nav-h: 34px\n  --header-x: 32px  --hero-x: 32px\n  --copy-max: 920px  --lede: 16px  --lede-max: 520px\n\n>= 901px AND max-height 850px:\n  --h1-min: 52px  --h1-max: 120px  --hero-y: 28px\n  --header-y: 18px  --lede: 15px  (h1 margin-top 16px, lede margin-top 16px)\n\n>= 901px AND max-height 720px:\n  --h1-min: 44px  --h1-max: 96px  --hero-y: 18px\n  --header-y: 14px  --eyebrow: 11px  --lede: 13.5px  --btn-h: 38px\n\n>= 901px (desktop lock, all heights):\n  html, body { height:100%; overflow:hidden }  .page { height:100dvh; overflow:hidden }\n  .bloom { width: min(82vw, 1180px); height: min(82vw, 1180px);\n           filter: blur(28px) contrast(18); }\n\n<= 900px:\n  no vh lock; page may scroll; nav becomes a full-viewport overlay menu:\n  - header keeps brand + burger button (<button class=\"burger\" aria-label=\"Open navigation\" aria-expanded=\"false\">, three 18×1.5px bars in var(--text), gap 5px)\n  - .menu { position:fixed; inset:0; z-index:50; background: rgba(10,10,12,0.94);\n            backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);\n            display:flex; flex-direction:column; align-items:flex-start; justify-content:center;\n            padding: 0 32px; gap: 26px;\n            transform: translateY(-100%); transition: transform 0.45s var(--ease-out); }\n  - .menu.open { transform: none }\n  - menu shows the 3 nav links stacked at --nav: 22px plus the CTA pill\n  - Escape key, link click, and resize >900px all close it\n  --h1-min: 44px  --h1-max: 96px  --header-x: 24px  --hero-x: 24px\n  --copy-max: 100%  --lede: 16px\n  .bloom { width: min(120vw, 720px); height: min(120vw, 720px);\n           filter: blur(22px) contrast(16); }\n  .bloom { top: 56%; }   /* bias bloom down so wordmark reads on top of it */\n\n<= 560px:\n  --h1-min: 38px  --h1-max: 80px  --eyebrow: 11px\n  --lede: 15px  --header-x: 20px  --hero-x: 20px\n  footer becomes flex-direction:column; align-items:flex-start; gap:10px\n  header CTA is hidden (it lives inside the menu on mobile)\n  .bloom { width: min(160vw, 560px); height: min(160vw, 560px);\n           filter: blur(20px) contrast(15); }\n```\n\nThe orb sizes and orbital radii are CSS custom properties on the `.bloom` container; on <=900px they shrink to `--orb-1: 36%, --orb-2: 30%, --orb-3: 26%, --orb-4: 22%, --orb-5: 18%, --orb-6: 16%` and the JS orbit radii are divided by 1.3 so the metaball mass stays compact.\n\n---\n\n## I. FAILURE GUARDS — the page is never blank, never broken\n\n1. **Black-first:** the FIRST CSS rule is `html, body { background:#0a0a0c !important; color:#f4ecdc }`; the body tag carries `style=\"background:#0a0a0c;color:#f4ecdc\"`; a later rule restates `html, body { background:#0a0a0c; background: var(--bg, #0a0a0c) }`.\n2. **Resting opacity is 1:** every `.appear` element's un-animated computed style is its final state (opacity 1, no transform). Animations use `fill-mode: both` only to hold the FROM state during the delay. If CSS animations fail or JS never runs, the page renders complete and readable. The 6 orbs sit at their base offsets in CSS (no animation needed) and JS only adjusts `transform`. If JS never runs, the bloom is still visible.\n3. **rAF entrance fallback:** after two `requestAnimationFrame` ticks, for every `.appear` element check `el.getAnimations({ subtree: false })`; if none has `playState` `\"running\"` or `\"finished\"`, add `.is-in` to every `.appear` element. `.is-in { animation: none !important; opacity: 1 !important; transform: none !important; filter: none !important; clip-path: none !important }`. If the browser lacks `getAnimations`, perform the same `.is-in` fallback after the two rAFs.\n4. **Bloom fallback (no filter support):** if `CSS.supports('filter', 'blur(28px) contrast(18)')` returns false, JS adds `.no-bloom` to `<html>`. CSS must then show 6 absolutely-positioned CSS circles (no blur/contrast) with `opacity: 0.85; background: var(--accent-deep); border-radius: 50%;` so the wordmark still sits on a recognisable warm form. Page remains readable; telemetry `errors` receives a short string.\n5. **Reduced motion:** inside `@media (prefers-reduced-motion: reduce)`: `*, *::before, *::after { transition: none !important; animation: none !important; }`; force `.appear` to `opacity:1 !important; transform: none !important; filter: none !important; clip-path: none !important`; the JS orbit loop still runs once (paints the initial frame, sets `ready = true`, increments `frames` by 1) and then `cancelAnimationFrame`s itself; the `.bloom` filter keyframe `hue-drift` is disabled by the universal rule; the grain animation is hidden. The static frame must still show the 6 orbs at their base offsets so the page reads as designed.\n6. **No console output** in the happy path: no `console.log`, no thrown exceptions; every filter and animation property read is guarded.\n\n---\n\n## J. THE EXACT JS (only this, vanilla, one IIFE at end of body)\n\n1. **Telemetry first, before anything else:**\n   `window.__DEMO__ = { name: 'chrome-bloom', ready: false, frames: 0, effects: [], errors: [] };`\n   Push `'liquid-chrome'`, `'conic-sheen'`, `'hue-drift'`, `'grain-texture'`, and `'mobile-menu'` into `effects` once implemented (in that order). Add `window.addEventListener('error', e => window.__DEMO__.errors.push(String(e.message || e)))`.\n2. **Cache DOM references once:** `document.documentElement`, `.bloom`, all 6 `.orb` nodes (in order), `.grain`, `.burger`, `.menu`, all `.menu` links, and the result of `matchMedia('(prefers-reduced-motion: reduce)')`. Never call `getBoundingClientRect` inside the rAF loop. Cache the bloom container's `clientWidth` / `clientHeight` once at startup and recompute on resize only.\n3. **Orbit data table** (read-only constant in JS): an array of 6 objects `{ baseXPct, baseYPct, radiusPct, omega, phase, sizePct }` matching the orb sizes and starting offsets in Section B. On resize, divide every `radiusPct` by the orb size factor appropriate to the current breakpoint (1.0 desktop, 1.3 mobile) so the metaball mass stays compact.\n4. **rAF loop:** `requestAnimationFrame` reads `t = performance.now() / 1000`; for each of the 6 orbs, compute `x = baseX + radius * Math.cos(omega * t + phase)` and `y = baseY + radius * Math.sin(omega * t + phase)`; assign `orb.style.transform = `translate(${x.toFixed(2)}%, ${y.toFixed(2)}%)``. The bloom container transform is left to CSS keyframes. On the FIRST completed paint, set `window.__DEMO__.ready = true`. Increment `window.__DEMO__.frames` by exactly 1 each rAF tick. If `document.hidden`, skip the assignment and the paint, but keep the rAF alive. If `prefersReduced.matches`, paint exactly one frame then `cancelAnimationFrame` and return.\n5. **Resize handler:** one `resizeQueued` boolean; on `window.resize` set the flag and call `requestAnimationFrame` once; in the rAF callback recompute the cached bloom `clientWidth` / `clientHeight`, recompute breakpoint, update orb radius factors. No timers, no `getBoundingClientRect` per frame.\n6. **Entrance fallback:** after two nested `requestAnimationFrame` callbacks following `DOMContentLoaded`, iterate every `.appear` and check `el.getAnimations`; if no animation is running or finished, add `.is-in` to every `.appear`. If `getAnimations` is missing, add `.is-in` to every `.appear` after the same two rAFs.\n7. **Mobile menu:** burger click toggles `.menu.open` and `aria-expanded`; `Escape` key closes; any menu link click closes; `window.resize` to width > 900px closes and clears `aria-expanded`.\n8. **Feature support check:** at startup, if `CSS.supports('filter', 'blur(28px) contrast(18)')` is false, add `.no-bloom` to `<html>` and skip the rAF loop entirely; push a string into `errors` like `'filter-blur-contrast-unsupported'`.\n\nNo other JS. No libraries. No console calls. No `setTimeout` / `setInterval`. All motion is `requestAnimationFrame` or CSS keyframes.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["exactly one <h1> with the exact text 'Brands, in liquid metal.' rendered in an italic serif (Cormorant Garamond) with a conic-gradient chrome sheen via background-clip: text","a fixed .bloom container holds exactly 6 absolutely-positioned .orb circles whose transforms are updated per rAF; the container applies filter: blur(28px) contrast(18) once in CSS and is animated with transform only","window.__DEMO__ is initialized with name 'chrome-bloom', ready becomes true after the first painted orbit frame, frames increments once per rAF tick, effects contains 'liquid-chrome', 'conic-sheen', 'hue-drift', 'grain-texture', and 'mobile-menu', and errors stays empty in the happy path","exactly 3 nav links, 1 CTA, 1 lede paragraph and 3 footer items exist with accessible names; every anchor and button has a visible :focus-visible ring and the page is single-viewport (height:100dvh; overflow:hidden) at >=901px","no horizontal overflow at 390px, 768px, 1440px, or 1920px; the prefers-reduced-motion reduce block keeps the page readable and static; the entire index.html is under 90 KB and uses Google Fonts as the only network request","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"],"usage":{"build_tokens":18097,"build_prompt_tokens":8990,"build_completion_tokens":9107,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":64067,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":20679,"total_tokens":38776},"verification":{"passed":false,"passed_checks":17,"total_checks":18,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"26923 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"8 frames, effects: liquid-chrome, conic-sheen, hue-drift, grain-texture, mobile-menu"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":false,"detail":"14 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Brands, in liquid metal.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Bloom — Generative Brand Studio"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=344 pageErrors=0"}]},"crosscheck":{"slug":"chrome-bloom","reviewer_model":"gpt-5.6-luna","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":5,"requirements_met":["Desktop composition uses the required header, centered hero, and three-part footer within one viewport","Header contains the Bloom brand, exactly three primary nav links, and the Start a project CTA","Hero copy matches the specified eyebrow, h1, and lede exactly","Footer copy and three social links match the brief","Six orb elements, radial chrome gradients, compositing filter, grain SVG, conic text sheen, and hue-drift keyframes are present","Entrance animations use the requested named keyframes, stagger delays, and easing","Mobile overlay menu structure and breakpoint behavior are implemented"],"requirements_missed":["The six orbs do not have their required base x/y offsets in CSS; those offsets exist only in JavaScript","The page adds an unrequested continuously pulsing footer status dot animation","The rendered bloom does not read as a single premium liquid-chrome mass; it reads as a brightly colored neon blob","The rendered wordmark is visually flat beige/gold rather than convincingly chrome-sheened","Mobile orb sizes at <=560px differ from the specified responsive table, using 32/28/24/20/16/14% instead of 36/30/26/22/18/16%","The hue-drift keyframes hard-code desktop blur and contrast values, overriding the specified mobile filter values during animation"],"issues":[{"issue":"The central bloom is visibly wrong for the requested liquid-metal aesthetic: it shows saturated yellow, red, and white neon regions rather than a unified warm chrome metaball surface.","severity":"high","evidence":"Screenshot: the bloom behind the heading is a high-saturation yellow/red/white shape with separate visually distinct zones, not a restrained gold liquid-chrome mass."},{"issue":"The bloom overwhelms and competes with the hero text instead of supporting it.","severity":"medium","evidence":"Screenshot: the brightest white and red bloom sits directly behind the eyebrow and h1, reducing the expensive, controlled contrast described in the brief."},{"issue":"Orb fallback positioning is not implemented in CSS as required.","severity":"medium","evidence":"The .orb rules set left:50% and top:50% but no base x/y transforms; the only offsets are computed in JS as baseXPct/baseYPct."},{"issue":"An extra animated effect violates the stated motion restriction.","severity":"medium","evidence":"The footer dot uses animation: pulse 2.4s ease-in-out infinite, while the brief permits the chrome bloom motion and grain jitter as the only animated visual effects."},{"issue":"The hue animation defeats the mobile filter tuning.","severity":"low","evidence":"@keyframes hue-drift always uses filter: blur(28px) contrast(18), including on mobile where the brief requires 22/16 or 20/15."}],"summary":"The structure and copy are largely faithful, but the screenshot fails the central visual promise: the supposed liquid chrome renders as an uncontrolled neon blob and the wordmark lacks a convincing chrome finish. Several implementation details also violate the specified motion and fallback behavior.","usage":{"input":19455,"output":1224,"total":20679,"reasoning":469,"cached":0},"latency_ms":12255}},{"slug":"data-tide","title":"Data Tide","category":"Landing Page","effect":"A flowing ribbon field that reacts to a simulated signal, with a floating glass panel","platform":"workbuddy","platform_label":"Workbuddy proxy","model":"glm-5.3","model_label":"GLM-5.3 (glm-5.3)","demo":"/demos/data-tide/index.html","doc":"/prompts/data-tide.md","prompt_text":"\nDESIGN BRIEF — \"Data Tide\"\n\nA. WHAT IS BEING BUILT\n\nBuild ONE complete, valid, self-contained 'index.html': a single-viewport dark landing page for\na fictional observability product called **Tide**. Document title (exact):\n'Tide — See the whole signal'. '<html lang=\"en\">'.\n\nThe page is: a deep blue-black ground (#05080d); a full-bleed PROCEDURAL ribbon field rendered\non a <canvas> as the cinematic backdrop (40 horizontal sine-summed 1px polylines that surge when\na travelling Gaussian envelope passes every 6 s); a fixed header with a liquid-metal pill nav; a\nvertically centred hero with a two-line masked <h1>, a lede, two CTAs, and ONE floating glass\npanel below the copy showing a fake live metric readout in monospace (ticks every 1.2 s) plus a\nsparkline drawn in inline SVG; and a three-item stats footer. That is the whole page. One frame.\nNo scroll on desktop.\n\nDO NOT BUILD — every one of these is a failure:\n- No video, no WebGL, no Three.js, no Lottie, no GSAP, no jQuery, no React, no Tailwind CDN.\n- No remote images, no .mp4/.webm/.gif, no base64 photo blobs, no third-party CDN of any kind.\n  Google Fonts (preconnect + one stylesheet) is the ONLY network request.\n- No extra sections, no feature cards, no logo strip, no pricing table, no testimonial, no FAQ,\n  no form, no newsletter input, no cookie banner, no second panel, no chart other than the one\n  sparkline, no footer beyond the three stats.\n- No scroll indicator, no cursor blob, no custom scrollbar, no marquee, no typing effect.\n- No vertical scrolling on desktop: at >=901px 'html, body { height:100%; overflow:hidden }'\n  and '.page { height:100dvh; overflow:hidden }'. Scroll is permitted ONLY at <=900px.\n- No text-shadow glows, no neon outlines on type, no drop shadows under the headline.\n- No console.log / console.warn / console.error anywhere in the happy path; no uncaught\n  exceptions; no network request other than Google Fonts.\n\nB. THE BACKDROP — PROCEDURAL RIBBON FIELD\n\nB.1 The canvas element\n  <canvas id=\"tide\" class=\"tide-canvas\" aria-hidden=\"true\"></canvas>\n  CSS: position:fixed; inset:0; width:100%; height:100%; display:block; z-index:0;\n  pointer-events:none; (transparent — the body ground shows through; never paint the ground\n  inside the canvas). It is fixed, not absolute, so it never moves during layout.\n\nB.2 Resolution policy (implement exactly)\n  const DPR = Math.min(window.devicePixelRatio || 1, 2);          // cap DPR at 2\n  plus a backing-store clamp: if (iw * ih * DPR * DPR > 3,400,000) lower DPR to\n  DPR = Math.sqrt(3400000 / (iw * ih)). Then:\n    canvas.width  = Math.max(1, Math.round(innerWidth  * DPR));\n    canvas.height = Math.max(1, Math.round(innerHeight * DPR));\n    ctx.setTransform(DPR, 0, 0, DPR, 0, 0);   // ALL drawing math stays in CSS pixels\n  Lines are 1px and must stay crisp, so render at full clamped DPR (no half-resolution trick).\n  Recompute on resize with a 150 ms debounce, and on orientationchange.\n\nB.3 Determinism — a seeded PRNG, not Math.random\n  The field must be identical on every load and across implementations. Use mulberry32 seeded\n  with 20260114:\n    function mulberry32(a) { return function () { a |= 0; a = (a + 0x6D2B79F5) | 0;\n      var t = Math.imul(a ^ (a >>> 15), 1 | a);\n      t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;\n      return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; }\n    var rnd = mulberry32(20260114);\n  Build exactly 40 ribbon records. For EACH ribbon i = 0..39, with u = i / 39, consume rnd()\n  IN THIS EXACT ORDER (11 calls per ribbon; never reorder, never skip):\n    1  mix   = rnd()        // colour mix along the blue->teal ramp\n    2  alpha = rnd()        // base stroke alpha\n    3  amp   = rnd()        // wave amplitude fraction\n    4  p1    = rnd() * 6.28318\n    5  p2    = rnd() * 6.28318\n    6  p3    = rnd() * 6.28318\n    7  w1    = 0.9  + 0.5  * rnd()     // rad/s\n    8  w2    = 0.5  + 0.35 * rnd()\n    9  w3    = 1.4  + 0.8  * rnd()\n    10 bAmp  = rnd()        // vertical bob fraction\n    11 bPh   = rnd() * 6.28318; bSp = 0.12 + 0.1 * rnd()   // bob speed from a 12th call\n  Derived per ribbon (compute once, at build time, and PRE-BUILD BOTH STROKE STRINGS so the\n  rAF loop performs ZERO string allocation):\n    frac  = 0.07 + 0.86 * u                        // vertical anchor, even spread\n    ampF  = 0.018 + 0.052 * amp                    // fraction of viewport height\n    alphaF= 0.10 + 0.34 * alpha\n    r     = Math.round(88 + 42 * mix); g = Math.round(138 + 62 * mix); b = Math.round(178 + 18 * mix)\n    base  = 'rgba(r,g,b,alphaF)'                   // cool steel-blue -> dim teal\n    hot   = 'rgba(58,224,198, min(0.72, alphaF * 1.7))'   // surge highlight colour\n  The 'hot' string is built once with the numeric value baked in (toFixed(3)).\n\nB.4 The wave shape (all in CSS px; W, H = innerWidth, innerHeight)\n  Wavelengths scale with width: k1 = 2π / (0.30·W), k2 = 2π / (0.13·W), k3 = 2π / (0.055·W).\n  Per ribbon, per sample x:\n    base = frac·H + bAmp'·H · sin(bSp·t + bPh)          where bAmp' = 0.008 + 0.008·bAmp\n    wave = 0.54·sin(k1·x − w1·t + p1)\n         + 0.30·sin(k2·x + w2·t + p2)\n         + 0.16·sin(k3·x − w3·t + p3)\n    y    = base + ampF·H · env(x) · wave\n  env(x) is the surge envelope (B.5). The three components travel in mixed directions (two\n  rightward, one leftward) at incommensurate speeds, so the field never visibly repeats.\n\nB.5 THE SURGE — a travelling Gaussian envelope, period exactly 6.0 s\n    p   = (t mod 6.0) / 6.0\n    cx  = (−0.15 + 1.30·p) · W                       // fully off-left at p=0, off-right at p=1\n    σ   = 0.085 · W\n    g(x)= exp(−0.5·((x−cx)/σ)²)\n    env(x) = 0.30 + 2.30·g(x)\n  At rest ribbons wobble at 30% amplitude; inside the envelope they lift to 260% — a visible\n  surge sweeping left to right every 6 seconds, lifting exactly the ribbons it passes.\n\nB.6 Two-pass stroke, per ribbon, per frame\n  ctx.lineWidth = 1; ctx.lineCap = 'round'; ctx.lineJoin = 'round' (set once per frame).\n  Pass 1 (full width): x from −STEP to W+STEP inclusive, step STEP = 10 CSS px at >900px width,\n  STEP = 14 at <=900px. strokeStyle = ribbon.base. One beginPath + moveTo + lineTo… + stroke.\n  Pass 2 (surge highlight, only when the window intersects the canvas):\n    window = [cx − 2.6σ, cx + 2.6σ] clamped to [−STEP, W+STEP]; same sampling step; same y\n    formula; strokeStyle = ribbon.hot (teal #3ae0c6, brighter alpha). This makes the surge read\n    as a bright band of tide moving through the field.\n  Draw count: DRAWN = 26 ribbons when innerWidth <= 900, else all 40 (the array still holds 40;\n  only the drawn count changes, so resizing never re-seeds).\n  Budget: 40 ribbons × ~148 samples × 2 passes ≈ 11,800 lineTo per frame at 1440px — trivial.\n  Cap the frame rate: skip the draw when (ts − lastDraw) < 15.5 ms. Clamp dt to 0.05 s so a tab\n  switch cannot teleport the field. If document.hidden, cancel the rAF and restart on\n  visibilitychange (reset the last-timestamp so dt starts clean).\n  Each rAF tick increments window.__DEMO__.frames — including ticks whose draw is skipped.\n\nB.7 Scrim (CSS, above canvas, below content)\n  .tide-scrim is a fixed div at z-index:1 with pointer-events:none and:\n    background:\n      radial-gradient(72% 60% at 50% 46%, rgba(5,8,13,0.62) 0%, rgba(5,8,13,0.38) 46%,\n                      rgba(5,8,13,0) 100%),\n      linear-gradient(180deg, rgba(5,8,13,0.60) 0%, rgba(5,8,13,0.10) 16%, rgba(5,8,13,0) 34%,\n                      rgba(5,8,13,0) 66%, rgba(5,8,13,0.14) 84%, rgba(5,8,13,0.64) 100%);\n  The centre ellipse is load-bearing: the hero copy and the glass panel sit inside it, so text\n  contrast wins no matter how bright the surge is. The top ramp protects the header; the bottom\n  ramp protects the stats. Values are not adjustable upward.\n\nB.8 Grain: SVG feTurbulence at 5%, static (zero per-frame cost)\n  Inline (NOT a data URI, NOT remote):\n    <svg class=\"grain-svg\" aria-hidden=\"true\" focusable=\"false\">\n      <filter id=\"tideGrain\" x=\"0\" y=\"0\" width=\"100%\" height=\"100%\">\n        <feTurbulence type=\"fractalNoise\" baseFrequency=\"0.9\" numOctaves=\"3\"\n                      stitchTiles=\"stitch\" seed=\"11\" result=\"n\"/>\n        <feColorMatrix type=\"saturate\" values=\"0\"/>\n      </filter>\n      <rect width=\"100%\" height=\"100%\" filter=\"url(#tideGrain)\"/>\n    </svg>\n  CSS: .grain-svg { position:fixed; inset:-4%; width:108%; height:108%; z-index:100;\n       opacity:0.05; mix-blend-mode:soft-light; pointer-events:none;\n       animation:grain-drift 7s steps(7, end) infinite; will-change:transform; }\n    @keyframes grain-drift {\n      0%   { transform: translate3d(0,0,0) }       14.3% { transform: translate3d(-1.1%, 0.9%, 0) }\n      28.6%{ transform: translate3d(0.8%, -1.2%, 0) } 42.9% { transform: translate3d(-0.6%, -0.7%, 0) }\n      57.1%{ transform: translate3d(1.0%, 0.6%, 0) } 71.4% { transform: translate3d(-0.9%, 1.1%, 0) }\n      85.7%{ transform: translate3d(0.5%, -0.5%, 0) } 100% { transform: translate3d(0,0,0) } }\n\nC. THE LAYER STACK (back -> front), with z-index\n\n  z 0    html, body            background:#05080d (see failure guards)\n  z 0    #tide                 fixed canvas, the ribbon field (transparent)\n  z 1    .tide-scrim           fixed vignette/scrim gradients\n  z 2    .page                 position:relative; display:grid; grid-template-rows:auto 1fr auto;\n                               min-height:100dvh; (desktop lock: height:100dvh; overflow:hidden)\n  auto   header / main / footer  children of .page, no z-index of their own\n  z 100  .grain-svg            feTurbulence grain, 5%, soft-light\n  Nothing else may create a stacking context above z 2 inside .page. The mobile menu overlay,\n  when open, sits at z-index:90 (below grain, above .page). The glass panel's\n  backdrop-filter samples the canvas and scrim beneath it — this is intended.\n\nD. TOKEN TABLE (declare all of these on :root; default values are the ~1440px design)\n\n  Colour\n    --bg: #05080d\n    --text: #ffffff\n    --muted: #9aa7b2              (8.1:1 on #05080d — safe for the lede)\n    --stat: #d3dae2\n    --accent: #3ae0c6\n    --warn: #f5b04a               (latency-up delta chip)\n    --border: rgba(255,255,255,0.16)\n    --border-soft: rgba(255,255,255,0.12)\n    --pill-line: rgba(198,208,220,0.55)\n    --pill-line-hover: rgba(235,242,248,0.9)\n    --glass-hi: rgba(16,26,38,0.55)\n    --glass-lo: rgba(8,13,20,0.34)\n    --grain-opacity: 0.05\n    --focus: rgba(94,230,210,0.9)\n\n  Type\n    --logo: 15.5px\n    --logo-mark: 22px\n    --nav: 14px\n    --nav-h: 40px\n    --btn: 13.5px\n    --btn-h: 40px\n    --h1: 46px\n    --lede: 15.5px\n    --badge: 12.5px\n    --stat-size: 13.5px\n    --panel-value: 40px           (big monospace readout)\n    --panel-label: 12.5px\n    --panel-mono: 12px\n    --spark-h: 56px\n\n  Space / geometry\n    --header-y: 22px\n    --header-x: 40px\n    --stats-x: 72px\n    --stats-y: 36px\n    --badge-mb: 24px\n    --h1-mb: 20px\n    --lede-mb: 28px\n    --actions-mb: 34px\n    --copy-max: 860px\n    --lede-max: 500px\n    --panel-w: 560px\n    --panel-pad: 22px\n    --panel-gap: 12px\n    --panel-radius: 18px\n    --nav-gap: 10px\n    --cta-gap: 14px\n    --hero-btn-gap: 12px\n    --stat-gap: 36px\n\n  Fonts\n    --font-sans: \"Inter\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto,\n                 Helvetica, Arial, sans-serif\n    --font-serif: \"Instrument Serif\", \"Iowan Old Style\", \"Palatino Linotype\", Palatino,\n                  Georgia, serif\n    --font-mono: \"IBM Plex Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace\n  Load with <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">,\n  <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>, and ONE\n  <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500&family=Instrument+Serif:ital@1&family=IBM+Plex+Mono:wght@400;500&display=swap\">.\n  If the request fails the system fallbacks above must still look intentional.\n\nE. THE COMPOSITION — build order\n\nE.1 <body style=\"background:#05080d;color:#fff\"> (inline attribute, belt and braces).\n    Also body { overflow-x:hidden } at every breakpoint — horizontal overflow is forbidden.\n\nE.2 <header class=\"site-header\">\n    padding: var(--header-y) var(--header-x); display:flex; align-items:center; gap:16px;\n    .brand { display:inline-flex; align-items:center; gap:10px; color:#fff; text-decoration:none }\n      .brand-mark { width:var(--logo-mark); height:var(--logo-mark); flex:0 0 auto }\n        An INLINE SVG, viewBox=\"0 0 22 22\": three horizontal wave paths, stroke\n        stroke=\"url(#tideGrad)\" stroke-width=\"1.5\" fill=\"none\" stroke-linecap=\"round\":\n          M3 6.5 C6 4.5, 9.5 8.5, 12.5 6.5 S18 4.5, 19.5 6.5   (opacity 1)\n          M3 11  C6 9,   9.5 13,   12.5 11  S18 9,   19.5 11   (opacity 0.66)\n          M3 15.5 C6 13.5, 9.5 17.5, 12.5 15.5 S18 13.5, 19.5 15.5 (opacity 0.38)\n        where #tideGrad is a linearGradient x1=0 y1=0 x2=1 y2=0 with stops\n        #3ae0c6 -> #6aa8ff. aria-hidden=\"true\" (the wordmark carries the name).\n      .brand-word { font-size:var(--logo); letter-spacing:0.14em; text-transform:uppercase;\n                    font-weight:500; white-space:nowrap }   -> text \"Tide\"\n    nav.nav-desktop { margin-left:auto; display:flex; align-items:center; gap:var(--nav-gap) }\n      ul { display:flex; align-items:center; gap:var(--nav-gap); list-style:none; margin:0; padding:0 }\n      Four <a class=\"pill appear\"> links, in this order and with these EXACT strings:\n        \"Product\"  \"Integrations\"  \"Pricing\"  \"Docs\"\n      Then <a class=\"btn btn-ghost btn-sm appear\"> with the string \"Start free\", separated\n      from the last pill by var(--cta-gap).\n    button.burger (hidden at >=901px): 40x40, aria-label=\"Open menu\", aria-expanded=\"false\",\n      aria-controls=\"mobile-menu\", border:1px solid var(--border), border-radius:7px,\n      background:transparent; three 1px x 16px white bars (spans) at 6px vertical spacing.\n\nE.3 <main class=\"hero\">\n    CSS: .hero { display:flex; flex-direction:column; align-items:center; justify-content:center;\n                 padding:8px 24px 20px; min-height:0 }\n    .hero-inner { width:100%; max-width:var(--copy-max); margin:0 auto; text-align:center;\n                  display:flex; flex-direction:column; align-items:center }\n    At >=901px only, .hero-inner { transform:translateY(-1.5vh) } (optical centring).\n    Children, in order:\n      1. p.badge.appear\n            inline-flex; align-items:center; gap:8px; margin:0 0 var(--badge-mb);\n            font-size:var(--badge); letter-spacing:0.08em; text-transform:uppercase;\n            color:var(--muted);\n            .badge-dot { width:5px; height:5px; border-radius:50%; background:var(--accent);\n                         box-shadow:0 0 10px rgba(58,224,198,0.75);\n                         animation:badge-pulse 2.4s ease-in-out infinite }\n            Copy (exact): \"New — Tide Signal Engine v2\"\n      2. h1.hero-title  (the ONLY h1 on the page)\n            font-size:var(--h1); font-weight:500; letter-spacing:-0.045em; line-height:1.12;\n            margin:0 0 var(--h1-mb); color:var(--text);\n            Exactly TWO <span class=\"headline-line\"> children, each display:block;\n            overflow:hidden; padding:0.06em 0.15em 0.14em; (the padding stops the italic serif\n            and the descenders from being clipped by the mask):\n              Line 1:  All your signals,\n              Line 2:  one <em>living</em> tide.\n            em { font-family:var(--font-serif); font-style:italic; font-weight:400;\n                 font-size:1.08em; letter-spacing:-0.03em; color:var(--muted) }   <-- NOT white\n      3. p.lede.appear\n            max-width:var(--lede-max); margin:0 0 var(--lede-mb); font-size:var(--lede);\n            line-height:1.62; color:var(--muted);\n            Copy (exact): \"Tide streams millions of datapoints a second into one living ribbon\n            field — so latency, errors and saturation show up as motion you can read before\n            they become incidents.\"\n      4. div.hero-actions { display:flex; gap:var(--hero-btn-gap); justify-content:center;\n                            align-items:center; flex-wrap:wrap; margin-bottom:var(--actions-mb) }\n            a.btn.btn-solid.appear -> \"Start streaming\"\n            a.btn.btn-line.appear  -> \"Book a live demo\"\n      5. THE GLASS PANEL — three nested elements (entrance / float / tilt must never share a\n         transform, so each gets its own node):\n           div.panel-wrap.appear.appear--panel    (entrance animation + perspective)\n             div.panel-float                      (infinite idle float)\n               div.glass                          (JS-driven tilt via custom props)\n         .panel-wrap { width:100%; max-width:var(--panel-w); perspective:1200px }\n         .panel-float { animation:panel-float 9s ease-in-out infinite;\n                        @keyframes panel-float { 0%,100% { transform:translateY(0) }\n                                                 50% { transform:translateY(-5px) } } }\n         .glass { position:relative; border-radius:var(--panel-radius);\n                  padding:var(--panel-pad); display:flex; flex-direction:column;\n                  gap:var(--panel-gap);\n                  background:linear-gradient(160deg, var(--glass-hi) 0%, var(--glass-lo) 100%);\n                  backdrop-filter:blur(22px) saturate(1.4);\n                  -webkit-backdrop-filter:blur(22px) saturate(1.4);\n                  box-shadow:0 30px 80px rgba(0,0,0,0.55), 0 4px 18px rgba(0,0,0,0.32);\n                  transform:rotateX(var(--tilt-x,0deg)) rotateY(var(--tilt-y,0deg));\n                  will-change:transform; }\n         THE 1PX GRADIENT BORDER — the mask trick, on .glass::before:\n           content:\"\"; position:absolute; inset:0; border-radius:inherit; padding:1px;\n           pointer-events:none;\n           background:linear-gradient(135deg, rgba(255,255,255,0.42) 0%,\n                        rgba(255,255,255,0.10) 28%, rgba(58,224,198,0.38) 58%,\n                        rgba(255,255,255,0.16) 100%);\n           -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);\n           -webkit-mask-composite:xor; mask-composite:exclude;\n         Inner sheen on .glass::after: content:\"\"; position:absolute; inset:0;\n           border-radius:inherit; pointer-events:none;\n           background:linear-gradient(180deg, rgba(255,255,255,0.09) 0%, transparent 34%);\n         Panel content, in order (all rows width:100%):\n           a) .glass-head { display:flex; justify-content:space-between; align-items:center }\n              span.metric-label { font-family:var(--font-mono); font-size:var(--panel-label);\n                color:var(--muted); letter-spacing:0.02em }  -> \"ingest.pipeline.latency\"\n              span.live { display:inline-flex; align-items:center; gap:6px;\n                font-family:var(--font-mono); font-size:10.5px; letter-spacing:0.14em;\n                color:var(--accent) }  -> \"LIVE\"  with span.live-dot (5px circle,\n                background:var(--accent), box-shadow:0 0 8px rgba(58,224,198,0.8),\n                animation:badge-pulse 2.4s ease-in-out infinite)\n           b) .metric-row { display:flex; align-items:baseline; gap:10px }\n              span#metricValue.metric-value { font-family:var(--font-mono);\n                font-size:var(--panel-value); font-weight:500; letter-spacing:-0.02em;\n                color:#ffffff; font-variant-numeric:tabular-nums; line-height:1.05 }\n                -> initial text \"238.6\"\n              span.metric-unit { font-family:var(--font-mono); font-size:14px;\n                color:var(--muted) } -> \"ms\"\n              span#metricDelta.metric-delta { margin-left:auto; font-family:var(--font-mono);\n                font-size:12px; padding:3px 8px; border-radius:6px; white-space:nowrap }\n                -> initial text \"▾ 1.2%\"\n                .metric-delta.down { color:var(--accent); background:rgba(58,224,198,0.10) }\n                .metric-delta.up   { color:var(--warn);   background:rgba(245,176,74,0.12) }\n           c) the sparkline — INLINE SVG, exact markup:\n              <svg class=\"spark\" viewBox=\"0 0 480 56\" preserveAspectRatio=\"none\" role=\"img\"\n                   aria-label=\"Latency sparkline over the last 48 samples\">\n                <defs><linearGradient id=\"sparkFill\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n                  <stop offset=\"0%\" stop-color=\"#3ae0c6\" stop-opacity=\"0.20\"/>\n                  <stop offset=\"100%\" stop-color=\"#3ae0c6\" stop-opacity=\"0\"/>\n                </linearGradient></defs>\n                <path id=\"sparkArea\" fill=\"url(#sparkFill)\"/>\n                <polyline id=\"sparkLine\" fill=\"none\" stroke=\"#3ae0c6\" stroke-width=\"1.5\"\n                          vector-effect=\"non-scaling-stroke\" stroke-linejoin=\"round\"\n                          stroke-linecap=\"round\"/>\n                <circle id=\"sparkDot\" r=\"2.5\" fill=\"#3ae0c6\"/>\n              </svg>\n              .spark { display:block; width:100%; height:var(--spark-h); min-width:0 }\n           d) .glass-foot { display:flex; justify-content:space-between; gap:8px 14px;\n              flex-wrap:wrap; font-family:var(--font-mono); font-size:var(--panel-mono);\n              color:var(--muted) }\n              Exactly three spans, each .sub with a <b> child (color:#d3dae2; font-weight:500;\n              font-variant-numeric:tabular-nums):\n                <b id=\"subP50\">184</b> p50 ms\n                <b id=\"subP95\">410</b> p95 ms\n                <b id=\"subErr\">0.03</b>% err\n         The panel must be keyboard-independent (no focusable controls inside it) and its\n         ticking numbers must NOT be in any aria-live region.\n\nE.4 <footer class=\"stats\">\n    padding:0 var(--stats-x) var(--stats-y);\n    ul { display:flex; align-items:center; justify-content:center; gap:var(--stat-gap);\n         flex-wrap:wrap; list-style:none; margin:0; padding:0 }\n    Exactly THREE <li class=\"stat appear\"> items:\n      <b>2.4M</b> datapoints per second\n      <b>140+</b> native integrations\n      <b>38ms</b> p50 query latency\n    .stat { font-size:var(--stat-size); color:var(--stat); letter-spacing:0.01em;\n            display:inline-flex; gap:7px; align-items:baseline; white-space:nowrap }\n    .stat b { color:#ffffff; font-weight:500; font-variant-numeric:tabular-nums }\n\nE.5 Mobile menu (<=900px only)\n    div#mobile-menu.mobile-menu[hidden] — position:fixed; inset:0; z-index:90;\n    background:rgba(4,8,13,0.72); backdrop-filter:blur(18px) saturate(1.2);\n    -webkit-backdrop-filter:blur(18px) saturate(1.2);\n    display:flex; flex-direction:column; align-items:center; justify-content:center; gap:18px;\n    Contains the same four links at font-size:22px plus \"Start free\" as a solid button.\n    Toggled by .burger; closed by: clicking any link, pressing Escape, or a resize to >=901px.\n    When open: body gets .menu-open { overflow:hidden } and aria-expanded flips to \"true\".\n\nF. THE UI LANGUAGE\n\nF.1 Liquid-metal pill (.pill) — the nav links\n    display:inline-flex; align-items:center; justify-content:center;\n    height:var(--nav-h); padding:0 18px; border-radius:7px;\n    overflow:hidden; position:relative; isolation:isolate;\n    border:1px solid var(--pill-line);\n    background:linear-gradient(105deg, #05070a 0%, #252c36 48%, #414b58 100%);\n    color:#e9edf2; font-size:var(--nav); font-weight:500; letter-spacing:0.005em;\n    text-decoration:none; transition:border-color .35s ease, background .35s ease,\n                                  box-shadow .35s ease, color .35s ease;\n    .pill::before { content:\"\"; position:absolute; inset:-1px; z-index:-1;\n      background:linear-gradient(115deg, transparent 30%, rgba(255,255,255,0.16) 50%,\n                                 transparent 70%);\n      transform:translateX(-120%); transition:transform .6s ease }\n    .pill:hover::before { transform:translateX(120%) }\n    .pill:hover { border-color:var(--pill-line-hover); color:#ffffff;\n      background:linear-gradient(105deg, #0e1319 0%, #333e4c 45%, #5b6a7a 100%);\n      box-shadow:0 0 18px rgba(160,210,230,0.16) }\n    .pill:focus-visible { outline:2px solid var(--focus); outline-offset:3px }\n\nF.2 Buttons (.btn)\n    display:inline-flex; align-items:center; justify-content:center;\n    height:var(--btn-h); padding:0 20px; border-radius:7px;\n    font-size:var(--btn); font-weight:500; letter-spacing:0.01em;\n    text-decoration:none; cursor:pointer; border:1px solid transparent;\n    transition:transform .3s ease, background .3s ease, border-color .3s ease,\n               box-shadow .3s ease, color .3s ease;\n    .btn-solid { background:linear-gradient(105deg, #e8eef2 0%, #ffffff 52%, #d3dce2 100%);\n                 color:#06090d; border-color:rgba(255,255,255,0.5) }\n    .btn-solid:hover { transform:translateY(-1px);\n                 box-shadow:0 8px 26px rgba(160,200,225,0.20) }\n    .btn-line  { background:rgba(255,255,255,0.02); color:#ffffff;\n                 border-color:var(--border) }\n    .btn-line:hover { border-color:rgba(255,255,255,0.42);\n                 background:rgba(255,255,255,0.06) }\n    .btn-ghost { background:linear-gradient(105deg, rgba(255,255,255,0.06) 0%,\n                 rgba(255,255,255,0.02) 55%, rgba(255,255,255,0.10) 100%);\n                 color:#ffffff; border-color:var(--pill-line) }   // header CTA, matches pills\n    .btn-sm { height:var(--btn-h); padding:0 16px; font-size:var(--btn) }\n    Hover lift is translateY(-1px) ONLY — nothing bounces, nothing scales on hover.\n\nF.3 Focus ring, global\n    :focus-visible { outline:2px solid var(--focus); outline-offset:3px; border-radius:inherit }\n    Never remove outlines; never set outline:0 without a replacement.\n\nF.4 Anti-flash rule (first three rules of the stylesheet, in this order)\n    1) html, body { background:#05080d !important; color:#ffffff; }\n    2) (the inline body attribute from E.1)\n    3) html, body { background:#05080d; background:var(--bg, #05080d); margin:0; padding:0;\n                    font-family:var(--font-sans); -webkit-font-smoothing:antialiased;\n                    text-rendering:optimizeLegibility; }\n\nG. MOTION\n\nG.1 Entrance system\n    .appear { opacity:1 }                        // RESTING STATE IS VISIBLE — see I\n    .appear { animation-duration:1.05s; animation-fill-mode:both;\n              animation-timing-function:cubic-bezier(0.16,1,0.3,1);\n              animation-delay:var(--d, 0.08s); }\n    .is-in { animation:none !important; opacity:1 !important; transform:none !important;\n             clip-path:none !important; filter:none !important; }\n\nG.2 Stagger table (element -> animation -> --d)\n    brand            in-scale   0.08s\n    nav pill 1       in-scale   0.16s\n    nav pill 2       in-soft    0.28s\n    nav pill 3       in-scale   0.40s\n    nav pill 4       in-soft    0.52s\n    header CTA       in-scale   0.34s\n    badge            in-pop     0.22s\n    h1 line 1        in-mask    0.42s\n    h1 line 2        in-mask    0.62s\n    lede             in-soft    0.82s   (animation-duration:1.25s)\n    solid CTA        in-btn     0.96s\n    line CTA         in-side    1.10s\n    glass panel      in-panel   1.20s   (animation-duration:1.15s, on .panel-wrap only)\n    stat 1           in-stat    1.32s\n    stat 2           in-stat    1.44s\n    stat 3           in-stat    1.56s\n    (the canvas has no entrance animation; it fades in once via .tide-canvas\n     { animation:in-fade 2.2s ease both } with --d 0s)\n\nG.3 Keyframes — every one ends at opacity 1 and the identity transform\n    @keyframes in-fade  { from { opacity:0 } to { opacity:1 } }\n    @keyframes in-scale { from { opacity:0; transform:scale(0.84) }\n                          to   { opacity:1; transform:scale(1) } }\n    @keyframes in-soft  { from { opacity:0; transform:translateY(14px) }\n                          to   { opacity:1; transform:translateY(0) } }\n    @keyframes in-mask  { from { opacity:0; transform:translateY(40%) }\n                          to   { opacity:1; transform:translateY(0) } }   // clipped by .headline-line\n    @keyframes in-pop   { 0%   { opacity:0; transform:scale(0.90) }\n                          70%  { opacity:1; transform:scale(1.03) }\n                          100% { opacity:1; transform:scale(1) } }\n    @keyframes in-btn   { from { opacity:0; transform:translateY(18px) scale(0.94) }\n                          to   { opacity:1; transform:translateY(0) scale(1) } }\n    @keyframes in-side  { from { opacity:0; transform:translateX(22px) }\n                          to   { opacity:1; transform:translateX(0) } }\n    @keyframes in-stat  { from { opacity:0; transform:translateY(20px) }\n                          to   { opacity:1; transform:translateY(0) } }\n    @keyframes in-panel { from { opacity:0; transform:translateY(26px) scale(0.97) }\n                          to   { opacity:1; transform:translateY(0) scale(1) } }\n    @keyframes badge-pulse { 0%,100% { opacity:1; transform:scale(1) }\n                             50%      { opacity:0.55; transform:scale(0.82) } }\n\nG.4 Runtime motion (not entrance)\n    panel-float: .panel-float translateY 0 -> -5px -> 0 over 9s ease-in-out, infinite.\n    panel tilt: JS-driven, max 2deg, damped (see J). Never a CSS transition — the damping IS\n    the animation.\n    metric ticker: text swaps every 1200 ms with no transition (tabular numerics prevent\n    layout jitter).\n    The ribbon field runs continuously at <= 60 fps (see B.6).\n\nH. RESPONSIVE TABLE — every breakpoint is a token override table\n\n  >=1600px\n    --logo:17px; --logo-mark:24px; --nav:15px; --nav-h:44px; --btn:15px; --btn-h:44px;\n    --h1:56px; --lede:18px; --badge:13.5px; --stat-size:15px; --panel-value:46px;\n    --panel-label:13.5px; --panel-mono:13px; --spark-h:64px; --header-y:28px; --header-x:64px;\n    --stats-x:96px; --stats-y:44px; --copy-max:980px; --lede-max:560px; --panel-w:620px;\n    --panel-pad:26px\n\n  >=1920px\n    --h1:64px; --lede:19px; --badge:14px; --stat-size:15.5px; --panel-value:52px; --panel-w:660px;\n    --panel-pad:28px; --spark-h:68px; --header-y:30px; --header-x:72px; --stats-x:120px;\n    --stats-y:52px; --copy-max:1060px; --lede-max:600px; --badge-mb:28px; --h1-mb:24px;\n    --lede-mb:32px; --actions-mb:38px\n\n  1280px – 1599px\n    --h1:50px; --header-x:48px; --copy-max:900px; --panel-w:580px\n\n  901px – 1279px\n    --h1:40px; --nav-h:36px; --copy-max:760px; --lede-max:440px; --panel-w:520px;\n    --panel-value:34px; --spark-h:48px; --stats-x:48px; --stat-gap:28px; --header-y:18px;\n    --badge-mb:18px; --h1-mb:16px; --lede-mb:22px; --actions-mb:26px\n\n  >=901px AND (max-height:850px)\n    --h1:38px; --lede:14.5px; --panel-w:480px; --panel-value:32px; --spark-h:44px;\n    --panel-pad:18px; --panel-gap:10px; --header-y:18px; --stats-y:26px; --badge-mb:16px;\n    --h1-mb:14px; --lede-mb:18px; --actions-mb:22px\n\n  >=901px AND (max-height:720px)\n    --h1:32px; --lede:13.5px; --badge:11.5px; --panel-w:440px; --panel-value:26px;\n    --panel-mono:11px; --panel-label:11.5px; --spark-h:32px; --panel-pad:14px; --panel-gap:8px;\n    --panel-radius:14px; --header-y:14px; --stats-y:20px; --badge-mb:10px; --h1-mb:8px;\n    --lede-mb:12px; --actions-mb:14px; --stat-gap:24px\n\n  >=901px DESKTOP LOCK (apply in the same block)\n    html, body { height:100%; overflow:hidden }\n    .page { height:100dvh; overflow:hidden }\n    ONE FRAME, NO SCROLL.\n\n  <=900px  (phone + small tablet — scrolling IS allowed here)\n    Remove the vh lock: html, body { height:auto; overflow-x:hidden; overflow-y:auto }\n    .page { height:auto; min-height:100dvh }\n    .nav-desktop { display:none }  .burger { display:inline-flex }\n    .hero { justify-content:flex-start; padding:16px 20px 56px }  .hero-inner { transform:none }\n    --h1:40px; --header-y:18px; --header-x:24px; --stats-x:24px; --stats-y:32px; --stat-gap:18px;\n    --copy-max:640px; --lede-max:420px; --panel-w:100%; --panel-pad:18px; --panel-value:34px;\n    --spark-h:48px; --badge-mb:18px; --h1-mb:16px; --lede-mb:22px; --actions-mb:26px\n    Menu overlay per E.5.\n\n  <=560px\n    --h1:34px; --lede:15px; --badge:12px; --stat-size:12.5px; --header-x:18px; --stats-x:18px;\n    --panel-pad:16px; --panel-value:30px; --spark-h:44px; --hero-btn-gap:10px\n    .hero-actions { flex-direction:column; align-items:stretch; width:100% }\n    .hero-actions .btn { width:100% }\n    .headline-line { padding:0.06em 0.10em 0.14em }\n    .stats ul { flex-direction:column; align-items:center; gap:10px }   // three stacked lines\n  No horizontal overflow at 390px, 768px, 1440px or 1920px: never set a width wider than 100vw,\n  keep .panel-wrap and .lede inside a 20px gutter, and let .headline-line shrink (it is a block,\n  so it wraps rather than overflows). body keeps overflow-x:hidden everywhere as the backstop.\n\nI. FAILURE GUARDS\n\n  I.1 Resting opacity 1. '.appear { opacity:1 }' is the base rule; the animation only ever\n      animates FROM 0. If CSS animations are unsupported, disabled, or interrupted, every\n      element is already in its final state. The page is NEVER blank.\n  I.2 animationend settlement. On each element's own 'animationend', add '.is-in' so the\n      element drops the animation and keeps its end state (prevents any post-animation\n      repaint from re-running the keyframe, and frees compositor layers).\n  I.3 Two-rAF fallback. After two nested requestAnimationFrames from DOMContentLoaded, for\n      every '.appear': if 'el.getAnimations().some(a => a.playState === \"running\" ||\n      a.playState === \"finished\")' is false, add '.is-in'. Guard with\n      'typeof el.getAnimations === \"function\"' — if absent, add '.is-in' to all.\n  I.4 Canvas fallback. If 'canvas.getContext(\"2d\")' returns null, hide #tide, keep the ground\n      + scrim, set '__DEMO__.ready = true', push 'tide-canvas-fallback' into effects, and\n      push nothing into errors. The rAF loop must STILL run (frames keeps counting), and the\n      metric ticker, sparkline, entrance fallback and menu must still work — the page must\n      still look finished (a dark page with a glass readout panel).\n  I.5 Panel guards. Every getElementById for the panel is null-checked; a missing sparkline\n      must not break the ticker. The tilt code only runs when\n      matchMedia('(hover: hover) and (pointer: fine)').matches — never on touch.\n  I.6 prefers-reduced-motion: reduce\n      @media (prefers-reduced-motion: reduce) {\n        *, *::before, *::after { transition:none !important; animation:none !important; }\n        .appear, .headline-line, .badge-dot, .live-dot, h1 em { opacity:1 !important;\n          transform:none !important; clip-path:none !important; filter:none !important; }\n        .grain-svg, .panel-float { animation:none !important }\n      }\n      Canvas under reduced motion: draw ONE static frame with t = 0 and the surge envelope\n      frozen at centre (cx = 0.5·W), then keep the rAF loop running ONLY to increment\n      __DEMO__.frames (no dt advance, no draw, no tilt). The metric ticker and sparkline DO\n      keep updating — they are a data readout, not motion. The visual result is a completely\n      static, fully readable page; telemetry still advances.\n  I.7 Contrast. --muted #9aa7b2 on #05080d is 8.1:1; --stat #d3dae2 and #fff are higher; the\n      delta colours #3ae0c6 and #f5b04a are >= 9:1 on the glass. Never lower text opacity\n      below these values.\n\nJ. THE JAVASCRIPT — exactly this much, nothing more\n\n  One IIFE at the end of <body>, 'use strict'. Order: (1) telemetry first —\n  window.__DEMO__ = { name:'data-tide', ready:false, frames:0, effects:[], errors:[] }, then a\n  window 'error' listener pushing String(e.message || 'error'); (2) entrance settlement and the\n  two-rAF fallback exactly per I.2/I.3; (3) mobile menu wiring exactly per E.5; (4) the seeded\n  PRNG and the 40 ribbon records exactly per B.3 (12 rnd() calls per ribbon, exact order);\n  (5) canvas sizing, draw, and the rAF loop per B.4-B.6 and subsection 8 below; (6) the metric\n  ticker and sparkline; (7) the pointer tilt; (8) boot — size the canvas, draw once, set\n  __DEMO__.ready = true after the first paint of the field, start the rAF loop. Use straight\n  quotes in real JS, never the typographic quotes used in this prose.\n\n  6. METRIC TICKER + SPARKLINE (ticks every 1.2 s)\n    Cache by id: metricValue, metricDelta, subP50, subP95, subErr, sparkLine, sparkArea,\n    sparkDot. Keep a ring buffer buf of NFULL = 48 samples. PREFILL it once at boot with the\n    SAME rnd() stream (do not re-seed; the remaining stream after the ribbons is deterministic)\n    using the recurrence v = min(322, max(178, prev + (rnd() - 0.5) * 22)), starting from\n    v0 = 238.6 — so the chart renders FULL on first paint, never empty. renderPanel() writes\n    everything (below). After prefill, call renderPanel() once, then setInterval(push, 1200).\n    A push takes the last value, computes nv with the same clamp formula using\n    (Math.random() - 0.5) * 22 (live randomness is fine here — the field's determinism is what\n    matters), appends, drops the head past 48, and calls renderPanel().\n    renderPanel():\n      - metricValue.textContent = cur.toFixed(1)\n      - delta: d = ((cur - prev) / prev) * 100; textContent = (d <= 0 ? '▾ ' : '▴ ') +\n        Math.abs(d).toFixed(1) + '%'; classList.toggle('down', d <= 0) and ('up', d > 0).\n      - subP50 = String(Math.round(cur * 0.77)); subP95 = String(Math.round(cur * 1.72));\n        subErr = (0.02 + cur / 9000).toFixed(2).\n      - sparkline: for each sample j, x = (j * 480) / 47; y = 50 - clamp((v - 170) / 160,\n        0, 1) * 44; build the points string 'x,y' joined by spaces for #sparkLine; build\n        #sparkArea d = 'M0,56' + 'Lx,y' for each sample + 'L480,56 Z'; set #sparkDot\n        cx=480 and cy to the last y. All numbers toFixed(1).\n    Every id lookup is null-checked; a missing sparkline must not break the ticker. The\n    ticking numbers are NOT in any aria-live region.\n\n  7. PANEL TILT — max 2deg, damped (I.5)\n    Only when matchMedia('(hover: hover) and (pointer: fine)').matches AND !reduce.\n    State: tX, tY (targets), cX, cY (current). On pointermove { passive:true } over window:\n      nx = e.clientX / innerWidth; ny = e.clientY / innerHeight;\n      tY = clamp((0.5 - nx) * 4, -2, 2);   // rotateY, leans toward pointer-x\n      tX = clamp((ny - 0.5) * 4, -2, 2);   // rotateX, leans toward pointer-y\n    window 'blur' resets tX = tY = 0. Inside the rAF loop each frame:\n      cX += (tX - cX) * 0.08; cY += (tY - cY) * 0.08;   // 8% of the gap per frame = damped\n      glass.style.setProperty('--tilt-x', cX.toFixed(3) + 'deg');\n      glass.style.setProperty('--tilt-y', cY.toFixed(3) + 'deg');\n    The tilt is clamped hard at ±2deg and driven by the damped custom properties only —\n    never a CSS transition, the damping IS the animation. The glass element is cached once;\n    if missing, tilt silently does nothing.\n\n  8. CANVAS + rAF LOOP (parameters already pinned in B.2, B.4 - B.6 — do not deviate)\n    resize(): apply B.2 (DPR cap + 3.4M pixel clamp, setTransform so drawing math stays in\n    CSS px; DRAWN = iw <= 900 ? 26 : 40; STEP = iw <= 900 ? 14 : 10), then draw(reduce) once.\n    Debounced 150 ms on resize; immediate on orientationchange.\n    draw(still): ctx.clearRect (the canvas is transparent — the body ground shows through;\n    never paint the ground inside the canvas), then the exact k1/k2/k3, σ, cx, envelope,\n    two-pass strokes and surge-window clamp from B.4-B.6.\n    frame(ts): rAF itself first; D.frames++ every tick; first tick initialises last/lastDraw\n    and returns; dt = min((ts − last)/1000, 0.05); if reduce return AFTER incrementing frames;\n    else t += dt, run the tilt damping step (7), skip the draw when (ts − lastDraw) < 15.5 ms,\n    otherwise lastDraw = ts and draw(false).\n    visibilitychange: hidden -> cancelAnimationFrame; visible -> reset last = 0 and restart.\n  8b. Canvas failure (I.4): if getContext('2d') returns null, hide #tide, still boot the rAF\n      loop (frames keep counting), the ticker/sparkline/menu/entrance must still work, and\n      push 'tide-canvas-fallback' into effects. Push nothing into errors.\n\n  9. Effects list — push each implemented effect name exactly once, in this order:\n     'ribbon-field', 'surge-envelope', 'glass-panel', 'metric-ticker', 'sparkline-svg',\n     'entrance-stagger', 'pill-shine', 'grain', then 'panel-tilt' only if tilt is active.\n     (If the canvas failed, replace 'ribbon-field'/'surge-envelope' with\n     'tide-canvas-fallback' in that position.)\n\n  No console.log anywhere. No unhandled promise. No network request other than Google Fonts.\n  No localStorage, no fetch, no timers other than the single 1200 ms ticker interval and the\n  150 ms resize debounce. File size target: under 90 KB (this page should land around 24-32 KB).\n\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the page contains exactly one <h1>, split into exactly 2 .headline-line elements each with overflow:hidden, and the footer contains exactly 3 .stat items","a #tide canvas exists with non-zero width and paints non-blank pixels, and window.__DEMO__.frames advances by at least 20 in 1.2s","the #metricValue textContent changes within 2.6s of load, and the #sparkLine points attribute contains exactly 48 coordinate pairs","no element overflows the viewport horizontally at 390, 768, 1440 and 1920 widths, and exactly one .glass panel exists","window.__DEMO__.effects includes 'ribbon-field', 'surge-envelope', 'metric-ticker' and 'glass-panel', and window.__DEMO__.errors is empty","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"],"usage":{"build_tokens":24841,"build_prompt_tokens":13157,"build_completion_tokens":11684,"build_reasoning_tokens":332,"build_calls":1,"build_attempts":1,"build_latency_ms":73926,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":26792,"total_tokens":51633},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"34244 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"6 frames, effects: ribbon-field, surge-envelope, glass-panel, metric-ticker, sparkline-svg, entrance-stagger, pill-shine, grain, panel-tilt"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"35 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 20.39, stdev 52.28, coverage 0.1342"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 557258054 -> 2006951307"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"All your signals,\none living tide.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Tide — See the whole signal"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=472 pageErrors=0"}]},"crosscheck":{"slug":"data-tide","reviewer_model":"glm-5.3-flash","reviewer_platform":"workbuddy","verdict":"confirmed","confidence":"high","aesthetic_score":8,"requirements_met":["Single self-contained index.html, correct title and lang, Google Fonts as only network request","Full token table on :root with all specified values","Seeded mulberry32(20260114), 40 ribbon records, 12 rnd() calls in exact order, pre-built stroke strings","DPR cap at 2 plus 3.4M pixel clamp, setTransform to CSS px, 150ms debounced resize + orientationchange","Surge envelope: 6.0s period, Gaussian sigma 0.085W, env 0.30 + 2.30g, two-pass stroke with teal hot highlight and clamped window","Deterministic 48-sample prefill from the same rnd() stream; ticker every 1200ms with exact delta/sub formulas and sparkline geometry (x=j*480/47, y=50-clamp*44)","Glass panel: mask-composite 1px gradient border, ::after sheen, backdrop-filter, three-node wrap/float/glass transform separation","Stagger table matches exactly (in-scale/in-soft alternation on pills, in-pop badge, in-mask h1 lines, 1.25s lede, 1.15s panel, in-stat footer)","Desktop lock at >=901px (height:100%, overflow:hidden) plus max-height token overrides; scrolling permitted only <=900px","Mobile menu at z-index:90 with Escape, link-click, and >=901px-resize close; aria-expanded flips","Reduced-motion: static frame at cx=0.5W, animations killed, ticker/sparkline continue, rAF only counts frames","Effects list pushed in required order with conditional panel-tilt and tide-canvas-fallback substitution","Zero console output, no forbidden libraries/CDNs, damped tilt clamped at 2deg gated on hover:fine and !reduce"],"requirements_missed":[],"issues":[{"issue":"resize() only redraws immediately under reduced-motion; in normal mode a resize leaves the field stale until the next throttled rAF tick (up to 15.5ms) — brief section 8 says resize should 'draw(reduce) once' unconditionally","severity":"low","evidence":"scheduleResize(): 'sizeCanvas(); if (reduce) draw(true);' — draw omitted when not reduce"},{"issue":"Escape keydown always calls closeMenu even when the menu is already closed, and resize listener closes menu at >=901px on every resize event rather than only on transition — harmless but sloppier than specified behavior implies","severity":"low","evidence":"document.addEventListener('keydown', function (e) { if (e.key === 'Escape') closeMenu(); })"},{"issue":"Screenshot could not be visually inspected (no image input available), so rendered composition, scrim coverage, and ribbon rendering could only be verified from code","severity":"low","evidence":"review performed on source only"}],"summary":"The implementation is a near-literal transcription of the brief: seeded field math, envelope, stagger table, glass border mask, ticker recurrence, and breakpoint token overrides all match line-for-line. Only trivial deviations exist (resize redraw gating, overeager menu close handlers), and the visual composition cannot be independently confirmed since no screenshot was actually evaluated.","usage":{"input":25203,"output":1589,"total":26792,"reasoning":808,"cached":0},"latency_ms":40881}},{"slug":"echo","title":"Echo","category":"Landing Page","effect":"A rotating sonar sweep lights phosphor contacts over expanding ping rings on canvas","platform":"cmdcode","platform_label":"cmd-code","model":"xiaomi/mimo-v2.6-pro","model_label":"Xiaomi MiMo v2.6 Pro (xiaomi/mimo-v2.6-pro)","demo":"/demos/echo/index.html","doc":"/prompts/echo.md","prompt_text":"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.\n\n---\n\n## B. THE BACKDROP — procedural sonar scope (the ONLY visual effect besides grain)\n\nA 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.\n\n**Geometry (canvas 2D, no libraries):**\n\n- 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`.\n- 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`.\n- 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.\n\n**Graticule (drawn ONCE per resize into an offscreen canvas, blitted once per frame):**\n\n- 6 concentric range rings at `R * k / 6` for `k = 1..6`, `lineWidth 1`, stroke `rgba(92,255,157,0.13)`.\n- 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.\n- 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.\n- The offscreen canvas matches the main canvas backing size; per-frame cost is exactly one `drawImage`.\n\n**Ping rings — 4 expanding waves:**\n\n- 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).\n- Radius `r_k = R * (0.06 + 0.94 * pk^0.82)` — the exponent front-loads expansion so rings decelerate like a real wavefront.\n- 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.\n\n**Sweep and its fading trail:**\n\n- Leading angle `a = (t / 6.4) * TAU - PI/2` (`TAU = Math.PI * 2`; bearing 000 at top, clockwise; one revolution every 6.4s).\n- 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`.\n- 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.\n\n**Contact blips — 30 targets (22 at `<= 900px`, 16 at `<= 560px`):**\n\n- 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}`.\n- Ignition: track the previous sweep angle; when it crosses a contact's `ang` (wrap-aware), set `life = 1`.\n- Decay: `life = max(0, life - dt / 2.6)` — 2.6s fade, so one 6.4s revolution leaves ~12 lit contacts.\n- 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.\n\n**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.\n\n**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%)`.\n\n**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.\n\n---\n\n## C. LAYER STACK (back → front)\n\n1. `html, body` — deep navy `#030c16`, forced twice (see Failure Guards).\n2. `canvas#sonar` — z-index 0, the scope: graticule, pings, sweep, contacts, edge fade, vignette.\n3. `.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.\n4. `.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).\n5. `.grain` — z-index 5, above all content.\n\n---\n\n## D. TOKEN TABLE (defaults at ~1440px)\n\n```\n--bg:            #030c16\n--bg-2:          #061524\n--text:          #e7f3ef\n--text-soft:     #c3d7d4\n--muted:         #8ba7ae\n--dim:           #55727c\n--accent:        #5cff9d\n--accent-dim:    rgba(92,255,157,0.55)\n--accent-line:   rgba(92,255,157,0.28)\n--accent-fill:   rgba(92,255,157,0.06)\n--border:        rgba(180,225,214,0.14)\n--border-soft:   rgba(180,225,214,0.09)\n--logo:          15px\n--logo-mark:     20px\n--nav:           13.5px\n--nav-h:         38px\n--btn:           13.5px\n--btn-h:         42px\n--chip:          11.5px\n--chip-h:        28px\n--h1:            60px\n--lead:          16.5px\n--readout:       12.5px\n--foot:          12.5px\n--header-y:      24px\n--header-x:      40px\n--hero-x:        40px\n--hero-y:        40px\n--footer-y:      22px\n--copy-max:      560px\n--lead-max:      520px\n--h1-gap:        20px\n--lead-gap:      20px\n--action-gap:    28px\n--readout-gap:   26px\n--sans:          \"Space Grotesk\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif\n--mono:          \"IBM Plex Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace\n--ease-out:      cubic-bezier(0.16,1,0.3,1)\n--enter-dur:     0.95s\n```\n\nFonts (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`.\n\n---\n\n## E. THE COMPOSITION — exact build order\n\nBody: `margin:0; min-width:320px; font-family:var(--sans); -webkit-font-smoothing:antialiased` (navy-first rule per I.1).\n\n**HEADER** — `padding: var(--header-y) var(--header-x); display:flex; align-items:center; justify-content:space-between; gap:24px`.\n\n- 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}`).\n- 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`.\n- Right: `a.cta` (href `#`, class `btn btn--ghost btn--sm`) text `Request a sea trial`, height `var(--nav-h)`, padding 0 16px.\n\n**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%.\n\n1. `.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.\n2. `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):\n   Line 1: `Sound sees`\n   Line 2: `what light cannot.`\n   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`.\n3. `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.`\n4. `.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 `#`).\n5. `.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`).\n\n**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)`.\n\n- Left: mono `var(--foot)`, `color: var(--dim)`: `© 2026 Echo Marine Systems`.\n- Centre: mono `var(--foot)`, `color: var(--dim)`: `beacon: transmitting` with the same 7px status square (`margin-right:8px`) as the nav marker.\n- Right: exactly 3 links `research`, `voyages`, `contact` — mono `var(--foot)`, `color: var(--muted)`, `gap:22px`, href `#`, hover `color: var(--text)`.\n\n---\n\n## F. THE UI LANGUAGE\n\nOne 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.\n\n---\n\n## G. MOTION — entrance stagger + named keyframes\n\nAll 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`.\n\nStagger table (element → modifier → --d):\n```\nbrand            appear--fade   0.08s\nnav platform     appear--fade   0.16s\nnav fleet        appear--fade   0.24s\nnav science      appear--fade   0.32s\nheader cta       appear--chip   0.40s\nchip             appear--pop    0.52s\nh1 line 1        appear--mask   0.66s\nh1 line 2        appear--mask   0.80s\nlead             appear--soft   0.98s\nprimary button   appear--chip   1.14s\nghost button     appear--chip   1.22s\nreadout line     appear--read   1.34s  (duration 1.1s)\nfooter left      appear--fade   1.46s\nfooter centre    appear--fade   1.54s\nfooter right     appear--fade   1.62s\n```\n\nNamed keyframes (all end at opacity 1 / identity transform):\n```\nin-fade   from { opacity: 0 }                                          to { opacity: 1 }\nin-soft   from { opacity: 0; transform: translateY(14px); filter: blur(4px) } to { opacity: 1; transform: none; filter: blur(0) }\nin-mask   from { opacity: 0; transform: translateY(104%) }             to { opacity: 1; transform: none }\nin-chip   from { opacity: 0; transform: translateY(10px) scale(0.96) } to { opacity: 1; transform: none }\nin-pop    0% { opacity:0; transform: scale(0.9) } 70% { opacity:1; transform: scale(1.03) } 100% { opacity:1; transform:none }\nin-read   from { opacity: 0; clip-path: inset(0 100% 0 0) }            to { opacity: 1; clip-path: inset(0 -4% 0 0) }\n```\n\nH1 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.\n\nContinuous 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.\n\n---\n\n## H. RESPONSIVE TABLE — token overrides per breakpoint (exact)\n\n```\n>= 1600px:\n  --h1: 72px  --logo: 16px  --nav: 14.5px  --nav-h: 42px  --btn: 14.5px\n  --btn-h: 46px  --lead: 17.5px  --readout: 13px  --foot: 13px  --chip: 12px\n  --header-y: 28px  --header-x: 56px  --hero-x: 56px  --copy-max: 620px  --lead-max: 560px\n\n>= 1920px:\n  --h1: 84px  --header-x: 72px  --hero-x: 72px  --copy-max: 680px\n  --lead: 18.5px  --readout: 13.5px  --lead-max: 600px\n\n1280px – 1599px:\n  --h1: 64px  --header-x: 48px  --hero-x: 48px  --copy-max: 540px\n\n901px – 1279px:\n  --h1: 48px  --nav-h: 34px  --header-x: 32px  --hero-x: 32px\n  --copy-max: 460px  --lead: 15.5px  --readout: 12px\n\n>= 901px AND max-height 850px:\n  --h1: 44px  --hero-y: 24px  --header-y: 18px  --chip-h: 26px  --lead: 15px\n  --h1-gap: 14px  --lead-gap: 16px  --action-gap: 18px  --readout-gap: 16px\n\n>= 901px AND max-height 720px:\n  --h1: 38px  --hero-y: 14px  --header-y: 12px  --lead: 14px  --readout: 11.5px\n  --btn-h: 38px  --chip-h: 24px  --footer-y: 14px  --h1-gap: 12px  --lead-gap: 12px\n  --action-gap: 14px  --readout-gap: 12px\n\n>= 901px (desktop lock, all heights):\n  html, body { height:100%; overflow:hidden }  .page { height:100dvh; overflow:hidden }\n\n<= 900px:\n  no vh lock; page may scroll; nav becomes a full-viewport overlay menu:\n  - header keeps brand + burger button (<button class=\"burger\" aria-label=\"Menu\" aria-expanded=\"false\">, three 18×1.5px bars in var(--text), gap 5px)\n  - .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) }\n  - .menu.open { transform: none }\n  - 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\n  --h1: 40px  --header-x: 24px  --hero-x: 24px  --copy-max: 100%  --lead-max: 520px\n  --hero-y: 28px  --readout: 11.5px\n  .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)\n\n<= 560px:\n  --h1: 32px  --lead: 14.5px  --readout: 11px  --header-x: 20px  --hero-x: 20px\n  footer becomes flex-direction:column; align-items:flex-start; gap:10px\n  header CTA is hidden (it lives inside the menu on mobile)\n  the readout drops the DEPTH segment via span.opt { display:none }\n```\n\nCanvas 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.\n\n---\n\n## I. FAILURE GUARDS — the page is never blank, never broken\n\n1. **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) }`.\n2. **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.\n3. **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 }`.\n4. **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.\n5. **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.\n6. **No console output** in the happy path: no `console.log`, no thrown exceptions; every canvas call null-guarded.\n\n---\n\n## J. THE EXACT JS (only this, vanilla, one IIFE at end of body)\n\n1. **Telemetry first:**\n   `window.__DEMO__ = { name: 'echo', ready: false, frames: 0, effects: [], errors: [] };`\n   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'`.\n2. **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.\n3. **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).\n4. **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.\n5. **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`.\n6. **Entrance fallback:** two nested rAFs after DOMContentLoaded, then the `.is-in` sweep of section I.3.\n7. **Menu (mobile only):** burger click toggles `.menu.open` and `aria-expanded`; Escape, any menu link click, or resize to `>900px` closes it.\n\nNo other JS. No libraries. No timers. No console calls.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["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"],"usage":{"build_tokens":33938,"build_prompt_tokens":9286,"build_completion_tokens":24652,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":260791,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":36641,"total_tokens":70579},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"26567 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"11 frames, effects: sonar-sweep, ping-rings, contact-blips, range-graticule, grain-texture"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"40 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 11.39, stdev 4.06, coverage 1"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 1204085703 -> 2982020773"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Sound sees\nwhat light cannot.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Echo — Sonar Autonomy for the Deep"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=440 pageErrors=0"}]},"crosscheck":{"slug":"echo","reviewer_model":"glm-5.2","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":8,"requirements_met":["Full sonar canvas: graticule blit, 4 ping rings with pk^0.82 exponent, 8 trail buckets, leading edge + tip dot, contact glow+core, edge fade, vignette","Graticule: 6 range rings, 36 bearing ticks (major every 30°), 4 labels at R+26px, offscreen canvas blitted once per frame","30 contacts via mulberry32 seed 20260214, wrap-aware ignition, 2.6s decay, 64×64 glow sprite + core circle","Grain element with inline SVG feTurbulence data-URI, 0.9s steps(4), correct jitter translates","Layer stack z-indices correct: canvas 0, scrim 1, page 2, grain 5","Token table :root defaults match exactly","Header composition: brand SVG, blinking cursor, 3 nav links with marker on platform, CTA","Hero: chip, h1 with em on Sound, lead, two buttons, readout with accent values and opt span","Footer: copyright, beacon with 7px marker, exactly 3 links","Button styles: primary gradient, ghost, sm, active/focus-visible","Motion stagger table: all 15 elements with correct modifiers and delays","Named keyframes: in-fade, in-soft, in-mask, in-chip, in-pop, in-read, blink, pulse, grain","Copy text is exact and specific throughout (no filler)","Reduced motion: frozen frame at bearing 042°, pk=[0.18,0.42,0.66,0.90], life=0.55, frames=1","Canvas fallback: .no-canvas class + repeating-radial-gradient","Entrance fallback: two nested rAFs then .is-in sweep","Mobile menu: burger, overlay, Escape/link/resize close handlers","Telemetry hook exact shape; effects pushed in specified order","DPR caps: 2 desktop, 1.5 mobile; setTransform applied","Per-frame budget ≤78 primitives; zero ctx.filter/shadowBlur","Responsive overrides present for >=1600, >=1920, 1280-1599, max-height 850/720, <=900, <=560","Contact count scales: 30/22/16 at correct breakpoints","Graticule labels drop to 000/180 only at <=560px"],"requirements_missed":["901–1279px breakpoint token overrides entirely absent — no @media (min-width:901px) and (max-width:1279px) rule exists","Menu adds visibility:hidden and a visibility transition not specified in the brief"],"issues":[{"issue":"Missing 901–1279px breakpoint: at 1024px width, --copy-max stays 560px (should be 460px), --header-x/--hero-x stay 40px (should be 32px), --nav-h stays 38px (should be 34px). Copy column extends ~140px further into the scope area than designed.","severity":"medium","evidence":"No @media (min-width:901px) and (max-width:1279px) block in CSS; brief table specifies --h1:48px --nav-h:34px --header-x:32px --hero-x:32px --copy-max:460px --lead:15.5px --readout:12px for this range"},{"issue":"Menu panel uses visibility:hidden with a visibility transition delay not in the brief, which slightly alters the specified transition property list","severity":"low","evidence":"transition:transform 0.45s var(--ease-out), visibility 0s 0.45s — brief specifies only transition: transform 0.45s var(--ease-out)"}],"summary":"A highly faithful implementation that nails the sonar scope, motion choreography, copy, and token system — the missing 901–1279px breakpoint is the one concrete gap, causing a wider copy column and larger padding at common laptop widths. Premium look with one structural omission.","usage":{"input":18375,"output":18266,"total":36641,"reasoning":17290,"cached":0},"latency_ms":183750}},{"slug":"filament","title":"Filament","category":"Landing Page","effect":"Animated incandescent filament: SVG zig-zag wire stroked with a warm gradient inside a radial glass envelope; glow and colour pulse on a slow loop while a blurred heat-shimmer layer translates on a noise-like path.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/filament/index.html","doc":"/prompts/filament.md","prompt_text":"A. WHAT IS BEING BUILT\nYou are building a single-viewport cinematic landing page for a fictional hardware company named FILAMENT. The document title must be exactly \"FILAMENT — Incandescent Computing\". The html element must have lang=\"en\". The viewport meta tag is \"width=device-width, initial-scale=1\". At viewport widths of 901px and above, the body must be height:100dvh with overflow:hidden so the page never scrolls. At widths of 900px and below, the body must be min-height:100dvh with overflow-y:auto and overflow-x:hidden to permit vertical scrolling. There is exactly one h1 element on the page; it contains the text \"FILAMENT\". The page uses real header, nav, main, and footer elements. Every visible control has an accessible name: the two nav links are \"Specs\" and \"Pre-order\", and the primary call to action is a link with the text \"Reserve unit\". The logo in the header is a span with the text \"FILAMENT\" and is not a heading. Do NOT build: any scrollable sections on desktop, any additional pages or sections beyond header/main/footer, any external media such as mp4, webm, gif, or remote images, any CDN links, any base64 photo blobs, any React, Tailwind, GSAP, Three.js, or Lottie dependencies. The only permitted network request is the Google Fonts stylesheet for Inter with a system fallback stack of system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif. No other network requests are allowed.\n\nB. THE BACKDROP\nThe backdrop consists of three stacked elements behind all content. First, a fixed div with class \"vignette\" covering the viewport at z-index 0. It uses a background of radial-gradient(circle at 50% 45%, rgba(251,191,36,0.08) 0%, rgba(251,191,36,0.02) 30%, rgba(251,191,36,0) 70%) layered over the solid base colour #050505. This creates the near-black environment with a faint 8 percent accent glow centred slightly above the bulb. Second, a canvas element with id \"dust\" sits at z-index 1. It is fixed, display:block, width:100%, height:100%, and pointer-events:none. The canvas resolution trick is mandatory: the JavaScript resize handler sets the canvas backing store to exactly Math.floor(window.innerWidth / 2) by Math.floor(window.innerHeight / 2) pixels. The CSS width and height stretch this half-resolution buffer to fill the screen, halving the fill-rate cost while remaining visually smooth for soft particles. The canvas renders 64 ember particles. To respect the per-frame budget, a single 16 by 16 pixel offscreen canvas is precomputed once at load: it contains a radial gradient dot from rgba(251,191,36,0.9) at the centre to rgba(251,191,36,0) at the edge. Inside the animation loop, the code never calls ctx.filter and never sets ctx.shadowBlur. It only calls ctx.clearRect(0,0,w,h) followed by 64 ctx.drawImage calls using integer coordinates. Each particle has a base position stored as a normalized 0 to 1 value, a speed between 0.0002 and 0.0005, a phase between 0 and 6.283, and a scale between 0.5 and 1.0. The horizontal drift is Math.sin(t * p.sp + p.ph) * 24 and the vertical drift is Math.cos(t * p.sp * 0.8 + p.ph) * 24, producing a slow noise-like wander. The per-frame cost budget for this canvas is 0.3 milliseconds or less: 64 drawImage calls plus one clearRect on a half-resolution surface. Third, the glass envelope is a div with class \"envelope\" at z-index 2. It is a perfect circle with width and height equal to var(--bulb-size), border-radius:50%, and a background of radial-gradient(circle at 50% 50%, rgba(255,255,255,0.03) 0%, rgba(255,255,255,0.01) 60%, rgba(255,255,255,0) 68%) combined with radial-gradient(circle at 50% 40%, rgba(251,191,36,0.15) 0%, rgba(251,191,36,0) 70%). It has a 1 pixel rim created by box-shadow: 0 0 0 1px rgba(251,191,36,0.25) inset and an outer atmospheric glow of box-shadow: 0 0 120px 20px rgba(251,191,36,0.12). The envelope is the only light source; the surrounding copy is lit by the radial falloff already present in the vignette and envelope layers.\n\nC. THE LAYER STACK\nThe stacking order from back to front is exactly:\nz-index 0: div.vignette (fixed, full viewport, near-black base and faint accent glow)\nz-index 1: canvas#dust (fixed, full viewport, 64 ember particles at half resolution)\nz-index 2: div.envelope (the glass sphere, absolutely centred, size var(--bulb-size))\nz-index 3: div.filament-wrap (absolutely centred inside the bulb footprint; contains the SVG)\n  inside filament-wrap: svg.filament at z-index 1 draws two posts and the zig-zag wire path with stroke=\"url(#wire-grad)\" and stroke-width 2.\n  inside filament-wrap: svg.shimmer at z-index 2 is a duplicate of the wire path with filter: blur(10px) applied via CSS, stroke #fbbf24, and opacity 0.6. It is translated by the JS loop.\n  inside filament-wrap: div.wire-glow at z-index 3 is a copy of the wire rendered as an SVG drop-shadow filter layer whose opacity is animated from 0.55 to 0.90 and back.\nz-index 4: main (contains the hero h1, tagline, and button; text is lit by the falloff)\nz-index 5: header (fixed top) and footer (fixed bottom) sit above main for navigation, both with z-index 10 in the normal flow but logically above the stage.\nAll decorative bulb elements have aria-hidden=\"true\" so they are not in the accessibility tree.\n\nD. A TOKEN TABLE\n:root {\n  --bg: #050505;\n  --accent: #fbbf24;\n  --accent-bright: #fde68a;\n  --accent-dim: #b45309;\n  --text: #f5f5f4;\n  --text-dim: #a8a29e;\n  --glass-rim: rgba(251,191,36,0.25);\n  --glass-glow: rgba(251,191,36,0.12);\n  --font-sans: 'Inter', system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n  --h1-size: clamp(3rem, 8vw, 7rem);\n  --h1-weight: 600;\n  --h1-tracking: -0.04em;\n  --body-size: clamp(1rem, 1.2vw, 1.125rem);\n  --btn-py: 0.875rem;\n  --btn-px: 1.75rem;\n  --btn-radius: 999px;\n  --nav-gap: 2rem;\n  --pad-x: clamp(1.5rem, 5vw, 4rem);\n  --pad-y: clamp(1.5rem, 4vh, 3rem);\n  --bulb-size: clamp(280px, 40vmin, 480px);\n  --wire-width: 2px;\n  --blur-shimmer: 10px;\n  --track-gap: 2rem;\n}\nEvery colour and size in the composition references these tokens. No hard-coded hex values appear outside the token block except inside SVG gradient stops, which use rgba(251,191,36,1) and rgba(180,83,9,1).\n\nE. THE COMPOSITION\nThe header is position:fixed with top:0, left:0, right:0, padding:var(--pad-y) var(--pad-x), display:flex, justify-content:space-between, align-items:center, and z-index:10. It contains a span.brand with font-weight:600, letter-spacing:0.02em, color:var(--text), and text \"FILAMENT\". The nav element has aria-label=\"Primary\" and contains two links: a with href=\"#specs\" and text \"Specs\", and a with href=\"#preorder\" and text \"Pre-order\". The nav uses display:flex and gap:var(--nav-gap).\nThe main element is position:relative, display:flex, flex-direction:column, justify-content:center, align-items:center, text-align:center, padding:var(--pad-y) var(--pad-x), min-height:100dvh, and z-index:4. The bulb div.envelope and its filament-wrap are position:absolute, top:50%, left:50%, transform:translate(-50%,-50%), width:var(--bulb-size), height:var(--bulb-size). Because they are absolutely positioned inside main, they sit behind the text content which is in normal flow with z-index relative to main's children. The hero block is a div.hero with margin-top:auto and margin-bottom:auto to vertically centre. Inside it, the h1 has font-size:var(--h1-size), font-weight:var(--h1-weight), letter-spacing:var(--h1-tracking), line-height:1, margin:0, color:var(--text), and text \"FILAMENT\". The tagline is a p.tagline with max-width:32ch, margin:1.5rem auto 0, font-size:var(--body-size), line-height:1.6, color:var(--text-dim), and text \"The first incandescent compute module. 64 threads of warm, analog-safe light.\" The call to action is an a.btn with href=\"#preorder\", margin-top:2.5rem, and text \"Reserve unit\". The SVG filament is built as follows: an svg with viewBox=\"0 0 200 120\" and width 100 percent. Two posts are line elements from (40,110) to (40,60) and (160,110) to (160,60) with stroke #a8a29e and stroke-width 3. The zig-zag wire is a path with d=\"M40 60 L60 30 L80 60 L100 30 L120 60 L140 30 L160 60\" filled none, stroke url(#wire-grad), stroke-width 2, stroke-linecap round, stroke-linejoin round. The linearGradient with id wire-grad goes from x1=\"0\" y1=\"0\" to x2=\"0\" y2=\"1\" and stops at 0 percent rgba(251,191,36,1) and 100 percent rgba(180,83,9,1). The shimmer is an identical svg with class shimmer and filter:blur(var(--blur-shimmer)).\nThe footer is position:fixed, bottom:0, left:0, right:0, padding:var(--pad-y) var(--pad-x), display:flex, justify-content:space-between, align-items:center, font-size:0.75rem, color:var(--text-dim), z-index:10. The left side is a p with text \"© 2025 Filament Hardware Inc.\" The right side is a div.status containing a span.dot with width 6px, height 6px, border-radius 50%, background var(--accent), display inline-block, margin-right 0.5rem, and box-shadow 0 0 6px var(--accent), followed by the text \"System nominal\". The status pill has background rgba(251,191,36,0.1), border 1px solid rgba(251,191,36,0.2), padding 0.25rem 0.75rem, border-radius 999px.\n\nF. THE UI LANGUAGE\nThe primary button .btn is display:inline-flex, align-items:center, justify-content:center, padding:var(--btn-py) var(--btn-px), border-radius:var(--btn-radius), background:linear-gradient(180deg, #fbbf24 0%, #d97706 100%), color:#1c1917, font-weight:500, text-decoration:none, border:1px solid rgba(255,255,255,0.1), box-shadow:0 4px 14px rgba(251,191,36,0.25), transition:transform 0.2s ease, box-shadow 0.2s ease. On hover it becomes transform:translateY(-1px) and box-shadow:0 6px 20px rgba(251,191,36,0.4). On focus-visible it receives outline:2px solid var(--accent) and outline-offset:3px. Nav links and footer text use color:var(--text) with transition:color 0.2s and become color:var(--accent) on hover. All focusable elements receive the same :focus-visible ring: outline:2px solid var(--accent), outline-offset:3px, border-radius:2px. The status pill uses the exact background and border specified in section E. There is no other chrome; the interface relies entirely on the button treatment and the warm accent glow for hierarchy.\n\nG. MOTION\nThe entrance stagger table is:\nspan.brand -> animation fade-slide-down, delay 0ms, duration 600ms, ease-out, fill both\nnav a:nth-child(1) -> animation fade-slide-down, delay 80ms, duration 600ms, ease-out, fill both\nnav a:nth-child(2) -> animation fade-slide-down, delay 140ms, duration 600ms, ease-out, fill both\nh1 -> animation fade-slide-up, delay 200ms, duration 700ms, ease-out, fill both\np.tagline -> animation fade-slide-up, delay 320ms, duration 700ms, ease-out, fill both\na.btn -> animation fade-slide-up, delay 440ms, duration 700ms, ease-out, fill both\nfooter -> animation fade-slide-up, delay 560ms, duration 700ms, ease-out, fill both\nThese animations are applied only when the body has the class \"animate\", which is added by JavaScript after the double-rAF ready hook. The base CSS for every animated element is opacity:1, so if JavaScript never runs the elements remain fully visible at their resting state. The named keyframes are:\n@keyframes fade-slide-down { from { opacity:0; transform:translateY(-8px); } to { opacity:1; transform:translateY(0); } }\n@keyframes fade-slide-up { from { opacity:0; transform:translateY(12px); } to { opacity:1; transform:translateY(0); } }\nThe permanent ambient motion is:\ndiv.wire-glow -> the JS drives style.opacity between 0.55 and 0.90 using a sine wave at period 3490ms; no CSS animation is used for opacity to respect the JS-only rAF contract.\nsvg.shimmer -> the JS drives style.transform translate between -3px and 3px horizontally and -2px and 2px vertically using sine and cosine at periods 5700ms and 4500ms respectively; the element also carries a CSS filter blur(10px).\ncanvas#dust -> the JS drives the 64 particles using the half-resolution drawImage loop described in section B. All three effects are pushed into window.__DEMO__.effects as the strings \"canvas-dust\", \"filament-glow\", and \"shimmer-drift\".\n\nH. A RESPONSIVE TABLE\nAt 390x844 (mobile portrait): --pad-x is overridden to 1.25rem, --bulb-size is overridden to 220px, --h1-size is overridden to clamp(2.5rem, 12vw, 4rem), body overflow-y is auto, and main min-height is auto with padding-top 6rem and padding-bottom 6rem.\nAt 768x1024 (tablet portrait): --pad-x is 2rem, --bulb-size is 300px, --h1-size is clamp(3rem, 9vw, 5rem), body scrolls vertically.\nAt 901px to 1439px (small desktop): height is 100dvh with overflow hidden, --bulb-size is 380px, --h1-size is clamp(4rem, 7vw, 6rem), --pad-x is 3rem.\nAt 1440x900 and 1920x1080 (large desktop): --bulb-size is 480px, --h1-size is 7rem, --pad-x is 4rem, and the footer status pill remains visible.\nMax-height breakpoint 1: @media (min-width:901px) and (max-height:700px) sets --bulb-size to 300px and --h1-size to clamp(2.5rem, 6vw, 4rem) to prevent overflow on short desktop windows.\nMax-height breakpoint 2: @media (min-width:901px) and (max-height:600px) sets --bulb-size to 240px and --h1-size to clamp(2rem, 5vw, 3rem), and reduces the tagline max-width to 28ch.\nNo horizontal overflow exists at any of the four tested sizes because all padding uses clamp() and the bulb is centred with translate(-50%,-50%) inside a flex column that never exceeds viewport width.\n\nI. FAILURE GUARDS\nResting opacity is guaranteed: every element that later animates starts with opacity:1 in the base stylesheet. The keyframes begin at opacity:0 and end at opacity:1 with animation-fill-mode:both, so the final resting state is opacity:1. If JavaScript is disabled or throws before adding the .animate class, no entrance animation is applied and the page is fully readable and static. The two-rAF fallback is implemented as requestAnimationFrame(function(){ requestAnimationFrame(function(){ window.__DEMO__.ready = true; document.body.classList.add(\"animate\"); }); }); ensuring the ready flag is set only after the first paint is committed. Reduced motion is honoured in full: the media query @media (prefers-reduced-motion: reduce) sets animation-duration to 0.01ms, animation-iteration-count to 1, transition-duration to 0.01ms, and forces opacity:1 and transform:none on every element. The JavaScript checks window.matchMedia(\"(prefers-reduced-motion: reduce)\").matches at startup; if true it does not start the rAF loop, does not push ambient effects, and sets window.__DEMO__.ready = true immediately after the double-rAF so the page remains static and readable with zero console output.\n\nJ. THE EXACT JS\nPlace the following script at the end of body. It uses no template literals and no external libraries.\nwindow.__DEMO__ = { name: 'filament', ready: false, frames: 0, effects: [], errors: [] };\nwindow.addEventListener('error', function(e) { window.__DEMO__.errors.push(String((e && (e.message || e.error)) || 'unknown')); });\n(function() {\n  var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n  var canvas = document.getElementById('dust');\n  var ctx = canvas.getContext('2d');\n  var w = 0, h = 0;\n  var dot = document.createElement('canvas');\n  dot.width = 16; dot.height = 16;\n  var dctx = dot.getContext('2d');\n  var g = dctx.createRadialGradient(8,8,0,8,8,8);\n  g.addColorStop(0,'rgba(251,191,36,0.9)');\n  g.addColorStop(1,'rgba(251,191,36,0)');\n  dctx.fillStyle = g;\n  dctx.fillRect(0,0,16,16);\n  var particles = [];\n  for (var i=0;i<64;i++) {\n    particles.push({ bx: Math.random(), by: Math.random(), sp: 0.0002 + Math.random()*0.0003, ph: Math.random()*6.283, sz: 0.5 + Math.random()*0.5 });\n  }\n  function resize() {\n    w = Math.floor(window.innerWidth / 2);\n    h = Math.floor(window.innerHeight / 2);\n    canvas.width = w;\n    canvas.height = h;\n  }\n  window.addEventListener('resize', resize);\n  resize();\n  var shimmer = document.getElementById('shimmer');\n  var glow = document.getElementById('wire-glow');\n  function frame(t) {\n    window.__DEMO__.frames++;\n    if (!reduce) {\n      ctx.clearRect(0,0,w,h);\n      for (var i=0;i<64;i++) {\n        var p = particles[i];\n        var x = (p.bx * w + Math.sin(t * p.sp + p.ph) * 24) | 0;\n        var y = (p.by * h + Math.cos(t * p.sp * 0.8 + p.ph) * 24) | 0;\n        ctx.drawImage(dot, x, y, 16 * p.sz, 16 * p.sz);\n      }\n      var shX = Math.sin(t * 0.0011) * 3;\n      var shY = Math.cos(t * 0.0014) * 2;\n      shimmer.style.transform = 'translate(' + shX.toFixed(2) + 'px,' + shY.toFixed(2) + 'px)';\n      var op = 0.55 + Math.sin(t * 0.0018) * 0.35;\n      glow.style.opacity = op.toFixed(3);\n    }\n    requestAnimationFrame(frame);\n  }\n  requestAnimationFrame(function() {\n    requestAnimationFrame(function() {\n      window.__DEMO__.ready = true;\n      document.body.classList.add('animate');\n      if (!reduce) {\n        window.__DEMO__.effects.push('canvas-dust');\n        window.__DEMO__.effects.push('filament-glow');\n        window.__DEMO__.effects.push('shimmer-drift');\n      }\n    });\n  });\n  if (!reduce) {\n    requestAnimationFrame(frame);\n  }\n})();\n\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["document.querySelectorAll('h1').length === 1 and the h1 textContent is 'FILAMENT'","window.__DEMO__ exists, window.__DEMO__.ready === true, and window.__DEMO__.errors.length === 0 after load","window.__DEMO__.frames >= 1 within 1.2 seconds and the canvas backing store width equals Math.floor(window.innerWidth / 2)","at a viewport of 1440x900, getComputedStyle(document.body).overflow === 'hidden' and document.documentElement.scrollWidth <= window.innerWidth (no horizontal overflow)","the elements #shimmer and #wire-glow receive inline transform and opacity updates respectively when not in reduced-motion, and remain untransformed in reduced-motion","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"],"usage":{"build_tokens":36003,"build_prompt_tokens":5695,"build_completion_tokens":30308,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":3,"build_latency_ms":543056,"wasted_tokens":75390,"repair_tokens":0,"crosscheck_tokens":0,"total_tokens":36003},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"16914 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"57 frames, effects: canvas-dust, filament-glow, shimmer-drift"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"77 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 4.1, stdev 27.58, coverage 0.0217"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 593351159 -> 3932778044"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"FILAMENT\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"FILAMENT — Incandescent Computing"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=173 pageErrors=0"}]},"crosscheck":{"slug":"filament","reviewer_model":"glm-5.2","reviewer_platform":"workbuddy","verdict":"unparsed","confidence":"low","summary":"","issues":[],"usage":{"input":0,"output":0,"total":0,"reasoning":0,"cached":0},"raw":""}},{"slug":"fringe","title":"Fringe","category":"Landing Page","effect":"A turbulence-cut pigment panel wipes across and occludes a monumental studio headline.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/fringe/index.html","doc":"/prompts/fringe.md","prompt_text":"A. WHAT IS BEING BUILT\n\nBuild one complete index.html for Sable & Weft, a print-and-textile studio working in pigment on heavy 12 oz cotton. The page is a single-viewport cinematic portfolio landing screen titled “Fringe — Sable & Weft”. Set the document language to lang=\"en\", the character encoding to UTF-8, and the responsive viewport meta tag to width=device-width, initial-scale=1. The page uses a 5/7 asymmetric editorial composition: copy occupies the left five columns and a 720 × 880 pigment panel occupies the right seven columns, extends beyond the right viewport edge by exactly 12% of its own width, and visually covers the final letters of the headline. The artwork must look like a torn printed textile swatch, not like a conventional rounded card.\n\nAt widths of 901 px and above, set html, body, and .site-shell to width:100%; height:100dvh; min-height:100dvh; overflow:hidden. At widths through 900 px, allow normal vertical document scrolling; never allow horizontal scrolling. The fixed visual field may become position:relative and preserve its aspect ratio, but all content must fit the listed responsive layouts without clipping. There is no routing, modal, menu drawer, carousel, form submission, video, image, WebGL scene, text input, or secondary page.\n\nDo NOT build a navigation drawer, account controls, cookie notice, testimonial slider, pricing table, CMS preview, contact modal, scroll-jacked sequence, video background, image gallery, loading percentage, cursor follower, WebGL renderer, framework bundle, or decorative element detached from the printed-swatch concept. Do not use remote media, external scripts, third-party CDN resources, embedded fonts other than Google Fonts, base64 raster content, animated GIFs, or stock imagery. Do not simulate paper with bitmap textures. Do not use repeated geometric holes, polygon clipping, CSS border-radius cards, or regular geometry as the panel edge; the visible perimeter must be authored by SVG turbulence displacement. Do not add console output. Do not create more than one h1.\n\nB. THE BACKDROP\n\nThe full viewport background is #12100C. Place one decorative .ambient field behind all content with position:absolute; inset:0; z-index:0; pointer-events:none. It uses two radial-gradient pools only: radial-gradient(circle at 13% 18%, rgba(122,139,92,0.10) 0, rgba(122,139,92,0.035) 23%, transparent 48%), and radial-gradient(circle at 82% 76%, rgba(217,201,163,0.075) 0, rgba(217,201,163,0.018) 28%, transparent 52%). The gradients are composited on #12100C and must remain subtle enough that #E8E2D6 type has at least 12:1 contrast against the darkest region.\n\nAdd exactly 3 fine registration rules to the ambient field: one horizontal rule from x=0 to x=100% at y=64px, one vertical rule at x=5.5556vw from y=0 to y=100%, and one vertical rule at x=91.1111vw from y=0 to y=100%. Each is 1 px, rgba(232,226,214,0.07), with no blur. These are alignment marks, not panel boundaries. Do not add more ambient elements.\n\nThe hero artwork is one 720 px × 880 px .swatch-wrap positioned in the right grid region. Internally it contains one SVG .edge-mask with width=720 height=880 viewBox=\"0 0 720 880\", defs, one mask, one path, one displacement filter, and one turbulence filter. The source path is a closed, hand-authored irregular contour: M 92 34 C 156 8 252 48 334 24 C 424 -2 508 18 584 8 C 650 0 698 38 688 88 C 678 138 718 182 704 232 C 690 284 724 334 708 388 C 694 436 724 492 700 540 C 678 584 714 632 690 676 C 670 716 692 770 654 806 C 622 838 584 822 548 852 C 506 886 462 862 416 874 C 366 888 328 866 284 874 C 228 884 188 850 142 846 C 94 842 58 812 66 762 C 74 714 22 676 34 628 C 44 588 10 544 28 500 C 46 454 4 410 24 362 C 42 316 0 274 20 224 C 38 180 54 144 48 104 C 42 70 58 46 92 34 Z.\n\nThe mask path is filled white. Apply filter=\"url(#fringe-displace)\" to that path. Define the displacement filter in user-space coordinates with x=\"-20%\" y=\"-20%\" width=\"140%\" height=\"140%\" and color-interpolation-filters=\"sRGB\". Inside it, define filter id=\"fringe-noise\" with x=\"-20%\" y=\"-20%\" width=\"140%\" height=\"140%\": feTurbulence type=\"fractalNoise\" baseFrequency=\"0.014 0.055\" numOctaves=\"4\" seed=\"17\" result=\"noise\"; then feDisplacementMap in=\"SourceGraphic\" in2=\"noise\" scale=\"34\" xChannelSelector=\"R\" yChannelSelector=\"G\". The mask uses mask-type:luminance. The displaced white shape reveals the pure-CSS panel only inside the ragged contour; everything outside must show the backdrop. The 12% crop comes from placing the swatch’s rightmost 86.4 px outside the viewport, not from additional masking.\n\nInside .swatch-art, use a conic-gradient(from var(--a), #1B2A2E 0deg, #7A8B5C 108deg, #D9C9A3 216deg, #1B2A2E 360deg). Layer two radial pools over it at 40% opacity: radial-gradient(circle at 28% 24%, rgba(217,201,163,0.95) 0, rgba(217,201,163,0.28) 24%, transparent 52%), and radial-gradient(circle at 72% 74%, rgba(27,42,46,0.95) 0, rgba(27,42,46,0.26) 30%, transparent 57%). The first begins at 40% opacity as a complete layer, and the second also begins at 40% opacity. Add exactly one inline SVG grain overlay above these layers, with opacity:0.09, mix-blend-mode:soft-light, pointer-events:none, and its own feTurbulence type=\"fractalNoise\" baseFrequency=\"0.82\" numOctaves=\"3\" seed=\"9\". The grain overlay is clipped by the same .edge-mask SVG instance and is not animated through filter primitives per frame.\n\nAnimate the swatch’s registered custom property --a from 0deg to 360deg over 26s linear infinite. The only live filter mutation is changing the displacement feTurbulence seed every 900 ms among the fixed sequence 17, 23, 31, 41, 53, 67, 79, 89. Restarting the browser’s SVG noise implementation makes the edge crawl like wet paper without reflowing text or recalculating DOM geometry.\n\nNo canvas is used. The intrinsic 720 × 880 SVG is the resolution mechanism: the browser rasterizes the mask at device-pixel-aware resolution and CSS scales the 720 × 880 art box. The right-edge crop is performed by the viewport and transform, never by changing SVG coordinates. The live per-frame budget is at most 3 composited properties across the hero: one registered conic-angle animation on the swatch art, one browser-owned opacity/transform reveal on the swatch, and the rAF callback itself. The JS rAF loop must not query layout, read computed styles, resize observers, canvas pixels, or element bounds. The 900 ms seed interval must change one attribute only when its timestamp is reached. The page should sustain at least 20 frames per 1.2 seconds on a 1440 × 900 headless viewport; the only required rAF bookkeeping is an increment and one timestamp comparison.\n\nC. THE LAYER STACK\n\nUse explicit z-index values and no stacking-context ambiguity. The body background layer is z-index:auto. The .ambient field is z-index:0. The global .shell is position:relative; z-index:1; height:100%; pointer-events:none so decorative overlap does not block controls. The header is z-index:10. The main hero is z-index:1. The hero copy column is z-index:1. The large h1 is z-index:1. The swatch-wrap is position:absolute within the hero’s right region and has z-index:2; this must sit above the h1 so the ragged edge occludes its final letters. The swatch shadow is contained inside the masked SVG and has z-index:0 within the swatch. The swatch grain overlay is z-index:3 within the swatch. The hero metadata rail is z-index:5. The footer is z-index:10. No visible element may have a negative z-index. The focus ring must draw above its control at z-index:11.\n\nThe h1 is positioned so the last 3 characters “WEFT” extend beneath the leftmost ragged region of the swatch at 1440 × 900. Do not solve overlap with reduced opacity. At the 1440 breakpoint, the text’s visual right edge is x=956 px, the swatch’s viewport position begins near x=744 px, and at least 54 px of “WEFT” is hidden by opaque panel pixels on first paint. The panel always remains fully opaque where its mask is white.\n\nD. TOKEN TABLE\n\nDefine every size and color in :root. Use these exact tokens:\n--bg: #12100C;\n--ink: #E8E2D6;\n--ink-muted: rgba(232,226,214,0.62);\n--ink-faint: rgba(232,226,214,0.38);\n--moss: #7A8B5C;\n--linen: #D9C9A3;\n--deep: #1B2A2E;\n--line: rgba(232,226,214,0.14);\n--line-soft: rgba(232,226,214,0.07);\n--shell-pad-x: 5.5556vw;\n--shell-pad-y: 32px;\n--header-h: 64px;\n--footer-h: 58px;\n--hero-h: calc(100dvh - 122px);\n--grid-columns: minmax(0, 5fr) minmax(0, 7fr);\n--grid-gap: 0px;\n--headline-size: clamp(84px, 8.1vw, 156px);\n--headline-leading: 0.78;\n--headline-tracking: -0.072em;\n--headline-weight: 600;\n--eyebrow-size: 11px;\n--eyebrow-leading: 1;\n--eyebrow-tracking: 0.19em;\n--body-size: 15px;\n--body-leading: 1.55;\n--meta-size: 10px;\n--meta-leading: 1.4;\n--meta-tracking: 0.14em;\n--rule-size: 1px;\n--swatch-w: 720px;\n--swatch-h: 880px;\n--swatch-crop: 86.4px;\n--swatch-top: -96px;\n--swatch-radius: 0px;\n--button-h: 42px;\n--button-pad-x: 18px;\n--pill-radius: 999px;\n--focus-width: 2px;\n--focus-offset: 4px;\n--ease-expo: cubic-bezier(.76,0,.24,1);\n--reveal-duration: 1.1s;\n--type-duration: 600ms;\n--grain-seed-ms: 900ms;\n--grain-rotate: 26s.\n\nUse border-box sizing globally. Use the system stack -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif for controls and the Google Font request for “DM Mono” only, with ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", monospace as fallback. Load the permitted font with display=swap and no other network request. The display face remains the system sans stack so a failed font request cannot alter the composition.\n\nE. THE COMPOSITION\n\nThe document contains one header, one main, and one footer in source order. Header .site-header uses position:absolute; top:0; left:0; right:0; height:var(--header-h); padding:0 var(--shell-pad-x); display:grid; grid-template-columns:5fr 7fr; align-items:center; border-bottom:var(--rule-size) solid var(--line-soft). Its first child is a linked wordmark with exact accessible name “Sable & Weft home” and exact visible text “SABLE / WEFT”. Set it to font:600 13px/1 var(--font-mono); letter-spacing:0.08em. Its second child is a nav with aria-label=\"Primary navigation\", display:flex; justify-content:flex-end; gap:28px. It contains exactly 3 links: “Selected work”, “Material practice”, and “Studio notes”. Each has exact visible copy in that capitalization, 11 px monospace text, and a 1 px bottom rule that expands from 0% to 100% on hover.\n\nMain .hero uses position:absolute; inset:var(--header-h) 0 var(--footer-h); height:var(--hero-h); display:grid; grid-template-columns:var(--grid-columns); overflow:visible. The left .copy-column occupies the 5-column track with padding:clamp(96px,15vh,152px) 0 72px var(--shell-pad-x); position:relative; z-index:1. The right .art-column is the 7-column track and has position:relative; z-index:2; pointer-events:none.\n\nInside .copy-column, place an eyebrow first with exact text “PRINT + TEXTILE STUDIO / EST. 2018”. It uses --eyebrow-size, --eyebrow-leading, --eyebrow-tracking, color:var(--moss), and margin:0 0 28px. The only h1 has exact content “MADE BY HAND. / HELD BY / WEFT.” Use three block-level lines, font-size:var(--headline-size), line-height:var(--headline-leading), letter-spacing:var(--headline-tracking), font-weight:var(--headline-weight), text-transform:uppercase, color:var(--ink), and width:min(100%, 960px). The line containing “WEFT.” must extend 72 px beyond the nominal 5-column boundary into the art track. The h1 margin is 0. Set paint-order:stroke fill; -webkit-text-stroke:0.35px rgba(232,226,214,0.08) to counter subpixel softness without reducing type contrast.\n\nPlace .lede below the h1 with max-width:410px; margin:34px 0 0; exact text: “Pigment, pressure and patience. We print limited editions onto heavy cotton for rooms that keep their marks.” Use --body-size and --body-leading, color:var(--ink-muted). Beneath it place .actions with display:flex; gap:10px; margin-top:28px. The primary control is an anchor with exact visible text “VIEW THE EDITIONS” and accessible name “View the editions”. The secondary control is an anchor with exact visible text “MATERIAL NOTES” and accessible name “Read material notes”. Do not add icons to either control.\n\nAdd .edition-note in the lower left with margin-top:clamp(48px,10vh,104px); display:grid; grid-template-columns:1fr 1fr; gap:24px; width:410px; border-top:1px solid var(--line); padding-top:14px. The first block has label “LATEST DROP” and value “No. 08 / WET PAPER”. The second has label “SUBSTRATE” and value “12 OZ COTTON”. Use label color:var(--ink-faint), value color:var(--ink), 10 px monospace, and 14 px vertical separation.\n\nThe .art-column contains the only .swatch-wrap. At 1440 × 900, place it at left:-126px relative to the art track, top:var(--swatch-top), width:var(--swatch-w), height:var(--swatch-h), and do not scale it. The viewport crop will hide exactly 86.4 px. At 1920 × 1080, left:-112px and top:-118px. The .edge-mask and .swatch-art are both position:absolute; inset:0; width:720px; height:880px. The art starts at clip-path:inset(0 0 0 100%). A masked drop impression is allowed as a second filled path inside the same mask at translate(10px, 14px), fill rgba(0,0,0,0.28), but it must not extend the 720 × 880 coordinate system or add a CSS shadow.\n\nPlace one .material-tag inside the art-column, not inside the masked swatch, at left:42px; bottom:70px; z-index:5; transform:translateX(5.5556vw). It is a 2-row caption with border-left:2px solid var(--linen); padding:2px 0 2px 12px; color:var(--ink). Exact rows: “DYE LOT 08 / OLIVE CHALK” and “HAND-PULLED / 18 VARIATIONS”. Use --meta-size, --meta-leading, --meta-tracking, and opacity:0.84. The tag remains above the panel and therefore is not occluded.\n\nFooter .site-footer is position:absolute; left:0; right:0; bottom:0; height:var(--footer-h); padding:0 var(--shell-pad-x); display:grid; grid-template-columns:5fr 7fr; align-items:center; border-top:1px solid var(--line-soft); color:var(--ink-faint); font:var(--meta-size)/var(--meta-leading) var(--font-mono); letter-spacing:var(--meta-tracking). The first cell has exact text “PORTLAND, OREGON / 45.5152° N”. The second cell has text-align:right and exact text “INDEPENDENT PRESS / 2025”. Neither is interactive.\n\nF. THE UI LANGUAGE\n\nBoth action anchors use height:var(--button-h), padding:0 var(--button-pad-x), border-radius:var(--pill-radius), display:inline-flex; align-items:center; justify-content:center; font:600 10px/1 var(--font-mono); letter-spacing:0.12em; text-decoration:none; transition:transform 180ms var(--ease-expo), background-color 180ms ease, color 180ms ease, border-color 180ms ease. The primary uses background:linear-gradient(110deg,#D9C9A3 0%,#B8AE87 100%); color:#12100C; border:1px solid #D9C9A3. On hover, transform:translateY(-2px); background:linear-gradient(110deg,#E8E2D6 0%,#D9C9A3 100%); border-color:#E8E2D6. On active, transform:translateY(0). The secondary uses background:transparent; color:var(--ink); border:1px solid rgba(232,226,214,0.28). On hover, transform:translateY(-2px); background:rgba(232,226,214,0.07); border-color:rgba(232,226,214,0.62). On active, transform:translateY(0).\n\nNavigation links have color:var(--ink-muted), text-decoration:none, and a pseudo-element with height:1px, bottom:-6px, left:0, width:0, background:var(--linen), transform-origin:right center, transition:width 220ms var(--ease-expo), transform 220ms var(--ease-expo). Hover and focus-visible use width:100% and transform-origin:left center. Every visible control receives a 2 px #D9C9A3 focus-visible outline with 4 px offset, never removed without replacement. Hover media queries must not hide focus.\n\nG. MOTION\n\nRegister the custom property with @property --a { syntax: \"<angle>\"; inherits: false; initial-value: 0deg; }. Animate .swatch-art with keyframes fringe-rotate { from { --a:0deg; } to { --a:360deg; } }, 26s linear infinite, animation-delay:-6.4s. Animate .swatch-wrap reveal with fringe-reveal { from { clip-path:inset(0 0 0 100%); transform:translate3d(64px,0,0); opacity:0.34; } to { clip-path:inset(0 0 0 0); transform:translate3d(0,0,0); opacity:1; } }, duration 1.1s, timing cubic-bezier(.76,0,.24,1), delay 120ms, fill-mode:both. The clip-path animation is permitted by the assignment as the one named reveal exception; during the ongoing rAF loop animate only transforms and opacity.\n\nThe exact initial stagger is:\n.brand -> fade-up -> 0ms\nnav -> fade-up -> 80ms\neyebrow -> fade-up -> 260ms\nh1 -> fade-up -> 700ms\nlede -> fade-up -> 860ms\nactions -> fade-up -> 980ms\nedition-note -> fade-up -> 1100ms\nmaterial-tag -> fade-up -> 1240ms\nfooter -> fade-up -> 460ms\nswatch-wrap -> clip wipe -> 120ms\nswatch-art -> conic rotation -> starts at 120ms and runs infinitely.\n\nDefine @keyframes fringe-up as from { opacity:0; transform:translate3d(0,18px,0); } to { opacity:1; transform:translate3d(0,0,0); }. Apply it for 600ms with cubic-bezier(.22,1,.36,1) and fill-mode:both. Define @keyframes fringe-rotate exactly as stated above. Define @keyframes fringe-reveal exactly as stated above. Do not add parallax, scroll-linked transforms, letter-by-letter effects, or looping movement to text. Do not alter DOM attributes except the one SVG turbulence seed every 900 ms.\n\nH. RESPONSIVE TABLE\n\nAt 1440 × 900, use the root values without breakpoint overrides. The expected headline maximum is 116.64 px from 8.1vw. The art remains 720 × 880 with left:-126px and top:-96px. The right crop remains 86.4 px.\n\nAt 1920 × 1080, override --shell-pad-y:36px; --headline-size:155.52px; --lede max-width:430px; .art-column .swatch-wrap { left:-112px; top:-118px; }. The footer stays bottom-anchored and the page remains exactly 100dvh.\n\nAt 768 × 1024 and all widths from 601 through 900, set html, body, and .site-shell { height:auto; min-height:100dvh; overflow-x:hidden; overflow-y:auto; }. Change .site-header to position:relative; height:72px; padding:0 32px; grid-template-columns:1fr; nav { display:none; }. Set .hero to position:relative; inset:auto; height:auto; min-height:calc(100dvh - 72px); display:block; overflow:visible; padding:68px 32px 48px. Set .copy-column { padding:0; }. Set --headline-size:clamp(72px,13.2vw,101px); --headline-leading:0.82; .copy-column h1 { width:100%; }. Set .swatch-wrap { position:absolute; left:auto; right:-86.4px; top:332px; width:540px; height:660px; }. Because the internal SVG remains 720 × 880 and the wrapper uses a 0.75 scale, preserve the torn contour and keep 64.8 px outside the viewport. Hide .material-tag at this breakpoint. Set .edition-note { margin-top:610px; }. Set .site-footer { position:relative; height:64px; padding:0 32px; grid-template-columns:1fr 1fr; }. This breakpoint scrolls vertically but has no horizontal overflow.\n\nAt 390 × 844, set --shell-pad-x:20px; --headline-size:58px; --headline-leading:0.84; --headline-tracking:-0.065em; --body-size:14px. Set .site-header { height:62px; padding:0 20px; }. Set .hero { min-height:1230px; padding:54px 20px 46px; }. Set .eyebrow { margin-bottom:22px; }. Set .lede { max-width:320px; margin-top:26px; }. Set .actions { flex-direction:column; align-items:flex-start; gap:9px; margin-top:24px; }. Set .swatch-wrap { top:548px; right:-57.6px; width:384px; height:469.333px; }. Preserve an 8% crop. Set .edition-note { position:absolute; left:20px; top:1080px; width:320px; margin-top:0; }. Hide .material-tag. Set .site-footer { position:relative; height:70px; padding:0 20px; grid-template-columns:1fr; align-content:center; gap:5px; }. Footer first and second cells both use text-align:left.\n\nAt max-height:760px and min-width:901px, set --header-h:54px; --footer-h:48px; --headline-size:min(8.1vw,104px); .copy-column { padding-top:72px; padding-bottom:36px; }; .lede { margin-top:22px; }; .actions { margin-top:18px; }; .edition-note { margin-top:34px; }; .swatch-wrap { top:-132px; }. The page remains overflow:hidden and all footer content stays visible.\n\nAt max-height:680px and min-width:901px, set --header-h:50px; --footer-h:44px; --headline-size:86px; --headline-leading:0.76; .copy-column { padding-top:54px; padding-bottom:24px; }; .eyebrow { margin-bottom:18px; }; .lede { max-width:360px; font-size:13px; margin-top:16px; }; .actions { margin-top:14px; }. Set .edition-note { margin-top:22px; padding-top:10px; }. Set .swatch-wrap { top:-170px; }. Do not scale typography below 86 px.\n\nI. FAILURE GUARDS\n\nThe final resting state of every text, control, rule, caption and panel is opacity:1; disabled opacity is never used to hide failed animation. All entrance animations use fill-mode:both and a deterministic final state. If SVG mask support or turbulence fails, the mask path still presents a visible hand-authored contour because the unfiltered white path remains the fallback base, and the panel must remain readable. If the custom-property animation is unsupported, --a remains 0deg and the conic gradient remains valid. If IntersectionObserver is unavailable, immediately mark all entrance targets as finished rather than waiting for observation.\n\nUse a two-rAF fallback after DOMContentLoaded: first requestAnimationFrame schedules a second requestAnimationFrame; the second starts the reveal classes and telemetry loop. If requestAnimationFrame is unavailable, apply the final reveal classes immediately, set ready:true after a 0 ms timeout, and do not increment frames. If document.visibilityState is hidden, keep the page static and resume only after visibility returns; telemetry frames continue only while visible. Never leave controls at opacity:0 if scripting is disabled, so use a .js class on html only after the script begins and provide no-js-safe visible base styles.\n\nThe prefers-reduced-motion:reduce block must cancel fringe-up, fringe-rotate and fringe-reveal with animation:none !important; force animation-delay:0s; set transform:none; opacity:1; clip-path:inset(0); stop all CSS transitions and button hover translations. JavaScript must not start the continuous loop, must not change the noise seed, and must set ready:true after the second rAF or timeout. The reduced-motion page remains complete, static and fully interactive.\n\nJ. THE EXACT JS\n\nPlace this script as the final element in body with defer omitted because it is already last. It must use no imports, no fetch, no external data, no storage, and no console methods. The exact source logic is:\n\nconst demo = { name: 'fringe', ready: false, frames: 0, effects: [], errors: [] };\nwindow.__DEMO__ = demo;\nconst motionQuery = window.matchMedia('(prefers-reduced-motion: reduce)');\nconst reveals = Array.from(document.querySelectorAll('[data-reveal]'));\nconst swatch = document.querySelector('.swatch-wrap');\nconst noise = document.querySelector('#fringe-noise-source');\nconst seeds = [17, 23, 31, 41, 53, 67, 79, 89];\nlet seedIndex = 0;\nlet nextSeed = 0;\nlet running = false;\nlet startedAt = 0;\n\nwindow.addEventListener('error', function(event) {\n  demo.errors.push(String(event.message || 'Unknown error'));\n});\n\nfunction finishReveals() {\n  reveals.forEach(function(element) {\n    element.classList.add('is-revealed');\n  });\n  if (swatch) swatch.classList.add('is-revealed');\n}\n\nfunction startFrameLoop(timestamp) {\n  if (!running || motionQuery.matches) {\n    demo.ready = true;\n    return;\n  }\n  demo.frames += 1;\n  if (!startedAt) startedAt = timestamp;\n  if (noise && timestamp >= nextSeed) {\n    seedIndex = (seedIndex + 1) % seeds.length;\n    noise.setAttribute('seed', String(seeds[seedIndex]));\n    nextSeed = timestamp + 900;\n  }\n  window.requestAnimationFrame(startFrameLoop);\n}\n\nfunction begin() {\n  document.documentElement.classList.add('js');\n  finishReveals();\n  demo.effects.push('swatch-wipe', 'conic-rotation', 'grain-jitter', 'type-stagger');\n  if (motionQuery.matches) {\n    demo.ready = true;\n    return;\n  }\n  running = true;\n  nextSeed = 0;\n  window.requestAnimationFrame(startFrameLoop);\n}\n\nmotionQuery.addEventListener('change', function(event) {\n  if (event.matches) {\n    running = false;\n    finishReveals();\n  }\n});\n\ndocument.addEventListener('visibilitychange', function() {\n  if (document.hidden) {\n    running = false;\n  } else if (!motionQuery.matches) {\n    running = true;\n    nextSeed = 0;\n    window.requestAnimationFrame(startFrameLoop);\n  }\n});\n\nif (typeof window.requestAnimationFrame === 'function') {\n  window.requestAnimationFrame(function() {\n    window.requestAnimationFrame(begin);\n  });\n} else {\n  window.setTimeout(begin, 0);\n}\n\nThe final HTML must include the three ambient rules as empty decorative elements with aria-hidden=\"true\", a single SVG mask with aria-hidden=\"true\", and no text duplicated in SVG. Navigation anchors may use href=\"#editions\", \"#materials\", and \"#notes\"; those targets must resolve to existing noninteractive landmarks or sections within the same document, and clicking them must not create horizontal movement.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["At 1440×900, document.documentElement.scrollWidth equals document.documentElement.clientWidth, body has height 100dvh with overflow hidden, and the .swatch-wrap is at least 720px wide with clip-path ending at inset(0 0 0 0).","The document contains exactly one h1, real header/main/footer/nav landmarks, two named action anchors, three named navigation links, and every visible button or link has a non-empty accessible name.","The edge mask contains feTurbulence with baseFrequency=\"0.014 0.055\" numOctaves=\"4\" and a fed feDisplacementMap with scale=\"34\"; the visible panel is above the h1 in stacking order and overlaps at least 54px of the headline.","After load, window.__DEMO__.name is \"fringe\", ready is true, effects contains swatch-wipe, conic-rotation, grain-jitter and type-stagger, and errors.length is 0.","Over a 1.2-second interval at 1440×900, window.__DEMO__.frames increases by at least 20; with prefers-reduced-motion:reduce, no rAF frame counter is required, the grain seed remains unchanged, and all content is visible with animation set to none.","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"],"usage":{"build_tokens":47734,"build_prompt_tokens":7844,"build_completion_tokens":39890,"build_reasoning_tokens":32540,"build_calls":1,"build_attempts":3,"build_latency_ms":514658,"wasted_tokens":0,"repair_tokens":22996,"crosscheck_tokens":0,"total_tokens":70730},"verification":{"passed":true,"passed_checks":18,"total_checks":18,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"22536 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"6 frames, effects: swatch-wipe, conic-rotation, grain-jitter, type-stagger"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"34 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"MADE BY HAND.\nHELD BY\nWEFT.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Fringe — Sable & Weft"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=426 pageErrors=0"}]},"crosscheck":{"slug":"fringe","reviewer_model":"glm-5.3-flash","reviewer_platform":"workbuddy","verdict":"unparsed","confidence":"low","summary":"","issues":[],"usage":{"input":0,"output":0,"total":0,"reasoning":0,"cached":0},"raw":""}},{"slug":"grid-collapse","title":"Grid Collapse","category":"Landing Page","effect":"A perspective grid collapsing toward a vanishing point under terminal type","platform":"workbuddy","platform_label":"Workbuddy proxy","model":"kimi-k3","model_label":"Kimi-K3 (kimi-k3)","demo":"/demos/grid-collapse/index.html","doc":"/prompts/grid-collapse.md","prompt_text":"Build one complete, self-contained `index.html` for **Vector** — a developer platform landing page. Document title: `Vector — Grid Collapse`. `<html lang=\"en\">`. ONE file only: plain HTML + CSS + JS in `<style>`/`<script>`. No frameworks, no bundlers, no external media of any kind (no images, no video, no fonts other than Google Fonts, no CDN scripts). Every visual pixel is generated by CSS gradients, SVG, or `<canvas>`.\n\n**Viewport rule:** on viewports `>= 901px` wide the page is a single frame: `html, body { height:100%; overflow:hidden }` and `.page { height:100dvh; overflow:hidden }`. NO SCROLL on desktop. On viewports `<= 900px` the page may scroll vertically. No horizontal overflow at any width.\n\n**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 forms, no pricing tables, no testimonials, no extra sections beyond the specified header / hero / footer, no footer links beyond the three specified, no cookie banner, no scroll indicator, no hamburger menu on desktop (menu only on `<=900px`).\n\n---\n\n## A. WHAT IS BEING BUILT\n\nA single-viewport landing page for a developer platform called **Vector**. A full-bleed `<canvas>` renders an infinite perspective grid — a floor plane of hairlines receding toward a vanishing point at the vertical centre of the upper third, continuously accelerating toward the viewer (classic infinite-tunnel). The grid fades to pure black in the distance via a radial mask. Overlaid on top: monospace terminal-style type — a small prompt line, a large geometric-sans H1, a single-line muted \"output\" line. A thin horizontal scanning line sweeps the viewport top-to-bottom every 4 seconds. The aesthetic is black-terminal, precision-engineered, cinematic.\n\n---\n\n## B. THE BACKDROP — procedural canvas grid (the ONLY visual effect besides the scanline)\n\nA single `<canvas id=\"grid\">` fills the viewport: `position:fixed; inset:0; width:100%; height:100%; z-index:0; display:block`. It is repainted every animation frame via `requestAnimationFrame`. It is NEVER a static image.\n\n**Projection maths (canvas 2D, no libraries):**\n\n- Camera at origin looking down -Z. Screen centre `(cx, cy)` where `cx = w/2`, `cy = h * 0.42` (vanishing point sits at 42% of viewport height — the upper-third anchor).\n- Focal length `f = h * 0.9`.\n- World floor plane at `y = 1` (camera height 1 unit).\n- A world point `(x, 1, z)` with `z > 0` projects to screen:\n  `sx = cx + (x * f) / z`\n  `sy = cy + (f * 1) / z`   (floor projects below the horizon line `cy` because screen y grows downward and the camera looks level; the \"+1\" camera height pushes floor lines down)\n- Depth range drawn: `z` from `zNear = 0.6` to `zFar = 80`.\n\n**Longitudinal lines (the rails converging at the vanishing point):**\n\n- `N = 26` lines, world x positions `x_i = (i - (N-1)/2) * 1.6` for `i = 0..25` (symmetric fan from `-20` to `+20` in world units, spacing 1.6).\n- Each line is drawn as a polyline of 60 segments sampling z exponentially from `zNear` to `zFar`: `z_k = zNear * (zFar/zNear)^(k/59)`. Exponential sampling keeps segment density high near the camera where perspective stretches.\n- Stroke colour: `rgba(110, 231, 168, alpha)` — the accent `#6ee7a8` — where alpha per segment is `0.34 * (1 - z_k/zFar)^1.6`. LineWidth 1px. Near segments are brightest; the far ones die out.\n\n**Transverse lines (the rungs rushing toward the viewer):**\n\n- Rungs sit at world depths `z_j = zNear * step^j` with `step = 1.14`, for all `j` with `z_j <= zFar` (≈ 38 rungs).\n- Each rung is a horizontal line across the floor at depth `z_j`: project `(-22, 1, z_j)` and `(22, 1, z_j)` to screen and connect.\n- Motion: a continuous phase `p = (t * speed) % 1` where `t` is seconds since start and `speed = 0.55`. Effective rung depth is `z_j' = z_j / step^p` — i.e. every rung's depth divides by the step as phase advances, so rungs accelerate toward the camera and wrap seamlessly (the classic infinite-corridor trick: when `p` wraps, rung `j` takes the exact place rung `j+1` held).\n- Rung alpha: `0.30 * (1 - z_j'/zFar)^1.5`, same accent colour, 1px.\n\n**Radial distance mask:** after all grid strokes, paint a radial gradient overlay centred at `(cx, cy)`: `rgba(0,0,0,0)` at radius 0 → `rgba(0,0,0,0)` at `r1 = min(w,h)*0.28` → `rgba(0,0,0,0.55)` at `r2 = min(w,h)*0.62` → `rgba(0,0,0,0.96)` at `r3 = max(w,h)*0.95`. This kills the grid at the edges and far distance so the frame fades to black.\n\n**Vignette:** a second full-canvas radial gradient centred at screen centre: transparent to `rgba(0,0,0,0.42)` at the corners, drawn last, to deepen the cinematic frame.\n\n**Frame budget:** total strokes per frame ≈ 26 polylines × 60 segments + ~38 rungs ≈ 1600 line segments. Use a single `ctx.beginPath()` per alpha bucket (bucket alphas into 8 quantised levels, one path per level) to keep draw calls under 10 per frame. Cap the device pixel ratio at `Math.min(window.devicePixelRatio, 2)`. Target 60fps; the loop must drop nothing — if `document.hidden`, skip painting but keep the rAF alive.\n\n**Resize:** on window resize, set `canvas.width = innerWidth * dpr`, `canvas.height = innerHeight * dpr`, `ctx.setTransform(dpr,0,0,dpr,0,0)`, repaint immediately.\n\n**Scanline (CSS, separate element):** a `div.scanline`, `position:fixed; left:0; right:0; height:1px; top:0; z-index:3; pointer-events:none; background: linear-gradient(90deg, transparent, rgba(110,231,168,0.55) 18%, rgba(110,231,168,0.55) 82%, transparent); box-shadow: 0 0 12px rgba(110,231,168,0.35)`. Keyframes `scan`: `0% { transform: translateY(-2vh); opacity:0 } 6% { opacity:1 } 92% { opacity:1 } 100% { transform: translateY(102vh); opacity:0 }`, `animation: scan 4s linear infinite`.\n\n---\n\n## C. LAYER STACK (back → front)\n\n1. `html, body` — pure black `#000000`, forced twice (see Failure Guards).\n2. `canvas#grid` — z-index 0, the perspective grid + radial masks + vignette.\n3. `.scrim` — z-index 1, a fixed `linear-gradient(180deg, rgba(0,0,0,0.55) 0%, rgba(0,0,0,0.15) 34%, rgba(0,0,0,0.12) 62%, rgba(0,0,0,0.72) 100%)` guaranteeing text contrast top and bottom.\n4. `.page` — z-index 2, `position:relative; display:grid; grid-template-rows: auto 1fr auto; min-height:100dvh` (height:100dvh inside the desktop lock).\n5. `.scanline` — z-index 3, above content (it sweeps over everything, terminal-style).\n6. `.grain` — z-index 4, `position:fixed; inset:-50%; width:200%; height:200%; pointer-events:none; opacity:0.05`, an inline SVG `feTurbulence` noise (`<svg><filter id=\"n\"><feTurbulence type=\"fractalNoise\" baseFrequency=\"0.8\" numOctaves=\"2\" stitchTiles=\"stitch\"/><feColorMatrix type=\"saturate\" values=\"0\"/></filter><rect width=\"100%\" height=\"100%\" filter=\"url(#n)\"/></svg>` as a data-URI background, tile size 160px), animated with `grain 0.9s steps(4) infinite` jittering `transform: translate` among 4 offsets `(0,0) (-2%,1%) (1%,-2%) (-1%,2%)`.\n\n---\n\n## D. TOKEN TABLE (defaults at ~1440px)\n\n```\n--bg:            #000000\n--text:          #f2f2f2\n--muted:         #8f8f8f\n--dim:           #5c5c5c\n--accent:        #6ee7a8\n--accent-dim:    rgba(110,231,168,0.55)\n--accent-line:   rgba(110,231,168,0.28)\n--border:        rgba(255,255,255,0.14)\n--border-soft:   rgba(255,255,255,0.09)\n--logo:          15px\n--logo-mark:     20px\n--nav:           13.5px\n--nav-h:         38px\n--btn:           13.5px\n--btn-h:         42px\n--prompt:        13px\n--h1:            56px\n--output:        14px\n--foot:          12.5px\n--header-y:      24px\n--header-x:      40px\n--hero-x:        40px\n--hero-y:        48px\n--copy-max:      880px\n--prompt-max:    560px\n--mono:          \"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace\n--sans:          \"Inter\", system-ui, -apple-system, \"Segoe UI\", sans-serif\n```\n\nFonts (Google Fonts ONLY, with fallbacks as listed): `Inter:wght@400;500;600` and `JetBrains+Mono:wght@400;500`, loaded with one `<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>` and one stylesheet link with `display=swap`.\n\n---\n\n## E. THE COMPOSITION — exact build order\n\n**.page** is the grid container with three rows: `<header>`, `<main>`, `<footer>`.\n\n**HEADER** — `padding: var(--header-y) var(--header-x); display:flex; align-items:center; justify-content:space-between; gap:24px`.\n\n- Left: `a.brand` (href `#`, accessible name \"Vector home\"): an inline SVG mark 20×20 — a right-pointing chevron built from two strokes: `<path d=\"M4 3 L16 10 L4 17\" fill=\"none\" stroke=\"#6ee7a8\" stroke-width=\"2.2\" stroke-linecap=\"square\"/>` — followed by the word `vector` in `var(--mono)`, `font-size: var(--logo); font-weight:500; color:var(--text); letter-spacing:0.02em`. The underscore cursor `_` after the wordmark, `color: var(--accent)`, blinking: keyframes `blink` `0%,49% {opacity:1} 50%,100% {opacity:0}`, `animation: blink 1.06s steps(1) infinite`.\n- Centre: `nav` with exactly 4 links: `docs`, `runtime`, `pricing`, `changelog` — all `href=\"#\"`, mono, `font-size: var(--nav); color: var(--muted)`, each preceded by `./` in `var(--dim)` (so they read `./docs` etc.). Height `var(--nav-h)`, `display:flex; align-items:center; gap:30px`. Hover: `color: var(--accent)` over 0.18s ease; the `./` stays dim.\n- Right: `a.cta` (href `#`) — the header button, text `$ npx vector init`, mono, `font-size: var(--btn); height: var(--btn-h); padding: 0 18px; display:inline-flex; align-items:center; border:1px solid var(--accent-line); border-radius:6px; color:var(--accent); background: rgba(110,231,168,0.06)`. Hover: `background: rgba(110,231,168,0.14); border-color: var(--accent-dim); box-shadow: 0 0 16px rgba(110,231,168,0.18)`, 0.2s ease.\n\n**MAIN (hero)** — `display:flex; flex-direction:column; justify-content:center; align-items:flex-start; padding: var(--hero-y) var(--hero-x); max-width: var(--copy-max); margin: 0 auto; width:100%`. Left-aligned, vertically centred within the 1fr row — NOT bottom-anchored, NOT right-aligned.\n\n1. `.prompt` — mono, `font-size: var(--prompt); color: var(--dim); max-width: var(--prompt-max)`. Exact copy: `hongvin@vector:~$ deploy --grid --watch`. The `hongvin@vector` part is `color: var(--accent)`; `:~$` is `var(--dim)`; the command is `var(--text)`. After the command, a block cursor `▉` (U+2589) in `var(--accent)` with the same `blink` animation.\n2. `h1` — EXACTLY ONE `<h1>`, `var(--sans)`, `font-weight:600; font-size: var(--h1); letter-spacing:-0.04em; line-height:1.06; color:var(--text); margin: 22px 0 0`. Two masked lines (see Motion), exact copy:\n   Line 1: `Ship to the grid.`\n   Line 2: `Collapse the distance.`\n   Line 2 contains `<em>distance</em>` styled `font-family: var(--mono); font-style:normal; font-weight:400; font-size:0.92em; color: var(--accent); letter-spacing:-0.02em`.\n3. `.output` — mono, single line, `font-size: var(--output); color: var(--muted); margin-top:26px; white-space:nowrap`. Exact copy: `[ok] 214 nodes online · median latency 11ms · build 0.9.4`. The `[ok]` token is `color: var(--accent)`. On narrow screens it must shrink (see responsive), never wrap, never overflow.\n\n**FOOTER** — `padding: 22px var(--header-x); display:flex; align-items:center; justify-content:space-between; gap:20px; border-top:1px solid var(--border-soft)`.\n\n- Left: mono `var(--foot)`, `color: var(--dim)`: `© 2026 Vector Systems`.\n- Centre: mono `var(--foot)`, `color: var(--dim)`: `status: all systems nominal` with a 7px square `background: var(--accent); box-shadow: 0 0 8px var(--accent-dim)` inline before it (a status pixel, `display:inline-block; margin-right:8px`).\n- Right: exactly 3 links `github`, `x`, `irc` — mono, `var(--foot)`, `color: var(--muted)`, `gap:22px`, href `#`, hover `color: var(--text)`.\n\n---\n\n## F. THE UI LANGUAGE\n\nThere is exactly ONE button style in the page (the header CTA) — the terminal-chip: 6px radius, 1px `var(--accent-line)` border, 6% accent fill, mono text, accent glow on hover as specified above. Nav links are plain mono text with the `./` prefix convention — no pills, no backgrounds, no underline ever; hover swaps colour to accent. Footer links: same treatment in `var(--foot)`. Every interactive element gets `:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 4px }`. Selection: `::selection { background: rgba(110,231,168,0.28); color: #fff }`. No element on the page ever shows a default blue focus ring or a text underline.\n\n---\n\n## G. MOTION — entrance stagger + named keyframes\n\nAll entrance animations: `animation-duration: 0.95s; animation-fill-mode: both; animation-timing-function: cubic-bezier(0.16,1,0.3,1); animation-delay: var(--d, 0.08s)`. The `.appear` class is applied to each animated element with a modifier class selecting keyframes and an inline `--d`.\n\nStagger table (element → modifier → --d):\n```\nbrand          appear--fade   0.08s\nnav link 1     appear--fade   0.16s\nnav link 2     appear--fade   0.24s\nnav link 3     appear--fade   0.32s\nnav link 4     appear--fade   0.40s\nheader cta     appear--chip    0.46s\nprompt line    appear--type    0.55s\nh1 line 1      appear--mask    0.70s\nh1 line 2      appear--mask    0.86s\noutput line    appear--soft    1.06s  (duration 1.2s)\nfooter left    appear--fade    1.22s\nfooter centre  appear--fade    1.30s\nfooter right   appear--fade    1.38s\n```\n\nNamed keyframes (all end at opacity 1 / identity transform):\n```\nin-fade   from { opacity: 0 }                                          to { opacity: 1 }\nin-soft   from { opacity: 0; transform: translateY(12px) }             to { opacity: 1; transform: none }\nin-mask   from { opacity: 0; transform: translateY(105%) }             to { opacity: 1; transform: none }\nin-chip   from { opacity: 0; transform: translateY(10px) scale(0.96) } to { opacity: 1; transform: none }\nin-type   from { opacity: 0; clip-path: inset(0 100% 0 0) }            to { opacity: 1; clip-path: inset(0 -5% 0 0) }\n```\n\nThe H1 masking: each line of the H1 is wrapped in `<span class=\"line\"><span class=\"line-inner appear appear--mask\">…</span></span>` where `.line { display:block; overflow:hidden; padding: 0.04em 0.1em 0.10em }` and `.line-inner { display:inline-block }`. The `in-mask` translateY(105%) is clipped by the parent's overflow — the type rises out of a hard edge, terminal-style.\n\n`in-type` on the prompt line reveals it left-to-right via clip-path, like a command being echoed.\n\nThe canvas grid, scanline, blink, and grain animations run continuously, independent of the entrance stagger.\n\n---\n\n## H. RESPONSIVE TABLE — token overrides per breakpoint (exact)\n\n```\n>= 1600px:\n  --h1: 72px  --logo: 16px  --nav: 14.5px  --nav-h: 42px  --btn: 14.5px\n  --btn-h: 46px  --prompt: 14px  --output: 15px  --foot: 13px\n  --header-y: 28px  --header-x: 56px  --hero-x: 56px  --copy-max: 1020px\n\n>= 1920px:\n  --h1: 84px  --header-x: 72px  --hero-x: 72px  --copy-max: 1140px\n  --output: 16px  --prompt: 15px\n\n1280px – 1599px:\n  --h1: 62px  --header-x: 48px  --hero-x: 48px  --copy-max: 920px\n\n901px – 1279px:\n  --h1: 46px  --nav-h: 34px  --header-x: 32px  --hero-x: 32px\n  --copy-max: 760px  --output: 13px\n\n>= 901px AND max-height 850px:\n  --h1: 42px  --hero-y: 28px  --header-y: 18px  (h1 margin-top 16px, output margin-top 18px)\n\n>= 901px AND max-height 720px:\n  --h1: 36px  --hero-y: 18px  --header-y: 14px  --prompt: 12px  --output: 12.5px\n\n>= 901px (desktop lock, all heights):\n  html, body { height:100%; overflow:hidden }  .page { height:100dvh; overflow:hidden }\n\n<= 900px:\n  no vh lock; page may scroll; nav becomes a full-viewport overlay menu:\n  - header keeps brand + burger button (<button class=\"burger\" aria-label=\"Menu\" aria-expanded=\"false\">, three 18×1.5px bars in var(--text), gap 5px)\n  - .menu { position:fixed; inset:0; z-index:10; background: rgba(0,0,0,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 cubic-bezier(0.16,1,0.3,1) }\n  - .menu.open { transform: none }\n  - menu shows the 4 nav links stacked at --nav: 22px plus the CTA chip; Escape key, link click, and resize >900px all close it\n  --h1: 40px  --header-x: 24px  --hero-x: 24px  --copy-max: 100%\n\n<= 560px:\n  --h1: 32px  --prompt: 11.5px  --output: 11.5px  --header-x: 20px  --hero-x: 20px\n  footer becomes flex-direction:column; align-items:flex-start; gap:10px\n  header CTA is hidden (it lives inside the menu on mobile)\n```\n\nCanvas constants also scale: below 901px the vanishing point `cy = h * 0.40`, longitudinal line count `N = 18`, rung step unchanged. The `.output` line at <=560px drops the `· build 0.9.4` segment via a `<span class=\"opt\">` hidden at that breakpoint (so the line never overflows).\n\n---\n\n## I. FAILURE GUARDS — the page is never blank, never broken\n\n1. **Black-first:** the FIRST CSS rule is `html, body { background:#000000 !important; color:#f2f2f2 }`; the body tag carries `style=\"background:#000;color:#f2f2f2\"`; a later rule restates `html, body { background:#000000; background: var(--bg, #000000) }`.\n2. **Resting opacity is 1:** every `.appear` element's un-animated computed style is its final state (opacity 1, no transform). Animations use `fill-mode: both` only to hold the FROM state during the delay. If CSS animations fail or JS never runs, the page renders complete and readable.\n3. **rAF entrance fallback:** after two `requestAnimationFrame` ticks, if `el.getAnimations()` reports nothing running or finished for any `.appear`, JS adds `.is-in` to every `.appear` (`.is-in { animation:none !important; opacity:1 !important; transform:none !important; clip-path:none !important }`).\n4. **Canvas fallback:** if `canvas.getContext('2d')` returns null, JS adds class `.no-canvas` to `<html>` and CSS paints a static CSS-only grid: `.no-canvas body { background-image: linear-gradient(var(--border-soft) 1px, transparent 1px), linear-gradient(90deg, var(--border-soft) 1px, transparent 1px); background-size: 48px 48px; }` — a flat hairline grid so the page still reads as \"grid\", statically.\n5. **Reduced motion:** inside `@media (prefers-reduced-motion: reduce)`: `*, *::before, *::after { transition:none !important; animation:none !important }`; force `.appear, .line-inner, .prompt, .output` to `opacity:1 !important; transform:none !important; clip-path:none !important`; the canvas loop still runs but with `speed = 0` (grid drawn once, static) and the scanline is `display:none`.\n6. **No console output** in the happy path: no `console.log`, no thrown exceptions; every canvas call guarded.\n\n---\n\n## J. THE EXACT JS (only this, vanilla, one IIFE at end of body)\n\n1. **Telemetry first, before anything else:**\n   `window.__DEMO__ = { name: 'grid-collapse', ready: false, frames: 0, effects: [], errors: [] };`\n   Push `'perspective-grid'` and `'scanline'` into `effects` once implemented. Add `window.addEventListener('error', e => window.__DEMO__.errors.push(String(e.message || e)))`.\n2. **Canvas loop:** get `#grid`, get 2d context (guard null → `.no-canvas` path), implement the projection maths from section B verbatim (26 lines, 60 segments, exponential z sampling, step 1.14, speed 0.55, 8 alpha buckets). Each frame: increment `window.__DEMO__.frames` by exactly 1, clear to `#000`, draw lines, rungs, radial mask, vignette. On the FIRST completed paint set `window.__DEMO__.ready = true`. Loop with `requestAnimationFrame`; skip painting while `document.hidden` but keep the loop alive.\n3. **Resize handler** (debounced via rAF flag): resize canvas backing store with dpr cap 2, repaint.\n4. **Entrance fallback:** two nested rAFs after DOMContentLoaded, then per `.appear`: if `el.getAnimations` exists and returns no running/finished animations, add `.is-in` to all `.appear` elements.\n5. **Menu (mobile only):** burger click toggles `.menu.open` and `aria-expanded`; Escape closes; any menu link click closes; window resize to >900px closes.\n6. **prefers-reduced-motion:** read `matchMedia('(prefers-reduced-motion: reduce)')` once at startup; if true, set grid speed to 0 (paint one static frame, still set ready, still count that 1 frame).\n\nNo other JS. No libraries. No console calls.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["exactly one <h1> split into exactly 2 masked line elements (.line > .line-inner) with text 'Ship to the grid.' and 'Collapse the distance.'","canvas#grid paints non-blank content and keeps repainting (pixel hash changes across frames) at 1440x900","window.__DEMO__.ready becomes true, window.__DEMO__.frames increments in the rAF loop, effects contains 'perspective-grid' and 'scanline', errors stays empty","a .scanline element sweeps the viewport with a 4s linear infinite animation, and exactly 4 nav links + 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","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"],"usage":{"build_tokens":13811,"build_prompt_tokens":6937,"build_completion_tokens":6874,"build_reasoning_tokens":45,"build_calls":1,"build_attempts":1,"build_latency_ms":146947,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":45888,"total_tokens":59699},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"20654 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"14 frames, effects: perspective-grid, scanline"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"43 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 1.16, stdev 4.13, coverage 0.0833"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 3235564425 -> 1628655722"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Ship to the grid.\nCollapse the distance.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Vector — Grid Collapse"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=271 pageErrors=0"}]},"crosscheck":{"slug":"grid-collapse","reviewer_model":"deepseek-v4.1-flash","reviewer_platform":"workbuddy","verdict":"confirmed","confidence":"high","aesthetic_score":7,"requirements_met":["Single self-contained index.html, no frameworks, no external media (SVG data-URI grain only)","Layer stack correct: canvas z0, scrim z1, .page z2, .scanline z3, .grain z4","Header composition: chevron brand + blinking _ cursor, 4 ./-prefixed nav links, accent chip CTA '$ npx vector init'","Hero left-aligned and vertically centred with prompt / two masked H1 lines / output line","Exact copy reproduced: prompt, 'Ship to the grid.' / 'Collapse the distance.' with mono accent <em>distance</em>, [ok] output line","Footer: © 2026 Vector Systems, status pixel + 'status: all systems nominal', github/x/irc","Canvas projection maths (cx=w/2, cy=h*0.42, f=h*0.9, zNear 0.6, zFar 80, N=26, 60 exponential segments, step 1.14, speed 0.55) implemented as specified","8 alpha buckets with single beginPath per level; radial distance mask + vignette drawn after grid","Scanline CSS (1px, 4s linear, gradient + box-shadow) matches spec exactly","Entrance stagger table implemented with correct modifiers and --d values; in-fade/in-soft/in-mask/in-chip/in-type keyframes verbatim","Responsive token overrides for 1600/1920/1280-1599/901-1279/max-height 850/720/900/560 all present and correct","Mobile: burger, full-viewport overlay menu, Escape/link/resize close, CTA relocated into menu","Failure guards: black-first rule + body inline style, .is-in fallback, .no-canvas CSS grid, reduced-motion rules, no console calls"],"requirements_missed":["Reduced-motion: the rAF chain is terminated after the single static frame (if (!reduced) requestAnimationFrame(loop)), whereas the brief requires the loop to keep running with speed = 0","Reduced-motion: the mobile .menu is forced to visibility:visible/position:static/transform:none, turning the overlay into an in-flow block rendered below the footer on <=900px"],"issues":[{"issue":"Reduced-motion mobile menu becomes a static in-flow section appended under the footer instead of an overlay","severity":"medium","evidence":"@media (prefers-reduced-motion: reduce){ .menu{ visibility:visible; transform:none; position:static; background:none; ... } } — the menu sits after .page in the DOM"},{"issue":"Reduced-motion canvas stops animating entirely rather than running a static loop","severity":"low","evidence":"if (!reduced) { requestAnimationFrame(loop); } combined with a separate one-shot paint(0)"},{"issue":"Frame counter increments twice on the reduced-motion path","severity":"low","evidence":"loop() paints and increments, then the reduced block runs paint(0) and increments __DEMO__.frames again"},{"issue":"Longitudinal polyline drops one segment at every alpha-bucket transition (moveTo instead of lineTo), leaving sub-pixel gaps in the rails","severity":"low","evidence":"if (lvl !== prevBucket) { ...; paths[lvl].push(0, sx, sy); prevBucket = lvl; } else { paths[lvl].push(1, sx, sy); }"},{"issue":"Rung alpha is quantised with the rail denominator 0.34 although rung peak alpha is 0.30, so rungs land in a coarser bucket than specified","severity":"low","evidence":"lvl = Math.round(alpha * (BUCKETS - 1) / 0.34) applied to alpha = 0.30 * Math.pow(1 - ze / zFar, 1.5)"}],"summary":"The page renders exactly as briefed: black terminal frame, converging accent grid at cy=42%, correctly staggered terminal-chip header and masked hero, with the exact required copy and no layout breakage in the 1024x640 capture. Only real deviations are reduced-motion details (the rAF loop halts, and the mobile overlay menu degrades to a static block under the footer), none of which affect the default desktop composition.","usage":{"input":15366,"output":7156,"total":22522,"reasoning":6098,"cached":15232},"latency_ms":32752}},{"slug":"halide","title":"Halide","category":"Landing Page","effect":"A camera iris of 8 SVG aperture blades breathing open and shut inside a rotating conic lens ring, with lens bloom and chromatic fringe","platform":"cmdcode","platform_label":"cmd-code","model":"xiaomi/mimo-v2.6-pro","model_label":"Xiaomi MiMo v2.6 Pro (xiaomi/mimo-v2.6-pro)","demo":"/demos/halide/index.html","doc":"/prompts/halide.md","prompt_text":"## A. WHAT IS BEING BUILT\n\nBuild one complete, valid index.html — a single-viewport cinematic landing page for **Halide**, a computational photography-AI company. Document title: \"Halide — Night is Undeveloped Light\". html lang=\"en\". ONE file: plain HTML + one style block + one vanilla script at end of body. No build step, no framework, no bundler.\n\n**Viewport rule:** at width >= 901px the page is exactly one frame: html, body { height:100%; overflow:hidden } and .page { height:100dvh; overflow:hidden }. NO SCROLL on desktop. At width <= 900px the page may scroll vertically. No horizontal overflow at 390, 768, 1440 or 1920.\n\n**Do NOT build:** no video, mp4/webm/gif, remote images, base64 photo blobs, third-party CDN URLs, WebGL, Three.js, GSAP, Lottie, jQuery, Tailwind, React, audio, forms, pricing tables, testimonials, blog, cookie banner, scroll indicator, modal dialogs, gallery grids, extra sections beyond header / hero / stats footer, footer content beyond the three specified stats, desktop hamburger (burger only <= 900px). Google Fonts is the ONLY network request.\n\n**The single visual idea:** a black photographic darkroom, one light behind one lens. Centre-upper: a camera iris — 8 SVG aperture blades on barrel-ring pivots, breathing open and shut over 16s. A conic-gradient focus ring turns around it like a lens barrel (28s); a warm lens bloom breathes behind (9s); a 6px red/cyan chromatic fringe drifts on the blade rim (34s). Dust motes rise through the bloom on a tiny canvas. Over the optics: a bottom-anchored hero (eyebrow chip, two masked serif headline lines, lead, two buttons) and a three-item stats footer on a hairline.\n\n---\n\n## B. THE BACKDROP — the optical stack (counts, blur radii, gradient stops, drift periods)\n\nEverything behind the type lives in one fixed .backdrop at inset:0, overflow:hidden, pointer-events:none, z-index:0.\n\n1. **.room-bg** (inset:0, z-index:0) background layers, back to front: radial-gradient(ellipse 130% 85% at 50% 40%, rgba(255,180,84,0.10) 0%, rgba(255,150,60,0.045) 30%, transparent 62%); radial-gradient(circle at 50% 40%, rgba(255,240,214,0.06) 0%, transparent 26%); linear-gradient(180deg, #07060a 0%, #0b0908 38%, #060505 72%, #030202 100%).\n2. **.bloom** (z-index:1) centred (left:50%, top:var(--iris-top), translate(-50%,-50%)), size calc(var(--iris) * 1.55); background:radial-gradient(circle, rgba(255,214,150,0.50) 0%, rgba(255,180,84,0.26) 24%, rgba(255,140,50,0.10) 46%, transparent 72%); filter:blur(48px); opacity:0.76; animation bloom-pulse 9s ease-in-out infinite (opacity 0.62 <-> 0.90, scale 1 <-> 1.05).\n3. **.bloom-core** (z-index:2) size calc(var(--iris) * 0.5), same centring; background:radial-gradient(circle, rgba(255,240,214,0.55) 0%, rgba(255,196,120,0.18) 38%, transparent 70%); filter:blur(18px); opacity:0.8.\n4. **.flare** (z-index:3) left:50%; top:var(--iris-top); width:150vw; height:3px; transform:translate(-50%,-50%) rotate(-2.4deg); background:linear-gradient(90deg, transparent 0%, rgba(122,200,255,0.20) 20%, rgba(255,220,170,0.42) 50%, rgba(255,92,122,0.18) 80%, transparent 100%); filter:blur(7px); opacity:0.42; animation flare-drift 22s ease-in-out infinite (translateX -2.5% <-> +2.5%, rotate -2.4deg <-> 1.6deg, opacity 0.34 <-> 0.58).\n5. **.iris-ring** (z-index:5, the rotating conic ring) size var(--iris), centred, border-radius:50%, background:conic-gradient(from 0deg, rgba(255,180,84,0.00) 0deg, rgba(255,180,84,0.55) 40deg, rgba(255,240,214,0.85) 72deg, rgba(255,180,84,0.10) 130deg, rgba(255,180,84,0.00) 190deg, rgba(90,209,255,0.28) 250deg, rgba(255,180,84,0.42) 305deg, rgba(255,180,84,0.00) 360deg); mask plus -webkit-mask radial-gradient(circle, transparent 0 calc(50% - 26px), #000 calc(50% - 25px) 50%, transparent calc(50% + 1px)) — a 26px ring; rest rotate(24deg); animation ring-spin 28s linear infinite.\n6. **.iris-ticks** (z-index:6) same box; background:repeating-conic-gradient(from 0deg, rgba(255,236,210,0.55) 0deg 0.5deg, transparent 0.5deg 7.5deg) — exactly 48 ticks; outer 8px mask ring radial-gradient(circle, transparent 0 calc(50% - 9px), #000 calc(50% - 8px) 50%, transparent calc(50% + 1px)); opacity:0.28; animation ticks-spin 40s linear infinite reverse.\n7. **svg.iris** (z-index:7) viewBox \"0 0 400 400\" on a wrapper of size calc(var(--iris) * 0.72) centred on the axis. Exactly 8 blades. Barrel: <circle cx=\"200\" cy=\"200\" r=\"150\" fill=\"none\" stroke=\"rgba(255,196,120,0.22)\" stroke-width=\"1.2\"/>. Blade path, instanced 8 times: d=\"M200 50 A150 150 0 0 1 324.4 116.1 Z\" — a circular segment: pivot corner (200,50) at the barrel top, leading corner (324.4,116.1), 56 degrees of arc clockwise. Each instance: outer <g transform=\"rotate(k * 45, 200, 200)\"> for k = 0..7 wrapping <g class=\"blade\"> with transform-box:view-box; transform-origin:200px 50px; transform:rotate(30deg) at rest; animation aperture-breathe 16s ease-in-out infinite. Keyframes swing rotate(10deg) (aperture radius 118 in the 400 viewBox, open) to rotate(56deg) (radius 16, nearly shut); the 8 straight leading edges form the opening. Blade fill <linearGradient id=\"bladeMetal\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\"> stops offset 0 #35302a, 0.55 #1c1916, 1 #0f0d0b (fill url(#bladeMetal)); stroke=\"rgba(255,196,120,0.30)\" stroke-width=\"1.2\". Hairline rim <circle cx=\"200\" cy=\"200\" r=\"118\" fill=\"none\" stroke=\"rgba(90,209,255,0.16)\" stroke-width=\"1\"/>.\n8. **.glass** (z-index:8) svg.iris box, border-radius:50%, background:radial-gradient(circle at 34% 28%, rgba(255,255,255,0.16) 0%, transparent 40%), radial-gradient(circle at 50% 50%, rgba(90,209,255,0.10) 0%, transparent 60%); box-shadow: inset 0 0 60px rgba(0,0,0,0.55).\n9. **.fringe** (z-index:9, chromatic fringe) size calc(var(--iris) * 0.78), centred, border-radius:50%, background:conic-gradient(from 15deg, rgba(255,92,122,0.55) 0deg, transparent 70deg, rgba(90,209,255,0.55) 150deg, transparent 250deg, rgba(255,92,122,0.55) 360deg); 12px ring mask radial-gradient(circle, transparent 0 calc(50% - 13px), #000 calc(50% - 12px) calc(50% - 1px), transparent 50%); filter:blur(6px); opacity:0.32; mix-blend-mode:screen; animations fringe-spin 34s linear infinite reverse + fringe-breathe 7s ease-in-out infinite (opacity 0.24 <-> 0.38).\n10. **canvas#dust** (z-index:10) fixed inset:0, size 100%, pointer-events:none. Exactly 34 motes at desktop, 24 at <= 900px, 16 at <= 560px. Per mote (deterministic LCG, seed 20260214): radius 0.6px–2.2px, rise 4px/s–11px/s, sway amplitude 8px–22px at 0.12Hz–0.31Hz, fill rgba(255,206,152, a) with a 0.05–0.18. Pure time function: y = (y0 - t * vy) mod (h + 60) - 30; x = x0 + sin(t * swayHz * 6.2832 + phase) * swayAmp. Cost: 34 arcs + 1 clear per frame — under 40 canvas primitives, ZERO ctx.filter, ZERO ctx.shadowBlur anywhere in the code.\n11. **.grain** (z-index:30) fixed inset:-50%, size 200%, opacity:0.045; background: inline SVG feTurbulence data-URI (feTurbulence type=\"fractalNoise\" baseFrequency=\"0.8\" numOctaves=\"2\" stitchTiles=\"stitch\" then feColorMatrix type=\"saturate\" values=\"0\"), tile 160px; animation grain 0.9s steps(4) infinite jittering translate among (0,0) (-2%,1%) (1%,-2%) (-1%,2%).\n\n**Resolution trick:** dpr = Math.min(window.devicePixelRatio || 1, 2); canvas.width = cssWidth * dpr; canvas.height = cssHeight * dpr; ctx.setTransform(dpr, 0, 0, dpr, 0, 0). Sizes are read once in resizeCanvas — getBoundingClientRect allowed THERE only. Resize: one boolean flag + requestAnimationFrame, no timers.\n\n**Per-frame cost budget:** 1 clear + 34 arc fills, under 1.5ms at 1440x900. Ring, ticks, blades, fringe, bloom, flare, grain animate transform/opacity only — zero layout, zero JS paint. No DOM reads beyond cached handles in the loop. The loop sustains >= 20 frames per 1.2s at 1440x900 headless. If document.hidden, keep loop and counter alive, skip the paint.\n\n---\n\n## C. THE LAYER STACK (back -> front, z-index)\n\n1. html, body — #07060a, forced twice (Failure Guards).\n2. .backdrop — z-index:0, fixed inset:0, pointer-events:none, overflow:hidden. Inside it: .room-bg 0, .bloom 1, .bloom-core 2, .flare 3, .iris-stage 5 (fixed box left:50%, top:var(--iris-top), size var(--iris), transform:translate(-50%,-50%)) holding .iris-ring 5, .iris-ticks 6, svg.iris 7 (width calc(var(--iris) * 0.72), centred left:50% top:50% translate(-50%,-50%)), .glass 8, .fringe 9; canvas#dust 10; .grain 30.\n3. .scrim — z-index:20, fixed inset:0, pointer-events:none, background:linear-gradient(180deg, rgba(3,2,2,0.55) 0%, rgba(3,2,2,0.10) 30%, rgba(3,2,2,0.28) 62%, rgba(3,2,2,0.86) 100%) so the bottom-anchored hero and stats always clear contrast over the bloom.\n4. .page — z-index:40, position:relative; display:grid; grid-template-rows:auto minmax(0, 1fr) auto; min-height:100dvh (height:100dvh inside the desktop lock).\n5. main.hero z-index:50; header.site-header and footer.stats-footer z-index:60.\n6. .menu (mobile, when open) — fixed inset:0, z-index:80.\n\n---\n\n## D. TOKEN TABLE (defaults at 1440x900)\n\n```\n--bg:            #07060a\n--bg-2:          #0b0908\n--text:          #f6f1ea\n--text-soft:     #d8cfc4\n--muted:         #9b9287\n--dim:           #6b645b\n--accent:        #ffb454\n--accent-hot:    #ffe3ae\n--accent-soft:   rgba(255,180,84,0.55)\n--accent-line:   rgba(255,180,84,0.30)\n--fringe-r:      #ff5c7a\n--fringe-b:      #5ad1ff\n--border:        rgba(255,236,210,0.16)\n--border-soft:   rgba(255,236,210,0.09)\n--focus:         #ffe3ae\n--serif:         \"Instrument Serif\", Georgia, \"Times New Roman\", serif\n--sans:          \"Inter\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif\n--mono:          \"JetBrains Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace\n--logo:          16px\n--logo-track:    0.22em\n--nav:           13.5px\n--nav-h:         38px\n--btn:           13.5px\n--btn-h:         44px\n--chip-h:        30px\n--eyebrow:       12px\n--h1:            78px\n--lead:          18px\n--stat-val:      26px\n--stat-label:    12px\n--header-y:      26px\n--header-x:      44px\n--hero-x:        44px\n--hero-bottom:   56px\n--copy-max:      720px\n--iris:          560px\n--iris-top:      42%\n--radius-pill:   999px\n--focus-w:       2px\n--focus-offset:  3px\n--bloom-blur:    48px\n--fringe-blur:   6px\n--grain-op:      0.045\n--ease-out:      cubic-bezier(0.16,1,0.3,1)\n--enter-dur:     0.95s\n```\n\nFonts: Google Fonts ONLY — Instrument Serif 400, Inter 400;500;600, JetBrains Mono 400;500 — one preconnect to https://fonts.gstatic.com (crossorigin) and one stylesheet link with display=swap, with the fallback stacks above.\n\n---\n\n## E. THE COMPOSITION — exact build order\n\n**.page** holds <header class=\"site-header\">, <main class=\"hero\">, <footer class=\"stats-footer\"> in grid rows auto / 1fr / auto.\n\n**HEADER** — display:flex; align-items:center; justify-content:space-between; gap:24px; padding:var(--header-y) var(--header-x).\n\n- a.brand (href \"#\", accessible name \"Halide home\"): inline SVG mark 22x22 — circle cx 11 cy 11 r 8.4 fill none stroke currentColor stroke-width 1.5 plus three 1px micro-iris chords in var(--accent) at 0/120/240 degrees — then \"HALIDE\", var(--sans), font-size var(--logo), weight 600, letter-spacing var(--logo-track), color var(--text), no underline.\n- nav with exactly 3 links — \"Optics\", \"Pipeline\", \"Journal\" — href \"#\", var(--sans), font-size var(--nav), weight 500, color var(--muted), display:flex; gap:32px; height:var(--nav-h); align-items:center. Hover: color var(--text), 0.18s ease.\n- a.cta (href \"#\", text \"Request access\"): height var(--btn-h); padding 0 20px; inline-flex; align-items:center; border-radius var(--radius-pill); font-size var(--btn); font-weight 600; color var(--accent-hot); border 1px solid var(--accent-line); background linear-gradient(180deg, rgba(255,180,84,0.16) 0%, rgba(255,180,84,0.05) 100%).\n\n**MAIN (hero, bottom-anchored)** — display:flex; flex-direction:column; justify-content:flex-end; align-items:flex-start; padding:0 var(--hero-x) var(--hero-bottom); copy hugs the LEFT page padding — never centred, right-aligned or vertically centred. Inner .hero-copy: width:100%; max-width:var(--copy-max); display:flex; flex-direction:column; align-items:flex-start.\n\n1. .chip (a <p>, not interactive): height var(--chip-h); padding 0 14px; inline-flex; align-items:center; border-radius var(--radius-pill); border 1px solid var(--border); background rgba(255,180,84,0.06); var(--mono) at var(--eyebrow), letter-spacing 0.18em, color var(--accent). Exact copy: \"COMPUTATIONAL PHOTOGRAPHY\".\n2. h1 — EXACTLY ONE <h1>, var(--serif), font-weight 400, font-size var(--h1), line-height 0.98, letter-spacing -0.015em, color var(--text), margin: 20px 0 0. Two masked lines (Motion): line 1 \"Night is\"; line 2 \"undeveloped light.\" with <em>undeveloped</em> italic, color var(--accent-hot), text-shadow 1.5px 0 rgba(255,92,122,0.35), -1.5px 0 rgba(90,209,255,0.35) (the lens fringe).\n3. .lead — var(--sans), var(--lead), line-height 1.55, letter-spacing -0.012em, color var(--text-soft), max-width 560px, margin: 22px 0 0. Exact copy: \"Halide rebuilds the photograph your sensor almost saw — twelve-frame multi-exposure fusion, per-channel denoise and full lens simulation, resolved in 38 milliseconds.\"\n4. .actions — display:flex; gap:12px; flex-wrap:wrap; margin-top: 30px. Two anchors: .btn--primary href \"#\" text \"Request early access\"; .btn--ghost href \"#\" text \"See the pipeline\".\n\n**FOOTER (three-item stats)** — display:grid; grid-template-columns:repeat(3, 1fr); gap:0; padding: 22px var(--header-x) 28px; border-top:1px solid var(--border-soft). Exactly one <ul> with exactly three <li>, nothing else. Each li: display:grid; gap:6px; padding:0 30px; li:first-child padding-left:0; li:last-child padding-right:0; li + li border-left:1px solid var(--border-soft); li:nth-child(2) text-align:center; li:nth-child(3) text-align:right. Each li holds <strong class=\"stat-val\"> (var(--mono), var(--stat-val), weight 500, letter-spacing -0.02em, color var(--text)) and <span class=\"stat-label\"> (var(--mono), var(--stat-label), letter-spacing 0.14em, uppercase, color var(--dim)). Exact pairs left to right: \"38 ms\" / \"RAW fusion latency\"; \"+1.4 EV\" / \"shadow detail recovered\"; \"12\" / \"frames per capture\".\n\n---\n\n## F. THE UI LANGUAGE\n\n- **.btn (shared base):** inline-flex; align-items:center; justify-content:center; height var(--btn-h); padding 0 22px; border-radius var(--radius-pill); font-family var(--sans); font-size var(--btn); font-weight 600; letter-spacing 0.01em; no underline; position:relative; overflow:hidden; transition transform/border-color/background/color/box-shadow 220ms var(--ease-out).\n- **.btn--primary:** color #241403; border 1px solid rgba(255,227,174,0.65); background linear-gradient(180deg, #ffe3ae 0%, #ffb454 52%, #e08f2c 100%); box-shadow 0 14px 44px rgba(255,180,84,0.22), inset 0 1px 0 rgba(255,255,255,0.62). Hover: background linear-gradient(180deg, #fff4dd 0%, #ffc272 48%, #ea9a38 100%), translateY(-1px), box-shadow 0 18px 54px rgba(255,180,84,0.32). Active translateY(0).\n- **.btn--ghost:** color var(--text-soft); border 1px solid var(--border); background rgba(255,236,210,0.035). Hover: color var(--text), border-color var(--accent-line), background rgba(255,180,84,0.08), translateY(-1px).\n- **Glint** on .btn and .cta: content \"\"; position:absolute; inset:-1px; background linear-gradient(112deg, transparent 0%, transparent 36%, rgba(255,240,214,0.20) 50%, transparent 64%, transparent 100%); transform translateX(-140%); transition transform 680ms var(--ease-out); hover drives it to translateX(140%).\n- **.cta (header)** takes .btn base at padding 0 20px with section E's amber hairline look; hover: border-color var(--accent-soft), background linear-gradient(180deg, rgba(255,180,84,0.24) 0%, rgba(255,180,84,0.08) 100%), box-shadow 0 0 20px rgba(255,180,84,0.20).\n- **Nav links** plain, no pills, no underline; hover color var(--text). **.chip** never reacts.\n- **Focus:** every a and button gets :focus-visible { outline: var(--focus-w) solid var(--focus); outline-offset: var(--focus-offset); border-radius: var(--radius-pill) }. Selection: ::selection { background: rgba(255,180,84,0.30); color: #1a1005 }. No default blue ring, no underline anywhere.\n\n---\n\n## G. MOTION — entrance stagger and named keyframes\n\nEntrance animations: animation-duration var(--enter-dur); animation-fill-mode both; animation-timing-function var(--ease-out); animation-delay var(--d, 0.08s). Each element: .appear + one modifier + inline --d. Resting state is the final state.\n\nStagger table (element -> modifier -> --d):\n```\nbrand            appear--fade   0.08s\nnav link 1       appear--fade   0.16s\nnav link 2       appear--fade   0.24s\nnav link 3       appear--fade   0.32s\nheader cta       appear--chip   0.40s\niris stage       appear--open   0.18s   (duration 1.4s)\neyebrow chip     appear--pop    0.62s\nh1 line 1        appear--mask   0.76s\nh1 line 2        appear--mask   0.92s\nlead             appear--soft   1.10s\nprimary button   appear--chip   1.24s\nghost button     appear--chip   1.32s\nstat 1           appear--stat   1.42s\nstat 2           appear--stat   1.50s\nstat 3           appear--stat   1.58s\n```\n\nNamed entrance keyframes (all end at opacity 1 / identity):\n```\nin-fade   from { opacity: 0 }                                                to { opacity: 1 }\nin-soft   from { opacity: 0; transform: translateY(14px); filter: blur(4px) } to { opacity: 1; transform: none; filter: blur(0) }\nin-mask   from { opacity: 0; transform: translateY(108%) }                   to { opacity: 1; transform: none }\nin-chip   from { opacity: 0; transform: translateY(10px) scale(0.96) }       to { opacity: 1; transform: none }\nin-pop    0% { opacity: 0; transform: scale(0.94) }  68% { opacity: 1; transform: scale(1.03) }  100% { opacity: 1; transform: none }\nin-open   from { opacity: 0; transform: scale(0.86) }                        to { opacity: 1; transform: none }\n```\n\nNamed continuous keyframes (the optics):\n```\naperture-breathe  16s ease-in-out infinite\n  0% { transform: rotate(10deg) }  44% { transform: rotate(56deg) }\n  52% { transform: rotate(56deg) }  96% { transform: rotate(10deg) }\n  100% { transform: rotate(10deg) }\nring-spin         28s linear infinite          from { transform: rotate(24deg) } to { transform: rotate(384deg) }\nticks-spin        40s linear infinite reverse  (360deg)\nfringe-spin       34s linear infinite reverse  (360deg)\nbloom-pulse       9s ease-in-out infinite  0%,100% { opacity: 0.62; transform: translate(-50%,-50%) scale(1) }  50% { opacity: 0.90; transform: translate(-50%,-50%) scale(1.05) }\nfringe-breathe    7s ease-in-out infinite  0%,100% { opacity: 0.24 }  50% { opacity: 0.38 }\nflare-drift       22s ease-in-out infinite  0%,100% { transform: translate(-50%,-50%) translateX(-2.5%) rotate(-2.4deg); opacity: 0.34 }  50% { transform: translate(-50%,-50%) translateX(2.5%) rotate(1.6deg); opacity: 0.58 }\ngrain             0.9s steps(4) infinite  translate among (0,0) (-2%,1%) (1%,-2%) (-1%,2%)\n```\n\nH1 masking: each line is <span class=\"line\"><span class=\"line-inner appear appear--mask\" style=\"--d:0.76s\">…</span></span> with .line { display:block; overflow:hidden; padding: 0.06em 0.06em 0.12em } and .line-inner { display:inline-block } — type rising from a hard edge. Periods 16s/28s/34s/40s/9s/7s are non-harmonic so the optics never re-sync.\n\n---\n\n## H. RESPONSIVE TABLE — exact token overrides per breakpoint\n\n```\n>= 1600px:\n  --iris: 640px  --h1: 92px  --lead: 19px  --logo: 17px  --nav: 14px  --nav-h: 40px\n  --btn: 14px  --btn-h: 46px  --stat-val: 30px\n  --header-y: 30px  --header-x: 56px  --hero-x: 56px  --hero-bottom: 64px  --copy-max: 820px\n\n>= 1920px:\n  --iris: 720px  --h1: 106px  --lead: 20px  --stat-val: 34px  --stat-label: 12.5px\n  --header-x: 72px  --hero-x: 72px  --hero-bottom: 72px  --copy-max: 900px\n\n1280px – 1599px:\n  --iris: 520px  --h1: 74px  --copy-max: 680px\n\n901px – 1279px:\n  --iris: 440px  --iris-top: 38%  --h1: 58px  --lead: 16px  --stat-val: 24px\n  --nav: 12.5px  --btn: 12.5px  --header-x: 30px  --hero-x: 30px  --hero-bottom: 44px  --copy-max: 600px\n\n>= 901px AND max-height 850px:\n  --iris: 420px  --iris-top: 35%  --h1: 54px  --lead: 16px  --stat-val: 22px  --chip-h: 28px\n  --header-y: 18px  --hero-bottom: 36px  (h1 margin-top 14px, lead 16px, actions 22px)\n\n>= 901px AND max-height 720px:\n  --iris: 340px  --iris-top: 32%  --h1: 44px  --lead: 15px  --eyebrow: 11px  --stat-val: 20px  --stat-label: 11px\n  --chip-h: 26px  --btn-h: 38px  --header-y: 14px  --hero-bottom: 26px  (footer padding 14px top, 18px bottom)\n\n>= 901px (desktop lock, all heights):\n  html, body { height:100%; overflow:hidden }  .page { height:100dvh; overflow:hidden }\n\n<= 900px:\n  no viewport lock; vertical scroll; nav + CTA move into a burger menu:\n  - header keeps .brand + <button class=\"burger\" aria-label=\"Open navigation\" aria-expanded=\"false\">, three 18x1.5px bars, var(--text), gap 5px\n  - .menu { position:fixed; inset:0; z-index:80; background: rgba(7,6,10,0.94); 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 cubic-bezier(0.16,1,0.3,1) }  .menu.open { transform: none }\n  - menu shows 3 nav links at 22px + the .cta chip; Escape, any link click, resize > 900px close it\n  --iris: min(78vw, 460px)  --iris-top: 34%  --h1: 46px  --lead: 16px  --stat-val: 22px\n  --header-x: 24px  --hero-x: 24px  --hero-bottom: 40px  --copy-max: 100%   (dust: 24 motes)\n\n<= 560px:\n  --iris: min(86vw, 340px)  --iris-top: 30%  --h1: 36px  --lead: 15px  --eyebrow: 11px\n  --stat-val: 19px  --stat-label: 10.5px  --header-x: 20px  --hero-x: 20px  --hero-bottom: 32px\n  stats footer stacks: grid-template-columns:1fr; li { padding: 12px 0; text-align: left !important }; li + li { border-left: 0; border-top: 1px solid var(--border-soft) }\n  .actions { width:100% }  .btn { flex: 1 1 100% }   (dust: 16 motes)\n```\n\n---\n\n## I. FAILURE GUARDS — the page is never blank, never broken\n\n1. **Black-first:** the FIRST CSS rule is html, body { background:#07060a !important; color:#f6f1ea }; body carries style=\"background:#07060a;color:#f6f1ea\"; a later rule restates html, body { background:#07060a; background: var(--bg, #07060a) }.\n2. **Resting opacity is 1:** every .appear element's un-animated computed style is its final state (opacity 1, no transform, no filter); keyframes own the FROM states; fill-mode:both holds them only during the delay. If CSS animation fails or JS never runs, the page renders complete.\n3. **Two-rAF entrance fallback:** after two nested requestAnimationFrame callbacks, for each .appear call el.getAnimations({ subtree:false }); if none reports playState \"running\"/\"finished\", add .is-in to ALL .appear. No getAnimations: run the sweep unconditionally after the two rAFs. .is-in forces animation:none !important; opacity:1 !important; transform:none !important; filter:none !important; clip-path:none !important.\n4. **Canvas fallback:** if canvas or getContext(\"2d\") is missing, add .no-canvas to <html>, push \"canvas-unavailable\" into window.__DEMO__.errors, and paint static dust on .backdrop::after with two radial-gradient dot layers (rgba(255,206,152,0.14) 0 1.5px, transparent 2px) at background-size 180px 220px and 260px 300px, offsets 40px 0 and 0 90px. Optics stay fully visible; only motes degrade.\n5. **Reduced motion:** inside @media (prefers-reduced-motion: reduce): *, *::before, *::after { transition:none !important; animation:none !important }, and .appear / .appear.is-in forced to opacity:1 !important; transform:none !important; filter:none !important; clip-path:none !important. Optics freeze at mid-breath resting transforms: .blade rotate(30deg) (half-open), .iris-ring rotate(24deg), .iris-ticks rotate(-12deg), .fringe rotate(38deg) opacity 0.32, .bloom translate(-50%,-50%) scale(1) opacity 0.76, .flare translate(-50%,-50%) rotate(-2.4deg) opacity 0.42, .grain display:none. Dust paints exactly ONE static frame at t = 0 then stops; ready still becomes true, frames stops at 1. Nothing moves.\n6. **Contrast floor:** h1 and stat values #f6f1ea on #07060a, lead #d8cfc4 over the scrim, labels #6b645b 12px uppercase above 4.5:1 — never drop text opacity below 0.86.\n7. **Zero console output:** no console calls anywhere, no thrown exceptions, every canvas and DOM call guarded.\n\n---\n\n## J. THE EXACT JS (only this, vanilla, one IIFE at end of body)\n\n1. **Telemetry first:** window.__DEMO__ = { name: 'halide', ready: false, frames: 0, effects: [], errors: [] }; then push exactly these effect names once, in order: 'aperture-iris', 'lens-bloom', 'chromatic-fringe', 'dust-motes', 'film-grain'. Then window.addEventListener('error', function (e) { window.__DEMO__.errors.push(String(e.message || e)); }).\n2. **Canvas dust loop:** cache document.documentElement, canvas#dust, the 2D context (null -> .no-canvas path, push \"canvas-unavailable\"), the 34 motes of section B ({ x0, y0, r, vy, swayAmp, swayHz, phase, alpha }), and a cached { w, h } refreshed only on resize. drawDust(t) clears and fills each mote with the time-pure formulas — one beginPath, one arc, one fill, fillStyle 'rgba(255,206,152,' + alpha + ')'. NEVER ctx.filter, ctx.shadowBlur, or getBoundingClientRect in the loop. Each rAF tick increments window.__DEMO__.frames by exactly 1 then paints; the FIRST completed paint sets ready = true. While document.hidden, skip paint, keep loop and counter.\n3. **Resize:** resizeCanvas() reads canvas.getBoundingClientRect() ONCE (the only permitted call), recomputes { w, h }, sets dpr = Math.min(window.devicePixelRatio || 1, 2), canvas.width = w * dpr, canvas.height = h * dpr, ctx.setTransform(dpr, 0, 0, dpr, 0, 0), repaints. Schedule via one boolean resizeQueued + requestAnimationFrame — no timers.\n4. **Entrance fallback:** the two nested rAFs, the getAnimations check, the .is-in sweep (Guard 3).\n5. **Mobile menu (<= 900px):** burger click toggles .menu.open and aria-expanded; Escape, any menu link click, and resize past 900px close it.\n6. **prefers-reduced-motion:** read matchMedia('(prefers-reduced-motion: reduce)') once; if it matches, drawDust(0) once, set ready on that paint, count that frame, schedule no further rAF.\n\nNo other JS. No libraries. No timers. No console calls.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the document contains exactly one h1 built from exactly two .line > .line-inner masked lines with the visible copy 'Night is' and 'undeveloped light.', and uses semantic header, nav, main, and footer landmarks with exactly 3 nav links and 3 stat items","an svg.iris exists with exactly 8 .blade groups animating rotate(10deg) to rotate(56deg) on a 16s aperture-breathe loop, and a .iris-ring with a conic-gradient background rotating 360deg over 28s linear","window.__DEMO__ is initialized with name 'halide', ready becomes true after the first painted dust frame, frames increments once per requestAnimationFrame tick (>= 20 per 1.2s at 1440x900), effects contains aperture-iris, lens-bloom, chromatic-fringe, dust-motes and film-grain, and errors stays empty in the happy path","canvas#dust paints non-blank pixel content at 1440x900 and the script never calls ctx.filter, ctx.shadowBlur, or getBoundingClientRect inside the animation loop","no horizontal overflow at 390px, 768px, 1440px or 1920px; at >= 901px body has overflow:hidden and .page height is 100dvh; a prefers-reduced-motion: reduce block freezes all optics and dust while leaving every text element at opacity 1","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"],"usage":{"build_tokens":28032,"build_prompt_tokens":117,"build_completion_tokens":27915,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":3,"build_latency_ms":271813,"wasted_tokens":42357,"repair_tokens":49093,"crosscheck_tokens":23269,"total_tokens":100394},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"32472 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"29 frames, effects: aperture-iris, lens-bloom, chromatic-fringe, dust-motes, film-grain"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"26 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 32.29, stdev 74.55, coverage 0.1591"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 3940025823 -> 845380663"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Night is\nundeveloped light.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Halide — Night is Undeveloped Light"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=387 pageErrors=0"}]},"crosscheck":{"slug":"halide","reviewer_model":"gpt-5.5","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":6,"requirements_met":["Single cinematic viewport composition at desktop with header, hero, and three-stat footer visible","Required Halide copy is present and specific","Header navigation, CTA, hero chip, two headline lines, lead, two buttons, and three stats are present","Procedural optical stack is present: iris SVG, conic ring, bloom, fringe, dust canvas, grain","Desktop screenshot shows no scrollbars or horizontal overflow"],"requirements_missed":["Dust motes are not implemented as tiny 0.6px–2.2px arcs; they render as large glowing blobs across the page","Bloom and bloom-core are mis-centered in CSS and do not sit cleanly behind the lens axis","The requested single visual idea is weakened: the iris/lens is dim and visually secondary to scattered glow blobs","Canvas dust implementation violates the specified exact fill style and primitive budget behavior","Optical centering uses negative margins instead of the specified translate centering in several layers"],"issues":[{"issue":"Dust motes render as oversized glowing orbs instead of tiny rising dust particles","severity":"high","evidence":"Screenshot shows many large soft circular blobs, some tens of pixels wide; JS builds gradients with `var R = (30 + m.r * 12) * scale` and draws `ctx.arc(0, 0, m.R, 0, TAU)` instead of radius 0.6px–2.2px."},{"issue":"Canvas dust does not follow the brief's exact per-mote paint style","severity":"high","evidence":"Brief requires `fillStyle 'rgba(255,206,152,' + alpha + ')'` and radius 0.6px–2.2px; generated code uses cached radial gradients via `ctx.createRadialGradient(...)` and large halo radii."},{"issue":"Bloom layers are mathematically mis-centered relative to the iris","severity":"high","evidence":"`.bloom` sets `left:50%`, `margin-left:calc(var(--iris) * -0.775)`, and also `transform:translate(-50%,-50%)`; this double-applies horizontal and vertical centering offset for an element already sized `calc(var(--iris) * 1.55)`."},{"issue":"Bloom-core has the same double-centering error","severity":"medium","evidence":"`.bloom-core` uses `margin-left:calc(var(--iris) * -0.25); margin-top:calc(var(--iris) * -0.25); transform:translate(-50%,-50%)` on an element sized `calc(var(--iris) * 0.5)`."},{"issue":"The lens is visually too subdued compared with the accidental dust glow field","severity":"medium","evidence":"In the 1024x640 screenshot the central iris is dark and low-contrast while the page is dominated by large scattered warm light spots, contrary to the brief's 'one light behind one lens' composition."},{"issue":"The requested bloom behind the optic is not visibly centered behind the iris","severity":"medium","evidence":"Screenshot shows no strong warm radial bloom centered on the iris; the brightest glow is distributed as separate blobs around the viewport."},{"issue":"Optical stage centering does not use the specified transform method","severity":"low","evidence":"Brief specifies `.iris-stage ... transform:translate(-50%,-50%)`; generated CSS uses `margin-left:calc(var(--iris) / -2); margin-top:calc(var(--iris) / -2);` and no centering transform."},{"issue":"Mobile menu default CSS differs from specified display behavior","severity":"low","evidence":"Brief specifies `.menu { display:flex; ... transform: translateY(-100%) }`; generated CSS sets `.menu{ display:none; ... visibility:hidden; }` and only enables display in the mobile media query."}],"summary":"The page has the right content structure and a recognizable dark photographic landing-page composition, but the optical effect is compromised by oversized glowing dust blobs and mis-centered bloom layers. It looks competent, not blank or broken, but it does not faithfully deliver the brief's precise 'one light behind one lens' cinematic optical stack.","usage":{"input":21731,"output":1538,"total":23269,"reasoning":516,"cached":0},"latency_ms":23316}},{"slug":"headlight","title":"Headlight","category":"Landing Page","effect":"A procedural night-city long-exposure canvas that evolves continuously behind a bottom-anchored Nocturne ride-service interface.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/headlight/index.html","doc":"/prompts/headlight.md","prompt_text":"A. WHAT IS BEING BUILT\n\nBuild one complete single-file index.html for a cinematic landing page titled “Headlight”. Set the document language to lang=\"en\", use the document title “Headlight — Nocturne”, and include the viewport meta tag with width=device-width, initial-scale=1, viewport-fit=cover. The page is a night-city ride-service brand presentation for Nocturne, operating only from 23:00 through 05:00.\n\nAt viewport widths of 901px and wider, html, body, and the app shell must be exactly 100dvh high, with overflow:hidden. There is no page scroll at desktop sizes. At widths of 900px and narrower, the document may scroll vertically, but it must never scroll horizontally. The canvas is a visual background, not a video. All visible content remains readable when animation is reduced.\n\nUse semantic real page structure: one header, one main, one footer, and one navigation region. There must be exactly one visible h1. Every visible interactive element must have an accessible name through visible text or aria-label. Every focusable control must have a visible :focus-visible ring.\n\nThe design direction is editorial, restrained, and cinematic: a black road surface, accumulated sodium-amber traffic trails, sparse red brake-light interruptions, and cool white-blue headlight accents. The interface is left-heavy and typographic, with the copy anchored in the lower-left quadrant and a single CTA pill anchored in the lower-right. The visual should feel like a long-exposure photograph taken from a moving car, not a gradient or neon particle demo.\n\nDo NOT build a video element, image element, remote asset, base64 asset, third-party CDN, React, Tailwind, GSAP, Three.js, Lottie, WebGL renderer, audio player, cookie banner, modal, authentication flow, map, pricing calculator, route planner, or multi-page navigation. Do NOT use an external font request. Use only CSS gradients, inline SVG, or canvas. Do NOT add a second h1. Do NOT make the lower third visually noisy. Do not use horizontal scrolling. Do not add decorative controls without an accessible name. Do not use ctx.filter or ctx.shadowBlur inside any per-particle loop.\n\nB. THE BACKDROP\n\nCreate one black canvas with id=\"traffic-canvas\", positioned as a fixed full-viewport layer. Use a black background matching #050507. The canvas is decorative with aria-hidden=\"true\", but it must remain the principal visual backdrop.\n\nThe procedural particle system contains exactly 140 particles. Seed the particle data once at startup with a deterministic seeded pseudo-random generator using seed 27041986. Each particle has a normalized x position, normalized y position, velocity, length, width, alpha, hue, and phase. No particle is recreated after startup. Seed the positions so 104 particles use sodium amber, 39 particles use brake red, and 17 particles use headlight blue-white, producing 74.3% amber, 27.9% red, and 12.1% blue-white within rounding tolerance.\n\nThe advection field is:\na = sin(x * 0.004 + t * 0.2) * cos(y * 0.003)\n\nTreat x and y as canvas pixel coordinates. Convert the scalar angle-like value into a cheap velocity rotation. Each frame, calculate vx = cos(a) * speed and vy = sin(a) * speed, where speed varies by particle from 0.42 to 1.18 pixels per frame. Add a small forward velocity of 0.24 to 0.62 pixels per frame in the x direction. Keep particles in an infinite conceptual field by wrapping x from -40 to width + 40 and y from -40 to height + 40.\n\nEach particle must leave a 6px to 34px trail by stamping points into an offscreen buffer. Create one offscreen canvas with the same backing dimensions as the visible canvas. Every frame, fade the offscreen buffer to 6% opacity rather than clearing it. Implement the fade by filling the buffer with rgba(5,5,7,0.06) using source-over. Then draw all particle stamps with globalCompositeOperation = \"lighter\". Each particle contributes a 3px vertical smear: draw its previous and current positions with a 3px-wide rounded line or three adjacent alpha-weighted stamps. Trail alpha is 0.12 to 0.42. Hues are restricted to 18deg through 42deg for sodium trails, 358deg through 8deg for brake red, and 204deg through 222deg for headlight blue. Do not use a rainbow palette.\n\nThe visible canvas composites the offscreen buffer onto a CSS-filtered canvas with filter: blur(0.4px). The canvas element is not allowed to use a larger blur. The offscreen buffer is not allowed to use ctx.filter or ctx.shadowBlur. Composite the buffer onto the visible canvas with globalCompositeOperation = \"lighter\" and globalAlpha controlled by the exposure ramp.\n\nPlace exactly two oversized radial-gradient glow fields behind the canvas. Use one 500px circle with center rgba(42,22,8,0.42), transparent edge rgba(42,22,8,0), placed left:18%, top:24%, and one 500px circle with center rgba(10,20,32,0.34), transparent edge rgba(10,20,32,0), placed right:12%, bottom:8%. Each uses filter: blur(70px). These are static CSS layers at z-index 0, not animated particles. They create road-surface value without competing with the type.\n\nAt 60fps, apply a 6% per-frame buffer fade, equivalent to a visible tail of approximately 16 frames. Do not use a hard reset, timed scene loop, or particle respawn sequence. The field is continuously evolving and must not visibly repeat. Apply cursor parallax to the entire canvas container: translate it by a maximum of 10px in x and 10px in y toward the pointer. Use a 0.04 lerp per frame. Pointer values are normalized from the viewport center and clamped to [-1,1].\n\nFor the canvas backing resolution, use a maximum device pixel ratio of 1.5. Set backing width to Math.round(clientWidth * dpr), backing height to Math.round(clientHeight * dpr), cap the total backing pixel count at 2,500,000, and reduce dpr to 1 if the cap would be exceeded. The offscreen buffer uses the same dimensions. Set canvas.width and canvas.height only on resize, never inside the animation loop.\n\nThe per-frame cost budget is 140 particle updates, 140 current-position stamps, 140 previous-position stamps, one buffer fade fill, and one visible-buffer composite. Use no more than 420 draw operations per frame. Cache the canvas contexts, dimensions, particle objects, color strings, and alpha values before the loop. Never call getBoundingClientRect inside rAF. The rAF loop must sustain at least 20 frames per 1.2 seconds at 1440x900 headless desktop resolution. Use transform and opacity for DOM motion. The canvas drawing itself may update pixels, but all DOM animation must be transform/opacity only.\n\nC. THE LAYER STACK\n\nUse these exact z-index values from back to front:\n\nz-index 0: the two static glow fields inside .road-glows.\nz-index 1: the black canvas layer .canvas-wrap, containing the traffic canvas and its exposure overlay.\nz-index 2: a full-viewport vignette .vignette with transparent center and rgba(0,0,0,0.52) edges.\nz-index 3: a fine grain layer .grain using a CSS repeating radial-gradient pattern at opacity 0.045, pointer-events:none.\nz-index 4: the semantic page shell .site-shell, containing header, main, and footer.\nz-index 5: focus rings and dialogs if any, although no dialog is required.\nz-index 6: the bottom-right CTA cluster .cta-cluster.\n\nThe canvas-wrap must have inset:-12px so its ±10px parallax never exposes an unpainted edge. The canvas itself must be width:100%, height:100%, display:block. The page shell must use position:relative and pointer-events:none for decorative regions only; all text and controls must receive pointer-events:auto.\n\nD. TOKEN TABLE\n\nDefine every visual size and color as CSS custom properties on :root:\n\n--bg: #050507;\n--ink: #F4F0E8;\n--muted: #A39D92;\n--dim: #6F6A63;\n--amber: #FFB347;\n--red: #FF5E3A;\n--blue: #7FB2FF;\n--line: rgba(244,240,232,0.18);\n--line-strong: rgba(244,240,232,0.34);\n--panel: rgba(5,5,7,0.34);\n--focus: #F4F0E8;\n--max-width: 1440px;\n--content-x: 72px;\n--header-height: 82px;\n--footer-height: 34px;\n--hero-top: 26vh;\n--hero-bottom: 0px;\n--hero-gap: 34px;\n--type-display: clamp(54px, 7.2vw, 128px);\n--type-body: 16px;\n--type-small: 12px;\n--type-label: 11px;\n--button-height: 52px;\n--button-padding-x: 24px;\n--radius-pill: 999px;\n--radius-small: 8px;\n--canvas-blur: 0.4px;\n--glow-blur: 70px;\n--glow-size: 500px;\n--parallax-max: 10px;\n--exposure-duration: 1400ms;\n--ease-out: cubic-bezier(0.16, 1, 0.3, 1);\n--ease-smooth: cubic-bezier(0.22, 1, 0.36, 1);\n\nUse the system font stack: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, “Segoe UI”, sans-serif. Inter may be used only if already installed; no Google Fonts request is needed.\n\nE. THE COMPOSITION\n\nBuild the page in this order.\n\nFirst, add the static backdrop elements inside a div.road-backdrop. Add two .glow elements with exact 500px dimensions and blur(70px), then the canvas-wrap and vignette. The canvas-wrap receives the canvas and an absolutely positioned .exposure-wash with background:#050507 and opacity:0.\n\nSecond, create header.site-header with position:absolute, top:0, left:var(--content-x), right:var(--content-x), height:var(--header-height), display:flex, align-items:center, justify-content:space-between, z-index:4. The left brand is a link or span with the exact visible text “NOCTURNE” and aria-label “Nocturne home”. Set it to font-size:13px, font-weight:700, letter-spacing:0.24em, color:var(--ink). The right nav contains exactly two visible links: “The service” and “Safety”. Both must have href=\"#service” and href=\"#safety” respectively, with accessible names, color:var(--muted), font-size:var(--type-small), and a 22px horizontal gap.\n\nThird, create main#service.hero. Position it as a full-height flex container using display:grid, grid-template-columns:minmax(0, 1fr) auto, grid-template-rows:1fr auto, align-items:end, column-gap:var(--hero-gap), row-gap:24px, padding:0 var(--content-x) 62px. The hero must sit above the footer and leave the lower third visually readable. The copy block is grid-column:1, grid-row:2, max-width:700px. The CTA cluster is grid-column:2, grid-row:2, align-self:end, padding-bottom:4px.\n\nThe copy block contains a small eyebrow with exact text “NIGHT SERVICE / 23:00—05:00”, followed by exactly one h1 with the exact text “Take the long way home.” Set h1 margin:16px 0 22px, max-width:700px, color:var(--ink), font-size:var(--type-display), line-height:0.91, font-weight:650, letter-spacing:-0.065em. Add a paragraph with exact text “Nocturne moves you through the sleeping city with a driver, a quiet cabin, and the road already awake.” Set max-width:410px, margin:0, color:var(--muted), font-size:var(--type-body), line-height:1.55.\n\nFourth, add a service-status strip above the CTA or directly below the paragraph with exact text “LIVE WINDOW · NEXT DEPARTURE 23:18” and a small 7px amber status dot. The strip must be inline-flex, gap:10px, align-items:center, height:30px, margin-top:26px, border-top:1px solid var(--line), border-bottom:1px solid var(--line), color:var(--muted), font-size:var(--type-label), letter-spacing:0.14em.\n\nThe CTA cluster contains one primary pill button with exact visible text “Book a Nocturne” and aria-label “Book a Nocturne ride”. It is 52px high, padding 0 24px, border-radius:999px, and uses a horizontal gradient from #FFB347 to #FFD27A. Text is #050507, font-size:13px, font-weight:750. It must be a real button or link. Beside it, place one quiet text link with exact text “See how it works”, href=\"#how-it-works”, color:var(--ink), font-size:13px, and underline offset:5px. The CTA cluster is display:flex, align-items:center, gap:20px.\n\nSixth, create footer.site-footer with position:absolute, left:var(--content-x), right:var(--content-x), bottom:0, height:var(--footer-height), display:flex, align-items:center, justify-content:space-between, color:var(--dim), font-size:10px, letter-spacing:0.1em. Left text: “NOCTURNE / CITY TRANSIT AFTER DARK”. Right text: “23:00—05:00 / 365 NIGHTS”. Do not add another navigation control.\n\nThe darkest quadrant is the lower-left. The particle field must be biased toward y positions between 4% and 62% of the canvas height. Exactly 24 particles may enter the lower 38% at reduced alpha, but no particle may be rendered over the h1 at opacity above 0.18. Apply a CSS linear-gradient from rgba(5,5,7,0.04) at 42% to rgba(5,5,7,0.84) at 78% as a readability scrim inside the page shell.\n\nF. THE UI LANGUAGE\n\nThe primary button uses background:linear-gradient(100deg, #FFB347 0%, #FFC76B 52%, #FFD98C 100%), border:1px solid rgba(255,235,190,0.72), box-shadow:0 8px 26px rgba(255,179,71,0.18). Its resting transform is translate3d(0,0,0). On hover, change transform to translate3d(0,-2px,0), increase box-shadow to 0 12px 30px rgba(255,179,71,0.28), and change the gradient endpoint to #FFE1A2. On active, translate to translate3d(0,1px,0) and use box-shadow:none. The transition is transform 180ms var(--ease-out), box-shadow 180ms var(--ease-out), background 180ms linear.\n\nThe quiet link rests with color:#F4F0E8 and opacity:0.78. On hover, opacity:1 and color:#FFB347. The transition is opacity 160ms linear, color 160ms linear. All links have text-decoration:none except the quiet link, which uses text-decoration:underline.\n\nButtons and links use border-radius:999px or the defined small radius. The visible focus state is outline:2px solid var(--focus), outline-offset:4px, with a 1px dark inner contrast ring. Do not remove native focus styles without replacement. The cursor is default except for links and buttons, where cursor:pointer is used.\n\nG. MOTION\n\nUse CSS keyframes named exposure-ramp, copy-rise, eyebrow-rise, cta-rise, and status-pulse. The canvas exposure begins at opacity:0 and reaches opacity:1 over 1400ms using exposure-ramp from opacity:0 to opacity:1. The canvas-wrap itself translates from translate3d(-10px, 6px, 0) to translate3d(0,0,0), but pointer parallax writes inline transform on the same element after the initial animation. To prevent conflict, put the entrance animation on .canvas-inner and the parallax transform on .canvas-wrap.\n\nThe DOM entrance sequence is:\n\n- header: opacity 0→1, transform translate3d(0,-10px,0)→translate3d(0,0,0), duration 700ms, delay 80ms, easing var(--ease-out).\n- eyebrow: opacity 0→1, transform translate3d(0,12px,0)→translate3d(0,0,0), duration 620ms, delay 260ms.\n- h1: opacity 0→1, transform translate3d(0,24px,0)→translate3d(0,0,0), duration 900ms, delay 340ms.\n- paragraph: opacity 0→1, transform translate3d(0,18px,0)→translate3d(0,0,0), duration 720ms, delay 520ms.\n- status strip: opacity 0→1, transform translate3d(0,10px,0)→translate3d(0,0,0), duration 600ms, delay 720ms.\n- CTA cluster: opacity 0→1, transform translate3d(14px,0,0)→translate3d(0,0,0), duration 720ms, delay 620ms.\n- footer: opacity 0→1, transform translate3d(0,8px,0)→translate3d(0,0,0), duration 600ms, delay 900ms.\n- canvas: exposure-ramp 1400ms, delay 0ms.\n- status dot: status-pulse 1800ms ease-in-out infinite, scaling 0.82 to 1.18 and opacity 0.55 to 1.\n\nUse the following named keyframes exactly:\n@keyframes exposure-ramp { from { opacity: 0; } to { opacity: 1; } }\n@keyframes copy-rise { from { opacity: 0; transform: translate3d(0,24px,0); } to { opacity: 1; transform: translate3d(0,0,0); } }\n@keyframes eyebrow-rise { from { opacity: 0; transform: translate3d(0,12px,0); } to { opacity: 1; transform: translate3d(0,0,0); } }\n@keyframes cta-rise { from { opacity: 0; transform: translate3d(14px,0,0); } to { opacity: 1; transform: translate3d(0,0,0); } }\n@keyframes status-pulse { 0%,100% { opacity: 0.55; transform: scale(0.82); } 50% { opacity: 1; transform: scale(1.18); } }\n\nThe JavaScript animation loop continuously updates the particle buffer, exposure, and pointer parallax. Effects pushed into telemetry must be exactly “field-advection”, “trail-decay”, “cursor-parallax”, and “exposure-ramp”. Increment window.__DEMO__.frames once for every rAF callback.\n\nH. THE RESPONSIVE TABLE\n\nAt 1920x1080, set --content-x:96px, --type-display:128px, --hero-bottom:0px, --button-height:52px. Keep the desktop grid as minmax(0,1fr) auto.\n\nAt 1440x900, set --content-x:72px, --type-display:104px, --header-height:82px, --hero-bottom:0px, and hero padding-bottom:62px.\n\nAt 768x1024, set --content-x:36px, --header-height:70px, --type-display:76px, --hero-bottom:0px, and --button-height:50px. Change .hero to grid-template-columns:1fr, grid-template-rows:1fr auto auto, padding-bottom:48px. Copy occupies grid-column:1 and grid-row:2. The CTA cluster occupies grid-column:1, grid-row:3, justify-self:start, padding-bottom:0. The canvas glow positions become left:8%, top:18% and right:2%, bottom:18%. Allow vertical document scrolling because 768 is below 901px.\n\nAt 390x844, set --content-x:22px, --header-height:62px, --type-display:58px, --type-body:15px, --type-small:11px, --button-height:50px, --button-padding-x:20px, and --hero-gap:22px. Hide the footer’s left text while keeping the right operating-hours text. Header navigation gap is 14px. The CTA cluster is width:100%, flex-direction:column, align-items:flex-start, gap:14px. The h1 max-width:350px, line-height:0.94, margin-bottom:18px. The paragraph max-width:335px. The status strip width:100%, font-size:9px, letter-spacing:0.1em. Canvas glow size is 360px, blur is 58px, and the glow positions are left:-90px, top:18% and right:-120px, bottom:12%. Keep all content inside the viewport width and allow vertical scrolling.\n\nAdd a max-height:760px desktop override for widths 901px and above. Reduce --header-height to 64px, --type-display to 72px, --hero-bottom to 0px, hero padding-bottom to 42px, and h1 margin-top to 12px. Reduce paragraph line-height to 1.4 and button height to 46px. Keep the bottom third clear.\n\nAdd a max-height:620px desktop override for widths 901px and above. Reduce --header-height to 54px, --type-display to 58px, --hero-bottom to 0px, hero padding-bottom to 30px, h1 margin:10px 0 14px, and footer height to 24px. Hide the paragraph only if its measured content would extend below 30px from the viewport bottom; otherwise preserve it. No CSS overflow-x is allowed at any breakpoint.\n\nI. FAILURE GUARDS\n\nEvery animated DOM element must have a resting opacity:1 rule after its entrance animation, so a failed CSS animation cannot leave invisible copy. The canvas wrapper must default to opacity:1, and the exposure wash must default to opacity:1 after the ramp. Do not use an animation-fill-mode that permanently leaves an element hidden.\n\nImplement a two-rAF fallback. After DOMContentLoaded, schedule the first animation frame with requestAnimationFrame(begin), and schedule a second with requestAnimationFrame(begin) only if the first callback did not start the loop. The begin function must be idempotent, guarded by a boolean named loopStarted. If requestAnimationFrame is unavailable, start a static seeded frame once, set exposure to 1, set window.__DEMO__.ready = true after first paint, and do not schedule further frames.\n\nIf ResizeObserver is unavailable, use a cached window resize listener. Resize handling may read client dimensions and update canvas dimensions outside the rAF loop. During resize, preserve the previous offscreen image by copying it into a temporary canvas at the old size and drawing it into the new buffer with globalAlpha 0.55. Debounce resize work by 120ms.\n\nUnder prefers-reduced-motion: reduce, remove all entrance animations, stop cursor parallax, and render one static seeded canvas frame with exposure 1. Keep the page fully readable. Do not stop semantic controls or hover focus states. Set the animation loop to a static mode and do not increment frames after the first static frame.\n\nJ. THE EXACT JS\n\nThe script must be plain JavaScript placed at the end of body. It must use this structure and behavior:\n\nconst demo = window.__DEMO__ = { name: \"headlight\", ready: false, frames: 0, effects: [], errors: [] };\nwindow.addEventListener(\"error\", event => { demo.errors.push(String(event.message || \"Unknown error\")); });\n\nconst canvas = document.getElementById(\"traffic-canvas\");\nconst context = canvas.getContext(\"2d\", { alpha: true });\nconst buffer = document.createElement(\"canvas\");\nconst bufferContext = buffer.getContext(\"2d\", { alpha: true });\nconst wrap = document.querySelector(\".canvas-wrap\");\nconst reduceMotion = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\nlet width = 0, height = 0, dpr = 1, bufferWidth = 0, bufferHeight = 0;\nlet pointerX = 0, pointerY = 0, targetX = 0, targetY = 0, currentX = 0, currentY = 0;\nlet loopStarted = false, running = true, lastTime = 0, elapsed = 0, exposure = 0;\nlet seed = 27041986;\nconst random = () => { seed = (seed * 1664525 + 1013904223) >>> 0; return seed / 4294967296; };\nconst particles = Array.from({ length: 140 }, (_, index) => {\n  const group = index < 39 ? \"red\" : index < 56 ? \"blue\" : \"amber\";\n  const color = group === \"red\" ? \"255,94,58\" : group === \"blue\" ? \"127,178,255\" : \"255,179,71\";\n  return { x: random() * Math.max(window.innerWidth, 1), y: random() * Math.max(window.innerHeight * 0.62, 1), px: 0, py: 0, speed: 0.42 + random() * 0.76, forward: 0.24 + random() * 0.38, length: 6 + random() * 28, alpha: 0.12 + random() * 0.30, hue: group === \"red\" ? 358 + random() * 10 : group === \"blue\" ? 204 + random() * 18 : 18 + random() * 24, color, group };\n});\nparticles.forEach(particle => { particle.px = particle.x; particle.py = particle.y; });\n\nfunction resize() {\n  width = window.innerWidth;\n  height = window.innerHeight;\n  dpr = Math.min(window.devicePixelRatio || 1, 1.5);\n  if (width * height * dpr * dpr > 2500000) dpr = 1;\n  bufferWidth = canvas.width = Math.round(width * dpr);\n  bufferHeight = canvas.height = Math.round(height * dpr);\n  buffer.width = bufferWidth;\n  buffer.height = bufferHeight;\n  context.setTransform(dpr, 0, 0, dpr, 0, 0);\n  bufferContext.setTransform(dpr, 0, 0, dpr, 0, 0);\n  bufferContext.fillStyle = \"#050507\";\n  bufferContext.fillRect(0, 0, width, height);\n}\nfunction frame(time) {\n  if (!running) return;\n  if (!lastTime) lastTime = time;\n  const delta = Math.min(32, time - lastTime);\n  lastTime = time;\n  elapsed += delta * 0.0166667;\n  exposure = Math.min(1, exposure + delta / 1400);\n  currentX += (targetX - currentX) * 0.04;\n  currentY += (targetY - currentY) * 0.04;\n  wrap.style.transform = \"translate3d(\" + (currentX * 10).toFixed(2) + \"px,\" + (currentY * 10).toFixed(2) + \"px,0)\";\n  bufferContext.globalCompositeOperation = \"source-over\";\n  bufferContext.fillStyle = \"rgba(5,5,7,0.06)\";\n  bufferContext.fillRect(0, 0, width, height);\n  bufferContext.globalCompositeOperation = \"lighter\";\n  for (const particle of particles) {\n    const angle = Math.sin(particle.x * 0.004 + elapsed * 0.2) * Math.cos(particle.y * 0.003);\n    const vx = Math.cos(angle) * particle.speed + particle.forward;\n    const vy = Math.sin(angle) * particle.speed;\n    particle.px = particle.x;\n    particle.py = particle.y;\n    particle.x += vx;\n    particle.y += vy;\n    if (particle.x < -40) particle.x = width + 40;\n    if (particle.x > width + 40) particle.x = -40;\n    if (particle.y < -40) particle.y = height + 40;\n    if (particle.y > height + 40) particle.y = -40;\n    const lowerFade = particle.y > height * 0.62 ? Math.max(0.18, 1 - (particle.y - height * 0.62) / (height * 0.38)) : 1;\n    const alpha = particle.alpha * lowerFade;\n    bufferContext.strokeStyle = \"hsla(\" + particle.hue + \",100%,68%,\" + alpha.toFixed(3) + \")\";\n    bufferContext.lineWidth = 3;\n    bufferContext.lineCap = \"round\";\n    bufferContext.beginPath();\n    bufferContext.moveTo(particle.px, particle.py);\n    bufferContext.lineTo(particle.x, particle.y);\n    bufferContext.stroke();\n  }\n  context.globalCompositeOperation = \"source-over\";\n  context.clearRect(0, 0, width, height);\n  context.globalCompositeOperation = \"lighter\";\n  context.globalAlpha = exposure;\n  context.drawImage(buffer, 0, 0, width, height);\n  context.globalAlpha = 1;\n  context.globalCompositeOperation = \"source-over\";\n  demo.frames += 1;\n  if (exposure >= 1 && demo.effects.indexOf(\"exposure-ramp\") === -1) demo.effects.push(\"exposure-ramp\");\n  if (demo.effects.indexOf(\"field-advection\") === -1) demo.effects.push(\"field-advection\");\n  if (demo.effects.indexOf(\"trail-decay\") === -1) demo.effects.push(\"trail-decay\");\n  if (demo.effects.indexOf(\"cursor-parallax\") === -1) demo.effects.push(\"cursor-parallax\");\n  if (demo.frames === 1) requestAnimationFrame(() => { demo.ready = true; });\n  requestAnimationFrame(frame);\n}\nfunction begin() {\n  if (loopStarted) return;\n  loopStarted = true;\n  if (reduceMotion.matches) {\n    elapsed = 0;\n    exposure = 1;\n    bufferContext.fillStyle = \"#050507\";\n    bufferContext.fillRect(0, 0, width, height);\n    particles.forEach(particle => {\n      const angle = Math.sin(particle.x * 0.004) * Math.cos(particle.y * 0.003);\n      particle.px = particle.x - Math.cos(angle) * particle.length;\n      particle.py = particle.y - Math.sin(angle) * particle.length;\n      bufferContext.strokeStyle = \"hsla(\" + particle.hue + \",100%,68%,\" + particle.alpha.toFixed(3) + \")\";\n      bufferContext.lineWidth = 3;\n      bufferContext.lineCap = \"round\";\n      bufferContext.beginPath();\n      bufferContext.moveTo(particle.px, particle.py);\n      bufferContext.lineTo(particle.x, particle.y);\n      bufferContext.stroke();\n    });\n    context.globalCompositeOperation = \"lighter\";\n    context.drawImage(buffer, 0, 0, width, height);\n    demo.effects.push(\"field-advection\", \"trail-decay\", \"cursor-parallax\", \"exposure-ramp\");\n    demo.frames = 1;\n    demo.ready = true;\n    return;\n  }\n  requestAnimationFrame(frame);\n}\nwindow.addEventListener(\"pointermove\", event => {\n  targetX = Math.max(-1, Math.min(1, event.clientX / width * 2 - 1));\n  targetY = Math.max(-1, Math.min(1, event.clientY / height * 2 - 1));\n}, { passive: true });\nwindow.addEventListener(\"resize\", resize, { passive: true });\nresize();\nrequestAnimationFrame(begin);\nrequestAnimationFrame(begin);\n\nThe script must not log to the console. It must not throw when pointer events, ResizeObserver, rAF, or canvas contexts are unavailable. If a context is unavailable, leave the CSS backdrop and readable content visible, set ready to true after first paint, and record no uncaught exception.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["The output is one valid index.html with no external media or framework dependencies, contains exactly one h1, and has real header, main, footer, and nav landmarks.","At 1440x900, html, body, and the app shell are 100dvh high with overflow:hidden; at 390x844, 768x1024, 1440x900, and 1920x1080, document scrollWidth never exceeds clientWidth.","The runtime object exists as window.__DEMO__ with name headlight, ready becomes true after first paint, frames increments once per rAF callback, effects contains the four named effects, and errors captures window errors.","The canvas starts with exactly 140 seeded particles, uses an offscreen 6-percent fading buffer, lighter compositing, 3px vertical stamps, hue values between 18 and 42 degrees for sodium particles, and never calls ctx.filter or ctx.shadowBlur in the particle loop.","With prefers-reduced-motion: reduce, the page remains readable with all text at opacity 1, the canvas renders a static seeded frame, cursor parallax is disabled, and no repeating rAF loop is scheduled.","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"],"usage":{"build_tokens":63961,"build_prompt_tokens":7991,"build_completion_tokens":55970,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":767830,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":21308,"total_tokens":85269},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"35798 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"3 frames, effects: field-advection, trail-decay, cursor-parallax"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"26 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 0.53, stdev 5.84, coverage 0.0139"},{"name":"canvas spans the viewport width","pass":true,"detail":"1464px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 3878568169 -> 4289543272"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Take the long way home.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Headlight — Nocturne"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=314 pageErrors=0"}]},"crosscheck":{"slug":"headlight","reviewer_model":"MiniMaxAI/MiniMax-M3","reviewer_platform":"cmdcode","verdict":"partial","confidence":"high","aesthetic_score":5,"requirements_met":["Document lang, title, and viewport meta correct","All required copy strings present verbatim (eyebrow, h1, paragraph, status, footer, CTA)","Single h1 with required text and styling tokens","CTA cluster with primary pill button and quiet link","Two radial glow layers with correct colors, sizes, positions, and 70px blur","Canvas backdrop with inset:-12px and CSS blur(0.4px)","Vignette and grain layers present at correct z-indexes","Footer shows required left/right text strings","Readability scrim implemented as gradient on .site-shell::before","System font stack used, no Google Fonts request","Header brand 'NOCTURNE' with letter-spacing 0.24em and nav links to #service/#safety","Reduced-motion fallback implemented with static seeded frame","Telemetry hook with name, ready, frames, effects, errors fields","Canvas DPR handling with cap and 2.5M pixel budget"],"requirements_missed":["Particle counts wrong: brief requires 39 red + 17 blue + 104 amber = 160 total, but implementation uses 39 red + 17 blue + 84 amber = 140 total with `index < 39 ? 'red' : index < 56 ? 'blue' : 'amber'`","Tone-mapping wrong: brief says 74.3% amber / 27.9% red / 12.1% blue, implementation produces 60% amber / 27.9% red / 12.1% blue","Hue ranges wrong: brief says sodium 18–42deg, brake red 358–8deg (wrapping), headlight blue 204–222deg; implementation uses 358–368 / 204–222 / 18–42 which collapses the percentage signs into numeric degree ranges (e.g. `358 + random() * 10` produces 358–368, not 358–8 wrap)","Excess DOM wrappers: brief requires the exposure wash to be inside .canvas-wrap; implementation wraps canvas in two additional divs (.canvas-inner, .canvas-drift) plus the wash, adding opacity-fading overlays not in the brief","Body still overflows horizontally on small viewports because html/body do not enforce `overflow-x: hidden` in the base styles (only inside `@media (max-width: 900px)`); brief requires it always","Per-frame draw operations exceed budget: brief allows ≤420 (140 updates + 140 current stamps + 140 previous stamps + 1 fade + 1 composite); implementation uses globalAlpha + 6 path commands + 3 property writes per particle, which inflates the per-frame draw call count and likely violates the cost budget","Brief specifies 'Every particle must leave a 6px to 34px trail by stamping points'; implementation draws only a single line per particle with no multi-stamp trail","Status strip is rendered inside the .hero-copy block (and uses `id=\"safety\"`) instead of being placed above the CTA cluster or directly below the paragraph as a sibling; this collapses the layout structure","CTA cluster and footer share z-index logic poorly: footer is z-index:1 while site-shell is z-index:4, so footer sits below the shell's ::before scrim and may be visually subdued beyond spec","Exposure ramp default state is wrong: brief says exposure wash defaults to opacity:0 then ramps to 1; implementation declares `opacity:1` on `.exposure-wash` and animates from 0→1 with `backwards` fill mode, so on first paint the wash is hidden (correct behavior) but the final opacity is back to 1 (correct). However the canvas-inner also animates `opacity` from 0→1 separately, causing double fade not specified in the brief","Pointer parallax uses `event.clientX / width * 2 - 1` but width is 0 at startup before resize runs, so first pointer event after load gets NaN; brief calls for clamping [-1,1] which fails on the first frame","Brief mandates 'exactly one visible h1' and a dedicated `<main>` with `id=\"service\"`; implementation reuses `#service` on the status strip inside main, creating duplicate IDs","Quiet link underline offset is `5px`, correct, but `text-decoration:underline` is applied at all states including hover which loses the offset on hover (the brief requires the offset to persist)","Brand link `href=\"#service\"` is wrong: brief requires it to be a home link, but pointing to #service (same as 'The service') creates a duplicate navigation target. Spec only mandates aria-label 'Nocturne home', not a particular href, so this is a soft miss"],"issues":[{"issue":"Duplicate `id=\"service\"` on <main> and `id=\"safety\"` is placed inside hero-copy on the status strip, breaking the navigation anchors","severity":"medium","evidence":"<main id=\"service\" class=\"hero\" ...><div class=\"hero-copy\">...<div id=\"safety\" class=\"status-strip\" ..."},{"issue":"Particle group split is wrong: amber group only gets indices 56..139 (84 particles) instead of the required 104","severity":"high","evidence":"const group = index < 39 ? \"red\" : index < 56 ? \"blue\" : \"amber\"; produces 39 red + 17 blue + 84 amber"},{"issue":"Sodium hue range should be 18–42deg but brief also restricts brake red to 358–8deg (wrapping); code generates 358–368 instead of 358–8 modulo wrap","severity":"medium","evidence":"group === \"red\" ? 358 + random() * 10 : group === \"blue\" ? 204 + random() * 18 : 18 + random() * 24"},{"issue":"Implementation wraps the canvas in two extra divs (.canvas-inner, .canvas-drift) plus an exposure wash, none of which are specified in the brief","severity":"low","evidence":"<div class=\"canvas-inner\"><div class=\"canvas-drift\"><canvas id=\"traffic-canvas\"></canvas><div class=\"exposure-wash\"></div></div></div>"},{"issue":"Status strip is a child of .hero-copy and uses role=\"status\" which would announce the whole strip to screen readers, while the brief describes a decorative timing line","severity":"low","evidence":"<div id=\"safety\" class=\"status-strip\" role=\"status\">"},{"issue":"Body baseline lacks `overflow-x: hidden` outside the media query; only added via `@media (max-width: 900px)`","severity":"low","evidence":"body { ... } (no overflow-x) then @media (max-width: 900px) html,body,.site-shell { overflow-x: hidden; }"},{"issue":"Footer z-index 1 sits below the page shell (z-index 4) and its scrim, so the footer is drawn under the readability overlay","severity":"low","evidence":".site-footer { ... z-index: 1; }"},{"issue":"Screenshot shows the entire visual weight of the trail field clustered in the top-left quadrant with the lower-right and lower-center nearly empty, contradicting the brief's 'darkest quadrant is lower-left' and 'biased toward y between 4% and 62%'","severity":"high","evidence":"Screenshot at 1024x640: traffic-light trails concentrated upper-left, lower third mostly empty except for a few stray amber/red specks"},{"issue":"Screenshot shows the .vignette darkening the bottom-center heavily while the required readability scrim on .site-shell::before is also darkening that same area, producing a visible dark band that crops through the paragraph and status strip","severity":"medium","evidence":"Visible dark band runs horizontally across the page just above the status strip in the rendered image"},{"issue":"Type ramp at 1024px renders the h1 at roughly 70–80px (between the clamp minimums) and looks noticeably smaller than the brief's intended editorial display weight at this width","severity":"low","evidence":"Screenshot: 'Take the long way home.' occupies roughly 55% of viewport width; the brief expects clamp(54px, 7.2vw, 128px) ≈ 73px at 1024px which is roughly what is rendered, so this is in spec but visually understated for an editorial landing"}],"summary":"Composition, copy, tokens, and z-index stack are mostly correct, but the particle group split (60/27.9/12.1 amber/red/blue instead of 74.3/27.9/12.1) and the unrelated DOM wrappers + duplicate IDs are clear spec violations. The screenshot reveals a visually weak trail field concentrated in the top-left rather than the described long-exposure road surface, with an unintended dark band cutting across the lower copy.","usage":{"input":19218,"output":2090,"total":21308,"reasoning":0,"cached":0},"latency_ms":20696}},{"slug":"interactive-discovery","title":"Interactive Discovery","category":"Hero Section","effect":"A cursor-following spotlight unmasks a warm procedural deep-core plate over a cold sedimentary strata plate","platform":"workbuddy","platform_label":"Workbuddy proxy","model":"hy4-preview-f","model_label":"Hy4 preview (hy4-preview-f)","demo":"/demos/interactive-discovery/index.html","doc":"/prompts/interactive-discovery.md","prompt_text":"DESIGN BRIEF — \"Interactive Discovery\"\n\nA. WHAT IS BEING BUILT\n\nBuild ONE complete, valid, self-contained `index.html`: a single-viewport, dark, cinematic hero\nfor a fictional geology brand called **Lithos**. Document title (exact):\n`Lithos — Layers Hold Tales of Time`. `<html lang=\"en\">`.\n\nThe page is: a near-black sedimentary ground; a PROCEDURAL strata plate built only from CSS\ngradients; a second, warmer PROCEDURAL \"deep core\" plate stacked directly on top of it that is\nrevealed ONLY inside a soft circular spotlight which trails the pointer — that reveal is the\nsignature effect and the whole point of the page; a fixed top bar holding the Lithos mark, the\nwordmark, a centred glass pill of five nav items and a \"Sign Up\" pill; a two-line display\nheadline; one short paragraph bottom-left; one short paragraph plus an amber \"Start Digging\"\nbutton bottom-right; and a slim footer rule.\n\nStructure it with real landmarks: exactly one <header> (containing the <nav>), exactly one <main>\n(the hero and every one of its layers), exactly one <footer>. Exactly one <h1>.\n\nAt viewport widths of 901px and above the page is EXACTLY ONE FRAME: `html, body { height:100%;\noverflow:hidden }` and `.page { height:100dvh; overflow:hidden }`. It does not scroll. At 900px\nand below the page may scroll vertically, and horizontal scrolling is forbidden at every width.\n\nB. WHAT NOT TO BUILD — every one of these is a failure\n\n- No React, no TypeScript, no Vite, no Tailwind, no lucide-react, no JSX, no npm, no build step.\n  The crawled source prompt asked for that stack; this library ships one vanilla file instead.\n  Translate every Tailwind utility into plain CSS and every lucide icon into an inline SVG you\n  draw yourself.\n- **No <canvas> element anywhere in the document.** The source built its spotlight mask with a\n  hidden canvas plus `toDataURL()`. Do not. The mask is a CSS `radial-gradient` applied through\n  `mask-image`, repositioned by writing two CSS custom properties. This is mandatory: the\n  automated verifier measures the first <canvas> it finds and a hidden one fails the build.\n- No external media of any kind: no `<img>`, no remote `url(...)`, no\n  `https://images.higgs.ai`, no .png/.jpg/.webp/.mp4/.webm/.gif, no base64 photo blobs, no\n  <iframe>, no <video>, no <object>. Both \"images\" in this design are procedural CSS gradients.\n- No framework or motion library: no GSAP, no Three.js, no Lottie, no jQuery, no external script.\n- Google Fonts is the ONLY permitted network request. Nothing else may leave the page.\n- No second <h1>. No extra sections, feature cards, pricing table, testimonial, FAQ, form,\n  newsletter input, cookie banner, modal, gallery, carousel or scroll indicator.\n- No `console.log`, `console.warn`, `console.error`, `alert` or `debugger` anywhere.\n- No text-shadow glow on the headline, no neon outline, no drop shadow under the type.\n- Never truncate. Never write \"rest of the code here\". The file must be complete and under 90 KB.\n\nC. THE TWO PROCEDURAL PLATES  (this replaces the source's BG_IMAGE_1 and BG_IMAGE_2)\n\nThe source revealed a second photograph through the mask. Photography is banned, so build the same\nidea out of gradients: two clearly different geology plates occupying the identical box, so the\nspotlight reads as a lens into a second world. Both are `position:absolute; inset:0;` children of\n<main> and are never given an explicit width or height.\n\nC.1 `#layer-base` — the cold surface plate (z-index 1). One element, this exact background stack\n    (topmost first), plus `background-color:#05060a`:\n      radial-gradient(120% 80% at 50% 6%, rgba(232,112,42,0.10) 0%, rgba(232,112,42,0) 62%),\n      radial-gradient(90% 60% at 12% 78%, rgba(126,148,178,0.12) 0%, rgba(126,148,178,0) 66%),\n      radial-gradient(70% 50% at 88% 30%, rgba(120,180,200,0.07) 0%, rgba(120,180,200,0) 70%),\n      repeating-linear-gradient(178deg, rgba(255,255,255,0.030) 0 2px, rgba(255,255,255,0) 2px 9px),\n      repeating-linear-gradient(172deg, rgba(255,255,255,0.018) 0 1px, rgba(255,255,255,0) 1px 27px),\n      repeating-linear-gradient(184deg, rgba(255,255,255,0.012) 0 4px, rgba(255,255,255,0) 4px 61px),\n      linear-gradient(180deg, #0d1014 0%, #090b0f 44%, #05060a 100%);\n    The three repeating gradients are the sedimentary bedding planes; the 178/172/184deg angles keep\n    them from looking printed. It carries the Ken Burns zoom (see G).\n\nC.2 `#layer-reveal` — the warm deep core (z-index 2, `pointer-events:none`). A visibly different\n    plate: molten amber strata threaded with cold mineral veins, i.e. what is under the crust.\n    This exact background stack, plus `background-color:#1a0f07`:\n      radial-gradient(58% 44% at 50% 54%, rgba(255,196,120,0.55) 0%, rgba(232,112,42,0.22) 42%, rgba(232,112,42,0) 74%),\n      radial-gradient(40% 30% at 22% 24%, rgba(255,214,150,0.28) 0%, rgba(255,214,150,0) 70%),\n      repeating-linear-gradient(176deg, rgba(255,168,88,0.30) 0 3px, rgba(255,168,88,0) 3px 14px),\n      repeating-linear-gradient(184deg, rgba(120,220,255,0.16) 0 1px, rgba(120,220,255,0) 1px 34px),\n      linear-gradient(180deg, #1a0f07 0%, #2a1509 46%, #0b0705 100%);\n    Its mask is the signature effect (section D). Nothing else about it moves.\n\nC.3 Overlays, above both plates and below the copy:\n    - `.grain` (z-index 3): one inline SVG, `aria-hidden=\"true\"`, `position:absolute; inset:0;\n      width:100%; height:100%; opacity:0.05; mix-blend-mode:soft-light; pointer-events:none`,\n      containing\n        <filter id=\"lithosGrain\" x=\"0\" y=\"0\" width=\"100%\" height=\"100%\">\n          <feTurbulence type=\"fractalNoise\" baseFrequency=\"0.82\" numOctaves=\"3\" stitchTiles=\"stitch\" seed=\"11\" result=\"n\"/>\n          <feColorMatrix type=\"saturate\" values=\"0\"/>\n        </filter>\n        <rect width=\"100%\" height=\"100%\" filter=\"url(#lithosGrain)\"/>\n      It is STATIC — no animation, no per-frame cost. It is what makes two gradient plates read as\n      photographed rock instead of as CSS.\n    - `.spot-ring` (z-index 4): see D.5.\n    - `.vignette` (z-index 5): `position:absolute; inset:0; pointer-events:none;` with\n      `background: radial-gradient(120% 92% at 50% 44%, rgba(0,0,0,0) 0%, rgba(0,0,0,0.30) 56%, rgba(0,0,0,0.74) 100%)`.\n      This is what guarantees the white copy always wins, however bright the deep core is.\n    - <main> itself is `isolation:isolate` so the grain's blend mode cannot reach outside the hero.\n\nD. THE SPOTLIGHT — the signature effect, implement exactly this\n\nD.1 Constants and state\n    - `const SPOTLIGHT_R = 260;` (CSS pixels — the spotlight radius).\n    - `const LERP = 0.10;`\n    - Raw pointer `mouse = { x: innerWidth / 2, y: innerHeight / 2 }` and eased pointer\n      `smooth = { x: mouse.x, y: mouse.y }`.\n      NOTE: the source initialised the pointer off-screen at `{-999,-999}` so nothing was revealed\n      until the mouse moved. Here the spotlight starts PARKED AT THE VIEWPORT CENTRE instead, so\n      the page reads correctly on first paint, in a screenshot, and on touch devices with no\n      pointer at all. This is deliberate — keep it.\n    - One `mousemove` listener on `window` with `{ passive: true }` that ONLY stores\n      `mouse.x = e.clientX; mouse.y = e.clientY;`. It must never touch the DOM, never read layout,\n      never write a style.\n    - The lerp runs in the single rAF loop (D.3): `smooth.x += (mouse.x - smooth.x) * LERP;` and the\n      same for y. That 0.10 easing is the trailing weight of the light — it must be visible, so do\n      not raise it above 0.12 and do not snap.\n\nD.2 The mask — pure CSS, both prefixes, on `#layer-reveal`\n      -webkit-mask-image: radial-gradient(circle 260px at var(--spot-x, 50%) var(--spot-y, 50%),\n          rgba(255,255,255,1) 0%, rgba(255,255,255,1) 40%, rgba(255,255,255,0.75) 60%,\n          rgba(255,255,255,0.40) 75%, rgba(255,255,255,0.12) 88%, rgba(255,255,255,0) 100%);\n      mask-image: radial-gradient(circle 260px at var(--spot-x, 50%) var(--spot-y, 50%),\n          rgba(255,255,255,1) 0%, rgba(255,255,255,1) 40%, rgba(255,255,255,0.75) 60%,\n          rgba(255,255,255,0.40) 75%, rgba(255,255,255,0.12) 88%, rgba(255,255,255,0) 100%);\n      -webkit-mask-repeat: no-repeat;  mask-repeat: no-repeat;\n      -webkit-mask-size: 100% 100%;    mask-size: 100% 100%;\n    These are the source's exact falloff stops (1, 1, 0.75, 0.40, 0.12, 0 at 0 / 40 / 60 / 75 / 88 /\n    100%). The revealed disc is solid through its first 40% and dissolves softly to nothing at\n    260px, which is the \"soft circular mask\" the design is named for. Because the gradient's last\n    stop is fully transparent, EVERY pixel more than 260px from the spotlight centre has mask alpha\n    0: the reveal plate is completely invisible there and only the cold base plate shows.\n\nD.3 The single loop\n    One `requestAnimationFrame` loop drives the entire page. Every tick, in this order:\n      1. `smooth.x += (mouse.x - smooth.x) * LERP;`  (then y)\n      2. write the spotlight position (D.4)\n      3. `window.__DEMO__.frames += 1;`\n      4. `requestAnimationFrame(frame);`\n    The loop keeps running when the pointer is still — that is what lets the light settle with\n    weight instead of freezing, and what keeps the telemetry honest. Never start a second loop.\n\nD.4 Writing the position — cheap, and never layout\n    Write exactly two custom properties on `#layer-reveal` itself:\n      reveal.style.setProperty('--spot-x', x.toFixed(1) + 'px');\n      reveal.style.setProperty('--spot-y', y.toFixed(1) + 'px');\n    Skip the write entirely when both values changed by less than 0.1px since the previous frame\n    (cache `lastX` / `lastY`) — that keeps a still page at zero paint cost. Never write `left`,\n    `top`, `width`, `height`, `margin` or `background-position`; never call\n    `getBoundingClientRect` inside the loop. `transform` and custom properties only.\n\nD.5 The rim — `.spot-ring`\n    One 520px x 520px (2 x SPOTLIGHT_R) absolutely positioned div at `top:0; left:0`,\n    `border-radius:50%`, `pointer-events:none`, z-index 4, with\n      background: radial-gradient(circle, rgba(255,168,88,0) 62%, rgba(255,168,88,0.12) 80%, rgba(255,168,88,0) 92%);\n    Its default CSS transform is `translate3d(calc(50vw - 260px), calc(50vh - 260px), 0)` so it is\n    already centred before the script runs, and the loop then overwrites it with\n    `transform: translate3d(Xpx, Ypx, 0)` where X = smooth.x - SPOTLIGHT_R and Y = smooth.y - SPOTLIGHT_R.\n    Transform only. It lives inside <main>, which is `overflow:hidden`, so its 520px box can never\n    create a scrollbar at 390px.\n\nD.6 Coarse pointers and resize\n    - Wrap the `mousemove` listener in a `window.matchMedia('(pointer: fine)').matches` guard. On a\n      touch device nothing is attached and the spotlight simply stays parked at the centre — the\n      page must look deliberate, not broken.\n    - One `resize` listener clamps `mouse` and `smooth` into the new `innerWidth` / `innerHeight`.\n      No ResizeObserver, no scroll listener, no IntersectionObserver.\n\nE. THE COMPOSITION — copy is fixed, use these exact strings\n\nE.1 <header class=\"site-header\">\n    `position:fixed; top:0; left:0; right:0; z-index:100; display:flex; align-items:center;\n    justify-content:space-between; padding:18px 24px;` and at >=768px `padding:22px 40px`.\n    - Left — `<a class=\"brand\" href=\"#top\">`: `display:inline-flex; align-items:center; gap:12px;\n      color:#fff; text-decoration:none;` containing\n        · an inline `<svg width=\"26\" height=\"26\" viewBox=\"0 0 256 256\" aria-hidden=\"true\"\n          focusable=\"false\">` with exactly one path:\n          `<path fill=\"#ffffff\" d=\"M 256 256 L 128 256 L 0 128 L 128 128 Z M 256 128 L 128 128 L 0 0 L 128 0 Z\"/>`\n          (the source's two-diamond mark, kept verbatim);\n        · `<span class=\"brand-word\">Lithos</span>` — 24px, weight 400, italic, colour #ffffff,\n          `font-family:var(--font-display)`.\n    - Centre — `<nav aria-label=\"Primary\">`, `position:absolute; left:50%; transform:translateX(-50%);`\n      `display:none` below 768px, otherwise `display:flex; align-items:center; gap:2px; padding:6px;`\n      with `background:rgba(255,255,255,0.16); backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);\n      border:1px solid rgba(255,255,255,0.28); border-radius:999px;`.\n      Exactly five `<button type=\"button\">` children, in this order and with these exact labels:\n        **Course** (the active one — `background:rgba(255,255,255,0.92); color:#0a0b0d;` and\n        `aria-current=\"page\"`), then **Field Guides**, **Geology**, **Plans**, **Live Tour**\n        (`color:rgba(255,255,255,0.80)`; hover `background:rgba(255,255,255,0.20); color:#ffffff`).\n      Every one of the five: `padding:7px 16px; border:0; border-radius:999px; font-size:13.5px;\n      font-weight:500; cursor:pointer; white-space:nowrap; font-family:inherit;\n      transition:background .25s ease, color .25s ease`.\n    - Right — `<a class=\"btn-signup\" href=\"#signup\">Sign Up</a>`: `background:#ffffff; color:#111318;\n      border-radius:999px; padding:10px 24px; font-size:13.5px; font-weight:600; text-decoration:none;\n      white-space:nowrap;`, hover `background:#e9ecef`. `display:none` below 768px.\n    - Below 768px a `<button class=\"burger\" type=\"button\" aria-label=\"Open menu\"\n      aria-expanded=\"false\" aria-controls=\"mobile-menu\">` is shown in the right slot (40x40, 1px\n      `rgba(255,255,255,0.28)` border, radius 10px, transparent background) holding three 1px x 16px\n      white <span> bars. This is the source's lucide hamburger, redrawn by hand.\n      `#mobile-menu` is a `<div class=\"mobile-menu\" id=\"mobile-menu\" hidden>` at\n      `position:fixed; inset:0; z-index:90; background:rgba(5,6,10,0.84);\n      backdrop-filter:blur(18px); -webkit-backdrop-filter:blur(18px); display:flex;\n      flex-direction:column; align-items:center; justify-content:center; gap:18px;`\n      containing the same five labels as `<a class=\"menu-link\" href=\"#course\">`-style links (22px,\n      white) plus `<a class=\"menu-signup\" href=\"#signup\">Sign Up</a>` (solid white pill, dark text).\n      `.mobile-menu[hidden] { display:none }` — without that rule the `hidden` attribute loses to\n      the `display:flex` above. Opening sets `aria-expanded=\"true\"`; the menu closes on a click on\n      any link, on Escape, and on a resize to >=768px.\n\nE.2 <main class=\"hero\">\n    `position:absolute; inset:0; overflow:hidden; isolation:isolate; background:#05060a;` — the\n    layer stack from C, the rim from D.5, and the copy below all live here. The base plate bleeds\n    behind the fixed header, exactly as the source's background image did.\n\nE.3 The copy — inside `<div class=\"hero-copy\">` at `position:absolute; inset:0; z-index:10;\n    pointer-events:none;` (its links re-enable `pointer-events:auto` so the masked plates never\n    swallow a click):\n    - `<h1 class=\"hero-title\">` — `position:absolute; top:14%; left:0; right:0; margin:0;\n      padding:0 20px; text-align:center; color:#ffffff; line-height:0.95; font-weight:400;`\n      containing EXACTLY two `display:block` spans and nothing else:\n        · `<span class=\"line-1\">Layers hold</span>` — `font-family:var(--font-display);\n          font-style:italic; font-weight:400; font-size:var(--h1); letter-spacing:-0.05em;`\n        · `<span class=\"line-2\">tales of time</span>` — `font-family:var(--font-sans);\n          font-weight:400; font-size:var(--h1); letter-spacing:-0.08em; margin-top:-4px;`\n      The two lines are the whole headline. Nothing else may be inside the <h1>.\n    - `<p class=\"lede-left\">` — `position:absolute; bottom:104px; left:40px; max-width:260px;\n      margin:0; font-size:14px; line-height:1.65; color:rgba(255,255,255,0.80);`, exact text:\n      \"Every layer of sediment records a chapter of our planet, from ancient seabeds to drifting\n      ash, layered across millions of years beneath us.\" It is `display:none` below 640px.\n    - `<div class=\"cta-block\">` — `position:absolute; bottom:96px; right:40px; max-width:260px;\n      display:flex; flex-direction:column; align-items:flex-start; gap:20px;` containing\n        · `<p class=\"lede-right\">` — `margin:0; font-size:13.5px; line-height:1.65;\n          color:rgba(255,255,255,0.80);`, exact text: \"Our interactive maps let you peel back the\n          crust to trace how stones, fossils, and deep time combine to shape the ground beneath\n          your feet.\"\n        · `<a class=\"btn-dig\" href=\"#dig\">Start Digging</a>` — `background:#e8702a; color:#ffffff;\n          font-size:13.5px; font-weight:500; padding:12px 28px; border-radius:999px;\n          text-decoration:none; white-space:nowrap; font-family:inherit;\n          transition:background .25s ease, transform .25s ease, box-shadow .25s ease;`.\n          Hover: `background:#d2611f; transform:scale(1.03); box-shadow:0 10px 30px rgba(232,112,42,0.30)`.\n          Active: `transform:scale(0.95)`.\n\nE.4 <footer class=\"site-footer\">\n    `position:absolute; left:0; right:0; bottom:0; z-index:100; display:flex; align-items:center;\n    justify-content:space-between; gap:16px; padding:14px 24px;\n    border-top:1px solid rgba(255,255,255,0.10); color:rgba(255,255,255,0.55);\n    font-size:10.5px; letter-spacing:0.14em; text-transform:uppercase;` with two non-interactive\n    spans: left \"Lithos Field Institute\", right \"Deep Time Atlas / 2026\". The source had no footer;\n    the house contract requires the landmark, so it is one hairline row that never collides with\n    the bottom-left paragraph (bottom:104px) or the CTA block (bottom:96px).\n\nF. THE SYSTEM\n\nF.1 Token table — declare every one of these on `:root` and use them rather than untracked values:\n      --bg:#05060a;          --ink:#ffffff;        --muted:rgba(255,255,255,0.80);\n      --muted-dim:rgba(255,255,255,0.55);\n      --accent:#e8702a;      --accent-hi:#d2611f;  --line:rgba(255,255,255,0.10);\n      --line-strong:rgba(255,255,255,0.28);        --glass:rgba(255,255,255,0.16);\n      --spot-r:260px;\n      --font-sans:'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n      --font-display:'Playfair Display', 'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, 'Times New Roman', serif;\n      --h1:clamp(38px, 6.4vw, 92px);\n      --ease-out:cubic-bezier(0.16, 1, 0.3, 1);\n      --duration-reveal:1100ms;  --duration-fade:1000ms;  --duration-zoom:1800ms;\n\nF.2 Reset — `*, *::before, *::after { box-sizing:border-box }`; `html, body { margin:0; padding:0 }`;\n    `body { background:#05060a; color:var(--ink); font-family:var(--font-sans);\n    -webkit-font-smoothing:antialiased; text-rendering:optimizeLegibility; min-width:320px }`.\n\nF.3 Fonts — exactly two preconnects and one stylesheet, nothing else:\n      <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n      <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n      <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Playfair+Display:ital,wght@1,400;1,500&display=swap\">\n    If that request fails, the system stacks above must still look intentional.\n\nF.4 Focus — `:focus-visible { outline:2px solid #ffffff; outline-offset:3px; border-radius:inherit }`.\n    Never remove an outline without replacing it. The contrast of every visible string must clear\n    4.5:1 against what is behind it.\n\nG. MOTION — the source's entrance system, with the house anti-blank guard\n\nG.1 Keyframes (these exact names and values):\n      @keyframes heroReveal { 0% { opacity:0; transform:translateY(28px); filter:blur(12px) }\n                              100% { opacity:1; transform:translateY(0); filter:blur(0) } }\n      @keyframes heroFadeUp { 0% { opacity:0; transform:translateY(20px) }\n                              100% { opacity:1; transform:translateY(0) } }\n      @keyframes heroZoom   { 0% { transform:scale(1.12) } 100% { transform:scale(1) } }\n\nG.2 Classes:\n      .hero-anim  { opacity:1; animation-duration:1000ms; animation-fill-mode:both;\n                    animation-timing-function:cubic-bezier(0.16, 1, 0.3, 1); }\n      .hero-reveal{ animation-name:heroReveal; animation-duration:1100ms; }\n      .hero-fade  { animation-name:heroFadeUp; animation-duration:1000ms; }\n      .hero-zoom  { animation:heroZoom 1800ms cubic-bezier(0.16, 1, 0.3, 1) both; }\n      .is-in      { animation:none !important; opacity:1 !important; transform:none !important;\n                    filter:none !important; }\n    The resting state is opacity 1 and the animation only ever animates FROM 0 — the source wrote\n    `.hero-anim { opacity:0 }`, which blanks the page if an animation never runs. That is the one\n    source rule you must NOT copy. Use `animation-fill-mode:both` so a delayed element is hidden by\n    its own 0% keyframe during the delay, not by its base style.\n\nG.3 Stagger table (element -> class -> animation-delay):\n      .site-header      hero-anim hero-fade    100ms\n      .brand            hero-anim hero-fade    160ms\n      .hero-title .line-1  hero-anim hero-reveal  250ms\n      .hero-title .line-2  hero-anim hero-reveal  420ms\n      .lede-left        hero-anim hero-fade    700ms\n      .cta-block        hero-anim hero-fade    850ms\n      .site-footer      hero-anim hero-fade   1000ms\n      #layer-base       hero-zoom              (no delay — the Ken Burns zoom-out, scale 1.12 -> 1)\n\nG.4 Settlement — for every element carrying .hero-anim or .hero-zoom, add a listener for its own\n    `animationend` that adds `.is-in`; then, after two nested requestAnimationFrames, add `.is-in`\n    to any of them whose `getAnimations()` (guarded with `typeof el.getAnimations === 'function'`)\n    reports nothing running or finished. That is the \"page is never blank\" guard: if animations are\n    unsupported or suppressed, every element is already in its final, visible state.\n\nG.5 `@media (prefers-reduced-motion: reduce)` must contain:\n      *, *::before, *::after { animation:none !important; transition:none !important; }\n      .hero-anim, .hero-zoom, #layer-base { opacity:1 !important; transform:none !important;\n                                            filter:none !important; }\n    and the script must then attach no pointer listener and park the spotlight at the viewport\n    centre (see I). The page stays completely readable and completely static — no zoom, no blur\n    rise, no trailing light — while the telemetry loop keeps counting frames with zero visual\n    change.\n\nH. RESPONSIVE TABLE — every step is a token override\n\n  >=1600px           --h1:96px;  header padding 26px 56px;  .lede-left/.cta-block offset 56px;\n                     .lede-left max-width 280px; .cta-block max-width 280px\n  1280px – 1599px    --h1:80px\n  1024px – 1279px    --h1:68px;  header padding 20px 32px;  .lede-left/.cta-block offset 32px\n  901px – 1023px     --h1:56px;  nav pill buttons 12.5px with padding 6px 12px\n\n  <=900px  (phone + small tablet — vertical scrolling IS allowed here)\n    html, body { height:auto; overflow-x:hidden }\n    .page { height:auto; min-height:100dvh; overflow:visible }\n    .hero { position:relative; inset:auto; min-height:100dvh; overflow:hidden;\n            display:flex; flex-direction:column; justify-content:flex-end;\n            padding:104px 24px 88px; }\n    .hero-copy { position:static; inset:auto; pointer-events:auto;\n                 display:flex; flex-direction:column; gap:22px; }\n    .hero-title, .lede-left, .cta-block { position:static; inset:auto; text-align:left;\n                                          max-width:420px; }\n    .hero-title { padding:0; margin:0 0 4px }\n    .lede-left { display:block }\n    .site-footer { position:static }\n    The plate layers, the grain, the rim and the vignette stay `position:absolute; inset:0` inside\n    the hero, so they still cover the whole hero however tall it becomes. Nothing may extend past\n    100vw: keep every padding inside the box and every max-width below the viewport.\n\n  <=560px\n    --h1:38px;  .brand-word 20px;  .lede-left font-size 13px;  .lede-right font-size 13px;\n    .btn-dig padding 11px 22px;  .site-footer font-size 9.5px;  .hero padding 96px 20px 76px\n\n  Below 768px: the centre nav pill and the desktop \"Sign Up\" pill are `display:none` and the\n  burger plus #mobile-menu take over. Below 640px: .lede-left is `display:none` and the CTA block\n  becomes the single bottom-anchored block.\n\nI. THE JAVASCRIPT — use this structure, preserving every named constant, guard and telemetry field\n\n  (() => {\n    'use strict';\n    window.__DEMO__ = {\n      name: 'interactive-discovery', ready: false, frames: 0,\n      effects: ['strata-layers', 'spotlight-mask', 'cursor-lerp', 'spot-ring',\n                'entrance-stagger', 'ken-burns', 'grain'],\n      errors: []\n    };\n    const D = window.__DEMO__;\n    window.addEventListener('error', (e) => D.errors.push(String((e && e.message) || 'error')));\n\n    const SPOTLIGHT_R = 260;\n    const LERP = 0.10;\n    const reveal = document.getElementById('layer-reveal');\n    const ring = document.querySelector('.spot-ring');\n    const reduce = !!(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches);\n    const fine = !!(window.matchMedia && window.matchMedia('(pointer: fine)').matches);\n\n    let vw = window.innerWidth, vh = window.innerHeight;\n    const mouse = { x: vw / 2, y: vh / 2 };\n    const smooth = { x: vw / 2, y: vh / 2 };\n    let lastX = -1, lastY = -1;\n\n    // entrance settlement + never-blank fallback (G.4)\n    const animated = Array.prototype.slice.call(document.querySelectorAll('.hero-anim, .hero-zoom'));\n    animated.forEach((el) => el.addEventListener('animationend', () => el.classList.add('is-in')));\n    requestAnimationFrame(() => requestAnimationFrame(() => {\n      animated.forEach((el) => {\n        const running = typeof el.getAnimations === 'function'\n          ? el.getAnimations().some((a) => a.playState === 'running' || a.playState === 'finished')\n          : false;\n        if (!running) el.classList.add('is-in');\n      });\n      D.ready = true;\n    }));\n\n    // mask capability guard — never throw, never go blank\n    const maskOk = !!(window.CSS && window.CSS.supports &&\n      (window.CSS.supports('mask-image', 'radial-gradient(#000,#000)') ||\n       window.CSS.supports('-webkit-mask-image', 'radial-gradient(#000,#000)')));\n    if (!maskOk && reveal) { reveal.classList.add('no-mask'); D.effects.push('mask-fallback'); }\n\n    function write(x, y) {\n      if (Math.abs(x - lastX) < 0.1 && Math.abs(y - lastY) < 0.1) return;\n      lastX = x; lastY = y;\n      if (reveal) {\n        reveal.style.setProperty('--spot-x', x.toFixed(1) + 'px');\n        reveal.style.setProperty('--spot-y', y.toFixed(1) + 'px');\n      }\n      if (ring) {\n        ring.style.transform = 'translate3d(' + (x - SPOTLIGHT_R).toFixed(1) + 'px,' +\n                               (y - SPOTLIGHT_R).toFixed(1) + 'px,0)';\n      }\n    }\n    write(smooth.x, smooth.y);            // first paint already shows the centre spotlight\n\n    if (fine && !reduce) {\n      window.addEventListener('mousemove', (e) => {\n        mouse.x = e.clientX; mouse.y = e.clientY;\n      }, { passive: true });\n      window.addEventListener('resize', () => {\n        vw = window.innerWidth; vh = window.innerHeight;\n        mouse.x = Math.min(Math.max(mouse.x, 0), vw);\n        mouse.y = Math.min(Math.max(mouse.y, 0), vh);\n        smooth.x = Math.min(Math.max(smooth.x, 0), vw);\n        smooth.y = Math.min(Math.max(smooth.y, 0), vh);\n      });\n    }\n\n    function frame() {\n      requestAnimationFrame(frame);\n      D.frames += 1;\n      if (reduce) return;                 // static page, telemetry still counting\n      smooth.x += (mouse.x - smooth.x) * LERP;\n      smooth.y += (mouse.y - smooth.y) * LERP;\n      write(smooth.x, smooth.y);\n    }\n    requestAnimationFrame(frame);\n  })();\n\n  No other JavaScript API. No console output. No unhandled promise. No network request other than\n  Google Fonts. Target file size: 18-32 KB, hard ceiling 90 KB.\n\nJ. FAILURE GUARDS, collected\n\n  J.1 If CSS animations never run, the entrance elements are already opacity 1 (G.2) and G.4 adds\n      .is-in to them — the page is never blank and never unreadable.\n  J.2 If `mask-image` is unsupported, `#layer-reveal.no-mask` falls back to\n      `opacity:0.45; -webkit-mask-image:none; mask-image:none;` so the deep core still shows as a\n      soft full-frame wash. Push 'mask-fallback' into effects and push nothing into errors.\n  J.3 If `window.matchMedia` is missing, `reduce` and `fine` are false and the page simply runs\n      with motion on. Nothing throws.\n  J.4 If the reveal element is missing, `write()` skips it silently; the loop keeps counting frames.\n  J.5 Under prefers-reduced-motion the loop still runs so `frames` advances, but it writes nothing:\n      the spotlight is parked at the viewport centre, the page is static, readable and error-free.\n  J.6 The whole script is one IIFE in a single <script> at the end of <body>. It must not run\n      before the DOM it queries exists.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the reveal layer carries a non-none mask-image (or -webkit-mask-image) built from a radial-gradient with the 1/1/0.75/0.40/0.12/0 stops, and the document contains no <canvas> element and no external image request","the spotlight centre tracks the pointer: --spot-x and --spot-y are written on the reveal layer, and after the pointer moves to a new coordinate both values change and the mask centre follows them","outside the 260px spotlight radius the reveal layer is fully transparent, so the cold strata plate is the only layer visible away from the cursor","the spotlight is parked at the viewport centre before any pointer input, and with prefers-reduced-motion: reduce the page is static, fully readable and reports zero errors","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"],"usage":{"build_tokens":39026,"build_prompt_tokens":9263,"build_completion_tokens":29763,"build_reasoning_tokens":23548,"build_calls":1,"build_attempts":1,"build_latency_ms":284668,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":0,"total_tokens":39026},"verification":{"passed":true,"passed_checks":18,"total_checks":18,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"18640 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"9 frames, effects: strata-layers, spotlight-mask, cursor-lerp, spot-ring, entrance-stagger, ken-burns, grain"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"24 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Layers hold\ntales of time\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Lithos — Layers Hold Tales of Time"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=427 pageErrors=0"}]},"crosscheck":null},{"slug":"lattice","title":"Lattice","category":"Landing Page","effect":"A slowly rotating gyroid wireframe lattice projected to depth-faded hairlines over a thin horizon glow","platform":"cmdcode","platform_label":"cmd-code","model":"xiaomi/mimo-v2.6-pro","model_label":"Xiaomi MiMo v2.6 Pro (xiaomi/mimo-v2.6-pro)","demo":"/demos/lattice/index.html","doc":"/prompts/lattice.md","prompt_text":"Build one complete, self-contained `index.html` for **Lattice** — a materials-science company that architects gyroid-phase ceramics. Document title: `Lattice — Matter, Architected`. `<html lang=\"en\">`. ONE file only: plain HTML + CSS + JS in `<style>` and one `<script>` at end of body. No build step, no framework, no bundler, no React, Tailwind, GSAP, Three.js, Lottie, jQuery, or external scripts. NO EXTERNAL MEDIA: no mp4/webm/gif, no remote images, no base64 blobs, no third-party CDN URLs. Google Fonts is the ONLY permitted network request (Inter + IBM Plex Mono) with system fallback stacks. Every visible pixel comes from CSS gradients, inline SVG, text, or canvas.\n\n**Viewport rule:** at widths `>= 901px` the page is one frame: `html, body { height:100%; overflow:hidden }` and `.page { height:100dvh; overflow:hidden }`. NO SCROLL on desktop. At widths `<= 900px` the page may scroll vertically. No horizontal overflow at 390x844, 768x1024, 1440x900, or 1920x1080, ever.\n\n**Do NOT build:** no WebGL, no video, no audio, no images, no forms, no newsletter capture, no pricing table, no testimonials, no logos wall, no case-study carousel, no blog feed, no dashboard, no cookie banner, no scroll indicator, no hamburger on desktop (burger only at `<=900px`), no sections beyond the specified header / hero / footer, no footer links beyond the three specified, no second `<h1>`, no `console.log` in the shipped file.\n\n---\n\n## A. WHAT IS BEING BUILT\n\nA single-viewport landing page for **Lattice**, a materials-science company. A full-bleed `<canvas>` renders a slowly rotating 3D wireframe lattice — a cubic node grid whose vertices are Newton-projected onto the gyroid isosurface `F = 0`, so the wireframe reads as an architected minimal surface, not a plain cube. A pinhole camera projects it to 2D; hairline edges are depth-bucketed and alpha-faded so far geometry dissolves and near geometry stays crisp. Behind it a thin horizon glow: a soft band plus a 1px core line dying at both ends. Everything is monochrome — one cold near-white on near-black — precise and technical. Over the field: a compact left-aligned type block (eyebrow, one H1 of two masked lines, one lead sentence, one mono spec readout, two buttons) and a hairline footer with batch status. Composition is asymmetric: type in the left 44%, lattice right-of-centre, never covered by text on desktop.\n\n---\n\n## B. THE BACKDROP — procedural gyroid lattice on canvas (the ONLY visual effect besides the grain)\n\nA single `<canvas id=\"lattice\">` fills the viewport: `position:fixed; inset:0; width:100%; height:100%; z-index:0; display:block; pointer-events:none`. Repainted every animation frame; NEVER a static image.\n\n**Geometry, built ONCE at startup:**\n\n- Node grid: `i, j, k ∈ 0..8` at `(x,y,z) = ((i-4)*0.62, (j-4)*0.62, (k-4)*0.62)`. Spacing 0.62, cube half-extent 2.48, exactly 729 nodes.\n- Gyroid field with `a = 0.9`: `F(p) = sin(a x)cos(a y) + sin(a y)cos(a z) + sin(a z)cos(a x)`.\n- Analytic gradient: `dF/dx = a cos(a x)cos(a y) - a sin(a z)sin(a x)`; `dF/dy = -a sin(a x)sin(a y) + a cos(a y)cos(a z)`; `dF/dz = -a sin(a y)sin(a z) + a cos(a z)cos(a x)`.\n- Newton projection onto `F = 0`: `p' = p - 0.9 * F(p) * grad / |grad|^2`, once per node; skip when `|grad|^2 < 0.05`; clamp displacement length to 0.34. This bends the cubic grid into a gyroid-like saddle mesh.\n- Edges: `+x`, `+y`, `+z` neighbours where they exist: `3 * 8 * 9 * 9 = 1944` edges. Nodes in `Float32Array(2187)`, edge pairs in `Uint16Array(3888)`. Nothing is allocated during animation.\n\n**Camera and projection (per frame):**\n\n- Rotation per node, in order: yaw about Y `theta = t * (2*PI/48)` (48s per revolution), pitch about X `phi = 0.42 + 0.10 * sin(t * 2*PI/74)`, roll about Z `psi = 0.06 * sin(t * 2*PI/90)`; `t` = seconds since start.\n- Pinhole projection: camera at `z = CAM_Z = 9.0` on the origin; `depth = 9.0 - zRot`; `sx = cx + xRot * f / depth`, `sy = cy - yRot * f / depth`. Focal `f = min(h, w * 1.35) * zoom` with `zoom = 1.0`, or `0.88` at desktop max-height 850px, `0.78` at max-height 720px. Centre: desktop `cx = w * 0.635, cy = h * 0.54`; `<=900px` `cx = w * 0.5, cy = h * 0.42`. At 1440x900 with `f = h` the structure spans `5.56 * 900 / 9 = 556px` = 62% of viewport height, the designed size.\n\n**Depth sorting and fade (the core look):**\n\n- Per node store `sx`, `sy`, `depth`, validity in preallocated `Float32Array(729)` x3 + `Uint8Array(729)`; invalid when `depth < 5.8` or `depth > 12.4` (near cull + fog cutoff).\n- Per edge mean depth `dm = (d0 + d1) / 2`; drop the edge if either endpoint is invalid. Bucket `b = floor((dm - 5.8) / (12.4 - 5.8) * 12)` clamped `0..11` (b0 = nearest); push the edge index into one of 12 preallocated `Uint16Array(1944)` buffers with an integer count each — zero allocation per frame.\n- Paint buckets 11 (far) → 0 (near): one `beginPath()`, the bucket's `moveTo/lineTo` pairs, one `stroke()`. Exactly 12 stroke calls per frame.\n- Stroke: `lineWidth = 1`, `lineCap = 'butt'`, colour `rgba(223, 233, 243, alpha)` where `alpha[b] = 0.03 + 0.29 * pow(1 - b / 11, 1.25)` (near ≈ 0.32, far ≈ 0.03). Cache the 12 `strokeStyle` strings at startup.\n\n**Horizon glow (2 fill primitives, gradients cached on resize):**\n\n- Band: `fillRect(0, cy - h*0.13, w, h*0.26)` with vertical gradient stops `rgba(223,233,243,0) 0%`, `rgba(223,233,243,0.11) 50%`, `rgba(223,233,243,0) 100%`.\n- Core line: `fillRect(cx - w*0.36, cy - 0.5, w*0.72, 1)` with a horizontal linear gradient stopped `rgba(223,233,243,0) 0%`, `rgba(223,233,243,0.26) 50%`, `rgba(223,233,243,0) 100%`.\n- Both draw BEFORE the wireframe and are built once in `resizeCanvas()` — `createLinearGradient` is never called inside the loop.\n\n**Vignette (1 fill primitive, gradient cached on resize):** full-canvas radial gradient centred `(w/2, h/2)`, radius `max(w,h) * 0.78`, stops `rgba(3,5,8,0) 0%`, `rgba(3,5,8,0.18) 55%`, `rgba(3,5,8,0.62) 100%`, drawn last.\n\n**Per-frame cost budget:** 729 node rotations (~18 flops each) + 729 perspective divides + up to 1944 moveTo/lineTo pairs + 12 strokes + 3 gradient fills = **15 draw calls, ≈ 45k flops per frame**, far under the 20-frames-per-1.2s bar. NEVER call `getBoundingClientRect` in the loop; cache `document.hidden`, canvas and context handles outside it. NEVER set `ctx.filter` or `ctx.shadowBlur`. The glow's softness comes from gradient stops alone.\n\n**Resolution trick:** `canvas.width = cssW * dpr`, `canvas.height = cssH * dpr`, then `ctx.setTransform(dpr,0,0,dpr,0,0)`, with `dpr = Math.min(devicePixelRatio, 2)` at `>=901px`, `Math.min(devicePixelRatio, 1.5)` at `<=900px`. Resize is scheduled by one boolean + rAF flag, never a timer.\n\n**Grain (CSS element):** `.grain` fixed, `inset:-50%; width:200%; height:200%; opacity:0.04`, data-URI inline-SVG `feTurbulence` (`type=\"fractalNoise\" baseFrequency=\"0.85\" numOctaves=\"2\" stitchTiles=\"stitch\"` + `feColorMatrix saturate 0`), tile 180px, `animation: grain 0.95s steps(4) infinite` among offsets `(0,0) (-2%,1%) (1%,-2%) (-1%,2%)`.\n\n---\n\n## C. LAYER STACK (back → front)\n\n1. `html, body` — `#05070a`, with a base gradient `linear-gradient(180deg, #070a0e 0%, #05070a 46%, #030508 100%)` forced twice (see Failure Guards).\n2. `canvas#lattice` — z-index 0: horizon glow, 12 depth-sorted wire batches, vignette.\n3. `.scrim` — z-index 1, fixed: desktop `linear-gradient(90deg, rgba(5,7,10,0.88) 0%, rgba(5,7,10,0.66) 32%, rgba(5,7,10,0.10) 60%, rgba(5,7,10,0.34) 100%)` (text-protection left, structure breathing right); at `<=900px` `linear-gradient(180deg, rgba(5,7,10,0.55) 0%, rgba(5,7,10,0.20) 40%, rgba(5,7,10,0.80) 100%)`.\n4. `.page` — z-index 2, `position:relative; display:grid; grid-template-rows:auto 1fr auto; min-height:100dvh`.\n5. `.grain` — z-index 5, pointer-events none.\n6. `.menu` — z-index 80 when open (`<=900px` only).\nFocus rings stay above local stacking contexts.\n\n---\n\n## D. TOKEN TABLE (defaults at ~1440px)\n\n```\n--bg:            #05070a\n--bg-deep:       #030508\n--bg-top:        #070a0e\n--text:          #eef3f8\n--muted:         #97a4b0\n--dim:           #616e7a\n--accent:        #dfe9f3\n--accent-dim:    rgba(223,233,243,0.55)\n--accent-line:   rgba(223,233,243,0.30)\n--accent-fill:   rgba(223,233,243,0.06)\n--wire:          rgba(223,233,243,1)\n--border:        rgba(255,255,255,0.14)\n--border-soft:   rgba(255,255,255,0.08)\n--logo:          15px\n--logo-mark:     22px\n--nav:           13px\n--nav-h:         38px\n--eyebrow:       11.5px\n--h1:            62px\n--lead:          16px\n--spec:          12.5px\n--btn:           13px\n--btn-h:         42px\n--foot:          12px\n--header-y:      26px\n--header-x:      44px\n--hero-x:        44px\n--hero-y:        40px\n--hero-max:      640px\n--hero-gap:      24px\n--copy-gap-h1:   22px\n--copy-gap-lead: 24px\n--radius-btn:    4px\n--focus-w:       2px\n--focus-off:     3px\n--mono:  \"IBM Plex Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace\n--sans:  \"Inter\", ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif\n--ease-out:      cubic-bezier(0.16,1,0.3,1)\n--enter-dur:     0.95s\n--grain-dur:     0.95s\n```\n\nCanvas constants mirror the tokens in JS: `NODES_N 9`, `SPACING 0.62`, `GYROID_A 0.9`, `NEWTON 0.9`, `DISPLACE_MAX 0.34`, `CAM_Z 9.0`, `DEPTH_NEAR 5.8`, `DEPTH_FAR 12.4`, `BUCKETS 12`, `YAW_PERIOD 48`, `PITCH_PERIOD 74`, `ROLL_PERIOD 90`, `DPR_MAX 2`, `DPR_MAX_MOBILE 1.5`.\n\nFonts (Google Fonts ONLY): `Inter:wght@400;500;600` and `IBM+Plex+Mono:wght@400;500`, one `<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>` plus one stylesheet link with `display=swap`.\n\n---\n\n## E. THE COMPOSITION — exact build order\n\n**.page** is the grid container with three rows: `<header>`, `<main>`, `<footer>`.\n\n**HEADER** — `padding: var(--header-y) var(--header-x); display:flex; align-items:center; justify-content:space-between; gap:24px`.\n\n- Left: `a.brand` (href `#`, accessible name \"Lattice home\"): inline SVG mark 22x22 — a wireframe unit cell: `<path d=\"M11 2 L20 7 L20 16 L11 21 L2 16 L2 7 Z M11 2 L11 11.5 M11 11.5 L20 7 M11 11.5 L2 7\" fill=\"none\" stroke=\"#dfe9f3\" stroke-width=\"1.3\" stroke-linejoin=\"miter\"/>` — then the wordmark `lattice`, `var(--sans); font-size:var(--logo); font-weight:600; letter-spacing:0.14em; text-transform:lowercase; color:var(--text)`, then a blinking underscore `_` in `var(--accent)` (`blink` keyframes, `animation: blink 1.06s steps(1) infinite`).\n- Centre: `nav` with exactly 4 links — `01 materials`, `02 process`, `03 research`, `04 index` — all `href=\"#\"`, mono, `font-size:var(--nav)`; container `height:var(--nav-h); display:flex; align-items:center; gap:28px`. Numerals sit in `<span class=\"num\">` at `color:var(--dim)`, words at `color:var(--muted)`. Hover over 0.18s ease: word → `var(--text)`, numeral → `var(--accent)`.\n- Right: `a.cta` (href `#`), text `Request data sheet` — mono, `font-size:var(--btn); height:var(--btn-h); padding:0 18px; display:inline-flex; align-items:center; border:1px solid var(--accent-line); border-radius:var(--radius-btn); color:var(--text); background:var(--accent-fill)`. Hover 0.2s ease: `background:rgba(223,233,243,0.13); border-color:var(--accent-dim); box-shadow:0 0 18px rgba(223,233,243,0.14); transform:translateY(-1px)`.\n\n**MAIN (hero)** — `display:flex; flex-direction:column; justify-content:center; align-items:flex-start; padding:var(--hero-y) var(--hero-x); width:100%`; inner `.hero-copy { width:100%; max-width:var(--hero-max) }`. Left-aligned, vertically centred in the 1fr row; on desktop it never exceeds 44vw so it cannot collide with the lattice at `cx = w*0.635`.\n\n1. `.eyebrow` — mono, `font-size:var(--eyebrow); letter-spacing:0.18em; color:var(--dim); text-transform:uppercase`. Exact copy: `LATTICE MATERIALS · ARCHITECTED SOLIDS · EST. 2021`.\n2. `h1` — EXACTLY ONE `<h1>`, `var(--sans); font-weight:600; font-size:var(--h1); letter-spacing:-0.045em; line-height:1.04; color:var(--text); margin:var(--copy-gap-h1) 0 0`. Two masked lines (see Motion), exact copy:\n   Line 1: `Matter, architected.`\n   Line 2: `Down to the unit cell.`\n   Line 2 wraps `<em>unit cell</em>` styled `font-family:var(--mono); font-style:normal; font-weight:400; font-size:0.86em; letter-spacing:-0.02em; color:var(--accent)`.\n3. `p.lead` — `var(--sans); font-size:var(--lead); line-height:1.6; letter-spacing:-0.012em; color:var(--muted); margin:var(--copy-gap-lead) 0 0; max-width:520px`. Exact copy: `Lattice grows gyroid-phase ceramics whose stiffness is programmed by geometry, then verified one unit cell at a time.`\n4. `.spec` — mono, `font-size:var(--spec); color:var(--dim); margin-top:18px; white-space:nowrap`. Exact copy: `G-surface · Si–O system · ρ* 0.42 g/cm³ · E*/E_s 3.1`. The `G-surface` token is `color:var(--accent)`. At `<=560px` the `· E*/E_s 3.1` tail sits in `<span class=\"opt\">` and is hidden so the line never wraps.\n5. `.actions` — `display:flex; gap:12px; flex-wrap:wrap; margin-top:30px`. `a.btn.btn--primary` text `Request the data sheet`; `a.btn.btn--ghost` text `Read the method`. Both `href=\"#\"`; treatments in section F.\n\n**FOOTER** — `padding:22px var(--header-x); display:flex; align-items:center; justify-content:space-between; gap:20px; border-top:1px solid var(--border-soft)`.\n\n- Left: mono `var(--foot)`, `color:var(--dim)`: `© 2026 Lattice Materials`.\n- Centre: mono `var(--foot)`, `color:var(--dim)`: `SEM batch 07 · released 12 MAR`, preceded by a 7px square status pixel `display:inline-block; margin-right:8px; background:var(--accent); box-shadow:0 0 8px var(--accent-dim)`.\n- Right: exactly 3 links `papers`, `patents`, `contact` — mono `var(--foot)`, `color:var(--muted)`, `gap:22px`, `href=\"#\"`, hover `color:var(--text)` over 0.18s.\n\n---\n\n## F. THE UI LANGUAGE\n\nTwo button treatments, both squared and instrument-like. `.btn` is `display:inline-flex; align-items:center; justify-content:center; height:var(--btn-h); padding:0 20px; border-radius:var(--radius-btn); font-family:var(--mono); font-size:var(--btn); letter-spacing:0.01em; text-decoration:none; transition: transform 200ms var(--ease-out), background 200ms var(--ease-out), border-color 200ms var(--ease-out), color 200ms var(--ease-out), box-shadow 200ms var(--ease-out)`.\n\n- `.btn--primary`: `color:#05070a; font-weight:500; border:1px solid rgba(255,255,255,0.55); background:linear-gradient(180deg, #f4f8fc 0%, #dfe9f3 55%, #c3cfdb 100%); box-shadow:0 12px 30px rgba(223,233,243,0.12), inset 0 1px 0 rgba(255,255,255,0.7)`. Hover: `background:linear-gradient(180deg, #ffffff 0%, #eaf1f8 52%, #d0dbe6 100%); transform:translateY(-1px); box-shadow:0 16px 38px rgba(223,233,243,0.20), inset 0 1px 0 rgba(255,255,255,0.8)`. Active: `transform:translateY(0)`.\n- `.btn--ghost`: `color:var(--text); border:1px solid var(--border); background:rgba(255,255,255,0.03)`. Hover: `border-color:var(--accent-line); background:rgba(223,233,243,0.07); transform:translateY(-1px)`. Active: `transform:translateY(0)`.\n\nThe header CTA reuses the ghost family with `background:var(--accent-fill)`. Nav and footer links are plain mono text with numeral prefixes — no pills, no backgrounds, no underlines. Focus: every `a`/`button` gets `:focus-visible { outline:var(--focus-w) solid var(--accent); outline-offset:var(--focus-off); border-radius:2px }`; `::selection { background:rgba(223,233,243,0.22); color:#ffffff }`.\n\n---\n\n## G. MOTION — entrance stagger + named keyframes\n\nAll 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`, one modifier selecting keyframes, and inline `--d`.\n\nStagger table (element → modifier → --d):\n```\nbrand            appear--fade   0.08s\nnav link 1       appear--fade   0.16s\nnav link 2       appear--fade   0.23s\nnav link 3       appear--fade   0.30s\nnav link 4       appear--fade   0.37s\nheader cta       appear--chip   0.44s\neyebrow          appear--type   0.52s\nh1 line 1        appear--mask   0.66s\nh1 line 2        appear--mask   0.80s\nlead             appear--soft   0.98s   (duration 1.15s)\nspec line        appear--soft   1.12s\nprimary button   appear--chip   1.24s\nghost button     appear--chip   1.32s\nfooter left      appear--fade   1.44s\nfooter centre    appear--fade   1.52s\nfooter right     appear--fade   1.60s\n```\n\nNamed keyframes (all end at opacity 1 / identity transform):\n```\nin-fade   from { opacity: 0 }                                          to { opacity: 1 }\nin-soft   from { opacity: 0; transform: translateY(12px) }             to { opacity: 1; transform: none }\nin-mask   from { opacity: 0; transform: translateY(104%) }             to { opacity: 1; transform: none }\nin-chip   from { opacity: 0; transform: translateY(8px) scale(0.97) }  to { opacity: 1; transform: none }\nin-type   from { opacity: 0; clip-path: inset(0 100% 0 0) }            to { opacity: 1; clip-path: inset(0 -4% 0 0) }\nblink     0%,49% { opacity: 1 }  50%,100% { opacity: 0 }\ngrain     0% { transform: translate(0,0) }  25% { transform: translate(-2%,1%) }\n          50% { transform: translate(1%,-2%) }  75% { transform: translate(-1%,2%) }\n```\n\nH1 masking: each line is `<span class=\"line\"><span class=\"line-inner appear appear--mask\">…</span></span>` with `.line { display:block; overflow:hidden; padding:0.03em 0.06em 0.09em }`, `.line-inner { display:inline-block }`. `in-mask` rises out of that hard edge; `in-type` reveals the eyebrow left-to-right like a typed label. Canvas rotation and grain jitter run continuously, independent of the stagger.\n\n---\n\n## H. RESPONSIVE TABLE — token overrides per breakpoint (exact)\n\n```\n>= 1600px:\n  --h1: 74px  --logo: 16px  --nav: 13.5px  --btn: 13.5px  --btn-h: 44px\n  --lead: 17px  --spec: 13px  --foot: 12.5px\n  --header-y: 30px  --header-x: 60px  --hero-x: 60px  --hero-max: 700px\n\n>= 1920px:\n  --h1: 86px  --header-x: 76px  --hero-x: 76px  --hero-max: 780px\n  --lead: 18px  --nav-h: 42px  --btn-h: 46px\n\n1280px – 1599px:\n  --h1: 56px  --header-x: 40px  --hero-x: 40px  --hero-max: 600px\n\n901px – 1279px:\n  --h1: 44px  --nav-h: 34px  --header-x: 30px  --hero-x: 30px\n  --hero-max: 520px  --lead: 15px  --spec: 12px\n\n>= 901px AND max-height 850px:\n  --h1: 46px  --hero-y: 24px  --header-y: 18px  --lead: 15px\n  --copy-gap-h1: 16px  --copy-gap-lead: 18px   (spec margin-top 14px, actions margin-top 22px)\n  canvas zoom: 0.88\n\n>= 901px AND max-height 720px:\n  --h1: 38px  --hero-y: 14px  --header-y: 12px  --eyebrow: 11px\n  --lead: 13.5px  --spec: 11.5px  --btn-h: 36px  --foot: 11px\n  canvas zoom: 0.78\n\n>= 901px (desktop lock, all heights):\n  html, body { height:100%; overflow:hidden }   .page { height:100dvh; overflow:hidden }\n  .scrim uses the 90deg gradient; hero-copy max-width never exceeds 44vw\n\n<= 900px:\n  no height lock; page may scroll; nav becomes a full-viewport overlay menu:\n  - header keeps brand + burger <button class=\"burger\" aria-label=\"Menu\" aria-expanded=\"false\">\n    (three 18x1.5px bars in var(--text), gap 5px); the header CTA hides here\n  - .menu { position:fixed; inset:0; z-index:80; background:rgba(5,7,10,0.94); backdrop-filter:blur(12px);\n    display:flex; flex-direction:column; align-items:flex-start; justify-content:center; padding:0 32px;\n    gap:24px; transform:translateY(-100%); transition:transform 0.45s var(--ease-out) }\n  - .menu.open { transform:none }; links at --nav: 22px + the CTA chip; Escape, link click, or resize >900px all close it\n  canvas: cx = w*0.5, cy = h*0.42, dpr cap 1.5\n  --h1: 40px  --header-x: 24px  --hero-x: 24px  --hero-max: 100%  --hero-y: 64px\n\n<= 560px:\n  --h1: 32px  --lead: 14.5px  --spec: 11px  --eyebrow: 10.5px  --header-x: 20px  --hero-x: 20px\n  .spec .opt { display:none }\n  footer becomes flex-direction:column; align-items:flex-start; gap:10px\n```\n\nThe `min-height:100dvh` stays at every breakpoint; only the `901px+` lock adds `overflow:hidden`. No element exceeds `100vw` at the four required viewports.\n\n---\n\n## I. FAILURE GUARDS — the page is never blank, never broken\n\n1. **Black-first:** the FIRST CSS rule is `html, body { background:#05070a !important; color:#eef3f8 }`; the body tag carries `style=\"background:#05070a;color:#eef3f8\"`; a later rule restates `html, body { background:#05070a; background-image:linear-gradient(180deg,#070a0e 0%,#05070a 46%,#030508 100%) }`.\n2. **Resting opacity is 1:** every `.appear` element's un-animated computed style is its final state (opacity 1, no transform, no clip-path). `fill-mode: both` only holds the FROM state during the delay. If animations fail or JS never runs, the page renders complete.\n3. **Two-rAF entrance fallback:** after two nested rAF ticks, for every `.appear` run `el.getAnimations({subtree:false})`; if none reports `playState` \"running\" or \"finished\", add `.is-in` to ALL `.appear`. If `getAnimations` is missing, apply `.is-in` after the two rAFs. `.is-in { animation:none !important; opacity:1 !important; transform:none !important; clip-path:none !important; filter:none !important }`.\n4. **Canvas fallback:** if `canvas.getContext('2d')` returns null or throws, add `.no-canvas` to `<html>`, push the string `'canvas-unavailable'` into `window.__DEMO__.errors`, and CSS paints a static wire substitute: `.no-canvas body { background-image: linear-gradient(rgba(255,255,255,0.06) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.06) 1px, transparent 1px); background-size: 56px 56px; background-position: center; }` plus `.no-canvas .scrim::after` a static 1px horizon `linear-gradient(90deg, transparent, rgba(223,233,243,0.22), transparent)` at `top:54%; height:1px`.\n5. **Reduced motion:** inside `@media (prefers-reduced-motion: reduce)`: `*, *::before, *::after { transition:none !important; animation:none !important }`; force `.appear, .line-inner, .eyebrow` to `opacity:1 !important; transform:none !important; clip-path:none !important`; hide `.grain` and the cursor blink; the rAF loop keeps running (`frames` increments) with `t` frozen at 7.5s, painting one identical static frame per tick — readable, static, never blank.\n6. **Zero console output:** no `console.*` calls, no thrown exceptions, every canvas call guarded, the geometry builder wrapped so a maths failure degrades to the raw cubic grid.\n\n---\n\n## J. THE EXACT JS (only this, vanilla, one IIFE at end of body)\n\n1. **Telemetry first, before anything else:**\n   `window.__DEMO__ = { name: 'lattice', ready: false, frames: 0, effects: [], errors: [] };`\n   Push `'gyroid-lattice'`, `'horizon-glow'`, `'grain-texture'` into `effects` in that order, exactly once. Then `window.addEventListener('error', e => window.__DEMO__.errors.push(String(e.message || e)))`.\n2. **Build geometry once:** `pts = new Float32Array(2187)`, `idx = new Uint16Array(3888)`, working `rx/ry/rz` and `sx/sy/dd` `Float32Array(729)`, `ok = new Uint8Array(729)`. Fill the 9x9x9 grid at spacing 0.62, Newton-project per section B inside try/catch (on failure keep raw grid points), build the 1944 edge pairs (`+x/+y/+z` neighbours). Allocate 12 `Uint16Array(1944)` bucket buffers + counts and 12 cached `'rgba(223,233,243,' + alpha[b] + ')'` strings with `alpha[b] = 0.03 + 0.29 * Math.pow(1 - b/11, 1.25)`.\n3. **Canvas setup:** grab `#lattice`, guard the 2d context (null → `.no-canvas` + one errors push). `resizeCanvas()`: read `innerWidth/innerHeight` (never getBoundingClientRect), `dpr = Math.min(devicePixelRatio, innerWidth <= 900 ? 1.5 : 2)`, set the backing store, `ctx.setTransform(dpr,0,0,dpr,0,0)`, rebuild the three cached gradients and `cx, cy, f` per section B. Resize queues via a boolean + rAF only.\n4. **Render loop:** one `requestAnimationFrame` callback. Each tick: `window.__DEMO__.frames++` once; if `document.hidden`, skip paint but keep the loop alive; `t = reduced ? 7.5 : ms/1000`; compute `theta, phi, psi` per section B (sin/cos computed 3 times per frame, not per node); rotate all 729 nodes into `rx/ry/rz`; project to `sx/sy/dd`; validity `5.8 < depth < 12.4`; reset bucket counts, bucket every edge by mean depth; `clearRect(0,0,w,h)`; paint horizon band, horizon core; for `b = 11..0`: `beginPath()`, `moveTo/lineTo` the bucket, `stroke()` at `lineWidth 1`; paint the vignette. On the FIRST completed paint set `window.__DEMO__.ready = true`. NO allocation, NO getBoundingClientRect, NO ctx.filter, NO ctx.shadowBlur, NO gradient recreation in the loop.\n5. **Entrance fallback:** two nested rAFs after DOMContentLoaded, then the `.appear` `getAnimations` check and `.is-in` sweep per I.3.\n6. **Mobile menu (`<=900px` only):** burger toggles `.menu.open` + `aria-expanded`; Escape, any link click, or resize to `>900px` closes.\n7. **Reduced motion:** read `matchMedia('(prefers-reduced-motion: reduce)')` once at startup; when true freeze `t` at 7.5 (loop and `frames` keep running) and hide `.grain` via CSS.\n\nNo other JS. No libraries. No timers. No console calls.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the document contains exactly one h1 with the text 'Matter, architected. Down to the unit cell.' and uses semantic header, nav, main, and footer landmarks with every anchor and button carrying an accessible name","canvas#lattice paints non-blank 3D wireframe content at 1440x900, its pixels change across consecutive frames, and window.__DEMO__.frames increments exactly once per requestAnimationFrame tick","window.__DEMO__ is initialized with name 'lattice', ready becomes true after the first painted lattice frame, effects contains 'gyroid-lattice' and 'horizon-glow', and errors stays empty in the happy path","the rAF loop sustains at least 20 painted frames within a 1.2s window at 1440x900 headless, and the render loop body never calls getBoundingClientRect, ctx.filter, or ctx.shadowBlur","no horizontal overflow at 390px, 768px, 1440px, or 1920px; at >=901px body has overflow:hidden and .page is 100dvh; a prefers-reduced-motion reduce block forces all entrance elements to opacity 1 and freezes the lattice to a static frame","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"],"usage":{"build_tokens":19261,"build_prompt_tokens":46,"build_completion_tokens":19215,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":2,"build_latency_ms":178433,"wasted_tokens":0,"repair_tokens":35037,"crosscheck_tokens":22026,"total_tokens":76324},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"30027 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"15 frames, effects: gyroid-lattice, horizon-glow, grain-texture"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"73 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 32.19, stdev 52.67, coverage 0.3722"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 1212161589 -> 1440421020"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Matter, architected.\nDown to the unit cell.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Lattice — Matter, Architected"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=458 pageErrors=0"}]},"crosscheck":{"slug":"lattice","reviewer_model":"MiniMaxAI/MiniMax-M3","reviewer_platform":"cmdcode","verdict":"partial","confidence":"high","aesthetic_score":7,"requirements_met":["Single self-contained index.html with no external media","Google Fonts loaded with Inter and IBM Plex Mono","Telemetry hook with correct fields (name, ready, frames, effects, errors)","Three effects pushed in correct order","Gyroid Newton projection with analytic gradient and displacement clamp","12 depth buckets with preallocated Uint16Array buffers and cached strokeStyle strings","Horizon glow band and 1px core line built from cached gradients","Vignette radial gradient drawn last","Canvas resolution trick with dpr capped at 2 / 1.5 and setTransform","Grain texture via inline SVG feTurbulence data URI with stepped animation","Brand mark SVG path matches brief exactly","Nav with 4 mono links, numerals in .num, word/nav hover color transition","Header CTA ghost-style button with correct hover state","Hero eyebrow with in-type reveal and exact copy","Two-line H1, .em styled mono accent for 'unit cell'","Lead paragraph with exact copy and max-width:520px","Spec line with .hi accent on 'G-surface' and .opt span at <=560px","Two actions: btn--primary with gradient + inset highlight, btn--ghost with subtle bg","Footer with copyright, centered SEM batch with pip, three mono links","Viewport lock at >=901px (html/body overflow:hidden, page height:100dvh)","Mobile burger at <=900px and full-screen menu overlay with backdrop blur","Reduced motion fallback that freezes t at 7.5s and hides grain/blink","Entrance fallback via two nested rAFs + getAnimations check + .is-in sweep","Canvas fallback grid + horizon line via .no-canvas class"],"requirements_missed":["Canvas center at desktop is cx = w * 0.635, but rendered lattice looks centered slightly right-of-center (around ~0.62 visually fine)","Rendered lattice occupies a noticeably larger share of the viewport than the designed 62% — at 1024x640 the wireframe appears to span ~80% of viewport width/height","Horizon glow band and 1px core line are essentially invisible in the screenshot — core line cannot be seen at all, band is barely detectable","Depth fade does not read clearly: far geometry dissolves but the bulk of the mesh reads as a uniformly dense, near-opaque tangle rather than a sparse architected surface with selective crisp near edges","Spec line `G-surface · Si–O system · ρ* 0.42 g/cm³ · E*/E_s 3.1` renders as monospace but the in-text stylized Greek/sub characters (ρ*) are using plain ASCII p* with a star, not the actual ρ character (the brief says 'ρ*' which is acceptable as text, but it is followed by a plain ' g' that reads as '0.42 g/cm³' — the 'g' should be italicized or rendered as g for grams; brief copy uses the unicode ρ so partial)","Footer pip is not visibly glowing in the screenshot (only the 7px square is seen, no halo)","Header CTA appears far to the right at 1024px (visually fine, but the right alignment pushes it slightly off-center relative to the grid)","Footer has 4 line items rendered side-by-side at 1024px, but the footer should align footer-left flush-left, footer-center physically centered, footer-right flush-right — the layout looks acceptable but the middle item is slightly offset"],"issues":[{"issue":"Lattice wireframe reads as a dense opaque cube-like tangle rather than a clearly architected gyroid minimal surface","severity":"medium","evidence":"Screenshot: the structure occupies most of the right half of the viewport and individual gyroid saddles are not legible; near geometry does not stand out as crisper than far geometry","fix_hint":"Increase depth-bucket count or steepen alpha falloff so near edges are clearly more luminous than far edges; verify the 12-stroke ordering and that the b=0 alpha (0.32) is producing visible contrast"},{"issue":"Horizon glow band and 1px core line are not visible in the rendered page","severity":"medium","evidence":"Screenshot shows no detectable soft horizontal band around y=cy and no 1px line crossing the lattice","fix_hint":"Verify createLinearGradient receives correct y1/y2 values for gradBand (top, bottom) and that the fillRect Y coordinates align; the fillStyle assignment is inside the loop but only on resize — confirm gradBand/gradCore are not null on first paint"},{"issue":"Vignette appears too strong, washing the canvas edges to near-black and partially clipping the lattice's left/back faces","severity":"low","evidence":"Screenshot edges (especially bottom and top corners) are nearly pure black; lattice wireframe fades into the vignette aggressively"},{"issue":"Footer status pip lacks the expected halo glow","severity":"low","evidence":"Screenshot shows a flat 7px square in the center footer line; box-shadow:0 0 8px var(--accent-dim) appears to render with little or no visible glow against the dark background","fix_hint":"Increase the blur radius or use a stronger accent color for the glow"},{"issue":"Greek ρ character in spec line is rendered as plain ASCII 'p' (the brief uses the actual ρ glyph, but the source code uses 'p*')","severity":"low","evidence":"HTML spec line: `ρ* 0.42 g/cm³` uses U+03C1 (ρ) which is correct in source — actually verified the source uses 'ρ' so this is not an issue, disregard","fix_hint":"N/A"}],"summary":"The implementation faithfully delivers the structural brief — typography, layout, tokens, telemetry, motion choreography, and failure guards are all present and correctly wired. Visually it lands in the right neighborhood but the depth fade and horizon glow read weakly in the screenshot, leaving the canvas looking like a uniformly dense cube tangle rather than a clearly architected gyroid with crisp near edges and dissolved far edges.","usage":{"input":20591,"output":1435,"total":22026,"reasoning":0,"cached":0},"latency_ms":26971}},{"slug":"margin","title":"Margin","category":"Landing Page","effect":"Two counter-scrolling columns of procedural deadstock garment plates frame a static marketplace message.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/margin/index.html","doc":"/prompts/margin.md","prompt_text":"A. BUILD TARGET\n\nCreate one complete, framework-free index.html for the Second Skin marketplace landing page. Set the document title to \"Second Skin — Deadstock, kept in motion\" and set lang=\"en\". Load the only permitted network resource, the Open Sans stylesheet from fonts.googleapis.com, followed by fonts.gstatic.com, with a system fallback of Arial, Helvetica, sans-serif; if the request fails, all typography must remain legible. No other network request is allowed.\n\nAt viewport widths of 901px and wider, the document must use height: 100dvh, min-height: 700px, and overflow: hidden. The page itself is a fixed, full-viewport editorial marketplace scene and must not scroll. At widths of 900px and below, replace that rule with height: auto, min-height: 100dvh, overflow-x: hidden, and normal vertical document scrolling. This is not a scroll-driven concept: the counters run continuously rather than responding to page scroll.\n\nUse semantic header, nav, main, section, article, figure, and footer elements. Include exactly one h1. Every visible link, button, nav item, and size label must have an accessible name. Decorative SVGs and texture layers must use aria-hidden=\"true\". Add :focus-visible outlines using a 2px solid #B4674E outline with a 3px #F4F1EA offset.\n\nDo NOT build a product grid, shopping cart, account modal, filter drawer, checkout flow, video background, image carousel, external image, photograph, WebGL scene, Three.js canvas, scroll-linked reveal, horizontal page scroll, text marquee, or mobile pin. Do not include a second h1, a cookie notice, a newsletter modal, autoplay sound, a cursor follower, or a JavaScript animation library. Do not use Tailwind, React, GSAP, Lottie, MP4, WebM, GIF, remote raster media, CDN-hosted scripts, base64 imagery, or data-URI photography.\n\nB. THE BACKDROP\n\nThe page background is a flat #F4F1EA bone field. Place one fixed decorative canvas behind all content at z-index 0. It is not a garment renderer: it is a low-cost ambient weave that prevents the large bone areas from reading as empty. The canvas is pointer-events: none and has the same dimensions as the viewport.\n\nCreate the canvas at devicePixelRatio, capped at 1.5. In JavaScript, multiply window.innerWidth and window.innerHeight by the capped ratio, assign those values to canvas.width and canvas.height, and set the CSS size to 100% by 100%. This is the canvas resolution trick. Redraw it only on resize, not on every animation frame.\n\nThe static canvas background is transparent. Draw a 3px lattice in two passes. First draw horizontal lines at y = 0, 3, 6 through height using rgba(26,24,21,0.018). Then draw vertical lines at x = 0, 3, 6 through width using rgba(26,24,21,0.012). The resulting average composite cost is under 0.7 fill rectangles at a 1.5 DPR cap. Do not call ctx.filter, ctx.shadowBlur, ctx.drawImage, or any per-item blur operation. The visible garment cards supply the detailed texture; the canvas is only a page-level paper grain.\n\nThe primary moving backdrop is the pair of column tracks. Each column contains 12 article elements: 6 authored garment plates followed by an exact DOM duplicate of those 6 articles. Each card is 320px wide by 440px high on the desktop scale. The 12-card track uses 16px row gaps and is 6,768px tall including the six repeated cards and eleven inter-card gaps. The first and second columns are identical in dimensions but use different authored plate order and opposite motion direction. Their transform animation travels exactly 50% of the duplicated track, 3,384px, over 34 seconds with linear timing. The duplicated boundary contains the same 16px gap as every internal boundary, so the loop is visually continuous.\n\nEach card has a flat #E8E4DC field. Inside every card, render one inline SVG with viewBox=\"0 0 320 440\", width 320px, height 440px. The SVG must contain exactly three overlapping garment-body path shapes: a left shoulder and sleeve path, a main body path, and a right shoulder and sleeve path. All three paths use the same muted dye color selected for that card, a 1.5px #1A1815 stroke, and a solid fill. Over the SVG, place a 3px repeating-linear-gradient weave layer with background-image repeating-linear-gradient(0deg, rgba(26,24,21,0.04) 0px, rgba(26,24,21,0.04) 1px, transparent 1px, transparent 3px), plus a second repeating-linear-gradient rotated 90 degrees at the same opacity.\n\nUse six dye colors in this exact order: #7C8B7A, #9B9A78, #A97862, #6F7D82, #B4674E, and #8A786D. Cycle those six colors through the first authored six cards in each column, then repeat the colors in the duplicated six. Use these silhouettes in the SVG, with the path coordinates measured in the 320 by 440 viewBox: left path \"M80 112 C54 118 38 141 28 177 L45 245 L80 224 L88 137 Z\"; body path \"M88 118 C108 101 134 101 158 118 L177 345 C153 360 96 360 72 345 Z\"; right path \"M158 112 C184 118 200 141 210 177 L193 245 L158 224 L150 137 Z\". Apply the same geometry to all cards and vary the SVG group transform by card: identity for the first row, translateY(0) for all rows, and use a 0.92 to 1.04 scale range across the six cards. The variation must not alter the 320 by 440 card bounds.\n\nAdd one hang-tag rectangle inside each SVG. Center it at x=250, y=86, width 54px, height 78px, with a 1.5px #1A1815 outline, a small 4px circular punch hole at x=250 y=99, and a 0.35 opacity #F4F1EA diagonal fold line. Set the tag rectangle group to transform-origin 250px 86px and scale(0.6). Render the size text at x=250, y=133, using the card's own selected font stack, font-size 15px, font-weight 700, text-anchor middle, fill #1A1815, and the exact size string assigned to that plate. Alternate the size strings XS, S, M, L, XL, and 2XL by card index.\n\nThe apparent travel of each fade region is 900px: each column is covered by a 128px top gradient and a 128px bottom gradient. The gradients are static and do not animate. They are #F4F1EA at 0% opacity 1, #F4F1EA at 58% opacity 0.86, transparent at 100%, with the bottom gradient mirrored by reversing its stop sequence. Place them inside each column, outside the moving track, with pointer-events: none.\n\nC. THE LAYER STACK\n\nThe stacking order is explicit and must not be changed.\n\nz-index 0: the ambient weave canvas, fixed to the viewport, pointer-events none.\n\nz-index 1: the page bone background on body and the full-width hero shell.\n\nz-index 2: the left marquee column, at the left edge of the central desktop grid.\n\nz-index 3: the left column's top and bottom fade overlays.\n\nz-index 4: the center message panel, positioned in the 40vw horizontal gap.\n\nz-index 5: the right marquee column.\n\nz-index 6: the right column's top and bottom fade overlays.\n\nz-index 7: the header navigation, which remains above all moving material.\n\nz-index 8: the footer metadata, which remains above all moving material.\n\nz-index 9: a single 1px transparent focus-ring utility, pointer-events none, used only as a visual stacking helper when needed.\n\nThe columns must not overlap the center panel on desktop. The center panel is 40vw wide at widths from 901px through 1920px, capped at 640px. The two column cards remain 320px wide. At 1440px, use a centered three-column grid with 320px, minmax(320px, 40vw), 320px and 40px gaps. The grid occupies 1,296px at 1440px, leaving 72px of outer breathing room. At 1920px, the 640px center cap creates 400px of intentional page margin on each side of the full composition.\n\nD. TOKEN TABLE\n\nDefine every value used by the page in :root:\n\n--bg: #F4F1EA\n--ink: #1A1815\n--plate: #E8E4DC\n--sage: #7C8B7A\n--rust: #B4674E\n--white-muted: rgba(244,241,234,0.72)\n--line: rgba(26,24,21,0.18)\n--line-strong: rgba(26,24,21,0.34)\n--grain: rgba(26,24,21,0.04)\n--dye-1: #7C8B7A\n--dye-2: #9B9A78\n--dye-3: #A97862\n--dye-4: #6F7D82\n--dye-5: #B4674E\n--dye-6: #8A786D\n--page-min: 700px\n--header-height: 76px\n--footer-height: 66px\n--grid-gap: 40px\n--card-width: 320px\n--card-height: 440px\n--card-gap: 16px\n--fade-height: 128px\n--center-width: 40vw\n--center-max: 640px\n--gutter: 32px\n--radius-pill: 999px\n--radius-tag: 4px\n--type-display: clamp(3.5rem, 6.2vw, 7.75rem)\n--type-title: clamp(1.75rem, 3.15vw, 4rem)\n--type-body: 16px\n--type-small: 12px\n--type-label: 11px\n--leading-display: 0.88\n--leading-title: 0.98\n--leading-body: 1.45\n--track-label: 0.14em\n--motion-counter: 34s\n--motion-hover: 220ms\n--motion-tag: 400ms\n--ease-hover: cubic-bezier(.2,.8,.2,1)\n--ease-entrance: cubic-bezier(.25,.1,.25,1)\n\nE. THE COMPOSITION\n\nBuild the document in this order.\n\nFirst create body and the semantic page wrapper. Body has margin 0, background var(--bg), color var(--ink), font-family Arial, Helvetica, sans-serif, overflow-x hidden, and -webkit-font-smoothing: antialiased. Set the header height to var(--header-height), display grid with grid-template-columns 1fr auto 1fr, align-items center, padding 0 var(--gutter), and border-bottom 1px solid var(--line). Place the wordmark link at the left with exact visible copy \"SECOND SKIN\", font-size 14px, font-weight 800, letter-spacing 0.18em, and accessible label \"Second Skin home\". Place the nav in the center with two links, exact visible copy \"SHOP DEADSTOCK\" and \"HOW IT WORKS\", both 11px, uppercase, 700, letter-spacing 0.12em. Place the right-side link at the right with exact visible copy \"SELL A PIECE\", border-bottom 1px solid var(--ink), and accessible label \"Sell a piece\". Header z-index is 7.\n\nThen create main and the single hero section. The hero is width 100%, height calc(100dvh - var(--header-height) - var(--footer-height)), min-height 560px, and display grid. The desktop grid is grid-template-columns var(--card-width) minmax(320px, var(--center-width)) var(--card-width), column-gap var(--grid-gap), align-items stretch, justify-content center, padding 0 32px. The center block has z-index 4, display flex, flex-direction column, justify-content center, align-items flex-start, padding 0 28px, and text-align left.\n\nCreate the center message with the following exact copy. The eyebrow is \"THE DEADSTOCK EDIT / 06\". The h1 is \"Wear the / in-between.\" The h1 uses max-width 560px, font-size var(--type-display), line-height var(--leading-display), font-weight 500, letter-spacing -0.075em, margin 0, and wraps on the literal slash using a line break. The paragraph is \"Discontinued shapes, unworn surplus, and hard-to-find layers. Browse pieces that no longer make it to the shelf.\" Its max-width is 390px, margin 26px 0 0, font-size var(--type-body), line-height var(--leading-body), color rgba(26,24,21,0.72). The primary action is an accessible link with visible copy \"EXPLORE THE EDIT\" and aria-label \"Explore the deadstock edit\". The secondary action is a text link with visible copy \"VIEW SELLING GUIDE\" and aria-label \"View the selling guide\". The action row uses margin-top 32px, display flex, gap 18px, align-items center, flex-wrap wrap.\n\nBelow the center message, add a compact inventory line with exact copy \"2,481 pieces rescued\" and a 6px sage dot. It sits 44px below the action row, with font-size 12px, letter-spacing 0.08em, and color rgba(26,24,21,0.62). This line is informational, not a control.\n\nCreate the left and right marquee columns as two section elements with aria-label \"Discontinued garment plates\". Each section is 320px wide, height 100%, overflow hidden, position relative. Inside each section, create a .track containing 12 .plate articles. Set track position absolute at top 0 and left 0, width 320px, display flex, flex-direction column, gap 16px, and will-change transform. Animate the left track with keyframes columnUp from translateY(0) to translateY(-3384px), and the right track with keyframes columnDown from translateY(-3384px) to translateY(0). Both animations are 34s linear infinite, with animation-delay 0s on the left and -17s on the right. Do not pause either track on hover.\n\nEach plate article has position relative, width 320px, height 440px, flex 0 0 440px, overflow hidden, background var(--plate), border 1px solid rgba(26,24,21,0.08), and transform-origin center. Add a figure element with margin 0 and an SVG filling the article. Add a small plate index in the upper-left corner at 16px 16px, font-size 10px, font-weight 700, letter-spacing 0.12em, and copy \"SS / 01\" through \"SS / 12\" by authored row. The index must not obscure the garment body.\n\nPlace the exact top fade in each column with position absolute, inset 0 0 auto, height 128px, background linear-gradient(to bottom, #F4F1EA 0%, rgba(244,241,234,0.86) 58%, transparent 100%), z-index 3. Place the bottom fade with position absolute, inset auto 0 0, height 128px, background linear-gradient(to top, #F4F1EA 0%, rgba(244,241,234,0.86) 58%, transparent 100%), z-index 3.\n\nCreate the footer as a grid with height var(--footer-height), padding 0 var(--gutter), border-top 1px solid var(--line), display grid, grid-template-columns 1fr auto 1fr, align-items center, font-size 11px, letter-spacing 0.08em. Left copy is \"CURATED IN LONDON / 2025\". Center copy is \"NO SEASON. NO SAMPLE SALE.\". Right copy is \"SCROLL LESS. FIND MORE.\" and is right-aligned. Footer is not sticky on mobile.\n\nF. THE UI LANGUAGE\n\nThe primary action is a 48px high pill with padding 0 22px, border-radius 999px, background linear-gradient(135deg, #1A1815 0%, #34302A 100%), color #F4F1EA, font-size 11px, font-weight 800, letter-spacing 0.13em, and border 1px solid #1A1815. Its hover state is background linear-gradient(135deg, #34302A 0%, #1A1815 100%), transform translateY(-2px), and box-shadow 0 6px 0 rgba(180,103,78,0.36). The primary action must return to its original position in 180ms when focus or hover ends.\n\nThe secondary action has no filled background, a 1px bottom border, padding 0 0 4px, and color var(--ink). Its hover state is color var(--rust), border-color var(--rust), and transform translateX(3px). The selling link is not a pill. Header links use color var(--ink) and a 2px bottom border that changes from transparent to var(--rust) on hover and focus-visible. No link changes layout on hover.\n\nPlate hover is 220ms with var(--ease-hover): transform translateY(-6px) scale(1.03), border-color var(--line-strong), and a subtle box-shadow 0 12px 24px rgba(26,24,21,0.08). Because the card is inside a moving track, apply hover transform to the article and never stop the parent track. The SVG tag group swings from rotate(-2deg) to rotate(4deg) over 400ms with ease-out, using transform-origin 250px 86px. Return to rotate(0deg) on pointer leave over 400ms. All hover motion is disabled under reduced motion.\n\nG. MOTION\n\nThe entrance grammar is opacity 0 to 1 plus translateY(20px) to translateY(0), duration 620ms, using var(--ease-entrance), fill mode both. Apply .rise once on load.\n\nUse this exact stagger:\n\nheader .wordmark: rise, delay 0ms.\nheader nav: rise, delay 80ms.\nheader .sell-link: rise, delay 120ms.\ncenter .eyebrow: rise, delay 180ms.\ncenter h1: rise, delay 260ms.\ncenter p: rise, delay 380ms.\ncenter .actions: rise, delay 480ms.\ncenter .inventory: rise, delay 560ms.\nfooter .footer-left: rise, delay 620ms.\nfooter .footer-center: rise, delay 680ms.\nfooter .footer-right: rise, delay 740ms.\n\nDefine rise as opacity 0 and transform translateY(20px) at 0%, opacity 1 and transform translateY(0) at 100%. Define columnUp and columnDown as the two exact transform tracks described in the composition. Define tagSwing as rotate(0deg) at 0%, rotate(4deg) at 55%, and rotate(0deg) at 100%. Use only transform and opacity in animated CSS. The marquee uses transform only. Entrance uses transform and opacity only. The tag uses transform only.\n\nH. RESPONSIVE TABLE\n\nAt 1440px by 900px and 1920px by 1080px, retain the desktop values. The composition remains one viewport, the center width is min(40vw, 640px), and the two 320px columns remain visible. At a viewport height of 760px, override --header-height to 64px, --footer-height to 54px, --gutter to 24px, --type-display to clamp(3rem, 5.7vw, 6rem), and set the hero minimum height to 520px. At a viewport height of 680px, override --header-height to 58px, --footer-height to 48px, --gutter to 20px, --type-display to clamp(2.6rem, 5.2vw, 5rem), set the center paragraph max-width to 330px, and reduce the action row margin-top to 22px.\n\nAt 901px through 1023px, set --card-width: 280px, --card-height: 386px, --grid-gap: 24px, --gutter: 24px, --fade-height: 104px, and --center-width: 40vw. Render the cards at 280 by 386 in CSS, but keep the SVG viewBox at 0 0 320 440 so the silhouettes do not distort. Update the track's exact half travel to 2,976px using the duplicated six-card geometry and 14px card gap. The 34s duration remains unchanged.\n\nAt 768px by 1024px, switch to the stacked mobile composition. Set body and the document to normal vertical flow. Set header height 64px, footer height 58px, gutter 24px, and hero height auto. Use a single-column main grid with grid-template-rows: auto 520px; row-gap 24px. Put the center message first, then place both columns in a two-column subgrid with grid-template-columns 1fr 1fr and column-gap 16px. Each mobile column is width 100%, max-width 360px, and height 520px. Set cards to 100% width, 440px high, and use the same SVG viewBox with preserveAspectRatio=\"xMidYMid slice\". Reduce the card gap to 14px and set the mobile track half travel to 2,964px. The mobile fade height is 96px. The center text uses a max-width of 520px, text-align left, and padding 0 24px.\n\nAt 390px by 844px, set header height 60px, footer height auto with min-height 92px, gutter 16px, row-gap 18px, grid-template-rows: auto 420px, and grid-template-columns 1fr 1fr. Set the mobile columns to 171px wide, cards to 171px wide and 300px high, card gap 12px, fade height 78px, and mobile track half travel to 1,872px. The SVG uses preserveAspectRatio=\"xMidYMid slice\". The center message has padding 0 8px, h1 font-size 48px, line-height 0.9, paragraph max-width 340px, and action row gap 12px. Reduce the inventory line margin-top to 28px. Keep the two-column counter-scroll; do not hide either column.\n\nAt max-width 900px, header navigation keeps both links but reduces the gap to 14px, the sell link becomes text-only, and the header uses grid-template-columns 1fr auto. Put the sell link in the second row of the nav? No: move the sell link to the footer by duplicating no controls. Hide the header sell link with display:none, which removes it from the accessibility tree, and keep the two center nav links. The footer becomes a 2-row layout with the left and right metadata on the first row and the center statement on the second row. At 390px, footer padding is 14px 16px and all footer text is 9px.\n\nI. FAILURE GUARDS\n\nSet every animated entrance element's resting opacity to 1 and resting transform to translateY(0). Do not leave opacity 0 if JavaScript fails. The plate tracks have their authored content in the DOM before animation begins. If CSS animation is unavailable, cards remain visible in a static, usable column. If fonts fail, the fallback stack renders all copy without layout collapse. If SVG path rendering fails, the plate field, index, and size tag remain readable.\n\nUse a two-requestAnimationFrame fallback to set the telemetry ready flag: first rAF schedules the second rAF, and the second rAF sets ready true. The telemetry object must exist before either frame is scheduled. If reduced motion is enabled, cancel the marquee animations by setting animation: none, show the first six cards in each column at opacity 1, and do not call requestAnimationFrame continuously. Read the media query once at startup and again on its change event.\n\nJ. THE EXACT JS\n\nUse this JavaScript at the end of body:\n\nconst telemetry = {\n  name: \"margin\",\n  ready: false,\n  frames: 0,\n  effects: [],\n  errors: []\n};\nwindow.__DEMO__ = telemetry;\n\nwindow.addEventListener(\"error\", event => {\n  telemetry.errors.push(String(event.message || \"window error\"));\n});\n\nconst reduceMotionQuery = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\nconst canvas = document.querySelector(\"[data-weave]\");\nconst context = canvas && canvas.getContext(\"2d\", { alpha: true });\nconst ratio = Math.min(window.devicePixelRatio || 1, 1.5);\nlet running = true;\nlet previous = 0;\n\nfunction drawWeave() {\n  if (!canvas || !context) return;\n  const width = window.innerWidth;\n  const height = window.innerHeight;\n  canvas.width = Math.max(1, Math.floor(width * ratio));\n  canvas.height = Math.max(1, Math.floor(height * ratio));\n  canvas.style.width = width + \"px\";\n  canvas.style.height = height + \"px\";\n  context.setTransform(ratio, 0, 0, ratio, 0, 0);\n  context.clearRect(0, 0, width, height);\n  context.fillStyle = \"rgba(26,24,21,0.018)\";\n  for (let y = 0; y < height; y += 3) {\n    context.fillRect(0, y, width, 1);\n  }\n  context.fillStyle = \"rgba(26,24,21,0.012)\";\n  for (let x = 0; x < width; x += 3) {\n    context.fillRect(x, 0, 1, height);\n  }\n}\n\nfunction markReady() {\n  telemetry.ready = true;\n  requestAnimationFrame(frame);\n}\n\nfunction frame(time) {\n  if (!running) return;\n  if (previous === 0) previous = time;\n  const delta = Math.min(50, time - previous);\n  previous = time;\n  telemetry.frames += 1;\n  if (telemetry.frames === 1) telemetry.effects.push(\"ambient-weave\");\n  if (telemetry.frames === 1) telemetry.effects.push(\"counter-scroll\");\n  if (telemetry.frames === 1) telemetry.effects.push(\"rise\");\n  if (delta > 40) telemetry.effects.push(\"frame-recovery\");\n  requestAnimationFrame(frame);\n}\n\nfunction stopForReducedMotion() {\n  running = false;\n  telemetry.effects.push(\"reduced-motion\");\n}\n\ndrawWeave();\nwindow.addEventListener(\"resize\", drawWeave, { passive: true });\nif (reduceMotionQuery.matches) {\n  stopForReducedMotion();\n} else {\n  requestAnimationFrame(() => requestAnimationFrame(markReady));\n}\nreduceMotionQuery.addEventListener(\"change\", event => {\n  if (event.matches) stopForReducedMotion();\n});\n\nwindow.__DEMO__ = telemetry;\n\nThe implementation must keep the rAF loop at one request per frame, increment frames exactly once inside that callback, avoid getBoundingClientRect entirely, avoid layout reads after initial setup, and produce no console output.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["The page contains exactly one h1, semantic header, nav, main, and footer, and document.documentElement.scrollHeight does not exceed window.innerHeight at 1440x900.","At 1440x900, both marquee sections contain 12 plate articles, each plate measures 320 by 440 CSS pixels, and the two tracks use opposite 34-second linear animations.","Every plate contains an inline SVG with exactly three garment paths, a 3px repeating-linear-gradient texture layer, a centered 0.6-scale hang-tag group, and one readable size label.","At 390x844, document.documentElement.scrollWidth is no greater than window.innerWidth, both counter-scrolling columns remain visible, and the hero switches to the stacked responsive composition.","window.__DEMO__ exists with name margin, ready becomes true after first paint, frames increases inside the rAF loop, effects contains the named effects, and the page has no uncaught browser errors.","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"],"usage":{"build_tokens":56960,"build_prompt_tokens":6902,"build_completion_tokens":50058,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":569284,"wasted_tokens":0,"repair_tokens":126421,"crosscheck_tokens":30611,"total_tokens":213992},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"71912 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"10 frames, effects: ambient-weave, counter-scroll, rise"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"73 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 0.27, stdev 1.85, coverage 0.0206"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 276700121 -> 3769831736"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Wear the\nin-between.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Second Skin — Deadstock, kept in motion"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=789 pageErrors=0"}]},"crosscheck":{"slug":"margin","reviewer_model":"gpt-5.6-luna","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":5,"requirements_met":["The page uses the specified title, language, semantic landmarks, and one h1.","The visible copy is specific and matches the brief.","The desktop presentation uses the intended bone, ink, muted dye, editorial palette.","The page includes procedural SVG garment plates, CSS weave layers, fades, counter-scroll animations, and focus styling.","The 390px, 768px, and 1440px compositions avoid horizontal overflow according to the supplied harness."],"requirements_missed":["The desktop grid does not place the center message between the two garment columns because the DOM/grid item order is wrong.","The ambient canvas is not the required static two-pass lattice; it adds extra fibers and a continuously repainted moving mark.","The 901px–1023px layout does not reliably keep both 280px columns visible alongside the center panel.","The required column article ordering is not satisfied for the left column: it repeats SS / 01 through SS / 06 rather than using the required different authored order.","The specified static canvas opacity values are changed from 0.018/0.012 to 0.045/0.03, with additional much darker marks.","The canvas is repainted every animation frame, contradicting the requirement to redraw it only on resize."],"issues":[{"issue":"The primary composition is structurally misordered: the center message renders in the left grid column, the left garment track renders in the center, and the right track renders on the right.","severity":"high","evidence":"In .hero, .center-message is the first grid item, followed by .mobile-columns with display: contents; the screenshot shows the headline at the far left and a garment plate in the center instead of columns-message-column."},{"issue":"The 1024px screenshot visibly clips the intended composition and loses the left garment column.","severity":"high","evidence":"At 1024px the compact grid requires roughly 1018px for columns and gaps before the 64px hero padding, while the screenshot shows only the message area at left, the center track, and the right track."},{"issue":"The ambient backdrop is visibly a dotted/fiber pattern rather than the specified subtle two-pass 3px lattice.","severity":"medium","evidence":"drawWeave() adds a fiberRow/fiberColumn loop with repeated 5x2 and 2x5 rectangles and uses rgba(26,24,21,0.16); repaintWeaveMark() adds a moving rgba(26,24,21,0.32) mark."},{"issue":"The canvas animation violates the requested motion coupling and performance choreography.","severity":"medium","evidence":"frame() calls repaintWeaveMark(time) and schedules itself every frame, while the brief requires the canvas to redraw only on resize and the counters/tracks to run independently."},{"issue":"The left column does not use a different authored plate order from the right column.","severity":"medium","evidence":"The left track's authored six are SS / 01 through SS / 06 in order, while the brief explicitly requires different authored plate order for the two columns."},{"issue":"The screenshot reads as a broken/default generated composition rather than a deliberate three-column marketplace scene.","severity":"high","evidence":"The headline occupies a narrow left strip, the central garment is disconnected from the message, and the right card is partially clipped at the viewport edge."}],"summary":"The page has the correct copy, palette, and procedural ingredients, but the defining desktop composition is wrong: the message and garment columns are in the wrong positions and the 1024px view is visibly clipped. The canvas also ignores the required static low-cost lattice and instead adds conspicuous animated texture that makes the result look like an unrefined AI mockup.","usage":{"input":29190,"output":1421,"total":30611,"reasoning":516,"cached":0},"latency_ms":12366}},{"slug":"monolith-type","title":"Monolith Type","category":"Landing Page","effect":"A giant extruded wordmark with a specular sweep and a floor reflection","platform":"workbuddy","platform_label":"Workbuddy proxy","model":"gpt-5.5","model_label":"GPT-5.5 (gpt-5.5)","demo":"/demos/monolith-type/index.html","doc":"/prompts/monolith-type.md","prompt_text":"A. Build one complete valid index.html for a single-viewport landing page for a type-design studio named MONOLITH. Document title: Monolith Type — Monumental Letterforms. Set lang=\"en\". One file only: plain HTML, inline CSS, and one small inline script. No build step, no framework, no bundler. Google Fonts is the only permitted network request; use it for Inter and Instrument Serif with system fallbacks, and do not load any other URL. No external media: no images, no video, no GIF, no canvas, no WebGL, no Three.js, no Lottie, no icon CDN, no base64 photo blobs. Every visible pixel must be generated from HTML, CSS gradients, SVG primitives, pseudo-elements, and text. Desktop is exactly one frame with no scroll at width >= 901px: html and body height:100%; overflow:hidden; .page height:100dvh; overflow:hidden. Mobile and tablet at width <= 900px may scroll vertically but must never overflow horizontally. Create exactly one h1, and split the word MONOLITH into per-letter spans so each letter can cascade in. The page contains only: header, main hero, and footer with three small stat items. Do NOT build extra sections, cards, pricing, forms, testimonials, case studies, blog links, app screenshots, video controls, dark-mode toggles, cookie banners, or a large footer sitemap. The core visual idea is a giant extruded wordmark with a slow specular sweep across its top face and a blurred vertically-faded floor reflection beneath it.\n\nB. THE BACKDROP. Use pure CSS and SVG only; no canvas. The cinematic field is a procedural stage built from three fixed elements behind the page: .stage-field, .stage-grid, and .stage-noise. .stage-field covers inset:0 with background color #050505 and these layered gradients, back to front: radial-gradient(circle at 50% 118%, rgba(242,242,232,0.16) 0%, rgba(242,242,232,0.075) 14%, rgba(242,242,232,0.025) 31%, transparent 52%); radial-gradient(ellipse at 50% 65%, rgba(138,138,122,0.075) 0%, rgba(82,82,74,0.032) 36%, transparent 68%); linear-gradient(180deg, #050505 0%, #070706 38%, #020202 100%). .stage-field::before is the horizon haze: position absolute; left:50%; top:58%; width:min(86vw, 1280px); height:1px; transform:translateX(-50%); background:linear-gradient(90deg, transparent 0%, rgba(242,242,232,0.10) 18%, rgba(242,242,232,0.32) 50%, rgba(242,242,232,0.10) 82%, transparent 100%); filter:blur(0.5px); opacity:0.75. .stage-field::after is the floor plane: position absolute; left:50%; bottom:-12%; width:min(118vw, 1640px); height:46%; transform:translateX(-50%) perspective(760px) rotateX(66deg); transform-origin:50% 100%; border-radius:50% 50% 0 0; background:radial-gradient(ellipse at 50% 0%, rgba(255,255,245,0.105) 0%, rgba(230,230,215,0.045) 22%, rgba(120,120,108,0.018) 48%, transparent 70%), repeating-linear-gradient(90deg, rgba(255,255,255,0.045) 0 1px, transparent 1px 82px), repeating-linear-gradient(0deg, rgba(255,255,255,0.032) 0 1px, transparent 1px 70px); opacity:0.68; mask-image:linear-gradient(180deg, rgba(0,0,0,0.92) 0%, rgba(0,0,0,0.54) 40%, transparent 100%). .stage-grid covers inset:0; opacity:0.35; background-image: linear-gradient(rgba(255,255,255,0.018) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.018) 1px, transparent 1px); background-size:96px 96px; background-position:center calc(50% + 8px); mask-image:radial-gradient(ellipse at 50% 58%, black 0%, black 36%, transparent 74%). Animate .stage-grid with grid-drift for 28s linear infinite from background-position:center calc(50% + 8px) to calc(50% + 96px) calc(50% + 104px). .stage-noise is an inline SVG filter texture element, not an external image: an absolutely positioned div with opacity:0.12, mix-blend-mode:screen, pointer-events:none, background generated by an inline SVG data URL is not allowed; instead create an inline svg element in the HTML containing feTurbulence baseFrequency 0.72, numOctaves 2, seed 17, stitchTiles stitch, and use a full-viewport rect with filter url(#noise-filter). Its CSS opacity is 0.105 on desktop and 0.075 on mobile. The backdrop frame budget is light: animate only transform and background-position; no blur animation; no layout-affecting properties in loops. The only continuously moving visual effects are grid-drift at 28s, the wordmark specular sweep at 7.2s, the optional floor gleam at 9.6s, and the rAF telemetry counter.\n\nC. THE LAYER STACK, back to front. html and body are #050505 at z-index auto. .backdrop is fixed inset:0 z-index:0 pointer-events:none overflow:hidden. Inside .backdrop: .stage-field z-index:0, .stage-grid z-index:1, .stage-noise svg z-index:2. .page is position:relative z-index:10 min-height:100dvh display:grid grid-template-rows:auto 1fr auto. header.site-header is z-index:30. main.hero is z-index:20. .wordmark-scene is z-index:20. The H1 .monolith-stack is position:relative z-index:3. The extrusion layers inside the H1 sit in ordered z-index values: .extrude[data-depth=\"12\"] z-index:1 through .extrude[data-depth=\"1\"] z-index:12, .top-face z-index:20, .specular-mask z-index:24, .letter spans within .top-face z-index:25. The reflection .reflection is z-index:1 beneath the wordmark but above the floor glow. .floor-glow is z-index:0. .hero-copy is z-index:25. footer.metrics is z-index:30. .mobile-menu-panel, when open, is fixed z-index:80. .grain-vignette is fixed z-index:100 pointer-events:none, but keep its opacity under 0.18 so text contrast remains readable.\n\nD. TOKEN TABLE. Define tokens in :root for the 1440px desktop default. Colors: --bg:#050505; --bg-2:#090908; --text:#f7f4ea; --text-soft:#d8d4c9; --muted:#98958b; --muted-2:#6f6c64; --accent:#f2f2e8; --accent-dim:#b9b6aa; --line:rgba(255,255,245,0.14); --line-strong:rgba(255,255,245,0.28); --line-soft:rgba(255,255,245,0.09); --black:#000000; --metal-1:#fbfaef; --metal-2:#dedacf; --metal-3:#a4a095; --metal-4:#4d4b46; --metal-5:#1d1c1a; --floor:rgba(242,242,232,0.13); --focus:#fff7c2. Type: --font-sans:\"Inter\", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; --font-serif:\"Instrument Serif\", Georgia, serif; --logo:16px; --nav:13px; --eyebrow:12px; --copy:16px; --stat:12.5px; --button:13px; --word:clamp(118px, 17.8vw, 258px); --word-track:-0.095em; --letter-depth:1.8px; --word-line:0.78; --small-caps-track:0.16em. Layout: --header-x:42px; --header-y:28px; --header-h:44px; --page-pad-x:42px; --hero-pad-top:22px; --hero-pad-bottom:36px; --hero-gap:26px; --copy-max:610px; --word-wrap-max:1380px; --footer-x:42px; --footer-y:28px; --metrics-gap:34px. Shapes: --radius-pill:999px; --radius-soft:14px; --button-h:40px; --nav-h:38px; --chip-h:30px; --focus-w:2px; --focus-offset:4px. Wordmark: --extrude-count:12; --extrude-x:3.8px; --extrude-y:4.4px; --top-shadow:0 1px 0 rgba(255,255,255,0.42), 0 18px 40px rgba(0,0,0,0.66); --body-shadow:0 34px 80px rgba(0,0,0,0.62); --reflection-y:21px; --reflection-blur:8px; --reflection-opacity:0.28; --reflection-height:0.72; --specular-w:32%; --specular-o:0.42. Motion: --ease-out:cubic-bezier(0.16,1,0.3,1); --ease-metal:cubic-bezier(0.65,0,0.35,1); --enter-dur:1.05s; --soft-dur:1.2s; --letter-dur:0.92s; --sweep-dur:7.2s; --grid-dur:28s; --gleam-dur:9.6s.\n\nE. THE COMPOSITION. First CSS rule must be exactly html, body { background: #050505 !important; color: #f7f4ea; } and the body tag must include style=\"background:#050505;color:#f7f4ea\" to prevent a white flash. Body uses margin:0; min-width:320px; font-family:var(--font-sans); text-rendering:geometricPrecision; -webkit-font-smoothing:antialiased. Page grid: .page { position:relative; z-index:10; min-height:100dvh; display:grid; grid-template-rows:auto minmax(0, 1fr) auto; } Desktop lock at >=901px sets html, body { height:100%; overflow:hidden; } and .page { height:100dvh; overflow:hidden; }. Header build order: create header.site-header with display:flex; align-items:center; justify-content:space-between; height:var(--header-h); padding:var(--header-y) var(--header-x) 0; gap:24px. The brand is an anchor with accessible name MONOLITH home and text MONOLITH, font-size var(--logo), font-weight 650, letter-spacing:0.18em, line-height:1, color:var(--text), text-decoration:none. Before the brand text include a 20px by 20px SVG mark: a vertical stone slab rectangle with x=5 y=2 width=10 height=16 rx=1.5 fill none stroke currentColor stroke-width=1.6 and a diagonal highlight path M8 15 L14 4 opacity 0.55. Header nav contains exactly three links: Work, Foundry, Contact. Each link is a pill built to the UI language below. Header right CTA is an anchor with text Request specimen. Mobile burger appears only at <=900px and is a button with aria-label=\"Open navigation\", aria-expanded toggled, and three 16px bars.\n\nHero build order: main.hero is display:grid; place-items:center; min-width:0; padding:var(--hero-pad-top) var(--page-pad-x) var(--hero-pad-bottom); overflow:visible. Inside it, .hero-inner is width:100%; max-width:var(--word-wrap-max); display:grid; justify-items:center; align-content:center; gap:var(--hero-gap); transform:translateY(-1.2vh) on desktop. Place a small chip above the wordmark: text VARIABLE TYPE SYSTEMS / 2026, height var(--chip-h), padding 0 13px, border 1px solid var(--line), border-radius var(--radius-pill), font-size var(--eyebrow), letter-spacing var(--small-caps-track), color var(--muted), background rgba(255,255,245,0.035). The H1 is visually the giant word only and contains the accessible text MONOLITH once. Structure: h1.monolith-title aria-label=\"MONOLITH\" with .monolith-stack aria-hidden=\"true\" containing exactly 13 visible stacked copies: 12 extrusion spans with class extrude and data-depth values 12 down to 1, then one .top-face. Every copy spells MONOLITH with exactly eight child spans class letter containing M, O, N, O, L, I, T, H. Do not add another h1 or duplicate readable h1 text outside aria-label. H1 CSS: margin:0; width:100%; max-width:var(--word-wrap-max); font-size:var(--word); line-height:var(--word-line); letter-spacing:var(--word-track); font-weight:900; font-family:var(--font-sans); text-align:center; text-transform:uppercase; white-space:nowrap; user-select:none. .monolith-stack is position:relative; display:block; min-height:0.78em; perspective:900px; transform-style:preserve-3d. Extrude layers are position:absolute; inset:0; display:flex; justify-content:center; color set per depth; transform:translate(calc(var(--depth) * var(--extrude-x)), calc(var(--depth) * var(--extrude-y))); text-shadow:0 1px 0 rgba(255,255,255,0.05); filter:brightness(var(--b)). Set depth colors explicitly from nearest to farthest: depth 1 #c9c5b9, 2 #aaa69b, 3 #8e8a80, 4 #747066, 5 #5d5a52, 6 #484640, 7 #37352f, 8 #292721, 9 #201e19, 10 #181611, 11 #11100d, 12 #0b0a08. .top-face is position:relative; display:flex; justify-content:center; color:transparent; background:linear-gradient(180deg, #fffdf2 0%, #e4e0d4 24%, #9d998d 54%, #4d4a43 100%); -webkit-background-clip:text; background-clip:text; text-shadow:var(--top-shadow). .top-face::after is the dark lower bevel: content:\"\"; position:absolute; left:0; right:0; bottom:-0.05em; height:0.14em; background:linear-gradient(180deg, rgba(255,255,255,0.10), rgba(0,0,0,0.52)); -webkit-background-clip:text; background-clip:text; color:transparent; pointer-events:none. .specular-mask is an absolutely positioned child overlay inset:-0.08em -0.04em 0; pointer-events:none; mix-blend-mode:screen; background:linear-gradient(115deg, transparent 0%, transparent 39%, rgba(255,255,255,0.0) 43%, rgba(255,255,255,var(--specular-o)) 50%, rgba(255,255,255,0.10) 54%, transparent 61%, transparent 100%); transform:translateX(-72%); clip-path:inset(0 0 0 0); -webkit-background-clip:text; background-clip:text; color:transparent; animation:specular-sweep var(--sweep-dur) var(--ease-metal) infinite; animation-delay:1.2s. Because background-clip:text cannot clip an empty overlay in all browsers, duplicate the MONOLITH letters inside .specular-mask with aria-hidden=\"true\" and identical letter spans.\n\nReflection build order: place .reflection immediately after the H1 in .wordmark-scene. It duplicates the top-face MONOLITH only, aria-hidden=\"true\", font settings identical to h1, transform:translateY(var(--reflection-y)) scaleY(calc(-1 * var(--reflection-height))); transform-origin:50% 0%; filter:blur(var(--reflection-blur)); opacity:var(--reflection-opacity); color:transparent; background:linear-gradient(180deg, rgba(255,255,245,0.28) 0%, rgba(255,255,245,0.12) 28%, rgba(255,255,245,0.035) 58%, transparent 100%); -webkit-background-clip:text; background-clip:text; mask-image:linear-gradient(180deg, rgba(0,0,0,0.58) 0%, rgba(0,0,0,0.26) 36%, transparent 78%); pointer-events:none. Add .floor-glow under it: width:min(82vw, 1120px); height:120px; margin-top:-42px; border-radius:50%; background:radial-gradient(ellipse at center, rgba(242,242,232,0.16) 0%, rgba(242,242,232,0.058) 32%, transparent 72%); filter:blur(18px); animation:floor-gleam var(--gleam-dur) ease-in-out infinite. Hero copy below the scene is exactly: Custom lettering for interfaces that need mass, silence, and impossible polish. It is a p.hero-copy with margin:0; max-width:var(--copy-max); color:var(--muted); font-size:var(--copy); line-height:1.65; text-align:center; letter-spacing:-0.012em. Footer metrics: footer.metrics contains nav-free small facts only, exactly three items in a ul: 01 Variable wordmarks, 02 Optical correction, 03 UI type systems. Footer CSS: padding:0 var(--footer-x) var(--footer-y); display:flex; justify-content:center; align-items:center. The ul is display:flex; gap:var(--metrics-gap); list-style:none; padding:0; margin:0; flex-wrap:wrap; justify-content:center. Each li uses font-size var(--stat), letter-spacing:0.13em, text-transform:uppercase, color:var(--muted-2). The numeric prefix is a span color var(--accent-dim), opacity 0.82, margin-right:8px.\n\nF. THE UI LANGUAGE. All clickable header pills share .pill. CSS: height:var(--nav-h); display:inline-flex; align-items:center; justify-content:center; padding:0 16px; border-radius:var(--radius-pill); border:1px solid rgba(255,255,245,0.12); color:var(--text-soft); text-decoration:none; font-size:var(--nav); font-weight:560; letter-spacing:-0.01em; line-height:1; background:linear-gradient(180deg, rgba(255,255,245,0.055) 0%, rgba(255,255,245,0.018) 100%); box-shadow:inset 0 1px 0 rgba(255,255,255,0.08), 0 12px 34px rgba(0,0,0,0.24); backdrop-filter:blur(12px); -webkit-backdrop-filter:blur(12px); position:relative; overflow:hidden; transition:transform 240ms var(--ease-out), border-color 240ms var(--ease-out), color 240ms var(--ease-out), background 240ms var(--ease-out). .pill::before is a 115deg shine: content:\"\"; position:absolute; inset:-1px; background:linear-gradient(115deg, transparent 0%, transparent 34%, rgba(255,255,255,0.16) 50%, transparent 66%, transparent 100%); transform:translateX(-130%); transition:transform 620ms var(--ease-out); pointer-events:none. .pill:hover transforms translateY(-1px), border-color rgba(255,255,245,0.30), color var(--text), background linear-gradient(180deg, rgba(255,255,245,0.095) 0%, rgba(255,255,245,0.032) 100%). .pill:hover::before transform:translateX(130%). .pill:active transform:translateY(0). The CTA .pill--primary uses border-color rgba(242,242,232,0.32), color #11110f, background linear-gradient(180deg, #fbfaef 0%, #d8d4c8 100%), box-shadow inset 0 1px 0 rgba(255,255,255,0.72), 0 16px 46px rgba(242,242,232,0.16); hover background linear-gradient(180deg, #ffffff 0%, #e6e2d6 100%). Focus: every a and button has :focus-visible outline var(--focus-w) solid var(--focus); outline-offset:var(--focus-offset); border-radius:var(--radius-pill). The chip uses the same material but never appears clickable. The mobile menu panel uses a dark glass sheet: background rgba(5,5,5,0.88); backdrop-filter blur(22px); border-top 1px solid var(--line); links height 56px, font-size 22px, letter-spacing -0.03em.\n\nG. MOTION. Resting opacity is always 1 on .appear elements; animations define from states only, and the page remains readable if animation fails. Apply .appear plus a modifier to each animated element. Stagger table: brand mark and word .appear--scale delay 0.06s; nav Work .appear--soft delay 0.14s; nav Foundry .appear--soft delay 0.20s; nav Contact .appear--soft delay 0.26s; header CTA .appear--scale delay 0.32s; chip .appear--pop delay 0.24s; extrusion group .appear--depth delay 0.30s; top-face letter M in-letter delay 0.44s; O delay 0.49s; N delay 0.54s; second O delay 0.59s; L delay 0.64s; I delay 0.69s; T delay 0.74s; H delay 0.79s; specular sweep begins delay 1.20s; reflection .appear--reflect delay 0.88s; hero copy .appear--soft delay 1.02s; metric 01 .appear--stat delay 1.10s; metric 02 delay 1.22s; metric 03 delay 1.34s. Keyframes: in-scale from opacity:0 transform:scale(0.88) to opacity:1 transform:none. in-soft from opacity:0 transform:translateY(14px) filter:blur(5px) to opacity:1 transform:none filter:blur(0). in-pop from 0% opacity:0 transform:scale(0.92), 68% opacity:1 transform:scale(1.035), 100% opacity:1 transform:scale(1). in-depth from opacity:0 transform:translateY(22px) rotateX(12deg) scale(0.985) to opacity:1 transform:none. in-letter from opacity:0 transform:translateY(0.42em) rotateX(72deg) scaleY(0.82); transform-origin:50% 100%; to opacity:1 transform:none. in-reflect from opacity:0 transform:translateY(calc(var(--reflection-y) + 22px)) scaleY(calc(-1 * var(--reflection-height))) to opacity:var(--reflection-opacity) transform:translateY(var(--reflection-y)) scaleY(calc(-1 * var(--reflection-height))). in-stat from opacity:0 transform:translateY(16px) to opacity:1 transform:none. specular-sweep keeps opacity 1 and animates transform from translateX(-76%) to translateX(76%) at 56%, then holds translateX(76%) until 100% so the shine pauses after each pass. grid-drift moves the grid by exactly 96px on both axes. floor-gleam animates opacity 0.52 at 0%, 0.78 at 50%, 0.52 at 100% and scaleX 0.96 to 1.04 to 0.96. Each animation uses animation-fill-mode:both and animation-timing-function:var(--ease-out). On animationend for each .appear element, add .is-in and set inline animation none only for that target through a class rule: .appear.is-in { opacity:1; transform:none; filter:none; clip-path:none; animation:none; }.\n\nH. RESPONSIVE TABLE. Defaults above are for 1440x900. For width >= 1600px set --header-x:56px; --header-y:34px; --logo:17px; --nav:14px; --nav-h:40px; --button:14px; --word:clamp(150px, 17.6vw, 316px); --copy:17px; --copy-max:660px; --word-wrap-max:1660px; --hero-gap:30px; --footer-x:56px; --footer-y:32px; --metrics-gap:42px; --extrude-x:4.4px; --extrude-y:5.0px; --reflection-blur:10px. For width >= 1920px set --header-x:72px; --header-y:38px; --logo:18px; --nav:14.5px; --nav-h:42px; --word:clamp(176px, 17.2vw, 360px); --copy:18px; --copy-max:710px; --word-wrap-max:1840px; --hero-gap:34px; --footer-y:36px; --metrics-gap:52px; --extrude-x:4.8px; --extrude-y:5.4px; --reflection-y:24px; --reflection-blur:11px. For 1280px to 1599px set --header-x:42px; --word:clamp(112px, 17.4vw, 238px); --copy-max:590px; --hero-gap:24px; --extrude-x:3.6px; --extrude-y:4.2px. For 901px to 1279px set --header-x:30px; --header-y:22px; --page-pad-x:30px; --footer-x:30px; --footer-y:22px; --nav:12.5px; --nav-h:36px; --button:12.5px; --logo:15px; --word:clamp(86px, 16.4vw, 188px); --copy:15px; --copy-max:540px; --hero-gap:20px; --metrics-gap:24px; --extrude-x:2.9px; --extrude-y:3.5px; --reflection-y:16px; --reflection-blur:7px. For desktop max-height <= 850px and width >= 901px set --header-y:20px; --hero-pad-bottom:22px; --hero-gap:18px; --word:clamp(82px, 15.8vw, 210px); --copy:14.5px; --footer-y:18px; .hero-inner transform:translateY(-0.4vh). For desktop max-height <= 720px and width >= 901px set --header-y:16px; --header-h:38px; --hero-pad-top:8px; --hero-pad-bottom:14px; --hero-gap:12px; --word:clamp(72px, 14.2vw, 174px); --copy:13.5px; --chip-h:26px; --eyebrow:11px; --footer-y:12px; --stat:11.5px; .reflection opacity:0.20; .floor-glow height:84px. For width <= 900px remove the desktop height lock: html, body { min-height:100%; overflow-x:hidden; } .page { min-height:100dvh; height:auto; overflow:hidden; grid-template-rows:auto minmax(560px, 1fr) auto; } tokens: --header-x:22px; --header-y:20px; --page-pad-x:22px; --footer-x:22px; --footer-y:24px; --header-h:42px; --logo:15px; --word:clamp(54px, 15.5vw, 118px); --word-track:-0.075em; --copy:15px; --copy-max:520px; --hero-pad-top:46px; --hero-pad-bottom:44px; --hero-gap:20px; --metrics-gap:18px; --extrude-x:2.1px; --extrude-y:2.7px; --reflection-y:13px; --reflection-blur:6px; --reflection-opacity:0.22. Hide desktop nav and desktop CTA, show burger. Mobile menu: closed visibility:hidden opacity:0 transform:translateY(-8px); open visibility:visible opacity:1 transform:none; transition 220ms var(--ease-out). For width <= 560px set --header-x:18px; --page-pad-x:18px; --word:clamp(43px, 14.8vw, 78px); --copy:14.5px; --copy-max:340px; --hero-pad-top:54px; --hero-pad-bottom:38px; --hero-gap:18px; --chip-h:28px; --eyebrow:10.5px; --stat:11px; --metrics-gap:13px; --extrude-x:1.35px; --extrude-y:1.8px; --reflection-y:10px; --reflection-blur:4px; .metrics ul flex-direction:column; align-items:center. For width <= 400px set --word:clamp(38px, 14.2vw, 58px); --word-track:-0.062em; --copy-max:310px; --page-pad-x:16px; --header-x:16px; --extrude-x:1.05px; --extrude-y:1.45px. At all widths ensure body, .page, header, main, footer, and .monolith-title use max-width:100%; box-sizing:border-box; and no element exceeds 100vw.\n\nI. FAILURE GUARDS. The .appear class must have opacity:1 as its resting declaration before animation names are applied; animation keyframes handle initial opacity so a failed animation never leaves content invisible. Add .no-motion class only if needed by script after fallback; do not depend on it for readability. JS fallback: after two requestAnimationFrame callbacks, inspect every .appear element with el.getAnimations({ subtree:false }); if none of its animations has playState \"running\" or \"finished\", add .is-in to every .appear and add .is-in to every .letter. If an element fires animationend, add .is-in to that element. If the browser lacks getAnimations, run the same .is-in fallback after the two rAFs. prefers-reduced-motion: reduce must set *, *::before, *::after { animation:none !important; transition:none !important; scroll-behavior:auto !important; } and force .appear, .appear.is-in, .letter, .reflection, .specular-mask, .stage-grid, .floor-glow { opacity:1; transform:none; filter:none; clip-path:none; } except .reflection must retain transform:translateY(var(--reflection-y)) scaleY(calc(-1 * var(--reflection-height))) and opacity:var(--reflection-opacity) so it remains visually correct. In reduced motion, hide the moving shine by setting .specular-mask { display:none; }. Maintain contrast: body text color #f7f4ea on #050505 and muted copy #98958b on #050505 exceed 4.5:1 for paragraph-sized text; do not lower opacity on text below 0.86. Add visible focus rings for all controls. Add a window error listener before running animation code and push String(event.message || event.error || \"unknown error\") to window.__DEMO__.errors. No console.log, console.warn, or console.error in the happy path.\n\nJ. THE EXACT JS. Keep the script as a small IIFE at the end of body. It must do only telemetry, animation fallback, and mobile menu behavior. First line inside the script sets window.__DEMO__ = { name: \"monolith-type\", ready: false, frames: 0, effects: [], errors: [] }; then push implemented effect names exactly once in this order: \"css-extruded-wordmark\", \"specular-sweep\", \"floor-reflection\", \"procedural-css-stage\", \"letter-cascade\", \"mobile-menu\". Add the error listener immediately after creating the object. Start a requestAnimationFrame loop named tick that increments window.__DEMO__.frames once per animation frame; do not perform DOM reads inside the loop. After the first tick has executed and the browser has had one paint opportunity, set window.__DEMO__.ready = true; use requestAnimationFrame(function(){ window.__DEMO__.ready = true; }) from inside the first tick or the immediately following scheduled frame. Cache document.documentElement, the menu button, the mobile panel, and all nav links. Burger behavior: clicking the burger toggles class menu-open on documentElement, updates aria-expanded to \"true\" or \"false\", updates aria-label to \"Close navigation\" or \"Open navigation\", and sets panel aria-hidden to the inverse. Escape closes the menu. Clicking any link inside the mobile panel closes the menu. Resizing to width > 900 closes the menu. Closing means remove menu-open, button aria-expanded=\"false\", button aria-label=\"Open navigation\", panel aria-hidden=\"true\". Animation behavior: add animationend listeners to every .appear and every .letter; on end add .is-in. Run the two-rAF fallback described above. The script must not create elements, must not fetch, must not use timers, must not write to localStorage, and must not log. Guard every optional element check so missing elements cannot throw.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the generated index.html contains exactly one h1, and the h1 exposes aria-label=\"MONOLITH\" while its visual wordmark is split into exactly eight .letter spans per visible word copy","there are exactly 12 extrusion layers plus one top-face layer for the MONOLITH wordmark, and the page contains a .specular-mask with an active specular-sweep CSS animation","window.__DEMO__ exists with name \"monolith-type\", ready becomes true after first paint, frames increments via requestAnimationFrame, and effects contains css-extruded-wordmark, specular-sweep, floor-reflection, procedural-css-stage, letter-cascade, and mobile-menu","at viewport widths 1440 and 1920, html/body and .page are locked to one 100dvh frame with overflow hidden; at 390px and 768px there is no horizontal overflow","the document performs no network requests except Google Fonts and contains no canvas, video, img, iframe, external media URLs, framework scripts, or third-party CDN assets","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"],"usage":{"build_tokens":28254,"build_prompt_tokens":8382,"build_completion_tokens":19872,"build_reasoning_tokens":37,"build_calls":1,"build_attempts":1,"build_latency_ms":54039,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":88944,"total_tokens":117198},"verification":{"passed":false,"passed_checks":17,"total_checks":18,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"30828 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"10 frames, effects: css-extruded-wordmark, specular-sweep, floor-reflection, procedural-css-stage, letter-cascade, mobile-menu"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":false,"detail":"8 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"M\nO\nN\nO\nL\nI\nT\nH\nM\nO\nN\nO\nL\nI\nT\nH\nM\nO\nN\nO\nL\nI\nT\nH\nM\nO\nN\nO\nL\nI\n\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Monolith Type — Monumental Letterforms"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=434 pageErrors=0"}]},"crosscheck":{"slug":"monolith-type","reviewer_model":"MiniMaxAI/MiniMax-M3","reviewer_platform":"cmdcode","verdict":"partial","confidence":"high","aesthetic_score":7,"requirements_met":["Single h1 with aria-label='MONOLITH'","12 extrusion spans with data-depth 12..1 plus top-face","Each extrusion has 8 letter spans M O N O L I T H","Specular mask overlay with duplicated letters and sweep animation","Reflection div with flipped/transformed text and floor-glow","Procedural backdrop (stage-field, stage-grid, stage-noise) with feTurbulence SVG","Hero copy text matches brief exactly","Footer metrics: 01 Variable wordmarks / 02 Optical correction / 03 UI type systems","Header has brand mark SVG, three nav links, Request specimen CTA","Mobile burger menu with aria-expanded/aria-label toggling","Token table in :root with required values","Desktop lock: html, body height 100%, overflow hidden at >=901px","Pill component with .pill::before shine and hover transform","Inter + Instrument Serif via Google Fonts preconnect","prefers-reduced-motion handling with .specular-mask display:none","window.__DEMO__ telemetry with effects and error listener","animationend adds .is-in class to .appear and .letter"],"requirements_missed":["Specular mask transform starts at translateX(-72%) but @keyframes specular-sweep keyframes go from -76% to 76% — initial transform not matching the keyframe start exactly",".top-face::after is supposed to be inside the text via background-clip:text but the rule sets color:transparent on the pseudo while the parent is also color:transparent; the bevel strip may not render as intended on a transparent parent","Reflection z-index:1 but reflection div is OUTSIDE h1.monolith-stack in the DOM; brief places it inside .wordmark-scene which is fine, but reflection does not duplicate the 12 extrusion layers (brief only requires top-face duplicate, so this is actually correct)","Header padding uses padding-top only (var(--header-y) var(--header-x) 0) but the brief specifies padding var(--header-y) var(--header-x) 0 — this matches, however the desktop lock does not include the height lock padding for chip rendering","Grid-drift keyframes use background-position but the .stage-grid already has background-position set — animation overrides it; this works but the 96px 96px shift in BOTH axes may cause visible jumpiness vs the requested 96px on both axes (correct per spec)","letter:nth-child(1) targets first letter of first extrusion span (M of depth 12), not letters within the top-face — so the in-letter cascade applies to the BACK extrusion letters (which are dark/hidden behind the top-face) rather than visible letters; the top-face letters use class .in-letter but rely on :nth-child for delay, meaning all extrusion letters cascade with the same delays as top-face — wasted/incorrect cascade",".stage-grid background-position keyframe goes from 'center calc(50% + 8px)' to 'calc(50% + 96px) calc(50% + 104px)' which is a 96px shift in both axes; but 104-8=96, so this is correct","Reflection opacity is 0.28 default and 0.20 at <=720px height — but on a 1024x640 viewport the desktop default 0.28 applies; per the screenshot the reflection appears very faint, which matches the brief"],"issues":[{"issue":"Letter cascade animation targets extrusion letter spans (behind top-face) rather than visible top-face letters, so the requested per-letter cascade is effectively invisible","severity":"high","evidence":".letter:nth-child(N){animation-delay:.44s...} and the visible top-face letters have class 'in-letter' but no per-child delay; extrusion letters at data-depth 12..2 cascade through but are occluded by .top-face which is z-index:20","fix_hint":"Scope the .letter:nth-child delay rules to .top-face .letter or apply delays only to top-face letters"},{"issue":"Stage grid background-size is 96px but background-position goes from 'center calc(50% + 8px)' — using 'center' as first value means the grid is centered horizontally with a vertical offset; this means grid-drift horizontal motion starts from a computed 'center' not from a deterministic pixel, causing the horizontal drift to be 0px (since center is auto-centered)","severity":"medium","evidence":"@keyframes grid-drift{from{background-position:center calc(50% + 8px)}to{background-position:calc(50% + 96px) calc(50% + 104px)}}","fix_hint":"Set background-position explicitly to pixel values (e.g. '0px calc(50% + 8px)') so the 96px horizontal shift is observable"},{"issue":"Specular mask initial transform translateX(-72%) does not match the keyframe start at translateX(-76%) — for the 1.2s delay the element sits at -72% before animation begins, then jumps to -76% when animation starts","severity":"low","evidence":".specular-mask{transform:translateX(-72%)} and @keyframes specular-sweep{0%{transform:translateX(-76%)...}","fix_hint":"Align the static transform with the 0% keyframe or set animation-delay to 0 with a different starting rule"},{"issue":"Reflection uses raw <span> children for letters but the brief asked for the reflection to duplicate the top-face MONOLITH with the same per-letter class; the reflection here has no .letter class on its spans, so any reflection-only styling (e.g. background-clip on individual letters) would not apply","severity":"low","evidence":"<div class=\"reflection...\"...><span>M</span><span>O</span>... — no class='letter' on these spans","fix_hint":"Add class='letter' to each reflection span for consistency"},{"issue":"Mobile menu panel has top:calc(var(--header-y) + var(--header-h) + 14px) but the brief only requires it to be 'a dark glass sheet' — position is implementation detail; however the panel lacks an explicit max-height and could overflow the viewport on small phones with long content","severity":"low","evidence":".mobile-menu-panel{position:fixed;z-index:80;left:0;right:0;top:calc(var(--header-y) + var(--header-h) + 14px);...}","fix_hint":"Add max-height:calc(100dvh - header - safe padding) and overflow:auto"},{"issue":"Screenshot shows the wordmark with visible staircase extrusion, top-face gradient, and faint reflection — composition matches the brief; chip and footer render correctly. The specular sweep is not visible in the static screenshot but the element is present in DOM","severity":"low","evidence":"1024x640 screenshot inspection: chip 'VARIABLE TYPE SYSTEMS / 2026' present, extruded MONOLITH with metallic top face, faint vertical reflection, three footer items, all nav pills present"}],"summary":"The composition, copy, token system, and procedural backdrop are faithfully implemented and the screenshot shows a convincing metallic extruded wordmark with the requested chrome. The main substantive defect is that the per-letter cascade animation is applied to extrusion-layer letters (which sit behind the top-face and are mostly hidden) rather than to the visible top-face letters, weakening the requested letter-by-letter entrance. Secondary issues are a mis-centered stage-grid background that prevents the 96px horizontal drift from being visible and a small transform/keyframe mismatch on the specular mask.","usage":{"input":19842,"output":1760,"total":21602,"reasoning":0,"cached":0},"latency_ms":29504}},{"slug":"nebula","title":"Nebula","category":"Landing Page","effect":"Precomputed volumetric nebula drift with 240 depth-sorted stars and restrained observatory copy.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/nebula/index.html","doc":"/prompts/nebula.md","prompt_text":"A. WHAT IS BEING BUILT\n\nCreate one file named index.html containing a complete, framework-free landing page for a fictional space-observatory company named “NEBULA”. Set the document title to “NEBULA — Deep Space Observatory”, set the document language to en, and include <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">. Build with semantic <header>, <nav>, <main>, and <footer> elements. Include exactly one <h1> and no additional h1 elements.\n\nThe page is a single-viewport cinematic observatory landing page. It must present a precomputed five-layer canvas nebula, 240 canvas stars, a centred dark readability scrim, compact navigation, one primary call to action, and a small telemetry footer. The visual target is deep black-indigo space with a violet cloud core, cyan lower-left fringe, warm rose right fringe, and crisp white-violet stars.\n\nAt widths of 901px and above, set html, body, and the page shell to width:100%; height:100dvh; min-height:100dvh; overflow:hidden. Do not permit document scrolling. At widths of 900px and below, allow vertical document scrolling and prevent horizontal overflow. The page must render without errors at 390x844, 768x1024, 1440x900, and 1920x1080.\n\nDo NOT build a dashboard, article, pricing page, form, modal, carousel, secondary page, login screen, account system, video background, image gallery, loading screen, onboarding sequence, cookie banner, external feed, or multi-section scrolling site. Do NOT use React, Tailwind, GSAP, Three.js, Lottie, SVG filters, CSS animation filters, remote images, external media, base64 image data, third-party scripts, or third-party stylesheets. The only permitted network request is an optional Google Fonts stylesheet; use Georgia as the required system fallback if it is unavailable. Do not generate clouds or stars through per-pixel work in the requestAnimationFrame loop.\n\nB. THE BACKDROP\n\nUse one fixed full-viewport <canvas id=\"space\" aria-hidden=\"true\"> as the visual backdrop. Render the nebula and stars in that single visible canvas. Do not expose any decorative visual as a focus target or duplicate it as DOM markup.\n\nCreate five offscreen canvas layers at startup. Use a seeded value-noise generator built from a 256-entry seeded permutation table and deterministic integer hash arithmetic. Each cloud layer must be generated once into its own OffscreenCanvas when supported, otherwise into a detached regular canvas. Each layer uses a logical canvas resolution equal to viewportWidth divided by 2 and viewportHeight divided by 2, clamped to a maximum width of 960 and maximum height of 540. Set each visible canvas backing-store resolution to viewportWidth multiplied by devicePixelRatio and viewportHeight multiplied by devicePixelRatio, capped at a pixel ratio of 2.\n\nGenerate the five cloud layers from this fixed layer set:\n\n1. Core violet: 2,048x1,152 offscreen canvas at 0.5 viewport scale, six octaves, base frequency 0.0042, amplitude 0.31, colour stops rgba(55,27,115,0), rgba(124,78,226,0.34), rgba(190,157,255,0.42), rgba(49,18,91,0).\n2. Lower cyan veil: 2,048x1,152 offscreen canvas at 0.5 viewport scale, five octaves, base frequency 0.0031, amplitude 0.24, colour stops rgba(17,55,91,0), rgba(30,164,192,0.22), rgba(117,235,230,0.28), rgba(9,39,72,0).\n3. Right rose cloud: 2,048x1,152 offscreen canvas at 0.5 viewport scale, five octaves, base frequency 0.0028, amplitude 0.22, colour stops rgba(94,27,70,0), rgba(224,91,138,0.19), rgba(255,182,191,0.22), rgba(75,18,62,0).\n4. Upper indigo fog: 2,048x1,152 offscreen canvas at 0.5 viewport scale, four octaves, base frequency 0.0022, amplitude 0.19, colour stops rgba(18,22,68,0), rgba(57,63,154,0.20), rgba(126,122,226,0.18), rgba(9,14,43,0).\n5. Dark central dust: 2,048x1,152 offscreen canvas at 0.5 viewport scale, four octaves, base frequency 0.0036, amplitude 0.27, colour stops rgba(4,5,19,0), rgba(5,7,24,0.38), rgba(1,3,13,0.44), rgba(0,0,0,0).\n\nThe 2,048x1,152 values describe the maximum precompute texture coordinate system. Actual texture dimensions are viewport-scaled by 0.5 and clamped by the maximum dimensions above. Do not recompute noise when the viewport changes; regenerate all five layers only on a debounced resize of 180ms, and only when the rendered canvas dimensions change.\n\nDraw the backdrop in this fixed order per frame: fill the full visible canvas with #03040c; draw the five cloud textures using source-over with globalAlpha values 0.94, 0.76, 0.68, 0.74, and 0.86; draw the star field; draw the central CSS scrim over the complete canvas; draw a 1px inner border. The cloud drift periods are 74s, 91s, 113s, 137s, and 157s. Each layer receives a fixed sinusoidal translation of no more than 18px horizontally and 12px vertically, plus a scale from 1.00 to 1.025. Translation is derived from elapsed milliseconds and is applied with ctx.setTransform or ctx.translate. Do not alter cloud pixels, filter state, or shadow state during the loop.\n\nGenerate exactly 240 star specks. Split them into 3 depth groups: 72 near stars with radius 0.7 to 1.3px and brightness 0.62 to 0.92; 112 middle stars with radius 0.45 to 0.9px and brightness 0.35 to 0.70; 56 far stars with radius 0.25 to 0.55px and brightness 0.20 to 0.48. Seed every position, radius, brightness, and drift phase deterministically. Draw the far group first, then the middle group, then the near group. Apply a near-group horizontal drift of 4px over 62s, a middle-group drift of 2px over 88s, and a far-group drift of 1px over 131s. Draw stars as filled circles with no shadowBlur and no ctx.filter. A star’s twinkle is opacity-only and uses its seeded phase with a 3.8s, 5.1s, or 7.4s period. The total per-frame work must remain below 12ms on a 1440x900 headless viewport. The loop may perform one canvas clear, one background fill, five precomputed drawImage calls, one 240-item star loop, and one scrim draw. Do not allocate arrays, objects, gradients, or strings inside the loop.\n\nC. THE LAYER STACK\n\nBuild the stacking context on the page shell with position:relative and isolation:isolate. Use these z-index values from back to front:\n\n- Backdrop canvas: z-index:0.\n- Backdrop vignette: z-index:1, pointer-events:none.\n- Readability scrim: z-index:2, pointer-events:none.\n- Content scrim accent: z-index:3, pointer-events:none.\n- Header: z-index:10.\n- Main hero: z-index:20.\n- Footer: z-index:30.\n- Focus ring treatment: z-index:40.\n- Reduced-motion notice: z-index:50, display:none by default.\n\nThe canvas must use position:fixed; inset:0; width:100%; height:100%; display:block. The page shell must have no transform, filter, or backdrop-filter that creates an unintended containing block for fixed positioning. Do not add a second canvas.\n\nD. TOKEN TABLE\n\nDefine these exact custom properties on :root and use them consistently:\n\n--ink: #03040c\n--ink-raised: #0a0b1c\n--text: #f5f3ff\n--text-soft: #c6c1df\n--text-muted: #918ba9\n--accent: #a78bfa\n--accent-bright: #ddd6fe\n--accent-deep: #6d4bd1\n--cyan: #75ebe6\n--rose: #ffb6bf\n--line: rgba(245,243,255,0.18)\n--line-strong: rgba(221,214,254,0.38)\n--scrim: rgba(3,4,12,0.56)\n--scrim-soft: rgba(3,4,12,0.28)\n--glass: rgba(12,10,31,0.48)\n--button-shadow: rgba(0,0,0,0.32)\n--font-display: Georgia, 'Times New Roman', serif\n--font-ui: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\n--page-padding-x: 40px\n--page-padding-top: 26px\n--page-padding-bottom: 22px\n--header-height: 42px\n--nav-gap: 26px\n--hero-max-width: 920px\n--hero-copy-width: 720px\n--eyebrow-size: 11px\n--eyebrow-tracking: 0.24em\n--title-size: clamp(58px, 7.2vw, 122px)\n--title-line: 0.88\n--title-weight: 400\n--title-spacing: -0.065em\n--lede-size: clamp(16px, 1.35vw, 21px)\n--lede-line: 1.55\n--lede-width: 590px\n--button-height: 48px\n--button-padding-x: 24px\n--button-radius: 999px\n--pill-height: 30px\n--pill-radius: 999px\n--footer-gap: 18px\n--footer-size: 10px\n--footer-tracking: 0.16em\n--scrim-center: rgba(3,4,12,0.70)\n--scrim-edge: rgba(3,4,12,0)\n--ease-ui: cubic-bezier(0.22, 1, 0.36, 1)\n--ease-drift: linear\n\nDo not introduce undocumented sizing or colour literals in CSS. The canvas paint may use the fixed colour literals specified in section B.\n\nE. THE COMPOSITION\n\nHeader is positioned absolute at top:0, left:0, right:0, height:var(--header-height), padding:var(--page-padding-top) var(--page-padding-x) 0, display:flex, align-items:center, justify-content:space-between. The brand is an anchor with accessible name “NEBULA home” and exact visible text “NEBULA”. Use the display font at 17px, letter-spacing:0.22em, colour:var(--text). Navigation contains exactly three links in this order: “Mission”, “Signals”, and “Contact”. The nav display:flex; gap:var(--nav-gap); align-items:center. Each link is colour:var(--text-soft), font-size:12px, letter-spacing:0.08em, text-decoration:none. Each link has a 4px focus-visible ring.\n\nMain is a full-viewport grid with min-height:100dvh, display:grid; place-items:center; padding:112px var(--page-padding-x) 92px; text-align:center. The hero is width:min(var(--hero-max-width), 100%); margin-inline:auto. The hero contains an eyebrow pill reading “DEEP SPACE OBSERVATORY / 01”, a single h1 with the exact two-line markup “NEBULA<br><span>IN THE DARK</span>”, a lede reading “We map the invisible structures between stars, turning quiet photons into a clearer view of the universe.”, and two controls. The controls are an anchor with visible text “Explore the signal” and a secondary anchor with visible text “Read our mission”. The title span uses colour:var(--accent-bright). The h1 uses the display font, font-size:var(--title-size), line-height:var(--title-line), font-weight:var(--title-weight), letter-spacing:var(--title-spacing), margin:18px 0 24px, text-wrap:balance. The lede uses width:min(var(--lede-width), 100%), margin:0 auto 30px, colour:var(--text-soft), font-size:var(--lede-size), line-height:var(--lede-line), text-wrap:balance. Controls use display:flex; justify-content:center; gap:12px; flex-wrap:wrap.\n\nFooter is absolute at bottom:0, left:0, right:0, padding:0 var(--page-padding-x) var(--page-padding-bottom), display:flex; align-items:center; justify-content:space-between; gap:var(--footer-gap); font-size:var(--footer-size), letter-spacing:var(--footer-tracking), text-transform:uppercase, colour:var(--text-muted). The left text is “NEBULA / OBSERVATORY SYSTEMS”. The right text is “LIVE FIELD WINDOW  ·  23° 41′ N”. A centred footer rule is not required. Do not place any other visible copy in the viewport.\n\nF. THE UI LANGUAGE\n\nThe primary anchor has height:var(--button-height), padding:0 var(--button-padding-x), display:inline-flex, align-items:center, justify-content:center, border-radius:var(--button-radius), background:linear-gradient(135deg, #c4b5fd 0%, var(--accent) 52%, #7c5ce2 100%), color:#100a22, font-size:12px, font-weight:700, letter-spacing:0.06em, text-decoration:none, box-shadow:0 10px 28px var(--button-shadow). Hover state changes transform:translateY(-2px), background:linear-gradient(135deg, #ede9fe 0%, #c4b5fd 52%, #a78bfa 100%), and box-shadow:0 14px 34px rgba(167,139,250,0.28). The secondary anchor has the same height, padding, radius, font-size, letter-spacing, and border treatment, with border:1px solid var(--line-strong), background:rgba(12,10,31,0.36), color:var(--text). Hover state changes background:rgba(167,139,250,0.14), border-color:var(--accent), and transform:translateY(-2px).\n\nThe eyebrow pill is display:inline-flex, align-items:center, height:var(--pill-height), padding:0 13px, border:1px solid var(--line), border-radius:var(--pill-radius), background:var(--glass), color:var(--accent-bright), font-size:var(--eyebrow-size), font-weight:700, letter-spacing:var(--eyebrow-tracking), line-height:1. Hover is not required because the pill is not a control. Every anchor must have a visible :focus-visible ring using outline:3px solid var(--accent-bright); outline-offset:4px. Do not remove native focus visibility.\n\nG. MOTION\n\nUse named CSS keyframes exactly as follows:\n\n@keyframes nebulaRise {\n  from { opacity:0; transform:translate3d(0,18px,0); }\n  to { opacity:1; transform:translate3d(0,0,0); }\n}\n@keyframes nebulaFade {\n  from { opacity:0; }\n  to { opacity:1; }\n}\n@keyframes signalPulse {\n  0%,100% { transform:scale(1); opacity:0.88; }\n  50% { transform:scale(1.04); opacity:1; }\n}\n\nApply nebulaFade with duration:1200ms, fill-mode:both, to the header, footer, and eyebrow. Apply nebulaRise with duration:1100ms, fill-mode:both, to the h1, lede, primary anchor, and secondary anchor. Use these delays:\n\nheader -> nebulaFade -> 80ms\nfooter -> nebulaFade -> 420ms\neyebrow -> nebulaFade -> 180ms\nh1 -> nebulaRise -> 300ms\nlede -> nebulaRise -> 460ms\nprimary anchor -> nebulaRise -> 620ms\nsecondary anchor -> nebulaRise -> 700ms\n\nDo not animate layout properties. Do not animate opacity below 0.2 after entrance completion. The canvas drift is controlled by JavaScript using elapsed time and transform-equivalent canvas translation, not CSS keyframes. On every frame, increment window.__DEMO__.frames exactly once while the rAF loop is active. Push these effect names into window.__DEMO__.effects in this order: \"precomputed-nebula\", \"depth-stars\", \"camera-drift\". Do not push CSS animation names.\n\nH. RESPONSIVE TABLE\n\nApply these exact overrides:\n\nBase, 901px and above:\n--page-padding-x:40px\n--page-padding-top:26px\n--header-height:42px\n--nav-gap:26px\n--title-size:clamp(58px,7.2vw,122px)\n--lede-size:clamp(16px,1.35vw,21px)\n--button-height:48px\n--footer-size:10px\n\nAt max-width:900px:\n--page-padding-x:24px\n--page-padding-top:20px\n--header-height:38px\n--nav-gap:14px\n--title-size:clamp(52px,11vw,88px)\n--lede-size:17px\n--button-height:46px\n--footer-size:9px\nSet main padding:104px var(--page-padding-x) 96px. Keep vertical document scrolling. Set header position:relative, footer position:relative, main min-height:auto. The page shell min-height:100dvh.\n\nAt max-width:560px:\n--page-padding-x:18px\n--page-padding-top:16px\n--header-height:34px\n--nav-gap:9px\n--title-size:clamp(46px,14vw,68px)\n--lede-size:16px\n--lede-line:1.48\n--button-height:44px\n--button-padding-x:19px\n--footer-size:8px\nSet header flex-wrap:wrap; row-gap:10px. Set nav gap:var(--nav-gap). Set main padding:94px var(--page-padding-x) 74px. Set footer flex-direction:column; align-items:flex-start; gap:8px.\n\nAt max-height:720px and min-width:901px:\n--page-padding-top:18px\n--page-padding-bottom:16px\n--title-size:clamp(50px,6.4vw,92px)\n--lede-size:16px\n--button-height:42px\nSet main padding-top:92px; padding-bottom:74px. Set footer padding-bottom:var(--page-padding-bottom).\n\nAt max-height:600px and min-width:901px:\n--page-padding-top:14px\n--page-padding-bottom:12px\n--title-size:clamp(44px,5.6vw,72px)\n--lede-size:15px\n--lede-line:1.4\n--button-height:38px\n--button-padding-x:18px\nSet main padding-top:72px; padding-bottom:58px. Set h1 margin-top:12px; margin-bottom:16px. Set lede margin-bottom:18px. Set footer-size:8px.\n\nI. FAILURE GUARDS\n\nSet all entrance elements to opacity:1 as their resting state. Apply entrance animation only with animation-delay and animation-fill-mode:both; after animation completion, the computed opacity must remain 1. If CSS animation is unavailable, content must remain visible. Implement a two-rAF fallback: after the first paint, requestAnimationFrame twice; if the telemetry loop has not incremented frames, start the normal loop from that callback and set ready:true only after the first successful paint. Never leave ready:false after two animation frames.\n\nInside the prefers-reduced-motion: reduce block, set *, *::before, *::after to animation-duration:0.001ms!important; animation-iteration-count:1!important; transition-duration:0.001ms!important; scroll-behavior:auto!important. Set .hero, header, footer, .eyebrow, h1, .lede, .controls to opacity:1!important; transform:none!important. In JavaScript, detect matchMedia('(prefers-reduced-motion: reduce)').matches. If true, render one static frame with elapsed=0, set ready:true after paint, push all three effect names once, and do not schedule another animation frame. Never omit the stars or nebula in reduced-motion mode.\n\nJ. THE EXACT JS\n\nPlace this script at the end of body. It must use the existing DOM structure, the canvas id “space”, and no external dependency:\n\n(() => {\n  const telemetry = {\n    name: 'nebula',\n    ready: false,\n    frames: 0,\n    effects: [],\n    errors: []\n  };\n  window.__DEMO__ = telemetry;\n\n  const canvas = document.getElementById('space');\n  const ctx = canvas.getContext('2d', { alpha: false });\n  const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n  const TAU = Math.PI * 2;\n  const MAX_DPR = 2;\n  const textureScale = 0.5;\n  const cloudCount = 5;\n  const starCount = 240;\n  const clouds = [];\n  const stars = [];\n  const permutation = new Uint8Array(256);\n  let seed = 173;\n  let width = 0;\n  let height = 0;\n  let dpr = 1;\n  let start = performance.now();\n  let raf = 0;\n  let resizeTimer = 0;\n\n  function random() {\n    seed = (seed * 1664525 + 1013904223) >>> 0;\n    return seed / 4294967296;\n  }\n\n  function hash(x, y) {\n    return permutation[(x & 255) + permutation[y & 255]] / 255;\n  }\n\n  function noise(x, y) {\n    const xi = Math.floor(x);\n    const yi = Math.floor(y);\n    const xf = x - xi;\n    const yf = y - yi;\n    const u = xf * xf * (3 - 2 * xf);\n    const v = yf * yf * (3 - 2 * yf);\n    const a = hash(xi, yi);\n    const b = hash(xi + 1, yi);\n    const c = hash(xi, yi + 1);\n    const d = hash(xi + 1, yi + 1);\n    return (a * (1-u) + b * u) * (1-v) + (c * (1-u) + d * u) * v;\n  }\n\n  for (let i = 0; i < 256; i += 1) permutation[i] = i;\n  for (let i = 255; i > 0; i -= 1) {\n    const j = Math.floor(random() * (i + 1));\n    const temp = permutation[i];\n    permutation[i] = permutation[j];\n    permutation[j] = temp;\n  }\n\n  function makeCanvas(w, h) {\n    const element = typeof OffscreenCanvas !== 'undefined'\n      ? new OffscreenCanvas(w, h)\n      : document.createElement('canvas');\n    element.width = w;\n    element.height = h;\n    return element;\n  }\n\n  function buildCloud(index) {\n    const w = Math.max(1, Math.min(960, Math.round(width * textureScale)));\n    const h = Math.max(1, Math.min(540, Math.round(height * textureScale)));\n    const layer = makeCanvas(w, h);\n    const lc = layer.getContext('2d');\n    const image = lc.createImageData(w, h);\n    const data = image.data;\n    const palettes = [\n      [[0,0,0,0],[55,27,115,0],[124,78,226,87],[190,157,255,107],[49,18,91,0]],\n      [[0,0,0,0],[17,55,91,0],[30,164,192,56],[117,235,230,71],[9,39,72,0]],\n      [[0,0,0,0],[94,27,70,0],[224,91,138,48],[255,182,191,56],[75,18,62,0]],\n      [[0,0,0,0],[18,22,68,0],[57,63,154,51],[126,122,226,46],[9,14,43,0]],\n      [[0,0,0,0],[4,5,19,0],[5,7,24,97],[1,3,13,112],[0,0,0,0]]\n    ];\n    const palette = palettes[index];\n    const octaves = [6,5,5,4,4][index];\n    const base = [0.0042,0.0031,0.0028,0.0022,0.0036][index];\n    for (let y = 0; y < h; y += 1) {\n      for (let x = 0; x < w; x += 1) {\n        let value = 0;\n        let amplitude = 1;\n        let frequency = base;\n        let total = 0;\n        for (let octave = 0; octave < octaves; octave += 1) {\n          value += noise(x * frequency, y * frequency) * amplitude;\n          total += amplitude;\n          amplitude *= 0.54;\n          frequency *= 2.03;\n        }\n        value /= total;\n        const p = Math.max(0, Math.min(0.999, (value - 0.31) / 0.42));\n        const band = p * 4;\n        const low = Math.min(3, Math.floor(band));\n        const mix = band - low;\n        const a = palette[low];\n        const b = palette[low + 1];\n        const i = (y * w + x) * 4;\n        data[i] = a[0] + (b[0] - a[0]) * mix;\n        data[i + 1] = a[1] + (b[1] - a[1]) * mix;\n        data[i + 2] = a[2] + (b[2] - a[2]) * mix;\n        data[i + 3] = a[3] + (b[3] - a[3]) * mix;\n      }\n    }\n    lc.putImageData(image, 0, 0);\n    clouds[index] = { layer, period: [74000,91000,113000,137000,157000][index], alpha: [0.94,0.76,0.68,0.74,0.86][index] };\n  }\n\n  function buildStars() {\n    stars.length = 0;\n    for (let i = 0; i < starCount; i += 1) {\n      const group = i < 72 ? 0 : i < 184 ? 1 : 2;\n      const radius = group === 0 ? 0.7 + random() * 0.6 : group === 1 ? 0.45 + random() * 0.45 : 0.25 + random() * 0.30;\n      const brightness = group === 0 ? 0.62 + random() * 0.30 : group === 1 ? 0.35 + random() * 0.35 : 0.20 + random() * 0.28;\n      stars.push({ x: random(), y: random(), radius, brightness, group, phase: random() * TAU, period: group === 0 ? 3800 : group === 1 ? 5100 : 7400 });\n    }\n  }\n\n  function resize() {\n    width = Math.max(1, window.innerWidth);\n    height = Math.max(1, window.innerHeight);\n    dpr = Math.min(MAX_DPR, window.devicePixelRatio || 1);\n    canvas.width = Math.round(width * dpr);\n    canvas.height = Math.round(height * dpr);\n    canvas.style.width = width + 'px';\n    canvas.style.height = height + 'px';\n    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n    clouds.length = 0;\n    for (let i = 0; i < cloudCount; i += 1) buildCloud(i);\n    buildStars();\n  }\n\n  function render(elapsed) {\n    const t = reduced ? 0 : elapsed;\n    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n    ctx.globalAlpha = 1;\n    ctx.fillStyle = '#03040c';\n    ctx.fillRect(0, 0, width, height);\n    for (let i = 0; i < clouds.length; i += 1) {\n      const cloud = clouds[i];\n      const phase = t / cloud.period * TAU + i * 0.73;\n      const x = Math.sin(phase) * (12 + i * 1.5);\n      const y = Math.cos(phase * 0.91) * (7 + i);\n      const scale = 1 + (Math.sin(phase * 0.63) * 0.5 + 0.5) * 0.025;\n      ctx.save();\n      ctx.globalAlpha = cloud.alpha;\n      ctx.translate(width / 2 + x, height / 2 + y);\n      ctx.scale(scale, scale);\n      ctx.drawImage(cloud.layer, -width / 2, -height / 2, width, height);\n      ctx.restore();\n    }\n    for (let i = 0; i < stars.length; i += 1) {\n      const star = stars[i];\n      const drift = (star.group === 0 ? 4 : star.group === 1 ? 2 : 1) * Math.sin(t / (62000 + star.group * 33000) * TAU + star.phase);\n      const twinkle = 0.72 + 0.28 * Math.sin(t / star.period * TAU + star.phase);\n      ctx.globalAlpha = star.brightness * twinkle;\n      ctx.fillStyle = star.group === 0 ? '#f5f3ff' : star.group === 1 ? '#c4b5fd' : '#75ebe6';\n      ctx.beginPath();\n      ctx.arc(star.x * width + drift, star.y * height, star.radius, 0, TAU);\n      ctx.fill();\n    }\n    ctx.globalAlpha = 1;\n    const scrim = ctx.createRadialGradient(width / 2, height / 2, 0, width / 2, height / 2, Math.max(width, height) * 0.72);\n    scrim.addColorStop(0, 'rgba(3,4,12,0.70)');\n    scrim.addColorStop(0.48, 'rgba(3,4,12,0.46)');\n    scrim.addColorStop(1, 'rgba(3,4,12,0.04)');\n    ctx.fillStyle = scrim;\n    ctx.fillRect(0, 0, width, height);\n    ctx.strokeStyle = 'rgba(245,243,255,0.10)';\n    ctx.strokeRect(0.5, 0.5, width - 1, height - 1);\n  }\n\n  function loop(now) {\n    telemetry.frames += 1;\n    render(now - start);\n    if (!reduced) raf = requestAnimationFrame(loop);\n  }\n\n  window.addEventListener('error', event => {\n    telemetry.errors.push(String(event.message || 'window error'));\n  });\n\n  window.addEventListener('resize', () => {\n    window.clearTimeout(resizeTimer);\n    resizeTimer = window.setTimeout(resize, 180);\n  });\n\n  resize();\n  start = performance.now();\n  render(0);\n  telemetry.effects.push('precomputed-nebula', 'depth-stars', 'camera-drift');\n  requestAnimationFrame(() => {\n    requestAnimationFrame(() => {\n      telemetry.ready = true;\n      if (!reduced) loop(start);\n    });\n  });\n})();\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["A headless browser at 1440x900 finds document.documentElement.scrollHeight <= 900 and document.documentElement.scrollWidth <= 1440.","The page contains exactly one h1, semantic header/nav/main/footer elements, and the exact visible strings “NEBULA”, “IN THE DARK”, “Explore the signal”, and “Read our mission”.","After load, window.__DEMO__.name is “nebula”, window.__DEMO__.ready is true, and window.__DEMO__.effects contains “precomputed-nebula”, “depth-stars”, and “camera-drift”.","At 390x844, 768x1024, 1440x900, and 1920x1080, document.documentElement.scrollWidth equals or is less than the viewport width and all visible controls have accessible names.","With prefers-reduced-motion: reduce, the canvas remains painted, the hero text has computed opacity 1, and window.__DEMO__.frames does not increase after the initial static paint.","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"],"usage":{"build_tokens":52834,"build_prompt_tokens":7997,"build_completion_tokens":44837,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":3,"build_latency_ms":624574,"wasted_tokens":79994,"repair_tokens":23287,"crosscheck_tokens":21422,"total_tokens":97543},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"22275 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"32 frames, effects: precomputed-nebula, depth-stars, camera-drift"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"47 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 23.31, stdev 12.66, coverage 1"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 4076514205 -> 1559681144"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"NEBULA\nIN THE DARK\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"NEBULA — Deep Space Observatory"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=280 pageErrors=0"}]},"crosscheck":{"slug":"nebula","reviewer_model":"hy3","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":6,"requirements_met":["Document title 'NEBULA — Deep Space Observatory' set","lang='en' and viewport meta present","Single h1 with 'NEBULA<br><span>IN THE DARK</span>' markup","Semantic header/nav/main/footer landmarks","Full token table implemented exactly as specified","Five cloud layers with correct octaves, base frequencies, amplitude, and palette arrays","240 stars split into 72/112/56 depth groups with seeded properties","OffscreenCanvas with detached fallback for cloud generation","Layered canvas draw order: bg fill → 5 clouds → stars → scrim → inner border","Cloud drift periods 74/91/113/137/157 s with translation ≤18px/12px and scale 1.00–1.025","Star drift periods 62s/88s/131s with twinkle periods 3.8s/5.1s/7.4s","Telemetry object with ready, frames, effects, errors populated","Effects pushed as 'precomputed-nebula', 'depth-stars', 'camera-drift'","nebulaFade/nebulaRise keyframes with exact durations and delays (80/180/300/420/460/620/700 ms)","Exact copy: 'DEEP SPACE OBSERVATORY / 01', lede, footer strings","Primary/secondary anchor hover states match brief gradients and shadows","Responsive breakpoints at 900px, 560px, 720px height, 600px height implemented","prefers-reduced-motion block sets opacity:1!important and disables animations","Visible :focus-visible outline 3px var(--accent-bright) with 4px offset"],"requirements_missed":["Nebula visual target is very dim in the rendered output — violet core, cyan lower-left fringe, and warm rose right fringe are barely perceptible against the dark background","Stars appear sparse in the screenshot relative to the stated 'crisp white-violet stars' target, partially obscured by stacked scrim layers","Two-rAF fallback is non-strict: the rAF loop is started immediately at the end of the script AND a separate double-rAF fallback exists, rather than the loop starting only inside the fallback as the brief specifies"],"issues":[{"issue":"Rendered nebula is washed out — the specified violet core, cyan fringe and rose fringe are visually faint","severity":"medium","evidence":"Screenshot shows a near-monochrome dark indigo background with only a faint violet haze at the top; no discernible cyan or rose zones"},{"issue":"CSS scrim stack (vignette + readability-scrim + content-scrim-accent) is layered on top of the canvas-drawn scrim, compounding darkening and further suppressing nebula contrast","severity":"medium","evidence":".backdrop-vignette, .readability-scrim, .content-scrim-accent all use rgba(3,4,12,*) stops combined with the JS canvas scrim rgba(3,4,12,0.70→0.04)"},{"issue":"rAF loop is unconditionally scheduled outside the two-rAF fallback, breaking the brief's prescribed flow where the loop starts inside the fallback when frames==0","severity":"low","evidence":"Final lines: `if (!reduced) raf = requestAnimationFrame(loop);` runs alongside the nested double-rAF block that also conditionally calls loop()"},{"issue":"Star count visible in the rendered frame is sparse and lacks the 'crisp white-violet' punch the brief calls for","severity":"medium","evidence":"Screenshot shows roughly a dozen discernible stars across 1024x640; near-group 4px drift is overshadowed by darkening layers"},{"issue":"Redundant `animation: none;` declaration in :hover rules","severity":"low","evidence":".button-primary:hover and .button-secondary:hover both set `animation: none;` despite no animation being applied to begin with"}],"summary":"Composition, copy, tokens, motion choreography and responsive table are implemented faithfully and the page reads as a polished serif-driven observatory hero — but the cumulative canvas-plus-CSS scrim stack crushes the nebula's chromatic target, leaving the backdrop looking flat and the star field under-served. The two-rAF fallback is also structurally off-spec because the loop is started unconditionally outside the fallback path.","usage":{"input":16160,"output":5262,"total":21422,"reasoning":4224,"cached":128},"latency_ms":42721}},{"slug":"prism","title":"Prism","category":"Landing Page","effect":"A white beam enters an SVG triangular prism and fans into seven breathing spectrum wedges.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/prism/index.html","doc":"/prompts/prism.md","prompt_text":"A. BUILD TARGET\nDeliverable is exactly one file: index.html. It is a complete, valid, standalone HTML5 document. No build step, no bundler, no framework. Document title is literally: PRISM — Precision Dispersion Optics. The html element carries lang=\"en\". The viewport meta is: width=device-width, initial-scale=1. The single-viewport rule: at min-width 901px, html and body both receive height:100dvh; overflow:hidden; margin:0. At max-width 900px, body receives min-height:100dvh; overflow-y:auto; overflow-x:hidden. The page is a single-viewport cinematic landing page for PRISM, an optics company. The scene: a thin white beam enters a triangular glass prism from the left and disperses on the right into seven spectrum wedges. The copy block sits to the left of the prism; the hero is NOT centred. Do NOT build: scroll-snap sections, a second scroll viewport, centred hero copy, any <img>, <video>, <picture>, <iframe>, <object>, base64 photo blobs, remote image URLs, third-party CDN assets, CSS @import, GSAP, Three.js, Lottie, React, Tailwind, horizontal scrollbars at any tested width, or any element that rests at opacity:0 in the base CSS. Every visible control has an accessible name. There is exactly one <h1>. Real <header>, <nav>, <main>, and <footer> elements are present.\n\nB. THE BACKDROP\nOne <canvas id=\"grain\"> is the only canvas in the document. Element count for the backdrop: 1 canvas, 1 div.bg-drift, 1 body::after vignette. The canvas sits at z-index:0 and is sized by CSS to width:100%; height:100%; display:block. Canvas resolution trick: on init and on resize (both outside the rAF loop), read canvas.offsetWidth and canvas.offsetHeight once. Set canvas.width = Math.round(offsetWidth * 0.5) and canvas.height = Math.round(offsetHeight * 0.5). Do NOT use devicePixelRatio; the half-resolution backing store upscaled to full CSS size is the fill-rate trick. Draw exactly once per size, never per frame. Draw order on the 2D context: first a radial gradient centred at 70% 50% with inner colour rgba(138,180,255,0.10) at stop 0 and rgba(5,7,13,0) at stop 1 with radius Math.max(w,h)*0.8; then an ImageData noise pass where every 4th byte (alpha) is 8 and RGB bytes are a random 0-255 integer, applied via putImageData over the whole half-res surface. ctx.filter and ctx.shadowBlur are never called; there is no per-item canvas loop. Blur radii: div.bg-drift is a static layer with background: linear-gradient(105deg, transparent 0%, rgba(138,180,255,0.06) 45%, transparent 90%) and filter: blur(30px) applied once at parse time. body background is linear-gradient(180deg, #05070d 0%, #080b14 100%). Drift period: div.bg-drift animates transform: translate3d(-2%,0,0) to translate3d(2%,0,0) over 24s linear infinite alternate. Per-frame cost budget: JS execution <= 1.5ms; zero layout reads inside the loop; compositor paint <= 4ms; total rAF frame budget <= 8ms, guaranteeing the >=20 frames per 1.2s headless smoke threshold at 1440x900 with large headroom.\n\nC. THE LAYER STACK (back to front, z-index values)\nz-index:0 — #grain canvas (static noise + vignette gradient).\nz-index:1 — div.bg-drift (drifting accent wash).\nz-index:2 — div.beam (incoming white beam bar) and div.beam-highlight (travelling highlight child).\nz-index:3 — div.fan (dispersion container); inside it, div.wedge:nth-child(1) through (7) receive z-index:31 through 37 respectively.\nz-index:4 — svg.prism (glass triangle polygon with inner glow gradient).\nz-index:5 — polyline.specular (1px non-scaling specular edge over the prism).\nz-index:6 — div.content, which contains header, main.hero, and footer.\nz-index:7 — a:focus-visible outline ring (outline, not a box, so it paints above content without a new stacking context).\n\nD. TOKEN TABLE (every size and colour)\n--bg-0:#05070d; --bg-1:#080b14; --accent:#8ab4ff; --accent-glow:rgba(138,180,255,0.18); --text:#e6edf7; --text-dim:#93a2bd; --white:#ffffff; --spec:rgba(255,255,255,0.70). Wedge hues: --w1:#ff4d4d; --w2:#ff9f43; --w3:#ffe14d; --w4:#4dff88; --w5:#4de1ff; --w6:#4d6bff; --w7:#b44dff. Sizes: --prism-w:220px; --prism-h:190px; --beam-w:46vw; --beam-h:2px; --wedge-w:52vw; --wedge-h:6px; --pad-x:48px; --pad-y:32px; --gap:24px; --h1:clamp(2.5rem,5vw,4rem); --fs-p:1.0625rem; --radius:999px. Blur radii (one per wedge): --blur-1:4px; --blur-2:6px; --blur-3:8px; --blur-4:7px; --blur-5:5px; --blur-6:9px; --blur-7:6px. Opacity tokens: --wedge-op-1:0.85; --wedge-op-2:0.80; --wedge-op-3:0.90; --wedge-op-4:0.82; --wedge-op-5:0.88; --wedge-op-6:0.78; --wedge-op-7:0.84.\n\nE. THE COMPOSITION\nHeader: <header> is display:grid; grid-template-columns:auto 1fr auto; padding:var(--pad-y) var(--pad-x); gap:var(--gap). Child 1: <a class=\"brand\" href=\"#top\" aria-label=\"PRISM home\">PRISM</a>. Child 2: <nav aria-label=\"Primary\"> containing four links: <a href=\"#optics\">Optics</a>, <a href=\"#dispersion\">Dispersion</a>, <a href=\"#lab\">Lab</a>, <a href=\"#contact\">Contact</a>. Main hero: <main id=\"top\" class=\"hero\"> is display:grid; grid-template-columns:minmax(320px,38vw) 1fr; align-items:center; gap:48px; padding:0 var(--pad-x). Left column div.copy: <h1>Light, divided.</h1>; <p class=\"lede\">PRISM is an optics studio engineering dispersion-grade glass for spectrometry, imaging, and research.</p>; <a class=\"btn\" href=\"#contact\">Request a quote</a>. Right column div.scene holds the beam, fan, prism, and specular. Footer: <footer> is display:flex; justify-content:space-between; align-items:center; padding:var(--pad-y) var(--pad-x). Left text node: \"© 2025 PRISM Optics. All rights reserved.\" Right text node: \"Precision · Dispersion · Clarity\". SVG prism: inline <svg class=\"prism\" width=\"var(--prism-w)\" height=\"var(--prism-h)\" viewBox=\"0 0 220 190\" aria-hidden=\"true\"> containing <defs><radialGradient id=\"glass\" cx=\"50%\" cy=\"40%\" r=\"60%\"><stop offset=\"0%\" stop-color=\"rgba(255,255,255,0.14)\"/><stop offset=\"60%\" stop-color=\"rgba(138,180,255,0.08)\"/><stop offset=\"100%\" stop-color=\"rgba(255,255,255,0.02)\"/></radialGradient></defs>, then <polygon points=\"10,180 110,10 210,180\" fill=\"url(#glass)\"/>. Specular: <polyline class=\"specular\" points=\"10,180 110,10 210,180\" fill=\"none\" stroke=\"var(--spec)\" stroke-width=\"1\" vector-effect=\"non-scaling-stroke\"/>. Beam: <div class=\"beam\"><div class=\"beam-highlight\"></div></div>. Fan: <div class=\"fan\"> with seven <div class=\"wedge\" data-i=\"1\"></div> ... up to 7.\n\nF. THE UI LANGUAGE\n.btn treatment: display:inline-block; background:linear-gradient(180deg,#8ab4ff,#5a8dee); color:#05070d; padding:14px 28px; border-radius:var(--radius); font-weight:600; text-decoration:none; font-size:1rem; box-shadow:0 6px 20px rgba(90,141,238,0.35); transition:transform 160ms ease, filter 160ms ease. .btn:hover { transform:translateY(-1px); filter:brightness(1.08); } .btn:active { transform:translateY(0); } .btn:focus-visible, a:focus-visible { outline:2px solid var(--accent); outline-offset:3px; } .brand { font-weight:700; letter-spacing:0.18em; color:var(--text); text-decoration:none; } nav a { color:var(--text-dim); text-decoration:none; font-size:0.9375rem; margin-left:20px; transition:color 160ms ease, text-shadow 160ms ease; } nav a:hover { color:var(--white); text-shadow:0 0 12px rgba(138,180,255,0.6); } .lede { color:var(--text-dim); font-size:var(--fs-p); line-height:1.6; max-width:34ch; margin:0 0 28px; } h1 { font-size:var(--h1); line-height:1.05; margin:0 0 20px; color:var(--white); font-weight:600; letter-spacing:-0.02em; }\n\nG. MOTION — STAGGER TABLE AND KEYFRAMES\nElement -> animation -> delay:\n.brand -> floatIn -> 0ms\nnav a:nth-child(1) -> floatInRight -> 80ms\nnav a:nth-child(2) -> floatInRight -> 160ms\nnav a:nth-child(3) -> floatInRight -> 240ms\nnav a:nth-child(4) -> floatInRight -> 320ms\n.hero h1 -> fadeUp -> 240ms\n.hero .lede -> fadeUp -> 320ms\n.btn -> fadeUp -> 400ms\nfooter -> fadeIn -> 560ms\n.beam -> drawBeam -> 0ms\n.wedge[data-i=\"1\"] -> fanOut -> 200ms\n.wedge[data-i=\"2\"] -> fanOut -> 270ms\n.wedge[data-i=\"3\"] -> fanOut -> 340ms\n.wedge[data-i=\"4\"] -> fanOut -> 410ms\n.wedge[data-i=\"5\"] -> fanOut -> 480ms\n.wedge[data-i=\"6\"] -> fanOut -> 550ms\n.wedge[data-i=\"7\"] -> fanOut -> 620ms\n.fan -> fanBreathe -> 0ms (duration 12s, infinite)\n.beam-highlight -> beamTravel -> 0ms (duration 2.4s, linear infinite)\n.prism -> glowPulse -> 0ms (duration 12s, infinite)\nNamed keyframes:\n@keyframes floatIn { from { opacity:0; transform:translateY(-8px); } to { opacity:1; transform:none; } }\n@keyframes floatInRight { from { opacity:0; transform:translateX(-10px); } to { opacity:1; transform:none; } }\n@keyframes fadeUp { from { opacity:0; transform:translateY(12px); } to { opacity:1; transform:none; } }\n@keyframes fadeIn { from { opacity:0; } to { opacity:1; } }\n@keyframes drawBeam { from { transform:scaleX(0); transform-origin:left center; } to { transform:scaleX(1); transform-origin:left center; } }\n@keyframes fanOut { from { opacity:0; transform:scaleX(0.6) skewY(0deg); } to { opacity:0.88; transform:scaleX(1) skewY(-8deg); } }\n@keyframes fanBreathe { 0%,100% { transform:skewY(-6deg) scaleX(0.98); } 50% { transform:skewY(6deg) scaleX(1.02); } }\n@keyframes beamTravel { 0% { transform:translateX(-60%); } 100% { transform:translateX(160%); } }\n@keyframes glowPulse { 0%,100% { opacity:0.9; } 50% { opacity:1; } }\n\nH. RESPONSIVE TABLE (token overrides per breakpoint, including two max-height breakpoints)\n| Breakpoint / Condition | Overrides |\n| 1920x1080 base | --prism-w:220px; --prism-h:190px; --beam-w:46vw; --wedge-w:52vw; --pad-x:48px; --h1:clamp(2.5rem,5vw,4rem); |\n| 1440x900 | --prism-w:200px; --prism-h:172px; --beam-w:44vw; --wedge-w:50vw; --pad-x:40px; --h1:3.5rem; |\n| 768x1024 | .hero { grid-template-columns:1fr; } --prism-w:180px; --prism-h:154px; --beam-w:60vw; --wedge-w:66vw; --pad-x:32px; --h1:3rem; --gap:32px; |\n| 390x844 | --prism-w:150px; --prism-h:128px; --beam-w:70vw; --wedge-w:76vw; --pad-x:20px; --pad-y:24px; --h1:2.25rem; --gap:16px; .lede { max-width:100%; } |\n| max-height:780px | --prism-w:180px; --prism-h:150px; --wedge-h:5px; |\n| max-height:640px | --prism-w:150px; --prism-h:124px; --beam-h:1px; --wedge-h:4px; --pad-y:16px; |\nAt <=900px, body regains vertical scroll; all above overrides still apply and no horizontal overflow is permitted.\n\nI. FAILURE GUARDS\nResting opacity: every element in the base CSS has opacity:1. Animations are attached only under .is-ready (added by JS) and use animation-fill-mode:both, so if JS never runs the page is fully readable and static. Two-rAF fallback: if window.requestAnimationFrame is undefined, polyfill it as window.requestAnimationFrame = function(cb){ return setTimeout(function(){ cb(Date.now()); }, 16); }; and window.cancelAnimationFrame = function(id){ clearTimeout(id); }. Reduced motion: a @media (prefers-reduced-motion: reduce) block sets *, *::before, *::after { animation:none !important; transition:none !important; }. JS reads matchMedia(\"(prefers-reduced-motion: reduce)\").matches into a variable reduce. If reduce is true, JS still sets ready=true and runs the rAF loop only to increment the telemetry frames counter, but applies zero style updates, leaving the composed scene static. No console method is ever called; no uncaught exceptions are thrown.\n\nJ. THE EXACT JS\nInsert the following script at the end of <body>, with no template literals and no inner backticks:\n<script>\n(function(){\n  var D = window.__DEMO__ = { name:'prism', ready:false, frames:0, effects:[], errors:[] };\n  window.addEventListener('error', function(e){ D.errors.push(String(e.message)); });\n  var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n  if (!window.requestAnimationFrame) {\n    window.requestAnimationFrame = function(cb){ return setTimeout(function(){ cb(Date.now()); }, 16); };\n    window.cancelAnimationFrame = function(id){ clearTimeout(id); };\n  }\n  function drawGrain(c){\n    var w = c.width = Math.round(c.offsetWidth * 0.5);\n    var h = c.height = Math.round(c.offsetHeight * 0.5);\n    var x = c.getContext('2d');\n    x.clearRect(0,0,w,h);\n    var g = x.createRadialGradient(w*0.70, h*0.50, 0, w*0.70, h*0.50, Math.max(w,h)*0.80);\n    g.addColorStop(0, 'rgba(138,180,255,0.10)');\n    g.addColorStop(1, 'rgba(5,7,13,0)');\n    x.fillStyle = g;\n    x.fillRect(0,0,w,h);\n    var id = x.createImageData(w,h);\n    var d = id.data;\n    for (var i=0; i<d.length; i+=4) {\n      var v = (Math.random()*255)|0;\n      d[i] = v; d[i+1] = v; d[i+2] = v; d[i+3] = 8;\n    }\n    x.putImageData(id,0,0);\n  }\n  function init(){\n    var c = document.getElementById('grain');\n    if (c && c.offsetWidth) { D.effects.push('canvas-grain'); drawGrain(c); }\n    D.effects.push('beam-travel');\n    D.effects.push('fan-breathe');\n    D.effects.push('prism-glow');\n    document.documentElement.classList.add('is-ready');\n    requestAnimationFrame(function loop(){\n      D.frames++;\n      requestAnimationFrame(loop);\n    });\n    requestAnimationFrame(function(){\n      requestAnimationFrame(function(){ D.ready = true; });\n    });\n  }\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', init);\n  } else { init(); }\n  window.addEventListener('resize', function(){\n    var c = document.getElementById('grain');\n    if (c && c.offsetWidth) { drawGrain(c); }\n  });\n})();\n</script>\n\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["window.__DEMO__.ready === true after the load event and two animation frames have elapsed.","window.__DEMO__.effects contains exactly 'canvas-grain', 'beam-travel', 'fan-breathe', and 'prism-glow'.","The document contains exactly one h1, and header, nav, main, and footer elements all exist.","At 1440x900 and 390x844 viewport widths, document.documentElement.scrollWidth is <= window.innerWidth (no horizontal overflow).","After 1.2 seconds of runtime at 1440x900, window.__DEMO__.frames is >= 20 and window.__DEMO__.errors.length === 0.","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"],"usage":{"build_tokens":46858,"build_prompt_tokens":4991,"build_completion_tokens":41867,"build_reasoning_tokens":37183,"build_calls":1,"build_attempts":3,"build_latency_ms":430589,"wasted_tokens":68995,"repair_tokens":57347,"crosscheck_tokens":12642,"total_tokens":116847},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"13789 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"3 frames, effects: canvas-grain, beam-travel, fan-breathe, prism-glow"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"28 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 126.77, stdev 74.48, coverage 0.9365"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 2010330258 -> 4044166224"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Light, divided.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"PRISM — Precision Dispersion Optics"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=246 pageErrors=0"}]},"crosscheck":{"slug":"prism","reviewer_model":"gpt-5.5","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":7,"requirements_met":["The page renders as a single cinematic landing page with copy on the left and the prism/spectrum scene to the right.","The visible copy matches the requested PRISM text and is not placeholder filler.","The header, navigation, hero copy, CTA, and footer are visually present in the expected locations.","The scene uses procedural canvas/CSS/SVG visuals rather than image media.","The screenshot shows no obvious horizontal overflow or clipped primary content at 1024x640."],"requirements_missed":["The incoming beam is visually a short stub near the prism, not a thin white beam entering from the left as described.","The exact canvas technique is not followed: the canvas is modified every animation frame despite the brief requiring draw exactly once per size and never per frame.","The exact required JS was not inserted; extra live canvas mutation variables and per-frame fillRect logic were added.","The SVG prism structure does not match the brief: the specular polyline is placed in a separate svg.specular-svg instead of the specified polyline.specular over the prism structure.","The fanOut animation ends all wedges at opacity 0.88, overriding the seven distinct wedge opacity tokens after animation.","The beam width token is effectively ignored by clamping it to calc(var(--prism-w) + 24px), producing a much shorter beam than the specified --beam-w behavior.","Footer contents are wrapped in div elements rather than being the specified left and right text nodes.","The body::after vignette is assigned z-index:1, competing with the bg-drift layer instead of being cleanly represented as the described backdrop vignette layer."],"issues":[{"issue":"Incoming beam is mis-sized and visually fails the requested left-entry beam.","severity":"medium","evidence":"CSS: .beam { left: 0; width: min(var(--beam-w), calc(var(--prism-w) + 24px)); } Screenshot shows only a short white line immediately left of the prism, not a beam entering from the left side of the scene."},{"issue":"Canvas is repainted inside the hot rAF loop, directly violating the static grain requirement.","severity":"high","evidence":"JS loop contains liveContext.fillRect(liveSeed % liveWidth, (liveSeed >>> 8) % liveHeight, 2, 2); Brief required: Draw exactly once per size, never per frame."},{"issue":"Generated script is not the exact requested script.","severity":"high","evidence":"Generated JS adds liveContext/liveWidth/liveHeight/liveSeed state and per-frame canvas writes not present in the specified script."},{"issue":"Specular edge implementation does not match the requested SVG structure.","severity":"medium","evidence":"Generated HTML uses <svg class=\"specular-svg\"> containing <polyline class=\"specular\"> after a separate <svg class=\"prism\">. Brief specified svg.prism containing the polygon, with polyline.specular as the over-prism specular edge."},{"issue":"Wedge opacity tokens are defeated by the animation final state.","severity":"medium","evidence":"@keyframes fanOut { ... to { opacity:0.88; ... } } applies to all seven wedges with animation-fill-mode: both, overriding --wedge-op-1 through --wedge-op-7."},{"issue":"The spectrum reads as blurred colored rays rather than seven crisp dispersion wedges.","severity":"low","evidence":"Screenshot shows soft horizontal/diagonal blurred bands; CSS uses thin divs with height var(--wedge-h) and blur filters, not wedge-shaped geometry."},{"issue":"Layer stack is not implemented exactly for the vignette/backdrop.","severity":"low","evidence":"body::after has z-index: 1 while .bg-drift also has z-index: 1; the brief separately specified #grain at z-index 0 and bg-drift at z-index 1."},{"issue":"Footer markup does not match the requested text-node structure.","severity":"low","evidence":"Generated footer contains <div>© 2025 PRISM Optics. All rights reserved.</div> and <div>Precision · Dispersion · Clarity</div>, while the brief specified left and right text nodes."}],"summary":"The rendered page broadly resembles the requested PRISM landing page, but several exact implementation requirements were ignored. The biggest failures are the illegal per-frame canvas repainting, the shortened incoming beam, and structural deviations from the specified SVG and animation behavior.","usage":{"input":11073,"output":1569,"total":12642,"reasoning":496,"cached":0},"latency_ms":21224}},{"slug":"signal-room","title":"Signal Room","category":"Landing Page","effect":"Volumetric light shafts drift over a canvas field of audio-reactive bars","platform":"workbuddy","platform_label":"Workbuddy proxy","model":"gpt-5.5","model_label":"GPT-5.5 (gpt-5.5)","demo":"/demos/signal-room/index.html","doc":"/prompts/signal-room.md","prompt_text":"A. Build one complete valid index.html for a single-viewport landing page for an audio-AI company named Signal. Document title: Signal Room — Audio AI for Live Work. Set html lang=\"en\". ONE file only: plain HTML, inline CSS, and one small inline script at the end of body. No build step, no framework, no bundler, no React, no Tailwind, no GSAP, no Three.js, no Lottie, no external scripts, no external images, no video, no audio element, no GIF, no base64 photo blobs, and no third-party CDN URLs. Google Fonts is the only permitted network request; use it only for Inter and IBM Plex Mono with system fallback stacks. Every visible pixel must come from CSS gradients, inline SVG primitives, text, and one canvas. Desktop is exactly one frame with no scroll at width >= 901px: html and body height:100%; overflow:hidden; .page height:100dvh; overflow:hidden. Mobile and tablet at width <= 900px may scroll vertically but must never overflow horizontally. Accessibility is mandatory: real header, nav, main, and footer; exactly one h1; every control is a button or anchor with an accessible name; visible focus-visible rings; readable contrast; no inert clickable divs. The runtime telemetry hook must be exactly window.__DEMO__ = { name: 'signal-room', ready: false, frames: 0, effects: [], errors: [] }; and the script must set ready true after the first painted audio-bar 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 errors into errors. Keep the finished index.html under 90 KB. Do NOT build extra sections, pricing, testimonials, blogs, dashboards, forms, cookie banners, scroll indicators, app screenshots, waveform audio players, modal dialogs, or a large footer sitemap. The single visual idea is a dark room built from CSS volumetric light shafts falling from above and slowly drifting over a soft floor glow, with a bottom canvas field of 64 vertical bars moving as if driven by audio; the bars are the only fast continuous motion.\n\nB. THE BACKDROP — procedural cinematic room. Build the room with a fixed .backdrop at inset:0, overflow:hidden, pointer-events:none. The base room is .room-bg covering inset:0 with background layers, back to front: radial-gradient(ellipse at 50% 112%, rgba(119,232,255,0.18) 0%, rgba(119,232,255,0.075) 18%, rgba(119,232,255,0.022) 38%, transparent 62%); radial-gradient(circle at 20% 14%, rgba(94,134,255,0.14) 0%, rgba(94,134,255,0.045) 24%, transparent 52%); radial-gradient(circle at 78% 8%, rgba(205,238,255,0.11) 0%, rgba(205,238,255,0.035) 20%, transparent 46%); linear-gradient(180deg, #02040a 0%, #050915 39%, #03040a 74%, #010207 100%). Add .rear-haze at top:18%; left:50%; width:min(94vw, 1480px); height:34%; transform:translateX(-50%); background:radial-gradient(ellipse at 50% 50%, rgba(119,232,255,0.105) 0%, rgba(119,232,255,0.045) 34%, transparent 72%); filter:blur(42px); opacity:0.72. Add four shaft elements named .shaft--one through .shaft--four. Every shaft is position:absolute; top:-22%; transform-origin:50% 0%; filter:blur(var(--shaft-blur)); mix-blend-mode:screen; will-change:transform, opacity. Shaft one: left:13%; width:22vw; height:112vh; transform:skewX(-12deg) rotate(8deg); opacity:0.46; background:linear-gradient(180deg, rgba(202,244,255,0.00) 0%, rgba(202,244,255,0.25) 9%, rgba(119,232,255,0.145) 34%, rgba(119,232,255,0.052) 63%, rgba(119,232,255,0.00) 100%); animation:shaft-drift-a 24s ease-in-out infinite. Shaft two: left:36%; width:18vw; height:118vh; transform:skewX(10deg) rotate(-5deg); opacity:0.38; background:linear-gradient(180deg, rgba(255,255,255,0.00) 0%, rgba(220,249,255,0.20) 12%, rgba(125,180,255,0.11) 42%, rgba(125,180,255,0.040) 70%, transparent 100%); animation:shaft-drift-b 31s ease-in-out infinite. Shaft three: left:61%; width:24vw; height:116vh; transform:skewX(-8deg) rotate(6deg); opacity:0.42; background:linear-gradient(180deg, transparent 0%, rgba(216,248,255,0.21) 10%, rgba(119,232,255,0.125) 39%, rgba(119,232,255,0.046) 68%, transparent 100%); animation:shaft-drift-c 28s ease-in-out infinite. Shaft four: left:78%; width:15vw; height:104vh; transform:skewX(13deg) rotate(-9deg); opacity:0.30; background:linear-gradient(180deg, transparent 0%, rgba(196,226,255,0.17) 15%, rgba(119,232,255,0.082) 48%, rgba(119,232,255,0.026) 77%, transparent 100%); animation:shaft-drift-d 36s ease-in-out infinite. The soft floor glow is .floor-glow at left:50%; bottom:-11%; width:min(112vw, 1580px); height:38%; transform:translateX(-50%); border-radius:50% 50% 0 0; background:radial-gradient(ellipse at 50% 28%, rgba(119,232,255,0.24) 0%, rgba(119,232,255,0.112) 18%, rgba(83,113,190,0.050) 44%, transparent 72%); filter:blur(28px); opacity:0.82; animation:floor-breathe 13s ease-in-out infinite. Add .room-lines for the floor perspective: bottom:-4%; height:44%; width:100%; background-image:repeating-linear-gradient(90deg, rgba(161,239,255,0.060) 0 1px, transparent 1px 92px), repeating-linear-gradient(0deg, rgba(161,239,255,0.050) 0 1px, transparent 1px 64px); transform:perspective(840px) rotateX(68deg); transform-origin:50% 100%; opacity:0.32; mask-image:linear-gradient(180deg, transparent 0%, rgba(0,0,0,0.66) 20%, rgba(0,0,0,0.46) 58%, transparent 100%). Add .grain as inline SVG feTurbulence background at opacity 0.075, animated 1.1s steps(5), never above z-index 8.\n\nThe canvas is canvas#audio-bars, fixed left:50%; bottom:0; width:min(100vw, var(--bars-max-w)); height:var(--bars-h); transform:translateX(-50%); z-index:6; display:block; pointer-events:none. It draws exactly 64 bars at desktop, 52 bars at width <= 900px, and 44 bars at width <= 560px. Each bar is a rounded vertical capsule with width computed from available width and gap, minimum 3px, maximum 14px. Heights follow a deterministic sum of sines: base = 0.30 + 0.20*sin(t*2.6 + i*0.41) + 0.14*sin(t*4.9 + i*0.17) + 0.10*sin(t*8.2 + i*0.73); envelope = 0.62 + 0.28*sin(t*0.72) + 0.10*sin(t*1.13 + 1.7); height = clamp(0.10, 0.96, abs(base) * envelope + laneBoost). The centre 18 bars receive laneBoost 0.10 * (1 - abs(i - mid) / 18). Use canvas 2D only, no audio APIs. Gradient per bar: top rgba(210,250,255,0.94) 0%, mid rgba(119,232,255,0.70) 42%, base rgba(61,102,190,0.24) 100%. Draw a soft mirrored foot reflection with rgba(119,232,255,0.06) and blur 14px after the solid bars. Frame budget: 64 rounded rects plus 64 reflection rects and two global gradients per frame, under 140 draw primitives. Resolution trick: cap backing-store device pixel ratio at 1.75 on desktop and 1.5 on mobile, set canvas.width = cssWidth * dpr and canvas.height = cssHeight * dpr, then ctx.setTransform(dpr,0,0,dpr,0,0). Recompute dimensions on resize with one queued rAF, not a timer. If document.hidden, keep the rAF loop alive and increment frames, but skip paint until visible.\n\nC. THE LAYER STACK, back to front. html and body are #02040a at z-index auto. .backdrop is fixed inset:0 z-index:0 pointer-events:none overflow:hidden. Inside .backdrop: .room-bg z-index:0, .rear-haze z-index:1, .shaft--one z-index:2, .shaft--two z-index:3, .shaft--three z-index:2, .shaft--four z-index:3, .floor-glow z-index:4, .room-lines z-index:5, canvas#audio-bars z-index:6, .grain z-index:8. .page is position:relative z-index:10 min-height:100dvh display:grid grid-template-rows:auto 1fr auto. header.site-header is z-index:30. main.hero is z-index:20. .hero-panel is z-index:22 and must not use backdrop-filter stronger than 10px. footer.signal-footer is z-index:30. .mobile-menu-panel, when open, is fixed z-index:80. .focus-ring and browser outlines remain above local stacking contexts. Do not put any content behind the canvas bars at the bottom where it can become unreadable; footer sits above the bars with a bottom scrim.\n\nD. TOKEN TABLE. Define tokens in :root for the 1440px desktop default. Colors: --bg:#02040a; --bg-2:#050915; --text:#eff9fb; --text-soft:#cfe2e7; --muted:#91a6af; --muted-2:#617680; --accent:#77e8ff; --accent-2:#7b8cff; --accent-hot:#d2faff; --line:rgba(210,250,255,0.16); --line-strong:rgba(210,250,255,0.34); --line-soft:rgba(210,250,255,0.09); --panel:rgba(3,7,16,0.42); --panel-strong:rgba(5,10,22,0.66); --focus:#d2faff; --black:#000000. Type: --font-sans:\"Inter\", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; --font-mono:\"IBM Plex Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; --logo:16px; --nav:13px; --eyebrow:12px; --h1:82px; --lead:18px; --fine:12.5px; --button:13px; --metric:12px; --small-caps-track:0.16em. Layout: --header-x:42px; --header-y:28px; --header-h:44px; --page-pad-x:42px; --hero-pad-top:20px; --hero-pad-bottom:118px; --hero-max:860px; --hero-gap:22px; --hero-panel-pad-y:30px; --hero-panel-pad-x:38px; --footer-x:42px; --footer-y:26px; --footer-gap:28px; --nav-gap:10px. Buttons and shapes: --radius-pill:999px; --radius-panel:28px; --radius-soft:16px; --button-h:42px; --nav-h:38px; --chip-h:30px; --focus-w:2px; --focus-offset:4px; --panel-border-w:1px. Backdrop dimensions: --shaft-blur:22px; --bars-h:210px; --bars-max-w:1680px; --bars-bottom-fade:118px; --floor-line-size-x:92px; --floor-line-size-y:64px. Motion: --ease-out:cubic-bezier(0.16,1,0.3,1); --ease-soft:cubic-bezier(0.22,1,0.36,1); --enter-dur:1.05s; --soft-dur:1.25s; --shaft-a-dur:24s; --shaft-b-dur:31s; --shaft-c-dur:28s; --shaft-d-dur:36s; --floor-dur:13s; --grain-dur:1.1s. Canvas JS constants mirror the CSS dimensions: barCountDesktop 64; barCountTablet 52; barCountPhone 44; barGapDesktop 10; barGapTablet 8; barGapPhone 6; barRadius 999; paintDprDesktop 1.75; paintDprMobile 1.5. Fonts are loaded with one preconnect to https://fonts.gstatic.com and one Google Fonts stylesheet for Inter weights 400,500,600,700 and IBM Plex Mono weights 400,500,600 with display=swap.\n\nE. THE COMPOSITION. First CSS rule must be exactly html, body { background: #02040a !important; color: #eff9fb; } and the body tag must include style=\"background:#02040a;color:#eff9fb\" to prevent a white flash. Body uses margin:0; min-width:320px; font-family:var(--font-sans); text-rendering:geometricPrecision; -webkit-font-smoothing:antialiased. Page grid: .page { position:relative; z-index:10; min-height:100dvh; display:grid; grid-template-rows:auto minmax(0, 1fr) auto; } Desktop lock at >=901px sets html, body { height:100%; overflow:hidden; } and .page { height:100dvh; overflow:hidden; }. Header build order: create header.site-header with display:flex; align-items:center; justify-content:space-between; height:var(--header-h); padding:var(--header-y) var(--header-x) 0; gap:24px. The brand is an anchor href=\"#\" with accessible name \"Signal home\". It contains an inline SVG mark 22px by 22px: a rounded square rect x=2 y=2 width=18 height=18 rx=6 fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" and three vertical signal strokes at x=7,11,15 with rounded caps, heights 6,12,8, color var(--accent). Brand text is SIGNAL, font-size var(--logo), font-weight 700, letter-spacing:0.17em, color var(--text), text-decoration:none. Header nav contains exactly three links: Platform, Intelligence, Security. Each link is a pill from the UI language. Header right CTA is an anchor with text Hear a demo. Mobile burger appears only at <=900px and is a button with aria-label=\"Open navigation\", aria-expanded toggled, and three 16px bars.\n\nHero build order: main.hero is display:grid; place-items:center; min-width:0; padding:var(--hero-pad-top) var(--page-pad-x) var(--hero-pad-bottom); overflow:visible. Inside it, .hero-panel is width:100%; max-width:var(--hero-max); display:grid; justify-items:center; gap:var(--hero-gap); text-align:center; padding:var(--hero-panel-pad-y) var(--hero-panel-pad-x); border:var(--panel-border-w) solid var(--line); border-radius:var(--radius-panel); background:linear-gradient(180deg, rgba(5,10,22,0.50) 0%, rgba(3,7,16,0.30) 100%); box-shadow:0 28px 90px rgba(0,0,0,0.34), inset 0 1px 0 rgba(255,255,255,0.06); backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px). Place a chip above the h1: text REAL-TIME AUDIO AI, height var(--chip-h), padding 0 13px, border 1px solid var(--line), border-radius var(--radius-pill), font-size var(--eyebrow), font-family var(--font-mono), letter-spacing var(--small-caps-track), color var(--accent), background rgba(119,232,255,0.045). The H1 is exactly one element, text Hear the work before it slips., margin:0, max-width:800px, font-size:var(--h1), line-height:0.96, letter-spacing:-0.065em, font-weight:700, color:var(--text). Wrap the words in spans for entrance only, but do not split into extra headings. The lead paragraph exact copy is: Signal listens across meetings, calls, and customer conversations, then turns every live moment into searchable intelligence before context disappears. CSS: margin:0; max-width:680px; color:var(--text-soft); font-size:var(--lead); line-height:1.62; letter-spacing:-0.018em. Hero actions: a .button.button--primary with text Start with Signal, and a .button.button--ghost with text View security model; group display:flex; gap:12px; flex-wrap:wrap; justify-content:center. Footer: footer.signal-footer contains a ul with exactly three li metrics and no nav: 64 live bands, 18 ms analysis window, Private by design. Footer CSS: padding:0 var(--footer-x) var(--footer-y); display:flex; justify-content:center; align-items:center; position:relative. The ul is display:flex; gap:var(--footer-gap); list-style:none; padding:0; margin:0; flex-wrap:wrap; justify-content:center. Each li uses font-family:var(--font-mono); font-size:var(--metric); letter-spacing:0.10em; text-transform:uppercase; color:var(--muted). The numeric prefix or first word is a span color var(--accent-hot), opacity 0.92, margin-right:8px.\n\nF. THE UI LANGUAGE. All clickable header pills and hero buttons share a precise glass-signal treatment. .pill and .button are inline-flex; align-items:center; justify-content:center; border-radius:var(--radius-pill); text-decoration:none; position:relative; overflow:hidden; transition:transform 220ms var(--ease-out), border-color 220ms var(--ease-out), background 220ms var(--ease-out), color 220ms var(--ease-out), box-shadow 220ms var(--ease-out). .pill height:var(--nav-h); padding:0 15px; border:1px solid rgba(210,250,255,0.13); color:var(--text-soft); font-size:var(--nav); font-weight:560; background:linear-gradient(180deg, rgba(210,250,255,0.060) 0%, rgba(210,250,255,0.018) 100%); box-shadow:inset 0 1px 0 rgba(255,255,255,0.08), 0 12px 32px rgba(0,0,0,0.20). .pill::before and .button::before are a 112deg moving glint: content:\"\"; position:absolute; inset:-1px; background:linear-gradient(112deg, transparent 0%, transparent 36%, rgba(210,250,255,0.18) 50%, transparent 64%, transparent 100%); transform:translateX(-140%); transition:transform 680ms var(--ease-out); pointer-events:none. Hover moves any clickable up -1px, border-color rgba(210,250,255,0.36), color var(--text), and glint translateX(140%). Active returns translateY(0). .button height:var(--button-h); padding:0 18px; font-size:var(--button); font-weight:650; letter-spacing:-0.01em. .button--primary uses color:#031018; border-color rgba(210,250,255,0.56); background:linear-gradient(180deg, #d2faff 0%, #77e8ff 52%, #76a8ff 100%); box-shadow:0 18px 52px rgba(119,232,255,0.24), inset 0 1px 0 rgba(255,255,255,0.62). .button--primary:hover background:linear-gradient(180deg, #ffffff 0%, #98efff 48%, #8bb5ff 100%). .button--ghost uses color:var(--text-soft); border:1px solid var(--line); background:rgba(210,250,255,0.035). Focus: every a and button has :focus-visible outline var(--focus-w) solid var(--focus); outline-offset:var(--focus-offset); border-radius:var(--radius-pill). Selection: ::selection { background:rgba(119,232,255,0.30); color:#ffffff; }. The mobile panel uses background rgba(2,4,10,0.91), backdrop-filter blur(20px), border-top 1px solid var(--line), and stacked links at 24px with no extra pages.\n\nG. MOTION — entrance stagger and named keyframes. Resting opacity is always 1 on .appear elements; animations define from states only, and the page remains readable if animation fails. Apply .appear plus a modifier to each animated element with inline --d. Entrance animations use animation-duration:var(--enter-dur); animation-fill-mode:both; animation-timing-function:var(--ease-out); animation-delay:var(--d). Stagger table: brand .appear--scale delay 0.06s; nav Platform .appear--soft delay 0.14s; nav Intelligence .appear--soft delay 0.20s; nav Security .appear--soft delay 0.26s; header CTA .appear--scale delay 0.32s; chip .appear--pop delay 0.34s; h1 word Hear .appear--word delay 0.48s; h1 word the delay 0.55s; h1 word work delay 0.62s; h1 word before delay 0.69s; h1 word it delay 0.76s; h1 word slips. delay 0.83s; lead paragraph .appear--soft delay 1.00s; primary button .appear--scale delay 1.14s; ghost button .appear--scale delay 1.22s; metric 64 .appear--stat delay 1.30s; metric 18 .appear--stat delay 1.40s; metric Private .appear--stat delay 1.50s. Named keyframes: in-scale from opacity:0 transform:scale(0.92) to opacity:1 transform:none. in-soft from opacity:0 transform:translateY(14px) filter:blur(5px) to opacity:1 transform:none filter:blur(0). in-pop from 0% opacity:0 transform:scale(0.94), 68% opacity:1 transform:scale(1.035), 100% opacity:1 transform:scale(1). in-word from opacity:0 transform:translateY(0.46em) rotateX(36deg); transform-origin:50% 100%; filter:blur(4px) to opacity:1 transform:none filter:blur(0). in-stat from opacity:0 transform:translateY(12px) to opacity:1 transform:none. Continuous keyframes: shaft-drift-a drifts translate3d(-1.6vw,0,0) skewX(-12deg) rotate(7deg) to translate3d(1.2vw,1.4vh,0) skewX(-10deg) rotate(9deg) and back over 24s. shaft-drift-b drifts translate3d(1.1vw,0,0) skewX(10deg) rotate(-5deg) to translate3d(-1.4vw,1.8vh,0) skewX(12deg) rotate(-7deg) and back over 31s. shaft-drift-c drifts translate3d(-1.0vw,0,0) skewX(-8deg) rotate(6deg) to translate3d(1.6vw,1.2vh,0) skewX(-6deg) rotate(4deg) and back over 28s. shaft-drift-d drifts translate3d(0.8vw,0,0) skewX(13deg) rotate(-9deg) to translate3d(-1.0vw,1.0vh,0) skewX(11deg) rotate(-8deg) and back over 36s. floor-breathe animates opacity 0.72 to 0.90 to 0.72 and transform translateX(-50%) scaleX(0.985) to translateX(-50%) scaleX(1.025) to start over 13s. grain-shift uses five stepped offsets: 0 0, -2% 1%, 1% -2%, -1% 2%, 2% 0. The canvas bars run continuously via JS; their frame-to-frame height changes are the only fast continuous motion.\n\nH. RESPONSIVE TABLE — exact token overrides per breakpoint. Defaults above are for 1440x900. For width >= 1600px set --header-x:56px; --header-y:34px; --logo:17px; --nav:14px; --nav-h:40px; --button:14px; --button-h:44px; --h1:96px; --lead:19px; --hero-max:940px; --hero-gap:24px; --hero-panel-pad-y:34px; --hero-panel-pad-x:46px; --hero-pad-bottom:132px; --footer-x:56px; --footer-y:30px; --footer-gap:34px; --bars-h:232px; --bars-max-w:1840px; --shaft-blur:24px. For width >= 1920px set --header-x:72px; --header-y:38px; --logo:18px; --nav:14.5px; --nav-h:42px; --button-h:46px; --h1:112px; --lead:20px; --hero-max:1040px; --hero-gap:26px; --hero-panel-pad-y:38px; --hero-panel-pad-x:54px; --hero-pad-bottom:148px; --footer-y:34px; --footer-gap:42px; --bars-h:260px; --bars-max-w:2040px; --shaft-blur:26px. For 1280px to 1599px set --header-x:42px; --h1:78px; --lead:17px; --hero-max:820px; --hero-panel-pad-y:28px; --hero-panel-pad-x:34px; --hero-pad-bottom:112px; --bars-h:202px. For 901px to 1279px set --header-x:30px; --header-y:22px; --page-pad-x:30px; --footer-x:30px; --footer-y:22px; --nav:12.5px; --nav-h:36px; --button:12.5px; --button-h:40px; --logo:15px; --h1:60px; --lead:16px; --hero-max:720px; --hero-gap:18px; --hero-panel-pad-y:24px; --hero-panel-pad-x:28px; --hero-pad-bottom:98px; --footer-gap:22px; --bars-h:178px; --shaft-blur:20px. For desktop max-height <= 850px and width >= 901px set --header-y:20px; --header-h:40px; --hero-pad-top:10px; --hero-pad-bottom:86px; --hero-gap:16px; --hero-panel-pad-y:22px; --h1:58px; --lead:15px; --chip-h:28px; --footer-y:18px; --bars-h:168px; .hero-panel max-width:min(var(--hero-max), 760px). For desktop max-height <= 720px and width >= 901px set --header-y:14px; --header-h:36px; --hero-pad-top:4px; --hero-pad-bottom:72px; --hero-gap:12px; --hero-panel-pad-y:18px; --hero-panel-pad-x:26px; --h1:46px; --lead:13.5px; --eyebrow:11px; --chip-h:26px; --button-h:36px; --footer-y:12px; --metric:11px; --bars-h:138px; .hero-panel border-radius:22px; .floor-glow opacity:0.68. For width <= 900px remove the desktop height lock: html, body { min-height:100%; overflow-x:hidden; } .page { min-height:100dvh; height:auto; overflow:hidden; grid-template-rows:auto minmax(620px, auto) auto; } tokens: --header-x:22px; --header-y:20px; --header-h:42px; --page-pad-x:22px; --hero-pad-top:72px; --hero-pad-bottom:220px; --hero-max:100%; --hero-gap:18px; --hero-panel-pad-y:26px; --hero-panel-pad-x:22px; --h1:48px; --lead:16px; --button-h:42px; --bars-h:190px; --bars-max-w:980px; --shaft-blur:18px. At <=900px hide desktop nav and header CTA, show the burger, and create .mobile-menu-panel fixed inset:0 z-index:80 display:grid place-items:center transform:translateY(-100%) until html.menu-open transform:none. It contains the same three nav links and the CTA stacked with gap 22px. For width <=560px set --header-x:18px; --page-pad-x:18px; --hero-pad-top:54px; --hero-pad-bottom:190px; --hero-panel-pad-y:22px; --hero-panel-pad-x:18px; --radius-panel:22px; --h1:39px; --lead:14.5px; --eyebrow:10.5px; --chip-h:26px; --button:12.5px; --bars-h:170px; footer ul flex-direction:column; align-items:center; gap:10px. Canvas constants also scale: <=900px uses 52 bars and gap 8; <=560px uses 44 bars and gap 6; all widths keep zero horizontal overflow.\n\nI. FAILURE GUARDS. The .appear class must have opacity:1 as its resting declaration before animation names are applied; animation keyframes handle initial opacity so a failed animation never leaves content invisible. Black-first guard: first CSS rule is html, body { background: #02040a !important; color: #eff9fb; } and body has the inline background style. Two-rAF entrance fallback: after two nested requestAnimationFrame callbacks, inspect every .appear element with el.getAnimations({ subtree:false }); if none of its animations has playState \"running\" or \"finished\", add .is-in to every .appear. If a browser lacks getAnimations, run the same .is-in fallback after the two rAFs. .is-in forces animation:none !important; opacity:1 !important; transform:none !important; filter:none !important; clip-path:none !important. Canvas fallback: if canvas or 2D context is missing, add .no-canvas to html and CSS must show a static bar field using repeating-linear-gradient(90deg, rgba(119,232,255,0.40) 0 4px, transparent 4px 18px) masked by a bottom gradient on .backdrop::after; the page remains readable and the telemetry errors array receives a short string. Reduced motion: inside @media (prefers-reduced-motion: reduce), set *, *::before, *::after { animation:none !important; transition:none !important; scroll-behavior:auto !important; }, force .appear and .appear.is-in to opacity:1 transform:none filter:none clip-path:none, freeze the shafts at their base transforms, hide the grain animation, and make the canvas draw one static bar frame with t=2.4, then stop scheduling further visual changes after ready is true. In reduced motion, window.__DEMO__.frames may stop after the first painted frame, but ready must still become true and content must be static and readable. Maintain contrast: main text #eff9fb on #02040a, lead #cfe2e7 on the panel, and muted metric #91a6af all remain readable; do not lower text opacity below 0.86. Add a window error listener before running any other animation code and push String(event.message || event.error || \"unknown error\") to window.__DEMO__.errors. No console.log, console.warn, or console.error in the happy path. Guard every optional element check so missing elements cannot throw.\n\nJ. THE EXACT JS. Keep the script as a small IIFE at the end of body. It must do only telemetry, canvas bars, animation fallback, reduced-motion handling, resize handling, and mobile menu behavior. First line inside the script sets window.__DEMO__ = { name: 'signal-room', ready: false, frames: 0, effects: [], errors: [] }; then push implemented effect names exactly once in this order: \"volumetric-light-shafts\", \"floor-glow\", \"audio-bars-canvas\", \"grain-texture\", \"mobile-menu\". Add the error listener immediately after creating the object. Cache document.documentElement, canvas#audio-bars, the 2D context, the menu button, the mobile panel, all panel links, and matchMedia('(prefers-reduced-motion: reduce)'). Use no timers and no console calls. Implement a resizeCanvas function that reads canvas.getBoundingClientRect(), sets cssWidth and cssHeight, caps dpr to 1.75 or 1.5 based on innerWidth <= 900, sets canvas.width and canvas.height from css dimensions times dpr, and calls ctx.setTransform(dpr,0,0,dpr,0,0). Resize is scheduled by a boolean resizeQueued and requestAnimationFrame only. Implement roundedRect(ctx,x,y,w,h,r) locally with moveTo, lineTo, quadraticCurveTo, and closePath. The drawBars(timeMs) function clears the CSS-sized canvas, paints a bottom fade linear gradient from transparent 0% to rgba(2,4,10,0.62) 100%, then draws exactly barCount bars based on width breakpoint. Compute t = timeMs / 1000, mid = (barCount - 1) / 2, gap from breakpoint, available = cssWidth - 2 * sidePad where sidePad is 26 desktop, 20 tablet, 16 phone, barW = clamp between 3 and 14, startX = (cssWidth - (barCount * barW + (barCount - 1) * gap)) / 2. For each i, compute the sine formula from section B, clamp height, y = cssHeight - 18 - h, x = startX + i * (barW + gap). Draw reflection first using globalAlpha 0.46, filter blur(14px), y = cssHeight - 16, h = h * 0.32, fillStyle rgba(119,232,255,0.12). Reset filter and globalAlpha, create a vertical gradient for the solid bar using the exact color stops in section B, and fill the rounded capsule. Add a final top glow gradient across the bar field: radial-gradient equivalent via ctx.createRadialGradient centered at cssWidth/2, cssHeight*0.62 with inner radius 0 and outer radius min(cssWidth, cssHeight)*0.72, colors rgba(119,232,255,0.10) to transparent. Start a requestAnimationFrame loop named tick. At the top of every tick increment window.__DEMO__.frames by exactly 1. If document.hidden is false and context exists, call drawBars unless reduced motion has already painted once. On the first completed draw, schedule one more requestAnimationFrame and set window.__DEMO__.ready = true inside it, so ready follows the first paint opportunity. Continue the loop unless reduced motion has painted and ready is true. Animation fallback: add animationend listeners to every .appear; on end add .is-in; then run the two-rAF fallback described in section I. Burger behavior: clicking the burger toggles class menu-open on documentElement, updates aria-expanded to \"true\" or \"false\", updates aria-label to \"Close navigation\" or \"Open navigation\", and sets panel aria-hidden to the inverse. Escape closes the menu. Clicking any link inside the mobile panel closes the menu. Resizing to width > 900 closes the menu. Closing means remove menu-open, button aria-expanded=\"false\", button aria-label=\"Open navigation\", panel aria-hidden=\"true\". The script must not create visual DOM elements, must not fetch, must not use localStorage, and must not log.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the generated index.html contains exactly one h1 with the exact text 'Hear the work before it slips.' and uses semantic header, nav, main, and footer landmarks","canvas#audio-bars exists, paints a non-blank 64-bar audio field at 1440x900, and window.__DEMO__.frames increments once per requestAnimationFrame tick while effects includes 'audio-bars-canvas'","window.__DEMO__ is initialized with name 'signal-room', ready becomes true after the first painted bar frame, effects contains volumetric-light-shafts, floor-glow, audio-bars-canvas, grain-texture, and mobile-menu, and errors stays empty in the happy path","at viewport widths 1440 and 1920, html/body and .page are locked to one 100dvh frame with overflow hidden; at 390px and 768px vertical scroll may exist but there is no horizontal overflow","the document performs no network requests except Google Fonts, contains no external media or framework scripts, includes a prefers-reduced-motion reduce block, and every anchor or button has 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"],"usage":{"build_tokens":17590,"build_prompt_tokens":9104,"build_completion_tokens":8486,"build_reasoning_tokens":22,"build_calls":1,"build_attempts":1,"build_latency_ms":46035,"wasted_tokens":0,"repair_tokens":26588,"crosscheck_tokens":27457,"total_tokens":71635},"verification":{"passed":false,"passed_checks":19,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"25334 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"4 frames, effects: volumetric-light-shafts, floor-glow, audio-bars-canvas, grain-texture, mobile-menu"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":false,"detail":"10 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 23.1, stdev 47.63, coverage 0.5231"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 1219017982 -> 3576302936"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Hear the work before it slips.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Signal Room — Audio AI for Live Work"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=385 pageErrors=0"}]},"crosscheck":{"slug":"signal-room","reviewer_model":"hy4-preview-f","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":8,"requirements_met":["Document title 'Signal Room — Audio AI for Live Work' and lang=en","Single file ~25KB, well under 90KB","All :root tokens defined per brief","First CSS rule is html,body background guard; body has inline background","Backdrop layer stack with correct z-indices (0,1,2,3,4,5,6,8)","Four light shafts with exact positions, widths, transforms, gradients, animations and durations","Rear-haze, floor-glow with breathing animation, room-lines perspective grid, SVG grain","Canvas#audio-bars with 64/52/44 bars at correct breakpoints","Bar sine formula, envelope, laneBoost, clamp(0.10,0.96) match spec","Bar gradient stops rgba(210,250,255,0.94)/(119,232,255,0.70)/(61,102,190,0.24) at 0/42/100%","DPR 1.75 desktop / 1.5 mobile; setTransform and resize via single queued rAF","Telemetry hook window.__DEMO__ with name 'signal-room', ready/frames/effects/errors","Effects array pushed in exact order: volumetric-light-shafts, floor-glow, audio-bars-canvas, grain-texture, mobile-menu","Window error listener pushes to errors array","Stagger table delays exactly match spec (0.06, 0.14, 0.20, 0.26, 0.32, 0.34, 0.48, 0.55, 0.62, 0.69, 0.76, 0.83, 1.00, 1.14, 1.22, 1.30, 1.40, 1.50)","One h1 'Hear the work before it slips.' wrapped in word spans","Lead paragraph copy is verbatim from brief","Chip 'REAL-TIME AUDIO AI', primary 'Start with Signal', ghost 'View security model'","Header CTA 'Hear a demo' as anchor pill","Three footer metrics with correct span/text content","Real header/nav/main/footer landmarks","Mobile burger with aria-label, aria-expanded, aria-controls, three 16px bars","Mobile panel with three nav links plus CTA, aria-hidden toggling, Escape closes","Glass-signal pill/button treatment with 112deg glint, hover translateY -1px","Primary button gradient #d2faff→#77e8ff→#76a8ff and hover variant","Focus-visible outlines var(--focus-w) solid var(--focus)","::selection background rgba(119,232,255,0.30)","All eight responsive breakpoints (1600, 1920, 1280-1599, 901-1279, <=900, <=560, height<=850, height<=720)","Two-rAF animation fallback that adds .is-in when getAnimations missing or all finished","Reduced motion CSS disables animation and freezes shaft base transforms","Reduced motion JS draws one static frame at t=2.4 then stops","Footer bottom scrim above bars"],"requirements_missed":["SVG brand mark signal stroke heights: actual 4, 10, 6 instead of spec 6, 12, 8","Reflection color is rgba(119,232,255,0.12) with globalAlpha 0.46 (effective ~0.055) instead of spec rgba(119,232,255,0.06) directly"],"issues":[{"issue":"Brand SVG signal strokes are shorter than spec — actual path lengths 4, 10, 6 vs required 6, 12, 8","severity":"low","evidence":"<path d=\"M7 13V9M11 16V6M15 14V8\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"/>"},{"issue":"Reflection fill uses rgba(119,232,255,0.12) plus globalAlpha 0.46 (effective ≈0.055) instead of direct rgba(119,232,255,0.06)","severity":"low","evidence":"glowCtx.globalAlpha=.46;glowCtx.fillStyle=\"rgba(119,232,255,0.12)\";"},{"issue":"Volumetric light shafts render subtly in the viewport at 1024x640 even though CSS opacity values match the brief","severity":"low","evidence":"Visual inspection of screenshot — shafts are perceptible but understated"},{"issue":"hero-actions has no explicit width:100% on mobile, so buttons rely on flex-wrap without container width","severity":"low","evidence":"CSS only sets display:flex; gap:12px; flex-wrap:wrap; justify-content:center"}],"summary":"Faithful, near-complete implementation that satisfies the cinematic-room composition, responsive token table, telemetry contract, motion choreography, and accessibility rules, with only minor cosmetic deviations (shorter brand SVG stroke heights and a slightly off-canvas reflection opacity). Composition reads as premium with a well-centered hero panel, glass buttons, subtle volumetric ambience, and visible audio bars — minor shortcoming, not refuted.","usage":{"input":19497,"output":7960,"total":27457,"reasoning":6710,"cached":128},"latency_ms":81262}},{"slug":"slipstream","title":"Slipstream","category":"Landing Page","effect":"A 200vh sticky canvas spool with five independently drifting rounded-rectangle tape rings and a blended statement crossing its lower third.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/slipstream/index.html","doc":"/prompts/slipstream.md","prompt_text":"A. WHAT IS BEING BUILT\n\nBuild “Slipstream,” a single-page landing experience for Room Tone, a streaming service devoted to recorded music and long, uninterrupted listening. Produce exactly one complete index.html file with semantic HTML, plain CSS, and vanilla JavaScript. Set the document language to en, set the browser tab title to “Slipstream — Long-form listening by Room Tone,” and include one responsive viewport meta tag with width=device-width, initial-scale=1, and viewport-fit=cover.\n\nThis is an explicitly scroll-driven concept and is the sole exception to the normal desktop overflow rule. Create one 300dvh scroll stage: a 100dvh opening section followed by a 200dvh sticky section. The opening section scrolls at native speed. The second section uses position: sticky; top: 0; and remains pinned through its parent’s 200dvh range before an ease-out release. On widths from 901px through 1920px, do not disable document scrolling for this concept; allow only vertical scrolling and lock horizontal overflow. At widths of 900px and below, preserve the same 300dvh scroll mechanic while reducing canvas density and typography without changing the sequence.\n\nUse real header, main, section, nav, and footer elements. Include exactly one h1. The canvas is decorative and must use aria-hidden=\"true\"; the ring visual must have a concise text equivalent in visible HTML. Every visible link and button must have a discernible accessible name. Every keyboard-focusable element must receive a visible :focus-visible treatment.\n\nDo NOT build a hero video, audio player waveform, album grid, pricing table, account modal, login form, cursor tracker, magnetic button, 3D engine, horizontal carousel, cookie panel, chat widget, remote image, stock image, icon package, framework, bundler, or secondary page. Do NOT load any media file, base64 asset, third-party script, third-party stylesheet, animation library, or CDN endpoint other than the single permitted Google Fonts stylesheet. Do NOT add more than one h1. Do NOT add horizontal scrolling. Do NOT use SVG filters, canvas filters, canvas shadows, or per-ring shadowBlur.\n\nB. THE BACKDROP\n\nThe page field is #0C0B0A. Behind all content, place one fixed full-viewport .field element with a radial-gradient vignette centered at 55% 50%. The radial gradient uses these exact stops: 0% rgba(12,11,10,0); 38% rgba(12,11,10,0.18); 70% rgba(12,11,10,0.62); 100% rgba(12,11,10,0.94). Add a second linear-gradient from rgba(242,239,233,0.025) at 0deg to rgba(242,239,233,0) at 115deg. Do not add stars, grain images, noise textures, or moving particles.\n\nInside the sticky section, create exactly one canvas element. It must fill the sticky panel at width:100%; height:100%; display:block; transform-origin:50% 50%; transform:scale(1.5); will-change:transform. Apply overflow:hidden to the sticky panel so the 1.5-scaled canvas remains cropped. The 1.5 scale is the focal-anchor treatment: transform-origin is 0% 50%, and the drawing coordinates anchor the spool center near 6% of the logical CSS viewport width. The composition therefore reads as one oversized cropped spool entering from the left, not as a centred icon.\n\nUse a 0.75 backing-resolution trick. On resize, set the canvas backing store to floor(cssWidth × 0.75) by floor(cssHeight × 0.75), capped at 1440 × 960 backing pixels. Set canvas.style.width to the measured CSS pixel width plus \"px\" and canvas.style.height to the measured CSS pixel height plus \"px\". Configure the 2D context by calling ctx.setTransform(0.75, 0, 0, 0.75, 0, 0). Cap the effective drawing scale at width 960 and height 640 design units when the viewport exceeds 1280 × 853. This resolution remains at or below 691,200 pixels at 1920 × 1080.\n\nDraw exactly five concentric rounded rectangles. Their logical width multipliers are 1.000, 0.840, 0.680, 0.520, and 0.360. Their logical height multipliers are 1.220, 1.040, 0.860, 0.680, and 0.500. Base dimensions are max(viewportWidth × 1.5, 1300) CSS units wide and max(viewportHeight × 1.5, 1000) CSS units high. Center them at logical x = viewportWidth × 0.06 and y = viewportHeight × 0.50. Corner-radius multipliers are 0.230, 0.205, 0.180, 0.155, and 0.130 of base width. Stroke widths are 2, 4, 7, 10, and 14 CSS units. Ring 0, 1, 3, and 4 use #F2EFE9. Ring 2 alone uses #C8553D. Do not add fill. Line joins and caps are round.\n\nEach ring receives a base rotation of -90 + i × 6 degrees, so the five base angles are -90, -84, -78, -72, and -66 degrees. Rotate the complete group at exactly 360 degrees per 90 seconds. Translate the five rings independently along the horizontal axis at exactly 4, 7, 11, 15, and 18 CSS pixels per second. Use elapsed-seconds delta time and wrap each translation with modulo at the base logical width, preventing unbounded coordinates. The speed differences are intentionally unsynchronised.\n\nUse two drift periods for the backdrop field: a 46-second opacity cycle between 0.78 and 1.00 and a 72-second background-position cycle over 24px and 0px. Apply these through CSS animation only. The rings are canvas-drawn and must not also be duplicated as CSS borders. The canvas must render no more than five path operations, one clearRect, and one fillRect per frame. Keep the per-frame JavaScript budget below 2.2ms at 1440 × 900 in a warm headless run: cache all DOM references before the loop, read scrollY only in the scroll handler, do not call getBoundingClientRect inside rAF, and perform no canvas filter or shadowBlur calls. Target at least 20 delivered animation frames during every 1.2-second observation window at 1440 × 900.\n\nC. THE LAYER STACK\n\nUse this exact front-to-back z-index stack:\n\n1. The document and .field background use z-index:0.\n2. The fixed atmosphere layer uses z-index:1 and pointer-events:none.\n3. The sticky canvas clipping panel uses z-index:2 and overflow:hidden.\n4. The canvas uses z-index:2 within that panel.\n5. The release transform layer uses z-index:3 and contains the h1 and supporting copy.\n6. A non-interactive lower-third contrast veil uses z-index:4, background:linear-gradient(180deg, rgba(12,11,10,0) 44%, rgba(12,11,10,0.72) 100%), and pointer-events:none.\n7. The sticky section’s top edge marker uses z-index:5.\n8. The sticky motion control uses z-index:8.\n9. The opening section header and statement use z-index:10.\n10. The opening section footer uses z-index:10.\n11. The skip link uses z-index:100.\n\nThe opening section has position:relative; z-index:10; height:100dvh; padding:28px 40px 24px. The sticky wrapper has position:sticky; top:0; height:200dvh; z-index:2. The sticky viewport panel has position:absolute; inset:0; overflow:hidden. The .release-layer has position:absolute; inset:0; z-index:3; display:flex; align-items:flex-end; padding:0 40px 48px; will-change:transform. The h1 is positioned in the lower third, not centred.\n\nD. TOKEN TABLE\n\nDefine every used size and colour as a CSS custom property. Use these exact values:\n\n--bg: #0C0B0A;\n--surface: #141210;\n--surface-raised: #1B1816;\n--ink: #F2EFE9;\n--dust: #6B6560;\n--dust-light: #9A938C;\n--oxide: #C8553D;\n--oxide-hover: #DF654B;\n--hairline: rgba(242,239,233,0.18);\n--focus: #F2EFE9;\n--font-display: \"Space Grotesk\", \"Helvetica Neue\", Arial, sans-serif;\n--font-mono: \"IBM Plex Mono\", \"SFMono-Regular\", Consolas, monospace;\n--fs-eyebrow: 11px;\n--fs-nav: 12px;\n--fs-statement: clamp(19px, 1.55vw, 25px);\n--fs-h1: clamp(64px, 8.7vw, 164px);\n--fs-body: 15px;\n--fs-meta: 10px;\n--lh-eyebrow: 1.3;\n--lh-nav: 1.2;\n--lh-statement: 1.34;\n--lh-h1: 0.84;\n--lh-body: 1.55;\n--tracking-eyebrow: 0.18em;\n--tracking-nav: 0.08em;\n--tracking-h1: -0.065em;\n--space-1: 6px;\n--space-2: 10px;\n--space-3: 14px;\n--space-4: 20px;\n--space-5: 28px;\n--space-6: 40px;\n--space-7: 64px;\n--space-8: 96px;\n--radius-pill: 999px;\n--radius-panel: 18px;\n--border-hairline: 1px solid var(--hairline);\n--stage-height: 300dvh;\n--intro-height: 100dvh;\n--pin-height: 200dvh;\n--canvas-scale: 1.5;\n--canvas-resolution: 0.75;\n--release-distance: -10vh;\n--ease-release: cubic-bezier(0.16, 1, 0.3, 1);\n--ease-ui: cubic-bezier(0.22, 1, 0.36, 1);\n--duration-ui: 220ms;\n--duration-load: 700ms;\n--ring-stagger: 120ms;\n--focus-width: 2px;\n--focus-offset: 4px;\n--content-max: 1440px;\n--header-height: 52px;\n--footer-height: 36px;\n--canvas-stroke-0: 2px;\n--canvas-stroke-1: 4px;\n--canvas-stroke-2: 7px;\n--canvas-stroke-3: 10px;\n--canvas-stroke-4: 14px;\n\nE. THE COMPOSITION\n\nBuild the opening section as follows. Its content width is min(100%, var(--content-max)). Use a CSS grid with grid-template-columns:minmax(0, 1fr) auto, grid-template-rows:52px 1fr 36px, column-gap:24px, row-gap:20px, and height:100%. The header occupies grid-column:1 / -1 and grid-row:1. The statement occupies grid-column:1, grid-row:2, aligns itself to end, and has a maximum width of 680px. A small index block occupies grid-column:2, grid-row:2, aligns itself to end, and has width:180px. The footer occupies grid-column:1 / -1 and grid-row:3.\n\nThe header is a nav with display:grid; grid-template-columns:1fr auto 1fr; align-items:center; height:52px. The left item is the Room Tone wordmark link with exact copy “ROOM TONE”. The centre contains two text links with exact copy “Listen” and “Method”. The right item contains one pill button with exact copy “Pause motion”. The nav’s accessible label is “Primary navigation”. The wordmark uses --font-display, 15px, weight 600, letter-spacing -0.02em. Centre links use --font-mono, --fs-nav, --tracking-nav, uppercase, and --dust-light. The right button uses the pill treatment in section F.\n\nThe opening statement has exact copy: “A room for records that refuse to hurry. Start one side. Keep the lights low. Let the next room arrive on its own.” Use --fs-statement, --lh-statement, weight 400, max-width:680px, and colour:var(--ink). Above it, place one eyebrow with exact copy “RECORDED MUSIC / UNBROKEN TIME”. Below it, place one body paragraph with exact copy: “Room Tone streams complete recordings without visible countdowns, editorial interruptions, or algorithmic urgency. One selection stays in motion from its first room tone to its last fade.” The body uses --fs-body, --lh-body, max-width:520px, and colour:var(--dust-light).\n\nThe opening index block uses a 1px top border and four stacked metadata rows. The row labels and values must be exact: “FORMAT / LOSSLESS + LOSSLESS”; “SESSION / CONTINUOUS”; “SURFACE / DARK”; “CATALOGUE / 18,420 RECORDS”. Labels use --fs-meta, --tracking-eyebrow, and --dust. Values use --font-mono, 11px, and --ink. The block uses row-gap:12px and padding-top:14px.\n\nThe opening footer is a semantic footer with border-top:var(--border-hairline), display:flex, justify-content:space-between, align-items:center, color:var(--dust), font-family:var(--font-mono), font-size:var(--fs-meta), and text-transform:uppercase. Its exact copy is “A LISTENING SERVICE BY ROOM TONE” on the left and “SCROLL INTO THE SPOOL ↓” on the right.\n\nThe sticky section begins immediately after the opening section and is 200dvh tall. Its viewport panel contains the canvas, a lower-third veil, the release layer, an edge marker, and the motion control. The edge marker is positioned at top:28px; left:40px and uses exact copy “NOW PLAYING / SIDE A — UNTITLED STUDY IN FOLD”. It has --fs-eyebrow, --tracking-eyebrow, and --dust-light. Place a 6px oxide dot before the text using ::before.\n\nThe release layer places the h1 at the left edge. The h1 exact copy is “KEEP THE ROOM TONE.” Use --fs-h1, --lh-h1, --tracking-h1, font-weight:500, max-width:1050px, color:var(--ink), and mix-blend-mode:difference. The text must overlap the lower third of the canvas, producing white-to-black inversions where it crosses #F2EFE9. Beneath the h1, place one paragraph with exact copy: “No timer. No queue shuffle. No seam between records.” It uses --fs-body, --lh-body, color:var(--dust-light), max-width:420px, and margin-top:20px. Keep the h1 and paragraph in a copy column using display:grid; grid-template-columns:minmax(0, 1fr) 360px; column-gap:40px; align-items:end; width:100%; max-width:1440px.\n\nThe sticky motion control is positioned at right:40px; bottom:48px. Its exact initial copy is “Pause motion”. It uses the same pill treatment as the header. A second visually hidden status element is unnecessary; update the existing button’s aria-pressed and text copy from JavaScript.\n\nF. THE UI LANGUAGE\n\nUse flat geometry, 1px borders, and one oxide interaction colour. Text links have no permanent background. Their resting colour is var(--dust-light), border-bottom:1px solid transparent, and transition:color 180ms ease, border-color 180ms ease. Hover changes colour to var(--ink) and border-color to var(--oxide). Active colour is var(--oxide-hover).\n\nPill buttons have height:36px, padding:0 16px, border-radius:var(--radius-pill), border:1px solid rgba(242,239,233,0.28), background:linear-gradient(135deg, #24201E 0%, #151312 100%), color:var(--ink), font-family:var(--font-mono), font-size:11px, line-height:1, letter-spacing:0.06em, text-transform:uppercase, and transition:transform var(--duration-ui) var(--ease-ui), border-color var(--duration-ui) ease, background var(--duration-ui) ease. Hover uses transform:translateY(-2px), border-color:var(--oxide), and background:linear-gradient(135deg, #2B211E 0%, #181311 100%). Active uses transform:translateY(0) scale(0.98). Buttons with aria-pressed=\"true\" use border-color:var(--oxide) and a small inset oxide indicator through box-shadow:inset 0 0 0 1px rgba(200,85,61,0.24); box-shadow is a UI style only and is never used in canvas.\n\nThe skip link is position:fixed; top:8px; left:8px; z-index:100; transform:translateY(-160%); padding:10px 14px; background:var(--ink); color:var(--bg); font:12px var(--font-mono). On :focus, transform:translateY(0). Apply outline:2px solid var(--focus); outline-offset:4px to :focus-visible for a, button, and [tabindex]. Do not remove browser outlines.\n\nG. MOTION\n\nUse a single persistent requestAnimationFrame loop for the canvas, group rotation, independent ring translation, and scroll-derived release. Compute scroll state in a passive scroll listener and set a cached numeric value only. Do not query layout inside the loop.\n\nThe load sequence uses these exact opacity ramps:\n\nOpening eyebrow: 0 to 1 over 700ms, delay 40ms, using ease-out.\nOpening statement: 0 to 1 over 700ms, delay 120ms, using ease-out.\nMetadata block: 0 to 1 over 700ms, delay 220ms, using ease-out.\nHeader: 0 to 1 over 700ms, delay 0ms, using ease-out.\nFooter: 0 to 1 over 700ms, delay 300ms, using ease-out.\nCanvas ring 0: 0 to 1 over 700ms, delay 140ms, using ease-out.\nCanvas ring 1: 0 to 1 over 700ms, delay 260ms, using ease-out.\nCanvas ring 2: 0 to 1 over 700ms, delay 380ms, using ease-out.\nCanvas ring 3: 0 to 1 over 700ms, delay 500ms, using ease-out.\nCanvas ring 4: 0 to 1 over 700ms, delay 620ms, using ease-out.\nSticky h1: 0 to 1 over 700ms, delay 340ms, using ease-out.\nSticky paragraph: 0 to 1 over 700ms, delay 460ms, using ease-out.\nMotion control: 0 to 1 over 700ms, delay 540ms, using ease-out.\n\nCSS load animations affect opacity only. Name them reveal-up, reveal-fade, ring-reveal, drift-field, and drift-grain. The ring reveal is represented in canvas by the same delay arithmetic in JavaScript. All resting opacities are 1. The initial class .is-loading sets the animated HTML elements to opacity:0; remove is-loading after the second animation frame so they become visible if CSS animation is disabled.\n\nThe ring rotation is defined as groupAngle = elapsedSeconds × 4 degrees per second, which completes 360 degrees in 90 seconds. Independent translations use the exact speeds [4, 7, 11, 15, 18] CSS pixels per second. Clamp deltaSeconds to 0.050 to prevent jumps after a suspended tab. Pause the motion button sets all ring translation and rotation calculations to their current frozen values while preserving the current scroll release position. Its second state is “Play motion”, aria-pressed=\"true\", and its first state is “Pause motion”, aria-pressed=\"false\".\n\nThe final 25dvh of the 200dvh pin is the release zone. Convert progress there to 0 through 1 using cubic ease-out: 1 - (1 - progress)^3. Apply translate3d(0, releaseDistance × easedProgress, 0) only to .release-layer. The h1 and paragraph do not receive independent parallax. The sticky panel remains position:sticky until the 300dvh parent ends. No fade-out is applied.\n\nH. THE RESPONSIVE TABLE\n\nAt 1440 × 900 and widths from 1201px to 1600px, use --space-6:40px, --fs-h1:clamp(96px, 8.7vw, 144px), stage 300dvh, intro 100dvh, pin 200dvh, opening padding 28px 40px 24px, release padding 0 40px 48px, and copy grid minmax(0, 1fr) 360px.\n\nAt 1920 × 1080, override --content-max:1680px, --fs-h1:164px, opening padding 32px 64px 28px, release padding 0 64px 64px, metadata width 220px, and keep the canvas 1.5 scale with the left origin.\n\nAt widths from 901px to 1199px, override --space-6:28px, --fs-h1:clamp(76px, 8.7vw, 104px), opening padding 24px 28px 20px, release padding 0 28px 40px, copy grid-template-columns:1fr, copy paragraph max-width:420px, metadata width 160px, and canvas scale 1.35.\n\nAt 768 × 1024 and widths from 601px to 900px, use stage 300dvh, intro 100dvh, pin 200dvh, --fs-h1:clamp(68px, 11vw, 92px), --fs-statement:22px, opening padding 22px 24px 18px, release padding 0 24px 38px, header grid-template-columns:1fr auto, centre navigation display:none, statement max-width:560px, metadata width 150px, copy grid-template-columns:1fr, row-gap:16px, and canvas scale 1.25. The document may scroll vertically at this breakpoint; disable horizontal overflow.\n\nAt 390 × 844 and widths through 600px, use stage 300dvh, intro 100dvh, pin 200dvh, --fs-h1:clamp(50px, 15.3vw, 68px), --fs-statement:19px, --fs-body:14px, opening padding 18px 18px 16px, release padding 0 18px 32px, header grid-template-columns:1fr auto, centre navigation display:none, opening statement max-width:330px, metadata width 132px, metadata value font-size:9px, copy grid-template-columns:1fr, edge marker left:18px, motion control right:18px, bottom:24px, and canvas scale 1.10. Keep all text inside min-width:0 containers and use overflow-wrap:anywhere where a long catalogue value needs to wrap.\n\nUse max-height:760px at widths from 901px to 1600px. Override opening padding-top:18px, opening padding-bottom:16px, --header-height:44px, --footer-height:30px, --fs-h1:clamp(58px, 8vw, 104px), --fs-statement:18px, release padding-bottom:28px, and motion control bottom:28px. The stage and pin heights remain 300dvh and 200dvh.\n\nUse max-height:620px at all widths. Override opening padding-top:14px, opening padding-bottom:12px, --header-height:40px, --footer-height:26px, --fs-h1:clamp(46px, 7.8vw, 86px), --fs-statement:17px, --fs-body:13px, release padding-bottom:22px, and motion control bottom:18px. Do not remove the footer; reduce its gap and allow its right label to remain on the same line at widths above 600px.\n\nI. FAILURE GUARDS\n\nAll visible elements have resting opacity:1. Add .is-loading only before the first paint. Use a two-rAF readiness gate: after DOMContentLoaded and resize measurement, request one rAF, then request a second rAF, and only then remove is-loading, set telemetry ready:true, and start drawing. If requestAnimationFrame is unavailable, use a 120ms timeout that removes is-loading, sets ready:true, starts the fallback interval at 30fps, and increments telemetry frames once per fallback tick.\n\nRespect prefers-reduced-motion: reduce. In that mode, cancel CSS reveal animations, remove is-loading, keep every element at opacity:1, stop ring drift and group rotation at groupAngle:0 and translations:0, disable the background drift animations, and do not apply the release transform. Keep the scroll section readable and fully static. If canvas context acquisition returns null, hide the canvas with display:none and keep the HTML copy visible. If ResizeObserver is unavailable, use window resize. If the canvas backing width is below 2px, use 2px. Never throw from the resize or animation path. Add a window error listener before first paint. Keep a guard around context resetTransform so a lost canvas does not generate repeated console output.\n\nJ. THE EXACT JS\n\nPlace this JavaScript at the end of body. It must be copied exactly in structure, constants, selectors, hooks, and behaviour:\n\nconst NAME = \"slipstream\";\nconst state = {\n  width: 0,\n  height: 0,\n  scale: 1,\n  elapsed: 0,\n  last: 0,\n  scrollY: window.scrollY || 0,\n  scrollDirty: true,\n  paused: false,\n  started: false,\n  ready: false,\n  fallback: false,\n  effects: [\"ring-drift\", \"group-rotation\", \"sticky-release\", \"load-ramp\"]\n};\nwindow.__DEMO__ = {\n  name: NAME,\n  ready: false,\n  frames: 0,\n  effects: [],\n  errors: []\n};\nwindow.addEventListener(\"error\", function(event) {\n  if (window.__DEMO__.errors.length < 50) {\n    window.__DEMO__.errors.push(String(event.message || \"window error\"));\n  }\n});\n\nconst root = document.documentElement;\nconst stage = document.querySelector(\".scroll-stage\");\nconst canvas = document.querySelector(\"#tape\");\nconst ctx = canvas && canvas.getContext(\"2d\", { alpha: true });\nconst release = document.querySelector(\".release-layer\");\nconst motionButtons = Array.from(document.querySelectorAll(\"[data-motion-toggle]\"));\nconst reduced = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\nconst speeds = [4, 7, 11, 15, 18];\nconst translations = [0, 0, 0, 0, 0];\nconst baseRotations = [-90, -84, -78, -72, -66];\nconst revealDelays = [140, 260, 380, 500, 620];\nconst strokeWidths = [2, 4, 7, 10, 14];\nconst revealStart = [0, 0, 0, 0, 0];\nconst loadedAt = performance.now();\n\nfunction markEffects() {\n  window.__DEMO__.effects = state.effects.slice();\n}\n\nfunction setReady() {\n  if (state.ready) return;\n  state.ready = true;\n  root.classList.remove(\"is-loading\");\n  window.__DEMO__.ready = true;\n  markEffects();\n}\n\nfunction cubicOut(value) {\n  const inverse = 1 - value;\n  return 1 - inverse * inverse * inverse;\n}\n\nfunction measure() {\n  state.width = window.innerWidth;\n  state.height = window.innerHeight;\n  state.scale = state.width <= 600 ? 1.10 : state.width <= 900 ? 1.25 : state.width <= 1199 ? 1.35 : 1.5;\n  root.style.setProperty(\"--canvas-scale\", String(state.scale));\n  if (!canvas || !ctx) return;\n  const width = Math.max(2, Math.min(1440, Math.floor(state.width * 0.75)));\n  const height = Math.max(2, Math.min(960, Math.floor(state.height * 0.75)));\n  canvas.width = width;\n  canvas.height = height;\n  canvas.style.width = state.width + \"px\";\n  canvas.style.height = state.height + \"px\";\n  const scaleX = width / state.width;\n  const scaleY = height / state.height;\n  const fitScale = Math.min(scaleX, scaleY, state.width > 1280 && state.height > 853 ? 0.6667 : 1);\n  ctx.setTransform(fitScale, 0, 0, fitScale, 0, 0);\n  state.scrollDirty = true;\n}\n\nfunction opacityForRing(index, now) {\n  if (reduced.matches) return 1;\n  const delay = revealDelays[index];\n  if (now - loadedAt < delay) return 0;\n  return Math.min(1, (now - loadedAt - delay) / 700);\n}\n\nfunction updateRelease() {\n  if (!stage || !release) return;\n  if (reduced.matches) {\n    release.style.transform = \"translate3d(0, 0, 0)\";\n    return;\n  }\n  const top = stage.offsetTop;\n  const start = top + window.innerHeight;\n  const end = top + stage.offsetHeight - window.innerHeight;\n  const releaseStart = end - window.innerHeight * 0.25;\n  const progress = Math.max(0, Math.min(1, (state.scrollY - releaseStart) / (window.innerHeight * 0.25)));\n  const eased = cubicOut(progress);\n  const distance = window.innerHeight * -0.10;\n  release.style.transform = \"translate3d(0, \" + distance * eased + \"px, 0)\";\n}\n\nfunction draw(now, delta) {\n  if (!ctx || !canvas) return;\n  if (!state.paused && !reduced.matches) {\n    state.elapsed += delta;\n    const groupAngle = state.elapsed * 4;\n    const baseWidth = Math.max(state.width * 1.5, 1300);\n    for (let i = 0; i < 5; i += 1) {\n      translations[i] = (translations[i] + speeds[i] * delta) % baseWidth;\n    }\n    const revealOpacity = [1, 1, 1, 1, 1];\n    ctx.clearRect(0, 0, canvas.width, canvas.height);\n    ctx.fillStyle = \"#0C0B0A\";\n    ctx.fillRect(0, 0, canvas.width, canvas.height);\n    ctx.save();\n    ctx.translate(state.width * 0.06, state.height * 0.50);\n    ctx.rotate(groupAngle * Math.PI / 180);\n    const widthMultipliers = [1, 0.84, 0.68, 0.52, 0.36];\n    const heightMultipliers = [1.22, 1.04, 0.86, 0.68, 0.5];\n    const radiusMultipliers = [0.23, 0.205, 0.18, 0.155, 0.13];\n    for (let i = 0; i < 5; i += 1) {\n      const w = baseWidth * widthMultipliers[i];\n      const h = Math.max(state.height * 1.5, 1000) * heightMultipliers[i];\n      const radius = baseWidth * radiusMultipliers[i];\n      const alpha = Math.min(revealOpacity[i], opacityForRing(i, now));\n      ctx.save();\n      ctx.translate(translations[i], 0);\n      ctx.rotate(baseRotations[i] * Math.PI / 180);\n      ctx.beginPath();\n      ctx.roundRect(-w / 2, -h / 2, w, h, radius);\n      ctx.lineWidth = strokeWidths[i];\n      ctx.lineCap = \"round\";\n      ctx.lineJoin = \"round\";\n      ctx.strokeStyle = i === 2 ? \"#C8553D\" : \"#F2EFE9\";\n      ctx.globalAlpha = alpha;\n      ctx.stroke();\n      ctx.restore();\n    }\n    ctx.restore();\n    ctx.globalAlpha = 1;\n  } else {\n    ctx.clearRect(0, 0, canvas.width, canvas.height);\n    ctx.fillStyle = \"#0C0B0A\";\n    ctx.fillRect(0, 0, canvas.width, canvas.height);\n    ctx.save();\n    ctx.translate(state.width * 0.06, state.height * 0.50);\n    const baseWidth = Math.max(state.width * 1.5, 1300);\n    const widthMultipliers = [1, 0.84, 0.68, 0.52, 0.36];\n    const heightMultipliers = [1.22, 1.04, 0.86, 0.68, 0.5];\n    const radiusMultipliers = [0.23, 0.205, 0.18, 0.155, 0.13];\n    for (let i = 0; i < 5; i += 1) {\n      const w = baseWidth * widthMultipliers[i];\n      const h = Math.max(state.height * 1.5, 1000) * heightMultipliers[i];\n      const radius = baseWidth * radiusMultipliers[i];\n      ctx.save();\n      ctx.translate(translations[i], 0);\n      ctx.rotate(baseRotations[i] * Math.PI / 180);\n      ctx.beginPath();\n      ctx.roundRect(-w / 2, -h / 2, w, h, radius);\n      ctx.lineWidth = strokeWidths[i];\n      ctx.lineCap = \"round\";\n      ctx.lineJoin = \"round\";\n      ctx.strokeStyle = i === 2 ? \"#C8553D\" : \"#F2EFE9\";\n      ctx.globalAlpha = opacityForRing(i, now);\n      ctx.stroke();\n      ctx.restore();\n    }\n    ctx.restore();\n    ctx.globalAlpha = 1;\n  }\n}\n\nfunction frame(now) {\n  if (!state.last) state.last = now;\n  const delta = Math.min(0.05, Math.max(0, (now - state.last) / 1000));\n  state.last = now;\n  draw(now, delta);\n  if (state.scrollDirty) {\n    updateRelease();\n    state.scrollDirty = false;\n  }\n  window.__DEMO__.frames += 1;\n  requestAnimationFrame(frame);\n}\n\nfunction startFallback() {\n  if (state.fallback) return;\n  state.fallback = true;\n  const start = performance.now();\n  setInterval(function() {\n    const now = performance.now();\n    draw(now, Math.min(0.05, (now - state.last) / 1000 || 0.033));\n    state.last = now;\n    updateRelease();\n    window.__DEMO__.frames += 1;\n  }, 33.333);\n  setReady();\n}\n\nfunction setPaused(paused) {\n  state.paused = paused;\n  for (const button of motionButtons) {\n    button.textContent = paused ? \"Play motion\" : \"Pause motion\";\n    button.setAttribute(\"aria-pressed\", paused ? \"true\" : \"false\");\n  }\n}\n\nfor (const button of motionButtons) {\n  button.addEventListener(\"click\", function() {\n    setPaused(!state.paused);\n  });\n}\n\nwindow.addEventListener(\"scroll\", function() {\n  state.scrollY = window.scrollY || 0;\n  state.scrollDirty = true;\n}, { passive: true });\n\nwindow.addEventListener(\"resize\", measure, { passive: true });\nreduced.addEventListener(\"change\", function() {\n  state.scrollDirty = true;\n});\n\nmeasure();\nmarkEffects();\nif (typeof window.requestAnimationFrame === \"function\") {\n  requestAnimationFrame(function() {\n    requestAnimationFrame(function() {\n      setReady();\n      requestAnimationFrame(frame);\n    });\n  });\n} else {\n  startFallback();\n}\n\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["The delivered artifact is one valid index.html using only HTML, CSS, and vanilla JavaScript; loading at 1440x900 creates no horizontal overflow, exactly one h1, and real header, main, nav, section, and footer elements.","At 1440x900, the scroll stage measures 300dvh, the opening section measures 100dvh, the sticky section measures 200dvh with position:sticky and top:0, and window.__DEMO__ becomes { name: 'slipstream', ready: true, frames: a non-negative integer, effects: ['ring-drift','group-rotation','sticky-release','load-ramp'], errors: [] } after first paint.","The canvas contains five rounded-rectangle paths per frame with stroke widths [2,4,7,10,14], horizontal speeds [4,7,11,15,18], and exactly one stroke using #C8553D; its backing store uses the 0.75 resolution rule and never exceeds 1440 by 960 pixels.","At 390x844, 768x1024, 1440x900, and 1920x1080, document.documentElement.scrollWidth does not exceed document.documentElement.clientWidth, all visible controls have accessible names, and every keyboard-focusable control displays the specified focus-visible outline.","With prefers-reduced-motion: reduce, all visible content reaches opacity 1, canvas translation and group rotation remain fixed, the release transform remains translate3d(0,0,0), and a 1.2-second observation records at least 20 telemetry frames without console output or uncaught exceptions.","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"],"usage":{"build_tokens":45334,"build_prompt_tokens":8818,"build_completion_tokens":36516,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":457505,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":0,"total_tokens":45334},"verification":{"passed":true,"passed_checks":19,"total_checks":19,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"34238 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"34 frames, effects: ring-drift, group-rotation, sticky-release, load-ramp"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"81 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 16.89, stdev 49.55, coverage 0.4805"},{"name":"canvas spans the viewport width","pass":true,"detail":"2160px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 3350107029 -> 1669673381"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"KEEP THE ROOM TONE.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Slipstream — Long-form listening by Room Tone"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=723 pageErrors=0"}]},"crosscheck":{"slug":"slipstream","reviewer_model":"deepseek-v4.1-flash","reviewer_platform":"workbuddy","verdict":"unparsed","confidence":"low","summary":"","issues":[],"usage":{"input":0,"output":0,"total":0,"reasoning":0,"cached":0},"raw":""}},{"slug":"smolder","title":"Smolder","category":"Landing Page","effect":"An 18-word hero is revealed by scroll progress, with each word cooling from an ember glow into bone-white locked text.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/smolder/index.html","doc":"/prompts/smolder.md","prompt_text":"A. WHAT IS BEING BUILT\n\nBuild a single-file, scroll-mapped cinematic landing page for Longhand, a slow-reading essay platform. The output is exactly one index.html containing semantic HTML, plain CSS, and plain JavaScript. Set document.documentElement.lang to \"en\", set the document title to \"Longhand — One long thought, every week\", and set the viewport meta tag to width=device-width, initial-scale=1. The visual system is a warm-black editorial page with one 18-word opening paragraph as the hero. This is explicitly a scroll piece: the page must be taller than the viewport and use document scrolling to scrub the paragraph. At desktop widths of 901px and above, do not force overflow:hidden because the scroll mapping is the primary interaction. The body must have overflow-x:hidden, and the page must never produce horizontal overflow. At widths below 901px, retain normal vertical scrolling. The hero is left aligned to a 62ch measure, positioned in the lower two thirds of the first viewport, while the entire right half remains empty warm black. The hero paragraph uses clamp(28px, 4.6vw, 68px) and line-height 1.08. There is no navigation above the paragraph. A single 11px uppercase navigation line is pinned to the bottom of the first viewport.\n\nDo NOT build a feed, article grid, like button, share control, account menu, carousel, modal, image, video, texture, paper grain, vellum surface, letterpress effect, decorative illustration, logo mark, gradient orb, star field, loading screen, cookie banner, or secondary content section. Do NOT add a hero image or any external media. Do NOT add a second paragraph, rotating testimonials, counters, pricing cards, or a footer CTA. Do NOT use React, Tailwind, GSAP, Three.js, Lottie, WebGL, canvas, SVG filters, remote fonts other than Google Fonts, mp4, webm, gif, remote images, CDN URLs, base64 assets, or third-party scripts. Do NOT use a timer to drive the permanent word reveal. Do not add hidden interactive controls. Every visible control must have an accessible name.\n\nB. THE BACKDROP\n\nUse one body background built from CSS gradients. The base color is #14100E. Add exactly two radial gradients with no external image: radial-gradient(circle at 12% 78%, rgba(255,140,40,0.055) 0%, rgba(255,140,40,0.022) 24%, transparent 54%), and radial-gradient(circle at 4% 92%, rgba(245,239,230,0.025) 0%, transparent 35%). Add one linear overlay: linear-gradient(180deg, rgba(255,255,255,0.012) 0%, transparent 28%, rgba(0,0,0,0.12) 100%). The gradient stops and positions are fixed. Do not animate background-position, blur, opacity, or gradient values.\n\nThere are 18 semantic word groups. Each group contains one base word span and one ember duplicate span. The duplicate is positioned absolutely inside a relatively positioned word group, has the same font metrics and text, and is colored #FF8C28 with text-shadow: 0 0 26px rgba(255,140,40,0.35). The base span is colored #F5EFE6. The base word is at opacity 1 when locked, while its color is visually softened by the surrounding black; the initial perceived unread state is #6B6259 through a controlled opacity ramp beginning at 0.14. The ember duplicate is visible only during the ignition phase and fades to opacity 0 after the word locks.\n\nThe document must have a scroll track height of 180vh for desktop and 220vh for mobile. The first viewport contains the fixed-position hero stage. The word positions are obtained from one getBoundingClientRect() read per animation frame on the paragraph container. The paragraph's document position is cached at startup and on resize, but the live container rect is read exactly once per rAF callback before updating the 18 words. The scroll progress is calculated from window.scrollY and the cached paragraph travel range. Each word uses a 34%-viewport travel window. A word's index offset is 18px of scroll later for every next word, implemented as progressOffset = wordIndex * 18 / viewportHeight. A word's reveal progress is clamp((progress - progressOffset) / 0.34, 0, 1). The base opacity is 0.14 + 0.86 * revealProgress. The ember opacity is a triangular ignition envelope: ramp from 0 to 1 over the first 12% of the word's 34% travel, then ramp from 1 to 0 over the next 12%, with the remaining 10% at 0. The ember duplicate therefore never animates text-shadow itself; only opacity is animated, preserving the performance constraint. Apply opacity to the 18 base spans and 18 ember spans per frame. Do not write any transform in the scrub loop.\n\nThere is no canvas. The canvas count is exactly 0, the canvas resolution is not allocated, and the canvas resolution trick is intentionally not used. The CSS background is resolution independent. The per-frame budget is 18 base opacity writes plus 18 ember opacity writes, one cached scrollY read, one paragraph rect read, and no layout reads for individual words. Use only opacity updates inside the rAF loop. Do not call getBoundingClientRect on any item inside the loop. The target is at least 20 frames per 1.2 seconds on a 1440x900 headless viewport; the 36 opacity writes are inexpensive and the loop must continue while scrolling. When scroll has not changed, the loop may still increment the telemetry frame counter but must not rewrite identical opacity values.\n\nC. THE LAYER STACK\n\nBuild the layers back to front with these exact z-index values:\n\nz-index: -3 for the body gradient background.\nz-index: -2 for the optional non-interactive black vignette overlay, implemented as a body pseudo-element and containing no pointer events.\nz-index: 0 for the document scroll track.\nz-index: 10 for the fixed hero stage.\nz-index: 20 for the eyebrow, hero title, and paragraph inside the stage.\nz-index: 30 for the bottom navigation line.\nz-index: 40 for the focus ring layer created by :focus-visible on the only visible navigation control.\nz-index: 50 for no content; the telemetry object is not rendered.\n\nThe fixed hero stage has pointer-events:none except for the bottom navigation control. The paragraph remains selectable text and must not have pointer-events:none if that prevents text selection; set pointer-events:auto on the paragraph while keeping the decorative duplicate spans pointer-events:none. The body gradient must be fixed and the scroll track must provide actual document height.\n\nD. TOKEN TABLE\n\nDefine these CSS custom properties in :root and use them everywhere:\n\n--bg: #14100E;\n--bone: #F5EFE6;\n--ember: #FF8C28;\n--ash: #6B6259;\n--white: #F5EFE6;\n--muted: rgba(245,239,230,0.58);\n--faint: rgba(245,239,230,0.34);\n--line: rgba(245,239,230,0.14);\n--ember-soft: rgba(255,140,40,0.16);\n--stage-height: 100dvh;\n--track-height: 180vh;\n--measure: 62ch;\n--hero-top: 31vh;\n--hero-left: clamp(28px, 8vw, 144px);\n--hero-bottom: 15vh;\n--hero-gap: 24px;\n--eyebrow-size: 11px;\n--eyebrow-tracking: 0.18em;\n--title-size: clamp(28px, 4.6vw, 68px);\n--title-leading: 1.08;\n--title-weight: 450;\n--title-tracking: -0.035em;\n--nav-size: 11px;\n--nav-tracking: 0.16em;\n--nav-padding-inline: clamp(28px, 8vw, 144px);\n--nav-padding-block: 18px;\n--nav-gap: 28px;\n--ease-out: cubic-bezier(0.16, 1, 0.3, 1);\n--ease-standard: cubic-bezier(0.22, 1, 0.36, 1);\n--duration-load: 600ms;\n--duration-hover: 180ms;\n--stagger: 120ms;\n--word-window: 0.34;\n--word-index-offset-px: 18px;\n--glow-r: 0;\n--glow-g: 140;\n--glow-b: 40;\n--glow-radius: 26px;\n--glow-alpha: 0.35;\n--focus-ring: 0 0 0 2px var(--bg), 0 0 0 4px var(--ember);\n--radius-pill: 999px;\n--radius-control: 2px;\n--max-width: 1440px;\n--header-height: 0px;\n--footer-height: 47px;\n\nThe font stack is \"DM Sans\", \"Helvetica Neue\", Arial, sans-serif for UI, and \"Cormorant Garamond\", Georgia, serif for the paragraph. Load exactly one permitted Google Fonts stylesheet for DM Sans and Cormorant Garamond with weights 400, 500, and 600 for DM Sans and 400, 500 for Cormorant Garamond. If the network request fails, use the listed system fallbacks without blocking first paint.\n\nE. THE COMPOSITION\n\nUse real header, main, and footer elements. The document structure is header#site-header, main#main-content containing section#hero, then footer#site-footer containing nav#bottom-nav. The header has height 0, padding 0, and contains no visible content. It may contain a visually hidden h1? Do not do that: the hero paragraph is the one h1. The h1 is the complete 18-word paragraph, with the exact text: \"A quiet page can hold one thought without asking you to hurry through; read slowly, then carry it.\" The h1 must be the only h1 on the page.\n\nThe main element has min-height:var(--track-height), position:relative, and padding-top:var(--stage-height). The fixed hero stage is position:fixed, inset:0, height:var(--stage-height), width:100vw, and contains a content block with position:absolute, left:var(--hero-left), right:8vw, top:var(--hero-top), bottom:var(--hero-bottom), display:grid, grid-template-rows:auto 1fr, row-gap:var(--hero-gap), align-content:end, max-width:var(--measure). Because the right 8vw remains empty, the content width is capped at 62ch and does not stretch across the viewport. Use a single column; no cards or side illustrations.\n\nThe eyebrow is a p element with class eyebrow. Its exact copy is \"LONGHAND / ISSUE 014\". It is uppercase, font-size:var(--eyebrow-size), line-height:1, letter-spacing:var(--eyebrow-tracking), color:var(--faint), margin:0, and opacity 0 initially. The h1 is the paragraph itself, not a separate short title. Its width is 62ch, max-width:100%, margin:0, color:var(--bone), font-family:\"Cormorant Garamond\", Georgia, serif, font-size:var(--title-size), line-height:var(--title-leading), font-weight:var(--title-weight), letter-spacing:var(--title-tracking), text-wrap:balance, overflow-wrap:break-word. Each word is a span.word with position:relative, display:inline-block, white-space:nowrap. The base span has position:relative, z-index:1, and the ember span has position:absolute, inset:0, z-index:0, pointer-events:none, color:var(--ember), text-shadow:0 0 var(--glow-radius) rgba(var(--glow-r),var(--glow-g),var(--glow-b),var(--glow-alpha)); opacity 0.\n\nThe footer is position:fixed, left:0, right:0, bottom:0, height:var(--footer-height), z-index:30, border-top:1px solid var(--line), background:rgba(20,16,14,0.86), backdrop-filter:blur(8px). The navigation is display:flex, align-items:center, justify-content:space-between, height:100%, padding:0 var(--nav-padding-inline), gap:var(--nav-gap). It contains exactly one visible control, an a element with href=\"#current-issue\", accessible name \"Read the current issue\", and exact copy \"READ THE CURRENT ISSUE ↗\". It also contains a non-interactive span with exact copy \"ONE LONG PIECE / WEEK\". The navigation line is 11px uppercase with letter-spacing var(--nav-tracking). The link uses color:var(--bone), text-decoration:none, border-bottom:1px solid transparent, padding:7px 0. The helper span uses color:var(--faint). The anchor is keyboard focusable and has a visible focus ring.\n\nF. THE UI LANGUAGE\n\nThe only button-like control is the bottom navigation link. It has no filled background. Its default state is transparent with bone text and a 1px transparent bottom border. On hover, change color to var(--ember), border-bottom-color:var(--ember), and transform:translateY(-1px), with transition:color var(--duration-hover) var(--ease-standard), border-color var(--duration-hover) var(--ease-standard), transform var(--duration-hover) var(--ease-standard). On focus-visible, apply the exact focus ring box-shadow var(--focus-ring), outline:none, and retain the link's position. Do not add a pill button. The nav helper is a text label, not a control. All control hit areas are at least 44px high through nav padding and line-height.\n\nG. MOTION\n\nThe permanent scrub is frame-rate bound to scroll. It starts from the page's current scroll position and updates the 18 base and 18 ember opacities from the paragraph's live rect and cached travel distance. It must not use setInterval, a synthetic scroll loop, or an autonomous timer. The initial page state is scrollY 0, with all base words at opacity 0.14 and all ember words at opacity 0.\n\nThe load animation is once-only and applies to the eyebrow and the h1 as two elements. The eyebrow animates from opacity 0, transform:translateY(18px), to opacity 1, transform:translateY(0), over 600ms with ease-out. The h1 follows after a 120ms delay using the same 600ms transform and opacity animation. Do not animate individual word shadows during load. The scrub may begin immediately and may override the h1's opacity only for the word spans, not the h1 container.\n\nStagger table:\n\neyebrow -> rise-and-fade -> delay 0ms\nh1 -> rise-and-fade -> delay 120ms\nbase word 0 through word 17 -> scroll scrub -> no timer delay; index offset 0px through 306px\nember word 0 through word 17 -> scroll scrub ignition envelope -> same index offset as its base word\nbottom navigation -> fade-in -> delay 240ms, 360ms duration\n\nNamed keyframes:\n\n@keyframes riseAndFade { from { opacity:0; transform:translateY(18px); } to { opacity:1; transform:translateY(0); } }\n@keyframes navFade { from { opacity:0; transform:translateY(8px); } to { opacity:1; transform:translateY(0); } }\n\nUse animation-fill-mode:both. Remove the load animation from the h1 after 720ms only if doing so does not affect the scrubbed child opacities. The footer must remain readable if animation is interrupted.\n\nH. THE RESPONSIVE TABLE\n\nAt 390x844, set --track-height:220vh; --hero-left:24px; --hero-top:24vh; --hero-bottom:96px; --hero-gap:16px; --measure:100%; --title-size:clamp(32px, 10vw, 48px); --title-leading:1.12; --eyebrow-size:10px; --nav-padding-inline:24px; --nav-padding-block:16px; --nav-gap:16px; --footer-height:64px. The navigation wraps to two rows only if required by width, using flex-wrap:wrap and row-gap:6px, with the link first and the helper second. Keep all text inside the viewport.\n\nAt 768x1024, set --track-height:200vh; --hero-left:56px; --hero-top:28vh; --hero-bottom:92px; --hero-gap:20px; --measure:62ch; --title-size:clamp(40px, 7vw, 60px); --nav-padding-inline:56px; --footer-height:56px. Keep the fixed stage height 100dvh.\n\nAt 1440x900, set --track-height:180vh; --hero-left:clamp(72px, 8vw, 144px); --hero-top:31vh; --hero-bottom:15vh; --measure:62ch; --title-size:clamp(28px, 4.6vw, 68px); --footer-height:47px. The hero must occupy the left 62ch and leave the right half empty.\n\nAt 1920x1080, set --hero-left:clamp(96px, 8vw, 176px); --hero-top:33vh; --hero-bottom:16vh; --measure:62ch; --title-size:68px; --track-height:180vh; --footer-height:47px. Do not scale the paragraph above 68px.\n\nAdd a max-height:760px breakpoint for short desktop windows: set --hero-top:18vh; --hero-bottom:80px; --title-size:clamp(28px, 4.2vw, 54px); --title-leading:1.1; --footer-height:47px. Add a max-height:600px breakpoint: set --hero-top:12vh; --hero-bottom:72px; --hero-gap:10px; --eyebrow-size:10px; --title-size:clamp(26px, 4vw, 44px); --title-leading:1.06; --footer-height:43px; --nav-padding-block:12px. The short-height overrides must preserve the full paragraph and bottom navigation.\n\nI. FAILURE GUARDS\n\nAll visible resting elements must have opacity 1 after their one-time entrance animation, except the scroll-controlled word base spans and ember duplicates. The h1, eyebrow, footer, and navigation must never remain hidden if JavaScript fails. Put the default eyebrow opacity at 1 in CSS and apply the entrance animation through a class added by JavaScript, so pre-JS content is readable. The base word spans must default to opacity 0.14 and the ember duplicates to opacity 0. The page must be usable before JavaScript executes.\n\nUse a two-rAF fallback. After first paint, requestAnimationFrame(function(){ requestAnimationFrame(function(){ telemetry.ready = true; }); });. In the main animation loop, after the first successful frame, set ready true if it is still false. If requestAnimationFrame is unavailable, run one opacity initialization call and set ready true without an exception. Wrap all JavaScript setup in try/catch, push caught messages into telemetry.errors, and do not log to the console.\n\nImplement prefers-reduced-motion with @media (prefers-reduced-motion: reduce). Set animation:none, transition:none, scroll-behavior:auto. Set the eyebrow, h1, and footer to opacity 1 and transform:none. Set every base word to opacity 1 and every ember duplicate to opacity 0. Preserve the scroll-driven opacity mapping for base words, because it communicates reading position, but remove the load and hover transitions. The reduced-motion page must remain static and readable, with all 18 words at opacity 1 and no glow.\n\nJ. THE EXACT JS\n\nPlace this exact script at the end of body:\n\n<script>\n(() => {\n  \"use strict\";\n  const telemetry = { name: \"smolder\", ready: false, frames: 0, effects: [], errors: [] };\n  window.__DEMO__ = telemetry;\n  window.addEventListener(\"error\", event => {\n    telemetry.errors.push(String(event.message || \"window error\"));\n  });\n\n  const reduce = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\n  const hero = document.getElementById(\"hero\");\n  const paragraph = document.getElementById(\"opening-paragraph\");\n  const footer = document.querySelector(\"#site-footer\");\n  const wordNodes = paragraph ? Array.from(paragraph.querySelectorAll(\".word\")) : [];\n  const baseNodes = wordNodes.map(node => node.querySelector(\".base-word\"));\n  const emberNodes = wordNodes.map(node => node.querySelector(\".ember-word\"));\n  let cachedTop = 0;\n  let cachedTravel = 1;\n  let lastScrollY = -1;\n  let lastProgress = -1;\n  let rafId = 0;\n\n  telemetry.effects.push(\"load-rise-fade\", \"scroll-word-reveal\", \"ember-ignition\", \"ember-cooling\");\n\n  function measure() {\n    if (!paragraph) return;\n    const rect = paragraph.getBoundingClientRect();\n    const viewportHeight = Math.max(1, window.innerHeight);\n    cachedTop = rect.top + window.scrollY;\n    cachedTravel = Math.max(1, paragraph.offsetHeight + viewportHeight * 0.34);\n  }\n\n  function clamp(value, min, max) {\n    return Math.max(min, Math.min(max, value));\n  }\n\n  function updateWords() {\n    if (!paragraph || !wordNodes.length) return;\n    const rect = paragraph.getBoundingClientRect();\n    const viewportHeight = Math.max(1, window.innerHeight);\n    const progress = clamp((viewportHeight * 0.34 - rect.top) / cachedTravel, 0, 1);\n    if (Math.abs(progress - lastProgress) < 0.0001 && lastScrollY === window.scrollY) return;\n    lastProgress = progress;\n    lastScrollY = window.scrollY;\n\n    for (let index = 0; index < wordNodes.length; index += 1) {\n      const offset = index * 18 / viewportHeight;\n      const local = clamp((progress - offset) / 0.34, 0, 1);\n      const ignitionIn = clamp(local / 0.3529, 0, 1);\n      const cooling = clamp((local - 0.3529) / 0.3529, 0, 1);\n      const ember = ignitionIn >= 1 ? 1 - cooling : ignitionIn;\n      baseNodes[index].style.opacity = String(0.14 + local * 0.86);\n      emberNodes[index].style.opacity = reduce.matches ? \"0\" : String(ember);\n    }\n  }\n\n  function frame() {\n    telemetry.frames += 1;\n    updateWords();\n    if (!telemetry.ready) telemetry.ready = true;\n    rafId = window.requestAnimationFrame(frame);\n  }\n\n  function start() {\n    try {\n      measure();\n      updateWords();\n      if (footer) footer.classList.add(\"is-ready\");\n      requestAnimationFrame(() => requestAnimationFrame(() => {\n        telemetry.ready = true;\n      }));\n      rafId = window.requestAnimationFrame(frame);\n    } catch (error) {\n      telemetry.errors.push(String(error && error.message ? error.message : error));\n      telemetry.ready = true;\n    }\n  }\n\n  window.addEventListener(\"resize\", measure, { passive: true });\n  window.addEventListener(\"scroll\", measure, { passive: true });\n  start();\n})();\n</script>\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["The delivered artifact is one valid index.html with no external media and contains exactly one h1 containing the exact 18-word Longhand paragraph.","At 1440x900, the document has vertical scroll height of 180vh, the hero measure is capped at 62ch, and the right half contains no visible content or controls.","Scrolling changes the 18 base word opacities from 0.14 toward 1, with each word offset by 18px relative to the previous word and ember duplicates returning to opacity 0 after cooling.","The page exposes window.__DEMO__ with name smolder, ready true after first paint, an incrementing frames value, four named effects, and no uncaught window errors during load and scroll.","With prefers-reduced-motion enabled, all entrance and hover transitions are disabled, all 18 words are readable, and the bottom navigation link retains an accessible name and 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"],"usage":{"build_tokens":48480,"build_prompt_tokens":5941,"build_completion_tokens":42539,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":650429,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":13583,"total_tokens":62063},"verification":{"passed":true,"passed_checks":17,"total_checks":17,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"17607 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"45 frames, effects: load-rise-fade, scroll-word-reveal, ember-ignition, ember-cooling"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"77 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"A\nA\n \nquiet\nquiet\n \npage\npage\n \ncan\ncan\n \nhold\nhold\n \none\non\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Longhand — One long thought, every week"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=300 pageErrors=0"}]},"crosscheck":{"slug":"smolder","reviewer_model":"gpt-5.4","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":6,"requirements_met":["Single-screen editorial composition with warm-black gradient backdrop","Exact hero copy and eyebrow copy are present","Exactly one h1 used as the paragraph","Bottom navigation line is fixed to the viewport bottom","No extra sections, cards, images, or decorative media were added","Typography generally follows the requested serif/sans split","Footer nav uses the requested link text and helper label"],"requirements_missed":["Hero does not leave the right half empty at desktop size","Scroll-mapping technique does not match the brief's exact implementation","Cached paragraph position is not actually used for progress calculation","Paragraph rect is read outside the rAF loop on scroll events","Reduced-motion behavior conflicts with the brief's stated static readable state","Load animation is triggered via :has() on the body instead of a direct JS-added class approach described in the brief"],"issues":[{"issue":"Hero paragraph occupies too much horizontal space; the right half is not kept empty","severity":"high","evidence":"In the 1024x640 screenshot the two-line paragraph stretches far into the right side, leaving only a narrow empty strip. The brief required 'the entire right half remains empty warm black' and at desktop 'leave the right half empty.'"},{"issue":"Opening paragraph is rendered extremely dim in the captured composition","severity":"medium","evidence":"The screenshot shows the h1 nearly disappearing into the background at top-of-page. While the base words begin at 0.14 opacity, the result here is close to illegible rather than editorially softened."},{"issue":"Scroll progress is not calculated from window.scrollY and cached paragraph travel range as specified","severity":"high","evidence":"JS uses `const progress = clamp((viewportHeight * 0.34 - rect.top) / cachedTravel, 0, 1);` and never uses `cachedTop` after measuring, despite the brief requiring progress from `window.scrollY` and cached paragraph travel range."},{"issue":"Paragraph rect is read outside the animation frame loop on scroll","severity":"medium","evidence":"The brief required the paragraph document position to be cached at startup and on resize, with one live container rect read per rAF. This code adds `window.addEventListener('scroll', measure, { passive: true });`, and `measure()` calls `paragraph.getBoundingClientRect()`."},{"issue":"Reduced-motion implementation contradicts the requested static readable state","severity":"medium","evidence":"The CSS says `.base-word { opacity: 1 !important; }`, but the JS still runs and writes per-word opacities because `updateWords()` continues in reduced motion. The brief explicitly asked for a static readable page with all 18 words at opacity 1 and no glow."},{"issue":"Entrance animation wiring does not follow the stated failure-guard pattern","severity":"low","evidence":"The brief said to apply entrance animation 'through a class added by JavaScript'; this build instead relies on `body:has(#site-footer.is-ready) .eyebrow` and `body:has(#site-footer.is-ready) #opening-paragraph`."}],"summary":"The page hits the general mood and restraint, but the desktop composition misses the main spatial requirement: the paragraph intrudes too far into the right side instead of preserving an empty half-frame. Under the hood, the scroll technique also does not match the brief's exact method, so this is not a faithful implementation.","usage":{"input":12189,"output":1394,"total":13583,"reasoning":554,"cached":0},"latency_ms":17070}},{"slug":"solstice","title":"Solstice","category":"Landing Page","effect":"A 60-second solar arc continuously remaps the page from dawn amber through noon white-blue to dusk magenta.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/solstice/index.html","doc":"/prompts/solstice.md","prompt_text":"A. WHAT IS BEING BUILT\n\nBuild one complete, valid index.html for a cinematic single-viewport landing page named “SOLSTICE”. The document title must be “SOLSTICE — Solar Energy, From First Light to Full Potential”. Set the root language to lang=\"en\". The visual subject is a solar-energy company whose day begins in amber dawn, reaches white-blue solar noon, and closes in magenta dusk while the sun travels from left to right on a wide 60-second arc. The interface must communicate dependable energy, precision, and continuous operation rather than a consumer electricity shop.\n\nThe page structure must use one real header element, one nav element, one main element, one real footer element, and exactly one h1. The h1 contains two spans: the eyebrow line “ENERGY IN MOTION” and the company line “SOLSTICE”; the company line is the page’s principal title. Use semantic section elements for the hero copy, telemetry cluster, primary actions, and status rail. Every visible link, button, and clock remains a real interactive or semantic element with an accessible name. Decorative SVG, gradients, and generated particles must use aria-hidden=\"true\".\n\nDesktop viewport rule: from 901px through 1920px, set html and body to width:100%; height:100%; and the document shell to min-height:100dvh; height:100dvh; overflow:hidden. The only allowed vertical scrolling is at 900px and below, where set height:auto, min-height:100dvh, overflow-x:hidden, and allow normal document scrolling. At every tested width, html, body, and the document shell must have width:100% and max-width:100%; no child may extend beyond the viewport. Prevent horizontal overflow with overflow-x:clip on the page shell, with overflow-x:hidden as the fallback.\n\nResponsive targets are 390x844, 768x1024, 1440x900, and 1920x1080. The implementation must remain legible and free of clipped text at all four sizes. The single rAF visual system must sustain at least 20 frames per 1.2 seconds on a 1440x900 headless viewport. Cache all DOM reads outside the loop. In the loop, animate only transform and opacity. Do not read layout measurements in the loop and do not call getBoundingClientRect, offsetWidth, offsetHeight, getComputedStyle, canvas measureText, or querySelector from the loop.\n\nDo NOT build a navigation drawer, hamburger menu, account area, pricing table, product marketplace, article grid, form, modal, cookie banner, authentication flow, logo carousel, third-party map, image gallery, or secondary page. Do not add a second h1. Do not use React, Tailwind, GSAP, Three.js, Lottie, WebGL, remote media, mp4, webm, animated GIF, remote image, base64 image, icon font, analytics request, or third-party CDN. The only permitted network request is one Google Fonts stylesheet for “DM Sans” at weights 400, 500, 600 and “IBM Plex Mono” at weights 400, 500, loaded with display=swap. Include robust system fallbacks. The resulting file must be under 90 KB and produce no console output.\n\nB. THE BACKDROP\n\nCreate a full-viewport .backdrop fixed at inset:0 with z-index:0 and overflow:hidden. It must contain 24 deterministic CSS-drawn star or dust points, 9 large atmospheric cloud ellipses, 1 SVG horizon system, 1 canvas particle field, and 1 canvas sun/reflection compositor. All visible imagery is CSS, SVG, or canvas. Do not use external images.\n\nThe page atmosphere has exactly 12 radial-gradient and linear-gradient layers. Its base background must be a linear gradient to bottom. The initial dawn gradient stops are 0% #24152d, 32% #5a2942, 58% #c55245, 78% #f59e54, 92% #fed7a1, and 100% #fff0c7. The noon gradient stops are 0% #123a67, 30% #276b98, 55% #8fcbe3, 78% #dff4fb, 92% #fff9df, and 100% #ffffff. The dusk gradient stops are 0% #18152f, 30% #3d244e, 54% #773356, 76% #bd4768, 91% #e46b6d, and 100% #f7a56f. At runtime, interpolate all six stop colors from dawn to noon during progress 0.000–0.500, then from noon to dusk during progress 0.500–1.000. Convert each #RRGGBB component to a normalized channel, perform linear RGB interpolation, clamp the result to 0–255, and round to the nearest integer. Reassign the complete background string at most 30 times per second, every 33ms, while the sun transform updates every animation frame. This is a scripted compositor update, not layout measurement.\n\nAdd a full-width upper glow with a radial-gradient centered at the sun’s x position and 50% y position. Dawn center color is rgba(255,183,91,0.88), noon is rgba(255,255,231,0.98), and dusk is rgba(242,91,122,0.82). At rAF time, set CSS custom properties --sun-x and --glow-x and rewrite the glow background string. The glow uses blur-radius equivalent 70px through one absolutely positioned ellipse with filter:blur(38px); its opacity is 0.72. A second narrow horizon bloom is a 12px-high ellipse centered on the horizon with blur radius 24px and opacity 0.68.\n\nThe 24 stars have opacity 0.16–0.54, diameter 1–3px, and individual drift periods between 36s and 72s. Their horizontal travel ranges from 0px to 16px; vertical travel ranges from 0px to 8px. They loop without resizing. The 9 cloud ellipses use widths from 280px to 760px, heights from 32px to 88px, blur from 18px to 44px, horizontal opacity 0.08–0.20, and drift periods from 70s to 118s. Cloud drift is transform-only and stays within x:-220px to x:220px relative to its placement.\n\nThe horizon is one SVG with viewBox=\"0 0 1920 1080\" and preserveAspectRatio=\"none. It contains exactly one hard line from x=0 to x=1920 at y=742. The line uses stroke-width:1, vector-effect:non-scaling-stroke. It appears #fff2ce at dawn, #e8fbff at noon, and #ffc0a8 at dusk; interpolate this color in the same palette update. Beneath it, use one SVG rect of height 338 with a 42px vertical blur filter and a 52px linear gradient whose top color is the interpolated horizon color at 0.36 alpha and whose final color is transparent. Do not blur the hard line itself.\n\nThe canvas compositor is 480x270 CSS pixels by default, backing-store resolution 960x540 using devicePixelRatio clamped to 2, so it stays below 1036800 pixels at 1920x1080. At smaller viewports, set backing-store width to floor(cssWidth * 2) and height to floor(cssHeight * 2), with those totals capped at 960x540. Scale with CSS width:100%; height:100%. Redraw it when its backing resolution changes and when the 33ms palette tick changes. Precompute all 72 particles as objects containing normalized x, base y, radius, speed, amplitude, phase, and a precomputed warm/cool tint. The sun uses a 22px-radius core, a 7px amber-white corona ring, and a 64px radial corona at 0.16 alpha in the SVG or DOM layer. Its reflection is a canvas ellipse at the same normalized x, below the horizon, with radiusX 15px, radiusY 4px, vertical scale 0.24, and blur 12px represented by three concentric translucent ellipses rather than ctx.filter. Precompute color strings when the palette changes. Per frame, the rAF handler performs 72 cheap transform-equivalent canvas redraws only when a new palette bucket is active, and the visible sun, reflection, glow, and horizon wrapper are moved using cached normalized coordinates. The per-frame work budget is no more than 1 style write for sun transform, 1 style write for reflection transform, 1 style write for glow x, and 1 canvas redraw only on 33ms boundaries. Target 72 or fewer canvas path fills and 25 or fewer color string updates per 33ms palette bucket.\n\nC. THE LAYER STACK\n\nBuild the visual and content stack back to front with these exact z-index values:\n\nz-index 0: .backdrop, containing the base gradient, stars, clouds, upper atmospheric bloom, and solar glow.\nz-index 1: .star-field with 24 generated children.\nz-index 2: .cloud-field with 9 generated children.\nz-index 3: .solar-system, containing the .sun-disc, its two corona rings, and its .sun-reflection.\nz-index 4: .horizon-svg with the soft bloom rect and hard 1px line.\nz-index 5: .particle-canvas.\nz-index 6: .vignette, a pointer-events:none radial overlay that darkens all edges by 18%.\nz-index 10: header.site-header.\nz-index 20: main.hero.\nz-index 30: footer.site-footer and the bottom status rail.\nz-index 40: .focus-halo if required, though prefer native outline.\nz-index 50: .grain, an inline SVG feTurbulence texture at opacity 0.035, pointer-events:none.\n\nThe header, main, and footer must be position:relative with their declared z-index. The canvas and horizon are aria-hidden. Ensure pointer events do not obstruct controls. Decorative layers must not be focusable.\n\nD. TOKEN TABLE\n\nDefine every reusable visual value in :root using these exact tokens:\n\n--ink:#fdfaf3;\n--ink-muted:rgba(253,250,243,0.68);\n--ink-dim:rgba(253,250,243,0.46);\n--accent:#fb923c;\n--accent-bright:#ffd29a;\n--accent-deep:#b7432f;\n--line:rgba(255,255,255,0.22);\n--glass:rgba(17,23,39,0.24);\n--glass-strong:rgba(13,18,32,0.58);\n--focus:#fff4c7;\n--header-h:72px;\n--footer-h:72px;\n--page-x:clamp(24px,5.2vw,100px);\n--page-y:clamp(28px,4.4vh,44px);\n--content-max:1760px;\n--hero-columns:minmax(0,1.35fr) minmax(320px,0.65fr);\n--hero-gap:clamp(36px,7vw,128px);\n--h1-size:clamp(64px,8.4vw,150px);\n--h1-line:0.82;\n--h1-tracking:-0.075em;\n--eyebrow-size:11px;\n--eyebrow-tracking:0.28em;\n--lede-size:clamp(16px,1.18vw,20px);\n--lede-line:1.55;\n--lede-width:560px;\n--button-h:54px;\n--button-pad-x:25px;\n--button-radius:999px;\n--pill-pad-x:14px;\n--pill-h:32px;\n--clock-size:12px;\n--clock-tracking:0.14em;\n--card-w:356px;\n--card-pad:24px;\n--radius-card:18px;\n--radius-control:999px;\n--sun-size:44px;\n--reflection-w:32px;\n--reflection-h:8px;\n--horizon-y:68.7%;\n--motion-fast:180ms;\n--motion-base:520ms;\n--motion-slow:900ms;\n--ease-out:cubic-bezier(0.16,1,0.3,1);\n--ease-arc:cubic-bezier(0.22,0.68,0.18,1);\n--safe-bottom:env(safe-area-inset-bottom,0px);\n--safe-right:env(safe-area-inset-right,0px);\n--safe-left:env(safe-area-inset-left,0px).\n\nDo not introduce off-token typography, spacing, color, or sizing for major components. Sub-components may use 4px multiples or explicit pixel values documented in the composition.\n\nE. THE COMPOSITION\n\nCreate a .site-shell with max-width:1920px; margin-inline:auto; position:relative; min-height:100dvh; background:transparent. At desktop, the shell forms a 72px header, flexible hero, and 72px footer using display:grid; grid-template-rows:var(--header-h) minmax(0,1fr) var(--footer-h).\n\nThe header is a real header.site-header with height:72px; display:grid; grid-template-columns:1fr auto 1fr; align-items:center; padding:0 var(--page-x). The left anchor is an accessible home link named “SOLSTICE home”; its visible text is “SOLSTICE” in 13px, 600 weight, letter-spacing:0.18em. The middle nav contains exactly two links: “Mission” and “Technology”. Right-align a visible status pill containing a 6px green dot and the exact text “GRID ONLINE”. Give the nav 8px gap. On widths below 701px, hide the center nav with display:none; keep the logo and status pill.\n\nThe main.hero occupies the center grid row and uses max-width:var(--content-max); width:100%; margin:0 auto; padding:var(--page-y) var(--page-x); display:grid; grid-template-columns:var(--hero-columns); align-items:center; gap:var(--hero-gap). Left hero copy contains exactly this hierarchy and copy: eyebrow “ENERGY IN MOTION”; the single h1 with “SOLSTICE”; lede “Capture the day’s brightest hours. Solstice turns clean solar power into steady, intelligent energy for every horizon.”; CTA row with a primary link named “Explore our technology” with visible label “EXPLORE TECHNOLOGY” and a secondary link named “See our mission” with visible label “OUR MISSION”. The h1 is 1–150px according to the responsive tokens. Its eyebrow span is 11px, 500 weight, uppercase, 0.28em tracking, and must be positioned as a block above the company line. The lede max-width is 560px. Buttons sit in a 12px gap flex row.\n\nThe right hero instrument is a 356px wide .instrument-card with 24px padding, 18px radius, border:1px solid rgba(255,255,255,0.18), background:linear-gradient(145deg,rgba(14,20,38,0.56),rgba(14,20,38,0.22)), backdrop-filter:blur(18px), box-shadow:0 24px 70px rgba(7,10,24,0.24). It contains a top row labelled “LOCAL SOLAR TIME” and a 12px monospace simulated clock with exact initial text “05:42:18”. The clock has aria-label=\"Simulated local solar time\"; do not mark it aria-live. Add a horizontal 1px separator with 18px vertical margins. Add a 184px-wide arc mini-chart rendered as inline SVG: one 0.8px background arc, one 2px accent arc with stroke-dasharray:289 and stroke-dashoffset:289, and a 5px moving sun marker. Beneath it, place three numeric rows: “CAPTURE” with value “24.8 GWh”, “PEAK OUTPUT” with value “1.42 GW”, and “CO₂ AVOIDED” with value “18,604 t”. Each label is 9px uppercase with 0.18em tracking; each value is 15px monospace. Add a footer pill with exact text “98.7% AVAILABILITY”.\n\nThe bottom footer is a real footer.site-footer with height:72px; padding:0 var(--page-x); display:grid; grid-template-columns:1fr auto 1fr; align-items:center; border-top:1px solid var(--line); background:linear-gradient(to top,rgba(7,10,24,0.36),transparent). Left text is “POWERED BY THE SUN”. Center is a status rail containing “DAWN”, a 54px line, “NOON”, and “DUSK”; its active label changes by phase. Right text is “40.7128° N / 74.0060° W”. Use 10px monospace uppercase text and 0.18em tracking.\n\nThe hard horizon visually crosses at 68.7% viewport height. Keep it behind content but above the lower fields. Place the sun arc from x=8% at y=79% through x=50% at y=17% to x=92% at y=79%. The reflection’s center is x equal to the sun x and y equal to 74.5% plus 7.5% of the sun’s altitude loss. Its opacity is 0.20 at dawn, 0.14 at noon, and 0.25 at dusk.\n\nF. THE UI LANGUAGE\n\nThe primary link is 54px high with 25px horizontal padding, 999px radius, 12px monospace font, 600 weight, 0.12em tracking, and no visible border. Its exact resting gradient is linear-gradient(135deg,#fff1ce 0%,#ffd09a 48%,#fb923c 100%); text is #2a1730. On hover, transform:translateY(-2px); background:linear-gradient(135deg,#ffffff 0%,#ffe4bd 48%,#ffad65 100%); box-shadow:0 10px 28px rgba(251,146,60,0.30). On active, transform:translateY(0); box-shadow:none. Its focus-visible ring is 3px solid var(--focus), offset 4px.\n\nThe secondary link is 54px high, 25px horizontal padding, 999px radius, 1px solid rgba(255,255,255,0.52), background:rgba(12,18,34,0.18), text #fffaf1. On hover, background:rgba(255,255,255,0.12), border-color:rgba(255,255,255,0.82), transform:translateY(-2px). On active, transform:translateY(0).\n\nThe status pill is 32px high with 14px horizontal padding, 999px radius, border:1px solid rgba(255,255,255,0.22), background:rgba(11,17,32,0.26), and 10px monospace uppercase text. The online dot is 6px, #9ff0bb, with box-shadow:0 0 12px rgba(159,240,187,0.74).\n\nAll links use color:inherit, text-decoration:none, cursor:pointer. Every interactive target has a minimum 44px hit area, including header links whose visual height is smaller. Header link hover opacity transitions from 1 to 0.72 over 180ms. Global focus-visible is outline:3px solid var(--focus); outline-offset:4px. Do not remove outline without replacing it.\n\nG. MOTION\n\nUse the following initial stagger. Delays start after the first paint. Elements must already be present and readable.\n\nHeader logo: opacity 0 to 1 and translateY(-10px) to translateY(0), 520ms, delay 80ms.\nHeader nav links: opacity 0 to 1 and translateY(-8px) to translateY(0), 520ms, delays 140ms and 190ms.\nGrid status pill: opacity 0 to 1 and scale(0.96) to scale(1), 520ms, delay 220ms.\nHero eyebrow: opacity 0 to 1 and translateY(16px) to translateY(0), 720ms, delay 100ms.\nH1 company line: opacity 0 to 1 and translateY(28px) to translateY(0), 900ms, delay 160ms.\nHero lede: opacity 0 to 1 and translateY(18px) to translateY(0), 720ms, delay 300ms.\nPrimary CTA: opacity 0 to 1 and translateX(-16px) to translateX(0), 720ms, delay 410ms.\nSecondary CTA: opacity 0 to 1 and translateX(-16px) to translateX(0), 720ms, delay 470ms.\nInstrument card: opacity 0 to 1 and translateY(22px) to translateY(0) and scale(0.98) to scale(1), 900ms, delay 280ms.\nFooter status rail: opacity 0 to 1 and translateY(10px) to translateY(0), 720ms, delay 520ms.\nFooter metadata: opacity 0 to 1, 720ms, delay 600ms.\n\nNamed keyframes must be enter-up, enter-right, reveal-card, and drift-cloud. enter-up runs from opacity:0; transform:translateY(22px) to opacity:1; transform:translateY(0). enter-right runs from opacity:0; transform:translateX(-18px) to opacity:1; transform:translateX(0). reveal-card runs from opacity:0; transform:translateY(22px) scale(0.98) to opacity:1; transform:translateY(0) scale(1). drift-cloud alternates 0%, 100%, and 0% translate positions defined per child. Apply animation-fill-mode:both. Do not animate height, width, top, left, filter, background-position, or box-shadow continuously in the rAF loop.\n\nThe solar cycle duration is 60000ms. Progress starts at 0 on load and advances from elapsed modulo 60000 divided by 60000. Sun x is lerp from 0.08 to 0.92. Sun y follows a quarter ellipse: y=0.79-0.62*sqrt(max(0,1-pow((x-0.5)/0.42,2))). This places the apex at progress 0.5. The active phase label is DAWN for p<0.25, NOON for p from 0.25 through 0.75, and DUSK for p>0.75. The clock maps p=0 to 05:42:00 and p=0.5 to 12:00:00 and p=1 to 18:18:00, using piecewise linear interpolation in minutes. Format as HH:MM:SS.\n\nH. RESPONSIVE TABLE\n\nAt 1440x900 and 1920x1080, use the default tokens. Keep the hero centered, card width 356px, header/footer 72px, and page-x clamp values.\n\nAt 768x1024 and all widths from 901px to 1100px, set --page-x:42px; --page-y:30px; --hero-columns:minmax(0,1.2fr) minmax(290px,0.8fr); --hero-gap:42px; --card-w:310px; --card-pad:20px; --h1-size:92px; --lede-size:17px. Footer status rail remains visible. At widths 901–1100, header nav gap is 6px and header status pill hides only its text through display:none inside the pill while preserving the named anchor if that pill is interactive; because the status pill is non-interactive, hide the entire pill and its adjacent gap with display:none.\n\nAt max-width:900px, set html, body, and .site-shell to height:auto, min-height:100dvh, overflow:visible; set .site-header height:64px; grid-template-columns:1fr auto; padding-inline:22px. Set .site-footer height:auto, min-height:72px; padding:20px 22px calc(20px + var(--safe-bottom)); grid-template-columns:1fr auto; row-gap:16px. Set main.hero height:auto; grid-template-columns:1fr; gap:32px; padding:54px 22px 46px. Set --h1-size:78px; --page-x:22px; --card-w:min(100%,420px); --instrument-card max-width:100%. Put the instrument after the copy. The horizon remains fixed and follows the viewport while the document scrolls.\n\nAt max-width:600px, set --page-x:18px; --page-y:22px; --h1-size:clamp(58px,19vw,82px); --eyebrow-size:10px; --lede-size:16px; --lede-line:1.5. Header padding-inline:18px; footer padding-inline:18px. Hero padding:46px 18px 40px. CTA row is display:grid; grid-template-columns:1fr; width:min(100%,340px); gap:10px; both controls width:100%. Footer metadata spans both columns on its own row; footer grid-template-columns:1fr. Instrument width:100%, max-width:420px, and min-width:0. Status rail display:none. Header center nav remains display:none.\n\nAt max-height:760px and min-width:901px, set --page-y:20px; --button-h:48px; --card-pad:18px; --h1-size:104px; --lede-size:16px. Header and footer remain 64px and 64px. Hero grid is align-items:center; instrument arc chart height is 154px; three telemetry rows use 13px vertical padding.\n\nAt max-height:680px and min-width:901px, set --page-y:14px; --header-h:60px; --footer-h:60px; --button-h:44px; --card-pad:15px; --card-w:310px; --h1-size:82px; --lede-size:15px; --lede-line:1.38. Reduce eyebrow top/bottom spacing by 6px, CTA top margin to 24px, card row gaps to 9px, and mini-chart to 128px. If content would exceed 600px of hero height, allow only the main hero to scroll internally; keep header and footer fixed to the shell.\n\nI. FAILURE GUARDS\n\nEvery entry animation must start with opacity 0 only within a .motion-ready wrapper, and after animation completion its computed opacity must be 1. If a CSS animation is unsupported, failed, or cancelled, JavaScript must clear animation-name and set opacity 1 and transform:none on all [data-enter] elements after 1800ms. Resting opacity for all visible content must be 1.\n\nThe 60-second cycle must start only after first paint. Wrap runtime setup in requestAnimationFrame, then requestAnimationFrame, then call startRuntime(). This two-rAF fallback guarantees CSS entry animations and initial paint have begun before the telemetry object is marked ready. A missing canvas 2D context must not throw: hide only the canvas particle layer, keep CSS sun, reflection, gradient, and clock active, push “canvas-unavailable” into effects, and continue the rAF loop. Missing document.fonts must not block readiness.\n\nInside the prefers-reduced-motion:reduce block, set all *, *::before, and *::after to animation-duration:0.001ms!important; animation-iteration-count:1!important; transition-duration:0.001ms!important; scroll-behavior:auto!important. Set [data-enter], .star, .cloud, .grain to animation:none!important; opacity:1!important; transform:none!important. Freeze the sun at x=0.50, y=0.17, palette at noon, clock at 12:00:00, and the mini-chart at noon. Continue one requestAnimationFrame telemetry loop only if necessary to increment frames, but do not repaint changing visual state. The page must remain complete and readable.\n\nJ. THE EXACT JS\n\nPlace one script before </body>. Use “use strict”. Create the exact telemetry object first with:\nwindow.__DEMO__ = { name: 'solstice', ready: false, frames: 0, effects: [], errors: [] };\nRegister window error handling immediately, with preventDefault disabled and no console output. Push String(event.message || 'window-error') into window.__DEMO__.errors. Register unhandledrejection similarly. Register each completed entry animation by listening for animationend on [data-enter], pushing “enter-ui” once. Push “palette-cycle”, “solar-arc”, “canvas-particles”, and “reduced-motion-static” when those systems start. Do not push duplicate effect names.\n\nCache the following once in startRuntime(): document.documentElement, body, backdrop, sun, corona, reflection, glow, miniMarker, arc, card, clock, phaseLabel, canvas, and a precomputed NodeList of stars, clouds, and [data-enter] elements. Use cached viewport dimensions from innerWidth and innerHeight once per resize event, never inside the loop. The resize handler is passive and only calls resizeCanvas(), which writes canvas dimensions and cached cssWidth/cssHeight values.\n\nUse 72 deterministic particles generated with a seeded integer function:\nlet seed=73421;\nfunction rand(){ seed=(seed*1664525+1013904223)>>>0; return seed/4294967296; }\nStore particle fields as x=rand(), y=0.18+rand()*0.67, r=0.4+rand()*1.8, speed=0.004+rand()*0.012, amp=2+rand()*7, phase=rand()*6.283185307, alpha=0.10+rand()*0.34. Precompute no per-frame object allocations.\n\nUse these exact helpers:\nconst clamp=(v,a,b)=>Math.min(b,Math.max(a,v));\nconst lerp=(a,b,t)=>a+(b-a)*t;\nconst mixColor=(a,b,t)=>'rgb('+Math.round(lerp(a[0],b[0],t))+','+Math.round(lerp(a[1],b[1],t))+','+Math.round(lerp(a[2],b[2],t))+')';\nconst rgba=(c,a)=>'rgba('+c[0]+','+c[1]+','+c[2]+','+a+')';\nconst smooth=(t)=>t*t*(3-2*t);\nconst DAWN=[36,21,45], NOON=[18,58,103], DUSK=[24,21,47];\nconst DAWN_LOW=[245,158,84], NOON_LOW=[220,244,251], DUSK_LOW=[228,107,109];\nconst GLOW_DAWN=[255,183,91], GLOW_NOON=[255,255,231], GLOW_DUSK=[242,91,122];\nconst REF_DAWN=[255,190,103], REF_NOON=[240,253,255], REF_DUSK=[247,112,128];\n\npaletteAt(p) must return p<=0.5 ? {low:mixColor(DAWN_LOW,NOON_LOW,smooth(p*2)),glow:mixColor(GLOW_DAWN,GLOW_NOON,smooth(p*2)),ref:mixColor(REF_DAWN,REF_NOON,smooth(p*2))} : {low:mixColor(NOON_LOW,DUSK_LOW,smooth((p-0.5)*2)),glow:mixColor(GLOW_NOON,GLOW_DUSK,smooth((p-0.5)*2)),ref:mixColor(REF_NOON,REF_DUSK,smooth((p-0.5)*2))}.\nEach low and glow color object must have its three channel values available for rgba conversion; therefore use rgb arrays for those entries instead of strings: [36,21,45], [18,58,103], [24,21,47], [245,158,84], [220,244,251], [228,107,109], and glow arrays as listed. The final CSS background is:\n'linear-gradient(to bottom,'+rgba(colors[0],1)+' 0%,'+rgba(colors[1],1)+' 32%,'+rgba(colors[2],1)+' 58%,'+rgba(colors[3],1)+' 78%,'+rgba(colors[4],1)+' 92%,'+rgba(colors[5],1)+' 100%)'\nGenerate the six arrays by mixing each matching DAWN and NOON stop, or each matching NOON and DUSK stop. Assign it to backdrop.style.background only when the 33ms bucket changes. Set glow.style.transform once per rAF to translate3d(calc(var(--sun-x) - 50%),-18%,0), and set root.style.setProperty('--glow-x',(x*100).toFixed(2)+'%').\n\nFor normal motion set start=performance.now() and define frame(now):\nwindow.__DEMO__.frames+=1;\nconst p=((now-start)/60000)%1;\nconst x=lerp(0.08,0.92,p);\nconst q=(x-0.5)/0.42;\nconst altitude=Math.sqrt(Math.max(0,1-q*q));\nconst y=0.79-0.62*altitude;\nsun.style.transform='translate3d('+(x*100).toFixed(3)+'vw,'+(y*100).toFixed(3)+'vh,0)';\ncorona.style.transform='translate3d('+(x*100).toFixed(3)+'vw,'+(y*100).toFixed(3)+'vh,0) scale(1)';\nreflection.style.transform='translate3d('+(x*100).toFixed(3)+'vw,'+(0.745+(1-altitude)*0.075)*100+'.000vh,0)';\nminiMarker.setAttribute('transform','translate(92 '+x*184+',92 -46*altitude+')');\nif(now-lastPaint>=33){ lastPaint=now; applyPalette(p); updateClock(p); updateTelemetry(p); drawParticles(p); }\nrequestAnimationFrame(frame).\nDo not call setAttribute on miniMarker every frame; update its transform only in the 33ms palette bucket. The initial transform values are written before the loop.\n\nupdateClock(p) maps p to total minutes: p<0.5 ? lerp(342,720,p*2) : lerp(720,1098,(p-0.5)*2). Convert floor(totalMinutes*60) into hours, minutes, seconds, and set clock.textContent to padded values only when the second changes. updateTelemetry(p) sets the active phase text, updates mini arc dashoffset from 289 to 0 by p, and updates reflection opacity.\n\ndrawParticles(p) clears with clearRect, paints each particle at px=(particle.x*100*cachedWidth+Math.sin(p*6.283+phase)*amp)*scale, py=(particle.y*100*cachedHeight+p*speed*cachedHeight)%cachedHeight. Keep every wrapped y within cachedHeight. Paint only a 1.4px radius soft point and skip particles where opacity is below 0.12. Do not use ctx.filter, ctx.shadowBlur, or gradients inside the particle loop. If ctx exists, fill a precomputed current particle tint string and call ctx.beginPath/arc/fill at most 72 times.\n\nFor reduced motion, set p=0.5, call applyPalette(0.5), updateClock(0.5), updateTelemetry(0.5), and the exact noon transform values x=0.50, y=0.17, reflection y=0.7825. Push “reduced-motion-static”. Increment telemetry frames once per rAF with a separate loop that performs no DOM writes. Set window.__DEMO__.ready=true after startRuntime returns.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["At 1440x900, document.documentElement.scrollHeight does not exceed innerHeight, overflow is hidden, and exactly one h1 with the visible text SOLSTICE exists inside real header, main, nav, and footer landmarks.","Over a 1200ms interval at 1440x900, window.__DEMO__.frames increases by at least 20, window.__DEMO__.ready is true, and the object has the exact keys name: solstice, ready, frames, effects, and errors.","With prefers-reduced-motion set to reduce, the background and visible content are static, the clock reads 12:00:00, the sun remains at x=50% and y=17%, and every visible text element has opacity 1.","At 390x844, 768x1024, 1440x900, and 1920x1080, scrollWidth is no greater than clientWidth, all four primary labels are present, and no visible control lacks an accessible name or a 3px focus-visible outline.","The page creates no network requests other than the permitted Google Fonts stylesheet and contains no img, picture, video, base64 media, external script, external stylesheet, or framework dependency.","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"],"usage":{"build_tokens":49170,"build_prompt_tokens":8717,"build_completion_tokens":40453,"build_reasoning_tokens":29494,"build_calls":1,"build_attempts":3,"build_latency_ms":393426,"wasted_tokens":72674,"repair_tokens":32887,"crosscheck_tokens":22352,"total_tokens":104409},"verification":{"passed":false,"passed_checks":19,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"32537 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"5 frames, effects: canvas-particles, solar-arc, palette-cycle"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":false,"detail":"19 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 0.38, stdev 9.78, coverage 0.0015"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 2313764625 -> 2376457662"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"ENERGY IN MOTION\nSOLSTICE\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"SOLSTICE — Solar Energy, From First Light to Full Potential"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=378 pageErrors=0"}]},"crosscheck":{"slug":"solstice","reviewer_model":"gpt-5.6-luna","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":7,"requirements_met":["Rendered desktop composition has the requested left hero copy, CTA row, right telemetry card, atmospheric backdrop, horizon, and footer rail","Visible copy matches the specified SOLSTICE brief, including the lede, CTA labels, telemetry values, footer metadata, and availability pill","The screenshot shows the intended dawn palette, star field, sun arc marker, horizon line, and glass instrument-card treatment","The generated environment contains 24 stars, 9 clouds, a horizon SVG, particle canvas, solar-system elements, vignette, and grain layer","The solar cycle, clock, phase thresholds, and sun trajectory are substantially implemented","Named entry keyframes enter-up, enter-right, reveal-card, and drift-cloud are present","Responsive rules for tablet, mobile, and short desktop viewports are present"],"requirements_missed":["The hero copy, telemetry cluster, primary actions, and status rail are not implemented as semantic section elements; they are divs or a nav","The required desktop header nav gap is 8px, but the default CSS uses 24px","The upper glow is not the required blurred absolutely positioned ellipse; .solar-glow is a full-viewport element with no blur filter or ellipse geometry","The upper glow color is always emitted with alpha 0.98 instead of interpolating dawn 0.88, noon 0.98, and dusk 0.82","The sun reflection is a CSS ellipse with box-shadow, not a canvas reflection composed from three concentric translucent ellipses with the specified blur representation","The canvas backing resolution uses devicePixelRatio clamped from 1 to 2 rather than always sizing from cssWidth*2 and cssHeight*2 as required for smaller viewports","paletteAt returns the reflection color as a CSS string instead of retaining the required RGB channel array representation","The runtime performs additional per-frame root custom-property writes for --sun-x and --glow-x, and transforms three solar elements rather than adhering to the specified write budget","The responsive short-height overrides do not implement the required internal scrolling guard when hero content exceeds 600px","The status rail is a nav rather than the requested semantic status-rail section","The visible 1024x640 composition has no GRID ONLINE status pill because the implementation hides it for all 901–1100px widths, although the screenshot viewport is otherwise desktop-like"],"issues":[{"issue":"The atmospheric glow implementation is visibly and structurally wrong: the screenshot shows a broad diffuse wash, while the brief requires a distinct 70px-equivalent blurred ellipse centered at the sun.","severity":"high","evidence":"The runtime sets .solar-glow to width:100vw and height:100vh, but never sets filter:blur(38px), border-radius:50%, or ellipse dimensions."},{"issue":"The reflection technique does not match the brief.","severity":"high","evidence":"The code creates one .sun-reflection div with background:currentColor and box-shadow; no canvas reflection or three concentric ellipses are present."},{"issue":"At the supplied 1024x640 screenshot, the telemetry card is oversized and visually dominates the right side, with its bottom nearly colliding with the footer region.","severity":"medium","evidence":"The card occupies approximately x=672–981 and y=101–540 in the screenshot; the short-height rule increases telemetry row padding to 13px and leaves the card unusually tall."},{"issue":"The screenshot's footer and horizon are visually crowded: the horizon crosses directly through the CTA/card region and the footer begins only about 140px below it.","severity":"medium","evidence":"At 1024x640, the hard horizon is around y=439 while the footer border is around y=580, leaving a shallow lower field and making the card/footer relationship cramped."},{"issue":"The implementation looks like a competent generic AI landing page rather than a fully resolved cinematic system because several required atmospheric details are approximated with simple CSS defaults.","severity":"low","evidence":"The sun uses simple white box-shadow styling, the reflection is a single div, and the glow is a full-screen radial background rather than the specified layered compositor."}],"summary":"The page captures the requested visual language and most visible copy, but multiple named rendering techniques and semantic composition requirements are only approximated. The supplied screenshot is polished enough to pass as a landing page, yet the oversized card and simplified glow/reflection system prevent it from being a faithful implementation.","usage":{"input":20573,"output":1779,"total":22352,"reasoning":730,"cached":0},"latency_ms":16023}},{"slug":"tally","title":"Tally","category":"Landing Page","effect":"A full-viewport harbour LED lattice whose mint, slate, and amber pixels behave like a live departure board beneath a deliberately asymmetric navigation layer.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/tally/index.html","doc":"/prompts/tally.md","prompt_text":"A. WHAT IS BEING BUILT\n\nBuild one complete, dependency-free index.html for Northlight, a regional ferry and harbour operator publishing live sailings, tide windows, and berth status. The page is a single-viewport cinematic landing page whose viewport is itself the departure display. Set the document title to “Northlight — Live Harbour Departures”, set the document language to en-GB, and include a concise meta description: “Live ferry sailings, tide windows, and berth departures from Northlight Harbour.” Add a width=device-width, initial-scale=1 viewport meta tag.\n\nThe desktop canvas must occupy the entire 100dvh viewport. At viewport widths of 901px or greater, set html and body to width:100%, height:100dvh, overflow:hidden. At widths of 900px or less, allow vertical document scrolling with overflow-x:hidden. This is not a scroll piece: no section may induce a second page or horizontal scrollbar. The layout must remain usable at 390×844, 768×1024, 1440×900, and 1920×1080.\n\nUse semantic page structure: one header, one main, one footer, and one navigation. Include exactly one h1. Every visible link, button, and table control must have an accessible name. Every keyboard-focusable control must have a visible focus-visible ring. Do not build a video, image, photograph, card, glass panel, frosted surface, decorative border, modal, cookie banner, account menu, route planner, map, ticket basket, or secondary content section. Do not use React, Tailwind, GSAP, Three.js, Lottie, external media, remote images, base64 assets, third-party scripts, or a third-party CDN. Google Fonts is the only permitted network request; load “IBM Plex Mono” at weights 400, 500, and 600 and provide ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, “Liberation Mono”, monospace as the fallback stack. If the font request fails, the page must remain fully readable.\n\nB. THE BACKDROP\n\nThe only visual media is one full-bleed canvas element with id=\"tally-canvas\". It contains exactly 96 columns and 40 rows, for exactly 3,840 lattice points. The coordinate-space pitch is exactly 10px, producing a 960×400 source lattice. Keep the canvas at position:fixed with inset:0, width:100vw, and height:100dvh. Scale the source coordinate space to the viewport with the browser’s canvas presentation size. This low-resolution composition is deliberate: use a 960×400 backing surface rather than multiplying it by devicePixelRatio, and let CSS scale it to the viewport. This keeps all dot circles smooth enough at the chosen radii while limiting per-frame work.\n\nFor dot x from 0 through 95 and y from 0 through 39, draw at cx=5+x*10 and cy=5+y*10. At time t in seconds, compute:\n\nbase = 1.1 + 2.4 * abs(sin(x * 0.09 + t * 0.7) * sin(y * 0.11 - t * 0.5))\n\nUnlit dots use #5A6B72 at globalAlpha 0.12. A dot is lit when its computed normalized wave energy is at least 0.42. Normalize the sine-product contribution as clamp((abs(product)-0.18)/0.82, 0, 1). Mint-lit dots use #7DF9C6 at globalAlpha 0.52 + 0.48*energy. Three specified source columns, x equal to 21, 63, and 88, are berth columns. Within those columns, rows 0 through 39 are amber only during the live-departure state. At t below 9 seconds, columns 21 and 63 use the normal mint/slate calculation. At t from 9 through 10.2 seconds, column 88 alone receives one hard amber flash: its rows progress from row 0 at 9.00 seconds to row 39 at 10.20 seconds, each row holding #FFB020 at globalAlpha 1 for 80ms and returning to mint for the remainder of the column event. Before 9 seconds, mark the three berth states in the UI as “DEPARTING NOW” while the canvas remains mint; the canvas flash is a liveness proof, not a change in the semantic status.\n\nRender every dot in the first pass with ctx.beginPath(), ctx.arc(cx, cy, radius, 0, Math.PI*2), and ctx.fill(). Use a reusable Path2D? No: direct arcs in one first-pass loop are acceptable because 3,840 small paths remain below budget. Reset globalCompositeOperation to source-over before this pass.\n\nThe bloom is a second full-lattice pass. For every lit mint or amber dot, draw a second circle with radius multiplied by exactly 2.6, globalAlpha multiplied by 0.34, and ctx.fillStyle equal to that dot’s live colour. Set ctx.globalCompositeOperation to \"lighter\". Set ctx.filter exactly once before this second pass to \"blur(6px)\", and reset it to \"none\" immediately after the pass. Never assign ctx.filter or ctx.shadowBlur inside an individual dot loop. The 6px source-space blur becomes a visible 9–18px presentation-space bloom at tested desktop aspect ratios. This physical halo is the defining trick.\n\nAdd a pointer hotspot with a 220px coordinate-space radial influence. Cache pointer client coordinates in pointerX and pointerY. Convert them from viewport coordinates to canvas coordinates using 960/innerWidth and 400/innerHeight. On pointer movement, calculate a maximum per-frame hotspot delta of 0.08. In the animation loop, hotspotX += (targetX-hotspotX)*0.08 and hotspotY += (targetY-hotspotY)*0.08. Add 1.8 multiplied by falloff to the radius sum for every dot, where falloff=max(0, 1-distance/220), then square the falloff. Initial hotspotX=480 and hotspotY=200. Pointer entry sets the target; pointer leave returns the target to 480,200. The resulting visual settle is approximately 250ms at 60fps.\n\nThe wave has a 22-second visual period. It runs continuously with t elapsed in seconds; the supplied sine coefficients create interference movement across the fixed 10-unit coordinate pitch. No dot changes x or y position. Only radius, fill colour, and opacity change.\n\nImplement the load wipe by increasing revealY from -1 to 40 over 560ms. Each source row becomes visible when revealY is at least row-1. Interpolate the reveal boundary with a 10-unit feather. This is equivalent to top-to-bottom row activation at 14ms per row. After 560ms, animate a separate waveOpacity from 0 to 1 over exactly 400ms using an ease-out cubic. Before waveOpacity begins, rows may reach full slate opacity only. At 960ms, all rows and the mint wave are fully visible.\n\nThe per-frame cost budget is 3,840 first-pass arc fills plus no more than 1,650 second-pass lit fills. Precompute the 3,840 center coordinates, row reveal states, berth membership, and base sine factors outside the rAF loop. Calculate the shared time terms and hotspot positions once per frame. Do not call getBoundingClientRect, query selectors, read layout-affecting CSS, create gradients, create paths unrelated to the dot arcs, set canvas width, set canvas height, or resize the canvas inside the loop. Do not use ctx.shadowBlur. Do not call the 6px filter setter more than twice per frame. The main canvas dimensions and offscreen runtime resources are initialized before animation starts. The target budget is 4,000–5,500 path fills per frame and no more than 16ms of main-thread work on a 1440×900 headless browser for at least 20 frames in every 1.2-second sample.\n\nC. THE LAYER STACK\n\nThe full-viewport stack, back to front, is:\n\nz-index:0 — body background #04070A.\nz-index:1 — fixed #tally-canvas, opacity controlled by the wave reveal only.\nz-index:2 — a non-interactive top safety veil using a linear-gradient from rgba(4,7,10,0.84) at 0%, rgba(4,7,10,0.38) at 12%, rgba(4,7,10,0) at 28%, rgba(4,7,10,0) at 69%, rgba(4,7,10,0.50) at 100%. It is decorative and pointer-events:none.\nz-index:3 — semantic header, position:fixed, inset:0 0 auto 0.\nz-index:4 — main hero copy and status actions, position:fixed or absolute inside the viewport.\nz-index:5 — footer metadata rail, anchored to the bottom.\nz-index:6 — skip link, revealed only on keyboard focus.\nz-index:7 — a 2px amber event line used only for the t+9s class pulse.\n\nThere are no stacked glass or card surfaces. Text shadows may use 0 2px 18px rgba(4,7,10,0.92) and 0 1px 2px rgba(4,7,10,1); these are text legibility shadows, not containers.\n\nD. TOKEN TABLE\n\nDefine every size, spacing, colour, and timing as a CSS custom property in :root:\n\n--void: #04070A;\n--mint: #7DF9C6;\n--amber: #FFB020;\n--slate: #5A6B72;\n--paper: #F2F7F5;\n--muted: #9AA9AD;\n--hairline: rgba(122,249,198,0.18);\n--focus: #FFFFFF;\n--font-mono: \"IBM Plex Mono\", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", monospace;\n--page-x: 24px;\n--page-y: 24px;\n--header-h: 92px;\n--hero-col: 320px;\n--hero-bottom: 82px;\n--hero-gap: 18px;\n--footer-h: 58px;\n--rail-label-size: 10px;\n--rail-value-size: 12px;\n--brand-size: 15px;\n--eyebrow-size: 11px;\n--h1-size: clamp(42px, 4.25vw, 72px);\n--h1-line: 0.94;\n--body-size: 14px;\n--body-line: 1.55;\n--action-h: 44px;\n--action-radius: 999px;\n--track: 0.12em;\n--dur-fast: 160ms;\n--dur-base: 260ms;\n--dur-reveal: 400ms;\n--ease-out: cubic-bezier(0.16,1,0.3,1);\n--z-canvas: 1;\n--z-veil: 2;\n--z-header: 3;\n--z-main: 4;\n--z-footer: 5;\n--z-skip: 6;\n--z-event: 7;\n\nUse --mint, --slate, --amber, and --paper exactly as semantic colours. Do not introduce blue, purple, red, or card backgrounds.\n\nE. THE COMPOSITION\n\nBuild the document in this source order: skip link, header, main, footer, canvas, decorative veil, and event line. The canvas may appear first in paint order through z-index even if it follows semantics in source order.\n\nHeader is fixed at the top with left:24px, right:24px, top:0, height:92px, display:grid, grid-template-columns:320px minmax(0,1fr) auto; column-gap:24px; align-items:start; padding-top:20px. The first cell is a nav with aria-label=\"Primary navigation\". It contains an anchor named “Northlight home” and the exact text “NORTHLIGHT / HARBOUR”. Set it to --brand-size, weight 600, letter-spacing:0.10em, line-height:1.2, color:var(--paper), text-decoration:none. Under it place a 10px slate label at 6px top margin reading “LIVE CONTROL / NTH 04”.\n\nThe second header cell is the right-aligned sailing table, aligned to the top rail at the 24px page inset. Use a semantic table with class sailings and caption “Sailings from Northlight Harbour”. The table uses display:inline-grid; grid-template-columns:132px 92px 92px; gap:0; margin-left:auto; border-collapse:collapse; color:var(--slate); background:transparent. The caption is position:absolute; width:1px; height:1px; overflow:hidden; clip-path:inset(50%). Use th and td with height:24px, padding:0, border:0, font-size:10px, font-weight:500, line-height:24px, letter-spacing:0.08em, text-align:left. The first row contains the exact headers “SAILING”, “STATUS”, “BERTH”. The second row contains “06:40 KIRKGATE”, “BOARDING”, “02”. The third contains “07:15 ISLAY”, “ON TIME”, “04”. The fourth contains “08:05 SKERRY”, “DEPARTING NOW”, “06”. Only the status text in the final row is --amber. The second cell and the other values remain slate or paper. Do not add row backgrounds, cell borders, glass, or card padding. At widths below 901px, hide the caption visually while preserving semantics and reflow the table to a width-safe two-line arrangement.\n\nThe third header cell is a 44px-wide, 44px-high anchor with aria-label=\"Open live departures\". It displays the exact text “↗” at 20px, color:var(--mint). It is not enclosed by a box.\n\nMain is positioned left:var(--page-x), bottom:var(--hero-bottom), width:var(--hero-col), z-index:var(--z-main), and contains one hero column. Use display:flex; flex-direction:column; align-items:flex-start; gap:var(--hero-gap). Place the eyebrow first: exact text “NORTHLIGHT / LIVE HARBOUR”, 11px, weight 500, color:var(--mint), letter-spacing:var(--track). Place the h1 next with exact text “Know when<br>the water<br>opens.” Set margin:0, max-width:320px, color:var(--paper), font-size:var(--h1-size), line-height:var(--h1-line), font-weight:500, letter-spacing:-0.055em. Place the body paragraph next with exact text: “Three departures are moving now. Read the tide window, watch the berth, and leave with the next crossing.” Set max-width:310px, margin:0, color:var(--muted), font-size:var(--body-size), line-height:var(--body-line). Place the actions after a 2px gap. Use a div with display:flex; gap:10px. The primary anchor has exact visible text “VIEW LIVE SAILINGS” and accessible name “View live sailings”. It points to #live-sailings and has height:var(--action-h), padding:0 18px, border-radius:var(--action-radius), color:var(--void), background:linear-gradient(135deg, #B0FFE0 0%, var(--mint) 100%), font-size:11px, font-weight:600, letter-spacing:0.08em, display:inline-flex, align-items:center, text-decoration:none. The secondary anchor has exact visible text “TIDE WINDOWS” and accessible name “Tide windows”, points to #tide-windows, and uses transparent background, color:var(--paper), padding:0 4px, display:inline-flex, align-items:center, font-size:11px, font-weight:500, letter-spacing:0.08em.\n\nBelow the main text, include a visually compact status row with exact text “HARBOUR SIGNAL  /  09:42:18 UTC” and a 7px mint status dot. The dot uses background:var(--mint) and animation:none at rest. This row is not a card and has no border.\n\nFooter is fixed at left:24px, right:24px, bottom:0, height:var(--footer-h), display:grid; grid-template-columns:320px 1fr auto; gap:24px; align-items:center; border-top:1px solid var(--hairline); background:linear-gradient(to top, rgba(4,7,10,0.86), rgba(4,7,10,0)); z-index:var(--z-footer). The first metadata item is exact text “TIDE 2.4M / RISING” in 10px slate. The middle item is exact text “WIND NW 11KT / VISIBILITY 8.2MI” in 10px slate, centered. The last item is a link named “Harbour assistance” with exact text “HARBOUR ASSISTANCE  ↗”, 10px mint, right aligned. This is a thin metadata rail, not a card.\n\nThe semantic destinations #live-sailings and #tide-windows must be visually represented by the table caption and a visually hidden status note. Add one visually hidden p id=\"tide-windows\": “Northlight tide windows: 2.4 metres and rising; next safe crossing window 09:50 UTC.” Add id=\"live-sailings\" to the sailing table. Do not create visible extra sections.\n\nF. THE UI LANGUAGE\n\nButtons and pills use no glass and no border. The primary action is a solid mint-to-pale-mint diagonal pill. On hover, translate it by translateY(-2px), change its background to linear-gradient(135deg, #D5FFF0 0%, #8EFBCB 100%), and set box-shadow:0 8px 24px rgba(125,249,198,0.18). On active, translateY(0) and use the base background. Transition transform and box-shadow over 160ms using --ease-out; do not animate layout properties.\n\nThe secondary action is text-only with a 1px transparent underline. On hover, color:var(--mint) and border-bottom-color:var(--mint). On active, color:var(--paper). The top-right arrow anchor is text-only; hover color:var(--amber), transform:translateY(-2px). Every focus-visible target receives outline:2px solid var(--focus), outline-offset:4px. No focus ring may be clipped by overflow.\n\nStatus colors are semantic: “BOARDING” and “ON TIME” use slate, “DEPARTING NOW” uses amber, live signal text uses mint. Use uppercase IBM Plex Mono at 10–12px with letter spacing between 0.08em and 0.12em. Do not use rounded rectangular containers around table rows or metadata.\n\nG. MOTION\n\nThe load sequence begins at document script execution. Rows reveal top-to-bottom over 560ms at 14ms per row. Dot opacity is controlled by row reveal and wave opacity. The mint wave begins after the row reveal at 560ms and reaches opacity 1 at 960ms. The exact row-reveal easing is linear for the 14ms row progression; the wave fade uses cubic ease-out. The UI copy uses these delays:\n\nThe brand anchor: opacity 0 to 1 and translateY(8px) to translateY(0), duration 420ms, delay 120ms, ease-out.\nThe sailing table: opacity 0 to 1 and translateX(12px) to translateX(0), duration 460ms, delay 260ms, ease-out.\nThe eyebrow: opacity 0 to 1 and translateY(10px) to translateY(0), duration 420ms, delay 180ms, ease-out.\nThe h1: opacity 0 to 1 and translateY(16px) to translateY(0), duration 560ms, delay 220ms, ease-out.\nThe body paragraph: opacity 0 to 1 and translateY(10px) to translateY(0), duration 440ms, delay 360ms, ease-out.\nThe primary action: opacity 0 to 1 and translateY(8px) to translateY(0), duration 400ms, delay 470ms, ease-out.\nThe secondary action: opacity 0 to 1 and translateY(8px) to translateY(0), duration 400ms, delay 520ms, ease-out.\nThe status row: opacity 0 to 1 and translateY(8px) to translateY(0), duration 400ms, delay 600ms, ease-out.\nThe footer: opacity 0 to 1, duration 360ms, delay 680ms, ease-out.\n\nName the CSS keyframes exactly: ui-rise, ui-slide, and amber-column. ui-rise animates opacity and transform only. ui-slide animates opacity and transform only. amber-column animates opacity from 0 to 1 to 0 over 1200ms with steps timing. Add class “amber-flash” to the event line at 9000ms and remove it at 10200ms. The canvas independently executes the t+9s column event; the event line is a 2px horizontal signal indicator positioned at left:0, right:0, top:50%, opacity:0, pointer-events:none. Do not use perpetual CSS animation for the canvas.\n\nThe pointer hotspot is frame-driven, not CSS-driven. Its target follows pointermove and pointerleave with a per-frame lerp of exactly 0.08. The wave is always running after the 960ms reveal. The timestamp display must update only once per second by caching the previous displayed second, not once per animation frame.\n\nH. THE RESPONSIVE TABLE\n\nAt 1440×900 and 1920×1080, use the base tokens. Keep the hero at 320px and the table at the top-right. At 1920×1080, set --page-x:40px, --header-h:104px, --hero-bottom:96px, --footer-h:64px, and --hero-col:360px; set the h1 maximum to 80px. The source lattice remains 960×400 and is presented across the full viewport; do not increase dot count.\n\nAt 768×1024, set --page-x:20px, --page-y:20px, --header-h:156px, --hero-col:360px, --hero-bottom:108px, --footer-h:86px, --h1-size:52px. Header grid-template-columns:1fr auto; grid-template-rows:44px 96px; column-gap:16px. Put the navigation in row 1 column 1, the arrow anchor in row 1 column 2, and the sailing table in row 2 spanning both columns with justify-self:end. Reduce the table grid-template-columns to 126px 88px 76px. The table remains right-aligned and has no background. Main moves to left:20px and bottom:108px. Footer grid-template-columns:1fr auto; grid-template-rows:34px 28px; row-gap:0; height:86px. Put tide in row 1 column 1, wind in row 1 column 2, and assistance in row 2 column 1 / 3. No element may exceed the viewport width.\n\nAt 390×844, set --page-x:16px, --page-y:16px, --header-h:176px, --hero-col:calc(100vw - 32px), --hero-bottom:120px, --footer-h:108px, --h1-size:44px, --body-size:13px, --action-h:42px. Header grid-template-columns:1fr 36px; grid-template-rows:42px 118px; column-gap:8px. Place navigation in row 1 column 1 and arrow in row 1 column 2. The sailing table spans row 2, uses display:grid, grid-template-columns:1fr 1fr 1fr, width:100%, justify-self:stretch, font-size:9px. Header labels must wrap safely with min-width:0. Main left:16px, right:16px, bottom:120px, width:auto. Set h1 max-width:350px. The action row uses flex-wrap:wrap and gap:8px. The footer left:16px, right:16px, height:108px, grid-template-columns:1fr 1fr, grid-template-rows:30px 30px 30px, gap:4px 12px. Put tide in row 1 column 1 / 3, wind in row 2 column 1 / 3, and assistance in row 3 column 1 / 3. At 390px the page may scroll vertically, but the hero must not cover the footer or create horizontal overflow.\n\nAdd max-height breakpoints. At min-width:901px and max-height:760px, set --header-h:72px, --hero-bottom:58px, --footer-h:46px, --h1-size:42px, --body-size:12px, --action-h:38px, and --page-y:14px. Reduce header padding-top to 12px, table row height to 20px, and the gap between h1 and body to 12px. At min-width:901px and max-height:620px, set --header-h:60px, --hero-bottom:46px, --footer-h:38px, --h1-size:34px, --body-size:11px, --action-h:34px, --page-x:18px, --page-y:10px. Hide the body paragraph only in this exact short desktop condition, reduce the h1 to three lines at 34px, set table row height to 18px, and retain both accessible actions. Do not hide the h1, header, table, status row, or footer.\n\nI. FAILURE GUARDS\n\nAll animated DOM content must have resting opacity 1. Define its final opacity and transform in the base rule, then animate from opacity 0 only inside a .is-loading wrapper or an animation that fill-mode is both. Remove .is-loading after 1100ms. If JavaScript is unavailable, the body must not retain opacity 0: use a no-js base state with opacity 1 and add .js to the document element before applying load classes.\n\nUse the two-rAF fallback for ready telemetry: after initialization, call requestAnimationFrame(() => requestAnimationFrame(() => { window.__DEMO__.ready = true; })). This marks ready after the first painted frame and a second frame opportunity. Increment frames exactly once inside the rAF loop by incrementing window.__DEMO__.frames. Do not increment frames in the ready callback.\n\nThe animation loop must have a visibility-safe guard. If document.hidden is true, cancel the current rAF, do not increment frames, and restart on visibilitychange. On reduced motion, do not run the wave or load wipe. Render one static complete lattice at 960×400 with revealY=40, waveOpacity=1, hotspot at center, and t=0. Set ready using the same two-rAF fallback and keep the page readable. Do not update the clock. Do not add .is-loading to any UI element. Do not schedule the amber flash in reduced-motion mode. Do not animate pointer movement, UI entrance, or the status signal.\n\nThe complete prefers-reduced-motion: reduce block must set animation:none!important, transition:none!important, scroll-behavior:auto!important, and force all animated elements to opacity:1 and transform:none. It must also prevent the canvas script from entering the rAF loop while preserving the static canvas. Reduced-motion users must retain access to the live sailings table, tide information, actions, and footer.\n\nJ. THE EXACT JS\n\nUse this exact script structure and values. Adapt only the required DOM ids to the specified markup:\n\nconst root = document.documentElement;\nroot.classList.add(\"js\");\nwindow.__DEMO__ = { name: \"tally\", ready: false, frames: 0, effects: [], errors: [] };\nwindow.addEventListener(\"error\", event => {\n  window.__DEMO__.errors.push(String(event.message || \"window error\"));\n});\n\nconst canvas = document.getElementById(\"tally-canvas\");\nconst ctx = canvas.getContext(\"2d\", { alpha: false });\nconst COLS = 96;\nconst ROWS = 40;\nconst PITCH = 10;\nconst W = COLS * PITCH;\nconst H = ROWS * PITCH;\nconst reduceMotion = window.matchMedia(\"(prefers-reduced-motion: reduce)\");\ncanvas.width = W;\ncanvas.height = H;\nctx.imageSmoothingEnabled = true;\nctx.imageSmoothingQuality = \"low\";\n\nconst dots = new Array(COLS * ROWS);\nfor (let y = 0; y < ROWS; y++) {\n  for (let x = 0; x < COLS; x++) {\n    dots[y * COLS + x] = {\n      x,\n      y,\n      cx: 5 + x * PITCH,\n      cy: 5 + y * PITCH,\n      sx: Math.sin(x * 0.09),\n      cxPhase: Math.cos(x * 0.09),\n      sy: Math.sin(y * 0.11),\n      cyPhase: Math.cos(y * 0.11),\n      berth: x === 21 || x === 63 || x === 88\n    };\n  }\n}\n\nconst pointer = { x: W / 2, y: H / 2, targetX: W / 2, targetY: H / 2 };\nfunction setPointer(clientX, clientY) {\n  pointer.targetX = Math.max(0, Math.min(W, clientX * W / window.innerWidth));\n  pointer.targetY = Math.max(0, Math.min(H, clientY * H / window.innerHeight));\n}\nwindow.addEventListener(\"pointermove\", event => setPointer(event.clientX, event.clientY), { passive: true });\nwindow.addEventListener(\"pointerleave\", () => {\n  pointer.targetX = W / 2;\n  pointer.targetY = H / 2;\n});\n\nlet raf = 0;\nlet start = performance.now();\nlet previousSecond = -1;\nlet clock = document.getElementById(\"clock\");\nlet amberSent = false;\nlet amberEnded = false;\n\nfunction drawStatic() {\n  ctx.globalCompositeOperation = \"source-over\";\n  ctx.filter = \"none\";\n  ctx.clearRect(0, 0, W, H);\n  ctx.fillStyle = \"#04070A\";\n  ctx.fillRect(0, 0, W, H);\n  ctx.fillStyle = \"#5A6B72\";\n  ctx.globalAlpha = 0.12;\n  for (const dot of dots) {\n    ctx.beginPath();\n    ctx.arc(dot.cx, dot.cy, 1.1, 0, Math.PI * 2);\n    ctx.fill();\n  }\n  ctx.globalAlpha = 1;\n}\n\nfunction drawFrame(now) {\n  const elapsed = (now - start) / 1000;\n  const revealY = Math.min(ROWS, elapsed * 1000 / 14);\n  const waveOpacity = Math.max(0, Math.min(1, (elapsed * 1000 - 560) / 400));\n  const waveEase = 1 - Math.pow(1 - waveOpacity, 3);\n  pointer.x += (pointer.targetX - pointer.x) * 0.08;\n  pointer.y += (pointer.targetY - pointer.y) * 0.08;\n\n  if (elapsed >= 9 && !amberSent) {\n    document.body.classList.add(\"amber-flash\");\n    window.__DEMO__.effects.push(\"amber-column\");\n    amberSent = true;\n  }\n  if (elapsed >= 10.2 && !amberEnded) {\n    document.body.classList.remove(\"amber-flash\");\n    amberEnded = true;\n  }\n\n  ctx.globalCompositeOperation = \"source-over\";\n  ctx.filter = \"none\";\n  ctx.globalAlpha = 1;\n  ctx.fillStyle = \"#04070A\";\n  ctx.fillRect(0, 0, W, H);\n\n  for (const dot of dots) {\n    const rowAlpha = Math.max(0, Math.min(1, revealY - dot.y));\n    if (rowAlpha <= 0) continue;\n    const product = Math.abs(\n      (dot.sx * Math.cos(elapsed * 0.7) + dot.cxPhase * Math.sin(elapsed * 0.7)) *\n      (dot.sy * Math.cos(elapsed * 0.5) - dot.cyPhase * Math.sin(elapsed * 0.5))\n    );\n    const energy = Math.max(0, Math.min(1, (product - 0.18) / 0.82));\n    const distance = Math.hypot(dot.cx - pointer.x, dot.cy - pointer.y);\n    const falloff = Math.max(0, 1 - distance / 220);\n    const radius = 1.1 + 2.4 * product + 1.8 * falloff * falloff;\n    const lit = energy >= 0.42;\n    const amber = dot.x === 88 && elapsed >= 9 + dot.y * 0.03076923 && elapsed < 9.08 + dot.y * 0.03076923;\n    ctx.fillStyle = amber ? \"#FFB020\" : (lit ? \"#7DF9C6\" : \"#5A6B72\");\n    ctx.globalAlpha = amber ? rowAlpha : (lit ? (0.52 + 0.48 * energy) * waveEase * rowAlpha : 0.12 * rowAlpha);\n    ctx.beginPath();\n    ctx.arc(dot.cx, dot.cy, radius, 0, Math.PI * 2);\n    ctx.fill();\n  }\n\n  ctx.globalCompositeOperation = \"lighter\";\n  ctx.filter = \"blur(6px)\";\n  for (const dot of dots) {\n    const rowAlpha = Math.max(0, Math.min(1, revealY - dot.y));\n    if (rowAlpha <= 0) continue;\n    const product = Math.abs(\n      (dot.sx * Math.cos(elapsed * 0.7) + dot.cxPhase * Math.sin(elapsed * 0.7)) *\n      (dot.sy * Math.cos(elapsed * 0.5) - dot.cyPhase * Math.sin(elapsed * 0.5))\n    );\n    const energy = Math.max(0, Math.min(1, (product - 0.18) / 0.82));\n    const distance = Math.hypot(dot.cx - pointer.x, dot.cy - pointer.y);\n    const falloff = Math.max(0, 1 - distance / 220);\n    if (energy < 0.42 && !(dot.x === 88 && elapsed >= 9 + dot.y * 0.03076923 && elapsed < 9.08 + dot.y * 0.03076923)) continue;\n    const radius = (1.1 + 2.4 * product + 1.8 * falloff * falloff) * 2.6;\n    ctx.fillStyle = dot.x === 88 && elapsed >= 9 + dot.y * 0.03076923 && elapsed < 9.08 + dot.y * 0.03076923 ? \"#FFB020\" : \"#7DF9C6\";\n    ctx.globalAlpha = rowAlpha * waveEase * 0.34;\n    ctx.beginPath();\n    ctx.arc(dot.cx, dot.cy, radius, 0, Math.PI * 2);\n    ctx.fill();\n  }\n  ctx.filter = \"none\";\n  ctx.globalCompositeOperation = \"source-over\";\n  ctx.globalAlpha = 1;\n\n  const second = Math.floor(elapsed);\n  if (clock && second !== previousSecond) {\n    previousSecond = second;\n    const total = 9 * 3600 + 42 * 60 + 18 + second;\n    const hh = String(Math.floor(total / 3600) % 24).padStart(2, \"0\");\n    const mm = String(Math.floor(total / 60) % 60).padStart(2, \"0\");\n    const ss = String(total % 60).padStart(2, \"0\");\n    clock.textContent = hh + \":\" + mm + \":\" + ss + \" UTC\";\n  }\n\n  window.__DEMO__.frames += 1;\n  raf = requestAnimationFrame(drawFrame);\n}\n\nfunction startAnimation() {\n  if (reduceMotion.matches) {\n    drawStatic();\n    requestAnimationFrame(() => requestAnimationFrame(() => {\n      window.__DEMO__.ready = true;\n    }));\n    return;\n  }\n  start = performance.now();\n  window.__DEMO__.effects.push(\"wave-sweep\");\n  window.__DEMO__.effects.push(\"pointer-hotspot\");\n  window.__DEMO__.effects.push(\"load-wipe\");\n  raf = requestAnimationFrame(drawFrame);\n  requestAnimationFrame(() => requestAnimationFrame(() => {\n    window.__DEMO__.ready = true;\n  }));\n}\n\ndocument.addEventListener(\"visibilitychange\", () => {\n  if (document.hidden) {\n    cancelAnimationFrame(raf);\n  } else if (!reduceMotion.matches) {\n    start = performance.now();\n    raf = requestAnimationFrame(drawFrame);\n  }\n});\n\nstartAnimation();\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["The page loads as a single index.html with exactly one h1, semantic header/main/footer/nav elements, no external media, and no horizontal overflow at 390×844, 768×1024, 1440×900, and 1920×1080.","At 1440×900, document.documentElement.scrollHeight equals the viewport height, body overflow is hidden, and the canvas presents a 96×40 lattice of 3,840 fixed-pitch dots using the specified slate, mint, and amber colours.","After two animation frames, window.__DEMO__.name is \"tally\", window.__DEMO__.ready is true, window.__DEMO__.frames is greater than 0, and the effects array contains \"wave-sweep\", \"pointer-hotspot\", and \"load-wipe\" unless prefers-reduced-motion is active.","During a normal 1.2-second sample at 1440×900, the rAF loop increments window.__DEMO__.frames by at least 20, makes no console output, records no uncaught errors, and does not call getBoundingClientRect inside the animation loop.","With prefers-reduced-motion: reduce, the page remains readable and static, the rAF animation loop is not started, no amber flash is scheduled, and the canvas still contains a complete static 3,840-dot lattice.","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"],"usage":{"build_tokens":42733,"build_prompt_tokens":8876,"build_completion_tokens":33857,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":559582,"wasted_tokens":0,"repair_tokens":37142,"crosscheck_tokens":20839,"total_tokens":100714},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"31216 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"106 frames, effects: wave-sweep, pointer-hotspot, load-wipe"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"63 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 33.14, stdev 53.4, coverage 1"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 2426527567 -> 726195884"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Know when\nthe water\nopens.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Northlight — Live Harbour Departures"},{"name":"document declares lang","pass":true,"detail":"en-GB"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=593 pageErrors=0"}]},"crosscheck":{"slug":"tally","reviewer_model":"gpt-5.5","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":7,"requirements_met":["Desktop composition uses the required fixed header, fixed hero, fixed footer, full-bleed canvas, veil, and event line stack.","The visible copy is specific to Northlight and mostly matches the required wording.","The header sailing table is present at the top right with the required rows and amber final status.","The hero column contains the required eyebrow, h1, paragraph, two actions, and harbour signal row.","The footer metadata rail is thin and anchored to the bottom rather than rendered as a card.","The screenshot shows the intended monochrome/mint/amber harbour-display visual language rather than generic stock imagery."],"requirements_missed":["At 1024×640 the hero copy is laid directly over a very bright mint dot cluster, reducing legibility and undermining the requested cinematic departure-display composition.","The canvas bloom pass is not implemented as the specified direct second full-lattice pass on the main context; it draws bloom circles to an offscreen canvas and then blurs the drawn image.","The bloom alpha calculation is wrong for lit mint dots: it uses rowAlpha * waveEase * 0.34 instead of multiplying the dot’s live alpha by exactly 0.34.","The generated canvas loop does not reset ctx.filter to \"none\" before the first pass each frame, contrary to the exact script/technique requirement.","The load wipe does not implement the specified row visibility rule and 10-unit feather; it uses a simple per-row alpha clamp based on revealY - row.","The CSS keyframe amber-column uses a 50% midpoint fade, not the requested opacity 0 to 1 to 0 over 1200ms with steps timing as a hard event pulse.","The status row is visually too close to the footer rail at the tested short desktop height, making the lower-left composition cramped.","Several sizes, colours, and timings are introduced as additional custom properties beyond the required token table rather than using only the specified design tokens."],"issues":[{"issue":"Hero title and paragraph sit on top of a high-intensity mint canvas blob in the screenshot, so the main message is visually contaminated by the backdrop instead of reading cleanly.","severity":"medium","evidence":"Screenshot at 1024×640 shows the large mint dot mass directly behind “Know when / the water / opens.” and the paragraph."},{"issue":"Canvas bloom technique deviates from the brief: the second pass is rendered into bloomCanvas and composited with drawImage, not drawn as the required second full-lattice pass with blurred arcs on the main context.","severity":"high","evidence":"Script creates `const bloomCanvas = document.createElement(\"canvas\")` and draws bloom arcs with `bloomCtx`, then calls `ctx.drawImage(bloomCanvas, 0, 0)`."},{"issue":"Bloom alpha is not the required live dot alpha multiplied by 0.34.","severity":"high","evidence":"Script sets `bloomAlphas[i] = rowAlpha * waveEase * 0.34;` instead of using `(0.52 + 0.48 * energy) * waveEase * rowAlpha * 0.34` for mint-lit dots."},{"issue":"First canvas pass does not explicitly reset ctx.filter to none each frame before drawing the lattice.","severity":"medium","evidence":"In `drawFrame`, before the first pass it sets `ctx.globalCompositeOperation`, `ctx.globalAlpha`, and `ctx.fillStyle`, but not `ctx.filter = \"none\"`."},{"issue":"Load wipe row activation is simplified and does not match the specified feathered reveal boundary.","severity":"medium","evidence":"Script computes `rowAlphas[y]` from `revealY - y`; the brief required each row visible when revealY is at least row-1 with a 10-unit feather."},{"issue":"Amber event line keyframe is not a hard steps pulse as specified.","severity":"medium","evidence":"`@keyframes amber-column` uses `0%`, `50%`, and `100%`; the animation rule uses `steps(1,end)`, but the midpoint is not the requested hard 0-to-1-to-0 event line choreography."},{"issue":"Footer/status area is cramped in the actual 1024×640 rendering.","severity":"low","evidence":"Screenshot shows the harbour signal row only just above the footer border, with the lower composition compressed."}],"summary":"The page gets the broad Northlight departure-board composition right, but several of the brief’s defining canvas and motion techniques are not implemented exactly. The screenshot is usable, but the hero is poorly separated from the brightest canvas cluster and the short-height layout feels cramped.","usage":{"input":19216,"output":1623,"total":20839,"reasoning":516,"cached":0},"latency_ms":21459}},{"slug":"tessellate","title":"Tessellate","category":"Landing Page","effect":"SVG pattern morphs between triangular, hexagonal, and herringbone tilings behind an animated rotating mask, cycling every 6s.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/tessellate/index.html","doc":"/prompts/tessellate.md","prompt_text":"A. WHAT IS BEING BUILT:\nA single-viewport cinematic landing page for \"TESSELLATE\", a generative-design studio. The document title is \"TESSELLATE — Generative Design Studio\". The <html> element carries lang=\"en\". The viewport meta tag is <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">. The CSS locks the desktop viewport: html, body { height: 100dvh; } @media (min-width: 901px) { html, body { overflow: hidden; } }. DO NOT BUILD: multi-page routing, any <canvas> element, mp4/webm/gif media, remote images (except Google Fonts), base64 photo blobs, Tailwind CSS, React, GSAP, Three.js, Lottie, WebGL, external CDNs, horizontal scrollbars, invisible focus states, console.log statements, unhandled promise rejections, or non-vector blur (e.g., no box-shadow blur on animated elements). Every pixel must originate from CSS gradients, SVG shapes, or system fonts.\n\nB. THE BACKDROP:\nThe backdrop consists of 1 parent <svg id=\"backdrop-svg\">, 3 <pattern> definitions (#pat-tri, #pat-hex, #pat-herr), 1 <mask id=\"anim-mask\"> containing 1 <g class=\"mask-shapes\"> with exactly 6 vector child shapes (2 <polygon>, 2 <path>, 1 <circle>, 1 <rect>), and 3 stacked <rect> elements (#rect-tri, #rect-hex, #rect-herr) inside a <g mask=\"url(#anim-mask)\">. Blur radii are exactly 0 — no SVG <feGaussianBlur> or CSS filter: blur() is applied to the backdrop, guaranteeing crisp vector rendering at any DPI. Gradient stops: the page background is a CSS radial-gradient(circle at 50% 40%, #1a1025 0%, #0a0610 70%, #000000 100%). Pattern fills use flat alpha: triangle paths are rgba(192,132,252,0.15) and rgba(192,132,252,0.05); hexagon uses rgba(192,132,252,0.10); herringbone uses rgba(192,132,252,0.08) and rgba(192,132,252,0.12). Drift periods: .mask-shapes rotates 360deg over 24s (linear infinite) and scales from 1 to 1.15 and back over 12s (ease-in-out infinite). The 3 rects cross-fade over a single 18s cycle (6 seconds per tiling). Canvas resolution trick: N/A — vector-only SVG (no <canvas>), so no devicePixelRatio scaling is required. The SVG uses viewBox=\"0 0 1440 900\" and preserveAspectRatio=\"xMidYMid slice\" to guarantee crisp scaling at any viewport size without pixelation, replacing the canvas DPR trick entirely. Per-frame cost budget: total JS execution per frame must be < 2ms. We cache the #readout-text DOM node. We NEVER call getBoundingClientRect inside the loop. CSS handles all animation (opacity and transform only). The only JS math is Math.floor(Date.now() / 6000) % 3, an O(1) operation taking < 0.01ms.\n\nC. THE LAYER STACK (back to front, z-index values):\n- z-index: 0 — #bg-gradient (a body::before pseudo-element with the radial gradient, position: fixed; inset: 0;).\n- z-index: 1 — #backdrop-svg (position: fixed; inset: 0; width: 100%; height: 100%; pointer-events: none;).\n- z-index: 2 — #vignette (a fixed div with background: radial-gradient(circle, transparent 40%, #000 120%); pointer-events: none;).\n- z-index: 3 — <main> (position: relative; z-index: 3;).\n- z-index: 10 — <header> (position: fixed; top: 0; left: 0; right: 0;).\n- z-index: 10 — <footer> (position: fixed; bottom: 0; left: 0; right: 0;).\n- Interactive controls (:focus-visible) receive position: relative; z-index: 100; and outline: 2px solid var(--accent); outline-offset: 4px;.\nThe <nav> lives inside <header>.\n\nD. A TOKEN TABLE (--name: value) for every size and colour:\n--accent: #c084fc;\n--accent-glow: rgba(192, 132, 252, 0.35);\n--bg-dark: #0a0610;\n--bg-mid: #1a1025;\n--bg-black: #000000;\n--text-primary: #f8fafc;\n--text-secondary: #94a3b8;\n--font-display: 'Space Grotesk', system-ui, sans-serif;\n--font-mono: 'JetBrains Mono', ui-monospace, monospace;\n--header-h: 80px;\n--footer-h: 64px;\n--hero-pad-top: 40px;\n--hero-pad-x: 24px;\n--grid-gap: 16px;\n--pill-pad-y: 10px;\n--pill-pad-x: 24px;\n--pill-radius: 999px;\n--readout-size: 0.75rem;\n--h1-size: clamp(2.5rem, 8vw, 6rem);\n--h1-line: 1.05;\n--z-bg: 0;\n--z-backdrop: 1;\n--z-vignette: 2;\n--z-content: 3;\n--z-header: 10;\n--z-footer: 10;\n(Include Google Fonts link for Space Grotesk 700 and JetBrains Mono 400 in the head).\n\nE. THE COMPOSITION — header, hero, footer as a build order:\n1. <header>: height: var(--header-h); padding: 0 24px; display: flex; align-items: center; justify-content: space-between; Contains <nav> (display: flex; gap: 24px;) with links: <a href=\"#work\">Work</a>, <a href=\"#studio\">Studio</a>, <a href=\"#contact\">Contact</a>. Accessible names are provided by the text nodes 'Work', 'Studio', 'Contact'.\n2. <main>: padding-top: var(--header-h); padding-bottom: var(--footer-h); padding-left: var(--hero-pad-x); padding-right: var(--hero-pad-x); display: grid; grid-template-columns: 1fr; gap: var(--grid-gap); place-items: center; text-align: center; min-height: 100dvh;\n  - <h1> (the only h1 on the page): copy is 'TESSELLATE'. Style: font-family: var(--font-display); font-size: var(--h1-size); line-height: var(--h1-line); font-weight: 700; letter-spacing: -0.02em; color: var(--text-primary); margin: 0; text-transform: uppercase;\n  - <p id=\"subtitle\">: copy is 'Generative design studio engineering structural patterns for spatial interfaces.' Style: color: var(--text-secondary); font-family: var(--font-display); max-width: 600px; margin: 0;\n  - <div id=\"readout\">: contains <span id=\"readout-text\">CURRENT TILING: TRIANGULAR</span>. Style: font-family: var(--font-mono); font-size: var(--readout-size); color: var(--accent); text-transform: uppercase; letter-spacing: 0.1em; margin-top: 8px;\n3. <footer>: height: var(--footer-h); padding: 0 24px; display: flex; align-items: center; justify-content: space-between; Left side: <span>© 2024 Tessellate Studio</span> (font-family: var(--font-mono); font-size: 0.7rem; color: var(--text-secondary);). Right side: <button id=\"toggle-motion\" class=\"pill\">Pause Motion</button> (accessible name 'Pause Motion' from text content).\n\nF. THE UI LANGUAGE — button/pill treatments:\nThe .pill button (#toggle-motion) uses: background: linear-gradient(135deg, rgba(192,132,252,0.15), rgba(192,132,252,0.05)); border: 1px solid var(--accent-glow); color: var(--text-primary); padding: var(--pill-pad-y) var(--pill-pad-x); border-radius: var(--pill-radius); font-family: var(--font-mono); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; cursor: pointer; transition: background 0.3s ease, transform 0.3s ease, box-shadow 0.3s ease;\nHover state: background: linear-gradient(135deg, rgba(192,132,252,0.3), rgba(192,132,252,0.1)); transform: translateY(-2px); box-shadow: 0 4px 12px var(--accent-glow);\nFocus-visible state: outline: 2px solid var(--accent); outline-offset: 4px; position: relative; z-index: 100;\nNav links: color: var(--text-secondary); text-decoration: none; transition: color 0.2s; Hover: color: var(--text-primary);. Focus-visible gets the same ring.\n\nG. MOTION — a stagger table (element -> animation -> delay -> duration):\n| Element | Animation | Delay | Duration |\n|---|---|---|---|\n| .mask-shapes | maskRotate | 0s | 24s |\n| .mask-shapes | maskScale | 0s | 12s |\n| #rect-tri | crossFadeTri | 0s | 18s |\n| #rect-hex | crossFadeHex | 0s | 18s |\n| #rect-herr | crossFadeHerr | 0s | 18s |\n| h1 | fadeUp | 0.2s | 1s |\n| #subtitle | fadeUp | 0.4s | 1s |\n| #readout | fadeUp | 0.6s | 1s |\n| #toggle-motion | fadeUp | 0.8s | 1s |\nNamed keyframes:\n@keyframes maskRotate { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }\n@keyframes maskScale { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.15); } }\n@keyframes crossFadeTri { 0%, 30% { opacity: 1; } 33.33%, 96.66% { opacity: 0; } 100% { opacity: 1; } }\n@keyframes crossFadeHex { 0%, 30% { opacity: 0; } 33.33%, 63.33% { opacity: 1; } 66.66%, 100% { opacity: 0; } }\n@keyframes crossFadeHerr { 0%, 63.33% { opacity: 0; } 66.66%, 96.66% { opacity: 1; } 100% { opacity: 0; } }\n@keyframes fadeUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }\nAll elements using fadeUp have animation-fill-mode: both;. .mask-shapes has transform-box: view-box; transform-origin: 50% 50%;.\n\nH. A RESPONSIVE TABLE — token overrides per breakpoint:\n1. @media (max-width: 900px) (tablet & mobile, scroll enabled): html, body { overflow: auto; } --h1-size: clamp(2rem, 10vw, 4rem); --header-h: 70px; --footer-h: 56px;\n2. @media (max-width: 600px) (mobile): --header-h: 64px; --hero-pad-x: 16px; --grid-gap: 12px; --pill-pad-x: 16px;\n3. @media (min-width: 901px) and (max-height: 800px) (max-height breakpoint 1): --h1-size: 4rem; --header-h: 70px; --footer-h: 56px;\n4. @media (min-width: 901px) and (max-height: 700px) (max-height breakpoint 2): --h1-size: 3rem; --header-h: 60px; --footer-h: 48px; --hero-pad-top: 20px;\nNo horizontal overflow occurs at 390x844, 768x1024, 1440x900, or 1920x1080 because #backdrop-svg uses preserveAspectRatio=\"xMidYMid slice\" and body { overflow-x: hidden; }.\n\nI. FAILURE GUARDS:\n1. Resting opacity 1: All fadeUp elements rest at opacity: 1; transform: none; after their animation completes (due to animation-fill-mode: both). If animations are stripped, they default to visible.\n2. Two-rAF fallback: window.__DEMO__.ready = true is set inside a double requestAnimationFrame callback, guaranteeing the flag flips strictly after the browser's first paint, preventing false-positive readiness.\n3. Reduced-motion: A @media (prefers-reduced-motion: reduce) block sets animation: none !important; transition: none !important; for all elements. #rect-hex and #rect-herr are set to display: none;, #rect-tri is forced to opacity: 1;. .mask-shapes is given a static transform: rotate(15deg) scale(1.05); to preserve the composition without motion. The JS still runs the rAF loop to increment window.__DEMO__.frames (so telemetry passes) but does not manipulate DOM styles. document.body.classList.add('reduced') is added for any JS conditionals.\n\nJ. THE EXACT JS:\nwindow.__DEMO__ = { name: 'tessellate', ready: false, frames: 0, effects: [], errors: [] };\nwindow.addEventListener('error', (e) => { window.__DEMO__.errors.push(e.message); });\n['svg-pattern-crossfade', 'mask-rotate-scale', 'monospace-readout'].forEach(e => window.__DEMO__.effects.push(e));\n\nconst readouts = ['TRIANGULAR', 'HEXAGONAL', 'HERRINGBONE'];\nlet lastIdx = -1;\nconst readoutEl = document.getElementById('readout-text');\n\nfunction loop() {\n  window.__DEMO__.frames++;\n  const idx = Math.floor(Date.now() / 6000) % 3;\n  if (idx !== lastIdx) {\n    lastIdx = idx;\n    if (readoutEl) readoutEl.textContent = 'CURRENT TILING: ' + readouts[idx];\n  }\n  requestAnimationFrame(loop);\n}\n\nrequestAnimationFrame(() => {\n  requestAnimationFrame(() => {\n    window.__DEMO__.ready = true;\n    requestAnimationFrame(loop);\n  });\n});\n\ndocument.addEventListener('DOMContentLoaded', () => {\n  const btn = document.getElementById('toggle-motion');\n  if (btn) {\n    btn.addEventListener('click', () => {\n      const paused = document.body.classList.toggle('paused');\n      btn.textContent = paused ? 'Play Motion' : 'Pause Motion';\n      const els = document.querySelectorAll('#rect-tri, #rect-hex, #rect-herr, .mask-shapes');\n      els.forEach(el => el.style.animationPlayState = paused ? 'paused' : 'running');\n    });\n  }\n});\n\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["window.__DEMO__.ready === true","window.__DEMO__.frames > 20 after 1200ms","document.querySelectorAll('h1').length === 1","getComputedStyle(document.documentElement).getPropertyValue('--accent').trim() === '#c084fc'","window.__DEMO__.errors.length === 0","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"],"usage":{"build_tokens":37666,"build_prompt_tokens":4295,"build_completion_tokens":33371,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":613694,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":9860,"total_tokens":47526},"verification":{"passed":true,"passed_checks":18,"total_checks":18,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"13104 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"10 frames, effects: svg-pattern-crossfade, mask-rotate-scale, monospace-readout"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"47 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"TESSELLATE\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"TESSELLATE — Generative Design Studio"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=178 pageErrors=0"}]},"crosscheck":{"slug":"tessellate","reviewer_model":"glm-5.3-flash","reviewer_platform":"workbuddy","verdict":"partial","confidence":"medium","aesthetic_score":7,"requirements_met":["Document title, lang='en', viewport meta correct","Token table fully declared in :root with all required variables","Backdrop SVG with 3 patterns (#pat-tri, #pat-hex, #pat-herr), mask with exactly 6 shapes (2 polygon, 2 path, 1 circle, 1 rect), 3 cross-fading rects in masked group","viewBox 0 0 1440 900 with preserveAspectRatio xMidYMid slice; no blur anywhere","Layer stack z-indexes 0/1/2/3/10 as specified","Header/nav links Work/Studio/Contact with text accessible names","Hero copy exact: h1 'TESSELLATE', subtitle sentence, readout span","Footer left '© 2024 Tessellate Studio' mono 0.7rem, right .pill button 'Pause Motion'","All fadeUp delays 0.2/0.4/0.6/0.8s with fill-mode both","Named keyframes match brief exactly (maskRotate, maskScale, crossFadeTri/Hex/Herr, fadeUp)","All 4 responsive breakpoint blocks present with correct token overrides","prefers-reduced-motion block matches brief (rect-hex/herr hidden, rect-tri opacity 1, static mask transform)","JS telemetry hook byte-for-byte faithful to section J","Google Fonts preconnect + Space Grotesk 700 / JetBrains Mono 400 with fallback stacks"],"requirements_missed":["Pattern fill alphas deviate: brief requires triangle paths filled rgba(192,132,252,0.15) AND rgba(192,132,252,0.05); implementation uses fill 0.05 with 0.15 only as stroke color","Screenshot could not be inspected — rendered composition unverifiable"],"issues":[{"issue":"Cannot evaluate rendered composition: screenshot not viewable in this environment; no image-based issues could be confirmed or ruled out","severity":"medium","evidence":"System reminder: the current model does not support images"},{"issue":"Triangle pattern fills do not use the two specified flat-alpha fills (0.15 and 0.05); 0.15 is applied as stroke instead of fill","severity":"low","evidence":"#pat-tri: fill=\"rgba(192,132,252,0.05)\" stroke=\"rgba(192,132,252,0.15)\""},{"issue":"Header uses justify-content: space-between but contains only the nav, so the brief's space-between behavior is inert (no left-side element); screenshot may show nav oddly left-anchored against an empty right side","severity":"low","evidence":"header { justify-content: space-between; } with only <nav> as child"}],"summary":"Code is a faithful, nearly byte-exact implementation of the brief's structure, tokens, keyframes, breakpoints, and JS telemetry; the only concrete deviation is the triangle pattern alpha assignment. The screenshot could not be inspected, so the rendered composition itself remains unverified.","usage":{"input":8872,"output":988,"total":9860,"reasoning":191,"cached":0},"latency_ms":22207}},{"slug":"thermal","title":"Thermal","category":"Landing Page","effect":"A diffusing 48x27 canvas heat map mapped through an inferno palette with a live hottest-cell readout","platform":"cmdcode","platform_label":"cmd-code","model":"xiaomi/mimo-v2.6-pro","model_label":"Xiaomi MiMo v2.6 Pro (xiaomi/mimo-v2.6-pro)","demo":"/demos/thermal/index.html","doc":"/prompts/thermal.md","prompt_text":"## A. WHAT IS BEING BUILT\n\nBuild one complete, self-contained `index.html` for **Thermal** — an industrial-monitoring company turning sensor meshes into live heat maps. Document title: `Thermal — Continuous Heat Intelligence`. `<html lang=\"en\">`. ONE file: plain HTML + CSS + JS in `<style>` and one `<script>` at end of body. No build step, no framework, no bundler. Google Fonts is the ONLY network request (Inter + IBM Plex Mono, system fallback stacks). Every visible pixel comes from CSS gradients, inline SVG, text, and one `<canvas>`.\n\n**Viewport rule:** at width `>= 901px` the page is one locked frame — `html, body { height:100%; overflow:hidden }` and `.page { height:100dvh; overflow:hidden }`. NO SCROLL on desktop. At width `<= 900px` the page may scroll vertically. No horizontal overflow at any width.\n\nThe single visual idea: a full-bleed `<canvas>` heat map — a 48×27 cell grid where three drifting emitters inject heat that diffuses between neighbours every frame, each cell mapped through an inferno-structured ramp. Cool blue edges, violet mid-field, hot magenta core, white-pink peak. Over it: a HUD with a legend strip (20°C → 620°C), a live hottest-cell readout, and a crosshair on that cell. Industrial-monitoring, instrument-grade, cinematic.\n\n**Do NOT build:** no video, no WebGL, no Three.js, no Lottie, no GSAP, no jQuery, no Tailwind, no React, no remote or base64 images, no audio, no forms, no pricing tables, no testimonials, no dashboards, no charts beyond the legend strip, no sections beyond header / hero / footer, no footer links beyond the three specified, no cookie banner, no scroll indicator, no desktop hamburger.\n\n---\n\n## B. THE BACKDROP — procedural heat field (the ONLY visual effect besides grain and the marker)\n\nA single `<canvas id=\"heat\">` fills the viewport: `position:fixed; inset:0; width:100%; height:100%; z-index:0; display:block`, repainted every animation frame via `requestAnimationFrame`. NEVER a static image.\n\n**The field (canvas 2D, no libraries):**\n\n- Grid `W = 48`, `H = 27` at `>= 901px` (1296 cells); at `<= 900px` transpose to `W = 27`, `H = 48` (still 1296) so cells stay near-square on portrait screens.\n- Two `Float32Array(1296)` buffers `cur` / `nxt`; index `i = y * W + x`.\n- **Diffusion step, once per frame**, clamped (zero-flux) neighbours:\n  `lap = up + down + left + right - 4 * c`\n  `v = (c + 0.18 * lap) * 0.9962`   (`K = 0.18`, `COOL = 0.9962`)\n  if `x < 2 || x > W-3 || y < 2 || y > H-3` then `v *= 0.982` (`EDGE_DAMP` — this keeps the rim cold blue and the core hot)\n  clamp `v` to `[0, 1]`, write `nxt[i]`; swap buffer references after the pass. No allocation in the loop.\n- **Emitters: exactly 3**, `k = 0..2`, at time `t` seconds:\n  `ex = 23.5 + 22 * Math.sin(t * 0.19 + k * 2.094)`\n  `ey = 13.0 + 11.5 * Math.sin(t * 0.27 + k * 4.189)`\n  (phases are thirds of a turn). Round to the nearest cell; inject the 3×3 footprint with weights `[[0.28,0.55,0.28],[0.55,1.00,0.55],[0.28,0.55,0.28]] * 0.14` (`INJ`), clamped to 1.0.\n- **Pointer injection:** `pointermove` only stores the latest client position. Once per frame map it through the CACHED canvas rect to a cell and inject `0.22` (`POINTER_INJ`) at the centre, `0.11` at its 4-neighbours, clamped to 1.0 — at most one injection per frame. `pointerleave` clears it.\n- **Ramp LUT:** build `Uint8Array(768)` (256 RGB triplets) ONCE from 8 stops — `0.00 #030614`, `0.14 #0b2a72`, `0.32 #3a1f92`, `0.50 #8b1c8a`, `0.68 #d4217f`, `0.82 #ff3d8f`, `0.93 #ff9ad0`, `1.00 #ffeaf4` — linearly interpolated. Per cell: `idx = Math.min(255, (Math.pow(v, 0.85) * 255) | 0)`; write `lut[3*idx], lut[3*idx+1], lut[3*idx+2], 255` into the ImageData.\n- **The resolution trick (the entire cost saving):** the field renders into an offscreen canvas `field` sized EXACTLY `W × H` (48×27): one `putImageData` per frame. Then ONE `ctx.drawImage(field, 0, 0, W, H, 0, 0, cssW, cssH)` with `imageSmoothingEnabled = true`, `imageSmoothingQuality = 'high'` upscales it — the smoothing IS the bloom, free from the compositor. NEVER set `ctx.filter` or `ctx.shadowBlur`. Main canvas backing store `cssW*dpr × cssH*dpr`, `dpr = Math.min(devicePixelRatio, 2)` (1.5 at `<= 900px`), `ctx.setTransform(dpr,0,0,dpr,0,0)`. `field` is never resized.\n- **Overlay pass:** a second offscreen canvas `fx` at `cssW*dpr × cssH*dpr`, redrawn ONLY on resize: (1) cell hairlines `rgba(4,7,22,0.50)` 1px at every boundary, (2) heavy lines `rgba(4,7,22,0.75)` 1px every 8 cells, (3) cool edge tint — radial `rgba(11,42,114,0)` at 42% → `rgba(11,42,114,0.24)` at 100%, (4) vignette — radial `rgba(3,6,20,0)` at 52% → `rgba(3,6,20,0.62)` at 100%. Composited each frame with ONE `ctx.drawImage(fx, 0, 0, cssW, cssH)`.\n- **Hottest cell:** the colour pass tracks `maxV / maxX / maxY` and the sum. Write ONE `transform: translate3d(maxX*cellW, maxY*cellH, 0)` to `.hotspot` per frame (cached cell size). Update readout text every 6th frame (`frames % 6 === 0`) only when changed. `T = Math.round(20 + v * 600)` → 20–620°C; mean = `Math.round(20 + (sum/1296) * 600)`.\n- **Per-frame cost budget:** 1296-cell diffusion (≈ 6.5k array reads), 1296 LUT lookups, 1 `putImageData`, 2 `drawImage`, 1 DOM transform, ≤ 1 text write — 3 canvas primitives total. DOM writes are transform/opacity only. NEVER call `getBoundingClientRect` in the loop; the canvas rect and cell size are cached on resize and reused by the pointer mapper. If `document.hidden`, keep the rAF alive and keep counting frames but skip painting.\n- **Grain (CSS):** `.grain`, `position:fixed; inset:-50%; width:200%; height:200%; pointer-events:none; opacity:0.05`, data-URI SVG `feTurbulence` (`type=\"fractalNoise\" baseFrequency=\"0.9\" numOctaves=\"2\" stitchTiles=\"stitch\"` + `feColorMatrix saturate 0`), tile 160px, `animation: grain 0.9s steps(4) infinite` jittering `translate` among `(0,0) (-2%,1%) (1%,-2%) (-1%,2%)`.\n\n---\n\n## C. THE LAYER STACK (back → front)\n\n1. `html, body` — `#04071a`, forced twice (see Failure Guards).\n2. `canvas#heat` — z-index 0: upscaled heat field + cached `fx` overlay.\n3. `.scrim` — z-index 1, `position:fixed; inset:0; background: linear-gradient(180deg, rgba(4,7,22,0.74) 0%, rgba(4,7,22,0.20) 28%, rgba(4,7,22,0.18) 58%, rgba(4,7,22,0.82) 100%)` for text contrast top and bottom.\n4. `.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).\n5. `.hotspot` — z-index 3, the hottest-cell crosshair, `position:fixed; pointer-events:none`.\n6. `.grain` — z-index 4; browser focus outlines stay visible regardless.\n\n---\n\n## D. TOKEN TABLE (defaults at ~1440px)\n\n```\n--bg:            #04071a\n--text:          #eef2fa\n--text-soft:     #c6d0e4\n--muted:         #8a93ab\n--dim:           #5a6480\n--accent:        #ff3d8f\n--accent-dim:    rgba(255,61,143,0.55)\n--accent-line:   rgba(255,61,143,0.30)\n--accent-fill:   rgba(255,61,143,0.06)\n--cool:          #2b5fd0\n--cool-line:     rgba(43,95,208,0.45)\n--line:          rgba(255,255,255,0.14)\n--line-soft:     rgba(255,255,255,0.08)\n--panel:         rgba(4,7,22,0.55)\n--panel-line:    rgba(255,255,255,0.12)\n--logo:          15px\n--logo-track:    0.22em\n--nav:           12px\n--nav-h:         34px\n--eyebrow:       11.5px\n--h1:            64px\n--lead:          17px\n--btn:           13px\n--btn-h:         42px\n--hud-label:     10.5px\n--hud-value:     30px\n--hud-stat:      11.5px\n--legend-label:  10.5px\n--legend-h:      8px\n--legend-w:      252px\n--foot:          12.5px\n--header-y:      24px\n--header-x:      40px\n--hero-x:        40px\n--hero-y:        32px\n--hero-max:      1180px\n--hero-gap:      56px\n--hud-w:         296px\n--hud-pad:       20px\n--foot-y:        22px\n--radius-chip:   3px\n--radius-panel:  6px\n--mono:          \"IBM Plex Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace\n--sans:          \"Inter\", system-ui, -apple-system, \"Segoe UI\", sans-serif\n--ease-out:      cubic-bezier(0.16,1,0.3,1)\n--enter-dur:     0.95s\n--grain-dur:     0.9s\n--pulse-dur:     2.4s\n--blink-dur:     1.06s\n```\n\nCanvas JS constants mirroring the CSS: `FIELD_W 48`, `FIELD_H 27`, `FIELD_W_SM 27`, `FIELD_H_SM 48`, `DPR_MAX 2`, `DPR_MAX_SM 1.5`, `TEMP_MIN 20`, `TEMP_SPAN 600`, `LUT_N 256`, `READOUT_EVERY 6` (plus `K`, `COOL`, `EDGE_DAMP`, `INJ`, `POINTER_INJ` as defined in B). Fonts: one `<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>` + one stylesheet (`display=swap`) loading `Inter:wght@400;500;600` and `IBM+Plex+Mono:wght@400;500`.\n\n---\n\n## E. THE COMPOSITION — exact build order\n\n**.page** is the grid container with three rows: `<header>`, `<main>`, `<footer>`.\n\n**HEADER** — `padding: var(--header-y) var(--header-x); display:flex; align-items:center; justify-content:space-between; gap:24px`.\n\n- `a.brand` (href `#`, accessible name \"Thermal home\"): inline SVG 20×20 — four 7×7 cells (`rx=\"1\"`) at (2,2), (11,2), (2,11), (11,11) filled `#0b2a72`, `#8b1c8a`, `#3a1f92`, `#ff3d8f` — then wordmark `THERMAL`, `var(--mono)`, `font-size:var(--logo); font-weight:500; letter-spacing:var(--logo-track); color:var(--text)` — then a 5px live pixel `background:var(--accent); box-shadow:0 0 8px var(--accent-dim); margin-left:10px` with `blink`.\n- `nav`, `display:flex; align-items:center; gap:30px; height:var(--nav-h)`: exactly 4 links `System`, `Sensors`, `Firmware`, `Docs` (rendered uppercase, `font-size:var(--nav); letter-spacing:0.14em; color:var(--muted)`), all `href=\"#\"`, each with an aria-hidden index `01`–`04` in `var(--dim)`, `margin-right:8px`. Hover `color:var(--text)` 0.18s; index stays dim.\n- Right: `a.btn.btn--ghost` (href `#`) text `Request a pilot`, header variant `height:var(--nav-h); padding:0 16px`.\n- At `<= 900px` the nav is replaced by `button.burger` (`aria-label=\"Open navigation\"`, `aria-expanded=\"false\"`, three 18×1.5px `var(--text)` bars, gap 5px).\n\n**MAIN (hero)** — `display:grid; grid-template-columns: minmax(0,1fr) var(--hud-w); gap:var(--hero-gap); align-items:center; padding:var(--hero-y) var(--hero-x); width:100%; max-width:var(--hero-max); margin:0 auto; min-width:0`. Copy left, HUD right, centred in the 1fr row.\n\nLeft column:\n\n1. `.eyebrow` — mono `var(--eyebrow)`, `letter-spacing:0.18em; text-transform:uppercase; color:var(--dim)`. Copy: `PLANT 04 — FURNACE ARRAY B — 20 Hz` with `PLANT 04` in `var(--accent)`.\n2. `h1` — EXACTLY ONE `<h1>`, `var(--sans); font-weight:600; font-size:var(--h1); letter-spacing:-0.045em; line-height:1.02; color:var(--text); margin:18px 0 0`. Two masked lines (see Motion): line 1 `Heat is data.`, line 2 `Failure is a signal.` with `<em>signal</em>` = `font-family:var(--mono); font-style:normal; font-weight:400; font-size:0.90em; letter-spacing:-0.02em; color:var(--accent)`.\n3. `p.lead` — `font-size:var(--lead); line-height:1.6; color:var(--text-soft); max-width:520px; margin:20px 0 0`. Copy: `Thermal fuses four thousand furnace, bearing and transformer sensors into one continuous heat map, so your team watches the failure form — before the first alarm ever fires.`\n4. `.actions` — `display:flex; gap:12px; flex-wrap:wrap; margin-top:28px`: `a.btn.btn--hot` `Request a pilot`, `a.btn.btn--ghost` `Read the spec sheet`, both `href=\"#\"`, `height:var(--btn-h); padding:0 20px; font-size:var(--btn)`.\n\nRight column `aside.hud` — `width:100%; max-width:var(--hud-w); padding:var(--hud-pad); border:1px solid var(--panel-line); border-radius:var(--radius-panel); background:var(--panel); backdrop-filter:blur(8px); display:grid; gap:16px`.\n\n- `.hud-head` — `display:flex; align-items:center; justify-content:space-between`: label `HOTTEST CELL` mono `var(--hud-label); letter-spacing:0.2em; color:var(--dim)` + a 5px accent pixel with `blink`.\n- `.hud-value` — mono `var(--hud-value); font-weight:500; color:var(--accent); letter-spacing:0.02em; margin-top:-8px`. JS owns it; static fallback `R14 · C27 · 312°C`. JS format: `R` + (row+1) + ` · C` + (col+1) + ` · ` + T + `°C`.\n- `.hud-stats` — a `<dl>`, `display:grid; grid-template-columns:1fr 1fr; gap:10px 16px`, mono `var(--hud-stat)`, labels `var(--dim)`, values `var(--text-soft)`: `MEAN` → JS `214°C` (fallback), `GRID` → `48 × 27`, `EMITTERS` → `3`, `K / COOL` → `0.18 / 0.9962`.\n- `.legend` — `width:var(--legend-w); max-width:100%; height:var(--legend-h); border-radius:2px; border:1px solid var(--line-soft); background:linear-gradient(90deg, #030614 0%, #0b2a72 14%, #3a1f92 32%, #8b1c8a 50%, #d4217f 68%, #ff3d8f 82%, #ff9ad0 93%, #ffeaf4 100%)` (the LUT stops). Below: `.legend-scale` `display:flex; justify-content:space-between; margin-top:8px`, mono `var(--legend-label)` in `var(--dim)`: `20°C`, `320°C`, `620°C`.\n- `.hud-foot` — mono 10px `var(--dim)`: `SAMPLING 20 Hz · DIFFUSION 48×27`.\n\n**FOOTER** — `padding:var(--foot-y) var(--header-x); display:flex; align-items:center; justify-content:space-between; gap:20px; border-top:1px solid var(--line-soft)`. Left mono `var(--foot)` `var(--dim)`: `© 2026 Thermal Industrial Systems`. Centre: a 7px accent square (`margin-right:8px; box-shadow:0 0 8px var(--accent-dim)`) + `sensor mesh nominal · 4,096 nodes`. Right: exactly 3 links `spec sheet`, `status`, `contact`, mono `var(--foot)`, `var(--muted)`, `gap:22px`, hover `color:var(--text)`.\n\n---\n\n## F. THE UI LANGUAGE\n\nTwo button treatments only, both square-cut instrument chips with `border-radius: var(--radius-chip)`. `a.btn` is `inline-flex; align-items:center; justify-content:center; text-decoration:none; font-family:var(--mono); font-weight:500; letter-spacing:0.06em; transition: background 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease`.\n\n- `.btn--hot` (hero primary): `color:#1a0512; border:1px solid rgba(255,61,143,0.55); background:linear-gradient(180deg, #ff9ad0 0%, #ff3d8f 52%, #d4217f 100%); box-shadow:0 14px 40px rgba(255,61,143,0.22), inset 0 1px 0 rgba(255,255,255,0.5)`. Hover: `background:linear-gradient(180deg, #ffe0f0 0%, #ff5fa5 50%, #e0268a 100%); transform:translateY(-1px); box-shadow:0 18px 48px rgba(255,61,143,0.32), inset 0 1px 0 rgba(255,255,255,0.6)`. Active: `transform:none`.\n- `.btn--ghost` (hero secondary + header CTA): `color:var(--accent); border:1px solid var(--accent-line); background:var(--accent-fill)`. Hover: `background:rgba(255,61,143,0.13); border-color:var(--accent-dim); box-shadow:0 0 16px rgba(255,61,143,0.18)`.\n- Nav and footer links are plain mono text — no pills, no backgrounds, no underline ever; hover swaps colour only. Every interactive element gets `:focus-visible { outline:2px solid var(--accent); outline-offset:3px; border-radius:3px }`. Selection: `::selection { background: rgba(255,61,143,0.30); color:#fff }`. No default blue focus ring, no text underline anywhere.\n\n---\n\n## G. MOTION — entrance stagger + named keyframes\n\nAll 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`, one modifier, and an inline `--d`.\n\nStagger table (element → modifier → --d):\n```\nbrand            appear--fade   0.06s\nnav 01 System    appear--fade   0.14s\nnav 02 Sensors   appear--fade   0.20s\nnav 03 Firmware  appear--fade   0.26s\nnav 04 Docs      appear--fade   0.32s\nheader cta       appear--chip   0.40s\neyebrow          appear--fade   0.48s\nh1 line 1        appear--mask   0.62s\nh1 line 2        appear--mask   0.78s\nlead             appear--soft   0.96s   (duration 1.15s)\nhot button       appear--chip   1.10s\nghost button     appear--chip   1.18s\nhud panel        appear--hud    1.26s   (duration 1.25s)\nhud value        appear--fade   1.40s\nlegend strip     appear--wipe   1.46s   (duration 1.10s)\nfooter left      appear--fade   1.58s\nfooter centre    appear--fade   1.64s\nfooter right     appear--fade   1.70s\n```\n\nNamed keyframes (all end at opacity 1 / identity transform):\n```\nin-fade   from { opacity: 0 }                                                 to { opacity: 1 }\nin-soft   from { opacity: 0; transform: translateY(14px); filter: blur(4px) } to { opacity: 1; transform: none; filter: blur(0) }\nin-mask   from { opacity: 0; transform: translateY(105%) }                    to { opacity: 1; transform: none }\nin-chip   from { opacity: 0; transform: translateY(8px) scale(0.96) }         to { opacity: 1; transform: none }\nin-hud    from { opacity: 0; transform: translateX(18px) }                    to { opacity: 1; transform: none }\nin-wipe   from { opacity: 0; clip-path: inset(0 100% 0 0) }                   to { opacity: 1; clip-path: inset(0 -2% 0 0) }\n```\n\nH1 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.08em 0.10em }`, `.line-inner { display:inline-block }`. `in-wipe` reveals the legend gradient left→right like a scale being drawn. Continuous keyframes, independent of the stagger:\n```\nblink      0%,49% { opacity: 1 }  50%,100% { opacity: 0 }                                        (1.06s steps(1) infinite)\nhot-pulse  0% { transform: scale(0.55); opacity: 0.85 }  70% { opacity: 0 }  100% { transform: scale(1.7); opacity: 0 }  (2.4s ease-out infinite)\ngrain      0% { translate(0,0) } 25% { translate(-2%,1%) } 50% { translate(1%,-2%) } 75% { translate(-1%,2%) }          (0.9s steps(4) infinite)\n```\n`.hotspot` is positioned ONLY by JS via `transform: translate3d()`; its child `.hotspot-ring` (1px `var(--accent)` square, `opacity:0.8`) runs `hot-pulse`. Crosshair: `::before` 13px×1px bar, `::after` 1px×13px bar, both `background:var(--accent-dim)`, centred in the cell. The field animates via rAF, not CSS.\n\n---\n\n## H. RESPONSIVE TABLE — token overrides per breakpoint (exact)\n\n```\n>= 1600px:\n  --h1: 76px  --logo: 16px  --nav: 12.5px  --btn: 13.5px  --btn-h: 44px\n  --lead: 18px  --hud-w: 320px  --hud-value: 34px  --legend-w: 276px\n  --header-x: 56px  --hero-x: 56px  --hero-max: 1320px  --hero-gap: 64px  --foot: 13px\n\n>= 1920px:\n  --h1: 88px  --lead: 19px  --hud-w: 340px  --hud-value: 38px  --legend-w: 296px\n  --header-x: 72px  --hero-x: 72px  --hero-max: 1480px  --hero-gap: 72px\n\n1280px – 1599px:\n  --h1: 62px  --header-x: 44px  --hero-x: 44px  --hero-max: 1200px  --hud-w: 300px\n\n901px – 1279px:\n  --h1: 50px  --lead: 15.5px  --nav: 11.5px  --nav-h: 32px  --btn: 12.5px  --btn-h: 40px\n  --header-x: 30px  --hero-x: 30px  --hero-y: 24px  --hero-gap: 36px\n  --hud-w: 260px  --hud-pad: 16px  --hud-value: 26px  --legend-w: 216px  --foot: 12px\n\n>= 901px AND max-height 850px:\n  --h1: 52px  --header-y: 16px  --hero-y: 20px  --lead: 15.5px  --hud-value: 26px\n  (h1 margin-top 12px, lead margin-top 14px, .actions margin-top 20px, .hud gap 12px)\n\n>= 901px AND max-height 720px:\n  --h1: 44px  --header-y: 12px  --hero-y: 12px  --foot-y: 12px  --eyebrow: 11px\n  --lead: 14px  --hud-pad: 16px  --hud-value: 22px  --legend-h: 6px  --nav-h: 30px  --btn-h: 36px\n\n>= 901px (desktop lock, all heights):\n  html, body { height:100%; overflow:hidden }  .page { height:100dvh; overflow:hidden }\n\n<= 900px:\n  no vh lock; page may scroll; canvas field transposes to 27 x 48; dpr cap 1.5\n  nav becomes a fixed overlay menu:\n  - header keeps brand + burger (button.burger, aria-label \"Open navigation\", aria-expanded toggled)\n  - .menu { position:fixed; inset:0; z-index:10; background: rgba(4,7,22,0.94); backdrop-filter: blur(14px);\n      display:flex; flex-direction:column; align-items:flex-start; justify-content:center; padding: 0 32px; gap: 24px;\n      transform: translateY(-100%); transition: transform 0.45s var(--ease-out) }  .menu.open { transform: none }\n  - menu holds the 4 nav links stacked at --nav: 22px plus the ghost CTA chip; Escape, any link click, and resize > 900px all close it\n  hero becomes one column: grid-template-columns: 1fr; gap: 28px; align-items: start\n  --h1: 42px  --header-x: 22px  --hero-x: 22px  --hero-max: 100%  --hud-w: 100%\n\n<= 560px:\n  --h1: 34px  --lead: 15px  --header-x: 18px  --hero-x: 18px  --hud-value: 24px\n  --legend-w: 100%  --foot: 11.5px\n  footer becomes flex-direction:column; align-items:flex-start; gap:10px\n  header CTA is hidden (it lives inside the menu on mobile)\n```\n\n---\n\n## I. FAILURE GUARDS — the page is never blank, never broken\n\n1. **Dark-first:** the FIRST CSS rule is `html, body { background:#04071a !important; color:#eef2fa }`; the body tag carries `style=\"background:#04071a;color:#eef2fa\"`; a later rule restates `html, body { background:#04071a; background: var(--bg, #04071a) }`.\n2. **Resting opacity is 1:** every `.appear` element's un-animated state is its final state (opacity 1, no transform, no clip-path); `fill-mode: both` only holds the FROM state during the delay. With animations failed or JS absent the page is complete, including the fallback readouts.\n3. **Two-rAF fallback:** after two nested `requestAnimationFrame` ticks, inspect every `.appear` with `el.getAnimations({ subtree:false })`; if none reports `running` or `finished` (or `getAnimations` is missing), add `.is-in` to every `.appear`. `.is-in { animation:none !important; opacity:1 !important; transform:none !important; filter:none !important; clip-path:none !important }`.\n4. **Canvas fallback:** if canvas or 2D context is missing, add `.no-canvas` to `<html>` and paint a static heat map in CSS: `.no-canvas body { background: radial-gradient(ellipse at 50% 58%, #ff3d8f 0%, #d4217f 16%, #8b1c8a 34%, #3a1f92 56%, #0b2a72 78%, #030614 100%) }`. HUD, legend and readouts stay readable; push one string into `window.__DEMO__.errors`.\n5. **Reduced motion:** inside `@media (prefers-reduced-motion: reduce)`: `*, *::before, *::after { transition:none !important; animation:none !important }`; force `.appear, .line-inner` to `opacity:1 !important; transform:none !important; clip-path:none !important`; hide `.grain` and `.hotspot-ring`; the canvas paints ONE static frame at fixed `t = 6.4s` (field already structured), parks the crosshair on that frame's hottest cell, sets `ready = true` and `frames = 1`, then stops scheduling. Static, complete, readable.\n6. **Zero console output** in the happy path: no `console.log`, no thrown exceptions; every canvas call null-guarded.\n\n---\n\n## J. THE EXACT JS (only this, vanilla, one IIFE at end of body)\n\n1. **Telemetry first:** `window.__DEMO__ = { name: 'thermal', ready: false, frames: 0, effects: [], errors: [] };` then push exactly these, once, in order: `'heat-diffusion'`, `'inferno-lookup'`, `'hottest-cell-hud'`, `'grain-texture'`. Immediately attach `window.addEventListener('error', e => window.__DEMO__.errors.push(String(e.message || e)))`.\n2. **Cache everything up front:** `document.documentElement`, `canvas#heat`, its 2D context (null → `.no-canvas` path + one errors push), the `field` canvas (`W × H`) and its ImageData, the `fx` overlay canvas, `cur`/`nxt` Float32Array(1296), the LUT, `.hotspot`, `.hud-value`, the mean `dd`, burger, menu, menu links, and `matchMedia('(prefers-reduced-motion: reduce)')`.\n3. **Palette build:** interpolate the 8 section-B stops into the 256-entry LUT once at startup.\n4. **Layout cache:** `resize()`, scheduled by a boolean `resizeQueued` + `requestAnimationFrame` (never a timer): measure the canvas ONCE with `getBoundingClientRect`, store `cssW, cssH, rect, cellW = cssW / W, cellH = cssH / H`, size the backing store with `dpr = Math.min(devicePixelRatio, innerWidth <= 900 ? 1.5 : 2)`, `ctx.setTransform(dpr,0,0,dpr,0,0)`, redraw `fx` (hairlines every cell, heavy lines every 8 cells, cool edge tint, vignette). Switch the field to 27×48 at `innerWidth <= 900`, back to 48×27 above.\n5. **The rAF loop `frame(ts)`:** increment `window.__DEMO__.frames` by exactly 1 every tick. If `document.hidden`, skip painting and reschedule. Otherwise with `t = ts / 1000`: (a) diffusion pass verbatim from B, no allocation, (b) advance and inject the 3 emitters, (c) one queued pointer injection, (d) LUT colour-pass tracking `maxV/maxX/maxY` and the sum, (e) `putImageData`, (f) `drawImage(field …)` upscaled with smoothing, (g) `drawImage(fx …)`, (h) `translate3d(maxX*cellW, maxY*cellH, 0)` on `.hotspot`, (i) every 6th frame, if changed, `R{maxY+1} · C{maxX+1} · {Math.round(20 + maxV*600)}°C` into `.hud-value` and `{Math.round(20 + (sum/1296)*600)}°C` into the mean. On the FIRST completed paint set `window.__DEMO__.ready = true`. Always reschedule `requestAnimationFrame(frame)`.\n6. **Entrance fallback:** two nested rAFs after DOMContentLoaded, then the `.is-in` sweep (Failure Guards 3).\n7. **Menu (mobile only):** burger toggles `.menu.open` and `aria-expanded`; Escape closes; any menu link click closes; resize to `> 900px` closes.\n8. **Reduced motion:** read the query once; if it matches, run one `frame(6400)` with no reschedule — one static paint, `ready = true`, `frames = 1`.\n\nNo other JS. No timers. No libraries. No console calls.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["exactly one h1 with the exact text 'Heat is data. Failure is a signal.' plus semantic header, nav, main, and footer landmarks","canvas#heat paints a non-blank heat map and its pixel hash changes across consecutive frames at 1440x900, with the field sized 48x27 at >=901px and 27x48 at <=900px","window.__DEMO__ starts as name 'thermal' with ready false, ready becomes true after the first painted frame, frames increments once per rAF tick reaching at least 20 in 1.2s at 1440x900, effects includes heat-diffusion, inferno-lookup, hottest-cell-hud and grain-texture, and errors stays empty","the .hud-value readout matches /R\\d+ · C\\d+ · \\d+°C/ and changes over time, and .legend carries the 8-stop thermal linear-gradient containing #ff3d8f with 20°C / 320°C / 620°C scale labels","no horizontal overflow at 390px, 768px, 1440px, or 1920px; at >=901px body has overflow:hidden and .page height is 100dvh; a prefers-reduced-motion: reduce block forces all content visible and static","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"],"usage":{"build_tokens":38840,"build_prompt_tokens":9714,"build_completion_tokens":29126,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":309077,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":22940,"total_tokens":61780},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"32586 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"16 frames, effects: heat-diffusion, inferno-lookup, hottest-cell-hud, grain-texture"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"24 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 39.86, stdev 13.05, coverage 1"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 3690389995 -> 2981976436"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Heat is data.\nFailure is a signal.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Thermal — Continuous Heat Intelligence"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=585 pageErrors=0"}]},"crosscheck":{"slug":"thermal","reviewer_model":"gpt-5.4","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":7,"requirements_met":["Single-frame desktop composition with full-bleed procedural heat canvas","Required header / hero / footer structure is present in the rendered composition","Brand mark, four-item nav, and header ghost CTA match the briefed content","Hero copy matches the specified Thermal messaging and formatting","HUD panel includes hottest-cell label, live value, stats, legend strip, and sampling footer","Footer contains the specified copyright, centre status line, and exactly three links","Color language broadly matches the requested cool-edge / violet / magenta / white-hot ramp","Grid hairlines and heavier periodic guides are visibly present over the heat field","Industrial mono/sans pairing and chip-style buttons are implemented"],"requirements_missed":["Visible hottest-cell marker does not appear to identify the actual hottest visible cell","Crosshair/hotspot effect is visually weak and reads as detached from the heat maxima","The screenshoted HUD temperature conflicts with the brightest visible heat core"],"issues":[{"issue":"Hottest-cell crosshair appears on the wrong location","severity":"high","evidence":"HUD reads \"R25 · C46 · 620°C\", and the visible pink crosshair sits near the lower-right hotspot, while the brightest white-pink heat peaks in the screenshot are upper-left and center-left."},{"issue":"Readout-to-image mismatch makes the instrument HUD look untrustworthy","severity":"high","evidence":"The panel claims a max of 620°C at the lower-right region, but that region is not rendered as the hottest area; the center-left emitter is visibly much hotter."},{"issue":"Hotspot marker is too inconspicuous relative to the briefed cinematic instrument marker","severity":"medium","evidence":"In the screenshot the marker reduces to a small plus and does not clearly read as a crosshair with pulsing ring on the active hottest cell."}],"summary":"Most of the page composition is on-brief and the copy is specific, not filler. The major acceptance failure is the hottest-cell instrumentation: the marker and HUD do not agree with the actual rendered heat field in the screenshot.","usage":{"input":21596,"output":1344,"total":22940,"reasoning":819,"cached":0},"latency_ms":21336}},{"slug":"undertow","title":"Undertow","category":"Landing Page","effect":"A self-illuminating curl-noise particle vortex draws the eye toward an off-centre data drain.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/undertow/index.html","doc":"/prompts/undertow.md","prompt_text":"WHAT IS BEING BUILT\n\nBuild a single-viewport cinematic landing page for a data-infrastructure company named “UNDERTOW”. The page is a dark, editorial product statement: an approximately 900-particle canvas vortex moves behind restrained navigation, a left-aligned hero block, a compact proof strip, and a minimal footer. The visual centre is not a conventional logo lockup. It is an off-centre black drain at 66% horizontal and 51% vertical, surrounded by teal particle acceleration. The hero copy occupies the calmer negative space from 10% to 51% of the viewport.\n\nSet the document title to “Undertow — Data infrastructure in motion” and the root language to en. Use <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">. At viewport widths of 901px and above, html, body, and .page must be exactly 100dvh high and overflow:hidden. At 900px and below, use normal document flow, allow vertical scrolling, and preserve a minimum content height of 100dvh. Never allow horizontal overflow at any supported width.\n\nThe document must contain one <header>, one <main>, one <footer>, and one <nav>. It must contain exactly one <h1>. Every visible control must be a real link or button with an accessible name. Use :focus-visible to render a visible 2px accent ring with a 3px offset.\n\nDo NOT build a dashboard, chart, table, pricing grid, account interface, modal, menu drawer, login form, multi-section marketing site, horizontal carousel, video background, image gallery, 3D scene, or secondary page. Do NOT add a second h1, decorative heading, cookie banner, chat launcher, cursor follower, scroll indicator, framework, library, external asset, or remote API call. Do NOT use image files, SVG files, base64 media, video, GIFs, web fonts other than Google Fonts, or third-party CDNs. Do NOT use React, Tailwind, GSAP, Three.js, Lottie, WebGL, or a build step. Google Fonts is the sole permitted network request; import “Manrope” at weights 400, 500, and 600, with ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, “Segoe UI”, sans-serif as the fallback stack.\n\nTHE BACKDROP\n\nCreate a fixed .backdrop element covering the viewport with z-index 0. Its base background is a near-black radial and linear gradient composite, not a flat fill. Use these exact gradient decisions:\n\n- base color: #05080c;\n- outer field: radial-gradient(circle at 66% 51%, #06131a 0%, #071017 17%, #080d13 35%, #05080c 68%);\n- upper-left cast: radial-gradient(circle at 12% 4%, rgba(42, 62, 73, 0.30) 0%, rgba(14, 25, 32, 0.10) 28%, transparent 52%);\n- lower-edge sink: linear-gradient(180deg, transparent 0%, rgba(4, 7, 10, 0) 62%, rgba(0, 0, 0, 0.54) 100%);\n- drain: radial-gradient(circle at 66% 51%, #000306 0%, #000306 5%, rgba(0, 3, 6, 0.98) 9%, rgba(0, 5, 8, 0.68) 15%, transparent 26%).\n\nAdd a .grain element above the canvas and below the copy. It must be a CSS-generated background, 190px by 190px, repeated at 0, 0, using four inline radial-gradient dots with opacities 0.026, 0.018, 0.021, and 0.012. Apply opacity 1. Do not animate the grain. Add a vignette with inset box-shadow 0 0 190px 64px rgba(0, 0, 0, 0.50), so the left copy and the drain remain the darkest readable regions.\n\nRender one visible <canvas class=\"vortex\"> and one non-DOM offscreen glow canvas. Use exactly 900 particles at 901px and above, 720 from 601px through 900px, 580 from 431px through 600px, and 420 below 431px. Particle positions use CSS-pixel coordinates. Each particle carries x, y, previousX, previousY, age, life, speedBias, size, hueMix, and phase. Particle life is randomized from 5.4s to 10.8s. Initial life offsets must be distributed across the full life interval so the field is populated on the first frame.\n\nThe vortex centre is exactly 66% of viewport width and 51% of viewport height. The radial pull strength is 0.0022 per frame per CSS pixel, capped so a particle does not gain more than 2.4 CSS pixels of inward displacement in one frame. Tangential motion uses two offset sine fields as a cheap curl-noise approximation. For a particle at x, y, and time t, compute fieldA as sin(y * 0.0062 + t * 0.00037 + phase), fieldB as sin(x * 0.0047 - t * 0.00029 - phase), and the curl turn as sin((x + y) * 0.0031 + t * 0.00021 + fieldA * 0.72) + fieldB * 0.42. Apply 0.19 CSS pixels of clockwise tangential displacement, then apply the radial pull, then apply a 0.12px drag. A signed cross-clockwise bias of 0.35 multiplied by speedBias creates occasional counter-current strands without reversing the main vortex.\n\nEach particle is drawn as a 1px line from its previous position to its current position. Use source-over for the core field. Particle stroke colors interpolate from #19333a at 70% acceleration to #5eead4 at 100% acceleration, with the final 14% of life approaching #d8fffa so respawns do not flash. Stroke alpha ranges from 0.09 to 0.72. Particle size is stored as a CSS value from 0.7px to 1.25px, but canvas strokes remain exactly 1 device-independent pixel at DPR 1.5. Do not use round particles or glow sprites.\n\nRespawn a particle when life reaches zero, when it enters the central 3.2% radius, or when it leaves the viewport by more than 80px. Respawn on an elliptical rim between 46% and 78% of the larger viewport dimension from the drain. Bias 72% of respawns into the right and lower arcs. Reset previousX and previousY to x and y before the next update.\n\nEvery animation frame, clear and update all particles, draw all particle strokes once to the visible canvas, then composite the blurred offscreen field. Create the glow canvas at exactly 50% of the visible canvas backing resolution. Draw the visible particle field to the glow canvas through one drawImage call. Set glowCtx.filter to blur(3.2px) exactly once immediately before that drawImage call, reset it to “none” immediately afterward, and never set glowCtx.filter inside the particle loop. Draw the glow canvas into the visible canvas once with globalCompositeOperation “screen” and globalAlpha 0.34. Reset composite modes after compositing.\n\nThe visible canvas uses devicePixelRatio capped at 1.5. Backing dimensions equal rounded CSS dimensions multiplied by 1.5. Set the CSS width and height to 100%. The offscreen canvas uses a 0.5 scale multiplier relative to the visible backing dimensions, not 0.5 device pixels. Resize both canvases only when a resize event or the first measurement changes the stored width or height. The target per-frame cost on a 1440x900 headless viewport is 900 particle updates, 900 single line strokes, one full-resolution offscreen downscale, one filtered offscreen upscale, and no DOM layout reads. Target less than 7ms of JavaScript and canvas work per frame, less than 19,800 visible backing pixels processed per particle coordinate, and no allocation inside the particle loop.\n\nTHE LAYER STACK\n\nUse these exact z-index values from back to front:\n\n- body and .page background: 0;\n- .backdrop gradients: 0;\n- .vortex canvas: 1;\n- .grain: 2;\n- .vignette: 3;\n- header.site-header: 10;\n- main.hero: 20;\n- footer.site-footer: 30;\n- focus rings inherit their control stacking context and must remain above adjacent decoration.\n\nThe header, hero, and footer must be transparent and must not receive pointer-events:none. Decorative background layers must have pointer-events:none and aria-hidden=\"true\". The canvas must have aria-hidden=\"true\". The page must never put dense particle strokes behind body copy with opacity below 0.78; apply a left-side linear scrim from rgba(5, 8, 12, 0.90) 0% to rgba(5, 8, 12, 0.46) 38% to transparent 57%.\n\nTHE TOKEN TABLE\n\nDefine every visual decision in :root with these exact names and values:\n\n- --font-sans: “Manrope”, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, “Segoe UI”, sans-serif;\n- --color-bg: #05080c;\n- --color-surface: #0a1117;\n- --color-copy: #eef7f5;\n- --color-muted: #8da19f;\n- --color-faint: #536563;\n- --color-line: rgba(148, 184, 180, 0.18);\n- --color-accent: #5eead4;\n- --color-accent-bright: #d8fffa;\n- --color-dark-accent: #0f4f49;\n- --header-height: 76px;\n- --header-pad-x: clamp(24px, 4.4vw, 84px);\n- --page-pad-x: clamp(24px, 5.2vw, 100px);\n- --page-pad-y: 32px;\n- --hero-max-width: 624px;\n- --hero-copy-width: 570px;\n- --eyebrow-size: 11px;\n- --eyebrow-tracking: 0.20em;\n- --eyebrow-line: 14px;\n- --h1-size: clamp(52px, 5.25vw, 84px);\n- --h1-line: 0.94;\n- --h1-tracking: -0.055em;\n- --h1-weight: 600;\n- --body-size: 16px;\n- --body-line: 1.65;\n- --body-width: 492px;\n- --nav-size: 12px;\n- --nav-tracking: 0.05em;\n- --button-height: 46px;\n- --button-pad-x: 20px;\n- --button-radius: 999px;\n- --button-font-size: 12px;\n- --button-tracking: 0.04em;\n- --pill-height: 30px;\n- --pill-pad-x: 11px;\n- --pill-radius: 999px;\n- --pill-font-size: 10px;\n- --pill-tracking: 0.13em;\n- --proof-gap: 34px;\n- --proof-value-size: 19px;\n- --proof-label-size: 9px;\n- --proof-label-tracking: 0.14em;\n- --footer-size: 10px;\n- --footer-tracking: 0.12em;\n- --focus-width: 2px;\n- --focus-offset: 3px;\n- --motion-ui: 720ms;\n- --motion-fast: 360ms;\n- --ease-out: cubic-bezier(0.16, 1, 0.3, 1).\n\nTHE COMPOSITION\n\nBuild the body as <div class=\"page\"> containing .backdrop, canvas.vortex, .grain, .vignette, then the semantic page landmarks. The .page is position:relative; isolation:isolate; min-height:100dvh.\n\nBuild header.site-header as a semantic header positioned absolute at inset:0 0 auto 0, height:var(--header-height), and padding-inline:var(--header-pad-x). Its inner .header-row uses display:grid; grid-template-columns:1fr auto 1fr; align-items:center; height:100%. The left child is <a class=\"wordmark\" href=\"#main\" aria-label=\"Undertow home\">UNDERTOW</a>. The center child is <nav aria-label=\"Primary navigation\"> with display:flex; gap:34px. Its exact link labels are “Platform”, “Network”, and “Company”. The right child is <a class=\"header-cta\" href=\"#contact\">Talk to us <span aria-hidden=\"true\">↗</span></a>, justified-self:end. Do not center the navigation by visual guess; preserve the 1fr auto 1fr grid.\n\nBuild main#main.hero with position:relative; z-index:20; display:grid; grid-template-columns:minmax(0, 1fr) minmax(360px, 0.86fr); grid-template-rows:auto 1fr auto; min-height:100dvh; padding:132px var(--page-pad-x) 82px. The hero content starts in column 1, row 2, aligned to center with align-self:center and transform:translateY(-16px). The drain remains visible in column 2 around 66% viewport width.\n\nCreate an .eyebrow row above the heading. It contains a 6px accent status dot and the exact text “DATA INFRASTRUCTURE / LIVE”. Do not place the status dot inside the accessible name. Add one <h1> with the exact lines “Data moves.<br>Undertow knows<br>where it leads.” The h1 uses the h1 tokens, max-width:624px, margin:22px 0 28px, and text-wrap:balance.\n\nPlace this exact paragraph below the h1: “Undertow maps live data flow across your systems, reveals pressure before it becomes failure, and routes every workload toward a clearer path.” Set its exact width to var(--body-width), color:var(--color-muted), and margin:0 0 32px.\n\nCreate .actions as display:flex; gap:12px; align-items:center. The primary link is <a class=\"button button-primary\" href=\"#contact\">Explore the platform <span aria-hidden=\"true\">→</span></a>. The secondary link is <a class=\"button button-secondary\" href=\"#network\">View the network</a>. Do not use disabled controls.\n\nCreate .proof as a semantic <ul> display:grid; grid-template-columns:repeat(3, max-content); gap:var(--proof-gap); margin:38px 0 0; padding:0; list-style:none. The three exact items are “99.999% / UPTIME TARGET”, “38 ms / MEDIAN ROUTE TIME”, and “2.4B / EVENTS / DAY”. Each li contains a .proof-value and a .proof-label. Do not label the values as observed results; they are product claims shown as landing-page content.\n\nBuild footer.site-footer as position:absolute; inset:auto var(--page-pad-x) 28px 0; z-index:30; display:grid; grid-template-columns:1fr auto 1fr; align-items:center; min-height:24px; font-size:var(--footer-size); letter-spacing:var(--footer-tracking); color:var(--color-faint). The left text is “© 2026 UNDERTOW”. The center is a 6px teal status dot with the visually hidden text “All systems operational”; do not attach an accessible name to a non-control span. The right link is <a href=\"mailto:hello@undertow.systems\">hello@undertow.systems</a>, justified-self:end.\n\nTHE UI LANGUAGE\n\nStyle the wordmark as color:var(--color-copy), font-size:14px, font-weight:600, letter-spacing:0.18em, and text-decoration:none. Its hover color is var(--color-accent). Nav links are color:var(--color-muted), font-size:var(--nav-size), letter-spacing:var(--nav-tracking), text-decoration:none, and transition:color 180ms ease. Hover and focus-visible color is var(--color-accent-bright).\n\nThe header CTA has height:36px, padding:0 15px, display:inline-flex, align-items:center, gap:9px, border:1px solid rgba(94, 234, 212, 0.34), border-radius:999px, color:var(--color-accent), text-decoration:none, font-size:11px, letter-spacing:0.04em, background:rgba(11, 31, 32, 0.34). Hover transforms to translateY(-1px), changes border-color to rgba(94, 234, 212, 0.72), and changes color to var(--color-accent-bright).\n\nThe primary button has exact background:linear-gradient(110deg, #d8fffa 0%, #5eead4 54%, #3ccfba 100%); color:#05201d; border:1px solid rgba(216, 255, 250, 0.72); box-shadow:0 8px 30px rgba(24, 183, 164, 0.13), inset 0 1px 0 rgba(255, 255, 255, 0.55). Its hover transform is translateY(-2px); its shadow becomes 0 12px 36px rgba(24, 183, 164, 0.22), inset 0 1px 0 rgba(255, 255, 255, 0.72). Its active transform is translateY(0). The secondary button uses background:rgba(9, 17, 22, 0.62), border:1px solid var(--color-line), and color:var(--color-copy). Hover changes background to rgba(15, 34, 38, 0.82), border-color to rgba(94, 234, 212, 0.46), and transform to translateY(-2px).\n\nAll focus selectors use outline:var(--focus-width) solid var(--color-accent); outline-offset:var(--focus-offset). Do not remove the browser outline without replacing it. Transitions animate transform, opacity, color, border-color, background, and box-shadow; canvas particle motion is rendered inside canvas and does not trigger DOM layout.\n\nMOTION\n\nUse named keyframes named reveal-up, signal-pulse, and drain-breathe. reveal-up animates opacity from 0 to 1 and transform from translate3d(0, 18px, 0) to translate3d(0, 0, 0) over 720ms with var(--ease-out). signal-pulse animates opacity from 0.45 to 1 and transform from scale(0.82) to scale(1) over 1800ms ease-in-out. drain-breathe animates the static drain’s opacity from 0.88 to 1 and transform from scale(0.985) to scale(1) over 5200ms ease-in-out, alternate, infinite. Because the drain is part of a CSS pseudo-element, preserve its final opacity at 1 and set transform-origin:66% 51%.\n\nApply this exact stagger:\n\n- header .wordmark -> reveal-up -> 80ms;\n- primary nav -> reveal-up -> 150ms;\n- header CTA -> reveal-up -> 220ms;\n- .eyebrow -> reveal-up -> 300ms;\n- h1 -> reveal-up -> 390ms;\n- hero paragraph -> reveal-up -> 500ms;\n- .button-primary -> reveal-up -> 610ms;\n- .button-secondary -> reveal-up -> 670ms;\n- .proof li:nth-child(1) -> reveal-up -> 760ms;\n- .proof li:nth-child(2) -> reveal-up -> 820ms;\n- .proof li:nth-child(3) -> reveal-up -> 880ms;\n- footer -> reveal-up -> 980ms;\n- .status-dot -> signal-pulse -> 1100ms;\n- drain pseudo-element -> drain-breathe -> 0ms.\n\nAdd these exact animations:\n\n- .wordmark, nav, .header-cta, .eyebrow, h1, .hero-copy p, .actions > *, .proof li, footer { animation:reveal-up var(--motion-ui) var(--ease-out) both; }\n- .status-dot { animation:signal-pulse 1800ms ease-in-out 1100ms 2 both; }\n- .vortex-drain { animation:drain-breathe 5200ms ease-in-out 0ms infinite alternate; }\n\nSet every animated element’s resting opacity to 1. Do not leave content dependent on JavaScript for visibility. The JavaScript adds no entrance classes; the CSS animation is declarative and must reveal to opacity 1.\n\nThe canvas rAF loop is continuous only when reduced motion is not preferred. It increments telemetry.frames exactly once per callback. It must sustain at least 20 callbacks during any 1.2-second sample at 1440x900. Cache width, height, particle count, and particle array references outside the loop. Never call getBoundingClientRect, getComputedStyle, offsetWidth, offsetHeight, clientWidth, or clientHeight inside the loop.\n\nTHE RESPONSIVE TABLE\n\nApply these exact overrides:\n\nAt 1440x900 and 1920x1080:\n- use all root tokens;\n- --page-pad-x:100px;\n- --page-pad-y:32px;\n- --header-height:76px;\n- --hero-max-width:624px;\n- --h1-size:84px at 1920px and 76px at 1440px through clamp resolution;\n- particle count:900;\n- hero grid columns:minmax(0, 1fr) minmax(360px, 0.86fr);\n- footer bottom:28px.\n\nAt 768x1024:\n- --header-height:68px;\n- --header-pad-x:32px;\n- --page-pad-x:42px;\n- --h1-size:64px;\n- --h1-line:0.96;\n- --body-width:560px;\n- --proof-gap:28px;\n- particle count:720;\n- hero padding-top:112px;\n- hero padding-bottom:88px;\n- hero grid columns:minmax(0, 1fr) minmax(210px, 0.55fr);\n- drain remains at 66% 51%;\n- allow vertical document scroll.\n\nAt 600px maximum width:\n- --header-height:64px;\n- --header-pad-x:22px;\n- --page-pad-x:24px;\n- --h1-size:52px;\n- --h1-line:0.98;\n- --body-size:15px;\n- --body-width:100%;\n- --proof-gap:20px;\n- --proof-value-size:17px;\n- particle count:580;\n- header nav display:none;\n- header grid-template-columns:1fr auto;\n- hero grid-template-columns:1fr;\n- hero padding:112px 24px 78px;\n- hero content transform:none;\n- .actions flex-wrap:wrap;\n- .proof grid-template-columns:repeat(3, minmax(0, 1fr));\n- footer position:relative;\n- footer inset:auto;\n- margin:0 24px 26px;\n- footer grid-template-columns:1fr auto;\n- footer center display:none;\n- allow vertical document scroll.\n\nAt 430px maximum width:\n- --header-pad-x:18px;\n- --page-pad-x:18px;\n- --h1-size:44px;\n- --h1-line:1.00;\n- --h1-tracking:-0.045em;\n- --body-size:15px;\n- --body-line:1.58;\n- --button-height:46px;\n- --button-pad-x:17px;\n- particle count:420;\n- header CTA font-size:10px;\n- header CTA padding-inline:12px;\n- hero padding:96px 18px 64px;\n- .actions display:grid;\n- grid-template-columns:1fr;\n- gap:10px;\n- .actions .button width:100%;\n- .proof grid-template-columns:1fr;\n- gap:18px;\n- .proof li display:grid;\n- grid-template-columns:92px 1fr;\n- align-items:baseline;\n- footer margin-inline:18px;\n- footer font-size:9px;\n\nAt 901px minimum width and 820px maximum height:\n- --header-height:64px;\n- --page-pad-x:56px;\n- --h1-size:62px;\n- --body-size:15px;\n- hero padding-top:102px;\n- hero padding-bottom:64px;\n- .proof margin-top:26px;\n- footer bottom:18px;\n- particle count:760;\n- retain single-viewport clipping.\n\nAt 900px maximum width and 720px maximum height:\n- --header-height:60px;\n- --h1-size:48px;\n- --body-line:1.5;\n- hero padding-top:88px;\n- hero padding-bottom:64px;\n- .proof margin-top:22px;\n- particle count:560;\n- allow vertical document scroll.\n\nFAILURE GUARDS\n\nEvery non-canvas content element must have opacity:1 as its base state. CSS animation “both” states must finish at opacity:1 and transform:none. If canvas support is unavailable, keep the CSS gradient backdrop, drain, grain, vignette, and copy visible. The h1 and all navigation must remain usable with JavaScript disabled.\n\nUse a two-rAF fallback. The first rAF is scheduled after setup. The second rAF marks telemetry.ready=true and starts the continuous particle loop only after the first frame has been committed. If the main animation rAF has not advanced after 1500ms, a setTimeout watchdog may call start once, guarded by a boolean, but it must not create a second loop. This two-rAF ordering prevents ready=true before first paint while guaranteeing the animation scheduler is active.\n\nUnder prefers-reduced-motion:reduce, remove all entrance, pulse, and drain animations with animation:none; force every animated element to opacity:1 and transform:none; stop the particle loop after rendering exactly one static frame; retain the full CSS backdrop and 1px particle field; set telemetry.frames to 1; leave telemetry.ready true. Buttons must have no movement on hover under reduced motion, changing only color or border-color. Do not call cancelAnimationFrame after a loop that was never started.\n\nAdd window error and unhandledrejection listeners that push human-readable strings into telemetry.errors. Wrap setup and animation callbacks in try/catch. On a catch, push the error message, set telemetry.ready=true, and stop scheduling further frames. Never write to console. The document must remain readable if WebGL, external fonts, or canvas APIs are unavailable.\n\nTHE EXACT JS\n\nUse this exact script at the end of body. It has no dependencies and no network requests:\n\n<script>\n(() => {\n  'use strict';\n  const telemetry = window.__DEMO__ = { name: 'undertow', ready: false, frames: 0, effects: [], errors: [] };\n  const reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n  const canvas = document.querySelector('.vortex');\n  const ctx = canvas.getContext('2d', { alpha: true, desynchronized: true });\n  const glow = document.createElement('canvas');\n  const glowCtx = glow.getContext('2d', { alpha: true, desynchronized: true });\n  const drain = { x: 0, y: 0 };\n  let width = 0;\n  let height = 0;\n  let ratio = 1;\n  let particles = [];\n  let count = 900;\n  let raf = 0;\n  let running = false;\n  let started = false;\n  let last = 0;\n  let time = 0;\n\n  const pushError = value => {\n    if (telemetry.errors.length < 20) telemetry.errors.push(String(value && value.message ? value.message : value));\n  };\n\n  window.addEventListener('error', event => pushError(event.error || event.message));\n  window.addEventListener('unhandledrejection', event => pushError(event.reason));\n\n  const makeParticle = (initial, i) => {\n    const life = 5.4 + Math.random() * 5.4;\n    const rim = Math.max(width, height) * (0.46 + Math.random() * 0.32);\n    const angle = Math.random() * Math.PI * 2;\n    let x;\n    let y;\n    if (initial) {\n      x = drain.x + Math.cos(angle) * rim * (0.78 + Math.random() * 0.22);\n      y = drain.y + Math.sin(angle) * rim * (0.56 + Math.random() * 0.22);\n    } else {\n      const biased = Math.random() < 0.72;\n      const a = biased ? (Math.random() < 0.5 ? 0 : Math.PI) + (Math.random() - 0.5) * 2.2 : Math.random() * Math.PI * 2;\n      x = drain.x + Math.cos(a) * rim;\n      y = drain.y + Math.sin(a) * rim * 0.72;\n    }\n    const p = {\n      x, y, previousX: x, previousY: y,\n      life, age: initial ? Math.random() * life : 0,\n      speedBias: 0.65 + Math.random() * 0.7,\n      size: 0.7 + Math.random() * 0.55,\n      hueMix: 0.38 + Math.random() * 0.62,\n      phase: (i % 97) * 0.0647 + Math.random() * 0.3\n    };\n    return p;\n  };\n\n  const sizeCanvas = () => {\n    width = window.innerWidth;\n    height = window.innerHeight;\n    ratio = Math.min(window.devicePixelRatio || 1, 1.5);\n    canvas.width = Math.round(width * ratio);\n    canvas.height = Math.round(height * ratio);\n    canvas.style.width = width + 'px';\n    canvas.style.height = height + 'px';\n    ctx.setTransform(ratio, 0, 0, ratio, 0, 0);\n    const glowScale = 0.5;\n    glow.width = Math.max(1, Math.round(canvas.width * glowScale));\n    glow.height = Math.max(1, Math.round(canvas.height * glowScale));\n    glowCtx.setTransform(glowScale, 0, 0, glowScale, 0, 0);\n    drain.x = width * 0.66;\n    drain.y = height * 0.51;\n    count = width <= 430 ? 420 : width <= 600 ? 580 : width <= 900 ? 720 : 900;\n    particles = new Array(count);\n    for (let i = 0; i < count; i += 1) particles[i] = makeParticle(true, i);\n    ctx.clearRect(0, 0, width, height);\n  };\n\n  const resetParticle = (p, i) => {\n    const old = p;\n    const next = makeParticle(false, i);\n    p.x = next.x;\n    p.y = next.y;\n    p.previousX = next.x;\n    p.previousY = next.y;\n    p.life = next.life;\n    p.age = 0;\n    p.speedBias = next.speedBias;\n    p.size = next.size;\n    p.hueMix = next.hueMix;\n    p.phase = next.phase;\n  };\n\n  const render = delta => {\n    ctx.clearRect(0, 0, width, height);\n    glowCtx.clearRect(0, 0, width, height);\n    time += delta;\n    const frameScale = Math.min(1.6, Math.max(0.35, delta / 16.667));\n    for (let i = 0; i < count; i += 1) {\n      const p = particles[i];\n      p.previousX = p.x;\n      p.previousY = p.y;\n      const dx = drain.x - p.x;\n      const dy = drain.y - p.y;\n      const distance = Math.sqrt(dx * dx + dy * dy) || 1;\n      const fieldA = Math.sin(p.y * 0.0062 + time * 0.00037 + p.phase);\n      const fieldB = Math.sin(p.x * 0.0047 - time * 0.00029 - p.phase);\n      const curl = Math.sin((p.x + p.y) * 0.0031 + time * 0.00021 + fieldA * 0.72) + fieldB * 0.42;\n      const inward = Math.min(2.4, distance * 0.0022 * frameScale);\n      const tangent = 0.19 * frameScale;\n      p.x += (-dy / distance) * tangent + (dx / distance) * inward - p.x * 0.00001;\n      p.y += (dx / distance) * tangent + (dy / distance) * inward - p.y * 0.00001;\n      p.x += curl * 0.055 * p.speedBias * frameScale;\n      p.y -= Math.abs(curl) * 0.035 * p.speedBias * frameScale;\n      p.age += delta;\n      const progress = p.age / p.life;\n      const acceleration = Math.min(1, Math.max(0, (distance - 28) / Math.max(width, height) * 1.7));\n      if (progress >= 1 || distance < Math.max(24, Math.min(width, height) * 0.032) || p.x < -80 || p.x > width + 80 || p.y < -80 || p.y > height + 80) {\n        resetParticle(p, i);\n        continue;\n      }\n      const lifeFade = Math.min(1, progress * 10, (1 - progress) * 7);\n      const mix = acceleration * p.hueMix;\n      const red = Math.round(25 + mix * 69);\n      const green = Math.round(51 + mix * 183);\n      const blue = Math.round(58 + mix * 194);\n      const alpha = (0.09 + acceleration * 0.63) * Math.max(0, lifeFade);\n      ctx.beginPath();\n      ctx.moveTo(p.previousX, p.previousY);\n      ctx.lineTo(p.x, p.y);\n      ctx.strokeStyle = 'rgba(' + red + ',' + green + ',' + blue + ',' + alpha.toFixed(3) + ')';\n      ctx.lineWidth = 1;\n      ctx.stroke();\n    }\n    glowCtx.globalCompositeOperation = 'source-over';\n    glowCtx.filter = 'blur(3.2px)';\n    glowCtx.drawImage(canvas, 0, 0, width, height);\n    glowCtx.filter = 'none';\n    ctx.save();\n    ctx.globalCompositeOperation = 'screen';\n    ctx.globalAlpha = 0.34;\n    ctx.drawImage(glow, 0, 0, width, height);\n    ctx.restore();\n  };\n\n  const frame = now => {\n    if (!running) return;\n    try {\n      const delta = last ? Math.min(40, now - last) : 16.667;\n      last = now;\n      render(delta);\n      telemetry.frames += 1;\n      raf = window.requestAnimationFrame(frame);\n    } catch (error) {\n      pushError(error);\n      running = false;\n      telemetry.ready = true;\n    }\n  };\n\n  const start = () => {\n    if (started || !ctx || !glowCtx) return;\n    started = true;\n    running = true;\n    last = 0;\n    telemetry.effects.push('vortex-particle-field', 'offscreen-glow-composite', 'interface-reveal');\n    raf = window.requestAnimationFrame(frame);\n    window.setTimeout(() => {\n      if (!started) start();\n    }, 1500);\n  };\n\n  try {\n    sizeCanvas();\n    window.addEventListener('resize', sizeCanvas, { passive: true });\n    if (!ctx || !glowCtx) {\n      canvas.style.display = 'none';\n      telemetry.ready = true;\n    } else if (reduced) {\n      render(16.667);\n      telemetry.frames = 1;\n      telemetry.effects.push('vortex-particle-field', 'offscreen-glow-composite', 'static-reduced-motion');\n      window.requestAnimationFrame(() => { window.requestAnimationFrame(() => { telemetry.ready = true; }); });\n    } else {\n      window.requestAnimationFrame(() => {\n        window.requestAnimationFrame(() => {\n          start();\n          telemetry.ready = true;\n        });\n      });\n    }\n  } catch (error) {\n    pushError(error);\n    canvas.style.display = 'none';\n    telemetry.ready = true;\n  }\n})();\n</script>\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["A headless browser reports document.title exactly as “Undertow — Data infrastructure in motion”, document.documentElement.lang as “en”, exactly one h1, and semantic header, nav, main, and footer elements.","At 1440x900 and 1920x1080 the page has no vertical document scrollbar and no horizontal overflow, while at 390x844, 768x1024, and 600x900 all content remains inside the viewport width and the page remains readable with vertical scrolling.","window.__DEMO__ has name “undertow”, ready is true after first paint, frames increases by at least 20 over a 1200ms interval at 1440x900, effects includes “vortex-particle-field” and “offscreen-glow-composite”, and errors remains empty.","The visible canvas has 900 particle line segments on the initial desktop frame, the offscreen glow is composited once per frame, and no getBoundingClientRect or getComputedStyle call occurs inside the animation loop.","With prefers-reduced-motion set to reduce, telemetry.frames equals 1, all content has opacity 1, no CSS entrance, pulse, or drain animation is active, and every visible link retains a non-transparent :focus-visible outline.","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"],"usage":{"build_tokens":45167,"build_prompt_tokens":8886,"build_completion_tokens":36281,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":616671,"wasted_tokens":0,"repair_tokens":110185,"crosscheck_tokens":19769,"total_tokens":175121},"verification":{"passed":true,"passed_checks":20,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"27892 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"5 frames, effects: vortex-particle-field, offscreen-glow-composite, interface-reveal"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"46 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 1.05, stdev 11.51, coverage 0.0094"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 729743888 -> 91465120"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Data moves.\nUndertow knows\nwhere it leads.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Undertow — Data infrastructure in motion"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=434 pageErrors=0"}]},"crosscheck":{"slug":"undertow","reviewer_model":"gpt-5.4","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":6,"requirements_met":["single-viewport dark editorial landing page structure is present","left-aligned hero block occupies the calmer left side of the viewport","header uses restrained navigation with wordmark left, nav centered, CTA right","hero copy matches the specified headline and paragraph text","proof strip exists with the three requested claims","canvas, grain, vignette, and dark gradient backdrop layers are present","overall palette stays near-black with teal accents","footer is minimal and visually subdued"],"requirements_missed":["visual centre is not a clearly legible off-centre black drain at 66% / 51%","teal particle acceleration around the drain is not visibly established in the rendered composition","left-side footer alignment does not match the required inset padding","exact footer layout from the brief is not honored in CSS","the required exact JavaScript implementation was not used","responsive particle-count table is not fully implemented","several motion and particle-behavior details differ from the brief"],"issues":[{"issue":"The central drain, which was supposed to be the visual centrepiece, is effectively missing in the rendered image.","severity":"high","evidence":"Brief: “The visual centre ... is an off-centre black drain at 66% horizontal and 51% vertical, surrounded by teal particle acceleration.” Screenshot shows a mostly empty dark right half with only faint vertical glow bands; no distinct drain ring or visible sink point."},{"issue":"The vortex field does not read as an approximately 900-particle cinematic swirl concentrated around the drain.","severity":"high","evidence":"Screenshot shows sparse, barely perceptible speckling rather than a readable vortex with teal acceleration. The intended focal motion field behind the hero is visually absent."},{"issue":"Footer horizontal placement is wrong; the left edge is flush to the viewport instead of padded to page inset.","severity":"medium","evidence":"CSS uses `.site-footer { inset: auto var(--page-pad-x) 28px 0; }` while the brief required `inset:auto var(--page-pad-x) 28px 0` only because the right value differs, but the stated composition also requires a three-column footer aligned with page padding. In the screenshot “© 2026 UNDERTOW” sits hard against the left edge, unlike the hero/header padding."},{"issue":"The exact JS contract in the design brief was not followed; the generated script materially changes the particle implementation.","severity":"medium","evidence":"Brief said “Use this exact script at the end of body.” Actual script changes `getContext` options, changes `glowCtx.setTransform(glowScale...)` to `setTransform(1...)`, adds `crossBias`, changes particle age progression from `p.age += delta` to `p.age += delta * 0.001`, calls `render(16.667)` inside `sizeCanvas()`, and alters the watchdog/start ordering."},{"issue":"Responsive particle counts do not match the brief’s override table.","severity":"medium","evidence":"Script uses only `420 / 580 / 720 / 900`. Brief additionally required `760` at `min-width:901px and max-height:820px` and `560` at `max-width:900px and max-height:720px`."},{"issue":"The glow canvas scaling implementation does not match the requested technique.","severity":"medium","evidence":"Brief required the offscreen canvas to use a 0.5 scale multiplier relative to visible backing dimensions and the provided exact JS sets `glowCtx.setTransform(glowScale, 0, 0, glowScale, 0, 0)`. Generated code instead sets `glowCtx.setTransform(1, 0, 0, 1, 0, 0)`."},{"issue":"The footer center operational-status treatment is visually weak and not centered as a distinct middle element in the screenshot.","severity":"low","evidence":"Brief required a three-column footer with the center as a 6px teal status dot. In the screenshot the middle area reads as part of the email string rather than a clearly separated centered status marker."},{"issue":"The page reads more like a generic dark SaaS hero than the specified cinematic product statement built around a drain/vortex phenomenon.","severity":"medium","evidence":"The screenshot is dominated by a standard left-copy/right-empty composition. The unique centerpiece—the off-centre drain with particle acceleration—does not define the page."}],"summary":"The copy block and general dark premium styling are in place, but the page misses the main visual requirement: there is no convincing off-centre drain or readable teal vortex anchoring the composition. It looks competent but generic, and several exact motion and particle implementation requirements were not followed.","usage":{"input":18457,"output":1312,"total":19769,"reasoning":178,"cached":0},"latency_ms":17357}},{"slug":"upslope","title":"Upslope","category":"Landing Page","effect":"A single market curve draws across a black editorial slide, then a sector marker and eight quarterly ticks resolve around it.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/upslope/index.html","doc":"/prompts/upslope.md","prompt_text":"A. WHAT IS BEING BUILT\n\nBuild one self-contained index.html for Moraine, a venture-research house publishing one market curve at a time. The document title must be “Moraine — Upslope”. Set the root language attribute to lang=\"en\". Use a responsive viewport meta tag. The page is a cinematic, single-slide landing page, not a dashboard, scrolling article, chart tool, or application shell.\n\nThe desktop experience is locked to one viewport: at widths of 901px and above, set html and body to width:100%, height:100%, and height:100dvh where supported; set body overflow:hidden. The composition must remain visible at 1440×900, 1920×1080, and any desktop viewport whose height is at least 600px. At widths of 900px and below, permit vertical document scrolling and use overflow-x:hidden; never permit horizontal scrolling at 390×844, 768×1024, 1440×900, or 1920×1080.\n\nUse semantic document structure: one header, one main, and one footer. The header contains the Moraine wordmark and slide identifier. Main contains the editorial introduction and the market-chart figure. Footer contains the research descriptor, publication label, and one inquiry link. Include exactly one h1: “Markets move before consensus catches up.” No other h1 element is permitted. The chart labels use div, span, text, or SVG text; they must not become additional headings.\n\nUse semantic nav only if it contains a visible interactive or navigational control; otherwise omit nav rather than adding an empty landmark. Every visible link or button must have an explicit accessible name through its text or aria-label. Every focusable control must display a visible :focus-visible ring.\n\nDo NOT build:\n- A responsive dashboard, tooltip system, data table, legend panel, axis cross-selector, date filter, account menu, or chart settings UI.\n- More than one chart, more than one curve, multiple series, bars, candlesticks, particles, floating cards, glassmorphic panels, or decorative dashboard chrome.\n- Remote imagery, raster images, video, GIFs, base64 assets, icon fonts, third-party scripts, frameworks, CSS libraries, animation libraries, or runtime APIs beyond browser-native JavaScript.\n- A continuous looping ambient animation. Motion occurs during the one-time entrance and on one deliberate pointer-triggered redraw.\n- A mobile-first stacked chart that exceeds the viewport or creates horizontal overflow.\n- A cookie banner, modal, cookie wall, lead-capture form, newsletter form, or secondary navigation.\n- A canvas renderer. This concept must use one SVG so the curve remains a vector object and the sector marker remains inspectable.\n- Any copy implying live financial advice, guaranteed returns, or an actual investment recommendation.\n\nB. THE BACKDROP\n\nCreate one fixed, full-viewport backdrop element named .slide-backdrop. It is a CSS-painted field, not an image. Its base color is #0B0B0F. Add three fixed radial gradients and one linear wash:\n\n1. An indigo radial gradient centered at 18% 18%, fading from rgba(91,43,217,0.20) at 0% to rgba(91,43,217,0) at 48%.\n2. A pink radial gradient centered at 78% 68%, fading from rgba(255,111,181,0.10) at 0% to rgba(255,111,181,0) at 42%.\n3. A blue radial gradient centered at 58% 44%, fading from rgba(63,186,255,0.055) at 0% to rgba(63,186,255,0) at 36%.\n4. A top-to-bottom linear gradient from rgba(255,255,255,0.025) at 0% to rgba(255,255,255,0) at 34%.\n\nThe backdrop has exactly 14 stationary dust points. Implement them as 14 CSS-positioned span elements inside .backdrop-dust. Each point is 2×2px, border-radius:50%, and uses a solid color selected from rgba(255,255,255,0.20), rgba(255,255,255,0.12), or rgba(138,143,156,0.18). Their x coordinates are 4%, 9%, 14%, 19%, 25%, 31%, 37%, 43%, 49%, 55%, 61%, 68%, 76%, and 84%. Their y coordinates are 17%, 73%, 29%, 84%, 51%, 12%, 66%, 37%, 91%, 6%, 78%, 47%, 20%, and 58%. Do not animate these points.\n\nAdd a 1px horizontal rail at y=72px using the header’s bottom border. Add a 1px vertical rail at x:5vw from the top rail to the bottom edge at 20% white opacity. The rail is visual structure, not a control.\n\nThe hero chart is one inline SVG with viewBox=\"0 0 1200 620\", preserveAspectRatio=\"none\", overflow:visible. It occupies the lower 62% of the desktop viewport. Give its wrapper width:110vw and a left offset of -5vw so the curve and chart bounds bleed beyond the right edge. Do not use a higher-resolution canvas. The SVG coordinate system is the resolution strategy: fixed viewBox coordinates provide a stable 1200×620 drawing surface, while CSS scales it to the viewport. The SVG must have aria-labelledby and a title/description pair.\n\nThe chart contains exactly 8 x-axis ticks, one y-axis title, one curve path ghost, one area path, one visible curve path, one sector marker group, one 100×100 halo circle, one 40px crosshair line, one center dot, one numeric label, and one small caption. There are no second paths representing a comparison series.\n\nThe resting area fill uses a vertical linear gradient: #5B2BD9 at 0% with 0.22 alpha, #8D4DE4 at 48% with 0.11 alpha, #FF6FB5 at 100% with 0 alpha. The fill extends to y=520 and must fade to fully transparent at the bottom. The visible path uses a horizontal linear gradient from #5B2BD9 at 0% to #FF6FB5 at 100%. The ghost path is 24px wide with stroke rgba(255,255,255,0.09), fill=\"none\", stroke-linecap=\"round\", and stroke-linejoin=\"round\". The visible path is 4px wide with the same cap and join. It must be a smooth cubic path, not a polyline. Use this exact initial path: M -70 492 C 82 475, 145 438, 230 448 S 350 350, 430 374 S 560 282, 650 305 S 760 202, 850 226 S 985 128, 1270 74.\n\nThe marker is centered at cx=\"850\", cy=\"226\". Its halo is exactly 100×100 with fill rgba(255,255,255,0.07) and no stroke. A 2px vertical tick drops 40px from the marker center to the x-axis region. The center dot is 10×10 with a radial or linear gradient from #3FBAFF to #536EFB. The marker label is “$18.4B” and its caption is “AI infrastructure · 2031”.\n\nThe x-axis baseline is x1=\"0\" y1=\"520\" x2=\"1200\" y2=\"520\", stroke #8A8F9C at 0.34 alpha and width 1. It is divided by 8 evenly spaced ticks at x=0, 150, 300, 450, 600, 750, 900, and 1050. Each tick has a 7px vertical stroke and a label. Labels read “Q1 ’24”, “Q2 ’24”, “Q3 ’24”, “Q4 ’24”, “Q1 ’25”, “Q2 ’25”, “Q3 ’25”, “Q4 ’25”. The labels are 12px and use #8A8F9C at 0.82 alpha. The y-axis title is rotated -90 degrees, positioned at x=34 and y=250, and reads “MARKET VALUE / USD”.\n\nThe per-frame animation budget is explicit: the entrance loop must perform no more than 2 style writes per frame, read no layout dimensions, and allocate no objects inside the rAF callback. The only animated values are SVG stroke-dashoffset, SVG opacity, and transform applied to the marker group. The rAF loop must not call getBoundingClientRect, getComputedStyle, querySelector, createElement, or canvas APIs. Cache all elements and path lengths before the loop starts. Use a duration of 1800ms, a 60fps target, and a maximum of 108 entrance frames. The redraw interaction is 900ms with a maximum of 54 frames. Both loops are time-based and must remove their frame request when complete.\n\nC. THE LAYER STACK\n\nBuild the page in this exact back-to-front z-index order:\n\n- z-index:0 — .slide-backdrop and its three gradients.\n- z-index:1 — .backdrop-dust, containing the 14 stationary points.\n- z-index:2 — .vertical-rail, the fixed 1px left guide.\n- z-index:3 — .slide-header, positioned at the top 72px rail.\n- z-index:4 — .hero-copy, positioned in the upper-left text region.\n- z-index:5 — .chart-stage, the SVG’s wide lower-region wrapper.\n- z-index:6 — .chart-annotation, only if needed for a non-SVG caption; prefer SVG text.\n- z-index:7 — .slide-footer, positioned at the bottom.\n- z-index:8 — :focus-visible ring state; the browser ring must appear above neighboring content.\n\nThe chart must visually pass behind the copy, but the copy remains above the chart. The chart’s right side may be clipped by the viewport intentionally; the SVG itself must never create document-level horizontal overflow. Keep every z-index explicit.\n\nD. TOKEN TABLE\n\nDefine these CSS custom properties on :root with these exact values:\n\n- --bg: #0B0B0F\n- --indigo: #5B2BD9\n- --pink: #FF6FB5\n- --blue: #3FBAFF\n- --blue-end: #536EFB\n- --muted: #8A8F9C\n- --white: #FFFFFF\n- --ink: #F4F3F7\n- --line: rgba(255,255,255,0.15)\n- --line-soft: rgba(255,255,255,0.09)\n- --ghost: rgba(255,255,255,0.09)\n- --copy-width: 55%\n- --header-height: 72px\n- --footer-height: 56px\n- --page-pad-x: 5vw\n- --page-pad-y: 28px\n- --hero-top: 116px\n- --chart-top: 38vh\n- --chart-height: 62vh\n- --h1-size: clamp(40px, 4.4vw, 76px)\n- --h1-line: 0.98\n- --h1-weight: 560\n- --body-size: 15px\n- --body-line: 1.55\n- --eyebrow-size: 11px\n- --eyebrow-line: 1.2\n- --meta-size: 12px\n- --meta-line: 1.35\n- --tick-size: 12px\n- --rail: 1px\n- --focus-ring: #3FBAFF\n- --duration-entrance: 1800ms\n- --duration-marker: 240ms\n- --duration-tick: 420ms\n- --ease-editorial: cubic-bezier(0.65, 0, 0.35, 1)\n- --ease-ui: cubic-bezier(0.22, 1, 0.36, 1)\n\nUse system fallback stack: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, “Segoe UI”, sans-serif. Load Google Fonts only if the implementation uses it; the page must remain correct if the request fails.\n\nE. THE COMPOSITION\n\nBuild the body as follows.\n\n1. Header. The header is fixed or absolutely positioned at inset:0 0 auto 0, height:72px, padding:0 5vw, display:flex, align-items:center, justify-content:space-between, border-bottom:1px solid var(--line), background:transparent. The left wordmark is an anchor or span with exact text “MORaine” in 13px, letter-spacing:0.18em, color:var(--ink), and text-transform:uppercase. The right header text is “RESEARCH / 03” in 11px, letter-spacing:0.16em, color:var(--muted). On desktop, both elements are 32px from the horizontal page padding.\n\n2. Main. Main is position:relative; min-height:100%; padding:116px 5vw 76px. The hero copy is width:55%; max-width:760px; position:relative; z-index:4. Place the eyebrow at 0px below the hero top. Its exact copy is “FIELD NOTE 03 / ARTIFICIAL INTELLIGENCE”. It is 11px, uppercase, letter-spacing:0.18em, color:var(--muted). Place the h1 at margin-top:22px, width:100%, font-size:var(--h1-size), line-height:var(--h1-line), letter-spacing:-0.055em, color:var(--ink), font-weight:var(--h1-weight), and max-width:8.6em. Its exact copy is “Markets move before consensus catches up.” Place the deck 28px below the h1, width:470px, max-width:100%, font-size:var(--body-size), line-height:var(--body-line), color:var(--muted). Its exact copy is “Moraine maps the quiet inflection points shaping the next decade of venture. This curve tracks the infrastructure layer beneath the AI economy.” Place a metadata row 26px below the deck. It is a flex row with gap:18px. The first item reads “UPDATED 18 JUN 2025”. The second reads “8 QUARTERS / 1 SIGNAL”. Both are 11px, uppercase, letter-spacing:0.12em, color:var(--muted).\n\n3. Chart stage. Place the chart stage at position:absolute; left:-5vw; top:38vh; width:110vw; height:62vh; min-height:380px; z-index:5; pointer-events:none. The SVG must fill this stage. The path begins at x=-70 and ends at x=1270 so the visible curve runs off both edges. The area fill is clipped by the SVG bounds and fades before the bottom of the chart. Preserve the x-axis labels at 12px with 90ms stagger.\n\n4. Footer. Position the footer at left:5vw, right:5vw, bottom:24px, height:32px, display:flex, align-items:center, justify-content:space-between, z-index:7. The left exact copy is “MORAINE / A VENTURE-RESEARCH HOUSE”. The right exact copy is “ONE CURVE AT A TIME”. Use 10px, letter-spacing:0.16em, color:var(--muted), uppercase. The footer may include one link after the right copy, but it is optional; if present, use an underlined link labelled “Request the full note” with a visible focus ring.\n\nAt desktop widths, the h1 must not overlap the marker or the brightest curve section. The 55% copy measure and 62% chart placement create a deliberate diagonal from upper-left text to upper-right curve endpoint. The chart remains the dominant object: the visible stroke and ghost stroke together must occupy more visual area than any text block.\n\nF. THE UI LANGUAGE\n\nThere are no prominent CTA buttons in the default composition. If adding the footer inquiry link, style it as a 11px uppercase text link in var(--ink), text-decoration:underline, text-underline-offset:4px, border-radius:3px, padding:4px 0. Its hover color is var(--pink), and its focus-visible outline is 2px solid var(--focus-ring) with offset:4px. Do not add a filled rectangle button.\n\nThe Moraine wordmark may be an anchor only if it links to the current page. Its hover state changes opacity from 1 to 0.72 over 160ms. It must retain a visible focus ring. Do not use a pill-shaped navigation bar. The sector marker is a data annotation, not a control, and must not receive pointer events.\n\nAll visible interactive controls use a minimum hit area of 36×36px where their visual content is smaller. Focus treatment is outline:2px solid var(--focus-ring); outline-offset:4px; border-radius:3px. Hover must not move layout; it may change color, opacity, or text-decoration only.\n\nG. MOTION\n\nCreate the following named keyframes in CSS:\n\n- draw-curve: from stroke-dashoffset:var(--path-length) to stroke-dashoffset:0.\n- reveal-area: from opacity:0 to opacity:1.\n- pop-marker: from opacity:0; transform:scale(0.7) to opacity:1; transform:scale(1).\n- grow-crosshair: from transform:scaleY(0) to transform:scaleY(1).\n- reveal-tick: from opacity:0; transform:translateY(5px) to opacity:1; transform:translateY(0).\n- settle-copy: from opacity:0; transform:translateY(12px) to opacity:1; transform:translateY(0).\n\nSet the resting state of all copy, chart, marker, and tick elements to opacity:1 and transform:none before animation classes are applied. The entrance sequence must be:\n\n- .backdrop-dust: no animation.\n- .eyebrow: settle-copy, duration:520ms, delay:80ms, easing var(--ease-ui).\n- h1: settle-copy, duration:760ms, delay:180ms, easing var(--ease-ui).\n- .hero-deck: settle-copy, duration:620ms, delay:300ms, easing var(--ease-ui).\n- .hero-meta: settle-copy, duration:520ms, delay:420ms, easing var(--ease-ui).\n- .curve-ghost: draw-curve, duration:1800ms, delay:0ms, easing var(--ease-editorial).\n- .curve-visible: draw-curve, duration:1800ms, delay:0ms, easing var(--ease-editorial).\n- .curve-area: reveal-area, duration:300ms, delay:1500ms, easing linear.\n- .sector-marker: pop-marker, duration:240ms, delay:1600ms, easing var(--ease-editorial).\n- .crosshair: grow-crosshair, duration:180ms, delay:1600ms, easing var(--ease-ui).\n- tick 1: reveal-tick, duration:360ms, delay:1200ms.\n- tick 2: reveal-tick, duration:360ms, delay:1290ms.\n- tick 3: reveal-tick, duration:360ms, delay:1380ms.\n- tick 4: reveal-tick, duration:360ms, delay:1470ms.\n- tick 5: reveal-tick, duration:360ms, delay:1560ms.\n- tick 6: reveal-tick, duration:360ms, delay:1650ms.\n- tick 7: reveal-tick, duration:360ms, delay:1740ms.\n- tick 8: reveal-tick, duration:360ms, delay:1830ms.\n\nThe marker’s transform origin is its center at 850px, 226px. The crosshair must grow downward from the marker center, not from the SVG top. Set the marker group pointer-events:none.\n\nThe chart has one hover behavior. Add a pointerenter listener to the chart stage. Ignore it when the reduced-motion media query matches. On the first valid pointerenter, change the path data to this alternate cubic path: M -70 492 C 78 480, 150 466, 235 420 S 352 392, 440 330 S 558 314, 650 270 S 766 236, 858 174 S 1000 118, 1270 42. Redraw both strokes over 900ms with the same draw-curve timing. Fade the area from opacity:0 to 1 during the first 300ms. The marker remains at cx=850, cy=174 and the label changes from “$18.4B” to “$21.9B”. Do not listen to pointerleave. The redraw occurs once per page lifetime.\n\nH. THE RESPONSIVE TABLE\n\nUse these exact overrides:\n\nAt width 901px through 1439px:\n- --page-pad-x:5vw\n- --hero-top:104px\n- --chart-top:38vh\n- --chart-height:62vh\n- --h1-size:clamp(40px,4.4vw,64px)\n- .hero-copy width:55%\n- .hero-deck width:430px\n- .slide-header height:72px\n- .slide-footer bottom:24px\n\nAt width 1440px through 1919px:\n- --page-pad-x:5vw\n- --hero-top:116px\n- --chart-top:38vh\n- --chart-height:62vh\n- --h1-size:76px\n- .hero-copy width:55%\n- .hero-deck width:470px\n- .slide-header height:72px\n- .slide-footer bottom:24px\n\nAt width 1920px and above:\n- --page-pad-x:96px\n- --hero-top:124px\n- --chart-top:38vh\n- --chart-height:62vh\n- --h1-size:76px\n- .hero-copy width:55%; max-width:820px\n- .hero-deck width:470px\n- .slide-header height:72px\n- .slide-footer left:96px; right:96px; bottom:28px\n\nAt width 768px through 900px:\n- html and body: height:auto; min-height:100dvh; overflow-y:auto; overflow-x:hidden\n- --page-pad-x:32px\n- --page-pad-y:24px\n- --header-height:64px\n- --hero-top:96px\n- --chart-top:470px\n- --chart-height:520px\n- --h1-size:56px\n- --h1-line:1.02\n- .hero-copy width:76%\n- .hero-deck width:510px\n- .slide-header padding-left:32px; padding-right:32px; height:64px\n- .slide-footer position:relative; left:auto; right:auto; bottom:auto; margin:32px; min-height:40px\n- .chart-stage position:relative; left:-24px; top:auto; width:calc(100% + 48px); height:520px; margin-top:0\n- main padding:96px 32px 24px\n- .slide-footer text remains visible below the chart\n\nAt width 390px through 767px:\n- html and body: height:auto; min-height:100dvh; overflow-y:auto; overflow-x:hidden\n- --page-pad-x:20px\n- --page-pad-y:20px\n- --header-height:58px\n- --hero-top:82px\n- --chart-top:420px\n- --chart-height:460px\n- --h1-size:42px\n- --h1-line:1.03\n- .hero-copy width:100%\n- .hero-deck width:100%; font-size:14px\n- .hero-meta display:grid; grid-template-columns:1fr 1fr; gap:8px 14px\n- .slide-header padding-left:20px; padding-right:20px; height:58px\n- .slide-header right text font-size:9px\n- .slide-footer left:20px; right:20px; bottom:18px; gap:12px; align-items:flex-start\n- .slide-footer left copy and right copy each width:48%; font-size:9px; line-height:1.45\n- .chart-stage position:relative; left:-12px; top:auto; width:calc(100% + 24px); height:460px\n- main padding:82px 20px 72px\n- .vertical-rail display:none\n- .slide-footer position:relative; margin-top:28px; min-height:44px\n\nAt height 700px or less and width 901px or more:\n- --header-height:60px\n- --hero-top:82px\n- --h1-size:48px\n- .hero-deck font-size:13px; max-width:420px\n- .slide-header height:60px\n- .slide-footer bottom:14px\n- .chart-stage top:36vh; height:64vh; min-height:340px\n\nAt height 600px or less and width 901px or more:\n- --header-height:54px\n- --hero-top:68px\n- --h1-size:40px\n- .hero-deck font-size:12px; line-height:1.4\n- .slide-header height:54px\n- .slide-footer bottom:10px\n- .chart-stage top:34vh; height:66vh; min-height:320px\n\nI. FAILURE GUARDS\n\nAll content must be visible with the page loaded even if JavaScript is delayed or unavailable. Set the base opacity of .eyebrow, h1, .hero-deck, .hero-meta, .curve-ghost, .curve-visible, .curve-area, .sector-marker, .crosshair, and every tick to 1. JavaScript may add the entrance classes, but it must first establish the final resting values, then animate from explicit initial values.\n\nUse a two-rAF fallback before starting the timeline. The first requestAnimationFrame ensures the browser has committed the initial styles. The second requestAnimationFrame starts the entrance sequence. If requestAnimationFrame is unavailable, show the resting state immediately. If a path length is unavailable, use a precomputed fallback of 1550 SVG user units and do not throw.\n\nWrap initialization in try/catch. On error, set the telemetry error string, reveal the resting state, and do not allow the exception to escape. Add a window error listener before initialization. Never use console.log, console.warn, or console.error. Never leave a stale rAF loop after completion.\n\nIn the prefers-reduced-motion: reduce media query, set all animation-duration to 1ms, animation-delay to 0ms, transition-duration to 1ms, and transform to none. Do not perform the hover redraw. Keep all content at opacity:1. The chart must remain readable, with the full visible stroke, area fill, marker, number, and eight ticks already present.\n\nJ. THE EXACT JS\n\nUse plain JavaScript at the end of body. The implementation must contain this exact telemetry initialization before any other runtime work:\n\n    window.__DEMO__ = { name: 'upslope', ready: false, frames: 0, effects: [], errors: [] };\n\nAdd this error listener immediately:\n\n    window.addEventListener('error', function (event) {\n      window.__DEMO__.errors.push(String(event.message || 'Unknown error'));\n    });\n\nCache all required elements once:\n\n    var root = document.documentElement;\n    var stage = document.querySelector('.chart-stage');\n    var ghost = document.querySelector('.curve-ghost');\n    var visible = document.querySelector('.curve-visible');\n    var area = document.querySelector('.curve-area');\n    var marker = document.querySelector('.sector-marker');\n    var crosshair = document.querySelector('.crosshair');\n    var ticks = Array.prototype.slice.call(document.querySelectorAll('.axis-tick'));\n    var number = document.querySelector('.sector-value');\n    var reduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n    var hasRedrawn = false;\n    var running = false;\n    var startTime = 0;\n    var mode = 'entrance';\n    var pathLength = 1550;\n    var duration = 1800;\n    var rafId = 0;\n\nUse this initial path string exactly:\n\n    var initialPath = 'M -70 492 C 82 475, 145 438, 230 448 S 350 350, 430 374 S 560 282, 650 305 S 760 202, 850 226 S 985 128, 1270 74';\n\nUse this alternate path string exactly:\n\n    var alternatePath = 'M -70 492 C 78 480, 150 466, 235 420 S 352 392, 440 330 S 558 314, 650 270 S 766 236, 858 174 S 1000 118, 1270 42';\n\nSet the SVG ghost and visible path stroke-dasharray and stroke-dashoffset to pathLength. If getTotalLength is available, call it once for each path before the loop and use the larger finite result; otherwise retain 1550. Do not call getTotalLength inside the frame callback.\n\nSet the marker transform-origin to 850px 226px with CSS. Set the crosshair transform-origin to 850px 226px with CSS. Set the area opacity to 0 only after the two-rAF fallback begins, and set the marker opacity to 0 and scale to 0.7 at the same time. Set all tick elements to opacity:0 and transform:translateY(5px) at the same time. Set copy elements to opacity:0 and transform:translateY(12px) at the same time.\n\nUse the following animation function. It must contain no layout reads and no DOM creation:\n\n    function animate(now) {\n      if (!startTime) startTime = now;\n      var elapsed = now - startTime;\n      var progress = Math.max(0, Math.min(1, elapsed / duration));\n      var eased = progress < 0.5\n        ? 4 * progress * progress * progress\n        : 1 - Math.pow(-2 * progress + 2, 3) / 2;\n      var offset = pathLength * (1 - eased);\n      ghost.style.strokeDashoffset = String(offset);\n      visible.style.strokeDashoffset = String(offset);\n      if (elapsed >= 240) {\n        area.style.opacity = String(Math.min(1, (elapsed - 240) / 300));\n      }\n      if (elapsed >= 1200) {\n        for (var i = 0; i < ticks.length; i += 1) {\n          var tickProgress = Math.max(0, Math.min(1, (elapsed - (1200 + i * 90)) / 360));\n          ticks[i].style.opacity = String(tickProgress);\n          ticks[i].style.transform = 'translateY(' + ((1 - tickProgress) * 5) + 'px)';\n        }\n      }\n      if (elapsed >= 1600) {\n        var markerProgress = Math.max(0, Math.min(1, (elapsed - 1600) / 240));\n        marker.style.opacity = String(markerProgress);\n        marker.style.transform = 'scale(' + (0.7 + markerProgress * 0.3) + ')';\n        crosshair.style.opacity = String(Math.min(1, markerProgress * 1.25));\n        crosshair.style.transform = 'scaleY(' + Math.min(1, markerProgress * 1.4) + ')';\n      }\n      window.__DEMO__.frames += 1;\n      if (progress < 1) {\n        rafId = window.requestAnimationFrame(animate);\n      } else {\n        running = false;\n        window.__DEMO__.effects.push('entrance-complete');\n      }\n    }\n\nFor the two-rAF fallback, call requestAnimationFrame twice before setting startTime and requesting the third frame. The first callback only records that the browser has committed styles. The second callback sets startTime=0, running=true, and requests animate. If reduced motion is true, skip the loop, set every animated element to its final value, push 'reduced-motion', and set ready=true after the first paint.\n\nFor hover redraw, use a pointerenter listener on stage. If reduced or hasRedrawn or running, return. Set hasRedrawn=true, mode='redraw', duration=900, startTime=0, pathLength to the cached alternate path length, set ghost.setAttribute('d', alternatePath), visible.setAttribute('d', alternatePath), set area opacity to 0, set marker transform-origin to 850px 174px, set marker transform to scale(0.7), set marker opacity to 0, set crosshair transform-origin to 850px 174px, set number.textContent='$21.9B', and start the same rAF loop. During redraw, use the same eased progress but update the area after 150ms, marker after 640ms, and crosshair after 640ms. Push 'hover-redraw' when complete. Do not restart the copy or tick animations.\n\nAfter the initial style setup and the first paint, set window.__DEMO__.ready = true. Use requestAnimationFrame once for this readiness assignment so it occurs after the first rendered frame.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["The page contains exactly one h1 with the text “Markets move before consensus catches up.” and includes semantic header, main, and footer elements.","At 1440×900, document.documentElement.scrollHeight is no greater than 900 and document.documentElement.scrollWidth equals document.documentElement.clientWidth; at 390×844, document.documentElement.scrollWidth also equals its clientWidth.","The chart is one SVG with viewBox=\"0 0 1200 620\", one 24px ghost stroke, one 4px gradient stroke, one area fill, one 100×100 marker halo, and eight x-axis tick labels.","window.__DEMO__ exists with name \"upslope\", ready becomes true after the first paint, frames increases during animation, effects contains \"entrance-complete\", and errors remains an array.","With prefers-reduced-motion: reduce, the h1, visible curve, area fill, marker, number, and all eight tick labels are visible at opacity 1, and no entrance or hover redraw animation is scheduled.","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"],"usage":{"build_tokens":58434,"build_prompt_tokens":8133,"build_completion_tokens":50301,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":693998,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":27586,"total_tokens":86020},"verification":{"passed":true,"passed_checks":18,"total_checks":18,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"34892 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"8 frames, effects: none declared"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"67 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Markets move before consensus catches up.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Moraine — Upslope"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=477 pageErrors=0"}]},"crosscheck":{"slug":"upslope","reviewer_model":"hy3","reviewer_platform":"workbuddy","verdict":"partial","confidence":"high","aesthetic_score":7,"requirements_met":["Document title is 'Moraine — Upslope' and lang='en' set","Exactly one h1 with exact required copy","header/main/footer landmarks present with correct contents","Three radial gradients and one linear wash on .slide-backdrop with exact rgba values","14 dust points at exact x/y coordinates with correct colors and 2x2px sizing","Vertical rail at 5vw with 20% white opacity from header to bottom","SVG viewBox 0 0 1200 620 with preserveAspectRatio none and overflow visible","Chart-stage positioned at left:-5vw, width:110vw, top:38vh, height:62vh","Initial cubic path matches the brief verbatim","Area fill gradient matches: #5B2BD9 0% 0.22 / #8D4DE4 48% 0.11 / #FF6FB5 100% 0","Visible path 4px stroke with horizontal indigo-to-pink gradient","Ghost path 24px stroke at rgba(255,255,255,0.09)","Marker halo 100x100, center dot 10x10 with blue gradient","8 x-axis ticks at x=0,150,300,450,600,750,900,1050 with correct Q1'24-Q4'25 labels","Crosshair 2px vertical 40px tick from marker center","Hero copy: eyebrow, h1, deck, meta with exact copy and spacing","Footer copy exact: 'MORAINE / A VENTURE-RESEARCH HOUSE' and 'ONE CURVE AT A TIME'","Inquiry link 'Request the full note' with underline and focus ring","CSS custom properties on :root match token table exactly","Two-rAF fallback before entrance timeline","Telemetry hook window.__DEMO__ with name 'upslope', frames, effects, errors","Error listener pushes to errors array","prefers-reduced-motion forces opacity:1, animation-duration:1ms","Responsive breakpoints at 390/768/901/1440/1920px and height overrides at 600/700px","Hover redraw handler changes path data, label text, marker/crosshair origins once"],"requirements_missed":["Y-axis title 'MARKET VALUE / USD' is positioned at SVG x=34 but the chart-stage is offset left:-5vw, mapping x=34 to approximately viewport x=-19px at 1024px width — the text is rendered fully off-screen on the left","Q1 '24 tick label at SVG x=0 with text-anchor:middle is positioned at viewport x=-5vw (approx -51px at 1024px width), so it is clipped off the left edge of the viewport — screenshot shows Q2 '24 as the leftmost visible label","Marker caption color is #A6AAB5 rather than the palette --muted #8A8F9C used elsewhere; minor inconsistency"],"issues":[{"issue":"Y-axis title rendered off-screen left due to chart-stage left:-5vw offset conflicting with x=34 position","severity":"medium","evidence":"<text x=\"34\" y=\"250\" transform=\"rotate(-90 34 250)\">MARKET VALUE / USD</text> inside SVG with .chart-stage { left: -5vw; width: 110vw } — visible in screenshot as absent"},{"issue":"Q1 '24 tick label clipped off left viewport edge; screenshot confirms Q2 '24 is leftmost","severity":"medium","evidence":"<text class=\"axis-label\" x=\"0\" y=\"548\" text-anchor=\"middle\">Q1 '24</text> — at chart-stage left:-5vw the center of this label maps to viewport x ≈ -51px on a 1024px desktop"},{"issue":"rAF loop performs far more than 2 style writes per frame (2 dashoffsets + area + 8 ticks×2 + marker×2 + crosshair×2 = ~20 writes once elapsed passes 1600ms), contradicting the 'no more than 2 style writes per frame' budget","severity":"low","evidence":"animate() body sets ghost/visible/area/ticks[0..7]/marker/crosshair in a single callback"},{"issue":"Marker caption uses custom fill #A6AAB5 instead of the design-system --muted #8A8F9C","severity":"low","evidence":"<text class=\"sector-caption\" x=\"874\" y=\"218\" fill=\"#A6AAB5\""}],"summary":"The page nails the cinematic single-slide composition, copy, tokens, animation choreography, telemetry, responsive tables, and reduced-motion fallback. The two real defects are visibility failures: the y-axis title and Q1 '24 label both sit inside the chart-stage's left bleed zone (left:-5vw) and are clipped off-screen on desktop, leaving the y-axis unlabelled and the x-axis starting at Q2 '24 in the rendered output.","usage":{"input":19730,"output":7856,"total":27586,"reasoning":6668,"cached":128},"latency_ms":62475}},{"slug":"vellum","title":"Vellum","category":"Landing Page","effect":"Huge serif type bleeds into view through slowly expanding radial ink masks on warm cotton paper","platform":"cmdcode","platform_label":"cmd-code","model":"xiaomi/mimo-v2.6-pro","model_label":"Xiaomi MiMo v2.6 Pro (xiaomi/mimo-v2.6-pro)","demo":"/demos/vellum/index.html","doc":"/prompts/vellum.md","prompt_text":"Build one complete, self-contained `index.html` for **Vellum** — a luxury letterpress and print studio landing page. Document title: `Vellum — Print That Outlives Us`. `<html lang=\"en\">`. ONE file only: plain HTML + CSS + JS in `<style>`/`<script>`. No frameworks, no bundlers, no external media (no images, no video, no audio, no base64 blobs, no CDN scripts, no fonts but Google Fonts). Every pixel comes from CSS gradients, inline SVG, text, or `<canvas>`. This is the library's light page: warm off-white cotton ground, near-black warm ink, one spot-colour oxide red as a second ink pass.\n\n**Viewport rule:** at `>= 901px` the page is one frame: `html, body { height:100%; overflow:hidden }` and `.page { height:100dvh; overflow:hidden }`. NO SCROLL on desktop. At `<= 900px` the page may scroll vertically. No horizontal overflow at any width — checked at 390x844, 768x1024, 1440x900, 1920x1080.\n\n**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 forms, no pricing tables, no testimonials, no carousel, no cookie banner, no scroll indicator, no desktop hamburger (menu only at `<=900px`), no sections beyond header / hero / footer, no footer links beyond the three specified, no dark mode toggle, no backdrop blur stronger than 14px.\n\n---\n\n## A. WHAT IS BEING BUILT\n\nA single-viewport landing page for a Lisbon letterpress studio called **Vellum**. Warm off-white cotton ground (`#f2ece2`), six soft ink blooms drifting beneath the surface on a low-resolution `<canvas>` upscaled and blurred by CSS, a hairline rule system (96px horizontal / 160px vertical grid at 4–5% ink, one major vertical rule, four corner registration marks), and a paper-grain overlay in multiply blend. The composition carries a mono eyebrow, a giant Fraunces serif H1 in two lines, a lead paragraph, two squared buttons, and a specimen panel with ink swatches. The signature effect is the **ink-bleed reveal**: each H1 glyph is an inline-block span whose radial-mask solid radius animates 0% → 124%, so every letter appears as a blot of ink spreading outward through paper fibre, feathered at the edge, staggered left-to-right.\n\n---\n\n## B. THE BACKDROP — paper, blooms, rules, grain\n\n**B1. `.paper`** — `position:fixed; inset:0; z-index:0; pointer-events:none`, `background-color: #f2ece2` plus four stacked gradients (first listed = frontmost):\n1. `radial-gradient(ellipse 70% 52% at 18% 8%, rgba(255,252,246,0.92) 0%, rgba(255,252,246,0.34) 38%, transparent 68%)` — light source, top left.\n2. `radial-gradient(ellipse 90% 70% at 82% 96%, rgba(120,100,74,0.16) 0%, rgba(120,100,74,0.06) 42%, transparent 74%)` — warm shade, bottom right.\n3. `linear-gradient(168deg, #f6f1e8 0%, #f2ece2 46%, #ece4d7 100%)` — stock tone.\n4. `linear-gradient(180deg, rgba(23,19,15,0.05) 0%, transparent 14%, transparent 86%, rgba(23,19,15,0.07) 100%)` — edge burn.\n\n**B2. `canvas#bloom`** — `position:fixed; inset:0; width:100%; height:100%; z-index:1; display:block; pointer-events:none; filter: blur(30px); opacity:0.86; mix-blend-mode: multiply`.\n\n**Resolution trick (critical):** the canvas never renders at device resolution. Backing store `iw = Math.max(160, Math.round(innerWidth * 0.28))`, `ih = Math.max(120, Math.round(innerHeight * 0.28))`; `canvas.width = iw; canvas.height = ih`. Dpr is deliberately NOT applied — the CSS stretch plus the single static `filter: blur(30px)` does all smoothing. At 1440x900 the store is 403x252.\n\n**Offscreen blobs (drawn once at init, never per frame):** two 96x96 canvases, each a single radial gradient from centre (48,48) radius 48:\n```\nink   : rgba(23,19,15,1) 0%, rgba(23,19,15,0.62) 34%, rgba(23,19,15,0.24) 62%, rgba(23,19,15,0) 100%\noxide : rgba(176,74,42,1) 0%, rgba(176,74,42,0.58) 34%, rgba(176,74,42,0.22) 62%, rgba(176,74,42,0) 100%\n```\n\n**The six blooms** — `bx, by` normalised, `r` a fraction of `min(iw,ih)`, `a` the globalAlpha, `P` the drift period in seconds:\n\n```\n#  blob   bx    by    r     a     P     phase   ax    ay\n1  ink    0.18  0.26  0.52  0.16  23s   0.0     0.06  0.05\n2  ink    0.74  0.18  0.46  0.13  31s   1.7     0.05  0.06\n3  oxide  0.46  0.72  0.58  0.20  27s   3.1     0.07  0.04\n4  ink    0.88  0.62  0.40  0.11  37s   4.4     0.04  0.05\n5  oxide  0.12  0.78  0.44  0.15  41s   2.2     0.06  0.05\n6  ink    0.60  0.40  0.62  0.09  47s   5.6     0.05  0.04\n```\n\nPer frame, `t` in seconds, with `w1 = 6.2832 / P`, `w2 = w1 / 1.37`, `w3 = w1 / 0.61` baked per bloom at init: `x = (bx + ax * Math.sin(t * w1 + phase)) * iw`, `y = (by + ay * Math.sin(t * w2 + phase * 0.6)) * ih`, `s = r * Math.min(iw, ih) * (1 + 0.12 * Math.sin(t * w3))`, then `ctx.globalAlpha = a; ctx.drawImage(blob, x - s/2, y - s/2, s, s)`. No division in the loop.\n\n**Per-frame cost budget:** 1 `clearRect` + 6 `drawImage`. No `createRadialGradient`, no `ctx.filter`, no `ctx.shadowBlur`, no `getBoundingClientRect`, no path building inside the loop. Six blits of ~210x210 onto a 403x252 target, under 1.4 ms, never more than 8 canvas draw calls per frame.\n\n**B3. `.rules`** — `position:fixed; inset:0; z-index:2; pointer-events:none`, background `repeating-linear-gradient(180deg, rgba(23,19,15,0.055) 0 1px, transparent 1px 96px), repeating-linear-gradient(90deg, rgba(23,19,15,0.040) 0 1px, transparent 1px 160px)`, masked so the grid dies at the frame edge with `radial-gradient(ellipse 78% 66% at 50% 44%, #000 0%, #000 40%, rgba(0,0,0,0.30) 72%, transparent 100%)` — set both `-webkit-mask-image` and `mask-image`.\n\n**B4. `.rule-v`** — the one major rule: `position:fixed; left:8.6vw; top:0; bottom:0; width:1px; z-index:2; background: linear-gradient(180deg, transparent 0%, rgba(23,19,15,0.22) 11%, rgba(23,19,15,0.22) 89%, transparent 100%)`.\n\n**B5. `.marks`** — four 18x18 registration crosshairs at `inset: 26px` (all corners), `z-index:2`, `opacity:0.5`, background an inline SVG data URI: a 6.5r circle plus a full cross, `stroke='%2317130F' stroke-opacity='.38' stroke-width='.8'`. Decorative: `aria-hidden=\"true\"`.\n\n**B6. `.sheen`** — `position:fixed; inset:-20% -60%; z-index:3; pointer-events:none; background: linear-gradient(104deg, transparent 0%, rgba(255,255,255,0.40) 46%, transparent 60%, transparent 100%); opacity:0.28; animation: sheen 34s cubic-bezier(0.45,0,0.55,1) infinite`; keyframes translate `-28% → 28%` on X only.\n\n**B7. `.vign`** — `position:fixed; inset:0; z-index:4; pointer-events:none; background: radial-gradient(ellipse 92% 84% at 50% 46%, transparent 0%, transparent 52%, rgba(60,48,34,0.07) 78%, rgba(60,48,34,0.14) 100%)`.\n\n**B8. `.grain`** — `position:fixed; inset:-50%; width:200%; height:200%; z-index:40; pointer-events:none; opacity:0.11; mix-blend-mode: multiply`, background an inline SVG `feTurbulence` data URI (`type=\"fractalNoise\" baseFrequency=\"0.82\" numOctaves=\"3\" stitchTiles=\"stitch\"` + `feColorMatrix type=\"saturate\" values=\"0\"`), `background-size: 180px`, animated `grain 1.05s steps(4) infinite` jittering `transform: translate` through `(0,0) (-1.5%,1%) (1%,-1.5%) (-1%,1.5%)`.\n\n---\n\n## C. LAYER STACK (back → front)\n\n1. `html, body` — `#f2ece2`, forced twice (see Failure Guards).\n2. `.paper` — z-index 0, the four gradients.\n3. `canvas#bloom` — z-index 1, blurred ink blooms, multiply.\n4. `.rules` + `.rule-v` + `.marks` — z-index 2, hairline system, all `pointer-events:none`.\n5. `.sheen` — z-index 3.\n6. `.vign` — z-index 4.\n7. `.page` — z-index 10, `position:relative; display:grid; grid-template-rows: auto minmax(0,1fr) auto; min-height:100dvh` (height:100dvh inside the desktop lock).\n8. `header` — z-index 30; `main` — z-index 20; `footer` — z-index 30.\n9. `.grain` — z-index 40, above everything, `pointer-events:none`.\n10. `.menu` (mobile overlay, only `<=900px`) — z-index 80 when open.\n\n---\n\n## D. TOKEN TABLE (defaults at 1440x900)\n\n```\n--paper:        #f2ece2\n--paper-2:      #e9e1d5\n--paper-hi:     #fbf7f0\n--ink:          #17130f\n--ink-soft:     #3a322b\n--muted:        #6e6357\n--muted-2:      #9a8e80\n--accent:       #b04a2a\n--accent-2:     #c08a2e\n--accent-wash:  rgba(176,74,42,0.10)\n--line:         rgba(23,19,15,0.20)\n--line-soft:    rgba(23,19,15,0.10)\n--line-hair:    rgba(23,19,15,0.055)\n--focus:        #b04a2a\n--serif:        \"Fraunces\", \"Times New Roman\", Georgia, serif\n--mono:         \"IBM Plex Mono\", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace\n--logo:         20px\n--logo-sub:     9.5px\n--nav:          13px\n--eyebrow:      11.5px\n--h1:           104px\n--h1-lh:        0.94\n--lead:         18px\n--btn:          12.5px\n--btn-h:        44px\n--spec-lg:      34px\n--spec-md:      18px\n--spec-sm:      11.5px\n--foot:         11.5px\n--track-caps:   0.18em\n--radius:       2px\n--header-x:     48px\n--header-y:     26px\n--header-h:     46px\n--page-x:       48px\n--hero-y:       36px\n--hero-gap:     22px\n--hero-max:     960px\n--aside-w:      300px\n--gap-col:      56px\n--footer-x:     48px\n--footer-y:     24px\n--grain-op:     0.11\n--ease-out:     cubic-bezier(0.16,1,0.3,1)\n--ease-ink:     cubic-bezier(0.33,0.02,0.28,1)\n--enter-dur:    0.9s\n--bleed-dur:    0.72s\n```\n\nFonts (Google Fonts ONLY, with the fallback stacks above): one `<link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>` and one stylesheet `https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,300..700&family=IBM+Plex+Mono:wght@400;500&display=swap`. This is the document's only network request.\n\n---\n\n## E. THE COMPOSITION — exact build order\n\n`.page` is the grid container with three rows: `<header>`, `<main>`, `<footer>`.\n\n**HEADER** — `height: var(--header-h); padding: var(--header-y) var(--header-x) 0; display:flex; align-items:center; justify-content:space-between; gap:24px`.\n- Left: `a.brand` (href `#`, accessible name \"Vellum home\"): `vellum` in `var(--serif)`, `font-size: var(--logo); font-weight:500; letter-spacing:-0.015em; color: var(--ink)`, then `<span class=\"brand-sub\">EST. 1974</span>` in `var(--mono)`, `font-size: var(--logo-sub); letter-spacing: var(--track-caps); color: var(--muted-2); vertical-align: super; margin-left:8px`.\n- Centre: `nav` with exactly 3 links — `Works`, `Process`, `Stock` — each `href=\"#\"`, `position:relative`, `var(--serif)`, `font-size: var(--nav); letter-spacing:0.01em; color: var(--ink-soft); text-decoration:none`, `display:flex; gap:34px; align-items:center`. Hairline underline `::after { content:\"\"; position:absolute; left:0; right:0; bottom:-6px; height:1px; background: var(--ink); transform: scaleX(0); transform-origin: 0 50%; transition: transform 0.28s var(--ease-out) }`; `:hover::after, :focus-visible::after { transform: scaleX(1) }`.\n- Right: `a.cta` — see section F; text `Request a proof`.\n\n**MAIN (hero)** — `display:grid; grid-template-columns: minmax(0,1fr) var(--aside-w); gap: var(--gap-col); align-items:center; padding: var(--hero-y) var(--page-x); max-width: var(--hero-max); margin: 0 auto; width:100%`. Left column `.hero-copy { display:flex; flex-direction:column; gap: var(--hero-gap); min-width:0 }`:\n\n1. `.eyebrow` — `var(--mono)`, `font-size: var(--eyebrow); letter-spacing: var(--track-caps); text-transform: uppercase; color: var(--muted)`. Copy: `Letterpress · Hot foil · Hand-set type`, preceded by a 34px `<span class=\"tick\">` hairline (1px tall, `background: var(--line)`, `display:inline-block; vertical-align:middle; margin-right:12px`).\n2. `h1` — EXACTLY ONE `<h1>`, `var(--serif)`, `font-weight:400; font-size: var(--h1); line-height: var(--h1-lh); letter-spacing:-0.035em; color: var(--ink); margin:0`, `aria-label=\"Print that outlives us.\"`. Two `.line` blocks (`display:block`) separated by one whitespace text node, so `textContent` reads `Print that outlives us.` while each line renders on its own row: Line 1 `Print that`, Line 2 `outlives us.`. Inside each line every glyph is `<span class=\"ltr\">X</span>`; words are wrapped in `<span class=\"word\">` (`display:inline-block; white-space:nowrap`) with a space text node between them. Letters of `outlives` take `ltr--spot` (`color: var(--accent)`) — the second ink pass. Spaces are NOT spans. See section G for the bleed.\n3. `.lead` — `var(--serif)`, `font-size: var(--lead); line-height:1.58; letter-spacing:-0.012em; color: var(--ink-soft); max-width: 52ch; margin:0`. Copy: `Vellum sets, presses and binds limited editions by hand — one sheet at a time, on presses that have not stopped running since 1974.`\n4. `.actions { display:flex; gap:12px; flex-wrap:wrap }` — `a.btn.btn--ink` text `Request a proof`, then `a.btn.btn--ghost` text `See the stock`.\n\nRight column `aside.specimen` — `border:1px solid var(--line-soft); background: linear-gradient(180deg, rgba(255,255,255,0.52) 0%, rgba(233,225,213,0.30) 100%); border-radius: var(--radius); padding: 22px 22px 18px; display:flex; flex-direction:column; gap:14px`, labelled by `<h2 class=\"spec-title\">Specimen 04</h2>` (`var(--mono); font-size:11.5px; letter-spacing:var(--track-caps); text-transform:uppercase; color:var(--muted); margin:0; font-weight:400`) — an `h2`, never an `h1`, the only other heading. Each `.spec-row` is `display:flex; align-items:baseline; justify-content:space-between; gap:12px`; each `.rule` is `height:1px; background: var(--line-soft); transform-origin:0 50%`:\n- Row 1: `<span class=\"spec-lg\">Aa</span>` (`var(--serif); font-size: var(--spec-lg); font-weight:300; letter-spacing:-0.02em; color:var(--ink)`) + right-aligned `<span class=\"spec-tag\">96 pt</span>` (`var(--mono); font-size: var(--spec-sm); color: var(--muted-2); letter-spacing:0.06em`).\n- `.rule`.\n- Row 2: `<span class=\"spec-md\">Hand-set</span>` at `var(--spec-md)` + tag `24 pt`.\n- `.rule`.\n- Row 3: `<span class=\"spec-sm\">Cotton · 600 gsm</span>` (`var(--mono); font-size: var(--spec-sm); letter-spacing:0.08em; color: var(--ink-soft)`) + tag `Ed. 90`.\n- `.swatches { display:flex; gap:8px }` — 4 chips, each `width:26px; height:26px; border:1px solid rgba(23,19,15,0.18)`, backgrounds `#17130f`, `#b04a2a`, `#c08a2e`, `#e9e1d5`; the row is `aria-hidden=\"true\"` (decorative, not a control).\n\n**FOOTER** — `padding: var(--footer-y) var(--footer-x); display:flex; align-items:center; justify-content:space-between; gap:20px; border-top:1px solid var(--line-soft)`. Left: `var(--mono)`, `font-size: var(--foot); color: var(--muted)`: `© 2026 Vellum Press · Lisbon`.\n- Centre: `ul.meta` with exactly 3 `li` — `600 gsm cotton`, `Editions of 90`, `Foil &amp; letterpress` — `display:flex; gap:0; list-style:none; margin:0; padding:0`; each `li` is `var(--mono); font-size: var(--foot); color: var(--muted); padding: 0 14px`, `border-left:1px solid var(--line-soft)` on all but the first and `border-right` on all but the last.\n- Right: exactly 3 links — `instagram`, `contact`, `studio` — `href=\"#\"`, `var(--mono); font-size: var(--foot); color: var(--ink-soft); text-decoration:none`, `display:flex; gap:22px`, hover `color: var(--accent)`. Each link's text is its accessible name.\n\n---\n\n## F. THE UI LANGUAGE — squared, letterpress, hairline\n\nTwo button treatments, both `border-radius: var(--radius)` (2px — print is square), `height: var(--btn-h); padding: 0 20px; display:inline-flex; align-items:center; justify-content:center; font-family: var(--mono); font-size: var(--btn); letter-spacing:0.06em; text-transform: uppercase; text-decoration:none`, and `transition: background 0.22s var(--ease-out), color 0.22s var(--ease-out), border-color 0.22s var(--ease-out), transform 0.22s var(--ease-out)`.\n\n- `.btn--ink`: `color: var(--paper-hi); border:1px solid var(--ink); background: linear-gradient(180deg, #241d17 0%, #17130f 58%, #0e0b09 100%); box-shadow: 0 14px 34px rgba(23,19,15,0.24), inset 0 1px 0 rgba(255,255,255,0.10)`. Hover `background: linear-gradient(180deg, #302720 0%, #1e1813 58%, #12100d 100%); transform: translateY(-1px)`. Active `transform: translateY(0)`.\n- `.btn--ghost`: `color: var(--ink); border:1px solid var(--line); background: linear-gradient(180deg, rgba(255,255,255,0.46) 0%, rgba(233,225,213,0.16) 100%)`. Hover `border-color: var(--ink); background: linear-gradient(180deg, rgba(255,255,255,0.70) 0%, rgba(233,225,213,0.30) 100%); transform: translateY(-1px)`.\n- `.cta` in the header: `height:34px; padding:0 14px; font-size:11.5px`, otherwise `.btn--ghost` but `color: var(--accent); border-color: rgba(176,74,42,0.42); background: var(--accent-wash)`; hover `border-color: var(--accent); background: rgba(176,74,42,0.16)`.\n\nEvery `a` and `button`: `:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; border-radius: var(--radius) }`. No default blue ring; no `text-decoration` but the nav hover hairline. `::selection { background: rgba(176,74,42,0.22); color: var(--ink) }`.\n\n---\n\n## G. MOTION — entrance stagger + named keyframes\n\nResting state first: `.appear { opacity: 1 }`. Entrance: `animation-duration: var(--enter-dur); animation-fill-mode: both; animation-timing-function: var(--ease-out); animation-delay: var(--d, 0s)`. Each animated element carries `.appear`, a modifier class, and an inline `--d`.\n\nThe ink-bleed animates a registered custom property driving a radial mask:\n```\n@property --bleed { syntax: \"<percentage>\"; inherits: false; initial-value: 124%; }\n.ltr { --bleed: 124%; --ox: 50%; --oy: 58%; display:inline-block;\n  -webkit-mask-image: radial-gradient(circle at var(--ox) var(--oy), #000 0%, #000 var(--bleed),\n    rgba(0,0,0,0.62) calc(var(--bleed) * 1.14), rgba(0,0,0,0.20) calc(var(--bleed) * 1.30), transparent calc(var(--bleed) * 1.46));\n  mask-image: identical to -webkit-mask-image }\n@keyframes in-bleed { from { --bleed: 0%; opacity: 0.55 } to { --bleed: 124%; opacity: 1 } }\n```\n`.ltr` also sets `--ox`/`--oy` per glyph from this six-origin cycle, in document order, restarting each line: `(34%,62%) (58%,44%) (46%,70%) (66%,52%) (38%,50%) (54%,66%)`. `.ltr--spot` letters add `+0.22s` to their delay — the oxide pass lands after the black pass.\n\nStagger table (element → modifier → `--d`):\n```\nbrand                appear--fade   0.06s\nnav Works            appear--fade   0.12s\nnav Process          appear--fade   0.18s\nnav Stock            appear--fade   0.24s\nheader cta           appear--rise   0.30s\neyebrow              appear--fade   0.34s\nh1 line 1 letters    appear--bleed  0.46s + i*0.034s  (i = 0..8 — 9 glyphs)\nh1 line 2 letters    appear--bleed  0.62s + i*0.030s  (i = 0..10 — 11 glyphs; +0.22s if .ltr--spot)\nlead                 appear--soft   1.18s\nbtn--ink             appear--rise   1.30s\nbtn--ghost           appear--rise   1.38s\nspecimen panel       appear--soft   1.02s\nspec row 1/2/3       appear--soft   1.10s / 1.18s / 1.26s\nfooter left          appear--fade   1.46s\nfooter centre        appear--fade   1.52s\nfooter right         appear--fade   1.58s\n```\n`.appear--bleed` uses `animation-duration: var(--bleed-dur); animation-timing-function: var(--ease-ink)`.\n\nNamed keyframes (all end at opacity 1 / identity transform):\n```\nin-fade   from { opacity: 0 }                                  to { opacity: 1 }\nin-soft   from { opacity: 0; transform: translateY(12px) }      to { opacity: 1; transform: none }\nin-rise   from { opacity: 0; transform: translateY(9px) scale(0.985) } to { opacity: 1; transform: none }\nin-bleed  from { --bleed: 0%; opacity: 0.55 }                   to { --bleed: 124%; opacity: 1 }\nin-rule   from { transform: scaleX(0) }                         to { transform: scaleX(1) }\nsheen     0% { transform: translate3d(-28%,0,0) } 50% { transform: translate3d(28%,0,0) } 100% { transform: translate3d(-28%,0,0) }\ngrain     0% { transform: translate(0,0) } 25% { transform: translate(-1.5%,1%) } 50% { transform: translate(1%,-1.5%) } 75% { transform: translate(-1%,1.5%) } 100% { transform: translate(0,0) }\n```\nThe `.tick` hairline and every specimen `.rule` use `in-rule` (`transform-origin: 0 50%`), delays matching their parent row. Canvas blooms, `sheen` and `grain` run continuously, independent of the entrance stagger.\n\n---\n\n## H. RESPONSIVE TABLE — exact token overrides\n\n```\n>= 1600px:\n  --h1: 120px  --logo: 21px  --nav: 13.5px  --lead: 19px  --btn: 13px  --btn-h: 46px\n  --header-x: 56px  --page-x: 56px  --footer-x: 56px  --hero-max: 1060px\n  --aside-w: 320px  --gap-col: 64px  --spec-lg: 38px\n\n>= 1920px:\n  --h1: 136px  --logo: 22px  --nav: 14px  --lead: 20px  --btn-h: 48px\n  --header-x: 72px  --page-x: 72px  --footer-x: 72px  --hero-max: 1180px\n  --aside-w: 340px  --gap-col: 76px  --spec-lg: 42px  --header-y: 30px\n\n1280px – 1599px:\n  --h1: 94px  --header-x: 44px  --page-x: 44px  --footer-x: 44px\n  --hero-max: 900px  --aside-w: 280px  --gap-col: 48px\n\n901px – 1279px:\n  --h1: 72px  --lead: 16.5px  --header-x: 32px  --page-x: 32px  --footer-x: 32px\n  --header-y: 22px  --hero-max: 760px  --aside-w: 244px  --gap-col: 36px  --spec-lg: 28px\n\n>= 901px AND max-height 850px:\n  --h1: 74px  --hero-y: 24px  --header-y: 18px  --hero-gap: 16px\n  --lead: 16px  --spec-lg: 28px  --footer-y: 18px\n\n>= 901px AND max-height 720px:\n  --h1: 58px  --hero-y: 14px  --header-y: 12px  --hero-gap: 12px\n  --lead: 14.5px  --eyebrow: 11px  --btn-h: 38px  --spec-lg: 24px  --spec-md: 16px\n  --footer-y: 12px  --aside-w: 224px\n\n>= 901px (desktop lock, all heights):\n  html, body { height:100%; overflow:hidden }   .page { height:100dvh; overflow:hidden }\n\n<= 900px:\n  no vh lock; page may scroll; nav becomes a full-viewport overlay menu:\n  - header keeps brand + burger: <button class=\"burger\" aria-label=\"Menu\" aria-expanded=\"false\">\n    with three 18x1.5px bars in var(--ink), gap 5px\n  - .menu { position:fixed; inset:0; z-index:80; background: rgba(242,236,226,0.94);\n    backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px); display:flex;\n    flex-direction:column; align-items:flex-start; justify-content:center;\n    padding: 0 32px; gap: 26px; transform: translateY(-100%);\n    transition: transform 0.45s var(--ease-out) }   .menu.open { transform: none }\n  - menu holds the 3 nav links stacked at 22px serif plus the header cta; Escape,\n    link click, and resize to >900px all close it\n  - main becomes grid-template-columns: minmax(0,1fr); the specimen aside drops below the\n    copy with margin-top: 28px; .rule-v is display:none\n  --h1: 46px  --header-x: 24px  --page-x: 24px  --footer-x: 24px  --hero-max: 100%\n  .grain opacity stays --grain-op: 0.11\n\n<= 560px:\n  --h1: 34px  --lead: 15px  --header-x: 20px  --page-x: 20px  --footer-x: 20px\n  --btn-h: 42px  --spec-lg: 26px  --hero-gap: 18px\n  footer becomes flex-direction:column; align-items:flex-start; gap:10px\n  .meta li:first-child { padding-left: 0 }; .meta wraps with row-gap 6px\n  header cta is display:none — it lives inside the menu on mobile\n  .swatches chips shrink to 22x22; --grain-op: 0.09; .marks is hidden\n```\n\nCanvas constants are responsive too: the 0.28 scale stays; at `<=900px` bloom `r` multiplies by 1.15 (fewer pixels, so blooms must read larger) and `a` by 0.85. The lead's `max-width` is `100%` below 901px. Each block above is a width `@media`; the max-height blocks add `and (min-width: 901px)`, desktop only.\n\n---\n\n## I. FAILURE GUARDS — the page is never blank, never broken\n\n1. **Paper-first:** the FIRST CSS rule is `html, body { background:#f2ece2 !important; color:#17130f }`; the body tag carries `style=\"background:#f2ece2;color:#17130f\"`; a later rule restates `html, body { background: var(--paper) }`.\n2. **Resting opacity is 1:** every `.appear` element's un-animated computed style is its final state (opacity 1, no transform). `.ltr` spans default to `--bleed: 124%` — a fully open mask — so a failed animation leaves legible type; if `@property` is unsupported, `mask-image` falls back to `none` and the letters show anyway. `fill-mode: both` only holds the FROM state during the delay.\n3. **Two-rAF entrance fallback:** after two nested `requestAnimationFrame` ticks, for each `.appear`, if `el.getAnimations` is missing or reports no animation with `playState` `\"running\"` or `\"finished\"`, add `.is-in` to every `.appear`. `.is-in { animation:none !important; opacity:1 !important; transform:none !important; -webkit-mask-image:none !important; mask-image:none !important }`.\n4. **Canvas fallback:** if `canvas.getContext('2d')` returns null, add `.no-canvas` to `<html>`; CSS hides `canvas#bloom` and paints static blooms on `.paper::after`: `radial-gradient(circle at 18% 26%, rgba(23,19,15,0.07), transparent 34%), radial-gradient(circle at 46% 72%, rgba(176,74,42,0.09), transparent 36%), radial-gradient(circle at 74% 18%, rgba(23,19,15,0.05), transparent 30%)`. The page reads correctly regardless.\n5. **Reduced motion:** inside `@media (prefers-reduced-motion: reduce)`: `*, *::before, *::after { animation:none !important; transition:none !important; scroll-behavior:auto !important }`; force `.appear, .ltr, .tick, .rule` to `opacity:1 !important; transform:none !important; -webkit-mask-image:none !important; mask-image:none !important`; freeze `.sheen` at `opacity:0.14` and stop the grain animation; the loop counts frames but repaints nothing.\n6. **Contrast:** body copy `#3a322b` on `#f2ece2` (`> 9:1`), muted mono `#6e6357` (`> 5:1`), never lower. No text opacity below 0.86.\n7. **No console output** in the happy path: no `console.*`, no thrown exceptions; every canvas and media-query call guarded.\n\n---\n\n## J. THE EXACT JS (one IIFE at the end of body, nothing else)\n\n1. `window.__DEMO__ = { name: 'vellum', ready: false, frames: 0, effects: [], errors: [] };` then `window.addEventListener('error', e => window.__DEMO__.errors.push(String((e && e.message) || e)))`. Push effect names once, in this order: `'ink-bleed-mask'`, `'ink-bloom-canvas'`, `'hairline-rules'`, `'paper-grain'`.\n2. **Cache at startup:** `document.documentElement`, `canvas#bloom`, its 2d context, `.burger`, `.menu`, all `.menu a`, `matchMedia('(prefers-reduced-motion: reduce)')`, `t0 = performance.now()`. No `getBoundingClientRect` anywhere in the file; all sizing reads `window.innerWidth` / `window.innerHeight`.\n3. **Blob builder:** `makeBlob(r,g,b)` builds a 96x96 canvas with one `createRadialGradient(48,48,0,48,48,48)` (stops `0 → rgba(r,g,b,1)`, `0.34 → 0.62 alpha`, `0.62 → 0.24 alpha`, `1 → 0 alpha`), fills a 96x96 rect, returns it. Called exactly twice (ink `23,19,15`; oxide `176,74,42`), never in the loop.\n4. **resize():** `iw = Math.max(160, Math.round(innerWidth * 0.28))`, `ih = Math.max(120, Math.round(innerHeight * 0.28))`, `canvas.width = iw; canvas.height = ih`. Scheduled only by a boolean `resizeQueued` consumed by one rAF; never a timer, never twice per frame.\n5. **draw(t):** `ctx.clearRect(0,0,iw,ih)`; for each of the 6 blooms compute `x, y, s` from the section B table with `Math.sin`, set `ctx.globalAlpha = a`, `ctx.drawImage(blob, x - s/2, y - s/2, s, s)`; reset `globalAlpha = 1`. No gradients, filters, shadows, paths.\n6. **Loop:** `function step(now){ window.__DEMO__.frames += 1; var paint = !document.hidden && (!reduced || !painted); if (paint) { draw(reduced ? 6.0 : (now - t0) / 1000); painted = true; window.__DEMO__.ready = true; } requestAnimationFrame(step) }`, kicked off once. `ready` becomes true after the first paint; `frames` increments once per frame; a hidden document counts the tick but skips painting.\n7. **Entrance fallback:** two nested `requestAnimationFrame` callbacks, then per `.appear` run the `getAnimations` check from guard 3 and add `.is-in` to all if any check fails.\n8. **Menu (mobile only):** burger click toggles `.menu.open` and `aria-expanded`; Escape closes; any menu link click closes; the `resize` handler closes the menu above 900px and sets `resizeQueued`.\n9. **Reduced motion:** if the media query matches, `reduced = true`, so `draw(6.0)` runs once, `ready` becomes true, and no further pixels change.\n\nNo other JS. No libraries, no timers, no `console` calls, no `fetch`. Final file must be under 90 KB.\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["exactly one <h1> whose normalized textContent is 'Print that outlives us.', split into 2 .line elements containing 20 .ltr glyph spans in total","canvas#bloom paints non-blank content and keeps repainting (pixel hash changes across frames) at 1440x900, and window.__DEMO__.frames increments once per rAF tick","window.__DEMO__ is initialized with name 'vellum', ready becomes true after the first paint, effects contains 'ink-bleed-mask', 'ink-bloom-canvas', 'hairline-rules' and 'paper-grain', and errors stays empty","at 1440px and 1920px html/body and .page are locked to a single 100dvh frame with overflow hidden; at 390px and 768px there is no horizontal overflow","semantic header/nav/main/footer exist with exactly 3 nav links and 3 footer links all carrying accessible names, no network requests occur except Google Fonts, and under prefers-reduced-motion every .ltr glyph is fully visible with no running animations","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"],"usage":{"build_tokens":25761,"build_prompt_tokens":39,"build_completion_tokens":25722,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":2,"build_latency_ms":219828,"wasted_tokens":42919,"repair_tokens":0,"crosscheck_tokens":29750,"total_tokens":55511},"verification":{"passed":false,"passed_checks":19,"total_checks":20,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"31295 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"7 frames, effects: ink-bleed-mask, ink-bloom-canvas, hairline-rules, paper-grain"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":false,"detail":"10 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas paints non-blank content","pass":true,"detail":"mean 23.04, stdev 37.33, coverage 0.3997"},{"name":"canvas spans the viewport width","pass":true,"detail":"1440px of 1440px"},{"name":"canvas keeps repainting (content changes)","pass":true,"detail":"hash 2445259559 -> 1041342436"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Print that\noutlives us.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Vellum — Print That Outlives Us"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"desktop is one viewport (no scroll)","pass":true,"detail":"doc 900 vs viewport 900"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=443 pageErrors=0"}]},"crosscheck":{"slug":"vellum","reviewer_model":"hy3","reviewer_platform":"workbuddy","verdict":"confirmed","confidence":"high","aesthetic_score":9,"requirements_met":["Exactly one h1 with aria-label='Print that outlives us.' and two .line blocks","Glyphs are inline-block .ltr spans; outlives uses ltr--spot (oxide red)","Ink-bleed reveal via registered --bleed custom property driving radial mask-image","Per-glyph --ox/--oy cycling (six-origin) resetting per line","Stagger delays for line 1 (0.46 + i*0.034) and line 2 (0.62 + i*0.030 + 0.22 if spot) implemented exactly","Canvas bloom rendered at downsampled store (innerWidth*0.28 / innerHeight*0.28), no dpr, single 30px blur","Six blooms (4 ink, 2 oxide) with correct bx/by/r/a/P/phase/ax/ay parameters, single clearRect + 6 drawImage","Reduced r * min(iw,ih) size and pulsing s = s0 * (1 + 0.12*sin(t*w3))","Hairline grid via repeating-linear-gradients with radial mask fade","Four registration marks at inset:26px in each corner via 4 background-image SVGs","Layer stack matches: paper z0, canvas z1, rules z2, sheen z3, vign z4, page z10, grain z40","All token values, responsive breakpoint tables (>=1920, >=1600, 1280-1599, 901-1279, max-height 850/720, <=900, <=560)","Squared buttons with correct hover/active states and shadow","Mobile menu overlay with transform/visibility transition, backdrop-filter blur(14px)","Failure guards: paper-first, resting opacity 1, two-rAF entrance fallback, canvas fallback, prefers-reduced-motion reset","Footer with 3 meta items and 3 links exactly as specified","Telemetry hook with window.__DEMO__, ready, frames, effects, errors pushed in correct order","Mobile burger toggle, Escape key, link click, and resize all close the menu"],"requirements_missed":[],"issues":[{"issue":"Header CTA 'REQUEST A PROOF' sits hard against the right edge with the box border visually touching the screenshot frame","severity":"low","evidence":"At 1024px, --header-x reduces to 32px (901-1279 breakpoint); the long button 'Request a proof' hugs the right margin with no breathing room"},{"issue":"Specimen row 'Cotton · 600 gsm' wraps to two lines at 1024px viewport","severity":"low","evidence":"spec-sm line wraps 'Cotton · 600' then 'gsm' inside the 244px aside column — minor aesthetic break, not incorrect per spec"},{"issue":"Top-right registration mark is visible bleeding through/around the header CTA button (z-index layering: marks z2 behind header z30, but the accent-wash CTA background is mostly transparent so the mark reads through)","severity":"low","evidence":"Screenshot shows a small crosshair-like shape adjacent to the right of the header CTA in the top-right corner"},{"issue":"Grain overlay (--grain-op 0.11) is barely perceptible at 1024x640; relies heavily on the muted film-grain SVG working in browsers","severity":"low","evidence":"No obvious film grain texture visible in the screenshot — likely just under-subtle, not a bug"}],"summary":"A high-fidelity, character-perfect implementation of the Vellum brief: paper backdrop, hairline grid, registration marks, down-sampled canvas, oxide accent on outlives, and the bleed-mask h1 with correct per-glyph stagger all land exactly as specified. Only nitpicks are layout-density (CTA crowding the right edge, specimen mono row breaking) and the grain being nearly invisible at this size — none break the design.","usage":{"input":22698,"output":7052,"total":29750,"reasoning":6113,"cached":128},"latency_ms":73052}},{"slug":"jiro-about-brand-overview","title":"About Brand Overview","category":"Section","effect":"A clean, light-themed about section featuring a two-column layout with animated reveal effects. Includes a pill badge, headline with muted secondary text, a featured image, and a 6","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/jiro-about-brand-overview/index.html","doc":"/prompts/jiro-about-brand-overview.md","prompt_text":"ADAPT THIS JIRO SECTION PROMPT INTO A STANDALONE VANILLA PAGE.\n\nThe text below is a real prompt from another prompt gallery. It describes a website\nSECTION — a \"About us\" block called \"About Brand Overview\". It is written for React + Tailwind +\nframer-motion. You are NOT building that. You are rebuilding the SAME section, with the\nsame intent, structure and content, under our contract:\n\n  · ONE self-contained index.html. No React, no Tailwind, no framer-motion, no lucide-react,\n    no build step, no dependencies at all.\n  · The section is the whole page. Put it on a page with minimal chrome: a slim header with\n    a wordmark and two nav links, then the section as the dominant content, then a one-line\n    footer. Nothing else. No hero, no pricing, no extra sections.\n  · Honour the section's OWN structure and content faithfully. If it asks for a heading, a\n    grid of cards, a mockup panel and badges, build exactly those things. Do not add or\n    remove parts.\n\n  · NO INVENTED FACTS. This is the rule reviewers fail these pages on most often:\n      - Never invent statistics. If the brief implies a stats row but supplies no numbers,\n        render the figures as em-dashes or \"XX\" — NEVER as plausible-looking values like\n        100 / 200 / 24 / 4.2M. A number that looks real is a fabricated claim.\n      - Never invent a brand name. If the brief's title contains a codename (\"About Us 02\n        Orbit\", \"Pricing 01 Kelo\"), that is a FILE name, not a company. Use a neutral\n        wordmark such as \"Studio\" or \"Company\" — do not promote the codename into a brand,\n        and do not put it in the <title> or a © line.\n      - Never invent testimonials, prices, customer names, awards, dates or copyright years.\n        A © line should read \"© Company\" with no year, or be omitted.\n      - Do not assert a stat category the brief never supplied (no \"Industry Awards\").\n      - Nav and footer links must point at real anchors inside this page, or be plain text.\n        Never ship a link to a section that does not exist.\n\n  · NO SCAFFOLDING IN THE OUTPUT. Every visible string must read as finished site copy.\n    Never render text like \"Neutral placeholder text for the descriptive paragraph.\",\n    \"One-line footer text placeholder.\", \"Wordmark\", or any sentence that addresses the\n    reviewer instead of a visitor. If the brief gives no copy, write short neutral marketing\n    copy that a real site could ship — never a description of what the copy would be.\n    Do not rephrase the brief's own \"Purpose\" paragraph as page copy.\n\n  · Write copy for the audience the brief names. If it is a personal brand or freelancer,\n    do not use agency voice (\"our clients\", \"the brands we serve\").\n\n  · Do not add perpetual motion the brief did not ask for. If the brief says motion happens\n    on scroll entry, do not also run an infinite rAF loop.\n  · Rebuild every animation it asks for with CSS or a small rAF loop. Nothing may be\n    imported.\n  · No external media: no images, no video, no icon fonts. Any icon or illustration must be\n    inline SVG you draw yourself. Any \"product mockup\" or \"screenshot\" must be built from\n    CSS/SVG shapes, not an image.\n  · Responsive at 390 / 768 / 1440 / 1920 with no horizontal overflow.\n  · Accessibility: exactly one <h1>, real header/main/footer landmarks, every visible\n    control has an accessible name, visible :focus-visible ring.\n  · A full prefers-reduced-motion: reduce block that leaves the page static and readable.\n  · window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] } —\n    set ready after first paint, increment frames inside the rAF loop, push effect names,\n    and push any window 'error' into errors.\n  · Under 90 KB. Zero console output. No uncaught exceptions.\n\n--- THE JIRO SECTION PROMPT (the brief you are adapting) ---\n\nBuild an About us section for a modern website: \"About Brand Overview\".\n\nPurpose\nGive visitors a calm, credibility-first introduction to the company behind the product. The section tells a short brand story and backs it with headline metrics, for first-time visitors weighing up the brand.\n\nLayout & content\n- Two-column layout pairing a text column with a featured photograph.\n- A small pill badge above the headline, then a headline with a muted secondary line of supporting copy.\n- A six-item stat grid, each entry combining a figure, a short label and a small arrow icon.\n- Keep wording neutral; the description supplies no real brand copy.\n\nVisual direction\nLight, minimal and airy. Lean on whitespace and restrained type rather than decoration, and let the photography carry most of the colour. Set headings in Inter Tight and keep supporting text quieter than the headline.\n\nInteraction & motion\nUse Framer Motion for staggered, scroll-triggered reveals on a custom cubic-bezier ease, so badge, headline, image and stats arrive in sequence rather than all at once.\n\nStack\n- React functional component + Tailwind CSS.\n- framer-motion and lucide-react.\n\nConstraints\n- Match the described layout and content faithfully.\n- Do not invent statistics, testimonials, prices, brand names or section counts.\n- Use neutral placeholder copy where the description supplies no real text.\n\n--- END JIRO PROMPT ---\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the section's own structure is present (headings, cards, panels as the brief describes)","exactly one <h1> and real header/main/footer landmarks","no external media: no img src, no video, no remote URL","no horizontal overflow at 390 / 768 / 1440","prefers-reduced-motion leaves the page static and readable","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"],"usage":{"build_tokens":36677,"build_prompt_tokens":1636,"build_completion_tokens":35041,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":459718,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":0,"total_tokens":36677},"verification":{"passed":true,"passed_checks":17,"total_checks":17,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"28148 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"6 frames, effects: badge-reveal, headline-reveal, image-reveal, stat-stagger"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"82 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"About Brand\nOverview\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"About Brand Overview"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=528 pageErrors=0"}]},"crosscheck":null},{"slug":"jiro-about-countdown","title":"About Countdown","category":"Section","effect":"A light-themed about section with animated number counters. Features a pill badge, large descriptive headline, and 4-column stats grid with animated count-up numbers on scroll. Use","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/jiro-about-countdown/index.html","doc":"/prompts/jiro-about-countdown.md","prompt_text":"ADAPT THIS JIRO SECTION PROMPT INTO A STANDALONE VANILLA PAGE.\n\nThe text below is a real prompt from another prompt gallery. It describes a website\nSECTION — a \"About us\" block called \"About Countdown\". It is written for React + Tailwind +\nframer-motion. You are NOT building that. You are rebuilding the SAME section, with the\nsame intent, structure and content, under our contract:\n\n  · ONE self-contained index.html. No React, no Tailwind, no framer-motion, no lucide-react,\n    no build step, no dependencies at all.\n  · The section is the whole page. Put it on a page with minimal chrome: a slim header with\n    a wordmark and two nav links, then the section as the dominant content, then a one-line\n    footer. Nothing else. No hero, no pricing, no extra sections.\n  · Honour the section's OWN structure and content faithfully. If it asks for a heading, a\n    grid of cards, a mockup panel and badges, build exactly those things. Do not add or\n    remove parts.\n\n  · NO INVENTED FACTS. This is the rule reviewers fail these pages on most often:\n      - Never invent statistics. If the brief implies a stats row but supplies no numbers,\n        render the figures as em-dashes or \"XX\" — NEVER as plausible-looking values like\n        100 / 200 / 24 / 4.2M. A number that looks real is a fabricated claim.\n      - Never invent a brand name. If the brief's title contains a codename (\"About Us 02\n        Orbit\", \"Pricing 01 Kelo\"), that is a FILE name, not a company. Use a neutral\n        wordmark such as \"Studio\" or \"Company\" — do not promote the codename into a brand,\n        and do not put it in the <title> or a © line.\n      - Never invent testimonials, prices, customer names, awards, dates or copyright years.\n        A © line should read \"© Company\" with no year, or be omitted.\n      - Do not assert a stat category the brief never supplied (no \"Industry Awards\").\n      - Nav and footer links must point at real anchors inside this page, or be plain text.\n        Never ship a link to a section that does not exist.\n\n  · NO SCAFFOLDING IN THE OUTPUT. Every visible string must read as finished site copy.\n    Never render text like \"Neutral placeholder text for the descriptive paragraph.\",\n    \"One-line footer text placeholder.\", \"Wordmark\", or any sentence that addresses the\n    reviewer instead of a visitor. If the brief gives no copy, write short neutral marketing\n    copy that a real site could ship — never a description of what the copy would be.\n    Do not rephrase the brief's own \"Purpose\" paragraph as page copy.\n\n  · Write copy for the audience the brief names. If it is a personal brand or freelancer,\n    do not use agency voice (\"our clients\", \"the brands we serve\").\n\n  · Do not add perpetual motion the brief did not ask for. If the brief says motion happens\n    on scroll entry, do not also run an infinite rAF loop.\n  · Rebuild every animation it asks for with CSS or a small rAF loop. Nothing may be\n    imported.\n  · No external media: no images, no video, no icon fonts. Any icon or illustration must be\n    inline SVG you draw yourself. Any \"product mockup\" or \"screenshot\" must be built from\n    CSS/SVG shapes, not an image.\n  · Responsive at 390 / 768 / 1440 / 1920 with no horizontal overflow.\n  · Accessibility: exactly one <h1>, real header/main/footer landmarks, every visible\n    control has an accessible name, visible :focus-visible ring.\n  · A full prefers-reduced-motion: reduce block that leaves the page static and readable.\n  · window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] } —\n    set ready after first paint, increment frames inside the rAF loop, push effect names,\n    and push any window 'error' into errors.\n  · Under 90 KB. Zero console output. No uncaught exceptions.\n\n--- THE JIRO SECTION PROMPT (the brief you are adapting) ---\n\nBuild an About us section for a modern website: \"About Countdown\".\n\nPurpose\nIntroduce the company behind the product with a light, minimal about section whose centrepiece is a row of animated number counters. It should read as an editorial statement of who we are, then back that statement with countable proof points.\n\nLayout & content\n- A small pill badge above the headline acting as the section eyebrow.\n- A large descriptive headline that carries the section message.\n- A four-column stats grid beneath the headline, each column holding a label and a count-up number.\n- Keep supporting copy short and use neutral placeholder wording.\n\nVisual direction\nLight theme, minimal and restrained: generous whitespace, quiet borders, and typographic hierarchy doing the work. Set headings in Geist and body text in Inter, inside a neutral palette with one subtle accent for the badge.\n\nInteraction & motion\nUse Framer Motion for blur-up reveals with custom cubic-bezier easing as the section enters the viewport. Stat numbers count up on scroll, with subtle hover effects on the columns.\n\nStack\n- React functional component + Tailwind CSS.\n- framer-motion.\n\nConstraints\n- Match the described layout and content faithfully.\n- Do not invent statistics, testimonials, prices, brand names or section counts.\n- Use neutral placeholder copy where the description supplies no real text.\n\n--- END JIRO PROMPT ---\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the section's own structure is present (headings, cards, panels as the brief describes)","exactly one <h1> and real header/main/footer landmarks","no external media: no img src, no video, no remote URL","no horizontal overflow at 390 / 768 / 1440","prefers-reduced-motion leaves the page static and readable","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"],"usage":{"build_tokens":54534,"build_prompt_tokens":2046,"build_completion_tokens":52488,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":897880,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":0,"total_tokens":54534},"verification":{"passed":true,"passed_checks":17,"total_checks":17,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"17490 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"21 frames, effects: blur-up-reveal, stat-count-up, stat-hover, nav-underline, smooth-anchor-scroll"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"75 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"We make complex ideas feel clear, useful, and human.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"About us — Studio"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=275 pageErrors=0"}]},"crosscheck":null},{"slug":"jiro-about-multi-layout","title":"About Multi Layout","category":"Section","effect":"A light-themed multi-layout about section with overlapping images and animated counters. Features uppercase badge, large Instrument Serif headline with muted text, asymmetric two-c","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/jiro-about-multi-layout/index.html","doc":"/prompts/jiro-about-multi-layout.md","prompt_text":"ADAPT THIS JIRO SECTION PROMPT INTO A STANDALONE VANILLA PAGE.\n\nThe text below is a real prompt from another prompt gallery. It describes a website\nSECTION — a \"About us\" block called \"About Multi Layout\". It is written for React + Tailwind +\nframer-motion. You are NOT building that. You are rebuilding the SAME section, with the\nsame intent, structure and content, under our contract:\n\n  · ONE self-contained index.html. No React, no Tailwind, no framer-motion, no lucide-react,\n    no build step, no dependencies at all.\n  · The section is the whole page. Put it on a page with minimal chrome: a slim header with\n    a wordmark and two nav links, then the section as the dominant content, then a one-line\n    footer. Nothing else. No hero, no pricing, no extra sections.\n  · Honour the section's OWN structure and content faithfully. If it asks for a heading, a\n    grid of cards, a mockup panel and badges, build exactly those things. Do not add or\n    remove parts.\n\n  · NO INVENTED FACTS. This is the rule reviewers fail these pages on most often:\n      - Never invent statistics. If the brief implies a stats row but supplies no numbers,\n        render the figures as em-dashes or \"XX\" — NEVER as plausible-looking values like\n        100 / 200 / 24 / 4.2M. A number that looks real is a fabricated claim.\n      - Never invent a brand name. If the brief's title contains a codename (\"About Us 02\n        Orbit\", \"Pricing 01 Kelo\"), that is a FILE name, not a company. Use a neutral\n        wordmark such as \"Studio\" or \"Company\" — do not promote the codename into a brand,\n        and do not put it in the <title> or a © line.\n      - Never invent testimonials, prices, customer names, awards, dates or copyright years.\n        A © line should read \"© Company\" with no year, or be omitted.\n      - Do not assert a stat category the brief never supplied (no \"Industry Awards\").\n      - Nav and footer links must point at real anchors inside this page, or be plain text.\n        Never ship a link to a section that does not exist.\n\n  · NO SCAFFOLDING IN THE OUTPUT. Every visible string must read as finished site copy.\n    Never render text like \"Neutral placeholder text for the descriptive paragraph.\",\n    \"One-line footer text placeholder.\", \"Wordmark\", or any sentence that addresses the\n    reviewer instead of a visitor. If the brief gives no copy, write short neutral marketing\n    copy that a real site could ship — never a description of what the copy would be.\n    Do not rephrase the brief's own \"Purpose\" paragraph as page copy.\n\n  · Write copy for the audience the brief names. If it is a personal brand or freelancer,\n    do not use agency voice (\"our clients\", \"the brands we serve\").\n\n  · Do not add perpetual motion the brief did not ask for. If the brief says motion happens\n    on scroll entry, do not also run an infinite rAF loop.\n  · Rebuild every animation it asks for with CSS or a small rAF loop. Nothing may be\n    imported.\n  · No external media: no images, no video, no icon fonts. Any icon or illustration must be\n    inline SVG you draw yourself. Any \"product mockup\" or \"screenshot\" must be built from\n    CSS/SVG shapes, not an image.\n  · Responsive at 390 / 768 / 1440 / 1920 with no horizontal overflow.\n  · Accessibility: exactly one <h1>, real header/main/footer landmarks, every visible\n    control has an accessible name, visible :focus-visible ring.\n  · A full prefers-reduced-motion: reduce block that leaves the page static and readable.\n  · window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] } —\n    set ready after first paint, increment frames inside the rAF loop, push effect names,\n    and push any window 'error' into errors.\n  · Under 90 KB. Zero console output. No uncaught exceptions.\n\n--- THE JIRO SECTION PROMPT (the brief you are adapting) ---\n\nBuild an About us section for a modern website: \"About Multi Layout\".\n\nPurpose\nIntroduce the company behind the product in a light, elegant multi-layout block. It pairs a human profile image with hard numbers, so visitors meet the people and the traction in one view.\n\nLayout & content\n- A small uppercase badge above a large serif headline and a muted supporting text line.\n- An asymmetric two-column grid: an overlapping profile image on one side, a descriptive paragraph on the other.\n- A stats row across four columns, each combining a count-up number with a short label.\n- Stack the grid to one column on small screens and let the stats row wrap.\n\nVisual direction\nLight and elegant, photography-led. Use Inter for body and interface text and Instrument Serif for the headline, with generous whitespace so the muted text sits quietly beneath the serif.\n\nInteraction & motion\nAnimate the counters on scroll, and give the image and text blocks a blur-up reveal with a premium cubic-bezier easing curve.\n\nStack\n- React functional component + Tailwind CSS.\n- framer-motion for the reveals and the count-up numbers.\n\nConstraints\n- Match the described layout and content faithfully.\n- Do not invent statistics, testimonials, prices, brand names or section counts.\n- Use neutral placeholder copy where the description supplies no real text.\n\n--- END JIRO PROMPT ---\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the section's own structure is present (headings, cards, panels as the brief describes)","exactly one <h1> and real header/main/footer landmarks","no external media: no img src, no video, no remote URL","no horizontal overflow at 390 / 768 / 1440","prefers-reduced-motion leaves the page static and readable","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"],"usage":{"build_tokens":19501,"build_prompt_tokens":1580,"build_completion_tokens":17921,"build_reasoning_tokens":14486,"build_calls":1,"build_attempts":1,"build_latency_ms":170638,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":0,"total_tokens":19501},"verification":{"passed":true,"passed_checks":17,"total_checks":17,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"12210 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"13 frames, effects: blur-up, count-up"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"72 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"About Multi Layout\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"About Multi Layout"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=559 pageErrors=0"}]},"crosscheck":null},{"slug":"jiro-about-team-overview","title":"About Team Overview","category":"Section","effect":"A light-themed about section with dot grid background, floating UI card, and animated metrics. Features uppercase badge, large responsive headline, CTA button with hover effect, he","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/jiro-about-team-overview/index.html","doc":"/prompts/jiro-about-team-overview.md","prompt_text":"ADAPT THIS JIRO SECTION PROMPT INTO A STANDALONE VANILLA PAGE.\n\nThe text below is a real prompt from another prompt gallery. It describes a website\nSECTION — a \"About us\" block called \"About Team Overview\". It is written for React + Tailwind +\nframer-motion. You are NOT building that. You are rebuilding the SAME section, with the\nsame intent, structure and content, under our contract:\n\n  · ONE self-contained index.html. No React, no Tailwind, no framer-motion, no lucide-react,\n    no build step, no dependencies at all.\n  · The section is the whole page. Put it on a page with minimal chrome: a slim header with\n    a wordmark and two nav links, then the section as the dominant content, then a one-line\n    footer. Nothing else. No hero, no pricing, no extra sections.\n  · Honour the section's OWN structure and content faithfully. If it asks for a heading, a\n    grid of cards, a mockup panel and badges, build exactly those things. Do not add or\n    remove parts.\n\n  · NO INVENTED FACTS. This is the rule reviewers fail these pages on most often:\n      - Never invent statistics. If the brief implies a stats row but supplies no numbers,\n        render the figures as em-dashes or \"XX\" — NEVER as plausible-looking values like\n        100 / 200 / 24 / 4.2M. A number that looks real is a fabricated claim.\n      - Never invent a brand name. If the brief's title contains a codename (\"About Us 02\n        Orbit\", \"Pricing 01 Kelo\"), that is a FILE name, not a company. Use a neutral\n        wordmark such as \"Studio\" or \"Company\" — do not promote the codename into a brand,\n        and do not put it in the <title> or a © line.\n      - Never invent testimonials, prices, customer names, awards, dates or copyright years.\n        A © line should read \"© Company\" with no year, or be omitted.\n      - Do not assert a stat category the brief never supplied (no \"Industry Awards\").\n      - Nav and footer links must point at real anchors inside this page, or be plain text.\n        Never ship a link to a section that does not exist.\n\n  · NO SCAFFOLDING IN THE OUTPUT. Every visible string must read as finished site copy.\n    Never render text like \"Neutral placeholder text for the descriptive paragraph.\",\n    \"One-line footer text placeholder.\", \"Wordmark\", or any sentence that addresses the\n    reviewer instead of a visitor. If the brief gives no copy, write short neutral marketing\n    copy that a real site could ship — never a description of what the copy would be.\n    Do not rephrase the brief's own \"Purpose\" paragraph as page copy.\n\n  · Write copy for the audience the brief names. If it is a personal brand or freelancer,\n    do not use agency voice (\"our clients\", \"the brands we serve\").\n\n  · Do not add perpetual motion the brief did not ask for. If the brief says motion happens\n    on scroll entry, do not also run an infinite rAF loop.\n  · Rebuild every animation it asks for with CSS or a small rAF loop. Nothing may be\n    imported.\n  · No external media: no images, no video, no icon fonts. Any icon or illustration must be\n    inline SVG you draw yourself. Any \"product mockup\" or \"screenshot\" must be built from\n    CSS/SVG shapes, not an image.\n  · Responsive at 390 / 768 / 1440 / 1920 with no horizontal overflow.\n  · Accessibility: exactly one <h1>, real header/main/footer landmarks, every visible\n    control has an accessible name, visible :focus-visible ring.\n  · A full prefers-reduced-motion: reduce block that leaves the page static and readable.\n  · window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] } —\n    set ready after first paint, increment frames inside the rAF loop, push effect names,\n    and push any window 'error' into errors.\n  · Under 90 KB. Zero console output. No uncaught exceptions.\n\n--- THE JIRO SECTION PROMPT (the brief you are adapting) ---\n\nBuild an About us section for a modern website: \"About Team Overview\".\n\nPurpose\nIntroduce the people behind the product in a light, minimal way, pairing a confident headline and CTA with visible proof of momentum. It is the opener visitors read when deciding who is behind the company.\n\nLayout & content\n- Uppercase badge above a large responsive headline, with a short supporting paragraph and a CTA button.\n- A hero image area with a floating UI card layered over it, containing an animated bar chart that reads as a dashboard or growth preview.\n- A four-column metrics row with animated counters, separated by vertical dividers.\n- Subtle blob shapes behind the content for depth.\n\nVisual direction\nLight theme, minimal and airy. Dot grid background, Inter typography, soft blurred blobs and restrained surfaces so the floating card and the metric numbers carry the emphasis.\n\nInteraction & motion\nBlur-up reveal animations on mount or scroll using premium cubic-bezier easing, counters that animate into view, and a hover effect on the CTA button.\n\nStack\n- React functional component + Tailwind CSS.\n- framer-motion for reveals, counters and hover states.\n\nConstraints\n- Match the described layout and content faithfully.\n- Do not invent statistics, testimonials, prices, brand names or section counts.\n- Use neutral placeholder copy where the description supplies no real text.\n\n--- END JIRO PROMPT ---\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the section's own structure is present (headings, cards, panels as the brief describes)","exactly one <h1> and real header/main/footer landmarks","no external media: no img src, no video, no remote URL","no horizontal overflow at 390 / 768 / 1440","prefers-reduced-motion leaves the page static and readable","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"],"usage":{"build_tokens":25954,"build_prompt_tokens":2003,"build_completion_tokens":23951,"build_reasoning_tokens":18927,"build_calls":1,"build_attempts":1,"build_latency_ms":244836,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":0,"total_tokens":25954},"verification":{"passed":false,"passed_checks":14,"total_checks":17,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"17007 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"21 frames, effects: blur-up-reveal, bar-chart-animation"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"37 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Meet the people behind the vision.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"About Us"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":false,"detail":"429 > 390 (div.blob.blob-1 [-39..156]; div.blob.blob-2 [273..429]; rect [-83..473]; rect [-83..473]; circle [-62..105]; circle [278..529]; path [-83..473])"},{"name":"no horizontal overflow @ 768px","pass":false,"detail":"845 > 768 (div.blob.blob-1 [-77..307]; div.blob.blob-2 [538..845]; circle [492..815])"},{"name":"no horizontal overflow @ 1440px","pass":false,"detail":"1584 > 1440 (div.blob.blob-1 [-144..456]; div.blob.blob-2 [1084..1584]; circle [905..1458])"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=271 pageErrors=0"}]},"crosscheck":null},{"slug":"jiro-about-us-01-blaze","title":"About us 01 Blaze","category":"Section","effect":"A travel-focused About section with adventure imagery, mission narrative, and warm editorial typography. Built for travel platforms, tourism brands, and experience-driven service b","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/jiro-about-us-01-blaze/index.html","doc":"/prompts/jiro-about-us-01-blaze.md","prompt_text":"ADAPT THIS JIRO SECTION PROMPT INTO A STANDALONE VANILLA PAGE.\n\nThe text below is a real prompt from another prompt gallery. It describes a website\nSECTION — a \"About us\" block called \"About us 01 Blaze\". It is written for React + Tailwind +\nframer-motion. You are NOT building that. You are rebuilding the SAME section, with the\nsame intent, structure and content, under our contract:\n\n  · ONE self-contained index.html. No React, no Tailwind, no framer-motion, no lucide-react,\n    no build step, no dependencies at all.\n  · The section is the whole page. Put it on a page with minimal chrome: a slim header with\n    a wordmark and two nav links, then the section as the dominant content, then a one-line\n    footer. Nothing else. No hero, no pricing, no extra sections.\n  · Honour the section's OWN structure and content faithfully. If it asks for a heading, a\n    grid of cards, a mockup panel and badges, build exactly those things. Do not add or\n    remove parts.\n\n  · NO INVENTED FACTS. This is the rule reviewers fail these pages on most often:\n      - Never invent statistics. If the brief implies a stats row but supplies no numbers,\n        render the figures as em-dashes or \"XX\" — NEVER as plausible-looking values like\n        100 / 200 / 24 / 4.2M. A number that looks real is a fabricated claim.\n      - Never invent a brand name. If the brief's title contains a codename (\"About Us 02\n        Orbit\", \"Pricing 01 Kelo\"), that is a FILE name, not a company. Use a neutral\n        wordmark such as \"Studio\" or \"Company\" — do not promote the codename into a brand,\n        and do not put it in the <title> or a © line.\n      - Never invent testimonials, prices, customer names, awards, dates or copyright years.\n        A © line should read \"© Company\" with no year, or be omitted.\n      - Do not assert a stat category the brief never supplied (no \"Industry Awards\").\n      - Nav and footer links must point at real anchors inside this page, or be plain text.\n        Never ship a link to a section that does not exist.\n\n  · NO SCAFFOLDING IN THE OUTPUT. Every visible string must read as finished site copy.\n    Never render text like \"Neutral placeholder text for the descriptive paragraph.\",\n    \"One-line footer text placeholder.\", \"Wordmark\", or any sentence that addresses the\n    reviewer instead of a visitor. If the brief gives no copy, write short neutral marketing\n    copy that a real site could ship — never a description of what the copy would be.\n    Do not rephrase the brief's own \"Purpose\" paragraph as page copy.\n\n  · Write copy for the audience the brief names. If it is a personal brand or freelancer,\n    do not use agency voice (\"our clients\", \"the brands we serve\").\n\n  · Do not add perpetual motion the brief did not ask for. If the brief says motion happens\n    on scroll entry, do not also run an infinite rAF loop.\n  · Rebuild every animation it asks for with CSS or a small rAF loop. Nothing may be\n    imported.\n  · No external media: no images, no video, no icon fonts. Any icon or illustration must be\n    inline SVG you draw yourself. Any \"product mockup\" or \"screenshot\" must be built from\n    CSS/SVG shapes, not an image.\n  · Responsive at 390 / 768 / 1440 / 1920 with no horizontal overflow.\n  · Accessibility: exactly one <h1>, real header/main/footer landmarks, every visible\n    control has an accessible name, visible :focus-visible ring.\n  · A full prefers-reduced-motion: reduce block that leaves the page static and readable.\n  · window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] } —\n    set ready after first paint, increment frames inside the rAF loop, push effect names,\n    and push any window 'error' into errors.\n  · Under 90 KB. Zero console output. No uncaught exceptions.\n\n--- THE JIRO SECTION PROMPT (the brief you are adapting) ---\n\nBuild an About us section for a modern website: \"About us 01 Blaze\".\n\nPurpose\nIntroduce a travel and experience-driven brand by pairing adventure imagery with a short mission narrative. Written for travel platforms, tourism brands and service businesses that sell trips or guided experiences.\n\nLayout & content\n- Editorial opening that pairs a mission statement with adventure photography.\n- A supporting narrative block explaining who the brand serves and what it stands for.\n- A photographic strip or gallery carrying the adventure imagery.\n- A marquee band running short brand or value phrases as animated text.\n\nVisual direction\nLight, warm and editorial: large display type, generous whitespace, and a warm photographic treatment so the imagery reads as the hero rather than decoration.\n\nInteraction & motion\nAnimate the mission text on entrance, let the phrase marquee scroll continuously, and add light interactive hover states on the photographic cards.\n\nStack\n- React functional component + Tailwind CSS.\n- framer-motion for entrance, marquee and hover animation.\n- lucide-react for iconography.\n\nConstraints\n- Match the described layout and content faithfully.\n- Do not invent statistics, testimonials, prices, brand names or section counts.\n- Use neutral placeholder copy where the description supplies no real text.\n\n--- END JIRO PROMPT ---\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the section's own structure is present (headings, cards, panels as the brief describes)","exactly one <h1> and real header/main/footer landmarks","no external media: no img src, no video, no remote URL","no horizontal overflow at 390 / 768 / 1440","prefers-reduced-motion leaves the page static and readable","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"],"usage":{"build_tokens":24786,"build_prompt_tokens":1562,"build_completion_tokens":23224,"build_reasoning_tokens":18956,"build_calls":1,"build_attempts":1,"build_latency_ms":215500,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":0,"total_tokens":24786},"verification":{"passed":true,"passed_checks":17,"total_checks":17,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"13544 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"24 frames, effects: entrance-animation, marquee-scroll, card-hover"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"73 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Curators of the wild.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"About us 01 Blaze"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=757 pageErrors=0"}]},"crosscheck":null},{"slug":"jiro-about-us-01-kelo","title":"About Us 01 Kelo","category":"Section","effect":"An AI product About section with company mission, team stats, and product philosophy narrative. Designed for AI startups, tech brands, and product-led SaaS company pages.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/jiro-about-us-01-kelo/index.html","doc":"/prompts/jiro-about-us-01-kelo.md","prompt_text":"ADAPT THIS JIRO SECTION PROMPT INTO A STANDALONE VANILLA PAGE.\n\nThe text below is a real prompt from another prompt gallery. It describes a website\nSECTION — a \"About us\" block called \"About Us 01 Kelo\". It is written for React + Tailwind +\nframer-motion. You are NOT building that. You are rebuilding the SAME section, with the\nsame intent, structure and content, under our contract:\n\n  · ONE self-contained index.html. No React, no Tailwind, no framer-motion, no lucide-react,\n    no build step, no dependencies at all.\n  · The section is the whole page. Put it on a page with minimal chrome: a slim header with\n    a wordmark and two nav links, then the section as the dominant content, then a one-line\n    footer. Nothing else. No hero, no pricing, no extra sections.\n  · Honour the section's OWN structure and content faithfully. If it asks for a heading, a\n    grid of cards, a mockup panel and badges, build exactly those things. Do not add or\n    remove parts.\n\n  · NO INVENTED FACTS. This is the rule reviewers fail these pages on most often:\n      - Never invent statistics. If the brief implies a stats row but supplies no numbers,\n        render the figures as em-dashes or \"XX\" — NEVER as plausible-looking values like\n        100 / 200 / 24 / 4.2M. A number that looks real is a fabricated claim.\n      - Never invent a brand name. If the brief's title contains a codename (\"About Us 02\n        Orbit\", \"Pricing 01 Kelo\"), that is a FILE name, not a company. Use a neutral\n        wordmark such as \"Studio\" or \"Company\" — do not promote the codename into a brand,\n        and do not put it in the <title> or a © line.\n      - Never invent testimonials, prices, customer names, awards, dates or copyright years.\n        A © line should read \"© Company\" with no year, or be omitted.\n      - Do not assert a stat category the brief never supplied (no \"Industry Awards\").\n      - Nav and footer links must point at real anchors inside this page, or be plain text.\n        Never ship a link to a section that does not exist.\n\n  · NO SCAFFOLDING IN THE OUTPUT. Every visible string must read as finished site copy.\n    Never render text like \"Neutral placeholder text for the descriptive paragraph.\",\n    \"One-line footer text placeholder.\", \"Wordmark\", or any sentence that addresses the\n    reviewer instead of a visitor. If the brief gives no copy, write short neutral marketing\n    copy that a real site could ship — never a description of what the copy would be.\n    Do not rephrase the brief's own \"Purpose\" paragraph as page copy.\n\n  · Write copy for the audience the brief names. If it is a personal brand or freelancer,\n    do not use agency voice (\"our clients\", \"the brands we serve\").\n\n  · Do not add perpetual motion the brief did not ask for. If the brief says motion happens\n    on scroll entry, do not also run an infinite rAF loop.\n  · Rebuild every animation it asks for with CSS or a small rAF loop. Nothing may be\n    imported.\n  · No external media: no images, no video, no icon fonts. Any icon or illustration must be\n    inline SVG you draw yourself. Any \"product mockup\" or \"screenshot\" must be built from\n    CSS/SVG shapes, not an image.\n  · Responsive at 390 / 768 / 1440 / 1920 with no horizontal overflow.\n  · Accessibility: exactly one <h1>, real header/main/footer landmarks, every visible\n    control has an accessible name, visible :focus-visible ring.\n  · A full prefers-reduced-motion: reduce block that leaves the page static and readable.\n  · window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] } —\n    set ready after first paint, increment frames inside the rAF loop, push effect names,\n    and push any window 'error' into errors.\n  · Under 90 KB. Zero console output. No uncaught exceptions.\n\n--- THE JIRO SECTION PROMPT (the brief you are adapting) ---\n\nBuild an About us section for a modern website: \"About Us 01 Kelo\".\n\nPurpose\nIntroduce an AI product company: what it stands for, who is behind it, and the philosophy that shapes what it builds. Aimed at AI startups, tech brands and product-led SaaS pages that need a credible company story.\n\nLayout & content\n- A mission statement block with a large lead sentence and a supporting paragraph.\n- A compact team stats strip; present the figures as editable stand-ins.\n- A product philosophy narrative explaining how the team approaches building its product.\n- A closing line or link that leads into the rest of the company pages.\n\nVisual direction\nLight, minimal and modern: generous whitespace, a restrained type scale, thin dividers and a calm neutral surface. The stats should read as quiet data points rather than loud badges.\n\nInteraction & motion\nAnimate the section on scroll: fade and short upward reveals for the mission copy, plus a staggered entrance for the stats strip.\n\nStack\n- React functional component + Tailwind CSS.\n- framer-motion for scroll reveals and lucide-react for small icons.\n\nConstraints\n- Match the described layout and content faithfully.\n- Do not invent statistics, testimonials, prices, brand names or section counts.\n- Use neutral placeholder copy where the description supplies no real text.\n\n--- END JIRO PROMPT ---\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the section's own structure is present (headings, cards, panels as the brief describes)","exactly one <h1> and real header/main/footer landmarks","no external media: no img src, no video, no remote URL","no horizontal overflow at 390 / 768 / 1440","prefers-reduced-motion leaves the page static and readable","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"],"usage":{"build_tokens":27155,"build_prompt_tokens":1635,"build_completion_tokens":25520,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":439584,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":0,"total_tokens":27155},"verification":{"passed":true,"passed_checks":17,"total_checks":17,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"17625 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"7 frames, effects: mission-fade-up-reveal, stats-staggered-reveal, philosophy-fade-up-reveal"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"73 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Intelligence, made more human.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"About Us 01 — Kelo"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=874 pageErrors=0"}]},"crosscheck":null},{"slug":"jiro-about-us-01-orbit","title":"About Us 01 Orbit","category":"Section","effect":"A coach About section with professional headshot, value proposition, and consultation booking CTAs. Designed for personal coaches, mentors, and one-on-one service providers.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/jiro-about-us-01-orbit/index.html","doc":"/prompts/jiro-about-us-01-orbit.md","prompt_text":"ADAPT THIS JIRO SECTION PROMPT INTO A STANDALONE VANILLA PAGE.\n\nThe text below is a real prompt from another prompt gallery. It describes a website\nSECTION — a \"About us\" block called \"About Us 01 Orbit\". It is written for React + Tailwind +\nframer-motion. You are NOT building that. You are rebuilding the SAME section, with the\nsame intent, structure and content, under our contract:\n\n  · ONE self-contained index.html. No React, no Tailwind, no framer-motion, no lucide-react,\n    no build step, no dependencies at all.\n  · The section is the whole page. Put it on a page with minimal chrome: a slim header with\n    a wordmark and two nav links, then the section as the dominant content, then a one-line\n    footer. Nothing else. No hero, no pricing, no extra sections.\n  · Honour the section's OWN structure and content faithfully. If it asks for a heading, a\n    grid of cards, a mockup panel and badges, build exactly those things. Do not add or\n    remove parts.\n\n  · NO INVENTED FACTS. This is the rule reviewers fail these pages on most often:\n      - Never invent statistics. If the brief implies a stats row but supplies no numbers,\n        render the figures as em-dashes or \"XX\" — NEVER as plausible-looking values like\n        100 / 200 / 24 / 4.2M. A number that looks real is a fabricated claim.\n      - Never invent a brand name. If the brief's title contains a codename (\"About Us 02\n        Orbit\", \"Pricing 01 Kelo\"), that is a FILE name, not a company. Use a neutral\n        wordmark such as \"Studio\" or \"Company\" — do not promote the codename into a brand,\n        and do not put it in the <title> or a © line.\n      - Never invent testimonials, prices, customer names, awards, dates or copyright years.\n        A © line should read \"© Company\" with no year, or be omitted.\n      - Do not assert a stat category the brief never supplied (no \"Industry Awards\").\n      - Nav and footer links must point at real anchors inside this page, or be plain text.\n        Never ship a link to a section that does not exist.\n\n  · NO SCAFFOLDING IN THE OUTPUT. Every visible string must read as finished site copy.\n    Never render text like \"Neutral placeholder text for the descriptive paragraph.\",\n    \"One-line footer text placeholder.\", \"Wordmark\", or any sentence that addresses the\n    reviewer instead of a visitor. If the brief gives no copy, write short neutral marketing\n    copy that a real site could ship — never a description of what the copy would be.\n    Do not rephrase the brief's own \"Purpose\" paragraph as page copy.\n\n  · Write copy for the audience the brief names. If it is a personal brand or freelancer,\n    do not use agency voice (\"our clients\", \"the brands we serve\").\n\n  · Do not add perpetual motion the brief did not ask for. If the brief says motion happens\n    on scroll entry, do not also run an infinite rAF loop.\n  · Rebuild every animation it asks for with CSS or a small rAF loop. Nothing may be\n    imported.\n  · No external media: no images, no video, no icon fonts. Any icon or illustration must be\n    inline SVG you draw yourself. Any \"product mockup\" or \"screenshot\" must be built from\n    CSS/SVG shapes, not an image.\n  · Responsive at 390 / 768 / 1440 / 1920 with no horizontal overflow.\n  · Accessibility: exactly one <h1>, real header/main/footer landmarks, every visible\n    control has an accessible name, visible :focus-visible ring.\n  · A full prefers-reduced-motion: reduce block that leaves the page static and readable.\n  · window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] } —\n    set ready after first paint, increment frames inside the rAF loop, push effect names,\n    and push any window 'error' into errors.\n  · Under 90 KB. Zero console output. No uncaught exceptions.\n\n--- THE JIRO SECTION PROMPT (the brief you are adapting) ---\n\nBuild an About us section for a modern website: \"About Us 01 Orbit\".\n\nPurpose\nIntroduce a coach as a real person and make the case for booking a consultation. It speaks to prospective clients of personal coaching, mentoring and one-on-one services who need to trust the practitioner first.\n\nLayout & content\n- A primary column anchored by a professional headshot of the coach.\n- A value proposition block: short headline plus one paragraph stating who the coach helps and how they work.\n- Supporting proof points that reinforce the value proposition.\n- Consultation booking calls to action, worded as a direct invitation to book time.\n\nVisual direction\nLight and minimal, with generous whitespace, restrained type and a calm editorial rhythm. Keep surfaces clean and let the headshot carry the warmth; nothing should read as loud or salesy.\n\nInteraction & motion\nThe headshot and copy fade and rise into place on scroll, with the call to action settling last. Use Framer Motion for short, subtle entrances only.\n\nStack\n- React functional component + Tailwind CSS.\n- npm install framer-motion\n\nConstraints\n- Match the described layout and content faithfully.\n- Do not invent statistics, testimonials, prices, brand names or section counts.\n- Use neutral placeholder copy where the description supplies no real text.\n\n--- END JIRO PROMPT ---\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the section's own structure is present (headings, cards, panels as the brief describes)","exactly one <h1> and real header/main/footer landmarks","no external media: no img src, no video, no remote URL","no horizontal overflow at 390 / 768 / 1440","prefers-reduced-motion leaves the page static and readable","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"],"usage":{"build_tokens":43808,"build_prompt_tokens":2041,"build_completion_tokens":41767,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":724824,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":0,"total_tokens":43808},"verification":{"passed":true,"passed_checks":17,"total_checks":17,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"37478 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"4 frames, effects: headshot-fade-rise, copy-fade-rise, proof-points-fade-rise, consultation-cta-fade-rise"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"57 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Support for your next clear step.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"About Us"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=843 pageErrors=0"}]},"crosscheck":null},{"slug":"jiro-about-us-02-orbit","title":"About Us 02 Orbit","category":"Section","effect":"A personal brand About section with portfolio imagery, achievement stats, and narrative storytelling layout. Built for creators, freelancers, and personal brand websites.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/jiro-about-us-02-orbit/index.html","doc":"/prompts/jiro-about-us-02-orbit.md","prompt_text":"ADAPT THIS JIRO SECTION PROMPT INTO A STANDALONE VANILLA PAGE.\n\nThe text below is a real prompt from another prompt gallery. It describes a website\nSECTION — a \"About us\" block called \"About Us 02 Orbit\". It is written for React + Tailwind +\nframer-motion. You are NOT building that. You are rebuilding the SAME section, with the\nsame intent, structure and content, under our contract:\n\n  · ONE self-contained index.html. No React, no Tailwind, no framer-motion, no lucide-react,\n    no build step, no dependencies at all.\n  · The section is the whole page. Put it on a page with minimal chrome: a slim header with\n    a wordmark and two nav links, then the section as the dominant content, then a one-line\n    footer. Nothing else. No hero, no pricing, no extra sections.\n  · Honour the section's OWN structure and content faithfully. If it asks for a heading, a\n    grid of cards, a mockup panel and badges, build exactly those things. Do not add or\n    remove parts.\n\n  · NO INVENTED FACTS. This is the rule reviewers fail these pages on most often:\n      - Never invent statistics. If the brief implies a stats row but supplies no numbers,\n        render the figures as em-dashes or \"XX\" — NEVER as plausible-looking values like\n        100 / 200 / 24 / 4.2M. A number that looks real is a fabricated claim.\n      - Never invent a brand name. If the brief's title contains a codename (\"About Us 02\n        Orbit\", \"Pricing 01 Kelo\"), that is a FILE name, not a company. Use a neutral\n        wordmark such as \"Studio\" or \"Company\" — do not promote the codename into a brand,\n        and do not put it in the <title> or a © line.\n      - Never invent testimonials, prices, customer names, awards, dates or copyright years.\n        A © line should read \"© Company\" with no year, or be omitted.\n      - Do not assert a stat category the brief never supplied (no \"Industry Awards\").\n      - Nav and footer links must point at real anchors inside this page, or be plain text.\n        Never ship a link to a section that does not exist.\n\n  · NO SCAFFOLDING IN THE OUTPUT. Every visible string must read as finished site copy.\n    Never render text like \"Neutral placeholder text for the descriptive paragraph.\",\n    \"One-line footer text placeholder.\", \"Wordmark\", or any sentence that addresses the\n    reviewer instead of a visitor. If the brief gives no copy, write short neutral marketing\n    copy that a real site could ship — never a description of what the copy would be.\n    Do not rephrase the brief's own \"Purpose\" paragraph as page copy.\n\n  · Write copy for the audience the brief names. If it is a personal brand or freelancer,\n    do not use agency voice (\"our clients\", \"the brands we serve\").\n\n  · Do not add perpetual motion the brief did not ask for. If the brief says motion happens\n    on scroll entry, do not also run an infinite rAF loop.\n  · Rebuild every animation it asks for with CSS or a small rAF loop. Nothing may be\n    imported.\n  · No external media: no images, no video, no icon fonts. Any icon or illustration must be\n    inline SVG you draw yourself. Any \"product mockup\" or \"screenshot\" must be built from\n    CSS/SVG shapes, not an image.\n  · Responsive at 390 / 768 / 1440 / 1920 with no horizontal overflow.\n  · Accessibility: exactly one <h1>, real header/main/footer landmarks, every visible\n    control has an accessible name, visible :focus-visible ring.\n  · A full prefers-reduced-motion: reduce block that leaves the page static and readable.\n  · window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] } —\n    set ready after first paint, increment frames inside the rAF loop, push effect names,\n    and push any window 'error' into errors.\n  · Under 90 KB. Zero console output. No uncaught exceptions.\n\n--- THE JIRO SECTION PROMPT (the brief you are adapting) ---\n\nBuild an About us section for a modern website: \"About Us 02 Orbit\".\n\nPurpose\nIntroduce a personal brand through portfolio imagery and a narrative story, so visitors understand who is behind the work and what has been achieved. Written for creators, freelancers, and personal brand websites.\n\nLayout & content\n- An about block combining a short narrative story with supporting portfolio imagery.\n- An achievement stats row that surfaces key numbers alongside the story.\n- Image and text arranged so the storytelling and the visuals read as one section.\n- Keep the copy structural: no real names, numbers, or quotes.\n\nVisual direction\nLight, minimal and modern: generous white space, restrained type, and imagery treated as the hero content rather than decoration. Stats should feel quiet and editorial, not loud.\n\nInteraction & motion\nAnimate the section on scroll entry with Framer Motion: fade and rise for the narrative and stats, and a gentle reveal for the portfolio images. Keep movement subtle and restrained.\n\nStack\n- React functional component + Tailwind CSS.\n- framer-motion for entrance and reveal animations.\n\nConstraints\n- Match the described layout and content faithfully.\n- Do not invent statistics, testimonials, prices, brand names or section counts.\n- Use neutral placeholder copy where the description supplies no real text.\n\n--- END JIRO PROMPT ---\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the section's own structure is present (headings, cards, panels as the brief describes)","exactly one <h1> and real header/main/footer landmarks","no external media: no img src, no video, no remote URL","no horizontal overflow at 390 / 768 / 1440","prefers-reduced-motion leaves the page static and readable","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"],"usage":{"build_tokens":18737,"build_prompt_tokens":1571,"build_completion_tokens":17166,"build_reasoning_tokens":13820,"build_calls":1,"build_attempts":1,"build_latency_ms":172252,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":0,"total_tokens":18737},"verification":{"passed":true,"passed_checks":17,"total_checks":17,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"11168 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"3 frames, effects: scroll-reveal, orbit-animation"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"45 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"About Us\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"About Us 02 Orbit"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=508 pageErrors=0"}]},"crosscheck":null},{"slug":"jiro-about-us-03-moxo","title":"About Us 03 Moxo","category":"Section","effect":"A brand About section with mission narrative, team highlight cards, and culture-driven imagery. Designed for SaaS brands, tech companies, and B2B product sites.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/jiro-about-us-03-moxo/index.html","doc":"/prompts/jiro-about-us-03-moxo.md","prompt_text":"ADAPT THIS JIRO SECTION PROMPT INTO A STANDALONE VANILLA PAGE.\n\nThe text below is a real prompt from another prompt gallery. It describes a website\nSECTION — a \"About us\" block called \"About Us 03 Moxo\". It is written for React + Tailwind +\nframer-motion. You are NOT building that. You are rebuilding the SAME section, with the\nsame intent, structure and content, under our contract:\n\n  · ONE self-contained index.html. No React, no Tailwind, no framer-motion, no lucide-react,\n    no build step, no dependencies at all.\n  · The section is the whole page. Put it on a page with minimal chrome: a slim header with\n    a wordmark and two nav links, then the section as the dominant content, then a one-line\n    footer. Nothing else. No hero, no pricing, no extra sections.\n  · Honour the section's OWN structure and content faithfully. If it asks for a heading, a\n    grid of cards, a mockup panel and badges, build exactly those things. Do not add or\n    remove parts.\n\n  · NO INVENTED FACTS. This is the rule reviewers fail these pages on most often:\n      - Never invent statistics. If the brief implies a stats row but supplies no numbers,\n        render the figures as em-dashes or \"XX\" — NEVER as plausible-looking values like\n        100 / 200 / 24 / 4.2M. A number that looks real is a fabricated claim.\n      - Never invent a brand name. If the brief's title contains a codename (\"About Us 02\n        Orbit\", \"Pricing 01 Kelo\"), that is a FILE name, not a company. Use a neutral\n        wordmark such as \"Studio\" or \"Company\" — do not promote the codename into a brand,\n        and do not put it in the <title> or a © line.\n      - Never invent testimonials, prices, customer names, awards, dates or copyright years.\n        A © line should read \"© Company\" with no year, or be omitted.\n      - Do not assert a stat category the brief never supplied (no \"Industry Awards\").\n      - Nav and footer links must point at real anchors inside this page, or be plain text.\n        Never ship a link to a section that does not exist.\n\n  · NO SCAFFOLDING IN THE OUTPUT. Every visible string must read as finished site copy.\n    Never render text like \"Neutral placeholder text for the descriptive paragraph.\",\n    \"One-line footer text placeholder.\", \"Wordmark\", or any sentence that addresses the\n    reviewer instead of a visitor. If the brief gives no copy, write short neutral marketing\n    copy that a real site could ship — never a description of what the copy would be.\n    Do not rephrase the brief's own \"Purpose\" paragraph as page copy.\n\n  · Write copy for the audience the brief names. If it is a personal brand or freelancer,\n    do not use agency voice (\"our clients\", \"the brands we serve\").\n\n  · Do not add perpetual motion the brief did not ask for. If the brief says motion happens\n    on scroll entry, do not also run an infinite rAF loop.\n  · Rebuild every animation it asks for with CSS or a small rAF loop. Nothing may be\n    imported.\n  · No external media: no images, no video, no icon fonts. Any icon or illustration must be\n    inline SVG you draw yourself. Any \"product mockup\" or \"screenshot\" must be built from\n    CSS/SVG shapes, not an image.\n  · Responsive at 390 / 768 / 1440 / 1920 with no horizontal overflow.\n  · Accessibility: exactly one <h1>, real header/main/footer landmarks, every visible\n    control has an accessible name, visible :focus-visible ring.\n  · A full prefers-reduced-motion: reduce block that leaves the page static and readable.\n  · window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] } —\n    set ready after first paint, increment frames inside the rAF loop, push effect names,\n    and push any window 'error' into errors.\n  · Under 90 KB. Zero console output. No uncaught exceptions.\n\n--- THE JIRO SECTION PROMPT (the brief you are adapting) ---\n\nBuild an About us section for a modern website: \"About Us 03 Moxo\".\n\nPurpose\nThis section tells a brand's story to prospective customers of a SaaS or tech company. It should establish why the company exists, what it stands for, and who is behind it, so a B2B visitor can form a human impression before evaluating the product.\n\nLayout & content\n- A mission narrative block that opens the section.\n- Team highlight cards introducing the people behind the brand.\n- Culture-driven imagery supporting the narrative.\n- A restrained call to action or contact prompt at the end.\n\nVisual direction\nGo dark and elegant, with photography as the primary visual material. Keep the palette restrained and the type refined so the mission copy reads as considered rather than promotional.\n\nInteraction & motion\nUse framer-motion for gentle reveal and hover motion on the narrative block and team cards; keep it soft and editorial rather than flashy.\n\nStack\n- React functional component + Tailwind CSS.\n- framer-motion for animation; lucide-react for icons.\n\nConstraints\n- Match the described layout and content faithfully.\n- Do not invent statistics, testimonials, prices, brand names or section counts.\n- Use neutral placeholder copy where the description supplies no real text.\n\n--- END JIRO PROMPT ---\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the section's own structure is present (headings, cards, panels as the brief describes)","exactly one <h1> and real header/main/footer landmarks","no external media: no img src, no video, no remote URL","no horizontal overflow at 390 / 768 / 1440","prefers-reduced-motion leaves the page static and readable","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"],"usage":{"build_tokens":30294,"build_prompt_tokens":1620,"build_completion_tokens":28674,"build_reasoning_tokens":0,"build_calls":1,"build_attempts":1,"build_latency_ms":337932,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":0,"total_tokens":30294},"verification":{"passed":true,"passed_checks":17,"total_checks":17,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"38711 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"4 frames, effects: mission-reveal, staggered-team-reveal, editorial-card-hover, narrative-link-hover"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"73 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Technology with a human point of view.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"About Us 03 Moxo"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=1084 pageErrors=0"}]},"crosscheck":null},{"slug":"jiro-about-us-03-orbit","title":"About Us 03 Orbit","category":"Section","effect":"An educator About section with course catalog preview, teacher bio, and enrollment-focused layout. Suited for online instructors, training platforms, and education brands.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/jiro-about-us-03-orbit/index.html","doc":"/prompts/jiro-about-us-03-orbit.md","prompt_text":"ADAPT THIS JIRO SECTION PROMPT INTO A STANDALONE VANILLA PAGE.\n\nThe text below is a real prompt from another prompt gallery. It describes a website\nSECTION — a \"About us\" block called \"About Us 03 Orbit\". It is written for React + Tailwind +\nframer-motion. You are NOT building that. You are rebuilding the SAME section, with the\nsame intent, structure and content, under our contract:\n\n  · ONE self-contained index.html. No React, no Tailwind, no framer-motion, no lucide-react,\n    no build step, no dependencies at all.\n  · The section is the whole page. Put it on a page with minimal chrome: a slim header with\n    a wordmark and two nav links, then the section as the dominant content, then a one-line\n    footer. Nothing else. No hero, no pricing, no extra sections.\n  · Honour the section's OWN structure and content faithfully. If it asks for a heading, a\n    grid of cards, a mockup panel and badges, build exactly those things. Do not add or\n    remove parts.\n\n  · NO INVENTED FACTS. This is the rule reviewers fail these pages on most often:\n      - Never invent statistics. If the brief implies a stats row but supplies no numbers,\n        render the figures as em-dashes or \"XX\" — NEVER as plausible-looking values like\n        100 / 200 / 24 / 4.2M. A number that looks real is a fabricated claim.\n      - Never invent a brand name. If the brief's title contains a codename (\"About Us 02\n        Orbit\", \"Pricing 01 Kelo\"), that is a FILE name, not a company. Use a neutral\n        wordmark such as \"Studio\" or \"Company\" — do not promote the codename into a brand,\n        and do not put it in the <title> or a © line.\n      - Never invent testimonials, prices, customer names, awards, dates or copyright years.\n        A © line should read \"© Company\" with no year, or be omitted.\n      - Do not assert a stat category the brief never supplied (no \"Industry Awards\").\n      - Nav and footer links must point at real anchors inside this page, or be plain text.\n        Never ship a link to a section that does not exist.\n\n  · NO SCAFFOLDING IN THE OUTPUT. Every visible string must read as finished site copy.\n    Never render text like \"Neutral placeholder text for the descriptive paragraph.\",\n    \"One-line footer text placeholder.\", \"Wordmark\", or any sentence that addresses the\n    reviewer instead of a visitor. If the brief gives no copy, write short neutral marketing\n    copy that a real site could ship — never a description of what the copy would be.\n    Do not rephrase the brief's own \"Purpose\" paragraph as page copy.\n\n  · Write copy for the audience the brief names. If it is a personal brand or freelancer,\n    do not use agency voice (\"our clients\", \"the brands we serve\").\n\n  · Do not add perpetual motion the brief did not ask for. If the brief says motion happens\n    on scroll entry, do not also run an infinite rAF loop.\n  · Rebuild every animation it asks for with CSS or a small rAF loop. Nothing may be\n    imported.\n  · No external media: no images, no video, no icon fonts. Any icon or illustration must be\n    inline SVG you draw yourself. Any \"product mockup\" or \"screenshot\" must be built from\n    CSS/SVG shapes, not an image.\n  · Responsive at 390 / 768 / 1440 / 1920 with no horizontal overflow.\n  · Accessibility: exactly one <h1>, real header/main/footer landmarks, every visible\n    control has an accessible name, visible :focus-visible ring.\n  · A full prefers-reduced-motion: reduce block that leaves the page static and readable.\n  · window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] } —\n    set ready after first paint, increment frames inside the rAF loop, push effect names,\n    and push any window 'error' into errors.\n  · Under 90 KB. Zero console output. No uncaught exceptions.\n\n--- THE JIRO SECTION PROMPT (the brief you are adapting) ---\n\nBuild an About us section for a modern website: \"About Us 03 Orbit\".\n\nPurpose\nIntroduce an educator and their teaching offer: who is behind the program, what can be learned, and how to enrol. Written for online instructors, training platforms and education brands.\n\nLayout & content\n- A light, minimal intro with a short mission statement and a supporting line about the teacher.\n- A course catalog preview: course cards, each with a title, a brief topic line and a neutral call to view it.\n- A teacher bio block pairing portrait imagery with a short biography and credentials.\n- An enrolment-focused closing area with a single clear call to action.\n- Keep the ordering readable: mission, catalog, teacher, enrol.\n\nVisual direction\nLight and minimal with generous whitespace, calm type scale and playful touches in the small details. Photography is used for the teacher and course thumbnails; avatars appear where people are referenced.\n\nInteraction & motion\nSections fade and rise into view with Framer Motion, and course cards lift subtly on hover. Motion stays gentle and human.\n\nStack\n- React functional component + Tailwind CSS.\n- framer-motion\n\nConstraints\n- Match the described layout and content faithfully.\n- Do not invent statistics, testimonials, prices, brand names or section counts.\n- Use neutral placeholder copy where the description supplies no real text.\n\n--- END JIRO PROMPT ---\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the section's own structure is present (headings, cards, panels as the brief describes)","exactly one <h1> and real header/main/footer landmarks","no external media: no img src, no video, no remote URL","no horizontal overflow at 390 / 768 / 1440","prefers-reduced-motion leaves the page static and readable","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"],"usage":{"build_tokens":25708,"build_prompt_tokens":1587,"build_completion_tokens":24121,"build_reasoning_tokens":18603,"build_calls":1,"build_attempts":1,"build_latency_ms":221177,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":0,"total_tokens":25708},"verification":{"passed":true,"passed_checks":17,"total_checks":17,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"21137 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"11 frames, effects: fade-rise, hover-lift, orbit"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"72 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Master your creative craft.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"About Us 03 Orbit"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=1140 pageErrors=0"}]},"crosscheck":null},{"slug":"jiro-about-us-04-orbit","title":"About Us 04 Orbit","category":"Section","effect":"A consultant About section with professional imagery, expertise badges, and engagement-focused CTAs. Built for consultants, advisors, and B2B service professionals.","platform":"openrouter","platform_label":"openrouter","model":"stealth/space-bunny-alpha","model_label":"Space Bunny Alpha (stealth/space-bunny-alpha)","demo":"/demos/jiro-about-us-04-orbit/index.html","doc":"/prompts/jiro-about-us-04-orbit.md","prompt_text":"ADAPT THIS JIRO SECTION PROMPT INTO A STANDALONE VANILLA PAGE.\n\nThe text below is a real prompt from another prompt gallery. It describes a website\nSECTION — a \"About us\" block called \"About Us 04 Orbit\". It is written for React + Tailwind +\nframer-motion. You are NOT building that. You are rebuilding the SAME section, with the\nsame intent, structure and content, under our contract:\n\n  · ONE self-contained index.html. No React, no Tailwind, no framer-motion, no lucide-react,\n    no build step, no dependencies at all.\n  · The section is the whole page. Put it on a page with minimal chrome: a slim header with\n    a wordmark and two nav links, then the section as the dominant content, then a one-line\n    footer. Nothing else. No hero, no pricing, no extra sections.\n  · Honour the section's OWN structure and content faithfully. If it asks for a heading, a\n    grid of cards, a mockup panel and badges, build exactly those things. Do not add or\n    remove parts.\n\n  · NO INVENTED FACTS. This is the rule reviewers fail these pages on most often:\n      - Never invent statistics. If the brief implies a stats row but supplies no numbers,\n        render the figures as em-dashes or \"XX\" — NEVER as plausible-looking values like\n        100 / 200 / 24 / 4.2M. A number that looks real is a fabricated claim.\n      - Never invent a brand name. If the brief's title contains a codename (\"About Us 02\n        Orbit\", \"Pricing 01 Kelo\"), that is a FILE name, not a company. Use a neutral\n        wordmark such as \"Studio\" or \"Company\" — do not promote the codename into a brand,\n        and do not put it in the <title> or a © line.\n      - Never invent testimonials, prices, customer names, awards, dates or copyright years.\n        A © line should read \"© Company\" with no year, or be omitted.\n      - Do not assert a stat category the brief never supplied (no \"Industry Awards\").\n      - Nav and footer links must point at real anchors inside this page, or be plain text.\n        Never ship a link to a section that does not exist.\n\n  · NO SCAFFOLDING IN THE OUTPUT. Every visible string must read as finished site copy.\n    Never render text like \"Neutral placeholder text for the descriptive paragraph.\",\n    \"One-line footer text placeholder.\", \"Wordmark\", or any sentence that addresses the\n    reviewer instead of a visitor. If the brief gives no copy, write short neutral marketing\n    copy that a real site could ship — never a description of what the copy would be.\n    Do not rephrase the brief's own \"Purpose\" paragraph as page copy.\n\n  · Write copy for the audience the brief names. If it is a personal brand or freelancer,\n    do not use agency voice (\"our clients\", \"the brands we serve\").\n\n  · Do not add perpetual motion the brief did not ask for. If the brief says motion happens\n    on scroll entry, do not also run an infinite rAF loop.\n  · Rebuild every animation it asks for with CSS or a small rAF loop. Nothing may be\n    imported.\n  · No external media: no images, no video, no icon fonts. Any icon or illustration must be\n    inline SVG you draw yourself. Any \"product mockup\" or \"screenshot\" must be built from\n    CSS/SVG shapes, not an image.\n  · Responsive at 390 / 768 / 1440 / 1920 with no horizontal overflow.\n  · Accessibility: exactly one <h1>, real header/main/footer landmarks, every visible\n    control has an accessible name, visible :focus-visible ring.\n  · A full prefers-reduced-motion: reduce block that leaves the page static and readable.\n  · window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] } —\n    set ready after first paint, increment frames inside the rAF loop, push effect names,\n    and push any window 'error' into errors.\n  · Under 90 KB. Zero console output. No uncaught exceptions.\n\n--- THE JIRO SECTION PROMPT (the brief you are adapting) ---\n\nBuild an About us section for a modern website: \"About Us 04 Orbit\".\n\nPurpose\nPresent a consultant or advisory practice in a way that reads as experienced and approachable. The section should establish professional credibility, spell out areas of expertise, and give visitors an obvious next step toward working together.\n\nLayout & content\n- Professional imagery presented alongside the section's narrative copy.\n- A row of expertise badges summarising the practice areas.\n- One or more engagement-focused calls to action inviting a conversation.\n- Use large type for the headline so the section reads as confident rather than dense.\n\nVisual direction\nLight, minimal and typographic. Large headline type should dominate, with imagery and badges kept restrained so the section stays calm for a B2B services audience.\n\nInteraction & motion\nUse Framer Motion for a gentle entrance animation as the section scrolls into view. Keep it to a single reveal pass; no looping motion.\n\nStack\n- React functional component + Tailwind CSS.\n- framer-motion.\n\nConstraints\n- Match the described layout and content faithfully.\n- Do not invent statistics, testimonials, prices, brand names or section counts.\n- Use neutral placeholder copy where the description supplies no real text.\n\n--- END JIRO PROMPT ---\n\n---\n\nTECHNICAL CONTRACT (non-negotiable):\n\n1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,\n   no framework, no package install. It must open correctly by double-clicking the file\n   and also over http://.\n2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.\n   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.\n3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of\n   photos. Every visual must be generated procedurally with CSS gradients, SVG, or\n   <canvas>. This keeps the page free to host and impossible to break with a dead link.\n4. Google Fonts is the ONLY permitted network request, and it must be declared with a\n   <link rel=\"preconnect\"> plus a system-font fallback stack so the page still looks\n   intentional offline. If you prefer, skip it and use a pure system stack.\n5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).\n   No horizontal scrollbar at any of those widths. Nothing may overflow its container.\n6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every\n   interactive element is a <button> or <a> with an accessible name; visible :focus-visible\n   ring; body text contrast ratio >= 4.5:1 against its background.\n7. Motion safety: wrap all continuous motion in\n   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or\n   near-static state. The page must remain fully readable and usable with motion off.\n8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single\n   requestAnimationFrame loop, use transform/opacity for animation, and never allocate\n   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.\n9. Runtime telemetry hook (used by the automated verifier — implement it exactly):\n   at the top of your script define\n     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };\n   then set `ready = true` after the first paint of your main effect, increment\n   `frames` once per animation frame inside your rAF loop, and push the name of each\n   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`\n   handler that pushes messages into `errors`. Do not rename these fields.\n10. Zero console output in the happy path. No console.log left behind. No uncaught\n    exceptions, no failed network requests.\n\nRESPONSE FORMAT:\nReturn ONLY the raw HTML inside a single ```html fenced code block. No explanation, no\npreamble, no commentary before or after the fence. The file must be complete and\nimmediately runnable — never truncate, never write \"// rest of code here\".","acceptance":["the section's own structure is present (headings, cards, panels as the brief describes)","exactly one <h1> and real header/main/footer landmarks","no external media: no img src, no video, no remote URL","no horizontal overflow at 390 / 768 / 1440","prefers-reduced-motion leaves the page static and readable","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"],"usage":{"build_tokens":19496,"build_prompt_tokens":1972,"build_completion_tokens":17524,"build_reasoning_tokens":15011,"build_calls":1,"build_attempts":1,"build_latency_ms":164891,"wasted_tokens":0,"repair_tokens":0,"crosscheck_tokens":0,"total_tokens":19496},"verification":{"passed":true,"passed_checks":17,"total_checks":17,"checks":[{"name":"demo file exists and is non-trivial","pass":true,"detail":"7505 bytes"},{"name":"window.__DEMO__.ready became true","pass":true,"detail":"14 frames, effects: scroll-reveal"},{"name":"animation loop is live (>= 20 frames / 1.2s)","pass":true,"detail":"72 frames in 1.2s"},{"name":"window.__DEMO__.errors empty","pass":true,"detail":"clean"},{"name":"canvas present (optional)","pass":true,"detail":"no canvas in this design"},{"name":"exactly one <h1> with text","pass":true,"detail":"\"Guidance for the modern enterprise.\""},{"name":"header/main/footer landmarks present","pass":true,"detail":"header,main,footer"},{"name":"interactive elements have accessible names","pass":true,"detail":"0 unnamed"},{"name":"document has <title>","pass":true,"detail":"Consultancy"},{"name":"document declares lang","pass":true,"detail":"en"},{"name":"no horizontal overflow @ 390px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 768px","pass":true,"detail":"clean"},{"name":"no horizontal overflow @ 1440px","pass":true,"detail":"clean"},{"name":"no console errors","pass":true,"detail":"clean"},{"name":"no uncaught page errors","pass":true,"detail":"clean"},{"name":"no failed network requests","pass":true,"detail":"clean"},{"name":"reduced-motion: page still renders content","pass":true,"detail":"h1=1 textLen=374 pageErrors=0"}]},"crosscheck":null}]}