diff --git a/web_prototype/assets/app.css b/web_prototype/assets/app.css
new file mode 100644
index 0000000..f79c6c7
--- /dev/null
+++ b/web_prototype/assets/app.css
@@ -0,0 +1,164 @@
+:root {
+ --color-brand:#E20074;
+ --color-brand-hover:#C40062;
+ --color-bg:#F0F0F0;
+ --color-surface:#FFFFFF;
+ --color-text:#222;
+ --color-text-secondary:#555;
+ --color-border:#E2E2E2;
+ --color-border-strong:#CFCFCF;
+ --color-success:#1E9E59;
+ --color-danger:#D22C32;
+ --color-warning:#D89200;
+ --radius:10px;
+ --shadow:0 2px 4px rgba(0,0,0,0.08),0 4px 12px rgba(0,0,0,0.06);
+ font-family:"Segoe UI",system-ui,-apple-system,BlinkMacSystemFont,"Helvetica Neue",Arial,sans-serif;
+}
+*{box-sizing:border-box;}body{margin:0;background:var(--color-bg);color:var(--color-text);display:flex;min-height:100vh;overflow:auto;font-family:inherit;-webkit-font-smoothing:antialiased;}a{text-decoration:none;color:inherit;}
+
+/* Layout */
+.sidebar{width:240px;background:#1E1E24;color:#DDD;display:flex;flex-direction:column;padding:1rem 0;} .sidebar h1{font-size:1.05rem;font-weight:600;padding:0 1.25rem 1rem;margin:0 0 .5rem;color:#FFF;letter-spacing:.5px;}
+.nav{list-style:none;margin:0;padding:0;flex:1;} .nav li a{display:flex;align-items:center;gap:.75rem;padding:.75rem 1.25rem;color:#B9B9C2;font-size:.9rem;border-left:4px solid transparent;transition:.2s;} .nav li a:hover{background:rgba(255,255,255,.05);color:#FFF;} .nav li a.active{background:rgba(226,0,116,.12);color:#FFF;border-left-color:var(--color-brand);font-weight:600;}
+.icon{width:18px;height:18px;border-radius:4px;background:linear-gradient(135deg,var(--color-brand),#FF4DA8);opacity:.8;}
+.footer{padding:1rem 1.25rem;font-size:.7rem;opacity:.55;}
+.main-wrapper{flex:1;display:flex;flex-direction:column;min-width:0;}
+.header{height:64px;background:var(--color-surface);display:flex;align-items:center;padding:0 1.5rem;gap:1rem;border-bottom:1px solid var(--color-border);} .brand-title{font-weight:600;letter-spacing:.5px;font-size:.9rem;color:var(--color-brand);}
+.search-box{flex:1;position:relative;} .search-box input{width:100%;padding:.65rem .9rem .65rem 2.25rem;border:1px solid var(--color-border);border-radius:999px;background:#FFF;font-size:.85rem;outline:none;transition:.2s;} .search-box input:focus{border-color:var(--color-brand);box-shadow:0 0 0 3px rgba(226,0,116,.25);} .search-box .magnifier{position:absolute;top:50%;left:.8rem;width:14px;height:14px;background:var(--color-brand);border-radius:50%;transform:translateY(-50%);box-shadow:0 0 0 3px rgba(226,0,116,.25);}
+.user-avatar{width:36px;height:36px;background:linear-gradient(135deg,var(--color-brand),#FF4DA8);border-radius:50%;box-shadow:0 0 0 3px rgba(226,0,116,.25);}
+.content{flex:1;overflow:visible;padding:1.5rem clamp(1rem,2vw,2rem) 2rem;display:flex;flex-direction:column;gap:1.5rem;}
+
+/* Cards & Utilities */
+.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:1.25rem;}
+.card{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius);padding:1.1rem 1.15rem 1.25rem;box-shadow:var(--shadow);display:flex;flex-direction:column;gap:.75rem;position:relative;}
+.card h3{margin:0;font-size:.95rem;font-weight:600;letter-spacing:.3px;}
+.small{font-size:.65rem;opacity:.75;}
+.primary-btn{align-self:flex-start;background:var(--color-brand);color:#FFF;font-size:.8rem;font-weight:600;border:none;padding:.65rem 1.1rem;border-radius:8px;cursor:pointer;letter-spacing:.3px;display:inline-flex;gap:.4rem;box-shadow:0 4px 10px -2px rgba(226,0,116,.45);transition:.2s;} .primary-btn:hover{background:var(--color-brand-hover);transform:translateY(-2px);box-shadow:0 6px 14px -2px rgba(226,0,116,.5);} .secondary-btn{background:#FFF;color:var(--color-brand);border:1px solid var(--color-brand);padding:.55rem .95rem;font-size:.75rem;border-radius:8px;cursor:pointer;font-weight:500;transition:.2s;} .secondary-btn:hover{background:rgba(226,0,116,.08);} .danger-btn{background:var(--color-danger);color:#FFF;border:none;padding:.55rem .95rem;font-size:.75rem;border-radius:8px;cursor:pointer;}
+.inline-note{font-size:.65rem;opacity:.8;margin-left:.35rem;}
+.status-pill{background:rgba(226,0,116,.1);color:var(--color-brand);padding:.25rem .55rem;font-size:.65rem;border-radius:999px;font-weight:500;white-space:nowrap;}
+.status-ok{background:rgba(30,158,89,.12);color:var(--color-success);} .status-warn{background:rgba(216,146,0,.15);color:var(--color-warning);} .status-bad{background:rgba(210,44,50,.15);color:var(--color-danger);}
+.divider{height:1px;background:var(--color-border);margin:.75rem 0;}
+
+/* Metrics */
+.metrics{display:flex;gap:1.25rem;margin-top:.25rem;} .metric{flex:1;background:#F8F8F9;padding:.6rem .7rem;border-radius:8px;display:flex;flex-direction:column;gap:.15rem;font-size:.7rem;line-height:1.25;border:1px solid #ECECEC;} .metric .val{font-size:.95rem;font-weight:600;color:var(--color-brand);}
+
+/* Tables */
+.table-wrapper{overflow:auto;border:1px solid var(--color-border);border-radius:10px;background:#FFF;}
+table{width:100%;border-collapse:collapse;font-size:.75rem;min-width:600px;}
+thead{background:#FBFBFC;} th,td{text-align:left;padding:.65rem .85rem;border-bottom:1px solid #EEE;white-space:nowrap;} tbody tr:hover{background:#FFF4FA;} tbody tr:last-child td{border-bottom:none;}
+.row-actions{opacity:0;transition:.15s;display:flex;gap:.3rem;} tbody tr:hover .row-actions{opacity:1;} .icon-btn{background:transparent;border:1px solid var(--color-border);width:26px;height:26px;padding:0;border-radius:6px;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:.6rem;color:var(--color-text-secondary);} .icon-btn:hover{border-color:var(--color-brand);color:var(--color-brand);} .overflow-menu{position:absolute;top:100%;right:0;background:#FFF;border:1px solid var(--color-border);border-radius:8px;min-width:160px;padding:.35rem .3rem;box-shadow:var(--shadow);display:none;z-index:10;} .overflow-menu.open{display:block;} .overflow-menu button{all:unset;display:block;width:100%;padding:.45rem .65rem;font-size:.7rem;color:var(--color-text-secondary);border-radius:6px;cursor:pointer;} .overflow-menu button:hover{background:rgba(226,0,116,.08);color:var(--color-brand);}
+
+/* Filters */
+.filters-bar{display:flex;gap:.6rem;flex-wrap:wrap;align-items:center;margin-bottom:.6rem;} .filters-bar input[type=search]{flex:1;min-width:220px;padding:.55rem .75rem;border:1px solid var(--color-border);border-radius:8px;font-size:.75rem;outline:none;} .filters-bar input[type=search]:focus{border-color:var(--color-brand);box-shadow:0 0 0 3px rgba(226,0,116,.2);} .chip{background:#FFF;border:1px solid var(--color-border);border-radius:999px;padding:.35rem .7rem;font-size:.65rem;display:inline-flex;align-items:center;gap:.35rem;cursor:pointer;} .chip.active{border-color:var(--color-brand);color:var(--color-brand);background:rgba(226,0,116,.08);}
+
+/* Tabs */
+.tabs{display:flex;gap:.75rem;margin-top:.3rem;border-bottom:1px solid var(--color-border);padding:0 .15rem;} .tab{padding:.55rem .95rem;font-size:.75rem;font-weight:500;cursor:pointer;color:var(--color-text-secondary);position:relative;border-radius:6px 6px 0 0;transition:.2s;} .tab.active{background:var(--color-surface);color:var(--color-brand);font-weight:600;box-shadow:0 -2px 6px rgba(0,0,0,.05);} .tab.active:after{content:"";position:absolute;left:0;bottom:-1px;width:100%;height:3px;background:var(--color-brand);border-radius:2px 2px 0 0;}
+
+/* Stepper */
+.stepper{display:flex;gap:1.5rem;align-items:flex-start;flex-wrap:wrap;padding:.4rem 0 .9rem;} .step{display:flex;align-items:center;gap:.6rem;position:relative;font-size:.7rem;color:var(--color-text-secondary);} .step-circle{width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.65rem;font-weight:600;border:2px solid var(--color-border);background:#FFF;color:var(--color-text-secondary);transition:.25s;} .step.active .step-circle{border-color:var(--color-brand);background:var(--color-brand);color:#FFF;box-shadow:0 0 0 4px rgba(226,0,116,.25);} .step.completed .step-circle{border-color:var(--color-brand);background:#FFF;color:var(--color-brand);} .step-label{max-width:120px;line-height:1.2;} .step:after{content:"";position:absolute;left:14px;top:34px;width:2px;height:26px;background:var(--color-border);opacity:.5;} .step:last-child:after{display:none;} @media (min-width:760px){.stepper.horizontal{flex-wrap:nowrap;}.stepper.horizontal .step{flex-direction:column;align-items:center;}.stepper.horizontal .step:after{display:none;}.stepper.horizontal .step:not(:last-child)::before{content:"";position:absolute;top:14px;left:50%;right:-50%;height:2px;background:var(--color-border);z-index:-1;}}
+
+/* Forms */
+.form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:1rem 1.25rem;margin-top:.75rem;} .form-group{display:flex;flex-direction:column;gap:.35rem;font-size:.7rem;} .form-group label{font-weight:600;font-size:.65rem;letter-spacing:.5px;text-transform:uppercase;color:var(--color-text-secondary);} .form-group input,.form-group select,.form-group textarea{padding:.6rem .7rem;border:1px solid var(--color-border);border-radius:8px;font-size:.75rem;background:#FFF;resize:vertical;min-height:42px;font-family:inherit;} .form-group textarea.code{font-family:ui-monospace,Consolas,"SFMono-Regular",monospace;line-height:1.35;tab-size:2;white-space:pre;} .form-group input:focus,.form-group select:focus,.form-group textarea:focus{outline:none;border-color:var(--color-brand);box-shadow:0 0 0 3px rgba(226,0,116,.25);} .mask-wrapper{position:relative;display:flex;align-items:center;} .mask-wrapper button{position:absolute;right:.45rem;top:50%;transform:translateY(-50%);border:none;background:rgba(226,0,116,.12);color:var(--color-brand);font-size:.6rem;padding:.3rem .5rem;border-radius:6px;cursor:pointer;} .mask-wrapper button:hover{background:var(--color-brand);color:#FFF;}
+
+/* KPI Cards */
+.kpi-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:1rem;} .kpi{background:var(--color-surface);border:1px solid var(--color-border);border-radius:14px;padding:.9rem 1rem;display:flex;flex-direction:column;gap:.35rem;box-shadow:var(--shadow);position:relative;overflow:hidden;} .kpi h4{margin:0;font-size:.6rem;letter-spacing:.6px;font-weight:600;text-transform:uppercase;color:var(--color-text-secondary);} .kpi .val{font-size:1.35rem;font-weight:600;color:var(--color-brand);line-height:1;} .kpi .trend{font-size:.6rem;font-weight:500;} .trend.up{color:var(--color-success);} .trend.down{color:var(--color-danger);} .sparkline{position:absolute;inset:0;opacity:.15;background:radial-gradient(circle at 80% 20%,var(--color-brand),transparent 60%);}
+
+/* Chart Placeholder */
+.chart-card{min-height:280px;} .chart-placeholder{flex:1;display:flex;align-items:center;justify-content:center;font-size:.7rem;color:var(--color-text-secondary);border:2px dashed var(--color-border);border-radius:12px;padding:1rem;margin-top:.5rem;}
+
+/* Health Panel */
+.health-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:.75rem;margin-top:.5rem;} .health-item{background:#FAFAFA;border:1px solid var(--color-border);border-radius:10px;padding:.6rem .7rem;display:flex;flex-direction:column;gap:.25rem;font-size:.65rem;} .health-item strong{font-size:.7rem;}
+
+/* Usage Bars */
+.usage-bar{height:6px;background:#ECECEC;border-radius:4px;overflow:hidden;position:relative;}
+.usage-bar > span{display:block;height:100%;background:linear-gradient(90deg,var(--color-brand),#FF4DA8);}
+.usage-bar.warn > span{background:linear-gradient(90deg,#D89200,#FFB347);}
+.usage-bar.danger > span{background:linear-gradient(90deg,#D22C32,#FF5A5F);}
+
+/* Toggles */
+.toggle {position:relative;display:inline-flex;align-items:center;gap:.55rem;font-size:.7rem;cursor:pointer;user-select:none;}
+.toggle input{appearance:none;width:40px;height:20px;background:#CFCFCF;border-radius:999px;position:relative;outline:none;transition:.25s;border:1px solid #B9B9B9;cursor:pointer;}
+.toggle input:after{content:"";position:absolute;left:2px;top:2px;width:16px;height:16px;border-radius:50%;background:#FFF;box-shadow:0 1px 2px rgba(0,0,0,.25);transition:.25s;}
+.toggle input:checked{background:var(--color-brand);border-color:var(--color-brand);}
+.toggle input:checked:after{transform:translateX(20px);}
+
+/* Slider (Hybrid Weights) */
+.slider-row{display:flex;align-items:center;gap:.75rem;font-size:.65rem;}
+.slider-row input[type=range]{flex:1;height:6px;background:linear-gradient(90deg,var(--color-brand),#FF4DA8);border-radius:4px;outline:none;-webkit-appearance:none;appearance:none;}
+.slider-row input[type=range]::-webkit-slider-thumb{appearance:none;width:18px;height:18px;border-radius:50%;background:#FFF;border:2px solid var(--color-brand);box-shadow:0 0 0 3px rgba(226,0,116,.25);cursor:pointer;}
+.slider-row .weight-badge{background:#FFF;border:1px solid var(--color-border);padding:.35rem .55rem;border-radius:6px;font-size:.6rem;font-weight:600;min-width:42px;text-align:center;}
+
+/* Metadata Rule Builder */
+.rule-builder{display:flex;flex-direction:column;gap:.6rem;}
+.rule-row{display:flex;gap:.5rem;flex-wrap:wrap;align-items:center;background:#FAFAFA;border:1px solid var(--color-border);padding:.55rem .6rem;border-radius:8px;position:relative;}
+.rule-row select,.rule-row input{padding:.45rem .55rem;font-size:.65rem;border:1px solid var(--color-border);border-radius:6px;min-width:120px;}
+.rule-row select:focus,.rule-row input:focus{outline:none;border-color:var(--color-brand);box-shadow:0 0 0 2px rgba(226,0,116,.25);}
+.rule-row button.remove-rule{background:transparent;border:1px solid var(--color-border);color:#666;font-size:.6rem;width:26px;height:26px;border-radius:6px;cursor:pointer;}
+.rule-row button.remove-rule:hover{color:var(--color-brand);border-color:var(--color-brand);}
+.add-rule-btn{background:#FFF;border:1px dashed var(--color-brand);color:var(--color-brand);padding:.5rem .75rem;font-size:.65rem;border-radius:8px;cursor:pointer;width:fit-content;}
+.add-rule-btn:hover{background:rgba(226,0,116,.08);}
+
+/* Upload Zone */
+.upload-zone{border:2px dashed var(--color-border);border-radius:12px;padding:1.1rem .9rem;text-align:center;background:#FCFCFD;display:flex;flex-direction:column;gap:.55rem;font-size:.65rem;color:var(--color-text-secondary);}
+.upload-zone.dragover{border-color:var(--color-brand);background:rgba(226,0,116,.04);}
+.file-list{display:flex;flex-direction:column;gap:.45rem;font-size:.6rem;margin-top:.4rem;}
+.file-item{display:flex;align-items:center;gap:.5rem;padding:.45rem .55rem;background:#FFF;border:1px solid var(--color-border);border-radius:8px;}
+.file-icon{width:26px;height:26px;border-radius:6px;background:linear-gradient(135deg,var(--color-brand),#FF4DA8);opacity:.85;display:flex;align-items:center;justify-content:center;color:#FFF;font-size:.55rem;font-weight:600;}
+
+/* ACL / Versions / Cost */
+.acl-table th,.acl-table td{padding:.5rem .6rem;font-size:.65rem;border-bottom:1px solid #EEE;text-align:left;}
+.acl-table th{background:#FBFBFC;font-weight:600;font-size:.6rem;letter-spacing:.5px;text-transform:uppercase;}
+.version-list{display:flex;flex-direction:column;gap:.45rem;font-size:.6rem;}
+.version-item{display:flex;justify-content:space-between;align-items:center;background:#FAFAFA;border:1px solid var(--color-border);padding:.45rem .55rem;border-radius:8px;gap:.5rem;}
+.version-item .meta{display:flex;flex-direction:column;line-height:1.25;}
+.version-item button{background:#FFF;border:1px solid var(--color-border);padding:.3rem .55rem;font-size:.55rem;border-radius:6px;cursor:pointer;}
+.version-item button:hover{border-color:var(--color-brand);color:var(--color-brand);}
+.cost-metrics{display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:.75rem;font-size:.6rem;margin-top:.6rem;}
+.cost-box{background:#FAFAFA;border:1px solid var(--color-border);border-radius:10px;padding:.55rem .65rem;display:flex;flex-direction:column;gap:.25rem;}
+.cost-box strong{font-size:.7rem;}
+.cost-val{font-size:.85rem;font-weight:600;color:var(--color-brand);}
+
+/* Flex helpers */
+.flex{display:flex;} .col{display:flex;flex-direction:column;} .gap-s{gap:.5rem;} .gap-m{gap:1rem;} .space-between{justify-content:space-between;} .align-center{align-items:center;}
+
+/* Responsive */
+@media (max-width:900px){.sidebar{width:200px;}.header{height:60px;}} @media (max-width:680px){.sidebar{display:none;} body{flex-direction:column;} .header{border-top:4px solid var(--color-brand);} }
+
+/* ================= LOGIN PAGE ================= */
+.login-shell{display:flex;flex-direction:column;min-height:100vh;width:100%;background:var(--color-bg);}
+.login-topbar{height:70px;background:var(--color-brand);display:flex;align-items:center;padding:0 2rem;}
+.login-topbar .brand-logo{width:44px;height:44px;background:#FFF;border-radius:8px;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:1.1rem;color:var(--color-brand);box-shadow:0 2px 6px rgba(0,0,0,.15);}
+.login-main{flex:1;display:flex;align-items:flex-start;justify-content:center;padding:2.5rem 1rem 3rem;overflow:auto;}
+.login-card{background:#FFF;border:1px solid var(--color-border);border-radius:14px;box-shadow:var(--shadow);width:100%;max-width:640px;padding:3.25rem 3rem 2.75rem;display:flex;flex-direction:column;gap:1.4rem;position:relative;}
+.login-card h2{margin:0;font-size:2.15rem;line-height:1.15;font-weight:700;letter-spacing:.5px;text-align:center;}
+.login-card h2 .sub{display:block;font-size:.6em;font-weight:500;opacity:.65;margin-top:.35rem;}
+.login-service{font-weight:600;text-align:center;font-size:.8rem;letter-spacing:.6px;margin-top:-.5rem;color:var(--color-text-secondary);}
+.login-form{display:flex;flex-direction:column;gap:1rem;}
+.login-form .field{display:flex;flex-direction:column;gap:.4rem;}
+.login-form input[type=text],.login-form input[type=password]{padding:.75rem .9rem;border:1px solid var(--color-border);border-radius:8px;font-size:.85rem;outline:none;background:#FFF;}
+.login-form input[type=text]:focus,.login-form input[type=password]:focus{border-color:var(--color-brand);box-shadow:0 0 0 3px rgba(226,0,116,.25);}
+.login-actions{display:flex;flex-direction:column;gap:.75rem;margin-top:.25rem;}
+.login-btn-primary{background:var(--color-brand);color:#FFF;border:none;border-radius:8px;padding:.85rem 1.1rem;font-size:.85rem;font-weight:600;cursor:pointer;box-shadow:0 4px 12px -2px rgba(226,0,116,.45);transition:.2s;}
+.login-btn-primary:hover{background:var(--color-brand-hover);transform:translateY(-2px);}
+.login-btn-secondary{background:#FFF;border:1px solid var(--color-border);border-radius:8px;padding:.8rem 1.1rem;font-size:.8rem;font-weight:600;cursor:pointer;}
+.login-btn-secondary:hover{border-color:var(--color-brand);color:var(--color-brand);}
+.login-meta{display:flex;align-items:center;justify-content:space-between;font-size:.65rem;}
+.login-meta a{color:var(--color-brand);text-decoration:none;font-weight:500;}
+.login-meta a:hover{text-decoration:underline;}
+.remember-row{display:flex;align-items:center;gap:.5rem;font-size:.65rem;color:var(--color-text-secondary);}
+.remember-row input{width:16px;height:16px;cursor:pointer;}
+.login-help{text-align:center;font-size:.6rem;}
+.login-help a{color:var(--color-brand);}
+.login-social{display:flex;justify-content:center;gap:1.1rem;margin-top:.4rem;}
+.login-social a{width:36px;height:36px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:#FFF;border:1px solid var(--color-border);font-size:.85rem;color:var(--color-text-secondary);transition:.2s;}
+.login-social a:hover{border-color:var(--color-brand);color:var(--color-brand);}
+.login-footer{border-top:1px solid var(--color-border);padding:.85rem 2rem;font-size:.55rem;color:var(--color-text-secondary);display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:.5rem;background:#FFF;}
+.legal-links{display:flex;gap:1rem;}
+.legal-links a{color:var(--color-text-secondary);text-decoration:none;}
+.legal-links a:hover{color:var(--color-brand);}
+@media (max-width:720px){
+ .login-card{padding:2.5rem 1.6rem 2.4rem;}
+ .login-card h2{font-size:1.85rem;}
+ .login-topbar{height:60px;padding:0 1rem;}
+ .login-footer{padding:.85rem 1rem;}
+}
+
diff --git a/web_prototype/assets/app.js b/web_prototype/assets/app.js
new file mode 100644
index 0000000..8dee386
--- /dev/null
+++ b/web_prototype/assets/app.js
@@ -0,0 +1,220 @@
+// Basic shared interactions for prototype
+(function(){
+ function qs(sel, ctx=document){ return ctx.querySelector(sel); }
+ function qsa(sel, ctx=document){ return Array.from(ctx.querySelectorAll(sel)); }
+
+ // Overflow menus (three dots) - open/close logic
+ document.addEventListener('click', e => {
+ const btn = e.target.closest('[data-menu]');
+ if(btn){
+ const id = btn.getAttribute('data-menu');
+ const menu = qs('#menu-' + id);
+ if(menu){
+ menu.classList.toggle('open');
+ // close others
+ qsa('.overflow-menu').forEach(m => { if(m !== menu) m.classList.remove('open'); });
+ }
+ } else if(!e.target.closest('.overflow-menu')) {
+ qsa('.overflow-menu').forEach(m => m.classList.remove('open'));
+ }
+ });
+
+ // Tabs
+ function initTabs(container){
+ if(!container) return;
+ const tabs = qsa('.tab', container);
+ const panelRoot = qs('#tabPanels') || container.parentElement;
+ tabs.forEach(tab => {
+ tab.addEventListener('click', () => {
+ const target = tab.getAttribute('data-tab');
+ tabs.forEach(t => t.classList.remove('active'));
+ tab.classList.add('active');
+ qsa('[data-panel]', panelRoot).forEach(p => {
+ p.style.display = p.getAttribute('data-panel') === target ? '' : 'none';
+ });
+ });
+ });
+ }
+ initTabs(qs('#modelTabs'));
+
+ // API Key mask toggle
+ qsa('[data-toggle-mask]').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const input = btn.parentElement.querySelector('[data-mask-target]');
+ if(!input) return;
+ if(input.type === 'password'){
+ input.type = 'text';
+ btn.textContent = 'Hide';
+ } else {
+ input.type = 'password';
+ btn.textContent = 'Show';
+ }
+ });
+ });
+
+ // Stepper logic (pipeline configuration)
+ const stepper = qs('#pipelineStepper');
+ if(stepper){
+ stepper.addEventListener('click', e => {
+ const stepEl = e.target.closest('.step');
+ if(!stepEl) return;
+ const targetStep = stepEl.getAttribute('data-step');
+ qsa('.step', stepper).forEach(s => {
+ const sStep = s.getAttribute('data-step');
+ s.classList.remove('active');
+ if(Number(sStep) < Number(targetStep)) s.classList.add('completed'); else s.classList.remove('completed');
+ });
+ stepEl.classList.add('active');
+ // toggle panels
+ qsa('[data-step-panel]').forEach(p => {
+ p.style.display = p.getAttribute('data-step-panel') === targetStep ? '' : 'none';
+ });
+ });
+ }
+
+ // Simple persistence of active nav via location
+ const path = location.pathname.split('/').pop();
+ qsa('.sidebar .nav a').forEach(a => {
+ if(a.getAttribute('href') === path){
+ a.classList.add('active');
+ } else {
+ a.classList.remove('active');
+ }
+ });
+
+ // ---- Retrieval Configuration Page Enhancements ----
+ (function initRetrievalConfig(){
+ const slider = qs('#hybridSlider');
+ if(!slider) return; // not on this page
+ const vW = qs('#vectorWeight');
+ const kW = qs('#keywordWeight');
+ const ruleBuilder = qs('#ruleBuilder');
+ const addRuleBtn = qs('#addRuleBtn');
+ const saveBtn = qs('#saveConfigBtn');
+ const saveStatus = qs('#saveStatus');
+
+ function markDirty(){ if(saveStatus){ saveStatus.textContent = 'Unsaved changes...'; saveStatus.style.color = 'var(--color-brand)'; } }
+ function markSaved(){ if(saveStatus){ saveStatus.textContent = 'Configuration saved (simulated)'; saveStatus.style.color = 'var(--color-text-secondary)'; } }
+
+ slider.addEventListener('input', () => {
+ const v = Number(slider.value); const k = 100 - v;
+ if(vW) vW.textContent = v + '%';
+ if(kW) kW.textContent = k + '%';
+ markDirty();
+ });
+
+ if(addRuleBtn && ruleBuilder){
+ addRuleBtn.addEventListener('click', () => {
+ const row = document.createElement('div');
+ row.className = 'rule-row'; row.setAttribute('data-rule','');
+ row.innerHTML = `\n \n document_type \n source_type \n date \n language \n \n \n == \n >= \n > \n <= \n < \n contains \n \n \n × `;
+ ruleBuilder.appendChild(row); markDirty();
+ });
+
+ ruleBuilder.addEventListener('click', e => {
+ if(e.target.classList.contains('remove-rule')){
+ const r = e.target.closest('[data-rule]');
+ if(r){ r.remove(); markDirty(); }
+ }
+ });
+ ['change','input'].forEach(evt => ruleBuilder.addEventListener(evt, markDirty));
+ }
+
+ ['mmToggle','rerankToggle','postRerankN','rerankerModel'].forEach(id => {
+ const el = qs('#'+id); if(el){ el.addEventListener('change', markDirty); el.addEventListener('input', markDirty); }
+ });
+
+ if(saveBtn){
+ saveBtn.addEventListener('click', () => {
+ saveBtn.disabled = true; const orig = saveBtn.textContent; saveBtn.textContent = 'Saving...';
+ setTimeout(()=>{ saveBtn.textContent = orig; saveBtn.disabled = false; markSaved(); }, 900);
+ });
+ }
+ })();
+
+ // ---- Knowledge Base Detail Page Enhancements ----
+ (function initKbDetail(){
+ const uploadZone = qs('#uploadZone');
+ if(!uploadZone) return; // not on detail page
+ const fileInput = qs('#fileInput');
+ const browseTrigger = qs('#browseTrigger');
+ const ingestQueue = qs('#ingestQueue');
+ const queueCount = qs('#queueCount');
+ const docTable = qs('#docTable');
+ const reindexBtn = qs('#reindexBtn');
+ const syncBtn = qs('#syncNowBtn');
+ const syncStatus = qs('#syncStatus');
+ const estimateBtn = qs('#estimateBtn');
+ const versionList = qs('#versionList');
+ const versionStatus = qs('#versionStatus');
+ const aclTable = qs('#aclTable');
+ const addAclBtn = qs('#addAclBtn');
+ const aclPrincipal = qs('#aclPrincipal');
+ const aclRole = qs('#aclRole');
+ const aclScope = qs('#aclScope');
+
+ function addDocs(files){
+ if(!files.length) return;
+ if(ingestQueue) ingestQueue.style.display = 'block';
+ if(queueCount){ queueCount.textContent = (Number(queueCount.textContent) + files.length).toString(); }
+ Array.from(files).forEach(file => {
+ const row = document.createElement('tr');
+ const ext = (file.name.split('.').pop()||'txt');
+ row.innerHTML = `
${file.name} ${ext} — pending `;
+ if(docTable) docTable.appendChild(row);
+ setTimeout(()=>{
+ const status = row.querySelector('.status-pill');
+ if(status){ status.textContent='ready'; status.classList.add('status-success'); }
+ row.children[2].textContent = Math.floor(Math.random()*120)+10;
+ }, 1300 + Math.random()*1200);
+ });
+ }
+
+ if(browseTrigger && fileInput){ browseTrigger.addEventListener('click', () => fileInput.click()); }
+ if(fileInput){ fileInput.addEventListener('change', e => addDocs(e.target.files)); }
+ if(uploadZone){
+ uploadZone.addEventListener('dragover', e => { e.preventDefault(); uploadZone.classList.add('drag-over'); });
+ uploadZone.addEventListener('dragleave', () => uploadZone.classList.remove('drag-over'));
+ uploadZone.addEventListener('drop', e => { e.preventDefault(); uploadZone.classList.remove('drag-over'); addDocs(e.dataTransfer.files); });
+ }
+ if(reindexBtn){
+ reindexBtn.addEventListener('click', () => {
+ reindexBtn.disabled = true; const orig = reindexBtn.textContent; reindexBtn.textContent = 'Reindexing...';
+ setTimeout(()=>{ reindexBtn.textContent = orig; reindexBtn.disabled = false; alert('Reindex simulation completed.'); }, 1800);
+ });
+ }
+ if(syncBtn && syncStatus){
+ syncBtn.addEventListener('click', () => {
+ syncStatus.textContent = 'Sync in progress...'; syncStatus.style.color = 'var(--color-brand)';
+ setTimeout(()=>{ syncStatus.textContent = 'Last sync: just now (simulated)'; syncStatus.style.color = 'var(--color-text-secondary)'; }, 1400);
+ });
+ }
+ if(estimateBtn){
+ estimateBtn.addEventListener('click', () => {
+ estimateBtn.disabled = true; const orig = estimateBtn.textContent; estimateBtn.textContent='Calculating...';
+ setTimeout(()=>{ const cost = 150 + Math.floor(Math.random()*60); const mc = qs('#monthlyCost'); if(mc) mc.textContent = '$'+cost; estimateBtn.textContent=orig; estimateBtn.disabled=false; }, 1000);
+ });
+ }
+ if(versionList){
+ versionList.addEventListener('click', e => {
+ const btn = e.target.closest('.rollback-btn'); if(!btn) return;
+ const v = btn.getAttribute('data-target');
+ if(confirm('Simulate rollback to version '+v+'?')){
+ if(versionStatus){ versionStatus.textContent = 'Rolling back to v'+v+' ...'; versionStatus.style.color = 'var(--color-brand)'; }
+ setTimeout(()=>{ if(versionStatus){ versionStatus.textContent = 'Current active version: v'+v+' (simulated rollback)'; versionStatus.style.color = 'var(--color-text-secondary)'; } }, 1300);
+ }
+ });
+ }
+ if(addAclBtn && aclTable){
+ addAclBtn.addEventListener('click', () => {
+ const p = (aclPrincipal && aclPrincipal.value.trim()) || ''; if(!p) return;
+ const role = (aclRole && aclRole.value) || 'reader'; const scope = (aclScope && aclScope.value.trim()) || 'all';
+ const row = document.createElement('tr');
+ row.innerHTML = `${p} ${role} ${scope} × `;
+ const body = aclTable.querySelector('tbody'); if(body) body.appendChild(row);
+ if(aclPrincipal) aclPrincipal.value=''; if(aclScope) aclScope.value='';
+ });
+ aclTable.addEventListener('click', e => { if(e.target.classList.contains('remove-acl')) e.target.closest('tr').remove(); });
+ }
+ })();
+})();
\ No newline at end of file
diff --git a/web_prototype/create_kb.html b/web_prototype/create_kb.html
new file mode 100644
index 0000000..c03a031
--- /dev/null
+++ b/web_prototype/create_kb.html
@@ -0,0 +1,287 @@
+
+
+
+
+Create Knowledge Base - RAG Dashboard
+
+
+
+
+
+
+
+
+
+
New Knowledge Base Wizard
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web_prototype/dashboard.html b/web_prototype/dashboard.html
new file mode 100644
index 0000000..059afa0
--- /dev/null
+++ b/web_prototype/dashboard.html
@@ -0,0 +1,148 @@
+
+
+
+
+Operational Dashboard - RAG Dashboard
+
+
+
+
+
+
+
+
+
+
+
+
Total Queries
+
182K
+
+4.2% vs prev
+
+
+
+
Avg Latency
+
742ms
+
-3.1%
+
+
+
+
Error Rate
+
0.84%
+
+0.12%
+
+
+
+
Active KBs
+
37
+
+2 new
+
+
+
+
+
Query Volume Over Time
+
+
Last 24h aggregated by 1h intervals.
+
+
+
System Health
+
+
API Gateway Healthy p95 810ms
+
Vector Store Healthy Shard 12/12
+
Workers High Load 76% util
+
Indexer Idle Queue 0
+
Embeddings Normal Batch 42
+
Cache Hot Hit 94%
+
+
+
Updated 30s ago
+
+
+
Top 5 Slowest Pipelines
+
+
+
+
+ Pipeline
+ Avg Latency
+ p95
+ Throughput
+ Error Rate
+ Last Run
+
+
+
+
+ Support-KB-v2
+ 1.42s
+ 2.85s
+ 19 q/min
+ 0.6%
+ 09:21
+
+
+ Hardware-Manuals
+ 1.31s
+ 2.40s
+ 23 q/min
+ 0.9%
+ 09:20
+
+
+ Auth-Guides
+ 1.18s
+ 2.10s
+ 27 q/min
+ 0.7%
+ 09:19
+
+
+ Legacy-Archive
+ 1.11s
+ 2.00s
+ 14 q/min
+ 1.3%
+ 09:18
+
+
+ Design-Docs
+ 1.05s
+ 1.92s
+ 32 q/min
+ 0.5%
+ 09:17
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web_prototype/index.html b/web_prototype/index.html
new file mode 100644
index 0000000..80a2ac5
--- /dev/null
+++ b/web_prototype/index.html
@@ -0,0 +1,138 @@
+
+
+
+
+RAG Dashboard Prototype
+
+
+
+
+
+
+
+
+
+
+
Knowledge Base Status
+
+
+ Documents
+ 4,218
+
+
+ Sources
+ 17
+
+
+ Vectors
+ 1.2M
+
+
+
+
+ Sync Progress
+ 62%
+
+
+
+
Create New Knowledge Base
+
+
+
+
Recent Activity (latest 24h)
+
+ 152 user queries processed
+ 87 new documents ingested
+ 4 pipeline adjustments
+
+
Latency stable at p95 820ms
+
+
+
+
Model Overview
+
+
Embedding Model: text-embedding-3-large
+
Generator: gpt-4o-mini
+
Reranker: cross-encoder-v2
+
Chunking: 512 tokens
+
Retriever Top-K: 8
+
+
Healthy
+
+
+
+
Recent RAG Queries (latest 5)
+
+
+
+
+ Query
+ Latency (ms)
+ Source
+ Time
+ Status
+
+
+
+
+ How to reset device firmware?
+ 732
+ manual.pdf
+ 09:21
+ OK
+
+
+ List authentication failure codes
+ 801
+ auth_guide.html
+ 09:18
+ OK
+
+
+ Can we purge stale vectors?
+ 915
+ system_kb
+ 09:10
+ OK
+
+
+ Explain retrieval scoring logic
+ 845
+ design_notes
+ 08:57
+ OK
+
+
+ Pipeline concurrency limits?
+ 1042
+ ops_doc
+ 08:43
+ OK
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web_prototype/kb_detail.html b/web_prototype/kb_detail.html
new file mode 100644
index 0000000..1968ccb
--- /dev/null
+++ b/web_prototype/kb_detail.html
@@ -0,0 +1,250 @@
+
+
+
+
+Knowledge Base Detail - RAG Dashboard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Multi-Modal Ingestion
+
+
+
+
⬆
+
Drag & Drop files (PDF, DOCX, TXT, MD, PNG, JPG, MP4, WAV)
+
or browse • remote URL
+
+
+
Queued: 0 files...
+
+
+
Settings apply to new ingestions. Existing vectors remain until reindexed.
+
+
+
+
+
Recent Documents
+
+ Name Type Chunks Status
+
+ product_guide.pdf pdf 184 ready
+ pricing_sheet.xlsx xlsx 42 ready
+ brand_assets.zip zip — pending
+
+
+
+
+
+
+
+
+
+
Version History
+
+ v1.3 • today • added 24 docs
Rollback
+ v1.2 • 2d ago • reindexed (chunk params)
Rollback
+ v1.1 • 6d ago • initial image embeddings
Rollback
+
+
Current active version: v1.3
+
+
+
+
+
Sync Monitor & Cost Metrics
+
+
+
+
+
+
Avg Embedding Latency
420ms
+
+
+
+
Compute Quota 62% used
+
+
+ Trigger Sync
+ Recalculate Cost Estimate
+
+
Idle.
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web_prototype/kb_list.html b/web_prototype/kb_list.html
new file mode 100644
index 0000000..0db6340
--- /dev/null
+++ b/web_prototype/kb_list.html
@@ -0,0 +1,162 @@
+
+
+
+
+Knowledge Bases - RAG Dashboard
+
+
+
+
+
+
+
+
+
+
+
+
+
All
+
Active
+
Paused
+
Sync Error
+
+
+
+
+
+ Name
+ Documents
+ Last Sync
+ Creation Date
+ Actions
+
+
+
+
+ Device Manuals v2
+ 4,218
+ Success 09:20
+ 2025-08-11
+
+
+ ⋯
+
+
+
+
+
+ Authentication Guides
+ 1,104
+ Success 09:05
+ 2025-08-15
+
+
+ ⋯
+
+
+
+
+
+ System Design Notes
+ 842
+ Partial 08:58
+ 2025-08-19
+
+
+ ⋯
+
+
+
+
+
+ Operations Runbooks
+ 2,309
+ Failed 08:41
+ 2025-08-23
+
+
+ ⋯
+
+
+
+
+
+ Legacy Archive
+ 18,044
+ Success 02:14
+ 2025-06-02
+
+
+ ⋯
+
+
+
+
+
+
+
+
+
Showing 1–5 of 42 knowledge bases
+
+ ‹
+ 1 / 9
+ ›
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web_prototype/kb_retrieval_config.html b/web_prototype/kb_retrieval_config.html
new file mode 100644
index 0000000..880aa44
--- /dev/null
+++ b/web_prototype/kb_retrieval_config.html
@@ -0,0 +1,195 @@
+
+
+
+
+KB Retrieval Configuration - RAG Dashboard
+
+
+
+
+
+
+
+
+
+
+
+
Multi-Modal Retrieval Settings
+
+
+ Enable Multi-Modal Search
+
+
When enabled: image & audio derived vectors included in top-K candidate gathering.
+
+
+
+
+
+
+
Reranking Optimization
+
Enable Reranker
+
+
Reranking applies cross-encoder scoring; disable if latency sensitive.
+
+
+
+
+
Hybrid Search Weights
+
+
Vector
+
+
Keyword
+
70%
+
30%
+
+
Weight influences blended score: final_score = V * (vector_w/100) + K * (keyword_w/100).
+
+
+
+
+
Metadata Filters (Rule Builder)
+
Define structured constraints applied before scoring (pre-filter stage).
+
+
+
+ document_type
+ source_type
+ date
+ language
+
+
+ ==
+ >=
+ >
+ <=
+ <
+ contains
+
+
+ ×
+
+
+
+ Add Rule
+
Rules combined with AND. Future: OR groups, parentheses.
+
+ Raw JSON Schema (optional)
+
+
+
+
+
+
+
+
Persist Configuration
+ Save Configuration
+
+
No unsaved changes.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web_prototype/login.html b/web_prototype/login.html
new file mode 100644
index 0000000..b57abe0
--- /dev/null
+++ b/web_prototype/login.html
@@ -0,0 +1,70 @@
+
+
+
+
+Login - RAG Dashboard
+
+
+
+
+
+
+
+ Servicename
+ Enter Login Username
+
+
+
+
+
+
+ Next
+ Cancel
+
+
+
+
+ No account?
Sign up or log in with your social network account.
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web_prototype/mcp.html b/web_prototype/mcp.html
new file mode 100644
index 0000000..a1de3f0
--- /dev/null
+++ b/web_prototype/mcp.html
@@ -0,0 +1,258 @@
+
+
+
+
+MCP Server Management - RAG Dashboard
+
+
+
+
+
+
+
+
+
+
+
+
+
Registered MCP Servers
+ + Register New MCP Server
+
+
+
+
+
+ Server URL
+ Tooling Namespace
+ Status
+ Owning Team
+ Last Check
+ Actions
+
+
+
+
+ https://mcp-hr.internal/api
+ hr.tools
+ Online
+ PeopleOps
+ 09:21
+
+ ⋯
+
+
+
+
+ https://mcp-finance.internal/v1
+ finance.calc
+ Degraded
+ Finance
+ 09:17
+
+ ⋯
+
+
+
+
+ https://mcp-legal.internal
+ legal.search
+ Online
+ Legal
+ 09:09
+
+ ⋯
+
+
+
+
+ grpc://mcp-ai-lab.internal:7443
+ ailab.exp
+ Online
+ AI Lab
+ 09:02
+
+ ⋯
+
+
+
+
+
+
+
+
Showing 1–4 of 4 MCP servers
+
+ ‹
+ 1 / 1
+ ›
+
+
+
+
+
+
+
MCP API Gateway Keys
+
Keys used by internal apps to call unified MCP gateway. Masked by default.
+
+
+
Last rotation: 5 days ago
+
+ Rotate Primary
+ Rotate Backup
+
+
+
Usage Limits
+
+
Requests Today: 12,480 / 50,000
+
+
Error Rate (24h): 0.42%
+
+
+
+
+
+
+
Context Function Mapping
+
Internal simple tool identifiers mapped to remote MCP server exposed function signatures.
+
+
+
+
+ Internal Tool Name
+ MCP Server URL
+ Remote Function
+ Auth Mode
+ Last Used
+ Actions
+
+
+
+
+ HR_policy_lookup
+ https://mcp-hr.internal/api
+ policies.lookup(v1)
+ gateway-key
+ 09:20
+
+ ⋯
+
+
+
+
+ Finance_cost_summary
+ https://mcp-finance.internal/v1
+ costs.aggregate(month)
+ gateway-key
+ 09:18
+
+ ⋯
+
+
+
+
+ Legal_doc_search
+ https://mcp-legal.internal
+ documents.search(text)
+ gateway-key
+ 09:11
+
+ ⋯
+
+
+
+
+ AI_experiment_trigger
+ grpc://mcp-ai-lab.internal:7443
+ exp.runVariant(set)
+ mTLS
+ 08:59
+
+ ⋯
+
+
+
+
+
+
+
+ Add Function Mapping
+ Bulk Import
+ Export JSON
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web_prototype/models_resources.html b/web_prototype/models_resources.html
new file mode 100644
index 0000000..d94816a
--- /dev/null
+++ b/web_prototype/models_resources.html
@@ -0,0 +1,132 @@
+
+
+
+
+Models & Resources - RAG Dashboard
+
+
+
+
+
+
+
+
+
+
+
LLM Models
+
Embedding Models
+
Vector Stores
+
+
+
+
Configured LLM Providers
+
+
+
Save LLM Config
+
+
+
Embedding Model Settings
+
+
+
Save Embedding Config
+
+
+
Vector Store Configuration
+
+
+
Save Vector Store
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/web_prototype/pipeline_config.html b/web_prototype/pipeline_config.html
new file mode 100644
index 0000000..82d63ca
--- /dev/null
+++ b/web_prototype/pipeline_config.html
@@ -0,0 +1,180 @@
+
+
+
+
+Pipeline Configuration - RAG Dashboard
+
+
+
+
+
+
+
+
+
+
Pipeline Steps
+
+
+
+
+
+ Prompt Template (Generation)
+ System: You are a helpful assistant. Use ONLY the provided context.\n\nUser Query: {{query}}\n\nRelevant Chunks:\n{{context_blocks}}\n\nAnswer concisely and cite sources.
+
+
+
+
+
+
+
Step Settings
+
Configuration depends on current active step in pipeline.
+
+
+
+ Ingestion Source
+ Cloud Storage Git Repo Web Crawl
+
+
+ File Types
+
+
+
+ Schedule (cron)
+
+
+
+
+
+ Chunk Size (tokens)
+
+
+
+ Chunk Overlap
+
+
+
+ Splitter Strategy
+ Recursive Naive Markdown Headers
+
+
+
+
+ Vector Store
+ pgvector-prod milvus-cluster
+
+
+ Distance Metric
+ cosine l2 dot
+
+
+ Min Score Threshold
+
+
+
+
+
+ Rerank Top-N
+
+
+
+ Score Normalization
+ minmax z-score
+
+
+
+
+ Answer Style Hint
+
+
+
+ Include Source Citations
+ Yes No
+
+
+
+
Save Pipeline
+
+
+
Preview Output (Sample)
+
Updated after generation step evaluation.
+
+ This device requires a factory reset performed via the embedded maintenance console. Refer to Section 4.2 of manual.pdf for command sequence. Sources: manual.pdf (p. 18)
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file