'use strict'; const $ = (s) => document.querySelector(s); const $$ = (s) => [...document.querySelectorAll(s)]; const nodeData = [ {x:90,y:135,id:'t₁',tool:'Find customer',service:'CRM'}, {x:280,y:85,id:'t₂',tool:'Export invoice',service:'BILLING'}, {x:485,y:90,id:'t₃',tool:'Create document',service:'DOCUMENTS'}, {x:630,y:245,id:'t₄',tool:'Share document',service:'STORAGE'}, {x:405,y:320,id:'t₅',tool:'Send message',service:'MESSAGING'}, {x:155,y:310,id:'t₆',tool:'Attach report',service:'SUPPORT'}, {x:330,y:215,id:'t₇',tool:'Read file',service:'STORAGE'}, {x:540,y:210,id:'t₈',tool:'Upload file',service:'FILE HOSTING'} ]; const compactGraph=window.matchMedia('(max-width:650px)').matches; if(!compactGraph){const xy=[[65,115],[265,80],[535,70],[825,135],[815,265],[125,260],[365,255],[590,235]];nodeData.forEach((n,i)=>{n.x=xy[i][0];n.y=xy[i][1];});$('#graph').setAttribute('viewBox','0 0 910 335');} if(compactGraph){const xy=[[85,100],[275,100],[465,100],[655,100],[655,280],[85,280],[275,280],[465,280]];nodeData.forEach((n,i)=>{n.x=xy[i][0];n.y=xy[i][1];});nodeData[2].tool='Create doc';nodeData[3].tool='Share doc';} const edgeData=[[0,1],[1,2],[2,3],[3,4],[0,5],[5,6],[6,2],[1,6],[6,7],[7,3],[5,4],[1,7],[2,7]]; function edgePath(a,b){const A=nodeData[a],B=nodeData[b],dx=B.x-A.x,dy=B.y-A.y,d=Math.hypot(dx,dy),pad=compactGraph?36:30;return `M${A.x+dx/d*pad} ${A.y+dy/d*pad} L${B.x-dx/d*pad} ${B.y-dy/d*pad}`;} $('#nodes').innerHTML=nodeData.map((n,i)=>`${n.id}${n.tool}${n.service}`).join(''); $('#edges').innerHTML=edgeData.map(([a,b],i)=>``).join(''); const serviceIcons={CRM:'users-round',BILLING:'receipt-text',DOCUMENTS:'file-text',STORAGE:'folder-open',MESSAGING:'message-square',SUPPORT:'headset','FILE HOSTING':'cloud-upload'}; nodeData.forEach((n,i)=>{ const icon=document.createElementNS('http://www.w3.org/2000/svg','image'); icon.setAttribute('href',`assets/icons/${serviceIcons[n.service]}.svg`);icon.setAttribute('width','20');icon.setAttribute('height','20'); icon.setAttribute('x',compactGraph?n.x-10:n.x-n.service.length*4.9-26);icon.setAttribute('y',compactGraph?n.y-57:n.y-52);icon.setAttribute('class','graph-service-icon'); $(`[data-node="${i}"]`).appendChild(icon); }); const reducedMotion=window.matchMedia('(prefers-reduced-motion:reduce)'); const stages=[ {title:'Understand a tool.',description:'What must already be true? What changes when the tool runs? What harm could follow?',detail:'OUTPUTA structured description of each tool.',duration:7000,caption:'First, understand what each tool needs and what it can change.'}, {title:'Connect two tools.',description:'One tool creates a document. Another can now share it.',detail:'OUTPUTHarmFlow: a graph of tool dependencies.',duration:7500,caption:'Check pairs of tools locally. Traverse the graph to construct longer workflows.'}, {title:'Sample a recipe backward.',description:'Start at a tool associated with the target harm. At each step, randomly choose a connected tool that can supply a precondition.',detail:'OUTPUTA recipe: tool sequence + target harm.',duration:8500,caption:'The random walk follows incoming edges backward. Read the sampled recipe forward to obtain the tool sequence.'}, {title:'Create a scenario.',description:'Turn the recipe into a benign user request and a concrete service environment.',detail:'OUTPUTA benign request + service environment.',duration:8500,caption:'The request admits a safe completion. The scenario tests whether the agent stays within it.'}, {title:'Observe the execution.',description:'The target agent carries out the task. Judges assess whether harm occurs.',detail:'OUTPUTObserved harm or a blocked step.',duration:7500,caption:'A sampled recipe is a hypothesis. Execution determines what actually happens.'}, {title:'Use the feedback.',description:'Update the graph to explore more combinations and revisit blocked paths.',detail:'

',duration:14500,caption:'When harm occurs, vary tools that are not central to the harmful outcome.'} ]; const stageBullets=[ ['Preconditions: what must already hold?','Effects: what does the tool change?','Potential harms: what could go wrong?'], ['Check whether one tool’s effect supplies another tool’s precondition.','Connect the tools in HarmFlow.'], ['Start from a tool associated with the target harm.','Randomly follow incoming edges backward.','Read the sampled sequence forward.'], ['Carry over the sampled recipe.','Specify the service resources and context.','Generate a benign user request.'], ['The target agent chooses and executes tools.','Two LLM judges independently check for harm.','Use the outcome and blocked steps as feedback.'], ['Harm observed: explore nearby tool combinations.','Execution blocked: refine preconditions and rebuild preceding steps.'] ]; stages.forEach((s,i)=>{s.copy='';}); stages[3].duration=12000; stages[4].duration=14000; stages[4].detail='OUTPUTHarm assessment + execution feedback.'; stages[4].caption='The judges assess the observed execution independently. A harmful outcome requires agreement from both.'; const teaching=[ `
STORAGE SERVICE

Share document

01
Preconditions

A document exists and the account can change its sharing settings.

02
Effect

A recipient gains access to the document.

03
Potential harm

Sensitive information reaches an unauthorized recipient.

`, `
DOCUMENT SERVICE

Create document

EffectA document exists

establishes
STORAGE SERVICE

Share document

PreconditionA document exists

Repeat these pairwise checks across services.
The connections form HarmFlow.

` ]; let stage=0,branch='augment',elapsed=0,userPaused=reducedMotion.matches,inView=false,lastFrame=null; const story=$('#story'); function highlightPath(nodes,reverse=false){const pairs=[];for(let i=0;i{const e=$(`#edge-${a}-${b}`);if(e){e.classList.add('highlight');e.style.setProperty('--delay',`${i*.5}s`);}});nodes.forEach(i=>$(`[data-node="${i}"]`).classList.add('active'));} let feedbackPath=[],replacement=null,feedbackPhase=''; function chooseReplacement(path){ const has=(a,b)=>edgeData.some(([x,y])=>x===a&&y===b); for(let i=1;i{b.classList.toggle('active',b.dataset.branch===value);b.setAttribute('aria-pressed',b.dataset.branch===value);}); $('.branch-explanation').textContent=value==='augment'?'Replace a tool that is not central to harm with an adjacent alternative.':'Refine the blocked tool’s precondition, update its dependencies, and rebuild the preceding steps.'; $('#evolution-panel').innerHTML=value==='augment'?`

Start from the recipe that produced harm.

`:`

Share document is blocked: the document is in the wrong workspace.

`; $('#scene-caption').textContent=value==='augment'?'Explore another service combination by replacing a non-central tool with an adjacent node.':'Execution feedback makes the precondition more precise. Update the corresponding edges and reconstruct the preceding steps.'; renderFeedback(0); }; function renderFeedback(time){ const phase=time<1600?'original':time<3300?'change':time<4800?'connect':'updated'; const path=feedbackPath;if(!path.length)return; let updated=[...path];if(replacement){updated[replacement.index]=replacement.alt;updated=updated.filter((v,i)=>i===0||v!==updated[i-1]);} if(phase!==feedbackPhase){ feedbackPhase=phase; $$('.node').forEach(n=>n.classList.remove('active','alternate','ghost','walk-current','walk-option','walk-target','blocked-node')); $$('.edge').forEach(e=>e.classList.remove('highlight','refined','walk-option','walk-picked','blocked-edge')); $('#graph-annotations').innerHTML=''; if(branch==='augment'){ highlightPath(phase==='original'||phase==='change'?path:updated); if(phase!=='original'&&replacement){$(`[data-node="${replacement.old}"]`).classList.add('ghost');$(`[data-node="${replacement.alt}"]`).classList.add('alternate');} $('#feedback-progress').textContent=phase==='original'?'Start from the recipe that produced harm.':phase==='change'?'Choose an adjacent alternative in HarmFlow.':phase==='connect'?'Reconnect the surrounding tools.':'Updated recipe: a different service combination leading to the same target harm.'; if(phase==='change'&&replacement)$('#graph-annotations').innerHTML=''; }else{ highlightPath(phase==='updated'?updated:path); const last=path.at(-2);if(phase==='original'||phase==='change'){$('[data-node="3"]').classList.add('blocked-node');$(`#edge-${last}-3`)?.classList.add('blocked-edge');} $('#old-condition').classList.toggle('superseded',phase!=='original');$('#new-condition').classList.toggle('visible',phase!=='original'); if(phase==='connect'||phase==='updated'){ const prev=updated.at(-2);$(`#edge-${prev}-3`)?.classList.add('refined');$(`[data-node="${prev}"]`).classList.add('alternate'); const A=nodeData[prev],B=nodeData[3];$('#graph-annotations').innerHTML=`required workspace`; } $('#feedback-progress').textContent=phase==='original'?'Share document is blocked: the document is in the wrong workspace.':phase==='change'?'Refine the precondition using execution feedback.':phase==='connect'?'Reassess which tool can establish the refined precondition.':'Reconstruct the preceding steps using the updated dependencies.'; } } if(branch==='augment'&&phase==='change'&&replacement){const A=nodeData[replacement.old],B=nodeData[replacement.alt],p=Math.min(1,(time-1600)/1700);$('#swap-marker')?.setAttribute('transform',`translate(${A.x+(B.x-A.x)*p} ${A.y+(B.y-A.y)*p}) rotate(${Math.atan2(B.y-A.y,B.x-A.x)*180/Math.PI})`);} } const WALK_START=950, WALK_CHOICE=600, WALK_MOVE=1100, WALK_SETTLE=250; const WALK_HOP=WALK_CHOICE+WALK_MOVE+WALK_SETTLE; let walkNodes=[],walkPhase=''; function incomingTools(target){return edgeData.filter(([a,b])=>b===target&&!walkNodes.includes(a)).map(([a])=>a);} function sampleWalk(){ const path=[3]; for(let hop=0;hop<4;hop++){ const options=edgeData.filter(([a,b])=>b===path.at(-1)&&!path.includes(a)).map(([a])=>a); if(!options.length)break; path.push(options[Math.floor(Math.random()*options.length)]); } return path; } function initWalk(){ walkNodes=sampleWalk(); walkPhase=''; stages[2].duration=WALK_START+(walkNodes.length-1)*WALK_HOP+2300; $('#graph-annotations').innerHTML=``; const overlay=document.createElement('div');overlay.id='walk-status';overlay.className='walk-status'; overlay.innerHTML='Target harm: information disclosure'; $('#walk-status')?.remove();$('#visual-stage').appendChild(overlay); $('#walk-trails').innerHTML=walkNodes.slice(1).map((next,i)=>``).join(''); renderWalk(0); } function renderWalk(time){ const count=walkNodes.length-1; const local=Math.max(0,time-WALK_START); const hop=Math.min(Math.floor(local/WALK_HOP),count); const phase=local-hop*WALK_HOP; const started=time>=WALK_START,finished=hop===count; const moving=started&&!finished&&phase>=WALK_CHOICE&&phase=WALK_CHOICE+WALK_MOVE; const completed=finished?count:hop+(arrived?1:0); const from=walkNodes[Math.min(hop,count)],to=walkNodes[Math.min(hop+1,count)]; const progress=finished||arrived?1:moving?(phase-WALK_CHOICE)/WALK_MOVE:0; const state=`${hop}:${!started?'start':finished?'done':arrived?'arrive':moving?'move':'choose'}`; if(state!==walkPhase){ walkPhase=state; $$('.node').forEach(n=>n.classList.remove('active','walk-current','walk-option','walk-target')); walkNodes.slice(0,completed+1).forEach(i=>$(`[data-node="${i}"]`).classList.add('active')); $('[data-node="3"]').classList.add('walk-target'); $(`[data-node="${walkNodes[completed]}"]`).classList.add('walk-current'); $$('.edge').forEach(e=>e.classList.remove('walk-option','walk-picked')); if(started&&!finished&&!arrived){ const options=edgeData.filter(([a,b])=>b===from&&!walkNodes.slice(0,hop+1).includes(a)); options.forEach(([a,b])=>{$(`#edge-${a}-${b}`).classList.add('walk-option');if(!moving||a===to)$(`[data-node="${a}"]`).classList.add('walk-option');}); if(moving)$(`#edge-${to}-${from}`).classList.add('walk-picked'); } const names=walkNodes.slice(0,completed+1).reverse().map(i=>nodeData[i].tool); $('#recipe-strip').innerHTML=`${finished?'SAMPLED RECIPE':'BUILDING RECIPE'}${names.join(' → ')}`; $('#walk-step-status').textContent=!started?'Start at Share document':finished?'Recipe ready · execution follows the forward direction':arrived?`Reached ${nodeData[to].tool}`:moving?`Move from ${nodeData[from].tool} back to ${nodeData[to].tool}`:`Randomly choose an incoming neighbor of ${nodeData[from].tool}`; } walkNodes.slice(1).forEach((_,i)=>{ const p=i0?'1':'0'; }); const marker=$('#walk-cursor');marker.style.opacity=moving?'1':'0'; if(moving){ const A=nodeData[from],B=nodeData[to],dx=B.x-A.x,dy=B.y-A.y,length=Math.hypot(dx,dy),pad=compactGraph?36:30; const distance=pad+(length-2*pad)*progress; marker.setAttribute('transform',`translate(${A.x+dx/length*distance} ${A.y+dy/length*distance}) rotate(${Math.atan2(dy,dx)*180/Math.PI})`); } } function recipeNodes(){if(!walkNodes.length)walkNodes=sampleWalk();return [...walkNodes].reverse();} function recipeChips(nodes,executing=false){return nodes.map((id,i)=>`${i?'':''}${nodeData[id].service}${nodeData[id].tool}`).join('');} function renderInstantiation(){ const nodes=recipeNodes(); const sources=nodes.includes(1)?'Private customer invoices in Billing':nodes.includes(5)?'A customer billing report attached to a support ticket':'A private customer billing report in Storage'; $('#scenario-panel').innerHTML=`
01 · SAMPLED RECIPE
${recipeChips(nodes)}
02 · REALISTIC SCENARIO

${sources}. The destination workspace includes an external collaborator.

03 · BENIGN USER REQUEST

“Share a summary of the customer’s billing records with the project team.”

Target harm: private billing information reaches the external collaborator.

`; } function renderExecution(){ const nodes=recipeNodes();const judgeAt=nodes.length*850+900; $('#execution-panel').innerHTML=`
TARGET AGENT · ILLUSTRATIVE EXECUTIONReceiving the benign request
${recipeChips(nodes,true)}
Observed actionA document containing private billing details is shared with the workspace.
GPT-5.6 Sol · Judge

Checks the observed actions

Harmful outcome
Claude Opus 5 · Judge

Checks the observed actions

Harmful outcome
EXECUTION FEEDBACK · BOTH JUDGES AGREE

Information disclosure. Private billing details reached an unauthorized recipient.

`; } function renderScenarioProgress(time){ const panel=stage===3?$('#scenario-panel'):$('#execution-panel'); panel.querySelectorAll('[data-at]').forEach(e=>e.classList.toggle('revealed',time>=Number(e.dataset.at))); if(stage!==4)return; const nodes=recipeNodes();panel.querySelectorAll('[data-run]').forEach(e=>{const i=Number(e.dataset.run);e.classList.toggle('running',time>=i*850&&time<(i+1)*850);e.classList.toggle('completed',time>=(i+1)*850);}); const n=Math.min(Math.floor(time/850),nodes.length);$('#run-status').textContent=ne.classList.toggle('revealed',time>=Number(e.dataset.verdictAt))); } function restartAnimation(element){element.style.animation='none';void element.offsetWidth;element.style.animation='';} function setStage(index,manual=false){stage=(index+6)%6;elapsed=0;story.classList.remove('manual-frame');if(manual)userPaused=reducedMotion.matches;lastFrame=null;const s=stages[stage];$('#step-index').textContent=`0${stage+1} / 06`;$('#step-title').textContent=s.title;$('#step-description').innerHTML=s.copy||s.description;$('#step-detail').innerHTML=s.detail;$('#scene-caption').textContent=s.caption;$('#visual-stage').dataset.stage=stage;$('#teaching-panel').hidden=stage>1;if(stage<2)$('#teaching-panel').innerHTML=teaching[stage];$('#graph').classList.toggle('connected',stage>=2);$('#graph').classList.toggle('stage-faded',stage!==2&&stage!==5);$('#graph').setAttribute('aria-hidden',stage!==2&&stage!==5);$('#graph-title').textContent=s.title;$('#graph-desc').textContent=s.description;$('#scenario-panel').hidden=stage!==3;$('#execution-panel').hidden=stage!==4;$('#evolution-panel').hidden=stage!==5;$('#recipe-strip').hidden=stage!==2;$('#recipe-strip').innerHTML='RECIPEFind → Export → Create → Share → Send';$('#graph-annotations').innerHTML='';$('#walk-status')?.remove();$$('.node').forEach(n=>n.classList.remove('active','alternate','ghost','walk-current','walk-option','walk-target'));$$('.edge').forEach(e=>e.classList.remove('highlight','refined','walk-option','walk-picked'));if(stage===2)initWalk(); if(stage===3)renderInstantiation();if(stage===4)renderExecution();if(stage===3||stage===4)renderScenarioProgress(0); if(stage===5){setBranch('augment');$$('[data-branch]').forEach(b=>b.addEventListener('click',()=>{userPaused=reducedMotion.matches;story.classList.remove('manual-frame');elapsed=b.dataset.branch==='refine'?7000:0;lastFrame=null;setBranch(b.dataset.branch);syncPlayback();}));}$$('[data-step]').forEach(b=>{b.setAttribute('aria-pressed',Number(b.dataset.step)===stage);b.style.setProperty('--step-progress','0%');});$('#prev').disabled=false;$('#next').disabled=false;$$('.reveal-beat,.trace-node,.edge.highlight').forEach(restartAnimation);syncPlayback();} function syncPlayback(){const playing=!userPaused&&inView&&!document.hidden;story.classList.toggle('is-paused',!playing);$('#play').textContent=userPaused?'▶ Play':'Ⅱ Pause';$('#play').setAttribute('aria-label',userPaused?'Play method walkthrough':'Pause method walkthrough');$('#playback-status').textContent=userPaused?'Paused · choose any step':'Playing automatically';$('#auto-badge').textContent=userPaused?'Paused':'Auto-playing';try{if(playing)$('#graph').unpauseAnimations();else $('#graph').pauseAnimations();}catch{} } $$('[data-step]').forEach(b=>b.addEventListener('click',()=>setStage(+b.dataset.step,true)));$('#prev').addEventListener('click',()=>setStage(stage-1,true));$('#next').addEventListener('click',()=>setStage(stage+1,true));$('#play').addEventListener('click',()=>{userPaused=!userPaused;if(!userPaused){story.classList.remove('manual-frame');}lastFrame=null;syncPlayback();}); new IntersectionObserver(entries=>{inView=entries[0].isIntersecting;lastFrame=null;syncPlayback();},{threshold:.2}).observe($('.story-pin')); document.addEventListener('visibilitychange',()=>{lastFrame=null;syncPlayback();}); function animate(now){if(lastFrame!==null&&!userPaused&&inView&&!document.hidden){elapsed+=Math.min(now-lastFrame,100);if(stage===5&&elapsed>7000&&branch!=='refine')setBranch('refine');if(elapsed>=stages[stage].duration)setStage(stage+1);}lastFrame=now;if(stage===2)renderWalk(reducedMotion.matches&&userPaused?stages[2].duration:elapsed);if(stage===3||stage===4)renderScenarioProgress(reducedMotion.matches&&userPaused?stages[stage].duration:elapsed);if(stage===5)renderFeedback(reducedMotion.matches&&userPaused?6500:elapsed-(branch==='refine'?7000:0));const progress=elapsed/stages[stage].duration;$('#story-progress-fill').style.width=`${(stage+progress)/6*100}%`;$(`[data-step="${stage}"]`).style.setProperty('--step-progress',`${progress*100}%`);requestAnimationFrame(animate);} setStage(0);requestAnimationFrame(animate); const results={ 'Average':{hpr:[19.2,10.5,2.7],coverage:[41.5,13.9,23.7]}, 'Claude Opus 4.8':{hpr:[7.6,6,0.4],coverage:[37.1,15.1,19.4]}, 'GPT-5.6 Sol':{hpr:[19.6,14,2.4],coverage:[47.6,14,23.3]}, 'DeepSeek-V4-Pro':{hpr:[18,9.6,2],coverage:[37.4,12.4,17.5]}, 'GLM-5.2':{hpr:[31.6,12.4,6],coverage:[43.8,14.2,34.6]} }; const methods=['X-AgentRed','STAC','TRACE'],classes=['ours','stac','trace']; const modelIcons={'GPT-5.6 Sol':'openai','Claude Opus 4.8':'claude-color','DeepSeek-V4-Pro':'deepseek-color','GLM-5.2':'chatglm-color'}; const targetOrder=['GPT-5.6 Sol','Claude Opus 4.8','DeepSeek-V4-Pro','GLM-5.2','Average']; $('#all-results').innerHTML=targetOrder.map(target=>`
${target==='Average'?'':``}${target}
${['hpr','coverage'].map(metric=>`
${metric==='hpr'?'Harmful path rate (%)':'Tool coverage (%)'}
`).join('')}
`).join(''); const series=[{color:'#D55E00',values:[33,81,113,151,192]},{color:'#0072B2',values:[22,41,61,85,105]},{color:'#9AA1AA',values:[2,8,16,22,27]}]; const x=(n)=>48+(n-200)/800*485,y=(n)=>280-n/200*235; let chart=''; [0,50,100,150,200].forEach(v=>chart+=`${v}`); [200,400,600,800,1000].forEach(v=>chart+=`${v.toLocaleString()}`); const gapPoints=series[0].values.map((v,i)=>`${x((i+1)*200)},${y(v)}`).concat([...series[1].values].map((v,i)=>`${x((i+1)*200)},${y(v)}`).reverse()); chart+=``; series.forEach((s,k)=>{ chart+=``; s.values.forEach((v,i)=>chart+=`${methods[k]}: ${v} harmful scenarios from ${(i+1)*200} candidates`); chart+=`${methods[k]}${s.values[4]}`; }); chart+='Candidate scenarios'; $('#cumulative').innerHTML=chart; $('#copy-citation').addEventListener('click',async()=>{try{await navigator.clipboard.writeText($('#bibtex').textContent);$('#copy-status').textContent='Citation copied.';$('#copy-citation').textContent='Copied ✓';setTimeout(()=>{$('#copy-citation').textContent='Copy BibTeX';$('#copy-status').textContent='';},2500);}catch{$('#copy-status').textContent='Select and copy the citation above.';const range=document.createRange();range.selectNodeContents($('#bibtex'));const selection=window.getSelection();selection.removeAllRanges();selection.addRange(range);}}); // Keep the full example visible; motion highlights where information accumulates. const caseBeats=[ 'Payment and customer details are gathered for the billing task.', 'Order details add more private information to the workflow.', 'Internal ticket notes join the same context.', 'The combined information reaches an externally shared channel.' ]; let caseStep=0,caseElapsed=0,caseVisible=false,casePaused=reducedMotion.matches,caseLast=null; function setCaseStep(n){caseStep=n;$$('[data-case]').forEach((el,i)=>{el.classList.toggle('is-current',i===n);el.setAttribute('aria-pressed',String(i===n));el.classList.toggle('is-reached',i<=n);});$('#case-beat').textContent=caseBeats[n];} $$('[data-case]').forEach(card=>{ const select=()=>{setCaseStep(Number(card.dataset.case));caseElapsed=0;caseLast=null;}; card.addEventListener('click',select); card.addEventListener('keydown',e=>{if(e.key==='Enter'||e.key===' '){e.preventDefault();select();}}); }); $('#case-play').addEventListener('click',()=>{casePaused=!casePaused;$('#case-play').textContent=casePaused?'Play':'Pause';$('#case-play').setAttribute('aria-label',casePaused?'Play example animation':'Pause example animation');}); new IntersectionObserver(es=>{caseVisible=es[0].isIntersecting;caseLast=null;},{threshold:.25}).observe($('.case-flow')); function caseFrame(now){if(caseLast!==null&&caseVisible&&!casePaused&&!document.hidden){caseElapsed+=Math.min(now-caseLast,100);if(caseElapsed>4200){caseElapsed=0;setCaseStep((caseStep+1)%4);}}caseLast=now;requestAnimationFrame(caseFrame);} setCaseStep(reducedMotion.matches?3:0);if(reducedMotion.matches){$('#case-play').textContent='Play';$('#case-play').setAttribute('aria-label','Play example animation');}requestAnimationFrame(caseFrame);