Trace a caption and its correction requests across three role lanes

HTML for this example

Select the code and use your copy shortcut. Changes in this preview are temporary.

<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Caption workflow · inspect the handoff</title><style>*{box-sizing:border-box}body{margin:0;background:#eef4f3;color:#294442;font:16px/1.55 system-ui,sans-serif}main{max-width:1040px;margin:auto;padding:24px}h1{font-size:34px;line-height:1.16}h2{font-size:24px}h3{font-size:19px}.eyebrow{letter-spacing:.06em;color:#5b7770}.card{padding:20px;background:#fff;border:1px solid #bed0cb;border-radius:5px;margin:18px 0}.toolbar{display:flex;flex-wrap:wrap;gap:9px;margin:16px 0}button,textarea{font:inherit;color:inherit}button{min-height:44px;padding:10px 12px;background:white;border:1px solid #5b837a;border-radius:4px;cursor:pointer}button[aria-pressed=true]{background:#386e62;color:white}button:focus-visible,textarea:focus-visible{outline:3px solid #b37427;outline-offset:3px}.laneheads{display:grid;grid-template-columns:repeat(3,1fr);text-align:center;font-weight:750;padding:12px 0;background:#dcebe5}.map{position:relative;height:1050px;background:linear-gradient(to right,#edf5ec 0%,#edf5ec 33.333%,#e9f2f5 33.333%,#e9f2f5 66.666%,#f7f1e4 66.666%)}.map svg{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}.node{position:absolute;width:29.333%;height:120px;left:calc(var(--col)*33.333% + 2%);top:calc(var(--row)*150px + 15px);text-align:left;background:white}.node strong,.node span{display:block}.node[data-route=true]{border:4px solid #b27a21;box-shadow:0 0 0 2px #fff}.map path[data-route=true]{stroke:#a36617;stroke-width:6}.route{border-left:6px solid #b88a36;background:#fff9e9}.handoffs li{margin:12px 0;padding:12px;background:#f2f7f4;overflow-wrap:anywhere}.comparison{display:grid;grid-template-columns:1fr 1fr;gap:16px}.comparison>div{padding:14px;background:#f5f3eb}label{display:block;font-weight:650;margin:14px 0 6px}textarea{width:100%;min-width:0;max-width:100%;min-height:90px;padding:10px;border:1px solid #8aa49a;border-radius:4px;resize:vertical}.copy p{white-space:pre-wrap;overflow-wrap:anywhere}#status{font-weight:650;color:#7a561d}footer{margin:24px 0;color:#55776d}[hidden]{display:none!important}@media(max-width:600px){main{padding:12px}h1{font-size:28px}.card{padding:14px}.laneheads{display:none}.map{height:auto;display:grid;gap:12px;padding:12px;background:#edf4ef}.map svg{display:none}.node{position:static;width:100%;height:auto;min-height:95px}.node:after{content:'↓ See named handoff and return destination below';display:block;font-size:16px;margin-top:10px;color:inherit}.comparison{grid-template-columns:1fr}}</style></head><body><main><p class="eyebrow">FICTIONAL EXHIBIT-CAPTION PROCESS · AUTHOR / EDITOR / LAYOUT</p><h1>Follow the packet across the lane</h1><p><strong>Start:</strong> author sends a caption draft with a source reference. <strong>End:</strong> matching layout proof ready for a separate publication review. This map does not publish or approve content.</p><section class="card"><h2>Responsibility lanes and named steps</h2><p>Arrows show the forward route and return connections. Select any step for its sender, packet and receiving destination. At small widths, the same labeled steps read in sequence.</p><div class="laneheads"><span>Author</span><span>Editor</span><span>Layout</span></div><div class="map"><svg viewBox="0 0 900 1050" preserveAspectRatio="none" aria-hidden="true"><defs><marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0 L10 5 L0 10Z" fill="#758f85"/></marker></defs><g fill="none" stroke="#758f85" stroke-width="3" marker-end="url(#arrow)"><path d="M150 135 L450 165"/><path d="M450 285 L150 315"/><path d="M150 315 L90 300 L90 225 L318 225"/><path d="M450 285 L450 465"/><path d="M450 585 L150 615"/><path d="M150 615 L80 600 L80 525 L318 525"/><path d="M450 585 L750 765"/><path d="M750 885 L450 915"/><path d="M450 915 L850 915 L850 825 L882 825"/></g></svg><button class="node" id="a1" style="--col:0;--row:0" type="button" aria-pressed="true"><strong>A1 · Author</strong><span>Send draft + source reference → E1</span></button><button class="node" id="e1" style="--col:1;--row:1" type="button" aria-pressed="false"><strong>E1 · Editor</strong><span>Reference missing → A2; present → E2</span></button><button class="node" id="a2" style="--col:0;--row:2" type="button" aria-pressed="false"><strong>A2 · Author</strong><span>Receive request, add reference → E1</span></button><button class="node" id="e2" style="--col:1;--row:3" type="button" aria-pressed="false"><strong>E2 · Editor</strong><span>Wording change → A3; accepted → L1</span></button><button class="node" id="a3" style="--col:0;--row:4" type="button" aria-pressed="false"><strong>A3 · Author</strong><span>Receive wording request, revise → E2</span></button><button class="node" id="l1" style="--col:2;--row:5" type="button" aria-pressed="false"><strong>L1 · Layout</strong><span>Prepare or correct proof → E3</span></button><button class="node" id="e3" style="--col:1;--row:6" type="button" aria-pressed="false"><strong>E3 · Editor</strong><span>Mismatch → L1; match → stated end</span></button></div></section><section class="card" aria-live="polite"><h2 id="step-title">A1 · Author sends the starting packet</h2><p id="step-detail">Sender: Author at A1. Packet: caption draft plus source reference. Receiver: Editor at E1, who checks the exact reference.</p></section><section class="card route"><h2>Select a route</h2><div class="toolbar"><button id="missing" type="button" aria-pressed="true">Reference missing</button><button id="present" type="button" aria-pressed="false">Reference present</button><button id="wording" type="button" aria-pressed="false">Wording change</button><button id="mismatch" type="button" aria-pressed="false">Proof mismatch</button><button id="match" type="button" aria-pressed="false">Proof matches</button></div><h3 id="route-title">A1 → E1 → A2 → E1</h3><ol id="handoffs" class="handoffs"></ol><p id="route-end">Return to the source-reference check. Wording is not yet reviewed.</p></section><button id="compare" type="button" aria-expanded="false" aria-controls="comparison">Compare return loop</button><section id="comparison" class="card" hidden><h2>The same start and end, a missing receiving step</h2><div class="comparison"><div><h3>Incomplete</h3><p>A1 → E1 → “return for correction” → E1</p><p>No author receiving or revision step is named. What packet returns?</p></div><div><h3>Corrected</h3><p>E1 → A2: request for the exact source reference.</p><p>A2 → E1: revised caption plus source reference.</p><p>A2 names the receiving work; E1 names its recheck.</p></div></div><p>Both maps start with the author packet and end with a matching proof ready for separate publication review. No speed improvement is claimed.</p></section><section class="card"><h2>Temporary handoff-mapping notes</h2><div id="fields"></div><div class="toolbar"><button id="build" type="button">Build handoff review</button><button id="reset" type="button">Reset typed work</button></div><p id="status" role="status"></p><section id="confirm" hidden><h3>Clear all eight mapping fields?</h3><div class="toolbar"><button id="keep" type="button">Keep my notes</button><button id="clear" type="button">Clear typed fields</button></div></section><section id="review" class="card copy" hidden><h3>Your supplied handoff</h3><div id="copy"></div><p>This is a map review, not execution or acceptance of work.</p></section></section><footer>Temporary notes only. Nothing is saved or sent. Check the normal and returned paths with each responsible person before adopting a real map.</footer></main><script>'use strict';const $=id=>document.getElementById(id);const steps={a1:['A1 · Author sends the starting packet','Sender: Author at A1. Packet: caption draft plus source reference. Receiver: Editor at E1, who checks the exact reference.'],e1:['E1 · Editor checks the reference','Missing: Editor at E1 sends an exact source-reference request to Author at A2. Present: E1 passes the referenced draft to wording review at E2.'],a2:['A2 · Author receives and completes the reference','Author receives the E1 request, adds the exact source reference and resends the revised caption plus reference to Editor at E1.'],e2:['E2 · Editor reviews wording','Change needed: send a wording-change request to Author at A3. Accepted wording: pass the accepted draft to Layout at L1.'],a3:['A3 · Author receives and revises wording','Author receives the E2 wording request, revises the caption and returns the draft to Editor at E2 for wording recheck.'],l1:['L1 · Layout prepares or corrects the proof','Layout receives the accepted wording from E2 or a mismatch request from E3. Prepare or correct the proof and send it to Editor at E3.'],e3:['E3 · Editor compares proof with accepted wording','A mismatch returns the proof and mismatch request to Layout at L1. A match reaches the stated end: ready for a separate publication review.']};for(const[id,row]of Object.entries(steps))$(id).addEventListener('click',()=>{for(const key of Object.keys(steps))$(key).setAttribute('aria-pressed',String(key===id));$('step-title').textContent=row[0];$('step-detail').textContent=row[1];});const routes={missing:['A1 → E1 → A2 → E1',['Author A1 → Editor E1: caption draft plus source reference.','Editor E1 → Author A2: request for the exact missing source reference.','Author A2 → Editor E1: revised caption plus exact source reference.'],'Return to the source-reference check. Wording is not yet reviewed.'],present:['A1 → E1 → E2',['Author A1 → Editor E1: draft and source reference.','Editor E1 → Editor E2: referenced draft for wording review.'],'Reference present reaches wording review, not publication.'],wording:['E2 → A3 → E2',['Editor E2 → Author A3: wording-change request.','Author A3 → Editor E2: revised caption for wording recheck.'],'Return to wording review at E2, not the source-reference check at E1.'],mismatch:['E2 → L1 → E3 → L1 → E3',['Editor E2 → Layout L1: accepted wording.','Layout L1 → Editor E3: proof to compare against the accepted wording.','Editor E3 → Layout L1: mismatched proof plus correction request.','Layout L1 → Editor E3: corrected proof for comparison.'],'Producing a proof is not the end. Recheck the corrected proof at E3.'],match:['E2 → L1 → E3 → stated end',['Editor E2 → Layout L1: accepted wording.','Layout L1 → Editor E3: proof.','Editor E3: matching proof ready for separate publication review.'],'No publication or content approval is performed here.']};const routeMap={missing:{nodes:['a1','e1','a2'],edges:[0,1,2]},present:{nodes:['a1','e1','e2'],edges:[0,3]},wording:{nodes:['e2','a3'],edges:[4,5]},mismatch:{nodes:['e2','l1','e3'],edges:[6,7,8]},match:{nodes:['e2','l1','e3'],edges:[6,7]}};const arrows=[...document.querySelectorAll('.map svg g path')];arrows.forEach((path,index)=>path.id='edge-'+index);function route(id){const row=routes[id],selectedRoute=routeMap[id];for(const key of Object.keys(steps)){const active=selectedRoute.nodes.includes(key);$(key).dataset.route=String(active);$(key).setAttribute('aria-description',active?'On the selected route: '+row[0]:'Not on the selected route: '+row[0]);}arrows.forEach((path,index)=>path.dataset.route=String(selectedRoute.edges.includes(index)));for(const key of Object.keys(routes))$(key).setAttribute('aria-pressed',String(key===id));$('route-title').textContent=row[0];$('handoffs').replaceChildren();for(const text of row[1]){const li=document.createElement('li');li.textContent=text;$('handoffs').append(li);}$('route-end').textContent=row[2];}for(const id of Object.keys(routes))$(id).addEventListener('click',()=>route(id));route('missing');$('compare').addEventListener('click',()=>{const open=$('comparison').hidden;$('comparison').hidden=!open;$('compare').setAttribute('aria-expanded',String(open));});const fields=[['start','Process start and end'],['role','Responsible sender role'],['packet','Packet or information sent'],['receiver','Receiving role and step ID'],['return','Receiving revision work and explicit return/recheck step'],['decision','Decision and branch conditions'],['source','Process source or participant-check reference'],['notes','Unresolved questions']];for(const[id,label]of fields){const l=document.createElement('label'),t=document.createElement('textarea');l.htmlFor=id;l.textContent=label;t.id=id;t.addEventListener('input',()=>{$('review').hidden=true;$('status').textContent='Mapping notes changed. Build a new review.';});$('fields').append(l,t);}$('build').addEventListener('click',()=>{const missing=['role','packet','receiver','return'].filter(id=>!$(id).value.trim());$('review').hidden=true;if(missing.length){$('status').textContent='Complete: '+missing.map(id=>({role:'sender role',packet:'packet',receiver:'receiving role/step',return:'revision and recheck step'})[id]).join(', ')+'.';return;}$('copy').replaceChildren();for(const[id,title]of fields){const h=document.createElement('h3'),p=document.createElement('p');h.textContent=title;p.textContent=$(id).value.trim()||'Not supplied.';$('copy').append(h,p);}$('review').hidden=false;$('status').textContent='Handoff review built from supplied text.';});$('reset').addEventListener('click',()=>{$('confirm').hidden=false;$('keep').focus();});$('keep').addEventListener('click',()=>{$('confirm').hidden=true;$('reset').focus();});$('clear').addEventListener('click',()=>{for(const[id]of fields)$(id).value='';$('review').hidden=true;$('confirm').hidden=true;$('status').textContent='Eight mapping fields cleared. Worked route is unchanged.';$('role').focus();});</script></body></html>