This repository has been archived on 2026-11-14. You can view files and clone it, but cannot push or open issues or pull requests.
gitea-pages/index.html
Telecom 78ba4afdf5 Thinner section divider (1px var(--border) vs 2px text)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 22:07:53 +05:00

3123 lines
109 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="ru">
<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&family=Playfair+Display:ital,wght@0,400;0,700;0,900;1,400&family=Lora:ital,wght@0,400;0,500;1,400&display=swap" rel="stylesheet">
<script>
// Apply theme ASAP to avoid flash of wrong theme. Runs before <body> parses.
(function() {
try {
const saved = localStorage.getItem('vibe.theme');
if (saved === 'dark' || saved === 'light') {
document.documentElement.setAttribute('data-theme', saved);
}
} catch (e) {}
})();
</script>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
/* Brand */
--blue: #0066cc;
--blue-hover: #0055b3;
--blue-link: #0058b3;
/* Surfaces */
--page-bg: #fff;
--bg: #efefef; /* nav / main background */
--surface: #fff; /* cards */
--card-neutral: #e3e3e3;
/* Text */
--text: #000;
--text-on-blue: #fff;
--text-65: rgba(0,0,0,0.65);
--text-55: rgba(0,0,0,0.55);
--text-45: rgba(0,0,0,0.45);
/* Lines + hovers */
--border: rgba(0,0,0,0.08);
--border-strong: rgba(0,0,0,0.12);
--hover: rgba(0,0,0,0.06);
--hover-light: rgba(0,0,0,0.03);
/* Status */
--positive: #059669;
--positive-bg: #dcfce7;
--negative: #dc2626;
/* Decorative */
--dot-color: rgba(0,0,0,0.05);
--code-bg: #1a1a1a; /* code blocks stay dark in both themes */
--code-text: #e5e5e5;
/* Misc */
--modal-scrim: rgba(0,0,0,0.4);
--shadow-card: 0 1px 2px rgba(0,0,0,0.04), 0 8px 24px rgba(0,0,0,0.06);
}
/* Dark theme — applied via [data-theme="dark"] OR auto when no explicit preference */
[data-theme="dark"] {
--page-bg: #16171c;
--bg: #1c1d22;
--surface: #212229;
--card-neutral: #2a2c33;
--text: #e8e8e8;
--text-65: rgba(255,255,255,0.72);
--text-55: rgba(255,255,255,0.55);
--text-45: rgba(255,255,255,0.40);
--border: rgba(255,255,255,0.08);
--border-strong: rgba(255,255,255,0.14);
--hover: rgba(255,255,255,0.06);
--hover-light: rgba(255,255,255,0.03);
--blue: #3b82f6;
--blue-hover: #60a5fa;
--blue-link: #60a5fa;
--positive: #34d399;
--positive-bg: rgba(52,211,153,0.15);
--negative: #f87171;
--dot-color: rgba(255,255,255,0.05);
--modal-scrim: rgba(0,0,0,0.65);
--shadow-card: 0 1px 2px rgba(0,0,0,0.4), 0 8px 24px rgba(0,0,0,0.3);
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--page-bg: #16171c;
--bg: #1c1d22;
--surface: #212229;
--card-neutral: #2a2c33;
--text: #e8e8e8;
--text-65: rgba(255,255,255,0.72);
--text-55: rgba(255,255,255,0.55);
--text-45: rgba(255,255,255,0.40);
--border: rgba(255,255,255,0.08);
--border-strong: rgba(255,255,255,0.14);
--hover: rgba(255,255,255,0.06);
--hover-light: rgba(255,255,255,0.03);
--blue: #3b82f6;
--blue-hover: #60a5fa;
--blue-link: #60a5fa;
--positive: #34d399;
--positive-bg: rgba(52,211,153,0.15);
--negative: #f87171;
--dot-color: rgba(255,255,255,0.05);
--modal-scrim: rgba(0,0,0,0.65);
--shadow-card: 0 1px 2px rgba(0,0,0,0.4), 0 8px 24px rgba(0,0,0,0.3);
}
}
html, body {
height: 100%;
margin: 0;
}
html {
scroll-behavior: smooth;
font-size: clamp(14.54px, 1.3vw, 22.74px); /* -20% от базы 28 */
overflow: hidden;
}
body {
overflow-y: scroll;
scroll-snap-type: y mandatory;
scroll-padding-top: 0;
}
/* PRESENTATION MODE: каждая секция = ровно 1 экран */
.hero,
section.section,
.dark-section {
scroll-snap-align: start;
scroll-snap-stop: normal;
min-height: 100dvh;
display: flex;
flex-direction: column;
justify-content: center;
padding-top: 4vh;
padding-bottom: 4vh;
box-sizing: border-box;
}
/* Усиливаем типографику для большого экрана */
.section-title { font-size: clamp(2rem, 3.6vw, 3.4rem) !important; line-height: 1.15; }
.hero-title { font-size: clamp(2.4rem, 5vw, 5rem) !important; line-height: 1.1; }
.section-body, .hero-sub { font-size: clamp(1rem, 1.3vw, 1.4rem) !important; }
.section-label { font-size: clamp(0.85rem, 1vw, 1.1rem) !important; }
.dark-title { font-size: clamp(2.2rem, 4vw, 4rem) !important; }
.dark-sub { font-size: clamp(1rem, 1.3vw, 1.4rem) !important; max-width: 600px; }
/* Контейнер шире на больших экранах */
@media (min-width: 1400px) {
.container { max-width: 1280px; }
nav { max-width: 1280px; }
}
/* Прячем sticky-нав в slide-режиме — мешает фиксации экранов */
nav { position: static !important; scroll-snap-align: none; }
/* ──────────────────────────────────────────────────────────
EDITORIAL / NYT REDESIGN — кремовый фон, serif, флэт
────────────────────────────────────────────────────────── */
:root {
--page-bg: #f5efe1; /* кремовая бумага */
--bg: #f5efe1;
--surface: #fbf7ec;
--card-neutral: #ece4d2;
--text: #1a1410; /* почти чёрный с тёплым оттенком */
--text-65: rgba(26,20,16,0.72);
--text-55: rgba(26,20,16,0.58);
--text-45: rgba(26,20,16,0.42);
--border: rgba(26,20,16,0.15);
--border-strong: rgba(26,20,16,0.28);
--hover: rgba(26,20,16,0.05);
--blue: #a8201a; /* журнальный красный вместо синего */
--blue-hover: #7e1813;
--blue-link: #a8201a;
}
[data-theme="dark"] {
--page-bg: #1a1714;
--bg: #1a1714;
--surface: #221d18;
--card-neutral: #2b251f;
--text: #f1e9d6;
--text-65: rgba(241,233,214,0.72);
--text-55: rgba(241,233,214,0.55);
--text-45: rgba(241,233,214,0.40);
--border: rgba(241,233,214,0.14);
--border-strong: rgba(241,233,214,0.28);
--hover: rgba(241,233,214,0.06);
--blue: #e07a72;
--blue-hover: #f1968e;
--blue-link: #e07a72;
}
body {
font-family: 'Lora', Georgia, serif !important;
background: var(--page-bg);
padding: 0;
}
.frame { gap: 0; min-height: 100dvh; display: block; }
/* Тонкая разделительная линия между слайдами */
.hero, section.section, .dark-section {
border-top: 1px solid var(--border) !important;
position: relative;
}
.hero { border-top: none !important; }
/* Шрифты заголовков */
.hero-title, .section-title, .dark-title,
.eq-box-title, .plan-step-title, .tool-name,
.create-card-title, .project-title, .gloss-term,
.prompt-title, .check-title, .modal-title,
.tg-text-title, .card-title, .ba-label {
font-family: 'Playfair Display', Georgia, serif !important;
font-weight: 400 !important;
letter-spacing: -0.01em !important;
}
/* HERO — журнальная обложка */
.hero {
background-image: none;
mask-image: none;
-webkit-mask-image: none;
text-align: center;
border-bottom: 1px solid var(--border);
}
.hero-title {
font-weight: 900 !important;
letter-spacing: -0.04em !important;
line-height: 0.95 !important;
}
.hero-title em, .section-title em {
font-style: italic !important;
text-decoration: none !important;
color: var(--blue);
font-weight: 400 !important;
}
.underline-blue {
text-decoration: none !important;
font-style: italic;
color: var(--blue);
}
/* Section label = SMALL CAPS */
.section-label {
font-family: 'Inter', sans-serif !important;
text-transform: uppercase !important;
letter-spacing: 0.18em !important;
font-weight: 500 !important;
color: var(--text-45) !important;
border-bottom: 1px solid var(--border);
padding-bottom: 8px;
margin-bottom: 24px;
display: inline-block;
}
/* Section разделители = тонкие линии, не блоки */
.section {
border-top: 1px solid var(--border) !important;
background: var(--page-bg);
}
/* Карточки — флэт, без скруглений и теней */
.card, .tool-card, .create-card, .plan-step, .gloss-item,
.project-card, .prompt-card, .eq-box, .check-card, .ba-card,
main, nav {
border-radius: 0 !important;
box-shadow: none !important;
background: transparent !important;
border: 1px solid var(--border) !important;
}
.create-card, .card { background: var(--card-neutral) !important; }
.eq-box.you { background: rgba(168,32,26,0.08) !important; border-color: var(--blue) !important; }
.eq-box.result { background: var(--surface) !important; }
/* CTA — журнальная кнопка с подчёркиванием */
.nav-cta, .btn-primary, .tg-cta, .dark-cta, .modal-submit {
border-radius: 0 !important;
background: var(--text) !important;
color: var(--page-bg) !important;
font-family: 'Inter', sans-serif !important;
font-weight: 500 !important;
text-transform: uppercase !important;
letter-spacing: 0.1em !important;
font-size: 0.85rem !important;
}
.nav-cta:hover, .btn-primary:hover, .tg-cta:hover, .modal-submit:hover {
background: var(--blue) !important;
}
/* Body-текст — serif */
.section-body, .hero-sub, .card-desc, .tool-role, .project-pain,
.plan-step-desc, .create-card-desc, .gloss-text, .check-list li,
.dark-sub, .modal-sub, .ba-row, .tg-text-body, .eq-box-desc, .prompt-context {
font-family: 'Lora', Georgia, serif !important;
font-style: italic;
}
/* Nav — журнальный header */
nav {
border: none !important;
border-bottom: 2px solid var(--text) !important;
background: var(--page-bg) !important;
border-radius: 0 !important;
margin: 0 !important;
max-width: 100% !important;
}
.nav-inner { max-width: 1280px; margin: 0 auto; padding: 0 32px; }
.logo-text {
font-family: 'Playfair Display', serif;
font-style: italic;
font-size: 1.2rem;
}
.logo-mark {
background: var(--blue) !important;
border-radius: 0 !important;
font-family: 'Inter', sans-serif;
}
.nav-link {
font-family: 'Inter', sans-serif;
text-transform: uppercase;
letter-spacing: 0.08em;
font-size: 0.75rem !important;
}
/* Drop-cap первое слово в hero (как в журналах) */
.hero-inner .badge {
font-family: 'Inter', sans-serif;
text-transform: uppercase;
letter-spacing: 0.2em;
border-top: 1px solid var(--text);
border-bottom: 1px solid var(--text);
padding: 6px 14px !important;
display: inline-block;
}
/* Slide-counter — номер страницы внизу */
.slide-counter {
position: fixed;
bottom: 18px;
right: 24px;
font-family: 'Playfair Display', serif;
font-style: italic;
font-size: 1rem;
color: var(--text-55);
z-index: 80;
pointer-events: none;
}
/* Project cards — карточки журнала */
.project-num {
font-family: 'Playfair Display', serif !important;
font-style: italic;
font-size: 1.5rem !important;
color: var(--blue) !important;
letter-spacing: 0 !important;
}
.project-title { font-size: 1.5rem !important; line-height: 1.2 !important; }
/* Глоссарий, инструменты — приподнимаем шрифт */
.gloss-term { font-size: 1.3rem !important; }
.tool-name { font-size: 1.2rem !important; }
.plan-step-title { font-size: 1.2rem !important; }
/* Убираем скругления QR-фрейма */
.tg-qr, .tg-block, .dark-section { border-radius: 0 !important; }
.dark-section {
background: var(--text) !important;
margin: 0 !important;
border-top: 1px solid var(--border);
}
.dark-title { color: var(--page-bg) !important; }
.dark-sub { color: rgba(255,255,255,0.7) !important; }
.dark-cta { background: var(--page-bg) !important; color: var(--text) !important; }
/* Hover убираем подъём — для флэт-журнала не подходит */
.tool-card:hover, .create-card:hover, .plan-step:hover,
.gloss-item:hover, .prompt-card:hover, .eq-box:hover,
.project-card:hover, .check-card:hover {
transform: none !important;
box-shadow: none !important;
border-color: var(--blue) !important;
}
/* Footer */
footer {
background: var(--text);
color: var(--page-bg);
border: none !important;
}
.footer-copy, .footer-link {
color: rgba(255,255,255,0.7) !important;
font-family: 'Inter', sans-serif;
text-transform: uppercase;
letter-spacing: 0.1em;
font-size: 0.7rem !important;
}
/* ── POLISH: shared hover transition for all card variants ── */
.tool-card, .create-card, .plan-step, .gloss-item, .project-card, .prompt-card, .eq-box {
transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease;
will-change: transform;
}
@media (hover: hover) {
.tool-card:hover, .create-card:hover, .plan-step:hover,
.gloss-item:hover, .prompt-card:hover, .eq-box:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-card);
}
/* project-card already has its own hover (border + ring) — strengthen with lift */
.project-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-card);
}
}
@media (prefers-reduced-motion: reduce) {
.tool-card, .create-card, .plan-step, .gloss-item, .project-card, .prompt-card, .eq-box {
transition: none;
}
.tool-card:hover, .create-card:hover, .plan-step:hover,
.gloss-item:hover, .project-card:hover, .prompt-card:hover, .eq-box:hover {
transform: none;
}
}
/* ── POLISH: scroll-triggered fade-in ── */
.reveal {
opacity: 0;
transform: translateY(8px);
transition: opacity 350ms ease, transform 350ms ease;
}
.reveal.in {
opacity: 1;
transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
.reveal { opacity: 1; transform: none; transition: none; }
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: var(--page-bg);
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 16px;
display: flex;
align-items: center;
position: sticky;
top: 12px;
z-index: 50;
/* match .container width so navbar aligns with section content below */
width: 100%;
max-width: 1024px;
margin: 0 auto;
}
@media (min-width: 640px) { nav { padding: 0 24px; } }
.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); }
/* ── AUTH (nav button + modal) ── */
.nav-cta-group { margin-left: auto; display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.theme-toggle {
width: 36px; height: 36px;
border: 1px solid var(--border);
background: transparent;
color: var(--text-65);
border-radius: 50%;
cursor: pointer;
display: inline-flex; align-items: center; justify-content: center;
flex-shrink: 0;
transition: background 150ms ease, color 150ms ease, border-color 150ms ease;
}
.theme-toggle:hover {
background: var(--hover);
color: var(--text);
border-color: var(--border-strong);
}
.theme-toggle svg { width: 16px; height: 16px; }
.theme-toggle .icon-moon { display: none; }
[data-theme="dark"] .theme-toggle .icon-sun { display: none; }
[data-theme="dark"] .theme-toggle .icon-moon { display: block; }
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) .theme-toggle .icon-sun { display: none; }
:root:not([data-theme="light"]) .theme-toggle .icon-moon { display: block; }
}
.nav-cdn-chip {
font-size: 0.75rem;
color: var(--text-55);
background: var(--hover);
padding: 4px 10px;
border-radius: 6px;
cursor: pointer;
user-select: none;
transition: background 150ms ease;
}
.nav-cdn-chip:hover { background: var(--hover-light); color: var(--negative); }
.nav-cdn-chip::after { content: ' ×'; opacity: 0.5; }
.modal-overlay {
position: fixed; inset: 0;
background: var(--modal-scrim);
display: none; align-items: center; justify-content: center;
z-index: 100; padding: 16px;
animation: fadeIn 150ms ease;
}
.modal-overlay.open { display: flex; }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.modal-card {
background: var(--surface);
border-radius: 16px;
padding: 32px 28px 28px;
max-width: 420px; width: 100%;
box-shadow: 0 20px 60px rgba(0,0,0,0.15);
position: relative;
animation: slideUp 200ms ease;
}
@keyframes slideUp { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
.modal-close {
position: absolute; top: 12px; right: 12px;
width: 32px; height: 32px;
background: transparent; border: none; cursor: pointer;
color: var(--text-45); font-size: 1.5rem; line-height: 1;
border-radius: 8px;
display: flex; align-items: center; justify-content: center;
transition: background 150ms ease, color 150ms ease;
}
.modal-close:hover { background: var(--hover); color: var(--text); }
.modal-title {
font-size: 1.25rem;
font-weight: 500;
letter-spacing: -0.01em;
color: var(--text);
margin-bottom: 6px;
}
.modal-sub {
font-size: 0.875rem;
color: var(--text-55);
margin-bottom: 20px;
line-height: 1.5;
}
.modal-input {
width: 100%;
padding: 12px 14px;
border: 1px solid var(--border);
border-radius: 10px;
font-size: 0.9375rem;
font-family: inherit;
color: var(--text);
background: var(--surface);
transition: border-color 150ms ease;
}
.modal-input:focus {
outline: none;
border-color: var(--blue);
}
.modal-hint {
margin-top: 10px;
font-size: 0.8125rem;
color: var(--text-55);
min-height: 1.2em;
font-variant-numeric: tabular-nums;
}
.modal-hint code {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
background: var(--hover);
padding: 1px 6px;
border-radius: 4px;
font-size: 0.8125rem;
color: var(--text);
}
.modal-hint.error { color: var(--negative); }
.modal-actions {
margin-top: 24px;
display: flex;
gap: 10px;
}
.modal-submit {
flex: 1;
background: var(--blue);
color: #fff;
font-size: 0.9375rem;
font-weight: 500;
padding: 12px 20px;
border: none;
border-radius: 10px;
cursor: pointer;
font-family: inherit;
transition: background 150ms ease;
}
.modal-submit:hover { background: var(--blue-hover); }
.modal-submit:disabled { opacity: 0.5; cursor: not-allowed; }
/* ── OPTIONAL BADGE on tool card ── */
.tool-card.optional { position: relative; opacity: 0.92; }
.tool-card .opt-badge {
position: absolute; top: 14px; right: 14px;
font-size: 0.6875rem; font-weight: 500;
padding: 2px 8px; border-radius: 6px;
background: rgba(0,0,0,0.06); color: var(--text-55);
letter-spacing: 0.02em;
}
/* ── ACTION PLAN ── */
.plan-flow {
margin-top: 40px;
display: grid;
gap: 12px;
grid-template-columns: 1fr;
}
@media (min-width: 768px) {
.plan-flow { grid-template-columns: repeat(4, 1fr); align-items: stretch; }
}
.plan-step {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 20px 18px;
display: flex;
flex-direction: column;
gap: 8px;
position: relative;
}
.plan-step-num {
font-size: 0.6875rem; font-weight: 500;
letter-spacing: 0.06em;
color: var(--blue);
text-transform: uppercase;
}
.plan-step-title {
font-size: 0.9375rem; font-weight: 500;
color: var(--text); line-height: 1.3;
}
.plan-step-desc {
font-size: 0.8125rem;
color: var(--text-55); line-height: 1.5;
flex: 1;
}
.plan-step code {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
background: var(--hover); padding: 1px 5px;
border-radius: 4px; font-size: 0.75rem; color: var(--text);
}
/* ── CREATE CARDS ── */
.create-cards {
margin-top: 40px;
display: grid;
gap: 12px;
grid-template-columns: 1fr;
}
@media (min-width: 640px) { .create-cards { grid-template-columns: repeat(3, 1fr); } }
.create-card {
background: var(--card-neutral);
border-radius: 12px;
padding: 28px 24px;
display: flex;
flex-direction: column;
gap: 12px;
}
.create-card-icon {
width: 44px; height: 44px;
background: var(--surface);
border-radius: 10px;
display: flex; align-items: center; justify-content: center;
color: var(--blue);
}
.create-card-icon svg { width: 22px; height: 22px; }
.create-card-title {
font-size: 0.9375rem; font-weight: 500;
color: var(--text);
}
.create-card-desc {
font-size: 0.8125rem;
color: var(--text-55); line-height: 1.5;
}
.create-card-example {
font-size: 0.75rem;
color: var(--text-45);
font-style: italic;
margin-top: auto;
}
/* ── ENTERPRISE EQUATION (vibe + hard = enterprise) ── */
.eq-flow {
margin-top: 40px;
display: grid;
gap: 16px;
grid-template-columns: 1fr;
align-items: stretch;
}
@media (min-width: 768px) {
.eq-flow {
grid-template-columns: 1fr auto 1fr auto 1fr;
align-items: stretch;
}
}
.eq-box {
background: var(--card-neutral);
border-radius: 12px;
padding: 24px;
display: flex;
flex-direction: column;
gap: 8px;
}
.eq-box.you {
background: rgba(0,102,204,0.08);
border: 1px solid rgba(0,102,204,0.2);
}
.eq-box.result {
background: var(--surface);
border: 1px solid var(--border);
}
.eq-box-label {
font-size: 0.6875rem;
font-weight: 500;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--text-45);
}
.eq-box.you .eq-box-label { color: var(--blue); }
.eq-box-title {
font-size: 1rem;
font-weight: 500;
color: var(--text);
line-height: 1.3;
}
.eq-box-desc {
font-size: 0.8125rem;
color: var(--text-55);
line-height: 1.5;
}
.eq-op {
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
font-weight: 500;
color: var(--text-45);
padding: 8px 0;
}
@media (min-width: 768px) { .eq-op { padding: 0 4px; } }
.eq-note {
margin-top: 32px;
background: var(--surface);
border: 1px solid var(--border);
border-left: 3px solid var(--blue);
border-radius: 8px;
padding: 20px 24px;
display: grid;
gap: 16px;
}
@media (min-width: 640px) { .eq-note { grid-template-columns: 1fr 1fr; } }
.eq-note-item {
display: flex;
gap: 12px;
align-items: flex-start;
}
.eq-note-icon {
width: 28px; height: 28px;
flex-shrink: 0;
color: var(--blue);
background: rgba(0,102,204,0.08);
border-radius: 6px;
display: flex; align-items: center; justify-content: center;
}
.eq-note-icon svg { width: 16px; height: 16px; }
.eq-note-text {
font-size: 0.8125rem;
color: var(--text-65);
line-height: 1.5;
}
.eq-note-text strong { color: var(--text); font-weight: 500; }
/* ── PROMPT CHEATSHEET ── */
.prompts {
margin-top: 40px;
display: flex;
flex-direction: column;
gap: 12px;
}
.prompt-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 20px 24px;
display: flex;
flex-direction: column;
gap: 12px;
}
@media (max-width: 640px) { .prompt-card { padding: 16px 18px; } }
.prompt-head {
display: flex;
align-items: center;
gap: 10px;
flex-wrap: wrap;
}
.prompt-step {
font-size: 0.6875rem;
font-weight: 500;
letter-spacing: 0.06em;
text-transform: uppercase;
padding: 3px 8px;
border-radius: 6px;
background: rgba(0,102,204,0.08);
color: var(--blue);
flex-shrink: 0;
}
.prompt-step.bonus {
background: rgba(0,0,0,0.06);
color: var(--text-65);
}
.prompt-title {
font-size: 0.9375rem;
font-weight: 500;
color: var(--text);
}
.prompt-context {
font-size: 0.8125rem;
color: var(--text-55);
line-height: 1.5;
}
.prompt-box {
position: relative;
background: #1a1a1a;
border-radius: 8px;
padding: 16px 56px 16px 18px;
}
.prompt-box pre {
margin: 0;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 0.8125rem;
line-height: 1.55;
color: #e5e5e5;
white-space: pre-wrap;
word-wrap: break-word;
}
.prompt-copy {
position: absolute;
top: 12px;
right: 12px;
border: none;
background: rgba(255,255,255,0.08);
color: #e5e5e5;
font-size: 0.75rem;
font-family: inherit;
padding: 6px 10px;
border-radius: 6px;
cursor: pointer;
display: inline-flex;
align-items: center;
gap: 5px;
transition: background 150ms ease, color 150ms ease;
}
.prompt-copy:hover { background: rgba(255,255,255,0.15); }
.prompt-copy.copied { background: var(--positive-bg); color: var(--positive); }
.prompt-copy svg { width: 12px; height: 12px; }
/* ── TELEGRAM ── */
.tg-block {
margin-top: 40px;
background: var(--card-neutral);
border-radius: 16px;
padding: 32px 28px;
display: grid;
gap: 28px;
grid-template-columns: 1fr;
align-items: center;
}
@media (min-width: 640px) {
.tg-block { grid-template-columns: auto 1fr; padding: 40px; gap: 36px; }
}
.tg-qr {
background: #fff;
padding: 12px;
border-radius: 12px;
width: 180px; height: 180px;
display: flex; align-items: center; justify-content: center;
flex-shrink: 0;
}
.tg-qr svg { width: 100%; height: 100%; display: block; }
.tg-text-title {
font-size: 1.25rem; font-weight: 500;
letter-spacing: -0.01em;
color: var(--text);
margin-bottom: 8px;
}
.tg-text-body {
font-size: 0.875rem;
color: var(--text-55); line-height: 1.5;
margin-bottom: 16px;
}
.tg-cta {
display: inline-flex; align-items: center; gap: 6px;
background: var(--blue); color: #fff;
font-size: 0.875rem; font-weight: 500;
padding: 10px 18px; border-radius: 8px;
text-decoration: none;
transition: background 150ms ease;
}
.tg-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;
/* subtle dot-grid backdrop, fades to clean at the edges */
background-image:
radial-gradient(ellipse at center, var(--dot-color) 1px, transparent 1.5px);
background-size: 22px 22px;
background-position: center top;
mask-image: radial-gradient(ellipse at center, black 40%, transparent 80%);
-webkit-mask-image: radial-gradient(ellipse at center, black 40%, transparent 80%);
}
@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: var(--surface);
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: var(--surface);
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 (horizontal slider, full-width below title) ── */
.glossary-slider {
margin-top: 40px;
width: 100%;
min-width: 0;
}
.glossary {
display: flex;
gap: 12px;
overflow-x: auto;
overflow-y: hidden;
scroll-snap-type: x mandatory;
-webkit-overflow-scrolling: touch;
padding-bottom: 8px;
width: 100%;
min-width: 0;
max-width: 100%;
}
.glossary::-webkit-scrollbar { height: 6px; }
.glossary::-webkit-scrollbar-track { background: transparent; }
.glossary::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
.gloss-item {
border: 1px solid var(--border);
border-radius: 12px;
background: var(--surface);
display: flex;
flex-direction: column;
scroll-snap-align: start;
min-height: 200px;
flex: 0 0 360px;
min-width: 360px;
}
@media (max-width: 640px) {
.gloss-item { flex-basis: 88vw; min-width: 88vw; min-height: 180px; }
}
.gloss-nav {
margin-top: 20px;
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
}
.gloss-nav-btn {
width: 40px; height: 40px;
border: 1px solid var(--border);
background: var(--surface);
border-radius: 50%;
display: flex; align-items: center; justify-content: center;
cursor: pointer;
transition: background 150ms ease, opacity 150ms ease, border-color 150ms ease;
color: var(--text-65);
}
.gloss-nav-btn:hover:not(:disabled) {
background: var(--blue); color: #fff; border-color: var(--blue);
}
.gloss-nav-btn:disabled { opacity: 0.3; cursor: not-allowed; }
.gloss-nav-btn svg { width: 18px; height: 18px; }
.gloss-counter {
font-size: 0.8125rem;
color: var(--text-55);
font-variant-numeric: tabular-nums;
min-width: 64px;
text-align: center;
}
.gloss-trigger {
width: 100%;
padding: 16px 20px 8px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
background: transparent;
border: none;
text-align: left;
cursor: default;
}
.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 { display: none; }
.gloss-body { display: block; }
.gloss-inner { overflow: visible; }
.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;
}
.gloss-text code {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
background: var(--hover);
padding: 1px 5px;
border-radius: 4px;
font-size: 0.8125rem;
color: var(--text);
}
/* ── 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: var(--surface);
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; }
/* ── CHECKLIST ── */
.checklist-grid {
margin-top: 40px;
display: grid;
gap: 12px;
}
@media (min-width: 768px) { .checklist-grid { grid-template-columns: repeat(2, 1fr); } }
.check-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 24px;
display: flex;
flex-direction: column;
gap: 12px;
transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease;
}
@media (hover: hover) {
.check-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-card); }
}
.check-card.featured { border-color: var(--blue); }
.check-card.muted { background: var(--card-neutral); }
.check-card-head {
display: flex;
align-items: center;
gap: 10px;
}
.check-badge {
font-size: 0.6875rem;
font-weight: 500;
padding: 3px 8px;
border-radius: 6px;
background: var(--hover);
color: var(--text-65);
letter-spacing: 0.02em;
text-transform: uppercase;
}
.check-badge.required { background: var(--blue); color: #fff; }
.check-badge.optional { background: var(--card-neutral); color: var(--text-65); }
.check-badge.warn { background: var(--positive-bg); color: var(--positive); }
.check-title {
font-size: 1rem;
font-weight: 500;
color: var(--text);
}
.check-list {
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: column;
gap: 10px;
}
.check-list li {
display: flex;
align-items: flex-start;
gap: 10px;
font-size: 0.875rem;
color: var(--text-65);
line-height: 1.5;
}
.check-list li::before {
content: '';
flex-shrink: 0;
width: 16px;
height: 16px;
margin-top: 3px;
border: 1.5px solid var(--border-strong);
border-radius: 4px;
background: var(--surface);
}
.check-list.dont li::before {
border: none;
background: none;
width: 16px;
height: 16px;
margin-top: 1px;
color: var(--negative);
content: '✕';
display: flex;
align-items: center;
justify-content: center;
font-size: 0.875rem;
font-weight: 600;
}
.check-list a {
color: var(--blue);
text-decoration: none;
font-weight: 500;
}
.check-list a:hover { color: var(--blue-hover); text-decoration: underline; }
.check-note {
font-size: 0.75rem;
color: var(--text-55);
font-style: italic;
line-height: 1.5;
}
/* Side announcement */
.vibe-announce {
position: fixed;
left: 20px;
top: 50%;
transform: translateY(-50%);
z-index: 100;
background: var(--blue);
color: var(--text-on-blue);
padding: 16px 18px;
border-radius: 12px;
box-shadow: var(--shadow-card);
max-width: 200px;
font-size: 0.875rem;
line-height: 1.5;
}
.vibe-announce-title {
font-weight: 700;
font-size: 1rem;
margin-bottom: 6px;
letter-spacing: 0.06em;
}
.vibe-announce-pass {
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 1.05rem;
font-weight: 600;
background: rgba(255,255,255,0.18);
padding: 6px 10px;
border-radius: 6px;
display: inline-block;
margin-top: 4px;
letter-spacing: 0.04em;
}
@media (max-width: 1200px) {
.vibe-announce { display: none; }
}
</style>
</head>
<body>
<div class="slide-counter" id="slide-counter" aria-hidden="true">— 1 —</div>
<aside class="vibe-announce" aria-label="Объявление">
<div class="vibe-announce-title">Wi-Fi и пароль</div>
<div>VIBECODING</div>
<div class="vibe-announce-pass">123456789</div>
</aside>
<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="#checklist" class="nav-link">Чек-лист</a>
<a href="#plan" class="nav-link">Action Plan</a>
<a href="#create" class="nav-link">Что создать</a>
<a href="#enterprise" class="nav-link">Зачем</a>
<a href="#prompts" class="nav-link">Промпты</a>
<a href="#telegram" class="nav-link">Сообщество</a>
</div>
<div class="nav-cta-group">
<button id="theme-toggle" type="button" class="theme-toggle" aria-label="Переключить тему">
<svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41"/></svg>
<svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
</button>
<span id="nav-cdn" class="nav-cdn-chip" hidden title="Выйти"></span>
<button id="nav-auth" type="button" class="nav-cta">Войти</button>
</div>
</div>
</nav>
<!-- AUTH MODAL -->
<div id="auth-modal" class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="auth-title">
<div class="modal-card">
<button class="modal-close" id="modal-close" aria-label="Закрыть">×</button>
<div class="modal-title" id="auth-title">Открыть мой VS Code</div>
<div class="modal-sub">Введи свою фамилию, чтобы создать папку проекта.</div>
<input id="auth-input" class="modal-input" type="text" placeholder="Zhuldyz Inat или Zhuldyz" autocomplete="off" autocapitalize="none" spellcheck="false" />
<div id="auth-hint" class="modal-hint">&nbsp;</div>
<div class="modal-actions">
<button id="auth-submit" type="button" class="modal-submit" disabled>Открыть VS Code →</button>
</div>
</div>
</div>
<!-- MAIN -->
<main id="top">
<!-- HERO -->
<section class="hero">
<div class="container hero-inner">
<span class="badge">Казахтелеком · Движение инноваторов</span>
<h1 class="hero-title">
<span class="underline-blue">Vibe Coding</span><br>
для сотрудников Казахтелеком
</h1>
<p class="hero-sub">
Опиши свою боль словами — Claude напишет код. Запусти прямо здесь, в браузере.<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: TELEGRAM COMMUNITY -->
<section class="section" id="telegram">
<div class="container">
<div class="section-label">Сообщество</div>
<h2 class="section-title">P2P-формат.<br><em>Не лекция — движение.</em></h2>
<p class="section-body">
Это не курс «сверху вниз». Это сообщество сотрудников КТ, которые учатся <strong>друг у друга</strong>: показывают свои проекты, спрашивают, помогают, делятся находками. Заходи — там уже несколько групп инноваторов.
</p>
<div class="tg-block">
<div class="tg-qr" aria-label="QR-код для Telegram-группы">
<svg viewBox="0 0 264 264" version="1.1" width="264" height="264" class="segno"><g transform="scale(8)"><path fill="#fff" d="M0 0h33v33h-33z"/><path class="qrline" stroke="#000" d="M2 2.5h7m1 0h1m2 0h2m2 0h1m1 0h1m1 0h1m2 0h7m-29 1h1m5 0h1m1 0h3m1 0h1m1 0h2m1 0h4m1 0h1m5 0h1m-29 1h1m1 0h3m1 0h1m1 0h2m2 0h5m5 0h1m1 0h3m1 0h1m-29 1h1m1 0h3m1 0h1m1 0h1m2 0h2m1 0h1m3 0h1m3 0h1m1 0h3m1 0h1m-29 1h1m1 0h3m1 0h1m1 0h1m5 0h2m1 0h3m2 0h1m1 0h3m1 0h1m-29 1h1m5 0h1m2 0h1m4 0h1m1 0h1m2 0h1m2 0h1m5 0h1m-29 1h7m1 0h1m1 0h1m1 0h1m1 0h1m1 0h1m1 0h1m1 0h1m1 0h7m-21 1h1m1 0h3m2 0h1m1 0h1m2 0h1m-20 1h2m1 0h1m1 0h2m3 0h2m1 0h1m1 0h1m2 0h2m1 0h1m1 0h5m-27 1h3m2 0h1m1 0h6m1 0h2m1 0h2m1 0h2m1 0h2m1 0h1m-28 1h2m2 0h4m4 0h1m1 0h2m9 0h3m-27 1h4m1 0h1m1 0h3m2 0h6m1 0h2m4 0h1m-27 1h1m2 0h1m1 0h1m1 0h1m1 0h2m2 0h1m2 0h4m1 0h2m-24 1h3m1 0h1m2 0h1m7 0h2m4 0h3m4 0h1m-27 1h3m1 0h1m1 0h3m2 0h1m2 0h3m1 0h1m1 0h2m2 0h1m1 0h1m-29 1h2m1 0h2m4 0h1m1 0h2m2 0h1m1 0h1m1 0h3m1 0h1m-21 1h1m2 0h3m1 0h1m1 0h2m1 0h3m3 0h3m1 0h1m-24 1h1m2 0h1m1 0h1m1 0h1m3 0h1m1 0h2m3 0h4m3 0h2m-29 1h1m2 0h1m1 0h2m4 0h1m1 0h1m1 0h3m2 0h2m1 0h2m1 0h3m-28 1h1m1 0h1m1 0h1m4 0h1m3 0h1m1 0h4m2 0h1m1 0h1m2 0h2m-29 1h1m1 0h1m2 0h2m1 0h1m1 0h2m1 0h1m1 0h1m1 0h9m1 0h1m-20 1h2m4 0h3m3 0h1m3 0h1m2 0h1m-28 1h7m1 0h1m1 0h1m2 0h1m4 0h3m1 0h1m1 0h1m1 0h3m-29 1h1m5 0h1m2 0h1m1 0h2m1 0h2m1 0h2m1 0h1m3 0h1m-25 1h1m1 0h3m1 0h1m1 0h1m1 0h1m3 0h3m1 0h1m1 0h6m1 0h2m-29 1h1m1 0h3m1 0h1m3 0h2m1 0h3m4 0h2m2 0h1m-25 1h1m1 0h3m1 0h1m1 0h1m3 0h3m1 0h4m3 0h2m1 0h1m1 0h1m-29 1h1m5 0h1m1 0h1m1 0h1m3 0h2m1 0h2m1 0h1m1 0h2m1 0h1m1 0h1m-28 1h7m2 0h1m2 0h3m2 0h2m1 0h1m1 0h1m1 0h1m2 0h2"/></g></svg>
</div>
<div>
<div class="tg-text-title">Telegram-группа инноваторов</div>
<div class="tg-text-body">
Отсканируй QR или жми на кнопку. Можно показать свой проект, попросить помощь, прислать идею.
</div>
<a href="https://t.me/+9UI8dt-60l8yMzJi" target="_blank" rel="noopener" class="tg-cta">
Открыть в Telegram →
</a>
</div>
</div>
</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 YOU -->
<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: REGISTER AI ACCOUNTS -->
<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">
До воркшопа создай бесплатные аккаунты в Claude, ChatGPT и Gemini. Можно через Google — 1 минута на каждый. Не нужно платить и не нужно API-ключи.
</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</div>
<div class="tool-role">
Anthropic. Лучший для длинных описаний и для генерации кода. Основной инструмент на воркшопе.
</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">
<circle cx="12" cy="12" r="9"/>
<path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/>
</svg>
</div>
<div class="tool-name">ChatGPT</div>
<div class="tool-role">
OpenAI. Универсал. Хорош для брейншторма и быстрых ответов. Запасной канал, если Claude занят.
</div>
<a href="https://chatgpt.com" target="_blank" rel="noopener" class="tool-link">
chatgpt.com
<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">
<path d="M12 2l2.5 7.5L22 12l-7.5 2.5L12 22l-2.5-7.5L2 12l7.5-2.5z"/>
</svg>
</div>
<div class="tool-name">Gemini</div>
<div class="tool-role">
Google. Силён в работе с Google-документами и таблицами. Удобен, если данные уже в Google Drive.
</div>
<a href="https://gemini.google.com" target="_blank" rel="noopener" class="tool-link">
gemini.google.com
<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>
</div>
</section>
<!-- SECTION 3.5: CHECKLIST -->
<section class="section" id="checklist">
<div class="container">
<div class="section-label">Чек-лист участника</div>
<h2 class="section-title">Что подготовить<br><em>до воркшопа</em></h2>
<p class="section-body">
Никаких установок, API-ключей и корпоративных регистраций не нужно. Всё работает в браузере.
</p>
<div class="checklist-grid">
<div class="check-card featured">
<div class="check-card-head">
<span class="check-badge required">Обязательно</span>
<span class="check-title">Telegram</span>
</div>
<ul class="check-list">
<li>Telegram установлен на телефоне или компе</li>
<li>Аккаунт активен — сможешь зайти в чат по QR со страницы</li>
</ul>
<div class="check-note">Чат инноваторов: помощь и обмен находками во время воркшопа.</div>
</div>
<div class="check-card">
<div class="check-card-head">
<span class="check-badge warn">Перед выходом</span>
<span class="check-title">Что взять с собой</span>
</div>
<ul class="check-list">
<li>Ноутбук заряжен + зарядка</li>
<li>Знаешь свою «боль» в работе — задачу или процесс, который хочешь автоматизировать (станет твоим MVP)</li>
<li>Если есть Excel-файл с примером данных по этой задаче — захвати</li>
</ul>
</div>
<div class="check-card muted">
<div class="check-card-head">
<span class="check-badge">Не нужно</span>
<span class="check-title">Что НЕ ставить</span>
</div>
<ul class="check-list dont">
<li>VS Code на компьютер — используем браузерную версию</li>
<li>Node.js, Python, Git — ничего из этого</li>
<li>API-ключи Claude или OpenAI</li>
</ul>
</div>
</div>
</div>
</section>
<!-- SECTION 4: ACTION PLAN -->
<section class="section" id="plan">
<div class="container">
<div class="section-label">04 — Action Plan</div>
<h2 class="section-title">Четыре шага<br><em>до рабочего MVP</em></h2>
<p class="section-body">
От «у меня есть боль» до «вот работающее приложение». Каждый шаг — 10–30 минут.
</p>
<div class="plan-flow">
<div class="plan-step">
<div class="plan-step-num">Шаг 1</div>
<div class="plan-step-title">Войди в свою папку</div>
<div class="plan-step-desc">
Нажми <b>«Войти»</b> наверху, введи ФИО — откроется твой редактор кода. Внутри уже готовый файл <code>PRD.md</code> (для идеи) и <code>TRD.md</code> (для технической части).
</div>
</div>
<div class="plan-step">
<div class="plan-step-num">Шаг 2</div>
<div class="plan-step-title">Опиши свою задачу в PRD.md</div>
<div class="plan-step-desc">
<code>PRD.md</code> — это файл, где ты простыми словами рассказываешь: что болит, кто будет пользоваться, как должно работать. Никакого кода. Это твоя идея на бумаге.
</div>
</div>
<div class="plan-step">
<div class="plan-step-num">Шаг 3</div>
<div class="plan-step-title">Попроси Claude сделать TRD</div>
<div class="plan-step-desc">
Открой Claude, скинь свой <code>PRD.md</code> и попроси: «Сделай TRD по этому PRD». <code>TRD.md</code> — это твоя идея, переведённая на язык кода. Claude напишет, ты сохранишь.
</div>
</div>
<div class="plan-step">
<div class="plan-step-num">Шаг 4</div>
<div class="plan-step-title">Запусти Claude Code</div>
<div class="plan-step-desc">
В редакторе снизу открой строку для команд и напиши <code>claude code</code>. Скажи: «реализуй TRD.md». Через 10–20 минут — рабочее приложение в твоей папке.
</div>
</div>
</div>
</div>
</section>
<!-- SECTION 5: WHAT YOU CAN CREATE -->
<section class="section" id="create">
<div class="container">
<div class="section-label">05 — Что можно создать</div>
<h2 class="section-title">Три формата.<br><em>Под твою задачу.</em></h2>
<p class="section-body">
Большинство офисных задач сводятся к трём типам решений. Выбери ближайший к твоей боли.
</p>
<div class="create-cards">
<div class="create-card">
<div class="create-card-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="18" height="18" rx="2"/>
<path d="M3 9h18M9 21V9"/>
</svg>
</div>
<div class="create-card-title">Дашборд</div>
<div class="create-card-desc">
Веб-страница с цифрами и графиками из твоих таблиц. Обновляется сама. Можно открыть с телефона.
</div>
<div class="create-card-example">
Например: динамика заявок / продаж / KPI команды за квартал.
</div>
</div>
<div class="create-card">
<div class="create-card-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<path d="M21.5 12a9.5 9.5 0 1 1-3-7L21 4l-1 4-4-1"/>
<circle cx="12" cy="12" r="1"/>
</svg>
</div>
<div class="create-card-title">Telegram-бот</div>
<div class="create-card-desc">
Бот, которому пишешь команду — он отвечает или что-то делает. Может тыкаться в Excel, базы, отчёты.
</div>
<div class="create-card-example">
Например: «/статус заявки 123» → текущий этап и кто ответственный.
</div>
</div>
<div class="create-card">
<div class="create-card-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="3" width="20" height="14" rx="2"/>
<path d="M8 21h8M12 17v4"/>
</svg>
</div>
<div class="create-card-title">Mini-приложение</div>
<div class="create-card-desc">
Страничка с формой для коллег. Они заполняют — у тебя автоматически собирается результат. Без таблиц и пересылки.
</div>
<div class="create-card-example">
Например: согласование командировки в один клик.
</div>
</div>
</div>
</div>
</section>
<!-- SECTION 6: VIBE + HARD = ENTERPRISE -->
<section class="section" id="enterprise">
<div class="container">
<div class="section-label">06 — Это не игрушка</div>
<h2 class="section-title">Vibe coding + Hard coding =<br><em>Enterprise solution</em></h2>
<p class="section-body">
Ты пишешь словами свою идею. Дев-команда КТ потом упаковывает её в прод. На выходе — реальная система внутри Казахтелеком, которой пользуются сотни человек.
</p>
<div class="eq-flow">
<div class="eq-box you">
<div class="eq-box-label">Vibe coding · ты</div>
<div class="eq-box-title">Описываешь боль и решение</div>
<div class="eq-box-desc">
Простыми словами в PRD. Знаешь свой домен лучше всех. Не лезешь в технологии.
</div>
</div>
<div class="eq-op">+</div>
<div class="eq-box">
<div class="eq-box-label">Hard coding · разработчики</div>
<div class="eq-box-title">Полируют до прода</div>
<div class="eq-box-desc">
Безопасность, нагрузка, интеграции, поддержка. Их часть после твоей.
</div>
</div>
<div class="eq-op">=</div>
<div class="eq-box result">
<div class="eq-box-label">Enterprise solution</div>
<div class="eq-box-title">Часть инфраструктуры КТ</div>
<div class="eq-box-desc">
Твой MVP становится сервисом, которым пользуются коллеги. Реально, не «на потом».
</div>
</div>
</div>
<div class="eq-note">
<div class="eq-note-item">
<div class="eq-note-icon">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M8 1l2 2h4v10H2V3h4l2-2z"/>
<path d="M6 8l2 2 4-4"/>
</svg>
</div>
<div class="eq-note-text">
<strong>Документация и помощь рядом.</strong> Справочные файлы лежат в твоей папке. Telegram-чат — для вопросов. Админы платформы — у тебя за плечом.
</div>
</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">07 — Программа первого занятия</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">
<div class="section-label">08 — Словарь</div>
<h2 class="section-title">Термины,<br><em>которые услышишь</em></h2>
<p class="section-body">
11 слов, которые встретятся сегодня. Без технического жаргона. Листай стрелками или свайпом.
</p>
<div class="glossary-slider">
<div class="glossary" id="glossary-list"></div>
<div class="gloss-nav">
<button type="button" id="gloss-prev" class="gloss-nav-btn" aria-label="Назад">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="10,3 5,8 10,13"/></svg>
</button>
<span class="gloss-counter" id="gloss-counter">1 / 11</span>
<button type="button" id="gloss-next" class="gloss-nav-btn" aria-label="Вперёд">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6,3 11,8 6,13"/></svg>
</button>
</div>
</div>
</div>
</section>
<!-- SECTION 6: PROJECTS -->
<section class="section" id="projects">
<div class="container">
<div class="section-label">09 — Твой проект</div>
<h2 class="section-title">Ты знаешь<br><em>боль изнутри</em></h2>
<p class="section-body">
Лучший проект — тот, где ты сам тратишь время впустую, или что-то давно хотел сделать «для себя».
Примеры из разных сфер — выбери ближайший или придумай свой.
</p>
<div class="projects-grid" id="projects-grid"></div>
</div>
</section>
<!-- SECTION: PROMPT CHEATSHEET -->
<section class="section" id="prompts">
<div class="container">
<div class="section-label">10 — Шпаргалка промптов</div>
<h2 class="section-title">Что писать<br><em>Claude'у</em></h2>
<p class="section-body">
Не знаешь с чего начать диалог? Скопируй готовый промпт, замени в нём текст в квадратных скобках на свой контекст. По шагам Action Plan + бонус для работы с Excel.
</p>
<div class="prompts" id="prompts-list"></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">
Жми «Войти» наверху — введи своё имя — и через 5 секунд ты в своей папке с готовыми файлами.
</p>
<button type="button" class="dark-cta" onclick="document.getElementById('nav-auth').click()" style="border:none;cursor:pointer;font-family:inherit">
Войти и начать →
</button>
</div>
</div>
</section>
<!-- FOOTER -->
<footer>
<span class="footer-copy">Казахтелеком · Движение инноваторов</span>
<div class="footer-links">
<a href="https://git.telecom.quest" target="_blank" rel="noopener" class="footer-link">Git</a>
<a href="https://t.me/+9UI8dt-60l8yMzJi" target="_blank" rel="noopener" class="footer-link">Telegram</a>
</div>
</footer>
</main>
</div>
<script>
const GLOSSARY = [
{
term: 'PRD',
abbr: 'PRD',
definition: '<strong>Product Requirements Document</strong> — описание задачи своими словами. Ты пишешь: что должна делать программа, кто ей пользуется, что считается успехом. Это уже умеют делать все, кто составляет ТЗ или внутренние инструкции.'
},
{
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: 'Mock',
abbr: 'Mock',
definition: 'Тестовые данные вместо реальных. Например: вместо боевой выгрузки из CRM — файл с похожей структурой, но с выдуманными именами и цифрами. Позволяет разрабатывать без доступа к боевым системам.'
},
{
term: 'Итерация',
abbr: 'Итерация',
definition: 'Один цикл: <strong>сделал → проверил → улучшил</strong>. В разработке через Claude это занимает минуты. Описал задачу → получил код → запустил → увидел что не так → поправил описание → снова запустил. Это и есть вайб-кодинг.'
},
{
term: 'Промпт',
abbr: 'Prompt',
definition: 'Твоя инструкция для Claude. Чем точнее описание — тем точнее результат. Хороший промпт = хороший PRD: контекст, задача, ограничения, критерий успеха. Всё что ты пишешь в Claude Desktop — это промпты.'
},
{
term: 'Dream to be',
abbr: 'Dream',
definition: 'Самая лучшая версия твоего решения, как ты её видишь в идеале. Опиши её в PRD без оглядки на «реально ли это» — Claude сам подскажет, что упростить.'
},
{
term: 'Git',
abbr: 'Git',
definition: 'Хранилище версий проекта. Каждое изменение записывается с подписью «когда» и «кто». Можно вернуться к любой прошлой версии. В VS Code это панель слева <strong>«Source Control»</strong> — кнопкой Commit & Push отправляешь свою работу в kt-innovators на git.telecom.quest.'
},
{
term: 'CSV',
abbr: 'CSV',
definition: 'Текстовый файл табличных данных. Excel открывает его как обычную таблицу. Хорош тем, что его легко читает и человек, и Claude — отдай CSV ИИ и попроси «найди аномалии» / «построй график».'
},
{
term: 'Markdown',
abbr: '.md',
definition: '<strong>Файл <code>.md</code></strong> — обычный текст с минимальным форматированием (заголовки, списки, ссылки). PRD.md, TRD.md, README.md — всё это markdown. Открывается в любом редакторе.'
}
];
const PROJECTS = [
{
num: '01',
title: 'Личный сайт-визитка',
pain: 'Хочу одну ссылку: кто я, чем занимаюсь, контакты, фото, проекты. Чтобы скинуть рекрутёру, заказчику или новому знакомому вместо длинного объяснения в мессенджере.',
tags: ['Личный сайт', 'Портфолио', 'Для себя']
},
{
num: '02',
title: 'Трекер привычек',
pain: 'Хочу каждый день отмечать: пил воду, тренировался, читал. Видеть свой стрик и не сбиваться. Без подписок и регистраций — просто открыл и щёлкнул.',
tags: ['Привычки', 'Здоровье', 'Личное']
},
{
num: '03',
title: 'Чек-лист подготовки к мероприятию',
pain: 'Организую встречу/тренинг/выезд — каждый раз пишу одно и то же в блокноте. Хочу шаблон-чек-лист со статусами и распределением задач между коллегами.',
tags: ['HR', 'Маркетинг', 'Командная работа']
},
{
num: '04',
title: 'Каталог внутренних регламентов',
pain: 'Документы и инструкции лежат на 5 разных дисках и в почте. Когда новичок спрашивает «как сделать X» — ищу полчаса. Нужна одна страница с поиском.',
tags: ['HR', 'База знаний', 'Поиск']
},
{
num: '05',
title: 'Опросник для клиентов / коллег',
pain: 'Хочу быстро собрать обратную связь — без Google Forms и подписки на SurveyMonkey. Своя страница, свои вопросы, результаты в табличке.',
tags: ['Опрос', 'Маркетинг', 'CSAT']
},
{
num: '06',
title: 'Генератор коммерческих предложений',
pain: 'Каждое КП собираю вручную в Word: меняю имя клиента, цены, сроки. Хочу форму — ввёл данные, получил PDF с моим оформлением.',
tags: ['Продажи', 'PDF', 'Шаблоны']
},
{
num: '07',
title: 'План-меню на неделю',
pain: 'Каждое воскресенье думаю «что готовить эту неделю». Хочу страницу: рецепты по дням, автоматический список покупок, чтобы один раз сходить в магазин.',
tags: ['Личное', 'Быт', 'Список покупок']
},
{
num: '08',
title: 'Учёт расходов простыми словами',
pain: 'Mint/CoinKeeper слишком сложные. Хочу 3 кнопки: «потратил столько-то на еду/транспорт/прочее» и график по неделям.',
tags: ['Финансы', 'Личное', 'Бюджет']
},
{
num: '09',
title: 'Бот-напоминалка в Telegram',
pain: 'Постоянно забываю про регулярные дела — оплатить интернет, поздравить коллегу, продлить подписку. Хочу простой TG-бот, который пинает за день до.',
tags: ['Telegram-бот', 'Напоминания', 'Календарь']
},
{
num: '10',
title: 'Лендинг для своего хобби / услуги',
pain: 'Делаю торты на заказ / тренирую / преподаю английский — но «портфолио» в Instagram неудобно показывать. Хочу одну страницу с примерами и формой заявки.',
tags: ['Лендинг', 'Услуги', 'Заявки']
},
{
num: '11',
title: 'Мини-CRM для своих клиентов',
pain: 'Веду 20 клиентов в Excel-табличке: имя, телефон, что обсуждали, когда позвонить. Хочу страницу с карточками, напоминаниями и заметками.',
tags: ['CRM', 'Продажи', 'Малый бизнес']
},
{
num: '12',
title: 'Тренажёр терминов / слов',
pain: 'Учу английский / готовлюсь к экзамену / запоминаю аббревиатуры. Хочу карточки-флешкарды: показал термин — вспомнил — перевернул — отметил «знаю / повторить».',
tags: ['Обучение', 'Языки', 'Флешкарды']
}
];
const PROMPTS = [
{
step: 'Шаг 1',
title: 'Помоги написать PRD',
context: 'Открой Claude. Опиши свою рабочую боль одним абзацем — Claude задаст уточняющие вопросы и оформит PRD.md.',
body: `Я сотрудник Казахтелеком, не программист. Помоги мне написать PRD.md — описание идеи продукта простыми словами.
Моя ситуация: [замени на свой текст — что болит, что хочешь автоматизировать]
Сначала задай мне 3-5 уточняющих вопросов. Потом, на основе моих ответов, оформи PRD.md по структуре:
- Что я хочу автоматизировать (одно предложение)
- Кто будет пользоваться
- Что должно произойти на выходе
- Список из 3-5 главных функций
- Что НЕ входит в задачу
Пиши без программистских терминов.`
},
{
step: 'Шаг 2',
title: 'Сгенерируй TRD из PRD',
context: 'Когда PRD.md готов — попроси Claude перевести его в техническое задание.',
body: `Прочитай файл PRD.md (моя идея, написана простыми словами).
Сгенерируй TRD.md — техническое задание для разработки MVP:
- Какие данные нужны
- Минимальный стек (HTML/CSS/JS, или Python+FastAPI, или Node.js — выбери проще)
- Структура папок проекта
- Как запускать
Цель — рабочий MVP за час, не идеальная архитектура.
Сохрани результат в TRD.md, заменив текущее содержимое файла.`
},
{
step: 'Шаг 3',
title: 'Запусти Claude Code на TRD',
context: 'В терминале VS Code вызови claude code — потом вставь этот промпт. Claude напишет код прямо в твою папку.',
body: `Прочитай TRD.md в текущей папке. Реализуй MVP по нему.
Принципы:
- Простой стек (предпочтительно HTML+CSS+JS без сборки)
- Все файлы в текущей папке
- Никаких внешних API-ключей и платных сервисов
- Если нужны данные — используй mock-значения, понятные обычному пользователю
В конце:
1. Напиши команду запуска (например, "открой index.html в браузере" или "python3 -m http.server 8000")
2. Список того, что готово, и что осталось доделать.`
},
{
step: 'Шаг 4',
title: 'Когда что-то сломалось',
context: 'Получил ошибку или результат выглядит странно — опиши Claude словами что видишь, остальное он разберёт сам.',
body: `Запусти приложение из текущей папки.
Опиши, какую ошибку видишь (если есть). Если ошибки нет — опиши, что выглядит не так.
Затем:
1. Найди причину
2. Исправь код
3. Перезапусти
4. Покажи, что теперь работает
Не спрашивай у меня дополнительных деталей — действуй.`
},
{
step: 'Шаг 5',
title: 'Улучши результат',
context: 'MVP работает — теперь делаешь итерацию: одно улучшение за раз. Каждая правка — это новый промпт.',
body: `Сейчас в приложении нужно изменить одну вещь:
[замени на свой запрос — например: "цифры должны быть с разделителями тысяч",
"добавь фильтр по дате", "сделай заголовок жирным"]
Внеси изменение. Не трогай остальное.
Опиши одной строкой, что именно ты поменял.`
},
{
step: 'Бонус',
bonus: true,
title: 'Разобрать Excel/CSV-файл',
context: 'У тебя есть таблица из CRM, 1С или Excel? Сохрани её как CSV и попроси Claude найти полезное.',
body: `У меня есть файл [имя_файла.csv] с данными о [тип данных, например: "заявках за квартал" / "сотрудниках" / "продажах"].
Прочитай его и покажи мне:
1. Сколько всего строк
2. Какие колонки (с короткими комментариями что они значат)
3. Топ-5 самых крупных значений
4. 3-5 аномалий или выбросов (дубликаты, странные суммы, пустые поля)
5. Один график, который покажет главную закономерность
Используй простые слова — я не программист.`
},
{
step: 'Бонус',
bonus: true,
title: 'Сделай Telegram-бот',
context: 'Хочешь бот, который что-то считает или напоминает? Этот промпт даст работающий бот за один запуск.',
body: `Сделай простой Telegram-бот на Python (python-telegram-bot).
Бот делает: [замени на задачу — например: "при команде /курс отвечает текущим курсом USD к KZT",
"присылает напоминание каждый день в 9:00", "по сообщению с числом выдаёт его в разных валютах"]
Требования:
- Один файл bot.py
- Токен бота читать из переменной окружения TG_TOKEN
- Минимум зависимостей в requirements.txt
- README.md: как получить токен через @BotFather и как запустить
Покажи код и инструкцию.`
}
];
const SCHEDULE = [
{
time: '09:00',
title: 'Введение в вайб-кодинг',
desc: 'Что изменилось. Почему эксперт в своём деле — идеальный постановщик задач для ИИ. Первое знакомство с Claude.',
tags: ['Теория'],
active: false
},
{
time: '09:45',
title: 'Инструменты и среда',
desc: 'Настройка Claude Desktop, telecom.quest VSCode.',
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
}
];
</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 as horizontal slider with prev/next buttons
const glossaryEl = document.getElementById('glossary-list');
GLOSSARY.forEach(item => {
const div = document.createElement('div');
div.className = 'gloss-item';
div.innerHTML = `
<div class="gloss-trigger">
<span class="gloss-term">${item.term}</span>
<span class="gloss-abbr">${item.abbr}</span>
</div>
<div class="gloss-body">
<div class="gloss-inner">
<div class="gloss-text">${item.definition}</div>
</div>
</div>
`;
glossaryEl.appendChild(div);
});
// Glossary slider controls
(function initGlossSlider() {
const prev = document.getElementById('gloss-prev');
const next = document.getElementById('gloss-next');
const counter = document.getElementById('gloss-counter');
const cards = Array.from(glossaryEl.querySelectorAll('.gloss-item'));
const total = cards.length;
let idx = 0;
function update() {
counter.textContent = `${idx + 1} / ${total}`;
prev.disabled = idx === 0;
next.disabled = idx === total - 1;
}
function go(i) {
idx = Math.max(0, Math.min(total - 1, i));
cards[idx].scrollIntoView({ behavior: 'smooth', inline: 'start', block: 'nearest' });
update();
}
function syncFromScroll() {
const baseLeft = glossaryEl.getBoundingClientRect().left;
let best = 0, bestDist = Infinity;
for (let i = 0; i < cards.length; i++) {
const d = Math.abs(cards[i].getBoundingClientRect().left - baseLeft);
if (d < bestDist) { bestDist = d; best = i; }
}
if (best !== idx) { idx = best; update(); }
}
prev.addEventListener('click', () => go(idx - 1));
next.addEventListener('click', () => go(idx + 1));
glossaryEl.addEventListener('scroll', syncFromScroll, { passive: true });
update();
})();
// 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);
});
// Render prompt cheatsheet
const promptsEl = document.getElementById('prompts-list');
function escapeHtml(s) {
return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
PROMPTS.forEach((item, i) => {
const card = document.createElement('div');
card.className = 'prompt-card';
card.innerHTML = `
<div class="prompt-head">
<span class="prompt-step${item.bonus ? ' bonus' : ''}">${item.step}</span>
<span class="prompt-title">${item.title}</span>
</div>
<div class="prompt-context">${item.context}</div>
<div class="prompt-box">
<button type="button" class="prompt-copy" data-idx="${i}" aria-label="Скопировать">
<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="5" y="5" width="9" height="9" rx="1"/><path d="M11 5V3a1 1 0 0 0-1-1H3a1 1 0 0 0-1 1v7a1 1 0 0 0 1 1h2"/>
</svg>
Скопировать
</button>
<pre>${escapeHtml(item.body)}</pre>
</div>
`;
promptsEl.appendChild(card);
});
// Wire copy buttons
promptsEl.addEventListener('click', async (e) => {
const btn = e.target.closest('.prompt-copy');
if (!btn) return;
const idx = +btn.dataset.idx;
try {
await navigator.clipboard.writeText(PROMPTS[idx].body);
const originalHTML = btn.innerHTML;
btn.classList.add('copied');
btn.innerHTML = '<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3,8 7,12 13,4"/></svg> Скопировано';
setTimeout(() => {
btn.classList.remove('copied');
btn.innerHTML = originalHTML;
}, 1800);
} catch (err) {
btn.textContent = 'Не вышло :(';
}
});
/* ── AUTH ──────────────────────────────────────────────
Smart single-form auth backed by register.telecom.quest.
- Login first (works for both FIO and CDN inputs).
- On 404 with FIO input → register (creates folder + Gitea repo).
- On 404 with CDN input → "not found, type your full name to register".
*/
const API_BASE = 'https://register.telecom.quest/api';
const CODE_BASE = 'https://code.telecom.quest/?folder=/Users/telecom/dev/_hack19may/';
const STORAGE_KEY = 'vibe.cdn';
// Cyrillic → Latin transliteration (basic, covers Russian + Kazakh-ish names)
const TRANSLIT = {
'а':'a','б':'b','в':'v','г':'g','д':'d','е':'e','ё':'e','ж':'zh','з':'z','и':'i','й':'i',
'к':'k','л':'l','м':'m','н':'n','о':'o','п':'p','р':'r','с':'s','т':'t','у':'u','ф':'f',
'х':'kh','ц':'ts','ч':'ch','ш':'sh','щ':'sch','ъ':'','ы':'y','ь':'','э':'e','ю':'yu','я':'ya',
'ә':'a','ғ':'g','қ':'k','ң':'n','ө':'o','ұ':'u','ү':'u','һ':'h','і':'i'
};
function translit(s) {
return s.toLowerCase().split('').map(ch => TRANSLIT[ch] ?? ch).join('');
}
function makeCdn(input) {
const trimmed = input.trim().replace(/\s+/g, ' ');
if (!trimmed) return { cdn: '', mode: 'empty' };
// Single token → treat as CDN as-is (lowercased, dots removed, only allowed chars)
if (!/\s/.test(trimmed)) {
const cdn = translit(trimmed).replace(/\./g, '').replace(/[^a-z0-9_-]/g, '');
return { cdn, mode: 'login' };
}
// Multi-word → ФИО, generate CDN as <initial><lastname>
const parts = trimmed.split(' ');
const first = parts[0];
const last = parts[parts.length - 1];
const cdn = (translit(first).charAt(0) + translit(last))
.replace(/\./g, '').replace(/[^a-z0-9_-]/g, '');
return { cdn, mode: 'fio' };
}
const authModal = document.getElementById('auth-modal');
const authBtn = document.getElementById('nav-auth');
const cdnChip = document.getElementById('nav-cdn');
const closeBtn = document.getElementById('modal-close');
const input = document.getElementById('auth-input');
const hint = document.getElementById('auth-hint');
const submit = document.getElementById('auth-submit');
function refreshHeader() {
const saved = localStorage.getItem(STORAGE_KEY);
if (saved) {
authBtn.textContent = 'Открыть VS Code →';
cdnChip.hidden = false;
cdnChip.textContent = saved;
} else {
authBtn.textContent = 'Войти';
cdnChip.hidden = true;
}
}
function redirectTo(cdn, vscodeUrl) {
window.location.href = vscodeUrl || (CODE_BASE + encodeURIComponent(cdn) + '/');
}
async function apiCall(path, body) {
const res = await fetch(API_BASE + path, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
const data = await res.json().catch(() => ({}));
return { ok: res.ok, status: res.status, data };
}
function openModal() {
authModal.classList.add('open');
setTimeout(() => input.focus(), 50);
input.value = '';
hint.innerHTML = '&nbsp;';
hint.classList.remove('error');
submit.disabled = true;
}
function closeModal() {
authModal.classList.remove('open');
}
input.addEventListener('input', () => {
const { cdn, mode } = makeCdn(input.value);
if (mode === 'empty') {
hint.innerHTML = '&nbsp;';
submit.disabled = true;
return;
}
if (!cdn) {
hint.textContent = 'Не получилось разобрать имя — попробуй латиницей';
hint.classList.add('error');
submit.disabled = true;
return;
}
hint.classList.remove('error');
const path = `/Users/telecom/dev/_hack19may/${cdn}/`;
if (mode === 'login') {
hint.innerHTML = `Откроется <code>${path}</code>`;
} else {
hint.innerHTML = `Твой CDN: <code>${cdn}</code> → <code>${path}</code>`;
}
submit.disabled = false;
});
async function handleSubmit() {
const { cdn, mode } = makeCdn(input.value);
if (!cdn) return;
submit.disabled = true;
submit.textContent = 'Проверяю...';
hint.classList.remove('error');
try {
// Step 1: try login (works for both CDN and FIO inputs)
const login = await apiCall('/login', { value: input.value.trim() });
if (login.ok) {
localStorage.setItem(STORAGE_KEY, login.data.cdn);
refreshHeader();
redirectTo(login.data.cdn, login.data.vscode_url);
return;
}
// Step 2: not found — auto-register with whatever was typed
if (login.status === 404) {
submit.textContent = 'Создаю аккаунт...';
hint.innerHTML = `Аккаунт <code>${cdn}</code> не найден — создаю новый...`;
// FIO mode → send fio (backend will generate cdn); CDN mode → send cdn directly
const regBody = mode === 'fio'
? { fio: input.value.trim() }
: { cdn: input.value.trim() };
const reg = await apiCall('/register', regBody);
if (reg.ok) {
localStorage.setItem(STORAGE_KEY, reg.data.cdn);
refreshHeader();
redirectTo(reg.data.cdn, reg.data.vscode_url);
return;
}
const msg = reg.data?.detail?.message || reg.data?.detail?.error || `HTTP ${reg.status}`;
hint.textContent = `Не удалось создать: ${msg}`;
hint.classList.add('error');
} else {
const msg = login.data?.detail?.message || login.data?.detail?.error || `HTTP ${login.status}`;
hint.textContent = `Ошибка: ${msg}`;
hint.classList.add('error');
}
} catch (err) {
hint.textContent = `Ошибка сети: ${err.message || err}`;
hint.classList.add('error');
} finally {
submit.disabled = false;
submit.textContent = 'Открыть VS Code →';
}
}
submit.addEventListener('click', handleSubmit);
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !submit.disabled) handleSubmit();
if (e.key === 'Escape') closeModal();
});
authBtn.addEventListener('click', () => {
const saved = localStorage.getItem(STORAGE_KEY);
if (saved) {
// Direct redirect using saved CDN — fast path, no API hit
redirectTo(saved);
} else {
openModal();
}
});
cdnChip.addEventListener('click', () => {
if (!confirm('Выйти и забыть CDN?')) return;
localStorage.removeItem(STORAGE_KEY);
refreshHeader();
});
closeBtn.addEventListener('click', closeModal);
authModal.addEventListener('click', (e) => {
if (e.target === authModal) closeModal();
});
refreshHeader();
/* ── THEME TOGGLE ───────────────────────────────────────
Manual override stored in localStorage. Falls back to system
(prefers-color-scheme) when no explicit choice. */
(function initThemeToggle() {
const btn = document.getElementById('theme-toggle');
if (!btn) return;
function currentTheme() {
const explicit = document.documentElement.getAttribute('data-theme');
if (explicit) return explicit;
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
btn.addEventListener('click', () => {
const next = currentTheme() === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', next);
try { localStorage.setItem('vibe.theme', next); } catch (e) {}
});
})();
/* ── POLISH: scroll-triggered fade-in ────────────────────
Marks all direct content blocks inside <section class="section">
with .reveal, then toggles .in on intersection.
Respects prefers-reduced-motion via CSS fallback. */
(function initReveal() {
if (!('IntersectionObserver' in window)) return;
const targets = document.querySelectorAll(
'.section .container > *, .section .container .section-header > *'
);
targets.forEach(el => el.classList.add('reveal'));
const io = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting) {
e.target.classList.add('in');
io.unobserve(e.target);
}
});
}, { rootMargin: '0px 0px -60px 0px', threshold: 0.05 });
targets.forEach(el => io.observe(el));
})();
// Slide counter + keyboard nav (PageDown / Arrow / Space)
(function() {
const counter = document.getElementById('slide-counter');
const slides = Array.from(document.querySelectorAll('.hero, section.section, .dark-section'));
const total = slides.length;
let current = 0;
const update = (idx) => {
current = idx;
if (counter) counter.textContent = `— ${idx + 1} / ${total} —`;
};
update(0);
const io = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting && e.intersectionRatio > 0.5) {
const idx = slides.indexOf(e.target);
if (idx >= 0) update(idx);
}
});
}, { threshold: [0.5, 0.6, 0.75] });
slides.forEach(s => io.observe(s));
const goTo = (idx) => {
idx = Math.max(0, Math.min(total - 1, idx));
slides[idx].scrollIntoView({ behavior: 'smooth', block: 'start' });
};
document.addEventListener('keydown', (e) => {
if (['INPUT', 'TEXTAREA'].includes(document.activeElement?.tagName)) return;
if (e.key === 'ArrowDown' || e.key === 'PageDown' || e.key === ' ') {
e.preventDefault(); goTo(current + 1);
} else if (e.key === 'ArrowUp' || e.key === 'PageUp') {
e.preventDefault(); goTo(current - 1);
} else if (e.key === 'Home') {
e.preventDefault(); goTo(0);
} else if (e.key === 'End') {
e.preventDefault(); goTo(total - 1);
}
});
})();
</script>
</body>
</html>