Секция карточек
Компонент-секция с карточками услуг. Каждая карточка имеет уникальную визуализацию: tool badges, концентрические дуги, чеклист автоматизации, бар-чарт, shield с протоколами и анимированная SVG-книга.
Превью
Услуга
Настройка автоматической проверки безопасности (CI/CD + SAST/DAST/SCA)
Подключаю SAST, DAST и SCA к CI/CD, чтобы проект автоматически проверял код, работающее приложение и зависимости на уязвимости при каждом обновлении.
Услуга
Проверка безопасности (blackbox, graybox, whitebox)
Проверяю сайт, приложение или API в подходящем формате: снаружи, с частичным доступом или с полным доступом к коду и ручной проверкой находок.
Услуга
Автоматизация
Автоматизируем рутину — от простого bash-скрипта до сложных решений под индивидуальные требования.
Услуга
Разбор найденных уязвимостей (Triage)
Разбираю отчёты сканеров и bug bounty, отсеиваю ложные срабатывания и расставляю приоритеты: что чинить срочно, а что может подождать.
Услуга
Поиск утечек данных (Data Leak Search)
Ищу, не утекли ли данные компании или пользователя: базы, пароли, документы, переписки, открытые базы и слитые архивы.
Открытые знания
Знания каждому!
Пишем понятные статьи и гайды по DevSecOps и не только. Рассказываем как настроить безопасность с нуля и сделать её частью культуры команды.
<script lang="ts">
// ─── Automation checklist items ───────────────────────────────────────────
const automationVisible = [
"ежедневный бэкап данных и логов",
"управление сетью и firewall-правилами",
"оптимизация работы git-репозиториев",
];
const automationHidden = [
"ротация ключей доступа",
"мониторинг состояния сервисов",
"автоочистка устаревших артефактов",
];
</script>
<section class="services-grid">
<style>
/* ── Grid layout ─────────────────────────────────────────────────── */
.services-grid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto auto;
gap: 0.75rem;
width: 100%;
box-sizing: border-box;
}
/* Card 1 — top-left */
.card-security-analysis {
grid-column: 1;
grid-row: 1;
}
/* Card 2 — top-right */
.card-pentest {
grid-column: 2;
grid-row: 1;
}
/* Card 3 — full-width middle */
.card-automation {
grid-column: 1 / -1;
grid-row: 2;
}
/* Card 4 — bottom-left */
.card-dev {
grid-column: 1;
grid-row: 3;
}
/* Card 5 — bottom-right */
.card-access {
grid-column: 2;
grid-row: 3;
}
/* Card 6 — full-width bottom (knowledge) */
.card-knowledge {
grid-column: 1 / -1;
grid-row: 4;
}
@media (max-width: 600px) {
.services-grid {
grid-template-columns: 1fr;
}
.card-security-analysis,
.card-pentest,
.card-automation,
.card-dev,
.card-access,
.card-knowledge {
grid-column: 1;
grid-row: auto;
}
}
/* ── Shared card shell ────────────────────────────────────────────── */
.card-shell {
position: relative;
isolation: isolate;
overflow: hidden;
border-radius: 0.75rem;
background: var(--background-inset);
padding: 0.375rem;
box-shadow:
inset 0px 1px 1px -0.5px rgba(0, 0, 0, 0.06),
inset 0px 3px 3px -1.5px rgba(0, 0, 0, 0.06),
inset 0px 6px 6px -3px rgba(0, 0, 0, 0.06),
inset 0 -0.5px rgba(255, 255, 255, 0.08),
inset 0 0 0 0.5px oklch(from var(--highlight) l c h / 8%);
}
.card-inner {
position: relative;
border-radius: 0.5rem;
border: 1px solid var(--border);
background: var(--background);
overflow: hidden;
display: flex;
flex-direction: column;
min-height: 18rem;
box-shadow:
0 -0.5px rgba(255, 255, 255, 0.08),
0 4px 8px rgba(0, 0, 0, 0.06),
0 0 0 0.5px oklch(from var(--highlight) l c h / 8%),
0 1px 6px -4px #000;
}
/* ── Visual areas ────────────────────────────────────────────────── */
.visual-area {
position: relative;
height: 9rem;
overflow: hidden;
flex-shrink: 0;
}
/* ── Card text ───────────────────────────────────────────────────── */
.card-body {
padding: 1rem 1.25rem 1.25rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
flex: 1;
}
.card-badge {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.6rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--foreground-muted, rgba(255, 255, 255, 0.4));
margin-bottom: 0.25rem;
}
.card-title {
font-size: 1rem;
font-weight: 600;
line-height: 1.3;
color: var(--foreground);
margin: 0;
}
.card-desc {
font-size: 0.82rem;
line-height: 1.6;
color: var(--foreground-muted, rgba(255, 255, 255, 0.55));
margin: 0;
flex: 1;
}
@media (min-width: 768px) {
.card-title {
font-size: 1.125rem;
}
.card-desc {
font-size: 0.9rem;
}
.card-badge {
font-size: 0.65rem;
}
}
/* ═══════════════════════════════════════════════════════════════════
CARD 1 — Security Analysis Visual
Tool badges on horizontal lines (like the Seamless Integrations card)
═══════════════════════════════════════════════════════════════════ */
.sa-visual {
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 1.1rem 0 0.5rem;
height: 100%;
gap: 0;
}
.sa-row {
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: 2.1rem;
gap: 0.75rem;
}
/* horizontal connector line */
.sa-row::before {
content: "";
position: absolute;
inset: 0;
margin: auto 0;
height: 1px;
background: var(--border, rgba(255, 255, 255, 0.1));
}
.sa-badge {
position: relative;
display: inline-flex;
align-items: center;
height: 1.75rem;
padding: 0 0.65rem;
border-radius: 999px;
background: var(--background);
border: 1px solid var(--border, rgba(255, 255, 255, 0.12));
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.65rem;
font-weight: 700;
letter-spacing: 0.06em;
color: var(--foreground-muted, rgba(255, 255, 255, 0.55));
white-space: nowrap;
z-index: 2;
}
/* accent the first badge in each row */
.sa-badge-accent {
color: var(--accent, #e8834a);
border-color: color-mix(in srgb, var(--accent, #e8834a) 30%, transparent);
}
/* slight stagger while keeping badges in the visible center zone */
.sa-row:nth-child(1) {
transform: translateX(-1.5rem);
}
.sa-row:nth-child(2) {
transform: translateX(1.5rem);
}
.sa-row:nth-child(3) {
transform: translateX(-0.75rem);
}
/* ═══════════════════════════════════════════════════════════════════
CARD 2 — Pentest Visual (concentric arcs, like Real-time Sync)
═══════════════════════════════════════════════════════════════════ */
.pt-visual {
position: absolute;
inset: 0;
}
/* vertical centre line */
.pt-vline {
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 1px;
background: color-mix(in srgb, var(--foreground, #fff) 12%, transparent);
}
/* outer arc */
.pt-arc-outer {
position: absolute;
left: -3rem;
right: -3rem;
top: 1.2rem;
aspect-ratio: 1;
border-radius: 50%;
border: 1px solid
color-mix(in srgb, var(--foreground, #fff) 10%, transparent);
}
/* outer arc accent half */
.pt-arc-outer-accent {
position: absolute;
left: -3rem;
right: -3rem;
top: 1.2rem;
aspect-ratio: 1;
border-radius: 50%;
border: 1px solid var(--accent, #e8834a);
/* show only right half */
-webkit-mask-image: linear-gradient(90deg, transparent 50%, black 50%);
mask-image: linear-gradient(90deg, transparent 50%, black 50%);
}
/* inner arc */
.pt-arc-inner {
position: absolute;
left: 0;
right: 0;
top: 3.5rem;
aspect-ratio: 1;
border-radius: 50%;
border: 1px solid
color-mix(in srgb, var(--foreground, #fff) 10%, transparent);
}
/* inner arc accent half */
.pt-arc-inner-accent {
position: absolute;
left: 0;
right: 0;
top: 3.5rem;
aspect-ratio: 1;
border-radius: 50%;
border: 1px solid var(--accent, #e8834a);
-webkit-mask-image: linear-gradient(90deg, black 50%, transparent 50%);
mask-image: linear-gradient(90deg, black 50%, transparent 50%);
}
/* ═══════════════════════════════════════════════════════════════════
CARD 3 — Automation (full-width checklist)
═══════════════════════════════════════════════════════════════════ */
.auto-visual {
display: flex;
flex-direction: column;
align-items: center;
padding: 1.25rem 1.5rem 0.5rem;
gap: 0.45rem;
height: 100%;
position: relative;
}
.auto-item {
display: flex;
align-items: center;
gap: 0.6rem;
width: 100%;
max-width: 32rem;
min-height: 2rem;
padding: 0.42rem 0.75rem;
border-radius: 999px;
border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
background: color-mix(in srgb, var(--background-inset) 70%, transparent);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.7rem;
color: var(--foreground-muted, rgba(255, 255, 255, 0.55));
overflow: hidden;
}
.auto-item span:last-child {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}
.auto-check {
display: grid;
place-items: center;
flex: 0 0 1.1rem;
width: 1.1rem;
height: 1.1rem;
border-radius: 50%;
background: var(--accent, #e8834a);
color: #fff;
font-size: 0.6rem;
font-weight: 800;
}
.auto-hidden {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.45rem;
width: 100%;
opacity: 0.42;
-webkit-mask-image: linear-gradient(180deg, black 0%, transparent 100%);
mask-image: linear-gradient(180deg, black 0%, transparent 100%);
}
.auto-item-muted .auto-check {
background: color-mix(
in srgb,
var(--accent, #e8834a) 35%,
rgba(255, 255, 255, 0.15)
);
}
/* full-width card gets a wider visual and shorter min-height */
.card-automation .card-inner {
min-height: 10rem;
flex-direction: row;
}
.card-automation .visual-area {
width: 52%;
height: auto;
flex-shrink: 0;
border-radius: 0 0 0 0.5rem;
}
.card-automation .card-body {
flex: 1;
justify-content: center;
}
@media (max-width: 600px) {
.card-automation .card-inner {
flex-direction: column;
}
.card-automation .visual-area {
width: 100%;
height: 9rem;
}
}
/* ═══════════════════════════════════════════════════════════════════
CARD 4 — Web Dev Visual (vertical bars, like Developer First)
═══════════════════════════════════════════════════════════════════ */
.dev-bars {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 0;
height: 100%;
padding: 1.25rem 1.25rem 0.5rem;
}
.dev-bar {
flex: 1;
background: color-mix(in srgb, var(--foreground, #fff) 12%, transparent);
border-radius: 2px 2px 0 0;
min-width: 3px;
max-width: 7px;
}
.dev-bar-accent {
background: var(--accent, #e8834a) !important;
}
/* ═══════════════════════════════════════════════════════════════════
CARD 5 — Data Leak Search Visual (magnifier, like knowledge book)
═══════════════════════════════════════════════════════════════════ */
.leak-visual {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
}
.leak-visual svg {
width: min(82%, 12rem);
height: auto;
opacity: 0.9;
}
/* ═══════════════════════════════════════════════════════════════════
CARD 6 — Knowledge Visual (open book lines, like Real-time Sync)
═══════════════════════════════════════════════════════════════════ */
.knowledge-visual {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
}
.knowledge-visual svg {
width: min(90%, 18rem);
height: auto;
opacity: 0.85;
}
</style>
<!-- ══════════════════════════════════════════════════════════════════════
CARD 1 — Автоматическая проверка безопасности
══════════════════════════════════════════════════════════════════════ -->
<div class="card-shell card-security-analysis">
<div class="card-inner">
<div class="visual-area" style="height: 9.5rem;">
<div class="sa-visual" aria-hidden="true">
<!-- Row 1: SCA · SAST -->
<div class="sa-row">
<span class="sa-badge sa-badge-accent">SCA</span>
<span class="sa-badge">SAST</span>
</div>
<!-- Row 2: DAST · LINT (offset right) -->
<div class="sa-row">
<span class="sa-badge sa-badge-accent">DAST</span>
<span class="sa-badge">LINT</span>
</div>
<!-- Row 3: WAF · IaC -->
<div class="sa-row">
<span class="sa-badge">WAF</span>
<span class="sa-badge sa-badge-accent">IaC Scan</span>
</div>
</div>
</div>
<div class="card-body">
<p class="card-badge">Услуга</p>
<h3 class="card-title">
Настройка автоматической проверки безопасности (CI/CD + SAST/DAST/SCA)
</h3>
<p class="card-desc">
Подключаю SAST, DAST и SCA к CI/CD, чтобы проект автоматически
проверял код, работающее приложение и зависимости на уязвимости при
каждом обновлении.
</p>
</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════════════════
CARD 2 — Проверка безопасности
══════════════════════════════════════════════════════════════════════ -->
<div class="card-shell card-pentest">
<div class="card-inner">
<div
class="visual-area"
style="height: 9.5rem; overflow: hidden; position: relative;"
>
<div class="pt-visual" aria-hidden="true">
<div class="pt-vline"></div>
<div class="pt-arc-outer"></div>
<div class="pt-arc-outer-accent"></div>
<div class="pt-arc-inner"></div>
<div class="pt-arc-inner-accent"></div>
</div>
</div>
<div class="card-body">
<p class="card-badge">Услуга</p>
<h3 class="card-title">
Проверка безопасности (blackbox, graybox, whitebox)
</h3>
<p class="card-desc">
Проверяю сайт, приложение или API в подходящем формате: снаружи, с
частичным доступом или с полным доступом к коду и ручной проверкой
находок.
</p>
</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════════════════
CARD 3 — Автоматизация (full-width)
══════════════════════════════════════════════════════════════════════ -->
<div class="card-shell card-automation">
<div class="card-inner">
<!-- Visual: checklist on the left side -->
<div class="visual-area">
<div class="auto-visual" aria-hidden="true">
{#each automationVisible as item, i (item)}
<div class="auto-item" class:auto-item-muted={i === 2}>
<span class="auto-check">✓</span>
<span>{item}</span>
</div>
{/each}
<div class="auto-hidden">
{#each automationHidden as item (item)}
<div class="auto-item auto-item-muted">
<span class="auto-check">✓</span>
<span>{item}</span>
</div>
{/each}
</div>
</div>
</div>
<!-- Text on the right -->
<div class="card-body">
<p class="card-badge">Услуга</p>
<h3 class="card-title">Автоматизация</h3>
<p class="card-desc">
Автоматизируем рутину — от простого bash-скрипта до сложных решений
под индивидуальные требования.
</p>
</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════════════════
CARD 4 — Разбор найденных уязвимостей
══════════════════════════════════════════════════════════════════════ -->
<div class="card-shell card-dev">
<div class="card-inner">
<div class="visual-area" style="height: 9.5rem;" aria-hidden="true">
<div class="dev-bars">
{#each Array(28) as _, i (i)}
{@const heights = [
55, 40, 70, 45, 90, 60, 35, 80, 50, 75, 42, 88, 30, 65, 48, 92,
38, 72, 55, 85, 44, 68, 36, 78, 52, 62, 47, 58,
]}
<div
class="dev-bar"
class:dev-bar-accent={[4, 9, 12, 18, 23, 26].includes(i)}
style="height: {heights[i % heights.length]}%"
></div>
{/each}
</div>
</div>
<div class="card-body">
<p class="card-badge">Услуга</p>
<h3 class="card-title">Разбор найденных уязвимостей (Triage)</h3>
<p class="card-desc">
Разбираю отчёты сканеров и bug bounty, отсеиваю ложные срабатывания и
расставляю приоритеты: что чинить срочно, а что может подождать.
</p>
</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════════════════
CARD 5 — Поиск утечек данных
══════════════════════════════════════════════════════════════════════ -->
<div class="card-shell card-access">
<div class="card-inner">
<div
class="visual-area"
style="height: 9.5rem; position: relative;"
aria-hidden="true"
>
<div class="leak-visual">
<svg
viewBox="0 0 180 140"
fill="none"
xmlns="http://www.w3.org/2000/svg"
style="animation: bookFloat 6.8s ease-in-out infinite;"
>
<circle
cx="78"
cy="62"
r="34"
fill="color-mix(in srgb, currentColor 8%, transparent)"
stroke="color-mix(in srgb, currentColor 24%, transparent)"
stroke-width="2"
/>
<path
d="M103 87L139 123"
stroke="color-mix(in srgb, currentColor 32%, transparent)"
stroke-width="8"
stroke-linecap="round"
/>
<path
d="M59 55C68 47 82 45 93 52"
stroke="var(--accent, #e8834a)"
stroke-width="2"
stroke-linecap="round"
/>
<path
d="M54 72H101"
stroke="color-mix(in srgb, currentColor 16%, transparent)"
stroke-width="1.5"
stroke-linecap="round"
/>
<path
d="M62 84H93"
stroke="color-mix(in srgb, currentColor 16%, transparent)"
stroke-width="1.5"
stroke-linecap="round"
/>
<circle
cx="121"
cy="32"
r="4"
fill="var(--accent, #e8834a)"
opacity="0.8"
/>
<circle
cx="38"
cy="104"
r="3"
fill="color-mix(in srgb, currentColor 26%, transparent)"
/>
</svg>
</div>
</div>
<div class="card-body">
<p class="card-badge">Услуга</p>
<h3 class="card-title">Поиск утечек данных (Data Leak Search)</h3>
<p class="card-desc">
Ищу, не утекли ли данные компании или пользователя: базы, пароли,
документы, переписки, открытые базы и слитые архивы.
</p>
</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════════════════
CARD 6 — Знания каждому! (full-width)
══════════════════════════════════════════════════════════════════════ -->
<div class="card-shell card-knowledge">
<div class="card-inner" style="flex-direction: row; min-height: 10rem;">
<!-- Open book SVG visual -->
<div
class="visual-area"
style="width: 44%; height: auto; flex-shrink: 0;"
>
<div class="knowledge-visual" aria-hidden="true">
<svg
viewBox="0 0 220 150"
fill="none"
xmlns="http://www.w3.org/2000/svg"
style="animation: bookFloat 6.8s ease-in-out infinite;"
>
<!-- left page -->
<path
d="M108 118C82 101 56 96 26 101V31C57 25 82 33 108 51Z"
fill="color-mix(in srgb, currentColor 8%, transparent)"
stroke="color-mix(in srgb, currentColor 20%, transparent)"
stroke-width="1.4"
/>
<!-- right page -->
<path
d="M112 118C138 101 164 96 194 101V31C163 25 138 33 112 51Z"
fill="color-mix(in srgb, currentColor 8%, transparent)"
stroke="color-mix(in srgb, currentColor 20%, transparent)"
stroke-width="1.4"
/>
<!-- spine -->
<path
d="M110 51V122"
stroke="color-mix(in srgb, currentColor 22%, transparent)"
stroke-width="1.4"
stroke-linecap="round"
/>
<!-- left text lines -->
<path
d="M43 51C62 49 80 53 96 63"
stroke="color-mix(in srgb, currentColor 16%, transparent)"
stroke-width="1.4"
stroke-linecap="round"
fill="none"
/>
<path
d="M43 70C63 68 80 72 96 82"
stroke="color-mix(in srgb, currentColor 16%, transparent)"
stroke-width="1.4"
stroke-linecap="round"
fill="none"
/>
<path
d="M43 89C63 87 80 91 96 101"
stroke="color-mix(in srgb, currentColor 16%, transparent)"
stroke-width="1.4"
stroke-linecap="round"
fill="none"
/>
<!-- right text lines -->
<path
d="M177 51C158 49 140 53 124 63"
stroke="color-mix(in srgb, currentColor 16%, transparent)"
stroke-width="1.4"
stroke-linecap="round"
fill="none"
/>
<path
d="M177 70C157 68 140 72 124 82"
stroke="color-mix(in srgb, currentColor 16%, transparent)"
stroke-width="1.4"
stroke-linecap="round"
fill="none"
/>
<path
d="M177 89C157 87 140 91 124 101"
stroke="color-mix(in srgb, currentColor 16%, transparent)"
stroke-width="1.4"
stroke-linecap="round"
fill="none"
/>
<!-- shadow -->
<path
d="M26 101C58 98 83 104 110 122C137 104 162 98 194 101"
stroke="color-mix(in srgb, currentColor 28%, transparent)"
stroke-width="1.4"
stroke-linecap="round"
fill="none"
/>
</svg>
</div>
</div>
<!-- Text -->
<div class="card-body" style="justify-content: center;">
<p class="card-badge">Открытые знания</p>
<h3 class="card-title">Знания каждому!</h3>
<p class="card-desc">
Пишем понятные статьи и гайды по DevSecOps и не только. Рассказываем
как настроить безопасность с нуля и сделать её частью культуры
команды.
</p>
</div>
</div>
</div>
</section>
<style>
@keyframes bookFloat {
0%,
100% {
transform: translateY(0) rotate(-1deg);
}
50% {
transform: translateY(-0.6rem) rotate(1deg);
}
}
</style>
<script lang="ts">
// ─── Automation checklist items ───────────────────────────────────────────
const automationVisible = [
"ежедневный бэкап данных и логов",
"управление сетью и firewall-правилами",
"оптимизация работы git-репозиториев",
];
const automationHidden = [
"ротация ключей доступа",
"мониторинг состояния сервисов",
"автоочистка устаревших артефактов",
];
</script>
<section class="services-grid">
<style>
/* ── Grid layout ─────────────────────────────────────────────────── */
.services-grid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: auto auto auto;
gap: 0.75rem;
width: 100%;
box-sizing: border-box;
}
/* Card 1 — top-left */
.card-security-analysis {
grid-column: 1;
grid-row: 1;
}
/* Card 2 — top-right */
.card-pentest {
grid-column: 2;
grid-row: 1;
}
/* Card 3 — full-width middle */
.card-automation {
grid-column: 1 / -1;
grid-row: 2;
}
/* Card 4 — bottom-left */
.card-dev {
grid-column: 1;
grid-row: 3;
}
/* Card 5 — bottom-right */
.card-access {
grid-column: 2;
grid-row: 3;
}
/* Card 6 — full-width bottom (knowledge) */
.card-knowledge {
grid-column: 1 / -1;
grid-row: 4;
}
@media (max-width: 600px) {
.services-grid {
grid-template-columns: 1fr;
}
.card-security-analysis,
.card-pentest,
.card-automation,
.card-dev,
.card-access,
.card-knowledge {
grid-column: 1;
grid-row: auto;
}
}
/* ── Shared card shell ────────────────────────────────────────────── */
.card-shell {
position: relative;
isolation: isolate;
overflow: hidden;
border-radius: 0.75rem;
background: var(--background-inset);
padding: 0.375rem;
box-shadow:
inset 0px 1px 1px -0.5px rgba(0, 0, 0, 0.06),
inset 0px 3px 3px -1.5px rgba(0, 0, 0, 0.06),
inset 0px 6px 6px -3px rgba(0, 0, 0, 0.06),
inset 0 -0.5px rgba(255, 255, 255, 0.08),
inset 0 0 0 0.5px oklch(from var(--highlight) l c h / 8%);
}
.card-inner {
position: relative;
border-radius: 0.5rem;
border: 1px solid var(--border);
background: var(--background);
overflow: hidden;
display: flex;
flex-direction: column;
min-height: 18rem;
box-shadow:
0 -0.5px rgba(255, 255, 255, 0.08),
0 4px 8px rgba(0, 0, 0, 0.06),
0 0 0 0.5px oklch(from var(--highlight) l c h / 8%),
0 1px 6px -4px #000;
}
/* ── Visual areas ────────────────────────────────────────────────── */
.visual-area {
position: relative;
height: 9rem;
overflow: hidden;
flex-shrink: 0;
}
/* ── Card text ───────────────────────────────────────────────────── */
.card-body {
padding: 1rem 1.25rem 1.25rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
flex: 1;
}
.card-badge {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.6rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--foreground-muted, rgba(255, 255, 255, 0.4));
margin-bottom: 0.25rem;
}
.card-title {
font-size: 1rem;
font-weight: 600;
line-height: 1.3;
color: var(--foreground);
margin: 0;
}
.card-desc {
font-size: 0.82rem;
line-height: 1.6;
color: var(--foreground-muted, rgba(255, 255, 255, 0.55));
margin: 0;
flex: 1;
}
@media (min-width: 768px) {
.card-title {
font-size: 1.125rem;
}
.card-desc {
font-size: 0.9rem;
}
.card-badge {
font-size: 0.65rem;
}
}
/* ═══════════════════════════════════════════════════════════════════
CARD 1 — Security Analysis Visual
Tool badges on horizontal lines (like the Seamless Integrations card)
═══════════════════════════════════════════════════════════════════ */
.sa-visual {
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 1.1rem 0 0.5rem;
height: 100%;
gap: 0;
}
.sa-row {
position: relative;
display: flex;
align-items: center;
justify-content: center;
height: 2.1rem;
gap: 0.75rem;
}
/* horizontal connector line */
.sa-row::before {
content: "";
position: absolute;
inset: 0;
margin: auto 0;
height: 1px;
background: var(--border, rgba(255, 255, 255, 0.1));
}
.sa-badge {
position: relative;
display: inline-flex;
align-items: center;
height: 1.75rem;
padding: 0 0.65rem;
border-radius: 999px;
background: var(--background);
border: 1px solid var(--border, rgba(255, 255, 255, 0.12));
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.65rem;
font-weight: 700;
letter-spacing: 0.06em;
color: var(--foreground-muted, rgba(255, 255, 255, 0.55));
white-space: nowrap;
z-index: 2;
}
/* accent the first badge in each row */
.sa-badge-accent {
color: var(--accent, #e8834a);
border-color: color-mix(in srgb, var(--accent, #e8834a) 30%, transparent);
}
/* slight stagger while keeping badges in the visible center zone */
.sa-row:nth-child(1) {
transform: translateX(-1.5rem);
}
.sa-row:nth-child(2) {
transform: translateX(1.5rem);
}
.sa-row:nth-child(3) {
transform: translateX(-0.75rem);
}
/* ═══════════════════════════════════════════════════════════════════
CARD 2 — Pentest Visual (concentric arcs, like Real-time Sync)
═══════════════════════════════════════════════════════════════════ */
.pt-visual {
position: absolute;
inset: 0;
}
/* vertical centre line */
.pt-vline {
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 1px;
background: color-mix(in srgb, var(--foreground, #fff) 12%, transparent);
}
/* outer arc */
.pt-arc-outer {
position: absolute;
left: -3rem;
right: -3rem;
top: 1.2rem;
aspect-ratio: 1;
border-radius: 50%;
border: 1px solid
color-mix(in srgb, var(--foreground, #fff) 10%, transparent);
}
/* outer arc accent half */
.pt-arc-outer-accent {
position: absolute;
left: -3rem;
right: -3rem;
top: 1.2rem;
aspect-ratio: 1;
border-radius: 50%;
border: 1px solid var(--accent, #e8834a);
/* show only right half */
-webkit-mask-image: linear-gradient(90deg, transparent 50%, black 50%);
mask-image: linear-gradient(90deg, transparent 50%, black 50%);
}
/* inner arc */
.pt-arc-inner {
position: absolute;
left: 0;
right: 0;
top: 3.5rem;
aspect-ratio: 1;
border-radius: 50%;
border: 1px solid
color-mix(in srgb, var(--foreground, #fff) 10%, transparent);
}
/* inner arc accent half */
.pt-arc-inner-accent {
position: absolute;
left: 0;
right: 0;
top: 3.5rem;
aspect-ratio: 1;
border-radius: 50%;
border: 1px solid var(--accent, #e8834a);
-webkit-mask-image: linear-gradient(90deg, black 50%, transparent 50%);
mask-image: linear-gradient(90deg, black 50%, transparent 50%);
}
/* ═══════════════════════════════════════════════════════════════════
CARD 3 — Automation (full-width checklist)
═══════════════════════════════════════════════════════════════════ */
.auto-visual {
display: flex;
flex-direction: column;
align-items: center;
padding: 1.25rem 1.5rem 0.5rem;
gap: 0.45rem;
height: 100%;
position: relative;
}
.auto-item {
display: flex;
align-items: center;
gap: 0.6rem;
width: 100%;
max-width: 32rem;
min-height: 2rem;
padding: 0.42rem 0.75rem;
border-radius: 999px;
border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
background: color-mix(in srgb, var(--background-inset) 70%, transparent);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.7rem;
color: var(--foreground-muted, rgba(255, 255, 255, 0.55));
overflow: hidden;
}
.auto-item span:last-child {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
min-width: 0;
}
.auto-check {
display: grid;
place-items: center;
flex: 0 0 1.1rem;
width: 1.1rem;
height: 1.1rem;
border-radius: 50%;
background: var(--accent, #e8834a);
color: #fff;
font-size: 0.6rem;
font-weight: 800;
}
.auto-hidden {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.45rem;
width: 100%;
opacity: 0.42;
-webkit-mask-image: linear-gradient(180deg, black 0%, transparent 100%);
mask-image: linear-gradient(180deg, black 0%, transparent 100%);
}
.auto-item-muted .auto-check {
background: color-mix(
in srgb,
var(--accent, #e8834a) 35%,
rgba(255, 255, 255, 0.15)
);
}
/* full-width card gets a wider visual and shorter min-height */
.card-automation .card-inner {
min-height: 10rem;
flex-direction: row;
}
.card-automation .visual-area {
width: 52%;
height: auto;
flex-shrink: 0;
border-radius: 0 0 0 0.5rem;
}
.card-automation .card-body {
flex: 1;
justify-content: center;
}
@media (max-width: 600px) {
.card-automation .card-inner {
flex-direction: column;
}
.card-automation .visual-area {
width: 100%;
height: 9rem;
}
}
/* ═══════════════════════════════════════════════════════════════════
CARD 4 — Web Dev Visual (vertical bars, like Developer First)
═══════════════════════════════════════════════════════════════════ */
.dev-bars {
display: flex;
align-items: flex-end;
justify-content: space-between;
gap: 0;
height: 100%;
padding: 1.25rem 1.25rem 0.5rem;
}
.dev-bar {
flex: 1;
background: color-mix(in srgb, var(--foreground, #fff) 12%, transparent);
border-radius: 2px 2px 0 0;
min-width: 3px;
max-width: 7px;
}
.dev-bar-accent {
background: var(--accent, #e8834a) !important;
}
/* ═══════════════════════════════════════════════════════════════════
CARD 5 — Data Leak Search Visual (magnifier, like knowledge book)
═══════════════════════════════════════════════════════════════════ */
.leak-visual {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
}
.leak-visual svg {
width: min(82%, 12rem);
height: auto;
opacity: 0.9;
}
/* ═══════════════════════════════════════════════════════════════════
CARD 6 — Knowledge Visual (open book lines, like Real-time Sync)
═══════════════════════════════════════════════════════════════════ */
.knowledge-visual {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
}
.knowledge-visual svg {
width: min(90%, 18rem);
height: auto;
opacity: 0.85;
}
</style>
<!-- ══════════════════════════════════════════════════════════════════════
CARD 1 — Автоматическая проверка безопасности
══════════════════════════════════════════════════════════════════════ -->
<div class="card-shell card-security-analysis">
<div class="card-inner">
<div class="visual-area" style="height: 9.5rem;">
<div class="sa-visual" aria-hidden="true">
<!-- Row 1: SCA · SAST -->
<div class="sa-row">
<span class="sa-badge sa-badge-accent">SCA</span>
<span class="sa-badge">SAST</span>
</div>
<!-- Row 2: DAST · LINT (offset right) -->
<div class="sa-row">
<span class="sa-badge sa-badge-accent">DAST</span>
<span class="sa-badge">LINT</span>
</div>
<!-- Row 3: WAF · IaC -->
<div class="sa-row">
<span class="sa-badge">WAF</span>
<span class="sa-badge sa-badge-accent">IaC Scan</span>
</div>
</div>
</div>
<div class="card-body">
<p class="card-badge">Услуга</p>
<h3 class="card-title">
Настройка автоматической проверки безопасности (CI/CD + SAST/DAST/SCA)
</h3>
<p class="card-desc">
Подключаю SAST, DAST и SCA к CI/CD, чтобы проект автоматически
проверял код, работающее приложение и зависимости на уязвимости при
каждом обновлении.
</p>
</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════════════════
CARD 2 — Проверка безопасности
══════════════════════════════════════════════════════════════════════ -->
<div class="card-shell card-pentest">
<div class="card-inner">
<div
class="visual-area"
style="height: 9.5rem; overflow: hidden; position: relative;"
>
<div class="pt-visual" aria-hidden="true">
<div class="pt-vline"></div>
<div class="pt-arc-outer"></div>
<div class="pt-arc-outer-accent"></div>
<div class="pt-arc-inner"></div>
<div class="pt-arc-inner-accent"></div>
</div>
</div>
<div class="card-body">
<p class="card-badge">Услуга</p>
<h3 class="card-title">
Проверка безопасности (blackbox, graybox, whitebox)
</h3>
<p class="card-desc">
Проверяю сайт, приложение или API в подходящем формате: снаружи, с
частичным доступом или с полным доступом к коду и ручной проверкой
находок.
</p>
</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════════════════
CARD 3 — Автоматизация (full-width)
══════════════════════════════════════════════════════════════════════ -->
<div class="card-shell card-automation">
<div class="card-inner">
<!-- Visual: checklist on the left side -->
<div class="visual-area">
<div class="auto-visual" aria-hidden="true">
{#each automationVisible as item, i (item)}
<div class="auto-item" class:auto-item-muted={i === 2}>
<span class="auto-check">✓</span>
<span>{item}</span>
</div>
{/each}
<div class="auto-hidden">
{#each automationHidden as item (item)}
<div class="auto-item auto-item-muted">
<span class="auto-check">✓</span>
<span>{item}</span>
</div>
{/each}
</div>
</div>
</div>
<!-- Text on the right -->
<div class="card-body">
<p class="card-badge">Услуга</p>
<h3 class="card-title">Автоматизация</h3>
<p class="card-desc">
Автоматизируем рутину — от простого bash-скрипта до сложных решений
под индивидуальные требования.
</p>
</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════════════════
CARD 4 — Разбор найденных уязвимостей
══════════════════════════════════════════════════════════════════════ -->
<div class="card-shell card-dev">
<div class="card-inner">
<div class="visual-area" style="height: 9.5rem;" aria-hidden="true">
<div class="dev-bars">
{#each Array(28) as _, i (i)}
{@const heights = [
55, 40, 70, 45, 90, 60, 35, 80, 50, 75, 42, 88, 30, 65, 48, 92,
38, 72, 55, 85, 44, 68, 36, 78, 52, 62, 47, 58,
]}
<div
class="dev-bar"
class:dev-bar-accent={[4, 9, 12, 18, 23, 26].includes(i)}
style="height: {heights[i % heights.length]}%"
></div>
{/each}
</div>
</div>
<div class="card-body">
<p class="card-badge">Услуга</p>
<h3 class="card-title">Разбор найденных уязвимостей (Triage)</h3>
<p class="card-desc">
Разбираю отчёты сканеров и bug bounty, отсеиваю ложные срабатывания и
расставляю приоритеты: что чинить срочно, а что может подождать.
</p>
</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════════════════
CARD 5 — Поиск утечек данных
══════════════════════════════════════════════════════════════════════ -->
<div class="card-shell card-access">
<div class="card-inner">
<div
class="visual-area"
style="height: 9.5rem; position: relative;"
aria-hidden="true"
>
<div class="leak-visual">
<svg
viewBox="0 0 180 140"
fill="none"
xmlns="http://www.w3.org/2000/svg"
style="animation: bookFloat 6.8s ease-in-out infinite;"
>
<circle
cx="78"
cy="62"
r="34"
fill="color-mix(in srgb, currentColor 8%, transparent)"
stroke="color-mix(in srgb, currentColor 24%, transparent)"
stroke-width="2"
/>
<path
d="M103 87L139 123"
stroke="color-mix(in srgb, currentColor 32%, transparent)"
stroke-width="8"
stroke-linecap="round"
/>
<path
d="M59 55C68 47 82 45 93 52"
stroke="var(--accent, #e8834a)"
stroke-width="2"
stroke-linecap="round"
/>
<path
d="M54 72H101"
stroke="color-mix(in srgb, currentColor 16%, transparent)"
stroke-width="1.5"
stroke-linecap="round"
/>
<path
d="M62 84H93"
stroke="color-mix(in srgb, currentColor 16%, transparent)"
stroke-width="1.5"
stroke-linecap="round"
/>
<circle
cx="121"
cy="32"
r="4"
fill="var(--accent, #e8834a)"
opacity="0.8"
/>
<circle
cx="38"
cy="104"
r="3"
fill="color-mix(in srgb, currentColor 26%, transparent)"
/>
</svg>
</div>
</div>
<div class="card-body">
<p class="card-badge">Услуга</p>
<h3 class="card-title">Поиск утечек данных (Data Leak Search)</h3>
<p class="card-desc">
Ищу, не утекли ли данные компании или пользователя: базы, пароли,
документы, переписки, открытые базы и слитые архивы.
</p>
</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════════════════
CARD 6 — Знания каждому! (full-width)
══════════════════════════════════════════════════════════════════════ -->
<div class="card-shell card-knowledge">
<div class="card-inner" style="flex-direction: row; min-height: 10rem;">
<!-- Open book SVG visual -->
<div
class="visual-area"
style="width: 44%; height: auto; flex-shrink: 0;"
>
<div class="knowledge-visual" aria-hidden="true">
<svg
viewBox="0 0 220 150"
fill="none"
xmlns="http://www.w3.org/2000/svg"
style="animation: bookFloat 6.8s ease-in-out infinite;"
>
<!-- left page -->
<path
d="M108 118C82 101 56 96 26 101V31C57 25 82 33 108 51Z"
fill="color-mix(in srgb, currentColor 8%, transparent)"
stroke="color-mix(in srgb, currentColor 20%, transparent)"
stroke-width="1.4"
/>
<!-- right page -->
<path
d="M112 118C138 101 164 96 194 101V31C163 25 138 33 112 51Z"
fill="color-mix(in srgb, currentColor 8%, transparent)"
stroke="color-mix(in srgb, currentColor 20%, transparent)"
stroke-width="1.4"
/>
<!-- spine -->
<path
d="M110 51V122"
stroke="color-mix(in srgb, currentColor 22%, transparent)"
stroke-width="1.4"
stroke-linecap="round"
/>
<!-- left text lines -->
<path
d="M43 51C62 49 80 53 96 63"
stroke="color-mix(in srgb, currentColor 16%, transparent)"
stroke-width="1.4"
stroke-linecap="round"
fill="none"
/>
<path
d="M43 70C63 68 80 72 96 82"
stroke="color-mix(in srgb, currentColor 16%, transparent)"
stroke-width="1.4"
stroke-linecap="round"
fill="none"
/>
<path
d="M43 89C63 87 80 91 96 101"
stroke="color-mix(in srgb, currentColor 16%, transparent)"
stroke-width="1.4"
stroke-linecap="round"
fill="none"
/>
<!-- right text lines -->
<path
d="M177 51C158 49 140 53 124 63"
stroke="color-mix(in srgb, currentColor 16%, transparent)"
stroke-width="1.4"
stroke-linecap="round"
fill="none"
/>
<path
d="M177 70C157 68 140 72 124 82"
stroke="color-mix(in srgb, currentColor 16%, transparent)"
stroke-width="1.4"
stroke-linecap="round"
fill="none"
/>
<path
d="M177 89C157 87 140 91 124 101"
stroke="color-mix(in srgb, currentColor 16%, transparent)"
stroke-width="1.4"
stroke-linecap="round"
fill="none"
/>
<!-- shadow -->
<path
d="M26 101C58 98 83 104 110 122C137 104 162 98 194 101"
stroke="color-mix(in srgb, currentColor 28%, transparent)"
stroke-width="1.4"
stroke-linecap="round"
fill="none"
/>
</svg>
</div>
</div>
<!-- Text -->
<div class="card-body" style="justify-content: center;">
<p class="card-badge">Открытые знания</p>
<h3 class="card-title">Знания каждому!</h3>
<p class="card-desc">
Пишем понятные статьи и гайды по DevSecOps и не только. Рассказываем
как настроить безопасность с нуля и сделать её частью культуры
команды.
</p>
</div>
</div>
</div>
</section>
<style>
@keyframes bookFloat {
0%,
100% {
transform: translateY(0) rotate(-1deg);
}
50% {
transform: translateY(-0.6rem) rotate(1deg);
}
}
</style>
Карточки
| Карточка | Визуализация | Услуга |
|---|---|---|
| Security Analysis | Tool badges на линиях | SCA, SAST, DAST, LINT, WAF, IaC |
| Pentest | Концентрические дуги | Проверка защищённости |
| Automation | Чеклист с fade | Автоматизация процессов |
| Dev | Вертикальные бары | Разработка под заказ |
| Access | Shield + протокол chips | Настройка безопасного доступа |
| Knowledge | Анимированная книга | Открытые знания |
Особенности
- Адаптивная сетка: 2 колонки → 1 колонка на мобильных
- Карточки Automation и Knowledge — на всю ширину
- CSS-only анимация книги (
bookFloatkeyframes) - Поддержка тёмной/светлой темы через CSS-переменные
- Все визуализации — чистый HTML/CSS/SVG, без зависимостей
Использование
<script>
import CardSection from '$lib/components/ui/CardSection.svelte';
</script>
<CardSection /><script>
import CardSection from '$lib/components/ui/CardSection.svelte';
</script>
<CardSection />Кастомизация
Компонент не принимает пропсов — содержимое задано жёстко. Для изменений:
- Тексты и цены: заголовки, описания,
.card-price-value - Визуализации: CSS и SVG-разметка внутри компонента
- Списки: массивы
automationVisible,automationHidden
