Initial commit: Vibe Coding Workshop landing page

This commit is contained in:
ask 2026-05-15 19:14:24 +05:00
commit 21c3a6621a
4 changed files with 1301 additions and 0 deletions

22
README.md Normal file
View File

@ -0,0 +1,22 @@
# Vibe Coding Воркшоп — Казахтелеком
Информационная платформа для участников воркшопа по вайб-кодингу для финансового департамента Казахтелеком.
## Описание
Однодневный воркшоп: участники узнают что такое вайб-кодинг, выбирают свой проект из боли внутри департамента и к концу дня деплоят рабочий MVP в среде telecom.quest.
## Разделы
- **Введение** — до vs после вайб-кодинга
- **Почему финансисты** — три навыка которые уже есть
- **Инструменты** — Claude Desktop, VSCode/telecom.quest, Sandbox КТ
- **Программа дня** — расписание с 09:00 до 17:00
- **Словарь** — 7 терминов без технического жаргона (аккордеон)
- **Проекты** — 6 идей для проектов из финансового департамента
## Стек
- Vanilla HTML/CSS/JS — без фреймворков
- Inter (Google Fonts) — типографика
- Дизайн: стиль aqryl.app — серый фрейм, единый синий акцент #0066cc

136
data/content.js Normal file
View File

@ -0,0 +1,136 @@
const GLOSSARY = [
{
term: 'PRD',
abbr: 'PRD',
definition: '<strong>Product Requirements Document</strong> — описание задачи своими словами. Ты пишешь: что должна делать программа, кто ей пользуется, что считается успехом. Это уже умеют делать все финансисты — вы делаете это в каждом ТЗ для 1С.'
},
{
term: 'TRD',
abbr: 'TRD',
definition: '<strong>Technical Requirements Document</strong> — техническое задание, которое Claude генерирует из твоего PRD. Claude берёт твои слова и переводит на язык программиста. Ты не пишешь TRD — это делает ИИ.'
},
{
term: 'MVP',
abbr: 'MVP',
definition: '<strong>Minimum Viable Product</strong> — минимальный рабочий прототип. Самое простое, что решает проблему. Не идеальное — рабочее. Сначала делаем MVP, потом улучшаем. Как черновик отчёта: сначала цифры правильные, потом форматирование.'
},
{
term: 'Sandbox',
abbr: 'Sandbox',
definition: 'Тестовая среда с mock-данными. Можно ломать, пробовать, перезапускать — ничего страшного не произойдёт. Ваши реальные данные в 1С в полной безопасности. Всё что делается сегодня — только в sandbox.'
},
{
term: 'Mock',
abbr: 'Mock',
definition: 'Тестовые данные вместо реальных. Например: вместо реальных платёжек из 1С — файл с похожей структурой, но с выдуманными суммами и контрагентами. Позволяет разрабатывать без доступа к боевым системам.'
},
{
term: 'Итерация',
abbr: 'Итерация',
definition: 'Один цикл: <strong>сделал → проверил → улучшил</strong>. В разработке через Claude это занимает минуты. Описал задачу → получил код → запустил → увидел что не так → поправил описание → снова запустил. Это и есть вайб-кодинг.'
},
{
term: 'Промпт',
abbr: 'Prompt',
definition: 'Твоя инструкция для Claude. Чем точнее описание — тем точнее результат. Хороший промпт = хороший PRD: контекст, задача, ограничения, критерий успеха. Всё что ты пишешь в Claude Desktop — это промпты.'
}
];
const PROJECTS = [
{
num: '01',
title: 'Дашборд дебиторской задолженности',
pain: 'Каждый понедельник собираю данные из 1С вручную, строю таблицу в Excel. Тратится 2-3 часа. Хочу видеть картину в реальном времени.',
tags: ['Excel → Web', 'Визуализация', 'Автоматизация']
},
{
num: '02',
title: 'Чеклист закрытия периода',
pain: 'Список шагов закрытия есть только у меня в голове. Когда я в отпуске — коллеги теряются. Нужен интерактивный чеклист с историей выполнения.',
tags: ['Процесс', 'Командная работа', 'История']
},
{
num: '03',
title: 'Трекер согласования платежей',
pain: 'Платёжные поручения согласуем через email. Теряются версии, непонятно кто последний смотрел. Хочу одно место с историей статусов.',
tags: ['Согласования', 'Статусы', 'Аудит']
},
{
num: '04',
title: 'Конвертер банковской выписки',
pain: 'Выписка из банка приходит в одном формате, 1С принимает в другом. Каждый раз переделываю вручную через Excel. Это можно автоматизировать.',
tags: ['Парсинг', 'Конвертация', 'Банк']
},
{
num: '05',
title: 'Калькулятор бюджет vs факт',
pain: 'Отчёт по исполнению бюджета строю раз в месяц на 4 часа. Хочу вводить данные и сразу видеть отклонения с drill-down по статьям.',
tags: ['Аналитика', 'Бюджет', 'Отклонения']
},
{
num: '06',
title: 'Архив финансовых документов',
pain: 'Договоры, акты, счета — всё в разных папках. Когда нужно найти документ по контрагенту — ищешь полчаса. Нужен поиск с фильтрами.',
tags: ['Поиск', 'Архив', 'Документы']
}
];
const SCHEDULE = [
{
time: '09:00',
title: 'Введение в вайб-кодинг',
desc: 'Что изменилось. Почему финансисты — идеальные разработчики. Первое знакомство с Claude.',
tags: ['Теория'],
active: false
},
{
time: '09:45',
title: 'Инструменты и среда',
desc: 'Настройка Claude Desktop, telecom.quest VSCode, знакомство с Sandbox КТ.',
tags: ['Практика'],
active: false
},
{
time: '10:30',
title: 'Первый промпт: пишем вместе',
desc: 'Берём общую задачу, пишем PRD вместе, запускаем первый код. Каждый участник видит результат на своём экране.',
tags: ['Практика', 'Совместно'],
active: false
},
{
time: '11:30',
title: 'Выбор и проработка своего проекта',
desc: 'Каждый выбирает боль из своего опыта. Пишем PRD, Claude генерирует TRD. Наставник помогает с описанием.',
tags: ['Ваш проект'],
active: true
},
{
time: '13:00',
title: 'Обед',
desc: '',
tags: [],
active: false,
isBreak: true
},
{
time: '14:00',
title: 'Разработка MVP',
desc: 'Работаем по итерациям: описал → получил → проверил → улучшил. Claude пишет код, ты — ставишь задачу и принимаешь результат.',
tags: ['Ваш проект', 'Итерации'],
active: false
},
{
time: '16:00',
title: 'Демо и обратная связь',
desc: '3 минуты на команду: показываем что получилось, что сработало, что было сложно. Без оценок — только обмен опытом.',
tags: ['Демо'],
active: false
},
{
time: '16:45',
title: 'Запуск на telecom.quest',
desc: 'Каждый разворачивает свой MVP в среде КТ. Итоги дня, следующие шаги, ресурсы.',
tags: ['Финал'],
active: false
}
];

392
index.html Normal file
View File

@ -0,0 +1,392 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
<title>Vibe Coding Воркшоп — Казахтелеком</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,400;0,500;1,400&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="frame">
<!-- NAV -->
<nav>
<div class="nav-inner">
<a class="nav-logo" href="#top">
<span class="logo-mark">КТ</span>
<span class="logo-text">Vibe Workshop</span>
</a>
<div class="nav-links">
<a href="#intro" class="nav-link">Введение</a>
<a href="#tools" class="nav-link">Инструменты</a>
<a href="#schedule" class="nav-link">Программа</a>
<a href="#glossary" class="nav-link">Словарь</a>
<a href="#projects" class="nav-link">Проекты</a>
</div>
<a href="https://telecom.quest" target="_blank" rel="noopener" class="nav-cta">telecom.quest →</a>
</div>
</nav>
<!-- MAIN -->
<main id="top">
<!-- HERO -->
<section class="hero">
<div class="container hero-inner">
<span class="badge">Казахтелеком · Финансовый департамент · Полный день</span>
<h1 class="hero-title">
Воркшоп:<br>
<span class="underline-blue">Vibe Coding</span><br>
для финансистов
</h1>
<p class="hero-sub">
Сегодня ты напишешь своё первое рабочее приложение.<br>Без опыта программирования.
</p>
<div class="hero-meta">
<div class="hero-stat">
<div class="hero-stat-num">8ч</div>
<div class="hero-stat-label">полный день</div>
</div>
<div class="hero-divider"></div>
<div class="hero-stat">
<div class="hero-stat-num">1</div>
<div class="hero-stat-label">MVP на выходе</div>
</div>
<div class="hero-divider"></div>
<div class="hero-stat">
<div class="hero-stat-num">0</div>
<div class="hero-stat-label">строк кода вручную</div>
</div>
</div>
<a href="#intro" class="btn-primary">Начать →</a>
</div>
</section>
<!-- SECTION 1: WHAT IS VIBE CODING -->
<section class="section" id="intro">
<div class="container two-col">
<div class="section-header">
<div class="section-label">01 — Введение</div>
<h2 class="section-title">Что такое<br><em>вайб-кодинг?</em></h2>
<p class="section-body">
Раньше между идеей и программой стоял разработчик, ТЗ и несколько месяцев ожидания.
Теперь — только точное описание задачи.
</p>
</div>
<div class="before-after">
<div class="ba-card">
<div class="ba-label">Раньше</div>
<div class="ba-row">
<svg class="ba-icon neg" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7">
<circle cx="8" cy="8" r="7"/><line x1="10" y1="6" x2="6" y2="10"/><line x1="6" y1="6" x2="10" y2="10"/>
</svg>
<span>Описываешь задачу разработчику</span>
</div>
<div class="ba-row">
<svg class="ba-icon neg" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7">
<circle cx="8" cy="8" r="7"/><line x1="10" y1="6" x2="6" y2="10"/><line x1="6" y1="6" x2="10" y2="10"/>
</svg>
<span>Разработчик пишет ТЗ — итерация №1</span>
</div>
<div class="ba-row">
<svg class="ba-icon neg" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7">
<circle cx="8" cy="8" r="7"/><line x1="10" y1="6" x2="6" y2="10"/><line x1="6" y1="6" x2="10" y2="10"/>
</svg>
<span>Согласование, правки, спринт</span>
</div>
<div class="ba-row">
<svg class="ba-icon neg" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7">
<circle cx="8" cy="8" r="7"/><line x1="10" y1="6" x2="6" y2="10"/><line x1="6" y1="6" x2="10" y2="10"/>
</svg>
<span>Результат через 2–6 месяцев</span>
</div>
</div>
<div class="ba-card after">
<div class="ba-label after">Теперь</div>
<div class="ba-row">
<svg class="ba-icon pos" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7">
<circle cx="8" cy="8" r="7"/><polyline points="5,8 7,10 11,6"/>
</svg>
<span>Описываешь задачу словами в Claude</span>
</div>
<div class="ba-row">
<svg class="ba-icon pos" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7">
<circle cx="8" cy="8" r="7"/><polyline points="5,8 7,10 11,6"/>
</svg>
<span>Claude Code пишет код за секунды</span>
</div>
<div class="ba-row">
<svg class="ba-icon pos" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7">
<circle cx="8" cy="8" r="7"/><polyline points="5,8 7,10 11,6"/>
</svg>
<span>Ты проверяешь, даёшь обратную связь</span>
</div>
<div class="ba-row">
<svg class="ba-icon pos" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7">
<circle cx="8" cy="8" r="7"/><polyline points="5,8 7,10 11,6"/>
</svg>
<span>Рабочий прототип за часы</span>
</div>
</div>
</div>
</div>
</section>
<!-- SECTION 2: WHY FINANCIERS -->
<section class="section" id="why">
<div class="container">
<div class="section-label">02 — Почему вы</div>
<h2 class="section-title">Ты уже умеешь<br><em>самое важное</em></h2>
<p class="section-body">
Финансисты — лучшие постановщики задач. Ваш язык — это уже готовый PRD.
Три навыка, которые у вас есть по умолчанию:
</p>
<div class="cards-3">
<div class="card">
<svg class="card-icon" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<rect x="8" y="6" width="32" height="36" rx="3"/>
<line x1="16" y1="16" x2="32" y2="16"/>
<line x1="16" y1="22" x2="32" y2="22"/>
<line x1="16" y1="28" x2="24" y2="28"/>
</svg>
<div class="card-title">Описывать процессы</div>
<div class="card-desc">
Как вы закрываете период, как согласуете платёж — это уже структурированное описание задачи. Claude понимает именно такой язык.
</div>
</div>
<div class="card">
<svg class="card-icon" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<circle cx="24" cy="24" r="16"/>
<line x1="24" y1="16" x2="24" y2="24"/>
<line x1="24" y1="24" x2="30" y2="30"/>
</svg>
<div class="card-title">Видеть проблему</div>
<div class="card-desc">
Где данные расходятся, где Excel ломается, где теряется время — вы чувствуете это лучше любого разработчика. Эта экспертиза бесценна.
</div>
</div>
<div class="card">
<svg class="card-icon" viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<polyline points="8,28 18,18 26,26 40,12"/>
<polyline points="34,12 40,12 40,18"/>
</svg>
<div class="card-title">Принимать результат</div>
<div class="card-desc">
Принять или отклонить — это ваша ключевая роль. Цифра правильная или нет — именно ваша экспертиза даёт качество итоговому продукту.
</div>
</div>
</div>
</div>
</section>
<!-- SECTION 3: TOOLS -->
<section class="section" id="tools">
<div class="container">
<div class="section-label">03 — Инструменты</div>
<h2 class="section-title">Три инструмента.<br><em>Один результат.</em></h2>
<p class="section-body">
Всё что нужно сегодня — уже настроено. Никаких установок, регистраций и ключей.
</p>
<div class="tools-grid">
<div class="tool-card">
<div class="tool-icon-wrap">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
</svg>
</div>
<div class="tool-name">Claude Desktop</div>
<div class="tool-role">
ИИ-помощник, который пишет код. Говоришь задачу — получаешь код. Исправляешь словами — получаешь новую версию.
</div>
<a href="https://claude.ai" target="_blank" rel="noopener" class="tool-link">
claude.ai
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.7">
<path d="M2 10L10 2M4 2h6v6"/>
</svg>
</a>
</div>
<div class="tool-card">
<div class="tool-icon-wrap">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/>
</svg>
</div>
<div class="tool-name">VSCode на telecom.quest</div>
<div class="tool-role">
Редактор кода прямо в браузере. Не нужно ничего устанавливать — открываешь ссылку и работаешь. Код Claude сразу запускается здесь.
</div>
<a href="https://telecom.quest" target="_blank" rel="noopener" class="tool-link">
telecom.quest
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="1.7">
<path d="M2 10L10 2M4 2h6v6"/>
</svg>
</a>
</div>
<div class="tool-card">
<div class="tool-icon-wrap">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<ellipse cx="12" cy="5" rx="9" ry="3"/>
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/>
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/>
</svg>
</div>
<div class="tool-name">Sandbox КТ</div>
<div class="tool-role">
Тестовая среда с mock-данными, похожими на реальные данные КТ. Можно ломать, перезапускать. Реальные данные в полной безопасности.
</div>
<span class="tool-link" style="cursor:default;opacity:0.5">Настроено заранее</span>
</div>
</div>
</div>
</section>
<!-- SECTION 4: SCHEDULE -->
<section class="section" id="schedule">
<div class="container two-col">
<div class="section-header">
<div class="section-label">04 — Программа</div>
<h2 class="section-title">Что будет<br><em>сегодня</em></h2>
<p class="section-body">
Полный день поделён на блоки с чёткими результатами. К 17:00 — твой MVP задеплоен.
</p>
</div>
<div class="schedule" id="schedule-list"></div>
</div>
</section>
<!-- SECTION 5: GLOSSARY -->
<section class="section" id="glossary">
<div class="container two-col">
<div class="section-header">
<div class="section-label">05 — Словарь</div>
<h2 class="section-title">Термины,<br><em>которые услышишь</em></h2>
<p class="section-body">
Семь слов, которые встретятся сегодня. Нажми на любое — увидишь объяснение
без технического жаргона.
</p>
</div>
<div class="glossary" id="glossary-list"></div>
</div>
</section>
<!-- SECTION 6: PROJECTS -->
<section class="section" id="projects">
<div class="container">
<div class="section-label">06 — Твой проект</div>
<h2 class="section-title">Ты знаешь<br><em>боль изнутри</em></h2>
<p class="section-body">
Лучший проект — тот, где ты сам тратишь время впустую. Вот примеры из финансовых департаментов —
выбери ближайший или опиши свой.
</p>
<div class="projects-grid" id="projects-grid"></div>
</div>
</section>
<!-- DARK CTA -->
<section class="section" style="border-top: none; padding-bottom: 64px;">
<div class="container">
<div class="dark-section">
<h2 class="dark-title">Запусти свой проект сегодня</h2>
<p class="dark-sub">
К концу дня — рабочий MVP в среде Казахтелеком. Первый шаг сделан.
</p>
<a href="https://telecom.quest" target="_blank" rel="noopener" class="dark-cta">
Открыть telecom.quest →
</a>
</div>
</div>
</section>
<!-- FOOTER -->
<footer>
<span class="footer-copy">Казахтелеком · Финансовый департамент · Воркшоп по вайб-кодингу</span>
<div class="footer-links">
<a href="https://claude.ai" target="_blank" rel="noopener" class="footer-link">Claude Desktop</a>
<a href="https://telecom.quest" target="_blank" rel="noopener" class="footer-link">telecom.quest</a>
</div>
</footer>
</main>
</div>
<script src="data/content.js"></script>
<script>
// Render schedule
const scheduleEl = document.getElementById('schedule-list');
SCHEDULE.forEach(item => {
const div = document.createElement('div');
div.className = 'sched-item' + (item.active ? ' active' : '') + (item.isBreak ? ' break' : '');
div.innerHTML = `
<span class="sched-time">${item.time}</span>
<span class="sched-dot"></span>
<div class="sched-content">
<div class="sched-title">${item.title}</div>
${item.desc ? `<div class="sched-desc">${item.desc}</div>` : ''}
${item.tags.length ? `<div class="sched-tags">${item.tags.map(t => `<span class="tag ${t === 'Ваш проект' || t === 'Финал' ? 'blue' : ''}">${t}</span>`).join('')}</div>` : ''}
</div>
`;
scheduleEl.appendChild(div);
});
// Render glossary
const glossaryEl = document.getElementById('glossary-list');
GLOSSARY.forEach(item => {
const div = document.createElement('div');
div.className = 'gloss-item';
div.innerHTML = `
<button class="gloss-trigger" aria-expanded="false">
<span class="gloss-term">${item.term}</span>
<span class="gloss-abbr">${item.abbr}</span>
<svg class="gloss-chevron" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round">
<polyline points="4,6 8,10 12,6"/>
</svg>
</button>
<div class="gloss-body" role="region">
<div class="gloss-inner">
<div class="gloss-text">${item.definition}</div>
</div>
</div>
`;
const btn = div.querySelector('.gloss-trigger');
btn.addEventListener('click', () => {
const isOpen = div.classList.contains('open');
document.querySelectorAll('.gloss-item.open').forEach(el => {
el.classList.remove('open');
el.querySelector('.gloss-trigger').setAttribute('aria-expanded', 'false');
});
if (!isOpen) {
div.classList.add('open');
btn.setAttribute('aria-expanded', 'true');
}
});
glossaryEl.appendChild(div);
});
// Render projects
const projectsEl = document.getElementById('projects-grid');
PROJECTS.forEach(item => {
const div = document.createElement('div');
div.className = 'project-card';
div.innerHTML = `
<div class="project-num">${item.num}</div>
<div class="project-title">${item.title}</div>
<div class="project-pain">"${item.pain}"</div>
<div class="project-tags">${item.tags.map(t => `<span class="tag">${t}</span>`).join('')}</div>
`;
projectsEl.appendChild(div);
});
</script>
</body>
</html>

751
style.css Normal file
View File

@ -0,0 +1,751 @@
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--blue: #0066cc;
--blue-hover: #0055b3;
--blue-link: #0058b3;
--bg: #efefef;
--card-neutral: #e3e3e3;
--text: #000;
--text-65: rgba(0,0,0,0.65);
--text-55: rgba(0,0,0,0.55);
--text-45: rgba(0,0,0,0.45);
--border: rgba(0,0,0,0.08);
--hover: rgba(0,0,0,0.06);
--hover-light: rgba(0,0,0,0.03);
--positive: #059669;
--positive-bg: #dcfce7;
--negative: #dc2626;
}
html { scroll-behavior: smooth; }
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: #fff;
min-height: 100dvh;
padding: 12px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: var(--text);
}
/* ── FRAME ── */
.frame {
min-height: calc(100dvh - 24px);
display: flex;
flex-direction: column;
gap: 12px;
}
/* ── NAV ── */
nav {
height: 64px;
background: var(--bg);
border-radius: 16px;
padding: 0 20px;
display: flex;
align-items: center;
position: sticky;
top: 12px;
z-index: 50;
}
.nav-inner {
display: flex;
align-items: center;
gap: 16px;
width: 100%;
}
.nav-logo {
display: inline-flex;
align-items: center;
gap: 8px;
text-decoration: none;
flex-shrink: 0;
}
.logo-mark {
background: var(--blue);
color: #fff;
font-size: 0.75rem;
font-weight: 500;
padding: 3px 7px;
border-radius: 6px;
letter-spacing: 0.02em;
}
.logo-text {
font-size: 0.875rem;
font-weight: 500;
color: var(--text);
}
.nav-links {
display: none;
flex: 1;
align-items: center;
justify-content: center;
gap: 4px;
}
@media (min-width: 768px) {
.nav-links { display: flex; }
}
.nav-link {
padding: 6px 12px;
font-size: 0.875rem;
font-weight: 400;
color: var(--text-65);
text-decoration: none;
border-radius: 8px;
transition: background 150ms ease, color 150ms ease;
}
.nav-link:hover {
background: var(--hover);
color: var(--text);
}
.nav-cta {
margin-left: auto;
background: var(--blue);
color: #fff;
font-size: 0.875rem;
font-weight: 500;
padding: 8px 16px;
border-radius: 8px;
text-decoration: none;
transition: background 150ms ease;
flex-shrink: 0;
}
.nav-cta:hover { background: var(--blue-hover); }
/* ── MAIN ── */
main {
background: var(--bg);
border-radius: 16px;
overflow: hidden;
flex: 1;
}
/* ── CONTAINER ── */
.container {
width: 100%;
max-width: 1024px;
margin: 0 auto;
padding: 0 16px;
}
@media (min-width: 640px) { .container { padding: 0 24px; } }
/* ── HERO ── */
.hero {
padding: 72px 0 56px;
text-align: center;
}
@media (min-width: 640px) { .hero { padding: 96px 0 72px; } }
@media (min-width: 1024px) { .hero { padding: 112px 0 80px; } }
.hero-inner {
display: flex;
flex-direction: column;
align-items: center;
}
.badge {
display: inline-block;
font-size: 0.75rem;
font-weight: 400;
color: var(--text-55);
letter-spacing: 0.02em;
margin-bottom: 20px;
}
.hero-title {
font-size: 2rem;
font-weight: 400;
line-height: 1.12;
letter-spacing: -0.02em;
color: var(--text);
max-width: 540px;
}
@media (min-width: 640px) { .hero-title { font-size: 2.5rem; } }
@media (min-width: 1024px) { .hero-title { font-size: 3rem; max-width: 640px; } }
.underline-blue {
text-decoration: underline;
text-decoration-color: var(--blue);
text-decoration-thickness: 3px;
text-underline-offset: 6px;
}
.hero-sub {
margin-top: 16px;
font-size: 0.9375rem;
font-weight: 400;
color: var(--text-55);
max-width: 400px;
line-height: 1.5;
}
@media (min-width: 640px) { .hero-sub { font-size: 1rem; } }
.hero-meta {
margin-top: 32px;
display: flex;
align-items: center;
gap: 20px;
justify-content: center;
flex-wrap: wrap;
}
.hero-stat {
text-align: center;
}
.hero-stat-num {
font-size: 1.5rem;
font-weight: 400;
color: var(--blue);
line-height: 1;
}
.hero-stat-label {
font-size: 0.75rem;
color: var(--text-45);
margin-top: 2px;
}
.hero-divider {
width: 1px;
height: 32px;
background: var(--border);
}
/* ── SECTION ── */
.section {
padding: 64px 0;
border-top: 1px solid var(--border);
}
@media (min-width: 640px) { .section { padding: 80px 0; } }
.section-label {
font-size: 0.75rem;
font-weight: 400;
color: var(--text-45);
letter-spacing: 0.04em;
text-transform: uppercase;
margin-bottom: 12px;
}
.section-title {
font-size: 1.5rem;
font-weight: 400;
line-height: 1.2;
letter-spacing: -0.02em;
color: var(--text);
max-width: 540px;
}
@media (min-width: 640px) { .section-title { font-size: 1.875rem; } }
.section-title em {
font-style: normal;
text-decoration: underline;
text-decoration-color: var(--blue);
text-decoration-thickness: 2px;
text-underline-offset: 5px;
}
.section-body {
margin-top: 12px;
font-size: 0.9375rem;
font-weight: 400;
color: var(--text-55);
max-width: 520px;
line-height: 1.6;
}
/* ── BEFORE / AFTER ── */
.before-after {
margin-top: 40px;
display: grid;
gap: 12px;
}
@media (min-width: 640px) {
.before-after { grid-template-columns: 1fr 1fr; }
}
.ba-card {
background: var(--card-neutral);
border-radius: 12px;
padding: 24px;
}
.ba-card.after {
background: #fff;
border: 1px solid var(--border);
}
.ba-label {
font-size: 0.6875rem;
font-weight: 500;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--text-45);
margin-bottom: 16px;
}
.ba-label.after { color: var(--blue); }
.ba-row {
display: flex;
align-items: flex-start;
gap: 10px;
font-size: 0.875rem;
color: var(--text-65);
line-height: 1.5;
margin-bottom: 10px;
}
.ba-row:last-child { margin-bottom: 0; }
.ba-icon {
width: 16px;
height: 16px;
flex-shrink: 0;
margin-top: 2px;
}
.ba-icon.neg { color: var(--negative); }
.ba-icon.pos { color: var(--positive); }
/* ── THREE CARDS ── */
.cards-3 {
margin-top: 40px;
display: grid;
gap: 12px;
}
@media (min-width: 640px) { .cards-3 { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .cards-3 { grid-template-columns: repeat(3, 1fr); } }
.card {
background: var(--card-neutral);
border-radius: 12px;
padding: 28px 24px;
}
.card-icon {
width: 48px;
height: 48px;
color: var(--blue);
margin-bottom: 16px;
}
.card-title {
font-size: 0.9375rem;
font-weight: 500;
letter-spacing: -0.01em;
color: var(--text);
margin-bottom: 8px;
}
.card-desc {
font-size: 0.8125rem;
font-weight: 400;
color: var(--text-55);
line-height: 1.5;
}
/* ── TOOLS TABLE ── */
.tools-grid {
margin-top: 40px;
display: grid;
gap: 12px;
}
@media (min-width: 640px) { .tools-grid { grid-template-columns: repeat(3, 1fr); } }
.tool-card {
background: #fff;
border: 1px solid var(--border);
border-radius: 12px;
padding: 24px;
display: flex;
flex-direction: column;
gap: 12px;
}
.tool-icon-wrap {
width: 44px;
height: 44px;
background: var(--card-neutral);
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
color: var(--blue);
}
.tool-icon-wrap svg { width: 22px; height: 22px; }
.tool-name {
font-size: 0.9375rem;
font-weight: 500;
color: var(--text);
}
.tool-role {
font-size: 0.8125rem;
color: var(--text-55);
line-height: 1.5;
}
.tool-link {
margin-top: auto;
font-size: 0.8125rem;
font-weight: 500;
color: var(--blue);
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 4px;
}
.tool-link:hover { color: var(--blue-hover); }
/* ── SCHEDULE ── */
.schedule {
margin-top: 40px;
display: flex;
flex-direction: column;
gap: 0;
}
.sched-item {
display: flex;
gap: 20px;
padding: 20px 0;
border-bottom: 1px solid var(--border);
position: relative;
}
.sched-item:last-child { border-bottom: none; }
.sched-time {
font-size: 0.8125rem;
font-weight: 400;
color: var(--text-45);
width: 52px;
flex-shrink: 0;
padding-top: 2px;
font-variant-numeric: tabular-nums;
}
.sched-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--card-neutral);
border: 2px solid var(--border);
flex-shrink: 0;
margin-top: 5px;
}
.sched-item.active .sched-dot {
background: var(--blue);
border-color: var(--blue);
}
.sched-content { flex: 1; min-width: 0; }
.sched-title {
font-size: 0.9375rem;
font-weight: 500;
color: var(--text);
}
.sched-item.break .sched-title { color: var(--text-55); font-weight: 400; }
.sched-desc {
margin-top: 4px;
font-size: 0.8125rem;
color: var(--text-55);
line-height: 1.5;
}
.sched-tags {
margin-top: 8px;
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.tag {
font-size: 0.6875rem;
font-weight: 500;
padding: 2px 8px;
border-radius: 6px;
background: var(--card-neutral);
color: var(--text-55);
}
.tag.blue { background: rgba(0,102,204,0.1); color: var(--blue); }
/* ── GLOSSARY ── */
.glossary {
margin-top: 40px;
display: flex;
flex-direction: column;
gap: 8px;
}
.gloss-item {
border: 1px solid var(--border);
border-radius: 12px;
overflow: hidden;
}
.gloss-trigger {
width: 100%;
padding: 16px 20px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
background: transparent;
border: none;
cursor: pointer;
text-align: left;
transition: background 150ms ease;
}
@media (prefers-reduced-motion: reduce) { .gloss-trigger { transition: none; } }
.gloss-trigger:hover { background: var(--hover-light); }
.gloss-term {
font-size: 0.9375rem;
font-weight: 500;
color: var(--text);
}
.gloss-abbr {
font-size: 0.75rem;
font-weight: 500;
color: var(--blue);
background: rgba(0,102,204,0.08);
padding: 2px 8px;
border-radius: 6px;
flex-shrink: 0;
}
.gloss-chevron {
width: 16px;
height: 16px;
color: var(--text-55);
flex-shrink: 0;
transition: transform 200ms ease;
}
@media (prefers-reduced-motion: reduce) { .gloss-chevron { transition: none; } }
.gloss-item.open .gloss-chevron { transform: rotate(180deg); }
.gloss-body {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 200ms ease;
}
@media (prefers-reduced-motion: reduce) { .gloss-body { transition: none; } }
.gloss-item.open .gloss-body { grid-template-rows: 1fr; }
.gloss-inner {
overflow: hidden;
}
.gloss-text {
padding: 0 20px 16px;
font-size: 0.875rem;
color: var(--text-65);
line-height: 1.6;
}
.gloss-text strong {
color: var(--text);
font-weight: 500;
}
/* ── PROJECTS ── */
.projects-grid {
margin-top: 40px;
display: grid;
gap: 12px;
}
@media (min-width: 640px) { .projects-grid { grid-template-columns: repeat(2, 1fr); } }
.project-card {
background: #fff;
border: 1px solid var(--border);
border-radius: 12px;
padding: 24px;
display: flex;
flex-direction: column;
gap: 10px;
cursor: pointer;
transition: border-color 150ms ease, box-shadow 150ms ease;
}
@media (prefers-reduced-motion: reduce) { .project-card { transition: none; } }
.project-card:hover {
border-color: rgba(0,102,204,0.3);
box-shadow: 0 0 0 3px rgba(0,102,204,0.06);
}
.project-num {
font-size: 0.6875rem;
font-weight: 500;
color: var(--text-45);
letter-spacing: 0.04em;
}
.project-title {
font-size: 0.9375rem;
font-weight: 500;
color: var(--text);
line-height: 1.4;
}
.project-pain {
font-size: 0.8125rem;
color: var(--text-55);
line-height: 1.5;
flex: 1;
}
.project-tags { display: flex; gap: 6px; flex-wrap: wrap; }
/* ── DARK CTA ── */
.dark-section {
background: linear-gradient(135deg, #000 0%, #002a63 50%, #0066cc 100%);
border-radius: 16px;
padding: 56px 24px;
text-align: center;
margin: 0 16px 0;
}
@media (min-width: 640px) { .dark-section { padding: 72px 40px; } }
.dark-title {
font-size: 1.75rem;
font-weight: 400;
color: #fff;
letter-spacing: -0.02em;
line-height: 1.2;
max-width: 460px;
margin: 0 auto;
}
@media (min-width: 640px) { .dark-title { font-size: 2.25rem; } }
.dark-sub {
margin-top: 12px;
font-size: 0.9375rem;
color: rgba(255,255,255,0.7);
max-width: 380px;
margin-left: auto;
margin-right: auto;
margin-top: 12px;
line-height: 1.5;
}
.dark-cta {
margin-top: 32px;
display: inline-flex;
align-items: center;
gap: 8px;
background: #fff;
color: var(--blue-link);
font-size: 0.9375rem;
font-weight: 500;
padding: 12px 24px;
border-radius: 10px;
text-decoration: none;
transition: color 150ms ease;
}
.dark-cta:hover { color: var(--blue-hover); }
/* ── FOOTER ── */
footer {
padding: 32px 24px 24px;
border-top: 1px solid var(--border);
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: 12px;
}
.footer-copy {
font-size: 0.75rem;
color: var(--text-45);
}
.footer-links {
display: flex;
gap: 16px;
}
.footer-link {
font-size: 0.75rem;
color: var(--text-55);
text-decoration: none;
}
.footer-link:hover { color: var(--text); }
/* ── UTILS ── */
.btn-primary {
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--blue);
color: #fff;
font-size: 0.875rem;
font-weight: 500;
padding: 10px 20px;
border-radius: 8px;
text-decoration: none;
transition: background 150ms ease;
margin-top: 24px;
}
.btn-primary:hover { background: var(--blue-hover); }
.two-col {
display: grid;
gap: 48px;
}
@media (min-width: 1024px) {
.two-col {
grid-template-columns: 1fr 1fr;
align-items: start;
}
}
.section-header { margin-bottom: 0; }