wasrusgen1-crm/docs/presentation_architecture.html

887 lines
66 KiB
HTML
Raw 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="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Архитектура платформы @wasrusgen1 CRM</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=Montserrat:wght@700;800;900&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
<style>
:root{
--bg:#0F0F1A;
--accent:#003E7E;
--accent2:#4338CA;
--success:#76BD22;
--warn:#F59E0B;
--danger:#EF4444;
--text:#F8FAFC;
--card:#1A1A2E;
--muted:#94A3B8;
--border:rgba(148,163,184,.16);
--mono:'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,monospace;
--display:'Montserrat',sans-serif;
}
*{margin:0;padding:0;box-sizing:border-box}
html,body{height:100%}
body{
font-family:'Inter',system-ui,sans-serif;
background:var(--bg);
color:var(--text);
overflow:hidden;
}
/* ===== DECK ===== */
.deck{position:fixed;inset:0}
.slide{
position:absolute;inset:0;
display:flex;flex-direction:column;
padding:64px 88px 90px;
opacity:0;visibility:hidden;
transform:translateY(14px);
transition:opacity .5s ease,transform .5s ease,visibility .5s;
overflow:hidden;
}
.slide.active{opacity:1;visibility:visible;transform:none;z-index:2}
/* aspect frame to keep 16:9 readable on any screen */
.stage{
position:relative;width:100%;height:100%;
max-width:1920px;max-height:1080px;margin:auto;
}
/* per-slide brand watermark (top-right) */
.slide-brand{
position:absolute;top:0;right:0;z-index:5;display:flex;align-items:center;
pointer-events:none;
}
/* background glows */
.slide::before{
content:"";position:absolute;width:760px;height:760px;border-radius:50%;
background:radial-gradient(circle,rgba(67,56,202,.16),transparent 62%);
top:-300px;right:-200px;pointer-events:none;
}
.slide::after{
content:"";position:absolute;width:620px;height:620px;border-radius:50%;
background:radial-gradient(circle,rgba(118,189,34,.08),transparent 65%);
bottom:-280px;left:-160px;pointer-events:none;
}
/* ===== TYPO ===== */
.kicker{
font-family:var(--mono);font-size:14px;letter-spacing:.28em;text-transform:uppercase;
color:var(--accent2);margin-bottom:18px;display:flex;align-items:center;gap:12px;
}
.kicker::before{content:"";width:30px;height:2px;background:var(--accent);display:inline-block}
h1{font-family:var(--display);font-size:64px;font-weight:900;line-height:1.04;letter-spacing:-.02em}
h2{font-family:var(--display);font-size:44px;font-weight:800;line-height:1.08;letter-spacing:-.015em;margin-bottom:10px}
.sub{color:var(--muted);font-size:20px;font-weight:400;line-height:1.5;max-width:980px}
.slide-body{flex:1;min-height:0;margin-top:34px;display:flex;flex-direction:column}
/* ===== TITLE SLIDE ===== */
.title-slide{justify-content:center}
.title-slide h1{font-size:84px;max-width:1300px}
.title-slide .lede{font-size:30px;color:var(--accent2);font-weight:600;margin-top:26px}
.brandtag{
display:inline-flex;align-items:center;gap:14px;
font-family:var(--mono);font-size:16px;color:var(--muted);
border:1px solid var(--border);border-radius:100px;padding:12px 22px;margin-bottom:40px;
background:rgba(26,26,46,.6);width:max-content;
}
.dot{width:10px;height:10px;border-radius:50%;background:var(--success);box-shadow:0 0 14px var(--success)}
.title-meta{
margin-top:64px;display:flex;gap:64px;flex-wrap:wrap;
}
.title-meta div span{display:block;font-family:var(--mono);font-size:13px;color:var(--muted);letter-spacing:.1em;text-transform:uppercase;margin-bottom:6px}
.title-meta div strong{font-size:24px;font-weight:700}
/* ===== CARDS / GRID ===== */
.grid{display:grid;gap:22px}
.g2{grid-template-columns:repeat(2,1fr)}
.g3{grid-template-columns:repeat(3,1fr)}
.g4{grid-template-columns:repeat(4,1fr)}
.card{
background:linear-gradient(160deg,var(--card),rgba(26,26,46,.55));
border:1px solid var(--border);border-radius:18px;padding:28px 26px;
position:relative;overflow:hidden;
}
.card .ic{
width:46px;height:46px;border-radius:12px;display:flex;align-items:center;justify-content:center;
font-size:22px;margin-bottom:16px;background:rgba(0,62,126,.18);border:1px solid rgba(0,62,126,.4);
}
.card h3{font-size:21px;font-weight:700;margin-bottom:8px}
.card p{color:var(--muted);font-size:16px;line-height:1.55}
.card.accent{border-color:rgba(67,56,202,.5)}
.card.accent .ic{background:rgba(67,56,202,.16);border-color:rgba(67,56,202,.4)}
.card.success .ic{background:rgba(118,189,34,.16);border-color:rgba(118,189,34,.4)}
.card.warn .ic{background:rgba(245,158,11,.14);border-color:rgba(245,158,11,.35)}
.tag{font-family:var(--mono);font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--accent2);font-weight:600}
.pill{display:inline-block;font-family:var(--mono);font-size:13px;padding:4px 12px;border-radius:100px;border:1px solid var(--border);color:var(--muted)}
.pill.rec{color:var(--success);border-color:rgba(118,189,34,.4);background:rgba(118,189,34,.1)}
/* ===== CODE / DIAGRAM BLOCK ===== */
.codeblock{
font-family:var(--mono);background:#0A0A12;border:1px solid var(--border);
border-radius:16px;padding:30px 34px;font-size:18px;line-height:1.85;color:#CBD5E1;
box-shadow:inset 0 0 0 1px rgba(0,62,126,.08);position:relative;
}
.codeblock .winbar{position:absolute;top:14px;left:18px;display:flex;gap:7px}
.codeblock .winbar i{width:11px;height:11px;border-radius:50%;display:inline-block}
.codeblock pre{margin-top:14px;white-space:pre;overflow:auto}
.c-key{color:var(--accent2)} .c-ok{color:var(--success)} .c-warn{color:var(--warn)}
.c-mut{color:var(--muted)} .c-acc{color:#5B8BD0;font-weight:700} .c-file{color:#F8FAFC}
/* ===== ARCH MAP ===== */
.archmap{display:flex;flex-direction:column;align-items:center;gap:0;font-family:var(--mono)}
.node{
border:1px solid var(--border);border-radius:14px;padding:16px 28px;background:var(--card);
text-align:center;min-width:300px;
}
.node strong{display:block;font-size:20px;font-family:'Inter';font-weight:700}
.node span{font-size:13px;color:var(--muted)}
.node.master{border-color:var(--accent2);box-shadow:0 0 32px rgba(67,56,202,.28);background:rgba(67,56,202,.1)}
.node.git{border-color:rgba(0,62,126,.5)}
.node.vps{border-color:rgba(118,189,34,.4);background:rgba(118,189,34,.05)}
.arrow{color:var(--accent2);font-size:22px;padding:8px 0;font-family:var(--mono)}
.arrow small{display:block;font-size:11px;color:var(--muted);letter-spacing:.1em}
.tenants{display:flex;gap:18px;margin-top:6px}
.tenant{
border:1px dashed rgba(118,189,34,.4);border-radius:12px;padding:14px 20px;
font-family:var(--mono);font-size:14px;text-align:center;background:rgba(118,189,34,.04);
}
.tenant b{color:var(--success)} .tenant span{display:block;color:var(--muted);font-size:12px;margin-top:4px}
/* ===== TABLE ===== */
table{width:100%;border-collapse:collapse;font-size:18px}
th,td{text-align:left;padding:18px 22px;border-bottom:1px solid var(--border)}
th{font-family:var(--mono);font-size:13px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);font-weight:600}
td{color:#E2E8F0}
tr:last-child td{border-bottom:none}
td .mono{font-family:var(--mono)}
.num{font-family:var(--mono);font-weight:700;color:var(--accent2);text-align:right}
.num.ok{color:var(--success)}
tbody tr:hover{background:rgba(0,62,126,.07)}
/* ===== CHECKLIST ===== */
.checklist{display:grid;grid-template-columns:1fr 1fr;gap:16px 38px;align-self:stretch}
.check{
display:flex;align-items:center;gap:18px;cursor:pointer;user-select:none;
background:var(--card);border:1px solid var(--border);border-radius:14px;padding:20px 24px;
transition:.25s;
}
.check:hover{border-color:rgba(0,62,126,.5);transform:translateX(3px)}
.box{
width:30px;height:30px;border-radius:8px;border:2px solid var(--muted);flex:0 0 auto;
display:flex;align-items:center;justify-content:center;transition:.2s;font-weight:900;color:transparent;
}
.check.done .box{background:var(--success);border-color:var(--success);color:#0c2b06}
.check.done .box::after{content:"✓"}
.check .label{font-size:19px;font-weight:500;transition:.2s}
.check.done .label{color:var(--muted);text-decoration:line-through}
.check .mono{font-family:var(--mono);color:var(--accent2);font-size:16px}
/* ===== TIMELINE / ROADMAP ===== */
.timeline{display:flex;align-items:stretch;gap:0;margin-top:30px}
.phase{flex:1;position:relative;padding:0 14px}
.phase .ring{
width:26px;height:26px;border-radius:50%;border:3px solid var(--accent);
background:var(--bg);margin:0 auto 22px;position:relative;z-index:2;
}
.phase.now .ring{background:var(--accent);box-shadow:0 0 22px var(--accent)}
.phase::before{
content:"";position:absolute;top:11px;left:-50%;width:100%;height:3px;
background:var(--border);z-index:1;
}
.phase:first-child::before{display:none}
.phase.now::before,.phase.now ~ .phase::before{background:var(--border)}
.phase-card{
background:var(--card);border:1px solid var(--border);border-radius:16px;padding:24px 22px;text-align:center;height:100%;
}
.phase.now .phase-card{border-color:var(--accent);background:rgba(0,62,126,.1)}
.phase .ph-tag{font-family:var(--mono);font-size:12px;letter-spacing:.12em;text-transform:uppercase;color:var(--accent2);margin-bottom:8px}
.phase h3{font-size:22px;font-weight:700;margin-bottom:10px}
.phase p{font-size:15px;color:var(--muted);line-height:1.5}
/* ===== ER DIAGRAM ===== */
.er .codeblock{font-size:16px;line-height:1.75}
/* generic list */
.blist{list-style:none;display:flex;flex-direction:column;gap:14px}
.blist li{display:flex;gap:14px;font-size:19px;line-height:1.5;align-items:flex-start}
.blist li::before{content:"▸";color:var(--accent2);font-weight:900;margin-top:2px}
.blist li b{color:var(--text)}
.blist li span{color:var(--muted)}
/* steps for lifecycle */
.steps{display:flex;flex-direction:column;gap:14px}
.step{display:flex;gap:22px;align-items:center;background:var(--card);border:1px solid var(--border);border-radius:14px;padding:20px 26px}
.step .no{font-family:var(--mono);font-size:26px;font-weight:700;color:var(--accent2);width:46px;flex:0 0 auto}
.step .txt{flex:1}
.step .txt b{font-size:20px;font-weight:700;display:block}
.step .txt span{color:var(--muted);font-size:16px}
.step .time{font-family:var(--mono);font-size:15px;color:var(--success);background:rgba(118,189,34,.12);border:1px solid rgba(118,189,34,.3);border-radius:100px;padding:7px 16px;white-space:nowrap}
/* P&L econ */
.pl{display:grid;grid-template-columns:1fr 1fr;gap:26px}
.pl .block{background:var(--card);border:1px solid var(--border);border-radius:16px;padding:30px}
.pl .block.rev{border-color:rgba(118,189,34,.35)}
.pl .block.cost{border-color:rgba(245,158,11,.35)}
.pl .row{display:flex;justify-content:space-between;padding:12px 0;border-bottom:1px dashed var(--border);font-size:18px}
.pl .row:last-child{border:none}
.pl .row b{font-family:var(--mono)}
.be{margin-top:24px;display:flex;gap:24px}
.be .stat{flex:1;background:linear-gradient(160deg,rgba(0,62,126,.14),rgba(26,26,46,.6));border:1px solid rgba(0,62,126,.4);border-radius:16px;padding:24px 28px}
.be .stat span{font-family:var(--mono);font-size:13px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)}
.be .stat strong{display:block;font-size:38px;font-weight:800;color:var(--accent2);margin-top:6px}
/* ===== PHONE FRAMES (new slides) ===== */
.screens{display:flex;gap:40px;justify-content:center;align-items:flex-start;flex-wrap:wrap}
.phone{
width:330px;border-radius:30px;background:#000;padding:8px;
box-shadow:0 20px 60px rgba(0,0,0,.5);flex:0 0 auto;
}
.phone.sm{width:300px}
.phscreen{
border-radius:24px;background:#F5F6F8;overflow:hidden;color:#1A1A2E;
font-family:'Inter',sans-serif;
}
.statusbar{
background:#003E7E;color:#fff;height:30px;display:flex;align-items:center;
justify-content:space-between;padding:0 16px;font-size:12px;font-weight:600;font-family:var(--mono);
}
.statusbar .sb-r{display:flex;gap:5px;align-items:center;letter-spacing:1px}
.ph-head{
background:#003E7E;color:#fff;padding:10px 16px 14px;display:flex;align-items:center;
justify-content:space-between;
}
.ph-head .ph-title{font-size:15px;font-weight:800;font-family:var(--display)}
.ph-head .ph-sub{font-size:11px;color:rgba(255,255,255,.7);margin-top:2px}
.ph-head .ph-badge{font-size:11px;font-weight:600;background:rgba(255,255,255,.15);padding:4px 10px;border-radius:100px}
.ph-body{padding:12px;display:flex;flex-direction:column;gap:10px;min-height:330px}
.scard{
background:#fff;border-radius:14px;padding:12px 14px;border:1px solid #E5E7EB;
box-shadow:0 1px 4px rgba(0,0,0,.05);
}
.scard .sc-top{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px}
.scard .sc-name{font-size:13px;font-weight:700;color:#1A1A2E}
.scard .sc-sub{font-size:11px;color:#8A94A6;margin-bottom:6px}
.scard .sc-money{font-size:13px;font-weight:700;color:#003E7E;font-variant-numeric:tabular-nums}
.scard .sc-warn{font-size:11px;color:#D97706;font-weight:600;margin-top:4px;display:flex;align-items:center;gap:4px}
.sbar{height:7px;background:#E5E7EB;border-radius:100px;overflow:hidden;margin-top:8px}
.sbar i{display:block;height:100%;background:#76BD22;border-radius:100px}
.sbar.warnbar i{background:#003E7E}
.sbadge{font-size:9px;font-weight:700;padding:2px 8px;border-radius:100px}
.sbadge.done{background:#DCFCE7;color:#15803D}
.sbadge.wait{background:#FEF3C7;color:#D97706}
.sbadge.act{background:#DBEAFE;color:#1D4ED8}
/* schedule grid (chess) */
.chess{width:100%;border-collapse:collapse;font-size:11px;background:#fff;border-radius:12px;overflow:hidden}
.chess th,.chess td{border:1px solid #E5E7EB;padding:7px 8px;text-align:left;color:#1A1A2E}
.chess th{background:#003E7E;color:#fff;font-size:10px;font-weight:700}
.chess td.time{font-family:var(--mono);color:#8A94A6;font-weight:600;background:#F9FAFB}
.cell-done{color:#15803D;font-weight:600}
.cell-act{color:#003E7E;font-weight:700}
.cell-free{color:#9CA3AF}
/* day schedule */
.daysched{display:flex;flex-direction:column;gap:2px;font-size:12px}
.ds-row{display:flex;gap:10px;align-items:flex-start;padding:6px 0}
.ds-time{font-family:var(--mono);font-size:11px;color:#003E7E;font-weight:700;width:46px;flex:0 0 auto}
.ds-block{background:#fff;border:1px solid #E5E7EB;border-left:3px solid #003E7E;border-radius:8px;padding:7px 10px;flex:1}
.ds-block.green{border-left-color:#76BD22}
.ds-block b{font-size:12px;font-weight:700;color:#1A1A2E;display:block}
.ds-block span{font-size:10px;color:#8A94A6}
.ds-marker{font-size:11px;color:#8A94A6;font-weight:600;padding:5px 0}
.ds-btn{margin-top:8px;background:#003E7E;color:#fff;text-align:center;padding:9px;border-radius:10px;font-size:12px;font-weight:700}
.scaption{text-align:center;color:var(--muted);font-size:17px;margin-top:24px;font-family:var(--mono);letter-spacing:.02em}
/* security 2-col */
.seccols{display:grid;grid-template-columns:0.95fr 1.05fr;gap:34px;height:100%;align-items:stretch}
.sec-arch{display:flex;flex-direction:column}
.sec-arch .codeblock{flex:1;font-size:15px;line-height:1.7}
.secgrid{display:grid;grid-template-columns:1fr 1fr;gap:16px;align-content:start}
.seccard{
background:var(--card);border:1px solid var(--border);border-radius:14px;padding:18px 18px;
}
.seccard .si{display:block;margin-bottom:8px}
.seccard h4{font-size:16px;font-weight:700;margin-bottom:5px}
.seccard p{color:var(--muted);font-size:12.5px;line-height:1.5}
.seccard.b1{border-color:rgba(118,189,34,.35)}
.seccard.b2{border-color:rgba(0,62,126,.4)}
/* ===== NAV ===== */
.nav{position:fixed;bottom:26px;left:50%;transform:translateX(-50%);display:flex;align-items:center;gap:18px;z-index:50}
.nav button{
width:46px;height:46px;border-radius:12px;border:1px solid var(--border);background:rgba(26,26,46,.85);
color:var(--text);font-size:20px;cursor:pointer;transition:.2s;backdrop-filter:blur(8px);
}
.nav button:hover{border-color:var(--accent2);color:var(--accent2)}
.nav .counter{font-family:var(--mono);font-size:15px;color:var(--muted);min-width:64px;text-align:center}
.nav .counter b{color:var(--text)}
.progress{position:fixed;top:0;left:0;height:3px;background:linear-gradient(90deg,var(--accent),var(--success));z-index:60;transition:width .5s ease;box-shadow:0 0 10px var(--accent)}
.brandmark{position:fixed;bottom:30px;left:40px;font-family:var(--mono);font-size:13px;color:var(--muted);z-index:50;letter-spacing:.1em}
.pagenum{position:fixed;bottom:30px;right:40px;font-family:var(--mono);font-size:13px;color:var(--muted);z-index:50}
.twocol{display:grid;grid-template-columns:1fr 1fr;gap:48px;align-items:center;height:100%}
.footnote{margin-top:auto;padding-top:22px;color:var(--muted);font-size:15px;font-family:var(--mono)}
@media(max-width:1100px){
.slide{padding:40px 44px 90px}
h1{font-size:48px}.title-slide h1{font-size:56px}h2{font-size:34px}
.g4{grid-template-columns:repeat(2,1fr)}.g3{grid-template-columns:repeat(2,1fr)}
.checklist{grid-template-columns:1fr}.timeline{flex-direction:column;gap:14px}
.phase::before{display:none}.twocol{grid-template-columns:1fr;gap:24px}
.seccols{grid-template-columns:1fr;gap:20px}
}
</style>
</head>
<body>
<div class="progress" id="progress"></div>
<div class="deck" id="deck">
<!-- 1 TITLE -->
<section class="slide title-slide active">
<div class="slide-brand"><span style="font-family:'Montserrat',sans-serif;font-weight:500;font-size:12px;color:rgba(255,255,255,.45)">@wasrusgen1</span><span style="display:inline-block;width:1px;height:14px;background:rgba(255,255,255,.2);margin:0 8px;vertical-align:middle"></span><span style="font-family:'Montserrat',sans-serif;font-weight:800;font-size:13px;color:#fff;letter-spacing:1px">CRM</span></div>
<div class="stage" style="display:flex;flex-direction:column;justify-content:center">
<div class="brandtag"><span class="dot"></span> @wasrusgen1 CRM · PLATFORM ARCHITECTURE</div>
<h1>Архитектура платформы<br><span style="color:var(--accent2)">@wasrusgen1 CRM</span></h1>
<p class="lede">Один продукт — много клиентов. Полный контроль.</p>
<div class="title-meta">
<div><span>Документ для</span><strong>Собственника системы</strong></div>
<div><span>Тема</span><strong>Ввод проекта в работу</strong></div>
<div><span>Дата</span><strong>2026</strong></div>
</div>
</div>
</section>
<!-- 2 SYSTEM MAP -->
<section class="slide">
<div class="slide-brand"><span style="font-family:'Montserrat',sans-serif;font-weight:500;font-size:12px;color:rgba(255,255,255,.45)">@wasrusgen1</span><span style="display:inline-block;width:1px;height:14px;background:rgba(255,255,255,.2);margin:0 8px;vertical-align:middle"></span><span style="font-family:'Montserrat',sans-serif;font-weight:800;font-size:13px;color:#fff;letter-spacing:1px">CRM</span></div>
<div class="stage">
<div class="kicker">Слайд 02 · Big picture</div>
<h2>Карта системы</h2>
<p class="sub">Единый код в GitHub разворачивается на VPS. Каждый клиент — отдельный поддомен и отдельная база.</p>
<div class="slide-body" style="justify-content:center">
<div class="archmap">
<div class="node master"><strong>Руслан · Мастер-панель</strong><span>управление всеми проектами</span></div>
<div class="arrow"></div>
<div class="node git"><strong>GitHub</strong><span>единый исходный код</span></div>
<div class="arrow"><small>авто-деплой CI/CD</small></div>
<div class="node vps"><strong>VPS сервер · Nginx + Docker</strong><span>роутинг по поддоменам</span></div>
<div class="arrow"></div>
<div class="tenants">
<div class="tenant"><b>salon1.crm.ru</b><span>→ PostgreSQL DB_1</span></div>
<div class="tenant"><b>salon2.crm.ru</b><span>→ PostgreSQL DB_2</span></div>
<div class="tenant"><b>salonN.crm.ru</b><span>→ PostgreSQL DB_N</span></div>
</div>
</div>
</div>
</div>
</section>
<!-- 3 THREE MODELS -->
<section class="slide">
<div class="slide-brand"><span style="font-family:'Montserrat',sans-serif;font-weight:500;font-size:12px;color:rgba(255,255,255,.45)">@wasrusgen1</span><span style="display:inline-block;width:1px;height:14px;background:rgba(255,255,255,.2);margin:0 8px;vertical-align:middle"></span><span style="font-family:'Montserrat',sans-serif;font-weight:800;font-size:13px;color:#fff;letter-spacing:1px">CRM</span></div>
<div class="stage">
<div class="kicker">Слайд 03 · Стратегия</div>
<h2>Три модели управления</h2>
<p class="sub">Как обслуживать множество клиентов на одной кодовой базе.</p>
<div class="slide-body">
<div class="grid g3" style="flex:1;align-content:center">
<div class="card">
<div class="ic">A</div>
<h3>Мультитенант</h3>
<p>Один деплой, одна БД, разделение по <span class="mono" style="color:var(--accent2)">tenant_id</span>. Дёшево в масштабе, но слабая изоляция и риск общего сбоя.</p>
<div style="margin-top:14px"><span class="pill">сложно стартовать</span></div>
</div>
<div class="card success">
<div class="ic">B</div>
<h3>Мультидеплой</h3>
<p>Отдельный контейнер + отдельная БД на клиента. Полная изоляция, простой старт, легко считать расходы по клиенту.</p>
<div style="margin-top:14px"><span class="pill rec">★ старт здесь</span></div>
</div>
<div class="card accent">
<div class="ic">C</div>
<h3>Гибрид</h3>
<p>Общий код и инфра-слой, изолированные БД, общий мониторинг и единое обновление. Баланс цены и изоляции на росте.</p>
<div style="margin-top:14px"><span class="pill rec">★ цель</span></div>
</div>
</div>
<div class="footnote">Рекомендация: начать с модели <b style="color:var(--success)">B (мультидеплой)</b> → перейти на <b style="color:var(--accent2)">C (гибрид)</b> при 5+ клиентах.</div>
</div>
</div>
</section>
<!-- 4 LIFECYCLE -->
<section class="slide">
<div class="slide-brand"><span style="font-family:'Montserrat',sans-serif;font-weight:500;font-size:12px;color:rgba(255,255,255,.45)">@wasrusgen1</span><span style="display:inline-block;width:1px;height:14px;background:rgba(255,255,255,.2);margin:0 8px;vertical-align:middle"></span><span style="font-family:'Montserrat',sans-serif;font-weight:800;font-size:13px;color:#fff;letter-spacing:1px">CRM</span></div>
<div class="stage">
<div class="kicker">Слайд 04 · Процесс</div>
<h2>Жизненный цикл нового проекта</h2>
<p class="sub">От подписи договора до запуска — около 3 рабочих дней.</p>
<div class="slide-body">
<div class="steps" style="margin:auto 0">
<div class="step"><div class="no">01</div><div class="txt"><b>Договор с клиентом</b><span>согласование условий, тариф, доступы</span></div><div class="time">1 день</div></div>
<div class="step"><div class="no">02</div><div class="txt"><b>Настройка инстанса</b><span>создание БД, конфиг, поддомен, деплой</span></div><div class="time">24 ч</div></div>
<div class="step"><div class="no">03</div><div class="txt"><b>Загрузка данных</b><span>салоны, пользователи, справочники</span></div><div class="time">12 ч</div></div>
<div class="step"><div class="no">04</div><div class="txt"><b>Обучение команды</b><span>КД, администраторы, мастера</span></div><div class="time">12 дня</div></div>
<div class="step"><div class="no">05</div><div class="txt"><b>Старт → техподдержка</b><span>пилот, мониторинг, SLA</span></div><div class="time">запуск</div></div>
</div>
</div>
</div>
</section>
<!-- 5 STACK -->
<section class="slide">
<div class="slide-brand"><span style="font-family:'Montserrat',sans-serif;font-weight:500;font-size:12px;color:rgba(255,255,255,.45)">@wasrusgen1</span><span style="display:inline-block;width:1px;height:14px;background:rgba(255,255,255,.2);margin:0 8px;vertical-align:middle"></span><span style="font-family:'Montserrat',sans-serif;font-weight:800;font-size:13px;color:#fff;letter-spacing:1px">CRM</span></div>
<div class="stage">
<div class="kicker">Слайд 05 · Технологии</div>
<h2>Стек технологий</h2>
<p class="sub">Проверенные инструменты, без экзотики — быстрый найм и поддержка.</p>
<div class="slide-body">
<div class="grid g4" style="flex:1;align-content:center">
<div class="card"><div class="ic"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg></div><h3>Frontend</h3><p>HTML/JS (текущий прототип) → <b style="color:var(--accent2)">React</b> в v2</p></div>
<div class="card"><div class="ic"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 00.33 1.82l.06.06a2 2 0 010 2.83 2 2 0 01-2.83 0l-.06-.06a1.65 1.65 0 00-1.82-.33 1.65 1.65 0 00-1 1.51V21a2 2 0 01-4 0v-.09A1.65 1.65 0 009 19.4a1.65 1.65 0 00-1.82.33l-.06.06a2 2 0 01-2.83-2.83l.06-.06A1.65 1.65 0 004.68 15a1.65 1.65 0 00-1.51-1H3a2 2 0 010-4h.09A1.65 1.65 0 004.6 9a1.65 1.65 0 00-.33-1.82l-.06-.06a2 2 0 012.83-2.83l.06.06A1.65 1.65 0 009 4.68a1.65 1.65 0 001-1.51V3a2 2 0 014 0v.09a1.65 1.65 0 001 1.51 1.65 1.65 0 001.82-.33l.06-.06a2 2 0 012.83 2.83l-.06.06A1.65 1.65 0 0019.4 9a1.65 1.65 0 001.51 1H21a2 2 0 010 4h-.09a1.65 1.65 0 00-1.51 1z"/></svg></div><h3>Backend</h3><p>Node.js + Express — REST API, авторизация</p></div>
<div class="card success"><div class="ic"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/></svg></div><h3>База данных</h3><p>PostgreSQL — <b style="color:var(--success)">одна БД на клиента</b></p></div>
<div class="card"><div class="ic"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><polygon points="13,2 3,14 12,14 11,22 21,10 12,10 13,2"/></svg></div><h3>Кеш / сессии</h3><p>Redis — сессии, очереди, кеш</p></div>
<div class="card"><div class="ic"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M21 16V8a2 2 0 00-1-1.73l-7-4a2 2 0 00-2 0l-7 4A2 2 0 003 8v8a2 2 0 001 1.73l7 4a2 2 0 002 0l7-4A2 2 0 0021 16z"/><polyline points="3.27,6.96 12,12.01 20.73,6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg></div><h3>Файлы</h3><p>S3 / MinIO — фото, документы, экспорты</p></div>
<div class="card accent"><div class="ic"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><polygon points="13,2 3,14 12,14 11,22 21,10 12,10 13,2"/></svg></div><h3>Деплой</h3><p>Docker Compose + GitHub Actions CI/CD</p></div>
<div class="card"><div class="ic"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M18 10h-1.26A8 8 0 109 20h9a5 5 0 000-10z"/></svg></div><h3>Хостинг</h3><p>VPS: Hetzner / Timeweb / Selectel</p></div>
<div class="card"><div class="ic"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0110 0v4"/></svg></div><h3>Сеть</h3><p>Nginx reverse-proxy + Let's Encrypt HTTPS</p></div>
</div>
</div>
</div>
</section>
<!-- 6 ER DIAGRAM -->
<section class="slide er">
<div class="slide-brand"><span style="font-family:'Montserrat',sans-serif;font-weight:500;font-size:12px;color:rgba(255,255,255,.45)">@wasrusgen1</span><span style="display:inline-block;width:1px;height:14px;background:rgba(255,255,255,.2);margin:0 8px;vertical-align:middle"></span><span style="font-family:'Montserrat',sans-serif;font-weight:800;font-size:13px;color:#fff;letter-spacing:1px">CRM</span></div>
<div class="stage">
<div class="kicker">Слайд 06 · Данные</div>
<h2>Схема базы данных</h2>
<p class="sub">8 ключевых сущностей. Каждый клиент получает изолированную копию этой схемы.</p>
<div class="slide-body" style="justify-content:center">
<div class="codeblock">
<div class="winbar"><i style="background:#ff5f57"></i><i style="background:#febc2e"></i><i style="background:#28c840"></i></div>
<pre><span class="c-mut"># SCHEMA.md — ER-диаграмма (одна БД на клиента)</span>
<span class="c-acc">┌────────────┐</span <span class="c-acc">┌────────────┐</span <span class="c-acc">┌────────────┐</span
<span class="c-acc">│ salons │</span<span class="c-key">──1:N──</span<span class="c-acc">│ users │</span<span class="c-key">──1:N──</span<span class="c-acc">│shiftRequest│</span
<span class="c-acc">└─────┬──────┘</span <span class="c-acc">└─────┬──────┘</span <span class="c-acc">└────────────┘</span
<span class="c-key">│ 1:N</span <span class="c-key">│ 1:N</span
<span class="c-acc">┌─────┴──────┐</span <span class="c-acc">┌─────┴──────┐</span <span class="c-acc">┌────────────┐</span
<span class="c-acc">│ clients │</span<span class="c-key">──1:N──</span<span class="c-acc">│ orders │</span<span class="c-key">──1:N──</span<span class="c-acc">│ ratings │</span
<span class="c-acc">└─────┬──────┘</span <span class="c-acc">└─────┬──────┘</span <span class="c-acc">└────────────┘</span
<span class="c-key">│ 1:N</span <span class="c-key">│ 1:N</span
<span class="c-acc">┌─────┴──────┐</span <span class="c-acc">┌─────┴──────┐</span
<span class="c-acc">│appointments│</span<span class="c-key">───────│</span <span class="c-acc">│ requests │</span
<span class="c-acc">└────────────┘</span <span class="c-acc">└────────────┘</span
<span class="c-ok">salons</span> → сеть салонов клиента
<span class="c-ok">users</span> → сотрудники (КД / админ / мастер)
<span class="c-ok">clients</span> → клиентская база салона
<span class="c-ok">orders</span> → сделки / заказы
<span class="c-ok">appointments</span>→ записи на услуги
<span class="c-ok">requests</span> → заявки / лиды
<span class="c-ok">shiftRequests</span>→ заявки на смены сотрудников
<span class="c-ok">ratings</span> → оценки и обратная связь</pre>
</div>
</div>
</div>
</section>
<!-- 7 MASTER PANEL -->
<section class="slide">
<div class="slide-brand"><span style="font-family:'Montserrat',sans-serif;font-weight:500;font-size:12px;color:rgba(255,255,255,.45)">@wasrusgen1</span><span style="display:inline-block;width:1px;height:14px;background:rgba(255,255,255,.2);margin:0 8px;vertical-align:middle"></span><span style="font-family:'Montserrat',sans-serif;font-weight:800;font-size:13px;color:#fff;letter-spacing:1px">CRM</span></div>
<div class="stage">
<div class="kicker">Слайд 07 · Инструмент владельца</div>
<h2>Мастер-панель Руслана</h2>
<p class="sub">Что нужно разработать для управления всем парком проектов из одного места.</p>
<div class="slide-body">
<div class="grid g2" style="flex:1;align-content:center">
<div class="card accent"><div class="ic"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2"/><rect x="9" y="3" width="6" height="4" rx="2"/></svg></div><h3>Список проектов</h3><p>Все клиентские инстансы: статус, last activity, число пользователей, тариф.</p></div>
<div class="card success"><div class="ic"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg></div><h3>Создать новый проект</h3><p>Кнопка «Новый проект» → деплой инстанса в 1 клик (или скриптом).</p></div>
<div class="card"><div class="ic"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><polyline points="22,12 18,12 15,21 9,3 6,12 2,12"/></svg></div><h3>Мониторинг</h3><p>Uptime, ошибки, нагрузка CPU/RAM, статус БД по каждому клиенту.</p></div>
<div class="card"><div class="ic"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><polyline points="23,4 23,10 17,10"/><polyline points="1,20 1,14 7,14"/><path d="M3.51 9a9 9 0 0114.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0020.49 15"/></svg></div><h3>Массовое обновление</h3><p>Выкатить новую версию на все проекты одновременно одной командой.</p></div>
</div>
</div>
</div>
</section>
<!-- 8 [NEW] WHAT EACH ROLE SEES -->
<section class="slide">
<div class="slide-brand"><span style="font-family:'Montserrat',sans-serif;font-weight:500;font-size:12px;color:rgba(255,255,255,.45)">@wasrusgen1</span><span style="display:inline-block;width:1px;height:14px;background:rgba(255,255,255,.2);margin:0 8px;vertical-align:middle"></span><span style="font-family:'Montserrat',sans-serif;font-weight:800;font-size:13px;color:#fff;letter-spacing:1px">CRM</span></div>
<div class="stage">
<div class="kicker">Слайд 08 · Интерфейсы по ролям</div>
<h2>Что видит каждая роль</h2>
<p class="sub">Один продукт — разные кабинеты. У каждой роли свой экран и свой набор данных.</p>
<div class="slide-body" style="justify-content:center">
<div class="screens">
<!-- Screen 1: КД дашборд -->
<div class="phone">
<div class="phscreen">
<div class="statusbar"><span>9:41</span><span class="sb-r">⣾ ▌</span></div>
<div class="ph-head">
<div><div class="ph-title">📊 КД Дашборд</div><div class="ph-sub">вся сеть · реальное время</div></div>
<div class="ph-badge">май 2026</div>
</div>
<div class="ph-body">
<div class="scard">
<div class="sc-top"><span class="sc-name">🏪 Салон Ленина</span><span class="sbadge done">27/30</span></div>
<div class="sc-sub">заказов выполнено</div>
<div class="sc-money">₽ 1 537 000 / 1 700 000</div>
<div class="sc-warn">⚠ 1 просрочен</div>
<div class="sbar"><i style="width:90%"></i></div>
</div>
<div class="scard">
<div class="sc-top"><span class="sc-name">🏪 Салон Победы</span><span class="sbadge done">20/22</span></div>
<div class="sc-sub">заказов выполнено</div>
<div class="sc-money">₽ 1 310 000 / 1 500 000</div>
<div class="sc-warn">⚠ 1 просрочен</div>
<div class="sbar"><i style="width:87%"></i></div>
</div>
<div class="scard">
<div class="sc-top"><span class="sc-name">🏪 Салон Садовая</span><span class="sbadge act">15/19</span></div>
<div class="sc-sub">заказов выполнено</div>
<div class="sc-money">₽ 980 000 / 1 250 000</div>
<div class="sbar"><i style="width:78%"></i></div>
</div>
</div>
</div>
</div>
<!-- Screen 2: Администратор шахматка -->
<div class="phone">
<div class="phscreen">
<div class="statusbar"><span>9:41</span><span class="sb-r">⣾ ▌</span></div>
<div class="ph-head">
<div><div class="ph-title">Салон Ленина</div><div class="ph-sub">Шахматка · сегодня, 22 мая</div></div>
<div class="ph-badge">Админ</div>
</div>
<div class="ph-body">
<table class="chess">
<thead><tr><th>Время</th><th>Анна К.</th><th>Мария С.</th></tr></thead>
<tbody>
<tr><td class="time">10:00</td><td class="cell-done">✅ Орлова</td><td class="cell-free"></td></tr>
<tr><td class="time">11:00</td><td class="cell-done">✅ Соколова</td><td class="cell-free"></td></tr>
<tr><td class="time">12:00</td><td class="cell-free">○ Свободно</td><td class="cell-done">✅ Лебедев</td></tr>
<tr><td class="time">14:00</td><td class="cell-act">🔵 Ким Л.</td><td class="cell-act">🔵 Петрова</td></tr>
<tr><td class="time">15:00</td><td class="cell-act">🔵 Ким Л.</td><td class="cell-act">🔵 Петрова</td></tr>
<tr><td class="time">16:00</td><td class="cell-act">🔵 Захарова</td><td class="cell-free"></td></tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="scaption">КД — вся сеть в реальном времени · Администратор — управление командой</div>
</div>
</div>
</section>
<!-- 9 [NEW] MANAGER MOBILE -->
<section class="slide">
<div class="slide-brand"><span style="font-family:'Montserrat',sans-serif;font-weight:500;font-size:12px;color:rgba(255,255,255,.45)">@wasrusgen1</span><span style="display:inline-block;width:1px;height:14px;background:rgba(255,255,255,.2);margin:0 8px;vertical-align:middle"></span><span style="font-family:'Montserrat',sans-serif;font-weight:800;font-size:13px;color:#fff;letter-spacing:1px">CRM</span></div>
<div class="stage">
<div class="kicker">Слайд 09 · Мобильный кабинет</div>
<h2>Менеджер: работа из телефона</h2>
<p class="sub">Весь рабочий день — в кармане. Заказы, расписание, чек-ин на объекте.</p>
<div class="slide-body" style="justify-content:center">
<div class="screens">
<!-- Screen 1: orders -->
<div class="phone sm">
<div class="phscreen">
<div class="statusbar"><span>9:41</span><span class="sb-r">⣾ ▌</span></div>
<div class="ph-head">
<div><div class="ph-title">Мои заказы</div><div class="ph-sub">3 активных + 3 лида</div></div>
<div class="ph-badge">Менеджер</div>
</div>
<div class="ph-body">
<div class="scard">
<div class="sc-top"><span class="sc-name">МБ-2025-041 · Иванова А.С.</span></div>
<div class="sc-sub">Кухня · Ленина 34</div>
<div class="sc-warn">⚠ Техника — ждём 2 позиции</div>
<div class="sc-money" style="margin-top:6px">₽ 186 000 · аванс ✓</div>
</div>
<div class="scard">
<div class="sc-top"><span class="sc-name">МБ-2025-038 · Петров К.Н.</span><span class="sbadge done">готов</span></div>
<div class="sc-sub">Шкаф-купе · Победы 12</div>
<div class="sc-money" style="margin-top:6px">₽ 94 000 · оплачен ✓</div>
</div>
<div class="scard">
<div class="sc-top"><span class="sc-name">МБ-2025-044 · Сидоров</span><span class="sbadge wait">замер</span></div>
<div class="sc-sub">Гардероб · Садовая 5</div>
<div class="sc-money" style="margin-top:6px">оценка ₽ 120 000</div>
</div>
</div>
</div>
</div>
<!-- Screen 2: day schedule -->
<div class="phone sm">
<div class="phscreen">
<div class="statusbar"><span>9:41</span><span class="sb-r">⣾ ▌</span></div>
<div class="ph-head">
<div><div class="ph-title">Расписание дня</div><div class="ph-sub">22 мая · 4 встречи</div></div>
<div class="ph-badge">Сегодня</div>
</div>
<div class="ph-body">
<div class="daysched">
<div class="ds-marker">━● 10:00 — Начало смены</div>
<div class="ds-row"><span class="ds-time">12:00</span><div class="ds-block"><b>Ким Л. — Консультация</b><span>Кухня · 2 ч</span></div></div>
<div class="ds-row"><span class="ds-time">15:30</span><div class="ds-block green"><b>Захаров П. — Замер</b><span>Садовая 5 · GPS-чекин</span></div></div>
<div class="ds-row"><span class="ds-time">17:00</span><div class="ds-block"><b>Лебедева — Договор</b><span>офис · подпись</span></div></div>
<div class="ds-marker">━○ 19:00 — Конец смены</div>
<div class="ds-btn">+ Добавить встречу</div>
</div>
</div>
</div>
</div>
</div>
<div class="scaption">Заказы · Расписание · GPS-чекин · База знаний</div>
</div>
</div>
</section>
<!-- 10 CHECKLIST -->
<section class="slide">
<div class="slide-brand"><span style="font-family:'Montserrat',sans-serif;font-weight:500;font-size:12px;color:rgba(255,255,255,.45)">@wasrusgen1</span><span style="display:inline-block;width:1px;height:14px;background:rgba(255,255,255,.2);margin:0 8px;vertical-align:middle"></span><span style="font-family:'Montserrat',sans-serif;font-weight:800;font-size:13px;color:#fff;letter-spacing:1px">CRM</span></div>
<div class="stage">
<div class="kicker">Слайд 10 · Onboarding</div>
<h2>Как подключить нового клиента</h2>
<p class="sub">Интерактивный чеклист — кликните по пунктам. <span style="color:var(--accent2)">Прогресс сохраняется в сессии.</span></p>
<div class="slide-body" style="justify-content:center">
<div class="checklist" id="checklist">
<div class="check"><div class="box"></div><div class="label">Подписан договор</div></div>
<div class="check"><div class="box"></div><div class="label">Получены данные: сеть, салоны, сотрудники</div></div>
<div class="check"><div class="box"></div><div class="label">Создан поддомен <span class="mono">client-name.wasrusgen1.ru</span></div></div>
<div class="check"><div class="box"></div><div class="label">Развёрнута БД и заполнены справочники</div></div>
<div class="check"><div class="box"></div><div class="label">Созданы учётные записи (КД, Администраторы)</div></div>
<div class="check"><div class="box"></div><div class="label">Проведено обучение команды</div></div>
<div class="check"><div class="box"></div><div class="label">Запущен пилот</div></div>
</div>
</div>
</div>
</section>
<!-- 11 [EXPANDED] DATABASE & SECURITY -->
<section class="slide">
<div class="slide-brand"><span style="font-family:'Montserrat',sans-serif;font-weight:500;font-size:12px;color:rgba(255,255,255,.45)">@wasrusgen1</span><span style="display:inline-block;width:1px;height:14px;background:rgba(255,255,255,.2);margin:0 8px;vertical-align:middle"></span><span style="font-family:'Montserrat',sans-serif;font-weight:800;font-size:13px;color:#fff;letter-spacing:1px">CRM</span></div>
<div class="stage">
<div class="kicker">Слайд 11 · Архитектура и защита</div>
<h2>База данных и безопасность</h2>
<p class="sub">Данные каждого клиента физически отделены — утечка одного не затрагивает других.</p>
<div class="slide-body">
<div class="seccols">
<!-- Section A: DB architecture -->
<div class="sec-arch">
<div class="tag" style="margin-bottom:12px">A · Архитектура базы данных</div>
<div class="codeblock">
<div class="winbar"><i style="background:#ff5f57"></i><i style="background:#febc2e"></i><i style="background:#28c840"></i></div>
<pre><span class="c-acc">PostgreSQL</span> <span class="c-mut">per-tenant</span>
<span class="c-ok">├── crm_client1/</span>
<span class="c-ok">│ ├── </span><span class="c-file">salons · users · clients</span>
<span class="c-ok">│ ├── </span><span class="c-file">orders · appointments</span>
<span class="c-ok">│ ├── </span><span class="c-file">requests · ratings</span>
<span class="c-ok">│ └── </span><span class="c-file">shift_requests</span>
<span class="c-ok">├── crm_client2/</span>
<span class="c-ok">└── crm_clientN/</span>
<span class="c-acc">Redis</span> <span class="c-mut"># сессии + KPI-кеш</span>
<span class="c-acc">MinIO/S3</span> <span class="c-mut"># документы, фото</span>
<span class="c-acc">Nginx</span> <span class="c-mut"># routing + TLS termination</span></pre>
</div>
</div>
<!-- Section B: Security grid -->
<div>
<div class="tag" style="margin-bottom:12px">B · Безопасность</div>
<div class="secgrid">
<div class="seccard b1"><div style="display:flex;align-items:center;justify-content:center;width:44px;height:44px;background:rgba(0,62,126,.15);border-radius:10px;flex-shrink:0;margin-bottom:8px"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><polyline points="9,12 11,14 15,10"/></svg></div><h4>Физическая изоляция</h4><p>Каждый клиент — отдельная PostgreSQL БД. Код одного клиента физически не может достать данные другого.</p></div>
<div class="seccard b2"><div style="display:flex;align-items:center;justify-content:center;width:44px;height:44px;background:rgba(0,62,126,.15);border-radius:10px;flex-shrink:0;margin-bottom:8px"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M21 2l-2 2m-7.61 7.61a5.5 5.5 0 11-7.778 7.778 5.5 5.5 0 017.777-7.777zm0 0L15.5 7.5m0 0l3 3L22 7l-3-3m-3.5 3.5L19 4"/></svg></div><h4>Авторизация JWT</h4><p>Access (15 мин) + Refresh (30 дней). Роль и salonId в payload. Проверка на каждом запросе.</p></div>
<div class="seccard b2"><div style="display:flex;align-items:center;justify-content:center;width:44px;height:44px;background:rgba(0,62,126,.15);border-radius:10px;flex-shrink:0;margin-bottom:8px"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0110 0v4"/></svg></div><h4>HTTPS / TLS</h4><p>Let's Encrypt, автообновление. HTTP → 301 redirect. HSTS включён.</p></div>
<div class="seccard b1"><div style="display:flex;align-items:center;justify-content:center;width:44px;height:44px;background:rgba(0,62,126,.15);border-radius:10px;flex-shrink:0;margin-bottom:8px"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><polyline points="1,4 1,10 7,10"/><path d="M3.51 15a9 9 0 102.13-9.36L1 10"/></svg></div><h4>Резервные копии</h4><p>Ежедневный pg_dump в S3. Хранение 30 дней. RTO &lt; 1 ч, RPO &lt; 24 ч.</p></div>
<div class="seccard b2"><div style="display:flex;align-items:center;justify-content:center;width:44px;height:44px;background:rgba(0,62,126,.15);border-radius:10px;flex-shrink:0;margin-bottom:8px"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14,2 14,8 20,8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10,9 9,9 8,9"/></svg></div><h4>Аудит-лог</h4><p>Каждое изменение заказа/клиента — с userId + timestamp. Просмотр только admin/owner.</p></div>
<div class="seccard b1"><div style="display:flex;align-items:center;justify-content:center;width:44px;height:44px;background:rgba(0,62,126,.15);border-radius:10px;flex-shrink:0;margin-bottom:8px"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg></div><h4>RBAC</h4><p>5 ролей (owner, admin, manager, measurer, assembler). Матрица прав в SCHEMA.md.</p></div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- 12 INFRA COST -->
<section class="slide">
<div class="slide-brand"><span style="font-family:'Montserrat',sans-serif;font-weight:500;font-size:12px;color:rgba(255,255,255,.45)">@wasrusgen1</span><span style="display:inline-block;width:1px;height:14px;background:rgba(255,255,255,.2);margin:0 8px;vertical-align:middle"></span><span style="font-family:'Montserrat',sans-serif;font-weight:800;font-size:13px;color:#fff;letter-spacing:1px">CRM</span></div>
<div class="stage">
<div class="kicker">Слайд 12 · Инфраструктура</div>
<h2>Инфраструктура и стоимость</h2>
<p class="sub">Расходы растут вместе с числом клиентов — на старте почти нулевые.</p>
<div class="slide-body" style="justify-content:center">
<div class="card" style="padding:8px 16px">
<table>
<thead><tr><th>Этап</th><th>Конфигурация</th><th style="text-align:right">$/мес</th></tr></thead>
<tbody>
<tr><td><b>Прототип</b> <span style="color:var(--muted)">(сейчас)</span></td><td class="mono">GitHub Pages</td><td class="num ok">$0</td></tr>
<tr><td><b>Пилот</b> <span style="color:var(--muted)">(13 клиента)</span></td><td class="mono">VPS 2CPU / 4GB + Postgres</td><td class="num">~$2535</td></tr>
<tr><td><b>Рост</b> <span style="color:var(--muted)">(410 клиентов)</span></td><td class="mono">VPS 4CPU / 8GB + backup</td><td class="num">~$5080</td></tr>
<tr><td><b>Масштаб</b> <span style="color:var(--muted)">(10+ клиентов)</span></td><td class="mono">2 VPS + балансировщик</td><td class="num">~$120200</td></tr>
</tbody>
</table>
</div>
<div class="footnote">При 8 000 ₽/мес с клиента стоимость инфраструктуры — менее <b style="color:var(--success)">3 %</b> от выручки даже на масштабе.</div>
</div>
</div>
</section>
<!-- 13 ECONOMICS -->
<section class="slide">
<div class="slide-brand"><span style="font-family:'Montserrat',sans-serif;font-weight:500;font-size:12px;color:rgba(255,255,255,.45)">@wasrusgen1</span><span style="display:inline-block;width:1px;height:14px;background:rgba(255,255,255,.2);margin:0 8px;vertical-align:middle"></span><span style="font-family:'Montserrat',sans-serif;font-weight:800;font-size:13px;color:#fff;letter-spacing:1px">CRM</span></div>
<div class="stage">
<div class="kicker">Слайд 13 · Экономика</div>
<h2>Экономика продукта · P&amp;L</h2>
<p class="sub">Точка безубыточности — первый же клиент. Маржа растёт с каждым следующим.</p>
<div class="slide-body" style="justify-content:center">
<div class="pl">
<div class="block cost">
<div class="tag" style="color:var(--warn)">— РАСХОДЫ / мес</div>
<div class="row"><span>Сервер (VPS + Postgres)</span><b>~$35</b></div>
<div class="row"><span>Поддержка (N часов)</span><b>переменные</b></div>
<div class="row"><span>Прочее (домены, бэкап)</span><b>~$5</b></div>
<div class="row" style="color:var(--warn)"><span><b>Итого база</b></span><b>~$40</b></div>
</div>
<div class="block rev">
<div class="tag" style="color:var(--success)">+ ДОХОДЫ / мес</div>
<div class="row"><span>Тариф на клиента</span><b>8 000 ₽</b></div>
<div class="row"><span>5 клиентов</span><b>40 000 ₽</b></div>
<div class="row"><span>10 клиентов</span><b>80 000 ₽</b></div>
<div class="row" style="color:var(--success)"><span><b>Модель X × 8 000 ₽</b></span><b>растёт линейно</b></div>
</div>
</div>
<div class="be">
<div class="stat"><span>Break-even</span><strong>1 клиент</strong></div>
<div class="stat"><span>Целевая маржа</span><strong>85 %+</strong></div>
<div class="stat"><span>При</span><strong>5+ клиентах</strong></div>
</div>
</div>
</div>
</section>
<!-- 14 ROADMAP -->
<section class="slide">
<div class="slide-brand"><span style="font-family:'Montserrat',sans-serif;font-weight:500;font-size:12px;color:rgba(255,255,255,.45)">@wasrusgen1</span><span style="display:inline-block;width:1px;height:14px;background:rgba(255,255,255,.2);margin:0 8px;vertical-align:middle"></span><span style="font-family:'Montserrat',sans-serif;font-weight:800;font-size:13px;color:#fff;letter-spacing:1px">CRM</span></div>
<div class="stage">
<div class="kicker">Слайд 14 · Roadmap</div>
<h2>Roadmap продукта</h2>
<p class="sub">Четыре фазы — от прототипа до AI-функций.</p>
<div class="slide-body" style="justify-content:center">
<div class="timeline">
<div class="phase now">
<div class="ring"></div>
<div class="phase-card"><div class="ph-tag">Фаза 1 · сейчас</div><h3>Прототип</h3><p>Готовый интерфейс → первый пилотный клиент.</p></div>
</div>
<div class="phase">
<div class="ring"></div>
<div class="phase-card"><div class="ph-tag">Фаза 2</div><h3>Бэкенд</h3><p>Node.js + PostgreSQL + авторизация (JWT).</p></div>
</div>
<div class="phase">
<div class="ring"></div>
<div class="phase-card"><div class="ph-tag">Фаза 3</div><h3>Мобильное (PWA)</h3><p>Установка на телефон, офлайн, push-уведомления.</p></div>
</div>
<div class="phase">
<div class="ring"></div>
<div class="phase-card"><div class="ph-tag">Фаза 4</div><h3>AI-функции</h3><p>Прогноз сделок, автоматизация рутины, аналитика.</p></div>
</div>
</div>
</div>
</div>
</section>
<!-- 15 SLA -->
<section class="slide">
<div class="slide-brand"><span style="font-family:'Montserrat',sans-serif;font-weight:500;font-size:12px;color:rgba(255,255,255,.45)">@wasrusgen1</span><span style="display:inline-block;width:1px;height:14px;background:rgba(255,255,255,.2);margin:0 8px;vertical-align:middle"></span><span style="font-family:'Montserrat',sans-serif;font-weight:800;font-size:13px;color:#fff;letter-spacing:1px">CRM</span></div>
<div class="stage">
<div class="kicker">Слайд 15 · Обязательства</div>
<h2>SLA и поддержка</h2>
<p class="sub">Понятные обязательства перед клиентом — основа доверия и удержания.</p>
<div class="slide-body">
<div class="grid g4" style="flex:1;align-content:center">
<div class="card success"><div class="ic"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12,6 12,12 16,14"/></svg></div><h3>Uptime</h3><p style="font-size:34px;font-weight:800;color:var(--success);font-family:var(--mono)">99.5 %</p></div>
<div class="card"><div class="ic"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07A19.5 19.5 0 013.07 9.8a19.79 19.79 0 01-3.07-8.64A2 2 0 012 .18h3a2 2 0 012 1.72 12.84 12.84 0 00.7 2.81 2 2 0 01-.45 2.11L6.11 7.11a16 16 0 006.77 6.77l1.06-1.06a2 2 0 012.11-.45 12.84 12.84 0 002.81.7A2 2 0 0122 15h-.08z"/></svg></div><h3>Реакция</h3><p style="font-size:34px;font-weight:800;color:var(--accent2);font-family:var(--mono)">4 ч</p><p>в рабочее время</p></div>
<div class="card"><div class="ic"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><polyline points="17,1 21,5 17,9"/><path d="M3 11V9a4 4 0 014-4h14"/><polyline points="7,23 3,19 7,15"/><path d="M21 13v2a4 4 0 01-4 4H3"/></svg></div><h3>Обновления</h3><p style="font-size:34px;font-weight:800;color:var(--accent2);font-family:var(--mono)">2 нед</p><p>регулярный цикл</p></div>
<div class="card accent"><div class="ic"><svg width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><polygon points="13,2 3,14 12,14 11,22 21,10 12,10 13,2"/></svg></div><h3>Onboarding</h3><p style="font-size:34px;font-weight:800;color:var(--accent);font-family:var(--mono)">3 дня</p><p>до запуска</p></div>
</div>
</div>
</div>
</section>
<!-- 16 NEXT STEPS -->
<section class="slide">
<div class="slide-brand"><span style="font-family:'Montserrat',sans-serif;font-weight:500;font-size:12px;color:rgba(255,255,255,.45)">@wasrusgen1</span><span style="display:inline-block;width:1px;height:14px;background:rgba(255,255,255,.2);margin:0 8px;vertical-align:middle"></span><span style="font-family:'Montserrat',sans-serif;font-weight:800;font-size:13px;color:#fff;letter-spacing:1px">CRM</span></div>
<div class="stage">
<div class="kicker">Слайд 16 · План действий</div>
<h2>Следующие шаги</h2>
<p class="sub">Конкретный план на 4 недели — от выбора клиента до запуска пилота.</p>
<div class="slide-body">
<div class="steps" style="margin:auto 0">
<div class="step"><div class="no">W1</div><div class="txt"><b>Выбрать первого пилотного клиента</b><span>сеть салонов, готовая дать данные и команду</span></div><div class="time">Неделя 1</div></div>
<div class="step"><div class="no">W2</div><div class="txt"><b>Развернуть бэкенд на VPS</b><span>Node.js + PostgreSQL + Docker + CI/CD</span></div><div class="time">Неделя 2</div></div>
<div class="step"><div class="no">W3</div><div class="txt"><b>Перевести прототип на реальные данные</b><span>подключить API, загрузить салоны и пользователей</span></div><div class="time">Неделя 3</div></div>
<div class="step"><div class="no">W4</div><div class="txt"><b>Запуск пилота с командой клиента</b><span>обучение, мониторинг, сбор обратной связи</span></div><div class="time">Неделя 4</div></div>
</div>
<div class="footnote">Готов начать с недели 1 — нужен выбор пилотного клиента.</div>
</div>
</div>
</section>
</div>
<div class="brandmark">@wasrusgen1 · CRM PLATFORM</div>
<div class="pagenum" id="pagenum">01 / 16</div>
<nav class="nav">
<button id="prev" aria-label="Назад"></button>
<div class="counter"><b id="cur">1</b> / 16</div>
<button id="next" aria-label="Вперёд"></button>
</nav>
<script>
(function(){
const slides=[...document.querySelectorAll('.slide')];
let i=0;const total=slides.length;
const progress=document.getElementById('progress');
const cur=document.getElementById('cur');
const pagenum=document.getElementById('pagenum');
function pad(n){return String(n).padStart(2,'0');}
function show(n){
i=Math.max(0,Math.min(total-1,n));
slides.forEach((s,k)=>s.classList.toggle('active',k===i));
progress.style.width=((i+1)/total*100)+'%';
cur.textContent=i+1;
pagenum.textContent=pad(i+1)+' / '+pad(total);
}
document.getElementById('next').onclick=()=>show(i+1);
document.getElementById('prev').onclick=()=>show(i-1);
document.addEventListener('keydown',e=>{
if(['ArrowRight','PageDown',' '].includes(e.key)){e.preventDefault();show(i+1);}
else if(['ArrowLeft','PageUp'].includes(e.key)){e.preventDefault();show(i-1);}
else if(e.key==='Home')show(0);
else if(e.key==='End')show(total-1);
});
// interactive checklist
document.querySelectorAll('#checklist .check').forEach(c=>{
c.addEventListener('click',()=>c.classList.toggle('done'));
});
show(0);
})();
</script>
</body>
</html>