- SSR-пререндер через Vite + react-dom/server (entry-server.jsx, scripts/prerender.mjs) - Секция партнёров: RU-CENTER, REG.RU, МТВ, КОНТУР - Реквизиты компании в футере (ИНН, ОГРН, банк) - Копирайт с 2011 года - Логотип увеличен (h-8→h-12 навбар, h-7→h-10 футер) - favicon.ico пересобран с 16×16, 32×32, 48×48 из logo.png - og:image и twitter:image переключены на .png - apple-touch-icon.png и og-image.png сгенерированы через sharp - SSR-safe: localStorage и window.location проверяются на typeof Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
225 lines
11 KiB
JavaScript
225 lines
11 KiB
JavaScript
import React, { useState } from 'react'
|
||
import { ChevronRight, Server, Shield, Headphones, Phone, Mail, MapPin, CheckCircle2 } from 'lucide-react'
|
||
import { useLanguage } from '../contexts/LanguageContext.jsx'
|
||
import { useReveal } from '../components/useReveal.js'
|
||
|
||
const serviceIcons = [Server, Shield, Headphones]
|
||
|
||
function ServiceCard({ icon: Icon, title, description, points }) {
|
||
const ref = useReveal()
|
||
return (
|
||
<div ref={ref} className="section-reveal bg-white border border-slate-200 rounded-xl p-8 hover:shadow-lg hover:border-blue-200 transition-all duration-300 flex flex-col gap-4">
|
||
<div className="w-12 h-12 bg-blue-50 rounded-lg flex items-center justify-center">
|
||
<Icon size={24} className="text-blue-700" />
|
||
</div>
|
||
<h3 className="text-xl font-bold text-slate-900">{title}</h3>
|
||
<p className="text-slate-600 text-sm leading-relaxed">{description}</p>
|
||
<ul className="flex flex-col gap-2 mt-auto pt-4 border-t border-slate-100">
|
||
{points.map((p, i) => (
|
||
<li key={i} className="flex items-center gap-2 text-sm text-slate-700">
|
||
<CheckCircle2 size={16} className="text-blue-600 flex-shrink-0" />
|
||
{p}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default function Home() {
|
||
const { t } = useLanguage()
|
||
const [formState, setFormState] = useState({ name: '', company: '', message: '' })
|
||
const [submitted, setSubmitted] = useState(false)
|
||
|
||
const aboutRef = useReveal()
|
||
const contactRef = useReveal()
|
||
|
||
const services = t('services.items')
|
||
const stats = [t('about.stat1'), t('about.stat2'), t('about.stat3')]
|
||
|
||
const handleSubmit = (e) => {
|
||
e.preventDefault()
|
||
setSubmitted(true)
|
||
}
|
||
|
||
return (
|
||
<div>
|
||
{/* Hero */}
|
||
<section className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-900 via-blue-950 to-slate-900 relative overflow-hidden">
|
||
<div className="absolute inset-0 opacity-20">
|
||
<div className="absolute top-1/4 left-1/4 w-96 h-96 bg-blue-500 rounded-full blur-3xl" />
|
||
<div className="absolute bottom-1/4 right-1/4 w-80 h-80 bg-blue-700 rounded-full blur-3xl" />
|
||
</div>
|
||
<div className="max-w-4xl mx-auto px-4 sm:px-6 py-32 text-center relative z-10">
|
||
<div className="inline-flex items-center gap-2 bg-blue-500/10 border border-blue-500/20 rounded-full px-4 py-1.5 mb-8">
|
||
<div className="w-2 h-2 bg-blue-400 rounded-full animate-pulse" />
|
||
<span className="text-blue-300 text-sm font-medium">IT-аутсорсинг · Безопасность · Поддержка</span>
|
||
</div>
|
||
<h1 className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold text-white leading-tight mb-6">
|
||
{t('hero.title')}
|
||
</h1>
|
||
<p className="text-lg sm:text-xl text-slate-300 max-w-2xl mx-auto mb-10 leading-relaxed">
|
||
{t('hero.subtitle')}
|
||
</p>
|
||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||
<a href="#contact" className="px-8 py-4 bg-blue-600 hover:bg-blue-500 text-white font-semibold rounded-lg transition-all duration-300 flex items-center justify-center gap-2 group">
|
||
{t('hero.cta')}
|
||
<ChevronRight size={20} className="group-hover:translate-x-1 transition-transform" />
|
||
</a>
|
||
<a href="#services" className="px-8 py-4 border border-white/20 hover:border-white/40 text-white font-semibold rounded-lg transition-all duration-300">
|
||
{t('hero.ctaSecondary')}
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{/* Services */}
|
||
<section id="services" className="py-24 bg-slate-50">
|
||
<div className="max-w-6xl mx-auto px-4 sm:px-6">
|
||
<div className="text-center mb-16">
|
||
<h2 className="text-3xl sm:text-4xl md:text-5xl font-bold text-slate-900 mb-4">{t('services.title')}</h2>
|
||
<p className="text-slate-500 text-lg max-w-2xl mx-auto">{t('services.subtitle')}</p>
|
||
</div>
|
||
<div className="grid md:grid-cols-3 gap-6">
|
||
{services.map((svc, i) => (
|
||
<ServiceCard key={i} icon={serviceIcons[i]} {...svc} />
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{/* About */}
|
||
<section id="about" className="py-24 bg-white">
|
||
<div className="max-w-6xl mx-auto px-4 sm:px-6">
|
||
<div ref={aboutRef} className="section-reveal grid md:grid-cols-2 gap-16 items-center">
|
||
<div>
|
||
<h2 className="text-3xl sm:text-4xl font-bold text-slate-900 mb-6">{t('about.title')}</h2>
|
||
<p className="text-slate-600 text-lg leading-relaxed mb-4">{t('about.text1')}</p>
|
||
<p className="text-slate-600 leading-relaxed">{t('about.text2')}</p>
|
||
</div>
|
||
<div className="grid grid-cols-3 gap-4">
|
||
{stats.map((s, i) => (
|
||
<div key={i} className="bg-slate-50 rounded-xl p-6 text-center border border-slate-100">
|
||
<div className="text-3xl font-bold text-blue-700 mb-1">{s.value}</div>
|
||
<div className="text-sm text-slate-500">{s.label}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{/* Partners */}
|
||
<section className="py-16 bg-slate-50 border-t border-slate-100">
|
||
<div className="max-w-6xl mx-auto px-4 sm:px-6">
|
||
<div className="text-center mb-10">
|
||
<h2 className="text-2xl sm:text-3xl font-bold text-slate-900 mb-2">{t('partners.title')}</h2>
|
||
<p className="text-slate-500">{t('partners.subtitle')}</p>
|
||
</div>
|
||
<div className="flex flex-wrap justify-center gap-4">
|
||
{[
|
||
{ name: 'RU-CENTER', sub: 'Руцентр' },
|
||
{ name: 'REG.RU', sub: 'Регистратор доменов' },
|
||
{ name: 'МТВ', sub: 'Телекоммуникации' },
|
||
{ name: 'КОНТУР', sub: 'Электронная отчётность' },
|
||
].map(p => (
|
||
<div key={p.name} className="bg-white border border-slate-200 rounded-xl px-8 py-5 flex flex-col items-center gap-1 min-w-[160px] hover:border-blue-200 hover:shadow-sm transition-all duration-200">
|
||
<span className="text-xl font-bold text-slate-800 tracking-tight">{p.name}</span>
|
||
<span className="text-xs text-slate-400">{p.sub}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{/* Contact */}
|
||
<section id="contact" className="py-24 bg-slate-900">
|
||
<div className="max-w-6xl mx-auto px-4 sm:px-6">
|
||
<div ref={contactRef} className="section-reveal grid md:grid-cols-2 gap-16">
|
||
<div>
|
||
<h2 className="text-3xl sm:text-4xl font-bold text-white mb-4">{t('contact.title')}</h2>
|
||
<p className="text-slate-400 text-lg mb-10">{t('contact.subtitle')}</p>
|
||
<div className="flex flex-col gap-6">
|
||
<div className="flex items-start gap-4">
|
||
<div className="w-10 h-10 bg-blue-500/10 rounded-lg flex items-center justify-center flex-shrink-0 mt-0.5">
|
||
<Phone size={20} className="text-blue-400" />
|
||
</div>
|
||
<div>
|
||
<div className="text-slate-400 text-xs uppercase tracking-wider mb-1">{t('contact.phone')}</div>
|
||
<div className="flex flex-col gap-1">
|
||
{t('contact.phones').map((phone, i) => (
|
||
<a key={i} href={`tel:${phone.replace(/\D/g,'')}`} className="text-white font-medium hover:text-blue-400 transition-colors">
|
||
{phone}
|
||
</a>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-4">
|
||
<div className="w-10 h-10 bg-blue-500/10 rounded-lg flex items-center justify-center flex-shrink-0">
|
||
<Mail size={20} className="text-blue-400" />
|
||
</div>
|
||
<div>
|
||
<div className="text-slate-400 text-xs uppercase tracking-wider mb-0.5">{t('contact.email')}</div>
|
||
<a href="mailto:info@sag24.ru" className="text-white font-medium hover:text-blue-400 transition-colors">info@sag24.ru</a>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-4">
|
||
<div className="w-10 h-10 bg-blue-500/10 rounded-lg flex items-center justify-center flex-shrink-0">
|
||
<MapPin size={20} className="text-blue-400" />
|
||
</div>
|
||
<div>
|
||
<div className="text-slate-400 text-xs uppercase tracking-wider mb-0.5">{t('contact.address')}</div>
|
||
<span className="text-white font-medium">{t('contact.addressValue')}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="bg-white/5 border border-white/10 rounded-xl p-8">
|
||
{submitted ? (
|
||
<div className="flex flex-col items-center justify-center h-full gap-4 py-8">
|
||
<CheckCircle2 size={48} className="text-green-400" />
|
||
<p className="text-white text-center text-lg">{t('contact.formSuccess')}</p>
|
||
</div>
|
||
) : (
|
||
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
|
||
<input
|
||
type="text"
|
||
required
|
||
placeholder={t('contact.formName')}
|
||
value={formState.name}
|
||
onChange={e => setFormState({ ...formState, name: e.target.value })}
|
||
className="w-full bg-white/10 border border-white/10 rounded-lg px-4 py-3 text-white placeholder-slate-400 focus:outline-none focus:border-blue-500 transition-colors text-sm"
|
||
/>
|
||
<input
|
||
type="text"
|
||
placeholder={t('contact.formCompany')}
|
||
value={formState.company}
|
||
onChange={e => setFormState({ ...formState, company: e.target.value })}
|
||
className="w-full bg-white/10 border border-white/10 rounded-lg px-4 py-3 text-white placeholder-slate-400 focus:outline-none focus:border-blue-500 transition-colors text-sm"
|
||
/>
|
||
<textarea
|
||
required
|
||
rows={5}
|
||
placeholder={t('contact.formMessage')}
|
||
value={formState.message}
|
||
onChange={e => setFormState({ ...formState, message: e.target.value })}
|
||
className="w-full bg-white/10 border border-white/10 rounded-lg px-4 py-3 text-white placeholder-slate-400 focus:outline-none focus:border-blue-500 transition-colors text-sm resize-none"
|
||
/>
|
||
<button
|
||
type="submit"
|
||
className="w-full py-3 bg-blue-600 hover:bg-blue-500 text-white font-semibold rounded-lg transition-colors"
|
||
>
|
||
{t('contact.formSubmit')}
|
||
</button>
|
||
</form>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
)
|
||
}
|