Files
mek-tech-consulting/views/consultancy-framework.ejs

123 lines
25 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Consultancy Framework — Mek-Tech</title><link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap" rel="stylesheet"><link rel="stylesheet" href="/css/style.css"><link rel="stylesheet" href="/css/dynamic.css"><link rel="icon" href="/img/favicon.svg"><style>
.consult-page{max-width:860px;margin:0 auto;padding:1.5rem 0 3rem}
.consult-page h1{font-size:1.5rem;font-weight:800;letter-spacing:-.4px;margin-bottom:4px;background:linear-gradient(135deg,var(--text),var(--accent));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.consult-page .sub{font-size:.75rem;color:var(--text-muted)}
.cp-bar{display:flex;gap:.5rem;align-items:center;margin:.75rem 0 1rem;padding:.6rem .9rem;background:var(--bg-card);border:1px solid var(--border);border-radius:10px;flex-wrap:wrap}
.cp-bar select{flex:1;min-width:180px;padding:.4rem .55rem;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text);font-size:.76rem;font-family:inherit}.cp-bar select:focus{outline:none;border-color:var(--accent)}
.cp-bar .cp-link{display:inline-flex;align-items:center;gap:.3rem;padding:.3rem .65rem;border-radius:5px;font-size:.66rem;font-weight:600;cursor:pointer;border:1px solid var(--border);background:var(--bg);color:var(--text-muted);text-decoration:none;font-family:inherit}.cp-bar .cp-link:hover{color:var(--text)}
.cp-bar .cp-on{font-size:.62rem;color:var(--green);display:flex;align-items:center;gap:.25rem}.cp-bar .cp-on::before{content:'';width:6px;height:6px;border-radius:50%;background:var(--green);animation:pulse 2s ease infinite}
.alert{background:var(--green-bg);border:1px solid var(--green);color:var(--green);padding:.4rem .7rem;border-radius:6px;font-size:.7rem;margin-bottom:.75rem}
.prev{background:var(--bg-card);border:1px solid var(--border);border-radius:8px;padding:.6rem .9rem;margin-bottom:.75rem;display:flex;align-items:center;gap:.6rem;flex-wrap:wrap;font-size:.7rem}.prev span{color:var(--text-dim);font-size:.6rem}.prev a{font-size:.62rem;text-decoration:none;padding:.25rem .55rem;border-radius:5px;white-space:nowrap}
.steps-row{display:flex;gap:2px;margin-bottom:1rem;background:var(--bg-card);border:1px solid var(--border);border-radius:8px;padding:3px}
.sb{flex:1;display:flex;align-items:center;gap:.35rem;padding:.42rem .45rem;border:none;background:none;color:var(--text-dim);font-family:inherit;font-size:.63rem;font-weight:600;cursor:pointer;border-radius:6px}.sb:hover{color:var(--text);background:var(--border-light)}.sb.active{color:var(--accent);background:rgba(88,166,255,.06)}.sb.done{color:var(--green)}
.sb .si{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:.56rem;font-weight:800;border:2px solid currentColor;flex-shrink:0}.sb.active .si{background:var(--accent);color:#fff;border-color:var(--accent)}.sb.done .si{background:var(--green);color:#fff;border-color:var(--green)}
.qc{background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:1.3rem;animation:fadeIn .4s ease}.qc h2{font-size:1rem;font-weight:700;color:var(--text);margin-bottom:2px}.qc .sub{font-size:.68rem;color:var(--text-muted);margin-bottom:1rem}
.qg{display:grid;grid-template-columns:1fr 1fr;gap:.65rem}@media(max-width:600px){.qg{grid-template-columns:1fr}}
.qf{display:flex;flex-direction:column;gap:2px}.qf.full{grid-column:1/-1}
.qf label{font-size:.6rem;font-weight:700;color:var(--text-dim);text-transform:uppercase;letter-spacing:.3px}
.qf input,.qf select,.qf textarea{padding:.4rem .55rem;background:var(--bg);border:1px solid var(--border);border-radius:5px;color:var(--text);font-size:.73rem;font-family:inherit;width:100%}
.qf input:focus,.qf select:focus,.qf textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 2px rgba(88,166,255,.08)}.qf textarea{resize:vertical;min-height:60px}.qf .hint{font-size:.55rem;color:var(--text-dim)}
.qa{display:flex;justify-content:space-between;align-items:center;margin-top:1.2rem;gap:.4rem}.qa .step-num{font-size:.62rem;color:var(--text-dim)}
.btn{display:inline-flex;align-items:center;gap:.3rem;padding:.4rem .85rem;border-radius:6px;font-size:.7rem;font-weight:600;cursor:pointer;border:none;font-family:inherit;text-decoration:none;transition:all .12s}
.btn-o{background:var(--bg);color:var(--text-muted);border:1px solid var(--border)}.btn-o:hover{color:var(--text)}.btn-p{background:var(--accent);color:#fff}.btn-p:hover{background:var(--accent-hover)}.btn-g{background:var(--green);color:#fff}.btn-g:hover{filter:brightness(1.1)}
.res-h{text-align:center;padding:1.5rem;background:linear-gradient(135deg,var(--bg-card),rgba(88,166,255,.03));border:1px solid var(--border);border-radius:10px;margin-bottom:.75rem}
.res-h h2{font-size:1.2rem;font-weight:800;color:var(--text);margin-bottom:3px}.res-h .rp{font-size:.68rem;color:var(--text-muted)}.res-h .tier{display:inline-flex;gap:.2rem;margin-top:5px}
.tb{padding:.12rem .5rem;border-radius:999px;font-size:.62rem;font-weight:700}.tb.a{background:var(--green-bg);color:var(--green)}.tb.b{background:var(--orange-bg);color:var(--orange)}.tb.c{background:var(--red-bg);color:var(--red)}
.sec{margin-top:1rem}.sec h3{font-size:.78rem;font-weight:700;color:var(--text);margin-bottom:.45rem}
.tg{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:.35rem}
.ti{background:var(--bg);border:1px solid var(--border-light);border-radius:6px;padding:.5rem .65rem}.ti .tl{font-size:.54rem;color:var(--text-dim);text-transform:uppercase;letter-spacing:.3px;font-weight:700;margin-bottom:2px}.ti .tv{font-size:.7rem;color:var(--text);font-weight:600}
.ph{background:var(--bg-card);border:1px solid var(--border);border-radius:8px;padding:.8rem 1rem;margin-bottom:.4rem;border-left:3px solid var(--accent)}.ph h4{font-size:.76rem;font-weight:700;color:var(--text);margin-bottom:.25rem}.ph ul{list-style:none}.ph li{padding:.12rem 0;font-size:.68rem;color:var(--text-muted);display:flex;gap:.3rem}.ph li::before{content:'→';color:var(--accent);font-weight:700;flex-shrink:0}.ph.note{border-left-color:var(--orange)}.ph.note.red{border-left-color:var(--red)}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.5}}
/* Agent */
.agent-float{position:fixed;bottom:1.2rem;right:1.2rem;width:46px;height:46px;border-radius:50%;background:linear-gradient(135deg,var(--accent),var(--purple));color:#fff;border:none;cursor:pointer;font-size:1.1rem;box-shadow:0 4px 16px rgba(88,166,255,.2);z-index:50;transition:transform .15s}
.agent-float:hover{transform:scale(1.06)}
.agent-popup{display:none;position:fixed;bottom:4.5rem;right:1rem;width:350px;max-height:440px;background:var(--bg-card);border:1px solid var(--border);border-radius:10px;z-index:49;flex-direction:column;box-shadow:0 8px 40px rgba(0,0,0,.3)}
.agent-popup.open{display:flex}.agent-popup .ah{border-bottom:1px solid var(--border);padding:.5rem .7rem;display:flex;align-items:center;gap:.35rem;font-size:.68rem;font-weight:700;color:var(--text)}.agent-popup .ah .dot{width:6px;height:6px;border-radius:50%;background:var(--green);animation:pulse 2s ease infinite}.agent-popup .ah span{flex:1}.agent-popup .ah button{background:none;border:none;color:var(--text-dim);cursor:pointer;font-size:.9rem}
.agent-popup .ab{flex:1;overflow-y:auto;padding:.5rem;display:flex;flex-direction:column;gap:.4rem;max-height:300px}
.agent-popup .af{border-top:1px solid var(--border);padding:.45rem;display:flex;gap:.25rem}.agent-popup .af input{flex:1;padding:.3rem .45rem;background:var(--bg);border:1px solid var(--border);border-radius:5px;color:var(--text);font-size:.66rem;font-family:inherit}.agent-popup .af input:focus{outline:none;border-color:var(--accent)}.agent-popup .af button{background:var(--accent);color:#fff;border:none;border-radius:5px;padding:.3rem .55rem;font-size:.63rem;font-weight:600;cursor:pointer}
.am{padding:.4rem .55rem;border-radius:6px;font-size:.64rem;line-height:1.4;max-width:88%}.am.user{align-self:flex-end;background:var(--accent);color:#fff}.am.agent{align-self:flex-start;background:var(--border-light);color:var(--text)}.am .who{display:block;font-size:.55rem;color:var(--text-dim);margin-bottom:2px;font-weight:600}
</style></head><body>
<nav class="navbar"><div class="nav-inner"><a href="/" class="nav-brand"><img src="/img/logo.svg" class="nav-logo" alt="Mek-Tech"></a><div style="display:flex;align-items:center;gap:.4rem"><a href="/" class="nav-icon-btn" title="Dashboard"><i data-lucide="layout-dashboard" style="width:15px;height:15px"></i> Dashboard</a><button class="theme-toggle" id="themeToggle" onclick="toggleTheme()"></button><a href="/auth/logout" class="nav-icon-btn" style="color:var(--red)"><i data-lucide="log-out" style="width:15px;height:15px"></i> Uitloggen</a></div></div></nav>
<div class="main-area" style="max-width:960px;margin:0 auto;padding:1.5rem 2rem 3rem">
<div class="consult-page">
<a href="javascript:history.back()" class="back-link"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6"/></svg> Back</a>
<h1>Enterprise Solution Architect</h1>
<p class="sub">Connect a client, complete the 5-step assessment, generate a full architecture roadmap</p>
<% if (saved) { %><div class="alert">✓ Assessment saved to <strong><%= client ? client.name : 'client' %></strong></div><% } %>
<div class="cp-bar">
<select id="cs" onchange="selectClient(this.value)"><option value="">— Select a client —</option><% clients.forEach(function(c){ %><option value="<%= c.id %>" <%= String(c.id)===String(clientId)?'selected':'' %>><%= c.name %></option><% }) %></select>
<% if(clientId){ %><span class="cp-on">Connected</span><a href="/clients/<%= clientId %>" class="cp-link">View Client</a><% } %>
</div>
<% if (prevAssessment && !loadedAnswers) { %>
<div class="prev"><strong style="color:var(--text)">Previous assessment</strong> <span><%= new Date(prevAssessment.created_at).toLocaleString('en-US') %></span>
<a href="/consulting/report/<%= prevAssessment.id %>" class="cp-link">View</a>
<a href="/consulting?client_id=<%= clientId %>" class="cp-link" style="border-color:var(--accent);color:var(--accent)">New</a></div><% } %>
<div id="stepsRow" class="steps-row"></div>
<div id="qArea"></div>
<div id="results" style="display:none"></div>
</div></div>
<button class="agent-float" onclick="toggleAgent()">🧠</button>
<div class="agent-popup" id="apop"><div class="ah"><span class="dot"></span><span>AI Consultant</span><button onclick="toggleAgent()">×</button></div><div class="ab" id="amsgs"><div class="am agent"><span class="who">Agent</span>Hello! I analyze your assessment in real-time. Ask about industry benchmarks, data lake sizing, AI platforms, budgets, or roadmaps.</div></div><div class="af"><input id="ainp" placeholder="Ask me anything..." onkeydown="if(event.keyCode===13)send()"><button onclick="send()">Send</button></div></div>
<script src="/js/theme.js"></script>
<script>
const STEPS=[{title:"Company Profile",sub:"Organization details & context",flds:[{n:'company',l:'Organization Name',t:'text',r:true,h:'Full legal/brand name'},{n:'industry',l:'Industry',t:'sel',o:['Banking & Finance','Insurance','Healthcare','Retail & E-commerce','Manufacturing','Telecom & Media','Energy & Utilities','Government','Education & Research','Technology & SaaS','Other'],r:true},{n:'employees',l:'Workforce Size',t:'sel',o:['1-50','51-200','201-1000','1001-5000','5001-20000','20000+']},{n:'challenge',l:'Primary Challenge',t:'area',r:true,h:'Key business/technical problem to solve'},{n:'tech',l:'Current Tech Stack',t:'area',h:'E.g. Oracle on-prem, Power BI, manual Excel'}]},{title:"Infrastructure",sub:"Current infrastructure landscape",flds:[{n:'infra',l:'Infrastructure Model',t:'sel',o:['Fully on-premise','Hybrid (on-prem+cloud)','Single cloud','Multi-cloud','Fully cloud-native'],r:true},{n:'servers',l:'Server Footprint',t:'sel',o:['< 10','10-50','50-200','200-1000','1000+']},{n:'cloud',l:'Cloud Provider',t:'sel',o:['None','AWS','Azure','GCP','Multi-cloud']},{n:'virt',l:'Virtualization',t:'sel',o:['VMware vSphere','Proxmox','Hyper-V','Docker/K8s','Bare metal']},{n:'net',l:'Network Fabric',t:'sel',o:['1GbE','10GbE','25GbE','40GbE','100GbE+']}]},{title:"Data & Analytics",sub:"Data landscape & tooling",flds:[{n:'dvol',l:'Data Volume',t:'sel',o:['< 100 GB','100 GB - 1 TB','1-10 TB','10-100 TB','100 TB - 1 PB','> 1 PB'],r:true},{n:'dgrowth',l:'Monthly Growth',t:'sel',o:['< 1%','1-5%','5-15%','15-30%','> 30%']},{n:'dtypes',l:'Data Variety',t:'sel',o:['Structured only','Structured + semi-structured','All types (streaming,IoT)']},{n:'bi',l:'BI/Analytics Tools',t:'sel',o:['Excel only','Power BI','Tableau','Looker','SAP Analytics Cloud','Multiple']},{n:'gov',l:'Data Governance',t:'sel',o:['None','Basic access control','GDPR/AVG','GDPR+sector','Full enterprise']}]},{title:"AI & Machine Learning",sub:"AI maturity & goals",flds:[{n:'aimat',l:'AI Maturity',t:'sel',o:['No AI/ML','Basic reporting','PoC ML','Production ML','Advanced AI (LLMs,CV)'],r:true},{n:'aigoal',l:'AI Goal',t:'sel',o:['Predictive analytics','NLP/chatbots/LLMs','Computer vision','Recommendation engines','Generative AI','Process automation','Exploring']},{n:'aiteam',l:'AI Team Size',t:'sel',o:['None','1-3','4-10','10-30','30+']},{n:'aipref',l:'AI Model Preference',t:'sel',o:['Open-source (Llama,DeepSeek)','Cloud AI services','Proprietary (OpenAI,Anthropic)','Hybrid','Undecided']}]},{title:"Requirements",sub:"Performance, security, budget & timeline",flds:[{n:'perf',l:'Performance',t:'sel',o:['Standard','Low latency','Real-time/streaming','Batch']},{n:'avail',l:'Availability SLA',t:'sel',o:['99%','99.5%','99.9%','99.99%','99.999%']},{n:'sec',l:'Security Level',t:'sel',o:['Standard','Enhanced (Zero Trust)','High (HSM,encryption)','Mission critical']},{n:'budget',l:'Annual IT Budget',t:'sel',o:['< €50K','€50K-€250K','€250K-€1M','€1M-€5M','> €5M']},{n:'timeline',l:'Timeline',t:'sel',o:['ASAP (<3 months)','3-6 months','6-12 months','12-24 months','Flexible/phased']}]}];
let step=0,ans={},cid='<%= clientId||"" %>';
<% if(loadedAnswers){ %>ans=<%- JSON.stringify(loadedAnswers) %>;<% } %>
<% if(loadedReport){ %>
document.getElementById('stepsRow').style.display='none';
document.getElementById('results').innerHTML=buildRes(<%- JSON.stringify(loadedReport) %>,ans)+'<div class="qa" style="margin-top:1rem"><a href="/consulting?client_id='+cid+'" class="btn btn-o">New Assessment</a></div>';
document.getElementById('results').style.display='block';
<% }else{ %>rStep(0);<% } %>
function rStep(i){step=i;const s=STEPS[i];
let h='';STEPS.forEach((x,j)=>{h+='<button class="sb'+(j===step?' active':j<step?' done':'')+'" onclick="goStep('+j+')"><span class="si">'+(j+1)+'</span>'+(window.innerWidth>600?x.title:'')+'</button>'});
document.getElementById('stepsRow').innerHTML=h;
h='<div class="qc"><h2>'+s.title+'</h2><div class="sub">'+s.sub+'</div><div class="qg">';
s.flds.forEach(f=>{const v=ans[f.n]||'';h+='<div class="qf'+(f.t==='area'?' full':'')+'"><label>'+f.l+'</label>';
if(f.t==='sel'){h+='<select name="'+f.n+'" onchange="sv()"><option value="">— Select —</option>';f.o.forEach(o=>h+='<option value="'+o+'"'+(v===o?' selected':'')+'>'+o+'</option>');h+='</select>'}
else if(f.t==='area')h+='<textarea name="'+f.n+'" onchange="sv()" rows="2">'+v+'</textarea>';
else h+='<input type="text" name="'+f.n+'" onchange="sv()" value="'+v+'">';
if(f.h)h+='<span class="hint">'+f.h+'</span>';h+='</div>'});
h+='</div><div class="qa"><span class="step-num">Step '+(i+1)+' of '+STEPS.length+'</span><div style="display:flex;gap:.4rem">';
if(i>0)h+='<button class="btn btn-o" onclick="prev()">Back</button>';
h+=i<STEPS.length-1?'<button class="btn btn-p" onclick="next()">Next</button>':'<button class="btn btn-g" onclick="finish()">Generate Solution</button>';
h+='</div></div></div>';document.getElementById('qArea').innerHTML=h;document.getElementById('qArea').scrollIntoView({behavior:'smooth',block:'start'})}
function sv(){STEPS[step].flds.forEach(f=>{const el=document.querySelector('[name="'+f.n+'"]');if(el)ans[f.n]=el.value})}
function prev(){sv();if(step>0)rStep(step-1)}
function goStep(i){if(i<=step){sv();rStep(i)}}
function next(){sv();const req=STEPS[step].flds.filter(f=>f.r);let ok=true;req.forEach(f=>{if(!ans[f.n]||!ans[f.n].trim()){const el=document.querySelector('[name="'+f.n+'"]');if(el)el.style.borderColor='#f85149';ok=false}});if(!ok)return;if(step<STEPS.length-1)rStep(step+1)}
function selectClient(v){window.location.href=v?'/consulting?client_id='+v:'/consulting'}
function finish(){sv();if(!cid){addMsg('agent','Connect to a client first.');return}const req=STEPS[4].flds.filter(f=>f.r);let ok=true;req.forEach(f=>{if(!ans[f.n]||!ans[f.n].trim())ok=false});if(!ok){addMsg('agent','Fill in all required fields.');return}const rpt=genRpt();fetch('/consulting/save',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({client_id:parseInt(cid),answers:ans,report:rpt})}).then(r=>r.json()).then(()=>{document.getElementById('qArea').style.display='none';document.getElementById('results').innerHTML=buildRes(rpt,ans)+'<div class="qa" style="margin-top:1rem"><a href="/consulting?client_id='+cid+'" class="btn btn-o">New Assessment</a><a href="/clients/'+cid+'" class="btn btn-p" style="margin-left:auto">View Client →</a></div>';document.getElementById('results').style.display='block';document.getElementById('results').scrollIntoView({behavior:'smooth'});addMsg('agent','✅ Architecture solution generated & saved!')}).catch(e=>addMsg('agent','Error: '+e.message))}
function genRpt(){let dwT='Standard',aiT='Standard',inT='Standard';const dv=(ans.dvol||''),vg=(ans.aigoal||''),am=(ans.aimat||''),it=(ans.infra||''),bg=(ans.budget||'');if(dv.includes('PB')||dv.includes('100 TB'))dwT='Enterprise';else if(dv.includes('10 TB'))dwT='Advanced';if(vg.includes('Generative')||vg.includes('LLM')||am.includes('Advanced'))aiT='Enterprise';else if(vg.includes('NLP')||vg.includes('Computer')||am.includes('Production'))aiT='Advanced';if(bg.includes('5M')||bg.includes('1M')||it.includes('Multi-cloud'))inT='Enterprise';else if(bg.includes('250K')||it.includes('Hybrid'))inT='Advanced';let dwS={},aiS={},inS={};
if(dwT==='Enterprise')dwS={Lakehouse:'MinIO + Iceberg + Kubernetes','Query Engine':'Spark Streaming + Trino + dbt',Catalog:'Hive Metastore + DataHub',Governance:'Apache Atlas + Ranger',Storage:'S3 (MinIO multi-site)'};
else if(dwT==='Advanced')dwS={Lakehouse:'MinIO + Iceberg + K8s','Query Engine':'Spark + Trino + dbt',Catalog:'Hive Metastore',Governance:'Apache Ranger',Storage:'MinIO/S3'};
else dwS={Lakehouse:'MinIO/S3 + PostgreSQL','Query Engine':'Spark batch + dbt',Catalog:'Basic metadata',Governance:'IAM + audit',Storage:'MinIO'};
if(aiT==='Enterprise')aiS={Serving:'vLLM + SGLang (multi-GPU)',Orchestrator:'Kubeflow + MLflow + Ray',Monitoring:'Prometheus + Grafana + Evidently',Frameworks:'PyTorch + LangChain + LlamaIndex',GPU:'NVIDIA H100 / L40S'};
else if(aiT==='Advanced')aiS={Serving:'Ollama + vLLM',Orchestrator:'MLflow + Prefect',Monitoring:'Grafana + MLflow',Frameworks:'PyTorch + scikit-learn + XGBoost',GPU:'NVIDIA L40S / A10'};
else aiS={Serving:'Ollama / HuggingFace',Orchestrator:'MLflow basic',Monitoring:'Streamlit',Frameworks:'scikit-learn + XGBoost',GPU:'CPU + optional GPU'};
if(inT==='Enterprise')inS={Compute:'vSphere + Rancher/K8s + bare metal',Network:'100GbE leaf-spine','Storage':'Pure Storage + NetApp AFF','Sec/DR':'Zero Trust + HSM + Veeam',Obs:'Grafana + Loki + Tempo'};
else if(inT==='Advanced')inS={Compute:'vSphere + Proxmox + Docker',Network:'25GbE','Storage':'NetApp FAS + MinIO','Sec/DR':'Zero Trust + Veeam',Obs:'Grafana + Prometheus'};
else inS={Compute:'Proxmox VE + Docker',Network:'10GbE','Storage':'TrueNAS + MinIO','Sec/DR':'pfSense + PBS',Obs:'Grafana + Netdata'};
let g=dwT==='Standard'&&aiT==='Standard'?'C':(dwT==='Enterprise'||aiT==='Enterprise'?'A':'B');return{dwT,aiT,inT,grade:g,dwS,aiS,inS}}
function buildRes(rpt,a){let h='<div class="res-h"><h2>'+(a.company||'Organization')+'</h2><div class="rp">'+a.industry+' · '+a.employees+' · '+a.budget+' · '+a.timeline+'</div><div class="tier"><span class="tb '+rpt.grade.toLowerCase()+'">Grade: '+rpt.grade+'</span><span class="tb b">Data: '+rpt.dwT+'</span><span class="tb b">AI: '+rpt.aiT+'</span></div></div>';h+='<div class="sec"><h3>Data Lakehouse — '+rpt.dwT+'</h3><div class="tg">';for(const[k,v] of Object.entries(rpt.dwS))h+='<div class="ti"><div class="tl">'+k+'</div><div class="tv">'+v+'</div></div>';h+='</div></div><div class="sec"><h3>AI/ML Platform — '+rpt.aiT+'</h3><div class="tg">';for(const[k,v] of Object.entries(rpt.aiS))h+='<div class="ti"><div class="tl">'+k+'</div><div class="tv">'+v+'</div></div>';h+='</div></div><div class="sec"><h3>Infrastructure — '+rpt.inT+'</h3><div class="tg">';for(const[k,v] of Object.entries(rpt.inS))h+='<div class="ti"><div class="tl">'+k+'</div><div class="tv">'+v+'</div></div>';h+='</div></div><div class="sec"><h3>Implementation Roadmap</h3>';[{t:'Phase 1: Foundation (0-3m)',i:['Infrastructure audit & baseline','PoC: Data lake on MinIO + Spark','AI readiness assessment','Security baseline','Team training: data engineering']},{t:'Phase 2: Build (3-6m)',i:['Lakehouse implementation (Iceberg+Trino+dbt)','Data catalog & governance rollout','CI/CD pipeline (GitOps)','ML platform (MLflow)','Data pipeline migration']},{t:'Phase 3: Scale (6-12m)',i:['AI model serving (vLLM/SGLang)','Automated retraining + monitoring','Real-time streaming (Kafka/Flink)','Self-service analytics','FinOps implementation']},{t:'Phase 4: Optimize (12m+)',i:['Multi-region replication','Advanced AI (RAG, agents)','Data mesh evaluation','Enterprise observability','Continuous optimization']}].forEach(p=>{h+='<div class="ph"><h4>'+p.t+'</h4><ul>';p.i.forEach(it=>h+='<li>'+it+'</li>');h+='</ul></div>'});return h+'</div>'}
function toggleAgent(){document.getElementById('apop').classList.toggle('open')}
function addMsg(role,txt){const c=document.getElementById('amsgs');const d=document.createElement('div');d.className='am '+role;d.innerHTML=(role==='agent'?'<span class="who">Agent</span>':'')+txt;c.appendChild(d);c.scrollTop=c.scrollHeight}
function send(){const i=document.getElementById('ainp');const t=i.value.trim();if(!t)return;addMsg('user',t);i.value='';setTimeout(()=>agentReply(t),300)}
function agentReply(q){const ql=q.toLowerCase();let r='';const ind=(ans.industry||'').toLowerCase();const kb={banking:'Azure Synapse + Databricks + Snowflake (DW), Azure ML + risk models (AI), Cisco/HPE/Oracle (Infra). PSD2/DORA compliance required.',insurance:'Azure Synapse + Teradata (DW), Azure ML + SAS (AI), Cisco/Dell/NetApp (Infra). Solvency II compliance.',healthcare:'Databricks + Snowflake + Azure (DW), PyTorch + MONAI (AI), Dell/HPE/Pure (Infra). HIPAA/NEN 7510 compliance.',retail:'BigQuery + dbt + Databricks (DW), Vertex AI + Python (AI), GCP/Cisco/Dell (Infra). Real-time personalization.',manufacturing:'Azure Synapse + SAP BW + Databricks (DW), Azure ML + CV (AI). IoT & edge computing.',telecom:'Snowflake + Oracle + dbt (DW), Vertex AI + Python (AI), Nokia/Cisco/Dell (Infra). 5G & streaming.',energy:'Azure Synapse + Databricks (DW), TensorFlow + Python (AI). Smart grid & IoT.',government:'Oracle Exadata + Azure Synapse (DW), Azure ML + Python (AI), Cisco/Dell/NetApp (Infra).'};(ql.includes('industry')||ql.includes('sector'))?(Object.entries(kb).find(([k])=>ind.includes(k))?(r='<b>'+ans.industry+'</b>: '+Object.entries(kb).find(([k])=>ind.includes(k))[1]):(r='Tell me the industry (Step 1) to get benchmarks.')):(ql.includes('data')||ql.includes('lake'))?(r=(dv=ans.dvol||'').includes('PB')||dv.includes('100 TB')?'Enterprise lakehouse: MinIO + Iceberg + Spark Streaming + Trino + dbt on K8s. Multi-site S3 storage.':dv.includes('10 TB')?'Advanced: MinIO + Iceberg + Spark + dbt on K8s.':dv?'Standard: MinIO/S3 + Spark batch + dbt.':'What is your data volume? (Step 3)'):(ql.includes('ai')||ql.includes('ml')||ql.includes('model'))?(r=(ans.aigoal||'')?(ans.aigoal.includes('Generative')||ans.aigoal.includes('LLM')?'Enterprise AI: vLLM + SGLang on H100 GPUs, LangChain + LlamaIndex, Kubeflow + MLflow.':ans.aigoal.includes('NLP')||ans.aigoal.includes('Computer')?'Advanced: Ollama/vLLM on L40S, MLflow + PyTorch.':'Standard: scikit-learn + XGBoost + Streamlit. CPU + optional GPU.'):'What is your AI goal? (Step 4)'):(ql.includes('budget')||ql.includes('cost'))?(r=(ans.budget||'')?(ans.budget.includes('5M')||ans.budget.includes('1M')?'Enterprise: 100GbE, multi-GPU, Pure+NetApp, Zero Trust. €1-5M/yr.':ans.budget.includes('250K')?'Advanced: 25GbE, 1-2 GPUs, NetApp+MinIO. €250K-1M/yr.':'Standard: 10GbE, Proxmox+Docker, TrueNAS+MinIO.'):'What is your budget? (Step 5)'):(ql.includes('help')||ql.includes('guide'))?r='Ask about: <b>industry benchmarks</b>, <b>data lake size</b>, <b>AI models</b>, <b>budget planning</b>, or <b>roadmap</b>.':r='Try asking: <b>"data lake"</b>, <b>"AI platform"</b>, <b>"budget"</b>, or <b>"help"</b>.';addMsg('agent',r)}
rStep(0);
</script>
</body></html>