Ctrl K
ComunidadGuías…

netmapper.html

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
<!DOCTYPE html>
<html lang="it">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>InfraNet Pro</title>
    <!-- Icona della scheda: e' il GLIFO Font Awesome `fa-network-wired` (U+F6FF),
         lo stesso dell'intestazione qui sotto, reso col font vendorizzato su canvas
         e catturato in PNG (64px) — non un disegno rifatto a mano, che divergerebbe
         dal marchio al primo ritocco. Sta IN LINEA e non come file perche' le rotte
         statiche sono una lista bianca esplicita (niente cartella servita a tappeto)
         e la pagina di login la vede chi non ha ancora una sessione: un data-URI
         evita sia una rotta pubblica in piu' sia una richiesta di rete, e funziona
         offline. Senza, il browser chiedeva /favicon.ico e prendeva 404 a ogni
         apertura. Per rigenerarla: stesso glifo, stessi colori (--accent su --bg). -->
    <link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAFmElEQVR4AexaTYgbZRh+v0l2t5u0qXXXrYpSWhAqHhQUBNGDrOipVrEeBFEr9uClCC1S7UE8VIt0QXrxULEqQg+u+IcHpYsHRRA86MmCsKUIatdda9NNttlN5uvzDEmab5KZzAwzITuz4fs2877f+z7v+zyZSeZnLfF/WYUtE3uLWydPjJcmzxZKk/OYy5h6SCd7m2ev7Jm9g54vx56LmzdvnxoHaZC8JEp9obUcUiLTANuJWcQc1sHedrJX9szeyYFcyKlX010CFLdMPG9bjT8USCOhhLneR4lcyInc3GQMAYpbJ45opT5EUBqIg4YxSuRGjp3etgBUR2v1dudiGrfJkVxb3BwBeHxAnZMtZ9rfyZWcydMRoJFrvAojjbs9aPUcpSZnoQAWviQO9AxLsbPJ2eLv/B7wzNKnD7rOKOE8YY+lLPWQY2bwD7lbtpZ7JKMvcreUyK6M8hdy55fgVFYFAO8pCsDzZ2zHPxqPPCkrs79I9ceLUv3p33ATOcwlRvydtRGLFKBtxblR3/uc1I6eFH3rDhErQhnkMJcYxIqzt06sCJ11pvfe1qVtUn/6JZGxTb0DwniBQSxihknzinX7kxHglttFT97srhXZJpYGZmQAn8REBPCpN3RLAxNAlS/Jpv3TUnjgJt/JGMYOSqmBCTAoQmHrbAgQVrG0xW/sAYP6RPk7fvX0XN+zQcYwdlB9bewBSSit/v5T1OI/sUETi5ixAXYAJbIH8Hc8/+n7IrWrHaUibgKDWMSMiOCblogArJj/8mMZO3ZQ1F8XRGybrnATOcwlBrHCJQePTkwAtpA7+7mM77tPCg9uN87+8t/OctmY9BlnichhLjGMwJiNRAWIuddE4PwFGBmV+qNPydqBI7FO+zY+YzX50Bd3HfYu4GBWMi1vAXBDYvX1d2X1jfdkbf+hWKd9171mF7Doi7sOeycHvxsyngLYu+6Uxv14Iq4U2lunA72TA7l4MfAUwCshbX5PAaz53yX385yI1uuXM3onB3LxIuEpAH+7R996RUbffFlGTs94zvwsTnhWqiY+bPr98sKsEUuAaRSBTb8fDnsnB3IxcjsMbwEYtLYq+e8+k5FTxz1n/pszotZqjG5P2vT75YVZIxYx2wWwQZt+Pxz2LuCAcM/hL4BnWnoWAgugb7hRasc+kOrcBeOSttflKy9n6TcehiCP+cTxko9rjEmyhrt2MAHGi1I7cUYaD+NJ+njBjRHMRh7ziSPA60qCj2uMEcR2rQdxII/5xBHgBUkJJED98WfF3n13ELy+McQhnjuQPq65/VFs4hAvSG4gAezdeIJu5YLg9Y8BjoPninR8WHO5o5nAcfACZAcSIADOug2JLEDX5avHAw/GRVWHucYlcgI1IgsQldSw5W0IMGyfyKD7Sd0eEFbAyALUH9tnnBEaZ30d/w7DuLBNteKZ64Xb6WdcKyfse2QBwhYa1vhAAljnfhWxG/FwAI6D50JzfFhzuaOZwHHwAmRTgEq/uPxXn4h17rd+YYHWiUM8dzB9XHP7o9jEIV6A3AoFWOgbuFKRscPPSO77r6XrxkTf5GYAbmAwnzgCvKb3+ht8XGNMYjWuV2ttLVi44TXfsvze1f//ydjRF6UwvcN4yBHkTM2JQR7zieNVh2uMSbJGZ21ytywlOMAlky9yt7Stf8gke5Amd6t6ZQkHtpRhZ22UyZ1fgrZWcipr7JucbQoguUbuHQiQpb2g3OQsjgDLyxcXlNYHIUImBrmSM8k6AnCjcmXpI6X0a9xO8yRHcm1xbAtAR+Xy0nGo8wK203g4lMmNHMGvPQwB6KU6lp27A18SM7DTIESZXMiJ3MDJGF0CcJXHx8rlxcPV8uI2PBx9Ak+ZZ3DWhCelch7rfa8dEDOw4SrE3s6zV/bM3smBXMjJFeuY1wAAAP//5EIQ5AAAAAZJREFUAwBH4IGcwmKtLwAAAABJRU5ErkJggg==">
    <link rel="stylesheet" href="/vendor/fontawesome/css/all.min.css">
    <!-- CSS modularizzato (ex style.css monolitico). ORDINE = cascata: i token
         per primi, poi base→layout→componenti→tema. Mappa moduli + token: styles/README.md -->
    <link rel="stylesheet" href="/styles/01-tokens.css">
    <link rel="stylesheet" href="/styles/02-base.css">
    <link rel="stylesheet" href="/styles/03-layout.css">
    <link rel="stylesheet" href="/styles/04-floor-rack.css">
    <link rel="stylesheet" href="/styles/05-cables-wifi.css">
    <link rel="stylesheet" href="/styles/06-panels.css">
    <link rel="stylesheet" href="/styles/07-modals.css">
    <link rel="stylesheet" href="/styles/08-topology.css">
    <link rel="stylesheet" href="/styles/09-user-theme.css">
    <link rel="stylesheet" href="/styles/10-modern.css">
    <link rel="stylesheet" href="/styles/11-overview.css">
    <link rel="stylesheet" href="/styles/12-touch.css">
</head>

<body>

<header>
    <div class="header-title"><i class="fas fa-network-wired"></i> <span>Infra<span style="color:var(--accent)">Net</span> Pro</span></div>
    <div class="header-sep"></div>

    <!-- Project management -->
    <div class="project-bar">
        <select id="project-select" data-change="project-select" data-i18n-tip="project.tipCurrent" data-tip="Progetto corrente"></select>
        <button class="toolbar-btn" data-act="project-new"       data-i18n-tip="project.tipNew"       data-tip="Nuovo progetto"><i class="fas fa-plus"></i></button>
        <button class="toolbar-btn" data-act="project-rename"    data-i18n-tip="project.tipRename"    data-tip="Rinomina"><i class="fas fa-pen"></i></button>
        <button class="toolbar-btn" data-act="project-duplicate" data-i18n-tip="project.tipDuplicate" data-tip="Duplica"><i class="fas fa-copy"></i></button>
        <button class="toolbar-btn danger-quiet" data-act="project-delete" data-i18n-tip="project.tipDelete" data-tip="Elimina progetto"><i class="fas fa-trash"></i></button>
    </div>
    <!-- «Salva» sta QUI, attaccato alla barra del progetto: salva il progetto che il
         selettore lì accanto ha aperto, e i due gesti si leggono insieme. Prima stava
         tre gruppi più in là, dopo «Esporta», dove nessuno lo cercava. Spostato su
         richiesta dell'utente il 30/08.
         ⚠️ L'ordine di questi bottoni è anche l'ordine di TABULAZIONE: qui «Salva»
         arriva prima di Multisito ed Esporta, che è la priorità voluta anche da
         tastiera. Nessuna regola CSS dipende dalla posizione (niente selettori di
         fratello adiacente su questi id): a spostarlo si sposta e basta. -->
    <button class="toolbar-btn" id="btn-save" data-act="project-save" data-i18n-tip="save.tip" data-tip="Salva (Ctrl+S)">
        <i id="save-icon" class="fas fa-floppy-disk"></i><span id="save-label" data-i18n="save.label"> Salva </span>
    </button>
    <div class="header-sep"></div>
    <!-- Il layer SOPRA il progetto: un progetto = una sede, questo = come le sedi
         si parlano. Fra lui e il selettore di progetto ora c'è «Salva», ma resta
         di casa lì: è la stessa domanda a una scala più grande («dove sono?»),
         non un'esportazione. -->
    <button class="toolbar-btn" id="btn-org" data-act="org-open" data-i18n-tip="org.tip" data-tip="Sedi, uplink WAN e collegamenti fra le sedi"><i class="fas fa-sitemap"></i><span class="btn-label" data-i18n="org.btnLabel"> Multisito</span></button>
    <div id="impexp-menu-wrap" style="position:relative">
        <button class="toolbar-btn" id="btn-impexp" data-act="impexp-toggle" data-i18n-tip="impexp.tip" data-tip="Importa / Esporta">
            <i class="fas fa-right-left"></i><span class="btn-label" data-i18n="impexp.btnLabel"> Esporta</span><i class="fas fa-chevron-down" style="font-size:0.6rem;margin-left:4px;opacity:.6"></i>
        </button>
        <div id="impexp-dropdown" class="header-menu-dropdown" style="display:none">
            <button class="header-menu-item" data-act="impexp-import-json">
                <i class="fas fa-file-import"></i> <span data-i18n="impexp.importJson">Importa progetto JSON</span>
            </button>
            <button class="header-menu-item" data-act="impexp-import-csv">
                <i class="fas fa-file-csv"></i> <span data-i18n="impexp.importCsv">Importa dispositivi da CSV</span>
            </button>
            <div class="header-menu-divider"></div>
            <button class="header-menu-item" onclick="exportJSON();closeImpExpMenu()">
                <i class="fas fa-file-export"></i> <span data-i18n="impexp.exportJson">Esporta progetto JSON</span>
            </button>
            <button class="header-menu-item" id="btn-export-pdf" onclick="openPdfExportOptions();closeImpExpMenu()">
                <i class="fas fa-file-pdf"></i> <span data-i18n="impexp.exportPdf">Esporta PDF</span>
            </button>
            <button class="header-menu-item" onclick="openLabelExportOptions();closeImpExpMenu()">
                <i class="fas fa-tags"></i> <span data-i18n="impexp.labels">Esporta etichette…</span>
            </button>
            <!-- ASSE B-safe: nessun onclick inline (gonfierebbe il cricchetto handler-inline);
                 il click e' agganciato in export.js via addEventListener su #btn-export-drawio. -->
            <button class="header-menu-item" id="btn-export-drawio">
                <i class="fas fa-project-diagram"></i> <span data-i18n="impexp.exportDrawio">Esporta per draw.io</span>
            </button>
            <div class="header-menu-divider"></div>
            <button class="header-menu-item" data-act="dcim-open">
                <i class="fas fa-rotate"></i> <span data-i18n="integrations.menuItem">Sincronizzazione DCIM/IPAM…</span>
            </button>
        </div>
    </div>

    <!-- Undo / Redo -->
    <button class="toolbar-btn" id="btn-undo" data-act="undo" data-i18n-tip="undo.tip" data-tip="Annulla (Ctrl+Z)" disabled><i class="fas fa-rotate-left"></i></button>
    <button class="toolbar-btn" id="btn-redo" data-act="redo" data-i18n-tip="redo.tip" data-tip="Ripeti (Ctrl+Y)"  disabled><i class="fas fa-rotate-right"></i></button>
    <!-- NIENTE `margin-left:auto` qui (né sul gemello `.header-right`): i due margini auto
         accumulavano lo spazio libero in DUE vuoti simmetrici attorno a una ricerca a
         larghezza fissa. Ora lo spazio lo assorbe la RICERCA (`flex-grow`, sotto), che
         riempie il centro e spinge il cluster destro al bordo — niente vuoti. -->

    <!-- Search + chip freschezza. La ricerca ha PRIORITÀ DI ESPANSIONE (flex-grow):
         cresce a riempire lo spazio fra il gruppo modifica e il cluster destro. I
         divisori `|` la abbracciano (6px); il basis (media query) resta l'ammortizzatore
         che decide il wrap, il grow riempie il resto. -->
    <div class="search-panel">
        <div class="search-input-wrap">
            <i class="fas fa-search"></i>
            <input id="global-search" type="search" data-i18n-ph="search.ph" placeholder="Cerca dispositivi, porte, link…" autocomplete="off"
                   data-input="global-search" data-focus="global-search" data-keydown="global-search">
            <button id="global-search-clear" data-i18n-tip="search.clearTip" data-tip="Cancella" data-act="search-clear"><i class="fas fa-times"></i></button>
        </div>
        <div id="search-results"></div>
    </div>
    <!-- ZONA DI STATO — una sola, e sta QUI per una ragione MECCANICA, non estetica.
         I quattro chip (freschezza · SNMPv3 da configurare · porte libere · auto-poll)
         rispondono tutti alla stessa domanda — «come sta il progetto adesso?» — e prima
         vivevano in TRE punti diversi della barra: chi guardava non aveva un posto dove
         guardare. Riuniti, il posto è uno.
         ⚠️ Il posto è SUBITO DOPO la ricerca, e non in fondo al cluster destro, per una
         ragione MISURATA: la ricerca è l'unico elemento con `flex-grow`, quindi quando un
         chip compare è LEI a cedere la larghezza, e i bottoni alla sua destra restano dove
         sono. Misurato a 1920px accendendo i quattro chip insieme — Scopri, Verifica,
         Storia, Automazioni e l'account si spostano di ZERO px; prima (coi due badge in
         fondo al cluster destro) si spostavano di 267, 210, 210 px. Dashboard è l'unica che
         cambia posizione, ma perché il fitter le collassa l'ETICHETTA: non è un chip che la
         spinge, è la scala di cedimento che fa il suo mestiere.
         ⚠️ NIENTE larghezza riservata: riservare lo spazio dei chip spenti significa
         tenere l'header sempre alla sua larghezza PEGGIORE, e il fitter
         (src/app-header-fit.js) spegnerebbe le etichette SEMPRE invece che solo coi
         badge accesi. La riserva risolverebbe lo stesso difetto pagandolo in mutismo.
         `display:contents` (09-user-theme.css) evita il gap doppio quando sono tutti
         spenti; i chip nascono `display:none` e li accende il JS uno per volta. -->
    <div id="status-cluster" class="status-cluster">
        <!-- Freschezza del progetto (ex badge dentro il bottone Sync, ritirato): lo
        alimenta _renderSyncFreshness in app-snmp.js. Clic → apre la Dashboard. -->
        <button class="status-chip sync-fresh-chip" id="sync-fresh-chip" data-act="overview-toggle" style="display:none" data-i18n-tip="sync.freshChipTip" data-tip="Freschezza dati del progetto · clic per la Dashboard"></button>
        <!-- SNMPv3 «da configurare»: è un avviso PRE-Verifica. Larghezza fissa (min-width
        in 09-user-theme.css) perché 1, 12 o 128 device occupino lo stesso spazio; a
        zero device esce dal flusso (aria-hidden) e non lascia cuscinetto. -->
        <button class="status-chip s-warn" id="v3-pending-chip" data-act="v3-jump-next" data-tip="Device SNMPv3 da configurare — clic per saltare al prossimo"></button>
        <button class="status-chip s-info" id="spare-active-badge" data-act="spare-highlight-off" data-i18n-tip="spare.badgeTip" data-tip="Porte libere evidenziate nel rack — clicca per spegnere">
            <i class="fas fa-plug-circle-plus"></i><span data-i18n="report.spare">Porte libere</span><i class="fas fa-times chip-x"></i>
        </button>
        <button class="status-chip s-ok" id="autopoll-badge" data-act="automation-menu-toggle" data-i18n-tip="autopoll.tip" data-tip="Polling automatico attivo — clic per le automazioni"></button>
        <!-- Il badge del filtro VLAN vive ora nella sotto-header (#modern-subbar,
            src/app-subbar.js: _vlanFilterEl), centrato tra breadcrumb e statistiche. -->
    </div>
    <!-- `|` che chiude la zona «ricerca + stato» e apre il cluster delle azioni. È uno
         dei CINQUE divisori rimasti (erano 13): la struttura la fa lo spazio, la linea
         separa i DOMINI. -->
    <div class="header-sep"></div>

    <!-- Actions — allineati a destra -->
<!-- Tooltip topologia (compare vicino al cursore al hover sulle linee) -->
<div id="topo-tip" onmouseenter="clearTimeout(_topoTipTimer)" onmouseleave="_hideTopoTip()"></div>

<input type="file" id="map-upload"  accept="image/*,.svg" data-change="map-upload">
    <input type="file" id="json-upload" accept=".json"        data-change="json-upload">
    <div class="header-right">
        <!-- Vista di sintesi. E' un COMMUTATORE DI VISTA come Topologia, non un
             report: apre #overview al posto della planimetria, non sporca il
             documento e la scelta vive in localStorage (vedi app-overview.js). -->
        <button class="toolbar-btn" id="btn-overview" data-act="overview-toggle"
                data-i18n-tip="ov.tip"
                data-tip="Vista di sintesi: cosa manca al documento, cosa non torna con la realtà, quanto margine resta">
            <i class="fas fa-circle-info"></i><span class="btn-label" data-i18n="ov.label"> Dashboard</span>
        </button>
        <button class="toolbar-btn" id="btn-discover" data-act="disc-open"
                data-i18n-tip="discover.tip"
                data-tip="Scansiona la rete via SNMP, poi espande automaticamente via LLDP/CDP">
            <i class="fas fa-satellite-dish"></i><span class="btn-label" data-i18n="discover.label"> Scopri</span>
        </button>
        <button class="toolbar-btn primary" id="btn-drift" data-act="run-drift-check"
                data-i18n-tip="verify.tip"
                data-tip="Controllo completo: rilegge i dati SNMP, ricostruisce la topologia e verifica la presenza reale (ping/ARP/TCP), evidenziando differenze e cambi IP">
            <i class="fas fa-clipboard-check"></i><span class="btn-label" data-i18n="verify.label"> Verifica</span>
        </button>
        <div class="header-sep"></div>
        <!-- «Report e analisi» è stato ASSORBITO nella Dashboard (porte libere e mappa L3
             nei drill-down dei tile, coerenza VLAN wireless come verdetto ⑤ Sicurezza).
             Resta la sola «Storia modifiche», ora bottone header autonomo (il log è un
             registro temporale, non una fotografia di stato → non appartiene alla Dashboard). -->
        <button class="toolbar-btn" id="btn-audit" data-act="report-audit" data-i18n-tip="report.audit" data-tip="Storia modifiche">
            <i class="fas fa-clock-rotate-left"></i><span class="btn-label" data-i18n="report.auditLabel"> Storia</span>
        </button>
        <button class="toolbar-btn icon-only" id="btn-assistant" data-act="assistant-open"
                data-i18n-tip="assistant.toolbarTip" data-tip="Impostazioni assistente (admin) · la chat si apre dalla tab «Assistente» o col tasto A">
            <i class="fas fa-robot"></i><span class="btn-label" data-i18n="assistant.label"> Assistente</span>
        </button>
        <div id="automation-menu-wrap" style="position:relative">
            <button class="toolbar-btn icon-only" id="btn-automation" data-act="automation-menu-toggle" data-i18n-tip="autom.tip" data-tip="Automazioni di rete: polling SNMP in background e rinnovo automatico IP (DHCP)">
                <i class="fas fa-arrows-rotate"></i><span class="btn-label" data-i18n="autom.label"> Automazioni</span><i class="fas fa-chevron-down" style="font-size:0.6rem;margin-left:4px;opacity:.6"></i>
            </button>
            <div id="automation-dropdown" class="header-menu-dropdown autom-pop" style="display:none"></div>
        </div>
        <!-- Slot generico per le voci di menu dei moduli a pagamento (vuoto se nessun modulo).
             Niente stile inline: il layout è in CSS (.modules-nav-slot → display:contents) così
             quando è vuoto non riserva un gap nel cluster destro. -->
        <div id="modules-nav-slot" class="modules-nav-slot"></div>
        <div class="header-sep"></div>
        <!-- Badge utente + menu (la lingua vive dentro questo menu) -->
        <div id="user-menu-wrap" style="position:relative">
            <button class="toolbar-btn" id="btn-user" data-act="user-menu-toggle" data-i18n-tip="user.tipAccount" data-tip="Account">
                <i class="fas fa-user-circle"></i>
                <span id="user-label" style="margin-left:5px;font-size:0.8rem">…</span>
                <i class="fas fa-chevron-down" style="font-size:0.65rem;margin-left:4px;opacity:.6"></i>
            </button>
            <div id="user-dropdown" style="display:none;position:absolute;right:0;top:calc(100% + 6px);background:var(--panel-bg);border:1px solid var(--panel-border);border-radius:8px;min-width:180px;z-index:500;box-shadow:0 8px 24px rgba(0,0,0,.5);padding:6px 0;">
                <div id="user-info-row" style="padding:8px 14px 6px;font-size:0.85rem;color:var(--text-muted);border-bottom:1px solid var(--panel-border);margin-bottom:4px;"></div>
                <button class="user-menu-item admin-only" data-act="user-manager-open">
                    <i class="fas fa-shield-halved"></i> <span data-i18n="user.manage">Utenti e accessi</span>
                </button>
                <button class="user-menu-item admin-only" data-act="change-password">
                    <i class="fas fa-key"></i> <span data-i18n="user.changePw">Cambia password</span>
                </button>
                <button class="user-menu-item viewer-ok" data-act="change-password" style="display:none">
                    <i class="fas fa-key"></i> <span data-i18n="user.changePw">Cambia password</span>
                </button>
                <div style="border-top:1px solid var(--panel-border);margin:4px 0"></div>
                <div class="user-menu-lang">
                    <span style="font-size:0.85rem;color:var(--text-muted)"><i class="fas fa-language" style="margin-right:6px;opacity:.8"></i><span data-i18n="user.language">Lingua</span></span>
                    <span class="lang-opts">
                        <button class="lang-opt" data-lang="it" data-act="lang-switch">IT</button>
                        <button class="lang-opt" data-lang="en" data-act="lang-switch">EN</button>
                    </span>
                </div>
                <div style="border-top:1px solid var(--panel-border);margin:4px 0"></div>
                <button class="user-menu-item" data-act="logout" style="color:#f85149">
                    <i class="fas fa-sign-out-alt"></i> <span data-i18n="user.logout">Esci</span>
                </button>
            </div>
        </div>
    </div>
</header>

<!-- Sotto-header: breadcrumb (sx) - suggerimento prossimo-passo (centro) -
     statistiche progetto (dx). Contenuto reso da src/app-subbar.js (renderSubbar,
     agganciato a renderAll). Fuori da #export-area -> non entra negli export. -->
<div id="modern-subbar" aria-live="polite"></div>

<div id="workspace">
    <!-- Vista PANORAMICA: fuori da #export-area (come la sotto-header) perche' e'
         una lettura del progetto, non parte del disegno esportato. Il contenuto
         lo rende src/app-overview.js; qui resta vuoto e nascosto finche' la vista
         non e' attiva (body.view-overview). -->
    <main id="overview" role="region" data-i18n-aria="ov.label" aria-label="Dashboard"></main>
    <div id="export-area">
        <aside id="sidebar-left">
            <div class="sidebar-section" style="flex:1;overflow-y:auto">
                <div class="sidebar-palette-head">
                    <h3 class="sidebar-title" data-i18n="palette.title">Libreria elementi</h3>
                    <div class="sidebar-palette-actions">
                        <button type="button" class="sidebar-mini-btn" data-act="palette-expand" data-val="1" data-i18n-tip="palette.expandAll" data-tip="Espandi tutte le sezioni"><i class="fas fa-angles-down"></i></button>
                        <button type="button" class="sidebar-mini-btn" data-act="palette-expand" data-val="0" data-i18n-tip="palette.collapseAll" data-tip="Comprimi tutte le sezioni"><i class="fas fa-angles-up"></i></button>
                    </div>
                </div>
                <div class="sidebar-search-wrap">
                    <i class="fas fa-magnifying-glass"></i>
                    <input id="palette-search" type="text" data-i18n-ph="palette.search" placeholder="Cerca elemento..." data-input="palette-filter">
                    <button id="palette-search-clear" type="button" class="sidebar-search-clear" data-act="palette-filter-clear" data-i18n-tip="palette.clearSearch" data-tip="Pulisci ricerca">
                        <i class="fas fa-xmark"></i>
                    </button>
                </div>

                <section class="palette-group" data-palette-group="ambienti">
                    <button type="button" class="palette-group-head" data-act="palette-group" data-group="ambienti" aria-expanded="true">
                        <span><i class="far fa-square"></i> <span data-i18n="pgroup.ambienti">Ambienti</span></span>
                        <i class="fas fa-chevron-down palette-group-chevron"></i>
                    </button>
                    <div class="palette-group-body">
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="storey"><i class="fas fa-layer-group"></i> <span data-i18n="dev.storey">Piano</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="room"><i class="far fa-square"></i> <span data-i18n="dev.room">Stanza / Area</span></div>
                    </div>
                </section>

                <section class="palette-group" data-palette-group="floor_net">
                    <button type="button" class="palette-group-head" data-act="palette-group" data-group="floor_net" aria-expanded="true">
                        <span><i class="fas fa-diagram-project"></i> <span data-i18n="pgroup.floor_net">Planimetria · Rete</span></span>
                        <i class="fas fa-chevron-down palette-group-chevron"></i>
                    </button>
                    <div class="palette-group-body">
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="wallport"><i class="fas fa-ethernet"></i> <span data-i18n="dev.wallport">Presa a muro</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="ap"><i class="fas fa-wifi"></i> <span data-i18n="dev.ap">Access Point</span></div>
                    </div>
                </section>

                <section class="palette-group" data-palette-group="floor_endpoints">
                    <button type="button" class="palette-group-head" data-act="palette-group" data-group="floor_endpoints" aria-expanded="true">
                        <span><i class="fas fa-laptop"></i> <span data-i18n="pgroup.floor_endpoints">Planimetria · Endpoint</span></span>
                        <i class="fas fa-chevron-down palette-group-chevron"></i>
                    </button>
                    <div class="palette-group-body">
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="webcam"><i class="fas fa-video"></i> <span data-i18n="dev.webcam">Webcam / CCTV</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="printer"><i class="fas fa-print"></i> <span data-i18n="dev.printer">Stampante di rete</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="voip"><i class="fas fa-phone"></i> <span data-i18n="dev.voip">Telefono VoIP</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="badgereader"><i class="fas fa-id-card"></i> <span data-i18n="dev.badgereader">Lettore badge</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="pc"><i class="fas fa-desktop"></i> <span data-i18n="dev.pc">PC / Workstation</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="mobile"><i class="fas fa-mobile-screen-button"></i> <span data-i18n="dev.mobile">Smartphone / Tablet</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="iot"><i class="fas fa-microchip"></i> <span data-i18n="dev.iot">Dispositivo IoT</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="projector"><i class="fas fa-chalkboard"></i> <span data-i18n="dev.projector">Proiettore</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="tv"><i class="fas fa-tv"></i> <span data-i18n="dev.tv">Smart TV / Media Player</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="doorctrl"><i class="fas fa-door-open"></i> <span data-i18n="dev.doorctrl">Door Controller</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="panelboard"><i class="fas fa-bolt"></i> <span data-i18n="dev.panelboard">Quadro elettrico</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="homelab"><i class="fas fa-cubes"></i> <span data-i18n="dev.homelab">Mini-server</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="nasdesktop"><i class="fas fa-hard-drive"></i> <span data-i18n="dev.nasdesktop">NAS (desktop)</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="customfloor"><i class="fas fa-cube"></i> <span data-i18n="dev.customfloor">Endpoint personalizzato</span></div>
                    </div>
                </section>

                <section class="palette-group" data-palette-group="rack_passive">
                    <button type="button" class="palette-group-head" data-act="palette-group" data-group="rack_passive" aria-expanded="true">
                        <span><i class="fas fa-layer-group"></i> <span data-i18n="pgroup.rack_passive">Rack · Passivi</span></span>
                        <i class="fas fa-chevron-down palette-group-chevron"></i>
                    </button>
                    <div class="palette-group-body">
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="patchpanel"><i class="fas fa-bars"></i> <span data-i18n="dev.patchpanel">Patch Panel (2U)</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="blankpanel"><i class="fas fa-minus"></i> <span data-i18n="dev.blankpanel">Pannello vuoto (1U)</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="cablemanager"><i class="fas fa-grip-lines"></i> <span data-i18n="dev.cablemanager">Passacavo (1U)</span></div>
                    </div>
                </section>

                <section class="palette-group" data-palette-group="rack_active">
                    <button type="button" class="palette-group-head" data-act="palette-group" data-group="rack_active" aria-expanded="true">
                        <span><i class="fas fa-server"></i> <span data-i18n="pgroup.rack_active">Rack · Attivi</span></span>
                        <i class="fas fa-chevron-down palette-group-chevron"></i>
                    </button>
                    <div class="palette-group-body">
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="switch"><i class="fas fa-network-wired"></i> <span data-i18n="dev.switch">Switch</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="router"><i class="fas fa-route"></i> <span data-i18n="dev.router">Router</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="firewall"><i class="fas fa-shield-halved"></i> <span data-i18n="dev.firewall">Firewall</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="server"><i class="fas fa-server"></i> <span data-i18n="dev.server">Server</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="hypervisor"><i class="fas fa-layer-group"></i> <span data-i18n="dev.hypervisor">Hypervisor</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="nas"><i class="fas fa-database"></i> <span data-i18n="dev.nas">Storage (SAN / RAID)</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="kvm"><i class="fas fa-keyboard"></i> <span data-i18n="dev.kvm">KVM Switch</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="ups"><i class="fas fa-car-battery"></i> <span data-i18n="dev.ups">UPS</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="pdu"><i class="fas fa-plug"></i> <span data-i18n="dev.pdu">PDU</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="ats"><i class="fas fa-shuffle"></i> <span data-i18n="dev.ats">ATS — Transfer Switch</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="pbx"><i class="fas fa-phone-volume"></i> <span data-i18n="dev.pbx">Centralino VoIP</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="consolesvr"><i class="fas fa-terminal"></i> <span data-i18n="dev.consolesvr">Console Server</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="wlanctrl"><i class="fas fa-wifi"></i> <span data-i18n="dev.wlanctrl">WLAN Controller</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="mediaconv"><i class="fas fa-right-left"></i> <span data-i18n="dev.mediaconv">Media Converter</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="nvr"><i class="fas fa-record-vinyl"></i> <span data-i18n="dev.nvr">NVR / Videosorveglianza (2U)</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="sdwan"><i class="fas fa-cloud-bolt"></i> <span data-i18n="dev.sdwan">SD-WAN Edge</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="vpncon"><i class="fas fa-key"></i> <span data-i18n="dev.vpncon">VPN Concentrator</span></div>
                        <div class="equip-item" draggable="true" data-dragstart="equip" data-type="customrack"><i class="fas fa-cube"></i> <span data-i18n="dev.customrack">Dispositivo rack generico</span></div>
                    </div>
                </section>
            </div>
        </aside>

        <div id="sidebar-divider" data-tip="Trascina per ridimensionare · Clic per nascondere/mostrare palette">
            <div id="sidebar-collapse-btn" data-act="sidebar-toggle" data-tip="Nascondi/mostra palette">
                <i class="fas fa-chevron-left" id="sidebar-collapse-icon"></i>
            </div>
        </div>

        <main id="floorplan" ondragover="event.preventDefault();" ondrop="handleDrop(event,'floor')" onwheel="handleFloorZoom(event)">
            <div class="zoom-controls" style="left:14px;right:auto">
                <button class="zoom-btn" data-act="zoom-floor" data-delta="-0.1"><i class="fas fa-minus"></i></button>
                <span class="zoom-label" id="floor-zoom-lbl">100%</span>
                <button class="zoom-btn" data-act="zoom-floor" data-delta="0.1"><i class="fas fa-plus"></i></button>
            </div>
            <!-- La griglia sta nella FINESTRA, non nella tela — ed è per questo che
                 non ha un bordo. Un background si disegna solo dove c'è un elemento:
                 finché stava dentro #floor-canvas (5000×5000px fissi) finiva dove
                 finiva quel rettangolo, e bastava trascinare di 200px oltre l'origine
                 per vedere il vuoto. Qui l'elemento è grande quanto la vista e a
                 seguire la panoramica è il MOTIVO (background-position), che non ha
                 estremi. Il disegno lo compone paintFloorGrid() in
                 src/app-search-zoom-rack.js: qui non c'è nessun numero.
                 ⚠️ Deve restare PRIMA di #floor-canvas: fra fratelli posizionati a
                 z-index 0/auto decide l'ordine del DOM, e una griglia dopo la tela
                 coprirebbe apparati e cavi. -->
            <div id="floorplan-grid"></div>
            <div id="floor-canvas">
                <img id="floor-bg-img" src="" alt="mappa">
                <div id="floor-structures"></div>
                <div id="floor-items"></div>
                <svg id="topo-floor-overlay" xmlns="http://www.w3.org/2000/svg"></svg>
            </div>
            <!-- Barra controlli-vista mappa: posizionata DIRETTAMENTE in #floorplan
                 (viewport fissa) e NON in #floor-canvas (zoomato/trascinato).
                 Raccoglie i controlli "come guardo la mappa": il toggle Topologia
                 (#btn-topology, stato gestito da _setTopoBtn) e, separato da |, il
                 Filtra VLAN (#topo-legend, contenuto da _renderTopoLegend). -->
            <div id="map-view-bar">
                <div id="topo-legend"></div>
                <span id="map-view-sep"></span>
                <button class="toolbar-btn" id="btn-topology" data-act="toggle-topology" oncontextmenu="event.preventDefault();discoverTopology(true)"
                        data-i18n-tip="topology.tip"
                        data-tip="Vista: mostra i collegamenti LLDP/CDP sulla planimetria · tasto destro per aggiornare">
                    <i class="fas fa-project-diagram"></i><span class="btn-label" data-i18n="topology.label"> Topologia</span>
                </button>
            </div>
        </main>

        <div id="floor-rack-divider" data-tip="Trascina per ridimensionare · Clic per nascondere/mostrare rack">
            <div id="rack-collapse-btn" data-act="rack-panel-toggle" data-tip="Nascondi/mostra rack">
                <i class="fas fa-chevron-right" id="rack-collapse-icon"></i>
            </div>
        </div>

        <aside id="rack-view" ondragover="event.preventDefault();" ondrop="handleDrop(event,'rack')">
            <!-- Tab bar: Rack / Proprietà / Assistente -->
            <div id="right-tab-bar" role="tablist" data-i18n-aria="tab.barAria" aria-label="Pannello destro">
                <div class="right-tab active" id="tab-rack" role="tab" aria-selected="true" aria-controls="rack-viewport" tabindex="0" data-act="right-tab" data-tab="rack">
                    <i class="fas fa-server" style="font-size:var(--fs-xs)"></i> <span data-i18n="tab.rack">Rack</span>
                </div>
                <div class="right-tab" id="tab-props" role="tab" aria-selected="false" aria-controls="props-panel-wrap" tabindex="0" data-act="right-tab" data-tab="props">
                    <i class="fas fa-sliders" style="font-size:var(--fs-xs)"></i> <span data-i18n="tab.props">Proprietà</span>
                    <span class="tab-badge" id="props-tab-badge"></span>
                </div>
                <div class="right-tab" id="tab-ai" role="tab" aria-selected="false" aria-controls="ai-panel-wrap" tabindex="0" data-act="right-tab" data-tab="ai"
                     data-i18n-tip="assistant.tabTip" data-tip="Assistente di rete — domande, diagnosi e bozze · scorciatoia: A">
                    <i class="fas fa-robot" style="font-size:var(--fs-xs)"></i> <span data-i18n="tab.ai">Assistente</span>
                    <span class="tab-badge" id="ai-tab-badge"></span>
                </div>
            </div>

            <!-- Pannello Proprietà (nascosto di default) -->
            <div id="props-panel-wrap">
                <div id="props-panel"><p style="color:var(--text-muted);font-size:var(--fs-sm)">Seleziona un elemento per modificarne le proprietà.</p></div>
            </div>

            <!-- Pannello Assistente AI (nascosto di default). Due stati gestiti da
                 app-ai.js: #ai-empty (non configurato, statico bilingue + ruolo via
                 .admin-only/.viewer-ok) e #ai-chat (configurato → composer chat).
                 La conversazione vive in sessione browser (project-global), non nel JSON. -->
            <div id="ai-panel-wrap" role="tabpanel" aria-labelledby="tab-ai" data-i18n-aria="tab.ai" aria-label="Assistente">
                <div id="ai-panel">
                    <!-- Stato: NON configurato -->
                    <div id="ai-empty">
                        <div class="ai-head">
                            <span class="ai-head-title"><i class="fas fa-robot"></i> <span data-i18n="assistant.title">Assistente</span></span>
                            <span class="ai-chip ai-chip-off" data-i18n-tip="assistant.chipOffTip" data-tip="L'assistente non è ancora configurato"><i class="fas fa-circle-half-stroke"></i> <span data-i18n="assistant.chipOff">Non configurato</span></span>
                        </div>
                        <div class="ai-empty">
                            <i class="fas fa-robot ai-empty-icon"></i>
                            <p class="ai-empty-title" data-i18n="assistant.emptyTitle">L'assistente non è ancora configurato</p>
                            <p class="ai-empty-sub" data-i18n="assistant.emptySub">Interroga la tua rete documentata in linguaggio naturale — presenze, VLAN, IP liberi, diagnosi e bozze di automazione — sempre citando i dati reali. I dati restano sul tuo server.</p>
                            <button class="toolbar-btn primary admin-only" data-act="ai-settings-open"><i class="fas fa-gear"></i> <span data-i18n="assistant.configure">Configura l'assistente</span></button>
                            <p class="ai-empty-hint viewer-ok" style="display:none" data-i18n="assistant.askAdmin">Chiedi a un amministratore di configurare l'assistente.</p>
                        </div>
                        <div class="ai-foot"><i class="fas fa-shield-halved"></i> <span data-i18n="assistant.footer">Advisory: propone, confermi tu. Esce solo l'inventario sanitizzato.</span></div>
                    </div>
                    <!-- Stato: configurato (chat) — mostrato da app-ai.js quando abilitato -->
                    <div id="ai-chat" style="display:none">
                        <div class="ai-head">
                            <span class="ai-head-title"><i class="fas fa-robot"></i> <span data-i18n="assistant.title">Assistente</span></span>
                            <span class="ai-head-actions">
                                <span class="ai-chip" id="ai-chip-status"></span>
                                <button type="button" id="ai-clear-btn" class="ai-gear danger" data-act="ai-clear" data-i18n-aria="assistant.clear" data-i18n-tip="assistant.clear" aria-label="Pulisci chat" style="display:none"><i class="fas fa-trash"></i></button>
                            </span>
                        </div>
                        <div id="ai-messages" class="ai-messages" aria-live="polite"></div>
                        <div class="ai-composer">
                            <textarea id="ai-input" rows="2" data-i18n-ph="assistant.inputPh" placeholder="Chiedi qualcosa sulla tua rete…"></textarea>
                            <button class="toolbar-btn primary" id="ai-send-btn" data-act="ai-send" data-i18n-aria="assistant.send" aria-label="Invia"><i class="fas fa-paper-plane"></i></button>
                        </div>
                        <div class="ai-foot"><i class="fas fa-shield-halved"></i> <span data-i18n="assistant.footer">Advisory: propone, confermi tu. Esce solo l'inventario sanitizzato.</span></div>
                    </div>
                </div>
            </div>

            <div id="rack-viewport" onwheel="handleRackZoom(event)" onscroll="renderCables()">
                <div class="rack-header rack-header-floating">
                    <select id="rack-select" data-change="rack-select"></select>
                    <div id="rack-menu-wrap" style="position:relative">
                        <button class="toolbar-btn" id="rack-menu-btn" style="margin:0;padding:3px 9px" data-act="rack-menu-toggle" data-tip="Gestione rack">
                            <i class="fas fa-server"></i> Rack <i class="fas fa-chevron-down" style="font-size:0.62rem;opacity:.7;margin-left:4px"></i>
                        </button>
                        <div id="rack-menu-dropdown" class="rack-menu-dropdown" style="display:none">
                            <button class="rack-menu-item" data-act="rack-add"><i class="fas fa-plus"></i> <span data-i18n="rack.new">Nuovo rack</span></button>
                            <button class="rack-menu-item" data-act="rack-rename"><i class="fas fa-pen"></i> <span data-i18n="rack.rename">Rinomina rack</span></button>
                            <div class="rack-menu-size-row">
                                <span data-i18n="rack.size">Dimensione</span>
                                <label class="rack-size-control">U <input id="rack-size-input" type="number" min="6" max="60" value="42" data-change="rack-size" data-keydown="rack-size-key"></label>
                            </div>
                            <button class="rack-menu-item" id="btn-rack-floor" data-act="rack-on-floor" data-i18n-tip="rack.onFloorTip" data-tip="Piazza/rimuovi questo rack dalla planimetria"><i class="fas fa-map-pin"></i> <span data-i18n="rack.onFloor">Piazza su planimetria</span></button>
                            <button class="rack-menu-item" id="btn-rack-unum" data-act="rack-unumber" data-i18n-tip="rack.uNumTip" data-tip="Inverte solo la visualizzazione: 1 in alto come nei rack telco/ETSI"><i class="fas fa-arrows-up-down"></i> <span id="btn-rack-unum-label" data-i18n="rack.uNumFromTop">Numerazione U dall'alto</span></button>
                            <button class="rack-menu-item danger" data-act="rack-delete"><i class="fas fa-trash"></i> <span data-i18n="rack.delete">Elimina rack</span></button>
                        </div>
                    </div>
                </div>
                <svg id="rack-cable-overlay"><path id="temp-link-rack" d=""></path></svg>
                <div id="cross-rack-banner"></div>
                <div id="lag-sel-banner"></div>
                <div class="zoom-controls">
                    <button class="zoom-btn" data-act="zoom-rack" data-delta="-0.1"><i class="fas fa-minus"></i></button>
                    <button type="button" class="zoom-label" id="rack-zoom-lbl" data-act="rack-fit" data-i18n-tip="rack.fitTip" data-tip="Adatta alla larghezza: il rack intero nella finestra">100%</button>
                    <button class="zoom-btn" data-act="zoom-rack" data-delta="0.1"><i class="fas fa-plus"></i></button>
                </div>
                <div id="rack-chassis-wrap">
                    <div id="rack-ruler"></div>
                    <div class="rack-chassis" id="rack-chassis"></div>
                </div>
            </div>
        </aside>

        <svg id="cable-overlay"><path id="temp-link" d=""></path></svg>
    </div>
</div>

<div id="popup"></div>

<!-- Modal -->
<div id="modal-overlay">
    <div id="modal-box" role="dialog" aria-modal="true" aria-labelledby="modal-msg">
        <p id="modal-msg"></p>
        <input id="modal-input" type="text" autocomplete="off">
        <div id="modal-actions">
            <button id="modal-cancel" class="toolbar-btn"         data-act="modal-resolve" data-val="0" data-i18n="common.cancel">Annulla</button>
            <button id="modal-ok"     class="toolbar-btn primary" data-act="modal-resolve" data-val="1">OK</button>
        </div>
    </div>
</div>

<!-- Server offline banner -->
<div id="conn-banner">
    <i class="fas fa-triangle-exclamation"></i>
    Server non raggiungibile — avvia <code>avvia.bat</code> e ricarica la pagina.
</div>

<!-- ===== MODAL ROOT: tutti i modali (tool-modal-overlay) stanno QUI, fuori da <header>,
     per non ereditarne il containing-block (un backdrop-filter/transform/filter
     sull&#39;header ancorerebbe gli overlay position:fixed al box header -> tagliati). ===== -->
<div id="modal-root">

<!-- VLAN membership modal -->
<div id="vlan-members-overlay" class="tool-modal-overlay" data-act="vm-backdrop" style="display:none">
  <div class="tool-modal" style="width:480px;max-height:80vh" role="dialog" aria-modal="true" aria-labelledby="vm-title">
    <div class="tool-modal-header um-header">
      <h3 id="vm-title"><i class="fas fa-layer-group"></i>Membership VLAN</h3>
      <button class="toolbar-btn" data-act="vm-close" style="padding:4px 8px"><i class="fas fa-times"></i></button>
    </div>
    <div id="vm-body" class="tool-modal-body"></div>
    <div class="tool-modal-footer">
      <button id="vm-btn-filter" class="um-btn primary"></button>
      <button class="um-btn ghost" data-act="vm-close"><i class="fas fa-times"></i> <span data-i18n="common.close">Chiudi</span></button>
    </div>
  </div>
</div>

<!-- ===== MODAL SINCRONIZZAZIONE DCIM/IPAM ===== -->
<div id="dcim-overlay" class="tool-modal-overlay" data-act="dcim-backdrop">
  <div id="dcim-modal" class="tool-modal" role="dialog" aria-modal="true" aria-labelledby="dcim-title">
    <div class="tool-modal-header um-header">
      <h3 id="dcim-title"><i class="fas fa-rotate"></i> <span data-i18n="integrations.title">Sincronizzazione DCIM/IPAM</span></h3>
      <button class="toolbar-btn" data-act="dcim-close" style="padding:4px 8px" data-tip="Chiudi"><i class="fas fa-times"></i></button>
    </div>
    <div class="tool-modal-body">
      <p data-i18n="integrations.hint" style="color:var(--text-muted);margin:0 0 14px;font-size:var(--fs-md)">Collega un'istanza DCIM/IPAM esterna (NetBox) per importare o esportare la documentazione di rete.</p>
      <div class="um-form">
        <div class="um-row" style="align-items:center">
          <label style="min-width:110px" data-i18n="integrations.adapter">Adattatore</label>
          <span style="display:inline-flex;align-items:center;gap:6px;font-weight:500"><i class="fas fa-database" style="color:var(--text-muted)"></i> NetBox</span>
        </div>
        <div class="um-row">
          <input type="text" id="dcim-url" data-input="dcim-cfg" data-i18n-ph="integrations.urlPh" placeholder="https://dcim.azienda.local" autocomplete="off" style="flex:1" aria-describedby="dcim-url-hint">
          <small id="dcim-url-hint" data-i18n="integrations.urlHint" style="color:var(--text-muted)">Inserisci solo la base, non /api/dcim/devices: gli endpoint vengono aggiunti automaticamente.</small>
        </div>
        <div class="um-row" style="display:flex;flex-direction:column;align-items:stretch;gap:5px">
          <label for="dcim-token" data-i18n="integrations.tokenLabel">Token API NetBox v2</label>
          <input type="password" id="dcim-token" data-input="dcim-cfg" data-i18n-ph="integrations.tokenPh" aria-describedby="dcim-token-hint" placeholder="nbt_key.secret" autocomplete="off" style="flex:1">
          <small id="dcim-token-hint" data-i18n="integrations.tokenHint" style="color:var(--text-muted)">Crea in NetBox un token v2. Il token dell'API InfraNet si gestisce da Account → Token API.</small>
        </div>
        <div class="um-row">
          <label style="display:flex;align-items:center;gap:8px;cursor:pointer">
            <input type="checkbox" id="dcim-verifytls" checked>
            <span data-i18n="integrations.verifyTls">Verifica certificato TLS</span>
          </label>
        </div>
        <div class="um-row" style="align-items:center;gap:12px">
          <button class="um-btn" id="dcim-test-btn" data-act="dcim-test"><i class="fas fa-plug"></i> <span data-i18n="integrations.test">Prova connessione</span></button>
          <span id="dcim-status" data-i18n="integrations.notConnected" style="font-size:.85rem;color:var(--text-muted)">Non connesso</span>
        </div>
      </div>
      <div id="dcim-catalog-summary" style="margin-top:12px;padding:9px 11px;border:0.5px solid var(--border);border-radius:var(--radius);font-size:.8rem;color:var(--text-muted)"></div>
      <div class="um-tabs" style="margin-top:16px">
        <button class="um-tab active" id="dcim-tab-import" data-act="dcim-tab" data-tab="import"><i class="fas fa-download"></i> <span data-i18n="integrations.importTab">Importa</span></button>
        <button class="um-tab" id="dcim-tab-export" data-act="dcim-tab" data-tab="export"><i class="fas fa-lock"></i> <span data-i18n="integrations.exportTab">Esporta · Pro</span></button>
      </div>
      <div id="dcim-import-pane"><div id="dcim-import-body"></div></div>
      <div id="dcim-export-pane" style="display:none"><div id="dcim-export-body"></div></div>
    </div>
    <div class="tool-modal-footer">
      <button class="um-btn primary" data-act="dcim-save"><i class="fas fa-floppy-disk"></i> <span data-i18n="integrations.save">Salva</span></button>
      <button class="um-btn ghost" data-act="dcim-close"><i class="fas fa-times"></i> <span data-i18n="common.close">Chiudi</span></button>
    </div>
  </div>
</div>

<!-- ===== MODAL GESTIONE UTENTI ===== -->
<div id="user-manager-overlay" class="tool-modal-overlay" data-act="um-backdrop">
  <div id="user-manager-modal" class="tool-modal" style="width:520px;max-height:85vh" role="dialog" aria-modal="true" aria-labelledby="user-manager-title">
    <div class="tool-modal-header um-header">
      <h3 id="user-manager-title"><i class="fas fa-shield-halved"></i><span data-i18n="um.title">Utenti e accessi</span></h3>
      <button class="toolbar-btn" data-act="um-close" style="padding:4px 8px"><i class="fas fa-times"></i></button>
    </div>
    <div class="um-tabs">
      <button class="um-tab active" id="um-tab-users" data-act="um-switch-tab" data-tab="users">
        <i class="fas fa-users-cog"></i> <span data-i18n="um.tabUsers">Utenti</span>
      </button>
      <button class="um-tab" id="um-tab-tokens" data-act="um-switch-tab" data-tab="tokens">
        <i class="fas fa-key"></i> <span data-i18n="um.tabTokens">Token API</span>
      </button>
      <button class="um-tab" id="um-tab-ai" data-act="um-switch-tab" data-tab="ai">
        <i class="fas fa-robot"></i> <span data-i18n="um.tabAi">Assistente AI</span>
      </button>
    </div>
    <div class="tool-modal-body">
      <!-- ===== Pane UTENTI ===== -->
      <div class="um-pane active" id="um-pane-users">
        <!-- Form nuovo utente -->
        <div class="um-form">
          <h3><i class="fas fa-user-plus"></i> <span data-i18n="um.newUser">Nuovo utente</span></h3>
          <div class="um-row">
            <input type="text" id="um-new-username" data-i18n-ph="um.phUsername" placeholder="Nome utente" autocomplete="off">
            <input type="password" id="um-new-password" placeholder="Password" autocomplete="new-password">
          </div>
          <div class="um-row">
            <select id="um-new-role">
              <option value="viewer" data-i18n="um.roleViewer">Viewer — solo lettura</option>
              <option value="admin" data-i18n="um.roleAdmin">Admin — accesso completo</option>
            </select>
            <button class="um-btn primary" data-act="um-create-user">
              <i class="fas fa-plus"></i> <span data-i18n="um.create">Crea</span>
            </button>
          </div>
          <div class="um-msg" id="um-new-msg"></div>
        </div>
        <!-- Lista utenti -->
        <div id="um-user-list"><i class="fas fa-spinner fa-spin" style="color:var(--text-muted)"></i> Caricamento…</div>
      </div>
      <!-- ===== Pane TOKEN API ===== -->
      <div class="um-pane" id="um-pane-tokens">
        <!-- Form nuovo token -->
        <div class="um-form">
          <h3><i class="fas fa-key"></i> <span data-i18n="tk.new">Nuovo token</span></h3>
          <div class="um-row">
            <input type="text" id="tk-new-label" data-i18n-ph="tk.phLabel" placeholder="Etichetta — es. ansible, dashboard" autocomplete="off">
            <!-- Scadenza in giorni: vuoto = non scade (il comportamento di sempre). -->
            <input type="number" id="tk-new-days" min="0" step="1" style="max-width:150px" data-i18n-ph="tk.phDays" placeholder="Scadenza (giorni)" autocomplete="off">
            <button class="um-btn primary" data-act="tk-create">
              <i class="fas fa-plus"></i> <span data-i18n="tk.generate">Genera token</span>
            </button>
          </div>
          <div class="um-msg" id="tk-new-msg"></div>
        </div>
        <!-- Reveal one-time del token appena creato -->
        <div class="tk-reveal" id="tk-reveal">
          <div class="tk-reveal-note"><i class="fas fa-triangle-exclamation"></i> <span data-i18n="tk.created">Token generato — copialo ora, non sarà più visibile.</span></div>
          <div class="tk-reveal-row">
            <code class="tk-code" id="tk-reveal-code"></code>
            <button class="um-btn ghost" id="tk-copy-btn" data-act="tk-copy"><i class="fas fa-copy"></i> <span data-i18n="tk.copy">Copia</span></button>
          </div>
        </div>
        <!-- Lista token attivi -->
        <div class="tk-sec-label"><span data-i18n="tk.active">Token attivi</span></div>
        <div id="tk-list"><i class="fas fa-spinner fa-spin" style="color:var(--text-muted)"></i></div>
        <div class="tk-foot-hint"><i class="fas fa-eye"></i> <span data-i18n="tk.readonly">sola lettura · API</span> <code>/api/v1</code></div>
      </div>
      <!-- ===== Pane ASSISTENTE AI (L0: config + «mostra cosa esce») ===== -->
      <div class="um-pane" id="um-pane-ai">
        <div class="um-form">
          <h3><i class="fas fa-robot"></i> <span data-i18n="ai.cfgTitle">Assistente AI (advisory)</span></h3>
          <p class="ai-cfg-intro" data-i18n="ai.cfgIntro">L'assistente interroga la TUA rete documentata. I dati restano sul server: esce solo l'inventario sanitizzato (mai password o community SNMP). Di default l'inferenza è locale (Ollama).</p>
          <label class="ai-cfg-toggle">
            <input type="checkbox" id="ai-cfg-enabled">
            <span data-i18n="ai.cfgEnable">Abilita l'assistente</span>
          </label>
          <label class="ai-cfg-label" for="ai-cfg-endpoint" data-i18n="ai.cfgEndpoint">Endpoint (OpenAI-compatibile)</label>
          <input type="text" id="ai-cfg-endpoint" class="ai-cfg-input" placeholder="http://localhost:11434/v1" autocomplete="off">
          <label class="ai-cfg-label" for="ai-cfg-model" data-i18n="ai.cfgModel">Modello</label>
          <input type="text" id="ai-cfg-model" class="ai-cfg-input" data-i18n-ph="ai.cfgModelPh" placeholder="es. llama3.1, gpt-4o-mini" autocomplete="off">
          <label class="ai-cfg-label" for="ai-cfg-key" data-i18n="ai.cfgKey">Chiave API (solo cloud · write-only)</label>
          <input type="password" id="ai-cfg-key" class="ai-cfg-input" data-i18n-ph="ai.cfgKeyPh" placeholder="non necessaria in locale" autocomplete="new-password">
          <div class="ai-cfg-hint"><i class="fas fa-lock"></i> <span data-i18n="ai.cfgKeyHint">La chiave resta sul server, non torna mai al browser e non finisce nel progetto.</span></div>
          <!-- Ambito dati: cosa esce verso il modello (privacy + costo) -->
          <div class="ai-cfg-group">
            <div class="ai-cfg-grouphd"><i class="fas fa-shield-halved"></i> <span data-i18n="ai.scopeTitle">Ambito dati — cosa esce verso il modello</span></div>
            <label class="ai-cfg-check"><input type="checkbox" id="ai-scope-devices"> <span data-i18n="ai.scopeDevices">Inventario device</span></label>
            <label class="ai-cfg-check"><input type="checkbox" id="ai-scope-ports"> <span data-i18n="ai.scopePorts">Porte (stato/VLAN/collegamenti)</span></label>
            <label class="ai-cfg-check"><input type="checkbox" id="ai-scope-snmpHealth"> <span data-i18n="ai.scopeHealth">Salute SNMP (CPU/RAM/toner/UPS)</span></label>
            <label class="ai-cfg-check"><input type="checkbox" id="ai-scope-topology"> <span data-i18n="ai.scopeTopology">Topologia (collegamenti tra device)</span></label>
            <label class="ai-cfg-check"><input type="checkbox" id="ai-scope-drift"> <span data-i18n="ai.scopeDrift">Drift / Verifica</span></label>
          </div>
          <!-- Capacità: cosa l'assistente può fare -->
          <div class="ai-cfg-group">
            <div class="ai-cfg-grouphd"><i class="fas fa-sliders"></i> <span data-i18n="ai.featTitle">Capacità — cosa può fare</span></div>
            <label class="ai-cfg-check"><input type="checkbox" id="ai-feat-qa"> <span data-i18n="ai.featQa">Q&amp;A sulla rete</span></label>
            <label class="ai-cfg-check"><input type="checkbox" id="ai-feat-diagnostics"> <span data-i18n="ai.featDiag">Diagnostica</span></label>
            <label class="ai-cfg-check"><input type="checkbox" id="ai-feat-gaps"> <span data-i18n="ai.featGaps">Trova lacune</span></label>
            <label class="ai-cfg-check"><input type="checkbox" id="ai-feat-suggestions"> <span data-i18n="ai.featSugg">Suggerimenti</span></label>
            <label class="ai-cfg-check"><input type="checkbox" id="ai-feat-ansible"> <span data-i18n="ai.featAnsible">Bozza Ansible</span></label>
          </div>
          <div class="um-row" style="margin-top:10px">
            <button class="um-btn primary admin-only" id="ai-cfg-save" data-act="ai-cfg-save"><i class="fas fa-floppy-disk"></i> <span data-i18n="ai.cfgSave">Salva</span></button>
            <button class="um-btn ghost" id="ai-cfg-preview-btn" data-act="ai-cfg-preview"><i class="fas fa-eye"></i> <span data-i18n="ai.cfgPreview">Mostra cosa esce</span></button>
          </div>
          <div class="um-msg" id="ai-cfg-msg"></div>
        </div>
        <div class="ai-cfg-preview" id="ai-cfg-preview" style="display:none">
          <div class="ai-cfg-preview-head"><i class="fas fa-shield-halved"></i> <span data-i18n="ai.cfgPreviewHead">Questo è ciò che uscirebbe verso il modello — sanitizzato (niente password né community SNMP).</span></div>
          <pre class="ai-cfg-preview-out" id="ai-cfg-preview-out"></pre>
        </div>
      </div>
    </div>
    <div class="tool-modal-footer">
      <button class="um-btn ghost" data-act="um-close"><i class="fas fa-times"></i> <span data-i18n="common.close">Chiudi</span></button>
    </div>
  </div>
</div>

<!-- ===== MODAL CAMBIO PASSWORD ===== -->
<div id="chpwd-overlay" class="tool-modal-overlay" data-act="chpwd-backdrop" style="display:none">
  <div class="tool-modal" style="width:380px" role="dialog" aria-modal="true" aria-labelledby="chpwd-title">
    <div class="tool-modal-header um-header">
      <h3 id="chpwd-title"><i class="fas fa-key"></i><span data-i18n="chpwd.title">Cambia Password</span></h3>
      <button class="toolbar-btn" data-act="chpwd-close" style="padding:4px 8px"><i class="fas fa-times"></i></button>
    </div>
    <div class="tool-modal-body">
      <div class="um-row"><input type="password" id="chpwd-old" data-i18n-ph="chpwd.phOld" placeholder="Password attuale" autocomplete="current-password"></div>
      <div class="um-row"><input type="password" id="chpwd-new" data-i18n-ph="chpwd.phNew" placeholder="Nuova password" autocomplete="new-password"></div>
      <div class="um-row"><input type="password" id="chpwd-confirm" data-i18n-ph="chpwd.phConfirm" placeholder="Conferma nuova password" autocomplete="new-password"></div>
      <div class="um-msg" id="chpwd-msg"></div>
    </div>
    <div class="tool-modal-footer">
      <button class="um-btn ghost" data-act="chpwd-close" data-i18n="common.cancel">Annulla</button>
      <button class="um-btn primary" data-act="chpwd-submit"><i class="fas fa-check"></i> <span data-i18n="chpwd.update">Aggiorna</span></button>
    </div>
  </div>
</div>

<!-- ===== MODAL TOPOLOGY CRAWL ===== -->
<!-- ===== MODAL EDITOR PROTOCOLLI MANAGEMENT ===== -->
<div id="mgmt-proto-overlay" class="tool-modal-overlay" style="display:none" data-act="mgmt-proto-backdrop">
  <div class="tool-modal" role="dialog" aria-modal="true" aria-labelledby="mgmt-proto-title">
    <div class="tool-modal-header">
      <h3 id="mgmt-proto-title"><i class="fas fa-cog"></i> <span data-i18n="mgmt.title">Protocolli di management</span></h3>
      <button class="toolbar-btn" data-act="mgmt-proto-close" style="padding:4px 8px"><i class="fas fa-times"></i></button>
    </div>
    <div class="tool-modal-body">
      <div class="tm-hint" data-i18n-html="mgmt.hint">
        Aggiungi/togli protocolli e app di gestione. Lo <strong>scheme</strong> deve includere <code>://</code> (o <code>:</code> per scheme "opaque" come <code>anydesk:</code>). L'URL finale al click sarà <code>scheme + IP del device</code>.<br>
        Esempi: <code>winbox://</code>, <code>anydesk:</code>, <code>teamviewer10:</code>, <code>tightvnc://</code>.
        L'app di destinazione deve essere installata e avere registrato lo scheme nell'OS.
      </div>
      <table class="disc-table" style="margin-top:10px">
        <thead><tr><th style="width:30%">Label</th><th style="width:35%">Scheme</th><th style="width:25%" data-i18n="mgmt.internalId">ID interno</th><th></th></tr></thead>
        <tbody id="mgmt-proto-tbody"></tbody>
      </table>
    </div>
    <div class="tool-modal-footer">
      <button class="toolbar-btn" data-act="mgmt-proto-close" data-i18n="common.close">Chiudi</button>
      <button class="toolbar-btn" data-act="mgmt-proto-reset"><i class="fas fa-rotate-left"></i> <span data-i18n="mgmt.resetDefault">Ripristina default</span></button>
      <button class="toolbar-btn primary" data-act="mgmt-proto-add"><i class="fas fa-plus"></i> <span data-i18n="common.add">Aggiungi</span></button>
    </div>
  </div>
</div>

<div id="topodisc-overlay" class="tool-modal-overlay" data-act="topo-crawl-backdrop">
  <div class="tool-modal wide" role="dialog" aria-modal="true" aria-labelledby="topodisc-title">
    <div class="tool-modal-header">
      <h3 id="topodisc-title"><i class="fas fa-project-diagram"></i> <span data-i18n="topocrawl.title">Scopri rete da topologia</span></h3>
      <button class="toolbar-btn" data-act="topo-crawl-close" style="padding:4px 8px"><i class="fas fa-times"></i></button>
    </div>
    <div class="tool-modal-body">
      <div class="tm-hint" data-i18n="topocrawl.hint">Inserisci un dispositivo di partenza raggiungibile via SNMP. L'app seguirà i collegamenti LLDP/CDP e mapperà automaticamente tutta la rete connessa.</div>
      <div class="tm-row">
        <div class="tm-field" style="width:160px">
          <label data-i18n="topocrawl.seedIp">IP seme (punto di partenza)</label>
          <input id="td-seed" placeholder="192.168.1.1">
        </div>
        <div class="tm-field" style="width:110px">
          <label>SNMP</label>
          <select id="td-driver">
            <option value="snmp-v2c">SNMPv2c</option>
            <option value="snmp-v3">SNMPv3</option>
          </select>
        </div>
        <div class="tm-field" style="width:120px">
          <label>Community</label>
          <input id="td-community" type="password" autocomplete="new-password" value="public">
        </div>
        <div class="tm-field" style="width:80px">
          <label data-i18n="topocrawl.maxDepth">Profondità max</label>
          <input id="td-depth" type="number" value="5" min="1" max="10">
        </div>
        <div class="tm-field" style="width:70px">
          <label data-i18n="topocrawl.timeout">Timeout (s)</label>
          <input id="td-timeout" type="number" value="3" min="1" max="10">
        </div>
      </div>
      <div id="td-log"></div>
      <div id="td-results" style="display:none">
        <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">
          <span id="td-summary" style="font-size:0.8rem;font-weight:600"></span>
          <label style="font-size:var(--fs-xs);color:var(--text-muted);cursor:pointer">
            <input type="checkbox" id="td-selall" data-change="td-selall" style="margin-right:4px;accent-color:var(--accent)"><span data-i18n="common.selectAllPl">Seleziona tutti</span>
          </label>
        </div>
        <div style="max-height:260px;overflow-y:auto">
          <table class="disc-table">
            <thead><tr><th></th><th>IP</th><th>Hostname</th><th data-i18n="common.type">Tipo</th><th data-i18n="common.description">Descrizione</th><th>Hop</th></tr></thead>
            <tbody id="td-tbody"></tbody>
          </table>
        </div>
      </div>
    </div>
    <div class="tool-modal-footer">
      <button class="toolbar-btn" data-act="topo-crawl-close" data-i18n="common.cancel">Annulla</button>
      <button class="toolbar-btn" id="td-scan-btn" data-act="topo-crawl-run"><i class="fas fa-satellite-dish"></i> <span data-i18n="topocrawl.scan">Scopri rete</span></button>
      <button class="toolbar-btn primary" id="td-import-btn" data-act="topo-crawl-import" style="display:none"><i class="fas fa-file-import"></i> <span data-i18n="common.importSelected">Importa selezionati</span></button>
    </div>
  </div>
</div>

<!-- ===== MODAL DISCOVERY RETE ===== -->
<div id="disc-overlay" class="tool-modal-overlay" data-act="disc-backdrop">
  <div class="tool-modal wide" role="dialog" aria-modal="true" aria-labelledby="disc-title">
    <div class="tool-modal-header">
      <h3 id="disc-title"><i class="fas fa-satellite-dish"></i> <span data-i18n="disc.title">Scopri dispositivi in rete</span></h3>
      <button class="toolbar-btn" data-act="disc-close" style="padding:4px 8px"><i class="fas fa-times"></i></button>
    </div>
    <div class="tool-modal-body">
      <div id="disc-setup">
      <div class="tm-hint" data-i18n="disc.hint">Indica un intervallo di rete: ogni indirizzo viene interrogato via SNMP e quelli che rispondono vengono importati.</div>
      <div class="tm-row">
        <div class="tm-field grow">
          <label data-i18n="disc.fieldRange">Intervallo di rete</label>
          <input id="disc-subnet" placeholder="192.168.1.0/24  oppure  192.168.1.1-254" style="width:100%">
        </div>
        <div class="tm-field" style="width:140px;min-width:140px">
          <label data-i18n="disc.fieldCommunity">Community SNMP</label>
          <input id="disc-community" type="password" autocomplete="new-password" value="public">
        </div>
        <div class="tm-field timeout">
          <label data-i18n="disc.fieldTimeout">Attesa (s)</label>
          <input id="disc-timeout" type="number" value="2" min="1" max="10">
        </div>
      </div>
      <!-- Associazione VLAN ↔ subnet (declare-first, bidirezionale): la subnet dello
           scan è LO STESSO dato del pannello VLAN → si digita una volta sola. Scegli
           una VLAN già dichiarata e il suo subnet riempie l'intervallo; oppure indica
           una VLAN e alla scansione la subnet viene dichiarata nel pannello. Opzionale. -->
      <div class="tm-row disc-vlan-row">
        <div class="tm-field" style="width:260px;min-width:260px">
          <label data-i18n="disc.fieldVlan">Associa a VLAN (facoltativo)</label>
          <select id="disc-vlan" data-change="disc-vlan">
            <option value="" data-i18n="disc.vlanNone">— nessuna —</option>
            <option value="__new__" data-i18n="disc.vlanNewOpt">Nuova VLAN…</option>
          </select>
        </div>
        <div class="tm-field" id="disc-vlan-new-wrap" style="width:120px;min-width:120px;display:none">
          <label data-i18n="disc.vlanNewLabel">Numero VLAN</label>
          <input id="disc-vlan-new" type="number" min="1" max="4094" placeholder="es. 30" data-change="disc-vlan" autocomplete="off">
        </div>
        <div id="disc-vlan-hint" class="disc-vlan-hint" aria-live="polite"></div>
      </div>
      <!-- Linea full-frame: separa "cosa/dove scansionare" (sopra) da "come scansionare" (sotto). -->
      <div class="disc-sep"></div>
      <div class="disc-opt-row">
        <label class="disc-speed" data-i18n-tip="disc.speedTip" data-tip="Bilanciata: consigliata, ritmo gentile. Veloce: in parallelo, più rapida. Prudente: serializzata con pause casuali per non farsi notare da un IDS — più lenta.">
          <span data-i18n="disc.speed">Velocità</span>
          <select id="disc-scan-mode">
            <option value="normal" data-i18n="disc.speedFast">Veloce</option>
            <option value="safe" selected data-i18n="disc.speedBalanced">Bilanciata (consigliata)</option>
            <option value="stealth" data-i18n="disc.speedCareful">Prudente (reti monitorate)</option>
          </select>
        </label>
        <fieldset class="disc-scan-group">
          <legend><i class="fas fa-sliders-h" aria-hidden="true"></i> <span data-i18n="disc.moreGroup">Cerca più a fondo</span> <span class="disc-group-hint" data-i18n="disc.moreGroupHint">— trova di più, impiega più tempo</span></legend>
          <div class="disc-opt-grid">
            <label class="disc-opt-item disc-opt-deep">
              <input id="disc-deep-scan" type="checkbox" data-change="disc-deep-scan">
              <span class="disc-opt-text"><span class="disc-opt-title" data-i18n="disc.deepScan">Riconosci meglio i dispositivi</span><span class="disc-opt-desc" data-i18n="disc.deepScanDesc">prova le porte più comuni per capire cos'è ogni apparato</span></span>
            </label>
            <label class="disc-opt-item disc-opt-mdns">
              <input id="disc-mdns" type="checkbox">
              <span class="disc-opt-text"><span class="disc-opt-title" data-i18n="disc.mdns">Trova anche i dispositivi silenziosi</span><span class="disc-opt-desc" data-i18n="disc.mdnsDesc">telefoni, TV, stampanti ed elettrodomestici smart (solo stessa sottorete)</span></span>
            </label>
            <label class="disc-opt-item disc-opt-ignoreping">
              <input id="disc-ignore-ping" type="checkbox">
              <span class="disc-opt-text"><span class="disc-opt-title" data-i18n="disc.ignorePing">Includi chi non risponde al ping</span><span class="disc-opt-desc" data-i18n="disc.ignorePingDesc">utile dietro firewall che bloccano il ping ma lasciano passare SNMP</span></span>
            </label>
            <label class="disc-opt-item">
              <input id="disc-expand-topology" type="checkbox">
              <span class="disc-opt-text"><span class="disc-opt-title" data-i18n="disc.expand">Segui i collegamenti tra gli switch</span><span class="disc-opt-desc" data-i18n="disc.expandDesc">estende la ricerca oltre l'intervallo indicato, apparato per apparato</span></span>
            </label>
          </div>
        </fieldset>
      </div>
      </div><!-- /#disc-setup -->
      <div id="disc-progress" class="tm-progress"></div>
      <div id="disc-results" style="display:none">
        <div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:8px">
          <span id="disc-summary" style="font-size:0.8rem;font-weight:600"></span>
          <label style="font-size:var(--fs-xs);color:var(--text-muted);cursor:pointer">
            <input type="checkbox" id="disc-selall" data-change="disc-selall" style="margin-right:4px;accent-color:var(--accent)"><span data-i18n="common.selectAllPl">Seleziona tutti</span>
          </label>
        </div>
        <div id="disc-results-table-wrap">
          <table class="disc-table disc-scan-table">
            <thead><tr><th></th><th data-i18n="common.status">Stato</th><th data-i18n="common.name">Nome</th><th>IP</th><th>Vendor</th><th>MAC</th><th data-i18n="common.type">Tipo</th></tr></thead>
            <tbody id="disc-tbody"></tbody>
          </table>
        </div>
        <div style="margin-top:10px;font-size:var(--fs-xs);color:var(--text-muted)">
          <span data-i18n="disc.rackTarget">Rack di destinazione:</span> <strong id="disc-rack-name">—</strong>
          <span style="margin-left:8px;opacity:.6" data-i18n="disc.rackTargetNote">(dispositivi rack aggiuntivi al rack corrente)</span>
        </div>
      </div>
    </div>
    <div class="tool-modal-footer">
      <button class="toolbar-btn" id="disc-new-search" data-act="disc-new-search" style="margin-right:auto;display:none"><i class="fas fa-arrow-left"></i> <span data-i18n="disc.newSearch">Nuova ricerca</span></button>
      <button class="toolbar-btn" data-act="disc-close" data-i18n="common.cancel">Annulla</button>
      <button class="toolbar-btn" id="disc-scan-btn" data-act="disc-run"><i class="fas fa-search"></i> <span data-i18n="disc.scan">Scansiona</span></button>
      <button class="toolbar-btn primary" id="disc-import-btn" data-act="disc-import" disabled><i class="fas fa-file-import"></i> <span data-i18n="common.addSelected">Aggiungi selezionati</span></button>
    </div>
  </div>
</div>

<!-- ===== MODAL IMPORTA CSV ===== -->
<div id="csv-overlay" class="tool-modal-overlay" data-act="csv-backdrop">
  <div class="tool-modal wide" role="dialog" aria-modal="true" aria-labelledby="csv-title">
    <div class="tool-modal-header">
      <h3 id="csv-title"><i class="fas fa-file-csv"></i> <span data-i18n="impexp.importCsv">Importa dispositivi da CSV</span></h3>
      <button class="toolbar-btn" data-act="csv-close" style="padding:4px 8px"><i class="fas fa-times"></i></button>
    </div>
    <div class="tool-modal-body">
      <div class="tm-hint">
        Formato CSV (intestazione obbligatoria):<br>
        <code style="font-size:var(--fs-2xs);opacity:.8">name, hostname, ip, type, rack, rackU, sizeU, ports</code><br>
        Campi obbligatori: <strong>name</strong>, <strong>type</strong>. Tutti gli altri sono facoltativi.<br>
        Tipi validi: switch · router · firewall · server · nas · kvm · ups · pdu · ats · patchpanel · ap · wallport · webcam · tv · iot · pc · customfloor · customrack
      </div>
      <div class="tm-row" style="gap:8px;margin-bottom:8px">
        <button class="toolbar-btn" data-act="csv-file-pick"><i class="fas fa-folder-open"></i> Scegli file</button>
        <input type="file" id="csv-file" accept=".csv,.txt" style="display:none" data-change="csv-file">
        <span style="font-size:var(--fs-xs);color:var(--text-muted);align-self:center">oppure incolla il testo qui sotto</span>
      </div>
      <textarea id="csv-textarea" rows="7"
        placeholder="name,hostname,ip,type,rack,rackU,sizeU,ports&#10;Switch-01,,192.168.1.1,switch,Rack A,,,24&#10;Server-02,,192.168.1.10,server,Rack A,,,4"
        style="width:100%;background:#0d1117;border:1px solid var(--panel-border);color:var(--text-main);padding:8px;border-radius:5px;font-family: var(--font-mono);font-size:0.78rem;resize:vertical"
        data-input="csv-preview"></textarea>
      <div id="csv-preview" style="margin-top:10px;display:none">
        <div style="font-size:var(--fs-xs);font-weight:600;margin-bottom:6px;color:var(--text-muted)">ANTEPRIMA</div>
        <div style="max-height:200px;overflow-y:auto">
          <table class="disc-table" id="csv-preview-table"></table>
        </div>
        <div id="csv-errors" style="margin-top:6px;font-size:var(--fs-xs);color:var(--fault-color)"></div>
      </div>
    </div>
    <div class="tool-modal-footer">
      <button class="toolbar-btn" data-act="csv-close" data-i18n="common.cancel">Annulla</button>
      <button class="toolbar-btn primary" id="csv-import-btn" data-act="csv-import" disabled><i class="fas fa-file-import"></i> <span data-i18n="common.import">Importa</span></button>
    </div>
  </div>
</div>

<!-- ===== MODAL IMPORTA LEASE DHCP ===== -->
<div id="dhcp-overlay" class="tool-modal-overlay" data-act="dhcp-backdrop">
  <div class="tool-modal wide" role="dialog" aria-modal="true" aria-labelledby="dhcp-title">
    <div class="tool-modal-header">
      <h3 id="dhcp-title"><i class="fas fa-table-list"></i> <span data-i18n="dhcp.title">Lease DHCP</span></h3>
      <button class="toolbar-btn" data-act="dhcp-close" style="padding:4px 8px"><i class="fas fa-times"></i></button>
    </div>
    <div class="tool-modal-body">
      <div class="tm-hint" data-i18n="dhcp.hint">Incolla o carica la tabella dei lease DHCP. Formati riconosciuti in automatico: ISC dhcpd, dnsmasq, Kea (CSV) e CSV generico.</div>
      <div id="dhcp-live-section" style="border:1px solid var(--panel-border);border-radius:6px;padding:10px;margin-bottom:10px;display:none">
        <div style="font-size:var(--fs-xs);font-weight:600;margin-bottom:8px;color:var(--text-muted)"><i class="fas fa-cloud-arrow-down"></i> <span data-i18n="dhcp.liveTitle">Recupero live dall'API del firewall</span></div>
        <div class="tm-row" style="gap:8px;flex-wrap:wrap;align-items:center;font-size:0.78rem">
          <select id="dhcp-live-vendor" data-change="dhcp-vendor" style="background:#0d1117;border:1px solid var(--panel-border);color:var(--text-main);padding:5px 7px;border-radius:4px">
            <option value="fortigate">Fortinet FortiGate</option>
            <option value="panos">Palo Alto PAN-OS</option>
            <option value="opnsense">OPNsense</option>
            <option value="mikrotik">MikroTik RouterOS</option>
          </select>
          <input id="dhcp-live-host" placeholder="Host / IP" style="background:#0d1117;border:1px solid var(--panel-border);color:var(--text-main);padding:5px 7px;border-radius:4px;width:140px">
          <input id="dhcp-live-port" type="number" placeholder="443" style="background:#0d1117;border:1px solid var(--panel-border);color:var(--text-main);padding:5px 7px;border-radius:4px;width:72px">
          <span style="display:inline-flex;align-items:center;gap:5px;flex:1 1 180px;min-width:0"><label id="dhcp-cred1-label" style="color:var(--text-muted)">API token</label><input id="dhcp-live-cred1" type="password" autocomplete="off" style="background:#0d1117;border:1px solid var(--panel-border);color:var(--text-main);padding:5px 7px;border-radius:4px;flex:1;min-width:0"></span>
          <span id="dhcp-cred2-wrap" style="display:none;align-items:center;gap:5px"><label id="dhcp-cred2-label" style="color:var(--text-muted)"></label><input id="dhcp-live-cred2" type="password" autocomplete="off" style="background:#0d1117;border:1px solid var(--panel-border);color:var(--text-main);padding:5px 7px;border-radius:4px;width:150px"></span>
          <label style="display:inline-flex;align-items:center;gap:4px;color:var(--text-muted)"><input type="checkbox" id="dhcp-live-insecure" style="width:auto"> <span data-i18n="dhcp.insecureTls">Salta verifica TLS (cert self-signed)</span></label>
          <button class="toolbar-btn" id="dhcp-live-fetch" data-act="dhcp-fetch-live" style="margin-left:auto"><i class="fas fa-cloud-arrow-down"></i> <span data-i18n="dhcp.fetch">Recupera</span></button>
        </div>
        <div style="font-size:var(--fs-2xs);color:var(--text-muted);margin-top:6px" data-i18n="dhcp.liveNote">Le credenziali servono solo per questo recupero e non vengono salvate.</div>
      </div>
      <div class="tm-row" style="gap:8px;margin-bottom:8px">
        <button class="toolbar-btn" data-act="dhcp-file-pick"><i class="fas fa-folder-open"></i> <span data-i18n="dhcp.chooseFile">Scegli file</span></button>
        <input type="file" id="dhcp-file" accept=".csv,.txt,.leases" style="display:none" data-change="dhcp-file">
        <span style="font-size:var(--fs-xs);color:var(--text-muted);align-self:center" data-i18n="dhcp.orPaste">oppure incolla il testo qui sotto</span>
      </div>
      <textarea id="dhcp-textarea" rows="8"
        placeholder="10.0.20.50  aa:bb:cc:dd:ee:ff  laptop-01&#10;ISC dhcpd / dnsmasq / Kea CSV / CSV (ip,mac,hostname)…"
        style="width:100%;background:#0d1117;border:1px solid var(--panel-border);color:var(--text-main);padding:8px;border-radius:5px;font-family: var(--font-mono);font-size:0.78rem;resize:vertical"
        data-input="dhcp-preview"></textarea>
      <div class="tm-row" style="gap:10px;align-items:center;margin-top:10px">
        <button class="toolbar-btn primary" id="dhcp-use-btn" data-act="dhcp-use" disabled><i class="fas fa-plus"></i> <span data-i18n="dhcp.add">Aggiungi</span></button>
        <div id="dhcp-summary" style="font-size:0.78rem;font-weight:600;color:var(--accent-color)"></div>
      </div>
      <div id="dhcp-errors" style="margin-top:6px;font-size:var(--fs-xs);color:var(--fault-color)"></div>
      <!-- Fonti lease in memoria (persistite nel progetto, una per DHCP server) -->
      <div id="dhcp-sources" style="margin-top:14px"></div>
      <!-- Opzione presenza: un lease RILASCIATO annota il grigio "probabilmente scollegato" (mai rosso) -->
      <div style="margin-top:14px;border-top:1px solid var(--panel-border);padding-top:12px">
        <label style="display:inline-flex;align-items:center;gap:8px;cursor:pointer;font-size:0.8rem">
          <input type="checkbox" id="dhcp-released-hint" data-change="dhcp-released-hint" style="width:auto">
          <span><i class="fas fa-plug-circle-xmark" style="color:var(--text-muted)"></i> <span data-i18n="dhcp.releasedHint">Lease rilasciato = probabilmente scollegato</span></span>
        </label>
        <div style="font-size:var(--fs-2xs);color:var(--text-muted);margin-top:6px" data-i18n="dhcp.releasedHintDesc">Se un dispositivo documentato non dà segni di vita ma ha rilasciato il lease DHCP, la Verifica lo segna come probabilmente scollegato: resta grigio, mai rosso. Solo dai lease rilasciati, mai da quelli scaduti.</div>
      </div>
    </div>
    <div class="tool-modal-footer" style="justify-content:space-between">
      <button class="toolbar-btn" id="dhcp-clear-btn" data-act="dhcp-clear"><i class="fas fa-trash"></i> <span data-i18n="dhcp.clearAll">Svuota tutto</span></button>
      <button class="toolbar-btn primary" id="dhcp-verify-btn" data-act="dhcp-verify"><i class="fas fa-clipboard-check"></i> <span data-i18n="dhcp.verifyNow">Verifica ora</span></button>
    </div>
  </div>
</div>

<!-- PDF export options -->
<div id="pdf-export-overlay" class="tool-modal-overlay" onclick="if(event.target===this)closePdfExportOptions()">
  <div class="tool-modal" style="width:520px;max-width:96vw" role="dialog" aria-modal="true" aria-labelledby="pdf-export-title">
    <div class="tool-modal-header">
      <h3 id="pdf-export-title"><i class="fas fa-file-pdf"></i> <span data-i18n="pdf.title">Opzioni export PDF</span></h3>
      <button class="toolbar-btn" onclick="closePdfExportOptions()" style="padding:4px 8px"><i class="fas fa-times"></i></button>
    </div>
    <div class="tool-modal-body">
      <!-- Il dossier di consegna (copertina in testa, storia modifiche in coda) è SEMPRE
           nel PDF: la casella è accesa e bloccata, e export.js lo rimette comunque nelle
           opzioni anche se qualcuno chiamasse exportPDF() senza. Tutto il resto parte
           spento (PDF_EXPORT_DEFAULTS) e lo accende chi lo vuole. -->
      <label style="display:flex;align-items:flex-start;gap:8px;margin-bottom:12px;padding:8px 10px;border:1px solid var(--panel-border);border-radius:6px">
        <input type="checkbox" id="pdfopt-dossier" checked disabled style="margin-top:3px">
        <span>
          <span data-i18n="pdf.dossier">Dossier di consegna</span>
          <small data-i18n="pdf.dossierSub" style="display:block;color:var(--text-muted)">Copertina e storia modifiche: sempre incluse</small>
        </span>
      </label>
      <div style="display:flex;gap:8px;justify-content:flex-end;margin-bottom:10px">
        <button class="toolbar-btn" style="padding:3px 8px;font-size:var(--fs-xs)" onclick="setPdfExportAll(true)" data-i18n="common.selectAll">Seleziona tutto</button>
        <button class="toolbar-btn" style="padding:3px 8px;font-size:var(--fs-xs)" onclick="setPdfExportAll(false)" data-i18n="common.deselectAll">Deseleziona tutto</button>
      </div>
      <div style="display:flex;flex-direction:column;gap:8px">
        <label style="display:flex;align-items:center;gap:8px"><input type="checkbox" id="pdfopt-plan" onchange="syncPdfExportUi()"> <span data-i18n="pdf.plan">Planimetria (pagina 1)</span></label>
        <label style="display:flex;align-items:center;gap:8px;padding-left:22px;opacity:.95"><input type="checkbox" id="pdfopt-bg"> <span data-i18n="pdf.bg">Include immagine di sfondo planimetria</span></label>
        <label style="display:flex;align-items:center;gap:8px"><input type="checkbox" id="pdfopt-overview"> <span data-i18n="pdf.overview">Dashboard (sintesi in testa)</span></label>
        <label style="display:flex;align-items:center;gap:8px"><input type="checkbox" id="pdfopt-inventory"> <span data-i18n="pdf.inventory">Inventario cavi</span></label>
        <label style="display:flex;align-items:center;gap:8px"><input type="checkbox" id="pdfopt-asbuilt"> <span data-i18n="pdf.asbuilt">Tracciato cablaggio (As-Built)</span></label>
        <label style="display:flex;align-items:center;gap:8px"><input type="checkbox" id="pdfopt-racks"> <span data-i18n="pdf.racks">Vista rack</span></label>
        <label style="display:flex;align-items:center;gap:8px"><input type="checkbox" id="pdfopt-ports"> <span data-i18n="pdf.ports">Assegnazione porte</span></label>
        <label style="display:flex;align-items:center;gap:8px"><input type="checkbox" id="pdfopt-vlans"> <span data-i18n="pdf.vlans">Sommario VLAN</span></label>
        <label style="display:flex;align-items:center;gap:8px"><input type="checkbox" id="pdfopt-topology"> <span data-i18n="pdf.topology">Topologia LLDP/CDP</span></label>
        <label style="display:flex;align-items:center;gap:8px"><input type="checkbox" id="pdfopt-vms"> <span data-i18n="pdf.vms">Macchine virtuali (per host)</span></label>
        <label style="display:flex;align-items:center;gap:8px"><input type="checkbox" id="pdfopt-pdu"> <span data-i18n="pdf.pdu">Alimentazione: PDU e prese</span></label>
        <label style="display:flex;align-items:center;gap:8px"><input type="checkbox" id="pdfopt-spare"> <span data-i18n="pdf.spare">Porte libere (capacità per rack)</span></label>
        <label style="display:flex;align-items:center;gap:8px"><input type="checkbox" id="pdfopt-assets"> <span data-i18n="pdf.assets">Registro asset (inventario dispositivi + note)</span></label>
        <label style="display:flex;align-items:center;gap:8px"><input type="checkbox" id="pdfopt-recovery"> <span data-i18n="pdf.recovery">Ripristinabilità (backup · identità · posizione)</span></label>
        <label style="display:flex;align-items:center;gap:8px"><input type="checkbox" id="pdfopt-wan"> <span data-i18n="pdf.wan">WAN: mappa delle sedi e ripristino dei collegamenti</span></label>
      </div>
      <div id="pdfopt-hint" data-i18n="impexp.pickSections" style="margin-top:12px;font-size:0.76rem;color:var(--text-muted)">Seleziona le sezioni che vuoi includere nel PDF.</div>
    </div>
    <div class="tool-modal-footer">
      <button class="toolbar-btn" onclick="closePdfExportOptions()" data-i18n="common.cancel">Annulla</button>
      <button class="toolbar-btn primary" onclick="confirmPdfExport()"><i class="fas fa-file-export"></i> <span data-i18n="common.export">Esporta</span></button>
    </div>
  </div>
</div>

<!-- Cable-label export options -->
<div id="label-export-overlay" class="tool-modal-overlay" onclick="if(event.target===this)closeLabelExportOptions()">
  <div class="tool-modal" style="width:520px;max-width:96vw" role="dialog" aria-modal="true" aria-labelledby="label-export-title">
    <div class="tool-modal-header">
      <h3 id="label-export-title"><i class="fas fa-tags"></i> <span data-i18n="lbl.title">Esporta etichette cavi</span></h3>
      <button class="toolbar-btn" onclick="closeLabelExportOptions()" style="padding:4px 8px"><i class="fas fa-times"></i></button>
    </div>
    <div class="tool-modal-body">
      <div style="display:flex;flex-direction:column;gap:12px">
        <label style="display:flex;flex-direction:column;gap:4px">
          <span style="font-size:0.8rem;font-weight:600" data-i18n="lbl.format">Formato</span>
          <select id="lblexp-format" onchange="syncLabelExportUi()" style="padding:6px">
            <option value="csv">CSV (mail-merge: Avery Design&amp;Print / Dymo Connect / Word)</option>
            <option value="avery-l7651">PDF — Avery L7651 (A4, 65/foglio, 38×21mm)</option>
            <option value="avery-22806">PDF — Avery 22806 (Letter, 12/foglio, quadrata)</option>
            <option value="dymo-99010">PDF — Dymo 99010 (LabelWriter, 89×28mm)</option>
            <option value="dymo-11353">PDF — Dymo 11353 (LabelWriter, 25×13mm)</option>
            <option value="generic-grid">PDF — Griglia generica (configurabile)</option>
          </select>
        </label>

        <div style="display:flex;flex-direction:column;gap:4px">
          <span style="font-size:0.8rem;font-weight:600" data-i18n="lbl.fields">Campi da esportare</span>
          <div id="lblexp-fields" style="display:grid;grid-template-columns:1fr 1fr;gap:4px 14px"></div>
        </div>

        <label id="lblexp-wrap-row" style="display:flex;align-items:center;gap:8px">
          <input type="checkbox" id="lblexp-wrap" onchange="syncLabelExportUi()"> <span data-i18n="lbl.wrap">Etichetta a bandierina (ID ripetuto, leggibile avvolgendo il cavo)</span>
        </label>

        <div id="lblexp-grid" style="display:none;flex-direction:column;gap:6px;border-top:1px solid var(--panel-border);padding-top:10px">
          <span style="font-size:0.8rem;font-weight:600" data-i18n="lbl.gridTitle">Griglia generica (mm)</span>
          <div style="display:grid;grid-template-columns:repeat(4,1fr);gap:6px">
            <label style="font-size:var(--fs-2xs)">colonne<input type="number" id="lblg-cols" value="4" min="1" style="width:100%" oninput="syncLabelExportUi()"></label>
            <label style="font-size:var(--fs-2xs)">righe<input type="number" id="lblg-rows" value="10" min="1" style="width:100%" oninput="syncLabelExportUi()"></label>
            <label style="font-size:var(--fs-2xs)">largh. et.<input type="number" id="lblg-labelW" value="48" min="1" style="width:100%" oninput="syncLabelExportUi()"></label>
            <label style="font-size:var(--fs-2xs)">alt. et.<input type="number" id="lblg-labelH" value="25" min="1" style="width:100%" oninput="syncLabelExportUi()"></label>
            <label style="font-size:var(--fs-2xs)">marg. sx<input type="number" id="lblg-marginLeft" value="8" min="0" style="width:100%"></label>
            <label style="font-size:var(--fs-2xs)">marg. alto<input type="number" id="lblg-marginTop" value="12" min="0" style="width:100%"></label>
            <label style="font-size:var(--fs-2xs)">passo X<input type="number" id="lblg-pitchX" value="49" min="1" style="width:100%"></label>
            <label style="font-size:var(--fs-2xs)">passo Y<input type="number" id="lblg-pitchY" value="27" min="1" style="width:100%"></label>
          </div>
        </div>

        <div id="lblexp-preview" style="border-top:1px solid var(--panel-border);padding-top:10px"></div>
      </div>
      <div id="lblexp-hint" style="margin-top:12px;font-size:0.76rem;color:var(--text-muted)"></div>
    </div>
    <div class="tool-modal-footer">
      <button class="toolbar-btn" onclick="closeLabelExportOptions()" data-i18n="common.cancel">Annulla</button>
      <button class="toolbar-btn primary" onclick="confirmLabelExport()"><i class="fas fa-file-export"></i> <span data-i18n="common.export">Esporta</span></button>
    </div>
  </div>
</div>

<!-- Shared segment bind existing device -->
<div id="shared-bind-overlay" class="tool-modal-overlay" data-act="ssbind-backdrop">
  <div class="tool-modal" style="width:620px;max-width:96vw" role="dialog" aria-modal="true" aria-labelledby="shared-bind-title">
    <div class="tool-modal-header">
      <h3 id="shared-bind-title"><i class="fas fa-link"></i> Collega a dispositivo esistente</h3>
      <button class="toolbar-btn" data-act="ssbind-close" style="padding:4px 8px"><i class="fas fa-times"></i></button>
    </div>
    <div id="shared-bind-body" class="tool-modal-body"></div>
    <div class="tool-modal-footer">
      <button class="toolbar-btn" id="shared-bind-back" data-act="ssbind-back" style="display:none"><i class="fas fa-arrow-left"></i> <span data-i18n="common.back">Indietro</span></button>
      <button class="toolbar-btn primary" data-act="ssbind-close" data-i18n="common.close">Chiudi</button>
    </div>
  </div>
</div>

<!-- ===== SEDI E COLLEGAMENTI (layer multi-sede, Fase 1) =====
     Il piano SOPRA il progetto: un progetto documenta un edificio, questo
     pannello documenta come gli edifici si parlano. Corpo e piè di pagina sono
     costruiti interamente da src/app-inter-site.js (mappa + form + coerenza):
     qui c'è solo il guscio, così l'a11y del `.tool-modal-overlay` (focus-trap +
     Esc) vale anche per lui senza codice dedicato. -->
<div id="org-overlay" class="tool-modal-overlay" data-act="org-backdrop">
  <div id="org-modal" class="tool-modal" style="width:min(1150px,96vw);max-height:92vh" role="dialog" aria-modal="true" aria-labelledby="org-title">
    <div class="tool-modal-header um-header">
      <h3 id="org-title"><i class="fas fa-sitemap"></i> <span data-i18n="org.title">Sedi e collegamenti</span></h3>
      <button class="toolbar-btn" data-act="org-close" style="padding:4px 8px" data-i18n-tip="common.close" data-tip="Chiudi"><i class="fas fa-times"></i></button>
    </div>
    <div id="org-body" class="tool-modal-body"></div>
    <div id="org-footer" class="tool-modal-footer"></div>
  </div>
</div>

</div>

<script src="/lib/i18n.js"></script>
<script src="/lib/cidr.js"></script>
<script src="/lib/snmp-target.js"></script>
<script src="/lib/host-merge.js"></script>
<script src="/lib/ipam.js"></script>
<script src="/lib/ipam-audit.js"></script>
<script src="/lib/lag-audit.js"></script>
<script src="/lib/lag-reconcile.js"></script>
<script src="/lib/netnames.js"></script>
<script src="/lib/name-abbrev.js"></script>
<script src="/lib/linkstate.js"></script>
<script src="/lib/link-model.js"></script>
<script src="/lib/cabling.js"></script>
<script src="/lib/topo-lines.js"></script>
<script src="/lib/cable-labels.js"></script>
<script src="/lib/cable-validate.js"></script>
<script src="/lib/port-id.js"></script>
<script src="/lib/correlate.js"></script>
<script src="/lib/topology-plan.js"></script>
<script src="/lib/discovery-history.js"></script>
<script src="/lib/temporal-confidence.js"></script>
<script src="/lib/proof.js"></script>
<script src="/lib/certainty.js"></script>
<script src="/lib/presence-store.js"></script>
<script src="/lib/cable-reconcile.js"></script>
<script src="/lib/frontpanel.js"></script>
<script src="/lib/stack.js"></script>
<script src="/lib/ha-pair.js"></script>
<script src="/lib/mac-class.js"></script>
<script src="/lib/device-signatures.js"></script>
<script src="/lib/device-patterns.js"></script>
<script src="/lib/spare-ports.js"></script>
<script src="/lib/l3-gateway.js"></script>
<script src="/lib/wave-path.js"></script>
<script src="/lib/wifi-spec.js"></script>
<script src="/lib/radio.js"></script>
<script src="/lib/vm-nics.js"></script>
<script src="/lib/os-icon-sprite.js"></script>
<script src="/lib/os-icon.js"></script>
<script src="/lib/vlan-trunk.js"></script>
<script src="/lib/wifi-vlan-check.js"></script>
<script src="/lib/panel-skin.js"></script>
<script src="/lib/power-mib.js"></script>
<!-- field-provenance PRIMA di identity-reconcile: la provenienza di un valore copiato da una
     lettura (markFromRead…) la usa anche lui, e il registro dei campi a doppia natura sta qui. -->
<script src="/lib/field-provenance.js"></script>
<!-- identity-reconcile PRIMA di chi giudica l'identità hardware: la Verifica, la
     Panoramica e il dossier PDF devono chiedere alla STESSA regola se una misura
     è riconfermata o è solo l'ultimo noto. -->
<script src="/lib/identity-reconcile.js"></script>
<script src="/lib/drift-report.js"></script>
<script src="/lib/drift-snapshot.js"></script>
<script src="/lib/backup-ref.js"></script>
<script src="/lib/project-schema.js"></script>
<script src="/lib/project-format.js"></script>
<script src="/lib/project-networks.js"></script>
<script src="/lib/ai-grounding.js"></script>
<script src="/lib/snmp-silence.js"></script>
<script src="/lib/verify-trend.js"></script>
<script src="/lib/ai-draft.js"></script>
<script src="/lib/onboarding.js"></script>
<script src="/lib/subbar-stats.js"></script>
<script src="/lib/hw-capabilities.js"></script>
<script src="/lib/dhcp-lease.js"></script>
<script src="/lib/update-summary.js"></script>
<script src="/lib/drift-adopt.js"></script>
<script src="/lib/audit-log.js"></script>
<script src="/lib/handoff.js"></script>
<!-- port-state PRIMA dei due export: gli danno il colore delle porte «spente a
     mano» e «senza link», e la regola dev'essere la stessa del rack a schermo. -->
<script src="/lib/port-state.js"></script>
<script src="/lib/xml-escape.js"></script>
<script src="/lib/drawio-export.js"></script>
<script src="/export.js"></script>
<!-- Bundle esbuild dei moduli glue migrati a ESM (vedi build.js / src/main.js).
     Caricato per ULTIMO: i globali legacy esistono già; i moduli convertiti li
     leggono via window e ri-pubblicano la propria API (es. app-audit). -->
<script src="/dist/app.bundle.js"></script>
</body>
</html>