Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8366052d0c | ||
|
|
c53ff4fbb8 | ||
| fc7cc25de3 |
173
AGENTS.md
Normal file
173
AGENTS.md
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
<!-- vibe42-agents-version: v3-guided-2026-06-01 -->
|
||||||
|
# Vibe42 — учебная песочница для лендингов
|
||||||
|
|
||||||
|
Workspace юзера `sayat_aydarbaev`. Это **учебная среда**, где обычные люди (не разработчики) пробуют сделать свой первый сайт.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 ТВОЯ РОЛЬ
|
||||||
|
|
||||||
|
Ты — **гид и помощник**, а не слепой исполнитель. Цель сессии — чтобы юзер вышел с:
|
||||||
|
1. **рабочим лендингом**, опубликованным по адресу `https://pages.git.vibe42.kz/sayat_aydarbaev/<repo>/`,
|
||||||
|
2. ощущением «это было легко» — без серверов, БД, токенов, конфигов.
|
||||||
|
|
||||||
|
Юзер не разработчик. Ему важен **результат, который видно в браузере**, а не код.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🗺 СЦЕНАРИЙ ПЕРВОГО ЗАХОДА (юзер только зашёл, ещё ничего нет)
|
||||||
|
|
||||||
|
1. Поздоровайся коротко: «Привет! Тут за 10 минут собираем лендинг и публикуем его в интернете. О чём хочешь сделать?»
|
||||||
|
2. Если он не знает — предложи **4 конкретных идеи** (выбирай близкие к нему, не абстрактные):
|
||||||
|
- Промо хобби (фотография / музыка / спорт)
|
||||||
|
- Резюме / personal page с контактами
|
||||||
|
- Афиша мероприятия (концерт, день рождения, мастер-класс)
|
||||||
|
- Меню заведения / прайс услуг
|
||||||
|
- Лендинг продукта или будущего проекта (waitlist)
|
||||||
|
3. Уточни **2 короткие детали**: стиль (тёмный/светлый/яркий) и главную цель (рассказать / собрать заявку / показать работы).
|
||||||
|
4. Сразу делай `./new-project <name>` и собирай страницу. Не спрашивай разрешения на каждый шаг.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 💬 ЕСЛИ ЮЗЕР ОТВЕЧАЕТ РАСПЛЫВЧАТО
|
||||||
|
|
||||||
|
Юзер говорит «сделай что-нибудь» / «ну хз» / «сюрприз» → **не делай ничего абстрактного**.
|
||||||
|
|
||||||
|
Скажи: «Давай определимся, я задам 3 коротких вопроса:
|
||||||
|
1. Это для тебя лично, для проекта/бизнеса, или для события?
|
||||||
|
2. Главная цель — рассказать о чём-то / собрать заявку / показать портфолио?
|
||||||
|
3. Любимое настроение — строгое тёмное, лёгкое светлое, яркое цветное?»
|
||||||
|
|
||||||
|
После ответов **сразу** предложи 2 конкретных варианта названия+структуры. Дай выбрать и иди делать.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚫 ЕСЛИ ЮЗЕР ХОЧЕТ СЛОЖНОЕ — ПЕРЕФОРМУЛИРУЙ В ЛЕНДИНГ
|
||||||
|
|
||||||
|
| Запрос | Что делаем вместо |
|
||||||
|
|--------|-------------------|
|
||||||
|
| «магазин с корзиной» | лендинг с товарами + кнопка «купить» = ссылка на WhatsApp / Telegram |
|
||||||
|
| «соцсеть» | лендинг будущего проекта + waitlist-форма (Formspree / Getform) |
|
||||||
|
| «блог с админкой» | personal-page + ссылки на статьи в Telegram/Medium |
|
||||||
|
| «приложение для записи» | лендинг услуги + ссылка на Calendly / WhatsApp |
|
||||||
|
| «сайт с входом юзеров» | публичный лендинг без логина (нам логин не нужен) |
|
||||||
|
| «бот в Telegram» | лендинг с описанием бота + кнопка `t.me/...` |
|
||||||
|
|
||||||
|
**Не говори «это невозможно».** Скажи: «У нас песочница только для статических сайтов. Давай сделаем лендинг, который покажет твою идею — а кнопки/формы свяжем с готовыми сервисами (WhatsApp, Telegram, Formspree)». Юзер счастлив, результат за 15 минут.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📐 ШАБЛОНЫ СТРАНИЦ (выбирай под идею юзера)
|
||||||
|
|
||||||
|
### A — Промо продукта/услуги
|
||||||
|
**Секции:** Hero (заголовок + подзаголовок + CTA-кнопка) → 3-4 преимущества (иконка emoji + текст) → социальное доказательство (отзыв или цифра) → CTA (кнопка/телефон/мессенджер).
|
||||||
|
|
||||||
|
### B — Personal / резюме
|
||||||
|
**Секции:** Hero (фото-аватарка + имя + одна фраза «кто я») → О себе (1-2 абзаца) → 3-5 карточек проектов/опыта → Контакты (email, telegram, github как ссылки-кнопки).
|
||||||
|
|
||||||
|
### C — Афиша мероприятия
|
||||||
|
**Секции:** Hero (название + дата + место крупно) → Программа (список с временем) → Локация (картинка-placeholder + адрес) → Регистрация (форма Formspree или контакт).
|
||||||
|
|
||||||
|
### D — Меню / прайс
|
||||||
|
**Секции:** Hero (название + слоган) → Меню/прайс (категории с ценами) → Контакты (телефон, адрес, часы работы, карта-картинка).
|
||||||
|
|
||||||
|
### E — Waitlist для будущего проекта
|
||||||
|
**Секции:** Hero (название проекта + одна фраза + email-форма) → 3 фичи «что будет» → FAQ (3 пункта) → CTA (та же email-форма).
|
||||||
|
|
||||||
|
Все шаблоны — **одна страница, прокрутка вниз**. Никаких роутов, ничего динамического.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚡ РИТУАЛ ПОСЛЕ ПЕРВОГО ЗАПУСКА
|
||||||
|
|
||||||
|
Как только готов первый рабочий вариант (даже грубый):
|
||||||
|
|
||||||
|
1. **Сразу запушь:**
|
||||||
|
```bash
|
||||||
|
git add -A
|
||||||
|
git commit -m "v1"
|
||||||
|
git push origin HEAD:pages
|
||||||
|
```
|
||||||
|
2. **ОБЯЗАТЕЛЬНО** дай юзеру ссылку **жирно**:
|
||||||
|
> 🎉 Готово! Твой лендинг здесь: **https://pages.git.vibe42.kz/sayat_aydarbaev/<repo>/**
|
||||||
|
3. Скажи: «Открой в новой вкладке, посмотри. Что хочешь поменять?»
|
||||||
|
4. Дальше короткие итерации: правка → push → новый URL-показ. Каждые 2-3 правки — push.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ ЖЕЛЕЗНЫЕ ПРАВИЛА (НЕ нарушать никогда)
|
||||||
|
|
||||||
|
1. **Только статика — HTML + CSS + JS в браузере.**
|
||||||
|
2. **Никакого бэкенда.** Никаких Node/Express/FastAPI/Django/PHP/Go-серверов. Никаких БД. Никакого Redis.
|
||||||
|
3. **Никакой аутентификации / OAuth / JWT.**
|
||||||
|
4. **Никакого Docker, nginx, sudo, системных настроек.**
|
||||||
|
5. **Никаких тяжёлых сборщиков** (`npm install` дерево на 500МБ). Tailwind — только через CDN.
|
||||||
|
6. **НИКОГДА `git init` в workspace root (`/workspaces/sayat_aydarbaev`)** — это папка-контейнер юзера, не репозиторий.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ ВСЕГДА работай через `./new-project`
|
||||||
|
|
||||||
|
Если юзер сказал «сделай сайт NAME» / «создай проект NAME»:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /workspaces/sayat_aydarbaev
|
||||||
|
./new-project NAME # создаёт repo в Gitea + клонит локально в ./NAME/
|
||||||
|
cd NAME
|
||||||
|
# теперь создавай index.html / style.css / script.js внутри ./NAME
|
||||||
|
```
|
||||||
|
|
||||||
|
`./new-project` сам создаёт repo, клонит, и копирует туда `AGENTS.md` + `design.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🌐 Git и публикация
|
||||||
|
|
||||||
|
**НЕТ GitHub.** Self-hosted git: **https://git.vibe42.kz**
|
||||||
|
|
||||||
|
- Профиль юзера: https://git.vibe42.kz/sayat_aydarbaev
|
||||||
|
- Pages (живые лендинги): https://pages.git.vibe42.kz/sayat_aydarbaev/<repo>/
|
||||||
|
- Креды уже в `/workspaces/sayat_aydarbaev/.git-credentials` — git push/clone работают без пароля
|
||||||
|
- **НЕ спрашивай юзера про GitHub URL / токен** — их не нужно
|
||||||
|
|
||||||
|
### Опубликовать лендинг
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add -A
|
||||||
|
git commit -m "site"
|
||||||
|
git push origin HEAD:pages
|
||||||
|
```
|
||||||
|
|
||||||
|
Ветка **`pages`** (Caddy её обслуживает; `gh-pages` тоже работает как fallback). Push → лендинг доступен мгновенно.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 Когда что-то идёт не так
|
||||||
|
|
||||||
|
- **Pages 404** → запушь ветку `pages` снова: `git push origin HEAD:pages -f`
|
||||||
|
- **Не дёргай Gitea API типа `/repos/.../pages`, `/settings/pages`, `/deploy_keys`** — их нет
|
||||||
|
- **Не пытайся «настроить Pages через UI Gitea»** — Pages у нас работают только через push в ветку `pages`
|
||||||
|
- Запуталось — сделай новый чистый проект через `./new-project NAME-v2`, перенеси туда работающий index.html
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ❌ Чего НЕ делать НИКОГДА
|
||||||
|
|
||||||
|
- ❌ `git init` в workspace root
|
||||||
|
- ❌ `npm install` с прод-зависимостями (express/mongoose/pg/prisma/next/nuxt)
|
||||||
|
- ❌ Создавать `server.js` / `app.py` / `main.go` как backend
|
||||||
|
- ❌ Использовать `gh` CLI или GitHub API
|
||||||
|
- ❌ Вызывать Gitea Pages-API (его нет)
|
||||||
|
- ❌ Долгое отлаживание Pages — почти всегда решение «push HEAD:pages»
|
||||||
|
- ❌ Просить юзера ввести токен/URL/пароль — всё уже настроено
|
||||||
|
- ❌ Задавать юзеру 10 вопросов подряд (максимум 2-3 за раз)
|
||||||
|
- ❌ Показывать юзеру голый код больше 1 раза — ему важен результат, а не как написано
|
||||||
|
- ❌ Предлагать «давай сначала дизайн в Figma» — мы делаем сразу в HTML
|
||||||
|
- ❌ Говорить «это сложно» — переформулируй в простое
|
||||||
|
- ❌ Зависать в обсуждениях — сделай первый вариант грубо, потом итерируй
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎨 design.md
|
||||||
|
|
||||||
|
Рядом лежит `design.md` с готовой палитрой, типографикой и стартер-шаблоном `index.html`. **Начинай с него.** Не выдумывай новые цвета — модифицируй существующие.
|
||||||
860
checklist.html
Normal file
860
checklist.html
Normal file
@ -0,0 +1,860 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>Чек-лист проверки | Цифровой инспектор БиОТ</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--ink: #0F1218;
|
||||||
|
--cyan: #00E5FF;
|
||||||
|
--white: #fff;
|
||||||
|
--gray-500: #5B6573;
|
||||||
|
--gray-100: #F2F4F7;
|
||||||
|
--gray-800: #1A1D25;
|
||||||
|
--gray-700: #252833;
|
||||||
|
--gray-600: #3A3E4A;
|
||||||
|
--red: #FF4D4D;
|
||||||
|
--green: #00C853;
|
||||||
|
--amber: #FFB300;
|
||||||
|
--red-bg: rgba(255,77,77,0.08);
|
||||||
|
--green-bg: rgba(0,200,83,0.08);
|
||||||
|
--amber-bg: rgba(255,179,0,0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
*{box-sizing:border-box;margin:0;padding:0}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font: 15px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
|
||||||
|
color: var(--ink);
|
||||||
|
background: #f0f2f5;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== TOP BAR ===== */
|
||||||
|
.topbar {
|
||||||
|
background: var(--ink);
|
||||||
|
color: var(--white);
|
||||||
|
padding: 12px 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
.topbar .back {
|
||||||
|
color: var(--cyan);
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.topbar .title { font-weight: 700; font-size: 16px; flex: 1 }
|
||||||
|
.topbar .save {
|
||||||
|
color: var(--cyan);
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== HEADER CARD ===== */
|
||||||
|
.header-card {
|
||||||
|
background: var(--white);
|
||||||
|
margin: 16px 16px 0;
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.06);
|
||||||
|
}
|
||||||
|
.header-card .field {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 10px 0;
|
||||||
|
border-bottom: 1px solid #f0f2f5;
|
||||||
|
}
|
||||||
|
.header-card .field:last-child { border-bottom: none }
|
||||||
|
.header-card .field .label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--gray-500);
|
||||||
|
width: 110px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.header-card .field .value {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.header-card .field select, .header-card .field input {
|
||||||
|
flex: 1;
|
||||||
|
border: 1px solid #e0e3e8;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: inherit;
|
||||||
|
background: #fafbfc;
|
||||||
|
color: var(--ink);
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar {
|
||||||
|
margin: 16px 16px 0;
|
||||||
|
background: var(--white);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 14px 20px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.06);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
.progress-bar .track {
|
||||||
|
flex: 1;
|
||||||
|
height: 6px;
|
||||||
|
background: #e5e7eb;
|
||||||
|
border-radius: 3px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.progress-bar .fill {
|
||||||
|
height: 100%;
|
||||||
|
background: var(--cyan);
|
||||||
|
border-radius: 3px;
|
||||||
|
transition: width 0.3s;
|
||||||
|
}
|
||||||
|
.progress-bar .pct {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--cyan);
|
||||||
|
min-width: 45px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== TABS ===== */
|
||||||
|
.tabs {
|
||||||
|
display: flex;
|
||||||
|
margin: 16px 16px 0;
|
||||||
|
background: var(--white);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 4px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.06);
|
||||||
|
overflow-x: auto;
|
||||||
|
gap: 2px;
|
||||||
|
}
|
||||||
|
.tab {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 90px;
|
||||||
|
padding: 10px 8px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 11px;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--gray-500);
|
||||||
|
cursor: pointer;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition: all 0.15s;
|
||||||
|
font-family: inherit;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.tab.active {
|
||||||
|
background: var(--ink);
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
.tab .tab-emoji { display: block; font-size: 18px; margin-bottom: 2px }
|
||||||
|
|
||||||
|
/* ===== CHECKLIST ===== */
|
||||||
|
.checklist {
|
||||||
|
margin: 12px 16px 80px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.checklist.active { display: block }
|
||||||
|
|
||||||
|
.check-item {
|
||||||
|
background: var(--white);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
||||||
|
transition: box-shadow 0.15s;
|
||||||
|
}
|
||||||
|
.check-item.selected { box-shadow: 0 0 0 2px var(--cyan); }
|
||||||
|
.check-item.has-violation { box-shadow: 0 0 0 1px rgba(255,77,77,0.3); }
|
||||||
|
|
||||||
|
.check-item .item-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
.check-item .item-num {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
background: #f0f2f5;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--gray-500);
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: 1px;
|
||||||
|
}
|
||||||
|
.check-item .item-text { flex: 1; font-size: 14px; font-weight: 500 }
|
||||||
|
.check-item .item-note {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--gray-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status buttons */
|
||||||
|
.status-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
.status-btn {
|
||||||
|
flex: 1;
|
||||||
|
padding: 10px 8px;
|
||||||
|
border: 2px solid #e5e7eb;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: var(--white);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.15s;
|
||||||
|
font-family: inherit;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.status-btn:hover { border-color: #d1d5db }
|
||||||
|
.status-btn.pass.active {
|
||||||
|
border-color: var(--green);
|
||||||
|
background: var(--green-bg);
|
||||||
|
color: var(--green);
|
||||||
|
}
|
||||||
|
.status-btn.fail.active {
|
||||||
|
border-color: var(--red);
|
||||||
|
background: var(--red-bg);
|
||||||
|
color: var(--red);
|
||||||
|
}
|
||||||
|
.status-btn.na.active {
|
||||||
|
border-color: var(--gray-500);
|
||||||
|
background: #f0f2f5;
|
||||||
|
color: var(--gray-500);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Violation form */
|
||||||
|
.violation-form {
|
||||||
|
display: none;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 12px;
|
||||||
|
border-top: 1px solid #f0f2f5;
|
||||||
|
}
|
||||||
|
.violation-form.show { display: block }
|
||||||
|
|
||||||
|
.violation-form .form-row {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.violation-form label {
|
||||||
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--gray-500);
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.violation-form textarea, .violation-form input, .violation-form select {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid #e0e3e8;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: inherit;
|
||||||
|
background: #fafbfc;
|
||||||
|
color: var(--ink);
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
.violation-form textarea:focus, .violation-form input:focus, .violation-form select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--cyan);
|
||||||
|
}
|
||||||
|
.violation-form .photo-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 8px 14px;
|
||||||
|
border: 1px dashed #d1d5db;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fafbfc;
|
||||||
|
font-size: 13px;
|
||||||
|
color: var(--gray-500);
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
.violation-form .auto-fill-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 8px 14px;
|
||||||
|
border: 1px solid rgba(0,229,255,0.3);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: rgba(0,229,255,0.06);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--cyan);
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
.risk-options { display: flex; gap: 8px }
|
||||||
|
.risk-opt {
|
||||||
|
flex: 1;
|
||||||
|
padding: 8px;
|
||||||
|
border: 2px solid #e5e7eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-family: inherit;
|
||||||
|
background: var(--white);
|
||||||
|
transition: all 0.15s;
|
||||||
|
}
|
||||||
|
.risk-opt.low.active { border-color: var(--green); background: var(--green-bg); color: var(--green) }
|
||||||
|
.risk-opt.mid.active { border-color: var(--amber); background: var(--amber-bg); color: var(--amber) }
|
||||||
|
.risk-opt.high.active { border-color: var(--red); background: var(--red-bg); color: var(--red) }
|
||||||
|
|
||||||
|
/* ===== BOTTOM BAR ===== */
|
||||||
|
.bottom-bar {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: var(--white);
|
||||||
|
border-top: 1px solid #e5e7eb;
|
||||||
|
padding: 12px 20px;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
z-index: 100;
|
||||||
|
box-shadow: 0 -2px 10px rgba(0,0,0,0.05);
|
||||||
|
}
|
||||||
|
.bottom-bar .btn {
|
||||||
|
flex: 1;
|
||||||
|
padding: 14px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
transition: all 0.15s;
|
||||||
|
}
|
||||||
|
.bottom-bar .btn-preview {
|
||||||
|
background: var(--white);
|
||||||
|
border: 2px solid var(--ink);
|
||||||
|
color: var(--ink);
|
||||||
|
}
|
||||||
|
.bottom-bar .btn-generate {
|
||||||
|
background: var(--ink);
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
.bottom-bar .btn-generate:hover { background: #2a2d35 }
|
||||||
|
.bottom-bar .btn-preview:hover { background: #f0f2f5 }
|
||||||
|
|
||||||
|
/* ===== MODAL ===== */
|
||||||
|
.modal-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0,0,0,0.6);
|
||||||
|
z-index: 200;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.modal-overlay.show { display: flex }
|
||||||
|
.modal {
|
||||||
|
background: var(--white);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 28px;
|
||||||
|
max-width: 700px;
|
||||||
|
width: 100%;
|
||||||
|
max-height: 85vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.modal h3 { font-size: 20px; font-weight: 700; margin-bottom: 6px }
|
||||||
|
.modal .modal-meta { font-size: 13px; color: var(--gray-500); margin-bottom: 20px }
|
||||||
|
.modal table { width: 100%; border-collapse: collapse; font-size: 13px }
|
||||||
|
.modal th {
|
||||||
|
background: #f3f4f6;
|
||||||
|
padding: 8px 10px;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--gray-500);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
.modal td {
|
||||||
|
padding: 8px 10px;
|
||||||
|
border-bottom: 1px solid #f3f4f6;
|
||||||
|
}
|
||||||
|
.modal .close-btn {
|
||||||
|
margin-top: 20px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
background: #f0f2f5;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
.risk-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 4px; vertical-align: middle }
|
||||||
|
.risk-dot.red { background: var(--red) }
|
||||||
|
.risk-dot.amber { background: var(--amber) }
|
||||||
|
.modal .empty-msg { text-align: center; color: var(--gray-500); padding: 40px 0; font-size: 15px }
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.checklist-container { max-width: 768px; margin: 0 auto }
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
position: fixed;
|
||||||
|
top: 80px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background: var(--ink);
|
||||||
|
color: var(--white);
|
||||||
|
padding: 12px 24px;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
z-index: 300;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.toast.show { opacity: 1 }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="topbar">
|
||||||
|
<a href="index.html" class="back">+</a>
|
||||||
|
<span class="title">Новая проверка</span>
|
||||||
|
<a href="#" class="save" onclick="saveChecklist()">Сохранить</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="max-width:768px; margin:0 auto">
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="header-card">
|
||||||
|
<div class="field">
|
||||||
|
<span class="label">Объект</span>
|
||||||
|
<select><option>Цех №3</option><option>Складской комплекс</option><option>Административный корпус</option><option>Строительная площадка</option></select>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<span class="label">Подразделение</span>
|
||||||
|
<select><option>Производственный участок</option><option>Ремонтная служба</option><option>Энергоцех</option><option>Транспортный цех</option></select>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<span class="label">Ответственный</span>
|
||||||
|
<input type="text" placeholder="ФИО ответственного лица">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<span class="label">Направление</span>
|
||||||
|
<select id="mainDirection"><option>Комплексная проверка</option><option>Охрана труда и ТБ</option><option>Пожарная безопасность</option><option>Электробезопасность</option><option>Транспортная безопасность</option><option>Охрана здоровья</option><option>Выездная проверка</option></select>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<span class="label">Дата проверки</span>
|
||||||
|
<input type="date" value="2026-06-03">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Progress -->
|
||||||
|
<div class="progress-bar">
|
||||||
|
<span style="font-size:12px;color:var(--gray-500);white-space:nowrap">Пройдено:</span>
|
||||||
|
<div class="track"><div class="fill" id="progressFill" style="width:0%"></div></div>
|
||||||
|
<span class="pct" id="progressPct">0%</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tabs -->
|
||||||
|
<div class="tabs" id="tabBar">
|
||||||
|
<button class="tab active" data-tab="1"><span class="tab-emoji">⚠</span>ОТ и ТБ</button>
|
||||||
|
<button class="tab" data-tab="2"><span class="tab-emoji">🔥</span>Пожарная</button>
|
||||||
|
<button class="tab" data-tab="3"><span class="tab-emoji">⚡</span>Электро</button>
|
||||||
|
<button class="tab" data-tab="4"><span class="tab-emoji">🛣</span>Транспорт</button>
|
||||||
|
<button class="tab" data-tab="5"><span class="tab-emoji">❤</span>Здоровье</button>
|
||||||
|
<button class="tab" data-tab="6"><span class="tab-emoji">🗺</span>Выездные</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Checklist sections -->
|
||||||
|
<div id="checklistContainer"></div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bottom bar -->
|
||||||
|
<div class="bottom-bar">
|
||||||
|
<button class="btn btn-preview" onclick="showPreview()">Предпросмотр</button>
|
||||||
|
<button class="btn btn-generate" onclick="generateOrder()">Сформировать указание</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modal -->
|
||||||
|
<div class="modal-overlay" id="modalOverlay" onclick="if(event.target===this)closeModal()">
|
||||||
|
<div class="modal" id="modalContent"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Toast -->
|
||||||
|
<div class="toast" id="toast"></div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// ===== CHECKLIST DATA =====
|
||||||
|
const checklistSections = {
|
||||||
|
1: {
|
||||||
|
title: 'Охрана труда и ТБ',
|
||||||
|
items: [
|
||||||
|
{id:'1.1', text:'Наличие защитных ограждений на вращающихся и движущихся механизмах', note:'Пункт 1.2 Правил ОТ'},
|
||||||
|
{id:'1.2', text:'Состояние проходов, проездов и эвакуационных путей', note:'Свободны, не загромождены'},
|
||||||
|
{id:'1.3', text:'Наличие знаков безопасности и сигнальной разметки', note:'ГОСТ 12.4.026'},
|
||||||
|
{id:'1.4', text:'Применение средств индивидуальной защиты работниками', note:'Каски, очки, перчатки, спецобувь'},
|
||||||
|
{id:'1.5', text:'Наличие ограждений при работе на высоте (выше 1.3м)', note:'Страховочные системы, перила'},
|
||||||
|
{id:'1.6', text:'Состояние лестниц, подмостей и средств подмащивания', note:'Исправность, бирки, даты испытаний'},
|
||||||
|
{id:'1.7', text:'Исправность и своевременное освидетельствование грузоподъемных механизмов', note:'Краны, тельферы, стропы'},
|
||||||
|
{id:'1.8', text:'Наличие нарядов-допусков на огневые, газоопасные и высотные работы', note:'Оформлены, подписаны, сроки'},
|
||||||
|
{id:'1.9', text:'Проведение инструктажей (вводный, первичный, повторный, внеплановый)', note:'Записи в журналах, подписи'},
|
||||||
|
{id:'1.10', text:'Наличие и ведение журналов по охране труда', note:'Журнал инструктажа, журнал выдачи СИЗ, журнал НС'}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
2: {
|
||||||
|
title: 'Пожарная безопасность',
|
||||||
|
items: [
|
||||||
|
{id:'2.1', text:'Наличие и состояние первичных средств пожаротушения (огнетушители)', note:'Не просрочены, опломбированы, доступны'},
|
||||||
|
{id:'2.2', text:'Доступность пожарных гидрантов и рукавов', note:'Не загромождены, укомплектованы'},
|
||||||
|
{id:'2.3', text:'Наличие планов эвакуации и указателей выходов', note:'Актуальные, освещённые, на видных местах'},
|
||||||
|
{id:'2.4', text:'Состояние эвакуационных выходов и путей', note:'Не заперты, свободны, освещены'},
|
||||||
|
{id:'2.5', text:'Исправность автоматической пожарной сигнализации и оповещения', note:'Датчики дыма, сирены, проверки'},
|
||||||
|
{id:'2.6', text:'Проведение противопожарных инструктажей', note:'Записи в журнале, подписи, периодичность'},
|
||||||
|
{id:'2.7', text:'Состояние электропроводки и электрооборудования', note:'Отсутствие скруток, повреждений изоляции'},
|
||||||
|
{id:'2.8', text:'Наличие мест для курения, оборудованных по нормам', note:'Урны, знаки, удалённость от строений'}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
3: {
|
||||||
|
title: 'Электробезопасность',
|
||||||
|
items: [
|
||||||
|
{id:'3.1', text:'Наличие защитного заземления и зануления оборудования', note:'Визуальная целостность, протоколы замеров'},
|
||||||
|
{id:'3.2', text:'Состояние распределительных щитов и шкафов', note:'Закрыты, промаркированы, чистота'},
|
||||||
|
{id:'3.3', text:'Наличие предупреждающих знаков и плакатов на электроустановках', note:'«Осторожно! Электрическое напряжение»'},
|
||||||
|
{id:'3.4', text:'Наличие и сроки испытания диэлектрических средств защиты', note:'Перчатки, боты, коврики, штампы'},
|
||||||
|
{id:'3.5', text:'Наличие у персонала группы допуска по электробезопасности', note:'Удостоверения, сроки, соответствие работам'},
|
||||||
|
{id:'3.6', text:'Состояние изоляции кабелей и проводов', note:'Отсутствие повреждений, провисаний, скруток'},
|
||||||
|
{id:'3.7', text:'Наличие однолинейных схем электроснабжения', note:'Актуальные, на рабочих местах'}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
4: {
|
||||||
|
title: 'Транспортная безопасность',
|
||||||
|
items: [
|
||||||
|
{id:'4.1', text:'Проведение предрейсовых медицинских осмотров водителей', note:'Журнал, подписи, штампы'},
|
||||||
|
{id:'4.2', text:'Техническое состояние транспортных средств перед выездом', note:'Тормоза, рулевое, фары, шины'},
|
||||||
|
{id:'4.3', text:'Наличие и оформление путевых листов', note:'Заполнены, отметки механика и медика'},
|
||||||
|
{id:'4.4', text:'Наличие допусков на управление спецтехникой', note:'Удостоверения, категории, сроки'},
|
||||||
|
{id:'4.5', text:'Состояние грузозахватных приспособлений на транспорте', note:'Стропы, цепи, крюки — бирки, испытания'},
|
||||||
|
{id:'4.6', text:'Проведение инструктажей водителей и механизаторов', note:'Записи, подписи, периодичность'}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
title: 'Охрана здоровья',
|
||||||
|
items: [
|
||||||
|
{id:'5.1', text:'Проведение периодических медицинских осмотров работников', note:'График, заключения, допуски'},
|
||||||
|
{id:'5.2', text:'Наличие и укомплектованность аптечек первой помощи', note:'Сроки годности, опись, доступность'},
|
||||||
|
{id:'5.3', text:'Санитарное состояние производственных и бытовых помещений', note:'Чистота, уборка, дезинфекция'},
|
||||||
|
{id:'5.4', text:'Наличие питьевой воды и условия для приёма пищи', note:'Куллеры, столовая/комната приёма пищи'},
|
||||||
|
{id:'5.5', text:'Состояние систем вентиляции и кондиционирования', note:'Работают, чистые, проверки'},
|
||||||
|
{id:'5.6', text:'Освещённость рабочих мест', note:'Нормы, исправность светильников, замеры'},
|
||||||
|
{id:'5.7', text:'Параметры микроклимата на рабочих местах', note:'Температура, влажность, сквозняки'}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
6: {
|
||||||
|
title: 'Выездные проверки',
|
||||||
|
items: [
|
||||||
|
{id:'6.1', text:'Состояние мест производства работ на выездном объекте', note:'Ограждения, порядок, безопасность'},
|
||||||
|
{id:'6.2', text:'Наличие ограждений опасных зон на выездных объектах', note:'Котлованы, проёмы, высотные участки'},
|
||||||
|
{id:'6.3', text:'Безопасность складирования материалов и конструкций', note:'Устойчивость, проходы, высота штабелей'},
|
||||||
|
{id:'6.4', text:'Наличие первичных средств пожаротушения на выездном объекте', note:'Огнетушители, ящики с песком'},
|
||||||
|
{id:'6.5', text:'Соблюдение технологии и проекта производства работ', note:'ППР на месте, соответствие выполняемых работ'},
|
||||||
|
{id:'6.6', text:'Фиксация GPS-координат места проверки', note:'Широта/долгота, фото объекта'}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== STATE =====
|
||||||
|
const state = {};
|
||||||
|
Object.keys(checklistSections).forEach(sec => {
|
||||||
|
checklistSections[sec].items.forEach(item => {
|
||||||
|
state[item.id] = { status: null, description:'', photo:'', risk:'', deadline:'', requirement:'', measure:'' };
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
let currentTab = 1;
|
||||||
|
|
||||||
|
// ===== RENDER =====
|
||||||
|
function renderChecklist(sectionId) {
|
||||||
|
const sec = checklistSections[sectionId];
|
||||||
|
let html = '';
|
||||||
|
sec.items.forEach((item, idx) => {
|
||||||
|
const st = state[item.id];
|
||||||
|
const hasViolation = st.status === 'fail';
|
||||||
|
const selected = st.status !== null;
|
||||||
|
html += `
|
||||||
|
<div class="check-item ${selected ? 'selected' : ''} ${hasViolation ? 'has-violation' : ''}" id="item-${item.id}">
|
||||||
|
<div class="item-header">
|
||||||
|
<div class="item-num">${idx + 1}</div>
|
||||||
|
<div>
|
||||||
|
<div class="item-text">${item.text}</div>
|
||||||
|
${item.note ? `<div class="item-note">${item.note}</div>` : ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="status-row">
|
||||||
|
<button class="status-btn pass ${st.status==='pass'?'active':''}" onclick="setStatus('${item.id}','pass')">Соответствует</button>
|
||||||
|
<button class="status-btn fail ${st.status==='fail'?'active':''}" onclick="setStatus('${item.id}','fail')">Не соответствует</button>
|
||||||
|
<button class="status-btn na ${st.status==='na'?'active':''}" onclick="setStatus('${item.id}','na')">Не применяется</button>
|
||||||
|
</div>
|
||||||
|
<div class="violation-form ${hasViolation ? 'show' : ''}" id="form-${item.id}">
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Описание нарушения</label>
|
||||||
|
<textarea rows="2" placeholder="Опишите нарушение..." onchange="updateField('${item.id}','description',this.value)">${st.description}</textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Фото нарушения</label>
|
||||||
|
<button class="photo-btn" onclick="alert('В боевой версии: камера / галерея')">+ Прикрепить фото</button>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Категория риска</label>
|
||||||
|
<div class="risk-options">
|
||||||
|
<div class="risk-opt low ${st.risk==='low'?'active':''}" onclick="setRisk('${item.id}','low',this)">Низкий</div>
|
||||||
|
<div class="risk-opt mid ${st.risk==='mid'?'active':''}" onclick="setRisk('${item.id}','mid',this)">Средний</div>
|
||||||
|
<div class="risk-opt high ${st.risk==='high'?'active':''}" onclick="setRisk('${item.id}','high',this)">Высокий</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Срок устранения</label>
|
||||||
|
<input type="date" value="${st.deadline}" onchange="updateField('${item.id}','deadline',this.value)">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Ответственное лицо</label>
|
||||||
|
<input type="text" placeholder="Должность и ФИО" value="${st.measure}" onchange="updateField('${item.id}','measure',this.value)">
|
||||||
|
</div>
|
||||||
|
<button class="auto-fill-btn" onclick="autoFillViolation('${item.id}','${item.text}')">+ ИИ: автозаполнение</button>
|
||||||
|
</div>
|
||||||
|
</div>`;
|
||||||
|
});
|
||||||
|
document.getElementById('checklistContainer').innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStatus(id, status) {
|
||||||
|
if (state[id].status === status) {
|
||||||
|
state[id].status = null;
|
||||||
|
} else {
|
||||||
|
state[id].status = status;
|
||||||
|
}
|
||||||
|
if (status !== 'fail') {
|
||||||
|
state[id].description = '';
|
||||||
|
state[id].risk = '';
|
||||||
|
state[id].deadline = '';
|
||||||
|
state[id].measure = '';
|
||||||
|
}
|
||||||
|
renderChecklist(currentTab);
|
||||||
|
updateProgress();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRisk(id, risk, el) {
|
||||||
|
state[id].risk = risk;
|
||||||
|
renderChecklist(currentTab);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateField(id, field, val) {
|
||||||
|
state[id][field] = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
function autoFillViolation(id, itemText) {
|
||||||
|
const autoData = getAutoViolation(itemText);
|
||||||
|
state[id].description = autoData.description;
|
||||||
|
state[id].requirement = autoData.requirement;
|
||||||
|
state[id].measure = autoData.measure;
|
||||||
|
state[id].risk = autoData.risk;
|
||||||
|
state[id].deadline = autoData.deadline;
|
||||||
|
renderChecklist(currentTab);
|
||||||
|
showToast('+ ИИ: запись сформирована');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAutoViolation(text) {
|
||||||
|
// Simulated AI auto-fill based on checklist item
|
||||||
|
const map = {
|
||||||
|
'ограждений': { description:'Отсутствует защитное ограждение вращающихся/движущихся механизмов', requirement:'Установить защитное ограждение согласно требованиям правил ОТ', measure:'Начальник участка', risk:'high', deadline:'2026-06-15' },
|
||||||
|
'СИЗ': { description:'Работники не применяют средства индивидуальной защиты', requirement:'Обеспечить применение СИЗ согласно нормам выдачи', measure:'Мастер участка', risk:'mid', deadline:'2026-06-10' },
|
||||||
|
'высоте': { description:'Работы на высоте выполняются без страховочной системы', requirement:'Выполнять работы на высоте только с применением страховочной привязи', measure:'Начальник участка', risk:'high', deadline:'2026-06-12' },
|
||||||
|
'огнетушител': { description:'Огнетушитель не прошёл своевременную перезарядку', requirement:'Выполнить перезарядку огнетушителя', measure:'Руководитель объекта', risk:'mid', deadline:'2026-06-10' },
|
||||||
|
'эвакуац': { description:'Эвакуационные выходы загромождены / заперты', requirement:'Обеспечить свободный доступ к эвакуационным выходам', measure:'Руководитель объекта', risk:'high', deadline:'2026-06-08' },
|
||||||
|
'заземл': { description:'Отсутствует или нарушено защитное заземление оборудования', requirement:'Восстановить защитное заземление согласно ПУЭ', measure:'Главный энергетик', risk:'high', deadline:'2026-06-14' },
|
||||||
|
'проводк': { description:'Выявлены повреждения изоляции электропроводки', requirement:'Заменить повреждённые участки электропроводки', measure:'Электрик участка', risk:'high', deadline:'2026-06-11' },
|
||||||
|
'медосмотр': { description:'У работников отсутствуют отметки о прохождении медосмотра', requirement:'Организовать прохождение медицинского осмотра', measure:'Специалист по ОТ', risk:'mid', deadline:'2026-06-20' },
|
||||||
|
'путев': { description:'Путевые листы оформлены с нарушениями / отсутствуют', requirement:'Обеспечить правильное оформление путевых листов', measure:'Механик гаража', risk:'low', deadline:'2026-06-09' },
|
||||||
|
'инструктаж': { description:'Пропущен срок проведения инструктажа', requirement:'Провести внеплановый инструктаж с записью в журнале', measure:'Мастер участка', risk:'mid', deadline:'2026-06-07' },
|
||||||
|
'знаков': { description:'Отсутствуют знаки безопасности в установленных местах', requirement:'Установить знаки безопасности согласно ГОСТ', measure:'Начальник участка', risk:'mid', deadline:'2026-06-17' },
|
||||||
|
'аптеч': { description:'Аптечка не укомплектована / просрочены медикаменты', requirement:'Укомплектовать аптечку согласно нормам', measure:'Специалист по ОТ', risk:'low', deadline:'2026-06-10' },
|
||||||
|
'вентиляц': { description:'Система вентиляции не обеспечивает нормативный воздухообмен', requirement:'Провести ремонт/очистку системы вентиляции', measure:'Главный механик', risk:'mid', deadline:'2026-06-21' },
|
||||||
|
'GPS': { description:'Не зафиксированы GPS-координаты места проверки', requirement:'Включить геолокацию и зафиксировать координаты', measure:'Инспектор', risk:'low', deadline:'2026-06-04' }
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [key, val] of Object.entries(map)) {
|
||||||
|
if (text.toLowerCase().includes(key.toLowerCase())) return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { description:'Нарушение требований безопасности', requirement:'Устранить нарушение в соответствии с нормативными требованиями', measure:'Ответственный руководитель', risk:'mid', deadline:'2026-06-17' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== TAB SWITCHING =====
|
||||||
|
document.getElementById('tabBar').addEventListener('click', function(e) {
|
||||||
|
const tab = e.target.closest('.tab');
|
||||||
|
if (!tab) return;
|
||||||
|
currentTab = parseInt(tab.dataset.tab);
|
||||||
|
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
||||||
|
tab.classList.add('active');
|
||||||
|
renderChecklist(currentTab);
|
||||||
|
});
|
||||||
|
|
||||||
|
// ===== PROGRESS =====
|
||||||
|
function updateProgress() {
|
||||||
|
let total = 0;
|
||||||
|
let checked = 0;
|
||||||
|
Object.keys(checklistSections).forEach(sec => {
|
||||||
|
checklistSections[sec].items.forEach(item => {
|
||||||
|
total++;
|
||||||
|
if (state[item.id].status !== null) checked++;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const pct = total > 0 ? Math.round(checked / total * 100) : 0;
|
||||||
|
document.getElementById('progressFill').style.width = pct + '%';
|
||||||
|
document.getElementById('progressPct').textContent = pct + '%';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== GET VIOLATIONS =====
|
||||||
|
function getViolations() {
|
||||||
|
const violations = [];
|
||||||
|
Object.keys(checklistSections).forEach(sec => {
|
||||||
|
checklistSections[sec].items.forEach(item => {
|
||||||
|
const st = state[item.id];
|
||||||
|
if (st.status === 'fail') {
|
||||||
|
const riskLabel = {low:'Низкий', mid:'Средний', high:'Высокий'};
|
||||||
|
violations.push({
|
||||||
|
id: item.id,
|
||||||
|
num: violations.length + 1,
|
||||||
|
section: checklistSections[sec].title,
|
||||||
|
text: item.text,
|
||||||
|
description: st.description || item.text,
|
||||||
|
risk: st.risk || 'mid',
|
||||||
|
riskLabel: riskLabel[st.risk] || 'Средний',
|
||||||
|
deadline: st.deadline || '—',
|
||||||
|
responsible: st.measure || '—',
|
||||||
|
measure: (st.description || item.text).length > 80 ? (st.description || item.text) : 'Устранить нарушение согласно нормативным требованиям'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return violations;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== PREVIEW =====
|
||||||
|
function showPreview() {
|
||||||
|
const violations = getViolations();
|
||||||
|
const objName = document.querySelector('.header-card select').value;
|
||||||
|
const dept = document.querySelectorAll('.header-card select')[1].value;
|
||||||
|
const date = document.querySelector('.header-card input[type="date"]').value;
|
||||||
|
|
||||||
|
let html = `<h3>Предпросмотр нарушений</h3>`;
|
||||||
|
html += `<div class="modal-meta">Объект: ${objName} · Подразделение: ${dept} · Дата: ${date}</div>`;
|
||||||
|
|
||||||
|
if (violations.length === 0) {
|
||||||
|
html += `<div class="empty-msg">Нарушений не выявлено</div>`;
|
||||||
|
} else {
|
||||||
|
html += `<table>
|
||||||
|
<tr><th>№</th><th>Нарушение</th><th>Риск</th><th>Срок</th><th>Ответственный</th></tr>`;
|
||||||
|
violations.forEach(v => {
|
||||||
|
html += `<tr>
|
||||||
|
<td>${v.num}</td>
|
||||||
|
<td>${v.description.length > 80 ? v.description.substring(0,80)+'...' : v.description}</td>
|
||||||
|
<td><span class="risk-dot ${v.risk==='high'?'red':v.risk==='mid'?'amber':''}"></span>${v.riskLabel}</td>
|
||||||
|
<td>${v.deadline}</td>
|
||||||
|
<td>${v.responsible}</td>
|
||||||
|
</tr>`;
|
||||||
|
});
|
||||||
|
html += `</table>`;
|
||||||
|
}
|
||||||
|
html += `<button class="close-btn" onclick="closeModal()">Закрыть</button>`;
|
||||||
|
document.getElementById('modalContent').innerHTML = html;
|
||||||
|
document.getElementById('modalOverlay').classList.add('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateOrder() {
|
||||||
|
const violations = getViolations();
|
||||||
|
if (violations.length === 0) {
|
||||||
|
showToast('+ Нет нарушений для формирования указания');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const objName = document.querySelector('.header-card select').value;
|
||||||
|
const dept = document.querySelectorAll('.header-card select')[1].value;
|
||||||
|
const date = document.querySelector('.header-card input[type="date"]').value;
|
||||||
|
const responsible = document.querySelector('.header-card input[type="text"]').value || '—';
|
||||||
|
|
||||||
|
let html = `<h3>Указание по безопасности и охране труда</h3>`;
|
||||||
|
html += `<div class="modal-meta">
|
||||||
|
Номер: ${new Date().getTime().toString().slice(-6)} ·
|
||||||
|
Объект: ${objName} ·
|
||||||
|
Подразделение: ${dept} ·
|
||||||
|
Дата: ${date} ·
|
||||||
|
Проверяющий: ${responsible}
|
||||||
|
</div>`;
|
||||||
|
html += `<table>
|
||||||
|
<tr><th>№</th><th>Выявленное нарушение</th><th>Корректирующее мероприятие</th><th>Ответственный</th><th>Срок</th></tr>`;
|
||||||
|
violations.forEach(v => {
|
||||||
|
html += `<tr>
|
||||||
|
<td>${v.num}</td>
|
||||||
|
<td>${v.description}</td>
|
||||||
|
<td>${v.measure}</td>
|
||||||
|
<td>${v.responsible}</td>
|
||||||
|
<td>${v.deadline}</td>
|
||||||
|
</tr>`;
|
||||||
|
});
|
||||||
|
html += `</table>
|
||||||
|
<div style="display:flex;justify-content:space-between;margin-top:16px;padding-top:12px;border-top:1px solid #e5e7eb;font-size:13px;color:var(--gray-500)">
|
||||||
|
<span>Подпись проверяющего: ____________</span>
|
||||||
|
<span>Подпись руководителя: ____________</span>
|
||||||
|
</div>
|
||||||
|
<div style="text-align:right;margin-top:12px;font-size:11px;color:var(--gray-500)">
|
||||||
|
<span style="background:var(--ink);color:var(--white);padding:4px 8px;border-radius:4px;font-family:monospace">QR-код</span>
|
||||||
|
Проверка подлинности
|
||||||
|
</div>`;
|
||||||
|
html += `<button class="close-btn" onclick="closeModal()">Закрыть</button>`;
|
||||||
|
html += `<button class="btn" style="width:100%;margin-top:8px;background:var(--cyan);color:var(--ink);border:none;padding:12px;border-radius:10px;font-weight:700;cursor:pointer;font-family:inherit" onclick="alert('В боевой версии: экспорт в PDF / Word')">Скачать PDF</button>`;
|
||||||
|
|
||||||
|
document.getElementById('modalContent').innerHTML = html;
|
||||||
|
document.getElementById('modalOverlay').classList.add('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeModal() {
|
||||||
|
document.getElementById('modalOverlay').classList.remove('show');
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveChecklist() {
|
||||||
|
showToast('+ Чек-лист сохранён');
|
||||||
|
}
|
||||||
|
|
||||||
|
function showToast(msg) {
|
||||||
|
const t = document.getElementById('toast');
|
||||||
|
t.textContent = msg;
|
||||||
|
t.classList.add('show');
|
||||||
|
setTimeout(() => t.classList.remove('show'), 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== INIT =====
|
||||||
|
renderChecklist(1);
|
||||||
|
updateProgress();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
110
design.md
Normal file
110
design.md
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<!-- vibe42-design-version: v1-2026-06-01 -->
|
||||||
|
# Design system — Vibe42 песочница
|
||||||
|
|
||||||
|
Базовые цвета и типографика для лендингов. Можно отклоняться, но начинай с этого.
|
||||||
|
|
||||||
|
## Палитра
|
||||||
|
|
||||||
|
| Token | Hex | Использование |
|
||||||
|
|-------|-----|---------------|
|
||||||
|
| `--ink` | `#0F1218` | Тёмный фон / основной текст |
|
||||||
|
| `--cyan` | `#00E5FF` | Основной акцент (кнопки, лого) |
|
||||||
|
| `--cyan-50` | `#E8FCFF` | Светлая подложка для акцентов |
|
||||||
|
| `--white` | `#FFFFFF` | Основной фон |
|
||||||
|
| `--gray-500` | `#5B6573` | Вторичный текст |
|
||||||
|
| `--gray-100` | `#F2F4F7` | Сепараторы / тонкие фоны |
|
||||||
|
|
||||||
|
## Типографика
|
||||||
|
|
||||||
|
```css
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
|
||||||
|
```
|
||||||
|
|
||||||
|
| Уровень | Размер | Вес | line-height |
|
||||||
|
|---------|--------|-----|-------------|
|
||||||
|
| h1 (hero) | 56px | 800 | 1.05 |
|
||||||
|
| h2 (section) | 36px | 700 | 1.15 |
|
||||||
|
| h3 | 22px | 700 | 1.3 |
|
||||||
|
| body | 17px | 400 | 1.6 |
|
||||||
|
| small | 14px | 400 | 1.5 |
|
||||||
|
|
||||||
|
На мобиле — h1 уменьши до 36px, h2 до 28px.
|
||||||
|
|
||||||
|
## Лейаут
|
||||||
|
|
||||||
|
- max-width контента: **1140px** (контейнер с padding по бокам)
|
||||||
|
- секция: `padding: 80px 24px` (мобила: `48px 20px`)
|
||||||
|
- gap между блоками внутри секции: `24-32px`
|
||||||
|
- border-radius: `8px` (кнопки, карточки), `16px` (большие карточки)
|
||||||
|
|
||||||
|
## Кнопки
|
||||||
|
|
||||||
|
```css
|
||||||
|
.btn-primary {
|
||||||
|
background: var(--cyan); color: var(--ink);
|
||||||
|
padding: 14px 28px; border-radius: 8px;
|
||||||
|
font-weight: 700; text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.btn-secondary {
|
||||||
|
background: transparent; color: var(--ink);
|
||||||
|
border: 2px solid var(--ink);
|
||||||
|
padding: 12px 26px; border-radius: 8px;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Стартер `index.html`
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>Мой проект</title>
|
||||||
|
<style>
|
||||||
|
:root{--ink:#0F1218;--cyan:#00E5FF;--cyan-50:#E8FCFF;--white:#fff;--gray-500:#5B6573;--gray-100:#F2F4F7}
|
||||||
|
*{box-sizing:border-box;margin:0;padding:0}
|
||||||
|
body{font:17px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;color:var(--ink);background:var(--white)}
|
||||||
|
.container{max-width:1140px;margin:0 auto;padding:80px 24px}
|
||||||
|
.hero{background:var(--ink);color:var(--white)}
|
||||||
|
.hero h1{font-size:56px;font-weight:800;line-height:1.05;margin-bottom:24px}
|
||||||
|
.hero p{font-size:20px;color:#9aa3b2;max-width:600px;margin-bottom:32px}
|
||||||
|
.btn{display:inline-block;background:var(--cyan);color:var(--ink);padding:14px 28px;border-radius:8px;font-weight:700;text-decoration:none}
|
||||||
|
.btn:hover{background:#1be5ff}
|
||||||
|
.section h2{font-size:36px;font-weight:700;margin-bottom:24px}
|
||||||
|
.card{background:var(--gray-100);border-radius:16px;padding:32px;margin-bottom:16px}
|
||||||
|
@media (max-width:640px){.hero h1{font-size:36px}.section h2{font-size:28px}.container{padding:48px 20px}}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section class="hero">
|
||||||
|
<div class="container">
|
||||||
|
<h1>Заголовок проекта</h1>
|
||||||
|
<p>Подзаголовок — пара предложений о чём это.</p>
|
||||||
|
<a class="btn" href="#section">Начать</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section id="section" class="section">
|
||||||
|
<div class="container">
|
||||||
|
<h2>Секция</h2>
|
||||||
|
<div class="card">Контент карточки.</div>
|
||||||
|
<div class="card">Контент карточки.</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Чем НЕ пользоваться
|
||||||
|
|
||||||
|
- Bootstrap, Material UI, Chakra, Ant Design — слишком тяжело и не нужно для лендинга
|
||||||
|
- Font Awesome — используй emoji (🚀 ⚡ ✨) или inline SVG
|
||||||
|
- jQuery — vanilla JS более чем достаточно
|
||||||
|
|
||||||
|
## Чем МОЖНО (если очень надо)
|
||||||
|
|
||||||
|
- **Tailwind через CDN**: `<script src="https://cdn.tailwindcss.com"></script>` — для прототипа OK
|
||||||
|
- **Lottie animations через CDN**
|
||||||
|
- **Placeholder картинки**: `https://picsum.photos/800/600`, `https://placehold.co/600x400`
|
||||||
|
- **Шрифты Google Fonts через `<link>`** в head
|
||||||
876
index.html
Normal file
876
index.html
Normal file
@ -0,0 +1,876 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||||
|
<title>Цифровой инспектор БиОТ — автоматизация проверок безопасности и охраны труда</title>
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--ink: #0F1218;
|
||||||
|
--cyan: #00E5FF;
|
||||||
|
--cyan-50: #E8FCFF;
|
||||||
|
--white: #fff;
|
||||||
|
--gray-500: #5B6573;
|
||||||
|
--gray-100: #F2F4F7;
|
||||||
|
--gray-800: #1A1D25;
|
||||||
|
--gray-700: #252833;
|
||||||
|
--gray-600: #3A3E4A;
|
||||||
|
--red: #FF4D4D;
|
||||||
|
--green: #00C853;
|
||||||
|
--amber: #FFB300;
|
||||||
|
}
|
||||||
|
|
||||||
|
*{box-sizing:border-box;margin:0;padding:0}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font: 17px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
|
||||||
|
color: var(--white);
|
||||||
|
background: var(--ink);
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container { max-width: 1140px; margin: 0 auto; padding: 0 24px }
|
||||||
|
|
||||||
|
/* ===== Hero ===== */
|
||||||
|
.hero {
|
||||||
|
padding: 100px 0 80px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.hero::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -40%;
|
||||||
|
right: -15%;
|
||||||
|
width: 600px;
|
||||||
|
height: 600px;
|
||||||
|
background: radial-gradient(circle, rgba(0,229,255,0.06) 0%, transparent 70%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.hero .container { position: relative; z-index: 1 }
|
||||||
|
.hero .badge {
|
||||||
|
display: inline-block;
|
||||||
|
background: rgba(0,229,255,0.1);
|
||||||
|
border: 1px solid rgba(0,229,255,0.2);
|
||||||
|
color: var(--cyan);
|
||||||
|
padding: 6px 16px;
|
||||||
|
border-radius: 100px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.hero h1 {
|
||||||
|
font-size: 56px;
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1.05;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
}
|
||||||
|
.hero h1 span { color: var(--cyan) }
|
||||||
|
.hero p {
|
||||||
|
font-size: 20px;
|
||||||
|
color: #9aa3b2;
|
||||||
|
max-width: 620px;
|
||||||
|
margin-bottom: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-box {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
background: rgba(0,229,255,0.05);
|
||||||
|
border: 1px solid rgba(0,229,255,0.15);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px 20px;
|
||||||
|
margin-bottom: 36px;
|
||||||
|
font-size: 15px;
|
||||||
|
color: #d1d5db;
|
||||||
|
}
|
||||||
|
.highlight-box .timer {
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 20px;
|
||||||
|
color: var(--cyan);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hero .cta-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-bottom: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
background: var(--cyan);
|
||||||
|
color: var(--ink);
|
||||||
|
padding: 14px 28px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 16px;
|
||||||
|
text-decoration: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s, transform 0.15s;
|
||||||
|
}
|
||||||
|
.btn:hover { background: #1be5ff; transform: translateY(-1px) }
|
||||||
|
.btn-outline {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--cyan);
|
||||||
|
border: 2px solid rgba(0,229,255,0.3);
|
||||||
|
padding: 12px 26px;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 16px;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.2s, background 0.2s;
|
||||||
|
}
|
||||||
|
.btn-outline:hover { border-color: var(--cyan); background: rgba(0,229,255,0.05) }
|
||||||
|
|
||||||
|
.hero-stats {
|
||||||
|
display: flex;
|
||||||
|
gap: 40px;
|
||||||
|
padding-top: 32px;
|
||||||
|
border-top: 1px solid rgba(255,255,255,0.08);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.hero-stats .stat .num {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--cyan);
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.hero-stats .stat .label {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #6b7280;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Sections ===== */
|
||||||
|
.section { padding: 80px 0 }
|
||||||
|
.section-label {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--cyan);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.1em;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
.section h2 {
|
||||||
|
font-size: 36px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1.15;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.section .subtitle {
|
||||||
|
color: #9aa3b2;
|
||||||
|
font-size: 18px;
|
||||||
|
max-width: 600px;
|
||||||
|
margin-bottom: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Direction cards ===== */
|
||||||
|
.directions { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 16px }
|
||||||
|
.dir-card {
|
||||||
|
background: var(--gray-800);
|
||||||
|
border: 1px solid rgba(255,255,255,0.05);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 28px;
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
.dir-card:hover { border-color: rgba(0,229,255,0.2) }
|
||||||
|
.dir-card .dir-icon {
|
||||||
|
width: 48px; height: 48px;
|
||||||
|
background: rgba(0,229,255,0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
font-size: 22px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.dir-card h3 { font-size: 17px; font-weight: 700; margin-bottom: 4px }
|
||||||
|
.dir-card p { color: #6b7280; font-size: 14px }
|
||||||
|
|
||||||
|
/* ===== Process steps ===== */
|
||||||
|
.big-steps { display: flex; flex-direction: column; gap: 24px }
|
||||||
|
.big-step {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto 1fr;
|
||||||
|
gap: 24px;
|
||||||
|
background: var(--gray-800);
|
||||||
|
border: 1px solid rgba(255,255,255,0.05);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 32px;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
.big-step:hover { border-color: rgba(0,229,255,0.15) }
|
||||||
|
.big-step .step-num {
|
||||||
|
width: 44px; height: 44px;
|
||||||
|
background: var(--cyan);
|
||||||
|
color: var(--ink);
|
||||||
|
border-radius: 12px;
|
||||||
|
display: flex; align-items: center; justify-content: center;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 18px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.big-step .step-body h3 { font-size: 19px; font-weight: 700; margin-bottom: 6px }
|
||||||
|
.big-step .step-body .step-subtitle { color: var(--cyan); font-size: 13px; font-weight: 600; margin-bottom: 12px; text-transform: uppercase; letter-spacing: 0.05em }
|
||||||
|
.big-step .step-body p { color: #9aa3b2; font-size: 15px; margin-bottom: 12px }
|
||||||
|
.big-step .step-body ul {
|
||||||
|
list-style: none;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.big-step .step-body ul li {
|
||||||
|
background: rgba(0,229,255,0.06);
|
||||||
|
border: 1px solid rgba(0,229,255,0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 6px 14px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #d1d5db;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Doc preview block ===== */
|
||||||
|
.doc-block {
|
||||||
|
background: var(--gray-800);
|
||||||
|
border: 1px solid rgba(255,255,255,0.05);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 40px;
|
||||||
|
}
|
||||||
|
.doc-block h3 { font-size: 20px; font-weight: 700; margin-bottom: 24px }
|
||||||
|
.doc-preview {
|
||||||
|
background: var(--white);
|
||||||
|
color: var(--ink);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 24px 32px;
|
||||||
|
font-size: 13px;
|
||||||
|
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
.doc-preview .doc-header {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
.doc-preview .doc-meta {
|
||||||
|
font-size: 11px;
|
||||||
|
color: #6b7280;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
border-bottom: 2px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
.doc-preview table { width: 100%; border-collapse: collapse }
|
||||||
|
.doc-preview th {
|
||||||
|
background: #f3f4f6;
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #6b7280;
|
||||||
|
}
|
||||||
|
.doc-preview td {
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-bottom: 1px solid #f3f4f6;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.risk-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px }
|
||||||
|
.risk-dot.high { background: var(--red) }
|
||||||
|
.risk-dot.mid { background: var(--amber) }
|
||||||
|
|
||||||
|
/* ===== AI section ===== */
|
||||||
|
.ai-section {
|
||||||
|
background: linear-gradient(135deg, var(--gray-800) 0%, var(--ink) 100%);
|
||||||
|
border: 1px solid rgba(0,229,255,0.08);
|
||||||
|
border-radius: 24px;
|
||||||
|
padding: 60px;
|
||||||
|
}
|
||||||
|
.ai-flow {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 20px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
.ai-step {
|
||||||
|
background: var(--gray-700);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 24px;
|
||||||
|
text-align: center;
|
||||||
|
min-width: 160px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.ai-step .ai-icon { font-size: 32px; margin-bottom: 12px }
|
||||||
|
.ai-step h4 { font-size: 15px; font-weight: 700; margin-bottom: 4px }
|
||||||
|
.ai-step p { font-size: 13px; color: #6b7280 }
|
||||||
|
.ai-arrow {
|
||||||
|
font-size: 24px;
|
||||||
|
color: var(--cyan);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.ai-result {
|
||||||
|
background: rgba(0,229,255,0.08);
|
||||||
|
border: 1px solid rgba(0,229,255,0.2);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 24px;
|
||||||
|
flex: 1.5;
|
||||||
|
min-width: 280px;
|
||||||
|
}
|
||||||
|
.ai-result h4 { font-size: 15px; font-weight: 700; color: var(--cyan); margin-bottom: 12px }
|
||||||
|
.ai-result .ai-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 6px 0;
|
||||||
|
font-size: 13px;
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.05);
|
||||||
|
color: #9aa3b2;
|
||||||
|
}
|
||||||
|
.ai-result .ai-row span:last-child { color: #d1d5db; font-weight: 600; text-align: right }
|
||||||
|
.timer-badge {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
background: rgba(0,229,255,0.12);
|
||||||
|
border: 1px solid rgba(0,229,255,0.25);
|
||||||
|
color: var(--cyan);
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 100px;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== Dashboard mockup ===== */
|
||||||
|
.dash-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
|
||||||
|
gap: 14px;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.dash-card {
|
||||||
|
background: var(--gray-700);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 22px 20px;
|
||||||
|
}
|
||||||
|
.dash-card .val {
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: var(--cyan);
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.dash-card .lbl { font-size: 12px; color: #6b7280 }
|
||||||
|
.dash-card.crit .val { color: var(--red) }
|
||||||
|
.dash-card.warn .val { color: var(--amber) }
|
||||||
|
.dash-card.ok .val { color: var(--green) }
|
||||||
|
|
||||||
|
.dir-bars { display: flex; flex-direction: column; gap: 10px }
|
||||||
|
.dir-bar {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
}
|
||||||
|
.dir-bar .dir-name { width: 180px; font-size: 13px; color: #9aa3b2; flex-shrink: 0 }
|
||||||
|
.dir-bar .bar-track {
|
||||||
|
flex: 1;
|
||||||
|
height: 10px;
|
||||||
|
background: var(--gray-700);
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.dir-bar .bar-fill { height: 100%; border-radius: 5px; background: var(--cyan) }
|
||||||
|
.dir-bar .bar-val { font-size: 13px; font-weight: 700; color: var(--white); width: 30px; text-align: right }
|
||||||
|
.dir-bar .bar-fill.w2 { width: 46%; background: #2196F3 }
|
||||||
|
.dir-bar .bar-fill.w3 { width: 35%; background: var(--amber) }
|
||||||
|
.dir-bar .bar-fill.w4 { width: 19%; background: #9C27B0 }
|
||||||
|
.dir-bar .bar-fill.w5 { width: 13%; background: var(--green) }
|
||||||
|
|
||||||
|
/* ===== Waitlist ===== */
|
||||||
|
.waitlist {
|
||||||
|
background: linear-gradient(135deg, var(--gray-800) 0%, var(--ink) 100%);
|
||||||
|
border: 1px solid rgba(0,229,255,0.1);
|
||||||
|
border-radius: 24px;
|
||||||
|
padding: 60px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.waitlist h2 { font-size: 32px; margin-bottom: 12px }
|
||||||
|
.waitlist p { color: #9aa3b2; margin-bottom: 32px }
|
||||||
|
.waitlist form {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
max-width: 480px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.waitlist input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 14px 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid rgba(255,255,255,0.1);
|
||||||
|
background: var(--gray-700);
|
||||||
|
color: var(--white);
|
||||||
|
font-size: 16px;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
.waitlist input:focus { border-color: var(--cyan) }
|
||||||
|
.waitlist input::placeholder { color: #6b7280 }
|
||||||
|
|
||||||
|
/* ===== FAQ ===== */
|
||||||
|
.faq-list { max-width: 700px }
|
||||||
|
.faq-item {
|
||||||
|
border-bottom: 1px solid rgba(255,255,255,0.06);
|
||||||
|
padding: 24px 0;
|
||||||
|
}
|
||||||
|
.faq-item summary {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 17px;
|
||||||
|
cursor: pointer;
|
||||||
|
list-style: none;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
.faq-item summary::-webkit-details-marker { display: none }
|
||||||
|
.faq-item summary::after {
|
||||||
|
content: '+';
|
||||||
|
font-size: 22px;
|
||||||
|
color: var(--cyan);
|
||||||
|
font-weight: 400;
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
.faq-item[open] summary::after { content: '\2212' }
|
||||||
|
.faq-item p { color: #9aa3b2; font-size: 15px; margin-top: 12px; padding-right: 40px }
|
||||||
|
|
||||||
|
/* ===== Footer ===== */
|
||||||
|
.footer {
|
||||||
|
padding: 48px 0;
|
||||||
|
border-top: 1px solid rgba(255,255,255,0.06);
|
||||||
|
color: #6b7280;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
.footer .container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
.footer a { color: #9aa3b2; text-decoration: none }
|
||||||
|
.footer a:hover { color: var(--cyan) }
|
||||||
|
|
||||||
|
/* ===== Mobile ===== */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.hero { padding: 60px 0 48px }
|
||||||
|
.hero h1 { font-size: 34px }
|
||||||
|
.hero p { font-size: 17px }
|
||||||
|
.hero-stats { gap: 20px }
|
||||||
|
.section { padding: 48px 0 }
|
||||||
|
.section h2 { font-size: 28px }
|
||||||
|
.big-step { grid-template-columns: 1fr; gap: 16px }
|
||||||
|
.doc-block { padding: 24px }
|
||||||
|
.doc-preview { overflow-x: auto; padding: 16px }
|
||||||
|
.ai-section { padding: 32px 24px; border-radius: 16px }
|
||||||
|
.ai-flow { flex-direction: column }
|
||||||
|
.ai-arrow { transform: rotate(90deg) }
|
||||||
|
.waitlist { padding: 32px 24px; border-radius: 16px }
|
||||||
|
.waitlist form { flex-direction: column }
|
||||||
|
.waitlist h2 { font-size: 24px }
|
||||||
|
.dir-bar .dir-name { width: 120px; font-size: 12px }
|
||||||
|
.dash-grid { grid-template-columns: repeat(2, 1fr) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.hero h1 { font-size: 28px }
|
||||||
|
.hero .cta-row { flex-direction: column; align-items: stretch }
|
||||||
|
.btn, .btn-outline { text-align: center; justify-content: center }
|
||||||
|
.directions { grid-template-columns: 1fr }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- ===== HERO ===== -->
|
||||||
|
<section class="hero">
|
||||||
|
<div class="container">
|
||||||
|
<div class="badge">Продукт в разработке</div>
|
||||||
|
<h1><span>Цифровой инспектор БиОТ</span><br>проверки безопасности — быстро и без бумаги</h1>
|
||||||
|
<p>Мобильная и веб-система для проверок по охране труда, пожарной, электро-, транспортной безопасности и охране здоровья. С автоматическим формированием предписаний по требованиям законодательства РК.</p>
|
||||||
|
|
||||||
|
<div class="highlight-box">
|
||||||
|
<span class="timer">10-15 мин</span>
|
||||||
|
<span>на оформление проверки вместо 1-2 часов</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cta-row">
|
||||||
|
<a href="checklist.html" class="btn">Открыть чек-лист</a>
|
||||||
|
<a href="#process" class="btn-outline">Как работает</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="hero-stats">
|
||||||
|
<div class="stat"><div class="num">6</div><div class="label">направлений проверок</div></div>
|
||||||
|
<div class="stat"><div class="num">PDF + Word</div><div class="label">экспорт документа</div></div>
|
||||||
|
<div class="stat"><div class="num">ИИ-модуль</div><div class="label">автораспознавание</div></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ===== DIRECTIONS ===== -->
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-label">Направления проверок</div>
|
||||||
|
<h2>6 направлений — одна система</h2>
|
||||||
|
<p class="subtitle">Инспектор выбирает направление, объект и подразделение — система ведёт по чек-листу.</p>
|
||||||
|
<div class="directions">
|
||||||
|
<div class="dir-card">
|
||||||
|
<div class="dir-icon">⚠</div>
|
||||||
|
<div>
|
||||||
|
<h3>Охрана труда и ТБ</h3>
|
||||||
|
<p>СИЗ, ограждения, работа на высоте, грузоподъёмные механизмы, документация</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dir-card">
|
||||||
|
<div class="dir-icon">🔥</div>
|
||||||
|
<div>
|
||||||
|
<h3>Пожарная безопасность</h3>
|
||||||
|
<p>Огнетушители, гидранты, планы эвакуации, датчики, противопожарное состояние</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dir-card">
|
||||||
|
<div class="dir-icon">⚡</div>
|
||||||
|
<div>
|
||||||
|
<h3>Электробезопасность</h3>
|
||||||
|
<p>Заземление, изоляция, распределительные щиты, допуски персонала</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dir-card">
|
||||||
|
<div class="dir-icon">🛣</div>
|
||||||
|
<div>
|
||||||
|
<h3>Транспортная безопасность</h3>
|
||||||
|
<p>Техсостояние транспорта, предрейсовые осмотры, путевые листы, допуски</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dir-card">
|
||||||
|
<div class="dir-icon">❤</div>
|
||||||
|
<div>
|
||||||
|
<h3>Охрана здоровья</h3>
|
||||||
|
<p>Медосмотры, санитарное состояние, аптечки, условия труда на рабочих местах</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dir-card">
|
||||||
|
<div class="dir-icon">🗺</div>
|
||||||
|
<div>
|
||||||
|
<h3>Выездные проверки</h3>
|
||||||
|
<p>GPS-координаты, проверка мест производства работ, рейтинг объектов по риску</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ===== PROCESS ===== -->
|
||||||
|
<section class="section" id="process" style="background: var(--gray-800); border-top:1px solid rgba(255,255,255,0.04); border-bottom:1px solid rgba(255,255,255,0.04)">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-label">Бизнес-процесс</div>
|
||||||
|
<h2>4 этапа — от проверки до отчёта</h2>
|
||||||
|
<p class="subtitle">Система ведёт инспектора по шагам, исключая ручное оформление.</p>
|
||||||
|
|
||||||
|
<div class="big-steps">
|
||||||
|
<div class="big-step">
|
||||||
|
<div class="step-num">1</div>
|
||||||
|
<div class="step-body">
|
||||||
|
<div class="step-subtitle">На объекте</div>
|
||||||
|
<h3>Инспектор заполняет чек-лист</h3>
|
||||||
|
<p>Выбирает объект, подразделение, ответственное лицо, направление, дату. Проходит пункты чек-листа: соответствует / не соответствует / не применяется.</p>
|
||||||
|
<ul>
|
||||||
|
<li>Фото нарушения</li>
|
||||||
|
<li>Категория риска</li>
|
||||||
|
<li>Срок устранения</li>
|
||||||
|
<li>Ответственный</li>
|
||||||
|
<li>Нормативная ссылка</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="big-step">
|
||||||
|
<div class="step-num">2</div>
|
||||||
|
<div class="step-body">
|
||||||
|
<div class="step-subtitle">Автоматически</div>
|
||||||
|
<h3>Формирование записи о нарушении</h3>
|
||||||
|
<p>Инспектор фиксирует факт — система сама формулирует нарушение, требование и корректирующее мероприятие по корпоративному шаблону.</p>
|
||||||
|
<ul>
|
||||||
|
<li>Формулировка нарушения</li>
|
||||||
|
<li>Нормативное требование</li>
|
||||||
|
<li>Корректирующее мероприятие</li>
|
||||||
|
<li>Категория риска</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ===== DOCUMENT AUTOGENERATION ===== -->
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-label">Этап 3</div>
|
||||||
|
<h2>Автоматическое «Указание по БиОТ»</h2>
|
||||||
|
<p class="subtitle">Форма по корпоративному шаблону или утверждённому образцу организации, в соответствии с законодательством РК.</p>
|
||||||
|
|
||||||
|
<div class="doc-block">
|
||||||
|
<h3>Предпросмотр формируемого документа</h3>
|
||||||
|
<div class="doc-preview">
|
||||||
|
<div class="doc-header">Указание по безопасности и охране труда № 12/06</div>
|
||||||
|
<div class="doc-meta">Объект: Цех №3 · Подразделение: Производственный участок · Проверяющий: Иванов А.С. · Дата: 03.06.2026</div>
|
||||||
|
<table>
|
||||||
|
<tr><th>№</th><th>Выявленное нарушение</th><th>Корректирующее мероприятие</th><th>Ответственный</th><th>Срок</th></tr>
|
||||||
|
<tr>
|
||||||
|
<td>1</td>
|
||||||
|
<td><span class="risk-dot high"></span>Работы на высоте без страховочной привязи</td>
|
||||||
|
<td>Обеспечить применение страховочных систем</td>
|
||||||
|
<td>Начальник участка</td>
|
||||||
|
<td>15.06.2026</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>2</td>
|
||||||
|
<td><span class="risk-dot high"></span>Огнетушитель не прошёл своевременную перезарядку</td>
|
||||||
|
<td>Выполнить перезарядку огнетушителя</td>
|
||||||
|
<td>Руководитель объекта</td>
|
||||||
|
<td>10.06.2026</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>3</td>
|
||||||
|
<td><span class="risk-dot mid"></span>Отсутствует запись в журнале инструктажа</td>
|
||||||
|
<td>Внести запись и провести инструктаж</td>
|
||||||
|
<td>Мастер участка</td>
|
||||||
|
<td>20.06.2026</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<div style="display:flex; justify-content: space-between; margin-top: 16px; padding-top: 12px; border-top: 1px solid #e5e7eb; font-size: 11px; color: #6b7280;">
|
||||||
|
<span>Подпись проверяющего: ___________</span>
|
||||||
|
<span>Подпись руководителя: ___________</span>
|
||||||
|
<span style="background:#000;color:#fff;padding:4px 8px;border-radius:4px;font-family:monospace">QR-код</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ===== AI MODULE ===== -->
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="ai-section">
|
||||||
|
<div class="section-label" style="color: var(--cyan)">Самая ценная функция</div>
|
||||||
|
<h2>ИИ-модуль: фото → готовая запись</h2>
|
||||||
|
<p class="subtitle" style="margin: 0 auto 0 0">Инспектор делает снимок нарушения — система сама определяет тип, формулировку и предлагает строку для указания.</p>
|
||||||
|
|
||||||
|
<div class="ai-flow">
|
||||||
|
<div class="ai-step">
|
||||||
|
<div class="ai-icon">📷</div>
|
||||||
|
<h4>Фото нарушения</h4>
|
||||||
|
<p>Инспектор фотографирует нарушение на объекте</p>
|
||||||
|
</div>
|
||||||
|
<div class="ai-arrow">➔</div>
|
||||||
|
<div class="ai-step">
|
||||||
|
<div class="ai-icon">🧠</div>
|
||||||
|
<h4>Распознавание</h4>
|
||||||
|
<p>ИИ определяет тип нарушения по снимку</p>
|
||||||
|
</div>
|
||||||
|
<div class="ai-arrow">➔</div>
|
||||||
|
<div class="ai-result">
|
||||||
|
<h4>Автоматически предложено:</h4>
|
||||||
|
<div class="ai-row"><span>Нарушение</span><span>Работник без страховочной привязи</span></div>
|
||||||
|
<div class="ai-row"><span>Мероприятие</span><span>Организовать контроль применения СИЗ</span></div>
|
||||||
|
<div class="ai-row"><span>Категория риска</span><span style="color:var(--red)">Высокий</span></div>
|
||||||
|
<div class="ai-row"><span>Нормативная ссылка</span><span>Правила ОТ при работе на высоте §12</span></div>
|
||||||
|
<div class="timer-badge" style="margin-top:16px">⚡ Готово к вставке в Указание</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ===== DASHBOARD ===== -->
|
||||||
|
<section class="section" style="background: var(--gray-800); border-top:1px solid rgba(255,255,255,0.04); border-bottom:1px solid rgba(255,255,255,0.04)">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-label">Этап 4</div>
|
||||||
|
<h2>Дашборд руководителя</h2>
|
||||||
|
<p class="subtitle">Аналитика в реальном времени по всем объектам, подразделениям и направлениям.</p>
|
||||||
|
|
||||||
|
<div class="dash-grid">
|
||||||
|
<div class="dash-card"><div class="val">142</div><div class="lbl">Проверок за месяц</div></div>
|
||||||
|
<div class="dash-card crit"><div class="val">37</div><div class="lbl">Нарушений</div></div>
|
||||||
|
<div class="dash-card ok"><div class="val">82%</div><div class="lbl">Устранено</div></div>
|
||||||
|
<div class="dash-card warn"><div class="val">5</div><div class="lbl">Просрочено</div></div>
|
||||||
|
<div class="dash-card crit"><div class="val">3</div><div class="lbl">Критических</div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-bottom:8px; font-size:13px; font-weight:700; color:#9aa3b2">Нарушения по направлениям</div>
|
||||||
|
<div class="dir-bars">
|
||||||
|
<div class="dir-bar">
|
||||||
|
<span class="dir-name">Охрана труда и ТБ</span>
|
||||||
|
<span class="bar-track"><span class="bar-fill" style="width:100%"></span></span>
|
||||||
|
<span class="bar-val">48</span>
|
||||||
|
</div>
|
||||||
|
<div class="dir-bar">
|
||||||
|
<span class="dir-name">Электробезопасность</span>
|
||||||
|
<span class="bar-track"><span class="bar-fill w2"></span></span>
|
||||||
|
<span class="bar-val">22</span>
|
||||||
|
</div>
|
||||||
|
<div class="dir-bar">
|
||||||
|
<span class="dir-name">Пожарная безопасность</span>
|
||||||
|
<span class="bar-track"><span class="bar-fill w3"></span></span>
|
||||||
|
<span class="bar-val">17</span>
|
||||||
|
</div>
|
||||||
|
<div class="dir-bar">
|
||||||
|
<span class="dir-name">Транспортная безопасность</span>
|
||||||
|
<span class="bar-track"><span class="bar-fill w4"></span></span>
|
||||||
|
<span class="bar-val">9</span>
|
||||||
|
</div>
|
||||||
|
<div class="dir-bar">
|
||||||
|
<span class="dir-name">Охрана здоровья</span>
|
||||||
|
<span class="bar-track"><span class="bar-fill w5"></span></span>
|
||||||
|
<span class="bar-val">6</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ===== TECH STACK ===== -->
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-label">Технологии</div>
|
||||||
|
<h2>Стек и план первого релиза</h2>
|
||||||
|
<p class="subtitle">MVP за 1-2 месяца: мобильное приложение, сервер, база данных, ИИ-модуль.</p>
|
||||||
|
<div class="directions">
|
||||||
|
<div class="dir-card">
|
||||||
|
<div class="dir-icon">📱</div>
|
||||||
|
<div><h3>Flutter</h3><p>Мобильное приложение Android + iOS. Один код, две платформы, офлайн-режим.</p></div>
|
||||||
|
</div>
|
||||||
|
<div class="dir-card">
|
||||||
|
<div class="dir-icon">⚙</div>
|
||||||
|
<div><h3>FastAPI + Python</h3><p>API-сервер, генерация PDF/Word, хранение фото, авторизация по ролям.</p></div>
|
||||||
|
</div>
|
||||||
|
<div class="dir-card">
|
||||||
|
<div class="dir-icon">🗃</div>
|
||||||
|
<div><h3>PostgreSQL</h3><p>Чек-листы, проверки, нарушения, пользователи, объекты, подразделения, дашборд.</p></div>
|
||||||
|
</div>
|
||||||
|
<div class="dir-card">
|
||||||
|
<div class="dir-icon">👥</div>
|
||||||
|
<div><h3>3 роли</h3><p>Инспектор (чек-листы), Руководитель (аналитика), Администратор (настройка системы).</p></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ===== WHAT YOU GET ===== -->
|
||||||
|
<section class="section" style="background: var(--gray-800); border-top:1px solid rgba(255,255,255,0.04); border-bottom:1px solid rgba(255,255,255,0.04)">
|
||||||
|
<div class="container">
|
||||||
|
<h2 style="text-align:center; margin-bottom: 48px">Что вы получаете</h2>
|
||||||
|
<div class="directions">
|
||||||
|
<div class="dir-card">
|
||||||
|
<div class="dir-icon">📄</div>
|
||||||
|
<div>
|
||||||
|
<h3>Чек-листы по 6 направлениям</h3>
|
||||||
|
<p>Структурированы под требования РК. Настраиваются под ваше предприятие.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dir-card">
|
||||||
|
<div class="dir-icon">📄</div>
|
||||||
|
<div>
|
||||||
|
<h3>Указание по БиОТ</h3>
|
||||||
|
<p>Автоформирование по вашему корпоративному шаблону. PDF, Word, QR-код, эл. подпись.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dir-card">
|
||||||
|
<div class="dir-icon">📈</div>
|
||||||
|
<div>
|
||||||
|
<h3>Аналитическая панель</h3>
|
||||||
|
<p>Дашборд, графики по направлениям, KPI подразделений, карта выездных проверок.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dir-card">
|
||||||
|
<div class="dir-icon">🧠</div>
|
||||||
|
<div>
|
||||||
|
<h3>ИИ-распознавание</h3>
|
||||||
|
<p>Фото нарушения → готовая строка для указания. Оформление за 10 минут вместо 2 часов.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dir-card">
|
||||||
|
<div class="dir-icon">🔔</div>
|
||||||
|
<div>
|
||||||
|
<h3>Контроль сроков</h3>
|
||||||
|
<p>Уведомления о просрочках, рейтинг ответственных, индекс безопасности подразделений.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="dir-card">
|
||||||
|
<div class="dir-icon">🔒</div>
|
||||||
|
<div>
|
||||||
|
<h3>Корпоративная авторизация</h3>
|
||||||
|
<p>Роли: Инспектор, Руководитель, Администратор. Доступ по корпоративному логину.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ===== WAITLIST ===== -->
|
||||||
|
<section class="section" id="waitlist">
|
||||||
|
<div class="container">
|
||||||
|
<div class="waitlist">
|
||||||
|
<h2>Хотите попробовать первым?</h2>
|
||||||
|
<p>Оставьте контакты — сообщим о запуске и предоставим ранний доступ.</p>
|
||||||
|
<form action="https://formspree.io/f/YOUR_FORM_ID" method="POST">
|
||||||
|
<input type="email" name="email" placeholder="Email" required>
|
||||||
|
<button type="submit" class="btn">Отправить</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ===== FAQ ===== -->
|
||||||
|
<section class="section">
|
||||||
|
<div class="container">
|
||||||
|
<div class="section-label">FAQ</div>
|
||||||
|
<h2>Частые вопросы</h2>
|
||||||
|
<div class="faq-list">
|
||||||
|
<details class="faq-item">
|
||||||
|
<summary>Когда будет готова первая версия?</summary>
|
||||||
|
<p>MVP — 1-2 месяца. Включает: авторизация, чек-листы по 6 направлениям, фотофиксация, автоформирование «Указания по БиОТ», экспорт PDF, история проверок.</p>
|
||||||
|
</details>
|
||||||
|
<details class="faq-item">
|
||||||
|
<summary>Можно ли использовать нашу форму Указания по БиОТ?</summary>
|
||||||
|
<p>Да, система настраивается под утверждённый корпоративный шаблон или форму, применяемую в организации в соответствии с законодательством РК.</p>
|
||||||
|
</details>
|
||||||
|
<details class="faq-item">
|
||||||
|
<summary>Работает ли без интернета на объекте?</summary>
|
||||||
|
<p>Да, офлайн-режим: все данные сохраняются на устройстве и синхронизируются с сервером при появлении сети.</p>
|
||||||
|
</details>
|
||||||
|
<details class="faq-item">
|
||||||
|
<summary>Как работает ИИ-распознавание нарушений?</summary>
|
||||||
|
<p>Инспектор делает фото нарушения, система определяет тип, формулирует запись, предлагает корректирующее мероприятие, категорию риска и нормативную ссылку. Инспектор подтверждает — запись попадает в Указание.</p>
|
||||||
|
</details>
|
||||||
|
<details class="faq-item">
|
||||||
|
<summary>Какие роли есть в системе?</summary>
|
||||||
|
<p>Инспектор — проводит проверки, Руководитель — видит дашборд и аналитику, Администратор — настраивает чек-листы, шаблоны и доступы.</p>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="footer">
|
||||||
|
<div class="container">
|
||||||
|
<span>2026 · Цифровой инспектор БиОТ</span>
|
||||||
|
<a href="#waitlist">Оставить заявку</a>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Reference in New Issue
Block a user