Files
spike-board/public/index.html
2026-10-24 11:31:46 +02:00

706 lines
110 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<title>Spike 50 Parts Board</title>
<link rel="manifest" href="manifest.webmanifest" crossorigin="use-credentials">
<meta name="theme-color" content="#1d5fc4">
<meta name="mobile-web-app-capable" content="yes"><meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-title" content="Spike 50"><link rel="apple-touch-icon" href="icon-192.png">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans+Condensed:wght@500;600;700&family=IBM+Plex+Sans:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500&display=swap">
<style>
/* Layout: workshop parts board — drawing on the left, the numbered parts list beside it, status colour on every marker */
:root{
--bg:#eef1f4; --panel:#ffffff; --ink:#18212c; --muted:#5d6977; --line:#d5dbe2; --soft:#e4e9ef;
--accent:#1d5fc4; --accent-ink:#ffffff;
--have:#1f8a4c; --miss:#c8332b; --ord:#c27a0e; --skip:#7a8796; --none:#9aa6b3;
--have-bg:#e3f3e9; --miss-bg:#fbe6e4; --ord-bg:#fbf0dc; --skip-bg:#eceff2;
--f-display:"IBM Plex Sans Condensed","Arial Narrow",system-ui,sans-serif;
--f-body:"IBM Plex Sans",system-ui,-apple-system,"Segoe UI",sans-serif;
--f-mono:"IBM Plex Mono",ui-monospace,"SF Mono",Menlo,monospace;
}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){
--bg:#12171d; --panel:#1a212a; --ink:#e6ebf0; --muted:#98a4b2; --line:#2c3642; --soft:#232c37;
--accent:#5b97ef; --accent-ink:#0d1520;
--have:#4cc27c; --miss:#ef6b62; --ord:#e7a93e; --skip:#8d99a7; --none:#6b7785;
--have-bg:#173226; --miss-bg:#3a1d1c; --ord-bg:#382b13; --skip-bg:#232b34; color-scheme:dark}}
:root[data-theme="dark"]{
--bg:#12171d; --panel:#1a212a; --ink:#e6ebf0; --muted:#98a4b2; --line:#2c3642; --soft:#232c37;
--accent:#5b97ef; --accent-ink:#0d1520;
--have:#4cc27c; --miss:#ef6b62; --ord:#e7a93e; --skip:#8d99a7; --none:#6b7785;
--have-bg:#173226; --miss-bg:#3a1d1c; --ord-bg:#382b13; --skip-bg:#232b34; color-scheme:dark}
*{box-sizing:border-box}
[hidden]{display:none!important}
html{color-scheme:light}
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--f-body);font-size:14px;line-height:1.45;padding-left:max(16px,env(safe-area-inset-left,0px));padding-right:max(16px,env(safe-area-inset-right,0px));padding-block:16px 40px}
.wrap{max-width:1400px;margin:0 auto;display:grid;gap:14px}
h1,h2,h3{font-family:var(--f-display);margin:0;text-wrap:balance}
.mono{font-family:var(--f-mono);font-variant-numeric:tabular-nums}
button{font:inherit;color:inherit;cursor:pointer}
button:focus-visible,a:focus-visible,input:focus-visible,textarea:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
a{color:var(--accent)}
/* header */
.head{display:flex;flex-wrap:wrap;gap:12px 24px;align-items:end;justify-content:space-between}
.head h1{font-size:30px;font-weight:700;letter-spacing:-.01em}
.plate{display:flex;flex-wrap:wrap;gap:6px 14px;color:var(--muted);font-size:13px}
.plate b{color:var(--ink);font-weight:500}
.tally{display:flex;gap:6px;flex-wrap:wrap}
.chip{display:inline-flex;align-items:center;gap:6px;border-radius:999px;padding:3px 10px;font-size:12.5px;font-weight:500;background:var(--soft);white-space:nowrap}
.dot{width:9px;height:9px;border-radius:50%;flex:none;background:var(--none)}
.s-have .dot,.dot.s-have{background:var(--have)} .s-miss .dot,.dot.s-miss{background:var(--miss)} .s-ord .dot,.dot.s-ord{background:var(--ord)} .s-skip .dot,.dot.s-skip{background:var(--skip)}
.bar{height:6px;border-radius:3px;background:var(--soft);overflow:hidden;display:flex}
.bar i{display:block;height:100%}
/* tabs */
.tabs{display:flex;gap:6px;overflow-x:auto;padding-bottom:4px;scrollbar-width:thin}
.tab{flex:none;border:1px solid var(--line);background:var(--panel);border-radius:8px;padding:8px 12px;display:grid;gap:5px;min-width:150px;text-align:left}
.tab .t{font-family:var(--f-display);font-weight:600;font-size:14.5px}
.tab .m{font-size:12px;color:var(--muted);display:flex;justify-content:space-between;gap:8px}
.tab[aria-selected="true"]{border-color:var(--accent);box-shadow:inset 0 -3px 0 var(--accent)}
.tab.shop .t{color:var(--miss)}
/* main */
.main{display:grid;grid-template-columns:minmax(0,1.45fr) minmax(320px,1fr);gap:14px;align-items:start}
@media (max-width:900px){.main{grid-template-columns:1fr}}
.card{background:var(--panel);border:1px solid var(--line);border-radius:10px;min-width:0}
.dhead{display:flex;flex-wrap:wrap;gap:8px;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--line)}
.dhead h2{font-size:18px}
.tools{display:flex;gap:6px;flex-wrap:wrap;align-items:center}
.btn{border:1px solid var(--line);background:var(--panel);border-radius:7px;padding:5px 10px;font-size:13px}
.btn.on{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
.btn.primary{background:var(--accent);color:var(--accent-ink);border-color:var(--accent);font-weight:500}
.viewport{overflow:auto;max-height:72vh;background:#fff;border-radius:0 0 10px 10px;position:relative}
.stage{position:relative;transform-origin:0 0}
.stage img{display:block;width:100%;height:auto;max-width:none;user-select:none;-webkit-user-drag:none}
.stage.editing{cursor:crosshair}
.pin{position:absolute;transform:translate(-50%,-50%);width:30px;height:24px;border-radius:12px;border:2.5px solid var(--none);background:rgba(255,255,255,.15);padding:0;touch-action:none}
.pin.s-have{border-color:#1f8a4c;background:rgba(31,138,76,.22)} .pin.s-miss{border-color:#c8332b;background:rgba(200,51,43,.25)}
.pin.s-ord{border-color:#c27a0e;background:rgba(194,122,14,.25)} .pin.s-skip{border-color:#7a8796;background:rgba(122,135,150,.18)}
.pin.s-none{border-color:#1d5fc4;border-style:dashed;background:rgba(29,95,196,.06)}
.pin.sel{box-shadow:0 0 0 3px #1d5fc4,0 0 0 6px rgba(29,95,196,.35);z-index:3}
.pin.orphan{border-color:#8a5cc8;border-style:dotted}
.stage.editing .pin{cursor:grab}
.nodraw{padding:28px 20px;display:grid;gap:12px;justify-items:start;color:var(--muted);max-width:60ch}
.nodraw h3{color:var(--ink);font-size:17px}
.note{font-size:12.5px;color:var(--muted);padding:8px 12px;border-top:1px solid var(--line)}
/* list */
.lhead{padding:10px 12px;border-bottom:1px solid var(--line);display:grid;gap:8px}
.filters{display:flex;gap:4px;flex-wrap:wrap}
.filters .btn{padding:3px 9px;font-size:12.5px}
input[type=search],textarea{width:100%;border:1px solid var(--line);background:var(--bg);color:var(--ink);border-radius:7px;padding:6px 9px;font:inherit}
.list{max-height:72vh;overflow:auto}
.row{display:grid;grid-template-columns:44px minmax(0,1fr) auto;gap:10px;align-items:center;padding:8px 12px;border-bottom:1px solid var(--soft);width:100%;background:none;border-left:0;border-right:0;border-top:0;text-align:left}
.row:hover{background:var(--soft)}
.row.sel{background:var(--soft);box-shadow:inset 3px 0 0 var(--accent)}
.ref{font-family:var(--f-mono);font-weight:500;font-size:13px;border:2px solid var(--none);border-radius:10px;text-align:center;padding:1px 0}
.row.s-have .ref{border-color:var(--have)} .row.s-miss .ref{border-color:var(--miss)} .row.s-ord .ref{border-color:var(--ord)} .row.s-skip .ref{border-color:var(--skip)}
.rname{font-weight:500;overflow-wrap:anywhere}
.rsub{font-size:12px;color:var(--muted)}
.stat{font-size:12px;color:var(--muted);white-space:nowrap}
.detail{padding:12px;border-bottom:1px solid var(--line);background:var(--soft);display:grid;gap:10px}
.sbtns{display:grid;grid-template-columns:repeat(4,1fr);gap:6px}
.sbtns button{border:1.5px solid var(--line);background:var(--panel);border-radius:8px;padding:7px 4px;font-weight:500;font-size:13px}
.sbtns button[data-s="have"][aria-pressed="true"]{background:var(--have-bg);border-color:var(--have);color:var(--have)}
.sbtns button[data-s="miss"][aria-pressed="true"]{background:var(--miss-bg);border-color:var(--miss);color:var(--miss)}
.sbtns button[data-s="ord"][aria-pressed="true"]{background:var(--ord-bg);border-color:var(--ord);color:var(--ord)}
.sbtns button[data-s="skip"][aria-pressed="true"]{background:var(--skip-bg);border-color:var(--skip);color:var(--skip)}
.vars{display:grid;gap:4px}
.var{display:grid;grid-template-columns:auto minmax(0,1fr);gap:2px 10px;background:var(--panel);border:1px solid var(--line);border-radius:7px;padding:6px 8px;font-size:12.5px}
.var .o{font-family:var(--f-mono)}
.var .d{overflow-wrap:anywhere}
.tags{grid-column:1/-1;display:flex;gap:4px;flex-wrap:wrap}
.tag{font-size:11px;border-radius:4px;padding:1px 6px;background:var(--soft);color:var(--muted)}
.tag.good{background:var(--have-bg);color:var(--have)} .tag.bad{background:var(--miss-bg);color:var(--miss)} .tag.mine{background:var(--accent);color:var(--accent-ink)}
.var.no{opacity:.55}
.lbl{font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted);font-weight:600}
.shop{padding:14px;display:grid;gap:16px}
.shop table{width:100%;border-collapse:collapse;font-size:13px}
.shop th,.shop td{text-align:left;padding:6px 8px;border-bottom:1px solid var(--soft);vertical-align:top}
.shop th{font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--muted)}
.tblwrap{overflow-x:auto}
.empty{padding:20px 12px;color:var(--muted)}
.toast{position:fixed;left:50%;bottom:calc(16px + env(safe-area-inset-bottom,0px));transform:translateX(-50%);background:var(--ink);color:var(--bg);padding:7px 14px;border-radius:8px;font-size:13px;z-index:20}
.savebadge{font-size:12px;color:var(--muted)}
.confirm{display:flex;gap:6px;align-items:center;flex-wrap:wrap;font-size:12.5px}
@media (prefers-reduced-motion:no-preference){.pin{transition:box-shadow .15s}}
.sub{display:grid;gap:6px}
.item{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:start;background:var(--panel);border:1px solid var(--line);border-radius:7px;padding:6px 8px;font-size:13px}
.item .when{font-size:11.5px;color:var(--muted)}
.item p{margin:0;white-space:pre-wrap;overflow-wrap:anywhere}
.item a{overflow-wrap:anywhere}
.x{border:0;background:none;color:var(--muted);font-size:16px;line-height:1;padding:2px 4px;border-radius:4px}
.x:hover{color:var(--miss);background:var(--miss-bg)}
.addrow{display:grid;grid-template-columns:minmax(0,1.4fr) minmax(0,1fr) auto;gap:6px}
@media (max-width:480px){.addrow{grid-template-columns:1fr}}
input[type=url],input[type=text]{width:100%;border:1px solid var(--line);background:var(--bg);color:var(--ink);border-radius:7px;padding:6px 9px;font:inherit}
/* photos, viewer, mobile */
.pin::after{content:"";position:absolute;inset:-8px}
.pgrid{display:grid;grid-template-columns:repeat(auto-fill,minmax(96px,1fr));gap:6px}
.pgrid.big{grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:8px}
.ph{position:relative;border:1px solid var(--line);border-radius:8px;overflow:hidden;padding:0;background:var(--soft);aspect-ratio:1}
.ph img{width:100%;height:100%;object-fit:cover;display:block}
.ph span{position:absolute;left:0;right:0;bottom:0;padding:14px 6px 4px;font-size:11.5px;color:#fff;background:linear-gradient(transparent,rgba(0,0,0,.7));text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.ph .pend{position:absolute;top:4px;right:4px;background:var(--ord);color:#fff;border-radius:6px;padding:0 5px;font-size:10.5px}
.gup{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:8px;align-items:center}
.tab.photos .t{color:var(--accent)}
#viewer{position:fixed;inset:0;z-index:50;background:#0b0e12;color:#fff;display:grid;grid-template-rows:auto minmax(0,1fr) auto}
#viewer[hidden]{display:none}
.vbar{display:flex;gap:6px;align-items:center;padding:calc(8px + env(safe-area-inset-top,0px)) 10px 8px;background:#111820}
.vbar button,.vnav{border:1px solid #33404f;background:#1b2530;color:#fff;border-radius:8px;min-width:42px;height:40px;padding:0 12px;font-size:16px}
.vbar .vt{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:14px;padding:0 6px}
.vcanvas{position:relative;overflow:hidden;touch-action:none;user-select:none;-webkit-user-select:none}
.vstage{position:absolute;left:0;top:0;transform-origin:0 0;will-change:transform}
.vstage.white{background:#fff}
.vstage img{display:block;width:100%;height:100%;pointer-events:none;-webkit-user-drag:none}
.vstage .pin{transform:translate(-50%,-50%) scale(var(--k,1));width:34px;height:26px;border-width:3px}
.vstage .pin .lab{position:absolute;left:50%;bottom:calc(100% + 4px);transform:translateX(-50%);background:#18212c;color:#fff;font:600 13px var(--f-mono);padding:1px 7px;border-radius:6px;white-space:nowrap}
.vnav{position:absolute;top:50%;transform:translateY(-50%);width:44px;height:64px;padding:0;background:rgba(17,24,32,.7);z-index:2}
.vnav.prev{left:6px}.vnav.next{right:6px}
.vbottom{background:var(--panel);color:var(--ink);padding:8px 10px calc(10px + env(safe-area-inset-bottom,0px));max-height:42vh;overflow:auto;display:grid;gap:8px}
.vbottom .cap{font-size:14px;overflow-wrap:anywhere}
.vchips{display:flex;gap:5px;overflow-x:auto;padding-bottom:2px;scrollbar-width:thin}
.vchip{flex:none;min-width:40px;height:36px;border-radius:18px;border:2.5px solid var(--none);background:var(--soft);font:500 13px var(--f-mono);padding:0 8px}
.vchip.s-have{border-color:var(--have)}.vchip.s-miss{border-color:var(--miss)}.vchip.s-ord{border-color:var(--ord)}.vchip.s-skip{border-color:var(--skip)}
.vchip.sel{background:var(--accent);color:var(--accent-ink);border-color:var(--accent)}
.vsel{display:grid;gap:6px}
.vsel .nm{font-weight:600;overflow-wrap:anywhere}
.vsel .nm .mono{color:var(--accent)}
.vbottom .sbtns button{padding:11px 2px}
@media (max-width:700px){
body{padding-inline:max(10px,env(safe-area-inset-left,0px));font-size:15px}
.head h1{font-size:22px}
.plate{font-size:12px;gap:2px 10px}
.tabs{position:sticky;top:0;z-index:5;background:var(--bg);padding:6px 0;margin-top:-6px}
.tab{min-width:112px;padding:7px 10px}
.row{padding:12px;grid-template-columns:40px minmax(0,1fr) auto}
.sbtns button{padding:12px 2px;font-size:14px}
.btn{padding:9px 12px;font-size:14px}
.filters .btn{padding:7px 11px;font-size:13px}
input[type=search],input[type=url],input[type=text],textarea{font-size:16px;padding:9px 10px}
.viewport{max-height:56vh}
.list{max-height:none}
.x{font-size:22px;padding:6px 10px}
.dhead h2{font-size:17px}
}
</style>
</head>
<body>
<div class="wrap">
<header class="head">
<div style="display:grid;gap:6px">
<h1>Rieju Spike 50 · Parts Board</h1>
<div class="plate">
<span>Model <b>Spike 50 (RR/E2/4)</b></span><span>First reg. <b>2005</b></span>
<span>Engine <b>Minarelli AM6</b></span><span>Frame no. <b class="mono">…550038</b></span><span>Colour <b>Blue</b></span>
</div>
</div>
<div style="display:grid;gap:6px;min-width:min(100%,320px)">
<div class="tally" id="tally"></div>
<div class="bar" id="bar"></div>
<div style="display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap;align-items:center"><span class="savebadge" id="savebadge">Loading saved progress…</span><a class="savebadge" href="api/export">Download backup</a></div>
</div>
</header>
<nav class="tabs" id="tabs" role="tablist" aria-label="Drawing groups"></nav>
<main id="main"></main>
</div>
<div class="toast" id="toast" hidden></div>
<div id="viewer" hidden role="dialog" aria-modal="true" aria-label="Image viewer">
<div class="vbar"><button data-v="close" aria-label="Close">✕</button><span class="vt" id="vtitle"></span>
<button data-v="zout" aria-label="Zoom out">−</button><button data-v="zin" aria-label="Zoom in">+</button><button data-v="fit">Fit</button></div>
<div class="vcanvas" id="vcanvas"><div class="vstage" id="vstage" hidden></div>
<button class="vnav prev" data-v="prev" aria-label="Previous" hidden>‹</button><button class="vnav next" data-v="next" aria-label="Next" hidden>›</button></div>
<div class="vbottom" id="vbottom"></div>
</div>
<script>
const GROUPS = [{"key":"chassis","title":"Frame & swingarm","refs":[{"r":"1","v":[{"o":"0/000.010.1001","d":"LIMITED EDITION RR CHASSIS (BAST.>=74239)","a":true,"c":""},{"o":"0/000.010.0550","d":"COMPLETE PAINTED CHASSIS","a":false,"c":""},{"o":"0/000.010.1010","d":"LIMITED EDITION RR CHASSIS (BAST.<=74238)","a":false,"c":""},{"o":"0/000.010.1002","d":"LIMITED EDITION RR CHASSIS 2005","a":true,"c":""}]},{"r":"2","v":[{"o":"0/000.020.0510","d":"SWINGING ARM GREY WITHOUT TIE ROD","a":true,"c":""},{"o":"0/000.020.5100","d":"LIMITED EDITION RR BASCULE 2005","a":true,"c":""},{"o":"0/000.020.0550","d":"COMPLETE PAINTED BASCULE","a":true,"c":""}]},{"r":"3","v":[{"o":"0/000.440.5000B","d":"PLASTIC WASHER M6 6.2x14x1.2","a":true,"c":""},{"o":"0/000.980.0581","d":"DISCONTINUED","a":false,"c":""}]},{"r":"4","v":[{"o":"0/000.980.0580","d":"DISCONTINUED","a":false,"c":""}]},{"r":"5","v":[{"o":"0/000.730.0700","d":"BRAKE LEVER","a":false,"c":""}]},{"r":"6","v":[{"o":"0/000.210.0510","d":"COMPLETE KICKSTAND","a":false,"c":""},{"o":"0/000.210.0540","d":"COMPLETE KICKSTAND","a":false,"c":""}]},{"r":"7","v":[{"o":"0/000.980.0541","d":"ENGINE BRACKET 2005","a":true,"c":""},{"o":"0/000.980.0540","d":"ENGINE BRACKET","a":true,"c":""}]},{"r":"8","v":[{"o":"0/000.340.5101B","d":"QUICK NUT M6 (bag of 5)","a":true,"c":""},{"o":"0/000.380.0710","d":"CHAIN TURNBUCKLE ONLY","a":true,"c":""}]},{"r":"9","v":[{"o":"0/000.740.0040","d":"REAR FOOT SUPPORTS","a":false,"c":""},{"o":"0/000.740.0210","d":"REAR FOOT SUPPORTS","a":true,"c":""}]},{"r":"10","v":[{"o":"0/000.380.5000","d":"CHAIN ADJUSTER R/H","a":false,"c":""},{"o":"0/000.380.0690","d":"COMPLETE CHAIN TURNBUCKLE","a":false,"c":""}]},{"r":"11","v":[{"o":"0/000.340.0170B","d":"NUT M6 SELF-LOCKING (bag of 5)","a":true,"c":""},{"o":"0/000.460.0670B","d":"SCREW M8x25 (bag of 5)","a":true,"c":""}]},{"r":"12","v":[{"o":"0/000.440.0160B","d":"WASHER 8.5x17x1.25 (bag of 5)","a":true,"c":""}]},{"r":"13","v":[{"o":"0/000.460.0600B","d":"SCREW M6x16","a":true,"c":""},{"o":"0/000.460.0622","d":"SCREW M6x35","a":true,"c":""}]},{"r":"14","v":[{"o":"0/000.340.0180B","d":"NUT M8 SELF-LOCKING (bag of 5)","a":true,"c":""},{"o":"0/000.380.5001","d":"CHAIN ADJUSTER L/H","a":false,"c":""}]},{"r":"15","v":[{"o":"0/000.440.0210","d":"SPRING WASHER","a":true,"c":""}]},{"r":"16","v":[{"o":"0/000.740.0061","d":"RIGHT FOOTREST","a":false,"c":""}]},{"r":"17","v":[{"o":"0/000.740.0071","d":"LEFT FOOTREST","a":false,"c":""}]},{"r":"18","v":[{"o":"0/000.360.0720","d":"FRONT RUBBER TANK MOUNT","a":true,"c":""}]},{"r":"19","v":[{"o":"0/000.360.0090","d":"RUBBER TANK MOUNT","a":false,"c":""}]},{"r":"20","v":[{"o":"0/000.680.0060","d":"CLAMP","a":true,"c":""},{"o":"0/000.680.0070","d":"CABLE TIE","a":true,"c":""}]},{"r":"21","v":[{"o":"0/000.360.0700","d":"RUBBER TANK HALF-SHAFT","a":true,"c":""}]},{"r":"22","v":[{"o":"0/000.360.0790","d":"RUBBER CHAIN SHOE","a":false,"c":""},{"o":"0/000.360.5001","d":"CHAIN SLIDER","a":true,"c":""}]},{"r":"23","v":[{"o":"0/000.710.0300","d":"CHAIN GUIDE","a":false,"c":""},{"o":"0/000.710.0310","d":"COMPLETE CHAIN GUIDE","a":false,"c":""}]},{"r":"24","v":[{"o":"0/000.360.0370","d":"SWING ARM BUSH","a":true,"c":""}]},{"r":"25","v":[{"o":"0/000.190.0460","d":"SWINGARM AXLE","a":false,"c":""}]},{"r":"26","v":[{"o":"0/000.980.0741","d":"REAR BRAKE CALIPER BRACKET","a":false,"c":""},{"o":"0/000.980.0742","d":"REAR BRAKE CALIPER BRACKET J.JUAN","a":true,"c":""},{"o":"0/000.980.0740","d":"REAR BRAKE CALIPER BRACKET","a":false,"c":""}]},{"r":"27","v":[{"o":"0/000.470.0201","d":"LEFT FOOTREST SPRING (FRAME>701877)","a":true,"c":""}]},{"r":"28","v":[{"o":"0/000.360.0792","d":"CHAIN ROLLER","a":true,"c":""}]},{"r":"29","v":[{"o":"0/000.470.0010","d":"KICKSTAND RETURN SPRING","a":false,"c":""}]},{"r":"30","v":[{"o":"0/000.470.0760","d":"REAR BRAKE LEVER SPRING","a":true,"c":""}]},{"r":"31","v":[{"o":"0/000.340.0180B","d":"NUT M8 SELF-LOCKING (bag of 5)","a":true,"c":""}]},{"r":"32","v":[{"o":"0/000.480.0731","d":"KICKSTAND SPACER","a":true,"c":""}]},{"r":"33","v":[{"o":"0/000.480.0210","d":"RIDER FOOTREST SPACER","a":true,"c":""}]},{"r":"34","v":[{"o":"0/000.460.0471","d":"SCREW M8x45","a":false,"c":""}]},{"r":"35","v":[{"o":"0/000.460.0490","d":"SCREW M8x55 DIN 933","a":true,"c":""}]},{"r":"36","v":[{"o":"0/000.480.0645","d":"CHAIN ROLLER INNER BUSHING","a":true,"c":""}]},{"r":"37","v":[{"o":"0/000.380.0700","d":"WHEEL STOP TURNBUCKLE","a":true,"c":""}]},{"r":"38","v":[{"o":"0/000.440.0130B","d":"WASHER 6.5x17x1.5 (bag of 5)","a":true,"c":""}]},{"r":"39","v":[{"o":"0/000.340.0186","d":"REAR AXLE NUT","a":true,"c":""}]},{"r":"40","v":[{"o":"0/000.360.0700","d":"RUBBER TANK HALF-SHAFT","a":true,"c":""}]},{"r":"41","v":[{"o":"0/000.470.0200","d":"RIGHT FOOTREST SPRING (FRAME>701877)","a":true,"c":""}]},{"r":"42","v":[{"o":"0/000.390.1695","d":"DECAL SET BLUE 2005","a":true,"c":"C"},{"o":"0/000.390.1693","d":"DECAL SET RED 2005","a":true,"c":"A"},{"o":"0/000.390.1694","d":"DECAL SET BLACK 2005","a":true,"c":"B"},{"o":"0/000.390.1696","d":"DECAL SET YELLOW 2005","a":true,"c":"D"},{"o":"0/000.390.1632","d":"BLUE SPIKE 2002 DECALS SET","a":true,"c":"K"}]},{"r":"43","v":[{"o":"0/000.680.0165","d":"CLAMP 40-60","a":true,"c":""}]},{"r":"44","v":[{"o":"0/000.980.0569","d":"REAR OIL TANK BRACKET","a":false,"c":""}]},{"r":"46","v":[{"o":"0/000.460.0660B","d":"SCREW M8x20 (bag of 5)","a":true,"c":""}]},{"r":"47","v":[{"o":"0/000.460.0720","d":"SCREW 5.5x22","a":true,"c":""}]},{"r":"48","v":[{"o":"0/000.680.0300","d":"RUBBER","a":true,"c":""}]},{"r":"51","v":[{"o":"0/000.460.0745B","d":"SCREW 5.5x19 (bag of 5)","a":true,"c":""}]},{"r":"53","v":[{"o":"0/000.460.1192","d":"CHAIN COVER SCREW","a":false,"c":""}]},{"r":"80","v":[{"o":"0/000.480.0750","d":"BRAKE LEVER SPACER","a":false,"c":""}]},{"r":"100","v":[{"o":"0/000.460.0310","d":"SCREW M6x22","a":true,"c":""}]},{"r":"101","v":[{"o":"0/000.340.0170","d":"NUT M6","a":true,"c":""}]}],"pins":{"42":[[80.56,16.53]],"18":[[19.2,27.59]],"20":[[23.26,27.59],[31.3,33.53]],"21":[[21.47,29.45]],"100":[[49.02,39.81]],"35":[[4.84,45.63]],"19":[[27.63,47.26]],"30":[[11.55,49.36]],"16":[[23.34,48.54]],"41":[[25.45,51.57]],"31":[[31.54,50.99],[14.05,64.26],[39.19,71.36]],"12":[[5.07,54.25],[32.16,53.78],[38.41,55.88],[11.09,61.93],[33.96,71.59],[36.38,73.69]],"11":[[40.98,56.69]],"26":[[67.76,63.1]],"33":[[34.35,64.73]],"34":[[42.78,67.52]],"25":[[53.32,67.52]],"15":[[93.6,69.73]],"29":[[22.09,73.34]],"14":[[95.39,77.88]],"37":[[91.57,78.46]],"38":[[85.09,81.02]],"24":[[62.45,87.54]],"39":[[65.65,90.1]],"23":[[81.89,90.1]],"22":[[68.85,92.78]],"1":[[7.81,33.99]],"2":[[59.72,69.62]],"3":[[46.53,44.35]],"4":[[35.36,21.19]],"5":[[7.49,62.51]],"6":[[29.51,82.89]],"7":[[53.4,85.1]],"8":[[88.99,70.43]],"9":[[26.93,22.7]],"10":[[80.48,63.8]],"13":[[86.57,84.63]],"17":[[39.03,68.1]],"27":[[36.69,65.89]],"28":[[39.73,61.47]],"32":[[23.58,68.1]],"36":[[41.3,62.98]],"40":[[25.68,30.5]],"46":[[26.78,74.39]],"101":[[52.07,38.18]]}},{"key":"controls-steer","title":"Handlebar & controls","refs":[{"r":"1","v":[{"o":"0/000.530.5010","d":"HANDLEBAR GRIPS","a":true,"c":""},{"o":"0/000.530.0520","d":"RUBBER GRIPS SET 22 & 24","a":false,"c":""}]},{"r":"2","v":[{"o":"0/000.530.5000","d":"TWIST GRIP ASSY","a":true,"c":""},{"o":"0/000.530.0510","d":"COMPLETE THROTTLE GRIP 2000","a":false,"c":""}]},{"r":"3","v":[{"o":"0/000.550.0544","d":"THROTTLE CABLE EURO 2","a":true,"c":""},{"o":"0/000.550.0542","d":"THROTTLE CABLE EURO 2 (BAST.<73599)","a":false,"c":""},{"o":"0/000.550.0545","d":"THROTTLE CABLE FOR PRICOL OIL PUMP","a":true,"c":""},{"o":"0/000.550.0543","d":"THROTTLE CABLE 2003","a":true,"c":""},{"o":"0/000.550.0540","d":"THROTTLE CABLE","a":false,"c":""}]},{"r":"4","v":[{"o":"0/000.640.5700","d":"CABLE ADJUSTER","a":true,"c":""},{"o":"0/000.640.0700","d":"TURNBUCKLE & CABLES SET","a":true,"c":""}]},{"r":"5","v":[{"o":"0/000.990.5001","d":"R/H MIRROR","a":true,"c":""}]},{"r":"6","v":[{"o":"0/000.990.5000","d":"L/H MIRROR","a":false,"c":""},{"o":"0/000.990.0070","d":"REAR-VIEW MIRROR","a":true,"c":""}]},{"r":"7","v":[{"o":"0/000.830.0500","d":"COMPLETE CLUTCH LEVER ASSY","a":true,"c":""},{"o":"0/000.830.5001","d":"FULL CLUTCH LEVER","a":true,"c":""}]},{"r":"8","v":[{"o":"0/000.680.0070","d":"CABLE TIE","a":true,"c":""}]},{"r":"9","v":[{"o":"0/000.720.5000","d":"HANDLEBAR LEFT SWITCH","a":true,"c":""},{"o":"0/000.830.0310","d":"CLUTCH LEVER","a":true,"c":""},{"o":"0/000.720.0160","d":"SWITCH COMPLETE","a":false,"c":""}]},{"r":"10","v":[{"o":"0/000.550.0550","d":"CLUTCH CABLE","a":false,"c":""}]},{"r":"11","v":[{"o":"0/000.170.5000","d":"RR PAINTED HANDLEBAR","a":true,"c":""},{"o":"0/000.170.0550","d":"HANDLEBARS","a":false,"c":""},{"o":"0/000.170.5100","d":"HANDLEBAR","a":true,"c":""}]},{"r":"12","v":[{"o":"0/000.720.5001","d":"LIGHT SWITCH","a":true,"c":""},{"o":"0/000.410.0570","d":"HANDLEBAR END CAPS","a":true,"c":""}]},{"r":"13","v":[{"o":"0/000.720.0070","d":"FRONT BRAKE STOP SWITCH","a":true,"c":""},{"o":"0/000.640.7208","d":"FRONT BRAKE STOP SWITCH J.JUAN","a":false,"c":""}]},{"r":"14","v":[{"o":"0/000.640.0450","d":"FRONT BRAKE KIT SET","a":false,"c":""},{"o":"0/000.640.0455","d":"MASTER CYLINDER REPAIR KIT","a":false,"c":""}]},{"r":"15","v":[{"o":"0/000.820.0313","d":"MASTER CYLINDER ASSY (FRAME>552371)","a":false,"c":""},{"o":"0/000.820.0300","d":"COMPLETE FRONT BRAKE LEVER ASSY","a":false,"c":""},{"o":"0/000.820.0312","d":"COMPLETE FRONT BRAKE MASTER CYLINDER","a":false,"c":""}]},{"r":"16","v":[{"o":"0/000.830.1001","d":"FRONT BRAKE LEVER","a":false,"c":""},{"o":"0/000.830.0164","d":"FRONT BRAKE HANDLE ONLY","a":true,"c":""},{"o":"0/000.830.1003","d":"FRONT BRAKE LEVER (FRAME>552371)","a":true,"c":""}]},{"r":"17","v":[{"o":"0/000.830.1002","d":"CLUTCH LEVER ONLY","a":true,"c":""},{"o":"0/000.830.0510","d":"CLUTCH HANDLE ONLY","a":false,"c":""}]},{"r":"18","v":[{"o":"0/000.830.5110","d":"CHOKE LEVER","a":true,"c":""},{"o":"0/000.290.0570","d":"FUEL CAP VENT HOSE","a":false,"c":""}]},{"r":"19","v":[{"o":"0/000.290.0247","d":"FRONT BRAKE HOSE","a":true,"c":""},{"o":"0/000.290.0255","d":"FRONT BRAKE HOSE (FRAME>552371)","a":true,"c":""},{"o":"0/000.290.0580","d":"FRONT BRAKE TUBE ONLY","a":true,"c":""}]},{"r":"20","v":[{"o":"0/000.460.1300B","d":"BRAKE HOSE BANJO BOLT (bag of 5)","a":true,"c":""}]},{"r":"21","v":[{"o":"0/000.440.1040B","d":"BRAKE BANJO WASHER (bag of 5)","a":true,"c":""}]},{"r":"22","v":[{"o":"0/000.550.0548","d":"CHOKE CABLE","a":false,"c":""}]},{"r":"23","v":[{"o":"0/000.720.0170","d":"SWITCH COMPLETE","a":false,"c":""}]},{"r":"24","v":[{"o":"0/000.550.0549","d":"CHOKE CABLE 2003","a":false,"c":""}]},{"r":"25","v":[{"o":"0/000.830.0520","d":"CHOKE HANDLE ONLY","a":false,"c":""}]},{"r":"26","v":[{"o":"0/000.350.5001","d":"THROTTLE HOUSING CAP","a":true,"c":""}]},{"r":"27","v":[{"o":"0/000.720.5000","d":"HANDLEBAR LEFT SWITCH","a":true,"c":""}]},{"r":"28","v":[{"o":"0/000.530.5000","d":"TWIST GRIP ASSY","a":true,"c":""}]},{"r":"29","v":[{"o":"0/000.530.5010","d":"HANDLEBAR GRIPS","a":true,"c":""}]},{"r":"30","v":[{"o":"0/000.990.5001","d":"R/H MIRROR","a":true,"c":""}]},{"r":"31","v":[{"o":"0/000.640.5700","d":"CABLE ADJUSTER","a":true,"c":""}]},{"r":"32","v":[{"o":"0/000.550.5003","d":"CLUTCH CABLE","a":false,"c":""}]},{"r":"33","v":[{"o":"0/000.830.5110","d":"CHOKE LEVER","a":true,"c":""}]},{"r":"34","v":[{"o":"0/000.830.0310","d":"CLUTCH LEVER","a":true,"c":""}]},{"r":"35","v":[{"o":"0/000.830.5000","d":"COMPLETE CLUTCH LEVER ASSY 2003","a":true,"c":""}]}]},{"key":"electrics","title":"Electrics & lights","refs":[{"r":"1","v":[{"o":"0/001.000.0082","d":"COMPLETE 2003 DASHBOARD 6-WAY","a":false,"c":""},{"o":"0/001.000.2071","d":"INSTRUMENT CONSOLE ASSY","a":false,"c":""},{"o":"0/001.000.0072","d":"COMPLETE 2003 DASHBOARD","a":false,"c":""}]},{"r":"2","v":[{"o":"0/000.360.0050","d":"RUBBER MOUNT","a":true,"c":""},{"o":"0/000.460.1170B","d":"PLASTIC SCREW M5x16","a":false,"c":""}]},{"r":"3","v":[{"o":"0/000.980.7130","d":"DASH BRACKET FOR KEY IGNITION","a":true,"c":""}]},{"r":"4","v":[{"o":"0/001.060.0041","d":"IGNITION KEY AND STEERING LOCK","a":false,"c":""}]},{"r":"5","v":[{"o":"0/000.720.0070","d":"FRONT BRAKE STOP SWITCH","a":true,"c":""}]},{"r":"6","v":[{"o":"0/000.720.5000","d":"HANDLEBAR LEFT SWITCH","a":true,"c":""}]},{"r":"7","v":[{"o":"0/000.680.0060","d":"CLAMP","a":true,"c":""}]},{"r":"8","v":[{"o":"0/000.160.0551","d":"MAIN WIRING HARNESS","a":true,"c":""},{"o":"0/000.160.0550","d":"MAIN WIRING HARNESS 2003","a":false,"c":""},{"o":"0/000.160.0540","d":"MAIN WIRING HARNESS (Chassis <=45977)","a":true,"c":""}]},{"r":"9","v":[{"o":"0/001.200.5031","d":"INDICATOR RELAY","a":true,"c":""},{"o":"0/001.200.0030","d":"INDICATOR RELAY (Chassis <=45977)","a":false,"c":""}]},{"r":"10","v":[{"o":"0/000.160.0526","d":"TAILLIGHT WIRING","a":true,"c":""}]},{"r":"11","v":[{"o":"0/000.980.2000","d":"TAILLIGHT BRACKET EU","a":true,"c":""},{"o":"0/000.980.0520","d":"TAILLIGHT BRACKET","a":true,"c":""}]},{"r":"12","v":[{"o":"0/000.850.0200","d":"BULB","a":true,"c":""},{"o":"0/000.850.0220","d":"REAR LIGHT BULB 12V","a":true,"c":""}]},{"r":"13","v":[{"o":"0/000.150.0510","d":"COMPLETE TAILLIGHT","a":true,"c":""},{"o":"0/000.630.0501","d":"TAILLIGHT LENS EU","a":false,"c":""}]},{"r":"14","v":[{"o":"0/000.600.0250","d":"COMPLETE HEADLIGHT UNIT","a":false,"c":""},{"o":"0/000.470.0700","d":"HEADLIGHT AIM SPRING","a":true,"c":""}]},{"r":"15","v":[{"o":"0/000.600.0210","d":"COMPLETE HEADLIGHT UNIT","a":true,"c":""},{"o":"0/000.460.0100B","d":"HEADLIGHT MOUNT SCREW (bag of 5)","a":true,"c":""}]},{"r":"16","v":[{"o":"0/000.850.0055","d":"HEADLIGHT BULB","a":true,"c":""}]},{"r":"17","v":[{"o":"0/000.620.0500","d":"BULB HOLDER","a":false,"c":""},{"o":"0/000.620.0790","d":"HEADLIGHT SOCKET","a":false,"c":""}]},{"r":"18","v":[{"o":"0/000.360.0760","d":"RUBBER CAP","a":true,"c":""}]},{"r":"19","v":[{"o":"0/005.070.4710","d":"VOLTAGE REGULATOR DUCATI","a":false,"c":""}]},{"r":"20","v":[{"o":"0/000.460.0622","d":"SCREW M6x35","a":true,"c":""},{"o":"0/000.460.0620B","d":"SCREW M6x25 (bag of 5)","a":true,"c":""}]},{"r":"21","v":[{"o":"0/000.460.1151","d":"TEMPERATURE SENSOR","a":true,"c":""}]},{"r":"22","v":[{"o":"0/000.420.5000","d":"HORN (AC)","a":false,"c":""}]},{"r":"23","v":[{"o":"0/000.460.3010","d":"KICKSTAND SWITCH","a":true,"c":""}]},{"r":"24","v":[{"o":"0/000.720.0520","d":"IGNITION COIL 12V","a":true,"c":""}]},{"r":"25","v":[{"o":"0/005.070.2005","d":"SPARK PLUG CAP FACOMSA","a":false,"c":""}]},{"r":"26","v":[{"o":"0/005.110.2022","d":"SPARK PLUG CAP","a":true,"c":""}]},{"r":"27","v":[{"o":"0/005.822.2419","d":"STATOR (listed as lens screw)","a":true,"c":""}]},{"r":"28","v":[{"o":"0/000.460.0220","d":"HEADLIGHT AIM SCREW","a":false,"c":""}]},{"r":"29","v":[{"o":"0/000.460.0744","d":"TAILLIGHT SCREW 4.2x50","a":true,"c":""},{"o":"0/000.440.0229","d":"WASHER","a":false,"c":""}]},{"r":"30","v":[{"o":"0/001.060.0070","d":"IGNITION KEY 6-WAY 2003","a":false,"c":""},{"o":"0/000.460.0340","d":"SCREW M8x20","a":true,"c":""},{"o":"0/000.460.3002","d":"TAMPER-PROOF SCREW","a":false,"c":""}]},{"r":"31","v":[{"o":"0/000.460.0744","d":"TAILLIGHT SCREW 4.2x50","a":true,"c":""}]},{"r":"32","v":[{"o":"0/000.160.0527","d":"IGNITION LOOM 6-WAY 2003","a":true,"c":""},{"o":"0/000.160.0522","d":"IGNITION LOOM 4-WAY","a":true,"c":""},{"o":"0/000.460.0651","d":"SCREW M6x45","a":true,"c":""}]},{"r":"33","v":[{"o":"0/000.460.0610B","d":"SCREW M6x20 (bag of 5)","a":true,"c":""}]},{"r":"34","v":[{"o":"0/000.440.0130B","d":"WASHER 6.5x17x1.5 (bag of 5)","a":true,"c":""}]},{"r":"35","v":[{"o":"0/000.340.0170B","d":"NUT M6 (bag of 5)","a":true,"c":""},{"o":"0/000.770.0700","d":"HEADLIGHT STOP","a":true,"c":""}]},{"r":"36","v":[{"o":"0/000.620.1510","d":"HEADLIGHT SOCKET BRACKET","a":true,"c":""},{"o":"0/001.000.0111","d":"SPEEDOMETER ONLY","a":false,"c":""}]},{"r":"37","v":[{"o":"0/000.980.0575","d":"PLASTIC DASH BRACKET (KEY WITH LOCK)","a":true,"c":""},{"o":"0/000.980.0571","d":"PLASTIC DASH BRACKET (KEY WITH LOCK)","a":false,"c":""}]},{"r":"38","v":[{"o":"0/000.150.0510","d":"COMPLETE TAILLIGHT","a":true,"c":""}]},{"r":"39","v":[{"o":"0/000.850.0210","d":"TAILLIGHT BULB","a":false,"c":""}]},{"r":"44","v":[{"o":"0/000.460.1165B","d":"SELF-TAPPING SCREW 3.5x12 (bag of 5)","a":true,"c":""}]},{"r":"45","v":[{"o":"0/000.150.2200","d":"L/H INDICATOR","a":true,"c":""},{"o":"0/000.150.0410","d":"L/H FRONT INDICATOR","a":false,"c":""}]},{"r":"46","v":[{"o":"0/000.150.2100","d":"R/H INDICATOR","a":true,"c":""},{"o":"0/000.150.0420","d":"R/H REAR INDICATOR","a":false,"c":""}]},{"r":"47","v":[{"o":"0/000.150.0410","d":"L/H INDICATOR","a":false,"c":""}]},{"r":"48","v":[{"o":"0/000.150.0410","d":"L/H INDICATOR","a":false,"c":""}]},{"r":"49","v":[{"o":"0/000.150.0420","d":"R/H INDICATOR","a":false,"c":""}]}],"pins":{"39":[[75.88,17.46]],"12":[[79.78,16.53]],"37":[[17.1,19.56]],"29":[[90.09,20.95]],"36":[[21.86,36.32]],"35":[[28.18,35.27],[79.39,68.45]],"38":[[87.59,35.74]],"45":[[63.23,36.9],[43.09,78.46]],"30":[[35.75,38.18]],"32":[[15.07,47.73]],"46":[[87.28,48.89],[37.47,42.96]],"14":[[13.9,63.1]],"17":[[24.75,62.28]],"16":[[21.86,63.33]],"19":[[45.59,64.26]],"25":[[78.84,64.38]],"20":[[49.1,67.99]],"15":[[22.4,74.27]],"22":[[62.76,82.89]],"21":[[55.5,88.36]],"24":[[65.57,87.54]],"26":[[81.89,88.24]],"23":[[58.63,93.13]],"1":[[33.8,15.13]],"2":[[35.28,24.68]],"3":[[35.91,29.92]],"4":[[34.89,33.99]],"5":[[42.0,31.66]],"6":[[54.72,35.04]],"7":[[50.66,50.41],[64.72,46.57]],"8":[[55.27,56.0]],"9":[[58.86,41.33]],"10":[[77.28,37.37]],"11":[[72.44,21.42]],"13":[[82.98,13.5]],"18":[[27.71,64.38]],"27":[[86.1,50.87]],"31":[[8.9,33.06]],"33":[[83.76,71.83]],"34":[[28.02,31.08]]}},{"key":"exhaust-radiator-tank-airfilter","title":"Exhaust, cooling, tanks & airbox","refs":[{"r":"1","v":[{"o":"0/005.082.0272","d":"COMPLETE ENGINE","a":false,"c":""},{"o":"0/005.082.0318","d":"COMPLETE ENGINE","a":false,"c":""},{"o":"0/005.082.0312","d":"COMPLETE ENGINE (BAST.<=73599)","a":false,"c":""}]},{"r":"2","v":[{"o":"0/000.290.0514","d":"COMPLETE EXHAUST (BAST.>=73600)","a":true,"c":""},{"o":"0/000.290.0515","d":"COMPLETE EXHAUST","a":true,"c":""},{"o":"0/000.290.0503","d":"COMPLETE EXHAUST AUSTRIA","a":false,"c":""},{"o":"0/000.290.0512","d":"COMPLETE EXHAUST (BAST.<=73599)","a":true,"c":""}]},{"r":"3","v":[{"o":"0/000.260.0240","d":"EXHAUST CONNECTOR","a":true,"c":""},{"o":"0/000.680.0560","d":"EXHAUST JOINT CLAMP","a":true,"c":""},{"o":"0/000.680.0550","d":"CLAMPS","a":true,"c":""}]},{"r":"4","v":[{"o":"0/000.480.0711","d":"SPACER 14x8x5","a":true,"c":""},{"o":"0/000.360.0160","d":"EXHAUST/SILENCER GASKET","a":false,"c":""}]},{"r":"5","v":[{"o":"0/000.870.0102","d":"SILENCER","a":false,"c":""},{"o":"0/000.870.0101","d":"SILENCER","a":false,"c":""}]},{"r":"6","v":[{"o":"0/000.350.1000","d":"AIRBOX COVER","a":false,"c":""}]},{"r":"7","v":[{"o":"0/001.070.0050","d":"AIR FILTER","a":false,"c":""}]},{"r":"8","v":[{"o":"0/000.910.1021","d":"AIRBOX BODY","a":false,"c":""},{"o":"0/000.910.1010","d":"AIRBOX BODY (BAST.<=73599)","a":false,"c":""}]},{"r":"9","v":[{"o":"0/000.360.0610","d":"CARB-TO-AIRBOX RUBBER","a":true,"c":""},{"o":"0/000.360.0600","d":"CARB-TO-AIRBOX RUBBER","a":false,"c":""}]},{"r":"10","v":[{"o":"0/000.680.0150","d":"CARB RUBBER CLAMP","a":false,"c":""}]},{"r":"11","v":[{"o":"0/000.220.0560","d":"EXHAUST HEAT SHIELD","a":true,"c":""},{"o":"0/000.220.0562","d":"EXHAUST HEAT SHIELD","a":false,"c":""}]},{"r":"12","v":[{"o":"0/000.360.0460","d":"EXHAUST RUBBER MOUNT","a":false,"c":""}]},{"r":"13","v":[{"o":"0/000.470.6010","d":"FUEL LINE GUARD SPRING","a":false,"c":""}]},{"r":"14","v":[{"o":"0/005.460.1070B","d":"EXHAUST SPRING","a":true,"c":""}]},{"r":"15","v":[{"o":"0/000.290.0420","d":"FUEL LINE","a":true,"c":""}]},{"r":"16","v":[{"o":"0/000.680.0510","d":"FUEL LINE CLAMP","a":true,"c":""}]},{"r":"17","v":[{"o":"0/000.290.0420","d":"FUEL LINE","a":true,"c":""}]},{"r":"18","v":[{"o":"0/000.680.0520","d":"FUEL TAP CLAMP","a":true,"c":""}]},{"r":"19","v":[{"o":"0/000.680.0501","d":"CLIP","a":true,"c":""}]},{"r":"20","v":[{"o":"0/000.980.0572","d":"UPPER OIL TANK BRACKET","a":true,"c":""}]},{"r":"21","v":[{"o":"0/000.030.0550","d":"OIL TANK","a":true,"c":""}]},{"r":"22","v":[{"o":"0/000.410.0600","d":"OIL TANK CAP","a":true,"c":""}]},{"r":"23","v":[{"o":"0/000.410.0550","d":"OIL DIPSTICK/CAP","a":false,"c":""}]},{"r":"24","v":[{"o":"0/000.910.0200","d":"TOOL BOX","a":true,"c":""}]},{"r":"25","v":[{"o":"0/000.440.0129","d":"LOCK WASHER","a":true,"c":""}]},{"r":"26","v":[{"o":"0/000.220.0300","d":"R/H RADIATOR GUARD","a":true,"c":""}]},{"r":"27","v":[{"o":"0/000.220.0310","d":"L/H RADIATOR GUARD","a":true,"c":""}]},{"r":"28","v":[{"o":"0/000.680.0200","d":"CLAMP","a":true,"c":""}]},{"r":"29","v":[{"o":"0/000.460.1190B","d":"PLASTIC SCREW M6x12 (bag of 5)","a":false,"c":""}]},{"r":"30","v":[{"o":"0/000.640.0161","d":"RADIATOR HOSE SET","a":false,"c":""},{"o":"0/000.640.5100","d":"RADIATOR HOSE SET","a":false,"c":""}]},{"r":"31","v":[{"o":"0/000.360.0400","d":"RUBBER MOUNT","a":true,"c":""}]},{"r":"32","v":[{"o":"0/000.360.0710","d":"RUBBER SCREW HOUSING","a":false,"c":""}]},{"r":"33","v":[{"o":"0/000.580.5000","d":"RADIATOR","a":false,"c":""}]},{"r":"34","v":[{"o":"0/000.410.0150","d":"RADIATOR CAP","a":true,"c":""},{"o":"0/000.410.0151","d":"RADIATOR CAP (KTM type)","a":true,"c":""}]},{"r":"35","v":[{"o":"0/000.290.0630","d":"RADIATOR OVERFLOW PIPE 5x10","a":true,"c":""}]},{"r":"36","v":[{"o":"0/000.760.0020","d":"TOOL BAG","a":true,"c":""}]},{"r":"37","v":[{"o":"0/000.290.0630","d":"RADIATOR PIPE 5x10","a":true,"c":""}]},{"r":"38","v":[{"o":"0/000.410.0500","d":"FUEL TANK CAP","a":true,"c":""},{"o":"0/000.410.0502","d":"FUEL TANK CAP WITH KEY","a":true,"c":""}]},{"r":"39","v":[{"o":"0/000.360.0010","d":"RUBBER GASKET","a":true,"c":""}]},{"r":"40","v":[{"o":"0/000.030.0500","d":"FUEL TANK","a":false,"c":""}]},{"r":"41","v":[{"o":"0/000.290.0420","d":"FUEL LINE","a":true,"c":""}]},{"r":"42","v":[{"o":"0/000.370.0500","d":"FUEL TAP","a":true,"c":""}]},{"r":"43","v":[{"o":"0/000.460.0294B","d":"SCREW M5x50 (bag of 5)","a":true,"c":""}]},{"r":"44","v":[{"o":"0/005.430.5002","d":"KICK START LEVER","a":false,"c":""}]},{"r":"45","v":[{"o":"0/000.460.0311","d":"GEAR LEVER SCREW","a":true,"c":""}]},{"r":"46","v":[{"o":"0/005.430.0030","d":"GEAR PEDAL","a":false,"c":""}]},{"r":"47","v":[{"o":"0/000.460.0550B","d":"SCREW M8x100 (bag of 5)","a":false,"c":""}]},{"r":"48","v":[{"o":"0/000.460.1190B","d":"PLASTIC SCREW M6x12 (bag of 5)","a":false,"c":""}]},{"r":"50","v":[{"o":"0/000.680.0510","d":"FUEL LINE CLAMP","a":true,"c":""}]},{"r":"51","v":[{"o":"0/000.460.1189B","d":"PLASTIC SCREW M6x9 (bag of 5)","a":false,"c":""}]},{"r":"52","v":[{"o":"0/000.440.1210","d":"SPECIAL LOCK WASHER","a":true,"c":""}]},{"r":"53","v":[{"o":"0/000.440.1210","d":"SPECIAL LOCK WASHER","a":true,"c":""}]},{"r":"54","v":[{"o":"0/000.460.0740","d":"RADIATOR GUARD SCREW","a":true,"c":""}]},{"r":"55","v":[{"o":"0/000.460.0651","d":"SCREW M6x45","a":true,"c":""}]},{"r":"56","v":[{"o":"0/000.440.0160B","d":"WASHER 8.5x17 (bag of 5)","a":true,"c":""}]},{"r":"57","v":[{"o":"0/000.340.0180B","d":"NUT M8 (bag of 5)","a":true,"c":""}]},{"r":"58","v":[{"o":"0/000.460.0660B","d":"SCREW M8x20 (bag of 5)","a":true,"c":""}]},{"r":"59","v":[{"o":"0/000.360.0550","d":"SILENCER RUBBER MOUNT","a":false,"c":""}]},{"r":"60","v":[{"o":"0/000.440.0129","d":"LOCK WASHER","a":true,"c":""}]},{"r":"61","v":[{"o":"0/000.460.0150","d":"EXHAUST SHIELD SCREW (BAST.<=73599)","a":false,"c":""},{"o":"0/000.460.1189B","d":"PLASTIC SCREW M6x9 (bag of 5)","a":false,"c":""}]},{"r":"62","v":[{"o":"0/000.370.0440","d":"OIL TANK TAP","a":true,"c":""}]},{"r":"63","v":[{"o":"0/000.970.5050","d":"SPARK PLUG WRENCH","a":true,"c":""}]},{"r":"64","v":[{"o":"0/000.640.0000","d":"AIS VALVE","a":false,"c":""}]},{"r":"65","v":[{"o":"0/000.640.5101","d":"AIS VALVE PIPE SET","a":false,"c":""}]},{"r":"66","v":[{"o":"0/000.680.0534","d":"HOSE CLAMP 17/8","a":true,"c":""}]},{"r":"67","v":[{"o":"0/000.440.0131","d":"WASHER M6","a":true,"c":""}]},{"r":"68","v":[{"o":"0/000.460.0300B","d":"SCREW M6x16 (bag of 5)","a":true,"c":""}]},{"r":"69","v":[{"o":"0/000.660.2020","d":"EXHAUST PLATE","a":false,"c":""}]},{"r":"70","v":[{"o":"0/000.490.2000B","d":"EXHAUST GASKET 1.2mm","a":true,"c":""}]}],"pins":{"25":[[70.1,10.13]],"28":[[20.14,15.6]],"43":[[66.12,15.83]],"48":[[32.08,23.28],[10.3,68.68]],"29":[[51.21,22.82]],"70":[[57.22,26.78]],"10":[[72.21,28.64]],"56":[[84.39,28.52],[41.69,67.99],[71.35,87.08],[77.44,91.15]],"68":[[58.0,34.69]],"65":[[72.44,35.74]],"50":[[40.44,39.81],[51.83,43.54],[37.16,93.48]],"59":[[88.91,41.44]],"31":[[15.38,44.24]],"35":[[20.84,44.0]],"66":[[65.73,43.19],[61.05,49.83]],"36":[[34.5,46.57]],"41":[[49.34,46.92]],"64":[[61.98,46.92]],"42":[[40.75,49.94]],"17":[[48.48,59.02]],"53":[[10.38,60.65]],"22":[[37.31,63.8]],"26":[[22.48,68.68]],"61":[[62.45,73.81],[63.54,88.94]],"20":[[23.42,75.44]],"21":[[38.88,76.48]],"15":[[43.09,77.3]],"55":[[49.8,80.21]],"60":[[60.89,81.02]],"47":[[68.15,84.17]],"45":[[95.47,83.7]],"19":[[29.35,88.59]],"13":[[40.36,88.01],[41.37,58.79]],"14":[[43.56,88.94]],"11":[[59.09,91.5]],"57":[[80.56,93.13]],"62":[[33.1,94.88]],"46":[[92.82,93.71]],"1":[[69.01,73.34]],"2":[[49.96,94.53]],"3":[[72.44,54.37]],"4":[[68.46,46.33]],"5":[[77.28,39.23]],"6":[[68.93,18.98]],"7":[[76.5,11.76]],"8":[[80.87,14.09]],"9":[[76.11,29.1]],"12":[[44.65,67.87]],"16":[[48.17,53.2]],"18":[[45.75,40.51],[37.63,82.07]],"23":[[28.81,65.77]],"24":[[16.71,52.5]],"27":[[4.06,45.4]],"30":[[30.21,39.93]],"33":[[9.76,26.89]],"34":[[12.18,13.74]],"37":[[32.55,15.25]],"38":[[44.34,15.48]],"39":[[32.71,28.87]],"40":[[55.19,39.23]],"44":[[67.37,66.71]],"51":[[14.21,75.09],[24.67,78.35]],"52":[[24.59,82.89]],"58":[[91.1,36.44]],"63":[[25.29,47.61]],"67":[[61.05,34.69]],"69":[[61.12,28.29]]}},{"key":"motord","title":"Engine (Minarelli AM6)","refs":[{"r":"0","v":[{"o":"0/005.710.6871","d":"GASKET SET","a":true,"c":""},{"o":"0/000.640.9999","d":"TOOL SET","a":true,"c":""},{"o":"0/005.710.7266","d":"CUB SET","a":true,"c":""}]},{"r":"15","v":[{"o":"0/005.155.7141","d":"CARBURETOR PHBN 16 (SPIKE >= 73600)","a":false,"c":""},{"o":"0/005.155.7113","d":"CARBURETOR PHBN 12","a":false,"c":""}]},{"r":"19","v":[{"o":"0/005.718.2941","d":"CYLINDER WITH PISTON","a":false,"c":""}]},{"r":"62","v":[{"o":"0/005.711.2150","d":"PISTON COMPLETE 40.3","a":true,"c":""}]},{"r":"77","v":[{"o":"0/005.770.5252","d":"CYLINDER HEAD","a":true,"c":""}]},{"r":"82","v":[{"o":"0/005.822.2419","d":"STATOR","a":true,"c":""}]},{"r":"111","v":[{"o":"0/005.110.1559","d":"SPARK PLUG NGK BR9","a":true,"c":""}]},{"r":"212","v":[{"o":"0/005.210.7327","d":"CLUTCH COVER","a":false,"c":""}]},{"r":"213","v":[{"o":"0/005.210.7325","d":"OIL PUMP COVER","a":false,"c":""}]},{"r":"214","v":[{"o":"0/005.210.9114","d":"FLYWHEEL COVER","a":false,"c":""}]},{"r":"381","v":[{"o":"0/005.715.0780","d":"COMPLETE CRANKSHAFT","a":true,"c":""}]},{"r":"382","v":[{"o":"0/005.715.3464","d":"CONNECTING ROD 18","a":true,"c":""},{"o":"0/005.715.3377","d":"CONNECTING ROD 16","a":false,"c":""}]},{"r":"471","v":[{"o":"0/005.470.1067","d":"SPRING GUIDE CUP","a":true,"c":""}]},{"r":"582","v":[{"o":"0/005.580.4211","d":"FRONT SPROCKET Z11","a":false,"c":""}]},{"r":"606","v":[{"o":"0/005.601.4980","d":"INTAKE MANIFOLD 20","a":true,"c":""}]},{"r":"641","v":[{"o":"0/005.640.0196","d":"OIL PUMP PRICOL","a":true,"c":""},{"o":"0/005.640.1080","d":"OIL PUMP MIKUNI","a":false,"c":""}]},{"r":"681","v":[{"o":"0/005.714.2080","d":"PISTON RING SET 40.3x1.2","a":true,"c":""}]},{"r":"682","v":[{"o":"0/005.714.2080","d":"PISTON RING SET 40.3x1.2","a":true,"c":""}]},{"r":"701","v":[{"o":"0/005.703.7080","d":"CRANKCASE PAIR","a":false,"c":""}]},{"r":"911","v":[{"o":"0/005.010.3387","d":"PRIMARY SHAFT","a":false,"c":""}]},{"r":"912","v":[{"o":"0/005.010.1167","d":"KICKSTART SHAFT","a":true,"c":""}]},{"r":"914","v":[{"o":"0/005.010.4347","d":"SECONDARY SHAFT","a":true,"c":""}]},{"r":"915","v":[{"o":"0/005.010.5110","d":"SELECTOR SHAFT","a":true,"c":""}]},{"r":"917","v":[{"o":"0/005.010.9040","d":"COUNTERSHAFT","a":false,"c":""}]},{"r":"2430","v":[{"o":"0/005.240.5335","d":"OIL PUMP CABLE RUBBER","a":false,"c":""}]},{"r":"2478","v":[{"o":"0/005.240.5334","d":"OIL LINE RUBBER","a":true,"c":""}]},{"r":"2607","v":[{"o":"0/005.260.5535","d":"HOSE","a":true,"c":""}]},{"r":"2655","v":[{"o":"0/005.260.6057","d":"TUBE SUPPORT","a":true,"c":""}]},{"r":"4001","v":[{"o":"0/005.710.2527","d":"CLUTCH BASKET WITH GEAR 20/71","a":true,"c":""}]},{"r":"4045","v":[{"o":"0/005.400.5290","d":"KICKSTART GEAR Z21","a":false,"c":""}]},{"r":"4060","v":[{"o":"0/005.400.5673","d":"COUNTERSHAFT GEAR Z34","a":false,"c":""}]},{"r":"4066","v":[{"o":"0/005.400.6547","d":"KICKSTART PINION","a":true,"c":""}]},{"r":"4625","v":[{"o":"0/005.460.1407","d":"KICKSTART PINION SPRING","a":false,"c":""}]},{"r":"4665","v":[{"o":"0/005.460.2297","d":"KICKSTART RETURN SPRING","a":false,"c":""}]},{"r":"5303","v":[{"o":"0/005.530.1047B","d":"CIRCLIP (bag of 5)","a":true,"c":""}]},{"r":"5310","v":[{"o":"0/005.530.1759B","d":"O-RING 25.07x2.62 VITON (bag of 5)","a":true,"c":""}]},{"r":"5315","v":[{"o":"0/005.530.1788","d":"RETAINING RING 15.1x2.7","a":true,"c":""}]},{"r":"5330","v":[{"o":"0/005.530.8860","d":"FLYWHEEL GASKET","a":true,"c":""}]},{"r":"6015","v":[{"o":"0/005.600.0871","d":"REED VALVE","a":true,"c":""}]},{"r":"9323","v":[{"o":"0/005.030.2130","d":"PISTON PIN CLIP","a":true,"c":""}]},{"r":"9351","v":[{"o":"0/005.030.3016","d":"CIRCLIP 8","a":true,"c":""}]},{"r":"9352","v":[{"o":"0/005.030.3026","d":"CIRCLIP 12","a":true,"c":""}]},{"r":"9355","v":[{"o":"0/005.030.3057","d":"CIRCLIP 18","a":true,"c":""}]},{"r":"9356","v":[{"o":"0/005.030.3066","d":"CIRCLIP 18 KICKSTART","a":true,"c":""}]},{"r":"9362","v":[{"o":"0/005.030.3127","d":"CIRCLIP 20","a":true,"c":""}]},{"r":"9363","v":[{"o":"0/005.030.3139","d":"CIRCLIP 14","a":true,"c":""}]},{"r":"9365","v":[{"o":"0/005.030.3147","d":"CIRCLIP 20","a":true,"c":""}]},{"r":"24107","v":[{"o":"0/005.240.3750","d":"CARB OVERFLOW TUBE","a":false,"c":""}]},{"r":"49001","v":[{"o":"0/005.490.0439","d":"STARTER MOTOR BRACKET","a":true,"c":""}]},{"r":"49005","v":[{"o":"0/005.490.0190","d":"FIXING BAND","a":false,"c":""}]},{"r":"49022","v":[{"o":"0/005.490.0499","d":"FIXING BAND 8 BLACK","a":false,"c":""}]},{"r":"49070","v":[{"o":"0/005.490.4663","d":"SPACER 16.9x24x8","a":true,"c":""}]},{"r":"49099","v":[{"o":"0/005.490.3662","d":"NUT M5x0.75","a":true,"c":""}]},{"r":"49131","v":[{"o":"0/005.490.6514","d":"WASHER 16x25x0.4","a":true,"c":""}]},{"r":"49135","v":[{"o":"0/005.490.6556","d":"WASHER 20.45x28x0.6","a":true,"c":""}]},{"r":"49193","v":[{"o":"0/005.490.7010","d":"WASHER 14x26x0.5","a":true,"c":""}]},{"r":"49244","v":[{"o":"0/000.460.0310","d":"SCREW M6x22","a":true,"c":""}]},{"r":"49255","v":[{"o":"0/005.491.9059","d":"SCREW M6x12","a":true,"c":""}]},{"r":"49256","v":[{"o":"0/005.490.8599","d":"SCREW M5x12","a":true,"c":""}]},{"r":"49292","v":[{"o":"0/005.491.9200","d":"SCREW M6x20","a":false,"c":""}]},{"r":"49405","v":[{"o":"0/005.491.7570","d":"WASHER 6x11x1.2","a":true,"c":""}]},{"r":"49514","v":[{"o":"0/005.491.9631","d":"SCREW M6x25","a":true,"c":""}]},{"r":"49528","v":[{"o":"0/005.491.8071","d":"DISCONTINUED","a":false,"c":""}]},{"r":"53309","v":[{"o":"0/005.530.1749B","d":"O-RING 2.62 (bag of 5)","a":true,"c":""}]},{"r":"53310","v":[{"o":"0/005.530.1739B","d":"O-RING 2.62 (bag of 5)","a":true,"c":""}]}],"pins":{"15":[[21.94,21.65]],"19":[[52.07,21.65]],"62":[[61.9,36.67]],"77":[[53.47,15.37]],"82":[[10.77,24.56]],"111":[[53.55,12.57]],"212":[[79.16,86.38]],"213":[[89.38,46.22]],"214":[[1.01,35.16]],"381":[[38.8,35.74]],"382":[[48.56,12.22]],"471":[[65.18,90.22]],"582":[[18.81,53.32]],"606":[[29.66,20.02]],"641":[[83.06,44.35]],"681":[[61.9,33.29]],"682":[[62.06,34.81]],"701":[[25.21,32.13],[50.74,47.5]],"911":[[34.5,54.95]],"912":[[70.18,90.69]],"914":[[25.76,66.71]],"915":[[14.21,71.25]],"917":[[33.02,40.51]],"2430":[[85.95,48.31]],"2478":[[93.52,38.18]],"4001":[[66.9,52.15],[52.15,66.94]],"4045":[[58.24,86.61]],"4060":[[68.31,42.72]],"4066":[[58.39,95.81]],"4625":[[60.27,88.13]],"4665":[[64.17,99.65]],"5303":[[45.04,27.47]],"5310":[[96.8,32.13]],"5315":[[79.78,66.24]],"5330":[[6.87,22.47]],"6015":[[32.94,19.79]],"9323":[[53.32,37.6]],"9351":[[79.39,38.18],[74.32,56.58],[72.83,65.89]],"9352":[[7.42,65.77],[19.44,71.59]],"9355":[[33.72,61.23]],"9356":[[61.75,97.79]],"9362":[[16.32,63.45],[24.12,56.58]],"9363":[[40.2,75.2]],"24107":[[31.38,10.94]],"49005":[[25.14,22.12]],"49022":[[27.01,15.13]],"49070":[[44.81,39.12]],"49131":[[72.29,92.2]],"49135":[[23.73,65.54],[33.72,70.2]],"49244":[[27.17,17.0]],"49255":[[41.45,42.26],[99.06,73.92]],"49292":[[29.12,16.41]],"53309":[[65.42,22.12]],"53310":[[65.34,24.21]]}},{"key":"plastics","title":"Bodywork & seat","refs":[{"r":"1","v":[{"o":"0/000.620.1600","d":"HEADLIGHT COWL BLUE 2005","a":false,"c":"C"},{"o":"0/000.620.1602","d":"HEADLIGHT COWL RED 2005","a":true,"c":"A"},{"o":"0/000.620.5201","d":"HEADLIGHT COWL BLACK 2005","a":false,"c":"D"},{"o":"0/000.620.2500","d":"HEADLIGHT COWL BLUE (older)","a":true,"c":"D"},{"o":"0/000.620.2501","d":"HEADLIGHT COWL BLUE 2002","a":true,"c":"K"}]},{"r":"2","v":[{"o":"0/000.360.0171","d":"RUBBER HEADLIGHT BRACKET","a":false,"c":""},{"o":"0/000.360.0173","d":"RUBBER HEADLIGHT LOCK BRACKET","a":false,"c":""}]},{"r":"3","v":[{"o":"0/000.570.2612","d":"R/H RADIATOR PANEL BLUE","a":true,"c":"C"},{"o":"0/000.570.2620","d":"R/H RADIATOR PANEL RED","a":true,"c":"A"},{"o":"0/000.570.2616","d":"R/H RADIATOR PANEL BLACK","a":false,"c":"B"},{"o":"0/000.570.2500","d":"R/H RADIATOR PANEL BLUE (older)","a":true,"c":"D"}]},{"r":"4","v":[{"o":"0/000.050.2100","d":"FRONT MUDGUARD","a":false,"c":"C"},{"o":"0/000.050.0522","d":"FRONT MUDGUARD","a":false,"c":"A"},{"o":"0/000.050.2500","d":"FRONT MUDGUARD BLUE (older)","a":true,"c":"D"},{"o":"0/000.050.0530","d":"FRONT MUDGUARD GREY","a":true,"c":"E"}]},{"r":"5","v":[{"o":"0/000.220.0540","d":"FORK GUARDS (pair)","a":false,"c":""},{"o":"0/000.220.5001","d":"FORK GUARD","a":false,"c":""}]},{"r":"6","v":[{"o":"0/000.570.2613","d":"L/H RADIATOR PANEL BLUE","a":false,"c":"C"},{"o":"0/000.570.2621","d":"L/H RADIATOR PANEL RED","a":true,"c":"A"},{"o":"0/000.570.2617","d":"L/H RADIATOR PANEL BLACK","a":false,"c":"B"},{"o":"0/000.570.2510","d":"L/H RADIATOR PANEL BLUE (older)","a":true,"c":"D"}]},{"r":"7","v":[{"o":"0/000.570.2602","d":"R/H SIDE PANEL BLUE","a":false,"c":"C"},{"o":"0/000.570.2610","d":"R/H SIDE PANEL RED","a":true,"c":"A"},{"o":"0/000.570.2606","d":"R/H SIDE PANEL BLACK","a":true,"c":"B"},{"o":"0/000.570.2520","d":"R/H SIDE PANEL BLUE (older)","a":true,"c":"D"}]},{"r":"8","v":[{"o":"0/000.350.5510","d":"HEADLIGHT COVER BLUE","a":true,"c":"C"},{"o":"0/000.350.5517","d":"HEADLIGHT COVER BLACK","a":true,"c":"B"},{"o":"0/000.460.0600B","d":"SCREW M6x16","a":true,"c":""}]},{"r":"9","v":[{"o":"0/000.040.0550","d":"COMPLETE SEAT GREY","a":true,"c":"C"},{"o":"0/000.040.2200","d":"SEAT","a":true,"c":""},{"o":"0/000.040.2300","d":"SEAT","a":true,"c":"D"},{"o":"0/000.040.2140","d":"COMPLETE SEAT BLUE","a":true,"c":"D"}]},{"r":"10","v":[{"o":"0/000.570.2603","d":"L/H SIDE PANEL BLUE","a":false,"c":"C"},{"o":"0/000.570.2611","d":"L/H SIDE PANEL RED","a":true,"c":"A"},{"o":"0/000.570.2607","d":"L/H SIDE PANEL BLACK","a":false,"c":"B"},{"o":"0/000.570.2530","d":"L/H SIDE PANEL BLUE (older)","a":false,"c":"D"}]},{"r":"11","v":[{"o":"0/000.060.2600","d":"REAR MUDGUARD BLUE","a":false,"c":"C"},{"o":"0/000.060.2630","d":"REAR MUDGUARD RED","a":false,"c":"A"},{"o":"0/000.060.2660","d":"REAR MUDGUARD BLACK","a":false,"c":"B"},{"o":"0/000.060.2500","d":"REAR MUDGUARD BLUE (older)","a":true,"c":"D"}]},{"r":"12","v":[{"o":"0/000.220.0510","d":"FRONT SPROCKET COVER","a":true,"c":""}]},{"r":"13","v":[{"o":"0/000.200.0311","d":"SHOCK COVER","a":true,"c":""},{"o":"0/000.200.0310","d":"SHOCK COVER","a":false,"c":""}]},{"r":"14","v":[{"o":"0/000.200.0301","d":"CHAIN GUARD","a":true,"c":""},{"o":"0/000.200.0300","d":"CHAIN COVER","a":false,"c":""}]},{"r":"15","v":[{"o":"0/000.470.0510","d":"BRAKE LINE GUIDE SPRING","a":true,"c":""},{"o":"0/000.360.0172","d":"RUBBER LIGHT SUPPORT","a":true,"c":""},{"o":"0/000.470.0520","d":"SPEEDO CABLE GUIDE SPRING","a":true,"c":""}]},{"r":"16","v":[{"o":"0/000.470.0510","d":"BRAKE LINE GUIDE SPRING","a":true,"c":""},{"o":"0/000.470.0520","d":"SPEEDO CABLE GUIDE SPRING","a":true,"c":""}]},{"r":"17","v":[{"o":"0/000.480.0450","d":"MUDGUARD SPACER","a":true,"c":""}]},{"r":"18","v":[{"o":"0/000.480.1160","d":"FORK CAP SPACER","a":true,"c":""}]},{"r":"19","v":[{"o":"0/000.460.1189B","d":"PLASTIC SCREW M6x9 (bag of 5)","a":false,"c":""}]},{"r":"20","v":[{"o":"0/000.460.1180B","d":"PLASTIC SCREW M6x17.5 (bag of 5)","a":false,"c":""}]},{"r":"21","v":[{"o":"0/000.460.2000B","d":"SCREW M5x10 (bag of 5)","a":true,"c":""}]},{"r":"22","v":[{"o":"0/000.440.0130B","d":"WASHER 6.5x17x1.5 (bag of 5)","a":true,"c":""}]},{"r":"23","v":[{"o":"0/000.460.0610B","d":"SCREW M6x20 (bag of 5)","a":true,"c":""}]},{"r":"24","v":[{"o":"0/000.460.0421","d":"FRONT BRAKE LEVER BOLT","a":true,"c":""},{"o":"0/000.460.0620B","d":"SCREW M6x25 (bag of 5)","a":true,"c":""}]},{"r":"25","v":[{"o":"0/000.440.0129","d":"LOCK WASHER","a":true,"c":""}]},{"r":"26","v":[{"o":"0/000.460.0291B","d":"SCREW M5x25 (bag of 5)","a":true,"c":""},{"o":"0/000.460.0280","d":"SCREW M5x12","a":true,"c":""}]},{"r":"27","v":[{"o":"0/000.340.0172","d":"NUT M5","a":true,"c":""}]},{"r":"28","v":[{"o":"0/000.340.0170B","d":"NUT M6 (bag of 5)","a":true,"c":""}]},{"r":"29","v":[{"o":"0/000.460.0322","d":"SCREW WITH SPACER","a":true,"c":""}]},{"r":"30","v":[{"o":"0/000.460.0294B","d":"SCREW M5x50 (bag of 5)","a":true,"c":""}]},{"r":"31","v":[{"o":"0/000.060.1020","d":"REAR MUDGUARD RR SPIKE 2005 (number plate holder)","a":false,"c":""},{"o":"0/000.060.1010","d":"TAIL MUDGUARD","a":false,"c":""}]},{"r":"32","v":[{"o":"0/000.440.5000B","d":"PLASTIC WASHER M6","a":true,"c":""}]},{"r":"33","v":[{"o":"0/000.220.0570","d":"BASH PLATE","a":true,"c":""},{"o":"0/000.460.1182B","d":"PLASTIC SCREW M6x15 (bag of 5)","a":true,"c":""}]},{"r":"34","v":[{"o":"0/000.680.0070","d":"CABLE TIE","a":true,"c":""},{"o":"0/000.680.0150","d":"CARB RUBBER CLAMP","a":false,"c":""}]},{"r":"35","v":[{"o":"0/000.980.0511","d":"HEADLIGHT BRACKET","a":true,"c":""},{"o":"0/000.360.0173","d":"RUBBER HEADLIGHT LOCK BRACKET","a":false,"c":""}]},{"r":"36","v":[{"o":"0/000.980.0511","d":"HEADLIGHT BRACKET","a":true,"c":""}]},{"r":"37","v":[{"o":"0/000.350.0500","d":"DISCONTINUED","a":false,"c":""}]},{"r":"38","v":[{"o":"0/000.300.0550","d":"DISCONTINUED","a":false,"c":""}]},{"r":"39","v":[{"o":"0/000.460.1165B","d":"SELF-TAPPING SCREW 3.5x12 (bag of 5)","a":true,"c":""}]},{"r":"40","v":[{"o":"0/000.460.1180B","d":"PLASTIC SCREW M6x17.5 (bag of 5)","a":false,"c":""}]},{"r":"41","v":[{"o":"0/000.460.0300B","d":"SCREW M6x16 (bag of 5)","a":true,"c":""}]},{"r":"42","v":[{"o":"0/000.440.5001","d":"FIBRE WASHER","a":true,"c":""}]},{"r":"43","v":[{"o":"0/000.680.5020","d":"CABLE GUIDE","a":false,"c":""}]},{"r":"44","v":[{"o":"0/000.620.5201","d":"HEADLIGHT COWL BLACK 2005","a":false,"c":""},{"o":"0/000.220.0540","d":"FORK GUARDS (pair)","a":false,"c":""}]},{"r":"45","v":[{"o":"0/000.460.1183","d":"SCREW","a":false,"c":""}]}],"pins":{"20":[[77.91,13.04]],"11":[[67.6,14.78]],"19":[[39.97,20.49],[77.36,35.16],[67.53,54.48]],"28":[[73.85,27.94],[29.35,32.36],[57.92,51.34],[69.09,61.58]],"10":[[78.22,47.61]],"21":[[48.79,51.46],[29.51,40.86]],"14":[[84.54,53.32]],"17":[[33.33,56.46]],"22":[[31.15,63.56],[68.31,68.45],[20.53,89.41]],"15":[[25.53,67.17]],"24":[[32.24,70.55]],"23":[[30.91,72.88]],"13":[[61.51,73.81]],"30":[[32.47,80.33]],"26":[[57.38,81.26]],"25":[[51.44,85.91],[31.54,76.37]],"42":[[15.46,92.2]],"33":[[33.33,92.43]],"34":[[43.87,94.99]],"41":[[17.25,96.97]],"1":[[12.26,28.75]],"3":[[16.0,46.22]],"4":[[10.15,76.02]],"5":[[18.74,80.33]],"6":[[38.33,49.01]],"7":[[46.37,25.61]],"8":[[71.51,68.92]],"9":[[54.64,29.8]],"12":[[48.24,81.14]],"16":[[23.42,75.9]],"18":[[25.68,94.76]],"27":[[22.4,72.99]],"29":[[17.1,93.36]],"31":[[87.28,20.95]],"32":[[50.43,15.25]],"35":[[35.44,15.48]],"36":[[31.93,21.42]],"43":[[2.5,84.63]],"44":[[28.88,83.7]],"45":[[33.57,33.29]]}},{"key":"suspension","title":"Fork, steering & rear shock","refs":[{"r":"0","v":[{"o":"0/000.130.0530","d":"COMPLETE FORK 2005","a":false,"c":""},{"o":"0/000.130.0540","d":"COMPLETE FORK","a":true,"c":""},{"o":"0/000.130.0520","d":"COMPLETE FORK (BAST.>=74239)","a":false,"c":""},{"o":"0/000.130.0510","d":"COMPLETE FORK (BAST.<=74238)","a":false,"c":""}]},{"r":"1","v":[{"o":"0/000.460.0345B","d":"SCREW M8x25 (bag of 5)","a":true,"c":""}]},{"r":"2","v":[{"o":"0/000.440.0180","d":"WASHER","a":true,"c":""}]},{"r":"3","v":[{"o":"0/000.460.0480","d":"SCREW M8x50","a":true,"c":""}]},{"r":"4","v":[{"o":"0/000.340.0180B","d":"NUT M8 (bag of 5)","a":true,"c":""}]},{"r":"5","v":[{"o":"0/000.880.0522","d":"RIGHT FORK LEG CAP","a":true,"c":""},{"o":"0/000.880.0511","d":"RIGHT FORK CAP","a":true,"c":""},{"o":"0/000.880.0530","d":"RIGHT FORK LEG CAP","a":true,"c":""}]},{"r":"6","v":[{"o":"0/000.880.0523","d":"LEFT FORK CAP","a":true,"c":""},{"o":"0/000.880.0521","d":"LEFT FORK CAP","a":true,"c":""},{"o":"0/000.880.0540","d":"LEFT FORK LEG CAP","a":true,"c":""}]},{"r":"7","v":[{"o":"0/000.710.0500","d":"FORK SPRING GUIDE","a":true,"c":""},{"o":"0/000.490.0520","d":"FORK CAP O-RING","a":true,"c":""}]},{"r":"8","v":[{"o":"0/000.410.0520","d":"LOWER FORK CAP","a":true,"c":""},{"o":"0/000.440.1200","d":"FORK SPRING WASHER","a":true,"c":""}]},{"r":"9","v":[{"o":"0/000.290.0520","d":"FORK BUMP STOP","a":true,"c":""},{"o":"0/000.460.0342","d":"SPRING STOP SCREW","a":true,"c":""}]},{"r":"10","v":[{"o":"0/000.470.0720","d":"FORK MAIN SPRING","a":false,"c":""},{"o":"0/000.470.0750","d":"FORK REBOUND SPRING","a":false,"c":""}]},{"r":"11","v":[{"o":"0/000.290.0551","d":"RIGHT FORK TUBE","a":true,"c":""},{"o":"0/000.290.0550","d":"RIGHT FORK TUBE","a":true,"c":""},{"o":"0/000.290.0531","d":"FORK TUBE","a":true,"c":""}]},{"r":"12","v":[{"o":"0/000.460.0320","d":"SCREW M8x30","a":true,"c":""},{"o":"0/000.440.1200","d":"FORK SPRING WASHER","a":true,"c":""}]},{"r":"13","v":[{"o":"0/000.460.0342","d":"SPRING STOP SCREW","a":true,"c":""}]},{"r":"14","v":[{"o":"0/000.710.0500","d":"FORK SPRING GUIDE","a":true,"c":""}]},{"r":"15","v":[{"o":"0/000.880.0501","d":"FORK LEG L/R","a":true,"c":""},{"o":"0/000.880.0500","d":"FORK LEG","a":true,"c":""},{"o":"0/000.880.0502","d":"FORK LEG","a":true,"c":""}]},{"r":"16","v":[{"o":"0/000.410.0210","d":"UPPER FORK CAP","a":false,"c":""}]},{"r":"17","v":[{"o":"0/000.410.0510","d":"THREADED FORK CAP","a":true,"c":""}]},{"r":"18","v":[{"o":"0/000.490.0510","d":"UPPER FORK O-RING","a":false,"c":""}]},{"r":"19","v":[{"o":"0/000.490.0500","d":"FORK O-RING","a":false,"c":""}]},{"r":"20","v":[{"o":"0/000.300.0500","d":"FORK CIRCLIP","a":true,"c":""}]},{"r":"21","v":[{"o":"0/000.290.0510","d":"FORK HEAD TUBE","a":true,"c":""},{"o":"0/000.290.0511","d":"FORK HEAD TUBE","a":true,"c":""}]},{"r":"22","v":[{"o":"0/000.980.0530","d":"FORK SEAL BRACKET","a":false,"c":""}]},{"r":"23","v":[{"o":"0/000.890.0500","d":"FORK OIL SEAL","a":true,"c":""}]},{"r":"24","v":[{"o":"0/000.300.0510","d":"FORK SEAL CIRCLIPS","a":false,"c":""}]},{"r":"25","v":[{"o":"0/000.890.0510","d":"FORK DUST SEAL","a":false,"c":""}]},{"r":"26","v":[{"o":"0/000.660.0670","d":"LOWER TRIPLE CLAMP WITH STEM","a":true,"c":""},{"o":"0/000.660.0663","d":"LOWER TRIPLE CLAMP","a":true,"c":""},{"o":"0/000.660.0661","d":"LOWER TRIPLE CLAMP (BAST.>=74239)","a":true,"c":""},{"o":"0/000.660.0660","d":"LOWER TRIPLE CLAMP (BAST.<=74238)","a":true,"c":""}]},{"r":"27","v":[{"o":"0/000.460.1602","d":"STEERING STOP SCREW","a":true,"c":""},{"o":"0/000.460.1601B","d":"STEERING STOP SCREW (bag of 5)","a":true,"c":""}]},{"r":"28","v":[{"o":"0/000.660.0680","d":"UPPER TRIPLE CLAMP","a":true,"c":""},{"o":"0/000.660.0662","d":"UPPER TRIPLE CLAMP","a":true,"c":""},{"o":"0/000.660.0650","d":"UPPER TRIPLE CLAMP","a":true,"c":""}]},{"r":"29","v":[{"o":"0/000.960.0100","d":"STEERING BEARING","a":true,"c":""},{"o":"0/000.960.0200","d":"STEERING BEARING","a":true,"c":""},{"o":"0/000.960.0040","d":"STEERING BEARING SET","a":false,"c":""}]},{"r":"30","v":[{"o":"0/000.200.7001","d":"COVER","a":false,"c":""}]},{"r":"31","v":[{"o":"0/000.200.7000","d":"STEERING DUST COVER","a":false,"c":""}]},{"r":"32","v":[{"o":"0/000.340.0550","d":"LOWER STEERING NUT","a":false,"c":""},{"o":"0/000.340.0620","d":"STEERING NUT (BAST.<=74238)","a":true,"c":""}]},{"r":"33","v":[{"o":"0/000.440.0050B","d":"STEERING NUT WASHER (bag of 5)","a":false,"c":""}]},{"r":"34","v":[{"o":"0/000.340.0325","d":"UPPER STEERING NUT","a":false,"c":""},{"o":"0/000.340.0320B","d":"STEERING STEM CAP NUT (bag of 5)","a":false,"c":""}]},{"r":"35","v":[{"o":"0/000.980.0630","d":"GUIDE","a":true,"c":""},{"o":"0/000.290.0540","d":"LEFT FORK TUBE","a":true,"c":""},{"o":"0/000.290.0541","d":"LEFT FORK TUBE","a":true,"c":""}]},{"r":"36","v":[{"o":"0/000.460.0320","d":"SCREW M8x30","a":true,"c":""},{"o":"0/000.460.0353","d":"SCREW M8x45","a":false,"c":""}]},{"r":"37","v":[{"o":"0/000.440.0180","d":"WASHER","a":true,"c":""}]},{"r":"38","v":[{"o":"0/000.460.0470","d":"SCREW M8x40","a":true,"c":""}]},{"r":"39","v":[{"o":"0/000.290.0530","d":"LEFT FORK VALVE TUBE","a":true,"c":""}]},{"r":"40","v":[{"o":"0/000.360.0370","d":"SWING ARM BUSH","a":true,"c":""}]},{"r":"41","v":[{"o":"0/000.460.0402B","d":"SCREW M10x50 (bag of 5)","a":true,"c":""},{"o":"0/000.340.0182B","d":"NUT M10 (bag of 5)","a":true,"c":""}]},{"r":"42","v":[{"o":"0/000.440.0110B","d":"WASHER 10x24x1.5 (bag of 5)","a":true,"c":""}]},{"r":"43","v":[{"o":"0/000.340.0182B","d":"NUT M10 (bag of 5)","a":true,"c":""},{"o":"0/000.460.0402B","d":"SCREW M10x50 (bag of 5)","a":true,"c":""}]},{"r":"44","v":[{"o":"0/000.180.0711","d":"SPIKE REAR SHOCK ABSORBER","a":false,"c":""},{"o":"0/000.180.5000","d":"REAR SHOCK ABSORBER","a":false,"c":""}]},{"r":"45","v":[{"o":"0/000.360.0370","d":"SWING ARM BUSH","a":true,"c":""},{"o":"0/000.350.0202","d":"HANDLEBAR CLAMP COVER","a":false,"c":""}]},{"r":"46","v":[{"o":"0/000.640.0360","d":"LINKAGE SET RIGHT","a":true,"c":""},{"o":"0/000.480.1550","d":"SLEEVE","a":true,"c":""}]},{"r":"47","v":[{"o":"0/000.640.0361","d":"LINKAGE SET LEFT","a":true,"c":""},{"o":"0/000.460.0407","d":"SCREW M10x75","a":true,"c":""}]},{"r":"48","v":[{"o":"0/000.770.0103","d":"COMPLETE LINKAGE 2007","a":false,"c":""}]}],"pins":{"38":[[61.83,12.57]],"54":[[82.28,18.39]],"28":[[54.25,23.63]],"17":[[37.7,26.31]],"13":[[25.45,30.5],[45.9,35.16]],"33":[[63.47,30.85]],"12":[[23.65,33.76],[46.37,38.88]],"11":[[21.47,46.33]],"19":[[32.55,52.39]],"42":[[73.15,54.48],[77.75,56.11],[83.45,84.98]],"20":[[31.85,55.65]],"35":[[43.17,56.69]],"21":[[31.23,58.44]],"41":[[69.4,61.0],[71.74,82.54]],"27":[[51.6,61.35]],"10":[[17.95,63.1]],"23":[[30.29,64.03]],"36":[[46.21,63.8]],"24":[[29.59,66.36]],"40":[[40.12,71.71]],"51":[[72.76,71.59]],"44":[[81.03,77.65]],"39":[[39.03,79.39]],"47":[[89.85,81.72]],"37":[[20.3,84.75]],"48":[[72.91,91.73]],"49":[[75.96,92.67]],"1":[[14.99,88.71]],"2":[[13.51,85.33]],"3":[[13.04,79.05]],"4":[[22.48,80.68]],"5":[[15.46,76.37]],"6":[[38.8,88.01]],"7":[[21.78,75.79]],"8":[[16.55,71.01]],"9":[[22.87,69.85]],"14":[[45.75,42.03]],"15":[[34.58,41.33]],"16":[[38.33,23.4]],"18":[[37.0,29.34]],"22":[[30.05,61.23]],"25":[[28.73,68.92]],"26":[[56.52,73.92]],"29":[[55.66,37.95]],"30":[[52.62,56.23]],"31":[[62.53,36.32]],"32":[[57.06,32.13]],"34":[[57.38,27.12]],"43":[[83.92,60.54]],"45":[[83.61,79.05]],"46":[[85.95,80.21]],"50":[[77.91,94.06]],"52":[[55.58,17.11]]}},{"key":"wheels","title":"Wheels, brakes & chain","refs":[{"r":"0","v":[{"o":"0/000.110.0300","d":"FRONT WHEEL COMPLETE","a":false,"c":""},{"o":"0/000.120.0300","d":"REAR WHEEL COMPLETE","a":false,"c":""},{"o":"0/000.820.0560","d":"COMPLETE BRAKE SET","a":true,"c":""},{"o":"0/000.820.0550","d":"COMPLETE BRAKE SET","a":true,"c":""}]},{"r":"1","v":[{"o":"0/000.190.0710","d":"FRONT AXLE","a":true,"c":""}]},{"r":"2","v":[{"o":"0/000.440.0123B","d":"WASHER 15 (bag of 5)","a":true,"c":""}]},{"r":"3","v":[{"o":"0/001.010.0110","d":"SPIKE SPEEDO DRIVE","a":false,"c":""}]},{"r":"4","v":[{"o":"0/000.480.1500","d":"FRONT SPACER","a":false,"c":""}]},{"r":"5","v":[{"o":"0/000.550.0501","d":"SPEEDOMETER CABLE","a":false,"c":""},{"o":"0/000.550.0500","d":"SPEEDOMETER CABLE","a":false,"c":""}]},{"r":"6","v":[{"o":"0/000.510.0551","d":"SPIKE FRONT TYRE","a":false,"c":""},{"o":"0/000.510.0550","d":"FRONT TYRE 80/90-21 (enduro)","a":true,"c":""}]},{"r":"7","v":[{"o":"0/000.230.0520","d":"FRONT SPOKES","a":false,"c":""}]},{"r":"8","v":[{"o":"0/000.540.0550","d":"FRONT RIM","a":true,"c":""}]},{"r":"9","v":[{"o":"0/000.070.0092","d":"FRONT HUB","a":true,"c":""},{"o":"0/000.070.0090","d":"FRONT HUB ONLY","a":false,"c":""}]},{"r":"10","v":[{"o":"0/000.520.0540","d":"INNER TUBE 21in (enduro)","a":true,"c":""}]},{"r":"11","v":[{"o":"0/000.220.0090","d":"RIM TAPE","a":false,"c":""}]},{"r":"12","v":[{"o":"0/000.960.0050","d":"WHEEL BEARING 6202","a":true,"c":""}]},{"r":"13","v":[{"o":"0/000.480.0240","d":"FRONT WHEEL BEARING SPACER","a":true,"c":""}]},{"r":"14","v":[{"o":"0/000.360.0410","d":"RUBBER BEARING SPACER","a":false,"c":""}]},{"r":"15","v":[{"o":"0/000.480.1510","d":"LEFT FRONT WHEEL SPACER","a":true,"c":""}]},{"r":"16","v":[{"o":"0/000.340.0300B","d":"AXLE NUT (bag of 5)","a":true,"c":""}]},{"r":"17","v":[{"o":"0/000.930.0060","d":"BRAKE PADS","a":true,"c":""},{"o":"0/000.640.0461","d":"REAR BRAKE PADS (FRAME>552371)","a":false,"c":""},{"o":"0/000.640.0420","d":"REAR CALIPER REPAIR KIT","a":false,"c":""}]},{"r":"18","v":[{"o":"0/000.860.0050","d":"FLOATING FRONT CALIPER","a":false,"c":""},{"o":"0/000.860.0015","d":"FRONT CALIPER GOLD RR/SPIKE 2006","a":true,"c":""},{"o":"0/000.860.0012","d":"SINGLE REAR CALIPER","a":true,"c":""}]},{"r":"19","v":[{"o":"0/000.290.0247","d":"FRONT BRAKE HOSE","a":true,"c":""},{"o":"0/000.290.0255","d":"FRONT BRAKE HOSE (FRAME>552371)","a":true,"c":""}]},{"r":"20","v":[{"o":"0/000.460.1300B","d":"BANJO BOLT (bag of 5)","a":true,"c":""}]},{"r":"21","v":[{"o":"0/000.840.0090","d":"BRAKE PEDAL CLEVIS","a":true,"c":""}]},{"r":"22","v":[{"o":"0/000.840.0060","d":"SECURING PIN","a":true,"c":""}]},{"r":"23","v":[{"o":"0/000.130.0150","d":"BRAKE PEDAL FORK","a":false,"c":""}]},{"r":"24","v":[{"o":"0/000.340.0130B","d":"NUT M6 (bag of 5)","a":true,"c":""}]},{"r":"25","v":[{"o":"0/000.240.0270","d":"BRAKE ROD","a":false,"c":""}]},{"r":"26","v":[{"o":"0/000.820.0306","d":"REAR BRAKE MASTER CYLINDER","a":false,"c":""},{"o":"0/000.820.0314","d":"REAR BRAKE MASTER CYLINDER (FRAME>552371)","a":false,"c":""}]},{"r":"27","v":[{"o":"0/000.680.0500","d":"BRAKE HOSE CLAMP","a":true,"c":""}]},{"r":"28","v":[{"o":"0/000.290.0590","d":"REAR BRAKE HOSE","a":true,"c":""},{"o":"0/000.290.0248","d":"REAR BRAKE HOSE","a":false,"c":""},{"o":"0/000.290.0246","d":"REAR BRAKE HOSE","a":false,"c":""},{"o":"0/000.290.0256","d":"REAR BRAKE HOSE (FRAME>552371)","a":false,"c":""}]},{"r":"29","v":[{"o":"0/000.720.0073","d":"REAR BRAKE STOP SWITCH","a":true,"c":""}]},{"r":"30","v":[{"o":"0/000.030.0170","d":"BRAKE FLUID RESERVOIR","a":true,"c":""}]},{"r":"31","v":[{"o":"0/000.290.0243","d":"BRAKE FLUID TUBE","a":false,"c":""}]},{"r":"32","v":[{"o":"0/000.480.5012","d":"RIGHT REAR WHEEL SPACER","a":true,"c":""},{"o":"0/000.480.1521","d":"REAR WHEEL SPACER L/R","a":true,"c":""}]},{"r":"33","v":[{"o":"0/000.480.1541","d":"REAR SPACER 35mm","a":true,"c":""},{"o":"0/000.480.1540","d":"REAR WHEEL SPACER","a":false,"c":""}]},{"r":"34","v":[{"o":"0/001.090.0050","d":"FRONT BRAKE DISC 260","a":false,"c":""},{"o":"0/001.090.0051","d":"REAR BRAKE DISC 200","a":true,"c":""}]},{"r":"35","v":[{"o":"0/001.090.0500","d":"FRONT BRAKE DISC 260","a":false,"c":""},{"o":"0/001.090.0020","d":"REAR BEARING SPACER","a":true,"c":""}]},{"r":"36","v":[{"o":"0/000.480.0200","d":"SPROCKET CARRIER SPACER","a":false,"c":""}]},{"r":"37","v":[{"o":"0/000.480.0850","d":"SPROCKET CARRIER","a":false,"c":""}]},{"r":"38","v":[{"o":"0/000.500.5001","d":"REAR SPROCKET Z52","a":true,"c":""},{"o":"0/000.500.0531","d":"REAR SPROCKET Z52 (BAST.>89752)","a":false,"c":""},{"o":"0/000.500.0530","d":"REAR SPROCKET Z48 (BAST.<73599)","a":false,"c":""},{"o":"0/000.500.0540","d":"ALUMINIUM REAR SPROCKET Z48","a":false,"c":""}]},{"r":"39","v":[{"o":"0/000.510.0560","d":"REAR TYRE","a":true,"c":""}]},{"r":"40","v":[{"o":"0/000.230.0530","d":"REAR SPOKES","a":false,"c":""}]},{"r":"41","v":[{"o":"0/000.540.0560","d":"REAR RIM","a":true,"c":""}]},{"r":"42","v":[{"o":"0/000.080.0092","d":"REAR HUB","a":true,"c":""},{"o":"0/000.080.0093","d":"REAR HUB","a":false,"c":""}]},{"r":"43","v":[{"o":"0/000.520.0550","d":"REAR INNER TUBE","a":true,"c":""}]},{"r":"44","v":[{"o":"0/000.220.0090","d":"RIM TAPE","a":false,"c":""}]},{"r":"45","v":[{"o":"0/000.440.1040B","d":"DISC WASHER (bag of 5)","a":true,"c":""}]},{"r":"46","v":[{"o":"0/000.460.0600B","d":"SCREW M6x16","a":true,"c":""}]},{"r":"47","v":[{"o":"0/000.440.0130B","d":"WASHER 6.5x17 (bag of 5)","a":true,"c":""}]},{"r":"48","v":[{"o":"0/000.460.0471","d":"SCREW M8x45","a":false,"c":""}]},{"r":"49","v":[{"o":"0/000.460.0651","d":"SCREW M6x45","a":true,"c":""}]},{"r":"50","v":[{"o":"0/000.440.0210","d":"SPRING WASHER","a":true,"c":""}]},{"r":"51","v":[{"o":"0/000.460.0671","d":"SCREW M8x30","a":true,"c":""}]},{"r":"52","v":[{"o":"0/000.340.0180B","d":"NUT M8 (bag of 5)","a":true,"c":""}]},{"r":"53","v":[{"o":"0/000.460.0840B","d":"DISC SCREW M8x35 (bag of 5)","a":true,"c":""}]},{"r":"54","v":[{"o":"0/000.460.0842B","d":"DISC SCREW M8x45 (bag of 5)","a":true,"c":""},{"o":"0/000.460.0673","d":"SCREW M8x40","a":true,"c":""}]},{"r":"55","v":[{"o":"0/000.460.0480","d":"SCREW M8x50","a":true,"c":""}]},{"r":"56","v":[{"o":"0/000.460.0821B","d":"COUNTERSUNK SCREW M6x20 (bag of 5)","a":true,"c":""}]},{"r":"57","v":[{"o":"0/000.340.0170B","d":"NUT M6 (bag of 5)","a":true,"c":""}]},{"r":"58","v":[{"o":"0/001.050.0210","d":"CHAIN (BAST.>73600)","a":false,"c":""},{"o":"0/001.050.0530","d":"COMPLETE CHAIN","a":false,"c":""},{"o":"0/001.050.0520","d":"COMPLETE CHAIN","a":false,"c":""}]},{"r":"59","v":[{"o":"0/000.190.0700","d":"REAR AXLE","a":false,"c":""}]},{"r":"60","v":[{"o":"0/000.860.0060","d":"REAR BRAKE CALIPER","a":false,"c":""},{"o":"0/000.860.0013","d":"REAR BRAKE CALIPER","a":false,"c":""}]}],"pins":{"30":[[19.28,15.13]],"12":[[42.47,27.71],[41.53,91.62]],"27":[[16.94,32.01]],"14":[[45.04,39.0],[39.19,90.57]],"16":[[93.6,59.02],[56.05,94.76]],"19":[[48.87,59.6]],"41":[[59.33,59.95]],"10":[[38.72,67.29]],"45":[[49.8,70.78]],"18":[[42.86,74.16]],"13":[[39.66,80.09]],"61":[[55.43,77.76]],"15":[[49.8,94.76]],"1":[[5.62,74.97]],"2":[[15.14,69.85]],"3":[[14.83,80.56]],"4":[[18.81,80.56]],"5":[[18.58,56.58]],"6":[[30.68,57.28]],"7":[[35.68,61.23]],"8":[[30.6,99.3]],"9":[[34.58,95.93]],"11":[[38.17,75.44]],"17":[[59.56,82.07]],"20":[[49.73,68.45]],"21":[[6.25,45.75]],"22":[[12.8,47.61]],"23":[[8.9,49.01]],"24":[[7.73,40.86]],"25":[[13.19,39.58]],"26":[[8.9,31.66]],"28":[[22.17,31.43]],"29":[[11.16,20.61]],"31":[[17.17,24.45]],"32":[[29.04,32.13]],"33":[[31.38,33.88]],"34":[[36.07,39.35]],"35":[[44.5,97.79]],"36":[[46.21,29.92]],"37":[[72.6,36.9]],"38":[[79.63,35.16]],"39":[[58.16,15.25]],"40":[[64.01,16.88]],"42":[[64.25,56.81]],"43":[[68.07,23.98]],"44":[[69.4,30.03]],"46":[[9.52,15.37]],"47":[[12.65,12.57]],"48":[[31.3,17.69]],"49":[[35.75,24.68]],"50":[[34.04,22.12]],"51":[[49.96,79.05]],"52":[[40.44,38.53]],"53":[[33.88,35.51]],"54":[[81.73,56.0]],"55":[[51.21,76.6]],"56":[[53.08,87.31]],"57":[[42.47,81.26]],"58":[[76.35,62.98]],"59":[[84.23,45.05]],"60":[[43.64,21.42]]}}];
const IMAGES = {"chassis":"img/chassis.webp","suspension":"img/suspension.webp","wheels":"img/wheels.webp","plastics":"img/plastics.webp","electrics":"img/electrics.webp","exhaust-radiator-tank-airfilter":"img/exhaust-radiator-tank-airfilter.webp","motord":"img/motord.webp"};
const NOTES = {
chassis:"Factory catalogue sheet 1 (titled Spike 2000; the same sheet covers 2002–2005 Spikes).",
suspension:"Heads-up: around the rear shock the drawing and the parts list number things differently. The drawing calls the shock 51 and the linkage 44–50; the parts list puts the shock under 44. Read the description before ordering.",
wheels:"Some tyre and tube lines in this list are the enduro RR sizes. Your papers say 100/80-17 front and 130/70-17 rear.",
motord:"This is the engine sheet that matches your bike (PHBN 16 carb, fitted from frame 73600 on). Rieju lists four earlier AM6 versions separately.",
plastics:"Colour variant C in the catalogue is the 2005 blue. Parts tagged “your colour” are the ones to look for.",
};
const FRAME = 550038;
const SITE = "https://www.oemmotorparts.com/en/model/rieju/spike-50/2002/drawing/";
const STATUS = {have:"Have",miss:"Missing",ord:"Ordered",skip:"Not needed"};
// ---------- fitment helpers ----------
function fits(desc){
const m = desc.match(/(BAST\.?|FRAME|Chassis|SPIKE)\s*(<=|>=|<|>)\s*(\d+)/i);
if(!m) return null;
const n=+m[3], op=m[2];
return op==="<="?FRAME<=n: op===">="?FRAME>=n: op==="<"?FRAME<n: FRAME>n;
}
function variantTags(v){
const t=[]; const f=fits(v.d);
if(f===true) t.push(["good","Fits frame 550038"]);
if(f===false) t.push(["bad","Not for your frame"]);
if(v.c==="C") t.push(["mine","Your colour (blue 2005)"]);
else if(v.c && v.c!=="") t.push(["", "Colour option "+v.c]);
t.push(v.a?["good","Orderable new"]:["","Discontinued / unavailable"]);
return t;
}
function bestName(ref){
const vs=ref.v;
const pick = vs.find(v=>v.c==="C") || vs.find(v=>fits(v.d)===true) || vs.find(v=>fits(v.d)!==false && !/DISCONTINUED/.test(v.d)) || vs[0];
return pick.d.replace(/\s*\((BAST|FRAME|Chassis|SPIKE)[^)]*\)/i,"").toLowerCase().replace(/^\w|\s\w/g,s=>s.toUpperCase()).replace(/\bL\/h\b/g,"L/H").replace(/\bR\/h\b/g,"R/H");
}
// ---------- state ----------
const EMPTY=()=>({parts:{}, links:{}, comments:{}, drawings:{}, photos:{}});
let state = EMPTY();
let current=GROUPS[0].key, selRef=null, filter="all", query="", zoom=1, editing=false, confirmBulk=false, online=false;
const pid=(g,r)=>g+"__"+r;
const LS="spike50-board-v1";
function statusOf(g,r){return (state.parts[pid(g,r)]||{}).s||"none"}
async function api(method,url,body,isRaw){
const opt={method,headers:{}};
if(body!==undefined){ if(isRaw){ opt.body=body; opt.headers["Content-Type"]=body.type; } else { opt.body=JSON.stringify(body); opt.headers["Content-Type"]="application/json"; } }
let res;
try{ res=await fetch(url,opt); }catch(_){ const er=new Error("You’re offline"); er.offline=true; throw er; }
if(res.status===401){ location.href="/login"; throw new Error("Please sign in again"); }
const data=await res.json().catch(()=>({}));
if(!res.ok) throw new Error(data.error||("Server answered "+res.status));
return data;
}
// ---------- offline storage + change queue (IndexedDB) ----------
const idb=new Promise((ok,no)=>{ setTimeout(()=>no(new Error("storage timeout")),3000); try{ const r=indexedDB.open("spike-board",1); r.onupgradeneeded=()=>{ r.result.createObjectStore("q",{autoIncrement:true}); r.result.createObjectStore("kv"); }; r.onsuccess=()=>ok(r.result); r.onerror=()=>no(r.error); }catch(e){ no(e); } });
const idbDo=(store,mode,fn)=>idb.then(db=>new Promise((ok,no)=>{ const t=db.transaction(store,mode); const out=fn(t.objectStore(store)); t.oncomplete=()=>ok(out&&"result" in out?out.result:undefined); t.onerror=()=>no(t.error); }));
const kvGet=k=>idbDo("kv","readonly",s=>s.get(k));
const kvSet=(k,v)=>idbDo("kv","readwrite",s=>s.put(v,k));
const qAdd=item=>idbDo("q","readwrite",s=>s.add(item));
const qDel=k=>idbDo("q","readwrite",s=>s.delete(k));
const qAll=()=>idb.then(db=>new Promise((ok,no)=>{ const t=db.transaction("q"); const s=t.objectStore("q"); const ks=s.getAllKeys(), vs=s.getAll(); t.oncomplete=()=>ok(ks.result.map((k,i)=>Object.assign({k},vs.result[i]))); t.onerror=()=>no(t.error); }));
let cacheTimer=null;
function saveCache(){ clearTimeout(cacheTimer); cacheTimer=setTimeout(()=>{ kvSet("state",JSON.parse(JSON.stringify(state))).catch(()=>{}); },150); }
let pendingN=0, tmpId=-1;
const stamp=()=>new Date().toLocaleTimeString([], {hour:"2-digit",minute:"2-digit"});
function badgeNow(){ setBadge(online ? (pendingN?`Syncing ${pendingN} saved change${pendingN>1?"s":""}…`:"Saved on your server · "+stamp()) : (pendingN?`Offline · ${pendingN} change${pendingN>1?"s":""} saved on this phone, will sync later`:"Offline · showing the copy saved on this device")); }
// send a change; if there's no connection keep it on the device and apply it locally
async function send(method,url,body,apply){
try{ return await api(method,url,body); }
catch(e){
if(!e.offline) throw e;
try{ await qAdd({method,url,body}); }catch(_){ throw e; }
online=false; pendingN++; if(apply) apply(); saveCache(); badgeNow(); render();
return {queued:true};
}
}
let flushing=false;
async function flushQueue(){
if(flushing) return pendingN; flushing=true;
try{
for(const it of await qAll()){
try{ await api(it.method,it.url,it.body); await qDel(it.k); }
catch(e){ if(e.offline) break; await qDel(it.k); toast("A change saved offline was rejected: "+e.message); }
}
pendingN=(await qAll()).length;
}catch(_){ pendingN=0; }
finally{ flushing=false; }
return pendingN;
}
function setBadge(t){ $("#savebadge").textContent=t; }
async function reload(){
await flushQueue();
if(pendingN){ online=false; badgeNow(); render(); return; }
try{ const s=await api("GET","api/state"); state=Object.assign(EMPTY(),s); online=true; saveCache(); badgeNow(); }
catch(e){ online=false; badgeNow(); }
render();
}
const writing={};
async function savePart(g,r,patch){
const id=pid(g,r); const cur=Object.assign({}, state.parts[id]||{}, patch);
state.parts[id]=cur; if(patch.s!==undefined) render();
saveCache();
try{ const r=await (writing[id]=(writing[id]||Promise.resolve()).catch(()=>{}).then(()=>send("PUT","api/parts/"+id,{s:cur.s||"none",note:cur.note||""}))); if(!r.queued){ online=true; badgeNow(); } }
catch(e){ toast("Couldn’t save: "+e.message); setBadge("Last change not saved. Check the server."); }
}
let pinTimer=null;
function savePins(g){
clearTimeout(pinTimer);
pinTimer=setTimeout(async()=>{
try{ saveCache(); await send("PUT","api/drawings/"+g+"/pins",{pins:(state.drawings[g]||{}).pins||null}); }catch(e){ toast("Couldn’t save marker positions: "+e.message); }
},400);
}
function pinsFor(g){ const d=state.drawings[g]; if(d&&d.pins) return d.pins; const G=GROUPS.find(x=>x.key===g); return G.pins||{}; }
function imageFor(g){ const d=state.drawings[g]; if(d&&d.image) return d.image; return IMAGES[g]||null; }
const linksOf=(g,r)=>state.links[pid(g,r)]||[];
const commentsOf=(g,r)=>state.comments[pid(g,r)]||[];
function when(ms){ return new Date(ms).toLocaleString([], {day:"2-digit",month:"2-digit",year:"numeric",hour:"2-digit",minute:"2-digit"}); }
function host(u){ try{ return new URL(u).hostname.replace(/^www\./,""); }catch(_){ return u; } }
// ---------- render ----------
const $=s=>document.querySelector(s);
function counts(keys){
const c={have:0,miss:0,ord:0,skip:0,none:0,total:0};
for(const G of GROUPS){ if(keys && !keys.includes(G.key)) continue; for(const r of G.refs){ c[statusOf(G.key,r.r)]++; c.total++; } }
return c;
}
function renderHead(){
const c=counts();
$("#tally").innerHTML = ["have","miss","ord","skip","none"].map(s=>`<span class="chip s-${s}"><span class="dot"></span>${s==="none"?"Unchecked":STATUS[s]} <b class="mono">${c[s]}</b></span>`).join("");
$("#bar").innerHTML = ["have","skip","ord","miss"].map(s=>`<i style="width:${c[s]/c.total*100}%;background:var(--${s})"></i>`).join("");
}
function renderTabs(){
const shopN=counts().miss+counts().ord;
$("#tabs").innerHTML = GROUPS.map(G=>{
const c=counts([G.key]); const done=c.have+c.skip;
return `<button class="tab" role="tab" aria-selected="${current===G.key}" data-g="${G.key}">
<span class="t">${G.title}</span>
<span class="bar">${["have","skip","ord","miss"].map(s=>`<i style="width:${c[s]/c.total*100}%;background:var(--${s})"></i>`).join("")}</span>
<span class="m"><span>${done}/${c.total} done</span><span>${c.miss?`<span style="color:var(--miss)">${c.miss} missing</span>`:(imageFor(G.key)?"drawing":"no drawing yet")}</span></span></button>`;
}).join("") + `<button class="tab photos" role="tab" aria-selected="${current==="gallery"}" data-g="gallery"><span class="t">Photos</span><span class="m"><span>${Object.values(state.photos||{}).reduce((n,l)=>n+l.length,0)} photos</span><span></span></span></button>`
+ `<button class="tab shop" role="tab" aria-selected="${current==="shop"}" data-g="shop"><span class="t">Shopping list</span><span class="m"><span>${shopN} parts to get</span><span></span></span></button>`;
}
function visibleRefs(G){
return G.refs.filter(r=>{
const s=statusOf(G.key,r.r);
if(filter!=="all" && s!==filter) return false;
if(query){ const q=query.toLowerCase(); if(!(r.r===q || r.v.some(v=>v.o.toLowerCase().includes(q)||v.d.toLowerCase().includes(q)))) return false; }
return true;
});
}
function renderMain(){
if(current==="shop") return renderShop();
if(current==="gallery") return renderGallery();
const G=GROUPS.find(x=>x.key===current);
const img=imageFor(G.key); const pins=pinsFor(G.key);
const refset=new Set(G.refs.map(r=>r.r));
let drawing;
if(img){
const pinHtml=Object.entries(pins).flatMap(([r,list])=>list.map((p,i)=>{
const orphan=!refset.has(r); const s=orphan?"none":statusOf(G.key,r);
return `<button class="pin s-${s}${selRef===r?" sel":""}${orphan?" orphan":""}" style="left:${p[0]}%;top:${p[1]}%" data-r="${r}" data-i="${i}" aria-label="Part ${r}${orphan?" (not in parts list)":", "+(STATUS[s]||"unchecked")}"></button>`;
})).join("");
drawing = `<div class="viewport" id="vp"><div class="stage${editing?" editing":""}" id="stage" style="width:${zoom*100}%"><img src="${img}" alt="Exploded drawing: ${G.title}" draggable="false">${pinHtml}</div></div>`;
} else {
drawing = `<div class="nodraw"><h3>No drawing for this group yet</h3>
<p>Open the factory drawing for this group, save it as an image, then add it here. You can then click each number on the drawing to place a marker.</p>
<div class="tools"><a class="btn" href="${SITE+G.key}" target="_blank" rel="noopener">Open drawing on OEM Motorparts</a>
${online?`<label class="btn primary" for="upl">Upload drawing</label><input type="file" id="upl" accept="image/png,image/jpeg,image/webp,image/gif" hidden>`:""}</div></div>`;
}
const c=counts([G.key]);
const rows=visibleRefs(G).map(r=>{
const s=statusOf(G.key,r.r); const note=(state.parts[pid(G.key,r.r)]||{}).note;
const onDraw = pins[r.r]?"":" · not on drawing";
const nl=linksOf(G.key,r.r).length, nc=commentsOf(G.key,r.r).length;
const extra=(nl?` · ${nl} link${nl>1?"s":""}`:"")+(nc?` · ${nc} comment${nc>1?"s":""}`:"");
let html=`<button class="row s-${s}${selRef===r.r?" sel":""}" data-r="${r.r}"><span class="ref">${r.r}</span>
<span style="min-width:0"><span class="rname">${esc(bestName(r))}</span><br><span class="rsub mono">${r.v.length>1?r.v.length+" variants":esc(r.v[0].o)}${img?onDraw:""}${extra}${note?" · "+esc(note):""}</span></span>
<span class="stat"><span class="dot s-${s}" style="display:inline-block;vertical-align:middle"></span> ${s==="none"?"":STATUS[s]}</span></button>`;
if(selRef===r.r) html+=detailHtml(G,r);
return html;
}).join("") || `<div class="empty">No parts match this filter.</div>`;
const orphanSel = selRef && !refset.has(selRef) ? `<div class="detail"><b>Number ${esc(selRef)} on the drawing</b><span class="rsub">This number isn’t in the 2002–2005 parts list. It may be a kit number or a part that only existed on the 2000 model. Check it on the website before ordering.</span>${editing?`<button class="btn" data-act="delpin">Remove this marker</button>`:""}</div>`:"";
$("#main").innerHTML = `<div class="main">
<section class="card">
<div class="dhead"><h2>${G.title}</h2>
<div class="tools">${img?`<button class="btn primary" data-act="fs">⤢ Full screen</button><button class="btn" data-z="-1" aria-label="Zoom out">−</button><span class="mono" style="min-width:3.5em;text-align:center">${Math.round(zoom*100)}%</span><button class="btn" data-z="1" aria-label="Zoom in">+</button>
<button class="btn${editing?" on":""}" data-act="edit">${editing?"Done moving markers":"Move markers"}</button>
${online?`<label class="btn" for="upl">Replace drawing</label><input type="file" id="upl" accept="image/png,image/jpeg,image/webp,image/gif" hidden>`:""}`:""}
<a class="btn" href="${SITE+G.key}" target="_blank" rel="noopener">Source</a></div></div>
${drawing}
${editing?`<div class="note"><b>Marker mode:</b> drag a marker to move it. Select a part in the list, then click the drawing to add a marker for it.</div>`:""}
${NOTES[G.key]?`<div class="note">${NOTES[G.key]}</div>`:""}
</section>
<section class="card">
<div class="lhead">
<div style="display:flex;justify-content:space-between;align-items:center;gap:8px;flex-wrap:wrap"><span class="lbl">${c.total} numbered parts</span>
${c.none? (confirmBulk?`<span class="confirm">Mark ${c.none} unchecked parts as Have? <button class="btn primary" data-act="bulkyes">Yes</button><button class="btn" data-act="bulkno">Cancel</button></span>`:`<button class="btn" data-act="bulk">Mark the rest as Have</button>`):""}</div>
<input type="search" id="q" placeholder="Search number, part name or OEM no." value="${esc(query)}">
<div class="filters">${[["all","All"],["none","Unchecked"],["miss","Missing"],["ord","Ordered"],["have","Have"],["skip","Not needed"]].map(([k,l])=>`<button class="btn${filter===k?" on":""}" data-f="${k}">${l}</button>`).join("")}</div>
</div>
${orphanSel}
<div class="list" id="list">${rows}</div>
</section></div>`;
if(selRef){ const el=document.querySelector(`.row.sel`); if(el && el.scrollIntoViewIfNeeded) el.scrollIntoViewIfNeeded(); }
}
function detailHtml(G,r){
const p=state.parts[pid(G.key,r.r)]||{}; const s=p.s||"none";
return `<div class="detail" data-detail="${r.r}">
<div class="sbtns">${Object.entries(STATUS).map(([k,l])=>`<button data-s="${k}" aria-pressed="${s===k}">${l}</button>`).join("")}</div>
<div class="vars"><span class="lbl">Catalogue entries for no. ${r.r}</span>${r.v.map(v=>{const f=fits(v.d); return `<div class="var${f===false||(v.c&&v.c!=="C"&&G.key==="plastics")?" no":""}">
<a class="o" href="https://www.oemmotorparts.com/en/search?filter=${encodeURIComponent(v.o)}" target="_blank" rel="noopener">${esc(v.o)}</a><a class="o" style="font-size:12px;opacity:.8" title="Search Google for this number" href="https://www.google.com/search?q=${encodeURIComponent('"'+v.o+'" Rieju Spike 50')}" target="_blank" rel="noopener">Google</a><span class="d">${esc(v.d)}</span>
<span class="tags">${variantTags(v).map(([c,t])=>`<span class="tag ${c}">${t}</span>`).join("")}</span></div>`}).join("")}</div>
<label class="lbl" for="note">Note</label>
<textarea id="note" rows="2" placeholder="Where it is, condition, where you ordered it…">${esc(p.note||"")}</textarea>
<div class="sub"><span class="lbl">Links</span>
${linksOf(G.key,r.r).map(l=>`<div class="item"><span><a href="${esc(l.url)}" target="_blank" rel="noopener noreferrer">${esc(l.label||host(l.url))}</a><br><span class="when">${esc(host(l.url))} · added ${when(l.at)}</span></span>${l.id>0?`<button class="x" data-dellink="${l.id}" aria-label="Remove link">×</button>`:`<span class="when">waiting</span>`}</div>`).join("")}
<form class="addrow" data-form="link"><input type="url" id="lurl" placeholder="https://… (eBay listing, shop, forum post)" required><input type="text" id="llabel" placeholder="Label (optional)"><button class="btn primary" type="submit">Add link</button></form>
</div>
<div class="sub"><span class="lbl">Comments</span>
${commentsOf(G.key,r.r).map(c=>`<div class="item"><span><p>${esc(c.text)}</p><span class="when">${when(c.at)}</span></span>${c.id>0?`<button class="x" data-delcomment="${c.id}" aria-label="Delete comment">×</button>`:`<span class="when">waiting</span>`}</div>`).join("")}
<form class="sub" data-form="comment"><textarea id="ctext" rows="2" placeholder="Write a comment, e.g. “Ordered from Motostion, 12 € + shipping”" required></textarea><div><button class="btn primary" type="submit">Add comment</button></div></form>
</div>
<div class="sub"><span class="lbl">Photos</span>
<div class="pgrid">${photoItems(pid(G.key,r.r)).map((p,i)=>`<button class="ph" data-ph="${pid(G.key,r.r)}|${i}" aria-label="Open photo"><img loading="lazy" src="${p.thumb}" alt="">${p.pending?`<i class="pend">waiting</i>`:""}</button>`).join("")}</div>
<div><label class="btn" for="pupl">+ Add photo</label><input type="file" id="pupl" accept="image/*" multiple hidden></div>
</div>
${editing&&pinsFor(G.key)[r.r]?`<button class="btn" data-act="delpin">Remove this part’s markers</button>`:""}
</div>`;
}
function renderShop(){
const sections=GROUPS.map(G=>{
const items=G.refs.filter(r=>["miss","ord"].includes(statusOf(G.key,r.r)));
if(!items.length) return "";
return `<div><h3 style="font-size:16px;margin-bottom:6px">${G.title}</h3><div class="tblwrap"><table><thead><tr><th>No.</th><th>Part</th><th>OEM number(s)</th><th>Status</th><th>Note &amp; links</th></tr></thead><tbody>${items.map(r=>{
const p=state.parts[pid(G.key,r.r)]||{};
const good=r.v.filter(v=>fits(v.d)!==false && (G.key!=="plastics"||!v.c||v.c==="C"));
return `<tr><td class="mono">${r.r}</td><td>${esc(bestName(r))}</td><td class="mono">${(good.length?good:r.v).map(v=>`<a href="https://www.oemmotorparts.com/en/search?filter=${encodeURIComponent(v.o)}" target="_blank" rel="noopener">${esc(v.o)}</a>${v.a?"":" <span class=\"tag\">unavailable</span>"}`).join("<br>")}</td><td><span class="chip s-${p.s}"><span class="dot"></span>${STATUS[p.s]}</span></td><td>${esc(p.note||"")}${linksOf(G.key,r.r).map(l=>`<br><a href="${esc(l.url)}" target="_blank" rel="noopener noreferrer">${esc(l.label||host(l.url))}</a>`).join("")}</td></tr>`}).join("")}</tbody></table></div></div>`;
}).join("");
$("#main").innerHTML=`<section class="card shop"><div style="display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap;align-items:center"><h2 style="font-size:20px">Shopping list</h2><button class="btn primary" data-act="copy">Copy list</button></div>
<p class="rsub" style="margin:0">Everything marked Missing or Ordered. Variants that don’t fit frame 550038 or aren’t your colour are left out. Unavailable numbers are worth searching on eBay and at breakers.</p>
${sections||`<div class="empty">Nothing marked Missing yet. Open a drawing group and mark the parts you don’t have.</div>`}</section>`;
}
function render(){ renderHead(); renderTabs(); renderMain(); }
function esc(s){return String(s).replace(/[&<>"]/g,c=>({"&":"&amp;","<":"&lt;",">":"&gt;","\"":"&quot;"}[c]))}
function toast(t){const el=$("#toast"); el.textContent=t; el.hidden=false; clearTimeout(toast.t); toast.t=setTimeout(()=>el.hidden=true,2600);}
// ---------- events ----------
document.addEventListener("click",async e=>{
if(e.target.closest("#stage") && !editing && !e.target.closest(".pin")){ openDrawing(current); return; }
const t=e.target.closest("button,[data-g]"); if(!t) return;
if(t.dataset.ph){ const [sc,i]=t.dataset.ph.split("|"); openPhotos(photoItems(sc),+i); return; }
if(t.dataset.g){ current=t.dataset.g; selRef=null; editing=false; confirmBulk=false; zoom=1; render(); try{localStorage.setItem(LS+"-tab",current)}catch(_){} return; }
if(t.classList.contains("pin")){ if(dragMoved) return; selRef=t.dataset.r; filter="all"; render(); return; }
if(t.classList.contains("row")){ selRef = selRef===t.dataset.r?null:t.dataset.r; render(); return; }
if(t.dataset.s && t.closest(".detail")){ const cur=statusOf(current,selRef); savePart(current,selRef,{s: cur===t.dataset.s?"none":t.dataset.s}); return; }
if(t.dataset.f){ filter=t.dataset.f; render(); return; }
if(t.dataset.z){ zoom=Math.min(3,Math.max(1,zoom+ (+t.dataset.z)*0.5)); render(); return; }
if(t.dataset.dellink){ const id=+t.dataset.dellink; try{ const r=await send("DELETE","api/links/"+id,undefined,()=>{ for(const k in state.links) state.links[k]=state.links[k].filter(l=>l.id!==id); }); if(!r.queued) await reload(); }catch(err){ toast("Couldn’t remove: "+err.message); } return; }
if(t.dataset.delcomment){ const id=+t.dataset.delcomment; try{ const r=await send("DELETE","api/comments/"+id,undefined,()=>{ for(const k in state.comments) state.comments[k]=state.comments[k].filter(c=>c.id!==id); }); if(!r.queued) await reload(); }catch(err){ toast("Couldn’t delete: "+err.message); } return; }
const a=t.dataset.act;
if(a==="fs"){ openDrawing(current); return; }
if(a==="edit"){ editing=!editing; render(); }
if(a==="bulk"){ confirmBulk=true; render(); }
if(a==="bulkno"){ confirmBulk=false; render(); }
if(a==="bulkyes"){ confirmBulk=false; const G=GROUPS.find(x=>x.key===current); for(const r of G.refs){ if(statusOf(G.key,r.r)==="none") await savePart(G.key,r.r,{s:"have"}); } toast("Marked as Have"); }
if(a==="delpin"){ const p=Object.assign({},pinsFor(current)); delete p[selRef]; state.drawings[current]=Object.assign({},state.drawings[current],{pins:p}); savePins(current); render(); }
if(a==="copy"){ const txt=GROUPS.flatMap(G=>G.refs.filter(r=>["miss","ord"].includes(statusOf(G.key,r.r))).map(r=>{const good=r.v.filter(v=>fits(v.d)!==false&&(G.key!=="plastics"||!v.c||v.c==="C"));return `${G.title} #${r.r}\t${bestName(r)}\t${(good.length?good:r.v).map(v=>v.o).join(" / ")}\t${STATUS[statusOf(G.key,r.r)]}`;})).join("\n");
try{ await navigator.clipboard.writeText(txt); toast("Copied"); }catch(_){ toast("Copy didn’t work here. Select the table and copy it instead."); } }
});
document.addEventListener("input",e=>{
if(e.target.id==="q"){ query=e.target.value.trim(); const pos=e.target.selectionStart; renderMain(); const q=$("#q"); q.focus(); q.setSelectionRange(pos,pos); }
});
document.addEventListener("change",async e=>{
if(e.target.id==="note"){ savePart(current,selRef,{note:e.target.value}); }
if((e.target.id==="pupl"||e.target.id==="gupl") && e.target.files.length){
const files=[...e.target.files], scope=e.target.id==="gupl"?"gallery":pid(current,selRef), cap=e.target.id==="gupl"?($("#gcap")||{}).value:"";
e.target.value=""; await addPhotos(files,scope,cap);
}
if(e.target.id==="upl" && e.target.files[0]){
toast("Uploading…");
try{ await api("POST","api/drawings/"+current+"/image",e.target.files[0],true); await reload(); editing=true; render(); toast("Drawing added. Select a part, then click its number on the drawing."); }
catch(err){ toast("Upload failed: "+err.message); }
}
});
document.addEventListener("submit",async e=>{
const f=e.target.closest("form[data-form]"); if(!f) return; e.preventDefault();
const id=pid(current,selRef);
try{
let r;
if(f.dataset.form==="link"){ const b={url:$("#lurl").value,label:$("#llabel").value};
r=await send("POST","api/parts/"+id+"/links",b,()=>{ (state.links[id]=state.links[id]||[]).push({id:tmpId--,url:b.url,label:b.label,at:Date.now()}); }); toast(r.queued?"Link saved offline":"Link added"); }
else { const b={text:$("#ctext").value};
r=await send("POST","api/parts/"+id+"/comments",b,()=>{ (state.comments[id]=state.comments[id]||[]).push({id:tmpId--,text:b.text,at:Date.now()}); }); toast(r.queued?"Comment saved offline":"Comment added"); }
if(!r.queued) await reload();
}catch(err){ toast(err.message); }
});
// marker drag + place
let drag=null, dragMoved=false;
document.addEventListener("pointerdown",e=>{
if(!editing) return; const pin=e.target.closest(".pin"); const stage=e.target.closest("#stage"); if(!stage) return;
const rect=stage.getBoundingClientRect();
if(pin){ drag={pin,r:pin.dataset.r,i:+pin.dataset.i,rect}; dragMoved=false; pin.setPointerCapture(e.pointerId); e.preventDefault(); return; }
if(selRef){ const x=(e.clientX-rect.left)/rect.width*100, y=(e.clientY-rect.top)/rect.height*100;
const p=JSON.parse(JSON.stringify(pinsFor(current))); (p[selRef]=p[selRef]||[]).push([+x.toFixed(2),+y.toFixed(2)]);
state.drawings[current]=Object.assign({},state.drawings[current],{pins:p}); savePins(current); render(); }
else toast("Select a part in the list first.");
});
document.addEventListener("pointermove",e=>{ if(!drag) return; dragMoved=true;
const x=Math.min(100,Math.max(0,(e.clientX-drag.rect.left)/drag.rect.width*100)), y=Math.min(100,Math.max(0,(e.clientY-drag.rect.top)/drag.rect.height*100));
drag.pin.style.left=x+"%"; drag.pin.style.top=y+"%"; drag.x=x; drag.y=y; });
document.addEventListener("pointerup",()=>{ if(!drag) return;
if(dragMoved && drag.x!=null){ const p=JSON.parse(JSON.stringify(pinsFor(current))); if(p[drag.r]&&p[drag.r][drag.i]) p[drag.r][drag.i]=[+drag.x.toFixed(2),+drag.y.toFixed(2)];
state.drawings[current]=Object.assign({},state.drawings[current],{pins:p}); savePins(current); }
else { selRef=drag.r; render(); }
drag=null; setTimeout(()=>dragMoved=false,0); });
// ---------- photos ----------
function partLabel(k){ const [g,r]=k.split("__"); const G=GROUPS.find(x=>x.key===g); return G?`${G.title} #${r}`:k; }
function photoItems(scope){
const mk=(k,p)=>Object.assign({},p,{scope:k,label:k==="gallery"?"":partLabel(k)});
if(scope==="parts") return Object.entries(state.photos||{}).filter(([k])=>k!=="gallery").flatMap(([k,l])=>l.map(p=>mk(k,p)));
return ((state.photos||{})[scope]||[]).map(p=>mk(scope,p));
}
async function prepPhoto(file){
const bmp=await createImageBitmap(file,{imageOrientation:"from-image"});
const mk=(max,q)=>{ const k=Math.min(1,max/Math.max(bmp.width,bmp.height)); const c=document.createElement("canvas"); c.width=Math.round(bmp.width*k); c.height=Math.round(bmp.height*k); c.getContext("2d").drawImage(bmp,0,0,c.width,c.height); return c.toDataURL("image/jpeg",q); };
const out={image:mk(2000,.85),thumb:mk(400,.75)}; if(bmp.close) bmp.close(); return out;
}
async function addPhotos(files,scope,caption){
let n=0, queued=false;
toast(`Adding ${files.length} photo${files.length>1?"s":""}…`);
for(const f of files){
try{
const d=await prepPhoto(f); const body={scope,caption:caption||"",image:d.image,thumb:d.thumb};
const r=await send("POST","api/photos",body,()=>{ (state.photos[scope]=state.photos[scope]||[]).unshift({id:tmpId--,caption:body.caption,at:Date.now(),src:d.image,thumb:d.thumb,pending:true}); });
if(r.queued) queued=true; n++;
}catch(e){ toast("Couldn’t add “"+f.name+"”: "+e.message); }
}
if(!queued) await reload(); else render();
if(n) toast(queued?`${n} photo${n>1?"s":""} saved on this phone, will upload when online`:`${n} photo${n>1?"s":""} added`);
}
let gq="";
function renderGallery(){
const all=photoItems("gallery"); const list=gq?all.filter(p=>p.caption.toLowerCase().includes(gq)):all;
const parts=photoItems("parts");
const tile=(p,key,i)=>`<button class="ph" data-ph="${key}|${i}" aria-label="Open photo"><img loading="lazy" src="${p.thumb}" alt="${esc(p.caption)}">${p.pending?`<i class="pend">waiting</i>`:""}${(p.label||p.caption)?`<span>${esc(p.label?p.label+(p.caption?" · "+p.caption:""):p.caption)}</span>`:""}</button>`;
$("#main").innerHTML=`<section class="card shop">
<div><h2 style="font-size:20px">Photos &amp; disassembly</h2><p class="rsub" style="margin:4px 0 0">Shared photos of the bike: what you took apart, condition, how things fit. Tap a photo to open and zoom. Photos of single parts are added inside the part (open a part, then “Add photo”).</p></div>
<div class="gup"><input type="text" id="gcap" placeholder="Caption for the new photos (optional), e.g. “Engine out, left side”"><label class="btn primary" for="gupl">+ Add photos</label><input type="file" id="gupl" accept="image/*" multiple hidden></div>
${all.length>6?`<input type="search" id="gq" placeholder="Search captions" value="${esc(gq)}">`:""}
${list.length?`<div class="pgrid big">${list.map((p,i)=>tile(p,"gallery",all.indexOf(p))).join("")}</div>`:`<div class="empty">${all.length?"No photos match.":"No photos yet. Add the first one."}</div>`}
${parts.length?`<div><h3 style="font-size:16px;margin-bottom:8px">Photos attached to parts</h3><div class="pgrid big">${parts.map((p,i)=>tile(p,"parts",i)).join("")}</div></div>`:""}
</section>`;
}
document.addEventListener("input",e=>{ if(e.target.id==="gq"){ gq=e.target.value.trim().toLowerCase(); const pos=e.target.selectionStart; renderGallery(); const q=$("#gq"); q.focus(); q.setSelectionRange(pos,pos); } });
// ---------- full-screen viewer (photos + drawings): pinch, drag, wheel, double-tap ----------
const vw={open:false,s:1,tx:0,ty:0,nw:0,nh:0,fw:0,fh:0,cw:0,ch:0,mode:"",list:[],i:0};
const vEl={};
const clamp=(v,a,b)=>Math.min(b,Math.max(a,v));
function vwClamp(){ const w=vw.fw*vw.s,h=vw.fh*vw.s;
vw.tx=w<=vw.cw?(vw.cw-w)/2:clamp(vw.tx,vw.cw-w,0); vw.ty=h<=vw.ch?(vw.ch-h)/2:clamp(vw.ty,vw.ch-h,0); }
function vwApply(){ vwClamp(); vEl.st.style.transform=`translate(${vw.tx}px,${vw.ty}px) scale(${vw.s})`; vEl.st.style.setProperty("--k",1/vw.s); }
function vwLayout(){ vw.cw=vEl.cv.clientWidth; vw.ch=vEl.cv.clientHeight; if(!vw.nw||!vw.cw) return;
const base=Math.min(vw.cw/vw.nw,vw.ch/vw.nh); vw.fw=vw.nw*base; vw.fh=vw.nh*base;
vEl.st.style.width=vw.fw+"px"; vEl.st.style.height=vw.fh+"px"; vwApply(); }
function vwZoomAt(ns,cx,cy){ ns=clamp(ns,1,8); const px=(cx-vw.tx)/vw.s, py=(cy-vw.ty)/vw.s; vw.s=ns; vw.tx=cx-px*ns; vw.ty=cy-py*ns; vwApply(); }
function vwZoomCentre(f){ vwZoomAt(vw.s*f,vw.cw/2,vw.ch/2); }
function vwFocusPin(r){ const pl=(pinsFor(current)[r]||[])[0]; if(!pl) return;
const ns=Math.max(vw.s,3); const px=pl[0]/100*vw.fw, py=pl[1]/100*vw.fh; vw.s=clamp(ns,1,8); vw.tx=vw.cw/2-px*vw.s; vw.ty=vw.ch/2-py*vw.s; vwApply(); }
function vwLoadImage(src,white,after){
vw.nw=0; vEl.st.hidden=true; vw.s=1;
const im=new Image(); im.alt=""; im.draggable=false;
im.onload=()=>{ vw.nw=im.naturalWidth; vw.nh=im.naturalHeight;
vEl.st.querySelector("img")?.remove(); vEl.st.prepend(im); vEl.st.classList.toggle("white",!!white);
vEl.st.hidden=false; vwLayout(); if(after) after(); };
im.onerror=()=>{ toast("Couldn’t load this image (offline and not saved yet?)"); };
im.src=src;
}
function vwPinsHtml(){
const G=GROUPS.find(x=>x.key===current); const refset=new Set(G.refs.map(r=>r.r));
return Object.entries(pinsFor(current)).flatMap(([r,list])=>list.map((p,i)=>{
const orphan=!refset.has(r); const st=orphan?"none":statusOf(current,r);
return `<button class="pin s-${st}${selRef===r?" sel":""}${orphan?" orphan":""}" style="left:${p[0]}%;top:${p[1]}%" data-vr="${r}" aria-label="Part ${r}">${selRef===r&&i===0?`<span class="lab">${r}</span>`:""}</button>`; })).join("");
}
function vwDrawingSheet(){
const G=GROUPS.find(x=>x.key===current); const pins=pinsFor(current);
const refs=G.refs.filter(r=>pins[r.r]);
const chips=refs.map(r=>`<button class="vchip s-${statusOf(current,r.r)}${selRef===r.r?" sel":""}" data-vr="${r.r}" data-fly="1">${r.r}</button>`).join("");
const R=selRef&&G.refs.find(r=>r.r===selRef);
const sel=R?`<div class="vsel"><div class="nm"><span class="mono">#${R.r}</span> ${esc(bestName(R))}</div>
<div class="sbtns">${Object.entries(STATUS).map(([k,l])=>`<button data-vs="${k}" aria-pressed="${statusOf(current,R.r)===k}" data-s="${k}">${l}</button>`).join("")}</div>
<div class="tools"><button class="btn" data-v="details">Notes, links &amp; photos</button><a class="btn" target="_blank" rel="noopener" href="https://www.oemmotorparts.com/en/search?filter=${encodeURIComponent(R.v[0].o)}">Find part</a></div></div>`
:`<div class="rsub">Tap a marker on the drawing, or a number below, to select a part. Pinch or double-tap to zoom.</div>`;
vEl.bt.innerHTML=`${sel}<div class="vchips">${chips||'<span class="rsub">No markers on this drawing yet.</span>'}</div>`;
vEl.st.querySelectorAll(".pin").forEach(n=>n.remove()); vEl.st.insertAdjacentHTML("beforeend",vwPinsHtml());
vEl.ti.textContent=G.title;
}
function vwPhotoSheet(){
const p=vw.list[vw.i]; if(!p) return;
vEl.ti.textContent=`${vw.i+1} / ${vw.list.length}`;
vEl.bt.innerHTML=`<div class="cap">${esc(p.label||"")}${p.label&&p.caption?" · ":""}${esc(p.caption||"")}${!p.label&&!p.caption?'<span class="rsub">No caption</span>':""}${p.at?` <span class="rsub">· ${when(p.at)}</span>`:""}</div>
${p.pending?'<div class="rsub">Waiting to upload.</div>':`<div class="tools"><button class="btn" data-v="cap">Edit caption</button><button class="btn" data-v="del">Delete photo</button></div>`}`;
vEl.pv.hidden=vEl.nx.hidden=vw.list.length<2;
}
function vwShowPhoto(){ const p=vw.list[vw.i]; vwLoadImage(p.src,false); vwPhotoSheet(); }
function openViewer(){
Object.assign(vEl,{root:$("#viewer"),cv:$("#vcanvas"),st:$("#vstage"),bt:$("#vbottom"),ti:$("#vtitle"),pv:document.querySelector(".vnav.prev"),nx:document.querySelector(".vnav.next")});
if(!vEl.ro){ vEl.ro=new ResizeObserver(()=>vwLayout()); vEl.ro.observe(vEl.cv); }
vEl.root.hidden=false; vw.open=true; document.body.style.overflow="hidden";
try{ history.pushState({v:1},""); }catch(_){}
}
function openDrawing(g){ const img=imageFor(g); if(!img) return; vw.mode="drawing"; openViewer(); vEl.st.querySelectorAll(".pin").forEach(n=>n.remove());
vEl.pv.hidden=vEl.nx.hidden=true; vwLoadImage(img,true,()=>{ vwDrawingSheet(); }); vwDrawingSheet(); }
function openPhotos(list,i){ if(!list.length) return; vw.mode="photos"; vw.list=list; vw.i=clamp(i,0,list.length-1); openViewer(); vEl.st.querySelectorAll(".pin").forEach(n=>n.remove()); vwShowPhoto(); }
function hideViewer(){ if(!vw.open) return; vw.open=false; vEl.root.hidden=true; document.body.style.overflow=""; vEl.st.innerHTML=""; }
function closeViewer(){ if(history.state&&history.state.v){ history.back(); } else hideViewer(); }
window.addEventListener("popstate",()=>hideViewer());
function vwSelect(r,fly){ selRef=r; filter="all"; vwDrawingSheet(); if(fly) vwFocusPin(r); render(); }
function vwStep(d){ if(vw.mode!=="photos"||vw.list.length<2) return; vw.i=(vw.i+d+vw.list.length)%vw.list.length; vwShowPhoto(); }
async function vwDelete(){
const p=vw.list[vw.i]; if(!p||p.id<0||!confirm("Delete this photo for everyone?")) return;
try{ const r=await send("DELETE","api/photos/"+p.id,undefined,()=>{ for(const k in state.photos) state.photos[k]=state.photos[k].filter(x=>x.id!==p.id); });
vw.list.splice(vw.i,1); if(!vw.list.length){ closeViewer(); } else { vw.i=Math.min(vw.i,vw.list.length-1); vwShowPhoto(); }
if(!r.queued) await reload(); else render(); }
catch(e){ toast("Couldn’t delete: "+e.message); }
}
async function vwCaption(){
const p=vw.list[vw.i]; const c=prompt("Caption",p.caption||""); if(c===null) return;
try{ const r=await send("PUT","api/photos/"+p.id,{caption:c},()=>{ const q=(state.photos[p.scope]||[]).find(x=>x.id===p.id); if(q) q.caption=c; });
p.caption=c; vwPhotoSheet(); if(!r.queued) await reload(); else render(); }
catch(e){ toast("Couldn’t save: "+e.message); }
}
document.addEventListener("click",async e=>{
const v=e.target.closest("#viewer"); if(!v) return; e.stopPropagation();
if(vw.moved){ return; }
const b=e.target.closest("button"); if(!b) return;
if(b.dataset.vr){ vwSelect(b.dataset.vr,!!b.dataset.fly); return; }
if(b.dataset.vs){ const R=selRef; const cur=statusOf(current,R); await savePart(current,R,{s:cur===b.dataset.vs?"none":b.dataset.vs}); vwDrawingSheet(); return; }
switch(b.dataset.v){
case "close": closeViewer(); break;
case "zin": vwZoomCentre(1.6); break;
case "zout": vwZoomCentre(1/1.6); break;
case "fit": vw.s=1; vwApply(); break;
case "prev": vwStep(-1); break;
case "next": vwStep(1); break;
case "del": vwDelete(); break;
case "cap": vwCaption(); break;
case "details": { const r=selRef; closeViewer(); setTimeout(()=>{ selRef=r; render(); },50); break; }
}
},true);
(function(){
const cv=document.getElementById("vcanvas"); const ptrs=new Map(); let g=null, lastTap=0, lastX=0, lastY=0;
const pt=e=>{ const r=cv.getBoundingClientRect(); return {x:e.clientX-r.left,y:e.clientY-r.top}; };
const begin=()=>{ const a=[...ptrs.values()];
if(a.length===1) g={t:"pan",x:a[0].x,y:a[0].y,tx:vw.tx,ty:vw.ty,sx:a[0].x,sy:a[0].y};
else if(a.length>=2){ g={t:"pinch",d:Math.hypot(a[0].x-a[1].x,a[0].y-a[1].y)||1,s:vw.s,mx:(a[0].x+a[1].x)/2,my:(a[0].y+a[1].y)/2,tx:vw.tx,ty:vw.ty}; } else g=null; };
cv.addEventListener("pointerdown",e=>{ if(e.target.closest(".vnav")) return; ptrs.set(e.pointerId,pt(e)); vw.moved=false; begin(); });
window.addEventListener("pointermove",e=>{ if(!ptrs.has(e.pointerId)||!g) return; const p=pt(e); ptrs.set(e.pointerId,p); const a=[...ptrs.values()];
if(g.t==="pan"&&a.length===1){ const dx=p.x-g.sx, dy=p.y-g.sy; if(Math.abs(dx)+Math.abs(dy)>8) vw.moved=true; vw.tx=g.tx+(p.x-g.x); vw.ty=g.ty+(p.y-g.y); vwApply(); }
else if(g.t==="pinch"&&a.length>=2){ vw.moved=true; const d=Math.hypot(a[0].x-a[1].x,a[0].y-a[1].y), mx=(a[0].x+a[1].x)/2, my=(a[0].y+a[1].y)/2;
const ns=clamp(g.s*d/g.d,1,8); const px=(g.mx-g.tx)/g.s, py=(g.my-g.ty)/g.s; vw.s=ns; vw.tx=mx-px*ns; vw.ty=my-py*ns; vwApply(); } });
const up=e=>{ if(!ptrs.has(e.pointerId)) return; const p=ptrs.get(e.pointerId); ptrs.delete(e.pointerId);
if(g&&g.t==="pan"&&ptrs.size===0){
const dx=p.x-g.sx, dy=p.y-g.sy;
if(vw.mode==="photos"&&vw.s<1.05&&Math.abs(dx)>70&&Math.abs(dx)>Math.abs(dy)*1.5){ vwStep(dx<0?1:-1); }
else if(!vw.moved&&e.pointerType==="touch"&&!e.target.closest(".pin")){ const now=Date.now();
if(now-lastTap<320&&Math.hypot(p.x-lastX,p.y-lastY)<30){ vwZoomAt(vw.s>1.2?1:2.8,p.x,p.y); lastTap=0; } else { lastTap=now; lastX=p.x; lastY=p.y; } }
}
begin(); setTimeout(()=>{ if(!ptrs.size) vw.moved=false; },60); };
window.addEventListener("pointerup",up); window.addEventListener("pointercancel",up);
cv.addEventListener("wheel",e=>{ e.preventDefault(); const p=pt(e); vwZoomAt(vw.s*Math.exp(-e.deltaY*0.0018),p.x,p.y); },{passive:false});
cv.addEventListener("dblclick",e=>{ if(e.target.closest(".pin")) return; const p=pt(e); vwZoomAt(vw.s>1.2?1:2.8,p.x,p.y); });
window.addEventListener("keydown",e=>{ if(!vw.open) return; if(e.key==="Escape") closeViewer(); if(e.key==="ArrowLeft") vwStep(-1); if(e.key==="ArrowRight") vwStep(1); if(e.key==="+"||e.key==="=") vwZoomCentre(1.4); if(e.key==="-") vwZoomCentre(1/1.4); });
})();
// ---------- boot ----------
try{ const t=localStorage.getItem(LS+"-tab"); if(t && (t==="shop"||t==="gallery"||GROUPS.some(g=>g.key===t))) current=t; }catch(_){}
if("serviceWorker" in navigator) navigator.serviceWorker.register("sw.js").catch(()=>{});
window.addEventListener("online",()=>reload());
window.addEventListener("offline",()=>{ online=false; badgeNow(); });
render();
(async()=>{ try{ const c=await kvGet("state"); if(c&&!online) state=Object.assign(EMPTY(),c); pendingN=(await qAll()).length; }catch(_){} render(); reload(); })();
// pick up changes made on another device when you come back to the tab
document.addEventListener("visibilitychange",()=>{ if(!document.hidden && !drag && !document.activeElement.matches("textarea,input")) reload(); });
</script>
</body>
</html>