Compare commits

..

25 Commits

Author SHA1 Message Date
timur
508b1747b0 Auth: point folder paths to /Users/telecom/dev/_hack19may to match register-api DEV_BASE 2026-05-19 00:03:50 +05:00
timur
fcf0731fc2 Move Telegram section under hero; refresh QR + group invite to +9UI8dt-60l8yMzJi 2026-05-18 23:54:22 +05:00
timur
80dedafb16 Add Telegram group section (QR + link) below hero 2026-05-18 23:48:12 +05:00
timur
a38e36a7c2 Hero: 'для финансистов' → 'для сотрудников Казахтелеком'; replace ZORRO.md mentions with GITEA.md link; add GITEA.md to repo 2026-05-18 23:46:45 +05:00
ask
e8be79edb0 Add dark theme: CSS variables, prefers-color-scheme auto-detect, sun/moon toggle in nav with localStorage 2026-05-17 21:45:35 +05:00
ask
3165ff0f9b Polish: subtle dot-grid backdrop in hero, fades to clean at edges via radial mask 2026-05-17 21:36:33 +05:00
ask
141349c95e Polish: subtle card hover-lift + scroll-fade-in for sections (respects reduced-motion) 2026-05-17 21:35:23 +05:00
ask
aa3e1bd0cc Nav: match container width (max 1024px, centered) so it aligns with section-labels 2026-05-17 21:31:08 +05:00
ask
3311df1232 Add prompt cheatsheet section with 7 copyable examples (PRD/TRD/code/iterate + bonus CSV+TG bot) 2026-05-17 21:25:09 +05:00
ask
ee07b1fe5a Glossary: restore slider, move below title (no two-col), 360px cards filling full container width 2026-05-17 21:19:57 +05:00
ask
27e5307265 Glossary: replace slider with auto-fit grid filling container width (like Action Plan) 2026-05-17 21:15:43 +05:00
ask
97e5df11b2 Add Vibe+Hard=Enterprise section, P2P framing, +2 finance project cards 2026-05-17 21:10:18 +05:00
ask
f1a4915738 Auth: auto-register on 404 for both FIO and CDN input modes 2026-05-17 20:54:03 +05:00
ask
b5bb8a81d7 Glossary slider fix: width:100% + min-width:0 on container so cards overflow instead of expanding it 2026-05-17 20:48:27 +05:00
ask
0ee5812834 Glossary slider: switch to CSS-grid auto-flow column + console.log diagnostics 2026-05-17 20:40:22 +05:00
ask
22137c6f90 Glossary slider: use index-based scrollIntoView instead of scrollTo (more reliable with snap) 2026-05-17 20:32:48 +05:00
ask
66e831f07f Glossary slider: move nav arrows below cards, add 'N / 14' counter 2026-05-17 20:27:48 +05:00
ask
e0daa85982 Glossary: convert grid to horizontal scroll-snap slider with prev/next buttons 2026-05-17 19:29:43 +05:00
ask
2e7959607e Glossary: replace accordion with always-expanded 2-column card grid 2026-05-17 19:25:39 +05:00
ask
a987baf90f Update auth modal placeholder: Zhuldyz Inat / Zhuldyz 2026-05-17 19:22:39 +05:00
ask
e3ddc7150d Add Action Plan, Что можно создать, Telegram community + expand glossary; Camunda optional 2026-05-17 19:09:20 +05:00
ask
004c8a1920 Wire auth modal to register.telecom.quest API (login + register flow) 2026-05-17 10:18:21 +05:00
ask
ccf82b5040 Add auth modal: smart FIO/CDN input redirecting to code-server folder 2026-05-17 08:05:00 +05:00
ask
941abab31e Inline CSS and JS into index.html (caddy-gitea serves all as text/plain) 2026-05-16 16:37:09 +05:00
ask
cd65423e0e Flatten: move content.js to root for root-served pages 2026-05-16 16:32:05 +05:00
4 changed files with 2339 additions and 956 deletions

50
GITEA.md Normal file
View File

@ -0,0 +1,50 @@
# 🚀 Как бесплатно опубликовать свой сайт (Gitea Pages)
На нашем сервере Gitea работает функция **Pages**. Вы можете выложить любой статический сайт (HTML, CSS, JS), и он будет доступен всему миру по ссылке:
`https://<ваш-логин>.telecom.quest/<имя-репозитория>/`
---
## 🛠 Пошаговая инструкция для новичков
### Шаг 1. Подготовьте файлы сайта
Главный файл должен называться **`index.html`** (все буквы строчные). Он должен лежать в корне проекта, не внутри `src/` или `dist/`.
### Шаг 2. Создайте ветку `gitea-pages`
Gitea Pages ищет сайт строго в ветке **`gitea-pages`**:
1. Откройте репозиторий в браузере.
2. Слева вверху — выпадающий список веток (обычно `main`).
3. Введите в поиск **`gitea-pages`** и нажмите **«Создать ветку»** (Create branch).
### Шаг 3. Загрузите файлы в ветку `gitea-pages`
Переключитесь на ветку `gitea-pages` и добавьте файлы сайта (`index.html`, картинки, стили).
* *Через Git:* `git checkout -b gitea-pages`, добавить файлы, `git commit`, `git push origin gitea-pages`.
* *Через сайт:* кнопка **«Добавить файл»** → **«Загрузить файлы»** в ветке `gitea-pages`.
### Шаг 4. Добавьте топик `gitea-pages-allowall`
**Без этого шага сайт не откроется (404).**
1. На главной странице репозитория справа найдите блок **«Темы»** (Topics) — карандашик/«Управление темами».
2. Впишите **`gitea-pages-allowall`** и сохраните.
---
## 🔗 Где искать мой сайт?
> 🌐 **`https://<ваш_логин>.telecom.quest/<имя_репозитория>/`**
Пример: репо `tjunussov/myportfolio` → `https://tjunussov.telecom.quest/myportfolio/`
*Обновление происходит автоматически при каждом пуше в ветку `gitea-pages`.*
---
## ⚠️ Частые ошибки
* **404 Not Found:**
- Проверьте, что есть **ветка** `gitea-pages` (точное имя).
- Проверьте, что в репо есть **топик** `gitea-pages-allowall`.
- `index.html` лежит в корне ветки `gitea-pages` (не в подпапке).
* **Сайт не обновился:** браузер кэширует. Нажмите `Ctrl + F5` (или `Cmd + Shift + R` на Mac).
* **Картинки/CSS «сломались»:** пути должны быть относительные (`./images/photo.png`, не `/images/photo.png`).

View File

@ -1,136 +0,0 @@
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
}
];

2358
index.html

File diff suppressed because it is too large Load Diff

751
style.css
View File

@ -1,751 +0,0 @@
*, *::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; }