// Каталог — публичная страница сайта S"MM. Шапка + sidebar + рабочая область. const CAT = { bg: '#f5ead4', card: '#fbf2de', cardHover: '#f3e7cb', sub: '#e8ddc4', panel: '#ecdfc1', hover: '#e3d4b3', active: '#d8c69e', ink: '#1a1a1e', dim: '#6a5a4a', faint: '#9c8e7c', fainter: '#b8ab97', accent: '#c96848', line: 'rgba(26,26,30,0.14)', lineStrong: 'rgba(26,26,30,0.22)', }; const CATSANS = '"Inter Tight", "Helvetica Neue", sans-serif'; const CATMONO = '"JetBrains Mono", ui-monospace, monospace'; const catMeta = { fontFamily: CATMONO, fontSize: 10, letterSpacing: '0.22em', textTransform: 'uppercase', color: CAT.dim }; // ── Иконки (контурные, в стиле основного сайта) ───────────────────── const cIco = (paths, vb = '0 0 24 24') => ({ size = 20, stroke = 1.6 } = {}) => ( ); const CIconGrid = cIco(<> ); const CIconSpark = cIco(<> ); const CIconCheck = cIco(<>); const CIconArrR = cIco(<> ); // ── Шапка сайта (тонкая, та же, что на главной) ───────────────────── // Навигация ведёт на главную страницу с указанием якоря. В Claude Design // переходы между файлами могут не работать — это нормально. const HOME = '/'; const SITE_NAV = [ { l: 'услуги', href: `${HOME}#pricing` }, { l: 'о нас', href: `${HOME}#philosophy` }, { l: 'faq', href: `${HOME}#faq` }, { l: 'контакты', href: `${HOME}#contacts` }, { l: 'войти', href: '/login' }, ]; function CatTopBar() { const palette = { '--bg': CAT.bg, '--ink': CAT.ink, '--accent': CAT.accent, '--sub': CAT.sub }; const isMobile = window.useIsMobile ? window.useIsMobile() : false; const [menuOpen, setMenuOpen] = React.useState(false); return ( <> {isMobile && menuOpen && ( )} ); } // ── Sidebar каталога ──────────────────────────────────────────────── function CatNavItem({ Icon, label, active, onClick }) { const [hov, setHov] = React.useState(false); const bg = active ? CAT.active : (hov ? CAT.hover : 'transparent'); const color = active ? CAT.accent : CAT.ink; return ( { e.preventDefault(); onClick(); }} onMouseEnter={() => setHov(true)} onMouseLeave={() => setHov(false)} style={{ position: 'relative', display: 'flex', alignItems: 'center', gap: 12, padding: '12px 18px 12px 21px', textDecoration: 'none', color, background: bg, transition: 'background .15s, color .15s', fontFamily: CATSANS, fontSize: 14.5, fontWeight: active ? 500 : 400, }}> {active && ( ); } function CatSidebar({ active, onSelect }) { return ( ); } // ── Page heading ──────────────────────────────────────────────────── function CatPageHeading({ title, sub }) { return (

{title}

{sub}

); } // ── Хост страницы ─────────────────────────────────────────────────── function CatTabsRow({ active, onSelect }) { const tabs = [ { id: 'tariffs', Icon: CIconGrid, label: 'Тарифы' }, { id: 'services', Icon: CIconSpark, label: 'Услуги' }, ]; return ( ); } function CatalogPage() { const isMobile = window.useIsMobile ? window.useIsMobile() : false; const [section, setSection] = React.useState('tariffs'); const goTo = (id) => { setSection(id); window.scrollTo({ top: 0, behavior: 'smooth' }); }; return (
{isMobile && }
{!isMobile && }
{section === 'tariffs' && window.CatalogTariffs ? goTo('services')} /> : section === 'services' && window.CatalogServices ? : null}
); } // Раздел «Тарифы» ───────────────────────────────────────────────────── const PLAN_TIERS_FULL = [ { name: 'Тир-1', sub: 'для одной соцсети, до 10 постов', price: '8 900', popular: false, features: [ '1 соцсеть на выбор (VK или Telegram)', '10 постов в месяц', 'Контент-план на месяц', 'Анализ конкурентов', '1 идея для контента', 'Отчёт в конце месяца', ], }, { name: 'Тир-2', sub: 'для двух соцсетей, до 15 постов', price: '13 900', popular: true, features: [ '2 соцсети (VK + Telegram, кросспостинг)', '15 постов в месяц', '2 истории', '2 внеплановых поста на инфоповоды', 'Контент-план на месяц', 'Анализ конкурентов', '2 идеи для контента', 'Отчёт в конце месяца', ], }, { name: 'Тир-3', sub: 'для двух соцсетей, до 20 постов', price: '18 500', popular: false, features: [ '2 соцсети (VK + Telegram, кросспостинг)', '20 постов в месяц', '4 истории', '2 графических рилса', '4 внеплановых поста на инфоповоды', 'Контент-план на месяц', 'Анализ конкурентов', '5 идей для контента', 'Отчёт в конце месяца', ], }, ]; const COMPARE_ROWS = [ { p: 'Цена в месяц', v: ['8 900 ₽', '13 900 ₽', '18 500 ₽'] }, { p: 'Соцсети', v: ['1', '2', '2'] }, { p: 'Постов в месяц', v: ['10', '15', '20'] }, { p: 'Историй в месяц', v: ['—', '2', '4'] }, { p: 'Графических рилсов', v: ['—', '—', '2'] }, { p: 'Внеплановых постов', v: ['—', '2', '4'] }, { p: 'Идей для контента', v: ['1', '2', '5'] }, { p: 'Контент-план', v: ['check', 'check', 'check'] }, { p: 'Анализ конкурентов', v: ['check', 'check', 'check'] }, { p: 'Отчётов в месяц', v: ['1', '1', '1'] }, ]; // ── Тарифы для SMM-специалистов (диапазонная модель, оплата за слоты) ── // ВНИМАНИЕ: предварительные цифры, синхронить с DIY_RANGES в боте // (orchestrator tariffs.py). Реальная цена при покупке считается ботом — // витрина только показывает модель. const DIY_MIN_SLOTS = 2; const DIY_RANGES = [ { min: 2, max: 3, per: 4000 }, { min: 4, max: 6, per: 3700 }, { min: 7, max: 11, per: 3590 }, { min: 12, max: null, per: null }, // договорная ]; // Что входит на каждого клиента (пакет diy_client). const DIY_CLIENT_FEATURES = [ '15 постов в месяц', '2 истории', '1 контент-план', '1 аудит конкурентов', '1 отчёт клиенту', '2 соцсети (VK + Telegram, кросспостинг)', 'Генерация изображений', ]; const DIY_FEATURES_NOTE = 'Рилсы — докупкой'; function diyRangeFor(n) { return DIY_RANGES.find((r) => n >= r.min && (r.max == null || n <= r.max)) || null; } function CompareCell({ value }) { if (value === 'check') { return ( ); } if (value === 'check-each') { return ( (для каждой) ); } return {value}; } function TierCard({ t }) { return (
{t.popular && ( популярный )}

{t.name}

{t.sub}

{t.price}
₽ в месяц
); } function CompareTable() { return (
{/* Заголовок */}
параметр
{['Тир-1', 'Тир-2', 'Тир-3'].map((label, i) => (
{label}
))}
{/* Строки */} {COMPARE_ROWS.map((row, i) => (
{row.p}
{row.v.map((val, j) => (
))}
))}
); } // ── Витрина для SMM-специалистов: карточка-калькулятор слотов ──────── function DiyStepBtn({ label, onClick, disabled }) { return ( ); } function DiyShowcase() { const isMobile = window.useIsMobile ? window.useIsMobile() : false; const [count, setCount] = React.useState(3); const range = diyRangeFor(count); const negotiable = !range || range.per == null; // 12+ → договорная const fmt = (n) => n.toLocaleString('ru-RU'); const total = (!negotiable && range) ? range.per * count : null; const rangeLabel = (r) => r.max == null ? `${r.min}+` : `${r.min}–${r.max}`; return (

Для SMM-специалистов

Ведёте клиентов сами? Платите за слоты — ставка снижается с количеством.

{/* Левая колонка: калькулятор */}
сколько клиентов ведёте?
setCount((v) => Math.max(DIY_MIN_SLOTS, v - 1))} /> {count} setCount((v) => v + 1)} />
{/* Расчёт */}
{negotiable ? ( <>
Договорная
от 12 клиентов — обсудим условия
) : ( <>
{fmt(range.per)} ₽ × {count} {count === 1 ? 'клиент' : (count < 5 ? 'клиента' : 'клиентов')}
{fmt(total)}
₽ в месяц
)}
{/* Мини-лесенка диапазонов */}
{DIY_RANGES.map((r, i) => { const active = range && r.min === range.min; return (
{rangeLabel(r)} {r.max == null || r.max > 1 ? 'клиентов' : 'клиента'} {r.per == null ? 'договорная' : `${fmt(r.per)} ₽/клиент`}
); })}
{/* Правая колонка: что входит + CTA */}
); } function CatalogTariffs({ onGoServices }) { const isMobile = window.useIsMobile ? window.useIsMobile() : false; return ( <> {/* Карточки тарифов — на мобиле друг под другом (фичи перечислены внутри) */}
{PLAN_TIERS_FULL.map((t) => )}
{/* Сравнительная таблица — только на десктопе. На мобиле карточки выше уже несут полный список фич, широкая таблица не нужна. */} {!isMobile && (

Сравнение тарифов

)} {/* Витрина для SMM-специалистов (диапазонная модель слотов) */} {/* CTA в услуги */}

Нужно что-то ещё?

Разовые услуги, аудиты, кампании — посмотрите каталог услуг

); } Object.assign(window, { CatalogPage, CatPageHeading, CatalogTariffs, CatSidebar, CAT, CATSANS, CATMONO, catMeta });