CTYPES: detect contract type after scan, show contextual comment + highlight recommended deliverable

This commit is contained in:
WASRUSGEN 2026-05-26 02:02:50 +03:00
parent bebc6bf463
commit fb19d6afd4

View File

@ -124,6 +124,9 @@ body{font-family:var(--font-ui);background:var(--surf);color:var(--ink);line-hei
.deliv-top{border-color:rgba(159,18,57,.3);background:var(--tint)}
.deliv-top .dn{color:var(--bg)}
.deliv-badge{font-size:10px;font-weight:700;background:var(--bg);color:#fff;border-radius:6px;padding:2px 7px;margin-left:auto;flex-shrink:0}
.deliv-highlighted{border-color:var(--bg)!important;background:linear-gradient(135deg,#fff 0%,rgba(159,18,57,.06) 100%)!important;box-shadow:0 0 0 2px rgba(159,18,57,.15)!important}
.deliv-highlighted .dn{color:var(--bg)!important}
.ctype-note{display:block;font-size:13px;color:var(--mut);line-height:1.5;margin-bottom:6px;padding:8px 10px;background:var(--surf);border-radius:8px;border-left:3px solid var(--bg)}
/* ── план после выбора ── */
.plan-what{background:var(--surf);border-radius:12px;padding:14px 16px;margin:16px 0;max-width:600px}
.plan-what-title{font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:1.2px;color:var(--mut);margin-bottom:10px}
@ -260,15 +263,16 @@ body{font-family:var(--font-ui);background:var(--surf);color:var(--ink);line-hei
.upload-box{max-width:560px;margin:0 auto;padding:0 0 24px}
/* ── SCAN ANIMATION ── */
.scan-wrap{display:flex;flex-direction:column;align-items:center;padding:52px 24px 60px;text-align:center}
.scan-doc-outer{position:relative;width:86px;height:112px;margin:0 auto 36px}
.scan-doc-card{width:86px;height:112px;background:var(--card);border:1.5px solid var(--line);border-radius:10px;overflow:hidden;position:relative;box-shadow:0 8px 28px rgba(0,0,0,.09)}
.scan-doc-card::before{content:'';position:absolute;left:12px;right:12px;top:16px;height:6px;background:var(--line);border-radius:3px;box-shadow:0 13px 0 var(--line),0 23px 0 var(--line),0 33px 0 var(--line),0 43px 0 var(--line),0 53px 0 var(--line),0 63px 0 rgba(159,18,57,.12)}
.scan-beam{position:absolute;left:0;right:0;height:2px;background:linear-gradient(90deg,transparent 0%,rgba(159,18,57,.25) 15%,var(--bg) 50%,rgba(159,18,57,.25) 85%,transparent 100%);box-shadow:0 0 10px 4px rgba(159,18,57,.3);animation:beamDown 1.7s ease-in-out infinite}
@keyframes beamDown{0%{top:-2px;opacity:0}7%{opacity:1}93%{opacity:1}100%{top:114px;opacity:0}}
.scan-av{position:absolute;bottom:-16px;right:-16px;width:40px;height:40px;border-radius:50%;border:3px solid var(--surf);object-fit:cover;object-position:center 14%;animation:avPulse 2s ease-in-out infinite}
@keyframes avPulse{0%,100%{box-shadow:0 0 0 0 rgba(159,18,57,.45)}60%{box-shadow:0 0 0 8px rgba(159,18,57,0)}}
.scan-label{font-size:18px;font-weight:700;color:var(--dark);min-height:28px;margin-bottom:7px}
.scan-wrap{display:flex;flex-direction:column;align-items:center;padding:48px 24px 56px;text-align:center}
.scan-doc-outer{position:relative;width:170px;height:220px;margin:0 auto 32px}
.scan-doc-card{width:170px;height:220px;background:var(--card);border:1.5px solid var(--line);border-radius:14px;overflow:hidden;position:relative;box-shadow:0 12px 40px rgba(0,0,0,.12)}
.scan-doc-card::before{content:'';position:absolute;left:22px;right:22px;top:22px;height:7px;background:var(--line);border-radius:4px;
box-shadow:0 16px 0 var(--line),0 29px 0 var(--line),0 42px 0 var(--line),0 55px 0 rgba(159,18,57,.15),0 68px 0 var(--line),0 81px 0 var(--line),0 94px 0 var(--line),0 107px 0 rgba(159,18,57,.1),0 120px 0 var(--line),0 133px 0 rgba(159,18,57,.08)}
.scan-beam{position:absolute;left:0;right:0;height:3px;background:linear-gradient(90deg,transparent 0%,rgba(159,18,57,.2) 10%,var(--bg) 50%,rgba(159,18,57,.2) 90%,transparent 100%);box-shadow:0 0 14px 5px rgba(159,18,57,.28);animation:beamDown 2s ease-in-out infinite;z-index:2}
@keyframes beamDown{0%{top:-3px;opacity:0}6%{opacity:1}94%{opacity:1}100%{top:223px;opacity:0}}
.scan-av{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:68px;height:68px;border-radius:50%;border:4px solid #fff;object-fit:cover;object-position:center 12%;box-shadow:0 4px 16px rgba(0,0,0,.18);z-index:3;animation:avPulse 2.2s ease-in-out infinite}
@keyframes avPulse{0%,100%{box-shadow:0 4px 16px rgba(0,0,0,.18),0 0 0 0 rgba(159,18,57,.4)}60%{box-shadow:0 4px 16px rgba(0,0,0,.18),0 0 0 10px rgba(159,18,57,0)}}
.scan-label{font-size:18px;font-weight:700;color:var(--dark);min-height:28px;margin-bottom:7px;transition:opacity .25s}
.scan-hint{font-size:13px;color:var(--mut);margin-bottom:20px}
.scan-dots{display:flex;gap:8px;justify-content:center}
.scan-dot{width:7px;height:7px;border-radius:50%;background:var(--bg);opacity:.3;animation:dotB 1.4s ease-in-out infinite}
@ -352,7 +356,8 @@ body{font-family:var(--font-ui);background:var(--surf);color:var(--ink);line-hei
<!-- ШАГ 2: результаты анализа (скрыт до сканирования) -->
<div id="el-step2" style="display:none">
<div class="msg"><div class="av"><img src="logos/elena-photo.jpg"></div><div class="bubble"><div class="nm">Елена</div>
Прочитала ваш договор 📄 Это <b>агентский договор на сборку мебели</b> (ЗОВ ↔ Агент).<br>
Прочитала ваш договор 📄 Это <b id="el-scan-type">агентский договор</b>.<br>
<span id="el-ctype-note" class="ctype-note"></span>
Нашла <b>12 моментов</b>, из них <b>5 критичных</b>. <span id="el-intro-tail"></span> Показываю 3:
<div class="risk-mini">
<div class="rn">п.1.1 · ст. 429 ЗоЗПП</div>
@ -373,22 +378,22 @@ body{font-family:var(--font-ui);background:var(--surf);color:var(--ink);line-hei
</div></div>
<div class="msg"><div class="av"><img src="logos/elena-photo.jpg"></div><div class="bubble"><div class="nm">Елена</div><span id="el-fork-q"></span></div></div>
<div class="deliverables">
<div class="deliv" onclick="selectDeliv('protocol')">
<div id="deliv-protocol" class="deliv" onclick="selectDeliv('protocol')">
<span class="di">📋</span>
<div><div class="dn">Протокол разногласий</div><div class="dd2">Список спорных пунктов + зачем менять каждый</div></div>
</div>
<div class="deliv" onclick="selectDeliv('redact')">
<div id="deliv-redact" class="deliv" onclick="selectDeliv('redact')">
<span class="di">✏️</span>
<div><div class="dn">Переработка с комментариями</div><div class="dd2">Новая редакция каждого пункта + пояснение изменений</div></div>
</div>
<div class="deliv" onclick="selectDeliv('clean')">
<div id="deliv-clean" class="deliv" onclick="selectDeliv('clean')">
<span class="di"></span>
<div><div class="dn">Чистая редакция</div><div class="dd2">Договор готов к подписанию — без лишних пояснений</div></div>
</div>
<div class="deliv deliv-top" onclick="selectDeliv('partner')">
<div id="deliv-partner" class="deliv deliv-top" onclick="selectDeliv('partner')">
<span class="di">🤝</span>
<div><div class="dn">Партнёрская редакция</div><div class="dd2">Вариант, который устроит обе стороны — без конфликта</div></div>
<span class="deliv-badge">Рекомендуем</span>
<span class="deliv-badge" id="deliv-rec-badge">Рекомендуем</span>
</div>
</div>
</div>
@ -671,6 +676,126 @@ function setMode(mode) {
window.scrollTo(0, 0);
}
/* ── ТИПЫ ДОГОВОРОВ ── */
const CTYPES = {
agent: {
emoji:'🤝', name:'агентский договор',
comment:'Агент действует от имени или за счёт другой стороны. Главные риски здесь — объём вашей ответственности перед третьими лицами и признаки трудовых отношений, которые могут всплыть при проверке.',
delivRec:'partner'
},
realty: {
emoji:'🏠', name:'договор купли-продажи недвижимости',
comment:'В недвижимости цена ошибки особенно высока. Проверяю обременения, условия передачи объекта и ответственность за скрытые дефекты — именно там чаще всего прячется главный риск.',
delivRec:'partner'
},
auto: {
emoji:'🚗', name:'договор купли-продажи автомобиля',
comment:'Смотрю на гарантии состояния, скрытые дефекты и условия передачи. Это три зоны, где покупатель чаще всего остаётся без защиты после подписания.',
delivRec:'clean'
},
construction: {
emoji:'🛋️', name:'договор подряда',
comment:'Договор подряда — здесь критичны сроки, критерии качества результата и ответственность за задержки. Разбираю ваши риски по каждому из этих блоков.',
delivRec:'redact'
},
services: {
emoji:'🎯', name:'договор оказания услуг',
comment:'В услугах часто размыт сам результат и ответственность исполнителя. Разберу что именно вы обязаны сделать и где вас можно поймать на формальном нарушении.',
delivRec:'redact'
},
labor: {
emoji:'📋', name:'трудовой договор',
comment:'Трудовой договор — смотрю на режим работы, зоны ответственности и условия расторжения. Здесь у работника есть обязательная защита по ТК, которую нельзя ужать без последствий.',
delivRec:'protocol'
},
loan: {
emoji:'💰', name:'договор займа',
comment:'Ключевые риски займа — скрытые проценты, штрафные санкции и условия досрочного требования. Часто самый опасный пункт написан мелким шрифтом в середине договора.',
delivRec:'redact'
},
supply: {
emoji:'📦', name:'договор поставки',
comment:'Поставка — проверяю условия приёмки, ответственность за качество товара и что происходит при просрочке. Именно здесь чаще всего возникают споры при исполнении.',
delivRec:'redact'
},
other: {
emoji:'📄', name:'договор',
comment:'Изучила структуру. Нашла пункты, которые стоит проверить внимательнее — показываю ключевые риски ниже.',
delivRec:null
}
};
const CTYPES_KEYWORDS = {
agent: ['агент','принципал','агентск','от имени и за счёт','за счёт принципала'],
realty: ['квартир','недвижим','жиль','комнат','нежилое','земельн','ипотек','объект недвижим','жилой дом'],
auto: ['автомобил','транспортн средств','машин','птс','пробег','кузов'],
construction: ['подряд','мебел','изготовл','монтаж','строител','ремонт','подрядчик','заказчик','результат работ'],
services: ['оказани','услуг','исполнител','заказчик','сервис','консульт','разработ','дизайн'],
labor: ['трудов','работодател','работник','должност','оклад','отпуск','увольнен','рабочее время'],
loan: ['займ','заём','кредит','процент годов','долг','заемщик','заёмщик','сумма займа'],
supply: ['поставщик','покупател','товар','партия','поставка','отгрузк','накладн']
};
let _demoCtypeIdx = 0;
const _demoSeq = ['agent','realty','auto','construction','services','labor','loan','supply'];
function detectCtype(text) {
if (text && text.length > 30) {
const t = text.toLowerCase();
for (const [key, kws] of Object.entries(CTYPES_KEYWORDS)) {
if (kws.some(kw => t.includes(kw))) return key;
}
return 'other';
}
// демо-режим: каждый клик — новый тип
const key = _demoSeq[_demoCtypeIdx % _demoSeq.length];
_demoCtypeIdx++;
return key;
}
function showResults(ctypeKey) {
// спрятать предыдущие шаги на случай прямого вызова
['el-step-upload','el-step-scan','el-step1'].forEach(id => {
const el = document.getElementById(id);
if (el) el.style.display = 'none';
});
const ctype = CTYPES[ctypeKey] || CTYPES.other;
// тип договора в первой реплике
document.getElementById('el-scan-type').textContent = ctype.emoji + ' ' + ctype.name;
const noteEl = document.getElementById('el-ctype-note');
noteEl.textContent = ctype.comment;
noteEl.style.display = ctype.comment ? 'block' : 'none';
// убрать старую подсветку, поставить новую
document.querySelectorAll('.deliv').forEach(el => {
el.classList.remove('deliv-highlighted');
});
// убрать badge у всех, потом поставить на нужный
document.querySelectorAll('.deliv-badge').forEach(b => b.style.display = 'none');
if (ctype.delivRec) {
const recEl = document.getElementById('deliv-' + ctype.delivRec);
if (recEl) {
recEl.classList.add('deliv-highlighted');
let badge = recEl.querySelector('.deliv-badge');
if (!badge) {
badge = document.createElement('span');
badge.className = 'deliv-badge';
recEl.appendChild(badge);
}
badge.textContent = 'Рекомендуем';
badge.style.display = '';
}
}
// показать результаты
document.getElementById('el-step-scan').style.display = 'none';
document.getElementById('el-step2').style.display = 'block';
document.getElementById('el-actbar').style.display = 'flex';
document.getElementById('el-step2').scrollIntoView({ behavior: 'smooth' });
}
/* ── СКАНИРОВАНИЕ ── */
const SCAN_PHRASES = [
'Читаю договор...',
@ -682,6 +807,9 @@ const SCAN_PHRASES = [
];
function startScan() {
const text = (document.getElementById('el-paste').value || '').trim();
const ctypeKey = detectCtype(text);
document.getElementById('el-step-upload').style.display = 'none';
document.getElementById('el-step-scan').style.display = 'block';
window.scrollTo(0, 0);
@ -697,12 +825,7 @@ function startScan() {
setTimeout(() => {
clearInterval(interval);
lbl.style.opacity = '0';
setTimeout(() => {
document.getElementById('el-step-scan').style.display = 'none';
document.getElementById('el-step2').style.display = 'block';
document.getElementById('el-actbar').style.display = 'flex';
document.getElementById('el-step2').scrollIntoView({ behavior: 'smooth' });
}, 200);
setTimeout(() => { showResults(ctypeKey); }, 200);
}, 4000);
}