/
ncit
/
studiosite
Обзор
Документация
Войти
/
ncit
/
studiosite
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
pixelforge.html
897 строк
63 KB
ncit
Update case studies, HR assets, add tests and config
09 июн 2026, 11:24
09 июн 2026, 11:24
b01411d
Код
Авторство
О чём код?
<!DOCTYPE html> <html lang="ru" class="scroll-smooth"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>КодЭкспресс — Разработка ПО, мобильных приложений и IT-решений на заказ</title> <meta name="description" content="КодЭкспресс — разработка программного обеспечения, мобильных приложений, сложных IT-проектов и стартапов. AI/ML, highload-системы, облачная архитектура. Полный цикл от аналитики до запуска."> <meta name="keywords" content="разработка ПО, разработка мобильных приложений, разработка программного обеспечения, IT проекты, стартапы, разработка на заказ, мобильная разработка, веб разработка, AI ML, highload системы, Flutter, iOS, Android, облачные решения, DevOps, микросервисы"> <link rel="canonical" href="https://nikmobdev.ru/"> <meta name="robots" content="index, follow"> <meta name="author" content="КодЭкспресс"> <!-- Yandex Webmaster verification --> <meta name="yandex-verification" content="274d114689562785"> <!-- Open Graph --> <meta property="og:type" content="website"> <meta property="og:locale" content="ru_RU"> <meta property="og:site_name" content="КодЭкспресс"> <meta property="og:title" content="КодЭкспресс — Разработка ПО, мобильных приложений и IT-решений"> <meta property="og:description" content="Разработка программного обеспечения, мобильных приложений, сложных IT-проектов и стартапов. Полный цикл от аналитики до запуска."> <meta property="og:url" content="https://nikmobdev.ru/"> <meta property="og:image" content="https://nikmobdev.ru/og-image.png"> <meta property="og:image:width" content="1200"> <meta property="og:image:height" content="630"> <!-- Twitter Card --> <meta name="twitter:card" content="summary_large_image"> <meta name="twitter:title" content="КодЭкспресс — Разработка ПО и мобильных приложений"> <meta name="twitter:description" content="Разработка программного обеспечения, мобильных приложений, сложных IT-проектов и стартапов."> <meta name="twitter:image" content="https://nikmobdev.ru/og-image.png"> <!-- Preload fonts for faster rendering --> <link rel="preload" href="https://api.fontshare.com/v2/css?f[]=clash-grotesk@400,500,600,700&f[]=plus-jakarta-sans@300,400,500,600,700,800&display=swap" as="style"> <link rel="preconnect" href="https://cdn.fontshare.com" crossorigin> <link rel="preconnect" href="https://mc.yandex.ru"> <link rel="dns-prefetch" href="https://cdn.tailwindcss.com"> <link rel="dns-prefetch" href="https://code.iconify.design"> <script src="https://cdn.tailwindcss.com"></script> <script src="https://code.iconify.design/iconify-icon/1.0.7/iconify-icon.min.js" defer></script> <link href="https://api.fontshare.com/v2/css?f[]=clash-grotesk@400,500,600,700&f[]=plus-jakarta-sans@300,400,500,600,700,800&display=swap" rel="stylesheet"> <script> tailwind.config = { theme: { extend: { fontFamily: { sans: ['Plus Jakarta Sans', 'sans-serif'], display: ['Clash Grotesk', 'sans-serif'], }, colors: { bento: { bg: '#F2F2F7', lime: '#D9F85F', coral: '#FF6B6B', purple: '#A855F7', cyan: '#22D3EE', yellow: '#FCD34D', dark: '#18181B' } }, animation: { 'float': 'float 6s ease-in-out infinite', 'wiggle': 'wiggle 1s ease-in-out infinite', 'bounce-slow': 'bounce 3s infinite', }, keyframes: { float: { '0%, 100%': { transform: 'translateY(0)' }, '50%': { transform: 'translateY(-10px)' }, }, wiggle: { '0%, 100%': { transform: 'rotate(-3deg)' }, '50%': { transform: 'rotate(3deg)' }, } } } } } </script> <style> body { background-color: #F2F2F7; color: #18181B; overflow-x: hidden; } .bento-card { background: #ffffff; border-radius: 2.5rem; transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.02), 0 2px 4px -1px rgba(0, 0, 0, 0.02); overflow: hidden; position: relative; -webkit-mask-image: -webkit-radial-gradient(white, white); } .bento-card:hover { transform: translateY(-4px) scale-[1.01]; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.05), 0 10px 10px -5px rgba(0, 0, 0, 0.01); z-index: 10; } /* Smooth anchor scrolling offset */ html { scroll-padding-top: 120px; } @keyframes slideUp { 0%, 20% { transform: translateY(0); } 25%, 45% { transform: translateY(-4rem); } 50%, 70% { transform: translateY(-8rem); } 75%, 95% { transform: translateY(-12rem); } 100% { transform: translateY(0); } } .slogan-slider { animation: slideUp 20s ease-in-out infinite; } @keyframes hrCarousel { 0%, 21% { transform: translateX(0); } 25%, 46% { transform: translateX(-25%); } 50%, 71% { transform: translateX(-50%); } 75%, 96% { transform: translateX(-75%); } 100% { transform: translateX(0); } } .hr-carousel { animation: hrCarousel 16s ease-in-out infinite; } @keyframes coderagCarousel { 0%, 28% { transform: translateX(0); } 33%, 61% { transform: translateX(-33.333%); } 66%, 94% { transform: translateX(-66.666%); } 100% { transform: translateX(0); } } .coderag-carousel { animation: coderagCarousel 15s ease-in-out infinite; } .calc-btn:focus { outline: none; } .img-loading { background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); background-size: 200% 100%; animation: shimmer 1.5s infinite; } @keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } } @view-transition { navigation: auto; } </style> <!-- JSON-LD Structured Data --> <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Organization", "name": "КодЭкспресс", "url": "https://nikmobdev.ru", "description": "Разработка программного обеспечения, мобильных приложений, сложных IT-проектов и стартапов", "email": "nikmobdev@gmail.com", "contactPoint": { "@type": "ContactPoint", "contactType": "sales", "email": "nikmobdev@gmail.com", "url": "https://t.me/nikitaf", "availableLanguage": "Russian" }, "areaServed": "RU", "knowsAbout": ["Разработка ПО", "Мобильные приложения", "AI/ML", "Highload системы", "DevOps", "Flutter", "iOS", "Android"] } </script> <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "WebSite", "name": "КодЭкспресс", "url": "https://nikmobdev.ru", "inLanguage": "ru", "description": "Разработка ПО, мобильных приложений и IT-решений на заказ" } </script> <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Service", "serviceType": "Разработка программного обеспечения", "provider": { "@type": "Organization", "name": "КодЭкспресс" }, "areaServed": "RU", "hasOfferCatalog": { "@type": "OfferCatalog", "name": "IT-услуги", "itemListElement": [ {"@type": "Offer", "itemOffered": {"@type": "Service", "name": "Разработка мобильных приложений"}}, {"@type": "Offer", "itemOffered": {"@type": "Service", "name": "Разработка веб-приложений"}}, {"@type": "Offer", "itemOffered": {"@type": "Service", "name": "AI и Machine Learning решения"}}, {"@type": "Offer", "itemOffered": {"@type": "Service", "name": "Highload системы"}}, {"@type": "Offer", "itemOffered": {"@type": "Service", "name": "Cloud и DevOps"}}, {"@type": "Offer", "itemOffered": {"@type": "Service", "name": "IT-консалтинг для стартапов"}} ] } } </script> <!-- Yandex.Metrika counter --> <script type="text/javascript"> (function(m,e,t,r,i,k,a){ m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)}; m[i].l=1*new Date(); for (var j = 0; j < document.scripts.length; j++) {if (document.scripts[j].src === r) { return; }} k=e.createElement(t),a=e.getElementsByTagName(t)[0],k.async=1,k.src=r,a.parentNode.insertBefore(k,a) })(window, document,'script','https://mc.yandex.ru/metrika/tag.js?id=107243393', 'ym'); ym(107243393, 'init', {ssr:true, webvisor:true, clickmap:true, ecommerce:"dataLayer", referrer: document.referrer, url: location.href, accurateTrackBounce:true, trackLinks:true}); </script> <noscript><div><img src="https://mc.yandex.ru/watch/107243393" style="position:absolute; left:-9999px;" alt="" /></div></noscript> <!-- /Yandex.Metrika counter --> </head> <body> <div class="min-h-screen p-4 md:p-6 lg:p-8 max-w-[1600px] mx-auto"> <!-- Floating Navigation --> <nav class="fixed top-6 left-1/2 -translate-x-1/2 z-50 bg-white/90 backdrop-blur-xl border border-white/20 shadow-lg shadow-black/5 rounded-full px-6 py-3 flex items-center gap-8"> <a href="#" id="nav-home" class="flex items-center gap-2 group"> <div class="w-8 h-8 bg-black rounded-full flex items-center justify-center text-white font-bold text-lg group-hover:rotate-12 transition-transform"> К </div> <span class="font-display font-bold text-lg hidden sm:block">КодЭкспресс</span> </a> <div class="hidden md:flex items-center gap-6"> <a href="#services" id="nav-services" class="text-sm font-medium text-gray-500 hover:text-black transition-colors">Услуги</a> <a href="#work" id="nav-work" class="text-sm font-medium text-gray-500 hover:text-black transition-colors">Проекты</a> <a href="#contact" id="nav-contact" class="text-sm font-medium text-gray-500 hover:text-black transition-colors">Контакты</a> </div> <a href="#contact" id="nav-cta-btn" class="bg-black text-white px-5 py-2 rounded-full text-sm font-bold hover:bg-bento-purple transition-colors"> Обсудить проект </a> </nav> <!-- Main Bento Grid Layout --> <main class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 md:gap-6 pt-20"> <!-- 1. Hero Title Card (2x2) --> <div class="bento-card col-span-1 md:col-span-2 row-span-2 p-8 md:p-12 flex flex-col justify-between group"> <div> <div class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-gray-100 text-xs font-bold uppercase tracking-wide mb-6"> <span class="w-2 h-2 rounded-full bg-green-500 animate-pulse"></span> Открыты к проектам </div> <h1 class="font-display font-semibold text-[2rem] sm:text-[2.5rem] md:text-7xl leading-[0.95] tracking-tight break-words overflow-hidden"> Разработка ПО,<br> которое<br> <span class="text-gray-400">масштабируется.</span> </h1> </div> <div class="flex items-center gap-4 mt-8"> <div class="flex gap-3"> <span class="px-3 py-1 rounded-full bg-gray-100 text-xs font-bold">AI / ML</span> <span class="px-3 py-1 rounded-full bg-gray-100 text-xs font-bold">Web</span> <span class="px-3 py-1 rounded-full bg-gray-100 text-xs font-bold">Mobile</span> <span class="px-3 py-1 rounded-full bg-gray-100 text-xs font-bold">Highload</span> </div> </div> <div class="absolute top-8 right-8 w-24 h-24 bg-bento-lime rounded-full blur-2xl opacity-0 group-hover:opacity-50 transition-opacity duration-500"></div> </div> <!-- 2. Abstract Visual (2x1) --> <div class="bento-card col-span-1 md:col-span-2 min-h-[300px] relative overflow-hidden group"> <img src="engineering-approach.webp" alt="Инженерный подход" class="absolute inset-0 w-full h-full object-cover transition-transform duration-700 group-hover:scale-110"> <div class="absolute inset-0 bg-gradient-to-r from-bento-yellow/20 to-transparent mix-blend-overlay"></div> <div class="absolute bottom-4 left-4 sm:bottom-8 sm:left-8 bg-white/90 backdrop-blur-md px-4 py-2 sm:px-6 sm:py-3 rounded-2xl"> <p class="font-display font-bold text-base sm:text-xl">Инженерный подход</p> </div> </div> <!-- 3. Highload (1x1) --> <div class="bento-card p-8 flex flex-col items-center justify-center text-center gap-4 group"> <div class="text-5xl filter drop-shadow-xl transform group-hover:scale-125 transition-transform duration-300"> <iconify-icon icon="lucide:server"></iconify-icon> </div> <h3 class="font-display font-bold text-xl leading-tight"> Highload<br>системы </h3> <p class="text-sm text-gray-500">1K+ запросов/сек</p> </div> <!-- 4. Speed / Fast Work (1x1) with animated slogans --> <div class="bento-card bg-bento-lime p-8 flex flex-col items-center justify-center text-center group relative overflow-hidden"> <div class="absolute -right-4 -top-4 w-24 h-24 bg-white/30 rounded-full blur-xl"></div> <div class="h-16 overflow-hidden relative w-full"> <div class="slogan-slider absolute w-full"> <p class="font-display font-bold text-lg h-16 flex items-center justify-center">Эффективная разработка.<br>Надёжный результат.</p> <p class="font-display font-bold text-lg h-16 flex items-center justify-center">От концепции до<br>стабильного production.</p> <p class="font-display font-bold text-lg h-16 flex items-center justify-center">Качество, которое<br>масштабируется.</p> <p class="font-display font-bold text-lg h-16 flex items-center justify-center">Не быстро и дёшево.<br>Правильно и надолго.</p> </div> </div> </div> <!-- 5. Mobile App Service (1x2) --> <h2 class="sr-only">Услуги по разработке программного обеспечения</h2> <div id="services" class="bento-card row-span-2 bg-[#F5F5F7] p-8 flex flex-col relative overflow-hidden group"> <div class="z-10 relative"> <div class="w-12 h-12 bg-white rounded-xl shadow-sm flex items-center justify-center text-2xl mb-6"> <iconify-icon icon="lucide:smartphone"></iconify-icon> </div> <h3 class="font-display font-bold text-3xl mb-2 text-black">Разработка мобильных<br>приложений</h3> <p class="text-black/70 font-semibold text-sm">iOS & Android, Flutter & Native</p> <a href="/mobile-development.html" class="inline-flex items-center gap-1 text-sm font-bold text-black mt-4 hover:text-bento-purple transition-colors">Подробнее <iconify-icon icon="lucide:arrow-right"></iconify-icon></a> </div> <div class="absolute bottom-[-5%] right-[-15%] w-[65%] transition-transform duration-500 group-hover:translate-y-[-20px] z-[1]"> <img src="rxpay-app.webp" alt="RxPay Mobile App" loading="lazy" class="rounded-3xl shadow-2xl rotate-[-8deg] border-[4px] border-white/30 opacity-30"> </div> </div> <!-- 6. Full Dev Cycle Roadmap (2x2) --> <div class="bento-card col-span-1 md:col-span-2 row-span-2 bg-white p-8 md:p-10 flex flex-col justify-between relative overflow-hidden group"> <div> <span class="bg-black text-white px-4 py-2 rounded-full font-bold text-sm">Полный цикл</span> <h3 class="font-display font-bold text-3xl md:text-4xl mt-6 mb-2 text-black">Цикл разработки</h3> <p class="text-gray-500 font-medium text-sm mb-8">От идеи до поддержки — прозрачный процесс на каждом этапе.</p> </div> <!-- Roadmap steps --> <div class="grid grid-cols-2 md:grid-cols-3 gap-3"> <div class="bg-[#F0FDFA] rounded-2xl p-4 hover:bg-[#E0F7F4] transition-colors"> <div class="text-teal-600 text-2xl mb-2"><iconify-icon icon="lucide:search"></iconify-icon></div> <p class="font-bold text-sm text-black">Аналитика</p> <p class="text-xs text-gray-500 mt-1">Требования и исследование</p> </div> <div class="bg-[#F5F3FF] rounded-2xl p-4 hover:bg-[#EDE9FE] transition-colors"> <div class="text-violet-600 text-2xl mb-2"><iconify-icon icon="lucide:pen-tool"></iconify-icon></div> <p class="font-bold text-sm text-black">Дизайн</p> <p class="text-xs text-gray-500 mt-1">UI/UX и прототипы</p> </div> <div class="bg-[#F7FEE7] rounded-2xl p-4 hover:bg-[#ECFCCB] transition-colors"> <div class="text-lime-600 text-2xl mb-2"><iconify-icon icon="lucide:code-2"></iconify-icon></div> <p class="font-bold text-sm text-black">Разработка</p> <p class="text-xs text-gray-500 mt-1">Backend, frontend, mobile</p> </div> <div class="bg-[#FFF1F2] rounded-2xl p-4 hover:bg-[#FFE4E6] transition-colors"> <div class="text-rose-500 text-2xl mb-2"><iconify-icon icon="lucide:test-tubes"></iconify-icon></div> <p class="font-bold text-sm text-black">Тестирование</p> <p class="text-xs text-gray-500 mt-1">QA и автотесты</p> </div> <div class="bg-[#FFFBEB] rounded-2xl p-4 hover:bg-[#FEF3C7] transition-colors"> <div class="text-amber-500 text-2xl mb-2"><iconify-icon icon="lucide:rocket"></iconify-icon></div> <p class="font-bold text-sm text-black">Запуск</p> <p class="text-xs text-gray-500 mt-1">CI/CD и деплой</p> </div> <div class="bg-[#F0FDF4] rounded-2xl p-4 hover:bg-[#DCFCE7] transition-colors"> <div class="text-green-600 text-2xl mb-2"><iconify-icon icon="lucide:headphones"></iconify-icon></div> <p class="font-bold text-sm text-black">Поддержка</p> <p class="text-xs text-gray-500 mt-1">Мониторинг и развитие</p> </div> </div> </div> <!-- 7. Problem Solving (1x1) --> <div class="bento-card p-5 flex flex-col justify-center items-center text-center bg-white group relative overflow-hidden"> <p class="font-display font-bold text-[10px] text-black mb-3 uppercase tracking-wider">Решаем задачи</p> <!-- Radial layout --> <div class="relative w-28 h-28"> <!-- Center --> <div class="absolute inset-0 flex items-center justify-center"> <div class="w-12 h-12 bg-black rounded-full flex items-center justify-center text-white"> <iconify-icon icon="lucide:target" class="text-lg"></iconify-icon> </div> </div> <!-- Orbiting icons --> <div class="absolute top-0 left-1/2 -translate-x-1/2 -translate-y-1 w-7 h-7 bg-[#F0FDFA] rounded-lg flex items-center justify-center text-teal-600 text-xs shadow-sm"> <iconify-icon icon="lucide:lightbulb"></iconify-icon> </div> <div class="absolute top-2 right-0 translate-x-1 w-7 h-7 bg-[#F5F3FF] rounded-lg flex items-center justify-center text-violet-600 text-xs shadow-sm"> <iconify-icon icon="lucide:rocket"></iconify-icon> </div> <div class="absolute bottom-2 right-0 translate-x-1 w-7 h-7 bg-[#FFF1F2] rounded-lg flex items-center justify-center text-rose-500 text-xs shadow-sm"> <iconify-icon icon="lucide:search"></iconify-icon> </div> <div class="absolute bottom-0 left-1/2 -translate-x-1/2 translate-y-1 w-7 h-7 bg-[#FFFBEB] rounded-lg flex items-center justify-center text-amber-500 text-xs shadow-sm"> <iconify-icon icon="lucide:check-circle"></iconify-icon> </div> <div class="absolute bottom-2 left-0 -translate-x-1 w-7 h-7 bg-[#F0FDF4] rounded-lg flex items-center justify-center text-green-600 text-xs shadow-sm"> <iconify-icon icon="lucide:wrench"></iconify-icon> </div> <div class="absolute top-2 left-0 -translate-x-1 w-7 h-7 bg-[#F7FEE7] rounded-lg flex items-center justify-center text-lime-600 text-xs shadow-sm"> <iconify-icon icon="lucide:layout"></iconify-icon> </div> </div> <div class="flex flex-wrap gap-1 justify-center mt-3"> <span class="text-[9px] text-gray-400 font-medium">Идея</span> <span class="text-[9px] text-gray-300">→</span> <span class="text-[9px] text-gray-400 font-medium">Анализ</span> <span class="text-[9px] text-gray-300">→</span> <span class="text-[9px] text-gray-400 font-medium">Решение</span> <span class="text-[9px] text-gray-300">→</span> <span class="text-[9px] text-gray-400 font-medium">Результат</span> </div> </div> <!-- 8. Tech Picker (1x1) --> <div class="bento-card bg-black text-white p-6 flex flex-col justify-between overflow-hidden"> <p class="text-xs text-gray-400 font-medium uppercase tracking-wide mb-3">Выберите стек под ваш проект</p> <div class="flex flex-wrap gap-2" id="tech-picker"> <button onclick="this.classList.toggle('bg-bento-cyan');this.classList.toggle('text-black');this.classList.toggle('bg-white/10')" class="px-3 py-1.5 rounded-full bg-white/10 text-xs font-mono transition-all hover:scale-105 cursor-pointer">React</button> <button onclick="this.classList.toggle('bg-bento-cyan');this.classList.toggle('text-black');this.classList.toggle('bg-white/10')" class="px-3 py-1.5 rounded-full bg-white/10 text-xs font-mono transition-all hover:scale-105 cursor-pointer">Node</button> <button onclick="this.classList.toggle('bg-bento-cyan');this.classList.toggle('text-black');this.classList.toggle('bg-white/10')" class="px-3 py-1.5 rounded-full bg-white/10 text-xs font-mono transition-all hover:scale-105 cursor-pointer">Go</button> <button onclick="this.classList.toggle('bg-bento-cyan');this.classList.toggle('text-black');this.classList.toggle('bg-white/10')" class="px-3 py-1.5 rounded-full bg-white/10 text-xs font-mono transition-all hover:scale-105 cursor-pointer">Python</button> <button onclick="this.classList.toggle('bg-bento-cyan');this.classList.toggle('text-black');this.classList.toggle('bg-white/10')" class="px-3 py-1.5 rounded-full bg-white/10 text-xs font-mono transition-all hover:scale-105 cursor-pointer">Next.js</button> <button onclick="this.classList.toggle('bg-bento-cyan');this.classList.toggle('text-black');this.classList.toggle('bg-white/10')" class="px-3 py-1.5 rounded-full bg-white/10 text-xs font-mono transition-all hover:scale-105 cursor-pointer">Flutter</button> <button onclick="this.classList.toggle('bg-bento-cyan');this.classList.toggle('text-black');this.classList.toggle('bg-white/10')" class="px-3 py-1.5 rounded-full bg-white/10 text-xs font-mono transition-all hover:scale-105 cursor-pointer">Swift</button> <button onclick="this.classList.toggle('bg-bento-cyan');this.classList.toggle('text-black');this.classList.toggle('bg-white/10')" class="px-3 py-1.5 rounded-full bg-white/10 text-xs font-mono transition-all hover:scale-105 cursor-pointer">Kotlin</button> <button onclick="this.classList.toggle('bg-bento-cyan');this.classList.toggle('text-black');this.classList.toggle('bg-white/10')" class="px-3 py-1.5 rounded-full bg-white/10 text-xs font-mono transition-all hover:scale-105 cursor-pointer">PostgreSQL</button> <button onclick="this.classList.toggle('bg-bento-cyan');this.classList.toggle('text-black');this.classList.toggle('bg-white/10')" class="px-3 py-1.5 rounded-full bg-white/10 text-xs font-mono transition-all hover:scale-105 cursor-pointer">K8s</button> <span class="px-3 py-1.5 text-xs text-gray-500 italic">и другие...</span> </div> </div> <!-- 9. AI / ML (Wide 2x1) --> <div class="bento-card col-span-1 md:col-span-2 p-8 bg-white relative overflow-hidden group"> <div> <h3 class="font-display font-bold text-5xl md:text-6xl mb-2 text-black">AI/ML</h3> <p class="font-medium text-lg text-gray-500">Предиктивные модели и интеллектуальные системы</p> <div class="flex flex-wrap gap-2 mt-4"> <span class="px-3 py-1 rounded-full bg-gray-100 text-sm font-mono text-black">AI Agents</span> <span class="px-3 py-1 rounded-full bg-gray-100 text-sm font-mono text-black">RAG</span> <span class="px-3 py-1 rounded-full bg-gray-100 text-sm font-mono text-black">NLP</span> <span class="px-3 py-1 rounded-full bg-gray-100 text-sm font-mono text-black">Scoring</span> </div> <a href="/ai-ml.html" class="inline-flex items-center gap-1 text-sm font-bold text-black mt-4 hover:text-bento-purple transition-colors">Подробнее <iconify-icon icon="lucide:arrow-right"></iconify-icon></a> </div> </div> <!-- 10. Cloud & Infra (1x1) --> <div class="bento-card p-8 flex flex-col justify-between bg-white group"> <iconify-icon icon="lucide:cloud" class="text-4xl"></iconify-icon> <div> <h3 class="font-display font-bold text-xl">Cloud & DevOps</h3> <p class="text-sm opacity-60 mt-1">AWS, GCP, Yandex Cloud, K8s</p> </div> </div> <!-- 11. Architecture (1x1) --> <div class="bento-card bg-bento-purple text-white p-8 flex flex-col justify-between group"> <div class="w-10 h-10 bg-white/20 rounded-full flex items-center justify-center"> <iconify-icon icon="lucide:blocks" class="text-xl"></iconify-icon> </div> <div> <h3 class="font-display font-bold text-xl">Архитектура</h3> <p class="text-sm opacity-80 mt-1">Микросервисы и Event-Driven</p> </div> </div> <!-- SECTION: Selected Works (Full Width) --> <div id="work" class="col-span-1 md:col-span-2 lg:col-span-4 mt-12 mb-6"> <h2 class="font-display font-bold text-4xl md:text-5xl ml-4">Наши проекты</h2> </div> <!-- Project 1: RxPay (2x2) --> <div class="bento-card col-span-1 md:col-span-2 row-span-2 p-0 group bg-[#0c1425] relative"> <div class="absolute top-6 left-6 z-10 flex gap-2"> <span class="px-4 py-2 bg-white rounded-full text-sm font-bold shadow-lg">Fintech</span> <span class="px-4 py-2 bg-bento-lime rounded-full text-sm font-bold shadow-lg">AI/ML</span> </div> <!-- BNPL metrics background image --> <img src="rxpay-metrics.svg" alt="RxPay BNPL Metrics" loading="lazy" class="absolute inset-0 w-full h-full object-cover opacity-40 group-hover:opacity-20 transition-opacity duration-500"> <!-- Text overlay --> <div class="absolute bottom-0 left-0 w-full p-8 bg-gradient-to-t from-[#0c1425] via-[#0c1425]/80 to-transparent text-white z-[5]"> <h3 class="font-display font-bold text-3xl">RxPay</h3> <p class="opacity-80 mt-2">Мобильное платёжное приложение, веб-платформа и ML-модели скоринга для BNPL-сервиса. Полный цикл финтех-продукта.</p> <div class="flex flex-wrap gap-4 mt-4"> <a href="/cases/rxpay.html" class="inline-flex items-center gap-2 text-white border-b border-white pb-1 hover:border-bento-lime hover:text-bento-lime transition-colors"> Читать кейс <iconify-icon icon="lucide:arrow-right"></iconify-icon> </a> <a href="https://rxpay.io/" target="_blank" class="inline-flex items-center gap-2 text-white/60 border-b border-white/30 pb-1 hover:text-white hover:border-white transition-colors"> rxpay.io <iconify-icon icon="lucide:external-link"></iconify-icon> </a> </div> </div> </div> <!-- Project 2: SkillMatrix HR Portal (1x1) --> <div class="bento-card p-0 group overflow-hidden relative"> <div class="flex w-[400%] h-full hr-carousel"> <img src="hr-matrix.png" alt="SkillMatrix - Competency Matrix" class="w-1/4 h-full object-cover object-left-top flex-shrink-0" loading="lazy"> <img src="hr-skills.png" alt="SkillMatrix - Skill Library" class="w-1/4 h-full object-cover object-left-top flex-shrink-0" loading="lazy"> <img src="hr-plans.png" alt="SkillMatrix - Evidence-Based Assessment" class="w-1/4 h-full object-cover object-left-top flex-shrink-0" loading="lazy"> <img src="hr-assess.png" alt="SkillMatrix - Assessment Builder" class="w-1/4 h-full object-cover object-left-top flex-shrink-0" loading="lazy"> </div> <a href="/cases/skillmatrix.html" class="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent md:bg-black/40 flex flex-col items-end justify-end p-4 md:items-center md:justify-center md:p-0 opacity-100 md:opacity-0 group-hover:opacity-100 transition-opacity duration-300"> <span class="font-display font-bold text-white text-xl hidden md:block">SkillMatrix</span> <span class="text-white/70 text-sm mt-1 hidden md:block">HR портал обучения и оценки</span> <span class="text-white text-xs font-medium flex items-center gap-1">Читать кейс <iconify-icon icon="lucide:arrow-right" class="text-sm"></iconify-icon></span> </a> </div> <!-- Project 3: CodeRAG (1x1) --> <div class="bento-card p-0 group overflow-hidden relative"> <div class="flex w-[300%] h-full coderag-carousel"> <img src="coderag-setup.webp" alt="CodeRAG - Setup" class="w-1/3 h-full object-cover object-left-top flex-shrink-0" loading="lazy"> <img src="coderag-patterns.webp" alt="CodeRAG - Patterns" class="w-1/3 h-full object-cover object-left-top flex-shrink-0" loading="lazy"> <img src="coderag-stats.webp" alt="CodeRAG - Index Stats" class="w-1/3 h-full object-cover object-left-top flex-shrink-0" loading="lazy"> </div> <a href="/cases/coderag.html" class="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent md:bg-black/40 flex flex-col items-end justify-end p-4 md:items-center md:justify-center md:p-0 opacity-100 md:opacity-0 group-hover:opacity-100 transition-opacity duration-300"> <span class="font-display font-bold text-white text-xl hidden md:block">CodeRAG</span> <span class="text-white/70 text-sm mt-1 text-center px-4 hidden md:block">RAG-система для качества кода<br>в бигтехе</span> <span class="text-white text-xs font-medium flex items-center gap-1">Читать кейс <iconify-icon icon="lucide:arrow-right" class="text-sm"></iconify-icon></span> </a> </div> <!-- Project 4: ТОиР Про (2x1) --> <div class="bento-card col-span-1 md:col-span-2 p-0 bg-[#1a1a1a] text-white flex flex-col md:flex-row items-stretch overflow-hidden group"> <div class="flex-1 p-8 flex flex-col justify-center"> <span class="text-bento-lime font-bold tracking-wider text-sm uppercase mb-2 block">Кейс</span> <h3 class="font-display font-bold text-3xl mb-4">ТОиР Про</h3> <p class="text-gray-400 mb-4">Система управления техническим обслуживанием и ремонтом оборудования. Веб-панель + мобильное приложение.</p> <div class="flex gap-2 flex-wrap mb-4"> <span class="px-3 py-1 rounded-full bg-white/10 text-xs font-mono">Web</span> <span class="px-3 py-1 rounded-full bg-white/10 text-xs font-mono">Mobile</span> <span class="px-3 py-1 rounded-full bg-white/10 text-xs font-mono">Enterprise</span> </div> <a href="/cases/toir-pro.html" class="inline-flex items-center gap-1 text-white/60 hover:text-white text-xs font-medium transition-colors"> Читать кейс <iconify-icon icon="lucide:arrow-right" class="text-sm"></iconify-icon> </a> </div> <div class="w-full md:w-1/2 h-56 md:h-auto overflow-hidden relative bg-[#111] flex flex-col items-center justify-center p-4"> <div class="relative w-full h-40 md:h-44 flex items-center justify-center" id="toir-slider"> <img src="toir-dashboard.webp" alt="ТОиР Про - Dashboard" class="toir-slide absolute max-h-full w-auto rounded-lg shadow-lg transition-opacity duration-500 opacity-100" loading="lazy"> <img src="toir-mobile-5.webp" alt="ТОиР Про - Главная" class="toir-slide absolute max-h-full w-auto rounded-lg shadow-lg transition-opacity duration-500 opacity-0" loading="lazy"> <img src="toir-mobile-1.webp" alt="ТОиР Про - Оборудование" class="toir-slide absolute max-h-full w-auto rounded-lg shadow-lg transition-opacity duration-500 opacity-0" loading="lazy"> <img src="toir-mobile-2.webp" alt="ТОиР Про - Детали" class="toir-slide absolute max-h-full w-auto rounded-lg shadow-lg transition-opacity duration-500 opacity-0" loading="lazy"> <img src="toir-mobile-3.webp" alt="ТОиР Про - Заявки" class="toir-slide absolute max-h-full w-auto rounded-lg shadow-lg transition-opacity duration-500 opacity-0" loading="lazy"> <img src="toir-mobile-4.webp" alt="ТОиР Про - Профиль" class="toir-slide absolute max-h-full w-auto rounded-lg shadow-lg transition-opacity duration-500 opacity-0" loading="lazy"> </div> <div class="flex items-center gap-3 mt-3"> <button onclick="toirPrev()" class="w-7 h-7 rounded-full bg-white/10 hover:bg-white/20 text-white flex items-center justify-center transition-colors text-sm"><</button> <div class="flex gap-1.5" id="toir-dots"></div> <button onclick="toirNext()" class="w-7 h-7 rounded-full bg-white/10 hover:bg-white/20 text-white flex items-center justify-center transition-colors text-sm">></button> </div> </div> </div> <!-- CTA: Cost Calculator + Contact --> <div id="contact" class="col-span-1 md:col-span-2 lg:col-span-4 mt-8"> <div class="bento-card bg-bento-yellow p-8 md:p-12 relative overflow-hidden"> <div class="absolute -left-20 -bottom-20 w-64 h-64 bg-white/30 rounded-full blur-3xl"></div> <div class="absolute right-20 top-20 w-32 h-32 bg-orange-400/20 rounded-full blur-2xl"></div> <div class="relative z-10 max-w-4xl mx-auto"> <h2 class="font-display font-bold text-3xl md:text-5xl mb-2 text-black text-center">Обсудить проект</h2> <p class="text-center text-black/60 font-medium mb-10">Выберите услуги и бюджет — мы свяжемся с вами</p> <!-- Form --> <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8"> <!-- Services --> <div> <p class="font-bold text-sm mb-3 text-black">Услуги</p> <div class="flex flex-wrap gap-2" id="form-services"> <button onclick="formToggle(this)" class="calc-btn px-4 py-2 rounded-full bg-white text-sm font-medium text-black hover:bg-gray-100 transition-all border border-black/10">Мобильное приложение</button> <button onclick="formToggle(this)" class="calc-btn px-4 py-2 rounded-full bg-white text-sm font-medium text-black hover:bg-gray-100 transition-all border border-black/10">Web-проект</button> <button onclick="formToggle(this)" class="calc-btn px-4 py-2 rounded-full bg-white text-sm font-medium text-black hover:bg-gray-100 transition-all border border-black/10">AI/ML</button> <button onclick="formToggle(this)" class="calc-btn px-4 py-2 rounded-full bg-white text-sm font-medium text-black hover:bg-gray-100 transition-all border border-black/10">UX/UI дизайн</button> <button onclick="formToggle(this)" class="calc-btn px-4 py-2 rounded-full bg-white text-sm font-medium text-black hover:bg-gray-100 transition-all border border-black/10">Highload</button> <button onclick="formToggle(this)" class="calc-btn px-4 py-2 rounded-full bg-white text-sm font-medium text-black hover:bg-gray-100 transition-all border border-black/10">DevOps</button> <button onclick="formToggle(this)" class="calc-btn px-4 py-2 rounded-full bg-white text-sm font-medium text-black hover:bg-gray-100 transition-all border border-black/10">Поддержка</button> <button onclick="formToggle(this)" class="calc-btn px-4 py-2 rounded-full bg-white text-sm font-medium text-black hover:bg-gray-100 transition-all border border-black/10">AI агенты</button> </div> </div> <!-- Budget --> <div> <p class="font-bold text-sm mb-3 text-black">Бюджет</p> <div class="flex flex-wrap gap-2" id="form-budget"> <button onclick="formSelect(this,'form-budget')" class="calc-btn px-4 py-2 rounded-full bg-white text-sm font-medium text-black hover:bg-gray-100 transition-all border border-black/10">До 500 тыс. ₽</button> <button onclick="formSelect(this,'form-budget')" class="calc-btn px-4 py-2 rounded-full bg-white text-sm font-medium text-black hover:bg-gray-100 transition-all border border-black/10">До 1 млн ₽</button> <button onclick="formSelect(this,'form-budget')" class="calc-btn px-4 py-2 rounded-full bg-white text-sm font-medium text-black hover:bg-gray-100 transition-all border border-black/10">1–3 млн ₽</button> <button onclick="formSelect(this,'form-budget')" class="calc-btn px-4 py-2 rounded-full bg-white text-sm font-medium text-black hover:bg-gray-100 transition-all border border-black/10">3–5 млн ₽</button> <button onclick="formSelect(this,'form-budget')" class="calc-btn px-4 py-2 rounded-full bg-white text-sm font-medium text-black hover:bg-gray-100 transition-all border border-black/10">5–10 млн ₽</button> <button onclick="formSelect(this,'form-budget')" class="calc-btn px-4 py-2 rounded-full bg-white text-sm font-medium text-black hover:bg-gray-100 transition-all border border-black/10">Более 10 млн ₽</button> </div> </div> <!-- Description --> <div class="md:col-span-2 relative"> <p class="font-bold text-sm mb-3 text-black">Кратко о проекте</p> <textarea id="form-desc" rows="5" maxlength="1000" oninput="document.getElementById('char-count').textContent=this.value.length" placeholder="Расскажите, что нужно сделать..." class="w-full px-5 py-4 rounded-2xl border-2 border-black/10 focus:border-black focus:outline-none bg-white text-black placeholder-gray-400 text-sm resize-none"></textarea> <span class="absolute bottom-4 right-4 text-xs text-black/30"><span id="char-count">0</span>/1000</span> </div> </div> <!-- Contact (required) --> <div class="mb-8"> <p class="font-bold text-sm mb-3 text-black">Контакт для связи <span class="text-red-500">*</span></p> <input type="text" id="form-contact" placeholder="Email, телефон или Telegram" oninput="this.style.borderColor=''" class="w-full md:w-1/2 px-5 py-4 rounded-2xl border-2 border-black/10 focus:border-black focus:outline-none bg-white text-black placeholder-gray-400 text-sm"> </div> <!-- File attach + Submit --> <div class="flex flex-col md:flex-row gap-6 items-start md:items-end mb-6"> <!-- Attach file --> <div class="flex items-center gap-3"> <label for="form-file" class="w-12 h-12 bg-black/10 hover:bg-black/20 rounded-full flex items-center justify-center cursor-pointer transition-colors flex-shrink-0"> <iconify-icon icon="lucide:paperclip" class="text-xl text-black"></iconify-icon> </label> <input type="file" id="form-file" class="hidden" accept=".txt,.doc,.docx,.pdf,.png,.jpg" onchange="showFileName(this)"> <div> <p class="font-bold text-sm text-black">Прикрепить файлы</p> <p class="text-xs text-black/50" id="file-name">Форматы: .txt, .doc, .pdf, .png, .jpg</p> </div> </div> <!-- Submit --> <div class="flex flex-col gap-3 flex-1 md:items-end"> <button onclick="submitForm()" id="submit-btn" class="px-10 py-4 bg-black text-white rounded-full font-bold hover:bg-gray-800 transition-all active:scale-95 disabled:opacity-40 disabled:cursor-not-allowed" disabled> Отправить </button> </div> </div> <!-- Privacy checkbox --> <div class="flex items-start gap-3 mb-8"> <input type="checkbox" id="privacy-check" onchange="document.getElementById('submit-btn').disabled=!this.checked" class="w-5 h-5 rounded border-2 border-black/20 accent-black mt-0.5 cursor-pointer flex-shrink-0"> <label for="privacy-check" class="text-sm text-black/60 cursor-pointer"> Даю согласие на <a href="privacy.html" target="_blank" class="underline text-black hover:text-black/80">Обработку персональных данных</a> </label> </div> <!-- Contact buttons --> <div class="flex flex-col sm:flex-row gap-4 justify-center items-center pt-4 border-t border-black/10"> <a href="https://t.me/nikitaf" target="_blank" class="inline-flex items-center gap-2 px-8 py-4 bg-black text-white rounded-full font-bold hover:bg-gray-800 transition-transform active:scale-95"> <iconify-icon icon="lucide:send"></iconify-icon> Написать в Telegram </a> <button onclick="copyEmail()" class="inline-flex items-center gap-2 px-8 py-4 bg-white text-black rounded-full font-bold hover:bg-gray-100 transition-transform active:scale-95 border-2 border-black/10 relative"> <iconify-icon icon="lucide:mail"></iconify-icon> <span id="email-btn-text">Скопировать Email</span> </button> </div> </div> </div> </div> <!-- Footer --> <footer class="col-span-1 md:col-span-2 lg:col-span-4 py-12 flex flex-col md:flex-row justify-between items-center gap-6 text-gray-500"> <div class="flex items-center gap-2"> <div class="w-6 h-6 bg-black rounded flex items-center justify-center text-white font-bold text-xs"> К </div> <span class="font-display font-bold text-black">КодЭкспресс</span> </div> <p class="text-sm">© 2026 КодЭкспресс. Все права защищены.</p> </footer> </main> </div> <!-- Fullscreen Gallery Modal --> <div id="gallery-modal" class="fixed inset-0 z-[100] bg-black/95 backdrop-blur-xl hidden flex-col" onclick="if(event.target===this)closeGallery()"> <!-- Top bar --> <div class="flex items-start justify-between p-4 md:p-6 flex-shrink-0"> <div class="max-w-xl"> <h3 id="gallery-title" class="text-white font-display font-bold text-lg md:text-xl"></h3> <p id="gallery-desc" class="text-white/50 text-xs mt-1 leading-relaxed line-clamp-2 md:line-clamp-none"></p> </div> <button onclick="closeGallery()" class="w-10 h-10 bg-white/10 hover:bg-white/20 rounded-full flex items-center justify-center text-white transition-colors flex-shrink-0 ml-4"><iconify-icon icon="lucide:x" class="text-lg"></iconify-icon></button> </div> <!-- Image area --> <div class="flex-1 flex items-center justify-center px-4 md:px-16 relative min-h-0"> <button onclick="galleryPrev()" class="absolute left-4 w-12 h-12 bg-white/10 hover:bg-white/20 rounded-full hidden md:flex items-center justify-center text-white transition-colors z-10"><iconify-icon icon="lucide:chevron-left" class="text-xl"></iconify-icon></button> <img id="gallery-img" class="max-h-full max-w-full object-contain rounded-xl shadow-2xl transition-opacity duration-300" src="" alt=""> <button onclick="galleryNext()" class="absolute right-4 w-12 h-12 bg-white/10 hover:bg-white/20 rounded-full hidden md:flex items-center justify-center text-white transition-colors z-10"><iconify-icon icon="lucide:chevron-right" class="text-xl"></iconify-icon></button> </div> <!-- Mobile nav + Dots --> <div class="flex items-center justify-center gap-4 p-3 flex-shrink-0"> <button onclick="galleryPrev()" class="w-10 h-10 bg-white/10 hover:bg-white/20 rounded-full flex md:hidden items-center justify-center text-white transition-colors"><iconify-icon icon="lucide:chevron-left" class="text-lg"></iconify-icon></button> <div class="flex gap-2" id="gallery-dots"></div> <button onclick="galleryNext()" class="w-10 h-10 bg-white/10 hover:bg-white/20 rounded-full flex md:hidden items-center justify-center text-white transition-colors"><iconify-icon icon="lucide:chevron-right" class="text-lg"></iconify-icon></button> </div> </div> <script> const galleries = { skillmatrix: { title: 'SkillMatrix', desc: 'Внутренний HR-портал для обучения и оценки сотрудников. Матрица компетенций, библиотека навыков, индивидуальные планы развития и аналитика по командам. Помогает системно растить экспертизу внутри компании.', images: ['hr-matrix.png', 'hr-skills.png', 'hr-plans.png', 'hr-assess.png'] }, coderag: { title: 'CodeRAG', desc: 'RAG-система для повышения качества кода в крупных технологических компаниях. Индексирует кодовую базу, распознаёт архитектурные паттерны и даёт контекстные рекомендации разработчикам на основе лучших практик проекта.', images: ['coderag-setup.webp', 'coderag-patterns.webp', 'coderag-stats.webp'] }, toir: { title: 'ТОиР Про', desc: 'Система управления техническим обслуживанием и ремонтом промышленного оборудования. Веб-панель для диспетчеров и мобильное приложение для инженеров на объектах. Учёт оборудования, заявки на ремонт, графики ТО и KPI-аналитика.', images: ['toir-dashboard.webp', 'toir-mobile-5.webp', 'toir-mobile-1.webp', 'toir-mobile-2.webp', 'toir-mobile-3.webp', 'toir-mobile-4.webp'] } }; let currentGallery = null; let galleryIndex = 0; function openGallery(name) { currentGallery = galleries[name]; galleryIndex = 0; const modal = document.getElementById('gallery-modal'); modal.classList.remove('hidden'); modal.classList.add('flex'); document.getElementById('gallery-title').textContent = currentGallery.title; document.getElementById('gallery-desc').textContent = currentGallery.desc; document.body.style.overflow = 'hidden'; renderGalleryDots(); showGallerySlide(); } function closeGallery() { const modal = document.getElementById('gallery-modal'); modal.classList.add('hidden'); modal.classList.remove('flex'); document.body.style.overflow = ''; } function renderGalleryDots() { const dots = document.getElementById('gallery-dots'); dots.innerHTML = ''; currentGallery.images.forEach((_, i) => { const dot = document.createElement('button'); dot.className = 'w-2 h-2 rounded-full transition-all ' + (i === 0 ? 'bg-white w-4' : 'bg-white/30'); dot.onclick = () => { galleryIndex = i; showGallerySlide(); }; dots.appendChild(dot); }); } function showGallerySlide() { const img = document.getElementById('gallery-img'); img.style.opacity = 0; setTimeout(() => { img.src = currentGallery.images[galleryIndex]; img.style.opacity = 1; }, 150); const dots = document.getElementById('gallery-dots').children; Array.from(dots).forEach((d, i) => { d.className = 'w-2 h-2 rounded-full transition-all ' + (i === galleryIndex ? 'bg-white w-4' : 'bg-white/30'); }); } function galleryNext() { galleryIndex = (galleryIndex + 1) % currentGallery.images.length; showGallerySlide(); } function galleryPrev() { galleryIndex = (galleryIndex - 1 + currentGallery.images.length) % currentGallery.images.length; showGallerySlide(); } document.addEventListener('keydown', (e) => { if (!currentGallery) return; if (e.key === 'Escape') closeGallery(); if (e.key === 'ArrowRight') galleryNext(); if (e.key === 'ArrowLeft') galleryPrev(); }); // Touch swipe for gallery let touchStartX = 0; const galleryModal = document.getElementById('gallery-modal'); galleryModal.addEventListener('touchstart', (e) => { touchStartX = e.touches[0].clientX; }, { passive: true }); galleryModal.addEventListener('touchend', (e) => { if (!currentGallery) return; const diff = e.changedTouches[0].clientX - touchStartX; if (Math.abs(diff) > 50) { if (diff < 0) galleryNext(); else galleryPrev(); } }, { passive: true }); // Copy email function copyEmail() { navigator.clipboard.writeText('nikmobdev@gmail.com'); const btn = document.getElementById('email-btn-text'); const original = btn.textContent; btn.textContent = 'Скопировано!'; setTimeout(() => { btn.textContent = original; }, 2000); } // File attach function showFileName(input) { const label = document.getElementById('file-name'); if (input.files.length > 0) { label.textContent = input.files[0].name; label.classList.add('text-black', 'font-medium'); label.classList.remove('text-black/50'); } } // Submit form function submitForm() { const contact = document.getElementById('form-contact').value.trim(); if (!contact) { document.getElementById('form-contact').style.borderColor = '#ef4444'; document.getElementById('form-contact').focus(); showToast('Укажите контакт для связи'); return; } const services = Array.from(document.querySelectorAll('#form-services .calc-btn[data-active="1"]')).map(b => b.textContent.trim()); const budget = document.querySelector('#form-budget .calc-btn[data-active="1"]')?.textContent.trim() || ''; const desc = document.getElementById('form-desc').value.trim(); const fileInput = document.getElementById('form-file'); const fd = new FormData(); fd.append('contact', contact); fd.append('services', services.join(', ')); fd.append('budget', budget); fd.append('description', desc); if (fileInput && fileInput.files.length) { fd.append('file', fileInput.files[0]); } fetch('/submit', { method: 'POST', body: fd }) .then(r => r.json()) .then(data => { if (data.ok) { // Reset form document.querySelectorAll('#form-services .calc-btn, #form-budget .calc-btn').forEach(b => { b.dataset.active = '0'; b.style.backgroundColor = '#fff'; b.style.color = '#000'; b.style.borderColor = 'rgba(0,0,0,0.1)'; }); document.getElementById('form-contact').value = ''; document.getElementById('form-contact').style.borderColor = ''; document.getElementById('form-desc').value = ''; document.getElementById('char-count').textContent = '0'; if (fileInput) fileInput.value = ''; const fileLabel = document.getElementById('file-name'); if (fileLabel) fileLabel.textContent = ''; // Show popup showToast('Заявка отправлена! Мы свяжемся с вами в ближайшее время.'); } }) .catch(() => showToast('Ошибка отправки. Попробуйте позже.')); } function showToast(msg) { const toast = document.createElement('div'); toast.textContent = msg; toast.style.cssText = 'position:fixed;bottom:2rem;left:50%;transform:translateX(-50%);background:#000;color:#fff;padding:12px 24px;border-radius:12px;font-size:14px;z-index:9999;opacity:0;transition:opacity .3s'; document.body.appendChild(toast); requestAnimationFrame(() => toast.style.opacity = '1'); setTimeout(() => { toast.style.opacity = '0'; setTimeout(() => toast.remove(), 300); }, 3000); } // Form toggles function formToggle(btn) { const isActive = btn.dataset.active === '1'; if (isActive) { btn.dataset.active = '0'; btn.style.backgroundColor = '#fff'; btn.style.color = '#000'; btn.style.borderColor = 'rgba(0,0,0,0.1)'; } else { btn.dataset.active = '1'; btn.style.backgroundColor = '#000'; btn.style.color = '#fff'; btn.style.borderColor = '#000'; } } function formSelect(btn, groupId) { const group = document.getElementById(groupId); group.querySelectorAll('.calc-btn').forEach(b => { b.dataset.active = '0'; b.style.backgroundColor = '#fff'; b.style.color = '#000'; b.style.borderColor = 'rgba(0,0,0,0.1)'; }); btn.dataset.active = '1'; btn.style.backgroundColor = '#000'; btn.style.color = '#fff'; btn.style.borderColor = '#000'; } const toirSlides = document.querySelectorAll('.toir-slide'); const toirDotsContainer = document.getElementById('toir-dots'); let toirIndex = 0; toirSlides.forEach((_, i) => { const dot = document.createElement('button'); dot.className = 'w-2 h-2 rounded-full transition-all ' + (i === 0 ? 'bg-white w-4' : 'bg-white/30'); dot.onclick = () => toirGo(i); toirDotsContainer.appendChild(dot); }); function toirGo(i) { toirSlides[toirIndex].classList.replace('opacity-100', 'opacity-0'); const dots = toirDotsContainer.children; dots[toirIndex].className = 'w-2 h-2 rounded-full transition-all bg-white/30'; toirIndex = i; toirSlides[toirIndex].classList.replace('opacity-0', 'opacity-100'); dots[toirIndex].className = 'w-2 h-2 rounded-full transition-all bg-white w-4'; } function toirNext() { toirGo((toirIndex + 1) % toirSlides.length); } function toirPrev() { toirGo((toirIndex - 1 + toirSlides.length) % toirSlides.length); } // Image preloader shimmer document.querySelectorAll('img').forEach(img => { if (img.complete) return; img.classList.add('img-loading'); img.addEventListener('load', () => img.classList.remove('img-loading'), { once: true }); img.addEventListener('error', () => img.classList.remove('img-loading'), { once: true }); }); </script> </body> </html>