{"id":11409,"date":"2026-10-29T17:00:42","date_gmt":"2026-10-29T16:00:42","guid":{"rendered":"https:\/\/retrofixer.it\/?page_id=11409"},"modified":"2026-10-29T17:03:39","modified_gmt":"2026-10-29T16:03:39","slug":"c64-pcb-explorer","status":"publish","type":"page","link":"https:\/\/retrofixer.it\/en\/c64-pcb-explorer\/","title":{"rendered":"C64 PCB Explorer"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"11409\" class=\"elementor elementor-11409\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-89ccabe e-con-full e-flex e-con e-parent\" data-id=\"89ccabe\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-7bbd2f4 elementor-widget elementor-widget-html\" data-id=\"7bbd2f4\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<link rel=\"preconnect\" href=\"https:\/\/fonts.googleapis.com\">\n<link rel=\"preconnect\" href=\"https:\/\/fonts.gstatic.com\" crossorigin>\n<link href=\"https:\/\/fonts.googleapis.com\/css2?family=Press+Start+2P&family=VT323&family=Inter:wght@400;500;600;700&display=swap\" rel=\"stylesheet\">\n\n<style>\n.rfpcb{\n  --bg:#0f0c24; --bg2:#171338; --panel:#1d1846; --line:#342c74; --text:#eceaff; --muted:#aaa5d8;\n  --c64l:#7c70da; --mag:#e0398a; --yel:#ffd23f; --grn:#5ad15f; --cya:#3fd8ff; --red:#f94144;\n  font-family:'Inter',system-ui,-apple-system,Segoe UI,Roboto,sans-serif; color:var(--text); background:var(--bg); line-height:1.5;\n  padding:36px 0 50px;\n}\n.rfpcb *,.rfpcb *::before,.rfpcb *::after{box-sizing:border-box}\n.rfpcb h2,.rfpcb h3,.rfpcb p{margin:0;padding:0}\n.rfpcb h2,.rfpcb h3{font-family:'Inter',system-ui,sans-serif!important;letter-spacing:0;text-transform:none;font-weight:700}\n.rfpcb a{color:var(--cya)}\n.rfpcb button,.rfpcb input{font:inherit;color:inherit}\n.rfpcb button{cursor:pointer}\n.rfpcb [hidden]{display:none!important}\n.rfpcb .wrap{max-width:1400px;margin:0 auto;padding:0 16px}\n.rfpcb .head h2{font-family:'Press Start 2P',monospace!important;font-size:clamp(18px,3vw,28px);line-height:1.4;color:var(--yel);margin-bottom:12px}\n.rfpcb .head p{color:var(--muted);max-width:820px;font-size:16px}\n.rfpcb .bar{display:flex;flex-wrap:wrap;gap:10px 18px;align-items:center;margin:22px 0 12px}\n.rfpcb .seg{display:inline-flex;border:1px solid var(--line);border-radius:8px;overflow:hidden;background:var(--bg2)}\n.rfpcb .seg button{background:none;border:0;padding:8px 13px;font-size:14px;color:var(--muted);border-right:1px solid var(--line);white-space:nowrap}\n.rfpcb .seg button:last-child{border-right:0}\n.rfpcb .seg button[aria-pressed=true]{background:#342c74;color:#fff}\n.rfpcb .seg button:hover{color:#fff}\n.rfpcb .lay{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:14px;color:var(--muted)}\n.rfpcb .lay label{display:inline-flex;align-items:center;gap:6px;cursor:pointer}\n.rfpcb .lay input{accent-color:#7c70da;width:16px;height:16px;margin:0}\n.rfpcb .find{position:relative;flex:1 1 260px;max-width:420px}\n.rfpcb .find input{width:100%;background:var(--bg2);border:1px solid var(--line);border-radius:8px;padding:8px 12px;font-size:15px;outline:none}\n.rfpcb .find input:focus{border-color:#7c70da;background:#1a1638}\n.rfpcb .sugg{position:absolute;left:0;right:0;top:100%;margin-top:4px;background:var(--panel);border:1px solid var(--line);border-radius:8px;z-index:20;max-height:320px;overflow:auto;box-shadow:0 10px 30px rgba(0,0,0,.5)}\n.rfpcb .sugg button{display:flex;gap:10px;width:100%;text-align:left;background:none;border:0;padding:7px 12px;font-size:14px;align-items:baseline}\n.rfpcb .sugg button:hover,.rfpcb .sugg button.on{background:#342c74}\n.rfpcb .sugg .k{font-family:'VT323',monospace;font-size:13px;color:var(--yel);min-width:64px;text-transform:uppercase}\n.rfpcb .main{display:grid;grid-template-columns:minmax(0,1fr) 340px;gap:14px}\n.rfpcb .view{position:relative;height:min(74vh,780px);min-height:380px;background:#07060f;border:1px solid var(--line);border-radius:10px;overflow:hidden;touch-action:none}\n.rfpcb .view canvas{display:block;width:100%;height:100%;cursor:crosshair}\n.rfpcb .view.drag canvas{cursor:grabbing}\n.rfpcb .zoom{position:absolute;right:10px;top:10px;display:flex;flex-direction:column;gap:6px}\n.rfpcb .zoom button{width:38px;height:38px;border-radius:8px;border:1px solid var(--line);background:rgba(23,19,56,.92);font-size:20px;line-height:1;display:flex;align-items:center;justify-content:center}\n.rfpcb .zoom button:hover{background:#342c74}\n.rfpcb .side-tag{position:absolute;left:10px;top:10px;font-family:'VT323',monospace;font-size:19px;background:rgba(15,12,36,.85);border:1px solid var(--line);border-radius:6px;padding:1px 9px;color:var(--yel);pointer-events:none}\n.rfpcb .tip{position:absolute;pointer-events:none;background:rgba(15,12,36,.95);border:1px solid #7c70da;border-radius:6px;padding:5px 9px;font-size:13px;white-space:nowrap;z-index:5;max-width:90%;overflow:hidden;text-overflow:ellipsis}\n.rfpcb .tip b{color:var(--yel)}\n.rfpcb .load{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;font-family:'Press Start 2P',monospace;font-size:12px;color:var(--muted);background:#07060f}\n.rfpcb .info{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:16px;height:min(74vh,780px);min-height:380px;overflow:auto;font-size:14px}\n.rfpcb .info h3{font-size:19px;color:#fff;margin-bottom:4px;word-break:break-word}\n.rfpcb .info .sub{color:var(--muted);margin-bottom:12px}\n.rfpcb .info .lbl{font-family:'VT323',monospace;font-size:16px;color:var(--yel);text-transform:uppercase;margin:14px 0 6px;letter-spacing:.5px}\n.rfpcb .info ul{margin:0;padding-left:18px;color:var(--muted)}\n.rfpcb .info li{margin:3px 0}\n.rfpcb .rows{display:flex;flex-direction:column;gap:2px}\n.rfpcb .row{display:grid;grid-template-columns:62px 1fr;gap:8px;align-items:baseline;background:none;border:0;border-radius:5px;padding:4px 6px;text-align:left;width:100%;font-size:14px}\n.rfpcb .row.c3{grid-template-columns:38px minmax(0,.9fr) minmax(0,1.1fr)}\n.rfpcb button.row:hover{background:#342c74}\n.rfpcb .row .r{font-weight:600;color:#fff}\n.rfpcb .row .m{color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.rfpcb .row .n{color:var(--cya);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n.rfpcb .row.bad .r{color:var(--red)}\n.rfpcb .ov{text-decoration:overline}\n.rfpcb .warn{background:rgba(249,65,68,.12);border:1px solid rgba(249,65,68,.55);border-radius:8px;padding:9px 11px;margin:10px 0;color:#ffd0d0}\n.rfpcb .note{background:rgba(63,216,255,.08);border:1px solid rgba(63,216,255,.35);border-radius:8px;padding:9px 11px;margin:10px 0;color:#cdeffd}\n.rfpcb .back{background:none;border:1px solid var(--line);border-radius:6px;padding:3px 10px;font-size:13px;color:var(--muted);margin-bottom:10px}\n.rfpcb .back:hover{color:#fff;border-color:#7c70da}\n.rfpcb .chip{display:inline-block;font-family:'VT323',monospace;font-size:15px;border:1px solid var(--line);border-radius:4px;padding:0 6px;margin:0 4px 4px 0;color:var(--muted)}\n.rfpcb .sw{display:inline-block;width:12px;height:12px;border-radius:3px;vertical-align:-1px;margin-right:6px}\n.rfpcb .credits{margin-top:16px;color:var(--muted);font-size:13.5px;max-width:980px}\n.rfpcb .fs .view,.rfpcb .fs .info{height:calc(100vh - 150px)}\n.rfpcb.isfs{overflow:auto;padding:12px 0}\n@media(max-width:980px){\n  .rfpcb .main{grid-template-columns:1fr}\n  .rfpcb .view{height:62vh}\n  .rfpcb .info{height:auto;max-height:none}\n}\n@media(max-width:600px){\n  .rfpcb .seg button{padding:7px 9px;font-size:13px}\n  .rfpcb #rfpBoards{display:flex;width:100%}\n  .rfpcb #rfpBoards button{flex:1 1 0;white-space:normal;line-height:1.25;padding:7px 5px}\n  .rfpcb .head p{font-size:15px}\n}\n<\/style>\n\n<div class=\"rfpcb\" data-no-translation>\n <div class=\"wrap\">\n  <div class=\"head\">\n   <h2 data-t=\"title\">C64 PCB Explorer<\/h2>\n   <p data-t=\"intro\">Le schede madri del Commodore 64 ricostruite pin per pin. Clicca un pin, una pista o un componente: vedi subito dove va il segnale e tutti i pin a cui deve arrivare. Utile in riparazione per controllare la continuit\u00e0 e trovare piste interrotte.<\/p>\n  <\/div>\n  <div class=\"bar\">\n   <div class=\"seg\" id=\"rfpBoards\" role=\"group\"><\/div>\n   <div class=\"seg\" id=\"rfpSide\" role=\"group\">\n    <button data-s=\"F\" aria-pressed=\"true\" data-t=\"top\">Lato componenti<\/button>\n    <button data-s=\"B\" aria-pressed=\"false\" data-t=\"bottom\">Lato saldature<\/button>\n   <\/div>\n   <div class=\"find\">\n    <input id=\"rfpFind\" type=\"search\" autocomplete=\"off\" spellcheck=\"false\" placeholder=\"Cerca: U7, 6510, PHI2, U7.39\u2026\" data-tp=\"find\">\n    <div class=\"sugg\" id=\"rfpSugg\" hidden><\/div>\n   <\/div>\n  <\/div>\n  <div class=\"lay\" id=\"rfpLay\">\n   <label><input type=\"checkbox\" data-l=\"tr\" checked><span data-t=\"lTr\">Piste<\/span><\/label>\n   <label><input type=\"checkbox\" data-l=\"other\" checked><span data-t=\"lOther\">Piste dell'altro lato<\/span><\/label>\n   <label><input type=\"checkbox\" data-l=\"zone\" checked><span data-t=\"lZone\">Zone di rame<\/span><\/label>\n   <label><input type=\"checkbox\" data-l=\"silk\" checked><span data-t=\"lSilk\">Serigrafia<\/span><\/label>\n   <label><input type=\"checkbox\" data-l=\"ref\" checked><span data-t=\"lRef\">Sigle<\/span><\/label>\n   <label id=\"rfpPhotoL\" hidden><input type=\"checkbox\" data-l=\"photo\"><span data-t=\"lPhoto\">Foto della scheda originale<\/span><\/label>\n   <label><input type=\"checkbox\" data-l=\"err\" checked><span data-t=\"lErr\">Anomalie del disegno<\/span><\/label>\n  <\/div>\n  <div class=\"main\" style=\"margin-top:12px\">\n   <div class=\"view\" id=\"rfpView\">\n    <canvas id=\"rfpCanvas\" aria-label=\"C64 PCB\"><\/canvas>\n    <div class=\"side-tag\" id=\"rfpTag\"><\/div>\n    <div class=\"zoom\">\n     <button id=\"rfpIn\" aria-label=\"Zoom +\">+<\/button>\n     <button id=\"rfpOut\" aria-label=\"Zoom \u2212\">\u2212<\/button>\n     <button id=\"rfpFit\" aria-label=\"Fit\" title=\"\">\u2922<\/button>\n     <button id=\"rfpFs\" aria-label=\"Fullscreen\" title=\"\">\u26f6<\/button>\n    <\/div>\n    <div class=\"tip\" id=\"rfpTip\" hidden><\/div>\n    <div class=\"load\" id=\"rfpLoad\" data-t=\"loading\">Caricamento della scheda\u2026<\/div>\n   <\/div>\n   <aside class=\"info\" id=\"rfpInfo\" aria-live=\"polite\"><\/aside>\n  <\/div>\n  <p class=\"credits\" data-th=\"credits\">I disegni sono le repliche KiCad di <a href=\"https:\/\/github.com\/bwack\" target=\"_blank\" rel=\"noopener\">Bwack<\/a> (<a href=\"https:\/\/github.com\/bwack\/C64-250407-Replica-KiCad\" target=\"_blank\" rel=\"noopener\">250407<\/a>, <a href=\"https:\/\/github.com\/bwack\/KU-14194HB-RevB-KiCad\" target=\"_blank\" rel=\"noopener\">KU-14194HB<\/a>, <a href=\"https:\/\/github.com\/bwack\/C64C-250469-KiCAD-Replica\" target=\"_blank\" rel=\"noopener\">250469<\/a>), distribuite con licenza CERN OHL v1.2 e convertite da RetroFixer per questa pagina. Sono ricostruzioni fatte da appassionati: prima di intervenire su una scheda confrontale con lo schema Commodore originale.<\/p>\n <\/div>\n<\/div>\n\n<script>\n(function(){\n'use strict';\nvar EN = \/^en\/i.test(document.documentElement.lang || '');\nvar T = {\n it:{\n  title:'C64 PCB Explorer',\n  intro:'Le schede madri del Commodore 64 ricostruite pin per pin. Clicca un pin, una pista o un componente: vedi subito dove va il segnale e tutti i pin a cui deve arrivare. Utile in riparazione per controllare la continuit\u00e0 e trovare piste interrotte.',\n  top:'Lato componenti', bottom:'Lato saldature', topTag:'LATO COMPONENTI', bottomTag:'LATO SALDATURE (visto da sotto)',\n  find:'Cerca: U7, 6510, PHI2, U7.39\u2026', lTr:'Piste', lOther:\"Piste dell'altro lato\", lZone:'Zone di rame', lSilk:'Serigrafia', lRef:'Sigle',\n  lPhoto:'Foto della scheda originale', lErr:'Anomalie del disegno', loading:'Caricamento della scheda\u2026', loadErr:'Non riesco a caricare la scheda. Riprova tra poco.',\n  fit:'Adatta alla finestra', fs:'Schermo intero',\n  how:'Come si usa', h1:'Clicca un pin o una pista per evidenziare il segnale e vedere tutti i pin a cui arriva.',\n  h2:'Clicca il corpo di un chip per vedere la piedinatura e i segnali di ogni pin.',\n  h3:'Trascina per spostarti, usa la rotella o due dita per lo zoom. Esc toglie la selezione.',\n  h4:'Nella ricerca puoi scrivere la sigla (U7), il chip (6510, SID), un segnale (PHI2, ~RESET) o un pin preciso (U7.39).',\n  stats:'{c} componenti \u00b7 {p} pin \u00b7 {n} segnali', comp:'Componenti principali', checks:'Controllo del disegno',\n  dangle:'Piste che finiscono nel vuoto', splitN:'Segnali divisi in pi\u00f9 parti', noErr:'Nessuna anomalia trovata nel disegno.',\n  errNote:'Il controllo segue solo il rame disegnato nel progetto KiCad. Un\u2019anomalia pu\u00f2 essere un errore della replica oppure un collegamento fatto in un altro modo (ponticello, schermo metallico). Controlla sempre sullo schema originale.',\n  signal:'Segnale', pinsOn:'{n} pin collegati', pinsList:'Pin a cui arriva il segnale',\n  split:'Nel disegno questo segnale \u00e8 diviso in {n} parti: i pin cerchiati di rosso non sono collegati alle piste disegnate. La linea tratteggiata rossa indica il collegamento mancante pi\u00f9 corto.',\n  unnamed:'segnale senza nome', back:'\u2190 Indietro', component:'Componente', side:'Lato', sideF:'componenti', sideB:'saldature',\n  pin:'Pin', name:'Nome', sig:'Segnale', nc:'non collegato', fp:'Impronta', track:'pista', via:'via',\n  dangleOne:'Estremo di pista scollegato', dangleTxt:'Questa pista del segnale {s} finisce senza toccare pad, via o altre piste.',\n  legF:'pista lato componenti', legB:'pista lato saldature',\n  many:'Segnale molto diffuso: i pin sono raggruppati per componente.', through:'Prosegue attraverso', noRes:'Nessun risultato',\n  kComp:'chip', kNet:'segnale', kPin:'pin',\n  b250407:'250407 \u00b7 biscottone', bku:'KU-14194HB \u00b7 biscottone', b250469:'250469 \u00b7 C64C',\n  d250407:'La scheda del C64 \u00abbiscottone\u00bb del 1983-84 (PCB 250407). Replica KiCad di Bwack e Der Alte Bastler, rev. 1.3.',\n  dku:'La prima scheda del C64 (KU-14194HB, 1982). Replica KiCad di Bwack, rev. 1.4.',\n  d250469:'La scheda del C64C (250469 rev. 4) usata dal 1987. Replica KiCad di Bwack, rev. 1.1. Contiene anche le foto della scheda originale usate per la ricostruzione: attivale con \u00abFoto della scheda originale\u00bb.',\n  photoNote:'Foto attiva: il rame del disegno \u00e8 semitrasparente, cos\u00ec puoi confrontarlo con la scheda vera.'\n },\n en:{\n  title:'C64 PCB Explorer',\n  intro:'The Commodore 64 motherboards rebuilt pin by pin. Click a pin, a track or a component to see straight away where the signal goes and every pin it must reach. Handy during repairs to check continuity and find broken tracks.',\n  top:'Component side', bottom:'Solder side', topTag:'COMPONENT SIDE', bottomTag:'SOLDER SIDE (seen from below)',\n  find:'Search: U7, 6510, PHI2, U7.39\u2026', lTr:'Tracks', lOther:'Other side tracks', lZone:'Copper zones', lSilk:'Silkscreen', lRef:'References',\n  lPhoto:'Photo of the original board', lErr:'Drawing issues', loading:'Loading the board\u2026', loadErr:'Could not load the board. Please try again shortly.',\n  fit:'Fit to window', fs:'Full screen',\n  how:'How to use it', h1:'Click a pin or a track to highlight the signal and see every pin it reaches.',\n  h2:'Click the body of a chip to see its pinout and the signal on each pin.',\n  h3:'Drag to move around, use the wheel or two fingers to zoom. Esc clears the selection.',\n  h4:'In the search box you can type a reference (U7), a chip (6510, SID), a signal (PHI2, ~RESET) or a specific pin (U7.39).',\n  stats:'{c} components \u00b7 {p} pins \u00b7 {n} signals', comp:'Main components', checks:'Drawing check',\n  dangle:'Tracks ending in mid-air', splitN:'Signals split into several parts', noErr:'No issues found in the drawing.',\n  errNote:'The check only follows the copper drawn in the KiCad project. An issue may be a mistake in the replica or a connection made another way (jumper, metal shield). Always check against the original schematic.',\n  signal:'Signal', pinsOn:'{n} pins connected', pinsList:'Pins the signal reaches',\n  split:'In the drawing this signal is split into {n} parts: the pins circled in red are not connected to the drawn tracks. The dashed red line shows the shortest missing connection.',\n  unnamed:'unnamed signal', back:'\u2190 Back', component:'Component', side:'Side', sideF:'component', sideB:'solder',\n  pin:'Pin', name:'Name', sig:'Signal', nc:'not connected', fp:'Footprint', track:'track', via:'via',\n  dangleOne:'Unconnected track end', dangleTxt:'This track of signal {s} ends without touching a pad, a via or another track.',\n  legF:'component side track', legB:'solder side track',\n  many:'Widely used signal: pins are grouped by component.', through:'Continues through', noRes:'No results',\n  kComp:'chip', kNet:'signal', kPin:'pin',\n  b250407:'250407 \u00b7 breadbin', bku:'KU-14194HB \u00b7 breadbin', b250469:'250469 \u00b7 C64C',\n  d250407:'The C64 \u201cbreadbin\u201d board from 1983-84 (PCB 250407). KiCad replica by Bwack and Der Alte Bastler, rev. 1.3.',\n  dku:'The very first C64 board (KU-14194HB, 1982). KiCad replica by Bwack, rev. 1.4.',\n  d250469:'The C64C board (250469 rev. 4) used from 1987. KiCad replica by Bwack, rev. 1.1. It also includes the photos of the original board used for the reconstruction: turn them on with \u201cPhoto of the original board\u201d.',\n  photoNote:'Photo on: the drawn copper is semi-transparent so you can compare it with the real board.'\n }\n};\nvar L = T[EN ? 'en' : 'it'];\nfunction tr(k, o){ var s = L[k] || k; if (o) for (var x in o) s = s.split('{' + x + '}').join(o[x]); return s; }\n\n\/\/ indirizzi dei file nella libreria media (in locale, sul server di prova, si usano quelli in dati\/)\nvar UP = \/^(localhost|127\\.)\/.test(location.hostname) ? null : '\/wp-content\/uploads\/2026\/10\/';\nvar FILES = UP ? {\n '250407':{data:UP + 'c64pcb-250407.txt'},\n 'ku':{data:UP + 'c64pcb-ku.txt'},\n '250469':{data:UP + 'c64pcb-250469-1.txt', img:UP}\n} : {\n '250407':{data:'dati\/c64pcb-250407.txt'},\n 'ku':{data:'dati\/c64pcb-ku.txt'},\n '250469':{data:'dati\/c64pcb-250469.txt', img:'dati\/'}\n};\nvar ORDER = ['250407', 'ku', '250469'];\n\nvar $ = function(id){ return document.getElementById(id); };\nvar root = document.querySelector('.rfpcb');\nvar cv = $('rfpCanvas'), ctx = cv.getContext('2d'), view = $('rfpView'), info = $('rfpInfo'), tip = $('rfpTip');\nvar COL = { board:'#10301f', boardEdge:'#5fae7a', F:'#d9a640', B:'#4f8fd6', pad:'#e6c46a', hole:'#07060f',\n  silk:'#e8e4d0', hiF:'#ffe45c', hiB:'#6fe6ff', hiPad:'#ffffff', err:'#f94144', sel:'#ff7ad9' };\n\n\/\/ ---------- testi statici ----------\n[].forEach.call(root.querySelectorAll('[data-t]'), function(e){ e.textContent = tr(e.getAttribute('data-t')); });\n$('rfpFind').placeholder = tr('find'); $('rfpFit').title = tr('fit'); $('rfpFs').title = tr('fs');\nif (EN) { var cr = root.querySelector('[data-th=credits]');\n  cr.innerHTML = 'The drawings are the KiCad replicas by <a href=\"https:\/\/github.com\/bwack\" target=\"_blank\" rel=\"noopener\">Bwack<\/a> (<a href=\"https:\/\/github.com\/bwack\/C64-250407-Replica-KiCad\" target=\"_blank\" rel=\"noopener\">250407<\/a>, <a href=\"https:\/\/github.com\/bwack\/KU-14194HB-RevB-KiCad\" target=\"_blank\" rel=\"noopener\">KU-14194HB<\/a>, <a href=\"https:\/\/github.com\/bwack\/C64C-250469-KiCAD-Replica\" target=\"_blank\" rel=\"noopener\">250469<\/a>), released under the CERN OHL v1.2 licence and converted by RetroFixer for this page. They are reconstructions made by enthusiasts: before working on a board, compare them with the original Commodore schematic.'; }\n\n\/\/ ---------- stato ----------\nvar B = null, cache = {}, boardId = null;\nvar S = { side:'F', tr:true, other:true, zone:true, silk:true, ref:true, photo:false, err:true };\nvar V = { s:1, ox:0, oy:0, fitted:true }; \/\/ schermo = ox + (\u00b1x)*s; fitted: la vista segue la finestra finche non si fa zoom\nvar sel = null;                          \/\/ {t:'net', n} | {t:'fp', i} | {t:'dangle', i}\nvar flash = null, dpr = 1, W = 0, H = 0, raf = 0;\n\nORDER.forEach(function(id){\n  var b = document.createElement('button'); b.textContent = tr('b' + id); b.setAttribute('data-b', id);\n  b.setAttribute('aria-pressed', 'false'); b.onclick = function(){ loadBoard(id); };\n  $('rfpBoards').appendChild(b);\n});\n\nfunction esc(s){ return String(s).replace(\/[&<>\"]\/g, function(c){ return {'&':'&amp;','<':'&lt;','>':'&gt;','\"':'&quot;'}[c]; }); }\n\/\/ ~{X} e ~X diventano testo soprallineato\nfunction fmt(s){\n  s = esc(s);\n  s = s.replace(\/~\\{([^}]*)\\}\/g, '<span class=\"ov\">$1<\/span>');\n  return s.replace(\/~([A-Za-z0-9_]+)\/g, '<span class=\"ov\">$1<\/span>');\n}\nfunction netName(n){\n  if (n < 0) return null;\n  var s = B.nets[n];\n  if (\/^(Net-\\(|unconnected-)\/.test(s)) return null;\n  \/\/ prima il percorso del foglio (\/foglio\/NOME), poi le barre scritte come {slash} o \\\n  if (s.charAt(0) === '\/') s = s.slice(s.lastIndexOf('\/', s.length - 2) + 1);\n  return s.replace(\/\\{slash\\}\/g, '\/').replace(\/\\\\\/g, '\/');\n}\nfunction netLabel(n){\n  var s = netName(n);\n  if (s) return fmt(s);\n  var m = \/^Net-\\((.*)\\)$\/.exec(B.nets[n] || '');\n  return '<i>' + tr('unnamed') + (m ? ' \u00b7 ' + fmt(m[1].replace(\/-Pad\/, '-')) : '') + '<\/i>';\n}\nfunction plain(s){ return String(s || '').replace(\/~\\{([^}]*)\\}\/g, '\/$1').replace(\/~\/g, '\/'); }\nfunction netPlain(n){ var s = netName(n); return s ? plain(s) : tr('unnamed'); }\n\n\/\/ ---------- caricamento ----------\nfunction loadBoard(id){\n  boardId = id; sel = null;\n  [].forEach.call($('rfpBoards').children, function(b){ b.setAttribute('aria-pressed', b.getAttribute('data-b') === id ? 'true' : 'false'); });\n  if (cache[id]) { setBoard(cache[id]); return; }\n  $('rfpLoad').hidden = false; $('rfpLoad').textContent = tr('loading');\n  fetch(FILES[id].data).then(function(r){ if (!r.ok) throw r.status; return r.text(); }).then(function(t){\n    var d = JSON.parse(t.slice(t.indexOf('{')));\n    prepare(d, id); cache[id] = d; if (boardId === id) setBoard(d);\n  }).catch(function(){ $('rfpLoad').textContent = tr('loadErr'); });\n}\n\nfunction prepare(d, id){\n  d.id = id;\n  \/\/ percorsi per strato\n  d.P = {};\n  ['F', 'B'].forEach(function(L){\n    var byW = {};\n    d.tr[L].forEach(function(t){\n      var p = byW[t[0]] || (byW[t[0]] = new Path2D());\n      p.moveTo(t[2], t[3]); for (var i = 4; i < t.length; i += 2) p.lineTo(t[i], t[i + 1]);\n    });\n    var z = new Path2D();\n    d.zone[L].forEach(function(r){ z.moveTo(r[1], r[2]); for (var i = 3; i < r.length; i += 2) z.lineTo(r[i], r[i + 1]); z.closePath(); });\n    var silk = {};\n    d.silk[L].forEach(function(g){ addLine(silk, g[0], g, 1); });\n    d.P[L] = { tr:byW, zone:z, silk:silk, pads:new Path2D(), fsilk:{}, fab:{} };\n  });\n  var edge = new Path2D();\n  d.edge.forEach(function(e){ edge.moveTo(e[0], e[1]); for (var i = 2; i < e.length; i += 2) edge.lineTo(e[i], e[i + 1]); });\n  d.P.edge = edge;\n  var holes = new Path2D(), vias = new Path2D();\n  \/\/ indice spaziale dei pad\n  d.G = 400; d.grid = {};\n  d.npads = 0;\n  d.netPads = {};\n  d.fp.forEach(function(f, fi){\n    var x0 = 1e9, y0 = 1e9, x1 = -1e9, y1 = -1e9;\n    f.p.forEach(function(p, pi){\n      d.npads++;\n      var r = Math.max(p[3], p[4]) \/ 2;\n      x0 = Math.min(x0, p[1] - r); y0 = Math.min(y0, p[2] - r); x1 = Math.max(x1, p[1] + r); y1 = Math.max(y1, p[2] + r);\n      if (p[8].indexOf('F') >= 0) padPath(d.P.F.pads, p);\n      if (p[8].indexOf('B') >= 0) padPath(d.P.B.pads, p);\n      if (p[7] > 0) { holes.moveTo(p[1] + p[7] \/ 2, p[2]); holes.arc(p[1], p[2], p[7] \/ 2, 0, 6.2832); }\n      if (p[9] >= 0) (d.netPads[p[9]] || (d.netPads[p[9]] = [])).push([fi, pi]);\n      gridAdd(d, p[1] - r, p[2] - r, p[1] + r, p[2] + r, [0, fi, pi]);\n    });\n    f.g.forEach(function(g){\n      g.slice(2).forEach(function(v, k){ if (k % 2) { y0 = Math.min(y0, v); y1 = Math.max(y1, v); } else { x0 = Math.min(x0, v); x1 = Math.max(x1, v); } });\n      addLine(d.P[f.s][g[0] ? 'fab' : 'fsilk'], g[1], g, 2);\n    });\n    f.bb = x0 <= x1 ? [x0, y0, x1, y1] : [f.x - 50, f.y - 50, f.x + 50, f.y + 50];\n  });\n  d.via.forEach(function(v, i){\n    vias.moveTo(v[0] + v[2] \/ 2, v[1]); vias.arc(v[0], v[1], v[2] \/ 2, 0, 6.2832);\n    if (v[3] > 0) { holes.moveTo(v[0] + v[3] \/ 2, v[1]); holes.arc(v[0], v[1], v[3] \/ 2, 0, 6.2832); }\n    gridAdd(d, v[0] - v[2] \/ 2, v[1] - v[2] \/ 2, v[0] + v[2] \/ 2, v[1] + v[2] \/ 2, [1, i]);\n  });\n  ['F', 'B'].forEach(function(L){\n    d.tr[L].forEach(function(t, i){\n      for (var k = 2; k + 3 < t.length; k += 2)\n        gridAdd(d, Math.min(t[k], t[k + 2]) - t[0] \/ 2, Math.min(t[k + 1], t[k + 3]) - t[0] \/ 2,\n                Math.max(t[k], t[k + 2]) + t[0] \/ 2, Math.max(t[k + 1], t[k + 3]) + t[0] \/ 2, [2, L, i, k]);\n    });\n  });\n  d.P.holes = holes; d.P.vias = vias;\n  d.netDesc = {};\n  \/\/ fotografie\n  \/\/ le foto sono in tasselli; si scaricano solo quando si accende la foto\n  d.imgs = { F:[], B:[] };\n  (d.img || []).forEach(function(m){ d.imgs[m.side].push(m); });\n  \/\/ sigle dei componenti ordinate: prima i chip\n  d.order = d.fp.map(function(f, i){ return i; }).sort(function(a, b){ return refKey(d.fp[a].r) < refKey(d.fp[b].r) ? -1 : 1; });\n}\nfunction refKey(r){ var m = \/^([A-Z]+)(\\d*)\/i.exec(r) || ['', r, '0']; var pre = {U:'0', CN:'1'}[m[1].toUpperCase()] || '2'; return pre + m[1] + ('0000' + m[2]).slice(-4); }\nfunction addLine(map, w, g, from){\n  var p = map[w] || (map[w] = new Path2D());\n  p.moveTo(g[from], g[from + 1]); for (var i = from + 2; i < g.length; i += 2) p.lineTo(g[i], g[i + 1]);\n}\nfunction gridAdd(d, x0, y0, x1, y1, item){\n  var G = d.G;\n  for (var cx = Math.floor(x0 \/ G); cx <= Math.floor(x1 \/ G); cx++)\n    for (var cy = Math.floor(y0 \/ G); cy <= Math.floor(y1 \/ G); cy++) {\n      var k = cx + ',' + cy; (d.grid[k] || (d.grid[k] = [])).push(item);\n    }\n}\nfunction padPath(P, p){\n  var a = p[5] * Math.PI \/ 180, c = Math.cos(a), s = Math.sin(a), hw = p[3] \/ 2, hh = p[4] \/ 2, x = p[1], y = p[2];\n  if (p[6] === 'c') { P.moveTo(x + hw, y); P.arc(x, y, hw, 0, 6.2832); return; }\n  if (p[6] === 'o' && Math.abs(hw - hh) > 1) {\n    var r, u, phi;\n    if (hw >= hh) { r = hh; u = hw - hh; phi = Math.atan2(-s, c); }\n    else { r = hw; u = hh - hw; phi = Math.atan2(c, s); }\n    var ex = Math.cos(phi) * u, ey = Math.sin(phi) * u;\n    P.moveTo(x + ex + Math.cos(phi - 1.5708) * r, y + ey + Math.sin(phi - 1.5708) * r);\n    P.arc(x + ex, y + ey, r, phi - 1.5708, phi + 1.5708);\n    P.arc(x - ex, y - ey, r, phi + 1.5708, phi + 4.7124);\n    P.closePath(); return;\n  }\n  if (p[6] === 'o') { P.moveTo(x + hw, y); P.arc(x, y, hw, 0, 6.2832); return; }\n  var ux = [c * hw, -s * hw], uy = [s * hh, c * hh];\n  P.moveTo(x + ux[0] + uy[0], y + ux[1] + uy[1]);\n  P.lineTo(x - ux[0] + uy[0], y - ux[1] + uy[1]);\n  P.lineTo(x - ux[0] - uy[0], y - ux[1] - uy[1]);\n  P.lineTo(x + ux[0] - uy[0], y + ux[1] - uy[1]);\n  P.closePath();\n}\n\nfunction setBoard(d){\n  B = d; $('rfpLoad').hidden = true;\n  $('rfpPhotoL').hidden = !(d.img && d.img.length);\n  if (!(d.img && d.img.length)) S.photo = false;\n  root.querySelector('[data-l=photo]').checked = S.photo;\n  fit(); showDefault(); fromHash(); draw();\n}\n\n\/\/ ---------- vista ----------\nfunction mx(){ return S.side === 'B' ? -1 : 1; }\nfunction toScreen(x, y){ return [V.ox + mx() * x * V.s, V.oy + y * V.s]; }\nfunction toWorld(sx, sy){ return [(sx - V.ox) \/ V.s * mx(), (sy - V.oy) \/ V.s]; }\nfunction fit(){\n  if (!B || W < 60 || H < 60) return;\n  var b = B.box, pad = 20, s = Math.min((W - 2 * pad) \/ (b[2] - b[0]), (H - 2 * pad) \/ (b[3] - b[1]));\n  V.s = s; V.fitted = true; var cx = (b[0] + b[2]) \/ 2, cy = (b[1] + b[3]) \/ 2;\n  V.ox = W \/ 2 - mx() * cx * s; V.oy = H \/ 2 - cy * s;\n}\nfunction zoomAt(f, sx, sy){\n  var w = toWorld(sx, sy);\n  var ns = Math.max(minS(), Math.min(V.s * f, 0.6));\n  V.s = ns; V.fitted = false; V.ox = sx - mx() * w[0] * ns; V.oy = sy - w[1] * ns; draw();\n}\nfunction minS(){ if (!B) return 0.001; var b = B.box; return Math.min(W \/ (b[2] - b[0]), H \/ (b[3] - b[1])) * 0.5; }\nfunction centerOn(x, y, s){\n  if (s) V.s = Math.max(V.s, s);\n  V.fitted = false;\n  V.ox = W \/ 2 - mx() * x * V.s; V.oy = H \/ 2 - y * V.s;\n}\nfunction resize(){\n  var r = view.getBoundingClientRect(); dpr = Math.min(window.devicePixelRatio || 1, 2);\n  var first = !W; var cx = W \/ 2, cy = H \/ 2, w = first ? null : toWorld(cx, cy);\n  W = r.width; H = r.height; cv.width = Math.round(W * dpr); cv.height = Math.round(H * dpr);\n  if (B) { if (first || V.fitted) fit(); else { V.ox = W \/ 2 - mx() * w[0] * V.s; V.oy = H \/ 2 - w[1] * V.s; } }\n  draw();\n}\nfunction draw(){ if (!raf) raf = requestAnimationFrame(render); }\n\n\/\/ ---------- disegno ----------\nfunction strokeMap(map, color, alpha, minPx){\n  ctx.strokeStyle = color; ctx.globalAlpha = alpha;\n  for (var w in map) { ctx.lineWidth = Math.max(+w, minPx \/ V.s); ctx.stroke(map[w]); }\n  ctx.globalAlpha = 1;\n}\nfunction render(){\n  raf = 0;\n  ctx.setTransform(1, 0, 0, 1, 0, 0);\n  ctx.fillStyle = '#07060f'; ctx.fillRect(0, 0, cv.width, cv.height);\n  if (!B) return;\n  var m = mx(), fr = S.side, bk = fr === 'F' ? 'B' : 'F';\n  ctx.setTransform(dpr * V.s * m, 0, 0, dpr * V.s, dpr * V.ox, dpr * V.oy);\n  ctx.lineCap = 'round'; ctx.lineJoin = 'round';\n  var ph = S.photo && B.imgs[fr].length ? B.imgs[fr] : null;\n  \/\/ scheda\n  ctx.fillStyle = COL.board; ctx.fill(B.P.edge, 'evenodd');\n  if (ph) ph.forEach(function(m){\n    if (!m.im) { m.im = new Image(); m.im.onload = function(){ m.ok = true; draw(); }; m.im.src = FILES[B.id].img + m.f; }\n    if (m.ok) ctx.drawImage(m.im, m.x, m.y, m.w + 3, m.h + 3);\n  });\n  var k = ph ? 0.45 : 1;\n  \/\/ altro lato, sotto\n  if (S.other) {\n    if (S.zone) { ctx.fillStyle = COL[bk]; ctx.globalAlpha = 0.10 * k; ctx.fill(B.P[bk].zone); ctx.globalAlpha = 1; }\n    if (S.tr) strokeMap(B.P[bk].tr, COL[bk], 0.32 * k, 0.6);\n  }\n  if (S.zone) { ctx.fillStyle = COL[fr]; ctx.globalAlpha = 0.22 * k; ctx.fill(B.P[fr].zone); ctx.globalAlpha = 1; }\n  if (S.tr) strokeMap(B.P[fr].tr, COL[fr], 0.9 * k, 0.6);\n  ctx.fillStyle = COL.pad; ctx.globalAlpha = ph ? 0.55 : 1; ctx.fill(B.P[fr].pads);\n  if (S.other) { ctx.globalAlpha = (ph ? 0.3 : 0.45); ctx.fill(B.P[bk].pads); }\n  ctx.fillStyle = '#c9cdd6'; ctx.globalAlpha = ph ? 0.5 : 1; ctx.fill(B.P.vias);\n  ctx.globalAlpha = 1; ctx.fillStyle = COL.hole; ctx.fill(B.P.holes);\n  if (S.silk) {\n    strokeMap(B.P[fr].silk, COL.silk, ph ? 0.5 : 0.75, 0.7);\n    strokeMap(B.P[fr].fsilk, COL.silk, ph ? 0.5 : 0.75, 0.7);\n  }\n  ctx.strokeStyle = COL.boardEdge; ctx.lineWidth = 1.2 \/ V.s; ctx.stroke(B.P.edge);\n\n  \/\/ selezione\n  if (sel && sel.t === 'net') drawNet(sel.n);\n  if (sel && sel.t === 'fp') drawFp(sel.i);\n  if (S.err) drawErr();\n  if (flash) drawFlash();\n  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\n  if (S.ref) drawRefs();\n  if (sel && sel.t === 'net') drawNetTags(sel.n);\n  if (sel && sel.t === 'fp') drawPinTags(sel.i);\n  $('rfpTag').textContent = tr(fr === 'F' ? 'topTag' : 'bottomTag');\n}\nfunction dim(){\n  ctx.save(); ctx.setTransform(1, 0, 0, 1, 0, 0);\n  ctx.fillStyle = 'rgba(7,6,15,.72)'; ctx.fillRect(0, 0, cv.width, cv.height); ctx.restore();\n}\nfunction netPaths(n){\n  if (B.netDesc[n]) return B.netDesc[n];\n  var o = { F:{}, B:{}, zF:new Path2D(), zB:new Path2D(), pF:new Path2D(), pB:new Path2D(), v:new Path2D() };\n  ['F', 'B'].forEach(function(L){\n    B.tr[L].forEach(function(t){\n      if (t[1] !== n) return;\n      var p = o[L][t[0]] || (o[L][t[0]] = new Path2D());\n      p.moveTo(t[2], t[3]); for (var i = 4; i < t.length; i += 2) p.lineTo(t[i], t[i + 1]);\n    });\n    B.zone[L].forEach(function(r){\n      if (r[0] !== n) return;\n      var z = o['z' + L]; z.moveTo(r[1], r[2]); for (var i = 3; i < r.length; i += 2) z.lineTo(r[i], r[i + 1]); z.closePath();\n    });\n  });\n  (B.netPads[n] || []).forEach(function(q){\n    var p = B.fp[q[0]].p[q[1]];\n    if (p[8].indexOf('F') >= 0) padPath(o.pF, p);\n    if (p[8].indexOf('B') >= 0) padPath(o.pB, p);\n  });\n  B.via.forEach(function(v){ if (v[4] === n) { o.v.moveTo(v[0] + v[2] \/ 2, v[1]); o.v.arc(v[0], v[1], v[2] \/ 2, 0, 6.2832); } });\n  return (B.netDesc[n] = o);\n}\nfunction drawNet(n){\n  dim();\n  var o = netPaths(n), fr = S.side, bk = fr === 'F' ? 'B' : 'F';\n  ctx.fillStyle = COL['hi' + bk]; ctx.globalAlpha = 0.18; ctx.fill(o['z' + bk]);\n  strokeMap(o[bk], COL['hi' + bk], 0.22, 8); strokeMap(o[bk], COL['hi' + bk], 0.85, 2.2);\n  ctx.fillStyle = COL['hi' + fr]; ctx.globalAlpha = 0.3; ctx.fill(o['z' + fr]);\n  strokeMap(o[fr], COL['hi' + fr], 0.25, 9); strokeMap(o[fr], COL['hi' + fr], 1, 2.6);\n  ctx.fillStyle = COL['hi' + bk]; ctx.globalAlpha = 0.8; ctx.fill(o['p' + bk]);\n  ctx.fillStyle = COL.hiPad; ctx.globalAlpha = 1; ctx.fill(o['p' + fr]); ctx.fill(o.v);\n  ctx.fillStyle = COL.hole; ctx.fill(B.P.holes);\n  \/\/ pad con un alone, cosi si vedono anche da lontano\n  ctx.strokeStyle = COL.sel; ctx.lineWidth = 2 \/ V.s;\n  (B.netPads[n] || []).forEach(function(q){\n    var p = B.fp[q[0]].p[q[1]], r = Math.max(p[3], p[4]) \/ 2 + 4 \/ V.s;\n    ctx.beginPath(); ctx.arc(p[1], p[2], r, 0, 6.2832); ctx.stroke();\n  });\n  var br = B.breaks[n];\n  if (br) {\n    ctx.strokeStyle = COL.err; ctx.lineWidth = 2.5 \/ V.s;\n    (B.netPads[n] || []).forEach(function(q){\n      var p = B.fp[q[0]].p[q[1]]; if (!p[11]) return;\n      ctx.beginPath(); ctx.arc(p[1], p[2], Math.max(p[3], p[4]) \/ 2 + 9 \/ V.s, 0, 6.2832); ctx.stroke();\n    });\n    ctx.setLineDash([8 \/ V.s, 6 \/ V.s]);\n    br.miss.forEach(function(l){ ctx.beginPath(); ctx.moveTo(l[0], l[1]); ctx.lineTo(l[2], l[3]); ctx.stroke(); });\n    ctx.setLineDash([]);\n  }\n}\nfunction drawFp(i){\n  dim();\n  var f = B.fp[i], fr = S.side, P = new Path2D();\n  f.p.forEach(function(p){ padPath(P, p); });\n  ctx.fillStyle = COL.hiPad; ctx.fill(P); ctx.fillStyle = COL.hole;\n  f.p.forEach(function(p){ if (p[7] > 0) { ctx.beginPath(); ctx.arc(p[1], p[2], p[7] \/ 2, 0, 6.2832); ctx.fill(); } });\n  var b = f.bb, m = 6 \/ V.s;\n  ctx.strokeStyle = COL.sel; ctx.lineWidth = 2 \/ V.s; ctx.strokeRect(b[0] - m, b[1] - m, b[2] - b[0] + 2 * m, b[3] - b[1] + 2 * m);\n  var g = {}; f.g.forEach(function(x){ if (!x[0]) addLine(g, x[1], x, 2); });\n  strokeMap(g, COL.sel, 1, 1);\n  \/\/ pin 1\n  var p1 = f.p.filter(function(p){ return p[0] === '1'; })[0];\n  if (p1) { ctx.strokeStyle = COL.hiF; ctx.lineWidth = 2 \/ V.s; ctx.beginPath(); ctx.arc(p1[1], p1[2], Math.max(p1[3], p1[4]) \/ 2 + 5 \/ V.s, 0, 6.2832); ctx.stroke(); }\n}\nfunction drawErr(){\n  ctx.strokeStyle = COL.err; ctx.lineWidth = 2 \/ V.s;\n  B.dangle.forEach(function(d, i){\n    if (d[0] !== S.side && !S.other) return;\n    var r = (sel && sel.t === 'dangle' && sel.i === i ? 16 : 7) \/ V.s;\n    ctx.globalAlpha = d[0] === S.side ? 1 : 0.55;\n    ctx.beginPath(); ctx.arc(d[1], d[2], r, 0, 6.2832); ctx.stroke();\n  });\n  ctx.globalAlpha = 1;\n}\nfunction drawFlash(){\n  var t = (performance.now() - flash.t) \/ 900;\n  if (t > 1) { flash = null; return; }\n  ctx.strokeStyle = COL.hiF; ctx.lineWidth = 3 \/ V.s; ctx.globalAlpha = 1 - t;\n  ctx.beginPath(); ctx.arc(flash.x, flash.y, (10 + 40 * t) \/ V.s, 0, 6.2832); ctx.stroke(); ctx.globalAlpha = 1;\n  draw();\n}\nfunction label(txt, x, y, size, color, bg){\n  ctx.font = '600 ' + size + 'px Inter,system-ui,sans-serif';\n  var w = ctx.measureText(txt).width;\n  if (bg) { ctx.fillStyle = bg; ctx.fillRect(x - w \/ 2 - 3, y - size * 0.62, w + 6, size * 1.24); }\n  ctx.fillStyle = color; ctx.fillText(txt, x - w \/ 2, y + size * 0.36);\n}\nfunction drawRefs(){\n  ctx.textBaseline = 'alphabetic';\n  B.fp.forEach(function(f, i){\n    var b = f.bb, w = (b[2] - b[0]) * V.s, h = (b[3] - b[1]) * V.s;\n    var size = Math.min(Math.min(w, h) * 0.42, Math.max(w, h) * 0.2, 30);\n    if (size < 8 || !f.p.length || \/\\*\/.test(f.r)) return;\n    var c = toScreen((b[0] + b[2]) \/ 2, (b[1] + b[3]) \/ 2);\n    if (c[0] < -50 || c[1] < -50 || c[0] > W + 50 || c[1] > H + 50) return;\n    var mine = f.s === S.side;\n    if (!mine && !S.other) return;\n    label(f.r, c[0], c[1], size, mine ? '#ffffff' : 'rgba(255,255,255,.45)', mine ? 'rgba(7,6,15,.55)' : null);\n    if (size > 15 && f.d && mine) label(shortVal(f), c[0], c[1] + size * 1.1, size * 0.6, '#ffd23f', 'rgba(7,6,15,.55)');\n  });\n}\nfunction shortVal(f){ return String(f.v).replace(\/_U\\d+$|_MPU$\/, ''); }\nfunction drawNetTags(n){\n  var list = B.netPads[n] || [];\n  if (V.s < 0.06 && list.length > 12) return;\n  list.forEach(function(q){\n    var f = B.fp[q[0]], p = f.p[q[1]], c = toScreen(p[1], p[2]);\n    if (c[0] < 0 || c[1] < 0 || c[0] > W || c[1] > H) return;\n    label(f.r + '.' + p[0], c[0], c[1] - Math.max(p[3], p[4]) \/ 2 * V.s - 12, 12, '#fff', 'rgba(224,57,138,.92)');\n  });\n}\nfunction drawPinTags(i){\n  var f = B.fp[i];\n  f.p.forEach(function(p){\n    var sz = Math.max(p[3], p[4]) * V.s; if (sz < 11) return;\n    var c = toScreen(p[1], p[2]);\n    label(p[0], c[0], c[1], Math.min(sz * 0.45, 14), '#07060f');\n    if (sz > 18 && p[10]) label(plain(p[10]), c[0], c[1] + sz * 0.5 + 9, 11, '#fff', 'rgba(52,44,116,.92)');\n  });\n}\n\n\/\/ ---------- ricerca nel disegno ----------\nfunction segDist(px, py, ax, ay, bx, by){\n  var dx = bx - ax, dy = by - ay, l = dx * dx + dy * dy, t = l ? Math.max(0, Math.min(1, ((px - ax) * dx + (py - ay) * dy) \/ l)) : 0;\n  return Math.hypot(px - ax - t * dx, py - ay - t * dy);\n}\nfunction padHit(p, x, y, tol){\n  var a = p[5] * Math.PI \/ 180, dx = x - p[1], dy = y - p[2];\n  var lx = dx * Math.cos(a) - dy * Math.sin(a), ly = dx * Math.sin(a) + dy * Math.cos(a);\n  if (p[6] === 'c') return Math.hypot(lx, ly) <= p[3] \/ 2 + tol;\n  return Math.abs(lx) <= p[3] \/ 2 + tol && Math.abs(ly) <= p[4] \/ 2 + tol;\n}\nfunction hit(sx, sy){\n  if (!B) return null;\n  var w = toWorld(sx, sy), x = w[0], y = w[1], tol = 3 \/ V.s, G = B.G;\n  var cands = [];\n  for (var cx = Math.floor((x - tol) \/ G); cx <= Math.floor((x + tol) \/ G); cx++)\n    for (var cy = Math.floor((y - tol) \/ G); cy <= Math.floor((y + tol) \/ G); cy++)\n      (B.grid[cx + ',' + cy] || []).forEach(function(it){ cands.push(it); });\n  var best = null, bestScore = 1e9;\n  cands.forEach(function(it){\n    var score;\n    if (it[0] === 0) {\n      var p = B.fp[it[1]].p[it[2]];\n      if (!S.other && p[8].indexOf(S.side) < 0) return;\n      if (!padHit(p, x, y, tol)) return;\n      score = (p[8].indexOf(S.side) >= 0 ? 0 : 50) + Math.hypot(x - p[1], y - p[2]) * V.s * 0.01;\n    } else if (it[0] === 1) {\n      var v = B.via[it[1]], d = Math.hypot(x - v[0], y - v[1]);\n      if (d > v[2] \/ 2 + tol) return; score = 10 + d * V.s * 0.01;\n    } else {\n      var L = it[1]; if (L !== S.side && !(S.other && S.tr)) return; if (!S.tr) return;\n      var t = B.tr[L][it[2]], k = it[3], dd = segDist(x, y, t[k], t[k + 1], t[k + 2], t[k + 3]);\n      if (dd > t[0] \/ 2 + tol) return; score = (L === S.side ? 20 : 70) + dd * V.s * 0.01;\n    }\n    if (score < bestScore) { bestScore = score; best = it; }\n  });\n  if (best) {\n    if (best[0] === 0) { var pp = B.fp[best[1]].p[best[2]]; return { t:'pad', fi:best[1], pi:best[2], n:pp[9] }; }\n    if (best[0] === 1) return { t:'via', n:B.via[best[1]][4] };\n    return { t:'track', L:best[1], n:B.tr[best[1]][best[2]][1] };\n  }\n  \/\/ corpo di un componente: il piu piccolo che contiene il punto\n  var fi = -1, area = 1e18;\n  B.fp.forEach(function(f, i){\n    if (f.s !== S.side && !S.other) return;\n    var b = f.bb; if (x < b[0] || x > b[2] || y < b[1] || y > b[3]) return;\n    var a = (b[2] - b[0]) * (b[3] - b[1]) * (f.s === S.side ? 1 : 4);\n    if (a < area) { area = a; fi = i; }\n  });\n  return fi >= 0 ? { t:'fp', fi:fi } : null;\n}\n\n\/\/ ---------- pannello ----------\nfunction pinRow(fi, pi, withRef){\n  var f = B.fp[fi], p = f.p[pi];\n  var bad = p[11] ? ' bad' : '';\n  return '<button class=\"row' + bad + '\" data-go=\"' + fi + ':' + pi + '\"><span class=\"r\">' + esc(f.r) + '.' + esc(p[0]) + '<\/span>' +\n    '<span class=\"m\">' + (p[10] ? fmt(p[10]) + ' \u00b7 ' : '') + esc(shortVal(f)) + '<\/span><\/button>';\n}\nfunction showDefault(){\n  var d = B, nNets = Object.keys(d.netPads).length;\n  var h = '<h3>' + esc(tr('b' + d.id)) + '<\/h3><p class=\"sub\">' + esc(tr('d' + d.id)) + '<\/p>';\n  h += '<p>' + esc(tr('stats', { c:d.fp.length, p:d.npads, n:nNets })) + '<\/p>';\n  h += '<div class=\"lbl\">' + esc(tr('how')) + '<\/div><ul><li>' + esc(tr('h1')) + '<\/li><li>' + esc(tr('h2')) + '<\/li><li>' + esc(tr('h3')) + '<\/li><li>' + esc(tr('h4')) + '<\/li><\/ul>';\n  h += '<div class=\"lbl\">' + esc(tr('comp')) + '<\/div><div class=\"rows\">';\n  d.order.forEach(function(i){\n    var f = d.fp[i]; if (!f.d) return;\n    h += '<button class=\"row\" data-fp=\"' + i + '\"><span class=\"r\">' + esc(f.r) + '<\/span><span class=\"m\">' + esc(shortVal(f)) + ' \u00b7 ' + esc(f.d[EN ? 1 : 0]) + '<\/span><\/button>';\n  });\n  h += '<\/div><div class=\"lbl\">' + esc(tr('checks')) + '<\/div>';\n  var nb = Object.keys(d.breaks);\n  if (!nb.length && !d.dangle.length) h += '<p>' + esc(tr('noErr')) + '<\/p>';\n  else {\n    h += '<div class=\"rows\">';\n    if (nb.length) {\n      h += '<p class=\"sub\" style=\"margin:4px 0\">' + esc(tr('splitN')) + '<\/p>';\n      nb.forEach(function(n){ h += '<button class=\"row\" data-net=\"' + n + '\"><span class=\"r\" style=\"color:var(--red)\">\u00d7' + d.breaks[n].groups + '<\/span><span class=\"n\">' + netLabel(+n) + '<\/span><\/button>'; });\n    }\n    if (d.dangle.length) {\n      h += '<p class=\"sub\" style=\"margin:8px 0 4px\">' + esc(tr('dangle')) + '<\/p>';\n      d.dangle.forEach(function(x, i){ h += '<button class=\"row\" data-dg=\"' + i + '\"><span class=\"r\" style=\"color:var(--red)\">' + (x[0] === 'F' ? tr('sideF').slice(0, 4) : tr('sideB').slice(0, 4)) + '.<\/span><span class=\"n\">' + netLabel(x[3]) + '<\/span><\/button>'; });\n    }\n    h += '<\/div>';\n  }\n  h += '<div class=\"note\">' + esc(tr('errNote')) + '<\/div>';\n  if (S.photo) h += '<div class=\"note\">' + esc(tr('photoNote')) + '<\/div>';\n  info.innerHTML = h; info.scrollTop = 0;\n}\nfunction showNet(n){\n  var list = (B.netPads[n] || []).slice().sort(function(a, b){\n    var A = B.fp[a[0]], Bb = B.fp[b[0]];\n    var ka = refKey(A.r), kb = refKey(Bb.r);\n    return ka === kb ? (parseInt(A.p[a[1]][0], 36) - parseInt(Bb.p[b[1]][0], 36)) : (ka < kb ? -1 : 1);\n  });\n  var h = '<button class=\"back\" data-back>' + esc(tr('back')) + '<\/button>';\n  h += '<div class=\"lbl\" style=\"margin-top:0\">' + esc(tr('signal')) + '<\/div><h3>' + netLabel(n) + '<\/h3>';\n  h += '<p class=\"sub\">' + esc(tr('pinsOn', { n:list.length })) + '<\/p>';\n  h += '<p class=\"sub\" style=\"font-size:13px\"><span class=\"sw\" style=\"background:' + COL.hiF + '\"><\/span>' + esc(tr('legF')) + ' &nbsp; <span class=\"sw\" style=\"background:' + COL.hiB + '\"><\/span>' + esc(tr('legB')) + '<\/p>';\n  if (B.breaks[n]) h += '<div class=\"warn\">' + esc(tr('split', { n:B.breaks[n].groups })) + '<\/div>';\n  h += '<div class=\"lbl\">' + esc(tr('pinsList')) + '<\/div><div class=\"rows\">';\n  if (list.length > 40) {\n    h += '<p class=\"sub\">' + esc(tr('many')) + '<\/p>';\n    var by = {}, ord = [];\n    list.forEach(function(q){ if (!by[q[0]]) { by[q[0]] = []; ord.push(q[0]); } by[q[0]].push(q); });\n    ord.forEach(function(fi){\n      var f = B.fp[fi];\n      h += '<button class=\"row\" data-go=\"' + fi + ':' + by[fi][0][1] + '\"><span class=\"r\">' + esc(f.r) + '<\/span><span class=\"m\">' +\n        by[fi].map(function(q){ return esc(f.p[q[1]][0]); }).join(', ') + ' \u00b7 ' + esc(shortVal(f)) + '<\/span><\/button>';\n    });\n  } else list.forEach(function(q){ h += pinRow(q[0], q[1]); });\n  h += '<\/div>';\n  var via = [];\n  list.forEach(function(q){\n    var f = B.fp[q[0]];\n    if (f.p.length !== 2 || !\/^(FB|R|L|JP|E|SJ)\\d\/i.test(f.r)) return;\n    var o = f.p[1 - q[1]];\n    if (o[9] >= 0 && o[9] !== n) via.push('<button class=\"row\" data-net=\"' + o[9] + '\"><span class=\"r\">' + esc(f.r) + '<\/span><span class=\"m\">' + esc(shortVal(f)) + ' \u2192 <span class=\"n\">' + netLabel(o[9]) + '<\/span><\/span><\/button>');\n  });\n  if (via.length) h += '<div class=\"lbl\">' + esc(tr('through')) + '<\/div><div class=\"rows\">' + via.join('') + '<\/div>';\n  info.innerHTML = h; info.scrollTop = 0;\n}\nfunction showFp(i){\n  var f = B.fp[i];\n  var h = '<button class=\"back\" data-back>' + esc(tr('back')) + '<\/button>';\n  h += '<div class=\"lbl\" style=\"margin-top:0\">' + esc(tr('component')) + '<\/div><h3>' + esc(f.r) + ' \u00b7 ' + esc(shortVal(f)) + '<\/h3>';\n  h += '<p class=\"sub\">' + (f.d ? esc(f.d[EN ? 1 : 0]) + '<br>' : '') + esc(tr('side')) + ': ' + esc(tr(f.s === 'F' ? 'sideF' : 'sideB')) + ' \u00b7 ' + esc(tr('fp')) + ': ' + esc(f.l) + '<\/p>';\n  var pins = f.p.map(function(p, k){ return k; }).sort(function(a, b){\n    var x = f.p[a][0], y = f.p[b][0], nx = parseInt(x, 10), ny = parseInt(y, 10);\n    return (isNaN(nx) || isNaN(ny)) ? (x < y ? -1 : x > y ? 1 : 0) : nx - ny;\n  });\n  h += '<div class=\"rows\"><div class=\"row c3\" style=\"color:var(--yel);font-family:VT323,monospace;font-size:15px\"><span>' + esc(tr('pin')) + '<\/span><span>' + esc(tr('name')) + '<\/span><span>' + esc(tr('sig')) + '<\/span><\/div>';\n  pins.forEach(function(k){\n    var p = f.p[k];\n    h += '<button class=\"row c3' + (p[11] ? ' bad' : '') + '\" ' + (p[9] >= 0 ? 'data-net=\"' + p[9] + '\" data-pin=\"' + i + ':' + k + '\"' : 'disabled') + '><span class=\"r\">' + esc(p[0]) + '<\/span><span class=\"m\">' + (p[10] ? fmt(p[10]) : '') +\n      '<\/span><span class=\"n\">' + (p[9] >= 0 ? netLabel(p[9]) : '<i>' + esc(tr('nc')) + '<\/i>') + '<\/span><\/button>';\n  });\n  h += '<\/div>';\n  info.innerHTML = h; info.scrollTop = 0;\n}\nfunction showDangle(i){\n  var d = B.dangle[i];\n  info.innerHTML = '<button class=\"back\" data-back>' + esc(tr('back')) + '<\/button><h3>' + esc(tr('dangleOne')) + '<\/h3>' +\n    '<p class=\"sub\">' + esc(tr('side')) + ': ' + esc(tr(d[0] === 'F' ? 'sideF' : 'sideB')) + '<\/p><div class=\"warn\">' +\n    tr('dangleTxt', { s:netLabel(d[3]) }) + '<\/div><div class=\"rows\"><button class=\"row\" data-net=\"' + d[3] + '\"><span class=\"r\">\u2192<\/span><span class=\"n\">' + netLabel(d[3]) + '<\/span><\/button><\/div>' +\n    '<div class=\"note\">' + esc(tr('errNote')) + '<\/div>';\n}\n\nfunction select(s, opts){\n  sel = s; opts = opts || {};\n  if (!s) showDefault();\n  else if (s.t === 'net') showNet(s.n);\n  else if (s.t === 'fp') showFp(s.i);\n  else if (s.t === 'dangle') showDangle(s.i);\n  if (opts.go) { centerOn(opts.go[0], opts.go[1], opts.zoom); flash = { x:opts.go[0], y:opts.go[1], t:performance.now() }; }\n  toHash(); draw();\n}\nfunction zoomToFp(i){\n  var b = B.fp[i].bb, w = b[2] - b[0], h = b[3] - b[1];\n  V.s = Math.min(W \/ (w * 2.2), H \/ (h * 2.2), 0.14); centerOn((b[0] + b[2]) \/ 2, (b[1] + b[3]) \/ 2);\n}\nfunction showSideOf(layers){\n  if (layers.indexOf(S.side) < 0 && layers.length === 1) setSide(layers);\n}\n\ninfo.addEventListener('click', function(e){\n  var t = e.target.closest('button'); if (!t || !B) return;\n  if (t.hasAttribute('data-back')) { select(null); return; }\n  if (t.hasAttribute('data-pin') && sel && sel.t === 'fp') {\n    var q = t.getAttribute('data-pin').split(':'), p = B.fp[+q[0]].p[+q[1]];\n    select({ t:'net', n:+t.getAttribute('data-net'), pin:[+q[0], +q[1]] }, { go:[p[1], p[2]] }); return;\n  }\n  if (t.hasAttribute('data-net')) {\n    var n = +t.getAttribute('data-net'), br = B.breaks[n];\n    var go = null;\n    if (br) go = [(br.miss[0][0] + br.miss[0][2]) \/ 2, (br.miss[0][1] + br.miss[0][3]) \/ 2];\n    select({ t:'net', n:n }, go ? { go:go, zoom:0.03 } : null); return;\n  }\n  if (t.hasAttribute('data-go')) {\n    var g = t.getAttribute('data-go').split(':'), pp = B.fp[+g[0]].p[+g[1]];\n    showSideOf(pp[8]); centerOn(pp[1], pp[2], 0.05); flash = { x:pp[1], y:pp[2], t:performance.now() }; draw(); return;\n  }\n  if (t.hasAttribute('data-fp')) { var i = +t.getAttribute('data-fp'); showSideOf(B.fp[i].s); zoomToFp(i); select({ t:'fp', i:i }); return; }\n  if (t.hasAttribute('data-dg')) { var k = +t.getAttribute('data-dg'), d = B.dangle[k]; setSide(d[0]); select({ t:'dangle', i:k }, { go:[d[1], d[2]], zoom:0.1 }); }\n});\n\n\/\/ ---------- lato e strati ----------\nfunction setSide(s){\n  if (s === S.side) return;\n  var c = toWorld(W \/ 2, H \/ 2);\n  S.side = s;\n  [].forEach.call($('rfpSide').children, function(b){ b.setAttribute('aria-pressed', b.getAttribute('data-s') === s ? 'true' : 'false'); });\n  V.ox = W \/ 2 - mx() * c[0] * V.s; draw();\n}\n[].forEach.call($('rfpSide').children, function(b){ b.onclick = function(){ setSide(b.getAttribute('data-s')); }; });\n[].forEach.call($('rfpLay').querySelectorAll('input'), function(c){\n  c.onchange = function(){ S[c.getAttribute('data-l')] = c.checked; if (c.getAttribute('data-l') === 'photo' && (!sel)) showDefault(); draw(); };\n});\n\n\/\/ ---------- puntatore ----------\nvar ptrs = {}, drag = null, pinch = null, moved = false;\ncv.addEventListener('pointerdown', function(e){\n  cv.setPointerCapture(e.pointerId); ptrs[e.pointerId] = [e.offsetX, e.offsetY];\n  var ids = Object.keys(ptrs);\n  if (ids.length === 1) { drag = { x:e.offsetX, y:e.offsetY, ox:V.ox, oy:V.oy }; moved = false; }\n  else if (ids.length === 2) {\n    var a = ptrs[ids[0]], b = ptrs[ids[1]];\n    pinch = { d:Math.hypot(a[0] - b[0], a[1] - b[1]), s:V.s, c:toWorld((a[0] + b[0]) \/ 2, (a[1] + b[1]) \/ 2) }; drag = null; moved = true;\n  }\n  tip.hidden = true;\n});\ncv.addEventListener('pointermove', function(e){\n  if (ptrs[e.pointerId]) ptrs[e.pointerId] = [e.offsetX, e.offsetY];\n  var ids = Object.keys(ptrs);\n  if (pinch && ids.length === 2) {\n    var a = ptrs[ids[0]], b = ptrs[ids[1]], d = Math.hypot(a[0] - b[0], a[1] - b[1]);\n    V.s = Math.max(minS(), Math.min(pinch.s * d \/ pinch.d, 0.6)); V.fitted = false;\n    var mxp = (a[0] + b[0]) \/ 2, myp = (a[1] + b[1]) \/ 2;\n    V.ox = mxp - mx() * pinch.c[0] * V.s; V.oy = myp - pinch.c[1] * V.s; draw(); return;\n  }\n  if (drag) {\n    var dx = e.offsetX - drag.x, dy = e.offsetY - drag.y;\n    if (Math.abs(dx) + Math.abs(dy) > 4) { moved = true; view.classList.add('drag'); }\n    if (moved) { V.ox = drag.ox + dx; V.oy = drag.oy + dy; V.fitted = false; draw(); }\n    return;\n  }\n  if (e.pointerType === 'mouse') hover(e.offsetX, e.offsetY);\n});\nfunction endPtr(e){\n  var was = ptrs[e.pointerId]; delete ptrs[e.pointerId];\n  if (Object.keys(ptrs).length < 2) pinch = null;\n  if (drag && !moved && was && e.type === 'pointerup') click(e.offsetX, e.offsetY);\n  if (!Object.keys(ptrs).length) { drag = null; view.classList.remove('drag'); }\n}\ncv.addEventListener('pointerup', endPtr); cv.addEventListener('pointercancel', endPtr);\ncv.addEventListener('pointerleave', function(){ tip.hidden = true; });\ncv.addEventListener('wheel', function(e){ e.preventDefault(); zoomAt(Math.exp(-e.deltaY * (e.deltaMode ? 0.05 : 0.0015)), e.offsetX, e.offsetY); }, { passive:false });\ncv.addEventListener('dblclick', function(e){ zoomAt(2, e.offsetX, e.offsetY); });\n\nfunction click(x, y){\n  var h = hit(x, y);\n  if (!h) { if (sel) select(null); return; }\n  if (h.t === 'fp') select({ t:'fp', i:h.fi });\n  else if (h.n >= 0) select({ t:'net', n:h.n, pin:h.t === 'pad' ? [h.fi, h.pi] : null });\n  else if (h.t === 'pad') select({ t:'fp', i:h.fi });\n}\nfunction hover(x, y){\n  var h = hit(x, y);\n  if (!h) { tip.hidden = true; return; }\n  var s;\n  if (h.t === 'pad') {\n    var f = B.fp[h.fi], p = f.p[h.pi];\n    s = '<b>' + esc(f.r) + '.' + esc(p[0]) + '<\/b> ' + (p[10] ? fmt(p[10]) + ' ' : '') + '\u00b7 ' + esc(shortVal(f)) + (p[9] >= 0 ? ' \u2192 ' + netLabel(p[9]) : '');\n  } else if (h.t === 'fp') {\n    var g = B.fp[h.fi]; s = '<b>' + esc(g.r) + '<\/b> ' + esc(shortVal(g)) + (g.d ? ' \u00b7 ' + esc(g.d[EN ? 1 : 0]) : '');\n  } else s = esc(tr(h.t)) + ' \u00b7 ' + (h.n >= 0 ? netLabel(h.n) : '');\n  tip.innerHTML = s; tip.hidden = false;\n  var tw = tip.offsetWidth;\n  tip.style.left = Math.min(x + 14, W - tw - 6) + 'px'; tip.style.top = (y + 16 > H - 30 ? y - 34 : y + 16) + 'px';\n}\n\n$('rfpIn').onclick = function(){ zoomAt(1.6, W \/ 2, H \/ 2); };\n$('rfpOut').onclick = function(){ zoomAt(1 \/ 1.6, W \/ 2, H \/ 2); };\n$('rfpFit').onclick = function(){ fit(); draw(); };\n$('rfpFs').onclick = function(){\n  if (document.fullscreenElement) document.exitFullscreen();\n  else if (root.requestFullscreen) root.requestFullscreen();\n};\ndocument.addEventListener('fullscreenchange', function(){\n  var on = document.fullscreenElement === root; root.classList.toggle('isfs', on); root.querySelector('.wrap').classList.toggle('fs', on);\n  setTimeout(resize, 50);\n});\ndocument.addEventListener('keydown', function(e){\n  if (e.key === 'Escape' && sel && document.activeElement !== $('rfpFind')) select(null);\n});\n\n\/\/ ---------- ricerca ----------\nvar find = $('rfpFind'), sugg = $('rfpSugg'), results = [], cur = 0;\nfunction norm(s){ return plain(s).toUpperCase().replace(\/[\\s_\\-]\/g, '').replace(\/^\\\/+\/, '\/').replace(\/\u03a6\/g, 'PHI'); }\nfunction search(q){\n  results = [];\n  if (!B || !q.trim()) return;\n  var Q = norm(q), m = \/^([A-Z]+\\d+)[.:\\s-]?([A-Z0-9]+)$\/i.exec(q.trim());\n  if (m) B.fp.forEach(function(f, fi){\n    if (f.r.toUpperCase() !== m[1].toUpperCase()) return;\n    f.p.forEach(function(p, pi){ if (String(p[0]).toUpperCase() === m[2].toUpperCase()) results.push({ k:'kPin', fi:fi, pi:pi, h:'<b>' + esc(f.r) + '.' + esc(p[0]) + '<\/b> ' + (p[10] ? fmt(p[10]) : '') + (p[9] >= 0 ? ' \u2192 ' + netLabel(p[9]) : '') }); });\n  });\n  B.order.forEach(function(i){\n    var f = B.fp[i], hay = norm(f.r + ' ' + f.v + ' ' + (f.d ? f.d[EN ? 1 : 0] : ''));\n    var exact = norm(f.r) === Q;\n    if (exact || (Q.length > 1 && hay.indexOf(Q) >= 0)) results.push({ k:'kComp', fi:i, score:exact ? 0 : 1, h:'<b>' + esc(f.r) + '<\/b> ' + esc(shortVal(f)) + (f.d ? ' \u00b7 ' + esc(f.d[EN ? 1 : 0]) : '') });\n  });\n  var nets = [];\n  for (var n in B.netPads) {\n    var name = netName(+n); if (!name) continue;\n    var nn = norm(name), pos = nn.indexOf(Q.replace(\/^\\\/\/, ''));\n    if (nn === Q || nn === '\/' + Q.replace(\/^\\\/\/, '')) nets.unshift({ k:'kNet', n:+n, h:fmt(name) + ' <span style=\"color:var(--muted)\">\u00b7 ' + B.netPads[n].length + '<\/span>' });\n    else if (pos >= 0 && Q.length > 0) nets.push({ k:'kNet', n:+n, pos:pos, len:nn.length, h:fmt(name) + ' <span style=\"color:var(--muted)\">\u00b7 ' + B.netPads[n].length + '<\/span>' });\n  }\n  results.sort(function(a, b){ return (a.score || 0) - (b.score || 0); });\n  results = results.concat(nets).slice(0, 40);\n}\nfunction renderSugg(){\n  if (!find.value.trim()) { sugg.hidden = true; return; }\n  sugg.hidden = false;\n  if (!results.length) { sugg.innerHTML = '<button disabled><span class=\"k\"><\/span>' + esc(tr('noRes')) + '<\/button>'; return; }\n  sugg.innerHTML = results.map(function(r, i){ return '<button data-i=\"' + i + '\"' + (i === cur ? ' class=\"on\"' : '') + '><span class=\"k\">' + esc(tr(r.k)) + '<\/span><span>' + r.h + '<\/span><\/button>'; }).join('');\n}\nfunction pick(r){\n  if (!r) return;\n  sugg.hidden = true; find.blur();\n  if (r.k === 'kPin') { var p = B.fp[r.fi].p[r.pi]; showSideOf(p[8]);\n    if (p[9] >= 0) select({ t:'net', n:p[9], pin:[r.fi, r.pi] }, { go:[p[1], p[2]], zoom:0.05 }); else select({ t:'fp', i:r.fi }, { go:[p[1], p[2]], zoom:0.05 }); }\n  else if (r.k === 'kComp') { showSideOf(B.fp[r.fi].s); zoomToFp(r.fi); select({ t:'fp', i:r.fi }); }\n  else { select({ t:'net', n:r.n }); }\n}\nfind.addEventListener('input', function(){ cur = 0; search(find.value); renderSugg(); });\nfind.addEventListener('focus', function(){ if (find.value) { search(find.value); renderSugg(); } });\nfind.addEventListener('keydown', function(e){\n  if (e.key === 'ArrowDown') { cur = Math.min(cur + 1, results.length - 1); renderSugg(); e.preventDefault(); }\n  else if (e.key === 'ArrowUp') { cur = Math.max(cur - 1, 0); renderSugg(); e.preventDefault(); }\n  else if (e.key === 'Enter') { pick(results[cur]); e.preventDefault(); }\n  else if (e.key === 'Escape') { sugg.hidden = true; }\n});\nsugg.addEventListener('mousedown', function(e){ var b = e.target.closest('button[data-i]'); if (b) { e.preventDefault(); pick(results[+b.getAttribute('data-i')]); } });\ndocument.addEventListener('click', function(e){ if (!e.target.closest('.find')) sugg.hidden = true; });\n\n\/\/ ---------- indirizzo condivisibile: #pcb=250469:U7 oppure #pcb=250469:U7.39 ----------\nfunction toHash(){\n  if (!B) return;\n  var v = B.id;\n  if (sel && sel.t === 'fp') v += ':' + B.fp[sel.i].r;\n  if (sel && sel.t === 'net') { var pn = sel.pin || (B.netPads[sel.n] || [])[0]; if (pn) v += ':' + B.fp[pn[0]].r + '.' + B.fp[pn[0]].p[pn[1]][0]; }\n  try { history.replaceState(null, '', '#pcb=' + v); } catch (e) {}\n}\nfunction fromHash(){\n  var m = \/#pcb=([a-z0-9]+)(?::([A-Za-z0-9]+)(?:\\.([A-Za-z0-9]+))?)?\/.exec(location.hash);\n  if (!m || m[1] !== B.id || !m[2]) return;\n  for (var i = 0; i < B.fp.length; i++) {\n    var f = B.fp[i]; if (f.r !== m[2]) continue;\n    if (m[3]) { for (var k = 0; k < f.p.length; k++) if (f.p[k][0] === m[3] && f.p[k][9] >= 0) { showSideOf(f.p[k][8]); select({ t:'net', n:f.p[k][9], pin:[i, k] }, { go:[f.p[k][1], f.p[k][2]], zoom:0.05 }); return; } }\n    showSideOf(f.s); zoomToFp(i); select({ t:'fp', i:i }); return;\n  }\n}\n\nnew ResizeObserver(resize).observe(view);\nvar hb = \/#pcb=([a-z0-9]+)\/.exec(location.hash);\nloadBoard(hb && FILES[hb[1]] ? hb[1] : '250407');\n})();\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>C64 PCB Explorer Le schede madri del Commodore 64 ricostruite pin per pin. Clicca un pin, una pista o un componente: vedi subito dove va il<span class=\"excerpt-hellip\"> [\u2026]<\/span><\/p>\n","protected":false},"author":1,"featured_media":11417,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-11409","page","type-page","status-publish","has-post-thumbnail","hentry"],"_links":{"self":[{"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/pages\/11409","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/comments?post=11409"}],"version-history":[{"count":6,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/pages\/11409\/revisions"}],"predecessor-version":[{"id":11415,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/pages\/11409\/revisions\/11415"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/media\/11417"}],"wp:attachment":[{"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/media?parent=11409"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/categories?post=11409"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/retrofixer.it\/en\/wp-json\/wp\/v2\/tags?post=11409"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}