12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RMCF0603FT6R80</title>
<style>
  * { margin: 0; padding: 0; box-sizing: border-box; }
  body {
    background: #0d1117;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    overflow: hidden;
    height: 100vh;
    display: flex;
    flex-direction: column;
    color: #e6edf3;
  }

  /* ── Top toolbar ── */
  .toolbar {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 8px 14px;
    background: #161b22;
    border-bottom: 1px solid #21262d;
    flex-shrink: 0;
    font-size: 13px;
  }
  .toolbar .title {
    font-weight: 600;
    color: #00b8b0;
    font-size: 15px;
    letter-spacing: 0.02em;
  }
  .toolbar .spacer { flex: 1; }
  .toolbar .sep {
    width: 1px;
    height: 22px;
    background: #21262d;
    margin: 0 4px;
    flex-shrink: 0;
  }
  .toolbar .btn-group {
    display: flex;
    align-items: center;
    gap: 2px;
    flex-shrink: 0;
  }
  .toolbar button {
    background: rgba(0, 184, 176, 0.12);
    border: 1px solid rgba(0, 184, 176, 0.25);
    border-radius: 4px;
    padding: 4px 10px;
    cursor: pointer;
    font-size: 12px;
    color: #8b949e;
    transition: all 0.15s;
  }
  .toolbar button:hover { background: rgba(0,184,176,0.18); color: #e6edf3; border-color: rgba(0,184,176,0.4); }
  .toolbar button:active { background: rgba(0,184,176,0.28); }
  .toolbar button.active { background: rgba(0,184,176,0.2); color: #00b8b0; border-color: rgba(0,184,176,0.4); }
  .toolbar .zoom-level {
    font-size: 11px;
    color: #8b949e;
    min-width: 44px;
    text-align: center;
    font-variant-numeric: tabular-nums;
  }

  /* ── Canvas (dark background) ── */
  .canvas-wrap {
    flex: 1;
    overflow: hidden;
    position: relative;
    cursor: grab;
    background-color: #0d1117;
    background-image:
      radial-gradient(ellipse at 50% 50%, rgba(0,184,176,0.03) 0%, transparent 70%),
      linear-gradient(rgba(255,255,255,0.015) 1px, transparent 1px),
      linear-gradient(90deg, rgba(255,255,255,0.015) 1px, transparent 1px);
    background-size: 100% 100%, 20px 20px, 20px 20px;
  }
  .canvas-wrap.dragging { cursor: grabbing; }
  .canvas-wrap.no-grid {
    background-image: radial-gradient(ellipse at 50% 50%, rgba(0,184,176,0.03) 0%, transparent 70%);
    background-size: 100% 100%;
  }

  .canvas {
    position: absolute;
    transform-origin: 0 0;
  }
  .canvas svg { display: block; }

  /* ── Tooltip ── */
  .tooltip {
    display: none;
    position: fixed;
    background: #21262d;
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    color: #e6edf3;
    padding: 10px 14px;
    border-radius: 10px;
    border: 1px solid rgba(0, 184, 176, 0.25);
    font-size: 12px;
    line-height: 1.6;
    pointer-events: none;
    user-select: none;
    z-index: 100;
    max-width: 300px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.4);
    transition: border-color 0.15s;
  }
  .tooltip.visible { display: block; }
  .tooltip.sticky {
    pointer-events: auto;
    user-select: text;
    border-color: #00e6dc;
    box-shadow: 0 8px 32px rgba(0,184,176,0.15), 0 8px 24px rgba(0,0,0,0.4);
  }
  .tooltip .tt-name { font-weight: 700; font-size: 15px; color: #00e6dc; }
  .tooltip .tt-pin { color: #8b949e; font-size: 11px; margin-left: 4px; }
  .tooltip .tt-type {
    display: inline-block;
    margin-top: 4px;
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 11px;
    font-weight: 500;
  }
  .tooltip .tt-desc {
    margin-top: 6px;
    padding-top: 6px;
    border-top: 1px solid #21262d;
    color: #8b949e;
    font-size: 11px;
    line-height: 1.5;
  }
  .tooltip .tt-source {
    margin-top: 6px;
    padding-top: 6px;
    border-top: 1px solid #21262d;
    font-size: 10px;
    color: #484f58;
  }
  .tooltip .tt-source a { color: #00b8b0; text-decoration: none; }
  .tooltip .tt-source a:hover { text-decoration: underline; }
  .tooltip .tt-source-label { color: #E6B450; font-weight: 600; }
  .tooltip .tt-hint {
    margin-top: 6px;
    font-size: 9px;
    color: #484f58;
    font-style: italic;
  }
  .tooltip .tt-group-name { font-weight: 700; font-size: 14px; color: #E6B450; }
  .tooltip .tt-group-badge {
    display: inline-block;
    margin-top: 4px;
    padding: 2px 8px;
    border-radius: 4px;
    font-size: 11px;
    font-weight: 500;
    background: rgba(230,180,80,0.15);
    color: #FFD180;
    border: 1px solid rgba(230,180,80,0.3);
  }

  /* ── Wiki edit button ── */
  .edit-btn {
    position: absolute; top: 10px; right: 10px; z-index: 100;
    display: flex; align-items: center; gap: 6px;
    padding: 5px 10px;
    background: rgba(22,27,34,0.75);
    backdrop-filter: blur(8px);
    border: 1px solid rgba(230,180,80,0.2);
    border-radius: 6px;
    color: #8b949e; font-size: 11px;
    cursor: pointer; text-decoration: none;
    transition: all 0.2s; opacity: 0.7;
  }
  .edit-btn:hover {
    opacity: 1;
    background: rgba(230,180,80,0.12);
    border-color: rgba(230,180,80,0.4);
    color: #FFD180;
  }
  .edit-btn svg { width: 12px; height: 12px; }

  /* Regen prompt modal */
  .regen-overlay {
    display: none;
    position: fixed; inset: 0;
    background: rgba(0,0,0,0.6);
    z-index: 2000;
    align-items: center; justify-content: center;
  }
  .regen-overlay.visible { display: flex; }
  .regen-modal {
    background: undefined;
    border: 1px solid undefined;
    border-radius: 10px;
    padding: 20px 24px;
    max-width: 520px;
    width: 90%;
    box-shadow: 0 12px 40px rgba(0,0,0,0.5);
  }
  .regen-modal h3 {
    margin: 0 0 12px;
    font-size: 14px;
    font-weight: 600;
    color: undefined;
  }
  .regen-modal .prompt-text {
    background: undefined;
    border: 1px solid undefined;
    border-radius: 6px;
    padding: 12px 14px;
    font-size: 13px;
    line-height: 1.5;
    color: undefined;
    white-space: pre-wrap;
    word-break: break-word;
    user-select: all;
  }
  .regen-modal .modal-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 14px;
  }
  .regen-modal .copy-status {
    font-size: 11px;
    color: undefined;
  }
  .regen-modal .copy-status.copied { color: undefined; }
  .regen-modal .modal-btns { display: flex; gap: 8px; }
  .regen-modal .modal-btns button {
    padding: 6px 14px;
    border-radius: 6px;
    border: 1px solid undefined;
    background: undefined;
    color: undefined;
    font-size: 12px;
    cursor: pointer;
  }
  .regen-modal .modal-btns button:hover { background: rgba(255,255,255,0.06); }
  .regen-modal .modal-btns .copy-btn {
    background: rgba(0,230,220,0.12);
    border-color: rgba(0,230,220,0.3);
    color: undefined;
  }
  .regen-modal .modal-btns .copy-btn:hover { background: rgba(0,230,220,0.22); }

  /* Pin highlight on hover */
  .canvas svg .pin-highlight path, .canvas svg path.pin-highlight { stroke: #00e6dc !important; }
  .canvas svg .group-highlight path { stroke: #FFD180 !important; }

  /* Discrete pin label highlight (injected <text> elements) */
  .canvas svg text.pin-label-highlight { fill: #00e6dc !important; }
  .canvas svg text.pin-num-highlight { fill: #00e6dc !important; }

  /* Discrete pin hit zone outline (visible on hover) */
  .canvas svg rect.pin-hit-zone { fill: rgba(0,230,220,0.06); stroke: #00e6dc; stroke-width: 0.12; stroke-dasharray: 0.3,0.2; stroke-opacity: 0.5; pointer-events: all; cursor: pointer; rx: 0.3; }
  .canvas svg rect.pin-hit-zone:hover { fill: rgba(0,230,220,0.12); stroke-opacity: 0.8; stroke-width: 0.15; }

  /* ── Bottom panel ── */
  .bottom-panel {
    background: #161b22;
    border-top: 1px solid #21262d;
    flex-shrink: 0;
  }
  .info-row {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 1px 0;
    padding: 5px 8px;
    font-size: 10px;
  }
  .info-item {
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
    padding: 1px 0;
    margin-right: 8px;
  }
  .info-item::after {
    content: '';
    display: inline-block;
    width: 1px;
    height: 10px;
    background: #21262d;
    margin-left: 8px;
    vertical-align: middle;
  }
  .info-item:last-child::after { display: none; }
  .info-item:last-child { margin-right: 0; }
  .info-label {
    color: #484f58;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    font-size: 9px;
  }
  .info-value { color: #8b949e; }
  .info-value a { color: #00b8b0; text-decoration: none; }
  .info-value a:hover { text-decoration: underline; }
  .info-divider { height: 1px; background: #21262d; margin: 0 8px; }
  .coord-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 4px 8px;
    font-size: 10px;
    color: #8b949e;
    font-variant-numeric: tabular-nums;
  }
</style>
</head>
<body>

<div class="toolbar">
  <span class="title">RMCF0603FT6R80</span>
  <div class="spacer"></div>
  <div class="btn-group">
    <button onclick="zoomOut()">&#x2212;</button>
    <span class="zoom-level" id="zoom-level">100%</span>
    <button onclick="zoomIn()">&#x2b;</button>
  </div>
  <div class="sep"></div>
  <button onclick="zoomFit()">Fit</button>
  <button onclick="zoomReset()">1:1</button>
  <button onclick="toggleGrid()" id="grid-btn" class="active">Grid</button>
  <button onclick="toggleBBox()" id="bbox-btn" class="active">BBox</button>
  <button onclick="toggleHits()" id="hits-btn">Hits</button>
  <div class="sep"></div>
  <button onclick="showRegenPrompt()" title="Show regeneration prompt to paste into Claude Code">Regen</button>
</div>

<div class="regen-overlay" id="regen-overlay" onclick="if(event.target===this)closeRegenPrompt()">
  <div class="regen-modal">
    <h3>Regeneration Prompt</h3>
    <div class="prompt-text" id="regen-prompt-text"></div>
    <div class="modal-footer">
      <span class="copy-status" id="regen-copy-status">Copied to clipboard</span>
      <div class="modal-btns">
        <button onclick="closeRegenPrompt()">Close</button>
        <button class="copy-btn" onclick="copyRegenText()">Copy</button>
      </div>
    </div>
  </div>
</div>

<div class="canvas-wrap" id="canvas-wrap">
  <a class="edit-btn" href="https://coder.john-service-wiki-a054078430ce38e4.containers.adom.inc/proxy/8785/wiki/components/rmcf0603ft6r80/edit?field=content" target="_blank" title="Suggest corrections to this symbol on the Adom Wiki">
    <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
      <path d="M11.5 1.5l3 3L5 14H2v-3L11.5 1.5z"/>
      <path d="M9.5 3.5l3 3"/>
    </svg>
    Suggest edit
  </a>
  <div class="canvas" id="canvas">
    <svg
  xmlns:svg="http://www.w3.org/2000/svg"
  xmlns="http://www.w3.org/2000/svg"
  xmlns:xlink="http://www.w3.org/1999/xlink"
  xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
  version="1.1"
  width="5.0292mm" height="10.0584mm" viewBox="-4.7567 -4.0808 12.6320 18.2200">

<defs>
  <linearGradient id="adom-body-grad" x1="0" y1="0" x2="0" y2="1">
    <stop offset="0%" stop-color="#1a2332"/>
    <stop offset="100%" stop-color="#141c27"/>
  </linearGradient>
</defs>

<style>
  /* Stroked text groups (KiCad renders text as paths in <g class="stroked-text">) */
  g.stroked-text path {
    stroke: #ffffff !important;
    fill: #ffffff !important;
  }

  /* Hidden text elements (used for hit detection) */
  text[opacity="0"] {
    fill: #ffffff !important;
  }

  /* Pin highlight state (toggled by JS) */
  .pin-highlight path {
    stroke: #00e6dc !important;
  }
  .pin-highlight g.stroked-text path {
    stroke: #00e6dc !important;
  }

  /* Group label highlight */
  .group-highlight g.stroked-text path {
    stroke: #FFD180 !important;
  }
</style>

  <desc>Image generated by Eeschema-SVG </desc>
<g style="fill:none; fill-opacity:1.0000;stroke:#000000; stroke-opacity:1.0000;
stroke-linecap:round; stroke-linejoin:round;"
 transform="translate(0 0) scale(1 1)">
</g>
<g style="fill:none; 
stroke:#4a5568; stroke-width:0.2540; stroke-opacity:1; 
stroke-linecap:round; stroke-linejoin:round;">
<rect x="0.543300" y="2.489200" width="2.032000" height="5.080000" rx="0.000000" />
</g>
<g style="fill:none; 
stroke:#4a5568; stroke-width:0.1524; stroke-opacity:1; 
stroke-linecap:round; stroke-linejoin:round;">
<path d="M1.5593 2.4892
L1.5593 1.2192
" />
<path d="M1.5593 7.5692
L1.5593 8.8392
" />
</g>
<g style="fill:none; 
stroke:#484f58; stroke-width:0.1524; stroke-opacity:1; 
stroke-linecap:round; stroke-linejoin:round;">
<g transform="rotate(-90.000000 3.5913 5.0292)">

</g>
<g transform="rotate(-90.000000 1.5593 5.0292)">

</g>
</g> 
<text x="1.559" y="0.219" font-size="1.1" font-family="sans-serif" fill="#ffffff" text-anchor="middle" class="pin-num-label">1</text>
<text x="1.559" y="10.939" font-size="1.1" font-family="sans-serif" fill="#ffffff" text-anchor="middle" class="pin-num-label">2</text>
<rect x="-4.4567" y="-3.7807999999999997" width="12.032" height="17.619999999999997" fill="none" stroke="#00e6dc" stroke-width="0.1" stroke-dasharray="0.3,0.3" opacity="0.4" class="debug-bbox"/></svg>
  </div>
</div>

<div class="tooltip" id="tooltip"></div>

<div class="bottom-panel">
  <div class="info-row">
    <div class="info-item"><span class="info-label">Pins</span><span class="info-value">2</span></div><div class="info-item"><span class="info-label">Datasheet</span><span class="info-value"><a href="~" target="_blank">PDF</a></span></div><div class="info-item"><span class="info-label">Desc</span><span class="info-value">Resistor</span></div>
  </div>
  <div class="info-divider"></div>
  <div class="coord-row">
    <span id="cursor-pos">X: 0.00 &nbsp; Y: 0.00 mm</span>
    <span style="margin-left:auto;color:#00b8b0;font-weight:600;font-size:12px">SymView</span>
  </div>
</div>

<script>
  const pins = [{"name":"1","number":"1","type":"Passive","electricalType":"passive","color":"#78909c","desc":"","x":0,"y":3.81,"angle":270,"length":1.27},{"name":"2","number":"2","type":"Passive","electricalType":"passive","color":"#78909c","desc":"","x":0,"y":-3.81,"angle":90,"length":1.27}];
  const groups = [];
  const svgW = 12.632;
  const svgH = 18.22;
  const contentBounds = {"x":-4.4567,"y":-3.7807999999999997,"w":12.032,"h":17.619999999999997};
  const svgOffset = {"x":1.5593,"y":5.0292};
  const hidePinNames = true;
  const hidePinNumbers = true;
  const datasheetUrl = "~";
  const mfrName = "";
  const _symbolName = "RMCF0603FT6R80";
  const _symPath = "/home/adom/project/project-content/schematics/symbols/RMCF0603FT6R80/RMCF0603FT6R80.kicad_sym";

  const wrap = document.getElementById('canvas-wrap');
  const canvas = document.getElementById('canvas');
  const tooltip = document.getElementById('tooltip');
  const zoomLabel = document.getElementById('zoom-level');
  const cursorPos = document.getElementById('cursor-pos');

  let scale = 1, panX = 0, panY = 0;
  let isDragging = false;
  let dragStartX, dragStartY, panStartX, panStartY;
  let gridVisible = true;
  let stickyPin = null; // currently pinned tooltip

  const MM_TO_PX = 3.78;
  const svgPxW = svgW * MM_TO_PX;
  const svgPxH = svgH * MM_TO_PX;

  const svgEl = canvas.querySelector('svg');
  if (svgEl) {
    svgEl.setAttribute('width', svgPxW + 'px');
    svgEl.setAttribute('height', svgPxH + 'px');
  }

  function updateTransform() {
    canvas.style.transform = 'translate(' + panX + 'px,' + panY + 'px) scale(' + scale + ')';
    zoomLabel.textContent = Math.round(scale * 100) + '%';
  }

  function zoomFit() {
    const ww = wrap.clientWidth, wh = wrap.clientHeight;
    const pad = 40;
    // Read the bbox rect directly from the SVG DOM — it's already positioned correctly
    const bboxRect = svgEl ? svgEl.querySelector('.debug-bbox') : null;
    if (bboxRect) {
      const bb = bboxRect.getBBox();
      const vb = svgEl.viewBox.baseVal;
      // Convert SVG user-space coords to pixel coords within the SVG element
      const pxPerUnitX = svgPxW / vb.width;
      const pxPerUnitY = svgPxH / vb.height;
      const pxX = (bb.x - vb.x) * pxPerUnitX;
      const pxY = (bb.y - vb.y) * pxPerUnitY;
      const pxW = bb.width * pxPerUnitX;
      const pxH = bb.height * pxPerUnitY;
      scale = Math.min((ww - pad * 2) / pxW, (wh - pad * 2) / pxH, 10);
      if (!isFinite(scale) || scale <= 0) scale = 1;
      panX = (ww - pxW * scale) / 2 - pxX * scale;
      panY = (wh - pxH * scale) / 2 - pxY * scale;
    } else {
      scale = Math.min((ww - pad * 2) / svgPxW, (wh - pad * 2) / svgPxH, 10);
      if (!isFinite(scale) || scale <= 0) scale = 1;
      panX = (ww - svgPxW * scale) / 2;
      panY = (wh - svgPxH * scale) / 2;
    }
    updateTransform();
  }

  function zoomReset() {
    scale = 1;
    panX = (wrap.clientWidth - svgPxW) / 2;
    panY = (wrap.clientHeight - svgPxH) / 2;
    updateTransform();
  }

  function zoomIn() { zoomAt(wrap.clientWidth / 2, wrap.clientHeight / 2, 1.25); }
  function zoomOut() { zoomAt(wrap.clientWidth / 2, wrap.clientHeight / 2, 0.8); }

  function zoomAt(cx, cy, factor) {
    const ns = Math.max(0.1, Math.min(scale * factor, 20));
    const r = ns / scale;
    panX = cx - (cx - panX) * r;
    panY = cy - (cy - panY) * r;
    scale = ns;
    updateTransform();
  }

  function toggleGrid() {
    gridVisible = !gridVisible;
    wrap.classList.toggle('no-grid', !gridVisible);
    document.getElementById('grid-btn').classList.toggle('active', gridVisible);
  }

  let bboxVisible = true;
  function toggleBBox() {
    bboxVisible = !bboxVisible;
    const el = svgEl && svgEl.querySelector('.debug-bbox');
    if (el) el.setAttribute('opacity', bboxVisible ? '0.4' : '0');
    document.getElementById('bbox-btn').classList.toggle('active', bboxVisible);
  }

  let hitsVisible = false;
  function toggleHits() {
    hitsVisible = !hitsVisible;
    if (svgEl) {
      svgEl.querySelectorAll('.pin-hit-zone').forEach(r => {
        r.style.visibility = hitsVisible ? '' : 'hidden';
      });
    }
    document.getElementById('hits-btn').classList.toggle('active', hitsVisible);
  }

  // ── Regen prompt modal ──
  let _regenPrompt = '';
  function buildRegenPrompt() {
    const folder = _symPath.substring(0, _symPath.lastIndexOf('/'));
    return 'Regenerate the SymView for ' + _symbolName + '.\n\n'
      + 'Symbol: ' + _symPath + '\n'
      + 'Metadata: ' + folder + '/' + _symbolName + '-metadata.json\n\n'
      + 'Re-export the SVG via the KiCad service, regenerate the SymView HTML at the same path, '
      + 'and re-display it in the Adom app.';
  }
  function showRegenPrompt() {
    _regenPrompt = buildRegenPrompt();
    document.getElementById('regen-prompt-text').textContent = _regenPrompt;
    document.getElementById('regen-overlay').classList.add('visible');
    const status = document.getElementById('regen-copy-status');
    navigator.clipboard.writeText(_regenPrompt).then(() => {
      status.textContent = 'Copied to clipboard';
      status.classList.add('copied');
    }).catch(() => {
      status.textContent = 'Click Copy to copy';
      status.classList.remove('copied');
    });
  }
  function closeRegenPrompt() {
    document.getElementById('regen-overlay').classList.remove('visible');
  }
  function copyRegenText() {
    navigator.clipboard.writeText(_regenPrompt).then(() => {
      const status = document.getElementById('regen-copy-status');
      status.textContent = 'Copied to clipboard';
      status.classList.add('copied');
    });
  }

  // Mouse wheel zoom
  wrap.addEventListener('wheel', (e) => {
    e.preventDefault();
    const r = wrap.getBoundingClientRect();
    zoomAt(e.clientX - r.left, e.clientY - r.top, e.deltaY < 0 ? 1.12 : 0.89);
  }, { passive: false });

  // Pan
  wrap.addEventListener('mousedown', (e) => {
    if (e.button !== 0) return;
    isDragging = true;
    dragStartX = e.clientX; dragStartY = e.clientY;
    panStartX = panX; panStartY = panY;
    wrap.classList.add('dragging');
  });
  window.addEventListener('mousemove', (e) => {
    const r = wrap.getBoundingClientRect();
    const mx = (e.clientX - r.left - panX) / scale / MM_TO_PX;
    const my = (e.clientY - r.top - panY) / scale / MM_TO_PX;
    cursorPos.innerHTML = 'X: ' + mx.toFixed(2) + ' &nbsp; Y: ' + my.toFixed(2) + ' mm';
    if (!isDragging) return;
    panX = panStartX + (e.clientX - dragStartX);
    panY = panStartY + (e.clientY - dragStartY);
    updateTransform();
  });
  window.addEventListener('mouseup', () => { isDragging = false; wrap.classList.remove('dragging'); });

  // ── Pin highlighting ──
  const pinTextGroups = new Map();
  const pinWireGroups = new Map();
  if (svgEl) {
    // Build text group lookup
    svgEl.querySelectorAll('g.stroked-text').forEach(g => {
      const desc = g.querySelector('desc');
      if (desc) pinTextGroups.set(desc.textContent.trim(), g);
    });

    // Build wire group lookup using coordinate matching (not sibling navigation)
    if (svgOffset) {
      const ox = svgOffset.x, oy = svgOffset.y;
      // Collect all simple 2-point line paths in the SVG
      const allPaths = svgEl.querySelectorAll('path');
      const wirePaths = [];
      allPaths.forEach(p => {
        const d = p.getAttribute('d');
        if (!d) return;
        const m = d.match(/^M([\d.]+)\s+([\d.]+)\s+L([\d.]+)\s+([\d.]+)\s*$/);
        if (m) wirePaths.push({ el: p, x1: parseFloat(m[1]), y1: parseFloat(m[2]), x2: parseFloat(m[3]), y2: parseFloat(m[4]) });
      });

      for (const pin of pins) {
        const tipX = ox + pin.x;
        const tipY = oy - pin.y;
        const wireLen = pin.length || 2.54;
        const isHoriz = (pin.angle === 0 || pin.angle === 180);

        for (const w of wirePaths) {
          const dx = Math.abs(w.x2 - w.x1), dy = Math.abs(w.y2 - w.y1);
          const len = Math.sqrt(dx * dx + dy * dy);
          if (Math.abs(len - wireLen) > 0.05) continue;
          if (isHoriz && dy > 0.01) continue;
          if (!isHoriz && dx > 0.01) continue;
          // Check if one endpoint is near the pin tip
          const d1 = Math.hypot(w.x1 - tipX, w.y1 - tipY);
          const d2 = Math.hypot(w.x2 - tipX, w.y2 - tipY);
          if (Math.min(d1, d2) < 0.1) {
            // Found the wire path element directly
            pinWireGroups.set(pin.name, w.el);
            break;
          }
        }
      }
    }
  }

  function highlightPin(pin) {
    clearHighlight();
    // IC symbols: stroked-text groups
    [pin.name, pin.number].forEach(label => {
      const g = pinTextGroups.get(label);
      if (g) g.classList.add('pin-highlight');
    });
    const wireG = pinWireGroups.get(pin.name);
    if (wireG) wireG.classList.add('pin-highlight');
    // Discrete symbols: injected <text> labels
    if (svgEl && hidePinNames) {
      svgEl.querySelectorAll('text.pin-label').forEach(t => {
        if (t.textContent.trim() === pin.name) t.classList.add('pin-label-highlight');
      });
      svgEl.querySelectorAll('text.pin-num-label').forEach(t => {
        if (t.textContent.trim() === pin.number) t.classList.add('pin-num-highlight');
      });
    }
  }
  function clearHighlight() {
    if (svgEl) {
      svgEl.querySelectorAll('.pin-highlight').forEach(g => g.classList.remove('pin-highlight'));
      svgEl.querySelectorAll('.group-highlight').forEach(g => g.classList.remove('group-highlight'));
      svgEl.querySelectorAll('.pin-label-highlight').forEach(t => t.classList.remove('pin-label-highlight'));
      svgEl.querySelectorAll('.pin-num-highlight').forEach(t => t.classList.remove('pin-num-highlight'));
    }
  }

  // ── Tooltip positioning ──
  function positionTooltip(x, y) {
    const tw = tooltip.offsetWidth || 280;
    const th = tooltip.offsetHeight || 120;
    let tx = x + 14;
    let ty = y + 14;
    if (tx + tw > window.innerWidth - 14) tx = x - tw - 14;
    if (ty + th > window.innerHeight - 14) ty = y - th - 14;
    tx = Math.max(14, tx);
    ty = Math.max(14, ty);
    tooltip.style.left = tx + 'px';
    tooltip.style.top = ty + 'px';
  }

  // ── Source attribution HTML ──
  function sourceHtml() {
    if (!datasheetUrl || !mfrName) return '';
    return '<div class="tt-source"><span class="tt-source-label">Source:</span> <a href="' + datasheetUrl + '" target="_blank">' + mfrName + ' Datasheet</a></div>';
  }

  function hintHtml() {
    return '<div class="tt-hint">click to pin &middot; select text</div>';
  }

  function showTooltip(x, y, pin) {
    if (stickyPin) return; // don't override sticky
    highlightPin(pin);
    let html = '<div><span class="tt-name">' + pin.name + '</span><span class="tt-pin">Pin ' + pin.number + '</span></div>' +
      '<div><span class="tt-type" style="background:' + pin.color + '22; color:' + pin.color + '; border:1px solid ' + pin.color + '44">' + pin.type + '</span></div>';
    if (pin.desc) html += '<div class="tt-desc">' + pin.desc + '</div>';
    html += sourceHtml();
    html += hintHtml();
    tooltip.innerHTML = html;
    tooltip.classList.add('visible');
    tooltip.classList.remove('sticky');
    positionTooltip(x, y);
  }

  function showGroupTooltip(x, y, group) {
    if (stickyPin) return;
    clearHighlight();
    const g = pinTextGroups.get(group.name);
    if (g) g.classList.add('group-highlight');
    let html = '<div><span class="tt-group-name">' + group.name + '</span></div>' +
      '<div><span class="tt-group-badge">Function Group</span></div>';
    if (group.desc) html += '<div class="tt-desc">' + group.desc + '</div>';
    tooltip.innerHTML = html;
    tooltip.classList.add('visible');
    tooltip.classList.remove('sticky');
    positionTooltip(x, y);
  }

  function hideTooltip() {
    if (stickyPin) return;
    tooltip.classList.remove('visible');
    clearHighlight();
  }

  // ── Sticky tooltip (click-to-pin) ──
  function toggleSticky(pin, x, y) {
    if (stickyPin === pin) {
      // Unpin
      stickyPin = null;
      tooltip.classList.remove('sticky');
      hideTooltip();
    } else {
      // Pin
      stickyPin = pin;
      highlightPin(pin);
      let html = '<div><span class="tt-name">' + pin.name + '</span><span class="tt-pin">Pin ' + pin.number + '</span></div>' +
        '<div><span class="tt-type" style="background:' + pin.color + '22; color:' + pin.color + '; border:1px solid ' + pin.color + '44">' + pin.type + '</span></div>';
      if (pin.desc) html += '<div class="tt-desc">' + pin.desc + '</div>';
      html += sourceHtml();
      tooltip.innerHTML = html;
      tooltip.classList.add('visible', 'sticky');
      positionTooltip(x, y);
    }
  }

  // Dismiss sticky on click outside
  document.addEventListener('click', (e) => {
    if (!stickyPin) return;
    if (tooltip.contains(e.target)) return; // click inside tooltip
    if (e.target.closest('[data-pin]') || e.target.closest('[data-pin-num]') || e.target.closest('[data-pin-wire]')) return;
    stickyPin = null;
    tooltip.classList.remove('sticky', 'visible');
    clearHighlight();
  });

  wrap.addEventListener('mouseleave', hideTooltip);

  // ── Pin hit zones ──
  function setupPinHitZones() {
    if (!svgEl) return;

    if (hidePinNames) {
      // Discrete components (hidden pin names): compute hit zones from injected labels
      // Covers MOSFETs, resistors, etc. — standard KiCad discrete symbols hide pin names
      // but may leave pin numbers visible, so we check hidePinNames only
      setupDiscreteHitZones();
    } else {
      // Standard IC: use hidden text elements for hit detection
      setupTextHitZones();
    }
  }

  function setupTextHitZones() {
    // IC symbols: two hit zones per pin, derived from actual rendered text bounds (getBBox):
    //   1. INSIDE body — covers pin name text (bbox from stroked-text group)
    //   2. OUTSIDE body — covers pin wire + number area (bbox or coordinate-based)
    if (!svgOffset) return;
    const ox = svgOffset.x, oy = svgOffset.y;
    const pitch = 2.54;
    const zoneH = pitch * 0.95;
    const wireExt = 1.5;
    const pad = 0.3; // padding around text bbox

    // Build lookup: text content → stroked-text group bbox
    const textBBoxMap = {};
    svgEl.querySelectorAll('g.stroked-text').forEach(g => {
      const desc = g.querySelector('desc');
      if (desc) {
        try {
          const bbox = g.getBBox();
          if (bbox.width > 0 && bbox.height > 0) {
            const key = desc.textContent.trim();
            if (!textBBoxMap[key]) textBBoxMap[key] = [];
            textBBoxMap[key].push(bbox);
          }
        } catch (e) {}
      }
    });

    function addHitZone(hx, hy, hw, hh, pin) {
      const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
      rect.setAttribute('x', hx);
      rect.setAttribute('y', hy);
      rect.setAttribute('width', hw);
      rect.setAttribute('height', hh);
      rect.setAttribute('class', 'pin-hit-zone');
      rect.setAttribute('data-pin', pin.name);
      rect.setAttribute('rx', '0.3');
      svgEl.appendChild(rect);
      rect.addEventListener('mouseenter', (ev) => showTooltip(ev.clientX, ev.clientY, pin));
      rect.addEventListener('mousemove', (ev) => { if (!stickyPin) positionTooltip(ev.clientX, ev.clientY); });
      rect.addEventListener('mouseleave', hideTooltip);
      rect.addEventListener('click', (ev) => { ev.stopPropagation(); toggleSticky(pin, ev.clientX, ev.clientY); });
    }

    for (const pin of pins) {
      const tipSvgX = ox + pin.x;
      const tipSvgY = oy - pin.y;
      const wireLen = pin.length || 2.54;
      const isHoriz = (pin.angle === 0 || pin.angle === 180);

      // Pin name hitbox: use actual text bbox if available
      const nameBBoxes = textBBoxMap[pin.name];
      if (nameBBoxes && nameBBoxes.length > 0) {
        // Pick the bbox closest to this pin's body-edge position
        const bodyEdgeX = isHoriz
          ? (pin.angle === 0 ? tipSvgX + wireLen : tipSvgX - wireLen)
          : tipSvgX;
        const bodyEdgeY = isHoriz
          ? tipSvgY
          : (pin.angle === 90 ? tipSvgY - wireLen : tipSvgY + wireLen);
        let best = nameBBoxes[0];
        let bestDist = Infinity;
        for (const bb of nameBBoxes) {
          const cx = bb.x + bb.width / 2, cy = bb.y + bb.height / 2;
          const d = Math.hypot(cx - bodyEdgeX, cy - bodyEdgeY);
          if (d < bestDist) { bestDist = d; best = bb; }
        }
        addHitZone(best.x - pad, best.y - pad, best.width + pad * 2, best.height + pad * 2, pin);
      } else {
        // Fallback: fixed-width zone inside body
        const bodyDepth = 8;
        if (isHoriz) {
          const cy = tipSvgY - zoneH / 2;
          if (pin.angle === 0) addHitZone(tipSvgX + wireLen, cy, bodyDepth, zoneH, pin);
          else addHitZone(tipSvgX - wireLen - bodyDepth, cy, bodyDepth, zoneH, pin);
        } else {
          const cx = tipSvgX - zoneH / 2;
          if (pin.angle === 90) addHitZone(cx, tipSvgY - wireLen - bodyDepth, zoneH, bodyDepth, pin);
          else addHitZone(cx, tipSvgY + wireLen, zoneH, bodyDepth, pin);
        }
      }

      // Pin number hitbox: use actual text bbox if available
      const numBBoxes = textBBoxMap[pin.number];
      if (numBBoxes && numBBoxes.length > 0) {
        const tipDist = (bb) => Math.hypot(bb.x + bb.width / 2 - tipSvgX, bb.y + bb.height / 2 - tipSvgY);
        let best = numBBoxes[0], bestD = tipDist(best);
        for (const bb of numBBoxes) { const d = tipDist(bb); if (d < bestD) { bestD = d; best = bb; } }
        addHitZone(best.x - pad, best.y - pad, best.width + pad * 2, best.height + pad * 2, pin);
      } else {
        // Fallback: wire area
        if (isHoriz) {
          const cy = tipSvgY - zoneH / 2;
          if (pin.angle === 0) addHitZone(tipSvgX - wireExt, cy, wireLen + wireExt, zoneH, pin);
          else addHitZone(tipSvgX, cy, wireLen + wireExt, zoneH, pin);
        } else {
          const cx = tipSvgX - zoneH / 2;
          if (pin.angle === 90) addHitZone(cx, tipSvgY, zoneH, wireLen + wireExt, pin);
          else addHitZone(cx, tipSvgY - wireExt, zoneH, wireLen + wireExt, pin);
        }
      }
    }
  }

  function setupDiscreteHitZones() {
    // For discrete components (hidden pin names/numbers), create hit zones
    // using getBBox() on injected text labels for precise alignment.
    // Falls back to coordinate-based zones if text elements aren't found.
    if (!svgOffset) return;
    const ox = svgOffset.x, oy = svgOffset.y;
    const pad = 0.5; // generous padding for easy hover on small labels

    // Build lookup: text content → getBBox for injected labels
    const labelBBoxMap = {};
    const allTextEls = svgEl.querySelectorAll('text.pin-label, text.pin-num-label');
    allTextEls.forEach(t => {
      try {
        const bbox = t.getBBox();
        if (bbox.width > 0 && bbox.height > 0) {
          const key = t.textContent.trim();
          if (!labelBBoxMap[key]) labelBBoxMap[key] = [];
          labelBBoxMap[key].push(bbox);
        }
      } catch (e) {}
    });

    for (const pin of pins) {
      const tipSvgX = ox + pin.x;
      const tipSvgY = oy - pin.y;
      const wireLen = pin.length || 2.54;

      // Collect bounding boxes of this pin's name and number labels
      const bboxes = [];

      // Find name label bbox (closest to pin tip)
      const nameBBs = labelBBoxMap[pin.name];
      if (nameBBs) {
        let best = nameBBs[0], bestD = Infinity;
        for (const bb of nameBBs) {
          const d = Math.hypot(bb.x + bb.width / 2 - tipSvgX, bb.y + bb.height / 2 - tipSvgY);
          if (d < bestD) { bestD = d; best = bb; }
        }
        bboxes.push(best);
      }

      // Find number label bbox (closest to pin tip)
      const numBBs = labelBBoxMap[pin.number];
      if (numBBs) {
        let best = numBBs[0], bestD = Infinity;
        for (const bb of numBBs) {
          const d = Math.hypot(bb.x + bb.width / 2 - tipSvgX, bb.y + bb.height / 2 - tipSvgY);
          if (d < bestD) { bestD = d; best = bb; }
        }
        bboxes.push(best);
      }

      if (bboxes.length > 0) {
        // Union text bboxes into a single hit zone (just covers labels near the tip)
        let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
        for (const bb of bboxes) {
          minX = Math.min(minX, bb.x);
          minY = Math.min(minY, bb.y);
          maxX = Math.max(maxX, bb.x + bb.width);
          maxY = Math.max(maxY, bb.y + bb.height);
        }

        const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
        rect.setAttribute('x', minX - pad);
        rect.setAttribute('y', minY - pad);
        rect.setAttribute('width', maxX - minX + pad * 2);
        rect.setAttribute('height', maxY - minY + pad * 2);
        rect.setAttribute('class', 'pin-hit-zone');
        rect.setAttribute('data-pin-wire', pin.name);
        rect.setAttribute('rx', '0.3');
        svgEl.appendChild(rect);

        rect.addEventListener('mouseenter', (ev) => showTooltip(ev.clientX, ev.clientY, pin));
        rect.addEventListener('mousemove', (ev) => { if (!stickyPin) positionTooltip(ev.clientX, ev.clientY); });
        rect.addEventListener('mouseleave', hideTooltip);
        rect.addEventListener('click', (ev) => { ev.stopPropagation(); toggleSticky(pin, ev.clientX, ev.clientY); });
      } else {
        // Fallback: coordinate-based zone around wire area
        const perpHalf = 1.5;
        let hitX, hitY, hitW, hitH;
        switch (pin.angle) {
          case 0:   hitX = tipSvgX - 1; hitY = tipSvgY - perpHalf; hitW = wireLen + 1; hitH = perpHalf * 2; break;
          case 180: hitX = tipSvgX - wireLen; hitY = tipSvgY - perpHalf; hitW = wireLen + 1; hitH = perpHalf * 2; break;
          case 270: hitX = tipSvgX - perpHalf; hitY = tipSvgY; hitW = perpHalf * 2; hitH = wireLen + 1; break;
          case 90:  hitX = tipSvgX - perpHalf; hitY = tipSvgY - wireLen; hitW = perpHalf * 2; hitH = wireLen + 1; break;
          default:  hitX = tipSvgX - 2; hitY = tipSvgY - 2; hitW = 4; hitH = 4;
        }
        const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
        rect.setAttribute('x', hitX);
        rect.setAttribute('y', hitY);
        rect.setAttribute('width', hitW);
        rect.setAttribute('height', hitH);
        rect.setAttribute('class', 'pin-hit-zone');
        rect.setAttribute('data-pin-wire', pin.name);
        rect.setAttribute('rx', '0.3');
        svgEl.appendChild(rect);
        rect.addEventListener('mouseenter', (ev) => showTooltip(ev.clientX, ev.clientY, pin));
        rect.addEventListener('mousemove', (ev) => { if (!stickyPin) positionTooltip(ev.clientX, ev.clientY); });
        rect.addEventListener('mouseleave', hideTooltip);
        rect.addEventListener('click', (ev) => { ev.stopPropagation(); toggleSticky(pin, ev.clientX, ev.clientY); });
      }
    }
  }

  // ── Group label hit zones ──
  function setupGroupHitZones() {
    if (!svgEl) return;
    const texts = svgEl.querySelectorAll('text');
    texts.forEach(textEl => {
      const label = textEl.textContent.trim();
      const group = groups.find(g => g.name === label);
      if (!group) return;
      const fSize = parseFloat(textEl.getAttribute('font-size')) || 1;
      if (Math.abs(fSize - 1.016) > 0.1) return;

      const x = parseFloat(textEl.getAttribute('x')) || 0;
      const y = parseFloat(textEl.getAttribute('y')) || 0;
      const tLen = parseFloat(textEl.getAttribute('textLength')) || 6;
      const anchor = textEl.getAttribute('text-anchor') || 'start';

      const padX = fSize * 0.4;
      const padY = fSize * 0.15; // tight vertical padding to avoid spilling into pin zones
      let rx, rw = tLen + padX * 2, rh = fSize + padY * 2;
      if (anchor === 'end') rx = x - tLen - padX;
      else if (anchor === 'middle') rx = x - tLen / 2 - padX;
      else rx = x - padX;
      const ry = y - fSize - padY;

      const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
      rect.setAttribute('x', rx);
      rect.setAttribute('y', ry);
      rect.setAttribute('width', rw);
      rect.setAttribute('height', rh);
      rect.setAttribute('class', 'pin-hit-zone');
      rect.setAttribute('data-group', group.name);
      rect.setAttribute('rx', '0.3');
      svgEl.appendChild(rect);

      rect.addEventListener('mouseenter', (ev) => showGroupTooltip(ev.clientX, ev.clientY, group));
      rect.addEventListener('mousemove', (ev) => { if (!stickyPin) positionTooltip(ev.clientX, ev.clientY); });
      rect.addEventListener('mouseleave', hideTooltip);
    });
  }

  // Keyboard shortcuts
  window.addEventListener('keydown', (e) => {
    if (e.key === 'f' || e.key === 'F') zoomFit();
    else if (e.key === '1') zoomReset();
    else if (e.key === '+' || e.key === '=') zoomIn();
    else if (e.key === '-') zoomOut();
    else if (e.key === 'g' || e.key === 'G') toggleGrid();
    else if (e.key === 'Escape') {
      closeRegenPrompt();
      stickyPin = null;
      tooltip.classList.remove('sticky', 'visible');
      clearHighlight();
    }
  });

  // Auto-fit on window/container resize (e.g. iframe becoming visible)
  new ResizeObserver(() => zoomFit()).observe(wrap);

  // Init
  requestAnimationFrame(() => {
    zoomFit();
    setupPinHitZones();
    setupGroupHitZones();
    // Hide hit zones by default (user can toggle with Hits button)
    if (!hitsVisible && svgEl) {
      svgEl.querySelectorAll('.pin-hit-zone').forEach(r => { r.style.visibility = 'hidden'; });
    }
  });

  // Zoom to a named region of the symbol (for AI screenshot verification)
  // Regions: 'top', 'bottom', 'left', 'right', 'fit' (full symbol)
  function zoomToRegion(region) {
    if (!contentBounds) { zoomFit(); return; }
    const ww = wrap.clientWidth, wh = wrap.clientHeight;
    const svgEl = document.querySelector('#canvas svg');
    if (!svgEl) return;
    const vb = svgEl.viewBox.baseVal;
    const svgPxW = svgEl.clientWidth || svgEl.getBoundingClientRect().width;
    const svgPxH = svgEl.clientHeight || svgEl.getBoundingClientRect().height;
    const pxPerUnitX = svgPxW / vb.width;
    const pxPerUnitY = svgPxH / vb.height;

    // Content bounds in SVG user-space
    const cb = contentBounds;
    const cx = cb.x, cy = cb.y, cw = cb.w || cb.width, ch = cb.h || cb.height;
    // Region crop (SVG user-space coordinates)
    let rx, ry, rw, rh;
    const margin = 2; // mm padding
    if (region === 'top') {
      rx = cx - margin; ry = cy - margin; rw = cw + margin * 2; rh = Math.min(ch * 0.12, 50) + margin;
    } else if (region === 'bottom') {
      const cropH = Math.min(ch * 0.12, 50);
      ry = cy + ch - cropH - margin; rx = cx - margin; rw = cw + margin * 2; rh = cropH + margin * 2;
    } else if (region === 'top-left') {
      rx = cx - margin; ry = cy - margin; rw = cw * 0.4 + margin; rh = Math.min(ch * 0.12, 50) + margin;
    } else if (region === 'top-right') {
      rx = cx + cw * 0.6 - margin; ry = cy - margin; rw = cw * 0.4 + margin * 2; rh = Math.min(ch * 0.12, 50) + margin;
    } else if (region === 'bottom-left') {
      const cropH = Math.min(ch * 0.12, 50);
      ry = cy + ch - cropH - margin; rx = cx - margin; rw = cw * 0.4 + margin; rh = cropH + margin * 2;
    } else if (region === 'bottom-right') {
      const cropH = Math.min(ch * 0.12, 50);
      ry = cy + ch - cropH - margin; rx = cx + cw * 0.6 - margin; rw = cw * 0.4 + margin * 2; rh = cropH + margin * 2;
    } else if (region === 'left') {
      rx = cx - margin; ry = cy - margin; rw = cw * 0.35 + margin; rh = ch + margin * 2;
    } else if (region === 'right') {
      rx = cx + cw * 0.65 - margin; ry = cy - margin; rw = cw * 0.35 + margin * 2; rh = ch + margin * 2;
    } else {
      zoomFit(); return;
    }
    // Convert to pixel coords
    const pxX = (rx - vb.x) * pxPerUnitX;
    const pxY = (ry - vb.y) * pxPerUnitY;
    const pxW = rw * pxPerUnitX;
    const pxH = rh * pxPerUnitY;
    // Compute scale to fit region in viewport
    const ns = Math.min(ww / pxW, wh / pxH) * 0.92;
    scale = Math.max(0.1, Math.min(ns, 20));
    panX = ww / 2 - (pxX + pxW / 2) * scale;
    panY = wh / 2 - (pxY + pxH / 2) * scale;
    updateTransform();
  }

  // Screenshot capture handler for Adom Viewer mgmt relay
  // Send SVG data back to parent for rendering (avoids sandbox restrictions on Image loading)
  window.addEventListener('message', (e) => {
    // Zoom-to-region command (from AI via mgmt relay)
    if (e.data?.type === 'sym_zoom_region') {
      zoomToRegion(e.data.region || 'fit');
      return;
    }
    if (e.data?.type === 'mgmt_capture_request') {
      const reqId = e.data._reqId;
      try {
        const svg = document.querySelector('#canvas svg');
        if (!svg) { window.parent.postMessage({ type: 'mgmt_canvas_capture', _reqId: reqId, data: null }, '*'); return; }
        const clone = svg.cloneNode(true);
        // Inline pin-hit-zone styles so they're visible in standalone SVG
        clone.querySelectorAll('.pin-hit-zone').forEach(el => {
          el.setAttribute('fill', 'rgba(0,230,220,0.06)');
          el.setAttribute('stroke', '#00e6dc');
          el.setAttribute('stroke-width', '0.12');
          el.setAttribute('stroke-dasharray', '0.3,0.2');
          el.setAttribute('stroke-opacity', '0.5');
          el.setAttribute('rx', '0.3');
        });
        const vb = svg.getAttribute('viewBox');
        const parts = vb ? vb.split(/\s+/).map(Number) : [0, 0, 100, 100];
        let vbX = parts[0], vbY = parts[1], vbW = parts[2] || 100, vbH = parts[3] || 100;

        // Crop to visible viewport when zoomed/panned
        const svgPxW = svg.clientWidth || svg.getBoundingClientRect().width;
        const svgPxH = svg.clientHeight || svg.getBoundingClientRect().height;
        const ww = wrap.clientWidth, wh = wrap.clientHeight;
        if (scale !== 1 || panX !== (ww - svgPxW) / 2 || panY !== (wh - svgPxH) / 2) {
          const pxPerUnitX = svgPxW / vbW;
          const pxPerUnitY = svgPxH / vbH;
          // Visible viewport in pixel space of the SVG element
          const visLeft = -panX / scale;
          const visTop = -panY / scale;
          const visW = ww / scale;
          const visH = wh / scale;
          // Convert to SVG user-space
          vbX = vbX + visLeft / pxPerUnitX;
          vbY = vbY + visTop / pxPerUnitY;
          vbW = visW / pxPerUnitX;
          vbH = visH / pxPerUnitY;
        }

        clone.setAttribute('viewBox', vbX + ' ' + vbY + ' ' + vbW + ' ' + vbH);
        clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
        const xml = new XMLSerializer().serializeToString(clone);
        // Send SVG XML + dimensions to parent for canvas rendering
        window.parent.postMessage({
          type: 'mgmt_canvas_capture',
          _reqId: reqId,
          svgXml: xml,
          svgWidth: vbW,
          svgHeight: vbH,
          bgColor: '#0d1117'
        }, '*');
      } catch (err) {
        window.parent.postMessage({ type: 'mgmt_canvas_capture', _reqId: reqId, data: null }, '*');
      }
    }
  });
</script>
</body>
</html>