app
Adom Footprint
Public Made by Adomby adom
KiCad footprint creator with layer HUD viewer and delivery to KiCad/Fusion 360
← Commit history
Publish 1.0.19
14 files changed
+565−169
Cargo.lock+1−1@@ -4,7 +4,7 @@ version = 4 [[package]] name = "adom-footprint"-version = "1.0.18"+version = "1.0.19" dependencies = [ "anyhow", "clap",
Cargo.toml+1−1@@ -1,6 +1,6 @@ [package] name = "adom-footprint"-version = "1.0.18"+version = "1.0.19" edition = "2021" description = "Create, preview, lint, and deliver KiCad footprints (.kicad_mod) with interactive FpView viewer"
bin/adom-footprint⋯ 1 unchanged line ⋯
package.json+32−1@@ -1,6 +1,6 @@ { "slug": "adom-footprint",- "version": "1.0.18",+ "version": "1.0.19", "type": "app", "description": "KiCad footprint creator with layer HUD viewer and delivery to KiCad/Fusion 360", "dependencies": {@@ -27,5 +27,36 @@ "library", "smd", "pads"+ ],+ "discovery_triggers": [+ "create a kicad footprint",+ "make a .kicad_mod",+ "footprint viewer",+ "view this footprint",+ "pcb land pattern",+ "add thermal vias to the exposed pad",+ "generate heatsink vias",+ "place the refdes and value",+ "send footprint to fusion",+ "kicad footprint for this chip"+ ],+ "discovery_pitch": "Create, view and edit KiCad PCB footprints in a live layer viewer — auto-placed RefDes/Value, auto-generated heatsink vias, heatsink-aware solder paste, and one-click delivery to KiCad/Fusion/Altium/OrCAD.",+ "sample_prompts": [+ {+ "label": "Make a footprint",+ "prompt": "Create a KiCad footprint for a QFN-32 with a thermal pad"+ },+ {+ "label": "View a part",+ "prompt": "Show me the footprint for the ESP32-S3-WROOM-1 and let me toggle its layers"+ },+ {+ "label": "Heatsink vias",+ "prompt": "Add heatsink vias to this exposed pad so I do not have to draw them"+ },+ {+ "label": "Place labels",+ "prompt": "Auto-place the RefDes and Value clear of the pads and save it"+ } ] }
page.json+33−2@@ -1,6 +1,6 @@ { "slug": "adom-footprint",- "version": "1.0.18",+ "version": "1.0.19", "type": "app", "description": "KiCad footprint creator with layer HUD viewer and delivery to KiCad/Fusion 360", "dependencies": {@@ -35,5 +35,36 @@ "title": "Adom Footprint", "org": "adom", "updated_at": "2026-06-25T01:59:28.504Z",- "source_visibility": "public"+ "source_visibility": "public",+ "discovery_triggers": [+ "create a kicad footprint",+ "make a .kicad_mod",+ "footprint viewer",+ "view this footprint",+ "pcb land pattern",+ "add thermal vias to the exposed pad",+ "generate heatsink vias",+ "place the refdes and value",+ "send footprint to fusion",+ "kicad footprint for this chip"+ ],+ "discovery_pitch": "Create, view and edit KiCad PCB footprints in a live layer viewer — auto-placed RefDes/Value, auto-generated heatsink vias, heatsink-aware solder paste, and one-click delivery to KiCad/Fusion/Altium/OrCAD.",+ "sample_prompts": [+ {+ "label": "Make a footprint",+ "prompt": "Create a KiCad footprint for a QFN-32 with a thermal pad"+ },+ {+ "label": "View a part",+ "prompt": "Show me the footprint for the ESP32-S3-WROOM-1 and let me toggle its layers"+ },+ {+ "label": "Heatsink vias",+ "prompt": "Add heatsink vias to this exposed pad so I do not have to draw them"+ },+ {+ "label": "Place labels",+ "prompt": "Auto-place the RefDes and Value clear of the pads and save it"+ }+ ] }
src/app.html+51−51@@ -136,10 +136,14 @@ #chipinfo a.ci-ds { display:inline-block; margin-top:9px; color:var(--accent2); text-decoration:none; font-size:11.5px; border:1px solid rgba(0,184,177,.4); border-radius:7px; padding:4px 9px; } #chipinfo a.ci-ds:hover { background:rgba(0,184,177,.12); } .svgbox.grab { cursor:grabbing; }- .svgbox svg { position:absolute; left:0; top:0; width:100%; height:100%; transform-origin:0 0; display:block; }+ /* DIRECT child only — must not match the zoom-toolbar button icon <svg>s,+ which live inside .zoomtools inside .svgbox (else they'd all absolutely+ stack at 0,0 and only one would be visible). */+ .svgbox > svg { position:absolute; left:0; top:0; width:100%; height:100%; transform-origin:0 0; display:block; } .zoomtools { position:absolute; left:10px; top:10px; display:flex; gap:6px; z-index:5; } .zoomtools button { width:30px; height:30px; border-radius:8px; border:1px solid var(--border); background:var(--surface2,#1c2230); color:#cfd8e2; font-size:16px; cursor:pointer; display:grid; place-items:center; } .zoomtools button:hover { border-color:var(--accent2); color:var(--accent2); }+ .zoomtools button svg { width:15px; height:15px; display:block; } .zoomtools button:focus, .zoomtools button:focus-visible { outline:none; } .panhint { position:absolute; right:10px; bottom:9px; color:var(--text3); font-size:11px; z-index:5; pointer-events:none; } #svgbox [data-pad] { cursor:pointer; }@@ -194,6 +198,8 @@ #textBody .savebtn.saved { background:transparent; border:1px solid var(--border); color:var(--text3); cursor:default; } #textBody .savebtn.unsaved { background:linear-gradient(180deg,#1c7a4a,#155f39); border:1px solid #2a9c63; color:#dffff0; cursor:pointer; box-shadow:0 2px 10px rgba(40,180,110,.25); } #textBody .savebtn.saving { background:var(--surface2,#1c2230); border:1px solid var(--border); color:#cfd8e2; cursor:default; }+ /* Shared viewer core (layer rows, zoom tools, viewport) — same as the embed. */+ /*__AV_CSS__*/ </style> </head> <body>@@ -215,8 +221,9 @@ <button class="sbtn" data-target="altium">Altium</button> <button class="sbtn" data-target="orcad">OrCAD</button> </span>- <button class="layers-reopen" id="layersBtn" style="display:none" data-tooltip="Show the Layers panel — toggle copper, assembly, courtyard, paste apertures, and the 300µm solder-paste dots (off by default).">▦ Layers</button>- <button class="layers-reopen" id="textBtn" style="display:none" data-tooltip="Edit the >NAME (RefDes) and >VALUE text placement — drag the labels on the board, nudge with arrows/inputs, or hit Auto-layout to snap them clear of the pads. Saved into the .kicad_mod so every tool (KiCad/Fusion/Altium/OrCAD) shows it.">⌖ Text</button>+ <button class="layers-reopen" id="layersBtn" style="display:none" data-tooltip="Show the Layers panel — toggle copper, silkscreen, fab, courtyard, heatsink vias, paste apertures, and the 300µm solder-paste jet dots (off by default).">▦ Layers</button>+ <button class="layers-reopen" id="textBtn" style="display:none" data-tooltip="Edit the >NAME (RefDes) and >VALUE text placement — drag the labels on the board, nudge with arrows/inputs, or hit Auto-layout to snap them clear of the pads. Auto-saves into the .kicad_mod so every tool (KiCad/Fusion/Altium/OrCAD) shows it.">⌖ Text</button>+ <button class="layers-reopen" id="embedBtn" style="display:none" data-tooltip="Open the standalone interactive embed viewer for this footprint in a new tab — the lean, self-contained widget (canvas + Layers) you can drop into a wiki page. Same shared viewer core as this app.">⧉ Embed</button> <span class="conn" id="conn" data-tooltip="Live connection to the adom-footprint Rust server. Green = the UI is polling /state and the AI can drive this view in real time (push a new footprint, run /eval, read the console)."> <span class="dot"></span><span id="connText">connecting…</span> </span>@@ -318,11 +325,14 @@ </div> </div> </div>- <footer>rendered via service-kicad — standalone Rust, no local KiCad install</footer>+ <footer>rendered locally in Rust — no KiCad install, no service round-trip</footer> <div id="tt"></div> <div id="toast"></div> + <!-- Shared viewer core (window.AV): layer list, toggle logic, pan/zoom, icons —+ the SAME core the static embed uses, so the two viewers cannot drift. -->+ <script>/*__AV_JS__*/</script> <script> (function(){ const post = (level,args)=>{ try{ navigator.sendBeacon('console', JSON.stringify({level,msg:args.map(a=>{try{return typeof a==='string'?a:JSON.stringify(a)}catch{return String(a)}}).join(' ')})); }catch(e){} };@@ -388,7 +398,9 @@ _fpFit(); if(box.querySelector('.zoomtools')===null){ const t=document.createElement('div'); t.className='zoomtools';- t.innerHTML='<button id="fpZin" title="Zoom in">+</button><button id="fpZout" title="Zoom out">−</button><button id="fpFit" title="Fit">⤢</button>';+ // Icons from the shared core — incl. a proper zoom-to-FIT glyph (magnifier+ // + inner square), NOT the four-corner "fullscreen/expand" icon.+ t.innerHTML='<button id="fpZin" title="Zoom in">'+AV.ICON_ZIN+'</button><button id="fpZout" title="Zoom out">'+AV.ICON_ZOUT+'</button><button id="fpFit" title="Zoom to fit">'+AV.ICON_FIT+'</button>'; box.appendChild(t); const h=document.createElement('div'); h.className='panhint'; h.textContent='drag to pan · scroll to zoom'; box.appendChild(h); t.querySelector('#fpZin').onclick=()=>{const r=box.getBoundingClientRect();_fpZoom(r.width/2,r.height/2,1.2)};@@ -396,7 +408,10 @@ t.querySelector('#fpFit').onclick=_fpFit; } if(_panZoomWired) return; _panZoomWired=true;- box.addEventListener('wheel',e=>{ e.preventDefault(); const r=box.getBoundingClientRect(); _fpZoom(e.clientX-r.left,e.clientY-r.top,e.deltaY<0?1.12:1/1.12); },{passive:false});+ // Smooth, delta-PROPORTIONAL zoom (matches adom-symbol) — exp of the actual+ // scroll amount, device-unit normalised + per-event clamped, so the trackpad+ // no longer leaps. ZOOM_K=0.0022: lower = gentler.+ box.addEventListener('wheel',e=>{ e.preventDefault(); let d=e.deltaY; if(e.deltaMode===1)d*=16; else if(e.deltaMode===2)d*=box.clientHeight; d=Math.max(-50,Math.min(50,d)); const r=box.getBoundingClientRect(); _fpZoom(e.clientX-r.left,e.clientY-r.top,Math.exp(-d*0.0022)); },{passive:false}); let drag=false,lx=0,ly=0; box.addEventListener('pointerdown',e=>{ if(e.target.closest('.zoomtools'))return; drag=true;lx=e.clientX;ly=e.clientY;box.classList.add('grab');box.setPointerCapture(e.pointerId); }); box.addEventListener('pointermove',e=>{ if(!drag)return; _fpTx+=e.clientX-lx;_fpTy+=e.clientY-ly;lx=e.clientX;ly=e.clientY;_fpApply(); });@@ -408,16 +423,10 @@ // Adom-computed paste overlays are matched by their group class. // Every layer is its own <g class="adom-layer-…"> in the self-rendered SVG, // in the original adom-footprint palette.- const LAYER_DEFS=[- {key:'copper', name:'Copper', kind:'group', sel:'.adom-layer-copper', sw:'#C83434'},- {key:'padnum', name:'Pad numbers', kind:'group', sel:'.adom-layer-padnum', sw:'#ffffff', ring:true},- {key:'silk', name:'Silkscreen', kind:'group', sel:'.adom-layer-silk', sw:'#00cccc'},- {key:'fab', name:'Fab (F.Fab)', kind:'group', sel:'.adom-layer-fab', sw:'#6e7681', ring:true},- {key:'courtyard', name:'Courtyard', kind:'group', sel:'.adom-layer-courtyard', sw:'#FF26E2', ring:true},- {key:'drill', name:'Drill', kind:'group', sel:'.adom-layer-drill', sw:'#888888', ring:true},- {key:'paste-ap', name:'Paste apertures', kind:'group', sel:'.adom-layer-paste-apertures', sw:'#8aa0b8', ring:true},- {key:'paste-dots',name:'Paste dots', kind:'group', sel:'.adom-layer-paste-dots', sw:'#37c8a6', special:true},- ];+ // Layer list comes from the shared viewer core (window.AV) — ONE source of+ // truth, identical to the embed. Redefining it here is what caused the drift+ // (stale names, missing layers, wrong toggle colours), so don't.+ const LAYER_DEFS=AV.LAYER_DEFS; // v2: reset once — earlier prefs were saved while vendor silk/courtyard/fab // were (buggily) empty, leaving critical layers stuck off. Start fresh so the // correct defaults (copper/silk/fab/courtyard ON, paste-dots OFF) apply.@@ -425,39 +434,12 @@ function loadLayerState(){ try{ return JSON.parse(localStorage.getItem(LPREF))||{}; }catch{ return {}; } } function saveLayerState(st){ try{ localStorage.setItem(LPREF, JSON.stringify(st)); }catch{} } function svgEl(){ return document.querySelector('#svgbox svg'); }- function colorMatch(el, hex){- hex=hex.toUpperCase();- const a=(el.getAttribute('stroke')||'')+'|'+(el.getAttribute('fill')||'')+'|'+(el.getAttribute('style')||'');- return a.toUpperCase().includes(hex);- }- function applyLayer(def, on){- const sv=svgEl(); if(!sv) return;- if(def.kind==='group'){- sv.querySelectorAll(def.sel).forEach(g=>g.style.display=on?'':'none');- } else {- sv.querySelectorAll('path,rect,circle,polygon,line').forEach(el=>{- if(colorMatch(el,def.color)) el.style.display=on?'':'none';- });- }- }+ // Toggle helpers delegate to the shared viewer core so behaviour is IDENTICAL+ // to the embed (group-based show/hide; empty groups treated as absent).+ function applyLayer(def, on){ AV.applyLayer(svgEl(), def, on); }+ function groupHasGeo(def){ return AV.groupHasGeo(svgEl(), def); }+ function layerPresent(def){ return AV.layerPresent(svgEl(), def); } let layerState=loadLayerState();- // A layer is "present" only if the render actually contains its geometry —- // so the HUD lists exactly the layers this footprint has, no empty rows.- // True only if the group actually contains drawn geometry (not just an empty- // <g> placeholder) — so layers with nothing in THIS footprint aren't listed.- function groupHasGeo(def){- const sv=svgEl(); if(!sv) return false;- let n=0;- sv.querySelectorAll(def.sel).forEach(g=>{ n+=g.querySelectorAll('path,rect,circle,line,polygon,polyline,text').length; });- return n>0;- }- function layerPresent(def){- const sv=svgEl(); if(!sv) return false;- if(def.kind==='group') return groupHasGeo(def);- let found=false;- sv.querySelectorAll('path,rect,circle,polygon,line').forEach(el=>{ if(colorMatch(el,def.color)) found=true; });- return found;- } function setupLayers(s){ const hud=document.getElementById('layersHud'), btn=document.getElementById('layersBtn'); if(!s.footprintName){ hud.style.display='none'; btn.style.display='none'; return; }@@ -558,6 +540,9 @@ function setupTextEditor(s){ const btn=document.getElementById('textBtn'), hud=document.getElementById('textHud');+ const embedB=document.getElementById('embedBtn');+ const loaded = !!(s.footprintName && s.hasMod);+ if(embedB) embedB.style.display = loaded ? 'inline-flex' : 'none'; if(!s.footprintName || !s.hasMod || !s.text){ btn.style.display='none'; hud.style.display='none'; return; } btn.style.display='inline-flex'; const fresh = s.footprintName!==_txtName;@@ -571,7 +556,10 @@ injectLabels(); updateHudTerms(); syncInputs(); updateStatus(); _refreshUndoBtns(); hud.style.display='flex'; // always shown as a docked section in the right panel const cb=document.getElementById('autoOnOpen'); if(cb)cb.checked=autoOnOpenChecked();- setSaveState(_canSave ? 'saved' : 'nofile');+ // Only (re)set the chip on a FRESH part — never on a routine poll, or it+ // clobbers an in-flight 'unsaved'/'saving' and cancels the debounced auto-save.+ if(fresh) setSaveState(_canSave ? 'saved' : 'nofile');+ else if(!_canSave) setSaveState('nofile'); // While "Auto-layout on open" is checked, every freshly-opened footprint gets // auto-laid-out and auto-saved — so you can sweep the library fixing placement. if(fresh && autoOnOpenChecked() && _txt.reference && _txt.value){ autoLayout(true); }@@ -648,7 +636,8 @@ function setSaveState(st){ _saveState=st; const b=document.getElementById('saveBtn'); if(!b)return; if(st==='nofile'){ b.style.display='none'; return; } b.style.display=''; b.disabled=(st!=='unsaved'); b.className='tbtn savebtn '+st;- b.textContent = st==='saving' ? 'Saving…' : st==='saved' ? '✓ Saved to library' : '● Save to library';+ // Auto-saving, so the chip reports status rather than prompting a click.+ b.textContent = st==='saving' ? 'Saving…' : st==='saved' ? '✓ Saved automatically' : 'Saving…'; } async function saveFootprint(quiet){ if(_saveState==='saving' || !_canSave) return false;@@ -661,8 +650,16 @@ function _txtItem(p){ return {x:p.x, y:p.y, size:p.size}; } async function applyTextNow(){ if(!_txt.reference||!_txt.value) return;- try{ await fetch('text',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({reference:_txtItem(_txt.reference),value:_txtItem(_txt.value)})}); _txt.dirty=false; setSaveState('unsaved'); }catch(e){}+ try{ await fetch('text',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({reference:_txtItem(_txt.reference),value:_txtItem(_txt.value)})}); _txt.dirty=false; setSaveState('unsaved'); scheduleSave(); }catch(e){} }+ // Auto-save (adom-symbol style): every edit persists to the library .kicad_mod+ // on a short debounce — no manual Save click needed. The button is just a live+ // status chip. Nav away also flushes a pending save (see loadChip).+ // Track pending-save with a dedicated flag, NOT _saveState — the /state poll+ // resets the visible chip and would otherwise cancel a debounced auto-save.+ let _saveTimer=null, _needsSave=false;+ function scheduleSave(){ if(!_canSave) return; _needsSave=true; clearTimeout(_saveTimer); _saveTimer=setTimeout(()=>{ if(_needsSave && _canSave){ _needsSave=false; saveFootprint(true); } },600); }+ function flushSave(){ if(_saveTimer){ clearTimeout(_saveTimer); _saveTimer=null; } if(_needsSave && _canSave){ _needsSave=false; return saveFootprint(true); } } // Normalized text height: 1 mm default, scaled down so a label never dwarfs a tiny part. function _textSize(b){ const me=Math.max(b.maxx-b.minx, b.maxy-b.miny); return Math.round(Math.max(0.5,Math.min(1.0, me/2.5))*10)/10; } async function autoLayout(save){@@ -684,6 +681,8 @@ } // HUD wiring document.getElementById('textBtn').onclick=()=>{ document.getElementById('rightpanel').classList.remove('collapsed'); const h=document.getElementById('textHud'); h.classList.remove('collapsed'); h.scrollIntoView({block:'nearest'}); };+ // Open the standalone embed viewer for the current footprint in a new tab.+ document.getElementById('embedBtn').onclick=()=>window.open('embed','_blank','noopener'); document.getElementById('textClose').onclick=()=>document.getElementById('textHud').classList.toggle('collapsed'); document.getElementById('textHead').addEventListener('click',e=>{ if(!e.target.closest('.hbtn')) document.getElementById('textHud').classList.toggle('collapsed'); }); document.getElementById('autoLayoutBtn').onclick=()=>autoLayout(false); // manual: update working copy; click Save to persist@@ -778,6 +777,7 @@ function updateUrl(mpn){ try{ const u=new URL(location.href); u.searchParams.set('mpn',mpn); history.replaceState(null,'',u); }catch(e){} } async function loadChipMpn(mpn){ if(!mpn||mpn===loadedMpn)return;+ try{ await flushSave(); }catch(e){} // auto-save the chip we're leaving const prev=loadedMpn; loadedMpn=mpn; try{ const r=await fetch('load-chip',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({mpn})}); if(!r.ok){ loadedMpn=prev; showToast('✕ '+mpn+' not in library','err'); return; }
src/embed.html+23−77@@ -2,99 +2,45 @@ <title>__NAME__ - adom-footprint</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <style>-@font-face{font-family:'Satoshi';font-weight:400;font-display:swap;src:url(data:font/woff2;base64,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) format('woff2')}@font-face{font-family:'Satoshi';font-weight:500;font-display:swap;src:url(data:font/woff2;base64,d09GMgABAAAAAGP8AA8AAAABIBQAAGOaAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP0ZGVE0cGnobgYNwHKAiBmAAiE4RCAqCxyCB+28Lh3YAATYCJAOPaAQgBZJXB5xgG2X1B9g2DW4eaidQKePr1ia2YhybErcNiDr3xzacFcHGAVDA/6Jn//9nJZUxtl3dLyAQiFmQKcFFcSbkctWEdDWqccq7EZHRKLqaoc/VheFl3igRlDXhlDyCxcOUTMlIcDe7ubhSPMP6Ot9vBDWKQCDYVyC/T0JKb16LUjHzg5rfSZ9l6ngnGosl0i03BG0TiyU1uQTRqMv82j/8Ldn9MG17+/kBCrSpNqE75X/C4oliiGRpv7mH28OWDrIKjF0eI2olp16iaKwqsnqADwAVoCN6soCewAGB+vevXj2rkw+sVj0rQsU/z8/tz30LRo3B5qgxR4hj4IBR+Rki0R9t9hGQT45UQJtyVEi0hQWoxMzAolIlRMFIQLQZ4uf0j1JBtEgSPCmiIUAEYkTsRfRFiLyEBBLMG6yGFrEa0lEqo+Yzo521/X9AlU466UbnHdSmjAMWYz0D/w+/H7/WPvdLiNAKiWiaPWkP9AYzj/6fP2CduY8sjCCBLEijBdbl7wA8m3ED87+mBgCuV+AwK3EEveu2n7ZGJcZM6BLbqQlfCuLzf3g7/ZfW1/DiK4i7DyVpJ0j/r2C8fzjyElfSnmH7uv93aj8jWQszus0CUgqIF2RfJ4qbtE6r7ct5p+X8yH1/dvsHAEkJtwAg2Y/xG/93av4gJnadtyZy5nRusX+2LKuRJVf6Oj2nE8ACuAgpgAuWsG7Aom66w28iFIQEyWwUugQI+Pc/Vcvqmv6a0kVReXp1DpKdaWdhQ4KDdAoXtEOch9II3EBTeLMiD/pglIglF3fn/3Pv3vnvjr9WY2scUYZRGFOAAaZRnZmsdCjgX7fWMt/ufSfZ28c8lQgUs/+psGx0O56FAVelgChYsLsHTEQyTSCCtb94P9sI10hVU5C/P+BI8Qsl0xJtupY1bRkzLFmas2IOaGT0s7B7CSxXoCtgOvh7Va0WnyAsOc0Yq9vRhqSZ5cXQk3burOuuS3F68P0PgsAnRAogJVGUlrJW3rVMy0FykjaBBMUFgzSkSPHMTSFpvSmG8lIORXlz1ZUur6vvqlzUzVVXtud/3eKOC7t5KVrwAB9gJNEC7XzhxS7bicIHGOBqE3zP79fq3n/W5A1+4w+lgr9BNKmUSNscKZnSKldrpe0O3gYAHJEwJBSgkHGJm5mDX3iY2yeau9Be8JFtkCygEQtPG0JS0TEqFeEihI6xEVKE56G1StvvcCZh4VIJyxYuYWlne+fgbx1RZ4Lu6ijpcAdQsoyQMRbZod87Lekq6XBhYBA/Cm6/JM/z6nxOKw1KmtHFO/ZuWkUgYaUDGMjyAigLfK7G65vA92IPMpRQ7FaWEHLqbvh9vdhd9zeFDqXIYyileBJyXvCCBBER8X7fZblqAgFqVEjNgGZYOX+877BNe8BKLROj4mgQ7RGYPj5LjrSbpFvyMn418nEdMmfFYru1P5ZdSlhhKOwEMu4yvlcLAXz4SNsN+PzBbbv7q/uQ6wQEzICbAQoFjgOEjw/HUSgkTBiccFEQISGcaDsgiRLhJEUPxcbacFmnI5Q0aWFJ1xhKk6awNJuHstAPOHiggAAjwfbCTnwNYyBf+0MaGPCA/4cBYYx09LvFYPyYHVubmn/CvuWfKYb8a8WIvvXwb9UDI7I3ds2v1UD+sdKBdlFR1FghIsfhQ+HeJMGGGZ+fgPoL9wVJDBjJ4J9T1xK0twJoZ+9uwClQfP96url7VrVv3G/vY9DUs7A8fuI7vQNEtKZTALoC09VCagAGhk1BD2IHiIx4AnbhTbDBiQrUGUzClq80ZAUUBJTC1TgCeBLN3IbLp+4m7ld4su3DSATG8XL13xr+jEUz/ia2DJLY6zRGBMhibaYD8pgrtuQ560ol+jqaAwMhlFXncJNg/Z0X3s+Mk+SwyIXLWctjPM1z9yzmVTPd5gN28QDHgCc5A/iec+N3/lUhvGoVoijcZQgXEBuYK2IQW4pl0ElS9JUBBSEKvnzyw40eo6ag3TZFImV5XKJKuKsnmDdIo1pce1HX/bv37SP1mCHDz/QC7Gt9/KzqR/10a6J/rGB0wEzHn2qOCWxqelDrYFFek6fh1U22KcQV93EGU3fA39wZSNz2eaj8AqmsQCKBDYRyKpCkeY5jiumeKvtKZwv3KrAFDlm57jbblL3npDjMnIS9OoJL6UZSTYhdGAqwyes8yli/NZYQt8hEheVSSVdpDvIAgtjlBNaR1qZ5JOV0vg5kRzImrIMJw8qvrvJIhoMvBPtWqurbE5eCv3n4ZoMX/8KAJXmLHK8jkIaXyGbkoHvmLnBtirZJte8/0/woKDp4VPFHlHoU0r+QvJbXEF5s39KxA/UY0ZbidqbtbfNlOt5Y64U6ml2fUV+9Ddhe38LEW93uJ/GamgRjrvVzuN5TU6rN0IqkKGse+MCFNstEQFK/ssiwX73i1W2KJ2jZv8FjqqxnPar6XtrQa7lsX/YY3R4BOY/3IvA2EqJxWFc25iuk2r76OirNZfVD1txGeW9OHhyvF3KsLn3z4rPoQX5C+zuyloJ6gS9otohjZya3x9MIvVxQ/SLKcS2xdBs1/ihcSerHmq7y9M5jOMZ51xjclExZ2rAb+X9rMQL2s0RhzQadI2dKXLlT42kDlk386UqTRl+pUgZq1DB0zDE877xjFCCwL2vWmdSsJdPaXMkKhoiPD1wlDCIkhEmUCBcba8NnoR/UaAJPAs8DL0DqgZeAl4FXgLeAs3DuAu8DnwJzwGfAN8C3wM/An8BfIEB6AJkAZGGQ5aEwQ9GKEE7EGITFNHysI8QhQpwjxjNC/IJcGnJ6/SEP1h9ycf0h65EgZ5fdg+sK34euhsdmgCDfJ+HMEw7ofqK3Ie7tUyyLQgm9DSR7BKh3vmVP/RkJ+GMEt62F17aOIF2r/oj42qP+Zf1HeJAD27G2qWmvRzhSLuBlQOdMsr/+WNJKZ3mk/FnpVcbqbs2szR8Te32k4TXKltnydOxtSX+iv9hf73ta7/7XURSuERAhIYqiOTLn8ER4O7JLQgbqIUYPZvAeP+nLmP8MGWdFupIjBUzgXqYtwUoSOere/qGzKSNYQiWo6OVBqLW3zPDBZMoOk2iB8P6UmGTUSxNH9UHrn9pnjQKKFwjkIR7k+9gl8c4c1oY072tJwlvciWGecXo340uRgTQuLr++h9pj9WvJSguvCXfvXcbO0MvClY4wbQAFXUKYEL+PuD2KuBfKzJElgLfIUieJDYw9I2XkAQIdP8N8uy+cr/1fBW7mywWZcxyntZyDlZCX86aE6GHv6d6C77w/JEG65GGHEfvL8ZwUfZu/rbg2Bz4Jay1s8T07MMHiCcLSNwST41qnIMLchx0vsRCkwuri1vMmxaklOVr6qawstZyWb6Qgv7wQU4TD9D2ucHDDQ7nKVO9aHai3xkjxIR/xyWdffPPdDz/96sv0KVkqahpaOvpiUDMyMbOwsnNwcnEbKuNgskyzZs1ngSxiybIVq9as27Rt175Dx06duXDlpu9hU8E6NQ0tHX2xQ5uTm7f4jEKgZUvdjj0Hjr48gBeEQvqFUyTZSfIqBXXFlJ9WMQrrwUMkEoUO0w9L7iIP8pFP+fLtR0tbR1dP38DQqBv/Td5xuvhyayg5O781mj8x5Jjus+ywKcj/D3YQYqvQt+r8roEDxcboZhipAI5tHGrbEp0IdZ8P+xRVeS5z37fE9CtWZSszuaNGYvALIR7qaBbVmKrzHY5x8folhkr5ylJOKlKlqldXm6slNXq0QafzJpn3OL4JCImISYoU+eCjTz774ltflk+bPCpqGlo6egZGJmYW1mbjs0cccHJxGyrDpBGjxg5quU3hpqvMhFlz5ssCsmjJshWr1qxnM2zbte/QsdOc1e3c0IVcunLtxm08tN98f6P7v0w5V0FKDb00oqWjP2zUMjNrs8N5TnHzFl80BFq2yXbtO3S8rf8bUlvRYrISkppn8LK8csiXAqr4wrJ5lagchlJYl8GNKdS8Hg1BCCHE4/F4PB5vZSPjMjFsatbNe6Dc1ih/DlZi6CCKRLITfnI6Dagk0gLz1VsbFouD5C0CT0Iw0SiB3BctC0kzj6KYh0JBhlIFhEH3EMLSQnQGqLgD+aGiLlkGl7TzFaSs6KEZprlz5DBvGjQNOteAkSnkY/QBkmdcOD4cqMelZ7EgliqVZDeKp+3cdNuljw5/9oCtgIiESA2gBCLRJHIDdp3vvvgvKJtEdrSsDENVSDfiOG6X+6ygYMvd1cB2NfCqkoDkITDhKJuWZZebyb5bHxlH+TXYr6gKtFwFdEJa/9kC2xZSeQkaIWGt5bbgL4+68oqMw+OPIpQ5kUS+CPQ7rxddfClKbgdkDiWehFGCR6soRzvOYxOnyitAtTKJHDA5TlLVaqjt3FJhO3XYLKQxBrMokAL92BquOkvkBipv0IaN2vfjr5PllIzMrEWIJ5I6+V+WUaZEPqmQUCSEAgx3R1Hd874KXwSERMQkkXUyJTPd3CQfGSJoPyAshBcpUUVxyWSUHKkQrA5BqDjM8Bf5hyw4LJ0SLVFo0Dy+tKYfoVlhJCZuGlmDkuDEaJ6aqkahUCnazVE5TKpun/rQ6D0+ASERMUlkPeMmTZkxN/fkM58iBOjFgLJgTApJC6eITHJKpYLBQwiJQofpYHHhCWDelPSoa2JTs25+mo+qhiSCtg4349BFDJcUXJM917YkBSnQk3YsjR7SM+2CHyXcj+skcdzgGq+UUEqVpXxVkcpUVaoZta1Oql4aNL6xSaU1LiqnQCAQCAQripKkoqahpaMvBjeMTMwsrM1GsUcccHJxGyrDlBGjxsq41OR8SmCaNINZc+az2M+yrFq3aduufYeOnZZzyqVrt81D+K38pfu/TFnU8CYtfTFClZm12KHJyc1btql27Tt0XINaWVFiqhJS84ygLFku5OdFN5WZ8E5IVVov0o6GGrgofln8473OVfFsxD3026O7Wpm2dXT19A0MjYwz6WWKWTf/Gt758lYn1dhuXPVR0xSOyuBTl4WpESApnLz1S92Vww81thsMFgZfSogRSfXGYzUOjWs8JcpVtmrOQNWexS11wxcBIRExSVEiKmoaWjr6YqAZmZhZWNk5OLm4DbUpkelJZtKsOfNZxLJV6zZt27WfQxw7de7StdvbPN6kPpnW9MUOXU5u3nlGVBaVQz7FWvlgg98Ag0baGtH2sM+jtbR1dPX0DQyNjE1Mzbp5T0aWBoRQSfUAsZud/Ar7Z2JFUigNc+1O2FKEHwSERMQkkdWZkplubpJPEiJoPyAt5LpIiYaLS5aMXblUBAsBKkznScGmnxpV0rPuqahKNW7jG7YCpOUqtb+xFLCfSzAuiqs7uYx2woFjUrOwYi7tk2RXn/cf+Rb37fRIqKUwX45iRet1OhAnlkRL9ZusTA6bPG4caZ0YDrh4Y7EajCAwAiOmMzWs2md7JFlzPABTGDD1rMqrp703wDcBIRExSZGKffDRJ5998a0oMSpqGlo6egZGJmYW1mYj2OGIEy5uQ2WYMGLU2LtGEwPMADPAxJgYE2NiDMMwDMMwMSbGxJgY8xtBeIQYE2NijFjBOrdBlx3OciQnFzcP7zCqFD8bkg2gNrM9y67tO3S8CQpHlsSQkJpnDMpi5VK+FMQVxxKn3bNWZcNngyZDx7Ac7DVzQk49B77t3LSLfRrddqnuSq7duO3fiXvAesxT7dlLXpU3ec+H8okv337SirTR0dXTNzA0MjYxNTMvi41tadWtv4Vf6A3YokDqUsWXGxjfe7cpvuQ8toV3t0JckryeM8FVuF/yXAlghGJaCVL+pFLrEjluug6NaZ1cjpKezZsAAAA+DmZLAAAAAACwchlmjBg1dqDbeL9ZEwAAAAAAAABM4Ry5SJeuXLtxu549AQBAhV4Hra51yjAMc9x0G++aL0GFDj+gu7A9uh1X97rmOicFLMuyLByFBtkHswAAAAAAAAAfDs25dCHFLepN7xJ8KTYF7+llrWsbo+TXrm4ZUYCO2XY17PPdcLyqtfqFjcs7+1tYA8Itc/VIVFxDmonFUBkPCbaRcVsKhUYgeg4WyzTEDasnqBrTQWtZnc80xAiVAyuqyFjKhBgKp4vUy2eio+ro6tzAxI2sXsyLaiVuh71DXjWwcfQc9Gf3PBQh+R05EaUdZrqwsJLf7i4ERT57VAqzcMfGYyXZnaE296ozC8ssoJ+maZqmHwu9V+/Vj6V+MKVdmZu3+IQh0BqY1o6LIpSoTXHJdQe3V+/Vq5lKsAgRqDAKV4fXy4HfO1d1dI9WIpFIaqIK0aCsGmZu0xCpK13b+1ZIHHIomWhLXedaODUwNzgKV3jKVQ7d97wmbipqhhaVkvUYGJmYWWKFnYOTi9tQGQ/qMMdYtmrdpm279h3mGKd9D5camrT079sxD/ko/nhINhJqcydQ5WbRQm4KI5Lie+imhzykWsPgIRKJQhfGTWy4TnhXUO12UhFP9lC3p2h7DW/eS4upraOrp29gaGRcFkDb0qpbH8oVc3/cmvDeaW62ljEQ9DNaRa7Yz5PcUhc4WSgTUcHYCFSY6rJXIYosuPPHiH/sLToB4R+bKXT6XauR/Whk3cEHvbQa5qJoSj0qR9L4aIpsZ/zwpN+vPl0IC1aLQovCJ4tkYmsSltquXVs1PEQiUegwvcss5c4o8vlAbhyZzpCjvHEVlooRaCXG/9OV6nSTNtWmczYTdXE2xTAMwzDMamLcYY7WzHhnTlMLhd6Cm3etHdqKvnVVZienn7Np3ezVezVt1elFtMVGEI0wkZJ8GZjVqMpzJTJLATJCIWlQ1m3p+IvplAaNml75DHklm0YtCm5PmhKzhBQcicZWYUo6bpOSHBBXL2Q1hqjXeqdKKb3KpVd1Wdn5qmkYuf+0rrBut55MvehLP0MzyUJlo2xFaDs7slsOCuyknEXowpWbcldgD568ePPRfS1XIxBJ5MvNkN3PrQqnC76c6RILfnZIfK3a7sB0HpSfRHdo7S2E+9UYgppod32Xj+YEUV205KNhsOS1K7j9J7hOlYXJqiuV2piG37sy+3GUzshQHrwGPvffU/+Wps5/0XCKMCRSiUwKSmxshMS+EU96WgbWAaMR3H0r6PYRSdIwskz9X7cEcpiINHDQraJvOR5bGhw50efME483AQuBAjmLEMFFlCiukiVzs0cmd3nybFSgxCal2vxHrMtePaaUmTbtlFdeOW3WrDPeeKPRO581mTOn1YJFbb777oJffrnojz8uWbLkcggZdCUe6961gavFRCi3HglV24Bi1fJGqVJJoXTppFGTZjLonHPIqMUVcuiOO5R16KDikUdUderE0KOXmmHDlntuEssL82mFLfmDECow95SllxVI40P83UvI7PApUsZ3c1xu8W339ekJ6NAGEsToEPnGiYLo08qYkOwD6PsLTGTArdh/PPtAgpSXrg1f/yaDXk3YJm9yQ+bK0z7y5V17dc4HzWsbgG9Oa30gebMvYq8haS7vEDCQLgN6pRelBXxVYeciwyNIYtfsL5XzxIwGQDZnemTqzPG21aGJIkqgWLOhJKcvab/47H8vDUB8IMN//Mu/FGYUZ0xJ2LicAakKOKEq7nYYozzr0vG3tvqWjJi33UMDb21C6V4dsYsXz2jVgl4m8dsjSy2R6Lxa8VT+9k44By7GISAbBpPldzYoyZosL/SY+q2I5CnCJ6zv7sXn000PuSP39G6Z0cyitOihk1SqUGaNeqVIq94HRLm4rBIlxeQJoth1/meAPKz7J5fMu/ePi0lE9JTbQ8pLomlZmxruWcLTGDBUnbea1RF74JgrEOVkU/JYHWG9/e0b4JOo/CiHtWJm2d01W2sZZ5de8w1/9kVb5Y/++Kf3L15VtA9X3kMAcg5IDy1idtuHhz57/leAjE+3wCKfbfyCr/y2FcZRRceGUixCRH4NQDcAkCkt8vTzfICwH7CT/901TAUEbKOEN8NwB0yTCSBY8UhwZUHxikLs+iNbPUgOyKxG/Zy3r5pAwc4APAbVOLpEjETitJOw8uKSycx+xBR+nvvSsg/r65cIgay1dNLJxecJxC8Hta4uWVWkvU0CGY8okqsU/yAg978/bywl4KO67r4RrVj94QZYkLNS20ZqOIqewP5ffQAAfGJukPM0umxnd340wrvPF/OvYX1o7anvCUARalDEKsIMajxKYyCj2Xqw3ud7aDgJxlcWhkPHovxkgVab9FyfVFV1gS7D8C4fpGIgkbiYPACHpUdsshESWiedgrqjSpfhy+KQfMKJnDWez9QB8tQ3lTDTLQmNsFAr3ed+ByCw4MQCyxuzV8JOnj2we0GtcH8flP5ujjUHYyOvtwcKIbaieO0XSdMMnWVVE8x6TLbqHC7/VWxJblZuUPUtwGWR+ozP0CgxVCKcu9hYkKu7bICasy7r5CLh5Q6imakTq/WVx1bkFlQ+qjjjjjVYbcVVdjAV/LSfS67d84vBltwDtSqSl0VF9xMdkTbIjRtSDTW3g4ubHd3qyzoqYfLg8icROB+Wg6DwIn/uKsg+6noBDLuI/RTowzEVWHFDMydI//pTPpGxHKoA7ZXBOmUvJccbcUVgRWKzGQOyJdbc4GyjsylYHnyN4DB6DIfnbSnEAYjw9qeAxDm0SnPqeRSRYmfBZ6z1XP2eym/l3jhg6PoLNg9EV7W2n5CCFB3eUI5F/SLWCAJtdMML9TWpdo6tH4DkFJmyTK2PhyI1oI0KUpR/bwN62KU48u7qSYhLHhSgHhRQprSv69DZm/+VCSrHq45YVqZ2ZqPKx2mDGzmWlHeV4J2uj/CK28nIEf8JWUSUWFZdEBHrqikRTWpGLm5N8h8RMseM0YJlqUDpqLLL5VK9RmmrEgimGz0VZxJgOfKN/7Y+JSckKk81gjwG0nFQJVwAiK8PUjUJN9q1lqlPwmFaqT4NPKBdw7vnwgPQrs4YM/6usiqmdop67ZF/wQmdRat1caYhPYmi/Esb0MMuxZEXFw01IXIQ8K/aVIq2dWjrzZ8/Uuog0qNOVJlZiypOS7ieo+roh3lFIEpEuTCl6dG2gjiX4xUFN2mi1z8viq8q1V+bravLrW/IY04gyppcrDONRA8Gqb6M8P4Q2ZL0RYjAEyWKkWTJjO2VyUSeEv8oVcVZjTaexLqE6DFln2nTCr3ySpFZs4q98UaJdz47ZM6cKgsWVfvuuzo/Y1B9PNYNt4GrF0Uoby5C1c0nXXpf06S5xZxzru9pcaUfE2gW98FKAAJG2b/rf9ZDlTWrEMHB67dOmgzZy5NDIU8BFc0y9D8Lgg1gA9gANosVsy9sARMJsp06KOpUsZAIJTtcEmEzizSzJKdPqrfix0JFkPPnh9wIKPmcFCVUflFSQP0XbQpo+GYxgcY/20V48N0kCroOdoAdW33ypy8szPyy7TC8//2rFeZuoTv777eE6Qz+3hK/W4EPvkqKr6FU1tH/1xLVtSimbMT9G6xSSw5UzaX6hsVYNpIB/lAuMaplFPuXsdUsp04TCJ2FJbBEVoIlsdLsClab5fAGOLwuNq9Hm19Fi+cCVpKVUlf+wOQY1XtHA0PBIiXYK0OeUrUaNBO77r4uQ8bN+GjR3xARGdExaeMyZcuJnA7CqUqTLbrlKVWrQTOx6+7rMmTczPe3YVY4wXJu8l319si3ocZznDXnxLlyWzghF8ft5BpqmiP6xI9gd+A8uTvCzdl/TuqxJvfY3CYukKvnTopYrjep7x3qbwDusv32pIHB5IE512K/PGloP3noXBMZnktQMxYuTy11690Nt/EIIPa3k8cGnBO3hQv4wyMME0y/FlOeeqZNu1ZiY2a88NI7r7wxa9pr4z547r1Jn0z4yJwFS1/63FvzLfS1b30vvz6UKFPp90D1/+lzGLNlz5Erd+tFqVWn3mFHHHXDLXeMeeqZ58ZNmDSVAio6ZaqMWPGiAdN6yzXX3XDTLbfdcde9kErSGNiM2XO3QbAYu4mUIOSqZB/qERch1+z0kd6SEHLdLh/rKxYhN+z2if7iEHLTHp8aEB8ht+x1zaCECLltn+uGbI+QO/brNGxHhNx1QJcRiRFyT4puo5LCYmUOBkPFAFlGMSk4IBQgKhDQIBh3HjAoqykDDQgaDIMWBHQCwO01vrSBZOCLvsvab3LpjxDzfwhaZCxUfOZ6rELwEueoeBuPhRcfEoZTugkKk0ziShIrrnoNGjXZzK0maEFiiHzo5qXYU2m88zU4p7fSXXUKOm10GLADgijAYBQcPMQrHL+OFBnwFEnkmOtGfA4JMRlzZlZlmwL1ml31wIBxr5OGnqRLRXKLd/eAESoXKZv70GaXtLkObDaSza1vM0vYXHo2E9Fm79p0gs3WsVa8zdysH2czlSXnDHAjWcMzAmaKUf/TgKGkWEb6fGXtIkqs3owFVsPETl+ovsOD0mMUKdOTsuoaYKexODUkYynB06DHlP2naSxNNeHYKgi0cJlz4P5pOpennvwSGIi0GbDkyFPLy7py3fkjwTgxNhuPNSfeNgleZ+Lq1EdtZyG1r+0687VFiJh1lqxNPYN2DZL6GMuVH4FQcXavs3h96tlEtUhpK7ZOgPCukYr4TNfGP35mpAqQ4aYSbOahYL/y6BQHeVt3qPG0B626bheftHB8yX9SDIP4/z7AhrnqHqBcT1Fu46W0Vb4I3TJbouRfHepeGnvjm1/f67w71NfFeohtrKtLREbHpIXDoPPhuPDqnIu49yoKnW316gx2YqcVIC6bNmq9Fed/683PBlsECBYqUowESXbbL41Izq/3+vFy1eodc1Kjc1BabEqbdmJjZrzw0juvvDFr2mvjPnjuvUmfTPhozhefvTWvgVnQqJUT9IOpsBbFTvAjJAybfC3aOlUYxpWJSMcJeRyO1PfOM+MmMwpDQUi7D9775GMIgaunnpsw5YmIzfW6dB7U9UyolQL4phA1nrGruD64DcH1q4n7pUvulKEFBFCPbkPgPHfUuTJeem2veTc4DcQP6Dlv2xO4ifVXg5u7yILPHz6vB3gOkO/WH1hPZipPGmGPOhGuTv6zgC924EH7ERHtdUqjS2nSZciMM5lyW4yxaqnWBaX4qHie+CyzzTmTkpS5eTxvXtP3vFeWCovJ0mBpswxYPJYly8GOjnGyM914FNU11LnqglNxMZxwA27WUNFgauhqON+Au60OV2s9ru6fu5L6HSf8fbO0BKx32BnNrqSNy5gFV0pVxvyc0sx2fyMHmhUx65wzMUlJ4bC2/XvOC0uJxWCx3jgLPsH7WTq/+RQgOk8vG1z11VLm3Gb8vwD8x4/+93/+66svPv/sk4/+6k8890zgwsfDxRS1Y/uXL0u2w3azXvSr+URc9a0RN+3k8IOqB0UPCh5kfPX3RMlE0UTWROZEysS+r+q/0n/F+orxuef9lafP7y/db1saJ51ef3rOwLahLcHIgs36gjNCJFZMjHbFCzMb83ERZof/mxsSFhEVE5eQlJKWkZWTV1BUUlZRVVPX0NTS1tHV0zcwNDLGxsHFwycgJAIRk5CSkVNQUlHTgGnp6BkUMDIxs0BYpcEAW4F7pUp3UL4SlerUqnfEYUcdd0KDk04547RGTc4675wWbVpddMEllwstWpBgoZW1XY0o4XUXL6yZ0pRWU0wHHSlEQntVqrDedEAu7BixPQJFtp+hOuVFSK48cKcDiiMiCRSgUDEnO0ftnehaHLE6CFU746ywdlTEpaL9RPbJlCFbjlxZChXVWIFyFcq8ywY/YfnssUuMh4MZyP9LMED+ArQ/Qfs02P0H4PhjAMj8ADVFflM4BDgr5hB2NNYWO3mcA351z6Uig2mILsXKcRpiAVDxBOQCXt0IcU+DT5DkUr+GbmcR1uD8SQIGxy0w3DMOpFVDZ4JUXlp581YEneP0uzjC1M+6NIH0miMHrYMevA7oQSDOd14aPnhCK/GeKMrgWB23iBfzs3hkSGYi5fRqMEPoLIImp5xxqTKsSq8aMpiqKxW6CxQ9/69ApvsJ12aCyJPcx/EmKohgWo76flFoB6mIZDXANv7M2Qb3xT24BWqkQrnyNI5+m4vmKZiRDVhYuBTvNq/RCI2B68ZcWHx3H01LFF7iclZeX855AN5JUmcppZwHA3Ngf0ITuCQS7jo36HvC/gh95yWyGTCoPII8lDHhEyFzWYgBKdtWWhiGSsgtGNRgiIyeM4oWVqQmdwz5EQslGFQeAZyi3C/GT3QTYJwBfn6lA+eqDcwCDKoupVMlR7eGZo1sKIUR5UeklnLkAcRij9wRo1inBjDg7nHuQ6SsI9BNoXioynm8NQvr/bCbrTFsQxdu7LLVYnQcKzoEkXzWkNOqjVHjDElJ95hEIsIGwTCvLRahtb7+DVia2TudxKKj7cTo2BGvuzCxtrbDLe7t1u5//x0Ft9uq2Zh6LbWhoFGFWqgiyvb62mZbLKyE4hhKkRQ8xzQIXJFTLuDFiD8JeznhbbmvuUYdzeRUSb1slu50MgeppSyXPBcdM5YXQ5qlUo24YutjWnQijSQghad0I0pDoe+nSfaUDIcq0v60y5/FHQc5y/Kwj2BWPvYS1zxu6PTHrm3yHEU952QcA35G7/PcDTUufxciIdNbwAw/EuDi7w9cHvW8CsVfyHm95diPJEWmj9veIy6m/bHEOoV15QljHEAl6JO7htyW0GrrWrhCCMUjDQpVGp7TNucmtNDrXeiFNMICwYqQ8Drj4Jr1D1X+b0i4vhqqIjLEQfl86QJXzsVnQ0l63Zq0X40Ny4YMtWfZXpJExnvqOVEBq1XNG9MR2HMOSzWDDGmqaNPjkdBjBXEiofFlvPoQYvUJ3KhhKYoTKvuyqKTN82hhU+lxWT5njEdcefLjzN+LkU8IJtRpSN1cfhnPFWxW6ihyLNXs+PAAq+nzVD4bxClXpukk4T29QFVWIpwkEN8pNV/hquYU5A91tzHH+2wS+76/9UOvcfXyWPe9Xw/pPfTANxu+Gtx32MF+1y0jAwMBIZk4GY67L+AKKp9HHeW7ZFBAelW5cg5ipogtY4xTwtXTCiXHchIScobamtCQlumSJakmQYNSWELjowCZ/qQjz9m8SxsB8VcpssO+cAu9yhYxbO8zbQX48gGWmDUsT55FrdK0C1CrhQJaKfLN1TJOd6HRt4p5bebBZgnO3c2SzDEXCZbqZxif/xcC+396MjxmdpSktUFjVWniVIaXS25rwubpWGgmLNUu9Ov44Si1EBCZnRy6eOqzsxdBuqK3+bbDGCzFubtRgFgDCrATNPUCS6rXdGeX1opoKO1yEbNI+zbg3Lo3VrAn2lQWa2skwNMQHBsKs6iiqrzgPm2CPNwC356J1PmeQLDpKvoO3dCII0y2L6FwSB/iTMJ3bZg8doTLGXL9MEOeQYaNeJyxx1mGTpGDLOUkkBftFOB78FpD2Otd+jBPjVvhbvwsLzr73yLUStbXwIBakRjBjEZy5PhAwRytnzapcnBCtdI4PU3VGLTILOUh19GtGjLs26bmkFIO06Z5qxNektbVEOuAMuRZlx0R4HiBybKqmM3Zw6pmLLdyo+HT9zfDA9PtnPLUmC/mWKX1Vsql9GUdkK3mZVd+/e1dL4fqfIwi4ka0CsVATzNQOItmK2SPkJ+yZDmArM6uHJJmVa66B5f0BYd0Z6JN757Te/q2HAxNS9e+auTzbzI0etc9l5xA/rqZwIJqFdofv5wH0JQCeIY35wtIEdEtzy/pY6DyWQAGcsgli6LSsKM5ih5qNfFsMSy6w2BZ/L+Jqp68y04YEV5QLYvMzrnhoL9g4VoMS6tpeVnI5PL7WCwKlfK4Lx3hYwZFh9Z4BFTKz1XjtgLKi1uOqmyXvnFsX6s3DimGmvhcLwIRwI02dxiO79WrPMDSvp46BJRN2Rq8KXjtUb+HySVMtlqRTQcvsB+BagKcK/n07c1ysO6jDr+/3Zg90+0qBJSrvrNGBkxGszIslV8ICRv88O63ja2W88HXKXwsetPFtgiLFcTSE2ag8h5IAhGI917Lj48uo57mpxQFlTu2Gk31VdtH83N+BBAzlAHih0VmFzKD3vmWr4ED0NAiChnnLCSnAIQlhXFDRWhIVQM4SSQIHxfH6ANCPoIxNo+BNtq0d9YRppvSpu9SeaKcai5jaZieph5UJpgp8lI8WGkrKKB0iBxQTBNQedWI/HK+7Q65T7nsFOOke+BWbzyHLmBTZOlnj7WmsnS/fBfie5UH7a4Xjc/yKfSvZcWjS3+7PAUTV0h7X0K6xaaKNgbZaXOdqkz5S6gKz/T4t96lJbGiQW9L91akyZSAgSXtyBDVA04mKZ0vn43PVUrjgQ4KKPpoaiHfdBHKUwHHc2gj2zXcI7Ku5XzPVe2SjCLPoRTXx9yWMjTHLeCnyCXm88+gp7lWftzvSeURxApPXwSAvu9HbxiG7zwuw3epzNwBibdRtSMPOyi2UtRAaHgUfFPBeU+wdKCCIUVhViPj2bKGfqDENMPq8XjIPBeRHFW9x7/c7vPH79lzqNpQudTvd/TKD19euILtPjMsqvJA7oT3Au4zK9JA2aZIIT+lyFsvUrlK5bfgBmIKsvIlcRFiulicVCsHm2e71Anx5604qqs8P0DwkaRllwoKL6VbtSJull/ZAeS0Li6HYRF/SxbxAHOMe/7KOQE8o1sz5OGBg/g7ABPJcVuR1V87spsovWQ9jQnw5s/M6ZMqb9Dp+1MFDrr8K8cHCxgSLISE+76fFft6qMwUqSZrOdXoufWqI/3JfP6UGsQy/T5WxfvEu6VQ0i33mPhbNecbARBCb33iFgwgZhK+elvxdfONhY80E5DHwDUe1LOqGmHTu2k9+sSObD7f8wSrwWnEcWEDoo6SKy2yEkpVDPKmmrfhC0nkFTPPRmPpL8G8hZ6ah1Pt67oPmlPhUL5ari6+J0rVf37e22/7rjatELqb31I+CXXYYWBD7crqX/jO6136dVSb6irFydE5d67DRHHw6AFEZsBrK/M/mFLVsqs5P5BXDjD1o/8LoQvG+D35Ohm0of+yXgYl6H994RnTfwoYKtxnPuz0P8J0i2JebxLgq/80K/CZPt1Ydr4ydjCZqomH2F3pYTww2GmiHtW4gyzcMz/lQGTEyMlQwQdImuoiSbBpE2X30tOydiuFKFF/5rNDPsmtZHV4Lp1Ke9tZJtEqWfdFrP4v47wXcgmc9uCOTuAefhV2qNuPXelFi9eKnthxNq5mqoWWEmm/aMZBLaqS5+WylL4ThwX7uJ1lObT/6GVhHZz8LwwcqSxrVtNndMFrEbL8oz4oXPajLC1qNxldEqMoETywkzKjtn2EzFQydKJj0pT9/xd1FhCxwa6Ir8CQ4/Thp0iObvpR8K2bXqz7KN67oTOPBFbiEVaubVM7QgAvyjmdmotpyZ84Y9mO1Ku12RUBTQ/m44c9cWfHmys8o32jjhP7o3jPNjpww5MecP2t1afF7C3nCJM3eFfFAL7b9VGgb07hswLLFiOLQnrmRJ5bXXbakWE/apzC4gkIQ8kwqgwQolDE41d6iY0oouZ4JWEy+Jo7mkFQEtBNX9xqCb6iK4LTEgDUWd1jwgvhOwEN5oRLeEohSxL+uE+rf5qW0/hUNztJDxk0MsZBkMC52WVyR+O406FxMLr0SLcAy+ZEipYjexqqx8TVszEqLkk0qhnMpDxUKPds5DOIszMJ33gqTuhbqdx1g4uNgcDrIhMfI4zgUmsmNnGITrNyUrCfXh+CQvvUDglnoabl5AXdWtOkXi+j8gcsglDxoChAbAAnYnohB6QNzPcAn5vvjlzJqOe8LbiuHTXl0J2fY/MGOfpZtwb7WaUYpDR6sZxYpyjEHKUaiG/RuZsuxcVbcBCAZ1rgPMIxUlIMh3YdclUvl+2wF+J0o3i1nS1Nl2WPyzggkfCD4cl5hZ1h0Jz8jSmaV8uxY5Pximc2Ja+espT8lM6XXM11Lrlik2lSNJkUmoiPP5kVphJxWWtGE41qETjtsyWQhZ8IjGIhgGe6QTq7xJCNBOfcNuDb8SoYRUcrZxzLjaMohh/J0c5kMcumVkPGG9+TQ2uQ0EPXD29a4nZt4W3EMfuMbyDt/6bDcGhEHBOpIKFx++Krn2kzDb/u5hHirBZihgwqCzt858IiYDPUWSgWVnQGq8oO2j6mzODYedfeDVNgDzERMDyGNvPVWprdJoR0KeKIA07sZqMq4VEMisbhDByJWcs04SCeGQl4CbyKpZjF2lVkKNTU1mBeEakU8QwSSrQZVXMnCeN/IgJ8iwaiVooEIRtID8JAeMfIHBnDLk/t5cQjRsAxS1HEJQdkN2cE7tvkdlDzJDK4ZZKBOTaTBI0kENQkuVKB5juoeOLd6gafZ/BFIdqod20U7DgLq4ekweDPHhj5nPlkkiGDCoCceGNRG/v5oZqN1S6aWezKp4FhRlhSSkuRQAhFcwAJB+2j4i/880QJ6G/eYD8UpkPdawPDZqKSorF+J/Z3Or480aLDzzn9+bbZOvpN3vdN9fVmoJB0AMPZ+guXCKMi5yvaBxXrWBJhtLxIecJNB3w10jCQ7LghWpIAQbn7nehV0vAINT+vKCBLKGzpSq8VoG308UtpGn5hcRM2JbAiCgtzf4W7RUlmR3sSnxLmMWmmHHZXklrbWfaCmW96TXrh7Kb32+k09uJM+nI6m70FTz9NyOuw9OYIF9A5Bm6TBrEM+Gsgfl3A0rVkHCT/j3UYg2ZFF8OTuFX2U9ZHYUJd7SwRAlfQyOFlayAgJWtHhh1CSdpBqfWlLkAl0q1H4gHUCQIv3xgslT350oVPz5hAw6N3nhPYvk/3MlB5X3Xcx6crP5TOlG2Xg89D4a2XVFzfVVL5sy6Kq42Ft4aAPUM5gDMohGBrHt8QPnSFyTOLU2QddLFTizMm6wx50bQZLVNNF2ybO6Vl5jTlccYakAZZaVQSTRNKBQX9lhqD8t2nM4ZDAU3zJy9wKELhkDtv6sK5ypmOYsG0+W3Sd9lZOGWekTZAjTExBSwulLUcKpBUmyHuqDyzQVV5RuNu7vG0rguG3DAzELl1r6xYw+TVZcjzhY82hwL8bCCsAJ9znA0ZKN/XearmwEH1s2IGuGzK9oeFdFKIHg+yy8Z+EaCSm7s5ZFVHFNPvMBPBPRBBO/jS7WtRgALmunwpLfVq9eoyP5Zu7JKp42iWvrrPmbqZcwPKWc7mguy/llV/UlueLm1qkW2WJUvTT54/q/0orjduksyJXCXtqKPfp8plp1YbZvQ0VZzC3bRPyJDdyJISeOaYuunAxifuLUhiOqPCacTwigp63lVGpi1lhSfxh6VtOU66OjXIGbJJs1A2hS00tcigMTXJ8GT0nsEZmuolFIIpyGOo5WVYZX2fzVKt71XJzlfJx4eMzmcGpccrEHMTLxVU3RjWVlCRMto2iGvJQAG9+3jn3W9zrYXOeqQKf9SsqBXyJW2l8juJXtxnKaPakzq0jrJ8gFuXuV4Gmj11nIkMg4/3f87Fq7hQuR0YAzEUcHAh6TpqPok8g73kWR6zC9eS6TTpxq0kZ8V64dppdd5RFSt41nLazIbLmi8VwCgtK9ZG5hBkx3lZfnFZnlEnLkUpdZpJtxc13XbaIQ7tYa0tXZm7985LEHJypFM24C6HKs2LNLawiS60LYbcfK/3IB7t71TdYRvJo7p2Qs7oi3WjgS3znnA3OVnMOBjVNDJXyHN8Ys708Un+pE5vHqbYT2JCESbwaqM0kkDxx87yQZgU+oyU0nsiA0ERp1SDCLW2AFZPFdKEDjiTJawaWOT4V7fqM3di6FsdrkQXN/Grq7QwfDLbPkQKjNHpYdKK4bFHKPPRrHKIbl35dJXbhYSt9jpLXorTphE+bYzX8mtmjzTM3YosLQrAeD5ksV9ZHSCxwWdzq1L6Z4ZLtBGaJSv9TjoQ33UJ2rJ4LPLYDZPymAnqREx0/zegao/i/8iY/zGBMv33OubrCObAYBVFjjQE6xsSP7ZUMAGIcAXpS2761wTpa7XJ/dMoWtBPRI1MAth0jnXuMk9dUSCH1vWC0pI8pW2oKdygZ+mMGypK1+oKM1vWPy85n7S3wF6Tw0uv8Fx5lPgzQf9Jv+YPm1n0TD0oTVI6XUoFCVWxayY32gzx87bCpfR41ilkfxbhFD7zFEEaVmdCj9O5wilTZu0//oc0nwLOuSydbGfDQKWp49ymhwniZJ5NLxWb1VwyvYCHP3Ulx9vUQtZXb13gRv1wYCNZuelYQuKn6iM0A1dR9OmnDznxVElLXB2EN8r5evXzrgNeL0QFcjoMPetNd3oO5KcU+fqpZFem81OGn0JYELn/29N9ngMTmRWec/M0vQvPz+Wn9bppD3nscPqtP4NdXzlrN3w4tuM3V53feqBwLywovz+9ReXiAuhJJAsVgtTvilB7gjFZkTGCmLjzwHYczoJBgy/1eFT0cepEdBTwt2DmEWAQevxgl+gg/ec6dbr2cVZ27aOAE91h+94J3dctCt33Ttg4uNwybni6KADLYBa4LX+slzDMVB4kZXOVRuBu96jv6RdWbmspkSrbC+E92z3Bkhexr1/h699XXH1L4Ot2WytGdsRUdm+pKhQo5HyhRi3gGmXAbVQ3how16eFHW3/Uvee3A2hKR3dujywUKBR8SG8QC7VGcYYiYVgdqq8rM27KfHfKt7KzJ7x2dBByfjn5Mu7Sb4LE6v6hyNo9o7E1E7uFrLRSim232QqJTNbdJkQEAYmFF5uC3c5Q917AfcAW2C+XpNdpO0sqyneUanPJqk1zQgfXOtrgbh5pqL9AUub3KhgkooLO46roIe0Ms3DSJGTQTQK5zCQAg98Vd6Komr1jMbXje4T5aWWUE5KrlrbCZgScK6Mea9tNUg40c81ja6vslxz8GvPKuUBnsWWsC7zeXFWtbqd0wL36iRgaeHDAaNZjkjfx4NQfAm7H0oRiBk2uYhYW9XRsK23bXJggO+h75bMjmom35t9TjTpOPQ6wLxh6sWLdgI9/cRn5ZIpUUKLRF9gt2jPUPHyuC94zd62i9NGT8cqHnfuSbdCTctXQxc+/t7fQO9za/nbqrNCxU1keoEfpM3MhjUjXPbCZyReyUHXeoM65YaHSa8HFukjo3xGV8USY0RuHB36LoWXu4oK28nR3YoexxVke2HpqucWGzcwl86hZ9DeWL9txGdvV0MA+n6k3H588cbNrauepLmpVaQi/Vqmo6tj3t/j2hIpGeQinJLz8fAfwf1v86VBHx6HD9zpOJO2I6W4eHN29ZWgUZF9fvLRJ/w1JT3qoB73ZUTn2xUub1L9EgcwdCV1TKQdERXH21pKSsD/Wy4fV6ws/a6PJJ999fKLhTu+BxKPtwa6SEE6FAIjti3aKk5IbbCdoX1DnvgioD+Kt8RQ6h0rPY9M2BPoWNtUVFtc0RFwq/5lofJz9tS7uirRjtuEZOZsmMU0zi9BmW7Q9t3YEnGwxZ8wLXub59baDdNNr8Zm/3jiYCBB/8Nlkxc2el4/d6p2o3NS4pXv8WXr5766KZxmVEOnx6hr7IqZsy5ZS59Ydze5Z7CGRXNYrE4PcTJkezilJJd+aih/2L5FoYWtqGyFfgZS9sJbniYv71yG4YUd9sUTk4oSNESq7o1+3rD/HE/OZ+RIOD+jI+ffW1bjay9dZHCQhjcFT5ZO0RaimyPPJ44V99Q3VPeWe5qIQJZ0ngplknSO8KeJCMpB1G8OM95rtzcAT9Nmv681mndZk0V13LLTtGoGSYVBTWCg9eyOfKgzlozlnrocELoCZ2wvOjtepkr3Tipre18N7XtfzXx0Abeh8I/jeAnKGdo4GJEqeljbWzKSz/35nee3jktXShqrp0iTe+9H14O+KhUbhTFy559YDceV/nNK2A82lho7oLcbnhu7YXlAADojv1N6RbAf+85h5S8Q8wIvF2z8/4HanFlQM/9vJMXdyNNbfgNVm8aQYPPyPp+00gz5fuUHZjSh16yd8A6rraaysdLW0zmxFKhy2HKW3PMVMiBVxJUp+RJt3g48skQIqtA8QyRZzNsPlHq2vbRsopRlzatbsl6uRIZVVJlVZh9RWoDkf3zkcYXbb/FTI2zwCgatGpuUWbraAcA3xV7TbDBU7+kHCt7RloK6+baQ8z5A5ILA8gDcYakw51fCQxiqTaqxDSmQS0KUjuwfj6juHw0vaOyo3NQO8VK4W1KC1hmB5I4xcZEj4XqwIBgQBuWWRXKdRC7EdqtQ0dJq8QdEwiUyKgBaEb+kvoRmyK9er4VsNnT0xRVtL/c1Il4zG4KvVAnuLMZPqbOuzlAmVZp5AbYREygIhX2kCh4HgK4Tj1mbmms3ltjQkvL+RA8FskG0mEjEROcScCEz59keCRyDO/E7m5g+G58elBfrNbsTeTk1GssQyWJgAjiEPkC/uPmx644NLKyqGvvbVbTk4knR/4OFN0pTsumS7v4aYB1sLtSxzSt7ZbBKLT8VZUvNeyibn86ix7LTe4lIdnohYygrrYN7EhrQioJn6TX5+dBXR3ExYTZBNGqxDBtt4YT8E+EtXtOv1NRYiraR9Td2jZTRTZlsa6JKq42GVVSrrI0Ca/RbXOlBCMxNrNujhK5a0gdKKVto8irw56Y0MaQxocoDNP+tSueu7IvBElg3roO+FGVnhQDXXmCDOzCrGRXwp0oZsjSnlFivUSpvag28M/ZOjt6phKAzWhv57zZRfrjboijWgC5m0KWwTyMTwFMGhZf9HsDckKMCTYUupiYAEjzayuSo56LyyXFQWFCk51rFcWRFkv7YMmg683qr8J+Lidjpb9Y+tEJ6fXtXFq7HPLm+E14XzW6vRangtaKo48fj4YmXrZfflcnxnZ79UNl8AtDFZfvKLn+S7axyDOwsyt36vGxTn5iWDo+3SDTIvRbvMW+oD0p1kiOIuYSmHIckM1Yfd1ELhEEp7M1wfmilmED8h3FOAXboqSRVwEDPF0WHDb8JitDgcw/7aEmbOEuGuwiKzKO4RPgHfd0qEwgJaij5NmhYZ+wpKIhDqaYnadHlaDBUUPVCbiI6aiI4Zp06sfwgFd944FXl8HhahssC/qud61E6jf6W6Uu7tMPuIrChLFVdlkUs1Fk55hNnqJyoyS70rQILOJGCrcxNWAmXC6qzCHS1lzq1V62wOKkRnssR8Rgpa9Jvs5rXU7HTl9mvONC5wRmorm4C2j0z6p0S2JJd+KZdOQ5Lpjbi2/7GHMGSSs3ubsqtvvsk2mXYYTWyWybjNbATviu9UVF8SXBJU3aoYhk5zTuOwY/5jZ1zOcC4cyNSjHBJHVZGkCLTXpOTw3pVgFflOuarn+xaEvdBtzkQUYAttiSxPyeaeE+NkoVaKDsZ1ILQKa40yVCftr9iflCjPACS4QWigpLgRj8Nj+/5dBTCWNb95b7pmcGSqenCYvlKNP3MpI725NeUA3cX0jB0dhrIajcACnZUF8+VEFkSlMUU5yhC+dH+hhKd1VYC/g/ODCwlImrj9181BWlCQ8Z1BX+DJP9fUeEaYnGtoOBemHrPadr/4mC2/S+0JCd8bE29is37n+aekaSg2KCGDKCVmANwcnv+CD6QcZAG/DIaQSQJBWa48NZ1P5VN54HVs4az32QWfzWe5QbWqAquhVNJL2x3IdFieNJPB9RAqNFyeTMLVFkA2+XsgNz4G31zxfn4Io97pFAipbIOsUyPncJOuJSX+pa3RVz8DZA53ru2AuB2gaklwZY7UWHTN1AngtfwOO1CVbJ4F5XBHrKafAJdWXH3BZeHBtROe44nJvUp3ciLQbCMERYYfCsaloPtO6SFx0TVrA2JfxqX0p6WApHeL8o9ddgTVDS3TpDb/71sOiqdgykgB/ZFk4aris6sBO84YTX/r8iCNksalljdXTJKeBfvufdWNMb6fFfN1VLSHGoPIY1exmCRIw0keAdRZRjRQYwSoxtQmbkrWK4Yo94la+V5yOonLIJN4TFvIwlXlfP4v6vwgnZLOzQsyJ9WlqzCZO+n9lh6TR5EIl/+ZWU4TQqIdw2C62bINZqKuvPfhBvR+LxHcafLdNSiAfQQw4ge1nRC3M/Aw6dMtT/XyUO0sVe+U51r8dznJ38bkxTC6MJ2dGqekbMTlMtK+SN8kpwSob2iVxe0ndxGNDkajNjovarb23e96aAg68TnSWj2ILHZONodTxL3+dEsoxMcMlTXKEY9VGd7zpufbJoFdyDZqv8nR5zzUml6deyn5oCJ8zp4HbNsIhIh+P45Tc6VOfFwnVK4cVnJse8Jaj7GxMBnQ5kkSQkWQZ65EeUpUigfXdYo4nX7HeuRyvDzVLVcR4Ok8HpOdQ6M9pvMKKDT2ID74ULEuiJpgoFSHFwR3mhWvi5qdFOqgtgAa+E7Hz9G1qTTC2KeRcZpG1XD4UCPzohbLsdQ8lnC80synf/4cn7n9Zu3e8X05yPPDGJQ0dipWQdm4KZdO+yI9Xk4JYK2blkmmukp8bUpuaRdQgyxcLXOnJsijtda+ZJMuUadCUBi9WpPjlDRjdJX8OiNFKVR+U5S8QnperZp86lRTo6ipZ5rYJuE0DyZKX+ZvNwIm+/zHN1tdmZaOhnOjLALL4eYoDteSxbwTPV5VXeOv99SQxdyTe92ruidBKWigFF/tF4SDTNyAyYEYladwdOAUDy2Kzpc3GkCWvkPx1vC4QZU7XTKiyikxP+UiB17NDVy0974atrXVbtu21V08G3BIqJC0yIXgCLLW6W4sRqrsGgJesp0TBtl5js7WespVJQwpZUyOVqjMkmeRPs3OPEGkCoRMwFNnN2WNviBD6zmzfGh4kVRbDWPlDKqsjP6Uegt+XImcaxLtl7LG1tEo5WYWSyvry4z9fqGo5LB5XVk99z9fmK5IYBWrPfZ7RpMeUfIeP/3Nv8ZspuszhCxtY2lbZ8WmVkAQK2BBbbi2IFjWqDXxSmm3GkrofLVRQXUwZdBgNEMkjnjsbSCwJCJGTLdnoSKzvJ+41qpa+5hddkylJr8bulgM6nAQLhU1uT/O5lW/E5iMjbLgLqgwSv6Y2GM4nVGxm0exC1Z51vSn8Gdx4HwXtuvKUsTIPOAQFoI7TQGzuo/+cXaaUcWbgIU6W99ZUhcNv/VDThrjRNubFfVYaX6JGB3833ogrkBmrCY2WdKNXVVNWmXC5s9HmzpSC8riaoeGBOWQ3jJWgIhFerNIXICA+3jWPSkEd5zIKIGseKeWpDli2VJpX+J0Jb12dp9jDLH5NXZf2r9/JQjexUsGUp1wUNrMN7GL92YxtNKKTz9eVZdTUc7IKGdUZFNUxOZQeFW1+uIpw0X+sAx5M9BciH2rP211ypek00PtuxskJK9ha/MsBUJkm2TmvNpSrNBgzZeKp8+oNmN1xAbMB2cp02+rY76isZgTQrI8a5OStuwlG0Zaczv2UIpPeFzfjqG0G4Lqd6hvi4wW490jymwT8iYQ0TZoqQMeOqQyta3aTTZwesjthtBv/WmeepkVyurn/DANRcBqLFEGyvq6mRutpjQ9tIYNibe5Y88WirqD5wQ5D/z3qJAFSfJZ4gRHKC8ubNtoGSLm+EX0+OkJTSPB7xg3HJP4y6YoVkzq4yDAnpaf1oekcf77yuRHh2ywrMNAu38Ndxyl9GVNq9H6XICDPttprd5ydW+pqleeawb8WtqWgcE32h/76S9Q4qMxOMdvwqD3P87jzqbn1hWGhBQBQg6OOsF2EUvPk3x2yvLM+niXq3d6doTcN45iCzH5aGMySuihUL18e5/2Q++aei9AOCmU7WTWOh5Vhc6e+4S1DUZoTpTPh/NonfUBh8XBpDPZwJ0mHTKZKtZzKSK+UjaHyuI2rzG9FHF5QpZ4WpyMhCUSdXsvCnIKrjsaQWZWWfLLKUZpfHRfVvj8S9vlUw2HxvCEroV5PW3ZNdNfE1CaRMkCmIsiG6uxHgmZkqNDJ/6jafCfP6CZ1+E1HCp85rwqmTeOHV1lIWQ8bGeZprenqrdUPb2G64U9oGkyeU3fU8yrnXBqhDE9zOTGs6ET1KYELIf7QfGa/5meljjknhKf21eHz1FKrRgMNhuHQW8i4NB7rvspm2M2ojE4Qsp7ws+s5NTZYa50xM9ZE3Maozdp9b5hb4ahf0FFroLMivp1+viibnjiomZRmsMv7K3k+nqcjdgepYJVDrvDSULal406ys52mpzbnOAwELCYdKYDcUzTpw8j4MWxeh1VG2rWvQtp3NOijqHhncLeANhF4b/gSxxJGknKVTupPNaYnHBKsPokRtnTPUg7jCLEAJIGjdk4ifVstN6vOFLaZlm8t2yIl9C232xlsVg1dslnDkHszYxaY4XsYmQdv6gxehNn74fqL9AmwH5o32pnTXT4XQI68jtA0ezUdYg4HYHnrGJtciH9iF7jdP+7HmPZLlA0jHQqivYug1QxPU+KPqUNJ3zDZuPIOSsTKh4dS/YAHotCB6FRwXjC6jvOKmPRGBwG7S4AhQNy/525sQ478YRK+ZJXuXLcg3MXTAmnNEjiSeFJwOxXnSv85wbREF+dWFbCoQYhO96wV2S1vMLJmDH0yxWNOuUKzcFcQ7+0Qkg/27PlHlS1M9DTi+oa4BP2eCmvqNfjUhZmVpUHaV6L5KryGkmEtPsUKZbBNVDq+zqUuNz3qjajRkkdoSV/mQSdKMdHbmkt18V0hSEmfYTzBZ33BZ0Ceeev4FCL/yz8jOPnWKaDXAV2+OG53inPnfm6sleN2m+IeiLed60bX/Q5wVMf9DmPbBmcGsuu3NvePyGsHBi3Dx3LboGT7epYFgKm7ttFfFdvNVgejjKSIYaqi4uKqFIHR+nu8PEcNKDqNqclwiLNbxuKBmF6LZL8Xb+bmZuK7TFPKQ0nKSYS0Wd3+OZ4107LSVhGeUuupUhxbhWDykeHe9lD20zB+pZXL72c3+kYxaJkjZeuXPYVbalNDdksbI80X/EoWXt2CawQ0eFjGmdfv7nUU5d7jtMVWgXZVlux9nfkI0UmwVXYhSqF52OU2fouRr2TOGi9TgXIh2e+MJU96Vmb5J3J7utyPj/yiEal6mzdqdy8ZOhvjjRXdwSwlWgwbeZPohLkk7nCWxNzyF1A+67Hpgtc6RbRWLJJXXWtjMC1quX8zHYz6EK+FULCqmi56rCvgBIUhi8Uf0UCgRa9iDdVlPJ5u9qtkes5XvEREh2lUmovZY0rXSed70EE8Rguw3HlFF8qyK00ukriUl7rL2JN5flcJpnDNig5zYeR5rIRtkjss3HUxfYuffj9h9LFyRP+C4x354MYlVYywCWSVEKq/fg+I9t9wSFOP49mJ2zcUS9utXHnB9abQDLmY9qWW6DbbghwmOCNUsieNoIRxx/nFVROtZQJddqgi4kvQJUveKnBhh4KviOBgb5UETCPjm35pTUX7IBTbBtQ76Wa3hX+OV6B9awWXLBT/fEeEu26rHTRiwdLhAVGG6kMdfbchrcy5EGdQ5uNZrSvTjr3OGAkxNmNlj8S5/zrnD4HV16YzcT9fR5y0duxQ0zmYXP6e6DIh0PJKEfdzMtvXrcrHx1ZIZehqNuaRqMn/WxVah8A04JZY1zg4VppqnwfIglypptut8TPDVrmfBxUbR07pAZXR5bf6ZY1y50facWRgNBhPQ8jpt6JSku+v/bnaVLl6dR6lhRZPS+TS65ZRVzfKE9xnihFvD0F7hEpzzWBvO3/gVLtO/8rlziEY39AoXYJWWJxPgE20QuQQoIIWJ3ysOUQQNUzs8beQCz9J5X/GjFYgHjYjy5M56TGKXM34vLotC/59KUBmZxOuIMv6Lgv6BDIOry2JKeAuFoll554LSRdz2ScL5KufsJKxTdJRPIipQ8U9V7psGPqfHANUG5SVlvDUL9Kb0l7JjwdeTFUBhVQTQQLRQIwHeHkaUt4QG1EbC2QNhYPdkVV9XcFbzZJbOK74jtsJ12EGERCa4GIySt+GURPIgqKwhmSg42TXU6klrBxr02CerEpd/cFhgyyZe0INtGoL85y5GCSkVlzqCE0fb+n1dBXHkMRQdZOwqelw+5DdaALKa5vmG7U1KxC9TCjRsocbDalagafUvwcoHyjwxf/QQ2/mIAr3MGMJsbEDiWTuXJh6i0u+pUg9O/w0U6z/RRB6s3qPRspg9gKOrC3k9JukiPmaZdpUY3mRccVLK8a9XJ2kN/8CyspvGBKMBpABvMiIJZXJte8okEF/feExrporiJm0SDHHrobi2nFiA43MhvFFwcppZU2goduul7bpNe7tTo6Tad1GtcOpDh40Rhfr/HfWwpeYUm1i33hiPKt5UuP++YR/cNSIP2EwuvZ7qApb4aEnnR1MrjzPFSgynvG65/W7srkUXiZjYlD3/HZqKgnpcMbvnJ+fL61Ycdv7iOfDEbUs1DEh/MLPQx+Lj+z4dv3nOi3SH9y6t76s6yKODSbMRH64VHtGz6MF8m5WaEfSOLnPnsyB/1h7zuz2mDt1Xlnnr9DgKr4elC7RmtrczntLbA26oGMpqWQqVqZgqojU2g6EPkSEebA3zJv4PTxRL/SO0Ad5iZlJqcEwxtTcn54RP7mQf5pjP1qJksDs3l6rYBfoAHrWx3dsrKhcBQfhrkCU8EAYuIlRXBS2bAgNVmQmiJITgUWN8a386YaH+WnAuWbyW+Z7wpIMuY2GZMkeFTwTnKegSeCDDw5lnoxKfFQdMy5hEQy0FwBJ2zCkVNTEoFGr6NHVjmiD+NrYY7AbBywmHlJEezUstQQfP9wqAXg+vWgNo3W1npRSNulBUnBXlWjWO1w/pqxppEiA9tniBbhjN6okh/monAGNf9gdvh2v5PJbJFBHIw/GcOB5LHN7/Zn8VVmFru4RxehCoPiUYFkTDwUFqHq1hXyd1nZQcDtA9OnvRn/F/f3BhX1dYe4KAtfQz5f4aifUTkw3KmFOSy9nsXW6vJpZzRnfPiqd3JuvCHxUXzi/aScdmN2Jj49EyQeQ1IfpSK/ZtxNRac6fyz6cfzR97bqrST168AqYPq1+4XF7efKtQO4l1QNM7Jtor8s8dAu2DLxRkeQ/ZVrDzibySKoieP39rMNqzM285MnpjoLRe+n5nqOZMbh+aQ8Kp/Ee5C6HBX5KCz0uYpq0slsCZ2WTaN7wpnnOJGax9xFLyaoqrREx0j6ey8mGpTUkwQsMiMGGx1DioklxaYwe9MvvWY2tFFYGE/J5+aQBbzLqnmqttiub7Kz5rcdGQJyHTBcdnkzL5XIxE8hHdyevGC+ar4vjvnMbb5S5hnzjJPCG3t5PR8SQRDvl1+PuB0BsUNaiUAi4D/+ebe4DXyx4/tton6uhwFMBBFFxzuO9+svb7m9Bawk5lejHPaHAD34BgQVVq6szC0K7fb3APoBBDsqgP4Zs3qk3QFmvgIv7LBgZWVxPgq9x8UNrwRWfQsSFVijIopUKgmwOShZmJeq1GqeWg1uL4rBwQtCf08oVVylEpAh4LqwIFVpNFwNDEx+xI6NhRdSORHi0i6jzH3/w9XcspqEyl0j/vVyi2MMKVLITDZIbLWDMt9djmKN9Gu9gmFrOfjxwL5DQU69Yt6kZiCtBz8ZGN+uS+CPVgrFVTWjB9BE7diBPQXx7NFKAQT2qOCVLa4n24x+e+NT7rVPAelrDwKxfXHDor2ZC36NBd9df9ezaMlY/mXAVwEVwMu9qlwFV6/8YB+p/sa0qN/1pA3wVDVlZTDdTEtjpbHMNDoENGrp0DCMtPQrVF6mVxEmCGpyKalUk9VbRCCWwtprg8vVgig/pVaMPh31P5ZcyVQcouD7iOMym87ybWnF1kQ4NcNpJv2sQdpGUdcW1Qn+tt9VUUua8R4zJ+DPm1dOBbrl/mPbAjSusJyPEylbxrYb2X/i8zlWrAL6/db7M0XcQgABrBuuqvOA6Y9n73u18zTJfwAh8Jrdcm0TLrdPBAWFwFYK59tvtw6dE6kXLqSmXLyIv6p9uvab5t6dcTj97zFKk++hC9ub3Qf734x/0N8nmszdmppe/2BeMGd9LVcDfhCHl/srG/idq8Dw1t2rer9dv3s97be3Rq/q7g6587xz5MZXDbAevOR7B1AjLvDFg3TL5L0vH0Tn/1EU3DSVmkf0pCxbUB7hbREbszdGYHoOXj6MnY8BSgnbAXf3Ov/Mv5AY1a7KC44n5ZFlcuk5/O0NTDCtfcdhIoIrx6hywQN23HwFfyl2yNEPeg/sPN4cUVOKElQLFXU9kymp6UWxRc01nq4VvZE7cvFiac2NBxCXpedrAvedi7+9rHmk22AYNpHopc2j9bXtgyU0K9G+bvILId7/JPiOEGBipG2qNLNNOJbcn6Ws9cNL2TJNoxMhzaxJTGbPnEpx26lu31xNY2M70rpoQlkYn2g0E7nMKi6zhMcmU7isxLcTBXGaE/JczYzSAyT5+MqQNDZfDAVLFdIJZKJOUbdWlo8669qtHPMQk0tHJh+eJ7cZYxRO8vkR0HCxorlrNZH6pTgLAX3aoDyq/tDWNSqmk5LCbBojT3tU5r0BXciUTf42dL/Cf2LJO2WZug+dliP0dIrFPFG+aOv3ujZxHjV5orCSl/KA8/+JiU4pN+R0Y9lD8jABJ3sC9r4xpEcUsO+Nnwp018W/YcC0vaH/Qg/swcz/E87XQ1oI4nu7ewCdmeTp35taQT9735qOIXKFANINEsEFkpHLjfH4dyZ9YASPTxx76fd8neq+wSgcHyW1LLdwtyliRMaAO0IQhqDdaR1XEsgqirar5HZHqI5CUV/pv4+GqOLMEtR6Q7C0NEoyOX7wF0WB7xkDCsjCk/IIspKcJjQ3AKg1KhV2rkBuP6Sw8QSyQpqC9n84g0Wl0znD6SywDQdTRox0QPVKf50FQszjLURqbvKwXWhXQUqGVLWnFVzh+KOzLiw1r2TdGrtBbIwgZu6e9MsM5QYCtxVxsVBVfjGQwuOQKJCoRgqRsTlJ2rieuFiX69sUE9cNbmOX1INjmUBFsJiy+YIoyvtKcpg/Fywuypdhv4AYqpFAlCm6uKaOzcym2GkVoDZwyKuwSbqDf2lKZ6F7aEpLFVrOBSgkP0mLKVgkRDfIeiQeixGilOSFEwhq3mrm2cjIPTjBAwKlstbWssAnCuRuCDA3I9PDTqK4VxiOjftk1UxnfjPKygjL9iC8nPoMq+civPQmQDUJyzhVNyARQ27dzNwcCuasgP5gzb69z8pLx0sHwV2AMM3c2ma2tLaakZaWt173FR7O8AtNlyaSknLLcwhWZrkujZhryEW/BiJRLspsd4OZCxDnCIc1VIOGvU0OEKh7YJxj6tRityabuhzQvxlSxJFHrliJoOWhjPwbylJ4vrSD5XWgzT5fqTwlSWWXzCkTwtxpr3F6Gtkc9ba7XL8g6Ixn2+7mIO0pmmaTfZx3t/ng/tixnlx0MArZXgMf3HcYdZ4ipeaQ7AE59PfP5fg+puc/5ydUxSsyionepfUFGqtBIS44RTGWOt7GeDC/j/b+WIkSjl24F0q4vF7czsFM420tq2bj33+zvON/6LpwKj79Xm+TzuU6iaNfmdzR/icpOpFD1joUl7bkG32d16EvJPnqQgMi2YXNnpiDKP+C3gymUo93LFuHnvzJedsYXUaVFLTjKiWklMnyUW4BaaGRAd6l+YnksiQy+Ytw2KNGTe4/sTncTfLkMt8DrQ009CYne83f0JI1YcwHjw230usmum6GdwlQ7dVMNZCM0P5PC6grlcoZJVizEL9gTVwAxF20iGwiMQLDc8fCBfg4kLDjA3Ddk+AHhNz+FubX+fFN0GEf2PxgXx3sd9tJVV+7zpW+e5toxcWawkbeDmTd5b9RkCl2Kr/KiN1iToSb3+RZagGsJI3MYJbiqHSEvhkMAovh2mIFa6jKJmMo1UIyaiKlKe5S4h4ZtPV6zIVyDoKq0xz7wlRcb3H7ImNTHJ16NpnqxZwk1otF49tBQIF/bjkrNpD+3yCNex3w+UNei8m3Mb6Yd2tE6z6AQXz/LIXg3bUCcnYP2T6Q/NUCHiAXBFkQZGtuNWS8i+ntn1jnWJzdiauHcYoZpBjkxVRXknehOXQ2O3e/3EG9nmGRac4u3ZnH3O405pXBgtIZCUgVm/4rHDv3neGjwD0s9VOd3kqTqX357Ye+0peKUJgW/c9ZyjcSHG2pGuoDfseMXyreVUwsQB7jfc3jRa6OvPZSUu1DcpUeZYKkak1aKWb1fxWQGyEL1mxj1r7SG92pG3IEtQjADGYzMVvXitnlaIYx41yv+s3J2+EOkuPy0/lpARgsismx92Jke9wPs2z7g9QApnmRVOaQ8jDtbFsVv90xI6TOJD/hsuIuIeaqM110NJ/bUlplZnWMYi8vNs6r+zl2sVnHjIpLtabi5css0o11wrnFiJupeVreWYaJ5KSwJp176Hw0mSExa063Q0nIbtbEsC1fkBr5bV36e+3X+qJInoVTKynIQfbEAJCf3eNI26u3bDlxdlGYcNLM9YhxrIlr7+aWluRZgBxwkCFZIDvyA4c0EStvxrPpLE0LegwGWE+BNHPGAJQLu8CPzPhVX7iyixjppXdmrnDaCpLGjoy2YhrXqSPugT1gH/gD/vd768xY7e7OATUFpjLNhKoSmWgqVFdlbLlJ2nJuQoyAZW9stJhaihbwLTIgdsB+1gAG0AxFBkQV4gIAVCILsck7xzxa1XTALvBTFSGbFSu2hwWxX36/OyP9uJm3Uj9O+duh/en0ARAjuOPQipn3BzGEX/iB20n3cEhFZYvZZY2WkR9okwH2RK1eZfJFiHLqKlKOdiH+zR4a3HqwnfTSUKsUsz6f4kw3lcMtKY523Cpjj05xRKnKuow7FqNEdTFK70ol1yglSbZJOx6jDwj0EdvQHj9ltjfFm/f59S03vJRUjjRY73IKm4i2jynvWDKtKetDydoBKc/NP8EFbYo9oTu6gscFLTnTYxJzp9LbufyWnLpIOn0ctOsz6lDM3ZpG/8PMGu9Dh2dRvU9KLhtI4+5xG1YfZfov4CRcnm3ZCn/uLx0zzLcprTFSzSfKwvxUYy2yY7NyLEkXhFrPmQaPow4O+7LvGoeKYuqlkE70mNGyjBxuytnFSAZkYpqqkRI/ynASHsUlhZDNOMA4RZsGDfUYqb6dVkfJZ+BWIwuJts8ZJRmH/yks3dVPFwL5SxgJgAICAUcAYHszIQAO2g0BBNkIBFAkfIMAGuWMQgAdhGwMAXTJZlcIoIeQQyGAPt4QIIAB6raEAIZkKisEMKa2/hDAhPoxCQFMfbzdOGZU77Y45pPoibZcgNirfYmAdfc+joB0n8DU5N4Uamu/Fg7qSAK4FYwcCMNlB4Zs1oHDs+TAk4oHJYgTAQeRVDIdEoKbcpBoz9ghSWkxDmmWK3PIcNhjh2ye3XoHmemdloOC3EvaUp5Mr+AlEMChe3kHAvrd8fIvNO4WOjd3Jy6jVLEkIUeYfbv2nHg6lvfh5jI9WkpCGhOrlDimzfCn66zKdjiiOivjRt2OzCfE5Xt9kNQ2UAgQlGZUnpLnFGlagjKDWAexIVtdbWL3owZ3K/GvDsACIoaTk5gRz0uQvAIsRsbl8uaDXjrAHCd4CrJAX1J9AfZt2336UF3oibVNXDxM5amyP0zFIOeNepHAuKexDFJ5pEDOMAJwmV/ZpVFTaoakVKBlinmZmCa5k+XCMxgmdqpLcUW3tcI4NDu5R+V4XUmlZLxKiAZ9moh6YzGti+GAiLNBwG6AJq7qJCaSRUcxJzcKmteSRVoItAiQI6TknNsBq3xQaZwpG8vJsaIA1/iCyrhlQl9mfQ8UZ2S4hvC08G3uoA8pSSDEzdW4EgG3g8xiMVkIpuHOdXvJMoe55O1IiiU42YwNKaDt1MQ2CbIHH0aup6Bx4hAGDPH4oDB9SYuvFYTQesNNgeEziCzCHhMTJK+gE3kzU2ENYhNYZTW6vYc7V2QFGU8Sbqwo6RQ6bIW6bAS6SA73SoltvmDDnQ0XI527qRAuOLegObe2EcYp7ysyl9XLzjaghQmS19XtFiIVUC190krHGhn6lCRi9jRRJz2lMGxro9dqrKqio36oF7ep5Z927K4aKg5fSBTGFc96t8o4Iu9SXH53R4Z4Wbp9yJLuYSxWoVvxxk+YtBdDDfvyy1hqF+rOpbhGwbURSC9WFxP/RwwSdWxSF5QBullPn1oZURiHxo/OALntHGbuLjQd5CKmQ4Q7ngwsQl6pEFrsg4iqhCIjn710njAckhuoXkn+nkPbDEw9dtY6AH/KZTL6T/2TTl3etteyDEP6HXdEnyWpdvnDvNyQyJxxX5z2xIJFR5z2iQ7N7BLvFPlumy8e6fZEpy5vhPihR6+zXP4rdNuAQaF++SBTGWmloGjvVXuuZDkeuRf8lGSt8sIrL3120FavvbXdzz4ChcvhEB4REBFJIBL88TeSSApJIxlYCpoiMpJDCFHIU0BFswydIiXKVKj23Y8wqGFajkWdBk1aVtC2sp+GY9PBoUvPKlz6DBjiMeqF6YyZMGUm29+jyJoNW3adcz57bBxcPHwCQiJQv/z20qtcSUjJyCkoqahlMLxqOnoGBYxMzCxdScAqh1KvZsxqsHVFMildmkuJUmXKVTRiSp6vvpNGTRQlodI+1KglN+DfwmYtWrVpr05Ve221zQEpUqVJr0UrsQvuatPunhS3HXTG/TK67kYHu5pIpizZcuTKk69AoSLFShxSqky5CpWqyqlajVp16h12xFHHHNfghJNOOV15ZzRWqcInxxWpdkK+Eodk1WfObtilztNWyRk/neJuutUFF11y2RVJFa+W+zo88NAjjz3RqUu3NPsX36ffgEFDho0YNSaPY//nxk2YNKX5aS/RjKDr3njrXfiDj9DzuS/NyeCoLNoX349+9qvf/elvSxgCzdp5Px4SdfHY1hN02iFKrjiSDghCRBlUpGUaYhSKxsBaeq5gSp5INr15IxswTnWp6E+sTmimcXQ0fXcfBJwfI2nFxnF7i+V5vBVJFRf4YhrsXADCjfL3fNFJkbNw6lSJJtnROqW45SyWDbpJPAd5v13ooW2zHKQZIr3MqtJRexlRZYk4952eFchBIs6rAAxlbMUclFUgh35cFSkiq5YNhtmAgeqDMA5r1y5jF20fs3qB3+pLX7SXZFIEn3E61KRaPUL0ZNaujVdUZicI80Ts1mXuYu1z1gZhEYJ1+WJRNU4uQOOkFTPbtjGlZRDioD4qqhI3hY3sS7vnlnavhUvVebXQvxWqZTFcvJAiNhnnna+sYeN841QF4XbBrvP4LbKq1hqPMkDO+nHdRqKjMxHz5cVkaeOlmrOGVK3LIs3NXWJn6qTpgGR7C2umkXA59+oJiwhWFp1xfBSqDoOPP+Aj0Oow+NUDPOxdpjbXcDNMFVoMWKZtKhMHxc0EQA8ZInSH2+Jk4EfNEQNTRT3j0wGHjd7k6vzijOJKkLZZFWyHWHW4KloQwrvvqhnY0MSustnEwioIN5581T1Rx1ij+J1ICGidolfs0X65oogLG3dq7TnfVjp7taimbGywsRrGhkBzaduT6GusLhqluZe28ugyo1pRYmJL2YC5BGBKQGYSiKYEwCSQdpRAyy9+0AEn1RSNpS/jfkY8OcAMG0egVTI94kZbRPwiUJmcOO/FcJ6Hi4rFlKwi68gdEiV+APLFxATljVXmJZj1nkNv/479OO5b/6eKKe99yZENJzlW6tnHVHfuSoarII2MyR1TomWjjcp1NIG+DAoNPqXCAKH6xDtWBZmGyifTUO3k/n7v59XPTkVI+snanS107v7CbR6Xn10UDzQQ7+s9AcnP78hIdTefThahfiP1HMYM/sHwDnUrAKUV6LYKqU9woBLBzf0vBMAlCUtcmnDW/Q8DoIwX/DFdmVqyBAZAOcOMMoZnwIgfYBgwI2vMiCdgGAiDv7QLQJYAACIAIwAzAgAAeCYCMBEACMDMYn6F4EocsE9snDY54sXTRtTDISBGmuui0yHhANbNRzveS9t+euO5xXzKk8ayzSIvAnsG2DCPRm5Ti5s09hiiP1gQUOV3InGLfGpgIH07XrAmLiboZMWNX8Q699OmhD1mmyAf9Z+M0J7YZx++6HziExFvGtSnJWbMNo4DuEkd8iHdxQoPfSzoUd23P0K6Vmkmdb4SsLt4L36TPjxTLAAA) format('woff2')}@font-face{font-family:'Familjen Grotesk';font-weight:700;font-display:swap;src:url(data:font/woff2;base64,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) format('woff2')}-:root{--bg:#0d1117;--surface:#161b22;--elevated:#1c2128;--overlay:#21262d;--border:#30363d;--text:#e6edf3;--text-2:#8b949e;--text-3:#484f58;--accent:#00b8b0;--accent-bright:#00e6dc;--purple:#8c6bf7;--blue:#58a6ff;--green:#3fb950;--red:#f85149;--yellow:#d29922}-.tools button svg,.vp .tools button svg{width:15px;height:15px;display:block}-- :root{--bg:#0d1117;--panel:#11161f;--panel2:#161c27;--border:#222b38;--text:#e6edf3;--muted:#8b949e;--accent:#37c8a6}- *{box-sizing:border-box;margin:0;padding:0}html,body{height:100%}- body{background:var(--bg);color:var(--text);font-family:'Satoshi','Segoe UI',sans-serif;display:flex;flex-direction:column;overflow:hidden}- header{display:flex;align-items:center;gap:9px;padding:9px 14px;border-bottom:1px solid var(--border);flex:0 0 auto}- header .dot{width:9px;height:9px;border-radius:50%;background:var(--accent);box-shadow:0 0 10px var(--accent)}- header .b{font-weight:700;font-size:14px;font-family:'Familjen Grotesk',sans-serif}header .b span{color:var(--muted);font-weight:400}- header .nm{margin-left:auto;font-weight:600;font-size:13px;color:#cfd8e2}- main{flex:1;display:flex;min-height:0}- .vpwrap{flex:1;position:relative;min-width:0}- .vp{position:absolute;inset:0;overflow:hidden;cursor:grab;background:radial-gradient(circle at 50% 40%,#121a26 0%,#0d1117 70%)}- .vp.grab{cursor:grabbing}- .vp svg{position:absolute;left:0;top:0;width:100%;height:100%;transform-origin:0 0;display:block}- .tools{position:absolute;left:10px;top:10px;display:flex;gap:6px;z-index:5}- .tools button{width:30px;height:30px;border-radius:8px;border:1px solid var(--border);background:var(--panel2);color:#cfd8e2;font-size:16px;cursor:pointer;display:grid;place-items:center}- .tools button:hover{border-color:var(--accent);color:var(--accent)}- .hint{position:absolute;right:10px;bottom:9px;color:var(--muted);font-size:11px;z-index:5;pointer-events:none}- aside{flex:0 0 248px;border-left:1px solid var(--border);background:var(--panel);display:flex;flex-direction:column;min-height:0}- aside .ah{padding:9px 12px;border-bottom:1px solid var(--border);font-size:12px;color:var(--muted)}- .layers{padding:6px}- .lyr{display:flex;align-items:center;gap:9px;padding:8px 9px;border-radius:8px;cursor:pointer}- .lyr:hover{background:var(--panel2)}- .lyr .sw{width:13px;height:13px;border-radius:3px;border:1px solid rgba(255,255,255,.15);flex:0 0 13px}- .lyr .sw.ring{background:transparent!important}- .lyr .ln{flex:1;font-size:13px}- .lyr .meta{font-size:10px;color:var(--muted)}- .lyr .chk{width:16px;height:16px;border-radius:4px;border:1.5px solid #3a4757;display:grid;place-items:center;color:#0b0f17;font-size:11px}- .lyr.on .chk{background:var(--accent);border-color:var(--accent)}- .lyr.off .ln,.lyr.off .sw{opacity:.4}- .lyr.special{background:linear-gradient(100deg,rgba(55,200,166,.10),transparent);border:1px solid rgba(55,200,166,.25);margin-top:4px}- .lyr.special .ln{color:#7ef0d2;font-weight:600}- .note{font-size:11px;color:var(--muted);line-height:1.45;padding:8px 11px}- .note b{color:#7ef0d2}+/*__AV_CSS__*/+:root{--bg:#0d1117;--panel:#11161f;--border:#222b38;--text:#e6edf3;--muted:#8b949e;--accent:#37c8a6}+*{box-sizing:border-box;margin:0;padding:0}html,body{height:100%}+body{background:var(--bg);color:var(--text);font-family:'Satoshi','Segoe UI',system-ui,sans-serif;display:flex;flex-direction:column;overflow:hidden}+header{display:flex;align-items:center;gap:9px;padding:9px 14px;border-bottom:1px solid var(--border);flex:0 0 auto}+header .dot{width:9px;height:9px;border-radius:50%;background:var(--accent);box-shadow:0 0 10px var(--accent)}+header .b{font-weight:700;font-size:14px}header .b span{color:var(--muted);font-weight:400}+header .nm{margin-left:auto;font-weight:600;font-size:13px;color:#cfd8e2}+main{flex:1;display:flex;min-height:0}+.vpwrap{flex:1;position:relative;min-width:0}+aside{flex:0 0 248px;border-left:1px solid var(--border);background:var(--panel);display:flex;flex-direction:column;min-height:0}+aside .ah{padding:9px 12px;border-bottom:1px solid var(--border);font-size:12px;color:var(--muted)}+.layers{padding:6px}+.note{font-size:11px;color:var(--muted);line-height:1.45;padding:8px 11px}+.note b{color:#7ef0d2} </style></head> <body> <header><span class="dot"></span><span class="b">adom-footprint <span>· interactive</span></span><span class="nm">__NAME__</span></header> <main> <div class="vpwrap">- <div class="vp" id="vp">- <div class="tools"><button id="zin" title="Zoom in"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="10.5" cy="10.5" r="6.5"/><path d="M20 20l-4.5-4.5M10.5 8v5M8 10.5h5"/></svg></button><button id="zout" title="Zoom out"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="10.5" cy="10.5" r="6.5"/><path d="M20 20l-4.5-4.5M8 10.5h5"/></svg></button><button id="fit" title="Fit"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 9V5a1 1 0 0 1 1-1h4M20 9V5a1 1 0 0 0-1-1h-4M4 15v4a1 1 0 0 0 1 1h4M20 15v4a1 1 0 0 1-1 1h-4"/></svg></button></div>- <div class="hint">drag to pan · scroll to zoom</div>+ <div class="av-vp" id="vp"> __SVG__ </div> </div> <aside> <div class="ah">Layers</div> <div class="layers" id="layers"></div>- <div class="note"><b>InstaPCB</b> jets paste dot-by-dot - those 300µm positions travel with the footprint. Off by default; toggle it on.</div>+ <div class="note">The <b>solder paste jet</b> layer shows the 300µm paste-dispensing dots that travel with the footprint. Off by default — toggle it on.</div> </aside> </main>+<script>/*__AV_JS__*/</script> <script> var META=__META__; (function(){- var vp=document.getElementById('vp'), svg=vp.querySelector('svg');- if(svg){ svg.removeAttribute('width'); svg.removeAttribute('height'); }- var scale=1,tx=0,ty=0;- function apply(){ if(svg) svg.style.transform='translate('+tx+'px,'+ty+'px) scale('+scale+')'; }- function zoomAt(cx,cy,f){ tx=cx-(cx-tx)*f; ty=cy-(cy-ty)*f; scale*=f; apply(); }- function fit(){ scale=1;tx=0;ty=0;apply(); }- vp.addEventListener('wheel',function(e){ e.preventDefault(); var r=vp.getBoundingClientRect(); zoomAt(e.clientX-r.left,e.clientY-r.top,e.deltaY<0?1.12:1/1.12); },{passive:false});- var drag=false,lx=0,ly=0;- vp.addEventListener('pointerdown',function(e){ drag=true;lx=e.clientX;ly=e.clientY;vp.classList.add('grab');vp.setPointerCapture(e.pointerId); });- vp.addEventListener('pointermove',function(e){ if(!drag)return; tx+=e.clientX-lx;ty+=e.clientY-ly;lx=e.clientX;ly=e.clientY;apply(); });- vp.addEventListener('pointerup',function(){ drag=false;vp.classList.remove('grab'); });- document.getElementById('zin').onclick=function(){ var r=vp.getBoundingClientRect();zoomAt(r.width/2,r.height/2,1.2); };- document.getElementById('zout').onclick=function(){ var r=vp.getBoundingClientRect();zoomAt(r.width/2,r.height/2,1/1.2); };- document.getElementById('fit').onclick=fit;- // layers- var DEFS=[- {key:'copper',name:'Copper',kind:'color',color:'#C83434',sw:'#C83434'},- {key:'assembly',name:'Assembly (F.Fab)',kind:'color',color:'#C872AB',sw:'#C872AB'},- {key:'courtyard',name:'Courtyard',kind:'color',color:'#F2EDA1',sw:'#F2EDA1',ring:true},- {key:'paste-ap',name:'Paste apertures',kind:'group',sel:'.adom-layer-paste-apertures',sw:'#8aa0b8',ring:true},- {key:'paste-dots',name:'InstaPCB paste dots',kind:'group',sel:'.adom-layer-paste-dots',sw:'#37c8a6',special:true}- ];- function cmatch(el,hex){ hex=hex.toUpperCase(); var a=((el.getAttribute('stroke')||'')+'|'+(el.getAttribute('fill')||'')+'|'+(el.getAttribute('style')||'')).toUpperCase(); return a.indexOf(hex)>=0; }- function applyLayer(def,on){ if(!svg)return;- if(def.kind==='group'){ var g=svg.querySelectorAll(def.sel); for(var i=0;i<g.length;i++)g[i].style.display=on?'':'none'; }- else { var els=svg.querySelectorAll('path,rect,circle,polygon,line'); for(var j=0;j<els.length;j++){ if(cmatch(els[j],def.color)) els[j].style.display=on?'':'none'; } } }- var box=document.getElementById('layers'), state={};- DEFS.forEach(function(def){- var on = def.special ? false : true; state[def.key]=on;- var meta = def.key==='paste-dots' ? ((META.pasteDots||0)+' @ 300µm') : def.key==='paste-ap' ? ((META.pasteApertures||0)+' openings') : '';- var row=document.createElement('div'); row.className='lyr '+(on?'on':'off')+(def.special?' special':'');- row.innerHTML='<span class="sw'+(def.ring?' ring':'')+'" style="'+(def.ring?('border-color:'+def.sw):('background:'+def.sw))+'"></span>'- +'<span class="ln">'+def.name+'</span>'+(meta?'<span class="meta">'+meta+'</span>':'')+'<span class="chk">'+(on?'✓':'')+'</span>';- row.onclick=function(){ var nv=!row.classList.contains('on'); row.classList.toggle('on',nv); row.classList.toggle('off',!nv); row.querySelector('.chk').textContent=nv?'✓':''; state[def.key]=nv; applyLayer(def,nv); };- box.appendChild(row); applyLayer(def,on);- });- apply();+ var vp=document.getElementById('vp');+ var svg=vp.querySelector('svg');+ var ctl=AV.initViewport(vp);+ AV.addToolbar(vp,ctl);+ AV.buildLayers({ svg:svg, container:document.getElementById('layers'), state:{}, meta:META }); })(); </script> </body></html>
src/fp_gen.rs+114−17@@ -12,6 +12,12 @@ pub struct Pad { pub shape: Option<String>, pub angle: Option<f64>, pub layers: Option<String>,+ /// Mount type: `smd` (default) | `thru_hole` | `np_thru_hole`. Drives the pad+ /// token and the default copper/mask layers.+ pub mount: Option<String>,+ /// Drill diameter (mm) for thru-hole pads. Emits `(drill D)` — without it a+ /// thru-hole pad is meaningless and adom-lbr can only make an EAGLE `<smd>`.+ pub drill: Option<f64>, } pub struct Rect {@@ -28,6 +34,10 @@ pub struct FpInput { pub courtyard: Option<Rect>, pub description: String, pub tags: String,+ /// Body/silk outline shape: `rect` (default) | `circle`. For round parts,+ /// `circle` emits `fp_circle` on F.Fab + F.SilkS (real round silk) instead of+ /// a bounding-box rectangle — adom-lbr transcribes it to an EAGLE `<circle>`.+ pub body_shape: Option<String>, } /// Print an f64 the way JS `${x}` / Number.toString does (shortest round-trip,@@ -156,21 +166,43 @@ pub fn generate_kicad_mod(input: &FpInput) -> String { s += " (stroke (width 0.05) (type default)) (fill none) (layer \"F.CrtYd\"))\n"; } + // For round parts, the body/fab/silk outline is a circle, not a rect — so a+ // machine pin / round part carries real round silk (adom-lbr → EAGLE <circle>)+ // instead of a bounding box. Centre + radius derive from the body rect.+ let round = input.body_shape.as_deref() == Some("circle");+ let circle_of = |b: &Rect, grow: f64| -> (f64, f64, f64) {+ let (cx, cy) = ((b.x1 + b.x2) / 2.0, (b.y1 + b.y2) / 2.0);+ let r = (b.x2 - b.x1).abs().max((b.y2 - b.y1).abs()) / 2.0 + grow;+ (cx, cy, r)+ };+ // Fab outline if let Some(b) = &input.body {- s += &format!(" (fp_rect (start {} {}) (end {} {})\n", n(b.x1), n(b.y1), n(b.x2), n(b.y2));- s += " (stroke (width 0.1) (type default)) (fill none) (layer \"F.Fab\"))\n";+ if round {+ let (cx, cy, r) = circle_of(b, 0.0);+ s += &format!(" (fp_circle (center {} {}) (end {} {})\n", n(cx), n(cy), n(cx + r), n(cy));+ s += " (stroke (width 0.1) (type default)) (fill none) (layer \"F.Fab\"))\n";+ } else {+ s += &format!(" (fp_rect (start {} {}) (end {} {})\n", n(b.x1), n(b.y1), n(b.x2), n(b.y2));+ s += " (stroke (width 0.1) (type default)) (fill none) (layer \"F.Fab\"))\n";+ } } // Silkscreen outline (slightly outside fab) if let Some(b) = &input.body {- let m = 0.12;- s += &format!(" (fp_rect (start {} {}) (end {} {})\n", n(b.x1 - m), n(b.y1 - m), n(b.x2 + m), n(b.y2 + m));- s += " (stroke (width 0.12) (type default)) (fill none) (layer \"F.SilkS\"))\n";+ if round {+ let (cx, cy, r) = circle_of(b, 0.12);+ s += &format!(" (fp_circle (center {} {}) (end {} {})\n", n(cx), n(cy), n(cx + r), n(cy));+ s += " (stroke (width 0.12) (type default)) (fill none) (layer \"F.SilkS\"))\n";+ } else {+ let m = 0.12;+ s += &format!(" (fp_rect (start {} {}) (end {} {})\n", n(b.x1 - m), n(b.y1 - m), n(b.x2 + m), n(b.y2 + m));+ s += " (stroke (width 0.12) (type default)) (fill none) (layer \"F.SilkS\"))\n";+ } } - // Pin 1 marker- if !input.pads.is_empty() {+ // Pin 1 marker — a corner chevron only makes sense for a rectangular body.+ if !input.pads.is_empty() && !round { if let Some(b) = &input.body { s += &format!(" (fp_line (start {} {}) (end {} {})\n", n(b.x1 - 0.3), n(b.y1 - 0.3), n(b.x1 + 0.3), n(b.y1 - 0.3)); s += " (stroke (width 0.12) (type default)) (layer \"F.SilkS\"))\n";@@ -202,13 +234,25 @@ pub fn generate_kicad_mod(input: &FpInput) -> String { // Pads for pad in &input.pads {+ let mount = pad.mount.clone().unwrap_or_else(|| "smd".to_string());+ let is_th = mount == "thru_hole" || mount == "np_thru_hole"; let shape = pad.shape.clone().unwrap_or_else(|| "roundrect".to_string());- let layers = pad.layers.clone().unwrap_or_else(|| "\"F.Cu\" \"F.Paste\" \"F.Mask\"".to_string());+ // THT pads plate through every copper layer + mask (no paste); SMD pads+ // are front copper + paste + mask.+ let layers = pad.layers.clone().unwrap_or_else(|| {+ if is_th { "\"*.Cu\" \"*.Mask\"".to_string() } else { "\"F.Cu\" \"F.Paste\" \"F.Mask\"".to_string() }+ }); let angle = match pad.angle { Some(a) if a != 0.0 => format!(" {}", n(a)), _ => String::new(), };- s += &format!(" (pad \"{}\" smd {} (at {} {}{}) (size {} {})\n", pad.number, shape, n(pad.x), n(pad.y), angle, n(pad.dx), n(pad.dy));+ // Emit the drill right after size, e.g. (drill 1.1) — required for a THT+ // pad to be real; without it adom-lbr can only make an EAGLE <smd>.+ let drill = match pad.drill {+ Some(d) if d > 0.0 => format!(" (drill {})", n(d)),+ _ => String::new(),+ };+ s += &format!(" (pad \"{}\" {} {} (at {} {}{}) (size {} {}){}\n", pad.number, mount, shape, n(pad.x), n(pad.y), angle, n(pad.dx), n(pad.dy), drill); s += &format!(" (layers {})", layers); if shape == "roundrect" { s += " (roundrect_rratio 0.25)";@@ -256,13 +300,14 @@ mod tests { let input = FpInput { name: "R_0402_1005Metric".into(), pads: vec![- Pad { number: "1".into(), x: -0.485, y: 0.0, dx: 0.6, dy: 0.7, shape: Some("roundrect".into()), angle: None, layers: None },- Pad { number: "2".into(), x: 0.485, y: 0.0, dx: 0.6, dy: 0.7, shape: Some("roundrect".into()), angle: None, layers: None },+ Pad { number: "1".into(), x: -0.485, y: 0.0, dx: 0.6, dy: 0.7, shape: Some("roundrect".into()), angle: None, layers: None, mount: None, drill: None },+ Pad { number: "2".into(), x: 0.485, y: 0.0, dx: 0.6, dy: 0.7, shape: Some("roundrect".into()), angle: None, layers: None, mount: None, drill: None }, ], body: Some(Rect { x1: -0.5, y1: -0.27, x2: 0.5, y2: 0.27 }), courtyard: Some(Rect { x1: -0.93, y1: -0.47, x2: 0.93, y2: 0.47 }), description: "0402 (1005 metric) resistor".into(), tags: "resistor 0402".into(),+ body_shape: None, }; let got = generate_kicad_mod(&input); let golden = include_str!("../tests/golden/R_0402.kicad_mod");@@ -279,15 +324,16 @@ mod tests { name: "LQFP_TEST".into(), pads: vec![ // bottom-row pad: outer edge at y = 5.9 + 1.5/2 = 6.65- Pad { number: "1".into(), x: -3.5, y: 5.9, dx: 0.3, dy: 1.5, shape: Some("roundrect".into()), angle: None, layers: None },+ Pad { number: "1".into(), x: -3.5, y: 5.9, dx: 0.3, dy: 1.5, shape: Some("roundrect".into()), angle: None, layers: None, mount: None, drill: None }, // top-row pad: outer edge at y = -5.9 - 1.5/2 = -6.65- Pad { number: "2".into(), x: 3.5, y: -5.9, dx: 0.3, dy: 1.5, shape: Some("roundrect".into()), angle: None, layers: None },+ Pad { number: "2".into(), x: 3.5, y: -5.9, dx: 0.3, dy: 1.5, shape: Some("roundrect".into()), angle: None, layers: None, mount: None, drill: None }, ], // Deliberately too-tight: courtyard/body only ±5.0 while pads reach ±6.65. body: Some(Rect { x1: -5.0, y1: -5.0, x2: 5.0, y2: 5.0 }), courtyard: Some(Rect { x1: -5.0, y1: -5.0, x2: 5.0, y2: 5.0 }), description: String::new(), tags: String::new(),+ body_shape: None, }; let got = generate_kicad_mod(&input); // pad outer extent ±6.65, +1 mm margin -> ±7.65@@ -305,13 +351,14 @@ mod tests { let big = FpInput { name: "LQFP".into(), pads: vec![- Pad { number: "1".into(), x: -3.5, y: 5.9, dx: 0.3, dy: 1.5, shape: None, angle: None, layers: None },- Pad { number: "2".into(), x: 3.5, y: -5.9, dx: 0.3, dy: 1.5, shape: None, angle: None, layers: None },+ Pad { number: "1".into(), x: -3.5, y: 5.9, dx: 0.3, dy: 1.5, shape: None, angle: None, layers: None, mount: None, drill: None },+ Pad { number: "2".into(), x: 3.5, y: -5.9, dx: 0.3, dy: 1.5, shape: None, angle: None, layers: None, mount: None, drill: None }, ], body: Some(Rect { x1: -5.0, y1: -5.0, x2: 5.0, y2: 5.0 }), courtyard: Some(Rect { x1: -5.0, y1: -5.0, x2: 5.0, y2: 5.0 }), description: String::new(), tags: String::new(),+ body_shape: None, }; let tp = text_placement(&big); assert_eq!((tp.reference.x, tp.value.x), (0.0, 0.0), "centered on X");@@ -328,16 +375,66 @@ mod tests { let tiny = FpInput { name: "R_0402".into(), pads: vec![- Pad { number: "1".into(), x: -0.485, y: 0.0, dx: 0.6, dy: 0.7, shape: None, angle: None, layers: None },- Pad { number: "2".into(), x: 0.485, y: 0.0, dx: 0.6, dy: 0.7, shape: None, angle: None, layers: None },+ Pad { number: "1".into(), x: -0.485, y: 0.0, dx: 0.6, dy: 0.7, shape: None, angle: None, layers: None, mount: None, drill: None },+ Pad { number: "2".into(), x: 0.485, y: 0.0, dx: 0.6, dy: 0.7, shape: None, angle: None, layers: None, mount: None, drill: None }, ], body: Some(Rect { x1: -0.5, y1: -0.27, x2: 0.5, y2: 0.27 }), courtyard: Some(Rect { x1: -0.93, y1: -0.47, x2: 0.93, y2: 0.47 }), description: String::new(), tags: String::new(),+ body_shape: None, }; let tt = text_placement(&tiny); assert!(tt.reference.size < 1.0, "tiny part scales text below 1 mm, got {}", tt.reference.size); assert!(tt.reference.y <= tt.courtyard.top && tt.value.y >= tt.courtyard.bottom, "invariant holds for tiny part"); }++ #[test]+ fn thru_hole_pad_emits_drill_and_all_copper() {+ // The machine-pin case from issue #102: a round Ø1.6 pad with a 1.1 mm+ // drill must come out as a real thru_hole pad with (drill) + *.Cu *.Mask.+ let input = FpInput {+ name: "MachinePin".into(),+ pads: vec![Pad {+ number: "1".into(), x: 0.0, y: 0.0, dx: 1.6, dy: 1.6,+ shape: Some("circle".into()), angle: None, layers: None,+ mount: Some("thru_hole".into()), drill: Some(1.1),+ }],+ body: Some(Rect { x1: -1.0, y1: -1.0, x2: 1.0, y2: 1.0 }),+ courtyard: Some(Rect { x1: -1.2, y1: -1.2, x2: 1.2, y2: 1.2 }),+ description: String::new(), tags: String::new(),+ body_shape: Some("circle".into()),+ };+ let km = generate_kicad_mod(&input);+ assert!(km.contains("(pad \"1\" thru_hole circle (at 0 0) (size 1.6 1.6) (drill 1.1)"),+ "thru-hole pad must carry mount + drill, got:\n{km}");+ assert!(km.contains("(layers \"*.Cu\" \"*.Mask\")"), "THT pad plates all copper, got:\n{km}");+ // and NOT an smd pad+ assert!(!km.contains("(pad \"1\" smd"), "must not fall back to smd, got:\n{km}");+ }++ #[test]+ fn round_body_emits_fp_circle_silk_not_rect() {+ // Round parts get real round silk (fp_circle) on F.SilkS + F.Fab, not a box.+ let input = FpInput {+ name: "RoundPart".into(),+ pads: vec![Pad {+ number: "1".into(), x: 0.0, y: 0.0, dx: 1.6, dy: 1.6,+ shape: Some("circle".into()), angle: None, layers: None,+ mount: Some("thru_hole".into()), drill: Some(1.1),+ }],+ body: Some(Rect { x1: -1.0, y1: -1.0, x2: 1.0, y2: 1.0 }),+ courtyard: None,+ description: String::new(), tags: String::new(),+ body_shape: Some("circle".into()),+ };+ let km = generate_kicad_mod(&input);+ assert!(km.contains("(fp_circle (center 0 0)") && km.contains("(layer \"F.SilkS\"))"),+ "round part must emit fp_circle silk, got:\n{km}");+ assert!(km.contains("(layer \"F.Fab\"))"), "round part must emit fp_circle fab, got:\n{km}");+ // silk radius (1.0 + 0.12) sits just outside the fab radius (1.0)+ assert!(km.contains("(center 0 0) (end 1.12 0)"), "silk circle grows 0.12 beyond body, got:\n{km}");+ // no rectangular silk/fab box for a round part+ assert!(!km.contains("fp_rect") || input.courtyard.is_some(), "no fab/silk rect for round part, got:\n{km}");+ } }
src/fp_render.rs+39@@ -280,6 +280,16 @@ pub fn render(fp: &Footprint) -> (String, f64, f64) { } s.push_str("</g>"); + // ── Heatsink / thermal vias (auto-generated under an exposed thermal pad) ──+ // The array an EE would otherwise hand-draw: a copper ring + drilled hole per+ // via, at IPC-typical pitch, tiled across the exposed pad. Rendered on top of+ // the pad copper so it reads as real thermal relief.+ s.push_str(r##"<g class="adom-layer-heatsink">"##);+ for (vx, vy) in heatsink_vias(&fp.pads) {+ let _ = write!(s, r##"<circle cx="{:.3}" cy="{:.3}" r="0.30" fill="#8a5a2b" stroke="#e8a13a" stroke-width="0.05"/><circle cx="{:.3}" cy="{:.3}" r="0.15" fill="#0d1117"/>"##, vx, vy, vx, vy);+ }+ s.push_str("</g>");+ // ── Drill holes (dark with grey ring) ── s.push_str(r##"<g class="adom-layer-drill">"##); for p in &fp.pads {@@ -313,6 +323,35 @@ pub fn render(fp: &Footprint) -> (String, f64, f64) { (s, 0.0, 0.0) } +/// Auto-generate a thermal-via grid under the exposed pad — the "heatsink" an EE+/// would otherwise hand-draw. Finds the exposed pad (the largest ~square SMD pad+/// ≥1.8 mm on a side) and tiles it with vias at IPC-typical 1.2 mm pitch, inset so+/// each 0.6 mm via ring stays inside the pad. Returns via centres in footprint mm;+/// empty when there is no exposed pad (so most parts show no heatsink layer).+fn heatsink_vias(pads: &[Pad]) -> Vec<(f64, f64)> {+ let ep = pads.iter()+ .filter(|p| p.mount == "smd" && p.w.min(p.h) >= 1.8 && {+ let a = p.w / p.h.max(0.001);+ (0.6..=1.67).contains(&a)+ })+ .max_by(|a, b| (a.w * a.h).partial_cmp(&(b.w * b.h)).unwrap_or(std::cmp::Ordering::Equal));+ let ep = match ep { Some(p) => p, None => return Vec::new() };+ let pitch = 1.2_f64;+ let margin = 0.6 / 2.0 + 0.15; // via-ring radius + clearance from the pad edge+ let span = |dim: f64| (dim - 2.0 * margin).max(0.0);+ let nx = ((span(ep.w) / pitch).floor() as i32 + 1).max(1);+ let ny = ((span(ep.h) / pitch).floor() as i32 + 1).max(1);+ let x0 = ep.x - (nx as f64 - 1.0) * pitch / 2.0;+ let y0 = ep.y - (ny as f64 - 1.0) * pitch / 2.0;+ let mut out = Vec::with_capacity((nx * ny) as usize);+ for j in 0..ny {+ for i in 0..nx {+ out.push((x0 + i as f64 * pitch, y0 + j as f64 * pitch));+ }+ }+ out+}+ fn poly_d(pts: &[(f64, f64)]) -> String { let mut d = String::new(); for (i, &(x, y)) in pts.iter().enumerate() {
src/main.rs+35−11@@ -135,6 +135,10 @@ struct CreateInput { description: String, #[serde(default)] tags: String,+ /// Body/silk outline shape — accept `bodyShape`, `body_shape`, or `shape`+ /// ("circle" for round parts → fp_circle silk/fab instead of a box).+ #[serde(default, rename = "bodyShape", alias = "body_shape", alias = "shape")]+ body_shape: Option<String>, } #[derive(serde::Deserialize)]@@ -150,6 +154,12 @@ struct JsonPad { angle: Option<f64>, #[serde(default)] layers: Option<String>,+ /// Mount type — accept `mount`, `type`, or `padType` (smd | thru_hole | np_thru_hole).+ #[serde(default, alias = "type", alias = "padType")]+ mount: Option<String>,+ /// Drill diameter (mm) — accept `drill`, `drillDiameter`, or `hole`.+ #[serde(default, alias = "drillDiameter", alias = "hole")]+ drill: Option<f64>, } #[derive(serde::Deserialize)]@@ -167,24 +177,32 @@ fn main() { } } -fn cmd_embed(file: PathBuf, out: Option<PathBuf>) -> Result<()> {- let content = fs::read_to_string(&file).with_context(|| format!("reading {}", file.display()))?;- let name = first_footprint_name(&content).unwrap_or_else(|| "footprint".to_string());- // The embed IS the InstaPCB layer panel — paste overlay belongs here.- let svg = render::render_footprint_svg_with_paste(&content, bbox_from_mod(&content))?;- let aps = paste::paste_apertures(&content);+/// Build the standalone interactive embed HTML for one footprint — the lean,+/// self-contained viewer driven by the SHARED viewer core (same as the live app).+/// Reused by both the `embed` CLI and the live server's `/embed` route.+pub fn build_embed_html(kicad_mod: &str, name: &str) -> Result<String> {+ let svg = render::render_footprint_svg_with_paste(kicad_mod, bbox_from_mod(kicad_mod))?;+ let aps = paste::paste_apertures(kicad_mod); let dot_count = paste::paste_overlay_svg(&aps, 0.0, 0.0).2; let meta = serde_json::json!({ "pasteApertures": aps.len(), "pasteDots": dot_count });- let html = include_str!("embed.html")- .replace("__NAME__", &esc(&name))+ Ok(include_str!("embed.html")+ .replace("/*__AV_CSS__*/", include_str!("viewer_core.css"))+ .replace("/*__AV_JS__*/", include_str!("viewer_core.js"))+ .replace("__NAME__", &esc(name)) .replace("__SVG__", &svg)- .replace("__META__", &meta.to_string());+ .replace("__META__", &meta.to_string()))+}++fn cmd_embed(file: PathBuf, out: Option<PathBuf>) -> Result<()> {+ let content = fs::read_to_string(&file).with_context(|| format!("reading {}", file.display()))?;+ let name = first_footprint_name(&content).unwrap_or_else(|| "footprint".to_string());+ let html = build_embed_html(&content, &name)?; let out = out.unwrap_or_else(|| { let folder = file.parent().map(|p| p.to_path_buf()).unwrap_or_else(|| PathBuf::from(".")); folder.join(format!("{name}-embed.html")) }); fs::write(&out, &html).with_context(|| format!("writing {}", out.display()))?;- println!("OK: interactive embed ({} pads, {} paste dots) → {}", content.matches("(pad ").count(), dot_count, out.display());+ println!("OK: interactive embed ({} pads) → {}", content.matches("(pad ").count(), out.display()); Ok(()) } @@ -252,12 +270,15 @@ fn to_input(c: &CreateInput) -> fp_gen::FpInput { shape: p.shape.clone(), angle: p.angle, layers: p.layers.clone(),+ mount: p.mount.clone(),+ drill: p.drill, }) .collect(), body: c.body.as_ref().map(|r| fp_gen::Rect { x1: r.x1, y1: r.y1, x2: r.x2, y2: r.y2 }), courtyard: c.courtyard.as_ref().map(|r| fp_gen::Rect { x1: r.x1, y1: r.y1, x2: r.x2, y2: r.y2 }), description: c.description.clone(), tags: c.tags.clone(),+ body_shape: c.body_shape.clone(), } } @@ -329,6 +350,8 @@ fn fpinput_from_ds2sf(v: &serde_json::Value) -> Result<fp_gen::FpInput> { shape: p.get("shape").and_then(|x| x.as_str()).map(String::from), angle: p.get("rotation").and_then(|x| x.as_f64()).or_else(|| p.get("angle").and_then(|x| x.as_f64())), layers: None,+ mount: p.get("mount").or_else(|| p.get("type")).and_then(|x| x.as_str()).map(String::from),+ drill: p.get("drill").or_else(|| p.get("drillDiameter")).or_else(|| p.get("hole")).and_then(|x| x.as_f64()), }); } if pads.is_empty() {@@ -356,6 +379,7 @@ fn fpinput_from_ds2sf(v: &serde_json::Value) -> Result<fp_gen::FpInput> { courtyard, description: v.get("description").and_then(|x| x.as_str()).unwrap_or("").to_string(), tags: String::new(),+ body_shape: v.get("bodyShape").or_else(|| v.get("shape")).and_then(|x| x.as_str()).map(String::from), }) } @@ -523,7 +547,7 @@ header{{display:flex;align-items:center;gap:10px;padding:14px 22px;border-bottom .svgbox{{margin-top:16px;border:1px solid #222b38;border-radius:10px;background:#0d1117;overflow:hidden}}.svgbox svg{{display:block;width:100%;height:auto;max-height:64vh}}footer{{text-align:center;color:#5b6675;font-size:11px;padding:10px}}</style></head>\ <body><header><span class=\"dot\"></span><span class=\"brand\">adom-footprint <span>· PCB footprint viewer</span></span></header>\ <div class=\"wrap\"><div class=\"card\"><div class=\"name\">{t}</div>{m}{d}<div class=\"svgbox\">{svg}</div></div></div>\-<footer>rendered via service-kicad — standalone, no local KiCad</footer></body></html>",+<footer>rendered locally in Rust — no KiCad, no service round-trip</footer></body></html>", t = esc(name), m = if meta.is_empty() { String::new() } else { format!("<div class=\"meta\">{}</div>", esc(&meta)) }, d = if desc.is_empty() { String::new() } else { format!("<div class=\"desc\">{}</div>", esc(desc)) },
src/paste.rs+43−7@@ -1,12 +1,14 @@-//! InstaPCB solder-paste **dispensing positions** — an Adom-computed overlay.+//! Solder-paste **dispensing positions** — an Adom-computed overlay. //! //! Standard PCB CAD shows a solid F.Paste aperture (the stencil opening). Adom's-//! InstaPCB line doesn't use a stencil — it *jets* solder paste dot-by-dot. So a-//! footprint in the Adom ecosystem also carries the actual dispensing positions:-//! a grid of ~300 µm paste dots tiled across each aperture. This module parses-//! the pads from a `.kicad_mod`, computes those dots, and emits two SVG layer-//! groups (aperture outlines + dispensing dots) in the same page coordinates the-//! footprint is rendered at, so they overlay exactly.+//! paste-jet process doesn't use a stencil — it *jets* solder paste dot-by-dot. So+//! a footprint in the Adom ecosystem also carries the actual dispensing positions:+//! a grid of ~300 µm paste dots tiled across each aperture — and kept CLEAR of any+//! thermal via, so jetted paste never sits over a via barrel (it would wick down+//! the hole during reflow, starving the joint and causing voids). This module+//! parses the pads from a `.kicad_mod`, computes those dots, and emits two SVG+//! layer groups (aperture outlines + dispensing dots) in the same page coordinates+//! the footprint is rendered at, so they overlay exactly. use regex::Regex; @@ -23,6 +25,30 @@ const DOT_DIA: f64 = 0.30; // 300 µm jetted paste dot const DOT_PITCH: f64 = 0.45; // center-to-center spacing of dispensing positions const MARGIN: f64 = 0.06; // keep dots just inside the aperture edge const MAX_DOTS: usize = 600; // safety cap so a huge thermal pad can't explode the DOM+/// Keep-out radius (mm) from a thermal-via centre — paste dots inside this are+/// dropped so jetted paste never lands over a via barrel. ≈ via-ring radius (0.30)+/// + dot radius (0.15) + a little clearance.+const VIA_KEEPOUT: f64 = 0.55;++/// Detect the exposed thermal pad among the paste apertures and return its+/// auto-generated thermal-via centres (footprint-local mm). MUST mirror+/// `fp_render::heatsink_vias` so the paste keep-out lines up with the rendered vias.+fn heatsink_vias(aps: &[Aperture]) -> Vec<(f64, f64)> {+ let ep = aps.iter()+ .filter(|a| a.w.min(a.h) >= 1.8 && { let r = a.w / a.h.max(0.001); (0.6..=1.67).contains(&r) })+ .max_by(|a, b| (a.w * a.h).partial_cmp(&(b.w * b.h)).unwrap_or(std::cmp::Ordering::Equal));+ let ep = match ep { Some(p) => p, None => return Vec::new() };+ let pitch = 1.2_f64;+ let margin = 0.6 / 2.0 + 0.15;+ let span = |dim: f64| (dim - 2.0 * margin).max(0.0);+ let nx = ((span(ep.w) / pitch).floor() as i32 + 1).max(1);+ let ny = ((span(ep.h) / pitch).floor() as i32 + 1).max(1);+ let x0 = ep.cx - (nx as f64 - 1.0) * pitch / 2.0;+ let y0 = ep.cy - (ny as f64 - 1.0) * pitch / 2.0;+ let mut out = Vec::new();+ for j in 0..ny { for i in 0..nx { out.push((x0 + i as f64 * pitch, y0 + j as f64 * pitch)); } }+ out+} /// Parse SMD pad apertures (pads that get solder paste) from a `.kicad_mod`. pub fn paste_apertures(kicad_mod: &str) -> Vec<Aperture> {@@ -78,6 +104,9 @@ pub fn paste_overlay_svg(aps: &[Aperture], ox: f64, oy: f64) -> (String, String, let mut apert = String::from(r#"<g class="adom-layer-paste-apertures">"#); let mut dots = String::from(r#"<g class="adom-layer-paste-dots">"#); let mut n = 0usize;+ // Thermal-via centres on the exposed pad — paste dots that land on one are+ // dropped so jetted paste doesn't wick down the barrel during reflow.+ let vias = heatsink_vias(aps); for ap in aps { let (px, py) = (ox + ap.cx, oy + ap.cy);@@ -107,6 +136,13 @@ pub fn paste_overlay_svg(aps: &[Aperture], ox: f64, oy: f64) -> (String, String, // local offset within aperture (centered), then rotate. let lx = -span_x / 2.0 + ix as f64 * DOT_PITCH; let ly = -span_y / 2.0 + iy as f64 * DOT_PITCH;+ // Footprint-local dot centre (before the page offset) — used to test+ // against the thermal vias, which are in footprint-local coords.+ let local_dx = ap.cx + lx * cos - ly * sin;+ let local_dy = ap.cy + lx * sin + ly * cos;+ if vias.iter().any(|&(vx, vy)| ((local_dx - vx).powi(2) + (local_dy - vy).powi(2)).sqrt() < VIA_KEEPOUT) {+ continue; // paste dot would sit over a via — skip it+ } let dx = px + lx * cos - ly * sin; let dy = py + lx * sin + ly * cos; dots.push_str(&format!(
src/server.rs+31−1@@ -38,6 +38,16 @@ static CONSOLE: Mutex<Option<Vec<String>>> = Mutex::new(None); static EVAL_ID: AtomicU64 = AtomicU64::new(1); const APP_HTML: &str = include_str!("app.html");+const AV_CSS: &str = include_str!("viewer_core.css");+const AV_JS: &str = include_str!("viewer_core.js");++/// The app HTML with the shared viewer core (window.AV + av- CSS) injected — the+/// SAME core the static embed uses, so the two viewers can't drift.+fn app_html() -> String {+ APP_HTML+ .replace("/*__AV_CSS__*/", AV_CSS)+ .replace("/*__AV_JS__*/", AV_JS)+} const FAVICON: &str = r##"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#ffffff" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><rect x="6.5" y="9" width="3" height="6" rx="0.6"/><rect x="14.5" y="9" width="3" height="6" rx="0.6"/><path d="M6 6.2h1.2"/></svg>"##; pub fn set_state(s: AppState) {@@ -296,7 +306,27 @@ fn hd_post(body: &str) { fn route(method: &Method, url: &str, body: &str) -> Response<std::io::Cursor<Vec<u8>>> { match (method, url) {- (Method::Get, "/") | (Method::Get, "/index.html") => html(APP_HTML),+ (Method::Get, "/") | (Method::Get, "/index.html") => html(&app_html()),+ // The standalone interactive embed for the currently-loaded footprint —+ // opened by the app's "Embed" button (new tab), the same lean viewer the+ // `embed` CLI writes to a file.+ (Method::Get, "/embed") => {+ let (name, km) = {+ let g = STATE.lock().unwrap();+ match g.as_ref() {+ Some(s) if !s.kicad_mod.is_empty() => (s.footprint_name.clone(), s.kicad_mod.clone()),+ _ => (String::new(), String::new()),+ }+ };+ if km.is_empty() {+ html("<!doctype html><meta charset=utf-8><body style=\"background:#0d1117;color:#8b949e;font-family:sans-serif;padding:24px\">No footprint loaded to embed.</body>")+ } else {+ match crate::build_embed_html(&km, &name) {+ Ok(h) => html(&h),+ Err(e) => { let _ = e; html("<!doctype html><body>embed render failed</body>") }+ }+ }+ } (Method::Get, "/favicon.svg") => svg(FAVICON), (Method::Get, "/state") => json_resp(&state_json()), // Left library rail: list every chip in the chip-fetcher library, mirror its
src/viewer_core.cssadded+32@@ -0,0 +1,32 @@+/* ── adom-footprint shared viewer core (CSS) ───────────────────────────────+ Included VERBATIM by both the live app (app.html) and the static embed+ (embed.html) so the canvas, zoom toolbar and layer-panel rows look identical+ and cannot drift. Self-contained literal colours (no dependency on either+ file's CSS vars). Class prefix: av- */++.av-vp{position:absolute;inset:0;overflow:hidden;cursor:grab;background:radial-gradient(circle at 50% 42%,#121a26 0%,#0d1117 72%)}+.av-vp.grab{cursor:grabbing}+/* DIRECT child only — the zoom-toolbar button icons (.av-tools svg) also live+ inside .av-vp; without '>' they'd inherit position:absolute and stack at 0,0. */+.av-vp > svg{position:absolute;left:0;top:0;width:100%;height:100%;transform-origin:0 0;display:block}++/* zoom toolbar */+.av-tools{position:absolute;left:10px;top:10px;display:flex;gap:6px;z-index:5}+.av-tools button{width:30px;height:30px;border-radius:8px;border:1px solid #222b38;background:#161c27;color:#cfd8e2;cursor:pointer;display:grid;place-items:center}+.av-tools button svg{width:15px;height:15px;display:block}+.av-tools button:hover{border-color:#37c8a6;color:#37c8a6}+.av-tools button:focus,.av-tools button:focus-visible{outline:none}+.av-panhint{position:absolute;right:10px;bottom:9px;color:#8b949e;font-size:11px;z-index:5;pointer-events:none}++/* layer panel rows */+.av-lrow{display:flex;align-items:center;gap:9px;padding:8px 9px;border-radius:8px;cursor:pointer}+.av-lrow:hover{background:#161c27}+.av-lrow .sw{width:13px;height:13px;border-radius:3px;border:1px solid rgba(255,255,255,.15);flex:0 0 13px}+.av-lrow .sw.ring{background:transparent!important}+.av-lrow .ln{flex:1;font-size:13px}+.av-lrow .meta{font-size:10px;color:#8b949e}+.av-lrow .chk{width:16px;height:16px;border-radius:4px;border:1.5px solid #3a4757;display:grid;place-items:center;color:#0b0f17;font-size:11px}+.av-lrow.on .chk{background:#37c8a6;border-color:#37c8a6}+.av-lrow.off .ln,.av-lrow.off .sw{opacity:.4}+.av-lrow.special{background:linear-gradient(100deg,rgba(55,200,166,.10),transparent);border:1px solid rgba(55,200,166,.25);margin-top:4px}+.av-lrow.special .ln{color:#7ef0d2;font-weight:600}
src/viewer_core.jsadded+130@@ -0,0 +1,130 @@+/* ── adom-footprint shared viewer core (window.AV) ─────────────────────────+ Single source of truth for the layer list, toggle behaviour, defaults,+ pan/zoom and toolbar icons. Included VERBATIM by both app.html (live) and+ embed.html (static) so the two viewers cannot drift. Pure client-side; works+ on any SVG that carries the <g class="adom-layer-*"> groups. */+(function(){+ var ICON_ZIN='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="10.5" cy="10.5" r="6.5"/><path d="M20 20l-4.5-4.5M10.5 8v5M8 10.5h5"/></svg>';+ var ICON_ZOUT='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="10.5" cy="10.5" r="6.5"/><path d="M20 20l-4.5-4.5M8 10.5h5"/></svg>';+ // Zoom-to-FIT = a magnifier with a small square inside (fit content to view).+ // Deliberately NOT the four-corner expand/"fullscreen" glyph.+ var ICON_FIT='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="10.5" cy="10.5" r="6.5"/><path d="M20 20l-4.5-4.5"/><rect x="7.6" y="7.6" width="5.8" height="5.8" rx="1.2"/></svg>';++ // The canonical layer list. Every layer is a <g class="adom-layer-*"> in the+ // self-rendered SVG, so toggling is group-based (robust) — never colour-match.+ // `kind:'group'` on every entry: all layers are toggled by their <g> group+ // (robust), never by colour-match. (app.html still reads def.kind.)+ var LAYER_DEFS=[+ {key:'copper', name:'Copper', kind:'group', sel:'.adom-layer-copper', sw:'#C83434'},+ // Auto-generated thermal-via array under the exposed pad — only present (and+ // listed) when the footprint has an exposed thermal pad.+ {key:'heatsink', name:'Heatsink vias', kind:'group', sel:'.adom-layer-heatsink', sw:'#e8a13a'},+ {key:'padnum', name:'Pad numbers', kind:'group', sel:'.adom-layer-padnum', sw:'#ffffff', ring:true},+ {key:'silk', name:'Silkscreen', kind:'group', sel:'.adom-layer-silk', sw:'#00cccc'},+ {key:'fab', name:'Fab (F.Fab)', kind:'group', sel:'.adom-layer-fab', sw:'#6e7681', ring:true},+ {key:'courtyard', name:'Courtyard', kind:'group', sel:'.adom-layer-courtyard', sw:'#FF26E2', ring:true},+ {key:'drill', name:'Drill', kind:'group', sel:'.adom-layer-drill', sw:'#888888', ring:true},+ {key:'paste-ap', name:'Paste apertures', kind:'group', sel:'.adom-layer-paste-apertures', sw:'#8aa0b8', ring:true},+ // Generic name — the 300µm solder-paste dispensing dots. Off by default.+ {key:'paste-dots',name:'Solder paste jet', kind:'group', sel:'.adom-layer-paste-dots', sw:'#37c8a6', special:true},+ ];++ // True only if the group actually holds drawn geometry (not an empty <g>) — so+ // layers with nothing in THIS footprint aren't listed as dead, dim rows.+ function groupHasGeo(svg,def){+ if(!svg) return false; var n=0,g=svg.querySelectorAll(def.sel);+ for(var i=0;i<g.length;i++){ n+=g[i].querySelectorAll('path,rect,circle,line,polygon,polyline,text').length; }+ return n>0;+ }+ function layerPresent(svg,def){ return groupHasGeo(svg,def); }+ function applyLayer(svg,def,on){+ if(!svg) return; var g=svg.querySelectorAll(def.sel);+ for(var i=0;i<g.length;i++) g[i].style.display=on?'':'none';+ }++ // Build layer rows into a container. opts:+ // svg, container, state {key:bool}, meta {pasteDots,pasteApertures},+ // onToggle(key,on). Missing state key -> special OFF, others ON. Skips empty+ // non-special groups. Returns the keys actually shown.+ function buildLayers(opts){+ var svg=opts.svg, box=opts.container, state=opts.state||{}, meta=opts.meta||{}, shown=[];+ box.innerHTML='';+ LAYER_DEFS.forEach(function(def){+ if(!def.special && !groupHasGeo(svg,def)) return;+ var on = def.special ? (state[def.key]===true) : (state[def.key]!==false);+ var m = def.key==='paste-dots' ? ((meta.pasteDots||0)+' @ 300µm')+ : def.key==='paste-ap' ? ((meta.pasteApertures||0)+' openings') : '';+ var row=document.createElement('div');+ row.className='av-lrow '+(on?'on':'off')+(def.special?' special':'');+ row.innerHTML='<span class="sw'+(def.ring?' ring':'')+'" style="'+(def.ring?('border-color:'+def.sw):('background:'+def.sw))+'"></span>'+ +'<span class="ln">'+def.name+'</span>'+(m?'<span class="meta">'+m+'</span>':'')+ +'<span class="chk">'+(on?'✓':'')+'</span>';+ row.onclick=function(){+ var nv=!row.classList.contains('on');+ row.classList.toggle('on',nv); row.classList.toggle('off',!nv);+ row.querySelector('.chk').textContent=nv?'✓':'';+ state[def.key]=nv; applyLayer(svg,def,nv);+ if(opts.onToggle) opts.onToggle(def.key,nv);+ };+ box.appendChild(row); applyLayer(svg,def,on); shown.push(def.key);+ });+ return shown;+ }++ // Wire pan + zoom on a viewport element (the box that contains the <svg>).+ // Returns a controller {fit, zoomIn, zoomOut, apply}.+ function initViewport(vp){+ var svg=vp.querySelector('svg');+ if(svg){ svg.removeAttribute('width'); svg.removeAttribute('height'); }+ var s=1,tx=0,ty=0;+ function apply(){ var el=vp.querySelector('svg'); if(el) el.style.transform='translate('+tx+'px,'+ty+'px) scale('+s+')'; }+ function zoomAt(cx,cy,f){ tx=cx-(cx-tx)*f; ty=cy-(cy-ty)*f; s*=f; apply(); }+ function fit(){ s=1;tx=0;ty=0;apply(); }+ // Smooth, delta-PROPORTIONAL zoom (matches adom-symbol). A fixed per-event+ // factor makes the trackpad leap — one gentle two-finger nudge fires a burst+ // of tiny wheel events and a fixed 1.12× compounds. Instead derive the factor+ // from how much actually scrolled (exp → smooth + reversible), normalise+ // device units, and clamp one event so a chunky mouse notch can't jump.+ var ZOOM_K=0.0022; // sensitivity: lower = gentler+ vp.addEventListener('wheel',function(e){+ e.preventDefault();+ var d=e.deltaY;+ if(e.deltaMode===1) d*=16; else if(e.deltaMode===2) d*=vp.clientHeight;+ d=Math.max(-50,Math.min(50,d));+ var r=vp.getBoundingClientRect();+ zoomAt(e.clientX-r.left,e.clientY-r.top,Math.exp(-d*ZOOM_K));+ },{passive:false});+ var drag=false,lx=0,ly=0;+ vp.addEventListener('pointerdown',function(e){ if(e.target.closest('.av-tools'))return; drag=true;lx=e.clientX;ly=e.clientY;vp.classList.add('grab');vp.setPointerCapture(e.pointerId); });+ vp.addEventListener('pointermove',function(e){ if(!drag)return; tx+=e.clientX-lx;ty+=e.clientY-ly;lx=e.clientX;ly=e.clientY;apply(); });+ vp.addEventListener('pointerup',function(){ drag=false;vp.classList.remove('grab'); });+ fit();+ return {+ fit:fit,+ zoomIn:function(){ var r=vp.getBoundingClientRect(); zoomAt(r.width/2,r.height/2,1.2); },+ zoomOut:function(){ var r=vp.getBoundingClientRect(); zoomAt(r.width/2,r.height/2,1/1.2); },+ apply:apply+ };+ }++ // Inject the standard zoom toolbar (+ / − / fit) + pan hint into a viewport,+ // wired to a controller from initViewport. Idempotent.+ function addToolbar(vp,ctl){+ if(vp.querySelector('.av-tools')) return;+ var t=document.createElement('div'); t.className='av-tools';+ t.innerHTML='<button data-z="in" title="Zoom in">'+ICON_ZIN+'</button>'+ +'<button data-z="out" title="Zoom out">'+ICON_ZOUT+'</button>'+ +'<button data-z="fit" title="Zoom to fit">'+ICON_FIT+'</button>';+ vp.appendChild(t);+ t.querySelector('[data-z="in"]').onclick=ctl.zoomIn;+ t.querySelector('[data-z="out"]').onclick=ctl.zoomOut;+ t.querySelector('[data-z="fit"]').onclick=ctl.fit;+ var h=document.createElement('div'); h.className='av-panhint'; h.textContent='drag to pan · scroll to zoom'; vp.appendChild(h);+ }++ window.AV={+ LAYER_DEFS:LAYER_DEFS, groupHasGeo:groupHasGeo, layerPresent:layerPresent,+ applyLayer:applyLayer, buildLayers:buildLayers, initViewport:initViewport,+ addToolbar:addToolbar, ICON_FIT:ICON_FIT, ICON_ZIN:ICON_ZIN, ICON_ZOUT:ICON_ZOUT+ };+})();