/* Animated hero with live terminal */ const HERO_ASCII = String.raw` ██╗ ██╗██╗███╗ ██╗███████╗████████╗██╗ █████╗ ██████╗ ██║ ██║██║████╗ ██║██╔════╝╚══██╔══╝██║ ██╔══██╗██╔══██╗ ██║ █╗ ██║██║██╔██╗ ██║█████╗ ██║ ██║ ███████║██████╔╝ ██║███╗██║██║██║╚██╗██║██╔══╝ ██║ ██║ ██╔══██║██╔══██╗ ╚███╔███╔╝██║██║ ╚████║███████╗ ██║ ███████╗██║ ██║██████╔╝ ╚══╝╚══╝ ╚═╝╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═════╝ python · php · docker · nginx · seo ── работаю как разработчик `; const TERMINAL_SCENES = [ { cmd: 'winetlab status', lines: [ { kind: 'out', text: 'available · принимаю небольшие задачи' }, { kind: 'muted', text: 'отвечу в течение нескольких часов' }, ], }, { cmd: 'winetlab fix --form contact.php', lines: [ { kind: 'out', text: 'scan: 4 issues found in /forms/contact.php' }, { kind: 'warn', text: '⚠ PHPMailer creds in plain text · letter goes to spam' }, { kind: 'warn', text: '⚠ no CSRF token · бот-спам через форму' }, { kind: 'ok', text: '✓ applied fixes · отправил тестовое письмо' }, { kind: 'out', text: 'eta: 2h · цена от 3 000 ₽' }, ], }, { cmd: 'winetlab audit https://your-site.ru', lines: [ { kind: 'out', text: 'crawling 312 pages · robots.txt · sitemap.xml' }, { kind: 'err', text: '✗ 17 страниц с дублями canonical' }, { kind: 'err', text: '✗ schema.org Article невалидна (missing author)' }, { kind: 'ok', text: '✓ отчёт + правки готовы · pdf отправил' }, ], }, { cmd: 'winetlab deploy --vps --docker --ssl', lines: [ { kind: 'out', text: 'preparing nginx reverse proxy · letsencrypt · docker-compose' }, { kind: 'ok', text: '✓ app online · https://example.ru · uptime 99.9%' }, { kind: 'muted', text: 'передаю инструкцию по обновлениям' }, ], }, ]; function Terminal() { const [scene, setScene] = React.useState(0); const [phase, setPhase] = React.useState('cmd'); // cmd → out → idle const [history, setHistory] = React.useState([]); // completed scenes (cap at 1 previous) const current = TERMINAL_SCENES[scene]; const cmdText = useTyping(current.cmd, { speed: 28, start: phase === 'cmd', onDone: () => setPhase('out'), }); // Reset typing when scene changes React.useEffect(() => { setPhase('cmd'); }, [scene]); // After output appears, wait then advance React.useEffect(() => { if (phase !== 'out') return; const t = setTimeout(() => setPhase('idle'), 1400 + current.lines.length * 200); return () => clearTimeout(t); }, [phase, scene]); React.useEffect(() => { if (phase !== 'idle') return; const t = setTimeout(() => { // archive current scene into history (cap at last 1 so terminal stays full) setHistory((h) => [current].slice(-1)); setScene((s) => (s + 1) % TERMINAL_SCENES.length); }, 1200); return () => clearTimeout(t); }, [phase]); return (
~/winetlab — zsh — 80×24
main logs
{HERO_ASCII}
— готов к работе. чем помочь?
{/* Previous scene (dimmed) */} {history.map((h, hi) => (
${h.cmd}
{h.lines.map((l, i) => (
{l.text}
))}
))} {/* Current scene */}
$ {cmdText} {phase === 'cmd' && }
{phase !== 'cmd' && current.lines.map((l, i) => ( ))} {phase === 'idle' && (
$
)}
); } function SceneLine({ idx, kind, text }) { // Stagger appearance const [show, setShow] = React.useState(false); React.useEffect(() => { const t = setTimeout(() => setShow(true), 120 + idx * 180); return () => clearTimeout(t); }, []); return (
{text}
); } function Hero({ setRoute }) { const contact = getPublicContact(); return (
v3.5 Python · PHP · Docker · Nginx · SEO Технические задачи
для сайтов без агентств
{/* Я — {contact.specialist_name || ''}, {contact.specialist_title || ''}. */} Исправляю ошибки, настраиваю SEO, серверы, формы и API. Без длинных договоров: одна задача — одно решение — понятная цена. { e.preventDefault(); setRoute('contact'); window.scrollTo(0,0); }} className="btn btn-primary"> Оставить заявку { e.preventDefault(); scrollToId('stack'); }} className="btn btn-ghost"> $ что я умею
5+ лет опыта с PHP, Python, Docker
точечные задачи от 2 000 ₽
прямо или через фриланс-площадки
{[ { k: 'uptime', v: <>99.9% по последним проектам }, { k: 'response', v: <>обычно <2 часов }, { k: 'start', v: <>от 2 000 ₽ }, { k: 'stack', v: <>PHP · Python · Docker · Nginx }, { k: 'format', v: <>напрямую · безопасная сделка }, ].map((it, i) => (
# {it.k}
{it.v}
))}
); } window.Hero = Hero;