Files
site-portfolio/public/cases/ai-platform-primo-rpa-v2/index.html
T
Hermes 336f9b5707 feat(portfolio): add portfolio-card--bleed variant, use in Portfolio block + design-system
- new .portfolio-card--bleed: image pinned bottom-right, no padding,
  corner rounded by card (overflow:hidden)
- design-system catalog: add bleed specimen (ru/en)
- projects-section (ru/en): portfolio cards now use --bleed
2026-09-16 15:25:53 +02:00

1200 lines
74 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" data-theme="dark" data-radius="medium" data-font="merriweather">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
<meta
name="description"
content="Кейс: редизайн Primo RPA AI Server — снижение порога входа для клиентов без команды внедрения."
/>
<title>AI Server в Primo RPA — кейс</title>
<script>
(function () {
var FONT_CSS = {
merriweather: "/assets/fonts/merriweather.css",
geologica: "/assets/fonts/geologica.css",
mono: "/assets/fonts/mono.css",
};
try {
var t = JSON.parse(localStorage.getItem("siteThemeV1") || "{}");
var d = document.documentElement;
var m = t.mode;
if (m !== "light" && m !== "dark" && m !== "system") m = "system";
var eff =
m === "system"
? window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light"
: m;
d.dataset.theme = eff;
var h = typeof t.h === "number" ? t.h : typeof t.hue === "number" ? t.hue : null;
var s = typeof t.s === "number" ? t.s : typeof t.sat === "number" ? t.sat : null;
var l = typeof t.l === "number" ? t.l : typeof t.light === "number" ? t.light : null;
if (h !== null) d.style.setProperty("--theme-h", String(h));
if (s !== null) d.style.setProperty("--theme-s", String(s));
if (l !== null) {
var L = Number(l);
if (Number.isFinite(L)) d.style.setProperty("--theme-l", String(Math.min(70, Math.max(20, L))));
}
var hVal = h !== null ? h : 210;
var sVal = s !== null ? s : 90;
var lVal = l !== null ? Math.min(70, Math.max(20, Number(l))) : 40;
var H = (((hVal % 360) + 360) % 360) / 360;
var S = Math.min(100, Math.max(0, sVal)) / 100;
var Lv = Math.min(100, Math.max(0, lVal)) / 100;
var r, g, b;
if (S === 0) { r = g = b = Lv; }
else {
var q = Lv < 0.5 ? Lv * (1 + S) : Lv + S - Lv * S;
var p = 2 * Lv - q;
var hue2rgb = function(p, q, t) {
if (t < 0) t += 1; if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
};
r = hue2rgb(p, q, H + 1/3);
g = hue2rgb(p, q, H);
b = hue2rgb(p, q, H - 1/3);
}
var lum = 0.2126 * r + 0.7152 * g + 0.0722 * b;
d.style.setProperty("--on-accent", lum > 0.58 ? "hsl(220 14% 12%)" : "hsl(0 0% 98%)");
var rad = t.radius;
if (rad === "soft" || rad === "medium" || rad === "strong") {
d.dataset.radius = rad;
} else {
d.dataset.radius = "medium";
}
var f = t.font;
if (f === "onest") f = "geologica";
if (f === "anonymous") f = "mono";
if (f !== "merriweather" && f !== "mono" && f !== "geologica") f = "merriweather";
d.dataset.font = f;
var mb = t.meshBg;
if (mb === "off") {
d.dataset.meshBg = "off";
} else {
d.dataset.meshBg = "on";
}
var href = FONT_CSS[f];
if (href && !document.querySelector('link[data-theme-font="' + f + '"]')) {
var link = document.createElement("link");
link.rel = "stylesheet";
link.href = href;
link.setAttribute("data-theme-font", f);
document.head.appendChild(link);
}
} catch (e) {}
})();
</script>
<link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon/favicon-32x32.png" />
<link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon/favicon-16x16.png" />
<link rel="apple-touch-icon" sizes="180x180" href="/assets/favicon/apple-touch-icon.png" />
<link rel="manifest" href="/assets/favicon/site.webmanifest" />
<link rel="preload" href="/assets/fonts/merriweather.css" as="style" />
<link rel="preload" href="/css/main.css" as="style" />
<link rel="preload" href="/js/skill-connectors.js" as="script" />
<link rel="modulepreload" href="/js/main.js" />
<link rel="stylesheet" href="/css/main.css" />
<script src="/js/skill-connectors.js" defer></script>
<script type="module" src="/js/main.js"></script>
<link rel="stylesheet" href="/vendor/plyr/plyr.css" />
<style>
.case-article {
max-width: 48rem;
margin: 0 auto;
}
.case-article h2 {
margin: 2rem 0 0.75rem;
font-size: clamp(1.7rem, 3vw, 2rem);
font-weight: 700;
color: var(--text);
}
.case-article h3 {
font-size: 1.55rem;
font-weight: 600;
color: var(--text);
}
.case-article .section__prose {
margin-bottom: 0.75rem;
font-size: 1.1rem;
line-height: 1.7;
color: var(--text);
}
.case-article img,
.case-article video {
display: block;
max-width: 100%;
height: auto;
margin: 0.75rem 0 1rem;
}
.case-article ul,
.case-article ol {
margin: 0.5rem 0 1rem 1.25rem;
padding: 0;
}
.case-article li {
margin-bottom: 0.35rem;
font-size: 1.1rem;
line-height: 1.7;
}
.case-article a:not(.btn) {
color: var(--accent);
}
.case-meta {
display: grid;
grid-template-columns: auto 1fr;
gap: 0.25rem 0.75rem;
margin: 1.25rem 0 1.5rem;
font-size: 1.05rem;
line-height: 1.6;
color: var(--text-muted);
}
.case-meta dt {
font-weight: 600;
color: var(--text);
}
.case-meta dd {
margin: 0;
}
</style>
<script>
(function () {
try {
var t = JSON.parse(localStorage.getItem("siteThemeV1") || "{}");
var d = document.documentElement;
var m = t.mode;
if (m !== "light" && m !== "dark" && m !== "system") m = "system";
var eff =
m === "system"
? window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light"
: m;
d.dataset.theme = eff;
var h = typeof t.h === "number" ? t.h : typeof t.hue === "number" ? t.hue : null;
var s = typeof t.s === "number" ? t.s : typeof t.sat === "number" ? t.sat : null;
var l = typeof t.l === "number" ? t.l : typeof t.light === "number" ? t.light : null;
if (h !== null) d.style.setProperty("--theme-h", String(h));
if (s !== null) d.style.setProperty("--theme-s", String(s));
if (l !== null) {
var L = Number(l);
if (Number.isFinite(L)) d.style.setProperty("--theme-l", String(Math.min(70, Math.max(20, L))));
}
var rad = t.radius;
if (rad === "soft" || rad === "medium" || rad === "strong") {
d.dataset.radius = rad;
} else {
d.dataset.radius = "medium";
}
var f = t.font;
if (f === "onest") f = "geologica";
if (f === "anonymous") f = "mono";
if (f === "merriweather" || f === "mono" || f === "geologica") {
d.dataset.font = f;
} else {
d.dataset.font = "merriweather";
}
var mb = t.meshBg;
if (mb === "off") {
d.dataset.meshBg = "off";
} else {
d.dataset.meshBg = "on";
}
} catch (e) {}
})();
</script>
</head>
<body>
<a class="skip-link" href="#case-main">К содержимому кейса</a>
<header class="header" data-theme-picker>
<div class="header__inner">
<a class="logo" href="../../" aria-label="На главную">НМ</a>
<div class="header__cluster">
<nav class="nav" aria-label="Разделы страницы">
<a href="#contact">Контакты</a>
</nav>
</div>
</div>
</header>
<div class="fab-stack" aria-label="Быстрые действия">
<div class="theme-panel-fab-wrap">
<button type="button" class="fab fab--secondary glass-neutral" data-theme-panel-btn aria-expanded="false" aria-controls="theme-panel" aria-label="Настройки стиля">
<span class="theme-panel-fab__icon theme-panel-fab__icon--open-hide" aria-hidden="true"></span>
<svg class="theme-panel-fab__icon theme-panel-fab__icon--close" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path d="M6 6L18 18M18 6L6 18" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
</svg>
</button>
<div id="theme-panel" class="theme-picker__panel glass-neutral" role="dialog" aria-label="Тема и оформление" hidden data-theme-panel>
<div class="theme-picker__stack">
<section class="theme-picker__section" aria-labelledby="theme-accent-title">
<h3 id="theme-accent-title" class="theme-picker__section-title">Акцентный цвет</h3>
<div class="colorpicker" data-colorpicker>
<div class="colorpicker__plane-box">
<canvas class="colorpicker__plane" width="320" height="180" data-sl-canvas aria-label="Насыщенность и светлость акцента"></canvas>
<div class="colorpicker__knob" data-sl-knob></div>
</div>
<div class="colorpicker__hue-wrap">
<label class="colorpicker__hue-sr" for="theme-hue-slider">Оттенок акцента</label>
<input id="theme-hue-slider" class="colorpicker__hue" type="range" min="0" max="360" value="210" data-hue-slider aria-valuemin="0" aria-valuemax="360" />
</div>
</div>
</section>
<section class="theme-picker__section" aria-labelledby="theme-mode-title">
<h3 id="theme-mode-title" class="theme-picker__section-title">Тема</h3>
<div class="radius-seg theme-mode-seg" role="radiogroup" aria-labelledby="theme-mode-title">
<button type="button" class="radius-seg__btn" role="radio" aria-checked="false" data-theme-mode="system" aria-label="Как в системе"><span class="radius-seg__viz" aria-hidden="true"><span class="theme-mode-seg__ico theme-mode-seg__ico--system"></span></span></button>
<button type="button" class="radius-seg__btn" role="radio" aria-checked="false" data-theme-mode="light" aria-label="Светлая тема"><span class="radius-seg__viz" aria-hidden="true"><span class="theme-mode-seg__ico theme-mode-seg__ico--light"></span></span></button>
<button type="button" class="radius-seg__btn" role="radio" aria-checked="false" data-theme-mode="dark" aria-label="Тёмная тема"><span class="radius-seg__viz" aria-hidden="true"><span class="theme-mode-seg__ico theme-mode-seg__ico--dark"></span></span></button>
</div>
</section>
<section class="theme-picker__section" aria-labelledby="theme-radius-title">
<h3 id="theme-radius-title" class="theme-picker__section-title">Скругление углов</h3>
<div class="radius-seg" role="radiogroup" aria-labelledby="theme-radius-title">
<button type="button" class="radius-seg__btn" role="radio" aria-checked="false" data-radius-option="soft" aria-label="Слабое скругление"><span class="radius-seg__viz" aria-hidden="true"><span class="radius-seg__arc radius-seg__arc--soft"></span></span></button>
<button type="button" class="radius-seg__btn" role="radio" aria-checked="false" data-radius-option="medium" aria-label="Среднее скругление"><span class="radius-seg__viz" aria-hidden="true"><span class="radius-seg__arc radius-seg__arc--medium"></span></span></button>
<button type="button" class="radius-seg__btn" role="radio" aria-checked="false" data-radius-option="strong" aria-label="Сильное скругление"><span class="radius-seg__viz" aria-hidden="true"><span class="radius-seg__arc radius-seg__arc--strong"></span></span></button>
</div>
</section>
<section class="theme-picker__section" aria-labelledby="theme-typography-title">
<h3 id="theme-typography-title" class="theme-picker__section-title">Типографика</h3>
<div class="radius-seg" role="radiogroup" aria-labelledby="theme-typography-title">
<button type="button" class="radius-seg__btn theme-font-seg__btn" role="radio" aria-checked="false" data-font-option="geologica" aria-label="Sans"><span class="theme-font-seg__label">Sans</span></button>
<button type="button" class="radius-seg__btn theme-font-seg__btn" role="radio" aria-checked="false" data-font-option="merriweather" aria-label="Sans Serif"><span class="theme-font-seg__label">Sans Serif</span></button>
<button type="button" class="radius-seg__btn theme-font-seg__btn" role="radio" aria-checked="false" data-font-option="mono" aria-label="Mono"><span class="theme-font-seg__label">Mono</span></button>
</div>
</section>
<section class="theme-picker__section" aria-labelledby="theme-mesh-bg-title">
<h3 id="theme-mesh-bg-title" class="theme-picker__section-title">Фон страницы</h3>
<div class="radius-seg radius-seg--two" role="radiogroup" aria-labelledby="theme-mesh-bg-title">
<button type="button" class="radius-seg__btn theme-mesh-bg-seg__btn" role="radio" aria-checked="false" data-mesh-bg-option="on" aria-label="Показать фоновый градиент"><span class="theme-mesh-bg-seg__label">Вкл</span></button>
<button type="button" class="radius-seg__btn theme-mesh-bg-seg__btn" role="radio" aria-checked="false" data-mesh-bg-option="off" aria-label="Скрыть фоновый градиент"><span class="theme-mesh-bg-seg__label">Выкл</span></button>
</div>
</section>
</div>
</div>
</div>
<a class="fab fab--primary" href="#contact" aria-label="Связаться">
<span class="fab-primary__ico" aria-hidden="true"></span>
</a>
</div>
<div class="content-edit-fab-stack" aria-label="Правки контента" data-content-edit-ui>
<button
type="button"
class="fab fab--secondary glass-neutral content-edit-fab"
data-content-edit-mode-toggle
hidden
aria-pressed="false"
aria-label="Режим правок контента"
title="Режим правок"
>
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path
d="M4 20h4l10.5-10.5a2.12 2.12 0 0 0 0-3L16.5 4.5a2.12 2.12 0 0 0-3 0L3 15v5z"
stroke="currentColor"
stroke-width="1.75"
stroke-linejoin="round"
/>
</svg>
<span class="content-edit-fab__badge" hidden aria-hidden="true">0</span>
</button>
</div>
<main id="case-main" class="section section--tight">
<div class="section__inner case-article">
<p class="hero__eyebrow">Product Design · AI</p>
<h1 class="case-page__h1">Упростил настройку Primo RPA AI Server: клиенты настраивают ИИ-сервис без команды внедрения</h1>
<p class="case-page__sub">
Платформа интеллектуальной обработки документов<br />
и оркестрации ИИ-агентов. Обращения к команде внедрения упали на 40%, настройка первого проекта ужалась с ~3 дней до ~4 часов.
</p>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/main-new.webp"
alt="Обновлённый редактор разметки датасета — главный экран сервиса после редизайна"
width="1200"
height="675"
loading="lazy"
decoding="async"
data-image-viewer
/>
</figure>
<dl class="case-meta">
<dt>Продукт</dt>
<dd>AI Server. Нейросети во внутреннем контуре компании, настраивается через веб-интерфейс</dd>
<dt>Роль</dt>
<dd>UX/UI-дизайнер (единственный на проекте)</dd>
<dt>Задача</dt>
<dd>Снизить порог входа, чтобы клиент настраивал сервис сам</dd>
<dt>Стек</dt>
<dd>Ant Design + React, стилизация Hope UI</dd>
<dt>Формат</dt>
<dd>Редизайн живого продукта, релизы не останавливались</dd>
</dl>
<div class="case-block" id="problem">
<h2>С настройкой справлялись только эксперты. Это тормозило продажи</h2>
<p class="section__prose">
AI Server разворачивается на внутренних серверах клиентов. Продукт активно тестировали, и обратная связь показала неприятное: настроить его могли только эксперты и команда внедрения Primo RPA. Каждый новый клиент съедал часы техподдержки. Продукт терял привлекательность и плохо масштабировался.
</p>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">✅</span>
<div class="case-callout__content">
<p>
Бизнес-цель: клиент проходит настройку AI Server сам, без техподдержки и команды внедрения. По этому критерию мы и мерили успех редизайна.
</p>
</div>
</aside>
<p class="section__prose">
Начал со знакомства с командой и экспертами по настройке. Разделил сценарии на главные и вспомогательные, собрал визуализацию того, как ведёт себя пользователь, и вместе с разработкой искал, где интерфейс можно упростить. Я уже работал с ИИ и похожими веб-платформами, поэтому быстро разобрался в домене и говорил с разработкой на одном языке.
</p>
</div>
<div class="case-block" id="constraints">
<h2>Ограничения</h2>
<ul>
<li>Продуктом уже пользовались клиенты. Остановить разработку и пересобрать интерфейс целиком было нельзя.</li>
<li>Изменения приходилось вписывать в план релизов, отдельного «редизайн-спринта» никто не выделял.</li>
<li>Жёсткий стек: Ant Design под React со стилизацией под Hope UI. Новые компоненты рисовать только в крайнем случае, сначала выжимаем всё из готовой библиотеки.</li>
</ul>
</div>
<div class="case-block" id="navigation">
<h2>Навигация путала: разделы проекта смешивались с главным меню</h2>
<p class="section__prose">
В сервисе стояла привычная для админок схема: слева боковое меню, справа контент. Пространство расходовалось расточительно, а при открытии проекта навигация по его разделам оказывалась вперемешку с главной. Пользователь терялся.
</p>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji" aria-hidden="true">💡</span>
<div class="case-callout__content">
<p>
Две навигации в одном боковом меню заставляли каждый раз разбираться, «где я». Развёл их по уровням: главная сверху, проектная внутри проекта.
</p>
</div>
</aside>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/main-new.webp"
alt="Главный экран AI Server с компактной верхней навигацией"
width="1200"
height="675"
loading="lazy"
decoding="async"
data-image-viewer
/>
<figcaption>Главный экран AI Server с компактной верхней навигацией — список проектов занимает почти всю ширину экрана</figcaption>
</figure>
<p class="section__prose">
Перенёс главную навигацию наверх и ужал её. Контенту стало заметно просторнее, а схема осталась привычной. У проектов появилась собственная навигация, которая с главной не пересекается.
</p>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/nav-project-nlp.webp"
alt="Внутренняя навигация NLP-проекта"
width="1200"
height="675"
loading="lazy"
decoding="async"
data-image-viewer
/>
<figcaption>Внутренняя навигация NLP-проекта — горизонтальные пункты-шаги под главным меню</figcaption>
</figure>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/nav-project-ocr.webp"
alt="Внутренняя навигация OCR-проекта"
width="1200"
height="675"
loading="lazy"
decoding="async"
data-image-viewer
/>
<figcaption>Внутренняя навигация OCR-проекта — тот же паттерн со своим набором разделов</figcaption>
</figure>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">✨</span>
<div class="case-callout__content">
<p>
Стрелочная форма пунктов подсказывает порядок настройки, но не навязывает его: разделы остаются обычными табами. Подсказка без принуждения.
</p>
</div>
</aside>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/nav-secondary-detail_view.webp"
alt="Крупный план вторичной навигации — пункты в форме стрелок"
width="1200"
height="675"
loading="lazy"
decoding="async"
data-image-viewer
/>
<figcaption>Крупный план вторичной навигации — пункты в форме стрелок, ведущих слева направо</figcaption>
</figure>
</div>
<div class="case-block" id="editor">
<h2>Редактор датасета: экран, где пользователи живут</h2>
<p class="section__prose">
Здесь проводят больше всего времени. Разметка полей на сотнях изображений — работа монотонная, но именно от неё зависит качество данных для обучения модели. Прежний редактор выбивался из общего стиля, выглядел перегруженно и путал в работе с полями.
</p>
<p class="section__prose">
Разобрал редактор на микросценарии, выстроил их по приоритету и связкам. Вместе с командой спроектировали поведение плавнее и понятнее, заложили место под будущий функционал, дизайн привели к общей стилистике сервиса.
</p>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/dataset-editor.webp"
alt="Редактор разметки датасета — размечаемый документ в центре, панели полей и инструментов по бокам"
width="1200"
height="675"
loading="lazy"
decoding="async"
data-image-viewer
/>
<figcaption>Редактор разметки датасета — размечаемый документ в центре, панели полей и инструментов по бокам</figcaption>
</figure>
<p class="section__prose">
Страница стала симметричной. Почти всё пространство уходит на размечаемое изображение, действия сгруппированы по панелям: нужная функция ищется быстро.
</p>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/dataset_editor-wireframe-default_mode.webp"
alt="Схематичный каркас редактора — центральная рабочая область и боковые панели"
width="1200"
height="675"
loading="lazy"
decoding="async"
data-image-viewer
/>
<figcaption>Схематичный каркас редактора — центральная рабочая область и боковые панели, режим по умолчанию</figcaption>
</figure>
<p class="section__prose">
Для продвинутых пользователей добавил режим «Во весь экран». Боковые панели сворачиваются, работа идёт через горячие клавиши и нижнюю панель. На небольших мониторах это спасает.
</p>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/full-screen-mode-with-panels.webp"
alt="Полноэкранный режим редактора с раскрытыми боковыми панелями"
width="1200"
height="675"
loading="lazy"
decoding="async"
data-image-viewer
/>
<figcaption>Полноэкранный режим редактора с раскрытыми боковыми панелями</figcaption>
</figure>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/full-screen-mode-zen.webp"
alt="Полноэкранный режим без панелей — только размечаемый документ и нижняя панель"
width="1200"
height="675"
loading="lazy"
decoding="async"
data-image-viewer
/>
<figcaption>Полноэкранный режим без панелей — на экране только размечаемый документ и нижняя панель инструментов</figcaption>
</figure>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/dataset_editor-wireframe-zen_mode-both_panels.webp"
alt="Четыре варианта каркаса полноэкранного режима"
width="1200"
height="675"
loading="lazy"
decoding="async"
data-image-viewer
/>
<figcaption>Четыре варианта каркаса полноэкранного режима — обе панели, без панелей, только левая, только правая</figcaption>
</figure>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/dataset_editor-main_toolbar.webp"
alt="Нижняя панель инструментов — переключение между изображениями и быстрые действия разметки"
width="1200"
height="675"
loading="lazy"
decoding="async"
data-image-viewer
/>
<figcaption>Нижняя панель инструментов — переключение между изображениями и быстрые действия разметки</figcaption>
</figure>
</div>
<div class="case-block" id="mapping">
<h2>Десятки полей на одном скане, и место под документ не страдает</h2>
<p class="section__prose">
Смежная задача: настроить уникальные параметры полей датасета. Большая область нужна под скан документа, полей десятки, у каждого свои типы и зависимые параметры.
</p>
<figure class="case-media" style="margin: 0.85rem 0 1.2rem">
<div class="case-video-shell">
<video
controls
preload="metadata"
playsinline
poster="../../assets/ai-platform-primo-rpa/fields-maping.webp"
data-case-video-player
style="display: block; width: 100%; height: auto; margin: 0"
>
<source src="../../assets/ai-platform-primo-rpa/fields-maping.webm" type="video/webm" />
<source src="../../assets/ai-platform-primo-rpa/fields-maping.mp4" type="video/mp4" />
Ваш браузер не поддерживает видео.
</video>
</div>
<figcaption>Прототип сопоставления полей — пользователь выбирает поле на скане документа и настраивает его параметры в компактной панели</figcaption>
</figure>
<p class="section__prose">
Интерфейс вышел компактным. Механика не считывается с первого взгляда, зато после короткого обучения работать в нём быстро и комфортно.
</p>
</div>
<div class="case-block" id="learning">
<h2>Карточки обучения: паттерн, который разошёлся по всему продукту</h2>
<p class="section__prose">
После разметки датасета модель дообучают (fine-tuning). Разобрали все действия пользователя, перебрали несколько компоновок страницы. Остановились на ленте из карточек.
</p>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/in_project-learn-overview.webp"
alt="Раздел обучения моделей — вертикальная лента карточек"
width="1200"
height="675"
loading="lazy"
decoding="async"
data-image-viewer
/>
<figcaption>Раздел обучения моделей — вертикальная лента карточек, сверху карточка настройки нового обучения</figcaption>
</figure>
<p class="section__prose">
В начале ленты карточка настройки: результат разметки активного датасета, параметры, запуск fine-tuning.
</p>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/in_project-learn-card-new.webp"
alt="Карточка настройки обучения — матрица качества датасета и блок параметров запуска"
width="1200"
height="675"
loading="lazy"
decoding="async"
data-image-viewer
/>
<figcaption>Карточка настройки обучения — матрица качества датасета и блок параметров запуска</figcaption>
</figure>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">⭐</span>
<div class="case-callout__content">
<p>
Матрица оценки качества разметки показывает готовность датасета к обучению на одном экране. Раньше приходилось вручную просматривать поля по всем документам.
</p>
</div>
</aside>
<p class="section__prose">
Под каждый запущенный процесс появляется своя карточка. Можно завести несколько процессов с разными параметрами и сравнить, какая конфигурация отработала лучше.
</p>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/in_project-learn-card-learning_process.webp"
alt="Карточка запущенного процесса обучения — статус, прогресс и метрики"
width="1200"
height="675"
loading="lazy"
decoding="async"
data-image-viewer
/>
<figcaption>Карточка запущенного процесса обучения — статус, прогресс и метрики, разнесённые по логическим блокам</figcaption>
</figure>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/in_project-learn-card-learning_process_wireframe.webp"
alt="Каркас карточки процесса обучения"
width="1200"
height="675"
loading="lazy"
decoding="async"
data-image-viewer
/>
<figcaption>Каркас карточки процесса обучения — схема расположения информационных блоков и действий</figcaption>
</figure>
<p class="section__prose">
Карточки сворачиваются, в шапке остаются название и статус.
</p>
<figure class="case-media case-media--natural">
<img
src="../../assets/ai-platform-primo-rpa/in_project-learn-card-сollapsed.webp"
alt="Свёрнутая карточка обучения — одна строка с названием и статусом процесса"
width="1200"
height="675"
loading="lazy"
decoding="async"
/>
<figcaption>Свёрнутая карточка обучения — одна строка с названием и статусом процесса</figcaption>
</figure>
<p class="section__prose">
Паттерны из этих карточек потом разошлись по другим разделам продукта. Единый стиль стало проще держать.
</p>
</div>
<div class="case-block" id="processes">
<h2>Долгие процессы — в соседнюю вкладку</h2>
<p class="section__prose">
Часть задач идёт параллельно с остальной работой. Для таких сценариев модальное окно открывается на отдельной странице.
</p>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji" aria-hidden="true">💡</span>
<div class="case-callout__content">
<p>
Пользователь держит окно процесса в соседней вкладке и следит за прогрессом, а с проектом продолжает работать в доступных разделах.
</p>
</div>
</aside>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/fullscreen_modal-import_dataset.webp"
alt="Импорт датасета в модальном окне на отдельной странице"
width="1440"
height="1024"
loading="lazy"
decoding="async"
data-image-viewer
/>
<figcaption>Импорт датасета в модальном окне на отдельной странице — форма загрузки на весь экран</figcaption>
</figure>
<p class="section__prose">
На странице проекта в это время висит заглушка с кнопкой перехода к процессу.
</p>
<figure class="case-media case-media--wide">
<img
src="../../assets/ai-platform-primo-rpa/fullscreen_modal-in_project-import_dataset.webp"
alt="Заглушка активного импорта внутри проекта"
width="1200"
height="675"
loading="lazy"
decoding="async"
data-image-viewer
/>
<figcaption>Заглушка активного импорта внутри проекта — индикатор процесса и кнопка перехода</figcaption>
</figure>
</div>
<div class="case-block" id="decisions">
<h2>Что я решил не делать, и почему</h2>
<ul>
<li><strong>Не стал блокировать кнопки на отдельных страницах</strong> во время активных процессов. Модалка на отдельной странице технически проще, а прогресс виден в соседней вкладке.</li>
<li><strong>Не рисовал новые компоненты</strong>, кроме крайних случаев. Готовая библиотека Ant Design ускоряла разработку и держала интерфейс консистентным.</li>
<li><strong>Не убрал боковые панели редактора совсем</strong>, а сделал сворачиваемыми. Новичкам нужны подсказки на экране, продвинутым — максимум места под документ.</li>
<li><strong>Не заводил отдельную дизайн-систему.</strong> В продукте уже был фреймворк, в разработку передавал ссылки на готовые юзерфлоу в Figma.</li>
</ul>
<h3>Передача в разработку</h3>
<p class="section__prose">
Раз в неделю обсуждали дизайн с командой: список задач, приоритеты, сроки под планы релизов. С верстальщиком уточнял детали макетов тет-а-тет и давал фидбэк после вёрстки, чтобы результат не разошёлся с задуманным поведением.
</p>
</div>
<div class="case-block" id="results">
<h2>Результат</h2>
<aside class="case-callout" data-callout>
<span class="case-callout__emoji case-callout__emoji--pulse" aria-hidden="true">📈</span>
<div class="case-callout__content">
<p>
Настройка первого проекта: было ~3 дня, стало ~4 часа. 7 из 10 клиентов проходят её без техподдержки.
</p>
</div>
</aside>
<ul>
<li>Обращения к команде внедрения на этапе настройки сократились на <strong>40%</strong>. Базовую настройку клиенты проходят сами.</li>
<li><strong>7 из 10</strong> новых клиентов настроили первый проект без техподдержки. До редизайна — 1 из 10.</li>
<li>Настройка первого проекта: с <strong>~3 дней до ~4 часов</strong>.</li>
<li>Переработано <strong>30+ экранов</strong>: навигация, редактор датасета, обучение моделей, импорт данных.</li>
<li>Заложены паттерны, на которых функционал наращивается без больших переделок.</li>
<li>В компанию наняли второго дизайнера. Я ввёл её в курс дела и курировал результат, после адаптации проект целиком перешёл под её ответственность.</li>
</ul>
</div>
<div class="case-block" id="conclusions">
<h2>Выводы</h2>
<ul>
<li>Жёсткий фреймворк оказался не ограничением, а ускорителем: готовая библиотека компонентов позволила думать о сценариях, а не о пикселях.</li>
<li>Самые ценные инсайты дали эксперты по настройке. В следующий раз вовлёк бы команду внедрения ещё раньше.</li>
<li>Передача проекта новому дизайнеру подтвердила: заложенные паттерны работают, их можно объяснить и поддерживать без автора.</li>
</ul>
</div>
</div>
</main>
<section class="section section--surface" data-reveal>
<div class="section__inner">
<h2 class="section__title">Смотрите также</h2>
<ol class="mini-projects-grid" data-mini-projects-list>
<li class="mini-projects-grid__item" data-mp-cells="3" data-mp-cells-sm="2" data-related-slug="ai-platform-primo-rpa">
<a class="mp-card mp-card--link mp-card--stack" href="../ai-platform-primo-rpa/" aria-label="Платформа для работы с AI — открыть кейс">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../assets/ai-platform-primo-rpa/cover.webp"
alt=""
width="400"
height="225"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Primo AI Server</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">UX/UI</li>
<li class="mp-card__tag">Web App</li>
</ul>
<p class="mp-card__desc">Интерфейс для обучения и использования AI‑моделей в бизнес‑процессах: работа с данными, сценариями автоматизации и Smart OCR.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="3" data-mp-cells-sm="2" data-related-slug="lunacy">
<a class="mp-card mp-card--link mp-card--stack" href="../lunacy/" aria-label="Графический редактор Lunacy — открыть кейс">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../assets/lunacy/cover.webp"
alt=""
width="400"
height="225"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Lunacy</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">UX/UI</li>
<li class="mp-card__tag">Desktop App</li>
</ul>
<p class="mp-card__desc">Работа над интерфейсом Lunacy в команде Icons8: user flow, сложные прототипы, улучшение функциональности и интерфейсной логики.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="3" data-mp-cells-sm="2" data-related-slug="manu-game-maker" hidden>
<a class="mp-card mp-card--link mp-card--stack" href="../manu-game-maker/" aria-label="Игровой движок MANU — открыть кейс">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../assets/manu-game-maker/cover.webp"
alt=""
width="400"
height="225"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Игровой движок MANU</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Product Design</li>
<li class="mp-card__tag">Desktop App</li>
</ul>
<p class="mp-card__desc">Редизайн и развитие интерфейса игрового движка: новая концепция работы, обновлённая визуальная система и проработка сотен сложных пользовательских сценариев.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="3" data-mp-cells-sm="2" data-related-slug="mobile-clinic-app" hidden>
<a class="mp-card mp-card--link mp-card--stack" href="../mobile-clinic-app/" aria-label="Мобильное приложение для сети клиник — открыть кейс">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../assets/mobile-clinic-app/cover.webp"
alt=""
width="400"
height="225"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Мобильное приложение для сети клиник</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">UX/UI</li>
<li class="mp-card__tag">Mobile App</li>
</ul>
<p class="mp-card__desc">Дизайн мобильного приложения для записи к врачам и работы с медицинскими услугами. Фокус на простом пользовательском пути, понятной навигации и быстром доступе к нужным действиям.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="3" data-mp-cells-sm="2" data-related-slug="orc-automation-panel">
<a class="mp-card mp-card--link mp-card--stack" href="../orc-automation-panel/" aria-label="Веб-панель для оркестрации автоматизаций — открыть кейс">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../assets/orc-primo-rpa/cover.webp"
alt=""
width="400"
height="225"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Primo Orc</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Менторство и наставничество</li>
</ul>
<p class="mp-card__desc">Веб-кабинет оркестратора Primo RPA: проекты, запуск и контроль RPA-роботов. Редизайн ключевых экранов под продакшен без остановки релизов.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="3" data-mp-cells-sm="2" data-related-slug="task-distribution-concept" hidden>
<a class="mp-card mp-card--link mp-card--stack" href="../task-distribution-concept/" aria-label="Концепт сервиса распределения задач — открыть кейс">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../assets/task-distribution-concept/cover.webp"
alt=""
width="400"
height="225"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Концепт сервиса распределения задач</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">UX/UI</li>
<li class="mp-card__tag">Analytics</li>
</ul>
<p class="mp-card__desc">Продуктовый концепт сервиса для постановки, распределения и отслеживания задач, с акцентом на прозрачность процессов и удобство командной работы.</p>
</div>
</a>
</li>
<li class="mini-projects-grid__item" data-mp-cells="3" data-mp-cells-sm="2" data-related-slug="task-tracker" hidden>
<a class="mp-card mp-card--link mp-card--stack" href="../task-tracker/" aria-label="Таск-трекер для бек-офис системы — открыть кейс">
<div class="mp-card__media" aria-hidden="true">
<img
class="mp-card__image"
src="../../assets/task-tracker/cover.webp"
alt=""
width="400"
height="225"
loading="lazy"
decoding="async"
/>
</div>
<div class="mp-card__body">
<h3 class="mp-card__title">Таск-трекер для бек-офис системы</h3>
<ul class="mp-card__tags" aria-label="Тип проекта">
<li class="mp-card__tag">Product Design</li>
<li class="mp-card__tag">Web App</li>
</ul>
<p class="mp-card__desc">Интерфейс для планирования и распределения задач в операционном отделе: общий пул, личный ToDo, обзор по исполнителям; вкладки, фильтры, доска / список / таблица, Drag&amp;Drop.</p>
</div>
</a>
</li>
</ol>
</div>
</section>
<section hidden class="section section--surface" aria-hidden="true">
<div class="section__inner">
<div class="projects__heading">
<h2 class="section__title projects__title">Портфолио</h2>
<div class="projects__controls" aria-label="Навигация по карточкам портфолио">
<button type="button" class="projects__nav-btn" data-projects-prev aria-label="Прокрутить влево" aria-controls="projects-viewport">
<span class="projects__nav-ico projects__nav-ico--left" aria-hidden="true"></span>
</button>
<button type="button" class="projects__nav-btn" data-projects-next aria-label="Прокрутить вправо" aria-controls="projects-viewport">
<span class="projects__nav-ico projects__nav-ico--right" aria-hidden="true"></span>
</button>
</div>
</div>
<div class="projects-carousel">
<div id="projects-viewport" class="projects-carousel__viewport" data-projects-viewport>
<div class="projects-carousel__track">
<div class="projects-carousel__spacer" aria-hidden="true"></div>
<article class="projects-carousel__item">
<a class="portfolio-card portfolio-card--bleed" href="../orc-automation-panel/">
<div class="portfolio-card__body">
<h3 class="portfolio-card__title">Веб-панель для оркестрации автоматизаций</h3>
<p class="portfolio-card__summary">
Веб-кабинет оркестратора Primo RPA: проекты, запуск и контроль RPA-роботов. Редизайн ключевых экранов под
продакшен без остановки релизов.
</p>
<ul class="portfolio-card__tags" aria-label="Ключевые теги">
<li class="portfolio-card__tag">UX/UI</li>
<li class="portfolio-card__tag">Web App</li>
</ul>
</div>
<div class="portfolio-card__cover" aria-hidden="true">
<img
class="portfolio-card__image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-defer-src="../../assets/orc-primo-rpa/cover.webp"
alt=""
width="400"
height="225"
decoding="async"
/>
</div>
</a>
</article>
<article class="projects-carousel__item">
<a class="portfolio-card portfolio-card--bleed" href="../ai-platform-primo-rpa/">
<div class="portfolio-card__body">
<h3 class="portfolio-card__title">Платформа для работы с AI</h3>
<p class="portfolio-card__summary">
Интерфейс для обучения и использования AI‑моделей в бизнес‑процессах: работа с данными, сценариями
автоматизации и Smart OCR.
</p>
<ul class="portfolio-card__tags" aria-label="Ключевые теги">
<li class="portfolio-card__tag">UX/UI</li>
<li class="portfolio-card__tag">Web App</li>
</ul>
</div>
<div class="portfolio-card__cover" aria-hidden="true">
<img
class="portfolio-card__image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-defer-src="../../assets/ai-platform-primo-rpa/cover.webp"
alt=""
width="400"
height="225"
decoding="async"
/>
</div>
</a>
</article>
<article class="projects-carousel__item">
<a class="portfolio-card portfolio-card--bleed" href="../task-tracker/">
<div class="portfolio-card__body">
<h3 class="portfolio-card__title">Таск-трекер для бек-офис системы</h3>
<p class="portfolio-card__summary">
Интерфейс для планирования и распределения задач в операционном отделе: общий пул, личный ToDo, обзор по
исполнителям; вкладки, фильтры, виды доска / список / таблица, Drag&amp;Drop.
</p>
<ul class="portfolio-card__tags" aria-label="Ключевые теги">
<li class="portfolio-card__tag">Product Design</li>
<li class="portfolio-card__tag">Web App</li>
</ul>
</div>
<div class="portfolio-card__cover" aria-hidden="true">
<img
class="portfolio-card__image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-defer-src="../../assets/task-tracker/cover.webp"
alt=""
width="400"
height="225"
decoding="async"
/>
</div>
</a>
</article>
<article class="projects-carousel__item">
<a class="portfolio-card portfolio-card--bleed" href="../manu-game-maker/">
<div class="portfolio-card__body">
<h3 class="portfolio-card__title">Игровой движок MANU</h3>
<p class="portfolio-card__summary">
Редизайн и развитие интерфейса игрового движка: новая концепция работы, обновлённая визуальная система и
проработка сотен сложных пользовательских сценариев.
</p>
<ul class="portfolio-card__tags" aria-label="Ключевые теги">
<li class="portfolio-card__tag">Product Design</li>
<li class="portfolio-card__tag">Desktop App</li>
</ul>
</div>
<div class="portfolio-card__cover" aria-hidden="true">
<img
class="portfolio-card__image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-defer-src="../../assets/manu-game-maker/cover.webp"
alt=""
width="400"
height="225"
decoding="async"
/>
</div>
</a>
</article>
<article class="projects-carousel__item">
<a class="portfolio-card portfolio-card--bleed" href="../lunacy/">
<div class="portfolio-card__body">
<h3 class="portfolio-card__title">Графический редактор Lunacy</h3>
<p class="portfolio-card__summary">
Работа над интерфейсом Lunacy в команде Icons8: user flow, сложные прототипы, улучшение функциональности и
интерфейсной логики.
</p>
<ul class="portfolio-card__tags" aria-label="Ключевые теги">
<li class="portfolio-card__tag">UX/UI</li>
<li class="portfolio-card__tag">Desktop App</li>
</ul>
</div>
<div class="portfolio-card__cover" aria-hidden="true">
<img
class="portfolio-card__image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-defer-src="../../assets/lunacy/cover.webp"
alt=""
width="400"
height="225"
decoding="async"
/>
</div>
</a>
</article>
<article class="projects-carousel__item">
<a class="portfolio-card portfolio-card--bleed" href="../mobile-clinic-app/">
<div class="portfolio-card__body">
<h3 class="portfolio-card__title">Мобильное приложение для сети клиник</h3>
<p class="portfolio-card__summary">
Дизайн мобильного приложения для записи к врачам и работы с медицинскими услугами. Фокус на простом
пользовательском пути, понятной навигации и быстром доступе к нужным действиям.
</p>
<ul class="portfolio-card__tags" aria-label="Ключевые теги">
<li class="portfolio-card__tag">UX/UI</li>
<li class="portfolio-card__tag">Mobile App</li>
</ul>
</div>
<div class="portfolio-card__cover" aria-hidden="true">
<img
class="portfolio-card__image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-defer-src="../../assets/mobile-clinic-app/cover.webp"
alt=""
width="400"
height="225"
decoding="async"
/>
</div>
</a>
</article>
<article class="projects-carousel__item">
<a class="portfolio-card portfolio-card--bleed" href="../task-distribution-concept/">
<div class="portfolio-card__body">
<h3 class="portfolio-card__title">Концепт сервиса распределения задач</h3>
<p class="portfolio-card__summary">
Продуктовый концепт сервиса для постановки, распределения и отслеживания задач, с акцентом на прозрачность
процессов и удобство командной работы.
</p>
<ul class="portfolio-card__tags" aria-label="Ключевые теги">
<li class="portfolio-card__tag">UX/UI</li>
<li class="portfolio-card__tag">Analytics</li>
</ul>
</div>
<div class="portfolio-card__cover" aria-hidden="true">
<img
class="portfolio-card__image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-defer-src="../../assets/task-distribution-concept/cover.webp"
alt=""
width="400"
height="225"
decoding="async"
/>
</div>
</a>
</article>
<article class="projects-carousel__item">
<a class="portfolio-card portfolio-card--bleed" href="../obsidian-plugin-columns-view/">
<div class="portfolio-card__body">
<h3 class="portfolio-card__title">Колоночный вид — плагин для Obsidian</h3>
<p class="portfolio-card__summary">
Интерфейс для многоколоночной работы с заметками: удобная навигация, фильтрация и настройка
колончатого отображения прямо в редакторе Obsidian.
</p>
<ul class="portfolio-card__tags" aria-label="Ключевые теги">
<li class="portfolio-card__tag">UX/UI</li>
<li class="portfolio-card__tag">Plugin</li>
</ul>
</div>
<div class="portfolio-card__cover" aria-hidden="true">
<img
class="portfolio-card__image"
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
data-defer-src="../../assets/obsidian-plugin-columns-view/filter use.webp"
alt=""
width="400"
height="225"
decoding="async"
/>
</div>
</a>
</article>
<div class="projects-carousel__spacer" aria-hidden="true"></div>
</div>
</div>
<button
type="button"
class="projects-carousel__edge-hit projects-carousel__edge-hit--left"
data-projects-edge-prev
aria-label="Прокрутить портфолио влево"
></button>
<button
type="button"
class="projects-carousel__edge-hit projects-carousel__edge-hit--right"
data-projects-edge-next
aria-label="Прокрутить портфолио вправо"
></button>
</div>
</div>
</section>
<section id="contact" class="section section--surface section--footerish" data-reveal>
<div class="section__inner">
<h2 class="section__title">Контакты</h2>
<ul class="contact-grid">
<li><a class="contact-card contact-card--link contact-card--brand-tg" href="https://t.me/mamadaevv" rel="noopener noreferrer"><span class="contact-card__corner" aria-hidden="true"></span><span class="contact-card__icon" aria-hidden="true"><img src="../../assets/shared/icons/tg.svg" alt="" width="24" height="24" loading="eager" decoding="async" /></span><span class="contact-card__body"><span class="contact-card__name">Telegram</span><span class="contact-card__hint">Отвечу сегодня</span></span></a></li>
<li><a class="contact-card contact-card--link contact-card--brand-vk" href="https://vk.com/mamadaevv" rel="noopener noreferrer"><span class="contact-card__corner" aria-hidden="true"></span><span class="contact-card__icon" aria-hidden="true"><img src="../../assets/shared/icons/vk.svg" alt="" width="24" height="24" loading="eager" decoding="async" /></span><span class="contact-card__body"><span class="contact-card__name">ВКонтакте</span><span class="contact-card__hint">Стабильный коннект</span></span></a></li>
<li><button type="button" class="contact-card contact-card--action contact-card--brand-email" data-contact-copy data-email-b64="bmlrQG1hbWFkYWV2di5zaXRl" data-hint-default="Скопировать адрес" aria-label="Скопировать адрес электронной почты в буфер обмена"><span class="contact-card__corner contact-card__corner--copy" aria-hidden="true"></span><span class="contact-card__icon contact-card__icon--accent" aria-hidden="true"></span><span class="contact-card__body"><span class="contact-card__name">Почта</span><span class="contact-card__hint" data-contact-copy-hint>Скопировать адрес</span></span></button></li>
</ul>
</div>
</section>
<footer class="footer">
<div class="footer__inner">
<span class="footer__chunk footer__chunk--date">© 2015–2026<span class="footer__sep" aria-hidden="true"> · </span></span>
<span class="footer__chunk footer__chunk--main">Дизайн и разработка сайта — Николай Мамадаев</span>
<span class="footer__chunk footer__chunk--meta footer__attr"><span class="footer__sep" aria-hidden="true"> · </span><a href="../../cases/design-system/">Design System</a><span class="footer__sep" aria-hidden="true"> · </span><a href="../../cookie-policy/">Политика cookie</a></span>
<span class="footer__chunk footer__chunk--lang"><span class="footer__sep" aria-hidden="true"> · </span><nav class="lang-switch" aria-label="Выбор языка" data-lang-switch>
<a class="lang-switch__btn" data-lang="ru" href="/">RU</a>
<a class="lang-switch__btn" data-lang="en" href="/en/">EN</a>
</nav>
<script>
(function () {
var p = location.pathname.replace(/^\/+/, "");
var isEn = p.indexOf("en/") === 0;
var base = isEn ? p.slice(3) : p;
var links = document.querySelectorAll("[data-lang-switch] [data-lang]");
for (var i = 0; i < links.length; i++) {
var b = links[i];
var l = b.getAttribute("data-lang");
b.setAttribute("href", (l === "en" ? "/en/" : "/") + base);
if ((l === "en") === isEn) b.setAttribute("aria-current", "true");
}
})();
</script>
</span>
</div>
</footer>
<aside class="cookie-notice glass-neutral" hidden data-cookie-notice>
<p class="cookie-notice__text">
Сайт использует cookie для комфортной работы. Подробнее — в
<a href="../../cookie-policy/" data-cookie-policy-link>cookie policy</a>.
</p>
<button type="button" class="btn btn--secondary btn--flat cookie-notice__accept" data-cookie-accept>
<span>Принять</span>
<span class="cookie-notice__accept-icon" aria-hidden="true">
<img src="../../assets/shared/icons/сookie.svg" alt="" width="20" height="20" loading="eager" decoding="async" />
</span>
</button>
</aside>
<div id="nav-more-menu-floating" class="nav-more__floating" hidden data-nav-more-menu-floating aria-hidden="true"></div>
</body>
</html>