# The Build Kit — Blueprint

**A complete, copy-anything blueprint for a cinematic, AI-built web "kit": a living library of motion and type, where the page itself is the demo.**

You are reading the source recipe for a site that was built entirely by *describing it* to an AI — no hand-written code. This document hands that same starting point to you. Drop it into your AI (Claude, ChatGPT, Gemini, whatever you use), tell it what you want to make, and let it build *your* version.

> **How to use this file:** paste it into an AI chat and say something like *"Read this blueprint. I want to build my own version about \_\_\_\_. Start with the shell and one demo, then we'll iterate."* Everything below is generic and reusable — there's no tracking, no keys, no personal data. Use it freely.

If you want the *why* behind building this way — the mindset, the workflow, how to think in prompts instead of syntax — read **Transmission 003** (linked at the bottom of the kit).

---

## 1. The one big idea

The bottleneck in building for the web is no longer syntax — it's **clarity**. If you can describe what you want precisely, an AI can write the code. So the whole site is organized around that truth:

- **Every tile is a live, working effect** *and* a copy-paste **prompt** that recreates it.
- Visitors don't read code — they read a sentence, click **copy**, and paste it into their own AI.
- The site teaches by being the thing it teaches.

Build for that loop: *see it live → read the plain-language prompt → steal the prompt → make your own.*

---

## 2. Architecture at a glance

```
/kit/
  index.html            ← the SHELL: a fixed top switch + two <iframe>s side by side
  motion/index.html     ← Library A: ~100 animation demos
  type/index.html       ← Library B: type specimens
  assets/               ← background video(s), posters
  build-your-own.md     ← this file
```

**The shell** loads both libraries as iframes in a horizontal track and *slides* between them — so switching feels like one continuous page, not a navigation. A persistent pill switch floats at the top. The background and the typed title stay continuous across the slide.

**Each library** is a single self-contained HTML file: all CSS in one `<style>`, all JS in a few `<script>` tags, no build step, no dependencies except two web fonts.

### The shell (complete, generic)

```html
<!doctype html><html lang="en"><head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<title>Build Kit</title>
<style>
  *{box-sizing:border-box;margin:0;padding:0}
  html,body{height:100%;background:#06060f;overflow:hidden;font-family:ui-monospace,monospace}
  /* two libraries live side by side in one track; switching SLIDES horizontally */
  .frames{position:fixed;inset:0;width:200vw;display:flex;transition:transform .55s cubic-bezier(.16,1,.3,1)}
  .frames.show-b{transform:translateX(-100vw)}
  .frame{width:100vw;height:100%;border:0;flex:none;background:#06060f}
  .switch{position:fixed;top:11px;left:50%;transform:translateX(-50%);z-index:1000;display:inline-flex;
    padding:4px;border-radius:999px;background:rgba(11,11,20,.72);backdrop-filter:blur(18px);
    border:1px solid rgba(255,255,255,.12)}
  .swseg{width:104px;padding:9px 0;border:0;background:none;cursor:pointer;color:#9a9bb8;
    font:600 12px/1 ui-monospace,monospace;letter-spacing:.14em;text-transform:uppercase}
  .swseg.active{color:#06060f}
  .swslide{position:absolute;top:4px;left:4px;height:calc(100% - 8px);width:104px;border-radius:999px;
    background:linear-gradient(110deg,#66f0ff,#b388ff 38%,#ff7ae0 68%,#ffd479);transition:transform .42s cubic-bezier(.16,1,.3,1)}
  .swslide.b{transform:translateX(104px)}
</style></head><body>
  <div class="switch">
    <span class="swslide" id="sl"></span>
    <button class="swseg active" data-mode="a">Motion</button>
    <button class="swseg" data-mode="b">Type</button>
  </div>
  <div class="frames" id="track">
    <iframe class="frame" id="fa" data-src="/kit/motion/"></iframe>
    <iframe class="frame" id="fb" data-src="/kit/type/"></iframe>
  </div>
<script>
(function(){
  var track=document.getElementById('track'), sl=document.getElementById('sl');
  var segs=[].slice.call(document.querySelectorAll('.swseg'));
  var frames={a:document.getElementById('fa'), b:document.getElementById('fb')};
  // load both up front; a runtime timestamp busts the iframe cache so edits always show
  var bust='?t='+Date.now();
  Object.keys(frames).forEach(function(k){ frames[k].src=frames[k].getAttribute('data-src').split('?')[0]+bust; });
  function setMode(m){
    segs.forEach(function(b){ b.classList.toggle('active', b.dataset.mode===m); });
    sl.classList.toggle('b', m==='b'); track.classList.toggle('show-b', m==='b');
  }
  segs.forEach(function(b){ b.addEventListener('click', function(){ setMode(b.dataset.mode); }); });
})();
</script></body></html>
```

---

## 3. The visual system (design tokens)

Keep this tiny and consistent. The whole look comes from one gradient + a near-black base + a mono UI font and an expressive display font.

```css
:root{
  --bg:#06060f;            /* near-black base */
  --cyan:#66f0ff; --violet:#b388ff; --magenta:#ff7ae0; --gold:#ffd479;
  --holo:linear-gradient(110deg,#66f0ff 0%,#b388ff 38%,#ff7ae0 68%,#ffd479 100%);
  --bright:#eef0ff; --dim:#9a9bb8; --faint:#34354f;
  --ease:cubic-bezier(.16,1,.3,1);
  --mono:'Spline Sans Mono',ui-monospace,monospace;   /* UI / prompts */
  --display:'Clash Display',system-ui,sans-serif;     /* big title */
}
```

- **Holo gradient** = the brand. Use it for the title fill (`background-clip:text`), the active pill, accents.
- **`--ease`** on everything that moves. One easing curve = cohesion.
- Two fonts, no more: a mono for chrome, a display for the hero.

---

## 4. The fixed-layout model (the thing that makes it feel like an app)

Each library uses **fixed regions** instead of normal document flow, so the hero never scrolls away and the background can be *zoned*:

- `nav` — fixed top breadcrumb (z-index high).
- `.hero` — a **fixed band** holding the editable title + the filter menu. Title row is `flex:1`; the menu sits at the bottom via `margin-top:auto`.
- `#scroller` — a **fixed, independently scrollable** region *below* the hero that holds the card grid. Its top edge is computed in JS from the hero's height.
- A background video is zoned to the **top band only**; the grid below sits on a calm dark page.

```css
.hero{position:fixed;top:54px;left:0;right:0;z-index:70;height:clamp(420px,56vh,600px);
  display:flex;flex-direction:column;align-items:center;text-align:center;padding:0 32px 14px;pointer-events:none}
.hero *{pointer-events:auto}
.hero .title-row{flex:1;display:flex;align-items:center;justify-content:center}
.hero .filterbar{margin-top:auto}     /* menu pinned to the bottom of the band */

#scroller{position:fixed;left:0;right:0;bottom:0;z-index:2;overflow-y:auto;
  -webkit-mask:linear-gradient(180deg,transparent 0,rgba(0,0,0,.08) 58px,rgba(0,0,0,.45) 120px,#000 200px)}
```

```js
// position the scroller right under the hero; expose the band height as a CSS var for the video
function layout(){
  var top = 54 + hero.offsetHeight + 14;
  scroller.style.top = top + 'px';
  document.documentElement.style.setProperty('--heroH', top + 'px');
}
addEventListener('resize', layout); layout();
```

The soft **`mask`** on `#scroller` makes cards dissolve into the scene as they rise to the top, instead of a hard cut.

---

## 5. The demo-card pattern (repeat ~100×)

Every effect is one self-contained `<article>`: a live **stage**, a title + description, and the **prompt** that recreates it. The prompt is the product.

```html
<article class="card" data-cat="background">
  <div class="stage"><!-- the live effect lives here (CSS, canvas, or SVG) --></div>
  <div class="card-body">
    <div class="card-meta"><span class="idx">07</span><span class="tag">background</span></div>
    <h3 class="card-title">Aurora Blobs</h3>
    <p class="card-desc">Soft blurred color clouds drift and breathe behind the content.</p>
    <button class="prompt">
      <span class="prompt-text">Build an ambient background of three large, heavily blurred
      radial-gradient blobs in cyan, violet and magenta that slowly drift and scale on different
      timings, blending together like an aurora.</span>
      <span class="prompt-copy">copy</span>
    </button>
  </div>
</article>
```

```css
.card{position:relative;border-radius:18px;overflow:hidden;border:1px solid rgba(255,255,255,.13);
  background:linear-gradient(180deg,rgba(255,255,255,.07) 0%,rgba(16,16,28,.62) 22%,rgba(9,9,17,.8) 100%);
  backdrop-filter:blur(22px) saturate(1.35)}   /* iOS frosted glass: light top → dark bottom */
.stage{position:relative;height:188px;display:grid;place-items:center;overflow:hidden;background:transparent}
```

Copy only happens on the **copy button** — a plain tile click drives the *preview* (next section). One line of JS:

```js
btn.addEventListener('click', function(){
  navigator.clipboard.writeText(btn.querySelector('.prompt-text').textContent.trim());
});
```

---

## 6. The signature move: the title becomes the preview

The hero title is **editable** (`contenteditable`). Type your own word; it stays. Then clicking any tile re-renders *the title itself* in that effect — the page becomes your sandbox:

- **Text effects** → your typed word takes on the animation (scaled up by **font-size**, not `transform`, so it stays crisp).
- **Backgrounds** → become a full-page layer over the video (`mix-blend-mode:screen` drops the dark parts so only the glow shows).
- **3D / interactive / buttons** → render at native size (so pointer math works) or scale up with a parallax tilt.

```html
<div class="title-row">
  <h1 id="title" contenteditable="true" spellcheck="false" style="white-space:pre-wrap">MOTION</h1>
  <span class="caret"></span>
</div>
```

```js
var word = 'MOTION';
title.addEventListener('input', function(){ word = (title.innerText||'').replace(/[ \t]+/g,' ').trim(); });

// clicking a tile routes by category:
card.addEventListener('click', function(e){
  if (e.target.closest('.prompt')) return;            // copy button never previews
  var cat = card.dataset.cat;
  if (cat === 'text')        applyTextEffect(card, word);
  else if (cat === 'background') applyFullPageLayer(card);
  else                       applyBigPreview(card);   // native size or parallax
});
```

**Crispness trick:** scaling text with `transform:scale()` rasterizes and bolds it. Scale by **`font-size`** instead and it renders natively sharp:

```js
var fs = parseFloat(getComputedStyle(el).fontSize);
el.style.fontSize = Math.round(fs * scaleFactor) + 'px';   // crisp, not blurred
```

---

## 7. The effect cookbook (the techniques worth stealing)

These are the reusable building blocks. Each is a few lines.

**Glow over video — screen blend.** Put any bright effect over busy footage and only the light survives:
```css
.over-video{mix-blend-mode:screen}   /* black → invisible, bright → glows */
```

**Aurora / drifting blobs.**
```css
.aurora .b{position:absolute;border-radius:50%;filter:blur(34px);mix-blend-mode:screen}
.aurora .b1{width:210px;height:210px;background:radial-gradient(circle,#66f0ff,transparent 70%);animation:drift 9s var(--ease) infinite alternate}
@keyframes drift{to{transform:translate(40px,-30px) scale(1.3)}}
```

**Per-letter stagger** (any length, any word) — set the delay inline so it isn't capped by hardcoded `nth-child` rules:
```js
[...word].forEach(function(ch,i){
  var s=document.createElement('span'); s.textContent=ch;
  s.style.animationDelay=(i*0.08)+'s';   // continuous wave across the whole phrase
  row.appendChild(s);
});
```

**Chromatic glitch** — two offset clones via pseudo-elements (keep it one line; `attr()` can't render returns):
```css
.glitch{position:relative}
.glitch::before,.glitch::after{content:attr(data-text);position:absolute;inset:0}
.glitch::before{color:#ff7ae0;clip-path:inset(0 0 55% 0);animation:gA 2.6s steps(2) infinite}
.glitch::after {color:#66f0ff;clip-path:inset(55% 0 0 0);animation:gB 2.2s steps(2) infinite}
```

**Canvas particles, sized safely** — measure the real box (with a parent fallback) so the buffer never collapses to 1px and stretches into a solid block:
```js
function fit(cv){
  var r=cv.getBoundingClientRect(), p=cv.parentNode, DPR=Math.min(devicePixelRatio||1,2);
  var w=r.width||cv.clientWidth||(p&&p.clientWidth)||340;
  var h=r.height||cv.clientHeight||(p&&p.clientHeight)||188;
  cv.width=Math.max(2,Math.round(w*DPR)); cv.height=Math.max(2,Math.round(h*DPR));
  return cv.getContext('2d');
}
```

**Magnetic / parallax button** — stays put, banks toward the cursor in 3D (smoother than translating it around):
```js
zone.addEventListener('pointermove', function(e){
  var r=zone.getBoundingClientRect();
  var x=(e.clientX-r.left)/r.width-0.5, y=(e.clientY-r.top)/r.height-0.5;
  btn.style.transform='perspective(820px) rotateY('+(x*28)+'deg) rotateX('+(-y*28)+'deg)';
});
zone.addEventListener('pointerleave', function(){ btn.style.transform=''; });
```

**Spotlight / flashlight** — feed the cursor into a CSS variable, paint with a radial gradient:
```js
el.addEventListener('pointermove', e=>{var r=el.getBoundingClientRect();
  el.style.setProperty('--mx', (e.clientX-r.left)+'px'); el.style.setProperty('--my',(e.clientY-r.top)+'px');});
```
```css
.spot::before{background:radial-gradient(220px circle at var(--mx,50%) var(--my,50%),rgba(102,240,255,.22),transparent 70%)}
```

---

## 8. Performance & polish (the details that sell it)

- **Lazy-init every demo** with `IntersectionObserver` so off-screen animations cost nothing; stop them when they scroll away.
  ```js
  var io=new IntersectionObserver(es=>es.forEach(e=>{ e.isIntersecting ? start(e.target) : stop(e.target); }),{rootMargin:'140px',threshold:.1});
  document.querySelectorAll('[data-fx]').forEach(el=>io.observe(el));
  ```
- **Return a cleanup function** from each animation (`return ()=>cancelAnimationFrame(raf)`) so previews don't leak.
- **Respect `prefers-reduced-motion`**: hide the video, calm the loops.
- **A dark "tray"** behind the card grid (`rgba(4,4,10,.8)`, laid over any full-screen animation) keeps the cards readable on busy backgrounds.
- **A "present" mode** that hides all chrome and fills the screen with the live preview over the video — perfect for screenshots and screen recordings.
- **Cross-frame sync:** the typed word is mirrored between libraries with `postMessage`, so it's one continuous page.

---

## 9. Build your own — paste this into your AI

> I want to build a cinematic, single-page "kit" website in plain HTML/CSS/JS (no framework, no build step). Use this blueprint as the reference. My kit is about **\_\_\_\_\_\_** and each tile should be a live demo plus a copy-paste prompt that recreates it.
>
> Start by generating: (1) the fixed-layout shell with a top switch, (2) one library page with the design tokens, the fixed hero + scrollable grid, and (3) three example demo cards wired to preview in the editable title. Keep everything in one HTML file per page, all CSS in one `<style>`, vanilla JS only. Then we'll add more demos one at a time.
>
> Constraints: near-black base, one signature gradient, two fonts, one easing curve. Lazy-init animations with IntersectionObserver and return cleanup functions. Make it feel like an app, not a document.

Then iterate the way this kit was built: **describe one tile, see it, refine the words, repeat.** That's the whole skill.

---

## 10. Go deeper

- **Transmission 003** — the mindset and workflow behind building this way with AI: how to think in prompts, how the loop compounds, and how anyone can do it. *(linked at the bottom of the kit)*
- The live kit itself is your reference implementation — every tile carries the exact prompt that made it.

*Built with words, not code. Take it and make it yours.*
