{"id":27,"date":"2026-07-09T11:39:50","date_gmt":"2026-07-09T11:39:50","guid":{"rendered":"https:\/\/downloads.meltxsoftware.com\/?page_id=27"},"modified":"2026-07-09T11:39:50","modified_gmt":"2026-07-09T11:39:50","slug":"27-2","status":"publish","type":"page","link":"https:\/\/downloads.meltxsoftware.com\/?page_id=27","title":{"rendered":""},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"en\" data-theme=\"light\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>New Suite ITAM \u2014 Audit Intelligence Console \u2014 MeltX Software Solutions<\/title>\n<link rel=\"icon\" href=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADRCAYAAACJgf3NAAB6S0lEQVR42u19d5xdVbX\/d+1z7p0+k0IaCUmAQCAhkQ5SHFR8omChDOpPxY4ICGJD0ecQO4pYUNRnA5SWoKAiolgydBGkh14inUDatFvO2ev3xzn3nF3PnRQEYe7n8YTM5M6de\/faa63v+q7vFxh\/jD\/GH+OP8cf4Y\/wx\/hh\/jD\/GH+OP8cf4Y\/wx\/hh\/jD\/GH+OP8cf4Y\/wx\/hh\/jD\/GH+OP8cf4Y\/wx\/hh\/jD\/GH+OP8cemPmj8LXjRfxY8\/taMB8hL9MGE\/lOpb8VCeuaZKYQD8q9MXbGKly24i7HkVAaIm35OzOg7cpl4ZsGU5DNbnj7P1A16nvHHeIC8wIGwYApNXXEAL1sKCdrAA9vPom\/FsuwzWbasT27ooe\/rWxo8s2AKYflyDBxwqsQSjAfOeID85wOiry+5zaeuWMXLlh0Zu76r99i\/d0Zh16QgjmfGQTwdEjNlHM8goi0AnsjgHpayE0AHgcsSKBNYgBnMDAbXAK4CNMrMwwI8yMxrwfI5SfQUMT1OFD\/FKD\/WMlJddc35h6xxB16\/6F1+gMizzRI5\/hmOB8jmfZ\/6mXqxXLgCom8pB49fffNcEmJ74toiBu0E8LaQcjYzpoggaKGgBUQEMIMJAEuwjMEswSwB5uTPmJO2g9NPhxkgyj4qImoED8AMGdUQx9WIGGsZ\/ChYPgLQnSRoRRzJFd2d1YcGzjpyyAzw3t7lwdSpq3hjstR4gIw\/AID6+paKZ56ZQgMDr470zHBnZy0YWSxIvJKI92TwzsQ8V5TaykKEyaGXEWRcB8sILGNmsASDkwBo9N6cvP9JcFDypTxAGkHABEYaOMkTpAGU\/K8AQRAJEAWAECAS4DhCHI2CmR8j0F0M\/ifJ+PqI5G23nXvk42ZZtrEl3XiAjAcFANCex9+wmOL4ABLi1SRojyAobxmW2wAGZFyFjGvgOJYMyKwvYaYsCMBJALBUAwRZUDCnZz4LhPTbmAEm9WuNoGoEEMDMnAYPZX+ZwByQCCGCECABGdch66ODYL4DQgxIiq9sibtvvvG8N64fD5bxAClojpMaXQ2K3v6\/h0Orwr0FizeD8T+CsLjU1p1USXEdkBEzkJRaLEUaCGTe\/tphdn0tCw4lKzTOePbfUEovbvxJHjDWn2Xfz0kMMjNLJhARUUBBCaAAMqqC49rjzDxAJC4NOP77zRe881k1WF7uPcvLO0D6lgZ9ANSeYucPDewTgN4KooODIFwQlNsBWQfLGggiIgFK6xnKb3vjcCrZgbSbHkp2UINHyxyN2z99JmZwGniuTJOVaGwHkx5YDICYJTOQNDvMAQUhiaAMyBhxXHmaWFzJkL+uDI\/+9d7ffXBQBSWWLTtS4mU2l3k5Bgj19S0Vagmx4N1Xzm4plY6AoLcLIfYIyx1gWQPiGkOIWBAJEoKECIiIlEwgtVveKHusm988vGbmYOUgO0qtPPjMr7me359V9OeSLJlYgplIBIEIWsAcQ0aVR5n5koDFr25d+u5\/alnlZVR+vYwChKmvD2LZMsqyxaKj\/vQqEZY\/APBbSy2d3eAYHFeZSMRCpEFBgkgQBIn83fIexjxgfA2387\/dwcJJD2OXaPbzq5kmRcj0niVNTEyuTMaNH8wMhpRgEAWhEEEZsj4KZl4uiX\/eOlz79c2XfXjk5VR+0csnYyRl1IK+peWgbcLhJMQxFISvCsIWcFwBwJEgIYQQgkRSQYmknkJSUanBoR\/Q\/JbPz4oG1xq3PbMnOBr\/bnwtzy5wZBVXsCpfb2Q6x8+xg099LsnMLAEKRNACEEHWRx8kQWeHFfnzW373wScAoLe3PxwYgARemoHykg6QpBxIAmPGIb9r32Jy6ztBdHxQalsMAByNMgkh08AgSgNCEKERJGpw5CCReXs7egjrQNvfq2cTrcxylkSu4GCr2S8quXzlGwDItEfJg53zgI3BDArCQIRlyNroaiacE5A88\/ZlRz\/8Ui69XpoB0rc0QPphzTvo8pbSlPB9gvhjYbljPmQElvWYhIAgCtL\/TTJGOkMgApI\/N8uqpj2BdgD1Bl7vWVywrZVJsu83s5BUYsaRNZwQMhf3SwwDaVN\/tmz0+ZLBUpAIKWxFXK+sI8Q\/r9drZ973+xMezjPKkvil0sy\/tAKkv19gxUJCo5x61+XvBIWfDkqti8ERIOsxZa1FEgCUZgvRyBZpSSUEOcqlLCUQFxxs62bP5nxsZQnr1tcDL8tG7syRZQlW+wstGLwomhVM+nM4y7fs7zODYwKFQdiKOBodZMnfr1fr337gihNX5ZeUm4IzHiAvwKO39+9hY44x\/\/9d9moS4alB2PIqggTHtUiQEBQIIbIySigBkZdVja8n51Iah5Upv2l9WSUd7CkHjbQD54CFnSWXcXC1W77R7+gZyv38+evTeptG8LCjD9J\/vv29WeBIBhCjESj10SeY5VfvabnqR1i2LEZfX4BlS\/+ry67\/\/gDpZ9FgrM5\/2yVzRdi6BCI4SgQhEFdjApEIRDa6EEmG4CSNNHoMAgniBmLlbmJ9aJKrZHE310pAcGPC7oRezdvbeftLK9PYsK8+JzFnKsm3S6P\/0UrCrGS0fw9WKjbJzByTCEIhyojrozcB8pR7fv+xK\/\/bs8l\/d4D09ocYWBIBoPnv+P0JoPALYbltEteHGUJIvccQaY+RN+BCzSDpnzlhXA8023zG4biN04NKDeqJN3P4ex29X\/D0M06kzZFpknKJYAUHN5+vZJml8fckMxCLoByCJTiOz2ZR+dy9vzv5CfT3CywB\/tvQrv\/OAOnvF40FoW37LtktLJW+E5Q69uOoAkDGCSQlSIi8r0iCQWjllPnvvsOVX9LcqJmIeSwzDgPF8tJQ1JscCbvXRKZcz+cEB1DQczSQqvTnwZ6024HvKcPSfyczeJL4lwymoNQmZFx9SiL67P2\/\/+TZJrI4HiDPS9b4e4ik16Dt3nbZKULQ\/4qwpYXj0UiIQAghBJCVUnCiVA0oN88gSunhzxwmX8oL43rmH44yzHF75wHByPue4udn5bksdEt7DhOG1pA1btKD+Ggv9vczMxMDkSARUlCCjCq\/jus46cE\/f\/LRtOT6r6Ct\/BcFCBP6lgksOzLe9vBlC4NS2w9Eub0X0QgAxEKIQBgoVNZfEHGSVBS0Kg0YFcY1a3BAGjMOR3C4SInW97ue34UuQSHjFs9b1K8zMxNAY+tfPGWYKwCU51AyR+MvEft6J\/W1smQGy6DUHsio+gw4PuG+P558UTqoCrBsWTweIJulEScJAPP6fvN+EbacIcJyD0ejkRAiSMqpLBiyTCEEMZEgH5TboFVZh6cIVnXNMfSaPYWB4W++jeGg8+d5skrzGUeTMq\/J17xBnw8NrfeJDCjY5KilC15JNqEQMq7+qC1e9cnbr\/zWsNJHjgfIppRUMw75cXt7+4zvheW2DyCugsBxyh6EIMFJHKiNeJpJyOxDFF6VfkC9MK59SByHUc067CmD7Cyj9AQ85uBwz1XgpJIYN3uapJpR7Zt9TWaZRi\/LPIPTbMYDyZAsSu0BR5V\/xVHtvQ\/95Qt3vJiDRLzYZxsYeHU057CLd+js2HKg1NLxAY5GYwIziSAQ1JhbJBmkERwinWc0GnMh0j9LMwuINFJg+iGTdrMrmSM\/L43vNQOncSiy4OD8vx3fnwdM2uxLPVMpgWUiW6zFlvH9VvkkzbInn\/Kn672svXa1vNMDUuOQZcEBLZNogc9pWaxnUQEgiGvDESjYlYLSNdsc+KV3YGBJhP5+AbWHGg+Q4szW17c0GBh4dTTv8N8cUiq3XCvC8u6yPpSUVCJFqESKQAm9r8hKqpxXxXlppQ7ZGA3GLFuH155p6CuyyvebEGr2nP6yhzVkrPH\/2FFyGYff2jvRmm8d7oUnCzEcnC0lsD3Bqpd4aWDnT6a+b+Qepib\/EBDKqBITy24Rls7f9nVf\/FrCCiZOAuXF8whelM14P2jFWcfLeX2XnkRh+RxB1AZZj4UIQqGWUem\/K5QRTjiHefYgITglreciCBZSVcSOtcskc1joKHnyZtczAGykqaS\/Vvlbemnnmrd4puOUrecazb2jXEyUIAzkKVnt1QNTKyG19wbOASPn9Rm5epa8RIVglpJlJEWp7VUT5u6\/aMZWO16+6pzTq+jrC7BixYsC4XpxpbT+ftHYL9jmyEvPLJU7jpf1USkIIBEIkQ71LJRKCBZEZGaQbBhoBIe\/aTVqdPY20J4a3Oof8n1ys08wJ93W4ZPwT9qdzX1GSWEnLGz2IM6deI2P5WQRZCWUA4jwDSM9vy\/nk8ooKLWW4nr1piAaPvSBgW889mLpS148AZLSEeb0\/qI1nDrpV2FL5+GyPhQJShhUGpFQKaGUIEng3JQuok3H81NFbJQJKgLjhnHTw+qiYzipJnA29jb06ppJ+CbzNpmwGe3FutnzRtkBHDR+tszLPTZmKs3mPebzO98Lc+FLHY7GkQhbQxlHj8ioeujKga\/e+mIIkhdHgKQw7uyDL5tY7pAXBy0dr5G1wSgpqZTdDBdtxOw7KB8Q5uuxRVwiz+TctfHHZs1vbPzZ1A4H90nhOSnDSf35ZQGhEQXsYZPQ6EHhbM6VdzbiKjmLMqA533GXh1bGTUtDGYmgHMo4fg5RdNgjV3\/lqhc6SOjFkjm2fvv50wLuujwote3K9aGI0n6DNLpIY7iXlE5qkORllhIcDnati02bLQc13dbzwZ\/Im9YiKon630oAueYGrt6m8AC6SkKLlgJr791L2\/c09w6qffE8JynqDIDAN70HmGVMIggArsgoPmzlVV\/9I3Y7uoSb\/6\/+8mvSG8HxpvOnBaXOPwWltl24PhwJEYYiRacyiJYEN0qtwCQeJv+uQL3I9yhcZYlVI8OQ4XEcVmedLTWelBV4UOgf7tKD7DIIPgTNIjBqMGxeorHStNsoE2ulZs7hcmalgqyqZh6oSBs7\/tvT3Oevi5WSTkDGEoQSCXF4z+y9bll3w5n3orc\/xMoB+fIJkDQ4tjn011OD1rY\/B2HrKzgejUQQhmTMLdKSKu9DhDAIhxmaxYkcTxYc7omwyXGCSd921dnWLciN2QkcEKzNs4ID7SkODi4IjgKmMUGjvrun4xlszcZh1kUhLC6Yr4zTCJV6lktbPcdg1QyOxt8nEEspiVACwiMmzNrz5nVXf+2+FyJIXpgSK+05tn7T+dOC9q4rglLLzgltJAi1niJDq4xpuNV3NJAq0pQLjQY6VwlxHVA2yyAvGqNDqUV74GPvZ\/Tb3NcfFcC4Y13I8vcXcPLD8jJLKpe\/9GYaHVVTy7iCHRpvSSslKBDMqMRx7bDHr\/3WH\/\/TPcl\/fijT3y+whOS8d17eHbR3XR6WWrPgyPsKYXCmhBYIOStX2QAknV6hzgk4H96xfwinfj+M\/W2t91B48fqGnavs0UsuK5OpWrsKm9iJHOmQsXJ4C4MD7Pw9lOLGeevrLAI1YPReykb+oGSy7GKCOgq1spqCuhlImWBZl4BsDUT4m5n7fuIADCyJ0NsfvkQDJDlc8w66vIWi2sVhuX1XGY3WG5kjb7hJGwAKoUK7CmVdqOux5sTXbC4bPELHxNkqIdSAkQrdHepuCGnTd3U6b\/GR8pVVtgiR2UFKz4qEax02AxpS+gizWePbzFzzvbAYt47fN2MCKK9bC1wtI0s\/jYZ1NrEVDNbOPkN5E5TPjQTLOAa4NRDiNzP3PekVGFgSoa8veKkFSEJXX7JEoqv6y6Cl53WyPlwXQpTSIOBGAAhlPdbgV3GDWpKVVRkKZRwYjUskjbpbD4C8BzF4UE0PV666bt\/G7sFZ3kcrr5Pt29n8PfLAdj+XWoZZk3xrDwXusodlnjuYyQIrtOfKl6vyXyEPVCVzUZZFzM\/GzoDO4SsBAcdRDNBEQeIPM\/Y9YTaWLYv\/E7SU\/1gP0hBV2PbI35xRap1wEteG6iRESTQGfCKlpft6DXV6nvYlmiROAWuVnZnBDZ36cH2y1lxZFS0kFKzA6j9LleyRBeCAf5GJC36PwiFe4bouHCWdOlgtGIYqr5PIpN34UDELFXQuj0Eb5DbmJLXbWqP4VQ\/c+L3BxhLkf3UGyYKj79fHlVt6TkqgXBEq2cEKDpHBt0KfmmvBoTBmdfYtWVCocgGbB0y9XUmprNUDwHqZxGAVgXHd1Mphblzq+v62khDyLGdO7sEeFRLtYMPuOZjzrOq62a29EKllCS042JNFrUyt9k+AljkcvYoxfKQsFSnvJWeXCDNAYVyvRCIov2I0EL8CiNG3VDyfF\/3zXsf19S0NLr\/8kHj7wy95bVBuO5+5LomQ0UcaGaSxQy6UNVlBBqQrBBORkfpRcBsaEKx+s6eoluMDcyl5qJdUzsMi9bn1TGBrW5nPZZQ9zE7tXO1wGigWYJeLbhqKVe\/nL4DsAaGhUA89MNkICms4mT+PsuMOBTKW+v2VRwVZwILeF4EECSmjughad+yavnPX4JWf\/tPzCf8+vwHS3y9WnHW83PGwi+egte0KQdRBkEhFFRQuVboRKIQWEMKYd1BmOeCeIGu4v+MGZu1AGGxbs7k2cH21R2k2vYaGhIE0Wor1HNnfJadZjn7Tk46a2XMSdgwH2QlaGGLW6qWjl6O5LhcKZIUcpakB41IT0yCCqgHmfG+z1yY4rsdUatmva8bODw9ed\/otzxcD+HkMECZMXSUWLOwrxaXWy8JS2w7MNRmIIBBKyaQ04rlWldKYa3OOMfKSYA3Jss+a9F0I943o3CU3G2Tv3AAuxirBeQPDmOQbAzM4ZxYaLGzOXNy3uhHkcMxwzB4hf+fIau7ZZhxAwxDYep98dg3WQlcTdnB+UXCqXEdv6J6552WDV579JPr7BQYGNmuQPG89SG\/v8gDLjoyjoPytUmv3K2U0EgkKAlLVRnTIloS6DajMQ8wZh\/mhGB+CMgSWjg9BqbPNjTst3Rv9iYf6bg\/VpFu0QZvPsI1kQVmiUgw9HQ052cEBBxuYHQ2xBgawtfCloWpSR9w0WFkqeVrtFzzCDR4BCja3Ns3ngmtGAmVGEoNItDHkhZP3+VRXoru1ebcSn58M0rc0WHn5IfH8t196RFjuOr0hrpD3HY1GnFQZ0DSDmGJuajxkpQq7ttaUVO3hEplIjJf3ZOyE2FQL1wScXVkmx0i1EsJg1+Z9Clw3scMpyuJhAc3LT9YHnq5ezlMGkRsmJhc507tEZbCXHX1G40exetB12koGijAAwTKKglLLVFEbmjn0xBmXoBebtR\/Z\/AHS3y9w1nG8oG+32SiVLyNCC0FSqlmlZIYG3ypooFQkhI5iOdCZRglCpsGMWkr56RLpyiezozQwbz4YG3YueBLG4pA5IbY5Uk6VQ7XZb25vQHpmhB3wOlLF5vOzRhgslgJCcwKjAXa4nh+Ji6\/5PrkROrKFuBk620C9UCA4rkcUtOzSMX3xw0PXf3uz9iObu8Si3uUHCIBYlsTPwnLbJHDEQgQiQapICYxc2VAIWw4U2mXjrFnJnsS62auWZI5G+zD1o6ANrWySohZMpJCr4NrdNnfZvYxWVmBkVznHrhVaOEQdWHs9dnA4Vo19vCoo5aprocx439gxczFU8RkOsW3lTaDmUknO\/itgWZcQwfem73LsnERra\/MMETdrgPT1LRUDA6+OdnjH748vtfYcKOsjURAEQToIzPsOla3b0LBK6OzIxaOlzpPiRNGSNWE1aVEuVJUQ1oh2pjKHo0lUaCXuqa\/avyg\/IS339ANd5HTrW0fN6eoWFQOqsIJVeli9TS4KAZ2awibtRfrZw8wGZV4qL88oDbMZhhoLUmV1kRoAmuuVg7VgitLpS1pSp\/AAxDJmoqCHqfrThLWxgl5cJVZ\/v1hx1nG88Ki9thWifDEjDgJCQCKgxNWMKJuO57R1Tv871XfziEfnZ8Kz4wCPTYBOoLOXnKQXCVOx+8ZqrvE10qDXIksDFOjlwvIMIc+CVKPXocK9jYwh4VsjVup4V0la6GoF\/abXdMR8ontQslixNR3ARdnFgUhqXxMso0gELfM6Z+z09NBfzrtxc5Ramy2D9K1YSAAxx\/yjoNzaSRyDUlfYHNLNM0iaNVKTzBStcvKYLP3XjNdmoCvkSMVunlU2oTVmA7ZEDeXiIy6SnbJ3ogOmWrNr61iZt70ZyHCRLckpDKcGZnavs8Jj0pEpVvsFZnsjcAwbiTr6xJoXomnh5tL7zZEw42vMFjOZnSWmcREppZaMq5IlfW3GbkfPxrJlclNLLbGZSqtg2bIj44XvvuzdYWv3gTKhrwf5jENoe+VqqUUNpAq+N4EVPVgNAwd8u9dGqaC96c5byaNHxVkgkYFKZbumGomPTUjYQSvxGXJqPYJSIrp4Tw4RBZc5aMZKgzq9Nl47\/OxbfemqwII6E3R3Ag2sB5N6Hyns5MLAdL8XbJNHk0WrIOyJ4sp3AfCmllqboU7rF+gHdnhov4khyTtFUJqWNOZCmAtNpj6u6QFYqCflkPZn1165xx+wWGhaZbQmIKqbFOjiRI2hqbQ+dDM4pG3m2XQY6hFPUP\/bQW50v3e+paaiteNmLAK\/X3tOgpYWjKswHTzPj6z\/hCdDMSMmEQbMtbc8c8u5v9sUkWyx6dljIWHJEhmgviRs7Z4OjuIEtRIgERjBIaBS2rMBoIa4GES1fFHAvsFMZMpz25jfnxMO7bKhkBToKAPM18bskgyVlk2zXUq4Mqd\/WOl4L9h6Lzy\/B7ufi62ST2HP66wDs\/8BPPYO6lRHa8qV4NAQN+fvBv21sgFz2++rJGbJiPn0WbP62rBsAW9sMtikJr2\/n8VZZ+3EOx31l8VhqfwjyFriHEuK1YDQN\/8aTbo1HXcKKyTNmK33BLi39pySmxqXiFUekncACO\/QS\/kXlYKR\/QwqHoQV3Iym2IHRuPp6AhUiZRflxMyi2QswRbNJf6\/UPqtoUAqHJZsWNSZ\/Ljfq0WdEBa5W9tfMLGmSSVnGsQjLU6IWOTzy9Pev3tiGfXP0IAyKvi5KLWWAcyFpZRPQhHYtCVCvv0ZBnc05XGgT\/9RsIMnZ25hi1PoNhOIlqpwBm9KxybzZ8+U4d39kzzjgX9JKNyFNNCn\/XaVyuxqvlQ1h7Qy+lXDYw2lAR5I9ZAOlcPdOlnOvBp6QG\/hgPTjYJlla\/u5sbobCdy4aME8g45oExMlb7PDOGRvbsIuNL62WBkuWkFz0visPLJU73sD1UcXERtfP1SRAzdSopHdzYxnw3OwOFRI25wpspn+YdJLGwTbW9cw9EUf\/YGY57TC6B2HsgIJtSRy1n7JWYQnmZp6Z1NjDxbIHgE69K\/31Kze7Ru9wuErBVqQ3LwqV72YOde35kEW5IecF6NIBQ2NdGMQyliRKPQjkFza2Yd\/4Jr2fRd+KZXRvx8R\/lFrad5NxLRaCgjwYdE+OohnHmJpGX0Pd3OYMtuGNSY7zfc2kUrB\/QOfy9PB6bHBO4UqpL+zyQ9RfX87B4mJGsUUMLNQFc+yVmFQZz0XhVStxHXaX4qOpNql6MzrXAhwsazZBDkvxngHUEPHiZ+++4AGgnzbESHSjMkhv799DLCF5X+ekN5daO3eTcTVOe48c0hWUkRHVGYft5gQbmjW23pjZkLSya3Sy9zDYRcFmVz2rTZcBS8DAibvDYtNaSiDsKdGyX11qg0CTMKi1Yo0NPwPaZKv00EtQey3ZzLiwty0d\/DRbm9ej9cvOnsBJfWdte9AwLmWfIgvYLA\/NnssohSWJsFVSvFFZZKOa9JXvnYv+A5bTvesf\/mUYlmcAzCLBdfW+gwJH5mgycXbyeIyJrcUqBeJYIhCkflCaYyv75W4UOR0P+9VsCptBmyrjy5oNqM+hsXsJhbbQ\/umy8b4pu93+yb2lU1zomqUwqX3LUerPSxnYURxDCCpsvm34HU72tPKbkDPgrPdFnbhHDBI7tk1Z+OvR5UtXAX0BMLaGXWx478EBliyRv\/\/3X99Sau3aTcY1mWWPfCjImgegb7ClD+jYHrRZrFG9oc2sjCX3dISoVCMIMvax9VuQ\/VNZGxzQlTzMIIdJNWedtc2p7TI7+WJsqpoY1HNrxqFbGLgzmQUJmwRJdUAoPaggp8LaBgHTqULi2OknQMYSsYzR09WGOI5zWQUu8hZxHHY1k7BksM3EZthZVN9pZ2JmSSIok4xPTrLI89ikL1twalILCzpZ8xlXdjzyDUAbtswwBnXSyirPyiGsoMGy+tcCAtYP1+kDh2yLI187B6vWVBAE5G7iGA6DGH3FVYOKHMNKdlBToBD79NKjsW6rXxRKGiN7BuNEl9RsQXChdiYj1m3BQGxkcrthBpyW2Ayy+wep\/ZxE2JKxdv0wTjv5bThg7x0wODQCIQguizgj+zAbumYGGKDLyVrvk9usJ\/0ZAcd1ZqIjpyzom7chiJbYsOyxNMCSJXK3o5cfELa078VRJVn0yNi4mbUyGTZnrPnl6b9Yom4OU73PR98wa93kxiqXBE45aie843Vz8dy6CoLGh+LIEk6eF9z71OyZTmdbdcr15aC5sMqIZWYv7GttQ3qGl7p4gmNI5qSH2+8dmZihtQ3JpL9WaWc9JZ0KIsRxjOHRCr7X\/y686637oFKpZavSbAjRccrORg4iG++TfVGo5bJ5iZqXglG6E0sZkyi1RTI+AQCjd7l4XjJI8tNwYhC2gEjIzFE2dZnVJqjaEAs+xqxGnHMwQYtuy+S6I6AWJQf2yx\/eBe8\/ZB5Wr6sgUSQ1lAZdW3js4Gyxi1XqUA7Mfjep7bQqqonsPOzuHoHcG4AmpCHt92JMNtCwjYFgQdRs7agYjTbnl0EiCkZAHMeo1ur40Zffh\/f3vQpSNhPK1tjZpNpNFS1pMdu9IrNrs9IoAcEByxoD\/M7OeX1TMDAQjeX8jz1A+lksW3ZkvPsx18wXQekNXB9lIShQHWWhCCvk0JNsepuZAzr2ktHsHeUGjaOxRlKPJD7z7kX44Ju3w+r1FQgNJNAHjG6pUSazd2HHBFzfQJQmUqVSZixvEod2FjvLBVZ6LXVH3cwc8Dvpuvbg1YBgtgKJfCZDVvZlJiEIUZQEx4+\/8j70vXEPVGtRWlZB6xG018+Sjf6FdBp+RqNWyZuMgkGy8pmyhvI2ykspYwrKk4IwemcKx26+AOnFqSIN+feX2rrKIMQi2yFvTMel0TOM7TbTWRG2xKaT4u2EElPwVDI+\/a7F+MhhO2D1+gpS4XfSNgI1kQeDfarZz45V3sdX\/yplo0s82tir8O9HWPCzzu5lz7TeKLkYLj4XnCwEi9Vg\/OxAENfrEeI4xs++\/gEc9vrdEUUxwkA4UUC9zwLZpZrCB0suE2MvHeT\/HMwVZiPQk3kJQcYgjo\/GbkeXMDAQb6YAYRpYsiTa5\/3XdBHwLo6qmbYVCYJL3SPHndw3o\/HCDXqC1HcpXHsi2kwkn\/uIdK1XSsbH37EIn3znIqwbqiZ1NzmHXmTcvPp6rPVawT7qiKqi6Fr4clHxXTdjTgEHLAEKn4KjNdFWyyQ4oFRDSshFyPTQYpgZgSBUanWEAeFX3z4Gbz5wV0SxRBgakwN2VQCwwJBGAOjvk1ECQipZxcNcsPoxTbA+kHFdEoU7Thx6+jUJolUsgj2mAOntXx4AQNxGB5fberZkjuKGWaYJzxkfOqlLTk5jeshCyE9\/A9XmXToOHTKokQQhlowPH7ojTnnvzlg\/UmvIajgOnD2bYU0sTYNGCT5eUoYimSoknv4l\/3tUNDxt9j7pqLMDCChaHUaDqKiuevlmHGnmCAiVSg2t5ZDO\/+6xOHDfhUlwpJmDDeauNQeB82Jy9nm2tGlWmXDOlG7CWtCztgQRpIzfn8CymyGDHIADZPrN7yFBTBQwCTJlbF1wZKOEJTShLJi4P5uq7ObqrFFymU4d1EBWJOO9B8\/H5967CwaHa85mmW2eF2npnh2MWlMcAdLSk82hVfO1ul1vkypVZmLZTgat1z22odhiT859BpxmU8zsQqg0RgMCITBaqaG1tYTzv\/sR7Lf79qhrZZWy4qNpLbGjvJUF+z0wOF1wnBkbvdKXuGBcyJIBDjiuA6CDpsx543RgWVwUB80DpL9fLFlCcv\/jB7YmER7AUQVCIMh9xe2hlxIsxDbDs5DGrLN7od2gXHTbuNA2AgJBiGLGew6ej1M\/tAdGKnVImWL2ro1EVpUzYHGk1DlK1tBrDS\/sIZaJZLH9uxMB9SiCEMkqg7SWiVgrPfx7IlLb7bAPENtggkXhkc6tvTAQGBmtoLuzFcu+\/1Hss+t2iGKJUhgUkr2hvU+cc1Ntbh5bgWNfFJxr9Uq2BrnapSYNxftEUog5jkkE3dUSHdqsWRfNm\/MDRNLetLyl3NrdysyxsiNAJoeKbQYpw7xCHXRum\/hnqunBuV9h70zYjzBIguT\/vX47fPOEV6JWjxDHMkG4XDMHi3oOhe9lNpzO124iD8RqqWVkTCEI1VodLeUQnznmYHr3W\/fBaKUGZqmUhFwg0mBtKxLDOe033ibb2Ic1pC3HTcKAMDg8immTu3Hpj07EHou31soqX2zY9g5Q91fYKEfJBUjo9tlMmsi4Wm3AvsSc5Wp+gR8JABg4QG50gAxgeQo5iMOZY5AiHsDKL+huaHOyH6uHxFiPZQ+NWd+Sc92WcBAAi4PkTftvjTNO2g\/1KEYUSwgyp8om1V5adHp2WjGrvHrndJ5cDbCgpNntaGvBp49+IxbMm4mDehfjA2\/rRaVaT8o1RV7SxR42zXXMPXn\/diMK6v\/8fQ4DgfVDo5g1bSJ+86MTsHD7Wc2DI81EzG5qkTod11yxbJEJz2AV6pq2TmOxmnWrVxQs6wzmfbq2fsP2CbvXPVkXzcorLFkie0+6eR4JsWdcH2FAClNpI+chSPfSkkYccxABjYbZKsPYpVZeXF75g0TioFfOwRkn7Y8ojlGPYgQCnoUsleFqZw629lbSlVH7wJH5WpPZDWG0UkNPZys+d+ybsOO8LbF+uIKhkSoOec0uOO7dB6JWiyDjODHvZYderjVPguP9tSguzobWVgyRCEPCusFhzNlyMi7+4Uex3dzpYwwOF+nQvlgMcIJQZDhqriIza7si9qAUvjkaMXNMIiyD5BuSUmn5hgdIo7ziOD44aOkos5RxjrjodTA7xMrgJP1pSJd7YJhnIbLqbueSjxxzoISBQBQzDnrlHPzg0wek5U0EIVz6tGoQSv13UVfbjM1Ee4daHWwm3xMIwmiliskTO\/DZYw7BNltNwdBwFWGQ8NnWD43itfvuhOPf83rEcZyQ\/og0qrjSH2nvk2704xjywe1vyFbmCLB23Qh22HYGfvOjEzFvzjTETYJDB\/RYH\/gpdavpxWjQXBwESbgGgk6IXeFrsVOdPwsSCTDeXFRmibGgVzHjTSwjfSrsOkwuCrIlY1nQS7CLjm3wjhz9yYYuGjfKrVfvvhX+73MHorUcoFKN8kzCDoTER9NP\/o\/gM5mxECGGCAij1SomT+zEZ445BHO22gJDI1UEysELhMD6wVG8+pULcfx7DkIcx2kmgee2dKq8k3vIB5+\/YfbaS6HAmnVD2HnBbPzmhydi7qwtkpWCJpmDyJVDFDi7cTCV10qAru4EF4uajQ0HlUPtchPL\/BHtPZTkWwVkDID3bpv1mpnpEpXYgABhWrKE5F4nXD8NxHvE9QoILFzYNTvStN6TGmWGY1fb04OkN7f0vmkWNLmB5dZeO03HTz7\/OrS3hBitRggC0iFrx+FihwqLe69cu5WZGUlwjFYxeUInPvPhQ7DVjEkYNoIjC5JAYN3QCHr3XoiPvvcNaSaREA0jQEbhjIPhyOgmDQWusirAc2sHsduiubj4Bx\/F9Ck9iGXz4HAFhiphyl7JV01Qjl37HtnGoYK86fZ3MpEgNVck4NI1Tk1fOY5JhO1hKF6T1kxjD5DGcDCIsV9QauuWcRTnbYJjSunfEbbTudMMExrvidkQmnYxZl2C1BsUJAJxzNh1x2n4af\/r0dVewvBoDUFQtB3IHh6UqY8rzdVfCgTxyEgFUyd345SPvAmztkyDQwgXY0bLJL17L8THPvgmSClRj6LUiY5dawHGXArOSbu2cah8bqUwwHOrB7H3ztti6ZnHY\/LEziQ4xMbKF7gZEG6huIxqz+6BJ+ASA4Rjx56hawuzvYpN+YYZvS75w6m8wSgWEb82YekmSAzBt2ZplhbSMR9x0899E1V7V1xqgycvL2tDVioDQhwzdpk\/FWcveSOmTmzH0EitQZdnv586HBwtp0p8UlYJgZHRCs2cNhGf\/cgh2HLaRIyM6pkjgbrI+RrXD45gvz12xCeOfjMCIVCv1yHIXESy5h3peqr0vNd6ExuGhFWr1+PA\/Rbgos0RHOlnSYYghaupNkpzKhJ1sCBqN3pq0eR1PhrAzELKGKB4XyzoK6dDQxpTgAwseXXU17c0AGFfGdcyOgKrEjKF8CGgil+yVwPXmI47cX8LriN95r5pVhBJkEgs3HYyzvnSwZg1tRODI1WESS2jTMelV4gOHp4VGBCCMDxSwewtJ+OU496MaVMmYKRSnDlMPY0gEFg\/OIJX7rYDPn3sYSiVAtTqKbjgs6DO1o5hbxgaN2sYCjy7ehCHvGZnnPftj2BCdzuk5E3IHHoNYZWo1mao+b6C2S0qzqpcKRT0VKfTexA6DdgBgWMGaG778NMLU+h2DAGSGrQ\/ucWMOWCaL+tVgFm45GT0JswlIZOZU7KPgp0jkxLs0YzKOfTa1zhDKjIb540NEoE4lth21gSc86VDsNW0LqwfriIMyHgNcAjXQafJGA350Mgotp09BZ\/9yJswqacDlUrNOniUZg8VNHe9xvWDI9h1p23w2eP70FIKUa3VlYGnzkg2YGhbWin9jjAQWPXcen7Ta3fBOacfjbbWMqTkjLK+gRGhfTxupjEcJakD5LAg7ZzrptE2NJaDy8cFNoEx\/7sxKBDEYp\/keXW41xkgiQkOEAfBbqLU1iI5TlOPS42ieD9BUZdQVya5qam9W5CM9BIscW1iZpRLAqr4yUYHiWTMmdGDc7\/8Zmw7awLWDVYSWJOVTTf3EI7Nm1kIwtDQKLadPRUnf\/hg9HS3Y7RaMw4eZf\/Y+phkZZUgHdot3nEuPnN8H1rLIWq1GkRjTpJTO8g4XGSyZBvB8czq9TjiDXvQ2d\/4EEqlcOODo0HmSKkzpTDQ3Xez98muNhzif6SWhu7DrlYfil+I8hmZ+zqGrjJl2Yh5H1cfIjz4buMJ9qYsh\/u8GvwixaZlQO65wmTCea4a2TaFVLHf\/OYIBPDIE4OZgsomBUnKAp49vRu\/+upbsWi7qVi7fhRBQKbBuJk5tKlwEBCGRiqYN3caTv7wwejqbEO1WneWLOy\/iJUAyv80DAQGG0Hy0SPR2lpOMkk6zLEYrebnktFHBFatXo\/3Hb4\/fvb1D6JUDrPA3tiHZEYQBIhjiceefDYNEolcucElZerJKhZ1RPvdSDlQyq4IHM07\/Gc04WYBwC6J2oneh4hCeonErhxHGemQffg0O9TF2ampRJY6oTEdtwZc9hujDdziWKK7o4yf\/vZO\/PTSuxAGyS7I5giSaZM7cO6X34JdF0zHmvWjFAaCXEGby\/gja8gHB0cxf+vp+MxHDkmCQ9myMzMEGUFCTQMlyXaDQyNYvOPW+PyJ70B7WxmVSjX5Gbb2lGURFwSEVc+tx4f\/32vw\/SVHJfI86XR\/o4NDMogIsYzx3o9\/D9f+8250drQgjqVzaAx35vC6ZlnNtwupMjOLkt3ZSZqFgJQA87Y9s5+abX4EgWv+gYFX854fvaFbIP4SwB1phNl6RBraJPMKgYsHgCahkex6nnw6WRoDVWnQw4Dw1xsfRbkksOfC6ZAxp9KnG\/dhi3Tpqr21hNfvMw\/\/vPNxPPjoanS0lVhK6TDdTF5NKAiDQ6N4xY5b4ZMfeiPaW1tQrUcpKuY+7OqfcOF36eEjRIBKtYaZ07fAoh3m4qbb7sP6oRG0lEJIKRWnWNYGmYEQWLV6EMe+67U4\/ZR3ZOzmDQkOsxxslGW1Wh1Hfew7uPiya3lCTwfFsYRf6Frtj9BYMXDs03jMT1Gg+6WfQ7IWIvS2QBKJUhTHf40GH70P6BMN3Sxh9+fJ7x3G0TYATZFx3aHMId1ecprPH2yKgFofOuBirz+gi4NleAsSgO6OMr5xzj9x1rJbEQQEqenzbkSQpJlkYncrzvnyodhv19lYvW6EEqqFjdMHKUVkl4Vz8IkPvRFtLWXUjODgMZRVZH\/UjmyS\/EkQCAwNj2K7bWfhCx9\/F6ZM6sZIpYJA6NmOGVkQPLdmEJ\/4wEH45mffjjgNDtrAm8QZHPUIR33sO\/j15ddjyqRuiuMYzY1+FAUVVgM5AxXIpBjpXpEe9RfXrI1dckUgcLJERUSLkt\/oGX+JtbzRxXM8Pyi1NTo\/0ssgByXEcoWVDjlR5a51DRw1KNijRwUH0Y0BycnEfUJXC7557k04a+mtCERabvGmlVtSMro7W3D2lw7FQfvOw3PrhlPtrfx1BkJg\/dAIdl+8NT7xwTegFDRgWDJwKvIcevXPyHkQfQ18EAQYGhrF3NnT0f+J92DK5B6MVEYRBiJ7n4iYCYQ164bw+Y++GV\/8+OHJwSba4OBQX3cjOKq1Oo468du45I83YOqkbtSjulEVwBoY65W0mwVepDZpDkettW+P45hO4Gz4iTAYvNhs1EUBUXknEGWAobVVZ\/wQJyFR89vKG3Q41m+NjMAMh2mL9UbrG3rMDCklejrL+MY5N+I7592cUkc2PZNIyWhtCfGjL7wZh\/TOx7OrhxEGlAmmrV0\/hN0XbYOPve\/1EEKgHsVGcBQXWGz0JGPPOJRlkuHhCrbacgr6P\/EeTJ00IRFtSwOAAFq9bhBLPnYYTv7wIYhjuUklKAGQUkKIhJH8juO+hUuuuAFTJnWhHkV6g62RWmGiT\/CJf3OhlJGqWgIv+VVdhGFzmS2fC1E6Ytg++YbcjcrqQfacejCtWLGMt9rjfcdQUF4oo6qExsGCB3VQ1ltU4QO9ViRm3TrYpghkYKFj\/8Gpl8Ra0KVB2tYS4u83\/5ur9Zj232VW+jo24UCkPUkYCBy033Z4+PE1uHnFY+jqaEG1Vse+u26H4446EGEQIIoamrT5ZJwLm3E1ZKiwT2HtO\/S\/IwShWq9ji0k92HnRPNxz\/0qsXT+IQBDWrBvGlz9+BD72\/oMSPlcgsvei8dZtyHvTIC6uWz+Ctx93Oq4YuBlbTOxCPYrdivdw3Ppaf+HtW1lnK5tIlpmZYAWTufLtMERKcXYudUya9JPquqcrjTfVCpAVK5YxAMza472fIRJbsqwzwMK96WVmFbZMWNRdEG0TzBkcUoXfHH6F5pwEeVPn2Bdobwlp4OZ\/Y2i4igN2n5N9HpsSJMzJbf3G\/efjqVWD+Mft\/0ZHWwnvPXx\/zNlyCwyOVBAGQVZOwXHsUTjrQCHSRUaTzFqwULZjsvXsGVi3fgj\/vGUFoljyaSe\/jY5794FJcAihvQeEDQyOlLi4Zt0Q+o45DX+77nZMmdSdZQ7z0mKvcLUdHPZeuV1qEQyYV5UHKkLCUOQbySDmVq6Xzq8PP\/ZMWl1xYCFYWILXfeJPHbVaeAqIuyAjYgd2bVGkXZRrt4GlQkGWRpaA+lzkrCv1FVJSFFOsrMTM6GgL+ZpbHqPV60bx2r3mpml\/04OECDhov+2xdnAUV9\/0MFY88Ci2mT0VW82YjEq1rgin2Yfbzh7NC5pGuPmeh7LDy5jQ04nfXLYcPzv\/D2AGvtv\/LnrfEa\/KFp3clPSxvZIGN+u5NYM49ENf4+tvugdbTOyiehQZhEMjONx9q90TuCwNnHMMtWHz+N6bdChPn5LQ4qUEBYGk+u\/ioScebCBZeg+SIlhrRsQ0hpwEGesDMc9++JjcY0341+NTzgbLlOERelCWqrT5ivEmRLGkyT2tOOd3t+Pz3x\/ID+4mNCWNplZKxpdPeD2Ofcc+eOSx53DGT\/6AO+99FF2dbYildJZH+b\/rJRQRKS+fnQfYFRxZAHLSE\/R0deDSP16Fs35xCepRhB9++b1451v2QRQVLzrRWMsqIbB67SAOO\/rruPFf99GkiZ1UyzKHwcBiB7W+mb2ecsHaapbQqg02kEwY5b+lw8UupCt\/zqQUobkqkqVlkL6pC8WKFct49m7v2S4Iyh+Rsq5y2smZOdjjMe6CZqEbObpx7SauUiaWDtmwarJs2xr\/LZnR0VbC9bc9hmfXjuC1e21tHfaNDRJm4DV7bYt6LHHF1XfzbXc\/QrNmTMI2s6ehUqknusUFAz+1NAiCAIIIQRjk2rZN2vv8UgI6Otpw0SV\/wY\/P\/S2CIMBPvvoBHPGGPRBFMYIgKMyazTJIo+d45rl1OPzor+PGW+\/DxAmdHEURkQNdgktHeaxlT4FElGW9pxqDcpF7GMhJb8p3RCQoEJDy1nj48b8DcwWwUnr2cMUMiCAFsxqfisPw0hrKwFbEc\/Jt7OBgw1OQPW+ubguQS1jq\/BvzTZAcRTFP7mnFOb+9FZ8+48rssMlNzCSN5v1\/jzkQXzj2f2jVc+vxrf\/7Pa696V50d7UjjqVjfmFPoBuKhKf\/cBku+u1ydLa3ae8JGQWXGlgMQkd7G8696I\/48bm\/RUd7K849\/WgcdtDuiOIYYRg0LSmpAHpuBMdTq9bire\/\/Km689X5M7OlEVI90aSSYUkeq1Ctc5TS7zD+Lg8OiqOhzOgLcJkPskEpyYLfEM1WoV8sgU\/qODVYOnCNn7P7uA4Kw5ZA4qjIBwlhm0VNZwxsi\/+2JrY0weJEo+5fQWMGs9Szu74EfGcu+jwAmKSU62su48Y7H8dBjq\/H6fechEAKSeRMyCdJAk9hnl7no6mzlK666m2656yFMnzIB87ediUq1ntnQkRUcEuVSCVIyvvWjZbjuprtw932PgAHs\/or5qNXr2Q+yoOGUGtLW3opzLvgDzl16BSZP6MIvzzgGB+63U9pzbLiJmPpzojhGGARY+dgqvOX9X8Fd9\/4bE3o6UK9H1ueqeSlqPaTT6CcHYXwzDs6bfXZCwYp+GXsY5joowPowUi3ZmMEkCPKJeOiJ84EVZDXpcw94r1g5cI7ccvejDgrC1tdwVI2ZOdB2CywxZS1Q1EraSKH663S7uxYEB7uErLP\/JrOJYwv+bZDpJDrbSvjX3U\/i\/pWr8T\/7bItSGGxykBAIUkrsuXg2dXe24IqrVuBfdz6Inq52LNh+K1Srde35G3OElpYyKpUavvGDi\/DPW+9BT1cHwjDAzbfei0q1jj12np9kIdZxWE45X6VSCT8++xJccMmV2GJSN355xjF41V47JCLSKZs262+YHSUie7Nj4zkeeORJHPqBr+K+h55AT3cb6vUYtv6AUsoUuviqB9un82Ua93iCwyzJ7DNklubkIZo2luMFS7kmHn7yp845SCNAZu9+1FtFUHplHFU4gbt87q5wRSlsrwY4ZDJNVEOaZpseuVI48HCZx4LeHGpIWHLIGDKW6Gwv49Z7nsS9Dz+Hg\/abh1IYpMOzTWA5UrJ4tefiOZjU044\/XXsPbrr9fnS2t2Lxgq1RqdaUBl+itaWMwaERfOW75+OOux9CV2c74ig5fC3lEv51x71Yu34Ir9x9p5QRkBx2yYxQCIRhiO\/\/dBl+\/fvlPHP6JDrv2x\/Bvrtvj3o9QhiYVHMdU9MUaByPehSjVApx\/8NP4K0f+CoefvTpNDgi7TN2KK2n9Lqcm+eWafJ7L9qIp2EY6vU3LNLmdXG2DHlUQDB4UA4f\/UNggAFQ4BoSztzlXW+jsLRLHNckAUK344WzGWOnLzg7KdfsvGEcZZmT2OZ4k5SMo7zRZNe8iuqvlOhsL+GO+5\/CPQ+v4jfstx2Fodj0IElr9j0WzcGs6RNw5TX34Obb70d7WwsWL9wGtVoifdre3oK164bxle+ej\/sefDQNjkjJf4y21hLuvPshrF6zHnvvliy8RVGMcimEEAJn\/mQpfvvHq7HtnGl04feOwx6Lk+cPAuHYsnTN5N0BEsUxWsqlLDj+\/fgqdHe2oR5F7Gx2jSoi\/XAcpbbNAi86Q\/bl6wkOB43daRqkzc4cEHLijFaRXSt+jOGn61aALFzYJ1asWMYzd33nu0QyRVd6kLHAuFkrTu6sov2H7jqlPT\/g8gWBz8JZScXs8uhQ3xTlDZGS0dEW4s77nqK7Hngar993O7S2hPkkfCP698a1WY9i7LzjLGw9axKuuPpu3HTbfRBEeMXCbdFSLuHxp57FV75zHh5a+RQ621uREPt03F5K5tbWMt1178N4\/MlnsNeuC9De3opqtYZv\/\/AC\/OEv12H7rWfiojOPw6IdZqNSrSEIRDarafZKte9LtxKjKEZrSwvuuu\/fOOxDX8ejT6xCV0cr6lGsIkGMRjlt0oEyMxz7wuMm\/uzuoTDnrr2AvUbrtt3I+QumnbSiZG\/MVxqaC7U4LP8Yg0+MegNky13f\/R4RhNvLuCbBSonVSJue4MjNYOzDbqRP0ldtfUp67uGSz3\/DLUvZMFOBXaIx0iAp4Y4HnsYd9z2F171yHtpbS6jVY2dP4jt2DcSJkG\/U1esRFs1PguTP196Dm269F4EgdHe148vf+RWeeOo5dLa3II5jx\/uUfphpKXbv\/Svx6BPPYMft5+B7P74If1r+T+w4bxYu+O5HsGC7WahUcgGIsc54zDI5imO0tbbi1rsewhEfPg2PP\/UcOjtaEUWRVkJ5IXrXnIyLFOmVRly5FMmYpJNZysO09\/aVbOZ5Ujo5hcrCWSkOYnDcGo\/8sD7y7BAAY4OnnwWWkNzzg3+6Mii3HhhVR+KskdeMFosgOGQ0ZfhevEf2UttNLyzZYEqTMsgMTHb3P9mUXX\/+IBBYvW4Ye+40C7\/48hGY1NOGSrWuq46wilzpLZjq9EaUH75YMtpaW\/D7v92O45dciHoUo7uzDYNDI2hpCTmOJRFsa2XzRhSCUK1U0dPTgSeeeg47zpuJX53xYWy\/zZYYGa1sgBSo+xFFMdrb2\/CPW+5F3zHfwLr1w2hvazGCw6YYeakdefNDTnEOh9aurZqZb\/lbeyJO0TwuYnCYAcWZ6nxy5pJrjeVoEEfzK2vuehSAuf95aiNTtLK07LpI9dm2b2NF0hEelQ+\/u6tCunQFh25Co3nsJTMXgnuTzFrmt9Utkq9HUcSTutvxzzsfw7s+cyGeWT2ElpZSRr5T\/RCl1L3UGyxiKRPKffK\/nHknjoyM4k2vWYyzv\/5utLaEGBwaQVtrKdm0Awx9KOmk9Mg4RntbC55etRav2HEOLj7reGy\/zQwMj4wiEGS9ng35p16P0N7ehqv\/cRcO\/9DXsX5wBO1tLYijSClVMvNQdh9oY8mJDadcuM18oHASrQs1ty+wm30rOGSBpZwK3uR0ejYvzGSWVmKZ+zl4rp2cvWukRrItCpy3n36bu8xvFL5UEarBOejvgImlLYHqthpjeyBppmhQPYowoasFt979JN7z2YvwzHNDaGstIcoYqjqNohEUJjxp\/iMEYWh4BK9+5Y4457Sj0NISYrRSQyCIctcsadOxldcchgKr1w1h8Q5bYdn3j8NWMyZjeHhTgkNmwdHR0Y6r\/nEn3nbsNzE0Ukl\/58gwvIHOoHWvVLv3NLTyFs41B1unSxsaMxuGRbaIH7Sxgil+rjGI3S5b+dlvyQfowtdpslvJkFVell222NR3hkcYLs8M7N4Mg7HnbguIab4R7ODbGHRpht\/4sfGzoyhZurrtvifxnlMuxNPPDqK1tYR6PcqCgqWeJRqBov6Z+U8gCINDw9h\/j\/k457Sj0NZSwmilxoEQYMumTUdXSqHA6rVDvPOOs3Hhdz+CaVt0Y3h4NNsj39B\/ktcJ1OoROjs7cNUNd+Adx32LRytVtLYkF4L5OVtusgW74\/revtTEBE1lHLc4B6BpX6n2d06xEDbWdz1aWOmsXdWJcgll66wSH8RhCcPpPt6WSIPWZEpFk9WTOTLauyH8bNg0Z1ag1rK\/lvoZjVLLJFDm65z2mqY9X2FOCI7obm\/B7fc8iXd9+gI88cw6dLS3oFaPvFliLP8EgrB+cAj777E9zjvj\/ZjU007Do1UEAZGpB6y6Oq1eO4RX7jKPLjrz2CQ4Riqe4JBNA0MmAgWo1yN0d3XiyqtuwduO\/RaGR0bRWi7laJrbh0Qp5R2Lccb\/1\/7bnJ+xtphtEGLNGZubwGihXWzaWKNA4M\/whVRLtqo3QNIeBLJqKo345h\/OAZ5FM\/YIiPkUUOCqFT1mO5k9MNQ3yaxpFSzPgJEV2zEGSBChnq7Kdne24K4HnsK7Pn0+Hvj3s+jqSINkE+r9IBBYt24Ye+28NS789gcwfYsuDA9XkhVeQ2srDAWeWzuEV++9Ay783jGY2NNuBUcjayWHP0Hl9EyWZ7bGgalHMbq7O\/G7P12Poz72HQwNj6Crs52iOIaUsmHQqJV5BqxOcOj8KnoDlGlfOWVpWVUkYa2sJBi74\/BytYzddjIF8tjrNaIMoe3mvl4VcdSsxKob1sfaQE+nGTvhSc+eiG9HuIipaTZxjubeWq6xamHSffKk9XqYk\/3zarWGtrYSPnfC4XjHW\/ZHKSTc98izOOrkC3Dvw8+gu6MFtUTHe+OCREoEAWHN2iHsNH8mzjvjA5g+pRtDw5UcMeNcCvTAfXfEud\/6EFpbShgdrdqZA8gIl5zu5esZIz+cUkpEUYyenk5cfNk1eN8nf4DhkVH0dHXgi5\/7GI4\/+t2oVCokpSRS7OlMXV2n54tafOi9FMPnvJv2l5YkD1vjAzKFrm3qfPZ7ks4cgJOBzj4jUeYalABx0t1n7vKOQyks7yijmmxQTfIfms9CeANozDYcCD9c55mF+ODCJrIyKZzny4DJ8weCeGS0SpMmduKzxx6KebOnY7ttZqCttYw773kYa9aP4srr78N+u87FrGk9GBmtbZLAmhCEkdEqZk2fiAP2nI+\/XLcCzzy3Hm2tJRABz64ZwiEHLMbPvv4+hGGQiM4Fwjm81IjUlE3p8gst\/d8ojjFxYjcuuHQAHznl\/xDHEbq7OtB\/yglYtNMO2G7buZg+dQquueGfICRcL+YkPbP2U8xtwaIJOBOsJSc25io+57EmcL1PIV670Nm76Mf2tg0BcpirtTNRXzMMgIRngDQEQwwhp7HbEQ+HDL\/pi2dr+vphX69QnElU0yFmVm4zVShNacps2jNzKjwwWqUpk7rw2eMOxbw50zE0UsHQcAVv+Z+98L63HYgwAJ54eh3e85kLseLBp9Dd1WJAwL5mOP13X7m1fhTbbz0NS8\/8MHbYZjrWD45izbphHPmG3fGz096HQAhU0w1FzifsOrSsQMwsJaQBO7OUiKIIkyZ24fxLBnDc53+KOIrQ093JX\/z8Sdh50QIMDg5haGgYB7\/hNTj548ckXiSJ626uFJvV+BbnzYbrfZ+zTT8nv7MUO5RxND0px4Wq2T7D6UGTgwasrEg0sJIRtJZGG0+s092nHBusXHmO3HLnd7xGBOW9ZFRNuFh608SmMokzKh1eIR53JvewCK4lHIPGrKMOymK\/7nXXoLuzlvuTDzoQAiOjVUyd3IXPffQwzJqxBYbSvXIiQqVaw6Id56KzoxV3rHgYawZH8bcb7sfer5iDrWZMwMho3ZlJsoFhE1pKQxVkyuQuvKF3J\/z9hnvRu9f2+OEX3416FKMeRUiRrqysUweRY+WGTZ7YhV\/9ejk+2v9zRFEdkyb08KmfO5EWLZiP9YNDCIIQJAijo6PYacf5mDVzOq665h9ZICckCgei6MwOpkqrZWqqzldYJ6c6NlYd67fcIKkaCGraaWoChpYHDZQlq+ysSQaIGPJpXtP5PeBJaVFN5r43YfPOeMXb96KwfICMKjLJMhoCQc13imH3L1yg5eudpLqbrOyQFOwS2IRGLbCpoWU1MlrB1Mk9OOW4w7Dl9MTQJgyCfD22ESQ7bI3Jk7pxyx33Y836Efzlunux5+LZmDtzEoYrDfFo9lM5rBIAypxEoFKtYUJXOw5+9WK8oXcRCIQoivIyh0hn56qrusbXXcHxi2V\/x4mnno04qmPa1Mn48hc+QfO33waDg8MIsp0RghACo6OjWLDDdpg1cwauuvr6xAYhEGApHdA\/LBKixcvyqJsoOs26qqKrxGa3UY5pnkRkoFjO8wTD1zFt4IgEpPw3V+\/4YYP27BsUrs4aWRjCCmajZvUV2oE0dtmdQtfsCg52LVGpSlDs9jJ0qc7nusDIGsZACAyPVDB9ygQlOGpacOT9gsC69cN47X4749j3HoLWlhBPP7ceR518Hq675WFM7GrsSMAqrzSTl6xZtkuxQBCGR6vo7mxFIARqUZRUxApKVVTGub4exTEmTejEmWdfjpOWnA2WEaZP2wJf\/N9PYN42czA0OIwwDK1PPwgCrF03iNcesB8+\/5kTQeDEsCcNVht6LUIkpaF5Bq1EctiF2\/q7FoTsLuWVSppdwcGGfhuZPVHSRqxW98acdPfpO\/dtLSg4Qsq6zub16FF5GinysXCNw0ukRZe0Mo+500HOTGWWeI5bO6\/9aXikgpnTJ+GU4w7FjGkTMTJaQxCKwqZ6tFrDjvNmY9qUibj5tvt4aKRCf772Huw0bzrmbzMVwyPVTA\/LtCvWXYBd2SRZumpo2Y6NkVtQVkmJST2d+M7P\/oAvnLEUBInp07bAV\/o\/hbmzZ2FoWM0c9k5hI5PsOH97zJ0zG1dfdwPqUT3fM9G4eVyQMdkGdeAlMFpST8rBN7ZGi6zBzRmHNS1XS3XO9NqIBIP\/gepzv04RXk8GYX6CZQxItmjrSj1YFBwa9u0LDuPWSX0jDGxdzzxsUdqhWB8X2CmowTE0PIqtt5qKL5x4RCLTOVqDCIS\/YUjXagMRYN3gMHr3WoSPfuBQKpcCDA6N4uj+i7D8xvsxobs9m5MkL2nDBnmsZOeiqXwzYCCKY0zs7sAZP\/k9vnDGRQhIYvq0KfjSFz6JrWZtiaHhEQRB2DTIgiDE2rXrsP9+e+Mrp34W5VIJtVrjEsjnDl6zUEV\/gJE7Q1l7GNBXul19q71SDXhU4ZkdgE\/BYLgRTI1S6anki712BlnRt4AwMMAzFr+9nWX0YRACj+J24+nJguRUmrtTdMG5zJJTjh31ZsoVgcomtkxRrKUge7kmCAQNDY1gu21m4DPHHoYJ3e2phlUzJixli9qCBEarNczfditMnzoJN916N0arNfz5miST7LjNdAyNVDd56aqosS+ir0tmTJ7QiTN++gd88bsXIxSM6VOn4Mv9n8ScWVsm5MYwUCK\/+IUGaSbZeus5WLjj9rj62hswOjqKUhimErHq7o9L6QawV7adowA2CYTuXfaCle1MtsEs3+AbD5BW+4IEcXwx19Zc31A10XPswHIAS7DVK97GkuWHSATtybK2yv9n9YUo6dPnC+5bclIngIZ0qE4hgCkrw8busWMeo+8YpH4Yg8MVzN9mBj577KHo7mwbY3AoAZIFSVJubb\/tVpg+dTJuv\/N+jFZq+OPVd2P+NlOw03YzMDRS3SSvjQ0OnLSEm9Ddjm\/++Hf4ypm\/Rks5xMwtp2HJ\/34cc7aaiaGRkaSsYsdSfeHMRqBSGcXWc2Zj4YIdcPU11ydBUgqJpSxAJAG\/UIc1+yIuXrHWg7GpeKG95uueoSgXPkEIiZ9wbfXdwEpbtAFYQgDwxO0XVqYvWvFeEYRTpIw4EdSwzTm1iPfs\/bKyPehaomJz6ISChj7d\/PDovKofEOmZI\/HsmL\/NlvjMsYeio6MV1WqUBgc7nWUdNZZ22TaMOXfZaR5KpRA33XI3wMDlA3dh3uwpWLT9DIyM1jZaCGJDgwMAujrbcNoPf4uvn3UpOjtaEYYBvrbkZMzbZi7WrluPUlgygoPHcjWkg1SB0dEK5s7ZCgsXzsdV11yH0ZFRLpdLxFIW3PrQe1aPryX7elqozrfOHXgX9J8NHzkFb60dE1vDjQCQJPoWqqsf9\/UgnBh4EhPJRyCCRHaNndpFxDr91977YCh8G2n5jmtDH3btKOsyoqbNGbMXJVPQqsRCecF2M\/GZ4w5FR3trNngji1pXUNgY5zyOJLq7OnD7XQ\/iD1delyiMhAIyljj21AtxyZW3YWJPe9Nh4qb+01A86WxvxanfXoav\/eAS9HS2gQioVms4+5fLsGbNWrS1Jmu9ynqeHQxkB09jw48BhGGAdevX4xWLdsJpX+lHV1cnjYyMcJDaLLhF46TOldLMPaVJB7KaaTciWXCGEqTVyjTMjnFA5juVbEtBxkOoVx5T4VsLxujFqcHKlefI6Yv79gqC8l4ySoUbAIc5p+vWgEuqXnencgtfawiGOXC0tw+9qhpZYximmWPnBXPxqQ+\/Be1tiRJ7IAT8xgRNblQixHGMrs52PPjw4\/jSGWfjyaeeRUtLCbGUmeHn5QN3YcbUbuy2cA6GR6ubLZOwObMFobO9BZ8\/\/SL84Nw\/YdKEzgRGlhJBGOCe+x7A\/Q8+jP333ROtLS2o1yOronK\/NtJgdVLKrdHKKGbPmoVddl6E62+4kdauXYeWlnK6GwNsrIYzW5q87JljOBBMTRwEerXhdrVqnFpKv5UAfgQjK7+hSAv4\/UFIxitYY9BKJ5HMnolIQwJUGtQO9vBtDAjP9CRX90aMN5Jgo2JBQFg3OILdFm2Dkz\/yFrS2lLLg8GcNLgwRApLg6GrHivtXYsnpv8DadYPoaG9NlCvBqX4toRQIfOq032Dp5TdhUnd7tnTVjJKS\/xm7\/wyNzBGn4txlfP70i\/DDX\/0ZE3vaIWWcagckW4gTJ3Tj5ltux+dP\/QYGB4fQ2tpi6QabcDJZ3lZ6zgmDEOvXD2LHHbbHt077MqZNnYLh4eGUbNmMpa0QK61FN8DysHRSjGAxjbVqQ53Mu4Sr9eoGzJx66vGDAOJGeeUMkKlTV3F6q9wl69WkT3FYH0AH99zDOpffuZdvIw2jRWvGQSrTSg1MkxTZKKv23mU7fOLoN0EEAvV6pAUHAQXHwCeHI9HV2Y6bb7sXX\/zmLzA4NJxkjsxqrEFaSBi75VKIT3\/jN7jw8pswsbsd9bo7SJwDRrgCKOFVNXY22tuSzPHj8\/+CyRM6k3JLsroXw1EUobuzA7fefhc+9bkvY9Wzz6G9rU1RUdEvACoIDPXyCMMA6wcHMXfubHzrm1\/FljOmJ0EihMWiYJgOYkb\/oVhE5xxg16jAzeqG72va1FC9wOFR9sRdaR0lnGxeAFixYimAJZi1w9tqEdWOIRItgDRUDrXUSNmwxTXZNlEL\/w4JeY0ZTaUKbRipc8ICIbBucASv3HU7nPTBgxOFwDhOpsCOj5rG2KZKKdHT3YF\/\/GsFTjvzfNSjCOVyyOlBI3Nw2bCnFkT441V38pRJHbTXK7bG8EjVg0CxjusxO78uU2pKW2sZp5x2AX629O+Y1NORHHib\/0ZgZsmS2lpb8ORTz+Dmf92GvffYFZMnTUS1WjVQPLYs4tzvE+foVrWKKVtMxt577YEbbrgRzz67Gq2tLZAyNs1qHEqZsPhcynwFXoaFl9VtKdeQ3dDbnLDEkJ0ESP4ItbW3NyBeZ4CkSBY90bdgZNpzk4+koDSdOcpo73AsnrBBDrNp0HC4DjkWrBxENZfadwNxMDn\/gUjKqn122w4nfeBgIJXPbJAJyfrH\/efm0ZAyacj\/fu0tOOOHF4GZE7lSKRPVUWZDDklmF5gQQEBEf756BTraW\/DKXbbBaLVmBQgZsqJmYFCq2hgGAcJA4NNfOw+\/uuQaTOxpz6bvcK4VJ++TlBKtrWU8s+pZ3HDjzdjlFTth+vSpqXW0sFx2XZ5XPgi4mgbJXnvtgRv+8U+sevZZtJZb8oUnqGIeKBAbZKtX9fatXhVF8\/kdaJcuL5Srh0p8EfW1TwMrsxfh5Br09S0NVpx1vJy26LC9g7BlZxnXE30scr9Ql7+cBtGZMituk3un3a9OSJQO16mUV0PEg8OjtP+eO+BjH3gjwAndonhno9jkJskcjO6udvzxbzfiuz+5GAERwkBASsk59Ttf7DEZp8wMEsnfufKau9DeWsa+u87DaKVm1f9af2e9jkRhXQjCJ7\/8K1x02fWYlJZVjszBtjMTIGWMlpYWPLdmDa6+9ga8YtECzJo5A5VKxXqfqCBz+IJkiy0mY9999saNN\/4TzzyzCuWWckJw1I0JnTe7SV3X8EVTXIGLrf5cVgxms6\/f6CQAPInh9i8Cq7TbyxkgDdr71AWHbinC8sE5kmVxm3Kbc7gkIA3elr3M0vgX0unqPjE4d3AktmNVvGrPHeiE970BUnISHDSGCbniJGgGiZSMnq52\/OHK63HWLy5FuRwmavBSmoNScmuG5R8uASi3hLjy2rtQCgX232N7jFZqip4WF6BmMvMO+dgXz8Wlf\/onJk\/oQOQODrDhzqXZD0jJ5VKJBoeGcM11\/8CinXbA7K1mYbRS0TKJC8Ao8hBJgqSGSRMnYP\/99sFNN\/8Lq599FkEioN3wcKFckhTwuc6ysZLg2R0nm1dlBI4v0+hgTJzIX\/G1qN3\/C7VB96JYA2mjzkLeKOujTOAgE2JIVy+zuYTuOW0IKyj7wuz0dqBCizVXz5KvcWlvahzFtNuibdBSTlQ5xjbF5gy10dyf0pu8q7MNF\/327\/hBGhwEIJYxnHsnBR96AkEkh7m7sxXf+L8r8P1f\/hU9XYkTlQuxyhi5UZzZpn3si+fi93\/5FyZN6ETk7jncbAUdJqU4jtDW2or16wfx6c8uwfX\/+Cd6uruT5ywALXyB0\/jdgyDA8PAwZs+ehZ12WoBapdIoHcmJbLmCw8Xuhc4kty4iazcd+mIcdH4fGZzgtG65wWzQ\/TDvsiMlALQMVVZwHD0OERLLrLB2BUI6v5TQhRWg072dNGbTKN61rdiIGmkPFjn3Ka1UE2Ho5EOhMU+LlUFN9ro7O9rwq4uvxM\/O\/wPaWss5xV4DWxyieC69qMZATEpmyejqaME3f3IFzjz3L+jubE2FFdxDwDAUkCxx4pJz8fu\/3oxJEzoQxZFD5ZDtz4FdpkfJS4njCOVyCaOVCj7X\/1Vcdc31mDihB1Ec2XrJTWk4+fcLIVCrRajVajoHxrGX7rRk06SGVERUN2FiVU7I3nxlpxSROTZPXr9Ij+F1aXrg5gECMPqWBjdf9uERBt8oRAmgBtXWpTdklE9eQ8bGC5VoqoWl83nItcnGBvmNyEUroqbwbZ6NJUAJfPqLC\/6I8359Jbo62thQ7MjlhMwD6uMS5Yctq727OlrxzZ\/8Ed\/++Z\/R1WEHSRzHKIWJpfQJp56DP\/ztX5jU05lIgfqpOeZuN1l9oiKFJOMY5VIJLCVO\/dLXceVfl2PChB7IOIYt3ePLLIadAKPhy57\/zo7BsI8v5RKpthUZ7X7FoLYQfD426aHI9YZIgOVqhPFN2gJUkwBB7zN3NQhIf82xaDC7jBBNvSl1YGMtPgGm\/I4mSMZuKkn2JrCH0l4o98+ePzeQJEFoKZdw1i9+i4su\/Rs621szlQw16zFQ4BfPDoNJfZjK6a54T2cbvvuLP+MrZ\/0ene0tyXpRqjxSLoUYrVTx4c\/9DFdefScm9TQyh\/L8epbmoh1+dYFNDaw4zox2+NQvfh2XXHoZenp6NOs4KuzhyP8nymdKMPt0qVQfMKoNl8yPq8KAW0TOsURF7rwnQQQmvhnr\/r3G7D8KA2TggCSShOCBqD4cAwg8G4HWEop74Ofe0dAJjPremCNTOXoQFHjOsQeTYe1taMwWyqUSvv+zS\/C7P1+H7q52TWjBEFGzHFMNa2I7QI0bMRFXkOjpasP\/XbAcXzzzd2hrSazYyuUQg8Oj+Mjnf45r\/nkvJnS3JWqHDvV0VmdCjlvY7JfY0ldiSBkjCASVyiWcdvp3sew3v8WECT1pIHOTQarRumcRYku+5pQiF\/Tvsq5gXT3WuXTF3h7YEpizgyR9Z+hPvnjwwzxLlkiAafu4\/R6W8i4RlIhZSrbZuNpCikvBxC8sJ+HWRJIOzSNbK1jJbLCNkl19COd\/Tjl8mphcEs740VJc8bcb0dPVng3eDMFkZqWBNNTHLVoMs28lNH\/tUkpM7G7Hz5cNYMn3LkVPV1saHL\/ADbc8gInd7YjiOO8w2EfWtOt4583bIPMZJaCUEkSEtrZWfOuMM3Hury5Ad1dXAfRM9rGzZn9MunYWG0tRDNWA07lXok1Nx\/C7qUqcDhshI8qT9chY\/s1VXgFA4VpZb++pwbJlS6KdjvjZn0mEi\/WrySVG3HwXxLKKNvBqAmwDHa\/cDwxK9dim4o1AkVKiVEoMc77xgwvxj5vvTp1pYx8RjiypVbjUxxttq4FuOZTQmYGYkiD55W+uAYGx8vFnceNtDyZlVcN+gM2FMhPlKyYFWm+SSyeAkyBpb2vFd8\/8IYaHhvDho9+PoaGhdJgpPJkjt+Jlidy4HUYpPRYbaLZk6GAqObLxOxkbhqSCLh6ohtCYnrO8GyMr73BHfJMAmTp1IQNAzPHvKKp+MhkWcpFTqL2g4tsRgLa9T80oBbZOrPKmk2ku2jw4YinRWi6hUqvjtDMvwE233Yvu7sQjkJ0Wcz5TIAfRzpQ0dR1eI\/hjKTGhuw3n\/\/Y6BIIwISU4GmUN6w2oS50S8DuCuXYpjFJEJkBFd1cnfvrzcxDFEY77yNEYHh6GtAavXACgm7CsrZVrzWi83up+5rbHLlyr+zzzm0TFBPQHAFEaC9EGBciyFO6tD4\/eKNr5ARGU58m4JlU0Bh4imV\/Sx1zHbeZW5HgTfDeSt1HXXcbjOEZraxmDgyP4+pkX4I67H0Z36hHIJi0\/o+GbQynXgXNdCkZfZc178sMvpaTO9oSiIVX6iCKr6epB9H7D0fc4VGcKXLmyv9fV1YWzz\/4VhoaG8YmTPoparY44jgvZCWSO3g0xab\/DMfvfGx2107dYtayYb+9QASSTNmsNmcrfFUV7s1Ez9\/b2hw9ccWKVgcsoKCXcN+uwmzOOAggOnr0Qjxg2G7YLzJaKo9WFuduyBmoj0d7WirXrhtB\/+jm4456HuauzLanz2UVvUGj4zN7hpaUcaPUv0mggYU6AidNMIqXdf5myS25xZz2r5LMC1+62PYhVe8FkpyRGV3cXLr5oGb78ldNQKpUQBEH6+siNXWlxIAmwrTQYhoUCs3tPBDbFBAzK5z3q0F06B5rkriMYIMHM92Jo0g35RH3DAwQDaZlFkBfLqAowRGNgZx4ISz40E4tw9SPSDA72S08qb4otFkcmiuW73+LUpemZZ9fi1NPPwYOPPI6ujjZSmbAaRM368BMaOKOpNNrDTX3xQM8eauCzflCctgJ6CcXsMCVS3Z1Uqrg1R9BU1109Sj5c5HSnpHviBPzhsj9gyZe+km5Ohglb1+p57fJL2eRjfR4DuOzFXTCujSTmAoCZbkGT4toApBtWxpcCN9eLKqnmigXLjowBpi0WPfWPOKrdKcKSQMJ\/ZyeHpqGZ5GLyuv09WB34uzbN9KEXYA3uEjwIXEDQjmOJzvY2\/PvxZ\/C\/p\/0cjzzacJeN\/Dep9fqlJSGjqcY76BIMW+FFh17dVtU2l8iS+ffZKZNVMbD78FuQvPo5KHOVKIrQ1dODP13+R3z2s59Lqf7lzELO5GtZK7Dqe2Egkl67PDtwWJcNtUtqLiQUaQB\/kGDYYmmzZmpMzo+9vacGA0uWRCC+gJI9danCc5wNqlQbArcLkUORkayhV1MKgiWFr1PfjbsjjmN0drbjwX8\/gVO\/eTaeWrUaHW2t6VaetXPAlgeFS5mcbQGzfPdZGmJoBj3ClN53Kv9J5zKYNYg1reG0\/kiaVJRssk5OWoy0nJhy560IXRN6cM1VV+EznzkF1Wot2U508bcIvowJy+eF9XmJe2iceLco54Ds2Ym\/ciCzOYcgZvwTIw\/eksZAvEkB0hgahoQLo9roaKLpq5ZE+dtienqwmmdhrseaGkpej0H7RlFpy8aSlmoj3Ngfv\/u+lVhy+rm8eu16tLeWEcdRgQWDqZhi856UBl4rG3VZRX3Ipfeu6u2ujuPdA1CLW6SVFR5TVRPwoGasa+QlmgotN4KkHqFrwgTceMMN+OQnP4X169ejra0Bi9vKL2pV6B7qZuIaxJp\/IDyaZ2ZmdcO4TUhF6ZYUnYNGs1607zImJt+SJRJ9fcEdvzn2IebozyJoIWbEqrEKa6QBh\/Mt62Yl3IzkB6nXomwP3LgQwUpo4t1dHbj59vtx6unnYN36IWptKWeZw3XD5dQSjww\/a2UJuVijuVqfdLipNt4mrdlm0z9ecxi2vR+1QaFBonQYWCLPDg5lmWJ4VT+g9XodnT3duOWmm3HCCSdi1apVaG9vzGtIC1ozc7EtnmAItzHZqiMaOZXVrOFrxgtE9SWBAkj5HBAuK2rONyxAAPShr\/Fr\/5A5BrMUxhuu04zZszrrXLAyBMJcTZxrddcVYEpD3tPdgX\/ccg++9r3zUKvW0FIuJXR143ayd5ThgZpNCNV9s+fQExxuqsocI3NgMmU1fUtE0iNkkMvrcJFgX9b\/eFjXjcNssq4Nq4moHqGjpxv333sfn\/Sxk\/DU00+hq6tT8VSHrz\/SVs+N0pwsPpmp\/Zu+T2NYZCDPtEaCiBi4GEP3PZvuQ\/FmCZBlabM+fe2zf5X10TtEUCZmjt38Ki1fO+BJeMllmo2aAyljh4SpOSSMY4merg4sv\/Y2fP175yGKYpRKQbonbSppmHqxuaguw2N3nH+ffbPD1byr1sQyLy1y6gUX29PZE3BmW8bT8oE0y0PL5sUEWgCfr71Z3kZRHe1dnXjowQdwwvEfxYMPPoSuri5E9UgBUwt6kHypiXQFfjYkQ\/PzQ2NTMPP1II3ZRwTgrLGe+zEHSNasDyyJmMUPSAQEltagip2HXTVHdbMynUruJkfL6WoFheGaiytcsfwmfPOsxv64yATWDFTHSWVxWRNn+Crs21UvB1WI3tEHpHIzTqNS1upyR2D6Gcys8uI8aweqdBMMsTb2+AO6OHCNnxBHEdo6O\/jRf\/8bJxx3HO6++270TJigZBJ770OFky1lErY9K+FnSGyo9n0MEoLBf8Xww7c3a843KkAGBk6NAVAc8QVxfeRxCkKRvuvGYbccTYk9zF8DjVLSqX1DO9GZbFiW1NndXR247MrrceZPf4NSKUAghCJqYKI9yJaKjHKEPPsGZDTA7CgpyFuGwcxCLli5sZLq0bhl5b1xzJPIvLnd3Cb7UvNkEr0r5lw0vLGdGEVo62jHs6ufw8dPPAG33XYrJkzoSOBzjWxowsqZ4Iem6GJB7uxvuWmMf6addclnbMjZFxsWhMS9vf3BA1ecuJ6l\/J4IysRQFqlglh5qPWDoYlk3likY5qdvOHsElmhva8HS3y7H939+KcqlZIdbSqkPALPlDqka6zjYtmx5Wliuvmzf7Oy8jZltNMvNXs33NuwBGUNXtWe4HVy1JqjJa9VWEtTV1sbFkTf3htRsBkxRHEVobWvFmrVrcNLxx+Haa29AZ2cnEMdwvrcN5StlnGGKAyL1gaHmOwxjKbViQAiW8U0YeeSv6bmPNoTeugGP5EDt8NbvT+J67W6Q2IJlBI2fBa0h48zdSSslpJvA6CXheeROG2++lJg7ezoeWvlk6tBKKSXCAwZYdg2pm6tF7fBBwWNlpbKhTo7i380vyAynx0ozezvmJgRGOKU5i35Po9nOxTOEQHV0FBMmTsTkLSbj4fvvR1Au5ZKk3IzEqugBb6R5kIeYGINEwBy\/FUOP\/NZHTHQ9gg1\/CUvQ24vw5j+dPDx5u9e1BWHbq6Wsx8QNIWzrsKuNF9lkNHh6kIID5HAoAhFWPbcWpTBIadd+RquT0Gc1tI5g9xxQpSHWFcr17yfvgSgKNK0Ekk6qunOrzmmyCv\/hV7M7fIRBWD0JG0zgsBRitDKaKJqUSzCV39mFcBkADDkl\/ryH3\/pjcmQPIP4Xhh75VPrleMw1EzYuTgkAFvzPGRPrgVxBQkyFjJnBwr0tBhR5P7BLjlLx0HZTnO2bnSzejjs43DsqbkjX7dIr9dfqotvAt00Jl3+ih1YCpxqgzcj1edcXs43ZwZ4uWjtwlmRWsHImx5fztdRlOniznLkBSJ5IYGt5oTCZpNkjOhRDKy\/dkOyxET2I2oucGqz48ydWg\/l0EbRkvQg7LBB0NEraKVurwfMmlI3gYNdswPA8VzYaGSz9XB92rMRygSElKyLe6tBPFdpmE9qUFs2fnQM9g0SolDCGSomD5mJ68jmacw\/vCSawYv6uTvTJkWV1m7PkXWkERzYclUazD4+2wEY14K7WIx1DUMAyvgZDK383VuRqM2SQxt\/tpwW9U9prbcO3iSDYWkb1XMYxv20bgzDd\/MZBSGTfzavf7KzpbLkGec4dFfXmllbmMBd5rEzivi25Qb92Nvso6J0Mv3nlfWr63ni\/ZpVJ+T6G36a5qMwDtIUv+\/mdFQB73idtWm7TTryHnvXBX5F+nZlcJIMCQPZi6JGr0pZigwJEbEKAMPoW0oqB44cg+X+JQkIqu69z\/rNBkMISLSIkAi5pIZWewE7DTsCtqwXHxNnBMPY1yR7B5Jxlra6E2hNwNpTGtaGYlmVAxnyFXWCF91ZX0CHl8JI503HpAxga\/XYmg2MA6SALOgeMnK8ksCpwzib0rGcB5adYdiYF8xAySIkBWP56Y4NjUzNIykFZGmBpn5z3+q9dLcKWfWV9NGZKm39fHczsR4acJvXeGtnqUdh52ItqeI\/ItkII9N3eme2Kc1oMa+bhVyPnAsIhNqyhd10UqSWZ42aHTzbWBUc3JXc6M3LSe5Cj9yILtR3bpFyZjJNHV7nxQqos6oux\/rEHke2hb9hDbHKAYBlAxAT5cZaRTFzPDZUP18zD5VVXeMBg3MYS+YfOHv6SYbmg3jm2R50D2nWstJqUDGlrTWkEVstOzse4hZfSr1NZWJ9Qq5N0e8bR+JvEBQihu88wAlN9syzUEdYsyyyd2DXZd+qVuestE9XizJwb7ESuSAhmfAvrH3sgPedyY053sMnxsWIFo29psPr3H3ls4txXTQtK7XtxXIsBCNYW6ItmCp7bjL2OpKQ7BjXvZ\/xe7oWi286vWciXL+s5mK3Wba6DD2ypIzqRNrO48CJhpMPpTqFr93vh+lxcmcNS4Ld7ORdSRcUoFZGRFMjxrSrBXvlmCZBg8IMYrrwTGIrHlJqetwABgBVLgX6IrmdarqOo+i6isJtlzCCm1DSFVQqHq0nUblZ3A8mWh0QWgcbCFHvEpOG4PQG77NHmLQXB4ZAYteRAbeswMrOc8r\/ErLN7naWLYhfhKVdZo6ywqR6D3GnJW\/Y0b761QS2aN\/7g4k0N8mQJ2LAumWWVvk4rBGT8LtQfu3tTssdmKrHSC33FQvr3Hz67RsbyxFRHX1pmO\/mONzsbdLh3L5AN2kDGEhXpAaMeJunc7WbjptNgW2dDyx4vRmt3nF2wLbtudouAmR3A3IjHeH7HZiW5f7d8ldfU9FKDww5QXw8I586JPVT0rB\/Y6jdjvc0bJZRr5pFxhIyQi9KJ+a8wsvKPG9uYb\/4MAgArljH6lgZrLzvurp45+y0ISm2LZFyLU7UxUmQnSSkdtd\/Zaa7TFIp0bwD6tXOT7yRLcAzG5NxCe7J63q0JC8uwxm0yBCfNJSul810Je7ccHvTK2Htnc+LuuL3Vi4KKyiqXvA7MxUkL+NCkkgzDVWLPhNyF33qWoqhhb6+UYpKSyuxJUOtbUHu2uiml1eZDsdRHf78AgHn\/KE+W9fptIDGd43w20nwizAU9Qd6pqwqHbt6TQ4erkNPlmOwbUCgUkqDz+Z122NJ5K7tWh+2Zi58TxoWoWBG1A4Xvk46EybHNqrw0F0dpWhAIm\/7gCBSEysR8k7PHZiyxGjStJRIrFtIDV3xulZTRMSBBTJCca8Oj2PDFnnaz6TUH1eyTHULZHqEFn4g2DKdZzVpVLZsAOBXp9YznROnY8Hy1eGVwBoeFKDnBA+j7Hk05Wvb6gP4rwztzMZbVmFXKsTWslTZ62Dw4XDXYWLJAEhwy\/unmDI7NW2KppVZvf7h2+Zfu7p69z+Sw1PZKlrUYTML0lfMeCJc7kn449Mk1mjXQDlU\/IyM4yIvEWZlr77P413CdJaABs7pVBdn1dQ+rAD4DS00d3+BocYHSpWmYCXZM4jRQgxQyKmmrw8p3uaztaAx9BzSl8cLBSMxEIZjvRvvwERgZiTelKX9+Syz1efv6xLzB\/cKosuo6EYS7xlE1sVDI6tMxs1CdWsDN6BJemU+XQolWLsgC0h6ccCoXBodNx2BHkzy24Sn867QFpZSiLENNh43MzQe37NdUtvuwDaVnND+URrMuAcQsaF+sf+imzZk9Nn+Jpf4OCxbwA1ecWAXh\/0kZryMSlBpOkO4TYU+h2Se+hmY9hL9\/acagZdfwUFmB1QKApXPwZcn2w\/G9YE9wqPNFY83XbO7ZHxzsn0uQl+9m6nRlb74smu+QU57JKXH6vNzYzA2mLnBSGhzh5gyO5zOD5DSUZUfGs\/c\/+XBRarmYo2rE4EB\/c4sPr88Ucyx0DC4YgLmUDh30b2ZNpAxNyx4n+GCJPcCeS+SHNcFn2JPlNmDgWUzgLM4k7C3jnH+XmZnI4YS7sY258veYXH+VOYIIQ+b4bAw9\/D5sII39hetBHP3Iuqu+clf3rD1LQbntABlHUeKaW7Chp5QE7NSYzf5bgV7ZQVuBw37aJwwHa1Kt+aA7eiILQtayHDz9QvaTSbVI0P0tsjTiZveaYIK5zWkyEZI0RDbcm30v2w5VjvfJyhbZ70i+6biv7\/AFjNl4kNvGIIYIQkh5E4bl4cA63px9x38uQABg5UAaJF\/9S\/esPRcHYdtOMqpFlNABim4zQkFjmjNi00m9PmFXGkn3chBbMG4z2NYPd5J+U7Np+GIGARdsKxrblykhz2P1wLlJprtfgvo+NZAnMrYhGYadNVsWaL5sbc04NqR0cXp4FC1EKVSSgAlPIuTXo7LyOYyV6fiiDBAAWLmc0Q8xc2XpD5WocpAIyzNlXI8o7YEKB4Cu1VyjDIL9oTsbTk2orYkniTVg4wZ9tFDpXQls47BqMDEKykMtyMmJ4jmzhHvYapeLRsmk7OhYgeopSV1eiWPNEsqfkQ\/ZMv\/d4FkB4BpAh2D9w3ekZ1g+X0dX\/EcCJOUWrhhYMhRT9FYZ1x4VQSlk5tghHFewAciWZx6zeYhcvQ10ZUON5Cjh9jIxJXmkYVtgWxTrvgPp35P+380siWyjU83zADBVDk3VeSPDGn6IpOi4OjYqm+lwYWM2AAulF3xeVez+vlRRjAQzH4Whh659PpryFyhAkOn7PnH1GY\/KWuVNEtEaEiLIJS8MkqBlQGgFBlxrpnbmyA46g+FGthxmm7ACUy2hlLIGMLxKAI9erxuF8\/xsrS\/wsHaVDOt4n5wLSZQvs8Hx\/qjWAsYn4hKRG0OfTUYoUHEQ+AKHkSJWkPLjGH5kKdD7vDTl\/1kUqwDZmrX3R3sRlP8MliXmKDV0Zw8aA689Qs4+yUsbB4xrrLe61TTyAydtZIphr4s61nU1Ni7gWeDi4oFh0frqGJaWyPg+NtefPdJKYxGM8Ml\/sp41yIdkmT3IGNAtTmgkYYll9BUMP\/L55wuxemEzSOOx7MgYvf3hYzecOUBcOxJEEZFIFd7UxaJM5U0rJTyTczINMo0tP7KGXGyxTtnMKtBpLuS8vW2PdvLoFFuLTPoSkult4rB0htmDqC9AZ\/76vFjAbqFr833NSYd5CVoUHAryRL4+hFx195iCIygxx99TgiP+Tx3XAC\/EY+WARG9\/uP6ab9zdMWO3OwSJI5AsWKkNNDFDbypNqNXiLJneEyBPc0kObhHZAs6+GYJrW9FUg1Reo0FaNPdXLHavY6hYQO6kbFbjgnGdBEl9r0SHnGEr9hue4y4otxmVhDzQL9kZSH0kmYPl9zD08IlKQ84v7QBRgmToum+u6NhyjztIBIcTOGjwh1hDqFzQqx9xyfXHcmUPLjDiyUswz4E0rcDAug96kXusU\/8JKNorN2cOzcidSr1midOZ75NjDZkUjSEyZ0MNxcksNTLIWveDdy+cCMWmmnAESxYcECWGPPOFCo4XNkAaQbLb0aWhm364onPLXe8gBEeAEDDHMt26pGbCCvk5smch2QFQSZLQxQL0OYFnWq0fLmvg5lBRdFQKno3AAl9wVnhoLr4YjD0SHWKGb77ChnENckqPvfJLZvmY7mCQ56yT419dsgwFQ8JEroeCEElwnPBCBccLHyAA8OTNaSY5fUX7zN3+JUBvJqIWZhkDLLysVXYbZnrmGMZSlnNfmp2lita\/N9vN8N36sMoeNtZqPdQRclwEvuaejF111u0N9Odkx4zHuw+vZwhvXz2Wybjj+7KmvhEcKVr1DQw\/\/IkXMjheGBTL9+jtDzGwJJq56wf3jsPS7wXRFnG9FhFR2ECHSLtxtUOYlwc6Y5atBp6LpFCNsseEYpuJTWdxpovIOcWkxyp80OgRnMqGvovDpaKIgj0Ru3xjj5MTFwwFmwWJ67\/VISADRCSI4\/hTGHnk9Bc6OF5cAQIAvb0hBgaiybu9Z4eSaLlUUGm+jCt1ZoQwShsupH+bzbWfqm7T6m3FEgWpYk1qyBpIKtymnLXsUEXPZTiZVftr6Tm8JtSaU+YVVIyabRg2g3HV97XZUlOTs+PkKLL\/4MWpMWzEkt+PkUd+qaBV\/EIeyeBFFSArV0r09oaj1\/\/2mc6piy9i8G4UlOdxHMVpgJBJBPQKpzkJdrmImTGtpiw4XDCyfnOTt+TKxaSNfWxHyWbYMhslIfzSP0YPksO3zD6gwWV06ln4sptlMushIgKN4Z7NnoqNPzBmIhGRCJl5FRC\/FSMrN8ie4OUVII0gQV8w9NR5Q0Pbd53fXumZKcLy7pAxwJIzKRiv7KUfTTK6V82SYYykPWquVi+V4aVaGrmHd\/AsHzWj9LsNOw0BDK2s8lDfXX1bUVoYm0gujSHdMCXWBCEgb4XAGzG08qZkQr4yerEcxxdfgAAAVjDQL7DyHDn85C2\/a5++eC0RvS5t3iIAgl2q7D60xynEYNXb3uBQe5HkyEu3MEFez7NaErJzrReqvYOtAFLkqGXMcIzXSu6eAwVicNy0byiab3Bx4+36e5IAAokAkBehHByOdQ89kZzHlfGL6SS+SAMEAAaS972vLxj+63nXd05ddA0DB1BQmsSyHiVLRYqckOreZGs6saZQaSoTKj1IoQCas2SzpXTgynJuFRSniJyu1wsPLGzNNMgEKiyFFLh2\/tnZO9AYFNQLkCkqaFAigAIGMaQ8GcOPfByjq6vYRIG3l0eT3qR5n7LgPdNRwo8pCN7MUQ0JFIyguWWCOTCEU5zaq83LttBD1iwXU+ub2TvYmaZASBteMxwUbB\/65YQ81BFv5eRqsseAUDXKYiZAgoIAHD\/ETEdj+KGGXyC\/0M34f2EGMfqSvr5gZOD8wZGnb7uwfcrCYYAOIBGUWMZRY3bF3iYU8NIxTJIhu5p92KINrmGhDh5ow0lddEQlNcKppq6u6pLV7Jv+8YB\/NRfOHoR0JphzwOfpurXsQs3nHUTgmCBEIigtLwKCwzH84J34D\/OqXroBAiQi2cltK0aePvPati12+BNAi0VQms0cUWIQn1jAmRNhVYWjYFNO\/1rB4pANjbrQJs2b0YaNPT1K4dZeEdvX8nMHWQotyJnPlDfKRGPMIK4GnJr0IgCnWQNrmHAchh7+PGqrR7CZ1Ude3iWWp+TCggXlyfSKUwB8jgSFMq4nQQKIscjp+IOFvTevEjwugWj1uVg3LgWaKTi63WndMkQFCo86tX+M4tE8hkMxRm\/Axi+QIFREYMm\/RUCfxPoHH3ixl1QvjQABAPQLYIkEgMk7vX13Ar7JJA7guA5IGSWcLq2sUqgkbk2nIgPLwpmLQ++q4fns3u0wJUntnZbigactMWoqRLpmKKSPHYnGdNDd9PWCvxczIFKZpyeY+BQMPnJO+rUXzXzjZRAg6evv7Q0wMBABwMQd+44B4QuCghlS1gApYxAJbgzulNtVP+y6Dx97bd1yCJcLvUWKqB3q4ZcNRiLBxwZQ5YBSlXv2KTKyTZ5Ug8k42NnCUtHXNmBcnlBCSATJzJJ\/DJZLMLLyKeR7R\/K\/7YAF+G9\/rFwpk2wygMqzK24SUxaeF3DcAtDOJMISy6ghWivsksi8qX3iziqQmyuEmGRCP1IFKrCD06bp7N8+JDgUTKwsok8Zsz7Ecetr9gJGs05mA+64TVNuHCSlSiOAEAz5N5B4N4Ye+hHq64bwIuBTvZwziLs3AdCz4+G7EPMpBBwBABzXJSd8OOFbzy3wtNCzCDOc02pvw8zUXE6IbTfZMRv3OLKcNWfxNdHeTGHFk0FglIlanAhABGZ5J5i+guGHLlQu3\/\/awHhpBkj2O\/UJYFkMAJN2eOv\/SMmfAuFAMIFlLd3dJZHNRWxbMYvXNLb9bec8RlFnBPwWdNjQGYoTFrZXdP0B0ox16Cq\/GnqkhDQwJN8HwhkYis8BVlaUpCRfIofppfroT+vepJHv3vbg15OgTwB4HUiA4zpSaDjQGnZHJvEjYUVlkfbfmWB3M3lQvXkfm9I7F1JUClODsw9Bo5ajjGzIaY9BIBJJyyTvYeD7GK78AnhyRMka8Uvstn2pP\/oCYFmW6nu2edNrmKLjALyJRFDiOAJzHKdnJTAPtGcy30DB2KLhKw2zRkVvbuts7H00CcYMCYPGOVNtB8baZBc8ZJptQ0CkTypvYokfYqR+AfDY6EupnHqZBog7UCZu84ZFMfEHmfltRGIaM4NlHWCks5SsoSdnT+B1X3I22GNw0rV5Ur4ZB3vVIMd2Ps0MYg4wAMRJA05BsoAu6wD+yET\/h8GHLld+0Es2MF6GAaIGygJulF6d8w6aAhkdQZL+H0PuRxSAZQzmOBErYxbcYFiwe3uv+QKXOeNwLDIVBIABE7OpW8yKtfJYH0b\/kYk\/50EBMMuHAFoK4DwMP3SngX6+pAPjZRwgao+yghrNPAB0b\/0\/e0Zx9HYCHwyi7RMXAgnmiMGI0xlKg2pPzewWnMiYe48DftG8Ij93b+bI9mY8QgmNiE09xUlQg3Ai5Som\/AXgizA0+hfg6eH07wil+eaXyyl5GQeI+h70BsBAvt65YEG5Y3j6vhzHbwXx6wGan4isSCRaEhwry1vCXYZtiJOT2V80NTrVBp5Fk3CFSZuohSS9d5i7jQMs5XMALYeQv4ekKzD88NNGtnje7AXGA+S\/LqssF8BAToeYN6+ldXj67iLAgczyQDB2AYmOpOqKk1KJpWTIRtdMCWlS281gRTyamvh9wOk5yNISafAIJDQQp3QxnghAwGlAUKOpZ9wN4GomvhwIr8PQA6uMoMDLLVuMB8gGvS99AniGtGAB0DZzz1mQYg\/J6AV4D4AXgGhCXs3LRtAgaWQUJXgGUSI6R\/aeuyJlarN72RDUVjfAUsiNKC+D0uyQwVlxFUwPgnAzg68F0\/UYfngFdF7Uy7KEGg+QzRsslspG+5Q9psdC7siMXQBaDJYLAMwFeApIWKUWNzSG7ck3c1IBKYHQyEKJz3xOgoRGUs+gqMQ\/cRCgRwHcy8AdIHkLRHgn1j\/4kKNMGs8U4wGy2R8C6CNfwCTd\/t6TSsHoDBI0TxLmEMutGbwVM08FeAoYE8FoB2QLCOWk6rIXsXKkK8lGAFfBPILEc+w5Ap5h0BMgegiMlQj4IUTxoxj599OeniEwssR4UIwHyH8yw0xlddbieQSYsVsL1le7EHBnKYjaOebWKEA5kFRuNPwxIiBGDSQixPUKAhpFFA6jPViH5zorwM31Jq8rAHop3e3n8YAYD5AX2\/tKQK9hLzGwuYXQ0iDInn88GDbz4\/8DuwcjJxpqCtIAAAAASUVORK5CYII=\" type=\"image\/png\">\n<style>\n\/* ==========================================================================\n   MeltX New Suite ITAM \u2014 Audit Intelligence Console\n   Design system: \"blueprint \/ systems console\"\n   ========================================================================== *\/\n\n@import url('https:\/\/fonts.googleapis.com\/css2?family=Space+Grotesk:wght@400;500;600;700&family=IBM+Plex+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap');\n\n:root{\n  \/* -- brand -- *\/\n  --brand:        #2F5FBF;\n  --brand-2:      #16305E;\n  --brand-light:  #7FA0DE;\n  --cyan:         #0E93A0;\n\n  \/* -- severity -- *\/\n  --crit:   #D6453F;\n  --high:   #E38B29;\n  --med:    #D6A620;\n  --low:    #5B8AA6;\n  --ok:     #3E9C6E;\n\n  \/* -- light theme surfaces -- *\/\n  --ink:        #0A1628;\n  --ink-soft:   #33445E;\n  --muted:      #6B7A93;\n  --paper:      #F3F5FA;\n  --surface:    #FFFFFF;\n  --surface-2:  #EDF0F8;\n  --line:       rgba(10,22,40,0.10);\n  --line-strong:rgba(10,22,40,0.18);\n  --shadow:     0 1px 2px rgba(10,22,40,0.04), 0 8px 24px -12px rgba(10,22,40,0.18);\n  --shadow-lg:  0 12px 40px -12px rgba(10,22,40,0.28);\n\n  \/* -- typography -- *\/\n  --f-display: 'Space Grotesk', 'Segoe UI', Helvetica, Arial, sans-serif;\n  --f-body:    'IBM Plex Sans', 'Segoe UI', Helvetica, Arial, sans-serif;\n  --f-mono:    'IBM Plex Mono', 'SFMono-Regular', Consolas, monospace;\n\n  --radius-sm: 8px;\n  --radius:    12px;\n  --radius-lg: 18px;\n\n  --sidebar-w: 272px;\n  --topbar-h: 68px;\n}\n\n[data-theme=\"dark\"]{\n  --ink:        #EAF0FB;\n  --ink-soft:   #C3CEE3;\n  --muted:      #8B9BB8;\n  --paper:      #0A1220;\n  --surface:    #10192C;\n  --surface-2:  #16223A;\n  --line:       rgba(230,238,255,0.09);\n  --line-strong:rgba(230,238,255,0.16);\n  --shadow:     0 1px 2px rgba(0,0,0,0.3), 0 8px 30px -10px rgba(0,0,0,0.55);\n  --shadow-lg:  0 20px 60px -15px rgba(0,0,0,0.6);\n  --brand-light:#9FB8E8;\n}\n\n*,*::before,*::after{ box-sizing:border-box; }\nhtml{ scroll-behavior:smooth; }\nbody{\n  margin:0;\n  font-family:var(--f-body);\n  background:var(--paper);\n  color:var(--ink);\n  -webkit-font-smoothing:antialiased;\n  transition:background .25s ease, color .25s ease;\n  overflow-wrap:break-word;\n  word-break:break-word;\n}\nimg{ max-width:100%; display:block; }\na{ color:inherit; text-decoration:none; }\nbutton{ font-family:inherit; cursor:pointer; }\nul{ margin:0; padding:0; list-style:none; }\nh1,h2,h3,h4{ margin:0; font-family:var(--f-display); letter-spacing:-0.01em; }\np{ margin:0; }\n::selection{ background:var(--brand); color:#fff; }\n\n\/* faint blueprint grid texture on the main canvas *\/\n.blueprint-bg{\n  background-image:\n    linear-gradient(var(--line) 1px, transparent 1px),\n    linear-gradient(90deg, var(--line) 1px, transparent 1px);\n  background-size: 34px 34px;\n  background-position: -1px -1px;\n}\n\n\/* scrollbars *\/\n::-webkit-scrollbar{ width:10px; height:10px; }\n::-webkit-scrollbar-track{ background:transparent; }\n::-webkit-scrollbar-thumb{ background:var(--line-strong); border-radius:8px; }\n::-webkit-scrollbar-thumb:hover{ background:var(--brand-light); }\n\n\/* focus visibility *\/\na:focus-visible, button:focus-visible, input:focus-visible, [tabindex]:focus-visible{\n  outline:2px solid var(--brand);\n  outline-offset:2px;\n  border-radius:4px;\n}\n\n\/* ==========================================================================\n   Shell layout\n   ========================================================================== *\/\n.shell{\n  display:grid;\n  grid-template-columns: var(--sidebar-w) 1fr;\n  min-height:100vh;\n}\n\n\/* ---- Sidebar ---- *\/\n.sidebar{\n  position:sticky; top:0; height:100vh;\n  background:linear-gradient(180deg, var(--brand-2) 0%, #0D1E3C 62%, #091528 100%);\n  color:#EAF0FF;\n  display:flex; flex-direction:column;\n  padding:22px 16px 18px;\n  z-index:40;\n  border-right:1px solid rgba(255,255,255,0.06);\n}\n.brand-row{\n  display:flex; align-items:center; gap:11px;\n  padding:4px 6px 20px;\n  border-bottom:1px solid rgba(255,255,255,0.10);\n  margin-bottom:16px;\n}\n.brand-row img{ width:34px; height:34px; border-radius:8px; }\n.brand-name{ font-family:var(--f-display); font-weight:700; font-size:15.5px; letter-spacing:0.01em; line-height:1.15; color:#fff; }\n.brand-sub{ font-family:var(--f-mono); font-size:10px; letter-spacing:0.12em; text-transform:uppercase; color:#8FA6D9; margin-top:2px; }\n\n.nav-eyebrow{\n  font-family:var(--f-mono); font-size:10.5px; letter-spacing:.14em; text-transform:uppercase;\n  color:#6F87BF; margin:6px 10px 8px;\n}\n.nav-list{ display:flex; flex-direction:column; gap:3px; }\n.nav-item{\n  display:flex; align-items:center; gap:11px;\n  padding:9px 10px; border-radius:9px;\n  color:#C7D5F2; font-size:13.6px; font-weight:500;\n  position:relative;\n  transition:background .15s ease, color .15s ease, padding-left .15s ease;\n}\n.nav-item .num{\n  font-family:var(--f-mono); font-size:10.5px; color:#6F87BF;\n  width:20px; flex-shrink:0;\n}\n.nav-item .label{ flex:1; }\n.nav-item .dot{\n  width:7px; height:7px; border-radius:50%; background:var(--crit);\n  box-shadow:0 0 0 3px rgba(214,69,63,0.25);\n  flex-shrink:0;\n}\n.nav-item:hover{ background:rgba(255,255,255,0.06); color:#fff; }\n.nav-item.active{ background:rgba(255,255,255,0.12); color:#fff; }\n.nav-item.active .num{ color:var(--brand-light); }\n.nav-item.active::before{\n  content:''; position:absolute; left:-16px; top:8px; bottom:8px; width:3px;\n  background:linear-gradient(180deg,#7FA0DE,#3E7BE0); border-radius:3px;\n}\n\n.sidebar-status{\n  margin-top:auto; padding-top:16px; border-top:1px solid rgba(255,255,255,0.10);\n}\n.status-row{ display:flex; align-items:center; justify-content:space-between; padding:6px 10px; font-size:12px; }\n.status-row .k{ color:#8FA6D9; font-family:var(--f-mono); font-size:10.5px; letter-spacing:.06em; text-transform:uppercase; }\n.status-row .v{ font-family:var(--f-mono); font-weight:600; }\n.v.warn{ color:#F0A24B; }\n.v.crit{ color:#F0736D; }\n.v.ok{ color:#5FD9A4; }\n\n\/* ---- Topbar ---- *\/\n.topbar{\n  position:sticky; top:0; z-index:30;\n  height:var(--topbar-h);\n  display:flex; align-items:center; gap:16px;\n  padding:0 28px;\n  background:color-mix(in srgb, var(--paper) 88%, transparent);\n  backdrop-filter:blur(10px);\n  border-bottom:1px solid var(--line);\n}\n.crumb{ font-family:var(--f-mono); font-size:12px; color:var(--muted); display:flex; align-items:center; gap:8px; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n.crumb b{ color:var(--ink); font-weight:600; }\n.topbar-spacer{ flex:1; }\n.searchbtn{\n  display:flex; align-items:center; gap:10px;\n  border:1px solid var(--line-strong); background:var(--surface);\n  color:var(--muted); padding:8px 12px 8px 10px; border-radius:9px;\n  font-size:13px; min-width:230px;\n}\n.searchbtn kbd{\n  font-family:var(--f-mono); font-size:10.5px; background:var(--surface-2);\n  border:1px solid var(--line-strong); border-radius:5px; padding:1px 6px; margin-left:auto; color:var(--muted);\n}\n.iconbtn{\n  width:38px; height:38px; border-radius:9px; border:1px solid var(--line-strong);\n  background:var(--surface); display:flex; align-items:center; justify-content:center;\n  color:var(--ink-soft);\n  transition:transform .15s ease, background .15s ease, color .15s ease;\n}\n.iconbtn:hover{ background:var(--surface-2); color:var(--brand); }\n.iconbtn svg{ width:18px; height:18px; }\n\n\/* ---- Main ---- *\/\n.main{ min-width:0; }\n.page{\n  padding:34px 34px 90px;\n  max-width:1280px;\n  margin:0 auto;\n  animation: pageIn .32s cubic-bezier(.2,.7,.2,1);\n}\n@keyframes pageIn{ from{ opacity:0; transform:translateY(8px);} to{opacity:1; transform:none;} }\n\n@media (prefers-reduced-motion: reduce){\n  *{ animation-duration:.001ms !important; animation-iteration-count:1 !important; transition-duration:.001ms !important; scroll-behavior:auto !important; }\n}\n\n\/* ==========================================================================\n   Typography helpers\n   ========================================================================== *\/\n.eyebrow{\n  font-family:var(--f-mono); font-size:11.5px; letter-spacing:.16em; text-transform:uppercase;\n  color:var(--brand); font-weight:600; display:flex; align-items:center; gap:9px;\n}\n.eyebrow::before{ content:''; width:20px; height:1px; background:var(--brand); display:inline-block; }\n.h-hero{ font-size:clamp(2.1rem,4.2vw,3.4rem); font-weight:700; line-height:1.04; letter-spacing:-0.02em; }\n.h1{ font-size:clamp(1.6rem,2.6vw,2.05rem); font-weight:700; line-height:1.15; }\n.h2{ font-size:1.36rem; font-weight:700; }\n.h3{ font-size:1.06rem; font-weight:600; }\n.lede{ font-size:1.06rem; line-height:1.6; color:var(--ink-soft); max-width:74ch; }\n.body-text{ font-size:.975rem; line-height:1.68; color:var(--ink-soft); }\n.body-text + .body-text{ margin-top:12px; }\n.muted{ color:var(--muted); }\n.mono{ font-family:var(--f-mono); }\n.section-gap{ margin-top:52px; }\n.section-gap-sm{ margin-top:30px; }\n\n\/* ==========================================================================\n   Cards\n   ========================================================================== *\/\n.card{\n  background:var(--surface);\n  border:1px solid var(--line);\n  border-radius:var(--radius);\n  box-shadow:var(--shadow);\n}\n.card-pad{ padding:22px; }\n\n.bracket-card{ position:relative; }\n.bracket-card::before,.bracket-card::after{\n  content:''; position:absolute; width:16px; height:16px; pointer-events:none;\n  border-color:var(--brand); opacity:.55; transition:all .2s ease;\n}\n.bracket-card::before{ top:-1px; left:-1px; border-top:2px solid; border-left:2px solid; border-top-left-radius:6px;}\n.bracket-card::after{ bottom:-1px; right:-1px; border-bottom:2px solid; border-right:2px solid; border-bottom-right-radius:6px;}\n.bracket-card:hover::before,.bracket-card:hover::after{ width:22px; height:22px; opacity:1; }\n\n.doc-card{\n  display:flex; flex-direction:column; gap:14px;\n  padding:22px; height:100%;\n  transition:transform .18s ease, box-shadow .18s ease, border-color .18s ease;\n}\n.doc-card:hover{ transform:translateY(-3px); box-shadow:var(--shadow-lg); border-color:var(--line-strong); }\n.doc-card .top-row{ display:flex; align-items:flex-start; justify-content:space-between; }\n.doc-card .num{ font-family:var(--f-mono); font-size:12px; color:var(--brand); font-weight:600; letter-spacing:.05em; }\n.doc-card .ico{\n  width:38px; height:38px; border-radius:10px;\n  display:flex; align-items:center; justify-content:center;\n  background:var(--surface-2); color:var(--brand);\n}\n.doc-card .ico svg{ width:19px; height:19px; }\n.doc-card h3{ font-size:1.12rem; }\n.doc-card p{ font-size:.875rem; color:var(--muted); line-height:1.55; flex:1; }\n.doc-card .foot{ display:flex; align-items:center; justify-content:space-between; font-size:12px; padding-top:10px; border-top:1px dashed var(--line-strong); }\n.doc-card .foot .stat{ font-family:var(--f-mono); color:var(--ink-soft); font-weight:600; }\n.doc-card .go{ display:flex; align-items:center; gap:5px; color:var(--brand); font-weight:600; }\n.doc-card .go svg{ width:13px; height:13px; transition:transform .15s ease; }\n.doc-card:hover .go svg{ transform:translateX(3px); }\n\n.grid{ display:grid; gap:18px; }\n.grid-2{ grid-template-columns:repeat(2,1fr); }\n.grid-3{ grid-template-columns:repeat(3,1fr); }\n.grid-4{ grid-template-columns:repeat(4,1fr); }\n.grid-6{ grid-template-columns:repeat(6,1fr); }\n.grid-featured{ grid-template-columns:1.3fr 1fr; }\n.grid-arch{ grid-template-columns:repeat(4,1fr); }\n@media (max-width:1100px){ .grid-4{ grid-template-columns:repeat(2,1fr);} .grid-6{grid-template-columns:repeat(3,1fr);} .grid-3{grid-template-columns:repeat(2,1fr);} .grid-arch{grid-template-columns:repeat(2,1fr);} }\n@media (max-width:720px){ .grid-2,.grid-3,.grid-4,.grid-featured{ grid-template-columns:1fr;} .grid-6{grid-template-columns:repeat(2,1fr);} .grid-arch{grid-template-columns:1fr;} }\n\n\/* ==========================================================================\n   Stat tiles \/ readout strip\n   ========================================================================== *\/\n.readout{\n  display:flex; flex-wrap:wrap; gap:0;\n  border:1px solid var(--line); border-radius:var(--radius);\n  background:var(--surface); overflow:hidden; box-shadow:var(--shadow);\n}\n.readout .stat{\n  flex:1 1 150px; padding:18px 20px; border-right:1px solid var(--line);\n  position:relative;\n}\n.readout .stat:last-child{ border-right:none; }\n.readout .stat .k{ font-family:var(--f-mono); font-size:10.5px; letter-spacing:.1em; text-transform:uppercase; color:var(--muted); }\n.readout .stat .v{ font-family:var(--f-display); font-size:1.86rem; font-weight:700; margin-top:5px; letter-spacing:-0.01em; }\n.readout .stat .v .unit{ font-size:1rem; font-weight:600; color:var(--muted); margin-left:2px; }\n.readout .stat.flag .v{ color:var(--crit); }\n\n.stat-card{\n  padding:18px 20px; border-radius:var(--radius); border:1px solid var(--line); background:var(--surface); box-shadow:var(--shadow);\n  display:flex; flex-direction:column; gap:6px;\n}\n.stat-card .top{ display:flex; align-items:center; justify-content:space-between;}\n.stat-card .k{ font-family:var(--f-mono); font-size:11px; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); }\n.stat-card .v{ font-family:var(--f-display); font-size:2rem; font-weight:700; }\n.stat-card .d{ font-size:12.5px; color:var(--muted); line-height:1.5; }\n.stat-card .ico{ width:30px;height:30px;border-radius:8px;display:flex;align-items:center;justify-content:center; }\n.stat-card .ico svg{ width:16px;height:16px; }\n\n\/* ==========================================================================\n   Badges \/ pills \/ chips\n   ========================================================================== *\/\n.badge{\n  display:inline-flex; align-items:center; gap:5px;\n  font-family:var(--f-mono); font-size:10.5px; font-weight:600; letter-spacing:.05em; text-transform:uppercase;\n  padding:3px 9px; border-radius:99px; border:1px solid transparent; white-space:nowrap;\n}\n.badge.crit{ background:color-mix(in srgb, var(--crit) 14%, transparent); color:var(--crit); border-color:color-mix(in srgb, var(--crit) 35%, transparent); }\n.badge.high{ background:color-mix(in srgb, var(--high) 16%, transparent); color:var(--high); border-color:color-mix(in srgb, var(--high) 38%, transparent); }\n.badge.med { background:color-mix(in srgb, var(--med) 18%, transparent); color:#8A6E10; border-color:color-mix(in srgb, var(--med) 40%, transparent); }\n[data-theme=\"dark\"] .badge.med{ color:var(--med); }\n.badge.low { background:color-mix(in srgb, var(--low) 16%, transparent); color:var(--low); border-color:color-mix(in srgb, var(--low) 38%, transparent); }\n.badge.ok{ background:color-mix(in srgb, var(--ok) 15%, transparent); color:var(--ok); border-color:color-mix(in srgb, var(--ok) 38%, transparent); }\n.badge.neutral{ background:var(--surface-2); color:var(--ink-soft); border-color:var(--line-strong); }\n.badge.brand{ background:color-mix(in srgb, var(--brand) 14%, transparent); color:var(--brand); border-color:color-mix(in srgb, var(--brand) 34%, transparent); }\n\n.status-pill{\n  display:inline-flex; align-items:center; gap:6px; font-size:12.5px; font-weight:600; padding:4px 10px 4px 8px; border-radius:99px;\n}\n.status-pill.implemented{ background:color-mix(in srgb, var(--ok) 14%, transparent); color:var(--ok); }\n.status-pill.partial{ background:color-mix(in srgb, var(--high) 14%, transparent); color:var(--high); }\n.status-pill.stub{ background:color-mix(in srgb, var(--crit) 12%, transparent); color:var(--crit); }\n.status-pill.gated{ background:color-mix(in srgb, var(--brand) 13%, transparent); color:var(--brand); }\n.status-pill::before{ content:''; width:6px; height:6px; border-radius:50%; background:currentColor; }\n\n.chip{\n  font-family:var(--f-mono); font-size:12px; font-weight:600; padding:7px 14px; border-radius:99px;\n  border:1px solid var(--line-strong); background:var(--surface); color:var(--ink-soft);\n  transition:all .15s ease; display:inline-flex; align-items:center; gap:7px;\n}\n.chip .cnt{ opacity:.65; font-weight:500; }\n.chip:hover{ border-color:var(--brand); color:var(--brand); }\n.chip.active{ background:var(--ink); color:#fff; border-color:var(--ink); }\n.chip.active.crit{ background:var(--crit); border-color:var(--crit); }\n.chip.active.high{ background:var(--high); border-color:var(--high); }\n.chip.active.med{ background:var(--med); border-color:var(--med); color:#3a2f04;}\n.chip.active.low{ background:var(--low); border-color:var(--low); }\n.chip-row{ display:flex; gap:9px; flex-wrap:wrap; align-items:center; }\n\n\/* ==========================================================================\n   Tables\n   ========================================================================== *\/\n.tbl-wrap{ border:1px solid var(--line); border-radius:var(--radius); overflow-x:auto; box-shadow:var(--shadow); }\ntable.dtbl{ width:100%; border-collapse:collapse; background:var(--surface); }\ntable.dtbl th{\n  text-align:left; font-family:var(--f-mono); font-size:10.5px; letter-spacing:.08em; text-transform:uppercase;\n  color:var(--muted); font-weight:600; padding:12px 16px; background:var(--surface-2); border-bottom:1px solid var(--line);\n  white-space:nowrap;\n}\ntable.dtbl td{ padding:13px 16px; font-size:13.6px; border-bottom:1px solid var(--line); vertical-align:top; color:var(--ink-soft); line-height:1.5; }\ntable.dtbl tr:last-child td{ border-bottom:none; }\ntable.dtbl tr:hover td{ background:var(--surface-2); }\ntable.dtbl td.mono, table.dtbl td code{ font-family:var(--f-mono); font-size:12.5px; color:var(--brand-2); }\n[data-theme=\"dark\"] table.dtbl td.mono, [data-theme=\"dark\"] table.dtbl td code{ color:var(--brand-light); }\ntable.dtbl td strong{ color:var(--ink); }\ncode{ font-family:var(--f-mono); background:var(--surface-2); padding:1px 6px; border-radius:5px; font-size:.88em; }\n\n\/* ==========================================================================\n   Accordion \/ expandable finding cards\n   ========================================================================== *\/\n.acc{ border:1px solid var(--line); border-radius:var(--radius); background:var(--surface); overflow:hidden; box-shadow:var(--shadow); }\n.acc + .acc{ margin-top:12px; }\n.acc-head{\n  display:flex; align-items:center; gap:14px; padding:16px 18px; user-select:none;\n}\n.acc-head .sevbar{ width:4px; align-self:stretch; border-radius:3px; flex-shrink:0; min-height:34px; }\n.acc-head .idtag{ font-family:var(--f-mono); font-size:12px; color:var(--muted); font-weight:600; flex-shrink:0; width:60px; }\n.acc-head .ttl{ font-weight:600; font-size:14.5px; flex:1; line-height:1.4; }\n.acc-head .tags{ display:none; }\n.acc-chev{ width:26px; height:26px; border-radius:7px; background:var(--surface-2); display:flex; align-items:center; justify-content:center; flex-shrink:0; transition:transform .2s ease; }\n.acc.open .acc-chev{ transform:rotate(180deg); }\n.acc-body{ max-height:0; overflow:hidden; transition:max-height .3s cubic-bezier(.2,.7,.2,1); }\n.acc.open .acc-body{ max-height:4000px; }\n.acc-body-in{ padding:2px 18px 22px 78px; }\n.finding-meta{ font-family:var(--f-mono); font-size:11px; color:var(--muted); margin-bottom:14px; letter-spacing:.02em; }\n.finding-block{ margin-bottom:14px; }\n.finding-block .lbl{ font-family:var(--f-mono); font-size:10.5px; text-transform:uppercase; letter-spacing:.09em; font-weight:700; margin-bottom:5px; display:block; }\n.finding-block.loc .lbl{ color:var(--brand); }\n.finding-block.exp .lbl{ color:var(--crit); }\n.finding-block.fix .lbl{ color:var(--ok); }\n.finding-block p{ font-size:13.6px; line-height:1.62; color:var(--ink-soft); }\n.acc-head:hover{ background:var(--surface-2); cursor:pointer; }\n\n.codebox{\n  background:var(--ink); color:#D9E4FA; border-radius:10px; padding:16px 16px 14px;\n  font-family:var(--f-mono); font-size:12.3px; line-height:1.65; overflow-x:auto; position:relative;\n  border:1px solid rgba(255,255,255,0.08);\n}\n[data-theme=\"dark\"] .codebox{ background:#060B14; }\n.codebox pre{ margin:0; white-space:pre; overflow-wrap:normal; word-break:normal; }\n.copybtn{\n  position:absolute; top:10px; right:10px; font-family:var(--f-mono); font-size:10.5px;\n  background:rgba(255,255,255,0.08); color:#C7D5F2; border:1px solid rgba(255,255,255,0.14);\n  padding:5px 9px; border-radius:6px; transition:background .15s ease;\n}\n.copybtn:hover{ background:rgba(255,255,255,0.16); }\n.copybtn.copied{ background:var(--ok); color:#fff; border-color:var(--ok); }\n\n\/* ==========================================================================\n   Gauge (security posture)\n   ========================================================================== *\/\n.gauge-wrap{ display:flex; align-items:center; gap:28px; flex-wrap:wrap; }\n.gauge-readout{ flex:1 1 220px; min-width:0; }\n.gauge-readout .level{ font-family:var(--f-display); font-size:1.9rem; font-weight:700; }\n.gauge-svg text{ font-family:var(--f-mono); }\n.chart-box{ min-width:0; flex:0 1 auto; max-width:100%; }\n.chart-box svg{ width:100%; height:auto; display:block; }\n\n\/* ==========================================================================\n   Bars \/ progress\n   ========================================================================== *\/\n.barlist{ display:flex; flex-direction:column; gap:13px; }\n.barrow{ display:grid; grid-template-columns:150px 1fr 46px; align-items:center; gap:12px; }\n.barrow .lbl{ font-size:12.6px; color:var(--ink-soft); font-weight:500; }\n.barrow .track{ height:9px; border-radius:6px; background:var(--surface-2); overflow:hidden; }\n.barrow .fill{ height:100%; border-radius:6px; background:linear-gradient(90deg,var(--brand-2),var(--brand)); width:0; transition:width 1s cubic-bezier(.2,.7,.2,1); }\n.barrow .val{ font-family:var(--f-mono); font-size:12px; text-align:right; color:var(--muted); }\n\n.lifecycle-track{ display:flex; gap:6px; flex-wrap:wrap; }\n.lifecycle-step{\n  flex:1 1 90px; min-width:90px; padding:12px 12px 11px; border-radius:9px; border:1px solid var(--line);\n  background:var(--surface); position:relative;\n}\n.lifecycle-step .n{ font-family:var(--f-mono); font-size:10px; color:var(--muted); }\n.lifecycle-step .t{ font-size:12px; font-weight:600; margin-top:3px; line-height:1.3; }\n.lifecycle-step .bar{ height:4px; border-radius:3px; margin-top:9px; }\n.lifecycle-step.implemented .bar{ background:var(--ok); }\n.lifecycle-step.absent .bar{ background:var(--crit); }\n\n\/* ==========================================================================\n   Journey steps\n   ========================================================================== *\/\n.journey{ display:flex; flex-direction:column; }\n.jstep{ display:flex; gap:16px; position:relative; padding-bottom:22px; }\n.jstep:last-child{ padding-bottom:0; }\n.jstep::before{\n  content:''; position:absolute; left:15px; top:32px; bottom:0; width:1px; background:var(--line-strong);\n}\n.jstep:last-child::before{ display:none; }\n.jstep .num{\n  width:32px; height:32px; border-radius:9px; background:var(--surface-2); border:1px solid var(--line-strong);\n  display:flex; align-items:center; justify-content:center; font-family:var(--f-mono); font-weight:700; font-size:13px;\n  color:var(--brand); flex-shrink:0; z-index:1;\n}\n.jstep p{ font-size:13.8px; line-height:1.6; color:var(--ink-soft); padding-top:4px; }\n\n\/* ==========================================================================\n   Persona \/ value cards\n   ========================================================================== *\/\n.persona-card{ padding:18px; display:flex; gap:14px; align-items:flex-start; }\n.persona-card .ico{ width:40px;height:40px;border-radius:10px; background:var(--surface-2); color:var(--brand); display:flex;align-items:center;justify-content:center; flex-shrink:0;}\n.persona-card .ico svg{ width:20px;height:20px; }\n.persona-card h4{ font-size:14.5px; font-weight:700; margin-bottom:3px; }\n.persona-card p{ font-size:12.8px; color:var(--muted); line-height:1.55; }\n\n.callout{\n  border-radius:var(--radius); padding:16px 18px; border:1px solid; display:flex; gap:13px; align-items:flex-start;\n}\n.callout svg{ width:19px; height:19px; flex-shrink:0; margin-top:1px; }\n.callout p{ font-size:13.4px; line-height:1.6; }\n.callout strong{ font-weight:700; }\n.callout.danger{ background:color-mix(in srgb, var(--crit) 8%, var(--surface)); border-color:color-mix(in srgb, var(--crit) 30%, transparent); color:var(--crit); }\n.callout.danger p{ color:var(--ink-soft); }\n.callout.warn{ background:color-mix(in srgb, var(--high) 8%, var(--surface)); border-color:color-mix(in srgb, var(--high) 30%, transparent); color:var(--high); }\n.callout.warn p{ color:var(--ink-soft); }\n.callout.info{ background:color-mix(in srgb, var(--brand) 7%, var(--surface)); border-color:color-mix(in srgb, var(--brand) 28%, transparent); color:var(--brand); }\n.callout.info p{ color:var(--ink-soft); }\n\n\/* ==========================================================================\n   Module tabs (Functional page)\n   ========================================================================== *\/\n.tabbar{ display:flex; gap:6px; flex-wrap:wrap; border-bottom:1px solid var(--line); padding-bottom:0; margin-bottom:24px; }\n.tabbtn{\n  padding:10px 16px; font-size:13.4px; font-weight:600; color:var(--muted); border-bottom:2px solid transparent; margin-bottom:-1px;\n  transition:color .15s ease, border-color .15s ease;\n}\n.tabbtn:hover{ color:var(--ink); }\n.tabbtn.active{ color:var(--brand); border-color:var(--brand); }\n\n\/* ==========================================================================\n   Roadmap \/ checklist\n   ========================================================================== *\/\n.checklist{ display:flex; flex-direction:column; gap:2px; }\n.check-row{ display:flex; align-items:flex-start; gap:12px; padding:11px 6px; border-bottom:1px solid var(--line); }\n.check-row:last-child{ border-bottom:none; }\n.check-row input[type=checkbox]{\n  appearance:none; width:19px; height:19px; border-radius:6px; border:1.5px solid var(--line-strong);\n  margin-top:1px; flex-shrink:0; position:relative; background:var(--surface); transition:all .15s ease;\n}\n.check-row input[type=checkbox]:checked{ background:var(--ok); border-color:var(--ok); }\n.check-row input[type=checkbox]:checked::after{\n  content:''; position:absolute; left:5px; top:1px; width:5px; height:10px; border:solid #fff;\n  border-width:0 2px 2px 0; transform:rotate(40deg);\n}\n.check-row label{ font-size:13.6px; line-height:1.55; color:var(--ink-soft); }\n.check-row.done label{ color:var(--muted); text-decoration:line-through; text-decoration-color:var(--line-strong); }\n.check-row .tagref{ font-family:var(--f-mono); font-size:10.5px; color:var(--brand); margin-left:auto; flex-shrink:0; padding-top:2px; }\n\n\/* ==========================================================================\n   Q&A accordion (open questions)\n   ========================================================================== *\/\n.qgroup{ margin-bottom:10px; }\n.qgroup-head{\n  display:flex; align-items:center; gap:12px; padding:15px 18px; background:var(--surface); border:1px solid var(--line);\n  border-radius:var(--radius); cursor:pointer;\n}\n.qgroup-head .letter{\n  width:32px;height:32px; border-radius:9px; background:var(--brand); color:#fff; font-family:var(--f-display); font-weight:700;\n  display:flex; align-items:center; justify-content:center; flex-shrink:0; font-size:14px;\n}\n.qgroup-head h3{ font-size:14.5px; flex:1; }\n.qgroup-head .cnt{ font-family:var(--f-mono); font-size:11.5px; color:var(--muted); }\n.qgroup-body{ max-height:0; overflow:hidden; transition:max-height .3s ease; padding:0 18px; }\n.qgroup.open .qgroup-body{ max-height:6000px; padding:14px 18px 6px; }\n.qitem{ display:flex; gap:12px; padding:12px 0; border-top:1px dashed var(--line-strong); }\n.qitem:first-child{ border-top:none; }\n.qitem .qn{ font-family:var(--f-mono); font-size:12px; color:var(--brand); font-weight:700; flex-shrink:0; }\n.qitem p{ font-size:13.4px; line-height:1.62; color:var(--ink-soft); }\n\n\/* ==========================================================================\n   Command palette \/ search overlay\n   ========================================================================== *\/\n.overlay{\n  position:fixed; inset:0; background:rgba(6,11,20,0.55); backdrop-filter:blur(3px);\n  display:flex; align-items:flex-start; justify-content:center; padding-top:12vh; z-index:100;\n  opacity:0; pointer-events:none; transition:opacity .15s ease;\n}\n.overlay.show{ opacity:1; pointer-events:auto; }\n.palette{\n  width:min(620px,92vw); background:var(--surface); border-radius:var(--radius-lg); box-shadow:var(--shadow-lg);\n  border:1px solid var(--line-strong); overflow:hidden; transform:translateY(-8px); transition:transform .18s ease;\n}\n.overlay.show .palette{ transform:translateY(0); }\n.palette-input{ display:flex; align-items:center; gap:12px; padding:16px 18px; border-bottom:1px solid var(--line); }\n.palette-input svg{ width:18px;height:18px; color:var(--muted); flex-shrink:0; }\n.palette-input input{ border:none; outline:none; background:transparent; font-size:15px; flex:1; color:var(--ink); font-family:var(--f-body); }\n.palette-results{ max-height:52vh; overflow-y:auto; padding:8px; }\n.palette-item{ display:flex; align-items:center; gap:12px; padding:10px 12px; border-radius:9px; }\n.palette-item:hover, .palette-item.sel{ background:var(--surface-2); }\n.palette-item .pic{ width:28px;height:28px;border-radius:7px; background:var(--surface-2); display:flex;align-items:center;justify-content:center; flex-shrink:0; color:var(--brand);}\n.palette-item .pt{ font-size:13.5px; font-weight:600; }\n.palette-item .ps{ font-size:11.5px; color:var(--muted); margin-top:1px; }\n.palette-empty{ padding:30px 20px; text-align:center; color:var(--muted); font-size:13.5px; }\n.palette-hint{ display:flex; gap:14px; padding:10px 18px; border-top:1px solid var(--line); font-size:11px; color:var(--muted); font-family:var(--f-mono); }\n\n\/* ==========================================================================\n   Misc\n   ========================================================================== *\/\n.divider{ height:1px; background:var(--line); margin:26px 0; }\n.hr-dash{ border-top:1px dashed var(--line-strong); margin:20px 0; }\n.tag-row{ display:flex; gap:6px; flex-wrap:wrap; }\n.small-link{ font-size:12.5px; color:var(--brand); font-weight:600; display:inline-flex; align-items:center; gap:4px; }\n.small-link svg{ width:12px;height:12px; }\n.fig-label{ font-family:var(--f-mono); font-size:11px; letter-spacing:.08em; text-transform:uppercase; color:var(--muted); margin-bottom:10px; }\n\n.back-top{\n  position:fixed; right:26px; bottom:26px; width:44px;height:44px; border-radius:50%;\n  background:var(--ink); color:#fff; display:flex; align-items:center; justify-content:center;\n  box-shadow:var(--shadow-lg); opacity:0; pointer-events:none; transform:translateY(8px);\n  transition:all .2s ease; z-index:20;\n}\n.back-top.show{ opacity:1; pointer-events:auto; transform:none; }\n.back-top svg{ width:18px;height:18px; }\n\n.legend{ display:flex; gap:16px; flex-wrap:wrap; font-size:12px; color:var(--muted); }\n.legend .li{ display:flex; align-items:center; gap:6px; }\n.legend .sw{ width:9px;height:9px;border-radius:3px; }\n\n.hero-shell{ position:relative; overflow:hidden; border-radius:var(--radius-lg); border:1px solid var(--line); background:var(--surface); box-shadow:var(--shadow); }\n.hero-inner{ position:relative; padding:40px 40px 34px; z-index:1; }\n.hero-grid-fx{\n  position:absolute; inset:0;\n  background-image:linear-gradient(var(--line) 1px, transparent 1px), linear-gradient(90deg, var(--line) 1px, transparent 1px);\n  background-size:28px 28px; mask-image:radial-gradient(ellipse 80% 80% at 70% 20%, black 10%, transparent 70%);\n}\n.hero-glow{ position:absolute; top:-140px; right:-120px; width:480px; height:480px; border-radius:50%;\n  background:radial-gradient(circle, color-mix(in srgb, var(--brand) 30%, transparent), transparent 70%); filter:blur(10px); }\n\n.typewrap{ display:inline-block; border-right:2px solid var(--brand); padding-right:3px; }\n\n\/* Mobile nav toggle *\/\n.menu-toggle{ display:none; }\n.sidebar-scrim{ position:fixed; inset:0; background:rgba(0,0,0,.4); z-index:39; opacity:0; pointer-events:none; transition:opacity .2s ease; }\n.sidebar-scrim.show{ opacity:1; pointer-events:auto; }\n@media (max-width:980px){\n  .shell{ grid-template-columns:1fr; }\n  .sidebar{ position:fixed; left:0; top:0; bottom:0; width:272px; transform:translateX(-100%); transition:transform .22s ease; box-shadow:var(--shadow-lg); }\n  .sidebar.open{ transform:translateX(0); }\n  .menu-toggle{ display:flex; }\n  .page{ padding:24px 18px 80px; }\n}\n@media (max-width:640px){\n  .topbar{ padding:0 14px; gap:8px; }\n  .searchbtn{ min-width:0; padding:9px; gap:0; }\n  .searchbtn span:not(#searchIcon), .searchbtn kbd{ display:none; }\n  .crumb{ font-size:11px; }\n  .crumb span{ display:none; }\n}\n\n\/* Print *\/\n@media print{\n  .sidebar,.topbar,.back-top,.overlay{ display:none !important; }\n  .shell{ display:block; }\n  .page{ padding:0; max-width:none; }\n  .card,.acc,.readout{ box-shadow:none !important; break-inside:avoid; }\n}\n\n<\/style>\n<\/head>\n<body>\n\n<div class=\"shell\">\n\n  <div class=\"sidebar-scrim\" id=\"scrim\"><\/div>\n\n  <aside class=\"sidebar\" id=\"sidebar\">\n    <div class=\"brand-row\">\n      <img decoding=\"async\" src=\"data:image\/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADRCAYAAACJgf3NAAB6S0lEQVR42u19d5xdVbX\/d+1z7p0+k0IaCUmAQCAhkQ5SHFR8omChDOpPxY4ICGJD0ecQO4pYUNRnA5SWoKAiolgydBGkh14inUDatFvO2ev3xzn3nF3PnRQEYe7n8YTM5M6de\/faa63v+q7vFxh\/jD\/GH+OP8cf4Y\/wx\/hh\/jD\/GH+OP8cf4Y\/wx\/hh\/jD\/GH+OP8cf4Y\/wx\/hh\/jD\/GH+OP8cf4Y\/wx\/hh\/jD\/GH+OP8cemPmj8LXjRfxY8\/taMB8hL9MGE\/lOpb8VCeuaZKYQD8q9MXbGKly24i7HkVAaIm35OzOg7cpl4ZsGU5DNbnj7P1A16nvHHeIC8wIGwYApNXXEAL1sKCdrAA9vPom\/FsuwzWbasT27ooe\/rWxo8s2AKYflyDBxwqsQSjAfOeID85wOiry+5zaeuWMXLlh0Zu76r99i\/d0Zh16QgjmfGQTwdEjNlHM8goi0AnsjgHpayE0AHgcsSKBNYgBnMDAbXAK4CNMrMwwI8yMxrwfI5SfQUMT1OFD\/FKD\/WMlJddc35h6xxB16\/6F1+gMizzRI5\/hmOB8jmfZ\/6mXqxXLgCom8pB49fffNcEmJ74toiBu0E8LaQcjYzpoggaKGgBUQEMIMJAEuwjMEswSwB5uTPmJO2g9NPhxkgyj4qImoED8AMGdUQx9WIGGsZ\/ChYPgLQnSRoRRzJFd2d1YcGzjpyyAzw3t7lwdSpq3hjstR4gIw\/AID6+paKZ56ZQgMDr470zHBnZy0YWSxIvJKI92TwzsQ8V5TaykKEyaGXEWRcB8sILGNmsASDkwBo9N6cvP9JcFDypTxAGkHABEYaOMkTpAGU\/K8AQRAJEAWAECAS4DhCHI2CmR8j0F0M\/ifJ+PqI5G23nXvk42ZZtrEl3XiAjAcFANCex9+wmOL4ABLi1SRojyAobxmW2wAGZFyFjGvgOJYMyKwvYaYsCMBJALBUAwRZUDCnZz4LhPTbmAEm9WuNoGoEEMDMnAYPZX+ZwByQCCGCECABGdch66ODYL4DQgxIiq9sibtvvvG8N64fD5bxAClojpMaXQ2K3v6\/h0Orwr0FizeD8T+CsLjU1p1USXEdkBEzkJRaLEUaCGTe\/tphdn0tCw4lKzTOePbfUEovbvxJHjDWn2Xfz0kMMjNLJhARUUBBCaAAMqqC49rjzDxAJC4NOP77zRe881k1WF7uPcvLO0D6lgZ9ANSeYucPDewTgN4KooODIFwQlNsBWQfLGggiIgFK6xnKb3vjcCrZgbSbHkp2UINHyxyN2z99JmZwGniuTJOVaGwHkx5YDICYJTOQNDvMAQUhiaAMyBhxXHmaWFzJkL+uDI\/+9d7ffXBQBSWWLTtS4mU2l3k5Bgj19S0Vagmx4N1Xzm4plY6AoLcLIfYIyx1gWQPiGkOIWBAJEoKECIiIlEwgtVveKHusm988vGbmYOUgO0qtPPjMr7me359V9OeSLJlYgplIBIEIWsAcQ0aVR5n5koDFr25d+u5\/alnlZVR+vYwChKmvD2LZMsqyxaKj\/vQqEZY\/APBbSy2d3eAYHFeZSMRCpEFBgkgQBIn83fIexjxgfA2387\/dwcJJD2OXaPbzq5kmRcj0niVNTEyuTMaNH8wMhpRgEAWhEEEZsj4KZl4uiX\/eOlz79c2XfXjk5VR+0csnYyRl1IK+peWgbcLhJMQxFISvCsIWcFwBwJEgIYQQgkRSQYmknkJSUanBoR\/Q\/JbPz4oG1xq3PbMnOBr\/bnwtzy5wZBVXsCpfb2Q6x8+xg099LsnMLAEKRNACEEHWRx8kQWeHFfnzW373wScAoLe3PxwYgARemoHykg6QpBxIAmPGIb9r32Jy6ztBdHxQalsMAByNMgkh08AgSgNCEKERJGpw5CCReXs7egjrQNvfq2cTrcxylkSu4GCr2S8quXzlGwDItEfJg53zgI3BDArCQIRlyNroaiacE5A88\/ZlRz\/8Ui69XpoB0rc0QPphzTvo8pbSlPB9gvhjYbljPmQElvWYhIAgCtL\/TTJGOkMgApI\/N8uqpj2BdgD1Bl7vWVywrZVJsu83s5BUYsaRNZwQMhf3SwwDaVN\/tmz0+ZLBUpAIKWxFXK+sI8Q\/r9drZ973+xMezjPKkvil0sy\/tAKkv19gxUJCo5x61+XvBIWfDkqti8ERIOsxZa1FEgCUZgvRyBZpSSUEOcqlLCUQFxxs62bP5nxsZQnr1tcDL8tG7syRZQlW+wstGLwomhVM+nM4y7fs7zODYwKFQdiKOBodZMnfr1fr337gihNX5ZeUm4IzHiAvwKO39+9hY44x\/\/9d9moS4alB2PIqggTHtUiQEBQIIbIySigBkZdVja8n51Iah5Upv2l9WSUd7CkHjbQD54CFnSWXcXC1W77R7+gZyv38+evTeptG8LCjD9J\/vv29WeBIBhCjESj10SeY5VfvabnqR1i2LEZfX4BlS\/+ry67\/\/gDpZ9FgrM5\/2yVzRdi6BCI4SgQhEFdjApEIRDa6EEmG4CSNNHoMAgniBmLlbmJ9aJKrZHE310pAcGPC7oRezdvbeftLK9PYsK8+JzFnKsm3S6P\/0UrCrGS0fw9WKjbJzByTCEIhyojrozcB8pR7fv+xK\/\/bs8l\/d4D09ocYWBIBoPnv+P0JoPALYbltEteHGUJIvccQaY+RN+BCzSDpnzlhXA8023zG4biN04NKDeqJN3P4ex29X\/D0M06kzZFpknKJYAUHN5+vZJml8fckMxCLoByCJTiOz2ZR+dy9vzv5CfT3CywB\/tvQrv\/OAOnvF40FoW37LtktLJW+E5Q69uOoAkDGCSQlSIi8r0iCQWjllPnvvsOVX9LcqJmIeSwzDgPF8tJQ1JscCbvXRKZcz+cEB1DQczSQqvTnwZ6024HvKcPSfyczeJL4lwymoNQmZFx9SiL67P2\/\/+TZJrI4HiDPS9b4e4ik16Dt3nbZKULQ\/4qwpYXj0UiIQAghBJCVUnCiVA0oN88gSunhzxwmX8oL43rmH44yzHF75wHByPue4udn5bksdEt7DhOG1pA1btKD+Ggv9vczMxMDkSARUlCCjCq\/jus46cE\/f\/LRtOT6r6Ct\/BcFCBP6lgksOzLe9vBlC4NS2w9Eub0X0QgAxEKIQBgoVNZfEHGSVBS0Kg0YFcY1a3BAGjMOR3C4SInW97ue34UuQSHjFs9b1K8zMxNAY+tfPGWYKwCU51AyR+MvEft6J\/W1smQGy6DUHsio+gw4PuG+P558UTqoCrBsWTweIJulEScJAPP6fvN+EbacIcJyD0ejkRAiSMqpLBiyTCEEMZEgH5TboFVZh6cIVnXNMfSaPYWB4W++jeGg8+d5skrzGUeTMq\/J17xBnw8NrfeJDCjY5KilC15JNqEQMq7+qC1e9cnbr\/zWsNJHjgfIppRUMw75cXt7+4zvheW2DyCugsBxyh6EIMFJHKiNeJpJyOxDFF6VfkC9MK59SByHUc067CmD7Cyj9AQ85uBwz1XgpJIYN3uapJpR7Zt9TWaZRi\/LPIPTbMYDyZAsSu0BR5V\/xVHtvQ\/95Qt3vJiDRLzYZxsYeHU057CLd+js2HKg1NLxAY5GYwIziSAQ1JhbJBmkERwinWc0GnMh0j9LMwuINFJg+iGTdrMrmSM\/L43vNQOncSiy4OD8vx3fnwdM2uxLPVMpgWUiW6zFlvH9VvkkzbInn\/Kn672svXa1vNMDUuOQZcEBLZNogc9pWaxnUQEgiGvDESjYlYLSNdsc+KV3YGBJhP5+AbWHGg+Q4szW17c0GBh4dTTv8N8cUiq3XCvC8u6yPpSUVCJFqESKQAm9r8hKqpxXxXlppQ7ZGA3GLFuH155p6CuyyvebEGr2nP6yhzVkrPH\/2FFyGYff2jvRmm8d7oUnCzEcnC0lsD3Bqpd4aWDnT6a+b+Qepib\/EBDKqBITy24Rls7f9nVf\/FrCCiZOAuXF8whelM14P2jFWcfLeX2XnkRh+RxB1AZZj4UIQqGWUem\/K5QRTjiHefYgITglreciCBZSVcSOtcskc1joKHnyZtczAGykqaS\/Vvlbemnnmrd4puOUrecazb2jXEyUIAzkKVnt1QNTKyG19wbOASPn9Rm5epa8RIVglpJlJEWp7VUT5u6\/aMZWO16+6pzTq+jrC7BixYsC4XpxpbT+ftHYL9jmyEvPLJU7jpf1USkIIBEIkQ71LJRKCBZEZGaQbBhoBIe\/aTVqdPY20J4a3Oof8n1ys08wJ93W4ZPwT9qdzX1GSWEnLGz2IM6deI2P5WQRZCWUA4jwDSM9vy\/nk8ooKLWW4nr1piAaPvSBgW889mLpS148AZLSEeb0\/qI1nDrpV2FL5+GyPhQJShhUGpFQKaGUIEng3JQuok3H81NFbJQJKgLjhnHTw+qiYzipJnA29jb06ppJ+CbzNpmwGe3FutnzRtkBHDR+tszLPTZmKs3mPebzO98Lc+FLHY7GkQhbQxlHj8ioeujKga\/e+mIIkhdHgKQw7uyDL5tY7pAXBy0dr5G1wSgpqZTdDBdtxOw7KB8Q5uuxRVwiz+TctfHHZs1vbPzZ1A4H90nhOSnDSf35ZQGhEQXsYZPQ6EHhbM6VdzbiKjmLMqA533GXh1bGTUtDGYmgHMo4fg5RdNgjV3\/lqhc6SOjFkjm2fvv50wLuujwote3K9aGI0n6DNLpIY7iXlE5qkORllhIcDnati02bLQc13dbzwZ\/Im9YiKon630oAueYGrt6m8AC6SkKLlgJr791L2\/c09w6qffE8JynqDIDAN70HmGVMIggArsgoPmzlVV\/9I3Y7uoSb\/6\/+8mvSG8HxpvOnBaXOPwWltl24PhwJEYYiRacyiJYEN0qtwCQeJv+uQL3I9yhcZYlVI8OQ4XEcVmedLTWelBV4UOgf7tKD7DIIPgTNIjBqMGxeorHStNsoE2ulZs7hcmalgqyqZh6oSBs7\/tvT3Oevi5WSTkDGEoQSCXF4z+y9bll3w5n3orc\/xMoB+fIJkDQ4tjn011OD1rY\/B2HrKzgejUQQhmTMLdKSKu9DhDAIhxmaxYkcTxYc7omwyXGCSd921dnWLciN2QkcEKzNs4ID7SkODi4IjgKmMUGjvrun4xlszcZh1kUhLC6Yr4zTCJV6lktbPcdg1QyOxt8nEEspiVACwiMmzNrz5nVXf+2+FyJIXpgSK+05tn7T+dOC9q4rglLLzgltJAi1niJDq4xpuNV3NJAq0pQLjQY6VwlxHVA2yyAvGqNDqUV74GPvZ\/Tb3NcfFcC4Y13I8vcXcPLD8jJLKpe\/9GYaHVVTy7iCHRpvSSslKBDMqMRx7bDHr\/3WH\/\/TPcl\/fijT3y+whOS8d17eHbR3XR6WWrPgyPsKYXCmhBYIOStX2QAknV6hzgk4H96xfwinfj+M\/W2t91B48fqGnavs0UsuK5OpWrsKm9iJHOmQsXJ4C4MD7Pw9lOLGeevrLAI1YPReykb+oGSy7GKCOgq1spqCuhlImWBZl4BsDUT4m5n7fuIADCyJ0NsfvkQDJDlc8w66vIWi2sVhuX1XGY3WG5kjb7hJGwAKoUK7CmVdqOux5sTXbC4bPELHxNkqIdSAkQrdHepuCGnTd3U6b\/GR8pVVtgiR2UFKz4qEax02AxpS+gizWePbzFzzvbAYt47fN2MCKK9bC1wtI0s\/jYZ1NrEVDNbOPkN5E5TPjQTLOAa4NRDiNzP3PekVGFgSoa8veKkFSEJXX7JEoqv6y6Cl53WyPlwXQpTSIOBGAAhlPdbgV3GDWpKVVRkKZRwYjUskjbpbD4C8BzF4UE0PV666bt\/G7sFZ3kcrr5Pt29n8PfLAdj+XWoZZk3xrDwXusodlnjuYyQIrtOfKl6vyXyEPVCVzUZZFzM\/GzoDO4SsBAcdRDNBEQeIPM\/Y9YTaWLYv\/E7SU\/1gP0hBV2PbI35xRap1wEteG6iRESTQGfCKlpft6DXV6nvYlmiROAWuVnZnBDZ36cH2y1lxZFS0kFKzA6j9LleyRBeCAf5GJC36PwiFe4bouHCWdOlgtGIYqr5PIpN34UDELFXQuj0Eb5DbmJLXbWqP4VQ\/c+L3BxhLkf3UGyYKj79fHlVt6TkqgXBEq2cEKDpHBt0KfmmvBoTBmdfYtWVCocgGbB0y9XUmprNUDwHqZxGAVgXHd1Mphblzq+v62khDyLGdO7sEeFRLtYMPuOZjzrOq62a29EKllCS042JNFrUyt9k+AljkcvYoxfKQsFSnvJWeXCDNAYVyvRCIov2I0EL8CiNG3VDyfF\/3zXsf19S0NLr\/8kHj7wy95bVBuO5+5LomQ0UcaGaSxQy6UNVlBBqQrBBORkfpRcBsaEKx+s6eoluMDcyl5qJdUzsMi9bn1TGBrW5nPZZQ9zE7tXO1wGigWYJeLbhqKVe\/nL4DsAaGhUA89MNkICms4mT+PsuMOBTKW+v2VRwVZwILeF4EECSmjughad+yavnPX4JWf\/tPzCf8+vwHS3y9WnHW83PGwi+egte0KQdRBkEhFFRQuVboRKIQWEMKYd1BmOeCeIGu4v+MGZu1AGGxbs7k2cH21R2k2vYaGhIE0Wor1HNnfJadZjn7Tk46a2XMSdgwH2QlaGGLW6qWjl6O5LhcKZIUcpakB41IT0yCCqgHmfG+z1yY4rsdUatmva8bODw9ed\/otzxcD+HkMECZMXSUWLOwrxaXWy8JS2w7MNRmIIBBKyaQ04rlWldKYa3OOMfKSYA3Jss+a9F0I943o3CU3G2Tv3AAuxirBeQPDmOQbAzM4ZxYaLGzOXNy3uhHkcMxwzB4hf+fIau7ZZhxAwxDYep98dg3WQlcTdnB+UXCqXEdv6J6552WDV579JPr7BQYGNmuQPG89SG\/v8gDLjoyjoPytUmv3K2U0EgkKAlLVRnTIloS6DajMQ8wZh\/mhGB+CMgSWjg9BqbPNjTst3Rv9iYf6bg\/VpFu0QZvPsI1kQVmiUgw9HQ052cEBBxuYHQ2xBgawtfCloWpSR9w0WFkqeVrtFzzCDR4BCja3Ns3ngmtGAmVGEoNItDHkhZP3+VRXoru1ebcSn58M0rc0WHn5IfH8t196RFjuOr0hrpD3HY1GnFQZ0DSDmGJuajxkpQq7ttaUVO3hEplIjJf3ZOyE2FQL1wScXVkmx0i1EsJg1+Z9Clw3scMpyuJhAc3LT9YHnq5ezlMGkRsmJhc507tEZbCXHX1G40exetB12koGijAAwTKKglLLVFEbmjn0xBmXoBebtR\/Z\/AHS3y9w1nG8oG+32SiVLyNCC0FSqlmlZIYG3ypooFQkhI5iOdCZRglCpsGMWkr56RLpyiezozQwbz4YG3YueBLG4pA5IbY5Uk6VQ7XZb25vQHpmhB3wOlLF5vOzRhgslgJCcwKjAXa4nh+Ji6\/5PrkROrKFuBk620C9UCA4rkcUtOzSMX3xw0PXf3uz9iObu8Si3uUHCIBYlsTPwnLbJHDEQgQiQapICYxc2VAIWw4U2mXjrFnJnsS62auWZI5G+zD1o6ANrWySohZMpJCr4NrdNnfZvYxWVmBkVznHrhVaOEQdWHs9dnA4Vo19vCoo5aprocx439gxczFU8RkOsW3lTaDmUknO\/itgWZcQwfem73LsnERra\/MMETdrgPT1LRUDA6+OdnjH748vtfYcKOsjURAEQToIzPsOla3b0LBK6OzIxaOlzpPiRNGSNWE1aVEuVJUQ1oh2pjKHo0lUaCXuqa\/avyg\/IS339ANd5HTrW0fN6eoWFQOqsIJVeli9TS4KAZ2awibtRfrZw8wGZV4qL88oDbMZhhoLUmV1kRoAmuuVg7VgitLpS1pSp\/AAxDJmoqCHqfrThLWxgl5cJVZ\/v1hx1nG88Ki9thWifDEjDgJCQCKgxNWMKJuO57R1Tv871XfziEfnZ8Kz4wCPTYBOoLOXnKQXCVOx+8ZqrvE10qDXIksDFOjlwvIMIc+CVKPXocK9jYwh4VsjVup4V0la6GoF\/abXdMR8ontQslixNR3ARdnFgUhqXxMso0gELfM6Z+z09NBfzrtxc5Ramy2D9K1YSAAxx\/yjoNzaSRyDUlfYHNLNM0iaNVKTzBStcvKYLP3XjNdmoCvkSMVunlU2oTVmA7ZEDeXiIy6SnbJ3ogOmWrNr61iZt70ZyHCRLckpDKcGZnavs8Jj0pEpVvsFZnsjcAwbiTr6xJoXomnh5tL7zZEw42vMFjOZnSWmcREppZaMq5IlfW3GbkfPxrJlclNLLbGZSqtg2bIj44XvvuzdYWv3gTKhrwf5jENoe+VqqUUNpAq+N4EVPVgNAwd8u9dGqaC96c5byaNHxVkgkYFKZbumGomPTUjYQSvxGXJqPYJSIrp4Tw4RBZc5aMZKgzq9Nl47\/OxbfemqwII6E3R3Ag2sB5N6Hyns5MLAdL8XbJNHk0WrIOyJ4sp3AfCmllqboU7rF+gHdnhov4khyTtFUJqWNOZCmAtNpj6u6QFYqCflkPZn1165xx+wWGhaZbQmIKqbFOjiRI2hqbQ+dDM4pG3m2XQY6hFPUP\/bQW50v3e+paaiteNmLAK\/X3tOgpYWjKswHTzPj6z\/hCdDMSMmEQbMtbc8c8u5v9sUkWyx6dljIWHJEhmgviRs7Z4OjuIEtRIgERjBIaBS2rMBoIa4GES1fFHAvsFMZMpz25jfnxMO7bKhkBToKAPM18bskgyVlk2zXUq4Mqd\/WOl4L9h6Lzy\/B7ufi62ST2HP66wDs\/8BPPYO6lRHa8qV4NAQN+fvBv21sgFz2++rJGbJiPn0WbP62rBsAW9sMtikJr2\/n8VZZ+3EOx31l8VhqfwjyFriHEuK1YDQN\/8aTbo1HXcKKyTNmK33BLi39pySmxqXiFUekncACO\/QS\/kXlYKR\/QwqHoQV3Iym2IHRuPp6AhUiZRflxMyi2QswRbNJf6\/UPqtoUAqHJZsWNSZ\/Ljfq0WdEBa5W9tfMLGmSSVnGsQjLU6IWOTzy9Pev3tiGfXP0IAyKvi5KLWWAcyFpZRPQhHYtCVCvv0ZBnc05XGgT\/9RsIMnZ25hi1PoNhOIlqpwBm9KxybzZ8+U4d39kzzjgX9JKNyFNNCn\/XaVyuxqvlQ1h7Qy+lXDYw2lAR5I9ZAOlcPdOlnOvBp6QG\/hgPTjYJlla\/u5sbobCdy4aME8g45oExMlb7PDOGRvbsIuNL62WBkuWkFz0visPLJU73sD1UcXERtfP1SRAzdSopHdzYxnw3OwOFRI25wpspn+YdJLGwTbW9cw9EUf\/YGY57TC6B2HsgIJtSRy1n7JWYQnmZp6Z1NjDxbIHgE69K\/31Kze7Ru9wuErBVqQ3LwqV72YOde35kEW5IecF6NIBQ2NdGMQyliRKPQjkFza2Yd\/4Jr2fRd+KZXRvx8R\/lFrad5NxLRaCgjwYdE+OohnHmJpGX0Pd3OYMtuGNSY7zfc2kUrB\/QOfy9PB6bHBO4UqpL+zyQ9RfX87B4mJGsUUMLNQFc+yVmFQZz0XhVStxHXaX4qOpNql6MzrXAhwsazZBDkvxngHUEPHiZ+++4AGgnzbESHSjMkhv799DLCF5X+ekN5daO3eTcTVOe48c0hWUkRHVGYft5gQbmjW23pjZkLSya3Sy9zDYRcFmVz2rTZcBS8DAibvDYtNaSiDsKdGyX11qg0CTMKi1Yo0NPwPaZKv00EtQey3ZzLiwty0d\/DRbm9ej9cvOnsBJfWdte9AwLmWfIgvYLA\/NnssohSWJsFVSvFFZZKOa9JXvnYv+A5bTvesf\/mUYlmcAzCLBdfW+gwJH5mgycXbyeIyJrcUqBeJYIhCkflCaYyv75W4UOR0P+9VsCptBmyrjy5oNqM+hsXsJhbbQ\/umy8b4pu93+yb2lU1zomqUwqX3LUerPSxnYURxDCCpsvm34HU72tPKbkDPgrPdFnbhHDBI7tk1Z+OvR5UtXAX0BMLaGXWx478EBliyRv\/\/3X99Sau3aTcY1mWWPfCjImgegb7ClD+jYHrRZrFG9oc2sjCX3dISoVCMIMvax9VuQ\/VNZGxzQlTzMIIdJNWedtc2p7TI7+WJsqpoY1HNrxqFbGLgzmQUJmwRJdUAoPaggp8LaBgHTqULi2OknQMYSsYzR09WGOI5zWQUu8hZxHHY1k7BksM3EZthZVN9pZ2JmSSIok4xPTrLI89ikL1twalILCzpZ8xlXdjzyDUAbtswwBnXSyirPyiGsoMGy+tcCAtYP1+kDh2yLI187B6vWVBAE5G7iGA6DGH3FVYOKHMNKdlBToBD79NKjsW6rXxRKGiN7BuNEl9RsQXChdiYj1m3BQGxkcrthBpyW2Ayy+wep\/ZxE2JKxdv0wTjv5bThg7x0wODQCIQguizgj+zAbumYGGKDLyVrvk9usJ\/0ZAcd1ZqIjpyzom7chiJbYsOyxNMCSJXK3o5cfELa078VRJVn0yNi4mbUyGTZnrPnl6b9Yom4OU73PR98wa93kxiqXBE45aie843Vz8dy6CoLGh+LIEk6eF9z71OyZTmdbdcr15aC5sMqIZWYv7GttQ3qGl7p4gmNI5qSH2+8dmZihtQ3JpL9WaWc9JZ0KIsRxjOHRCr7X\/y686637oFKpZavSbAjRccrORg4iG++TfVGo5bJ5iZqXglG6E0sZkyi1RTI+AQCjd7l4XjJI8tNwYhC2gEjIzFE2dZnVJqjaEAs+xqxGnHMwQYtuy+S6I6AWJQf2yx\/eBe8\/ZB5Wr6sgUSQ1lAZdW3js4Gyxi1XqUA7Mfjep7bQqqonsPOzuHoHcG4AmpCHt92JMNtCwjYFgQdRs7agYjTbnl0EiCkZAHMeo1ur40Zffh\/f3vQpSNhPK1tjZpNpNFS1pMdu9IrNrs9IoAcEByxoD\/M7OeX1TMDAQjeX8jz1A+lksW3ZkvPsx18wXQekNXB9lIShQHWWhCCvk0JNsepuZAzr2ktHsHeUGjaOxRlKPJD7z7kX44Ju3w+r1FQgNJNAHjG6pUSazd2HHBFzfQJQmUqVSZixvEod2FjvLBVZ6LXVH3cwc8Dvpuvbg1YBgtgKJfCZDVvZlJiEIUZQEx4+\/8j70vXEPVGtRWlZB6xG018+Sjf6FdBp+RqNWyZuMgkGy8pmyhvI2ykspYwrKk4IwemcKx26+AOnFqSIN+feX2rrKIMQi2yFvTMel0TOM7TbTWRG2xKaT4u2EElPwVDI+\/a7F+MhhO2D1+gpS4XfSNgI1kQeDfarZz45V3sdX\/yplo0s82tir8O9HWPCzzu5lz7TeKLkYLj4XnCwEi9Vg\/OxAENfrEeI4xs++\/gEc9vrdEUUxwkA4UUC9zwLZpZrCB0suE2MvHeT\/HMwVZiPQk3kJQcYgjo\/GbkeXMDAQb6YAYRpYsiTa5\/3XdBHwLo6qmbYVCYJL3SPHndw3o\/HCDXqC1HcpXHsi2kwkn\/uIdK1XSsbH37EIn3znIqwbqiZ1NzmHXmTcvPp6rPVawT7qiKqi6Fr4clHxXTdjTgEHLAEKn4KjNdFWyyQ4oFRDSshFyPTQYpgZgSBUanWEAeFX3z4Gbz5wV0SxRBgakwN2VQCwwJBGAOjvk1ECQipZxcNcsPoxTbA+kHFdEoU7Thx6+jUJolUsgj2mAOntXx4AQNxGB5fberZkjuKGWaYJzxkfOqlLTk5jeshCyE9\/A9XmXToOHTKokQQhlowPH7ojTnnvzlg\/UmvIajgOnD2bYU0sTYNGCT5eUoYimSoknv4l\/3tUNDxt9j7pqLMDCChaHUaDqKiuevlmHGnmCAiVSg2t5ZDO\/+6xOHDfhUlwpJmDDeauNQeB82Jy9nm2tGlWmXDOlG7CWtCztgQRpIzfn8CymyGDHIADZPrN7yFBTBQwCTJlbF1wZKOEJTShLJi4P5uq7ObqrFFymU4d1EBWJOO9B8\/H5967CwaHa85mmW2eF2npnh2MWlMcAdLSk82hVfO1ul1vkypVZmLZTgat1z22odhiT859BpxmU8zsQqg0RgMCITBaqaG1tYTzv\/sR7Lf79qhrZZWy4qNpLbGjvJUF+z0wOF1wnBkbvdKXuGBcyJIBDjiuA6CDpsx543RgWVwUB80DpL9fLFlCcv\/jB7YmER7AUQVCIMh9xe2hlxIsxDbDs5DGrLN7od2gXHTbuNA2AgJBiGLGew6ej1M\/tAdGKnVImWL2ro1EVpUzYHGk1DlK1tBrDS\/sIZaJZLH9uxMB9SiCEMkqg7SWiVgrPfx7IlLb7bAPENtggkXhkc6tvTAQGBmtoLuzFcu+\/1Hss+t2iGKJUhgUkr2hvU+cc1Ntbh5bgWNfFJxr9Uq2BrnapSYNxftEUog5jkkE3dUSHdqsWRfNm\/MDRNLetLyl3NrdysyxsiNAJoeKbQYpw7xCHXRum\/hnqunBuV9h70zYjzBIguT\/vX47fPOEV6JWjxDHMkG4XDMHi3oOhe9lNpzO124iD8RqqWVkTCEI1VodLeUQnznmYHr3W\/fBaKUGZqmUhFwg0mBtKxLDOe033ibb2Ic1pC3HTcKAMDg8immTu3Hpj07EHou31soqX2zY9g5Q91fYKEfJBUjo9tlMmsi4Wm3AvsSc5Wp+gR8JABg4QG50gAxgeQo5iMOZY5AiHsDKL+huaHOyH6uHxFiPZQ+NWd+Sc92WcBAAi4PkTftvjTNO2g\/1KEYUSwgyp8om1V5adHp2WjGrvHrndJ5cDbCgpNntaGvBp49+IxbMm4mDehfjA2\/rRaVaT8o1RV7SxR42zXXMPXn\/diMK6v\/8fQ4DgfVDo5g1bSJ+86MTsHD7Wc2DI81EzG5qkTod11yxbJEJz2AV6pq2TmOxmnWrVxQs6wzmfbq2fsP2CbvXPVkXzcorLFkie0+6eR4JsWdcH2FAClNpI+chSPfSkkYccxABjYbZKsPYpVZeXF75g0TioFfOwRkn7Y8ojlGPYgQCnoUsleFqZw629lbSlVH7wJH5WpPZDWG0UkNPZys+d+ybsOO8LbF+uIKhkSoOec0uOO7dB6JWiyDjODHvZYderjVPguP9tSguzobWVgyRCEPCusFhzNlyMi7+4Uex3dzpYwwOF+nQvlgMcIJQZDhqriIza7si9qAUvjkaMXNMIiyD5BuSUmn5hgdIo7ziOD44aOkos5RxjrjodTA7xMrgJP1pSJd7YJhnIbLqbueSjxxzoISBQBQzDnrlHPzg0wek5U0EIVz6tGoQSv13UVfbjM1Ee4daHWwm3xMIwmiliskTO\/DZYw7BNltNwdBwFWGQ8NnWD43itfvuhOPf83rEcZyQ\/og0qrjSH2nvk2704xjywe1vyFbmCLB23Qh22HYGfvOjEzFvzjTETYJDB\/RYH\/gpdavpxWjQXBwESbgGgk6IXeFrsVOdPwsSCTDeXFRmibGgVzHjTSwjfSrsOkwuCrIlY1nQS7CLjm3wjhz9yYYuGjfKrVfvvhX+73MHorUcoFKN8kzCDoTER9NP\/o\/gM5mxECGGCAij1SomT+zEZ445BHO22gJDI1UEysELhMD6wVG8+pULcfx7DkIcx2kmgee2dKq8k3vIB5+\/YfbaS6HAmnVD2HnBbPzmhydi7qwtkpWCJpmDyJVDFDi7cTCV10qAru4EF4uajQ0HlUPtchPL\/BHtPZTkWwVkDID3bpv1mpnpEpXYgABhWrKE5F4nXD8NxHvE9QoILFzYNTvStN6TGmWGY1fb04OkN7f0vmkWNLmB5dZeO03HTz7\/OrS3hBitRggC0iFrx+FihwqLe69cu5WZGUlwjFYxeUInPvPhQ7DVjEkYNoIjC5JAYN3QCHr3XoiPvvcNaSaREA0jQEbhjIPhyOgmDQWusirAc2sHsduiubj4Bx\/F9Ck9iGXz4HAFhiphyl7JV01Qjl37HtnGoYK86fZ3MpEgNVck4NI1Tk1fOY5JhO1hKF6T1kxjD5DGcDCIsV9QauuWcRTnbYJjSunfEbbTudMMExrvidkQmnYxZl2C1BsUJAJxzNh1x2n4af\/r0dVewvBoDUFQtB3IHh6UqY8rzdVfCgTxyEgFUyd345SPvAmztkyDQwgXY0bLJL17L8THPvgmSClRj6LUiY5dawHGXArOSbu2cah8bqUwwHOrB7H3ztti6ZnHY\/LEziQ4xMbKF7gZEG6huIxqz+6BJ+ASA4Rjx56hawuzvYpN+YYZvS75w6m8wSgWEb82YekmSAzBt2ZplhbSMR9x0899E1V7V1xqgycvL2tDVioDQhwzdpk\/FWcveSOmTmzH0EitQZdnv586HBwtp0p8UlYJgZHRCs2cNhGf\/cgh2HLaRIyM6pkjgbrI+RrXD45gvz12xCeOfjMCIVCv1yHIXESy5h3peqr0vNd6ExuGhFWr1+PA\/Rbgos0RHOlnSYYghaupNkpzKhJ1sCBqN3pq0eR1PhrAzELKGKB4XyzoK6dDQxpTgAwseXXU17c0AGFfGdcyOgKrEjKF8CGgil+yVwPXmI47cX8LriN95r5pVhBJkEgs3HYyzvnSwZg1tRODI1WESS2jTMelV4gOHp4VGBCCMDxSwewtJ+OU496MaVMmYKRSnDlMPY0gEFg\/OIJX7rYDPn3sYSiVAtTqKbjgs6DO1o5hbxgaN2sYCjy7ehCHvGZnnPftj2BCdzuk5E3IHHoNYZWo1mao+b6C2S0qzqpcKRT0VKfTexA6DdgBgWMGaG778NMLU+h2DAGSGrQ\/ucWMOWCaL+tVgFm45GT0JswlIZOZU7KPgp0jkxLs0YzKOfTa1zhDKjIb540NEoE4lth21gSc86VDsNW0LqwfriIMyHgNcAjXQafJGA350Mgotp09BZ\/9yJswqacDlUrNOniUZg8VNHe9xvWDI9h1p23w2eP70FIKUa3VlYGnzkg2YGhbWin9jjAQWPXcen7Ta3fBOacfjbbWMqTkjLK+gRGhfTxupjEcJakD5LAg7ZzrptE2NJaDy8cFNoEx\/7sxKBDEYp\/keXW41xkgiQkOEAfBbqLU1iI5TlOPS42ieD9BUZdQVya5qam9W5CM9BIscW1iZpRLAqr4yUYHiWTMmdGDc7\/8Zmw7awLWDVYSWJOVTTf3EI7Nm1kIwtDQKLadPRUnf\/hg9HS3Y7RaMw4eZf\/Y+phkZZUgHdot3nEuPnN8H1rLIWq1GkRjTpJTO8g4XGSyZBvB8czq9TjiDXvQ2d\/4EEqlcOODo0HmSKkzpTDQ3Xez98muNhzif6SWhu7DrlYfil+I8hmZ+zqGrjJl2Yh5H1cfIjz4buMJ9qYsh\/u8GvwixaZlQO65wmTCea4a2TaFVLHf\/OYIBPDIE4OZgsomBUnKAp49vRu\/+upbsWi7qVi7fhRBQKbBuJk5tKlwEBCGRiqYN3caTv7wwejqbEO1WneWLOy\/iJUAyv80DAQGG0Hy0SPR2lpOMkk6zLEYrebnktFHBFatXo\/3Hb4\/fvb1D6JUDrPA3tiHZEYQBIhjiceefDYNEolcucElZerJKhZ1RPvdSDlQyq4IHM07\/Gc04WYBwC6J2oneh4hCeonErhxHGemQffg0O9TF2ampRJY6oTEdtwZc9hujDdziWKK7o4yf\/vZO\/PTSuxAGyS7I5giSaZM7cO6X34JdF0zHmvWjFAaCXEGby\/gja8gHB0cxf+vp+MxHDkmCQ9myMzMEGUFCTQMlyXaDQyNYvOPW+PyJ70B7WxmVSjX5Gbb2lGURFwSEVc+tx4f\/32vw\/SVHJfI86XR\/o4NDMogIsYzx3o9\/D9f+8250drQgjqVzaAx35vC6ZlnNtwupMjOLkt3ZSZqFgJQA87Y9s5+abX4EgWv+gYFX854fvaFbIP4SwB1phNl6RBraJPMKgYsHgCahkex6nnw6WRoDVWnQw4Dw1xsfRbkksOfC6ZAxp9KnG\/dhi3Tpqr21hNfvMw\/\/vPNxPPjoanS0lVhK6TDdTF5NKAiDQ6N4xY5b4ZMfeiPaW1tQrUcpKuY+7OqfcOF36eEjRIBKtYaZ07fAoh3m4qbb7sP6oRG0lEJIKRWnWNYGmYEQWLV6EMe+67U4\/ZR3ZOzmDQkOsxxslGW1Wh1Hfew7uPiya3lCTwfFsYRf6Frtj9BYMXDs03jMT1Gg+6WfQ7IWIvS2QBKJUhTHf40GH70P6BMN3Sxh9+fJ7x3G0TYATZFx3aHMId1ecprPH2yKgFofOuBirz+gi4NleAsSgO6OMr5xzj9x1rJbEQQEqenzbkSQpJlkYncrzvnyodhv19lYvW6EEqqFjdMHKUVkl4Vz8IkPvRFtLWXUjODgMZRVZH\/UjmyS\/EkQCAwNj2K7bWfhCx9\/F6ZM6sZIpYJA6NmOGVkQPLdmEJ\/4wEH45mffjjgNDtrAm8QZHPUIR33sO\/j15ddjyqRuiuMYzY1+FAUVVgM5AxXIpBjpXpEe9RfXrI1dckUgcLJERUSLkt\/oGX+JtbzRxXM8Pyi1NTo\/0ssgByXEcoWVDjlR5a51DRw1KNijRwUH0Y0BycnEfUJXC7557k04a+mtCERabvGmlVtSMro7W3D2lw7FQfvOw3PrhlPtrfx1BkJg\/dAIdl+8NT7xwTegFDRgWDJwKvIcevXPyHkQfQ18EAQYGhrF3NnT0f+J92DK5B6MVEYRBiJ7n4iYCYQ164bw+Y++GV\/8+OHJwSba4OBQX3cjOKq1Oo468du45I83YOqkbtSjulEVwBoY65W0mwVepDZpDkettW+P45hO4Gz4iTAYvNhs1EUBUXknEGWAobVVZ\/wQJyFR89vKG3Q41m+NjMAMh2mL9UbrG3rMDCklejrL+MY5N+I7592cUkc2PZNIyWhtCfGjL7wZh\/TOx7OrhxEGlAmmrV0\/hN0XbYOPve\/1EEKgHsVGcBQXWGz0JGPPOJRlkuHhCrbacgr6P\/EeTJ00IRFtSwOAAFq9bhBLPnYYTv7wIYhjuUklKAGQUkKIhJH8juO+hUuuuAFTJnWhHkV6g62RWmGiT\/CJf3OhlJGqWgIv+VVdhGFzmS2fC1E6Ytg++YbcjcrqQfacejCtWLGMt9rjfcdQUF4oo6qExsGCB3VQ1ltU4QO9ViRm3TrYpghkYKFj\/8Gpl8Ra0KVB2tYS4u83\/5ur9Zj232VW+jo24UCkPUkYCBy033Z4+PE1uHnFY+jqaEG1Vse+u26H4446EGEQIIoamrT5ZJwLm3E1ZKiwT2HtO\/S\/IwShWq9ji0k92HnRPNxz\/0qsXT+IQBDWrBvGlz9+BD72\/oMSPlcgsvei8dZtyHvTIC6uWz+Ctx93Oq4YuBlbTOxCPYrdivdw3Ppaf+HtW1lnK5tIlpmZYAWTufLtMERKcXYudUya9JPquqcrjTfVCpAVK5YxAMza472fIRJbsqwzwMK96WVmFbZMWNRdEG0TzBkcUoXfHH6F5pwEeVPn2Bdobwlp4OZ\/Y2i4igN2n5N9HpsSJMzJbf3G\/efjqVWD+Mft\/0ZHWwnvPXx\/zNlyCwyOVBAGQVZOwXHsUTjrQCHSRUaTzFqwULZjsvXsGVi3fgj\/vGUFoljyaSe\/jY5794FJcAihvQeEDQyOlLi4Zt0Q+o45DX+77nZMmdSdZQ7z0mKvcLUdHPZeuV1qEQyYV5UHKkLCUOQbySDmVq6Xzq8PP\/ZMWl1xYCFYWILXfeJPHbVaeAqIuyAjYgd2bVGkXZRrt4GlQkGWRpaA+lzkrCv1FVJSFFOsrMTM6GgL+ZpbHqPV60bx2r3mpml\/04OECDhov+2xdnAUV9\/0MFY88Ci2mT0VW82YjEq1rgin2Yfbzh7NC5pGuPmeh7LDy5jQ04nfXLYcPzv\/D2AGvtv\/LnrfEa\/KFp3clPSxvZIGN+u5NYM49ENf4+tvugdbTOyiehQZhEMjONx9q90TuCwNnHMMtWHz+N6bdChPn5LQ4qUEBYGk+u\/ioScebCBZeg+SIlhrRsQ0hpwEGesDMc9++JjcY0341+NTzgbLlOERelCWqrT5ivEmRLGkyT2tOOd3t+Pz3x\/ID+4mNCWNplZKxpdPeD2Ofcc+eOSx53DGT\/6AO+99FF2dbYildJZH+b\/rJRQRKS+fnQfYFRxZAHLSE\/R0deDSP16Fs35xCepRhB9++b1451v2QRQVLzrRWMsqIbB67SAOO\/rruPFf99GkiZ1UyzKHwcBiB7W+mb2ecsHaapbQqg02kEwY5b+lw8UupCt\/zqQUobkqkqVlkL6pC8WKFct49m7v2S4Iyh+Rsq5y2smZOdjjMe6CZqEbObpx7SauUiaWDtmwarJs2xr\/LZnR0VbC9bc9hmfXjuC1e21tHfaNDRJm4DV7bYt6LHHF1XfzbXc\/QrNmTMI2s6ehUqknusUFAz+1NAiCAIIIQRjk2rZN2vv8UgI6Otpw0SV\/wY\/P\/S2CIMBPvvoBHPGGPRBFMYIgKMyazTJIo+d45rl1OPzor+PGW+\/DxAmdHEURkQNdgktHeaxlT4FElGW9pxqDcpF7GMhJb8p3RCQoEJDy1nj48b8DcwWwUnr2cMUMiCAFsxqfisPw0hrKwFbEc\/Jt7OBgw1OQPW+ubguQS1jq\/BvzTZAcRTFP7mnFOb+9FZ8+48rssMlNzCSN5v1\/jzkQXzj2f2jVc+vxrf\/7Pa696V50d7UjjqVjfmFPoBuKhKf\/cBku+u1ydLa3ae8JGQWXGlgMQkd7G8696I\/48bm\/RUd7K849\/WgcdtDuiOIYYRg0LSmpAHpuBMdTq9bire\/\/Km689X5M7OlEVI90aSSYUkeq1Ctc5TS7zD+Lg8OiqOhzOgLcJkPskEpyYLfEM1WoV8sgU\/qODVYOnCNn7P7uA4Kw5ZA4qjIBwlhm0VNZwxsi\/+2JrY0weJEo+5fQWMGs9Szu74EfGcu+jwAmKSU62su48Y7H8dBjq\/H6fechEAKSeRMyCdJAk9hnl7no6mzlK666m2656yFMnzIB87ediUq1ntnQkRUcEuVSCVIyvvWjZbjuprtw932PgAHs\/or5qNXr2Q+yoOGUGtLW3opzLvgDzl16BSZP6MIvzzgGB+63U9pzbLiJmPpzojhGGARY+dgqvOX9X8Fd9\/4bE3o6UK9H1ueqeSlqPaTT6CcHYXwzDs6bfXZCwYp+GXsY5joowPowUi3ZmMEkCPKJeOiJ84EVZDXpcw94r1g5cI7ccvejDgrC1tdwVI2ZOdB2CywxZS1Q1EraSKH663S7uxYEB7uErLP\/JrOJYwv+bZDpJDrbSvjX3U\/i\/pWr8T\/7bItSGGxykBAIUkrsuXg2dXe24IqrVuBfdz6Inq52LNh+K1Srde35G3OElpYyKpUavvGDi\/DPW+9BT1cHwjDAzbfei0q1jj12np9kIdZxWE45X6VSCT8++xJccMmV2GJSN355xjF41V47JCLSKZs262+YHSUie7Nj4zkeeORJHPqBr+K+h55AT3cb6vUYtv6AUsoUuviqB9un82Ua93iCwyzJ7DNklubkIZo2luMFS7kmHn7yp845SCNAZu9+1FtFUHplHFU4gbt87q5wRSlsrwY4ZDJNVEOaZpseuVI48HCZx4LeHGpIWHLIGDKW6Gwv49Z7nsS9Dz+Hg\/abh1IYpMOzTWA5UrJ4tefiOZjU044\/XXsPbrr9fnS2t2Lxgq1RqdaUBl+itaWMwaERfOW75+OOux9CV2c74ig5fC3lEv51x71Yu34Ir9x9p5QRkBx2yYxQCIRhiO\/\/dBl+\/fvlPHP6JDrv2x\/Bvrtvj3o9QhiYVHMdU9MUaByPehSjVApx\/8NP4K0f+CoefvTpNDgi7TN2KK2n9Lqcm+eWafJ7L9qIp2EY6vU3LNLmdXG2DHlUQDB4UA4f\/UNggAFQ4BoSztzlXW+jsLRLHNckAUK344WzGWOnLzg7KdfsvGEcZZmT2OZ4k5SMo7zRZNe8iuqvlOhsL+GO+5\/CPQ+v4jfstx2Fodj0IElr9j0WzcGs6RNw5TX34Obb70d7WwsWL9wGtVoifdre3oK164bxle+ej\/sefDQNjkjJf4y21hLuvPshrF6zHnvvliy8RVGMcimEEAJn\/mQpfvvHq7HtnGl04feOwx6Lk+cPAuHYsnTN5N0BEsUxWsqlLDj+\/fgqdHe2oR5F7Gx2jSoi\/XAcpbbNAi86Q\/bl6wkOB43daRqkzc4cEHLijFaRXSt+jOGn61aALFzYJ1asWMYzd33nu0QyRVd6kLHAuFkrTu6sov2H7jqlPT\/g8gWBz8JZScXs8uhQ3xTlDZGS0dEW4s77nqK7Hngar993O7S2hPkkfCP698a1WY9i7LzjLGw9axKuuPpu3HTbfRBEeMXCbdFSLuHxp57FV75zHh5a+RQ621uREPt03F5K5tbWMt1178N4\/MlnsNeuC9De3opqtYZv\/\/AC\/OEv12H7rWfiojOPw6IdZqNSrSEIRDarafZKte9LtxKjKEZrSwvuuu\/fOOxDX8ejT6xCV0cr6lGsIkGMRjlt0oEyMxz7wuMm\/uzuoTDnrr2AvUbrtt3I+QumnbSiZG\/MVxqaC7U4LP8Yg0+MegNky13f\/R4RhNvLuCbBSonVSJue4MjNYOzDbqRP0ldtfUp67uGSz3\/DLUvZMFOBXaIx0iAp4Y4HnsYd9z2F171yHtpbS6jVY2dP4jt2DcSJkG\/U1esRFs1PguTP196Dm269F4EgdHe148vf+RWeeOo5dLa3II5jx\/uUfphpKXbv\/Svx6BPPYMft5+B7P74If1r+T+w4bxYu+O5HsGC7WahUcgGIsc54zDI5imO0tbbi1rsewhEfPg2PP\/UcOjtaEUWRVkJ5IXrXnIyLFOmVRly5FMmYpJNZysO09\/aVbOZ5Ujo5hcrCWSkOYnDcGo\/8sD7y7BAAY4OnnwWWkNzzg3+6Mii3HhhVR+KskdeMFosgOGQ0ZfhevEf2UttNLyzZYEqTMsgMTHb3P9mUXX\/+IBBYvW4Ye+40C7\/48hGY1NOGSrWuq46wilzpLZjq9EaUH75YMtpaW\/D7v92O45dciHoUo7uzDYNDI2hpCTmOJRFsa2XzRhSCUK1U0dPTgSeeeg47zpuJX53xYWy\/zZYYGa1sgBSo+xFFMdrb2\/CPW+5F3zHfwLr1w2hvazGCw6YYeakdefNDTnEOh9aurZqZb\/lbeyJO0TwuYnCYAcWZ6nxy5pJrjeVoEEfzK2vuehSAuf95aiNTtLK07LpI9dm2b2NF0hEelQ+\/u6tCunQFh25Co3nsJTMXgnuTzFrmt9Utkq9HUcSTutvxzzsfw7s+cyGeWT2ElpZSRr5T\/RCl1L3UGyxiKRPKffK\/nHknjoyM4k2vWYyzv\/5utLaEGBwaQVtrKdm0Awx9KOmk9Mg4RntbC55etRav2HEOLj7reGy\/zQwMj4wiEGS9ng35p16P0N7ehqv\/cRcO\/9DXsX5wBO1tLYijSClVMvNQdh9oY8mJDadcuM18oHASrQs1ty+wm30rOGSBpZwK3uR0ejYvzGSWVmKZ+zl4rp2cvWukRrItCpy3n36bu8xvFL5UEarBOejvgImlLYHqthpjeyBppmhQPYowoasFt979JN7z2YvwzHNDaGstIcoYqjqNohEUJjxp\/iMEYWh4BK9+5Y4457Sj0NISYrRSQyCIctcsadOxldcchgKr1w1h8Q5bYdn3j8NWMyZjeHhTgkNmwdHR0Y6r\/nEn3nbsNzE0Ukl\/58gwvIHOoHWvVLv3NLTyFs41B1unSxsaMxuGRbaIH7Sxgil+rjGI3S5b+dlvyQfowtdpslvJkFVell222NR3hkcYLs8M7N4Mg7HnbguIab4R7ODbGHRpht\/4sfGzoyhZurrtvifxnlMuxNPPDqK1tYR6PcqCgqWeJRqBov6Z+U8gCINDw9h\/j\/k457Sj0NZSwmilxoEQYMumTUdXSqHA6rVDvPOOs3Hhdz+CaVt0Y3h4NNsj39B\/ktcJ1OoROjs7cNUNd+Adx32LRytVtLYkF4L5OVtusgW74\/revtTEBE1lHLc4B6BpX6n2d06xEDbWdz1aWOmsXdWJcgll66wSH8RhCcPpPt6WSIPWZEpFk9WTOTLauyH8bNg0Z1ag1rK\/lvoZjVLLJFDm65z2mqY9X2FOCI7obm\/B7fc8iXd9+gI88cw6dLS3oFaPvFliLP8EgrB+cAj777E9zjvj\/ZjU007Do1UEAZGpB6y6Oq1eO4RX7jKPLjrz2CQ4Riqe4JBNA0MmAgWo1yN0d3XiyqtuwduO\/RaGR0bRWi7laJrbh0Qp5R2Lccb\/1\/7bnJ+xtphtEGLNGZubwGihXWzaWKNA4M\/whVRLtqo3QNIeBLJqKo345h\/OAZ5FM\/YIiPkUUOCqFT1mO5k9MNQ3yaxpFSzPgJEV2zEGSBChnq7Kdne24K4HnsK7Pn0+Hvj3s+jqSINkE+r9IBBYt24Ye+28NS789gcwfYsuDA9XkhVeQ2srDAWeWzuEV++9Ay783jGY2NNuBUcjayWHP0Hl9EyWZ7bGgalHMbq7O\/G7P12Poz72HQwNj6Crs52iOIaUsmHQqJV5BqxOcOj8KnoDlGlfOWVpWVUkYa2sJBi74\/BytYzddjIF8tjrNaIMoe3mvl4VcdSsxKob1sfaQE+nGTvhSc+eiG9HuIipaTZxjubeWq6xamHSffKk9XqYk\/3zarWGtrYSPnfC4XjHW\/ZHKSTc98izOOrkC3Dvw8+gu6MFtUTHe+OCREoEAWHN2iHsNH8mzjvjA5g+pRtDw5UcMeNcCvTAfXfEud\/6EFpbShgdrdqZA8gIl5zu5esZIz+cUkpEUYyenk5cfNk1eN8nf4DhkVH0dHXgi5\/7GI4\/+t2oVCokpSRS7OlMXV2n54tafOi9FMPnvJv2l5YkD1vjAzKFrm3qfPZ7ks4cgJOBzj4jUeYalABx0t1n7vKOQyks7yijmmxQTfIfms9CeANozDYcCD9c55mF+ODCJrIyKZzny4DJ8weCeGS0SpMmduKzxx6KebOnY7ttZqCttYw773kYa9aP4srr78N+u87FrGk9GBmtbZLAmhCEkdEqZk2fiAP2nI+\/XLcCzzy3Hm2tJRABz64ZwiEHLMbPvv4+hGGQiM4Fwjm81IjUlE3p8gst\/d8ojjFxYjcuuHQAHznl\/xDHEbq7OtB\/yglYtNMO2G7buZg+dQquueGfICRcL+YkPbP2U8xtwaIJOBOsJSc25io+57EmcL1PIV670Nm76Mf2tg0BcpirtTNRXzMMgIRngDQEQwwhp7HbEQ+HDL\/pi2dr+vphX69QnElU0yFmVm4zVShNacps2jNzKjwwWqUpk7rw2eMOxbw50zE0UsHQcAVv+Z+98L63HYgwAJ54eh3e85kLseLBp9Dd1WJAwL5mOP13X7m1fhTbbz0NS8\/8MHbYZjrWD45izbphHPmG3fGz096HQAhU0w1FzifsOrSsQMwsJaQBO7OUiKIIkyZ24fxLBnDc53+KOIrQ093JX\/z8Sdh50QIMDg5haGgYB7\/hNTj548ckXiSJ626uFJvV+BbnzYbrfZ+zTT8nv7MUO5RxND0px4Wq2T7D6UGTgwasrEg0sJIRtJZGG0+s092nHBusXHmO3HLnd7xGBOW9ZFRNuFh608SmMokzKh1eIR53JvewCK4lHIPGrKMOymK\/7nXXoLuzlvuTDzoQAiOjVUyd3IXPffQwzJqxBYbSvXIiQqVaw6Id56KzoxV3rHgYawZH8bcb7sfer5iDrWZMwMho3ZlJsoFhE1pKQxVkyuQuvKF3J\/z9hnvRu9f2+OEX3416FKMeRUiRrqysUweRY+WGTZ7YhV\/9ejk+2v9zRFEdkyb08KmfO5EWLZiP9YNDCIIQJAijo6PYacf5mDVzOq665h9ZICckCgei6MwOpkqrZWqqzldYJ6c6NlYd67fcIKkaCGraaWoChpYHDZQlq+ysSQaIGPJpXtP5PeBJaVFN5r43YfPOeMXb96KwfICMKjLJMhoCQc13imH3L1yg5eudpLqbrOyQFOwS2IRGLbCpoWU1MlrB1Mk9OOW4w7Dl9MTQJgyCfD22ESQ7bI3Jk7pxyx33Y836Efzlunux5+LZmDtzEoYrDfFo9lM5rBIAypxEoFKtYUJXOw5+9WK8oXcRCIQoivIyh0hn56qrusbXXcHxi2V\/x4mnno04qmPa1Mn48hc+QfO33waDg8MIsp0RghACo6OjWLDDdpg1cwauuvr6xAYhEGApHdA\/LBKixcvyqJsoOs26qqKrxGa3UY5pnkRkoFjO8wTD1zFt4IgEpPw3V+\/4YYP27BsUrs4aWRjCCmajZvUV2oE0dtmdQtfsCg52LVGpSlDs9jJ0qc7nusDIGsZACAyPVDB9ygQlOGpacOT9gsC69cN47X4749j3HoLWlhBPP7ceR518Hq675WFM7GrsSMAqrzSTl6xZtkuxQBCGR6vo7mxFIARqUZRUxApKVVTGub4exTEmTejEmWdfjpOWnA2WEaZP2wJf\/N9PYN42czA0OIwwDK1PPwgCrF03iNcesB8+\/5kTQeDEsCcNVht6LUIkpaF5Bq1EctiF2\/q7FoTsLuWVSppdwcGGfhuZPVHSRqxW98acdPfpO\/dtLSg4Qsq6zub16FF5GinysXCNw0ukRZe0Mo+500HOTGWWeI5bO6\/9aXikgpnTJ+GU4w7FjGkTMTJaQxCKwqZ6tFrDjvNmY9qUibj5tvt4aKRCf772Huw0bzrmbzMVwyPVTA\/LtCvWXYBd2SRZumpo2Y6NkVtQVkmJST2d+M7P\/oAvnLEUBInp07bAV\/o\/hbmzZ2FoWM0c9k5hI5PsOH97zJ0zG1dfdwPqUT3fM9G4eVyQMdkGdeAlMFpST8rBN7ZGi6zBzRmHNS1XS3XO9NqIBIP\/gepzv04RXk8GYX6CZQxItmjrSj1YFBwa9u0LDuPWSX0jDGxdzzxsUdqhWB8X2CmowTE0PIqtt5qKL5x4RCLTOVqDCIS\/YUjXagMRYN3gMHr3WoSPfuBQKpcCDA6N4uj+i7D8xvsxobs9m5MkL2nDBnmsZOeiqXwzYCCKY0zs7sAZP\/k9vnDGRQhIYvq0KfjSFz6JrWZtiaHhEQRB2DTIgiDE2rXrsP9+e+Mrp34W5VIJtVrjEsjnDl6zUEV\/gJE7Q1l7GNBXul19q71SDXhU4ZkdgE\/BYLgRTI1S6anki712BlnRt4AwMMAzFr+9nWX0YRACj+J24+nJguRUmrtTdMG5zJJTjh31ZsoVgcomtkxRrKUge7kmCAQNDY1gu21m4DPHHoYJ3e2phlUzJixli9qCBEarNczfditMnzoJN916N0arNfz5miST7LjNdAyNVDd56aqosS+ir0tmTJ7QiTN++gd88bsXIxSM6VOn4Mv9n8ScWVsm5MYwUCK\/+IUGaSbZeus5WLjj9rj62hswOjqKUhimErHq7o9L6QawV7adowA2CYTuXfaCle1MtsEs3+AbD5BW+4IEcXwx19Zc31A10XPswHIAS7DVK97GkuWHSATtybK2yv9n9YUo6dPnC+5bclIngIZ0qE4hgCkrw8busWMeo+8YpH4Yg8MVzN9mBj577KHo7mwbY3AoAZIFSVJubb\/tVpg+dTJuv\/N+jFZq+OPVd2P+NlOw03YzMDRS3SSvjQ0OnLSEm9Ddjm\/++Hf4ypm\/Rks5xMwtp2HJ\/34cc7aaiaGRkaSsYsdSfeHMRqBSGcXWc2Zj4YIdcPU11ydBUgqJpSxAJAG\/UIc1+yIuXrHWg7GpeKG95uueoSgXPkEIiZ9wbfXdwEpbtAFYQgDwxO0XVqYvWvFeEYRTpIw4EdSwzTm1iPfs\/bKyPehaomJz6ISChj7d\/PDovKofEOmZI\/HsmL\/NlvjMsYeio6MV1WqUBgc7nWUdNZZ22TaMOXfZaR5KpRA33XI3wMDlA3dh3uwpWLT9DIyM1jZaCGJDgwMAujrbcNoPf4uvn3UpOjtaEYYBvrbkZMzbZi7WrluPUlgygoPHcjWkg1SB0dEK5s7ZCgsXzsdV11yH0ZFRLpdLxFIW3PrQe1aPryX7elqozrfOHXgX9J8NHzkFb60dE1vDjQCQJPoWqqsf9\/UgnBh4EhPJRyCCRHaNndpFxDr91977YCh8G2n5jmtDH3btKOsyoqbNGbMXJVPQqsRCecF2M\/GZ4w5FR3trNngji1pXUNgY5zyOJLq7OnD7XQ\/iD1delyiMhAIyljj21AtxyZW3YWJPe9Nh4qb+01A86WxvxanfXoav\/eAS9HS2gQioVms4+5fLsGbNWrS1Jmu9ynqeHQxkB09jw48BhGGAdevX4xWLdsJpX+lHV1cnjYyMcJDaLLhF46TOldLMPaVJB7KaaTciWXCGEqTVyjTMjnFA5juVbEtBxkOoVx5T4VsLxujFqcHKlefI6Yv79gqC8l4ySoUbAIc5p+vWgEuqXnencgtfawiGOXC0tw+9qhpZYximmWPnBXPxqQ+\/Be1tiRJ7IAT8xgRNblQixHGMrs52PPjw4\/jSGWfjyaeeRUtLCbGUmeHn5QN3YcbUbuy2cA6GR6ubLZOwObMFobO9BZ8\/\/SL84Nw\/YdKEzgRGlhJBGOCe+x7A\/Q8+jP333ROtLS2o1yOronK\/NtJgdVLKrdHKKGbPmoVddl6E62+4kdauXYeWlnK6GwNsrIYzW5q87JljOBBMTRwEerXhdrVqnFpKv5UAfgQjK7+hSAv4\/UFIxitYY9BKJ5HMnolIQwJUGtQO9vBtDAjP9CRX90aMN5Jgo2JBQFg3OILdFm2Dkz\/yFrS2lLLg8GcNLgwRApLg6GrHivtXYsnpv8DadYPoaG9NlCvBqX4toRQIfOq032Dp5TdhUnd7tnTVjJKS\/xm7\/wyNzBGn4txlfP70i\/DDX\/0ZE3vaIWWcagckW4gTJ3Tj5ltux+dP\/QYGB4fQ2tpi6QabcDJZ3lZ6zgmDEOvXD2LHHbbHt077MqZNnYLh4eGUbNmMpa0QK61FN8DysHRSjGAxjbVqQ53Mu4Sr9eoGzJx66vGDAOJGeeUMkKlTV3F6q9wl69WkT3FYH0AH99zDOpffuZdvIw2jRWvGQSrTSg1MkxTZKKv23mU7fOLoN0EEAvV6pAUHAQXHwCeHI9HV2Y6bb7sXX\/zmLzA4NJxkjsxqrEFaSBi75VKIT3\/jN7jw8pswsbsd9bo7SJwDRrgCKOFVNXY22tuSzPHj8\/+CyRM6k3JLsroXw1EUobuzA7fefhc+9bkvY9Wzz6G9rU1RUdEvACoIDPXyCMMA6wcHMXfubHzrm1\/FljOmJ0EihMWiYJgOYkb\/oVhE5xxg16jAzeqG72va1FC9wOFR9sRdaR0lnGxeAFixYimAJZi1w9tqEdWOIRItgDRUDrXUSNmwxTXZNlEL\/w4JeY0ZTaUKbRipc8ICIbBucASv3HU7nPTBgxOFwDhOpsCOj5rG2KZKKdHT3YF\/\/GsFTjvzfNSjCOVyyOlBI3Nw2bCnFkT441V38pRJHbTXK7bG8EjVg0CxjusxO78uU2pKW2sZp5x2AX629O+Y1NORHHib\/0ZgZsmS2lpb8ORTz+Dmf92GvffYFZMnTUS1WjVQPLYs4tzvE+foVrWKKVtMxt577YEbbrgRzz67Gq2tLZAyNs1qHEqZsPhcynwFXoaFl9VtKdeQ3dDbnLDEkJ0ESP4ItbW3NyBeZ4CkSBY90bdgZNpzk4+koDSdOcpo73AsnrBBDrNp0HC4DjkWrBxENZfadwNxMDn\/gUjKqn122w4nfeBgIJXPbJAJyfrH\/efm0ZAyacj\/fu0tOOOHF4GZE7lSKRPVUWZDDklmF5gQQEBEf756BTraW\/DKXbbBaLVmBQgZsqJmYFCq2hgGAcJA4NNfOw+\/uuQaTOxpz6bvcK4VJ++TlBKtrWU8s+pZ3HDjzdjlFTth+vSpqXW0sFx2XZ5XPgi4mgbJXnvtgRv+8U+sevZZtJZb8oUnqGIeKBAbZKtX9fatXhVF8\/kdaJcuL5Srh0p8EfW1TwMrsxfh5Br09S0NVpx1vJy26LC9g7BlZxnXE30scr9Ql7+cBtGZMituk3un3a9OSJQO16mUV0PEg8OjtP+eO+BjH3gjwAndonhno9jkJskcjO6udvzxbzfiuz+5GAERwkBASsk59Ttf7DEZp8wMEsnfufKau9DeWsa+u87DaKVm1f9af2e9jkRhXQjCJ7\/8K1x02fWYlJZVjszBtjMTIGWMlpYWPLdmDa6+9ga8YtECzJo5A5VKxXqfqCBz+IJkiy0mY9999saNN\/4TzzyzCuWWckJw1I0JnTe7SV3X8EVTXIGLrf5cVgxms6\/f6CQAPInh9i8Cq7TbyxkgDdr71AWHbinC8sE5kmVxm3Kbc7gkIA3elr3M0vgX0unqPjE4d3AktmNVvGrPHeiE970BUnISHDSGCbniJGgGiZSMnq52\/OHK63HWLy5FuRwmavBSmoNScmuG5R8uASi3hLjy2rtQCgX232N7jFZqip4WF6BmMvMO+dgXz8Wlf\/onJk\/oQOQODrDhzqXZD0jJ5VKJBoeGcM11\/8CinXbA7K1mYbRS0TKJC8Ao8hBJgqSGSRMnYP\/99sFNN\/8Lq599FkEioN3wcKFckhTwuc6ysZLg2R0nm1dlBI4v0+hgTJzIX\/G1qN3\/C7VB96JYA2mjzkLeKOujTOAgE2JIVy+zuYTuOW0IKyj7wuz0dqBCizVXz5KvcWlvahzFtNuibdBSTlQ5xjbF5gy10dyf0pu8q7MNF\/327\/hBGhwEIJYxnHsnBR96AkEkh7m7sxXf+L8r8P1f\/hU9XYkTlQuxyhi5UZzZpn3si+fi93\/5FyZN6ETk7jncbAUdJqU4jtDW2or16wfx6c8uwfX\/+Cd6uruT5ywALXyB0\/jdgyDA8PAwZs+ehZ12WoBapdIoHcmJbLmCw8Xuhc4kty4iazcd+mIcdH4fGZzgtG65wWzQ\/TDvsiMlALQMVVZwHD0OERLLrLB2BUI6v5TQhRWg072dNGbTKN61rdiIGmkPFjn3Ka1UE2Ho5EOhMU+LlUFN9ro7O9rwq4uvxM\/O\/wPaWss5xV4DWxyieC69qMZATEpmyejqaME3f3IFzjz3L+jubE2FFdxDwDAUkCxx4pJz8fu\/3oxJEzoQxZFD5ZDtz4FdpkfJS4njCOVyCaOVCj7X\/1Vcdc31mDihB1Ec2XrJTWk4+fcLIVCrRajVajoHxrGX7rRk06SGVERUN2FiVU7I3nxlpxSROTZPXr9Ij+F1aXrg5gECMPqWBjdf9uERBt8oRAmgBtXWpTdklE9eQ8bGC5VoqoWl83nItcnGBvmNyEUroqbwbZ6NJUAJfPqLC\/6I8359Jbo62thQ7MjlhMwD6uMS5Yctq727OlrxzZ\/8Ed\/++Z\/R1WEHSRzHKIWJpfQJp56DP\/ztX5jU05lIgfqpOeZuN1l9oiKFJOMY5VIJLCVO\/dLXceVfl2PChB7IOIYt3ePLLIadAKPhy57\/zo7BsI8v5RKpthUZ7X7FoLYQfD426aHI9YZIgOVqhPFN2gJUkwBB7zN3NQhIf82xaDC7jBBNvSl1YGMtPgGm\/I4mSMZuKkn2JrCH0l4o98+ePzeQJEFoKZdw1i9+i4su\/Rs621szlQw16zFQ4BfPDoNJfZjK6a54T2cbvvuLP+MrZ\/0ene0tyXpRqjxSLoUYrVTx4c\/9DFdefScm9TQyh\/L8epbmoh1+dYFNDaw4zox2+NQvfh2XXHoZenp6NOs4KuzhyP8nymdKMPt0qVQfMKoNl8yPq8KAW0TOsURF7rwnQQQmvhnr\/r3G7D8KA2TggCSShOCBqD4cAwg8G4HWEop74Ofe0dAJjPremCNTOXoQFHjOsQeTYe1taMwWyqUSvv+zS\/C7P1+H7q52TWjBEFGzHFMNa2I7QI0bMRFXkOjpasP\/XbAcXzzzd2hrSazYyuUQg8Oj+Mjnf45r\/nkvJnS3JWqHDvV0VmdCjlvY7JfY0ldiSBkjCASVyiWcdvp3sew3v8WECT1pIHOTQarRumcRYku+5pQiF\/Tvsq5gXT3WuXTF3h7YEpizgyR9Z+hPvnjwwzxLlkiAafu4\/R6W8i4RlIhZSrbZuNpCikvBxC8sJ+HWRJIOzSNbK1jJbLCNkl19COd\/Tjl8mphcEs740VJc8bcb0dPVng3eDMFkZqWBNNTHLVoMs28lNH\/tUkpM7G7Hz5cNYMn3LkVPV1saHL\/ADbc8gInd7YjiOO8w2EfWtOt4583bIPMZJaCUEkSEtrZWfOuMM3Hury5Ad1dXAfRM9rGzZn9MunYWG0tRDNWA07lXok1Nx\/C7qUqcDhshI8qT9chY\/s1VXgFA4VpZb++pwbJlS6KdjvjZn0mEi\/WrySVG3HwXxLKKNvBqAmwDHa\/cDwxK9dim4o1AkVKiVEoMc77xgwvxj5vvTp1pYx8RjiypVbjUxxttq4FuOZTQmYGYkiD55W+uAYGx8vFnceNtDyZlVcN+gM2FMhPlKyYFWm+SSyeAkyBpb2vFd8\/8IYaHhvDho9+PoaGhdJgpPJkjt+Jlidy4HUYpPRYbaLZk6GAqObLxOxkbhqSCLh6ohtCYnrO8GyMr73BHfJMAmTp1IQNAzPHvKKp+MhkWcpFTqL2g4tsRgLa9T80oBbZOrPKmk2ku2jw4YinRWi6hUqvjtDMvwE233Yvu7sQjkJ0Wcz5TIAfRzpQ0dR1eI\/hjKTGhuw3n\/\/Y6BIIwISU4GmUN6w2oS50S8DuCuXYpjFJEJkBFd1cnfvrzcxDFEY77yNEYHh6GtAavXACgm7CsrZVrzWi83up+5rbHLlyr+zzzm0TFBPQHAFEaC9EGBciyFO6tD4\/eKNr5ARGU58m4JlU0Bh4imV\/Sx1zHbeZW5HgTfDeSt1HXXcbjOEZraxmDgyP4+pkX4I67H0Z36hHIJi0\/o+GbQynXgXNdCkZfZc178sMvpaTO9oSiIVX6iCKr6epB9H7D0fc4VGcKXLmyv9fV1YWzz\/4VhoaG8YmTPoparY44jgvZCWSO3g0xab\/DMfvfGx2107dYtayYb+9QASSTNmsNmcrfFUV7s1Ez9\/b2hw9ccWKVgcsoKCXcN+uwmzOOAggOnr0Qjxg2G7YLzJaKo9WFuduyBmoj0d7WirXrhtB\/+jm4456HuauzLanz2UVvUGj4zN7hpaUcaPUv0mggYU6AidNMIqXdf5myS25xZz2r5LMC1+62PYhVe8FkpyRGV3cXLr5oGb78ldNQKpUQBEH6+siNXWlxIAmwrTQYhoUCs3tPBDbFBAzK5z3q0F06B5rkriMYIMHM92Jo0g35RH3DAwQDaZlFkBfLqAowRGNgZx4ISz40E4tw9SPSDA72S08qb4otFkcmiuW73+LUpemZZ9fi1NPPwYOPPI6ujjZSmbAaRM368BMaOKOpNNrDTX3xQM8eauCzflCctgJ6CcXsMCVS3Z1Uqrg1R9BU1109Sj5c5HSnpHviBPzhsj9gyZe+km5Ohglb1+p57fJL2eRjfR4DuOzFXTCujSTmAoCZbkGT4toApBtWxpcCN9eLKqnmigXLjowBpi0WPfWPOKrdKcKSQMJ\/ZyeHpqGZ5GLyuv09WB34uzbN9KEXYA3uEjwIXEDQjmOJzvY2\/PvxZ\/C\/p\/0cjzzacJeN\/Dep9fqlJSGjqcY76BIMW+FFh17dVtU2l8iS+ffZKZNVMbD78FuQvPo5KHOVKIrQ1dODP13+R3z2s59Lqf7lzELO5GtZK7Dqe2Egkl67PDtwWJcNtUtqLiQUaQB\/kGDYYmmzZmpMzo+9vacGA0uWRCC+gJI9danCc5wNqlQbArcLkUORkayhV1MKgiWFr1PfjbsjjmN0drbjwX8\/gVO\/eTaeWrUaHW2t6VaetXPAlgeFS5mcbQGzfPdZGmJoBj3ClN53Kv9J5zKYNYg1reG0\/kiaVJRssk5OWoy0nJhy560IXRN6cM1VV+EznzkF1Wot2U508bcIvowJy+eF9XmJe2iceLco54Ds2Ym\/ciCzOYcgZvwTIw\/eksZAvEkB0hgahoQLo9roaKLpq5ZE+dtienqwmmdhrseaGkpej0H7RlFpy8aSlmoj3Ngfv\/u+lVhy+rm8eu16tLeWEcdRgQWDqZhi856UBl4rG3VZRX3Ipfeu6u2ujuPdA1CLW6SVFR5TVRPwoGasa+QlmgotN4KkHqFrwgTceMMN+OQnP4X169ejra0Bi9vKL2pV6B7qZuIaxJp\/IDyaZ2ZmdcO4TUhF6ZYUnYNGs1607zImJt+SJRJ9fcEdvzn2IebozyJoIWbEqrEKa6QBh\/Mt62Yl3IzkB6nXomwP3LgQwUpo4t1dHbj59vtx6unnYN36IWptKWeZw3XD5dQSjww\/a2UJuVijuVqfdLipNt4mrdlm0z9ecxi2vR+1QaFBonQYWCLPDg5lmWJ4VT+g9XodnT3duOWmm3HCCSdi1apVaG9vzGtIC1ozc7EtnmAItzHZqiMaOZXVrOFrxgtE9SWBAkj5HBAuK2rONyxAAPShr\/Fr\/5A5BrMUxhuu04zZszrrXLAyBMJcTZxrddcVYEpD3tPdgX\/ccg++9r3zUKvW0FIuJXR143ayd5ThgZpNCNV9s+fQExxuqsocI3NgMmU1fUtE0iNkkMvrcJFgX9b\/eFjXjcNssq4Nq4moHqGjpxv333sfn\/Sxk\/DU00+hq6tT8VSHrz\/SVs+N0pwsPpmp\/Zu+T2NYZCDPtEaCiBi4GEP3PZvuQ\/FmCZBlabM+fe2zf5X10TtEUCZmjt38Ki1fO+BJeMllmo2aAyljh4SpOSSMY4merg4sv\/Y2fP175yGKYpRKQbonbSppmHqxuaguw2N3nH+ffbPD1byr1sQyLy1y6gUX29PZE3BmW8bT8oE0y0PL5sUEWgCfr71Z3kZRHe1dnXjowQdwwvEfxYMPPoSuri5E9UgBUwt6kHypiXQFfjYkQ\/PzQ2NTMPP1II3ZRwTgrLGe+zEHSNasDyyJmMUPSAQEltagip2HXTVHdbMynUruJkfL6WoFheGaiytcsfwmfPOsxv64yATWDFTHSWVxWRNn+Crs21UvB1WI3tEHpHIzTqNS1upyR2D6Gcys8uI8aweqdBMMsTb2+AO6OHCNnxBHEdo6O\/jRf\/8bJxx3HO6++270TJigZBJ770OFky1lErY9K+FnSGyo9n0MEoLBf8Xww7c3a843KkAGBk6NAVAc8QVxfeRxCkKRvuvGYbccTYk9zF8DjVLSqX1DO9GZbFiW1NndXR247MrrceZPf4NSKUAghCJqYKI9yJaKjHKEPPsGZDTA7CgpyFuGwcxCLli5sZLq0bhl5b1xzJPIvLnd3Cb7UvNkEr0r5lw0vLGdGEVo62jHs6ufw8dPPAG33XYrJkzoSOBzjWxowsqZ4Iem6GJB7uxvuWmMf6addclnbMjZFxsWhMS9vf3BA1ecuJ6l\/J4IysRQFqlglh5qPWDoYlk3likY5qdvOHsElmhva8HS3y7H939+KcqlZIdbSqkPALPlDqka6zjYtmx5Wliuvmzf7Oy8jZltNMvNXs33NuwBGUNXtWe4HVy1JqjJa9VWEtTV1sbFkTf3htRsBkxRHEVobWvFmrVrcNLxx+Haa29AZ2cnEMdwvrcN5StlnGGKAyL1gaHmOwxjKbViQAiW8U0YeeSv6bmPNoTeugGP5EDt8NbvT+J67W6Q2IJlBI2fBa0h48zdSSslpJvA6CXheeROG2++lJg7ezoeWvlk6tBKKSXCAwZYdg2pm6tF7fBBwWNlpbKhTo7i380vyAynx0ozezvmJgRGOKU5i35Po9nOxTOEQHV0FBMmTsTkLSbj4fvvR1Au5ZKk3IzEqugBb6R5kIeYGINEwBy\/FUOP\/NZHTHQ9gg1\/CUvQ24vw5j+dPDx5u9e1BWHbq6Wsx8QNIWzrsKuNF9lkNHh6kIID5HAoAhFWPbcWpTBIadd+RquT0Gc1tI5g9xxQpSHWFcr17yfvgSgKNK0Ekk6qunOrzmmyCv\/hV7M7fIRBWD0JG0zgsBRitDKaKJqUSzCV39mFcBkADDkl\/ryH3\/pjcmQPIP4Xhh75VPrleMw1EzYuTgkAFvzPGRPrgVxBQkyFjJnBwr0tBhR5P7BLjlLx0HZTnO2bnSzejjs43DsqbkjX7dIr9dfqotvAt00Jl3+ih1YCpxqgzcj1edcXs43ZwZ4uWjtwlmRWsHImx5fztdRlOniznLkBSJ5IYGt5oTCZpNkjOhRDKy\/dkOyxET2I2oucGqz48ydWg\/l0EbRkvQg7LBB0NEraKVurwfMmlI3gYNdswPA8VzYaGSz9XB92rMRygSElKyLe6tBPFdpmE9qUFs2fnQM9g0SolDCGSomD5mJ68jmacw\/vCSawYv6uTvTJkWV1m7PkXWkERzYclUazD4+2wEY14K7WIx1DUMAyvgZDK383VuRqM2SQxt\/tpwW9U9prbcO3iSDYWkb1XMYxv20bgzDd\/MZBSGTfzavf7KzpbLkGec4dFfXmllbmMBd5rEzivi25Qb92Nvso6J0Mv3nlfWr63ni\/ZpVJ+T6G36a5qMwDtIUv+\/mdFQB73idtWm7TTryHnvXBX5F+nZlcJIMCQPZi6JGr0pZigwJEbEKAMPoW0oqB44cg+X+JQkIqu69z\/rNBkMISLSIkAi5pIZWewE7DTsCtqwXHxNnBMPY1yR7B5Jxlra6E2hNwNpTGtaGYlmVAxnyFXWCF91ZX0CHl8JI503HpAxga\/XYmg2MA6SALOgeMnK8ksCpwzib0rGcB5adYdiYF8xAySIkBWP56Y4NjUzNIykFZGmBpn5z3+q9dLcKWfWV9NGZKm39fHczsR4acJvXeGtnqUdh52ItqeI\/ItkII9N3eme2Kc1oMa+bhVyPnAsIhNqyhd10UqSWZ42aHTzbWBUc3JXc6M3LSe5Cj9yILtR3bpFyZjJNHV7nxQqos6oux\/rEHke2hb9hDbHKAYBlAxAT5cZaRTFzPDZUP18zD5VVXeMBg3MYS+YfOHv6SYbmg3jm2R50D2nWstJqUDGlrTWkEVstOzse4hZfSr1NZWJ9Qq5N0e8bR+JvEBQihu88wAlN9syzUEdYsyyyd2DXZd+qVuestE9XizJwb7ESuSAhmfAvrH3sgPedyY053sMnxsWIFo29psPr3H3ls4txXTQtK7XtxXIsBCNYW6ItmCp7bjL2OpKQ7BjXvZ\/xe7oWi286vWciXL+s5mK3Wba6DD2ypIzqRNrO48CJhpMPpTqFr93vh+lxcmcNS4Ld7ORdSRcUoFZGRFMjxrSrBXvlmCZBg8IMYrrwTGIrHlJqetwABgBVLgX6IrmdarqOo+i6isJtlzCCm1DSFVQqHq0nUblZ3A8mWh0QWgcbCFHvEpOG4PQG77NHmLQXB4ZAYteRAbeswMrOc8r\/ErLN7naWLYhfhKVdZo6ywqR6D3GnJW\/Y0b761QS2aN\/7g4k0N8mQJ2LAumWWVvk4rBGT8LtQfu3tTssdmKrHSC33FQvr3Hz67RsbyxFRHX1pmO\/mONzsbdLh3L5AN2kDGEhXpAaMeJunc7WbjptNgW2dDyx4vRmt3nF2wLbtudouAmR3A3IjHeH7HZiW5f7d8ldfU9FKDww5QXw8I586JPVT0rB\/Y6jdjvc0bJZRr5pFxhIyQi9KJ+a8wsvKPG9uYb\/4MAgArljH6lgZrLzvurp45+y0ISm2LZFyLU7UxUmQnSSkdtd\/Zaa7TFIp0bwD6tXOT7yRLcAzG5NxCe7J63q0JC8uwxm0yBCfNJSul810Je7ccHvTK2Htnc+LuuL3Vi4KKyiqXvA7MxUkL+NCkkgzDVWLPhNyF33qWoqhhb6+UYpKSyuxJUOtbUHu2uiml1eZDsdRHf78AgHn\/KE+W9fptIDGd43w20nwizAU9Qd6pqwqHbt6TQ4erkNPlmOwbUCgUkqDz+Z122NJ5K7tWh+2Zi58TxoWoWBG1A4Xvk46EybHNqrw0F0dpWhAIm\/7gCBSEysR8k7PHZiyxGjStJRIrFtIDV3xulZTRMSBBTJCca8Oj2PDFnnaz6TUH1eyTHULZHqEFn4g2DKdZzVpVLZsAOBXp9YznROnY8Hy1eGVwBoeFKDnBA+j7Hk05Wvb6gP4rwztzMZbVmFXKsTWslTZ62Dw4XDXYWLJAEhwy\/unmDI7NW2KppVZvf7h2+Zfu7p69z+Sw1PZKlrUYTML0lfMeCJc7kn449Mk1mjXQDlU\/IyM4yIvEWZlr77P413CdJaABs7pVBdn1dQ+rAD4DS00d3+BocYHSpWmYCXZM4jRQgxQyKmmrw8p3uaztaAx9BzSl8cLBSMxEIZjvRvvwERgZiTelKX9+Syz1efv6xLzB\/cKosuo6EYS7xlE1sVDI6tMxs1CdWsDN6BJemU+XQolWLsgC0h6ccCoXBodNx2BHkzy24Sn867QFpZSiLENNh43MzQe37NdUtvuwDaVnND+URrMuAcQsaF+sf+imzZk9Nn+Jpf4OCxbwA1ecWAXh\/0kZryMSlBpOkO4TYU+h2Se+hmY9hL9\/acagZdfwUFmB1QKApXPwZcn2w\/G9YE9wqPNFY83XbO7ZHxzsn0uQl+9m6nRlb74smu+QU57JKXH6vNzYzA2mLnBSGhzh5gyO5zOD5DSUZUfGs\/c\/+XBRarmYo2rE4EB\/c4sPr88Ucyx0DC4YgLmUDh30b2ZNpAxNyx4n+GCJPcCeS+SHNcFn2JPlNmDgWUzgLM4k7C3jnH+XmZnI4YS7sY258veYXH+VOYIIQ+b4bAw9\/D5sII39hetBHP3Iuqu+clf3rD1LQbntABlHUeKaW7Chp5QE7NSYzf5bgV7ZQVuBw37aJwwHa1Kt+aA7eiILQtayHDz9QvaTSbVI0P0tsjTiZveaYIK5zWkyEZI0RDbcm30v2w5VjvfJyhbZ70i+6biv7\/AFjNl4kNvGIIYIQkh5E4bl4cA63px9x38uQABg5UAaJF\/9S\/esPRcHYdtOMqpFlNABim4zQkFjmjNi00m9PmFXGkn3chBbMG4z2NYPd5J+U7Np+GIGARdsKxrblykhz2P1wLlJprtfgvo+NZAnMrYhGYadNVsWaL5sbc04NqR0cXp4FC1EKVSSgAlPIuTXo7LyOYyV6fiiDBAAWLmc0Q8xc2XpD5WocpAIyzNlXI8o7YEKB4Cu1VyjDIL9oTsbTk2orYkniTVg4wZ9tFDpXQls47BqMDEKykMtyMmJ4jmzhHvYapeLRsmk7OhYgeopSV1eiWPNEsqfkQ\/ZMv\/d4FkB4BpAh2D9w3ekZ1g+X0dX\/EcCJOUWrhhYMhRT9FYZ1x4VQSlk5tghHFewAciWZx6zeYhcvQ10ZUON5Cjh9jIxJXmkYVtgWxTrvgPp35P+380siWyjU83zADBVDk3VeSPDGn6IpOi4OjYqm+lwYWM2AAulF3xeVez+vlRRjAQzH4Whh659PpryFyhAkOn7PnH1GY\/KWuVNEtEaEiLIJS8MkqBlQGgFBlxrpnbmyA46g+FGthxmm7ACUy2hlLIGMLxKAI9erxuF8\/xsrS\/wsHaVDOt4n5wLSZQvs8Hx\/qjWAsYn4hKRG0OfTUYoUHEQ+AKHkSJWkPLjGH5kKdD7vDTl\/1kUqwDZmrX3R3sRlP8MliXmKDV0Zw8aA689Qs4+yUsbB4xrrLe61TTyAydtZIphr4s61nU1Ni7gWeDi4oFh0frqGJaWyPg+NtefPdJKYxGM8Ml\/sp41yIdkmT3IGNAtTmgkYYll9BUMP\/L55wuxemEzSOOx7MgYvf3hYzecOUBcOxJEEZFIFd7UxaJM5U0rJTyTczINMo0tP7KGXGyxTtnMKtBpLuS8vW2PdvLoFFuLTPoSkult4rB0htmDqC9AZ\/76vFjAbqFr833NSYd5CVoUHAryRL4+hFx195iCIygxx99TgiP+Tx3XAC\/EY+WARG9\/uP6ab9zdMWO3OwSJI5AsWKkNNDFDbypNqNXiLJneEyBPc0kObhHZAs6+GYJrW9FUg1Reo0FaNPdXLHavY6hYQO6kbFbjgnGdBEl9r0SHnGEr9hue4y4otxmVhDzQL9kZSH0kmYPl9zD08IlKQ84v7QBRgmToum+u6NhyjztIBIcTOGjwh1hDqFzQqx9xyfXHcmUPLjDiyUswz4E0rcDAug96kXusU\/8JKNorN2cOzcidSr1midOZ75NjDZkUjSEyZ0MNxcksNTLIWveDdy+cCMWmmnAESxYcECWGPPOFCo4XNkAaQbLb0aWhm364onPLXe8gBEeAEDDHMt26pGbCCvk5smch2QFQSZLQxQL0OYFnWq0fLmvg5lBRdFQKno3AAl9wVnhoLr4YjD0SHWKGb77ChnENckqPvfJLZvmY7mCQ56yT419dsgwFQ8JEroeCEElwnPBCBccLHyAA8OTNaSY5fUX7zN3+JUBvJqIWZhkDLLysVXYbZnrmGMZSlnNfmp2lita\/N9vN8N36sMoeNtZqPdQRclwEvuaejF111u0N9Odkx4zHuw+vZwhvXz2Wybjj+7KmvhEcKVr1DQw\/\/IkXMjheGBTL9+jtDzGwJJq56wf3jsPS7wXRFnG9FhFR2ECHSLtxtUOYlwc6Y5atBp6LpFCNsseEYpuJTWdxpovIOcWkxyp80OgRnMqGvovDpaKIgj0Ru3xjj5MTFwwFmwWJ67\/VISADRCSI4\/hTGHnk9Bc6OF5cAQIAvb0hBgaiybu9Z4eSaLlUUGm+jCt1ZoQwShsupH+bzbWfqm7T6m3FEgWpYk1qyBpIKtymnLXsUEXPZTiZVftr6Tm8JtSaU+YVVIyabRg2g3HV97XZUlOTs+PkKLL\/4MWpMWzEkt+PkUd+qaBV\/EIeyeBFFSArV0r09oaj1\/\/2mc6piy9i8G4UlOdxHMVpgJBJBPQKpzkJdrmImTGtpiw4XDCyfnOTt+TKxaSNfWxHyWbYMhslIfzSP0YPksO3zD6gwWV06ln4sptlMushIgKN4Z7NnoqNPzBmIhGRCJl5FRC\/FSMrN8ie4OUVII0gQV8w9NR5Q0Pbd53fXumZKcLy7pAxwJIzKRiv7KUfTTK6V82SYYykPWquVi+V4aVaGrmHd\/AsHzWj9LsNOw0BDK2s8lDfXX1bUVoYm0gujSHdMCXWBCEgb4XAGzG08qZkQr4yerEcxxdfgAAAVjDQL7DyHDn85C2\/a5++eC0RvS5t3iIAgl2q7D60xynEYNXb3uBQe5HkyEu3MEFez7NaErJzrReqvYOtAFLkqGXMcIzXSu6eAwVicNy0byiab3Bx4+36e5IAAokAkBehHByOdQ89kZzHlfGL6SS+SAMEAAaS972vLxj+63nXd05ddA0DB1BQmsSyHiVLRYqckOreZGs6saZQaSoTKj1IoQCas2SzpXTgynJuFRSniJyu1wsPLGzNNMgEKiyFFLh2\/tnZO9AYFNQLkCkqaFAigAIGMaQ8GcOPfByjq6vYRIG3l0eT3qR5n7LgPdNRwo8pCN7MUQ0JFIyguWWCOTCEU5zaq83LttBD1iwXU+ub2TvYmaZASBteMxwUbB\/65YQ81BFv5eRqsseAUDXKYiZAgoIAHD\/ETEdj+KGGXyC\/0M34f2EGMfqSvr5gZOD8wZGnb7uwfcrCYYAOIBGUWMZRY3bF3iYU8NIxTJIhu5p92KINrmGhDh5ow0lddEQlNcKppq6u6pLV7Jv+8YB\/NRfOHoR0JphzwOfpurXsQs3nHUTgmCBEIigtLwKCwzH84J34D\/OqXroBAiQi2cltK0aePvPati12+BNAi0VQms0cUWIQn1jAmRNhVYWjYFNO\/1rB4pANjbrQJs2b0YaNPT1K4dZeEdvX8nMHWQotyJnPlDfKRGPMIK4GnJr0IgCnWQNrmHAchh7+PGqrR7CZ1Ude3iWWp+TCggXlyfSKUwB8jgSFMq4nQQKIscjp+IOFvTevEjwugWj1uVg3LgWaKTi63WndMkQFCo86tX+M4tE8hkMxRm\/Axi+QIFREYMm\/RUCfxPoHH3ixl1QvjQABAPQLYIkEgMk7vX13Ar7JJA7guA5IGSWcLq2sUqgkbk2nIgPLwpmLQ++q4fns3u0wJUntnZbigactMWoqRLpmKKSPHYnGdNDd9PWCvxczIFKZpyeY+BQMPnJO+rUXzXzjZRAg6evv7Q0wMBABwMQd+44B4QuCghlS1gApYxAJbgzulNtVP+y6Dx97bd1yCJcLvUWKqB3q4ZcNRiLBxwZQ5YBSlXv2KTKyTZ5Ug8k42NnCUtHXNmBcnlBCSATJzJJ\/DJZLMLLyKeR7R\/K\/7YAF+G9\/rFwpk2wygMqzK24SUxaeF3DcAtDOJMISy6ghWivsksi8qX3iziqQmyuEmGRCP1IFKrCD06bp7N8+JDgUTKwsok8Zsz7Ecetr9gJGs05mA+64TVNuHCSlSiOAEAz5N5B4N4Ye+hHq64bwIuBTvZwziLs3AdCz4+G7EPMpBBwBABzXJSd8OOFbzy3wtNCzCDOc02pvw8zUXE6IbTfZMRv3OLKcNWfxNdHeTGHFk0FglIlanAhABGZ5J5i+guGHLlQu3\/\/awHhpBkj2O\/UJYFkMAJN2eOv\/SMmfAuFAMIFlLd3dJZHNRWxbMYvXNLb9bec8RlFnBPwWdNjQGYoTFrZXdP0B0ox16Cq\/GnqkhDQwJN8HwhkYis8BVlaUpCRfIofppfroT+vepJHv3vbg15OgTwB4HUiA4zpSaDjQGnZHJvEjYUVlkfbfmWB3M3lQvXkfm9I7F1JUClODsw9Bo5ajjGzIaY9BIBJJyyTvYeD7GK78AnhyRMka8Uvstn2pP\/oCYFmW6nu2edNrmKLjALyJRFDiOAJzHKdnJTAPtGcy30DB2KLhKw2zRkVvbuts7H00CcYMCYPGOVNtB8baZBc8ZJptQ0CkTypvYokfYqR+AfDY6EupnHqZBog7UCZu84ZFMfEHmfltRGIaM4NlHWCks5SsoSdnT+B1X3I22GNw0rV5Ur4ZB3vVIMd2Ps0MYg4wAMRJA05BsoAu6wD+yET\/h8GHLld+0Es2MF6GAaIGygJulF6d8w6aAhkdQZL+H0PuRxSAZQzmOBErYxbcYFiwe3uv+QKXOeNwLDIVBIABE7OpW8yKtfJYH0b\/kYk\/50EBMMuHAFoK4DwMP3SngX6+pAPjZRwgao+yghrNPAB0b\/0\/e0Zx9HYCHwyi7RMXAgnmiMGI0xlKg2pPzewWnMiYe48DftG8Ij93b+bI9mY8QgmNiE09xUlQg3Ai5Som\/AXgizA0+hfg6eH07wil+eaXyyl5GQeI+h70BsBAvt65YEG5Y3j6vhzHbwXx6wGan4isSCRaEhwry1vCXYZtiJOT2V80NTrVBp5Fk3CFSZuohSS9d5i7jQMs5XMALYeQv4ekKzD88NNGtnje7AXGA+S\/LqssF8BAToeYN6+ldXj67iLAgczyQDB2AYmOpOqKk1KJpWTIRtdMCWlS281gRTyamvh9wOk5yNISafAIJDQQp3QxnghAwGlAUKOpZ9wN4GomvhwIr8PQA6uMoMDLLVuMB8gGvS99AniGtGAB0DZzz1mQYg\/J6AV4D4AXgGhCXs3LRtAgaWQUJXgGUSI6R\/aeuyJlarN72RDUVjfAUsiNKC+D0uyQwVlxFUwPgnAzg68F0\/UYfngFdF7Uy7KEGg+QzRsslspG+5Q9psdC7siMXQBaDJYLAMwFeApIWKUWNzSG7ck3c1IBKYHQyEKJz3xOgoRGUs+gqMQ\/cRCgRwHcy8AdIHkLRHgn1j\/4kKNMGs8U4wGy2R8C6CNfwCTd\/t6TSsHoDBI0TxLmEMutGbwVM08FeAoYE8FoB2QLCOWk6rIXsXKkK8lGAFfBPILEc+w5Ap5h0BMgegiMlQj4IUTxoxj599OeniEwssR4UIwHyH8yw0xlddbieQSYsVsL1le7EHBnKYjaOebWKEA5kFRuNPwxIiBGDSQixPUKAhpFFA6jPViH5zorwM31Jq8rAHop3e3n8YAYD5AX2\/tKQK9hLzGwuYXQ0iDInn88GDbz4\/8DuwcjJxpqCtIAAAAASUVORK5CYII=\" alt=\"MeltX mark\">\n      <div>\n        <div class=\"brand-name\">MeltX ITAM<\/div>\n        <div class=\"brand-sub\">Audit Console<\/div>\n      <\/div>\n    <\/div>\n\n    <div class=\"nav-eyebrow\">Handover documents<\/div>\n    <ul class=\"nav-list\" id=\"navList\"><\/ul>\n\n    <div class=\"sidebar-status\">\n      <div class=\"status-row\"><span class=\"k\">RBAC<\/span><span class=\"v crit\" id=\"statRbac\">OFF<\/span><\/div>\n      <div class=\"status-row\"><span class=\"k\">Test coverage<\/span><span class=\"v crit\">0%<\/span><\/div>\n      <div class=\"status-row\"><span class=\"k\">Open findings<\/span><span class=\"v warn\">34<\/span><\/div>\n      <div class=\"status-row\"><span class=\"k\">Generated<\/span><span class=\"v\">Jul 2026<\/span><\/div>\n    <\/div>\n  <\/aside>\n\n  <div class=\"main\">\n    <header class=\"topbar\">\n      <button class=\"iconbtn menu-toggle\" id=\"menuToggle\" aria-label=\"Toggle menu\"><\/button>\n      <div class=\"crumb\" id=\"crumb\">Command Center<\/div>\n      <div class=\"topbar-spacer\"><\/div>\n      <button class=\"searchbtn\" id=\"openSearch\">\n        <span id=\"searchIcon\"><\/span>\n        <span>Search everything\u2026<\/span>\n        <kbd>\/<\/kbd>\n      <\/button>\n      <button class=\"iconbtn\" id=\"themeToggle\" aria-label=\"Toggle theme\"><\/button>\n      <button class=\"iconbtn\" id=\"printBtn\" aria-label=\"Print\"><\/button>\n    <\/header>\n\n    <main id=\"app\" class=\"blueprint-bg\"><\/main>\n  <\/div>\n<\/div>\n\n<button class=\"back-top\" id=\"backTop\" aria-label=\"Back to top\"><\/button>\n\n<div class=\"overlay\" id=\"searchOverlay\">\n  <div class=\"palette\">\n    <div class=\"palette-input\">\n      <span id=\"paletteIcon\"><\/span>\n      <input type=\"text\" id=\"paletteInput\" placeholder=\"Search findings, features, modules\u2026\" autocomplete=\"off\">\n    <\/div>\n    <div class=\"palette-results\" id=\"paletteResults\"><\/div>\n    <div class=\"palette-hint\">\n      <span>\u2191\u2193 navigate<\/span><span>\u21b5 open<\/span><span>esc close<\/span>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\n\/* ==========================================================================\n   MeltX New Suite ITAM \u2014 Audit Intelligence Console\n   Content data compiled from documents 00\u201306 of the engineering handover.\n   ========================================================================== *\/\n\nconst DATA = {};\n\nDATA.meta = {\n  org: \"MeltX Software Solutions\",\n  product: \"New Suite, ITAM\",\n  suite: \"MeltX Enterprise Suite\",\n  generated: \"July 2026\",\n  note: \"Source-of-truth documentation reconstructed from the code. Paths are relative to backend-dev-fam-changes-locked\/ and frontend-dev-itam-locked\/.\"\n};\n\n\/* -------------------------------------------------------------------------\n   Global stats \u2014 used on the command center + sidebar status readout\n   ------------------------------------------------------------------------- *\/\nDATA.stats = {\n  backendLOC: 135000,\n  frontendLOC: 331000,\n  totalLOC: 466000,\n  backendFiles: 890,\n  frontendFiles: 1216,\n  totalFiles: 2106,\n  modules: 7,\n  testCoverage: 0,\n  todoCount: 38,\n  consoleLogCount: 343,\n  dupFiles: 28,\n  reportViews: 129,\n  apiActionsApprox: 150,\n  collectionsApprox: 145,\n  rbacStatus: \"OFF\",\n  findingsTotal: 34\n};\n\nDATA.severityCounts = { critical: 4, high: 10, medium: 13, low: 7 };\n\n\/* -------------------------------------------------------------------------\n   Navigation\n   ------------------------------------------------------------------------- *\/\nDATA.nav = [\n  { id: \"home\", num: \"\u2014\", label: \"Command Center\", icon: \"grid\" },\n  { id: \"overview\", num: \"00\", label: \"System Overview\", icon: \"layers\" },\n  { id: \"business\", num: \"01\", label: \"Business Overview\", icon: \"briefcase\" },\n  { id: \"functional\", num: \"02\", label: \"Functional Spec\", icon: \"box\" },\n  { id: \"technical\", num: \"03\", label: \"Technical Architecture\", icon: \"cpu\" },\n  { id: \"analytics\", num: \"04\", label: \"Analytics & Code Health\", icon: \"activity\" },\n  { id: \"security\", num: \"05\", label: \"Security Audit\", icon: \"shield\", flagCritical: true },\n  { id: \"questions\", num: \"06\", label: \"Open Questions\", icon: \"help-circle\" }\n];\n\nDATA.docCards = [\n  { id: \"overview\", num: \"00\", title: \"System Overview\", desc: \"What the platform is, who runs it, the tech stack, and the one architectural idea to understand before anything else.\", stat: \"7 modules\", icon: \"layers\" },\n  { id: \"business\", num: \"01\", title: \"Business Overview\", desc: \"The problem it solves, eight personas, the ITAM value proposition, and core workflows end to end.\", stat: \"8 personas\", icon: \"briefcase\" },\n  { id: \"functional\", num: \"02\", title: \"Functional Specification\", desc: \"Every feature by module, full lifecycle coverage, the real permission model, and three user journeys.\", stat: \"150+ actions\", icon: \"box\" },\n  { id: \"technical\", num: \"03\", title: \"Technical Architecture\", desc: \"Request lifecycle, authentication, the data model with 145 collections, and the full API surface.\", stat: \"145 collections\", icon: \"cpu\" },\n  { id: \"analytics\", num: \"04\", title: \"Analytics & Code Health\", desc: \"Reporting and KPIs, plus the honest engineering quality signals: tests, dead code, complexity.\", stat: \"0% test coverage\", icon: \"activity\" },\n  { id: \"security\", num: \"05\", title: \"Security Audit\", desc: \"Threat model and 34 findings, four of them critical, each with an exploit scenario and a proposed fix.\", stat: \"34 findings\", icon: \"shield\" },\n  { id: \"questions\", num: \"06\", title: \"Open Questions\", desc: \"Everything the code alone could not answer, grouped by owning team, waiting on confirmation.\", stat: \"25 questions\", icon: \"help-circle\" }\n];\n\n\/* -------------------------------------------------------------------------\n   00 \u2014 System Overview\n   ------------------------------------------------------------------------- *\/\nDATA.overview = {\n  intro: `New Suite ITAM is MeltX's in-house IT Asset Management and IT Service Management platform, built and branded internally as the MeltX Enterprise Suite. It is a single web application that follows hardware and software assets through their entire lifecycle, runs a service desk for incidents, requests and change approvals, maintains a configuration management database fed by a discovery agent, and layers procurement, contracts, finance and depreciation, compliance tracking, dashboards, and a no-code form and workflow builder on top of all of it.`,\n  stack: [\n    { name: \"Backend API + worker\", tech: \"Node.js 22, Express 4, Mongoose 8 (MongoDB), Redis \/ BullMQ\", role: \"REST-style API, background jobs, global search indexing\", icon: \"server\" },\n    { name: \"Frontend SPA\", tech: \"React 19, Vite 7, MUI 7, Zustand, React Router 7\", role: \"Single-page web client\", icon: \"monitor\" },\n    { name: \"Discovery agent\", tech: \"meltx-agent 1.0.3 \u2014 Tauri \/ MSI installer, out of repo scope\", role: \"Pushes hardware & software inventory into the CMDB\", icon: \"radar\" }\n  ],\n  users: [\n    { name: \"Super Admin\", desc: \"Unrestricted access that bypasses every access check in the system.\", icon: \"crown\" },\n    { name: \"IT asset managers \/ administrators\", desc: \"Manage hardware assets, procurement, software licenses, contracts and vendors.\", icon: \"clipboard\" },\n    { name: \"Service desk agents \/ support groups\", desc: \"Work tickets, changes and service catalog requests.\", icon: \"headset\" },\n    { name: \"Auditors \/ asset-audit teams\", desc: \"Physical and mobile asset verification, gated to a specific team.\", icon: \"search-check\" },\n    { name: \"End users \/ employees\", desc: \"Self-service portal: raise requests, view assigned assets.\", icon: \"user\" },\n    { name: \"Discovery agent (machine identity)\", desc: \"Authenticates with a separate agent token to post CMDB data.\", icon: \"radar\" }\n  ],\n  valueProp: `Consolidate hardware assets, software licenses, configuration items and the service desk into one system so the organization can answer: what do we own, who has it, what does it cost and what is it worth now after depreciation, are we license-compliant, what is under warranty or contract, and what is the audit trail of every change.`,\n  backendStack: [\"Node.js, pinned 22.19 on the frontend via Volta; CI currently builds on Node 20.11\", \"Express 4 with Helmet, CORS and Morgan logging\", \"MongoDB via Mongoose 8; Redis via ioredis; queues via BullMQ\", \"jsonwebtoken (HS256) + bcrypt; client-side RSA password encryption via node-forge\", \"AWS S3 and a custom \\u201cL3\\u201d object store\", \"node-cron, cron-parser, BullMQ, and a proprietary vendored global-search worker\", \"xlsx, csv-parser, pdfmake, EJS email templates, nodemailer\", \"Modular monolith with a path alias per module: core, fam, cmdb, sam, ticketing, impexp, fleet\"],\n  frontendStack: [\"React 19 + Vite 7 + SWC, MUI 7 + MUI X (DataGrid, date pickers, tree view)\", \"Zustand for state; React Router 7 for routing; axios for HTTP\", \"@xyflow\/react + reactflow + dagre for workflow graphs; jodit-react + lexical for rich text\", \"qrcode.react, react-barcode, react-qr-barcode-scanner; react-leaflet for maps\", \"jsencrypt (client-side RSA, pairs with backend node-forge)\", \"echarts-for-react for charts; papaparse + xlsx for import\/export\"],\n  archFlow: [\n    { id: \"spa\", label: \"React SPA\", sub: \"Bearer JWT + RBAC hint headers\", col: \"client\" },\n    { id: \"agent\", label: \"Discovery Agent\", sub: \"Long-lived agent JWT\", col: \"client\" },\n    { id: \"proxy\", label: \"Reverse proxy\", sub: \"Nginx \u2014 unverified\", col: \"edge\" },\n    { id: \"api\", label: \"Express API\", sub: \"server.js\", col: \"backend\" },\n    { id: \"dispatch\", label: \"Dynamic dispatcher\", sub: \"\/api\/v1\/exec\/:module\/:ctrl\/:action\", col: \"backend\" },\n    { id: \"worker\", label: \"Search worker\", sub: \"worker.js \u2014 change streams\", col: \"backend\" },\n    { id: \"mongo\", label: \"MongoDB\", sub: \"Mongoose ODM\", col: \"data\" },\n    { id: \"redis\", label: \"Redis\", sub: \"BullMQ + search cache\", col: \"data\" },\n    { id: \"s3\", label: \"S3 \/ L3 storage\", sub: \"Object storage\", col: \"data\" },\n    { id: \"smtp\", label: \"SMTP\", sub: \"nodemailer\", col: \"data\" }\n  ],\n  dispatcher: `Almost the entire API is a single meta-route. Instead of one route file per resource, requests go to GET, POST, PUT or DELETE \/api\/v1\/exec\/:module\/:ctrl\/:action\/:_id?. The dispatcher validates the module, then calls the root controller, which resolves the matching controller (a \"JController\") and calls its ExecGet, ExecPost, ExecPut or ExecDelete method. Each JController publishes a per-verb allowlist of action names, and only actions on that list can be called. This design is central to both how features are added and where the security risk concentrates.`,\n  dayOne: [\n    { t: \"Access control is feature-flagged off.\", d: \"FEATURE_RBAC defaults to OFF and is OFF in the committed env. With it off, any authenticated user passing the controller action allowlist can call any action.\", flag: \"critical\" },\n    { t: \"The API is one dynamic dispatcher.\", d: \"Not conventional REST routes \u2014 get comfortable with the module \/ controller \/ action pattern before tracing a feature through the code.\", flag: \"info\" },\n    { t: \"Schemas can be created at runtime.\", d: \"A form builder compiles Mongoose models on the fly from DB config via change streams.\", flag: \"info\" },\n    { t: \"Modules are separate git-subtree repos\", d: \"stitched into src\/Modules \u2014 a change to a module is a change to its own upstream repo.\", flag: \"info\" },\n    { t: \"Secrets are committed in the repo.\", d: \"env \/ env.production contain AWS keys, JWT signing keys, SMTP creds and the admin seed password. Treat as compromised and rotate.\", flag: \"critical\" }\n  ]\n};\n\n\/* -------------------------------------------------------------------------\n   01 \u2014 Business Overview\n   ------------------------------------------------------------------------- *\/\nDATA.business = {\n  problem: `Organizations lose money and control when they cannot see their IT estate: hardware goes missing or sits idle, software is over-purchased or under-licensed (audit risk), warranties and contracts lapse unnoticed, and the service desk works from spreadsheets. New Suite ITAM puts hardware assets, software licenses, configuration items, procurement, contracts, finance and depreciation, and an IT service desk into one system with a single audit trail.`,\n  personas: [\n    { name: \"Super Admin \/ platform owner\", what: \"Full control; seeded at install; bypasses all access checks.\", icon: \"crown\" },\n    { name: \"IT asset manager \/ administrator\", what: \"Registers assets, runs procurement, manages vendors \/ contracts, depreciation.\", icon: \"clipboard\" },\n    { name: \"Software asset \/ license manager\", what: \"Tracks entitlements vs installs, runs compliance, manages renewals.\", icon: \"key\" },\n    { name: \"Service desk agent \/ support group\", what: \"Handles service catalog requests, fulfillment tasks, knowledge base.\", icon: \"headset\" },\n    { name: \"Asset audit \/ mobile verification team\", what: \"Physical and mobile asset verification and approvals.\", icon: \"search-check\" },\n    { name: \"Approvers \/ managers\", what: \"Approve procurement, changes and requests via workflow.\", icon: \"check-circle\" },\n    { name: \"End users \/ employees\", what: \"Raise requests, receive assigned assets, self-service.\", icon: \"user\" },\n    { name: \"Discovery agent (machine identity)\", what: \"Pushes hardware \/ software inventory into the CMDB.\", icon: \"radar\" }\n  ],\n  valueProps: [\n    { t: \"Know what you own and where it is\", d: \"A hardware asset register with tags, serials, RFID, location and department, plus physical verification.\", icon: \"map-pin\" },\n    { t: \"Stay license-compliant\", d: \"Reconcile entitlements against installs and agent-discovered software; flag over-deployment and over-licensing.\", icon: \"key\" },\n    { t: \"Control spend\", d: \"Procurement request to purchase order to receipt, with vendors, contracts, and finance \/ depreciation.\", icon: \"trending-up\" },\n    { t: \"Run the service desk\", d: \"Service catalog requests, fulfillment tasks, SLAs and a knowledge base.\", icon: \"headset\" },\n    { t: \"Single source of truth (CMDB)\", d: \"Configuration items and relationships fed by an automated discovery agent.\", icon: \"database\" },\n    { t: \"Auditability\", d: \"Per-module change logs and denormalized history on every record.\", icon: \"file-text\" }\n  ],\n  capabilities: [\n    [\"Hardware asset tracking\", \"implemented\", \"\"],\n    [\"Asset assignment \/ possession\", \"implemented\", \"\"],\n    [\"Asset movement \/ transfer\", \"implemented\", \"\"],\n    [\"Asset disposal \/ retirement\", \"implemented\", \"\"],\n    [\"Depreciation \/ financials\", \"implemented\", \"\"],\n    [\"Maintenance \/ work orders \/ parts\", \"implemented\", \"\"],\n    [\"Procurement \/ vendors \/ PO\", \"implemented\", \"\"],\n    [\"Warranty \/ AMC \/ insurance\", \"implemented\", \"\"],\n    [\"Software asset & license management\", \"implemented\", \"\"],\n    [\"License compliance\", \"implemented\", \"\"],\n    [\"Software contracts \/ renewals\", \"partial\", \"Renewal fields exist; no automated expiry-notification cron found.\"],\n    [\"CMDB \/ configuration items\", \"gated\", \"Flag-gated behind APPLICATION_CMDB=ON.\"],\n    [\"Discovery agent ingestion\", \"implemented\", \"\"],\n    [\"ITSM: service catalog \/ requests\", \"implemented\", \"\"],\n    [\"ITSM: incidents \/ problems \/ change \/ CAB\", \"partial\", \"Served by a separate ticketing service; this repo covers catalog + KB + SLA.\"],\n    [\"Knowledge base\", \"implemented\", \"\"],\n    [\"SLA management\", \"implemented\", \"Engine lives in core workflow.\"],\n    [\"Approval workflows\", \"implemented\", \"v1 live; v2 is a dormant rewrite.\"],\n    [\"Import \/ export\", \"implemented\", \"\"],\n    [\"Reporting \/ dashboards \/ KPIs\", \"implemented\", \"\"],\n    [\"Fleet (vehicle) management\", \"stub\", \"Empty placeholder module.\"],\n    [\"Audit trail\", \"implemented\", \"\"],\n    [\"RBAC \/ access control\", \"stub\", \"Implemented but OFF by default \u2014 see Security Audit.\"]\n  ],\n  workflows: [\n    {\n      t: \"Hardware: procure to deploy\",\n      steps: [\"Manager raises a procurement request, routed through an approval workflow\", \"Approved request becomes a purchase request with vendor quotes, then one or more purchase orders (PDF generated and emailed)\", \"Goods arrive: inward receipt, then QC\", \"Asset lands in inventory with tags \/ RFID, then is assigned to a user or location\", \"Asset can be moved \/ transferred, maintained (work request \\u2192 work order, consuming parts), depreciated over time, and finally disposed\", \"Physical existence confirmed periodically via mobile \/ RFID \/ possession verification\"]\n    },\n    {\n      t: \"Software: license to compliance\",\n      steps: [\"Software procured through the SAM chain (request \\u2192 PO); a contract is captured\", \"PO becomes entitlements (total licenses purchased), feeding a license inventory pool\", \"Licenses allocated to users \/ devices; installations recorded\", \"Installations linked to the entitlement that covers them\", \"A compliance job reconciles purchased vs. max(linkage-consumed, agent-discovered), flagging over-deployment (audit risk) or over-licensing (waste)\"]\n    },\n    {\n      t: \"Service request fulfillment (ITSM)\",\n      steps: [\"Employee requests a catalog item (laptop, software license) from the service catalog\", \"A service request is created with one RITM record per item; a catalog template defines tasks, approval workflow and SLA\", \"Tasks route to assignment groups; SLA timers run in the core workflow engine and escalate on breach\", \"Fulfillment tasks with an itamFormTrigger (Asset Issue \/ Transfer \/ Return, Software Allocation) drive the corresponding FAM \/ SAM action\"]\n    },\n    {\n      t: \"Discovery and CMDB reconciliation\",\n      steps: [\"Windows discovery agent authenticates to \/api\/v1\/agents and posts hardware \/ software inventory\", \"Ingestion matches each record to a CI definition and to a FAM asset by serial\", \"CI instance is upserted and an audit diff is written\", \"Unmatched records become flagged data for manual reconciliation\", \"This CMDB data also feeds SAM software-discovery compliance\"]\n    }\n  ],\n  risks: [\n    { level: \"danger\", t: \"Access control is off by default\", d: \"Any logged-in user can currently read or change almost any record, including other departments' assets, financials, and license keys.\" },\n    { level: \"danger\", t: \"Account takeover via password reset\", d: \"An account can be taken over through the password-reset flow without access to the victim's email, because the reset step does not check the emailed code.\" },\n    { level: \"danger\", t: \"Live credentials are committed\", d: \"Cloud keys, database, mail and token-signing secrets are in the repository. Treat as compromised and rotate.\" },\n    { level: \"warn\", t: \"License keys stored in plaintext\", d: \"Software license keys are stored and returned in plaintext, exposing purchased IP to any user.\" },\n    { level: \"info\", t: \"Two scope gaps to plan around\", d: \"The fleet module is not built, and the incident \/ change \/ CAB desk lives in a separate service \u2014 \\u201csingle pane of glass\\u201d isn't yet true for those areas.\" }\n  ]\n};\n\/* -------------------------------------------------------------------------\n   02 \u2014 Functional Specification\n   ------------------------------------------------------------------------- *\/\nDATA.functional = {\n  intro: `Every feature maps to code. A \"feature action\" is invoked as VERB \/api\/v1\/exec\/<module>\/<ctrl>\/<action>\/<_id?> because the API is a dynamic dispatcher, not conventional routes. The <ctrl> names below are the dispatcher keys registered in each module's index.js.`,\n  modules: [\n    {\n      id: \"core\", name: \"Core\", full: \"Platform foundation\", icon: \"layout-grid\",\n      blurb: \"Accounts, roles, access, organizations, locations, groups, teams, projects, vendors, brands, categories, books, counters, settings, notifications, broadcasts, files, form builder, table generator, workflows, visibility, reports, search, cron jobs, archive.\",\n      features: [\n        [\"Authentication\", \"static user routes\", \"login, register, logout, reset, forgot, forgot\/reset, refresh, profile, publickey\"],\n        [\"User & role management\", \"accounts, roles\", \"createRole \/ updateRole \/ removeRole; user CRUD\"],\n        [\"RBAC permissions\", \"access\", \"upsert (admin), byRoleMenu, me\/:menuType, isAllowed\"],\n        [\"Org hierarchy\", \"organizations, locations, groups, teams, projects\", \"getParents, getDepartmentsByLocation, getMobileTeam\"],\n        [\"Vendors \/ brands \/ categories\", \"vendors, brands, brandmodels, categories\", \"getCategoryTree, getBrandsByCategory\"],\n        [\"Dynamic form builder (no-code schemas)\", \"formbuilder\", \"create, update (compiles a Mongoose model), getModels\"],\n        [\"Dynamic tables (DataGrid config)\", \"tablegenerator\", \"find, findOne\"],\n        [\"Approval workflows\", \"workflows\", \"getInstance, getHistory, updateSteps, getLocations\"],\n        [\"Visibility rules (config only)\", \"visibility\", \"CRUD \u2014 enforcement not wired, see note\"],\n        [\"Notifications \/ broadcasts\", \"notifications, broadcasts\", \"preview, getRecipients, getMyBroadcasts\"],\n        [\"Files\", \"files\", \"upload, download, detect (object detection)\"],\n        [\"Reporting\", \"reports\", \"saveOrUpdateReport (runs a report view module)\"],\n        [\"Global search\", \"search\", \"populated by change streams \/ worker; no direct CRUD\"],\n        [\"Scheduled jobs (config)\", \"cronjobs\", \"create, update, runJobNow\"],\n        [\"Settings \/ counters \/ books \/ archive\", \"settings, counters, books, archive\", \"branding config, sequences, depreciation books, soft-delete tombstones\"]\n      ],\n      note: \"Visibility is a stored config object, not an enforcement engine \u2014 no backend code matches a visibility document's audience against the current user. Workflow v2 is a richer rewrite that is dormant (not registered, model-name collision).\"\n    },\n    {\n      id: \"fam\", name: \"FAM\", full: \"Fixed \/ hardware asset management\", icon: \"hard-drive\",\n      blurb: \"Asset finances, inventories, possession, android verification, RFID, depreciations, parts, procurement (requests \/ PO \/ QC \/ inward), dashboard KPIs, work requests \/ orders, feedback, contracts, maintenance logs, disposals, assignments, movements, exceptions.\",\n      features: [\n        [\"Asset register\", \"assetinventories\", \"getAssetByCode, getAvailModelsFromInventory, getAssetLog\"],\n        [\"Asset assignment \/ issue\", \"assetassignments\", \"generateReceipt, revokeAsset, findTotalAssignedAssetOfUser\"],\n        [\"Asset movement \/ transfer\", \"assetmovements\", \"moveAssetConfirmation, getTransferHistory\"],\n        [\"Asset disposal \/ retirement\", \"assetdisposals\", \"disposal batches, dispose-number generation\"],\n        [\"Depreciation\", \"depreciations\", \"getAssetRatePerUnitCost, ledger generation\"],\n        [\"Asset finance\", \"assetfinances\", \"book \/ tax cost, written-down value\"],\n        [\"Physical verification (mobile)\", \"androidAssetVerification\", \"verifyAssets, verifyAndConfirm, verifyandRecorded\"],\n        [\"Possession checks\", \"assetPossession\", \"getCurrentSchedulePeriod, interval schedules\"],\n        [\"RFID tagging\", \"rfid\", \"saveRfidTags, previewRfidGeneration\"],\n        [\"Exception handling\", \"exception\", \"createResolvedException, skipException, resolve\"],\n        [\"Maintenance\", \"workrequests, workorders, maintenancelogs, feedbacks\", \"reviseWorkRequest, findMaintenanceLogByAsset\"],\n        [\"Spare parts & stock\", \"parts\", \"createStock, updateStock, transferStock\"],\n        [\"AMC \/ warranty \/ insurance\", \"contracts\", \"contract CRUD tied to an asset\"],\n        [\"Procurement\", \"procurementrequests, purchaseorders, qcs, inwards\", \"addVendors, generatePo, revisePo, generateQc\"],\n        [\"Dashboards \/ KPIs\", \"dashboardKpis\", \"assetLifeCycleKpi, getLocationWiseAssetsKPI, getAMCKPI\"]\n      ],\n      note: \"\"\n    },\n    {\n      id: \"sam\", name: \"SAM\", full: \"Software asset management\", icon: \"key\",\n      blurb: \"Procurement, contracts, entitlements, installations, linkages, allocations, compliances, audits, decommissions, inventories, logs, reportings.\",\n      features: [\n        [\"Software inventory (license pools)\", \"inventories\", \"getLicenseMappings, getDiscoveryInstalledSoftware\"],\n        [\"Entitlements (licenses purchased)\", \"entitlements\", \"getEntitlementsByInventory; create blocked (405); update merges to pool\"],\n        [\"License allocation\", \"allocations\", \"getDevices; create per assignment type\"],\n        [\"Installations\", \"installations\", \"getCiItemDetails, status transitions\"],\n        [\"License linkage (consumption ledger)\", \"linkages\", \"findLinkedSoftwares, create (increments consumed)\"],\n        [\"Compliance\", \"compliances\", \"getDeviceCompliance, makeCompliant, runComplianceJob\"],\n        [\"Contracts \/ renewals\", \"contracts\", \"createInitialEntry, createAdditionalContracts\"],\n        [\"Audits\", \"audits\", \"create (emails auditor \/ owner), update\"],\n        [\"Decommission \/ harvesting\", \"decommissions\", \"create, update (harvests on Completed)\"],\n        [\"Procurement\", \"procurementrequests, purchaseorders\", \"addVendors, generatePo\"]\n      ],\n      note: \"Compliance rule: actualUsage = max(linkage-consumed, agent-discovered); actualUsage > purchased \\u2192 Non-Compliant \/ audit risk; < purchased \\u2192 over-licensed \/ cost risk. SAM dashboard controllers exist but are not registered (dead code).\"\n    },\n    {\n      id: \"cmdb\", name: \"CMDB\", full: \"Configuration management database\", icon: \"database\",\n      blurb: \"Flag-gated behind APPLICATION_CMDB=ON. CI Class \\u2192 CI Category \\u2192 CI Item taxonomy, instance data, relationships, flagged data, alerts, support groups.\",\n      features: [\n        [\"CI class \/ category \/ item definitions\", \"Cmdb_CiClasses, Cmdb_CiCategories, Cmdb_CiItems\", \"custom attributes per class\"],\n        [\"CI instances (per serial)\", \"Cmdb_CiItemData + Cmdb_CiCoreItemData\", \"bridges to FAM asset; owner \/ IP \/ MAC \/ FQDN\"],\n        [\"CI relationships\", \"Cmdb_CiRelationships\", \"17 relationship types\"],\n        [\"Discovery reconciliation flags\", \"Cmdb_FlaggedData\", \"manual reconciliation queue\"],\n        [\"Threshold alerts &#038; events\", \"Cmdb_alerts, Cmdb_alertEvents\", \"\"],\n        [\"Change audit\", \"Cmdb_AuditLog\", \"\"],\n        [\"Agent ingestion\", \"\/api\/v1\/agents\", \"login, data, alerts, alertevents\"]\n      ],\n      note: \"\"\n    },\n    {\n      id: \"ticketing\", name: \"Ticketing\", full: \"ITSM: catalog + KB + SLA\", icon: \"headset\",\n      blurb: \"kbGroups, kbCategories, articles, articleTemplates, serviceRequestRecords, serviceRequests, catalogs, escalationMatrix, timerThresholds, catalogTemplates, itemGroups.\",\n      features: [\n        [\"Service request + RITM + CTASK\", \"Ticketing_ServiceRequests \/ Records \/ Tasks\", \"the ticket, its line items, and fulfillment tasks\"],\n        [\"Service catalog + templates\", \"Ticketing_Catalogs, CatalogTemplates\", \"item config, entitlements\"],\n        [\"Knowledge base\", \"Ticketing_Articles, KBCategories, KBGroups\", \"articles, versions, categories, groups, feedback\"],\n        [\"SLA policies, instances, escalation\", \"Ticketing_SLAs, SLAInstances\", \"timers, escalation matrices\"],\n        [\"Comments \/ work notes\", \"Ticketing_Comments\", \"supports isPrivate\"],\n        [\"Feedback \/ CSAT\", \"Ticketing_ServiceFeedbacks\", \"\"]\n      ],\n      note: \"Incident \/ problem \/ change \/ CAB collections exist in the live DB but have no models in this repo \u2014 served by a separate ITSM service.\"\n    },\n    {\n      id: \"impexp\", name: \"Import \/ Export &#038; Fleet\", full: \"Data movement + fleet stub\", icon: \"shuffle\",\n      blurb: \"Import: 3-step upload \\u2192 validate \\u2192 importJobs (CSV \/ XLSX \/ JSON). Export: builds XLSX with deep-populated refs. Fleet is a stub.\",\n      features: [\n        [\"Import\", \"3-step flow\", \"Guarded by isAuthenticated only \u2014 no RBAC. Row-by-row insert, no bulk \/ transaction.\"],\n        [\"Export\", \"exportFile\/generate|download\", \"Cells starting with = are prefixed; + - @ are not escaped.\"],\n        [\"Fleet\", \"src\/Modules\/fleet\", \"Stub only \u2014 no models or controllers; initModule runs a no-op migration.\"]\n      ],\n      note: \"\"\n    }\n  ],\n  lifecycle: [\n    { t: \"Procure\", status: \"implemented\" }, { t: \"Receive\", status: \"implemented\" },\n    { t: \"Deploy \/ assign\", status: \"implemented\" }, { t: \"Maintain\", status: \"implemented\" },\n    { t: \"Transfer \/ move\", status: \"implemented\" }, { t: \"Verify\", status: \"implemented\" },\n    { t: \"Depreciate\", status: \"implemented\" }, { t: \"Retire \/ dispose\", status: \"implemented\" },\n    { t: \"Software: entitle\\u2192reconcile\", status: \"implemented\" }, { t: \"Fleet lifecycle\", status: \"absent\" }\n  ],\n  permissions: {\n    intro: `RBAC is data-driven and enforced only when FEATURE_RBAC=ON (default OFF). A permission is a Core_Accesses document: role, key (rbacKey), menu, menuType, actions[] (list of \"module\/ctrl\/action\" strings), enabled, scope (own\/all), rowFilter. The client sends x-rbac-key \/ x-rbac-menu headers computed in the SPA; the server checks whether any of the user's roles has a matching enabled permission whose actions[] contains the requested action.`,\n    matrix: [\n      [\"SUPER_ADMIN\", \"Everything; bypasses all checks\"],\n      [\"Any authenticated user (RBAC OFF)\", \"Every allowlisted action on every module \u2014 the current default state\"],\n      [\"Any authenticated user (PUBLIC_APIS)\", \"~30 actions regardless of RBAC (dashboards, form\/table reads, report runner)\"],\n      [\"Asset Audit team member\", \"Physical \/ mobile audit menus\"],\n      [\"Discovery agent\", \"CMDB ingestion endpoints (separate token)\"]\n    ],\n    warn: \"There is no record-level (row) enforcement even with RBAC on: the scope \/ rowFilter mechanism is stubbed and commented out. See Security Audit SEC-01 and SEC-04.\"\n  },\n  integrations: [\n    [\"Discovery agent (hardware \/ software inventory)\", \"inbound\"],\n    [\"CSV \/ XLSX \/ JSON import\", \"inbound\"],\n    [\"XLSX export\", \"outbound\"],\n    [\"Email (SMTP, EJS templates)\", \"outbound\"],\n    [\"AWS S3 \/ custom \\u201cL3\\u201d object storage\", \"outbound\"],\n    [\"Object-detection ML service\", \"outbound\"],\n    [\"Geocoding (pincode \\u2192 lat\/long)\", \"outbound\"],\n    [\"External-integration webhook (\/insert)\", \"inbound\"],\n    [\"NMS (network monitoring)\", \"outbound\"],\n    [\"In-app notifications &#038; broadcasts\", \"internal\"],\n    [\"Google OAuth tokens (ticketing)\", \"external\"]\n  ],\n  journeys: [\n    {\n      t: \"Assign a laptop to a new employee\",\n      steps: [\n        \"Employee (or manager) submits a service catalog request for a laptop in the SPA. An SR and a RITM record are created; the catalog template spawns approval and fulfillment tasks.\",\n        \"The approval task routes through the workflow engine to the manager; on approval the fulfillment task (itamFormTrigger = Asset Issue Form) is actioned.\",\n        \"IT selects an available asset from inventory and creates an assignment, generating a receipt PDF and emailing the user.\",\n        \"The asset's CI record links to the user on next discovery; the asset appears under \\u201cassets assigned to me.\\u201d\"\n      ]\n    },\n    {\n      t: \"Prove software license compliance for an audit\",\n      steps: [\n        \"SAM manager captures the contract and PO; entitlements are generated.\",\n        \"Licenses are allocated to users \/ devices and installations recorded, linked to entitlements.\",\n        \"The discovery agent reports actual installed software into CMDB.\",\n        \"The manager runs the compliance job; the engine reconciles purchased vs. max(consumed, discovered) and writes a compliance summary with over \/ under-use, surfaced on the SAM dashboard.\"\n      ]\n    },\n    {\n      t: \"Physically verify assets on the floor (mobile)\",\n      steps: [\n        \"An asset-audit team member opens the mobile verification view and scans an asset barcode \/ QR \/ RFID.\",\n        \"They confirm presence \/ condition, recorded against the scheduled possession period.\",\n        \"Discrepancies become exceptions for follow-up; a scheduled job emails asset holders when verification is due.\"\n      ]\n    }\n  ]\n};\n\n\/* -------------------------------------------------------------------------\n   03 \u2014 Technical Architecture\n   ------------------------------------------------------------------------- *\/\nDATA.technical = {\n  intro: `New Suite ITAM is a modular monolith. One Express process serves the whole API; a second Node process runs the search worker; both are managed by PM2 in fork mode. Domain code is split into modules (core, fam, cmdb, sam, ticketing, impexp, fleet) maintained as separate git repositories and stitched in as git subtrees under src\/Modules.`,\n  layers: [\n    { name: \"HTTP \/ middleware\", loc: \"src\/app.js, src\/Middlewares\/\", role: \"CORS, Helmet, body parse, method guard, sanitize (writes), logging\" },\n    { name: \"Routing\", loc: \"src\/Routes\/, src\/Routes\/V1\/\", role: \"Static routes + one dynamic dispatcher\" },\n    { name: \"Dispatch controller\", loc: \"src\/Controllers\/index.js\", role: \"Maps (module, ctrl, action, verb) \\u2192 controller method; enforces allowlists\" },\n    { name: \"Module controllers\", loc: \"src\/Modules\/<mod>\/<feature>\/*.Controller.js\", role: \"\\u201cJControllers\\u201d \u2014 the business actions\" },\n    { name: \"Services\", loc: \"src\/Services\/, module services\", role: \"Schema builder, access, storage, logging, email, counters, search\" },\n    { name: \"Data models\", loc: \"src\/Modules\/**\/**.model.js\", role: \"Mongoose schemas \u2014 static + runtime-compiled\" },\n    { name: \"Jobs \/ workers\", loc: \"worker.js, src\/Jobs\/, core\/cronJob\", role: \"Search indexing, cron, BullMQ queues\" }\n  ],\n  middleware: [\n    \"CORS \u2014 origins from an allowlist; requests with no Origin header are always allowed\",\n    \"trust proxy set to true (trusts all upstream proxies); x-powered-by disabled\",\n    \"Query parser \u2014 qs with depth 10, parameterLimit 1000, arrayLimit 1000\",\n    \"Helmet instantiated, but almost every protection is disabled: CSP, COEP, CORP, HSTS, frameguard, noSniff all false\",\n    \"Health route GET \/api\/health\",\n    \"Body parsers scoped to \/api \u2014 JSON + urlencoded, 20mb limit\",\n    \"Method guard \u2014 only GET \/ POST \/ PUT \/ DELETE allowed under \/api\",\n    \"Sanitization \u2014 xss() over body\/query\/params, but only for non-GET methods\",\n    \"Morgan request logging\",\n    \"API routes mounted at \/api, then a JSON error handler returning err.status \/ err.message\"\n  ],\n  dispatchFlow: [\n    { a: \"SPA\", b: \"POST \/api\/v1\/exec\/fam\/assets\/create\", d: \"Bearer JWT + x-rbac-key \/ x-rbac-menu headers\" },\n    { a: \"Middleware\", b: \"CORS, Helmet, 20mb JSON, method guard, xss sanitize\", d: \"on writes only\" },\n    { a: \"V1 router\", b: \"attachUser\", d: \"verifies JWT, LRU cache 10 min\" },\n    { a: \"checkAccess\", b: \"SUPER_ADMIN? PUBLIC_APIS? FEATURE_RBAC? hasPermission()\", d: \"authorization\" },\n    { a: \"RootCtrl dispatch\", b: \"action in getPostActions()?\", d: \"capability gate\" },\n    { a: \"JController.create()\", b: \"Mongoose write\", d: \"business logic\" },\n    { a: \"MongoDB\", b: \"JSON result \\u2192 client\", d: \"\" }\n  ],\n  auth: {\n    tokens: [\n      \"JWT, HS256 (symmetric). signToken uses ACCESS_TOKEN_PRIVATE_KEY (a shared secret despite the name); access token expiry 12h, refresh token 30d.\",\n      \"verifyToken calls jwt.verify with no algorithms allowlist.\",\n      \"Token payload carries uid, roles, roleIds, locationIds, departmentIds, name, email.\",\n      \"Passwords hashed with bcrypt, cost 10.\"\n    ],\n    attachUser: [\n      \"Reads x-rbac-key \/ x-rbac-menu headers into req.\",\n      \"Extracts the token from Authorization: Bearer or req.query.authtoken. If absent, continues unauthenticated.\",\n      \"Verified tokens cached in an LRU keyed by the raw token string, TTL 10 min \u2014 a revoked token stays valid in-process for up to 10 minutes.\"\n    ],\n    flows: [\n      [\"POST \/login\", \"RSA-decrypts password; lockout after 3 tries \/ 15 min; optional email 2FA; issues access + refresh tokens\"],\n      [\"POST \/register\", \"Public self-registration; accepts a roles array from the body; status:'off'\"],\n      [\"POST \/logout (auth)\", \"Deletes the supplied refresh token\"],\n      [\"POST \/reset (auth)\", \"Sets caller's password \u2014 no current-password proof required\"],\n      [\"POST \/forgot\", \"Emails a 6-digit OTP (Math.random); stores a reset record, 10-min expiry\"],\n      [\"POST \/forgot\/reset\", \"Sets password by email if a non-expired reset record exists \u2014 does not verify the OTP\"],\n      [\"POST \/profile (auth)\", \"Field-allowlisted profile update; can change password\"],\n      [\"POST \/refresh\", \"Verifies refresh token, rotates it, detects reuse\"],\n      [\"GET \/publickey\", \"Serves the RSA public key for client-side password encryption\"]\n    ],\n    rbacSteps: [\n      \"Require fireUser.uid, else 401\",\n      \"If roles include SUPER_ADMIN, allow unconditionally\",\n      \"If module\/ctrl\/action is in PUBLIC_APIS, allow\",\n      \"Only if FEATURE_RBAC === 'ON' call hasPermission(); otherwise fall through and allow\"\n    ],\n    rbacNote: `hasPermission loads Access docs for the user's roleIds, filtered by the client-supplied rbacKey\/rbacMenu, and checks whether any role's actions[] contains \"module\/ctrl\/action.\" It is action-level only: the row\/record-level filter is stubbed and the call site is commented out. Consequence: even with RBAC on, there is no ownership \/ department \/ location enforcement. FEATURE_RBAC is OFF in the committed env.`\n  },\n  config: [\n    \"Loaded by dotenv + dotenv-expand: .env first, then .env.<NODE_ENV> for non-development\",\n    \"Committed templates: env, env-local, env.production, env.qa \u2014 named without a leading dot; appear to contain real secrets\",\n    \"Feature flags: APPLICATION_{CORE,FAM,CMDB,SAM,TICKETING,IMPEXP,FLEET,PATCH}=ON, FEATURE_RBAC=OFF, APPLICATION_LOG_EXPIRY=30\",\n    \"Key groups: server host\/port, MONGO_URI, JWT keys, admin seed, storage (L3 or S3), SMTP, Redis, CORS, external URLs\",\n    \"Frontend config compiled into the bundle via Vite define + VITE_* vars \u2014 includes a hardcoded NMS base URL and API key\"\n  ],\n  jobs: [\n    \"Search worker (worker.js): connects Mongo + Redis, recursively loads all *.model.js, compiles dynamic models, starts the proprietary global-search-worker. Opens a change stream on Core_FormCollections and recompiles Mongoose models live \u2014 the runtime schema engine.\",\n    \"The API process also spawns the search worker as a worker_thread, in addition to the PM2-managed worker.js process \u2014 unverified whether both run in production.\",\n    \"Scheduling runs through a custom DB-polling scheduler (CronJobRunner) despite node-cron \/ BullMQ being dependencies: polls every 10s, acquires a distributed lock, computes next run with cron-parser, and dynamically require()s the job file. Handlers: WorkflowSLA, GlobalSearchReindex, UnusedFilesCleanup, InventorySnapshot, AuditSnapshot, AssetPossessionCheck, sam\/ComplianceSummary.\",\n    \"Caching: in-process LRU for verified JWTs; Redis for search \/ queues.\"\n  ],\n  dataModel: {\n    intro: `Reconstructed from the Mongoose model files and a live mongodump (141 collections). There is no tenantId: data isolation is meant to be by location + department, driven by the token's locationIds\/departmentIds and RBAC scope\/rowFilter. That row-level enforcement is currently not active.`,\n    families: [\n      { prefix: \"core_\", count: 29, domain: \"Users, roles\/access, org\/location\/group\/team, vendors, brands, categories, files, counters, settings, workflows, notifications\" },\n      { prefix: \"fam_\", count: 23, domain: \"Hardware assets, assignments, finance, depreciation, disposals, movements, inward\/QC, contracts, maintenance, parts, procurement\" },\n      { prefix: \"sam_\", count: 18, domain: \"Software inventory, entitlements, allocations, installations, compliance linkages, contracts, audits, procurement\" },\n      { prefix: \"cmdb_\", count: 14, domain: \"CI classes\/categories\/items, instance data, relationships, flagged data, alerts, support groups, audit logs\" },\n      { prefix: \"ticketing_\", count: 61, domain: \"Service catalog\/requests (RITM\/CTASK), KB, SLA, plus incident\/problem\/change\/CAB owned by a separate service\" }\n    ],\n    drift: `Several ticketing_ collections in the live dump (incidents, problems, changes, ritms, ctasks, cab*, workflowserviceinstances) have no Mongoose model in this repo \u2014 a separate ITSM service. The live DB also stores RBAC in core_roleaccessmappings, while this repo's Access model targets core_accesses. One of the two is stale.`,\n    entities: [\n      { name: \"User\", detail: \"email\/mobile\/empId unique; password (select:false); twoFactorEnabled; failedAttempts\/lockUntil; roleDepartment[]. Email-format validator is commented out.\" },\n      { name: \"Access \/ RBAC\", detail: \"role, key, menu, actions[] (\\\"module\/ctrl\/action\\\"), enabled, scope (own\/all), rowFilter \u2014 not enforced.\" },\n      { name: \"Hardware Asset\", detail: \"assetTag\/rfidTag\/serial\/capitalNumber unique-sparse; AMC\/insurance\/warranty blocks; refs to category, brand model, vendor(s), location(s), department, PO.\" },\n      { name: \"Asset Finance \/ Depreciation\", detail: \"book\/tax cost, accumulated depreciation, written-down value. Monetary fields stored as String \u2014 blocks numeric aggregation.\" },\n      { name: \"Software chain\", detail: \"Product \\u2192 Entitlement \\u2192 Allocation \\u2192 Installation \\u2192 Linkage. License keys stored in plaintext. allocatedTo is polymorphic (User | CI item).\" },\n      { name: \"CI Item \/ CI Item Data\", detail: \"CI type taxonomy plus per-serial instances; Cmdb_CiCoreItemData bridges to FAM and holds ownedBy\/ipAddress\/macAddress\/fqdn.\" },\n      { name: \"Service Request \/ Record \/ Task\", detail: \"The SR, its per-item RITM records, and fulfillment\/approval CTASKs, linked to workflow + SLA instances.\" }\n    ],\n    integrity: [\n      \"Audit fields consistent: timestamps + AddUserFields plugin (createdBy\/updatedBy), plus dedicated log collections\",\n      \"No soft-delete flag (isDeleted) \u2014 lifecycle via status enums + an archive module\",\n      \"Good TTLs: refresh tokens 30d, password resets 900s, 2FA codes 300s\",\n      \"Index drift (SAM): declared unique indexes absent in the live DB \u2014 duplicate license\/allocation records currently possible\",\n      \"Fragile design: fam_assetassignments has a unique index on the asset array \u2014 an asset can appear in only one assignment document ever\",\n      \"Sensitive data at rest: core_usertokens.token and password-reset token\/otp stored in plaintext; license keys plaintext\"\n    ]\n  },\n  apiSurface: [\n    [\"POST \/api\/v1\/users\/login, \/register, \/forgot, \/forgot\/reset, \/refresh\", \"public\", \"Auth flows\"],\n    [\"GET \/api\/v1\/users\/publickey\", \"public\", \"RSA public key\"],\n    [\"POST \/users\/logout, \/reset, \/profile; GET \/profile\", \"authenticated\", \"Session \/ profile \/ self-service\"],\n    [\"\/api\/v1\/files\/*\", \"mixed \\u2014 GET \/:id is public\", \"Upload \/ download \/ detect\"],\n    [\"\/api\/v1\/access\/*\", \"authenticated \/ admin\", \"RBAC + constraint metadata\"],\n    [\"\/api\/v1\/import\/*\", \"authenticated only \\u2014 no RBAC\", \"CSV \/ XLSX import\"],\n    [\"\/api\/v1\/externalIntegrations\/insert\", \"none\", \"Inbound webhook (creates roles)\"],\n    [\"\/api\/v1\/agents\/*\", \"agent token \\u2014 \/login public, mints 364d token\", \"CMDB discovery ingestion\"],\n    [\"\/api\/v1\/exec\/:module\/:ctrl\/:action\/:_id?\", \"attachUser + checkAccess (RBAC gated)\", \"All domain actions\"]\n  ],\n  libraries: `mongoose (ODM), bullmq \/ ioredis (queues, cache), jsonwebtoken \/ bcrypt \/ node-forge (auth, crypto), @aws-sdk\/* (S3), xlsx \/ csv-parser \/ papaparse (import\/export), pdfmake (PDF), nodemailer \/ ejs \/ juice (email), xss \/ validatorjs (input handling), winston \/ winston-mongodb \/ morgan (logging), helmet \/ cors (HTTP security), date-holidays \/ cron-parser \/ node-cron (scheduling \/ SLA), deep-diff (audit diffs).`\n};\n\n\/* -------------------------------------------------------------------------\n   04 \u2014 Analytics & Code Health\n   ------------------------------------------------------------------------- *\/\nDATA.analytics = {\n  reportViews: [\n    { area: \"Core (FAR, depreciation, approvals, org)\", count: 64 },\n    { area: \"CMDB (discovery, utilization, unmanaged, accuracy)\", count: 36 },\n    { area: \"SAM (compliance, utilization, renewals, procurement)\", count: 26 },\n    { area: \"FAM dashboard charts\", count: 3 }\n  ],\n  dashboards: [\n    { t: \"FAM dashboards\", d: \"Asset lifecycle KPI, location-wise counts, AMC KPI, decommissioned assets, month-on-month purchase, insured assets, life-expiry. Many sit on PUBLIC_APIS \u2014 bypass RBAC (still need a valid JWT).\" },\n    { t: \"SAM KPIs\", d: \"Compliance rate, license utilization, over\/under-consumption, renewal pipeline. The backend's SAM dashboard KPI controllers are not registered \u2014 dead code; live KPIs come from report views + compliance summaries.\" },\n    { t: \"CMDB reporting \/ KPI\", d: \"Discovery freshness, orphan-CI ratio, CI accuracy\/completeness, unauthorized CIs, agent health, asset utilization.\" },\n    { t: \"Core reports\", d: \"Fixed Asset Register (group-wise, book-wise), depreciation (location\/month-wise), time-taken-for-approval.\" }\n  ],\n  dataSources: [\n    [\"Asset counts \/ lifecycle \/ location\", \"Fam_AssetInventories, Fam_AssetAssignments, Fam_AssetMovements, Fam_AssetDisposals\"],\n    [\"Depreciation \/ FAR \/ financials\", \"Fam_Assetfinances, Fam_DepreciationLedgers, Core_Books, Fam_PurchaseOrders\"],\n    [\"License compliance \/ utilization\", \"Sam_ComplianceSummary, Sam_Entitlements, Sam_Installations, Sam_Linkages\"],\n    [\"Discovery \/ CMDB accuracy\", \"Cmdb_CiItemData, Cmdb_CiCoreItemData, Cmdb_FlaggedData, Cmdb_AuditLog\"],\n    [\"Service desk \/ SLA\", \"Ticketing_ServiceRequests, Ticketing_SLAInstances + external ITSM collections\"],\n    [\"Global search index\", \"Core_GlobalSearch \u2014 denormalized, worker-maintained\"]\n  ],\n  kpiNote: `The SAM compliance KPI is the analytically load-bearing one: actualUsage = max(linkage-consumed, agent-discovered), compared to purchased. Two near-identical implementations exist (an on-demand controller path and a job path) \u2014 a drift risk if one is updated and not the other. Financial values are stored as strings, forcing string-to-number coercion in aggregations and blocking numeric range indexing.`,\n  healthCards: [\n    { k: \"Automated tests\", v: \"0\", d: \"Zero test or spec files outside node_modules in either app. CI runs lint + a config syntax check only.\", flag: \"critical\", icon: \"test-tube\" },\n    { k: \"TODO \/ FIXME markers\", v: \"38\", d: \"Includes the security-critical \\u201cTODO: Add fine-grained RBAC here\\u201d directly in the auth middleware.\", flag: \"warn\", icon: \"list-todo\" },\n    { k: \"console.log statements\", v: \"343\", d: \"In backend src\/, alongside the winston logger. Several dump fireUser \/ request bodies (PII leak, SEC-30). Production log level is \\u2018error,\\u2019 which mutes winston but not raw console.log.\", flag: \"warn\", icon: \"terminal\" },\n    { k: \"copy \/ bkp duplicate files\", v: \"28\", d: \"~25 in the backend, 3 in the frontend. Pollute the tree and make it unclear which file is authoritative.\", flag: \"warn\", icon: \"copy\" },\n    { k: \"Empty \/ dead modules\", v: \"6+\", d: \"The entire Denormalization plugin tree, Workflow.SLA.Service.js, all Constraints\/index.js files, textLimitRules \u2014 all empty.\", flag: \"neutral\", icon: \"package-x\" },\n    { k: \"Dormant subsystems\", v: \"3\", d: \"workflow2 (model-name collision), sam\/dashboard KPI controllers (unregistered), fleet module (stub).\", flag: \"neutral\", icon: \"moon\" }\n  ],\n  scale: { backendFiles: 890, backendLOC: 135000, frontendFiles: 1216, frontendLOC: 331000, totalLOC: 466000 },\n  hotspots: [\n    { file: \"fam\/dashboard\/Dashboard.Controller.js\", lines: 1380, note: \"Dense aggregation logic \u2014 good candidate for decomposition + tests\" },\n    { file: \"sam\/dashboard\/SamKpi.Controller.js\", lines: 1337, note: \"Dead code (not registered), but still 1,337 lines to maintain mentally\" },\n    { file: \"sam\/contract\/Contract.Controller.js\", lines: 1000, note: \"Drives the contract-to-entitlement chain \u2014 central to compliance correctness\" },\n    { file: \"SchemaBuilder.Service.js + dispatcher + Base.Controller\", lines: null, note: \"Small files, but high blast radius \u2014 a change here affects every module\" }\n  ],\n  driftNotes: [\n    \"SAM unique indexes declared in schemas are absent in the live DB (duplicate license\/allocation records possible)\",\n    \"RBAC lives in core_roleaccessmappings in the live DB but the repo model targets core_accesses \u2014 one is stale\",\n    \"Several ITSM collections in the live DB have no models here (separate service)\"\n  ],\n  positives: [\n    \"Consistent module structure and path aliasing make navigation predictable\",\n    \"Audit fields (createdBy\/updatedBy\/timestamps) applied uniformly via a schema plugin\",\n    \"Sensible TTL indexes on tokens \/ OTPs \/ logs\",\n    \"The global-search worker uses a proper Redis leader lock and resume tokens\",\n    \"Refresh-token rotation with reuse detection is implemented correctly\"\n  ],\n  nextSteps: [\n    \"Introduce a test harness (Jest\/Vitest + supertest) starting with the dispatcher, auth, RBAC, and the SAM compliance calculation\",\n    \"Delete copy\/bkp\/empty files or move experiments out of src\/ to shrink the attack surface and confusion\",\n    \"Consolidate the two compliance implementations into one shared function\",\n    \"Convert financial fields to Decimal128\/Number and add the missing SAM\/finance indexes\",\n    \"Replace console.log with structured, redacted logging and set up log-based KPIs\/alerting\"\n  ]\n};\nDATA.security = {\nfindings: [\n{\n  id: `SEC-01`, title: `RBAC is disabled by default; an authenticated user is a near-superuser`, severity: `Critical`,\n  tags: `OWASP A01:2021, API1\/API5:2023 \u00b7 CWE-862, CWE-285`,\n  location: `Auth middleware (the FEATURE_RBAC check and the checkAccess function); the committed environment file, where FEATURE_RBAC is set to OFF; the access service.`,\n  body: [`checkAccess only calls hasPermission, the function that actually evaluates a user's permissions, when FEATURE_RBAC is set to the string \"ON.\" The default is \"OFF,\" and the committed environment file also sets it to \"OFF.\" With the flag off, the only gate left is \"does this request carry a valid JWT,\" plus the per-controller action allowlist, which restricts which actions exist, not who is allowed to call them. On top of that, SUPER_ADMIN bypasses every check unconditionally, and the authorization middleware itself still carries a comment reading \"Add fine-grained RBAC here.\"`],\n  exploit: `A low-privilege employee logs in and obtains a valid JWT like anyone else. With that token alone, they can update another user's account, including an administrator's, delete a hardware asset record that does not belong to their department, or read the full list of software entitlements, license keys included. All of these succeed today because RBAC is off.`,\n  fix: `Change the default to ON rather than OFF, and fail closed: if the flag is not ON in a production environment, reject the request rather than silently allowing it. Do not trust the client-supplied rbac key and menu headers to widen a permission check; resolve the required permission server-side from the module, controller and action alone.`,\n  code: [\n    \"\/\/ src\/Middlewares\/Auth.Middleware.js\",\n    \"const { FEATURE_RBAC = `ON` } = process.env; \/\/ default ON, not OFF\",\n    \"\",\n    \"exports.checkAccess = async (req, res, next) => {\",\n    \"  const { fireUser, rbacKey, rbacMenu, params } = req;\",\n    \"  const { module, ctrl, action, _id } = params;\",\n    \"  if (!fireUser?.uid) return sendError(res, { code: 401, message: `Unauthorized` });\",\n    \"  if (fireUser.roles.includes(`SUPER_ADMIN`)) return next();\",\n    \"  if (PUBLIC_APIS.includes(`${module}\/${ctrl}\/${action}`)) return next();\",\n    \"  if (FEATURE_RBAC !== `ON` && process.env.NODE_ENV === `production`) {\",\n    \"    logger.error(`RBAC is disabled in production - refusing request`);\",\n    \"    return sendError(res, { code: 403, message: `Access control unavailable` });\",\n    \"  }\",\n    \"  const allowed = await hasPermission(fireUser, rbacKey, rbacMenu, module, ctrl, action, req.method.toLowerCase(), _id);\",\n    \"  if (!allowed) return sendError(res, { code: 403, message: `Access Denied` });\",\n    \"  return next();\",\n    \"};\",\n  ],\n},\n{\n  id: `SEC-02`, title: `Password reset never verifies the one-time code, allowing full account takeover`, severity: `Critical`,\n  tags: `OWASP A07:2021, API2:2023 \u00b7 CWE-640, CWE-294, CWE-620`,\n  location: `The forgot\/reset and forgot endpoints on the user routes.`,\n  body: [`The reset endpoint validates only an email and a new password, and proceeds as long as any non-expired password-reset record exists for that email. It never checks the emailed one-time code, verification code, or token against what the caller submitted. The forgot endpoint that creates that reset record is itself unauthenticated and will create one for any valid email address on request.`],\n  exploit: `An attacker who simply knows a victim's email address, for example an administrator's work email, calls the forgot endpoint to create a ten-minute reset record, then immediately calls forgot\/reset with that same email and a password of their own choosing. The administrator's password is set. No access to the victim's mailbox is required at any point, and the result is a full account takeover.`,\n  fix: `Require the one-time code as part of the reset request, look up the most recent reset record for the user, reject if it is missing, expired, or if the submitted code does not match, and delete the reset record once it has been used so it cannot be replayed. Store the code hashed rather than in plain text, and rate-limit reset attempts.`,\n  code: [\n    \"\/\/ src\/Routes\/V1\/User.Route.js  (\/forgot\/reset)\",\n    \"const errors = validate(req.body, {\",\n    \"  email: `email|required`, otp: `string|required`, password: `string|required|min:8`\",\n    \"});\",\n    \"const resetRequest = await UserPasswordResetModel.findOne({ userId }).sort({ createdAt: -1 });\",\n    \"if (!resetRequest || resetRequest.expiresAt < Date.now()) return throwError(`Invalid or expired reset token.`, 400);\",\n    \"if (String(resetRequest.otp) !== String(req.body.otp)) {   \/\/ required check, currently missing\",\n    \"  return throwError(`Invalid OTP`, 400);\",\n    \"}\",\n    \"\/\/ consume single-use, then update password ...\",\n    \"await UserPasswordResetModel.deleteMany({ userId });\",\n  ],\n},\n{\n  id: `SEC-03`, title: `Live secrets are committed to the repository`, severity: `Critical`,\n  tags: `OWASP A05\/A02:2021, API8:2023 \u00b7 CWE-798, CWE-312, CWE-522`,\n  location: `The env, env-local, env.production and env.qa files. The gitignore file's .env* exclusion lines are commented out, and the committed files are named without a leading dot, so they are tracked by git rather than ignored.`,\n  body: [`What appear to be real, working secrets are committed directly into the repository: an AWS access key and secret, all three JWT signing keys used for access tokens, refresh tokens and agent tokens, the MongoDB connection string, the admin seed password, the internal L3 storage token, SMTP passwords, and an object-detection service API key.`],\n  exploit: `Anyone with read access to the repository, or to a leaked archive of it, and it is worth noting that zip snapshots of the same codebase exist alongside it, can forge valid JWTs using the signing keys and instantly impersonate any user including an administrator, read from and write to the S3 bucket, connect directly to the production MongoDB instance, and send email as the organization.`,\n  fix: `Treat every one of these committed secrets as already compromised and rotate all of them without delay: the AWS keys, all three JWT signing keys, the database password, SMTP credentials, the L3 token, the object-detection key, and the admin seed password. Remove the secrets from the repository and from its git history using a tool such as git filter-repo, load configuration from a proper secrets manager or CI-injected environment variables instead, restore the .gitignore entries for .env files, and add automated secret scanning to the CI pipeline.`,\n},\n{\n  id: `SEC-04`, title: `Systemic insecure direct object reference: unscoped CRUD by id across roughly 90 controllers`, severity: `Critical`,\n  tags: `OWASP A01:2021, API1:2023 \u00b7 CWE-639, CWE-566`,\n  location: `The base controller that every module controller inherits from, and the access service, where the row-filter mechanism is stubbed out and its call site is commented out.`,\n  body: [`The base controller's findOne, update and remove methods all operate on a document id alone, with no owner, location or department filter applied. Its find, count and options methods pass the client-supplied query straight through, also unscoped. The authenticated user is used only to stamp createdBy and updatedBy on writes, never to constrain what can be read or changed. The system does have a scope and row-filter mechanism defined in the data model for exactly this purpose, but the handlers that would implement it are empty stubs, and the place in the code that would call them is commented out. This means that even with RBAC switched on, there is still no ownership enforcement at the record level.`],\n  exploit: `An employee in one department enumerates or harvests object ids, for instance from a list API response, and then calls a read or update action directly against another department's records: someone else's hardware asset, or someone else's software contract. All of these calls succeed today, regardless of department boundaries.`,\n  fix: `Implement the row filter and wire it back in. Build a scope predicate from the authenticated user's location and department ids, which are already present in the JWT, and inject that predicate into every read and write in the base controller so all roughly 90 module controllers inherit it automatically. Add per-model overrides for the handful of models that do not carry a location or department field.`,\n  code: [\n    \"\/\/ src\/Services\/Access.Service.js - implement the stub and re-enable at the call site\",\n    \"RowFilterHandlers.checkOrgHierarchy = async (params, { fireUser }) => ({\",\n    \"  $or: [\",\n    \"    { location: { $in: fireUser.locationIds || [] } },\",\n    \"    { department: { $in: fireUser.departmentIds || [] } },\",\n    \"    { createdBy: fireUser.uid },\",\n    \"  ],\",\n    \"});\",\n    \"\/\/ In Base.Controller.find\/findOne\/update\/remove, merge the scope filter:\",\n    \"const scope = await buildScopeFilter(fireUser, ModelName);   \/\/ {} for scope:`all`\",\n    \"query = { $and: [ query, scope ] };\",\n  ],\n},\n{\n  id: `SEC-05`, title: `NoSQL operator injection and mass assignment through the shared CRUD helper`, severity: `High`,\n  tags: `OWASP A03:2021, API3:2023 \u00b7 CWE-943, CWE-915`,\n  location: `The CRUD helper's find and findOneAndUpdate calls, the dispatcher's query handling, and the sanitize middleware, which only cleans string values on writes and never strips operator characters from keys.`,\n  body: [`On reads, the client's query string is passed directly into params and then straight into Mongoose's find call; a call to normalize object ids that would have caught some of this is commented out. The XSS sanitizer that does run only cleans string values on write requests, and never strips dollar-sign or dot characters from object keys on either reads or writes. On writes, update passes the entire request body through as the update document.`],\n  exploit: `A crafted query string containing a MongoDB operator such as $where or $ne can return records a filter was intended to exclude, and a similarly crafted $regex-based query can be used to blind-exfiltrate hashed values from any collection that exposes them. On the write side, a request body containing an operator key such as $rename, or simply extra fields the caller should not be able to set, such as status, price or owner, can mutate fields the caller was never meant to touch.`,\n  fix: `Add key-level sanitization that applies to every HTTP method, not just writes, stripping operator and dot characters from the body, query and params. Never pass a raw request body as a Mongoose update document; instead build an explicit, schema-aware allowlist of settable fields. Re-enable the object-id normalization step, and validate that query values are scalars or object ids rather than nested objects carrying operator keys, unless a field is explicitly whitelisted to accept one.`,\n  code: [\n    \"\/\/ app.js - apply to ALL methods, strip operator\/dot keys from body, query, params\",\n    \"const mongoSanitize = require(`express-mongo-sanitize`);\",\n    \"app.use(`\/api`, mongoSanitize({ replaceWith: `_` }));\",\n    \"\/\/ Crud.Helper.js - never pass a raw body as an update doc\",\n    \"async function update(JModel, query, item) {\",\n    \"  const $set = pickAllowedFields(JModel, item);        \/\/ schema-path allowlist, drop $-keys\",\n    \"  return JModel.findOneAndUpdate(query, { $set }, { new: true, runValidators: true });\",\n    \"}\",\n  ],\n},\n{\n  id: `SEC-06`, title: `Unauthenticated discovery-agent enrollment mints 364-day tokens`, severity: `High`,\n  tags: `OWASP A07\/A01:2021, API2:2023 \u00b7 CWE-306, CWE-287, CWE-613`,\n  location: `The agent login route, its enrollment helper, and its mount point ahead of the rest of the authentication chain.`,\n  body: [`The agent login endpoint accepts a serial number, a name and an agent identifier, with no shared secret, no device pre-registration, and no serial-number validation of any kind, and in exchange returns a JWT signed with the dedicated agent signing key that stays valid for 364 days. That token is accepted either as a header or as a query-string parameter.`],\n  exploit: `Anyone who can simply reach the agent login endpoint receives a long-lived, valid agent token. With it, they can post forged configuration-item data to overwrite existing CI records by serial number, effectively hiding a rogue device or corrupting inventory data, and can read the platform's alert rules.`,\n  fix: `Require device pre-registration and a per-device enrollment secret before a token is ever issued. Issue short-lived, rotating tokens rather than 364-day ones, bind each token to its registered serial number, and verify incoming tokens with a pinned signing algorithm and a specific issuer.`,\n  code: [\n    \"\/\/ Require an enrollment secret and a pre-provisioned device record\",\n    \"router.post(`\/login`, async (req, res) => {\",\n    \"  const { serial, enrollmentSecret } = req.body;\",\n    \"  const device = await AgentDeviceModel.findOne({ serial, status: `approved` });\",\n    \"  if (!device || !(await bcrypt.compare(enrollmentSecret, device.secretHash)))\",\n    \"    return sendError(res, { code: 401, message: `Enrollment denied` });\",\n    \"  const token = await signToken({ serial, deviceId: device._id }, AGENT_TOKEN_PRIVATE_KEY, `24h`);\",\n    \"  return sendResponse(res, { token }, 200);\",\n    \"});\",\n  ],\n},\n{\n  id: `SEC-07`, title: `Unauthenticated file download at GET \/files\/:id`, severity: `High`,\n  tags: `OWASP A01:2021, API1:2023 \u00b7 CWE-306, CWE-639`,\n  location: `The file route's GET-by-id handler, which, unlike its sibling delete and options handlers, does not require authentication.`,\n  body: [`Any caller, authenticated or not, can request a file by its object id and have it streamed straight back to them, with no authentication check and no ownership check of any kind. Uploaded files on this platform include invoices, contracts, license certificates, audit reports and profile images.`],\n  exploit: `An unauthenticated attacker who either enumerates object ids, which are partially predictable since they encode a timestamp and a counter, or who simply harvests file ids surfaced in other API responses, can download sensitive documents without ever logging in.`,\n  fix: `Require authentication on this route, and add an ownership or scope check tying the requested file back to the requester's organization or the specific entity it belongs to.`,\n  code: [\n    \"router.get(`\/:id`, isAuthenticated, async (req, res) => {\",\n    \"  const file = await FileService.findOne(req.params.id);\",\n    \"  if (!file) return throwError(`File not found`, 404);\",\n    \"  if (!(await userCanAccessFile(req.fireUser, file))) return throwError(`Forbidden`, 403);\",\n    \"  res.setHeader(`Content-Type`, file.mime);\",\n    \"  (await getFileStream(file.path)).pipe(res);\",\n    \"});\",\n  ],\n},\n{\n  id: `SEC-08`, title: `Path traversal and arbitrary file read through the export download action`, severity: `High`,\n  tags: `OWASP A01\/A05:2021, API1:2023 \u00b7 CWE-22, CWE-23`,\n  location: `The export controller's download action, which is on the GET action allowlist.`,\n  body: [`The download action reads a file path straight out of the request parameters, checks only that the path exists on disk, and then sends that file back to the caller, with no confinement to any particular base directory at all.`],\n  exploit: `An authenticated user can request an arbitrary server-side path, for instance the system password file or the application's own environment file, and read it directly, which includes the committed secrets described in SEC-03.`,\n  fix: `Confine the download action to a specific exports directory, resolve the requested path against that directory, and reject anything that would resolve outside it. Longer term, avoid taking a client-supplied path at all; hand back an opaque export id instead, and resolve that id to a server-side path internally.`,\n  code: [\n    \"const path = require(`path`);\",\n    \"const EXPORT_DIR = path.resolve(process.cwd(), `exports`);\",\n    \"const resolved = path.resolve(EXPORT_DIR, path.basename(String(filePath)));\",\n    \"if (!resolved.startsWith(EXPORT_DIR + path.sep) || !fs.existsSync(resolved))\",\n    \"  return res.status(404).json({ error: `File not found` });\",\n    \"return res.sendFile(resolved);\",\n  ],\n},\n{\n  id: `SEC-09`, title: `Dynamic require of a client-supplied file name in the reporting controllers (local file inclusion)`, severity: `High`,\n  tags: `OWASP A03\/A08:2021, API8:2023 \u00b7 CWE-98, CWE-94, CWE-22`,\n  location: `The core, CMDB and SAM reporting controllers, where a report name from the client is built directly into a require path. The core report-runner action is also on the public API list, meaning it needs no specific permission at all.`,\n  body: [`The report path is built by joining a fixed directory with a client-supplied file name and requiring the result. Node's path handling collapses ..\/ sequences in that join, so an authenticated caller can load, and by loading, execute, any JavaScript module that exists on disk and happens to export a findEntries function.`],\n  exploit: `An authenticated user requests a report using a crafted file name that walks up and out of the intended reports directory. On its own this is arbitrary local module execution and information disclosure; combined with any file-write primitive elsewhere in the system, for example an uploaded JavaScript file landing somewhere predictable, it could escalate to full remote code execution. Whether a writable JavaScript path is actually reachable in this deployment was not confirmed and is listed as an open question.`,\n  fix: `Replace the dynamic require with a fixed, explicit whitelist of known report modules, so a client-supplied string can never influence which file gets loaded.`,\n  code: [\n    \"const REPORTS = require(`.\/views`);            \/\/ { assetLifeCycle: require(`.\/views\/assetLifeCycle`), ... }\",\n    \"const report = REPORTS[fileName];\",\n    \"if (!report || typeof report.findEntries !== `function`) throw new Error(`Unknown report`);\",\n    \"const reportData = await report.findEntries(params, fireUser);\",\n  ],\n},\n{\n  id: `SEC-10`, title: `Form builder allows runtime model hijack and arbitrary collection reads`, severity: `High`,\n  tags: `OWASP A01\/A08:2021, API3:2023 \u00b7 CWE-913, CWE-915, CWE-94`,\n  location: `The form builder controller's create and getModels actions, the schema-builder service, and the storage service, which protects only two system models by name. The formbuilder find and findOne actions also sit on the public API list.`,\n  body: [`A user, either because RBAC is off or because they hold ordinary form-builder access, can submit a form definition naming an arbitrary model, collection and module. When that form is activated, the schema builder deletes and re-registers a Mongoose model bound to whatever collection name was supplied; only two system collections are protected against this. Separately, the getModels action, called with no name, lists every collection in the database, and called with a name, returns a sample document from that collection.`],\n  exploit: `An attacker activates a form whose target collection is, for example, the users collection, paired with a permissive schema of their own choosing, or simply calls getModels repeatedly to enumerate every collection in the database and pull a sample document from each one.`,\n  fix: `Restrict form-builder management to administrators, following the same fix as SEC-01. Validate any submitted collection name against a denylist of reserved, system-level collections. Forbid re-binding a model name that already exists. And remove the arbitrary-collection sampling behavior from getModels entirely.`,\n},\n{\n  id: `SEC-11`, title: `No rate limiting anywhere, and one-time codes are generated with Math.random`, severity: `High`,\n  tags: `OWASP A07:2021, API4:2023 \u00b7 CWE-307, CWE-338, CWE-330, CWE-770`,\n  location: `No rate-limiting middleware of any kind exists in the backend. The two-factor code and the password-reset code are both generated the same way, using Math.random scaled into a six-digit range.`,\n  body: [`Login does have a per-account lockout, three attempts within 15 minutes, but there is no IP-based or global throttle anywhere in the system. The forgot-password endpoint, one-time code verification, and effectively every other endpoint have no throttling at all. On top of that, the codes themselves are generated with a non-cryptographic pseudo-random number generator.`],\n  exploit: `An attacker can brute-force a six-digit one-time code with unlimited attempts, since nothing caps how many guesses are allowed, or attempt to predict it from the underlying Math.random state. Separately, an attacker can flood the forgot-password endpoint to mailbomb or effectively lock out real users, or spread login attempts thinly across many different accounts to stay under the per-account lockout threshold.`,\n  fix: `Add rate limiting globally, with a stricter limit specifically on authentication-related endpoints, and switch one-time code generation to a cryptographically secure random source.`,\n  code: [\n    \"const rateLimit = require(`express-rate-limit`);\",\n    \"app.use(`\/api`, rateLimit({ windowMs: 60_000, max: 300 }));\",\n    \"const authLimiter = rateLimit({ windowMs: 15*60_000, max: 10, keyGenerator: r => r.ip + `:` + (r.body?.email||``) });\",\n    \"router.post(`\/login`, authLimiter, ...); router.post(`\/forgot`, authLimiter, ...);\",\n    \"\/\/ OTP\",\n    \"const otp = require(`crypto`).randomInt(100000, 1000000);\",\n  ],\n},\n{\n  id: `SEC-12`, title: `Security misconfiguration: Helmet disabled, trust proxy misconfigured, verbose errors`, severity: `High`,\n  tags: `OWASP A05:2021, API8:2023 \u00b7 CWE-16, CWE-693, CWE-319, CWE-209`,\n  location: `The main application file, where Helmet is instantiated but nearly every protection it offers is switched off, trust proxy is set to trust all upstream hops, and the global error handler returns the raw error message to the client.`,\n  body: [`Content security policy, cross-origin embedder and resource policies, HSTS, frame guarding and MIME-sniff protection are all turned off. Trusting all proxies for the client IP defeats any IP-based rate limiting or logging, since the value can be spoofed through a forwarded-for header. The error handler leaking internal messages back to the client is a smaller issue on its own, but it compounds the others by giving an attacker more information to work with.`],\n  exploit: `With no content security policy in place, the stored cross-site scripting issue described in SEC-15 becomes considerably more dangerous than it would be with a policy present. With trust proxy trusting every hop, rate limiting and audit logging based on client IP can both be trivially bypassed by an attacker who simply sets their own forwarded-for header.`,\n  fix: `Enable a real Helmet baseline including a content security policy, scope trust proxy to the actual number of proxy hops in the real deployment rather than trusting all of them, and return only generic error messages to clients in production.`,\n  code: [\n    \"app.use(helmet({\",\n    \"  contentSecurityPolicy: { directives: { defaultSrc: [\\\"`self`\\\"], scriptSrc: [\\\"`self`\\\"], objectSrc: [\\\"`none`\\\"], frameAncestors: [\\\"`none`\\\"] } },\",\n    \"  hsts: { maxAge: 31536000, includeSubDomains: true },\",\n    \"  frameguard: { action: `deny` }, noSniff: true, crossOriginResourcePolicy: { policy: `same-site` },\",\n    \"}));\",\n    \"app.set(`trust proxy`, 1);\",\n  ],\n},\n{\n  id: `SEC-13`, title: `Vulnerable dependency: xlsx 0.18.5`, severity: `High`,\n  tags: `OWASP A06:2021, API8:2023 \u00b7 CWE-1321 (CVE-2023-30533), CWE-400 (CVE-2024-22363)`,\n  location: `Both applications' package.json files, which pin xlsx at 0.18.5.`,\n  body: [`This is the last version of SheetJS published to npm, and it carries a known prototype-pollution vulnerability and a known regular-expression denial-of-service vulnerability. Fixes for both ship only from SheetJS's own CDN rather than through npm, so a plain npm install cannot resolve a patched version. This library is used to parse user-uploaded spreadsheets during import, meaning attacker-controlled input reaches the vulnerable parser directly.`],\n  exploit: `A crafted XLSX file, uploaded through the ordinary import flow, can trigger prototype pollution or cause the parser to hang under a ReDoS pattern.`,\n  fix: `Migrate to the SheetJS CDN build, pinned to a specific patched version in package.json, or replace the dependency with exceljs. Run parsing inside a worker with a timeout, and validate file size and shape before attempting to parse. Add npm audit or a tool such as Snyk to CI as part of the SEC-03 remediation.`,\n},\n{\n  id: `SEC-14`, title: `Software license keys are stored and returned in plain text`, severity: `High`,\n  tags: `OWASP A02:2021, API3:2023 \u00b7 CWE-312, CWE-311, CWE-200`,\n  location: `The entitlement model and the software inventory model, neither of which excludes the license key field from normal reads or applies any encryption to it. The base controller's find and findOne methods return it with no projection.`,\n  body: [`License keys are stored as ordinary plaintext strings and are returned in full by any list or read call against these models. With RBAC off, as described in SEC-01, that means every authenticated user in the system, regardless of role, can read them.`],\n  exploit: `A simple read of the entitlements list returns every purchased license key to any authenticated user. These keys represent real purchased intellectual property and can be copied out and reused or resold.`,\n  fix: `Encrypt license keys at rest, for example with envelope encryption through a key management service or AES-256-GCM, exclude the field from normal reads, and expose a dedicated, permission-gated and audit-logged \"reveal license key\" action instead. Mask the key by default in any list view.`,\n},\n{\n  id: `SEC-15`, title: `Stored cross-site scripting via rich-text rendering, paired with a weak backend sanitizer`, severity: `Medium`,\n  tags: `OWASP A03:2021 \u00b7 CWE-79`,\n  location: `Several frontend rich-text and attachment rendering components that inject HTML directly into the page, and the backend sanitizer, which runs only on writes and permits attributes and tags that can still carry a script vector.`,\n  body: [`Rich-text content, meaning ticket bodies, knowledge-base articles and similar fields, is rendered by inserting HTML directly rather than through safe text rendering. The backend's sanitizer runs only when content is written, not when it is displayed, and its allowlist still permits things like anchor tags with an href attribute, which can carry a javascript: URL. With no content security policy in place either, as covered in SEC-12, there is no second layer of defense if a malicious payload does get through.`],\n  exploit: `A user stores a malicious knowledge-base article or ticket comment. When a different user, potentially an administrator, later views it, the embedded script executes in their browser session and can steal their access token out of localStorage, which compounds directly with SEC-16.`,\n  fix: `Sanitize on render using a library such as DOMPurify on the client, in addition to sanitizing on write. Tighten the backend sanitizer's allowlist to drop javascript: and data: URIs from anchor href attributes, and add a strict content security policy as described in SEC-12.`,\n},\n{\n  id: `SEC-16`, title: `JWT stored in localStorage, and tokens passed in URL query strings`, severity: `Medium`,\n  tags: `OWASP A02\/A07:2021, API2:2023 \u00b7 CWE-522, CWE-598, CWE-1004`,\n  location: `The frontend's token storage helper, which always uses localStorage, several export and download flows that place the access token directly into the download URL, and the backend, which accepts a token passed as an authtoken query parameter.`,\n  body: [`Access and refresh tokens are both kept in localStorage, which means any successful cross-site scripting attack, such as the one described in SEC-15, can read them directly. On top of that, access tokens are placed into export download URLs, which means they can leak through browser history, through proxy or server access logs, and through the Referer header sent to any third party the page links to.`],\n  exploit: `A cross-site scripting payload reads the token straight out of localStorage and exfiltrates it, or a token that was embedded in a download URL shows up later in a shared browser history, a corporate proxy log, or a Referer header.`,\n  fix: `Move tokens out of localStorage and into memory, backed by HttpOnly, Secure, SameSite=Strict cookies with CSRF protection, or, at a minimum, stop placing tokens in URLs at all and rely on the Authorization header, or on short-lived, signed download links for cases like exports. Remove support for the authtoken query parameter on the server side.`,\n},\n{\n  id: `SEC-17`, title: `Unauthenticated webhook can create roles`, severity: `Medium`,\n  tags: `OWASP A01:2021, API5:2023 \u00b7 CWE-306, CWE-862, CWE-915`,\n  location: `The external-integration insert route, which carries no authentication middleware at all.`,\n  body: [`For an event type of \"role.created,\" this endpoint creates a new user-role document directly from the request body, with no authentication and full mass assignment of whatever fields are supplied.`],\n  exploit: `An unauthenticated caller can create arbitrary role documents, polluting reference data and potentially causing integrity problems anywhere those role names are looked up or trusted elsewhere in the system.`,\n  fix: `Require authentication plus a verified webhook signature, using an HMAC shared secret, validate the event type against an explicit allowlist, and map only specific, expected fields into the create call rather than passing the body through directly.`,\n},\n{\n  id: `SEC-18`, title: `Import routes bypass RBAC entirely`, severity: `Medium`,\n  tags: `OWASP A01:2021, API5:2023 \u00b7 CWE-862`,\n  location: `The import routes, which require only that a caller be authenticated and do not run the same access-check step the dispatcher routes use.`,\n  body: [`Because checkAccess never runs on these routes, any authenticated user can bulk import or upsert records into any mapped table, entirely bypassing the module and action authorization that would otherwise apply through the normal dispatcher.`],\n  exploit: `A user with no particular permission at all can still perform a bulk import against a sensitive table, since the import path simply never checks what they are allowed to do.`,\n  fix: `Route imports through the same checkAccess authorization used elsewhere, or introduce a dedicated permission per importable table, and validate the requested target table against the caller's actual permissions before running the import.`,\n},\n{\n  id: `SEC-19`, title: `Plaintext refresh tokens and reset codes at rest`, severity: `Medium`,\n  tags: `OWASP A02:2021, API2:2023 \u00b7 CWE-312, CWE-256`,\n  location: `The user token model and the password-reset model, both of which store their token and code values as plain text.`,\n  body: [`Refresh tokens and password-reset tokens and codes are all stored unencrypted. A database read, whether through legitimate access or through the injection issue described in SEC-05, yields directly usable session or reset material.`],\n  exploit: `Anyone who can read these two collections, by whatever means, obtains working session tokens or reset codes they can use immediately.`,\n  fix: `Store only a hash, for example SHA-256, of both refresh tokens and reset tokens, and compare hashes at verification time rather than storing the raw value. Keep the existing short expiry windows, which are already reasonable.`,\n},\n{\n  id: `SEC-20`, title: `Two-factor code has no expiry check and no attempt limit, and is stored in plain text`, severity: `Medium`,\n  tags: `OWASP A07:2021, API2:2023 \u00b7 CWE-287, CWE-307, CWE-613`,\n  location: `The two-factor verification step on the user routes, where an expiry timestamp is set but never actually checked at verification time, and there is no attempt counter at all.`,\n  body: [`The verification step compares the submitted code against the stored one but never checks whether it has expired, and nothing tracks or limits how many times a caller can attempt it. Combined with the weak code generation described in SEC-11, a six-digit code is both brute-forceable and effectively non-expiring in practice.`],\n  exploit: `An attacker with unlimited attempts and no expiry to race against can brute-force a two-factor code at their leisure.`,\n  fix: `Set a real, short expiry, for example five minutes, and actually check it at verification time. Cap the number of attempts and lock out after that cap is reached. Store the code hashed rather than in plain text, and invalidate it immediately on successful use.`,\n},\n{\n  id: `SEC-21`, title: `SMTP certificate validation is disabled`, severity: `Medium`,\n  tags: `OWASP A02\/A05:2021 \u00b7 CWE-295`,\n  location: `The email service's TLS configuration, which explicitly disables certificate validation.`,\n  body: [`The mail transport is configured to accept invalid or self-signed TLS certificates from whatever server it connects to, which opens the door to a man-in-the-middle interception of outbound mail, mail that includes one-time codes and password-reset links.`],\n  exploit: `An attacker positioned on the network path between the application and its mail relay can intercept, and potentially read or alter, outbound email, including sensitive authentication material.`,\n  fix: `Remove the override and let certificate validation default back to true. If an internal mail relay genuinely uses a private certificate authority, add that authority explicitly rather than disabling validation altogether.`,\n},\n{\n  id: `SEC-22`, title: `Open self-registration accepts a client-controlled roles field`, severity: `Medium`,\n  tags: `OWASP A07\/A01:2021, API5:2023 \u00b7 CWE-862, CWE-915`,\n  location: `The public registration endpoint, which accepts a roles array directly from the request body and creates the new account with status set to off.`,\n  body: [`Registration requires no authentication and takes a roles array straight from whoever is calling it. New accounts start out inactive, which does reduce the immediate risk, but self-registration combined with client-controlled role injection is still a meaningful risk on an internal enterprise tool if the activation process does not re-validate or discard those roles.`],\n  exploit: `A self-registered account could carry an elevated role from the moment it is created, and whether that role sticks depends entirely on what the (separate) activation process does with it, which is itself an open question for the owning team.`,\n  fix: `Disable open self-registration in favor of administrator-provisioned accounts, or require an invite token. Regardless of which path is chosen, never accept a roles field from the client; assign roles server-side, and only at activation time.`,\n},\n{\n  id: `SEC-23`, title: `JWT verification has no algorithm allowlist, and verified tokens are cached for 10 minutes`, severity: `Medium`,\n  tags: `OWASP A02\/A07:2021, API2:2023 \u00b7 CWE-347, CWE-613`,\n  location: `The token helper's verify call, which does not pin which algorithms it will accept, and the in-process LRU cache in the auth middleware.`,\n  body: [`Not pinning the accepted algorithm is a defense-in-depth gap against algorithm-confusion attacks. Separately, once a token has been verified, that verification is cached in memory for ten minutes keyed by the raw token string, which means a logout, a role change, or an account lockout will not actually take effect from the application's point of view until that cache entry expires.`],\n  exploit: `These two issues do not on their own hand an attacker a working exploit, but they widen the blast radius of other findings: SEC-03's exposed signing keys are more dangerous without a pinned algorithm, and a compromised or just-revoked token stays useful for up to ten minutes after revocation.`,\n  fix: `Pass an explicit algorithms option to the verify call, restricting it to HS256. On logout, role change, or lockout, evict the corresponding cache entry immediately rather than waiting for the TTL, or shorten the TTL and add a token-revocation or version claim that gets checked on every use.`,\n},\n{\n  id: `SEC-24`, title: `CSV and formula injection on export`, severity: `Medium`,\n  tags: `OWASP A03:2021, API8:2023 \u00b7 CWE-1236`,\n  location: `The export helper's cell-safety function, which only prefixes cells that begin with an equals sign.`,\n  body: [`Cells beginning with a plus sign, a minus sign, an at sign, a tab, or a carriage return are left untouched. When the exported spreadsheet is later opened, or re-saved as CSV, in a desktop spreadsheet application, cells like these can still execute as formulas.`],\n  exploit: `A record containing a value like \"+HYPERLINK(...)\" or \"-2+3+cmd...\" in an exported field can execute as a formula the moment someone opens the exported file locally, potentially reaching out to an external resource or running an embedded command depending on the spreadsheet application's own settings.`,\n  fix: `Prefix a single quote on any cell beginning with any of the equals, plus, minus, at, tab or carriage-return characters, not just the equals sign.`,\n  code: [\n    \"const safeValue = (v) => { const s = String(v ?? ``); return \/^[=+\\\\-@\\\\t\\\\r]\/.test(s) ? ``${s}\\` : s; };\",\n  ],\n},\n{\n  id: `SEC-25`, title: `Hardcoded network-monitoring API key and internal host shipped in the frontend bundle`, severity: `Medium`,\n  tags: `OWASP A05\/A02:2021, API8:2023 \u00b7 CWE-798, CWE-200`,\n  location: `The frontend build configuration, which compiles an internal network-monitoring base URL and an API key directly into the client bundle, and the request library, which sends that key as a bearer token.`,\n  body: [`Both an API key and an internal, private-range host address are compiled into every JavaScript bundle shipped to the browser. The key value itself looks like a placeholder, but whether production actually ships a real key in its place could not be confirmed from the code, and in any case any key shipped to a browser has to be treated as public.`],\n  exploit: `Anyone who opens the browser\\`s developer tools, or simply downloads and reads the JavaScript bundle, can read both the internal host address and the API key directly.`,\n  fix: `Never embed API keys in client-side code. Proxy calls to the network-monitoring service through the backend instead, where the key can be held server-side, and remove the internal host address from the client configuration entirely.`,\n},\n{\n  id: `SEC-26`, title: `Dynamic require of a cron job file name sourced from the database`, severity: `Medium`,\n  tags: `OWASP A08:2021, API8:2023 \u00b7 CWE-98, CWE-94`,\n  location: `The cron job runner, which requires a job handler file from a path built using a \"jobFile\" field stored in the database, validated only when the cron row is first created.`,\n  body: [`The scheduler dynamically requires a module path assembled from a database field. A jobFile value containing a directory traversal sequence could cause the scheduler to load a module that was never intended to run as a job. Whoever can create or update cron job rows, which today means anyone, since RBAC is off as described in SEC-01, effectively controls what this scheduler will load and execute.`],\n  exploit: `A user able to create or edit a scheduled job row sets its jobFile field to a path that walks outside the intended jobs directory, and the scheduler dutifully requires whatever it finds there on its next poll.`,\n  fix: `Map jobFile values through a fixed registry of explicitly allowed job handlers, rather than building a require path from the raw field, and reject any value that contains a path separator outright.`,\n},\n{\n  id: `SEC-27`, title: `L3 storage \"presigned\" URLs are not actually signed`, severity: `Medium`,\n  tags: `OWASP A01\/A02:2021, API1:2023 \u00b7 CWE-639, CWE-284`,\n  location: `The internal L3 storage service, whose so-called presigned URL is simply the raw object path, unlike the S3 adapter, which does generate genuine, time-limited signed URLs.`,\n  body: [`Access to an L3-backed object depends entirely on possession of a static, shared bearer token rather than on any per-object or time-limited control. If a URL, or the shared token, ever reaches a client or gets logged somewhere, there is no expiry or scoping to fall back on.`],\n  exploit: `Anyone who obtains an L3 object URL along with the shared token, whether through a log, a browser history entry, or any other leak, can access that object indefinitely, with no way to revoke just that one link.`,\n  fix: `Implement genuine signed, expiring URLs in the L3 service, for example an HMAC computed over the object key and an expiry timestamp, or, more simply, always proxy L3 downloads through the authenticated backend with an explicit ownership check rather than handing out direct URLs at all.`,\n},\n{\n  id: `SEC-28`, title: `State-changing operations are exposed as GET requests`, severity: `Low`,\n  tags: `OWASP A04:2021, API8:2023 \u00b7 CWE-650`,\n  location: `The SAM compliance controller, where runComplianceJob and makeCompliant both sit in the GET action list rather than the POST list.`,\n  body: [`Both of these actions mutate data, and runComplianceJob in particular triggers an unbounded recompute across the entire software inventory, which makes it a denial-of-service surface on top of simply being a side effect attached to a verb that is conventionally expected to be safe.`],\n  exploit: `A GET request, which caches, proxies and browsers may all treat as safe to repeat or prefetch, can trigger a full compliance recompute or force a product into a compliant state as a side effect.`,\n  fix: `Move both actions to POST, apply proper authorization, and add a concurrency guard, or move the heavy recompute onto a queue, so it cannot be triggered repeatedly in quick succession.`,\n},\n{\n  id: `SEC-29`, title: `Insufficient security audit logging`, severity: `Low`,\n  tags: `OWASP A09:2021, API9:2023 \u00b7 CWE-778`,\n  location: `There is no dedicated audit trail anywhere for authentication or authorization events, such as logins, permission changes, or role assignments. What exists today is CI-data change history and the ordinary winston error and HTTP logs.`,\n  body: [`Without a specific, tamper-evident record of security-relevant events, an account takeover such as the one described in SEC-02, an insecure direct object reference such as SEC-04, or an unnoticed privilege change would all leave little to no forensic trail behind them.`],\n  exploit: `After an incident, there would be very little to reconstruct what actually happened: which account was used, when a permission changed, or when an asset's ownership was transferred.`,\n  fix: `Emit structured, tamper-evident audit events for login success and failure, password and two-factor changes, permission and role changes, and asset-ownership transfers, and send them to an append-only store separate from ordinary application logs.`,\n},\n{\n  id: `SEC-30`, title: `PII appears in application logs`, severity: `Low`,\n  tags: `OWASP A09:2021, API9:2023 \u00b7 CWE-532`,\n  location: `Several controllers across the codebase, including the inventory location master, the team controller and the agent route, that log the authenticated user object or full request bodies directly with console.log.`,\n  body: [`User identity fields and raw request bodies end up written directly into log files, which is a straightforward personal-data exposure risk for anyone with log access.`],\n  exploit: `Anyone with read access to application logs, which is typically a broader group than those with direct database access, can read personal information that was never meant to be logged at all.`,\n  fix: `Remove console.log calls that dump user or request objects, and replace them with structured logging that redacts sensitive fields by default.`,\n},\n{\n  id: `SEC-31`, title: `CORS allows any request that carries no Origin header`, severity: `Low`,\n  tags: `OWASP A05:2021, API8:2023 \u00b7 CWE-346`,\n  location: `The CORS configuration in the main application file, which explicitly allows any request with no Origin header through.`,\n  body: [`Non-browser clients, which typically do not send an Origin header at all, are always allowed through regardless of the configured allowlist. Combined with the token-in-query-string issue described in SEC-16, this meaningfully widens the set of non-browser callers that can reach the API.`],\n  exploit: `A script or tool making direct HTTP calls, rather than calls originating from a browser page, bypasses the origin allowlist entirely simply by not sending an Origin header.`,\n  fix: `For an API that is meant to be browser-only, reject requests with a missing or unrecognized origin on state-changing routes, while keeping an explicit allowlist for the origins that are genuinely expected.`,\n},\n{\n  id: `SEC-32`, title: `ReDoS risk via user-controlled regular expressions in search pipelines`, severity: `Low`,\n  tags: `OWASP A03:2021, API4:2023 \u00b7 CWE-1333`,\n  location: `A shared pipeline utility and several per-module pipeline helpers in SAM and import\/export, all of which interpolate user-supplied search text directly into a MongoDB $regex or $regexMatch stage without escaping it.`,\n  body: [`Because the search text is not escaped before being used as a regular expression, a caller can supply a pattern engineered to trigger catastrophic backtracking, tying up the database or the application process on that single query.`],\n  exploit: `A search request carrying a deliberately pathological regular expression pattern in its search text causes the matching query to take a very long time to complete, degrading service for other users in the meantime.`,\n  fix: `Escape regular-expression metacharacters in any user-supplied search text before it reaches a $regex stage, or move to MongoDB text indexes or anchored-prefix matching instead, and cap the maximum length of search input.`,\n},\n{\n  id: `SEC-33`, title: `Weak client-side password encryption scheme`, severity: `Low`,\n  tags: `OWASP A02:2021 \u00b7 CWE-780`,\n  location: `The frontend's RSA encryption helper, using PKCS#1 v1.5 padding, its server-side counterpart, and the public key endpoint, which is itself fetched over the same channel it is meant to protect.`,\n  body: [`This client-side RSA layer was originally added as a remediation for an earlier \"cleartext transmission\" finding. PKCS#1 v1.5 padding is known to be susceptible to Bleichenbacher-style attacks, and in any case this mechanism was never a substitute for TLS, only a supplement to it that mainly keeps plaintext passwords out of logs and browser history.`],\n  exploit: `In a scenario where TLS itself is somehow compromised or absent, the PKCS#1 v1.5 padding scheme is theoretically vulnerable to a padding-oracle style attack against the encrypted password.`,\n  fix: `Prefer OAEP-SHA256 end to end, since the server already supports a newer decrypt path that uses it, and continue to treat this mechanism as defense-in-depth on top of TLS, never as a replacement for it.`,\n},\n{\n  id: `SEC-34`, title: `validatorjs is an effectively unmaintained dependency`, severity: `Low`,\n  tags: `OWASP A06:2021, API8:2023 \u00b7 CWE-1104`,\n  location: `Both applications' package.json files, where validatorjs is used for authentication input validation.`,\n  body: [`This library has seen little to no maintenance activity in some time, which means any future vulnerability discovered in it may not receive a fix.`],\n  exploit: `Not directly exploitable on its own today, but it represents accumulating risk: a future vulnerability in an unmaintained dependency used on the authentication path would have no vendor patch to rely on.`,\n  fix: `Migrate to zod, which is already a frontend dependency, or to joi or express-validator, and enforce schema validation server-side on every action rather than relying on ad hoc checks.`,\n},\n],\n\nthreatModel: {\n  boundaries: [\n    { t: \"Browser SPA to API\", d: \"JWT bearer (or ?authtoken= query). The SPA computes RBAC hint headers client-side \\u2014 attacker-controllable.\" },\n    { t: \"Discovery agent to API\", d: \"A separate long-lived agent JWT, minted by an unauthenticated endpoint.\" },\n    { t: \"API to data stores\", d: \"MongoDB (Mongoose), Redis (BullMQ + search), S3\/L3 object storage, SMTP, external ML\/geocoding services.\" },\n    { t: \"Admin\/ops to host\", d: \"env files on disk, PM2, MongoDB (bindIp: 0.0.0.0 in the reference config).\" },\n    { t: \"CI\/CD\", d: \"git subtree module repos; CircleCI \/ GitLab CI.\" }\n  ],\n  surface: [\n    \"One dynamic dispatcher exposing ~150 controller actions across 6 modules\",\n    \"Static routes: \/users\/*, \/files\/*, \/access\/*, \/import\/*, \/externalIntegrations\/*, \/agents\/*\",\n    \"File upload\/download, CSV\/XLSX import\/export, runtime schema\/form builder, workflow engine, background jobs\"\n  ],\n  sensitiveData: [\n    \"User PII \\u2014 names, emails, mobiles, employee codes\",\n    \"Credentials \\u2014 bcrypt hashes (select:false), but plaintext refresh tokens, reset tokens\/OTPs, 2FA codes\",\n    \"Software license keys \\u2014 plaintext\",\n    \"Financial data \\u2014 acquisition cost, depreciation, PO \/ contract values\",\n    \"Asset ownership \/ CMDB \\u2014 who holds which device, IP \/ MAC \/ FQDN\",\n    \"Committed secrets \\u2014 cloud keys, DB \/ mail \/ Redis creds, token-signing keys\"\n  ],\n  abuseCases: [\n    \"Tamper with asset ownership \\u2014 reassign or transfer another department's asset (IDOR)\",\n    \"Exfiltrate license keys \\u2014 read sam\/entitlements\/find and receive plaintext keys\",\n    \"Cross-department data theft \\u2014 read financials, contracts, tickets outside one's org scope\",\n    \"Forge CMDB inventory \\u2014 enroll a rogue agent, overwrite CI records by serial\",\n    \"Falsify compliance \\u2014 mutate entitlement \/ linkage counts before an audit\",\n    \"Privilege escalation \\u2014 self-register with elevated roles, or via the unauthenticated webhook\",\n    \"Account takeover \\u2014 reset a manager's \/ admin's password without email access\",\n    \"Steal uploaded documents \\u2014 invoices, contracts, ID documents via the unauthenticated file endpoint\"\n  ]\n},\n\nremediation: {\n  quickWins: [\n    { t: \"Rotate all committed secrets and purge them from git history\", ref: \"SEC-03\" },\n    { t: \"Fix \/forgot\/reset to verify the OTP\", ref: \"SEC-02\" },\n    { t: \"Add isAuthenticated + ownership check to GET \/files\/:id\", ref: \"SEC-07\" },\n    { t: \"Confine export download to the exports directory\", ref: \"SEC-08\" },\n    { t: \"Whitelist report names instead of require(fileName)\", ref: \"SEC-09\" },\n    { t: \"Add express-rate-limit and switch OTPs to crypto.randomInt\", ref: \"SEC-11\" },\n    { t: \"Enable a real Helmet baseline + CSP, scope trust proxy, generic errors\", ref: \"SEC-12\" },\n    { t: \"Auth + signature on \/externalIntegrations\/insert\", ref: \"SEC-17\" },\n    { t: \"Remove tls.rejectUnauthorized:false; pin JWT algorithms:['HS256']\", ref: \"SEC-21 \/ 23\" },\n    { t: \"Escape + - @ on CSV export; stop shipping the NMS key client-side\", ref: \"SEC-24 \/ 25\" }\n  ],\n  structural: [\n    { t: \"Turn RBAC on by default and fail closed, then implement record-level scoping in Base.Controller\", ref: \"SEC-01 \/ 04\" },\n    { t: \"Centralize input sanitization \\u2014 express-mongo-sanitize on all methods; never pass raw req.body as an update document\", ref: \"SEC-05\" },\n    { t: \"Redesign agent enrollment \\u2014 device pre-registration, enrollment secret, short-lived rotating tokens\", ref: \"SEC-06\" },\n    { t: \"Encrypt license keys and other secrets at rest, with a gated reveal action and audit\", ref: \"SEC-14 \/ 19\" },\n    { t: \"Harden the form builder \\u2014 admin-only, reserved-collection denylist, no model re-binding\", ref: \"SEC-10\" },\n    { t: \"Move tokens out of localStorage \/ URLs to HttpOnly cookies + CSRF, or signed download links\", ref: \"SEC-16\" },\n    { t: \"Adopt a security pipeline \\u2014 secret scanning, npm audit \/ Snyk, SAST, dependency upgrades in CI\", ref: \"SEC-13 \/ 34\" },\n    { t: \"Add security audit logging for auth \/ authz \/ ownership events\", ref: \"SEC-29\" }\n  ],\n  priorityOrder: [\"SEC-03\", \"SEC-02\", \"SEC-01\", \"SEC-04\", \"SEC-07\", \"SEC-08\", \"SEC-09\", \"SEC-05\", \"SEC-06\", \"SEC-11\", \"SEC-12\", \"SEC-14\"]\n},\n\nllmApplicability: `Not applicable. No large-language-model or generative-AI feature was found in either codebase. The only ML\/AI touchpoints are an external object-detection image service (a computer-vision API, not an LLM \\u2014 SSRF surface is limited since the host is env-fixed) and a rule-based chatbot in ticketing (node\/edge driven, deterministic, not an LLM). If an LLM is later added, reassess against the OWASP LLM Top 10, especially prompt injection, sensitive-information disclosure (given the plaintext license keys and PII here), and excessive agency if it can call these mutating APIs.`\n};\n\/* -------------------------------------------------------------------------\n   06 \u2014 Open Questions\n   ------------------------------------------------------------------------- *\/\nDATA.openQuestions = {\n  intro: `This is the list of things the code could not answer definitively, addressed to the owning team. Each item is tagged with why it matters. Please confirm or correct.`,\n  groups: [\n    {\n      letter: \"A\", title: \"Configuration and deployment\", sub: \"Blocking for accurate risk assessment\",\n      items: [\n        \"Is FEATURE_RBAC actually ON in production? The committed env sets it OFF and the code default is OFF. If it is OFF in prod, SEC-01 \/ SEC-04 are actively exploitable.\",\n        \"Which env file is actually loaded in each environment? The loader reads .env and .env.<NODE_ENV>, but committed files are named env, env.production, env.qa (no leading dot). This determines whether the SEC-03 secrets are live.\",\n        \"Are the committed secrets real and still valid? AWS_ACCESS_KEY_ID begins AK (AKIA pattern) and the JWT keys look real. Please confirm and rotate regardless.\",\n        \"Is there a reverse proxy (Nginx) terminating TLS and enforcing HTTPS\/HSTS? Helmet's HSTS is disabled in-app and trust proxy is true. Is MongoDB network-isolated in production?\",\n        \"Does the API run clustered or single-process? ecosystem.config.js uses instances:1, exec_mode:'fork'. The in-process JWT LRU cache assumes single-process.\",\n        \"Is the search worker run twice? server.js spawns it as a worker_thread and PM2 also runs worker.js as a separate process.\"\n      ]\n    },\n    {\n      letter: \"B\", title: \"Architecture and data model\", sub: \"\",\n      items: [\n        \"Where does the incident \/ problem \/ change \/ CAB desk live? Those ticketing_* collections exist in the live DB dump but have no models in this repo.\",\n        \"RBAC collection drift: the live DB stores permissions in core_roleaccessmappings, but this repo's Access model targets core_accesses. Which is authoritative?\",\n        \"SAM index drift: schema-declared unique indexes (entitlementNo, allocationNo, unitAssetTag, {entitlement,installation}) are absent in the live DB dump.\",\n        \"NMS module: the frontend calls a network-monitoring service (hardcoded 192.168.1.74:5000, key \\\"abc\\\"). Is NMS a real integration for production?\",\n        \"Fleet module: is it intentionally a stub for now, or is code missing from the subtree pull?\",\n        \"possession vs possession23: two asset-possession implementations exist under fam\/asset. Which is live?\"\n      ]\n    },\n    {\n      letter: \"C\", title: \"Authentication and identity\", sub: \"\",\n      items: [\n        \"Is SSO \/ LDAP \/ AD planned? No SAML \/ OIDC \/ LDAP code exists; auth is local email + password + optional email OTP.\",\n        \"Is open self-registration (POST \/users\/register) intended to be reachable in production? It is unauthenticated and accepts a roles array. New accounts are status:'off' \\u2014 what is the activation process?\",\n        \"The register roles field vs. the roleDepartment model: the model uses roleDepartment[], but registration writes a flat roles array. Is roles silently dropped, or does it grant anything?\",\n        \"Agent enrollment: is POST \/agents\/login (unauthenticated, 364-day token) the intended enrollment flow, or is device pre-registration handled elsewhere? How are agents deprovisioned?\"\n      ]\n    },\n    {\n      letter: \"D\", title: \"Security finding exploitability\", sub: \"To confirm against a running instance\",\n      items: [\n        \"SEC-09 (reporting require): is any writable .js path reachable by an attacker? Determines LFI-only vs. full RCE.\",\n        \"SEC-05 (NoSQL injection): is Mongoose running with any global sanitize\/strict settings, and do per-module transformFindQuery overrides cover the controllers that matter?\",\n        \"SEC-17 (\/externalIntegrations\/insert): is this endpoint exposed publicly, or only on an internal network segment?\",\n        \"L3 object store (SEC-27): is the L3 service itself authenticated \/ network-isolated, and are its object URLs ever handed to browser clients?\",\n        \"Object-detection and geocoding services: are OBJECT_DETECTION_BASE_URL and LAT_LONG_URL internal or third-party?\"\n      ]\n    },\n    {\n      letter: \"E\", title: \"Business logic questions\", sub: \"For correctness, not security\",\n      items: [\n        \"SAM decommission accounting: \\\"Suspended\\\"\/\\\"Uninstalled\\\" free the linkage but do not decrement inventory.consumedQuantity, while \\\"Returned\\\" does. Is that intended?\",\n        \"Financial values as strings: Fam_Assetfinances stores monetary amounts as strings. Is there a reason, given it blocks numeric aggregation?\",\n        \"Which workflow engine is canonical \\u2014 v1 (core\/workflow, live) or v2 (core\/workflow2, dormant)? What is the migration plan given the model-name collision?\",\n        \"Contract renewal notifications: SAM contracts have renewal fields but no cron that emails on approaching expiry was found \\u2014 is this handled manually or is a job missing?\"\n      ]\n    },\n    {\n      letter: \"F\", title: \"Assumptions made in this documentation\", sub: \"\",\n      items: [\n        \"We assumed the two \\u201clocked\\u201d folders are the current authoritative snapshots; the sibling .zip files appear to be the same snapshots.\",\n        \"We treated the mongodump under POC\/TJSB - New ITAM\/test_itam\/ as representative of the live schema; it is a POC\/tenant instance, so some collections\/indexes may differ elsewhere.\",\n        \"We did not run the application, execute exploits, or run npm audit (offline). Dependency-vulnerability claims (SEC-13) are from known public advisories, not a live scan.\",\n        \"Severity ratings are CVSS-style qualitative judgments, assuming the app is internet-reachable or reachable by a semi-trusted internal population. Adjust down if hardened internal-only.\",\n        \"Where a finding's impact depends on runtime configuration (especially FEATURE_RBAC), we documented the worst credible case and tagged the dependency.\"\n      ]\n    },\n    {\n      letter: \"G\", title: \"What we could not review\", sub: \"\",\n      items: [\n        \"The proprietary @meltx\/global-search-worker package (vendored as a .tgz) was reviewed only through its integration points, not its internal source.\",\n        \"The Windows discovery agent binary\/installer (meltx-agent) is out of repo scope; its client-side auth and update mechanism were not reviewed.\",\n        \"The separate ITSM (incident\/change\/CAB) service source was not available in this repo.\"\n      ]\n    }\n  ]\n};\n\n\/* Compute derived search index once all sections are defined *\/\nDATA.searchIndex = [];\n\n\n\/* ==========================================================================\n   MeltX New Suite ITAM \u2014 Audit Intelligence Console\n   Application logic: icons, utils, charts, router, page renderers.\n   ========================================================================== *\/\n\n\/* -------------------------------------------------------------------------\n   Icon library \u2014 minimal original line-icon set, 24x24, stroke-based\n   ------------------------------------------------------------------------- *\/\nconst ICONS = {\n  grid: '<rect x=\"3\" y=\"3\" width=\"7.5\" height=\"7.5\" rx=\"1.6\"\/><rect x=\"13.5\" y=\"3\" width=\"7.5\" height=\"7.5\" rx=\"1.6\"\/><rect x=\"3\" y=\"13.5\" width=\"7.5\" height=\"7.5\" rx=\"1.6\"\/><rect x=\"13.5\" y=\"13.5\" width=\"7.5\" height=\"7.5\" rx=\"1.6\"\/>',\n  layers: '<path d=\"M12 3 21 8 12 13 3 8 12 3Z\"\/><path d=\"M3 13 12 18 21 13\"\/><path d=\"M3 17.5 12 22.5 21 17.5\"\/>',\n  briefcase: '<rect x=\"2.5\" y=\"7.5\" width=\"19\" height=\"12.5\" rx=\"2\"\/><path d=\"M8 7.5V5.5A2 2 0 0 1 10 3.5H14A2 2 0 0 1 16 5.5V7.5\"\/><path d=\"M2.5 13H21.5\"\/>',\n  box: '<path d=\"M3.5 7 12 3 20.5 7 12 11 3.5 7Z\"\/><path d=\"M3.5 7V17L12 21M20.5 7V17L12 21M12 11V21\"\/>',\n  cpu: '<rect x=\"7\" y=\"7\" width=\"10\" height=\"10\" rx=\"1.4\"\/><rect x=\"10\" y=\"10\" width=\"4\" height=\"4\" rx=\".6\"\/><path d=\"M9 2.5V6M15 2.5V6M9 18V21.5M15 18V21.5M2.5 9H6M2.5 15H6M18 9H21.5M18 15H21.5\"\/>',\n  activity: '<path d=\"M2.5 12H7L9.5 5 14 19 16.5 12H21.5\"\/>',\n  shield: '<path d=\"M12 2.5 20 5.5V11C20 16 16.6 19.8 12 21.5 7.4 19.8 4 16 4 11V5.5L12 2.5Z\"\/>',\n  'shield-alert': '<path d=\"M12 2.5 20 5.5V11C20 16 16.6 19.8 12 21.5 7.4 19.8 4 16 4 11V5.5L12 2.5Z\"\/><path d=\"M12 8V12.5\"\/><circle cx=\"12\" cy=\"15.6\" r=\".9\" fill=\"currentColor\" stroke=\"none\"\/>',\n  'help-circle': '<circle cx=\"12\" cy=\"12\" r=\"9.3\"\/><path d=\"M9.2 9.3C9.4 7.9 10.6 7 12 7 13.6 7 15 8.1 15 9.6 15 11 13.8 11.6 12.8 12.2 12.2 12.6 12 13 12 13.8\"\/><circle cx=\"12\" cy=\"17\" r=\".9\" fill=\"currentColor\" stroke=\"none\"\/>',\n  server: '<rect x=\"3\" y=\"4\" width=\"18\" height=\"7\" rx=\"1.6\"\/><rect x=\"3\" y=\"13\" width=\"18\" height=\"7\" rx=\"1.6\"\/><circle cx=\"7\" cy=\"7.5\" r=\".9\" fill=\"currentColor\" stroke=\"none\"\/><circle cx=\"7\" cy=\"16.5\" r=\".9\" fill=\"currentColor\" stroke=\"none\"\/>',\n  monitor: '<rect x=\"2.5\" y=\"4\" width=\"19\" height=\"13\" rx=\"1.6\"\/><path d=\"M8 21H16M12 17V21\"\/>',\n  radar: '<circle cx=\"12\" cy=\"12\" r=\"9.3\"\/><circle cx=\"12\" cy=\"12\" r=\"5.6\"\/><circle cx=\"12\" cy=\"12\" r=\"1.9\"\/><path d=\"M12 12 18.5 6.5\"\/>',\n  crown: '<path d=\"M3.5 8.5 7.5 11 12 4.5 16.5 11 20.5 8.5 19 18H5L3.5 8.5Z\"\/>',\n  clipboard: '<rect x=\"5\" y=\"4.5\" width=\"14\" height=\"17\" rx=\"2\"\/><rect x=\"9\" y=\"2.5\" width=\"6\" height=\"3.5\" rx=\"1\"\/><path d=\"M8.5 11H15.5M8.5 15H15.5\"\/>',\n  headset: '<path d=\"M4 13V12A8 8 0 0 1 20 12V13\"\/><rect x=\"2.5\" y=\"13\" width=\"4\" height=\"6\" rx=\"1.4\"\/><rect x=\"17.5\" y=\"13\" width=\"4\" height=\"6\" rx=\"1.4\"\/><path d=\"M20 19V20A2 2 0 0 1 18 22H14\"\/>',\n  'search-check': '<circle cx=\"10.5\" cy=\"10.5\" r=\"7\"\/><path d=\"M20.5 20.5 15.7 15.7\"\/><path d=\"M7.5 10.5 9.5 12.5 13.5 8\"\/>',\n  user: '<circle cx=\"12\" cy=\"8\" r=\"3.6\"\/><path d=\"M4.5 20.5C5.4 16.6 8.3 14.5 12 14.5S18.6 16.6 19.5 20.5\"\/>',\n  key: '<circle cx=\"8\" cy=\"15\" r=\"4.2\"\/><path d=\"M11 12 18.5 4.5M16 7 18.5 9.5M13.3 9.7 15.5 11.9\"\/>',\n  'map-pin': '<path d=\"M12 21.5S19 15 19 9.8A7 7 0 0 0 5 9.8C5 15 12 21.5 12 21.5Z\"\/><circle cx=\"12\" cy=\"9.6\" r=\"2.4\"\/>',\n  'trending-up': '<path d=\"M2.5 17.5 9.5 10.5 13.5 14.5 21.5 6.5\"\/><path d=\"M15.5 6.5H21.5V12.5\"\/>',\n  database: '<ellipse cx=\"12\" cy=\"5.5\" rx=\"8\" ry=\"3\"\/><path d=\"M4 5.5V18.5C4 20.2 7.6 21.5 12 21.5S20 20.2 20 18.5V5.5\"\/><path d=\"M4 12C4 13.7 7.6 15 12 15S20 13.7 20 12\"\/>',\n  'file-text': '<path d=\"M6 2.5H14L19 7.5V21.5H6Z\"\/><path d=\"M14 2.5V7.5H19\"\/><path d=\"M9 12.5H15.5M9 16.5H15.5\"\/>',\n  'check-circle': '<circle cx=\"12\" cy=\"12\" r=\"9.3\"\/><path d=\"M7.5 12.2 10.3 15 16.5 8.7\"\/>',\n  'hard-drive': '<rect x=\"2.5\" y=\"8\" width=\"19\" height=\"10\" rx=\"2\"\/><path d=\"M2.5 14.5H21.5\"\/><circle cx=\"6.5\" cy=\"16\" r=\".8\" fill=\"currentColor\" stroke=\"none\"\/><circle cx=\"10\" cy=\"16\" r=\".8\" fill=\"currentColor\" stroke=\"none\"\/>',\n  shuffle: '<path d=\"M2.5 6H6.7L15 18H21.5M15.5 6H21.5M2.5 18H6.7L9.3 14\"\/><path d=\"M18 3 21.5 6 18 9M18 15 21.5 18 18 21\"\/>',\n  'list-todo': '<rect x=\"3\" y=\"4.5\" width=\"4\" height=\"4\" rx=\"1\"\/><rect x=\"3\" y=\"10.5\" width=\"4\" height=\"4\" rx=\"1\"\/><rect x=\"3\" y=\"16.5\" width=\"4\" height=\"4\" rx=\"1\"\/><path d=\"M10 6.5H21M10 12.5H21M10 18.5H21\"\/>',\n  terminal: '<rect x=\"2.5\" y=\"4\" width=\"19\" height=\"16\" rx=\"2\"\/><path d=\"M6.5 9 10.5 12 6.5 15M13 15.5H17.5\"\/>',\n  copy: '<rect x=\"8.5\" y=\"8.5\" width=\"12\" height=\"12\" rx=\"2\"\/><path d=\"M15.5 8.5V5.5A2 2 0 0 0 13.5 3.5H5.5A2 2 0 0 0 3.5 5.5V13.5A2 2 0 0 0 5.5 15.5H8.5\"\/>',\n  'package-x': '<path d=\"M3.5 7 12 3 20.5 7 12 11 3.5 7Z\"\/><path d=\"M3.5 7V17L12 21M20.5 7V17L12 21M12 11V21\"\/><path d=\"M9.5 14.5 14.5 19.5M14.5 14.5 9.5 19.5\" stroke=\"var(--paper)\" stroke-width=\"2.4\"\/>',\n  moon: '<path d=\"M20 13.8A8.5 8.5 0 1 1 10.2 4 6.8 6.8 0 0 0 20 13.8Z\"\/>',\n  sun: '<circle cx=\"12\" cy=\"12\" r=\"4.3\"\/><path d=\"M12 2.5V5M12 19V21.5M4.2 4.2 6 6M18 18 19.8 19.8M2.5 12H5M19 12H21.5M4.2 19.8 6 18M18 6 19.8 4.2\"\/>',\n  'test-tube': '<path d=\"M9 3 9 14.5A3.5 3.5 0 0 0 16 14.5L16 3\"\/><path d=\"M7 3H18M9.5 11H15.5\"\/>',\n  search: '<circle cx=\"10.5\" cy=\"10.5\" r=\"7\"\/><path d=\"M20.5 20.5 15.7 15.7\"\/>',\n  x: '<path d=\"M5 5 19 19M19 5 5 19\"\/>',\n  menu: '<path d=\"M3.5 6.5H20.5M3.5 12H20.5M3.5 17.5H20.5\"\/>',\n  'chevron-down': '<path d=\"M5.5 9 12 15.5 18.5 9\"\/>',\n  'chevron-right': '<path d=\"M9 5.5 15.5 12 9 18.5\"\/>',\n  'arrow-right': '<path d=\"M4 12H20M13.5 5.5 20 12 13.5 18.5\"\/>',\n  'arrow-up': '<path d=\"M12 20V4M5.5 10.5 12 4 18.5 10.5\"\/>',\n  'external-link': '<path d=\"M18 13.5V19A2 2 0 0 1 16 21H5A2 2 0 0 1 3 19V8A2 2 0 0 1 5 6H10.5\"\/><path d=\"M14.5 3H21V9.5M21 3 11.5 12.5\"\/>',\n  check: '<path d=\"M4.5 12.5 9.5 17.5 19.5 6.5\"\/>',\n  'alert-triangle': '<path d=\"M12 3.5 22 20.5H2L12 3.5Z\"\/><path d=\"M12 10V14.5\"\/><circle cx=\"12\" cy=\"17.3\" r=\".9\" fill=\"currentColor\" stroke=\"none\"\/>',\n  clock: '<circle cx=\"12\" cy=\"12\" r=\"9.3\"\/><path d=\"M12 6.5V12L16 14.5\"\/>',\n  lock: '<rect x=\"4.5\" y=\"10.5\" width=\"15\" height=\"10.5\" rx=\"2\"\/><path d=\"M7.5 10.5V7.5A4.5 4.5 0 0 1 16.5 7.5V10.5\"\/>',\n  zap: '<path d=\"M13 2.5 4.5 14H11L10.5 21.5 19.5 10H13L13 2.5Z\"\/>',\n  target: '<circle cx=\"12\" cy=\"12\" r=\"9.3\"\/><circle cx=\"12\" cy=\"12\" r=\"5.4\"\/><circle cx=\"12\" cy=\"12\" r=\"1.6\" fill=\"currentColor\" stroke=\"none\"\/>',\n  filter: '<path d=\"M3 4.5H21L14 13V19.5L10 21.5V13L3 4.5Z\"\/>',\n  download: '<path d=\"M12 3V15.5M6.5 11 12 16.5 17.5 11\"\/><path d=\"M4 19.5H20\"\/>',\n  printer: '<rect x=\"4.5\" y=\"8.5\" width=\"15\" height=\"8\" rx=\"1.6\"\/><path d=\"M7 8.5V3.5H17V8.5M7 20.5H17V14.5H7Z\"\/>',\n  info: '<circle cx=\"12\" cy=\"12\" r=\"9.3\"\/><path d=\"M12 11V16.5\"\/><circle cx=\"12\" cy=\"7.6\" r=\".9\" fill=\"currentColor\" stroke=\"none\"\/>',\n  'book-open': '<path d=\"M12 6.5C10.5 5 7.5 4.3 3.5 4.5V18.5C7.5 18.3 10.5 19 12 20.5C13.5 19 16.5 18.3 20.5 18.5V4.5C16.5 4.3 13.5 5 12 6.5Z\"\/><path d=\"M12 6.5V20.5\"\/>',\n  list: '<path d=\"M9 6.5H21M9 12H21M9 17.5H21\"\/><circle cx=\"4.5\" cy=\"6.5\" r=\"1\" fill=\"currentColor\" stroke=\"none\"\/><circle cx=\"4.5\" cy=\"12\" r=\"1\" fill=\"currentColor\" stroke=\"none\"\/><circle cx=\"4.5\" cy=\"17.5\" r=\"1\" fill=\"currentColor\" stroke=\"none\"\/>',\n  'corner-down-right': '<path d=\"M5 3.5V10.5A3 3 0 0 0 8 13.5H19M14.5 9 19 13.5 14.5 18\"\/>',\n  compass: '<circle cx=\"12\" cy=\"12\" r=\"9.3\"\/><path d=\"M15.5 8.5 13.2 13.2 8.5 15.5 10.8 10.8 15.5 8.5Z\"\/>',\n  layout: '<rect x=\"3\" y=\"3.5\" width=\"18\" height=\"17\" rx=\"2\"\/><path d=\"M3 8.5H21\"\/>',\n  gauge: '<circle cx=\"12\" cy=\"13\" r=\"8.3\"\/><path d=\"M12 13 16 8.5M12 4.7V6.5\"\/>',\n  home: '<path d=\"M4 11 12 4 20 11V20.5H14V15H10V20.5H4Z\"\/>'\n};\n\nfunction iconHTML(name, size) {\n  size = size || 18;\n  const body = ICONS[name] || ICONS.info;\n  return `<svg viewBox=\"0 0 24 24\" width=\"${size}\" height=\"${size}\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.7\" stroke-linecap=\"round\" stroke-linejoin=\"round\">${body}<\/svg>`;\n}\n\/* icon() returns a real DOM element so it can be used directly as a child of h(...) *\/\nfunction icon(name, size) {\n  const tmp = document.createElement('span');\n  tmp.innerHTML = iconHTML(name, size);\n  return tmp.firstElementChild;\n}\n\n\/* -------------------------------------------------------------------------\n   Small utilities\n   ------------------------------------------------------------------------- *\/\nfunction esc(s) {\n  if (s === null || s === undefined) return '';\n  return String(s).replace(\/[&<>\"']\/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c]));\n}\nfunction fmtNum(n) {\n  if (n === null || n === undefined) return '\u2014';\n  return n.toLocaleString('en-US');\n}\nfunction fmtK(n) {\n  if (n >= 1000) return (n \/ 1000).toFixed(n % 1000 === 0 ? 0 : 1) + 'K';\n  return String(n);\n}\nfunction qs(sel, root) { return (root || document).querySelector(sel); }\nfunction qsa(sel, root) { return Array.from((root || document).querySelectorAll(sel)); }\nfunction h(tag, attrs, ...children) {\n  const node = document.createElement(tag);\n  attrs = attrs || {};\n  Object.keys(attrs).forEach((k) => {\n    if (k === 'class') node.className = attrs[k];\n    else if (k === 'html') node.innerHTML = attrs[k];\n    else if (k.startsWith('on') && typeof attrs[k] === 'function') node.addEventListener(k.slice(2), attrs[k]);\n    else node.setAttribute(k, attrs[k]);\n  });\n  children.flat(Infinity).forEach((c) => {\n    if (c === null || c === undefined || c === false) return;\n    if (typeof c === 'string' || typeof c === 'number' || typeof c === 'boolean') {\n      node.appendChild(document.createTextNode(String(c)));\n    } else if (c instanceof Node) {\n      node.appendChild(c);\n    }\n  });\n  return node;\n}\nfunction severityRank(sev) {\n  const order = { Critical: 0, High: 1, Medium: 2, Low: 3 };\n  return order[sev] ?? 9;\n}\nfunction sevClass(sev) { return String(sev || '').toLowerCase(); }\n\n\/* Animate a count-up on numeric readouts *\/\nfunction countUp(el, target, opts) {\n  opts = opts || {};\n  const dur = opts.dur || 900;\n  const suffix = opts.suffix || '';\n  const decimals = opts.decimals || 0;\n  const start = performance.now();\n  function frame(now) {\n    const t = Math.min(1, (now - start) \/ dur);\n    const eased = 1 - Math.pow(1 - t, 3);\n    const val = target * eased;\n    el.textContent = (decimals ? val.toFixed(decimals) : Math.round(val).toLocaleString('en-US')) + suffix;\n    if (t < 1) requestAnimationFrame(frame);\n    else el.textContent = (decimals ? target.toFixed(decimals) : target.toLocaleString('en-US')) + suffix;\n  }\n  requestAnimationFrame(frame);\n}\n\n\/* Reveal-on-scroll for cards *\/\nfunction observeReveal(root) {\n  const items = qsa('.reveal', root);\n  if (!items.length) return;\n  const io = new IntersectionObserver((entries) => {\n    entries.forEach((e) => {\n      if (e.isIntersecting) {\n        e.target.style.animation = `pageIn .5s cubic-bezier(.2,.7,.2,1) both`;\n        e.target.style.animationDelay = e.target.dataset.delay || '0ms';\n        io.unobserve(e.target);\n      }\n    });\n  }, { threshold: 0.1 });\n  items.forEach((it) => io.observe(it));\n}\n\/* -------------------------------------------------------------------------\n   Charts \u2014 hand-rolled SVG, no dependencies\n   ------------------------------------------------------------------------- *\/\n\n\/* Donut chart: segments = [{label,value,color}] *\/\nfunction donutSVG(segments, opts) {\n  opts = opts || {};\n  const size = opts.size || 200;\n  const stroke = opts.stroke || 26;\n  const r = (size - stroke) \/ 2;\n  const c = size \/ 2;\n  const circumference = 2 * Math.PI * r;\n  const total = segments.reduce((a, s) => a + s.value, 0) || 1;\n  let offset = 0;\n  const rings = segments.map((s, i) => {\n    const frac = s.value \/ total;\n    const len = frac * circumference;\n    const dash = `${len} ${circumference - len}`;\n    const rotation = (offset \/ total) * 360 - 90;\n    offset += s.value;\n    return `<circle class=\"donut-seg\" cx=\"${c}\" cy=\"${c}\" r=\"${r}\" fill=\"none\" stroke=\"${s.color}\"\n      stroke-width=\"${stroke}\" stroke-dasharray=\"${dash}\" stroke-dashoffset=\"0\"\n      transform=\"rotate(${rotation} ${c} ${c})\" stroke-linecap=\"butt\"\n      style=\"--seg-len:${len}; --seg-gap:${circumference - len};\"\/>`;\n  }).join('');\n  return `<svg viewBox=\"0 0 ${size} ${size}\" width=\"${size}\" height=\"${size}\" class=\"donut-svg\">${rings}<\/svg>`;\n}\n\n\/* Animate donut rings drawing in *\/\nfunction animateDonut(container) {\n  qsa('circle.donut-seg', container).forEach((c, i) => {\n    const len = c.style.getPropertyValue('--seg-len');\n    const gap = c.style.getPropertyValue('--seg-gap');\n    c.style.strokeDasharray = `0 ${parseFloat(len) + parseFloat(gap)}`;\n    c.style.transition = 'stroke-dasharray 1s cubic-bezier(.2,.7,.2,1)';\n    c.style.transitionDelay = (i * 90) + 'ms';\n    requestAnimationFrame(() => requestAnimationFrame(() => {\n      c.style.strokeDasharray = `${len} ${gap}`;\n    }));\n  });\n}\n\n\/* Semicircle risk gauge. zones = [{label,color,from,to}] over 0..1, needleAt in 0..1 *\/\nfunction gaugeSVG(needleAt, zoneCount, colors) {\n  const w = 320, h = 190, cx = 160, cy = 165, r = 122;\n  const segAngle = Math.PI \/ zoneCount;\n  let arcs = '';\n  for (let i = 0; i < zoneCount; i++) {\n    const a0 = Math.PI - i * segAngle;\n    const a1 = Math.PI - (i + 1) * segAngle;\n    const x0 = cx + r * Math.cos(a0), y0 = cy - r * Math.sin(a0);\n    const x1 = cx + r * Math.cos(a1), y1 = cy - r * Math.sin(a1);\n    arcs += `<path d=\"M${x0.toFixed(2)} ${y0.toFixed(2)} A ${r} ${r} 0 0 1 ${x1.toFixed(2)} ${y1.toFixed(2)}\"\n      fill=\"none\" stroke=\"${colors[i]}\" stroke-width=\"22\" stroke-linecap=\"butt\" opacity=\"0.92\"\/>`;\n  }\n  const needleAngle = Math.PI - needleAt * Math.PI;\n  const nx = cx + (r - 8) * Math.cos(needleAngle);\n  const ny = cy - (r - 8) * Math.sin(needleAngle);\n  return `<svg viewBox=\"0 0 ${w} ${h}\" width=\"${w}\" height=\"${h}\" class=\"gauge-svg\">\n    ${arcs}\n    <circle cx=\"${cx}\" cy=\"${cy}\" r=\"9\" fill=\"var(--ink)\"\/>\n    <line id=\"gaugeNeedle\" x1=\"${cx}\" y1=\"${cy}\" x2=\"${cx}\" y2=\"${cy - r + 30}\" stroke=\"var(--ink)\" stroke-width=\"4\" stroke-linecap=\"round\"\n      transform=\"rotate(0 ${cx} ${cy})\" style=\"transform-origin:${cx}px ${cy}px; transition: transform 1.1s cubic-bezier(.2,.8,.2,1);\" data-final=\"${(needleAngle * 180 \/ Math.PI - 90).toFixed(1)}\"\/>\n  <\/svg>`;\n}\nfunction animateGauge(container) {\n  const needle = qs('#gaugeNeedle', container);\n  if (!needle) return;\n  const final = needle.dataset.final;\n  needle.style.transform = `rotate(-90deg)`;\n  requestAnimationFrame(() => requestAnimationFrame(() => {\n    needle.style.transform = `rotate(${final}deg)`;\n  }));\n}\n\n\/* Animate .fill bars once visible *\/\nfunction animateBars(container) {\n  qsa('.barrow .fill', container).forEach((f) => {\n    const target = f.dataset.width;\n    requestAnimationFrame(() => requestAnimationFrame(() => { f.style.width = target; }));\n  });\n  qsa('.lifecycle-step .bar', container);\n}\n\n\/* -------------------------------------------------------------------------\n   Router\n   ------------------------------------------------------------------------- *\/\nconst ROUTES = {\n  home: { render: renderHome, label: 'Command Center' },\n  overview: { render: renderOverview, label: 'System Overview' },\n  business: { render: renderBusiness, label: 'Business Overview' },\n  functional: { render: renderFunctional, label: 'Functional Specification' },\n  technical: { render: renderTechnical, label: 'Technical Architecture' },\n  analytics: { render: renderAnalytics, label: 'Analytics & Code Health' },\n  security: { render: renderSecurity, label: 'Security Audit' },\n  questions: { render: renderQuestions, label: 'Open Questions' }\n};\n\nlet currentDeepLink = null;\n\nfunction parseHash() {\n  const raw = location.hash.replace(\/^#\\\/?\/, '');\n  const [page, ...rest] = raw.split('\/');\n  return { page: page || 'home', deep: rest.join('\/') || null };\n}\n\nfunction navigate(pageId, deep) {\n  location.hash = '\/' + pageId + (deep ? '\/' + deep : '');\n}\n\nfunction router() {\n  const { page, deep } = parseHash();\n  const route = ROUTES[page] ? page : 'home';\n  currentDeepLink = deep;\n  renderShellNav(route);\n  const app = qs('#app');\n  app.classList.remove('page');\n  app.innerHTML = '';\n  const pageEl = h('div', { class: 'page' });\n  app.appendChild(pageEl);\n  ROUTES[route].render(pageEl, deep);\n  qs('#crumb').innerHTML = `${iconHTML('home', 13)}<span style=\"margin:0 2px;\">MeltX ITAM<\/span> ${iconHTML('chevron-right', 12)} <b>${ROUTES[route].label}<\/b>`;\n  window.scrollTo({ top: 0, behavior: 'instant' in window ? 'instant' : 'auto' });\n  observeReveal(pageEl);\n  closeSidebarMobile();\n}\n\nfunction renderShellNav(activeId) {\n  const list = qs('#navList');\n  list.innerHTML = '';\n  DATA.nav.forEach((item) => {\n    const li = h('li');\n    const a = h('a', {\n      class: 'nav-item' + (item.id === activeId ? ' active' : ''),\n      href: '#\/' + item.id\n    },\n      h('span', { class: 'num' }, item.num),\n      h('span', { class: 'label' }, item.label)\n    );\n    if (item.flagCritical && DATA.severityCounts.critical > 0) {\n      a.appendChild(h('span', { class: 'dot', title: DATA.severityCounts.critical + ' critical findings' }));\n    }\n    li.appendChild(a);\n    list.appendChild(li);\n  });\n}\n\/* -------------------------------------------------------------------------\n   PAGE: Home \/ Command Center\n   ------------------------------------------------------------------------- *\/\nfunction renderHome(root) {\n  const s = DATA.stats;\n  const sev = DATA.severityCounts;\n\n  const hero = h('section', { class: 'hero-shell' },\n    h('div', { class: 'hero-grid-fx' }),\n    h('div', { class: 'hero-glow' }),\n    h('div', { class: 'hero-inner' },\n      h('div', { class: 'eyebrow' }, 'MeltX Software Solutions \u2014 Engineering Handover'),\n      h('h1', { class: 'h-hero', style: 'margin-top:14px;max-width:16ch;' }, 'Audit Intelligence Console'),\n      h('p', { class: 'lede', style: 'margin-top:14px;' },\n        'Every finding, feature, and architectural decision behind New Suite ITAM, compiled from the full engineering handover into one place \u2014 built for whoever is looking: engineers tracing a bug, testers scoping coverage, or directors reading the risk.'),\n      h('div', { style: 'margin-top:28px;' }, renderReadout())\n    )\n  );\n  root.appendChild(hero);\n\n  \/\/ Posture + severity row\n  const row = h('div', { class: 'grid grid-featured section-gap reveal' });\n  row.appendChild(renderPostureCard());\n  row.appendChild(renderSeverityCard());\n  root.appendChild(row);\n\n  \/\/ Start-here by audience\n  root.appendChild(h('div', { class: 'section-gap reveal' },\n    h('div', { class: 'fig-label' }, 'FIG. 00 \u2014 WHERE TO START'),\n    renderAudienceTiles()\n  ));\n\n  \/\/ Doc cards\n  root.appendChild(h('div', { class: 'section-gap reveal' },\n    h('div', { style: 'display:flex;align-items:baseline;justify-content:space-between;margin-bottom:16px;' },\n      h('h2', { class: 'h2' }, 'The seven documents'),\n      h('span', { class: 'muted', style: 'font-size:12.5px;' }, 'Click any card to open it')\n    ),\n    renderDocCardsGrid()\n  ));\n\n  animateGauge(root);\n  animateDonut(root);\n}\n\nfunction renderReadout() {\n  const s = DATA.stats;\n  const items = [\n    { k: 'Total LOC', v: s.totalLOC, suffix: '', display: fmtK(s.totalLOC) },\n    { k: 'Files', v: s.totalFiles, display: fmtNum(s.totalFiles) },\n    { k: 'Modules', v: s.modules, display: String(s.modules) },\n    { k: 'Test coverage', v: 0, display: '0%', flag: true },\n    { k: 'RBAC (default)', v: null, display: 'OFF', flag: true },\n    { k: 'Findings', v: s.findingsTotal, display: String(s.findingsTotal) }\n  ];\n  const wrap = h('div', { class: 'readout' });\n  items.forEach((it) => {\n    wrap.appendChild(h('div', { class: 'stat' + (it.flag ? ' flag' : '') },\n      h('div', { class: 'k' }, it.k),\n      h('div', { class: 'v' }, it.display)\n    ));\n  });\n  return wrap;\n}\n\nfunction renderPostureCard() {\n  const sev = DATA.severityCounts;\n  const zoneCount = 5;\n  const colors = ['#3E9C6E', '#5B8AA6', '#D6A620', '#E38B29', '#D6453F'];\n  const needleAt = Math.min(0.97, 0.8 + sev.critical * 0.035);\n  const card = h('div', { class: 'card card-pad bracket-card' });\n  card.appendChild(h('div', { class: 'fig-label' }, 'FIG. 01 \u2014 SECURITY POSTURE'));\n  const inner = h('div', { class: 'gauge-wrap' });\n  const gaugeBox = h('div', { class: 'chart-box', style: 'max-width:320px;', html: gaugeSVG(needleAt, zoneCount, colors) });\n  inner.appendChild(gaugeBox);\n  inner.appendChild(h('div', { class: 'gauge-readout' },\n    h('div', { class: 'badge crit', style: 'margin-bottom:10px;' }, icon('alert-triangle', 12), ' Critical risk'),\n    h('div', { class: 'level' }, sev.critical + ' critical findings'),\n    h('p', { class: 'body-text', style: 'margin-top:10px;' },\n      `RBAC is off by default, the password-reset flow can be bypassed, live credentials are committed to the repository, and roughly 90 controllers have no record-level access control. Any one of these is a same-day fix; together they are the platform's actual attack surface.`),\n    h('a', { class: 'small-link', style: 'margin-top:14px;', href: '#\/security' }, 'Open the full security audit', icon('arrow-right', 12))\n  ));\n  card.appendChild(inner);\n  return card;\n}\n\nfunction renderSeverityCard() {\n  const sev = DATA.severityCounts;\n  const segs = [\n    { label: 'Critical', value: sev.critical, color: 'var(--crit)' },\n    { label: 'High', value: sev.high, color: 'var(--high)' },\n    { label: 'Medium', value: sev.medium, color: 'var(--med)' },\n    { label: 'Low', value: sev.low, color: 'var(--low)' }\n  ];\n  const card = h('div', { class: 'card card-pad bracket-card', style: 'display:flex;flex-direction:column;' });\n  card.appendChild(h('div', { class: 'fig-label' }, 'FIG. 02 \u2014 34 FINDINGS BY SEVERITY'));\n  const chartRow = h('div', { style: 'display:flex;align-items:center;gap:22px;flex:1;flex-wrap:wrap;' });\n  const donutBox = h('div', { class: 'chart-box', style: 'position:relative;max-width:168px;flex:0 0 auto;', html: donutSVG(segs, { size: 168, stroke: 24 }) });\n  const centerLabel = h('div', { style: 'position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;pointer-events:none;' },\n    h('div', { style: 'font-family:var(--f-display);font-size:1.9rem;font-weight:700;' }, String(DATA.stats.findingsTotal)),\n    h('div', { style: 'font-size:10.5px;color:var(--muted);font-family:var(--f-mono);text-transform:uppercase;letter-spacing:.06em;' }, 'total')\n  );\n  donutBox.appendChild(centerLabel);\n  chartRow.appendChild(donutBox);\n  const legend = h('div', { style: 'display:flex;flex-direction:column;gap:10px;flex:1 1 140px;min-width:0;' });\n  segs.forEach((sgm) => {\n    legend.appendChild(h('div', { style: 'display:flex;align-items:center;gap:9px;font-size:13px;' },\n      h('span', { style: `width:10px;height:10px;border-radius:3px;background:${sgm.color};flex-shrink:0;` }),\n      h('span', { style: 'flex:1;color:var(--ink-soft);' }, sgm.label),\n      h('span', { class: 'mono', style: 'font-weight:700;' }, sgm.value)\n    ));\n  });\n  chartRow.appendChild(legend);\n  card.appendChild(chartRow);\n  card.appendChild(h('a', { class: 'small-link', style: 'margin-top:16px;', href: '#\/security' }, 'Filter findings by severity', icon('arrow-right', 12)));\n  return card;\n}\n\nfunction renderAudienceTiles() {\n  const tiles = [\n    { role: 'Developers', icon: 'terminal', desc: 'Trace a feature through the dispatcher, then check the fix for your module.', links: [['Technical Architecture', 'technical'], ['Functional Spec', 'functional']] },\n    { role: 'Testers & QA', icon: 'search-check', desc: 'Zero automated tests today \u2014 start with lifecycle coverage and open findings.', links: [['Analytics & Code Health', 'analytics'], ['Security Audit', 'security']] },\n    { role: 'Sr. Developers', icon: 'cpu', desc: 'Architecture, data model drift, and the 34 findings with proposed fixes.', links: [['Security Audit', 'security'], ['Technical Architecture', 'technical']] },\n    { role: 'Directors & Advisors', icon: 'briefcase', desc: 'Business value, risk in plain terms, and what needs a decision.', links: [['Business Overview', 'business'], ['Open Questions', 'questions']] }\n  ];\n  const grid = h('div', { class: 'grid grid-4' });\n  tiles.forEach((t) => {\n    const card = h('div', { class: 'card card-pad', style: 'display:flex;flex-direction:column;gap:10px;' },\n      h('div', { class: 'doc-card .ico', style: 'width:36px;height:36px;border-radius:9px;background:var(--surface-2);color:var(--brand);display:flex;align-items:center;justify-content:center;' }, icon(t.icon, 17)),\n      h('h4', { style: 'font-size:14px;font-weight:700;margin-top:2px;' }, t.role),\n      h('p', { style: 'font-size:12.5px;color:var(--muted);line-height:1.55;flex:1;' }, t.desc),\n      h('div', { style: 'display:flex;flex-direction:column;gap:6px;margin-top:4px;' },\n        ...t.links.map(([label, id]) => h('a', { class: 'small-link', href: '#\/' + id }, label, icon('arrow-right', 11)))\n      )\n    );\n    grid.appendChild(card);\n  });\n  return grid;\n}\n\nfunction renderDocCardsGrid() {\n  const grid = h('div', { class: 'grid grid-3' });\n  DATA.docCards.forEach((d) => {\n    const card = h('a', { class: 'card doc-card bracket-card', href: '#\/' + d.id },\n      h('div', { class: 'top-row' },\n        h('span', { class: 'num' }, 'DOC ' + d.num),\n        h('span', { class: 'ico' }, icon(d.icon, 18))\n      ),\n      h('h3', {}, d.title),\n      h('p', {}, d.desc),\n      h('div', { class: 'foot' },\n        h('span', { class: 'stat' }, d.stat),\n        h('span', { class: 'go' }, 'Open', icon('arrow-right', 12))\n      )\n    );\n    grid.appendChild(card);\n  });\n  return grid;\n}\n\/* -------------------------------------------------------------------------\n   PAGE: 00 System Overview\n   ------------------------------------------------------------------------- *\/\nfunction renderOverview(root) {\n  const d = DATA.overview;\n  root.appendChild(pageHeader('00', 'System Overview', 'What the platform is, who runs it, and the one architectural idea to understand before anything else.'));\n\n  root.appendChild(h('p', { class: 'lede' }, d.intro));\n\n  root.appendChild(sectionTitle('Deployable pieces'));\n  const stackGrid = h('div', { class: 'grid grid-3' });\n  d.stack.forEach((s) => {\n    stackGrid.appendChild(h('div', { class: 'card card-pad' },\n      h('div', { style: 'display:flex;align-items:center;gap:10px;margin-bottom:10px;' },\n        h('span', { style: 'width:34px;height:34px;border-radius:9px;background:var(--surface-2);color:var(--brand);display:flex;align-items:center;justify-content:center;flex-shrink:0;' }, icon(s.icon, 17)),\n        h('h4', { style: 'font-size:14.5px;font-weight:700;' }, s.name)\n      ),\n      h('p', { class: 'mono', style: 'font-size:11.5px;color:var(--muted);line-height:1.5;margin-bottom:8px;' }, s.tech),\n      h('p', { style: 'font-size:13px;color:var(--ink-soft);line-height:1.5;' }, s.role)\n    ));\n  });\n  root.appendChild(stackGrid);\n\n  root.appendChild(sectionTitle('Who uses it'));\n  const userGrid = h('div', { class: 'grid grid-3' });\n  d.users.forEach((u) => {\n    userGrid.appendChild(h('div', { class: 'card persona-card' },\n      h('div', { class: 'ico' }, icon(u.icon, 19)),\n      h('div', {}, h('h4', {}, u.name), h('p', {}, u.desc))\n    ));\n  });\n  root.appendChild(userGrid);\n\n  root.appendChild(sectionTitle('The core value proposition'));\n  root.appendChild(h('div', { class: 'callout info' }, icon('target', 19), h('p', {}, d.valueProp)));\n\n  root.appendChild(sectionTitle('High-level architecture'));\n  root.appendChild(h('div', { class: 'fig-label' }, 'FIG. 03 \u2014 REQUEST & DATA FLOW'));\n  root.appendChild(renderArchDiagram(d.archFlow));\n\n  root.appendChild(sectionTitle('The one thing to understand first: the dynamic dispatcher'));\n  root.appendChild(h('div', { class: 'card card-pad' },\n    h('p', { class: 'body-text' }, d.dispatcher),\n    h('div', { class: 'codebox', style: 'margin-top:16px;' }, codeBox(['GET  | POST | PUT | DELETE   \/api\/v1\/exec\/:module\/:ctrl\/:action\/:_id?']))\n  ));\n\n  root.appendChild(sectionTitle('Two-column: backend & frontend stack'));\n  const colWrap = h('div', { class: 'grid grid-2' });\n  colWrap.appendChild(h('div', { class: 'card card-pad' },\n    h('h4', { class: 'h3', style: 'margin-bottom:12px;' }, 'Backend'),\n    bulletList(d.backendStack)\n  ));\n  colWrap.appendChild(h('div', { class: 'card card-pad' },\n    h('h4', { class: 'h3', style: 'margin-bottom:12px;' }, 'Frontend'),\n    bulletList(d.frontendStack)\n  ));\n  root.appendChild(colWrap);\n\n  root.appendChild(sectionTitle('What a new engineer must know on day one'));\n  const dayOneWrap = h('div', { style: 'display:flex;flex-direction:column;gap:12px;' });\n  d.dayOne.forEach((item, i) => {\n    const cls = item.flag === 'critical' ? 'danger' : item.flag === 'warn' ? 'warn' : 'info';\n    const ic = item.flag === 'critical' ? 'alert-triangle' : 'info';\n    dayOneWrap.appendChild(h('div', { class: 'callout ' + cls },\n      icon(ic, 19),\n      h('p', {}, h('strong', {}, (i + 1) + '. ' + item.t + ' '), item.d)\n    ));\n  });\n  root.appendChild(dayOneWrap);\n}\n\nfunction renderArchDiagram(flow) {\n  const cols = { client: [], edge: [], backend: [], data: [] };\n  flow.forEach((f) => cols[f.col].push(f));\n  const colTitles = { client: 'Client', edge: 'Edge', backend: 'Backend (PM2)', data: 'Data & external' };\n  const wrap = h('div', { class: 'card card-pad' });\n  const grid = h('div', { class: 'grid grid-arch', style: 'gap:16px;' });\n  Object.keys(cols).forEach((k) => {\n    const colEl = h('div', {},\n      h('div', { class: 'mono', style: 'font-size:10.5px;text-transform:uppercase;letter-spacing:.08em;color:var(--muted);margin-bottom:10px;text-align:center;' }, colTitles[k]),\n      h('div', { style: 'display:flex;flex-direction:column;gap:10px;' })\n    );\n    const stackEl = colEl.lastChild;\n    cols[k].forEach((node) => {\n      stackEl.appendChild(h('div', { style: 'border:1px solid var(--line-strong);border-radius:9px;padding:10px 12px;background:var(--surface-2);text-align:center;' },\n        h('div', { style: 'font-size:12.5px;font-weight:700;' }, node.label),\n        h('div', { class: 'mono', style: 'font-size:10px;color:var(--muted);margin-top:2px;' }, node.sub)\n      ));\n    });\n    grid.appendChild(colEl);\n  });\n  wrap.appendChild(grid);\n  wrap.appendChild(h('p', { class: 'muted', style: 'font-size:11.5px;margin-top:16px;text-align:center;' },\n    'Client and Agent authenticate independently \\u2192 requests pass through the edge to the Express API \\u2192 the dynamic dispatcher resolves the module\/controller\/action \\u2192 data stores. The search worker runs alongside, driven by MongoDB change streams.'));\n  return wrap;\n}\n\nfunction codeBox(lines) {\n  const pre = document.createElement('pre');\n  pre.textContent = lines.join('\\n');\n  return pre;\n}\nfunction renderCodeBox(lines) {\n  const box = h('div', { class: 'codebox' });\n  const btn = h('button', { class: 'copybtn', onclick: (e) => copyCode(lines.join('\\n'), e.target) }, 'Copy');\n  box.appendChild(btn);\n  box.appendChild(codeBox(lines));\n  return box;\n}\nfunction copyCode(text, btn) {\n  navigator.clipboard?.writeText(text).then(() => {\n    const old = btn.textContent;\n    btn.textContent = 'Copied';\n    btn.classList.add('copied');\n    setTimeout(() => { btn.textContent = old; btn.classList.remove('copied'); }, 1400);\n  }).catch(() => {});\n}\n\n\/* Shared small helpers for page composition *\/\nfunction pageHeader(num, title, sub) {\n  return h('div', { style: 'margin-bottom:22px;' },\n    h('div', { class: 'eyebrow' }, 'Document ' + num),\n    h('h1', { class: 'h1', style: 'margin-top:10px;' }, title),\n    sub ? h('p', { class: 'lede', style: 'margin-top:10px;' }, sub) : null\n  );\n}\nfunction sectionTitle(t, sub) {\n  return h('div', { class: 'section-gap', style: 'margin-bottom:16px;' },\n    h('h2', { class: 'h2' }, t),\n    sub ? h('p', { class: 'muted', style: 'font-size:13px;margin-top:4px;' }, sub) : null\n  );\n}\nfunction bulletList(items) {\n  const ul = h('ul', { style: 'display:flex;flex-direction:column;gap:9px;' });\n  items.forEach((t) => {\n    ul.appendChild(h('li', { style: 'display:flex;gap:9px;font-size:13.2px;line-height:1.55;color:var(--ink-soft);' },\n      h('span', { style: 'color:var(--brand);flex-shrink:0;margin-top:1px;' }, icon('chevron-right', 13)),\n      h('span', { html: linkify(t) })\n    ));\n  });\n  return ul;\n}\nfunction linkify(t) { return esc(t); }\n\n\/* -------------------------------------------------------------------------\n   PAGE: 01 Business Overview\n   ------------------------------------------------------------------------- *\/\nfunction renderBusiness(root) {\n  const d = DATA.business;\n  root.appendChild(pageHeader('01', 'Business Overview', 'The problem it solves, who it is for, and how value moves through the system.'));\n  root.appendChild(h('p', { class: 'lede' }, d.problem));\n\n  root.appendChild(sectionTitle('Target users & personas'));\n  const pGrid = h('div', { class: 'grid grid-4' });\n  d.personas.forEach((p) => {\n    pGrid.appendChild(h('div', { class: 'card persona-card', style: 'flex-direction:column;text-align:left;' },\n      h('div', { class: 'ico' }, icon(p.icon, 18)),\n      h('div', {}, h('h4', { style: 'margin-top:8px;' }, p.name), h('p', {}, p.what))\n    ));\n  });\n  root.appendChild(pGrid);\n\n  root.appendChild(sectionTitle('The ITAM value proposition'));\n  const vGrid = h('div', { class: 'grid grid-3' });\n  d.valueProps.forEach((v) => {\n    vGrid.appendChild(h('div', { class: 'card card-pad bracket-card' },\n      h('span', { style: 'width:36px;height:36px;border-radius:10px;background:var(--surface-2);color:var(--brand);display:flex;align-items:center;justify-content:center;margin-bottom:12px;' }, icon(v.icon, 18)),\n      h('h4', { style: 'font-size:14.5px;font-weight:700;margin-bottom:6px;' }, v.t),\n      h('p', { style: 'font-size:13px;color:var(--muted);line-height:1.55;' }, v.d)\n    ));\n  });\n  root.appendChild(vGrid);\n\n  root.appendChild(sectionTitle('Core business capabilities', 'What is actually implemented today, as evidenced in the code.'));\n  root.appendChild(capabilitiesTable(d.capabilities));\n\n  root.appendChild(sectionTitle('Key business workflows'));\n  const wfWrap = h('div', { class: 'grid grid-2' });\n  d.workflows.forEach((wf) => {\n    const card = h('div', { class: 'card card-pad' },\n      h('h4', { class: 'h3', style: 'margin-bottom:14px;' }, wf.t),\n      h('div', { class: 'journey' })\n    );\n    const journeyEl = card.lastChild;\n    wf.steps.forEach((st, i) => {\n      journeyEl.appendChild(h('div', { class: 'jstep' },\n        h('span', { class: 'num' }, i + 1),\n        h('p', {}, st)\n      ));\n    });\n    wfWrap.appendChild(card);\n  });\n  root.appendChild(wfWrap);\n\n  root.appendChild(sectionTitle('Business risks worth leadership attention'));\n  const riskWrap = h('div', { style: 'display:flex;flex-direction:column;gap:12px;' });\n  d.risks.forEach((r) => {\n    const cls = r.level === 'danger' ? 'danger' : r.level === 'warn' ? 'warn' : 'info';\n    const ic = r.level === 'danger' ? 'alert-triangle' : r.level === 'warn' ? 'alert-triangle' : 'info';\n    riskWrap.appendChild(h('div', { class: 'callout ' + cls }, icon(ic, 19), h('p', {}, h('strong', {}, r.t + '. '), r.d)));\n  });\n  root.appendChild(riskWrap);\n}\n\nfunction capabilitiesTable(rows) {\n  const wrap = h('div', { class: 'tbl-wrap' });\n  const table = h('table', { class: 'dtbl' });\n  table.appendChild(h('thead', {}, h('tr', {}, h('th', {}, 'Capability'), h('th', {}, 'Status'), h('th', {}, 'Notes'))));\n  const tbody = h('tbody');\n  rows.forEach(([cap, status, note]) => {\n    const label = { implemented: 'Implemented', partial: 'Partial', stub: 'Stub only', gated: 'Flag-gated' }[status];\n    tbody.appendChild(h('tr', {},\n      h('td', {}, h('strong', {}, cap)),\n      h('td', {}, h('span', { class: 'status-pill ' + status }, label)),\n      h('td', { class: 'muted' }, note || '\\u2014')\n    ));\n  });\n  table.appendChild(tbody);\n  wrap.appendChild(table);\n  return wrap;\n}\n\/* -------------------------------------------------------------------------\n   PAGE: 02 Functional Specification\n   ------------------------------------------------------------------------- *\/\nlet activeModuleTab = 'core';\n\nfunction renderFunctional(root, deep) {\n  const d = DATA.functional;\n  if (deep && d.modules.some(m => m.id === deep)) activeModuleTab = deep;\n\n  root.appendChild(pageHeader('02', 'Functional Specification', 'Every feature by module, full lifecycle coverage, the real permission model, and how the three key journeys actually run.'));\n  root.appendChild(h('div', { class: 'callout info' }, icon('info', 18), h('p', {}, d.intro)));\n\n  root.appendChild(sectionTitle('Module explorer'));\n  const moduleWrap = h('div', {});\n  const tabbar = h('div', { class: 'tabbar' });\n  const panelHost = h('div', { id: 'modulePanel' });\n  d.modules.forEach((m) => {\n    const btn = h('button', { class: 'tabbtn' + (m.id === activeModuleTab ? ' active' : ''), onclick: () => { activeModuleTab = m.id; renderModulePanel(panelHost, m); qsa('.tabbtn', tabbar).forEach(b => b.classList.remove('active')); btn.classList.add('active'); } }, m.name);\n    tabbar.appendChild(btn);\n  });\n  moduleWrap.appendChild(tabbar);\n  moduleWrap.appendChild(panelHost);\n  root.appendChild(moduleWrap);\n  const activeModule = d.modules.find(m => m.id === activeModuleTab) || d.modules[0];\n  renderModulePanel(panelHost, activeModule);\n\n  root.appendChild(sectionTitle('Asset lifecycle coverage'));\n  root.appendChild(renderLifecycleTrack(d.lifecycle));\n\n  root.appendChild(sectionTitle('Roles & permissions', 'RBAC is data-driven, and enforced only when FEATURE_RBAC=ON.'));\n  root.appendChild(h('p', { class: 'body-text' }, d.permissions.intro));\n  root.appendChild(h('div', { style: 'margin-top:14px;' }, permissionsTable(d.permissions.matrix)));\n  root.appendChild(h('div', { class: 'callout danger', style: 'margin-top:14px;' }, icon('alert-triangle', 19), h('p', {}, d.permissions.warn)));\n\n  root.appendChild(sectionTitle('Integrations & data flow'));\n  root.appendChild(integrationsTable(d.integrations));\n\n  root.appendChild(sectionTitle('End-to-end user journeys'));\n  const jWrap = h('div', { style: 'display:flex;flex-direction:column;gap:16px;' });\n  d.journeys.forEach((j, idx) => {\n    const card = h('div', { class: 'card card-pad' },\n      h('div', { style: 'display:flex;align-items:center;gap:10px;margin-bottom:14px;' },\n        h('span', { class: 'badge brand' }, 'Journey ' + String.fromCharCode(65 + idx)),\n        h('h4', { class: 'h3' }, j.t)\n      ),\n      h('div', { class: 'journey' })\n    );\n    const journeyEl = card.lastChild;\n    j.steps.forEach((st, i) => journeyEl.appendChild(h('div', { class: 'jstep' }, h('span', { class: 'num' }, i + 1), h('p', {}, st))));\n    jWrap.appendChild(card);\n  });\n  root.appendChild(jWrap);\n}\n\nfunction renderModulePanel(host, m) {\n  host.innerHTML = '';\n  const card = h('div', { class: 'card card-pad' });\n  card.appendChild(h('div', { style: 'display:flex;align-items:center;gap:12px;margin-bottom:8px;' },\n    h('span', { style: 'width:38px;height:38px;border-radius:10px;background:var(--surface-2);color:var(--brand);display:flex;align-items:center;justify-content:center;' }, icon(m.icon, 19)),\n    h('div', {}, h('h3', { style: 'font-size:16px;' }, m.name), h('div', { class: 'muted', style: 'font-size:12px;' }, m.full))\n  ));\n  card.appendChild(h('p', { class: 'body-text', style: 'margin:12px 0 18px;' }, m.blurb));\n\n  const wrap = h('div', { class: 'tbl-wrap' });\n  const table = h('table', { class: 'dtbl' });\n  table.appendChild(h('thead', {}, h('tr', {}, h('th', {}, 'Feature'), h('th', {}, 'Controller'), h('th', {}, 'Notable actions'))));\n  const tbody = h('tbody');\n  m.features.forEach((f) => {\n    tbody.appendChild(h('tr', {}, h('td', {}, h('strong', {}, f[0])), h('td', { class: 'mono' }, f[1]), h('td', { class: 'muted' }, f[2])));\n  });\n  table.appendChild(tbody);\n  wrap.appendChild(table);\n  card.appendChild(wrap);\n\n  if (m.note) card.appendChild(h('div', { class: 'callout warn', style: 'margin-top:16px;' }, icon('info', 18), h('p', {}, m.note)));\n  host.appendChild(card);\n}\n\nfunction renderLifecycleTrack(stages) {\n  const wrap = h('div', { class: 'card card-pad' });\n  const track = h('div', { class: 'lifecycle-track' });\n  stages.forEach((s, i) => {\n    track.appendChild(h('div', { class: 'lifecycle-step ' + s.status },\n      h('div', { class: 'n' }, String(i + 1).padStart(2, '0')),\n      h('div', { class: 't' }, s.t),\n      h('div', { class: 'bar' })\n    ));\n  });\n  wrap.appendChild(track);\n  wrap.appendChild(h('div', { class: 'legend', style: 'margin-top:16px;' },\n    h('div', { class: 'li' }, h('span', { class: 'sw', style: 'background:var(--ok);' }), 'Implemented'),\n    h('div', { class: 'li' }, h('span', { class: 'sw', style: 'background:var(--crit);' }), 'Absent \/ stub')\n  ));\n  return wrap;\n}\n\nfunction permissionsTable(rows) {\n  const wrap = h('div', { class: 'tbl-wrap' });\n  const table = h('table', { class: 'dtbl' });\n  table.appendChild(h('thead', {}, h('tr', {}, h('th', {}, 'Principal'), h('th', {}, 'Capability'))));\n  const tbody = h('tbody');\n  rows.forEach(([principal, cap]) => tbody.appendChild(h('tr', {}, h('td', {}, h('strong', {}, principal)), h('td', { class: 'muted' }, cap))));\n  table.appendChild(tbody);\n  wrap.appendChild(table);\n  return wrap;\n}\n\nfunction integrationsTable(rows) {\n  const wrap = h('div', { class: 'tbl-wrap' });\n  const table = h('table', { class: 'dtbl' });\n  table.appendChild(h('thead', {}, h('tr', {}, h('th', {}, 'Integration'), h('th', {}, 'Direction'))));\n  const tbody = h('tbody');\n  const dirIcon = { inbound: 'download', outbound: 'external-link', internal: 'shuffle', external: 'external-link' };\n  rows.forEach(([name, dir]) => {\n    tbody.appendChild(h('tr', {},\n      h('td', {}, h('strong', {}, name)),\n      h('td', {}, h('span', { class: 'badge neutral' }, icon(dirIcon[dir] || 'shuffle', 11), ' ' + dir))\n    ));\n  });\n  table.appendChild(tbody);\n  wrap.appendChild(table);\n  return wrap;\n}\n\/* -------------------------------------------------------------------------\n   PAGE: 03 Technical Architecture\n   ------------------------------------------------------------------------- *\/\nfunction renderTechnical(root) {\n  const d = DATA.technical;\n  root.appendChild(pageHeader('03', 'Technical Architecture', 'Request lifecycle, authentication, the data model, and the full API surface.'));\n  root.appendChild(h('p', { class: 'lede' }, d.intro));\n\n  root.appendChild(sectionTitle('Layers'));\n  root.appendChild(layersTable(d.layers));\n\n  root.appendChild(sectionTitle('Middleware pipeline', 'Executed in order for every request under \/api.'));\n  root.appendChild(h('div', { class: 'card card-pad' }, numberedMiniList(d.middleware)));\n\n  root.appendChild(sectionTitle('Request lifecycle', 'FIG. 04 \u2014 a single POST \/exec call, end to end.'));\n  root.appendChild(renderSequence(d.dispatchFlow));\n\n  root.appendChild(sectionTitle('Authentication & session model'));\n  const authGrid = h('div', { class: 'grid grid-2' });\n  authGrid.appendChild(h('div', { class: 'card card-pad' }, h('h4', { class: 'h3', style: 'margin-bottom:12px;' }, 'Tokens'), bulletList(d.auth.tokens)));\n  authGrid.appendChild(h('div', { class: 'card card-pad' }, h('h4', { class: 'h3', style: 'margin-bottom:12px;' }, 'attachUser'), bulletList(d.auth.attachUser)));\n  root.appendChild(authGrid);\n\n  root.appendChild(h('div', { class: 'section-gap-sm' }, h('h4', { class: 'h3', style: 'margin-bottom:12px;' }, 'Login & account flows')));\n  root.appendChild(flowsTable(d.auth.flows));\n\n  root.appendChild(h('div', { class: 'section-gap-sm' }, h('h4', { class: 'h3', style: 'margin-bottom:12px;' }, 'checkAccess evaluation order')));\n  root.appendChild(h('div', { class: 'card card-pad' }, numberedMiniList(d.auth.rbacSteps)));\n  root.appendChild(h('div', { class: 'callout danger', style: 'margin-top:14px;' }, icon('alert-triangle', 19), h('p', {}, d.auth.rbacNote)));\n\n  root.appendChild(sectionTitle('Configuration & environments'));\n  root.appendChild(h('div', { class: 'card card-pad' }, bulletList(d.config)));\n\n  root.appendChild(sectionTitle('Background jobs, workers & caching'));\n  const jobsWrap = h('div', { style: 'display:flex;flex-direction:column;gap:12px;' });\n  d.jobs.forEach((j) => jobsWrap.appendChild(h('div', { class: 'card card-pad' }, h('p', { class: 'body-text' }, j))));\n  root.appendChild(jobsWrap);\n\n  root.appendChild(sectionTitle('Data model', '145 collections across 5 families, reconstructed from the Mongoose models and a live export.'));\n  root.appendChild(h('p', { class: 'body-text' }, d.dataModel.intro));\n  root.appendChild(h('div', { style: 'margin-top:16px;' }, collectionBars(d.dataModel.families)));\n  root.appendChild(h('div', { class: 'callout warn', style: 'margin-top:14px;' }, icon('alert-triangle', 19), h('p', {}, d.dataModel.drift)));\n\n  root.appendChild(h('div', { class: 'section-gap-sm' }, h('h4', { class: 'h3', style: 'margin-bottom:12px;' }, 'Key entities')));\n  const entGrid = h('div', { class: 'grid grid-2' });\n  d.dataModel.entities.forEach((e) => {\n    entGrid.appendChild(h('div', { class: 'card card-pad' },\n      h('h4', { style: 'font-size:13.5px;font-weight:700;margin-bottom:6px;color:var(--brand-2);' }, e.name),\n      h('p', { style: 'font-size:12.8px;color:var(--muted);line-height:1.55;' }, e.detail)\n    ));\n  });\n  root.appendChild(entGrid);\n\n  root.appendChild(h('div', { class: 'section-gap-sm' }, h('h4', { class: 'h3', style: 'margin-bottom:12px;' }, 'Data-integrity observations')));\n  root.appendChild(h('div', { class: 'card card-pad' }, bulletList(d.dataModel.integrity)));\n\n  root.appendChild(sectionTitle('API surface', 'The dynamic dispatcher means real endpoints are (module, ctrl, action) triples \u2014 see the Functional Spec for the per-module inventory. These are the static routes.'));\n  root.appendChild(apiSurfaceTable(d.apiSurface));\n\n  root.appendChild(sectionTitle('Key libraries'));\n  root.appendChild(h('div', { class: 'card card-pad' }, h('p', { class: 'body-text' }, d.libraries)));\n}\n\nfunction layersTable(layers) {\n  const wrap = h('div', { class: 'tbl-wrap' });\n  const table = h('table', { class: 'dtbl' });\n  table.appendChild(h('thead', {}, h('tr', {}, h('th', {}, 'Layer'), h('th', {}, 'Location'), h('th', {}, 'Responsibility'))));\n  const tbody = h('tbody');\n  layers.forEach((l) => tbody.appendChild(h('tr', {}, h('td', {}, h('strong', {}, l.name)), h('td', { class: 'mono' }, l.loc), h('td', { class: 'muted' }, l.role))));\n  table.appendChild(tbody);\n  wrap.appendChild(table);\n  return wrap;\n}\n\nfunction numberedMiniList(items) {\n  const wrap = h('div', { style: 'display:flex;flex-direction:column;gap:10px;' });\n  items.forEach((t, i) => {\n    wrap.appendChild(h('div', { style: 'display:flex;gap:12px;align-items:flex-start;' },\n      h('span', { class: 'mono', style: 'color:var(--brand);font-weight:700;font-size:12px;flex-shrink:0;width:20px;' }, String(i + 1).padStart(2, '0')),\n      h('p', { style: 'font-size:13.2px;line-height:1.6;color:var(--ink-soft);' }, t)\n    ));\n  });\n  return wrap;\n}\n\nfunction renderSequence(steps) {\n  const wrap = h('div', { class: 'card card-pad' });\n  const track = h('div', { style: 'display:flex;flex-direction:column;' });\n  steps.forEach((s, i) => {\n    const row = h('div', { style: 'display:flex;gap:16px;align-items:flex-start;position:relative;padding-bottom:' + (i === steps.length - 1 ? '0' : '22px') });\n    if (i !== steps.length - 1) {\n      row.appendChild(h('div', { style: 'position:absolute;left:15px;top:32px;bottom:0;width:1px;background:var(--line-strong);' }));\n    }\n    row.appendChild(h('span', { style: 'width:32px;height:32px;border-radius:9px;background:var(--surface-2);border:1px solid var(--line-strong);display:flex;align-items:center;justify-content:center;font-family:var(--f-mono);font-weight:700;font-size:12px;color:var(--brand);flex-shrink:0;z-index:1;' }, i + 1));\n    row.appendChild(h('div', { style:'padding-top:3px;' },\n      h('div', { style: 'font-weight:700;font-size:13.5px;' }, s.a, s.b ? h('span', { class: 'mono', style: 'font-weight:500;color:var(--brand-2);margin-left:8px;font-size:12px;' }, s.b) : null),\n      s.d ? h('div', { class: 'muted', style: 'font-size:12.3px;margin-top:2px;' }, s.d) : null\n    ));\n    track.appendChild(row);\n  });\n  wrap.appendChild(track);\n  return wrap;\n}\n\nfunction flowsTable(rows) {\n  const wrap = h('div', { class: 'tbl-wrap' });\n  const table = h('table', { class: 'dtbl' });\n  table.appendChild(h('thead', {}, h('tr', {}, h('th', {}, 'Endpoint'), h('th', {}, 'Behavior'))));\n  const tbody = h('tbody');\n  rows.forEach(([ep, behavior]) => tbody.appendChild(h('tr', {}, h('td', { class: 'mono', style: 'white-space:nowrap;' }, ep), h('td', { class: 'muted' }, behavior))));\n  table.appendChild(tbody);\n  wrap.appendChild(table);\n  return wrap;\n}\n\nfunction collectionBars(families) {\n  const max = Math.max(...families.map(f => f.count));\n  const wrap = h('div', { class: 'card card-pad' });\n  const list = h('div', { class: 'barlist' });\n  families.forEach((f) => {\n    list.appendChild(h('div', { class: 'barrow' },\n      h('div', { class: 'lbl mono' }, f.prefix),\n      h('div', { class: 'track' }, h('div', { class: 'fill', 'data-width': (f.count \/ max * 100) + '%' })),\n      h('div', { class: 'val' }, f.count)\n    ));\n  });\n  wrap.appendChild(list);\n  wrap.appendChild(h('div', { style: 'margin-top:16px;' },\n    ...families.map(f => h('div', { style: 'font-size:12px;color:var(--muted);margin-top:6px;' }, h('strong', { style: 'color:var(--ink-soft);' }, f.prefix + ' '), f.domain))\n  ));\n  requestAnimationFrame(() => animateBars(wrap));\n  return wrap;\n}\n\nfunction apiSurfaceTable(rows) {\n  const wrap = h('div', { class: 'tbl-wrap' });\n  const table = h('table', { class: 'dtbl' });\n  table.appendChild(h('thead', {}, h('tr', {}, h('th', {}, 'Route'), h('th', {}, 'Auth'), h('th', {}, 'Purpose'))));\n  const tbody = h('tbody');\n  rows.forEach(([route, auth, purpose]) => {\n    tbody.appendChild(h('tr', {},\n      h('td', { class: 'mono', style: 'font-size:11.8px;' }, route),\n      h('td', {}, authBadge(auth)),\n      h('td', { class: 'muted' }, purpose)\n    ));\n  });\n  table.appendChild(tbody);\n  wrap.appendChild(table);\n  return wrap;\n}\nfunction authBadge(auth) {\n  const isPublic = \/public|none\/i.test(auth) && !\/mixed\/i.test(auth);\n  const isMixed = \/mixed\/i.test(auth);\n  const cls = isPublic ? 'crit' : isMixed ? 'high' : 'ok';\n  return h('span', { class: 'badge ' + cls, style: 'white-space:normal;' }, auth);\n}\n\/* -------------------------------------------------------------------------\n   PAGE: 04 Analytics & Code Health\n   ------------------------------------------------------------------------- *\/\nfunction renderAnalytics(root) {\n  const d = DATA.analytics;\n  root.appendChild(pageHeader('04', 'Analytics & Code Health', 'Reporting and KPIs, plus the honest engineering quality signals behind the platform.'));\n\n  root.appendChild(sectionTitle('Scale'));\n  const scaleWrap = h('div', { class: 'grid grid-3' });\n  scaleWrap.appendChild(scaleCard('Backend', d.scale.backendFiles, d.scale.backendLOC, 'server'));\n  scaleWrap.appendChild(scaleCard('Frontend', d.scale.frontendFiles, d.scale.frontendLOC, 'monitor'));\n  scaleWrap.appendChild(h('div', { class: 'card card-pad', style: 'display:flex;flex-direction:column;justify-content:center;align-items:center;text-align:center;background:var(--ink);border-color:var(--ink);' },\n    h('div', { class: 'mono', style: 'font-size:11px;letter-spacing:.08em;text-transform:uppercase;color:#8FA6D9;' }, 'Combined'),\n    h('div', { style: 'font-family:var(--f-display);font-size:2.3rem;font-weight:700;color:#fff;margin-top:6px;' }, fmtK(d.scale.totalLOC)),\n    h('div', { style: 'color:#B9CBEE;font-size:12px;margin-top:2px;' }, 'lines of code')\n  ));\n  root.appendChild(scaleWrap);\n\n  root.appendChild(sectionTitle('Code health signals', 'Evidence-based, not estimated.'));\n  const hGrid = h('div', { class: 'grid grid-3' });\n  d.healthCards.forEach((c) => {\n    const flagColor = c.flag === 'critical' ? 'var(--crit)' : c.flag === 'warn' ? 'var(--high)' : 'var(--muted)';\n    hGrid.appendChild(h('div', { class: 'stat-card' },\n      h('div', { class: 'top' },\n        h('span', { class: 'k' }, c.k),\n        h('span', { class: 'ico', style: `background:color-mix(in srgb, ${flagColor} 15%, transparent); color:${flagColor};` }, icon(c.icon, 15))\n      ),\n      h('div', { class: 'v', style: `color:${c.flag === 'critical' ? 'var(--crit)' : 'var(--ink)'};` }, c.v),\n      h('div', { class: 'd' }, c.d)\n    ));\n  });\n  root.appendChild(hGrid);\n\n  root.appendChild(sectionTitle('Reporting & dashboards', 'Report views are code-defined aggregation modules \u2014 one file, one findEntries() function, one fixed pipeline.'));\n  root.appendChild(reportViewsBars(d.reportViews));\n  const dashWrap = h('div', { class: 'grid grid-2', style: 'margin-top:16px;' });\n  d.dashboards.forEach((dd) => dashWrap.appendChild(h('div', { class: 'card card-pad' }, h('h4', { style: 'font-size:13.5px;font-weight:700;margin-bottom:6px;' }, dd.t), h('p', { style: 'font-size:12.8px;color:var(--muted);line-height:1.55;' }, dd.d))));\n  root.appendChild(dashWrap);\n\n  root.appendChild(h('div', { class: 'section-gap-sm' }, h('h4', { class: 'h3', style: 'margin-bottom:12px;' }, 'Data sources behind the metrics')));\n  root.appendChild(dataSourcesTable(d.dataSources));\n  root.appendChild(h('div', { class: 'callout warn', style: 'margin-top:14px;' }, icon('alert-triangle', 19), h('p', {}, d.kpiNote)));\n\n  root.appendChild(sectionTitle('Complexity hotspots'));\n  root.appendChild(hotspotsTable(d.hotspots));\n\n  root.appendChild(sectionTitle('Schema & database drift'));\n  root.appendChild(h('div', { class: 'card card-pad' }, bulletList(d.driftNotes)));\n\n  const twoCol = h('div', { class: 'grid grid-2 section-gap' });\n  twoCol.appendChild(h('div', { class: 'card card-pad' },\n    h('div', { style: 'display:flex;align-items:center;gap:8px;margin-bottom:12px;' }, icon('check-circle', 18), h('h4', { class: 'h3' }, 'Positive signals')),\n    bulletList(d.positives)\n  ));\n  twoCol.appendChild(h('div', { class: 'card card-pad' },\n    h('div', { style: 'display:flex;align-items:center;gap:8px;margin-bottom:12px;' }, icon('target', 18), h('h4', { class: 'h3' }, 'Recommended next steps')),\n    numberedMiniList(d.nextSteps)\n  ));\n  root.appendChild(twoCol);\n}\n\nfunction scaleCard(name, files, loc, ic) {\n  return h('div', { class: 'card card-pad' },\n    h('div', { style: 'display:flex;align-items:center;gap:9px;margin-bottom:10px;' },\n      h('span', { style: 'width:30px;height:30px;border-radius:8px;background:var(--surface-2);color:var(--brand);display:flex;align-items:center;justify-content:center;' }, icon(ic, 15)),\n      h('span', { style: 'font-weight:700;font-size:13.5px;' }, name)\n    ),\n    h('div', { style: 'font-family:var(--f-display);font-size:1.9rem;font-weight:700;' }, fmtK(loc)),\n    h('div', { class: 'muted', style: 'font-size:12px;margin-top:1px;' }, 'lines across ' + fmtNum(files) + ' files')\n  );\n}\n\nfunction reportViewsBars(rows) {\n  const max = Math.max(...rows.map(r => r.count));\n  const wrap = h('div', { class: 'card card-pad' });\n  const list = h('div', { class: 'barlist' });\n  rows.forEach((r) => {\n    list.appendChild(h('div', { class: 'barrow' },\n      h('div', { class: 'lbl' }, r.area.length > 26 ? r.area.slice(0, 24) + '\\u2026' : r.area),\n      h('div', { class: 'track' }, h('div', { class: 'fill', 'data-width': (r.count \/ max * 100) + '%' })),\n      h('div', { class: 'val' }, r.count)\n    ));\n  });\n  wrap.appendChild(list);\n  requestAnimationFrame(() => animateBars(wrap));\n  return wrap;\n}\n\nfunction dataSourcesTable(rows) {\n  const wrap = h('div', { class: 'tbl-wrap' });\n  const table = h('table', { class: 'dtbl' });\n  table.appendChild(h('thead', {}, h('tr', {}, h('th', {}, 'Metric family'), h('th', {}, 'Source collections'))));\n  const tbody = h('tbody');\n  rows.forEach(([fam, src]) => tbody.appendChild(h('tr', {}, h('td', {}, h('strong', {}, fam)), h('td', { class: 'mono', style: 'font-size:11.8px;' }, src))));\n  table.appendChild(tbody);\n  wrap.appendChild(table);\n  return wrap;\n}\n\nfunction hotspotsTable(rows) {\n  const wrap = h('div', { class: 'tbl-wrap' });\n  const table = h('table', { class: 'dtbl' });\n  table.appendChild(h('thead', {}, h('tr', {}, h('th', {}, 'File'), h('th', {}, 'Lines'), h('th', {}, 'Note'))));\n  const tbody = h('tbody');\n  rows.forEach((r) => tbody.appendChild(h('tr', {}, h('td', { class: 'mono', style: 'font-size:11.8px;' }, r.file), h('td', {}, r.lines ? fmtNum(r.lines) : '\\u2014'), h('td', { class: 'muted' }, r.note))));\n  table.appendChild(tbody);\n  wrap.appendChild(table);\n  return wrap;\n}\n\/* -------------------------------------------------------------------------\n   PAGE: 05 Security Audit \u2014 findings explorer\n   ------------------------------------------------------------------------- *\/\nlet secFilterSeverity = 'all';\nlet secFilterText = '';\nlet secOpenId = null;\n\nfunction renderSecurity(root, deep) {\n  const d = DATA.security;\n  const sev = DATA.severityCounts;\n  secOpenId = deep || null;\n  if (secOpenId) secFilterSeverity = 'all';\n\n  root.appendChild(pageHeader('05', 'Security Audit', 'Static review of the backend and frontend. No code was modified \u2014 every fix below is a proposal for engineering to evaluate.'));\n\n  \/\/ Severity strip (doubles as filter)\n  root.appendChild(severityStrip(sev));\n\n  \/\/ Threat model (compact)\n  root.appendChild(sectionTitle('Threat model'));\n  const tGrid = h('div', { class: 'grid grid-2' });\n  const boundariesCard = h('div', { class: 'card card-pad' }, h('h4', { class: 'h3', style: 'margin-bottom:12px;' }, 'Trust boundaries'));\n  const bList = h('div', { style: 'display:flex;flex-direction:column;gap:12px;' });\n  d.threatModel.boundaries.forEach((b) => bList.appendChild(h('div', {}, h('div', { style: 'font-weight:700;font-size:13px;margin-bottom:2px;' }, b.t), h('div', { class: 'muted', style: 'font-size:12.3px;line-height:1.5;' }, b.d))));\n  boundariesCard.appendChild(bList);\n  tGrid.appendChild(boundariesCard);\n\n  const rightCol = h('div', { style: 'display:flex;flex-direction:column;gap:16px;' });\n  rightCol.appendChild(h('div', { class: 'card card-pad' }, h('h4', { class: 'h3', style: 'margin-bottom:10px;' }, 'Attack surface'), bulletList(d.threatModel.surface)));\n  rightCol.appendChild(h('div', { class: 'card card-pad' }, h('h4', { class: 'h3', style: 'margin-bottom:10px;' }, 'Sensitive data at risk'), bulletList(d.threatModel.sensitiveData)));\n  tGrid.appendChild(rightCol);\n  root.appendChild(tGrid);\n\n  root.appendChild(h('div', { class: 'section-gap-sm' }, h('h4', { class: 'h3', style: 'margin-bottom:12px;' }, 'ITAM-specific abuse cases')));\n  root.appendChild(h('div', { class: 'card card-pad' }, numberedMiniList(d.threatModel.abuseCases)));\n\n  \/\/ Findings explorer\n  root.appendChild(sectionTitle('Findings explorer', '34 findings, sorted by severity. Filter, search, or click any row to expand.'));\n  root.appendChild(findingsControls());\n  const listHost = h('div', { id: 'findingsList' });\n  root.appendChild(listHost);\n  renderFindingsList(listHost);\n\n  \/\/ Remediation roadmap\n  root.appendChild(sectionTitle('Remediation roadmap'));\n  const roadWrap = h('div', { class: 'grid grid-2' });\n  roadWrap.appendChild(roadmapCard('Quick wins', 'Hours to a few days, high impact', d.remediation.quickWins, 'qw'));\n  roadWrap.appendChild(roadmapCard('Structural fixes', 'Weeks, require design work', d.remediation.structural, 'sf'));\n  root.appendChild(roadWrap);\n\n  root.appendChild(h('div', { class: 'section-gap-sm' }, h('h4', { class: 'h3', style: 'margin-bottom:12px;' }, 'Suggested priority order')));\n  root.appendChild(priorityChain(d.remediation.priorityOrder));\n\n  root.appendChild(sectionTitle('OWASP LLM Top 10 applicability'));\n  root.appendChild(h('div', { class: 'callout info' }, icon('info', 18), h('p', {}, d.llmApplicability)));\n\n  if (secOpenId) {\n    requestAnimationFrame(() => {\n      const el = document.getElementById('finding-' + secOpenId);\n      if (el) { el.classList.add('open'); el.scrollIntoView({ behavior: 'smooth', block: 'center' }); }\n    });\n  }\n}\n\nfunction severityStrip(sev) {\n  const wrap = h('div', { class: 'readout', style: 'margin-bottom:8px;' });\n  const items = [\n    { label: 'Critical', v: sev.critical, cls: 'crit' },\n    { label: 'High', v: sev.high, cls: 'high' },\n    { label: 'Medium', v: sev.medium, cls: 'med' },\n    { label: 'Low', v: sev.low, cls: 'low' }\n  ];\n  items.forEach((it) => {\n    const colorVar = { crit: 'var(--crit)', high: 'var(--high)', med: 'var(--med)', low: 'var(--low)' }[it.cls];\n    wrap.appendChild(h('div', { class: 'stat' },\n      h('div', { class: 'k' }, it.label),\n      h('div', { class: 'v', style: `color:${colorVar};` }, it.v)\n    ));\n  });\n  return wrap;\n}\n\nfunction findingsControls() {\n  const wrap = h('div', { class: 'card card-pad', style: 'display:flex;gap:14px;flex-wrap:wrap;align-items:center;margin-bottom:16px;' });\n  const chipRow = h('div', { class: 'chip-row' });\n  const sevs = [['all', 'All', DATA.stats.findingsTotal], ['critical', 'Critical', DATA.severityCounts.critical], ['high', 'High', DATA.severityCounts.high], ['medium', 'Medium', DATA.severityCounts.medium], ['low', 'Low', DATA.severityCounts.low]];\n  sevs.forEach(([key, label, count]) => {\n    const chip = h('button', {\n      class: 'chip' + (secFilterSeverity === key ? ' active ' + key : ''),\n      onclick: () => { secFilterSeverity = key; refreshFindings(); }\n    }, label, h('span', { class: 'cnt' }, '' + count));\n    chip.dataset.sev = key;\n    chipRow.appendChild(chip);\n  });\n  wrap.appendChild(chipRow);\n  const search = h('div', { class: 'searchbtn', style: 'min-width:220px;flex:1;cursor:text;' },\n    h('span', {}, icon('search', 15)),\n    h('input', {\n      placeholder: 'Search findings\\u2026', style: 'border:none;outline:none;background:transparent;font-size:13px;width:100%;font-family:inherit;color:var(--ink);',\n      oninput: (e) => { secFilterText = e.target.value.toLowerCase(); refreshFindings(); }\n    })\n  );\n  wrap.appendChild(search);\n  return wrap;\n}\n\nfunction refreshFindings() {\n  qsa('.chip[data-sev]').forEach((c) => {\n    c.classList.toggle('active', c.dataset.sev === secFilterSeverity);\n    c.classList.toggle(c.dataset.sev, c.dataset.sev === secFilterSeverity);\n  });\n  const host = qs('#findingsList');\n  if (host) renderFindingsList(host);\n}\n\nfunction matchFinding(f) {\n  if (secFilterSeverity !== 'all' && sevClass(f.severity) !== secFilterSeverity) return false;\n  if (secFilterText) {\n    const hay = (f.id + ' ' + f.title + ' ' + f.location + ' ' + f.tags).toLowerCase();\n    if (!hay.includes(secFilterText)) return false;\n  }\n  return true;\n}\n\nfunction renderFindingsList(host) {\n  host.innerHTML = '';\n  const list = DATA.security.findings.filter(matchFinding).sort((a, b) => severityRank(a.severity) - severityRank(b.severity));\n  if (!list.length) {\n    host.appendChild(h('div', { class: 'card card-pad', style: 'text-align:center;color:var(--muted);padding:40px;' }, 'No findings match that filter.'));\n    return;\n  }\n  list.forEach((f) => host.appendChild(findingCard(f)));\n}\n\nfunction findingCard(f) {\n  const isOpen = secOpenId === f.id;\n  const acc = h('div', { class: 'acc' + (isOpen ? ' open' : ''), id: 'finding-' + f.id });\n  const head = h('div', { class: 'acc-head', onclick: () => { acc.classList.toggle('open'); } },\n    h('span', { class: 'sevbar', style: `background:var(--${sevClass(f.severity) === 'medium' ? 'med' : sevClass(f.severity)});` }),\n    h('span', { class: 'idtag' }, f.id),\n    h('span', { class: 'ttl' }, f.title),\n    h('span', { class: 'badge ' + (sevClass(f.severity) === 'medium' ? 'med' : sevClass(f.severity)) }, f.severity),\n    h('span', { class: 'acc-chev' }, icon('chevron-down', 14))\n  );\n  const body = h('div', { class: 'acc-body' });\n  const bodyIn = h('div', { class: 'acc-body-in' });\n  bodyIn.appendChild(h('div', { class: 'finding-meta' }, f.tags));\n  bodyIn.appendChild(h('div', { class: 'finding-block loc' }, h('span', { class: 'lbl' }, 'Where it lives'), h('p', {}, f.location)));\n  f.body.forEach((p) => bodyIn.appendChild(h('div', { class: 'finding-block' }, h('p', {}, p))));\n  bodyIn.appendChild(h('div', { class: 'finding-block exp' }, h('span', { class: 'lbl' }, 'How it could be exploited'), h('p', {}, f.exploit)));\n  if (f.fix) bodyIn.appendChild(h('div', { class: 'finding-block fix' }, h('span', { class: 'lbl' }, 'Proposed fix'), h('p', {}, f.fix)));\n  if (f.code) bodyIn.appendChild(renderCodeBox(f.code));\n  body.appendChild(bodyIn);\n  acc.appendChild(head);\n  acc.appendChild(body);\n  return acc;\n}\n\nfunction roadmapCard(title, sub, items, prefix) {\n  const card = h('div', { class: 'card card-pad' },\n    h('h4', { class: 'h3' }, title),\n    h('div', { class: 'muted', style: 'font-size:12px;margin-bottom:12px;' }, sub),\n    h('div', { class: 'checklist' })\n  );\n  const list = card.lastChild;\n  items.forEach((it, i) => {\n    const cbId = prefix + i;\n    const row = h('div', { class: 'check-row' },\n      h('input', { type: 'checkbox', id: cbId, onchange: (e) => row.classList.toggle('done', e.target.checked) }),\n      h('label', { style: 'flex:1;cursor:pointer;', for: cbId }, it.t),\n      h('span', { class: 'tagref mono' }, it.ref)\n    );\n    list.appendChild(row);\n  });\n  return card;\n}\n\nfunction priorityChain(order) {\n  const wrap = h('div', { class: 'card card-pad', style: 'display:flex;gap:10px;flex-wrap:wrap;align-items:center;' });\n  order.forEach((id, i) => {\n    wrap.appendChild(h('a', { href: '#\/security\/' + id, class: 'badge brand', style: 'font-size:11.5px;padding:6px 12px;' }, id));\n    if (i < order.length - 1) wrap.appendChild(h('span', { style: 'color:var(--line-strong);' }, icon('arrow-right', 13)));\n  });\n  return wrap;\n}\n\/* -------------------------------------------------------------------------\n   PAGE: 06 Open Questions\n   ------------------------------------------------------------------------- *\/\nfunction renderQuestions(root, deep) {\n  const d = DATA.openQuestions;\n  root.appendChild(pageHeader('06', 'Open Questions', 'Everything the code alone could not answer, addressed to the owning team. Please confirm or correct.'));\n\n  const wrap = h('div', {});\n  d.groups.forEach((g) => {\n    const isOpen = deep === g.letter || (!deep && g.letter === 'A');\n    const grp = h('div', { class: 'qgroup' + (isOpen ? ' open' : ''), id: 'qgroup-' + g.letter });\n    const head = h('div', { class: 'qgroup-head', onclick: () => grp.classList.toggle('open') },\n      h('span', { class: 'letter' }, g.letter),\n      h('h3', { html: esc(g.title) + (g.sub ? ` <span style=\"font-weight:400;color:var(--muted);font-size:12px;\">\\u2014 ${esc(g.sub)}<\/span>` : '') }),\n      h('span', { class: 'cnt' }, g.items.length + ' item' + (g.items.length === 1 ? '' : 's'))\n    );\n    const body = h('div', { class: 'qgroup-body' });\n    g.items.forEach((it, i) => {\n      body.appendChild(h('div', { class: 'qitem' }, h('span', { class: 'qn' }, g.letter + (i + 1)), h('p', {}, it)));\n    });\n    grp.appendChild(head);\n    grp.appendChild(body);\n    wrap.appendChild(grp);\n  });\n  root.appendChild(wrap);\n}\n\n\/* -------------------------------------------------------------------------\n   Command palette \/ global search\n   ------------------------------------------------------------------------- *\/\nlet paletteIndex = [];\nfunction buildSearchIndex() {\n  const idx = [];\n  DATA.nav.filter(n => n.id !== 'home').forEach((n) => idx.push({ type: 'Page', title: n.label, sub: 'Document ' + n.num, icon: n.icon, go: () => navigate(n.id) }));\n  DATA.security.findings.forEach((f) => idx.push({ type: f.severity + ' finding', title: f.id + ' \\u00b7 ' + f.title, sub: f.location.slice(0, 70), icon: 'shield', go: () => navigate('security', f.id) }));\n  DATA.functional.modules.forEach((m) => {\n    idx.push({ type: 'Module', title: m.name + ' \\u2014 ' + m.full, sub: m.blurb.slice(0, 70), icon: m.icon, go: () => navigate('functional', m.id) });\n    m.features.forEach((feat) => idx.push({ type: 'Feature \\u00b7 ' + m.name, title: feat[0], sub: feat[2] ? feat[2].slice(0, 70) : feat[1], icon: m.icon, go: () => navigate('functional', m.id) }));\n  });\n  DATA.business.personas.forEach((p) => idx.push({ type: 'Persona', title: p.name, sub: p.what, icon: p.icon, go: () => navigate('business') }));\n  DATA.openQuestions.groups.forEach((g) => idx.push({ type: 'Open questions', title: g.letter + ' \\u2014 ' + g.title, sub: g.items.length + ' questions', icon: 'help-circle', go: () => navigate('questions', g.letter) }));\n  paletteIndex = idx;\n}\n\nlet paletteSel = -1;\nfunction openPalette() {\n  qs('#searchOverlay').classList.add('show');\n  const input = qs('#paletteInput');\n  input.value = '';\n  input.focus();\n  renderPaletteResults('');\n}\nfunction closePalette() { qs('#searchOverlay').classList.remove('show'); qs('#paletteInput').blur(); }\n\nfunction renderPaletteResults(query) {\n  const host = qs('#paletteResults');\n  host.innerHTML = '';\n  paletteSel = -1;\n  let results = paletteIndex;\n  if (query) {\n    const q = query.toLowerCase();\n    results = paletteIndex.filter(it => (it.title + ' ' + it.sub + ' ' + it.type).toLowerCase().includes(q));\n  }\n  results = results.slice(0, 40);\n  if (!results.length) {\n    host.appendChild(h('div', { class: 'palette-empty' }, 'No matches. Try a different term.'));\n    return;\n  }\n  results.forEach((r, i) => {\n    const item = h('div', { class: 'palette-item', onclick: () => { r.go(); closePalette(); } },\n      h('span', { class: 'pic' }, icon(r.icon, 15)),\n      h('div', {}, h('div', { class: 'pt' }, r.title), h('div', { class: 'ps' }, r.type + (r.sub ? ' \\u00b7 ' + r.sub : '')))\n    );\n    host.appendChild(item);\n  });\n}\nfunction movePaletteSel(delta) {\n  const items = qsa('.palette-item');\n  if (!items.length) return;\n  if (paletteSel >= 0) items[paletteSel].classList.remove('sel');\n  paletteSel = (paletteSel + delta + items.length) % items.length;\n  items[paletteSel].classList.add('sel');\n  items[paletteSel].scrollIntoView({ block: 'nearest' });\n}\n\n\/* -------------------------------------------------------------------------\n   Theme\n   ------------------------------------------------------------------------- *\/\nfunction applyTheme(t) {\n  document.documentElement.setAttribute('data-theme', t);\n  qs('#themeToggle').innerHTML = iconHTML(t === 'dark' ? 'sun' : 'moon', 17);\n  try { localStorage.setItem('meltx-theme', t); } catch (e) {}\n}\nfunction initTheme() {\n  let t = 'light';\n  try { t = localStorage.getItem('meltx-theme') || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); } catch (e) {}\n  applyTheme(t);\n}\n\n\/* -------------------------------------------------------------------------\n   Mobile sidebar\n   ------------------------------------------------------------------------- *\/\nfunction closeSidebarMobile() {\n  qs('#sidebar').classList.remove('open');\n  qs('#scrim').classList.remove('show');\n}\n\n\/* -------------------------------------------------------------------------\n   Init\n   ------------------------------------------------------------------------- *\/\nfunction init() {\n  initTheme();\n  qs('#searchIcon').innerHTML = iconHTML('search', 15);\n  qs('#paletteIcon').innerHTML = iconHTML('search', 17);\n  qs('#menuToggle').innerHTML = iconHTML('menu', 18);\n  qs('#printBtn').innerHTML = iconHTML('printer', 16);\n  qs('#backTop').innerHTML = iconHTML('arrow-up', 17);\n\n  buildSearchIndex();\n\n  window.addEventListener('hashchange', router);\n  router();\n\n  qs('#openSearch').addEventListener('click', openPalette);\n  qs('#searchOverlay').addEventListener('click', (e) => { if (e.target.id === 'searchOverlay') closePalette(); });\n  qs('#paletteInput').addEventListener('input', (e) => renderPaletteResults(e.target.value));\n  qs('#paletteInput').addEventListener('keydown', (e) => {\n    if (e.key === 'ArrowDown') { e.preventDefault(); movePaletteSel(1); }\n    else if (e.key === 'ArrowUp') { e.preventDefault(); movePaletteSel(-1); }\n    else if (e.key === 'Enter') { const items = qsa('.palette-item'); if (paletteSel >= 0 && items[paletteSel]) items[paletteSel].click(); else if (items[0]) items[0].click(); }\n    else if (e.key === 'Escape') closePalette();\n  });\n  document.addEventListener('keydown', (e) => {\n    const tag = (e.target.tagName || '').toLowerCase();\n    const typing = tag === 'input' || tag === 'textarea';\n    if (!typing && (e.key === '\/' || ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k'))) {\n      e.preventDefault(); openPalette();\n    } else if (e.key === 'Escape') { closePalette(); }\n  });\n\n  qs('#themeToggle').addEventListener('click', () => {\n    const cur = document.documentElement.getAttribute('data-theme');\n    applyTheme(cur === 'dark' ? 'light' : 'dark');\n  });\n  qs('#printBtn').addEventListener('click', () => window.print());\n\n  qs('#menuToggle').addEventListener('click', () => { qs('#sidebar').classList.add('open'); qs('#scrim').classList.add('show'); });\n  qs('#scrim').addEventListener('click', closeSidebarMobile);\n\n  window.addEventListener('scroll', () => {\n    qs('#backTop').classList.toggle('show', window.scrollY > 500);\n  });\n  qs('#backTop').addEventListener('click', () => window.scrollTo({ top: 0, behavior: 'smooth' }));\n}\n\ndocument.addEventListener('DOMContentLoaded', init);\n\n<\/script>\n<\/body>\n<\/html>\n\n","protected":false},"excerpt":{"rendered":"<p>New Suite ITAM \u2014 Audit Intelligence Console \u2014 MeltX Software Solutions MeltX ITAM Audit Console Handover documents RBACOFF Test coverage0% Open findings34 GeneratedJul 2026 Command Center Search everything\u2026 \/ \u2191\u2193 navigate\u21b5 openesc close<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-27","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/downloads.meltxsoftware.com\/index.php?rest_route=\/wp\/v2\/pages\/27","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/downloads.meltxsoftware.com\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/downloads.meltxsoftware.com\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/downloads.meltxsoftware.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/downloads.meltxsoftware.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=27"}],"version-history":[{"count":1,"href":"https:\/\/downloads.meltxsoftware.com\/index.php?rest_route=\/wp\/v2\/pages\/27\/revisions"}],"predecessor-version":[{"id":29,"href":"https:\/\/downloads.meltxsoftware.com\/index.php?rest_route=\/wp\/v2\/pages\/27\/revisions\/29"}],"wp:attachment":[{"href":"https:\/\/downloads.meltxsoftware.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=27"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}