molecule
rf-mech-molecule-32x32
Public Made by Adomby adom
Mechanical datum — machine pins + mounting holes — 32 x 32 mm
main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>adom-2dboard</title>
<style>
/* Canonical Adom design tokens (adom-ui-design). Legacy names kept as aliases.
Primary accent #00b8b0; #00e6dc is the bright hover variant. */
:root { color-scheme: dark;
--bg:#0d1117; --panel:#161b22; --elevated:#1c2128; --overlay:#21262d; --edge:#30363d;
--ink:#e6edf3; --mut:#8b949e; --acc:#00b8b0; --acc2:#00e6dc;
--green:#3fb950; --red:#f85149; --yellow:#d29922; --hl:#4ff0e6;
--line:var(--edge); --txt:var(--ink); --muted:var(--mut); --accent:var(--acc); }
* { box-sizing: border-box; }
/* Three fonts by role: body Satoshi, headers Familjen Grotesk, data JetBrains Mono. */
html, body { margin: 0; height: 100%; background: var(--bg); color: var(--txt); font: 13px/1.45 'Satoshi', -apple-system, 'Segoe UI', Roboto, sans-serif; overflow: hidden; }
#svgbox { position: absolute; inset: 0; cursor: grab; }
#svgbox:active { cursor: grabbing; }
#svgbox svg { display: block; }
.empty { position: absolute; inset: 0; display: grid; place-items: center; color: var(--muted); }
/* overlay is invisible until hovered / net-highlighted; only pads+vias+traces take pointer events.
pointer-events:FILL (not all) so the hit area is the pad SHAPE only — `all` would include the
phantom 1mm default stroke and make the hit zone ~2.5mm wide. */
#svgbox svg .adom-overlay .adom-pad .pad-body, #svgbox svg .adom-overlay .adom-via .pad-body { pointer-events: fill; cursor: pointer; }
#svgbox svg .adom-overlay .ov-trace { pointer-events: stroke; cursor: pointer; }
/* fills are clickable (select the net) but never hovered — hover only targets pads/vias */
#svgbox svg .adom-overlay .ov-zone { pointer-events: fill; cursor: pointer; }
/* ── hover: gentle white outline on the single pad/via ── */
#svgbox svg .adom-pad.pad-hl .pad-body { stroke: #fff !important; stroke-width: .07; paint-order: stroke; }
#svgbox svg .adom-via.pad-hl .pad-body { stroke: #fff !important; stroke-width: .05; }
/* cross-probe: the part selected from the schematic view — bright accent outline
on all of its pads so the whole footprint reads as "this component". */
#svgbox svg .adom-pad.comp-xhl .pad-body { stroke: var(--acc2) !important; stroke-width: .14 !important; paint-order: stroke; }
/* ── click net: dim every base layer; show the selected net's copper EXACTLY as on the
PCB (solid traces w/ round joins, real pads w/ drill holes), just in focus. No
outlines, no brightness boost, no transition (that caused the shrink). ── */
#svgbox svg.dim .adom-flip > g[class*="adom-layer-"] { opacity: .16 !important; }
#svgbox svg .adom-overlay .ov-trace.hl { stroke: var(--hlc) !important; }
#svgbox svg .adom-overlay .ov-zone.hl { fill: var(--hlc) !important; fill-opacity: .3 !important; stroke: none !important; }
#svgbox svg .adom-overlay .adom-pad.hl .pad-body, #svgbox svg .adom-overlay .adom-via.hl .pad-body { fill: var(--hlc) !important; }
#svgbox svg .adom-overlay .adom-pad.hl .pad-hole, #svgbox svg .adom-overlay .adom-via.hl .pad-hole { fill: #161b22 !important; }
/* ── chrome ── */
.panel { position: absolute; background: rgba(22,27,34,.82); backdrop-filter: blur(8px); border: 1px solid var(--line); border-radius: 10px; }
/* single right-side panel: Layers section on top, Nets below */
#side { top: 12px; right: 12px; width: 212px; max-height: 94vh; padding: 10px 12px; display: flex; flex-direction: column; }
#side h1 { margin: 0 0 8px; font-size: 12px; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); font-weight: 600; }
.sec { margin-bottom: 10px; min-height: 0; display: flex; flex-direction: column; }
.sec-h { font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); font-weight: 600; margin: 2px 0 5px; border-bottom: 1px solid var(--line); padding-bottom: 3px; }
#layers { max-height: 42vh; overflow-y: auto; }
#netlist { max-height: 30vh; overflow-y: auto; }
.lyr { display: flex; align-items: center; gap: 7px; padding: 2px 0; cursor: pointer; user-select: none; }
.lyr input { accent-color: var(--accent); }
#fitbtn { margin-top: 4px; width: 100%; background: #17231c; color: var(--txt); border: 1px solid var(--line); border-radius: 7px; padding: 5px; cursor: pointer; }
#fitbtn:hover { border-color: var(--accent); }
#sidebtns { display: flex; align-items: center; gap: 4px; margin-bottom: 8px; }
#sidebtns .lbl { color: var(--muted); font-size: 11px; margin-right: auto; }
.sidebtn { background: #17231c; color: var(--muted); border: 1px solid var(--line); border-radius: 6px; padding: 2px 8px; cursor: pointer; font-size: 11px; }
.sidebtn.on { background: var(--accent); color: #111; border-color: var(--accent); font-weight: 600; }
.lyr-name { cursor: pointer; }
.lyr-name:hover { color: var(--accent); text-decoration: underline; }
/* the copper layer currently raised to the front (click its name again to reset) */
.lyr.raised .lyr-name { color: var(--accent); font-weight: 600; }
.lyr.raised .lyr-name::after { content: " \2191"; }
.netrow { padding: 3px 7px; border-radius: 5px; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
.netrow:hover { background: #1c2a22; color: var(--hl); }
.netrow.sel { background: var(--accent); color: #06232a; font-weight: 600; }
.netrow.sel:hover { color: #06232a; }
#stat { bottom: 12px; left: 12px; padding: 6px 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
#netchip { bottom: 12px; left: 50%; transform: translateX(-50%); padding: 6px 13px; display: none; }
#netchip b { color: var(--accent); text-transform: uppercase; font-size: 10px; letter-spacing: .06em; margin-right: 4px; }
.muted { color: var(--muted); }
/* ── component info card ── */
#card { position: fixed; z-index: 9999; display: none; min-width: 180px; max-width: 260px; padding: 10px 12px; pointer-events: none; }
.ci-h { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; margin-bottom: 6px; padding-bottom: 6px; border-bottom: 1px solid var(--line); }
.ci-ref { font-weight: 700; color: var(--accent); font-size: 14px; }
.ci-val { color: var(--txt); }
.ci-row { display: flex; justify-content: space-between; gap: 10px; padding: 1px 0; }
.ci-k { color: var(--muted); }
.ci-v { text-align: right; word-break: break-word; }
.ci-enrich { margin-top: 7px; padding-top: 7px; border-top: 1px solid var(--line); }
.pills { display: flex; flex-wrap: wrap; gap: 5px; }
.pill { background: #17231c; border: 1px solid var(--line); border-radius: 999px; padding: 1px 8px; font-size: 11px; }
.pill.ok { color: #57d97e; border-color: #2c5a3a; }
.pill.no { color: #e08a8a; border-color: #5a2c2c; }
.pill.tier { color: #f2d060; }
.pill.pop { color: #cba6ff; }
.ci-wiki { display: inline-block; margin-top: 6px; color: #6bb3ff; text-decoration: none; }
#side h1, .sec-h { font-family: 'Familjen Grotesk', -apple-system, sans-serif; }
/* ── canonical Adom 44px three-zone header (brand · subject · actions) ── */
header.app { display: none; position: fixed; top: 0; left: 0; right: 0; height: 44px; z-index: 50;
grid-template-columns: 1fr auto 1fr; align-items: center; gap: 12px; padding: 0 14px;
background: var(--panel); border-bottom: 1px solid var(--edge); }
body.chrome header.app { display: grid; }
body.chrome #svgbox { top: 44px; }
body.chrome #side { top: 56px; }
header.app .brand { display: flex; align-items: center; gap: 8px; min-width: 0; position: relative; cursor: default; padding-right: 14px; border-right: 1px solid var(--edge); }
header.app .brand svg { width: 16px; height: 16px; color: var(--ink); flex: 0 0 16px; }
header.app .app-name { font-family: 'Familjen Grotesk', -apple-system, sans-serif; font-weight: 600; font-size: 13px; color: var(--acc); white-space: nowrap; }
header.app .app-sub { color: var(--mut); font-size: 12px; white-space: nowrap; }
header.app .app-sub::before { content: "·"; margin: 0 6px; color: var(--mut); }
header.app .subject { justify-self: center; display: flex; align-items: baseline; gap: 10px; min-width: 0; }
header.app .subject-name { font-weight: 600; font-size: 13px; max-width: 44vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
header.app .subject-meta { font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 11px; color: var(--mut); white-space: nowrap; }
header.app .actions { justify-self: end; display: flex; align-items: center; gap: 8px; }
header.app .actions button { background: var(--elevated); color: var(--ink); border: 1px solid var(--edge); border-radius: 7px; width: 30px; height: 26px; cursor: pointer; display: grid; place-items: center; }
header.app .actions button:hover { border-color: var(--acc); }
header.app .actions button svg { width: 15px; height: 15px; }
header.app .appinfo { position: absolute; top: calc(100% + 6px); left: 0; min-width: 220px; background: var(--overlay); border: 1px solid var(--edge);
border-radius: 10px; box-shadow: 0 12px 34px #000a; padding: 12px 14px; font-size: 12px; opacity: 0; visibility: hidden; transition: opacity .18s ease; pointer-events: none; z-index: 60; }
header.app .brand:hover .appinfo { opacity: 1; visibility: visible; pointer-events: auto; transition-delay: .6s; }
header.app .appinfo .ai-row { display: flex; justify-content: space-between; gap: 16px; padding: 2px 0; }
header.app .appinfo .ai-k { color: var(--mut); }
header.app .appinfo .ai-v { font-family: 'JetBrains Mono', ui-monospace, monospace; }
header.app .appinfo a { color: var(--acc); text-decoration: none; }
header.app .appinfo a:hover { text-decoration: underline; }
/* ── toasts: transient feedback, bottom-center; also the AI's live channel ── */
#toasts { position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%); z-index: 3000; display: flex; flex-direction: column-reverse; gap: 8px; align-items: center; pointer-events: none; }
.toast { pointer-events: auto; min-width: 200px; max-width: min(420px,90vw); display: flex; align-items: center; gap: 10px; padding: 10px 13px; border-radius: 10px;
background: var(--overlay); border: 1px solid var(--edge); color: var(--ink); font-size: 13px; box-shadow: 0 10px 30px #000a; cursor: pointer; animation: toastin .18s ease; }
@keyframes toastin { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.toast.out { opacity: 0; transform: translateY(8px); transition: opacity .2s ease, transform .2s ease; }
.toast .ti { flex: 0 0 16px; width: 16px; height: 16px; }
.toast.success .ti { color: var(--green); } .toast.success { border-color: #2c5a3a; }
.toast.error .ti { color: var(--red); } .toast.error { border-color: #5a2c2c; }
.toast.info .ti { color: var(--acc); }
.toast .tmsg { flex: 1; min-width: 0; }
/* ── tooltips: body-appended fixed div, 600ms reveal, clamped both axes ── */
#tt { position: fixed; z-index: 99999; max-width: 260px; background: var(--overlay); color: var(--ink); border: 1px solid var(--edge);
border-radius: 7px; padding: 6px 9px; font-size: 12px; line-height: 1.4; box-shadow: 0 8px 22px #000b; pointer-events: none;
opacity: 0; transform: translateY(3px); transition: opacity .18s ease, transform .18s ease; text-transform: none; }
#tt.show { opacity: 1; transform: none; }
@media (hover: none), (pointer: coarse) { #tt { display: none; } }
</style>
</head>
<body>
<header class="app">
<div class="brand">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="16" rx="1.5"/><path d="M7 8h4v4H7zM14 14h3M15.5 8v4"/><circle cx="17" cy="8" r="1.1"/></svg>
<span class="app-name">PCB Viewer</span><span class="app-sub">interactive layout</span>
<div class="appinfo">
<div class="ai-row"><span class="ai-k">version</span><span class="ai-v" id="ai-ver">-</span></div>
<div class="ai-row"><span class="ai-k">engine</span><span class="ai-v">KiCad · Altium · Fusion</span></div>
<div class="ai-row"><span class="ai-k">source</span><a href="https://wiki.adom.inc/adom/adom-2dboard" target="_blank" rel="noopener">wiki page ↗</a></div>
</div>
</div>
<div class="subject"><span class="subject-name" id="hdr-name"></span><span class="subject-meta" id="hdr-meta"></span></div>
<div class="actions">
<button id="hdr-fit" data-tip="Fit the whole board to the view"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 9V4h5M20 9V4h-5M4 15v5h5M20 15v5h-5"/></svg></button>
</div>
</header>
<div id="svgbox"><div class="empty">loading…</div></div>
<div id="side" class="panel">
<div class="sec"><div class="sec-h">Layers</div><div id="layers"></div></div>
<div class="sec"><div class="sec-h">Nets</div><div id="netlist"></div></div>
<button id="fitbtn" data-tip="Zoom to fit the entire board">Fit</button>
</div>
<div id="stat" class="panel"></div>
<div id="netchip" class="panel"></div>
<div id="card" class="panel"></div>
<div id="toasts"></div>
<script>window.__PCB__={svg:"<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"19.000 19.000 42.000 42.000\" preserveAspectRatio=\"xMidYMid meet\"><rect class=\"adom-board-sub\" x=\"19.000\" y=\"19.000\" width=\"42.000\" height=\"42.000\" rx=\"0.3\" fill=\"#0d1117\"/><g class=\"adom-flip\" data-cx=\"40.000\"><g class=\"adom-layer-fabB\" data-side=\"B\">\n<title>SVG Image created as .tmpptPRB2.svg date 2026-09-01T03:31:06 </title>\n <desc>Image generated by PCBNEW </desc>\n<g style=\"fill:#000000; fill-opacity:1.0000;stroke:#000000; stroke-opacity:1.0000;\nstroke-linecap:round; stroke-linejoin:round;\"\n transform=\"translate(0 0) scale(1 1)\">\n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n<circle cx=\"51.8500\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n<circle cx=\"28.0000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n<circle cx=\"51.8500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n<circle cx=\"35.9500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"36.0000\" r=\"1.6000\" /> \n</g> \n</g><g class=\"adom-layer-maskB\" data-side=\"B\">\n<title>SVG Image created as .tmpOxgNFI.svg date 2026-09-01T03:31:07 </title>\n <desc>Image generated by PCBNEW </desc>\n<g style=\"fill:#000000; fill-opacity:1.0000;stroke:#000000; stroke-opacity:1.0000;\nstroke-linecap:round; stroke-linejoin:round;\"\n transform=\"translate(0 0) scale(1 1)\">\n</g>\n<g style=\"fill:#02FFEE; fill-opacity:0.4000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"2.6000\" /> \n<circle cx=\"51.8500\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"2.6000\" /> \n<circle cx=\"28.0000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"2.6000\" /> \n<circle cx=\"51.8500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"2.6000\" /> \n<circle cx=\"35.9500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"36.0000\" r=\"1.6000\" /> \n</g>\n<g style=\"fill:#FFFFFF; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"51.8500\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"52.0000\" r=\"1.6000\" /> \n</g>\n<g style=\"fill:#FFFFFF; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"28.0000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"44.0000\" r=\"1.6000\" /> \n</g>\n<g style=\"fill:#FFFFFF; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"51.8500\" cy=\"28.0000\" r=\"1.6000\" /> \n</g>\n<g style=\"fill:#FFFFFF; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"35.9500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"36.0000\" r=\"1.6000\" /> \n</g> \n</g><g class=\"adom-layer-copperB\" data-side=\"B\">\n<title>SVG Image created as .tmpLDMD1Q.svg date 2026-09-01T03:31:08 </title>\n <desc>Image generated by PCBNEW </desc>\n<g style=\"fill:#000000; fill-opacity:1.0000;stroke:#000000; stroke-opacity:1.0000;\nstroke-linecap:round; stroke-linejoin:round;\"\n transform=\"translate(0 0) scale(1 1)\">\n</g>\n<g style=\"fill:#4D7FC4; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"2.6000\" /> \n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"2.6000\" /> \n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"2.6000\" /> \n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"2.6000\" /> \n</g>\n<g style=\"fill:#0b0e13; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"51.8500\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"52.0000\" r=\"1.6000\" /> \n</g>\n<g style=\"fill:#0b0e13; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"28.0000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"44.0000\" r=\"1.6000\" /> \n</g>\n<g style=\"fill:#0b0e13; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"51.8500\" cy=\"28.0000\" r=\"1.6000\" /> \n</g>\n<g style=\"fill:#0b0e13; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"35.9500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"36.0000\" r=\"1.6000\" /> \n</g> \n</g><g class=\"adom-layer-silkB\" data-side=\"B\">\n<title>SVG Image created as .tmpmzT7ed.svg date 2026-09-01T03:31:08 </title>\n <desc>Image generated by PCBNEW </desc>\n<g style=\"fill:#000000; fill-opacity:1.0000;stroke:#000000; stroke-opacity:1.0000;\nstroke-linecap:round; stroke-linejoin:round;\"\n transform=\"translate(0 0) scale(1 1)\">\n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n<circle cx=\"51.8500\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n<circle cx=\"28.0000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n<circle cx=\"51.8500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n<circle cx=\"35.9500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"36.0000\" r=\"1.6000\" /> \n</g> \n</g><g class=\"adom-layer-maskF\" data-side=\"F\">\n<title>SVG Image created as .tmpSiXOFC.svg date 2026-09-01T03:31:09 </title>\n <desc>Image generated by PCBNEW </desc>\n<g style=\"fill:#000000; fill-opacity:1.0000;stroke:#000000; stroke-opacity:1.0000;\nstroke-linecap:round; stroke-linejoin:round;\"\n transform=\"translate(0 0) scale(1 1)\">\n</g>\n<g style=\"fill:#D864FF; fill-opacity:0.4000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"2.6000\" /> \n<circle cx=\"51.8500\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"2.6000\" /> \n<circle cx=\"28.0000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"2.6000\" /> \n<circle cx=\"51.8500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"2.6000\" /> \n<circle cx=\"35.9500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"36.0000\" r=\"1.6000\" /> \n</g>\n<g style=\"fill:#FFFFFF; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"51.8500\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"52.0000\" r=\"1.6000\" /> \n</g>\n<g style=\"fill:#FFFFFF; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"28.0000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"44.0000\" r=\"1.6000\" /> \n</g>\n<g style=\"fill:#FFFFFF; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"51.8500\" cy=\"28.0000\" r=\"1.6000\" /> \n</g>\n<g style=\"fill:#FFFFFF; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"35.9500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"36.0000\" r=\"1.6000\" /> \n</g> \n</g><g class=\"adom-layer-copperF\" data-side=\"F\">\n<title>SVG Image created as .tmp9p5JDY.svg date 2026-09-01T03:31:10 </title>\n <desc>Image generated by PCBNEW </desc>\n<g style=\"fill:#000000; fill-opacity:1.0000;stroke:#000000; stroke-opacity:1.0000;\nstroke-linecap:round; stroke-linejoin:round;\"\n transform=\"translate(0 0) scale(1 1)\">\n</g>\n<g style=\"fill:#C83434; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"2.6000\" /> \n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"2.6000\" /> \n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"2.6000\" /> \n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"2.6000\" /> \n</g>\n<g style=\"fill:#0b0e13; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"51.8500\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"52.0000\" r=\"1.6000\" /> \n</g>\n<g style=\"fill:#0b0e13; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"28.0000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"44.0000\" r=\"1.6000\" /> \n</g>\n<g style=\"fill:#0b0e13; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"51.8500\" cy=\"28.0000\" r=\"1.6000\" /> \n</g>\n<g style=\"fill:#0b0e13; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"35.9500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"36.0000\" r=\"1.6000\" /> \n</g> \n</g><g class=\"adom-layer-silkF\" data-side=\"F\">\n<title>SVG Image created as .tmpwgy40b.svg date 2026-09-01T03:31:10 </title>\n <desc>Image generated by PCBNEW </desc>\n<g style=\"fill:#000000; fill-opacity:1.0000;stroke:#000000; stroke-opacity:1.0000;\nstroke-linecap:round; stroke-linejoin:round;\"\n transform=\"translate(0 0) scale(1 1)\">\n</g>\n<g style=\"fill:none; \nstroke:#F2EDA1; stroke-width:0.0500; stroke-opacity:1; \nstroke-linecap:round; stroke-linejoin:round;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"2.7000\" /> \n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"2.7000\" /> \n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"2.7000\" /> \n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"2.7000\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n<circle cx=\"51.8500\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n<circle cx=\"28.0000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n<circle cx=\"51.8500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n<circle cx=\"35.9500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"36.0000\" r=\"1.6000\" /> \n</g> \n</g><g class=\"adom-layer-fabF\" data-side=\"F\">\n<title>SVG Image created as .tmphTseVW.svg date 2026-09-01T03:31:11 </title>\n <desc>Image generated by PCBNEW </desc>\n<g style=\"fill:#000000; fill-opacity:1.0000;stroke:#000000; stroke-opacity:1.0000;\nstroke-linecap:round; stroke-linejoin:round;\"\n transform=\"translate(0 0) scale(1 1)\">\n</g>\n<g style=\"fill:none; \nstroke:#AFAFAF; stroke-width:0.1250; stroke-opacity:1; \nstroke-linecap:round; stroke-linejoin:round;\">\n<text x=\"56.0000\" y=\"56.2500\"\ntextLength=\"1.8226\" font-size=\"0.6667\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">MP4</text>\n<g class=\"stroked-text\"><desc>MP4</desc>\n<path d=\"M55.3452 56.2248\nL55.3452 55.7248\n\" />\n<path d=\"M55.3452 55.7248\nL55.5119 56.0820\n\" />\n<path d=\"M55.5119 56.0820\nL55.6786 55.7248\n\" />\n<path d=\"M55.6786 55.7248\nL55.6786 56.2248\n\" />\n<path d=\"M55.9167 56.2248\nL55.9167 55.7248\n\" />\n<path d=\"M55.9167 55.7248\nL56.1071 55.7248\n\" />\n<path d=\"M56.1071 55.7248\nL56.1548 55.7486\n\" />\n<path d=\"M56.1548 55.7486\nL56.1786 55.7724\n\" />\n<path d=\"M56.1786 55.7724\nL56.2024 55.8200\n\" />\n<path d=\"M56.2024 55.8200\nL56.2024 55.8915\n\" />\n<path d=\"M56.2024 55.8915\nL56.1786 55.9391\n\" />\n<path d=\"M56.1786 55.9391\nL56.1548 55.9629\n\" />\n<path d=\"M56.1548 55.9629\nL56.1071 55.9867\n\" />\n<path d=\"M56.1071 55.9867\nL55.9167 55.9867\n\" />\n<path d=\"M56.6310 55.8915\nL56.6310 56.2248\n\" />\n<path d=\"M56.5119 55.7010\nL56.3929 56.0581\n\" />\n<path d=\"M56.3929 56.0581\nL56.7024 56.0581\n\" />\n</g></g>\n<g style=\"fill:none; \nstroke:#AFAFAF; stroke-width:0.1500; stroke-opacity:1; \nstroke-linecap:round; stroke-linejoin:round;\">\n<text x=\"51.8500\" y=\"36.5000\"\ntextLength=\"3.2024\" font-size=\"1.3333\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">H14</text>\n<g class=\"stroked-text\"><desc>H14</desc>\n<path d=\"M50.6119 36.4548\nL50.6119 35.4548\n\" />\n<path d=\"M50.6119 35.9310\nL51.1833 35.9310\n\" />\n<path d=\"M51.1833 36.4548\nL51.1833 35.4548\n\" />\n<path d=\"M52.1833 36.4548\nL51.6119 36.4548\n\" />\n<path d=\"M51.8976 36.4548\nL51.8976 35.4548\n\" />\n<path d=\"M51.8976 35.4548\nL51.8024 35.5977\n\" />\n<path d=\"M51.8024 35.5977\nL51.7071 35.6929\n\" />\n<path d=\"M51.7071 35.6929\nL51.6119 35.7405\n\" />\n<path d=\"M53.0405 35.7882\nL53.0405 36.4548\n\" />\n<path d=\"M52.8024 35.4072\nL52.5643 36.1215\n\" />\n<path d=\"M52.5643 36.1215\nL53.1833 36.1215\n\" />\n</g><text x=\"43.9000\" y=\"28.5000\"\ntextLength=\"2.2500\" font-size=\"1.3333\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">H9</text>\n<g class=\"stroked-text\"><desc>H9</desc>\n<path d=\"M43.1381 28.4548\nL43.1381 27.4548\n\" />\n<path d=\"M43.1381 27.9310\nL43.7095 27.9310\n\" />\n<path d=\"M43.7095 28.4548\nL43.7095 27.4548\n\" />\n<path d=\"M44.2333 28.4548\nL44.4238 28.4548\n\" />\n<path d=\"M44.4238 28.4548\nL44.5190 28.4072\n\" />\n<path d=\"M44.5190 28.4072\nL44.5667 28.3596\n\" />\n<path d=\"M44.5667 28.3596\nL44.6619 28.2167\n\" />\n<path d=\"M44.6619 28.2167\nL44.7095 28.0262\n\" />\n<path d=\"M44.7095 28.0262\nL44.7095 27.6453\n\" />\n<path d=\"M44.7095 27.6453\nL44.6619 27.5501\n\" />\n<path d=\"M44.6619 27.5501\nL44.6143 27.5024\n\" />\n<path d=\"M44.6143 27.5024\nL44.5190 27.4548\n\" />\n<path d=\"M44.5190 27.4548\nL44.3286 27.4548\n\" />\n<path d=\"M44.3286 27.4548\nL44.2333 27.5024\n\" />\n<path d=\"M44.2333 27.5024\nL44.1857 27.5501\n\" />\n<path d=\"M44.1857 27.5501\nL44.1381 27.6453\n\" />\n<path d=\"M44.1381 27.6453\nL44.1381 27.8834\n\" />\n<path d=\"M44.1381 27.8834\nL44.1857 27.9786\n\" />\n<path d=\"M44.1857 27.9786\nL44.2333 28.0262\n\" />\n<path d=\"M44.2333 28.0262\nL44.3286 28.0739\n\" />\n<path d=\"M44.3286 28.0739\nL44.5190 28.0739\n\" />\n<path d=\"M44.5190 28.0739\nL44.6143 28.0262\n\" />\n<path d=\"M44.6143 28.0262\nL44.6619 27.9786\n\" />\n<path d=\"M44.6619 27.9786\nL44.7095 27.8834\n\" />\n</g><text x=\"35.9500\" y=\"52.5000\"\ntextLength=\"2.2500\" font-size=\"1.3333\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">H8</text>\n<g class=\"stroked-text\"><desc>H8</desc>\n<path d=\"M35.1881 52.4548\nL35.1881 51.4548\n\" />\n<path d=\"M35.1881 51.9310\nL35.7595 51.9310\n\" />\n<path d=\"M35.7595 52.4548\nL35.7595 51.4548\n\" />\n<path d=\"M36.3786 51.8834\nL36.2833 51.8358\n\" />\n<path d=\"M36.2833 51.8358\nL36.2357 51.7882\n\" />\n<path d=\"M36.2357 51.7882\nL36.1881 51.6929\n\" />\n<path d=\"M36.1881 51.6929\nL36.1881 51.6453\n\" />\n<path d=\"M36.1881 51.6453\nL36.2357 51.5501\n\" />\n<path d=\"M36.2357 51.5501\nL36.2833 51.5024\n\" />\n<path d=\"M36.2833 51.5024\nL36.3786 51.4548\n\" />\n<path d=\"M36.3786 51.4548\nL36.5690 51.4548\n\" />\n<path d=\"M36.5690 51.4548\nL36.6643 51.5024\n\" />\n<path d=\"M36.6643 51.5024\nL36.7119 51.5501\n\" />\n<path d=\"M36.7119 51.5501\nL36.7595 51.6453\n\" />\n<path d=\"M36.7595 51.6453\nL36.7595 51.6929\n\" />\n<path d=\"M36.7595 51.6929\nL36.7119 51.7882\n\" />\n<path d=\"M36.7119 51.7882\nL36.6643 51.8358\n\" />\n<path d=\"M36.6643 51.8358\nL36.5690 51.8834\n\" />\n<path d=\"M36.5690 51.8834\nL36.3786 51.8834\n\" />\n<path d=\"M36.3786 51.8834\nL36.2833 51.9310\n\" />\n<path d=\"M36.2833 51.9310\nL36.2357 51.9786\n\" />\n<path d=\"M36.2357 51.9786\nL36.1881 52.0739\n\" />\n<path d=\"M36.1881 52.0739\nL36.1881 52.2643\n\" />\n<path d=\"M36.1881 52.2643\nL36.2357 52.3596\n\" />\n<path d=\"M36.2357 52.3596\nL36.2833 52.4072\n\" />\n<path d=\"M36.2833 52.4072\nL36.3786 52.4548\n\" />\n<path d=\"M36.3786 52.4548\nL36.5690 52.4548\n\" />\n<path d=\"M36.5690 52.4548\nL36.6643 52.4072\n\" />\n<path d=\"M36.6643 52.4072\nL36.7119 52.3596\n\" />\n<path d=\"M36.7119 52.3596\nL36.7595 52.2643\n\" />\n<path d=\"M36.7595 52.2643\nL36.7595 52.0739\n\" />\n<path d=\"M36.7595 52.0739\nL36.7119 51.9786\n\" />\n<path d=\"M36.7119 51.9786\nL36.6643 51.9310\n\" />\n<path d=\"M36.6643 51.9310\nL36.5690 51.8834\n\" />\n</g><text x=\"43.9000\" y=\"52.5000\"\ntextLength=\"3.2024\" font-size=\"1.3333\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">H12</text>\n<g class=\"stroked-text\"><desc>H12</desc>\n<path d=\"M42.6619 52.4548\nL42.6619 51.4548\n\" />\n<path d=\"M42.6619 51.9310\nL43.2333 51.9310\n\" />\n<path d=\"M43.2333 52.4548\nL43.2333 51.4548\n\" />\n<path d=\"M44.2333 52.4548\nL43.6619 52.4548\n\" />\n<path d=\"M43.9476 52.4548\nL43.9476 51.4548\n\" />\n<path d=\"M43.9476 51.4548\nL43.8524 51.5977\n\" />\n<path d=\"M43.8524 51.5977\nL43.7571 51.6929\n\" />\n<path d=\"M43.7571 51.6929\nL43.6619 51.7405\n\" />\n<path d=\"M44.6143 51.5501\nL44.6619 51.5024\n\" />\n<path d=\"M44.6619 51.5024\nL44.7571 51.4548\n\" />\n<path d=\"M44.7571 51.4548\nL44.9952 51.4548\n\" />\n<path d=\"M44.9952 51.4548\nL45.0905 51.5024\n\" />\n<path d=\"M45.0905 51.5024\nL45.1381 51.5501\n\" />\n<path d=\"M45.1381 51.5501\nL45.1857 51.6453\n\" />\n<path d=\"M45.1857 51.6453\nL45.1857 51.7405\n\" />\n<path d=\"M45.1857 51.7405\nL45.1381 51.8834\n\" />\n<path d=\"M45.1381 51.8834\nL44.5667 52.4548\n\" />\n<path d=\"M44.5667 52.4548\nL45.1857 52.4548\n\" />\n</g></g>\n<g style=\"fill:none; \nstroke:#AFAFAF; stroke-width:0.1250; stroke-opacity:1; \nstroke-linecap:round; stroke-linejoin:round;\">\n<text x=\"56.0000\" y=\"24.2500\"\ntextLength=\"1.8226\" font-size=\"0.6667\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">MP2</text>\n<g class=\"stroked-text\"><desc>MP2</desc>\n<path d=\"M55.3452 24.2248\nL55.3452 23.7248\n\" />\n<path d=\"M55.3452 23.7248\nL55.5119 24.0820\n\" />\n<path d=\"M55.5119 24.0820\nL55.6786 23.7248\n\" />\n<path d=\"M55.6786 23.7248\nL55.6786 24.2248\n\" />\n<path d=\"M55.9167 24.2248\nL55.9167 23.7248\n\" />\n<path d=\"M55.9167 23.7248\nL56.1071 23.7248\n\" />\n<path d=\"M56.1071 23.7248\nL56.1548 23.7486\n\" />\n<path d=\"M56.1548 23.7486\nL56.1786 23.7724\n\" />\n<path d=\"M56.1786 23.7724\nL56.2024 23.8200\n\" />\n<path d=\"M56.2024 23.8200\nL56.2024 23.8915\n\" />\n<path d=\"M56.2024 23.8915\nL56.1786 23.9391\n\" />\n<path d=\"M56.1786 23.9391\nL56.1548 23.9629\n\" />\n<path d=\"M56.1548 23.9629\nL56.1071 23.9867\n\" />\n<path d=\"M56.1071 23.9867\nL55.9167 23.9867\n\" />\n<path d=\"M56.3929 23.7724\nL56.4167 23.7486\n\" />\n<path d=\"M56.4167 23.7486\nL56.4643 23.7248\n\" />\n<path d=\"M56.4643 23.7248\nL56.5833 23.7248\n\" />\n<path d=\"M56.5833 23.7248\nL56.6310 23.7486\n\" />\n<path d=\"M56.6310 23.7486\nL56.6548 23.7724\n\" />\n<path d=\"M56.6548 23.7724\nL56.6786 23.8200\n\" />\n<path d=\"M56.6786 23.8200\nL56.6786 23.8677\n\" />\n<path d=\"M56.6786 23.8677\nL56.6548 23.9391\n\" />\n<path d=\"M56.6548 23.9391\nL56.3690 24.2248\n\" />\n<path d=\"M56.3690 24.2248\nL56.6786 24.2248\n\" />\n</g></g>\n<g style=\"fill:none; \nstroke:#AFAFAF; stroke-width:0.1500; stroke-opacity:1; \nstroke-linecap:round; stroke-linejoin:round;\">\n<text x=\"28.0000\" y=\"36.5000\"\ntextLength=\"2.2500\" font-size=\"1.3333\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">H2</text>\n<g class=\"stroked-text\"><desc>H2</desc>\n<path d=\"M27.2381 36.4548\nL27.2381 35.4548\n\" />\n<path d=\"M27.2381 35.9310\nL27.8095 35.9310\n\" />\n<path d=\"M27.8095 36.4548\nL27.8095 35.4548\n\" />\n<path d=\"M28.2381 35.5501\nL28.2857 35.5024\n\" />\n<path d=\"M28.2857 35.5024\nL28.3810 35.4548\n\" />\n<path d=\"M28.3810 35.4548\nL28.6190 35.4548\n\" />\n<path d=\"M28.6190 35.4548\nL28.7143 35.5024\n\" />\n<path d=\"M28.7143 35.5024\nL28.7619 35.5501\n\" />\n<path d=\"M28.7619 35.5501\nL28.8095 35.6453\n\" />\n<path d=\"M28.8095 35.6453\nL28.8095 35.7405\n\" />\n<path d=\"M28.8095 35.7405\nL28.7619 35.8834\n\" />\n<path d=\"M28.7619 35.8834\nL28.1905 36.4548\n\" />\n<path d=\"M28.1905 36.4548\nL28.8095 36.4548\n\" />\n</g><text x=\"51.8500\" y=\"52.5000\"\ntextLength=\"3.2024\" font-size=\"1.3333\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">H16</text>\n<g class=\"stroked-text\"><desc>H16</desc>\n<path d=\"M50.6119 52.4548\nL50.6119 51.4548\n\" />\n<path d=\"M50.6119 51.9310\nL51.1833 51.9310\n\" />\n<path d=\"M51.1833 52.4548\nL51.1833 51.4548\n\" />\n<path d=\"M52.1833 52.4548\nL51.6119 52.4548\n\" />\n<path d=\"M51.8976 52.4548\nL51.8976 51.4548\n\" />\n<path d=\"M51.8976 51.4548\nL51.8024 51.5977\n\" />\n<path d=\"M51.8024 51.5977\nL51.7071 51.6929\n\" />\n<path d=\"M51.7071 51.6929\nL51.6119 51.7405\n\" />\n<path d=\"M53.0405 51.4548\nL52.8500 51.4548\n\" />\n<path d=\"M52.8500 51.4548\nL52.7548 51.5024\n\" />\n<path d=\"M52.7548 51.5024\nL52.7071 51.5501\n\" />\n<path d=\"M52.7071 51.5501\nL52.6119 51.6929\n\" />\n<path d=\"M52.6119 51.6929\nL52.5643 51.8834\n\" />\n<path d=\"M52.5643 51.8834\nL52.5643 52.2643\n\" />\n<path d=\"M52.5643 52.2643\nL52.6119 52.3596\n\" />\n<path d=\"M52.6119 52.3596\nL52.6595 52.4072\n\" />\n<path d=\"M52.6595 52.4072\nL52.7548 52.4548\n\" />\n<path d=\"M52.7548 52.4548\nL52.9452 52.4548\n\" />\n<path d=\"M52.9452 52.4548\nL53.0405 52.4072\n\" />\n<path d=\"M53.0405 52.4072\nL53.0881 52.3596\n\" />\n<path d=\"M53.0881 52.3596\nL53.1357 52.2643\n\" />\n<path d=\"M53.1357 52.2643\nL53.1357 52.0262\n\" />\n<path d=\"M53.1357 52.0262\nL53.0881 51.9310\n\" />\n<path d=\"M53.0881 51.9310\nL53.0405 51.8834\n\" />\n<path d=\"M53.0405 51.8834\nL52.9452 51.8358\n\" />\n<path d=\"M52.9452 51.8358\nL52.7548 51.8358\n\" />\n<path d=\"M52.7548 51.8358\nL52.6595 51.8834\n\" />\n<path d=\"M52.6595 51.8834\nL52.6119 51.9310\n\" />\n<path d=\"M52.6119 51.9310\nL52.5643 52.0262\n\" />\n</g><text x=\"51.8500\" y=\"44.5000\"\ntextLength=\"3.2024\" font-size=\"1.3333\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">H15</text>\n<g class=\"stroked-text\"><desc>H15</desc>\n<path d=\"M50.6119 44.4548\nL50.6119 43.4548\n\" />\n<path d=\"M50.6119 43.9310\nL51.1833 43.9310\n\" />\n<path d=\"M51.1833 44.4548\nL51.1833 43.4548\n\" />\n<path d=\"M52.1833 44.4548\nL51.6119 44.4548\n\" />\n<path d=\"M51.8976 44.4548\nL51.8976 43.4548\n\" />\n<path d=\"M51.8976 43.4548\nL51.8024 43.5977\n\" />\n<path d=\"M51.8024 43.5977\nL51.7071 43.6929\n\" />\n<path d=\"M51.7071 43.6929\nL51.6119 43.7405\n\" />\n<path d=\"M53.0881 43.4548\nL52.6119 43.4548\n\" />\n<path d=\"M52.6119 43.4548\nL52.5643 43.9310\n\" />\n<path d=\"M52.5643 43.9310\nL52.6119 43.8834\n\" />\n<path d=\"M52.6119 43.8834\nL52.7071 43.8358\n\" />\n<path d=\"M52.7071 43.8358\nL52.9452 43.8358\n\" />\n<path d=\"M52.9452 43.8358\nL53.0405 43.8834\n\" />\n<path d=\"M53.0405 43.8834\nL53.0881 43.9310\n\" />\n<path d=\"M53.0881 43.9310\nL53.1357 44.0262\n\" />\n<path d=\"M53.1357 44.0262\nL53.1357 44.2643\n\" />\n<path d=\"M53.1357 44.2643\nL53.0881 44.3596\n\" />\n<path d=\"M53.0881 44.3596\nL53.0405 44.4072\n\" />\n<path d=\"M53.0405 44.4072\nL52.9452 44.4548\n\" />\n<path d=\"M52.9452 44.4548\nL52.7071 44.4548\n\" />\n<path d=\"M52.7071 44.4548\nL52.6119 44.4072\n\" />\n<path d=\"M52.6119 44.4072\nL52.5643 44.3596\n\" />\n</g></g>\n<g style=\"fill:none; \nstroke:#AFAFAF; stroke-width:0.1250; stroke-opacity:1; \nstroke-linecap:round; stroke-linejoin:round;\">\n<text x=\"24.0000\" y=\"24.2500\"\ntextLength=\"1.8226\" font-size=\"0.6667\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">MP1</text>\n<g class=\"stroked-text\"><desc>MP1</desc>\n<path d=\"M23.3452 24.2248\nL23.3452 23.7248\n\" />\n<path d=\"M23.3452 23.7248\nL23.5119 24.0820\n\" />\n<path d=\"M23.5119 24.0820\nL23.6786 23.7248\n\" />\n<path d=\"M23.6786 23.7248\nL23.6786 24.2248\n\" />\n<path d=\"M23.9167 24.2248\nL23.9167 23.7248\n\" />\n<path d=\"M23.9167 23.7248\nL24.1071 23.7248\n\" />\n<path d=\"M24.1071 23.7248\nL24.1548 23.7486\n\" />\n<path d=\"M24.1548 23.7486\nL24.1786 23.7724\n\" />\n<path d=\"M24.1786 23.7724\nL24.2024 23.8200\n\" />\n<path d=\"M24.2024 23.8200\nL24.2024 23.8915\n\" />\n<path d=\"M24.2024 23.8915\nL24.1786 23.9391\n\" />\n<path d=\"M24.1786 23.9391\nL24.1548 23.9629\n\" />\n<path d=\"M24.1548 23.9629\nL24.1071 23.9867\n\" />\n<path d=\"M24.1071 23.9867\nL23.9167 23.9867\n\" />\n<path d=\"M24.6786 24.2248\nL24.3929 24.2248\n\" />\n<path d=\"M24.5357 24.2248\nL24.5357 23.7248\n\" />\n<path d=\"M24.5357 23.7248\nL24.4881 23.7962\n\" />\n<path d=\"M24.4881 23.7962\nL24.4405 23.8439\n\" />\n<path d=\"M24.4405 23.8439\nL24.3929 23.8677\n\" />\n</g></g>\n<g style=\"fill:none; \nstroke:#AFAFAF; stroke-width:0.1500; stroke-opacity:1; \nstroke-linecap:round; stroke-linejoin:round;\">\n<text x=\"51.8500\" y=\"28.5000\"\ntextLength=\"3.2024\" font-size=\"1.3333\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">H13</text>\n<g class=\"stroked-text\"><desc>H13</desc>\n<path d=\"M50.6119 28.4548\nL50.6119 27.4548\n\" />\n<path d=\"M50.6119 27.9310\nL51.1833 27.9310\n\" />\n<path d=\"M51.1833 28.4548\nL51.1833 27.4548\n\" />\n<path d=\"M52.1833 28.4548\nL51.6119 28.4548\n\" />\n<path d=\"M51.8976 28.4548\nL51.8976 27.4548\n\" />\n<path d=\"M51.8976 27.4548\nL51.8024 27.5977\n\" />\n<path d=\"M51.8024 27.5977\nL51.7071 27.6929\n\" />\n<path d=\"M51.7071 27.6929\nL51.6119 27.7405\n\" />\n<path d=\"M52.5167 27.4548\nL53.1357 27.4548\n\" />\n<path d=\"M53.1357 27.4548\nL52.8024 27.8358\n\" />\n<path d=\"M52.8024 27.8358\nL52.9452 27.8358\n\" />\n<path d=\"M52.9452 27.8358\nL53.0405 27.8834\n\" />\n<path d=\"M53.0405 27.8834\nL53.0881 27.9310\n\" />\n<path d=\"M53.0881 27.9310\nL53.1357 28.0262\n\" />\n<path d=\"M53.1357 28.0262\nL53.1357 28.2643\n\" />\n<path d=\"M53.1357 28.2643\nL53.0881 28.3596\n\" />\n<path d=\"M53.0881 28.3596\nL53.0405 28.4072\n\" />\n<path d=\"M53.0405 28.4072\nL52.9452 28.4548\n\" />\n<path d=\"M52.9452 28.4548\nL52.6595 28.4548\n\" />\n<path d=\"M52.6595 28.4548\nL52.5643 28.4072\n\" />\n<path d=\"M52.5643 28.4072\nL52.5167 28.3596\n\" />\n</g></g>\n<g style=\"fill:none; \nstroke:#AFAFAF; stroke-width:0.1250; stroke-opacity:1; \nstroke-linecap:round; stroke-linejoin:round;\">\n<text x=\"24.0000\" y=\"56.2500\"\ntextLength=\"1.8226\" font-size=\"0.6667\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">MP3</text>\n<g class=\"stroked-text\"><desc>MP3</desc>\n<path d=\"M23.3452 56.2248\nL23.3452 55.7248\n\" />\n<path d=\"M23.3452 55.7248\nL23.5119 56.0820\n\" />\n<path d=\"M23.5119 56.0820\nL23.6786 55.7248\n\" />\n<path d=\"M23.6786 55.7248\nL23.6786 56.2248\n\" />\n<path d=\"M23.9167 56.2248\nL23.9167 55.7248\n\" />\n<path d=\"M23.9167 55.7248\nL24.1071 55.7248\n\" />\n<path d=\"M24.1071 55.7248\nL24.1548 55.7486\n\" />\n<path d=\"M24.1548 55.7486\nL24.1786 55.7724\n\" />\n<path d=\"M24.1786 55.7724\nL24.2024 55.8200\n\" />\n<path d=\"M24.2024 55.8200\nL24.2024 55.8915\n\" />\n<path d=\"M24.2024 55.8915\nL24.1786 55.9391\n\" />\n<path d=\"M24.1786 55.9391\nL24.1548 55.9629\n\" />\n<path d=\"M24.1548 55.9629\nL24.1071 55.9867\n\" />\n<path d=\"M24.1071 55.9867\nL23.9167 55.9867\n\" />\n<path d=\"M24.3690 55.7248\nL24.6786 55.7248\n\" />\n<path d=\"M24.6786 55.7248\nL24.5119 55.9153\n\" />\n<path d=\"M24.5119 55.9153\nL24.5833 55.9153\n\" />\n<path d=\"M24.5833 55.9153\nL24.6310 55.9391\n\" />\n<path d=\"M24.6310 55.9391\nL24.6548 55.9629\n\" />\n<path d=\"M24.6548 55.9629\nL24.6786 56.0105\n\" />\n<path d=\"M24.6786 56.0105\nL24.6786 56.1296\n\" />\n<path d=\"M24.6786 56.1296\nL24.6548 56.1772\n\" />\n<path d=\"M24.6548 56.1772\nL24.6310 56.2010\n\" />\n<path d=\"M24.6310 56.2010\nL24.5833 56.2248\n\" />\n<path d=\"M24.5833 56.2248\nL24.4405 56.2248\n\" />\n<path d=\"M24.4405 56.2248\nL24.3929 56.2010\n\" />\n<path d=\"M24.3929 56.2010\nL24.3690 56.1772\n\" />\n</g></g>\n<g style=\"fill:none; \nstroke:#AFAFAF; stroke-width:0.1500; stroke-opacity:1; \nstroke-linecap:round; stroke-linejoin:round;\">\n<text x=\"35.9500\" y=\"44.5000\"\ntextLength=\"2.2500\" font-size=\"1.3333\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">H7</text>\n<g class=\"stroked-text\"><desc>H7</desc>\n<path d=\"M35.1881 44.4548\nL35.1881 43.4548\n\" />\n<path d=\"M35.1881 43.9310\nL35.7595 43.9310\n\" />\n<path d=\"M35.7595 44.4548\nL35.7595 43.4548\n\" />\n<path d=\"M36.1405 43.4548\nL36.8071 43.4548\n\" />\n<path d=\"M36.8071 43.4548\nL36.3786 44.4548\n\" />\n</g><text x=\"28.0000\" y=\"52.5000\"\ntextLength=\"2.2500\" font-size=\"1.3333\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">H4</text>\n<g class=\"stroked-text\"><desc>H4</desc>\n<path d=\"M27.2381 52.4548\nL27.2381 51.4548\n\" />\n<path d=\"M27.2381 51.9310\nL27.8095 51.9310\n\" />\n<path d=\"M27.8095 52.4548\nL27.8095 51.4548\n\" />\n<path d=\"M28.7143 51.7882\nL28.7143 52.4548\n\" />\n<path d=\"M28.4762 51.4072\nL28.2381 52.1215\n\" />\n<path d=\"M28.2381 52.1215\nL28.8571 52.1215\n\" />\n</g><text x=\"28.0000\" y=\"44.5000\"\ntextLength=\"2.2500\" font-size=\"1.3333\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">H3</text>\n<g class=\"stroked-text\"><desc>H3</desc>\n<path d=\"M27.2381 44.4548\nL27.2381 43.4548\n\" />\n<path d=\"M27.2381 43.9310\nL27.8095 43.9310\n\" />\n<path d=\"M27.8095 44.4548\nL27.8095 43.4548\n\" />\n<path d=\"M28.1905 43.4548\nL28.8095 43.4548\n\" />\n<path d=\"M28.8095 43.4548\nL28.4762 43.8358\n\" />\n<path d=\"M28.4762 43.8358\nL28.6190 43.8358\n\" />\n<path d=\"M28.6190 43.8358\nL28.7143 43.8834\n\" />\n<path d=\"M28.7143 43.8834\nL28.7619 43.9310\n\" />\n<path d=\"M28.7619 43.9310\nL28.8095 44.0262\n\" />\n<path d=\"M28.8095 44.0262\nL28.8095 44.2643\n\" />\n<path d=\"M28.8095 44.2643\nL28.7619 44.3596\n\" />\n<path d=\"M28.7619 44.3596\nL28.7143 44.4072\n\" />\n<path d=\"M28.7143 44.4072\nL28.6190 44.4548\n\" />\n<path d=\"M28.6190 44.4548\nL28.3333 44.4548\n\" />\n<path d=\"M28.3333 44.4548\nL28.2381 44.4072\n\" />\n<path d=\"M28.2381 44.4072\nL28.1905 44.3596\n\" />\n</g><text x=\"35.9500\" y=\"28.5000\"\ntextLength=\"2.2500\" font-size=\"1.3333\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">H5</text>\n<g class=\"stroked-text\"><desc>H5</desc>\n<path d=\"M35.1881 28.4548\nL35.1881 27.4548\n\" />\n<path d=\"M35.1881 27.9310\nL35.7595 27.9310\n\" />\n<path d=\"M35.7595 28.4548\nL35.7595 27.4548\n\" />\n<path d=\"M36.7119 27.4548\nL36.2357 27.4548\n\" />\n<path d=\"M36.2357 27.4548\nL36.1881 27.9310\n\" />\n<path d=\"M36.1881 27.9310\nL36.2357 27.8834\n\" />\n<path d=\"M36.2357 27.8834\nL36.3310 27.8358\n\" />\n<path d=\"M36.3310 27.8358\nL36.5690 27.8358\n\" />\n<path d=\"M36.5690 27.8358\nL36.6643 27.8834\n\" />\n<path d=\"M36.6643 27.8834\nL36.7119 27.9310\n\" />\n<path d=\"M36.7119 27.9310\nL36.7595 28.0262\n\" />\n<path d=\"M36.7595 28.0262\nL36.7595 28.2643\n\" />\n<path d=\"M36.7595 28.2643\nL36.7119 28.3596\n\" />\n<path d=\"M36.7119 28.3596\nL36.6643 28.4072\n\" />\n<path d=\"M36.6643 28.4072\nL36.5690 28.4548\n\" />\n<path d=\"M36.5690 28.4548\nL36.3310 28.4548\n\" />\n<path d=\"M36.3310 28.4548\nL36.2357 28.4072\n\" />\n<path d=\"M36.2357 28.4072\nL36.1881 28.3596\n\" />\n</g><text x=\"28.0000\" y=\"28.5000\"\ntextLength=\"2.2500\" font-size=\"1.3333\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">H1</text>\n<g class=\"stroked-text\"><desc>H1</desc>\n<path d=\"M27.2381 28.4548\nL27.2381 27.4548\n\" />\n<path d=\"M27.2381 27.9310\nL27.8095 27.9310\n\" />\n<path d=\"M27.8095 28.4548\nL27.8095 27.4548\n\" />\n<path d=\"M28.8095 28.4548\nL28.2381 28.4548\n\" />\n<path d=\"M28.5238 28.4548\nL28.5238 27.4548\n\" />\n<path d=\"M28.5238 27.4548\nL28.4286 27.5977\n\" />\n<path d=\"M28.4286 27.5977\nL28.3333 27.6929\n\" />\n<path d=\"M28.3333 27.6929\nL28.2381 27.7405\n\" />\n</g><text x=\"43.9000\" y=\"36.5000\"\ntextLength=\"3.2024\" font-size=\"1.3333\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">H10</text>\n<g class=\"stroked-text\"><desc>H10</desc>\n<path d=\"M42.6619 36.4548\nL42.6619 35.4548\n\" />\n<path d=\"M42.6619 35.9310\nL43.2333 35.9310\n\" />\n<path d=\"M43.2333 36.4548\nL43.2333 35.4548\n\" />\n<path d=\"M44.2333 36.4548\nL43.6619 36.4548\n\" />\n<path d=\"M43.9476 36.4548\nL43.9476 35.4548\n\" />\n<path d=\"M43.9476 35.4548\nL43.8524 35.5977\n\" />\n<path d=\"M43.8524 35.5977\nL43.7571 35.6929\n\" />\n<path d=\"M43.7571 35.6929\nL43.6619 35.7405\n\" />\n<path d=\"M44.8524 35.4548\nL44.9476 35.4548\n\" />\n<path d=\"M44.9476 35.4548\nL45.0429 35.5024\n\" />\n<path d=\"M45.0429 35.5024\nL45.0905 35.5501\n\" />\n<path d=\"M45.0905 35.5501\nL45.1381 35.6453\n\" />\n<path d=\"M45.1381 35.6453\nL45.1857 35.8358\n\" />\n<path d=\"M45.1857 35.8358\nL45.1857 36.0739\n\" />\n<path d=\"M45.1857 36.0739\nL45.1381 36.2643\n\" />\n<path d=\"M45.1381 36.2643\nL45.0905 36.3596\n\" />\n<path d=\"M45.0905 36.3596\nL45.0429 36.4072\n\" />\n<path d=\"M45.0429 36.4072\nL44.9476 36.4548\n\" />\n<path d=\"M44.9476 36.4548\nL44.8524 36.4548\n\" />\n<path d=\"M44.8524 36.4548\nL44.7571 36.4072\n\" />\n<path d=\"M44.7571 36.4072\nL44.7095 36.3596\n\" />\n<path d=\"M44.7095 36.3596\nL44.6619 36.2643\n\" />\n<path d=\"M44.6619 36.2643\nL44.6143 36.0739\n\" />\n<path d=\"M44.6143 36.0739\nL44.6143 35.8358\n\" />\n<path d=\"M44.6143 35.8358\nL44.6619 35.6453\n\" />\n<path d=\"M44.6619 35.6453\nL44.7095 35.5501\n\" />\n<path d=\"M44.7095 35.5501\nL44.7571 35.5024\n\" />\n<path d=\"M44.7571 35.5024\nL44.8524 35.4548\n\" />\n</g><text x=\"43.9000\" y=\"44.5000\"\ntextLength=\"3.2024\" font-size=\"1.3333\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">H11</text>\n<g class=\"stroked-text\"><desc>H11</desc>\n<path d=\"M42.6619 44.4548\nL42.6619 43.4548\n\" />\n<path d=\"M42.6619 43.9310\nL43.2333 43.9310\n\" />\n<path d=\"M43.2333 44.4548\nL43.2333 43.4548\n\" />\n<path d=\"M44.2333 44.4548\nL43.6619 44.4548\n\" />\n<path d=\"M43.9476 44.4548\nL43.9476 43.4548\n\" />\n<path d=\"M43.9476 43.4548\nL43.8524 43.5977\n\" />\n<path d=\"M43.8524 43.5977\nL43.7571 43.6929\n\" />\n<path d=\"M43.7571 43.6929\nL43.6619 43.7405\n\" />\n<path d=\"M45.1857 44.4548\nL44.6143 44.4548\n\" />\n<path d=\"M44.9000 44.4548\nL44.9000 43.4548\n\" />\n<path d=\"M44.9000 43.4548\nL44.8048 43.5977\n\" />\n<path d=\"M44.8048 43.5977\nL44.7095 43.6929\n\" />\n<path d=\"M44.7095 43.6929\nL44.6143 43.7405\n\" />\n</g><text x=\"35.9500\" y=\"36.5000\"\ntextLength=\"2.2500\" font-size=\"1.3333\" lengthAdjust=\"spacingAndGlyphs\"\ntext-anchor=\"middle\" opacity=\"0\" stroke-opacity=\"0\">H6</text>\n<g class=\"stroked-text\"><desc>H6</desc>\n<path d=\"M35.1881 36.4548\nL35.1881 35.4548\n\" />\n<path d=\"M35.1881 35.9310\nL35.7595 35.9310\n\" />\n<path d=\"M35.7595 36.4548\nL35.7595 35.4548\n\" />\n<path d=\"M36.6643 35.4548\nL36.4738 35.4548\n\" />\n<path d=\"M36.4738 35.4548\nL36.3786 35.5024\n\" />\n<path d=\"M36.3786 35.5024\nL36.3310 35.5501\n\" />\n<path d=\"M36.3310 35.5501\nL36.2357 35.6929\n\" />\n<path d=\"M36.2357 35.6929\nL36.1881 35.8834\n\" />\n<path d=\"M36.1881 35.8834\nL36.1881 36.2643\n\" />\n<path d=\"M36.1881 36.2643\nL36.2357 36.3596\n\" />\n<path d=\"M36.2357 36.3596\nL36.2833 36.4072\n\" />\n<path d=\"M36.2833 36.4072\nL36.3786 36.4548\n\" />\n<path d=\"M36.3786 36.4548\nL36.5690 36.4548\n\" />\n<path d=\"M36.5690 36.4548\nL36.6643 36.4072\n\" />\n<path d=\"M36.6643 36.4072\nL36.7119 36.3596\n\" />\n<path d=\"M36.7119 36.3596\nL36.7595 36.2643\n\" />\n<path d=\"M36.7595 36.2643\nL36.7595 36.0262\n\" />\n<path d=\"M36.7595 36.0262\nL36.7119 35.9310\n\" />\n<path d=\"M36.7119 35.9310\nL36.6643 35.8834\n\" />\n<path d=\"M36.6643 35.8834\nL36.5690 35.8358\n\" />\n<path d=\"M36.5690 35.8358\nL36.3786 35.8358\n\" />\n<path d=\"M36.3786 35.8358\nL36.2833 35.8834\n\" />\n<path d=\"M36.2833 35.8834\nL36.2357 35.9310\n\" />\n<path d=\"M36.2357 35.9310\nL36.1881 36.0262\n\" />\n</g><path style=\"fill:none; \nstroke:#AFAFAF; stroke-width:0.0500; stroke-opacity:1; \nstroke-linecap:round; stroke-linejoin:round;fill:none\"\nd=\"M 53.0000,53.0000\n59.0000,53.0000\n59.0000,59.0000\n53.0000,59.0000\nZ\" /> \n<path style=\"fill:none; \nstroke:#AFAFAF; stroke-width:0.0500; stroke-opacity:1; \nstroke-linecap:round; stroke-linejoin:round;fill:none\"\nd=\"M 53.0000,21.0000\n59.0000,21.0000\n59.0000,27.0000\n53.0000,27.0000\nZ\" /> \n<path style=\"fill:none; \nstroke:#AFAFAF; stroke-width:0.0500; stroke-opacity:1; \nstroke-linecap:round; stroke-linejoin:round;fill:none\"\nd=\"M 21.0000,21.0000\n27.0000,21.0000\n27.0000,27.0000\n21.0000,27.0000\nZ\" /> \n<path style=\"fill:none; \nstroke:#AFAFAF; stroke-width:0.0500; stroke-opacity:1; \nstroke-linecap:round; stroke-linejoin:round;fill:none\"\nd=\"M 21.0000,53.0000\n27.0000,53.0000\n27.0000,59.0000\n21.0000,59.0000\nZ\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n<circle cx=\"51.8500\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n<circle cx=\"28.0000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n<circle cx=\"51.8500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n<circle cx=\"35.9500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"36.0000\" r=\"1.6000\" /> \n</g> \n</g><g class=\"adom-layer-crtydB\" data-side=\"B\">\n<title>SVG Image created as .tmpegHCtw.svg date 2026-09-01T03:31:12 </title>\n <desc>Image generated by PCBNEW </desc>\n<g style=\"fill:#000000; fill-opacity:1.0000;stroke:#000000; stroke-opacity:1.0000;\nstroke-linecap:round; stroke-linejoin:round;\"\n transform=\"translate(0 0) scale(1 1)\">\n</g>\n<g style=\"fill:none; \nstroke:#26E9FF; stroke-width:0.0500; stroke-opacity:1; \nstroke-linecap:round; stroke-linejoin:round;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"2.9900\" /> \n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"2.9900\" /> \n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"2.9900\" /> \n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"2.9900\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n<circle cx=\"51.8500\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n<circle cx=\"28.0000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n<circle cx=\"51.8500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n<circle cx=\"35.9500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"36.0000\" r=\"1.6000\" /> \n</g> \n</g><g class=\"adom-layer-crtydF\" data-side=\"F\">\n<title>SVG Image created as .tmpIw0SH7.svg date 2026-09-01T03:31:12 </title>\n <desc>Image generated by PCBNEW </desc>\n<g style=\"fill:#000000; fill-opacity:1.0000;stroke:#000000; stroke-opacity:1.0000;\nstroke-linecap:round; stroke-linejoin:round;\"\n transform=\"translate(0 0) scale(1 1)\">\n</g>\n<g style=\"fill:none; \nstroke:#FF26E2; stroke-width:0.0500; stroke-opacity:1; \nstroke-linecap:round; stroke-linejoin:round;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"2.9900\" /> \n<circle cx=\"51.8500\" cy=\"36.0000\" r=\"3.4500\" /> \n<circle cx=\"43.9000\" cy=\"28.0000\" r=\"3.4500\" /> \n<circle cx=\"35.9500\" cy=\"52.0000\" r=\"3.4500\" /> \n<circle cx=\"43.9000\" cy=\"52.0000\" r=\"3.4500\" /> \n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"2.9900\" /> \n<circle cx=\"28.0000\" cy=\"36.0000\" r=\"3.4500\" /> \n<circle cx=\"51.8500\" cy=\"52.0000\" r=\"3.4500\" /> \n<circle cx=\"51.8500\" cy=\"44.0000\" r=\"3.4500\" /> \n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"2.9900\" /> \n<circle cx=\"51.8500\" cy=\"28.0000\" r=\"3.4500\" /> \n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"2.9900\" /> \n<circle cx=\"35.9500\" cy=\"44.0000\" r=\"3.4500\" /> \n<circle cx=\"28.0000\" cy=\"52.0000\" r=\"3.4500\" /> \n<circle cx=\"28.0000\" cy=\"44.0000\" r=\"3.4500\" /> \n<circle cx=\"35.9500\" cy=\"28.0000\" r=\"3.4500\" /> \n<circle cx=\"28.0000\" cy=\"28.0000\" r=\"3.4500\" /> \n<circle cx=\"43.9000\" cy=\"36.0000\" r=\"3.4500\" /> \n<circle cx=\"43.9000\" cy=\"44.0000\" r=\"3.4500\" /> \n<circle cx=\"35.9500\" cy=\"36.0000\" r=\"3.4500\" /> \n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n<circle cx=\"51.8500\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n<circle cx=\"28.0000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n<circle cx=\"51.8500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n<circle cx=\"35.9500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"36.0000\" r=\"1.6000\" /> \n</g> \n</g><g class=\"adom-layer-edge\" data-side=\"X\">\n<title>SVG Image created as .tmpamHZn8.svg date 2026-09-01T03:31:13 </title>\n <desc>Image generated by PCBNEW </desc>\n<g style=\"fill:#000000; fill-opacity:1.0000;stroke:#000000; stroke-opacity:1.0000;\nstroke-linecap:round; stroke-linejoin:round;\"\n transform=\"translate(0 0) scale(1 1)\">\n</g>\n<g style=\"fill:none; \nstroke:#D0D2CD; stroke-width:0.0500; stroke-opacity:1; \nstroke-linecap:round; stroke-linejoin:round;\">\n<path d=\"M21.6000 20.0000 A1.6000 1.6000 0.0 0 0 20.0000 21.6000\" />\n<path d=\"M20.0000 58.4000\nL20.0000 21.6000\n\" />\n<path d=\"M21.6000 20.0000\nL58.4000 20.0000\n\" />\n<path d=\"M20.0000 58.4000 A1.6000 1.6000 0.0 0 0 21.6000 60.0000\" />\n<path d=\"M60.0000 21.6000 A1.6000 1.6000 0.0 0 0 58.4000 20.0000\" />\n<path d=\"M58.4000 60.0000\nL21.6000 60.0000\n\" />\n<path d=\"M60.0000 21.6000\nL60.0000 58.4000\n\" />\n<path d=\"M58.4000 60.0000 A1.6000 1.6000 0.0 0 0 60.0000 58.4000\" />\n</g>\n<g style=\"fill:#000000; fill-opacity:1.0000; stroke:none;\">\n<circle cx=\"56.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n<circle cx=\"51.8500\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"56.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n<circle cx=\"28.0000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"51.8500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"24.0000\" r=\"1.7250\" /> \n<circle cx=\"51.8500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"24.0000\" cy=\"56.0000\" r=\"1.7250\" /> \n<circle cx=\"35.9500\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"52.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"28.0000\" cy=\"28.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"36.0000\" r=\"1.6000\" /> \n<circle cx=\"43.9000\" cy=\"44.0000\" r=\"1.6000\" /> \n<circle cx=\"35.9500\" cy=\"36.0000\" r=\"1.6000\" /> \n</g> \n</g><g class=\"adom-overlay\"><g class=\"adom-pad\" data-net=\"1\" data-ref=\"MP4\" data-pad=\"1\" data-size=\"5.200×5.200\" data-shape=\"circle\" data-side=\"F\" style=\"--hlc:#c83434\"><circle class=\"pad-body\" cx=\"56.000\" cy=\"56.000\" r=\"2.600\" fill=\"transparent\"/><circle class=\"pad-hole\" cx=\"56.000\" cy=\"56.000\" r=\"1.725\" fill=\"transparent\" pointer-events=\"none\"/></g><g class=\"adom-pad\" data-net=\"2\" data-ref=\"MP2\" data-pad=\"1\" data-size=\"5.200×5.200\" data-shape=\"circle\" data-side=\"F\" style=\"--hlc:#c83434\"><circle class=\"pad-body\" cx=\"56.000\" cy=\"24.000\" r=\"2.600\" fill=\"transparent\"/><circle class=\"pad-hole\" cx=\"56.000\" cy=\"24.000\" r=\"1.725\" fill=\"transparent\" pointer-events=\"none\"/></g><g class=\"adom-pad\" data-net=\"3\" data-ref=\"MP1\" data-pad=\"1\" data-size=\"5.200×5.200\" data-shape=\"circle\" data-side=\"F\" style=\"--hlc:#c83434\"><circle class=\"pad-body\" cx=\"24.000\" cy=\"24.000\" r=\"2.600\" fill=\"transparent\"/><circle class=\"pad-hole\" cx=\"24.000\" cy=\"24.000\" r=\"1.725\" fill=\"transparent\" pointer-events=\"none\"/></g><g class=\"adom-pad\" data-net=\"4\" data-ref=\"MP3\" data-pad=\"1\" data-size=\"5.200×5.200\" data-shape=\"circle\" data-side=\"F\" style=\"--hlc:#c83434\"><circle class=\"pad-body\" cx=\"24.000\" cy=\"56.000\" r=\"2.600\" fill=\"transparent\"/><circle class=\"pad-hole\" cx=\"24.000\" cy=\"56.000\" r=\"1.725\" fill=\"transparent\" pointer-events=\"none\"/></g></g></g></svg>",meta:{"width_mm":42.0,"height_mm":42.0,"view_box":[19.0,19.0,42.0,42.0],"net_names":{"2":"unconnected-(MP2-Pad1)","1":"unconnected-(MP4-Pad1)","3":"unconnected-(MP1-Pad1)","4":"unconnected-(MP3-Pad1)"},"components":[{"reference":"MP4","value":"~","footprint":"Adom Footprint Library:MachinePinLargeShort","side":"top","mpn":"","lcsc":"","x":56.0,"y":56.0,"bbox":[50.8,50.8,61.2,61.2],"pad_count":1},{"reference":"H14","value":"MountingHole","footprint":"MountingHole:MountingHole_3.2mm_M3","side":"top","mpn":"","lcsc":"","x":51.85,"y":36.0,"bbox":[51.35,35.5,52.35,36.5],"pad_count":0},{"reference":"H9","value":"MountingHole","footprint":"MountingHole:MountingHole_3.2mm_M3","side":"top","mpn":"","lcsc":"","x":43.9,"y":28.0,"bbox":[43.4,27.5,44.4,28.5],"pad_count":0},{"reference":"H8","value":"MountingHole","footprint":"MountingHole:MountingHole_3.2mm_M3","side":"top","mpn":"","lcsc":"","x":35.95,"y":52.0,"bbox":[35.45,51.5,36.45,52.5],"pad_count":0},{"reference":"H12","value":"MountingHole","footprint":"MountingHole:MountingHole_3.2mm_M3","side":"top","mpn":"","lcsc":"","x":43.9,"y":52.0,"bbox":[43.4,51.5,44.4,52.5],"pad_count":0},{"reference":"MP2","value":"~","footprint":"Adom Footprint Library:MachinePinLargeShort","side":"top","mpn":"","lcsc":"","x":56.0,"y":24.0,"bbox":[50.8,18.8,61.2,29.2],"pad_count":1},{"reference":"H2","value":"MountingHole","footprint":"MountingHole:MountingHole_3.2mm_M3","side":"top","mpn":"","lcsc":"","x":28.0,"y":36.0,"bbox":[27.5,35.5,28.5,36.5],"pad_count":0},{"reference":"H16","value":"MountingHole","footprint":"MountingHole:MountingHole_3.2mm_M3","side":"top","mpn":"","lcsc":"","x":51.85,"y":52.0,"bbox":[51.35,51.5,52.35,52.5],"pad_count":0},{"reference":"H15","value":"MountingHole","footprint":"MountingHole:MountingHole_3.2mm_M3","side":"top","mpn":"","lcsc":"","x":51.85,"y":44.0,"bbox":[51.35,43.5,52.35,44.5],"pad_count":0},{"reference":"MP1","value":"~","footprint":"Adom Footprint Library:MachinePinLargeShort","side":"top","mpn":"","lcsc":"","x":24.0,"y":24.0,"bbox":[18.8,18.8,29.2,29.2],"pad_count":1},{"reference":"H13","value":"MountingHole","footprint":"MountingHole:MountingHole_3.2mm_M3","side":"top","mpn":"","lcsc":"","x":51.85,"y":28.0,"bbox":[51.35,27.5,52.35,28.5],"pad_count":0},{"reference":"MP3","value":"~","footprint":"Adom Footprint Library:MachinePinLargeShort","side":"top","mpn":"","lcsc":"","x":24.0,"y":56.0,"bbox":[18.8,50.8,29.2,61.2],"pad_count":1},{"reference":"H7","value":"MountingHole","footprint":"MountingHole:MountingHole_3.2mm_M3","side":"top","mpn":"","lcsc":"","x":35.95,"y":44.0,"bbox":[35.45,43.5,36.45,44.5],"pad_count":0},{"reference":"H4","value":"MountingHole","footprint":"MountingHole:MountingHole_3.2mm_M3","side":"top","mpn":"","lcsc":"","x":28.0,"y":52.0,"bbox":[27.5,51.5,28.5,52.5],"pad_count":0},{"reference":"H3","value":"MountingHole","footprint":"MountingHole:MountingHole_3.2mm_M3","side":"top","mpn":"","lcsc":"","x":28.0,"y":44.0,"bbox":[27.5,43.5,28.5,44.5],"pad_count":0},{"reference":"H5","value":"MountingHole","footprint":"MountingHole:MountingHole_3.2mm_M3","side":"top","mpn":"","lcsc":"","x":35.95,"y":28.0,"bbox":[35.45,27.5,36.45,28.5],"pad_count":0},{"reference":"H1","value":"MountingHole","footprint":"MountingHole:MountingHole_3.2mm_M3","side":"top","mpn":"","lcsc":"","x":28.0,"y":28.0,"bbox":[27.5,27.5,28.5,28.5],"pad_count":0},{"reference":"H10","value":"MountingHole","footprint":"MountingHole:MountingHole_3.2mm_M3","side":"top","mpn":"","lcsc":"","x":43.9,"y":36.0,"bbox":[43.4,35.5,44.4,36.5],"pad_count":0},{"reference":"H11","value":"MountingHole","footprint":"MountingHole:MountingHole_3.2mm_M3","side":"top","mpn":"","lcsc":"","x":43.9,"y":44.0,"bbox":[43.4,43.5,44.4,44.5],"pad_count":0},{"reference":"H6","value":"MountingHole","footprint":"MountingHole:MountingHole_3.2mm_M3","side":"top","mpn":"","lcsc":"","x":35.95,"y":36.0,"bbox":[35.45,35.5,36.45,36.5],"pad_count":0}],"track_count":0,"via_count":0,"pad_count":4,"inner_layers":[]}};</script>
<script>/* adom-2dboard interactive viewer core — shared by the live app (app.html) and the
* self-contained embed (embed.html). Consumes window.__PCB__ = { svg, meta }
* (embed) OR fetches /state (live). The whole reason this works: the SVG is
* self-rendered with data-net / data-ref / data-pad, so net/trace highlighting
* and component hover are pure DOM queries. */
(function () {
"use strict";
const $ = (s, r) => (r || document).querySelector(s);
const state = { meta: null, active: null, pinned: null, scale: 1, enrichCache: {}, side: "top", raised: null };
// ── mount ──────────────────────────────────────────────────────────────
function mount(svgText, meta) {
state.meta = meta || { net_names: {}, components: [] };
const box = $("#svgbox");
box.innerHTML = svgText;
const svg = box.querySelector("svg");
if (!svg) return;
svg.removeAttribute("width");
svg.removeAttribute("height");
svg.style.width = "100%";
svg.style.height = "100%";
state.svg = svg;
state.vb = (svg.getAttribute("viewBox") || "0 0 10 10").split(/\s+/).map(Number);
state.vb0 = state.vb.slice();
fitOverlayToBase(svg);
wireHover(svg);
wirePanZoom(svg);
buildLayerHUD();
buildNetList();
setStat();
updateChrome();
}
// Show the side panel (Layers/Nets) + stat only when there's room — i.e. the
// standalone tab or a fullscreened pane. In a small embedded thumbnail (APM
// board view) the chrome is hidden so it doesn't clutter the little preview.
function updateChrome() {
const big = innerWidth > 620;
// Header only when standalone/large; inside APM's iframe (embed) APM already
// wears the canonical header, so we'd double it. Panels still show when big.
document.body.classList.toggle("chrome", big && !window.__EMBED__);
["#side", "#stat"].forEach((s) => { const e = $(s); if (e) e.style.display = big ? "" : "none"; });
}
addEventListener("resize", updateChrome);
// ── observability: mirror console + errors to the server ring buffer (GET
// /console). Live-server only (an embed has no server). ──
function post(path, body) { try { fetch(path, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) }); } catch (e) {} }
function wireObservability() {
if (window.__EMBED__) return;
["log", "warn", "error"].forEach((k) => { const orig = console[k]; console[k] = function () { try { post("console", { level: k, msg: [].slice.call(arguments).map(String).join(" ") }); } catch (e) {} return orig.apply(console, arguments); }; });
addEventListener("error", (e) => post("console", { level: "error", msg: (e.message || "") + " @" + (e.filename || "") + ":" + (e.lineno || 0) }));
addEventListener("unhandledrejection", (e) => post("console", { level: "error", msg: "unhandledrejection: " + (e.reason && e.reason.message || e.reason) }));
}
// ── toasts: every action answers back + the AI's live narration channel ──
const TOAST_ICONS = {
success: '<svg class="ti" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>',
error: '<svg class="ti" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>',
info: '<svg class="ti" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M12 8h.01M11 12h1v4h1"/><circle cx="12" cy="12" r="9"/></svg>',
};
function toast(msg, type) {
type = type || "info";
const box = $("#toasts"); if (!box || !msg) return;
const el = document.createElement("div"); el.className = "toast " + type;
el.innerHTML = (TOAST_ICONS[type] || TOAST_ICONS.info) + '<span class="tmsg"></span>';
el.querySelector(".tmsg").textContent = msg;
while (box.children.length >= 4) box.removeChild(box.firstChild);
box.appendChild(el);
const kill = () => { el.classList.add("out"); setTimeout(() => el.remove(), 200); };
el.onclick = kill;
if (type !== "error") setTimeout(kill, 4000);
}
// Drain AI-pushed toasts + drive-commands (POST /ui/toast, /ui/cmd). Live only.
function pollUi() {
if (window.__EMBED__) return;
fetch("ui/pull").then((r) => r.json()).then((q) => {
(q.toasts || []).forEach((t) => toast(t.message, t.type));
(q.cmds || []).forEach(runCmd);
}).catch(() => {});
setTimeout(pollUi, 1000);
}
function runCmd(c) {
if (!c || !c.action) return;
if (c.action === "fit") { fit(); toast("Fit to view", "info"); }
else if (c.action === "net" && c.arg) { highlightNet(c.arg); toast("Net " + c.arg, "info"); }
else if (c.action === "clear") { clearNet && clearNet(); toast("Cleared", "info"); }
else if (c.action === "toast") { toast(c.arg || "", c.type || "info"); }
}
// ── tooltip: body-appended fixed div, 600ms reveal, clamped to viewport ──
let ttEl = null, ttTimer = null;
function showTip(target, e) {
const msg = target.getAttribute("data-tip"); if (!msg) return;
clearTimeout(ttTimer);
const cx = e.clientX, cy = e.clientY;
ttTimer = setTimeout(() => {
if (!ttEl) { ttEl = document.createElement("div"); ttEl.id = "tt"; document.body.appendChild(ttEl); }
ttEl.textContent = msg; ttEl.style.left = "-9999px"; ttEl.style.top = "0px"; ttEl.classList.add("show");
const M = 8, w = ttEl.offsetWidth, h = ttEl.offsetHeight;
let x = cx + 14, y = cy + 16;
if (x + w > innerWidth - M) x = cx - w - 14;
if (y + h > innerHeight - M) y = cy - h - 14;
x = Math.max(M, Math.min(x, innerWidth - w - M));
y = Math.max(M, Math.min(y, innerHeight - h - M));
ttEl.style.left = x + "px"; ttEl.style.top = y + "px";
}, 600);
}
function hideTip() { clearTimeout(ttTimer); if (ttEl) ttEl.classList.remove("show"); }
document.addEventListener("mouseover", (e) => { const t = e.target.closest && e.target.closest("[data-tip]"); if (t) showTip(t, e); });
document.addEventListener("mouseout", (e) => { if (e.target.closest && e.target.closest("[data-tip]")) hideTip(); });
document.addEventListener("mousedown", hideTip);
// ── header subject ──
function setSubject(name, meta) {
const n = $("#hdr-name"), m = $("#hdr-meta");
if (n) n.textContent = name || "";
if (m) m.textContent = meta || "";
}
function fitToast() { fit(); toast("Fit to view", "info"); }
// ── net + component highlighting ─────────────────────────────────────────
function highlightNet(net) {
if (net == null || net === "0" || net === "") return clearNet();
state.active = net;
state.svg.classList.add("dim");
state.svg.querySelectorAll(".adom-overlay [data-net]").forEach((el) => {
el.classList.toggle("hl", el.getAttribute("data-net") === String(net));
});
const name = state.meta.net_names[net] || "(unnamed)";
const cnt = state.svg.querySelectorAll('.adom-overlay [data-net="' + net + '"]').length;
chip('<b>net</b> ' + escapeHtml(name) + ' <span class="muted">· ' + cnt + " items</span>");
document.querySelectorAll("#netlist .netrow").forEach((r) => r.classList.toggle("sel", r.getAttribute("data-net") === String(net)));
}
function clearNet() {
if (state.pinned != null) return; // keep pinned net lit
state.active = null;
state.svg.classList.remove("dim");
state.svg.querySelectorAll(".hl").forEach((el) => el.classList.remove("hl"));
document.querySelectorAll("#netlist .netrow.sel").forEach((r) => r.classList.remove("sel"));
chip("");
}
// gentle single-pad highlight (hover)
function padHl(pad) { clearPadHl(); pad.classList.add("pad-hl"); }
function clearPadHl() { state.svg.querySelectorAll(".pad-hl").forEach((el) => el.classList.remove("pad-hl")); }
// ── CROSS-PROBE: click a part here → highlight the SAME reference in the sibling
// schematic view (and vice-versa), relayed by the host (APM) between the two
// same-origin iframes. Keyed by the component reference (both views tag by it). ──
function xpost(ref) { try { if (window.parent && window.parent !== window) window.parent.postMessage({ adomXprobe: true, ref: ref }, "*"); } catch (e) {} }
function clearComp() { if (state.svg) state.svg.querySelectorAll(".comp-xhl").forEach((el) => el.classList.remove("comp-xhl")); }
function highlightComp(ref) {
clearComp();
if (!state.svg || !ref) return;
state.svg.querySelectorAll('[data-ref="' + cssEsc(ref) + '"]').forEach((el) => el.classList.add("comp-xhl"));
}
// union bbox of the component's pads (svg user units = board mm), for recenter
function compBBox(ref) {
let mnx = 1e9, mny = 1e9, mxx = -1e9, mxy = -1e9, found = false;
state.svg.querySelectorAll('[data-ref="' + cssEsc(ref) + '"]').forEach((el) => {
try { const b = el.getBBox(); if (b.width || b.height) { mnx = Math.min(mnx, b.x); mny = Math.min(mny, b.y); mxx = Math.max(mxx, b.x + b.width); mxy = Math.max(mxy, b.y + b.height); found = true; } } catch (e) {}
});
return found ? [mnx, mny, mxx, mxy] : null;
}
function centerOn(ref) { const bb = compBBox(ref); if (bb) frameBBox(bb); }
// Zoom-to-fit a bbox: frame the part with margin (matching the viewport aspect),
// keep some context for tiny parts, never zoom out past the whole board.
function frameBBox(bb) {
if (!bb || !state.svg) return;
const x0 = bb[0], y0 = bb[1], x1 = bb[2], y1 = bb[3], cx = (x0 + x1) / 2, cy = (y0 + y1) / 2;
const rc = state.svg.getBoundingClientRect();
const aspect = (rc.width && rc.height) ? rc.width / rc.height : ((state.vb0[2] / state.vb0[3]) || 1);
let span = Math.max(x1 - x0, y1 - y0, 0.3) * 3.2; // the part fills ~1/3 of the short side
span = Math.max(span, 8); // don't over-zoom a tiny footprint
let w = aspect >= 1 ? span * aspect : span;
let h = aspect >= 1 ? span : span / aspect;
if (w > state.vb0[2] && h > state.vb0[3]) { w = state.vb0[2]; h = state.vb0[3]; }
state.vb = [cx - w / 2, cy - h / 2, w, h];
state.svg.setAttribute("viewBox", state.vb.join(" "));
}
function xclear() { try { if (window.parent && window.parent !== window) window.parent.postMessage({ adomXprobe: true, clear: true }, "*"); } catch (e) {} }
// Apply a cross-probe FROM the schematic view: HIGHLIGHT ONLY + recenter (no info
// card — the card shows only when the user clicks a part IN this app). No re-emit.
function applyXprobe(ref) { if (!ref) return; highlightComp(ref); centerOn(ref); }
// deselect propagated from the sibling view → clear our cross-probe highlight
function applyXclear() { clearComp(); }
addEventListener("message", (e) => { const d = e.data; if (!d || !d.adomXprobe) return; if (d.clear) applyXclear(); else if (d.ref) applyXprobe(d.ref); });
function wireHover(svg) {
// HOVER: pads + vias. Highlight the element + show its info. No net/fill lighting.
svg.addEventListener("mousemove", (e) => {
const pad = e.target.closest(".adom-pad");
if (pad) { padHl(pad); showPadCard(pad, e); return; }
const via = e.target.closest(".adom-via");
if (via) { padHl(via); showViaCard(via, e); return; }
clearPadHl(); hideCard();
});
svg.addEventListener("mouseleave", () => { clearPadHl(); hideCard(); });
// CLICK: light up the whole net (the "fill") + show fill info. Toggle off on
// empty click or clicking the same net again.
svg.addEventListener("click", (e) => {
// A pan-drag ends with mouseup then click; if the pointer actually moved,
// this "click" is the tail of a drag — don't highlight where the drag stopped.
if (svg.__panned) { svg.__panned = false; return; }
const t = e.target.closest("[data-net]");
// click empty space = deselect here AND in the sibling schematic view
if (!t) { state.pinned = null; clearNet(); clearComp(); xclear(); return; }
const net = t.getAttribute("data-net");
state.pinned = state.pinned === net ? null : net;
if (state.pinned) highlightNet(net); else clearNet();
// Net highlighting is NET-ONLY: do NOT add the component teal outline (comp-xhl)
// here — it competes with the net highlight. We still cross-probe the part to the
// schematic so it follows along; explicit component selection (and its teal
// outline in this view) comes from the schematic side via applyXprobe.
const ref = t.getAttribute("data-ref");
if (ref) xpost(ref);
});
}
// ── pad info card (with async enrichment for the parent part) ─────────────
function showPadCard(pad, e) {
const ref = pad.getAttribute("data-ref");
const num = pad.getAttribute("data-pad");
const net = pad.getAttribute("data-net");
const c = (state.meta.components || []).find((k) => k.reference === ref) || {};
const card = $("#card");
const mpn = c.mpn || "";
const netName = (state.meta.net_names || {})[net] || (net === "0" ? "(no net)" : "net " + net);
const rows = [];
rows.push('<div class="ci-h"><span class="ci-ref">' + escapeHtml(ref || "?") + ' · pad ' + escapeHtml(num || "?") +
'</span><span class="ci-val">' + escapeHtml(c.value || "") + "</span></div>");
rows.push(row("net", netName));
rows.push(row("pad", (pad.getAttribute("data-size") || "") + " · " + (pad.getAttribute("data-shape") || "") + " · " + (pad.getAttribute("data-side") === "B" ? "bottom" : "top")));
if (mpn) rows.push(row("mpn", mpn));
if (c.lcsc) rows.push(row("lcsc", c.lcsc));
rows.push('<div class="ci-enrich" id="ci-enrich">' + (mpn ? '<span class="muted">loading price · stock · wiki…</span>' : "") + "</div>");
card.innerHTML = rows.join("");
card.style.display = "block";
placeCard(card, e);
if (mpn) enrich(mpn, c.lcsc);
}
function showViaCard(via, e) {
const net = via.getAttribute("data-net");
const netName = (state.meta.net_names || {})[net] || (net === "0" ? "(no net)" : "net " + net);
const card = $("#card");
card.innerHTML = '<div class="ci-h"><span class="ci-ref">via</span></div>' + row("net", netName);
card.style.display = "block";
placeCard(card, e);
}
function row(k, v) { return '<div class="ci-row"><span class="ci-k">' + k + '</span><span class="ci-v">' + escapeHtml(String(v)) + "</span></div>"; }
function hideCard() { const c = $("#card"); if (c) c.style.display = "none"; }
function placeCard(card, e) {
const pad = 14, w = card.offsetWidth, h = card.offsetHeight;
let x = e.clientX + pad, y = e.clientY + pad;
if (x + w > innerWidth) x = e.clientX - w - pad;
if (y + h > innerHeight) y = e.clientY - h - pad;
card.style.left = Math.max(4, x) + "px";
card.style.top = Math.max(4, y) + "px";
}
function enrich(mpn, lcsc) {
const key = mpn + "|" + (lcsc || "");
const render = (d) => {
const el = $("#ci-enrich");
if (!el || state.enrichKey !== key) return;
if (!d || d.error) { el.innerHTML = '<span class="muted">enrichment unavailable</span>'; return; }
const parts = [];
if (d.stock != null) parts.push('<span class="pill ' + (d.stock > 0 ? "ok" : "no") + '">' + (d.stock > 0 ? d.stock + " in stock" : "no stock") + "</span>");
if (d.price) parts.push('<span class="pill">' + escapeHtml(d.price) + "</span>");
if (d.tier) parts.push('<span class="pill tier">' + escapeHtml(d.tier) + "</span>");
if (d.popularity != null) parts.push('<span class="pill pop">★ ' + d.popularity + "</span>");
let html = '<div class="pills">' + parts.join("") + "</div>";
if (d.wiki_url) html += '<a class="ci-wiki" href="' + escapeHtml(d.wiki_url) + '" target="_blank" rel="noopener">wiki page ↗</a>';
el.innerHTML = html || '<span class="muted">no data</span>';
};
state.enrichKey = key;
if (state.enrichCache[key]) return render(state.enrichCache[key]);
if (typeof fetch !== "function" || window.__EMBED__) return render(null);
const url = "enrich?mpn=" + encodeURIComponent(mpn) + (lcsc ? "&lcsc=" + encodeURIComponent(lcsc) : "");
let tries = 0;
const go = () => fetch(url).then((r) => r.json()).then((d) => {
if (d && d.pending && tries++ < 20) { setTimeout(go, 1500); return; } // vendor lookup warming
state.enrichCache[key] = d; render(d);
}).catch(() => render(null));
go();
}
// When the visual base is the EDA's OWN exported SVG (Altium's render/pcb.svg),
// the base is in PDF pixel space and our overlay is in board mm — unrelated
// systems. Measure both with getBBox() (which accounts for any transforms the
// exporter baked in) and scale/translate the overlay onto the base.
function fitOverlayToBase(svg) {
const base = svg.querySelector(".adom-base");
const fit = svg.querySelector(".adom-fit");
if (!base || !fit) return;
let bb, ob;
try { bb = base.getBBox(); ob = fit.getBBox(); } catch (e) { return; }
if (!bb.width || !bb.height || !ob.width || !ob.height) return;
// uniform scale so the overlay can't shear relative to the render
const s = Math.min(bb.width / ob.width, bb.height / ob.height);
const tx = bb.x + (bb.width - ob.width * s) / 2 - ob.x * s;
const ty = bb.y + (bb.height - ob.height * s) / 2 - ob.y * s;
fit.setAttribute("transform", "translate(" + tx + " " + ty + ") scale(" + s + ")");
state.baseFit = s;
}
// ── pan / zoom (mutate viewBox) ───────────────────────────────────────────
function wirePanZoom(svg) {
svg.addEventListener("wheel", (e) => {
e.preventDefault();
const [x, y, w, h] = state.vb;
const f = e.deltaY > 0 ? 1.12 : 0.893;
const r = svg.getBoundingClientRect();
const mx = x + ((e.clientX - r.left) / r.width) * w;
const my = y + ((e.clientY - r.top) / r.height) * h;
const nw = Math.min(state.vb0[2] * 8, Math.max(state.vb0[2] / 200, w * f));
const nh = nw * (h / w);
state.vb = [mx - (mx - x) * (nw / w), my - (my - y) * (nh / h), nw, nh];
svg.setAttribute("viewBox", state.vb.join(" "));
}, { passive: false });
let drag = null;
svg.addEventListener("mousedown", (e) => { drag = { x: e.clientX, y: e.clientY, vb: state.vb.slice() }; svg.__panned = false; });
addEventListener("mouseup", () => (drag = null));
addEventListener("mousemove", (e) => {
if (!drag) return;
// Past a small threshold this is a real pan, not a jittery click — mark it so
// the trailing click doesn't net-highlight where the drag was released.
if (Math.abs(e.clientX - drag.x) + Math.abs(e.clientY - drag.y) > 4) svg.__panned = true;
const r = svg.getBoundingClientRect();
const dx = ((e.clientX - drag.x) / r.width) * drag.vb[2];
const dy = ((e.clientY - drag.y) / r.height) * drag.vb[3];
state.vb = [drag.vb[0] - dx, drag.vb[1] - dy, drag.vb[2], drag.vb[3]];
svg.setAttribute("viewBox", state.vb.join(" "));
});
}
function fit() { if (state.svg) { state.vb = state.vb0.slice(); state.svg.setAttribute("viewBox", state.vb.join(" ")); } }
// ── Layers HUD ────────────────────────────────────────────────────────────
// key = <g class="adom-layer-KEY">; label shown; on = visible by default.
// KiCad-native base layers (each a <g class="adom-layer-KEY"> wrapping the EDA SVG)
const LAYERS = [
["copperF", "Copper · Top", true], ["copperB", "Copper · Bot", true],
["maskF", "Mask · Top", true], ["maskB", "Mask · Bot", true],
["silkF", "Silk · Top", true], ["silkB", "Silk · Bot", true],
["fabF", "Fab · Top", true], ["fabB", "Fab · Bot", true],
["crtydF", "Courtyard · Top", false], ["crtydB", "Courtyard · Bot", false],
["edge", "Board edge", true],
];
const SIDED = ["copper", "mask", "silk", "fab", "crtyd"]; // bases with F/B groups
// canonical z-order (bottom→top): whole FAR side first, then whole NEAR side.
// Within a side: mask (under copper), copper, silk, fab, courtyard.
const STACK = ["mask", "copper", "silk", "fab", "crtyd"];
// Inner copper layers for THIS board, front->back e.g. ["In1.Cu","In2.Cu"].
// Comes from meta so a 2/4/6-layer board configures itself.
function innerLayers() {
return (state.meta && state.meta.inner_layers) || [];
}
const innerKey = (l) => "copper" + String(l).replace(/\.Cu$/, "");
// Full copper stack, FRONT -> BACK: copperF, copperIn1 .. copperInN, copperB.
// This is the physical depth order and drives both z-ordering and dimming.
function copperStack() {
return ["copperF", ...innerLayers().map(innerKey), "copperB"];
}
function order(side) {
const F = side !== "bottom";
const lo = (b) => (F ? b + "B" : b + "F"); // far
const hi = (b) => (F ? b + "F" : b + "B"); // near
// inner copper sits physically between the two sides. Deepest-from-the-viewer
// first: viewing top that's In(n)..In1; viewing bottom it reverses.
const inners = innerLayers().map(innerKey);
const mid = F ? inners.slice().reverse() : inners.slice();
return [...STACK.map(lo), ...mid, ...STACK.map(hi), "edge"];
}
function setActiveSide(side) {
state.side = side;
const svg = state.svg, flip = svg.querySelector(".adom-flip");
if (!flip) return;
order(side).forEach((k) => { const g = svg.querySelector(".adom-layer-" + k); if (g) flip.appendChild(g); });
const ov = svg.querySelector(".adom-overlay"); if (ov) flip.appendChild(ov); // overlay stays on top
// MIRROR the board about its vertical centre when viewing the bottom
const cx = parseFloat(flip.getAttribute("data-cx")) || 0;
if (side === "bottom") flip.setAttribute("transform", "translate(" + (2 * cx) + " 0) scale(-1 1)");
else flip.removeAttribute("transform");
// fade the FAR side back (like KiCad) so the near side reads clearly
const far = side === "bottom" ? "F" : "B", near = side === "bottom" ? "B" : "F";
SIDED.forEach((base) => {
const fg = svg.querySelector(".adom-layer-" + base + far); if (fg) fg.style.opacity = "0.28";
const ng = svg.querySelector(".adom-layer-" + base + near); if (ng) ng.style.opacity = "1";
});
// buried copper reads dimmed until explicitly raised
innerLayers().map(innerKey).forEach((k) => {
const g = svg.querySelector(".adom-layer-" + k); if (g) g.style.opacity = "0.28";
});
state.raised = null;
document.querySelectorAll("#sidebtns .sidebtn").forEach((b) => b.classList.toggle("on", b.dataset.side === side));
markRaised();
}
// Copper layers physically IN FRONT of `key`, from the current viewing side.
// Viewing top, in front of In2 = [In1, F]; viewing bottom it is [In1's far side…]
// i.e. the same stack walked from the other end.
function copperInFrontOf(key) {
const stack = state.side === "bottom" ? copperStack().slice().reverse() : copperStack();
const i = stack.indexOf(key);
return i <= 0 ? [] : stack.slice(0, i);
}
function markRaised() {
document.querySelectorAll("#layers .lyr").forEach((r) => {
r.classList.toggle("raised", r.dataset.key === state.raised);
});
}
function raise(key) {
const svg = state.svg, flip = svg.querySelector(".adom-flip"), g = svg.querySelector(".adom-layer-" + key);
if (!g || !flip) return;
// Raising the SAME layer twice restores the normal stack.
if (state.raised === key) { setActiveSide(state.side); return; }
flip.appendChild(g);
const ov = svg.querySelector(".adom-overlay"); if (ov) flip.appendChild(ov);
const cb = document.querySelector('#layers input[data-layer="' + key + '"]');
if (cb && !cb.checked) { cb.checked = true; g.style.display = ""; }
if (copperStack().indexOf(key) >= 0) {
// Bring this copper layer fully forward and dim everything stacked in front
// of it — the near outer layer AND any intermediate inner layers — so the
// raised layer reads clearly through the board.
g.style.opacity = "1";
copperInFrontOf(key).forEach((k) => {
const o = svg.querySelector(".adom-layer-" + k); if (o) o.style.opacity = "0.28";
});
state.raised = key;
markRaised();
}
}
// LAYERS + this board's inner copper, inserted right after Copper · Top so the
// panel reads in physical stack order (Top, In1, In2, Bot).
function layerRows() {
const inners = innerLayers().map((l) => [innerKey(l), "Copper · " + String(l).replace(/\.Cu$/, ""), true]);
if (!inners.length) return LAYERS;
const rows = LAYERS.slice();
const at = rows.findIndex(([k]) => k === "copperF");
rows.splice(at < 0 ? 0 : at + 1, 0, ...inners);
return rows;
}
function buildLayerHUD() {
const el = $("#layers"); if (!el) return;
el.innerHTML =
'<div id="sidebtns"><span class="lbl">Front side</span>' +
'<button class="sidebtn on" data-side="top">Top</button>' +
'<button class="sidebtn" data-side="bottom">Bottom</button></div>' +
layerRows().map(([k, label, on]) =>
'<div class="lyr" data-key="' + k + '"><input type="checkbox" data-layer="' + k + '"' + (on ? " checked" : "") +
'><span class="lyr-name" data-raise="' + k + '">' + label + "</span></div>"
).join("");
el.querySelectorAll("input").forEach((cb) => cb.addEventListener("change", () => {
const g = state.svg.querySelector(".adom-layer-" + cb.getAttribute("data-layer"));
if (g) g.style.display = cb.checked ? "" : "none";
}));
el.querySelectorAll(".lyr-name").forEach((n) => n.addEventListener("click", () => raise(n.dataset.raise)));
el.querySelectorAll("#sidebtns .sidebtn").forEach((b) => b.addEventListener("click", () => setActiveSide(b.dataset.side)));
// apply default order + hide default-off layers
setActiveSide("top");
layerRows().filter(([, , on]) => !on).forEach(([k]) => { const g = state.svg.querySelector(".adom-layer-" + k); if (g) g.style.display = "none"; });
}
function buildNetList() {
const el = $("#netlist"); if (!el) return;
const names = state.meta.net_names || {};
const rows = Object.keys(names).filter((k) => k !== "0" && names[k]).sort((a, b) => names[a].localeCompare(names[b]));
el.innerHTML = rows.map((id) => '<div class="netrow" data-net="' + id + '">' + escapeHtml(names[id]) + "</div>").join("") || '<div class="muted">no named nets</div>';
el.querySelectorAll(".netrow").forEach((r) => {
// click-to-toggle only (net/fill highlight is a click action, not hover)
r.addEventListener("click", () => { const n = r.getAttribute("data-net"); state.pinned = state.pinned === n ? null : n; state.pinned ? highlightNet(n) : clearNet(); });
});
}
function setStat() {
const m = state.meta, el = $("#stat");
const txt = (m.components ? m.components.length : 0) + " components · " + (m.track_count || 0) + " tracks · " + (Object.keys(m.net_names || {}).length) + " nets";
if (el) el.textContent = txt;
setSubject(m.name || "Board", txt);
}
// ── helpers ───────────────────────────────────────────────────────────────
function chip(html) { const c = $("#netchip"); if (!c) return; c.innerHTML = html; c.style.display = html ? "block" : "none"; }
function escapeHtml(s) { return String(s).replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ }[c])); }
function cssEsc(s) { return String(s).replace(/["\\]/g, "\\$&"); }
// ── boot ──────────────────────────────────────────────────────────────────
window.AdomPcb = { mount, fit, highlightNet, toast, runCmd };
function boot() {
$("#fitbtn") && $("#fitbtn").addEventListener("click", fitToast);
$("#hdr-fit") && $("#hdr-fit").addEventListener("click", fitToast);
if (window.__PCB__) { window.__EMBED__ = true; mount(window.__PCB__.svg, window.__PCB__.meta); return; }
// live mode: AI channel (toasts + drive cmds), console ring buffer, header version
wireObservability();
pollUi();
fetch("version").then((r) => r.json()).then((v) => { const el = $("#ai-ver"); if (el && v && v.version) el.textContent = "v" + v.version; }).catch(() => {});
fetch("state").then((r) => r.json()).then((s) => {
if (s && s.svg) mount(s.svg, s.meta || {});
else $("#svgbox").innerHTML = '<div class="empty">No board loaded. POST a .kicad_pcb to /load.</div>';
}).catch(() => { $("#svgbox").innerHTML = '<div class="empty">server unreachable</div>'; });
}
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", boot); else boot();
})();
</script>
</body>
</html>