diff --git a/docs/cabinet.html b/docs/cabinet.html
index 511a954..88b20da 100644
--- a/docs/cabinet.html
+++ b/docs/cabinet.html
@@ -285,6 +285,8 @@ body{font-family:'Inter',sans-serif;background:var(--bg);color:var(--text);displ
💬 Спросить Елену
об этом этапе
@@ -393,6 +406,7 @@ function go(n){
document.getElementById('si'+n).classList.add('active');
if(PCTS[n]!=null){document.getElementById('pbPct').textContent=PCTS[n]+'%';document.getElementById('pbFill').style.width=PCTS[n]+'%';}
if(n===1)requestAnimationFrame(scrollChatBottom); // sv1 уже видим — высота посчитана, прыгаем вниз без «пролёта»
+ if(n===6)renderOpChat();
if(n===3)renderDocs();
if(n===4)renderAnalysis();
if(n===5)renderSpecPane();
@@ -447,6 +461,12 @@ function addMsg(role,text){const m=document.createElement("div");m.className="ms
function showTyping(){const t=document.createElement("div");t.className="msg";t.id="typing";t.innerHTML=`
Е
`;chat.appendChild(t);scrollChatBottom()}
function hideTyping(){const t=document.getElementById("typing");if(t)t.remove()}
+/* ── Канал «Консультант» (живой) ── */
+function opMsg(role,text){const c=document.getElementById("opChat");if(!c)return;const m=document.createElement("div");m.className="msg "+(role==="user"?"user":"");m.innerHTML=`
${role==='user'?'Я':'Р'}
${fmt(text)}
`;c.appendChild(m);const sc=c.parentElement;if(sc)sc.scrollTop=sc.scrollHeight;}
+function renderOpChat(){const c=document.getElementById("opChat");if(!c)return;c.innerHTML="";const m=state&&state.operator_chat||[];if(!m.length){c.innerHTML='
Напишите консультанту — Руслан ответит здесь и пришлёт уведомление в Telegram.
';return;}m.forEach(x=>opMsg(x.role==="user"?"user":"elena",x.content));}
+async function opSend(){const inp=document.getElementById("opInp");const t=inp.value.trim();if(!t)return;inp.value="";inp.style.height="auto";opMsg("user",t);state.operator_chat=state.operator_chat||[];state.operator_chat.push({role:"user",content:t});
+ try{const r=await fetch(`${API}/api/operator-chat`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({token,message:t})});const d=await r.json();if(d.messages)state.operator_chat=d.messages;}catch(e){opMsg("elena","Не доставлено: "+e.message);}
+}
/* ── Спросить Елену (этапы 3-5) ── */
const STAGE_LBL={3:"о документах",4:"о стратегии и модели",5:"о ТЗ и плане"};
function toggleAsk(){document.getElementById("askDock").classList.toggle("open")}
diff --git a/docs/crm.html b/docs/crm.html
index 248d20b..5428446 100644
--- a/docs/crm.html
+++ b/docs/crm.html
@@ -507,7 +507,7 @@ async function kDrop(e,pipe){
await loadProjects();renderPipeline();
}
-const MAINTABS=[{id:"deal",name:"Сделка",icon:"📇"},{id:"pricing",name:"Ценообразование",icon:"💰"},{id:"payments",name:"Платежи",icon:"💳"},{id:"tasks",name:"Задачи",icon:"📌"},{id:"docs",name:"Документы",icon:"📎"},{id:"analysis",name:"Анализ",icon:"📊"},{id:"deviations",name:"Отклонения",icon:"⚠️"}];
+const MAINTABS=[{id:"deal",name:"Сделка",icon:"📇"},{id:"chat",name:"Чат с клиентом",icon:"💬"},{id:"pricing",name:"Ценообразование",icon:"💰"},{id:"payments",name:"Платежи",icon:"💳"},{id:"tasks",name:"Задачи",icon:"📌"},{id:"docs",name:"Документы",icon:"📎"},{id:"analysis",name:"Анализ",icon:"📊"},{id:"deviations",name:"Отклонения",icon:"⚠️"}];
function renderClient(){
const crm=state.crm||{pipeline:"lead",deal_amount:0,paid_amount:0,contact:"",source:"",note:""};
const billing=crm.billing_type||"paid";
@@ -515,7 +515,8 @@ function renderClient(){
const today=new Date().toISOString().slice(0,10);
const overdue=(state.tasks||[]).filter(t=>!t.done&&t.due&&t.due
{if(id==="payments"&&deal>0&&left>0)return `${money(left)} `;if(id==="tasks"&&overdue)return `${overdue} `;if(id==="deviations"&&devN)return `${devN} `;if(id==="docs"&&docN)return `${docN} `;return ''};
+ const ocUnread=(state.operator_chat||[]).filter(m=>m.role==='user').length-(+localStorage.getItem('opseen_'+current)||0);
+ const badge=id=>{if(id==="payments"&&deal>0&&left>0)return `${money(left)} `;if(id==="tasks"&&overdue)return `${overdue} `;if(id==="deviations"&&devN)return `${devN} `;if(id==="docs"&&docN)return `${docN} `;if(id==="chat"&&ocUnread>0)return `${ocUnread} `;return ''};
document.getElementById("view").innerHTML=`
${esc((state.client_name||'?')[0])}
${esc(state.client_name||'Без имени')}
${esc(state.niche||'')} · ${state.messages.length} сообщений
@@ -552,6 +553,32 @@ async function opUpload(files){
renderClient();
}
function opDropFiles(e){e.preventDefault();e.currentTarget.style.background="#fff";opUpload(e.dataTransfer.files);}
+// ── Чат с клиентом (живой канал «Консультант») ──
+function opBubble(m){const me=m.role==='assistant';return `
${me?'Я':'К'}
${esc(m.content)}
`;}
+function renderOpChatTab(){
+ const box=document.getElementById("opChatBox");if(!box)return;
+ const msgs=state.operator_chat||[];
+ box.innerHTML=`
Живой канал с клиентом. Он видит это в кабинете → «💬 Консультант» и получает уведомление в Telegram.
+
+
+ ${msgs.length?msgs.map(opBubble).join(''):'
Сообщений нет. Напишите клиенту первым.
'}
+
+
+
+ ${micBtn('opChatInp',36)}
+ Отправить
+
`;
+ const t=document.getElementById('opThread');if(t)t.scrollTop=t.scrollHeight;
+}
+async function opChatSend(){
+ const inp=document.getElementById('opChatInp');const t=inp.value.trim();if(!t)return;
+ inp.value='';
+ try{const r=await fetch(`${API}/api/operator-chat`,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({token:current,message:t})});const d=await r.json();
+ if(d.error){toast('Ошибка: '+d.error,'err');return;}
+ state.operator_chat=d.messages||state.operator_chat;renderOpChatTab();
+ localStorage.setItem('opseen_'+current,(state.operator_chat||[]).filter(m=>m.role==='user').length);
+ }catch(e){toast('Ошибка: '+e.message,'err');}
+}
const DEV_STAGE={canvas:"📊 Стратегия",idef0:"🔧 Функции",spec:"📋 ТЗ",documents:"📁 Документы",methods:"🎯 Методологии",interview:"💬 Интервью"};
function renderDeviations(){
const dev=state.deviations||[];
@@ -634,6 +661,7 @@ function renderMainPanel(){
else if(mainTab==="tasks"){p.innerHTML=`
`;renderTasks();}
else if(mainTab==="analysis"){p.innerHTML=`
${TABS.map(t=>`
${t.icon} ${t.name}
`).join("")}
`;renderTab();}
else if(mainTab==="docs"){p.innerHTML=renderDocsTab();}
+ else if(mainTab==="chat"){p.innerHTML=`
`;renderOpChatTab();localStorage.setItem('opseen_'+current,(state.operator_chat||[]).filter(m=>m.role==='user').length);document.querySelectorAll('.mtab').forEach((b,i)=>{if(MAINTABS[i].id==='chat'){const bd=b.querySelector('.badge');if(bd)bd.remove();}});}
else if(mainTab==="deviations"){p.innerHTML=renderDeviations();}
}
function inviteTelegram(){const url=`https://t.me/wasrusgen1_consulting_bot?start=${current}`;navigator.clipboard.writeText(url).then(()=>alert("Ссылка для Telegram скопирована:\n\n"+url+"\n\nКлиент откроет кабинет прямо в Telegram.")).catch(()=>prompt("Ссылка для Telegram:",url));}