← back to Wallpaper Paint Antics

series2/index.html

215 lines

<!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>