add VibeMap - карта студентов

This commit is contained in:
Medet 2026-08-07 06:23:23 +00:00
parent e1c607655a
commit 244a43e28a
2 changed files with 1075 additions and 234 deletions

View File

@ -3,131 +3,286 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Обучение вайбкодингу | Vibe42</title> <title>Вайбкодинг Гайд | Обучение от Казахтелеком</title>
<style> <style>
* { margin: 0; padding: 0; box-sizing: border-box; } * { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, "Segoe UI", Roboto, sans-serif; background: #0f1218; color: #f4f4f5; line-height: 1.6; } body { font-family: -apple-system, "Segoe UI", Roboto, sans-serif; background: #f5f7fa; color: #1a202c; line-height: 1.6; }
a { color: #00b8cc; text-decoration: none; } a { color: #0066CC; text-decoration: none; transition: color 0.3s; }
a:hover { text-decoration: underline; } a:hover { color: #E4002B; text-decoration: underline; }
.nav { position: fixed; top: 0; left: 0; right: 0; background: rgba(15, 18, 24, 0.95); backdrop-filter: blur(10px); border-bottom: 1px solid #1f2937; z-index: 1000; padding: 12px 0; } /* Казахтелеком цвета */
:root {
--kt-blue: #0066CC;
--kt-red: #E4002B;
--kt-dark: #1a202c;
--kt-light: #f5f7fa;
--kt-gray: #718096;
}
.nav { position: fixed; top: 0; left: 0; right: 0; background: rgba(255, 255, 255, 0.98); backdrop-filter: blur(10px); border-bottom: 3px solid #0066CC; z-index: 1000; padding: 16px 0; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
.nav-container { max-width: 1200px; margin: 0 auto; display: flex; justify-content: space-between; align-items: center; padding: 0 20px; } .nav-container { max-width: 1200px; margin: 0 auto; display: flex; justify-content: space-between; align-items: center; padding: 0 20px; }
.nav-brand { font-size: 20px; font-weight: bold; color: #00b8cc; } .nav-brand { display: flex; align-items: center; gap: 12px; font-size: 22px; font-weight: bold; color: #0066CC; }
.nav-links { display: flex; gap: 24px; } .nav-brand-logo { width: 40px; height: 40px; background: linear-gradient(135deg, #0066CC 0%, #E4002B 100%); border-radius: 8px; display: flex; align-items: center; justify-content: center; color: white; font-size: 20px; }
.nav-links a { color: #f4f4f5; font-size: 14px; transition: color 0.3s; } .nav-links { display: flex; gap: 28px; }
.nav-links a:hover { color: #00b8cc; text-decoration: none; } .nav-links a { color: #1a202c; font-size: 15px; font-weight: 500; transition: all 0.3s; position: relative; }
.nav-links a:hover { color: #E4002B; text-decoration: none; }
.nav-links a::after { content: ''; position: absolute; bottom: -4px; left: 0; width: 0; height: 2px; background: #E4002B; transition: width 0.3s; }
.nav-links a:hover::after { width: 100%; }
.nav-cta { background: #E4002B; color: white !important; padding: 10px 20px; border-radius: 6px; }
.nav-cta:hover { background: #c40025; color: white !important; text-decoration: none; }
.nav-cta::after { display: none !important; }
.hero { padding: 160px 20px 100px; text-align: center; background: linear-gradient(180deg, #0f1218 0%, #1a1f2e 100%); } .hero { padding: 180px 20px 120px; text-align: center; background: linear-gradient(135deg, #0066CC 0%, #004c99 100%); position: relative; overflow: hidden; }
.hero h1 { font-size: clamp(36px, 6vw, 64px); margin-bottom: 24px; line-height: 1.2; } .hero::before { content: ''; position: absolute; top: -50%; right: -20%; width: 800px; height: 800px; background: radial-gradient(circle, rgba(228,0,43,0.15) 0%, transparent 70%); border-radius: 50%; }
.hero p { font-size: clamp(18px, 2.4vw, 24px); color: #9ca3af; max-width: 700px; margin: 0 auto 40px; } .hero::after { content: ''; position: absolute; bottom: -30%; left: -10%; width: 600px; height: 600px; background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%); border-radius: 50%; }
.hero-buttons { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; } .hero-content { position: relative; z-index: 1; max-width: 900px; margin: 0 auto; }
.btn { padding: 14px 28px; border-radius: 8px; font-size: 16px; font-weight: 600; cursor: pointer; border: none; transition: all 0.3s; } .hero-logo { width: 100px; height: 100px; background: white; border-radius: 20px; display: inline-flex; align-items: center; justify-content: center; font-size: 60px; margin-bottom: 32px; box-shadow: 0 10px 30px rgba(0,0,0,0.2); }
.btn-primary { background: #00b8cc; color: #0f1218; } .hero h1 { font-size: clamp(40px, 7vw, 72px); margin-bottom: 24px; line-height: 1.1; color: white; font-weight: 800; }
.btn-primary:hover { background: #00d4e8; transform: translateY(-2px); } .hero p { font-size: clamp(18px, 2.4vw, 24px); color: rgba(255,255,255,0.9); max-width: 700px; margin: 0 auto 48px; }
.btn-secondary { background: transparent; border: 2px solid #00b8cc; color: #00b8cc; } .hero-buttons { display: flex; gap: 20px; justify-content: center; flex-wrap: wrap; }
.btn-secondary:hover { background: #00b8cc; color: #0f1218; } .btn { padding: 16px 32px; border-radius: 10px; font-size: 17px; font-weight: 700; cursor: pointer; border: none; transition: all 0.3s; display: inline-flex; align-items: center; gap: 10px; }
.btn-primary { background: #E4002B; color: white; box-shadow: 0 4px 15px rgba(228,0,43,0.3); }
.btn-primary:hover { background: #ff1a47; transform: translateY(-3px); box-shadow: 0 8px 25px rgba(228,0,43,0.4); text-decoration: none; }
.btn-secondary { background: white; color: #0066CC; box-shadow: 0 4px 15px rgba(0,0,0,0.15); }
.btn-secondary:hover { background: #f0f0f0; transform: translateY(-3px); box-shadow: 0 8px 25px rgba(0,0,0,0.2); text-decoration: none; }
.btn-large { padding: 20px 40px; font-size: 19px; }
section { padding: 80px 20px; max-width: 1000px; margin: 0 auto; } section { padding: 100px 20px; max-width: 1200px; margin: 0 auto; }
section h2 { font-size: clamp(28px, 4vw, 40px); margin-bottom: 40px; color: #00b8cc; text-align: center; } section h2 { font-size: clamp(32px, 5vw, 48px); margin-bottom: 60px; text-align: center; color: #0066CC; font-weight: 700; position: relative; }
section h2::after { content: ''; display: block; width: 80px; height: 4px; background: linear-gradient(90deg, #0066CC, #E4002B); margin: 20px auto 0; border-radius: 2px; }
.card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 24px; } .card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 32px; }
.card { background: #1a1f2e; border-radius: 12px; padding: 28px; border: 1px solid #2d3748; transition: transform 0.3s, border-color 0.3s; } .card { background: white; border-radius: 16px; padding: 36px; border: 2px solid #e2e8f0; transition: all 0.3s; cursor: pointer; position: relative; overflow: hidden; }
.card:hover { transform: translateY(-4px); border-color: #00b8cc; } .card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 4px; background: linear-gradient(90deg, #0066CC, #E4002B); transform: scaleX(0); transition: transform 0.3s; }
.card h3 { font-size: 22px; margin-bottom: 16px; color: #f4f4f5; } .card:hover::before { transform: scaleX(1); }
.card p { color: #9ca3af; font-size: 16px; } .card:hover { transform: translateY(-8px); border-color: #0066CC; box-shadow: 0 12px 40px rgba(0,102,204,0.15); }
.card-icon { font-size: 36px; margin-bottom: 16px; } .card-icon { width: 72px; height: 72px; background: linear-gradient(135deg, #0066CC 0%, #004c99 100%); border-radius: 16px; display: flex; align-items: center; justify-content: center; font-size: 36px; margin-bottom: 24px; }
.card.recommended .card-icon { background: linear-gradient(135deg, #E4002B 0%, #c40025 100%); }
.card h3 { font-size: 24px; margin-bottom: 16px; color: #1a202c; font-weight: 600; }
.card p { color: #718096; font-size: 16px; line-height: 1.7; }
.card-badge { position: absolute; top: 20px; right: 20px; background: #E4002B; color: white; padding: 6px 14px; border-radius: 20px; font-size: 12px; font-weight: 700; text-transform: uppercase; }
.timeline { position: relative; padding-left: 40px; } .timeline { position: relative; padding-left: 60px; max-width: 900px; margin: 0 auto; }
.timeline-item { margin-bottom: 40px; position: relative; } .timeline-item { margin-bottom: 50px; position: relative; animation: fadeInUp 0.6s ease-out; }
.timeline-item::before { content: ""; position: absolute; left: -40px; top: 8px; width: 16px; height: 16px; border-radius: 50%; background: #00b8cc; } @keyframes fadeInUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
.timeline-item::after { content: ""; position: absolute; left: -32px; top: 24px; bottom: -40px; width: 2px; background: #2d3748; } .timeline-item::before { content: ''; position: absolute; left: -60px; top: 8px; width: 24px; height: 24px; border-radius: 50%; background: linear-gradient(135deg, #0066CC, #E4002B); border: 4px solid white; box-shadow: 0 2px 8px rgba(0,0,0,0.2); }
.timeline-item::after { content: ''; position: absolute; left: -52px; top: 32px; bottom: -50px; width: 4px; background: linear-gradient(180deg, #0066CC, #e2e8f0); border-radius: 2px; }
.timeline-item:last-child::after { display: none; } .timeline-item:last-child::after { display: none; }
.timeline-year { font-size: 14px; color: #00b8cc; font-weight: bold; margin-bottom: 8px; } .timeline-year { display: inline-block; background: linear-gradient(135deg, #0066CC, #004c99); color: white; padding: 6px 16px; border-radius: 20px; font-size: 14px; font-weight: 700; margin-bottom: 12px; }
.timeline-title { font-size: 20px; font-weight: bold; margin-bottom: 12px; } .timeline-title { font-size: 24px; font-weight: 700; margin-bottom: 16px; color: #1a202c; }
.timeline-desc { color: #9ca3af; font-size: 16px; } .timeline-desc { color: #718096; font-size: 16px; line-height: 1.7; }
.timeline-item:hover .timeline-title { color: #0066CC; transition: color 0.3s; }
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 24px; margin-top: 40px; } .stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 28px; margin-top: 50px; }
.stat-card { text-align: center; padding: 32px 20px; background: #1a1f2e; border-radius: 12px; border: 1px solid #2d3748; } .stat-card { text-align: center; padding: 40px 24px; background: white; border-radius: 16px; border: 2px solid #e2e8f0; transition: all 0.3s; cursor: pointer; }
.stat-number { font-size: 42px; font-weight: bold; color: #00b8cc; margin-bottom: 8px; } .stat-card:hover { transform: translateY(-6px); border-color: #0066CC; box-shadow: 0 10px 30px rgba(0,102,204,0.12); }
.stat-label { color: #9ca3af; font-size: 14px; } .stat-number { font-size: 48px; font-weight: 800; background: linear-gradient(135deg, #0066CC, #E4002B); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; margin-bottom: 12px; }
.stat-label { color: #718096; font-size: 15px; font-weight: 500; line-height: 1.5; }
.platform-list { list-style: none; } .platform-list { list-style: none; }
.platform-list li { padding: 16px 0; border-bottom: 1px solid #2d3748; display: flex; align-items: center; gap: 16px; } .platform-item { padding: 24px; background: white; border-radius: 12px; margin-bottom: 20px; border: 2px solid #e2e8f0; transition: all 0.3s; cursor: pointer; display: flex; align-items: center; gap: 24px; }
.platform-list li:last-child { border-bottom: none; } .platform-item:hover { border-color: #0066CC; transform: translateX(8px); box-shadow: 0 6px 20px rgba(0,102,204,0.1); }
.platform-icon { width: 48px; height: 48px; background: #2d3748; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 24px; } .platform-icon { width: 64px; height: 64px; background: linear-gradient(135deg, #0066CC 0%, #004c99 100%); border-radius: 12px; display: flex; align-items: center; justify-content: center; font-size: 28px; flex-shrink: 0; }
.platform-name { font-weight: bold; font-size: 18px; } .platform-info { flex: 1; }
.platform-desc { color: #9ca3af; font-size: 14px; } .platform-name { font-weight: 700; font-size: 20px; color: #1a202c; margin-bottom: 6px; }
.platform-desc { color: #718096; font-size: 15px; }
.steps { counter-reset: step; } .steps { counter-reset: step; }
.step { position: relative; padding-left: 60px; margin-bottom: 32px; } .step { position: relative; padding-left: 80px; margin-bottom: 40px; background: white; padding: 32px 32px 32px 80px; border-radius: 16px; border: 2px solid #e2e8f0; transition: all 0.3s; cursor: pointer; }
.step::before { counter-increment: step; content: counter(step); position: absolute; left: 0; top: 0; width: 40px; height: 40px; background: #00b8cc; color: #0f1218; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; font-size: 18px; } .step:hover { border-color: #0066CC; transform: translateX(8px); box-shadow: 0 8px 25px rgba(0,102,204,0.12); }
.step h3 { font-size: 20px; margin-bottom: 8px; } .step::before { counter-increment: step; content: counter(step); position: absolute; left: 20px; top: 28px; width: 48px; height: 48px; background: linear-gradient(135deg, #0066CC, #E4002B); color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 20px; box-shadow: 0 4px 12px rgba(0,102,204,0.3); }
.step p { color: #9ca3af; } .step h3 { font-size: 22px; margin-bottom: 12px; color: #1a202c; font-weight: 600; }
.step p { color: #718096; font-size: 16px; line-height: 1.7; }
.quote { background: #1a1f2e; border-left: 4px solid #00b8cc; padding: 24px 32px; margin: 40px 0; border-radius: 0 12px 12px 0; } .checklist { background: white; border-radius: 16px; padding: 40px; border: 2px solid #e2e8f0; }
.quote p { font-size: 18px; font-style: italic; color: #e5e7eb; margin-bottom: 16px; } .checklist-item { display: flex; align-items: flex-start; gap: 16px; padding: 20px 0; border-bottom: 1px solid #e2e8f0; transition: all 0.3s; cursor: pointer; }
.quote-author { color: #9ca3af; font-size: 14px; } .checklist-item:last-child { border-bottom: none; }
.checklist-item:hover { transform: translateX(8px); }
.check-icon { width: 32px; height: 32px; background: linear-gradient(135deg, #0066CC, #004c99); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: white; font-size: 18px; flex-shrink: 0; }
.check-content h4 { font-size: 18px; color: #1a202c; margin-bottom: 6px; font-weight: 600; }
.check-content p { color: #718096; font-size: 15px; }
.check-number { background: #E4002B; color: white; width: 28px; height: 28px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 700; flex-shrink: 0; }
.cta { text-align: center; padding: 100px 20px; background: linear-gradient(180deg, #1a1f2e 0%, #0f1218 100%); } .cta { text-align: center; padding: 120px 20px; background: linear-gradient(135deg, #0066CC 0%, #004c99 100%); position: relative; overflow: hidden; border-radius: 20px; margin: 60px 20px; }
.cta h2 { font-size: clamp(32px, 5vw, 48px); margin-bottom: 24px; } .cta::before { content: ''; position: absolute; top: -50%; right: -10%; width: 600px; height: 600px; background: radial-gradient(circle, rgba(228,0,43,0.2) 0%, transparent 70%); border-radius: 50%; }
.cta p { font-size: 18px; color: #9ca3af; max-width: 600px; margin: 0 auto 40px; } .cta-content { position: relative; z-index: 1; }
.cta h2 { color: white; margin-bottom: 32px; }
.cta h2::after { background: white; }
.cta p { font-size: 20px; color: rgba(255,255,255,0.9); max-width: 700px; margin: 0 auto 50px; }
footer { background: #1a1f2e; padding: 60px 20px; text-align: center; border-top: 1px solid #2d3748; } footer { background: #1a202c; padding: 80px 20px 40px; text-align: center; }
footer p { color: #9ca3af; font-size: 14px; } .footer-logos { display: flex; justify-content: center; gap: 40px; margin-bottom: 40px; flex-wrap: wrap; }
.footer-logo { width: 80px; height: 80px; background: white; border-radius: 16px; display: flex; align-items: center; justify-content: center; font-size: 40px; }
footer p { color: #718096; font-size: 14px; margin-bottom: 20px; }
.footer-links { display: flex; justify-content: center; gap: 24px; flex-wrap: wrap; margin-top: 20px; }
.footer-links a { color: #a0aec0; font-size: 14px; transition: color 0.3s; }
.footer-links a:hover { color: #E4002B; text-decoration: none; }
.interactive-demo { background: white; border-radius: 16px; padding: 40px; border: 2px solid #e2e8f0; margin-top: 50px; }
.demo-title { font-size: 24px; font-weight: 700; margin-bottom: 24px; color: #1a202c; text-align: center; }
.demo-buttons { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; margin-top: 30px; }
.demo-btn { padding: 14px 28px; border-radius: 8px; font-weight: 600; cursor: pointer; border: 2px solid #e2e8f0; background: white; transition: all 0.3s; }
.demo-btn:hover { border-color: #0066CC; background: #0066CC; color: white; transform: translateY(-2px); }
.demo-result { margin-top: 30px; padding: 24px; background: #f5f7fa; border-radius: 12px; text-align: center; color: #718096; }
@media (max-width: 768px) { @media (max-width: 768px) {
.nav-links { display: none; } .nav-links { display: none; }
.hero { padding-top: 120px; } .hero { padding-top: 140px; }
section { padding: 60px 20px; } .timeline { padding-left: 40px; }
.timeline-item::before { left: -40px; }
.timeline-item::after { left: -32px; }
.step { padding-left: 70px; }
.step::before { left: 10px; width: 40px; height: 40px; font-size: 16px; }
section { padding: 70px 20px; }
} }
</style> </style>
</head> </head>
<body> <body>
<nav class="nav"> <nav class="nav">
<div class="nav-container"> <div class="nav-container">
<div class="nav-brand">🎓 Vibe42</div> <div class="nav-brand">
<div class="nav-brand-logo">V</div>
<span>Vibe42 × Казахтелеком</span>
</div>
<div class="nav-links"> <div class="nav-links">
<a href="#what">Что это</a> <a href="#what">Что это</a>
<a href="#prepare">Подготовка</a>
<a href="#tools">Инструменты</a>
<a href="#history">История</a> <a href="#history">История</a>
<a href="#stats">Статистика</a> <a href="#stats">Статистика</a>
<a href="#platforms">Платформы</a> <a href="#steps">Шаги</a>
<a href="#learn">Обучение</a> <a href="#start" class="nav-cta">Начать сейчас</a>
<a href="#start">С чего начать</a>
</div> </div>
</div> </div>
</nav> </nav>
<header class="hero"> <header class="hero">
<h1>Обучение вайбкодингу</h1> <div class="hero-content">
<p>Как создавать приложения без написания кода — история, теория и практика no-code/low-code разработки</p> <div class="hero-logo">🚀</div>
<div class="hero-buttons"> <h1>Вайбкодинг Гайд</h1>
<button class="btn btn-primary" onclick="document.getElementById('what').scrollIntoView({behavior: 'smooth'})">Начать обучение</button> <p>Образовательная платформа от Казахтелеком: создавай сайты, ботов и приложения без программирования за 15 минут</p>
<button class="btn btn-secondary" onclick="window.open('obuchenie_vaybkodingu.pptx', '_blank')">Скачать презентацию</button> <div class="hero-buttons">
<button class="btn btn-primary btn-large" onclick="document.getElementById('prepare').scrollIntoView({behavior: 'smooth'})">
🎯 Начать обучение
</button>
<button class="btn btn-secondary btn-large" onclick="window.open('obuchenie_vaybkodingu.pptx', '_blank')">
📥 Скачать презентацию
</button>
</div>
</div> </div>
</header> </header>
<section id="what"> <section id="what">
<h2>📖 Что такое вайбкодинг?</h2> <h2>📖 Что такое вайбкодинг?</h2>
<div class="card-grid"> <div class="card-grid">
<div class="card"> <div class="card recommended">
<div class="card-icon">🚀</div> <span class="card-badge">Рекомендуется</span>
<h3>Vibe coding</h3>
<p>Современное название подхода к разработке без написания кода. Создание приложений через визуальные интерфейсы и конфигурацию.</p>
</div>
<div class="card">
<div class="card-icon">🎨</div> <div class="card-icon">🎨</div>
<h3>No-code</h3> <h3>No-Code</h3>
<p>Полное отсутствие написания кода. Использование готовых шаблонов, drag-and-drop интерфейсов и визуальных конструкторов.</p> <p>Полное отсутствие кода. Визуальные конструкторы, drag-and-drop интерфейсы, готовые шаблоны. Идеально для лендингов, сайтов-визиток и простых ботов.</p>
</div> </div>
<div class="card"> <div class="card">
<div class="card-icon">⚡</div> <div class="card-icon">⚡</div>
<h3>Low-code</h3> <h3>Low-Code</h3>
<p>Минимальное использование кода для кастомизации. Визуальная разработка + возможность добавить код при необходимости.</p> <p>Минимум кода для кастомизации. Визуальная разработка + возможность добавить код при необходимости. Для приложений с данными и интеграциями.</p>
</div>
<div class="card">
<div class="card-icon">🤖</div>
<h3>Vibe Coding</h3>
<p>Современный подход: ИИ-ассистенты (Claude, ChatGPT) помогают создавать код, ты только описываешь задачу словами. Будущее разработки уже здесь.</p>
</div> </div>
</div> </div>
<div class="quote">
<p>No-code/low-code платформы позволяют создавать приложения в 10-20 раз быстрее традиционной разработки, делая программирование доступным каждому.</p> <div class="interactive-demo">
<div class="quote-author">— Gartner Research</div> <div class="demo-title">🎮 Попробуй прямо сейчас!</div>
<p style="text-align: center; color: #718096; margin-bottom: 20px;">Нажми на кнопку — увидишь, как работает вайбкодинг:</p>
<div class="demo-buttons">
<button class="demo-btn" onclick="document.getElementById('demoResult').innerHTML = '✅ Сайт-визитка создан за 5 минут!'<br>Ты просто описал задачу — ИИ написал код.'">Создать сайт</button>
<button class="demo-btn" onclick="document.getElementById('demoResult').innerHTML = '🤖 Telegram-бот готов!'<br>Описал словами — получил работающего бота.'">Сделать бота</button>
<button class="demo-btn" onclick="document.getElementById('demoResult').innerHTML = '📊 Приложение с базой данных запущено!'<br>Формы, данные, отчёты — без программирования.'">Запустить приложение</button>
</div>
<div id="demoResult" class="demo-result">Результат появится здесь...</div>
</div>
</section>
<section id="prepare">
<h2>✅ Подготовка к тренингу</h2>
<div class="checklist">
<div class="checklist-item" onclick="this.querySelector('.check-icon').innerHTML = this.querySelector('.check-icon').innerHTML === '✓' ? '👤' : '✓'">
<div class="check-number">1</div>
<div class="check-icon">👤</div>
<div class="check-content">
<h4>Аккаунт ИИ-сервиса</h4>
<p>Claude (claude.ai) или ChatGPT (chatgpt.com). Рекомендуется вход через Google для простоты.</p>
</div>
</div>
<div class="checklist-item" onclick="this.querySelector('.check-icon').innerHTML = this.querySelector('.check-icon').innerHTML === '✓' ? '💻' : '✓'">
<div class="check-number">2</div>
<div class="check-icon">💻</div>
<div class="check-content">
<h4>Операционная система</h4>
<p>Windows или macOS — инструкция подстроится под вашу систему.</p>
</div>
</div>
<div class="checklist-item" onclick="this.querySelector('.check-icon').innerHTML = this.querySelector('.check-icon').innerHTML === '✓' ? '📦' : '✓'">
<div class="check-number">3</div>
<div class="check-icon">📦</div>
<div class="check-content">
<h4>Git установлен</h4>
<p>Система сохранения истории проекта. Скачай с git-scm.com или через Homebrew (macOS).</p>
</div>
</div>
<div class="checklist-item" onclick="this.querySelector('.check-icon').innerHTML = this.querySelector('.check-icon').innerHTML === '✓' ? '🔧' : '✓'">
<div class="check-number">4</div>
<div class="check-icon">🔧</div>
<div class="check-content">
<h4>Claude Code или Codex</h4>
<p>Инструмент для работы с кодом. Помогает писать, менять и проверять проект.</p>
</div>
</div>
<div class="checklist-item" onclick="this.querySelector('.check-icon').innerHTML = this.querySelector('.check-icon').innerHTML === '✓' ? '🐙' : '✓'">
<div class="check-number">5</div>
<div class="check-icon">🐙</div>
<div class="check-content">
<h4>Аккаунт GitHub</h4>
<p>github.com — хранилище кода и публикация сайтов через GitHub Pages.</p>
</div>
</div>
<div class="checklist-item" onclick="this.querySelector('.check-icon').innerHTML = this.querySelector('.check-icon').innerHTML === '✓' ? '📁' : '✓'">
<div class="check-number">6</div>
<div class="check-icon">📁</div>
<div class="check-content">
<h4>Папка проекта</h4>
<p>Создай рабочую папку (например, "Первый проект Вайбкодинг") и открой в приложении.</p>
</div>
</div>
<div class="checklist-item" onclick="this.querySelector('.check-icon').innerHTML = this.querySelector('.check-icon').innerHTML === '✓' ? '🔗' : '✓'">
<div class="check-number">7</div>
<div class="check-icon">🔗</div>
<div class="check-content">
<h4>Связь Codex и GitHub</h4>
<p>Авторизуй GitHub через GitHub CLI: `gh auth login` — следуй инструкциям.</p>
</div>
</div>
</div>
</section>
<section id="tools">
<h2>🛠 Выбор инструмента</h2>
<div class="card-grid">
<div class="card recommended">
<span class="card-badge">Рекомендуется</span>
<div class="card-icon">💬</div>
<h3>ChatGPT + Codex</h3>
<p>ChatGPT — чат с ИИ для обсуждения идей. Codex — инструмент OpenAI для работы с кодом: помогает писать, менять и проверять проект. Идеально для новичков.</p>
<button class="btn btn-primary" style="margin-top: 20px;" onclick="window.open('https://chatgpt.com', '_blank')">Выбрать ChatGPT →</button>
</div>
<div class="card">
<div class="card-icon">🎯</div>
<h3>Claude + Claude Code</h3>
<p>Claude — чат с ИИ от Anthropic. Claude Code — инструмент для работы с файлами: читает файлы, пишет код и помогает собирать проект. Альтернатива ChatGPT.</p>
<button class="btn btn-secondary" style="margin-top: 20px;" onclick="window.open('https://claude.ai', '_blank')">Выбрать Claude →</button>
</div>
</div> </div>
</section> </section>
@ -135,34 +290,34 @@
<h2>📅 История развития</h2> <h2>📅 История развития</h2>
<div class="timeline"> <div class="timeline">
<div class="timeline-item"> <div class="timeline-item">
<div class="timeline-year">1990-е</div> <span class="timeline-year">1990-е</span>
<div class="timeline-title">Рождение RAD</div> <div class="timeline-title">Рождение RAD (Rapid Application Development)</div>
<div class="timeline-desc">Visual Basic (1991) и Delphi (1995) — первые визуальные среды разработки с drag-and-drop интерфейсами. Принцип: перетаскивание элементов вместо написания кода.</div> <div class="timeline-desc">Visual Basic (1991) и Delphi (1995) — первые визуальные среды разработки. Принцип: перетаскивание элементов вместо написания кода.</div>
</div> </div>
<div class="timeline-item"> <div class="timeline-item">
<div class="timeline-year">2000-е</div> <span class="timeline-year">2000-е</span>
<div class="timeline-title">End-User Development</div> <div class="timeline-title">End-User Development</div>
<div class="timeline-desc">Microsoft Access, Excel, SharePoint Designer. Salesforce.com (1999) — облачные CRM с конструктором приложений. Рост автоматизации бизнес-процессов.</div> <div class="timeline-desc">Microsoft Access, Excel, SharePoint Designer. Salesforce.com (1999) — облачные CRM с конструктором приложений.</div>
</div> </div>
<div class="timeline-item"> <div class="timeline-item">
<div class="timeline-year">2011</div> <span class="timeline-year">2011</span>
<div class="timeline-title">Зарождение рынка</div> <div class="timeline-title">Зарождение рынка low-code</div>
<div class="timeline-desc">Появление первых специализированных low-code платформ. Начало формирования отдельного рынка инструментов.</div> <div class="timeline-desc">Появление первых специализированных low-code платформ. Начало формирования отдельного рынка.</div>
</div> </div>
<div class="timeline-item"> <div class="timeline-item">
<div class="timeline-year">9 июня 2014</div> <span class="timeline-year">2014</span>
<div class="timeline-title">Термин «low-code»</div> <div class="timeline-title">Термин «low-code» от Forrester</div>
<div class="timeline-desc">Аналитики Forrester Research (Клэй Ричардсон) вводят термин «low-code» в отчёте «New Development Platforms Emerge For Customer-Facing Applications».</div> <div class="timeline-desc">9 июня 2014: аналитики Forrester Research вводят термин «low-code» в отчёте о новых платформах разработки.</div>
</div> </div>
<div class="timeline-item"> <div class="timeline-item">
<div class="timeline-year">2017</div> <span class="timeline-year">2017</span>
<div class="timeline-title">Признание Forbes</div> <div class="timeline-title">Признание Forbes</div>
<div class="timeline-desc">Forbes называет no-code/low-code движение «чрезвычайно разрушительной технологией», которая изменит разработку ПО.</div> <div class="timeline-desc">Forbes называет no-code/low-code «чрезвычайно разрушительной технологией», которая изменит разработку ПО.</div>
</div> </div>
<div class="timeline-item"> <div class="timeline-item">
<div class="timeline-year">2020-2025</div> <span class="timeline-year">2020-2025</span>
<div class="timeline-title">Бум no-code</div> <div class="timeline-title">Бум no-code и ИИ-революция</div>
<div class="timeline-desc">Пандемия ускорила цифровизацию. Gartner: к 2024 году 65% приложений создаются на low-code. Рынок достиг $25 млрд. Интеграция ИИ (ChatGPT, автогенерация).</div> <div class="timeline-desc">Пандемия ускорила цифровизацию. Gartner: 65% приложений на low-code к 2024. Рынок $25 млрд. Интеграция ChatGPT и автогенерация кода.</div>
</div> </div>
</div> </div>
</section> </section>
@ -170,155 +325,63 @@
<section id="stats"> <section id="stats">
<h2>📊 Статистика рынка</h2> <h2>📊 Статистика рынка</h2>
<div class="stat-grid"> <div class="stat-grid">
<div class="stat-card"> <div class="stat-card" onclick="alert('Forrester Research прогнозировал рост рынка low-code до $15.5 млрд к 2020 году — прогноз полностью сбылся!')">
<div class="stat-number">$15.5 млрд</div> <div class="stat-number">$15.5 млрд</div>
<div class="stat-label">Рынок low-code к 2020 (Forrester)</div> <div class="stat-label">Рынок low-code к 2020<br>(Forrester)</div>
</div> </div>
<div class="stat-card"> <div class="stat-card" onclick="alert('Gartner предсказывает: к 2024 году 65% всех приложений будут создаваться на low-code платформах!')">
<div class="stat-number">65%</div> <div class="stat-number">65%</div>
<div class="stat-label">Приложений на low-code к 2024 (Gartner)</div> <div class="stat-label">Приложений на low-code к 2024<br>(Gartner)</div>
</div> </div>
<div class="stat-card"> <div class="stat-card" onclick="alert('Средняя скорость разработки на no-code/low-code платформах в 10-20 раз быстрее традиционного программирования!')">
<div class="stat-number">10-20x</div> <div class="stat-number">10-20x</div>
<div class="stat-label">Быстрее традиционной разработки</div> <div class="stat-label">Быстрее традиционной разработки</div>
</div> </div>
<div class="stat-card"> <div class="stat-card" onclick="alert('80% no-code разработчиков — люди без технического образования. Вайбкодинг действительно доступен каждому!')">
<div class="stat-number">80%</div> <div class="stat-number">80%</div>
<div class="stat-label">No-code разработчиков без тех. образования</div> <div class="stat-label">Разработчиков без тех. образования</div>
</div> </div>
<div class="stat-card"> <div class="stat-card" onclick="alert('Экономия затрат достигает 70% по сравнению с наймом профессиональных разработчиков!')">
<div class="stat-number">70%</div> <div class="stat-number">70%</div>
<div class="stat-label">Экономия затрат vs наём разработчиков</div> <div class="stat-label">Экономия затрат vs наём разработчиков</div>
</div> </div>
<div class="stat-card"> <div class="stat-card" onclick="alert('Глобальный рынок no-code/low-code достиг $25 млрд к 2025 году и продолжает расти!')">
<div class="stat-number">$25 млрд</div> <div class="stat-number">$25 млрд</div>
<div class="stat-label">Рынок no-code/low-code к 2025</div> <div class="stat-label">Рынок no-code/low-code к 2025</div>
</div> </div>
</div> </div>
</section> </section>
<section id="platforms"> <section id="steps">
<h2>🛠 Популярные платформы</h2> <h2>🚀 7 шагов к первому проекту</h2>
<ul class="platform-list">
<li>
<div class="platform-icon">🌐</div>
<div>
<div class="platform-name">Webflow, Tilda</div>
<div class="platform-desc">Лендинги и сайты с визуальным редактором</div>
</div>
</li>
<li>
<div class="platform-icon">💻</div>
<div>
<div class="platform-name">Bubble</div>
<div class="platform-desc">Полноценные веб-приложения без кода</div>
</div>
</li>
<li>
<div class="platform-icon">📊</div>
<div>
<div class="platform-name">Airtable, Notion</div>
<div class="platform-desc">Базы данных и управление проектами</div>
</div>
</li>
<li>
<div class="platform-icon">⚙️</div>
<div>
<div class="platform-name">Zapier, Make</div>
<div class="platform-desc">Автоматизация процессов между сервисами</div>
</div>
</li>
<li>
<div class="platform-icon">📱</div>
<div>
<div class="platform-name">Glide, Adalo</div>
<div class="platform-desc">Мобильные приложения из таблиц</div>
</div>
</li>
<li>
<div class="platform-icon">🤖</div>
<div>
<div class="platform-name">Vibe42</div>
<div class="platform-desc">Учебная платформа: сайты, боты, приложения за 15 минут</div>
</div>
</li>
</ul>
</section>
<section id="learn">
<h2>🎓 Принципы обучения</h2>
<div class="card-grid">
<div class="card">
<h3>📈 От простого к сложному</h3>
<p>Начинай с визитки или лендинга, затем переходи к формам, данным и интеграциям. Не пытайся сделать всё сразу.</p>
</div>
<div class="card">
<h3>⚡ Практика сразу</h3>
<p>Первый результат за 10-15 минут. Не зубри теорию — создавай проекты с первой минуты.</p>
</div>
<div class="card">
<h3>🧠 Понимать логику</h3>
<p>Не копируй слепо — разбирайся, почему работает именно так. Это поможет создавать свои решения.</p>
</div>
<div class="card">
<h3>🔄 Итерации</h3>
<p>Сделал → показал → получил фидбек → улучшил. Повторяй цикл как можно чаще.</p>
</div>
</div>
<div class="quote">
<p>Цель обучения — не выучить инструменты, а развить мышление создателя. Инструменты меняются, мышление остаётся.</p>
<div class="quote-author">— Vibe42 Methodology</div>
</div>
</section>
<section id="start">
<h2>🚀 С чего начать новичку?</h2>
<div class="steps"> <div class="steps">
<div class="step"> <div class="step" onclick="window.open('https://claude.ai', '_blank')">
<h3>Выбрать простую идею</h3> <h3>Шаг 1: Создай аккаунт Claude или ChatGPT</h3>
<p>Лендинг-визитка, персональное резюме, афиша мероприятия или Telegram-бот. Что-то, что можно сделать за один вечер.</p> <p>Открой claude.ai или chatgpt.com. Рекомендуется вход через Google — проще входить на разных устройствах. Проверь, что чат открывается.</p>
</div>
<div class="step" onclick="window.open('https://claude.ai/upgrade', '_blank')">
<h3>Шаг 2: Оформи подписку (рекомендуется)</h3>
<p>Claude Pro (~$20/мес) или ChatGPT Plus дают больше возможностей. Открой claude.ai/upgrade или настройки ChatGPT, выбери тариф.</p>
</div>
<div class="step" onclick="window.open('https://github.com/git-for-windows/git/releases', '_blank')">
<h3>Шаг 3: Установи Git</h3>
<p>Windows: скачай git-scm.com, установи с настройками по умолчанию. macOS: открой Терминал, вставь `brew install git`. Перезапусти приложение.</p>
</div> </div>
<div class="step"> <div class="step">
<h3>Открыть Vibe42</h3> <h3>Шаг 4: Установи Claude Code или Codex</h3>
<p>Ты уже в проекте! Следуй рецептам из AGENTS.md — там готовые шаблоны для каждого типа проекта.</p> <p>Скачай приложение для своей системы. Запусти, войди в аккаунт из шага 1. Убедись, что приложение открывается без ошибок.</p>
</div>
<div class="step" onclick="window.open('https://github.com', '_blank')">
<h3>Шаг 5: Создай аккаунт GitHub</h3>
<p>Открой github.com, зарегистрируйся через Google. Подтверди email через письмо. Проходи капчу внимательно с первого раза.</p>
</div> </div>
<div class="step"> <div class="step">
<h3>Создать первую версию</h3> <h3>Шаг 6: Создай папку проекта</h3>
<p>Используй design.md как основу для стилей. Скопируй структуру из презентации или создай свою.</p> <p>Создай папку (например, на рабочем столе) с названием вроде "Первый проект Вайбкодинг". Открой её в Claude Code через "Choose project" → "New project".</p>
</div> </div>
<div class="step"> <div class="step">
<h3>Опубликовать</h3> <h3>Шаг 7: Подключи GitHub к Codex</h3>
<p>Нажми «Опубликовать» — сайт мгновенно появится на pages.git.vibe42.kz/Medet/твой-проект.</p> <p>В Codex вставь: `gh auth login --hostname github.com --git-protocol https --web`. Открой ссылку, введи код с экрана в GitHub, подтверди авторизацию.</p>
</div>
<div class="step">
<h3>Поделиться и улучшить</h3>
<p>Покажи друзьям, получи фидбек, добавь формы, данные или ИИ-интеграции.</p>
</div>
</div>
</section>
<section>
<h2>⚠️ Типичные ошибки</h2>
<div class="card-grid">
<div class="card">
<div class="card-icon">❌</div>
<h3>Всё и сразу</h3>
<p>Желание сделать идеальный продукт с первого раза. Решение: начни с MVP за 15 минут.</p>
</div>
<div class="card">
<div class="card-icon">📚</div>
<h3>Учить программирование</h3>
<p>Параллельное изучение JavaScript или Python замедлит прогресс. No-code работает без кода!</p>
</div>
<div class="card">
<div class="card-icon">🏗️</div>
<h3>Сложные инструменты</h3>
<p>Использование Bubble для лендинга или React для бота. Выбирай стек по таблице в AGENTS.md.</p>
</div>
<div class="card">
<div class="card-icon">🔒</div>
<h3>Не публиковать</h3>
<p>Проект живёт только когда его видят другие. Публикуй после каждой итерации.</p>
</div> </div>
</div> </div>
</section> </section>
@ -328,43 +391,57 @@
<div class="stat-grid"> <div class="stat-grid">
<div class="stat-card"> <div class="stat-card">
<div class="stat-number">$50-100K</div> <div class="stat-number">$50-100K</div>
<div class="stat-label">No-code разработчик (США)</div> <div class="stat-label">No-code разработчик (США)<br>Фриланс и работа в компаниях</div>
</div> </div>
<div class="stat-card"> <div class="stat-card">
<div class="stat-number">∞</div> <div class="stat-number">∞</div>
<div class="stat-label">Фриланс: сайты и боты на заказ</div> <div class="stat-label">Фриланс: сайты и боты<br>на заказ от $500 за проект</div>
</div> </div>
<div class="stat-card"> <div class="stat-card">
<div class="stat-number">🚀</div> <div class="stat-number">🚀</div>
<div class="stat-label">Свой стартап без программиста</div> <div class="stat-label">Свой стартап<br>без сооснователя-программиста</div>
</div> </div>
<div class="stat-card"> <div class="stat-card">
<div class="stat-number">📊</div> <div class="stat-number">📊</div>
<div class="stat-label">Бизнес-аналитик с автоматизацией</div> <div class="stat-label">Бизнес-аналитик<br>с навыками автоматизации</div>
</div> </div>
</div> </div>
</section> </section>
<div class="cta" id="cta"> <div class="cta" id="start">
<h2>🎯 Готов начать?</h2> <div class="cta-content">
<p>Твой первый сайт или бот — за 15 минут. Не нужно быть программистом, нужно просто начать.</p> <h2>🎯 Готов начать?</h2>
<div class="hero-buttons"> <p>Твой первый сайт, бот или приложение — за 15 минут. Не нужно быть программистом. Просто следуй шагам выше.</p>
<button class="btn btn-primary" onclick="document.getElementById('start').scrollIntoView({behavior: 'smooth'})">Создать проект</button> <div class="hero-buttons">
<button class="btn btn-secondary" onclick="window.open('obuchenie_vaybkodingu.pptx', '_blank')">Скачать презентацию</button> <button class="btn btn-primary btn-large" onclick="document.getElementById('prepare').scrollIntoView({behavior: 'smooth'})">
📋 Пройти чеклист подготовки
</button>
<button class="btn btn-secondary btn-large" onclick="window.open('https://argynz.github.io/github-pages-guide/', '_blank')">
📖 Открыть полный гайд →
</button>
</div>
</div> </div>
</div> </div>
<footer> <footer>
<p>© 2025 Vibe42 | Образовательная платформа по вайбкодингу</p> <div class="footer-logos">
<p style="margin-top: 12px;"> <div class="footer-logo">🚀</div>
<a href="https://pages.git.vibe42.kz/Medet/">Pages</a> • <div class="footer-logo">V</div>
<a href="https://git.vibe42.kz/Medet">Git</a> • <div class="footer-logo">KT</div>
</div>
<p>© 2025 Vibe42 × Казахтелеком | Образовательная платформа по вайбкодингу</p>
<p>Создано для обучения будущих разработчиков Казахстана</p>
<div class="footer-links">
<a href="https://pages.git.vibe42.kz/Medet/">Vibe42 Pages</a>
<a href="https://git.vibe42.kz/Medet">Vibe42 Git</a>
<a href="https://wiki.ai-kt.kz">Alem AI</a> <a href="https://wiki.ai-kt.kz">Alem AI</a>
</p> <a href="https://argynz.github.io/github-pages-guide/">Гайд GitHub Pages</a>
<a href="https://claude.ai">Claude</a>
<a href="https://chatgpt.com">ChatGPT</a>
</div>
</footer> </footer>
<script> <script>
// Плавная прокрутка для якорных ссылок
document.querySelectorAll('a[href^="#"]').forEach(anchor => { document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) { anchor.addEventListener('click', function (e) {
e.preventDefault(); e.preventDefault();
@ -372,6 +449,23 @@
if (target) target.scrollIntoView({ behavior: 'smooth' }); if (target) target.scrollIntoView({ behavior: 'smooth' });
}); });
}); });
// Анимация при скролле
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, { threshold: 0.1 });
document.querySelectorAll('.card, .stat-card, .timeline-item, .step, .checklist-item').forEach(el => {
el.style.opacity = '0';
el.style.transform = 'translateY(30px)';
el.style.transition = 'opacity 0.6s ease-out, transform 0.6s ease-out';
observer.observe(el);
});
</script> </script>
</body> </body>
</html> </html>

747
vibemap.html Normal file
View File

@ -0,0 +1,747 @@
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta name="theme-color" content="#0066CC">
<meta name="description" content="VibeMap — карта студентов и проектов Vibe42. Найди единомышленников, поделись проектом.">
<title>VibeMap — карта студентов Vibe42</title>
<!-- PWA -->
<link rel="manifest" href="manifest.webmanifest">
<link rel="icon" type="image/png" sizes="192x192" href="icon-192.png">
<link rel="apple-touch-icon" sizes="192x192" href="icon-192.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="VibeMap">
<meta name="mobile-web-app-capable" content="yes">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css">
<style>
:root {
--kt-blue: #0066CC;
--kt-red: #E4002B;
--kt-dark: #1a202c;
--kt-light: #f5f7fa;
--text: #1a202c;
--text-muted: #718096;
--bg: #f5f7fa;
--card-bg: #ffffff;
--success: #2a9d8f;
--warning: #f4a261;
--danger: #e63946;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, "Segoe UI", Roboto, sans-serif; background: var(--bg); color: var(--text); overflow: hidden; }
/* TOP BAR */
.topbar {
position: fixed; top: 0; left: 0; right: 0;
background: linear-gradient(135deg, var(--kt-blue) 0%, #004c99 100%);
color: white; padding: 12px 16px; z-index: 1000;
display: flex; align-items: center; gap: 16px;
box-shadow: 0 2px 12px rgba(0,0,0,0.15);
}
.logo { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 18px; }
.logo-emoji { font-size: 28px; }
.logo-text div:first-child { font-size: 18px; font-weight: 800; }
.logo-text .tagline { font-size: 11px; opacity: 0.9; font-weight: 400; }
.menu-btn {
margin-left: auto; background: rgba(255,255,255,0.15);
border: none; color: white; width: 40px; height: 40px;
border-radius: 10px; cursor: pointer; font-size: 20px;
transition: background 0.3s;
}
.menu-btn:hover { background: rgba(255,255,255,0.25); }
.stats-btn {
background: rgba(255,255,255,0.15); border: none; color: white;
padding: 8px 14px; border-radius: 10px; cursor: pointer;
display: flex; align-items: center; gap: 8px; font-size: 13px;
transition: all 0.3s;
}
.stats-btn:hover { background: rgba(255,255,255,0.25); transform: translateY(-1px); }
.stats-count {
background: var(--kt-red); color: white;
padding: 2px 8px; border-radius: 12px; font-size: 12px;
font-weight: 700;
}
.city-selector {
background: rgba(255,255,255,0.2); border: 1px solid rgba(255,255,255,0.3);
color: white; padding: 8px 12px; border-radius: 8px;
font-size: 13px; cursor: pointer; outline: none;
}
.city-selector option { background: var(--kt-dark); color: white; }
/* FILTER BAR */
.filter-bar {
position: fixed; top: 70px; left: 0; right: 0;
background: white; padding: 12px 16px; z-index: 999;
display: flex; gap: 8px; overflow-x: auto;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.chip {
background: #f0f0f0; border: none; padding: 8px 14px;
border-radius: 20px; font-size: 13px; cursor: pointer;
white-space: nowrap; transition: all 0.3s;
display: flex; align-items: center; gap: 6px;
}
.chip:hover { background: #e0e0e0; }
.chip.active { background: var(--kt-blue); color: white; }
.chip .dot {
width: 8px; height: 8px; border-radius: 50%;
background: var(--kt-blue);
}
.chip .dot-vet { background: var(--success); }
.chip .dot-shelter { background: var(--warning); }
.chip .dot-petshop { background: var(--danger); }
/* MAP */
#map { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 1; }
/* FAB */
.fab {
position: fixed; bottom: 30px; right: 30px;
width: 64px; height: 64px; border-radius: 50%;
background: linear-gradient(135deg, var(--kt-red) 0%, #c40025 100%);
color: white; border: none; font-size: 32px;
cursor: pointer; box-shadow: 0 4px 20px rgba(228,0,43,0.4);
z-index: 1000; transition: all 0.3s;
}
.fab:hover { transform: scale(1.1); box-shadow: 0 6px 30px rgba(228,0,43,0.5); }
/* AI FAB */
.ai-fab {
position: fixed; bottom: 110px; right: 30px;
width: 52px; height: 52px; border-radius: 50%;
background: linear-gradient(135deg, var(--kt-blue) 0%, #004c99 100%);
color: white; border: none; font-size: 24px;
cursor: pointer; box-shadow: 0 4px 15px rgba(0,102,204,0.3);
z-index: 1000; transition: all 0.3s;
}
.ai-fab:hover { transform: scale(1.05); }
.ai-fab-pulse {
position: absolute; top: 50%; left: 50%;
transform: translate(-50%, -50%);
width: 100%; height: 100%; border-radius: 50%;
background: inherit; animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: translate(-50%, -50%) scale(1); opacity: 0.7; }
100% { transform: translate(-50%, -50%) scale(1.6); opacity: 0; }
}
/* AI PANEL */
.ai-panel {
position: fixed; bottom: 180px; right: 30px;
width: 340px; max-height: 450px;
background: white; border-radius: 16px;
box-shadow: 0 8px 40px rgba(0,0,0,0.15);
z-index: 1001; display: flex; flex-direction: column;
overflow: hidden;
}
.ai-panel[hidden] { display: none; }
.ai-panel-head {
background: linear-gradient(135deg, var(--kt-blue), #004c99);
color: white; padding: 14px 16px;
display: flex; justify-content: space-between; align-items: center;
}
.ai-head-info { display: flex; align-items: center; gap: 10px; }
.ai-head-avatar { font-size: 24px; }
.ai-head-name { font-weight: 700; font-size: 14px; }
.ai-head-status { font-size: 11px; opacity: 0.9; display: flex; align-items: center; gap: 4px; }
.ai-status-dot { width: 6px; height: 6px; background: #2a9d8f; border-radius: 50%; }
.ai-panel-close {
background: rgba(255,255,255,0.2); border: none;
color: white; width: 28px; height: 28px; border-radius: 50%;
cursor: pointer; font-size: 18px;
}
.ai-messages {
flex: 1; overflow-y: auto; padding: 16px;
display: flex; flex-direction: column; gap: 12px;
background: #f8f9fa;
}
.ai-msg {
max-width: 85%; padding: 12px 16px; border-radius: 14px;
font-size: 14px; line-height: 1.5;
}
.ai-msg.ai {
background: white; align-self: flex-start;
border-bottom-left-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.ai-msg.user {
background: var(--kt-blue); color: white;
align-self: flex-end; border-bottom-right-radius: 4px;
}
.ai-suggestions {
display: flex; gap: 8px; padding: 12px 16px;
overflow-x: auto; background: white;
border-top: 1px solid #e2e8f0;
}
.ai-sug {
background: #f0f4f8; color: var(--kt-blue);
padding: 8px 14px; border-radius: 20px;
font-size: 12px; white-space: nowrap; cursor: pointer;
border: 1px solid transparent; transition: all 0.3s;
}
.ai-sug:hover { border-color: var(--kt-blue); background: white; }
.ai-input-row {
display: flex; gap: 8px; padding: 12px 16px;
background: white; border-top: 1px solid #e2e8f0;
}
.ai-input-row input {
flex: 1; padding: 10px 14px; border: 1px solid #e2e8f0;
border-radius: 24px; font-size: 14px; outline: none;
}
.ai-input-row input:focus { border-color: var(--kt-blue); }
.ai-input-row button {
background: var(--kt-blue); color: white; border: none;
width: 40px; height: 40px; border-radius: 50%;
cursor: pointer; font-size: 16px;
transition: background 0.3s;
}
.ai-input-row button:hover { background: #004c99; }
/* DRAWER */
.drawer { position: fixed; inset: 0; z-index: 2000; }
.drawer[hidden] { display: none; }
.drawer-backdrop {
position: absolute; inset: 0; background: rgba(0,0,0,0.5);
}
.drawer-panel {
position: absolute; left: 0; top: 0; bottom: 0;
width: 300px; background: white;
box-shadow: 4px 0 20px rgba(0,0,0,0.15);
display: flex; flex-direction: column;
}
.drawer-head {
background: linear-gradient(135deg, var(--kt-blue), #004c99);
color: white; padding: 20px 16px;
display: flex; justify-content: space-between; align-items: center;
}
.drawer-logo { font-size: 20px; font-weight: 800; }
.drawer-close {
background: rgba(255,255,255,0.2); border: none;
color: white; width: 32px; height: 32px; border-radius: 50%;
cursor: pointer; font-size: 20px;
}
.drawer-nav { flex: 1; padding: 16px 0; }
.drawer-nav button {
width: 100%; padding: 14px 20px; text-align: left;
background: none; border: none; font-size: 15px;
color: var(--text); cursor: pointer;
display: flex; align-items: center; gap: 12px;
transition: background 0.2s;
}
.drawer-nav button:hover { background: #f0f4f8; }
.drawer-footer {
padding: 20px; border-top: 1px solid #e2e8f0;
font-size: 12px; color: var(--text-muted); text-align: center;
}
/* PREVIEW BOTTOM SHEET */
.preview {
position: fixed; bottom: 0; left: 0; right: 0;
background: white; border-radius: 20px 20px 0 0;
box-shadow: 0 -4px 20px rgba(0,0,0,0.15);
z-index: 1002; transform: translateY(100%);
transition: transform 0.3s;
max-height: 70vh; overflow-y: auto;
}
.preview.open { transform: translateY(0); }
.preview-handle {
width: 40px; height: 4px; background: #cbd5e0;
border-radius: 2px; margin: 12px auto;
}
.preview-content { padding: 0 20px 20px; }
.preview-title { font-size: 20px; font-weight: 700; margin-bottom: 12px; }
.preview-meta { display: flex; gap: 12px; margin-bottom: 16px; flex-wrap: wrap; }
.preview-tag {
background: #f0f4f8; color: var(--kt-blue);
padding: 4px 10px; border-radius: 12px; font-size: 12px;
}
.preview-desc { color: var(--text-muted); line-height: 1.6; margin-bottom: 16px; }
.preview-contact {
background: #f0f4f8; padding: 12px; border-radius: 10px;
margin-bottom: 16px;
}
.preview-actions { display: flex; gap: 12px; }
.preview-btn {
flex: 1; padding: 12px; border: none; border-radius: 10px;
font-weight: 600; cursor: pointer; transition: all 0.3s;
}
.preview-btn.primary { background: var(--kt-blue); color: white; }
.preview-btn.primary:hover { background: #004c99; }
.preview-btn.secondary { background: #f0f0f0; color: var(--text); }
.preview-btn.secondary:hover { background: #e0e0e0; }
/* MODAL */
.modal {
position: fixed; inset: 0; background: rgba(0,0,0,0.5);
z-index: 3000; display: none; justify-content: center;
align-items: center; padding: 20px;
}
.modal.open { display: flex; }
.modal-content {
background: white; border-radius: 16px;
max-width: 500px; width: 100%; max-height: 90vh;
overflow-y: auto; padding: 24px;
}
.modal-title { font-size: 22px; font-weight: 700; margin-bottom: 20px; }
.form-group { margin-bottom: 16px; }
.form-group label {
display: block; margin-bottom: 6px; font-weight: 600;
font-size: 14px; color: var(--text-muted);
}
.form-group input, .form-group textarea, .form-group select {
width: 100%; padding: 12px; border: 1px solid #e2e8f0;
border-radius: 10px; font-size: 14px; outline: none;
}
.form-group input:focus, .form-group textarea:focus { border-color: var(--kt-blue); }
.form-actions { display: flex; gap: 12px; margin-top: 20px; }
/* TOAST */
.toast {
position: fixed; bottom: 30px; left: 50%;
transform: translateX(-50%) translateY(100px);
background: var(--kt-dark); color: white;
padding: 14px 24px; border-radius: 10px;
z-index: 5000; transition: transform 0.3s;
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
}
.toast.show { transform: translateX(-50%) translateY(0); }
/* MASCOT */
.mascot {
position: fixed; bottom: 20px; left: 20px;
width: 100px; height: 100px; z-index: 999;
cursor: pointer; transition: transform 0.3s;
}
.mascot:hover { transform: scale(1.05); }
.mascot-bubble {
position: absolute; bottom: 100px; left: 50%;
transform: translateX(-50%);
background: white; padding: 12px 16px;
border-radius: 16px; box-shadow: 0 4px 15px rgba(0,0,0,0.15);
font-size: 13px; white-space: nowrap;
opacity: 0; transition: opacity 0.3s;
pointer-events: none;
}
.mascot:hover .mascot-bubble { opacity: 1; }
/* PWA INSTALL */
.pwa-install {
position: fixed; bottom: 20px; left: 50%;
transform: translateX(-50%);
background: linear-gradient(135deg, var(--kt-blue), #004c99);
color: white; padding: 12px 20px; border-radius: 30px;
z-index: 998; display: flex; align-items: center; gap: 10px;
border: none; cursor: pointer; box-shadow: 0 4px 15px rgba(0,102,204,0.3);
}
.pwa-close {
background: rgba(255,255,255,0.2); border: none;
color: white; width: 24px; height: 24px; border-radius: 50%;
cursor: pointer; font-size: 14px;
}
@media (max-width: 768px) {
.topbar { padding: 10px 12px; }
.logo-text .tagline { display: none; }
.ai-panel { width: calc(100% - 30px); right: 15px; bottom: 180px; }
.fab { bottom: 20px; right: 20px; width: 56px; height: 56px; }
.ai-fab { bottom: 90px; right: 20px; }
.mascot { width: 80px; height: 80px; bottom: 15px; left: 15px; }
}
</style>
</head>
<body>
<!-- TOP BAR -->
<header class="topbar">
<div class="logo">
<span class="logo-emoji">🚀</span>
<div class="logo-text">
<div>VibeMap</div>
<div class="tagline">Карта студентов Vibe42</div>
</div>
</div>
<button class="stats-btn" id="open-stats">
<span>📊</span>
<span>Проектов</span>
<span class="stats-count" id="project-count">0</span>
</button>
<select class="city-selector" id="city">
<option value="astana">Астана</option>
<option value="almaty">Алматы</option>
<option value="shymkent">Шымкент</option>
<option value="karaganda">Караганда</option>
<option value="aktobe">Актобе</option>
<option value="taraz">Тараз</option>
<option value="pavlodar">Павлодар</option>
<option value="uk">Усть-Каменогорск</option>
<option value="semey">Семей</option>
<option value="kostanay">Костанай</option>
</select>
<button class="menu-btn" id="open-menu" title="Меню">☰</button>
</header>
<!-- FILTER BAR -->
<div class="filter-bar">
<button class="chip chip-all active">Все</button>
<button class="chip" data-cat="website">🌐 Сайты</button>
<button class="chip" data-cat="bot">🤖 Боты</button>
<button class="chip" data-cat="app">📱 Приложения</button>
<button class="chip" data-cat="presentation">📊 Презентации</button>
<button class="chip" data-cat="course">📚 Курсы</button>
</div>
<!-- MAP -->
<div id="map"></div>
<!-- FAB -->
<button class="fab" id="fab" title="Добавить проект">+</button>
<!-- AI FAB -->
<button class="ai-fab" id="ai-fab" title="ИИ-помощник">
<span>💬</span>
<span class="ai-fab-pulse"></span>
</button>
<!-- AI PANEL -->
<div class="ai-panel" id="ai-panel" hidden>
<header class="ai-panel-head">
<div class="ai-head-info">
<span class="ai-head-avatar">🚀</span>
<div>
<div class="ai-head-name">Vibe42 Помощник</div>
<div class="ai-head-status"><span class="ai-status-dot"></span> Онлайн</div>
</div>
</div>
<button class="ai-panel-close" id="ai-panel-close">×</button>
</header>
<div class="ai-messages" id="ai-messages">
<div class="ai-msg ai">
👋 Привет! Я ИИ-помощник Vibe42. Спроси меня о чём угодно:
<br>• Как создать сайт?
<br>• Где найти проекты других?
<br>• Как опубликовать проект?
</div>
</div>
<div class="ai-suggestions" id="ai-suggestions">
<button class="ai-sug" data-sug="Как создать сайт?">Как создать сайт?</button>
<button class="ai-sug" data-sug="Где найти проекты?">Где найти проекты?</button>
<button class="ai-sug" data-sug="Как опубликовать?">Как опубликовать?</button>
</div>
<form class="ai-input-row" id="ai-form">
<input type="text" id="ai-input" placeholder="Спросите что угодно..." autocomplete="off">
<button type="submit">➤</button>
</form>
</div>
<!-- DRAWER -->
<div class="drawer" id="drawer" hidden>
<div class="drawer-backdrop" id="drawer-backdrop"></div>
<aside class="drawer-panel">
<div class="drawer-head">
<div class="drawer-logo">🚀 VibeMap</div>
<button class="drawer-close" id="drawer-close">×</button>
</div>
<nav class="drawer-nav">
<button data-nav="map">🗺️ Карта</button>
<button data-nav="projects">📁 Все проекты</button>
<button data-nav="stats">📊 Статистика</button>
<button data-nav="about">ℹ️ О платформе</button>
<button data-nav="install">📲 Установить</button>
<button data-nav="feedback">💬 Обратная связь</button>
</nav>
<div class="drawer-footer">
<div>VibeMap © 2025</div>
<div>Сделано с ❤️ для студентов Казахстана</div>
</div>
</aside>
</div>
<!-- PREVIEW BOTTOM SHEET -->
<div class="preview" id="preview">
<div class="preview-handle"></div>
<div class="preview-content">
<h3 class="preview-title" id="preview-title">Название проекта</h3>
<div class="preview-meta" id="preview-meta"></div>
<p class="preview-desc" id="preview-desc">Описание проекта</p>
<div class="preview-contact" id="preview-contact"></div>
<div class="preview-actions">
<button class="preview-btn primary" id="preview-open">Открыть проект</button>
<button class="preview-btn secondary" id="preview-close">Закрыть</button>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal" id="modal">
<div class="modal-content">
<h3 class="modal-title">➕ Добавить проект</h3>
<form id="project-form">
<div class="form-group">
<label>Название проекта</label>
<input type="text" name="title" required placeholder="Мой первый сайт">
</div>
<div class="form-group">
<label>Категория</label>
<select name="category">
<option value="website">🌐 Сайт</option>
<option value="bot">🤖 Telegram-бот</option>
<option value="app">📱 Приложение</option>
<option value="presentation">📊 Презентация</option>
<option value="course">📚 Курс</option>
</select>
</div>
<div class="form-group">
<label>Город</label>
<select name="city">
<option value="astana">Астана</option>
<option value="almaty">Алматы</option>
<option value="shymkent">Шымкент</option>
<option value="karaganda">Караганда</option>
<option value="aktobe">Актобе</option>
</select>
</div>
<div class="form-group">
<label>Ссылка на проект</label>
<input type="url" name="url" placeholder="https://pages.git.vibe42.kz/...">
</div>
<div class="form-group">
<label>Описание</label>
<textarea name="description" rows="4" placeholder="Расскажите о проекте..."></textarea>
</div>
<div class="form-group">
<label>Контакт (Telegram/Email)</label>
<input type="text" name="contact" placeholder="@username или email">
</div>
<div class="form-actions">
<button type="button" class="preview-btn secondary" id="modal-close">Отмена</button>
<button type="submit" class="preview-btn primary">Опубликовать</button>
</div>
</form>
</div>
</div>
<!-- TOAST -->
<div class="toast" id="toast"></div>
<!-- MASCOT -->
<div class="mascot" id="mascot" title="Помощник">
<div class="mascot-bubble" id="mascot-bubble">Нажми + чтобы добавить проект!</div>
<svg viewBox="0 0 100 100">
<defs>
<radialGradient id="mg" cx="0.4" cy="0.4" r="0.7">
<stop offset="0%" stop-color="#0080ff"/>
<stop offset="100%" stop-color="#0066CC"/>
</radialGradient>
</defs>
<circle cx="50" cy="50" r="45" fill="url(#mg)"/>
<circle cx="35" cy="42" r="8" fill="white"/>
<circle cx="65" cy="42" r="8" fill="white"/>
<circle cx="35" cy="42" r="4" fill="#1a202c"/>
<circle cx="65" cy="42" r="4" fill="#1a202c"/>
<circle cx="37" cy="40" r="2" fill="white"/>
<circle cx="67" cy="40" r="2" fill="white"/>
<ellipse cx="50" cy="60" rx="12" ry="8" fill="#fff5e2"/>
<path d="M45 58 Q50 63 55 58" stroke="#1a202c" stroke-width="2" fill="none" stroke-linecap="round"/>
<circle cx="25" cy="55" r="5" fill="#ff6b6b" opacity="0.4"/>
<circle cx="75" cy="55" r="5" fill="#ff6b6b" opacity="0.4"/>
</svg>
</div>
<!-- PWA INSTALL -->
<button class="pwa-install" id="pwa-install" hidden>
<span class="pwa-icon">📲</span>
<span class="pwa-text">Установить как приложение</span>
<span class="pwa-close" id="pwa-install-close">×</span>
</button>
<!-- Leaflet -->
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
// ДАННЫЕ ПРОЕКТОВ (мок-данные для примера)
const PROJECTS = [
{ id: 1, title: "Сайт-визитка", category: "website", city: "astana", url: "https://pages.git.vibe42.kz/Medet/site1", description: "Мой первый сайт на Vibe42", contact: "@medet" },
{ id: 2, title: "Telegram-бот", category: "bot", city: "almaty", url: "https://t.me/mybot", description: "Бот для записи на курсы", contact: "@user123" },
{ id: 3, title: "Презентация", category: "presentation", city: "astana", url: "#", description: "Презентация по вайбкодингу", contact: "student@mail.kz" },
{ id: 4, title: "Интернет-магазин", category: "app", city: "shymkent", url: "#", description: "Прототип магазина одежды", contact: "@shop" },
{ id: 5, title: "Курс по HTML", category: "course", city: "karaganda", url: "#", description: "Бесплатный курс для начинающих", contact: "@teacher" },
];
const CITIES = {
astana: { lat: 51.1694, lng: 71.4491, name: "Астана" },
almaty: { lat: 43.2220, lng: 76.8512, name: "Алматы" },
shymkent: { lat: 42.3000, lng: 69.6000, name: "Шымкент" },
karaganda: { lat: 49.8047, lng: 73.1094, name: "Караганда" },
aktobe: { lat: 50.2839, lng: 57.1670, name: "Актобе" },
taraz: { lat: 42.9000, lng: 71.3667, name: "Тараз" },
pavlodar: { lat: 52.2833, lng: 76.9667, name: "Павлодар" },
uk: { lat: 49.9667, lng: 82.6000, name: "Усть-Каменогорск" },
semey: { lat: 50.4111, lng: 80.2275, name: "Семей" },
kostanay: { lat: 53.2144, lng: 63.6246, name: "Костанай" }
};
// Инициализация карты
const map = L.map('map', { zoomControl: false }).setView([49.8, 71.5], 5);
L.control.zoom({ position: 'bottomright' }).addTo(map);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
// Маркеры
const markers = [];
function addMarkers(filter = 'all') {
markers.forEach(m => map.removeLayer(m));
markers.length = 0;
PROJECTS.forEach(p => {
if (filter !== 'all' && p.category !== filter) return;
const city = CITIES[p.city];
if (!city) return;
const marker = L.marker([city.lat, city.lng]).addTo(map);
marker.bindPopup(`<b>${p.title}</b><br>${p.description}`);
marker.on('click', () => showPreview(p));
markers.push(marker);
});
document.getElementById('project-count').textContent = markers.length;
}
addMarkers();
// Фильтры
document.querySelectorAll('.chip').forEach(chip => {
chip.addEventListener('click', () => {
document.querySelectorAll('.chip').forEach(c => c.classList.remove('active'));
chip.classList.add('active');
const filter = chip.dataset.cat || 'all';
addMarkers(filter);
});
});
// Preview
const preview = document.getElementById('preview');
function showPreview(project) {
document.getElementById('preview-title').textContent = project.title;
document.getElementById('preview-meta').innerHTML = `
<span class="preview-tag">${project.category}</span>
<span class="preview-tag">${CITIES[project.city]?.name}</span>
`;
document.getElementById('preview-desc').textContent = project.description;
document.getElementById('preview-contact').textContent = `Контакт: ${project.contact}`;
document.getElementById('preview-open').onclick = () => window.open(project.url, '_blank');
preview.classList.add('open');
}
document.getElementById('preview-close').onclick = () => preview.classList.remove('open');
// Modal
const modal = document.getElementById('modal');
document.getElementById('fab').onclick = () => modal.classList.add('open');
document.getElementById('modal-close').onclick = () => modal.classList.remove('open');
document.getElementById('project-form').onsubmit = (e) => {
e.preventDefault();
showToast('✅ Проект опубликован!');
modal.classList.remove('open');
};
// Drawer
const drawer = document.getElementById('drawer');
document.getElementById('open-menu').onclick = () => drawer.hidden = false;
document.getElementById('drawer-close').onclick = () => drawer.hidden = true;
document.getElementById('drawer-backdrop').onclick = () => drawer.hidden = true;
// AI Panel
const aiPanel = document.getElementById('ai-panel');
document.getElementById('ai-fab').onclick = () => aiPanel.hidden = false;
document.getElementById('ai-panel-close').onclick = () => aiPanel.hidden = true;
const aiMessages = document.getElementById('ai-messages');
const aiForm = document.getElementById('ai-form');
const aiInput = document.getElementById('ai-input');
aiForm.onsubmit = (e) => {
e.preventDefault();
const msg = aiInput.value.trim();
if (!msg) return;
aiMessages.innerHTML += `<div class="ai-msg user">${msg}</div>`;
aiInput.value = '';
setTimeout(() => {
const responses = {
'Как создать сайт?': '📝 Открой Vibe42, создай index.html в папке проекта. Используй design.md как основу!',
'Где найти проекты?': '🗺️ Смотри на карту! Фильтры сверху помогут найти нужную категорию.',
'Как опубликовать?': '🚀 Нажми кнопку "Опубликовать" в интерфейсе Vibe42. Ссылка будет: pages.git.vibe42.kz/...'
};
const answer = responses[msg] || 'Спасибо за вопрос! Я учусь. Попробуй спросить что-то из предложенных вариантов.';
aiMessages.innerHTML += `<div class="ai-msg ai">${answer}</div>`;
aiMessages.scrollTop = aiMessages.scrollHeight;
}, 500);
};
document.querySelectorAll('.ai-sug').forEach(btn => {
btn.onclick = () => {
aiInput.value = btn.dataset.sug;
aiForm.requestSubmit();
};
});
// Mascot
const mascot = document.getElementById('mascot');
const mascotBubble = document.getElementById('mascot-bubble');
let clickCount = 0;
mascot.onclick = () => {
clickCount++;
const messages = ['Нажми + чтобы добавить проект!', 'Исследуй карту!', 'Vibe42 — это круто! 🚀', 'Создавай, публикуй, вдохновляй!'];
mascotBubble.textContent = messages[clickCount % messages.length];
};
// Toast
function showToast(msg) {
const toast = document.getElementById('toast');
toast.textContent = msg;
toast.classList.add('show');
setTimeout(() => toast.classList.remove('show'), 3000);
}
// PWA Install
const installBtn = document.getElementById('pwa-install');
let deferredPrompt = null;
window.addEventListener('beforeinstallprompt', (e) => {
e.preventDefault();
deferredPrompt = e;
installBtn.hidden = false;
});
installBtn.onclick = async () => {
if (deferredPrompt) {
deferredPrompt.prompt();
await deferredPrompt.userChoice;
deferredPrompt = null;
installBtn.hidden = true;
}
};
document.getElementById('pwa-install-close').onclick = () => {
installBtn.hidden = true;
};
// City selector
document.getElementById('city').onchange = (e) => {
const city = CITIES[e.target.value];
if (city) map.flyTo([city.lat, city.lng], 11);
};
// Stats button
document.getElementById('open-stats').onclick = () => {
showToast(`📊 Всего проектов: ${PROJECTS.length}`);
};
</script>
</body>
</html>