# 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:** cmd-code (`openrouter`)
> **Model:** `stealth/space-bunny-alpha` — Space Bunny Alpha (stealth/space-bunny-alpha)
> **License:** Free / MIT — copy, paste, ship.
> **Self-contained:** yes — one `index.html`, no build step, no external media.

---

## 🎬 Live demo

Open [`public/demos/filament/index.html`](../public/demos/filament/index.html) — generated by the model
above from the prompt below, with no human edits to the markup.

## ✅ Verification

<!-- VERIFICATION:START -->
_Pending — this block is written by `tools/verify.mjs` after the build run._
<!-- VERIFICATION:END -->

## 📋 Prompt

```text
A. WHAT IS BEING BUILT
You 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.

B. THE BACKDROP
The 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.

C. THE LAYER STACK
The stacking order from back to front is exactly:
z-index 0: div.vignette (fixed, full viewport, near-black base and faint accent glow)
z-index 1: canvas#dust (fixed, full viewport, 64 ember particles at half resolution)
z-index 2: div.envelope (the glass sphere, absolutely centred, size var(--bulb-size))
z-index 3: div.filament-wrap (absolutely centred inside the bulb footprint; contains the SVG)
  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.
  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.
  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.
z-index 4: main (contains the hero h1, tagline, and button; text is lit by the falloff)
z-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.
All decorative bulb elements have aria-hidden="true" so they are not in the accessibility tree.

D. A TOKEN TABLE
:root {
  --bg: #050505;
  --accent: #fbbf24;
  --accent-bright: #fde68a;
  --accent-dim: #b45309;
  --text: #f5f5f4;
  --text-dim: #a8a29e;
  --glass-rim: rgba(251,191,36,0.25);
  --glass-glow: rgba(251,191,36,0.12);
  --font-sans: 'Inter', system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --h1-size: clamp(3rem, 8vw, 7rem);
  --h1-weight: 600;
  --h1-tracking: -0.04em;
  --body-size: clamp(1rem, 1.2vw, 1.125rem);
  --btn-py: 0.875rem;
  --btn-px: 1.75rem;
  --btn-radius: 999px;
  --nav-gap: 2rem;
  --pad-x: clamp(1.5rem, 5vw, 4rem);
  --pad-y: clamp(1.5rem, 4vh, 3rem);
  --bulb-size: clamp(280px, 40vmin, 480px);
  --wire-width: 2px;
  --blur-shimmer: 10px;
  --track-gap: 2rem;
}
Every 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).

E. THE COMPOSITION
The 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).
The 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)).
The 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.

F. THE UI LANGUAGE
The 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.

G. MOTION
The entrance stagger table is:
span.brand -> animation fade-slide-down, delay 0ms, duration 600ms, ease-out, fill both
nav a:nth-child(1) -> animation fade-slide-down, delay 80ms, duration 600ms, ease-out, fill both
nav a:nth-child(2) -> animation fade-slide-down, delay 140ms, duration 600ms, ease-out, fill both
h1 -> animation fade-slide-up, delay 200ms, duration 700ms, ease-out, fill both
p.tagline -> animation fade-slide-up, delay 320ms, duration 700ms, ease-out, fill both
a.btn -> animation fade-slide-up, delay 440ms, duration 700ms, ease-out, fill both
footer -> animation fade-slide-up, delay 560ms, duration 700ms, ease-out, fill both
These 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:
@keyframes fade-slide-down { from { opacity:0; transform:translateY(-8px); } to { opacity:1; transform:translateY(0); } }
@keyframes fade-slide-up { from { opacity:0; transform:translateY(12px); } to { opacity:1; transform:translateY(0); } }
The permanent ambient motion is:
div.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.
svg.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).
canvas#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".

H. A RESPONSIVE TABLE
At 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.
At 768x1024 (tablet portrait): --pad-x is 2rem, --bulb-size is 300px, --h1-size is clamp(3rem, 9vw, 5rem), body scrolls vertically.
At 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.
At 1440x900 and 1920x1080 (large desktop): --bulb-size is 480px, --h1-size is 7rem, --pad-x is 4rem, and the footer status pill remains visible.
Max-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.
Max-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.
No 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.

I. FAILURE GUARDS
Resting 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.

J. THE EXACT JS
Place the following script at the end of body. It uses no template literals and no external libraries.
window.__DEMO__ = { name: 'filament', ready: false, frames: 0, effects: [], errors: [] };
window.addEventListener('error', function(e) { window.__DEMO__.errors.push(String((e && (e.message || e.error)) || 'unknown')); });
(function() {
  var reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  var canvas = document.getElementById('dust');
  var ctx = canvas.getContext('2d');
  var w = 0, h = 0;
  var dot = document.createElement('canvas');
  dot.width = 16; dot.height = 16;
  var dctx = dot.getContext('2d');
  var g = dctx.createRadialGradient(8,8,0,8,8,8);
  g.addColorStop(0,'rgba(251,191,36,0.9)');
  g.addColorStop(1,'rgba(251,191,36,0)');
  dctx.fillStyle = g;
  dctx.fillRect(0,0,16,16);
  var particles = [];
  for (var i=0;i<64;i++) {
    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 });
  }
  function resize() {
    w = Math.floor(window.innerWidth / 2);
    h = Math.floor(window.innerHeight / 2);
    canvas.width = w;
    canvas.height = h;
  }
  window.addEventListener('resize', resize);
  resize();
  var shimmer = document.getElementById('shimmer');
  var glow = document.getElementById('wire-glow');
  function frame(t) {
    window.__DEMO__.frames++;
    if (!reduce) {
      ctx.clearRect(0,0,w,h);
      for (var i=0;i<64;i++) {
        var p = particles[i];
        var x = (p.bx * w + Math.sin(t * p.sp + p.ph) * 24) | 0;
        var y = (p.by * h + Math.cos(t * p.sp * 0.8 + p.ph) * 24) | 0;
        ctx.drawImage(dot, x, y, 16 * p.sz, 16 * p.sz);
      }
      var shX = Math.sin(t * 0.0011) * 3;
      var shY = Math.cos(t * 0.0014) * 2;
      shimmer.style.transform = 'translate(' + shX.toFixed(2) + 'px,' + shY.toFixed(2) + 'px)';
      var op = 0.55 + Math.sin(t * 0.0018) * 0.35;
      glow.style.opacity = op.toFixed(3);
    }
    requestAnimationFrame(frame);
  }
  requestAnimationFrame(function() {
    requestAnimationFrame(function() {
      window.__DEMO__.ready = true;
      document.body.classList.add('animate');
      if (!reduce) {
        window.__DEMO__.effects.push('canvas-dust');
        window.__DEMO__.effects.push('filament-glow');
        window.__DEMO__.effects.push('shimmer-drift');
      }
    });
  });
  if (!reduce) {
    requestAnimationFrame(frame);
  }
})();


---

TECHNICAL CONTRACT (non-negotiable):

1. Deliver ONE single file: a complete, valid `index.html`. No build step, no bundler,
   no framework, no package install. It must open correctly by double-clicking the file
   and also over http://.
2. Vanilla only. No React, no Tailwind CDN, no GSAP, no Three.js, no jQuery, no Lottie.
   Plain HTML + CSS + modern JavaScript in <style> and <script> tags.
3. NO external media. No .mp4, no .webm, no .gif, no remote images, no base64 blobs of
   photos. Every visual must be generated procedurally with CSS gradients, SVG, or
   <canvas>. This keeps the page free to host and impossible to break with a dead link.
4. Google Fonts is the ONLY permitted network request, and it must be declared with a
   <link rel="preconnect"> plus a system-font fallback stack so the page still looks
   intentional offline. If you prefer, skip it and use a pure system stack.
5. Responsive and correct at 390x844 (phone), 768x1024 (tablet) and 1440x900 (desktop).
   No horizontal scrollbar at any of those widths. Nothing may overflow its container.
6. Accessibility: exactly one <h1>; real <header>/<main>/<footer>/<nav> landmarks; every
   interactive element is a <button> or <a> with an accessible name; visible :focus-visible
   ring; body text contrast ratio >= 4.5:1 against its background.
7. Motion safety: wrap all continuous motion in
   `@media (prefers-reduced-motion: reduce)` rules that reduce it to a static or
   near-static state. The page must remain fully readable and usable with motion off.
8. Performance: no layout thrash. Cache DOM reads, batch writes inside a single
   requestAnimationFrame loop, use transform/opacity for animation, and never allocate
   inside the hot loop. Target 60fps on a 2019 laptop. Total file under 90 KB.
9. Runtime telemetry hook (used by the automated verifier — implement it exactly):
   at the top of your script define
     window.__DEMO__ = { name: '<slug>', ready: false, frames: 0, effects: [], errors: [] };
   then set `ready = true` after the first paint of your main effect, increment
   `frames` once per animation frame inside your rAF loop, and push the name of each
   implemented effect into `effects`. Add a `window.addEventListener('error', ...)`
   handler that pushes messages into `errors`. Do not rename these fields.
10. Zero console output in the happy path. No console.log left behind. No uncaught
    exceptions, no failed network requests.

RESPONSE FORMAT:
Return ONLY the raw HTML inside a single ```html fenced code block. No explanation, no
preamble, no commentary before or after the fence. The file must be complete and
immediately runnable — never truncate, never write "// rest of code here".
```

---

*LandingPagePrompts Prompt Library — free, verified, model-attributed.*
