PRODJECT NAVIGATOR | Управление проектами и продуктами будущего

Загрузка космического навигатора...

УПРАВЛЯЙТЕ ПРОЕКТАМИ КАК КОСМИЧЕСКОЙ МИССИЕЙ

3D-навигатор, интерактивные калькуляторы и AI-инструменты для менеджеров будущего

0
методик
0
инструментов
0
шаблонов

Планета Инициации

Определение целей и стейкхолдеров

КОСМИЧЕСКИЙ КАЛЬКУЛЯТОР БЮДЖЕТА

Рассчитайте стоимость вашей миссии с точностью до 95%

СТОИМОСТЬ МИССИИ

2 850 000
Сроки: 6 месяцев
Вероятность успеха: 78%
ROI: 142%

ГАЛАКТИКА ЗНАНИЙ

50000+ знаков экспертных материалов по управлению

СИМУЛЯТОР РИСКОВ

Тренируйтесь отражать астероиды проблем

Жизни: 3
Очки: 0
Уровень: 1

ГОТОВЫ К СЛЕДУЮЩЕЙ МИССИИ?

Получите полный доступ ко всем инструментам, шаблонам и персональному AI-ассистенту

✅ 150+ готовых шаблонов
✅ AI-анализ рисков
✅ Экспорт в Jira/Notion
✅ Командный доступ
/* ===== RESET & BASE ===== */ * { margin: 0; padding: 0; box-sizing: border-box; } :root { /* Color Palette */ --space-black: #0a0e17; --deep-space: #121828; --neon-cyan: #00f3ff; --electric-purple: #9d00ff; --cyber-pink: #ff00ff; --star-white: #ffffff; --nebula-blue: #1a2b4d; --comet-gray: #2a3a5a; /* Typography */ --font-heading: 'Orbitron', sans-serif; --font-body: 'Exo 2', sans-serif; /* Effects */ --glow: 0 0 20px rgba(0, 243, 255, 0.5); --glow-strong: 0 0 40px rgba(157, 0, 255, 0.7); --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } html { scroll-behavior: smooth; scroll-padding-top: 100px; } body { background: var(--space-black); color: var(--star-white); font-family: var(--font-body); font-size: 16px; line-height: 1.6; overflow-x: hidden; position: relative; } body::before { content: ''; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: radial-gradient(circle at 20% 30%, rgba(157, 0, 255, 0.1) 0%, transparent 50%), radial-gradient(circle at 80% 70%, rgba(0, 243, 255, 0.1) 0%, transparent 50%); z-index: -2; } /* ===== TYPOGRAPHY ===== */ h1, h2, h3, h4, .nav-logo, .btn { font-family: var(--font-heading); font-weight: 700; letter-spacing: 1px; text-transform: uppercase; } .hero-title { font-size: clamp(2.5rem, 5vw, 4.5rem); line-height: 1.1; margin-bottom: 1.5rem; } .title-line { display: block; } .highlight { color: var(--neon-cyan); text-shadow: var(--glow); position: relative; display: inline-block; } .highlight::after { content: ''; position: absolute; bottom: -5px; left: 0; width: 100%; height: 3px; background: linear-gradient(90deg, var(--neon-cyan), transparent); animation: pulse 2s infinite; } @keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } .section-title { font-size: 2.5rem; text-align: center; margin-bottom: 1rem; position: relative; } .section-title::after { content: ''; position: absolute; bottom: -10px; left: 50%; transform: translateX(-50%); width: 100px; height: 3px; background: linear-gradient(90deg, var(--neon-cyan), var(--electric-purple)); } /* ===== PRELOADER ===== */ .preloader { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: var(--space-black); display: flex; justify-content: center; align-items: center; z-index: 9999; transition: opacity 0.5s ease; } .preloader.fade-out { opacity: 0; pointer-events: none; } .neon-spinner { width: 80px; height: 80px; border: 4px solid transparent; border-top: 4px solid var(--neon-cyan); border-radius: 50%; animation: spin 1s linear infinite; box-shadow: var(--glow); } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } .preloader-text { margin-top: 20px; font-size: 1.2rem; color: var(--neon-cyan); animation: blink 2s infinite; } @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } } /* ===== NAVIGATION ===== */ .main-nav { position: fixed; top: 0; width: 100%; background: rgba(10, 14, 23, 0.9); backdrop-filter: blur(10px); border-bottom: 1px solid rgba(0, 243, 255, 0.2); z-index: 1000; padding: 1rem 0; } .nav-container { max-width: 1400px; margin: 0 auto; padding: 0 2rem; display: flex; justify-content: space-between; align-items: center; } .nav-logo { font-size: 1.8rem; font-weight: 900; display: flex; flex-direction: column; line-height: 1; } .logo-text { color: var(--star-white); text-shadow: var(--glow); } .logo-sub { color: var(--neon-cyan); font-size: 0.8rem; letter-spacing: 3px; } .nav-menu { display: flex; gap: 2rem; } .nav-link { color: var(--star-white); text-decoration: none; font-weight: 500; position: relative; padding: 0.5rem 0; transition: var(--transition); } .nav-link::after { content: ''; position: absolute; bottom: 0; left: 0; width: 0; height: 2px; background: var(--neon-cyan); transition: width 0.3s ease; } .nav-link:hover::after { width: 100%; } .nav-link:hover { color: var(--neon-cyan); } .nav-cta { background: linear-gradient(90deg, var(--neon-cyan), var(--electric-purple)); color: var(--space-black); border: none; padding: 0.8rem 2rem; font-weight: 700; border-radius: 4px; cursor: pointer; transition: var(--transition); text-transform: uppercase; } .nav-cta:hover { transform: translateY(-2px); box-shadow: var(--glow-strong); } .mobile-menu-btn { display: none; flex-direction: column; gap: 4px; cursor: pointer; } .hamburger-line { width: 25px; height: 2px; background: var(--neon-cyan); transition: var(--transition); } /* ===== HERO SECTION ===== */ .hero-section { min-height: 100vh; display: flex; flex-direction: column; justify-content: center; padding: 120px 2rem 2rem; position: relative; } .hero-content { max-width: 1400px; margin: 0 auto; width: 100%; position: relative; z-index: 2; } .hero-subtitle { font-size: 1.2rem; color: rgba(255, 255, 255, 0.8); margin-bottom: 3rem; max-width: 600px; } .hero-buttons { display: flex; gap: 1rem; margin-bottom: 4rem; flex-wrap: wrap; } .btn-primary, .btn-secondary { padding: 1rem 2.5rem; font-size: 1.1rem; font-weight: 700; border-radius: 6px; cursor: pointer; transition: var(--transition); display: flex; align-items: center; gap: 10px; border: none; } .btn-primary { background: linear-gradient(90deg, var(--neon-cyan), var(--electric-purple)); color: var(--space-black); } .btn-primary:hover { transform: translateY(-3px); box-shadow: var(--glow-strong); } .btn-secondary { background: transparent; color: var(--neon-cyan); border: 2px solid var(--neon-cyan); } .btn-secondary:hover { background: rgba(0, 243, 255, 0.1); transform: translateY(-3px); } .hero-stats { display: flex; gap: 3rem; margin-top: 4rem; } .stat-item { display: flex; flex-direction: column; align-items: flex-start; } .stat-number { font-size: 3rem; font-weight: 900; font-family: var(--font-heading); color: var(--neon-cyan); line-height: 1; } .stat-label { font-size: 0.9rem; color: rgba(255, 255, 255, 0.7); text-transform: uppercase; letter-spacing: 1px; } /* ===== 3D CANVAS ===== */ .canvas-container { position: absolute; top: 0; right: 0; width: 60%; height: 100%; z-index: 1; } #spaceCanvas { width: 100%; height: 100%; display: block; } .planet-info { position: absolute; bottom: 20%; right: 10%; background: rgba(10, 14, 23, 0.9); padding: 1.5rem; border-radius: 10px; border: 1px solid var(--neon-cyan); max-width: 300px; backdrop-filter: blur(10px); transform: translateY(0); animation: float 3s ease-in-out infinite; } @keyframes float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-10px); } } .planet-btn { background: transparent; color: var(--neon-cyan); border: 1px solid var(--neon-cyan); padding: 0.5rem 1rem; margin-top: 1rem; cursor: pointer; transition: var(--transition); } .planet-btn:hover { background: rgba(0, 243, 255, 0.1); } /* ===== CALCULATOR ===== */ .section-calculator { padding: 6rem 2rem; background: var(--deep-space); position: relative; } .calculator-container { max-width: 1400px; margin: 3rem auto 0; background: rgba(18, 24, 40, 0.8); border-radius: 20px; padding: 2rem; border: 1px solid var(--comet-gray); box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); } .calculator-tabs { display: flex; gap: 1rem; margin-bottom: 2rem; border-bottom: 2px solid var(--comet-gray); padding-bottom: 1rem; } .tab-btn { background: transparent; color: var(--star-white); border: none; padding: 0.8rem 2rem; font-size: 1rem; cursor: pointer; border-radius: 6px; transition: var(--transition); display: flex; align-items: center; gap: 8px; } .tab-btn.active { background: var(--nebula-blue); color: var(--neon-cyan); border: 1px solid var(--neon-cyan); } .calculator-content { display: grid; grid-template-columns: 1fr 1fr; gap: 3rem; } .calculator-inputs { display: flex; flex-direction: column; gap: 1.5rem; } .input-group { display: flex; flex-direction: column; gap: 0.5rem; } .input-group label { color: var(--neon-cyan); font-weight: 500; display: flex; justify-content: space-between; } .input-group input, .input-group select { background: var(--space-black); border: 1px solid var(--comet-gray); color: var(--star-white); padding: 0.8rem 1rem; border-radius: 6px; font-size: 1rem; transition: var(--transition); } .input-group input:focus, .input-group select:focus { outline: none; border-color: var(--neon-cyan); box-shadow: 0 0 0 2px rgba(0, 243, 255, 0.2); } .range-value { color: var(--neon-cyan); font-weight: 700; } .result-card { background: linear-gradient(135deg, var(--space-black), var(--deep-space)); padding: 2rem; border-radius: 15px; border: 1px solid var(--neon-cyan); box-shadow: var(--glow); } .result-amount { font-size: 3.5rem; font-weight: 900; color: var(--neon-cyan); margin: 1rem 0; display: flex; align-items: baseline; } .currency { font-size: 2rem; margin-right: 10px; } .result-details { display: flex; flex-direction: column; gap: 1rem; margin: 2rem 0; padding: 1.5rem 0; border-top: 1px solid var(--comet-gray); border-bottom: 1px solid var(--comet-gray); } .detail-item { display: flex; justify-content: space-between; align-items: center; } .detail-item span:first-child { color: rgba(255, 255, 255, 0.7); } .detail-item span:last-child { color: var(--neon-cyan); font-weight: 700; } .btn-download { width: 100%; background: linear-gradient(90deg, var(--neon-cyan), var(--electric-purple)); color: var(--space-black); border: none; padding: 1rem; font-size: 1.1rem; font-weight: 700; border-radius: 6px; cursor: pointer; transition: var(--transition); margin-top: 1.5rem; text-transform: uppercase; } .btn-download:hover { transform: translateY(-2px); box-shadow: var(--glow-strong); } /* ===== KNOWLEDGE GRID ===== */ .section-knowledge { padding: 6rem 2rem; } .knowledge-grid { max-width: 1400px; margin: 3rem auto 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 2rem; } .knowledge-card { background: rgba(18, 24, 40, 0.8); border-radius: 15px; padding: 2rem; border: 1px solid var(--comet-gray); transition: var(--transition); position: relative; overflow: hidden; } .knowledge-card::before { content: ''; position: absolute; top: 0; left: 0; width: 100%; height: 4px; background: linear-gradient(90deg, var(--neon-cyan), var(--electric-purple)); } .knowledge-card:hover { transform: translateY(-10px); border-color: var(--neon-cyan); box-shadow: var(--glow); } .card-tag { display: inline-block; background: rgba(0, 243, 255, 0.1); color: var(--neon-cyan); padding: 0.3rem 1rem; border-radius: 20px; font-size: 0.8rem; margin-bottom: 1rem; text-transform: uppercase; font-weight: 600; } .card-title { font-size: 1.5rem; margin-bottom: 1rem; color: var(--star-white); } .card-description { color: rgba(255, 255, 255, 0.7); margin-bottom: 1.5rem; line-height: 1.6; } .card-stats { display: flex; justify-content: space-between; align-items: center; color: rgba(255, 255, 255, 0.5); font-size: 0.9rem; } /* ===== RISK GAME ===== */ .section-risk { padding: 6rem 2rem; background: var(--deep-space); } .risk-game-container { max-width: 1000px; margin: 3rem auto 0; position: relative; } #riskCanvas { width: 100%; background: var(--space-black); border-radius: 15px; border: 2px solid var(--comet-gray); display: block; } .game-controls { margin-top: 2rem; display: flex; flex-direction: column; gap: 1.5rem; } .game-stats { display: flex; justify-content: center; gap: 3rem; font-size: 1.2rem; } .game-stats .stat { color: var(--neon-cyan); } .game-stats span { font-weight: 700; font-size: 1.5rem; } .risk-tools { display: flex; justify-content: center; gap: 1rem; flex-wrap: wrap; } .tool-btn { background: rgba(18, 24, 40, 0.8); color: var(--star-white); border: 1px solid var(--comet-gray); padding: 0.8rem 1.5rem; border-radius: 8px; cursor: pointer; transition: var(--transition); display: flex; align-items: center; gap: 8px; } .tool-btn:hover { border-color: var(--neon-cyan); background: rgba(0, 243, 255, 0.1); } .btn-start-game { background: linear-gradient(90deg, var(--electric-purple), var(--cyber-pink)); color: var(--star-white); border: none; padding: 1rem 3rem; font-size: 1.2rem; font-weight: 700; border-radius: 8px; cursor: pointer; transition: var(--transition); margin: 0 auto; display: block; width: fit-content; } .btn-start-game:hover { transform: scale(1.05); box-shadow: 0 0 30px rgba(255, 0, 255, 0.5); } /* ===== CTA SECTION ===== */ .section-cta { padding: 8rem 2rem; background: linear-gradient(135deg, var(--deep-space), var(--space-black)); position: relative; overflow: hidden; } .section-cta::before { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: url('data:image/svg+xml,'); } .cta-container { max-width: 800px; margin: 0 auto; position: relative; z-index: 1; text-align: center; } .cta-title { font-size: 3rem; margin-bottom: 1.5rem; background: linear-gradient(90deg, var(--neon-cyan), var(--star-white)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; } .cta-text { font-size: 1.2rem; color: rgba(255, 255, 255, 0.8); margin-bottom: 3rem; max-width: 600px; margin-left: auto; margin-right: auto; } .cta-form { display: flex; gap: 1rem; max-width: 600px; margin: 0 auto 2rem; } .email-input { flex: 1; background: rgba(255, 255, 255, 0.1); border: 2px solid var(--comet-gray); color: var(--star-white); padding: 1rem 1.5rem; border-radius: 8px; font-size: 1rem; transition: var(--transition); } .email-input:focus { outline: none; border-color: var(--neon-cyan); box-shadow: var(--glow); } .email-input::placeholder { color: rgba(255, 255, 255, 0.5); } .btn-cta { background: linear-gradient(90deg, var(--neon-cyan), var(--electric-purple)); color: var(--space-black); border: none; padding: 1rem 3rem; font-size: 1.1rem; font-weight: 700; border-radius: 8px; cursor: pointer; transition: var(--transition); text-transform: uppercase; white-space: nowrap; } .btn-cta:hover { transform: translateY(-2px); box-shadow: var(--glow-strong); } .cta-features { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 1rem; max-width: 600px; margin: 0 auto; } .feature { color: var(--neon-cyan); font-weight: 500; text-align: left; padding: 0.5rem 0; } /* ===== FOOTER ===== */ .main-footer { background: var(--space-black); padding: 4rem 2rem 2rem; border-top: 1px solid var(--comet-gray); } .footer-content { max-width: 1400px; margin: 0 auto; display: grid; grid-template-columns: 1fr 2fr; gap: 4rem; margin-bottom: 3rem; } .footer-logo { font-family: var(--font-heading); font-size: 1.8rem; color: var(--neon-cyan); margin-bottom: 1rem; font-weight: 700; } .social-links { display: flex; gap: 1rem; margin-top: 1.5rem; } .social-link { color: rgba(255, 255, 255, 0.7); text-decoration: none; transition: var(--transition); } .social-link:hover { color: var(--neon-cyan); } .footer-links { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2rem; } .link-group h4 { color: var(--neon-cyan); margin-bottom: 1.5rem; font-size: 1.1rem; } .link-group a { display: block; color: rgba(255, 255, 255, 0.7); text-decoration: none; margin-bottom: 0.8rem; transition: var(--transition); } .link-group a:hover { color: var(--neon-cyan); transform: translateX(5px); } .footer-bottom { max-width: 1400px; margin: 0 auto; padding-top: 2rem; border-top: 1px solid var(--comet-gray); text-align: center; color: rgba(255, 255, 255, 0.5); font-size: 0.9rem; } .footer-disclaimer { margin-top: 0.5rem; font-size: 0.8rem; color: rgba(255, 255, 255, 0.4); } /* ===== RESPONSIVE ===== */ @media (max-width: 1024px) { .canvas-container { width: 50%; opacity: 0.7; } .calculator-content { grid-template-columns: 1fr; } .footer-content { grid-template-columns: 1fr; gap: 3rem; } } @media (max-width: 768px) { .hero-section { padding-top: 100px; } .hero-buttons { flex-direction: column; } .canvas-container { position: relative; width: 100%; height: 400px; margin-top: 2rem; } .nav-menu { display: none; } .mobile-menu-btn { display: flex; } .nav-cta { display: none; } .hero-stats { flex-direction: column; gap: 1.5rem; } .cta-form { flex-direction: column; } .footer-links { grid-template-columns: 1fr; gap: 2rem; } } @media (max-width: 480px) { .section-title { font-size: 2rem; } .hero-title { font-size: 2rem; } .calculator-tabs { flex-direction: column; } .tab-btn { width: 100%; justify-content: center; } .knowledge-grid { grid-template-columns: 1fr; } } /* ===== UTILITY CLASSES ===== */ .container { max-width: 1400px; margin: 0 auto; padding: 0 2rem; } .section-header { text-align: center; margin-bottom: 3rem; } .section-subtitle { color: rgba(255, 255, 255, 0.7); font-size: 1.1rem; max-width: 600px; margin: 0 auto; } /* Scrollbar Styling */ ::-webkit-scrollbar { width: 10px; } ::-webkit-scrollbar-track { background: var(--space-black); } ::-webkit-scrollbar-thumb { background: var(--comet-gray); border-radius: 5px; } ::-webkit-scrollbar-thumb:hover { background: var(--neon-cyan); } /* Selection */ ::selection { background: rgba(0, 243, 255, 0.3); color: var(--star-white); } /* Focus Styles */ :focus-visible { outline: 2px solid var(--neon-cyan); outline-offset: 2px; }
// Main Application Controller class ProdjectNavigator { constructor() { this.init(); } init() { this.setupEventListeners(); this.animateStats(); this.setupScrollAnimations(); this.initMobileMenu(); this.setupCalculatorInputs(); this.loadContent(); // Remove preloader after everything loads window.addEventListener('load', () => { setTimeout(() => { document.querySelector('.preloader').classList.add('fade-out'); setTimeout(() => { document.querySelector('.preloader').style.display = 'none'; }, 500); }, 1500); }); } setupEventListeners() { // Start Journey Button document.getElementById('startJourney')?.addEventListener('click', () => { this.scrollToSection('calculator'); this.triggerConfetti(); }); // Navigation document.querySelectorAll('.nav-link').forEach(link => { link.addEventListener('click', (e) => { e.preventDefault(); const target = e.target.getAttribute('href'); if (target.startsWith('#')) { this.scrollToSection(target.substring(1)); } }); }); // Watch Demo Button document.getElementById('watchDemo')?.addEventListener('click', () => { this.showVideoModal(); }); // Download Report document.getElementById('downloadReport')?.addEventListener('click', () => { this.generateReport(); }); // Start Game document.getElementById('startGame')?.addEventListener('click', () => { this.startRiskGame(); }); // CTA Form document.querySelector('.btn-cta')?.addEventListener('click', (e) => { e.preventDefault(); this.handleCTASubmission(); }); // Tab Switching document.querySelectorAll('.tab-btn').forEach(tab => { tab.addEventListener('click', (e) => { this.switchTab(e.target.dataset.tab); }); }); // Range Inputs document.querySelectorAll('input[type="range"]').forEach(range => { range.addEventListener('input', (e) => { this.updateRangeValue(e.target); }); }); } scrollToSection(sectionId) { const section = document.getElementById(sectionId); if (section) { const offset = 100; const position = section.offsetTop - offset; window.scrollTo({ top: position, behavior: 'smooth' }); } } animateStats() { const stats = document.querySelectorAll('.stat-number'); const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const stat = entry.target; const target = parseInt(stat.dataset.count); const duration = 2000; const step = target / (duration / 16); let current = 0; const timer = setInterval(() => { current += step; if (current >= target) { current = target; clearInterval(timer); } stat.textContent = Math.floor(current); }, 16); observer.unobserve(stat); } }); }, { threshold: 0.5 }); stats.forEach(stat => observer.observe(stat)); } setupScrollAnimations() { const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -100px 0px' }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('animate-in'); } }); }, observerOptions); // Observe all sections document.querySelectorAll('section').forEach(section => { section.classList.add('fade-up'); observer.observe(section); }); // Add CSS for animations const style = document.createElement('style'); style.textContent = ` .fade-up { opacity: 0; transform: translateY(30px); transition: opacity 0.8s ease, transform 0.8s ease; } .animate-in { opacity: 1; transform: translateY(0); } `; document.head.appendChild(style); } initMobileMenu() { const menuBtn = document.querySelector('.mobile-menu-btn'); const navMenu = document.querySelector('.nav-menu'); if (!menuBtn || !navMenu) return; menuBtn.addEventListener('click', () => { navMenu.style.display = navMenu.style.display === 'flex' ? 'none' : 'flex'; menuBtn.classList.toggle('active'); }); // Close menu on link click document.querySelectorAll('.nav-link').forEach(link => { link.addEventListener('click', () => { navMenu.style.display = 'none'; menuBtn.classList.remove('active'); }); }); } setupCalculatorInputs() { // This will be populated by calculator.js console.log('Calculator inputs ready for initialization'); } switchTab(tabId) { // Update active tab button document.querySelectorAll('.tab-btn').forEach(tab => { tab.classList.toggle('active', tab.dataset.tab === tabId); }); // Show active tab content document.querySelectorAll('.tab-content').forEach(content => { content.classList.toggle('active', content.id === `tab-${tabId}`); }); // Update calculator for selected tab if (window.calculator) { window.calculator.updateForTab(tabId); } } updateRangeValue(rangeInput) { const valueDisplay = rangeInput.parentElement.querySelector('.range-value'); if (valueDisplay) { valueDisplay.textContent = `${rangeInput.value}${rangeInput.dataset.suffix || ''}`; } // Update calculator if it exists if (window.calculator) { window.calculator.calculate(); } } showVideoModal() { const modal = document.createElement('div'); modal.className = 'video-modal'; modal.innerHTML = ` `; document.body.appendChild(modal); // Add styles const styles = ` .video-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.9); display: flex; justify-content: center; align-items: center; z-index: 2000; } .modal-content { position: relative; max-width: 800px; width: 90%; } .modal-close { position: absolute; top: -40px; right: 0; background: none; border: none; color: white; font-size: 2rem; cursor: pointer; } .video-container { position: relative; padding-bottom: 56.25%; height: 0; } .video-container iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } `; const styleSheet = document.createElement('style'); styleSheet.textContent = styles; document.head.appendChild(styleSheet); // Close modal modal.querySelector('.modal-close').addEventListener('click', () => { document.body.removeChild(modal); document.head.removeChild(styleSheet); }); modal.addEventListener('click', (e) => { if (e.target === modal) { document.body.removeChild(modal); document.head.removeChild(styleSheet); } }); } generateReport() { // Generate PDF report const totalCost = document.getElementById('totalCost').textContent; const timeline = document.getElementById('timeline').textContent; const successRate = document.getElementById('successRate').textContent; const reportData = { totalCost, timeline, successRate, generatedAt: new Date().toLocaleString('ru-RU'), projectType: document.querySelector('.tab-btn.active').textContent }; // In real app, this would generate actual PDF alert(`Отчет сгенерирован!\n\nСтоимость: ${totalCost} ₽\nСроки: ${timeline}\nВероятность успеха: ${successRate}\n\nСсылка на скачивание отправлена на email.`); // Track conversion this.trackEvent('report_downloaded', reportData); } startRiskGame() { if (window.riskGame) { window.riskGame.start(); } else { console.error('Risk game not loaded'); } } handleCTASubmission() { const emailInput = document.querySelector('.email-input'); const email = emailInput.value; if (!this.validateEmail(email)) { emailInput.style.borderColor = '#ff0000'; alert('Пожалуйста, введите корректный email'); return; } // Simulate API call emailInput.disabled = true; document.querySelector('.btn-cta').textContent = 'Отправляем...'; setTimeout(() => { alert('Спасибо! Доступ к PRO-версии отправлен на вашу почту.'); emailInput.value = ''; emailInput.disabled = false; document.querySelector('.btn-cta').textContent = 'Запустить PRO-версию'; // Track conversion this.trackEvent('pro_version_requested', { email }); }, 1500); } validateEmail(email) { const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return re.test(email); } triggerConfetti() { if (typeof confetti === 'function') { confetti({ particleCount: 100, spread: 70, origin: { y: 0.6 } }); } } loadContent() { // Load knowledge base articles fetch('content/articles.json') .then(response => response.json()) .then(articles => { this.displayArticles(articles); }) .catch(error => { console.error('Error loading articles:', error); this.displayFallbackArticles(); }); } displayArticles(articles) { const container = document.querySelector('.knowledge-grid'); if (!container) return; container.innerHTML = articles.map(article => `
${article.category}

${article.title}

${article.description}

???? ${article.readTime} мин ???? ${article.date}
`).join(''); } displayFallbackArticles() { const fallbackArticles = [ { title: "Agile vs Waterfall: Выбор методологии", description: "Полное руководство по выбору между гибкими и каскадными методологиями управления проектами.", category: "Методологии", readTime: "8", date: "2024-01-15" }, { title: "North Star Metric для вашего продукта", description: "Как найти и использовать главную метрику продукта для принятия стратегических решений.", category: "Метрики", readTime: "12", date: "2024-01-10" }, { title: "Roadmap продукта: От идеи до релиза", description: "Пошаговое руководство по созданию и ведению roadmap продукта.", category: "Инструменты", readTime: "15", date: "2024-01-05" } ]; this.displayArticles(fallbackArticles); } trackEvent(eventName, data = {}) { // Integrate with analytics (Google Analytics, Yandex.Metrica, etc.) console.log(`Tracking: ${eventName}`, data); // Example for GA4 if (typeof gtag === 'function') { gtag('event', eventName, data); } } } // Initialize when DOM is ready document.addEventListener('DOMContentLoaded', () => { window.app = new ProdjectNavigator(); // Load additional libraries if (typeof AOS !== 'undefined') { AOS.init({ duration: 1000, once: true, offset: 100 }); } // Load confetti library if not present if (typeof confetti === 'undefined') { const script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/canvas-confetti@1.5.1/dist/confetti.browser.min.js'; document.head.appendChild(script); } });
class SpaceUniverse { constructor() { this.scene = null; this.camera = null; this.renderer = null; this.controls = null; this.planets = []; this.stars = []; this.init(); this.animate(); } init() { // Scene this.scene = new THREE.Scene(); this.scene.fog = new THREE.FogExp2(0x0a0e17, 0.001); // Camera this.camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); this.camera.position.z = 50; // Renderer const canvas = document.getElementById('spaceCanvas'); this.renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true }); this.renderer.setSize(canvas.clientWidth, canvas.clientHeight); this.renderer.setPixelRatio(window.devicePixelRatio); // Controls this.controls = new THREE.OrbitControls(this.camera, this.renderer.domElement); this.controls.enableDamping = true; this.controls.dampingFactor = 0.05; this.controls.minDistance = 20; this.controls.maxDistance = 200; this.controls.maxPolarAngle = Math.PI / 2; // Lighting this.setupLighting(); // Create universe this.createStars(); this.createPlanets(); this.createNebulas(); // Handle resize window.addEventListener('resize', () => this.onWindowResize()); // Handle planet clicks canvas.addEventListener('click', (event) => this.handleClick(event)); } setupLighting() { // Ambient light const ambientLight = new THREE.AmbientLight(0xffffff, 0.1); this.scene.add(ambientLight); // Point lights for planets const sunLight = new THREE.PointLight(0x00f3ff, 2, 100); sunLight.position.set(0, 0, 0); this.scene.add(sunLight); // Directional light const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5); directionalLight.position.set(5, 3, 5); this.scene.add(directionalLight); } createStars() { const starGeometry = new THREE.BufferGeometry(); const starCount = 5000; const positions = new Float32Array(starCount * 3); const sizes = new Float32Array(starCount); for (let i = 0; i < starCount; i++) { // Random position in sphere const radius = 300; const theta = Math.random() * Math.PI * 2; const phi = Math.acos((Math.random() * 2) - 1); positions[i * 3] = radius * Math.sin(phi) * Math.cos(theta); positions[i * 3 + 1] = radius * Math.sin(phi) * Math.sin(theta); positions[i * 3 + 2] = radius * Math.cos(phi); sizes[i] = Math.random() * 2; } starGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); starGeometry.setAttribute('size', new THREE.BufferAttribute(sizes, 1)); const starMaterial = new THREE.PointsMaterial({ color: 0xffffff, size: 0.1, transparent: true, opacity: 0.8 }); const stars = new THREE.Points(starGeometry, starMaterial); this.scene.add(stars); this.stars.push(stars); } createPlanets() { const planetsData = [ { name: 'initiation', label: 'Планета Инициации', color: 0x00f3ff, radius: 3, distance: 20, speed: 0.005, description: 'Определение целей и стейкхолдеров' }, { name: 'planning', label: 'Планета Планирования', color: 0x9d00ff, radius: 4, distance: 30, speed: 0.003, description: 'Создание roadmap и оценка ресурсов' }, { name: 'execution', label: 'Планета Исполнения', color: 0xff00ff, radius: 5, distance: 45, speed: 0.002, description: 'Управление командой и процессами' }, { name: 'control', label: 'Планета Контроля', color: 0x00ff9d, radius: 3.5, distance: 60, speed: 0.004, description: 'Мониторинг и управление рисками' }, { name: 'closure', label: 'Планета Завершения', color: 0xff9d00, radius: 4.5, distance: 75, speed: 0.0015, description: 'Завершение проекта и анализ результатов' } ]; planetsData.forEach((data, index) => { const planet = this.createPlanet(data); this.planets.push({ ...data, mesh: planet, angle: (index / planetsData.length) * Math.PI * 2 }); this.scene.add(planet); }); } createPlanet(data) { const geometry = new THREE.SphereGeometry(data.radius, 32, 32); const material = new THREE.MeshPhongMaterial({ color: data.color, shininess: 100, emissive: data.color, emissiveIntensity: 0.2 }); const planet = new THREE.Mesh(geometry, material); // Add glow effect const glowGeometry = new THREE.SphereGeometry(data.radius * 1.2, 32, 32); const glowMaterial = new THREE.MeshBasicMaterial({ color: data.color, transparent: true, opacity: 0.3 }); const glow = new THREE.Mesh(glowGeometry, glowMaterial); planet.add(glow); // Add ring for some planets if (data.name === 'control' || data.name === 'closure') { const ringGeometry = new THREE.RingGeometry(data.radius * 1.5, data.radius * 1.7, 32); const ringMaterial = new THREE.MeshBasicMaterial({ color: data.color, side: THREE.DoubleSide, transparent: true, opacity: 0.6 }); const ring = new THREE.Mesh(ringGeometry, ringMaterial); ring.rotation.x = Math.PI / 2; planet.add(ring); } // Add label const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); canvas.width = 256; canvas.height = 128; context.fillStyle = 'rgba(0, 0, 0, 0)'; context.fillRect(0, 0, 256, 128); context.font = 'bold 24px Arial'; context.fillStyle = `rgb(${data.color >> 16}, ${(data.color >> 8) & 255}, ${data.color & 255})`; context.textAlign = 'center'; context.fillText(data.label, 128, 64); const texture = new THREE.CanvasTexture(canvas); const spriteMaterial = new THREE.SpriteMaterial({ map: texture }); const sprite = new THREE.Sprite(spriteMaterial); sprite.scale.set(15, 7.5, 1); sprite.position.y = data.radius * 2.5; planet.add(sprite); return planet; } createNebulas() { // Create nebula-like particle clouds const nebulaCount = 3; for (let i = 0; i < nebulaCount; i++) { const nebulaGeometry = new THREE.BufferGeometry(); const particleCount = 1000; const positions = new Float32Array(particleCount * 3); const colors = new Float32Array(particleCount * 3); const center = new THREE.Vector3( (Math.random() - 0.5) * 100, (Math.random() - 0.5) * 100, (Math.random() - 0.5) * 100 ); const color = new THREE.Color(); color.setHSL(Math.random(), 0.8, 0.5); for (let j = 0; j < particleCount; j++) { const radius = Math.random() * 20; const theta = Math.random() * Math.PI * 2; const phi = Math.acos((Math.random() * 2) - 1); positions[j * 3] = center.x + radius * Math.sin(phi) * Math.cos(theta); positions[j * 3 + 1] = center.y + radius * Math.sin(phi) * Math.sin(theta); positions[j * 3 + 2] = center.z + radius * Math.cos(phi); colors[j * 3] = color.r; colors[j * 3 + 1] = color.g; colors[j * 3 + 2] = color.b; } nebulaGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); nebulaGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3)); const nebulaMaterial = new THREE.PointsMaterial({ size: 0.5, vertexColors: true, transparent: true, opacity: 0.3, blending: THREE.AdditiveBlending }); const nebula = new THREE.Points(nebulaGeometry, nebulaMaterial); this.scene.add(nebula); } } animate() { requestAnimationFrame(() => this.animate()); // Rotate planets this.planets.forEach(planet => { planet.angle += planet.speed; planet.mesh.position.x = Math.cos(planet.angle) * planet.distance; planet.mesh.position.z = Math.sin(planet.angle) * planet.distance; // Rotate planet on its axis planet.mesh.rotation.y += 0.01; }); // Rotate stars slowly this.stars.forEach(stars => { stars.rotation.y += 0.0001; }); this.controls.update(); this.renderer.render(this.scene, this.camera); } onWindowResize() { const canvas = document.getElementById('spaceCanvas'); this.camera.aspect = canvas.clientWidth / canvas.clientHeight; this.camera.updateProjectionMatrix(); this.renderer.setSize(canvas.clientWidth, canvas.clientHeight); } handleClick(event) { const canvas = document.getElementById('spaceCanvas'); const rect = canvas.getBoundingClientRect(); const mouse = new THREE.Vector2( ((event.clientX - rect.left) / canvas.clientWidth) * 2 - 1, -((event.clientY - rect.top) / canvas.clientHeight) * 2 + 1 ); const raycaster = new THREE.Raycaster(); raycaster.setFromCamera(mouse, this.camera); const intersects = raycaster.intersectObjects( this.planets.map(p => p.mesh) ); if (intersects.length > 0) { const planetMesh = intersects[0].object; const planetData = this.planets.find(p => p.mesh === planetMesh); if (planetData) { this.showPlanetInfo(planetData); // Animate camera to planet const targetPosition = planetMesh.position.clone(); targetPosition.multiplyScalar(0.5); new TWEEN.Tween(this.camera.position) .to(targetPosition, 2000) .easing(TWEEN.Easing.Quadratic.Out) .start(); } } } showPlanetInfo(planetData) { const infoPanel = document.querySelector('.planet-info'); if (infoPanel) { infoPanel.querySelector('h3').textContent = planetData.label; infoPanel.querySelector('p').textContent = planetData.description; // Show info panel infoPanel.style.opacity = '0'; infoPanel.style.display = 'block'; setTimeout(() => { infoPanel.style.opacity = '1'; infoPanel.style.transform = 'translateY(0)'; }, 100); } // Navigate to corresponding section setTimeout(() => { const sectionMap = { 'initiation': 'knowledge', 'planning': 'calculator', 'execution': 'tools', 'control': 'risk', 'closure': 'cta' }; if (sectionMap[planetData.name]) { window.app?.scrollToSection(sectionMap[planetData.name]); } }, 1000); } } // Initialize when page loads window.addEventListener('load', () => { const canvas = document.getElementById('spaceCanvas'); if (canvas) { // Load TWEEN if not already loaded if (typeof TWEEN === 'undefined') { const script = document.createElement('script'); script.src = 'https://cdnjs.cloudflare.com/ajax/libs/tween.js/18.6.4/tween.umd.js'; script.onload = () => { window.spaceUniverse = new SpaceUniverse(); }; document.head.appendChild(script); } else { window.spaceUniverse = new SpaceUniverse(); } } });
class ProjectCalculator { constructor() { this.currentTab = 'project'; this.inputs = {}; this.results = {}; this.chart = null; this.init(); } init() { this.setupInputs(); this.setupChart(); this.calculate(); // Listen for tab changes document.querySelectorAll('.tab-btn').forEach(tab => { tab.addEventListener('click', (e) => { this.currentTab = e.target.dataset.tab; this.updateForTab(this.currentTab); }); }); // Listen for input changes document.addEventListener('input', (e) => { if (e.target.matches('.calc-input, .calc-range, .calc-select')) { this.updateInputValue(e.target); this.calculate(); } }); } setupInputs() { const inputsConfig = { project: [ { id: 'teamSize', label: 'Размер команды', type: 'range', min: 1, max: 50, value: 5, step: 1, suffix: ' чел', costPer: 80000 }, { id: 'timeline', label: 'Срок реализации', type: 'range', min: 1, max: 36, value: 6, step: 1, suffix: ' мес', multiplier: 1 }, { id: 'complexity', label: 'Сложность проекта', type: 'range', min: 1, max: 10, value: 5, step: 1, suffix: '/10', multiplier: 1.2 }, { id: 'techStack', label: 'Технологический стек', type: 'select', options: [ { value: 'basic', label: 'Базовый (CMS, WordPress)', multiplier: 1 }, { value: 'standard', label: 'Стандартный (React, Node.js)', multiplier: 1.5 }, { value: 'advanced', label: 'Продвинутый (AI, Blockchain)', multiplier: 2.5 } ], value: 'standard' }, { id: 'infrastructure', label: 'Инфраструктура', type: 'range', min: 5000, max: 100000, value: 20000, step: 5000, suffix: ' ₽/мес' } ], product: [ { id: 'marketSize', label: 'Размер рынка', type: 'select', options: [ { value: 'niche', label: 'Нишевый (<1M пользователей)', multiplier: 1 }, { value: 'medium', label: 'Средний (1-10M пользователей)', multiplier: 1.8 }, { value: 'mass', label: 'Массовый (>10M пользователей)', multiplier: 2.5 } ], value: 'medium' }, { id: 'developmentTime', label: 'Время разработки MVP', type: 'range', min: 1, max: 24, value: 6, step: 1, suffix: ' мес' }, { id: 'teamProduct', label: 'Команда продукта', type: 'range', min: 2, max: 20, value: 6, step: 1, suffix: ' чел' }, { id: 'marketingBudget', label: 'Маркетинговый бюджет', type: 'range', min: 100000, max: 5000000, value: 500000, step: 100000, suffix: ' ₽' }, { id: 'scaling', label: 'Масштабирование', type: 'select', options: [ { value: 'low', label: 'Низкое', multiplier: 1 }, { value: 'medium', label: 'Среднее', multiplier: 1.5 }, { value: 'high', label: 'Высокое', multiplier: 2 } ], value: 'medium' } ], startup: [ { id: 'fundingRound', label: 'Раунд финансирования', type: 'select', options: [ { value: 'preSeed', label: 'Pre-Seed', multiplier: 1 }, { value: 'seed', label: 'Seed', multiplier: 2 }, { value: 'seriesA', label: 'Series A', multiplier: 5 } ], value: 'seed' }, { id: 'runway', label: 'Runway (запас времени)', type: 'range', min: 6, max: 36, value: 18, step: 1, suffix: ' мес' }, { id: 'teamStartup', label: 'Размер команды', type: 'range', min: 2, max: 15, value: 5, step: 1, suffix: ' чел' }, { id: 'burnRate', label: 'Месячные расходы', type: 'range', min: 500000, max: 5000000, value: 1500000, step: 50000, suffix: ' ₽/мес' }, { id: 'valuation', label: 'Ожидаемая оценка', type: 'select', options: [ { value: 'conservative', label: 'Консервативная', multiplier: 1 }, { value: 'market', label: 'Рыночная', multiplier: 1.5 }, { value: 'aggressive', label: 'Агрессивная', multiplier: 2 } ], value: 'market' } ] }; this.inputsConfig = inputsConfig; this.renderInputs(); } renderInputs() { const container = document.querySelector('.calculator-inputs'); if (!container) return; const inputs = this.inputsConfig[this.currentTab]; container.innerHTML = inputs.map(input => { let inputHTML = ''; if (input.type === 'range') { inputHTML = `
${input.min}${input.suffix} ${input.max}${input.suffix}
`; } else if (input.type === 'select') { const options = input.options.map(opt => `` ).join(''); inputHTML = `
`; } return inputHTML; }).join(''); // Initialize values inputs.forEach(input => { const element = document.getElementById(input.id); if (element) { this.inputs[input.id] = { value: parseFloat(element.value), config: input }; } }); } updateForTab(tabId) { this.currentTab = tabId; this.renderInputs(); this.calculate(); } updateInputValue(element) { const value = element.type === 'range' ? parseFloat(element.value) : element.value; const suffix = element.dataset.suffix || ''; // Update value display for range inputs if (element.type === 'range') { const valueDisplay = element.parentElement.querySelector('.range-value'); if (valueDisplay) { valueDisplay.textContent = `${value}${suffix}`; } } // Update stored value this.inputs[element.id] = { value: value, config: this.inputs[element.id]?.config }; } calculate() { const inputs = this.inputsConfig[this.currentTab]; let totalCost = 0; let breakdown = {}; inputs.forEach(input => { const element = document.getElementById(input.id); if (!element) return; let value = this.inputs[input.id]?.value || parseFloat(element.value); // Calculate cost for this input let cost = 0; switch (input.id) { case 'teamSize': cost = value * (input.costPer || 80000) * 6; // 6 months average breakdown['Команда'] = cost; break; case 'timeline': case 'developmentTime': // Timeline affects other costs const timelineMultiplier = value / 6; if (breakdown['Команда']) { breakdown['Команда'] *= timelineMultiplier; } breakdown['Сроки'] = value; break; case 'complexity': cost = value * 100000 * (input.multiplier || 1); breakdown['Сложность'] = cost; break; case 'techStack': const stackMultiplier = input.options.find(o => o.value === value)?.multiplier || 1; cost = 500000 * stackMultiplier; breakdown['Технологии'] = cost; break; case 'infrastructure': cost = value * 12; // Annual breakdown['Инфраструктура'] = cost; break; case 'marketingBudget': cost = value; breakdown['Маркетинг'] = cost; break; case 'burnRate': const runway = this.inputs['runway']?.value || 18; cost = value * runway; breakdown['Общие расходы'] = cost; break; default: if (input.multiplier) { cost = 500000 * input.multiplier; breakdown[input.label] = cost; } } totalCost += cost; }); // Apply tab-specific adjustments switch (this.currentTab) { case 'project': totalCost *= 1.1; // 10% contingency break; case 'product': totalCost *= 1.25; // 25% for product development break; case 'startup': totalCost *= 1.5; // 50% for startup risks break; } // Calculate additional metrics const timeline = this.inputs['timeline']?.value || this.inputs['developmentTime']?.value || this.inputs['runway']?.value || 6; // Success rate calculation let successRate = 70; // Base 70% // Adjust based on complexity if (this.inputs['complexity']) { successRate -= (this.inputs['complexity'].value - 5) * 3; } // Adjust based on team size if (this.inputs['teamSize']) { const teamSize = this.inputs['teamSize'].value; if (teamSize < 3) successRate -= 10; if (teamSize > 15) successRate -= 15; } successRate = Math.max(20, Math.min(95, successRate)); // ROI calculation let roi = 120; // Base 120% if (this.currentTab === 'startup') { const fundingRound = this.inputs['fundingRound']?.value; if (fundingRound === 'preSeed') roi = 300; if (fundingRound === 'seed') roi = 200; if (fundingRound === 'seriesA') roi = 150; } // Update results this.results = { totalCost: Math.round(totalCost), timeline: `${timeline} месяцев`, successRate: `${Math.round(successRate)}%`, roi: `${roi}%`, breakdown: breakdown }; this.updateDisplay(); this.updateChart(); } updateDisplay() { // Update total cost const totalElement = document.getElementById('totalCost'); if (totalElement) { totalElement.textContent = this.formatNumber(this.results.totalCost); } // Update timeline const timelineElement = document.getElementById('timeline'); if (timelineElement) { timelineElement.textContent = this.results.timeline; } // Update success rate const successElement = document.getElementById('successRate'); if (successElement) { successElement.textContent = this.results.successRate; // Color code based on percentage const percent = parseInt(this.results.successRate); if (percent >= 80) { successElement.style.color = '#00ff9d'; } else if (percent >= 60) { successElement.style.color = '#ff9d00'; } else { successElement.style.color = '#ff0000'; } } // Update ROI const roiElement = document.getElementById('roi'); if (roiElement) { roiElement.textContent = this.results.roi; // Color code based on ROI const percent = parseInt(this.results.roi); if (percent >= 200) { roiElement.style.color = '#00ff9d'; } else if (percent >= 100) { roiElement.style.color = '#ff9d00'; } else { roiElement.style.color = '#ff0000'; } } } setupChart() { const ctx = document.getElementById('costChart'); if (!ctx) return; // Destroy existing chart if (this.chart) { this.chart.destroy(); } this.chart = new Chart(ctx, { type: 'doughnut', data: { labels: ['Команда', 'Технологии', 'Маркетинг', 'Инфраструктура', 'Резерв'], datasets: [{ data: [40, 25, 20, 10, 5], backgroundColor: [ '#00f3ff', '#9d00ff', '#ff00ff', '#00ff9d', '#ff9d00' ], borderWidth: 0, hoverOffset: 15 }] }, options: { responsive: true, maintainAspectRatio: true, plugins: { legend: { position: 'bottom', labels: { color: '#ffffff', padding: 20, font: { family: 'Exo 2', size: 12 } } }, tooltip: { backgroundColor: 'rgba(10, 14, 23, 0.9)', titleColor: '#00f3ff', bodyColor: '#ffffff', borderColor: '#00f3ff', borderWidth: 1, callbacks: { label: (context) => { const label = context.label || ''; const value = context.raw || 0; return `${label}: ${value}%`; } } } }, cutout: '65%' } }); } updateChart() { if (!this.chart || !this.results.breakdown) return; // Calculate percentages from breakdown const total = this.results.totalCost; const breakdown = this.results.breakdown; const categories = { 'Команда': breakdown['Команда'] || 0, 'Технологии': breakdown['Технологии'] || breakdown['Сложность'] || 0, 'Маркетинг': breakdown['Маркетинг'] || 0, 'Инфраструктура': breakdown['Инфраструктура'] || 0, 'Резерв': total * 0.1 // 10% reserve }; // Convert to percentages const sum = Object.values(categories).reduce((a, b) => a + b, 0); const percentages = Object.values(categories).map(value => Math.round((value / sum) * 100) ); // Update chart data this.chart.data.datasets[0].data = percentages; this.chart.update(); } formatNumber(num) { return new Intl.NumberFormat('ru-RU').format(num); } generateReport() { // This would generate a detailed PDF report const report = { projectType: this.currentTab, timestamp: new Date().toISOString(), inputs: this.inputs, results: this.results, breakdown: this.results.breakdown }; console.log('Report generated:', report); // In production, this would send to backend for PDF generation return report; } } // Initialize calculator window.addEventListener('DOMContentLoaded', () => { window.calculator = new ProjectCalculator(); });
class RiskGame { constructor() { this.canvas = null; this.ctx = null; this.gameState = 'idle'; // idle, playing, gameover this.score = 0; this.lives = 3; this.level = 1; this.asteroids = []; this.projectiles = []; this.tools = []; this.lastSpawn = 0; this.spawnRate = 2000; // ms this.gameSpeed = 2; this.riskTypes = [ { name: 'Срыв дедлайна', color: '#ff9d00', speed: 1.5, health: 1, points: 100 }, { name: 'Уход ключевого разработчика', color: '#ff0000', speed: 2, health: 2, points: 200 }, { name: 'Изменение требований', color: '#ff00ff', speed: 1, health: 3, points: 300 }, { name: 'Бюджетные ограничения', color: '#9d00ff', speed: 1.2, health: 2, points: 250 }, { name: 'Технический долг', color: '#00f3ff', speed: 0.8, health: 4, points: 400 } ]; this.toolTypes = { mitigation: { name: 'Митeйшн', color: '#00ff9d', cooldown: 1000, lastUsed: 0 }, transfer: { name: 'Трансфер', color: '#009dff', cooldown: 1500, lastUsed: 0 }, acceptance: { name: 'Акцептанс', color: '#ff9d00', cooldown: 2000, lastUsed: 0 }, avoidance: { name: 'Избежание', color: '#ff0000', cooldown: 2500, lastUsed: 0 } }; this.init(); } init() { this.canvas = document.getElementById('riskCanvas'); if (!this.canvas) return; this.ctx = this.canvas.getContext('2d'); this.setupEventListeners(); this.setupTools(); this.gameLoop(); } setupEventListeners() { // Start game button document.getElementById('startGame')?.addEventListener('click', () => { this.start(); }); // Tool buttons document.querySelectorAll('.tool-btn').forEach(btn => { btn.addEventListener('click', (e) => { const tool = e.target.dataset.tool; this.useTool(tool); }); }); // Keyboard controls document.addEventListener('keydown', (e) => { if (this.gameState !== 'playing') return; switch(e.key) { case '1': case 'q': this.useTool('mitigation'); break; case '2': case 'w': this.useTool('transfer'); break; case '3': case 'e': this.useTool('acceptance'); break; case '4': case 'r': this.useTool('avoidance'); break; case ' ': this.shootProjectile(); break; } }); // Mouse controls for shooting this.canvas.addEventListener('click', (e) => { if (this.gameState !== 'playing') return; const rect = this.canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; this.shootProjectile(x, y); }); // Canvas resize window.addEventListener('resize', () => { this.resizeCanvas(); }); } setupTools() { this.tools = Object.keys(this.toolTypes).map(key => ({ type: key, ...this.toolTypes[key], active: false, progress: 0 })); } resizeCanvas() { const container = this.canvas.parentElement; this.canvas.width = container.clientWidth; this.canvas.height = Math.min(500, container.clientWidth * 0.6); } start() { this.gameState = 'playing'; this.score = 0; this.lives = 3; this.level = 1; this.asteroids = []; this.projectiles = []; this.lastSpawn = Date.now(); this.gameSpeed = 2; this.spawnRate = 2000; this.updateUI(); // Show game instructions this.showMessage('Уничтожайте риски! Используйте инструменты для защиты.'); } gameLoop() { this.clearCanvas(); if (this.gameState === 'playing') { this.update(); this.spawnAsteroids(); } this.draw(); requestAnimationFrame(() => this.gameLoop()); } clearCanvas() { this.ctx.fillStyle = '#0a0e17'; this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); // Draw starfield this.ctx.fillStyle = '#ffffff'; for (let i = 0; i < 100; i++) { const x = Math.random() * this.canvas.width; const y = Math.random() * this.canvas.height; const size = Math.random() * 2; this.ctx.fillRect(x, y, size, size); } } update() { const currentTime = Date.now(); // Update asteroids this.asteroids = this.asteroids.filter(asteroid => { asteroid.y += asteroid.speed * this.gameSpeed; // Check if asteroid reached bottom if (asteroid.y > this.canvas.height) { this.loseLife(); return false; } // Check collision with projectiles for (let i = this.projectiles.length - 1; i >= 0; i--) { const projectile = this.projectiles[i]; const dx = asteroid.x - projectile.x; const dy = asteroid.y - projectile.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < asteroid.radius + projectile.radius) { asteroid.health--; // Remove projectile this.projectiles.splice(i, 1); if (asteroid.health <= 0) { this.score += asteroid.points; this.showFloatingText(`+${asteroid.points}`, asteroid.x, asteroid.y, asteroid.color); // Chance to drop power-up if (Math.random() < 0.1) { this.createPowerUp(asteroid.x, asteroid.y); } return false; } } } // Apply active tool effects this.tools.forEach(tool => { if (tool.active) { const effect = this.getToolEffect(tool.type); if (effect) { effect(asteroid); } } }); return true; }); // Update projectiles this.projectiles = this.projectiles.filter(projectile => { projectile.y -= projectile.speed; projectile.lifetime--; return projectile.y > 0 && projectile.lifetime > 0; }); // Update tools cooldown this.tools.forEach(tool => { if (tool.active) { tool.progress = (currentTime - tool.lastUsed) / tool.cooldown; if (tool.progress >= 1) { tool.active = false; tool.progress = 0; } } }); // Update power-ups this.powerUps = (this.powerUps || []).filter(powerUp => { powerUp.y += 1; // Check collection by player (bottom of screen) if (powerUp.y > this.canvas.height - 50) { this.collectPowerUp(powerUp); return false; } return true; }); // Update level if (this.score >= this.level * 1000) { this.level++; this.gameSpeed *= 1.1; this.spawnRate = Math.max(500, this.spawnRate * 0.9); this.showMessage(`Уровень ${this.level}! Риски ускоряются.`); } // Update UI this.updateUI(); // Check game over if (this.lives <= 0) { this.gameOver(); } } spawnAsteroids() { const currentTime = Date.now(); if (currentTime - this.lastSpawn > this.spawnRate) { const riskType = this.riskTypes[Math.floor(Math.random() * this.riskTypes.length)]; this.asteroids.push({ x: Math.random() * (this.canvas.width - 100) + 50, y: -50, radius: 20 + Math.random() * 15, speed: riskType.speed + Math.random() * 0.5, health: riskType.health, maxHealth: riskType.health, color: riskType.color, name: riskType.name, points: riskType.points, rotation: Math.random() * Math.PI * 2, rotationSpeed: (Math.random() - 0.5) * 0.1 }); this.lastSpawn = currentTime; // Occasionally spawn multiple asteroids if (Math.random() < 0.3) { setTimeout(() => this.spawnAsteroids(), 500); } } } createPowerUp(x, y) { if (!this.powerUps) this.powerUps = []; const types = ['life', 'score', 'slow', 'rapid']; const type = types[Math.floor(Math.random() * types.length)]; let color, value; switch(type) { case 'life': color = '#00ff9d'; value = 1; break; case 'score': color = '#ff9d00'; value = 500; break; case 'slow': color = '#009dff'; value = 0.5; break; case 'rapid': color = '#ff00ff'; value = 2; break; } this.powerUps.push({ x, y, radius: 10, type, color, value }); } collectPowerUp(powerUp) { switch(powerUp.type) { case 'life': this.lives = Math.min(5, this.lives + powerUp.value); this.showFloatingText('+1 жизнь', powerUp.x, this.canvas.height - 50, powerUp.color); break; case 'score': this.score += powerUp.value; this.showFloatingText(`+${powerUp.value}`, powerUp.x, this.canvas.height - 50, powerUp.color); break; case 'slow': this.gameSpeed *= powerUp.value; setTimeout(() => { this.gameSpeed /= powerUp.value; }, 5000); this.showFloatingText('Замедление!', powerUp.x, this.canvas.height - 50, powerUp.color); break; case 'rapid': // Rapid fire for 5 seconds const originalSpawnRate = this.spawnRate; this.spawnRate = 300; setTimeout(() => { this.spawnRate = originalSpawnRate; }, 5000); this.showFloatingText('Рапид огонь!', powerUp.x, this.canvas.height - 50, powerUp.color); break; } } shootProjectile(x = this.canvas.width / 2, y = this.canvas.height) { if (this.gameState !== 'playing') return; this.projectiles.push({ x: x || this.canvas.width / 2, y: y || this.canvas.height, radius: 5, speed: 8, color: '#00f3ff', lifetime: 100 }); // Visual feedback this.showParticles(x, y, 5, '#00f3ff'); } useTool(toolType) { if (this.gameState !== 'playing') return; const tool = this.tools.find(t => t.type === toolType); if (!tool || tool.active) return; const currentTime = Date.now(); if (currentTime - tool.lastUsed < tool.cooldown) return; tool.active = true; tool.lastUsed = currentTime; tool.progress = 0; // Apply tool effect switch(toolType) { case 'mitigation': this.showMessage('Митeйшн: Снижена скорость рисков на 5 секунд'); break; case 'transfer': this.showMessage('Трансфер: Риски заморожены на 3 секунды'); break; case 'acceptance': this.showMessage('Акцептанс: Восстановлена 1 жизнь'); this.lives = Math.min(5, this.lives + 1); break; case 'avoidance': this.showMessage('Избежание: Уничтожены все мелкие риски'); this.asteroids = this.asteroids.filter(a => a.radius > 25); break; } this.updateUI(); } getToolEffect(toolType) { switch(toolType) { case 'mitigation': return (asteroid) => { asteroid.speed *= 0.5; }; case 'transfer': return (asteroid) => { asteroid.speed = 0; }; default: return null; } } loseLife() { this.lives--; this.showMessage(`Потеряна жизнь! Осталось: ${this.lives}`); this.updateUI(); // Screen shake effect this.shakeScreen(); } shakeScreen() { const originalX = this.canvas.style.marginLeft; const originalY = this.canvas.style.marginTop; let shake = 10; const shakeInterval = setInterval(() => { this.canvas.style.marginLeft = `${(Math.random() - 0.5) * shake}px`; this.canvas.style.marginTop = `${(Math.random() - 0.5) * shake}px`; shake *= 0.9; if (shake < 1) { clearInterval(shakeInterval); this.canvas.style.marginLeft = originalX; this.canvas.style.marginTop = originalY; } }, 30); } showFloatingText(text, x, y, color) { const floatingText = { text, x, y, color, alpha: 1, velocity: -2 }; if (!this.floatingTexts) this.floatingTexts = []; this.floatingTexts.push(floatingText); // Remove after animation setTimeout(() => { this.floatingTexts = this.floatingTexts.filter(t => t !== floatingText); }, 1000); } showParticles(x, y, count, color) { for (let i = 0; i < count; i++) { const particle = { x, y, vx: (Math.random() - 0.5) * 10, vy: (Math.random() - 0.5) * 10, radius: Math.random() * 3 + 1, color, life: 1 }; if (!this.particles) this.particles = []; this.particles.push(particle); } } showMessage(text) { this.message = { text, time: Date.now() }; setTimeout(() => { this.message = null; }, 3000); } draw() { // Draw player base (bottom of screen) this.ctx.fillStyle = '#00f3ff'; this.ctx.fillRect(this.canvas.width / 2 - 50, this.canvas.height - 20, 100, 20); // Draw player turret this.ctx.fillStyle = '#ffffff'; this.ctx.beginPath(); this.ctx.arc(this.canvas.width / 2, this.canvas.height - 20, 15, 0, Math.PI * 2); this.ctx.fill(); // Draw asteroids this.asteroids.forEach(asteroid => { // Asteroid body this.ctx.save(); this.ctx.translate(asteroid.x, asteroid.y); this.ctx.rotate(asteroid.rotation); this.ctx.fillStyle = asteroid.color; this.ctx.beginPath(); this.ctx.arc(0, 0, asteroid.radius, 0, Math.PI * 2); this.ctx.fill(); // Health bar const healthPercent = asteroid.health / asteroid.maxHealth; this.ctx.fillStyle = '#00ff9d'; this.ctx.fillRect(-asteroid.radius, -asteroid.radius - 10, asteroid.radius * 2 * healthPercent, 5); // Asteroid name this.ctx.fillStyle = '#ffffff'; this.ctx.font = '12px Arial'; this.ctx.textAlign = 'center'; this.ctx.fillText(asteroid.name, 0, -asteroid.radius - 15); this.ctx.restore(); // Update rotation asteroid.rotation += asteroid.rotationSpeed; }); // Draw projectiles this.projectiles.forEach(projectile => { this.ctx.fillStyle = projectile.color; this.ctx.beginPath(); this.ctx.arc(projectile.x, projectile.y, projectile.radius, 0, Math.PI * 2); this.ctx.fill(); }); // Draw power-ups (this.powerUps || []).forEach(powerUp => { this.ctx.fillStyle = powerUp.color; this.ctx.beginPath(); this.ctx.arc(powerUp.x, powerUp.y, powerUp.radius, 0, Math.PI * 2); this.ctx.fill(); // Icon based on type this.ctx.fillStyle = '#ffffff'; this.ctx.font = '10px Arial'; this.ctx.textAlign = 'center'; this.ctx.textBaseline = 'middle'; let icon = '?'; switch(powerUp.type) { case 'life': icon = '♥'; break; case 'score': icon = '★'; break; case 'slow': icon = '⏱'; break; case 'rapid': icon = '⚡'; break; } this.ctx.fillText(icon, powerUp.x, powerUp.y); }); // Draw particles (this.particles || []).forEach((particle, index) => { particle.x += particle.vx; particle.y += particle.vy; particle.life -= 0.02; this.ctx.globalAlpha = particle.life; this.ctx.fillStyle = particle.color; this.ctx.beginPath(); this.ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2); this.ctx.fill(); if (particle.life <= 0) { this.particles.splice(index, 1); } }); this.ctx.globalAlpha = 1; // Draw floating text (this.floatingTexts || []).forEach(text => { text.y += text.velocity; text.alpha -= 0.01; this.ctx.globalAlpha = text.alpha; this.ctx.fillStyle = text.color; this.ctx.font = 'bold 16px Arial'; this.ctx.textAlign = 'center'; this.ctx.fillText(text.text, text.x, text.y); }); this.ctx.globalAlpha = 1; // Draw message if (this.message) { const age = Date.now() - this.message.time; const alpha = age < 2500 ? 1 : 1 - ((age - 2500) / 500); this.ctx.globalAlpha = alpha; this.ctx.fillStyle = '#ffffff'; this.ctx.font = 'bold 20px Arial'; this.ctx.textAlign = 'center'; this.ctx.fillText(this.message.text, this.canvas.width / 2, 50); this.ctx.globalAlpha = 1; } // Draw tools cooldown this.drawTools(); // Draw game state if (this.gameState === 'gameover') { this.drawGameOver(); } else if (this.gameState === 'idle') { this.drawInstructions(); } } drawTools() { const toolWidth = 80; const startX = (this.canvas.width - (this.tools.length * toolWidth)) / 2; this.tools.forEach((tool, index) => { const x = startX + index * toolWidth; const y = this.canvas.height - 80; // Tool background this.ctx.fillStyle = tool.active ? tool.color : '#2a3a5a'; this.ctx.fillRect(x, y, toolWidth - 10, 40); // Cooldown overlay if (tool.progress < 1) { this.ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; this.ctx.fillRect(x, y + (1 - tool.progress) * 40, toolWidth - 10, tool.progress * 40); // Cooldown percentage this.ctx.fillStyle = '#ffffff'; this.ctx.font = '12px Arial'; this.ctx.textAlign = 'center'; this.ctx.fillText( `${Math.round((1 - tool.progress) * 100)}%`, x + (toolWidth - 10) / 2, y + 25 ); } // Tool name this.ctx.fillStyle = '#ffffff'; this.ctx.font = '10px Arial'; this.ctx.textAlign = 'center'; this.ctx.fillText(tool.name, x + (toolWidth - 10) / 2, y + 15); // Hotkey this.ctx.fillStyle = '#00f3ff'; this.ctx.font = 'bold 12px Arial'; this.ctx.fillText(index + 1, x + 10, y + 35); }); } drawInstructions() { this.ctx.fillStyle = 'rgba(0, 0, 0, 0.8)'; this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); this.ctx.fillStyle = '#00f3ff'; this.ctx.font = 'bold 24px Arial'; this.ctx.textAlign = 'center'; this.ctx.fillText('СИМУЛЯТОР РИСКОВ', this.canvas.width / 2, 100); this.ctx.fillStyle = '#ffffff'; this.ctx.font = '16px Arial'; this.ctx.fillText('Защитите проект от падающих рисков!', this.canvas.width / 2, 150); const instructions = [ '• Кликайте по рискам для уничтожения', '• Используйте инструменты (1-4) для защиты', '• Собирайте бонусы для усиления', '• Не пропустите более 3 рисков' ]; instructions.forEach((text, index) => { this.ctx.fillText(text, this.canvas.width / 2, 200 + index * 30); }); this.ctx.fillStyle = '#ff9d00'; this.ctx.font = 'bold 20px Arial'; this.ctx.fillText('Нажмите "Начать тренировку"', this.canvas.width / 2, 350); } drawGameOver() { this.ctx.fillStyle = 'rgba(0, 0, 0, 0.8)'; this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); this.ctx.fillStyle = '#ff0000'; this.ctx.font = 'bold 36px Arial'; this.ctx.textAlign = 'center'; this.ctx.fillText('МИССИЯ ПРОВАЛЕНА', this.canvas.width / 2, 150); this.ctx.fillStyle = '#ffffff'; this.ctx.font = '24px Arial'; this.ctx.fillText(`Счет: ${this.score}`, this.canvas.width / 2, 200); this.ctx.fillText(`Уровень: ${this.level}`, this.canvas.width / 2, 240); this.ctx.fillStyle = '#00f3ff'; this.ctx.font = '20px Arial'; this.ctx.fillText('Нажмите "Начать тренировку"', this.canvas.width / 2, 300); this.ctx.fillText('для новой попытки', this.canvas.width / 2, 330); // Draw risk management tips based on performance const tips = [ 'Совет: Используйте митейшн для частых рисков', 'Совет: Трансфер эффективен против крупных рисков', 'Совет: Собирайте бонусы для усиления', 'Совет: Чередуйте инструменты для максимального эффекта' ]; this.ctx.fillStyle = '#ff9d00'; this.ctx.font = '16px Arial'; this.ctx.fillText(tips[Math.floor(Math.random() * tips.length)], this.canvas.width / 2, 380); } updateUI() { document.getElementById('score').textContent = this.score; document.getElementById('lives').textContent = this.lives; document.getElementById('level').textContent = this.level; // Update tool buttons this.tools.forEach(tool => { const btn = document.querySelector(`.tool-btn[data-tool="${tool.type}"]`); if (btn) { btn.disabled = tool.active; btn.style.opacity = tool.active ? '0.5' : '1'; } }); } gameOver() { this.gameState = 'gameover'; // Show final score setTimeout(() => { alert(`Игра окончена!\n\nВаш счет: ${this.score}\nДостигнутый уровень: ${this.level}\n\nПопробуйте снова!`); }, 500); } } // Initialize game window.addEventListener('DOMContentLoaded', () => { window.riskGame = new RiskGame(); });
[ { "id": 1, "title": "Agile vs Waterfall: Полное руководство по выбору методологии", "description": "Подробное сравнение гибких и каскадных методологий управления проектами. Когда использовать Scrum, а когда Waterfall? Практические кейсы и рекомендации.", "category": "Методологии", "readTime": "12", "date": "2024-01-15", "content": "Полный текст статьи около 5000 знаков...", "keywords": ["agile", "waterfall", "scrum", "методологии", "управление проектами"], "image": "article1.jpg" }, { "id": 2, "title": "North Star Metric: Как найти главную метрику продукта", "description": "Пошаговое руководство по определению и внедрению North Star Metric. Примеры из практики Airbnb, Spotify и других успешных компаний.", "category": "Метрики", "readTime": "15", "date": "2024-01-10", "content": "Полный текст статьи около 6000 знаков...", "keywords": ["north star metric", "метрики продукта", "аналитика", "kpi"], "image": "article2.jpg" }, { "id": 3, "title": "Roadmap продукта: От стратегии до исполнения", "description": "Как создать эффективный roadmap, который поймут все стейкхолдеры. Шаблоны, инструменты и лучшие практики.", "category": "Инструменты", "readTime": "10", "date": "2024-01-05", "content": "Полный текст статьи около 4500 знаков...", "keywords": ["roadmap", "product roadmap", "планирование", "стратегия"], "image": "article3.jpg" }, { "id": 4, "title": "Управление рисками в IT-проектах: Практическое руководство", "description": "Полный цикл управления рисками: идентификация, оценка, мониторинг и митигация. Готовые чек-листы и шаблоны.", "category": "Риски", "readTime": "18", "date": "2024-01-01", "content": "Полный текст статьи около 7000 знаков...", "keywords": ["управление рисками", "risk management", "митигация", "оценка рисков"], "image": "article4.jpg" }, { "id": 5, "title": "Unit-экономика для продукта: Расчет и оптимизация", "description": "Как считать unit-экономику, какие метрики использовать и как оптимизировать каждый этап воронки. Кейсы из SaaS и маркетплейсов.", "category": "Финансы", "readTime": "20", "date": "2023-12-28", "content": "Полный текст статьи около 8000 знаков...", "keywords": ["unit-экономика", "cac", "ltv", "roi", "финансы продукта"], "image": "article5.jpg" }, { "id": 6, "title": "OKR vs KPI: Как правильно ставить цели команде", "description": "Подробное сравнение OKR и KPI с примерами внедрения. Как совмещать оба подхода для максимальной эффективности.", "category": "Цели", "readTime": "14", "date": "2023-12-25", "content": "Полный текст статьи около 5500 знаков...", "keywords": ["okr", "kpi", "постановка целей", "цели и ключевые результаты"], "image": "article6.jpg" }, { "id": 7, "title": "User Story Mapping: Визуализация пользовательского пути", "description": "Практическое руководство по созданию карт пользовательских историй. Инструменты, техники и примеры из реальных проектов.", "category": "UX/UI", "readTime": "11", "date": "2023-12-20", "content": "Полный текст статьи около 5000 знаков...", "keywords": ["user story mapping", "пользовательские истории", "ux", "customer journey"], "image": "article7.jpg" }, { "id": 8, "title": "Scrum: Полное руководство для начинающих", "description": "Пошаговое внедрение Scrum с нуля. Роли, артефакты, церемонии и инструменты для эффективной работы по Scrum.", "category": "Методологии", "readTime": "16", "date": "2023-12-15", "content": "Полный текст статьи около 6500 знаков...", "keywords": ["scrum", "agile", "спринт", "scrum мастер", "product owner"], "image": "article8.jpg" } ]