[object Object]

← back to Wallpaper Paint Antics

Create Wallo presentation and ten installer folly memes

4cb774c1627e93db85a1129a0c12c86f979c2d82 · 2026-09-05 00:45:33 -0700 · Steve Abrams

Files touched

Diff

commit 4cb774c1627e93db85a1129a0c12c86f979c2d82
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Sat Sep 5 00:45:33 2026 -0700

    Create Wallo presentation and ten installer folly memes
---
 scripts/production.cjs                             |   1 +
 series2/.gitignore                                 |   2 +
 series2/README.md                                  |  30 +++
 series2/STORYBOARD.md                              |  17 ++
 series2/animation.js                               | 214 ++++++++++++++++++++
 series2/assets/gsap.min.js                         |  10 +
 series2/assets/products/fairborn.jpg               | Bin 0 -> 137026 bytes
 series2/assets/products/kaya.jpg                   | Bin 0 -> 72914 bytes
 series2/assets/products/topiary.jpg                | Bin 0 -> 225209 bytes
 series2/assets/products/waterperry.jpg             | Bin 0 -> 114017 bytes
 series2/assets/products/wheaton.jpg                | Bin 0 -> 211476 bytes
 series2/audio.py                                   |  64 ++++++
 series2/build.mjs                                  |  15 ++
 series2/delivery-proof.mjs                         |  58 ++++++
 series2/episodes/04-fire-rated.html                | 215 ++++++++++++++++++++
 series2/episodes/05-wallpaper-or-paint.html        | 215 ++++++++++++++++++++
 series2/episodes/06-built-for-busy-spaces.html     | 215 ++++++++++++++++++++
 series2/episodes/07-sample-before-specifying.html  | 215 ++++++++++++++++++++
 series2/episodes/08-service-at-dw.html             | 215 ++++++++++++++++++++
 series2/gallery.mjs                                |  17 ++
 series2/index.html                                 | 215 ++++++++++++++++++++
 series2/mcp-proof.mjs                              |  33 ++++
 series2/meme-sources.json                          |  72 +++++++
 series2/memes.mjs                                  |  21 ++
 series2/memes/01-paste-gravity.png                 | Bin 0 -> 2536523 bytes
 series2/memes/02-one-inch.png                      | Bin 0 -> 2347754 bytes
 series2/memes/03-pattern-match.png                 | Bin 0 -> 2765593 bytes
 series2/memes/04-just-five-minutes.png             | Bin 0 -> 2379677 bytes
 series2/memes/05-paste-footprints.png              | Bin 0 -> 2240774 bytes
 series2/memes/06-roll-with-it.png                  | Bin 0 -> 2285873 bytes
 series2/memes/07-ceiling-paste.png                 | Bin 0 -> 2281611 bytes
 series2/memes/08-ladder-parking.png                | Bin 0 -> 2231931 bytes
 series2/memes/09-perfect-cut.png                   | Bin 0 -> 2227944 bytes
 series2/memes/10-last-roll.png                     | Bin 0 -> 2265272 bytes
 series2/memes/README.txt                           |  44 +++++
 series2/memes/index.html                           |   3 +
 series2/memes/manifest.json                        | 142 ++++++++++++++
 series2/production.cjs                             |  77 ++++++++
 series2/verification/04-fire-rated-1.3.png         | Bin 0 -> 637350 bytes
 series2/verification/04-fire-rated-11.6.png        | Bin 0 -> 529342 bytes
 series2/verification/04-fire-rated-16.5.png        | Bin 0 -> 620799 bytes
 series2/verification/04-fire-rated-19.png          | Bin 0 -> 630126 bytes
 series2/verification/04-fire-rated-3.5.png         | Bin 0 -> 602554 bytes
 series2/verification/04-fire-rated-6.2.png         | Bin 0 -> 540260 bytes
 series2/verification/04-fire-rated-8.2.png         | Bin 0 -> 535612 bytes
 series2/verification/04-fire-rated-check.txt       |  64 +++---
 series2/verification/04-fire-rated-decode.txt      |   1 +
 series2/verification/04-fire-rated-mp4-1.png       | Bin 0 -> 79339 bytes
 series2/verification/04-fire-rated-mp4-12.png      | Bin 0 -> 76034 bytes
 series2/verification/04-fire-rated-mp4-19.png      | Bin 0 -> 80009 bytes
 series2/verification/04-fire-rated-mp4-7.png       | Bin 0 -> 78574 bytes
 series2/verification/04-fire-rated-render.txt      |  96 +++++++++
 series2/verification/05-wallpaper-or-paint-1.3.png | Bin 0 -> 239846 bytes
 .../verification/05-wallpaper-or-paint-11.6.png    | Bin 0 -> 690665 bytes
 .../verification/05-wallpaper-or-paint-16.5.png    | Bin 0 -> 653769 bytes
 series2/verification/05-wallpaper-or-paint-19.png  | Bin 0 -> 662909 bytes
 series2/verification/05-wallpaper-or-paint-3.5.png | Bin 0 -> 237552 bytes
 series2/verification/05-wallpaper-or-paint-6.2.png | Bin 0 -> 430821 bytes
 series2/verification/05-wallpaper-or-paint-8.2.png | Bin 0 -> 701516 bytes
 .../verification/05-wallpaper-or-paint-check.txt   |  82 ++++++++
 .../verification/05-wallpaper-or-paint-decode.txt  |   1 +
 .../verification/05-wallpaper-or-paint-mp4-1.png   | Bin 0 -> 47589 bytes
 .../verification/05-wallpaper-or-paint-mp4-12.png  | Bin 0 -> 83133 bytes
 .../verification/05-wallpaper-or-paint-mp4-19.png  | Bin 0 -> 81001 bytes
 .../verification/05-wallpaper-or-paint-mp4-7.png   | Bin 0 -> 75445 bytes
 .../verification/05-wallpaper-or-paint-render.txt  |  96 +++++++++
 .../verification/06-built-for-busy-spaces-1.3.png  | Bin 0 -> 866472 bytes
 .../verification/06-built-for-busy-spaces-11.6.png | Bin 0 -> 802847 bytes
 .../verification/06-built-for-busy-spaces-16.5.png | Bin 0 -> 861542 bytes
 .../verification/06-built-for-busy-spaces-19.png   | Bin 0 -> 872794 bytes
 .../verification/06-built-for-busy-spaces-3.5.png  | Bin 0 -> 840309 bytes
 .../verification/06-built-for-busy-spaces-6.2.png  | Bin 0 -> 955091 bytes
 .../verification/06-built-for-busy-spaces-8.2.png  | Bin 0 -> 905227 bytes
 .../06-built-for-busy-spaces-check.txt             |  82 ++++++++
 .../06-built-for-busy-spaces-decode.txt            |   1 +
 .../06-built-for-busy-spaces-mp4-1.png             | Bin 0 -> 90416 bytes
 .../06-built-for-busy-spaces-mp4-12.png            | Bin 0 -> 91800 bytes
 .../06-built-for-busy-spaces-mp4-19.png            | Bin 0 -> 91408 bytes
 .../06-built-for-busy-spaces-mp4-7.png             | Bin 0 -> 100025 bytes
 .../06-built-for-busy-spaces-render.txt            |  96 +++++++++
 .../07-sample-before-specifying-1.3.png            | Bin 0 -> 807306 bytes
 .../07-sample-before-specifying-11.6.png           | Bin 0 -> 814774 bytes
 .../07-sample-before-specifying-16.5.png           | Bin 0 -> 847236 bytes
 .../07-sample-before-specifying-19.png             | Bin 0 -> 859364 bytes
 .../07-sample-before-specifying-3.5.png            | Bin 0 -> 775558 bytes
 .../07-sample-before-specifying-6.2.png            | Bin 0 -> 917191 bytes
 .../07-sample-before-specifying-8.2.png            | Bin 0 -> 817064 bytes
 .../07-sample-before-specifying-check.txt          |  82 ++++++++
 .../07-sample-before-specifying-decode.txt         |   1 +
 .../07-sample-before-specifying-mp4-1.png          | Bin 0 -> 87235 bytes
 .../07-sample-before-specifying-mp4-12.png         | Bin 0 -> 87156 bytes
 .../07-sample-before-specifying-mp4-19.png         | Bin 0 -> 86860 bytes
 .../07-sample-before-specifying-mp4-7.png          | Bin 0 -> 88520 bytes
 .../07-sample-before-specifying-render.txt         |  96 +++++++++
 series2/verification/08-service-at-dw-1.3.png      | Bin 0 -> 742400 bytes
 series2/verification/08-service-at-dw-11.6.png     | Bin 0 -> 719314 bytes
 series2/verification/08-service-at-dw-16.5.png     | Bin 0 -> 733288 bytes
 series2/verification/08-service-at-dw-19.png       | Bin 0 -> 746247 bytes
 series2/verification/08-service-at-dw-3.5.png      | Bin 0 -> 719382 bytes
 series2/verification/08-service-at-dw-6.2.png      | Bin 0 -> 790161 bytes
 series2/verification/08-service-at-dw-8.2.png      | Bin 0 -> 717755 bytes
 series2/verification/08-service-at-dw-check.txt    |  82 ++++++++
 series2/verification/08-service-at-dw-decode.txt   |   1 +
 series2/verification/08-service-at-dw-mp4-1.png    | Bin 0 -> 81412 bytes
 series2/verification/08-service-at-dw-mp4-12.png   | Bin 0 -> 82312 bytes
 series2/verification/08-service-at-dw-mp4-19.png   | Bin 0 -> 82390 bytes
 series2/verification/08-service-at-dw-mp4-7.png    | Bin 0 -> 82486 bytes
 series2/verification/08-service-at-dw-render.txt   |  96 +++++++++
 series2/verification/3x.txt                        |  15 ++
 series2/verification/checks.json                   | 217 +++++++++++++++++++++
 series2/verification/concat.txt                    |   5 +
 series2/verification/contact-sheet.png             | Bin 0 -> 856288 bytes
 series2/verification/delivery-proof.json           | 119 +++++++++++
 series2/verification/e2e-proof.json                | 109 +++++++++++
 series2/verification/mcp-gallery.png               | Bin 0 -> 1149444 bytes
 series2/verification/mcp-proof.json                | 116 +++++++++++
 series2/verification/mcp-stderr.txt                |   5 +
 series2/verification/memes-gallery.png             | Bin 0 -> 5898562 bytes
 series2/verification/memes-mobile.png              | Bin 0 -> 355172 bytes
 series2/verification/presentation-mobile.png       | Bin 0 -> 1174913 bytes
 series2/verification/verify-evidence.json          |  96 +++++++++
 .../wallo-wallcovering-presentation-decode.txt     |   1 +
 .../wallo-wallcovering-presentation-mp4-1.png      | Bin 0 -> 155748 bytes
 .../wallo-wallcovering-presentation-mp4-21.png     | Bin 0 -> 89561 bytes
 .../wallo-wallcovering-presentation-mp4-41.png     | Bin 0 -> 176155 bytes
 .../wallo-wallcovering-presentation-mp4-61.png     | Bin 0 -> 170228 bytes
 .../wallo-wallcovering-presentation-mp4-81.png     | Bin 0 -> 158760 bytes
 .../wallo-wallcovering-presentation-mp4-99.png     | Bin 0 -> 163764 bytes
 series2/watch.html                                 |   6 +
 watch.html                                         |   2 +-
 130 files changed, 3637 insertions(+), 41 deletions(-)

diff --git a/scripts/production.cjs b/scripts/production.cjs
index c85dfcc..c63e210 100644
--- a/scripts/production.cjs
+++ b/scripts/production.cjs
@@ -11,6 +11,7 @@ const out=path.join(root,'verification');
 const phase=process.argv[2]||'preview';
 function exec(cmd,args,log){const r=spawnSync(cmd,args,{cwd:root,encoding:'utf8',maxBuffer:16*1024*1024,env:{...process.env,HYPERFRAMES_TELEMETRY_DISABLED:'1'}});if(log)fs.writeFileSync(path.join(out,log),r.stdout+'\n'+r.stderr);if(r.status!==0)throw new Error(cmd+' '+args.join(' ')+'\n'+r.stdout+'\n'+r.stderr);return r.stdout;}
 (async()=>{
+ if(phase.startsWith('series2-')){process.argv[2]=phase.slice(8);await import('../series2/production.cjs');return;}
  if(phase==='mcp'){await import('./mcp-proof.mjs');return;}
  if(phase==='render'){
    for(const slug of slugs){console.log('Rendering '+slug);exec('node',[cli,'render','.', '-c','episodes/'+slug+'.html','--quality','high','--fps','30','--workers','2','--output','renders/'+slug+'.mp4'],slug+'-render.txt');console.log('Rendered '+slug);}
diff --git a/series2/.gitignore b/series2/.gitignore
index 2b2ee48..6d05660 100644
--- a/series2/.gitignore
+++ b/series2/.gitignore
@@ -7,3 +7,5 @@ renders/
 tmp/
 .hyperframes/
 verification/check-*/
+verification/server.json
+memes/*.zip
diff --git a/series2/README.md b/series2/README.md
new file mode 100644
index 0000000..ef898b1
--- /dev/null
+++ b/series2/README.md
@@ -0,0 +1,30 @@
+# Wallo presents: Material Notes
+
+Open `watch.html` for the full 100-second wallcovering presentation and five individual 20-second shorts. Open `memes/index.html` for ten installer-folly memes and their ZIP download.
+
+The five films feature actual product imagery from designerwallcoverings.com. Every featured product has a direct link in the gallery. The illustrations in the memes are original fictional room settings, not catalog product representations.
+
+## Deliverables
+
+- `renders/wallo-wallcovering-presentation.mp4`: all five chapters, H.264/AAC, 1080 × 1920, 30 fps.
+- `renders/04-fire-rated.mp4`: surface fire performance and asking for the correct report.
+- `renders/05-wallpaper-or-paint.mp4`: pattern, texture, depth and the role of paint.
+- `renders/06-built-for-busy-spaces.mp4`: specifying commercial vinyl for daily wear.
+- `renders/07-sample-before-specifying.mp4`: evaluating physical samples in the room.
+- `renders/08-service-at-dw.mp4`: samples, product information, quotes and ordering.
+- `memes/`: ten square PNG memes, download gallery, source manifest and ZIP.
+- `transcripts/`: narration scripts and featured product URLs.
+
+Wallo leads the presentation. Pippa demonstrates samples, cleaning and comparison. Character movement uses fixed-length shoulder/elbow/wrist and hip/knee/ankle chains, changing foot plants, head turns, blinks and speech-energy-driven mouths. The direction is restrained editorial 2D illustration, with original instrumental music beneath local Kokoro narration.
+
+## Reproduce
+
+Use `node ../scripts/production.cjs series2-<phase>` from this directory. Phases: audio, build, preview, check, render, verify, mcp, memes, delivery. `serve` starts a temporary localhost server for `smoke` (the six-way browser check). `node gallery.mjs` rebuilds the presentation gallery.
+
+Generated audio and videos stay on disk and are gitignored. Source, provenance, image originals and verification evidence are committed. `audio.json` includes measured speech timings. `products.json` and `sources.json` document retrieved product images and factual support. `meme-sources.json` preserves the original generator output locations.
+
+## Verification
+
+See `verification/verify-evidence.json` for all six rendered-video frame counts, hashes, dimensions, duration, codec and browser playback. `preview-evidence.json` proves deterministic seeking and articulated limbs. `mcp-proof.json` records real Chrome DevTools MCP calls. `delivery-proof.json` verifies audio signal, all ten meme downloads, ZIP contents, mobile layout and chapter navigation. `3x.txt` records attempted browser engines and any unavailable engines explicitly.
+
+Local review files only. Fire classification is never described as fireproof; project-specific test reports and mounting requirements must be checked for the selected material.
diff --git a/series2/STORYBOARD.md b/series2/STORYBOARD.md
new file mode 100644
index 0000000..85daac5
--- /dev/null
+++ b/series2/STORYBOARD.md
@@ -0,0 +1,17 @@
+# Five material notes — final storyboard
+
+Audience: designers, specifiers and customers choosing a wall finish. Format: five narrated 20-second vertical chapters plus one 100-second presentation. Characters: Wallo presents; Pippa demonstrates. Tone: adult, calm, informative, with understated personality. Actual DW product swatches remain unaltered; room applications are illustrative.
+
+Each chapter uses four discrete caption beats at approximately 0.6, 5.1, 9.6 and 14.1 seconds, following the discrete-text-sequence composition pattern. Text lives in a stable caption area. Character motion is custom joint animation on a continuous room stage. Narration completes before the ending hold.
+
+| Chapter | Opening | Demonstration | Clarification | Closing action |
+| --- | --- | --- | --- | --- |
+| Fire performance | Wallo enters with a report | Pippa presents Fairborn swatch | Flame spread and smoke do not mean fireproof | Request actual report and installation requirements |
+| Wallpaper or paint | Wallo introduces material choice | Paint roller and Waterperry sample comparison | Pattern, depth and texture versus a simple painted finish | Choose for the room’s purpose |
+| Daily life | Wallo presents commercial vinyl | Pippa crouches and wipes a sample | Check abrasion, cleanability and care | Specify the right material for traffic |
+| Physical samples | Wallo opens material discussion | Pippa compares Topiary View and finish chips | Light, color and pattern scale affect the result | Sample before specifying |
+| DW service | Wallo introduces support | Pippa handles Kaya sample and tablet | Samples, product information and quotes | Visit designerwallcoverings.com |
+
+Animation constraints: attached props follow wrists; elbows and knees bend with fixed bone lengths; foot plants and torso shifts accompany reaching; mouth shapes follow measured voice energy. Seven pose snapshots per film and reverse seeking are checked before rendering. Encoded frames are checked again after rendering.
+
+The installer-folly meme series is a separate illustrated comedy set: paste gravity, one more inch, pattern matching, five-minute touch-up, paste footprints, unrolled wallpaper, ceiling paste, powder-room ladder, wrong-end cutting and the last roll. Captions and complete illustrations are generated together, then inspected individually.
diff --git a/series2/animation.js b/series2/animation.js
new file mode 100644
index 0000000..c60c26f
--- /dev/null
+++ b/series2/animation.js
@@ -0,0 +1,214 @@
+/* Wallo Material Notes: deterministic articulated character acting and real DW samples. */
+(() => {
+ 'use strict';
+ const canvas=document.getElementById('stage'), c=canvas.getContext('2d');
+ const E=window.EPISODE, P=window.PRODUCTS, ep=E.index;
+ const ink='#243a3b', chalk='#eeeae2', teal='#284f4b', clay='#a9634d', brass='#b7924f';
+ const images={}; let telemetry=[];
+ const clamp=(x,a=0,b=1)=>Math.max(a,Math.min(b,x));
+ const mix=(a,b,p)=>a+(b-a)*p;
+ const smooth=p=>{p=clamp(p);return p*p*(3-2*p);};
+ const ease=(t,a,b)=>smooth((t-a)/(b-a));
+ const pulse=(t,a,b,c,d)=>ease(t,a,b)*(1-ease(t,c,d));
+ const point=(x,y)=>({x,y});
+ const dist=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y);
+ function group(x,y,r,s,fn){c.save();c.translate(x,y);c.rotate(r);c.scale(s,s);fn();c.restore();}
+ function path(fn,fill,stroke=null,w=2){c.beginPath();fn(c);if(fill){c.fillStyle=fill;c.fill();}if(stroke){c.lineWidth=w;c.strokeStyle=stroke;c.lineJoin='round';c.lineCap='round';c.stroke();}}
+ function line(x,y,u,v,col,w=2){path(q=>{q.moveTo(x,y);q.lineTo(u,v);},null,col,w);}
+ function rect(x,y,w,h,col){c.fillStyle=col;c.fillRect(x,y,w,h);}
+ function oval(x,y,rx,ry,col,stroke=null,w=2){path(q=>q.ellipse(x,y,rx,ry,0,0,Math.PI*2),col,stroke,w);}
+ function rr(x,y,w,h,r,col,stroke=null,lw=2){path(q=>q.roundRect(x,y,w,h,r),col,stroke,lw);}
+ function text(s,x,y,size=28,color=ink,font='Verdana',align='left'){c.font=`${size}px '${font}'`;c.textAlign=align;c.textBaseline='alphabetic';c.fillStyle=color;c.fillText(s,x,y);}
+ function wrap(s,x,y,max,size=36,color=ink,font='Verdana',lh=size*1.4){
+   c.font=`${size}px '${font}'`;let row='',lines=[];
+   for(const word of s.split(' ')){const test=row?row+' '+word:word;if(c.measureText(test).width>max&&row){lines.push(row);row=word;}else row=test;}
+   if(row)lines.push(row);lines.forEach((v,i)=>text(v,x,y+i*lh,size,color,font));return lines.length;
+ }
+ function imageCover(id,x,y,w,h){const im=images[id];if(!im||!im.complete)return;const k=Math.max(w/im.width,h/im.height);c.save();c.beginPath();c.rect(x,y,w,h);c.clip();c.drawImage(im,x+(w-im.width*k)/2,y+(h-im.height*k)/2,im.width*k,im.height*k);c.restore();}
+ function sample(id,x,y,w=126,h=164,r=0){group(x,y,r,1,()=>{rr(-w/2+7,-h/2+9,w,h,2,'#203a3a20');rect(-w/2-5,-h/2-5,w+10,h+10,'#f9f7f0');imageCover(id,-w/2,-h/2,w,h);line(-w/2,-h/2,w/2,-h/2,'#ffffff',2);});}
+ function bone(a,b,width,col,shade){
+   const angle=Math.atan2(b.y-a.y,b.x-a.x),d=dist(a,b);
+   group(a.x,a.y,angle,1,()=>{rr(-width*.18,-width/2,d+width*.36,width,width*.46,col);path(q=>{q.moveTo(5,width*.26);q.quadraticCurveTo(d*.5,width*.34,d-3,width*.22);},null,shade||'#00000018',2.3);});
+ }
+ function solveIK(root,target,l1,l2,pole){
+   const dx=target.x-root.x,dy=target.y-root.y,raw=Math.hypot(dx,dy),d=clamp(raw,Math.abs(l1-l2)+.01,l1+l2-.01);
+   const nx=raw?dx/raw:0,ny=raw?dy/raw:1,a=(l1*l1-l2*l2+d*d)/(2*d),h=Math.sqrt(Math.max(0,l1*l1-a*a));
+   const end=point(root.x+nx*d,root.y+ny*d),joint=point(root.x+nx*a-ny*h*pole,root.y+ny*a+nx*h*pole);
+   return {root,joint,end,l1,l2};
+ }
+ function rotatePoint(p,angle,origin=point(0,-258)){const x=p.x-origin.x,y=p.y-origin.y;return point(origin.x+x*Math.cos(angle)-y*Math.sin(angle),origin.y+x*Math.sin(angle)+y*Math.cos(angle));}
+ function key(t,frames){if(t<=frames[0][0])return frames[0].slice(1);for(let i=1;i<frames.length;i++)if(t<=frames[i][0]){const p=ease(t,frames[i-1][0],frames[i][0]);return frames[i].slice(1).map((v,j)=>mix(frames[i-1][j+1],v,p));}return frames.at(-1).slice(1);}
+ function gait(t,start,end,x0,x1){
+   const p=clamp((t-start)/(end-start));
+   const lift=(a,b)=>p>a&&p<b?Math.sin(Math.PI*(p-a)/(b-a))*44:0;
+   const left=mix(x0-30,(x0+x1)/2-30,smooth((p-.08)/.28))+mix(0,(x1-x0)/2,smooth((p-.65)/.3));
+   const right=mix(x0+30,x1+30,smooth((p-.35)/.3));
+   return {x:(left+right)/2,feet:[point(left,-lift(.08,.36)-lift(.65,.95)),point(right,-lift(.35,.65))],walking:p>0&&p<1,phase:p};
+ }
+ function voiceEnergy(t){const env=E.audio?.energy||[];return env[Math.min(env.length-1,Math.max(0,Math.floor(t*50)))]||0;}
+ function hand(x,y,r,pose='open',skin='#c89472',prop=null){
+   group(x,y,r,1,()=>{
+    if(prop==='sample')sample(E.product,8,-18,115,148,-.04);
+    if(prop==='fan')for(let k=0;k<3;k++)sample(['waterperry','topiary','kaya'][k],k*17-15,-43,91,130,(k-1)*.18);
+    if(prop==='report'){rr(-12,-134,135,176,4,'#f8f5e9',ink,2);rect(1,-117,108,5,teal);text('TEST',4,-87,22,ink);text('REPORT',4,-58,22,ink);for(let k=0;k<3;k++)line(4,-35+k*18,97,-35+k*18,'#a4aaa0',2);}
+    if(prop==='cloth'){path(q=>{q.moveTo(-28,-15);q.lineTo(30,-26);q.lineTo(43,35);q.lineTo(-21,44);q.closePath();},'#c8c5b5',ink,1);line(-17,-4,28,25,'#efeadb',3);}
+    if(prop==='roller'){line(0,-8,0,-72,ink,7);line(0,-72,37,-72,ink,7);line(37,-72,37,-108,ink,5);rr(-27,-132,127,38,11,'#8b9c8d',ink,2);rr(-9,-9,18,54,7,brass);}
+    if(prop==='tablet'){rr(-73,-76,145,170,8,ink);rr(-65,-67,129,151,4,'#f4f0e5');text('DW',-45,-33,27,teal,'Georgia');for(let i=0;i<3;i++)imageCover(['waterperry','fairborn','kaya'][i],-47+i*32,-6,27,60);}
+    if(pose==='point'){bone(point(-4,3),point(34,-18),13,skin);bone(point(0,9),point(16,15),21,skin);oval(-6,9,13,18,skin);}
+    else{rr(-14,-9,29,42,12,skin);oval(-15,5,7,13,skin);for(let i=0;i<3;i++)line(-7+i*6,14,-7+i*6,28,'#9e685446',1);}
+   });
+ }
+ function head(kind,x,y,t,look,mouth,tilt){
+   const male=kind==='wallo',skin=male?'#c89472':'#d5a185',dark=male?'#714f3b':'#834e39';
+   group(x,y,tilt,.82,()=>{
+    if(!male){oval(-4,-10,51,70,dark);oval(-39,-50,28,28,dark);path(q=>{q.moveTo(-57,-55);q.quadraticCurveTo(-81,-37,-59,-16);},null,'#a86b4b',7);}
+    // Tapered jaw, defined brow and modest nose maintain adult proportions.
+    path(q=>{q.moveTo(-41,-40);q.bezierCurveTo(-43,-77,43,-80,44,-34);q.lineTo(40,31);q.quadraticCurveTo(31,66,5,72);q.quadraticCurveTo(-26,65,-36,34);q.closePath();},skin);
+    path(q=>{q.moveTo(-38,-31);q.lineTo(-32,34);q.quadraticCurveTo(-24,52,-8,57);q.lineTo(-20,21);q.lineTo(-18,-46);q.closePath();},male?'#b58162':'#c39076');
+    oval(-40,3,9,16,skin);oval(42,2,8,15,skin);
+    if(male){path(q=>{q.moveTo(-39,-39);q.lineTo(-33,-18);q.lineTo(-30,-54);q.lineTo(31,-54);q.lineTo(43,-25);q.lineTo(42,-55);q.closePath();},dark);}
+    else{path(q=>{q.moveTo(-40,-27);q.quadraticCurveTo(-24,-63,2,-56);q.quadraticCurveTo(38,-37,43,-19);q.lineTo(38,-58);q.quadraticCurveTo(-4,-89,-37,-55);q.closePath();},dark);for(let i=0;i<4;i++)path(q=>{q.moveTo(-29+i*17,-56);q.quadraticCurveTo(-26+i*17,-43,-13+i*17,-39);},null,'#ae7150',2);}
+    const blink=(t+(male?.31:1.93))%4.4<.14;
+    for(const side of [-1,1]){const ex=side*18+look*3,ey=-8;path(q=>{q.moveTo(ex-10,ey);q.quadraticCurveTo(ex,ey-5,ex+10,ey);},null,ink,2.2);if(!blink){oval(ex+look*3,ey+1,3.5,4.3,ink);oval(ex+look*3+1,ey-.3,1.1,1.2,'#eee8dc');}line(ex-10,ey-15,ex+8,ey-17+(side===1?look*2:0),dark,2.5);}
+    path(q=>{q.moveTo(3+look*5,-5);q.lineTo(8+look*8,15);q.quadraticCurveTo(4+look*5,20,-1+look*5,17);},null,'#98644f',1.8);
+    if(male){path(q=>{q.moveTo(-20,29);q.quadraticCurveTo(-6,20,4,27);q.quadraticCurveTo(15,23,26,30);q.quadraticCurveTo(17,39,3,34);q.quadraticCurveTo(-9,37,-20,29);},dark);}
+    if(mouth>.12){oval(4,44,10+mouth*3,2+mouth*6,'#714735');if(mouth>.4)line(-2,41,11,41,'#efe6d7',2);}
+    else path(q=>{q.moveTo(-7,43);q.quadraticCurveTo(5,48,17,42);},null,'#714735',1.8);
+    if(male){path(q=>{q.moveTo(-49,-53);q.quadraticCurveTo(-48,-98,10,-95);q.quadraticCurveTo(47,-89,48,-53);q.closePath();},brass);path(q=>{q.moveTo(-49,-53);q.quadraticCurveTo(12,-64,72,-48);q.quadraticCurveTo(77,-35,39,-41);q.lineTo(-49,-43);q.closePath();},'#97773f');line(-27,-73,12,-84,'#d2b270',3);}
+    else oval(-39,14,3.5,5.5,brass);
+   });
+ }
+ function character(kind,t,settings={}){
+   const male=kind==='wallo',skin=male?'#c89472':'#d5a185',shirt=male?'#d6d0bc':clay,cloth=male?teal:'#c8c1af';
+   const walk=settings.walk||gait(t,0,1,settings.x,settings.x);let x=walk.x,y=settings.y||882;
+   const crouch=settings.crouch||0,lean=settings.lean||0,breath=Math.sin(t*2.1+(male?0:1.2))*1.7;
+   const hipY=-275+crouch,origin=point(0,hipY);
+   const bodyPoint=(u,v)=>rotatePoint(point(u,v+crouch+breath),lean,origin);
+   const leftShoulder=bodyPoint(-62,-469),rightShoulder=bodyPoint(62,-469);
+   const arms=[solveIK(leftShoulder,point(...settings.left),118,111,-1),solveIK(rightShoulder,point(...settings.right),118,111,1)];
+   const legs=walk.feet.map((foot,i)=>solveIK(point(i===0?-32:32,hipY),point(foot.x-x,foot.y-13),134,136,i===0?1:-1));
+   const mouth=male?voiceEnergy(t)*.88:0;
+   const joints={kind,x,y,arms,legs,lean,crouch,feet:walk.feet,walking:walk.walking,props:settings.props||[]};
+   telemetry.push(joints);
+   oval(x,y+6,94-crouch*.1,15,'#233a3b15');
+   group(x,y,0,settings.scale||1,()=>{
+    // Back arm and far leg precede torso so elbow overlaps read spatially.
+    const armDraw=(i)=>{const b=arms[i];bone(b.root,b.joint,39,shirt);bone(b.joint,b.end,27,skin);oval(b.joint.x,b.joint.y,16,16,skin);const sleeve=point(mix(b.root.x,b.joint.x,.77),mix(b.root.y,b.joint.y,.77));bone(b.root,sleeve,43,shirt);line(sleeve.x-8,sleeve.y,sleeve.x+9,sleeve.y,'#ffffff30',2);};
+    armDraw(0);
+    legs.forEach((b,i)=>{bone(b.root,b.joint,58,cloth);bone(b.joint,b.end,43,cloth);oval(b.joint.x,b.joint.y,23,23,cloth);line(b.root.x-8,b.root.y+30,b.joint.x-6,b.joint.y-16,male?'#41635b':'#dfd8c8',2);group(b.end.x,b.end.y+10,walk.walking&&walk.feet[i].y<0?-.12:0,1,()=>{path(q=>{q.moveTo(-24,-20);q.lineTo(20,-20);q.lineTo(27,-9);q.quadraticCurveTo(60,-5,56,14);q.lineTo(-24,14);q.closePath();},male?'#655545':'#626b62');line(-24,14,55,14,'#34413d',5);line(4,-8,25,-7,'#c6b69b',2);});});
+    group(0,hipY,lean,1,()=>{
+     const dy=crouch+breath-hipY;
+     path(q=>{q.moveTo(-63,-478+dy);q.quadraticCurveTo(-82,-460+dy,-71,-417+dy);q.lineTo(-60,-266+dy);q.quadraticCurveTo(0,-235+dy,60,-266+dy);q.lineTo(71,-417+dy);q.quadraticCurveTo(79,-460+dy,58,-479+dy);q.closePath();},shirt);
+     // Structured overall/apron silhouette, stitched seams, pockets and cloth folds.
+     path(q=>{q.moveTo(-50,-425+dy);q.lineTo(-46,-350+dy);q.lineTo(-61,-290+dy);q.lineTo(-60,-253+dy);q.lineTo(61,-253+dy);q.lineTo(61,-290+dy);q.lineTo(46,-350+dy);q.lineTo(48,-425+dy);q.closePath();},cloth);
+     for(const s of [-1,1]){line(s*44,-472+dy,s*38,-405+dy,cloth,17);oval(s*39,-414+dy,4.8,4.8,brass);line(s*49,-328+dy,s*41,-312+dy,male?'#59756b':'#a9a18f',2);}
+     rr(-27,-401+dy,54,56,4,male?'#254540':'#bab39f');path(q=>{q.moveTo(-23,-388+dy);q.quadraticCurveTo(0,-376+dy,24,-388+dy);},null,male?'#6a8778':'#e5dfd0',1.5);
+     line(-50,-266+dy,52,-266+dy,male?'#1e403b':'#aca590',3);
+     if(male){rr(-66,-321+dy,20,67,3,'#8d7452');line(-56,-342+dy,-56,-282+dy,brass,6);}
+     path(q=>{q.moveTo(-17,-509+dy);q.lineTo(-18,-474+dy);q.quadraticCurveTo(0,-454+dy,21,-474+dy);q.lineTo(18,-509+dy);q.closePath();},skin);
+    });
+    const hp=bodyPoint(0,-559);head(kind,hp.x,hp.y,t,settings.look||0,mouth,(settings.headTilt||0)+lean*.28);
+    armDraw(1);
+    arms.forEach((b,i)=>hand(b.end.x,b.end.y,(settings.wrists||[0,0])[i],(settings.gestures||['open','open'])[i],skin,(settings.props||[])[i]));
+   });
+   return joints;
+ }
+ function room(t){
+   rect(-350,-300,1800,1200,'#dfdbd1');rect(-350,765,1800,450,'#c9c2b1');
+   rect(20,0,240,706,'#e9e7dc');for(let i=0;i<4;i++)line(22+i*59,0,22+i*59,704,'#f7f4e9',7);
+   rect(318,26,647,727,'#c7bfae');rect(329,37,624,704,'#efede3');
+   if(ep===1){const a=ease(t,5.1,8);rect(343,49,596,678,'#aab7a4');c.save();c.beginPath();c.rect(343,49,596*a,678);c.clip();imageCover(E.product,343,49,596,678);c.restore();}
+   else imageCover(E.product,343,49,596,678);
+   line(340,743,943,743,'#faf7eb',4);line(-350,765,1450,765,'#a79f8d',2);
+   for(let i=0;i<6;i++)line(-200,814+i*54,1400,814+i*54,'#b6ad9a',1.4);
+   line(91,765,-170,1220,'#b6ad9a',1.4);line(880,765,1130,1220,'#b6ad9a',1.4);
+   // Light and shadow move gently with the camera, not with an arbitrary pulse.
+   path(q=>{q.moveTo(40,706);q.lineTo(263,706);q.lineTo(672,1120);q.lineTo(412,1120);q.closePath();},'#fcf7df28');
+   // A sample ledge ties the real products to the working material library.
+   rect(404,685,479,12,'#b29d7a');for(let i=0;i<3;i++)sample(P[(i+ep)%P.length].id,468+i*115,622,88,116,(i-1)*.045);
+ }
+ function documentPanel(t){
+   if(ep!==0)return;
+   const a=pulse(t,4.2,4.9,14.3,15.0);if(!a)return;
+   group(640,177+(1-a)*28,0,.88,()=>{
+    rr(-239,-168,478,336,2,'#f7f4e9',ink,1.5);text('BEFORE YOU SPECIFY',-210,-126,24,teal);
+    const labels=['PRODUCT / TEST REPORT','CLASSIFICATION','SUBSTRATE + ADHESIVE'];
+    labels.forEach((label,i)=>{const done=t>5.0+i*2.8;line(-209,-61+i*77,-182,-61+i*77,done?brass:'#bac0b5',3);text(label,-163,-52+i*77,21,ink);});
+   });
+ }
+ function swatchBoard(t){
+   if(ep!==3&&ep!==4)return;
+   const a=pulse(t,6.4,7.1,13.4,14);if(!a)return;
+   group(560,275+(1-a)*25,0,.95,()=>{
+    rr(-199,-139,398,278,2,'#f5f0e4',ink,1);sample(E.product,-83,-1,155,220,0);
+    rect(28,-111,137,83,'#c6bea9');rect(28,-16,137,69,'#8c9985');rect(28,64,137,43,'#b89672');
+    text(ep===3?'COMPARE YOUR FINISHES':'YOUR PROJECT SHORTLIST',-192,-162,23,ink);
+   });
+ }
+ function actionNotes(t){
+   if(ep!==2)return;const a=pulse(t,8,8.5,14.2,14.8);if(!a)return;
+   ['ABRASION','CLEANABILITY','CARE'].forEach((s,i)=>{const show=ease(t,8+i*.7,8.4+i*.7);if(show){rect(440,120+i*77,440,58,'#f4f0e6');text(s,468,159+i*77,27,ink);}});
+ }
+ function performances(t){
+   let wwalk=gait(t,.2,2.9,126,253),pwalk=gait(t,.5,3.1,949,801);
+   const w={walk:wwalk,left:[-85,-279],right:[149,-396],wrists:[-.1,-.36],props:[],gestures:['open','open'],look:.6,lean:Math.sin(t*.8)*.012};
+   const p={walk:pwalk,left:[-130,-402],right:[86,-275],wrists:[.14,.08],props:['sample'],look:-.6,lean:Math.sin(t*.73+1)*.013};
+   // Presenter gestures are deliberately different for each spoken clause.
+   const gesture=key(t,[[0,100,-285],[2.8,145,-411],[4.4,165,-440],[5.2,120,-367],[8.7,161,-441],[9.6,149,-378],[12.8,180,-432],[14,125,-360],[17,152,-419],[19.8,126,-383]]);
+   w.right=gesture;w.wrists[1]=key(t,[[0,0],[3,-.5],[5.2,-.15],[8,-.42],[11,.1],[14,-.4],[18,-.2]])[0];
+   w.gestures[1]=t>4.5&&t<8.8?'point':'open';w.headTilt=Math.sin(t*.65)*.022;w.look=t>4&&t<9?1:.1;
+   if(t<3){w.right=[90-Math.sin(t*4)*40,-277];w.left=[-90+Math.sin(t*4)*40,-277];p.left=[-90+Math.sin(t*4)*24,-280];p.props=[];}
+   if(ep===0){p.props=['report'];p.left=key(t,[[0,-85,-280],[3,-130,-365],[6,-131,-420],[10,-153,-445],[14,-125,-384],[19,-108,-376]]);p.wrists[0]=-.14;p.right=key(t,[[0,90,-277],[8,98,-320],[11,-10,-377],[14,93,-278]]);}
+   if(ep===1){
+    p.props=t<8?['roller']:['sample'];p.left=key(t,[[0,-88,-283],[3,-129,-447],[4.5,-142,-495],[5.2,-143,-411],[6,-146,-495],[7,-145,-397],[8,-100,-367],[11,-139,-436],[15,-103,-361]]);p.wrists[0]=.07;
+    if(t>9.3){p.walk=gait(t,9.3,11.4,801,870);p.look=-1;p.lean=-.06*ease(t,10,11);}
+    w.props=['sample'];w.left=key(t,[[0,-87,-282],[4,-98,-291],[6,-39,-389],[8,-30,-433],[12,-59,-386],[17,-78,-320]]);w.wrists[0]=-.08;
+   }
+   if(ep===2){
+    p.crouch=pulse(t,4,5.6,8.8,10.2)*113;p.lean=-pulse(t,4,5.4,9,10.3)*.10;
+    const wipe=pulse(t,5,5.8,8.2,9);p.props=['cloth'];p.left=[-147-wipe*17,-389+p.crouch+wipe*Math.sin(t*6)*42];p.wrists[0]=-.5;
+    p.right=[85,-284+p.crouch];p.look=-1;
+    w.props=['sample'];w.left=[-45,-371];w.wrists[0]=-.18;
+   }
+   if(ep===3){
+    p.props=['sample'];p.left=key(t,[[0,-92,-280],[3,-100,-366],[6,-138,-440],[10,-144,-478],[14,-126,-418],[18,-93,-340]]);p.wrists[0]=key(t,[[0,0],[3,-.08],[7,.22],[10,-.18],[13,.16],[17,0]])[0];
+    w.props=['fan'];w.left=key(t,[[0,-90,-280],[4,-33,-365],[7,-44,-400],[12,-24,-389],[16,-44,-346]]);w.wrists[0]=-.13;
+   }
+   if(ep===4){
+    w.props=['tablet'];w.left=[-43,-341];w.right=key(t,[[0,101,-280],[3,143,-413],[6,158,-444],[8.5,65,-361],[10,34,-340],[11.6,20,-356],[13,144,-411],[17,160,-444],[19,112,-374]]);w.gestures[1]=t>8&&t<12?'point':'open';
+    p.props=['fan'];p.left=[-129,-400];p.right=key(t,[[0,89,-280],[6,86,-280],[9,-18,-384],[12,76,-285],[17,100,-382],[19,86,-297]]);
+   }
+   // Camera-right actor is drawn first so the presenter remains the visual lead.
+   character('pippa',t,p);character('wallo',t,w);
+ }
+ function overlay(t){
+   rect(0,0,1080,437,chalk);rect(0,1437,1080,483,chalk);
+   text('DESIGNER WALLCOVERINGS',72,96,25,ink);text('WALLO PRESENTS',1008,96,21,teal,'Verdana','right');
+   line(72,124,1008,124,'#b9b6aa',1);text(E.kicker,72,178,24,teal);
+   E.title.split('\n').forEach((s,i)=>text(s,69,270+i*87,73,ink,'Georgia'));
+   text('MATERIAL NOTES',73,411,22,teal);text('WITH WALLO & PIPPA',1006,411,20,teal,'Verdana','right');
+   const product=P.find(p=>p.id===E.product);text(product.label,72,1481,25,ink);
+   line(72,1505,1008,1505,'#b9b6aa',1);
+   const segment=(E.audio?.segments||[]).filter(s=>t>=s.start).at(-1);
+   const caption=segment?E.captions[segment.index]:'A wallcovering presentation with Wallo.';
+   wrap(caption,72,1570,930,38,ink,'Verdana',55);
+   if(t>=16.8)text(E.takeaway,72,1738,37,teal,'Georgia');
+   wrap(E.note,72,1794,926,24,'#58635b','Verdana',34);
+   text('designerwallcoverings.com',72,1870,25,teal);
+   text(String(ep+1).padStart(2,'0')+' / 05',1008,1870,22,teal,'Verdana','right');
+   rect(72,1894,936,3,'#c9c7bb');rect(72,1894,936*clamp(t/20),3,teal);
+ }
+ function draw(t){
+   telemetry=[];rect(0,0,1080,1920,chalk);
+   c.save();c.beginPath();c.rect(0,437,1080,1000);c.clip();
+   const zoom=1+ease(t,3.3,5)*.08-ease(t,14.2,16)*.06;
+   c.translate(540,950);c.scale(zoom,zoom);c.translate(-540,-487);
+   room(t);documentPanel(t);swatchBoard(t);actionNotes(t);performances(t);
+   c.restore();overlay(t);window.__rigState=telemetry;
+ }
+ const driver={t:0};window.__timelines=window.__timelines||{};
+ window.__timelines.main=gsap.timeline({paused:true}).to(driver,{t:20,duration:20,ease:'none',onUpdate:()=>draw(driver.t)},0);
+ window.__draw=draw;
+ window.__ready=Promise.all(P.map(p=>new Promise((resolve,reject)=>{const im=new Image();images[p.id]=im;im.onload=resolve;im.onerror=()=>reject(new Error('Missing product '+p.id));im.src=p.local;}))).then(()=>document.fonts.ready).then(()=>{draw(driver.t);return true;});
+ draw(0);
+})();
diff --git a/series2/assets/gsap.min.js b/series2/assets/gsap.min.js
new file mode 100644
index 0000000..52de8f8
--- /dev/null
+++ b/series2/assets/gsap.min.js
@@ -0,0 +1,10 @@
+/*!
+ * GSAP 3.14.2
+ * https://gsap.com
+ *
+ * @license Copyright 2025, GreenSock. All rights reserved.
+ * Subject to the terms at https://gsap.com/standard-license.
+ * @author: Jack Doyle, jack@greensock.com
+ */
+
+!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t=t||self).window=t.window||{})}(this,function(e){"use strict";function _inheritsLoose(t,e){t.prototype=Object.create(e.prototype),(t.prototype.constructor=t).__proto__=e}function _assertThisInitialized(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}function r(t){return"string"==typeof t}function s(t){return"function"==typeof t}function t(t){return"number"==typeof t}function u(t){return void 0===t}function v(t){return"object"==typeof t}function w(t){return!1!==t}function x(){return"undefined"!=typeof window}function y(t){return s(t)||r(t)}function R(t){return(i=bt(t,ht))&&Fe}function S(t,e){return console.warn("Invalid property",t,"set to",e,"Missing plugin? gsap.registerPlugin()")}function T(t,e){return!e&&console.warn(t)}function U(t,e){return t&&(ht[t]=e)&&i&&(i[t]=e)||ht}function V(){return 0}function ga(t){var e,r,i=t[0];if(v(i)||s(i)||(t=[t]),!(e=(i._gsap||{}).harness)){for(r=yt.length;r--&&!yt[r].targetTest(i););e=yt[r]}for(r=t.length;r--;)t[r]&&(t[r]._gsap||(t[r]._gsap=new Xt(t[r],e)))||t.splice(r,1);return t}function ha(t){return t._gsap||ga(Pt(t))[0]._gsap}function ia(t,e,r){return(r=t[e])&&s(r)?t[e]():u(r)&&t.getAttribute&&t.getAttribute(e)||r}function ja(t,e){return(t=t.split(",")).forEach(e)||t}function ka(t){return Math.round(1e5*t)/1e5||0}function la(t){return Math.round(1e7*t)/1e7||0}function ma(t,e){var r=e.charAt(0),i=parseFloat(e.substr(2));return t=parseFloat(t),"+"===r?t+i:"-"===r?t-i:"*"===r?t*i:t/i}function na(t,e){for(var r=e.length,i=0;t.indexOf(e[i])<0&&++i<r;);return i<r}function oa(){var t,e,r=pt.length,i=pt.slice(0);for(_t={},t=pt.length=0;t<r;t++)(e=i[t])&&e._lazy&&(e.render(e._lazy[0],e._lazy[1],!0)._lazy=0)}function pa(t){return!!(t._initted||t._startAt||t.add)}function qa(t,e,r,i){pt.length&&!I&&oa(),t.render(e,r,i||!!(I&&e<0&&pa(t))),pt.length&&!I&&oa()}function ra(t){var e=parseFloat(t);return(e||0===e)&&(t+"").match(ot).length<2?e:r(t)?t.trim():t}function sa(t){return t}function ta(t,e){for(var r in e)r in t||(t[r]=e[r]);return t}function wa(t,e){for(var r in e)"__proto__"!==r&&"constructor"!==r&&"prototype"!==r&&(t[r]=v(e[r])?wa(t[r]||(t[r]={}),e[r]):e[r]);return t}function xa(t,e){var r,i={};for(r in t)r in e||(i[r]=t[r]);return i}function ya(t){var e=t.parent||L,r=t.keyframes?function _setKeyframeDefaults(i){return function(t,e){for(var r in e)r in t||"duration"===r&&i||"ease"===r||(t[r]=e[r])}}($(t.keyframes)):ta;if(w(t.inherit))for(;e;)r(t,e.vars.defaults),e=e.parent||e._dp;return t}function Aa(t,e,r,i,n){void 0===r&&(r="_first"),void 0===i&&(i="_last");var a,s=t[i];if(n)for(a=e[n];s&&s[n]>a;)s=s._prev;return s?(e._next=s._next,s._next=e):(e._next=t[r],t[r]=e),e._next?e._next._prev=e:t[i]=e,e._prev=s,e.parent=e._dp=t,e}function Ba(t,e,r,i){void 0===r&&(r="_first"),void 0===i&&(i="_last");var n=e._prev,a=e._next;n?n._next=a:t[r]===e&&(t[r]=a),a?a._prev=n:t[i]===e&&(t[i]=n),e._next=e._prev=e.parent=null}function Ca(t,e){t.parent&&(!e||t.parent.autoRemoveChildren)&&t.parent.remove&&t.parent.remove(t),t._act=0}function Da(t,e){if(t&&(!e||e._end>t._dur||e._start<0))for(var r=t;r;)r._dirty=1,r=r.parent;return t}function Fa(t,e,r,i){return t._startAt&&(I?t._startAt.revert(ft):t.vars.immediateRender&&!t.vars.autoRevert||t._startAt.render(e,!0,i))}function Ha(t){return t._repeat?wt(t._tTime,t=t.duration()+t._rDelay)*t:0}function Ja(t,e){return(t-e._start)*e._ts+(0<=e._ts?0:e._dirty?e.totalDuration():e._tDur)}function Ka(t){return t._end=la(t._start+(t._tDur/Math.abs(t._ts||t._rts||q)||0))}function La(t,e){var r=t._dp;return r&&r.smoothChildTiming&&t._ts&&(t._start=la(r._time-(0<t._ts?e/t._ts:((t._dirty?t.totalDuration():t._tDur)-e)/-t._ts)),Ka(t),r._dirty||Da(r,t)),t}function Ma(t,e){var r;if((e._time||!e._dur&&e._initted||e._start<t._time&&(e._dur||!e.add))&&(r=Ja(t.rawTime(),e),(!e._dur||Mt(0,e.totalDuration(),r)-e._tTime>q)&&e.render(r,!0)),Da(t,e)._dp&&t._initted&&t._time>=t._dur&&t._ts){if(t._dur<t.duration())for(r=t;r._dp;)0<=r.rawTime()&&r.totalTime(r._tTime),r=r._dp;t._zTime=-q}}function Na(e,r,i,n){return r.parent&&Ca(r),r._start=la((t(i)?i:i||e!==L?Ot(e,i,r):e._time)+r._delay),r._end=la(r._start+(r.totalDuration()/Math.abs(r.timeScale())||0)),Aa(e,r,"_first","_last",e._sort?"_start":0),xt(r)||(e._recent=r),n||Ma(e,r),e._ts<0&&La(e,e._tTime),e}function Oa(t,e){return(ht.ScrollTrigger||S("scrollTrigger",e))&&ht.ScrollTrigger.create(e,t)}function Pa(t,e,r,i,n){return Qt(t,e,n),t._initted?!r&&t._pt&&!I&&(t._dur&&!1!==t.vars.lazy||!t._dur&&t.vars.lazy)&&f!==It.frame?(pt.push(t),t._lazy=[n,i],1):void 0:1}function Ua(t,e,r,i){var n=t._repeat,a=la(e)||0,s=t._tTime/t._tDur;return s&&!i&&(t._time*=a/t._dur),t._dur=a,t._tDur=n?n<0?1e10:la(a*(n+1)+t._rDelay*n):a,0<s&&!i&&La(t,t._tTime=t._tDur*s),t.parent&&Ka(t),r||Da(t.parent,t),t}function Va(t){return t instanceof Zt?Da(t):Ua(t,t._dur)}function Ya(e,r,i){var n,a,s=t(r[1]),o=(s?2:1)+(e<2?0:1),u=r[o];if(s&&(u.duration=r[1]),u.parent=i,e){for(n=u,a=i;a&&!("immediateRender"in n);)n=a.vars.defaults||{},a=w(a.vars.inherit)&&a.parent;u.immediateRender=w(n.immediateRender),e<2?u.runBackwards=1:u.startAt=r[o-1]}return new te(r[0],u,r[1+o])}function Za(t,e){return t||0===t?e(t):e}function _a(t,e){return r(t)&&(e=ut.exec(t))?e[1]:""}function cb(t,e){return t&&v(t)&&"length"in t&&(!e&&!t.length||t.length-1 in t&&v(t[0]))&&!t.nodeType&&t!==h}function fb(r){return r=Pt(r)[0]||T("Invalid scope")||{},function(t){var e=r.current||r.nativeElement||r;return Pt(t,e.querySelectorAll?e:e===r?T("Invalid scope")||a.createElement("div"):r)}}function gb(t){return t.sort(function(){return.5-Math.random()})}function hb(t){if(s(t))return t;var p=v(t)?t:{each:t},_=Vt(p.ease),m=p.from||0,g=parseFloat(p.base)||0,y={},e=0<m&&m<1,T=isNaN(m)||e,b=p.axis,w=m,x=m;return r(m)?w=x={center:.5,edges:.5,end:1}[m]||0:!e&&T&&(w=m[0],x=m[1]),function(t,e,r){var i,n,a,s,o,u,h,l,f,d=(r||p).length,c=y[d];if(!c){if(!(f="auto"===p.grid?0:(p.grid||[1,X])[1])){for(h=-X;h<(h=r[f++].getBoundingClientRect().left)&&f<d;);f<d&&f--}for(c=y[d]=[],i=T?Math.min(f,d)*w-.5:m%f,n=f===X?0:T?d*x/f-.5:m/f|0,l=X,u=h=0;u<d;u++)a=u%f-i,s=n-(u/f|0),c[u]=o=b?Math.abs("y"===b?s:a):J(a*a+s*s),h<o&&(h=o),o<l&&(l=o);"random"===m&&gb(c),c.max=h-l,c.min=l,c.v=d=(parseFloat(p.amount)||parseFloat(p.each)*(d<f?d-1:b?"y"===b?d/f:f:Math.max(f,d/f))||0)*("edges"===m?-1:1),c.b=d<0?g-d:g,c.u=_a(p.amount||p.each)||0,_=_&&d<0?jt(_):_}return d=(c[t]-c.min)/c.max||0,la(c.b+(_?_(d):d)*c.v)+c.u}}function ib(i){var n=Math.pow(10,((i+"").split(".")[1]||"").length);return function(e){var r=la(Math.round(parseFloat(e)/i)*i*n);return(r-r%1)/n+(t(e)?0:_a(e))}}function jb(h,e){var l,f,r=$(h);return!r&&v(h)&&(l=r=h.radius||X,h.values?(h=Pt(h.values),(f=!t(h[0]))&&(l*=l)):h=ib(h.increment)),Za(e,r?s(h)?function(t){return f=h(t),Math.abs(f-t)<=l?f:t}:function(e){for(var r,i,n=parseFloat(f?e.x:e),a=parseFloat(f?e.y:0),s=X,o=0,u=h.length;u--;)(r=f?(r=h[u].x-n)*r+(i=h[u].y-a)*i:Math.abs(h[u]-n))<s&&(s=r,o=u);return o=!l||s<=l?h[o]:e,f||o===e||t(e)?o:o+_a(e)}:ib(h))}function kb(t,e,r,i){return Za($(t)?!e:!0===r?!!(r=0):!i,function(){return $(t)?t[~~(Math.random()*t.length)]:(r=r||1e-5)&&(i=r<1?Math.pow(10,(r+"").length-2):1)&&Math.floor(Math.round((t-r/2+Math.random()*(e-t+.99*r))/r)*r*i)/i})}function ob(e,r,t){return Za(t,function(t){return e[~~r(t)]})}function rb(t){return t.replace(tt,function(t){var e=t.indexOf("[")+1,r=t.substring(e||7,e?t.indexOf("]"):t.length-1).split(et);return kb(e?r:+r[0],e?0:+r[1],+r[2]||1e-5)})}function ub(t,e,r){var i,n,a,s=t.labels,o=X;for(i in s)(n=s[i]-e)<0==!!r&&n&&o>(n=Math.abs(n))&&(a=i,o=n);return a}function wb(t){return Ca(t),t.scrollTrigger&&t.scrollTrigger.kill(!!I),t.progress()<1&&Dt(t,"onInterrupt"),t}function zb(t){if(t)if(t=!t.name&&t.default||t,x()||t.headless){var e=t.name,r=s(t),i=e&&!r&&t.init?function(){this._props=[]}:t,n={init:V,render:ve,add:Jt,kill:Te,modifier:ye,rawVars:0},a={targetTest:0,get:0,getSetter:le,aliases:{},register:0};if(Lt(),t!==i){if(mt[e])return;ta(i,ta(xa(t,n),a)),bt(i.prototype,bt(n,xa(t,a))),mt[i.prop=e]=i,t.targetTest&&(yt.push(i),ct[e]=1),e=("css"===e?"CSS":e.charAt(0).toUpperCase()+e.substr(1))+"Plugin"}U(e,i),t.register&&t.register(Fe,i,we)}else St.push(t)}function Cb(t,e,r){return(6*(t+=t<0?1:1<t?-1:0)<1?e+(r-e)*t*6:t<.5?r:3*t<2?e+(r-e)*(2/3-t)*6:e)*zt+.5|0}function Db(e,r,i){var n,a,s,o,u,h,l,f,d,c,p=e?t(e)?[e>>16,e>>8&zt,e&zt]:0:Et.black;if(!p){if(","===e.substr(-1)&&(e=e.substr(0,e.length-1)),Et[e])p=Et[e];else if("#"===e.charAt(0)){if(e.length<6&&(e="#"+(n=e.charAt(1))+n+(a=e.charAt(2))+a+(s=e.charAt(3))+s+(5===e.length?e.charAt(4)+e.charAt(4):"")),9===e.length)return[(p=parseInt(e.substr(1,6),16))>>16,p>>8&zt,p&zt,parseInt(e.substr(7),16)/255];p=[(e=parseInt(e.substr(1),16))>>16,e>>8&zt,e&zt]}else if("hsl"===e.substr(0,3))if(p=c=e.match(rt),r){if(~e.indexOf("="))return p=e.match(it),i&&p.length<4&&(p[3]=1),p}else o=+p[0]%360/360,u=p[1]/100,n=2*(h=p[2]/100)-(a=h<=.5?h*(u+1):h+u-h*u),3<p.length&&(p[3]*=1),p[0]=Cb(o+1/3,n,a),p[1]=Cb(o,n,a),p[2]=Cb(o-1/3,n,a);else p=e.match(rt)||Et.transparent;p=p.map(Number)}return r&&!c&&(n=p[0]/zt,a=p[1]/zt,s=p[2]/zt,h=((l=Math.max(n,a,s))+(f=Math.min(n,a,s)))/2,l===f?o=u=0:(d=l-f,u=.5<h?d/(2-l-f):d/(l+f),o=l===n?(a-s)/d+(a<s?6:0):l===a?(s-n)/d+2:(n-a)/d+4,o*=60),p[0]=~~(o+.5),p[1]=~~(100*u+.5),p[2]=~~(100*h+.5)),i&&p.length<4&&(p[3]=1),p}function Eb(t){var r=[],i=[],n=-1;return t.split(Rt).forEach(function(t){var e=t.match(nt)||[];r.push.apply(r,e),i.push(n+=e.length+1)}),r.c=i,r}function Fb(t,e,r){var i,n,a,s,o="",u=(t+o).match(Rt),h=e?"hsla(":"rgba(",l=0;if(!u)return t;if(u=u.map(function(t){return(t=Db(t,e,1))&&h+(e?t[0]+","+t[1]+"%,"+t[2]+"%,"+t[3]:t.join(","))+")"}),r&&(a=Eb(t),(i=r.c).join(o)!==a.c.join(o)))for(s=(n=t.replace(Rt,"1").split(nt)).length-1;l<s;l++)o+=n[l]+(~i.indexOf(l)?u.shift()||h+"0,0,0,0)":(a.length?a:u.length?u:r).shift());if(!n)for(s=(n=t.split(Rt)).length-1;l<s;l++)o+=n[l]+u[l];return o+n[s]}function Ib(t){var e,r=t.join(" ");if(Rt.lastIndex=0,Rt.test(r))return e=Ft.test(r),t[1]=Fb(t[1],e),t[0]=Fb(t[0],e,Eb(t[1])),!0}function Rb(t){var e=(t+"").split("("),r=Bt[e[0]];return r&&1<e.length&&r.config?r.config.apply(null,~t.indexOf("{")?[function _parseObjectInString(t){for(var e,r,i,n={},a=t.substr(1,t.length-3).split(":"),s=a[0],o=1,u=a.length;o<u;o++)r=a[o],e=o!==u-1?r.lastIndexOf(","):r.length,i=r.substr(0,e),n[s]=isNaN(i)?i.replace(Nt,"").trim():+i,s=r.substr(e+1).trim();return n}(e[1])]:function _valueInParentheses(t){var e=t.indexOf("(")+1,r=t.indexOf(")"),i=t.indexOf("(",e);return t.substring(e,~i&&i<r?t.indexOf(")",r+1):r)}(t).split(",").map(ra)):Bt._CE&&Yt.test(t)?Bt._CE("",t):r}function Tb(t,e){for(var r,i=t._first;i;)i instanceof Zt?Tb(i,e):!i.vars.yoyoEase||i._yoyo&&i._repeat||i._yoyo===e||(i.timeline?Tb(i.timeline,e):(r=i._ease,i._ease=i._yEase,i._yEase=r,i._yoyo=e)),i=i._next}function Vb(t,e,r,i){void 0===r&&(r=function easeOut(t){return 1-e(1-t)}),void 0===i&&(i=function easeInOut(t){return t<.5?e(2*t)/2:1-e(2*(1-t))/2});var n,a={easeIn:e,easeOut:r,easeInOut:i};return ja(t,function(t){for(var e in Bt[t]=ht[t]=a,Bt[n=t.toLowerCase()]=r,a)Bt[n+("easeIn"===e?".in":"easeOut"===e?".out":".inOut")]=Bt[t+"."+e]=a[e]}),a}function Wb(e){return function(t){return t<.5?(1-e(1-2*t))/2:.5+e(2*(t-.5))/2}}function Xb(r,t,e){function Lm(t){return 1===t?1:i*Math.pow(2,-10*t)*G((t-a)*n)+1}var i=1<=t?t:1,n=(e||(r?.3:.45))/(t<1?t:1),a=n/Z*(Math.asin(1/i)||0),s="out"===r?Lm:"in"===r?function(t){return 1-Lm(1-t)}:Wb(Lm);return n=Z/n,s.config=function(t,e){return Xb(r,t,e)},s}function Yb(e,r){function Tm(t){return t?--t*t*((r+1)*t+r)+1:0}void 0===r&&(r=1.70158);var t="out"===e?Tm:"in"===e?function(t){return 1-Tm(1-t)}:Wb(Tm);return t.config=function(t){return Yb(e,t)},t}var F,I,l,L,h,n,a,i,o,f,d,c,p,_,m,g,b,k,O,M,C,P,A,D,z,E,B,Y,N={autoSleep:120,force3D:"auto",nullTargetWarn:1,units:{lineHeight:""}},j={duration:.5,overwrite:!1,delay:0},X=1e8,q=1/X,Z=2*Math.PI,W=Z/4,H=0,J=Math.sqrt,Q=Math.cos,G=Math.sin,K="function"==typeof ArrayBuffer&&ArrayBuffer.isView||function(){},$=Array.isArray,tt=/random\([^)]+\)/g,et=/,\s*/g,rt=/(?:-?\.?\d|\.)+/gi,it=/[-+=.]*\d+[.e\-+]*\d*[e\-+]*\d*/g,nt=/[-+=.]*\d+[.e-]*\d*[a-z%]*/g,at=/[-+=.]*\d+\.?\d*(?:e-|e\+)?\d*/gi,st=/[+-]=-?[.\d]+/,ot=/[^,'"\[\]\s]+/gi,ut=/^[+\-=e\s\d]*\d+[.\d]*([a-z]*|%)\s*$/i,ht={},lt={suppressEvents:!0,isStart:!0,kill:!1},ft={suppressEvents:!0,kill:!1},dt={suppressEvents:!0},ct={},pt=[],_t={},mt={},gt={},vt=30,yt=[],Tt="",bt=function _merge(t,e){for(var r in e)t[r]=e[r];return t},wt=function _animationCycle(t,e){var r=Math.floor(t=la(t/e));return t&&r===t?r-1:r},xt=function _isFromOrFromStart(t){var e=t.data;return"isFromStart"===e||"isStart"===e},kt={_start:0,endTime:V,totalDuration:V},Ot=function _parsePosition(t,e,i){var n,a,s,o=t.labels,u=t._recent||kt,h=t.duration()>=X?u.endTime(!1):t._dur;return r(e)&&(isNaN(e)||e in o)?(a=e.charAt(0),s="%"===e.substr(-1),n=e.indexOf("="),"<"===a||">"===a?(0<=n&&(e=e.replace(/=/,"")),("<"===a?u._start:u.endTime(0<=u._repeat))+(parseFloat(e.substr(1))||0)*(s?(n<0?u:i).totalDuration()/100:1)):n<0?(e in o||(o[e]=h),o[e]):(a=parseFloat(e.charAt(n-1)+e.substr(n+1)),s&&i&&(a=a/100*($(i)?i[0]:i).totalDuration()),1<n?_parsePosition(t,e.substr(0,n-1),i)+a:h+a)):null==e?h:+e},Mt=function _clamp(t,e,r){return r<t?t:e<r?e:r},Ct=[].slice,Pt=function toArray(t,e,i){return l&&!e&&l.selector?l.selector(t):!r(t)||i||!n&&Lt()?$(t)?function _flatten(t,e,i){return void 0===i&&(i=[]),t.forEach(function(t){return r(t)&&!e||cb(t,1)?i.push.apply(i,Pt(t)):i.push(t)})||i}(t,i):cb(t)?Ct.call(t,0):t?[t]:[]:Ct.call((e||a).querySelectorAll(t),0)},At=function mapRange(e,t,r,i,n){var a=t-e,s=i-r;return Za(n,function(t){return r+((t-e)/a*s||0)})},Dt=function _callback(t,e,r){var i,n,a,s=t.vars,o=s[e],u=l,h=t._ctx;if(o)return i=s[e+"Params"],n=s.callbackScope||t,r&&pt.length&&oa(),h&&(l=h),a=i?o.apply(n,i):o.call(n),l=u,a},St=[],zt=255,Et={aqua:[0,zt,zt],lime:[0,zt,0],silver:[192,192,192],black:[0,0,0],maroon:[128,0,0],teal:[0,128,128],blue:[0,0,zt],navy:[0,0,128],white:[zt,zt,zt],olive:[128,128,0],yellow:[zt,zt,0],orange:[zt,165,0],gray:[128,128,128],purple:[128,0,128],green:[0,128,0],red:[zt,0,0],pink:[zt,192,203],cyan:[0,zt,zt],transparent:[zt,zt,zt,0]},Rt=function(){var t,e="(?:\\b(?:(?:rgb|rgba|hsl|hsla)\\(.+?\\))|\\B#(?:[0-9a-f]{3,4}){1,2}\\b";for(t in Et)e+="|"+t+"\\b";return new RegExp(e+")","gi")}(),Ft=/hsl[a]?\(/,It=(O=Date.now,M=500,C=33,P=O(),A=P,z=D=1e3/240,g={time:0,frame:0,tick:function tick(){Al(!0)},deltaRatio:function deltaRatio(t){return b/(1e3/(t||60))},wake:function wake(){o&&(!n&&x()&&(h=n=window,a=h.document||{},ht.gsap=Fe,(h.gsapVersions||(h.gsapVersions=[])).push(Fe.version),R(i||h.GreenSockGlobals||!h.gsap&&h||{}),St.forEach(zb)),m="undefined"!=typeof requestAnimationFrame&&requestAnimationFrame,p&&g.sleep(),_=m||function(t){return setTimeout(t,z-1e3*g.time+1|0)},c=1,Al(2))},sleep:function sleep(){(m?cancelAnimationFrame:clearTimeout)(p),c=0,_=V},lagSmoothing:function lagSmoothing(t,e){M=t||1/0,C=Math.min(e||33,M)},fps:function fps(t){D=1e3/(t||240),z=1e3*g.time+D},add:function add(n,t,e){var a=t?function(t,e,r,i){n(t,e,r,i),g.remove(a)}:n;return g.remove(n),E[e?"unshift":"push"](a),Lt(),a},remove:function remove(t,e){~(e=E.indexOf(t))&&E.splice(e,1)&&e<=k&&k--},_listeners:E=[]}),Lt=function _wake(){return!c&&It.wake()},Bt={},Yt=/^[\d.\-M][\d.\-,\s]/,Nt=/["']/g,jt=function _invertEase(e){return function(t){return 1-e(1-t)}},Vt=function _parseEase(t,e){return t&&(s(t)?t:Bt[t]||Rb(t))||e};function Al(t){var e,r,i,n,a=O()-A,s=!0===t;if((M<a||a<0)&&(P+=a-C),(0<(e=(i=(A+=a)-P)-z)||s)&&(n=++g.frame,b=i-1e3*g.time,g.time=i/=1e3,z+=e+(D<=e?4:D-e),r=1),s||(p=_(Al)),r)for(k=0;k<E.length;k++)E[k](i,b,n,t)}function jn(t){return t<Y?B*t*t:t<.7272727272727273?B*Math.pow(t-1.5/2.75,2)+.75:t<.9090909090909092?B*(t-=2.25/2.75)*t+.9375:B*Math.pow(t-2.625/2.75,2)+.984375}ja("Linear,Quad,Cubic,Quart,Quint,Strong",function(t,e){var r=e<5?e+1:e;Vb(t+",Power"+(r-1),e?function(t){return Math.pow(t,r)}:function(t){return t},function(t){return 1-Math.pow(1-t,r)},function(t){return t<.5?Math.pow(2*t,r)/2:1-Math.pow(2*(1-t),r)/2})}),Bt.Linear.easeNone=Bt.none=Bt.Linear.easeIn,Vb("Elastic",Xb("in"),Xb("out"),Xb()),B=7.5625,Y=1/2.75,Vb("Bounce",function(t){return 1-jn(1-t)},jn),Vb("Expo",function(t){return Math.pow(2,10*(t-1))*t+t*t*t*t*t*t*(1-t)}),Vb("Circ",function(t){return-(J(1-t*t)-1)}),Vb("Sine",function(t){return 1===t?1:1-Q(t*W)}),Vb("Back",Yb("in"),Yb("out"),Yb()),Bt.SteppedEase=Bt.steps=ht.SteppedEase={config:function config(t,e){void 0===t&&(t=1);var r=1/t,i=t+(e?0:1),n=e?1:0;return function(t){return((i*Mt(0,.99999999,t)|0)+n)*r}}},j.ease=Bt["quad.out"],ja("onComplete,onUpdate,onStart,onRepeat,onReverseComplete,onInterrupt",function(t){return Tt+=t+","+t+"Params,"});var Ut,Xt=function GSCache(t,e){this.id=H++,(t._gsap=this).target=t,this.harness=e,this.get=e?e.get:ia,this.set=e?e.getSetter:le},qt=((Ut=Animation.prototype).delay=function delay(t){return t||0===t?(this.parent&&this.parent.smoothChildTiming&&this.startTime(this._start+t-this._delay),this._delay=t,this):this._delay},Ut.duration=function duration(t){return arguments.length?this.totalDuration(0<this._repeat?t+(t+this._rDelay)*this._repeat:t):this.totalDuration()&&this._dur},Ut.totalDuration=function totalDuration(t){return arguments.length?(this._dirty=0,Ua(this,this._repeat<0?t:(t-this._repeat*this._rDelay)/(this._repeat+1))):this._tDur},Ut.totalTime=function totalTime(t,e){if(Lt(),!arguments.length)return this._tTime;var r=this._dp;if(r&&r.smoothChildTiming&&this._ts){for(La(this,t),!r._dp||r.parent||Ma(r,this);r&&r.parent;)r.parent._time!==r._start+(0<=r._ts?r._tTime/r._ts:(r.totalDuration()-r._tTime)/-r._ts)&&r.totalTime(r._tTime,!0),r=r.parent;!this.parent&&this._dp.autoRemoveChildren&&(0<this._ts&&t<this._tDur||this._ts<0&&0<t||!this._tDur&&!t)&&Na(this._dp,this,this._start-this._delay)}return(this._tTime!==t||!this._dur&&!e||this._initted&&Math.abs(this._zTime)===q||!this._initted&&this._dur&&t||!t&&!this._initted&&(this.add||this._ptLookup))&&(this._ts||(this._pTime=t),qa(this,t,e)),this},Ut.time=function time(t,e){return arguments.length?this.totalTime(Math.min(this.totalDuration(),t+Ha(this))%(this._dur+this._rDelay)||(t?this._dur:0),e):this._time},Ut.totalProgress=function totalProgress(t,e){return arguments.length?this.totalTime(this.totalDuration()*t,e):this.totalDuration()?Math.min(1,this._tTime/this._tDur):0<=this.rawTime()&&this._initted?1:0},Ut.progress=function progress(t,e){return arguments.length?this.totalTime(this.duration()*(!this._yoyo||1&this.iteration()?t:1-t)+Ha(this),e):this.duration()?Math.min(1,this._time/this._dur):0<this.rawTime()?1:0},Ut.iteration=function iteration(t,e){var r=this.duration()+this._rDelay;return arguments.length?this.totalTime(this._time+(t-1)*r,e):this._repeat?wt(this._tTime,r)+1:1},Ut.timeScale=function timeScale(t,e){if(!arguments.length)return this._rts===-q?0:this._rts;if(this._rts===t)return this;var r=this.parent&&this._ts?Ja(this.parent._time,this):this._tTime;return this._rts=+t||0,this._ts=this._ps||t===-q?0:this._rts,this.totalTime(Mt(-Math.abs(this._delay),this.totalDuration(),r),!1!==e),Ka(this),function _recacheAncestors(t){for(var e=t.parent;e&&e.parent;)e._dirty=1,e.totalDuration(),e=e.parent;return t}(this)},Ut.paused=function paused(t){return arguments.length?(this._ps!==t&&((this._ps=t)?(this._pTime=this._tTime||Math.max(-this._delay,this.rawTime()),this._ts=this._act=0):(Lt(),this._ts=this._rts,this.totalTime(this.parent&&!this.parent.smoothChildTiming?this.rawTime():this._tTime||this._pTime,1===this.progress()&&Math.abs(this._zTime)!==q&&(this._tTime-=q)))),this):this._ps},Ut.startTime=function startTime(t){if(arguments.length){this._start=la(t);var e=this.parent||this._dp;return!e||!e._sort&&this.parent||Na(e,this,this._start-this._delay),this}return this._start},Ut.endTime=function endTime(t){return this._start+(w(t)?this.totalDuration():this.duration())/Math.abs(this._ts||1)},Ut.rawTime=function rawTime(t){var e=this.parent||this._dp;return e?t&&(!this._ts||this._repeat&&this._time&&this.totalProgress()<1)?this._tTime%(this._dur+this._rDelay):this._ts?Ja(e.rawTime(t),this):this._tTime:this._tTime},Ut.revert=function revert(t){void 0===t&&(t=dt);var e=I;return I=t,pa(this)&&(this.timeline&&this.timeline.revert(t),this.totalTime(-.01,t.suppressEvents)),"nested"!==this.data&&!1!==t.kill&&this.kill(),I=e,this},Ut.globalTime=function globalTime(t){for(var e=this,r=arguments.length?t:e.rawTime();e;)r=e._start+r/(Math.abs(e._ts)||1),e=e._dp;return!this.parent&&this._sat?this._sat.globalTime(t):r},Ut.repeat=function repeat(t){return arguments.length?(this._repeat=t===1/0?-2:t,Va(this)):-2===this._repeat?1/0:this._repeat},Ut.repeatDelay=function repeatDelay(t){if(arguments.length){var e=this._time;return this._rDelay=t,Va(this),e?this.time(e):this}return this._rDelay},Ut.yoyo=function yoyo(t){return arguments.length?(this._yoyo=t,this):this._yoyo},Ut.seek=function seek(t,e){return this.totalTime(Ot(this,t),w(e))},Ut.restart=function restart(t,e){return this.play().totalTime(t?-this._delay:0,w(e)),this._dur||(this._zTime=-q),this},Ut.play=function play(t,e){return null!=t&&this.seek(t,e),this.reversed(!1).paused(!1)},Ut.reverse=function reverse(t,e){return null!=t&&this.seek(t||this.totalDuration(),e),this.reversed(!0).paused(!1)},Ut.pause=function pause(t,e){return null!=t&&this.seek(t,e),this.paused(!0)},Ut.resume=function resume(){return this.paused(!1)},Ut.reversed=function reversed(t){return arguments.length?(!!t!==this.reversed()&&this.timeScale(-this._rts||(t?-q:0)),this):this._rts<0},Ut.invalidate=function invalidate(){return this._initted=this._act=0,this._zTime=-q,this},Ut.isActive=function isActive(){var t,e=this.parent||this._dp,r=this._start;return!(e&&!(this._ts&&this._initted&&e.isActive()&&(t=e.rawTime(!0))>=r&&t<this.endTime(!0)-q))},Ut.eventCallback=function eventCallback(t,e,r){var i=this.vars;return 1<arguments.length?(e?(i[t]=e,r&&(i[t+"Params"]=r),"onUpdate"===t&&(this._onUpdate=e)):delete i[t],this):i[t]},Ut.then=function then(t){var i=this,n=i._prom;return new Promise(function(e){function Fo(){var t=i.then;i.then=null,n&&n(),s(r)&&(r=r(i))&&(r.then||r===i)&&(i.then=t),e(r),i.then=t}var r=s(t)?t:sa;i._initted&&1===i.totalProgress()&&0<=i._ts||!i._tTime&&i._ts<0?Fo():i._prom=Fo})},Ut.kill=function kill(){wb(this)},Animation);function Animation(t){this.vars=t,this._delay=+t.delay||0,(this._repeat=t.repeat===1/0?-2:t.repeat||0)&&(this._rDelay=t.repeatDelay||0,this._yoyo=!!t.yoyo||!!t.yoyoEase),this._ts=1,Ua(this,+t.duration,1,1),this.data=t.data,l&&(this._ctx=l).data.push(this),c||It.wake()}ta(qt.prototype,{_time:0,_start:0,_end:0,_tTime:0,_tDur:0,_dirty:0,_repeat:0,_yoyo:!1,parent:null,_initted:!1,_rDelay:0,_ts:1,_dp:0,ratio:0,_zTime:-q,_prom:0,_ps:!1,_rts:1});var Zt=function(i){function Timeline(t,e){var r;return void 0===t&&(t={}),(r=i.call(this,t)||this).labels={},r.smoothChildTiming=!!t.smoothChildTiming,r.autoRemoveChildren=!!t.autoRemoveChildren,r._sort=w(t.sortChildren),L&&Na(t.parent||L,_assertThisInitialized(r),e),t.reversed&&r.reverse(),t.paused&&r.paused(!0),t.scrollTrigger&&Oa(_assertThisInitialized(r),t.scrollTrigger),r}_inheritsLoose(Timeline,i);var e=Timeline.prototype;return e.to=function to(t,e,r){return Ya(0,arguments,this),this},e.from=function from(t,e,r){return Ya(1,arguments,this),this},e.fromTo=function fromTo(t,e,r,i){return Ya(2,arguments,this),this},e.set=function set(t,e,r){return e.duration=0,e.parent=this,ya(e).repeatDelay||(e.repeat=0),e.immediateRender=!!e.immediateRender,new te(t,e,Ot(this,r),1),this},e.call=function call(t,e,r){return Na(this,te.delayedCall(0,t,e),r)},e.staggerTo=function staggerTo(t,e,r,i,n,a,s){return r.duration=e,r.stagger=r.stagger||i,r.onComplete=a,r.onCompleteParams=s,r.parent=this,new te(t,r,Ot(this,n)),this},e.staggerFrom=function staggerFrom(t,e,r,i,n,a,s){return r.runBackwards=1,ya(r).immediateRender=w(r.immediateRender),this.staggerTo(t,e,r,i,n,a,s)},e.staggerFromTo=function staggerFromTo(t,e,r,i,n,a,s,o){return i.startAt=r,ya(i).immediateRender=w(i.immediateRender),this.staggerTo(t,e,i,n,a,s,o)},e.render=function render(t,e,r){var i,n,a,s,o,u,h,l,f,d,c,p,_=this._time,m=this._dirty?this.totalDuration():this._tDur,g=this._dur,v=t<=0?0:la(t),y=this._zTime<0!=t<0&&(this._initted||!g);if(this!==L&&m<v&&0<=t&&(v=m),v!==this._tTime||r||y){if(_!==this._time&&g&&(v+=this._time-_,t+=this._time-_),i=v,f=this._start,u=!(l=this._ts),y&&(g||(_=this._zTime),!t&&e||(this._zTime=t)),this._repeat){if(c=this._yoyo,o=g+this._rDelay,this._repeat<-1&&t<0)return this.totalTime(100*o+t,e,r);if(i=la(v%o),v===m?(s=this._repeat,i=g):((s=~~(d=la(v/o)))&&s===d&&(i=g,s--),g<i&&(i=g)),d=wt(this._tTime,o),!_&&this._tTime&&d!==s&&this._tTime-d*o-this._dur<=0&&(d=s),c&&1&s&&(i=g-i,p=1),s!==d&&!this._lock){var T=c&&1&d,b=T===(c&&1&s);if(s<d&&(T=!T),_=T?0:v%g?g:v,this._lock=1,this.render(_||(p?0:la(s*o)),e,!g)._lock=0,this._tTime=v,!e&&this.parent&&Dt(this,"onRepeat"),this.vars.repeatRefresh&&!p&&(this.invalidate()._lock=1,d=s),_&&_!==this._time||u!=!this._ts||this.vars.onRepeat&&!this.parent&&!this._act)return this;if(g=this._dur,m=this._tDur,b&&(this._lock=2,_=T?g:-1e-4,this.render(_,!0),this.vars.repeatRefresh&&!p&&this.invalidate()),this._lock=0,!this._ts&&!u)return this;Tb(this,p)}}if(this._hasPause&&!this._forcing&&this._lock<2&&(h=function _findNextPauseTween(t,e,r){var i;if(e<r)for(i=t._first;i&&i._start<=r;){if("isPause"===i.data&&i._start>e)return i;i=i._next}else for(i=t._last;i&&i._start>=r;){if("isPause"===i.data&&i._start<e)return i;i=i._prev}}(this,la(_),la(i)))&&(v-=i-(i=h._start)),this._tTime=v,this._time=i,this._act=!l,this._initted||(this._onUpdate=this.vars.onUpdate,this._initted=1,this._zTime=t,_=0),!_&&v&&g&&!e&&!d&&(Dt(this,"onStart"),this._tTime!==v))return this;if(_<=i&&0<=t)for(n=this._first;n;){if(a=n._next,(n._act||i>=n._start)&&n._ts&&h!==n){if(n.parent!==this)return this.render(t,e,r);if(n.render(0<n._ts?(i-n._start)*n._ts:(n._dirty?n.totalDuration():n._tDur)+(i-n._start)*n._ts,e,r),i!==this._time||!this._ts&&!u){h=0,a&&(v+=this._zTime=-q);break}}n=a}else{n=this._last;for(var w=t<0?t:i;n;){if(a=n._prev,(n._act||w<=n._end)&&n._ts&&h!==n){if(n.parent!==this)return this.render(t,e,r);if(n.render(0<n._ts?(w-n._start)*n._ts:(n._dirty?n.totalDuration():n._tDur)+(w-n._start)*n._ts,e,r||I&&pa(n)),i!==this._time||!this._ts&&!u){h=0,a&&(v+=this._zTime=w?-q:q);break}}n=a}}if(h&&!e&&(this.pause(),h.render(_<=i?0:-q)._zTime=_<=i?1:-1,this._ts))return this._start=f,Ka(this),this.render(t,e,r);this._onUpdate&&!e&&Dt(this,"onUpdate",!0),(v===m&&this._tTime>=this.totalDuration()||!v&&_)&&(f!==this._start&&Math.abs(l)===Math.abs(this._ts)||this._lock||(!t&&g||!(v===m&&0<this._ts||!v&&this._ts<0)||Ca(this,1),e||t<0&&!_||!v&&!_&&m||(Dt(this,v===m&&0<=t?"onComplete":"onReverseComplete",!0),!this._prom||v<m&&0<this.timeScale()||this._prom())))}return this},e.add=function add(e,i){var n=this;if(t(i)||(i=Ot(this,i,e)),!(e instanceof qt)){if($(e))return e.forEach(function(t){return n.add(t,i)}),this;if(r(e))return this.addLabel(e,i);if(!s(e))return this;e=te.delayedCall(0,e)}return this!==e?Na(this,e,i):this},e.getChildren=function getChildren(t,e,r,i){void 0===t&&(t=!0),void 0===e&&(e=!0),void 0===r&&(r=!0),void 0===i&&(i=-X);for(var n=[],a=this._first;a;)a._start>=i&&(a instanceof te?e&&n.push(a):(r&&n.push(a),t&&n.push.apply(n,a.getChildren(!0,e,r)))),a=a._next;return n},e.getById=function getById(t){for(var e=this.getChildren(1,1,1),r=e.length;r--;)if(e[r].vars.id===t)return e[r]},e.remove=function remove(t){return r(t)?this.removeLabel(t):s(t)?this.killTweensOf(t):(t.parent===this&&Ba(this,t),t===this._recent&&(this._recent=this._last),Da(this))},e.totalTime=function totalTime(t,e){return arguments.length?(this._forcing=1,!this._dp&&this._ts&&(this._start=la(It.time-(0<this._ts?t/this._ts:(this.totalDuration()-t)/-this._ts))),i.prototype.totalTime.call(this,t,e),this._forcing=0,this):this._tTime},e.addLabel=function addLabel(t,e){return this.labels[t]=Ot(this,e),this},e.removeLabel=function removeLabel(t){return delete this.labels[t],this},e.addPause=function addPause(t,e,r){var i=te.delayedCall(0,e||V,r);return i.data="isPause",this._hasPause=1,Na(this,i,Ot(this,t))},e.removePause=function removePause(t){var e=this._first;for(t=Ot(this,t);e;)e._start===t&&"isPause"===e.data&&Ca(e),e=e._next},e.killTweensOf=function killTweensOf(t,e,r){for(var i=this.getTweensOf(t,r),n=i.length;n--;)Wt!==i[n]&&i[n].kill(t,e);return this},e.getTweensOf=function getTweensOf(e,r){for(var i,n=[],a=Pt(e),s=this._first,o=t(r);s;)s instanceof te?na(s._targets,a)&&(o?(!Wt||s._initted&&s._ts)&&s.globalTime(0)<=r&&s.globalTime(s.totalDuration())>r:!r||s.isActive())&&n.push(s):(i=s.getTweensOf(a,r)).length&&n.push.apply(n,i),s=s._next;return n},e.tweenTo=function tweenTo(t,e){e=e||{};var r,i=this,n=Ot(i,t),a=e.startAt,s=e.onStart,o=e.onStartParams,u=e.immediateRender,h=te.to(i,ta({ease:e.ease||"none",lazy:!1,immediateRender:!1,time:n,overwrite:"auto",duration:e.duration||Math.abs((n-(a&&"time"in a?a.time:i._time))/i.timeScale())||q,onStart:function onStart(){if(i.pause(),!r){var t=e.duration||Math.abs((n-(a&&"time"in a?a.time:i._time))/i.timeScale());h._dur!==t&&Ua(h,t,0,1).render(h._time,!0,!0),r=1}s&&s.apply(h,o||[])}},e));return u?h.render(0):h},e.tweenFromTo=function tweenFromTo(t,e,r){return this.tweenTo(e,ta({startAt:{time:Ot(this,t)}},r))},e.recent=function recent(){return this._recent},e.nextLabel=function nextLabel(t){return void 0===t&&(t=this._time),ub(this,Ot(this,t))},e.previousLabel=function previousLabel(t){return void 0===t&&(t=this._time),ub(this,Ot(this,t),1)},e.currentLabel=function currentLabel(t){return arguments.length?this.seek(t,!0):this.previousLabel(this._time+q)},e.shiftChildren=function shiftChildren(t,e,r){void 0===r&&(r=0);var i,n=this._first,a=this.labels;for(t=la(t);n;)n._start>=r&&(n._start+=t,n._end+=t),n=n._next;if(e)for(i in a)a[i]>=r&&(a[i]+=t);return Da(this)},e.invalidate=function invalidate(t){var e=this._first;for(this._lock=0;e;)e.invalidate(t),e=e._next;return i.prototype.invalidate.call(this,t)},e.clear=function clear(t){void 0===t&&(t=!0);for(var e,r=this._first;r;)e=r._next,this.remove(r),r=e;return this._dp&&(this._time=this._tTime=this._pTime=0),t&&(this.labels={}),Da(this)},e.totalDuration=function totalDuration(t){var e,r,i,n=0,a=this,s=a._last,o=X;if(arguments.length)return a.timeScale((a._repeat<0?a.duration():a.totalDuration())/(a.reversed()?-t:t));if(a._dirty){for(i=a.parent;s;)e=s._prev,s._dirty&&s.totalDuration(),o<(r=s._start)&&a._sort&&s._ts&&!a._lock?(a._lock=1,Na(a,s,r-s._delay,1)._lock=0):o=r,r<0&&s._ts&&(n-=r,(!i&&!a._dp||i&&i.smoothChildTiming)&&(a._start+=la(r/a._ts),a._time-=r,a._tTime-=r),a.shiftChildren(-r,!1,-Infinity),o=0),s._end>n&&s._ts&&(n=s._end),s=e;Ua(a,a===L&&a._time>n?a._time:n,1,1),a._dirty=0}return a._tDur},Timeline.updateRoot=function updateRoot(t){if(L._ts&&(qa(L,Ja(t,L)),f=It.frame),It.frame>=vt){vt+=N.autoSleep||120;var e=L._first;if((!e||!e._ts)&&N.autoSleep&&It._listeners.length<2){for(;e&&!e._ts;)e=e._next;e||It.sleep()}}},Timeline}(qt);ta(Zt.prototype,{_lock:0,_hasPause:0,_forcing:0});function dc(t,e,i,n,a,o){var u,h,l,f;if(mt[t]&&!1!==(u=new mt[t]).init(a,u.rawVars?e[t]:function _processVars(t,e,i,n,a){if(s(t)&&(t=Gt(t,a,e,i,n)),!v(t)||t.style&&t.nodeType||$(t)||K(t))return r(t)?Gt(t,a,e,i,n):t;var o,u={};for(o in t)u[o]=Gt(t[o],a,e,i,n);return u}(e[t],n,a,o,i),i,n,o)&&(i._pt=h=new we(i._pt,a,t,0,1,u.render,u,0,u.priority),i!==d))for(l=i._ptLookup[i._targets.indexOf(a)],f=u._props.length;f--;)l[u._props[f]]=h;return u}function jc(t,r,e,i){var n,a,s=r.ease||i||"power1.inOut";if($(r))a=e[t]||(e[t]=[]),r.forEach(function(t,e){return a.push({t:e/(r.length-1)*100,v:t,e:s})});else for(n in r)a=e[n]||(e[n]=[]),"ease"===n||a.push({t:parseFloat(t),v:r[n],e:s})}var Wt,Ht,Jt=function _addPropTween(t,e,i,n,a,o,u,h,l,f){s(n)&&(n=n(a||0,t,o));var d,c=t[e],p="get"!==i?i:s(c)?l?t[e.indexOf("set")||!s(t["get"+e.substr(3)])?e:"get"+e.substr(3)](l):t[e]():c,_=s(c)?l?ue:re:ee;if(r(n)&&(~n.indexOf("random(")&&(n=rb(n)),"="===n.charAt(1)&&(!(d=ma(p,n)+(_a(p)||0))&&0!==d||(n=d))),!f||p!==n||Ht)return isNaN(p*n)||""===n?(c||e in t||S(e,n),function _addComplexStringPropTween(t,e,r,i,n,a,s){var o,u,h,l,f,d,c,p,_=new we(this._pt,t,e,0,1,ge,null,n),m=0,g=0;for(_.b=r,_.e=i,r+="",(c=~(i+="").indexOf("random("))&&(i=rb(i)),a&&(a(p=[r,i],t,e),r=p[0],i=p[1]),u=r.match(at)||[];o=at.exec(i);)l=o[0],f=i.substring(m,o.index),h?h=(h+1)%5:"rgba("===f.substr(-5)&&(h=1),l!==u[g++]&&(d=parseFloat(u[g-1])||0,_._pt={_next:_._pt,p:f||1===g?f:",",s:d,c:"="===l.charAt(1)?ma(d,l)-d:parseFloat(l)-d,m:h&&h<4?Math.round:0},m=at.lastIndex);return _.c=m<i.length?i.substring(m,i.length):"",_.fp=s,(st.test(i)||c)&&(_.e=0),this._pt=_}.call(this,t,e,p,n,_,h||N.stringFilter,l)):(d=new we(this._pt,t,e,+p||0,n-(p||0),"boolean"==typeof c?_e:ce,0,_),l&&(d.fp=l),u&&d.modifier(u,this,t),this._pt=d)},Qt=function _initTween(t,e,r){var i,n,a,s,o,u,h,l,f,d,c,p,_,m=t.vars,g=m.ease,v=m.startAt,y=m.immediateRender,T=m.lazy,b=m.onUpdate,x=m.runBackwards,k=m.yoyoEase,O=m.keyframes,M=m.autoRevert,C=t._dur,P=t._startAt,A=t._targets,D=t.parent,S=D&&"nested"===D.data?D.vars.targets:A,z="auto"===t._overwrite&&!F,E=t.timeline;if(!E||O&&g||(g="none"),t._ease=Vt(g,j.ease),t._yEase=k?jt(Vt(!0===k?g:k,j.ease)):0,k&&t._yoyo&&!t._repeat&&(k=t._yEase,t._yEase=t._ease,t._ease=k),t._from=!E&&!!m.runBackwards,!E||O&&!m.stagger){if(p=(l=A[0]?ha(A[0]).harness:0)&&m[l.prop],i=xa(m,ct),P&&(P._zTime<0&&P.progress(1),e<0&&x&&y&&!M?P.render(-1,!0):P.revert(x&&C?ft:lt),P._lazy=0),v){if(Ca(t._startAt=te.set(A,ta({data:"isStart",overwrite:!1,parent:D,immediateRender:!0,lazy:!P&&w(T),startAt:null,delay:0,onUpdate:b&&function(){return Dt(t,"onUpdate")},stagger:0},v))),t._startAt._dp=0,t._startAt._sat=t,e<0&&(I||!y&&!M)&&t._startAt.revert(ft),y&&C&&e<=0&&r<=0)return void(e&&(t._zTime=e))}else if(x&&C&&!P)if(e&&(y=!1),a=ta({overwrite:!1,data:"isFromStart",lazy:y&&!P&&w(T),immediateRender:y,stagger:0,parent:D},i),p&&(a[l.prop]=p),Ca(t._startAt=te.set(A,a)),t._startAt._dp=0,t._startAt._sat=t,e<0&&(I?t._startAt.revert(ft):t._startAt.render(-1,!0)),t._zTime=e,y){if(!e)return}else _initTween(t._startAt,q,q);for(t._pt=t._ptCache=0,T=C&&w(T)||T&&!C,n=0;n<A.length;n++){if(h=(o=A[n])._gsap||ga(A)[n]._gsap,t._ptLookup[n]=d={},_t[h.id]&&pt.length&&oa(),c=S===A?n:S.indexOf(o),l&&!1!==(f=new l).init(o,p||i,t,c,S)&&(t._pt=s=new we(t._pt,o,f.name,0,1,f.render,f,0,f.priority),f._props.forEach(function(t){d[t]=s}),f.priority&&(u=1)),!l||p)for(a in i)mt[a]&&(f=dc(a,i,t,c,o,S))?f.priority&&(u=1):d[a]=s=Jt.call(t,o,a,"get",i[a],c,S,0,m.stringFilter);t._op&&t._op[n]&&t.kill(o,t._op[n]),z&&t._pt&&(Wt=t,L.killTweensOf(o,d,t.globalTime(e)),_=!t.parent,Wt=0),t._pt&&T&&(_t[h.id]=1)}u&&be(t),t._onInit&&t._onInit(t)}t._onUpdate=b,t._initted=(!t._op||t._pt)&&!_,O&&e<=0&&E.render(X,!0,!0)},Gt=function _parseFuncOrString(t,e,i,n,a){return s(t)?t.call(e,i,n,a):r(t)&&~t.indexOf("random(")?rb(t):t},Kt=Tt+"repeat,repeatDelay,yoyo,repeatRefresh,yoyoEase,autoRevert",$t={};ja(Kt+",id,stagger,delay,duration,paused,scrollTrigger",function(t){return $t[t]=1});var te=function(R){function Tween(e,r,i,n){var a;"number"==typeof r&&(i.duration=r,r=i,i=null);var s,o,u,h,l,f,d,c,p=(a=R.call(this,n?r:ya(r))||this).vars,_=p.duration,m=p.delay,g=p.immediateRender,b=p.stagger,x=p.overwrite,k=p.keyframes,O=p.defaults,M=p.scrollTrigger,C=p.yoyoEase,P=r.parent||L,A=($(e)||K(e)?t(e[0]):"length"in r)?[e]:Pt(e);if(a._targets=A.length?ga(A):T("GSAP target "+e+" not found. https://gsap.com",!N.nullTargetWarn)||[],a._ptLookup=[],a._overwrite=x,k||b||y(_)||y(m)){if(r=a.vars,(s=a.timeline=new Zt({data:"nested",defaults:O||{},targets:P&&"nested"===P.data?P.vars.targets:A})).kill(),s.parent=s._dp=_assertThisInitialized(a),s._start=0,b||y(_)||y(m)){if(h=A.length,d=b&&hb(b),v(b))for(l in b)~Kt.indexOf(l)&&((c=c||{})[l]=b[l]);for(o=0;o<h;o++)(u=xa(r,$t)).stagger=0,C&&(u.yoyoEase=C),c&&bt(u,c),f=A[o],u.duration=+Gt(_,_assertThisInitialized(a),o,f,A),u.delay=(+Gt(m,_assertThisInitialized(a),o,f,A)||0)-a._delay,!b&&1===h&&u.delay&&(a._delay=m=u.delay,a._start+=m,u.delay=0),s.to(f,u,d?d(o,f,A):0),s._ease=Bt.none;s.duration()?_=m=0:a.timeline=0}else if(k){ya(ta(s.vars.defaults,{ease:"none"})),s._ease=Vt(k.ease||r.ease||"none");var D,S,z,E=0;if($(k))k.forEach(function(t){return s.to(A,t,">")}),s.duration();else{for(l in u={},k)"ease"===l||"easeEach"===l||jc(l,k[l],u,k.easeEach);for(l in u)for(D=u[l].sort(function(t,e){return t.t-e.t}),o=E=0;o<D.length;o++)(z={ease:(S=D[o]).e,duration:(S.t-(o?D[o-1].t:0))/100*_})[l]=S.v,s.to(A,z,E),E+=z.duration;s.duration()<_&&s.to({},{duration:_-s.duration()})}}_||a.duration(_=s.duration())}else a.timeline=0;return!0!==x||F||(Wt=_assertThisInitialized(a),L.killTweensOf(A),Wt=0),Na(P,_assertThisInitialized(a),i),r.reversed&&a.reverse(),r.paused&&a.paused(!0),(g||!_&&!k&&a._start===la(P._time)&&w(g)&&function _hasNoPausedAncestors(t){return!t||t._ts&&_hasNoPausedAncestors(t.parent)}(_assertThisInitialized(a))&&"nested"!==P.data)&&(a._tTime=-q,a.render(Math.max(0,-m)||0)),M&&Oa(_assertThisInitialized(a),M),a}_inheritsLoose(Tween,R);var e=Tween.prototype;return e.render=function render(t,e,r){var i,n,a,s,o,u,h,l,f,d=this._time,c=this._tDur,p=this._dur,_=t<0,m=c-q<t&&!_?c:t<q?0:t;if(p){if(m!==this._tTime||!t||r||!this._initted&&this._tTime||this._startAt&&this._zTime<0!=_||this._lazy){if(i=m,l=this.timeline,this._repeat){if(s=p+this._rDelay,this._repeat<-1&&_)return this.totalTime(100*s+t,e,r);if(i=la(m%s),m===c?(a=this._repeat,i=p):(a=~~(o=la(m/s)))&&a===o?(i=p,a--):p<i&&(i=p),(u=this._yoyo&&1&a)&&(f=this._yEase,i=p-i),o=wt(this._tTime,s),i===d&&!r&&this._initted&&a===o)return this._tTime=m,this;a!==o&&(l&&this._yEase&&Tb(l,u),this.vars.repeatRefresh&&!u&&!this._lock&&i!==s&&this._initted&&(this._lock=r=1,this.render(la(s*a),!0).invalidate()._lock=0))}if(!this._initted){if(Pa(this,_?t:i,r,e,m))return this._tTime=0,this;if(!(d===this._time||r&&this.vars.repeatRefresh&&a!==o))return this;if(p!==this._dur)return this.render(t,e,r)}if(this._tTime=m,this._time=i,!this._act&&this._ts&&(this._act=1,this._lazy=0),this.ratio=h=(f||this._ease)(i/p),this._from&&(this.ratio=h=1-h),!d&&m&&!e&&!o&&(Dt(this,"onStart"),this._tTime!==m))return this;for(n=this._pt;n;)n.r(h,n.d),n=n._next;l&&l.render(t<0?t:l._dur*l._ease(i/this._dur),e,r)||this._startAt&&(this._zTime=t),this._onUpdate&&!e&&(_&&Fa(this,t,0,r),Dt(this,"onUpdate")),this._repeat&&a!==o&&this.vars.onRepeat&&!e&&this.parent&&Dt(this,"onRepeat"),m!==this._tDur&&m||this._tTime!==m||(_&&!this._onUpdate&&Fa(this,t,0,!0),!t&&p||!(m===this._tDur&&0<this._ts||!m&&this._ts<0)||Ca(this,1),e||_&&!d||!(m||d||u)||(Dt(this,m===c?"onComplete":"onReverseComplete",!0),!this._prom||m<c&&0<this.timeScale()||this._prom()))}}else!function _renderZeroDurationTween(t,e,r,i){var n,a,s,o=t.ratio,u=e<0||!e&&(!t._start&&function _parentPlayheadIsBeforeStart(t){var e=t.parent;return e&&e._ts&&e._initted&&!e._lock&&(e.rawTime()<0||_parentPlayheadIsBeforeStart(e))}(t)&&(t._initted||!xt(t))||(t._ts<0||t._dp._ts<0)&&!xt(t))?0:1,h=t._rDelay,l=0;if(h&&t._repeat&&(l=Mt(0,t._tDur,e),a=wt(l,h),t._yoyo&&1&a&&(u=1-u),a!==wt(t._tTime,h)&&(o=1-u,t.vars.repeatRefresh&&t._initted&&t.invalidate())),u!==o||I||i||t._zTime===q||!e&&t._zTime){if(!t._initted&&Pa(t,e,i,r,l))return;for(s=t._zTime,t._zTime=e||(r?q:0),r=r||e&&!s,t.ratio=u,t._from&&(u=1-u),t._time=0,t._tTime=l,n=t._pt;n;)n.r(u,n.d),n=n._next;e<0&&Fa(t,e,0,!0),t._onUpdate&&!r&&Dt(t,"onUpdate"),l&&t._repeat&&!r&&t.parent&&Dt(t,"onRepeat"),(e>=t._tDur||e<0)&&t.ratio===u&&(u&&Ca(t,1),r||I||(Dt(t,u?"onComplete":"onReverseComplete",!0),t._prom&&t._prom()))}else t._zTime||(t._zTime=e)}(this,t,e,r);return this},e.targets=function targets(){return this._targets},e.invalidate=function invalidate(t){return t&&this.vars.runBackwards||(this._startAt=0),this._pt=this._op=this._onUpdate=this._lazy=this.ratio=0,this._ptLookup=[],this.timeline&&this.timeline.invalidate(t),R.prototype.invalidate.call(this,t)},e.resetTo=function resetTo(t,e,r,i,n){c||It.wake(),this._ts||this.play();var a,s=Math.min(this._dur,(this._dp._time-this._start)*this._ts);return this._initted||Qt(this,s),a=this._ease(s/this._dur),function _updatePropTweens(t,e,r,i,n,a,s,o){var u,h,l,f,d=(t._pt&&t._ptCache||(t._ptCache={}))[e];if(!d)for(d=t._ptCache[e]=[],l=t._ptLookup,f=t._targets.length;f--;){if((u=l[f][e])&&u.d&&u.d._pt)for(u=u.d._pt;u&&u.p!==e&&u.fp!==e;)u=u._next;if(!u)return Ht=1,t.vars[e]="+=0",Qt(t,s),Ht=0,o?T(e+" not eligible for reset"):1;d.push(u)}for(f=d.length;f--;)(u=(h=d[f])._pt||h).s=!i&&0!==i||n?u.s+(i||0)+a*u.c:i,u.c=r-u.s,h.e&&(h.e=ka(r)+_a(h.e)),h.b&&(h.b=u.s+_a(h.b))}(this,t,e,r,i,a,s,n)?this.resetTo(t,e,r,i,1):(La(this,0),this.parent||Aa(this._dp,this,"_first","_last",this._dp._sort?"_start":0),this.render(0))},e.kill=function kill(t,e){if(void 0===e&&(e="all"),!(t||e&&"all"!==e))return this._lazy=this._pt=0,this.parent?wb(this):this.scrollTrigger&&this.scrollTrigger.kill(!!I),this;if(this.timeline){var i=this.timeline.totalDuration();return this.timeline.killTweensOf(t,e,Wt&&!0!==Wt.vars.overwrite)._first||wb(this),this.parent&&i!==this.timeline.totalDuration()&&Ua(this,this._dur*this.timeline._tDur/i,0,1),this}var n,a,s,o,u,h,l,f=this._targets,d=t?Pt(t):f,c=this._ptLookup,p=this._pt;if((!e||"all"===e)&&function _arraysMatch(t,e){for(var r=t.length,i=r===e.length;i&&r--&&t[r]===e[r];);return r<0}(f,d))return"all"===e&&(this._pt=0),wb(this);for(n=this._op=this._op||[],"all"!==e&&(r(e)&&(u={},ja(e,function(t){return u[t]=1}),e=u),e=function _addAliasesToVars(t,e){var r,i,n,a,s=t[0]?ha(t[0]).harness:0,o=s&&s.aliases;if(!o)return e;for(i in r=bt({},e),o)if(i in r)for(n=(a=o[i].split(",")).length;n--;)r[a[n]]=r[i];return r}(f,e)),l=f.length;l--;)if(~d.indexOf(f[l]))for(u in a=c[l],"all"===e?(n[l]=e,o=a,s={}):(s=n[l]=n[l]||{},o=e),o)(h=a&&a[u])&&("kill"in h.d&&!0!==h.d.kill(u)||Ba(this,h,"_pt"),delete a[u]),"all"!==s&&(s[u]=1);return this._initted&&!this._pt&&p&&wb(this),this},Tween.to=function to(t,e,r){return new Tween(t,e,r)},Tween.from=function from(t,e){return Ya(1,arguments)},Tween.delayedCall=function delayedCall(t,e,r,i){return new Tween(e,0,{immediateRender:!1,lazy:!1,overwrite:!1,delay:t,onComplete:e,onReverseComplete:e,onCompleteParams:r,onReverseCompleteParams:r,callbackScope:i})},Tween.fromTo=function fromTo(t,e,r){return Ya(2,arguments)},Tween.set=function set(t,e){return e.duration=0,e.repeatDelay||(e.repeat=0),new Tween(t,e)},Tween.killTweensOf=function killTweensOf(t,e,r){return L.killTweensOf(t,e,r)},Tween}(qt);ta(te.prototype,{_targets:[],_lazy:0,_startAt:0,_op:0,_onInit:0}),ja("staggerTo,staggerFrom,staggerFromTo",function(r){te[r]=function(){var t=new Zt,e=Ct.call(arguments,0);return e.splice("staggerFromTo"===r?5:4,0,0),t[r].apply(t,e)}});function rc(t,e,r){return t.setAttribute(e,r)}function zc(t,e,r,i){i.mSet(t,e,i.m.call(i.tween,r,i.mt),i)}var ee=function _setterPlain(t,e,r){return t[e]=r},re=function _setterFunc(t,e,r){return t[e](r)},ue=function _setterFuncWithParam(t,e,r,i){return t[e](i.fp,r)},le=function _getSetter(t,e){return s(t[e])?re:u(t[e])&&t.setAttribute?rc:ee},ce=function _renderPlain(t,e){return e.set(e.t,e.p,Math.round(1e6*(e.s+e.c*t))/1e6,e)},_e=function _renderBoolean(t,e){return e.set(e.t,e.p,!!(e.s+e.c*t),e)},ge=function _renderComplexString(t,e){var r=e._pt,i="";if(!t&&e.b)i=e.b;else if(1===t&&e.e)i=e.e;else{for(;r;)i=r.p+(r.m?r.m(r.s+r.c*t):Math.round(1e4*(r.s+r.c*t))/1e4)+i,r=r._next;i+=e.c}e.set(e.t,e.p,i,e)},ve=function _renderPropTweens(t,e){for(var r=e._pt;r;)r.r(t,r.d),r=r._next},ye=function _addPluginModifier(t,e,r,i){for(var n,a=this._pt;a;)n=a._next,a.p===i&&a.modifier(t,e,r),a=n},Te=function _killPropTweensOf(t){for(var e,r,i=this._pt;i;)r=i._next,i.p===t&&!i.op||i.op===t?Ba(this,i,"_pt"):i.dep||(e=1),i=r;return!e},be=function _sortPropTweensByPriority(t){for(var e,r,i,n,a=t._pt;a;){for(e=a._next,r=i;r&&r.pr>a.pr;)r=r._next;(a._prev=r?r._prev:n)?a._prev._next=a:i=a,(a._next=r)?r._prev=a:n=a,a=e}t._pt=i},we=(PropTween.prototype.modifier=function modifier(t,e,r){this.mSet=this.mSet||this.set,this.set=zc,this.m=t,this.mt=r,this.tween=e},PropTween);function PropTween(t,e,r,i,n,a,s,o,u){this.t=e,this.s=i,this.c=n,this.p=r,this.r=a||ce,this.d=s||this,this.set=o||ee,this.pr=u||0,(this._next=t)&&(t._prev=this)}ja(Tt+"parent,duration,ease,delay,overwrite,runBackwards,startAt,yoyo,immediateRender,repeat,repeatDelay,data,paused,reversed,lazy,callbackScope,stringFilter,id,yoyoEase,stagger,inherit,repeatRefresh,keyframes,autoRevert,scrollTrigger",function(t){return ct[t]=1}),ht.TweenMax=ht.TweenLite=te,ht.TimelineLite=ht.TimelineMax=Zt,L=new Zt({sortChildren:!1,defaults:j,autoRemoveChildren:!0,id:"root",smoothChildTiming:!0}),N.stringFilter=Ib;function Hc(t){return(Oe[t]||Me).map(function(t){return t()})}function Ic(){var t=Date.now(),o=[];2<t-Ce&&(Hc("matchMediaInit"),ke.forEach(function(t){var e,r,i,n,a=t.queries,s=t.conditions;for(r in a)(e=h.matchMedia(a[r]).matches)&&(i=1),e!==s[r]&&(s[r]=e,n=1);n&&(t.revert(),i&&o.push(t))}),Hc("matchMediaRevert"),o.forEach(function(e){return e.onMatch(e,function(t){return e.add(null,t)})}),Ce=t,Hc("matchMedia"))}var xe,ke=[],Oe={},Me=[],Ce=0,Pe=0,De=((xe=Context.prototype).add=function add(t,i,n){function Jw(){var t,e=l,r=a.selector;return e&&e!==a&&e.data.push(a),n&&(a.selector=fb(n)),l=a,t=i.apply(a,arguments),s(t)&&a._r.push(t),l=e,a.selector=r,a.isReverted=!1,t}s(t)&&(n=i,i=t,t=s);var a=this;return a.last=Jw,t===s?Jw(a,function(t){return a.add(null,t)}):t?a[t]=Jw:Jw},xe.ignore=function ignore(t){var e=l;l=null,t(this),l=e},xe.getTweens=function getTweens(){var e=[];return this.data.forEach(function(t){return t instanceof Context?e.push.apply(e,t.getTweens()):t instanceof te&&!(t.parent&&"nested"===t.parent.data)&&e.push(t)}),e},xe.clear=function clear(){this._r.length=this.data.length=0},xe.kill=function kill(i,t){var n=this;if(i?function(){for(var t,e=n.getTweens(),r=n.data.length;r--;)"isFlip"===(t=n.data[r]).data&&(t.revert(),t.getChildren(!0,!0,!1).forEach(function(t){return e.splice(e.indexOf(t),1)}));for(e.map(function(t){return{g:t._dur||t._delay||t._sat&&!t._sat.vars.immediateRender?t.globalTime(0):-1/0,t:t}}).sort(function(t,e){return e.g-t.g||-1/0}).forEach(function(t){return t.t.revert(i)}),r=n.data.length;r--;)(t=n.data[r])instanceof Zt?"nested"!==t.data&&(t.scrollTrigger&&t.scrollTrigger.revert(),t.kill()):t instanceof te||!t.revert||t.revert(i);n._r.forEach(function(t){return t(i,n)}),n.isReverted=!0}():this.data.forEach(function(t){return t.kill&&t.kill()}),this.clear(),t)for(var e=ke.length;e--;)ke[e].id===this.id&&ke.splice(e,1)},xe.revert=function revert(t){this.kill(t||{})},Context);function Context(t,e){this.selector=e&&fb(e),this.data=[],this._r=[],this.isReverted=!1,this.id=Pe++,t&&this.add(t)}var Se,Ee=((Se=MatchMedia.prototype).add=function add(t,e,r){v(t)||(t={matches:t});var i,n,a,s=new De(0,r||this.scope),o=s.conditions={};for(n in l&&!s.selector&&(s.selector=l.selector),this.contexts.push(s),e=s.add("onMatch",e),s.queries=t)"all"===n?a=1:(i=h.matchMedia(t[n]))&&(ke.indexOf(s)<0&&ke.push(s),(o[n]=i.matches)&&(a=1),i.addListener?i.addListener(Ic):i.addEventListener("change",Ic));return a&&e(s,function(t){return s.add(null,t)}),this},Se.revert=function revert(t){this.kill(t||{})},Se.kill=function kill(e){this.contexts.forEach(function(t){return t.kill(e,!0)})},MatchMedia);function MatchMedia(t){this.contexts=[],this.scope=t,l&&l.data.push(this)}var Re={registerPlugin:function registerPlugin(){for(var t=arguments.length,e=new Array(t),r=0;r<t;r++)e[r]=arguments[r];e.forEach(function(t){return zb(t)})},timeline:function timeline(t){return new Zt(t)},getTweensOf:function getTweensOf(t,e){return L.getTweensOf(t,e)},getProperty:function getProperty(i,t,e,n){r(i)&&(i=Pt(i)[0]);var a=ha(i||{}).get,s=e?sa:ra;return"native"===e&&(e=""),i?t?s((mt[t]&&mt[t].get||a)(i,t,e,n)):function(t,e,r){return s((mt[t]&&mt[t].get||a)(i,t,e,r))}:i},quickSetter:function quickSetter(r,e,i){if(1<(r=Pt(r)).length){var n=r.map(function(t){return Fe.quickSetter(t,e,i)}),a=n.length;return function(t){for(var e=a;e--;)n[e](t)}}r=r[0]||{};var s=mt[e],o=ha(r),u=o.harness&&(o.harness.aliases||{})[e]||e,h=s?function(t){var e=new s;d._pt=0,e.init(r,i?t+i:t,d,0,[r]),e.render(1,e),d._pt&&ve(1,d)}:o.set(r,u);return s?h:function(t){return h(r,u,i?t+i:t,o,1)}},quickTo:function quickTo(t,i,e){function by(t,e,r){return n.resetTo(i,t,e,r)}var r,n=Fe.to(t,ta(((r={})[i]="+=0.1",r.paused=!0,r.stagger=0,r),e||{}));return by.tween=n,by},isTweening:function isTweening(t){return 0<L.getTweensOf(t,!0).length},defaults:function defaults(t){return t&&t.ease&&(t.ease=Vt(t.ease,j.ease)),wa(j,t||{})},config:function config(t){return wa(N,t||{})},registerEffect:function registerEffect(t){var i=t.name,n=t.effect,e=t.plugins,a=t.defaults,r=t.extendTimeline;(e||"").split(",").forEach(function(t){return t&&!mt[t]&&!ht[t]&&T(i+" effect requires "+t+" plugin.")}),gt[i]=function(t,e,r){return n(Pt(t),ta(e||{},a),r)},r&&(Zt.prototype[i]=function(t,e,r){return this.add(gt[i](t,v(e)?e:(r=e)&&{},this),r)})},registerEase:function registerEase(t,e){Bt[t]=Vt(e)},parseEase:function parseEase(t,e){return arguments.length?Vt(t,e):Bt},getById:function getById(t){return L.getById(t)},exportRoot:function exportRoot(t,e){void 0===t&&(t={});var r,i,n=new Zt(t);for(n.smoothChildTiming=w(t.smoothChildTiming),L.remove(n),n._dp=0,n._time=n._tTime=L._time,r=L._first;r;)i=r._next,!e&&!r._dur&&r instanceof te&&r.vars.onComplete===r._targets[0]||Na(n,r,r._start-r._delay),r=i;return Na(L,n,0),n},context:function context(t,e){return t?new De(t,e):l},matchMedia:function matchMedia(t){return new Ee(t)},matchMediaRefresh:function matchMediaRefresh(){return ke.forEach(function(t){var e,r,i=t.conditions;for(r in i)i[r]&&(i[r]=!1,e=1);e&&t.revert()})||Ic()},addEventListener:function addEventListener(t,e){var r=Oe[t]||(Oe[t]=[]);~r.indexOf(e)||r.push(e)},removeEventListener:function removeEventListener(t,e){var r=Oe[t],i=r&&r.indexOf(e);0<=i&&r.splice(i,1)},utils:{wrap:function wrap(e,t,r){var i=t-e;return $(e)?ob(e,wrap(0,e.length),t):Za(r,function(t){return(i+(t-e)%i)%i+e})},wrapYoyo:function wrapYoyo(e,t,r){var i=t-e,n=2*i;return $(e)?ob(e,wrapYoyo(0,e.length-1),t):Za(r,function(t){return e+(i<(t=(n+(t-e)%n)%n||0)?n-t:t)})},distribute:hb,random:kb,snap:jb,normalize:function normalize(t,e,r){return At(t,e,0,1,r)},getUnit:_a,clamp:function clamp(e,r,t){return Za(t,function(t){return Mt(e,r,t)})},splitColor:Db,toArray:Pt,selector:fb,mapRange:At,pipe:function pipe(){for(var t=arguments.length,e=new Array(t),r=0;r<t;r++)e[r]=arguments[r];return function(t){return e.reduce(function(t,e){return e(t)},t)}},unitize:function unitize(e,r){return function(t){return e(parseFloat(t))+(r||_a(t))}},interpolate:function interpolate(e,i,t,n){var a=isNaN(e+i)?0:function(t){return(1-t)*e+t*i};if(!a){var s,o,u,h,l,f=r(e),d={};if(!0===t&&(n=1)&&(t=null),f)e={p:e},i={p:i};else if($(e)&&!$(i)){for(u=[],h=e.length,l=h-2,o=1;o<h;o++)u.push(interpolate(e[o-1],e[o]));h--,a=function func(t){t*=h;var e=Math.min(l,~~t);return u[e](t-e)},t=i}else n||(e=bt($(e)?[]:{},e));if(!u){for(s in i)Jt.call(d,e,s,"get",i[s]);a=function func(t){return ve(t,d)||(f?e.p:e)}}}return Za(t,a)},shuffle:gb},install:R,effects:gt,ticker:It,updateRoot:Zt.updateRoot,plugins:mt,globalTimeline:L,core:{PropTween:we,globals:U,Tween:te,Timeline:Zt,Animation:qt,getCache:ha,_removeLinkedListItem:Ba,reverting:function reverting(){return I},context:function context(t){return t&&l&&(l.data.push(t),t._ctx=l),l},suppressOverwrites:function suppressOverwrites(t){return F=t}}};ja("to,from,fromTo,delayedCall,set,killTweensOf",function(t){return Re[t]=te[t]}),It.add(Zt.updateRoot),d=Re.to({},{duration:0});function Mc(t,e){for(var r=t._pt;r&&r.p!==e&&r.op!==e&&r.fp!==e;)r=r._next;return r}function Oc(t,a){return{name:t,headless:1,rawVars:1,init:function init(t,n,e){e._onInit=function(t){var e,i;if(r(n)&&(e={},ja(n,function(t){return e[t]=1}),n=e),a){for(i in e={},n)e[i]=a(n[i]);n=e}!function _addModifiers(t,e){var r,i,n,a=t._targets;for(r in e)for(i=a.length;i--;)(n=(n=t._ptLookup[i][r])&&n.d)&&(n._pt&&(n=Mc(n,r)),n&&n.modifier&&n.modifier(e[r],t,a[i],r))}(t,n)}}}}var Fe=Re.registerPlugin({name:"attr",init:function init(t,e,r,i,n){var a,s,o;for(a in this.tween=r,e)o=t.getAttribute(a)||"",(s=this.add(t,"setAttribute",(o||0)+"",e[a],i,n,0,0,a)).op=a,s.b=o,this._props.push(a)},render:function render(t,e){for(var r=e._pt;r;)I?r.set(r.t,r.p,r.b,r):r.r(t,r.d),r=r._next}},{name:"endArray",headless:1,init:function init(t,e){for(var r=e.length;r--;)this.add(t,r,t[r]||0,e[r],0,0,0,0,0,1)}},Oc("roundProps",ib),Oc("modifiers"),Oc("snap",jb))||Re;te.version=Zt.version=Fe.version="3.14.2",o=1,x()&&Lt();function yd(t,e){return e.set(e.t,e.p,Math.round(1e4*(e.s+e.c*t))/1e4+e.u,e)}function zd(t,e){return e.set(e.t,e.p,1===t?e.e:Math.round(1e4*(e.s+e.c*t))/1e4+e.u,e)}function Ad(t,e){return e.set(e.t,e.p,t?Math.round(1e4*(e.s+e.c*t))/1e4+e.u:e.b,e)}function Bd(t,e){return e.set(e.t,e.p,1===t?e.e:t?Math.round(1e4*(e.s+e.c*t))/1e4+e.u:e.b,e)}function Cd(t,e){var r=e.s+e.c*t;e.set(e.t,e.p,~~(r+(r<0?-.5:.5))+e.u,e)}function Dd(t,e){return e.set(e.t,e.p,t?e.e:e.b,e)}function Ed(t,e){return e.set(e.t,e.p,1!==t?e.b:e.e,e)}function Fd(t,e,r){return t.style[e]=r}function Gd(t,e,r){return t.style.setProperty(e,r)}function Hd(t,e,r){return t._gsap[e]=r}function Id(t,e,r){return t._gsap.scaleX=t._gsap.scaleY=r}function Jd(t,e,r,i,n){var a=t._gsap;a.scaleX=a.scaleY=r,a.renderTransform(n,a)}function Kd(t,e,r,i,n){var a=t._gsap;a[e]=r,a.renderTransform(n,a)}function Nd(t,e){var r=this,i=this.target,n=i.style,a=i._gsap;if(t in hr&&n){if(this.tfm=this.tfm||{},"transform"===t)return mr.transform.split(",").forEach(function(t){return Nd.call(r,t,e)});if(~(t=mr[t]||t).indexOf(",")?t.split(",").forEach(function(t){return r.tfm[t]=xr(i,t)}):this.tfm[t]=a.x?a[t]:xr(i,t),t===vr&&(this.tfm.zOrigin=a.zOrigin),0<=this.props.indexOf(gr))return;a.svg&&(this.svgo=i.getAttribute("data-svg-origin"),this.props.push(vr,e,"")),t=gr}(n||e)&&this.props.push(t,e,n[t])}function Od(t){t.translate&&(t.removeProperty("translate"),t.removeProperty("scale"),t.removeProperty("rotate"))}function Pd(){var t,e,r=this.props,i=this.target,n=i.style,a=i._gsap;for(t=0;t<r.length;t+=3)r[t+1]?2===r[t+1]?i[r[t]](r[t+2]):i[r[t]]=r[t+2]:r[t+2]?n[r[t]]=r[t+2]:n.removeProperty("--"===r[t].substr(0,2)?r[t]:r[t].replace(cr,"-$1").toLowerCase());if(this.tfm){for(e in this.tfm)a[e]=this.tfm[e];a.svg&&(a.renderTransform(),i.setAttribute("data-svg-origin",this.svgo||"")),(t=Ue())&&t.isStart||n[gr]||(Od(n),a.zOrigin&&n[vr]&&(n[vr]+=" "+a.zOrigin+"px",a.zOrigin=0,a.renderTransform()),a.uncache=1)}}function Qd(t,e){var r={target:t,props:[],revert:Pd,save:Nd};return t._gsap||Fe.core.getCache(t),e&&t.style&&t.nodeType&&e.split(",").forEach(function(t){return r.save(t)}),r}function Sd(t,e){var r=Le.createElementNS?Le.createElementNS((e||"http://www.w3.org/1999/xhtml").replace(/^https/,"http"),t):Le.createElement(t);return r&&r.style?r:Le.createElement(t)}function Td(t,e,r){var i=getComputedStyle(t);return i[e]||i.getPropertyValue(e.replace(cr,"-$1").toLowerCase())||i.getPropertyValue(e)||!r&&Td(t,Tr(e)||e,1)||""}function Wd(){(function _windowExists(){return"undefined"!=typeof window})()&&window.document&&(Ie=window,Le=Ie.document,Ye=Le.documentElement,je=Sd("div")||{style:{}},Sd("div"),gr=Tr(gr),vr=gr+"Origin",je.style.cssText="border-width:0;line-height:0;position:absolute;padding:0",Xe=!!Tr("perspective"),Ue=Fe.core.reverting,Ne=1)}function Xd(t){var e,r=t.ownerSVGElement,i=Sd("svg",r&&r.getAttribute("xmlns")||"http://www.w3.org/2000/svg"),n=t.cloneNode(!0);n.style.display="block",i.appendChild(n),Ye.appendChild(i);try{e=n.getBBox()}catch(t){}return i.removeChild(n),Ye.removeChild(i),e}function Yd(t,e){for(var r=e.length;r--;)if(t.hasAttribute(e[r]))return t.getAttribute(e[r])}function Zd(e){var r,i;try{r=e.getBBox()}catch(t){r=Xd(e),i=1}return r&&(r.width||r.height)||i||(r=Xd(e)),!r||r.width||r.x||r.y?r:{x:+Yd(e,["x","cx","x1"])||0,y:+Yd(e,["y","cy","y1"])||0,width:0,height:0}}function $d(t){return!(!t.getCTM||t.parentNode&&!t.ownerSVGElement||!Zd(t))}function _d(t,e){if(e){var r,i=t.style;e in hr&&e!==vr&&(e=gr),i.removeProperty?("ms"!==(r=e.substr(0,2))&&"webkit"!==e.substr(0,6)||(e="-"+e),i.removeProperty("--"===r?e:e.replace(cr,"-$1").toLowerCase())):i.removeAttribute(e)}}function ae(t,e,r,i,n,a){var s=new we(t._pt,e,r,0,1,a?Ed:Dd);return(t._pt=s).b=i,s.e=n,t._props.push(r),s}function de(t,e,r,i){var n,a,s,o,u=parseFloat(r)||0,h=(r+"").trim().substr((u+"").length)||"px",l=je.style,f=pr.test(e),d="svg"===t.tagName.toLowerCase(),c=(d?"client":"offset")+(f?"Width":"Height"),p="px"===i,_="%"===i;if(i===h||!u||br[i]||br[h])return u;if("px"===h||p||(u=de(t,e,r,"px")),o=t.getCTM&&$d(t),(_||"%"===h)&&(hr[e]||~e.indexOf("adius")))return n=o?t.getBBox()[f?"width":"height"]:t[c],ka(_?u/n*100:u/100*n);if(l[f?"width":"height"]=100+(p?h:i),a="rem"!==i&&~e.indexOf("adius")||"em"===i&&t.appendChild&&!d?t:t.parentNode,o&&(a=(t.ownerSVGElement||{}).parentNode),a&&a!==Le&&a.appendChild||(a=Le.body),(s=a._gsap)&&_&&s.width&&f&&s.time===It.time&&!s.uncache)return ka(u/s.width*100);if(!_||"height"!==e&&"width"!==e)!_&&"%"!==h||wr[Td(a,"display")]||(l.position=Td(t,"position")),a===t&&(l.position="static"),a.appendChild(je),n=je[c],a.removeChild(je),l.position="absolute";else{var m=t.style[e];t.style[e]=100+i,n=t[c],m?t.style[e]=m:_d(t,e)}return f&&_&&((s=ha(a)).time=It.time,s.width=a[c]),ka(p?n*u/100:n&&u?100/n*u:0)}function fe(t,e,r,i){if(!r||"none"===r){var n=Tr(e,t,1),a=n&&Td(t,n,1);a&&a!==r?(e=n,r=a):"borderColor"===e&&(r=Td(t,"borderTopColor"))}var s,o,u,h,l,f,d,c,p,_,m,g=new we(this._pt,t.style,e,0,1,ge),v=0,y=0;if(g.b=r,g.e=i,r+="","var(--"===(i+="").substring(0,6)&&(i=Td(t,i.substring(4,i.indexOf(")")))),"auto"===i&&(f=t.style[e],t.style[e]=i,i=Td(t,e)||i,f?t.style[e]=f:_d(t,e)),Ib(s=[r,i]),i=s[1],u=(r=s[0]).match(nt)||[],(i.match(nt)||[]).length){for(;o=nt.exec(i);)d=o[0],p=i.substring(v,o.index),l?l=(l+1)%5:"rgba("!==p.substr(-5)&&"hsla("!==p.substr(-5)||(l=1),d!==(f=u[y++]||"")&&(h=parseFloat(f)||0,m=f.substr((h+"").length),"="===d.charAt(1)&&(d=ma(h,d)+m),c=parseFloat(d),_=d.substr((c+"").length),v=nt.lastIndex-_.length,_||(_=_||N.units[e]||m,v===i.length&&(i+=_,g.e+=_)),m!==_&&(h=de(t,e,f,_)||0),g._pt={_next:g._pt,p:p||1===y?p:",",s:h,c:c-h,m:l&&l<4||"zIndex"===e?Math.round:0});g.c=v<i.length?i.substring(v,i.length):""}else g.r="display"===e&&"none"===i?Ed:Dd;return st.test(i)&&(g.e=0),this._pt=g}function he(t){var e=t.split(" "),r=e[0],i=e[1]||"50%";return"top"!==r&&"bottom"!==r&&"left"!==i&&"right"!==i||(t=r,r=i,i=t),e[0]=kr[r]||r,e[1]=kr[i]||i,e.join(" ")}function ie(t,e){if(e.tween&&e.tween._time===e.tween._dur){var r,i,n,a=e.t,s=a.style,o=e.u,u=a._gsap;if("all"===o||!0===o)s.cssText="",i=1;else for(n=(o=o.split(",")).length;-1<--n;)r=o[n],hr[r]&&(i=1,r="transformOrigin"===r?vr:gr),_d(a,r);i&&(_d(a,gr),u&&(u.svg&&a.removeAttribute("transform"),s.scale=s.rotate=s.translate="none",Pr(a,1),u.uncache=1,Od(s)))}}function me(t){return"matrix(1, 0, 0, 1, 0, 0)"===t||"none"===t||!t}function ne(t){var e=Td(t,gr);return me(e)?Mr:e.substr(7).match(it).map(ka)}function oe(t,e){var r,i,n,a,s=t._gsap||ha(t),o=t.style,u=ne(t);return s.svg&&t.getAttribute("transform")?"1,0,0,1,0,0"===(u=[(n=t.transform.baseVal.consolidate().matrix).a,n.b,n.c,n.d,n.e,n.f]).join(",")?Mr:u:(u!==Mr||t.offsetParent||t===Ye||s.svg||(n=o.display,o.display="block",(r=t.parentNode)&&(t.offsetParent||t.getBoundingClientRect().width)||(a=1,i=t.nextElementSibling,Ye.appendChild(t)),u=ne(t),n?o.display=n:_d(t,"display"),a&&(i?r.insertBefore(t,i):r?r.appendChild(t):Ye.removeChild(t))),e&&6<u.length?[u[0],u[1],u[4],u[5],u[12],u[13]]:u)}function pe(t,e,r,i,n,a){var s,o,u,h=t._gsap,l=n||oe(t,!0),f=h.xOrigin||0,d=h.yOrigin||0,c=h.xOffset||0,p=h.yOffset||0,_=l[0],m=l[1],g=l[2],v=l[3],y=l[4],T=l[5],b=e.split(" "),w=parseFloat(b[0])||0,x=parseFloat(b[1])||0;r?l!==Mr&&(o=_*v-m*g)&&(u=w*(-m/o)+x*(_/o)-(_*T-m*y)/o,w=w*(v/o)+x*(-g/o)+(g*T-v*y)/o,x=u):(w=(s=Zd(t)).x+(~b[0].indexOf("%")?w/100*s.width:w),x=s.y+(~(b[1]||b[0]).indexOf("%")?x/100*s.height:x)),i||!1!==i&&h.smooth?(y=w-f,T=x-d,h.xOffset=c+(y*_+T*g)-y,h.yOffset=p+(y*m+T*v)-T):h.xOffset=h.yOffset=0,h.xOrigin=w,h.yOrigin=x,h.smooth=!!i,h.origin=e,h.originIsAbsolute=!!r,t.style[vr]="0px 0px",a&&(ae(a,h,"xOrigin",f,w),ae(a,h,"yOrigin",d,x),ae(a,h,"xOffset",c,h.xOffset),ae(a,h,"yOffset",p,h.yOffset)),t.setAttribute("data-svg-origin",w+" "+x)}function se(t,e,r){var i=_a(e);return ka(parseFloat(e)+parseFloat(de(t,"x",r+"px",i)))+i}function ze(t,e,i,n,a){var s,o,u=360,h=r(a),l=parseFloat(a)*(h&&~a.indexOf("rad")?lr:1)-n,f=n+l+"deg";return h&&("short"===(s=a.split("_")[1])&&(l%=u)!==l%180&&(l+=l<0?u:-u),"cw"===s&&l<0?l=(l+36e9)%u-~~(l/u)*u:"ccw"===s&&0<l&&(l=(l-36e9)%u-~~(l/u)*u)),t._pt=o=new we(t._pt,e,i,n,l,zd),o.e=f,o.u="deg",t._props.push(i),o}function Ae(t,e){for(var r in e)t[r]=e[r];return t}function Be(t,e,r){var i,n,a,s,o,u,h,l=Ae({},r._gsap),f=r.style;for(n in l.svg?(a=r.getAttribute("transform"),r.setAttribute("transform",""),f[gr]=e,i=Pr(r,1),_d(r,gr),r.setAttribute("transform",a)):(a=getComputedStyle(r)[gr],f[gr]=e,i=Pr(r,1),f[gr]=a),hr)(a=l[n])!==(s=i[n])&&"perspective,force3D,transformOrigin,svgOrigin".indexOf(n)<0&&(o=_a(a)!==(h=_a(s))?de(r,n,a,h):parseFloat(a),u=parseFloat(s),t._pt=new we(t._pt,i,n,o,u-o,yd),t._pt.u=h||0,t._props.push(n));Ae(i,l)}var Ie,Le,Ye,Ne,je,Ve,Ue,Xe,qe=Bt.Power0,Ze=Bt.Power1,We=Bt.Power2,He=Bt.Power3,Je=Bt.Power4,Qe=Bt.Linear,Ge=Bt.Quad,Ke=Bt.Cubic,$e=Bt.Quart,tr=Bt.Quint,er=Bt.Strong,rr=Bt.Elastic,ir=Bt.Back,nr=Bt.SteppedEase,ar=Bt.Bounce,sr=Bt.Sine,or=Bt.Expo,ur=Bt.Circ,hr={},lr=180/Math.PI,fr=Math.PI/180,dr=Math.atan2,cr=/([A-Z])/g,pr=/(left|right|width|margin|padding|x)/i,_r=/[\s,\(]\S/,mr={autoAlpha:"opacity,visibility",scale:"scaleX,scaleY",alpha:"opacity"},gr="transform",vr=gr+"Origin",yr="O,Moz,ms,Ms,Webkit".split(","),Tr=function _checkPropPrefix(t,e,r){var i=(e||je).style,n=5;if(t in i&&!r)return t;for(t=t.charAt(0).toUpperCase()+t.substr(1);n--&&!(yr[n]+t in i););return n<0?null:(3===n?"ms":0<=n?yr[n]:"")+t},br={deg:1,rad:1,turn:1},wr={grid:1,flex:1},xr=function _get(t,e,r,i){var n;return Ne||Wd(),e in mr&&"transform"!==e&&~(e=mr[e]).indexOf(",")&&(e=e.split(",")[0]),hr[e]&&"transform"!==e?(n=Pr(t,i),n="transformOrigin"!==e?n[e]:n.svg?n.origin:Ar(Td(t,vr))+" "+n.zOrigin+"px"):(n=t.style[e])&&"auto"!==n&&!i&&!~(n+"").indexOf("calc(")||(n=Or[e]&&Or[e](t,e,r)||Td(t,e)||ia(t,e)||("opacity"===e?1:0)),r&&!~(n+"").trim().indexOf(" ")?de(t,e,n,r)+r:n},kr={top:"0%",bottom:"100%",left:"0%",right:"100%",center:"50%"},Or={clearProps:function clearProps(t,e,r,i,n){if("isFromStart"!==n.data){var a=t._pt=new we(t._pt,e,r,0,0,ie);return a.u=i,a.pr=-10,a.tween=n,t._props.push(r),1}}},Mr=[1,0,0,1,0,0],Cr={},Pr=function _parseTransform(t,e){var r=t._gsap||new Xt(t);if("x"in r&&!e&&!r.uncache)return r;var i,n,a,s,o,u,h,l,f,d,c,p,_,m,g,v,y,T,b,w,x,k,O,M,C,P,A,D,S,z,E,R,F=t.style,I=r.scaleX<0,L="deg",B=getComputedStyle(t),Y=Td(t,vr)||"0";return i=n=a=u=h=l=f=d=c=0,s=o=1,r.svg=!(!t.getCTM||!$d(t)),B.translate&&("none"===B.translate&&"none"===B.scale&&"none"===B.rotate||(F[gr]=("none"!==B.translate?"translate3d("+(B.translate+" 0 0").split(" ").slice(0,3).join(", ")+") ":"")+("none"!==B.rotate?"rotate("+B.rotate+") ":"")+("none"!==B.scale?"scale("+B.scale.split(" ").join(",")+") ":"")+("none"!==B[gr]?B[gr]:"")),F.scale=F.rotate=F.translate="none"),m=oe(t,r.svg),r.svg&&(M=r.uncache?(C=t.getBBox(),Y=r.xOrigin-C.x+"px "+(r.yOrigin-C.y)+"px",""):!e&&t.getAttribute("data-svg-origin"),pe(t,M||Y,!!M||r.originIsAbsolute,!1!==r.smooth,m)),p=r.xOrigin||0,_=r.yOrigin||0,m!==Mr&&(T=m[0],b=m[1],w=m[2],x=m[3],i=k=m[4],n=O=m[5],6===m.length?(s=Math.sqrt(T*T+b*b),o=Math.sqrt(x*x+w*w),u=T||b?dr(b,T)*lr:0,(f=w||x?dr(w,x)*lr+u:0)&&(o*=Math.abs(Math.cos(f*fr))),r.svg&&(i-=p-(p*T+_*w),n-=_-(p*b+_*x))):(R=m[6],z=m[7],A=m[8],D=m[9],S=m[10],E=m[11],i=m[12],n=m[13],a=m[14],h=(g=dr(R,S))*lr,g&&(M=k*(v=Math.cos(-g))+A*(y=Math.sin(-g)),C=O*v+D*y,P=R*v+S*y,A=k*-y+A*v,D=O*-y+D*v,S=R*-y+S*v,E=z*-y+E*v,k=M,O=C,R=P),l=(g=dr(-w,S))*lr,g&&(v=Math.cos(-g),E=x*(y=Math.sin(-g))+E*v,T=M=T*v-A*y,b=C=b*v-D*y,w=P=w*v-S*y),u=(g=dr(b,T))*lr,g&&(M=T*(v=Math.cos(g))+b*(y=Math.sin(g)),C=k*v+O*y,b=b*v-T*y,O=O*v-k*y,T=M,k=C),h&&359.9<Math.abs(h)+Math.abs(u)&&(h=u=0,l=180-l),s=ka(Math.sqrt(T*T+b*b+w*w)),o=ka(Math.sqrt(O*O+R*R)),g=dr(k,O),f=2e-4<Math.abs(g)?g*lr:0,c=E?1/(E<0?-E:E):0),r.svg&&(M=t.getAttribute("transform"),r.forceCSS=t.setAttribute("transform","")||!me(Td(t,gr)),M&&t.setAttribute("transform",M))),90<Math.abs(f)&&Math.abs(f)<270&&(I?(s*=-1,f+=u<=0?180:-180,u+=u<=0?180:-180):(o*=-1,f+=f<=0?180:-180)),e=e||r.uncache,r.x=i-((r.xPercent=i&&(!e&&r.xPercent||(Math.round(t.offsetWidth/2)===Math.round(-i)?-50:0)))?t.offsetWidth*r.xPercent/100:0)+"px",r.y=n-((r.yPercent=n&&(!e&&r.yPercent||(Math.round(t.offsetHeight/2)===Math.round(-n)?-50:0)))?t.offsetHeight*r.yPercent/100:0)+"px",r.z=a+"px",r.scaleX=ka(s),r.scaleY=ka(o),r.rotation=ka(u)+L,r.rotationX=ka(h)+L,r.rotationY=ka(l)+L,r.skewX=f+L,r.skewY=d+L,r.transformPerspective=c+"px",(r.zOrigin=parseFloat(Y.split(" ")[2])||!e&&r.zOrigin||0)&&(F[vr]=Ar(Y)),r.xOffset=r.yOffset=0,r.force3D=N.force3D,r.renderTransform=r.svg?Fr:Xe?Rr:Dr,r.uncache=0,r},Ar=function _firstTwoOnly(t){return(t=t.split(" "))[0]+" "+t[1]},Dr=function _renderNon3DTransforms(t,e){e.z="0px",e.rotationY=e.rotationX="0deg",e.force3D=0,Rr(t,e)},Sr="0deg",zr="0px",Er=") ",Rr=function _renderCSSTransforms(t,e){var r=e||this,i=r.xPercent,n=r.yPercent,a=r.x,s=r.y,o=r.z,u=r.rotation,h=r.rotationY,l=r.rotationX,f=r.skewX,d=r.skewY,c=r.scaleX,p=r.scaleY,_=r.transformPerspective,m=r.force3D,g=r.target,v=r.zOrigin,y="",T="auto"===m&&t&&1!==t||!0===m;if(v&&(l!==Sr||h!==Sr)){var b,w=parseFloat(h)*fr,x=Math.sin(w),k=Math.cos(w);w=parseFloat(l)*fr,b=Math.cos(w),a=se(g,a,x*b*-v),s=se(g,s,-Math.sin(w)*-v),o=se(g,o,k*b*-v+v)}_!==zr&&(y+="perspective("+_+Er),(i||n)&&(y+="translate("+i+"%, "+n+"%) "),!T&&a===zr&&s===zr&&o===zr||(y+=o!==zr||T?"translate3d("+a+", "+s+", "+o+") ":"translate("+a+", "+s+Er),u!==Sr&&(y+="rotate("+u+Er),h!==Sr&&(y+="rotateY("+h+Er),l!==Sr&&(y+="rotateX("+l+Er),f===Sr&&d===Sr||(y+="skew("+f+", "+d+Er),1===c&&1===p||(y+="scale("+c+", "+p+Er),g.style[gr]=y||"translate(0, 0)"},Fr=function _renderSVGTransforms(t,e){var r,i,n,a,s,o=e||this,u=o.xPercent,h=o.yPercent,l=o.x,f=o.y,d=o.rotation,c=o.skewX,p=o.skewY,_=o.scaleX,m=o.scaleY,g=o.target,v=o.xOrigin,y=o.yOrigin,T=o.xOffset,b=o.yOffset,w=o.forceCSS,x=parseFloat(l),k=parseFloat(f);d=parseFloat(d),c=parseFloat(c),(p=parseFloat(p))&&(c+=p=parseFloat(p),d+=p),d||c?(d*=fr,c*=fr,r=Math.cos(d)*_,i=Math.sin(d)*_,n=Math.sin(d-c)*-m,a=Math.cos(d-c)*m,c&&(p*=fr,s=Math.tan(c-p),n*=s=Math.sqrt(1+s*s),a*=s,p&&(s=Math.tan(p),r*=s=Math.sqrt(1+s*s),i*=s)),r=ka(r),i=ka(i),n=ka(n),a=ka(a)):(r=_,a=m,i=n=0),(x&&!~(l+"").indexOf("px")||k&&!~(f+"").indexOf("px"))&&(x=de(g,"x",l,"px"),k=de(g,"y",f,"px")),(v||y||T||b)&&(x=ka(x+v-(v*r+y*n)+T),k=ka(k+y-(v*i+y*a)+b)),(u||h)&&(s=g.getBBox(),x=ka(x+u/100*s.width),k=ka(k+h/100*s.height)),s="matrix("+r+","+i+","+n+","+a+","+x+","+k+")",g.setAttribute("transform",s),w&&(g.style[gr]=s)};ja("padding,margin,Width,Radius",function(e,r){var t="Right",i="Bottom",n="Left",o=(r<3?["Top",t,i,n]:["Top"+n,"Top"+t,i+t,i+n]).map(function(t){return r<2?e+t:"border"+t+e});Or[1<r?"border"+e:e]=function(e,t,r,i,n){var a,s;if(arguments.length<4)return a=o.map(function(t){return xr(e,t,r)}),5===(s=a.join(" ")).split(a[0]).length?a[0]:s;a=(i+"").split(" "),s={},o.forEach(function(t,e){return s[t]=a[e]=a[e]||a[(e-1)/2|0]}),e.init(t,s,n)}});var Ir,Lr,Br,Yr={name:"css",register:Wd,targetTest:function targetTest(t){return t.style&&t.nodeType},init:function init(t,e,i,n,a){var s,o,u,h,l,f,d,c,p,_,m,g,v,y,T,b,w,x=this._props,k=t.style,O=i.vars.startAt;for(d in Ne||Wd(),this.styles=this.styles||Qd(t),b=this.styles.props,this.tween=i,e)if("autoRound"!==d&&(o=e[d],!mt[d]||!dc(d,e,i,n,t,a)))if(l=typeof o,f=Or[d],"function"===l&&(l=typeof(o=o.call(i,n,t,a))),"string"===l&&~o.indexOf("random(")&&(o=rb(o)),f)f(this,t,d,o,i)&&(T=1);else if("--"===d.substr(0,2))s=(getComputedStyle(t).getPropertyValue(d)+"").trim(),o+="",Rt.lastIndex=0,Rt.test(s)||(c=_a(s),(p=_a(o))?c!==p&&(s=de(t,d,s,p)+p):c&&(o+=c)),this.add(k,"setProperty",s,o,n,a,0,0,d),x.push(d),b.push(d,0,k[d]);else if("undefined"!==l){if(O&&d in O?(s="function"==typeof O[d]?O[d].call(i,n,t,a):O[d],r(s)&&~s.indexOf("random(")&&(s=rb(s)),_a(s+"")||"auto"===s||(s+=N.units[d]||_a(xr(t,d))||""),"="===(s+"").charAt(1)&&(s=xr(t,d))):s=xr(t,d),h=parseFloat(s),(_="string"===l&&"="===o.charAt(1)&&o.substr(0,2))&&(o=o.substr(2)),u=parseFloat(o),d in mr&&("autoAlpha"===d&&(1===h&&"hidden"===xr(t,"visibility")&&u&&(h=0),b.push("visibility",0,k.visibility),ae(this,k,"visibility",h?"inherit":"hidden",u?"inherit":"hidden",!u)),"scale"!==d&&"transform"!==d&&~(d=mr[d]).indexOf(",")&&(d=d.split(",")[0])),m=d in hr){if(this.styles.save(d),w=o,"string"===l&&"var(--"===o.substring(0,6)){if("calc("===(o=Td(t,o.substring(4,o.indexOf(")")))).substring(0,5)){var M=t.style.perspective;t.style.perspective=o,o=Td(t,"perspective"),M?t.style.perspective=M:_d(t,"perspective")}u=parseFloat(o)}if(g||((v=t._gsap).renderTransform&&!e.parseTransform||Pr(t,e.parseTransform),y=!1!==e.smoothOrigin&&v.smooth,(g=this._pt=new we(this._pt,k,gr,0,1,v.renderTransform,v,0,-1)).dep=1),"scale"===d)this._pt=new we(this._pt,v,"scaleY",v.scaleY,(_?ma(v.scaleY,_+u):u)-v.scaleY||0,yd),this._pt.u=0,x.push("scaleY",d),d+="X";else{if("transformOrigin"===d){b.push(vr,0,k[vr]),o=he(o),v.svg?pe(t,o,0,y,0,this):((p=parseFloat(o.split(" ")[2])||0)!==v.zOrigin&&ae(this,v,"zOrigin",v.zOrigin,p),ae(this,k,d,Ar(s),Ar(o)));continue}if("svgOrigin"===d){pe(t,o,1,y,0,this);continue}if(d in Cr){ze(this,v,d,h,_?ma(h,_+o):o);continue}if("smoothOrigin"===d){ae(this,v,"smooth",v.smooth,o);continue}if("force3D"===d){v[d]=o;continue}if("transform"===d){Be(this,o,t);continue}}}else d in k||(d=Tr(d)||d);if(m||(u||0===u)&&(h||0===h)&&!_r.test(o)&&d in k)u=u||0,(c=(s+"").substr((h+"").length))!==(p=_a(o)||(d in N.units?N.units[d]:c))&&(h=de(t,d,s,p)),this._pt=new we(this._pt,m?v:k,d,h,(_?ma(h,_+u):u)-h,m||"px"!==p&&"zIndex"!==d||!1===e.autoRound?yd:Cd),this._pt.u=p||0,m&&w!==o?(this._pt.b=s,this._pt.e=w,this._pt.r=Bd):c!==p&&"%"!==p&&(this._pt.b=s,this._pt.r=Ad);else if(d in k)fe.call(this,t,d,s,_?_+o:o);else if(d in t)this.add(t,d,s||t[d],_?_+o:o,n,a);else if("parseTransform"!==d){S(d,o);continue}m||(d in k?b.push(d,0,k[d]):"function"==typeof t[d]?b.push(d,2,t[d]()):b.push(d,1,s||t[d])),x.push(d)}T&&be(this)},render:function render(t,e){if(e.tween._time||!Ue())for(var r=e._pt;r;)r.r(t,r.d),r=r._next;else e.styles.revert()},get:xr,aliases:mr,getSetter:function getSetter(t,e,r){var i=mr[e];return i&&i.indexOf(",")<0&&(e=i),e in hr&&e!==vr&&(t._gsap.x||xr(t,"x"))?r&&Ve===r?"scale"===e?Id:Hd:(Ve=r||{})&&("scale"===e?Jd:Kd):t.style&&!u(t.style[e])?Fd:~e.indexOf("-")?Gd:le(t,e)},core:{_removeProperty:_d,_getMatrix:oe}};Fe.utils.checkPrefix=Tr,Fe.core.getStyleSaver=Qd,Br=ja((Ir="x,y,z,scale,scaleX,scaleY,xPercent,yPercent")+","+(Lr="rotation,rotationX,rotationY,skewX,skewY")+",transform,transformOrigin,svgOrigin,force3D,smoothOrigin,transformPerspective",function(t){hr[t]=1}),ja(Lr,function(t){N.units[t]="deg",Cr[t]=1}),mr[Br[13]]=Ir+","+Lr,ja("0:translateX,1:translateY,2:translateZ,8:rotate,8:rotationZ,8:rotateZ,9:rotateX,10:rotateY",function(t){var e=t.split(":");mr[e[1]]=Br[e[0]]}),ja("x,y,z,top,right,bottom,left,width,height,fontSize,padding,margin,perspective",function(t){N.units[t]="px"}),Fe.registerPlugin(Yr);var Nr=Fe.registerPlugin(Yr)||Fe,jr=Nr.core.Tween;e.Back=ir,e.Bounce=ar,e.CSSPlugin=Yr,e.Circ=ur,e.Cubic=Ke,e.Elastic=rr,e.Expo=or,e.Linear=Qe,e.Power0=qe,e.Power1=Ze,e.Power2=We,e.Power3=He,e.Power4=Je,e.Quad=Ge,e.Quart=$e,e.Quint=tr,e.Sine=sr,e.SteppedEase=nr,e.Strong=er,e.TimelineLite=Zt,e.TimelineMax=Zt,e.TweenLite=te,e.TweenMax=jr,e.default=Nr,e.gsap=Nr;if (typeof(window)==="undefined"||window!==e){Object.defineProperty(e,"__esModule",{value:!0})} else {delete e.default}});
diff --git a/series2/assets/products/fairborn.jpg b/series2/assets/products/fairborn.jpg
new file mode 100644
index 0000000..ac6ec12
Binary files /dev/null and b/series2/assets/products/fairborn.jpg differ
diff --git a/series2/assets/products/kaya.jpg b/series2/assets/products/kaya.jpg
new file mode 100644
index 0000000..2c1c22d
Binary files /dev/null and b/series2/assets/products/kaya.jpg differ
diff --git a/series2/assets/products/topiary.jpg b/series2/assets/products/topiary.jpg
new file mode 100644
index 0000000..32300f2
Binary files /dev/null and b/series2/assets/products/topiary.jpg differ
diff --git a/series2/assets/products/waterperry.jpg b/series2/assets/products/waterperry.jpg
new file mode 100644
index 0000000..c437c73
Binary files /dev/null and b/series2/assets/products/waterperry.jpg differ
diff --git a/series2/assets/products/wheaton.jpg b/series2/assets/products/wheaton.jpg
new file mode 100644
index 0000000..77ff44d
Binary files /dev/null and b/series2/assets/products/wheaton.jpg differ
diff --git a/series2/audio.py b/series2/audio.py
new file mode 100644
index 0000000..f6d5bdc
--- /dev/null
+++ b/series2/audio.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+"""Local Kokoro narrator, measured caption times, speech-driven mouth envelope, restrained score."""
+import json, math, subprocess
+from pathlib import Path
+import numpy as np
+import soundfile as sf
+from kokoro_onnx import Kokoro
+
+ROOT=Path(__file__).resolve().parent
+CACHE=Path.home()/'.cache/hyperframes/tts'
+engine=Kokoro(str(CACHE/'models/kokoro-v1.0.onnx'),str(CACHE/'voices/voices-v1.0.bin'))
+episodes=json.loads((ROOT/'episodes.json').read_text())
+rate=24000
+metadata=[]
+for ep_index,ep in enumerate(episodes):
+    voice=np.zeros(rate*20,dtype=np.float32)
+    segments=[]
+    starts=[0.6,5.1,9.6,14.1]
+    for i,line in enumerate(ep['lines']):
+        output=ROOT/'assets/voice'/f'{ep["slug"]}-{i}.wav'
+        if output.exists(): samples,sr=sf.read(output,dtype='float32')
+        else:
+            samples,sr=engine.create(line,voice='am_michael',speed=1.02,lang='en-us')
+            sf.write(output,samples,sr)
+        duration=len(samples)/sr
+        max_duration=4.22 if i<3 else 4.8
+        if duration>max_duration:
+            tempo=duration/max_duration
+            assert tempo<1.4, (ep['slug'],i,duration)
+            fitted=output.with_name(output.stem+'-fit.wav')
+            subprocess.run(['ffmpeg','-v','error','-y','-i',str(output),'-af',f'atempo={tempo}','-ar',str(rate),str(fitted)],check=True)
+            samples,sr=sf.read(fitted,dtype='float32')
+        duration=len(samples)/sr
+        assert sr==rate and starts[i]+duration<19.5
+        samples=samples*.65/max(.65,float(np.max(np.abs(samples))))
+        pos=round(starts[i]*rate);voice[pos:pos+len(samples)]+=samples
+        segments.append({'index':i,'start':starts[i],'end':round(starts[i]+duration,3),'text':line,'caption':ep['captions'][i],'duration':round(duration,3)})
+        print(ep['slug'],i,round(duration,2),'s',flush=True)
+    # A sparse original warm keyboard score, kept well below the spoken voice.
+    score=np.zeros(rate*20,dtype=np.float32)
+    chords=[[130.813,164.814,195.998,246.942],[110,146.832,174.614,220],[130.813,164.814,195.998,261.626],[97.999,146.832,195.998,246.942]]
+    for beat in range(24):
+        start=beat*.8
+        note=chords[(beat//6+ep_index)%4][beat%4]
+        length=min(2.5,20-start)
+        if length<=0:continue
+        ts=np.arange(int(length*rate))/rate
+        tone=(np.sin(2*np.pi*note*ts)+.23*np.sin(2*np.pi*note*2*ts)+.07*np.sin(2*np.pi*note*3*ts))
+        env=np.minimum(1,ts/.012)*np.exp(-ts*2.1)*.019
+        at=int(start*rate);score[at:at+len(ts)]+=tone*env
+    # Measured speech energy also controls mouth openness and dynamic bed ducking.
+    energy=[]
+    for i in range(1000):
+        frame=voice[i*480:(i+1)*480]
+        energy.append(float(min(1,np.sqrt(np.mean(frame**2))/.13)))
+    duck=np.repeat(1-np.minimum(1,np.array(energy)*3)*.60,480)
+    score*=duck
+    fade=np.minimum(1,np.arange(rate*20)/(rate*.25))*np.minimum(1,(rate*20-np.arange(rate*20))/(rate*.7))
+    mix=(voice+score)*fade
+    stereo=np.column_stack((mix,mix))
+    sf.write(ROOT/'assets'/f'mix-{ep_index}.wav',stereo,rate,subtype='PCM_16')
+    metadata.append({'slug':ep['slug'],'provider':'Kokoro local','voice':'am_michael','duration':20,'segments':segments,'energy':[round(v,4) for v in energy],'peak':float(np.max(np.abs(mix)))})
+(ROOT/'audio.json').write_text(json.dumps(metadata,indent=2)+'\n')
+print('Five narrated 20-second mixes complete.',flush=True)
diff --git a/series2/build.mjs b/series2/build.mjs
new file mode 100644
index 0000000..659624a
--- /dev/null
+++ b/series2/build.mjs
@@ -0,0 +1,15 @@
+import fs from 'node:fs';
+import path from 'node:path';
+const root=import.meta.dirname;
+const episodes=JSON.parse(fs.readFileSync(path.join(root,'episodes.json')));
+const products=JSON.parse(fs.readFileSync(path.join(root,'products.json')));
+const audio=JSON.parse(fs.readFileSync(path.join(root,'audio.json')));
+const animation=fs.readFileSync(path.join(root,'animation.js'),'utf8');
+for(const [i,ep] of episodes.entries()){
+ const make=(prefix)=>`<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=1080,height=1920"><title>Wallo presents — ${ep.shortTitle}</title><style>@font-face{font-family:Georgia;src:url('${prefix}assets/georgia.ttf')}@font-face{font-family:Verdana;src:url('${prefix}assets/verdana.ttf')}html,body{margin:0;width:1080px;height:1920px;overflow:hidden}#root{position:relative;width:1080px;height:1920px;overflow:hidden}.clip{position:absolute;inset:0;width:1080px;height:1920px}canvas{width:1080px;height:1920px}.preload{position:absolute;opacity:0;pointer-events:none}</style><script src="${prefix}assets/gsap.min.js"></script></head><body><div id="root" data-composition-id="main" data-start="0" data-duration="20" data-width="1080" data-height="1920" data-fps="30"><section id="material-scene" class="clip" data-start="0" data-duration="20" data-track-index="0"><canvas id="stage" width="1080" height="1920" aria-label="Wallo presenting ${ep.shortTitle.replaceAll('"','')}"></canvas><span class="preload" style="font-family:Georgia">Wallo</span><span class="preload" style="font-family:Verdana">Designer Wallcoverings</span></section><audio id="narrated-mix" src="${prefix}assets/mix-${i}.wav" data-start="0" data-duration="20" data-track-index="1"></audio></div><script>window.EPISODE=${JSON.stringify({...ep,index:i,audio:audio[i]})};window.PRODUCTS=${JSON.stringify(products.map(p=>({...p,local:prefix+p.file})))};${animation}</script></body></html>`;
+ fs.writeFileSync(path.join(root,'episodes',ep.slug+'.html'),make('../'));
+ fs.mkdirSync(path.join(root,'verification','check-'+ep.slug),{recursive:true});
+ fs.writeFileSync(path.join(root,'verification','check-'+ep.slug,'index.html'),make('../../'));
+ if(i===0)fs.writeFileSync(path.join(root,'index.html'),make(''));
+}
+console.log('Built five 20-second Wallo presentation compositions.');
diff --git a/series2/delivery-proof.mjs b/series2/delivery-proof.mjs
new file mode 100644
index 0000000..1f8de05
--- /dev/null
+++ b/series2/delivery-proof.mjs
@@ -0,0 +1,58 @@
+import fs from 'node:fs';
+import path from 'node:path';
+import assert from 'node:assert/strict';
+import crypto from 'node:crypto';
+import {execFileSync} from 'node:child_process';
+import {createRequire} from 'node:module';
+const require=createRequire(import.meta.url),root=import.meta.dirname;
+const {chromium}=require('/Users/macstudio3/Projects/Designer-Wallcoverings/node_modules/playwright');
+const manifest=JSON.parse(fs.readFileSync(path.join(root,'memes/manifest.json')));
+const evidence={at:new Date().toISOString(),memes:[],gallery:{},audio:[]};
+const browser=await chromium.launch({executablePath:'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',headless:true});
+try{
+ const page=await browser.newPage({viewport:{width:1440,height:1200}}),errors=[];
+ page.on('pageerror',e=>errors.push(e.message));
+ await page.goto('file://'+path.join(root,'memes/index.html'));
+ await page.evaluate(async()=>{for(const i of document.images){i.loading='eager';await i.decode();}});
+ assert.equal(await page.locator('figure').count(),10);
+ evidence.gallery.offlineImages='10/10 decoded';
+ const {port}=JSON.parse(fs.readFileSync(path.join(root,'verification/server.json')));
+ await page.goto('http://127.0.0.1:'+port+'/memes/index.html');
+ await page.evaluate(async()=>{for(const i of document.images){i.loading='eager';await i.decode();}});
+ for(const m of manifest){
+  const data=fs.readFileSync(path.join(root,'memes',m.file));
+  assert.equal(crypto.createHash('sha256').update(data).digest('hex'),m.sha256);assert(m.width>=1024);assert.equal(m.width,m.height);
+  const [download]=await Promise.all([page.waitForEvent('download'),page.locator('[id="'+m.id+'"] a[download]').click()]);
+  assert.equal(download.suggestedFilename(),m.file);assert.equal(await download.failure(),null);
+  evidence.memes.push({id:m.id,width:m.width,height:m.height,download:'PASS',sha256:m.sha256});
+  console.log(m.id+' download PASS');
+ }
+ // The all-in-one ZIP is a separate user journey from ten rapid individual downloads.
+ const zipPage=await browser.newPage();await zipPage.goto('http://127.0.0.1:'+port+'/memes/index.html');
+ const [zip]=await Promise.all([zipPage.waitForEvent('download'),zipPage.locator('a.download').click()]);assert.equal(await zip.failure(),null);
+ await zipPage.close();console.log('ZIP download PASS');
+ const entries=execFileSync('unzip',['-Z1',path.join(root,'memes/installer-follies-10-memes.zip')],{encoding:'utf8'}).trim().split('\n');
+ assert.deepEqual(entries.filter(x=>x.endsWith('.png')).sort(),manifest.map(m=>m.file).sort());
+ await page.screenshot({path:path.join(root,'verification/memes-gallery.png'),fullPage:true});
+ await page.setViewportSize({width:390,height:844});await page.screenshot({path:path.join(root,'verification/memes-mobile.png')});
+ assert(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth));
+ await page.locator('nav a').first().click();await page.waitForSelector('#presentation');
+ await page.evaluate(async()=>{const v=document.querySelector('#presentation');v.muted=true;await v.play();});
+ await page.waitForFunction(()=>document.querySelector('#presentation').currentTime>.4);
+ for(const t of [0,20,40,60,80]){
+  await page.locator('[data-jump="'+t+'"]').click();
+  await page.waitForFunction(t=>{const v=document.querySelector('#presentation');return v.currentTime>=t&&v.currentTime<t+3;},t);
+ }
+ await page.evaluate(()=>document.querySelector('#presentation').pause());
+ assert(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth));
+ await page.screenshot({path:path.join(root,'verification/presentation-mobile.png'),fullPage:true});
+ await page.locator('a[href="memes/index.html"]').first().click();await page.waitForSelector('figure');
+ assert.deepEqual(errors,[]);evidence.gallery={...evidence.gallery,verdict:'PASS',downloadTransport:'localhost HTTP',individualDownloads:10,zipPngs:10,mobileWidth:390,chapterJumps:5,roundTripNavigation:true,errors};
+ for(const ep of JSON.parse(fs.readFileSync(path.join(root,'episodes.json')))){
+  const audio=execFileSync('ffmpeg',['-v','error','-i',path.join(root,'renders',ep.slug+'.mp4'),'-vn','-f','f32le','-ac','1','pipe:1'],{maxBuffer:10e6});
+  let peak=0,sum=0;for(let i=0;i<audio.length;i+=4){const x=audio.readFloatLE(i);peak=Math.max(peak,Math.abs(x));sum+=x*x;}
+  const rms=Math.sqrt(sum/(audio.length/4));assert(peak>.05&&peak<1);assert(rms>.005);
+  evidence.audio.push({slug:ep.slug,peak,rms,verdict:'PASS'});
+ }
+ evidence.verdict='PASS';console.log('Delivery PASS: 10 memes, all downloads, ZIP, mobile galleries, five chapter jumps and five audio tracks.');
+}finally{await browser.close();fs.writeFileSync(path.join(root,'verification/delivery-proof.json'),JSON.stringify(evidence,null,2));}
diff --git a/series2/episodes/04-fire-rated.html b/series2/episodes/04-fire-rated.html
new file mode 100644
index 0000000..d141901
--- /dev/null
+++ b/series2/episodes/04-fire-rated.html
@@ -0,0 +1,215 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=1080,height=1920"><title>Wallo presents — Why fire-rated wallcovering?</title><style>@font-face{font-family:Georgia;src:url('../assets/georgia.ttf')}@font-face{font-family:Verdana;src:url('../assets/verdana.ttf')}html,body{margin:0;width:1080px;height:1920px;overflow:hidden}#root{position:relative;width:1080px;height:1920px;overflow:hidden}.clip{position:absolute;inset:0;width:1080px;height:1920px}canvas{width:1080px;height:1920px}.preload{position:absolute;opacity:0;pointer-events:none}</style><script src="../assets/gsap.min.js"></script></head><body><div id="root" data-composition-id="main" data-start="0" data-duration="20" data-width="1080" data-height="1920" data-fps="30"><section id="material-scene" class="clip" data-start="0" data-duration="20" data-track-index="0"><canvas id="stage" width="1080" height="1920" aria-label="Wallo presenting Why fire-rated wallcovering?"></canvas><span class="preload" style="font-family:Georgia">Wallo</span><span class="preload" style="font-family:Verdana">Designer Wallcoverings</span></section><audio id="narrated-mix" src="../assets/mix-0.wav" data-start="0" data-duration="20" data-track-index="1"></audio></div><script>window.EPISODE={"slug":"04-fire-rated","title":"Fire-rated.\nProperly specified.","shortTitle":"Why fire-rated wallcovering?","kicker":"01 / FIRE PERFORMANCE","product":"fairborn","note":"Classification ≠ fireproof. Confirm project requirements.","takeaway":"Request the test report.","lines":["Fire performance belongs in the specification, right alongside the design.","ASTM E eighty-four measures surface flame spread and smoke development.","A classification is not a promise that wallpaper is fireproof.","Request the product's test report and confirm the installation requirements."],"captions":["Fire performance belongs in the specification.","ASTM E84: surface flame spread + smoke development.","A classification does not mean fireproof.","Get the report. Confirm the installation requirements."],"index":0,"audio":{"slug":"04-fire-rated","provider":"Kokoro local","voice":"am_michael","duration":20,"segments":[{"index":0,"start":0.6,"end":4.825,"text":"Fire performance belongs in the specification, right alongside the design.","caption":"Fire performance belongs in the specification.","duration":4.225},{"index":1,"start":5.1,"end":9.326,"text":"ASTM E eighty-four measures surface flame spread and smoke development.","caption":"ASTM E84: surface flame spread + smoke development.","duration":4.226},{"index":2,"start":9.6,"end":13.803,"text":"A classification is not a promise that wallpaper is fireproof.","caption":"A classification does not mean fireproof.","duration":4.203},{"index":3,"start":14.1,"end":18.857,"text":"Request the product's test report and confirm the installation requirements.","caption":"Get the report. Confirm the installation requirements.","duration":4.757}],"energy":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0012,0.0047,0.0174,0.0557,0.0887,0.6506,0.9948,0.9031,0.7399,0.7077,0.583,0.5662,0.5773,0.6428,0.7302,0.2114,0.018,0.0035,0.169,0.3363,0.6278,0.2073,0.1404,0.1312,0.1064,0.1011,0.0759,0.381,0.5915,0.6192,0.6481,0.5202,0.4138,0.4348,0.4805,0.4767,0.4303,0.3543,0.2276,0.2018,0.3587,0.0515,0.0017,0.0031,0.0747,0.4884,0.4711,0.2873,0.2918,0.3463,0.3885,0.4873,0.6173,0.5417,0.6643,0.6181,0.5684,0.4733,0.4419,0.3606,0.3829,0.38,0.3523,0.2186,0.219,0.275,0.2166,0.3565,0.3256,0.3499,0.3976,0.3682,0.341,0.3755,0.2461,0.4064,0.4989,0.2891,0.0437,0.0029,0.3856,0.5779,0.4828,0.3195,0.1977,0.3538,0.3409,0.2953,0.2868,0.1419,0.0679,0.0611,0.0587,0.2512,0.0993,0.0146,0.1102,0.0638,0.1303,0.4596,0.4405,0.3785,0.3033,0.2883,0.2116,0.5138,0.4594,0.4209,0.397,0.1721,0.0989,0.0726,0.0239,0.0385,0.0181,0.0155,0.0021,0.0015,0.0019,0.0025,0.0034,0.0034,0.0041,0.0043,0.0062,0.0073,0.0066,0.0065,0.0051,0.0046,0.003,0.0015,0.0015,0.071,0.1492,0.2249,0.4519,0.9385,1,0.9874,1,1,0.8162,0.6613,0.3443,0.0984,0.038,0.18,0.4506,0.4012,0.3595,0.3249,0.3225,0.3881,0.6092,0.5798,0.6589,0.6693,0.5921,0.4484,0.4239,0.3984,0.4371,0.2673,0.1554,0.3871,0.4242,0.3069,0.2522,0.208,0.4185,0.3076,0.3236,0.3637,0.3982,0.4049,0.2554,0.1402,0.0903,0.0895,0.2676,0.2165,0.1187,0.1217,0.2067,0.3746,0.3223,0.2415,0.2228,0.2132,0.2597,0.5527,0.5283,0.4602,0.4673,0.5429,0.5048,0.3489,0.2544,0.1282,0.092,0.0527,0.0398,0.0231,0.0139,0.017,0.0189,0.0091,0.0026,0.0013,0.0008,0.0004,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0016,0.0034,0.0018,0.0031,0.0021,0.5753,0.9468,0.8893,1,0.9721,0.9578,1,1,1,1,0.7674,0.3763,0.6669,0.4738,0.1212,0.0101,0.133,0.4414,0.5266,0.5228,0.5135,0.4629,0.3655,0.2989,0.2832,0.3768,0.3985,0.4954,0.6561,0.7018,0.7038,0.7379,0.6468,0.5082,0.2998,0.2548,0.1823,0.0865,0.0091,0.0048,0.0043,0.0039,0.0028,0.0022,0.0017,0.0043,0.2383,0.5278,0.6138,0.511,0.3618,0.2954,0.0578,0.0121,0.0024,0.6567,0.8351,0.753,0.7574,0.7543,0.6554,0.6476,0.2019,0.3876,0.4468,0.3598,0.2605,0.144,0.1311,0.1131,0.118,0.0709,0.2716,0.6081,0.4748,0.4659,0.3909,0.4397,0.3742,0.3556,0.3079,0.3188,0.3661,0.4515,0.5435,0.4919,0.481,0.4957,0.3947,0.2402,0.281,0.225,0.3107,0.3294,0.3121,0.2211,0.4887,0.5531,0.4717,0.3992,0.3184,0.4129,0.5114,0.867,0.8262,0.7312,0.6503,0.1258,0.1174,0.1011,0.0759,0.1406,0.3891,0.2883,0.4013,0.4434,0.2424,0.0973,0.0715,0.0662,0.0547,0.2457,0.3585,0.4489,0.4251,0.404,0.4348,0.467,0.4782,0.5044,0.4776,0.4044,0.2293,0.4516,0.5444,0.6926,0.1904,0.0113,0.0015,0.0278,0.2218,0.5795,0.8909,0.9262,0.8666,0.9335,0.9704,0.9715,0.6365,0.5611,0.3318,0.2355,0.1121,0.1231,0.2565,0.3342,0.3726,0.4115,0.3258,0.1821,0.4132,0.5307,0.4751,0.3645,0.0725,0.0757,0.4859,0.6157,0.6818,0.6687,0.6689,0.6436,0.3866,0.0566,0.031,0.0066,0.0028,0.0022,0.1254,0.4669,0.4207,0.2224,0.1986,0.6867,0.7368,0.7779,0.7788,0.5736,0.3936,0.3443,0.2432,0.162,0.0362,0.0277,0.0449,0.0565,0.0946,0.0578,0.021,0.0131,0.0075,0.0038,0.0015,0.0014,0.0015,0.0008,0.0006,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0021,0.0021,0.0016,0.0025,0.3618,0.4943,0.2485,0.0591,0.0108,0.2846,0.3245,0.1782,0.8183,1,1,1,1,0.9405,0.44,0.5547,0.6129,0.5737,0.2519,0.6548,0.1975,0.1419,0.1302,0.3439,0.4803,0.1806,0.0764,0.0145,0.1717,0.1056,0.3375,0.7334,0.7553,0.6525,0.5488,0.4684,0.4379,0.3368,0.6547,0.6238,0.558,0.5201,0.1816,0.3275,0.2462,0.1531,0.0681,0.0863,0.0664,0.0569,0.0194,0.0051,0.0036,0.0049,0.0053,0.0065,0.0072,0.0065,0.0065,0.0066,0.0067,0.0062,0.0038,0.0042,0.0135,0.2344,0.6378,0.5686,0.6023,0.6041,0.3348,0.6032,0.8015,0.7852,0.8026,0.6993,0.6735,0.7098,0.8779,0.6633,0.7119,1,0.7157,0.1544,0.0266,0.0042,0.0039,0.1643,0.0768,0.0882,0.0576,0.6191,0.932,0.8178,0.7666,0.805,0.8902,1,0.9878,0.7006,0.6795,0.5953,0.5962,0.5852,0.5394,0.4726,0.3754,0.4374,0.2924,0.1028,0.0246,0.0232,0.4833,0.4923,0.5045,0.3176,0.2895,0.1274,0.132,0.1741,0.2316,0.2645,0.3002,0.4124,0.7962,0.8176,1,1,1,1,1,1,1,0.5335,0.1178,0.0253,0.0046,0.1096,0.2661,0.4154,0.5833,0.5267,0.4882,0.4096,0.3273,0.1101,0.0117,0.0046,0.1091,0.1814,0.3517,0.3902,0.3635,0.3318,0.3113,0.1517,0.0653,0.0335,0.2053,0.2957,0.3609,0.4206,0.6234,0.6289,0.2044,0.1255,0.1004,0.0725,0.055,0.3067,0.6165,0.4176,0.354,0.4898,0.5081,0.5526,0.4784,0.4998,0.4946,0.5018,0.475,0.3486,0.1177,0.0163,0.0043,0.0045,0.1248,0.0767,0.0727,0.2303,0.3068,0.1559,0.0856,0.0547,0.0223,0.0231,0.0122,0.0198,0.0274,0.035,0.0317,0.0175,0.0061,0.0019,0.0011,0.0009,0.0003,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0013,0.0129,0.1954,0.5581,0.7718,0.6137,0.2179,0.0355,0.0654,0.3038,0.155,0.0924,1,1,1,1,1,1,0.6747,0.4901,0.8324,0.556,0.2242,0.0614,0.0134,0.0222,0.337,0.6958,0.1979,0.0702,0.0113,0.0443,0.2074,0.0955,0.6543,0.6854,0.7905,0.6735,0.7761,0.7956,0.7237,0.4511,0.4212,0.6556,0.6012,0.5466,0.3626,0.1186,0.0892,0.2297,0.5434,0.5387,0.1668,0.0055,0.0045,0.1272,0.2692,0.1303,0.3839,0.5868,0.7874,0.7544,0.6104,0.442,0.3928,0.8115,0.5113,0.0873,0.0417,0.0296,0.0107,0.1209,0.2818,0.5463,0.5581,0.4529,0.2372,0.0633,0.0116,0.0068,0.1719,0.0887,0.1023,0.0726,0.5641,0.8498,1,0.9576,0.7551,0.7402,0.4627,0.1733,0.1045,0.0625,0.049,0.0329,0.0154,0.0033,0.0036,0.0023,0.0022,0.0028,0.0036,0.0043,0.0043,0.0052,0.0056,0.0068,0.0074,0.0081,0.0084,0.0095,0.011,0.0112,0.008,0.0089,0.1539,0.5766,0.6525,0.7862,0.8312,0.4877,0.1702,0.2287,0.2716,0.7589,0.8968,0.5656,0.6187,0.4997,0.2914,0.165,0.1191,0.132,0.1262,0.1389,0.0958,0.5584,1,1,0.895,0.9682,0.9233,0.9071,0.8977,0.8346,0.6209,0.6791,0.6277,0.5404,0.4059,0.2492,0.4399,0.4487,0.351,0.2837,0.1986,0.2479,0.3492,0.4258,0.546,0.5376,0.4801,0.2657,0.5963,0.5195,0.104,0.0039,0.1108,0.3389,0.6348,0.2999,0.3697,0.3802,0.5559,0.4946,0.5133,0.6291,0.5863,0.5183,0.4228,0.2744,0.582,0.7211,0.5863,0.5963,0.1387,0.4452,0.4537,0.3951,0.3685,0.3926,0.3758,0.3445,0.3244,0.3976,0.4419,0.4081,0.2139,0.0433,0.0107,0.1623,0.1254,0.0529,0.0498,0.409,0.759,0.7565,0.6322,0.6556,0.6586,0.7206,0.667,0.6501,0.5284,0.3946,0.3077,0.2734,0.1682,0.1285,0.1159,0.0953,0.0747,0.0651,0.0301,0.024,0.0274,0.0073,0.0524,0.1172,0.2582,0.2937,0.2481,0.1612,0.0368,0.0054,0.002,0.0009,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"peak":0.6490918397903442}};window.PRODUCTS=[{"id":"fairborn","title":"Fairborn Type 2 | Phillipe Romano","label":"Fairborn Type 2 · Phillipe Romano","url":"https://www.designerwallcoverings.com/products/prw-920735-rif-003","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/prw-920735-rif-003.jpg?v=1781716604","file":"assets/products/fairborn.jpg","description":"<p>When light rakes across the embossed surface, it reveals an organic grid-like pattern of broken lines. Although extremely durable — as you’d expect from a Type II vinyl wallcovering named for rock fissures — offering a soft, nature-inspired palette of creams, taupes, blues and browns. A small scale and intricate pattern appear almost seamless, and its 12 two-tone colorways resemble artisanal materials like mosaic</p><br><br>A captivating wallcovering featuring intricate detailing and refined textures, offering a sophisticated backdrop that elevates any interior space. The design evokes timeless elegance with a modern touch.  \n#luxuryinteriors #modernwallcoverings #elegantdesign\n","retrieved_at":"2026-09-05","local":"../assets/products/fairborn.jpg"},{"id":"waterperry","title":"Waterperry Ivory/Stone | Sanderson","label":"Waterperry / Ivory–Stone · Sanderson","url":"https://www.designerwallcoverings.com/products/waterperry-ivory-stone-sanderson","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DWAP216280_5340.jpg?v=1788582264","file":"assets/products/waterperry.jpg","description":"<div class=\"sanderson-product\">\n<p>Waterperry in Ivory/Stone is a Sanderson Wallcovering in a botanical/leaves and trees design. Part of the Sanderson heritage collection, this pattern reads in a palette of Alabaster, Linen, Mist. Supplied by the roll; a memo sample is available. An English design-house classic from Sanderson Design Group.</p>\n</div>","retrieved_at":"2026-09-05","local":"../assets/products/waterperry.jpg"},{"id":"wheaton","title":"Wheaton Type 2 | Phillipe Romano","label":"Wheaton Type 2 · Phillipe Romano","url":"https://www.designerwallcoverings.com/products/prw-920166-coa-004-1","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/d4639abbf9baa5237707.jpg?v=1785438111","file":"assets/products/wheaton.jpg","description":"<p>Weaving shimmering strands and beguiling texture, emulating the intricacy of our favored grasscloth Tide in the form of a durable Type II vinyl</p>\n","retrieved_at":"2026-09-05","local":"../assets/products/wheaton.jpg"},{"id":"topiary","title":"Topiary View Thyme | Sanderson","label":"Topiary View / Thyme · Sanderson","url":"https://www.designerwallcoverings.com/products/topiary-view-thyme-sanderson","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DHIP217511_f500.jpg?v=1788582179","file":"assets/products/topiary.jpg","description":"<div class=\"sanderson-product\">\n<p>Topiary View in Thyme is a Sanderson Wallcovering in a landscapes design. Part of the Sanderson heritage collection, this pattern reads in a palette of Alabaster, Stone, Oatmeal. Supplied by the roll; a memo sample is available. An English design-house classic from Sanderson Design Group.</p>\n</div>","retrieved_at":"2026-09-05","local":"../assets/products/topiary.jpg"},{"id":"kaya","title":"Kaya - Cream Weave Wallcovering | Malibu Wallcovering","label":"Kaya / Cream Weave · Malibu","url":"https://www.designerwallcoverings.com/products/kaya-cream-weave-wallcovering-malibu-wallpaper","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/0086435_kaya-cream-weave_650.jpg?v=1776906632","file":"assets/products/kaya.jpg","description":"<p>Elevate your space with the luxurious Kaya Cream Weave wallcovering. This exquisite nonwoven vinyl design features a warm neutral color palette and stunning texture, perfect for creating an elegant ambiance.</p>","retrieved_at":"2026-09-05","local":"../assets/products/kaya.jpg"}];/* Wallo Material Notes: deterministic articulated character acting and real DW samples. */
+(() => {
+ 'use strict';
+ const canvas=document.getElementById('stage'), c=canvas.getContext('2d');
+ const E=window.EPISODE, P=window.PRODUCTS, ep=E.index;
+ const ink='#243a3b', chalk='#eeeae2', teal='#284f4b', clay='#a9634d', brass='#b7924f';
+ const images={}; let telemetry=[];
+ const clamp=(x,a=0,b=1)=>Math.max(a,Math.min(b,x));
+ const mix=(a,b,p)=>a+(b-a)*p;
+ const smooth=p=>{p=clamp(p);return p*p*(3-2*p);};
+ const ease=(t,a,b)=>smooth((t-a)/(b-a));
+ const pulse=(t,a,b,c,d)=>ease(t,a,b)*(1-ease(t,c,d));
+ const point=(x,y)=>({x,y});
+ const dist=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y);
+ function group(x,y,r,s,fn){c.save();c.translate(x,y);c.rotate(r);c.scale(s,s);fn();c.restore();}
+ function path(fn,fill,stroke=null,w=2){c.beginPath();fn(c);if(fill){c.fillStyle=fill;c.fill();}if(stroke){c.lineWidth=w;c.strokeStyle=stroke;c.lineJoin='round';c.lineCap='round';c.stroke();}}
+ function line(x,y,u,v,col,w=2){path(q=>{q.moveTo(x,y);q.lineTo(u,v);},null,col,w);}
+ function rect(x,y,w,h,col){c.fillStyle=col;c.fillRect(x,y,w,h);}
+ function oval(x,y,rx,ry,col,stroke=null,w=2){path(q=>q.ellipse(x,y,rx,ry,0,0,Math.PI*2),col,stroke,w);}
+ function rr(x,y,w,h,r,col,stroke=null,lw=2){path(q=>q.roundRect(x,y,w,h,r),col,stroke,lw);}
+ function text(s,x,y,size=28,color=ink,font='Verdana',align='left'){c.font=`${size}px '${font}'`;c.textAlign=align;c.textBaseline='alphabetic';c.fillStyle=color;c.fillText(s,x,y);}
+ function wrap(s,x,y,max,size=36,color=ink,font='Verdana',lh=size*1.4){
+   c.font=`${size}px '${font}'`;let row='',lines=[];
+   for(const word of s.split(' ')){const test=row?row+' '+word:word;if(c.measureText(test).width>max&&row){lines.push(row);row=word;}else row=test;}
+   if(row)lines.push(row);lines.forEach((v,i)=>text(v,x,y+i*lh,size,color,font));return lines.length;
+ }
+ function imageCover(id,x,y,w,h){const im=images[id];if(!im||!im.complete)return;const k=Math.max(w/im.width,h/im.height);c.save();c.beginPath();c.rect(x,y,w,h);c.clip();c.drawImage(im,x+(w-im.width*k)/2,y+(h-im.height*k)/2,im.width*k,im.height*k);c.restore();}
+ function sample(id,x,y,w=126,h=164,r=0){group(x,y,r,1,()=>{rr(-w/2+7,-h/2+9,w,h,2,'#203a3a20');rect(-w/2-5,-h/2-5,w+10,h+10,'#f9f7f0');imageCover(id,-w/2,-h/2,w,h);line(-w/2,-h/2,w/2,-h/2,'#ffffff',2);});}
+ function bone(a,b,width,col,shade){
+   const angle=Math.atan2(b.y-a.y,b.x-a.x),d=dist(a,b);
+   group(a.x,a.y,angle,1,()=>{rr(-width*.18,-width/2,d+width*.36,width,width*.46,col);path(q=>{q.moveTo(5,width*.26);q.quadraticCurveTo(d*.5,width*.34,d-3,width*.22);},null,shade||'#00000018',2.3);});
+ }
+ function solveIK(root,target,l1,l2,pole){
+   const dx=target.x-root.x,dy=target.y-root.y,raw=Math.hypot(dx,dy),d=clamp(raw,Math.abs(l1-l2)+.01,l1+l2-.01);
+   const nx=raw?dx/raw:0,ny=raw?dy/raw:1,a=(l1*l1-l2*l2+d*d)/(2*d),h=Math.sqrt(Math.max(0,l1*l1-a*a));
+   const end=point(root.x+nx*d,root.y+ny*d),joint=point(root.x+nx*a-ny*h*pole,root.y+ny*a+nx*h*pole);
+   return {root,joint,end,l1,l2};
+ }
+ function rotatePoint(p,angle,origin=point(0,-258)){const x=p.x-origin.x,y=p.y-origin.y;return point(origin.x+x*Math.cos(angle)-y*Math.sin(angle),origin.y+x*Math.sin(angle)+y*Math.cos(angle));}
+ function key(t,frames){if(t<=frames[0][0])return frames[0].slice(1);for(let i=1;i<frames.length;i++)if(t<=frames[i][0]){const p=ease(t,frames[i-1][0],frames[i][0]);return frames[i].slice(1).map((v,j)=>mix(frames[i-1][j+1],v,p));}return frames.at(-1).slice(1);}
+ function gait(t,start,end,x0,x1){
+   const p=clamp((t-start)/(end-start));
+   const lift=(a,b)=>p>a&&p<b?Math.sin(Math.PI*(p-a)/(b-a))*44:0;
+   const left=mix(x0-30,(x0+x1)/2-30,smooth((p-.08)/.28))+mix(0,(x1-x0)/2,smooth((p-.65)/.3));
+   const right=mix(x0+30,x1+30,smooth((p-.35)/.3));
+   return {x:(left+right)/2,feet:[point(left,-lift(.08,.36)-lift(.65,.95)),point(right,-lift(.35,.65))],walking:p>0&&p<1,phase:p};
+ }
+ function voiceEnergy(t){const env=E.audio?.energy||[];return env[Math.min(env.length-1,Math.max(0,Math.floor(t*50)))]||0;}
+ function hand(x,y,r,pose='open',skin='#c89472',prop=null){
+   group(x,y,r,1,()=>{
+    if(prop==='sample')sample(E.product,8,-18,115,148,-.04);
+    if(prop==='fan')for(let k=0;k<3;k++)sample(['waterperry','topiary','kaya'][k],k*17-15,-43,91,130,(k-1)*.18);
+    if(prop==='report'){rr(-12,-134,135,176,4,'#f8f5e9',ink,2);rect(1,-117,108,5,teal);text('TEST',4,-87,22,ink);text('REPORT',4,-58,22,ink);for(let k=0;k<3;k++)line(4,-35+k*18,97,-35+k*18,'#a4aaa0',2);}
+    if(prop==='cloth'){path(q=>{q.moveTo(-28,-15);q.lineTo(30,-26);q.lineTo(43,35);q.lineTo(-21,44);q.closePath();},'#c8c5b5',ink,1);line(-17,-4,28,25,'#efeadb',3);}
+    if(prop==='roller'){line(0,-8,0,-72,ink,7);line(0,-72,37,-72,ink,7);line(37,-72,37,-108,ink,5);rr(-27,-132,127,38,11,'#8b9c8d',ink,2);rr(-9,-9,18,54,7,brass);}
+    if(prop==='tablet'){rr(-73,-76,145,170,8,ink);rr(-65,-67,129,151,4,'#f4f0e5');text('DW',-45,-33,27,teal,'Georgia');for(let i=0;i<3;i++)imageCover(['waterperry','fairborn','kaya'][i],-47+i*32,-6,27,60);}
+    if(pose==='point'){bone(point(-4,3),point(34,-18),13,skin);bone(point(0,9),point(16,15),21,skin);oval(-6,9,13,18,skin);}
+    else{rr(-14,-9,29,42,12,skin);oval(-15,5,7,13,skin);for(let i=0;i<3;i++)line(-7+i*6,14,-7+i*6,28,'#9e685446',1);}
+   });
+ }
+ function head(kind,x,y,t,look,mouth,tilt){
+   const male=kind==='wallo',skin=male?'#c89472':'#d5a185',dark=male?'#714f3b':'#834e39';
+   group(x,y,tilt,.82,()=>{
+    if(!male){oval(-4,-10,51,70,dark);oval(-39,-50,28,28,dark);path(q=>{q.moveTo(-57,-55);q.quadraticCurveTo(-81,-37,-59,-16);},null,'#a86b4b',7);}
+    // Tapered jaw, defined brow and modest nose maintain adult proportions.
+    path(q=>{q.moveTo(-41,-40);q.bezierCurveTo(-43,-77,43,-80,44,-34);q.lineTo(40,31);q.quadraticCurveTo(31,66,5,72);q.quadraticCurveTo(-26,65,-36,34);q.closePath();},skin);
+    path(q=>{q.moveTo(-38,-31);q.lineTo(-32,34);q.quadraticCurveTo(-24,52,-8,57);q.lineTo(-20,21);q.lineTo(-18,-46);q.closePath();},male?'#b58162':'#c39076');
+    oval(-40,3,9,16,skin);oval(42,2,8,15,skin);
+    if(male){path(q=>{q.moveTo(-39,-39);q.lineTo(-33,-18);q.lineTo(-30,-54);q.lineTo(31,-54);q.lineTo(43,-25);q.lineTo(42,-55);q.closePath();},dark);}
+    else{path(q=>{q.moveTo(-40,-27);q.quadraticCurveTo(-24,-63,2,-56);q.quadraticCurveTo(38,-37,43,-19);q.lineTo(38,-58);q.quadraticCurveTo(-4,-89,-37,-55);q.closePath();},dark);for(let i=0;i<4;i++)path(q=>{q.moveTo(-29+i*17,-56);q.quadraticCurveTo(-26+i*17,-43,-13+i*17,-39);},null,'#ae7150',2);}
+    const blink=(t+(male?.31:1.93))%4.4<.14;
+    for(const side of [-1,1]){const ex=side*18+look*3,ey=-8;path(q=>{q.moveTo(ex-10,ey);q.quadraticCurveTo(ex,ey-5,ex+10,ey);},null,ink,2.2);if(!blink){oval(ex+look*3,ey+1,3.5,4.3,ink);oval(ex+look*3+1,ey-.3,1.1,1.2,'#eee8dc');}line(ex-10,ey-15,ex+8,ey-17+(side===1?look*2:0),dark,2.5);}
+    path(q=>{q.moveTo(3+look*5,-5);q.lineTo(8+look*8,15);q.quadraticCurveTo(4+look*5,20,-1+look*5,17);},null,'#98644f',1.8);
+    if(male){path(q=>{q.moveTo(-20,29);q.quadraticCurveTo(-6,20,4,27);q.quadraticCurveTo(15,23,26,30);q.quadraticCurveTo(17,39,3,34);q.quadraticCurveTo(-9,37,-20,29);},dark);}
+    if(mouth>.12){oval(4,44,10+mouth*3,2+mouth*6,'#714735');if(mouth>.4)line(-2,41,11,41,'#efe6d7',2);}
+    else path(q=>{q.moveTo(-7,43);q.quadraticCurveTo(5,48,17,42);},null,'#714735',1.8);
+    if(male){path(q=>{q.moveTo(-49,-53);q.quadraticCurveTo(-48,-98,10,-95);q.quadraticCurveTo(47,-89,48,-53);q.closePath();},brass);path(q=>{q.moveTo(-49,-53);q.quadraticCurveTo(12,-64,72,-48);q.quadraticCurveTo(77,-35,39,-41);q.lineTo(-49,-43);q.closePath();},'#97773f');line(-27,-73,12,-84,'#d2b270',3);}
+    else oval(-39,14,3.5,5.5,brass);
+   });
+ }
+ function character(kind,t,settings={}){
+   const male=kind==='wallo',skin=male?'#c89472':'#d5a185',shirt=male?'#d6d0bc':clay,cloth=male?teal:'#c8c1af';
+   const walk=settings.walk||gait(t,0,1,settings.x,settings.x);let x=walk.x,y=settings.y||882;
+   const crouch=settings.crouch||0,lean=settings.lean||0,breath=Math.sin(t*2.1+(male?0:1.2))*1.7;
+   const hipY=-275+crouch,origin=point(0,hipY);
+   const bodyPoint=(u,v)=>rotatePoint(point(u,v+crouch+breath),lean,origin);
+   const leftShoulder=bodyPoint(-62,-469),rightShoulder=bodyPoint(62,-469);
+   const arms=[solveIK(leftShoulder,point(...settings.left),118,111,-1),solveIK(rightShoulder,point(...settings.right),118,111,1)];
+   const legs=walk.feet.map((foot,i)=>solveIK(point(i===0?-32:32,hipY),point(foot.x-x,foot.y-13),134,136,i===0?1:-1));
+   const mouth=male?voiceEnergy(t)*.88:0;
+   const joints={kind,x,y,arms,legs,lean,crouch,feet:walk.feet,walking:walk.walking,props:settings.props||[]};
+   telemetry.push(joints);
+   oval(x,y+6,94-crouch*.1,15,'#233a3b15');
+   group(x,y,0,settings.scale||1,()=>{
+    // Back arm and far leg precede torso so elbow overlaps read spatially.
+    const armDraw=(i)=>{const b=arms[i];bone(b.root,b.joint,39,shirt);bone(b.joint,b.end,27,skin);oval(b.joint.x,b.joint.y,16,16,skin);const sleeve=point(mix(b.root.x,b.joint.x,.77),mix(b.root.y,b.joint.y,.77));bone(b.root,sleeve,43,shirt);line(sleeve.x-8,sleeve.y,sleeve.x+9,sleeve.y,'#ffffff30',2);};
+    armDraw(0);
+    legs.forEach((b,i)=>{bone(b.root,b.joint,58,cloth);bone(b.joint,b.end,43,cloth);oval(b.joint.x,b.joint.y,23,23,cloth);line(b.root.x-8,b.root.y+30,b.joint.x-6,b.joint.y-16,male?'#41635b':'#dfd8c8',2);group(b.end.x,b.end.y+10,walk.walking&&walk.feet[i].y<0?-.12:0,1,()=>{path(q=>{q.moveTo(-24,-20);q.lineTo(20,-20);q.lineTo(27,-9);q.quadraticCurveTo(60,-5,56,14);q.lineTo(-24,14);q.closePath();},male?'#655545':'#626b62');line(-24,14,55,14,'#34413d',5);line(4,-8,25,-7,'#c6b69b',2);});});
+    group(0,hipY,lean,1,()=>{
+     const dy=crouch+breath-hipY;
+     path(q=>{q.moveTo(-63,-478+dy);q.quadraticCurveTo(-82,-460+dy,-71,-417+dy);q.lineTo(-60,-266+dy);q.quadraticCurveTo(0,-235+dy,60,-266+dy);q.lineTo(71,-417+dy);q.quadraticCurveTo(79,-460+dy,58,-479+dy);q.closePath();},shirt);
+     // Structured overall/apron silhouette, stitched seams, pockets and cloth folds.
+     path(q=>{q.moveTo(-50,-425+dy);q.lineTo(-46,-350+dy);q.lineTo(-61,-290+dy);q.lineTo(-60,-253+dy);q.lineTo(61,-253+dy);q.lineTo(61,-290+dy);q.lineTo(46,-350+dy);q.lineTo(48,-425+dy);q.closePath();},cloth);
+     for(const s of [-1,1]){line(s*44,-472+dy,s*38,-405+dy,cloth,17);oval(s*39,-414+dy,4.8,4.8,brass);line(s*49,-328+dy,s*41,-312+dy,male?'#59756b':'#a9a18f',2);}
+     rr(-27,-401+dy,54,56,4,male?'#254540':'#bab39f');path(q=>{q.moveTo(-23,-388+dy);q.quadraticCurveTo(0,-376+dy,24,-388+dy);},null,male?'#6a8778':'#e5dfd0',1.5);
+     line(-50,-266+dy,52,-266+dy,male?'#1e403b':'#aca590',3);
+     if(male){rr(-66,-321+dy,20,67,3,'#8d7452');line(-56,-342+dy,-56,-282+dy,brass,6);}
+     path(q=>{q.moveTo(-17,-509+dy);q.lineTo(-18,-474+dy);q.quadraticCurveTo(0,-454+dy,21,-474+dy);q.lineTo(18,-509+dy);q.closePath();},skin);
+    });
+    const hp=bodyPoint(0,-559);head(kind,hp.x,hp.y,t,settings.look||0,mouth,(settings.headTilt||0)+lean*.28);
+    armDraw(1);
+    arms.forEach((b,i)=>hand(b.end.x,b.end.y,(settings.wrists||[0,0])[i],(settings.gestures||['open','open'])[i],skin,(settings.props||[])[i]));
+   });
+   return joints;
+ }
+ function room(t){
+   rect(-350,-300,1800,1200,'#dfdbd1');rect(-350,765,1800,450,'#c9c2b1');
+   rect(20,0,240,706,'#e9e7dc');for(let i=0;i<4;i++)line(22+i*59,0,22+i*59,704,'#f7f4e9',7);
+   rect(318,26,647,727,'#c7bfae');rect(329,37,624,704,'#efede3');
+   if(ep===1){const a=ease(t,5.1,8);rect(343,49,596,678,'#aab7a4');c.save();c.beginPath();c.rect(343,49,596*a,678);c.clip();imageCover(E.product,343,49,596,678);c.restore();}
+   else imageCover(E.product,343,49,596,678);
+   line(340,743,943,743,'#faf7eb',4);line(-350,765,1450,765,'#a79f8d',2);
+   for(let i=0;i<6;i++)line(-200,814+i*54,1400,814+i*54,'#b6ad9a',1.4);
+   line(91,765,-170,1220,'#b6ad9a',1.4);line(880,765,1130,1220,'#b6ad9a',1.4);
+   // Light and shadow move gently with the camera, not with an arbitrary pulse.
+   path(q=>{q.moveTo(40,706);q.lineTo(263,706);q.lineTo(672,1120);q.lineTo(412,1120);q.closePath();},'#fcf7df28');
+   // A sample ledge ties the real products to the working material library.
+   rect(404,685,479,12,'#b29d7a');for(let i=0;i<3;i++)sample(P[(i+ep)%P.length].id,468+i*115,622,88,116,(i-1)*.045);
+ }
+ function documentPanel(t){
+   if(ep!==0)return;
+   const a=pulse(t,4.2,4.9,14.3,15.0);if(!a)return;
+   group(640,177+(1-a)*28,0,.88,()=>{
+    rr(-239,-168,478,336,2,'#f7f4e9',ink,1.5);text('BEFORE YOU SPECIFY',-210,-126,24,teal);
+    const labels=['PRODUCT / TEST REPORT','CLASSIFICATION','SUBSTRATE + ADHESIVE'];
+    labels.forEach((label,i)=>{const done=t>5.0+i*2.8;line(-209,-61+i*77,-182,-61+i*77,done?brass:'#bac0b5',3);text(label,-163,-52+i*77,21,ink);});
+   });
+ }
+ function swatchBoard(t){
+   if(ep!==3&&ep!==4)return;
+   const a=pulse(t,6.4,7.1,13.4,14);if(!a)return;
+   group(560,275+(1-a)*25,0,.95,()=>{
+    rr(-199,-139,398,278,2,'#f5f0e4',ink,1);sample(E.product,-83,-1,155,220,0);
+    rect(28,-111,137,83,'#c6bea9');rect(28,-16,137,69,'#8c9985');rect(28,64,137,43,'#b89672');
+    text(ep===3?'COMPARE YOUR FINISHES':'YOUR PROJECT SHORTLIST',-192,-162,23,ink);
+   });
+ }
+ function actionNotes(t){
+   if(ep!==2)return;const a=pulse(t,8,8.5,14.2,14.8);if(!a)return;
+   ['ABRASION','CLEANABILITY','CARE'].forEach((s,i)=>{const show=ease(t,8+i*.7,8.4+i*.7);if(show){rect(440,120+i*77,440,58,'#f4f0e6');text(s,468,159+i*77,27,ink);}});
+ }
+ function performances(t){
+   let wwalk=gait(t,.2,2.9,126,253),pwalk=gait(t,.5,3.1,949,801);
+   const w={walk:wwalk,left:[-85,-279],right:[149,-396],wrists:[-.1,-.36],props:[],gestures:['open','open'],look:.6,lean:Math.sin(t*.8)*.012};
+   const p={walk:pwalk,left:[-130,-402],right:[86,-275],wrists:[.14,.08],props:['sample'],look:-.6,lean:Math.sin(t*.73+1)*.013};
+   // Presenter gestures are deliberately different for each spoken clause.
+   const gesture=key(t,[[0,100,-285],[2.8,145,-411],[4.4,165,-440],[5.2,120,-367],[8.7,161,-441],[9.6,149,-378],[12.8,180,-432],[14,125,-360],[17,152,-419],[19.8,126,-383]]);
+   w.right=gesture;w.wrists[1]=key(t,[[0,0],[3,-.5],[5.2,-.15],[8,-.42],[11,.1],[14,-.4],[18,-.2]])[0];
+   w.gestures[1]=t>4.5&&t<8.8?'point':'open';w.headTilt=Math.sin(t*.65)*.022;w.look=t>4&&t<9?1:.1;
+   if(t<3){w.right=[90-Math.sin(t*4)*40,-277];w.left=[-90+Math.sin(t*4)*40,-277];p.left=[-90+Math.sin(t*4)*24,-280];p.props=[];}
+   if(ep===0){p.props=['report'];p.left=key(t,[[0,-85,-280],[3,-130,-365],[6,-131,-420],[10,-153,-445],[14,-125,-384],[19,-108,-376]]);p.wrists[0]=-.14;p.right=key(t,[[0,90,-277],[8,98,-320],[11,-10,-377],[14,93,-278]]);}
+   if(ep===1){
+    p.props=t<8?['roller']:['sample'];p.left=key(t,[[0,-88,-283],[3,-129,-447],[4.5,-142,-495],[5.2,-143,-411],[6,-146,-495],[7,-145,-397],[8,-100,-367],[11,-139,-436],[15,-103,-361]]);p.wrists[0]=.07;
+    if(t>9.3){p.walk=gait(t,9.3,11.4,801,870);p.look=-1;p.lean=-.06*ease(t,10,11);}
+    w.props=['sample'];w.left=key(t,[[0,-87,-282],[4,-98,-291],[6,-39,-389],[8,-30,-433],[12,-59,-386],[17,-78,-320]]);w.wrists[0]=-.08;
+   }
+   if(ep===2){
+    p.crouch=pulse(t,4,5.6,8.8,10.2)*113;p.lean=-pulse(t,4,5.4,9,10.3)*.10;
+    const wipe=pulse(t,5,5.8,8.2,9);p.props=['cloth'];p.left=[-147-wipe*17,-389+p.crouch+wipe*Math.sin(t*6)*42];p.wrists[0]=-.5;
+    p.right=[85,-284+p.crouch];p.look=-1;
+    w.props=['sample'];w.left=[-45,-371];w.wrists[0]=-.18;
+   }
+   if(ep===3){
+    p.props=['sample'];p.left=key(t,[[0,-92,-280],[3,-100,-366],[6,-138,-440],[10,-144,-478],[14,-126,-418],[18,-93,-340]]);p.wrists[0]=key(t,[[0,0],[3,-.08],[7,.22],[10,-.18],[13,.16],[17,0]])[0];
+    w.props=['fan'];w.left=key(t,[[0,-90,-280],[4,-33,-365],[7,-44,-400],[12,-24,-389],[16,-44,-346]]);w.wrists[0]=-.13;
+   }
+   if(ep===4){
+    w.props=['tablet'];w.left=[-43,-341];w.right=key(t,[[0,101,-280],[3,143,-413],[6,158,-444],[8.5,65,-361],[10,34,-340],[11.6,20,-356],[13,144,-411],[17,160,-444],[19,112,-374]]);w.gestures[1]=t>8&&t<12?'point':'open';
+    p.props=['fan'];p.left=[-129,-400];p.right=key(t,[[0,89,-280],[6,86,-280],[9,-18,-384],[12,76,-285],[17,100,-382],[19,86,-297]]);
+   }
+   // Camera-right actor is drawn first so the presenter remains the visual lead.
+   character('pippa',t,p);character('wallo',t,w);
+ }
+ function overlay(t){
+   rect(0,0,1080,437,chalk);rect(0,1437,1080,483,chalk);
+   text('DESIGNER WALLCOVERINGS',72,96,25,ink);text('WALLO PRESENTS',1008,96,21,teal,'Verdana','right');
+   line(72,124,1008,124,'#b9b6aa',1);text(E.kicker,72,178,24,teal);
+   E.title.split('\n').forEach((s,i)=>text(s,69,270+i*87,73,ink,'Georgia'));
+   text('MATERIAL NOTES',73,411,22,teal);text('WITH WALLO & PIPPA',1006,411,20,teal,'Verdana','right');
+   const product=P.find(p=>p.id===E.product);text(product.label,72,1481,25,ink);
+   line(72,1505,1008,1505,'#b9b6aa',1);
+   const segment=(E.audio?.segments||[]).filter(s=>t>=s.start).at(-1);
+   const caption=segment?E.captions[segment.index]:'A wallcovering presentation with Wallo.';
+   wrap(caption,72,1570,930,38,ink,'Verdana',55);
+   if(t>=16.8)text(E.takeaway,72,1738,37,teal,'Georgia');
+   wrap(E.note,72,1794,926,24,'#58635b','Verdana',34);
+   text('designerwallcoverings.com',72,1870,25,teal);
+   text(String(ep+1).padStart(2,'0')+' / 05',1008,1870,22,teal,'Verdana','right');
+   rect(72,1894,936,3,'#c9c7bb');rect(72,1894,936*clamp(t/20),3,teal);
+ }
+ function draw(t){
+   telemetry=[];rect(0,0,1080,1920,chalk);
+   c.save();c.beginPath();c.rect(0,437,1080,1000);c.clip();
+   const zoom=1+ease(t,3.3,5)*.08-ease(t,14.2,16)*.06;
+   c.translate(540,950);c.scale(zoom,zoom);c.translate(-540,-487);
+   room(t);documentPanel(t);swatchBoard(t);actionNotes(t);performances(t);
+   c.restore();overlay(t);window.__rigState=telemetry;
+ }
+ const driver={t:0};window.__timelines=window.__timelines||{};
+ window.__timelines.main=gsap.timeline({paused:true}).to(driver,{t:20,duration:20,ease:'none',onUpdate:()=>draw(driver.t)},0);
+ window.__draw=draw;
+ window.__ready=Promise.all(P.map(p=>new Promise((resolve,reject)=>{const im=new Image();images[p.id]=im;im.onload=resolve;im.onerror=()=>reject(new Error('Missing product '+p.id));im.src=p.local;}))).then(()=>document.fonts.ready).then(()=>{draw(driver.t);return true;});
+ draw(0);
+})();
+</script></body></html>
\ No newline at end of file
diff --git a/series2/episodes/05-wallpaper-or-paint.html b/series2/episodes/05-wallpaper-or-paint.html
new file mode 100644
index 0000000..0ddd481
--- /dev/null
+++ b/series2/episodes/05-wallpaper-or-paint.html
@@ -0,0 +1,215 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=1080,height=1920"><title>Wallo presents — Why specify wallpaper over paint?</title><style>@font-face{font-family:Georgia;src:url('../assets/georgia.ttf')}@font-face{font-family:Verdana;src:url('../assets/verdana.ttf')}html,body{margin:0;width:1080px;height:1920px;overflow:hidden}#root{position:relative;width:1080px;height:1920px;overflow:hidden}.clip{position:absolute;inset:0;width:1080px;height:1920px}canvas{width:1080px;height:1920px}.preload{position:absolute;opacity:0;pointer-events:none}</style><script src="../assets/gsap.min.js"></script></head><body><div id="root" data-composition-id="main" data-start="0" data-duration="20" data-width="1080" data-height="1920" data-fps="30"><section id="material-scene" class="clip" data-start="0" data-duration="20" data-track-index="0"><canvas id="stage" width="1080" height="1920" aria-label="Wallo presenting Why specify wallpaper over paint?"></canvas><span class="preload" style="font-family:Georgia">Wallo</span><span class="preload" style="font-family:Verdana">Designer Wallcoverings</span></section><audio id="narrated-mix" src="../assets/mix-1.wav" data-start="0" data-duration="20" data-track-index="1"></audio></div><script>window.EPISODE={"slug":"05-wallpaper-or-paint","title":"Color is only\nthe beginning.","shortTitle":"Why specify wallpaper over paint?","kicker":"02 / DESIGN BENEFITS","product":"waterperry","note":"Waterperry / Ivory–Stone · Sanderson. Illustration not to scale.","takeaway":"Give the room a point of view.","lines":["Paint gives you color. Wallcovering can give you another dimension.","Pattern creates rhythm. Texture changes how a surface catches light.","This Sanderson design makes one wall the room's focal point.","Choose wallpaper for character, and paint where you want simplicity."],"captions":["Color is the starting point.","Pattern. Texture. Depth.","One wall can define the room.","Wallpaper for character. Paint for simplicity."],"index":1,"audio":{"slug":"05-wallpaper-or-paint","provider":"Kokoro local","voice":"am_michael","duration":20,"segments":[{"index":0,"start":0.6,"end":4.483,"text":"Paint gives you color. Wallcovering can give you another dimension.","caption":"Color is the starting point.","duration":3.883},{"index":1,"start":5.1,"end":9.324,"text":"Pattern creates rhythm. Texture changes how a surface catches light.","caption":"Pattern. Texture. Depth.","duration":4.224},{"index":2,"start":9.6,"end":13.696,"text":"This Sanderson design makes one wall the room's focal point.","caption":"One wall can define the room.","duration":4.096},{"index":3,"start":14.1,"end":18.025,"text":"Choose wallpaper for character, and paint where you want simplicity.","caption":"Wallpaper for character. Paint for simplicity.","duration":3.925}],"energy":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0009,0.1389,0.1517,0.2161,0.4266,0.6739,0.6462,0.5771,0.6139,0.5614,0.433,0.2164,0.1501,0.0539,0.0411,0.0399,0.0431,0.0354,0.1053,0.2243,0.6331,0.7637,0.763,0.7776,0.2153,0.151,0.2944,0.4863,0.4505,0.3431,0.185,0.21,0.3098,0.3164,0.2116,0.0714,0.0078,0.0056,0.1376,0.2764,0.1865,0.2572,0.2807,0.2333,0.2763,0.6548,1,1,1,0.7987,0.5067,0.2944,0.2674,0.2038,0.1339,0.11,0.0764,0.0372,0.0147,0.0258,0.0142,0.0091,0.0021,0.0011,0.0013,0.0016,0.002,0.0026,0.0029,0.0033,0.0038,0.0049,0.0061,0.0069,0.0093,0.0082,0.0103,0.0119,0.0063,0.002,0.0032,0.0023,0.1299,0.2004,0.2621,0.4307,0.8747,1,1,1,1,1,1,1,1,0.2146,0.0166,0.0034,0.0024,0.3216,0.2786,0.7684,0.2106,0.1688,0.1775,0.1863,0.2622,0.4009,0.428,0.4323,0.4266,0.4068,0.4082,0.4208,0.375,0.2756,0.1075,0.0099,0.1278,0.112,0.292,0.4602,0.3299,0.3536,0.3732,0.3996,0.2945,0.192,0.1429,0.2159,0.4804,0.6552,0.741,0.6986,0.1646,0.1349,0.1213,0.1332,0.152,0.2048,0.2626,0.329,0.3624,0.3629,0.3881,0.3287,0.3673,0.3726,0.3997,0.4195,0.3788,0.4222,0.4985,0.4664,0.5747,0.6037,0.1863,0.1492,0.4204,0.4614,0.4105,0.3201,0.2066,0.1544,0.1624,0.4284,0.4281,0.3571,0.4048,0.4244,0.4703,0.4054,0.4486,0.462,0.4331,0.3091,0.2819,0.0874,0.1378,0.4752,0.6097,0.6132,0.3525,0.0734,0.1871,0.0938,0.0559,0.0252,0.0338,0.0075,0.0166,0.0055,0.0029,0.0011,0.0004,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0007,0.0124,0.1816,0.3305,0.4079,0.57,0.9222,0.8808,0.8585,0.7219,0.5228,0.2082,0.6079,0.6087,0.6309,0.5995,0.5675,0.5909,0.5489,0.3951,0.1521,0.0837,0.0489,0.0958,0.0936,0.3407,0.3996,0.392,0.3049,0.3854,0.5193,0.6533,0.5779,0.5296,0.5214,0.4893,0.3329,0.1213,0.0465,0.3398,0.2913,0.0841,0.0814,0.1494,0.1888,0.2429,0.3568,0.6675,1,1,1,0.4725,0.2437,0.1453,0.3793,0.2467,0.1728,0.0516,0.0263,0.0491,0.0408,0.0286,0.0309,0.0054,0.001,0.0013,0.0014,0.0016,0.0019,0.0021,0.0027,0.0034,0.0047,0.0053,0.0062,0.0078,0.0081,0.0067,0.0044,0.0086,0.0041,0.0018,0.066,0.2806,0.61,1,1,1,0.4813,0.0257,0.0772,0.1589,0.6571,0.4209,0.0663,0.0291,0.0822,0.275,0.4429,0.5916,0.5074,0.2999,0.1229,0.0153,0.0873,0.4598,0.5817,0.4037,0.3386,0.4669,0.6258,0.4973,0.4756,0.4904,0.5048,0.4834,0.5658,0.5097,0.5034,0.4594,0.2793,0.2598,0.2339,0.3842,0.4001,0.3637,0.3051,0.241,0.1865,0.2266,0.1016,0.1232,0.1919,0.2406,0.3852,0.4074,0.3841,0.4645,0.4875,0.4188,0.3531,0.3852,0.4183,0.3006,0.3745,0.4269,0.3591,0.3435,0.2982,0.272,0.2313,0.5985,0.9521,0.8054,0.8367,0.6945,0.6582,0.134,0.1047,0.0954,0.0726,0.2529,0.3523,0.3661,0.2502,0.2854,0.2723,0.1879,0.0208,0.006,0.0825,0.1042,0.0867,0.3597,0.415,0.3577,0.3488,0.4695,0.2983,0.03,0.0482,0.305,0.1933,0.2001,0.284,0.2558,0.243,0.2125,0.1811,0.1873,0.3172,0.3736,0.4281,0.5689,0.6375,0.8002,0.8189,0.772,0.3907,0.2836,0.1096,0.0718,0.0455,0.0166,0.0111,0.0128,0.0041,0.0013,0.0022,0.0008,0.0011,0.0005,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0011,0.0029,0.0557,0.2103,0.8746,1,0.8395,0.5963,0.8625,0.788,0.5799,0.4302,0.3683,0.3325,0.3073,0.6046,1,0.6848,0.7188,0.8196,0.7963,0.7829,0.7007,0.6394,0.3486,0.2692,0.6223,0.6302,0.4384,0.3552,0.6189,0.5471,0.5152,0.3375,0.2218,0.4245,0.4069,0.4702,0.4526,0.4177,0.3072,0.2324,0.2125,0.3909,0.5216,0.4742,0.368,0.3188,0.29,0.3059,0.3688,0.8644,0.7728,0.7514,0.7627,0.6532,0.7537,0.7418,0.7562,0.5359,0.3775,0.2997,0.248,0.2106,0.1804,0.1687,0.1277,0.0287,0.0103,0.0081,0.0116,0.0106,0.0093,0.0084,0.006,0.0085,0.0063,0.0055,0.2178,0.5037,0.5147,0.6706,0.4113,0.1395,0.1048,0.4407,0.6923,0.4845,0.092,0.0594,0.1906,0.2805,0.3104,0.5378,0.6701,0.9309,0.7825,0.7659,0.7138,0.7314,0.6515,0.5834,0.5421,0.4886,0.4144,0.2993,0.3044,0.5034,0.8413,0.9742,1,1,1,1,1,0.8349,0.6758,0.3405,0.2915,0.2431,0.1153,0.0893,0.0156,0.0032,0.0056,0.0073,0.0078,0.0074,0.0072,0.0041,0.0029,0.0032,0.0026,0.0018,0.0025,0.1047,0.4938,0.5581,0.3837,0.303,0.2594,0.2486,0.2992,0.4674,0.5778,0.7377,0.7841,0.7696,0.7744,0.6577,0.6135,0.5415,0.4837,0.514,0.4367,0.3495,0.2887,0.5585,0.5119,0.1609,0.1262,0.0949,0.0679,0.0701,0.8694,0.7886,0.7509,0.5974,0.2547,0.0749,0.0071,0.162,0.1265,0.3658,0.4008,0.3396,0.1303,0.0315,0.0043,0.0056,0.1676,0.0884,0.1233,0.0659,0.5457,0.8468,0.833,0.7201,0.4761,0.2168,0.1211,0.0677,0.0429,0.0178,0.0236,0.0123,0.0113,0.0025,0.0017,0.0249,0.017,0.0043,0.0015,0.0011,0.0008,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0009,0.0386,0.1824,0.6817,0.7273,0.5769,0.3643,0.562,0.5896,0.5214,0.4846,0.535,0.4699,0.3802,0.5335,0.2933,0.2917,0.282,0.3172,0.3253,0.6864,1,1,1,1,1,1,1,1,1,0.6132,0.1338,0.0275,0.0049,0.2359,0.192,0.4487,0.5891,0.5862,0.6167,0.5753,0.4624,0.1265,0.019,0.0066,0.1695,0.4457,0.5648,0.5228,0.2751,0.1806,0.1816,0.189,0.1104,0.3541,0.33,0.1795,0.0691,0.011,0.1234,0.132,0.1714,0.5335,0.7031,0.7372,0.6638,0.6932,0.8441,0.8036,0.8259,0.7696,0.5486,0.3022,0.0628,0.0136,0.007,0.0017,0.0015,0.1605,0.3318,0.3296,0.2849,0.2103,0.0983,0.0847,0.0586,0.0626,0.0163,0.0024,0.0029,0.0039,0.0046,0.0055,0.0052,0.0053,0.0058,0.0059,0.0076,0.0077,0.0089,0.0084,0.0099,0.0104,0.0109,0.008,0.0033,0.0642,0.5048,0.7302,0.8702,1,0.8107,0.4396,0.1355,0.0224,0.2355,0.3057,0.2853,0.3066,0.6858,1,1,1,1,0.8901,0.5764,0.2873,0.2003,0.1604,0.1956,0.3389,0.5707,0.4748,0.4969,0.3756,0.3242,0.3005,0.3038,0.3968,0.4095,0.354,0.3362,0.3035,0.372,0.5635,0.7634,0.8537,0.7715,0.7136,0.8108,0.6955,0.6036,0.4479,0.3035,0.0683,0.1532,0.3781,0.5799,0.6103,0.3608,0.3477,0.4844,0.5064,0.4801,0.3623,0.1955,0.0436,0.1377,0.0833,0.0779,0.0812,0.7548,0.9791,0.7436,0.4135,0.5236,0.6207,0.5031,0.5121,0.2668,0.2516,0.396,0.2576,0.0987,0.0897,0.1683,0.1185,0.0831,0.029,0.0385,0.0347,0.0279,0.0068,0.003,0.0018,0.0014,0.0004,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"peak":0.652790367603302}};window.PRODUCTS=[{"id":"fairborn","title":"Fairborn Type 2 | Phillipe Romano","label":"Fairborn Type 2 · Phillipe Romano","url":"https://www.designerwallcoverings.com/products/prw-920735-rif-003","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/prw-920735-rif-003.jpg?v=1781716604","file":"assets/products/fairborn.jpg","description":"<p>When light rakes across the embossed surface, it reveals an organic grid-like pattern of broken lines. Although extremely durable — as you’d expect from a Type II vinyl wallcovering named for rock fissures — offering a soft, nature-inspired palette of creams, taupes, blues and browns. A small scale and intricate pattern appear almost seamless, and its 12 two-tone colorways resemble artisanal materials like mosaic</p><br><br>A captivating wallcovering featuring intricate detailing and refined textures, offering a sophisticated backdrop that elevates any interior space. The design evokes timeless elegance with a modern touch.  \n#luxuryinteriors #modernwallcoverings #elegantdesign\n","retrieved_at":"2026-09-05","local":"../assets/products/fairborn.jpg"},{"id":"waterperry","title":"Waterperry Ivory/Stone | Sanderson","label":"Waterperry / Ivory–Stone · Sanderson","url":"https://www.designerwallcoverings.com/products/waterperry-ivory-stone-sanderson","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DWAP216280_5340.jpg?v=1788582264","file":"assets/products/waterperry.jpg","description":"<div class=\"sanderson-product\">\n<p>Waterperry in Ivory/Stone is a Sanderson Wallcovering in a botanical/leaves and trees design. Part of the Sanderson heritage collection, this pattern reads in a palette of Alabaster, Linen, Mist. Supplied by the roll; a memo sample is available. An English design-house classic from Sanderson Design Group.</p>\n</div>","retrieved_at":"2026-09-05","local":"../assets/products/waterperry.jpg"},{"id":"wheaton","title":"Wheaton Type 2 | Phillipe Romano","label":"Wheaton Type 2 · Phillipe Romano","url":"https://www.designerwallcoverings.com/products/prw-920166-coa-004-1","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/d4639abbf9baa5237707.jpg?v=1785438111","file":"assets/products/wheaton.jpg","description":"<p>Weaving shimmering strands and beguiling texture, emulating the intricacy of our favored grasscloth Tide in the form of a durable Type II vinyl</p>\n","retrieved_at":"2026-09-05","local":"../assets/products/wheaton.jpg"},{"id":"topiary","title":"Topiary View Thyme | Sanderson","label":"Topiary View / Thyme · Sanderson","url":"https://www.designerwallcoverings.com/products/topiary-view-thyme-sanderson","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DHIP217511_f500.jpg?v=1788582179","file":"assets/products/topiary.jpg","description":"<div class=\"sanderson-product\">\n<p>Topiary View in Thyme is a Sanderson Wallcovering in a landscapes design. Part of the Sanderson heritage collection, this pattern reads in a palette of Alabaster, Stone, Oatmeal. Supplied by the roll; a memo sample is available. An English design-house classic from Sanderson Design Group.</p>\n</div>","retrieved_at":"2026-09-05","local":"../assets/products/topiary.jpg"},{"id":"kaya","title":"Kaya - Cream Weave Wallcovering | Malibu Wallcovering","label":"Kaya / Cream Weave · Malibu","url":"https://www.designerwallcoverings.com/products/kaya-cream-weave-wallcovering-malibu-wallpaper","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/0086435_kaya-cream-weave_650.jpg?v=1776906632","file":"assets/products/kaya.jpg","description":"<p>Elevate your space with the luxurious Kaya Cream Weave wallcovering. This exquisite nonwoven vinyl design features a warm neutral color palette and stunning texture, perfect for creating an elegant ambiance.</p>","retrieved_at":"2026-09-05","local":"../assets/products/kaya.jpg"}];/* Wallo Material Notes: deterministic articulated character acting and real DW samples. */
+(() => {
+ 'use strict';
+ const canvas=document.getElementById('stage'), c=canvas.getContext('2d');
+ const E=window.EPISODE, P=window.PRODUCTS, ep=E.index;
+ const ink='#243a3b', chalk='#eeeae2', teal='#284f4b', clay='#a9634d', brass='#b7924f';
+ const images={}; let telemetry=[];
+ const clamp=(x,a=0,b=1)=>Math.max(a,Math.min(b,x));
+ const mix=(a,b,p)=>a+(b-a)*p;
+ const smooth=p=>{p=clamp(p);return p*p*(3-2*p);};
+ const ease=(t,a,b)=>smooth((t-a)/(b-a));
+ const pulse=(t,a,b,c,d)=>ease(t,a,b)*(1-ease(t,c,d));
+ const point=(x,y)=>({x,y});
+ const dist=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y);
+ function group(x,y,r,s,fn){c.save();c.translate(x,y);c.rotate(r);c.scale(s,s);fn();c.restore();}
+ function path(fn,fill,stroke=null,w=2){c.beginPath();fn(c);if(fill){c.fillStyle=fill;c.fill();}if(stroke){c.lineWidth=w;c.strokeStyle=stroke;c.lineJoin='round';c.lineCap='round';c.stroke();}}
+ function line(x,y,u,v,col,w=2){path(q=>{q.moveTo(x,y);q.lineTo(u,v);},null,col,w);}
+ function rect(x,y,w,h,col){c.fillStyle=col;c.fillRect(x,y,w,h);}
+ function oval(x,y,rx,ry,col,stroke=null,w=2){path(q=>q.ellipse(x,y,rx,ry,0,0,Math.PI*2),col,stroke,w);}
+ function rr(x,y,w,h,r,col,stroke=null,lw=2){path(q=>q.roundRect(x,y,w,h,r),col,stroke,lw);}
+ function text(s,x,y,size=28,color=ink,font='Verdana',align='left'){c.font=`${size}px '${font}'`;c.textAlign=align;c.textBaseline='alphabetic';c.fillStyle=color;c.fillText(s,x,y);}
+ function wrap(s,x,y,max,size=36,color=ink,font='Verdana',lh=size*1.4){
+   c.font=`${size}px '${font}'`;let row='',lines=[];
+   for(const word of s.split(' ')){const test=row?row+' '+word:word;if(c.measureText(test).width>max&&row){lines.push(row);row=word;}else row=test;}
+   if(row)lines.push(row);lines.forEach((v,i)=>text(v,x,y+i*lh,size,color,font));return lines.length;
+ }
+ function imageCover(id,x,y,w,h){const im=images[id];if(!im||!im.complete)return;const k=Math.max(w/im.width,h/im.height);c.save();c.beginPath();c.rect(x,y,w,h);c.clip();c.drawImage(im,x+(w-im.width*k)/2,y+(h-im.height*k)/2,im.width*k,im.height*k);c.restore();}
+ function sample(id,x,y,w=126,h=164,r=0){group(x,y,r,1,()=>{rr(-w/2+7,-h/2+9,w,h,2,'#203a3a20');rect(-w/2-5,-h/2-5,w+10,h+10,'#f9f7f0');imageCover(id,-w/2,-h/2,w,h);line(-w/2,-h/2,w/2,-h/2,'#ffffff',2);});}
+ function bone(a,b,width,col,shade){
+   const angle=Math.atan2(b.y-a.y,b.x-a.x),d=dist(a,b);
+   group(a.x,a.y,angle,1,()=>{rr(-width*.18,-width/2,d+width*.36,width,width*.46,col);path(q=>{q.moveTo(5,width*.26);q.quadraticCurveTo(d*.5,width*.34,d-3,width*.22);},null,shade||'#00000018',2.3);});
+ }
+ function solveIK(root,target,l1,l2,pole){
+   const dx=target.x-root.x,dy=target.y-root.y,raw=Math.hypot(dx,dy),d=clamp(raw,Math.abs(l1-l2)+.01,l1+l2-.01);
+   const nx=raw?dx/raw:0,ny=raw?dy/raw:1,a=(l1*l1-l2*l2+d*d)/(2*d),h=Math.sqrt(Math.max(0,l1*l1-a*a));
+   const end=point(root.x+nx*d,root.y+ny*d),joint=point(root.x+nx*a-ny*h*pole,root.y+ny*a+nx*h*pole);
+   return {root,joint,end,l1,l2};
+ }
+ function rotatePoint(p,angle,origin=point(0,-258)){const x=p.x-origin.x,y=p.y-origin.y;return point(origin.x+x*Math.cos(angle)-y*Math.sin(angle),origin.y+x*Math.sin(angle)+y*Math.cos(angle));}
+ function key(t,frames){if(t<=frames[0][0])return frames[0].slice(1);for(let i=1;i<frames.length;i++)if(t<=frames[i][0]){const p=ease(t,frames[i-1][0],frames[i][0]);return frames[i].slice(1).map((v,j)=>mix(frames[i-1][j+1],v,p));}return frames.at(-1).slice(1);}
+ function gait(t,start,end,x0,x1){
+   const p=clamp((t-start)/(end-start));
+   const lift=(a,b)=>p>a&&p<b?Math.sin(Math.PI*(p-a)/(b-a))*44:0;
+   const left=mix(x0-30,(x0+x1)/2-30,smooth((p-.08)/.28))+mix(0,(x1-x0)/2,smooth((p-.65)/.3));
+   const right=mix(x0+30,x1+30,smooth((p-.35)/.3));
+   return {x:(left+right)/2,feet:[point(left,-lift(.08,.36)-lift(.65,.95)),point(right,-lift(.35,.65))],walking:p>0&&p<1,phase:p};
+ }
+ function voiceEnergy(t){const env=E.audio?.energy||[];return env[Math.min(env.length-1,Math.max(0,Math.floor(t*50)))]||0;}
+ function hand(x,y,r,pose='open',skin='#c89472',prop=null){
+   group(x,y,r,1,()=>{
+    if(prop==='sample')sample(E.product,8,-18,115,148,-.04);
+    if(prop==='fan')for(let k=0;k<3;k++)sample(['waterperry','topiary','kaya'][k],k*17-15,-43,91,130,(k-1)*.18);
+    if(prop==='report'){rr(-12,-134,135,176,4,'#f8f5e9',ink,2);rect(1,-117,108,5,teal);text('TEST',4,-87,22,ink);text('REPORT',4,-58,22,ink);for(let k=0;k<3;k++)line(4,-35+k*18,97,-35+k*18,'#a4aaa0',2);}
+    if(prop==='cloth'){path(q=>{q.moveTo(-28,-15);q.lineTo(30,-26);q.lineTo(43,35);q.lineTo(-21,44);q.closePath();},'#c8c5b5',ink,1);line(-17,-4,28,25,'#efeadb',3);}
+    if(prop==='roller'){line(0,-8,0,-72,ink,7);line(0,-72,37,-72,ink,7);line(37,-72,37,-108,ink,5);rr(-27,-132,127,38,11,'#8b9c8d',ink,2);rr(-9,-9,18,54,7,brass);}
+    if(prop==='tablet'){rr(-73,-76,145,170,8,ink);rr(-65,-67,129,151,4,'#f4f0e5');text('DW',-45,-33,27,teal,'Georgia');for(let i=0;i<3;i++)imageCover(['waterperry','fairborn','kaya'][i],-47+i*32,-6,27,60);}
+    if(pose==='point'){bone(point(-4,3),point(34,-18),13,skin);bone(point(0,9),point(16,15),21,skin);oval(-6,9,13,18,skin);}
+    else{rr(-14,-9,29,42,12,skin);oval(-15,5,7,13,skin);for(let i=0;i<3;i++)line(-7+i*6,14,-7+i*6,28,'#9e685446',1);}
+   });
+ }
+ function head(kind,x,y,t,look,mouth,tilt){
+   const male=kind==='wallo',skin=male?'#c89472':'#d5a185',dark=male?'#714f3b':'#834e39';
+   group(x,y,tilt,.82,()=>{
+    if(!male){oval(-4,-10,51,70,dark);oval(-39,-50,28,28,dark);path(q=>{q.moveTo(-57,-55);q.quadraticCurveTo(-81,-37,-59,-16);},null,'#a86b4b',7);}
+    // Tapered jaw, defined brow and modest nose maintain adult proportions.
+    path(q=>{q.moveTo(-41,-40);q.bezierCurveTo(-43,-77,43,-80,44,-34);q.lineTo(40,31);q.quadraticCurveTo(31,66,5,72);q.quadraticCurveTo(-26,65,-36,34);q.closePath();},skin);
+    path(q=>{q.moveTo(-38,-31);q.lineTo(-32,34);q.quadraticCurveTo(-24,52,-8,57);q.lineTo(-20,21);q.lineTo(-18,-46);q.closePath();},male?'#b58162':'#c39076');
+    oval(-40,3,9,16,skin);oval(42,2,8,15,skin);
+    if(male){path(q=>{q.moveTo(-39,-39);q.lineTo(-33,-18);q.lineTo(-30,-54);q.lineTo(31,-54);q.lineTo(43,-25);q.lineTo(42,-55);q.closePath();},dark);}
+    else{path(q=>{q.moveTo(-40,-27);q.quadraticCurveTo(-24,-63,2,-56);q.quadraticCurveTo(38,-37,43,-19);q.lineTo(38,-58);q.quadraticCurveTo(-4,-89,-37,-55);q.closePath();},dark);for(let i=0;i<4;i++)path(q=>{q.moveTo(-29+i*17,-56);q.quadraticCurveTo(-26+i*17,-43,-13+i*17,-39);},null,'#ae7150',2);}
+    const blink=(t+(male?.31:1.93))%4.4<.14;
+    for(const side of [-1,1]){const ex=side*18+look*3,ey=-8;path(q=>{q.moveTo(ex-10,ey);q.quadraticCurveTo(ex,ey-5,ex+10,ey);},null,ink,2.2);if(!blink){oval(ex+look*3,ey+1,3.5,4.3,ink);oval(ex+look*3+1,ey-.3,1.1,1.2,'#eee8dc');}line(ex-10,ey-15,ex+8,ey-17+(side===1?look*2:0),dark,2.5);}
+    path(q=>{q.moveTo(3+look*5,-5);q.lineTo(8+look*8,15);q.quadraticCurveTo(4+look*5,20,-1+look*5,17);},null,'#98644f',1.8);
+    if(male){path(q=>{q.moveTo(-20,29);q.quadraticCurveTo(-6,20,4,27);q.quadraticCurveTo(15,23,26,30);q.quadraticCurveTo(17,39,3,34);q.quadraticCurveTo(-9,37,-20,29);},dark);}
+    if(mouth>.12){oval(4,44,10+mouth*3,2+mouth*6,'#714735');if(mouth>.4)line(-2,41,11,41,'#efe6d7',2);}
+    else path(q=>{q.moveTo(-7,43);q.quadraticCurveTo(5,48,17,42);},null,'#714735',1.8);
+    if(male){path(q=>{q.moveTo(-49,-53);q.quadraticCurveTo(-48,-98,10,-95);q.quadraticCurveTo(47,-89,48,-53);q.closePath();},brass);path(q=>{q.moveTo(-49,-53);q.quadraticCurveTo(12,-64,72,-48);q.quadraticCurveTo(77,-35,39,-41);q.lineTo(-49,-43);q.closePath();},'#97773f');line(-27,-73,12,-84,'#d2b270',3);}
+    else oval(-39,14,3.5,5.5,brass);
+   });
+ }
+ function character(kind,t,settings={}){
+   const male=kind==='wallo',skin=male?'#c89472':'#d5a185',shirt=male?'#d6d0bc':clay,cloth=male?teal:'#c8c1af';
+   const walk=settings.walk||gait(t,0,1,settings.x,settings.x);let x=walk.x,y=settings.y||882;
+   const crouch=settings.crouch||0,lean=settings.lean||0,breath=Math.sin(t*2.1+(male?0:1.2))*1.7;
+   const hipY=-275+crouch,origin=point(0,hipY);
+   const bodyPoint=(u,v)=>rotatePoint(point(u,v+crouch+breath),lean,origin);
+   const leftShoulder=bodyPoint(-62,-469),rightShoulder=bodyPoint(62,-469);
+   const arms=[solveIK(leftShoulder,point(...settings.left),118,111,-1),solveIK(rightShoulder,point(...settings.right),118,111,1)];
+   const legs=walk.feet.map((foot,i)=>solveIK(point(i===0?-32:32,hipY),point(foot.x-x,foot.y-13),134,136,i===0?1:-1));
+   const mouth=male?voiceEnergy(t)*.88:0;
+   const joints={kind,x,y,arms,legs,lean,crouch,feet:walk.feet,walking:walk.walking,props:settings.props||[]};
+   telemetry.push(joints);
+   oval(x,y+6,94-crouch*.1,15,'#233a3b15');
+   group(x,y,0,settings.scale||1,()=>{
+    // Back arm and far leg precede torso so elbow overlaps read spatially.
+    const armDraw=(i)=>{const b=arms[i];bone(b.root,b.joint,39,shirt);bone(b.joint,b.end,27,skin);oval(b.joint.x,b.joint.y,16,16,skin);const sleeve=point(mix(b.root.x,b.joint.x,.77),mix(b.root.y,b.joint.y,.77));bone(b.root,sleeve,43,shirt);line(sleeve.x-8,sleeve.y,sleeve.x+9,sleeve.y,'#ffffff30',2);};
+    armDraw(0);
+    legs.forEach((b,i)=>{bone(b.root,b.joint,58,cloth);bone(b.joint,b.end,43,cloth);oval(b.joint.x,b.joint.y,23,23,cloth);line(b.root.x-8,b.root.y+30,b.joint.x-6,b.joint.y-16,male?'#41635b':'#dfd8c8',2);group(b.end.x,b.end.y+10,walk.walking&&walk.feet[i].y<0?-.12:0,1,()=>{path(q=>{q.moveTo(-24,-20);q.lineTo(20,-20);q.lineTo(27,-9);q.quadraticCurveTo(60,-5,56,14);q.lineTo(-24,14);q.closePath();},male?'#655545':'#626b62');line(-24,14,55,14,'#34413d',5);line(4,-8,25,-7,'#c6b69b',2);});});
+    group(0,hipY,lean,1,()=>{
+     const dy=crouch+breath-hipY;
+     path(q=>{q.moveTo(-63,-478+dy);q.quadraticCurveTo(-82,-460+dy,-71,-417+dy);q.lineTo(-60,-266+dy);q.quadraticCurveTo(0,-235+dy,60,-266+dy);q.lineTo(71,-417+dy);q.quadraticCurveTo(79,-460+dy,58,-479+dy);q.closePath();},shirt);
+     // Structured overall/apron silhouette, stitched seams, pockets and cloth folds.
+     path(q=>{q.moveTo(-50,-425+dy);q.lineTo(-46,-350+dy);q.lineTo(-61,-290+dy);q.lineTo(-60,-253+dy);q.lineTo(61,-253+dy);q.lineTo(61,-290+dy);q.lineTo(46,-350+dy);q.lineTo(48,-425+dy);q.closePath();},cloth);
+     for(const s of [-1,1]){line(s*44,-472+dy,s*38,-405+dy,cloth,17);oval(s*39,-414+dy,4.8,4.8,brass);line(s*49,-328+dy,s*41,-312+dy,male?'#59756b':'#a9a18f',2);}
+     rr(-27,-401+dy,54,56,4,male?'#254540':'#bab39f');path(q=>{q.moveTo(-23,-388+dy);q.quadraticCurveTo(0,-376+dy,24,-388+dy);},null,male?'#6a8778':'#e5dfd0',1.5);
+     line(-50,-266+dy,52,-266+dy,male?'#1e403b':'#aca590',3);
+     if(male){rr(-66,-321+dy,20,67,3,'#8d7452');line(-56,-342+dy,-56,-282+dy,brass,6);}
+     path(q=>{q.moveTo(-17,-509+dy);q.lineTo(-18,-474+dy);q.quadraticCurveTo(0,-454+dy,21,-474+dy);q.lineTo(18,-509+dy);q.closePath();},skin);
+    });
+    const hp=bodyPoint(0,-559);head(kind,hp.x,hp.y,t,settings.look||0,mouth,(settings.headTilt||0)+lean*.28);
+    armDraw(1);
+    arms.forEach((b,i)=>hand(b.end.x,b.end.y,(settings.wrists||[0,0])[i],(settings.gestures||['open','open'])[i],skin,(settings.props||[])[i]));
+   });
+   return joints;
+ }
+ function room(t){
+   rect(-350,-300,1800,1200,'#dfdbd1');rect(-350,765,1800,450,'#c9c2b1');
+   rect(20,0,240,706,'#e9e7dc');for(let i=0;i<4;i++)line(22+i*59,0,22+i*59,704,'#f7f4e9',7);
+   rect(318,26,647,727,'#c7bfae');rect(329,37,624,704,'#efede3');
+   if(ep===1){const a=ease(t,5.1,8);rect(343,49,596,678,'#aab7a4');c.save();c.beginPath();c.rect(343,49,596*a,678);c.clip();imageCover(E.product,343,49,596,678);c.restore();}
+   else imageCover(E.product,343,49,596,678);
+   line(340,743,943,743,'#faf7eb',4);line(-350,765,1450,765,'#a79f8d',2);
+   for(let i=0;i<6;i++)line(-200,814+i*54,1400,814+i*54,'#b6ad9a',1.4);
+   line(91,765,-170,1220,'#b6ad9a',1.4);line(880,765,1130,1220,'#b6ad9a',1.4);
+   // Light and shadow move gently with the camera, not with an arbitrary pulse.
+   path(q=>{q.moveTo(40,706);q.lineTo(263,706);q.lineTo(672,1120);q.lineTo(412,1120);q.closePath();},'#fcf7df28');
+   // A sample ledge ties the real products to the working material library.
+   rect(404,685,479,12,'#b29d7a');for(let i=0;i<3;i++)sample(P[(i+ep)%P.length].id,468+i*115,622,88,116,(i-1)*.045);
+ }
+ function documentPanel(t){
+   if(ep!==0)return;
+   const a=pulse(t,4.2,4.9,14.3,15.0);if(!a)return;
+   group(640,177+(1-a)*28,0,.88,()=>{
+    rr(-239,-168,478,336,2,'#f7f4e9',ink,1.5);text('BEFORE YOU SPECIFY',-210,-126,24,teal);
+    const labels=['PRODUCT / TEST REPORT','CLASSIFICATION','SUBSTRATE + ADHESIVE'];
+    labels.forEach((label,i)=>{const done=t>5.0+i*2.8;line(-209,-61+i*77,-182,-61+i*77,done?brass:'#bac0b5',3);text(label,-163,-52+i*77,21,ink);});
+   });
+ }
+ function swatchBoard(t){
+   if(ep!==3&&ep!==4)return;
+   const a=pulse(t,6.4,7.1,13.4,14);if(!a)return;
+   group(560,275+(1-a)*25,0,.95,()=>{
+    rr(-199,-139,398,278,2,'#f5f0e4',ink,1);sample(E.product,-83,-1,155,220,0);
+    rect(28,-111,137,83,'#c6bea9');rect(28,-16,137,69,'#8c9985');rect(28,64,137,43,'#b89672');
+    text(ep===3?'COMPARE YOUR FINISHES':'YOUR PROJECT SHORTLIST',-192,-162,23,ink);
+   });
+ }
+ function actionNotes(t){
+   if(ep!==2)return;const a=pulse(t,8,8.5,14.2,14.8);if(!a)return;
+   ['ABRASION','CLEANABILITY','CARE'].forEach((s,i)=>{const show=ease(t,8+i*.7,8.4+i*.7);if(show){rect(440,120+i*77,440,58,'#f4f0e6');text(s,468,159+i*77,27,ink);}});
+ }
+ function performances(t){
+   let wwalk=gait(t,.2,2.9,126,253),pwalk=gait(t,.5,3.1,949,801);
+   const w={walk:wwalk,left:[-85,-279],right:[149,-396],wrists:[-.1,-.36],props:[],gestures:['open','open'],look:.6,lean:Math.sin(t*.8)*.012};
+   const p={walk:pwalk,left:[-130,-402],right:[86,-275],wrists:[.14,.08],props:['sample'],look:-.6,lean:Math.sin(t*.73+1)*.013};
+   // Presenter gestures are deliberately different for each spoken clause.
+   const gesture=key(t,[[0,100,-285],[2.8,145,-411],[4.4,165,-440],[5.2,120,-367],[8.7,161,-441],[9.6,149,-378],[12.8,180,-432],[14,125,-360],[17,152,-419],[19.8,126,-383]]);
+   w.right=gesture;w.wrists[1]=key(t,[[0,0],[3,-.5],[5.2,-.15],[8,-.42],[11,.1],[14,-.4],[18,-.2]])[0];
+   w.gestures[1]=t>4.5&&t<8.8?'point':'open';w.headTilt=Math.sin(t*.65)*.022;w.look=t>4&&t<9?1:.1;
+   if(t<3){w.right=[90-Math.sin(t*4)*40,-277];w.left=[-90+Math.sin(t*4)*40,-277];p.left=[-90+Math.sin(t*4)*24,-280];p.props=[];}
+   if(ep===0){p.props=['report'];p.left=key(t,[[0,-85,-280],[3,-130,-365],[6,-131,-420],[10,-153,-445],[14,-125,-384],[19,-108,-376]]);p.wrists[0]=-.14;p.right=key(t,[[0,90,-277],[8,98,-320],[11,-10,-377],[14,93,-278]]);}
+   if(ep===1){
+    p.props=t<8?['roller']:['sample'];p.left=key(t,[[0,-88,-283],[3,-129,-447],[4.5,-142,-495],[5.2,-143,-411],[6,-146,-495],[7,-145,-397],[8,-100,-367],[11,-139,-436],[15,-103,-361]]);p.wrists[0]=.07;
+    if(t>9.3){p.walk=gait(t,9.3,11.4,801,870);p.look=-1;p.lean=-.06*ease(t,10,11);}
+    w.props=['sample'];w.left=key(t,[[0,-87,-282],[4,-98,-291],[6,-39,-389],[8,-30,-433],[12,-59,-386],[17,-78,-320]]);w.wrists[0]=-.08;
+   }
+   if(ep===2){
+    p.crouch=pulse(t,4,5.6,8.8,10.2)*113;p.lean=-pulse(t,4,5.4,9,10.3)*.10;
+    const wipe=pulse(t,5,5.8,8.2,9);p.props=['cloth'];p.left=[-147-wipe*17,-389+p.crouch+wipe*Math.sin(t*6)*42];p.wrists[0]=-.5;
+    p.right=[85,-284+p.crouch];p.look=-1;
+    w.props=['sample'];w.left=[-45,-371];w.wrists[0]=-.18;
+   }
+   if(ep===3){
+    p.props=['sample'];p.left=key(t,[[0,-92,-280],[3,-100,-366],[6,-138,-440],[10,-144,-478],[14,-126,-418],[18,-93,-340]]);p.wrists[0]=key(t,[[0,0],[3,-.08],[7,.22],[10,-.18],[13,.16],[17,0]])[0];
+    w.props=['fan'];w.left=key(t,[[0,-90,-280],[4,-33,-365],[7,-44,-400],[12,-24,-389],[16,-44,-346]]);w.wrists[0]=-.13;
+   }
+   if(ep===4){
+    w.props=['tablet'];w.left=[-43,-341];w.right=key(t,[[0,101,-280],[3,143,-413],[6,158,-444],[8.5,65,-361],[10,34,-340],[11.6,20,-356],[13,144,-411],[17,160,-444],[19,112,-374]]);w.gestures[1]=t>8&&t<12?'point':'open';
+    p.props=['fan'];p.left=[-129,-400];p.right=key(t,[[0,89,-280],[6,86,-280],[9,-18,-384],[12,76,-285],[17,100,-382],[19,86,-297]]);
+   }
+   // Camera-right actor is drawn first so the presenter remains the visual lead.
+   character('pippa',t,p);character('wallo',t,w);
+ }
+ function overlay(t){
+   rect(0,0,1080,437,chalk);rect(0,1437,1080,483,chalk);
+   text('DESIGNER WALLCOVERINGS',72,96,25,ink);text('WALLO PRESENTS',1008,96,21,teal,'Verdana','right');
+   line(72,124,1008,124,'#b9b6aa',1);text(E.kicker,72,178,24,teal);
+   E.title.split('\n').forEach((s,i)=>text(s,69,270+i*87,73,ink,'Georgia'));
+   text('MATERIAL NOTES',73,411,22,teal);text('WITH WALLO & PIPPA',1006,411,20,teal,'Verdana','right');
+   const product=P.find(p=>p.id===E.product);text(product.label,72,1481,25,ink);
+   line(72,1505,1008,1505,'#b9b6aa',1);
+   const segment=(E.audio?.segments||[]).filter(s=>t>=s.start).at(-1);
+   const caption=segment?E.captions[segment.index]:'A wallcovering presentation with Wallo.';
+   wrap(caption,72,1570,930,38,ink,'Verdana',55);
+   if(t>=16.8)text(E.takeaway,72,1738,37,teal,'Georgia');
+   wrap(E.note,72,1794,926,24,'#58635b','Verdana',34);
+   text('designerwallcoverings.com',72,1870,25,teal);
+   text(String(ep+1).padStart(2,'0')+' / 05',1008,1870,22,teal,'Verdana','right');
+   rect(72,1894,936,3,'#c9c7bb');rect(72,1894,936*clamp(t/20),3,teal);
+ }
+ function draw(t){
+   telemetry=[];rect(0,0,1080,1920,chalk);
+   c.save();c.beginPath();c.rect(0,437,1080,1000);c.clip();
+   const zoom=1+ease(t,3.3,5)*.08-ease(t,14.2,16)*.06;
+   c.translate(540,950);c.scale(zoom,zoom);c.translate(-540,-487);
+   room(t);documentPanel(t);swatchBoard(t);actionNotes(t);performances(t);
+   c.restore();overlay(t);window.__rigState=telemetry;
+ }
+ const driver={t:0};window.__timelines=window.__timelines||{};
+ window.__timelines.main=gsap.timeline({paused:true}).to(driver,{t:20,duration:20,ease:'none',onUpdate:()=>draw(driver.t)},0);
+ window.__draw=draw;
+ window.__ready=Promise.all(P.map(p=>new Promise((resolve,reject)=>{const im=new Image();images[p.id]=im;im.onload=resolve;im.onerror=()=>reject(new Error('Missing product '+p.id));im.src=p.local;}))).then(()=>document.fonts.ready).then(()=>{draw(driver.t);return true;});
+ draw(0);
+})();
+</script></body></html>
\ No newline at end of file
diff --git a/series2/episodes/06-built-for-busy-spaces.html b/series2/episodes/06-built-for-busy-spaces.html
new file mode 100644
index 0000000..821415f
--- /dev/null
+++ b/series2/episodes/06-built-for-busy-spaces.html
@@ -0,0 +1,215 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=1080,height=1920"><title>Wallo presents — Performance in busy spaces</title><style>@font-face{font-family:Georgia;src:url('../assets/georgia.ttf')}@font-face{font-family:Verdana;src:url('../assets/verdana.ttf')}html,body{margin:0;width:1080px;height:1920px;overflow:hidden}#root{position:relative;width:1080px;height:1920px;overflow:hidden}.clip{position:absolute;inset:0;width:1080px;height:1920px}canvas{width:1080px;height:1920px}.preload{position:absolute;opacity:0;pointer-events:none}</style><script src="../assets/gsap.min.js"></script></head><body><div id="root" data-composition-id="main" data-start="0" data-duration="20" data-width="1080" data-height="1920" data-fps="30"><section id="material-scene" class="clip" data-start="0" data-duration="20" data-track-index="0"><canvas id="stage" width="1080" height="1920" aria-label="Wallo presenting Performance in busy spaces"></canvas><span class="preload" style="font-family:Georgia">Wallo</span><span class="preload" style="font-family:Verdana">Designer Wallcoverings</span></section><audio id="narrated-mix" src="../assets/mix-2.wav" data-start="0" data-duration="20" data-track-index="1"></audio></div><script>window.EPISODE={"slug":"06-built-for-busy-spaces","title":"Beautiful.\nBuilt for daily life.","shortTitle":"Performance in busy spaces","kicker":"03 / MATERIAL PERFORMANCE","product":"wheaton","note":"Type II performance varies by product. Request specifications.","takeaway":"Specify for the way a room lives.","lines":["In a busy space, the finish has a job to do.","Type two vinyl offers practical durability for demanding interiors.","Wheaton pairs a woven look with a commercial vinyl construction.","Check abrasion, cleanability, and the manufacturer's care instructions before specifying."],"captions":["Start with the room's daily demands.","Consider Type II commercial vinyl.","Wheaton: a woven look in Type II vinyl.","Check abrasion. Cleanability. Care."],"index":2,"audio":{"slug":"06-built-for-busy-spaces","provider":"Kokoro local","voice":"am_michael","duration":20,"segments":[{"index":0,"start":0.6,"end":3.693,"text":"In a busy space, the finish has a job to do.","caption":"Start with the room's daily demands.","duration":3.093},{"index":1,"start":5.1,"end":9.334,"text":"Type two vinyl offers practical durability for demanding interiors.","caption":"Consider Type II commercial vinyl.","duration":4.234},{"index":2,"start":9.6,"end":13.675,"text":"Wheaton pairs a woven look with a commercial vinyl construction.","caption":"Wheaton: a woven look in Type II vinyl.","duration":4.075},{"index":3,"start":14.1,"end":18.902,"text":"Check abrasion, cleanability, and the manufacturer's care instructions before specifying.","caption":"Check abrasion. Cleanability. Care.","duration":4.802}],"energy":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0012,0.0015,0.0021,0.2127,0.6512,0.6471,0.7932,0.945,0.9537,0.727,0.4854,0.3062,0.2264,0.1563,0.1451,1,1,1,1,1,0.3182,0.2795,0.3123,0.4062,0.506,0.3977,0.3576,0.3326,0.5822,0.6731,0.5581,0.5063,0.1297,0.0058,0.0028,0.003,0.6119,0.9917,0.8243,0.6299,0.6663,0.4952,0.3451,0.2338,0.1537,0.1372,0.1059,0.0932,0.2432,0.3006,0.2704,0.2103,0.0596,0.0105,0.0067,0.0088,0.0089,0.0108,0.0084,0.0079,0.0057,0.0032,0.0044,0.003,0.0018,0.0035,0.126,0.8047,0.5471,0.1905,0.1781,0.1512,0.1297,0.125,0.0996,0.1018,1,1,1,1,0.8537,0.8106,0.7733,0.714,0.6337,0.5126,0.4252,0.3675,0.5963,0.4882,0.4667,0.0908,0.1107,0.2512,0.3905,0.5846,0.5633,0.4974,0.2823,0.2234,0.3073,0.4833,0.5316,0.3813,0.278,0.2428,0.1915,0.149,0.1716,0.3004,0.3789,0.4765,0.8037,0.7764,0.7569,0.6732,0.7456,0.7082,1,0.9553,1,1,0.5379,0.2345,0.1297,0.0567,0.0064,0.1256,0.1565,0.238,0.3846,0.2614,0.2179,0.1768,0.142,0.2994,0.5863,0.7369,0.6726,0.6013,0.4784,0.4013,0.2237,0.1091,0.0441,0.0552,0.0332,0.0286,0.0324,0.0206,0.013,0.0034,0.0015,0.0014,0.0011,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0146,0.3635,0.1949,0.498,0.8208,1,1,0.8788,0.6189,0.0611,0.0112,0.0069,0.0034,0.1979,0.791,0.6863,0.2378,0.3542,0.5993,0.534,0.488,0.3899,0.3537,0.2956,0.1996,0.1988,0.1962,0.2189,0.5123,1,0.8119,0.5664,0.5503,0.628,0.5862,0.6006,0.4988,0.328,0.2652,0.2112,0.1634,0.1417,0.0856,0.064,0.0496,0.0071,0.0017,0.0032,0.0053,0.0054,0.0061,0.0066,0.0073,0.0088,0.0094,0.0108,0.0079,0.0039,0.002,0.0145,0.7027,1,1,1,1,0.5444,0.2029,0.202,0.177,0.1204,0.089,0.4282,0.5306,0.4205,0.4464,0.2516,0.4812,0.7323,0.5201,0.1112,0.0029,0.001,0.0021,0.0901,0.0718,0.1048,0.7089,0.845,0.7733,0.6227,0.5792,0.1986,0.0125,0.0027,0.0677,0.2826,0.4503,0.15,0.041,0.03,0.1569,0.249,0.5172,0.4127,0.2394,0.1591,0.1434,0.1091,0.2848,0.538,0.5392,0.4876,0.3992,0.4193,0.5255,0.4874,0.4662,0.2934,0.2307,0.144,0.1215,0.1773,0.8407,1,1,0.8165,0.5351,0.4383,0.5576,0.5394,0.3391,0.1548,0.2652,0.25,0.1892,0.1409,0.1207,0.1169,0.0671,0.0292,0.0436,0.0638,0.1015,0.0679,0.3979,0.2957,0.2086,0.1658,0.1924,0.5095,0.6187,0.4547,0.4907,0.4886,0.4568,0.5018,0.4344,0.4923,0.5269,0.611,0.5874,0.6453,0.4909,0.4781,0.3703,0.2635,0.4451,0.4315,0.32,0.3296,0.3613,0.3576,0.3637,0.354,0.3911,0.4264,0.3941,0.1979,0.0273,0.341,0.4423,0.179,0.0827,0.0498,0.1852,0.4017,0.3851,0.4586,0.455,0.455,0.4451,0.3769,0.2996,0.2211,0.1131,0.0838,0.0908,0.0643,0.0478,0.0123,0.0146,0.0112,0.009,0.0483,0.1337,0.2209,0.2495,0.1103,0.0212,0.0024,0.0006,0.0006,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0011,0.0018,0.0285,0.1618,0.2493,0.5878,0.7835,0.7383,0.625,0.5108,0.3396,0.2142,0.0617,0.0348,0.097,0.3064,0.5633,0.5583,0.6678,0.6168,0.5077,0.256,0.0639,0.0132,0.2325,0.1657,0.1664,0.2814,0.4519,0.5975,0.5744,0.5752,0.5124,0.6027,0.5268,0.6062,0.5736,0.5099,0.4171,0.4476,0.5277,0.3497,0.6471,0.5418,0.495,0.4534,0.37,0.3073,0.2545,0.2262,0.2522,0.3312,0.6848,0.831,0.8664,0.9886,1,1,1,1,0.8053,0.2719,0.1949,0.1991,0.2913,0.5387,0.5279,0.4369,0.4943,0.4473,0.4478,0.4493,0.4238,0.3939,0.3951,0.3783,0.993,1,1,1,0.7876,0.1765,0.0812,0.0156,0.0037,0.0016,0.0015,0.0619,0.0584,0.0197,0.0038,0.0022,0.0041,0.0057,0.0066,0.0071,0.0078,0.0091,0.0095,0.0084,0.0081,0.0058,0.0036,0.004,0.0054,0.2041,0.3427,0.5544,0.2462,0.2219,0.6373,0.7568,0.269,0.0713,0.0078,0.2037,0.2972,0.1735,0.4739,0.5672,0.486,0.4498,0.4459,0.3967,0.4686,0.681,0.7377,0.7784,0.8476,0.9518,0.5457,0.3864,0.6961,0.6268,0.5272,0.5707,0.1732,0.3942,0.4646,0.3571,0.3596,0.2378,0.1936,0.1895,0.1797,0.1946,0.1999,0.6102,1,0.767,0.547,0.6811,0.7671,0.6662,0.626,0.7062,0.6675,0.5947,0.5135,0.5267,0.4802,0.396,0.3741,0.2249,0.0466,0.0078,0.0033,0.1926,0.0703,0.4245,0.4691,0.4365,0.2804,0.3954,0.8333,0.6043,0.1644,0.011,0.0555,0.1302,0.7194,0.8248,0.5133,0.363,0.221,0.0587,0.0061,0.0654,0.3635,0.6616,0.5578,0.4599,0.149,0.2016,0.0944,0.0606,0.0246,0.0328,0.0071,0.016,0.0053,0.0027,0.0011,0.0011,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0007,0.0259,0.1969,0.6607,0.6082,0.6101,1,1,0.1408,0.0056,0.2396,0.8504,0.8922,0.4359,0.2495,0.1877,0.1491,0.4862,0.8914,1,1,0.9958,0.9084,0.8392,0.7016,0.4946,0.4991,0.4621,0.3939,0.3956,0.3726,0.309,0.32,0.1547,0.0801,0.0719,0.0332,0.0441,0.0093,0.0042,0.0022,0.0015,0.0017,0.001,0.0012,0.0012,0.0008,0.001,0.0008,0.0018,0.001,0.0035,0.0822,0.2841,0.0857,0.2211,0.8477,0.822,0.805,0.763,0.7168,0.6725,0.6392,0.3728,0.277,0.2009,0.8394,1,1,1,0.9496,0.6685,0.612,0.718,0.4759,0.218,0.3351,0.2447,0.1462,0.084,0.0782,0.0773,0.0189,0.0026,0.0039,0.0049,0.0065,0.0076,0.0081,0.0085,0.0105,0.0132,0.012,0.0166,0.0186,0.0118,0.0685,0.3254,0.677,0.8151,0.9263,0.7397,0.9111,0.6696,0.5808,0.6226,0.5796,0.8206,0.7288,0.8343,0.9161,0.87,1,0.9445,0.7657,0.6179,0.5163,0.2326,0.1398,0.1368,0.1286,0.1118,0.0991,1,0.7676,0.7901,0.6968,0.7064,0.3507,0.0479,0.04,0.2695,0.4935,0.4394,0.3898,0.1677,0.3072,0.3128,0.3401,0.3414,0.3791,0.3749,0.3305,0.2947,0.5008,0.3068,0.0196,0.0044,0.1406,0.1214,0.1345,0.503,0.5365,0.472,0.439,0.4725,0.414,0.3847,0.4213,0.4934,0.5946,0.443,0.2423,0.6681,0.7252,0.4319,0.0164,0.0395,0.0877,0.9913,1,0.8395,0.3687,0.0676,0.056,0.1081,0.4632,0.3998,0.3751,0.2008,0.4852,0.4153,0.3729,0.3721,0.3147,0.2382,0.1791,0.0982,0.1268,0.2313,0.4875,0.5342,0.2236,0.1202,0.1231,0.0951,0.0701,0.4074,0.4046,0.4104,0.3143,0.5498,0.659,0.5943,0.554,0.0938,0.0028,0.1451,0.918,0.7358,0.5321,0.3152,0.4069,0.4416,0.4936,0.2106,0.4072,0.15,0.0982,0.1001,0.058,0.4501,0.7039,0.4128,0.3592,0.334,0.2639,0.1672,0.1398,0.1268,0.0878,0.0891,0.047,0.0359,0.0291,0.0387,0.0092,0.0039,0.0014,0.0012,0.0003,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"peak":0.652305006980896}};window.PRODUCTS=[{"id":"fairborn","title":"Fairborn Type 2 | Phillipe Romano","label":"Fairborn Type 2 · Phillipe Romano","url":"https://www.designerwallcoverings.com/products/prw-920735-rif-003","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/prw-920735-rif-003.jpg?v=1781716604","file":"assets/products/fairborn.jpg","description":"<p>When light rakes across the embossed surface, it reveals an organic grid-like pattern of broken lines. Although extremely durable — as you’d expect from a Type II vinyl wallcovering named for rock fissures — offering a soft, nature-inspired palette of creams, taupes, blues and browns. A small scale and intricate pattern appear almost seamless, and its 12 two-tone colorways resemble artisanal materials like mosaic</p><br><br>A captivating wallcovering featuring intricate detailing and refined textures, offering a sophisticated backdrop that elevates any interior space. The design evokes timeless elegance with a modern touch.  \n#luxuryinteriors #modernwallcoverings #elegantdesign\n","retrieved_at":"2026-09-05","local":"../assets/products/fairborn.jpg"},{"id":"waterperry","title":"Waterperry Ivory/Stone | Sanderson","label":"Waterperry / Ivory–Stone · Sanderson","url":"https://www.designerwallcoverings.com/products/waterperry-ivory-stone-sanderson","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DWAP216280_5340.jpg?v=1788582264","file":"assets/products/waterperry.jpg","description":"<div class=\"sanderson-product\">\n<p>Waterperry in Ivory/Stone is a Sanderson Wallcovering in a botanical/leaves and trees design. Part of the Sanderson heritage collection, this pattern reads in a palette of Alabaster, Linen, Mist. Supplied by the roll; a memo sample is available. An English design-house classic from Sanderson Design Group.</p>\n</div>","retrieved_at":"2026-09-05","local":"../assets/products/waterperry.jpg"},{"id":"wheaton","title":"Wheaton Type 2 | Phillipe Romano","label":"Wheaton Type 2 · Phillipe Romano","url":"https://www.designerwallcoverings.com/products/prw-920166-coa-004-1","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/d4639abbf9baa5237707.jpg?v=1785438111","file":"assets/products/wheaton.jpg","description":"<p>Weaving shimmering strands and beguiling texture, emulating the intricacy of our favored grasscloth Tide in the form of a durable Type II vinyl</p>\n","retrieved_at":"2026-09-05","local":"../assets/products/wheaton.jpg"},{"id":"topiary","title":"Topiary View Thyme | Sanderson","label":"Topiary View / Thyme · Sanderson","url":"https://www.designerwallcoverings.com/products/topiary-view-thyme-sanderson","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DHIP217511_f500.jpg?v=1788582179","file":"assets/products/topiary.jpg","description":"<div class=\"sanderson-product\">\n<p>Topiary View in Thyme is a Sanderson Wallcovering in a landscapes design. Part of the Sanderson heritage collection, this pattern reads in a palette of Alabaster, Stone, Oatmeal. Supplied by the roll; a memo sample is available. An English design-house classic from Sanderson Design Group.</p>\n</div>","retrieved_at":"2026-09-05","local":"../assets/products/topiary.jpg"},{"id":"kaya","title":"Kaya - Cream Weave Wallcovering | Malibu Wallcovering","label":"Kaya / Cream Weave · Malibu","url":"https://www.designerwallcoverings.com/products/kaya-cream-weave-wallcovering-malibu-wallpaper","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/0086435_kaya-cream-weave_650.jpg?v=1776906632","file":"assets/products/kaya.jpg","description":"<p>Elevate your space with the luxurious Kaya Cream Weave wallcovering. This exquisite nonwoven vinyl design features a warm neutral color palette and stunning texture, perfect for creating an elegant ambiance.</p>","retrieved_at":"2026-09-05","local":"../assets/products/kaya.jpg"}];/* Wallo Material Notes: deterministic articulated character acting and real DW samples. */
+(() => {
+ 'use strict';
+ const canvas=document.getElementById('stage'), c=canvas.getContext('2d');
+ const E=window.EPISODE, P=window.PRODUCTS, ep=E.index;
+ const ink='#243a3b', chalk='#eeeae2', teal='#284f4b', clay='#a9634d', brass='#b7924f';
+ const images={}; let telemetry=[];
+ const clamp=(x,a=0,b=1)=>Math.max(a,Math.min(b,x));
+ const mix=(a,b,p)=>a+(b-a)*p;
+ const smooth=p=>{p=clamp(p);return p*p*(3-2*p);};
+ const ease=(t,a,b)=>smooth((t-a)/(b-a));
+ const pulse=(t,a,b,c,d)=>ease(t,a,b)*(1-ease(t,c,d));
+ const point=(x,y)=>({x,y});
+ const dist=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y);
+ function group(x,y,r,s,fn){c.save();c.translate(x,y);c.rotate(r);c.scale(s,s);fn();c.restore();}
+ function path(fn,fill,stroke=null,w=2){c.beginPath();fn(c);if(fill){c.fillStyle=fill;c.fill();}if(stroke){c.lineWidth=w;c.strokeStyle=stroke;c.lineJoin='round';c.lineCap='round';c.stroke();}}
+ function line(x,y,u,v,col,w=2){path(q=>{q.moveTo(x,y);q.lineTo(u,v);},null,col,w);}
+ function rect(x,y,w,h,col){c.fillStyle=col;c.fillRect(x,y,w,h);}
+ function oval(x,y,rx,ry,col,stroke=null,w=2){path(q=>q.ellipse(x,y,rx,ry,0,0,Math.PI*2),col,stroke,w);}
+ function rr(x,y,w,h,r,col,stroke=null,lw=2){path(q=>q.roundRect(x,y,w,h,r),col,stroke,lw);}
+ function text(s,x,y,size=28,color=ink,font='Verdana',align='left'){c.font=`${size}px '${font}'`;c.textAlign=align;c.textBaseline='alphabetic';c.fillStyle=color;c.fillText(s,x,y);}
+ function wrap(s,x,y,max,size=36,color=ink,font='Verdana',lh=size*1.4){
+   c.font=`${size}px '${font}'`;let row='',lines=[];
+   for(const word of s.split(' ')){const test=row?row+' '+word:word;if(c.measureText(test).width>max&&row){lines.push(row);row=word;}else row=test;}
+   if(row)lines.push(row);lines.forEach((v,i)=>text(v,x,y+i*lh,size,color,font));return lines.length;
+ }
+ function imageCover(id,x,y,w,h){const im=images[id];if(!im||!im.complete)return;const k=Math.max(w/im.width,h/im.height);c.save();c.beginPath();c.rect(x,y,w,h);c.clip();c.drawImage(im,x+(w-im.width*k)/2,y+(h-im.height*k)/2,im.width*k,im.height*k);c.restore();}
+ function sample(id,x,y,w=126,h=164,r=0){group(x,y,r,1,()=>{rr(-w/2+7,-h/2+9,w,h,2,'#203a3a20');rect(-w/2-5,-h/2-5,w+10,h+10,'#f9f7f0');imageCover(id,-w/2,-h/2,w,h);line(-w/2,-h/2,w/2,-h/2,'#ffffff',2);});}
+ function bone(a,b,width,col,shade){
+   const angle=Math.atan2(b.y-a.y,b.x-a.x),d=dist(a,b);
+   group(a.x,a.y,angle,1,()=>{rr(-width*.18,-width/2,d+width*.36,width,width*.46,col);path(q=>{q.moveTo(5,width*.26);q.quadraticCurveTo(d*.5,width*.34,d-3,width*.22);},null,shade||'#00000018',2.3);});
+ }
+ function solveIK(root,target,l1,l2,pole){
+   const dx=target.x-root.x,dy=target.y-root.y,raw=Math.hypot(dx,dy),d=clamp(raw,Math.abs(l1-l2)+.01,l1+l2-.01);
+   const nx=raw?dx/raw:0,ny=raw?dy/raw:1,a=(l1*l1-l2*l2+d*d)/(2*d),h=Math.sqrt(Math.max(0,l1*l1-a*a));
+   const end=point(root.x+nx*d,root.y+ny*d),joint=point(root.x+nx*a-ny*h*pole,root.y+ny*a+nx*h*pole);
+   return {root,joint,end,l1,l2};
+ }
+ function rotatePoint(p,angle,origin=point(0,-258)){const x=p.x-origin.x,y=p.y-origin.y;return point(origin.x+x*Math.cos(angle)-y*Math.sin(angle),origin.y+x*Math.sin(angle)+y*Math.cos(angle));}
+ function key(t,frames){if(t<=frames[0][0])return frames[0].slice(1);for(let i=1;i<frames.length;i++)if(t<=frames[i][0]){const p=ease(t,frames[i-1][0],frames[i][0]);return frames[i].slice(1).map((v,j)=>mix(frames[i-1][j+1],v,p));}return frames.at(-1).slice(1);}
+ function gait(t,start,end,x0,x1){
+   const p=clamp((t-start)/(end-start));
+   const lift=(a,b)=>p>a&&p<b?Math.sin(Math.PI*(p-a)/(b-a))*44:0;
+   const left=mix(x0-30,(x0+x1)/2-30,smooth((p-.08)/.28))+mix(0,(x1-x0)/2,smooth((p-.65)/.3));
+   const right=mix(x0+30,x1+30,smooth((p-.35)/.3));
+   return {x:(left+right)/2,feet:[point(left,-lift(.08,.36)-lift(.65,.95)),point(right,-lift(.35,.65))],walking:p>0&&p<1,phase:p};
+ }
+ function voiceEnergy(t){const env=E.audio?.energy||[];return env[Math.min(env.length-1,Math.max(0,Math.floor(t*50)))]||0;}
+ function hand(x,y,r,pose='open',skin='#c89472',prop=null){
+   group(x,y,r,1,()=>{
+    if(prop==='sample')sample(E.product,8,-18,115,148,-.04);
+    if(prop==='fan')for(let k=0;k<3;k++)sample(['waterperry','topiary','kaya'][k],k*17-15,-43,91,130,(k-1)*.18);
+    if(prop==='report'){rr(-12,-134,135,176,4,'#f8f5e9',ink,2);rect(1,-117,108,5,teal);text('TEST',4,-87,22,ink);text('REPORT',4,-58,22,ink);for(let k=0;k<3;k++)line(4,-35+k*18,97,-35+k*18,'#a4aaa0',2);}
+    if(prop==='cloth'){path(q=>{q.moveTo(-28,-15);q.lineTo(30,-26);q.lineTo(43,35);q.lineTo(-21,44);q.closePath();},'#c8c5b5',ink,1);line(-17,-4,28,25,'#efeadb',3);}
+    if(prop==='roller'){line(0,-8,0,-72,ink,7);line(0,-72,37,-72,ink,7);line(37,-72,37,-108,ink,5);rr(-27,-132,127,38,11,'#8b9c8d',ink,2);rr(-9,-9,18,54,7,brass);}
+    if(prop==='tablet'){rr(-73,-76,145,170,8,ink);rr(-65,-67,129,151,4,'#f4f0e5');text('DW',-45,-33,27,teal,'Georgia');for(let i=0;i<3;i++)imageCover(['waterperry','fairborn','kaya'][i],-47+i*32,-6,27,60);}
+    if(pose==='point'){bone(point(-4,3),point(34,-18),13,skin);bone(point(0,9),point(16,15),21,skin);oval(-6,9,13,18,skin);}
+    else{rr(-14,-9,29,42,12,skin);oval(-15,5,7,13,skin);for(let i=0;i<3;i++)line(-7+i*6,14,-7+i*6,28,'#9e685446',1);}
+   });
+ }
+ function head(kind,x,y,t,look,mouth,tilt){
+   const male=kind==='wallo',skin=male?'#c89472':'#d5a185',dark=male?'#714f3b':'#834e39';
+   group(x,y,tilt,.82,()=>{
+    if(!male){oval(-4,-10,51,70,dark);oval(-39,-50,28,28,dark);path(q=>{q.moveTo(-57,-55);q.quadraticCurveTo(-81,-37,-59,-16);},null,'#a86b4b',7);}
+    // Tapered jaw, defined brow and modest nose maintain adult proportions.
+    path(q=>{q.moveTo(-41,-40);q.bezierCurveTo(-43,-77,43,-80,44,-34);q.lineTo(40,31);q.quadraticCurveTo(31,66,5,72);q.quadraticCurveTo(-26,65,-36,34);q.closePath();},skin);
+    path(q=>{q.moveTo(-38,-31);q.lineTo(-32,34);q.quadraticCurveTo(-24,52,-8,57);q.lineTo(-20,21);q.lineTo(-18,-46);q.closePath();},male?'#b58162':'#c39076');
+    oval(-40,3,9,16,skin);oval(42,2,8,15,skin);
+    if(male){path(q=>{q.moveTo(-39,-39);q.lineTo(-33,-18);q.lineTo(-30,-54);q.lineTo(31,-54);q.lineTo(43,-25);q.lineTo(42,-55);q.closePath();},dark);}
+    else{path(q=>{q.moveTo(-40,-27);q.quadraticCurveTo(-24,-63,2,-56);q.quadraticCurveTo(38,-37,43,-19);q.lineTo(38,-58);q.quadraticCurveTo(-4,-89,-37,-55);q.closePath();},dark);for(let i=0;i<4;i++)path(q=>{q.moveTo(-29+i*17,-56);q.quadraticCurveTo(-26+i*17,-43,-13+i*17,-39);},null,'#ae7150',2);}
+    const blink=(t+(male?.31:1.93))%4.4<.14;
+    for(const side of [-1,1]){const ex=side*18+look*3,ey=-8;path(q=>{q.moveTo(ex-10,ey);q.quadraticCurveTo(ex,ey-5,ex+10,ey);},null,ink,2.2);if(!blink){oval(ex+look*3,ey+1,3.5,4.3,ink);oval(ex+look*3+1,ey-.3,1.1,1.2,'#eee8dc');}line(ex-10,ey-15,ex+8,ey-17+(side===1?look*2:0),dark,2.5);}
+    path(q=>{q.moveTo(3+look*5,-5);q.lineTo(8+look*8,15);q.quadraticCurveTo(4+look*5,20,-1+look*5,17);},null,'#98644f',1.8);
+    if(male){path(q=>{q.moveTo(-20,29);q.quadraticCurveTo(-6,20,4,27);q.quadraticCurveTo(15,23,26,30);q.quadraticCurveTo(17,39,3,34);q.quadraticCurveTo(-9,37,-20,29);},dark);}
+    if(mouth>.12){oval(4,44,10+mouth*3,2+mouth*6,'#714735');if(mouth>.4)line(-2,41,11,41,'#efe6d7',2);}
+    else path(q=>{q.moveTo(-7,43);q.quadraticCurveTo(5,48,17,42);},null,'#714735',1.8);
+    if(male){path(q=>{q.moveTo(-49,-53);q.quadraticCurveTo(-48,-98,10,-95);q.quadraticCurveTo(47,-89,48,-53);q.closePath();},brass);path(q=>{q.moveTo(-49,-53);q.quadraticCurveTo(12,-64,72,-48);q.quadraticCurveTo(77,-35,39,-41);q.lineTo(-49,-43);q.closePath();},'#97773f');line(-27,-73,12,-84,'#d2b270',3);}
+    else oval(-39,14,3.5,5.5,brass);
+   });
+ }
+ function character(kind,t,settings={}){
+   const male=kind==='wallo',skin=male?'#c89472':'#d5a185',shirt=male?'#d6d0bc':clay,cloth=male?teal:'#c8c1af';
+   const walk=settings.walk||gait(t,0,1,settings.x,settings.x);let x=walk.x,y=settings.y||882;
+   const crouch=settings.crouch||0,lean=settings.lean||0,breath=Math.sin(t*2.1+(male?0:1.2))*1.7;
+   const hipY=-275+crouch,origin=point(0,hipY);
+   const bodyPoint=(u,v)=>rotatePoint(point(u,v+crouch+breath),lean,origin);
+   const leftShoulder=bodyPoint(-62,-469),rightShoulder=bodyPoint(62,-469);
+   const arms=[solveIK(leftShoulder,point(...settings.left),118,111,-1),solveIK(rightShoulder,point(...settings.right),118,111,1)];
+   const legs=walk.feet.map((foot,i)=>solveIK(point(i===0?-32:32,hipY),point(foot.x-x,foot.y-13),134,136,i===0?1:-1));
+   const mouth=male?voiceEnergy(t)*.88:0;
+   const joints={kind,x,y,arms,legs,lean,crouch,feet:walk.feet,walking:walk.walking,props:settings.props||[]};
+   telemetry.push(joints);
+   oval(x,y+6,94-crouch*.1,15,'#233a3b15');
+   group(x,y,0,settings.scale||1,()=>{
+    // Back arm and far leg precede torso so elbow overlaps read spatially.
+    const armDraw=(i)=>{const b=arms[i];bone(b.root,b.joint,39,shirt);bone(b.joint,b.end,27,skin);oval(b.joint.x,b.joint.y,16,16,skin);const sleeve=point(mix(b.root.x,b.joint.x,.77),mix(b.root.y,b.joint.y,.77));bone(b.root,sleeve,43,shirt);line(sleeve.x-8,sleeve.y,sleeve.x+9,sleeve.y,'#ffffff30',2);};
+    armDraw(0);
+    legs.forEach((b,i)=>{bone(b.root,b.joint,58,cloth);bone(b.joint,b.end,43,cloth);oval(b.joint.x,b.joint.y,23,23,cloth);line(b.root.x-8,b.root.y+30,b.joint.x-6,b.joint.y-16,male?'#41635b':'#dfd8c8',2);group(b.end.x,b.end.y+10,walk.walking&&walk.feet[i].y<0?-.12:0,1,()=>{path(q=>{q.moveTo(-24,-20);q.lineTo(20,-20);q.lineTo(27,-9);q.quadraticCurveTo(60,-5,56,14);q.lineTo(-24,14);q.closePath();},male?'#655545':'#626b62');line(-24,14,55,14,'#34413d',5);line(4,-8,25,-7,'#c6b69b',2);});});
+    group(0,hipY,lean,1,()=>{
+     const dy=crouch+breath-hipY;
+     path(q=>{q.moveTo(-63,-478+dy);q.quadraticCurveTo(-82,-460+dy,-71,-417+dy);q.lineTo(-60,-266+dy);q.quadraticCurveTo(0,-235+dy,60,-266+dy);q.lineTo(71,-417+dy);q.quadraticCurveTo(79,-460+dy,58,-479+dy);q.closePath();},shirt);
+     // Structured overall/apron silhouette, stitched seams, pockets and cloth folds.
+     path(q=>{q.moveTo(-50,-425+dy);q.lineTo(-46,-350+dy);q.lineTo(-61,-290+dy);q.lineTo(-60,-253+dy);q.lineTo(61,-253+dy);q.lineTo(61,-290+dy);q.lineTo(46,-350+dy);q.lineTo(48,-425+dy);q.closePath();},cloth);
+     for(const s of [-1,1]){line(s*44,-472+dy,s*38,-405+dy,cloth,17);oval(s*39,-414+dy,4.8,4.8,brass);line(s*49,-328+dy,s*41,-312+dy,male?'#59756b':'#a9a18f',2);}
+     rr(-27,-401+dy,54,56,4,male?'#254540':'#bab39f');path(q=>{q.moveTo(-23,-388+dy);q.quadraticCurveTo(0,-376+dy,24,-388+dy);},null,male?'#6a8778':'#e5dfd0',1.5);
+     line(-50,-266+dy,52,-266+dy,male?'#1e403b':'#aca590',3);
+     if(male){rr(-66,-321+dy,20,67,3,'#8d7452');line(-56,-342+dy,-56,-282+dy,brass,6);}
+     path(q=>{q.moveTo(-17,-509+dy);q.lineTo(-18,-474+dy);q.quadraticCurveTo(0,-454+dy,21,-474+dy);q.lineTo(18,-509+dy);q.closePath();},skin);
+    });
+    const hp=bodyPoint(0,-559);head(kind,hp.x,hp.y,t,settings.look||0,mouth,(settings.headTilt||0)+lean*.28);
+    armDraw(1);
+    arms.forEach((b,i)=>hand(b.end.x,b.end.y,(settings.wrists||[0,0])[i],(settings.gestures||['open','open'])[i],skin,(settings.props||[])[i]));
+   });
+   return joints;
+ }
+ function room(t){
+   rect(-350,-300,1800,1200,'#dfdbd1');rect(-350,765,1800,450,'#c9c2b1');
+   rect(20,0,240,706,'#e9e7dc');for(let i=0;i<4;i++)line(22+i*59,0,22+i*59,704,'#f7f4e9',7);
+   rect(318,26,647,727,'#c7bfae');rect(329,37,624,704,'#efede3');
+   if(ep===1){const a=ease(t,5.1,8);rect(343,49,596,678,'#aab7a4');c.save();c.beginPath();c.rect(343,49,596*a,678);c.clip();imageCover(E.product,343,49,596,678);c.restore();}
+   else imageCover(E.product,343,49,596,678);
+   line(340,743,943,743,'#faf7eb',4);line(-350,765,1450,765,'#a79f8d',2);
+   for(let i=0;i<6;i++)line(-200,814+i*54,1400,814+i*54,'#b6ad9a',1.4);
+   line(91,765,-170,1220,'#b6ad9a',1.4);line(880,765,1130,1220,'#b6ad9a',1.4);
+   // Light and shadow move gently with the camera, not with an arbitrary pulse.
+   path(q=>{q.moveTo(40,706);q.lineTo(263,706);q.lineTo(672,1120);q.lineTo(412,1120);q.closePath();},'#fcf7df28');
+   // A sample ledge ties the real products to the working material library.
+   rect(404,685,479,12,'#b29d7a');for(let i=0;i<3;i++)sample(P[(i+ep)%P.length].id,468+i*115,622,88,116,(i-1)*.045);
+ }
+ function documentPanel(t){
+   if(ep!==0)return;
+   const a=pulse(t,4.2,4.9,14.3,15.0);if(!a)return;
+   group(640,177+(1-a)*28,0,.88,()=>{
+    rr(-239,-168,478,336,2,'#f7f4e9',ink,1.5);text('BEFORE YOU SPECIFY',-210,-126,24,teal);
+    const labels=['PRODUCT / TEST REPORT','CLASSIFICATION','SUBSTRATE + ADHESIVE'];
+    labels.forEach((label,i)=>{const done=t>5.0+i*2.8;line(-209,-61+i*77,-182,-61+i*77,done?brass:'#bac0b5',3);text(label,-163,-52+i*77,21,ink);});
+   });
+ }
+ function swatchBoard(t){
+   if(ep!==3&&ep!==4)return;
+   const a=pulse(t,6.4,7.1,13.4,14);if(!a)return;
+   group(560,275+(1-a)*25,0,.95,()=>{
+    rr(-199,-139,398,278,2,'#f5f0e4',ink,1);sample(E.product,-83,-1,155,220,0);
+    rect(28,-111,137,83,'#c6bea9');rect(28,-16,137,69,'#8c9985');rect(28,64,137,43,'#b89672');
+    text(ep===3?'COMPARE YOUR FINISHES':'YOUR PROJECT SHORTLIST',-192,-162,23,ink);
+   });
+ }
+ function actionNotes(t){
+   if(ep!==2)return;const a=pulse(t,8,8.5,14.2,14.8);if(!a)return;
+   ['ABRASION','CLEANABILITY','CARE'].forEach((s,i)=>{const show=ease(t,8+i*.7,8.4+i*.7);if(show){rect(440,120+i*77,440,58,'#f4f0e6');text(s,468,159+i*77,27,ink);}});
+ }
+ function performances(t){
+   let wwalk=gait(t,.2,2.9,126,253),pwalk=gait(t,.5,3.1,949,801);
+   const w={walk:wwalk,left:[-85,-279],right:[149,-396],wrists:[-.1,-.36],props:[],gestures:['open','open'],look:.6,lean:Math.sin(t*.8)*.012};
+   const p={walk:pwalk,left:[-130,-402],right:[86,-275],wrists:[.14,.08],props:['sample'],look:-.6,lean:Math.sin(t*.73+1)*.013};
+   // Presenter gestures are deliberately different for each spoken clause.
+   const gesture=key(t,[[0,100,-285],[2.8,145,-411],[4.4,165,-440],[5.2,120,-367],[8.7,161,-441],[9.6,149,-378],[12.8,180,-432],[14,125,-360],[17,152,-419],[19.8,126,-383]]);
+   w.right=gesture;w.wrists[1]=key(t,[[0,0],[3,-.5],[5.2,-.15],[8,-.42],[11,.1],[14,-.4],[18,-.2]])[0];
+   w.gestures[1]=t>4.5&&t<8.8?'point':'open';w.headTilt=Math.sin(t*.65)*.022;w.look=t>4&&t<9?1:.1;
+   if(t<3){w.right=[90-Math.sin(t*4)*40,-277];w.left=[-90+Math.sin(t*4)*40,-277];p.left=[-90+Math.sin(t*4)*24,-280];p.props=[];}
+   if(ep===0){p.props=['report'];p.left=key(t,[[0,-85,-280],[3,-130,-365],[6,-131,-420],[10,-153,-445],[14,-125,-384],[19,-108,-376]]);p.wrists[0]=-.14;p.right=key(t,[[0,90,-277],[8,98,-320],[11,-10,-377],[14,93,-278]]);}
+   if(ep===1){
+    p.props=t<8?['roller']:['sample'];p.left=key(t,[[0,-88,-283],[3,-129,-447],[4.5,-142,-495],[5.2,-143,-411],[6,-146,-495],[7,-145,-397],[8,-100,-367],[11,-139,-436],[15,-103,-361]]);p.wrists[0]=.07;
+    if(t>9.3){p.walk=gait(t,9.3,11.4,801,870);p.look=-1;p.lean=-.06*ease(t,10,11);}
+    w.props=['sample'];w.left=key(t,[[0,-87,-282],[4,-98,-291],[6,-39,-389],[8,-30,-433],[12,-59,-386],[17,-78,-320]]);w.wrists[0]=-.08;
+   }
+   if(ep===2){
+    p.crouch=pulse(t,4,5.6,8.8,10.2)*113;p.lean=-pulse(t,4,5.4,9,10.3)*.10;
+    const wipe=pulse(t,5,5.8,8.2,9);p.props=['cloth'];p.left=[-147-wipe*17,-389+p.crouch+wipe*Math.sin(t*6)*42];p.wrists[0]=-.5;
+    p.right=[85,-284+p.crouch];p.look=-1;
+    w.props=['sample'];w.left=[-45,-371];w.wrists[0]=-.18;
+   }
+   if(ep===3){
+    p.props=['sample'];p.left=key(t,[[0,-92,-280],[3,-100,-366],[6,-138,-440],[10,-144,-478],[14,-126,-418],[18,-93,-340]]);p.wrists[0]=key(t,[[0,0],[3,-.08],[7,.22],[10,-.18],[13,.16],[17,0]])[0];
+    w.props=['fan'];w.left=key(t,[[0,-90,-280],[4,-33,-365],[7,-44,-400],[12,-24,-389],[16,-44,-346]]);w.wrists[0]=-.13;
+   }
+   if(ep===4){
+    w.props=['tablet'];w.left=[-43,-341];w.right=key(t,[[0,101,-280],[3,143,-413],[6,158,-444],[8.5,65,-361],[10,34,-340],[11.6,20,-356],[13,144,-411],[17,160,-444],[19,112,-374]]);w.gestures[1]=t>8&&t<12?'point':'open';
+    p.props=['fan'];p.left=[-129,-400];p.right=key(t,[[0,89,-280],[6,86,-280],[9,-18,-384],[12,76,-285],[17,100,-382],[19,86,-297]]);
+   }
+   // Camera-right actor is drawn first so the presenter remains the visual lead.
+   character('pippa',t,p);character('wallo',t,w);
+ }
+ function overlay(t){
+   rect(0,0,1080,437,chalk);rect(0,1437,1080,483,chalk);
+   text('DESIGNER WALLCOVERINGS',72,96,25,ink);text('WALLO PRESENTS',1008,96,21,teal,'Verdana','right');
+   line(72,124,1008,124,'#b9b6aa',1);text(E.kicker,72,178,24,teal);
+   E.title.split('\n').forEach((s,i)=>text(s,69,270+i*87,73,ink,'Georgia'));
+   text('MATERIAL NOTES',73,411,22,teal);text('WITH WALLO & PIPPA',1006,411,20,teal,'Verdana','right');
+   const product=P.find(p=>p.id===E.product);text(product.label,72,1481,25,ink);
+   line(72,1505,1008,1505,'#b9b6aa',1);
+   const segment=(E.audio?.segments||[]).filter(s=>t>=s.start).at(-1);
+   const caption=segment?E.captions[segment.index]:'A wallcovering presentation with Wallo.';
+   wrap(caption,72,1570,930,38,ink,'Verdana',55);
+   if(t>=16.8)text(E.takeaway,72,1738,37,teal,'Georgia');
+   wrap(E.note,72,1794,926,24,'#58635b','Verdana',34);
+   text('designerwallcoverings.com',72,1870,25,teal);
+   text(String(ep+1).padStart(2,'0')+' / 05',1008,1870,22,teal,'Verdana','right');
+   rect(72,1894,936,3,'#c9c7bb');rect(72,1894,936*clamp(t/20),3,teal);
+ }
+ function draw(t){
+   telemetry=[];rect(0,0,1080,1920,chalk);
+   c.save();c.beginPath();c.rect(0,437,1080,1000);c.clip();
+   const zoom=1+ease(t,3.3,5)*.08-ease(t,14.2,16)*.06;
+   c.translate(540,950);c.scale(zoom,zoom);c.translate(-540,-487);
+   room(t);documentPanel(t);swatchBoard(t);actionNotes(t);performances(t);
+   c.restore();overlay(t);window.__rigState=telemetry;
+ }
+ const driver={t:0};window.__timelines=window.__timelines||{};
+ window.__timelines.main=gsap.timeline({paused:true}).to(driver,{t:20,duration:20,ease:'none',onUpdate:()=>draw(driver.t)},0);
+ window.__draw=draw;
+ window.__ready=Promise.all(P.map(p=>new Promise((resolve,reject)=>{const im=new Image();images[p.id]=im;im.onload=resolve;im.onerror=()=>reject(new Error('Missing product '+p.id));im.src=p.local;}))).then(()=>document.fonts.ready).then(()=>{draw(driver.t);return true;});
+ draw(0);
+})();
+</script></body></html>
\ No newline at end of file
diff --git a/series2/episodes/07-sample-before-specifying.html b/series2/episodes/07-sample-before-specifying.html
new file mode 100644
index 0000000..b693d83
--- /dev/null
+++ b/series2/episodes/07-sample-before-specifying.html
@@ -0,0 +1,215 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=1080,height=1920"><title>Wallo presents — The value of a real sample</title><style>@font-face{font-family:Georgia;src:url('../assets/georgia.ttf')}@font-face{font-family:Verdana;src:url('../assets/verdana.ttf')}html,body{margin:0;width:1080px;height:1920px;overflow:hidden}#root{position:relative;width:1080px;height:1920px;overflow:hidden}.clip{position:absolute;inset:0;width:1080px;height:1920px}canvas{width:1080px;height:1920px}.preload{position:absolute;opacity:0;pointer-events:none}</style><script src="../assets/gsap.min.js"></script></head><body><div id="root" data-composition-id="main" data-start="0" data-duration="20" data-width="1080" data-height="1920" data-fps="30"><section id="material-scene" class="clip" data-start="0" data-duration="20" data-track-index="0"><canvas id="stage" width="1080" height="1920" aria-label="Wallo presenting The value of a real sample"></canvas><span class="preload" style="font-family:Georgia">Wallo</span><span class="preload" style="font-family:Verdana">Designer Wallcoverings</span></section><audio id="narrated-mix" src="../assets/mix-3.wav" data-start="0" data-duration="20" data-track-index="1"></audio></div><script>window.EPISODE={"slug":"07-sample-before-specifying","title":"See it. Feel it.\nThen specify it.","shortTitle":"The value of a real sample","kicker":"04 / SAMPLING","product":"topiary","note":"Topiary View / Thyme · Sanderson. Illustration not to scale.","takeaway":"A sample makes the decision real.","lines":["A screen helps you shortlist. A physical sample helps you decide.","Hold it beside your flooring, your fabrics, and your paint.","Check the color in daylight and under the room's lighting.","Confirm the pattern scale and repeat before planning the installation."],"captions":["Shortlist online. Decide with a sample.","Compare it with the room's other finishes.","Check daylight and the room's lighting.","Confirm scale and repeat."],"index":3,"audio":{"slug":"07-sample-before-specifying","provider":"Kokoro local","voice":"am_michael","duration":20,"segments":[{"index":0,"start":0.6,"end":4.827,"text":"A screen helps you shortlist. A physical sample helps you decide.","caption":"Shortlist online. Decide with a sample.","duration":4.227},{"index":1,"start":5.1,"end":8.385,"text":"Hold it beside your flooring, your fabrics, and your paint.","caption":"Compare it with the room's other finishes.","duration":3.285},{"index":2,"start":9.6,"end":12.843,"text":"Check the color in daylight and under the room's lighting.","caption":"Check daylight and the room's lighting.","duration":3.243},{"index":3,"start":14.1,"end":18.175,"text":"Confirm the pattern scale and repeat before planning the installation.","caption":"Confirm scale and repeat.","duration":4.075}],"energy":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0014,0.0022,0.0015,0.0019,0.0025,0.5463,0.818,0.6078,0.5899,0.7306,0.7678,0.8636,0.7011,0.0936,0.0101,0.0573,0.0392,0.3234,0.6009,0.7328,0.849,0.916,0.9793,0.9384,0.895,0.8176,0.774,0.684,0.664,0.7179,0.6157,0.5665,0.5571,0.5117,0.3691,0.4505,0.5943,0.6677,0.6378,0.6586,0.6223,0.1551,0.0331,0.1041,0.1534,0.216,0.1506,0.0506,0.2725,0.3231,0.3622,0.3483,0.2679,0.2413,0.3407,0.3765,0.2875,0.2776,0.3049,0.2043,0.4944,0.8111,0.9393,1,1,1,0.7967,0.5085,0.2364,0.0494,0.0467,0.107,0.1432,0.1971,0.2595,0.1987,0.1264,0.1116,0.0626,0.0295,0.0467,0.1025,0.1438,0.2095,0.0784,0.0012,0.0234,0.0149,0.0022,0.0017,0.0018,0.0022,0.0027,0.003,0.0031,0.0039,0.0045,0.0054,0.0066,0.0083,0.0076,0.0097,0.0097,0.0088,0.0069,0.0161,1,1,0.3486,0.2444,0.1537,0.1303,0.1318,0.1237,0.9411,1,1,0.7285,0.3441,0.3915,0.8967,1,0.2822,0.0759,0.163,0.1926,0.7762,0.7846,0.5519,0.4852,0.2137,0.3372,0.3563,0.3329,0.2836,0.2421,0.2078,0.4724,0.8575,0.7684,0.6772,0.738,0.8087,0.7964,0.6664,0.4457,0.0994,0.0105,0.2872,0.5099,0.4037,0.4325,0.436,0.3565,0.307,0.222,0.2794,0.4267,0.5291,0.6441,0.5988,0.6754,0.4052,0.1067,0.019,0.1094,0.1162,0.1778,0.1603,0.0534,0.2256,0.3423,0.378,0.368,0.2237,0.1546,0.2687,0.4783,0.5406,0.3863,0.2849,0.3162,0.2678,0.2547,0.2036,0.188,0.0801,0.7898,0.7792,0.7591,0.7786,0.8492,0.986,0.8794,0.5905,0.394,0.2057,0.1336,0.0867,0.0812,0.0435,0.0503,0.0213,0.0375,0.0418,0.0266,0.0122,0.0032,0.0015,0.0012,0.0013,0.0007,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0011,0.0024,0.0024,0.0029,0.0032,0.0233,0.0672,0.1971,1,1,1,1,1,1,0.5342,0.2458,0.5829,1,0.8136,0.1556,0.1234,0.108,0.1331,0.5486,0.6113,0.6192,0.8265,0.7506,0.6058,0.5084,0.5969,0.9348,0.6343,0.681,0.6075,0.5898,0.5346,0.6018,0.3019,0.219,0.2324,0.3543,0.4669,0.4539,0.2364,0.1705,0.1722,0.157,0.1515,0.1337,0.0897,0.2237,0.3781,0.5626,1,1,1,1,0.8846,0.9308,0.8059,0.736,0.5555,0.3743,0.2852,0.2415,0.1844,0.1308,0.1178,0.0921,0.0904,0.0488,0.0495,0.0071,0.0034,0.005,0.0061,0.007,0.0081,0.0074,0.0073,0.0065,0.0043,0.0029,0.006,0.0059,0.1962,0.3423,0.7129,0.9033,0.5194,0.3005,0.2627,0.2043,0.164,0.1461,0.1102,0.1117,1,1,0.8362,0.8382,0.9066,0.9393,1,1,0.3556,0.2235,0.2036,0.3877,0.5487,0.5743,0.4802,0.4249,0.1988,0.045,0.0909,0.2236,0.5214,0.5086,0.345,0.1509,0.2757,0.2858,0.39,0.5102,0.5927,0.6042,0.5317,0.3825,0.4023,0.4515,0.4744,0.4158,0.148,0.0327,0.0045,0.006,0.2076,0.2237,0.1889,0.157,0.1701,0.5779,0.6864,0.7251,0.6215,0.5967,0.5397,0.3584,0.2205,0.1176,0.089,0.0489,0.0432,0.0144,0.0044,0.0036,0.0138,0.0465,0.0129,0.0037,0.0015,0.0012,0.0006,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0006,0.0481,0.2661,0.915,0.9709,0.7658,0.9489,1,1,0.4281,0.0399,0.0049,0.0238,0.0057,0.0249,0.0411,0.1567,0.7198,0.2802,0.0409,0.004,0.0022,0.2995,0.2947,0.2571,0.2997,0.3103,0.9399,1,1,0.8337,0.4778,0.4509,0.6073,0.497,0.552,0.496,0.4268,0.2843,0.1659,0.1826,0.2784,0.3495,0.4418,0.4967,0.4801,0.4348,0.3049,0.2041,0.1125,0.2589,0.7607,0.8046,0.5964,0.7033,0.6053,0.6243,0.6188,0.6099,0.3736,0.3748,0.3877,0.3881,0.3444,0.2618,0.2675,0.1921,0.1758,0.1439,0.12,0.0958,0.0603,0.0183,0.003,0.005,0.0021,0.0026,0.0035,0.0044,0.0051,0.0049,0.0049,0.0051,0.0057,0.0081,0.0057,0.0066,0.004,0.027,0.3331,0.5356,0.5952,0.6618,0.659,0.7863,0.7004,0.6776,0.4943,0.5434,0.3612,0.1945,0.3587,0.6035,0.5203,0.2418,0.139,0.1193,0.2507,0.5235,0.3342,0.2333,0.1963,0.1789,0.1722,0.1857,0.2926,0.4433,0.6064,0.7354,0.6695,0.6462,0.5722,0.5254,0.4503,0.4232,0.4576,0.4438,0.4135,0.3034,0.2666,0.4576,0.4512,0.1105,0.119,0.3009,0.366,0.4365,0.4852,0.5092,0.4508,0.5748,0.5667,0.6741,0.6098,0.5233,0.4997,0.3949,0.2821,0.1209,0.0668,0.128,0.0937,0.0715,0.0288,0.0287,0.0278,0.0213,0.0075,0.0048,0.0024,0.0013,0.0009,0.0003,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0009,0.1437,0.1386,0.7233,0.8509,0.6767,0.5781,0.4463,0.2158,0.1849,0.1824,0.1966,0.1373,0.2526,1,1,1,1,1,1,0.705,0.7533,0.7073,0.596,0.3683,0.5485,0.75,0.1713,0.0382,0.01,0.2151,0.3748,0.4666,0.4397,0.6123,0.8094,0.947,0.8063,0.711,0.5648,0.1915,0.3336,0.4299,0.4436,0.483,0.4823,0.5406,0.528,0.3176,0.3453,0.6133,0.6139,0.6634,0.5212,0.1017,0.009,0.0024,0.1311,0.6499,0.8821,0.7096,0.674,0.6727,0.6453,0.637,0.5834,0.5171,0.4904,0.416,0.3423,0.3169,0.2792,0.299,0.2996,0.3667,0.3815,0.4323,0.4302,0.4229,0.4021,0.3568,0.4291,0.4905,0.4763,0.245,0.0594,0.0087,0.0033,0.005,0.2758,0.1012,0.0695,0.0547,0.3631,0.4844,0.4434,0.3566,0.3081,0.2052,0.1365,0.0827,0.047,0.0129,0.0049,0.0105,0.0033,0.002,0.0027,0.0032,0.0042,0.0046,0.0052,0.006,0.0069,0.0071,0.0076,0.0076,0.0053,0.0069,0.0098,0.0036,0.0015,0.0949,0.8455,0.8829,0.2737,0.2589,0.2624,0.1869,0.4589,1,0.9303,0.9021,0.1779,0.0526,0.006,0.0053,0.2445,0.1393,0.0847,0.0551,0.2522,0.6826,0.8463,0.731,0.7892,0.8757,0.9053,0.9774,0.8982,0.7934,0.6905,0.6659,0.5732,0.5046,0.4559,0.4698,0.4255,0.2772,0.3274,0.3964,0.3661,0.4327,0.4954,0.5044,0.5192,0.4659,0.3514,0.2462,0.648,0.5665,0.1295,0.0048,0.086,0.2478,0.4937,0.2934,0.3336,0.3388,0.5165,0.4373,0.4446,0.5044,0.4648,0.4507,0.3938,0.2174,0.5216,0.7116,0.5028,0.628,0.3144,0.0906,0.1781,0.0994,0.0563,0.0261,0.0294,0.0073,0.0183,0.0052,0.0029,0.0012,0.0012,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"peak":0.6511131525039673}};window.PRODUCTS=[{"id":"fairborn","title":"Fairborn Type 2 | Phillipe Romano","label":"Fairborn Type 2 · Phillipe Romano","url":"https://www.designerwallcoverings.com/products/prw-920735-rif-003","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/prw-920735-rif-003.jpg?v=1781716604","file":"assets/products/fairborn.jpg","description":"<p>When light rakes across the embossed surface, it reveals an organic grid-like pattern of broken lines. Although extremely durable — as you’d expect from a Type II vinyl wallcovering named for rock fissures — offering a soft, nature-inspired palette of creams, taupes, blues and browns. A small scale and intricate pattern appear almost seamless, and its 12 two-tone colorways resemble artisanal materials like mosaic</p><br><br>A captivating wallcovering featuring intricate detailing and refined textures, offering a sophisticated backdrop that elevates any interior space. The design evokes timeless elegance with a modern touch.  \n#luxuryinteriors #modernwallcoverings #elegantdesign\n","retrieved_at":"2026-09-05","local":"../assets/products/fairborn.jpg"},{"id":"waterperry","title":"Waterperry Ivory/Stone | Sanderson","label":"Waterperry / Ivory–Stone · Sanderson","url":"https://www.designerwallcoverings.com/products/waterperry-ivory-stone-sanderson","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DWAP216280_5340.jpg?v=1788582264","file":"assets/products/waterperry.jpg","description":"<div class=\"sanderson-product\">\n<p>Waterperry in Ivory/Stone is a Sanderson Wallcovering in a botanical/leaves and trees design. Part of the Sanderson heritage collection, this pattern reads in a palette of Alabaster, Linen, Mist. Supplied by the roll; a memo sample is available. An English design-house classic from Sanderson Design Group.</p>\n</div>","retrieved_at":"2026-09-05","local":"../assets/products/waterperry.jpg"},{"id":"wheaton","title":"Wheaton Type 2 | Phillipe Romano","label":"Wheaton Type 2 · Phillipe Romano","url":"https://www.designerwallcoverings.com/products/prw-920166-coa-004-1","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/d4639abbf9baa5237707.jpg?v=1785438111","file":"assets/products/wheaton.jpg","description":"<p>Weaving shimmering strands and beguiling texture, emulating the intricacy of our favored grasscloth Tide in the form of a durable Type II vinyl</p>\n","retrieved_at":"2026-09-05","local":"../assets/products/wheaton.jpg"},{"id":"topiary","title":"Topiary View Thyme | Sanderson","label":"Topiary View / Thyme · Sanderson","url":"https://www.designerwallcoverings.com/products/topiary-view-thyme-sanderson","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DHIP217511_f500.jpg?v=1788582179","file":"assets/products/topiary.jpg","description":"<div class=\"sanderson-product\">\n<p>Topiary View in Thyme is a Sanderson Wallcovering in a landscapes design. Part of the Sanderson heritage collection, this pattern reads in a palette of Alabaster, Stone, Oatmeal. Supplied by the roll; a memo sample is available. An English design-house classic from Sanderson Design Group.</p>\n</div>","retrieved_at":"2026-09-05","local":"../assets/products/topiary.jpg"},{"id":"kaya","title":"Kaya - Cream Weave Wallcovering | Malibu Wallcovering","label":"Kaya / Cream Weave · Malibu","url":"https://www.designerwallcoverings.com/products/kaya-cream-weave-wallcovering-malibu-wallpaper","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/0086435_kaya-cream-weave_650.jpg?v=1776906632","file":"assets/products/kaya.jpg","description":"<p>Elevate your space with the luxurious Kaya Cream Weave wallcovering. This exquisite nonwoven vinyl design features a warm neutral color palette and stunning texture, perfect for creating an elegant ambiance.</p>","retrieved_at":"2026-09-05","local":"../assets/products/kaya.jpg"}];/* Wallo Material Notes: deterministic articulated character acting and real DW samples. */
+(() => {
+ 'use strict';
+ const canvas=document.getElementById('stage'), c=canvas.getContext('2d');
+ const E=window.EPISODE, P=window.PRODUCTS, ep=E.index;
+ const ink='#243a3b', chalk='#eeeae2', teal='#284f4b', clay='#a9634d', brass='#b7924f';
+ const images={}; let telemetry=[];
+ const clamp=(x,a=0,b=1)=>Math.max(a,Math.min(b,x));
+ const mix=(a,b,p)=>a+(b-a)*p;
+ const smooth=p=>{p=clamp(p);return p*p*(3-2*p);};
+ const ease=(t,a,b)=>smooth((t-a)/(b-a));
+ const pulse=(t,a,b,c,d)=>ease(t,a,b)*(1-ease(t,c,d));
+ const point=(x,y)=>({x,y});
+ const dist=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y);
+ function group(x,y,r,s,fn){c.save();c.translate(x,y);c.rotate(r);c.scale(s,s);fn();c.restore();}
+ function path(fn,fill,stroke=null,w=2){c.beginPath();fn(c);if(fill){c.fillStyle=fill;c.fill();}if(stroke){c.lineWidth=w;c.strokeStyle=stroke;c.lineJoin='round';c.lineCap='round';c.stroke();}}
+ function line(x,y,u,v,col,w=2){path(q=>{q.moveTo(x,y);q.lineTo(u,v);},null,col,w);}
+ function rect(x,y,w,h,col){c.fillStyle=col;c.fillRect(x,y,w,h);}
+ function oval(x,y,rx,ry,col,stroke=null,w=2){path(q=>q.ellipse(x,y,rx,ry,0,0,Math.PI*2),col,stroke,w);}
+ function rr(x,y,w,h,r,col,stroke=null,lw=2){path(q=>q.roundRect(x,y,w,h,r),col,stroke,lw);}
+ function text(s,x,y,size=28,color=ink,font='Verdana',align='left'){c.font=`${size}px '${font}'`;c.textAlign=align;c.textBaseline='alphabetic';c.fillStyle=color;c.fillText(s,x,y);}
+ function wrap(s,x,y,max,size=36,color=ink,font='Verdana',lh=size*1.4){
+   c.font=`${size}px '${font}'`;let row='',lines=[];
+   for(const word of s.split(' ')){const test=row?row+' '+word:word;if(c.measureText(test).width>max&&row){lines.push(row);row=word;}else row=test;}
+   if(row)lines.push(row);lines.forEach((v,i)=>text(v,x,y+i*lh,size,color,font));return lines.length;
+ }
+ function imageCover(id,x,y,w,h){const im=images[id];if(!im||!im.complete)return;const k=Math.max(w/im.width,h/im.height);c.save();c.beginPath();c.rect(x,y,w,h);c.clip();c.drawImage(im,x+(w-im.width*k)/2,y+(h-im.height*k)/2,im.width*k,im.height*k);c.restore();}
+ function sample(id,x,y,w=126,h=164,r=0){group(x,y,r,1,()=>{rr(-w/2+7,-h/2+9,w,h,2,'#203a3a20');rect(-w/2-5,-h/2-5,w+10,h+10,'#f9f7f0');imageCover(id,-w/2,-h/2,w,h);line(-w/2,-h/2,w/2,-h/2,'#ffffff',2);});}
+ function bone(a,b,width,col,shade){
+   const angle=Math.atan2(b.y-a.y,b.x-a.x),d=dist(a,b);
+   group(a.x,a.y,angle,1,()=>{rr(-width*.18,-width/2,d+width*.36,width,width*.46,col);path(q=>{q.moveTo(5,width*.26);q.quadraticCurveTo(d*.5,width*.34,d-3,width*.22);},null,shade||'#00000018',2.3);});
+ }
+ function solveIK(root,target,l1,l2,pole){
+   const dx=target.x-root.x,dy=target.y-root.y,raw=Math.hypot(dx,dy),d=clamp(raw,Math.abs(l1-l2)+.01,l1+l2-.01);
+   const nx=raw?dx/raw:0,ny=raw?dy/raw:1,a=(l1*l1-l2*l2+d*d)/(2*d),h=Math.sqrt(Math.max(0,l1*l1-a*a));
+   const end=point(root.x+nx*d,root.y+ny*d),joint=point(root.x+nx*a-ny*h*pole,root.y+ny*a+nx*h*pole);
+   return {root,joint,end,l1,l2};
+ }
+ function rotatePoint(p,angle,origin=point(0,-258)){const x=p.x-origin.x,y=p.y-origin.y;return point(origin.x+x*Math.cos(angle)-y*Math.sin(angle),origin.y+x*Math.sin(angle)+y*Math.cos(angle));}
+ function key(t,frames){if(t<=frames[0][0])return frames[0].slice(1);for(let i=1;i<frames.length;i++)if(t<=frames[i][0]){const p=ease(t,frames[i-1][0],frames[i][0]);return frames[i].slice(1).map((v,j)=>mix(frames[i-1][j+1],v,p));}return frames.at(-1).slice(1);}
+ function gait(t,start,end,x0,x1){
+   const p=clamp((t-start)/(end-start));
+   const lift=(a,b)=>p>a&&p<b?Math.sin(Math.PI*(p-a)/(b-a))*44:0;
+   const left=mix(x0-30,(x0+x1)/2-30,smooth((p-.08)/.28))+mix(0,(x1-x0)/2,smooth((p-.65)/.3));
+   const right=mix(x0+30,x1+30,smooth((p-.35)/.3));
+   return {x:(left+right)/2,feet:[point(left,-lift(.08,.36)-lift(.65,.95)),point(right,-lift(.35,.65))],walking:p>0&&p<1,phase:p};
+ }
+ function voiceEnergy(t){const env=E.audio?.energy||[];return env[Math.min(env.length-1,Math.max(0,Math.floor(t*50)))]||0;}
+ function hand(x,y,r,pose='open',skin='#c89472',prop=null){
+   group(x,y,r,1,()=>{
+    if(prop==='sample')sample(E.product,8,-18,115,148,-.04);
+    if(prop==='fan')for(let k=0;k<3;k++)sample(['waterperry','topiary','kaya'][k],k*17-15,-43,91,130,(k-1)*.18);
+    if(prop==='report'){rr(-12,-134,135,176,4,'#f8f5e9',ink,2);rect(1,-117,108,5,teal);text('TEST',4,-87,22,ink);text('REPORT',4,-58,22,ink);for(let k=0;k<3;k++)line(4,-35+k*18,97,-35+k*18,'#a4aaa0',2);}
+    if(prop==='cloth'){path(q=>{q.moveTo(-28,-15);q.lineTo(30,-26);q.lineTo(43,35);q.lineTo(-21,44);q.closePath();},'#c8c5b5',ink,1);line(-17,-4,28,25,'#efeadb',3);}
+    if(prop==='roller'){line(0,-8,0,-72,ink,7);line(0,-72,37,-72,ink,7);line(37,-72,37,-108,ink,5);rr(-27,-132,127,38,11,'#8b9c8d',ink,2);rr(-9,-9,18,54,7,brass);}
+    if(prop==='tablet'){rr(-73,-76,145,170,8,ink);rr(-65,-67,129,151,4,'#f4f0e5');text('DW',-45,-33,27,teal,'Georgia');for(let i=0;i<3;i++)imageCover(['waterperry','fairborn','kaya'][i],-47+i*32,-6,27,60);}
+    if(pose==='point'){bone(point(-4,3),point(34,-18),13,skin);bone(point(0,9),point(16,15),21,skin);oval(-6,9,13,18,skin);}
+    else{rr(-14,-9,29,42,12,skin);oval(-15,5,7,13,skin);for(let i=0;i<3;i++)line(-7+i*6,14,-7+i*6,28,'#9e685446',1);}
+   });
+ }
+ function head(kind,x,y,t,look,mouth,tilt){
+   const male=kind==='wallo',skin=male?'#c89472':'#d5a185',dark=male?'#714f3b':'#834e39';
+   group(x,y,tilt,.82,()=>{
+    if(!male){oval(-4,-10,51,70,dark);oval(-39,-50,28,28,dark);path(q=>{q.moveTo(-57,-55);q.quadraticCurveTo(-81,-37,-59,-16);},null,'#a86b4b',7);}
+    // Tapered jaw, defined brow and modest nose maintain adult proportions.
+    path(q=>{q.moveTo(-41,-40);q.bezierCurveTo(-43,-77,43,-80,44,-34);q.lineTo(40,31);q.quadraticCurveTo(31,66,5,72);q.quadraticCurveTo(-26,65,-36,34);q.closePath();},skin);
+    path(q=>{q.moveTo(-38,-31);q.lineTo(-32,34);q.quadraticCurveTo(-24,52,-8,57);q.lineTo(-20,21);q.lineTo(-18,-46);q.closePath();},male?'#b58162':'#c39076');
+    oval(-40,3,9,16,skin);oval(42,2,8,15,skin);
+    if(male){path(q=>{q.moveTo(-39,-39);q.lineTo(-33,-18);q.lineTo(-30,-54);q.lineTo(31,-54);q.lineTo(43,-25);q.lineTo(42,-55);q.closePath();},dark);}
+    else{path(q=>{q.moveTo(-40,-27);q.quadraticCurveTo(-24,-63,2,-56);q.quadraticCurveTo(38,-37,43,-19);q.lineTo(38,-58);q.quadraticCurveTo(-4,-89,-37,-55);q.closePath();},dark);for(let i=0;i<4;i++)path(q=>{q.moveTo(-29+i*17,-56);q.quadraticCurveTo(-26+i*17,-43,-13+i*17,-39);},null,'#ae7150',2);}
+    const blink=(t+(male?.31:1.93))%4.4<.14;
+    for(const side of [-1,1]){const ex=side*18+look*3,ey=-8;path(q=>{q.moveTo(ex-10,ey);q.quadraticCurveTo(ex,ey-5,ex+10,ey);},null,ink,2.2);if(!blink){oval(ex+look*3,ey+1,3.5,4.3,ink);oval(ex+look*3+1,ey-.3,1.1,1.2,'#eee8dc');}line(ex-10,ey-15,ex+8,ey-17+(side===1?look*2:0),dark,2.5);}
+    path(q=>{q.moveTo(3+look*5,-5);q.lineTo(8+look*8,15);q.quadraticCurveTo(4+look*5,20,-1+look*5,17);},null,'#98644f',1.8);
+    if(male){path(q=>{q.moveTo(-20,29);q.quadraticCurveTo(-6,20,4,27);q.quadraticCurveTo(15,23,26,30);q.quadraticCurveTo(17,39,3,34);q.quadraticCurveTo(-9,37,-20,29);},dark);}
+    if(mouth>.12){oval(4,44,10+mouth*3,2+mouth*6,'#714735');if(mouth>.4)line(-2,41,11,41,'#efe6d7',2);}
+    else path(q=>{q.moveTo(-7,43);q.quadraticCurveTo(5,48,17,42);},null,'#714735',1.8);
+    if(male){path(q=>{q.moveTo(-49,-53);q.quadraticCurveTo(-48,-98,10,-95);q.quadraticCurveTo(47,-89,48,-53);q.closePath();},brass);path(q=>{q.moveTo(-49,-53);q.quadraticCurveTo(12,-64,72,-48);q.quadraticCurveTo(77,-35,39,-41);q.lineTo(-49,-43);q.closePath();},'#97773f');line(-27,-73,12,-84,'#d2b270',3);}
+    else oval(-39,14,3.5,5.5,brass);
+   });
+ }
+ function character(kind,t,settings={}){
+   const male=kind==='wallo',skin=male?'#c89472':'#d5a185',shirt=male?'#d6d0bc':clay,cloth=male?teal:'#c8c1af';
+   const walk=settings.walk||gait(t,0,1,settings.x,settings.x);let x=walk.x,y=settings.y||882;
+   const crouch=settings.crouch||0,lean=settings.lean||0,breath=Math.sin(t*2.1+(male?0:1.2))*1.7;
+   const hipY=-275+crouch,origin=point(0,hipY);
+   const bodyPoint=(u,v)=>rotatePoint(point(u,v+crouch+breath),lean,origin);
+   const leftShoulder=bodyPoint(-62,-469),rightShoulder=bodyPoint(62,-469);
+   const arms=[solveIK(leftShoulder,point(...settings.left),118,111,-1),solveIK(rightShoulder,point(...settings.right),118,111,1)];
+   const legs=walk.feet.map((foot,i)=>solveIK(point(i===0?-32:32,hipY),point(foot.x-x,foot.y-13),134,136,i===0?1:-1));
+   const mouth=male?voiceEnergy(t)*.88:0;
+   const joints={kind,x,y,arms,legs,lean,crouch,feet:walk.feet,walking:walk.walking,props:settings.props||[]};
+   telemetry.push(joints);
+   oval(x,y+6,94-crouch*.1,15,'#233a3b15');
+   group(x,y,0,settings.scale||1,()=>{
+    // Back arm and far leg precede torso so elbow overlaps read spatially.
+    const armDraw=(i)=>{const b=arms[i];bone(b.root,b.joint,39,shirt);bone(b.joint,b.end,27,skin);oval(b.joint.x,b.joint.y,16,16,skin);const sleeve=point(mix(b.root.x,b.joint.x,.77),mix(b.root.y,b.joint.y,.77));bone(b.root,sleeve,43,shirt);line(sleeve.x-8,sleeve.y,sleeve.x+9,sleeve.y,'#ffffff30',2);};
+    armDraw(0);
+    legs.forEach((b,i)=>{bone(b.root,b.joint,58,cloth);bone(b.joint,b.end,43,cloth);oval(b.joint.x,b.joint.y,23,23,cloth);line(b.root.x-8,b.root.y+30,b.joint.x-6,b.joint.y-16,male?'#41635b':'#dfd8c8',2);group(b.end.x,b.end.y+10,walk.walking&&walk.feet[i].y<0?-.12:0,1,()=>{path(q=>{q.moveTo(-24,-20);q.lineTo(20,-20);q.lineTo(27,-9);q.quadraticCurveTo(60,-5,56,14);q.lineTo(-24,14);q.closePath();},male?'#655545':'#626b62');line(-24,14,55,14,'#34413d',5);line(4,-8,25,-7,'#c6b69b',2);});});
+    group(0,hipY,lean,1,()=>{
+     const dy=crouch+breath-hipY;
+     path(q=>{q.moveTo(-63,-478+dy);q.quadraticCurveTo(-82,-460+dy,-71,-417+dy);q.lineTo(-60,-266+dy);q.quadraticCurveTo(0,-235+dy,60,-266+dy);q.lineTo(71,-417+dy);q.quadraticCurveTo(79,-460+dy,58,-479+dy);q.closePath();},shirt);
+     // Structured overall/apron silhouette, stitched seams, pockets and cloth folds.
+     path(q=>{q.moveTo(-50,-425+dy);q.lineTo(-46,-350+dy);q.lineTo(-61,-290+dy);q.lineTo(-60,-253+dy);q.lineTo(61,-253+dy);q.lineTo(61,-290+dy);q.lineTo(46,-350+dy);q.lineTo(48,-425+dy);q.closePath();},cloth);
+     for(const s of [-1,1]){line(s*44,-472+dy,s*38,-405+dy,cloth,17);oval(s*39,-414+dy,4.8,4.8,brass);line(s*49,-328+dy,s*41,-312+dy,male?'#59756b':'#a9a18f',2);}
+     rr(-27,-401+dy,54,56,4,male?'#254540':'#bab39f');path(q=>{q.moveTo(-23,-388+dy);q.quadraticCurveTo(0,-376+dy,24,-388+dy);},null,male?'#6a8778':'#e5dfd0',1.5);
+     line(-50,-266+dy,52,-266+dy,male?'#1e403b':'#aca590',3);
+     if(male){rr(-66,-321+dy,20,67,3,'#8d7452');line(-56,-342+dy,-56,-282+dy,brass,6);}
+     path(q=>{q.moveTo(-17,-509+dy);q.lineTo(-18,-474+dy);q.quadraticCurveTo(0,-454+dy,21,-474+dy);q.lineTo(18,-509+dy);q.closePath();},skin);
+    });
+    const hp=bodyPoint(0,-559);head(kind,hp.x,hp.y,t,settings.look||0,mouth,(settings.headTilt||0)+lean*.28);
+    armDraw(1);
+    arms.forEach((b,i)=>hand(b.end.x,b.end.y,(settings.wrists||[0,0])[i],(settings.gestures||['open','open'])[i],skin,(settings.props||[])[i]));
+   });
+   return joints;
+ }
+ function room(t){
+   rect(-350,-300,1800,1200,'#dfdbd1');rect(-350,765,1800,450,'#c9c2b1');
+   rect(20,0,240,706,'#e9e7dc');for(let i=0;i<4;i++)line(22+i*59,0,22+i*59,704,'#f7f4e9',7);
+   rect(318,26,647,727,'#c7bfae');rect(329,37,624,704,'#efede3');
+   if(ep===1){const a=ease(t,5.1,8);rect(343,49,596,678,'#aab7a4');c.save();c.beginPath();c.rect(343,49,596*a,678);c.clip();imageCover(E.product,343,49,596,678);c.restore();}
+   else imageCover(E.product,343,49,596,678);
+   line(340,743,943,743,'#faf7eb',4);line(-350,765,1450,765,'#a79f8d',2);
+   for(let i=0;i<6;i++)line(-200,814+i*54,1400,814+i*54,'#b6ad9a',1.4);
+   line(91,765,-170,1220,'#b6ad9a',1.4);line(880,765,1130,1220,'#b6ad9a',1.4);
+   // Light and shadow move gently with the camera, not with an arbitrary pulse.
+   path(q=>{q.moveTo(40,706);q.lineTo(263,706);q.lineTo(672,1120);q.lineTo(412,1120);q.closePath();},'#fcf7df28');
+   // A sample ledge ties the real products to the working material library.
+   rect(404,685,479,12,'#b29d7a');for(let i=0;i<3;i++)sample(P[(i+ep)%P.length].id,468+i*115,622,88,116,(i-1)*.045);
+ }
+ function documentPanel(t){
+   if(ep!==0)return;
+   const a=pulse(t,4.2,4.9,14.3,15.0);if(!a)return;
+   group(640,177+(1-a)*28,0,.88,()=>{
+    rr(-239,-168,478,336,2,'#f7f4e9',ink,1.5);text('BEFORE YOU SPECIFY',-210,-126,24,teal);
+    const labels=['PRODUCT / TEST REPORT','CLASSIFICATION','SUBSTRATE + ADHESIVE'];
+    labels.forEach((label,i)=>{const done=t>5.0+i*2.8;line(-209,-61+i*77,-182,-61+i*77,done?brass:'#bac0b5',3);text(label,-163,-52+i*77,21,ink);});
+   });
+ }
+ function swatchBoard(t){
+   if(ep!==3&&ep!==4)return;
+   const a=pulse(t,6.4,7.1,13.4,14);if(!a)return;
+   group(560,275+(1-a)*25,0,.95,()=>{
+    rr(-199,-139,398,278,2,'#f5f0e4',ink,1);sample(E.product,-83,-1,155,220,0);
+    rect(28,-111,137,83,'#c6bea9');rect(28,-16,137,69,'#8c9985');rect(28,64,137,43,'#b89672');
+    text(ep===3?'COMPARE YOUR FINISHES':'YOUR PROJECT SHORTLIST',-192,-162,23,ink);
+   });
+ }
+ function actionNotes(t){
+   if(ep!==2)return;const a=pulse(t,8,8.5,14.2,14.8);if(!a)return;
+   ['ABRASION','CLEANABILITY','CARE'].forEach((s,i)=>{const show=ease(t,8+i*.7,8.4+i*.7);if(show){rect(440,120+i*77,440,58,'#f4f0e6');text(s,468,159+i*77,27,ink);}});
+ }
+ function performances(t){
+   let wwalk=gait(t,.2,2.9,126,253),pwalk=gait(t,.5,3.1,949,801);
+   const w={walk:wwalk,left:[-85,-279],right:[149,-396],wrists:[-.1,-.36],props:[],gestures:['open','open'],look:.6,lean:Math.sin(t*.8)*.012};
+   const p={walk:pwalk,left:[-130,-402],right:[86,-275],wrists:[.14,.08],props:['sample'],look:-.6,lean:Math.sin(t*.73+1)*.013};
+   // Presenter gestures are deliberately different for each spoken clause.
+   const gesture=key(t,[[0,100,-285],[2.8,145,-411],[4.4,165,-440],[5.2,120,-367],[8.7,161,-441],[9.6,149,-378],[12.8,180,-432],[14,125,-360],[17,152,-419],[19.8,126,-383]]);
+   w.right=gesture;w.wrists[1]=key(t,[[0,0],[3,-.5],[5.2,-.15],[8,-.42],[11,.1],[14,-.4],[18,-.2]])[0];
+   w.gestures[1]=t>4.5&&t<8.8?'point':'open';w.headTilt=Math.sin(t*.65)*.022;w.look=t>4&&t<9?1:.1;
+   if(t<3){w.right=[90-Math.sin(t*4)*40,-277];w.left=[-90+Math.sin(t*4)*40,-277];p.left=[-90+Math.sin(t*4)*24,-280];p.props=[];}
+   if(ep===0){p.props=['report'];p.left=key(t,[[0,-85,-280],[3,-130,-365],[6,-131,-420],[10,-153,-445],[14,-125,-384],[19,-108,-376]]);p.wrists[0]=-.14;p.right=key(t,[[0,90,-277],[8,98,-320],[11,-10,-377],[14,93,-278]]);}
+   if(ep===1){
+    p.props=t<8?['roller']:['sample'];p.left=key(t,[[0,-88,-283],[3,-129,-447],[4.5,-142,-495],[5.2,-143,-411],[6,-146,-495],[7,-145,-397],[8,-100,-367],[11,-139,-436],[15,-103,-361]]);p.wrists[0]=.07;
+    if(t>9.3){p.walk=gait(t,9.3,11.4,801,870);p.look=-1;p.lean=-.06*ease(t,10,11);}
+    w.props=['sample'];w.left=key(t,[[0,-87,-282],[4,-98,-291],[6,-39,-389],[8,-30,-433],[12,-59,-386],[17,-78,-320]]);w.wrists[0]=-.08;
+   }
+   if(ep===2){
+    p.crouch=pulse(t,4,5.6,8.8,10.2)*113;p.lean=-pulse(t,4,5.4,9,10.3)*.10;
+    const wipe=pulse(t,5,5.8,8.2,9);p.props=['cloth'];p.left=[-147-wipe*17,-389+p.crouch+wipe*Math.sin(t*6)*42];p.wrists[0]=-.5;
+    p.right=[85,-284+p.crouch];p.look=-1;
+    w.props=['sample'];w.left=[-45,-371];w.wrists[0]=-.18;
+   }
+   if(ep===3){
+    p.props=['sample'];p.left=key(t,[[0,-92,-280],[3,-100,-366],[6,-138,-440],[10,-144,-478],[14,-126,-418],[18,-93,-340]]);p.wrists[0]=key(t,[[0,0],[3,-.08],[7,.22],[10,-.18],[13,.16],[17,0]])[0];
+    w.props=['fan'];w.left=key(t,[[0,-90,-280],[4,-33,-365],[7,-44,-400],[12,-24,-389],[16,-44,-346]]);w.wrists[0]=-.13;
+   }
+   if(ep===4){
+    w.props=['tablet'];w.left=[-43,-341];w.right=key(t,[[0,101,-280],[3,143,-413],[6,158,-444],[8.5,65,-361],[10,34,-340],[11.6,20,-356],[13,144,-411],[17,160,-444],[19,112,-374]]);w.gestures[1]=t>8&&t<12?'point':'open';
+    p.props=['fan'];p.left=[-129,-400];p.right=key(t,[[0,89,-280],[6,86,-280],[9,-18,-384],[12,76,-285],[17,100,-382],[19,86,-297]]);
+   }
+   // Camera-right actor is drawn first so the presenter remains the visual lead.
+   character('pippa',t,p);character('wallo',t,w);
+ }
+ function overlay(t){
+   rect(0,0,1080,437,chalk);rect(0,1437,1080,483,chalk);
+   text('DESIGNER WALLCOVERINGS',72,96,25,ink);text('WALLO PRESENTS',1008,96,21,teal,'Verdana','right');
+   line(72,124,1008,124,'#b9b6aa',1);text(E.kicker,72,178,24,teal);
+   E.title.split('\n').forEach((s,i)=>text(s,69,270+i*87,73,ink,'Georgia'));
+   text('MATERIAL NOTES',73,411,22,teal);text('WITH WALLO & PIPPA',1006,411,20,teal,'Verdana','right');
+   const product=P.find(p=>p.id===E.product);text(product.label,72,1481,25,ink);
+   line(72,1505,1008,1505,'#b9b6aa',1);
+   const segment=(E.audio?.segments||[]).filter(s=>t>=s.start).at(-1);
+   const caption=segment?E.captions[segment.index]:'A wallcovering presentation with Wallo.';
+   wrap(caption,72,1570,930,38,ink,'Verdana',55);
+   if(t>=16.8)text(E.takeaway,72,1738,37,teal,'Georgia');
+   wrap(E.note,72,1794,926,24,'#58635b','Verdana',34);
+   text('designerwallcoverings.com',72,1870,25,teal);
+   text(String(ep+1).padStart(2,'0')+' / 05',1008,1870,22,teal,'Verdana','right');
+   rect(72,1894,936,3,'#c9c7bb');rect(72,1894,936*clamp(t/20),3,teal);
+ }
+ function draw(t){
+   telemetry=[];rect(0,0,1080,1920,chalk);
+   c.save();c.beginPath();c.rect(0,437,1080,1000);c.clip();
+   const zoom=1+ease(t,3.3,5)*.08-ease(t,14.2,16)*.06;
+   c.translate(540,950);c.scale(zoom,zoom);c.translate(-540,-487);
+   room(t);documentPanel(t);swatchBoard(t);actionNotes(t);performances(t);
+   c.restore();overlay(t);window.__rigState=telemetry;
+ }
+ const driver={t:0};window.__timelines=window.__timelines||{};
+ window.__timelines.main=gsap.timeline({paused:true}).to(driver,{t:20,duration:20,ease:'none',onUpdate:()=>draw(driver.t)},0);
+ window.__draw=draw;
+ window.__ready=Promise.all(P.map(p=>new Promise((resolve,reject)=>{const im=new Image();images[p.id]=im;im.onload=resolve;im.onerror=()=>reject(new Error('Missing product '+p.id));im.src=p.local;}))).then(()=>document.fonts.ready).then(()=>{draw(driver.t);return true;});
+ draw(0);
+})();
+</script></body></html>
\ No newline at end of file
diff --git a/series2/episodes/08-service-at-dw.html b/series2/episodes/08-service-at-dw.html
new file mode 100644
index 0000000..6a66b9c
--- /dev/null
+++ b/series2/episodes/08-service-at-dw.html
@@ -0,0 +1,215 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=1080,height=1920"><title>Wallo presents — Service at Designer Wallcoverings</title><style>@font-face{font-family:Georgia;src:url('../assets/georgia.ttf')}@font-face{font-family:Verdana;src:url('../assets/verdana.ttf')}html,body{margin:0;width:1080px;height:1920px;overflow:hidden}#root{position:relative;width:1080px;height:1920px;overflow:hidden}.clip{position:absolute;inset:0;width:1080px;height:1920px}canvas{width:1080px;height:1920px}.preload{position:absolute;opacity:0;pointer-events:none}</style><script src="../assets/gsap.min.js"></script></head><body><div id="root" data-composition-id="main" data-start="0" data-duration="20" data-width="1080" data-height="1920" data-fps="30"><section id="material-scene" class="clip" data-start="0" data-duration="20" data-track-index="0"><canvas id="stage" width="1080" height="1920" aria-label="Wallo presenting Service at Designer Wallcoverings"></canvas><span class="preload" style="font-family:Georgia">Wallo</span><span class="preload" style="font-family:Verdana">Designer Wallcoverings</span></section><audio id="narrated-mix" src="../assets/mix-4.wav" data-start="0" data-duration="20" data-track-index="1"></audio></div><script>window.EPISODE={"slug":"08-service-at-dw","title":"A better finish\nstarts with service.","shortTitle":"Service at Designer Wallcoverings","kicker":"05 / SERVICE AT DW","product":"kaya","note":"Samples, product information and quotes at designerwallcoverings.com.","takeaway":"Bring your project to DW.","lines":["Bring your project to Designer Wallcoverings. Start with what you love.","Explore the collection, request samples, and ask for product information.","Share the selected pattern and project details when requesting a quote.","From first shortlist to your order, make each choice an informed one."],"captions":["Start with your project and your ideas.","Products. Samples. Information.","Request a quote with your project details.","Make the next choice an informed one."],"index":4,"audio":{"slug":"08-service-at-dw","provider":"Kokoro local","voice":"am_michael","duration":20,"segments":[{"index":0,"start":0.6,"end":4.825,"text":"Bring your project to Designer Wallcoverings. Start with what you love.","caption":"Start with your project and your ideas.","duration":4.225},{"index":1,"start":5.1,"end":9.333,"text":"Explore the collection, request samples, and ask for product information.","caption":"Products. Samples. Information.","duration":4.233},{"index":2,"start":9.6,"end":13.826,"text":"Share the selected pattern and project details when requesting a quote.","caption":"Request a quote with your project details.","duration":4.226},{"index":3,"start":14.1,"end":18.217,"text":"From first shortlist to your order, make each choice an informed one.","caption":"Make the next choice an informed one.","duration":4.117}],"energy":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0011,0.0961,0.5037,0.9922,0.9758,0.7855,0.7486,0.9046,0.851,0.6868,0.7218,0.8719,0.8068,0.1328,0.023,0.0042,0.2099,0.1055,0.0814,0.6799,0.8387,0.6798,0.6157,0.6494,0.6933,0.6627,0.7698,0.5199,0.3233,0.2107,0.1887,0.5555,0.5551,0.4464,0.6825,0.6174,0.5769,0.4487,0.1957,0.0288,0.0082,0.0023,0.0013,0.2396,0.4031,0.1625,0.4058,0.2598,0.2201,0.1947,0.3378,0.5485,0.5269,0.4267,0.4388,0.4809,0.4284,0.3714,0.5615,0.7101,0.587,0.4203,0.5081,0.5875,0.5487,0.6258,0.6013,0.625,0.5175,0.4616,0.5375,0.4901,0.5221,0.5381,0.5403,0.4622,0.3472,0.3175,0.3063,0.4251,0.7535,0.7515,1,1,1,0.903,0.5636,0.1415,0.0203,0.0031,0.2815,0.1189,0.5535,0.2749,0.1858,0.1537,0.1621,0.2724,0.278,0.224,0.1986,0.1751,0.1418,0.1074,0.0541,0.0832,0.0341,0.0394,0.0157,0.0168,0.0308,0.0884,0.2284,0.3311,0.2441,0.1275,0.0087,0.0059,0.0033,0.0046,0.0045,0.0041,0.0042,0.0045,0.0057,0.0059,0.0076,0.0084,0.0084,0.0082,0.0067,0.0047,0.0027,0.0177,0.187,0.6008,0.8828,0.9224,0.3376,0.0085,1,1,1,1,1,1,1,1,0.861,0.5585,0.303,0.1054,0.0268,0.0131,0.0993,0.2208,0.3879,0.4641,0.3831,0.1547,0.1772,0.1101,0.0158,0.0302,0.2162,0.6315,0.912,0.7881,0.6285,0.298,0.0372,0.0195,0.0454,0.1386,0.1929,0.2578,0.3025,0.3697,0.4179,0.358,0.3465,0.3389,0.302,0.241,0.2961,0.3352,0.4138,0.4818,0.8012,0.6604,0.7945,0.9536,1,1,0.6285,0.4642,0.2849,0.161,0.0579,0.0645,0.0206,0.0309,0.0117,0.0146,0.0258,0.0394,0.0368,0.0214,0.008,0.0018,0.001,0.0002,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0019,0.0017,0.0011,0.0018,0.5634,0.709,0.4269,0.1222,0.1583,0.6329,0.8787,0.699,0.1987,0.019,0.0041,0.1138,0.4519,0.5322,1,1,1,1,1,1,1,0.2657,0.5142,0.8322,0.2769,0.0868,0.1956,0.2964,0.2711,0.5552,0.5245,0.7263,0.6913,0.7836,0.7566,0.7472,0.6632,0.3596,0.0338,0.0764,0.3868,0.5732,0.4759,0.3631,0.2568,0.4309,0.3095,0.162,0.0685,0.0936,0.0744,0.0381,0.0094,0.0044,0.004,0.0032,0.0031,0.0031,0.0025,0.0021,0.0016,0.002,0.0647,0.376,0.7752,0.8437,0.4383,0.1616,0.0242,0.2808,0.1449,0.0761,1,1,1,1,0.854,0.663,0.3114,0.4864,0.488,0.3525,0.3008,0.2698,0.2811,0.2598,0.2391,0.1929,0.6996,0.7168,0.7304,0.7696,0.7965,0.7231,0.574,0.1994,0.0217,0.1007,0.2751,0.2887,0.2808,0.2311,0.1691,0.1445,0.1307,0.135,0.2041,0.1786,0.0441,0.0105,0.0129,0.0161,0.0127,0.0142,0.0125,0.0115,0.0118,0.0128,0.0137,0.0107,0.154,0.4158,0.7403,0.7052,0.7283,0.5309,0.3178,0.0497,0.013,0.6251,0.6628,0.7481,0.8529,0.923,0.9697,1,0.8559,0.4458,0.5726,0.6757,0.2852,0.0319,0.0855,0.109,0.108,0.091,0.6473,0.5931,0.274,0.1121,0.0219,0.1639,0.0977,0.0892,0.4876,0.7883,0.7372,0.6155,0.8095,0.7335,0.8502,0.3447,0.6683,0.6132,0.5105,0.2562,0.0469,0.0051,0.0953,0.4081,0.4964,0.5182,0.5558,0.4736,0.2884,0.155,0.1133,0.1427,0.4684,0.3961,0.4788,0.5382,0.6239,0.4293,0.4968,0.4712,0.4952,0.5566,0.4216,0.2457,0.5089,0.4655,0.4835,0.4515,0.1532,0.2615,0.1422,0.0908,0.0485,0.049,0.0296,0.0193,0.0054,0.002,0.0018,0.0013,0.0012,0.0012,0,0,0,0,0,0,0,0,0,0,0,0,0,0.001,0.0176,0.1335,0.3196,0.5242,0.6617,0.6333,0.6252,1,1,1,1,1,1,0.9683,0.2683,0.2014,0.3731,0.8878,0.5775,0.5034,0.8155,0.6613,0.5339,0.5733,0.3485,0.678,0.4891,0.3674,0.5221,0.5019,0.8717,0.7672,0.7354,0.4802,0.0434,0.007,0.0017,0.0017,0.137,0.5303,0.6329,0.2839,0.243,0.1441,0.0345,0.0048,0.1065,0.2199,0.3849,0.2698,0.2654,0.3932,0.4965,0.7679,0.8316,0.9676,0.8646,0.8374,0.5223,0.3931,0.5669,0.5753,0.5542,0.4722,0.3912,0.3743,0.3833,0.4149,0.4057,0.3935,0.4561,0.4838,0.4453,0.2562,0.0735,0.0097,0.178,0.0819,0.3831,0.6071,0.6511,0.6588,0.5853,0.7425,0.6435,0.5918,0.3112,0.196,0.2676,0.4692,0.3983,0.6763,0.607,0.4722,0.3156,0.1118,0.0338,0.0059,0.0072,0.0294,0.19,0.3837,0.3623,0.2911,0.2873,0.2247,0.1024,0.0827,0.3362,0.2148,0.112,0.4884,0.5742,0.5036,0.5099,0.4333,0.4363,0.3769,0.2931,0.1855,0.1341,0.1054,0.0838,0.0628,0.0682,0.0634,0.1567,0.2792,0.3295,0.1428,0.0207,0.0096,0.0086,0.0103,0.0096,0.0089,0.0091,0.0091,0.0092,0.0118,0.0074,0.0074,0.0065,0.0036,0.0054,0.0759,0.4647,0.5848,0.8059,0.7143,0.709,0.5906,0.4904,0.6383,0.7418,0.5476,0.1494,0.0261,0.0041,0.19,0.1915,0.0782,0.1482,1,1,1,0.8573,0.5762,0.3269,0.7266,0.7295,0.4245,0.1427,0.0299,0.1945,0.5119,0.4364,0.4499,0.4746,0.4601,0.4549,0.4335,0.4069,0.284,0.0496,0.006,0.0046,0.2042,0.1225,0.0795,0.0432,0.0908,0.8781,1,1,1,1,0.7897,0.4142,0.1773,0.0861,0.0311,0.0311,0.0142,0.0101,0.0038,0.0016,0.0036,0.0019,0.0009,0.0007,0,0,0,0,0,0,0,0,0,0,0,0,0,0.001,0.0022,0.012,0.0537,0.0993,0.7228,0.9313,0.5745,0.6818,0.5345,0.3046,0.195,0.1542,0.1727,0.1715,0.1297,0.9073,1,1,1,1,0.893,0.4921,0.444,0.8511,0.6157,0.4911,0.364,0.3983,0.3385,0.3368,0.4294,0.3464,0.6109,0.9116,0.8949,1,1,0.8362,0.7028,0.4254,0.194,0.0635,0.1358,0.1468,0.2899,0.4678,0.544,0.4486,0.3702,0.5324,0.3758,0.0353,0.0023,0.0877,0.267,0.1239,0.3093,0.3106,0.3034,0.3333,0.397,0.4472,0.4991,0.4868,0.5127,0.2119,0.0433,0.0069,0.0419,0.6612,0.7768,0.8615,1,1,1,1,0.8929,0.7756,0.3027,0.2945,0.385,0.2788,0.177,0.115,0.1401,0.0767,0.0764,0.0248,0.0044,0.0042,0.0049,0.0061,0.0068,0.007,0.0067,0.0077,0.0076,0.0093,0.0094,0.0098,0.0086,0.0074,0.0131,0.0087,0.0079,0.1939,0.4091,0.7762,0.759,0.9694,0.7868,0.2621,0.0791,0.0386,0.0098,0.0016,0.0016,0.0024,0.4618,0.6747,0.7207,0.7305,0.6294,0.3927,0.1249,0.0624,0.4291,0.9693,0.6901,0.1991,0.0235,0.05,0.2326,0.5185,0.4737,0.1277,0.554,0.7177,0.7957,0.8991,0.8031,0.6922,0.5238,0.5511,0.566,0.4195,0.3409,0.3879,0.36,0.2806,0.4703,0.4132,0.4809,0.5342,0.5837,0.5359,0.4851,0.5486,0.5609,0.4602,0.2596,0.1275,0.0817,0.0942,0.0981,0.093,0.0886,0.5309,0.7216,0.6655,0.6307,0.7094,0.7424,0.8775,0.73,0.7545,0.6879,0.5274,0.507,0.4707,0.407,0.2623,0.2176,0.2125,0.1821,0.2216,0.2579,0.3535,0.2837,0.1136,0.1449,0.0605,0.0401,0.0572,0.0236,0.0283,0.0134,0.0051,0.0026,0.0019,0.0015,0.0015,0.0014,0.0011,0.0012,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"peak":0.6540911197662354}};window.PRODUCTS=[{"id":"fairborn","title":"Fairborn Type 2 | Phillipe Romano","label":"Fairborn Type 2 · Phillipe Romano","url":"https://www.designerwallcoverings.com/products/prw-920735-rif-003","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/prw-920735-rif-003.jpg?v=1781716604","file":"assets/products/fairborn.jpg","description":"<p>When light rakes across the embossed surface, it reveals an organic grid-like pattern of broken lines. Although extremely durable — as you’d expect from a Type II vinyl wallcovering named for rock fissures — offering a soft, nature-inspired palette of creams, taupes, blues and browns. A small scale and intricate pattern appear almost seamless, and its 12 two-tone colorways resemble artisanal materials like mosaic</p><br><br>A captivating wallcovering featuring intricate detailing and refined textures, offering a sophisticated backdrop that elevates any interior space. The design evokes timeless elegance with a modern touch.  \n#luxuryinteriors #modernwallcoverings #elegantdesign\n","retrieved_at":"2026-09-05","local":"../assets/products/fairborn.jpg"},{"id":"waterperry","title":"Waterperry Ivory/Stone | Sanderson","label":"Waterperry / Ivory–Stone · Sanderson","url":"https://www.designerwallcoverings.com/products/waterperry-ivory-stone-sanderson","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DWAP216280_5340.jpg?v=1788582264","file":"assets/products/waterperry.jpg","description":"<div class=\"sanderson-product\">\n<p>Waterperry in Ivory/Stone is a Sanderson Wallcovering in a botanical/leaves and trees design. Part of the Sanderson heritage collection, this pattern reads in a palette of Alabaster, Linen, Mist. Supplied by the roll; a memo sample is available. An English design-house classic from Sanderson Design Group.</p>\n</div>","retrieved_at":"2026-09-05","local":"../assets/products/waterperry.jpg"},{"id":"wheaton","title":"Wheaton Type 2 | Phillipe Romano","label":"Wheaton Type 2 · Phillipe Romano","url":"https://www.designerwallcoverings.com/products/prw-920166-coa-004-1","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/d4639abbf9baa5237707.jpg?v=1785438111","file":"assets/products/wheaton.jpg","description":"<p>Weaving shimmering strands and beguiling texture, emulating the intricacy of our favored grasscloth Tide in the form of a durable Type II vinyl</p>\n","retrieved_at":"2026-09-05","local":"../assets/products/wheaton.jpg"},{"id":"topiary","title":"Topiary View Thyme | Sanderson","label":"Topiary View / Thyme · Sanderson","url":"https://www.designerwallcoverings.com/products/topiary-view-thyme-sanderson","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DHIP217511_f500.jpg?v=1788582179","file":"assets/products/topiary.jpg","description":"<div class=\"sanderson-product\">\n<p>Topiary View in Thyme is a Sanderson Wallcovering in a landscapes design. Part of the Sanderson heritage collection, this pattern reads in a palette of Alabaster, Stone, Oatmeal. Supplied by the roll; a memo sample is available. An English design-house classic from Sanderson Design Group.</p>\n</div>","retrieved_at":"2026-09-05","local":"../assets/products/topiary.jpg"},{"id":"kaya","title":"Kaya - Cream Weave Wallcovering | Malibu Wallcovering","label":"Kaya / Cream Weave · Malibu","url":"https://www.designerwallcoverings.com/products/kaya-cream-weave-wallcovering-malibu-wallpaper","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/0086435_kaya-cream-weave_650.jpg?v=1776906632","file":"assets/products/kaya.jpg","description":"<p>Elevate your space with the luxurious Kaya Cream Weave wallcovering. This exquisite nonwoven vinyl design features a warm neutral color palette and stunning texture, perfect for creating an elegant ambiance.</p>","retrieved_at":"2026-09-05","local":"../assets/products/kaya.jpg"}];/* Wallo Material Notes: deterministic articulated character acting and real DW samples. */
+(() => {
+ 'use strict';
+ const canvas=document.getElementById('stage'), c=canvas.getContext('2d');
+ const E=window.EPISODE, P=window.PRODUCTS, ep=E.index;
+ const ink='#243a3b', chalk='#eeeae2', teal='#284f4b', clay='#a9634d', brass='#b7924f';
+ const images={}; let telemetry=[];
+ const clamp=(x,a=0,b=1)=>Math.max(a,Math.min(b,x));
+ const mix=(a,b,p)=>a+(b-a)*p;
+ const smooth=p=>{p=clamp(p);return p*p*(3-2*p);};
+ const ease=(t,a,b)=>smooth((t-a)/(b-a));
+ const pulse=(t,a,b,c,d)=>ease(t,a,b)*(1-ease(t,c,d));
+ const point=(x,y)=>({x,y});
+ const dist=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y);
+ function group(x,y,r,s,fn){c.save();c.translate(x,y);c.rotate(r);c.scale(s,s);fn();c.restore();}
+ function path(fn,fill,stroke=null,w=2){c.beginPath();fn(c);if(fill){c.fillStyle=fill;c.fill();}if(stroke){c.lineWidth=w;c.strokeStyle=stroke;c.lineJoin='round';c.lineCap='round';c.stroke();}}
+ function line(x,y,u,v,col,w=2){path(q=>{q.moveTo(x,y);q.lineTo(u,v);},null,col,w);}
+ function rect(x,y,w,h,col){c.fillStyle=col;c.fillRect(x,y,w,h);}
+ function oval(x,y,rx,ry,col,stroke=null,w=2){path(q=>q.ellipse(x,y,rx,ry,0,0,Math.PI*2),col,stroke,w);}
+ function rr(x,y,w,h,r,col,stroke=null,lw=2){path(q=>q.roundRect(x,y,w,h,r),col,stroke,lw);}
+ function text(s,x,y,size=28,color=ink,font='Verdana',align='left'){c.font=`${size}px '${font}'`;c.textAlign=align;c.textBaseline='alphabetic';c.fillStyle=color;c.fillText(s,x,y);}
+ function wrap(s,x,y,max,size=36,color=ink,font='Verdana',lh=size*1.4){
+   c.font=`${size}px '${font}'`;let row='',lines=[];
+   for(const word of s.split(' ')){const test=row?row+' '+word:word;if(c.measureText(test).width>max&&row){lines.push(row);row=word;}else row=test;}
+   if(row)lines.push(row);lines.forEach((v,i)=>text(v,x,y+i*lh,size,color,font));return lines.length;
+ }
+ function imageCover(id,x,y,w,h){const im=images[id];if(!im||!im.complete)return;const k=Math.max(w/im.width,h/im.height);c.save();c.beginPath();c.rect(x,y,w,h);c.clip();c.drawImage(im,x+(w-im.width*k)/2,y+(h-im.height*k)/2,im.width*k,im.height*k);c.restore();}
+ function sample(id,x,y,w=126,h=164,r=0){group(x,y,r,1,()=>{rr(-w/2+7,-h/2+9,w,h,2,'#203a3a20');rect(-w/2-5,-h/2-5,w+10,h+10,'#f9f7f0');imageCover(id,-w/2,-h/2,w,h);line(-w/2,-h/2,w/2,-h/2,'#ffffff',2);});}
+ function bone(a,b,width,col,shade){
+   const angle=Math.atan2(b.y-a.y,b.x-a.x),d=dist(a,b);
+   group(a.x,a.y,angle,1,()=>{rr(-width*.18,-width/2,d+width*.36,width,width*.46,col);path(q=>{q.moveTo(5,width*.26);q.quadraticCurveTo(d*.5,width*.34,d-3,width*.22);},null,shade||'#00000018',2.3);});
+ }
+ function solveIK(root,target,l1,l2,pole){
+   const dx=target.x-root.x,dy=target.y-root.y,raw=Math.hypot(dx,dy),d=clamp(raw,Math.abs(l1-l2)+.01,l1+l2-.01);
+   const nx=raw?dx/raw:0,ny=raw?dy/raw:1,a=(l1*l1-l2*l2+d*d)/(2*d),h=Math.sqrt(Math.max(0,l1*l1-a*a));
+   const end=point(root.x+nx*d,root.y+ny*d),joint=point(root.x+nx*a-ny*h*pole,root.y+ny*a+nx*h*pole);
+   return {root,joint,end,l1,l2};
+ }
+ function rotatePoint(p,angle,origin=point(0,-258)){const x=p.x-origin.x,y=p.y-origin.y;return point(origin.x+x*Math.cos(angle)-y*Math.sin(angle),origin.y+x*Math.sin(angle)+y*Math.cos(angle));}
+ function key(t,frames){if(t<=frames[0][0])return frames[0].slice(1);for(let i=1;i<frames.length;i++)if(t<=frames[i][0]){const p=ease(t,frames[i-1][0],frames[i][0]);return frames[i].slice(1).map((v,j)=>mix(frames[i-1][j+1],v,p));}return frames.at(-1).slice(1);}
+ function gait(t,start,end,x0,x1){
+   const p=clamp((t-start)/(end-start));
+   const lift=(a,b)=>p>a&&p<b?Math.sin(Math.PI*(p-a)/(b-a))*44:0;
+   const left=mix(x0-30,(x0+x1)/2-30,smooth((p-.08)/.28))+mix(0,(x1-x0)/2,smooth((p-.65)/.3));
+   const right=mix(x0+30,x1+30,smooth((p-.35)/.3));
+   return {x:(left+right)/2,feet:[point(left,-lift(.08,.36)-lift(.65,.95)),point(right,-lift(.35,.65))],walking:p>0&&p<1,phase:p};
+ }
+ function voiceEnergy(t){const env=E.audio?.energy||[];return env[Math.min(env.length-1,Math.max(0,Math.floor(t*50)))]||0;}
+ function hand(x,y,r,pose='open',skin='#c89472',prop=null){
+   group(x,y,r,1,()=>{
+    if(prop==='sample')sample(E.product,8,-18,115,148,-.04);
+    if(prop==='fan')for(let k=0;k<3;k++)sample(['waterperry','topiary','kaya'][k],k*17-15,-43,91,130,(k-1)*.18);
+    if(prop==='report'){rr(-12,-134,135,176,4,'#f8f5e9',ink,2);rect(1,-117,108,5,teal);text('TEST',4,-87,22,ink);text('REPORT',4,-58,22,ink);for(let k=0;k<3;k++)line(4,-35+k*18,97,-35+k*18,'#a4aaa0',2);}
+    if(prop==='cloth'){path(q=>{q.moveTo(-28,-15);q.lineTo(30,-26);q.lineTo(43,35);q.lineTo(-21,44);q.closePath();},'#c8c5b5',ink,1);line(-17,-4,28,25,'#efeadb',3);}
+    if(prop==='roller'){line(0,-8,0,-72,ink,7);line(0,-72,37,-72,ink,7);line(37,-72,37,-108,ink,5);rr(-27,-132,127,38,11,'#8b9c8d',ink,2);rr(-9,-9,18,54,7,brass);}
+    if(prop==='tablet'){rr(-73,-76,145,170,8,ink);rr(-65,-67,129,151,4,'#f4f0e5');text('DW',-45,-33,27,teal,'Georgia');for(let i=0;i<3;i++)imageCover(['waterperry','fairborn','kaya'][i],-47+i*32,-6,27,60);}
+    if(pose==='point'){bone(point(-4,3),point(34,-18),13,skin);bone(point(0,9),point(16,15),21,skin);oval(-6,9,13,18,skin);}
+    else{rr(-14,-9,29,42,12,skin);oval(-15,5,7,13,skin);for(let i=0;i<3;i++)line(-7+i*6,14,-7+i*6,28,'#9e685446',1);}
+   });
+ }
+ function head(kind,x,y,t,look,mouth,tilt){
+   const male=kind==='wallo',skin=male?'#c89472':'#d5a185',dark=male?'#714f3b':'#834e39';
+   group(x,y,tilt,.82,()=>{
+    if(!male){oval(-4,-10,51,70,dark);oval(-39,-50,28,28,dark);path(q=>{q.moveTo(-57,-55);q.quadraticCurveTo(-81,-37,-59,-16);},null,'#a86b4b',7);}
+    // Tapered jaw, defined brow and modest nose maintain adult proportions.
+    path(q=>{q.moveTo(-41,-40);q.bezierCurveTo(-43,-77,43,-80,44,-34);q.lineTo(40,31);q.quadraticCurveTo(31,66,5,72);q.quadraticCurveTo(-26,65,-36,34);q.closePath();},skin);
+    path(q=>{q.moveTo(-38,-31);q.lineTo(-32,34);q.quadraticCurveTo(-24,52,-8,57);q.lineTo(-20,21);q.lineTo(-18,-46);q.closePath();},male?'#b58162':'#c39076');
+    oval(-40,3,9,16,skin);oval(42,2,8,15,skin);
+    if(male){path(q=>{q.moveTo(-39,-39);q.lineTo(-33,-18);q.lineTo(-30,-54);q.lineTo(31,-54);q.lineTo(43,-25);q.lineTo(42,-55);q.closePath();},dark);}
+    else{path(q=>{q.moveTo(-40,-27);q.quadraticCurveTo(-24,-63,2,-56);q.quadraticCurveTo(38,-37,43,-19);q.lineTo(38,-58);q.quadraticCurveTo(-4,-89,-37,-55);q.closePath();},dark);for(let i=0;i<4;i++)path(q=>{q.moveTo(-29+i*17,-56);q.quadraticCurveTo(-26+i*17,-43,-13+i*17,-39);},null,'#ae7150',2);}
+    const blink=(t+(male?.31:1.93))%4.4<.14;
+    for(const side of [-1,1]){const ex=side*18+look*3,ey=-8;path(q=>{q.moveTo(ex-10,ey);q.quadraticCurveTo(ex,ey-5,ex+10,ey);},null,ink,2.2);if(!blink){oval(ex+look*3,ey+1,3.5,4.3,ink);oval(ex+look*3+1,ey-.3,1.1,1.2,'#eee8dc');}line(ex-10,ey-15,ex+8,ey-17+(side===1?look*2:0),dark,2.5);}
+    path(q=>{q.moveTo(3+look*5,-5);q.lineTo(8+look*8,15);q.quadraticCurveTo(4+look*5,20,-1+look*5,17);},null,'#98644f',1.8);
+    if(male){path(q=>{q.moveTo(-20,29);q.quadraticCurveTo(-6,20,4,27);q.quadraticCurveTo(15,23,26,30);q.quadraticCurveTo(17,39,3,34);q.quadraticCurveTo(-9,37,-20,29);},dark);}
+    if(mouth>.12){oval(4,44,10+mouth*3,2+mouth*6,'#714735');if(mouth>.4)line(-2,41,11,41,'#efe6d7',2);}
+    else path(q=>{q.moveTo(-7,43);q.quadraticCurveTo(5,48,17,42);},null,'#714735',1.8);
+    if(male){path(q=>{q.moveTo(-49,-53);q.quadraticCurveTo(-48,-98,10,-95);q.quadraticCurveTo(47,-89,48,-53);q.closePath();},brass);path(q=>{q.moveTo(-49,-53);q.quadraticCurveTo(12,-64,72,-48);q.quadraticCurveTo(77,-35,39,-41);q.lineTo(-49,-43);q.closePath();},'#97773f');line(-27,-73,12,-84,'#d2b270',3);}
+    else oval(-39,14,3.5,5.5,brass);
+   });
+ }
+ function character(kind,t,settings={}){
+   const male=kind==='wallo',skin=male?'#c89472':'#d5a185',shirt=male?'#d6d0bc':clay,cloth=male?teal:'#c8c1af';
+   const walk=settings.walk||gait(t,0,1,settings.x,settings.x);let x=walk.x,y=settings.y||882;
+   const crouch=settings.crouch||0,lean=settings.lean||0,breath=Math.sin(t*2.1+(male?0:1.2))*1.7;
+   const hipY=-275+crouch,origin=point(0,hipY);
+   const bodyPoint=(u,v)=>rotatePoint(point(u,v+crouch+breath),lean,origin);
+   const leftShoulder=bodyPoint(-62,-469),rightShoulder=bodyPoint(62,-469);
+   const arms=[solveIK(leftShoulder,point(...settings.left),118,111,-1),solveIK(rightShoulder,point(...settings.right),118,111,1)];
+   const legs=walk.feet.map((foot,i)=>solveIK(point(i===0?-32:32,hipY),point(foot.x-x,foot.y-13),134,136,i===0?1:-1));
+   const mouth=male?voiceEnergy(t)*.88:0;
+   const joints={kind,x,y,arms,legs,lean,crouch,feet:walk.feet,walking:walk.walking,props:settings.props||[]};
+   telemetry.push(joints);
+   oval(x,y+6,94-crouch*.1,15,'#233a3b15');
+   group(x,y,0,settings.scale||1,()=>{
+    // Back arm and far leg precede torso so elbow overlaps read spatially.
+    const armDraw=(i)=>{const b=arms[i];bone(b.root,b.joint,39,shirt);bone(b.joint,b.end,27,skin);oval(b.joint.x,b.joint.y,16,16,skin);const sleeve=point(mix(b.root.x,b.joint.x,.77),mix(b.root.y,b.joint.y,.77));bone(b.root,sleeve,43,shirt);line(sleeve.x-8,sleeve.y,sleeve.x+9,sleeve.y,'#ffffff30',2);};
+    armDraw(0);
+    legs.forEach((b,i)=>{bone(b.root,b.joint,58,cloth);bone(b.joint,b.end,43,cloth);oval(b.joint.x,b.joint.y,23,23,cloth);line(b.root.x-8,b.root.y+30,b.joint.x-6,b.joint.y-16,male?'#41635b':'#dfd8c8',2);group(b.end.x,b.end.y+10,walk.walking&&walk.feet[i].y<0?-.12:0,1,()=>{path(q=>{q.moveTo(-24,-20);q.lineTo(20,-20);q.lineTo(27,-9);q.quadraticCurveTo(60,-5,56,14);q.lineTo(-24,14);q.closePath();},male?'#655545':'#626b62');line(-24,14,55,14,'#34413d',5);line(4,-8,25,-7,'#c6b69b',2);});});
+    group(0,hipY,lean,1,()=>{
+     const dy=crouch+breath-hipY;
+     path(q=>{q.moveTo(-63,-478+dy);q.quadraticCurveTo(-82,-460+dy,-71,-417+dy);q.lineTo(-60,-266+dy);q.quadraticCurveTo(0,-235+dy,60,-266+dy);q.lineTo(71,-417+dy);q.quadraticCurveTo(79,-460+dy,58,-479+dy);q.closePath();},shirt);
+     // Structured overall/apron silhouette, stitched seams, pockets and cloth folds.
+     path(q=>{q.moveTo(-50,-425+dy);q.lineTo(-46,-350+dy);q.lineTo(-61,-290+dy);q.lineTo(-60,-253+dy);q.lineTo(61,-253+dy);q.lineTo(61,-290+dy);q.lineTo(46,-350+dy);q.lineTo(48,-425+dy);q.closePath();},cloth);
+     for(const s of [-1,1]){line(s*44,-472+dy,s*38,-405+dy,cloth,17);oval(s*39,-414+dy,4.8,4.8,brass);line(s*49,-328+dy,s*41,-312+dy,male?'#59756b':'#a9a18f',2);}
+     rr(-27,-401+dy,54,56,4,male?'#254540':'#bab39f');path(q=>{q.moveTo(-23,-388+dy);q.quadraticCurveTo(0,-376+dy,24,-388+dy);},null,male?'#6a8778':'#e5dfd0',1.5);
+     line(-50,-266+dy,52,-266+dy,male?'#1e403b':'#aca590',3);
+     if(male){rr(-66,-321+dy,20,67,3,'#8d7452');line(-56,-342+dy,-56,-282+dy,brass,6);}
+     path(q=>{q.moveTo(-17,-509+dy);q.lineTo(-18,-474+dy);q.quadraticCurveTo(0,-454+dy,21,-474+dy);q.lineTo(18,-509+dy);q.closePath();},skin);
+    });
+    const hp=bodyPoint(0,-559);head(kind,hp.x,hp.y,t,settings.look||0,mouth,(settings.headTilt||0)+lean*.28);
+    armDraw(1);
+    arms.forEach((b,i)=>hand(b.end.x,b.end.y,(settings.wrists||[0,0])[i],(settings.gestures||['open','open'])[i],skin,(settings.props||[])[i]));
+   });
+   return joints;
+ }
+ function room(t){
+   rect(-350,-300,1800,1200,'#dfdbd1');rect(-350,765,1800,450,'#c9c2b1');
+   rect(20,0,240,706,'#e9e7dc');for(let i=0;i<4;i++)line(22+i*59,0,22+i*59,704,'#f7f4e9',7);
+   rect(318,26,647,727,'#c7bfae');rect(329,37,624,704,'#efede3');
+   if(ep===1){const a=ease(t,5.1,8);rect(343,49,596,678,'#aab7a4');c.save();c.beginPath();c.rect(343,49,596*a,678);c.clip();imageCover(E.product,343,49,596,678);c.restore();}
+   else imageCover(E.product,343,49,596,678);
+   line(340,743,943,743,'#faf7eb',4);line(-350,765,1450,765,'#a79f8d',2);
+   for(let i=0;i<6;i++)line(-200,814+i*54,1400,814+i*54,'#b6ad9a',1.4);
+   line(91,765,-170,1220,'#b6ad9a',1.4);line(880,765,1130,1220,'#b6ad9a',1.4);
+   // Light and shadow move gently with the camera, not with an arbitrary pulse.
+   path(q=>{q.moveTo(40,706);q.lineTo(263,706);q.lineTo(672,1120);q.lineTo(412,1120);q.closePath();},'#fcf7df28');
+   // A sample ledge ties the real products to the working material library.
+   rect(404,685,479,12,'#b29d7a');for(let i=0;i<3;i++)sample(P[(i+ep)%P.length].id,468+i*115,622,88,116,(i-1)*.045);
+ }
+ function documentPanel(t){
+   if(ep!==0)return;
+   const a=pulse(t,4.2,4.9,14.3,15.0);if(!a)return;
+   group(640,177+(1-a)*28,0,.88,()=>{
+    rr(-239,-168,478,336,2,'#f7f4e9',ink,1.5);text('BEFORE YOU SPECIFY',-210,-126,24,teal);
+    const labels=['PRODUCT / TEST REPORT','CLASSIFICATION','SUBSTRATE + ADHESIVE'];
+    labels.forEach((label,i)=>{const done=t>5.0+i*2.8;line(-209,-61+i*77,-182,-61+i*77,done?brass:'#bac0b5',3);text(label,-163,-52+i*77,21,ink);});
+   });
+ }
+ function swatchBoard(t){
+   if(ep!==3&&ep!==4)return;
+   const a=pulse(t,6.4,7.1,13.4,14);if(!a)return;
+   group(560,275+(1-a)*25,0,.95,()=>{
+    rr(-199,-139,398,278,2,'#f5f0e4',ink,1);sample(E.product,-83,-1,155,220,0);
+    rect(28,-111,137,83,'#c6bea9');rect(28,-16,137,69,'#8c9985');rect(28,64,137,43,'#b89672');
+    text(ep===3?'COMPARE YOUR FINISHES':'YOUR PROJECT SHORTLIST',-192,-162,23,ink);
+   });
+ }
+ function actionNotes(t){
+   if(ep!==2)return;const a=pulse(t,8,8.5,14.2,14.8);if(!a)return;
+   ['ABRASION','CLEANABILITY','CARE'].forEach((s,i)=>{const show=ease(t,8+i*.7,8.4+i*.7);if(show){rect(440,120+i*77,440,58,'#f4f0e6');text(s,468,159+i*77,27,ink);}});
+ }
+ function performances(t){
+   let wwalk=gait(t,.2,2.9,126,253),pwalk=gait(t,.5,3.1,949,801);
+   const w={walk:wwalk,left:[-85,-279],right:[149,-396],wrists:[-.1,-.36],props:[],gestures:['open','open'],look:.6,lean:Math.sin(t*.8)*.012};
+   const p={walk:pwalk,left:[-130,-402],right:[86,-275],wrists:[.14,.08],props:['sample'],look:-.6,lean:Math.sin(t*.73+1)*.013};
+   // Presenter gestures are deliberately different for each spoken clause.
+   const gesture=key(t,[[0,100,-285],[2.8,145,-411],[4.4,165,-440],[5.2,120,-367],[8.7,161,-441],[9.6,149,-378],[12.8,180,-432],[14,125,-360],[17,152,-419],[19.8,126,-383]]);
+   w.right=gesture;w.wrists[1]=key(t,[[0,0],[3,-.5],[5.2,-.15],[8,-.42],[11,.1],[14,-.4],[18,-.2]])[0];
+   w.gestures[1]=t>4.5&&t<8.8?'point':'open';w.headTilt=Math.sin(t*.65)*.022;w.look=t>4&&t<9?1:.1;
+   if(t<3){w.right=[90-Math.sin(t*4)*40,-277];w.left=[-90+Math.sin(t*4)*40,-277];p.left=[-90+Math.sin(t*4)*24,-280];p.props=[];}
+   if(ep===0){p.props=['report'];p.left=key(t,[[0,-85,-280],[3,-130,-365],[6,-131,-420],[10,-153,-445],[14,-125,-384],[19,-108,-376]]);p.wrists[0]=-.14;p.right=key(t,[[0,90,-277],[8,98,-320],[11,-10,-377],[14,93,-278]]);}
+   if(ep===1){
+    p.props=t<8?['roller']:['sample'];p.left=key(t,[[0,-88,-283],[3,-129,-447],[4.5,-142,-495],[5.2,-143,-411],[6,-146,-495],[7,-145,-397],[8,-100,-367],[11,-139,-436],[15,-103,-361]]);p.wrists[0]=.07;
+    if(t>9.3){p.walk=gait(t,9.3,11.4,801,870);p.look=-1;p.lean=-.06*ease(t,10,11);}
+    w.props=['sample'];w.left=key(t,[[0,-87,-282],[4,-98,-291],[6,-39,-389],[8,-30,-433],[12,-59,-386],[17,-78,-320]]);w.wrists[0]=-.08;
+   }
+   if(ep===2){
+    p.crouch=pulse(t,4,5.6,8.8,10.2)*113;p.lean=-pulse(t,4,5.4,9,10.3)*.10;
+    const wipe=pulse(t,5,5.8,8.2,9);p.props=['cloth'];p.left=[-147-wipe*17,-389+p.crouch+wipe*Math.sin(t*6)*42];p.wrists[0]=-.5;
+    p.right=[85,-284+p.crouch];p.look=-1;
+    w.props=['sample'];w.left=[-45,-371];w.wrists[0]=-.18;
+   }
+   if(ep===3){
+    p.props=['sample'];p.left=key(t,[[0,-92,-280],[3,-100,-366],[6,-138,-440],[10,-144,-478],[14,-126,-418],[18,-93,-340]]);p.wrists[0]=key(t,[[0,0],[3,-.08],[7,.22],[10,-.18],[13,.16],[17,0]])[0];
+    w.props=['fan'];w.left=key(t,[[0,-90,-280],[4,-33,-365],[7,-44,-400],[12,-24,-389],[16,-44,-346]]);w.wrists[0]=-.13;
+   }
+   if(ep===4){
+    w.props=['tablet'];w.left=[-43,-341];w.right=key(t,[[0,101,-280],[3,143,-413],[6,158,-444],[8.5,65,-361],[10,34,-340],[11.6,20,-356],[13,144,-411],[17,160,-444],[19,112,-374]]);w.gestures[1]=t>8&&t<12?'point':'open';
+    p.props=['fan'];p.left=[-129,-400];p.right=key(t,[[0,89,-280],[6,86,-280],[9,-18,-384],[12,76,-285],[17,100,-382],[19,86,-297]]);
+   }
+   // Camera-right actor is drawn first so the presenter remains the visual lead.
+   character('pippa',t,p);character('wallo',t,w);
+ }
+ function overlay(t){
+   rect(0,0,1080,437,chalk);rect(0,1437,1080,483,chalk);
+   text('DESIGNER WALLCOVERINGS',72,96,25,ink);text('WALLO PRESENTS',1008,96,21,teal,'Verdana','right');
+   line(72,124,1008,124,'#b9b6aa',1);text(E.kicker,72,178,24,teal);
+   E.title.split('\n').forEach((s,i)=>text(s,69,270+i*87,73,ink,'Georgia'));
+   text('MATERIAL NOTES',73,411,22,teal);text('WITH WALLO & PIPPA',1006,411,20,teal,'Verdana','right');
+   const product=P.find(p=>p.id===E.product);text(product.label,72,1481,25,ink);
+   line(72,1505,1008,1505,'#b9b6aa',1);
+   const segment=(E.audio?.segments||[]).filter(s=>t>=s.start).at(-1);
+   const caption=segment?E.captions[segment.index]:'A wallcovering presentation with Wallo.';
+   wrap(caption,72,1570,930,38,ink,'Verdana',55);
+   if(t>=16.8)text(E.takeaway,72,1738,37,teal,'Georgia');
+   wrap(E.note,72,1794,926,24,'#58635b','Verdana',34);
+   text('designerwallcoverings.com',72,1870,25,teal);
+   text(String(ep+1).padStart(2,'0')+' / 05',1008,1870,22,teal,'Verdana','right');
+   rect(72,1894,936,3,'#c9c7bb');rect(72,1894,936*clamp(t/20),3,teal);
+ }
+ function draw(t){
+   telemetry=[];rect(0,0,1080,1920,chalk);
+   c.save();c.beginPath();c.rect(0,437,1080,1000);c.clip();
+   const zoom=1+ease(t,3.3,5)*.08-ease(t,14.2,16)*.06;
+   c.translate(540,950);c.scale(zoom,zoom);c.translate(-540,-487);
+   room(t);documentPanel(t);swatchBoard(t);actionNotes(t);performances(t);
+   c.restore();overlay(t);window.__rigState=telemetry;
+ }
+ const driver={t:0};window.__timelines=window.__timelines||{};
+ window.__timelines.main=gsap.timeline({paused:true}).to(driver,{t:20,duration:20,ease:'none',onUpdate:()=>draw(driver.t)},0);
+ window.__draw=draw;
+ window.__ready=Promise.all(P.map(p=>new Promise((resolve,reject)=>{const im=new Image();images[p.id]=im;im.onload=resolve;im.onerror=()=>reject(new Error('Missing product '+p.id));im.src=p.local;}))).then(()=>document.fonts.ready).then(()=>{draw(driver.t);return true;});
+ draw(0);
+})();
+</script></body></html>
\ No newline at end of file
diff --git a/series2/gallery.mjs b/series2/gallery.mjs
new file mode 100644
index 0000000..b1f2a3a
--- /dev/null
+++ b/series2/gallery.mjs
@@ -0,0 +1,17 @@
+import fs from 'node:fs';
+import path from 'node:path';
+const root=import.meta.dirname;
+const episodes=JSON.parse(fs.readFileSync(path.join(root,'episodes.json')));
+const products=JSON.parse(fs.readFileSync(path.join(root,'products.json')));
+const sources=JSON.parse(fs.readFileSync(path.join(root,'sources.json')));
+const esc=s=>s.replaceAll('&','&amp;').replaceAll('<','&lt;').replaceAll('"','&quot;');
+const html=`<!doctype html><html lang="en"><head><meta charset="utf-8"><link rel="icon" href="data:,"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Wallo presents — A guide to wallcoverings</title><style>
+@font-face{font-family:Editorial;src:url('assets/georgia.ttf')}@font-face{font-family:Information;src:url('assets/verdana.ttf')}*{box-sizing:border-box}body{margin:0;background:#eeeae2;color:#243a3b;font:16px/1.6 Information,sans-serif}main{max-width:1240px;margin:auto;padding:38px 30px 80px}nav{display:flex;justify-content:space-between;gap:20px;padding-bottom:23px;border-bottom:1px solid #b9b6aa;font-size:12px;letter-spacing:.12em}a{color:#284f4b;text-underline-offset:4px}h1,h2,h3{font-family:Editorial,serif;font-weight:400;line-height:1.12}h1{font-size:clamp(44px,5vw,70px);margin:16px 0 22px;letter-spacing:-.04em}h2{font-size:40px}h3{font-size:32px;margin:14px 0}p{max-width:650px}.eyebrow{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:#4d6459}.hero{display:flex;gap:64px;align-items:center;margin:44px 0 64px}.hero .intro{flex:1}.hero video{width:360px;max-width:100%;box-shadow:0 17px 45px #233a3b1a}video{aspect-ratio:9/16;display:block;background:#dad5c8;border-radius:2px}.primary{display:inline-block;margin:18px 0 10px;padding:12px 22px;background:#284f4b;color:#f7f3e9;text-decoration:none}.chapters{display:flex;flex-wrap:wrap;gap:8px;padding:18px 0}button{font:inherit;font-size:13px;background:transparent;border:1px solid #a8b0a2;color:#284f4b;padding:8px 12px;cursor:pointer}button:hover,button:focus-visible{background:#284f4b;color:#f7f3e9}.short{display:flex;gap:48px;align-items:center;padding:42px 0;border-top:1px solid #b9b6aa}.short video{width:245px;max-width:100%}.short .copy{max-width:620px}.meta{font-size:13px;color:#59645a}.product{display:flex;gap:18px;align-items:center;padding:20px 0}.product img{width:78px;height:94px;object-fit:cover}.product span{font-size:13px}details{border-top:1px solid #b9b6aa;margin-top:45px;padding-top:22px}summary{cursor:pointer}details li{margin:12px 0;max-width:900px}footer{margin-top:36px;font-size:12px;color:#59645a}.small{font-size:13px}@media(max-width:750px){main{padding:24px 21px 50px}.hero{flex-direction:column;gap:25px;align-items:flex-start}.hero video{width:100%;max-width:430px;margin:auto}.short{flex-direction:column;gap:24px;align-items:flex-start}.short video{width:100%;max-width:360px;margin:auto}nav{flex-wrap:wrap}h2{font-size:32px}}
+</style></head><body><main><nav><span>DESIGNER WALLCOVERINGS / MATERIAL NOTES</span><a href="../watch.html">Original Wallo &amp; Pippa shorts</a><a href="memes/index.html">10 installer memes</a></nav>
+<section class="hero"><div class="intro"><p class="eyebrow">A presentation by Wallo</p><h1>Good rooms start<br>with good choices.</h1><p>Five short lessons on specifying wallcoverings, with Wallo presenting and Pippa demonstrating real products from Designer Wallcoverings.</p><p>Fire performance. Design benefits. Daily wear. Sampling. Service.</p><a class="primary" href="renders/wallo-wallcovering-presentation.mp4" download>Download the full presentation</a><p class="meta">1 minute 40 seconds · narrated · captions · 1080 × 1920</p><div class="chapters" aria-label="Jump to presentation chapter">${episodes.map((ep,i)=>`<button data-jump="${i*20}">${i+1}. ${['Fire','Design','Performance','Samples','DW service'][i]}</button>`).join('')}</div><p class="small" id="playback-status" role="status">Sound on for Wallo’s presentation. Each chapter is also available below as a 20-second short.</p></div><video id="presentation" data-slug="wallo-wallcovering-presentation" controls playsinline preload="metadata" poster="verification/04-fire-rated-3.5.png" src="renders/wallo-wallcovering-presentation.mp4"></video></section>
+<h2>The five shorts</h2>${episodes.map((ep,i)=>{const p=products.find(p=>p.id===ep.product);return `<article class="short" id="${ep.slug}"><video data-slug="${ep.slug}" controls playsinline preload="metadata" poster="verification/${ep.slug}-6.2.png" src="renders/${ep.slug}.mp4"></video><div class="copy"><p class="eyebrow">${esc(ep.kicker)}</p><h3>${esc(ep.shortTitle)}</h3><p>${esc(ep.takeaway)}</p><p>${esc(ep.captions[1])} ${esc(ep.captions[3])}</p><div class="product"><a href="${p.url}" target="_blank" rel="noopener"><img src="${p.file}" alt="${esc(p.title)}"></a><span>Featured at Designer Wallcoverings<br><a href="${p.url}" target="_blank" rel="noopener">${esc(p.title)}</a></span></div><p class="meta">20 seconds · narrated by Wallo · captions</p><a href="renders/${ep.slug}.mp4" download>Download this short</a> · <a href="transcripts/${ep.slug}.txt">Read transcript</a></div></article>`;}).join('')}
+<details><summary>Product credits and information sources</summary><p class="small">Product images come directly from designerwallcoverings.com. Animated room installations are illustrative and not to scale. Ask for the selected product’s actual test report, specification and installation instructions. Product availability and terms may change.</p><ul>${sources.claims.map(s=>`<li><a href="${s.url}" target="_blank" rel="noopener">${esc(s.topic)}</a> — ${esc(s.statement)}</li>`).join('')}</ul></details><footer>Wallo &amp; Pippa / Material Notes · September 5, 2026<br>Original articulated character animation. Local synthetic narration. Product artwork credited to the brands shown. Local review copy.</footer></main><script>document.querySelectorAll('[data-jump]').forEach(b=>b.addEventListener('click',()=>{const v=document.getElementById('presentation');v.currentTime=Number(b.dataset.jump);v.play().catch(error=>{if(error.name!=='AbortError')document.getElementById('playback-status').textContent='Press play to start this chapter.';});v.scrollIntoView({behavior:'smooth',block:'center'});}));document.querySelectorAll('video').forEach(v=>v.addEventListener('play',()=>document.querySelectorAll('video').forEach(other=>{if(other!==v)other.pause();})));</script></body></html>`;
+fs.writeFileSync(path.join(root,'watch.html'),html);
+fs.mkdirSync(path.join(root,'transcripts'),{recursive:true});
+for(const ep of episodes)fs.writeFileSync(path.join(root,'transcripts',ep.slug+'.txt'),ep.shortTitle+'\n\n'+ep.lines.join('\n\n')+'\n\nFeatured product: '+products.find(p=>p.id===ep.product).url+'\n');
+console.log('Created presentation gallery and five transcripts.');
diff --git a/series2/index.html b/series2/index.html
new file mode 100644
index 0000000..2621efb
--- /dev/null
+++ b/series2/index.html
@@ -0,0 +1,215 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=1080,height=1920"><title>Wallo presents — Why fire-rated wallcovering?</title><style>@font-face{font-family:Georgia;src:url('assets/georgia.ttf')}@font-face{font-family:Verdana;src:url('assets/verdana.ttf')}html,body{margin:0;width:1080px;height:1920px;overflow:hidden}#root{position:relative;width:1080px;height:1920px;overflow:hidden}.clip{position:absolute;inset:0;width:1080px;height:1920px}canvas{width:1080px;height:1920px}.preload{position:absolute;opacity:0;pointer-events:none}</style><script src="assets/gsap.min.js"></script></head><body><div id="root" data-composition-id="main" data-start="0" data-duration="20" data-width="1080" data-height="1920" data-fps="30"><section id="material-scene" class="clip" data-start="0" data-duration="20" data-track-index="0"><canvas id="stage" width="1080" height="1920" aria-label="Wallo presenting Why fire-rated wallcovering?"></canvas><span class="preload" style="font-family:Georgia">Wallo</span><span class="preload" style="font-family:Verdana">Designer Wallcoverings</span></section><audio id="narrated-mix" src="assets/mix-0.wav" data-start="0" data-duration="20" data-track-index="1"></audio></div><script>window.EPISODE={"slug":"04-fire-rated","title":"Fire-rated.\nProperly specified.","shortTitle":"Why fire-rated wallcovering?","kicker":"01 / FIRE PERFORMANCE","product":"fairborn","note":"Classification ≠ fireproof. Confirm project requirements.","takeaway":"Request the test report.","lines":["Fire performance belongs in the specification, right alongside the design.","ASTM E eighty-four measures surface flame spread and smoke development.","A classification is not a promise that wallpaper is fireproof.","Request the product's test report and confirm the installation requirements."],"captions":["Fire performance belongs in the specification.","ASTM E84: surface flame spread + smoke development.","A classification does not mean fireproof.","Get the report. Confirm the installation requirements."],"index":0,"audio":{"slug":"04-fire-rated","provider":"Kokoro local","voice":"am_michael","duration":20,"segments":[{"index":0,"start":0.6,"end":4.825,"text":"Fire performance belongs in the specification, right alongside the design.","caption":"Fire performance belongs in the specification.","duration":4.225},{"index":1,"start":5.1,"end":9.326,"text":"ASTM E eighty-four measures surface flame spread and smoke development.","caption":"ASTM E84: surface flame spread + smoke development.","duration":4.226},{"index":2,"start":9.6,"end":13.803,"text":"A classification is not a promise that wallpaper is fireproof.","caption":"A classification does not mean fireproof.","duration":4.203},{"index":3,"start":14.1,"end":18.857,"text":"Request the product's test report and confirm the installation requirements.","caption":"Get the report. Confirm the installation requirements.","duration":4.757}],"energy":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0012,0.0047,0.0174,0.0557,0.0887,0.6506,0.9948,0.9031,0.7399,0.7077,0.583,0.5662,0.5773,0.6428,0.7302,0.2114,0.018,0.0035,0.169,0.3363,0.6278,0.2073,0.1404,0.1312,0.1064,0.1011,0.0759,0.381,0.5915,0.6192,0.6481,0.5202,0.4138,0.4348,0.4805,0.4767,0.4303,0.3543,0.2276,0.2018,0.3587,0.0515,0.0017,0.0031,0.0747,0.4884,0.4711,0.2873,0.2918,0.3463,0.3885,0.4873,0.6173,0.5417,0.6643,0.6181,0.5684,0.4733,0.4419,0.3606,0.3829,0.38,0.3523,0.2186,0.219,0.275,0.2166,0.3565,0.3256,0.3499,0.3976,0.3682,0.341,0.3755,0.2461,0.4064,0.4989,0.2891,0.0437,0.0029,0.3856,0.5779,0.4828,0.3195,0.1977,0.3538,0.3409,0.2953,0.2868,0.1419,0.0679,0.0611,0.0587,0.2512,0.0993,0.0146,0.1102,0.0638,0.1303,0.4596,0.4405,0.3785,0.3033,0.2883,0.2116,0.5138,0.4594,0.4209,0.397,0.1721,0.0989,0.0726,0.0239,0.0385,0.0181,0.0155,0.0021,0.0015,0.0019,0.0025,0.0034,0.0034,0.0041,0.0043,0.0062,0.0073,0.0066,0.0065,0.0051,0.0046,0.003,0.0015,0.0015,0.071,0.1492,0.2249,0.4519,0.9385,1,0.9874,1,1,0.8162,0.6613,0.3443,0.0984,0.038,0.18,0.4506,0.4012,0.3595,0.3249,0.3225,0.3881,0.6092,0.5798,0.6589,0.6693,0.5921,0.4484,0.4239,0.3984,0.4371,0.2673,0.1554,0.3871,0.4242,0.3069,0.2522,0.208,0.4185,0.3076,0.3236,0.3637,0.3982,0.4049,0.2554,0.1402,0.0903,0.0895,0.2676,0.2165,0.1187,0.1217,0.2067,0.3746,0.3223,0.2415,0.2228,0.2132,0.2597,0.5527,0.5283,0.4602,0.4673,0.5429,0.5048,0.3489,0.2544,0.1282,0.092,0.0527,0.0398,0.0231,0.0139,0.017,0.0189,0.0091,0.0026,0.0013,0.0008,0.0004,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0016,0.0034,0.0018,0.0031,0.0021,0.5753,0.9468,0.8893,1,0.9721,0.9578,1,1,1,1,0.7674,0.3763,0.6669,0.4738,0.1212,0.0101,0.133,0.4414,0.5266,0.5228,0.5135,0.4629,0.3655,0.2989,0.2832,0.3768,0.3985,0.4954,0.6561,0.7018,0.7038,0.7379,0.6468,0.5082,0.2998,0.2548,0.1823,0.0865,0.0091,0.0048,0.0043,0.0039,0.0028,0.0022,0.0017,0.0043,0.2383,0.5278,0.6138,0.511,0.3618,0.2954,0.0578,0.0121,0.0024,0.6567,0.8351,0.753,0.7574,0.7543,0.6554,0.6476,0.2019,0.3876,0.4468,0.3598,0.2605,0.144,0.1311,0.1131,0.118,0.0709,0.2716,0.6081,0.4748,0.4659,0.3909,0.4397,0.3742,0.3556,0.3079,0.3188,0.3661,0.4515,0.5435,0.4919,0.481,0.4957,0.3947,0.2402,0.281,0.225,0.3107,0.3294,0.3121,0.2211,0.4887,0.5531,0.4717,0.3992,0.3184,0.4129,0.5114,0.867,0.8262,0.7312,0.6503,0.1258,0.1174,0.1011,0.0759,0.1406,0.3891,0.2883,0.4013,0.4434,0.2424,0.0973,0.0715,0.0662,0.0547,0.2457,0.3585,0.4489,0.4251,0.404,0.4348,0.467,0.4782,0.5044,0.4776,0.4044,0.2293,0.4516,0.5444,0.6926,0.1904,0.0113,0.0015,0.0278,0.2218,0.5795,0.8909,0.9262,0.8666,0.9335,0.9704,0.9715,0.6365,0.5611,0.3318,0.2355,0.1121,0.1231,0.2565,0.3342,0.3726,0.4115,0.3258,0.1821,0.4132,0.5307,0.4751,0.3645,0.0725,0.0757,0.4859,0.6157,0.6818,0.6687,0.6689,0.6436,0.3866,0.0566,0.031,0.0066,0.0028,0.0022,0.1254,0.4669,0.4207,0.2224,0.1986,0.6867,0.7368,0.7779,0.7788,0.5736,0.3936,0.3443,0.2432,0.162,0.0362,0.0277,0.0449,0.0565,0.0946,0.0578,0.021,0.0131,0.0075,0.0038,0.0015,0.0014,0.0015,0.0008,0.0006,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0021,0.0021,0.0016,0.0025,0.3618,0.4943,0.2485,0.0591,0.0108,0.2846,0.3245,0.1782,0.8183,1,1,1,1,0.9405,0.44,0.5547,0.6129,0.5737,0.2519,0.6548,0.1975,0.1419,0.1302,0.3439,0.4803,0.1806,0.0764,0.0145,0.1717,0.1056,0.3375,0.7334,0.7553,0.6525,0.5488,0.4684,0.4379,0.3368,0.6547,0.6238,0.558,0.5201,0.1816,0.3275,0.2462,0.1531,0.0681,0.0863,0.0664,0.0569,0.0194,0.0051,0.0036,0.0049,0.0053,0.0065,0.0072,0.0065,0.0065,0.0066,0.0067,0.0062,0.0038,0.0042,0.0135,0.2344,0.6378,0.5686,0.6023,0.6041,0.3348,0.6032,0.8015,0.7852,0.8026,0.6993,0.6735,0.7098,0.8779,0.6633,0.7119,1,0.7157,0.1544,0.0266,0.0042,0.0039,0.1643,0.0768,0.0882,0.0576,0.6191,0.932,0.8178,0.7666,0.805,0.8902,1,0.9878,0.7006,0.6795,0.5953,0.5962,0.5852,0.5394,0.4726,0.3754,0.4374,0.2924,0.1028,0.0246,0.0232,0.4833,0.4923,0.5045,0.3176,0.2895,0.1274,0.132,0.1741,0.2316,0.2645,0.3002,0.4124,0.7962,0.8176,1,1,1,1,1,1,1,0.5335,0.1178,0.0253,0.0046,0.1096,0.2661,0.4154,0.5833,0.5267,0.4882,0.4096,0.3273,0.1101,0.0117,0.0046,0.1091,0.1814,0.3517,0.3902,0.3635,0.3318,0.3113,0.1517,0.0653,0.0335,0.2053,0.2957,0.3609,0.4206,0.6234,0.6289,0.2044,0.1255,0.1004,0.0725,0.055,0.3067,0.6165,0.4176,0.354,0.4898,0.5081,0.5526,0.4784,0.4998,0.4946,0.5018,0.475,0.3486,0.1177,0.0163,0.0043,0.0045,0.1248,0.0767,0.0727,0.2303,0.3068,0.1559,0.0856,0.0547,0.0223,0.0231,0.0122,0.0198,0.0274,0.035,0.0317,0.0175,0.0061,0.0019,0.0011,0.0009,0.0003,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0013,0.0129,0.1954,0.5581,0.7718,0.6137,0.2179,0.0355,0.0654,0.3038,0.155,0.0924,1,1,1,1,1,1,0.6747,0.4901,0.8324,0.556,0.2242,0.0614,0.0134,0.0222,0.337,0.6958,0.1979,0.0702,0.0113,0.0443,0.2074,0.0955,0.6543,0.6854,0.7905,0.6735,0.7761,0.7956,0.7237,0.4511,0.4212,0.6556,0.6012,0.5466,0.3626,0.1186,0.0892,0.2297,0.5434,0.5387,0.1668,0.0055,0.0045,0.1272,0.2692,0.1303,0.3839,0.5868,0.7874,0.7544,0.6104,0.442,0.3928,0.8115,0.5113,0.0873,0.0417,0.0296,0.0107,0.1209,0.2818,0.5463,0.5581,0.4529,0.2372,0.0633,0.0116,0.0068,0.1719,0.0887,0.1023,0.0726,0.5641,0.8498,1,0.9576,0.7551,0.7402,0.4627,0.1733,0.1045,0.0625,0.049,0.0329,0.0154,0.0033,0.0036,0.0023,0.0022,0.0028,0.0036,0.0043,0.0043,0.0052,0.0056,0.0068,0.0074,0.0081,0.0084,0.0095,0.011,0.0112,0.008,0.0089,0.1539,0.5766,0.6525,0.7862,0.8312,0.4877,0.1702,0.2287,0.2716,0.7589,0.8968,0.5656,0.6187,0.4997,0.2914,0.165,0.1191,0.132,0.1262,0.1389,0.0958,0.5584,1,1,0.895,0.9682,0.9233,0.9071,0.8977,0.8346,0.6209,0.6791,0.6277,0.5404,0.4059,0.2492,0.4399,0.4487,0.351,0.2837,0.1986,0.2479,0.3492,0.4258,0.546,0.5376,0.4801,0.2657,0.5963,0.5195,0.104,0.0039,0.1108,0.3389,0.6348,0.2999,0.3697,0.3802,0.5559,0.4946,0.5133,0.6291,0.5863,0.5183,0.4228,0.2744,0.582,0.7211,0.5863,0.5963,0.1387,0.4452,0.4537,0.3951,0.3685,0.3926,0.3758,0.3445,0.3244,0.3976,0.4419,0.4081,0.2139,0.0433,0.0107,0.1623,0.1254,0.0529,0.0498,0.409,0.759,0.7565,0.6322,0.6556,0.6586,0.7206,0.667,0.6501,0.5284,0.3946,0.3077,0.2734,0.1682,0.1285,0.1159,0.0953,0.0747,0.0651,0.0301,0.024,0.0274,0.0073,0.0524,0.1172,0.2582,0.2937,0.2481,0.1612,0.0368,0.0054,0.002,0.0009,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"peak":0.6490918397903442}};window.PRODUCTS=[{"id":"fairborn","title":"Fairborn Type 2 | Phillipe Romano","label":"Fairborn Type 2 · Phillipe Romano","url":"https://www.designerwallcoverings.com/products/prw-920735-rif-003","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/prw-920735-rif-003.jpg?v=1781716604","file":"assets/products/fairborn.jpg","description":"<p>When light rakes across the embossed surface, it reveals an organic grid-like pattern of broken lines. Although extremely durable — as you’d expect from a Type II vinyl wallcovering named for rock fissures — offering a soft, nature-inspired palette of creams, taupes, blues and browns. A small scale and intricate pattern appear almost seamless, and its 12 two-tone colorways resemble artisanal materials like mosaic</p><br><br>A captivating wallcovering featuring intricate detailing and refined textures, offering a sophisticated backdrop that elevates any interior space. The design evokes timeless elegance with a modern touch.  \n#luxuryinteriors #modernwallcoverings #elegantdesign\n","retrieved_at":"2026-09-05","local":"assets/products/fairborn.jpg"},{"id":"waterperry","title":"Waterperry Ivory/Stone | Sanderson","label":"Waterperry / Ivory–Stone · Sanderson","url":"https://www.designerwallcoverings.com/products/waterperry-ivory-stone-sanderson","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DWAP216280_5340.jpg?v=1788582264","file":"assets/products/waterperry.jpg","description":"<div class=\"sanderson-product\">\n<p>Waterperry in Ivory/Stone is a Sanderson Wallcovering in a botanical/leaves and trees design. Part of the Sanderson heritage collection, this pattern reads in a palette of Alabaster, Linen, Mist. Supplied by the roll; a memo sample is available. An English design-house classic from Sanderson Design Group.</p>\n</div>","retrieved_at":"2026-09-05","local":"assets/products/waterperry.jpg"},{"id":"wheaton","title":"Wheaton Type 2 | Phillipe Romano","label":"Wheaton Type 2 · Phillipe Romano","url":"https://www.designerwallcoverings.com/products/prw-920166-coa-004-1","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/d4639abbf9baa5237707.jpg?v=1785438111","file":"assets/products/wheaton.jpg","description":"<p>Weaving shimmering strands and beguiling texture, emulating the intricacy of our favored grasscloth Tide in the form of a durable Type II vinyl</p>\n","retrieved_at":"2026-09-05","local":"assets/products/wheaton.jpg"},{"id":"topiary","title":"Topiary View Thyme | Sanderson","label":"Topiary View / Thyme · Sanderson","url":"https://www.designerwallcoverings.com/products/topiary-view-thyme-sanderson","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/DHIP217511_f500.jpg?v=1788582179","file":"assets/products/topiary.jpg","description":"<div class=\"sanderson-product\">\n<p>Topiary View in Thyme is a Sanderson Wallcovering in a landscapes design. Part of the Sanderson heritage collection, this pattern reads in a palette of Alabaster, Stone, Oatmeal. Supplied by the roll; a memo sample is available. An English design-house classic from Sanderson Design Group.</p>\n</div>","retrieved_at":"2026-09-05","local":"assets/products/topiary.jpg"},{"id":"kaya","title":"Kaya - Cream Weave Wallcovering | Malibu Wallcovering","label":"Kaya / Cream Weave · Malibu","url":"https://www.designerwallcoverings.com/products/kaya-cream-weave-wallcovering-malibu-wallpaper","image_url":"https://cdn.shopify.com/s/files/1/0015/4117/7456/files/0086435_kaya-cream-weave_650.jpg?v=1776906632","file":"assets/products/kaya.jpg","description":"<p>Elevate your space with the luxurious Kaya Cream Weave wallcovering. This exquisite nonwoven vinyl design features a warm neutral color palette and stunning texture, perfect for creating an elegant ambiance.</p>","retrieved_at":"2026-09-05","local":"assets/products/kaya.jpg"}];/* Wallo Material Notes: deterministic articulated character acting and real DW samples. */
+(() => {
+ 'use strict';
+ const canvas=document.getElementById('stage'), c=canvas.getContext('2d');
+ const E=window.EPISODE, P=window.PRODUCTS, ep=E.index;
+ const ink='#243a3b', chalk='#eeeae2', teal='#284f4b', clay='#a9634d', brass='#b7924f';
+ const images={}; let telemetry=[];
+ const clamp=(x,a=0,b=1)=>Math.max(a,Math.min(b,x));
+ const mix=(a,b,p)=>a+(b-a)*p;
+ const smooth=p=>{p=clamp(p);return p*p*(3-2*p);};
+ const ease=(t,a,b)=>smooth((t-a)/(b-a));
+ const pulse=(t,a,b,c,d)=>ease(t,a,b)*(1-ease(t,c,d));
+ const point=(x,y)=>({x,y});
+ const dist=(a,b)=>Math.hypot(a.x-b.x,a.y-b.y);
+ function group(x,y,r,s,fn){c.save();c.translate(x,y);c.rotate(r);c.scale(s,s);fn();c.restore();}
+ function path(fn,fill,stroke=null,w=2){c.beginPath();fn(c);if(fill){c.fillStyle=fill;c.fill();}if(stroke){c.lineWidth=w;c.strokeStyle=stroke;c.lineJoin='round';c.lineCap='round';c.stroke();}}
+ function line(x,y,u,v,col,w=2){path(q=>{q.moveTo(x,y);q.lineTo(u,v);},null,col,w);}
+ function rect(x,y,w,h,col){c.fillStyle=col;c.fillRect(x,y,w,h);}
+ function oval(x,y,rx,ry,col,stroke=null,w=2){path(q=>q.ellipse(x,y,rx,ry,0,0,Math.PI*2),col,stroke,w);}
+ function rr(x,y,w,h,r,col,stroke=null,lw=2){path(q=>q.roundRect(x,y,w,h,r),col,stroke,lw);}
+ function text(s,x,y,size=28,color=ink,font='Verdana',align='left'){c.font=`${size}px '${font}'`;c.textAlign=align;c.textBaseline='alphabetic';c.fillStyle=color;c.fillText(s,x,y);}
+ function wrap(s,x,y,max,size=36,color=ink,font='Verdana',lh=size*1.4){
+   c.font=`${size}px '${font}'`;let row='',lines=[];
+   for(const word of s.split(' ')){const test=row?row+' '+word:word;if(c.measureText(test).width>max&&row){lines.push(row);row=word;}else row=test;}
+   if(row)lines.push(row);lines.forEach((v,i)=>text(v,x,y+i*lh,size,color,font));return lines.length;
+ }
+ function imageCover(id,x,y,w,h){const im=images[id];if(!im||!im.complete)return;const k=Math.max(w/im.width,h/im.height);c.save();c.beginPath();c.rect(x,y,w,h);c.clip();c.drawImage(im,x+(w-im.width*k)/2,y+(h-im.height*k)/2,im.width*k,im.height*k);c.restore();}
+ function sample(id,x,y,w=126,h=164,r=0){group(x,y,r,1,()=>{rr(-w/2+7,-h/2+9,w,h,2,'#203a3a20');rect(-w/2-5,-h/2-5,w+10,h+10,'#f9f7f0');imageCover(id,-w/2,-h/2,w,h);line(-w/2,-h/2,w/2,-h/2,'#ffffff',2);});}
+ function bone(a,b,width,col,shade){
+   const angle=Math.atan2(b.y-a.y,b.x-a.x),d=dist(a,b);
+   group(a.x,a.y,angle,1,()=>{rr(-width*.18,-width/2,d+width*.36,width,width*.46,col);path(q=>{q.moveTo(5,width*.26);q.quadraticCurveTo(d*.5,width*.34,d-3,width*.22);},null,shade||'#00000018',2.3);});
+ }
+ function solveIK(root,target,l1,l2,pole){
+   const dx=target.x-root.x,dy=target.y-root.y,raw=Math.hypot(dx,dy),d=clamp(raw,Math.abs(l1-l2)+.01,l1+l2-.01);
+   const nx=raw?dx/raw:0,ny=raw?dy/raw:1,a=(l1*l1-l2*l2+d*d)/(2*d),h=Math.sqrt(Math.max(0,l1*l1-a*a));
+   const end=point(root.x+nx*d,root.y+ny*d),joint=point(root.x+nx*a-ny*h*pole,root.y+ny*a+nx*h*pole);
+   return {root,joint,end,l1,l2};
+ }
+ function rotatePoint(p,angle,origin=point(0,-258)){const x=p.x-origin.x,y=p.y-origin.y;return point(origin.x+x*Math.cos(angle)-y*Math.sin(angle),origin.y+x*Math.sin(angle)+y*Math.cos(angle));}
+ function key(t,frames){if(t<=frames[0][0])return frames[0].slice(1);for(let i=1;i<frames.length;i++)if(t<=frames[i][0]){const p=ease(t,frames[i-1][0],frames[i][0]);return frames[i].slice(1).map((v,j)=>mix(frames[i-1][j+1],v,p));}return frames.at(-1).slice(1);}
+ function gait(t,start,end,x0,x1){
+   const p=clamp((t-start)/(end-start));
+   const lift=(a,b)=>p>a&&p<b?Math.sin(Math.PI*(p-a)/(b-a))*44:0;
+   const left=mix(x0-30,(x0+x1)/2-30,smooth((p-.08)/.28))+mix(0,(x1-x0)/2,smooth((p-.65)/.3));
+   const right=mix(x0+30,x1+30,smooth((p-.35)/.3));
+   return {x:(left+right)/2,feet:[point(left,-lift(.08,.36)-lift(.65,.95)),point(right,-lift(.35,.65))],walking:p>0&&p<1,phase:p};
+ }
+ function voiceEnergy(t){const env=E.audio?.energy||[];return env[Math.min(env.length-1,Math.max(0,Math.floor(t*50)))]||0;}
+ function hand(x,y,r,pose='open',skin='#c89472',prop=null){
+   group(x,y,r,1,()=>{
+    if(prop==='sample')sample(E.product,8,-18,115,148,-.04);
+    if(prop==='fan')for(let k=0;k<3;k++)sample(['waterperry','topiary','kaya'][k],k*17-15,-43,91,130,(k-1)*.18);
+    if(prop==='report'){rr(-12,-134,135,176,4,'#f8f5e9',ink,2);rect(1,-117,108,5,teal);text('TEST',4,-87,22,ink);text('REPORT',4,-58,22,ink);for(let k=0;k<3;k++)line(4,-35+k*18,97,-35+k*18,'#a4aaa0',2);}
+    if(prop==='cloth'){path(q=>{q.moveTo(-28,-15);q.lineTo(30,-26);q.lineTo(43,35);q.lineTo(-21,44);q.closePath();},'#c8c5b5',ink,1);line(-17,-4,28,25,'#efeadb',3);}
+    if(prop==='roller'){line(0,-8,0,-72,ink,7);line(0,-72,37,-72,ink,7);line(37,-72,37,-108,ink,5);rr(-27,-132,127,38,11,'#8b9c8d',ink,2);rr(-9,-9,18,54,7,brass);}
+    if(prop==='tablet'){rr(-73,-76,145,170,8,ink);rr(-65,-67,129,151,4,'#f4f0e5');text('DW',-45,-33,27,teal,'Georgia');for(let i=0;i<3;i++)imageCover(['waterperry','fairborn','kaya'][i],-47+i*32,-6,27,60);}
+    if(pose==='point'){bone(point(-4,3),point(34,-18),13,skin);bone(point(0,9),point(16,15),21,skin);oval(-6,9,13,18,skin);}
+    else{rr(-14,-9,29,42,12,skin);oval(-15,5,7,13,skin);for(let i=0;i<3;i++)line(-7+i*6,14,-7+i*6,28,'#9e685446',1);}
+   });
+ }
+ function head(kind,x,y,t,look,mouth,tilt){
+   const male=kind==='wallo',skin=male?'#c89472':'#d5a185',dark=male?'#714f3b':'#834e39';
+   group(x,y,tilt,.82,()=>{
+    if(!male){oval(-4,-10,51,70,dark);oval(-39,-50,28,28,dark);path(q=>{q.moveTo(-57,-55);q.quadraticCurveTo(-81,-37,-59,-16);},null,'#a86b4b',7);}
+    // Tapered jaw, defined brow and modest nose maintain adult proportions.
+    path(q=>{q.moveTo(-41,-40);q.bezierCurveTo(-43,-77,43,-80,44,-34);q.lineTo(40,31);q.quadraticCurveTo(31,66,5,72);q.quadraticCurveTo(-26,65,-36,34);q.closePath();},skin);
+    path(q=>{q.moveTo(-38,-31);q.lineTo(-32,34);q.quadraticCurveTo(-24,52,-8,57);q.lineTo(-20,21);q.lineTo(-18,-46);q.closePath();},male?'#b58162':'#c39076');
+    oval(-40,3,9,16,skin);oval(42,2,8,15,skin);
+    if(male){path(q=>{q.moveTo(-39,-39);q.lineTo(-33,-18);q.lineTo(-30,-54);q.lineTo(31,-54);q.lineTo(43,-25);q.lineTo(42,-55);q.closePath();},dark);}
+    else{path(q=>{q.moveTo(-40,-27);q.quadraticCurveTo(-24,-63,2,-56);q.quadraticCurveTo(38,-37,43,-19);q.lineTo(38,-58);q.quadraticCurveTo(-4,-89,-37,-55);q.closePath();},dark);for(let i=0;i<4;i++)path(q=>{q.moveTo(-29+i*17,-56);q.quadraticCurveTo(-26+i*17,-43,-13+i*17,-39);},null,'#ae7150',2);}
+    const blink=(t+(male?.31:1.93))%4.4<.14;
+    for(const side of [-1,1]){const ex=side*18+look*3,ey=-8;path(q=>{q.moveTo(ex-10,ey);q.quadraticCurveTo(ex,ey-5,ex+10,ey);},null,ink,2.2);if(!blink){oval(ex+look*3,ey+1,3.5,4.3,ink);oval(ex+look*3+1,ey-.3,1.1,1.2,'#eee8dc');}line(ex-10,ey-15,ex+8,ey-17+(side===1?look*2:0),dark,2.5);}
+    path(q=>{q.moveTo(3+look*5,-5);q.lineTo(8+look*8,15);q.quadraticCurveTo(4+look*5,20,-1+look*5,17);},null,'#98644f',1.8);
+    if(male){path(q=>{q.moveTo(-20,29);q.quadraticCurveTo(-6,20,4,27);q.quadraticCurveTo(15,23,26,30);q.quadraticCurveTo(17,39,3,34);q.quadraticCurveTo(-9,37,-20,29);},dark);}
+    if(mouth>.12){oval(4,44,10+mouth*3,2+mouth*6,'#714735');if(mouth>.4)line(-2,41,11,41,'#efe6d7',2);}
+    else path(q=>{q.moveTo(-7,43);q.quadraticCurveTo(5,48,17,42);},null,'#714735',1.8);
+    if(male){path(q=>{q.moveTo(-49,-53);q.quadraticCurveTo(-48,-98,10,-95);q.quadraticCurveTo(47,-89,48,-53);q.closePath();},brass);path(q=>{q.moveTo(-49,-53);q.quadraticCurveTo(12,-64,72,-48);q.quadraticCurveTo(77,-35,39,-41);q.lineTo(-49,-43);q.closePath();},'#97773f');line(-27,-73,12,-84,'#d2b270',3);}
+    else oval(-39,14,3.5,5.5,brass);
+   });
+ }
+ function character(kind,t,settings={}){
+   const male=kind==='wallo',skin=male?'#c89472':'#d5a185',shirt=male?'#d6d0bc':clay,cloth=male?teal:'#c8c1af';
+   const walk=settings.walk||gait(t,0,1,settings.x,settings.x);let x=walk.x,y=settings.y||882;
+   const crouch=settings.crouch||0,lean=settings.lean||0,breath=Math.sin(t*2.1+(male?0:1.2))*1.7;
+   const hipY=-275+crouch,origin=point(0,hipY);
+   const bodyPoint=(u,v)=>rotatePoint(point(u,v+crouch+breath),lean,origin);
+   const leftShoulder=bodyPoint(-62,-469),rightShoulder=bodyPoint(62,-469);
+   const arms=[solveIK(leftShoulder,point(...settings.left),118,111,-1),solveIK(rightShoulder,point(...settings.right),118,111,1)];
+   const legs=walk.feet.map((foot,i)=>solveIK(point(i===0?-32:32,hipY),point(foot.x-x,foot.y-13),134,136,i===0?1:-1));
+   const mouth=male?voiceEnergy(t)*.88:0;
+   const joints={kind,x,y,arms,legs,lean,crouch,feet:walk.feet,walking:walk.walking,props:settings.props||[]};
+   telemetry.push(joints);
+   oval(x,y+6,94-crouch*.1,15,'#233a3b15');
+   group(x,y,0,settings.scale||1,()=>{
+    // Back arm and far leg precede torso so elbow overlaps read spatially.
+    const armDraw=(i)=>{const b=arms[i];bone(b.root,b.joint,39,shirt);bone(b.joint,b.end,27,skin);oval(b.joint.x,b.joint.y,16,16,skin);const sleeve=point(mix(b.root.x,b.joint.x,.77),mix(b.root.y,b.joint.y,.77));bone(b.root,sleeve,43,shirt);line(sleeve.x-8,sleeve.y,sleeve.x+9,sleeve.y,'#ffffff30',2);};
+    armDraw(0);
+    legs.forEach((b,i)=>{bone(b.root,b.joint,58,cloth);bone(b.joint,b.end,43,cloth);oval(b.joint.x,b.joint.y,23,23,cloth);line(b.root.x-8,b.root.y+30,b.joint.x-6,b.joint.y-16,male?'#41635b':'#dfd8c8',2);group(b.end.x,b.end.y+10,walk.walking&&walk.feet[i].y<0?-.12:0,1,()=>{path(q=>{q.moveTo(-24,-20);q.lineTo(20,-20);q.lineTo(27,-9);q.quadraticCurveTo(60,-5,56,14);q.lineTo(-24,14);q.closePath();},male?'#655545':'#626b62');line(-24,14,55,14,'#34413d',5);line(4,-8,25,-7,'#c6b69b',2);});});
+    group(0,hipY,lean,1,()=>{
+     const dy=crouch+breath-hipY;
+     path(q=>{q.moveTo(-63,-478+dy);q.quadraticCurveTo(-82,-460+dy,-71,-417+dy);q.lineTo(-60,-266+dy);q.quadraticCurveTo(0,-235+dy,60,-266+dy);q.lineTo(71,-417+dy);q.quadraticCurveTo(79,-460+dy,58,-479+dy);q.closePath();},shirt);
+     // Structured overall/apron silhouette, stitched seams, pockets and cloth folds.
+     path(q=>{q.moveTo(-50,-425+dy);q.lineTo(-46,-350+dy);q.lineTo(-61,-290+dy);q.lineTo(-60,-253+dy);q.lineTo(61,-253+dy);q.lineTo(61,-290+dy);q.lineTo(46,-350+dy);q.lineTo(48,-425+dy);q.closePath();},cloth);
+     for(const s of [-1,1]){line(s*44,-472+dy,s*38,-405+dy,cloth,17);oval(s*39,-414+dy,4.8,4.8,brass);line(s*49,-328+dy,s*41,-312+dy,male?'#59756b':'#a9a18f',2);}
+     rr(-27,-401+dy,54,56,4,male?'#254540':'#bab39f');path(q=>{q.moveTo(-23,-388+dy);q.quadraticCurveTo(0,-376+dy,24,-388+dy);},null,male?'#6a8778':'#e5dfd0',1.5);
+     line(-50,-266+dy,52,-266+dy,male?'#1e403b':'#aca590',3);
+     if(male){rr(-66,-321+dy,20,67,3,'#8d7452');line(-56,-342+dy,-56,-282+dy,brass,6);}
+     path(q=>{q.moveTo(-17,-509+dy);q.lineTo(-18,-474+dy);q.quadraticCurveTo(0,-454+dy,21,-474+dy);q.lineTo(18,-509+dy);q.closePath();},skin);
+    });
+    const hp=bodyPoint(0,-559);head(kind,hp.x,hp.y,t,settings.look||0,mouth,(settings.headTilt||0)+lean*.28);
+    armDraw(1);
+    arms.forEach((b,i)=>hand(b.end.x,b.end.y,(settings.wrists||[0,0])[i],(settings.gestures||['open','open'])[i],skin,(settings.props||[])[i]));
+   });
+   return joints;
+ }
+ function room(t){
+   rect(-350,-300,1800,1200,'#dfdbd1');rect(-350,765,1800,450,'#c9c2b1');
+   rect(20,0,240,706,'#e9e7dc');for(let i=0;i<4;i++)line(22+i*59,0,22+i*59,704,'#f7f4e9',7);
+   rect(318,26,647,727,'#c7bfae');rect(329,37,624,704,'#efede3');
+   if(ep===1){const a=ease(t,5.1,8);rect(343,49,596,678,'#aab7a4');c.save();c.beginPath();c.rect(343,49,596*a,678);c.clip();imageCover(E.product,343,49,596,678);c.restore();}
+   else imageCover(E.product,343,49,596,678);
+   line(340,743,943,743,'#faf7eb',4);line(-350,765,1450,765,'#a79f8d',2);
+   for(let i=0;i<6;i++)line(-200,814+i*54,1400,814+i*54,'#b6ad9a',1.4);
+   line(91,765,-170,1220,'#b6ad9a',1.4);line(880,765,1130,1220,'#b6ad9a',1.4);
+   // Light and shadow move gently with the camera, not with an arbitrary pulse.
+   path(q=>{q.moveTo(40,706);q.lineTo(263,706);q.lineTo(672,1120);q.lineTo(412,1120);q.closePath();},'#fcf7df28');
+   // A sample ledge ties the real products to the working material library.
+   rect(404,685,479,12,'#b29d7a');for(let i=0;i<3;i++)sample(P[(i+ep)%P.length].id,468+i*115,622,88,116,(i-1)*.045);
+ }
+ function documentPanel(t){
+   if(ep!==0)return;
+   const a=pulse(t,4.2,4.9,14.3,15.0);if(!a)return;
+   group(640,177+(1-a)*28,0,.88,()=>{
+    rr(-239,-168,478,336,2,'#f7f4e9',ink,1.5);text('BEFORE YOU SPECIFY',-210,-126,24,teal);
+    const labels=['PRODUCT / TEST REPORT','CLASSIFICATION','SUBSTRATE + ADHESIVE'];
+    labels.forEach((label,i)=>{const done=t>5.0+i*2.8;line(-209,-61+i*77,-182,-61+i*77,done?brass:'#bac0b5',3);text(label,-163,-52+i*77,21,ink);});
+   });
+ }
+ function swatchBoard(t){
+   if(ep!==3&&ep!==4)return;
+   const a=pulse(t,6.4,7.1,13.4,14);if(!a)return;
+   group(560,275+(1-a)*25,0,.95,()=>{
+    rr(-199,-139,398,278,2,'#f5f0e4',ink,1);sample(E.product,-83,-1,155,220,0);
+    rect(28,-111,137,83,'#c6bea9');rect(28,-16,137,69,'#8c9985');rect(28,64,137,43,'#b89672');
+    text(ep===3?'COMPARE YOUR FINISHES':'YOUR PROJECT SHORTLIST',-192,-162,23,ink);
+   });
+ }
+ function actionNotes(t){
+   if(ep!==2)return;const a=pulse(t,8,8.5,14.2,14.8);if(!a)return;
+   ['ABRASION','CLEANABILITY','CARE'].forEach((s,i)=>{const show=ease(t,8+i*.7,8.4+i*.7);if(show){rect(440,120+i*77,440,58,'#f4f0e6');text(s,468,159+i*77,27,ink);}});
+ }
+ function performances(t){
+   let wwalk=gait(t,.2,2.9,126,253),pwalk=gait(t,.5,3.1,949,801);
+   const w={walk:wwalk,left:[-85,-279],right:[149,-396],wrists:[-.1,-.36],props:[],gestures:['open','open'],look:.6,lean:Math.sin(t*.8)*.012};
+   const p={walk:pwalk,left:[-130,-402],right:[86,-275],wrists:[.14,.08],props:['sample'],look:-.6,lean:Math.sin(t*.73+1)*.013};
+   // Presenter gestures are deliberately different for each spoken clause.
+   const gesture=key(t,[[0,100,-285],[2.8,145,-411],[4.4,165,-440],[5.2,120,-367],[8.7,161,-441],[9.6,149,-378],[12.8,180,-432],[14,125,-360],[17,152,-419],[19.8,126,-383]]);
+   w.right=gesture;w.wrists[1]=key(t,[[0,0],[3,-.5],[5.2,-.15],[8,-.42],[11,.1],[14,-.4],[18,-.2]])[0];
+   w.gestures[1]=t>4.5&&t<8.8?'point':'open';w.headTilt=Math.sin(t*.65)*.022;w.look=t>4&&t<9?1:.1;
+   if(t<3){w.right=[90-Math.sin(t*4)*40,-277];w.left=[-90+Math.sin(t*4)*40,-277];p.left=[-90+Math.sin(t*4)*24,-280];p.props=[];}
+   if(ep===0){p.props=['report'];p.left=key(t,[[0,-85,-280],[3,-130,-365],[6,-131,-420],[10,-153,-445],[14,-125,-384],[19,-108,-376]]);p.wrists[0]=-.14;p.right=key(t,[[0,90,-277],[8,98,-320],[11,-10,-377],[14,93,-278]]);}
+   if(ep===1){
+    p.props=t<8?['roller']:['sample'];p.left=key(t,[[0,-88,-283],[3,-129,-447],[4.5,-142,-495],[5.2,-143,-411],[6,-146,-495],[7,-145,-397],[8,-100,-367],[11,-139,-436],[15,-103,-361]]);p.wrists[0]=.07;
+    if(t>9.3){p.walk=gait(t,9.3,11.4,801,870);p.look=-1;p.lean=-.06*ease(t,10,11);}
+    w.props=['sample'];w.left=key(t,[[0,-87,-282],[4,-98,-291],[6,-39,-389],[8,-30,-433],[12,-59,-386],[17,-78,-320]]);w.wrists[0]=-.08;
+   }
+   if(ep===2){
+    p.crouch=pulse(t,4,5.6,8.8,10.2)*113;p.lean=-pulse(t,4,5.4,9,10.3)*.10;
+    const wipe=pulse(t,5,5.8,8.2,9);p.props=['cloth'];p.left=[-147-wipe*17,-389+p.crouch+wipe*Math.sin(t*6)*42];p.wrists[0]=-.5;
+    p.right=[85,-284+p.crouch];p.look=-1;
+    w.props=['sample'];w.left=[-45,-371];w.wrists[0]=-.18;
+   }
+   if(ep===3){
+    p.props=['sample'];p.left=key(t,[[0,-92,-280],[3,-100,-366],[6,-138,-440],[10,-144,-478],[14,-126,-418],[18,-93,-340]]);p.wrists[0]=key(t,[[0,0],[3,-.08],[7,.22],[10,-.18],[13,.16],[17,0]])[0];
+    w.props=['fan'];w.left=key(t,[[0,-90,-280],[4,-33,-365],[7,-44,-400],[12,-24,-389],[16,-44,-346]]);w.wrists[0]=-.13;
+   }
+   if(ep===4){
+    w.props=['tablet'];w.left=[-43,-341];w.right=key(t,[[0,101,-280],[3,143,-413],[6,158,-444],[8.5,65,-361],[10,34,-340],[11.6,20,-356],[13,144,-411],[17,160,-444],[19,112,-374]]);w.gestures[1]=t>8&&t<12?'point':'open';
+    p.props=['fan'];p.left=[-129,-400];p.right=key(t,[[0,89,-280],[6,86,-280],[9,-18,-384],[12,76,-285],[17,100,-382],[19,86,-297]]);
+   }
+   // Camera-right actor is drawn first so the presenter remains the visual lead.
+   character('pippa',t,p);character('wallo',t,w);
+ }
+ function overlay(t){
+   rect(0,0,1080,437,chalk);rect(0,1437,1080,483,chalk);
+   text('DESIGNER WALLCOVERINGS',72,96,25,ink);text('WALLO PRESENTS',1008,96,21,teal,'Verdana','right');
+   line(72,124,1008,124,'#b9b6aa',1);text(E.kicker,72,178,24,teal);
+   E.title.split('\n').forEach((s,i)=>text(s,69,270+i*87,73,ink,'Georgia'));
+   text('MATERIAL NOTES',73,411,22,teal);text('WITH WALLO & PIPPA',1006,411,20,teal,'Verdana','right');
+   const product=P.find(p=>p.id===E.product);text(product.label,72,1481,25,ink);
+   line(72,1505,1008,1505,'#b9b6aa',1);
+   const segment=(E.audio?.segments||[]).filter(s=>t>=s.start).at(-1);
+   const caption=segment?E.captions[segment.index]:'A wallcovering presentation with Wallo.';
+   wrap(caption,72,1570,930,38,ink,'Verdana',55);
+   if(t>=16.8)text(E.takeaway,72,1738,37,teal,'Georgia');
+   wrap(E.note,72,1794,926,24,'#58635b','Verdana',34);
+   text('designerwallcoverings.com',72,1870,25,teal);
+   text(String(ep+1).padStart(2,'0')+' / 05',1008,1870,22,teal,'Verdana','right');
+   rect(72,1894,936,3,'#c9c7bb');rect(72,1894,936*clamp(t/20),3,teal);
+ }
+ function draw(t){
+   telemetry=[];rect(0,0,1080,1920,chalk);
+   c.save();c.beginPath();c.rect(0,437,1080,1000);c.clip();
+   const zoom=1+ease(t,3.3,5)*.08-ease(t,14.2,16)*.06;
+   c.translate(540,950);c.scale(zoom,zoom);c.translate(-540,-487);
+   room(t);documentPanel(t);swatchBoard(t);actionNotes(t);performances(t);
+   c.restore();overlay(t);window.__rigState=telemetry;
+ }
+ const driver={t:0};window.__timelines=window.__timelines||{};
+ window.__timelines.main=gsap.timeline({paused:true}).to(driver,{t:20,duration:20,ease:'none',onUpdate:()=>draw(driver.t)},0);
+ window.__draw=draw;
+ window.__ready=Promise.all(P.map(p=>new Promise((resolve,reject)=>{const im=new Image();images[p.id]=im;im.onload=resolve;im.onerror=()=>reject(new Error('Missing product '+p.id));im.src=p.local;}))).then(()=>document.fonts.ready).then(()=>{draw(driver.t);return true;});
+ draw(0);
+})();
+</script></body></html>
\ No newline at end of file
diff --git a/series2/mcp-proof.mjs b/series2/mcp-proof.mjs
new file mode 100644
index 0000000..96ab8f8
--- /dev/null
+++ b/series2/mcp-proof.mjs
@@ -0,0 +1,33 @@
+// Uses the installed Chrome DevTools MCP collection in its documented isolated mode.
+// The shared MCP browser profile belongs to another session; never stop it.
+import {Client} from '/Users/macstudio3/.npm/_npx/99e3e8e098e94b9f/node_modules/@modelcontextprotocol/sdk/dist/esm/client/index.js';
+import {StdioClientTransport} from '/Users/macstudio3/.npm/_npx/99e3e8e098e94b9f/node_modules/@modelcontextprotocol/sdk/dist/esm/client/stdio.js';
+import {ListRootsRequestSchema} from '/Users/macstudio3/.npm/_npx/99e3e8e098e94b9f/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js';
+import fs from 'node:fs';
+import path from 'node:path';
+import assert from 'node:assert/strict';
+const root=import.meta.dirname;
+const evidence={tool:'chrome-devtools-mcp',version:'1.8.0',mode:'isolated headless',ticket:'TK-11288',at:new Date().toISOString(),calls:[]};
+const client=new Client({name:'wallo-presentation-proof',version:'1.0.0'},{capabilities:{roots:{listChanged:false}}});
+client.setRequestHandler(ListRootsRequestSchema,()=>({roots:[{uri:'file://'+root,name:'Wallo and Pippa video project'}]}));
+const transport=new StdioClientTransport({command:process.execPath,args:['/Users/macstudio3/.npm/_npx/15c61037b1978c83/node_modules/chrome-devtools-mcp/build/src/bin/chrome-devtools-mcp.js','--isolated','--headless','--no-usage-statistics','--no-performance-crux','--viewport=1440x1400'],stderr:'pipe'});
+let stderr='';
+async function call(name,args){let result=await client.callTool({name,arguments:args},undefined,{timeout:30000});evidence.calls.push({name,args,result});if(result.isError)throw new Error(JSON.stringify(result));return result;}
+try{
+ await client.connect(transport);transport.stderr?.on('data',b=>{stderr+=b.toString();});
+ const catalog=await client.listTools();evidence.availableTools=catalog.tools.map(x=>x.name);
+ const created=await call('new_page',{url:'file://'+path.join(root,'watch.html')});
+ const joined=(created.content||[]).filter(c=>c.type==='text').map(c=>c.text).join('\n');
+ const match=joined.match(/(?:^|\n)(\d+):[^\n]*\[selected\]/);if(!match)throw new Error('Cannot read page id from '+joined);
+ const pageId=Number(match[1]);
+ await call('evaluate_script',{pageId,function:'async () => { await Promise.all(Array.from(document.images, img => img.decode())); return {title:document.title, videos:document.querySelectorAll("video").length, products:document.querySelectorAll(".product img").length}; }'});
+ const result=await call('evaluate_script',{pageId,function:'async () => { const result=[]; for(const v of document.querySelectorAll("video")){v.muted=true;await v.play(); await new Promise((resolve,reject)=>{let attempts=0;const timer=setInterval(()=>{if(v.currentTime>.4){clearInterval(timer);resolve();}else if(++attempts>150){clearInterval(timer);reject(new Error("Video did not advance: "+v.dataset.slug));}},100);});v.pause();result.push({slug:v.dataset.slug,duration:v.duration,currentTime:v.currentTime,width:v.videoWidth,height:v.videoHeight,error:v.error?.message||null});}return result; }'});
+ const serialized=JSON.stringify(result);assert(serialized.includes('04-fire-rated'));assert(serialized.includes('08-service-at-dw'));assert(serialized.includes('wallo-wallcovering-presentation'));
+ await call('take_screenshot',{pageId,filePath:path.join(root,'verification','mcp-gallery.png'),fullPage:true});
+ // Exercise the chapter navigation in the real review page.
+ const chapter=await call('evaluate_script',{pageId,function:'async () => { const v=document.querySelector("#presentation"); v.muted=true; document.querySelector("[data-jump=\\"80\\"]").click(); await new Promise(r=>setTimeout(r,350)); v.pause(); return {time:v.currentTime,links:[...document.querySelectorAll(".product a")].map(a=>a.href)}; }'});
+ assert(JSON.stringify(chapter).includes('designerwallcoverings.com/products/'));
+ evidence.verdict='PASS';console.log('Chrome DevTools MCP: six video playbacks and chapter navigation PASS.');
+}finally{
+ await client.close();fs.writeFileSync(path.join(root,'verification','mcp-proof.json'),JSON.stringify(evidence,null,2));fs.writeFileSync(path.join(root,'verification','mcp-stderr.txt'),stderr);
+}
diff --git a/series2/meme-sources.json b/series2/meme-sources.json
new file mode 100644
index 0000000..5e68547
--- /dev/null
+++ b/series2/meme-sources.json
@@ -0,0 +1,72 @@
+[
+  {
+    "id": "01-paste-gravity",
+    "top": "THE PASTE WAS LEVEL.",
+    "bottom": "THE BUCKET WAS NOT.",
+    "scene": "An open paste bucket has tipped off a low pasting table, a single spectacular ribbon of white paste suspended midair; Wallo stares down at his freshly pasted boots; Pippa freezes holding a clean sponge. Beautiful real workroom, drop cloths, botanical wallpaper.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-093d3dce-bd65-47a2-a62f-3df1e48942ae.png"
+  },
+  {
+    "id": "02-one-inch",
+    "top": "JUST ONE MORE INCH.",
+    "bottom": "FAMOUS LAST WORDS.",
+    "scene": "Wallo on the middle rung of an open A-frame stepladder, comically stretching one arm toward a tiny missed corner; Pippa calmly slides the ladder closer from the floor. Exaggerated expression of recognizing the obvious, no injury or fall. Spatially correct ladder.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-8b8635f3-455c-4135-8217-094fe5d835a6.png"
+  },
+  {
+    "id": "03-pattern-match",
+    "top": "THE WALL IS PERFECT.",
+    "bottom": "THE LAST STRIP HAS QUESTIONS.",
+    "scene": "Wallo and Pippa study a crisp botanical wallpapered wall; the final strip is visibly half a repeat out of alignment. Wallo holds a tape measure like it has betrayed him. Realistic worksite and distinctly mismatched seam.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-33c283ec-43a0-484a-82fa-7e220c2fedc1.png"
+  },
+  {
+    "id": "04-just-five-minutes",
+    "top": "JUST A FIVE-MINUTE TOUCH-UP.",
+    "bottom": "THREE HOURS AGO.",
+    "scene": "A room mostly finished, Wallo kneeling beside one tiny corner lifting with tools neatly around him, Pippa looks at a clock and holds two cold coffees. Warm late-afternoon light. Dry deadpan humor.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-9c73f8a8-deef-4ab3-9b2e-94837e977a02.png"
+  },
+  {
+    "id": "05-paste-footprints",
+    "top": "FRESHLY CLEANED FLOOR.",
+    "bottom": "SIGNED BY THE INSTALLER.",
+    "scene": "Wallo confidently walks away from the pasting table leaving a trail of white paste bootprints across a dark floor; Pippa stands behind with mop and a raised eyebrow. Wallo finally looks back. No injuries.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-e2e27989-327f-42bc-9c52-5e587812646b.png"
+  },
+  {
+    "id": "06-roll-with-it",
+    "top": "I SAID BRING A DROP CLOTH.",
+    "bottom": "HE BROUGHT A WHOLE WALL.",
+    "scene": "A long unpasted wallpaper roll has unspooled around Wallo's ankles and across the floor like an absurd elegant carpet; Pippa holds the actual folded drop cloth. Wallo stands still, bemused, not falling.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-039d3af7-3fe1-4be8-9d24-6d8ef211ec31.png"
+  },
+  {
+    "id": "07-ceiling-paste",
+    "top": "AIMING FOR THE WALL.",
+    "bottom": "THE CEILING FELT LEFT OUT.",
+    "scene": "Wallo lifts a loaded paste brush too quickly and small white paste flecks arc upward to dot a pristine ceiling; Pippa catches one on her cap-less auburn bun and looks unimpressed. No bucket balancing above heads.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-80359971-c51c-4913-934b-275285ce3e29.png"
+  },
+  {
+    "id": "08-ladder-parking",
+    "top": "THE LADDER FITS.",
+    "bottom": "THE REST OF US DON'T.",
+    "scene": "A large open stepladder fills a tiny powder room between toilet and vanity. Wallo and Pippa squeeze beside it with wallpaper tools and look at each other. Anatomy plausible, no one atop toilet, dry visual comedy.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-db28bf56-d662-46f2-b55c-619bb1f47c0c.png"
+  },
+  {
+    "id": "09-perfect-cut",
+    "top": "MEASURE TWICE.",
+    "bottom": "STILL CUT THE WRONG END.",
+    "scene": "Wallo holds a beautifully cut wallpaper strip beside its matching offcut, realizing the pattern runs the wrong way; Pippa points silently to the directional mark on the roll. Scissors resting on table, no sharp tools pointed at anyone.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-16448e68-8fe3-418b-a07b-fb8bed046081.png"
+  },
+  {
+    "id": "10-last-roll",
+    "top": "THE LAST ROLL.",
+    "bottom": "NOW A TEAM-BUILDING EXERCISE.",
+    "scene": "Wallo and Pippa sit on the floor looking at a remaining bare strip of wall only a few inches wide and an empty cardboard wallpaper tube between them. Symmetric deadpan exhausted pose, beautiful almost-finished room.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-693f5f0b-485e-499d-abc4-0a7a6984af35.png"
+  }
+]
diff --git a/series2/memes.mjs b/series2/memes.mjs
new file mode 100644
index 0000000..c4aa551
--- /dev/null
+++ b/series2/memes.mjs
@@ -0,0 +1,21 @@
+import fs from 'node:fs';
+import path from 'node:path';
+import crypto from 'node:crypto';
+import {execFileSync} from 'node:child_process';
+const root=import.meta.dirname,dir=path.join(root,'memes');
+fs.mkdirSync(dir,{recursive:true});
+const source=JSON.parse(fs.readFileSync(path.join(root,'meme-sources.json')));
+const manifest=source.map(m=>{
+ const data=fs.readFileSync(m.generatedSource),file=m.id+'.png';
+ if(data.toString('hex',0,8)!=='89504e470d0a1a0a')throw Error('Invalid PNG '+m.id);
+ fs.copyFileSync(m.generatedSource,path.join(dir,file));
+ return {...m,file,width:data.readUInt32BE(16),height:data.readUInt32BE(20),bytes:data.length,sha256:crypto.createHash('sha256').update(data).digest('hex'),created_at:fs.statSync(m.generatedSource).birthtime.toISOString(),provenance:'Generated with image_gen from original Wallo and Pippa character reference. Wallpaper in these cartoons is illustrative, not a product representation.'};
+});
+fs.writeFileSync(path.join(dir,'manifest.json'),JSON.stringify(manifest,null,2)+'\n');
+const esc=s=>s.replaceAll('&','&amp;').replaceAll('<','&lt;').replaceAll('"','&quot;');
+fs.writeFileSync(path.join(dir,'index.html'),`<!doctype html><html lang="en"><head><meta charset="utf-8"><link rel="icon" href="data:,"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Installer follies — Wallo & Pippa</title><style>
+*{box-sizing:border-box}body{margin:0;background:#eeeae2;color:#243a3b;font:16px/1.6 system-ui,sans-serif}main{max-width:1320px;margin:auto;padding:32px 24px 80px}nav{display:flex;gap:24px;flex-wrap:wrap;border-bottom:1px solid #b9b6aa;padding-bottom:18px;font-size:13px}a{color:#284f4b;text-underline-offset:4px}h1{font:clamp(42px,6vw,80px)/1.08 Georgia,serif;letter-spacing:-.04em;margin:40px 0 16px}header p{max-width:640px}.download{display:inline-block;background:#284f4b;color:#fff;padding:12px 22px;text-decoration:none;margin:8px 0 32px}.memes{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:42px 26px}figure{margin:0}figure img{display:block;width:100%;height:auto;box-shadow:0 8px 25px #243a3b14}figcaption{display:flex;justify-content:space-between;gap:14px;padding:12px 0;font-size:14px}.note{margin-top:40px;font-size:13px;max-width:720px}@media(max-width:680px){.memes{grid-template-columns:1fr}main{padding:24px 18px 50px}}
+</style></head><body><main><nav><a href="../watch.html">Wallo’s wallcovering presentation</a><a href="../../watch.html">Original animated shorts</a><span>WALLO &amp; PIPPA / INSTALLER FOLLIES</span></nav><header><h1>It was going so well.</h1><p>Ten familiar decorating disasters. Paste with ambitions. Ladders with parking problems. One last strip with an attitude.</p><a class="download" href="installer-follies-10-memes.zip" download>Download all 10 memes · ZIP</a></header><div class="memes">${manifest.map((m,i)=>`<figure id="${m.id}"><a href="${m.file}" target="_blank" rel="noopener" aria-label="Open meme ${i+1} full size"><img src="${m.file}" width="${m.width}" height="${m.height}" alt="${esc(m.top+' '+m.bottom)}" loading="${i<2?'eager':'lazy'}"></a><figcaption><span>${String(i+1).padStart(2,'0')} / ${m.width} × ${m.height}</span><a href="${m.file}" download>Download PNG</a></figcaption></figure>`).join('')}</div><p class="note">Original illustrated comedy featuring Wallo &amp; Pippa. Wallpaper in these cartoons is illustrative. For real products from Designer Wallcoverings, see Wallo’s presentation. Created September 5, 2026.</p></main></body></html>`);
+fs.writeFileSync(path.join(dir,'README.txt'),'Wallo & Pippa — Installer Follies\nTen original square PNG memes.\n\n'+manifest.map(m=>m.file+'\n'+m.top+'\n'+m.bottom).join('\n\n')+'\n\nCreated September 5, 2026. Original generated illustrations; wallpaper is illustrative.\n');
+execFileSync('zip',['-q','-j',path.join(dir,'installer-follies-10-memes.zip'),...manifest.map(m=>path.join(dir,m.file)),path.join(dir,'README.txt')]);
+console.log('Packaged '+manifest.length+' original memes and ZIP.');
diff --git a/series2/memes/01-paste-gravity.png b/series2/memes/01-paste-gravity.png
new file mode 100644
index 0000000..220c282
Binary files /dev/null and b/series2/memes/01-paste-gravity.png differ
diff --git a/series2/memes/02-one-inch.png b/series2/memes/02-one-inch.png
new file mode 100644
index 0000000..61b5a77
Binary files /dev/null and b/series2/memes/02-one-inch.png differ
diff --git a/series2/memes/03-pattern-match.png b/series2/memes/03-pattern-match.png
new file mode 100644
index 0000000..303f973
Binary files /dev/null and b/series2/memes/03-pattern-match.png differ
diff --git a/series2/memes/04-just-five-minutes.png b/series2/memes/04-just-five-minutes.png
new file mode 100644
index 0000000..09e5364
Binary files /dev/null and b/series2/memes/04-just-five-minutes.png differ
diff --git a/series2/memes/05-paste-footprints.png b/series2/memes/05-paste-footprints.png
new file mode 100644
index 0000000..d34cb60
Binary files /dev/null and b/series2/memes/05-paste-footprints.png differ
diff --git a/series2/memes/06-roll-with-it.png b/series2/memes/06-roll-with-it.png
new file mode 100644
index 0000000..eb5dd6d
Binary files /dev/null and b/series2/memes/06-roll-with-it.png differ
diff --git a/series2/memes/07-ceiling-paste.png b/series2/memes/07-ceiling-paste.png
new file mode 100644
index 0000000..996fd36
Binary files /dev/null and b/series2/memes/07-ceiling-paste.png differ
diff --git a/series2/memes/08-ladder-parking.png b/series2/memes/08-ladder-parking.png
new file mode 100644
index 0000000..d2e8b52
Binary files /dev/null and b/series2/memes/08-ladder-parking.png differ
diff --git a/series2/memes/09-perfect-cut.png b/series2/memes/09-perfect-cut.png
new file mode 100644
index 0000000..1402372
Binary files /dev/null and b/series2/memes/09-perfect-cut.png differ
diff --git a/series2/memes/10-last-roll.png b/series2/memes/10-last-roll.png
new file mode 100644
index 0000000..3143e2f
Binary files /dev/null and b/series2/memes/10-last-roll.png differ
diff --git a/series2/memes/README.txt b/series2/memes/README.txt
new file mode 100644
index 0000000..0124f7b
--- /dev/null
+++ b/series2/memes/README.txt
@@ -0,0 +1,44 @@
+Wallo & Pippa — Installer Follies
+Ten original square PNG memes.
+
+01-paste-gravity.png
+THE PASTE WAS LEVEL.
+THE BUCKET WAS NOT.
+
+02-one-inch.png
+JUST ONE MORE INCH.
+FAMOUS LAST WORDS.
+
+03-pattern-match.png
+THE WALL IS PERFECT.
+THE LAST STRIP HAS QUESTIONS.
+
+04-just-five-minutes.png
+JUST A FIVE-MINUTE TOUCH-UP.
+THREE HOURS AGO.
+
+05-paste-footprints.png
+FRESHLY CLEANED FLOOR.
+SIGNED BY THE INSTALLER.
+
+06-roll-with-it.png
+I SAID BRING A DROP CLOTH.
+HE BROUGHT A WHOLE WALL.
+
+07-ceiling-paste.png
+AIMING FOR THE WALL.
+THE CEILING FELT LEFT OUT.
+
+08-ladder-parking.png
+THE LADDER FITS.
+THE REST OF US DON'T.
+
+09-perfect-cut.png
+MEASURE TWICE.
+STILL CUT THE WRONG END.
+
+10-last-roll.png
+THE LAST ROLL.
+NOW A TEAM-BUILDING EXERCISE.
+
+Created September 5, 2026. Original generated illustrations; wallpaper is illustrative.
diff --git a/series2/memes/index.html b/series2/memes/index.html
new file mode 100644
index 0000000..3b4d2f0
--- /dev/null
+++ b/series2/memes/index.html
@@ -0,0 +1,3 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><link rel="icon" href="data:,"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Installer follies — Wallo & Pippa</title><style>
+*{box-sizing:border-box}body{margin:0;background:#eeeae2;color:#243a3b;font:16px/1.6 system-ui,sans-serif}main{max-width:1320px;margin:auto;padding:32px 24px 80px}nav{display:flex;gap:24px;flex-wrap:wrap;border-bottom:1px solid #b9b6aa;padding-bottom:18px;font-size:13px}a{color:#284f4b;text-underline-offset:4px}h1{font:clamp(42px,6vw,80px)/1.08 Georgia,serif;letter-spacing:-.04em;margin:40px 0 16px}header p{max-width:640px}.download{display:inline-block;background:#284f4b;color:#fff;padding:12px 22px;text-decoration:none;margin:8px 0 32px}.memes{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:42px 26px}figure{margin:0}figure img{display:block;width:100%;height:auto;box-shadow:0 8px 25px #243a3b14}figcaption{display:flex;justify-content:space-between;gap:14px;padding:12px 0;font-size:14px}.note{margin-top:40px;font-size:13px;max-width:720px}@media(max-width:680px){.memes{grid-template-columns:1fr}main{padding:24px 18px 50px}}
+</style></head><body><main><nav><a href="../watch.html">Wallo’s wallcovering presentation</a><a href="../../watch.html">Original animated shorts</a><span>WALLO &amp; PIPPA / INSTALLER FOLLIES</span></nav><header><h1>It was going so well.</h1><p>Ten familiar decorating disasters. Paste with ambitions. Ladders with parking problems. One last strip with an attitude.</p><a class="download" href="installer-follies-10-memes.zip" download>Download all 10 memes · ZIP</a></header><div class="memes"><figure id="01-paste-gravity"><a href="01-paste-gravity.png" target="_blank" rel="noopener" aria-label="Open meme 1 full size"><img src="01-paste-gravity.png" width="1254" height="1254" alt="THE PASTE WAS LEVEL. THE BUCKET WAS NOT." loading="eager"></a><figcaption><span>01 / 1254 × 1254</span><a href="01-paste-gravity.png" download>Download PNG</a></figcaption></figure><figure id="02-one-inch"><a href="02-one-inch.png" target="_blank" rel="noopener" aria-label="Open meme 2 full size"><img src="02-one-inch.png" width="1254" height="1254" alt="JUST ONE MORE INCH. FAMOUS LAST WORDS." loading="eager"></a><figcaption><span>02 / 1254 × 1254</span><a href="02-one-inch.png" download>Download PNG</a></figcaption></figure><figure id="03-pattern-match"><a href="03-pattern-match.png" target="_blank" rel="noopener" aria-label="Open meme 3 full size"><img src="03-pattern-match.png" width="1254" height="1254" alt="THE WALL IS PERFECT. THE LAST STRIP HAS QUESTIONS." loading="lazy"></a><figcaption><span>03 / 1254 × 1254</span><a href="03-pattern-match.png" download>Download PNG</a></figcaption></figure><figure id="04-just-five-minutes"><a href="04-just-five-minutes.png" target="_blank" rel="noopener" aria-label="Open meme 4 full size"><img src="04-just-five-minutes.png" width="1254" height="1254" alt="JUST A FIVE-MINUTE TOUCH-UP. THREE HOURS AGO." loading="lazy"></a><figcaption><span>04 / 1254 × 1254</span><a href="04-just-five-minutes.png" download>Download PNG</a></figcaption></figure><figure id="05-paste-footprints"><a href="05-paste-footprints.png" target="_blank" rel="noopener" aria-label="Open meme 5 full size"><img src="05-paste-footprints.png" width="1254" height="1254" alt="FRESHLY CLEANED FLOOR. SIGNED BY THE INSTALLER." loading="lazy"></a><figcaption><span>05 / 1254 × 1254</span><a href="05-paste-footprints.png" download>Download PNG</a></figcaption></figure><figure id="06-roll-with-it"><a href="06-roll-with-it.png" target="_blank" rel="noopener" aria-label="Open meme 6 full size"><img src="06-roll-with-it.png" width="1254" height="1254" alt="I SAID BRING A DROP CLOTH. HE BROUGHT A WHOLE WALL." loading="lazy"></a><figcaption><span>06 / 1254 × 1254</span><a href="06-roll-with-it.png" download>Download PNG</a></figcaption></figure><figure id="07-ceiling-paste"><a href="07-ceiling-paste.png" target="_blank" rel="noopener" aria-label="Open meme 7 full size"><img src="07-ceiling-paste.png" width="1254" height="1254" alt="AIMING FOR THE WALL. THE CEILING FELT LEFT OUT." loading="lazy"></a><figcaption><span>07 / 1254 × 1254</span><a href="07-ceiling-paste.png" download>Download PNG</a></figcaption></figure><figure id="08-ladder-parking"><a href="08-ladder-parking.png" target="_blank" rel="noopener" aria-label="Open meme 8 full size"><img src="08-ladder-parking.png" width="1254" height="1254" alt="THE LADDER FITS. THE REST OF US DON'T." loading="lazy"></a><figcaption><span>08 / 1254 × 1254</span><a href="08-ladder-parking.png" download>Download PNG</a></figcaption></figure><figure id="09-perfect-cut"><a href="09-perfect-cut.png" target="_blank" rel="noopener" aria-label="Open meme 9 full size"><img src="09-perfect-cut.png" width="1254" height="1254" alt="MEASURE TWICE. STILL CUT THE WRONG END." loading="lazy"></a><figcaption><span>09 / 1254 × 1254</span><a href="09-perfect-cut.png" download>Download PNG</a></figcaption></figure><figure id="10-last-roll"><a href="10-last-roll.png" target="_blank" rel="noopener" aria-label="Open meme 10 full size"><img src="10-last-roll.png" width="1254" height="1254" alt="THE LAST ROLL. NOW A TEAM-BUILDING EXERCISE." loading="lazy"></a><figcaption><span>10 / 1254 × 1254</span><a href="10-last-roll.png" download>Download PNG</a></figcaption></figure></div><p class="note">Original illustrated comedy featuring Wallo &amp; Pippa. Wallpaper in these cartoons is illustrative. For real products from Designer Wallcoverings, see Wallo’s presentation. Created September 5, 2026.</p></main></body></html>
\ No newline at end of file
diff --git a/series2/memes/manifest.json b/series2/memes/manifest.json
new file mode 100644
index 0000000..02effa7
--- /dev/null
+++ b/series2/memes/manifest.json
@@ -0,0 +1,142 @@
+[
+  {
+    "id": "01-paste-gravity",
+    "top": "THE PASTE WAS LEVEL.",
+    "bottom": "THE BUCKET WAS NOT.",
+    "scene": "An open paste bucket has tipped off a low pasting table, a single spectacular ribbon of white paste suspended midair; Wallo stares down at his freshly pasted boots; Pippa freezes holding a clean sponge. Beautiful real workroom, drop cloths, botanical wallpaper.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-093d3dce-bd65-47a2-a62f-3df1e48942ae.png",
+    "file": "01-paste-gravity.png",
+    "width": 1254,
+    "height": 1254,
+    "bytes": 2536523,
+    "sha256": "d009f4d10d7e20dcc363c24708ec00aa34db16976998fbff6115f45c0e602fbe",
+    "created_at": "2026-09-05T07:30:16.249Z",
+    "provenance": "Generated with image_gen from original Wallo and Pippa character reference. Wallpaper in these cartoons is illustrative, not a product representation."
+  },
+  {
+    "id": "02-one-inch",
+    "top": "JUST ONE MORE INCH.",
+    "bottom": "FAMOUS LAST WORDS.",
+    "scene": "Wallo on the middle rung of an open A-frame stepladder, comically stretching one arm toward a tiny missed corner; Pippa calmly slides the ladder closer from the floor. Exaggerated expression of recognizing the obvious, no injury or fall. Spatially correct ladder.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-8b8635f3-455c-4135-8217-094fe5d835a6.png",
+    "file": "02-one-inch.png",
+    "width": 1254,
+    "height": 1254,
+    "bytes": 2347754,
+    "sha256": "96398bdd05fd8b31cd805201be76e529b35e15168b4cbf10d6a513eb2d69cb52",
+    "created_at": "2026-09-05T07:29:39.006Z",
+    "provenance": "Generated with image_gen from original Wallo and Pippa character reference. Wallpaper in these cartoons is illustrative, not a product representation."
+  },
+  {
+    "id": "03-pattern-match",
+    "top": "THE WALL IS PERFECT.",
+    "bottom": "THE LAST STRIP HAS QUESTIONS.",
+    "scene": "Wallo and Pippa study a crisp botanical wallpapered wall; the final strip is visibly half a repeat out of alignment. Wallo holds a tape measure like it has betrayed him. Realistic worksite and distinctly mismatched seam.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-33c283ec-43a0-484a-82fa-7e220c2fedc1.png",
+    "file": "03-pattern-match.png",
+    "width": 1254,
+    "height": 1254,
+    "bytes": 2765593,
+    "sha256": "6ecfe85ffa5dc2ee893ba6722fd0271baa06cb002e01903048d551a747e6ec07",
+    "created_at": "2026-09-05T07:31:22.901Z",
+    "provenance": "Generated with image_gen from original Wallo and Pippa character reference. Wallpaper in these cartoons is illustrative, not a product representation."
+  },
+  {
+    "id": "04-just-five-minutes",
+    "top": "JUST A FIVE-MINUTE TOUCH-UP.",
+    "bottom": "THREE HOURS AGO.",
+    "scene": "A room mostly finished, Wallo kneeling beside one tiny corner lifting with tools neatly around him, Pippa looks at a clock and holds two cold coffees. Warm late-afternoon light. Dry deadpan humor.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-9c73f8a8-deef-4ab3-9b2e-94837e977a02.png",
+    "file": "04-just-five-minutes.png",
+    "width": 1254,
+    "height": 1254,
+    "bytes": 2379677,
+    "sha256": "0b3c0755fcd51399e83d79a9b3c83c5abd939b757aeafaad392f0423ab26fef1",
+    "created_at": "2026-09-05T07:37:25.271Z",
+    "provenance": "Generated with image_gen from original Wallo and Pippa character reference. Wallpaper in these cartoons is illustrative, not a product representation."
+  },
+  {
+    "id": "05-paste-footprints",
+    "top": "FRESHLY CLEANED FLOOR.",
+    "bottom": "SIGNED BY THE INSTALLER.",
+    "scene": "Wallo confidently walks away from the pasting table leaving a trail of white paste bootprints across a dark floor; Pippa stands behind with mop and a raised eyebrow. Wallo finally looks back. No injuries.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-e2e27989-327f-42bc-9c52-5e587812646b.png",
+    "file": "05-paste-footprints.png",
+    "width": 1254,
+    "height": 1254,
+    "bytes": 2240774,
+    "sha256": "e054c409f13c27b3cc3aac63a220ccfb5934d6aae960033e735ccb6f9d67a28d",
+    "created_at": "2026-09-05T07:32:33.397Z",
+    "provenance": "Generated with image_gen from original Wallo and Pippa character reference. Wallpaper in these cartoons is illustrative, not a product representation."
+  },
+  {
+    "id": "06-roll-with-it",
+    "top": "I SAID BRING A DROP CLOTH.",
+    "bottom": "HE BROUGHT A WHOLE WALL.",
+    "scene": "A long unpasted wallpaper roll has unspooled around Wallo's ankles and across the floor like an absurd elegant carpet; Pippa holds the actual folded drop cloth. Wallo stands still, bemused, not falling.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-039d3af7-3fe1-4be8-9d24-6d8ef211ec31.png",
+    "file": "06-roll-with-it.png",
+    "width": 1254,
+    "height": 1254,
+    "bytes": 2285873,
+    "sha256": "a4f0d436e656e46ecb78715c52ec03eb3cac1c8e89c3fddf422478848f52be6f",
+    "created_at": "2026-09-05T07:33:29.114Z",
+    "provenance": "Generated with image_gen from original Wallo and Pippa character reference. Wallpaper in these cartoons is illustrative, not a product representation."
+  },
+  {
+    "id": "07-ceiling-paste",
+    "top": "AIMING FOR THE WALL.",
+    "bottom": "THE CEILING FELT LEFT OUT.",
+    "scene": "Wallo lifts a loaded paste brush too quickly and small white paste flecks arc upward to dot a pristine ceiling; Pippa catches one on her cap-less auburn bun and looks unimpressed. No bucket balancing above heads.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-80359971-c51c-4913-934b-275285ce3e29.png",
+    "file": "07-ceiling-paste.png",
+    "width": 1254,
+    "height": 1254,
+    "bytes": 2281611,
+    "sha256": "afb22dfb2a8f2638610d1ba9f6c5156f974cd50673215904064703d2d6abc85c",
+    "created_at": "2026-09-05T07:32:57.797Z",
+    "provenance": "Generated with image_gen from original Wallo and Pippa character reference. Wallpaper in these cartoons is illustrative, not a product representation."
+  },
+  {
+    "id": "08-ladder-parking",
+    "top": "THE LADDER FITS.",
+    "bottom": "THE REST OF US DON'T.",
+    "scene": "A large open stepladder fills a tiny powder room between toilet and vanity. Wallo and Pippa squeeze beside it with wallpaper tools and look at each other. Anatomy plausible, no one atop toilet, dry visual comedy.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-db28bf56-d662-46f2-b55c-619bb1f47c0c.png",
+    "file": "08-ladder-parking.png",
+    "width": 1254,
+    "height": 1254,
+    "bytes": 2231931,
+    "sha256": "de4771843523894c169562ed0447570ecdfac36b930088cb50a28998a37c9959",
+    "created_at": "2026-09-05T07:34:00.976Z",
+    "provenance": "Generated with image_gen from original Wallo and Pippa character reference. Wallpaper in these cartoons is illustrative, not a product representation."
+  },
+  {
+    "id": "09-perfect-cut",
+    "top": "MEASURE TWICE.",
+    "bottom": "STILL CUT THE WRONG END.",
+    "scene": "Wallo holds a beautifully cut wallpaper strip beside its matching offcut, realizing the pattern runs the wrong way; Pippa points silently to the directional mark on the roll. Scissors resting on table, no sharp tools pointed at anyone.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-16448e68-8fe3-418b-a07b-fb8bed046081.png",
+    "file": "09-perfect-cut.png",
+    "width": 1254,
+    "height": 1254,
+    "bytes": 2227944,
+    "sha256": "59786aac9142552d7d9f5c44339d5a55246ed95e7d472bdce98f0b6680c879b7",
+    "created_at": "2026-09-05T07:34:27.743Z",
+    "provenance": "Generated with image_gen from original Wallo and Pippa character reference. Wallpaper in these cartoons is illustrative, not a product representation."
+  },
+  {
+    "id": "10-last-roll",
+    "top": "THE LAST ROLL.",
+    "bottom": "NOW A TEAM-BUILDING EXERCISE.",
+    "scene": "Wallo and Pippa sit on the floor looking at a remaining bare strip of wall only a few inches wide and an empty cardboard wallpaper tube between them. Symmetric deadpan exhausted pose, beautiful almost-finished room.",
+    "generatedSource": "/Users/macstudio3/.codex/generated_images/01a07029-a0c9-71f3-aa9c-bca524323bd1/exec-693f5f0b-485e-499d-abc4-0a7a6984af35.png",
+    "file": "10-last-roll.png",
+    "width": 1254,
+    "height": 1254,
+    "bytes": 2265272,
+    "sha256": "47dfb70594427439c10635daab6f0a6ab5cda5fc10beaa4f617a7c990c073eb8",
+    "created_at": "2026-09-05T07:35:00.687Z",
+    "provenance": "Generated with image_gen from original Wallo and Pippa character reference. Wallpaper in these cartoons is illustrative, not a product representation."
+  }
+]
diff --git a/series2/production.cjs b/series2/production.cjs
new file mode 100644
index 0000000..a3f2921
--- /dev/null
+++ b/series2/production.cjs
@@ -0,0 +1,77 @@
+const fs=require('node:fs'),path=require('node:path'),assert=require('node:assert/strict');
+const {spawnSync}=require('node:child_process');
+const crypto=require('node:crypto');
+const root=__dirname,phase=process.argv[2],out=path.join(root,'verification');
+const episodes=JSON.parse(fs.readFileSync(path.join(root,'episodes.json')));
+const cli='/Users/macstudio3/.npm/_npx/702923228c2ce1e6/node_modules/hyperframes/bin/hyperframes.mjs';
+function exec(cmd,args,log){const r=spawnSync(cmd,args,{cwd:root,encoding:'utf8',maxBuffer:20e6,env:{...process.env,HYPERFRAMES_TELEMETRY_DISABLED:'1',HYPERFRAMES_PYTHON:'/Users/macstudio3/.venvs/wallo-media/bin/python'}});if(log)fs.writeFileSync(path.join(out,log),(r.stdout+'\n'+r.stderr).split('\n').map(l=>l.trimEnd()).join('\n'));if(r.status!==0)throw Error(r.stderr+'\n'+r.stdout);return r.stdout;}
+(async()=>{
+ if(phase==='serve'){
+  const http=require('node:http');
+  const types={'.html':'text/html','.js':'text/javascript','.json':'application/json','.png':'image/png','.jpg':'image/jpeg','.ttf':'font/ttf','.wav':'audio/wav','.mp4':'video/mp4','.txt':'text/plain'};
+  const server=http.createServer((req,res)=>{try{const name=decodeURIComponent(new URL(req.url,'http://localhost').pathname);const file=path.resolve(root,'.'+(name==='/'?'/watch.html':name));if(!file.startsWith(root+path.sep)||!fs.existsSync(file)||!fs.statSync(file).isFile()){res.writeHead(404);res.end('Not found');return;}const size=fs.statSync(file).size;res.setHeader('Content-Type',types[path.extname(file)]||'application/octet-stream');res.setHeader('Accept-Ranges','bytes');const match=req.headers.range?.match(/^bytes=(\d+)-(\d*)$/);if(match){const start=+match[1],end=Math.min(size-1,match[2]?+match[2]:size-1);if(start> end){res.writeHead(416);res.end();return;}res.writeHead(206,{'Content-Range':`bytes ${start}-${end}/${size}`,'Content-Length':end-start+1});fs.createReadStream(file,{start,end}).pipe(res);}else{res.writeHead(200,{'Content-Length':size});fs.createReadStream(file).pipe(res);}}catch(e){res.writeHead(500);res.end('Error');}});
+  server.listen(0,'127.0.0.1',()=>{fs.writeFileSync(path.join(out,'server.json'),JSON.stringify({port:server.address().port,pid:process.pid}));console.log('Local verification URL http://127.0.0.1:'+server.address().port);});return;
+ }
+ if(phase==='audio'){console.log(exec('/Users/macstudio3/.venvs/wallo-media/bin/python',['audio.py'],'audio-production.txt'));return;}
+ if(phase==='build'){console.log(exec('node',['build.mjs']));return;}
+ if(phase==='check'){
+  const checks=[],entry=path.join(root,'index.html'),saved=fs.readFileSync(entry);
+  try{for(const ep of episodes){fs.writeFileSync(entry,fs.readFileSync(path.join(root,'episodes',ep.slug+'.html'),'utf8').replaceAll('../assets/','assets/'));const r=exec('node',[cli,'check','.','--json'],ep.slug+'-check.txt');const end=r.lastIndexOf('}');const data=JSON.parse(r.slice(0,end+1));assert(data.ok);checks.push({slug:ep.slug,ok:data.ok,lint:data.lint,runtime:data.runtime,layout:data.layout});console.log(ep.slug+' check PASS');}}finally{fs.writeFileSync(entry,saved);}
+  fs.writeFileSync(path.join(out,'checks.json'),JSON.stringify(checks,null,2));return;
+ }
+ if(phase==='render'){
+  for(const ep of episodes){console.log('Rendering '+ep.slug);exec('node',[cli,'render','.', '-c','episodes/'+ep.slug+'.html','--quality','high','--fps','30','--workers','2','-o','renders/'+ep.slug+'.mp4'],ep.slug+'-render.txt');console.log('Rendered '+ep.slug);}
+  const list=episodes.map(ep=>"file '"+path.join(root,'renders',ep.slug+'.mp4')+"'").join('\n');fs.writeFileSync(path.join(out,'concat.txt'),list+'\n');
+  exec('ffmpeg',['-v','error','-y','-f','concat','-safe','0','-i','verification/concat.txt','-c','copy','-movflags','+faststart','renders/wallo-wallcovering-presentation.mp4'],'concat.log');console.log('Combined presentation complete.');return;
+ }
+ if(phase==='mcp'){await import('./mcp-proof.mjs');return;}
+ if(phase==='memes'){await import('./memes.mjs');return;}
+ if(phase==='smoke'){
+  const {port}=JSON.parse(fs.readFileSync(path.join(out,'server.json')));
+  console.log(exec('node',['/Users/macstudio3/.agents/skills/3x/run.js','--url','http://127.0.0.1:'+port+'/watch.html','--expect','Wallo','--selector','#presentation','--click','[data-jump="80"]','--no-open'],'3x.txt'));return;
+ }
+ if(phase==='delivery'){await import('./delivery-proof.mjs');return;}
+ const {chromium}=require('/Users/macstudio3/Projects/Designer-Wallcoverings/node_modules/playwright');
+ const browser=await chromium.launch({executablePath:'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',headless:true});
+ const page=await browser.newPage({viewport:{width:1080,height:1920}});const errors=[],checks=[];page.on('pageerror',e=>errors.push(e.message));
+ try{
+ if(phase==='http-debug'){
+  page.on('console',m=>{if(m.type()==='error')console.log('CONSOLE',m.text(),m.location());});
+  page.on('response',r=>{if(r.status()>=400)console.log('HTTP',r.status(),r.url());});
+  page.on('requestfailed',r=>console.log('REQUEST',r.url(),r.failure()));
+  const {port}=JSON.parse(fs.readFileSync(path.join(out,'server.json')));
+  await page.goto('http://127.0.0.1:'+port+'/watch.html');await page.locator('[data-jump="80"]').click();await page.waitForTimeout(1000);console.log('PAGE ERRORS',errors);return;
+ }
+ if(phase==='preview'){
+  for(const ep of episodes){
+   await page.goto('file://'+path.join(root,'episodes',ep.slug+'.html'));await page.evaluate(()=>window.__ready);
+   const snapshots=[];const angles={wallo:[[],[]],pippa:[[],[]]};let maxError=0;
+   for(const t of [1.3,3.5,6.2,8.2,11.6,16.5,19]){
+    await page.evaluate(t=>window.__draw(t),t);const b=await page.locator('#stage').screenshot({path:path.join(out,ep.slug+'-'+t+'.png')});snapshots.push(crypto.createHash('sha256').update(b).digest('hex'));
+    const rigs=await page.evaluate(()=>window.__rigState);
+    for(const rig of rigs){for(const limb of [...rig.arms,...rig.legs]){maxError=Math.max(maxError,Math.abs(Math.hypot(limb.root.x-limb.joint.x,limb.root.y-limb.joint.y)-limb.l1),Math.abs(Math.hypot(limb.end.x-limb.joint.x,limb.end.y-limb.joint.y)-limb.l2));}rig.arms.forEach((arm,i)=>angles[rig.kind][i].push(Math.atan2(arm.end.y-arm.joint.y,arm.end.x-arm.joint.x)));}
+   }
+   assert.equal(new Set(snapshots).size,7);assert(maxError<.00001);
+   const armAngleRanges=Object.fromEntries(Object.entries(angles).map(([kind,sides])=>[kind,sides.map(v=>Math.max(...v)-Math.min(...v))]));
+   for(const kind of ['wallo','pippa'])assert(Math.max(...armAngleRanges[kind])>.15,kind+' must articulate');
+   await page.evaluate(()=>window.__draw(7));const before=await page.locator('#stage').screenshot();await page.evaluate(()=>window.__draw(3));await page.evaluate(()=>window.__draw(7));assert(before.equals(await page.locator('#stage').screenshot()));
+   checks.push({slug:ep.slug,verdict:'PASS',uniqueFrames:7,maxBoneLengthError:maxError,armAngleRanges,reverseSeekDeterministic:true});console.log(ep.slug+' articulated preview PASS');
+  }
+  await page.setViewportSize({width:1500,height:1420});await page.setContent('<body style="margin:0;background:#eeeae2"><div style="display:grid;grid-template-columns:repeat(5,300px)">'+[6.2,11.6].flatMap(t=>episodes.map(ep=>'<div><img width="300" src="file://'+path.join(out,ep.slug+'-'+t+'.png')+'"><p>'+ep.slug+' / '+t+'s</p></div>')).join('')+'</div></body>');await page.evaluate(()=>Promise.all([...document.images].map(i=>i.decode())));await page.screenshot({path:path.join(out,'contact-sheet.png'),fullPage:true});
+ }else if(phase==='verify'){
+  for(const ep of [...episodes,{slug:'wallo-wallcovering-presentation',combined:true}]){
+   const file=path.join(root,'renders',ep.slug+'.mp4'),probe=JSON.parse(exec('ffprobe',['-v','error','-show_streams','-show_format','-of','json',file]));
+   const v=probe.streams.find(s=>s.codec_type==='video'),a=probe.streams.find(s=>s.codec_type==='audio');const expected=ep.combined?100:20;
+   assert.equal(v.width,1080);assert.equal(v.height,1920);assert.equal(+v.nb_frames,expected*30);assert.equal(v.r_frame_rate,'30/1');assert(Math.abs(+probe.format.duration-expected)<.15);assert(a);
+   exec('ffmpeg',['-v','error','-i',file,'-f','null','-'],ep.slug+'-decode.txt');
+   await page.goto('file://'+path.join(root,'watch.html'));const selector='video[data-slug="'+ep.slug+'"]';const video=page.locator(selector);
+   await video.evaluate(async v=>{v.muted=true;await v.play();});await page.waitForFunction(sel=>document.querySelector(sel).currentTime>.4,selector,{timeout:15000});await video.evaluate(v=>v.pause());
+   for(const t of (ep.combined?[1,21,41,61,81,99]:[1,7,12,19])){await video.evaluate((v,t)=>new Promise(resolve=>{v.addEventListener('seeked',resolve,{once:true});v.currentTime=t;}),t);await video.screenshot({path:path.join(out,ep.slug+'-mp4-'+t+'.png')});}
+   const playback=await video.evaluate(v=>({duration:v.duration,error:v.error?.message||null}));assert.equal(playback.error,null);
+   checks.push({slug:ep.slug,verdict:'PASS',duration:+probe.format.duration,frames:+v.nb_frames,width:v.width,height:v.height,audio:a.codec_name,bytes:fs.statSync(file).size,sha256:crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex'),playback});console.log(ep.slug+' encoded playback PASS');
+  }
+ }else throw Error('Unknown series2 phase '+phase);
+ assert.deepEqual(errors,[]);
+ }finally{await browser.close();}
+ fs.writeFileSync(path.join(out,phase+'-evidence.json'),JSON.stringify({at:new Date().toISOString(),checks,errors},null,2));
+})().catch(e=>{console.error(e.stack);process.exitCode=1;});
diff --git a/series2/verification/04-fire-rated-1.3.png b/series2/verification/04-fire-rated-1.3.png
new file mode 100644
index 0000000..75a2960
Binary files /dev/null and b/series2/verification/04-fire-rated-1.3.png differ
diff --git a/series2/verification/04-fire-rated-11.6.png b/series2/verification/04-fire-rated-11.6.png
new file mode 100644
index 0000000..146cb86
Binary files /dev/null and b/series2/verification/04-fire-rated-11.6.png differ
diff --git a/series2/verification/04-fire-rated-16.5.png b/series2/verification/04-fire-rated-16.5.png
new file mode 100644
index 0000000..47f0bc9
Binary files /dev/null and b/series2/verification/04-fire-rated-16.5.png differ
diff --git a/series2/verification/04-fire-rated-19.png b/series2/verification/04-fire-rated-19.png
new file mode 100644
index 0000000..c856f1f
Binary files /dev/null and b/series2/verification/04-fire-rated-19.png differ
diff --git a/series2/verification/04-fire-rated-3.5.png b/series2/verification/04-fire-rated-3.5.png
new file mode 100644
index 0000000..2959a1e
Binary files /dev/null and b/series2/verification/04-fire-rated-3.5.png differ
diff --git a/series2/verification/04-fire-rated-6.2.png b/series2/verification/04-fire-rated-6.2.png
new file mode 100644
index 0000000..4dcf933
Binary files /dev/null and b/series2/verification/04-fire-rated-6.2.png differ
diff --git a/series2/verification/04-fire-rated-8.2.png b/series2/verification/04-fire-rated-8.2.png
new file mode 100644
index 0000000..0c4fa61
Binary files /dev/null and b/series2/verification/04-fire-rated-8.2.png differ
diff --git a/series2/verification/04-fire-rated-check.txt b/series2/verification/04-fire-rated-check.txt
index c1e2f4f..10644ae 100644
--- a/series2/verification/04-fire-rated-check.txt
+++ b/series2/verification/04-fire-rated-check.txt
@@ -1,45 +1,12 @@
 {
-  "ok": false,
+  "ok": true,
   "strict": false,
   "lint": {
-    "ok": false,
-    "errorCount": 2,
+    "ok": true,
+    "errorCount": 0,
     "warningCount": 0,
     "infoCount": 0,
-    "findings": [
-      {
-        "code": "invalid_parent_traversal_in_asset_path",
-        "severity": "error",
-        "message": "Found 4 asset path(s) traversing above the project root with \"../\" (../../assets/ (3), ../../../assets/). Renders rewrite this against each sub-composition's source path, but Studio preview and other live consumers resolve against the project root and 404.",
-        "selector": "[data-composition-id]",
-        "dataAttributes": {},
-        "sourceFile": "/Users/macstudio3/Projects/wallpaper-paint-antics/series2/verification/check-04-fire-rated/index.html",
-        "bbox": {
-          "x": 0,
-          "y": 0,
-          "width": 0,
-          "height": 0
-        },
-        "time": 0,
-        "fixHint": "Use plain root-relative paths (e.g. \"assets/...\", \"capture/...\", \"fonts/...\") — compositions are served with the project root as their base URL, so paths must be root-relative, not relative to the compositions/ directory."
-      },
-      {
-        "code": "audio_src_not_found",
-        "severity": "error",
-        "message": "<audio> element references file(s) not found in the project: ../../assets/mix-0.wav. The rendered video will be silent.",
-        "selector": "[data-composition-id]",
-        "dataAttributes": {},
-        "sourceFile": "index.html",
-        "bbox": {
-          "x": 0,
-          "y": 0,
-          "width": 0,
-          "height": 0
-        },
-        "time": 0,
-        "fixHint": "Add the file \"../../assets/mix-0.wav\" to the project directory, or update the src attribute to point to an existing file."
-      }
-    ],
+    "findings": [],
     "filesScanned": 1
   },
   "runtime": {
@@ -55,8 +22,18 @@
     "warningCount": 0,
     "infoCount": 0,
     "findings": [],
-    "duration": 0,
-    "samples": [],
+    "duration": 20,
+    "samples": [
+      1.111,
+      3.333,
+      5.556,
+      7.778,
+      10,
+      12.222,
+      14.444,
+      16.667,
+      18.889
+    ],
     "transitionSamples": [],
     "transitionSamplesDropped": 0,
     "tolerance": 2,
@@ -79,7 +56,13 @@
     "infoCount": 0,
     "findings": [],
     "enabled": true,
-    "samples": [],
+    "samples": [
+      1.111,
+      5.556,
+      10,
+      14.444,
+      18.889
+    ],
     "checked": 0,
     "passed": 0
   },
@@ -96,3 +79,4 @@
   }
 }
 
+[hyperframes] browserGpuMode probe → hardware (WebGL renderer vendor="Google Inc. (Apple)" renderer="ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Ultra, Unspecified Version)")
diff --git a/series2/verification/04-fire-rated-decode.txt b/series2/verification/04-fire-rated-decode.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/series2/verification/04-fire-rated-decode.txt
@@ -0,0 +1 @@
+
diff --git a/series2/verification/04-fire-rated-mp4-1.png b/series2/verification/04-fire-rated-mp4-1.png
new file mode 100644
index 0000000..8af588d
Binary files /dev/null and b/series2/verification/04-fire-rated-mp4-1.png differ
diff --git a/series2/verification/04-fire-rated-mp4-12.png b/series2/verification/04-fire-rated-mp4-12.png
new file mode 100644
index 0000000..9120936
Binary files /dev/null and b/series2/verification/04-fire-rated-mp4-12.png differ
diff --git a/series2/verification/04-fire-rated-mp4-19.png b/series2/verification/04-fire-rated-mp4-19.png
new file mode 100644
index 0000000..30f6d4d
Binary files /dev/null and b/series2/verification/04-fire-rated-mp4-19.png differ
diff --git a/series2/verification/04-fire-rated-mp4-7.png b/series2/verification/04-fire-rated-mp4-7.png
new file mode 100644
index 0000000..8c07e73
Binary files /dev/null and b/series2/verification/04-fire-rated-mp4-7.png differ
diff --git a/series2/verification/04-fire-rated-render.txt b/series2/verification/04-fire-rated-render.txt
new file mode 100644
index 0000000..db48628
--- /dev/null
+++ b/series2/verification/04-fire-rated-render.txt
@@ -0,0 +1,96 @@
+
+◆  Rendering series2/episodes/04-fire-rated.html → /Users/macstudio3/Projects/wallpaper-paint-antics/series2/renders/04-fire-rated.mp4
+   30fps · high · 2 workers
+   GPU: browser GPU (auto-detect)
+
+[?25l│
+◇  Browser: cache
+[?25h
+  ✗ invalid_parent_traversal_in_asset_path: Found 4 asset path(s) traversing above the project root with "../" (../assets/ (4)). Renders rewrite this against each sub-composition's source path, but Studio preview and other live consumers resolve against the project root and 404.
+    Fix: Use plain root-relative paths (e.g. "assets/...", "capture/...", "fonts/...") — compositions are served with the project root as their base URL, so paths must be root-relative, not relative to the compositions/ directory.
+  Continuing render despite lint issues. Use --strict to block errors.
+
+  Parallel drawElement capture stays off for this install (a previous render had to fall back). Re-enable with HF_DE_PARALLEL_ROUTER=true.
+  █░░░░░░░░░░░░░░░░░░░░░░░░  5%  Compiling composition
+  ██░░░░░░░░░░░░░░░░░░░░░░░  10%  Extracting video frames
+  █████░░░░░░░░░░░░░░░░░░░░  20%  Processing audio tracks
+  ██████░░░░░░░░░░░░░░░░░░░  25%  Starting frame capture
+[BrowserManager] Browser launched (HeadlessChrome/152.0.7977.30, screenshot, gl=--use-gl=angle --use-angle=metal, headlessShell=true, platform=darwin)
+[initSession:screenshot] page.goto start (0ms)
+[initSession:screenshot] page.goto start (0ms)
+[HyperFrames] render runtime fps [object Object]
+[HyperFrames] render runtime fps [object Object]
+[initSession:screenshot] page.goto complete (137ms)
+[initSession:screenshot] page.goto complete (138ms)
+[initSession:screenshot] GSAP proxy flush complete (352ms)
+[initSession:screenshot] GSAP proxy flush complete (353ms)
+[initSession:screenshot] pollHfReady complete (357ms)
+[initSession:screenshot] pollHfReady complete (358ms)
+[initSession:screenshot] pollSubCompositionTimelines complete (ready) (359ms)
+[initSession:screenshot] applyVideoMetadataHints complete (359ms)
+[initSession:screenshot] pollSubCompositionTimelines complete (ready) (360ms)
+[initSession:screenshot] applyVideoMetadataHints complete (360ms)
+[initSession:screenshot] media + fonts + tailwind ready (361ms)
+[initSession:screenshot] media + fonts + tailwind ready (361ms)
+[initSession:screenshot] static-frame dedup: disabled (canvas/webgl) (365ms)
+[initSession:screenshot] static-frame dedup: disabled (canvas/webgl) (365ms)
+  ██████░░░░░░░░░░░░░░░░░░░  27%  Capturing frame 30/600 (2 workers)
+  ███████░░░░░░░░░░░░░░░░░░  30%  Capturing frame 60/600 (2 workers)
+  ████████░░░░░░░░░░░░░░░░░  32%  Capturing frame 90/600 (2 workers)
+  ████████░░░░░░░░░░░░░░░░░  34%  Capturing frame 120/600 (2 workers)
+  █████████░░░░░░░░░░░░░░░░  36%  Capturing frame 150/600 (2 workers)
+  █████████░░░░░░░░░░░░░░░░  39%  Capturing frame 180/600 (2 workers)
+  ██████████░░░░░░░░░░░░░░░  41%  Capturing frame 210/600 (2 workers)
+  ██████████░░░░░░░░░░░░░░░  43%  Capturing frame 240/600 (2 workers)
+  ███████████░░░░░░░░░░░░░░  45%  Capturing frame 270/600 (2 workers)
+  ████████████░░░░░░░░░░░░░  48%  Capturing frame 300/600 (2 workers)
+  ████████████░░░░░░░░░░░░░  50%  Capturing frame 330/600 (2 workers)
+  █████████████░░░░░░░░░░░░  52%  Capturing frame 360/600 (2 workers)
+  █████████████░░░░░░░░░░░░  54%  Capturing frame 390/600 (2 workers)
+  ██████████████░░░░░░░░░░░  57%  Capturing frame 420/600 (2 workers)
+  ██████████████░░░░░░░░░░░  59%  Capturing frame 450/600 (2 workers)
+  ███████████████░░░░░░░░░░  61%  Capturing frame 480/600 (2 workers)
+  ███████████████░░░░░░░░░░  63%  Capturing frame 510/600 (2 workers)
+  ████████████████░░░░░░░░░  66%  Capturing frame 540/600 (2 workers)
+  █████████████████░░░░░░░░  68%  Capturing frame 570/600 (2 workers)
+  █████████████████░░░░░░░░  70%  Capturing frame 600/600 (2 workers)
+  ██████████████████░░░░░░░  75%  Encoding video
+  ██████████████████████░░░  90%  Assembling final video
+  █████████████████████████  100%  Render complete
+
+◇  /Users/macstudio3/Projects/wallpaper-paint-antics/series2/renders/04-fire-rated.mp4
+   10.0 MB · 20.0s video · rendered in 20.1s
+
+[INFO] [Render] Pipeline started {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","platform":"darwin","arch":"arm64","nodeVersion":"v26.4.0","fps":{"num":30,"den":1},"format":"mp4","quality":"high","browserGpuMode":"auto","forceScreenshot":false,"protocolTimeout":300000,"browserTimeout":120000,"pageNavigationTimeout":60000,"playerReadyTimeout":45000}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"pipeline","status":"checkpoint","elapsedMs":2,"message":"started","format":"mp4","quality":"high","browserGpuMode":"auto","forceScreenshot":false,"protocolTimeoutMs":300000,"pageNavigationTimeoutMs":60000,"playerReadyTimeoutMs":45000,"requestedWorkers":2}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"compile","status":"start","elapsedMs":3,"needsAlpha":false}
+[INFO] [Compiler] Embedded local font file: assets/georgia.ttf (371 KB → data URI)
+[INFO] [Compiler] Embedded local font file: assets/verdana.ttf (182 KB → data URI)
+[INFO] Compiled composition metadata {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","entryFile":"episodes/04-fire-rated.html","staticDuration":20,"width":1080,"height":1920,"videoCount":0,"audioCount":1,"renderModeHints":{"recommendScreenshot":false,"reasons":[]}}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"compile","status":"end","elapsedMs":1296,"durationMs":1293,"needsAlpha":false}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"compile","status":"checkpoint","elapsedMs":1297,"message":"composition metadata resolved","width":1080,"height":1920,"videoCount":0,"audioCount":1,"imageCount":0,"deviceScaleFactor":1,"forceScreenshot":false,"compositionHash":"9e8bcfc702558d38"}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"browser_probe","status":"start","elapsedMs":1297,"forceScreenshot":false,"stagePhase":"calibrating"}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"browser_probe","status":"end","elapsedMs":1300,"durationMs":3,"forceScreenshot":false,"stagePhase":"calibrating"}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"browser_probe","status":"checkpoint","elapsedMs":1300,"message":"duration resolved","durationSeconds":20,"totalFrames":600,"compositionHash":"9e8bcfc702558d38","beginFrameStalled":false}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"video_extract","status":"start","elapsedMs":1300,"videoCount":0}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"video_extract","status":"end","elapsedMs":1301,"durationMs":1,"videoCount":0}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"video_extract","status":"checkpoint","elapsedMs":1301,"message":"frames resolved","videoCount":0,"extractedVideoCount":0,"totalFramesExtracted":0,"maxFramesPerVideo":0,"avgFramesPerExtractedVideo":null,"vfrPreflightCount":null,"vfrPreflightMs":null,"cacheHits":null,"cacheMisses":null,"transientRetries":null,"minVideoFrameCoverageRatio":null,"authoredTimedClipCount":3}
+[INFO] [Render] No HDR sources detected — rendering SDR {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d"}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"hdr_detection","status":"checkpoint","elapsedMs":1301,"message":"resolved","requestedHdrMode":"auto","effectiveHdr":"sdr","nativeHdrVideoCount":0,"nativeHdrImageCount":0}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"audio_process","status":"start","elapsedMs":1302,"audioCount":1}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"audio_process","status":"end","elapsedMs":1743,"durationMs":441,"audioCount":1}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"file_server","status":"start","elapsedMs":1743,"reused":false}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"file_server","status":"end","elapsedMs":1747,"durationMs":4}
+[hyperframes] browserGpuMode probe → hardware (WebGL renderer vendor="Google Inc. (Apple)" renderer="ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Ultra, Unspecified Version)")
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"capture_calibration","status":"checkpoint","elapsedMs":1939,"message":"skipped","requestedWorkers":2,"totalFrames":600,"htmlInCanvasDetected":false,"lowMemoryMode":false,"deInversionEligible":false,"deParallelRouterEligible":false}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"worker_resolution","status":"checkpoint","elapsedMs":1940,"message":"resolved","workerCount":2}
+[INFO] streaming-encode gate {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","enabled":false,"configFlag":true,"outputFormat":"mp4","workerCount":2,"durationSeconds":20,"maxDurationSeconds":240}
+[INFO] [Render] Fast capture: default-on drawElement disabled for this render — parallel capture has no runtime self-verification. Set PRODUCER_EXPERIMENTAL_FAST_CAPTURE=true to override. {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d"}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"capture_strategy","status":"checkpoint","elapsedMs":1940,"message":"resolved","workerCount":2,"forceScreenshot":false,"captureBeyondViewport":null,"useStreamingEncode":false,"useLayeredComposite":false,"usePageSideCompositing":false,"hasHdrContent":false,"hasShaderTransitions":false,"isPngSequence":false}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"capture_disk","status":"start","elapsedMs":1940,"stagePhase":"capturing","needsAlpha":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":0,"captureMode":"beginframe","captureOperation":"beginFrame"}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"capture_disk","status":"end","elapsedMs":17087,"durationMs":15147,"stagePhase":"capturing","needsAlpha":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"encode","status":"start","elapsedMs":17088,"stagePhase":"capturing","hasAudio":true,"isPngSequence":false,"isGif":false,"chunkedEncode":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"encode","status":"end","elapsedMs":19503,"durationMs":2415,"stagePhase":"capturing","hasAudio":true,"isPngSequence":false,"isGif":false,"chunkedEncode":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"assemble","status":"start","elapsedMs":19503,"stagePhase":"capturing","hasAudio":true,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"assemble","status":"end","elapsedMs":20009,"durationMs":506,"stagePhase":"capturing","hasAudio":true,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"f7437ad5-a523-431e-85b4-edfaf2a8cf2d","phase":"pipeline","status":"checkpoint","elapsedMs":20055,"message":"artifact validated","totalElapsedMs":20052}
diff --git a/series2/verification/05-wallpaper-or-paint-1.3.png b/series2/verification/05-wallpaper-or-paint-1.3.png
new file mode 100644
index 0000000..b593987
Binary files /dev/null and b/series2/verification/05-wallpaper-or-paint-1.3.png differ
diff --git a/series2/verification/05-wallpaper-or-paint-11.6.png b/series2/verification/05-wallpaper-or-paint-11.6.png
new file mode 100644
index 0000000..2b7f2c5
Binary files /dev/null and b/series2/verification/05-wallpaper-or-paint-11.6.png differ
diff --git a/series2/verification/05-wallpaper-or-paint-16.5.png b/series2/verification/05-wallpaper-or-paint-16.5.png
new file mode 100644
index 0000000..66700ba
Binary files /dev/null and b/series2/verification/05-wallpaper-or-paint-16.5.png differ
diff --git a/series2/verification/05-wallpaper-or-paint-19.png b/series2/verification/05-wallpaper-or-paint-19.png
new file mode 100644
index 0000000..68d643b
Binary files /dev/null and b/series2/verification/05-wallpaper-or-paint-19.png differ
diff --git a/series2/verification/05-wallpaper-or-paint-3.5.png b/series2/verification/05-wallpaper-or-paint-3.5.png
new file mode 100644
index 0000000..19ac63e
Binary files /dev/null and b/series2/verification/05-wallpaper-or-paint-3.5.png differ
diff --git a/series2/verification/05-wallpaper-or-paint-6.2.png b/series2/verification/05-wallpaper-or-paint-6.2.png
new file mode 100644
index 0000000..1c5bd02
Binary files /dev/null and b/series2/verification/05-wallpaper-or-paint-6.2.png differ
diff --git a/series2/verification/05-wallpaper-or-paint-8.2.png b/series2/verification/05-wallpaper-or-paint-8.2.png
new file mode 100644
index 0000000..b182d6d
Binary files /dev/null and b/series2/verification/05-wallpaper-or-paint-8.2.png differ
diff --git a/series2/verification/05-wallpaper-or-paint-check.txt b/series2/verification/05-wallpaper-or-paint-check.txt
new file mode 100644
index 0000000..10644ae
--- /dev/null
+++ b/series2/verification/05-wallpaper-or-paint-check.txt
@@ -0,0 +1,82 @@
+{
+  "ok": true,
+  "strict": false,
+  "lint": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": [],
+    "filesScanned": 1
+  },
+  "runtime": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": []
+  },
+  "layout": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": [],
+    "duration": 20,
+    "samples": [
+      1.111,
+      3.333,
+      5.556,
+      7.778,
+      10,
+      12.222,
+      14.444,
+      16.667,
+      18.889
+    ],
+    "transitionSamples": [],
+    "transitionSamplesDropped": 0,
+    "tolerance": 2,
+    "totalIssueCount": 0,
+    "truncated": false
+  },
+  "motion": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": [],
+    "enabled": false,
+    "samples": 0
+  },
+  "contrast": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": [],
+    "enabled": true,
+    "samples": [
+      1.111,
+      5.556,
+      10,
+      14.444,
+      18.889
+    ],
+    "checked": 0,
+    "passed": 0
+  },
+  "snapshots": {
+    "enabled": false,
+    "files": [],
+    "times": [],
+    "findingFiles": []
+  },
+  "_meta": {
+    "version": "0.8.29",
+    "latestVersion": "0.8.29",
+    "updateAvailable": false
+  }
+}
+
+[hyperframes] browserGpuMode probe → hardware (WebGL renderer vendor="Google Inc. (Apple)" renderer="ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Ultra, Unspecified Version)")
diff --git a/series2/verification/05-wallpaper-or-paint-decode.txt b/series2/verification/05-wallpaper-or-paint-decode.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/series2/verification/05-wallpaper-or-paint-decode.txt
@@ -0,0 +1 @@
+
diff --git a/series2/verification/05-wallpaper-or-paint-mp4-1.png b/series2/verification/05-wallpaper-or-paint-mp4-1.png
new file mode 100644
index 0000000..b4ab0dd
Binary files /dev/null and b/series2/verification/05-wallpaper-or-paint-mp4-1.png differ
diff --git a/series2/verification/05-wallpaper-or-paint-mp4-12.png b/series2/verification/05-wallpaper-or-paint-mp4-12.png
new file mode 100644
index 0000000..12609c1
Binary files /dev/null and b/series2/verification/05-wallpaper-or-paint-mp4-12.png differ
diff --git a/series2/verification/05-wallpaper-or-paint-mp4-19.png b/series2/verification/05-wallpaper-or-paint-mp4-19.png
new file mode 100644
index 0000000..fe410d0
Binary files /dev/null and b/series2/verification/05-wallpaper-or-paint-mp4-19.png differ
diff --git a/series2/verification/05-wallpaper-or-paint-mp4-7.png b/series2/verification/05-wallpaper-or-paint-mp4-7.png
new file mode 100644
index 0000000..9caff63
Binary files /dev/null and b/series2/verification/05-wallpaper-or-paint-mp4-7.png differ
diff --git a/series2/verification/05-wallpaper-or-paint-render.txt b/series2/verification/05-wallpaper-or-paint-render.txt
new file mode 100644
index 0000000..1a501b4
--- /dev/null
+++ b/series2/verification/05-wallpaper-or-paint-render.txt
@@ -0,0 +1,96 @@
+
+◆  Rendering series2/episodes/05-wallpaper-or-paint.html → /Users/macstudio3/Projects/wallpaper-paint-antics/series2/renders/05-wallpaper-or-paint.mp4
+   30fps · high · 2 workers
+   GPU: browser GPU (auto-detect)
+
+[?25l│
+◇  Browser: cache
+[?25h
+  ✗ invalid_parent_traversal_in_asset_path: Found 4 asset path(s) traversing above the project root with "../" (../assets/ (4)). Renders rewrite this against each sub-composition's source path, but Studio preview and other live consumers resolve against the project root and 404.
+    Fix: Use plain root-relative paths (e.g. "assets/...", "capture/...", "fonts/...") — compositions are served with the project root as their base URL, so paths must be root-relative, not relative to the compositions/ directory.
+  Continuing render despite lint issues. Use --strict to block errors.
+
+  Parallel drawElement capture stays off for this install (a previous render had to fall back). Re-enable with HF_DE_PARALLEL_ROUTER=true.
+  █░░░░░░░░░░░░░░░░░░░░░░░░  5%  Compiling composition
+  ██░░░░░░░░░░░░░░░░░░░░░░░  10%  Extracting video frames
+  █████░░░░░░░░░░░░░░░░░░░░  20%  Processing audio tracks
+  ██████░░░░░░░░░░░░░░░░░░░  25%  Starting frame capture
+[BrowserManager] Browser launched (HeadlessChrome/152.0.7977.30, screenshot, gl=--use-gl=angle --use-angle=metal, headlessShell=true, platform=darwin)
+[initSession:screenshot] page.goto start (0ms)
+[initSession:screenshot] page.goto start (0ms)
+[HyperFrames] render runtime fps [object Object]
+[HyperFrames] render runtime fps [object Object]
+[initSession:screenshot] page.goto complete (123ms)
+[initSession:screenshot] page.goto complete (123ms)
+[initSession:screenshot] GSAP proxy flush complete (365ms)
+[initSession:screenshot] GSAP proxy flush complete (364ms)
+[initSession:screenshot] pollHfReady complete (368ms)
+[initSession:screenshot] pollHfReady complete (370ms)
+[initSession:screenshot] pollSubCompositionTimelines complete (ready) (371ms)
+[initSession:screenshot] applyVideoMetadataHints complete (371ms)
+[initSession:screenshot] pollSubCompositionTimelines complete (ready) (370ms)
+[initSession:screenshot] applyVideoMetadataHints complete (370ms)
+[initSession:screenshot] media + fonts + tailwind ready (373ms)
+[initSession:screenshot] media + fonts + tailwind ready (371ms)
+[initSession:screenshot] static-frame dedup: disabled (canvas/webgl) (377ms)
+[initSession:screenshot] static-frame dedup: disabled (canvas/webgl) (375ms)
+  ██████░░░░░░░░░░░░░░░░░░░  27%  Capturing frame 30/600 (2 workers)
+  ███████░░░░░░░░░░░░░░░░░░  30%  Capturing frame 60/600 (2 workers)
+  ████████░░░░░░░░░░░░░░░░░  32%  Capturing frame 90/600 (2 workers)
+  ████████░░░░░░░░░░░░░░░░░  34%  Capturing frame 120/600 (2 workers)
+  █████████░░░░░░░░░░░░░░░░  36%  Capturing frame 150/600 (2 workers)
+  █████████░░░░░░░░░░░░░░░░  39%  Capturing frame 180/600 (2 workers)
+  ██████████░░░░░░░░░░░░░░░  41%  Capturing frame 210/600 (2 workers)
+  ██████████░░░░░░░░░░░░░░░  43%  Capturing frame 240/600 (2 workers)
+  ███████████░░░░░░░░░░░░░░  45%  Capturing frame 270/600 (2 workers)
+  ████████████░░░░░░░░░░░░░  48%  Capturing frame 300/600 (2 workers)
+  ████████████░░░░░░░░░░░░░  50%  Capturing frame 330/600 (2 workers)
+  █████████████░░░░░░░░░░░░  52%  Capturing frame 360/600 (2 workers)
+  █████████████░░░░░░░░░░░░  54%  Capturing frame 390/600 (2 workers)
+  ██████████████░░░░░░░░░░░  57%  Capturing frame 420/600 (2 workers)
+  ██████████████░░░░░░░░░░░  59%  Capturing frame 450/600 (2 workers)
+  ███████████████░░░░░░░░░░  61%  Capturing frame 480/600 (2 workers)
+  ███████████████░░░░░░░░░░  63%  Capturing frame 510/600 (2 workers)
+  ████████████████░░░░░░░░░  66%  Capturing frame 540/600 (2 workers)
+  █████████████████░░░░░░░░  68%  Capturing frame 570/600 (2 workers)
+  █████████████████░░░░░░░░  70%  Capturing frame 600/600 (2 workers)
+  ██████████████████░░░░░░░  75%  Encoding video
+  ██████████████████████░░░  90%  Assembling final video
+  █████████████████████████  100%  Render complete
+
+◇  /Users/macstudio3/Projects/wallpaper-paint-antics/series2/renders/05-wallpaper-or-paint.mp4
+   10.7 MB · 20.0s video · rendered in 18.6s
+
+[INFO] [Render] Pipeline started {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","platform":"darwin","arch":"arm64","nodeVersion":"v26.4.0","fps":{"num":30,"den":1},"format":"mp4","quality":"high","browserGpuMode":"auto","forceScreenshot":false,"protocolTimeout":300000,"browserTimeout":120000,"pageNavigationTimeout":60000,"playerReadyTimeout":45000}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"pipeline","status":"checkpoint","elapsedMs":1,"message":"started","format":"mp4","quality":"high","browserGpuMode":"auto","forceScreenshot":false,"protocolTimeoutMs":300000,"pageNavigationTimeoutMs":60000,"playerReadyTimeoutMs":45000,"requestedWorkers":2}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"compile","status":"start","elapsedMs":3,"needsAlpha":false}
+[INFO] [Compiler] Embedded local font file: assets/georgia.ttf (371 KB → data URI)
+[INFO] [Compiler] Embedded local font file: assets/verdana.ttf (182 KB → data URI)
+[INFO] Compiled composition metadata {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","entryFile":"episodes/05-wallpaper-or-paint.html","staticDuration":20,"width":1080,"height":1920,"videoCount":0,"audioCount":1,"renderModeHints":{"recommendScreenshot":false,"reasons":[]}}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"compile","status":"end","elapsedMs":65,"durationMs":62,"needsAlpha":false}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"compile","status":"checkpoint","elapsedMs":65,"message":"composition metadata resolved","width":1080,"height":1920,"videoCount":0,"audioCount":1,"imageCount":0,"deviceScaleFactor":1,"forceScreenshot":false,"compositionHash":"0d2c55e970967a43"}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"browser_probe","status":"start","elapsedMs":66,"forceScreenshot":false,"stagePhase":"calibrating"}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"browser_probe","status":"end","elapsedMs":68,"durationMs":2,"forceScreenshot":false,"stagePhase":"calibrating"}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"browser_probe","status":"checkpoint","elapsedMs":69,"message":"duration resolved","durationSeconds":20,"totalFrames":600,"compositionHash":"0d2c55e970967a43","beginFrameStalled":false}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"video_extract","status":"start","elapsedMs":69,"videoCount":0}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"video_extract","status":"end","elapsedMs":69,"durationMs":0,"videoCount":0}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"video_extract","status":"checkpoint","elapsedMs":70,"message":"frames resolved","videoCount":0,"extractedVideoCount":0,"totalFramesExtracted":0,"maxFramesPerVideo":0,"avgFramesPerExtractedVideo":null,"vfrPreflightCount":null,"vfrPreflightMs":null,"cacheHits":null,"cacheMisses":null,"transientRetries":null,"minVideoFrameCoverageRatio":null,"authoredTimedClipCount":3}
+[INFO] [Render] No HDR sources detected — rendering SDR {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c"}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"hdr_detection","status":"checkpoint","elapsedMs":70,"message":"resolved","requestedHdrMode":"auto","effectiveHdr":"sdr","nativeHdrVideoCount":0,"nativeHdrImageCount":0}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"audio_process","status":"start","elapsedMs":70,"audioCount":1}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"audio_process","status":"end","elapsedMs":435,"durationMs":365,"audioCount":1}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"file_server","status":"start","elapsedMs":435,"reused":false}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"file_server","status":"end","elapsedMs":441,"durationMs":6}
+[hyperframes] browserGpuMode probe → hardware (WebGL renderer vendor="Google Inc. (Apple)" renderer="ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Ultra, Unspecified Version)")
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"capture_calibration","status":"checkpoint","elapsedMs":629,"message":"skipped","requestedWorkers":2,"totalFrames":600,"htmlInCanvasDetected":false,"lowMemoryMode":false,"deInversionEligible":false,"deParallelRouterEligible":false}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"worker_resolution","status":"checkpoint","elapsedMs":629,"message":"resolved","workerCount":2}
+[INFO] streaming-encode gate {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","enabled":false,"configFlag":true,"outputFormat":"mp4","workerCount":2,"durationSeconds":20,"maxDurationSeconds":240}
+[INFO] [Render] Fast capture: default-on drawElement disabled for this render — parallel capture has no runtime self-verification. Set PRODUCER_EXPERIMENTAL_FAST_CAPTURE=true to override. {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c"}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"capture_strategy","status":"checkpoint","elapsedMs":629,"message":"resolved","workerCount":2,"forceScreenshot":false,"captureBeyondViewport":null,"useStreamingEncode":false,"useLayeredComposite":false,"usePageSideCompositing":false,"hasHdrContent":false,"hasShaderTransitions":false,"isPngSequence":false}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"capture_disk","status":"start","elapsedMs":630,"stagePhase":"capturing","needsAlpha":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":0,"captureMode":"beginframe","captureOperation":"beginFrame"}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"capture_disk","status":"end","elapsedMs":15352,"durationMs":14722,"stagePhase":"capturing","needsAlpha":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"encode","status":"start","elapsedMs":15353,"stagePhase":"capturing","hasAudio":true,"isPngSequence":false,"isGif":false,"chunkedEncode":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"encode","status":"end","elapsedMs":17948,"durationMs":2595,"stagePhase":"capturing","hasAudio":true,"isPngSequence":false,"isGif":false,"chunkedEncode":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"assemble","status":"start","elapsedMs":17948,"stagePhase":"capturing","hasAudio":true,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"assemble","status":"end","elapsedMs":18442,"durationMs":494,"stagePhase":"capturing","hasAudio":true,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"8e172cf7-46c8-46cd-825b-8bb7d5582e6c","phase":"pipeline","status":"checkpoint","elapsedMs":18490,"message":"artifact validated","totalElapsedMs":18478}
diff --git a/series2/verification/06-built-for-busy-spaces-1.3.png b/series2/verification/06-built-for-busy-spaces-1.3.png
new file mode 100644
index 0000000..6ba409f
Binary files /dev/null and b/series2/verification/06-built-for-busy-spaces-1.3.png differ
diff --git a/series2/verification/06-built-for-busy-spaces-11.6.png b/series2/verification/06-built-for-busy-spaces-11.6.png
new file mode 100644
index 0000000..66ef2c4
Binary files /dev/null and b/series2/verification/06-built-for-busy-spaces-11.6.png differ
diff --git a/series2/verification/06-built-for-busy-spaces-16.5.png b/series2/verification/06-built-for-busy-spaces-16.5.png
new file mode 100644
index 0000000..2c8221d
Binary files /dev/null and b/series2/verification/06-built-for-busy-spaces-16.5.png differ
diff --git a/series2/verification/06-built-for-busy-spaces-19.png b/series2/verification/06-built-for-busy-spaces-19.png
new file mode 100644
index 0000000..772bc12
Binary files /dev/null and b/series2/verification/06-built-for-busy-spaces-19.png differ
diff --git a/series2/verification/06-built-for-busy-spaces-3.5.png b/series2/verification/06-built-for-busy-spaces-3.5.png
new file mode 100644
index 0000000..057cb20
Binary files /dev/null and b/series2/verification/06-built-for-busy-spaces-3.5.png differ
diff --git a/series2/verification/06-built-for-busy-spaces-6.2.png b/series2/verification/06-built-for-busy-spaces-6.2.png
new file mode 100644
index 0000000..49ed162
Binary files /dev/null and b/series2/verification/06-built-for-busy-spaces-6.2.png differ
diff --git a/series2/verification/06-built-for-busy-spaces-8.2.png b/series2/verification/06-built-for-busy-spaces-8.2.png
new file mode 100644
index 0000000..40619f3
Binary files /dev/null and b/series2/verification/06-built-for-busy-spaces-8.2.png differ
diff --git a/series2/verification/06-built-for-busy-spaces-check.txt b/series2/verification/06-built-for-busy-spaces-check.txt
new file mode 100644
index 0000000..10644ae
--- /dev/null
+++ b/series2/verification/06-built-for-busy-spaces-check.txt
@@ -0,0 +1,82 @@
+{
+  "ok": true,
+  "strict": false,
+  "lint": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": [],
+    "filesScanned": 1
+  },
+  "runtime": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": []
+  },
+  "layout": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": [],
+    "duration": 20,
+    "samples": [
+      1.111,
+      3.333,
+      5.556,
+      7.778,
+      10,
+      12.222,
+      14.444,
+      16.667,
+      18.889
+    ],
+    "transitionSamples": [],
+    "transitionSamplesDropped": 0,
+    "tolerance": 2,
+    "totalIssueCount": 0,
+    "truncated": false
+  },
+  "motion": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": [],
+    "enabled": false,
+    "samples": 0
+  },
+  "contrast": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": [],
+    "enabled": true,
+    "samples": [
+      1.111,
+      5.556,
+      10,
+      14.444,
+      18.889
+    ],
+    "checked": 0,
+    "passed": 0
+  },
+  "snapshots": {
+    "enabled": false,
+    "files": [],
+    "times": [],
+    "findingFiles": []
+  },
+  "_meta": {
+    "version": "0.8.29",
+    "latestVersion": "0.8.29",
+    "updateAvailable": false
+  }
+}
+
+[hyperframes] browserGpuMode probe → hardware (WebGL renderer vendor="Google Inc. (Apple)" renderer="ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Ultra, Unspecified Version)")
diff --git a/series2/verification/06-built-for-busy-spaces-decode.txt b/series2/verification/06-built-for-busy-spaces-decode.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/series2/verification/06-built-for-busy-spaces-decode.txt
@@ -0,0 +1 @@
+
diff --git a/series2/verification/06-built-for-busy-spaces-mp4-1.png b/series2/verification/06-built-for-busy-spaces-mp4-1.png
new file mode 100644
index 0000000..a4dbe36
Binary files /dev/null and b/series2/verification/06-built-for-busy-spaces-mp4-1.png differ
diff --git a/series2/verification/06-built-for-busy-spaces-mp4-12.png b/series2/verification/06-built-for-busy-spaces-mp4-12.png
new file mode 100644
index 0000000..0cded93
Binary files /dev/null and b/series2/verification/06-built-for-busy-spaces-mp4-12.png differ
diff --git a/series2/verification/06-built-for-busy-spaces-mp4-19.png b/series2/verification/06-built-for-busy-spaces-mp4-19.png
new file mode 100644
index 0000000..cc44efe
Binary files /dev/null and b/series2/verification/06-built-for-busy-spaces-mp4-19.png differ
diff --git a/series2/verification/06-built-for-busy-spaces-mp4-7.png b/series2/verification/06-built-for-busy-spaces-mp4-7.png
new file mode 100644
index 0000000..ac3be65
Binary files /dev/null and b/series2/verification/06-built-for-busy-spaces-mp4-7.png differ
diff --git a/series2/verification/06-built-for-busy-spaces-render.txt b/series2/verification/06-built-for-busy-spaces-render.txt
new file mode 100644
index 0000000..d6f3fbd
--- /dev/null
+++ b/series2/verification/06-built-for-busy-spaces-render.txt
@@ -0,0 +1,96 @@
+
+◆  Rendering series2/episodes/06-built-for-busy-spaces.html → /Users/macstudio3/Projects/wallpaper-paint-antics/series2/renders/06-built-for-busy-spaces.mp4
+   30fps · high · 2 workers
+   GPU: browser GPU (auto-detect)
+
+[?25l│
+◇  Browser: cache
+[?25h
+  ✗ invalid_parent_traversal_in_asset_path: Found 4 asset path(s) traversing above the project root with "../" (../assets/ (4)). Renders rewrite this against each sub-composition's source path, but Studio preview and other live consumers resolve against the project root and 404.
+    Fix: Use plain root-relative paths (e.g. "assets/...", "capture/...", "fonts/...") — compositions are served with the project root as their base URL, so paths must be root-relative, not relative to the compositions/ directory.
+  Continuing render despite lint issues. Use --strict to block errors.
+
+  Parallel drawElement capture stays off for this install (a previous render had to fall back). Re-enable with HF_DE_PARALLEL_ROUTER=true.
+  █░░░░░░░░░░░░░░░░░░░░░░░░  5%  Compiling composition
+  ██░░░░░░░░░░░░░░░░░░░░░░░  10%  Extracting video frames
+  █████░░░░░░░░░░░░░░░░░░░░  20%  Processing audio tracks
+  ██████░░░░░░░░░░░░░░░░░░░  25%  Starting frame capture
+[BrowserManager] Browser launched (HeadlessChrome/152.0.7977.30, screenshot, gl=--use-gl=angle --use-angle=metal, headlessShell=true, platform=darwin)
+[initSession:screenshot] page.goto start (0ms)
+[initSession:screenshot] page.goto start (0ms)
+[HyperFrames] render runtime fps [object Object]
+[HyperFrames] render runtime fps [object Object]
+[initSession:screenshot] page.goto complete (122ms)
+[initSession:screenshot] page.goto complete (120ms)
+[initSession:screenshot] GSAP proxy flush complete (299ms)
+[initSession:screenshot] GSAP proxy flush complete (296ms)
+[initSession:screenshot] pollHfReady complete (300ms)
+[initSession:screenshot] pollHfReady complete (303ms)
+[initSession:screenshot] pollSubCompositionTimelines complete (ready) (301ms)
+[initSession:screenshot] applyVideoMetadataHints complete (301ms)
+[initSession:screenshot] pollSubCompositionTimelines complete (ready) (305ms)
+[initSession:screenshot] applyVideoMetadataHints complete (305ms)
+[initSession:screenshot] media + fonts + tailwind ready (303ms)
+[initSession:screenshot] media + fonts + tailwind ready (306ms)
+[initSession:screenshot] static-frame dedup: disabled (canvas/webgl) (306ms)
+[initSession:screenshot] static-frame dedup: disabled (canvas/webgl) (310ms)
+  ██████░░░░░░░░░░░░░░░░░░░  27%  Capturing frame 30/600 (2 workers)
+  ███████░░░░░░░░░░░░░░░░░░  30%  Capturing frame 60/600 (2 workers)
+  ████████░░░░░░░░░░░░░░░░░  32%  Capturing frame 90/600 (2 workers)
+  ████████░░░░░░░░░░░░░░░░░  34%  Capturing frame 120/600 (2 workers)
+  █████████░░░░░░░░░░░░░░░░  36%  Capturing frame 150/600 (2 workers)
+  █████████░░░░░░░░░░░░░░░░  39%  Capturing frame 180/600 (2 workers)
+  ██████████░░░░░░░░░░░░░░░  41%  Capturing frame 210/600 (2 workers)
+  ██████████░░░░░░░░░░░░░░░  43%  Capturing frame 240/600 (2 workers)
+  ███████████░░░░░░░░░░░░░░  45%  Capturing frame 270/600 (2 workers)
+  ████████████░░░░░░░░░░░░░  48%  Capturing frame 300/600 (2 workers)
+  ████████████░░░░░░░░░░░░░  50%  Capturing frame 330/600 (2 workers)
+  █████████████░░░░░░░░░░░░  52%  Capturing frame 360/600 (2 workers)
+  █████████████░░░░░░░░░░░░  54%  Capturing frame 390/600 (2 workers)
+  ██████████████░░░░░░░░░░░  57%  Capturing frame 420/600 (2 workers)
+  ██████████████░░░░░░░░░░░  59%  Capturing frame 450/600 (2 workers)
+  ███████████████░░░░░░░░░░  61%  Capturing frame 480/600 (2 workers)
+  ███████████████░░░░░░░░░░  63%  Capturing frame 510/600 (2 workers)
+  ████████████████░░░░░░░░░  66%  Capturing frame 540/600 (2 workers)
+  █████████████████░░░░░░░░  68%  Capturing frame 570/600 (2 workers)
+  █████████████████░░░░░░░░  70%  Capturing frame 600/600 (2 workers)
+  ██████████████████░░░░░░░  75%  Encoding video
+  ██████████████████████░░░  90%  Assembling final video
+  █████████████████████████  100%  Render complete
+
+◇  /Users/macstudio3/Projects/wallpaper-paint-antics/series2/renders/06-built-for-busy-spaces.mp4
+   10.0 MB · 20.0s video · rendered in 18.8s
+
+[INFO] [Render] Pipeline started {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","platform":"darwin","arch":"arm64","nodeVersion":"v26.4.0","fps":{"num":30,"den":1},"format":"mp4","quality":"high","browserGpuMode":"auto","forceScreenshot":false,"protocolTimeout":300000,"browserTimeout":120000,"pageNavigationTimeout":60000,"playerReadyTimeout":45000}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"pipeline","status":"checkpoint","elapsedMs":2,"message":"started","format":"mp4","quality":"high","browserGpuMode":"auto","forceScreenshot":false,"protocolTimeoutMs":300000,"pageNavigationTimeoutMs":60000,"playerReadyTimeoutMs":45000,"requestedWorkers":2}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"compile","status":"start","elapsedMs":3,"needsAlpha":false}
+[INFO] [Compiler] Embedded local font file: assets/georgia.ttf (371 KB → data URI)
+[INFO] [Compiler] Embedded local font file: assets/verdana.ttf (182 KB → data URI)
+[INFO] Compiled composition metadata {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","entryFile":"episodes/06-built-for-busy-spaces.html","staticDuration":20,"width":1080,"height":1920,"videoCount":0,"audioCount":1,"renderModeHints":{"recommendScreenshot":false,"reasons":[]}}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"compile","status":"end","elapsedMs":67,"durationMs":64,"needsAlpha":false}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"compile","status":"checkpoint","elapsedMs":67,"message":"composition metadata resolved","width":1080,"height":1920,"videoCount":0,"audioCount":1,"imageCount":0,"deviceScaleFactor":1,"forceScreenshot":false,"compositionHash":"e0455581ddbb79c3"}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"browser_probe","status":"start","elapsedMs":67,"forceScreenshot":false,"stagePhase":"calibrating"}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"browser_probe","status":"end","elapsedMs":71,"durationMs":3,"forceScreenshot":false,"stagePhase":"calibrating"}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"browser_probe","status":"checkpoint","elapsedMs":71,"message":"duration resolved","durationSeconds":20,"totalFrames":600,"compositionHash":"e0455581ddbb79c3","beginFrameStalled":false}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"video_extract","status":"start","elapsedMs":71,"videoCount":0}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"video_extract","status":"end","elapsedMs":72,"durationMs":1,"videoCount":0}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"video_extract","status":"checkpoint","elapsedMs":73,"message":"frames resolved","videoCount":0,"extractedVideoCount":0,"totalFramesExtracted":0,"maxFramesPerVideo":0,"avgFramesPerExtractedVideo":null,"vfrPreflightCount":null,"vfrPreflightMs":null,"cacheHits":null,"cacheMisses":null,"transientRetries":null,"minVideoFrameCoverageRatio":null,"authoredTimedClipCount":3}
+[INFO] [Render] No HDR sources detected — rendering SDR {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3"}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"hdr_detection","status":"checkpoint","elapsedMs":73,"message":"resolved","requestedHdrMode":"auto","effectiveHdr":"sdr","nativeHdrVideoCount":0,"nativeHdrImageCount":0}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"audio_process","status":"start","elapsedMs":73,"audioCount":1}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"audio_process","status":"end","elapsedMs":470,"durationMs":397,"audioCount":1}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"file_server","status":"start","elapsedMs":470,"reused":false}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"file_server","status":"end","elapsedMs":476,"durationMs":6}
+[hyperframes] browserGpuMode probe → hardware (WebGL renderer vendor="Google Inc. (Apple)" renderer="ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Ultra, Unspecified Version)")
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"capture_calibration","status":"checkpoint","elapsedMs":667,"message":"skipped","requestedWorkers":2,"totalFrames":600,"htmlInCanvasDetected":false,"lowMemoryMode":false,"deInversionEligible":false,"deParallelRouterEligible":false}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"worker_resolution","status":"checkpoint","elapsedMs":667,"message":"resolved","workerCount":2}
+[INFO] streaming-encode gate {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","enabled":false,"configFlag":true,"outputFormat":"mp4","workerCount":2,"durationSeconds":20,"maxDurationSeconds":240}
+[INFO] [Render] Fast capture: default-on drawElement disabled for this render — parallel capture has no runtime self-verification. Set PRODUCER_EXPERIMENTAL_FAST_CAPTURE=true to override. {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3"}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"capture_strategy","status":"checkpoint","elapsedMs":667,"message":"resolved","workerCount":2,"forceScreenshot":false,"captureBeyondViewport":null,"useStreamingEncode":false,"useLayeredComposite":false,"usePageSideCompositing":false,"hasHdrContent":false,"hasShaderTransitions":false,"isPngSequence":false}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"capture_disk","status":"start","elapsedMs":667,"stagePhase":"capturing","needsAlpha":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":0,"captureMode":"beginframe","captureOperation":"beginFrame"}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"capture_disk","status":"end","elapsedMs":15595,"durationMs":14928,"stagePhase":"capturing","needsAlpha":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"encode","status":"start","elapsedMs":15595,"stagePhase":"capturing","hasAudio":true,"isPngSequence":false,"isGif":false,"chunkedEncode":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"encode","status":"end","elapsedMs":18121,"durationMs":2526,"stagePhase":"capturing","hasAudio":true,"isPngSequence":false,"isGif":false,"chunkedEncode":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"assemble","status":"start","elapsedMs":18121,"stagePhase":"capturing","hasAudio":true,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"assemble","status":"end","elapsedMs":18643,"durationMs":522,"stagePhase":"capturing","hasAudio":true,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"03ff6e57-9352-4e2d-99f5-2365552ee0d3","phase":"pipeline","status":"checkpoint","elapsedMs":18691,"message":"artifact validated","totalElapsedMs":18688}
diff --git a/series2/verification/07-sample-before-specifying-1.3.png b/series2/verification/07-sample-before-specifying-1.3.png
new file mode 100644
index 0000000..ad26576
Binary files /dev/null and b/series2/verification/07-sample-before-specifying-1.3.png differ
diff --git a/series2/verification/07-sample-before-specifying-11.6.png b/series2/verification/07-sample-before-specifying-11.6.png
new file mode 100644
index 0000000..37f6e6c
Binary files /dev/null and b/series2/verification/07-sample-before-specifying-11.6.png differ
diff --git a/series2/verification/07-sample-before-specifying-16.5.png b/series2/verification/07-sample-before-specifying-16.5.png
new file mode 100644
index 0000000..58779f9
Binary files /dev/null and b/series2/verification/07-sample-before-specifying-16.5.png differ
diff --git a/series2/verification/07-sample-before-specifying-19.png b/series2/verification/07-sample-before-specifying-19.png
new file mode 100644
index 0000000..2c542bb
Binary files /dev/null and b/series2/verification/07-sample-before-specifying-19.png differ
diff --git a/series2/verification/07-sample-before-specifying-3.5.png b/series2/verification/07-sample-before-specifying-3.5.png
new file mode 100644
index 0000000..ff0b379
Binary files /dev/null and b/series2/verification/07-sample-before-specifying-3.5.png differ
diff --git a/series2/verification/07-sample-before-specifying-6.2.png b/series2/verification/07-sample-before-specifying-6.2.png
new file mode 100644
index 0000000..44d8d3c
Binary files /dev/null and b/series2/verification/07-sample-before-specifying-6.2.png differ
diff --git a/series2/verification/07-sample-before-specifying-8.2.png b/series2/verification/07-sample-before-specifying-8.2.png
new file mode 100644
index 0000000..35c96e6
Binary files /dev/null and b/series2/verification/07-sample-before-specifying-8.2.png differ
diff --git a/series2/verification/07-sample-before-specifying-check.txt b/series2/verification/07-sample-before-specifying-check.txt
new file mode 100644
index 0000000..10644ae
--- /dev/null
+++ b/series2/verification/07-sample-before-specifying-check.txt
@@ -0,0 +1,82 @@
+{
+  "ok": true,
+  "strict": false,
+  "lint": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": [],
+    "filesScanned": 1
+  },
+  "runtime": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": []
+  },
+  "layout": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": [],
+    "duration": 20,
+    "samples": [
+      1.111,
+      3.333,
+      5.556,
+      7.778,
+      10,
+      12.222,
+      14.444,
+      16.667,
+      18.889
+    ],
+    "transitionSamples": [],
+    "transitionSamplesDropped": 0,
+    "tolerance": 2,
+    "totalIssueCount": 0,
+    "truncated": false
+  },
+  "motion": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": [],
+    "enabled": false,
+    "samples": 0
+  },
+  "contrast": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": [],
+    "enabled": true,
+    "samples": [
+      1.111,
+      5.556,
+      10,
+      14.444,
+      18.889
+    ],
+    "checked": 0,
+    "passed": 0
+  },
+  "snapshots": {
+    "enabled": false,
+    "files": [],
+    "times": [],
+    "findingFiles": []
+  },
+  "_meta": {
+    "version": "0.8.29",
+    "latestVersion": "0.8.29",
+    "updateAvailable": false
+  }
+}
+
+[hyperframes] browserGpuMode probe → hardware (WebGL renderer vendor="Google Inc. (Apple)" renderer="ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Ultra, Unspecified Version)")
diff --git a/series2/verification/07-sample-before-specifying-decode.txt b/series2/verification/07-sample-before-specifying-decode.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/series2/verification/07-sample-before-specifying-decode.txt
@@ -0,0 +1 @@
+
diff --git a/series2/verification/07-sample-before-specifying-mp4-1.png b/series2/verification/07-sample-before-specifying-mp4-1.png
new file mode 100644
index 0000000..dd9e027
Binary files /dev/null and b/series2/verification/07-sample-before-specifying-mp4-1.png differ
diff --git a/series2/verification/07-sample-before-specifying-mp4-12.png b/series2/verification/07-sample-before-specifying-mp4-12.png
new file mode 100644
index 0000000..9c15656
Binary files /dev/null and b/series2/verification/07-sample-before-specifying-mp4-12.png differ
diff --git a/series2/verification/07-sample-before-specifying-mp4-19.png b/series2/verification/07-sample-before-specifying-mp4-19.png
new file mode 100644
index 0000000..ca49ac8
Binary files /dev/null and b/series2/verification/07-sample-before-specifying-mp4-19.png differ
diff --git a/series2/verification/07-sample-before-specifying-mp4-7.png b/series2/verification/07-sample-before-specifying-mp4-7.png
new file mode 100644
index 0000000..0dde72a
Binary files /dev/null and b/series2/verification/07-sample-before-specifying-mp4-7.png differ
diff --git a/series2/verification/07-sample-before-specifying-render.txt b/series2/verification/07-sample-before-specifying-render.txt
new file mode 100644
index 0000000..6c7239e
--- /dev/null
+++ b/series2/verification/07-sample-before-specifying-render.txt
@@ -0,0 +1,96 @@
+
+◆  Rendering series2/episodes/07-sample-before-specifying.html → /Users/macstudio3/Projects/wallpaper-paint-antics/series2/renders/07-sample-before-specifying.mp4
+   30fps · high · 2 workers
+   GPU: browser GPU (auto-detect)
+
+[?25l│
+◇  Browser: cache
+[?25h
+  ✗ invalid_parent_traversal_in_asset_path: Found 4 asset path(s) traversing above the project root with "../" (../assets/ (4)). Renders rewrite this against each sub-composition's source path, but Studio preview and other live consumers resolve against the project root and 404.
+    Fix: Use plain root-relative paths (e.g. "assets/...", "capture/...", "fonts/...") — compositions are served with the project root as their base URL, so paths must be root-relative, not relative to the compositions/ directory.
+  Continuing render despite lint issues. Use --strict to block errors.
+
+  Parallel drawElement capture stays off for this install (a previous render had to fall back). Re-enable with HF_DE_PARALLEL_ROUTER=true.
+  █░░░░░░░░░░░░░░░░░░░░░░░░  5%  Compiling composition
+  ██░░░░░░░░░░░░░░░░░░░░░░░  10%  Extracting video frames
+  █████░░░░░░░░░░░░░░░░░░░░  20%  Processing audio tracks
+  ██████░░░░░░░░░░░░░░░░░░░  25%  Starting frame capture
+[BrowserManager] Browser launched (HeadlessChrome/152.0.7977.30, screenshot, gl=--use-gl=angle --use-angle=metal, headlessShell=true, platform=darwin)
+[initSession:screenshot] page.goto start (0ms)
+[initSession:screenshot] page.goto start (0ms)
+[HyperFrames] render runtime fps [object Object]
+[HyperFrames] render runtime fps [object Object]
+[initSession:screenshot] page.goto complete (121ms)
+[initSession:screenshot] page.goto complete (122ms)
+[initSession:screenshot] GSAP proxy flush complete (294ms)
+[initSession:screenshot] GSAP proxy flush complete (295ms)
+[initSession:screenshot] pollHfReady complete (299ms)
+[initSession:screenshot] pollHfReady complete (299ms)
+[initSession:screenshot] pollSubCompositionTimelines complete (ready) (300ms)
+[initSession:screenshot] applyVideoMetadataHints complete (300ms)
+[initSession:screenshot] pollSubCompositionTimelines complete (ready) (301ms)
+[initSession:screenshot] applyVideoMetadataHints complete (301ms)
+[initSession:screenshot] media + fonts + tailwind ready (302ms)
+[initSession:screenshot] media + fonts + tailwind ready (302ms)
+[initSession:screenshot] static-frame dedup: disabled (canvas/webgl) (306ms)
+[initSession:screenshot] static-frame dedup: disabled (canvas/webgl) (306ms)
+  ██████░░░░░░░░░░░░░░░░░░░  27%  Capturing frame 30/600 (2 workers)
+  ███████░░░░░░░░░░░░░░░░░░  30%  Capturing frame 60/600 (2 workers)
+  ████████░░░░░░░░░░░░░░░░░  32%  Capturing frame 90/600 (2 workers)
+  ████████░░░░░░░░░░░░░░░░░  34%  Capturing frame 120/600 (2 workers)
+  █████████░░░░░░░░░░░░░░░░  36%  Capturing frame 150/600 (2 workers)
+  █████████░░░░░░░░░░░░░░░░  39%  Capturing frame 180/600 (2 workers)
+  ██████████░░░░░░░░░░░░░░░  41%  Capturing frame 210/600 (2 workers)
+  ██████████░░░░░░░░░░░░░░░  43%  Capturing frame 240/600 (2 workers)
+  ███████████░░░░░░░░░░░░░░  45%  Capturing frame 270/600 (2 workers)
+  ████████████░░░░░░░░░░░░░  48%  Capturing frame 300/600 (2 workers)
+  ████████████░░░░░░░░░░░░░  50%  Capturing frame 330/600 (2 workers)
+  █████████████░░░░░░░░░░░░  52%  Capturing frame 360/600 (2 workers)
+  █████████████░░░░░░░░░░░░  54%  Capturing frame 390/600 (2 workers)
+  ██████████████░░░░░░░░░░░  57%  Capturing frame 420/600 (2 workers)
+  ██████████████░░░░░░░░░░░  59%  Capturing frame 450/600 (2 workers)
+  ███████████████░░░░░░░░░░  61%  Capturing frame 480/600 (2 workers)
+  ███████████████░░░░░░░░░░  63%  Capturing frame 510/600 (2 workers)
+  ████████████████░░░░░░░░░  66%  Capturing frame 540/600 (2 workers)
+  █████████████████░░░░░░░░  68%  Capturing frame 570/600 (2 workers)
+  █████████████████░░░░░░░░  70%  Capturing frame 600/600 (2 workers)
+  ██████████████████░░░░░░░  75%  Encoding video
+  ██████████████████████░░░  90%  Assembling final video
+  █████████████████████████  100%  Render complete
+
+◇  /Users/macstudio3/Projects/wallpaper-paint-antics/series2/renders/07-sample-before-specifying.mp4
+   16.7 MB · 20.0s video · rendered in 19.6s
+
+[INFO] [Render] Pipeline started {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","platform":"darwin","arch":"arm64","nodeVersion":"v26.4.0","fps":{"num":30,"den":1},"format":"mp4","quality":"high","browserGpuMode":"auto","forceScreenshot":false,"protocolTimeout":300000,"browserTimeout":120000,"pageNavigationTimeout":60000,"playerReadyTimeout":45000}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"pipeline","status":"checkpoint","elapsedMs":1,"message":"started","format":"mp4","quality":"high","browserGpuMode":"auto","forceScreenshot":false,"protocolTimeoutMs":300000,"pageNavigationTimeoutMs":60000,"playerReadyTimeoutMs":45000,"requestedWorkers":2}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"compile","status":"start","elapsedMs":2,"needsAlpha":false}
+[INFO] [Compiler] Embedded local font file: assets/georgia.ttf (371 KB → data URI)
+[INFO] [Compiler] Embedded local font file: assets/verdana.ttf (182 KB → data URI)
+[INFO] Compiled composition metadata {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","entryFile":"episodes/07-sample-before-specifying.html","staticDuration":20,"width":1080,"height":1920,"videoCount":0,"audioCount":1,"renderModeHints":{"recommendScreenshot":false,"reasons":[]}}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"compile","status":"end","elapsedMs":67,"durationMs":64,"needsAlpha":false}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"compile","status":"checkpoint","elapsedMs":68,"message":"composition metadata resolved","width":1080,"height":1920,"videoCount":0,"audioCount":1,"imageCount":0,"deviceScaleFactor":1,"forceScreenshot":false,"compositionHash":"ff68578a5f8e8d0b"}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"browser_probe","status":"start","elapsedMs":68,"forceScreenshot":false,"stagePhase":"calibrating"}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"browser_probe","status":"end","elapsedMs":71,"durationMs":3,"forceScreenshot":false,"stagePhase":"calibrating"}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"browser_probe","status":"checkpoint","elapsedMs":71,"message":"duration resolved","durationSeconds":20,"totalFrames":600,"compositionHash":"ff68578a5f8e8d0b","beginFrameStalled":false}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"video_extract","status":"start","elapsedMs":72,"videoCount":0}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"video_extract","status":"end","elapsedMs":72,"durationMs":0,"videoCount":0}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"video_extract","status":"checkpoint","elapsedMs":73,"message":"frames resolved","videoCount":0,"extractedVideoCount":0,"totalFramesExtracted":0,"maxFramesPerVideo":0,"avgFramesPerExtractedVideo":null,"vfrPreflightCount":null,"vfrPreflightMs":null,"cacheHits":null,"cacheMisses":null,"transientRetries":null,"minVideoFrameCoverageRatio":null,"authoredTimedClipCount":3}
+[INFO] [Render] No HDR sources detected — rendering SDR {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67"}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"hdr_detection","status":"checkpoint","elapsedMs":73,"message":"resolved","requestedHdrMode":"auto","effectiveHdr":"sdr","nativeHdrVideoCount":0,"nativeHdrImageCount":0}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"audio_process","status":"start","elapsedMs":73,"audioCount":1}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"audio_process","status":"end","elapsedMs":457,"durationMs":384,"audioCount":1}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"file_server","status":"start","elapsedMs":457,"reused":false}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"file_server","status":"end","elapsedMs":462,"durationMs":5}
+[hyperframes] browserGpuMode probe → hardware (WebGL renderer vendor="Google Inc. (Apple)" renderer="ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Ultra, Unspecified Version)")
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"capture_calibration","status":"checkpoint","elapsedMs":654,"message":"skipped","requestedWorkers":2,"totalFrames":600,"htmlInCanvasDetected":false,"lowMemoryMode":false,"deInversionEligible":false,"deParallelRouterEligible":false}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"worker_resolution","status":"checkpoint","elapsedMs":654,"message":"resolved","workerCount":2}
+[INFO] streaming-encode gate {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","enabled":false,"configFlag":true,"outputFormat":"mp4","workerCount":2,"durationSeconds":20,"maxDurationSeconds":240}
+[INFO] [Render] Fast capture: default-on drawElement disabled for this render — parallel capture has no runtime self-verification. Set PRODUCER_EXPERIMENTAL_FAST_CAPTURE=true to override. {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67"}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"capture_strategy","status":"checkpoint","elapsedMs":654,"message":"resolved","workerCount":2,"forceScreenshot":false,"captureBeyondViewport":null,"useStreamingEncode":false,"useLayeredComposite":false,"usePageSideCompositing":false,"hasHdrContent":false,"hasShaderTransitions":false,"isPngSequence":false}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"capture_disk","status":"start","elapsedMs":654,"stagePhase":"capturing","needsAlpha":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":0,"captureMode":"beginframe","captureOperation":"beginFrame"}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"capture_disk","status":"end","elapsedMs":15880,"durationMs":15226,"stagePhase":"capturing","needsAlpha":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"encode","status":"start","elapsedMs":15880,"stagePhase":"capturing","hasAudio":true,"isPngSequence":false,"isGif":false,"chunkedEncode":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"encode","status":"end","elapsedMs":18990,"durationMs":3110,"stagePhase":"capturing","hasAudio":true,"isPngSequence":false,"isGif":false,"chunkedEncode":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"assemble","status":"start","elapsedMs":18991,"stagePhase":"capturing","hasAudio":true,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"assemble","status":"end","elapsedMs":19491,"durationMs":500,"stagePhase":"capturing","hasAudio":true,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"7c22a03b-b61a-471c-99bf-e52f96072a67","phase":"pipeline","status":"checkpoint","elapsedMs":19539,"message":"artifact validated","totalElapsedMs":19536}
diff --git a/series2/verification/08-service-at-dw-1.3.png b/series2/verification/08-service-at-dw-1.3.png
new file mode 100644
index 0000000..a8775c0
Binary files /dev/null and b/series2/verification/08-service-at-dw-1.3.png differ
diff --git a/series2/verification/08-service-at-dw-11.6.png b/series2/verification/08-service-at-dw-11.6.png
new file mode 100644
index 0000000..8a37cd0
Binary files /dev/null and b/series2/verification/08-service-at-dw-11.6.png differ
diff --git a/series2/verification/08-service-at-dw-16.5.png b/series2/verification/08-service-at-dw-16.5.png
new file mode 100644
index 0000000..4dc731b
Binary files /dev/null and b/series2/verification/08-service-at-dw-16.5.png differ
diff --git a/series2/verification/08-service-at-dw-19.png b/series2/verification/08-service-at-dw-19.png
new file mode 100644
index 0000000..54ddb9d
Binary files /dev/null and b/series2/verification/08-service-at-dw-19.png differ
diff --git a/series2/verification/08-service-at-dw-3.5.png b/series2/verification/08-service-at-dw-3.5.png
new file mode 100644
index 0000000..775add7
Binary files /dev/null and b/series2/verification/08-service-at-dw-3.5.png differ
diff --git a/series2/verification/08-service-at-dw-6.2.png b/series2/verification/08-service-at-dw-6.2.png
new file mode 100644
index 0000000..2cd94c6
Binary files /dev/null and b/series2/verification/08-service-at-dw-6.2.png differ
diff --git a/series2/verification/08-service-at-dw-8.2.png b/series2/verification/08-service-at-dw-8.2.png
new file mode 100644
index 0000000..39da3e8
Binary files /dev/null and b/series2/verification/08-service-at-dw-8.2.png differ
diff --git a/series2/verification/08-service-at-dw-check.txt b/series2/verification/08-service-at-dw-check.txt
new file mode 100644
index 0000000..10644ae
--- /dev/null
+++ b/series2/verification/08-service-at-dw-check.txt
@@ -0,0 +1,82 @@
+{
+  "ok": true,
+  "strict": false,
+  "lint": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": [],
+    "filesScanned": 1
+  },
+  "runtime": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": []
+  },
+  "layout": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": [],
+    "duration": 20,
+    "samples": [
+      1.111,
+      3.333,
+      5.556,
+      7.778,
+      10,
+      12.222,
+      14.444,
+      16.667,
+      18.889
+    ],
+    "transitionSamples": [],
+    "transitionSamplesDropped": 0,
+    "tolerance": 2,
+    "totalIssueCount": 0,
+    "truncated": false
+  },
+  "motion": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": [],
+    "enabled": false,
+    "samples": 0
+  },
+  "contrast": {
+    "ok": true,
+    "errorCount": 0,
+    "warningCount": 0,
+    "infoCount": 0,
+    "findings": [],
+    "enabled": true,
+    "samples": [
+      1.111,
+      5.556,
+      10,
+      14.444,
+      18.889
+    ],
+    "checked": 0,
+    "passed": 0
+  },
+  "snapshots": {
+    "enabled": false,
+    "files": [],
+    "times": [],
+    "findingFiles": []
+  },
+  "_meta": {
+    "version": "0.8.29",
+    "latestVersion": "0.8.29",
+    "updateAvailable": false
+  }
+}
+
+[hyperframes] browserGpuMode probe → hardware (WebGL renderer vendor="Google Inc. (Apple)" renderer="ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Ultra, Unspecified Version)")
diff --git a/series2/verification/08-service-at-dw-decode.txt b/series2/verification/08-service-at-dw-decode.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/series2/verification/08-service-at-dw-decode.txt
@@ -0,0 +1 @@
+
diff --git a/series2/verification/08-service-at-dw-mp4-1.png b/series2/verification/08-service-at-dw-mp4-1.png
new file mode 100644
index 0000000..f9532f9
Binary files /dev/null and b/series2/verification/08-service-at-dw-mp4-1.png differ
diff --git a/series2/verification/08-service-at-dw-mp4-12.png b/series2/verification/08-service-at-dw-mp4-12.png
new file mode 100644
index 0000000..edd7a25
Binary files /dev/null and b/series2/verification/08-service-at-dw-mp4-12.png differ
diff --git a/series2/verification/08-service-at-dw-mp4-19.png b/series2/verification/08-service-at-dw-mp4-19.png
new file mode 100644
index 0000000..9df9f77
Binary files /dev/null and b/series2/verification/08-service-at-dw-mp4-19.png differ
diff --git a/series2/verification/08-service-at-dw-mp4-7.png b/series2/verification/08-service-at-dw-mp4-7.png
new file mode 100644
index 0000000..681cff7
Binary files /dev/null and b/series2/verification/08-service-at-dw-mp4-7.png differ
diff --git a/series2/verification/08-service-at-dw-render.txt b/series2/verification/08-service-at-dw-render.txt
new file mode 100644
index 0000000..72bbaa0
--- /dev/null
+++ b/series2/verification/08-service-at-dw-render.txt
@@ -0,0 +1,96 @@
+
+◆  Rendering series2/episodes/08-service-at-dw.html → /Users/macstudio3/Projects/wallpaper-paint-antics/series2/renders/08-service-at-dw.mp4
+   30fps · high · 2 workers
+   GPU: browser GPU (auto-detect)
+
+[?25l│
+◇  Browser: cache
+[?25h
+  ✗ invalid_parent_traversal_in_asset_path: Found 4 asset path(s) traversing above the project root with "../" (../assets/ (4)). Renders rewrite this against each sub-composition's source path, but Studio preview and other live consumers resolve against the project root and 404.
+    Fix: Use plain root-relative paths (e.g. "assets/...", "capture/...", "fonts/...") — compositions are served with the project root as their base URL, so paths must be root-relative, not relative to the compositions/ directory.
+  Continuing render despite lint issues. Use --strict to block errors.
+
+  Parallel drawElement capture stays off for this install (a previous render had to fall back). Re-enable with HF_DE_PARALLEL_ROUTER=true.
+  █░░░░░░░░░░░░░░░░░░░░░░░░  5%  Compiling composition
+  ██░░░░░░░░░░░░░░░░░░░░░░░  10%  Extracting video frames
+  █████░░░░░░░░░░░░░░░░░░░░  20%  Processing audio tracks
+  ██████░░░░░░░░░░░░░░░░░░░  25%  Starting frame capture
+[BrowserManager] Browser launched (HeadlessChrome/152.0.7977.30, screenshot, gl=--use-gl=angle --use-angle=metal, headlessShell=true, platform=darwin)
+[initSession:screenshot] page.goto start (0ms)
+[initSession:screenshot] page.goto start (0ms)
+[HyperFrames] render runtime fps [object Object]
+[HyperFrames] render runtime fps [object Object]
+[initSession:screenshot] page.goto complete (128ms)
+[initSession:screenshot] page.goto complete (129ms)
+[initSession:screenshot] GSAP proxy flush complete (148ms)
+[initSession:screenshot] GSAP proxy flush complete (148ms)
+[initSession:screenshot] pollHfReady complete (149ms)
+[initSession:screenshot] pollHfReady complete (152ms)
+[initSession:screenshot] pollSubCompositionTimelines complete (ready) (156ms)
+[initSession:screenshot] applyVideoMetadataHints complete (156ms)
+[initSession:screenshot] pollSubCompositionTimelines complete (ready) (157ms)
+[initSession:screenshot] applyVideoMetadataHints complete (157ms)
+[initSession:screenshot] media + fonts + tailwind ready (158ms)
+[initSession:screenshot] media + fonts + tailwind ready (158ms)
+[initSession:screenshot] static-frame dedup: disabled (canvas/webgl) (162ms)
+[initSession:screenshot] static-frame dedup: disabled (canvas/webgl) (162ms)
+  ██████░░░░░░░░░░░░░░░░░░░  27%  Capturing frame 30/600 (2 workers)
+  ███████░░░░░░░░░░░░░░░░░░  30%  Capturing frame 60/600 (2 workers)
+  ████████░░░░░░░░░░░░░░░░░  32%  Capturing frame 90/600 (2 workers)
+  ████████░░░░░░░░░░░░░░░░░  34%  Capturing frame 120/600 (2 workers)
+  █████████░░░░░░░░░░░░░░░░  36%  Capturing frame 150/600 (2 workers)
+  █████████░░░░░░░░░░░░░░░░  39%  Capturing frame 180/600 (2 workers)
+  ██████████░░░░░░░░░░░░░░░  41%  Capturing frame 210/600 (2 workers)
+  ██████████░░░░░░░░░░░░░░░  43%  Capturing frame 240/600 (2 workers)
+  ███████████░░░░░░░░░░░░░░  45%  Capturing frame 270/600 (2 workers)
+  ████████████░░░░░░░░░░░░░  48%  Capturing frame 300/600 (2 workers)
+  ████████████░░░░░░░░░░░░░  50%  Capturing frame 330/600 (2 workers)
+  █████████████░░░░░░░░░░░░  52%  Capturing frame 360/600 (2 workers)
+  █████████████░░░░░░░░░░░░  54%  Capturing frame 390/600 (2 workers)
+  ██████████████░░░░░░░░░░░  57%  Capturing frame 420/600 (2 workers)
+  ██████████████░░░░░░░░░░░  59%  Capturing frame 450/600 (2 workers)
+  ███████████████░░░░░░░░░░  61%  Capturing frame 480/600 (2 workers)
+  ███████████████░░░░░░░░░░  63%  Capturing frame 510/600 (2 workers)
+  ████████████████░░░░░░░░░  66%  Capturing frame 540/600 (2 workers)
+  █████████████████░░░░░░░░  68%  Capturing frame 570/600 (2 workers)
+  █████████████████░░░░░░░░  70%  Capturing frame 600/600 (2 workers)
+  ██████████████████░░░░░░░  75%  Encoding video
+  ██████████████████████░░░  90%  Assembling final video
+  █████████████████████████  100%  Render complete
+
+◇  /Users/macstudio3/Projects/wallpaper-paint-antics/series2/renders/08-service-at-dw.mp4
+   9.4 MB · 20.0s video · rendered in 18.3s
+
+[INFO] [Render] Pipeline started {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","platform":"darwin","arch":"arm64","nodeVersion":"v26.4.0","fps":{"num":30,"den":1},"format":"mp4","quality":"high","browserGpuMode":"auto","forceScreenshot":false,"protocolTimeout":300000,"browserTimeout":120000,"pageNavigationTimeout":60000,"playerReadyTimeout":45000}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"pipeline","status":"checkpoint","elapsedMs":2,"message":"started","format":"mp4","quality":"high","browserGpuMode":"auto","forceScreenshot":false,"protocolTimeoutMs":300000,"pageNavigationTimeoutMs":60000,"playerReadyTimeoutMs":45000,"requestedWorkers":2}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"compile","status":"start","elapsedMs":3,"needsAlpha":false}
+[INFO] [Compiler] Embedded local font file: assets/georgia.ttf (371 KB → data URI)
+[INFO] [Compiler] Embedded local font file: assets/verdana.ttf (182 KB → data URI)
+[INFO] Compiled composition metadata {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","entryFile":"episodes/08-service-at-dw.html","staticDuration":20,"width":1080,"height":1920,"videoCount":0,"audioCount":1,"renderModeHints":{"recommendScreenshot":false,"reasons":[]}}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"compile","status":"end","elapsedMs":68,"durationMs":65,"needsAlpha":false}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"compile","status":"checkpoint","elapsedMs":69,"message":"composition metadata resolved","width":1080,"height":1920,"videoCount":0,"audioCount":1,"imageCount":0,"deviceScaleFactor":1,"forceScreenshot":false,"compositionHash":"1eaa969eb571cb69"}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"browser_probe","status":"start","elapsedMs":69,"forceScreenshot":false,"stagePhase":"calibrating"}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"browser_probe","status":"end","elapsedMs":72,"durationMs":3,"forceScreenshot":false,"stagePhase":"calibrating"}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"browser_probe","status":"checkpoint","elapsedMs":73,"message":"duration resolved","durationSeconds":20,"totalFrames":600,"compositionHash":"1eaa969eb571cb69","beginFrameStalled":false}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"video_extract","status":"start","elapsedMs":73,"videoCount":0}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"video_extract","status":"end","elapsedMs":73,"durationMs":0,"videoCount":0}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"video_extract","status":"checkpoint","elapsedMs":74,"message":"frames resolved","videoCount":0,"extractedVideoCount":0,"totalFramesExtracted":0,"maxFramesPerVideo":0,"avgFramesPerExtractedVideo":null,"vfrPreflightCount":null,"vfrPreflightMs":null,"cacheHits":null,"cacheMisses":null,"transientRetries":null,"minVideoFrameCoverageRatio":null,"authoredTimedClipCount":3}
+[INFO] [Render] No HDR sources detected — rendering SDR {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1"}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"hdr_detection","status":"checkpoint","elapsedMs":74,"message":"resolved","requestedHdrMode":"auto","effectiveHdr":"sdr","nativeHdrVideoCount":0,"nativeHdrImageCount":0}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"audio_process","status":"start","elapsedMs":74,"audioCount":1}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"audio_process","status":"end","elapsedMs":460,"durationMs":386,"audioCount":1}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"file_server","status":"start","elapsedMs":460,"reused":false}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"file_server","status":"end","elapsedMs":466,"durationMs":6}
+[hyperframes] browserGpuMode probe → hardware (WebGL renderer vendor="Google Inc. (Apple)" renderer="ANGLE (Apple, ANGLE Metal Renderer: Apple M3 Ultra, Unspecified Version)")
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"capture_calibration","status":"checkpoint","elapsedMs":654,"message":"skipped","requestedWorkers":2,"totalFrames":600,"htmlInCanvasDetected":false,"lowMemoryMode":false,"deInversionEligible":false,"deParallelRouterEligible":false}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"worker_resolution","status":"checkpoint","elapsedMs":655,"message":"resolved","workerCount":2}
+[INFO] streaming-encode gate {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","enabled":false,"configFlag":true,"outputFormat":"mp4","workerCount":2,"durationSeconds":20,"maxDurationSeconds":240}
+[INFO] [Render] Fast capture: default-on drawElement disabled for this render — parallel capture has no runtime self-verification. Set PRODUCER_EXPERIMENTAL_FAST_CAPTURE=true to override. {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1"}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"capture_strategy","status":"checkpoint","elapsedMs":655,"message":"resolved","workerCount":2,"forceScreenshot":false,"captureBeyondViewport":null,"useStreamingEncode":false,"useLayeredComposite":false,"usePageSideCompositing":false,"hasHdrContent":false,"hasShaderTransitions":false,"isPngSequence":false}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"capture_disk","status":"start","elapsedMs":655,"stagePhase":"capturing","needsAlpha":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":0,"captureMode":"beginframe","captureOperation":"beginFrame"}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"capture_disk","status":"end","elapsedMs":15167,"durationMs":14512,"stagePhase":"capturing","needsAlpha":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"encode","status":"start","elapsedMs":15167,"stagePhase":"capturing","hasAudio":true,"isPngSequence":false,"isGif":false,"chunkedEncode":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"encode","status":"end","elapsedMs":17651,"durationMs":2483,"stagePhase":"capturing","hasAudio":true,"isPngSequence":false,"isGif":false,"chunkedEncode":false,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"assemble","status":"start","elapsedMs":17652,"stagePhase":"capturing","hasAudio":true,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"assemble","status":"end","elapsedMs":18170,"durationMs":518,"stagePhase":"capturing","hasAudio":true,"workerCount":2,"forceScreenshot":false,"totalFrames":600,"framesCompleted":600,"captureMode":"beginframe","captureOperation":"encode"}
+[INFO] [Render:trace] {"renderJobId":"5fa6a5f6-cd24-49d6-9d22-f082e9ae96a1","phase":"pipeline","status":"checkpoint","elapsedMs":18217,"message":"artifact validated","totalElapsedMs":18214}
diff --git a/series2/verification/3x.txt b/series2/verification/3x.txt
new file mode 100644
index 0000000..742807b
--- /dev/null
+++ b/series2/verification/3x.txt
@@ -0,0 +1,15 @@
+
+/3x — verifying http://127.0.0.1:49234/watch.html
+
+Phase A — 3 unique methods:
+  [PASS] M1 HTTP contract  request layer — HTTP 200 text/html
+  [PASS] M2 headless render  render layer — screenshot 284KB → /var/folders/rq/j8g1f7nn6jv6_lr1cfmqym6w0000gn/T/3x-kdEc4r/render.png
+  [PASS] M3 automation E2E  interaction layer — 1×"#presentation", clicked "[data-jump="80"]", 0 JS errors
+
+Phase B — 3 browsers (cross-browser):
+  [PASS] B4 Google Chrome  cross-browser — automated render OK
+  [SKIP] B5 Safari  cross-browser — engine not installed, --no-open set
+  [SKIP] B6 Firefox  cross-browser — engine not installed, --no-open set
+
+  RESULT → 4/4 passed, 2 skipped
+  artifacts: /var/folders/rq/j8g1f7nn6jv6_lr1cfmqym6w0000gn/T/3x-kdEc4r
diff --git a/series2/verification/checks.json b/series2/verification/checks.json
new file mode 100644
index 0000000..21a92e5
--- /dev/null
+++ b/series2/verification/checks.json
@@ -0,0 +1,217 @@
+[
+  {
+    "slug": "04-fire-rated",
+    "ok": true,
+    "lint": {
+      "ok": true,
+      "errorCount": 0,
+      "warningCount": 0,
+      "infoCount": 0,
+      "findings": [],
+      "filesScanned": 1
+    },
+    "runtime": {
+      "ok": true,
+      "errorCount": 0,
+      "warningCount": 0,
+      "infoCount": 0,
+      "findings": []
+    },
+    "layout": {
+      "ok": true,
+      "errorCount": 0,
+      "warningCount": 0,
+      "infoCount": 0,
+      "findings": [],
+      "duration": 20,
+      "samples": [
+        1.111,
+        3.333,
+        5.556,
+        7.778,
+        10,
+        12.222,
+        14.444,
+        16.667,
+        18.889
+      ],
+      "transitionSamples": [],
+      "transitionSamplesDropped": 0,
+      "tolerance": 2,
+      "totalIssueCount": 0,
+      "truncated": false
+    }
+  },
+  {
+    "slug": "05-wallpaper-or-paint",
+    "ok": true,
+    "lint": {
+      "ok": true,
+      "errorCount": 0,
+      "warningCount": 0,
+      "infoCount": 0,
+      "findings": [],
+      "filesScanned": 1
+    },
+    "runtime": {
+      "ok": true,
+      "errorCount": 0,
+      "warningCount": 0,
+      "infoCount": 0,
+      "findings": []
+    },
+    "layout": {
+      "ok": true,
+      "errorCount": 0,
+      "warningCount": 0,
+      "infoCount": 0,
+      "findings": [],
+      "duration": 20,
+      "samples": [
+        1.111,
+        3.333,
+        5.556,
+        7.778,
+        10,
+        12.222,
+        14.444,
+        16.667,
+        18.889
+      ],
+      "transitionSamples": [],
+      "transitionSamplesDropped": 0,
+      "tolerance": 2,
+      "totalIssueCount": 0,
+      "truncated": false
+    }
+  },
+  {
+    "slug": "06-built-for-busy-spaces",
+    "ok": true,
+    "lint": {
+      "ok": true,
+      "errorCount": 0,
+      "warningCount": 0,
+      "infoCount": 0,
+      "findings": [],
+      "filesScanned": 1
+    },
+    "runtime": {
+      "ok": true,
+      "errorCount": 0,
+      "warningCount": 0,
+      "infoCount": 0,
+      "findings": []
+    },
+    "layout": {
+      "ok": true,
+      "errorCount": 0,
+      "warningCount": 0,
+      "infoCount": 0,
+      "findings": [],
+      "duration": 20,
+      "samples": [
+        1.111,
+        3.333,
+        5.556,
+        7.778,
+        10,
+        12.222,
+        14.444,
+        16.667,
+        18.889
+      ],
+      "transitionSamples": [],
+      "transitionSamplesDropped": 0,
+      "tolerance": 2,
+      "totalIssueCount": 0,
+      "truncated": false
+    }
+  },
+  {
+    "slug": "07-sample-before-specifying",
+    "ok": true,
+    "lint": {
+      "ok": true,
+      "errorCount": 0,
+      "warningCount": 0,
+      "infoCount": 0,
+      "findings": [],
+      "filesScanned": 1
+    },
+    "runtime": {
+      "ok": true,
+      "errorCount": 0,
+      "warningCount": 0,
+      "infoCount": 0,
+      "findings": []
+    },
+    "layout": {
+      "ok": true,
+      "errorCount": 0,
+      "warningCount": 0,
+      "infoCount": 0,
+      "findings": [],
+      "duration": 20,
+      "samples": [
+        1.111,
+        3.333,
+        5.556,
+        7.778,
+        10,
+        12.222,
+        14.444,
+        16.667,
+        18.889
+      ],
+      "transitionSamples": [],
+      "transitionSamplesDropped": 0,
+      "tolerance": 2,
+      "totalIssueCount": 0,
+      "truncated": false
+    }
+  },
+  {
+    "slug": "08-service-at-dw",
+    "ok": true,
+    "lint": {
+      "ok": true,
+      "errorCount": 0,
+      "warningCount": 0,
+      "infoCount": 0,
+      "findings": [],
+      "filesScanned": 1
+    },
+    "runtime": {
+      "ok": true,
+      "errorCount": 0,
+      "warningCount": 0,
+      "infoCount": 0,
+      "findings": []
+    },
+    "layout": {
+      "ok": true,
+      "errorCount": 0,
+      "warningCount": 0,
+      "infoCount": 0,
+      "findings": [],
+      "duration": 20,
+      "samples": [
+        1.111,
+        3.333,
+        5.556,
+        7.778,
+        10,
+        12.222,
+        14.444,
+        16.667,
+        18.889
+      ],
+      "transitionSamples": [],
+      "transitionSamplesDropped": 0,
+      "tolerance": 2,
+      "totalIssueCount": 0,
+      "truncated": false
+    }
+  }
+]
\ No newline at end of file
diff --git a/series2/verification/concat.txt b/series2/verification/concat.txt
new file mode 100644
index 0000000..07b702f
--- /dev/null
+++ b/series2/verification/concat.txt
@@ -0,0 +1,5 @@
+file '/Users/macstudio3/Projects/wallpaper-paint-antics/series2/renders/04-fire-rated.mp4'
+file '/Users/macstudio3/Projects/wallpaper-paint-antics/series2/renders/05-wallpaper-or-paint.mp4'
+file '/Users/macstudio3/Projects/wallpaper-paint-antics/series2/renders/06-built-for-busy-spaces.mp4'
+file '/Users/macstudio3/Projects/wallpaper-paint-antics/series2/renders/07-sample-before-specifying.mp4'
+file '/Users/macstudio3/Projects/wallpaper-paint-antics/series2/renders/08-service-at-dw.mp4'
diff --git a/series2/verification/contact-sheet.png b/series2/verification/contact-sheet.png
new file mode 100644
index 0000000..e474bd1
Binary files /dev/null and b/series2/verification/contact-sheet.png differ
diff --git a/series2/verification/delivery-proof.json b/series2/verification/delivery-proof.json
new file mode 100644
index 0000000..b751223
--- /dev/null
+++ b/series2/verification/delivery-proof.json
@@ -0,0 +1,119 @@
+{
+  "at": "2026-09-05T07:44:05.680Z",
+  "memes": [
+    {
+      "id": "01-paste-gravity",
+      "width": 1254,
+      "height": 1254,
+      "download": "PASS",
+      "sha256": "d009f4d10d7e20dcc363c24708ec00aa34db16976998fbff6115f45c0e602fbe"
+    },
+    {
+      "id": "02-one-inch",
+      "width": 1254,
+      "height": 1254,
+      "download": "PASS",
+      "sha256": "96398bdd05fd8b31cd805201be76e529b35e15168b4cbf10d6a513eb2d69cb52"
+    },
+    {
+      "id": "03-pattern-match",
+      "width": 1254,
+      "height": 1254,
+      "download": "PASS",
+      "sha256": "6ecfe85ffa5dc2ee893ba6722fd0271baa06cb002e01903048d551a747e6ec07"
+    },
+    {
+      "id": "04-just-five-minutes",
+      "width": 1254,
+      "height": 1254,
+      "download": "PASS",
+      "sha256": "0b3c0755fcd51399e83d79a9b3c83c5abd939b757aeafaad392f0423ab26fef1"
+    },
+    {
+      "id": "05-paste-footprints",
+      "width": 1254,
+      "height": 1254,
+      "download": "PASS",
+      "sha256": "e054c409f13c27b3cc3aac63a220ccfb5934d6aae960033e735ccb6f9d67a28d"
+    },
+    {
+      "id": "06-roll-with-it",
+      "width": 1254,
+      "height": 1254,
+      "download": "PASS",
+      "sha256": "a4f0d436e656e46ecb78715c52ec03eb3cac1c8e89c3fddf422478848f52be6f"
+    },
+    {
+      "id": "07-ceiling-paste",
+      "width": 1254,
+      "height": 1254,
+      "download": "PASS",
+      "sha256": "afb22dfb2a8f2638610d1ba9f6c5156f974cd50673215904064703d2d6abc85c"
+    },
+    {
+      "id": "08-ladder-parking",
+      "width": 1254,
+      "height": 1254,
+      "download": "PASS",
+      "sha256": "de4771843523894c169562ed0447570ecdfac36b930088cb50a28998a37c9959"
+    },
+    {
+      "id": "09-perfect-cut",
+      "width": 1254,
+      "height": 1254,
+      "download": "PASS",
+      "sha256": "59786aac9142552d7d9f5c44339d5a55246ed95e7d472bdce98f0b6680c879b7"
+    },
+    {
+      "id": "10-last-roll",
+      "width": 1254,
+      "height": 1254,
+      "download": "PASS",
+      "sha256": "47dfb70594427439c10635daab6f0a6ab5cda5fc10beaa4f617a7c990c073eb8"
+    }
+  ],
+  "gallery": {
+    "offlineImages": "10/10 decoded",
+    "verdict": "PASS",
+    "downloadTransport": "localhost HTTP",
+    "individualDownloads": 10,
+    "zipPngs": 10,
+    "mobileWidth": 390,
+    "chapterJumps": 5,
+    "roundTripNavigation": true,
+    "errors": []
+  },
+  "audio": [
+    {
+      "slug": "04-fire-rated",
+      "peak": 0.9288135766983032,
+      "rms": 0.08058590988470538,
+      "verdict": "PASS"
+    },
+    {
+      "slug": "05-wallpaper-or-paint",
+      "peak": 0.920005738735199,
+      "rms": 0.08268946597268269,
+      "verdict": "PASS"
+    },
+    {
+      "slug": "06-built-for-busy-spaces",
+      "peak": 0.9224570393562317,
+      "rms": 0.08203228385520384,
+      "verdict": "PASS"
+    },
+    {
+      "slug": "07-sample-before-specifying",
+      "peak": 0.9282917380332947,
+      "rms": 0.07986527677466175,
+      "verdict": "PASS"
+    },
+    {
+      "slug": "08-service-at-dw",
+      "peak": 0.9206538796424866,
+      "rms": 0.0865704145086865,
+      "verdict": "PASS"
+    }
+  ],
+  "verdict": "PASS"
+}
\ No newline at end of file
diff --git a/series2/verification/e2e-proof.json b/series2/verification/e2e-proof.json
new file mode 100644
index 0000000..13bc1a8
--- /dev/null
+++ b/series2/verification/e2e-proof.json
@@ -0,0 +1,109 @@
+{
+  "intent": "Deliver five articulated 20-second Wallo presentation shorts using real DW product images, one combined presentation, and ten installer-folly memes.",
+  "ticket": "TK-11288-create-five-articulated-wallo-and-pippa",
+  "risk_tier": "R2",
+  "environment": "Local macOS; Chrome; HyperFrames 0.8.29; local Kokoro ONNX; built-in image_gen; isolated Chrome DevTools MCP",
+  "timestamp": "2026-09-05T07:45:16.460026+00:00",
+  "baseline": {
+    "commit": "5c472441de3cc0ed63428e091357dedd285ab32f",
+    "original_trilogy": "Preserved; only original gallery navigation updated.",
+    "entry": "Open local presentation or meme gallery; all assets already generated on disk."
+  },
+  "build_identity": {
+    "animation.js": "7e62bbae5df712b24e41c39e11060928daca5e7331ca94ee206db2375b385964",
+    "episodes.json": "ccebebc9b446361e43abdd3b10f95f1cdcf02d585ab3ca7b501be600502ea7b6",
+    "gallery.mjs": "5e1f96f61e347854ff3be3f4d9cad139bb0192469ba51274c03989030aba31a3",
+    "memes.mjs": "4ba092b5318227b0c2d3cfa67aea273a4028a2f71a3b0fbe22204c85d6cb65c5",
+    "watch.html": "9b1d6b0a750a65a3dd21dfc81f88227cec35d171f916dd97dec62b99f3a7eb96",
+    "memes/index.html": "8a4a2777020ef709c8a2576420057f8e81e776fdcd2c261e45e744541b45f031"
+  },
+  "commands": [
+    "node ../scripts/production.cjs series2-audio",
+    "node ../scripts/production.cjs series2-build",
+    "node ../scripts/production.cjs series2-preview",
+    "node ../scripts/production.cjs series2-check",
+    "node ../scripts/production.cjs series2-render",
+    "node ../scripts/production.cjs series2-verify",
+    "node ../scripts/production.cjs series2-mcp",
+    "node ../scripts/production.cjs series2-smoke",
+    "node gallery.mjs",
+    "node memes.mjs",
+    "node ../scripts/production.cjs series2-delivery"
+  ],
+  "checks": [
+    {
+      "name": "Motion and rendering",
+      "verdict": "PASS",
+      "evidence": [
+        "preview-evidence.json",
+        "checks.json",
+        "contact-sheet.png"
+      ],
+      "assertions": "Five animated rigs, seven unique poses each, fixed bone lengths, independent limb angles, deterministic reverse seeking; HyperFrames checks pass."
+    },
+    {
+      "name": "Encoded media and playback",
+      "verdict": "PASS",
+      "evidence": [
+        "verify-evidence.json",
+        "mcp-proof.json"
+      ],
+      "assertions": "Five 600-frame 20-second 1080x1920/30fps H.264/AAC videos, combined 3000-frame 100.021333-second presentation; full decode and actual browser playback advance."
+    },
+    {
+      "name": "Audio",
+      "verdict": "PASS",
+      "evidence": [
+        "delivery-proof.json"
+      ],
+      "assertions": "Five non-silent audio tracks, measured peaks below digital clipping; speech timed within each chapter."
+    },
+    {
+      "name": "Installer meme files and downloads",
+      "verdict": "PASS",
+      "evidence": [
+        "delivery-proof.json",
+        "../memes/manifest.json",
+        "memes-gallery.png"
+      ],
+      "assertions": "Ten distinct 1254x1254 PNGs visually inspected; corrected one floating cup; exact captions readable. All ten individual HTTP downloads pass; ZIP separately downloaded and contains all ten PNGs plus README."
+    },
+    {
+      "name": "Mobile navigation and playback",
+      "verdict": "PASS",
+      "evidence": [
+        "delivery-proof.json",
+        "memes-mobile.png",
+        "presentation-mobile.png"
+      ],
+      "assertions": "390px mobile width without overflow; gallery round trip; all five chapter buttons jump correctly; zero page errors."
+    },
+    {
+      "name": "Six-way smoke",
+      "verdict": "PASS",
+      "evidence": [
+        "3x.txt"
+      ],
+      "assertions": "4/4 attempted passes: HTTP, headless render, interactive automation, Chrome."
+    },
+    {
+      "name": "Safari/WebKit and Firefox/Gecko",
+      "verdict": "SKIP",
+      "reason": "Test engines not installed. Optional engine parity; local Chrome delivery and file integrity fully exercised."
+    }
+  ],
+  "negative_and_retry": [
+    "Missing favicon console errors corrected with inline icon; six-way smoke repeated successfully.",
+    "Rapid chapter changes followed by pause triggered a rejected play promise; cancellation now handled and full delivery journey repeated with zero errors.",
+    "Chrome file URL downloads do not emit normal HTTP download events: local file gallery image decoding verified separately; download buttons verified over local HTTP.",
+    "ZIP tested as a separate fresh browser user journey after ten rapid individual downloads; actual archive content and checksum manifest verified."
+  ],
+  "side_effects": {
+    "publishing": "None",
+    "external_messages": "None",
+    "catalog_mutations": "None",
+    "original_generated_images": "Preserved in generator output directory; copies packaged in project."
+  },
+  "retained_state": "Finished PNGs, MP4s, mixed WAVs, transcripts, sources, galleries and proof retained. Local-only review server retained for one-click downloads; isolated verification browser sessions closed. No recurring job installed.",
+  "verdict": "PASS"
+}
diff --git a/series2/verification/mcp-gallery.png b/series2/verification/mcp-gallery.png
new file mode 100644
index 0000000..e488a1c
Binary files /dev/null and b/series2/verification/mcp-gallery.png differ
diff --git a/series2/verification/mcp-proof.json b/series2/verification/mcp-proof.json
new file mode 100644
index 0000000..92af6cd
--- /dev/null
+++ b/series2/verification/mcp-proof.json
@@ -0,0 +1,116 @@
+{
+  "tool": "chrome-devtools-mcp",
+  "version": "1.8.0",
+  "mode": "isolated headless",
+  "ticket": "TK-11288",
+  "at": "2026-09-05T07:35:00.156Z",
+  "calls": [
+    {
+      "name": "new_page",
+      "args": {
+        "url": "file:///Users/macstudio3/Projects/wallpaper-paint-antics/series2/watch.html"
+      },
+      "result": {
+        "content": [
+          {
+            "type": "text",
+            "text": "## Pages\n1: about:blank\n2: Wallo presents — A guide to wallcoverings (file:///Users/macstudio3/Projects/wallpaper-paint-antics/series2/watch.html) [selected]"
+          }
+        ]
+      }
+    },
+    {
+      "name": "evaluate_script",
+      "args": {
+        "pageId": 2,
+        "function": "async () => { await Promise.all(Array.from(document.images, img => img.decode())); return {title:document.title, videos:document.querySelectorAll(\"video\").length, products:document.querySelectorAll(\".product img\").length}; }"
+      },
+      "result": {
+        "content": [
+          {
+            "type": "text",
+            "text": "Script ran on page and returned:\n```json\n{\"title\":\"Wallo presents — A guide to wallcoverings\",\"videos\":6,\"products\":5}\n```"
+          }
+        ]
+      }
+    },
+    {
+      "name": "evaluate_script",
+      "args": {
+        "pageId": 2,
+        "function": "async () => { const result=[]; for(const v of document.querySelectorAll(\"video\")){v.muted=true;await v.play(); await new Promise((resolve,reject)=>{let attempts=0;const timer=setInterval(()=>{if(v.currentTime>.4){clearInterval(timer);resolve();}else if(++attempts>150){clearInterval(timer);reject(new Error(\"Video did not advance: \"+v.dataset.slug));}},100);});v.pause();result.push({slug:v.dataset.slug,duration:v.duration,currentTime:v.currentTime,width:v.videoWidth,height:v.videoHeight,error:v.error?.message||null});}return result; }"
+      },
+      "result": {
+        "content": [
+          {
+            "type": "text",
+            "text": "Script ran on page and returned:\n```json\n[{\"slug\":\"wallo-wallcovering-presentation\",\"duration\":100.021333,\"currentTime\":0.48809,\"width\":1080,\"height\":1920,\"error\":null},{\"slug\":\"04-fire-rated\",\"duration\":20,\"currentTime\":0.449355,\"width\":1080,\"height\":1920,\"error\":null},{\"slug\":\"05-wallpaper-or-paint\",\"duration\":20,\"currentTime\":0.449165,\"width\":1080,\"height\":1920,\"error\":null},{\"slug\":\"06-built-for-busy-spaces\",\"duration\":20,\"currentTime\":0.4462,\"width\":1080,\"height\":1920,\"error\":null},{\"slug\":\"07-sample-before-specifying\",\"duration\":20,\"currentTime\":0.450838,\"width\":1080,\"height\":1920,\"error\":null},{\"slug\":\"08-service-at-dw\",\"duration\":20,\"currentTime\":0.449939,\"width\":1080,\"height\":1920,\"error\":null}]\n```"
+          }
+        ]
+      }
+    },
+    {
+      "name": "take_screenshot",
+      "args": {
+        "pageId": 2,
+        "filePath": "/Users/macstudio3/Projects/wallpaper-paint-antics/series2/verification/mcp-gallery.png",
+        "fullPage": true
+      },
+      "result": {
+        "content": [
+          {
+            "type": "text",
+            "text": "Took a screenshot of the full current page.\nSaved screenshot to /Users/macstudio3/Projects/wallpaper-paint-antics/series2/verification/mcp-gallery.png."
+          }
+        ]
+      }
+    },
+    {
+      "name": "evaluate_script",
+      "args": {
+        "pageId": 2,
+        "function": "async () => { const v=document.querySelector(\"#presentation\"); v.muted=true; document.querySelector(\"[data-jump=\\\"80\\\"]\").click(); await new Promise(r=>setTimeout(r,350)); v.pause(); return {time:v.currentTime,links:[...document.querySelectorAll(\".product a\")].map(a=>a.href)}; }"
+      },
+      "result": {
+        "content": [
+          {
+            "type": "text",
+            "text": "Script ran on page and returned:\n```json\n{\"time\":80.260884,\"links\":[\"https://www.designerwallcoverings.com/products/prw-920735-rif-003\",\"https://www.designerwallcoverings.com/products/prw-920735-rif-003\",\"https://www.designerwallcoverings.com/products/waterperry-ivory-stone-sanderson\",\"https://www.designerwallcoverings.com/products/waterperry-ivory-stone-sanderson\",\"https://www.designerwallcoverings.com/products/prw-920166-coa-004-1\",\"https://www.designerwallcoverings.com/products/prw-920166-coa-004-1\",\"https://www.designerwallcoverings.com/products/topiary-view-thyme-sanderson\",\"https://www.designerwallcoverings.com/products/topiary-view-thyme-sanderson\",\"https://www.designerwallcoverings.com/products/kaya-cream-weave-wallcovering-malibu-wallpaper\",\"https://www.designerwallcoverings.com/products/kaya-cream-weave-wallcovering-malibu-wallpaper\"]}\n```"
+          }
+        ]
+      }
+    }
+  ],
+  "availableTools": [
+    "click",
+    "close_page",
+    "drag",
+    "emulate",
+    "evaluate_script",
+    "fill",
+    "fill_form",
+    "get_console_message",
+    "get_network_request",
+    "handle_dialog",
+    "hover",
+    "lighthouse_audit",
+    "list_console_messages",
+    "list_network_requests",
+    "list_pages",
+    "navigate_page",
+    "new_page",
+    "performance_analyze_insight",
+    "performance_start_trace",
+    "performance_stop_trace",
+    "press_key",
+    "resize_page",
+    "select_page",
+    "take_heapsnapshot",
+    "take_screenshot",
+    "take_snapshot",
+    "type_text",
+    "upload_file",
+    "wait_for"
+  ],
+  "verdict": "PASS"
+}
\ No newline at end of file
diff --git a/series2/verification/mcp-stderr.txt b/series2/verification/mcp-stderr.txt
new file mode 100644
index 0000000..c9dd57a
--- /dev/null
+++ b/series2/verification/mcp-stderr.txt
@@ -0,0 +1,5 @@
+(node:93110) ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.
+(Use `node --trace-warnings ...` to show where the warning was created)
+chrome-devtools-mcp exposes content of the browser instance to the MCP clients allowing them to inspect,
+debug, and modify any data in the browser or DevTools.
+Avoid sharing sensitive or personal information that you do not want to share with MCP clients.
diff --git a/series2/verification/memes-gallery.png b/series2/verification/memes-gallery.png
new file mode 100644
index 0000000..92392f9
Binary files /dev/null and b/series2/verification/memes-gallery.png differ
diff --git a/series2/verification/memes-mobile.png b/series2/verification/memes-mobile.png
new file mode 100644
index 0000000..a48fee6
Binary files /dev/null and b/series2/verification/memes-mobile.png differ
diff --git a/series2/verification/presentation-mobile.png b/series2/verification/presentation-mobile.png
new file mode 100644
index 0000000..6f120a2
Binary files /dev/null and b/series2/verification/presentation-mobile.png differ
diff --git a/series2/verification/verify-evidence.json b/series2/verification/verify-evidence.json
new file mode 100644
index 0000000..43d3b67
--- /dev/null
+++ b/series2/verification/verify-evidence.json
@@ -0,0 +1,96 @@
+{
+  "at": "2026-09-05T07:35:10.619Z",
+  "checks": [
+    {
+      "slug": "04-fire-rated",
+      "verdict": "PASS",
+      "duration": 20,
+      "frames": 600,
+      "width": 1080,
+      "height": 1920,
+      "audio": "aac",
+      "bytes": 10487302,
+      "sha256": "84b5301796330cf4080b88b476986a90ee3e76afe0b644173c05ab9726621364",
+      "playback": {
+        "duration": 20,
+        "error": null
+      }
+    },
+    {
+      "slug": "05-wallpaper-or-paint",
+      "verdict": "PASS",
+      "duration": 20,
+      "frames": 600,
+      "width": 1080,
+      "height": 1920,
+      "audio": "aac",
+      "bytes": 11171137,
+      "sha256": "7ba251546b1f9a4e441a472e99cf61b8f321da254a072d165166e075a7de37d5",
+      "playback": {
+        "duration": 20,
+        "error": null
+      }
+    },
+    {
+      "slug": "06-built-for-busy-spaces",
+      "verdict": "PASS",
+      "duration": 20,
+      "frames": 600,
+      "width": 1080,
+      "height": 1920,
+      "audio": "aac",
+      "bytes": 10514763,
+      "sha256": "c59bf4ecb63d354d2c0b467902a28746bf8256a1cff691738229231f4d41a5d9",
+      "playback": {
+        "duration": 20,
+        "error": null
+      }
+    },
+    {
+      "slug": "07-sample-before-specifying",
+      "verdict": "PASS",
+      "duration": 20,
+      "frames": 600,
+      "width": 1080,
+      "height": 1920,
+      "audio": "aac",
+      "bytes": 17499625,
+      "sha256": "ad4a67ae4f82707260340f787ebe0e9a91da923dcea9f4ca8aaf60ac8d063edd",
+      "playback": {
+        "duration": 20,
+        "error": null
+      }
+    },
+    {
+      "slug": "08-service-at-dw",
+      "verdict": "PASS",
+      "duration": 20,
+      "frames": 600,
+      "width": 1080,
+      "height": 1920,
+      "audio": "aac",
+      "bytes": 9883895,
+      "sha256": "88d124665cc88146623afaee24dff5ed41aa4a125415b0acf4119fe64c724a63",
+      "playback": {
+        "duration": 20,
+        "error": null
+      }
+    },
+    {
+      "slug": "wallo-wallcovering-presentation",
+      "verdict": "PASS",
+      "duration": 100.021333,
+      "frames": 3000,
+      "width": 1080,
+      "height": 1920,
+      "audio": "aac",
+      "bytes": 59550193,
+      "sha256": "f23a1f9646b76b9f9890d7fd9cec42e6110fdfb61a4e534d8d88db57c5812334",
+      "playback": {
+        "duration": 100.021333,
+        "error": null
+      }
+    }
+  ],
+  "errors": []
+}
\ No newline at end of file
diff --git a/series2/verification/wallo-wallcovering-presentation-decode.txt b/series2/verification/wallo-wallcovering-presentation-decode.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/series2/verification/wallo-wallcovering-presentation-decode.txt
@@ -0,0 +1 @@
+
diff --git a/series2/verification/wallo-wallcovering-presentation-mp4-1.png b/series2/verification/wallo-wallcovering-presentation-mp4-1.png
new file mode 100644
index 0000000..4af7c46
Binary files /dev/null and b/series2/verification/wallo-wallcovering-presentation-mp4-1.png differ
diff --git a/series2/verification/wallo-wallcovering-presentation-mp4-21.png b/series2/verification/wallo-wallcovering-presentation-mp4-21.png
new file mode 100644
index 0000000..39fe320
Binary files /dev/null and b/series2/verification/wallo-wallcovering-presentation-mp4-21.png differ
diff --git a/series2/verification/wallo-wallcovering-presentation-mp4-41.png b/series2/verification/wallo-wallcovering-presentation-mp4-41.png
new file mode 100644
index 0000000..f1c8503
Binary files /dev/null and b/series2/verification/wallo-wallcovering-presentation-mp4-41.png differ
diff --git a/series2/verification/wallo-wallcovering-presentation-mp4-61.png b/series2/verification/wallo-wallcovering-presentation-mp4-61.png
new file mode 100644
index 0000000..a13eefc
Binary files /dev/null and b/series2/verification/wallo-wallcovering-presentation-mp4-61.png differ
diff --git a/series2/verification/wallo-wallcovering-presentation-mp4-81.png b/series2/verification/wallo-wallcovering-presentation-mp4-81.png
new file mode 100644
index 0000000..02ecd5e
Binary files /dev/null and b/series2/verification/wallo-wallcovering-presentation-mp4-81.png differ
diff --git a/series2/verification/wallo-wallcovering-presentation-mp4-99.png b/series2/verification/wallo-wallcovering-presentation-mp4-99.png
new file mode 100644
index 0000000..dce01d5
Binary files /dev/null and b/series2/verification/wallo-wallcovering-presentation-mp4-99.png differ
diff --git a/series2/watch.html b/series2/watch.html
new file mode 100644
index 0000000..5fb73a1
--- /dev/null
+++ b/series2/watch.html
@@ -0,0 +1,6 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><link rel="icon" href="data:,"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Wallo presents — A guide to wallcoverings</title><style>
+@font-face{font-family:Editorial;src:url('assets/georgia.ttf')}@font-face{font-family:Information;src:url('assets/verdana.ttf')}*{box-sizing:border-box}body{margin:0;background:#eeeae2;color:#243a3b;font:16px/1.6 Information,sans-serif}main{max-width:1240px;margin:auto;padding:38px 30px 80px}nav{display:flex;justify-content:space-between;gap:20px;padding-bottom:23px;border-bottom:1px solid #b9b6aa;font-size:12px;letter-spacing:.12em}a{color:#284f4b;text-underline-offset:4px}h1,h2,h3{font-family:Editorial,serif;font-weight:400;line-height:1.12}h1{font-size:clamp(44px,5vw,70px);margin:16px 0 22px;letter-spacing:-.04em}h2{font-size:40px}h3{font-size:32px;margin:14px 0}p{max-width:650px}.eyebrow{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:#4d6459}.hero{display:flex;gap:64px;align-items:center;margin:44px 0 64px}.hero .intro{flex:1}.hero video{width:360px;max-width:100%;box-shadow:0 17px 45px #233a3b1a}video{aspect-ratio:9/16;display:block;background:#dad5c8;border-radius:2px}.primary{display:inline-block;margin:18px 0 10px;padding:12px 22px;background:#284f4b;color:#f7f3e9;text-decoration:none}.chapters{display:flex;flex-wrap:wrap;gap:8px;padding:18px 0}button{font:inherit;font-size:13px;background:transparent;border:1px solid #a8b0a2;color:#284f4b;padding:8px 12px;cursor:pointer}button:hover,button:focus-visible{background:#284f4b;color:#f7f3e9}.short{display:flex;gap:48px;align-items:center;padding:42px 0;border-top:1px solid #b9b6aa}.short video{width:245px;max-width:100%}.short .copy{max-width:620px}.meta{font-size:13px;color:#59645a}.product{display:flex;gap:18px;align-items:center;padding:20px 0}.product img{width:78px;height:94px;object-fit:cover}.product span{font-size:13px}details{border-top:1px solid #b9b6aa;margin-top:45px;padding-top:22px}summary{cursor:pointer}details li{margin:12px 0;max-width:900px}footer{margin-top:36px;font-size:12px;color:#59645a}.small{font-size:13px}@media(max-width:750px){main{padding:24px 21px 50px}.hero{flex-direction:column;gap:25px;align-items:flex-start}.hero video{width:100%;max-width:430px;margin:auto}.short{flex-direction:column;gap:24px;align-items:flex-start}.short video{width:100%;max-width:360px;margin:auto}nav{flex-wrap:wrap}h2{font-size:32px}}
+</style></head><body><main><nav><span>DESIGNER WALLCOVERINGS / MATERIAL NOTES</span><a href="../watch.html">Original Wallo &amp; Pippa shorts</a><a href="memes/index.html">10 installer memes</a></nav>
+<section class="hero"><div class="intro"><p class="eyebrow">A presentation by Wallo</p><h1>Good rooms start<br>with good choices.</h1><p>Five short lessons on specifying wallcoverings, with Wallo presenting and Pippa demonstrating real products from Designer Wallcoverings.</p><p>Fire performance. Design benefits. Daily wear. Sampling. Service.</p><a class="primary" href="renders/wallo-wallcovering-presentation.mp4" download>Download the full presentation</a><p class="meta">1 minute 40 seconds · narrated · captions · 1080 × 1920</p><div class="chapters" aria-label="Jump to presentation chapter"><button data-jump="0">1. Fire</button><button data-jump="20">2. Design</button><button data-jump="40">3. Performance</button><button data-jump="60">4. Samples</button><button data-jump="80">5. DW service</button></div><p class="small" id="playback-status" role="status">Sound on for Wallo’s presentation. Each chapter is also available below as a 20-second short.</p></div><video id="presentation" data-slug="wallo-wallcovering-presentation" controls playsinline preload="metadata" poster="verification/04-fire-rated-3.5.png" src="renders/wallo-wallcovering-presentation.mp4"></video></section>
+<h2>The five shorts</h2><article class="short" id="04-fire-rated"><video data-slug="04-fire-rated" controls playsinline preload="metadata" poster="verification/04-fire-rated-6.2.png" src="renders/04-fire-rated.mp4"></video><div class="copy"><p class="eyebrow">01 / FIRE PERFORMANCE</p><h3>Why fire-rated wallcovering?</h3><p>Request the test report.</p><p>ASTM E84: surface flame spread + smoke development. Get the report. Confirm the installation requirements.</p><div class="product"><a href="https://www.designerwallcoverings.com/products/prw-920735-rif-003" target="_blank" rel="noopener"><img src="assets/products/fairborn.jpg" alt="Fairborn Type 2 | Phillipe Romano"></a><span>Featured at Designer Wallcoverings<br><a href="https://www.designerwallcoverings.com/products/prw-920735-rif-003" target="_blank" rel="noopener">Fairborn Type 2 | Phillipe Romano</a></span></div><p class="meta">20 seconds · narrated by Wallo · captions</p><a href="renders/04-fire-rated.mp4" download>Download this short</a> · <a href="transcripts/04-fire-rated.txt">Read transcript</a></div></article><article class="short" id="05-wallpaper-or-paint"><video data-slug="05-wallpaper-or-paint" controls playsinline preload="metadata" poster="verification/05-wallpaper-or-paint-6.2.png" src="renders/05-wallpaper-or-paint.mp4"></video><div class="copy"><p class="eyebrow">02 / DESIGN BENEFITS</p><h3>Why specify wallpaper over paint?</h3><p>Give the room a point of view.</p><p>Pattern. Texture. Depth. Wallpaper for character. Paint for simplicity.</p><div class="product"><a href="https://www.designerwallcoverings.com/products/waterperry-ivory-stone-sanderson" target="_blank" rel="noopener"><img src="assets/products/waterperry.jpg" alt="Waterperry Ivory/Stone | Sanderson"></a><span>Featured at Designer Wallcoverings<br><a href="https://www.designerwallcoverings.com/products/waterperry-ivory-stone-sanderson" target="_blank" rel="noopener">Waterperry Ivory/Stone | Sanderson</a></span></div><p class="meta">20 seconds · narrated by Wallo · captions</p><a href="renders/05-wallpaper-or-paint.mp4" download>Download this short</a> · <a href="transcripts/05-wallpaper-or-paint.txt">Read transcript</a></div></article><article class="short" id="06-built-for-busy-spaces"><video data-slug="06-built-for-busy-spaces" controls playsinline preload="metadata" poster="verification/06-built-for-busy-spaces-6.2.png" src="renders/06-built-for-busy-spaces.mp4"></video><div class="copy"><p class="eyebrow">03 / MATERIAL PERFORMANCE</p><h3>Performance in busy spaces</h3><p>Specify for the way a room lives.</p><p>Consider Type II commercial vinyl. Check abrasion. Cleanability. Care.</p><div class="product"><a href="https://www.designerwallcoverings.com/products/prw-920166-coa-004-1" target="_blank" rel="noopener"><img src="assets/products/wheaton.jpg" alt="Wheaton Type 2 | Phillipe Romano"></a><span>Featured at Designer Wallcoverings<br><a href="https://www.designerwallcoverings.com/products/prw-920166-coa-004-1" target="_blank" rel="noopener">Wheaton Type 2 | Phillipe Romano</a></span></div><p class="meta">20 seconds · narrated by Wallo · captions</p><a href="renders/06-built-for-busy-spaces.mp4" download>Download this short</a> · <a href="transcripts/06-built-for-busy-spaces.txt">Read transcript</a></div></article><article class="short" id="07-sample-before-specifying"><video data-slug="07-sample-before-specifying" controls playsinline preload="metadata" poster="verification/07-sample-before-specifying-6.2.png" src="renders/07-sample-before-specifying.mp4"></video><div class="copy"><p class="eyebrow">04 / SAMPLING</p><h3>The value of a real sample</h3><p>A sample makes the decision real.</p><p>Compare it with the room's other finishes. Confirm scale and repeat.</p><div class="product"><a href="https://www.designerwallcoverings.com/products/topiary-view-thyme-sanderson" target="_blank" rel="noopener"><img src="assets/products/topiary.jpg" alt="Topiary View Thyme | Sanderson"></a><span>Featured at Designer Wallcoverings<br><a href="https://www.designerwallcoverings.com/products/topiary-view-thyme-sanderson" target="_blank" rel="noopener">Topiary View Thyme | Sanderson</a></span></div><p class="meta">20 seconds · narrated by Wallo · captions</p><a href="renders/07-sample-before-specifying.mp4" download>Download this short</a> · <a href="transcripts/07-sample-before-specifying.txt">Read transcript</a></div></article><article class="short" id="08-service-at-dw"><video data-slug="08-service-at-dw" controls playsinline preload="metadata" poster="verification/08-service-at-dw-6.2.png" src="renders/08-service-at-dw.mp4"></video><div class="copy"><p class="eyebrow">05 / SERVICE AT DW</p><h3>Service at Designer Wallcoverings</h3><p>Bring your project to DW.</p><p>Products. Samples. Information. Make the next choice an informed one.</p><div class="product"><a href="https://www.designerwallcoverings.com/products/kaya-cream-weave-wallcovering-malibu-wallpaper" target="_blank" rel="noopener"><img src="assets/products/kaya.jpg" alt="Kaya - Cream Weave Wallcovering | Malibu Wallcovering"></a><span>Featured at Designer Wallcoverings<br><a href="https://www.designerwallcoverings.com/products/kaya-cream-weave-wallcovering-malibu-wallpaper" target="_blank" rel="noopener">Kaya - Cream Weave Wallcovering | Malibu Wallcovering</a></span></div><p class="meta">20 seconds · narrated by Wallo · captions</p><a href="renders/08-service-at-dw.mp4" download>Download this short</a> · <a href="transcripts/08-service-at-dw.txt">Read transcript</a></div></article>
+<details><summary>Product credits and information sources</summary><p class="small">Product images come directly from designerwallcoverings.com. Animated room installations are illustrative and not to scale. Ask for the selected product’s actual test report, specification and installation instructions. Product availability and terms may change.</p><ul><li><a href="https://store.astm.org/e0084-20.html" target="_blank" rel="noopener">ASTM E84</a> — The surface-burning test reports flame-spread and smoke-developed indices. It does not establish that a product is fireproof.</li><li><a href="https://store.astm.org/e2404-15a.html" target="_blank" rel="noopener">Wallcovering test assembly</a> — ASTM E2404 addresses preparation and mounting for textile, paper and polymeric wallcoverings assessed with E84. Confirm product report and installation requirements.</li><li><a href="https://www.wallcoverings.org/page/TypesUsage" target="_blank" rel="noopener">Type II usage</a> — Type II vinyl is used in busy commercial/public spaces; actual product performance must be checked.</li><li><a href="https://www.wallcoverings.org/page/cleanability" target="_blank" rel="noopener">Care and cleanability</a> — Commercial vinyl can provide durable, cleanable finishes; contact the provider for a specific product and follow its care instructions.</li><li><a href="https://www.wallcoverings.org/page/GroundsSubstrates" target="_blank" rel="noopener">Performance varies</a> — Construction affects durability, cleanability and other properties. The specific product label and sample book list its qualities.</li><li><a href="https://www.designerwallcoverings.com/pages/interior-design-trade-lines" target="_blank" rel="noopener">DW services</a> — DW offers samples, product information and quotes. The presentation does not promise shipping speed, free installation, discounts or universal sample availability.</li></ul></details><footer>Wallo &amp; Pippa / Material Notes · September 5, 2026<br>Original articulated character animation. Local synthetic narration. Product artwork credited to the brands shown. Local review copy.</footer></main><script>document.querySelectorAll('[data-jump]').forEach(b=>b.addEventListener('click',()=>{const v=document.getElementById('presentation');v.currentTime=Number(b.dataset.jump);v.play().catch(error=>{if(error.name!=='AbortError')document.getElementById('playback-status').textContent='Press play to start this chapter.';});v.scrollIntoView({behavior:'smooth',block:'center'});}));document.querySelectorAll('video').forEach(v=>v.addEventListener('play',()=>document.querySelectorAll('video').forEach(other=>{if(other!==v)other.pause();})));</script></body></html>
\ No newline at end of file
diff --git a/watch.html b/watch.html
index f97333e..18ff6e0 100644
--- a/watch.html
+++ b/watch.html
@@ -1 +1 @@
-<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Wallo & Pippa — Three little decorating disasters</title><style>@font-face{font-family:Archivo;src:url('assets/archivo.ttf')}*{box-sizing:border-box}body{margin:0;background:#f4eadb;color:#243b42;font:17px/1.5 system-ui,sans-serif}main{max-width:1240px;padding:55px 28px 80px;margin:auto}header{border-bottom:3px solid #243b42;margin-bottom:34px}h1{font:clamp(38px,6vw,82px)/1 Archivo,sans-serif;letter-spacing:-.04em;margin:0 0 22px}.eyebrow{color:#206d6b;font-weight:800;letter-spacing:.13em;text-transform:uppercase;font-size:13px}.intro{max-width:750px;font-size:21px}.cast{display:flex;gap:26px;align-items:center;margin:24px 0 36px}.cast img{height:230px;object-fit:contain}.cast p{max-width:480px}.films{display:flex;gap:24px;align-items:flex-start}article{flex:1;min-width:0}video{display:block;width:100%;aspect-ratio:9/16;background:#243b42;border-radius:16px}h2{font:24px/1.2 Archivo,sans-serif;margin:18px 0 7px}a{color:#206d6b;font-weight:700}.meta{font-size:14px}footer{margin-top:45px;border-top:2px solid #cbb99c;padding-top:22px;font-size:14px}@media(max-width:750px){.films{flex-direction:column}article{max-width:470px;width:100%;margin:0 auto 30px}.cast{flex-wrap:wrap}.cast img{height:180px}}</style></head><body><main><header><p class="eyebrow">Meet your new decorating double act</p><h1>Wallo & Pippa.</h1><p class="intro">One loves a perfect pattern. The other can’t resist a splash of color. Somehow, the room always comes together.</p></header><section class="cast"><img src="assets/wallo.png" alt="Wallo, the wallpaper installer"><img src="assets/pippa.png" alt="Pippa, the painter"><p><b>Wallo</b> — the meticulous paperhanger with the mustard cap.<br><b>Pippa</b> — the fearless painter with an oversized sense of fun.<br><a href="assets/wallo.png" download>Wallo character PNG</a> · <a href="assets/pippa.png" download>Pippa character PNG</a></p></section><div class="films"><article><video data-slug="01-runaway-roll" controls playsinline preload="metadata" poster="verification/01-runaway-roll-19.png" src="renders/01-runaway-roll.mp4"></video><h2>The runaway roll</h2><p>The wallpaper has plans of its own.</p><p class="meta">20 seconds · 1080 × 1920 · sound on</p><a href="renders/01-runaway-roll.mp4" download>Download MP4</a></article><article><video data-slug="02-splash-of-personality" controls playsinline preload="metadata" poster="verification/02-splash-of-personality-19.png" src="renders/02-splash-of-personality.mp4"></video><h2>A splash of personality</h2><p>A roller flourish becomes accidental art.</p><p class="meta">20 seconds · 1080 × 1920 · sound on</p><a href="renders/02-splash-of-personality.mp4" download>Download MP4</a></article><article><video data-slug="03-great-wall-off" controls playsinline preload="metadata" poster="verification/03-great-wall-off-19.png" src="renders/03-great-wall-off.mp4"></video><h2>The great wall-off</h2><p>A race ends with a much better partnership.</p><p class="meta">20 seconds · 1080 × 1920 · sound on</p><a href="renders/03-great-wall-off.mp4" download>Download MP4</a></article></div><footer>Original character animation, music and sound effects. Created September 4, 2026. Local review copy.</footer></main></body></html>
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Wallo & Pippa — Three little decorating disasters</title><style>@font-face{font-family:Archivo;src:url('assets/archivo.ttf')}*{box-sizing:border-box}body{margin:0;background:#f4eadb;color:#243b42;font:17px/1.5 system-ui,sans-serif}main{max-width:1240px;padding:55px 28px 80px;margin:auto}header{border-bottom:3px solid #243b42;margin-bottom:34px}h1{font:clamp(38px,6vw,82px)/1 Archivo,sans-serif;letter-spacing:-.04em;margin:0 0 22px}.eyebrow{color:#206d6b;font-weight:800;letter-spacing:.13em;text-transform:uppercase;font-size:13px}.intro{max-width:750px;font-size:21px}.cast{display:flex;gap:26px;align-items:center;margin:24px 0 36px}.cast img{height:230px;object-fit:contain}.cast p{max-width:480px}.films{display:flex;gap:24px;align-items:flex-start}article{flex:1;min-width:0}video{display:block;width:100%;aspect-ratio:9/16;background:#243b42;border-radius:16px}h2{font:24px/1.2 Archivo,sans-serif;margin:18px 0 7px}a{color:#206d6b;font-weight:700}.meta{font-size:14px}footer{margin-top:45px;border-top:2px solid #cbb99c;padding-top:22px;font-size:14px}@media(max-width:750px){.films{flex-direction:column}article{max-width:470px;width:100%;margin:0 auto 30px}.cast{flex-wrap:wrap}.cast img{height:180px}}</style></head><body><main><header><p class="eyebrow">Meet your new decorating double act</p><h1>Wallo & Pippa.</h1><p class="intro">One loves a perfect pattern. The other can’t resist a splash of color. Somehow, the room always comes together.</p><p><a href="series2/watch.html">NEW: Wallo’s wallcovering presentation + five shorts</a> · <a href="series2/memes/index.html">10 installer memes</a></p></header><section class="cast"><img src="assets/wallo.png" alt="Wallo, the wallpaper installer"><img src="assets/pippa.png" alt="Pippa, the painter"><p><b>Wallo</b> — the meticulous paperhanger with the mustard cap.<br><b>Pippa</b> — the fearless painter with an oversized sense of fun.<br><a href="assets/wallo.png" download>Wallo character PNG</a> · <a href="assets/pippa.png" download>Pippa character PNG</a></p></section><div class="films"><article><video data-slug="01-runaway-roll" controls playsinline preload="metadata" poster="verification/01-runaway-roll-19.png" src="renders/01-runaway-roll.mp4"></video><h2>The runaway roll</h2><p>The wallpaper has plans of its own.</p><p class="meta">20 seconds · 1080 × 1920 · sound on</p><a href="renders/01-runaway-roll.mp4" download>Download MP4</a></article><article><video data-slug="02-splash-of-personality" controls playsinline preload="metadata" poster="verification/02-splash-of-personality-19.png" src="renders/02-splash-of-personality.mp4"></video><h2>A splash of personality</h2><p>A roller flourish becomes accidental art.</p><p class="meta">20 seconds · 1080 × 1920 · sound on</p><a href="renders/02-splash-of-personality.mp4" download>Download MP4</a></article><article><video data-slug="03-great-wall-off" controls playsinline preload="metadata" poster="verification/03-great-wall-off-19.png" src="renders/03-great-wall-off.mp4"></video><h2>The great wall-off</h2><p>A race ends with a much better partnership.</p><p class="meta">20 seconds · 1080 × 1920 · sound on</p><a href="renders/03-great-wall-off.mp4" download>Download MP4</a></article></div><footer>Original character animation, music and sound effects. Created September 4, 2026. Local review copy.</footer></main></body></html>

← 5c47244 auto-data-snapshot: 2026-09-05T00:27:41 (16 data files) — se  ·  back to Wallpaper Paint Antics  ·  (newest)