{"title":"Ubiquiti System Designer","description":"\u003c!-- ============================================================\n  AV EXPRESS — KIT BUILDER  (v3: Showroom + Command Center theme)\n  Paste this ENTIRE file into one \"Custom Liquid\" section in your\n  theme editor (Online Store \u003e Customize). No theme files are changed.\n\n  EDIT THE STEPS: see \"KIT CONFIG\" in the \u003cscript\u003e below. Each step\n  points at your collection handles — the builder pulls live\n  products, prices, photos, and stock from them.\n\n  COMPATIBILITY SPECS: the builder auto-reads specs from product\n  titles (\"12U\", \"24-Port\", \"PoE ... 190W\"). For precision, add\n  plain Shopify product TAGS to any product (Admin \u003e Product \u003e Tags):\n     ru:2           product is 2 rack-units tall\n     ports:24       switch\/panel port count\n     poe-budget:370 switch PoE power budget in watts\n     poe-draw:12    watts this device pulls over PoE\n     no-rack        never draw this product inside the rack\n============================================================ --\u003e\n\u003cdiv id=\"avx-kb\"\u003e\n  \u003cdiv class=\"avx-head\"\u003e\n    \u003cdiv class=\"avx-kicker\"\u003eAV Express Kit Builder\u003c\/div\u003e\n    \u003ch2 class=\"avx-title\" id=\"avx-main-title\"\u003eBuild Your Rack Kit\u003c\/h2\u003e\n    \u003cp class=\"avx-sub\" id=\"avx-main-sub\"\u003e\u003c\/p\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"avx-stepper\" id=\"avx-stepper\"\u003e\u003c\/div\u003e\n  \u003cdiv class=\"avx-layout\"\u003e\n    \u003cdiv class=\"avx-main\"\u003e\n      \u003cdiv class=\"avx-step-head\"\u003e\n        \u003cdiv\u003e\n          \u003cdiv class=\"avx-step-count\" id=\"avx-step-count\"\u003e\u003c\/div\u003e\n          \u003ch3 class=\"avx-step-title\" id=\"avx-step-title\"\u003e\u003c\/h3\u003e\n          \u003cp class=\"avx-step-blurb\" id=\"avx-step-blurb\"\u003e\u003c\/p\u003e\n        \u003c\/div\u003e\n        \u003ca class=\"avx-browse\" id=\"avx-browse\" href=\"#\" target=\"_blank\" rel=\"noopener\"\u003eBrowse full collection →\u003c\/a\u003e\n      \u003c\/div\u003e\n      \u003cdiv class=\"avx-grid\" id=\"avx-grid\"\u003e\u003c\/div\u003e\n      \u003cdiv class=\"avx-nav\"\u003e\n        \u003cbutton type=\"button\" class=\"avx-btn avx-ghost\" id=\"avx-back\"\u003e← Back\u003c\/button\u003e\n        \u003cbutton type=\"button\" class=\"avx-btn avx-solid\" id=\"avx-next\"\u003eNext step →\u003c\/button\u003e\n      \u003c\/div\u003e\n    \u003c\/div\u003e\n    \u003caside class=\"avx-side\" id=\"avx-side\"\u003e\n      \u003cdiv class=\"avx-side-card\"\u003e\n        \u003cdiv class=\"avx-side-head\"\u003eYour Virtual Rack\u003c\/div\u003e\n        \u003cdiv class=\"avx-rack-wrap\"\u003e\n          \u003cdiv class=\"avx-rack\" id=\"avx-rack\"\u003e\u003c\/div\u003e\n          \u003cdiv class=\"avx-rack-foot\" id=\"avx-rack-foot\"\u003e\u003c\/div\u003e\n          \u003cdiv class=\"avx-chips\" id=\"avx-chips\"\u003e\u003c\/div\u003e\n        \u003c\/div\u003e\n        \u003cdiv class=\"avx-checks\" id=\"avx-checks\"\u003e\u003c\/div\u003e\n        \u003cdiv class=\"avx-lines\" id=\"avx-lines\"\u003e\u003c\/div\u003e\n        \u003cdiv class=\"avx-totals\"\u003e\n          \u003cdiv class=\"avx-tot-row\"\u003e\n\u003cspan id=\"avx-count\"\u003e0 items\u003c\/span\u003e\u003cspan class=\"avx-tot\" id=\"avx-total\"\u003e$0.00\u003c\/span\u003e\n\u003c\/div\u003e\n          \u003cdiv class=\"avx-tot-note\"\u003eShipping \u0026amp; tax calculated at checkout\u003c\/div\u003e\n        \u003c\/div\u003e\n        \u003cdiv class=\"avx-snav\"\u003e\n          \u003cbutton type=\"button\" class=\"avx-btn avx-ghost avx-sback\" id=\"avx-sback\" title=\"Previous step\"\u003e←\u003c\/button\u003e\n          \u003cbutton type=\"button\" class=\"avx-btn avx-solid avx-snext\" id=\"avx-snext\"\u003eNext step →\u003c\/button\u003e\n        \u003c\/div\u003e\n        \u003cbutton type=\"button\" class=\"avx-btn avx-cart\" id=\"avx-addcart\" disabled\u003eAdd Kit to Cart\u003c\/button\u003e\n        \u003cdiv class=\"avx-msg\" id=\"avx-msg\"\u003e\u003c\/div\u003e\n      \u003c\/div\u003e\n    \u003c\/aside\u003e\n  \u003c\/div\u003e\n  \u003cdiv class=\"avx-mbar\" id=\"avx-mbar\"\u003e\n    \u003cdiv class=\"avx-mbar-info\" id=\"avx-minfo\"\u003e\n\u003cspan id=\"avx-mcount\"\u003e0 items\u003c\/span\u003e\u003cstrong id=\"avx-mtotal\"\u003e$0.00\u003c\/strong\u003e\n\u003c\/div\u003e\n    \u003cbutton type=\"button\" class=\"avx-btn avx-solid avx-mbtn\" id=\"avx-mnext\"\u003eNext →\u003c\/button\u003e\n    \u003cbutton type=\"button\" class=\"avx-btn avx-mcart avx-mbtn\" id=\"avx-mcartbtn\" disabled\u003eAdd Kit\u003c\/button\u003e\n  \u003c\/div\u003e\n\u003c\/div\u003e\n\n\u003cstyle\u003e\n#avx-kb{--teal:#17aec8;--teal-d:#0e94ac;--teal-x:#0b7b90;--ink:#13252f;--ink-2:#4a6270;--bg:#f4f8f9;--line:#dde7ea;--white:#fff;--bad:#c0392b;--ok:#1d8a4e;--warn:#b97a16;\n  font-family:inherit;color:var(--ink);max-width:1200px;margin:0 auto;padding:8px 0 90px;box-sizing:border-box;line-height:1.45}\n#avx-kb *,#avx-kb *:before,#avx-kb *:after{box-sizing:border-box}\n#avx-kb.avx-demo{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;padding:24px 16px 90px}\n#avx-kb button{font-family:inherit;cursor:pointer}\n#avx-kb .avx-head{text-align:center;margin-bottom:24px;background:linear-gradient(130deg,#0b3550 0%,#11708e 60%,#17aec8 100%);border-radius:20px;padding:36px 22px 32px}\n#avx-kb .avx-kicker{display:inline-block;background:rgba(255,255,255,.16);border:1px solid rgba(255,255,255,.4);color:#fff;font-size:11px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;padding:5px 12px;border-radius:999px;margin-bottom:12px}\n#avx-kb .avx-title{font-size:clamp(24px,4vw,36px);font-weight:800;margin:0 0 8px;color:#fff}\n#avx-kb .avx-sub{max-width:680px;margin:0 auto;color:rgba(255,255,255,.88);font-size:15px}\n#avx-kb .avx-stepper{display:flex;gap:8px;overflow-x:auto;padding:6px 2px 14px;-webkit-overflow-scrolling:touch}\n#avx-kb .avx-chip{flex:0 0 auto;display:flex;align-items:center;gap:8px;border:0;background:var(--white);border-radius:999px;padding:9px 15px 9px 9px;font-size:13px;font-weight:600;color:var(--ink-2);transition:all .15s;box-shadow:0 2px 10px rgba(19,37,47,.08)}\n#avx-kb .avx-chip .n{width:22px;height:22px;border-radius:50%;background:var(--bg);border:1.5px solid var(--line);display:flex;align-items:center;justify-content:center;font-size:11px;font-weight:700;color:var(--ink-2)}\n#avx-kb .avx-chip.on{color:var(--teal-x);box-shadow:0 4px 16px rgba(23,174,200,.35),inset 0 0 0 1.5px var(--teal)}\n#avx-kb .avx-chip.on .n{background:var(--teal);border-color:var(--teal);color:#fff}\n#avx-kb .avx-chip.done .n{background:var(--ok);border-color:var(--ok);color:#fff}\n#avx-kb .avx-layout{display:grid;grid-template-columns:minmax(0,1fr) 340px;gap:22px;align-items:start}\n#avx-kb .avx-main,#avx-kb .avx-side{min-width:0;max-width:100%}\n#avx-kb .avx-step-head\u003ediv{min-width:0}\n#avx-kb .avx-step-head{display:flex;justify-content:space-between;align-items:flex-end;gap:14px;margin-bottom:14px;flex-wrap:wrap}\n#avx-kb .avx-step-count{font-size:11px;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--teal-x)}\n#avx-kb .avx-step-title{font-size:22px;font-weight:800;margin:2px 0 4px}\n#avx-kb .avx-step-blurb{margin:0;color:var(--ink-2);font-size:14px;max-width:560px}\n#avx-kb .avx-browse{font-size:13px;font-weight:600;color:var(--teal-x);text-decoration:none;white-space:nowrap}\n#avx-kb .avx-browse:hover{text-decoration:underline}\n#avx-kb .avx-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:14px;min-height:220px}\n#avx-kb .avx-load{grid-column:1\/-1;text-align:center;color:var(--ink-2);padding:60px 10px;font-size:14px}\n#avx-kb .avx-spin{width:26px;height:26px;border:3px solid var(--line);border-top-color:var(--teal);border-radius:50%;margin:0 auto 12px;animation:avxspin .8s linear infinite}\n@keyframes avxspin{to{transform:rotate(360deg)}}\n#avx-kb .avx-card{background:var(--white);border:1.5px solid transparent;border-radius:16px;padding:12px;display:flex;flex-direction:column;gap:8px;transition:box-shadow .15s,border-color .15s,transform .15s;position:relative;box-shadow:0 2px 12px rgba(19,37,47,.07)}\n#avx-kb .avx-card:hover{transform:translateY(-3px);box-shadow:0 14px 30px rgba(19,37,47,.14)}\n#avx-kb .avx-card.in{border-color:var(--teal);box-shadow:0 0 0 1px var(--teal)}\n#avx-kb .avx-inbadge{position:absolute;top:8px;left:8px;background:var(--teal);color:#fff;font-size:10px;font-weight:700;padding:3px 8px;border-radius:999px;z-index:1}\n#avx-kb .avx-soldout{position:absolute;top:8px;left:8px;background:#8d9aa3;color:#fff;font-size:10px;font-weight:700;padding:3px 8px;border-radius:999px;z-index:1}\n#avx-kb .avx-img{width:100%;aspect-ratio:1\/1;object-fit:contain;background:var(--bg);border-radius:12px}\n#avx-kb .avx-vendor{font-size:10px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-2)}\n#avx-kb .avx-pname{font-size:13.5px;font-weight:600;line-height:1.3;margin:0;min-height:35px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}\n#avx-kb .avx-pname a{color:inherit;text-decoration:none}\n#avx-kb .avx-pname a:hover{color:var(--teal-x)}\n#avx-kb .avx-specs{display:flex;gap:4px;flex-wrap:wrap;min-height:18px}\n#avx-kb .avx-spec{font-size:10px;font-weight:700;background:var(--bg);border:1px solid var(--line);color:var(--ink-2);border-radius:5px;padding:1px 6px}\n#avx-kb .avx-price{font-size:15px;font-weight:800;color:var(--teal-x)}\n#avx-kb .avx-was{font-size:12px;font-weight:500;color:var(--ink-2);text-decoration:line-through;margin-left:6px}\n#avx-kb .avx-vsel{width:100%;border:1.5px solid var(--line);border-radius:8px;padding:7px 8px;font-size:12.5px;background:var(--white);color:var(--ink)}\n#avx-kb .avx-rowqty{display:flex;gap:8px;margin-top:auto}\n#avx-kb .avx-qty{display:flex;align-items:center;border:1.5px solid var(--line);border-radius:8px;overflow:hidden}\n#avx-kb .avx-qty button{width:26px;height:32px;border:0;background:var(--bg);font-size:15px;font-weight:700;color:var(--ink)}\n#avx-kb .avx-qty input{width:34px;height:32px;border:0;text-align:center;font-size:13px;font-weight:600;color:var(--ink);-moz-appearance:textfield;background:var(--white)}\n#avx-kb .avx-qty input::-webkit-outer-spin-button,#avx-kb .avx-qty input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}\n#avx-kb .avx-add{flex:1;border:0;border-radius:999px;background:var(--ink);color:#fff;font-size:12.5px;font-weight:700;height:32px;transition:background .15s}\n#avx-kb .avx-add:hover:not(:disabled){background:var(--teal-d)}\n#avx-kb .avx-add.ok{background:var(--ok)}\n#avx-kb .avx-add:disabled{background:#b8c3c9;cursor:not-allowed}\n#avx-kb .avx-nav{display:flex;justify-content:space-between;gap:12px;margin-top:20px}\n#avx-kb .avx-btn{border-radius:999px;font-size:14px;font-weight:700;padding:12px 22px;border:1.5px solid transparent;transition:all .15s}\n#avx-kb .avx-solid{background:var(--teal);color:#fff}\n#avx-kb .avx-solid:hover{background:var(--teal-d)}\n#avx-kb .avx-ghost{background:transparent;border-color:var(--line);color:var(--ink-2)}\n#avx-kb .avx-ghost:hover{border-color:var(--ink-2);color:var(--ink)}\n#avx-kb .avx-ghost:disabled{opacity:.4;cursor:not-allowed}\n#avx-kb .avx-side-card{background:linear-gradient(180deg,#10212c,#152631);border:0;border-radius:20px;padding:16px;position:sticky;top:12px;box-shadow:0 14px 40px rgba(11,37,50,.35);max-height:calc(100vh - 24px);display:flex;flex-direction:column}\n#avx-kb .avx-side-card\u003e*{flex:0 0 auto}\n#avx-kb .avx-side-head{font-size:15px;font-weight:800;margin-bottom:12px;display:flex;align-items:center;gap:8px;color:#fff}\n#avx-kb .avx-side-head:before{content:\"\";width:8px;height:8px;border-radius:50%;background:var(--teal);box-shadow:0 0 8px var(--teal)}\n#avx-kb .avx-rack-wrap{background:#0a141b;border-radius:12px;padding:12px 14px;margin-bottom:12px;flex:0 1 auto;min-height:130px;overflow-y:auto;box-shadow:inset 0 0 0 1px #203544}\n#avx-kb .avx-rack{position:relative;margin:0 auto;max-width:252px;border:4px solid #1b2b36;border-top-width:8px;border-bottom-width:8px;border-radius:6px;background:#121f28;padding:4px 14px;min-height:60px}\n#avx-kb .avx-rack:before,#avx-kb .avx-rack:after{content:\"\";position:absolute;top:4px;bottom:4px;width:7px;background:repeating-linear-gradient(0deg,#2b3f4c 0 3px,#121f28 3px 8px);border-radius:2px}\n#avx-kb .avx-rack:before{left:3px}\n#avx-kb .avx-rack:after{right:3px}\n#avx-kb .avx-u{position:relative;border-radius:2px;margin:2px 0;display:flex;align-items:center;gap:6px;padding:0 8px;color:#e8f4f7;overflow:hidden;animation:avxin .25s ease}\n@keyframes avxin{from{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:none}}\n#avx-kb .avx-u .ul{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:9.5px;font-weight:700;letter-spacing:.02em}\n#avx-kb .avx-u .uu{font-size:8.5px;font-weight:700;color:rgba(255,255,255,.55);flex:0 0 auto}\n#avx-kb .avx-u .pr{display:flex;gap:2px;flex:0 0 auto;align-items:center}\n#avx-kb .avx-u .pr i{width:4px;height:4px;background:#0a161d;border:1px solid rgba(255,255,255,.35);border-radius:1px;display:block}\n#avx-kb .avx-u .led{width:4px;height:4px;border-radius:50%;background:#3ee08f;box-shadow:0 0 4px #3ee08f;flex:0 0 auto}\n#avx-kb .avx-u .out{display:flex;gap:4px;flex:0 0 auto}\n#avx-kb .avx-u .out i{width:7px;height:7px;border-radius:50%;background:#0a161d;border:1.5px solid rgba(255,255,255,.4);display:block}\n#avx-kb .avx-u.k-switch{background:linear-gradient(180deg,#14859d,#0e6b80)}\n#avx-kb .avx-u.k-panel{background:linear-gradient(180deg,#2e4857,#243a47)}\n#avx-kb .avx-u.k-power{background:linear-gradient(180deg,#3d5665,#2f4551)}\n#avx-kb .avx-u.k-mount{background:linear-gradient(180deg,#476878,#3a5765)}\n#avx-kb .avx-u.k-vent{background:repeating-linear-gradient(0deg,#3a5765 0 2px,#314c59 2px 4px)}\n#avx-kb .avx-u.over{background:#6e2f28;outline:1.5px dashed #ff9c8f;outline-offset:-2px}\n#avx-kb .avx-u-free{border:1.5px dashed #2b4250;border-radius:3px;margin:2px 0;display:flex;align-items:center;justify-content:center;color:#51707f;font-size:9px;font-weight:700;letter-spacing:.08em}\n#avx-kb .avx-rack-empty{color:#51707f;font-size:11.5px;text-align:center;padding:26px 8px;font-weight:600}\n#avx-kb .avx-rack{box-shadow:0 0 24px rgba(23,174,200,.18)}\n#avx-kb .avx-rack-foot{text-align:center;font-size:11px;font-weight:700;color:#8fb6c2;margin-top:8px}\n#avx-kb .avx-chips{display:flex;flex-wrap:wrap;gap:5px;justify-content:center;margin-top:8px}\n#avx-kb .avx-chip2{font-size:10px;font-weight:700;background:#0c1922;border:1.5px solid #2a4150;border-radius:999px;padding:3px 9px;color:#9fc3cf}\n#avx-kb .avx-chip2.net{border-color:var(--teal);color:#4fd4ea}\n#avx-kb .avx-checks{display:flex;flex-direction:column;gap:7px;margin-bottom:12px;flex:0 1 auto;overflow-y:auto}\n#avx-kb .avx-ck{display:flex;gap:8px;font-size:12px;line-height:1.35;align-items:flex-start;color:#c6d9e2}\n#avx-kb .avx-ck .i{flex:0 0 16px;height:16px;border-radius:50%;color:#fff;font-size:10px;font-weight:800;display:flex;align-items:center;justify-content:center;margin-top:1px}\n#avx-kb .avx-ck.ok .i{background:var(--ok)}\n#avx-kb .avx-ck.warn .i{background:var(--warn)}\n#avx-kb .avx-ck.bad .i{background:var(--bad)}\n#avx-kb .avx-ck.tip .i{background:#7e97a3}\n#avx-kb .avx-ck.bad{color:#ff8a7a;font-weight:600}\n#avx-kb .avx-ck.warn{color:#ffc46b}\n#avx-kb .avx-meter{height:5px;background:#203544;border-radius:999px;overflow:hidden;margin-top:4px}\n#avx-kb .avx-meter b{display:block;height:100%;background:var(--teal);border-radius:999px;transition:width .3s}\n#avx-kb .avx-meter.hot b{background:var(--warn)}\n#avx-kb .avx-meter.full b{background:var(--bad)}\n#avx-kb .avx-lines{display:flex;flex-direction:column;gap:10px;max-height:220px;overflow-y:auto;margin-bottom:12px;flex:0 1 auto;min-height:50px}\n#avx-kb .avx-line{display:flex;gap:10px;align-items:center}\n#avx-kb .avx-line img{width:40px;height:40px;object-fit:contain;background:#fff;border-radius:6px;flex:0 0 auto}\n#avx-kb .avx-line-info{flex:1;min-width:0}\n#avx-kb .avx-line-name{font-size:12px;font-weight:600;line-height:1.25;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#e9f4f8}\n#avx-kb .avx-line-var{font-size:11px;color:#93acb8;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}\n#avx-kb .avx-line-price{font-size:12px;font-weight:700;white-space:nowrap;color:#4fd4ea}\n#avx-kb .avx-line .avx-qty{border-color:#2a4150}\n#avx-kb .avx-line .avx-qty button{height:24px;width:22px;font-size:13px;background:#0c1922;color:#e9f4f8}\n#avx-kb .avx-line .avx-qty input{height:24px;width:28px;font-size:12px;background:#152631;color:#fff}\n#avx-kb .avx-rm{border:0;background:none;color:#93acb8;font-size:16px;line-height:1;padding:2px 4px}\n#avx-kb .avx-rm:hover{color:#ff8a7a}\n#avx-kb .avx-empty-note{color:#93acb8;font-size:12.5px;text-align:center;padding:4px 0 10px}\n#avx-kb .avx-totals{border-top:1.5px solid #2a4150;padding-top:10px;margin-bottom:10px}\n#avx-kb .avx-tot-row{display:flex;justify-content:space-between;align-items:center;font-size:13px;color:#93acb8}\n#avx-kb .avx-tot{font-size:20px;font-weight:800;color:#fff}\n#avx-kb .avx-tot-note{font-size:11px;color:#93acb8;margin-top:2px}\n#avx-kb .avx-snav{display:flex;gap:8px;margin-bottom:8px}\n#avx-kb .avx-sback{flex:0 0 46px;padding:12px 0;text-align:center;border-color:#2a4150;color:#93acb8}\n#avx-kb .avx-sback:hover{border-color:#93acb8;color:#e9f4f8}\n#avx-kb .avx-snext{flex:1;box-shadow:0 0 16px rgba(23,174,200,.35)}\n#avx-kb .avx-cart{width:100%;background:var(--teal);color:#fff;font-size:15px;padding:13px;box-shadow:0 0 18px rgba(23,174,200,.35)}\n#avx-kb .avx-cart:hover:not(:disabled){background:#2cc2db}\n#avx-kb .avx-cart:disabled{background:#3a4f5b;color:#8aa0ab;cursor:not-allowed;box-shadow:none}\n#avx-kb .avx-msg{font-size:12.5px;margin-top:10px;text-align:center;min-height:16px;color:#93acb8}\n#avx-kb .avx-msg.err{color:#ff8a7a}\n#avx-kb .avx-msg.ok{color:#4fdd9a}\n#avx-kb .avx-rev-group{background:var(--white);border:1.5px solid var(--line);border-radius:12px;padding:14px 16px;margin-bottom:12px}\n#avx-kb .avx-rev-step{font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--teal-x);margin-bottom:8px}\n#avx-kb .avx-rev-row{display:flex;justify-content:space-between;gap:10px;font-size:13.5px;padding:4px 0}\n#avx-kb .avx-rev-row .q{color:var(--ink-2)}\n#avx-kb .avx-rev-skip{color:var(--ink-2);font-size:13px;font-style:italic}\n#avx-kb .avx-warn{background:#fdf3e7;border:1.5px solid #f0d4ae;border-radius:10px;padding:10px 14px;font-size:13px;color:#8a5a1e;margin-bottom:12px}\n#avx-kb .avx-demo-banner{background:#fdf3e7;border:1.5px solid #f0d4ae;color:#8a5a1e;font-size:12.5px;border-radius:10px;padding:8px 14px;text-align:center;margin-bottom:16px}\n#avx-kb .avx-mbar{display:none}\n@media (max-width:899px){\n  #avx-kb .avx-layout{grid-template-columns:minmax(0,1fr)}\n  #avx-kb .avx-side-card{position:static;max-height:none}\n  #avx-kb .avx-mbar{display:flex;position:fixed;left:0;right:0;bottom:0;background:#10212c;border-top:1.5px solid #2a4150;padding:10px 12px;z-index:50;align-items:center;gap:8px;box-shadow:0 -6px 20px rgba(11,37,50,.4)}\n  #avx-kb .avx-mbar-info{display:flex;flex-direction:column;font-size:11px;color:#93acb8;flex:1;min-width:0}\n  #avx-kb .avx-mbar-info strong{font-size:16px;color:#fff}\n  #avx-kb .avx-mbtn{padding:11px 14px;font-size:13px;white-space:nowrap}\n  #avx-kb .avx-mcart{background:#0c1922;color:#fff;border:1.5px solid #2a4150}\n  #avx-kb .avx-mcart:disabled{background:#0c1922;color:#5e7682}\n}\n@media (max-width:639px){\n  #avx-kb{padding-bottom:84px}\n  #avx-kb .avx-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}\n  #avx-kb .avx-card{padding:9px;gap:6px;border-radius:10px}\n  #avx-kb .avx-pname{font-size:12px;min-height:31px}\n  #avx-kb .avx-vendor{font-size:9px}\n  #avx-kb .avx-price{font-size:14px}\n  #avx-kb .avx-spec{font-size:9px;padding:1px 5px}\n  #avx-kb .avx-add{font-size:11px;height:30px}\n  #avx-kb .avx-qty button{width:22px;height:30px}\n  #avx-kb .avx-qty input{width:26px;height:30px;font-size:12px}\n  #avx-kb .avx-vsel{font-size:11.5px;padding:6px}\n  #avx-kb .avx-step-title{font-size:19px}\n  #avx-kb .avx-step-blurb{font-size:13px}\n  #avx-kb .avx-chip{font-size:12px;padding:6px 11px 6px 6px}\n  #avx-kb .avx-chip .n{width:20px;height:20px;font-size:10px}\n  #avx-kb .avx-head{margin-bottom:14px}\n  #avx-kb .avx-sub{font-size:13.5px}\n  #avx-kb .avx-btn{padding:11px 16px;font-size:13px}\n  #avx-kb .avx-rev-row{font-size:12.5px}\n}\n\u003c\/style\u003e\n\n\u003cscript\u003e\n(function(){\n\"use strict\";\n\/* ================= KIT CONFIG — edit freely =================\n   collections: your collection handles (URL part after \/collections\/)\n   kind: rack | mount | switch | net (connects to switch) | power | acc\n   defRU: rack-units assumed when not found in title\/tags\n   defDraw: PoE watts assumed for net devices when not tagged       *\/\nvar CFG = {\n  heading: \"Build Your Rack Kit\",\n  sub: \"New to rack builds? No problem. Go step by step, watch your virtual rack fill up, and let the compatibility checker confirm the power, ports, and space all work — then add the whole kit to your cart in one click.\",\n  steps: [\n    {title:\"Rack\", kind:\"rack\", blurb:\"Start with the backbone. Rack sizes are measured in U (rack units) — every device takes up 1U or more, so when in doubt, size up for growing room.\", collections:[\"racks\"], required:true},\n    {title:\"Patch Panel\", kind:\"mount\", defRU:1, blurb:\"Your wall cable runs terminate here, then short patch cables jump to the switch. Match the port count to the number of cable runs, plus spares.\", collections:[\"patch-panels\"]},\n    {title:\"Network Switch\", kind:\"switch\", defRU:1, blurb:\"The heart of the system. A PoE switch powers access points and cameras through the data cable — check its PoE budget (watts) covers everything you attach. The checker on the right tracks this for you.\", collections:[\"switches\",\"poe-switches\"]},\n    {title:\"Router \u0026 WiFi\", kind:\"net\", defDraw:15, blurb:\"The router shares the internet connection; access points spread WiFi. Each device here uses one switch port, and PoE models pull power from the switch's budget.\", collections:[\"routers\",\"wifi-access-points\"]},\n    {title:\"Power\", kind:\"power\", defRU:1, blurb:\"Protect the investment. A surge-protected PDU is the minimum; a UPS battery backup keeps the network alive through outages.\", collections:[\"rack-mount-power\",\"battery-backup\",\"surge-protection\"]},\n    {title:\"Patch Cables\", kind:\"acc\", blurb:\"Short 6in–3ft cables keep the rack clean. Rule of thumb: one per patch-panel port you'll use, plus one per device.\", collections:[\"cat6-patch-cables\",\"cat5e-patch-cables\"]},\n    {title:\"Accessories\", kind:\"mount\", defRU:1, blurb:\"Shelves hold non-rackmount gear, blanks cover empty slots for airflow, and velcro keeps the cabling professional.\", collections:[\"rack-shelves\",\"rack-blanks\",\"rack-accessories\",\"wire-cable-management\"]}\n  ],\n  perCollection: 24, maxCards: 48\n};\n\/* ============================================================ *\/\n\nvar state={step:0,items:[],cache:{},demo:false};\nvar $=function(id){return document.getElementById(id);};\nfunction esc(s){return String(s==null?\"\":s).replace(\/\u0026\/g,\"\u0026amp;\").replace(\/\u003c\/g,\"\u0026lt;\").replace(\/\u003e\/g,\"\u0026gt;\").replace(\/\"\/g,\"\u0026quot;\");}\nfunction money(n){return Number(n).toLocaleString(\"en-US\",{style:\"currency\",currency:\"USD\"});}\nfunction num(p){var n=parseFloat(p);return isNaN(n)?0:n;}\nfunction ph(label){\n  var svg=\"\u003csvg xmlns='http:\/\/www.w3.org\/2000\/svg' width='300' height='300'\u003e\u003crect width='300' height='300' fill='#eef3f5'\/\u003e\u003ctext x='150' y='158' font-family='Arial' font-size='22' font-weight='bold' fill='#9fb2bb' text-anchor='middle'\u003e\"+label+\"\u003c\/text\u003e\u003c\/svg\u003e\";\n  return \"data:image\/svg+xml;utf8,\"+encodeURIComponent(svg).replace(\/'\/g,\"%27\");\n}\n\n\/* ---------- spec detection (tags override, then title) ---------- *\/\nfunction tagStr(p){var t=p.tags;if(!t)return\"\";return(Array.isArray(t)?t.join(\"|\"):String(t)).toLowerCase();}\nfunction specs(p,stepIdx){\n  var s=CFG.steps[stepIdx],kind=s.kind||\"acc\",tags=tagStr(p),title=p.title||\"\",m;\n  function tn(k){var mm=tags.match(new RegExp(k+\":\\\\s*(\\\\d+)\"));return mm?+mm[1]:null;}\n  var ru=tn(\"ru\");\n  if(ru==null){m=title.match(\/(\\d{1,2})\\s*-?\\s*U\\b\/i);ru=m?+m[1]:null;}\n  if(tags.indexOf(\"no-rack\")\u003e-1)ru=0;\n  if(ru==null)ru=(kind===\"mount\"||kind===\"switch\"||kind===\"power\")?(s.defRU||1):0;\n  var ports=tn(\"ports\");\n  if(ports==null){m=title.match(\/(\\d{1,3})[\\s-]*port\/i);ports=m?+m[1]:0;}\n  var poe=\/poe\/i.test(title)||tn(\"poe-budget\")!=null;\n  var budget=tn(\"poe-budget\");\n  if(budget==null\u0026\u0026kind===\"switch\"\u0026\u0026poe){m=title.match(\/(\\d{2,4})\\s*W\\b\/i);budget=m?+m[1]:null;}\n  var draw=tn(\"poe-draw\"),drawEst=false;\n  if(draw==null\u0026\u0026kind===\"net\"\u0026\u0026\/(poe|access point|\\bap\\b|camera|cam\\b|doorbell)\/i.test(title)){draw=s.defDraw||15;drawEst=true;}\n  return {kind:kind,ru:ru||0,ports:ports||0,poe:kind===\"switch\"\u0026\u0026poe,budget:budget,draw:draw||0,drawEst:drawEst,needsPort:kind===\"net\"};\n}\nfunction specChips(sp){\n  var out=[];\n  if(sp.ru)out.push(sp.ru+\"U\");\n  if(sp.ports)out.push(sp.ports+\" ports\");\n  if(sp.poe)out.push(sp.budget?\"PoE \"+sp.budget+\"W\":\"PoE\");\n  if(sp.draw)out.push((sp.drawEst?\"~\":\"\")+sp.draw+\"W PoE draw\");\n  return out.map(function(c){return \"\u003cspan class='avx-spec'\u003e\"+c+\"\u003c\/span\u003e\";}).join(\"\");\n}\n\n\/* ---------- demo catalog (only when previewing off-store) ---------- *\/\nfunction demoCatalog(){\n  function P(t,v,price,opts,img,tags){return {id:Math.random()*1e9|0,title:t,vendor:v,handle:\"#\",tags:tags||[],images:[{src:ph(img)}],\n    variants:(opts||[[\"Default Title\",price,true]]).map(function(o){return {id:900000+(Math.random()*1e6|0),title:o[0],price:String(o[1]!=null?o[1]:price),available:o[2]!==false};})};}\n  return {\n    \"racks\":[P(\"12U Wall-Mount Swing-Out Rack\",\"ProMount\",\"429.74\",null,\"RACK\"),P(\"9U Skeleton Open Frame Rack\",\"ProMount\",\"189.00\",null,\"RACK\"),P(\"6U Wall Rack w\/ Glass Door\",\"NetFrame\",\"239.50\",null,\"RACK\"),P(\"18U Assembled Floor Rack\",\"NetFrame\",\"659.00\",null,\"RACK\")],\n    \"patch-panels\":[P(\"24-Port Blank Patch Panel + Cable Manager, 1U\",\"Vertical Cable\",\"29.68\",null,\"PANEL\"),P(\"48-Port Cat6 Loaded Patch Panel, 2U\",\"Vertical Cable\",\"119.00\",null,\"PANEL\"),P(\"12-Port Mini Patch Panel 1U\",\"NetFrame\",\"19.95\",null,\"PANEL\")],\n    \"switches\":[P(\"16-Port Gigabit PoE+ Switch 190W, 1U\",\"LinkCore\",\"249.00\",null,\"SWITCH\"),P(\"8-Port Gigabit Switch\",\"LinkCore\",\"59.00\",null,\"SWITCH\",[\"no-rack\"]),P(\"24-Port Managed PoE Switch 370W, 1U\",\"LinkCore\",\"459.00\",null,\"SWITCH\")],\n    \"routers\":[P(\"Mesh WiFi 7 Router\",\"AirWave\",\"375.00\",null,\"WIFI\",[\"poe-draw:0\"]),P(\"PoE WiFi 6 Access Point\",\"AirWave\",\"173.00\",[[\"Without PoE Injector\",\"173.00\",true],[\"With PoE Injector\",\"203.00\",true]],\"WIFI\",[\"poe-draw:13\"]),P(\"4MP PoE Turret Camera\",\"ViewLine\",\"89.00\",null,\"CAM\",[\"poe-draw:7\"])],\n    \"rack-mount-power\":[P(\"Rack-Mount PDU, 9 Outlet, 15A, 1U\",\"PowerSafe\",\"89.00\",null,\"POWER\"),P(\"1500VA UPS Battery Backup, 2U\",\"PowerSafe\",\"329.00\",null,\"UPS\"),P(\"Surge Protector Strip, 6 Outlet\",\"PowerSafe\",\"24.95\",null,\"SURGE\",[\"no-rack\"])],\n    \"cat6-patch-cables\":[P(\"Cat6a Slim Patch Cable\",\"Vertical Cable\",\"2.25\",[[\"Green \/ 1ft\",\"3.50\",true],[\"Blue \/ 1ft\",\"3.50\",true],[\"Black \/ 3ft\",\"5.00\",true],[\"White \/ 5ft\",\"5.75\",false]],\"CAT6\"),P(\"Cat6 Patch Cable 10-Pack\",\"Vertical Cable\",\"19.90\",null,\"CAT6\")],\n    \"rack-shelves\":[P(\"19\\\" Cantilever Shelf 1U\",\"Intellinet\",\"34.79\",null,\"SHELF\"),P(\"19\\\" Vented Blank Panel 1U\",\"Intellinet\",\"15.97\",null,\"BLANK\"),P(\"Velcro Roll 75ft\",\"Vertical Cable\",\"22.44\",[[\"1\/2in \/ Black\",\"24.59\",true],[\"3\/4in \/ Black\",\"22.44\",true]],\"VELCRO\",[\"no-rack\"]),P(\"Horizontal Cable Manager 1U\",\"NetFrame\",\"16.50\",null,\"MGMT\")]\n  };\n}\n\n\/* ---------- data loading ---------- *\/\nfunction loadStep(idx,cb){\n  var step=CFG.steps[idx];\n  if(state.cache[idx]){cb(state.cache[idx]);return;}\n  if(state.demo){state.cache[idx]=demoProductsFor(step);cb(state.cache[idx]);return;}\n  var results=[],pending=step.collections.length,failed=0;\n  step.collections.forEach(function(h,ci){\n    fetch(\"\/collections\/\"+h+\"\/products.json?limit=\"+CFG.perCollection)\n      .then(function(r){if(!r.ok)throw 0;return r.json();})\n      .then(function(j){results[ci]=(j\u0026\u0026j.products)||[];})\n      .catch(function(){results[ci]=[];failed++;})\n      .then(function(){\n        pending--;\n        if(pending===0){\n          var seen={},merged=[];\n          results.forEach(function(arr){(arr||[]).forEach(function(p){if(!seen[p.id]){seen[p.id]=1;merged.push(p);}});});\n          if(merged.length===0\u0026\u0026failed===step.collections.length\u0026\u0026!onStore()){\n            state.demo=true;document.getElementById(\"avx-kb\").classList.add(\"avx-demo\");showDemoBanner();\n            state.cache[idx]=demoProductsFor(step);cb(state.cache[idx]);return;\n          }\n          merged.sort(function(a,b){return avail(b)-avail(a);});\n          if(merged.length\u003eCFG.maxCards)merged=merged.slice(0,CFG.maxCards);\n          state.cache[idx]=merged;cb(merged);\n        }\n      });\n  });\n}\nfunction demoProductsFor(step){\n  var cat=demoCatalog(),out=[];\n  step.collections.forEach(function(h){if(cat[h])out=out.concat(cat[h]);});\n  if(!out.length){var k=Object.keys(cat);out=cat[k[0]];}\n  return out;\n}\nfunction onStore(){return \/\\.myshopify\\.com$|avexpress\/i.test(location.hostname);}\nfunction avail(p){return (p.variants||[]).some(function(v){return v.available!==false;})?1:0;}\nfunction showDemoBanner(){\n  if(document.getElementById(\"avx-demo-b\"))return;\n  var b=document.createElement(\"div\");b.id=\"avx-demo-b\";b.className=\"avx-demo-banner\";\n  b.innerHTML=\"\u003cstrong\u003ePreview mode\u003c\/strong\u003e — sample products shown. On avexpress.net this pulls your live collections, prices, and stock automatically.\";\n  var head=document.querySelector(\"#avx-kb .avx-head\");head.parentNode.insertBefore(b,head.nextSibling);\n}\n\n\/* ---------- stepper ---------- *\/\nfunction stepHasItems(i){return state.items.some(function(it){return it.step===i;});}\nfunction renderStepper(){\n  var html=\"\";\n  CFG.steps.forEach(function(s,i){\n    var cls=\"avx-chip\"+(i===state.step?\" on\":\"\")+(stepHasItems(i)?\" done\":\"\");\n    var mark=stepHasItems(i)?\"✓\":(i+1);\n    html+=\"\u003cbutton type='button' class='\"+cls+\"' data-go='\"+i+\"'\u003e\u003cspan class='n'\u003e\"+mark+\"\u003c\/span\u003e\"+esc(s.title)+\"\u003c\/button\u003e\";\n  });\n  var rvOn=state.step===CFG.steps.length;\n  html+=\"\u003cbutton type='button' class='avx-chip\"+(rvOn?\" on\":\"\")+\"' data-go='\"+CFG.steps.length+\"'\u003e\u003cspan class='n'\u003e★\u003c\/span\u003eReview \u0026amp; Add to Cart\u003c\/button\u003e\";\n  $(\"avx-stepper\").innerHTML=html;\n  Array.prototype.forEach.call($(\"avx-stepper\").querySelectorAll(\"[data-go]\"),function(b){\n    b.onclick=function(){go(parseInt(b.getAttribute(\"data-go\"),10));};\n  });\n}\n\n\/* ---------- product grid ---------- *\/\nfunction renderStep(){\n  var i=state.step;\n  syncNavButtons();\n  if(i===CFG.steps.length){renderReview();return;}\n  var s=CFG.steps[i];\n  $(\"avx-step-count\").textContent=\"Step \"+(i+1)+\" of \"+CFG.steps.length+(s.required?\"\":\" · optional\");\n  $(\"avx-step-title\").textContent=s.title;\n  $(\"avx-step-blurb\").textContent=s.blurb||\"\";\n  var br=$(\"avx-browse\");\n  if(state.demo){br.style.display=\"none\";}else{br.style.display=\"\";br.href=\"\/collections\/\"+s.collections[0];}\n  $(\"avx-grid\").innerHTML=\"\u003cdiv class='avx-load'\u003e\u003cdiv class='avx-spin'\u003e\u003c\/div\u003eLoading products…\u003c\/div\u003e\";\n  loadStep(i,function(products){\n    if(state.step!==i)return;\n    if(!products.length){\n      $(\"avx-grid\").innerHTML=\"\u003cdiv class='avx-load'\u003eNo products found in this collection yet.\u003cbr\u003eCheck the collection handles in the KIT CONFIG at the top of the code.\u003c\/div\u003e\";\n      return;\n    }\n    $(\"avx-grid\").innerHTML=products.map(function(p,pi){return cardHTML(p,pi,i);}).join(\"\");\n    products.forEach(function(p,pi){bindCard(p,pi);});\n    markInBuild();\n  });\n}\nfunction firstAvail(p){for(var k=0;k\u003cp.variants.length;k++){if(p.variants[k].available!==false)return k;}return 0;}\nfunction cardHTML(p,pi,stepIdx){\n  var vs=p.variants||[],multi=vs.length\u003e1,vi=firstAvail(p);\n  var v=vs[vi]||{},out=!avail(p),sp=specs(p,stepIdx);\n  var img=(p.images\u0026\u0026p.images[0]\u0026\u0026p.images[0].src)||(p.featured_image\u0026\u0026p.featured_image.src)||ph(\"NO IMAGE\");\n  var cmp=v.compare_at_price\u0026\u0026num(v.compare_at_price)\u003enum(v.price)?\"\u003cspan class='avx-was'\u003e\"+money(num(v.compare_at_price))+\"\u003c\/span\u003e\":\"\";\n  var sel=\"\";\n  if(multi){\n    sel=\"\u003cselect class='avx-vsel' data-p='\"+pi+\"'\u003e\"+vs.map(function(vv,k){\n      var dis=vv.available===false?\" disabled\":\"\",so=vv.available===false?\" (Sold out)\":\"\";\n      return \"\u003coption value='\"+k+\"'\"+(k===vi?\" selected\":\"\")+dis+\"\u003e\"+esc(vv.title)+\" — \"+money(num(vv.price))+so+\"\u003c\/option\u003e\";\n    }).join(\"\")+\"\u003c\/select\u003e\";\n  }\n  var link=state.demo?\"#\":\"\/products\/\"+p.handle;\n  return \"\u003cdiv class='avx-card' id='avx-c\"+pi+\"'\u003e\"+\n    (out?\"\u003cspan class='avx-soldout'\u003eSold out\u003c\/span\u003e\":\"\")+\n    \"\u003cimg class='avx-img' loading='lazy' src='\"+esc(img)+\"' alt='\"+esc(p.title)+\"'\u003e\"+\n    \"\u003cdiv class='avx-vendor'\u003e\"+esc(p.vendor||\"\")+\"\u003c\/div\u003e\"+\n    \"\u003ch4 class='avx-pname'\u003e\u003ca href='\"+esc(link)+\"' target='_blank' rel='noopener'\u003e\"+esc(p.title)+\"\u003c\/a\u003e\u003c\/h4\u003e\"+\n    \"\u003cdiv class='avx-specs'\u003e\"+specChips(sp)+\"\u003c\/div\u003e\"+\n    \"\u003cdiv class='avx-price' id='avx-pr\"+pi+\"'\u003e\"+money(num(v.price))+cmp+\"\u003c\/div\u003e\"+sel+\n    \"\u003cdiv class='avx-rowqty'\u003e\"+\n      \"\u003cspan class='avx-qty'\u003e\u003cbutton type='button' data-d='-1'\u003e−\u003c\/button\u003e\u003cinput type='number' min='1' value='1' id='avx-q\"+pi+\"'\u003e\u003cbutton type='button' data-d='1'\u003e+\u003c\/button\u003e\u003c\/span\u003e\"+\n      \"\u003cbutton type='button' class='avx-add' id='avx-a\"+pi+\"'\"+(out?\" disabled\":\"\")+\"\u003e\"+(out?\"Unavailable\":\"Add to build\")+\"\u003c\/button\u003e\"+\n    \"\u003c\/div\u003e\u003c\/div\u003e\";\n}\nfunction bindCard(p,pi){\n  var card=$(\"avx-c\"+pi);if(!card)return;\n  var selEl=card.querySelector(\".avx-vsel\");\n  function curVar(){var k=selEl?parseInt(selEl.value,10):firstAvail(p);return p.variants[k];}\n  if(selEl){selEl.onchange=function(){var v=curVar();\n    var cmp=v.compare_at_price\u0026\u0026num(v.compare_at_price)\u003enum(v.price)?\"\u003cspan class='avx-was'\u003e\"+money(num(v.compare_at_price))+\"\u003c\/span\u003e\":\"\";\n    $(\"avx-pr\"+pi).innerHTML=money(num(v.price))+cmp;\n    var a=$(\"avx-a\"+pi);a.disabled=v.available===false;a.textContent=v.available===false?\"Unavailable\":\"Add to build\";\n  };}\n  Array.prototype.forEach.call(card.querySelectorAll(\".avx-qty button\"),function(b){\n    b.onclick=function(){var q=$(\"avx-q\"+pi);q.value=Math.max(1,(parseInt(q.value,10)||1)+parseInt(b.getAttribute(\"data-d\"),10));};\n  });\n  $(\"avx-a\"+pi).onclick=function(){\n    var v=curVar();if(!v||v.available===false)return;\n    var qty=Math.max(1,parseInt($(\"avx-q\"+pi).value,10)||1);\n    var sp=specs(p,state.step);\n    addItem({vid:v.id,pid:p.id,title:p.title,variant:v.title===\"Default Title\"?\"\":v.title,\n      price:num(v.price),img:(p.images\u0026\u0026p.images[0]\u0026\u0026p.images[0].src)||ph(\"ITEM\"),handle:p.handle,step:state.step,\n      kind:sp.kind,ru:sp.ru,ports:sp.ports,poe:sp.poe,budget:sp.budget,draw:sp.draw,drawEst:sp.drawEst,needsPort:sp.needsPort},qty);\n    var a=$(\"avx-a\"+pi),old=a.textContent;a.textContent=\"Added ✓\";a.classList.add(\"ok\");\n    setTimeout(function(){a.textContent=old;a.classList.remove(\"ok\");},1200);\n    markInBuild();\n  };\n}\nfunction markInBuild(){\n  var i=state.step;if(i\u003e=CFG.steps.length)return;\n  var prods=state.cache[i]||[];\n  prods.forEach(function(p,pi){\n    var card=$(\"avx-c\"+pi);if(!card)return;\n    var inb=state.items.some(function(it){return it.pid===p.id;});\n    card.classList.toggle(\"in\",inb);\n    var b=card.querySelector(\".avx-inbadge\");\n    if(inb\u0026\u0026!b){b=document.createElement(\"span\");b.className=\"avx-inbadge\";b.textContent=\"In build\";card.appendChild(b);}\n    if(!inb\u0026\u0026b)b.remove();\n  });\n}\n\n\/* ---------- build state ---------- *\/\nfunction addItem(item,qty){\n  for(var k=0;k\u003cstate.items.length;k++){\n    if(state.items[k].vid===item.vid){state.items[k].qty+=qty;refresh();return;}\n  }\n  item.qty=qty;state.items.push(item);refresh();\n}\nfunction setQty(idx,d){\n  var it=state.items[idx];if(!it)return;\n  it.qty+=d;\n  if(it.qty\u003c1)state.items.splice(idx,1);\n  refresh();markInBuild();\n  if(state.step===CFG.steps.length)renderReview();\n}\nfunction removeItem(idx){\n  state.items.splice(idx,1);\n  refresh();markInBuild();\n  if(state.step===CFG.steps.length)renderReview();\n}\nfunction totals(){var t=0,c=0;state.items.forEach(function(it){t+=it.price*it.qty;c+=it.qty;});return {t:t,c:c};}\nfunction refresh(){renderSummary();renderStepper();}\n\n\/* ---------- compatibility checks ---------- *\/\nfunction sysCheck(){\n  var cap=0,used=0,budget=0,bUnknown=false,draw=0,est=false,portsA=0,portsN=0,\n      hasSwitch=false,hasPoE=false,hasRack=false,hasPower=false;\n  state.items.forEach(function(it){\n    var q=it.qty;\n    if(it.kind===\"rack\"){hasRack=true;cap+=it.ru*q;}\n    else used+=it.ru*q;\n    if(it.kind===\"switch\"){hasSwitch=true;portsA+=it.ports*q;\n      if(it.poe){hasPoE=true;if(it.budget)budget+=it.budget*q;else bUnknown=true;}}\n    if(it.needsPort)portsN+=q;\n    if(it.draw){draw+=it.draw*q;if(it.drawEst)est=true;}\n    if(it.kind===\"power\")hasPower=true;\n  });\n  var cks=[];\n  function meter(pct,cls){return \"\u003cdiv class='avx-meter \"+cls+\"'\u003e\u003cb style='width:\"+Math.min(100,pct)+\"%'\u003e\u003c\/b\u003e\u003c\/div\u003e\";}\n  if(!hasRack){\n    cks.push([\"warn\",\"Pick a rack first — everything mounts into it.\"]);\n  }else if(used\u003ecap){\n    cks.push([\"bad\",\"Rack is full: this build needs \"+used+\"U but the rack has \"+cap+\"U. Size up the rack or trim the build.\"]);\n  }else{\n    var pct=cap?Math.round(used\/cap*100):0;\n    cks.push([\"ok\",\"Rack space: \"+used+\"U of \"+cap+\"U used.\"+meter(pct,pct\u003e85?\"hot\":\"\")]);\n  }\n  if(portsN\u003e0\u0026\u0026!hasSwitch){\n    cks.push([\"warn\",portsN+\" device\"+(portsN\u003e1?\"s\":\"\")+\" will need a network port — add a switch.\"]);\n  }else if(hasSwitch\u0026\u0026portsN\u003e0){\n    var availP=Math.max(0,portsA-1);\n    if(portsN\u003eavailP)cks.push([\"bad\",\"Not enough switch ports: \"+portsN+\" needed, \"+availP+\" free (1 reserved for the internet uplink). Choose a bigger switch.\"]);\n    else cks.push([\"ok\",\"Switch ports: \"+portsN+\" of \"+availP+\" free ports used (1 reserved for uplink).\"]);\n  }\n  if(draw\u003e0){\n    var dTxt=(est?\"~\":\"\")+draw+\"W\";\n    if(!hasPoE)cks.push([\"bad\",\"Your WiFi\/camera gear is PoE-powered (\"+dTxt+\") — pick a PoE switch to power it.\"]);\n    else if(budget===0\u0026\u0026bUnknown)cks.push([\"warn\",\"PoE devices need \"+dTxt+\", but the switch's PoE budget isn't listed. Check its spec sheet before ordering.\"]);\n    else{\n      var pct2=budget?Math.round(draw\/budget*100):100;\n      if(draw\u003ebudget)cks.push([\"bad\",\"PoE over budget: devices need \"+dTxt+\" but the switch supplies \"+budget+\"W. Pick a higher-wattage PoE switch.\"+meter(pct2,\"full\")]);\n      else if(draw\u003ebudget*0.8)cks.push([\"warn\",\"PoE power is cutting it close: \"+dTxt+\" of \"+budget+\"W. Leave ~20% headroom for cold boots.\"+meter(pct2,\"hot\")]);\n      else cks.push([\"ok\",\"PoE power: \"+dTxt+\" of \"+budget+\"W budget — plenty of headroom.\"+meter(pct2,\"\")]);\n    }\n  }\n  if(state.items.length\u0026\u0026!hasPower)cks.push([\"tip\",\"Pro tip: add surge protection or a UPS — it's the cheapest insurance for the whole rack.\"]);\n  return cks;\n}\nfunction renderChecks(){\n  var cks=sysCheck(),el=$(\"avx-checks\");\n  if(!state.items.length){el.innerHTML=\"\";return;}\n  var ic={ok:\"✓\",warn:\"!\",bad:\"✕\",tip:\"i\"};\n  el.innerHTML=cks.map(function(c){\n    return \"\u003cdiv class='avx-ck \"+c[0]+\"'\u003e\u003cspan class='i'\u003e\"+ic[c[0]]+\"\u003c\/span\u003e\u003cspan style='flex:1;min-width:0'\u003e\"+c[1]+\"\u003c\/span\u003e\u003c\/div\u003e\";\n  }).join(\"\");\n}\n\n\/* ---------- virtual rack ---------- *\/\nfunction faceHTML(it){\n  var inner=\"\";\n  var t=it.title.toLowerCase();\n  if(it.kind===\"switch\"){\n    var n=Math.min(it.ports||8,16),pp=\"\";\n    for(var k=0;k\u003cn;k++)pp+=\"\u003ci\u003e\u003c\/i\u003e\";\n    inner=\"\u003cspan class='pr'\u003e\"+pp+\"\u003c\/span\u003e\u003cspan class='led'\u003e\u003c\/span\u003e\";\n  }else if(it.kind===\"power\"){\n    inner=\"\u003cspan class='out'\u003e\u003ci\u003e\u003c\/i\u003e\u003ci\u003e\u003c\/i\u003e\u003ci\u003e\u003c\/i\u003e\u003ci\u003e\u003c\/i\u003e\u003c\/span\u003e\";\n  }else if(\/patch panel|panel\/.test(t)\u0026\u0026it.kind===\"mount\"||it.kind===\"mount\"\u0026\u0026it.ports){\n    var n2=Math.min(it.ports||12,16),pp2=\"\";\n    for(var k2=0;k2\u003cn2;k2++)pp2+=\"\u003ci\u003e\u003c\/i\u003e\";\n    inner=\"\u003cspan class='pr'\u003e\"+pp2+\"\u003c\/span\u003e\";\n  }\n  return inner;\n}\nfunction uClass(it){\n  var t=it.title.toLowerCase();\n  if(it.kind===\"switch\")return \"k-switch\";\n  if(it.kind===\"power\")return \"k-power\";\n  if(\/blank|vent\/.test(t))return \"k-vent\";\n  if(\/patch panel|panel\/.test(t))return \"k-panel\";\n  return \"k-mount\";\n}\nfunction renderRack(){\n  var rackEl=$(\"avx-rack\"),foot=$(\"avx-rack-foot\"),chips=$(\"avx-chips\");\n  var cap=0,hasRack=false;\n  var mounts=[],others=[];\n  state.items.forEach(function(it){\n    if(it.kind===\"rack\"){hasRack=true;cap+=it.ru*it.qty;return;}\n    if(it.ru\u003e0)mounts.push(it);else others.push(it);\n  });\n  if(!hasRack\u0026\u0026!mounts.length){\n    rackEl.innerHTML=\"\u003cdiv class='avx-rack-empty'\u003eYour rack is empty.\u003cbr\u003ePick a rack to get started.\u003c\/div\u003e\";\n    foot.textContent=\"\";\n  }else{\n    var unit=cap\u003c=0?16:cap\u003c=9?20:cap\u003c=15?16:cap\u003c=24?12:8;\n    var html=\"\",usedU=0,blocks=0;\n    mounts.forEach(function(it){\n      for(var q=0;q\u003cit.qty\u0026\u0026blocks\u003c40;q++){\n        blocks++;\n        var over=hasRack\u0026\u0026(usedU+it.ru)\u003ecap;\n        usedU+=it.ru;\n        var lbl=esc(it.title.length\u003e30?it.title.slice(0,29)+\"…\":it.title);\n        html+=\"\u003cdiv class='avx-u \"+uClass(it)+(over?\" over\":\"\")+\"' style='height:\"+(it.ru*unit-2)+\"px'\u003e\"+\n          \"\u003cspan class='ul'\u003e\"+lbl+\"\u003c\/span\u003e\"+faceHTML(it)+\"\u003cspan class='uu'\u003e\"+it.ru+\"U\u003c\/span\u003e\u003c\/div\u003e\";\n      }\n    });\n    if(hasRack\u0026\u0026usedU\u003ccap){\n      var free=cap-usedU;\n      html+=\"\u003cdiv class='avx-u-free' style='height:\"+Math.max(18,free*unit-2)+\"px'\u003e\"+free+\"U FREE\u003c\/div\u003e\";\n    }\n    if(!hasRack)html+=\"\u003cdiv class='avx-rack-empty' style='padding:8px'\u003eNo rack selected yet!\u003c\/div\u003e\";\n    rackEl.innerHTML=html;\n    foot.textContent=hasRack?(usedU+\"U of \"+cap+\"U used\"+(usedU\u003ecap?\" — doesn't fit!\":\"\")):\"Pick a rack to size your build\";\n  }\n  var ch=\"\";\n  others.forEach(function(it){\n    var cls=it.needsPort?\" net\":\"\";\n    var w=it.draw?\" · \"+(it.drawEst?\"~\":\"\")+(it.draw*it.qty)+\"W\":\"\";\n    ch+=\"\u003cspan class='avx-chip2\"+cls+\"'\u003e\"+esc(it.title.length\u003e22?it.title.slice(0,21)+\"…\":it.title)+(it.qty\u003e1?\" ×\"+it.qty:\"\")+w+\"\u003c\/span\u003e\";\n  });\n  chips.innerHTML=ch?(\"\u003cspan class='avx-chip2' style='border-style:dashed'\u003eConnects to rack:\u003c\/span\u003e\"+ch):\"\";\n}\n\n\/* ---------- summary ---------- *\/\nfunction renderSummary(){\n  var tt=totals();\n  renderRack();renderChecks();\n  var lines=$(\"avx-lines\");\n  if(!state.items.length){\n    lines.innerHTML=\"\u003cdiv class='avx-empty-note'\u003eNo items yet — pick products on the left.\u003c\/div\u003e\";\n  }else{\n    lines.innerHTML=state.items.map(function(it,idx){\n      return \"\u003cdiv class='avx-line'\u003e\u003cimg src='\"+esc(it.img)+\"' alt=''\u003e\"+\n        \"\u003cdiv class='avx-line-info'\u003e\u003cdiv class='avx-line-name' title='\"+esc(it.title)+\"'\u003e\"+esc(it.title)+\"\u003c\/div\u003e\"+\n        (it.variant?\"\u003cdiv class='avx-line-var'\u003e\"+esc(it.variant)+\"\u003c\/div\u003e\":\"\")+\n        \"\u003cdiv class='avx-line-price'\u003e\"+money(it.price*it.qty)+\"\u003c\/div\u003e\u003c\/div\u003e\"+\n        \"\u003cspan class='avx-qty'\u003e\u003cbutton type='button' data-sq='\"+idx+\"' data-d='-1'\u003e−\u003c\/button\u003e\u003cinput type='number' value='\"+it.qty+\"' readonly\u003e\u003cbutton type='button' data-sq='\"+idx+\"' data-d='1'\u003e+\u003c\/button\u003e\u003c\/span\u003e\"+\n        \"\u003cbutton type='button' class='avx-rm' data-rm='\"+idx+\"' title='Remove'\u003e×\u003c\/button\u003e\u003c\/div\u003e\";\n    }).join(\"\");\n    Array.prototype.forEach.call(lines.querySelectorAll(\"[data-sq]\"),function(b){\n      b.onclick=function(){setQty(parseInt(b.getAttribute(\"data-sq\"),10),parseInt(b.getAttribute(\"data-d\"),10));};\n    });\n    Array.prototype.forEach.call(lines.querySelectorAll(\"[data-rm]\"),function(b){\n      b.onclick=function(){removeItem(parseInt(b.getAttribute(\"data-rm\"),10));};\n    });\n  }\n  $(\"avx-count\").textContent=tt.c+(tt.c===1?\" item\":\" items\");\n  $(\"avx-total\").textContent=money(tt.t);\n  $(\"avx-mcount\").textContent=tt.c+(tt.c===1?\" item\":\" items\");\n  $(\"avx-mtotal\").textContent=money(tt.t);\n  $(\"avx-addcart\").disabled=!state.items.length;\n  $(\"avx-mcartbtn\").disabled=!state.items.length;\n}\n\n\/* ---------- review ---------- *\/\nfunction renderReview(){\n  $(\"avx-step-count\").textContent=\"Final step\";\n  $(\"avx-step-title\").textContent=\"Review your kit\";\n  $(\"avx-step-blurb\").textContent=\"The compatibility check on the right has the final word on power, ports, and space. Happy with it? Add the whole kit to your cart in one click.\";\n  $(\"avx-browse\").style.display=\"none\";\n  var html=\"\",missing=[];\n  CFG.steps.forEach(function(s,i){\n    var its=state.items.filter(function(it){return it.step===i;});\n    if(!its.length\u0026\u0026s.required)missing.push(s.title);\n    html+=\"\u003cdiv class='avx-rev-group'\u003e\u003cdiv class='avx-rev-step'\u003e\"+(i+1)+\". \"+esc(s.title)+\"\u003c\/div\u003e\";\n    if(!its.length){html+=\"\u003cdiv class='avx-rev-skip'\u003eNothing selected — skipped\u003c\/div\u003e\";}\n    else{\n      its.forEach(function(it){\n        html+=\"\u003cdiv class='avx-rev-row'\u003e\u003cspan\u003e\u003cspan class='q'\u003e\"+it.qty+\" ×\u003c\/span\u003e \"+esc(it.title)+(it.variant?\" \u003cspan class='q'\u003e(\"+esc(it.variant)+\")\u003c\/span\u003e\":\"\")+\"\u003c\/span\u003e\u003cstrong\u003e\"+money(it.price*it.qty)+\"\u003c\/strong\u003e\u003c\/div\u003e\";\n      });\n    }\n    html+=\"\u003c\/div\u003e\";\n  });\n  var warn=missing.length?\"\u003cdiv class='avx-warn'\u003e⚠ Heads up — no \u003cstrong\u003e\"+esc(missing.join(\", \"))+\"\u003c\/strong\u003e selected. You can still add the kit to cart, or go back and pick one.\u003c\/div\u003e\":\"\";\n  var bad=sysCheck().filter(function(c){return c[0]===\"bad\";});\n  bad.forEach(function(c){warn+=\"\u003cdiv class='avx-warn'\u003e⚠ \"+c[1].replace(\/\u003c[^\u003e]*\u003e\/g,\"\")+\"\u003c\/div\u003e\";});\n  var tt=totals();\n  html+=\"\u003cdiv class='avx-rev-group'\u003e\u003cdiv class='avx-rev-row'\u003e\u003cstrong\u003eKit total (\"+tt.c+\" items)\u003c\/strong\u003e\u003cstrong style='font-size:18px'\u003e\"+money(tt.t)+\"\u003c\/strong\u003e\u003c\/div\u003e\u003c\/div\u003e\";\n  $(\"avx-grid\").innerHTML=warn+\"\u003cdiv style='grid-column:1\/-1'\u003e\"+html+\"\u003c\/div\u003e\";\n}\n\n\/* ---------- navigation ---------- *\/\nfunction syncNavButtons(){\n  var i=state.step,last=i===CFG.steps.length;\n  var nextTxt=last?\"Add Kit to Cart\":(i===CFG.steps.length-1?\"Review kit →\":\"Next step →\");\n  $(\"avx-back\").disabled=i===0;$(\"avx-sback\").disabled=i===0;\n  $(\"avx-next\").textContent=nextTxt;\n  $(\"avx-snext\").textContent=last?\"← Back to steps\":nextTxt;\n  $(\"avx-mnext\").textContent=last?\"← Steps\":(i===CFG.steps.length-1?\"Review →\":\"Next →\");\n}\nfunction nextAction(){if(state.step===CFG.steps.length){addKitToCart();}else{go(state.step+1);}}\nfunction go(i){\n  state.step=Math.max(0,Math.min(CFG.steps.length,i));\n  renderStepper();renderStep();\n  var top=document.getElementById(\"avx-kb\").getBoundingClientRect().top+window.pageYOffset-20;\n  if(window.pageYOffset\u003etop)window.scrollTo({top:top,behavior:\"smooth\"});\n}\n\n\/* ---------- add to cart ---------- *\/\nfunction addKitToCart(){\n  var msg=$(\"avx-msg\");msg.className=\"avx-msg\";\n  if(!state.items.length){msg.className=\"avx-msg err\";msg.textContent=\"Your build is empty — add some gear first.\";return;}\n  if(state.demo){msg.className=\"avx-msg ok\";msg.textContent=\"Preview mode — on avexpress.net this adds all \"+totals().c+\" items to the cart and opens it.\";return;}\n  var btn=$(\"avx-addcart\");btn.disabled=true;btn.textContent=\"Adding kit…\";\n  var payload={items:state.items.map(function(it){return {id:it.vid,quantity:it.qty};})};\n  fetch(\"\/cart\/add.js\",{method:\"POST\",headers:{\"Content-Type\":\"application\/json\",\"Accept\":\"application\/json\"},body:JSON.stringify(payload)})\n    .then(function(r){return r.json().then(function(j){return {ok:r.ok,j:j};});})\n    .then(function(res){\n      if(res.ok){\n        msg.className=\"avx-msg ok\";msg.textContent=\"Kit added! Opening your cart…\";\n        setTimeout(function(){window.location.href=\"\/cart\";},500);\n      }else{\n        btn.disabled=false;btn.textContent=\"Add Kit to Cart\";\n        msg.className=\"avx-msg err\";\n        msg.textContent=(res.j\u0026\u0026(res.j.description||res.j.message))||\"Couldn't add one of the items — it may have just sold out.\";\n      }\n    })\n    .catch(function(){\n      btn.disabled=false;btn.textContent=\"Add Kit to Cart\";\n      msg.className=\"avx-msg err\";msg.textContent=\"Network hiccup — please try again.\";\n    });\n}\n\n\/* ---------- init ---------- *\/\nfunction init(){\n  $(\"avx-main-title\").textContent=CFG.heading;\n  $(\"avx-main-sub\").textContent=CFG.sub;\n  if(!onStore()){state.demo=true;document.getElementById(\"avx-kb\").classList.add(\"avx-demo\");showDemoBanner();}\n  $(\"avx-back\").onclick=function(){go(state.step-1);};\n  $(\"avx-sback\").onclick=function(){go(state.step-1);};\n  $(\"avx-next\").onclick=nextAction;\n  $(\"avx-snext\").onclick=function(){if(state.step===CFG.steps.length){go(0);}else{nextAction();}};\n  $(\"avx-mnext\").onclick=function(){if(state.step===CFG.steps.length){go(0);}else{nextAction();}};\n  $(\"avx-addcart\").onclick=addKitToCart;\n  $(\"avx-mcartbtn\").onclick=function(){\n    if(state.step!==CFG.steps.length){go(CFG.steps.length);}\n    document.getElementById(\"avx-side\").scrollIntoView({behavior:\"smooth\"});\n    addKitToCart();\n  };\n  $(\"avx-minfo\").onclick=function(){document.getElementById(\"avx-side\").scrollIntoView({behavior:\"smooth\"});};\n  renderStepper();renderStep();renderSummary();\n}\nif(document.readyState===\"loading\"){document.addEventListener(\"DOMContentLoaded\",init);}else{init();}\n})();\n\u003c\/script\u003e","products":[{"product_id":"ubiquiti-access-point-u7-mesh","title":"Ubiquiti Access Point U7 Mesh","description":"\u003cp\u003e\u003cstrong\u003eUbiquiti Access Point U7 Mesh\u003c\/strong\u003e\u003cstrong\u003e\u003c\/strong\u003e\u003c\/p\u003e\n\u003ch2\u003eProduct Details\u003c\/h2\u003e\n\u003cp\u003e\u003cspan\u003eCompact indoor\/outdoor WiFi 7 AP with integrated long-range antenna system.\u003c\/span\u003e\u003c\/p\u003e\n\u003ch2\u003e\u003cspan\u003eSpecifications\u003c\/span\u003e\u003c\/h2\u003e\n\u003ctable data-path-to-node=\"1\" style=\"width: 100%; height: 666.188px;\"\u003e\n\u003ctbody\u003e\n\u003ctr style=\"height: 19.5938px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,1,0,0\"\u003e\u003cb data-path-to-node=\"1,1,0,0\" data-index-in-node=\"0\"\u003eMechanical\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,1,1,0\"\u003e\u003cb data-path-to-node=\"1,1,1,0\" data-index-in-node=\"0\"\u003eDimensions\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,1,2,0\"\u003e170 x 208 x 54.5 mm (6.7 x 8.2 x 2.1\")\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 19.5938px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 19.5938px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,2,1,0\"\u003e\u003cb data-path-to-node=\"1,2,1,0\" data-index-in-node=\"0\"\u003eWeight\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,2,2,0\"\u003e1.2 kg (2.6 lb)\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 19.5938px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 19.5938px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,3,1,0\"\u003e\u003cb data-path-to-node=\"1,3,1,0\" data-index-in-node=\"0\"\u003eEnclosure Material\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,3,2,0\"\u003eUV-stabilized polycarbonate, aluminum alloy\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 19.5938px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 19.5938px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,4,1,0\"\u003e\u003cb data-path-to-node=\"1,4,1,0\" data-index-in-node=\"0\"\u003eWeatherproofing\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,4,2,0\"\u003eIPX6\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 19.5938px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 19.5938px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,5,1,0\"\u003e\u003cb data-path-to-node=\"1,5,1,0\" data-index-in-node=\"0\"\u003eWind Survivability\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,5,2,0\"\u003e74.6 N at 200 km\/h (16.8 lbf at 125 mph)\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 19.5938px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,6,0,0\"\u003e\u003cb data-path-to-node=\"1,6,0,0\" data-index-in-node=\"0\"\u003eHardware\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,6,1,0\"\u003e\u003cb data-path-to-node=\"1,6,1,0\" data-index-in-node=\"0\"\u003eNetworking Interface\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,6,2,0\"\u003e(1) 1\/2.5 GbE RJ45 port\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 19.5938px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 19.5938px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,7,1,0\"\u003e\u003cb data-path-to-node=\"1,7,1,0\" data-index-in-node=\"0\"\u003ePower Method\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,7,2,0\"\u003ePoE+\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 39.1875px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 39.1875px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,8,1,0\"\u003e\u003cb data-path-to-node=\"1,8,1,0\" data-index-in-node=\"0\"\u003ePower Supply\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,8,2,0\"\u003eUniFi PoE Switch (Supported Voltage: 42.5–57V DC)\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 39.1875px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 39.1875px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,9,1,0\"\u003e\u003cb data-path-to-node=\"1,9,1,0\" data-index-in-node=\"0\"\u003eMax. Power Consumption\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,9,2,0\"\u003e19W\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 19.5938px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 19.5938px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,10,1,0\"\u003e\u003cb data-path-to-node=\"1,10,1,0\" data-index-in-node=\"0\"\u003eLEDs\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,10,2,0\"\u003e(1) Power: White\/Blue; (4) Mesh signal: Blue\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 19.5938px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 19.5938px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,11,1,0\"\u003e\u003cb data-path-to-node=\"1,11,1,0\" data-index-in-node=\"0\"\u003eMounting\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,11,2,0\"\u003eWall, Pole, Ceiling (Mounts included)\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 19.5938px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,12,0,0\"\u003e\u003cb data-path-to-node=\"1,12,0,0\" data-index-in-node=\"0\"\u003ePerformance\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,12,1,0\"\u003e\u003cb data-path-to-node=\"1,12,1,0\" data-index-in-node=\"0\"\u003eWiFi Standard\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,12,2,0\"\u003eWiFi 7 (802.11be)\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 19.5938px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 19.5938px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,13,1,0\"\u003e\u003cb data-path-to-node=\"1,13,1,0\" data-index-in-node=\"0\"\u003eSpatial Streams\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,13,2,0\"\u003e4\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 19.5938px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 19.5938px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,14,1,0\"\u003e\u003cb data-path-to-node=\"1,14,1,0\" data-index-in-node=\"0\"\u003eMIMO (2.4 \/ 5 GHz)\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,14,2,0\"\u003e2 x 2 \/ 2 x 2 (DL\/UL MU-MIMO)\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 39.1875px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 39.1875px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,15,1,0\"\u003e\u003cb data-path-to-node=\"1,15,1,0\" data-index-in-node=\"0\"\u003eMax. Data Rate (5 GHz)\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,15,2,0\"\u003e4.3 Gbps (BW240)\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 39.1875px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 39.1875px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,16,1,0\"\u003e\u003cb data-path-to-node=\"1,16,1,0\" data-index-in-node=\"0\"\u003eMax. Data Rate (2.4 GHz)\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,16,2,0\"\u003e688 Mbps (BW40)\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 19.5938px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 19.5938px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,17,1,0\"\u003e\u003cb data-path-to-node=\"1,17,1,0\" data-index-in-node=\"0\"\u003eMax. TX Power\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,17,2,0\"\u003e5 GHz: 26 dBm \/ 2.4 GHz: 23 dBm\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 39.1875px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 39.1875px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,18,1,0\"\u003e\u003cb data-path-to-node=\"1,18,1,0\" data-index-in-node=\"0\"\u003eCoverage Area\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,18,2,0\"\u003e465 m² (5,000 ft²) with Internal Directional Antenna\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 19.5938px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,19,0,0\"\u003e\u003cb data-path-to-node=\"1,19,0,0\" data-index-in-node=\"0\"\u003eAntennas\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,19,1,0\"\u003e\u003cb data-path-to-node=\"1,19,1,0\" data-index-in-node=\"0\"\u003eInternal Directional\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,19,2,0\"\u003e5 GHz: 12.5 dBi (45°) \/ 2.4 GHz: 8 dBi (90°)\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 19.5938px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 19.5938px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,20,1,0\"\u003e\u003cb data-path-to-node=\"1,20,1,0\" data-index-in-node=\"0\"\u003eExternal Omni\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,20,2,0\"\u003e5 GHz: 4 dBi \/ 2.4 GHz: 3 dBi (RP-SMA)\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 39.1875px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,21,0,0\"\u003e\u003cb data-path-to-node=\"1,21,0,0\" data-index-in-node=\"0\"\u003eSoftware\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,21,1,0\"\u003e\u003cb data-path-to-node=\"1,21,1,0\" data-index-in-node=\"0\"\u003eWireless Security\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,21,2,0\"\u003eWPA-PSK, WPA-Enterprise (WPA\/WPA2\/WPA3\/PPSK)\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 19.5938px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 19.5938px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,22,1,0\"\u003e\u003cb data-path-to-node=\"1,22,1,0\" data-index-in-node=\"0\"\u003eBSSID\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 19.5938px;\"\u003e\u003cspan data-path-to-node=\"1,22,2,0\"\u003e8 per radio\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 39.1875px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 39.1875px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,23,1,0\"\u003e\u003cb data-path-to-node=\"1,23,1,0\" data-index-in-node=\"0\"\u003eAdvanced Features\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,23,2,0\"\u003eMesh, Band Steering, 802.11r\/k\/v, Passpoint, VLAN\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 39.1875px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 39.1875px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,24,1,0\"\u003e\u003cb data-path-to-node=\"1,24,1,0\" data-index-in-node=\"0\"\u003eCaptive Portal\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,24,2,0\"\u003eCustom Branding, Vouchers, Payment, Guest Isolation\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003ctr style=\"height: 39.1875px;\"\u003e\n\u003ctd style=\"width: 15.0614%; height: 39.1875px;\"\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 27.673%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,25,1,0\"\u003e\u003cb data-path-to-node=\"1,25,1,0\" data-index-in-node=\"0\"\u003eApp Requirements\u003c\/b\u003e\u003c\/span\u003e\u003c\/td\u003e\n\u003ctd style=\"width: 53.3371%; height: 39.1875px;\"\u003e\u003cspan data-path-to-node=\"1,25,2,0\"\u003eUniFi Network: v8.3+; iOS: v10.20.2+; Android: v10.21.7+\u003c\/span\u003e\u003c\/td\u003e\n\u003c\/tr\u003e\n\u003c\/tbody\u003e\n\u003c\/table\u003e","brand":"Ubiquiti","offers":[{"title":"Default Title","offer_id":47028931231893,"sku":"U7-Mesh","price":219.0,"currency_code":"USD","in_stock":true}],"thumbnail_url":"\/\/cdn.shopify.com\/s\/files\/1\/0672\/4983\/2085\/files\/Screenshot2026-03-02143517.png?v=1772483733"}],"url":"https:\/\/www.avexpress.net\/collections\/ubiquiti-system-designer.oembed","provider":"AV Express","version":"1.0","type":"link"}