system-atlas

Build explorable isometric architecture atlases as HTML.

  • architecture
  • diagrams
  • isometric
  • documentation

Declared platforms: linux · macos

Install
npx skills add 'https://github.com/NousResearch/hermes-agent/tree/main/optional-skills/creative/system-atlas'
Download bundle ↓
main · 24fd22bScanned 2026-09-15

Contributors

GitHub-linked commit authors for this SKILL.md at the saved revision. Co-authors and history before file renames are not included.

File history ↗
View on GitHub
← Back to SKILL.md
<!doctype html><meta charset="utf-8"><title>__TITLE__</title><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:ital,wght@0,400;0,500;0,600;1,400&display=swap"><style>:root{  --paper:#E6DFBE; --paper-2:#DCD4AF; --ink:#17170F; --ink-2:#6E6B54; --rule:#B9B293; --ghost:#8E8A70;  --hi-bg:#17170F; --hi-fg:#E6DFBE; --face:#EFE9CC; --grid:#CFC8A3;  --mono:'IBM Plex Mono',ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;}@media (prefers-color-scheme: dark){  :root:not([data-theme="light"]){    --paper:#1E1D15; --paper-2:#26251B; --ink:#E6DFBE; --ink-2:#A8A388; --rule:#4A4838; --ghost:#6E6B54;    --hi-bg:#E6DFBE; --hi-fg:#1E1D15; --face:#2A2920; --grid:#33321F;  }}:root[data-theme="dark"]{  --paper:#1E1D15; --paper-2:#26251B; --ink:#E6DFBE; --ink-2:#A8A388; --rule:#4A4838; --ghost:#6E6B54;  --hi-bg:#E6DFBE; --hi-fg:#1E1D15; --face:#2A2920; --grid:#33321F;}*{box-sizing:border-box}html,body{height:100%;margin:0}body{background:var(--paper);color:var(--ink);font-family:var(--mono);font-size:13px;line-height:1.55;overflow:hidden}#app{display:grid;grid-template-rows:52px 1fr 30px;height:100vh;min-height:600px}#top{display:flex;align-items:stretch;border-bottom:1.5px solid var(--ink);overflow:hidden}.stat{padding:6px 14px 4px;border-right:1.5px solid var(--ink);min-width:0;white-space:nowrap}.stat .k{font-size:9px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-2)}.stat .v{font-size:15px;font-variant-numeric:tabular-nums}.stat.grow{flex:1;border-right:none;overflow:hidden}#controls{display:flex;gap:8px;align-items:center;padding:0 14px;border-left:1.5px solid var(--ink)}button.ctl{font:inherit;font-size:11px;letter-spacing:.06em;text-transform:uppercase;background:var(--paper);color:var(--ink);border:1.5px solid var(--ink);padding:5px 10px;cursor:pointer;box-shadow:2px 2px 0 var(--ink);white-space:nowrap}button.ctl:hover{background:var(--face)}button.ctl:active{transform:translate(1px,1px);box-shadow:1px 1px 0 var(--ink)}button.ctl:focus-visible{outline:2px solid var(--ink);outline-offset:2px}button.ctl.primary{background:var(--hi-bg);color:var(--hi-fg)}button.ctl.primary:hover{background:var(--hi-bg);opacity:.9}button.ctl:disabled{opacity:.4;cursor:default;box-shadow:none}#main{display:grid;grid-template-columns:232px 1fr 440px;min-height:0}#index{border-right:1.5px solid var(--ink);overflow-y:auto;padding:10px 8px 20px}#index h4{font-size:9px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-2);margin:12px 4px 6px;font-weight:500}#index h4:first-child{margin-top:2px}.ix{display:flex;align-items:flex-start;gap:8px;width:100%;text-align:left;font:inherit;font-size:11.5px;line-height:1.3;background:var(--paper);color:var(--ink);border:1.5px solid var(--ink);padding:7px 8px;margin:0 0 6px;cursor:pointer;box-shadow:2px 2px 0 var(--ink);text-transform:uppercase;letter-spacing:.02em}.ix .code{color:var(--ink-2);min-width:16px;font-size:10px;padding-top:1px}.ix .nm{flex:1}.ix .n{color:var(--ink-2);font-size:10.5px;font-variant-numeric:tabular-nums}.ix.ghost{border-style:dashed;box-shadow:none;color:var(--ink-2)}.ix.later{border-color:var(--rule);box-shadow:none;color:var(--ink-2);opacity:.75}.ix.later .n{font-size:9px;letter-spacing:.06em}.ix.new{outline:1.5px dashed var(--ink);outline-offset:2px}.ix.on{background:var(--hi-bg);color:var(--hi-fg)}.ix.on .code,.ix.on .n{color:var(--hi-fg);opacity:.75}.ix:hover:not(.on){background:var(--face)}.ix:focus-visible{outline:2px solid var(--ink);outline-offset:1px}#canvasWrap{position:relative;min-width:0;background:var(--paper)}#svg{width:100%;height:100%;display:block;cursor:grab;touch-action:none}#svg.dragging{cursor:grabbing}#zoom{position:absolute;right:12px;top:12px;display:flex;flex-direction:column;gap:6px}#zoom button{width:30px;height:26px;font:inherit;font-size:14px;background:var(--paper);border:1.5px solid var(--ink);color:var(--ink);cursor:pointer;box-shadow:2px 2px 0 var(--ink)}#rail{position:absolute;left:12px;top:12px;display:flex;align-items:center;gap:6px;flex-wrap:wrap;max-width:calc(100% - 70px)}#rail .ch{width:22px;height:22px;font:inherit;font-size:10px;background:var(--paper);border:1.5px solid var(--ink);color:var(--ink);cursor:pointer;padding:0;line-height:1}#rail .ch.done{background:var(--face)}#rail .ch.on{background:var(--hi-bg);color:var(--hi-fg)}#rail .ch.locked{border-style:dashed;color:var(--ink-2)}#rail .ch:focus-visible{outline:2px solid var(--ink);outline-offset:1px}#rail .title{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-2);margin-left:6px}#rail .title b{color:var(--ink);font-weight:500}#flowSel{position:absolute;left:12px;bottom:12px;display:none;gap:6px}#flowSel button{font:inherit;font-size:10px;letter-spacing:.08em;text-transform:uppercase;background:var(--paper);border:1.5px solid var(--ink);color:var(--ink);padding:3px 8px;cursor:pointer}#flowSel button.on{background:var(--hi-bg);color:var(--hi-fg)}#tip{position:absolute;pointer-events:none;background:var(--hi-bg);color:var(--hi-fg);font-size:11px;padding:4px 8px;max-width:280px;line-height:1.35;display:none;z-index:5}#panel{border-left:1.5px solid var(--ink);display:flex;flex-direction:column;min-height:0}#tabs{display:flex;border-bottom:1.5px solid var(--ink)}#tabs button{flex:1;font:inherit;font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;padding:9px 6px;background:var(--paper);color:var(--ink);border:none;border-right:1.5px solid var(--ink);cursor:pointer}#tabs button:last-child{border-right:none}#tabs button.on{background:var(--hi-bg);color:var(--hi-fg)}#tabs button:focus-visible{outline:2px solid var(--ink);outline-offset:-3px}#body{overflow-y:auto;padding:14px 18px 40px;flex:1}.eyebrow{font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-2)}h1.t{font-size:22px;font-weight:500;line-height:1.2;margin:2px 0 4px;text-wrap:balance}.lede{font-size:14.5px;line-height:1.5;margin:0 0 14px;max-width:56ch}.sub{color:var(--ink-2);font-size:11px;margin-bottom:14px}h3.sec{font-size:9.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-2);font-weight:500;margin:20px 0 6px;padding-bottom:4px;border-bottom:1.5px solid var(--ink)}#body p{margin:0 0 12px;max-width:62ch}#body ul{margin:0 0 12px;padding-left:18px;max-width:62ch}#body li{margin-bottom:6px}mark{background:var(--hi-bg);color:var(--hi-fg);padding:0 3px}.chip{display:inline-block;border:1.5px solid var(--ink);padding:1px 6px;font-size:10px;letter-spacing:.06em;text-transform:uppercase;margin:0 6px 6px 0;background:var(--paper);color:var(--ink);font-family:inherit;cursor:pointer}.chip.ghost{border-style:dashed;color:var(--ink-2)}.chip.static{cursor:default}pre{background:var(--face);border:1.5px solid var(--rule);padding:10px 12px;font-size:11px;line-height:1.45;overflow-x:auto;margin:0 0 12px;white-space:pre}.step{display:grid;grid-template-columns:24px 1fr;gap:8px;margin:0 0 8px;font-size:12px}.step .no{color:var(--ink-2);font-variant-numeric:tabular-nums}.step.cur .no{color:var(--ink);font-weight:600}.step.cur .tx{background:var(--face);outline:1.5px solid var(--ink);padding:2px 6px}.actions{display:flex;gap:8px;margin:14px 0 0;flex-wrap:wrap}.pk{border:1.5px solid var(--ink);padding:10px 12px;margin:0 0 12px}.pk .lbl{font-size:10px;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-2)}.pk .route{font-size:12px;margin:2px 0 8px}details{border-top:1.5px solid var(--rule);margin:8px 0 0;padding:6px 0 0}details summary{cursor:pointer;font-size:10.5px;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-2);list-style:none;display:flex;gap:8px;align-items:center}details summary::before{content:'+';display:inline-block;width:12px;color:var(--ink)}details[open] summary::before{content:'−'}details summary::-webkit-details-marker{display:none}details > *:not(summary){margin-top:8px}#hint{border-top:1.5px solid var(--ink);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-2);display:flex;align-items:center;padding:0 14px;gap:14px;overflow:hidden;white-space:nowrap}.q{padding-left:0;list-style:none}.q li{position:relative;padding-left:18px}.q li::before{content:'?';position:absolute;left:0;top:0;color:var(--ink-2)}.q li.res::before{content:'✓'}.q li.to::before{content:'→'}.q li.res>span,.q li.to>span{color:var(--ink-2);text-decoration:line-through;text-decoration-color:var(--rule)}.q li.to>span{text-decoration:none}.q .ans{margin-top:2px;padding-left:10px;border-left:1.5px solid var(--ink)}.pkt>.lbl{visibility:hidden}.pkt:hover>.lbl,.pkt.lbl-on>.lbl{visibility:visible}.node>.hl{visibility:hidden}.node:hover>.hl,.node.sel>.hl,.node.hov>.hl{visibility:visible}@keyframes halo{0%{opacity:.9}50%{opacity:.25}100%{opacity:.9}}.halo{animation:halo 1.6s ease-in-out infinite}@media (prefers-reduced-motion: reduce){.halo{animation:none}}@media (max-width:1100px){#main{grid-template-columns:200px 1fr 360px}}</style> <div id="app">  <div id="top">    <div id="stats" style="display:flex;min-width:0;flex:1"></div>    <div id="controls">      <button class="ctl" id="btnBack">◂ Back</button>      <button class="ctl primary" id="btnNext">Next ▸</button>      <button class="ctl" id="btnFlow">‖ Pause</button>      <button class="ctl" id="btnStep">Trace one step</button>      <button class="ctl" id="btnReset">Refit</button>    </div>  </div>  <div id="main">    <nav id="index" aria-label="Structures"></nav>    <div id="canvasWrap">      <svg id="svg" xmlns="http://www.w3.org/2000/svg">        <defs>          <pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="6" stroke="currentColor" stroke-width="1.1"/></pattern>          <pattern id="hatchLight" width="7" height="7" patternUnits="userSpaceOnUse" patternTransform="rotate(-45)"><line x1="0" y1="0" x2="0" y2="7" stroke="currentColor" stroke-width=".8" opacity=".55"/></pattern>        </defs>        <g id="world"></g>      </svg>      <div id="rail"></div>      <div id="flowSel"></div>      <div id="zoom"><button id="zin" aria-label="Zoom in">+</button><button id="zout" aria-label="Zoom out">−</button></div>      <div id="tip"></div>    </div>    <aside id="panel">      <div id="tabs">        <button data-tab="what" class="on">What it does</button>        <button data-tab="how">How it's built</button>        <button data-tab="cond">Open questions</button>      </div>      <div id="body"></div>    </aside>  </div>  <div id="hint">    <span>enter / ] next chapter</span><span>[ back</span><span>hover to read</span><span>click to pin</span><span>→ go inside</span><span>← come out</span><span>click a dot to inspect its packet</span>  </div></div> <script>/*__DATA__*/ /* ---------------- CHAPTERS (progressive disclosure) ---------------- *//* ---------------- ISO ---------------- */const TW=72, TH=36;function P(gx,gy,z=0){ return [(gx-gy)*TW/2, (gx+gy)*TH/2 - z]; }function pts(a){ return a.map(p=>p.join(',')).join(' '); }const byId = Object.fromEntries(NODES.map(n=>[n.id,n]));const center = n => [n.gx+n.w/2, n.gy+n.d/2];const NS='http://www.w3.org/2000/svg';const el=(t,a={},...kids)=>{const e=document.createElementNS(NS,t);for(const k in a)e.setAttribute(k,a[k]);kids.forEach(k=>e.appendChild(k));return e;};const question=c=>typeof c==='string'?{q:c}:c;const qOpen=n=>(n.cond||[]).map(question).filter(c=>!c.r&&!c.to).length; const qHtml=list=>`<ul class="q">${list.map(question).map(c=>c.r?`<li class="res"><span>${c.q}</span><div class="ans">✓ ${c.r}</div></li>`:c.to?`<li class="to"><span>${c.q}</span><div class="ans">→ ${c.to}</div></li>`:`<li>${c.q}</li>`).join('')}</ul>`;const esc=s=>String(s).replace(/[&<>]/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;'}[c])); /* ---------------- STATE ---------------- */const S = { ch:0, view:'world', inside:null, selected:null, hover:null, tab:'what', playing:true, tx:0, ty:0, k:1, packets:[], selPacket:null, flowSel:(typeof FLOWS!=='undefined'&&FLOWS[0]?FLOWS[0].id:'turn'), maxCh:0 };const svg=document.getElementById('svg'), world=document.getElementById('world'), tip=document.getElementById('tip'), body=document.getElementById('body');let sceneNodes=[], sceneHops=[];const revealedIds=()=>{ if(S.ch>=CH.length-1) return NODES.map(n=>n.id); const s=new Set(); for(let i=0;i<=S.ch;i++) CH[i].reveal.forEach(id=>s.add(id)); return [...s]; };const isNew=id=>S.ch<CH.length-1 && CH[S.ch].reveal.includes(id);const revealChapterOf=id=>{ for(let i=0;i<CH.length-1;i++) if(CH[i].reveal.includes(id)) return i; return CH.length-1; }; /* ---------------- SCENES ---------------- */function buildWorldScene(){  const ids=new Set(revealedIds()); sceneNodes=NODES.filter(n=>ids.has(n.id)); sceneHops=[];  const c=CH[S.ch];  if(c.flow){ c.flow.forEach((h,i)=>{ let bend=bendFor(h[0],h[1]); const rev=c.flow.findIndex(x=>x[0]===h[1]&&x[1]===h[0]); if(rev>-1&&rev<i) bend=bend==='xy'?'yx':'xy'; sceneHops.push({flow:'ch',idx:i,from:h[0],to:h[1],label:h[2],payload:h[3],bend}); }); }  else { FLOWS.forEach(f=>{ if(S.flowSel!=='both'&&f.id!==S.flowSel)return; f.hops.forEach((h,i)=>sceneHops.push({flow:f.id,idx:i,from:h[0],to:h[1],label:h[2],payload:h[3],bend:h[4]||'xy'})); }); }}function bendFor(a,b){ const t=FLOWS.flatMap(f=>f.hops||[]).find(h=>h[0]===a&&h[1]===b); return t?(t[4]||'xy'):'yx'; }function buildInsideScene(n){  const steps=n.steps||[]; sceneNodes=steps.map((s,i)=>({id:'s'+i,code:String(i+1),name:s[0],short:s[0],desc:s[1],gx:2+i*3.4,gy:2+i*0.6,w:2,d:2,h:34,inside:true}));  sceneHops=[]; for(let i=0;i<steps.length-1;i++) sceneHops.push({flow:'inside',idx:i,from:'s'+i,to:'s'+(i+1),label:steps[i][0]+' → '+steps[i+1][0],payload:{step:i+1,handsOff:steps[i+1][0]},bend:'xy'});}function hopPath(h){  const A=sceneNodes.find(n=>n.id===h.from), B=sceneNodes.find(n=>n.id===h.to); if(!A||!B) return null;  const [ax,ay]=center(A), [bx,by]=center(B); const mid = h.bend==='xy' ? [bx,ay] : [ax,by];  const g=[[ax,ay],mid,[bx,by]].filter((p,i,arr)=>i===0||p[0]!==arr[i-1][0]||p[1]!==arr[i-1][1]);  const scr=g.map(p=>P(p[0],p[1],0)); let len=0; const seg=[]; for(let i=1;i<scr.length;i++){const l=Math.hypot(scr[i][0]-scr[i-1][0],scr[i][1]-scr[i-1][1]);seg.push(l);len+=l;}  return {grid:g,scr,seg,len};}function pointAt(path,t){ let d=t*path.len; for(let i=0;i<path.seg.length;i++){ if(d<=path.seg[i]||i===path.seg.length-1){const u=path.seg[i]?Math.min(1,d/path.seg[i]):1;const a=path.scr[i],b=path.scr[i+1];return [a[0]+(b[0]-a[0])*u,a[1]+(b[1]-a[1])*u];} d-=path.seg[i]; } return path.scr[0]; } /* ---------------- RENDER ---------------- */function render(){  world.innerHTML='';  const gg=el('g',{opacity:.55}); const R=[-4,26];  for(let i=R[0];i<=R[1];i+=2){ const a=P(i,R[0]),b=P(i,R[1]);gg.appendChild(el('line',{x1:a[0],y1:a[1],x2:b[0],y2:b[1],stroke:'var(--grid)','stroke-width':.6})); const c=P(R[0],i),d=P(R[1],i);gg.appendChild(el('line',{x1:c[0],y1:c[1],x2:d[0],y2:d[1],stroke:'var(--grid)','stroke-width':.6})); }  world.appendChild(gg);  if(S.view==='world'&&S.ch>=CH.length-1){    const plate=(x0,y0,x1,y1,dash)=>{const p=el('polygon',{points:pts([P(x0,y0),P(x1,y0),P(x1,y1),P(x0,y1)]),fill:'none',stroke:'var(--ink)','stroke-width':.9,opacity:.5});if(dash)p.setAttribute('stroke-dasharray','5 4');return p;};    world.appendChild(plate(0.5,1.5,17,10.5,false)); world.appendChild(plate(2,9,17.5,16.5,false)); world.appendChild(plate(-2,-2,24,17.5,true));  }  const hg=el('g'); world.appendChild(hg);  sceneHops.forEach(h=>{ h.path=hopPath(h); if(!h.path)return; const ghost=byId[h.from]?.ghost||byId[h.to]?.ghost;    const pl=el('polyline',{points:pts(h.path.scr),fill:'none',stroke:'var(--ink)','stroke-width':h.flow!=='turn'&&h.flow!=='ch'&&h.flow!=='inside'?1:1.4,opacity:(h.flow!=='turn'&&h.flow!=='ch'&&h.flow!=='inside')||ghost?.7:1});    if((h.flow!=='turn'&&h.flow!=='ch'&&h.flow!=='inside')||ghost)pl.setAttribute('stroke-dasharray','3 3'); hg.appendChild(pl);    h.path.scr.slice(1,-1).forEach(p=>hg.appendChild(el('polygon',{points:pts([[p[0],p[1]-3.5],[p[0]+3.5,p[1]],[p[0],p[1]+3.5],[p[0]-3.5,p[1]]]),fill:'var(--ink)'})));  });  const ng=el('g'); world.appendChild(ng);  [...sceneNodes].sort((a,b)=>(a.gx+a.gy+a.w+a.d)-(b.gx+b.gy+b.w+b.d)).forEach(n=>ng.appendChild(drawNode(n)));  const lg=el('g'); world.appendChild(lg); sceneNodes.forEach(n=>lg.appendChild(drawLabel(n)));  world.appendChild(el('g',{id:'pkts'})); drawPackets(); renderRail();}function drawLabel(n){  const g=el('g',{style:'pointer-events:none'});  const b=P(n.gx+n.w, n.gy+n.d, 0); const txt=(n.short||n.name).toUpperCase(); const w=txt.length*6.6+10; const y=b[1]+9;  const dim=n.ghost&&!n.inside;  g.appendChild(el('rect',{x:b[0]-w/2,y:y,width:w,height:14,fill:S.selected===n.id?'var(--hi-bg)':'var(--paper)',stroke:dim?'var(--ghost)':'var(--ink)','stroke-width':.9,'stroke-dasharray':dim?'3 2':'none'}));  const t=el('text',{x:b[0],y:y+10.5,'text-anchor':'middle','font-size':'9','font-family':'var(--mono)','letter-spacing':'.06em',fill:S.selected===n.id?'var(--hi-fg)':(dim?'var(--ink-2)':'var(--ink)'),'font-weight':'500'}); t.textContent=txt; g.appendChild(t);  return g;}function drawNode(n){  const {gx,gy,w,d,h}=n; const sel=S.selected===n.id, hov=S.hover===n.id, fresh=!n.inside&&isNew(n.id);  const g=el('g',{class:'node'+(sel?' sel':'')+(hov?' hov':''),style:'cursor:pointer','data-id':n.id});  const sw=sel?2:1.2, dash=n.ghost?'4 3':null, face=n.ghost?'none':'var(--face)';  const face3=(p,fill,hatch,extra={})=>{const poly=el('polygon',{points:pts(p),fill:fill,stroke:'var(--ink)','stroke-width':sw,'stroke-linejoin':'round',...extra});if(dash)poly.setAttribute('stroke-dasharray',dash);g.appendChild(poly);if(hatch&&!n.ghost){g.appendChild(el('polygon',{points:pts(p),fill:'url(#'+hatch+')',style:'color:var(--ink)',stroke:'none'}));}};  const box=(z0,z1,hatchTop)=>{ face3([P(gx,gy+d,z0),P(gx+w,gy+d,z0),P(gx+w,gy+d,z1),P(gx,gy+d,z1)],face,'hatch'); face3([P(gx+w,gy,z0),P(gx+w,gy+d,z0),P(gx+w,gy+d,z1),P(gx+w,gy,z1)],face,'hatch'); face3([P(gx,gy,z1),P(gx+w,gy,z1),P(gx+w,gy+d,z1),P(gx,gy+d,z1)],face,hatchTop||null); };  const kind=n.kind||'box';  if(kind==='store'){ const L=3, gap=3, lh=(h-gap*(L-1))/L; for(let i=0;i<L;i++){ const z0=i*(lh+gap); box(z0,z0+lh,null);} }  else if(kind==='cards'){ const L=5, lh=h/L; for(let i=0;i<L;i++) box(i*lh,(i+1)*lh,null); }  else if(kind==='slab'){ box(0,h,'hatchLight'); }  else if(kind==='screen'){ box(0,h,null); const inset=.3; if(!n.ghost){ const scr=[P(gx+inset,gy+inset,h),P(gx+w-inset,gy+inset,h),P(gx+w-inset,gy+d-inset,h),P(gx+inset,gy+d-inset,h)]; g.appendChild(el('polygon',{points:pts(scr),fill:'var(--paper)',stroke:'var(--ink)','stroke-width':.9})); for(let i=1;i<=3;i++){const a=P(gx+inset+.2,gy+inset+i*.4,h),b=P(gx+w-inset-.4-(i===3?.5:0),gy+inset+i*.4,h);g.appendChild(el('line',{x1:a[0],y1:a[1],x2:b[0],y2:b[1],stroke:'var(--ink)','stroke-width':.8,opacity:.7}));} } }  else if(kind==='gate'){ box(0,h,null); if(!n.ghost){ const z0=h*.55,z1=h*.75; const band=[[P(gx,gy+d,z0),P(gx+w,gy+d,z0),P(gx+w,gy+d,z1),P(gx,gy+d,z1)],[P(gx+w,gy,z0),P(gx+w,gy+d,z0),P(gx+w,gy+d,z1),P(gx+w,gy,z1)]]; band.forEach(p=>g.appendChild(el('polygon',{points:pts(p),fill:'var(--ink)',opacity:.85,stroke:'none'}))); } }  else if(kind==='tall'){ box(0,h,null); if(!n.ghost){ // roof ridge line on top to read as "the brain"      const c=P(gx+w/2,gy+d/2,h); const a=P(gx,gy+d,h),b=P(gx+w,gy,h); g.appendChild(el('line',{x1:a[0],y1:a[1],x2:b[0],y2:b[1],stroke:'var(--ink)','stroke-width':.8,opacity:.5})); } }  else if(kind==='job'){ box(0,h,'hatchLight'); }  else { box(0,h,null); }  const top=[P(gx,gy,h),P(gx+w,gy,h),P(gx+w,gy+d,h),P(gx,gy+d,h)];  g.appendChild(el('polygon',{class:'hl',points:pts(top),fill:sel?'var(--ink)':'none',opacity:sel?.12:1,stroke:'var(--ink)','stroke-width':sel?2.5:1.8}));  if(fresh&&!sel){ g.appendChild(el('polygon',{class:'halo',points:pts([P(gx-.35,gy-.35,0),P(gx+w+.35,gy-.35,0),P(gx+w+.35,gy+d+.35,0),P(gx-.35,gy+d+.35,0)]),fill:'none',stroke:'var(--ink)','stroke-width':1.4,'stroke-dasharray':'4 3'})); }  const c=P(gx+w/2,gy+d/2,h); const chipW=16;  g.appendChild(el('rect',{x:c[0]-chipW/2,y:c[1]-8,width:chipW,height:14,fill:sel?'var(--hi-bg)':'var(--paper)',stroke:'var(--ink)','stroke-width':1}));  const t=el('text',{x:c[0],y:c[1]+3,'text-anchor':'middle','font-size':'9','font-family':'var(--mono)',fill:sel?'var(--hi-fg)':'var(--ink)','font-weight':'600'}); t.textContent=n.code; g.appendChild(t);  /* No render() on hover: it clears #world and detaches this very element, which     re-fires hover and kills the click. CSS :hover draws the highlight instead. */  g.addEventListener('mouseenter',e=>{S.hover=n.id;showTip(e,n);renderPanel();});  g.addEventListener('mousemove',e=>moveTip(e));  g.addEventListener('mouseleave',()=>{S.hover=null;hideTip();renderPanel();});  /* No click/dblclick here: hovering rebuilds the scene, so this element is gone     before the browser can pair press with release. Hit-testing lives on the svg. */  return g;} /* ---------------- PACKETS ---------------- */function initPackets(){ S.packets=[]; const flows=[...new Set(sceneHops.map(h=>h.flow))]; flows.forEach(f=>S.packets.push({flow:f,hop:0,t:0,speed:.5,pause:0})); S.selPacket=null; }function hopsOf(flow){ return sceneHops.filter(h=>h.flow===flow); }function drawPackets(){  const pg=world.querySelector('#pkts'); if(!pg)return; pg.innerHTML='';  S.packets.forEach((p,i)=>{ const hs=hopsOf(p.flow); const h=hs[p.hop]; if(!h||!h.path)return; const [x,y]=pointAt(h.path,p.t);    const isSel=S.selPacket===i; const g=el('g',{class:'pkt'+(isSel||S.ch<CH.length-1?' lbl-on':''),style:'cursor:pointer','data-i':i});    if(isSel) g.appendChild(el('circle',{cx:x,cy:y,r:11,fill:'none',stroke:'var(--ink)','stroke-width':1.2,'stroke-dasharray':'2 2'}));    g.appendChild(el('circle',{cx:x,cy:y,r:5.5,fill:'var(--ink)',stroke:'var(--paper)','stroke-width':1.6}));    const lg=el('g',{class:'lbl'}); const lbl=el('text',{x:x+10,y:y-8,'font-size':'10','font-family':'var(--mono)',fill:'var(--ink)','font-weight':'500'}); lbl.textContent=h.label;    lg.appendChild(el('rect',{x:x+7,y:y-19,width:h.label.length*6.4+6,height:14,fill:'var(--paper)',stroke:'var(--ink)','stroke-width':.8})); lg.appendChild(lbl); g.appendChild(lg);    pg.appendChild(g);  });}let last=0;function tick(ts){ const dt=Math.min(.05,(ts-last)/1000||0); last=ts; tweenTick(); if(S.playing){ S.packets.forEach(p=>advance(p,dt)); drawPackets(); if(S.selPacket!=null) renderPanel(); } requestAnimationFrame(tick); }function advance(p,dt){ const hs=hopsOf(p.flow); if(!hs.length)return; if(p.pause>0){p.pause-=dt;return;} const h=hs[p.hop]; if(!h||!h.path){p.hop=(p.hop+1)%hs.length;return;} p.t+=dt*p.speed*(320/Math.max(120,h.path.len)); if(p.t>=1){p.t=0;p.hop=(p.hop+1)%hs.length;p.pause=(p.hop===0?1.2:.35);} }function stepOnce(){ S.playing=false; syncFlowBtn(); let i=S.selPacket??0; const p=S.packets[i]; if(!p)return; const hs=hopsOf(p.flow); p.hop=(p.hop+1)%hs.length; p.t=.5; p.pause=0; S.selPacket=i; drawPackets(); renderPanel(); }function syncFlowBtn(){ document.getElementById('btnFlow').textContent = S.playing?'‖ Pause':'▸ Play'; }requestAnimationFrame(tick); /* ---------------- PANEL ---------------- */function nodeChip(id){ const n=byId[id]; return `<button class="chip${n.ghost?' ghost':''}" onclick="select('${id}')">${esc(n.code)} · ${esc(n.name)}</button>`; }function renderPanel(){  document.querySelectorAll('#tabs button').forEach(b=>b.classList.toggle('on',b.dataset.tab===S.tab));  if(S.selPacket!=null && S.packets[S.selPacket]){ const p=S.packets[S.selPacket]; const hs=hopsOf(p.flow); const h=hs[p.hop];    const A=sceneNodes.find(n=>n.id===h.from), B=sceneNodes.find(n=>n.id===h.to); const flowName=p.flow==='ch'?CH[S.ch].title:p.flow==='inside'?'inside '+byId[S.inside].name:(FLOWS.find(f=>f.id===p.flow)||{}).name;    body.innerHTML=`<div class="eyebrow">Data packet · ${esc(flowName)} · hop ${p.hop+1} of ${hs.length}</div><h1 class="t">${esc(h.label)}</h1>    <div class="pk"><div class="lbl">Route</div><div class="route">${esc(A.code)} ${esc(A.name)} &nbsp;→&nbsp; ${esc(B.code)} ${esc(B.name)}</div><div class="lbl">Payload (representative)</div><pre>${esc(JSON.stringify(h.payload,null,2))}</pre></div>    <p>Payload shapes are what the design implies, not measured traffic.</p>    <div class="actions"><button class="ctl" onclick="stepOnce()">Trace one step</button><button class="ctl" onclick="S.selPacket=null;S.playing=true;syncFlowBtn();renderPanel();drawPackets()">Release & play</button><button class="ctl" onclick="select('${esc(B.id)}')">Open ${esc(B.code)}</button></div>`; return; }  const id=S.selected??S.hover; const n=id?sceneNodes.find(x=>x.id===id):null;  if(!n){ renderChapterPanel(); return; }  if(n.inside){ const parent=byId[S.inside]; const i=+n.id.slice(1);    body.innerHTML=`<div class="eyebrow">Inside ${esc(parent.code)} · ${esc(parent.name)} · step ${i+1} of ${parent.steps.length}</div><h1 class="t">${esc(n.name)}</h1><p class="lede">${esc(n.desc)}</p>    <h3 class="sec">All steps</h3>${parent.steps.map((s,j)=>`<div class="step ${j===i?'cur':''}"><div class="no">${j+1}</div><div class="tx"><b>${esc(s[0])}</b> — ${esc(s[1])}</div></div>`).join('')}    <div class="actions"><button class="ctl" onclick="comeOut()">← Come back out</button></div>`; return; }  const status = n.ghost?'<span class="chip ghost static">Not switched on</span>':`<span class="chip static">${n.group==='loop'?'turn loop':n.group==='mem'?'memory':'supporting'}</span>`;  const pinned=S.selected===n.id;  let content='';  if(S.tab==='what') content=`<p class="lede">${n.one}</p><details${pinned?' open':''}><summary>Read more</summary><p>${n.what}</p></details>${n.steps?`<details><summary>Steps in execution</summary>${n.steps.map((s,j)=>`<div class="step"><div class="no">${j+1}</div><div class="tx"><b>${esc(s[0])}</b> — ${esc(s[1])}</div></div>`).join('')}</details>`:''}`;  else if(S.tab==='how') content=`<p class="lede">${n.one}</p><p>${n.how}</p>`;  else content=`<p class="lede">${n.one}</p>`+(n.cond&&n.cond.length?qHtml(n.cond):`<p>Nothing open here right now.</p>`);  body.innerHTML=`<div class="eyebrow">${esc(n.code)} · ${pinned?'pinned':'hovering'}${isNew(n.id)?' · new in this chapter':''}</div><h1 class="t">${esc(n.name)}</h1><div class="sub">${status}</div>${content}  ${pinned?`<div class="actions">${n.steps?`<button class="ctl" onclick="goInside('${n.id}')">→ Go inside</button>`:''}<button class="ctl" onclick="select(null)">Back to chapter</button></div>`:''}`;}function renderChapterPanel(){  const c=CH[S.ch]; const last=S.ch===CH.length-1;  if(S.tab==='how'){ body.innerHTML=HOW_HTML; return; }  if(S.tab==='cond'){ body.innerHTML=`<div class="eyebrow">${esc(TITLE)}</div><h1 class="t">Open questions</h1><div class="sub">${last?'everything unresolved, by structure':'for the structures revealed so far'}</div>`+NODES.filter(n=>revealedIds().includes(n.id)&&n.cond&&n.cond.length).map(n=>`<h3 class="sec">${n.code} · ${esc(n.name)}${n.ghost?' <span class="chip ghost static">later</span>':''}</h3>${qHtml(n.cond)}`).join(''); return; }  const chips = last ? '' : `<h3 class="sec">New in this chapter</h3><div>${c.reveal.map(nodeChip).join('')}</div>`;  const nav=`<div class="actions"><button class="ctl" onclick="prevCh()" ${S.ch===0?'disabled':''}>◂ Back</button><button class="ctl primary" onclick="nextCh()" ${last?'disabled':''}>${S.ch===CH.length-2?'Show the whole system ▸':'Next ▸'}</button></div>`;  body.innerHTML=`<div class="eyebrow">Chapter ${S.ch+1} of ${CH.length}</div><h1 class="t">${esc(c.title)}</h1><p class="lede">${c.lede}</p>${c.story}${chips}${nav}  ${last?`<h3 class="sec">Decisions locked</h3><ul>${DECISIONS_HTML}</ul>`:''}`;}document.querySelectorAll('#tabs button').forEach(b=>b.addEventListener('click',()=>{S.tab=b.dataset.tab;S.selPacket=null;renderPanel();drawPackets();})); /* ---------------- STATS ---------------- */function renderStats(){ const el=document.getElementById('stats'); const open=NODES.reduce((a,n)=>a+qOpen(n),0), routed=NODES.reduce((a,n)=>a+(n.cond||[]).filter(c=>c.to).length,0), res=NODES.reduce((a,n)=>a+(n.cond||[]).filter(c=>c.r).length,0);  const dyn={chapter:`${S.ch+1} / ${CH.length}`, shown:`${revealedIds().length} / ${NODES.length}`, questions:`${open} open · ${routed} routed · ${res} resolved`};  const all=[...(STATS||[]), {k:'Chapter',v:dyn.chapter},{k:'Structures shown',v:dyn.shown},{k:'Open questions',v:dyn.questions,grow:true}];  el.innerHTML=all.map(s=>`<div class="stat${s.grow?' grow':''}"><div class="k">${s.k}</div><div class="v">${s.v}</div></div>`).join(''); } /* ---------------- INDEX + RAIL ---------------- */function renderIndex(){  const ix=document.getElementById('index'); ix.innerHTML=''; const rev=new Set(revealedIds());  GROUPS.forEach(g=>{ const h=document.createElement('h4'); h.textContent=g.title; ix.appendChild(h);    NODES.filter(n=>n.group===g.id).forEach(n=>{ const b=document.createElement('button'); const shown=rev.has(n.id);      b.className='ix'+(n.ghost&&shown?' ghost':'')+(!shown?' later':'')+(S.selected===n.id?' on':'')+(isNew(n.id)?' new':''); b.dataset.id=n.id;      b.innerHTML=`<span class="code">${n.code}</span><span class="nm">${esc(n.name)}</span><span class="n" title="${shown?'open questions':'revealed in chapter'}">${shown?qOpen(n):'ch '+(revealChapterOf(n.id)+1)}</span>`;      b.addEventListener('click',()=>{ if(!shown){ gotoCh(revealChapterOf(n.id)); select(n.id); return; } if(S.view!=='world')comeOut(); select(n.id); focusNode(n); });      b.addEventListener('mouseenter',()=>{ if(S.view==='world'&&shown){S.hover=n.id;renderPanel();render();} });      b.addEventListener('mouseleave',()=>{ if(S.view==='world'){S.hover=null;renderPanel();render();} });      ix.appendChild(b); }); });  renderStats();}function renderRail(){ const r=document.getElementById('rail'); if(S.view!=='world'){ r.innerHTML=`<span class="title">THE SYSTEM &nbsp;›&nbsp; <b>${esc(byId[S.inside].code)} ${esc(byId[S.inside].name)}</b> &nbsp;·&nbsp; steps in execution</span>`; document.getElementById('flowSel').style.display='none'; return; }  r.innerHTML=CH.map((c,i)=>`<button class="ch ${i===S.ch?'on':i<S.ch?'done':''} ${i>S.maxCh+1?'locked':''}" title="${esc(c.title)}" onclick="gotoCh(${i})">${i+1}</button>`).join('')+`<span class="title">chapter ${S.ch+1} · <b>${esc(CH[S.ch].title)}</b></span>`;  const fs=document.getElementById('flowSel'); fs.style.display = S.ch===CH.length-1?'flex':'none';  if(!fs.dataset.built){ fs.innerHTML=[...FLOWS.map(f=>`<button data-f="${f.id}">${f.name}</button>`),'<button data-f="both">All flows</button>'].join(''); fs.dataset.built='1'; fs.querySelectorAll('button').forEach(b=>b.addEventListener('click',()=>{S.flowSel=b.dataset.f;buildWorldScene();initPackets();render();})); }  fs.querySelectorAll('button').forEach(b=>b.classList.toggle('on',b.dataset.f===S.flowSel));  document.getElementById('btnBack').disabled=S.ch===0; document.getElementById('btnNext').disabled=S.ch===CH.length-1;} /* ---------------- NAV ---------------- */function select(id){ S.selected=id; S.selPacket=null; renderIndex(); renderPanel(); render(); }function gotoCh(i){ i=Math.max(0,Math.min(CH.length-1,i)); if(S.view!=='world'){S.view='world';S.inside=null;} S.ch=i; S.maxCh=Math.max(S.maxCh,i); S.selected=null; S.hover=null; S.tab='what'; buildWorldScene(); initPackets(); S.playing=true; syncFlowBtn(); renderIndex(); render(); renderPanel(); fitView(true); }function nextCh(){ gotoCh(S.ch+1); } function prevCh(){ gotoCh(S.ch-1); }function goInside(id){ const n=byId[id]; if(!n||!n.steps)return; S.view='inside'; S.inside=id; S.selected='s0'; buildInsideScene(n); initPackets(); fitView(true); renderIndex(); render(); renderPanel(); }function comeOut(){ const was=S.inside; S.view='world'; S.inside=null; buildWorldScene(); initPackets(); S.selected=was; fitView(true); renderIndex(); render(); renderPanel(); }function orderList(){ return S.view==='world'?sceneNodes.map(n=>n.id):sceneNodes.map(n=>n.id); }document.addEventListener('keydown',e=>{ if(e.target.tagName==='INPUT')return; const L=orderList(); let i=L.indexOf(S.selected);  if(e.key==='ArrowDown'){e.preventDefault();select(L[Math.min(L.length-1,i+1)]);}  else if(e.key==='ArrowUp'){e.preventDefault();select(L[Math.max(0,i-1)]);}  else if(e.key==='ArrowRight'){e.preventDefault(); if(S.view==='world'&&S.selected)goInside(S.selected);}  else if(e.key==='ArrowLeft'){e.preventDefault(); if(S.view==='inside')comeOut(); else select(null);}  else if(e.key==='Enter'||e.key===']'){e.preventDefault(); if(S.view==='world')nextCh();}  else if(e.key==='['){e.preventDefault(); if(S.view==='world')prevCh();}  else if(e.key===' '){e.preventDefault();toggleFlow();}  else if(e.key==='Escape'){select(null);} }); /* ---------------- VIEW / PAN / ZOOM ---------------- */let tween=null;function applyView(){ world.setAttribute('transform',`translate(${S.tx},${S.ty}) scale(${S.k})`); }function tweenTick(){ if(!tween)return; const now=performance.now(); let u=Math.min(1,(now-tween.t0)/tween.dur); u=1-Math.pow(1-u,3); S.tx=tween.a.tx+(tween.b.tx-tween.a.tx)*u; S.ty=tween.a.ty+(tween.b.ty-tween.a.ty)*u; S.k=tween.a.k+(tween.b.k-tween.a.k)*u; applyView(); if(u>=1)tween=null; }function setView(tx,ty,k,anim){ tween=null; if(anim&&!matchMedia('(prefers-reduced-motion: reduce)').matches){ tween={a:{tx:S.tx,ty:S.ty,k:S.k},b:{tx,ty,k},t0:performance.now(),dur:520}; } else { S.tx=tx;S.ty=ty;S.k=k;applyView(); } }function bbox(){ let x0=1e9,y0=1e9,x1=-1e9,y1=-1e9; sceneNodes.forEach(n=>{[P(n.gx,n.gy,n.h),P(n.gx+n.w,n.gy,n.h),P(n.gx+n.w,n.gy+n.d,-24),P(n.gx,n.gy+n.d,0),P(n.gx,n.gy,0)].forEach(p=>{x0=Math.min(x0,p[0]);y0=Math.min(y0,p[1]);x1=Math.max(x1,p[0]);y1=Math.max(y1,p[1]);});}); return {x0,y0,x1,y1}; }function fitView(anim){ const r=svg.getBoundingClientRect(); if(!sceneNodes.length)return; const b=bbox(); const pad=40; const k=Math.min((r.width-pad*2)/Math.max(1,b.x1-b.x0),(r.height-pad*2)/Math.max(1,b.y1-b.y0)); if(r.width<80||r.height<80||!isFinite(k)||k<=0)return; const kk=Math.max(.2,Math.min(k,1.5)); setView(r.width/2-((b.x0+b.x1)/2)*kk, r.height/2-((b.y0+b.y1)/2)*kk+8, kk, anim); }function focusNode(n){ const r=svg.getBoundingClientRect(); const c=P(n.gx+n.w/2,n.gy+n.d/2,n.h/2); setView(r.width/2-c[0]*S.k, r.height/2-c[1]*S.k, S.k, true); }function zoomAt(f,cx,cy){ tween=null; const nk=Math.max(.35,Math.min(3,S.k*f)); S.tx=cx-(cx-S.tx)*(nk/S.k); S.ty=cy-(cy-S.ty)*(nk/S.k); S.k=nk; applyView(); }svg.addEventListener('wheel',e=>{e.preventDefault(); const r=svg.getBoundingClientRect(); zoomAt(e.deltaY<0?1.1:0.9,e.clientX-r.left,e.clientY-r.top);},{passive:false});/* Selection runs on pointerdown rather than click. render() and drawPackets() each   clear their layer, and both run on hover, so the box or dot under the cursor is   replaced between press and release — the browser then never synthesises a click on   it and click-to-pin silently did nothing. Pointer capture is deferred until a drag   actually starts for the same reason: capturing on pointerdown retargets the click   to the svg, away from the node. */function selectPacket(i){ S.selPacket=i; S.playing=false; syncFlowBtn(); renderPanel(); drawPackets(); }let drag=null, lastTap={id:null,t:0};svg.addEventListener('pointerdown',e=>{ if(e.button!==0)return; tween=null;  const hit=e.target.closest?e.target.closest('g.node,g.pkt'):null, isPkt=hit&&hit.classList.contains('pkt');  drag={x:e.clientX,y:e.clientY,tx:S.tx,ty:S.ty,moved:false,pid:e.pointerId,    pkt:isPkt?+hit.dataset.i:null, id:hit&&!isPkt?hit.dataset.id:null}; });svg.addEventListener('pointermove',e=>{ if(!drag)return; const dx=e.clientX-drag.x,dy=e.clientY-drag.y;  if(!drag.moved){ if(Math.hypot(dx,dy)<=3)return; drag.moved=true; svg.classList.add('dragging'); try{svg.setPointerCapture(drag.pid);}catch(_){} }  S.tx=drag.tx+dx; S.ty=drag.ty+dy; applyView(); });svg.addEventListener('pointercancel',()=>{ drag=null; svg.classList.remove('dragging'); });svg.addEventListener('pointerup',()=>{ const d=drag; drag=null; svg.classList.remove('dragging');  if(!d||d.moved)return;                                  // a pan, not a tap — leave selection alone  if(d.pkt!=null){ selectPacket(d.pkt); return; }  if(d.id==null){ select(null); return; }                 // empty canvas clears the pin  const n=sceneNodes.find(x=>x.id===d.id), now=performance.now();  if(lastTap.id===d.id&&now-lastTap.t<400){ lastTap={id:null,t:0}; if(n&&!n.inside&&n.steps){ goInside(d.id); return; } }  else lastTap={id:d.id,t:now};  select(d.id); });document.getElementById('zin').onclick=()=>{const r=svg.getBoundingClientRect();zoomAt(1.2,r.width/2,r.height/2);};document.getElementById('zout').onclick=()=>{const r=svg.getBoundingClientRect();zoomAt(1/1.2,r.width/2,r.height/2);};window.addEventListener('resize',()=>fitView(false)); /* ---------------- TIP ---------------- */function showTip(e,n){ tip.style.display='block'; tip.textContent=n.inside?n.desc:(n.ghost?'Later · ':'')+n.name+' — '+(n.one||'').replace(/<[^>]+>/g,''); moveTip(e); }function moveTip(e){ const r=svg.getBoundingClientRect(); let x=e.clientX-r.left+14,y=e.clientY-r.top+14; if(x+290>r.width)x-=300; if(y+60>r.height)y-=70; tip.style.left=x+'px'; tip.style.top=y+'px'; }function hideTip(){ tip.style.display='none'; } /* ---------------- CONTROLS ---------------- */function toggleFlow(){ S.playing=!S.playing; if(S.playing)S.selPacket=null; syncFlowBtn(); renderPanel(); drawPackets(); }document.getElementById('btnFlow').onclick=toggleFlow;document.getElementById('btnStep').onclick=stepOnce;document.getElementById('btnReset').onclick=()=>{ select(null); fitView(true); };document.getElementById('btnNext').onclick=nextCh; document.getElementById('btnBack').onclick=prevCh; /* ---------------- BOOT ---------------- */gotoCh(0);</script>