{"id":32330,"date":"2026-02-10T15:37:58","date_gmt":"2026-02-10T15:37:58","guid":{"rendered":"https:\/\/meber2026.graphoservice.it\/spare-part-template\/"},"modified":"2026-05-07T13:50:43","modified_gmt":"2026-05-07T13:50:43","slug":"spare-part-template","status":"publish","type":"page","link":"https:\/\/www.meber.it\/en\/spare-part-template\/","title":{"rendered":"Spare Part Template"},"content":{"rendered":"<div class=\"et_pb_section_0 et_pb_section et_section_regular et_flex_section\">\n<div class=\"et_pb_row_0 et_pb_row et_flex_row\">\n<div class=\"et_pb_column_0 et_pb_column et-last-child et_flex_column et_pb_css_mix_blend_mode_passthrough et_flex_column_24_24 et_flex_column_12_24_tablet et_flex_column_24_24_phone\">\n<div class=\"et_pb_group_0 et_pb_group et_pb_module et_flex_group et_pb_css_mix_blend_mode_passthrough\">\n<div class=\"et_pb_icon_0 et_pb_icon et_animated et_pb_module et_flex_module\"><span class=\"et_pb_icon_wrap\"><span class=\"et-pb-icon\">\uf0ad<\/span><\/span><\/div>\n\n<div class=\"et_pb_text_0 et_pb_text et_pb_bg_layout_dark et_animated et_pb_module et_block_module\"><div class=\"et_pb_text_inner\"><h1><strong>MERCURY <\/strong><\/h1>\n<\/div><\/div>\n\n<div class=\"et_pb_text_1 et_pb_text et_pb_bg_layout_dark et_animated et_pb_module et_block_module\"><div class=\"et_pb_text_inner\"><h1>Spare parts<\/h1>\n<\/div><\/div>\n\n<div class=\"et_pb_text_2 et_pb_text et_pb_bg_layout_light et_pb_module et_flex_module\"><div class=\"et_pb_text_inner\"><h6><span id=\"text2_b\" class=\"item_subtitle\">Sparte Parts Procurement Page<\/span><\/h6>\n<\/div><\/div>\n<\/div>\n\n<div class=\"et_pb_image_0 et_pb_image et_pb_module et_flex_module\"><span class=\"et_pb_image_wrap\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/www.meber.it\/wp-content\/uploads\/2026\/02\/baffo_bianco.svg\" title=\"baffo_bianco\" width=\"1600\" height=\"200\" srcset=\"https:\/\/www.meber.it\/wp-content\/uploads\/2026\/02\/baffo_bianco.svg 1600w\" sizes=\"(min-width: 0px) and (max-width: 480px) 480px, (min-width: 481px) and (max-width: 980px) 980px, (min-width: 981px) and (max-width: 1280px) 1280px, (min-width: 1281px) 1600px, 100vw\" class=\"wp-image-127\" \/><\/span><\/div>\n<\/div>\n<\/div>\n<\/div>\n\n<div class=\"et_pb_section_1 et_pb_section et_section_regular et_flex_section\">\n<div class=\"et_pb_row_1 et_pb_row et_flex_row\">\n<div class=\"et_pb_column_1 et_pb_column et-last-child et_flex_column et_pb_css_mix_blend_mode_passthrough et_flex_column_24_24 et_flex_column_24_24_tablet et_flex_column_24_24_phone\">\n<div class=\"et_pb_code_0 et_pb_code et_pb_module\"><div class=\"et_pb_code_inner\"><header class=\"topbar\">\n  <div class=\"brand-wrap\">\n    <img decoding=\"async\" src=\"https:\/\/www.meber.it\/website\/images\/logo-MeBer.svg\" width=\"140\" alt=\"Me.Ber Logo\">\n    <span class=\"brand-subtitle\">Spare Parts Procurement System<\/span>\n  <\/div>\n\n  <div class=\"search-cart\">\n    <div class=\"searchbox\">\n      <input id=\"q\" type=\"search\" placeholder=\"Cerca per codice, descrizione o posizione (es. 8) \u2026\" autocomplete=\"off\" aria-label=\"Cerca\">\n      <div id=\"suggestions\" class=\"suggestions\"><\/div>\n    <\/div>\n\n    <!-- Pulsante carrello -->\n    <button id=\"cart-toggle\" class=\"cart-button\" aria-expanded=\"false\" aria-controls=\"cart-panel\">\n      <span class=\"cart-icon\" aria-hidden=\"true\">\ud83d\uded2<\/span>\n<span class=\"cart-label\">Carrello<\/span>\n<span id=\"cart-count\" class=\"cart-count\">0<\/span>\n    <\/button>\n  <\/div>\n<\/header>\n<!-- Dropdown del carrello: si apre sopra i contenuti -->\n<section id=\"cart-panel\" class=\"cart-panel\" hidden=\"\">\n  <div id=\"cart\"><\/div>\n  <div class=\"cart-actions\">\n    <button id=\"checkout\" class=\"btn-primary\">Esporta ordine<\/button>\n  <\/div>\n<\/section>\n\n  <main class=\"layout\">\n    <aside class=\"panel tree\">\n      <h2>Naviga<\/h2>\n      <div id=\"tree\"><\/div>\n    <\/aside>\n\n<section class=\"panel drawing\">\n  <div class=\"panel-head\">\n    <h2>Vista<\/h2>\n    <button id=\"hs-tools-toggle\" class=\"icon-btn\" aria-expanded=\"false\" aria-controls=\"hs-tools\" title=\"Strumenti di modifica\">\n \u270e\n    <\/button>\n  <\/div>\n\n  <div id=\"hs-tools\" class=\"hs-tools\" hidden=\"\">\n    <button id=\"hs-edit-toggle\">Modifica hotspot<\/button>\n    <div class=\"hs-size\">\n      <label for=\"hs-radius\">Dimensione hotspot<\/label>\n<input id=\"hs-radius\" type=\"range\" min=\"8\" max=\"48\" step=\"1\" value=\"18\" disabled=\"disabled\"\/>\n<span id=\"hs-radius-val\" class=\"muted\">18 px<\/span>\n    <\/div>\n    <div class=\"hs-bg\">\n      <input id=\"hs-bg-file\" type=\"file\" accept=\"image\/*\" hidden=\"\">\n      <button id=\"hs-bg-btn\" disabled>Imposta immagine di sfondo<\/button>\n      <span id=\"hs-bg-note\" class=\"muted\"><\/span>\n    <\/div>\n    <button id=\"hs-export\" disabled>Esporta disegno<\/button>\n  <\/div>\n\n  <div id=\"drawing\"><\/div>\n<\/section>\n    <section class=\"panel list\">\n      <h2>Componenti<\/h2>\n      <div id=\"list\"><\/div>\n    <\/section>\n\n  <\/main>\n<div id=\"lightbox\" class=\"lightbox\" hidden=\"\">\n  <div class=\"lightbox-backdrop\"><\/div>\n  <div class=\"lightbox-body\">\n    <button class=\"lb-close\" aria-label=\"Chiudi\">\u00d7<\/button>\n    <img id=\"lb-img\" alt=\"Anteprima componente\">\n    <div id=\"lb-caption\" class=\"muted\"><\/div>\n  <\/div>\n<\/div><\/div><\/div>\n\n<div class=\"et_pb_code_1 et_pb_code et_pb_module\"><div class=\"et_pb_code_inner\"><style>\n:root{--gap:clamp(10px,2vw,18px);--radius:14px;--bg:#f6f7f9;--muted:#6b7280;--card:#fff;--accent:#0b62f6}\n*{box-sizing:border-box}\nbody{margin:0;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;background:var(--bg);color:#111}\n.topbar{display:flex;align-items:center;gap:var(--gap);padding:16px 20px;background:#fff;box-shadow:0 2px 12px rgba(0,0,0,.06);position:sticky;top:0;z-index:10}\n.topbar h1{font-size:18px;margin:0;white-space:nowrap}\n.searchbox{position:relative;flex:1;max-width:700px}\n.searchbox input{width:100%;padding:10px 12px;border:1px solid #e5e7eb;border-radius:10px}\n.suggestions{position:absolute;left:0;right:0;top:100%;background:#fff;border:1px solid #e5e7eb;border-top:none;display:none;max-height:320px;overflow:auto;border-radius:0 0 10px 10px}\n.layout{display:grid;grid-template-columns:260px 1fr 1fr ;gap:var(--gap);padding:var(--gap);align-items:start}\n.panel{background:var(--card);border-radius:var(--radius);padding:14px;box-shadow:0 4px 16px rgba(0,0,0,.05)}\n.panel h2{margin:0 0 10px;font-size:14px;color:var(--muted);font-weight:600;letter-spacing:.02em;text-transform:uppercase}\n.tree ul{list-style:none;margin:0;padding:0}\n.tree li{margin:6px 0}\n.tree button{background:none;border:none;text-align:left;padding:6px 8px;border-radius:8px;width:100%;cursor:pointer}\n.tree button:hover{background:#f2f4f8}\n.list-item{display:grid;grid-template-columns: 100px 80px 1fr auto;gap:10px;align-items:center;padding:8px;border-bottom:1px solid #eee}\n.list-item .code{font-family:ui-monospace, SFMono-Regular, Menlo, monospace;background:#f7f7f8;padding:4px 6px;border-radius:6px}\n.list-item .callout{background:#eef3ff;border:1px solid #d7e3ff;padding:2px 6px;border-radius:6px;font-size:12px}\n.qty{display:flex;gap:6px;align-items:center}\n.qty input{width:70px;padding:6px;border:1px solid #ddd;border-radius:8px}\n.cart-line{display:grid;grid-template-columns:1fr auto auto;gap:10px;align-items:center;padding:8px;border-bottom:1px solid #eee}\n.cart .total{display:flex;justify-content:space-between;margin-top:10px;font-weight:700}\n@media (max-width:1100px){.layout{grid-template-columns:1fr;grid-auto-rows:max-content}.cart{position:sticky;bottom:0}}\n\/* Recursive tree *\/\n.tree-ul, .children{list-style:none;margin:0;padding-left:0}\n.node{margin:4px 0}\n.node-row{display:flex;align-items:center;gap:6px;padding:4px 6px;border-radius:8px}\n.node-row .toggle{width:18px;height:18px;border:1px solid #d1d5db;border-radius:4px;background:#fff;position:relative}\n.node-row .toggle::after{content:'+';position:absolute;inset:0;display:grid;place-items:center;font-size:12px;color:#6b7280}\n.node.open > .node-row .toggle::after{content:'\u2013'}\n.node-row .title{background:none;border:none;cursor:pointer}\n.node .children{display:none;padding-left:14px;border-left:2px dashed #e5e7eb;margin-left:6px}\n.node.open > .children{display:block}\n.node, .leaf{padding-left:calc(var(--lvl,0) * 8px)}\n.leaf button.comp{background:none;border:none;cursor:pointer;padding:4px 8px;border-radius:6px}\n.leaf button.comp:hover{background:#f3f4f6}\n\n\/* Drawing panel *\/\n.dwg-wrap{width:100%;border:1px solid #e5e7eb;border-radius:10px;overflow:hidden;background:#fff}\n.dwg{display:block;max-width:100%;height:auto}\n.hotbar{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}\n\n\/* Lightbox *\/\n.lightbox{position:fixed;inset:0;display:grid;place-items:center;z-index:50}\n.lightbox[hidden]{display:none}\n.lightbox-backdrop{position:absolute;inset:0;background:rgba(0,0,0,.5)}\n.lightbox-body{position:relative;background:#fff;border-radius:12px;max-width:min(90vw,1000px);max-height:90vh;display:flex;flex-direction:column;gap:10px;padding:12px}\n.lightbox-body img{max-width:82vw;max-height:70vh;object-fit:contain;border-radius:8px}\n.lb-close{position:absolute;top:6px;right:8px;background:#fff;border:1px solid #e5e7eb;border-radius:50%;width:32px;height:32px;cursor:pointer}\n\n.hs-tools{display:flex;gap:8px;margin:6px 0 8px}\n.hs-tools button{padding:6px 10px;border:1px solid #d1d5db;border-radius:8px;background:#fff;cursor:pointer}\n.hs-editing .hotspot{cursor:move}\n\n\n.hs-tools{display:flex;flex-wrap:wrap;gap:10px;margin:6px 0 8px;align-items:center}\n.hs-tools button{padding:6px 10px;border:1px solid #d1d5db;border-radius:8px;background:#fff;cursor:pointer}\n.hs-size{display:flex;align-items:center;gap:8px}\n.hs-size label{font-size:12px;color:#6b7280}\n#hs-radius{width:160px}\n.hs-bg{display:flex;align-items:center;gap:8px}\n\n\/* Hotspot: mostra mano\/freccia, mai il cursore \"testo\" *\/\n.dwg-inline svg .hotspot { cursor: pointer; }\n.dwg-inline svg .hotspot text{\n  user-select: none;          \/* no selezione testo *\/\n  -webkit-user-select: none;\n  pointer-events: none;       \/* click passa al cerchio\/gruppo *\/\n}\n\n\/* In modalit\u00e0 editing: cursore \"grab\" per far capire che si trascina *\/\n.dwg-inline svg.editing .hotspot { cursor: grab; }\n.dwg-inline svg.editing .hotspot:active { cursor: grabbing; }\n\n\n:root{\n  \/* palette ispirata a Me.Ber.: rosso acceso + grigi scuri *\/\n  --mb-red: rgb(255, 175, 0); \/* rosso brand approx *\/\n  --mb-dark: #111;\n  --mb-muted: #666;\n  --mb-bg: #f6f7f9;\n  --radius: 16px;\n}\n\n\/* Topbar *\/\n.topbar{\n  position: sticky; top: 0; z-index: 20;\n  display: grid; grid-template-columns: 1fr auto;\n  gap: 12px; align-items: center;\n  padding: 10px 14px; background:#fff; border-bottom:1px solid #eaeaea;\n}\n.topbar .brand{ font-weight: 600; color: var(--mb-dark); }\n.search-cart{ display:flex; gap:12px; align-items:center; }\n.searchbox{ position: relative; width:min(560px, 60vw); }\n.searchbox input[type=\"search\"]{\n  width:100%; padding:10px 12px; border:1px solid #ddd; border-radius: 10px;\n}\n.sugmenu{ position:absolute; inset:auto 0 0 0; transform: translateY(100%);\n  background:#fff; border:1px solid #e6e6e6; border-radius:10px; margin-top:6px; max-height:50vh; overflow:auto; }\n\n\/* Cart button *\/\n.cart-button{\n  display:inline-flex; align-items:center; gap:8px;\n  border:1px solid #ddd; border-radius: 999px; padding:8px 12px; background:#fff;\n}\n.cart-count{\n  min-width:22px; height:22px; line-height:22px;\n  padding:0 6px; border-radius:999px; background: var(--mb-red); color:#fff; font-weight:700; font-size:12px;\n}\n\n\/* Cart panel collassabile *\/\n.cart-panel{\n  border-bottom:1px solid #eee; background:#fff;\n  padding: 10px 14px; display:block;\n}\n.cart-actions{ margin-top:10px; display:flex; justify-content:flex-end; }\n.btn-primary{ background: var(--mb-red); color:#fff; border:none; padding:10px 14px; border-radius:10px; }\n\n\/* Layout principale responsive *\/\n.main{\n  display:grid;\n  grid-template-columns: 1.2fr .8fr; \/* vista + lista (desktop) *\/\n  gap: 18px;\n  max-width: 1280px; margin: 16px auto; padding: 0 14px;\n}\n@media (max-width: 900px){\n  .main{ grid-template-columns: 1fr; }\n}\n\n\/* Vista (SVG) fluida con mantenimento aspect *\/\n#drawing .dwg-wrap{\n  background: var(--mb-bg);\n  border:1px solid #eee; border-radius: var(--radius);\n  padding: 12px; min-height: 320px;\n}\n#drawing .dwg-inline{\n  width:100%; height:auto;\n  display:block; overflow:auto;\n}\n#drawing .dwg-inline svg{\n  display:block;\n  width:100%; height:auto; \/* riempie orizzontale, scala verticalmente *\/\n}\n\n\/* Lista componenti \u201ccard\u201d *\/\n#list .list-item{\n  display:grid; grid-template-columns: auto 1fr auto; align-items:center;\n  gap:10px; padding:10px; border:1px solid #eee; border-radius:12px; background:#fff; margin-bottom:10px;\n}\n#list .callout{ font-weight:700; color: var(--mb-red); }\n#list .code{ font-family: ui-monospace, Menlo, Consolas, monospace; color:#333; }\n#list .qty input{ width:64px; }\n\n\/* Mobile: lista sotto alla vista *\/\n@media (max-width: 900px){\n  #list .list-item{ grid-template-columns: 1fr auto; }\n}\n\n\/* Tipografia ispirata a Me.Ber *\/\nbody{\n  font-family: \"Inter\", system-ui, -apple-system, \"Segoe UI\", Roboto, Arial, sans-serif;\n  color: var(--mb-dark);\n}\nh1,h2,h3{ font-weight: 800; letter-spacing: -0.01em; }\na, .btn-primary{ text-decoration: none; }\na:hover, .btn-primary:hover{ filter: brightness(.95); }\n\n\n\/* Topbar: contenitore per ricerca + carrello a destra *\/\n.topbar{\n  position: sticky; top: 0; z-index: 30; \/* sopra ai pannelli *\/\n  display: grid; grid-template-columns: 1fr auto; gap: 12px; align-items: center;\n}\n\n\/* wrapper a destra *\/\n.search-cart{ display:flex; align-items:center; gap: 12px; }\n\n\/* ---------- Suggerimenti ricerca: pi\u00f9 padding + spacing ---------- *\/\n.suggestions{\n  position:absolute; left:0; right:0; top:100%;\n  background:#fff; border:1px solid #e5e7eb; border-top:none;\n  display:none; max-height:50vh; overflow:auto;\n  border-radius:0 0 12px 12px; padding:8px; z-index: 25;\n  box-shadow: 0 12px 24px rgba(0,0,0,.08);\n}\n.suggestions .sug{\n  padding: 10px 12px; border-radius: 8px; cursor: pointer;\n}\n.suggestions .sug + .sug{ margin-top: 6px; }\n.suggestions .sug:hover{ background:#f6f7f9; }\n\n\/* ---------- Carrello: bottone con badge ---------- *\/\n.cart-button{\n  display:inline-flex; align-items:center; gap:8px;\n  border:1px solid #e5e7eb; background:#fff; padding:8px 12px;\n  border-radius: 999px; cursor: pointer;\n}\n.cart-count{\n  min-width:22px; height:22px; line-height:22px;\n  padding:0 6px; border-radius:999px; background:rgb(255, 175, 0);; color:#fff;\n  font-weight:700; font-size:12px; text-align:center;\n}\n\n\/* ---------- Carrello: dropdown overlay ---------- *\/\n.cart-panel{\n  position: absolute; right: 16px; top: calc(64px + 8px); \/* sotto la topbar *\/\n  width: min(520px, 92vw);\n  background:#fff; border:1px solid #e5e7eb; border-radius: 12px;\n  box-shadow: 0 16px 40px rgba(0,0,0,.12); z-index: 40; padding: 12px;\n}\n.cart-panel[hidden]{ display:none; }\n.cart-actions{ margin-top:10px; display:flex; justify-content:flex-end; }\n.btn-primary{ background:rgb(255, 175, 0);; color:#fff; border:none; padding:10px 14px; border-radius:10px; }\n\n\/* Cart rows *\/\n.cart-line{ display:grid; grid-template-columns: 1fr auto auto; gap:10px; align-items:center;\n  padding:8px; border-bottom:1px solid #eee; }\n.cart .total, .cart-panel .total{ display:flex; justify-content:space-between; margin-top:10px; font-weight:700; }\n\n\/* Assicuriamo che l\u2019header faccia da ancoraggio per l\u2019overlay *\/\n.topbar{ position: sticky; top:0; }\nbody{ position: relative; }\n\n.suggestions .sug.active {\n  background: #D60017;\n  color: #fff;\n}\n\n\/* Header della sezione con icona a destra *\/\n.panel-head{\n  display:flex; align-items:center; justify-content:space-between;\n  gap: 10px; margin-bottom: 8px;\n}\n.panel-head h2{ margin: 0; }\n\n\/* Icona toggle strumenti *\/\n.icon-btn{\n  display:inline-flex; align-items:center; justify-content:center;\n  width:36px; height:36px; border:1px solid #e5e7eb; border-radius:10px;\n  background:#fff; cursor:pointer; font-size:18px; line-height:1;\n}\n.icon-btn:hover{ background:#f6f7f9; }\n\n\/* Nascondi strumenti quando hidden \u00e8 presente *\/\n#hs-tools[hidden]{ display:none; }\n\n\/* (opzionale) transizione morbida quando compaiono *\/\n#hs-tools{\n  transition: opacity .15s ease;\n}\n\n\/* --- Brand area con sottotitolo --- *\/\n.brand-wrap{\n  display:flex;\n  align-items:center;\n  gap:12px;\n}\n.brand-subtitle{\n  font-size:14px;\n  font-weight:500;\n  color:#555;\n  letter-spacing:0.03em;\n  border-left:1px solid #ddd;\n  padding-left:10px;\n  line-height:1.2;\n}\n@media (max-width:700px){\n  .brand-subtitle{\n    display:none; \/* nascondi il testo su mobile per risparmiare spazio *\/\n  }\n}\n\n\/* ====== Cards componenti (nuovo layout) ====== *\/\n.list-item{\n  display: grid;\n  grid-template-columns: 92px 1fr auto;\n  gap: 14px;\n  align-items: center;\n  padding: 12px;\n  border: 1px solid #ececec;\n  border-radius: 12px;\n  background: #fff;\n  margin-bottom: 10px;\n}\n\n\/* Colonna POS *\/\n.list-item .pos{ display:flex; flex-direction:column; gap:4px; width: 80px; }\n.list-item .pos-label{\n  font-size: 13px; font-weight: 600; color: #9aa1ab; text-transform: none;\n}\n.list-item .pos-num{\n  color:var(--mm-accent); font-weight: 800; font-size: 22px; line-height: 1;\n}\n\n\/* Colonna centrale *\/\n.list-item .meta{ min-width: 0; } \/* evita overflow *\/\n.list-item .code-badge{\n  display:inline-block; padding:6px 10px; background:#f2f3f5; border-radius:8px;\n  font-family: ui-monospace, Menlo, Consolas, monospace;\n  font-size:14px; letter-spacing:0.02em; color:#111;\n}\n.list-item .title{ margin-top:18px 0px; display:flex; align-items:center; gap:8px; }\n.list-item .title strong{ font-size:18px; font-weight:800; color:#111; }\n.list-item .group{ margin-top:2px; color:#8a9099; text-transform:uppercase; letter-spacing:.03em; font-weight:300; font-size: 11px;}\n\n\/* Colonna quantit\u00e0\/azione *\/\n.list-item .qty{ display:flex; align-items:center; gap:8px; }\n.list-item .qty input{\n  width:64px; padding:6px 8px; border:1px solid #d9dce1; border-radius:10px;\n}\n.btn-ghost{\n  padding:8px 12px; border:1px solid #d9dce1; border-radius:10px; background: #ffaf00;\n    cursor: pointer;\n    color: #fff;\n}\n.btn-ghost:hover{ background:#f6f7f9; }\n\n\/* Mobile: impila quantit\u00e0 sotto *\/\n@media (max-width: 820px){\n  .list-item{ grid-template-columns: 78px 1fr; align-items:flex-start; }\n  .list-item .qty{ grid-column: 2 \/ 3; margin-top:8px; justify-content:flex-start; }\n}\n\n<\/style><\/div><\/div>\n\n<div class=\"et_pb_code_2 et_pb_code et_pb_module\"><div class=\"et_pb_code_inner\"><script>\n(async function () {\n  let editing = false;\n  let currentDrawing = null;\n  let defPath = '\/wp-content\/uploads\/spare-parts\/' ;\n\n  const components = await fetch('\/wp-content\/uploads\/spare-parts\/mercury_5_test\/it\/current\/components.json').then(r => r.json());\n  const tree       = await fetch('\/wp-content\/uploads\/spare-parts\/data\/products\/mercury5\/tree.json').then(r => r.json());\n  const drawings   = await fetch('\/wp-content\/uploads\/spare-parts\/data\/products\/mercury5\/drawings.json').then(r => r.json());\n  const index      = await fetch('\/wp-content\/uploads\/spare-parts\/data\/search\/mercury5.index.json').then(r => r.json());\n\n  const $ = s => document.querySelector(s);\n\n  const cartKey = 'mercury5_cart_v2';\n  const cart = {\n    lines: JSON.parse(localStorage.getItem(cartKey) || '[]'),\n    save(){ localStorage.setItem(cartKey, JSON.stringify(this.lines)); renderCart(); updateCartBadge(); },\n    add(id, qty=1){ const f=this.lines.find(l=>l.id===id); if(f) f.qty+=qty; else this.lines.push({id,qty}); this.save(); },\n    update(id, qty){ const it=this.lines.find(l=>l.id===id); if(it){ it.qty = Math.max(1, parseInt(qty||1,10)); this.save(); } },\n    remove(id){ this.lines = this.lines.filter(l=>l.id!==id); this.save(); }\n  };\n\n  function byId(id){ return components.components.find(c=>c.id===id); }\n\n  const cartToggle = document.getElementById('cart-toggle');\n  const cartPanel  = document.getElementById('cart-panel');\n  const cartCountEl= document.getElementById('cart-count');\n\n  function setCartOpen(open){\n    if(!cartPanel || !cartToggle) return;\n    cartPanel.hidden = !open;\n    cartToggle.setAttribute('aria-expanded', String(open));\n    if(open) window.scrollTo({ top: 0, behavior: 'smooth' });\n  }\n  function updateCartBadge(){\n    if(!cartCountEl) return;\n    const total = cart.lines.reduce((s,l)=>s + (parseInt(l.qty,10)||0), 0);\n    cartCountEl.textContent = String(total);\n  }\n  cartToggle?.addEventListener('click', ()=> setCartOpen(cartPanel.hidden));\n  document.addEventListener('click', (e)=>{\n    if(!cartPanel || !cartToggle) return;\n    if(!cartPanel.contains(e.target) && e.target !== cartToggle && !cartToggle.contains(e.target)){\n      setCartOpen(false);\n    }\n  });\n  updateCartBadge();\n\n  function renderTree(){\n    const el = $('#tree');\n    function nodeHTML(node, level=0){\n      const hasKids = (node.children && node.children.length) || (node.component_ids && node.component_ids.length);\n      const label = node.title;\n      const data = `data-node=\"${node.id}\"`;\n      const collapsedClass = (node.id === 'node-root') ? 'open' : 'collapsed';\n      let html = `<li class=\"node ${collapsedClass}\" ${data} style=\"--lvl:${level}\">\n        <div class=\"node-row\">\n          <button class=\"toggle\" aria-label=\"toggle\"><\/button>\n          <button class=\"title\" data-select=\"${node.id}\">${label}<\/button>\n        <\/div>`;\n      if(hasKids){\n        const kids = (node.children||[]).map(n=>nodeHTML(n, level+1)).join('');\n        const comps = (node.component_ids||[]).map(id=>{\n          const c = byId(id);\n          const code = (c && c.code) ? c.code : id;\n          return `<li class=\"leaf\" data-comp=\"${id}\" style=\"--lvl:${level+1}\"><button class=\"comp\">\u2022 ${code}<\/button><\/li>`;\n        }).join('');\n        html += `<ul class=\"children\">${kids}${comps}<\/ul>`;\n      }\n      html += `<\/li>`;\n      return html;\n    }\n    el.innerHTML = `<ul class=\"tree-ul\">${nodeHTML(tree.root, 0)}<\/ul>`;\n    function collectCompIds(node){\n      const out = new Set();\n      (function walk(n){\n        (n.component_ids || []).forEach(id => out.add(id));\n        (n.children || []).forEach(walk);\n      })(node);\n      return Array.from(out);\n    }\n    el.onclick = (e)=>{\n      const t = e.target;\n      const li = t.closest('.node');\n      if(t.classList.contains('toggle')){\n        if(li){ li.classList.toggle('open'); li.classList.toggle('collapsed'); }\n        return;\n      }\n      const titleBtn = t.closest('.title');\nif(titleBtn){\n  const nodeId = titleBtn.dataset.select;\n\n  \/\/ util per cercare il nodo nell'albero\n  function findById(n){\n    if(n.id===nodeId) return n;\n    for(const ch of (n.children||[])){\n      const out = findById(ch);\n      if(out) return out;\n    }\n    return null;\n  }\n\n  if(nodeId === 'node-root'){\n    \/\/ Apri disegno assieme + lista di TUTTE le parti dell\u2019assieme\n    renderDrawing('__ROOT__');\n    const allIds = collectCompIds(tree.root);\n    const items = allIds.map(id => byId(id)).filter(Boolean);\n    renderList(items);\n    if(li){ li.classList.add('open'); li.classList.remove('collapsed'); }\n  } else {\n    const n = findById(tree.root);\n    const groupTitle = n?.title || '';\n    if(groupTitle){\n      renderDrawing(groupTitle);\n      \/\/ Lista con TUTTE le parti del sotto-albero (anche se il nodo ha figli)\n      const ids = collectCompIds(n);\n      const items = ids.map(id => byId(id)).filter(Boolean);\n      renderList(items);\n    }\n    if(li){ li.classList.add('open'); li.classList.remove('collapsed'); }\n  }\n  return;\n}\n      const compBtn = t.closest('.leaf');\n      if(compBtn){\n        const compId = compBtn.dataset.comp;\n        const c = byId(compId);\n        if(c){ renderList([c]); }\n        return;\n      }\n    };\n  }\n\n  async function makeSquareDataURL(file){\n    const img = await new Promise((res, rej)=>{\n      const fr = new FileReader();\n      fr.onload = () => { const im = new Image(); im.onload=()=>res(im); im.onerror=rej; im.src = fr.result; };\n      fr.onerror = rej;\n      fr.readAsDataURL(file);\n    });\n    const side = Math.max(img.width, img.height);\n    const canvas = document.createElement('canvas');\n    canvas.width = side; canvas.height = side;\n    const ctx = canvas.getContext('2d');\n    ctx.fillStyle = '#ffffff'; ctx.fillRect(0,0,side,side);\n    const x = (side - img.width)\/2; const y = (side - img.height)\/2;\n    ctx.drawImage(img, x, y);\n    return canvas.toDataURL('image\/png', 0.92);\n  }\n\n  function getViewBox(svgRoot){\n    if (svgRoot.viewBox && svgRoot.viewBox.baseVal && svgRoot.viewBox.baseVal.width && svgRoot.viewBox.baseVal.height) {\n      return svgRoot.viewBox.baseVal;\n    }\n    const w = (svgRoot.width && svgRoot.width.baseVal && svgRoot.width.baseVal.value) ? svgRoot.width.baseVal.value : 1200;\n    const h = (svgRoot.height && svgRoot.height.baseVal && svgRoot.height.baseVal.value) ? svgRoot.height.baseVal.value : 1200;\n    return { x: 0, y: 0, width: w, height: h };\n  }\n\n  function applyBackgroundImage(svgRoot, dataUrl, vbBaseInput){\n    if(!svgRoot) return;\n    let vb = vbBaseInput || getViewBox(svgRoot);\n    const rects = Array.from(svgRoot.querySelectorAll('rect'));\n    const isFullRect = (r)=>{\n      const x = +r.getAttribute('x') || 0;\n      const y = +r.getAttribute('y') || 0;\n      const w = +r.getAttribute('width')  || 0;\n      const h = +r.getAttribute('height') || 0;\n      return Math.abs(x - (vb.x||0)) < 0.5 && Math.abs(y - (vb.y||0)) < 0.5 && Math.abs(w - (vb.width||0))  < 0.5 && Math.abs(h - (vb.height||0)) < 0.5;\n    };\n    const fullRects = rects.filter(isFullRect);\n    fullRects.forEach(r=>{ r.setAttribute('fill', 'none'); r.setAttribute('fill-opacity', '0'); });\n    let imgEl = svgRoot.querySelector('image[data-role=\"bg\"]');\n    if(!imgEl){\n      imgEl = document.createElementNS('http:\/\/www.w3.org\/2000\/svg','image');\n      imgEl.setAttribute('data-role','bg');\n      imgEl.setAttribute('x', String(vb.x || 0));\n      imgEl.setAttribute('y', String(vb.y || 0));\n      imgEl.setAttribute('width',  String(vb.width  || 1200));\n      imgEl.setAttribute('height', String(vb.height || 1200));\n      imgEl.setAttribute('preserveAspectRatio','xMidYMid slice');\n      imgEl.setAttribute('pointer-events','none');\n      if(fullRects.length){\n        const lastFullRect = fullRects[fullRects.length - 1];\n        lastFullRect.parentNode.insertBefore(imgEl, lastFullRect.nextSibling);\n      } else { svgRoot.insertBefore(imgEl, svgRoot.firstChild); }\n    } else {\n      imgEl.setAttribute('x', String(vb.x || 0));\n      imgEl.setAttribute('y', String(vb.y || 0));\n      imgEl.setAttribute('width',  String(vb.width  || 1200));\n      imgEl.setAttribute('height', String(vb.height || 1200));\n    }\n    imgEl.setAttributeNS('http:\/\/www.w3.org\/1999\/xlink','href', dataUrl);\n    imgEl.setAttribute('href', dataUrl);\n  }\n\n  function applyHotspotRadius(svgRoot, radiusPx){\n    svgRoot.querySelectorAll('.hotspot > circle').forEach(c=>{ c.setAttribute('r', radiusPx); });\n    const font = Math.max(10, Math.round(radiusPx * 0.9));\n    svgRoot.querySelectorAll('.hotspot > text').forEach(t=>{\n      t.setAttribute('font-size', font);\n      t.setAttribute('dominant-baseline','middle');\n      t.setAttribute('text-anchor','middle');\n    });\n  }\n\n  async function renderDrawing(groupTitle){\n    const el = document.querySelector('#drawing');\n    let dw = null;\n    if(groupTitle === '__ROOT__'){\n      dw = drawings.drawings.find(d=>d.title.endsWith('(Generale)')) || drawings.drawings.find(d=>d.title==='Mercury Cinque (Generale)');\n    } else if(groupTitle){\n      dw = drawings.drawings.find(d=>d.title===groupTitle);\n    }\n    currentDrawing = dw;\n\n    const exportBtn  = document.getElementById('hs-export');\n    const radiusInput= document.getElementById('hs-radius');\n    const radiusVal  = document.getElementById('hs-radius-val');\n    const bgBtn      = document.getElementById('hs-bg-btn');\n\n    if(exportBtn)   exportBtn.disabled   = !dw;\n    if(radiusInput) radiusInput.disabled = !dw;\n    if(bgBtn)       bgBtn.disabled       = !dw;\n\n    if(!dw){ el.innerHTML = '<p class=\"muted\">Nessun disegno disponibile<\/p>'; return; }\n    const svgText = await fetch(defPath + dw.svg_url).then(r=>r.text()).catch(()=>null);\n    if(!svgText){ el.innerHTML = '<p class=\"muted\">Disegno non disponibile<\/p>'; return; }\n\n    el.innerHTML = `<div class=\"dwg-wrap\"><div class=\"dwg-inline\">${svgText}<\/div><\/div>`;\n    const svgRoot = el.querySelector('.dwg-inline svg');\n    if(!svgRoot) return;\n    svgRoot.setAttribute('pointer-events','all');\n\n    const vbBase = getViewBox(svgRoot);\n\n    if(dw.bg_image_dataurl){ applyBackgroundImage(svgRoot, dw.bg_image_dataurl, vbBase); }\n\n    svgRoot.querySelectorAll('.hotspot').forEach(g=>{\n      const circle = g.querySelector('circle');\n      const text   = g.querySelector('text');\n      if(circle && text){\n        const cx = parseFloat(circle.getAttribute('cx')||'0');\n        const cy = parseFloat(circle.getAttribute('cy')||'0');\n        text.setAttribute('x', cx);\n        text.setAttribute('y', cy);\n        text.setAttribute('dominant-baseline','middle');\n        text.setAttribute('text-anchor','middle');\n      }\n    });\n\n    (dw.hotspots||[]).forEach(h=>{\n      if(h.x_pct!=null && h.y_pct!=null){\n        const g = svgRoot.querySelector(`[data-label=\"${h.label}\"]`);\n        if(g){\n          const cx = (vbBase.width * h.x_pct);\n          const cy = (vbBase.height * h.y_pct);\n          const circle = g.querySelector('circle');\n          const text = g.querySelector('text');\n          if(circle){ circle.setAttribute('cx', cx); circle.setAttribute('cy', cy); }\n          if(text){\n            text.setAttribute('x', cx);\n            text.setAttribute('y', cy);\n            text.setAttribute('dominant-baseline','middle');\n            text.setAttribute('text-anchor','middle');\n          }\n        }\n      }\n    });\n\n    const radiusPx = Math.max(8, Math.min(48, parseInt(dw.hotspot_radius_px || 18, 10)));\n    applyHotspotRadius(svgRoot, radiusPx);\n    if(radiusInput){\n      radiusInput.value = radiusPx;\n      if(radiusVal) radiusVal.textContent = `${radiusPx} px`;\n    }\n\n    \/\/ click su hotspot\nsvgRoot.addEventListener('click', (e)=>{\n  if(editing) return;\n  const g = e.target.closest('[data-label]');\n  if(!g) return;\n\n  const label = g.getAttribute('data-label');\n  const hotspot = (dw.hotspots||[]).find(x=>String(x.label)===String(label));\n  if(!hotspot) return;\n\n  \/\/ helper locali per trovare il nodo e raccogliere le parti\n  function findNodeByTitle(n, title){\n    if(n.title === title) return n;\n    for(const ch of (n.children||[])){\n      const out = findNodeByTitle(ch, title);\n      if(out) return out;\n    }\n    return null;\n  }\n  function collectCompIds(node){\n    const out = new Set();\n    (function walk(n){\n      (n.component_ids || []).forEach(id => out.add(id));\n      (n.children || []).forEach(walk);\n    })(node);\n    return Array.from(out);\n  }\n\n  const isRoot = (dw.title && dw.title.endsWith('(Generale)'));\n\n  \/\/ \u2705 Caso richiesto: sono sull\u2019assieme e clicco un hotspot -> apri tavola + aggiorna lista\n  if(isRoot && hotspot.target_drawing_title){\n    renderDrawing(hotspot.target_drawing_title);\n\n    const node = findNodeByTitle(tree.root, hotspot.target_drawing_title);\n    if(node){\n      const ids   = collectCompIds(node);\n      const items = ids.map(id => components.components.find(c=>c.id===id)).filter(Boolean);\n      renderList(items);\n    } else if (Array.isArray(hotspot.component_ids)) {\n      \/\/ fallback: se non trovo il nodo, mostra almeno i componenti collegati all'hotspot\n      const comps = hotspot.component_ids\n        .map(id => components.components.find(c=>c.id===id))\n        .filter(Boolean);\n      renderList(comps);\n    }\n    return;\n  }\n\n  \/\/ Comportamento standard (non-root): mostra i componenti dell\u2019hotspot\n  if(Array.isArray(hotspot.component_ids)){\n    const comps = hotspot.component_ids\n      .map(id => components.components.find(c=>c.id===id))\n      .filter(Boolean);\n    renderList(comps);\n  }\n}, false);\n    if(editing){\n      svgRoot.classList.add('editing');\n      let dragging = null;\n      svgRoot.addEventListener('mousedown', (e)=>{ const g = e.target.closest('[data-label]'); if(!g) return; dragging = g; });\n      svgRoot.addEventListener('mousemove', (e)=>{\n        if(!dragging) return;\n        const pt = svgRoot.createSVGPoint(); pt.x = e.clientX; pt.y = e.clientY;\n        const ctm = svgRoot.getScreenCTM && svgRoot.getScreenCTM(); if(!ctm) return;\n        let inv = null;\n        if(ctm.inverse){ inv = ctm.inverse(); }\n        else if (svgRoot.getCTM && svgRoot.getCTM().inverse){ inv = svgRoot.getCTM().inverse(); }\n        else { return; }\n        const sp = pt.matrixTransform(inv);\n        const cx = Math.max(0, Math.min(vbBase.width, sp.x));\n        const cy = Math.max(0, Math.min(vbBase.height, sp.y));\n        const circle = dragging.querySelector('circle');\n        const text = dragging.querySelector('text');\n        if(circle){ circle.setAttribute('cx', cx); circle.setAttribute('cy', cy); }\n        if(text){ text.setAttribute('x', cx); text.setAttribute('y', cy); }\n      }, { passive: true });\n      window.addEventListener('mouseup', ()=>{\n        if(!dragging) return;\n        const label = dragging.getAttribute('data-label');\n        const circle = dragging.querySelector('circle');\n        const cx = parseFloat(circle.getAttribute('cx'));\n        const cy = parseFloat(circle.getAttribute('cy'));\n        const hx = +(cx \/ vbBase.width).toFixed(6);\n        const hy = +(cy \/ vbBase.height).toFixed(6);\n        if(currentDrawing){\n          const hot = currentDrawing.hotspots.find(h=>String(h.label)===String(label));\n          if(hot){ hot.x_pct = hx; hot.y_pct = hy; }\n        }\n        dragging = null;\n      });\n    }\n\n    const radiusInputEl = document.getElementById('hs-radius');\n    const radiusValEl   = document.getElementById('hs-radius-val');\n    if(radiusInputEl){\n      radiusInputEl.oninput = (e)=>{\n        const val = parseInt(e.target.value, 10);\n        if(currentDrawing){\n          currentDrawing.hotspot_radius_px = val;\n          applyHotspotRadius(svgRoot, val);\n          if(radiusValEl) radiusValEl.textContent = `${val} px`;\n        }\n      };\n    }\n\n    const bgBtnEl  = document.getElementById('hs-bg-btn');\n    const bgFileEl = document.getElementById('hs-bg-file');\n    const bgNoteEl = document.getElementById('hs-bg-note');\n    if(bgBtnEl && bgFileEl){\n      bgBtnEl.onclick = ()=> bgFileEl.click();\n      bgFileEl.onchange = async ()=>{\n        const file = bgFileEl.files && bgFileEl.files[0];\n        if(!file) return;\n        const dataUrl = await makeSquareDataURL(file);\n        if(currentDrawing){\n          currentDrawing.bg_image_dataurl = dataUrl;\n          applyBackgroundImage(svgRoot, dataUrl, vbBase);\n          if(bgNoteEl) bgNoteEl.textContent = 'Sfondo impostato (quadrato)';\n        }\n        bgFileEl.value = '';\n      };\n    }\n  }\n\n  function openLightbox(src, caption){\n    const lb = document.getElementById('lightbox');\n    const img = document.getElementById('lb-img');\n    const cap = document.getElementById('lb-caption');\n    img.src = src; cap.textContent = caption||'';\n    lb.hidden = false;\n    function close(){ lb.hidden = true; img.src=''; cap.textContent=''; document.removeEventListener('keydown', onKey); }\n    document.querySelector('.lb-close').onclick = close;\n    document.querySelector('.lightbox-backdrop').onclick = close;\n    function onKey(e){ if(e.key==='Escape') close(); }\n    document.addEventListener('keydown', onKey);\n  }\n\nfunction renderList(items){\n  const el = document.getElementById('list');\n  if(!items || !items.length){\n    el.innerHTML = '<p class=\"muted\">Nessun componente<\/p>';\n    return;\n  }\n\n  el.innerHTML = items.map(c=>`\n    <div class=\"list-item\">\n      <!-- Colonna POS -->\n      <div class=\"pos\">\n        <div class=\"pos-label\">Pos.<\/div>\n        <div class=\"pos-num\">${c.callout || '-'}<\/div>\n      <\/div>\n\n      <!-- Colonna contenuti -->\n      <div class=\"meta\">\n        <div class=\"code-badge\">${c.code || '-'}<\/div>\n        <div class=\"title\">\n          <strong>${c.description || ''}<\/strong>\n          ${(c.media && c.media.length)\n            ? `<button class=\"img-btn\" data-img=\"${(c.media[0].href||c.media[0].url)}\" data-title=\"${(c.code||'')} \u2014 ${(c.description||'')}\">\ud83d\udcf7<\/button>`\n            : \"\"\n          }\n        <\/div>\n        <div class=\"group muted\">${c.group || ''}<\/div>\n      <\/div>\n\n      <!-- Colonna quantit\u00e0\/azione -->\n      <div class=\"qty\">\n        <input type=\"number\" min=\"1\" value=\"1\" data-id=\"${c.id}\">\n        <button data-add=\"${c.id}\" class=\"btn-ghost\">Aggiungi<\/button>\n      <\/div>\n    <\/div>\n  `).join('');\n\n  \/\/ interazioni\n  el.onclick = (e)=>{\n    const imgBtn = e.target.closest('.img-btn');\n    if(imgBtn){ openLightbox(imgBtn.dataset.img, imgBtn.dataset.title); return; }\n    const add = e.target.closest('button[data-add]');\n    if(add){\n      const id = add.dataset.add;\n      const qty = parseInt(el.querySelector(`input[data-id=\"${id}\"]`)?.value||'1',10);\n      cart.add(id, qty);\n    }\n  };\n}\n  function renderCart(){\n    const el = $('#cart');\n    if(!el) return;\n    if(!cart.lines.length){ el.innerHTML = '<p class=\"muted\">Carrello vuoto<\/p>'; return; }\n    el.innerHTML = cart.lines.map(l=>{\n      const c = byId(l.id);\n      return `<div class=\"cart-line\">\n        <div>${c?.code||l.id} \u2014 ${c?.description||''}<\/div>\n        <input type=\"number\" min=\"1\" value=\"${l.qty}\" data-cid=\"${l.id}\">\n        <button data-rm=\"${l.id}\">Rimuovi<\/button>\n      <\/div>`;\n    }).join('') + `<div class=\"total\"><span>Totale righe<\/span><span>${cart.lines.length}<\/span><\/div>`;\n    el.onclick = (e)=>{ const rm=e.target.closest('[data-rm]'); if(rm) cart.remove(rm.dataset.rm); };\n    el.oninput = (e)=>{ const inp=e.target.closest('input[data-cid]'); if(inp) cart.update(inp.dataset.cid, parseInt(inp.value||'1',10)); };\n  }\n\n  function search(term){\n    term = (term||'').trim().toLowerCase();\n    if(!term) return [];\n    const entries = index.entries.filter(e =>\n      e.code.toLowerCase().includes(term) ||\n      e.description.toLowerCase().includes(term) ||\n      e.path.toLowerCase().includes(term) ||\n      new RegExp(`(^|\\\\\\\\b)${term}(\\\\\\\\b|\\\\.)`).test(e.path.toLowerCase())\n    ).slice(0,30);\n    return entries.map(en => byId(en.id)).filter(Boolean);\n  }\n\/\/ --- Ricerca con suggerimenti + navigazione tastiera ---\nconst q = document.querySelector('#q');\nconst sug = document.querySelector('#suggestions');\n\nlet activeIndex = -1; \/\/ elemento selezionato\n\nfunction updateActiveSuggestion() {\n  const items = sug.querySelectorAll('.sug');\n  items.forEach((el, i) => {\n    el.classList.toggle('active', i === activeIndex);\n    if (i === activeIndex) el.scrollIntoView({ block: 'nearest' });\n  });\n}\n\nfunction selectSuggestion(index) {\n  const items = sug.querySelectorAll('.sug');\n  const el = items[index];\n  if (!el) return;\n  const id = el.dataset.id;\n  const c = byId(id);\n  if (c) renderList([c]);\n  sug.style.display = 'none';\n  activeIndex = -1;\n}\n\nq?.addEventListener('input', () => {\n  const term = q.value.trim();\n  const items = search(term);\n  if (!term || !items.length) {\n    sug.style.display = 'none';\n    return;\n  }\n  sug.style.display = 'block';\n  sug.innerHTML = items\n    .slice(0, 12)\n    .map(\n      (c) => `\n        <div class=\"sug\" data-id=\"${c.id}\">\n          <b>${c.code || '-'}<\/b> \u2013 Pos. ${c.callout || '-'} \u2014 ${c.description || ''}\n          ${(c.media && c.media.length) ? '\ud83d\udcf7' : ''}\n        <\/div>`\n    )\n    .join('');\n  activeIndex = -1;\n});\n\nq?.addEventListener('keydown', (e) => {\n  const items = sug.querySelectorAll('.sug');\n  if (!items.length || sug.style.display === 'none') return;\n\n  switch (e.key) {\n    case 'ArrowDown':\n      e.preventDefault();\n      activeIndex = (activeIndex + 1) % items.length;\n      updateActiveSuggestion();\n      break;\n    case 'ArrowUp':\n      e.preventDefault();\n      activeIndex = (activeIndex - 1 + items.length) % items.length;\n      updateActiveSuggestion();\n      break;\n    case 'Enter':\n      e.preventDefault();\n      if (activeIndex >= 0) selectSuggestion(activeIndex);\n      break;\n    case 'Escape':\n      sug.style.display = 'none';\n      activeIndex = -1;\n      break;\n  }\n});\n\nsug?.addEventListener('click', (e) => {\n  const it = e.target.closest('[data-id]');\n  if (!it) return;\n  const id = it.dataset.id;\n  const c = byId(id);\n  renderList([c]);\n  sug.style.display = 'none';\n  activeIndex = -1;\n});\n\ndocument.addEventListener('click', (e) => {\n  if (sug && !sug.contains(e.target) && e.target !== q) {\n    sug.style.display = 'none';\n    activeIndex = -1;\n  }\n});\n  const editBtn   = document.getElementById('hs-edit-toggle');\n  const exportBtn = document.getElementById('hs-export');\n\n  editBtn?.addEventListener('click', ()=>{\n    editing = !editing;\n    document.body.classList.toggle('hs-editing', editing);\n    editBtn.textContent = editing ? 'Fine modifica' : 'Modifica hotspot';\n    if(currentDrawing){\n      const title = currentDrawing.title && currentDrawing.title.endsWith('(Generale)') ? '__ROOT__' : currentDrawing.title;\n      renderDrawing(title);\n    }\n    if(exportBtn) exportBtn.disabled = !currentDrawing;\n    const radiusInput = document.getElementById('hs-radius');\n    if(radiusInput) radiusInput.disabled = !currentDrawing;\n    const bgBtn = document.getElementById('hs-bg-btn');\n    if(bgBtn) bgBtn.disabled = !currentDrawing;\n  });\n\n  exportBtn?.addEventListener('click', ()=>{\n    const payload = JSON.stringify(drawings, null, 2);\n    const blob = new Blob([payload], {type:'application\/json'});\n    const a = document.createElement('a');\n    a.href = URL.createObjectURL(blob);\n    a.download = 'drawings_updated.json';\n    a.click();\n    setTimeout(()=>URL.revokeObjectURL(a.href),0);\n  });\n\n  document.querySelector('#checkout')?.addEventListener('click', ()=>{\n    const payload = {\n      product_id:'mercury5',\n      lines: cart.lines.map(l=>{\n        const c=byId(l.id);\n        return { id:l.id, code:c?.code||'', callout:c?.callout||'', description:c?.description||'', qty:l.qty };\n      })\n    };\n    const blob = new Blob([JSON.stringify(payload,null,2)],{type:'application\/json'});\n    const a = document.createElement('a');\n    a.href = URL.createObjectURL(blob);\n    a.download = 'ordine-mercury5.json';\n    a.click();\n    setTimeout(()=>URL.revokeObjectURL(a.href), 0);\n  });\n\n\/\/ --- Toggle pannello strumenti (parte nascosto) ---\nconst toolsToggleBtn = document.getElementById('hs-tools-toggle');\nconst toolsPanel     = document.getElementById('hs-tools');\n\nfunction setToolsOpen(open){\n  if(!toolsPanel || !toolsToggleBtn) return;\n  if(open){\n    toolsPanel.hidden = false;\n    toolsToggleBtn.setAttribute('aria-expanded','true');\n  }else{\n    toolsPanel.hidden = true;\n    toolsToggleBtn.setAttribute('aria-expanded','false');\n    \/\/ Se sto chiudendo gli strumenti ed ero in modalit\u00e0 editing, disattivo l'editing per sicurezza\n    if(typeof editing !== 'undefined' && editing){\n      const editBtn = document.getElementById('hs-edit-toggle');\n      editing = false;\n      document.body.classList.toggle('hs-editing', false);\n      if(editBtn) editBtn.textContent = 'Modifica hotspot';\n      \/\/ re-render per togliere classi\/handler da editing\n      if(currentDrawing){\n        const title = currentDrawing.title && currentDrawing.title.endsWith('(Generale)') ? '__ROOT__' : currentDrawing.title;\n        renderDrawing(title);\n      }\n    }\n  }\n}\n\ntoolsToggleBtn?.addEventListener('click', ()=>{\n  const open = toolsPanel?.hidden ?? true;\n  setToolsOpen(open);\n});\n\n\n  renderTree();\n  renderDrawing('__ROOT__');\n  renderList([]);\n  renderCart();\n  updateCartBadge();\n})();\n<\/script><\/div><\/div>\n<\/div>\n<\/div>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_acf_changed":false,"footnotes":""},"class_list":["post-32330","page","type-page","status-publish","hentry"],"acf":[],"dsm_author":{"name":"admeb26","avatar_url":"https:\/\/www.meber.it\/wp-content\/plugins\/ultimate-member\/assets\/img\/default_avatar.jpg","archive_link":"https:\/\/www.meber.it\/en\/author\/admeb26\/","biodata":""},"dsm_categories":[],"dsm_attachment_categories":[],"dsm_featured_image":null,"dsm_comment_count":0,"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.9 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Spare Part Template - Me.Ber.<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.meber.it\/en\/spare-part-template\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Spare Part Template - Me.Ber.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.meber.it\/en\/spare-part-template\/\" \/>\n<meta property=\"og:site_name\" content=\"Me.Ber.\" \/>\n<meta property=\"article:modified_time\" content=\"2026-05-07T13:50:43+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.meber.it\\\/en\\\/spare-part-template\\\/\",\"url\":\"https:\\\/\\\/www.meber.it\\\/en\\\/spare-part-template\\\/\",\"name\":\"Spare Part Template - Me.Ber.\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.meber.it\\\/en\\\/#website\"},\"datePublished\":\"2026-02-10T15:37:58+00:00\",\"dateModified\":\"2026-05-07T13:50:43+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.meber.it\\\/en\\\/spare-part-template\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.meber.it\\\/en\\\/spare-part-template\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.meber.it\\\/en\\\/spare-part-template\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.meber.it\\\/en\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Spare Part Template\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.meber.it\\\/en\\\/#website\",\"url\":\"https:\\\/\\\/www.meber.it\\\/en\\\/\",\"name\":\"Me.Ber.\",\"description\":\"In Case of Emergerncy\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.meber.it\\\/en\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Spare Part Template - Me.Ber.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.meber.it\/en\/spare-part-template\/","og_locale":"en_US","og_type":"article","og_title":"Spare Part Template - Me.Ber.","og_url":"https:\/\/www.meber.it\/en\/spare-part-template\/","og_site_name":"Me.Ber.","article_modified_time":"2026-05-07T13:50:43+00:00","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.meber.it\/en\/spare-part-template\/","url":"https:\/\/www.meber.it\/en\/spare-part-template\/","name":"Spare Part Template - Me.Ber.","isPartOf":{"@id":"https:\/\/www.meber.it\/en\/#website"},"datePublished":"2026-02-10T15:37:58+00:00","dateModified":"2026-05-07T13:50:43+00:00","breadcrumb":{"@id":"https:\/\/www.meber.it\/en\/spare-part-template\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.meber.it\/en\/spare-part-template\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.meber.it\/en\/spare-part-template\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.meber.it\/en\/"},{"@type":"ListItem","position":2,"name":"Spare Part Template"}]},{"@type":"WebSite","@id":"https:\/\/www.meber.it\/en\/#website","url":"https:\/\/www.meber.it\/en\/","name":"Me.Ber.","description":"In Case of Emergerncy","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.meber.it\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"}]}},"_links":{"self":[{"href":"https:\/\/www.meber.it\/en\/wp-json\/wp\/v2\/pages\/32330","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.meber.it\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.meber.it\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.meber.it\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.meber.it\/en\/wp-json\/wp\/v2\/comments?post=32330"}],"version-history":[{"count":1,"href":"https:\/\/www.meber.it\/en\/wp-json\/wp\/v2\/pages\/32330\/revisions"}],"predecessor-version":[{"id":32331,"href":"https:\/\/www.meber.it\/en\/wp-json\/wp\/v2\/pages\/32330\/revisions\/32331"}],"wp:attachment":[{"href":"https:\/\/www.meber.it\/en\/wp-json\/wp\/v2\/media?parent=32330"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}