/* === MOD_002 · NET_FORGE — Styles ===
 * Extracted from mod_002_netmap.js (was MOD002_CSS template literal).
 * All rules are namespaced under .m002-* / body.m002-tool-* so global pages
 * stay unaffected even when this file is loaded.
 *
 * Inlined JS-constant values (must be kept in sync with the JS):
 *   190ms / 270ms / 350ms  ←  DETAIL_FADE_OUT_MS (190), DETAIL_ANIM_MS (350)
 *   130 / 260              ←  RADIAL_OUTER_R (130) and *2
 *   406.84                 ←  (Math.PI * (RADIAL_OUTER_R - 0.5)).toFixed(2)
 * If you change one of those constants in mod_002_netmap.js, regenerate the
 * matching numbers here too.
 */
.m002-host{position:absolute;inset:0;overflow:hidden;font-family:'Inter','Rajdhani',sans-serif;font-weight:500;color:#e8e8ee;background:radial-gradient(ellipse at 50% 0%,#0d0d14 0%,#06060a 70%);display:grid;grid-template-columns:220px 1fr 320px;grid-template-rows:1fr;isolation:isolate;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;text-rendering:optimizeLegibility;font-feature-settings:'cv11' 1,'ss01' 1,'tnum' 1,'calt' 1;}
.m002-leftpanel{background:rgba(8,8,14,0.92);border-right:1px solid #1a1a22;padding:14px 12px;overflow:hidden;display:flex;flex-direction:column;gap:14px;min-height:0;}
.m002-leftpanel-spacer{flex:0 0 8px;}
.m002-panel-section.m002-panel-section--legend{flex:1 1 auto;min-height:0;overflow:hidden;display:flex;flex-direction:column;}
.m002-panel-section.m002-panel-section--legend .m002-vlan-legend-body,
.m002-panel-section.m002-panel-section--legend .m002-subnet-legend-body{flex:1 1 auto;min-height:0;overflow:hidden;}
/* Layer-aware legends: VLAN legend lives in the VLAN layer; subnet legend
   in the routing layer. The other two layers see the off-topic ones hidden
   so the left rail stays focused on what's relevant to the current view. */
.m002-host[data-active-layer="vlan"] .m002-panel-section--subnets,
.m002-host[data-active-layer="physical"] .m002-panel-section--subnets,
.m002-host[data-active-layer="physical"] .m002-panel-section--legend:not(.m002-panel-section--subnets){display:none;}
.m002-host[data-active-layer="routing"] .m002-panel-section--legend:not(.m002-panel-section--subnets){display:none;}
.m002-rightpanel{background:rgba(8,8,14,0.92);border-left:1px solid #1a1a22;padding:14px;overflow-y:auto;display:flex;flex-direction:column;gap:10px;min-height:0;}
.m002-center{position:relative;overflow:hidden;}
.m002-panel-section{display:flex;flex-direction:column;gap:6px;}
.m002-panel-title{margin:0 0 4px 0;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;color:#5a5f6e;letter-spacing:2px;font-weight:500;text-transform:uppercase;}
.m002-panel-grid{display:flex;flex-direction:column;gap:3px;}
.m002-panel-hints{display:flex;flex-direction:column;gap:2px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:9px;color:#5a5f6e;letter-spacing:1.4px;padding-top:6px;border-top:1px solid #1a1a22;}
.m002-prefs-btn{flex:0 0 auto;display:flex;align-items:center;gap:8px;background:transparent;border:1px solid transparent;color:#7a7f8e;padding:6px 10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;letter-spacing:1.5px;cursor:pointer;margin-top:auto;}
.m002-prefs-btn:hover{border-color:#1a1a22;color:#e8e8ee;}
.m002-prefs-glyph{font-size:14px;line-height:1;}
.m002-prefs-row{display:flex;align-items:flex-start;gap:10px;justify-content:space-between;padding:8px 10px;background:#06060a;border:1px solid #1a1a22;}
.m002-prefs-row > span{display:flex;flex-direction:column;gap:2px;}
.m002-prefs-label{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;color:#e8e8ee;letter-spacing:1.4px;}
.m002-prefs-sublabel{font-family:'Inter','Rajdhani',sans-serif;font-size:11px;color:#7a7f8e;}
.m002-prefs-row input[type="checkbox"]{flex:0 0 auto;width:16px;height:16px;accent-color:#ff003c;cursor:pointer;}
.m002-prefs-section{display:flex;flex-direction:column;gap:6px;margin-bottom:14px;}
.m002-prefs-section-head{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:9px;letter-spacing:2px;color:#7a7f8e;padding:0 2px 4px;border-bottom:1px dashed #1a1a22;}
.m002-style-picker{display:flex;flex-direction:column;gap:6px;}
.m002-style-pill{display:flex;flex-direction:column;align-items:flex-start;gap:2px;padding:8px 10px;background:#06060a;border:1px solid #1a1a22;color:#7a7f8e;cursor:pointer;text-align:left;font-family:inherit;transition:border-color 0.15s,color 0.15s,background 0.15s;}
.m002-style-pill:hover{border-color:#2a2a36;color:#e8e8ee;}
.m002-style-pill.active{border-color:#ff003c;background:rgba(255,0,60,0.06);color:#e8e8ee;box-shadow:0 0 0 1px rgba(255,0,60,0.25),inset 0 0 12px rgba(255,0,60,0.08);}
.m002-style-pill-label{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;letter-spacing:1.6px;color:inherit;}
.m002-style-pill.active .m002-style-pill-label{color:#ff003c;text-shadow:0 0 6px rgba(255,0,60,0.5);}
.m002-style-pill-desc{font-family:'Inter','Rajdhani',sans-serif;font-size:11px;color:#7a7f8e;}
/* === Visual styles — keyed off host[data-grid-style] === */
/* Futuristic (default): grid layers visible (no override needed) */

/* === SKETCH style: light-mode pencil-on-graph-paper === */
/* Paper background + cream tint */
.m002-host[data-grid-style="sketch"]{background:#faf6ed;color:#2a2a30;}
.m002-host[data-grid-style="sketch"] .m002-tint{background:#faf6ed;}
/* Paper-grid: faint blue squares like real Karopapier */
.m002-host[data-grid-style="sketch"] .m002-grid-bg{fill:url(#m002-grid-sketch-minor);}
.m002-host[data-grid-style="sketch"] .m002-grid-bg2{fill:url(#m002-grid-sketch-major);}
/* Side panels: warm off-white with graphite hairlines */
.m002-host[data-grid-style="sketch"] .m002-leftpanel{background:rgba(245,240,228,0.96);border-right-color:#c8c2b0;}
.m002-host[data-grid-style="sketch"] .m002-rightpanel{background:rgba(245,240,228,0.96);border-left-color:#c8c2b0;}
/* Panel titles, hints, section heads — graphite muted */
.m002-host[data-grid-style="sketch"] .m002-panel-title,
.m002-host[data-grid-style="sketch"] .m002-prefs-section-head,
.m002-host[data-grid-style="sketch"] .m002-panel-hints,
.m002-host[data-grid-style="sketch"] .m002-cheat-hint,
.m002-host[data-grid-style="sketch"] .m002-cheat-title,
.m002-host[data-grid-style="sketch"] .m002-insp-empty,
.m002-host[data-grid-style="sketch"] .m002-insp-empty-title{color:#7a7568;}
.m002-host[data-grid-style="sketch"] .m002-panel-hints{border-top-color:#d6d0c0;}
.m002-host[data-grid-style="sketch"] .m002-cheat-hint{border-bottom-color:#d6d0c0;}
.m002-host[data-grid-style="sketch"] .m002-prefs-section-head{border-bottom-color:#d6d0c0;color:#7a7568;}
/* Forge / Tools palette buttons */
.m002-host[data-grid-style="sketch"] .m002-pal-btn{background:#fefcf6;border:1px solid #c8c2b0;color:#3a3a35;box-shadow:none;}
.m002-host[data-grid-style="sketch"] .m002-pal-btn:hover{border-color:#2a2a30;background:#fffefa;color:#2a2a30;}
.m002-host[data-grid-style="sketch"] .m002-pal-btn.active{border-color:#c44b3c;background:rgba(196,75,60,0.06);color:#2a2a30;box-shadow:none;}
.m002-host[data-grid-style="sketch"] .m002-pal-glyph{color:inherit;}
/* Settings (gear) button + style picker pills */
.m002-host[data-grid-style="sketch"] .m002-prefs-btn{color:#7a7568;}
.m002-host[data-grid-style="sketch"] .m002-prefs-btn:hover{border-color:#c8c2b0;color:#2a2a30;}
.m002-host[data-grid-style="sketch"] .m002-prefs-row{background:#fefcf6;border-color:#c8c2b0;}
.m002-host[data-grid-style="sketch"] .m002-prefs-label{color:#2a2a30;}
.m002-host[data-grid-style="sketch"] .m002-prefs-sublabel{color:#7a7568;}
.m002-host[data-grid-style="sketch"] .m002-prefs-row input[type="checkbox"]{accent-color:#c44b3c;}
.m002-host[data-grid-style="sketch"] .m002-style-pill{background:#fefcf6;border-color:#c8c2b0;color:#3a3a35;}
.m002-host[data-grid-style="sketch"] .m002-style-pill:hover{border-color:#2a2a30;background:#fffefa;color:#2a2a30;}
.m002-host[data-grid-style="sketch"] .m002-style-pill.active{border-color:#c44b3c;background:rgba(196,75,60,0.06);color:#2a2a30;box-shadow:none;}
.m002-host[data-grid-style="sketch"] .m002-style-pill.active .m002-style-pill-label{color:#c44b3c;text-shadow:none;}
.m002-host[data-grid-style="sketch"] .m002-style-pill-label{color:inherit;}
.m002-host[data-grid-style="sketch"] .m002-style-pill-desc{color:#7a7568;}
/* Layer bar (top of canvas) */
.m002-host[data-grid-style="sketch"] .m002-layerbar{background:rgba(254,252,246,0.92);border-color:#c8c2b0;box-shadow:none;backdrop-filter:none;}
.m002-host[data-grid-style="sketch"] .m002-layer-pill{color:#7a7568;background:transparent;border-color:transparent;}
.m002-host[data-grid-style="sketch"] .m002-layer-pill:hover{color:#2a2a30;}
.m002-host[data-grid-style="sketch"] .m002-layer-pill.active{color:#c44b3c;border-color:#c44b3c;background:rgba(196,75,60,0.05);text-shadow:none;}
/* Inspector (right panel) — labels, sub-headers, body text, dividers */
.m002-host[data-grid-style="sketch"] .m002-insp-head,
.m002-host[data-grid-style="sketch"] .m002-insp-id{color:#7a7568;}
.m002-host[data-grid-style="sketch"] .m002-insp-title{color:#2a2a30;text-shadow:none;}
.m002-host[data-grid-style="sketch"] .m002-link-hint{color:#7a7568;}
.m002-host[data-grid-style="sketch"] .m002-field-label{color:#7a7568;}
.m002-host[data-grid-style="sketch"] .m002-field input,
.m002-host[data-grid-style="sketch"] .m002-field select,
.m002-host[data-grid-style="sketch"] .m002-field textarea,
.m002-host[data-grid-style="sketch"] input.m002-input,
.m002-host[data-grid-style="sketch"] select.m002-select,
.m002-host[data-grid-style="sketch"] textarea.m002-textarea{background:#fefcf6;color:#2a2a30;border-color:#c8c2b0;}
.m002-host[data-grid-style="sketch"] .m002-action,
.m002-host[data-grid-style="sketch"] button.m002-btn{background:#fefcf6;border-color:#c8c2b0;color:#2a2a30;box-shadow:none;}
.m002-host[data-grid-style="sketch"] .m002-action:hover,
.m002-host[data-grid-style="sketch"] button.m002-btn:hover{border-color:#c44b3c;color:#c44b3c;background:#fffefa;}
/* Toast / status bar */
.m002-host[data-grid-style="sketch"] .m002-toast{background:rgba(254,252,246,0.96);border-color:#c8c2b0;color:#2a2a30;box-shadow:0 2px 6px rgba(40,40,30,0.12);text-shadow:none;}
.m002-host[data-grid-style="sketch"] .m002-statusbar{background:rgba(245,240,228,0.92);border-color:#c8c2b0;color:#7a7568;}
/* SVG canvas — devices, links, ports — pencil graphite, no glow */
.m002-host[data-grid-style="sketch"] .m002-dev-bg{fill:#fefcf6;stroke:#3a3a35;stroke-width:1.2;filter:none;}
.m002-host[data-grid-style="sketch"] .m002-device.selected .m002-dev-bg{stroke:#c44b3c;stroke-width:1.8;stroke-dasharray:4 3;}
.m002-host[data-grid-style="sketch"] .m002-device:hover .m002-dev-bg{stroke:#2a2a30;stroke-width:1.6;}
.m002-host[data-grid-style="sketch"] .m002-dev-type,
.m002-host[data-grid-style="sketch"] .m002-dev-name,
.m002-host[data-grid-style="sketch"] .m002-dev-notes,
.m002-host[data-grid-style="sketch"] .m002-dev-ip,
.m002-host[data-grid-style="sketch"] .m002-dev-ref-target,
.m002-host[data-grid-style="sketch"] .m002-dev-ref-hint{fill:#2a2a30;text-shadow:none;}
.m002-host[data-grid-style="sketch"] .m002-dev-type{fill:#7a7568;}
.m002-host[data-grid-style="sketch"] .m002-link{stroke:#3a3a35;}
.m002-host[data-grid-style="sketch"] .m002-link-flow{stroke:#c44b3c;}
.m002-host[data-grid-style="sketch"] .m002-stack-envelope{fill:rgba(254,252,246,0.6);stroke:#7a7568;stroke-dasharray:6 4;}
.m002-host[data-grid-style="sketch"] .m002-stack-collapsed .m002-dev-bg{fill:#f5efdf;stroke:#3a3a35;}
/* Drop-target glows in the futuristic style → soft tinted dashed outlines */
.m002-host[data-grid-style="sketch"] .m002-drop-ok{stroke:#6b8a6b !important;stroke-dasharray:5 3;filter:none !important;}
.m002-host[data-grid-style="sketch"] .m002-drop-bad{stroke:#c44b3c !important;stroke-dasharray:5 3;filter:none !important;}
/* Status pulses & tag dots — desaturate the neon */
.m002-host[data-grid-style="sketch"] .m002-pulse,
.m002-host[data-grid-style="sketch"] .m002-tag-dot{background:#c44b3c;box-shadow:none;}
/* Modals & port panel */
.m002-host[data-grid-style="sketch"] .m002-modal-backdrop{background:rgba(40,38,30,0.45);}
.m002-host[data-grid-style="sketch"] .m002-modal,
.m002-host[data-grid-style="sketch"] .m002-port-panel{background:#fefcf6;border-color:#c8c2b0;color:#2a2a30;box-shadow:0 8px 24px rgba(40,40,30,0.18);}
/* Map switcher (top-left bar) + zone bar */
.m002-host[data-grid-style="sketch"] .m002-tabbar{background:rgba(245,240,228,0.92);border-color:#c8c2b0;backdrop-filter:none;}
.m002-host[data-grid-style="sketch"] .m002-tab{color:#7a7568;border-color:transparent;background:transparent;}
.m002-host[data-grid-style="sketch"] .m002-tab:hover{color:#2a2a30;border-color:#c8c2b0;}
.m002-host[data-grid-style="sketch"] .m002-tab.active{background:rgba(196,75,60,0.06);border-color:#c44b3c;color:#c44b3c;}
.m002-host[data-grid-style="sketch"] .m002-tab-dot{background:#c8c2b0;}
.m002-host[data-grid-style="sketch"] .m002-tab.active .m002-tab-dot{background:#c44b3c;box-shadow:none;}
.m002-host[data-grid-style="sketch"] .m002-tab-name input.m002-tab-rename-input{color:#c44b3c;caret-color:#c44b3c;}
.m002-host[data-grid-style="sketch"] .m002-tab-close{color:#7a7568;}
.m002-host[data-grid-style="sketch"] .m002-tab-close:hover{background:rgba(196,75,60,0.12);color:#c44b3c;}
.m002-host[data-grid-style="sketch"] .m002-tab-add,
.m002-host[data-grid-style="sketch"] .m002-tab-more{background:rgba(254,252,246,0.92);border-color:#c8c2b0;color:#7a7568;backdrop-filter:none;}
.m002-host[data-grid-style="sketch"] .m002-tab-add:hover{border-color:#c44b3c;color:#c44b3c;}
.m002-host[data-grid-style="sketch"] .m002-tab-more:hover{border-color:#2a2a30;color:#2a2a30;}
.m002-host[data-grid-style="sketch"] .m002-tab-menu,
.m002-host[data-grid-style="sketch"] .m002-tab-ctx{background:rgba(254,252,246,0.97);border-color:#c8c2b0;color:#2a2a30;backdrop-filter:none;}
.m002-host[data-grid-style="sketch"] .m002-menu-sep{background:#d6d0c0;}
.m002-host[data-grid-style="sketch"] .m002-zonebar{background:rgba(245,240,228,0.92);border-color:#c8c2b0;backdrop-filter:none;}
.m002-host[data-grid-style="sketch"] .m002-zone-pill{color:#7a7568;border-color:transparent;background:transparent;}
.m002-host[data-grid-style="sketch"] .m002-zone-pill:hover{color:#2a2a30;}
.m002-host[data-grid-style="sketch"] .m002-zone-pill.active{background:rgba(196,75,60,0.06);border-color:#c44b3c;color:#c44b3c;}
/* Cheatsheet keys + dividers */
.m002-host[data-grid-style="sketch"] .m002-kbd{background:#fefcf6;border-color:#c8c2b0;color:#2a2a30;box-shadow:none;}
.m002-host[data-grid-style="sketch"] .m002-pal-sep{background:#d6d0c0;}
.m002-host[data-grid-style="sketch"] .m002-field-static{background:#fefcf6;border-color:#c8c2b0;color:#7a7568;}
/* Stack envelope — the dashed-rect "//STACK · NAME" wrapper around grouped devices */
.m002-host[data-grid-style="sketch"] .m002-stack-env-bg{fill:rgba(254,252,246,0.88) !important;stroke-width:1.4;}
.m002-host[data-grid-style="sketch"] .m002-stack-envelope{filter:none !important;}
.m002-host[data-grid-style="sketch"] .m002-stack-envelope:hover,
.m002-host[data-grid-style="sketch"] .m002-stack-envelope.m002-selected{filter:none !important;}
.m002-host[data-grid-style="sketch"] .m002-stack-collapsed .m002-dev-bg{fill:#fefcf6;stroke-width:1.4;}
.m002-host[data-grid-style="sketch"] .m002-stack-ghost{fill:rgba(254,252,246,0.6);}
/* VLAN + subnet legend — rows, inputs, add-button on cream paper */
.m002-host[data-grid-style="sketch"] .m002-vlan-row{background:#fefcf6;border-color:#c8c2b0;}
.m002-host[data-grid-style="sketch"] .m002-vlan-row:hover{border-color:#2a2a30;}
.m002-host[data-grid-style="sketch"] .m002-vlan-row.is-solo{background:rgba(196,75,60,0.06);box-shadow:none;}
.m002-host[data-grid-style="sketch"] .m002-vlan-row.is-solo .m002-vlan-row-dot{box-shadow:none;}
.m002-host[data-grid-style="sketch"] .m002-vlan-row-dot{box-shadow:none;}
.m002-host[data-grid-style="sketch"] .m002-vlan-legend-input{background:#fefcf6;border-color:#c8c2b0;color:#2a2a30;}
.m002-host[data-grid-style="sketch"] .m002-vlan-legend-input:focus{border-color:#c44b3c;}
.m002-host[data-grid-style="sketch"] .m002-vlan-legend-add-btn{border-color:#c44b3c;color:#c44b3c;background:#fefcf6;}
.m002-host[data-grid-style="sketch"] .m002-vlan-legend-add-btn:hover{background:rgba(196,75,60,0.08);}
.m002-host[data-grid-style="sketch"] .m002-vlan-legend-rm{opacity:.5;}
.m002-host[data-grid-style="sketch"] .m002-vlan-legend-rm:hover{opacity:1;}
/* Same treatment for VLAN chip-buttons (port-modal etc.) */
.m002-host[data-grid-style="sketch"] .m002-vlan-chip-btn{background:#fefcf6;border-color:#c8c2b0;color:#2a2a30;}
.m002-host[data-grid-style="sketch"] .m002-vlan-chip-btn.on{background:rgba(196,75,60,0.08);box-shadow:none;}
/* Minimap (bottom-right) */
.m002-host[data-grid-style="sketch"] .m002-minimap{background:rgba(254,252,246,0.94);border-color:#c8c2b0;backdrop-filter:none;}
.m002-host[data-grid-style="sketch"] .m002-minimap-title{color:#7a7568;}
.m002-host[data-grid-style="sketch"] .m002-minimap-toggle{color:#7a7568;}
.m002-host[data-grid-style="sketch"] .m002-minimap-toggle:hover{color:#c44b3c;}
.m002-host[data-grid-style="sketch"] .m002-minimap-svg{background:#faf6ed;}
/* Catch-all neutralizer for stray text-shadows in sketch mode */
.m002-host[data-grid-style="sketch"] *{text-shadow:none;}

/* Sketch cursor + bracket overrides moved to the end of the file so source
   order also favours them — see "SKETCH OVERRIDES (FINAL)" block. */
.m002-insp-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;flex:1;min-height:240px;text-align:center;color:#5a5f6e;}
.m002-insp-empty-title{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;letter-spacing:2px;color:#7a7f8e;margin-bottom:14px;}
.m002-insp-empty-hints{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:4px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:9px;letter-spacing:1.4px;}
.m002-cheat{display:flex;flex-direction:column;gap:14px;padding:2px 0 6px;}
.m002-cheat-hint{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:9px;letter-spacing:1.4px;color:#5a5f6e;text-align:center;padding:6px 0 4px;border-bottom:1px dashed #1a1a22;}
.m002-cheat-group{display:flex;flex-direction:column;gap:4px;}
.m002-cheat-title{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:9px;letter-spacing:2px;color:#7a7f8e;margin-bottom:2px;}
.m002-cheat-row{display:flex;align-items:center;gap:8px;padding:2px 0;}
.m002-cheat-keys{display:flex;align-items:center;gap:3px;flex-shrink:0;min-width:96px;}
.m002-cheat-plus{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:9px;color:#3a3f4e;}
.m002-kbd{display:inline-flex;align-items:center;justify-content:center;min-width:18px;height:18px;padding:0 5px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:9px;letter-spacing:1px;color:#cfd2d8;background:#0d0d12;border:1px solid #2a2f3a;border-bottom-color:#1a1d24;border-right-color:#1a1d24;border-radius:2px;box-shadow:0 1px 0 #050507,inset 0 1px 0 rgba(255,255,255,0.04);}
.m002-cheat-label{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:9px;letter-spacing:1.2px;color:#8a8f9e;}
.m002-rightpanel .m002-insp-head{display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid #1a1a22;padding-bottom:8px;}
.m002-host *{box-sizing:border-box}
.m002-host [hidden]{display:none!important;}
.m002-tint{position:absolute;inset:0;background:radial-gradient(ellipse at center,transparent 50%,rgba(255,0,60,0.04) 100%);pointer-events:none;}

.m002-board{position:absolute;inset:0;}
.m002-svg{width:100%;height:100%;display:block;}
/* Hide the OS cursor everywhere inside the module — the N.IVEN custom
   cursor (corner brackets + dot) carries the affordance on its own and
   the OS grab/move/crosshair glyphs clash with the sci-fi style. */
.m002-host,.m002-host *{cursor:none !important;}

.m002-grid-bg,.m002-grid-bg2{pointer-events:all;}

/* Snap-to-grid preview during free-move drag — dashed outline at the cell
   the element would land on if dropped now. Sits in m002-overlay (above
   devices) so it overlaps the element being dragged and stays visible
   even when the snap target is right under the cursor. */
.m002-snap-preview{opacity:.85;}

.m002-device{cursor:move;filter:drop-shadow(0 0 3px var(--accent)) drop-shadow(0 0 9px var(--accent));}
.m002-device:hover{filter:drop-shadow(0 0 5px var(--accent)) drop-shadow(0 0 14px var(--accent));}
.m002-device.m002-selected{filter:drop-shadow(0 0 6px var(--accent)) drop-shadow(0 0 18px var(--accent));}
.m002-device.m002-lifted{filter:drop-shadow(0 0 10px var(--accent)) drop-shadow(0 0 28px var(--accent)) drop-shadow(0 14px 26px rgba(0,0,0,.7)) drop-shadow(0 4px 6px rgba(0,0,0,.5));}
.m002-dev-bg{fill:#0a0a10;stroke:var(--accent);stroke-width:1.2;}
.m002-device.m002-selected .m002-dev-bg{stroke-width:2;}
.m002-device.m002-link-pending .m002-dev-bg{stroke-dasharray:4 3;}
.m002-rope{pointer-events:none;}
.m002-rope .m002-rope-line{filter:drop-shadow(0 0 3px currentColor);}
.m002-rope .m002-rope-tip{filter:drop-shadow(0 0 4px currentColor) drop-shadow(0 0 10px currentColor);}
.m002-device.m002-rope-target .m002-dev-bg{stroke-width:2.4;stroke-dasharray:none;filter:drop-shadow(0 0 6px var(--accent)) drop-shadow(0 0 14px var(--accent));}
.m002-host.m002-roping .m002-svg{cursor:crosshair;}
.m002-host.m002-roping .m002-device{cursor:crosshair;}
.m002-autolink{pointer-events:none;}
.m002-autolink .m002-autolink-stub{filter:drop-shadow(0 0 3px currentColor);transition:opacity 60ms ease-out;}
.m002-autolink .m002-autolink-stub-from{stroke:currentColor;color:var(--accent);}
.m002-autolink .m002-autolink-stub-to{stroke:currentColor;color:var(--accent);}
.m002-autolink .m002-autolink-tip{filter:drop-shadow(0 0 3px currentColor) drop-shadow(0 0 7px currentColor);}
.m002-autolink .m002-autolink-full{filter:drop-shadow(0 0 3px currentColor) drop-shadow(0 0 9px currentColor);}
.m002-device.m002-autolink-target .m002-dev-bg{stroke-width:2;stroke-dasharray:none;filter:drop-shadow(0 0 5px var(--accent)) drop-shadow(0 0 12px var(--accent));}
.m002-dev-type{font-size:9px;letter-spacing:1.6px;font-family:'JetBrains Mono','Share Tech Mono',monospace;fill:var(--accent);opacity:.85;}
.m002-dev-name{font-size:14px;font-weight:600;fill:#f5f3ff;letter-spacing:.5px;}
.m002-dev-name.m002-stack-name{font-size:18px;letter-spacing:.3px;}
.m002-dev-ip{font-size:10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;fill:#7a7f8e;}
.m002-dev-notes{font-size:10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;fill:#7a7f8e;font-style:italic;}
.m002-device-ref .m002-dev-bg{stroke-dasharray:6 3;}
.m002-device-ref.m002-device-coupled .m002-dev-bg{stroke-dasharray:none;stroke-width:1.6;filter:drop-shadow(0 0 3px rgba(192,132,252,.55)) drop-shadow(0 0 9px rgba(192,132,252,.3));}
.m002-dev-ref-target{font-size:10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;fill:var(--accent);letter-spacing:.6px;}
.m002-dev-ref-hint{font-size:8px;font-family:'JetBrains Mono','Share Tech Mono',monospace;fill:#7a7f8e;letter-spacing:1.4px;opacity:.7;}
.m002-ref-modes{display:flex;gap:6px;}
.m002-ref-mode{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;border:1px solid #1a1a22;padding:5px 8px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;letter-spacing:1.4px;color:#9aa0a8;cursor:pointer;background:transparent;}
.m002-ref-mode:hover{border-color:#c084fc;color:#e8e8ee;}
.m002-ref-mode.active{border-color:#c084fc;background:rgba(192,132,252,0.08);color:#c084fc;}
.m002-ref-mode input{accent-color:#c084fc;}
.m002-couple-card{display:flex;flex-direction:column;gap:4px;padding:8px 10px;border:1px solid rgba(192,132,252,.45);background:rgba(192,132,252,.06);}
.m002-couple-line{display:flex;gap:8px;align-items:center;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:12px;color:#e8e8ee;letter-spacing:.6px;}
.m002-couple-arrow{color:#c084fc;font-size:14px;}
.m002-couple-zone{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;color:#c084fc;letter-spacing:1.2px;text-transform:uppercase;}
.m002-field-static{padding:6px 8px;border:1px dashed #1f1f28;background:#0a0a10;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;color:#9aa0a8;letter-spacing:.6px;}
.m002-anchor-row{display:flex;gap:6px;align-items:stretch;}
.m002-anchor-row .m002-field-static{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.m002-anchor-row .m002-action{flex:0 0 auto;}
.m002-ref-fallback{margin:4px 0 8px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;color:#9aa0a8;letter-spacing:1px;}
.m002-ref-fallback summary{cursor:pointer;padding:4px 0;color:#7a7f8e;text-transform:uppercase;}
.m002-ref-fallback[open] summary{color:#c084fc;}
.m002-vlan-readonly{display:flex;flex-wrap:wrap;gap:4px;}
.m002-vlan-readonly .m002-vlan-chip-btn{cursor:default;pointer-events:none;}
.m002-vlan-readonly .m002-vlan-chip-btn.dim{opacity:.45;}

.m002-stack-collapsed{cursor:move;filter:drop-shadow(0 0 3px var(--accent)) drop-shadow(0 0 9px var(--accent));}
.m002-stack-collapsed:hover{filter:drop-shadow(0 0 5px var(--accent)) drop-shadow(0 0 14px var(--accent));}
.m002-stack-collapsed.m002-selected{filter:drop-shadow(0 0 6px var(--accent)) drop-shadow(0 0 18px var(--accent));}
.m002-stack-collapsed.m002-lifted{filter:drop-shadow(0 0 10px var(--accent)) drop-shadow(0 0 28px var(--accent)) drop-shadow(0 14px 26px rgba(0,0,0,.7)) drop-shadow(0 4px 6px rgba(0,0,0,.5));}
.m002-stack-collapsed.m002-stack-pending .m002-dev-bg{stroke-dasharray:4 3;}
.m002-stack-ghost{fill:#0a0a10;stroke:var(--accent);stroke-width:1;opacity:.55;}
.m002-stack-collapsed .m002-dev-bg{fill:#0a0a10;stroke:var(--accent);stroke-width:1.4;}
.m002-stack-collapsed.m002-selected .m002-dev-bg{stroke-width:2;}
.m002-stack-badge{font-size:11px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-weight:600;fill:var(--accent);letter-spacing:1px;}

.m002-stack-envelope{filter:drop-shadow(0 0 3px var(--accent)) drop-shadow(0 0 9px var(--accent));}
.m002-stack-envelope:hover{filter:drop-shadow(0 0 5px var(--accent)) drop-shadow(0 0 14px var(--accent));}
.m002-stack-envelope.m002-selected{filter:drop-shadow(0 0 6px var(--accent)) drop-shadow(0 0 18px var(--accent));}
.m002-stack-env-bg{fill:#0a0a10;stroke:var(--accent);stroke-width:1.4;opacity:1;}
.m002-stack-envelope.m002-selected .m002-stack-env-bg{stroke-width:2;}
.m002-stack-envelope.m002-selected .m002-stack-env-label{fill:var(--accent);}
.m002-stack-env-label{font-size:10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;fill:var(--accent);letter-spacing:1.5px;opacity:.8;}
/* Routing layer: a stack's envelope only glows when the stack itself is an
   L3 entity (data-l3="true" iff stackHasVip). Non-VIP envelopes recede so
   the eye lands on the actual IP-bearing members instead. */
.m002-host[data-active-layer="routing"] .m002-stack-envelope[data-l3="false"]{opacity:.18;filter:saturate(0) brightness(.55);}
.m002-host[data-active-layer="routing"] .m002-stack-envelope[data-l3="false"] .m002-stack-env-bg{stroke:#3a3a44;fill:transparent;pointer-events:all;}
.m002-host[data-active-layer="routing"] .m002-stack-envelope[data-l3="false"] .m002-stack-env-label{fill:#5a5f6e;}
.m002-host[data-active-layer="routing"] .m002-stack-envelope[data-l3="false"]:hover{opacity:.9;filter:saturate(.85) brightness(.95);}
/* Stack-member devices: dimmed white in physical/vlan, but in routing the
   IP-bearing members glow in their own accent (override below). */
.m002-device.m002-stack-member{filter:none;}
.m002-device.m002-stack-member:hover{filter:drop-shadow(0 0 2px rgba(245,243,255,.45)) drop-shadow(0 0 7px rgba(245,243,255,.25));}
.m002-device.m002-stack-member.m002-selected{filter:drop-shadow(0 0 3px rgba(245,243,255,.6)) drop-shadow(0 0 10px rgba(245,243,255,.35));}
.m002-device.m002-stack-member .m002-dev-bg{stroke:rgba(245,243,255,.45);stroke-dasharray:4 3;}
.m002-device.m002-stack-member .m002-dev-type{fill:rgba(245,243,255,.65);}
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member[data-l3="true"]{filter:drop-shadow(0 0 3px var(--accent)) drop-shadow(0 0 9px var(--accent));}
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member[data-l3="true"]:hover{filter:drop-shadow(0 0 5px var(--accent)) drop-shadow(0 0 14px var(--accent));}
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member[data-l3="true"].m002-selected{filter:drop-shadow(0 0 6px var(--accent)) drop-shadow(0 0 18px var(--accent));}
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member[data-l3="true"] .m002-dev-bg{stroke:var(--accent);}
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member[data-l3="true"] .m002-dev-type{fill:var(--accent);opacity:.85;}
/* Members of a VIP'd stack: the stack speaks for them in routing — keep them
   muted so the envelope is the visual L3 entity, not the individual nodes. */
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member-vip,
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member-vip[data-l3="true"]{filter:none;}
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member-vip:hover,
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member-vip[data-l3="true"]:hover{filter:drop-shadow(0 0 2px rgba(245,243,255,.45)) drop-shadow(0 0 7px rgba(245,243,255,.25));}
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member-vip .m002-dev-bg,
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member-vip[data-l3="true"] .m002-dev-bg{stroke:rgba(245,243,255,.45);}
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member-vip .m002-dev-type,
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member-vip[data-l3="true"] .m002-dev-type{fill:rgba(245,243,255,.65);opacity:1;}
.m002-stack-cable{stroke:#5a5f6e;stroke-width:1.2;stroke-dasharray:5 4;fill:none;opacity:.75;}
.m002-stack-cable-hit{stroke:transparent;stroke-width:18;fill:none;cursor:pointer;}
.m002-stacklink:hover .m002-stack-cable{stroke:#9aa0a8;opacity:1;}
.m002-stack-cable-label{font-size:9px;font-family:'JetBrains Mono','Share Tech Mono',monospace;fill:#9aa0a8;letter-spacing:1px;pointer-events:none;paint-order:stroke fill;stroke:#0a0a10;stroke-width:3px;stroke-linejoin:round;stroke-linecap:round;}
.m002-stacklink:hover .m002-stack-cable-label{fill:#9aa0a8;}

.m002-link-line{stroke-width:1.4;fill:none;}
.m002-link-hit{stroke:transparent;stroke-width:18;fill:none;cursor:pointer;}
/* Traffic-pulse on links incident to the current selection. The <path> is
   injected on-demand by applyIncidentFlow() — idle links never carry one.
   stroke-dashoffset is driven by startFlowTicker() (rAF) so the pulse runs
   even when the OS forces prefers-reduced-motion. */
.m002-link-flow{fill:none;stroke:#ffffff;stroke-width:2.4;stroke-dasharray:6 92;stroke-linecap:round;pointer-events:none;opacity:.95;filter:drop-shadow(0 0 3px #fff) drop-shadow(0 0 7px rgba(255,255,255,.55));}
.m002-link:hover .m002-link-line{stroke-width:1.8;filter:drop-shadow(0 0 2px rgba(255,255,255,0.55)) drop-shadow(0 0 6px rgba(255,255,255,0.25));}
.m002-link:hover .m002-link-label{filter:drop-shadow(0 0 2px rgba(255,255,255,0.4));}
.m002-link.m002-selected .m002-link-line{stroke:#ffffff;stroke-width:2.4;filter:drop-shadow(0 0 4px #fff) drop-shadow(0 0 10px rgba(255,255,255,0.65));}
.m002-link.m002-selected .m002-link-line.m002-link-stripe{filter:drop-shadow(0 0 4px currentColor) drop-shadow(0 0 10px currentColor);}
.m002-link.m002-selected .m002-link-label{fill:#ffffff;}
.m002-link.m002-selected .m002-link-label.m002-link-stripe-label{filter:drop-shadow(0 0 3px currentColor);}
.m002-link.m002-link-faded{opacity:.25;}
.m002-link-vlan-count{font-size:9px;font-family:'JetBrains Mono','Share Tech Mono',monospace;letter-spacing:1px;fill:#9aa0a8;opacity:.85;pointer-events:none;paint-order:stroke fill;stroke:#0a0a10;stroke-width:3px;stroke-linejoin:round;stroke-linecap:round;}
.m002-link:hover .m002-link-vlan-count{opacity:1;fill:#e8e8ee;}
.m002-link-label{font-size:9px;font-family:'JetBrains Mono','Share Tech Mono',monospace;text-anchor:middle;letter-spacing:1px;paint-order:stroke fill;stroke:#0a0a10;stroke-width:3px;stroke-linejoin:round;stroke-linecap:round;}

.m002-palette{display:none;}
.m002-palette-title{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;color:#5a5f6e;letter-spacing:2px;margin-bottom:6px;}
.m002-pal-btn{flex:0 0 auto;display:flex;align-items:center;gap:10px;background:transparent;border:1px solid transparent;color:#e8e8ee;padding:6px 10px;cursor:pointer;font-family:'Inter','Rajdhani',sans-serif;font-size:13px;letter-spacing:1.2px;text-align:left;transition:.15s;line-height:1.2;min-height:30px;}
.m002-panel-section{flex:0 0 auto;}
.m002-panel-grid{flex:0 0 auto;}
.m002-pal-btn:hover{border-color:#ff003c;background:rgba(255,0,60,0.06);}
.m002-pal-btn.ghost{color:#9aa0a8;}
.m002-pal-btn.active{background:rgba(0,212,255,0.1);border-color:#00d4ff;color:#00d4ff;}
.m002-pal-glyph{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:18px;width:20px;text-align:center;}
.m002-pal-dot{width:10px;height:10px;background:var(--accent);box-shadow:0 0 4px var(--accent),0 0 10px var(--accent);flex:0 0 auto;margin-left:4px;}
.m002-pal-sep{height:1px;background:#1a1a22;margin:6px 0;}

.m002-layerbar-wrap{position:absolute;top:18px;left:50%;transform:translateX(-50%);z-index:5;}
.m002-zonebar-wrap{position:absolute;top:18px;right:18px;z-index:5;}
/* Tab bar — Excel-style sheet tabs at the bottom of the canvas. */
.m002-tabbar-wrap{position:absolute;bottom:18px;left:18px;right:18px;z-index:5;display:flex;align-items:stretch;gap:6px;pointer-events:none;}
.m002-tabbar{flex:1;display:flex;align-items:stretch;gap:4px;overflow-x:auto;overflow-y:hidden;padding:4px;background:rgba(8,8,14,0.85);border:1px solid #1a1a22;backdrop-filter:blur(6px);pointer-events:auto;scrollbar-width:thin;scrollbar-color:#2a2a36 transparent;}
.m002-tabbar::-webkit-scrollbar{height:4px;}
.m002-tabbar::-webkit-scrollbar-track{background:transparent;}
.m002-tabbar::-webkit-scrollbar-thumb{background:#2a2a36;}
.m002-tabs{display:flex;align-items:stretch;gap:4px;flex:0 0 auto;}
.m002-tab{position:relative;display:inline-flex;align-items:center;gap:6px;padding:4px 8px 4px 10px;background:transparent;border:1px solid transparent;color:#7a7f8e;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;letter-spacing:1.4px;cursor:pointer;user-select:none;max-width:200px;transition:color .12s,border-color .12s,background-color .12s;}
.m002-tab:hover{color:#e8e8ee;border-color:#1a1a22;}
.m002-tab.active{background:rgba(0,212,255,0.08);border-color:#00d4ff;color:#00d4ff;}
.m002-tab-dot{width:5px;height:5px;border-radius:50%;background:#3a3f4e;flex:0 0 auto;}
.m002-tab.active .m002-tab-dot{background:#00d4ff;box-shadow:0 0 6px rgba(0,212,255,0.6);}
.m002-tab-name{flex:1 1 auto;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;text-transform:uppercase;}
.m002-tab-name input.m002-tab-rename-input{display:block;width:100%;background:transparent;border:none;color:#00d4ff;font:inherit;letter-spacing:inherit;text-transform:uppercase;outline:none;padding:0;margin:0;caret-color:#00d4ff;}
.m002-tab-close{flex:0 0 auto;display:none;align-items:center;justify-content:center;width:14px;height:14px;background:transparent;border:none;color:#7a7f8e;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:13px;line-height:1;cursor:pointer;padding:0;border-radius:2px;}
.m002-tab:hover .m002-tab-close,.m002-tab.active .m002-tab-close{display:inline-flex;}
.m002-tab-close:hover{background:rgba(255,0,60,0.18);color:#ff5577;}
.m002-tab-add,.m002-tab-more{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;background:rgba(8,8,14,0.85);border:1px solid #1a1a22;color:#7a7f8e;width:28px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:14px;line-height:1;cursor:pointer;backdrop-filter:blur(6px);pointer-events:auto;transition:color .12s,border-color .12s;}
.m002-tab-add:hover{border-color:#00d4ff;color:#00d4ff;}
.m002-tab-more{font-size:16px;letter-spacing:0;}
.m002-tab-more:hover{border-color:#ff003c;color:#ff003c;}
.m002-tab-menu{position:absolute;bottom:calc(100% + 6px);right:0;background:rgba(10,10,16,0.95);border:1px solid #1a1a22;min-width:200px;backdrop-filter:blur(6px);z-index:200;pointer-events:auto;}
.m002-tab-ctx{position:fixed;background:rgba(10,10,16,0.96);border:1px solid #1a1a22;min-width:180px;backdrop-filter:blur(6px);z-index:300;font-family:'JetBrains Mono','Share Tech Mono',monospace;}
.m002-menu-section{display:flex;flex-direction:column;}
.m002-menu-item{display:flex;justify-content:space-between;align-items:center;background:transparent;border:none;color:#e8e8ee;padding:8px 12px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;letter-spacing:1.5px;cursor:pointer;text-align:left;}
.m002-menu-item:hover{background:rgba(255,0,60,0.08);color:#ff003c;}
.m002-menu-item.active{color:#ff003c;}
.m002-menu-item.danger{color:#ff003c;}
.m002-menu-item.is-disabled{opacity:0.4;cursor:not-allowed;}
.m002-menu-item.is-disabled:hover{background:transparent;color:#e8e8ee;}
.m002-menu-dot{width:6px;height:6px;background:#ff003c;border-radius:50%;}
.m002-menu-sep{height:1px;background:#1a1a22;}

.m002-zonebar{display:flex;gap:4px;background:rgba(8,8,14,0.85);border:1px solid #1a1a22;padding:4px;backdrop-filter:blur(6px);}
.m002-zone-pill{background:transparent;border:1px solid transparent;color:#7a7f8e;padding:4px 10px;cursor:pointer;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;letter-spacing:1.4px;}
.m002-zone-pill:hover{color:#e8e8ee;}
.m002-zone-pill.active{background:rgba(0,212,255,0.08);border-color:#00d4ff;color:#00d4ff;}
.m002-zone-add{background:transparent;border:1px dashed #2a2a36;color:#7a7f8e;padding:4px 8px;cursor:pointer;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;line-height:1;}
.m002-zone-add:hover{border-color:#00d4ff;color:#00d4ff;}

.m002-layerbar{display:flex;gap:6px;background:rgba(8,8,14,0.85);border:1px solid #1a1a22;padding:6px;backdrop-filter:blur(6px);}
.m002-layer-pill{background:transparent;border:1px solid transparent;color:#9aa0a8;padding:6px 14px;cursor:pointer;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;letter-spacing:1.6px;}
.m002-layer-pill:hover{color:#e8e8ee;}
.m002-layer-pill.active{background:rgba(255,0,60,0.1);border-color:#ff003c;color:#ff003c;}

.m002-inspector.m002-rightpanel{padding:14px;display:flex;flex-direction:column;gap:8px;}
.m002-insp-head{display:flex;justify-content:space-between;align-items:center;}
.m002-insp-id{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;color:#ff003c;letter-spacing:2px;}
.m002-insp-close{background:transparent;border:none;color:#9aa0a8;font-size:18px;cursor:pointer;padding:0 4px;line-height:1;}
.m002-insp-close:hover{color:#ff003c;}
.m002-insp-body{display:flex;flex-direction:column;gap:8px;}
.m002-field{display:flex;flex-direction:column;gap:3px;}
.m002-field span{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:9px;color:#8a90a0;letter-spacing:1.5px;}
.m002-field input,.m002-field select,.m002-field textarea{background:#0a0a10;border:1px solid #1a1a22;color:#e8e8ee;padding:5px 8px;font-family:'Inter','Rajdhani',sans-serif;font-size:13px;outline:none;}
.m002-field input:focus,.m002-field select:focus,.m002-field textarea:focus{border-color:#ff003c;}
/* Port-count row: label + count + 24/48/-/+ buttons inline. The count uses
   the heading font so it reads as a value, not a control. Buttons hug the
   right edge via margin-left:auto on the group. */
.m002-field-ports{flex-direction:row;align-items:center;gap:10px;flex-wrap:wrap;}
.m002-field-ports>span:first-child{flex:0 0 auto;}
.m002-field-ports .m002-port-count{font-family:'Inter','Rajdhani',sans-serif;font-size:18px;font-weight:600;color:#e8e8ee;letter-spacing:.5px;min-width:24px;text-align:left;line-height:1;}
.m002-port-btns{display:flex;gap:6px;margin-left:auto;}
.m002-pcount-btn{background:transparent;border:1px solid;padding:4px 10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;font-weight:600;letter-spacing:1.4px;cursor:pointer;transition:.15s;min-width:32px;line-height:1;}
.m002-pcount-btn.preset{border-color:#00d4ff;color:#00d4ff;}
.m002-pcount-btn.preset:hover{background:rgba(0,212,255,0.12);}
.m002-pcount-btn.step.plus{border-color:#35ff7a;color:#35ff7a;}
.m002-pcount-btn.step.plus:hover{background:rgba(53,255,122,0.12);}
.m002-pcount-btn.step.minus{border-color:#ff003c;color:#ff003c;}
.m002-pcount-btn.step.minus:hover{background:rgba(255,0,60,0.12);}
.m002-row2{display:grid;grid-template-columns:1fr 1fr;gap:8px;}
.m002-link-summary{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:8px;background:#0a0a10;border:1px solid #1a1a22;}
.m002-link-end{font-weight:600;font-size:13px;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.m002-link-arrow{color:#ff003c;font-family:'JetBrains Mono','Share Tech Mono',monospace;}
.m002-ports-block{display:flex;flex-direction:column;gap:4px;}
.m002-ports-head{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;color:#b0b6c4;letter-spacing:1.5px;padding:4px 0;}
.m002-ports-prefix{display:flex;align-items:center;gap:6px;padding:3px 6px;background:transparent;border:1px dashed #2a2a32;border-radius:2px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;}
.m002-ports-prefix[hidden]{display:none;}
.m002-ports-prefix-label{color:#8a90a0;letter-spacing:1.4px;}
.m002-ports-prefix-val{color:inherit;background:transparent;padding:1px 5px;border:1px solid #1a1a22;border-radius:2px;font-size:11px;}
.m002-ports-prefix-clear{margin-left:auto;background:transparent;border:1px solid #2a2a32;color:#9aa0a8;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;width:18px;height:18px;line-height:1;cursor:pointer;border-radius:2px;padding:0;}
.m002-ports-prefix-clear:hover{border-color:#ff003c;color:#ff003c;}
.m002-ports-grid{display:flex;flex-direction:column;gap:2px;max-height:220px;overflow-y:auto;}
.m002-port-head-row{display:grid;grid-template-columns:18px 60px 1fr;gap:6px;align-items:center;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:9px;color:#8a90a0;letter-spacing:1.4px;padding:2px 4px;order:-1;}
.m002-port-row{display:grid;grid-template-columns:18px 60px 1fr;gap:6px;align-items:center;cursor:pointer;padding:1px 4px;border:1px solid transparent;border-radius:2px;}
.m002-port-row:hover{background:rgba(255,0,60,0.06);border-color:#ff003c;}
.m002-port-row.is-duplicate{background:rgba(255,0,60,0.14);border-color:#ff003c;animation:m002-dupe-pulse 1.6s ease-in-out infinite;}
.m002-port-row.is-duplicate input{border-color:#ff003c;color:#ff5a78;}
.m002-port-row.is-duplicate .m002-port-num{color:#ff5a78;}
.m002-pmodal-name.is-duplicate{border-color:#ff003c !important;color:#ff5a78;animation:m002-dupe-pulse 1.6s ease-in-out infinite;}
@keyframes m002-dupe-pulse{0%,100%{box-shadow:0 0 4px rgba(255,0,60,0.45);}50%{box-shadow:0 0 12px rgba(255,0,60,0.85);}}
.m002-port-num{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;color:#b8becc;text-align:left;}
.m002-port-row input{background:#0a0a10;border:1px solid #1a1a22;color:#e8e8ee;padding:2px 6px;font-size:11px;font-family:'JetBrains Mono','Share Tech Mono',monospace;outline:none;}
.m002-port-row input:focus{border-color:#ff003c;}
.m002-port-counter{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;color:#e8e8ee;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:0 4px;}
.m002-port-counter.dim{color:#8a90a0;}
.m002-link-hint{font-size:11px;color:#9aa0ae;line-height:1.4;margin:0;font-style:italic;}
.m002-link-end.dim{color:#5a5f6e;font-weight:400;}
.m002-lag-props{margin-top:4px;padding-top:10px;border-top:1px dashed #1a1a22;display:flex;flex-direction:column;gap:8px;}
.m002-lag-sides{display:flex;flex-direction:column;gap:10px;margin-top:4px;}
.m002-lag-side{display:flex;flex-direction:column;gap:8px;padding-top:10px;border-top:1px dashed #1a1a22;}
.m002-lag-side-head{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;letter-spacing:2px;color:#ff003c;}
.m002-port-lagtag{margin-left:6px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:9px;letter-spacing:1px;color:#ff003c;border:1px solid #ff003c;padding:1px 4px;border-radius:2px;background:rgba(255,0,60,0.08);}

.m002-stack-members{display:flex;flex-direction:column;gap:4px;}
.m002-stack-member{display:grid;grid-template-columns:14px 1fr auto 22px;gap:6px;align-items:center;background:#06060a;border:1px solid #1a1a22;padding:4px 8px;}
.m002-stack-member-dot{width:8px;height:8px;background:var(--accent);box-shadow:0 0 4px var(--accent);}
.m002-stack-member-name{font-size:12px;color:#e8e8ee;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.m002-stack-member-type{font-size:9px;font-family:'JetBrains Mono','Share Tech Mono',monospace;color:var(--accent);letter-spacing:1px;}
.m002-stack-member button{background:transparent;border:none;color:#9aa0a8;cursor:pointer;font-size:14px;line-height:1;padding:0;}
.m002-stack-member button:hover{color:#ff003c;}

/* The LAG modal still uses .m002-port-panel + .m002-port-modal-head/-id
   for its floating card. The PORT detail moved into the inspector panel, so
   no backdrop / floating panel rules remain for it. */
.m002-port-panel{background:#0a0a10;border:1px solid #ff003c;width:340px;max-width:calc(100% - 32px);padding:16px;display:flex;flex-direction:column;gap:12px;box-shadow:0 0 20px rgba(255,0,60,0.25);}
.m002-port-modal-head{display:flex;justify-content:space-between;align-items:center;}
.m002-port-modal-id{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;color:#ff003c;letter-spacing:2px;}
.m002-port-back{display:inline-flex;align-items:center;justify-content:flex-start;background:transparent;border:1px solid #1a1a22;color:#9aa0a8;padding:6px 10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;letter-spacing:1.4px;cursor:pointer;text-align:left;text-transform:uppercase;transition:.15s;}
.m002-port-back:hover{border-color:#ff003c;color:#ff003c;background:rgba(255,0,60,0.06);}
.m002-vlan-chips{display:flex;flex-wrap:wrap;gap:4px;min-height:24px;padding:4px;background:#06060a;border:1px solid #1a1a22;}
.m002-vlan-empty{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;color:#8a90a0;letter-spacing:1px;}
.m002-vlan-chip{display:inline-flex;align-items:center;gap:4px;padding:2px 6px;background:rgba(0,0,0,0.4);border:1px solid var(--vc);color:var(--vc);font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;letter-spacing:1px;}
.m002-vlan-chip button{background:transparent;border:none;color:var(--vc);cursor:pointer;font-size:13px;line-height:1;padding:0 2px;opacity:.6;}
.m002-vlan-chip button:hover{opacity:1;}
.m002-vlan-add{display:flex;gap:4px;}
.m002-vlan-input{flex:1;background:#0a0a10;border:1px solid #1a1a22;color:#e8e8ee;padding:5px 8px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:12px;outline:none;}
.m002-vlan-input:focus{border-color:#ff003c;}
.m002-vlan-add-btn{background:transparent;border:1px solid #ff003c;color:#ff003c;padding:5px 10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;letter-spacing:1.5px;cursor:pointer;}
.m002-vlan-add-btn:hover{background:rgba(255,0,60,0.1);}
.m002-pmodal-cp option.is-occupied,
.m002-link-port-select option.is-occupied{color:#5a5f6e;}
/* Collapsible details block in the device inspector — wraps HOSTNAME, IP and
   NOTES so the inspector stays tidy by default but the metadata is one click
   away. Mirrors the .m002-ref-fallback summary treatment. */
.m002-insp-details{margin:2px 0 6px;border:1px solid #1a1a22;background:rgba(10,10,16,0.6);}
.m002-insp-details>summary{cursor:pointer;padding:6px 10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;color:#a0a6b4;letter-spacing:1.6px;list-style:none;user-select:none;transition:.15s;}
.m002-insp-details>summary::-webkit-details-marker{display:none;}
.m002-insp-details>summary::before{content:'▸ ';color:#5a5f6e;}
.m002-insp-details[open]>summary{color:#ff003c;border-bottom:1px solid #1a1a22;}
.m002-insp-details[open]>summary::before{content:'▾ ';color:#ff003c;}
.m002-insp-details>summary:hover{color:#ff003c;}
.m002-insp-details>.m002-field{padding:0 10px;}
.m002-insp-details>.m002-field:first-of-type{padding-top:8px;}
.m002-insp-details>.m002-field:last-of-type{padding-bottom:10px;}
/* VLANs collapsible — same framed scaffolding as //DETAILS, with the picker
   getting body-padding so chips don't hug the inner border. */
.m002-insp-vlans{margin:2px 0 6px;border:1px solid #1a1a22;background:rgba(10,10,16,0.6);}
.m002-insp-vlans>summary{cursor:pointer;padding:6px 10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;color:#a0a6b4;letter-spacing:1.6px;list-style:none;user-select:none;transition:.15s;}
.m002-insp-vlans>summary::-webkit-details-marker{display:none;}
.m002-insp-vlans>summary::before{content:'▸ ';color:#5a5f6e;}
.m002-insp-vlans[open]>summary{color:#ff003c;border-bottom:1px solid #1a1a22;}
.m002-insp-vlans[open]>summary::before{content:'▾ ';color:#ff003c;}
.m002-insp-vlans>summary:hover{color:#ff003c;}
.m002-insp-vlans>.m002-vlan-picker{padding:8px 10px;}
.m002-port-actions{display:flex;flex-direction:column;gap:6px;margin-top:4px;}
.m002-action{display:flex;align-items:center;justify-content:center;gap:8px;background:transparent;border:1px solid #1a1a22;color:#e8e8ee;padding:7px 10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;letter-spacing:1.5px;cursor:pointer;transition:.15s;}
.m002-action:hover{border-color:#9aa0a8;}
.m002-action.danger{border-color:#ff003c;color:#ff003c;}
.m002-action.danger:hover{background:rgba(255,0,60,0.1);}
.m002-insp-del{margin-top:6px;background:transparent;border:1px solid #ff003c;color:#ff003c;padding:6px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;letter-spacing:2px;cursor:pointer;}
.m002-insp-del:hover{background:rgba(255,0,60,0.1);}
.m002-insp-back{align-self:flex-start;background:transparent;border:none;color:#7a7f8e;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;letter-spacing:1.5px;cursor:pointer;padding:2px 0;margin-bottom:2px;}
.m002-insp-back:hover{color:#e8e8ee;}

.m002-multi-selected .m002-dev-bg{stroke-width:2;stroke-dasharray:3 3;}
.m002-multi-selected.m002-stack-collapsed .m002-dev-bg{stroke-dasharray:3 3;}

/* Drag-to-stack: pulse the merge target while a device hovers over it.
   Green = same device type (valid stack), red = mismatched type (invalid). */
.m002-drag-stack-target.m002-merge-ok{animation:m002-merge-pulse-ok .5s ease-in-out infinite alternate!important;}
.m002-drag-stack-target.m002-merge-bad{animation:m002-merge-pulse-bad .5s ease-in-out infinite alternate!important;}
@keyframes m002-merge-pulse-ok{
  from{filter:drop-shadow(0 0 5px #35ff7a) drop-shadow(0 0 14px #35ff7a);}
  to  {filter:drop-shadow(0 0 12px #35ff7a) drop-shadow(0 0 30px #35ff7a);}
}
@keyframes m002-merge-pulse-bad{
  from{filter:drop-shadow(0 0 5px #ff003c) drop-shadow(0 0 14px #ff003c);}
  to  {filter:drop-shadow(0 0 12px #ff003c) drop-shadow(0 0 30px #ff003c);}
}

/* Conflict signaling — generic input-rejection visuals shared by every
   "doesn't make sense from a network perspective" guard (today: duplicate
   IPs; later: duplicate hostnames, overlapping subnets, ...).
   The field stays red as long as its value collides with another slot;
   the pulse fires for ~1.8s on the offending tile so the user can spot the
   conflicting element on the canvas at a glance. */
.m002-field-conflict,
input.m002-field-conflict,
.m002-iface-row input.m002-field-conflict,
.m002-route-row input.m002-field-conflict{
  border-color:#ff003c !important;
  color:#ff5a78 !important;
  background:rgba(255,0,60,0.10) !important;
  animation:m002-conflict-field-pulse 1.4s ease-in-out infinite;
}
@keyframes m002-conflict-field-pulse{
  0%,100%{box-shadow:0 0 4px rgba(255,0,60,0.50), inset 0 0 0 1px rgba(255,0,60,0.20);}
  50%   {box-shadow:0 0 14px rgba(255,0,60,0.95), inset 0 0 0 1px rgba(255,0,60,0.45);}
}
.m002-conflict-pulse{animation:m002-conflict-tile-pulse 0.55s ease-in-out 3 alternate;}
@keyframes m002-conflict-tile-pulse{
  from{filter:drop-shadow(0 0 6px #ff003c) drop-shadow(0 0 16px rgba(255,0,60,0.65));}
  to  {filter:drop-shadow(0 0 16px #ff003c) drop-shadow(0 0 36px rgba(255,0,60,0.95));}
}

.m002-lagtable-head{display:grid;grid-template-columns:60px 50px 1fr;gap:6px;align-items:center;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:9px;color:#5a5f6e;letter-spacing:1.4px;padding:2px 4px;}
.m002-lagtable-row{display:grid;grid-template-columns:60px 50px 1fr;gap:6px;align-items:center;cursor:pointer;padding:3px 4px;border:1px solid transparent;border-radius:2px;}
.m002-lagtable-row:hover{background:rgba(255,0,60,0.06);border-color:#ff003c;}
.m002-lagtable-name{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;color:#e8e8ee;letter-spacing:1px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.m002-lagtable-ports{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;color:#9aa0a8;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.m002-lagtable-cp{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;color:#e8e8ee;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:0 4px;}
.m002-lagtable-cp.dim{color:#5a5f6e;}

.m002-stacklinks-grid{display:flex;flex-direction:column;gap:4px;}
.m002-stacklink-row{display:grid;grid-template-columns:1fr 56px 14px 1fr 56px 22px;gap:4px;align-items:center;padding:3px 0;}
.m002-stacklink-row select{background:#0a0a10;border:1px solid #1a1a22;color:#e8e8ee;padding:3px 4px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;letter-spacing:.5px;outline:none;min-width:0;}
.m002-stacklink-row select:focus{border-color:#ff003c;}
.m002-stacklink-arrow{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;color:#5a5f6e;text-align:center;}
.m002-stacklink-row button[data-sl-rm]{background:transparent;border:1px solid transparent;color:#5a5f6e;font-size:14px;cursor:pointer;line-height:1;padding:2px 4px;}
.m002-stacklink-row button[data-sl-rm]:hover{color:#ff003c;border-color:#ff003c;}

.m002-lag-line{stroke-linecap:square;}
.m002-link.m002-link-bundle:hover .m002-lag-line{stroke:#e8e8ee;}
.m002-link.m002-selected .m002-lag-line{stroke:#ffffff;stroke-width:2.4;filter:drop-shadow(0 0 4px #fff) drop-shadow(0 0 10px rgba(255,255,255,0.65));}
.m002-link.m002-selected .m002-link-bundle-label{fill:#ffffff!important;}
.m002-lag-modal{position:absolute;inset:0;background:rgba(4,4,8,0.7);display:flex;align-items:center;justify-content:center;z-index:100;backdrop-filter:blur(2px);}
.m002-lag-modal-body{display:flex;flex-direction:column;gap:10px;}
.m002-lagm-ports{display:flex;flex-direction:column;gap:3px;max-height:240px;overflow-y:auto;}
.m002-lagm-port{display:flex;align-items:center;gap:8px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;color:#e8e8ee;cursor:pointer;padding:3px 6px;border:1px solid transparent;}
.m002-lagm-port:hover{background:rgba(255,0,60,0.05);border-color:#1a1a22;}
.m002-lagm-port.disabled{opacity:.4;cursor:not-allowed;}
.m002-lag-modal-close{background:transparent;border:none;color:#9aa0a8;font-size:18px;cursor:pointer;padding:0 4px;line-height:1;}
.m002-lag-modal-close:hover{color:#ff003c;}
.m002-port-lag-row{display:flex;gap:6px;align-items:center;flex-wrap:wrap;}
.m002-port-lag-select{flex:1;background:#0a0a10;border:1px solid #1a1a22;color:#e8e8ee;padding:4px 8px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;outline:none;}
.m002-link-bundle-label{font-size:10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;letter-spacing:1.5px;font-weight:600;paint-order:stroke fill;stroke:#0a0a10;stroke-width:3.5px;stroke-linejoin:round;stroke-linecap:round;}
/* Stack-pair aggregate summary — replaces N parallel non-paired stubs
   between two collapsed stacks (or stack + device). A single faint dashed
   line; hover lifts it so it's clearly actionable. Click opens the LAG
   configurator. */
.m002-link-agg .m002-link-hit{stroke-width:22;cursor:pointer;}
.m002-link-agg-line{transition:stroke .15s,opacity .15s;}
.m002-link-agg:hover .m002-link-agg-line{stroke:#9aa0a8;}
.m002-link-agg-label{font-size:9px;font-family:'JetBrains Mono','Share Tech Mono',monospace;letter-spacing:1.5px;paint-order:stroke fill;stroke:#0a0a10;stroke-width:3.5px;stroke-linejoin:round;}
/* Aggregate inspector — LAG-pair configurator that opens when the user
   clicks the dim summary line between two collapsed stacks. */
.m002-agg-side{display:flex;gap:6px;align-items:center;}
.m002-agg-lagpick,.m002-agg-lagname{flex:1 1 0;min-width:0;background:#06060a;border:1px solid #1a1a22;color:#e8e8ee;padding:4px 8px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;outline:none;}
.m002-agg-lagpick:focus,.m002-agg-lagname:focus{border-color:#35ff7a;}
.m002-agg-links{display:flex;flex-direction:column;gap:3px;}
.m002-agg-link-row{display:grid;grid-template-columns:1fr 18px 1fr;gap:6px;align-items:center;padding:3px 6px;background:#06060a;border:1px solid #1a1a22;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;color:#9aa0a8;}
.m002-agg-port{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.m002-agg-arrow{text-align:center;color:#5a5f6e;}
.m002-link-bundle .m002-link-hit{stroke-width:26;}

.m002-vlan-chip-btn{display:inline-flex;align-items:center;justify-content:center;gap:4px;padding:2px 7px;min-width:26px;background:transparent;border:1px solid #2a2a36;color:#9aa0ae;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;font-weight:600;letter-spacing:.5px;cursor:pointer;transition:.15s;}
.m002-vlan-chip-btn:hover{border-color:var(--vc);color:var(--vc);}
.m002-vlan-chip-btn.on{background:rgba(0,0,0,0.3);border-color:var(--vc);color:var(--vc);box-shadow:0 0 6px var(--vc);}
.m002-vlan-picker{display:flex;flex-wrap:wrap;gap:3px;}
.m002-vlan-legend-rm{background:transparent;border:none;color:var(--vc);cursor:pointer;font-size:13px;line-height:1;padding:0 2px;opacity:.5;}
.m002-vlan-legend-rm:hover{opacity:1;}
.m002-vlan-legend-add{display:flex;gap:4px;margin-top:6px;flex:0 0 auto;align-items:stretch;}
.m002-vlan-legend-input{flex:1 1 0;min-width:0;background:#06060a;border:1px solid #1a1a22;color:#e8e8ee;padding:4px 8px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;outline:none;}
.m002-vlan-legend-input:focus{border-color:#ff003c;}
.m002-vlan-legend-add-btn{background:transparent;border:1px solid #ff003c;color:#ff003c;padding:4px 10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;letter-spacing:1.5px;cursor:pointer;white-space:nowrap;flex:0 0 auto;align-self:stretch;}
.m002-vlan-legend-add-btn:hover{background:rgba(255,0,60,0.1);}

.m002-minimap{position:absolute;bottom:18px;right:18px;background:rgba(8,8,14,0.85);border:1px solid #1a1a22;backdrop-filter:blur(6px);width:180px;z-index:5;}
.m002-minimap[data-mm-state="closed"] .m002-minimap-svg{display:none;}
.m002-minimap-head{display:flex;justify-content:space-between;align-items:center;padding:4px 8px;}
.m002-minimap-title{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:9px;color:#5a5f6e;letter-spacing:2px;}
.m002-minimap-toggle{background:transparent;border:none;color:#7a7f8e;cursor:pointer;font-size:11px;line-height:1;padding:0 4px;}
.m002-minimap-toggle:hover{color:#ff003c;}
.m002-minimap-svg{width:100%;height:120px;display:block;cursor:crosshair;background:#06060a;}

.m002-vlan-legend{display:none;}
.m002-vlan-legend-title{display:none;}
.m002-vlan-legend-body{display:flex;flex-direction:column;gap:6px;}
.m002-vlan-legend-list{display:flex;flex-direction:column;gap:3px;flex:1 1 auto;min-height:0;overflow-y:auto;padding-right:4px;}
.m002-vlan-row{display:grid;grid-template-columns:8px 28px 1fr 18px;gap:6px;align-items:center;padding:4px 6px;background:#06060a;border:1px solid #1a1a22;cursor:pointer;transition:.12s;}
.m002-vlan-row:hover{border-color:var(--vc);}
.m002-vlan-row.is-solo{background:rgba(255,255,255,0.04);border-color:var(--vc);box-shadow:0 0 6px rgba(255,255,255,0.06),inset 0 0 6px var(--vc);}
.m002-vlan-row.is-solo .m002-vlan-row-dot{box-shadow:0 0 6px var(--vc),0 0 14px var(--vc);}
.m002-vlan-row.is-dimmed{opacity:.4;}
.m002-vlan-row.is-dimmed:hover{opacity:.85;}
.m002-vlan-row-dot{width:8px;height:8px;background:var(--vc);box-shadow:0 0 4px var(--vc),0 0 8px var(--vc);}
.m002-vlan-legend-filter{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:4px 6px;background:rgba(255,0,60,0.06);border:1px solid rgba(255,0,60,0.4);flex:0 0 auto;}
.m002-vlan-legend-filter-label{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;color:#ff5c7a;letter-spacing:1.5px;}
.m002-vlan-legend-clear{background:transparent;border:1px solid #ff003c;color:#ff003c;padding:2px 8px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;letter-spacing:1.5px;cursor:pointer;}
.m002-vlan-legend-clear:hover{background:rgba(255,0,60,0.15);}
.m002-vlan-row-id{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;color:var(--vc);letter-spacing:1px;}
.m002-vlan-row-name{background:transparent;border:none;color:#e8e8ee;padding:1px 4px;font-family:'Inter','Rajdhani',sans-serif;font-size:12px;outline:none;min-width:0;}
.m002-vlan-row-name:focus{background:rgba(255,255,255,0.04);}
.m002-vlan-row-rm{background:transparent;border:none;color:#5a5f6e;cursor:pointer;font-size:13px;line-height:1;padding:0;}
.m002-vlan-row-rm:hover{color:#ff003c;}
.m002-vlan-legend-list::-webkit-scrollbar{width:6px;}

/* L3 / Routing — subnet legend, link styles, dim, gateway badge */
.m002-subnet-legend-body{display:flex;flex-direction:column;gap:6px;}
.m002-subnet-legend-list{display:flex;flex-direction:column;gap:3px;flex:1 1 auto;min-height:0;overflow-y:auto;padding-right:4px;}
.m002-subnet-legend-list::-webkit-scrollbar{width:6px;}
.m002-subnet-legend-empty{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;color:#5a5f6e;letter-spacing:1px;font-style:italic;}
.m002-subnet-row{display:grid;grid-template-columns:8px auto 1fr 18px;gap:6px;align-items:center;padding:4px 6px;background:#06060a;border:1px solid #1a1a22;cursor:pointer;transition:.12s;}
.m002-subnet-row:hover{border-color:var(--sc);}
.m002-subnet-row.is-solo{background:rgba(255,255,255,0.04);border-color:var(--sc);box-shadow:0 0 6px rgba(255,255,255,0.06),inset 0 0 6px var(--sc);}
.m002-subnet-row.is-solo .m002-subnet-row-dot{box-shadow:0 0 6px var(--sc),0 0 14px var(--sc);}
.m002-subnet-row.is-dimmed{opacity:.4;}
.m002-subnet-row.is-dimmed:hover{opacity:.85;}
.m002-subnet-row-dot{width:8px;height:8px;background:var(--sc);box-shadow:0 0 4px var(--sc),0 0 8px var(--sc);}
.m002-subnet-row-cidr{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;color:var(--sc);letter-spacing:1px;white-space:nowrap;}
.m002-subnet-row-name{background:transparent;border:none;color:#e8e8ee;padding:1px 4px;font-family:'Inter','Rajdhani',sans-serif;font-size:12px;outline:none;min-width:0;}
.m002-subnet-row-name:focus{background:rgba(255,255,255,0.04);}
.m002-subnet-row-rm{background:transparent;border:none;color:#5a5f6e;cursor:pointer;font-size:13px;line-height:1;padding:0;}
.m002-subnet-row-rm:hover{color:#ff003c;}
.m002-subnet-legend-add{display:flex;gap:4px;flex:0 0 auto;align-items:stretch;}
.m002-subnet-legend-input{flex:1 1 0;min-width:0;background:#06060a;border:1px solid #1a1a22;color:#e8e8ee;padding:4px 8px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;outline:none;}
.m002-subnet-legend-input:focus{border-color:#35ff7a;}
.m002-subnet-legend-add-btn{background:transparent;border:1px solid #35ff7a;color:#35ff7a;padding:4px 10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;letter-spacing:1.5px;cursor:pointer;white-space:nowrap;flex:0 0 auto;align-self:stretch;}
.m002-subnet-legend-add-btn:hover{background:rgba(53,255,122,0.1);}
.m002-subnet-legend-auto{background:transparent;border:1px dashed #2a2a36;color:#7a7f8e;padding:3px 8px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:9px;letter-spacing:1.5px;cursor:pointer;align-self:flex-start;}
.m002-subnet-legend-auto:hover{border-color:#35ff7a;color:#35ff7a;}

/* L3 inspector — interfaces + routes table.
   Same framed scaffolding as .m002-insp-details (border + faint BG +
   summary-toggle row), but green accent on [open] because L3 reads as the
   routing layer color. */
.m002-insp-l3{margin:2px 0 6px;border:1px solid #1a1a22;background:rgba(10,10,16,0.6);}
.m002-insp-l3 > summary{cursor:pointer;padding:6px 10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;color:#a0a6b4;letter-spacing:1.6px;list-style:none;user-select:none;transition:.15s;}
.m002-insp-l3 > summary::-webkit-details-marker{display:none;}
.m002-insp-l3 > summary::before{content:'▸ ';color:#5a5f6e;}
.m002-insp-l3[open] > summary{color:#35ff7a;border-bottom:1px solid #1a1a22;}
.m002-insp-l3[open] > summary::before{content:'▾ ';color:#35ff7a;}
.m002-insp-l3 > summary:hover{color:#35ff7a;}
.m002-l3-block{padding:8px 10px;}
.m002-l3-block + .m002-l3-block{padding-top:0;}
.m002-l3-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;color:#a0a6b4;letter-spacing:1.5px;}
.m002-l3-head-actions{display:flex;gap:4px;}
.m002-iface-list,.m002-route-list{display:flex;flex-direction:column;gap:3px;}
.m002-iface-head{display:grid;grid-template-columns:8px 0.7fr 1.2fr 0.5fr 1.2fr 18px;gap:4px;align-items:center;padding:0 6px 2px 6px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:8px;color:#8a90a0;letter-spacing:1.5px;text-transform:uppercase;}
/* Endpoint / cloud / switch single-row L3 — IP + PREFIX, no NAME / GATEWAY */
.m002-iface-row--non-l3{grid-template-columns:8px 1.4fr 0.6fr 18px !important;}
.m002-iface-head--non-l3{grid-template-columns:8px 1.4fr 0.6fr 18px !important;}
.m002-route-head{display:grid;grid-template-columns:1.2fr 1.2fr 1fr 0.6fr 18px;gap:4px;align-items:center;padding:0 6px 2px 6px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:8px;color:#8a90a0;letter-spacing:1.5px;text-transform:uppercase;}
.m002-iface-row{display:grid;grid-template-columns:8px 0.7fr 1.2fr 0.5fr 1.2fr 18px;gap:4px;align-items:center;padding:3px 6px;background:#06060a;border:1px solid #1a1a22;transition:.12s;}
.m002-iface-row:hover{border-color:var(--sc);}
.m002-iface-dot{width:8px;height:8px;background:var(--sc);box-shadow:0 0 3px var(--sc);}
.m002-iface-row input,.m002-iface-row select,.m002-route-row input,.m002-route-row select{background:transparent;border:1px solid #1a1a22;color:#e8e8ee;padding:2px 4px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;outline:none;min-width:0;}
.m002-iface-row input:focus,.m002-iface-row select:focus,.m002-route-row input:focus,.m002-route-row select:focus{border-color:#35ff7a;}
.m002-iface-rm,.m002-route-rm{background:transparent;border:none;color:#5a5f6e;cursor:pointer;font-size:13px;line-height:1;padding:0;}
.m002-iface-rm:hover,.m002-route-rm:hover{color:#ff003c;}
.m002-route-row{display:grid;grid-template-columns:1.2fr 1.2fr 1fr 0.6fr 18px;gap:4px;align-items:center;padding:3px 6px;background:#06060a;border:1px solid #1a1a22;}
.m002-route-row.is-default{border-color:#ffae00;}
.m002-route-row.is-default .m002-route-dst{color:#ffae00;}

/* Routing-layer canvas dimming + link styles. Collapsed stacks get the
   same fade rule as plain devices so a switch-only stack doesn't suddenly
   look L3-active just because it's collapsed. */
.m002-host[data-active-layer="routing"] .m002-device[data-l3="false"],
.m002-host[data-active-layer="routing"] .m002-stack-collapsed[data-l3="false"]{opacity:.18;filter:saturate(0) brightness(.55);}
.m002-host[data-active-layer="routing"] .m002-device[data-l3="false"]:hover,
.m002-host[data-active-layer="routing"] .m002-stack-collapsed[data-l3="false"]:hover{opacity:.9;filter:saturate(.85) brightness(.95);}
/* VLAN solo dim — two diagnostic tiers:
   - unmatched (isolated OR adjacent): no VLAN configured here → fade to grey
   - configured-only: VLAN at device level but no port carries it → amber
     ("declared but not wired"), softly pulsing so it stands out */
@keyframes m002-vsolo-amber-pulse {
  0%, 100% { filter: drop-shadow(0 0 4px #ffbf3c) drop-shadow(0 0 11px #ffbf3c); }
  50%      { filter: drop-shadow(0 0 9px #ffbf3c) drop-shadow(0 0 28px #ffbf3c); }
}
/* Smooth in/out for solo dimming. Targets every canvas element that can carry
   a data-vlan-solo attribute so a toggle/clear interpolates instead of snapping. */
.m002-host[data-active-layer="vlan"] .m002-device,
.m002-host[data-active-layer="vlan"] .m002-stack-collapsed,
.m002-host[data-active-layer="vlan"] .m002-stack-envelope{transition:opacity .6s ease-out, filter .6s ease-out;}
/* Links get redrawn on solo change (stripes/count badges depend on the filter,
   so we can't just toggle attributes). The applyVlanSoloVisuals path tags the
   freshly drawn <g>s with .m002-vsolo-fade so they ease in from 0 toward
   their resting opacity (1 for matching links, .2 for dimmed ones). Leaving
   the `to` keyframe out lets the animation interpolate to whatever the rule
   below specifies, so dimmed links don't pop to full opacity then snap back. */
@keyframes m002-vsolo-link-fade { from { opacity: 0; } }
.m002-link.m002-vsolo-fade,
.m002-laglink.m002-vsolo-fade{animation:m002-vsolo-link-fade .55s ease-out;}
/* Links that don't carry any of the soloed VLANs fade to the background so
   the matching ones stand out. Transition smooths toggle on/off without
   needing a wholesale link rebuild. */
.m002-host[data-active-layer="vlan"] .m002-link,
.m002-host[data-active-layer="vlan"] .m002-laglink{transition:opacity .6s ease-out;}
.m002-host[data-active-layer="vlan"] .m002-link.m002-link-vsolo-dim,
.m002-host[data-active-layer="vlan"] .m002-laglink.m002-link-vsolo-dim{opacity:.07;}
.m002-host[data-active-layer="vlan"] .m002-link.m002-link-vsolo-dim:hover,
.m002-host[data-active-layer="vlan"] .m002-laglink.m002-link-vsolo-dim:hover{opacity:.4;}
/* Asymmetric link — VLAN configured on one endpoint only, the other side
   isn't tagged. Mirrors the configured-only amber state on devices/stacks
   ("declared but not wired through"). Opacity-only pulse (no drop-shadow)
   so the warning stays distinguishable from a genuinely amber-coloured VLAN
   without bringing back the glow halos that were stripped in v2.33.26.
   Phase + duration mirror m002-vsolo-amber-pulse (devices/stacks): both peak
   at the 50% mark so an asym link pulses *with* the configured-only device
   it's attached to, not against it. Trough is intentionally very deep (0.10)
   so the cadence reads strongly even at small zooms — opacity is perceived
   roughly along a power curve, so dropping the trough from 0.22 to 0.10
   reads as ~30 % more pulse swing rather than the linear ~15 %. */
@keyframes m002-link-vsolo-asym-pulse {
  0%, 100% { opacity: 0.10; }
  50%      { opacity: 1; }
}
.m002-host[data-active-layer="vlan"] .m002-link.m002-link-vsolo-asym,
.m002-host[data-active-layer="vlan"] .m002-laglink.m002-link-vsolo-asym{animation:m002-link-vsolo-asym-pulse 2.4s ease-in-out infinite;}
.m002-host[data-active-layer="vlan"] .m002-link.m002-link-vsolo-asym .m002-link-line,
.m002-host[data-active-layer="vlan"] .m002-link.m002-link-vsolo-asym .m002-lag-line,
.m002-host[data-active-layer="vlan"] .m002-laglink.m002-link-vsolo-asym .m002-link-line,
.m002-host[data-active-layer="vlan"] .m002-laglink.m002-link-vsolo-asym .m002-lag-line{stroke:#ffbf3c !important;}
/* Stacking cables ride along with the parent stack envelope's solo state —
   when the stack itself is dimmed/amber, the internal cables follow so they
   don't visually contradict the wrapper. */
.m002-host[data-active-layer="vlan"] .m002-stacklink{transition:opacity .6s ease-out;}
.m002-host[data-active-layer="vlan"] .m002-stacklink[data-vlan-solo="unmatched-isolated"],
.m002-host[data-active-layer="vlan"] .m002-stacklink[data-vlan-solo="unmatched-adjacent"]{opacity:.07;}
.m002-host[data-active-layer="vlan"] .m002-stacklink[data-vlan-solo="configured-only"]{animation:m002-link-vsolo-asym-pulse 2.4s ease-in-out infinite;}
.m002-host[data-active-layer="vlan"] .m002-stacklink[data-vlan-solo="configured-only"] .m002-stack-cable{stroke:#ffbf3c !important;opacity:1;}
.m002-host[data-active-layer="vlan"] .m002-device[data-vlan-solo="unmatched-isolated"],
.m002-host[data-active-layer="vlan"] .m002-device[data-vlan-solo="unmatched-adjacent"],
.m002-host[data-active-layer="vlan"] .m002-stack-collapsed[data-vlan-solo="unmatched-isolated"],
.m002-host[data-active-layer="vlan"] .m002-stack-collapsed[data-vlan-solo="unmatched-adjacent"],
.m002-host[data-active-layer="vlan"] .m002-stack-envelope[data-vlan-solo="unmatched-isolated"],
.m002-host[data-active-layer="vlan"] .m002-stack-envelope[data-vlan-solo="unmatched-adjacent"]{opacity:.18 !important;filter:saturate(0) brightness(.55) !important;}
.m002-host[data-active-layer="vlan"] .m002-device[data-vlan-solo="unmatched-isolated"]:hover,
.m002-host[data-active-layer="vlan"] .m002-device[data-vlan-solo="unmatched-adjacent"]:hover,
.m002-host[data-active-layer="vlan"] .m002-stack-collapsed[data-vlan-solo="unmatched-isolated"]:hover,
.m002-host[data-active-layer="vlan"] .m002-stack-collapsed[data-vlan-solo="unmatched-adjacent"]:hover,
.m002-host[data-active-layer="vlan"] .m002-stack-envelope[data-vlan-solo="unmatched-isolated"]:hover,
.m002-host[data-active-layer="vlan"] .m002-stack-envelope[data-vlan-solo="unmatched-adjacent"]:hover{opacity:.9 !important;filter:saturate(.85) brightness(.95) !important;}
.m002-host[data-active-layer="vlan"] .m002-device[data-vlan-solo="configured-only"],
.m002-host[data-active-layer="vlan"] .m002-stack-collapsed[data-vlan-solo="configured-only"],
.m002-host[data-active-layer="vlan"] .m002-stack-envelope[data-vlan-solo="configured-only"]{animation:m002-vsolo-amber-pulse 2.4s ease-in-out infinite;}
.m002-host[data-active-layer="vlan"] .m002-device[data-vlan-solo="configured-only"] .m002-dev-bg,
.m002-host[data-active-layer="vlan"] .m002-stack-collapsed[data-vlan-solo="configured-only"] .m002-dev-bg{stroke:#ffbf3c;}
.m002-host[data-active-layer="vlan"] .m002-stack-envelope[data-vlan-solo="configured-only"] .m002-stack-env-bg{stroke:#ffbf3c;}

/* Routing-solo dimming — sibling of VLAN solo for the routing layer. When the
   user solos one or more subnets (click in the subnet legend) every device,
   stack, envelope and link gets a data-routing-solo state stamped by JS.
   Non-participants fade to the background so the chosen subnet stands out;
   transit devices on the L3 ribbon path keep their participating state and
   remain lit. Same opacity/saturation values as VLAN-solo so both layers
   read with identical visual weight. */
.m002-host[data-active-layer="routing"] .m002-device,
.m002-host[data-active-layer="routing"] .m002-stack-collapsed,
.m002-host[data-active-layer="routing"] .m002-stack-envelope{transition:opacity .6s ease-out, filter .6s ease-out;}
.m002-host[data-active-layer="routing"] .m002-device[data-routing-solo="unmatched-isolated"],
.m002-host[data-active-layer="routing"] .m002-device[data-routing-solo="unmatched-adjacent"],
.m002-host[data-active-layer="routing"] .m002-stack-collapsed[data-routing-solo="unmatched-isolated"],
.m002-host[data-active-layer="routing"] .m002-stack-collapsed[data-routing-solo="unmatched-adjacent"],
.m002-host[data-active-layer="routing"] .m002-stack-envelope[data-routing-solo="unmatched-isolated"],
.m002-host[data-active-layer="routing"] .m002-stack-envelope[data-routing-solo="unmatched-adjacent"]{opacity:.18 !important;filter:saturate(0) brightness(.55) !important;}
.m002-host[data-active-layer="routing"] .m002-device[data-routing-solo="unmatched-isolated"]:hover,
.m002-host[data-active-layer="routing"] .m002-device[data-routing-solo="unmatched-adjacent"]:hover,
.m002-host[data-active-layer="routing"] .m002-stack-collapsed[data-routing-solo="unmatched-isolated"]:hover,
.m002-host[data-active-layer="routing"] .m002-stack-collapsed[data-routing-solo="unmatched-adjacent"]:hover,
.m002-host[data-active-layer="routing"] .m002-stack-envelope[data-routing-solo="unmatched-isolated"]:hover,
.m002-host[data-active-layer="routing"] .m002-stack-envelope[data-routing-solo="unmatched-adjacent"]:hover{opacity:.9 !important;filter:saturate(.85) brightness(.95) !important;}
/* Stacking cables ride along with their parent envelope's solo state. */
.m002-host[data-active-layer="routing"] .m002-stacklink{transition:opacity .6s ease-out;}
.m002-host[data-active-layer="routing"] .m002-stacklink[data-routing-solo="unmatched-isolated"],
.m002-host[data-active-layer="routing"] .m002-stacklink[data-routing-solo="unmatched-adjacent"]{opacity:.07;}
/* Links that don't carry the soloed subnet fade to the background. The L3
   ribbon above already speaks through the colour stripe, so dimming the
   underlying L2 wire pulls the eye onto the active subnet's path. */
.m002-host[data-active-layer="routing"] .m002-link,
.m002-host[data-active-layer="routing"] .m002-laglink{transition:opacity .6s ease-out;}
.m002-host[data-active-layer="routing"] .m002-link.m002-link-rsolo-dim,
.m002-host[data-active-layer="routing"] .m002-laglink.m002-link-rsolo-dim{opacity:.07;}
.m002-host[data-active-layer="routing"] .m002-link.m002-link-rsolo-dim:hover,
.m002-host[data-active-layer="routing"] .m002-laglink.m002-link-rsolo-dim:hover{opacity:.4;}

.m002-link-l3{transition:stroke-width .15s;}
.m002-link-l3-glow{opacity:.22;filter:blur(2px);pointer-events:none;}
/* Detached L3 ribbons — smooth Catmull-Rom curves through L3 endpoints.
   They live in their own SVG group above the link layer so they read as a
   logical overlay rather than a re-skinned wire. fill:none keeps the curve
   transparent inside concave bends; pointer-events:none stops them from
   eating clicks meant for devices. */
/* Routing layer: kill the L2 link-flow pulse so the only animated stream is
   the L3 ribbon aimed at the gateway. Selection still highlights, but the
   pulse on the underlying L2 wire would compete visually. */
.m002-host[data-active-layer="routing"] .m002-link-flow{display:none;}
.m002-l3-paths path{fill:none;pointer-events:none;}
.m002-l3-path{stroke-width:2.5;opacity:.95;stroke-linecap:round;stroke-linejoin:round;}
.m002-l3-path-glow{stroke-width:8;opacity:.22;filter:blur(2.5px);}
/* Flow pulse — short bright dash riding along the ribbon. Direction is
   driven from JS via data-flow-dir + the rAF ticker (CSS keyframes are
   silenced under prefers-reduced-motion on corporate Windows profiles). */
.m002-l3-path-flow{stroke-width:3.2;stroke-dasharray:6 92;opacity:.95;stroke-linecap:round;filter:drop-shadow(0 0 4px currentColor);}
.m002-link-l3-label{font-size:10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;letter-spacing:1px;font-weight:500;paint-order:stroke fill;stroke:#0a0a10;stroke-width:3.5px;stroke-linejoin:round;stroke-linecap:round;}
.m002-link-transit-label{font-size:10px;font-family:'JetBrains Mono','Share Tech Mono',monospace;letter-spacing:2px;font-weight:600;paint-order:stroke fill;stroke:#0a0a10;stroke-width:3.5px;stroke-linejoin:round;stroke-linecap:round;}
.m002-dev-l3{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:9px;letter-spacing:1px;fill:#35ff7a;display:none;}
.m002-host[data-active-layer="routing"] .m002-dev-l3{display:block;}
.m002-host[data-active-layer="routing"] .m002-dev-ip{display:none;}
.m002-dev-gw-badge{display:none;}
.m002-dev-gw-badge rect{fill:rgba(255,174,0,0.12);stroke:#ffae00;stroke-width:1;}
.m002-dev-gw-badge text{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:8px;letter-spacing:1px;font-weight:700;fill:#ffae00;}
.m002-host[data-active-layer="routing"] .m002-dev-gw-badge{display:block;}
.m002-action.small{padding:2px 8px;font-size:9px;}

/* L3 trio for non-L3 device inspector (endpoint/cloud/switch) */
.m002-field-l3-trio{display:flex;flex-direction:column;gap:4px;}
.m002-l3-trio{display:grid;grid-template-columns:1.4fr 0.6fr 1.4fr;gap:4px;}
.m002-l3-trio input,.m002-l3-trio select{background:#06060a;border:1px solid #1a1a22;color:#e8e8ee;padding:4px 8px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:12px;outline:none;min-width:0;}
.m002-l3-trio input:focus,.m002-l3-trio select:focus{border-color:#35ff7a;}

.m002-vlan-legend-list::-webkit-scrollbar-thumb{background:#1a1a22;}
.m002-vlan-legend-empty{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;color:#5a5f6e;letter-spacing:1px;}
.m002-vlan-legend-chip{display:inline-flex;align-items:center;gap:6px;padding:3px 8px;background:rgba(0,0,0,0.4);border:1px solid var(--vc);color:var(--vc);font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;letter-spacing:1px;}
.m002-vlan-legend-dot{width:8px;height:8px;background:var(--vc);box-shadow:0 0 4px var(--vc),0 0 8px var(--vc);}

.m002-statusbar{position:absolute;bottom:16px;left:18px;z-index:5;display:flex;align-items:center;gap:8px;background:rgba(8,8,14,0.85);border:1px solid #1a1a22;padding:6px 12px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;letter-spacing:1.5px;color:#9aa0a8;}
.m002-stat-tag{color:#ff003c;}
.m002-stat-sep{color:#2a2a36;}
.m002-stat-mode{color:#e8e8ee;}

.m002-toast-stack{position:absolute;bottom:24px;left:50%;transform:translateX(-50%);display:flex;flex-direction:column;align-items:center;gap:6px;pointer-events:none;z-index:1000;}
.m002-toast-item{background:rgba(8,8,14,0.95);border:1px solid #ff003c;padding:8px 16px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;letter-spacing:1.5px;color:#ff003c;opacity:0;transform:translateY(20px);transition:opacity .25s ease,transform .25s ease;white-space:nowrap;box-shadow:0 0 12px rgba(255,0,60,0.25);}
.m002-toast-item.show{opacity:1;transform:translateY(0);}
.m002-toast-item.leave{opacity:0;transform:translateY(-6px);}

.m002-inspector::-webkit-scrollbar,.m002-ports-grid::-webkit-scrollbar{width:6px;}
.m002-inspector::-webkit-scrollbar-thumb,.m002-ports-grid::-webkit-scrollbar-thumb{background:#1a1a22;}

/* Detail-View choreographed entry:
     • backdrop fades in + blur builds up (~520ms)
     • head bar slides down from -12px
     • central element: pinpoint → vertical line → expand horizontally
       (~500ms total, runs over phases inside one keyframe set)
     • ports pop in one-by-one from the centre (delay = base + i*stagger)
     • port stubs fade in last, each one gated by its own port's delay
   Exit reuses the overlay opacity transition only; individual elements
   fade out implicitly with the parent so the exit feels snappier. */
.m002-detail-overlay{position:absolute;inset:0;z-index:10;display:flex;flex-direction:column;background:radial-gradient(ellipse at 50% 0%,rgba(13,13,20,0.96) 0%,rgba(6,6,10,0.985) 70%);opacity:0;transition:opacity 190ms cubic-bezier(0.4,0,1,1);pointer-events:auto;backdrop-filter:blur(0px);-webkit-backdrop-filter:blur(0px);}
.m002-detail-overlay.m002-detail-overlay-show{opacity:1;transition:opacity 350ms cubic-bezier(0.16,1,0.3,1),backdrop-filter 350ms cubic-bezier(0.16,1,0.3,1),-webkit-backdrop-filter 350ms cubic-bezier(0.16,1,0.3,1);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);}
.m002-detail-head{display:flex;align-items:center;gap:14px;padding:12px 18px;border-bottom:1px solid #1a1a22;background:rgba(8,8,14,0.92);transform:translateY(-12px);opacity:0;transition:transform 190ms cubic-bezier(0.4,0,1,1),opacity 190ms cubic-bezier(0.4,0,1,1);}
.m002-detail-overlay.m002-detail-overlay-show .m002-detail-head{transform:translateY(0);opacity:1;transition:transform 350ms cubic-bezier(0.16,1,0.3,1) 60ms,opacity 270ms cubic-bezier(0.16,1,0.3,1) 60ms;}
.m002-detail-back{display:inline-flex;align-items:center;gap:8px;background:transparent;border:1px solid #1a1a22;color:#e8e8ee;padding:6px 12px;font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;letter-spacing:1.6px;cursor:pointer;transition:.15s;}
.m002-detail-back:hover{border-color:#ff003c;color:#ff003c;background:rgba(255,0,60,0.06);}
.m002-detail-back-glyph{font-size:14px;line-height:1;}
.m002-detail-title{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:12px;letter-spacing:2px;color:#9aa0a8;}
.m002-detail-spacer{flex:1;}
.m002-detail-body{flex:1;min-height:0;display:flex;align-items:center;justify-content:center;padding:24px;overflow:auto;cursor:zoom-out;}
.m002-detail-svg{display:block;max-width:100%;max-height:100%;}

/* Initial states for choreographed entry. Without these the elements pop in
   visible at frame 0 before the keyframe animation overrides on .show.
   Note: the *inner* wrappers carry the scale; the outer groups keep their
   SVG translate untouched so the port grid stays in place. */
.m002-detail-device-inner{transform:scale(0.005,0.02);opacity:0;}
.m002-detail-overlay .m002-detail-port-inner{transform:scale(0.6);opacity:0;}
.m002-detail-overlay .m002-detail-stub{opacity:0;}

/* Element entry choreography (1000ms total, 50ms lead-in delay):
     0–333ms   point appears, then vertical line grows to full height
     333–400ms HOLD — same scale, no motion (67ms breath)
     400–1000ms horizontal expansion to full width (600ms)
   Percentages map 33%→333ms, 40%→400ms, 100%→1000ms.
   Initial scale(0.005,0.02): 720×0.005=3.6 wide, 180×0.02=3.6 tall — a
   roughly square point. The fill animation (m002-detail-elem-fill) keeps
   the rect accent-coloured during the point/line phase so it reads as a
   clean blue line, then crossfades to the dark fill during horizontal
   expansion. */
@keyframes m002-detail-elem-emerge{
  0%   {transform:scale(0.005,0.02);opacity:0;}
  5%   {transform:scale(0.005,0.02);opacity:1;}
  33%  {transform:scale(0.005,1);   opacity:1;}
  40%  {transform:scale(0.005,1);   opacity:1;}
  100% {transform:scale(1,1);       opacity:1;}
}
@keyframes m002-detail-elem-fill{
  0%,40%   {fill:var(--accent);}
  60%,100% {fill:#0a0a10;}
}
@keyframes m002-detail-port-pop{
  0%   {transform:scale(0.6); opacity:0;}
  60%  {transform:scale(1.08);opacity:1;}
  100% {transform:scale(1);   opacity:1;}
}
/* Gentler variant for hop-in. The 60%-overshoot in port-pop reads as a
   "boing" snap when 24 ports do it within a 280ms window — combined with
   a 12ms stagger that's so tight the wave is invisible, the user
   reported "ich kann nicht mal sagen, ob da überhaupt eine Animation
   abgespielt wird". port-glide-in starts at scale(0.88) instead of 0.6
   (smaller initial = subtler grow), no overshoot, smoother sustained
   easing. The hop-in CSS rule pairs it with a 1.5× stagger multiplier so
   the per-port wave is actually perceivable. */
@keyframes m002-detail-port-glide-in{
  0%   {transform:scale(0.88); opacity:0;}
  100% {transform:scale(1);    opacity:1;}
}
@keyframes m002-detail-stub-fade{
  0%   {opacity:0;}
  100% {opacity:1;}
}

/* Reverse-pop choreography — mirrors the entry keyframes above, used when
   the user hops to a peer (everything except the survivor reverse-pops out
   before the survivor flies to centre). Stagger is reversed too: smaller
   pieces (stubs, ports, peer-tiles) leave first, central device collapses
   last, so the device "swallows" its surroundings before vanishing. */
@keyframes m002-detail-elem-collapse{
  0%   {transform:scale(1,1);        opacity:1;}
  60%  {transform:scale(0.005,1);    opacity:1;}
  67%  {transform:scale(0.005,1);    opacity:1;}
  95%  {transform:scale(0.005,0.02); opacity:1;}
  100% {transform:scale(0.005,0.02); opacity:0;}
}
@keyframes m002-detail-elem-empty{
  0%,40%   {fill:#0a0a10;}
  60%,100% {fill:var(--accent);}
}
@keyframes m002-detail-port-suck{
  0%   {transform:scale(1);   opacity:1;}
  100% {transform:scale(0.6); opacity:0;}
}
@keyframes m002-detail-stub-fade-out{
  0%   {opacity:1;}
  100% {opacity:0;}
}

.m002-detail-overlay.m002-detail-overlay-show .m002-detail-port-inner{animation:m002-detail-port-pop 215ms cubic-bezier(0.34,1.4,0.5,1) forwards;animation-delay:var(--enter-delay,700ms);}
.m002-detail-overlay.m002-detail-overlay-show .m002-detail-stub{animation:m002-detail-stub-fade 255ms ease-out forwards;animation-delay:calc(var(--enter-delay,700ms) + 200ms);}
/* Settled state — applied ~1100ms after entry. Lets render(s) re-render the
   detail body during edits without re-running the choreographed entry.
   Centre + peer pins live in the unified-tile block further down (operate
   on .m002-detail-tile-inner / -tile-bg). */
.m002-detail-overlay-settled .m002-detail-port-inner{animation:none!important;transform:none!important;opacity:1!important;}
.m002-detail-overlay-settled .m002-detail-stub{animation:none!important;opacity:1!important;}
.m002-detail-port{cursor:pointer;}
.m002-detail-port-box{transition:filter .15s,stroke .15s,stroke-width .15s;}
.m002-detail-port:hover .m002-detail-port-box{stroke:var(--accent);filter:drop-shadow(0 0 2px var(--accent)) drop-shadow(0 0 8px var(--accent));}
.m002-detail-port.is-selected .m002-detail-port-box{stroke:var(--accent);stroke-width:2.4;filter:drop-shadow(0 0 4px var(--accent)) drop-shadow(0 0 12px var(--accent));}
.m002-detail-port-num{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:14px;fill:#e8e8ee;letter-spacing:.5px;font-weight:600;}
.m002-detail-port.is-empty .m002-detail-port-num{fill:#5a5f6e;}
.m002-detail-port-peer{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;fill:#9aa0a8;letter-spacing:.5px;font-weight:600;}
.m002-detail-port-name{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:11px;fill:#9aa0a8;letter-spacing:.5px;font-weight:600;}
.m002-detail-stub{pointer-events:none;}

/* Peer-link — the wire connecting an uplink port on the central tile to
   its peer-tile above. Reuses .m002-link / .m002-link-hit / .m002-link-line
   from the grid so hover, selection, and theming match the canvas. */
.m002-detail-peer-link{opacity:0;}
.m002-detail-overlay.m002-detail-overlay-show .m002-detail-peer-link{
  /* 800ms delay = 100ms after peer-tiles + uplink-ports start at 700ms,
     so the wire visibly draws between two endpoints that have already
     arrived. The whole "upper row" group still reads as one wave to the
     user — peer-tile + port endpoints first, link bridging them right
     behind. */
  animation:m002-detail-stub-fade 320ms ease-out forwards;
  animation-delay:800ms;
}
.m002-detail-overlay-settled .m002-detail-peer-link{animation:none!important;opacity:1!important;}

/* Detail-view stack-cable — gets the same entry/hop/exit timing as the
   peer-link above so dashed stack wires arrive in the same upper-row
   wave instead of popping in instantly. The wrapper-group carries the
   fade; the inner <path class="m002-stack-cable"> keeps its grid
   stroke-opacity .75, so the composited end opacity stays at the
   familiar grid look (wrapper 1 × inner-stroke 0.75 = 0.75). */
.m002-detail-stack-cable{opacity:0;}
.m002-detail-overlay.m002-detail-overlay-show .m002-detail-stack-cable{
  animation:m002-detail-stub-fade 320ms ease-out forwards;
  animation-delay:800ms;
}
.m002-detail-overlay-settled .m002-detail-stack-cable{animation:none!important;opacity:1!important;}

/* ============================================================================
   Hop transient animations (v2.34 Push C, fixed v2.34.3). When the user
   clicks a peer-tile for a Position-Swap, the *tiles* glide via FLIP
   (Push A) while the *port-row + peer-links* fade out → re-render → fade
   in around them so the swap reads as one cohesive "upper layer
   transitions" instead of a hard innerHTML replace.

   Crucial: hopToPeer REMOVES .m002-detail-overlay-settled before adding
   hop-out, so settled's !important pins (transform:none, opacity:1,
   animation:none) on port-inner / stub / peer-link are off — otherwise
   per the CSS spec, !important on a regular property beats an animation's
   keyframe values, and the animations would silently run with no visual
   effect. Settled is re-added at the end of the hop so subsequent live
   inspector edits don't replay entry choreography.

   Tile-inner / tile-bg freeze rules below pin those at identity during
   hop-out + hop-in (specificity 0,5,0 beats the .show entry rules at
   0,4,0) so the surviving tiles don't have their entry-emerge animation
   restart from frame 0 just because the animation property momentarily
   changed when settled was removed. */
.m002-detail-overlay.m002-detail-hop-out .m002-detail-port .m002-detail-port-inner{
  animation:m002-detail-port-suck 200ms cubic-bezier(0.4,0,1,1) forwards!important;
  pointer-events:none;
}
.m002-detail-overlay.m002-detail-hop-out .m002-detail-stub{
  animation:m002-detail-stub-fade-out 200ms cubic-bezier(0.4,0,1,1) forwards!important;
  pointer-events:none;
}
.m002-detail-overlay.m002-detail-hop-out .m002-detail-peer-link{
  animation:m002-detail-stub-fade-out 200ms cubic-bezier(0.4,0,1,1) forwards!important;
  pointer-events:none;
}
.m002-detail-overlay.m002-detail-hop-out .m002-detail-stack-cable{
  animation:m002-detail-stub-fade-out 200ms cubic-bezier(0.4,0,1,1) forwards!important;
  pointer-events:none;
}
/* Hop-in port animation. Gentler than the initial entry (cubic-bezier
   1.2 instead of 1.4 = less aggressive overshoot) + 280ms duration so
   each port reads as a deliberate settle rather than a snap. Per-port
   stagger via the --enter-delay variable that renderDetailPortsMarkup
   already sets inline (PORT_BASE_DELAY 700ms + i × PORT_STAGGER_MS):
   subtract the 700ms base via calc() so the cascade starts immediately
   at hop-in time but still spreads i × 12ms across the row, giving the
   "wave" feel without the simultaneous-bounce ruppy crowding the user
   flagged on v2.34.3.
   stub + peer-link fade in slightly behind the ports (peer-link last so
   the line draws between two visible endpoints). */
.m002-detail-overlay.m002-detail-hop-in .m002-detail-port .m002-detail-port-inner{
  animation:m002-detail-port-glide-in 380ms cubic-bezier(0.16,1,0.3,1) forwards!important;
  /* Stagger ×1.5 vs the initial entry: PORT_STAGGER_MS is 12ms in JS, so the
     raw cascade is i × 12ms. Multiplied to 18ms here so the wave reads as a
     wave instead of a near-simultaneous batch. Initial entry is unaffected
     because that path uses the var directly without the multiplier. */
  animation-delay:calc((var(--enter-delay,700ms) - 700ms) * 1.5)!important;
}
.m002-detail-overlay.m002-detail-hop-in .m002-detail-stub{
  animation:m002-detail-stub-fade 320ms cubic-bezier(0.16,1,0.3,1) forwards!important;
  animation-delay:200ms!important;
}
.m002-detail-overlay.m002-detail-hop-in .m002-detail-peer-link{
  animation:m002-detail-stub-fade 360ms cubic-bezier(0.16,1,0.3,1) forwards!important;
  animation-delay:260ms!important;
}
.m002-detail-overlay.m002-detail-hop-in .m002-detail-stack-cable{
  animation:m002-detail-stub-fade 360ms cubic-bezier(0.16,1,0.3,1) forwards!important;
  animation-delay:260ms!important;
}
/* Endpoint tiles during hop. Without these, fresh endpoint-tile-inner DOM
   created by sync at t=200ms inherits ONLY the .m002-detail-overlay-show
   rule (720ms delay + port-pop). Even with the `both` fill mode added
   above, endpoint tiles would only animate at t=200+720=920ms, leaving
   them invisible from t=200 to t=920 (720ms of black space below the
   centre tile) and then popping in 240ms after the uplink ports already
   appeared at t=680ms — the bottom row would lag noticeably behind the
   top row.
   Hop-out: suck the old endpoint-inner out (mirrors peer-tile suck —
   100ms delay, 280ms duration — so top + bottom rows fade in step).
   Hop-in: glide the new endpoint-inner in via the same gentle keyframe
   the uplink ports use, with a small 120ms delay relative to hop-in so
   the bottom row arrives just behind the uplink ports and reads as one
   coordinated lower-row settle. `both` fill so the 0%-keyframe state
   (scale 0.88 opacity 0 — invisible) applies during the 120ms delay
   too — without it, the user sees a brief default-visible flash before
   the animation starts. */
.m002-detail-overlay.m002-detail-hop-out .m002-detail-tile.is-endpoint .m002-detail-tile-inner{
  /* No delay — sync at HOP_OUT_MS=200ms removes the old endpoint tile DOM
     entirely, so any animation must complete within that 200ms window.
     180ms duration leaves a 20ms paint-budget margin so the suck reads as
     finished before the FLIP starts. */
  animation:m002-detail-port-suck 180ms cubic-bezier(0.4,0,1,1) forwards!important;
  pointer-events:none;
}
.m002-detail-overlay.m002-detail-hop-in .m002-detail-tile.is-endpoint .m002-detail-tile-inner{
  animation:m002-detail-port-glide-in 380ms cubic-bezier(0.16,1,0.3,1) 120ms both!important;
}
/* Tile-inner / tile-bg freeze for the ENTIRE hop window — added by
   hopToPeer at start, removed at settle. This is the master pin that
   prevents role-flips (peer → center, center → peer) from restarting
   the show rule's emerge / port-pop animations mid-glide; without it,
   the new center tile would re-emerge from scale(0.005, 0.02) and
   pass through the 33% "thin vertical line" keyframe phase right as
   the FLIP is gliding it down — that's the "vertikaler Strich" the
   user flagged. The hop-out / hop-in rules below cover the same
   ground defensively (in case hopping is somehow off while those
   classes are on). */
/* The hopping pin EXCLUDES fresh tiles. Fresh tiles are tiles that were
   spawned BY this very hop's syncDetailFrame (uplink-peers / stack-peers
   of the new centre that weren't visible in the old layout). They need
   to stay HIDDEN through hop-out + FLIP and only pop in at hop-in time
   along with the ports + peer-links — without this :not(.fresh) the
   hopping pin would force them to identity/visible the instant they
   land in the DOM, so they'd appear at full size BEFORE the swap is
   visually complete (the "Elemente erscheinen vor dem Swap" the user
   flagged on v2.34.9).
   For non-fresh tiles, the pin is unchanged: it freezes their inner
   transform/opacity at identity through the hop so the FLIP is the
   only motion the user sees on those tiles. */
.m002-detail-overlay.m002-detail-overlay-hopping .m002-detail-tile.is-center:not(.m002-detail-fresh) .m002-detail-tile-inner,
.m002-detail-overlay.m002-detail-overlay-hopping .m002-detail-tile.is-peer:not(.m002-detail-fresh) .m002-detail-tile-inner{
  animation:none!important;
  transform:none!important;
  opacity:1!important;
}
.m002-detail-overlay.m002-detail-overlay-hopping .m002-detail-tile.is-center:not(.m002-detail-fresh) .m002-detail-tile-bg,
.m002-detail-overlay.m002-detail-overlay-hopping .m002-detail-tile.is-peer:not(.m002-detail-fresh) .m002-detail-tile-bg{
  animation:none!important;
  fill:#0a0a10!important;
}
/* Fresh tiles during hop-out + FLIP rely on the show-rule's port-pop
   animation in its delay phase + `both` fill to stay at the 0%
   keyframe state (scale 0.6 / opacity 0). No explicit hide rule is
   needed here — the show rule already does the right thing for fresh
   tiles, AS LONG AS the hopping pin doesn't get in the way (hence the
   :not(.m002-detail-fresh) above).
   At hop-in landing, the override below replaces the show rule's
   slow 700ms-delayed pop with an immediate 0ms-delayed one, so fresh
   tiles arrive in the same wave as the freshly-rendered ports +
   peer-links. Specificity (0,6,0) beats the show entry rule (0,4,0)
   without !important — the cascade picks this animation, and
   animation-level transforms beat any non-!important static rule. */
.m002-detail-overlay.m002-detail-overlay-hopping.m002-detail-hop-in .m002-detail-tile.m002-detail-fresh .m002-detail-tile-inner{
  animation:m002-detail-port-pop 280ms cubic-bezier(0.34,1.2,0.5,1) 0ms both;
}

/* Defensive duplicate for hop-out + hop-in phases. Specificity (0,5,0)
   beats the show entry rules (0,4,0) so these win without needing the
   show rule itself to carry !important. */
.m002-detail-overlay.m002-detail-hop-out .m002-detail-tile.is-center .m002-detail-tile-inner,
.m002-detail-overlay.m002-detail-hop-out .m002-detail-tile.is-peer .m002-detail-tile-inner,
.m002-detail-overlay.m002-detail-hop-in .m002-detail-tile.is-center .m002-detail-tile-inner,
.m002-detail-overlay.m002-detail-hop-in .m002-detail-tile.is-peer .m002-detail-tile-inner{
  animation:none!important;
  transform:none!important;
  opacity:1!important;
}
.m002-detail-overlay.m002-detail-hop-out .m002-detail-tile.is-center .m002-detail-tile-bg,
.m002-detail-overlay.m002-detail-hop-out .m002-detail-tile.is-peer .m002-detail-tile-bg,
.m002-detail-overlay.m002-detail-hop-in .m002-detail-tile.is-center .m002-detail-tile-bg,
.m002-detail-overlay.m002-detail-hop-in .m002-detail-tile.is-peer .m002-detail-tile-bg{
  animation:none!important;
  fill:#0a0a10!important;
}

/* ============================================================================
   Reverse-exit (v2.34 Push B). Mirrors the initial emerge — central tile
   collapses point→line→pinhole, peers + ports + stubs + peer-links fade
   out in parallel ahead of it so the centre is the last visual element to
   vanish. Triggered by .m002-detail-overlay-leaving (added by
   exitDetailView, removed when the overlay is hidden). Keeps the same
   keyframes the legacy WAAPI hop used (m002-detail-elem-collapse,
   m002-detail-port-suck, m002-detail-stub-fade-out, m002-detail-elem-empty)
   so we get the reverse motion for free.

   Selectors triple-class (.m002-detail-overlay.m002-detail-overlay-leaving
   .m002-detail-tile.is-center .m002-detail-tile-inner) to outweigh the
   .show entry rules in the cascade; !important to override the settled
   pin (the JS deliberately leaves .m002-detail-overlay-settled active to
   keep tiles' transforms stable until the leaving rules take over).
   ============================================================================ */
.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-stub{
  animation:m002-detail-stub-fade-out 200ms cubic-bezier(0.4,0,1,1) forwards!important;
  pointer-events:none;
}
.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-peer-link{
  animation:m002-detail-stub-fade-out 200ms cubic-bezier(0.4,0,1,1) forwards!important;
  pointer-events:none;
}
.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-stack-cable{
  animation:m002-detail-stub-fade-out 200ms cubic-bezier(0.4,0,1,1) forwards!important;
  pointer-events:none;
}
.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-port .m002-detail-port-inner{
  animation:m002-detail-port-suck 240ms cubic-bezier(0.4,0,1,1) forwards!important;
  pointer-events:none;
}
.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-tile.is-peer .m002-detail-tile-inner{
  animation:m002-detail-port-suck 280ms cubic-bezier(0.4,0,1,1) 100ms forwards!important;
  pointer-events:none;
}
.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-tile.is-center .m002-detail-tile-inner{
  animation:m002-detail-elem-collapse 480ms cubic-bezier(0.4,0,0.2,1) 200ms forwards!important;
}
.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-tile.is-center .m002-detail-tile-bg{
  animation:m002-detail-elem-empty 480ms cubic-bezier(0.4,0,0.2,1) 200ms forwards!important;
}

/* ============================================================================
   Unified detail-view tile architecture (v2.34 FLIP refactor).

   Every visible device in the Detail-View — the central element AND every
   uplink peer — is a single persistent .m002-detail-tile <g>. Slot
   membership (centre vs. peer-row) is a CSS-class + position decision; the
   underlying DOM node survives across hops. That lets us drive the
   Position-Swap motion via FLIP (First-Last-Invert-Play): the same DOM
   node literally translates from its old slot to its new one. No fade-out
   / respawn artefact in between.

   Layout transform is composed via CSS variables so JS can write layout
   and FLIP-delta independently:
     transform = translate(layout-x + flip-dx, layout-y + flip-dy)
                 * scale(layout-scale * flip-scale)
   --layout-* is the source-of-truth. --flip-* is set during the INVERT
   phase to make the tile visually appear at its OLD position, then cleared
   in a paired rAF to let the CSS transition glide it home (PLAY).
   ============================================================================ */
.m002-detail-tile{
  cursor:pointer;
  transform-origin:0 0;
  transform:
    translate(
      calc(var(--layout-x,0px) + var(--flip-dx,0px)),
      calc(var(--layout-y,0px) + var(--flip-dy,0px))
    )
    scale(calc(var(--layout-scale,1) * var(--flip-scale,1)));
  transition:transform 480ms cubic-bezier(0.4,0,0.2,1);
}
.m002-detail-tile.m002-no-transition{transition:none;}

/* Inner wrapper — carries the entry animation. transform-origin (0,0) is
   the tile's geometric centre because the rect is drawn from -w/2..w/2. */
.m002-detail-tile-inner{transform-origin:0 0;}

/* Initial state — only applies BEFORE the overlay's show class lands.
   Scoping to :not(.show) means tiles that survive a hop don't get yanked
   back to a "point" when the show state momentarily fluctuates; once
   shown, the animation + settled rules govern. */
.m002-detail-overlay:not(.m002-detail-overlay-show) .m002-detail-tile.is-center .m002-detail-tile-inner{
  transform:scale(0.005,0.02);
  opacity:0;
}
.m002-detail-overlay:not(.m002-detail-overlay-show) .m002-detail-tile.is-peer .m002-detail-tile-inner{
  transform:scale(0.6);
  opacity:0;
}

/* Entry choreography. Centre tile keeps the cinematic point→line→expand
   intro (m002-detail-elem-emerge keyframe — unchanged). Peers pop in
   ~680ms later in the same wave the legacy peer-tiles used.

   animation-fill-mode: BOTH — backwards fill is critical here. With just
   `forwards`, during the 700ms peer-tile delay the inner falls back to
   its default style (identity, fully visible) because the :not(.show)
   initial-state rule no longer matches once .show is on. Result: the
   peer-tiles render at full size for 700ms before snapping to the 0%
   keyframe and animating — visible as "instantly loaded" peers while
   the centre is still mid-emerge. `both` applies the 0% keyframe
   (scale 0.6 opacity 0) backwards through the delay, so peers stay
   invisible until their pop wave fires. Same fix on the centre's
   emerge — the 50ms delay was short enough to be imperceptible, but
   `both` is technically the correct mode for both entries. */
.m002-detail-overlay.m002-detail-overlay-show .m002-detail-tile.is-center .m002-detail-tile-inner{
  animation:m002-detail-elem-emerge 1000ms cubic-bezier(0.4,0,0.2,1) 50ms both;
}
.m002-detail-overlay.m002-detail-overlay-show .m002-detail-tile.is-center .m002-detail-tile-bg{
  animation:m002-detail-elem-fill 1000ms cubic-bezier(0.4,0,0.2,1) 50ms both;
}
.m002-detail-overlay.m002-detail-overlay-show .m002-detail-tile.is-peer .m002-detail-tile-inner{
  /* 700ms delay matches the uplink-port-row's PORT_BASE_DELAY in JS so
     peer-tiles + ports + peer-links arrive as one cohesive "upper row"
     wave AFTER the centre has emerged, instead of three separate phases. */
  animation:m002-detail-port-pop 280ms cubic-bezier(0.34,1.4,0.5,1) 700ms both;
}

/* Settled — pins inner state so live edits to the inspector don't replay
   the entry choreography. The OUTER tile transform stays driven by
   --layout-* + --flip-*, so settled is fully compatible with the FLIP
   hop (settled targets only the inner; the outer can still glide). */
.m002-detail-overlay-settled .m002-detail-tile-inner{
  animation:none!important;
  transform:none!important;
  opacity:1!important;
}
.m002-detail-overlay-settled .m002-detail-tile-bg{
  animation:none!important;
  fill:#0a0a10!important;
}

/* Hover + selection */
.m002-detail-tile .m002-detail-tile-bg{transition:filter .15s,stroke-width .15s,stroke .15s;}
.m002-detail-tile:hover .m002-detail-tile-bg{stroke-width:1.8;}
.m002-detail-tile.is-peer.is-selected .m002-detail-tile-bg{stroke-width:2;}
.m002-detail-tile.is-center.is-selected .m002-detail-tile-bg{
  stroke-width:2.4;
  filter:drop-shadow(0 0 4px var(--accent)) drop-shadow(0 0 14px var(--accent));
}

/* Tile name — slightly heavier than grid .m002-dev-name so it still reads
   at the peer (1×) scale; the centre's 1.875× makes it large again. */
.m002-detail-tile-name{
  font-family:'Inter','Rajdhani',sans-serif;
  font-size:18px;
  font-weight:600;
  fill:#f5f3ff;
  letter-spacing:.5px;
}

/* ============================================================================
   Endpoint tiles (v2.35) — downstream end-devices reachable via access
   ports. Same shape, size, and FLIP machinery as peer tiles, but positioned
   below the centre and visually distinguished by a small "P{n}" port badge
   sitting on the top edge (where the uplink wire enters from the centre).

   Pop-in cascade is offset 40ms after the peer wave so the eye reads top
   then bottom rather than both populations arriving simultaneously.
   ============================================================================ */
.m002-detail-overlay:not(.m002-detail-overlay-show) .m002-detail-tile.is-endpoint .m002-detail-tile-inner{
  transform:scale(0.6);
  opacity:0;
}
.m002-detail-overlay.m002-detail-overlay-show .m002-detail-tile.is-endpoint .m002-detail-tile-inner{
  /* `both` fill instead of `forwards` so the 0%-keyframe state (scale 0.6
     opacity 0 — invisible) applies during the 720ms delay instead of the
     default un-animated state (transform:none opacity:1 — visible). Without
     this, endpoint tiles created during a hop's sync (t=200ms) sat fully
     visible for 480ms before hop-in's animation kicked in, which the user
     read as "Ports erscheinen instant, keine Animation". `both` keeps the
     existing forwards behaviour at the end too. */
  animation:m002-detail-port-pop 280ms cubic-bezier(0.34,1.4,0.5,1) 720ms both;
}
.m002-detail-overlay-settled .m002-detail-tile.is-endpoint .m002-detail-tile-inner{
  animation:none!important;
  transform:none!important;
  opacity:1!important;
}
.m002-detail-tile.is-endpoint.is-selected .m002-detail-tile-bg{stroke-width:2;}
.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-tile.is-endpoint .m002-detail-tile-inner{
  animation:m002-detail-port-suck 280ms cubic-bezier(0.4,0,1,1) 100ms forwards!important;
  pointer-events:none;
}

/* Endpoint port-number badge — sits on the top edge of the tile where the
   uplink wire enters. Hidden by default (so the same tile DOM survives
   role flips into peer/centre slots without ghost text); shown only when
   the tile is in the endpoint role. */
.m002-detail-endpoint-badge{opacity:0;pointer-events:none;}
.m002-detail-tile.is-endpoint .m002-detail-endpoint-badge{opacity:1;}
.m002-detail-endpoint-badge rect{
  fill:#0a0a10;
  stroke:var(--accent);
  stroke-width:1;
}
.m002-detail-endpoint-badge text{
  font-family:'JetBrains Mono','Share Tech Mono',monospace;
  font-size:10px;
  letter-spacing:1px;
  fill:#cfd2d8;
  font-weight:600;
}

/* Endpoint link — wire from the centre's bottom edge to the endpoint
   tile's top. Reuses the peer-link visual class for stroke styling and
   inherits the entry fade; just needs the show-class entry hookup since
   it's tagged with the same .m002-detail-peer-link class as uplinks. The
   timing is the same wave (the show-rule already covers it). */

/* Overflow row — pagination control + free-port indicator below the
   endpoint row. Faded in slightly after the endpoint tiles land so the
   row reads as "tiles first, controls afterwards". */
.m002-detail-overlay .m002-detail-pagination,
.m002-detail-overlay .m002-detail-free-ports{opacity:0;}
.m002-detail-overlay.m002-detail-overlay-show .m002-detail-pagination,
.m002-detail-overlay.m002-detail-overlay-show .m002-detail-free-ports{
  animation:m002-detail-stub-fade 320ms ease-out forwards;
  animation-delay:880ms;
}
.m002-detail-overlay-settled .m002-detail-pagination,
.m002-detail-overlay-settled .m002-detail-free-ports{
  animation:none!important;
  opacity:1!important;
}
.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-pagination,
.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-free-ports{
  animation:m002-detail-stub-fade-out 200ms cubic-bezier(0.4,0,1,1) forwards!important;
  pointer-events:none;
}

.m002-detail-pag-btn{cursor:pointer;}
.m002-detail-pag-btn .m002-detail-pag-hit{
  fill:transparent;
  stroke:#1a1a22;
  stroke-width:1;
  transition:fill .15s,stroke .15s;
}
.m002-detail-pag-btn:hover .m002-detail-pag-hit{
  fill:rgba(255,0,60,0.06);
  stroke:#ff003c;
}
.m002-detail-pag-btn.is-disabled{cursor:default;opacity:0.35;}
.m002-detail-pag-btn.is-disabled:hover .m002-detail-pag-hit{
  fill:transparent;
  stroke:#1a1a22;
}
.m002-detail-pag-glyph{
  font-family:'JetBrains Mono','Share Tech Mono',monospace;
  font-size:14px;
  fill:#cfd2d8;
  font-weight:600;
  pointer-events:none;
}
.m002-detail-pag-btn:hover .m002-detail-pag-glyph{fill:#ff003c;}
.m002-detail-pag-indicator{
  font-family:'JetBrains Mono','Share Tech Mono',monospace;
  font-size:11px;
  letter-spacing:1.6px;
  fill:#9aa0a8;
  font-weight:600;
}
.m002-detail-free-ports{
  font-family:'JetBrains Mono','Share Tech Mono',monospace;
  font-size:10px;
  letter-spacing:1.8px;
  fill:#5a5f6e;
  font-weight:600;
}

@media (prefers-reduced-motion: reduce){
  .m002-detail-overlay,.m002-detail-overlay .m002-detail-head,.m002-detail-overlay.m002-detail-overlay-show,.m002-detail-overlay.m002-detail-overlay-show .m002-detail-head{transition:none!important;}
  .m002-detail-overlay.m002-detail-overlay-show .m002-detail-port-inner,.m002-detail-overlay.m002-detail-overlay-show .m002-detail-stub,.m002-detail-overlay.m002-detail-overlay-show .m002-detail-peer-link,.m002-detail-overlay.m002-detail-overlay-show .m002-detail-stack-cable,.m002-detail-overlay.m002-detail-overlay-show .m002-detail-tile-inner,.m002-detail-overlay.m002-detail-overlay-show .m002-detail-pagination,.m002-detail-overlay.m002-detail-overlay-show .m002-detail-free-ports,.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-tile-inner,.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-stub,.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-peer-link,.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-stack-cable,.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-port-inner,.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-pagination,.m002-detail-overlay.m002-detail-overlay-leaving .m002-detail-free-ports{animation:none!important;}
  .m002-detail-port-inner,.m002-detail-stub,.m002-detail-head,.m002-detail-peer-link,.m002-detail-stack-cable,.m002-detail-tile-inner,.m002-detail-pagination,.m002-detail-free-ports{transform:none!important;opacity:1!important;}
  .m002-detail-tile{transition:none!important;}
  .m002-detail-overlay{backdrop-filter:none!important;-webkit-backdrop-filter:none!important;}
}

/* Tool-aware cursor recolour. The N.IVEN custom cursor (red brackets +
   red dot) keeps its shape but shifts hue to match the active MOD_002
   tool. Body classes set in refreshToolHighlights drive this.
   :not(.m002-style-sketch) — sketch theme paints its own pencil tones; we
   exclude these futuristic-only neon recolours when sketch is active so the
   brackets/dot don't get forced back to neon-white/neon-green. */
body.m002-tool-select:not(.m002-style-sketch) .cur-bracket{border-color:#ffffff !important;}
body.m002-tool-select:not(.m002-style-sketch) .cursor-dot{background:#ffffff !important;box-shadow:0 0 10px #ffffff,0 0 3px #fff !important;}
body.m002-tool-link:not(.m002-style-sketch) .cur-bracket{border-color:#35ff7a !important;}
body.m002-tool-link:not(.m002-style-sketch) .cursor-dot{background:#35ff7a !important;box-shadow:0 0 10px #35ff7a,0 0 3px #fff !important;}
/* Delete tool: keeps the red brackets but swaps the centre dot for an X. */
body.m002-tool-delete .cursor-dot{display:none;}
body.m002-tool-delete:not(.m002-style-sketch) .cursor::before,
body.m002-tool-delete:not(.m002-style-sketch) .cursor::after{content:'';position:absolute;left:50%;top:50%;width:14px;height:1.5px;background:#ff003c;box-shadow:0 0 6px #ff003c;}
body.m002-tool-delete .cursor::before{transform:translate(-50%,-50%) rotate(45deg);}
body.m002-tool-delete .cursor::after{transform:translate(-50%,-50%) rotate(-45deg);}
/* Sketch + delete: pencil-red X without neon glow (still needs the content/positioning above) */
body.m002-tool-delete.m002-style-sketch .cursor::before,
body.m002-tool-delete.m002-style-sketch .cursor::after{content:'';position:absolute;left:50%;top:50%;width:14px;height:1.5px;background:#c44b3c;box-shadow:none;}
/* Sketch cursor tones — applied via the body.m002-style-sketch class.
   ROOT CAUSE NOTE: the global `.cursor` rule sets `mix-blend-mode:plus-lighter`
   (an "additive" blend) which works great over the dark canvas but eats black
   on a cream background — adding 0 to ~250 leaves the background untouched, so
   black brackets render invisible. Switching the blend mode back to `normal`
   for sketch is the actual fix; the rest of the cascade just sets --red so
   the existing border-based brackets pick up the right pencil tone per tool. */
body.m002-style-sketch{--red:#000000;--red-glow:#000000;}
body.m002-style-sketch.m002-tool-link{--red:#3a5a3a;--red-glow:#3a5a3a;}
body.m002-style-sketch.m002-tool-delete{--red:#8a1f12;--red-glow:#8a1f12;}
body.m002-style-sketch .cursor{mix-blend-mode:normal !important;}
/* Bump bracket strokes to 1.5px so the L-shapes read crisply on HiDPI screens
   without antialiasing them out to grey. */
body.m002-style-sketch .br-tl{border-top-width:1.5px !important;border-left-width:1.5px !important;}
body.m002-style-sketch .br-tr{border-top-width:1.5px !important;border-right-width:1.5px !important;}
body.m002-style-sketch .br-bl{border-bottom-width:1.5px !important;border-left-width:1.5px !important;}
body.m002-style-sketch .br-br{border-bottom-width:1.5px !important;border-right-width:1.5px !important;}
body.m002-style-sketch .cursor-dot{
  background:var(--red) !important;
  box-shadow:none !important;
  mix-blend-mode:normal !important;
}
/* Hover state in MOD_002: instead of growing (the global default), the
   cursor stands on its tip — the whole bracket frame pivots 45° around
   the cursor centre, brackets travel as one unit. */
body.m002-tool-select .cursor.active,
body.m002-tool-link .cursor.active,
body.m002-tool-delete .cursor.active{width:30px;height:30px;}
body.m002-tool-select .cursor.active .cur-bracket,
body.m002-tool-link .cursor.active .cur-bracket,
body.m002-tool-delete .cursor.active .cur-bracket{width:7px;height:7px;transform:none;}
.m002-cursor-frame{position:absolute;inset:0;transition:transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1),width 0.25s,height 0.25s;transform-origin:50% 50%;}
body.m002-tool-select .cursor.active .m002-cursor-frame,
body.m002-tool-link .cursor.active .m002-cursor-frame,
body.m002-tool-delete .cursor.active .m002-cursor-frame{transform:rotate(45deg);}
/* Pressed-while-hovering: shrink the rotated cursor like the unrotated one
   does on a background grab. Higher specificity than the bare .active rule
   above so it wins; no !important needed. */
body.m002-tool-select .cursor.active.down,
body.m002-tool-link .cursor.active.down,
body.m002-tool-delete .cursor.active.down{width:22px;height:22px;}
body.m002-tool-select .cursor.active.down .cur-bracket,
body.m002-tool-link .cursor.active.down .cur-bracket,
body.m002-tool-delete .cursor.active.down .cur-bracket{width:5px;height:5px;}
body.m002-tool-select .cursor.active.down .m002-cursor-frame,
body.m002-tool-link .cursor.active.down .m002-cursor-frame,
body.m002-tool-delete .cursor.active.down .m002-cursor-frame{transform:rotate(45deg) scale(0.8);}

/* Radial action menu — opens on background right-click. Centered on the
   click point. The first open plays a draw-in sequence: centre dot →
   vertical lines → semicircle arcs (top-pole sweeps down, bottom-pole
   sweeps up) → final UI fades over the construct lines. */
.m002-radial{position:absolute;width:260px;height:260px;transform:translate(-50%,-50%);transform-origin:center;opacity:0;pointer-events:auto;z-index:60;transition:opacity 80ms ease-out;filter:drop-shadow(0 8px 22px rgba(0,0,0,0.55));}
.m002-radial.m002-radial-in{opacity:1;}
.m002-radial.m002-radial-out{opacity:0;transform:translate(-50%,-50%) scale(0.92);transition:transform 140ms ease-in,opacity 140ms ease-in;}
.m002-radial.m002-radial-swap .m002-rad-svg{opacity:0;transform:scale(0.92);}
.m002-rad-svg{display:block;width:100%;height:100%;overflow:visible;transition:opacity 110ms ease-out,transform 110ms ease-out;transform-origin:center;}
.m002-rad-bg{fill:rgba(8,8,14,0.72);stroke:rgba(255,0,60,0.18);stroke-width:1;}
.m002-rad-core{fill:rgba(10,10,16,0.96);stroke:#1a1a22;stroke-width:1;}
.m002-rad-core-label{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;letter-spacing:1.6px;fill:#5a5f6e;pointer-events:none;text-transform:uppercase;}
.m002-rad-seg{cursor:pointer;}
.m002-rad-seg-path{fill:rgba(14,14,22,0.92);stroke:#2a2a36;stroke-width:1;transition:fill 120ms ease-out,stroke 120ms ease-out;}
.m002-rad-seg:hover .m002-rad-seg-path{fill:rgba(255,0,60,0.14);stroke:#ff003c;}
.m002-rad-seg-glyph{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:22px;fill:#e8e8ee;pointer-events:none;letter-spacing:0;}
.m002-rad-seg-label{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:10px;letter-spacing:1.8px;fill:#cfd2d8;pointer-events:none;text-transform:uppercase;}
.m002-rad-seg-hint{font-family:'JetBrains Mono','Share Tech Mono',monospace;font-size:8px;letter-spacing:1.4px;fill:#5a5f6e;pointer-events:none;text-transform:uppercase;}
.m002-rad-seg:hover .m002-rad-seg-glyph,
.m002-rad-seg:hover .m002-rad-seg-label{fill:#ff003c;}
.m002-rad-seg:hover .m002-rad-seg-hint{fill:#9aa0a8;}
/* Device picker: per-segment accent colour drives hover & dot. */
.m002-rad-seg-dev .m002-rad-seg-dot{fill:var(--accent);filter:drop-shadow(0 0 4px var(--accent));}
.m002-rad-seg-dev:hover .m002-rad-seg-path{fill:color-mix(in srgb,var(--accent) 14%,rgba(14,14,22,0.92));stroke:var(--accent);}
.m002-rad-seg-dev:hover .m002-rad-seg-label{fill:var(--accent);}
.m002-rad-seg-back{cursor:pointer;}
.m002-rad-seg-back:hover .m002-rad-core{stroke:#ff003c;fill:rgba(255,0,60,0.08);}
.m002-rad-seg-back:hover .m002-rad-core-label{fill:#ff003c;}
/* CANCEL centre tile (primary): same hover treatment as the back tile. */
.m002-rad-seg-cancel:hover .m002-rad-core{stroke:#ff003c;fill:rgba(255,0,60,0.08);}
.m002-rad-seg-cancel:hover .m002-rad-core-label{fill:#ff003c;}
/* Zone picker — active zone reads as already-occupied; hover still pops red. */
.m002-rad-seg-zone-active .m002-rad-seg-path{fill:rgba(255,0,60,0.10);stroke:rgba(255,0,60,0.55);}
.m002-rad-seg-zone-active .m002-rad-seg-label{fill:#ff003c;}

/* === Draw-in animation overlay (primary level only) ===
   The construct primitives — dot, vertical lines, two semicircle arcs — are
   drawn in sequence, then the real UI fades over the top while the overlay
   itself fades out. */
.m002-rad-dot{fill:#e8e8ee;transform-box:fill-box;transform-origin:center;transform:scale(0);}
.m002-rad-vline{stroke:#e8e8ee;stroke-width:1.5;stroke-linecap:round;stroke-dasharray:130;stroke-dashoffset:130;}
.m002-rad-arc{stroke:#e8e8ee;stroke-width:1.5;stroke-linecap:round;stroke-dasharray:406.84;stroke-dashoffset:406.84;}
/* Build-in animation gates on [data-fresh] + an entry-ring data-level (primary,
   element, stack, link, pen). [data-fresh] is set on the very first open AND
   preserved across DRAW → pen swaps so the pen ring replays the sequence after
   that gesture. Submenu navigation (NEW/MOVE/TOOL/PEN-COLOR/...) shifts to a
   non-entry level so the rule no longer matches; back-actions also strip
   data-fresh as a belt-and-braces guard. Durations are ~30% tighter than the
   original draft for a snappier feel. */
.m002-radial.m002-radial-in:where([data-level="primary"],[data-level="element"],[data-level="stack"],[data-level="link"],[data-level="pen"])[data-fresh] .m002-rad-dot{animation:m002-rad-dot-in 100ms ease-out forwards;}
.m002-radial.m002-radial-in:where([data-level="primary"],[data-level="element"],[data-level="stack"],[data-level="link"],[data-level="pen"])[data-fresh] .m002-rad-vline{animation:m002-rad-line-draw 125ms cubic-bezier(.55,.05,.35,1) 75ms forwards;}
.m002-radial.m002-radial-in:where([data-level="primary"],[data-level="element"],[data-level="stack"],[data-level="link"],[data-level="pen"])[data-fresh] .m002-rad-arc{animation:m002-rad-arc-draw 225ms cubic-bezier(.5,.05,.35,1) 175ms forwards;}
.m002-radial.m002-radial-in:where([data-level="primary"],[data-level="element"],[data-level="stack"],[data-level="link"],[data-level="pen"])[data-fresh] .m002-rad-anim{animation:m002-rad-fade-out 170ms ease-out 420ms forwards;}
@keyframes m002-rad-dot-in{0%{transform:scale(0);opacity:0;}40%{opacity:1;}100%{transform:scale(1);opacity:1;}}
@keyframes m002-rad-line-draw{to{stroke-dashoffset:0;}}
@keyframes m002-rad-arc-draw{to{stroke-dashoffset:0;}}
@keyframes m002-rad-fade-out{to{opacity:0;}}

/* Final UI is invisible until the construct lines have done their work. */
.m002-radial.m002-radial-in:where([data-level="primary"],[data-level="element"],[data-level="stack"],[data-level="link"],[data-level="pen"])[data-fresh] .m002-rad-bg,
.m002-radial.m002-radial-in:where([data-level="primary"],[data-level="element"],[data-level="stack"],[data-level="link"],[data-level="pen"])[data-fresh] .m002-rad-core,
.m002-radial.m002-radial-in:where([data-level="primary"],[data-level="element"],[data-level="stack"],[data-level="link"],[data-level="pen"])[data-fresh] .m002-rad-core-label,
.m002-radial.m002-radial-in:where([data-level="primary"],[data-level="element"],[data-level="stack"],[data-level="link"],[data-level="pen"])[data-fresh] .m002-rad-seg-path,
.m002-radial.m002-radial-in:where([data-level="primary"],[data-level="element"],[data-level="stack"],[data-level="link"],[data-level="pen"])[data-fresh] .m002-rad-seg-glyph,
.m002-radial.m002-radial-in:where([data-level="primary"],[data-level="element"],[data-level="stack"],[data-level="link"],[data-level="pen"])[data-fresh] .m002-rad-seg-label{opacity:0;animation:m002-rad-fade-final 155ms ease-out forwards;}
.m002-radial.m002-radial-in:where([data-level="primary"],[data-level="element"],[data-level="stack"],[data-level="link"],[data-level="pen"])[data-fresh] .m002-rad-bg        {animation-delay:380ms;}
.m002-radial.m002-radial-in:where([data-level="primary"],[data-level="element"],[data-level="stack"],[data-level="link"],[data-level="pen"])[data-fresh] .m002-rad-seg-path  {animation-delay:405ms;}
.m002-radial.m002-radial-in:where([data-level="primary"],[data-level="element"],[data-level="stack"],[data-level="link"],[data-level="pen"])[data-fresh] .m002-rad-core      {animation-delay:435ms;}
.m002-radial.m002-radial-in:where([data-level="primary"],[data-level="element"],[data-level="stack"],[data-level="link"],[data-level="pen"])[data-fresh] .m002-rad-seg-glyph,
.m002-radial.m002-radial-in:where([data-level="primary"],[data-level="element"],[data-level="stack"],[data-level="link"],[data-level="pen"])[data-fresh] .m002-rad-seg-label,
.m002-radial.m002-radial-in:where([data-level="primary"],[data-level="element"],[data-level="stack"],[data-level="link"],[data-level="pen"])[data-fresh] .m002-rad-core-label{animation-delay:475ms;}
@keyframes m002-rad-fade-final{from{opacity:0;}to{opacity:1;}}

/* =============================================================================
   SKETCH OVERRIDES (FINAL) — canvas elements only. Cursor overrides live in the
   single block above; the cascade picks up --red per tool from there.
   ============================================================================= */

/* --- Stack-member separators: the thin neon outline disappears on cream paper
   so members blend together. Bump stroke + use graphite + tighten dasharray
   subtly so the divider reads as a clear pencil line. */
.m002-host[data-grid-style="sketch"] .m002-device.m002-stack-member .m002-dev-bg{stroke:#3a3a35 !important;stroke-width:1.6 !important;}
.m002-host[data-grid-style="sketch"] .m002-device.m002-stack-member:hover .m002-dev-bg{stroke:#2a2a30 !important;stroke-width:1.8 !important;filter:none !important;}
.m002-host[data-grid-style="sketch"] .m002-device.m002-stack-member.m002-selected .m002-dev-bg{stroke:#c44b3c !important;stroke-width:2 !important;stroke-dasharray:4 3;filter:none !important;}
/* Type tag inside member tiles — graphite muted (was washed-out lavender) */
.m002-host[data-grid-style="sketch"] .m002-device.m002-stack-member .m002-dev-type{fill:#7a7568 !important;opacity:1 !important;}

/* --- Link / port labels: the original 3px black stroke used as a halo
   against dark canvas reads as a fat black outline against cream paper,
   making numbers look cartoonishly bold. Match the stroke to the paper so
   the halo disappears and glyphs read as plain Share Tech Mono. Apply to
   every label class that lives on the canvas: regular link "Nx" port count,
   VLAN count badge, LAG/stack badges, stack-envelope title. */
.m002-host[data-grid-style="sketch"] .m002-link-label,
.m002-host[data-grid-style="sketch"] .m002-link-vlan-count,
.m002-host[data-grid-style="sketch"] .m002-link-bundle-label,
.m002-host[data-grid-style="sketch"] .m002-stack-cable-label,
.m002-host[data-grid-style="sketch"] .m002-stack-env-label{stroke:#faf6ed !important;stroke-width:2px !important;fill:#3a3a35 !important;font-family:'JetBrains Mono','Share Tech Mono',monospace !important;paint-order:stroke fill !important;}
.m002-host[data-grid-style="sketch"] .m002-link:hover .m002-link-vlan-count,
.m002-host[data-grid-style="sketch"] .m002-link:hover .m002-link-bundle-label,
.m002-host[data-grid-style="sketch"] .m002-stacklink:hover .m002-stack-cable-label{fill:#2a2a30 !important;}
.m002-host[data-grid-style="sketch"] .m002-link.m002-selected .m002-link-label,
.m002-host[data-grid-style="sketch"] .m002-link.m002-selected .m002-link-vlan-count,
.m002-host[data-grid-style="sketch"] .m002-link.m002-selected .m002-link-bundle-label{fill:#c44b3c !important;}
.m002-host[data-grid-style="sketch"] .m002-link:hover .m002-link-label{filter:none !important;}
.m002-host[data-grid-style="sketch"] .m002-link.m002-selected .m002-link-label.m002-link-stripe-label{filter:none !important;}
/* Stack envelope title (// STACK · STACK-XX · ×N) — opacity bump only; the
   stroke + fill are unified with the other canvas labels above so they all
   read as crisp graphite Share Tech Mono with a cream paper halo. */
.m002-host[data-grid-style="sketch"] .m002-stack-env-label{opacity:1 !important;}
.m002-host[data-grid-style="sketch"] .m002-stack-envelope.m002-selected .m002-stack-env-label{fill:#c44b3c !important;}
/* Stack badge (×N count on collapsed stacks) — graphite. */
.m002-host[data-grid-style="sketch"] .m002-stack-badge{fill:#3a3a35 !important;}
/* --- Link line selection: white-on-white doesn't read on cream paper. Use
   pencil red for selected state and remove the halo glow filters. */
.m002-host[data-grid-style="sketch"] .m002-link-line{filter:none !important;}
.m002-host[data-grid-style="sketch"] .m002-link:hover .m002-link-line{filter:none !important;stroke-width:1.8;}
.m002-host[data-grid-style="sketch"] .m002-link.m002-selected .m002-link-line{stroke:#c44b3c !important;filter:none !important;}
.m002-host[data-grid-style="sketch"] .m002-link.m002-selected .m002-link-line.m002-link-stripe{filter:none !important;}
.m002-host[data-grid-style="sketch"] .m002-link.m002-link-bundle:hover .m002-lag-line{stroke:#2a2a30 !important;}
.m002-host[data-grid-style="sketch"] .m002-link.m002-selected .m002-lag-line{stroke:#c44b3c !important;filter:none !important;}

/* --- Radial action menu: re-skin the dark-glass / neon-red default into the
   ink-on-paper palette. Cream paper bg, graphite ink, pencil-red intent on
   hover. Keep the device-picker --accent tinting (it's already per-type) but
   soften it onto the cream substrate. */
.m002-host[data-grid-style="sketch"] .m002-radial{filter:drop-shadow(0 4px 14px rgba(0,0,0,0.18)) !important;}
/* The bg circle's stroke is dropped — the segment-path strokes themselves
   form the outline ring at r=130 (each donut-wedge's outer arc stroke butts
   against its neighbour's, drawing one continuous circle). A separate bg
   stroke either floated outside (gap moat between outline and segments) or
   sat inside (covered by the segments painted on top), so let the segments
   BE the silhouette and bump THEIR stroke to graphite 1.2px. */
.m002-host[data-grid-style="sketch"] .m002-rad-bg{fill:rgba(254,252,246,0.96) !important;stroke:none !important;}
.m002-host[data-grid-style="sketch"] .m002-rad-core{fill:#fefcf6 !important;stroke:#3a3a35 !important;stroke-width:1.2 !important;}
.m002-host[data-grid-style="sketch"] .m002-rad-core-label{fill:#3a3a35 !important;font-family:'JetBrains Mono','Share Tech Mono',monospace !important;}
.m002-host[data-grid-style="sketch"] .m002-rad-seg-path{fill:rgba(254,252,246,0.92) !important;stroke:#3a3a35 !important;stroke-width:1.2 !important;}
.m002-host[data-grid-style="sketch"] .m002-rad-seg:hover .m002-rad-seg-path{fill:rgba(196,75,60,0.10) !important;stroke:#c44b3c !important;}
.m002-host[data-grid-style="sketch"] .m002-rad-seg-glyph{fill:#3a3a35 !important;font-family:'JetBrains Mono','Share Tech Mono',monospace !important;}
.m002-host[data-grid-style="sketch"] .m002-rad-seg-label{fill:#3a3a35 !important;font-family:'JetBrains Mono','Share Tech Mono',monospace !important;}
.m002-host[data-grid-style="sketch"] .m002-rad-seg-hint{fill:#7a7568 !important;font-family:'JetBrains Mono','Share Tech Mono',monospace !important;}
.m002-host[data-grid-style="sketch"] .m002-rad-seg:hover .m002-rad-seg-glyph,
.m002-host[data-grid-style="sketch"] .m002-rad-seg:hover .m002-rad-seg-label{fill:#c44b3c !important;}
.m002-host[data-grid-style="sketch"] .m002-rad-seg:hover .m002-rad-seg-hint{fill:#3a3a35 !important;}
/* Device-picker segments — keep per-type --accent for the dot + label hover,
   but desaturate the hover bg so it sits on cream (was rgba(14,14,22,0.92)
   blended). Drop the dot drop-shadow glow — no neon on paper. */
.m002-host[data-grid-style="sketch"] .m002-rad-seg-dev .m002-rad-seg-dot{filter:none !important;}
.m002-host[data-grid-style="sketch"] .m002-rad-seg-dev:hover .m002-rad-seg-path{fill:color-mix(in srgb,var(--accent) 12%,#fefcf6) !important;stroke:var(--accent) !important;}
.m002-host[data-grid-style="sketch"] .m002-rad-seg-dev:hover .m002-rad-seg-label{fill:var(--accent) !important;}
/* Center back / cancel tile — pencil-red on hover instead of neon */
.m002-host[data-grid-style="sketch"] .m002-rad-seg-back:hover .m002-rad-core,
.m002-host[data-grid-style="sketch"] .m002-rad-seg-cancel:hover .m002-rad-core{stroke:#c44b3c !important;fill:rgba(196,75,60,0.06) !important;}
.m002-host[data-grid-style="sketch"] .m002-rad-seg-back:hover .m002-rad-core-label,
.m002-host[data-grid-style="sketch"] .m002-rad-seg-cancel:hover .m002-rad-core-label{fill:#c44b3c !important;}
/* Active-zone tile (already-current zone in zone picker) — pencil-red hint */
.m002-host[data-grid-style="sketch"] .m002-rad-seg-zone-active .m002-rad-seg-path{fill:rgba(196,75,60,0.08) !important;stroke:rgba(196,75,60,0.55) !important;}
.m002-host[data-grid-style="sketch"] .m002-rad-seg-zone-active .m002-rad-seg-label{fill:#c44b3c !important;}
/* Build-in animation primitives (centre dot, vlines, arcs) — graphite */
.m002-host[data-grid-style="sketch"] .m002-rad-dot{fill:#3a3a35 !important;}
.m002-host[data-grid-style="sketch"] .m002-rad-vline,
.m002-host[data-grid-style="sketch"] .m002-rad-arc{stroke:#3a3a35 !important;}

/* --- Subnet legend (Routing layer) --- cream rows, graphite ink, no glow.
   Replaces the dark glass + neon-CIDR look that clashed with the cream panel. */
.m002-host[data-grid-style="sketch"] .m002-subnet-row{background:#fefcf6 !important;border-color:#c8c2b0 !important;}
.m002-host[data-grid-style="sketch"] .m002-subnet-row:hover{border-color:#2a2a30 !important;}
.m002-host[data-grid-style="sketch"] .m002-subnet-row.is-solo{background:rgba(196,75,60,0.06) !important;border-color:#c44b3c !important;box-shadow:none !important;}
.m002-host[data-grid-style="sketch"] .m002-subnet-row.is-solo .m002-subnet-row-dot{box-shadow:none !important;}
.m002-host[data-grid-style="sketch"] .m002-subnet-row-dot{box-shadow:none !important;}
.m002-host[data-grid-style="sketch"] .m002-subnet-row-cidr{color:#3a3a35 !important;}
.m002-host[data-grid-style="sketch"] .m002-subnet-row-name{color:#3a3a35 !important;}
.m002-host[data-grid-style="sketch"] .m002-subnet-row-name:focus{background:rgba(196,75,60,0.06) !important;}
.m002-host[data-grid-style="sketch"] .m002-subnet-row-rm{color:#7a7568 !important;}
.m002-host[data-grid-style="sketch"] .m002-subnet-row-rm:hover{color:#c44b3c !important;}
.m002-host[data-grid-style="sketch"] .m002-subnet-legend-empty{color:#7a7568 !important;}
.m002-host[data-grid-style="sketch"] .m002-subnet-legend-input{background:#fefcf6 !important;border-color:#c8c2b0 !important;color:#2a2a30 !important;}
.m002-host[data-grid-style="sketch"] .m002-subnet-legend-input:focus{border-color:#c44b3c !important;}
.m002-host[data-grid-style="sketch"] .m002-subnet-legend-add-btn{background:#fefcf6 !important;border-color:#c44b3c !important;color:#c44b3c !important;}
.m002-host[data-grid-style="sketch"] .m002-subnet-legend-add-btn:hover{background:rgba(196,75,60,0.08) !important;}
.m002-host[data-grid-style="sketch"] .m002-subnet-legend-auto{border-color:#c8c2b0 !important;color:#7a7568 !important;}
.m002-host[data-grid-style="sketch"] .m002-subnet-legend-auto:hover{border-color:#c44b3c !important;color:#c44b3c !important;}
/* VLAN-legend "SOLO · N" filter banner — cream tint instead of neon-red field. */
.m002-host[data-grid-style="sketch"] .m002-vlan-legend-filter{background:rgba(196,75,60,0.06) !important;border-color:#c44b3c !important;}
.m002-host[data-grid-style="sketch"] .m002-vlan-legend-filter-label{color:#c44b3c !important;}
.m002-host[data-grid-style="sketch"] .m002-vlan-legend-clear{background:#fefcf6 !important;border-color:#c44b3c !important;color:#c44b3c !important;}
.m002-host[data-grid-style="sketch"] .m002-vlan-legend-clear:hover{background:rgba(196,75,60,0.12) !important;}
.m002-host[data-grid-style="sketch"] .m002-vlan-legend-empty{color:#7a7568 !important;}
.m002-host[data-grid-style="sketch"] .m002-vlan-row-id{color:#3a3a35 !important;}
.m002-host[data-grid-style="sketch"] .m002-vlan-row-name{color:#3a3a35 !important;background:transparent !important;}

/* --- Inspector internals (right panel) --- cream rows, graphite text. */
/* DETAILS collapsible header */
.m002-host[data-grid-style="sketch"] .m002-insp-details{background:rgba(254,252,246,0.6) !important;border-color:#c8c2b0 !important;}
.m002-host[data-grid-style="sketch"] .m002-insp-details>summary{color:#7a7568 !important;}
.m002-host[data-grid-style="sketch"] .m002-insp-details>summary::before{color:#7a7568 !important;}
.m002-host[data-grid-style="sketch"] .m002-insp-details[open]>summary{color:#c44b3c !important;border-bottom-color:#c8c2b0 !important;}
.m002-host[data-grid-style="sketch"] .m002-insp-details[open]>summary::before{color:#c44b3c !important;}
.m002-host[data-grid-style="sketch"] .m002-insp-details>summary:hover{color:#c44b3c !important;}
/* L3 collapsible header — sage green for sketch (matches link-tool tone) */
.m002-host[data-grid-style="sketch"] .m002-insp-l3{border-top-color:#c8c2b0 !important;}
.m002-host[data-grid-style="sketch"] .m002-insp-l3>summary{color:#7a7568 !important;}
.m002-host[data-grid-style="sketch"] .m002-insp-l3>summary::before{color:#7a7568 !important;}
.m002-host[data-grid-style="sketch"] .m002-insp-l3[open]>summary::before{color:#6b8a6b !important;}
/* L3 section heads (INTERFACES / ROUTES) */
.m002-host[data-grid-style="sketch"] .m002-l3-head{color:#3a3a35 !important;}
.m002-host[data-grid-style="sketch"] .m002-iface-head,
.m002-host[data-grid-style="sketch"] .m002-route-head{color:#7a7568 !important;}
/* Iface rows */
.m002-host[data-grid-style="sketch"] .m002-iface-row{background:#fefcf6 !important;border-color:#c8c2b0 !important;}
.m002-host[data-grid-style="sketch"] .m002-iface-row:hover{border-color:#3a3a35 !important;}
.m002-host[data-grid-style="sketch"] .m002-iface-dot{box-shadow:none !important;}
.m002-host[data-grid-style="sketch"] .m002-iface-row input,
.m002-host[data-grid-style="sketch"] .m002-iface-row select,
.m002-host[data-grid-style="sketch"] .m002-route-row input,
.m002-host[data-grid-style="sketch"] .m002-route-row select{background:#fefcf6 !important;border-color:#c8c2b0 !important;color:#3a3a35 !important;}
.m002-host[data-grid-style="sketch"] .m002-iface-row input:focus,
.m002-host[data-grid-style="sketch"] .m002-iface-row select:focus,
.m002-host[data-grid-style="sketch"] .m002-route-row input:focus,
.m002-host[data-grid-style="sketch"] .m002-route-row select:focus{border-color:#6b8a6b !important;}
.m002-host[data-grid-style="sketch"] .m002-iface-rm,
.m002-host[data-grid-style="sketch"] .m002-route-rm{color:#7a7568 !important;}
.m002-host[data-grid-style="sketch"] .m002-iface-rm:hover,
.m002-host[data-grid-style="sketch"] .m002-route-rm:hover{color:#c44b3c !important;}
/* Route rows */
.m002-host[data-grid-style="sketch"] .m002-route-row{background:#fefcf6 !important;border-color:#c8c2b0 !important;}
/* Default route highlight: was amber (#ffae00) — use pencil-red for sketch */
.m002-host[data-grid-style="sketch"] .m002-route-row.is-default{border-color:#c44b3c !important;background:rgba(196,75,60,0.05) !important;}
.m002-host[data-grid-style="sketch"] .m002-route-row.is-default .m002-route-dst{color:#c44b3c !important;}
/* PORTS preset / +/- pcount buttons — cyan/green/red → graphite/sage/pencil-red */
.m002-host[data-grid-style="sketch"] .m002-pcount-btn.preset{border-color:#3a3a35 !important;color:#3a3a35 !important;background:#fefcf6 !important;}
.m002-host[data-grid-style="sketch"] .m002-pcount-btn.preset:hover{background:rgba(58,58,53,0.08) !important;}
.m002-host[data-grid-style="sketch"] .m002-pcount-btn.step.plus{border-color:#6b8a6b !important;color:#6b8a6b !important;background:#fefcf6 !important;}
.m002-host[data-grid-style="sketch"] .m002-pcount-btn.step.plus:hover{background:rgba(107,138,107,0.10) !important;}
.m002-host[data-grid-style="sketch"] .m002-pcount-btn.step.minus{border-color:#c44b3c !important;color:#c44b3c !important;background:#fefcf6 !important;}
.m002-host[data-grid-style="sketch"] .m002-pcount-btn.step.minus:hover{background:rgba(196,75,60,0.10) !important;}
/* PORT TABLE rows — cream paper hover, graphite outline, cream input bg */
.m002-host[data-grid-style="sketch"] .m002-port-row:hover{background:rgba(196,75,60,0.06) !important;border-color:#c44b3c !important;}
.m002-host[data-grid-style="sketch"] .m002-port-row input{background:#fefcf6 !important;border-color:#c8c2b0 !important;color:#3a3a35 !important;}
.m002-host[data-grid-style="sketch"] .m002-port-row input:focus{border-color:#c44b3c !important;}
/* Auto-prefix hint row — neutral cream/tan/graphite, no red on cream paper */
.m002-host[data-grid-style="sketch"] .m002-ports-prefix{background:transparent !important;border-color:#c8c2b0 !important;}
.m002-host[data-grid-style="sketch"] .m002-ports-prefix-label{color:#7a7568 !important;}
.m002-host[data-grid-style="sketch"] .m002-ports-prefix-val{background:transparent !important;border-color:#c8c2b0 !important;color:#3a3a35 !important;}
.m002-host[data-grid-style="sketch"] .m002-ports-prefix-clear{border-color:#c8c2b0 !important;color:#7a7568 !important;}
.m002-host[data-grid-style="sketch"] .m002-ports-prefix-clear:hover{border-color:#c44b3c !important;color:#c44b3c !important;}
/* Duplicate-name pulse — pencil-red on cream, drop the bright glow */
.m002-host[data-grid-style="sketch"] .m002-port-row.is-duplicate{background:rgba(196,75,60,0.08) !important;border-color:#c44b3c !important;animation:none !important;}
.m002-host[data-grid-style="sketch"] .m002-port-row.is-duplicate input{border-color:#c44b3c !important;color:#c44b3c !important;}
.m002-host[data-grid-style="sketch"] .m002-port-row.is-duplicate .m002-port-num{color:#c44b3c !important;}
.m002-host[data-grid-style="sketch"] .m002-pmodal-name.is-duplicate{border-color:#c44b3c !important;color:#c44b3c !important;animation:none !important;box-shadow:none !important;}
/* VLAN chips wrapper (the dark frame around VLAN pills in port modal) */
.m002-host[data-grid-style="sketch"] .m002-vlan-chips{background:rgba(254,252,246,0.6) !important;border-color:#c8c2b0 !important;}
.m002-host[data-grid-style="sketch"] .m002-vlan-empty{color:#7a7568 !important;}
/* DELETE NODE / DELETE LINK / UNGROUP STACK — keep pencil-red intent on cream */
.m002-host[data-grid-style="sketch"] .m002-insp-del{background:#fefcf6 !important;border-color:#c44b3c !important;color:#c44b3c !important;}
.m002-host[data-grid-style="sketch"] .m002-insp-del:hover{background:rgba(196,75,60,0.10) !important;}
.m002-host[data-grid-style="sketch"] .m002-insp-back{color:#7a7568 !important;}
.m002-host[data-grid-style="sketch"] .m002-insp-back:hover{color:#3a3a35 !important;}
/* Inspector + ports-grid scrollbar thumb — cream tan instead of dark grey */
.m002-host[data-grid-style="sketch"] .m002-inspector::-webkit-scrollbar-thumb,
.m002-host[data-grid-style="sketch"] .m002-ports-grid::-webkit-scrollbar-thumb{background:#c8c2b0 !important;}
/* Stack inspector — MEMBERS list rows. Dark panel + neon accent tag still
   showed up in the otherwise-sketch'd Stack inspector. (.m002-stack-member is
   re-used as a class on both the canvas device tile AND this list row — these
   selectors are scoped to the list parent so only the panel rows recolour.) */
.m002-host[data-grid-style="sketch"] .m002-stack-members .m002-stack-member{background:#fefcf6 !important;border-color:#c8c2b0 !important;}
.m002-host[data-grid-style="sketch"] .m002-stack-members .m002-stack-member-dot{box-shadow:none !important;}
.m002-host[data-grid-style="sketch"] .m002-stack-members .m002-stack-member-name{color:#3a3a35 !important;}
.m002-host[data-grid-style="sketch"] .m002-stack-members .m002-stack-member-type{color:#7a7568 !important;}
.m002-host[data-grid-style="sketch"] .m002-stack-members .m002-stack-member button{color:#7a7568 !important;}
.m002-host[data-grid-style="sketch"] .m002-stack-members .m002-stack-member button:hover{color:#c44b3c !important;}

/* =============================================================================
   GLOW HALOS — neutralised at user request (v2.33.26)
   =============================================================================
   Drop-shadow on canvas elements (devices, stacks, envelopes, links, ports,
   rope/autolink interaction tools, drag-target / VLAN-solo pulse keyframes)
   produced bright auras around every wall + label visible in the screenshot.
   User wants them gone — steady-state AND interactive (hover/selected/lifted)
   AND animation pulses.

   Kept (intentionally NOT overridden):
   - .m002-radial drop-shadow: UI separation shadow for the floating menu, not
     a glow halo. Without it the menu would visually merge with the canvas.
   - saturate / brightness filters used by routing- and vlan-layer dimming:
     those are colour treatments, not glows.
   - .m002-vsolo-link-fade animation: opacity-only fade, no drop-shadow.

   The canvas-element kill block uses PLAIN filter:none (no !important) — it
   still beats the original drop-shadow rules because it's later in source
   order at the same specificity, but layer-dim rules like
   `.m002-host[data-active-layer="routing"] .m002-stack-collapsed[data-l3="false"]`
   have higher specificity and still win, so routing/VLAN dim treatments work.
   The drag-pulse / vlan-solo-pulse block KEEPS !important because the source
   rules use !important on their animation declaration. */
.m002-device,
.m002-device:hover,
.m002-device.m002-selected,
.m002-device.m002-lifted,
.m002-device.m002-rope-target .m002-dev-bg,
.m002-device.m002-autolink-target .m002-dev-bg,
.m002-device-ref.m002-device-coupled .m002-dev-bg,
.m002-device.m002-stack-member:hover,
.m002-device.m002-stack-member.m002-selected,
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member[data-l3="true"],
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member[data-l3="true"]:hover,
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member[data-l3="true"].m002-selected,
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member-vip:hover,
.m002-host[data-active-layer="routing"] .m002-device.m002-stack-member-vip[data-l3="true"]:hover,
.m002-stack-collapsed,
.m002-stack-collapsed:hover,
.m002-stack-collapsed.m002-selected,
.m002-stack-collapsed.m002-lifted,
.m002-stack-envelope,
.m002-stack-envelope:hover,
.m002-stack-envelope.m002-selected,
.m002-link-flow,
.m002-link:hover .m002-link-line,
.m002-link:hover .m002-link-label,
.m002-link.m002-selected .m002-link-line,
.m002-link.m002-selected .m002-link-line.m002-link-stripe,
.m002-link.m002-selected .m002-link-label.m002-link-stripe-label,
.m002-link.m002-selected .m002-lag-line,
.m002-rope .m002-rope-line,
.m002-rope .m002-rope-tip,
.m002-autolink .m002-autolink-stub,
.m002-autolink .m002-autolink-tip,
.m002-autolink .m002-autolink-full,
.m002-l3-path-flow,
.m002-detail-device.is-selected .m002-detail-dev-bg,
.m002-detail-port:hover .m002-detail-port-box,
.m002-detail-port.is-selected .m002-detail-port-box,
.m002-rad-seg-dev .m002-rad-seg-dot{
  filter: none;
}
/* Drag-target merge-pulse + VLAN-solo amber-pulse animations bake drop-shadow
   into their keyframes; killing the filter alone isn't enough because the
   animation drives it on every frame. Disable the animations outright. The
   visible state-stroke (e.g. amber stroke on configured-only stacks) still
   remains via the static rule that paints the stroke. */
.m002-drag-stack-target.m002-merge-ok,
.m002-drag-stack-target.m002-merge-bad,
.m002-host[data-active-layer="vlan"] .m002-stack-collapsed[data-vlan-solo="configured-only"],
.m002-host[data-active-layer="vlan"] .m002-stack-envelope[data-vlan-solo="configured-only"]{
  animation: none !important;
  filter: none !important;
}

/* === Drawing layer — "glass overlay" annotations + floating toolbar === */
/* Toolbar: vertical column, top-left of the canvas (just below the layerbar). */
/* Legacy left-edge toolbar — replaced by the radial DRAW submenu. Kept in
   the DOM (engine still reads .active states) but no longer rendered. */
.m002-draw-toolbar{display:none !important;}
.m002-draw-toolbar--legacy{position:absolute;top:80px;left:18px;z-index:6;display:flex;flex-direction:column;align-items:stretch;gap:6px;background:rgba(8,8,14,0.85);border:1px solid #1a1a22;padding:6px;backdrop-filter:blur(6px);font-family:'JetBrains Mono','Share Tech Mono',monospace;color:#7a7f8e;user-select:none;}
.m002-draw-toolbar .m002-draw-body{display:flex;flex-direction:column;gap:6px;}
.m002-draw-toolbar[data-collapsed="true"] .m002-draw-body{display:none;}
.m002-draw-toolbar[data-collapsed="true"]{padding:4px;}
.m002-draw-toggle{display:flex;align-items:center;justify-content:center;width:28px;height:24px;background:transparent;border:1px solid transparent;color:#7a7f8e;cursor:pointer;font-family:inherit;font-size:13px;line-height:1;padding:0;align-self:flex-end;transition:color .12s,border-color .12s,background-color .12s;}
.m002-draw-toggle:hover{color:#e8e8ee;border-color:#1a1a22;}
.m002-draw-toggle-glyph{display:none;}
.m002-draw-toolbar[data-collapsed="false"] .m002-draw-toggle-collapse{display:inline;}
.m002-draw-toolbar[data-collapsed="true"]  .m002-draw-toggle-expand{display:inline;color:#ff003c;}
.m002-draw-toolbar[data-collapsed="true"]  .m002-draw-toggle{align-self:stretch;}
.m002-draw-section{display:flex;flex-direction:column;gap:3px;}
.m002-draw-sep{height:1px;background:#1a1a22;margin:2px 0;}
.m002-draw-tool,
.m002-draw-act{display:flex;align-items:center;justify-content:center;width:28px;height:28px;background:transparent;border:1px solid transparent;color:#7a7f8e;cursor:pointer;font-family:inherit;font-size:14px;line-height:1;padding:0;transition:color .12s,border-color .12s,background-color .12s;}
.m002-draw-tool:hover,
.m002-draw-act:hover{color:#e8e8ee;border-color:#1a1a22;}
.m002-draw-tool.active{background:rgba(255,0,60,0.10);border-color:#ff003c;color:#ff003c;}
.m002-draw-act.active{color:#ff003c;}
.m002-draw-glyph{font-family:inherit;line-height:1;}
.m002-draw-colors{display:grid;grid-template-columns:1fr 1fr;gap:3px;}
.m002-draw-color{width:13px;height:13px;border-radius:50%;border:1px solid rgba(255,255,255,0.18);background:var(--c,#fff);cursor:pointer;padding:0;transition:transform .12s,box-shadow .12s,border-color .12s;justify-self:center;}
.m002-draw-color:hover{transform:scale(1.15);}
.m002-draw-color.active{border-color:#ff003c;box-shadow:0 0 0 1.5px rgba(255,0,60,0.6),0 0 6px rgba(255,0,60,0.4);transform:scale(1.15);}
.m002-draw-widths{flex-direction:column;gap:3px;align-items:center;}
.m002-draw-width{display:flex;align-items:center;justify-content:center;width:28px;height:18px;background:transparent;border:1px solid transparent;cursor:pointer;padding:0;transition:border-color .12s,background-color .12s;}
.m002-draw-width:hover{border-color:#1a1a22;}
.m002-draw-width.active{border-color:#ff003c;background:rgba(255,0,60,0.06);}
.m002-draw-w-dot{display:inline-block;background:#9aa0a8;border-radius:50%;}
.m002-draw-width.active .m002-draw-w-dot{background:#ff003c;box-shadow:0 0 4px rgba(255,0,60,0.6);}

/* Drawings group: defaults to no pointer-events so devices/links remain
   interactive. In drawing mode we crosshair the whole canvas. */
.m002-drawings{pointer-events:none;}
.m002-drawing-active{pointer-events:none;}
.m002-host.m002-drawings-hidden .m002-drawings,
.m002-host.m002-drawings-hidden .m002-drawing-active{display:none;}
.m002-host.m002-drawing-mode .m002-svg{cursor:crosshair;}
.m002-host.m002-drawing-mode[data-draw-mode="text"] .m002-svg{cursor:text;}
.m002-host.m002-eraser-mode .m002-svg{cursor:not-allowed;}
.m002-host.m002-wipe-mode .m002-svg{cursor:crosshair;}
.m002-wipe-cursor{opacity:0.85;}
/* In eraser mode strokes become the only interactive things — devices and
   links pause their hit-testing so the user can target a stroke that overlaps
   them. */
.m002-host.m002-drawing-mode .m002-drawings{pointer-events:none;}
.m002-host.m002-eraser-mode .m002-drawings{pointer-events:auto;}
.m002-host.m002-eraser-mode .m002-draw-shape{pointer-events:stroke;}
.m002-host.m002-eraser-mode .m002-draw-shape--text,
.m002-host.m002-eraser-mode .m002-draw-shape--rect,
.m002-host.m002-eraser-mode .m002-draw-shape--ellipse{pointer-events:all;}
.m002-host.m002-eraser-mode .m002-draw-shape:hover{opacity:0.45;cursor:not-allowed;}

/* Inline text-input — floats over the SVG at the click coords. */
.m002-draw-text-input{position:absolute;z-index:7;background:rgba(8,8,14,0.92);border:1px dashed #ff003c;padding:2px 6px;font-family:'Rajdhani','Inter',sans-serif;font-size:16px;font-weight:600;color:#ff003c;outline:none;min-width:80px;}

/* Sketch style — paper-friendly toolbar palette. */
.m002-host[data-grid-style="sketch"] .m002-draw-toolbar{background:rgba(254,252,246,0.94);border-color:#c8c2b0;color:#7a7568;}
.m002-host[data-grid-style="sketch"] .m002-draw-tool,
.m002-host[data-grid-style="sketch"] .m002-draw-act,
.m002-host[data-grid-style="sketch"] .m002-draw-toggle{color:#7a7568;}
.m002-host[data-grid-style="sketch"] .m002-draw-tool:hover,
.m002-host[data-grid-style="sketch"] .m002-draw-act:hover,
.m002-host[data-grid-style="sketch"] .m002-draw-toggle:hover{color:#2a2a30;border-color:#c8c2b0;}
.m002-host[data-grid-style="sketch"] .m002-draw-tool.active{background:rgba(196,75,60,0.08);border-color:#c44b3c;color:#c44b3c;}
.m002-host[data-grid-style="sketch"] .m002-draw-act.active{color:#c44b3c;}
.m002-host[data-grid-style="sketch"] .m002-draw-color.active{border-color:#c44b3c;box-shadow:0 0 0 1.5px rgba(196,75,60,0.6);}
.m002-host[data-grid-style="sketch"] .m002-draw-width.active{border-color:#c44b3c;background:rgba(196,75,60,0.06);}
.m002-host[data-grid-style="sketch"] .m002-draw-width.active .m002-draw-w-dot{background:#c44b3c;box-shadow:0 0 4px rgba(196,75,60,0.4);}
.m002-host[data-grid-style="sketch"] .m002-draw-sep{background:#d6d0c0;}
.m002-host[data-grid-style="sketch"] .m002-draw-toolbar[data-collapsed="true"] .m002-draw-toggle-expand{color:#c44b3c;}
.m002-host[data-grid-style="sketch"] .m002-draw-text-input{background:rgba(254,252,246,0.96);border-color:#c44b3c;color:#c44b3c;}
