// ============= PRICING =============
function Pricing() {
  const { t } = A.useApp();
  const tiers = t('pricing.tiers');
  const [yearly, setYearly] = React.useState(false);
  const ref = A.useReveal();
  return (
    <section id="pricing" className="pricing" data-screen-label="pricing">
      <div className="wrap">
        <div className="section-head reveal" ref={ref}>
          <span className="eyebrow"><span className="dot"></span>{t('pricing.eyebrow')}</span>
          <h2>{t('pricing.title1')} <span className="grad-text">{t('pricing.title2')}</span></h2>
          <p>{t('pricing.sub')}</p>
          <div className="bill-toggle">
            <button className={!yearly ? 'is-on' : ''} onClick={() => setYearly(false)}>{t('pricing.monthly')}</button>
            <button className={yearly ? 'is-on' : ''} onClick={() => setYearly(true)}>{t('pricing.yearly')}</button>
            <span className="bill-pill" style={{ transform: yearly ? 'translateX(100%)' : 'translateX(0)' }}></span>
          </div>
        </div>
        <div className="price-grid">
          {tiers.map((tier, i) => <PriceCard key={i} tier={tier} yearly={yearly} perMonth={t('pricing.perMonth')} />)}
        </div>
      </div>
    </section>
  );
}

function PriceCard({ tier, yearly, perMonth }) {
  const ref = A.useReveal();
  const { isAuthed } = A.useApp();
  const price = yearly ? Math.round(parseInt(tier.price.y.replace(/\s/g,'')) / 12) : parseInt(tier.price.m.replace(/\s/g,''));
  const planMap = { 'Старт': 'starter', 'Pro': 'pro', 'Studio': 'unlimited' };
  const planId = planMap[tier.name] ?? 'pro';
  const handleCta = () => {
    window.location.href = isAuthed ? '/checkout/' + planId : '/login';
  };
  return (
    <div className={`price card reveal${tier.highlight ? ' is-hl' : ''}`} ref={ref}>
      {tier.highlight && <span className="price-badge">{tier.badge}</span>}
      <div className="price-head">
        <h3>{tier.name}</h3>
        <p className="muted">{tier.d}</p>
      </div>
      <div className="price-amount">
        <span className="price-cur">₽</span>
        <span className="price-num">{A.fmt(price)}</span>
        <span className="price-per muted">{perMonth}</span>
      </div>
      {yearly && <div className="price-year mono muted">Списываем {tier.price.y} ₽ раз в год</div>}
      <button className={`btn ${tier.highlight ? 'btn-primary btn-spark' : 'btn-ghost'} price-cta`} onClick={handleCta}>
        {tier.cta} <A.Ico.arrow />
      </button>

      {!yearly && tier.price.y && (
        <div className="price-save-hint">
          Год — сэкономь {A.fmt(parseInt(tier.price.m.replace(/\s/g,'')) * 2)} ₽
        </div>
      )}
      <hr className="divider" style={{ margin: '6px 0' }} />
      <ul className="price-feats">
        {tier.features.map((f, i) => (
          <li key={i}><span className="price-check"><A.Ico.check /></span>{f}</li>
        ))}
      </ul>
    </div>
  );
}

window.Pricing = Pricing;

// ============= TESTIMONIALS =============
function Testimonials() {
  const { t } = A.useApp();
  const items = t('testimonials.items');
  const ref = A.useReveal();
  return (
    <section id="testimonials" className="testimonials" data-screen-label="testimonials">
      <div className="wrap">
        <div className="section-head reveal" ref={ref}>
          <span className="eyebrow"><span className="dot"></span>{t('testimonials.eyebrow')}</span>
          <h2>{t('testimonials.title1')} <span className="grad-text">{t('testimonials.title2')}</span></h2>
        </div>
        <div className="t-grid">
          {items.map((it, i) => {
            const colors = ['#a855f7','#f59e0b','#3b82f6','#10b981','#fb923c','#06b6d4'];
            return (
              <div key={i} className={`t-card card card-hover reveal reveal-delay-${(i%4)+1}`}>
                <p className="t-text">«{it.t}»</p>
                <div className="t-meta">
                  <span className="t-avatar" style={{ background: colors[i % colors.length] }}>{it.n.charAt(0)}</span>
                  <div>
                    <div className="t-name">{it.n}</div>
                    <div className="t-role muted">{it.r}</div>
                  </div>
                  <div className="t-stars">★★★★★</div>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </section>
  );
}

window.Testimonials = Testimonials;

// ============= REVIEWS PAGE =============
function ReviewsPage() {
  const { setReviewsOpen, t } = A.useApp();
  const close = React.useCallback(() => setReviewsOpen(false), []);

  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') close(); };
    document.addEventListener('keydown', onKey);
    document.body.style.overflow = 'hidden';
    return () => { document.removeEventListener('keydown', onKey); document.body.style.overflow = ''; };
  }, []);

  const tItems = t('testimonials.items');
  const sItems = t('stories.items');
  const colors = ['#a855f7','#f59e0b','#3b82f6','#10b981','#fb923c','#06b6d4'];

  return ReactDOM.createPortal(
    <div className="tp-page">
      {/* Top bar */}
      <div className="tp-bar">
        <button className="tp-back" onClick={close}>
          <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
            <path d="M10 3L5 8l5 5" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
          Назад
        </button>
        <div className="tp-bar-title">
          <span className="eyebrow" style={{padding:'4px 12px', fontSize:10}}>
            <span className="dot"></span>Отзывы
          </span>
          <span className="tp-bar-name">Истории клиентов</span>
        </div>
        <button className="tp-close" onClick={close} aria-label="Закрыть">
          <svg width="15" height="15" viewBox="0 0 15 15" fill="none">
            <path d="M2 2l11 11M13 2L2 13" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
          </svg>
        </button>
      </div>

      {/* Content */}
      <div className="tp-content wrap">

        {/* Intro */}
        <div className="tp-intro">
          <div className="tp-intro-row">
            <div>
              <h2>Что говорят <span>клиенты</span></h2>
              <p className="tp-intro-sub">Реальные истории фрилансеров и агентств, которые уже работают через Outforge.</p>
            </div>
            <div className="tp-intro-stats">
              <div className="tp-intro-stat">
                <div className="tp-intro-stat-v">148+</div>
                <div className="tp-intro-stat-l">клиентов</div>
              </div>
              <div className="tp-intro-stat">
                <div className="tp-intro-stat-v">4.9★</div>
                <div className="tp-intro-stat-l">рейтинг</div>
              </div>
            </div>
          </div>
        </div>

        {/* Testimonials grid */}
        <div className="rp-section-label">Отзывы</div>
        <div className="t-grid" style={{marginBottom: 56}}>
          {tItems.map((it, i) => (
            <div key={i} className="t-card card card-hover">
              <p className="t-text">«{it.t}»</p>
              <div className="t-meta">
                <span className="t-avatar" style={{ background: colors[i % colors.length] }}>{it.n.charAt(0)}</span>
                <div>
                  <div className="t-name">{it.n}</div>
                  <div className="t-role muted">{it.r}</div>
                </div>
                <div className="t-stars">★★★★★</div>
              </div>
            </div>
          ))}
        </div>

        {/* Stories grid */}
        <div className="rp-section-label">Кейсы · до и после</div>
        <div className="story-grid">
          {sItems.map((s, i) => (
            <div key={i} className="story card">
              <div className="story-h">
                <span className="story-avatar">{s.name.charAt(0)}</span>
                <div>
                  <div className="story-name">{s.name}</div>
                  <div className="story-days mono muted">{s.days}</div>
                </div>
              </div>
              <div className="story-graph">
                <div className="story-col">
                  <div className="story-label muted mono">ДО</div>
                  <div className="story-bar"><div className="story-bar-fill before"></div></div>
                  <div className="story-val">{s.before}</div>
                </div>
                <div className="story-arrow"><A.Ico.arrow /></div>
                <div className="story-col">
                  <div className="story-label muted mono">ПОСЛЕ</div>
                  <div className="story-bar"><div className="story-bar-fill after"></div></div>
                  <div className="story-val grad-text">{s.after}</div>
                </div>
              </div>
              <p className="story-text">«{s.t}»</p>
            </div>
          ))}
        </div>

      </div>
    </div>,
    document.body
  );
}

window.ReviewsPage = ReviewsPage;

// ============= BLOG PAGE =============
const BLOG_CATEGORIES = [
  { k: 'all', l: 'Все статьи' },
  { k: 'guide', l: 'Гайды' },
  { k: 'case', l: 'Кейсы' },
  { k: 'strategy', l: 'Стратегия' },
  { k: 'tools', l: 'Инструменты' },
  { k: 'templates', l: 'Шаблоны' },
  { k: 'mindset', l: 'Мышление' },
];

const BLOG_ARTICLES = [
  {
    id: 'guide-500-leads',
    category: 'guide',
    tag: 'Гайд',
    tagColor: '#3b82f6',
    cover: 'linear-gradient(135deg, #1a2a6c 0%, #1e3a8a 40%, #3730a3 100%)',
    coverLabel: '500 ЛИДОВ ЗА 1 ДЕНЬ',
    date: '12 мая 2026',
    readTime: '8 мин',
    title: 'Как найти 500 лидов за 1 день через YouTube',
    excerpt: 'Пошаговый разбор: как фильтровать каналы, какие ниши работают лучше всего и почему большинство аутричеров сливают бюджет на мёртвые контакты.',
    content: [
      {
        type: 'lead',
        text: 'Один из наших клиентов — фрилансер-монтажёр из Москвы — за один рабочий день нашёл 512 потенциальных клиентов с верифицированными контактами. Рассказываем, как он это сделал.',
      },
      {
        type: 'h2',
        text: 'Шаг 1. Выбери нишу с высоким спросом',
      },
      {
        type: 'p',
        text: 'Не все YouTube-ниши одинаково хорошо конвертируются. Лучшие для холодного аутрича: бизнес и финансы, фитнес, лайфстайл и beauty. Каналы в этих нишах чаще всего монетизированы и активно ищут подрядчиков.',
      },
      {
        type: 'list',
        items: [
          'Бизнес и финансы — ROI конверсии в 3× выше среднего',
          'Фитнес-блогеры — часто ищут монтажёра на постоянку',
          'Лайфстайл 50K–200K подписчиков — зарабатывают, но ещё не раздуты штатом',
          'Beauty и уходовая косметика — сезонные бюджеты на продакшн',
        ],
      },
      {
        type: 'h2',
        text: 'Шаг 2. Настрой фильтры правильно',
      },
      {
        type: 'p',
        text: 'В Outforge ставь диапазон 10 000–300 000 подписчиков. Каналы меньше 10K редко платят рыночные деньги. Каналы больше 300K уже работают с агентствами напрямую. «Золотое окно» — это 30K–150K: есть бюджет, нет сложных тендеров.',
      },
      {
        type: 'callout',
        text: '💡 Совет: добавь фильтр «последнее видео не старше 14 дней». Так ты отсеиваешь заброшенные каналы и попадаешь только к активным авторам.',
      },
      {
        type: 'h2',
        text: 'Шаг 3. Верификация контактов — обязательный этап',
      },
      {
        type: 'p',
        text: 'Из 500 найденных каналов около 32% не имеют публичного контакта или контакт нерабочий. Outforge автоматически проверяет Telegram-аккаунты через пул прокси и убирает «мёртвые» контакты до того, как ты начнёшь рассылку.',
      },
      {
        type: 'p',
        text: 'В итоге из 512 найденных каналов наш клиент получил 348 живых контактов. Именно по ним и запустил кампанию — без ручной работы.',
      },
      {
        type: 'h2',
        text: 'Результат',
      },
      {
        type: 'p',
        text: 'За первую неделю: 14 ответов, 6 созвонов, 3 оплаченных заказа на монтаж. Средний чек — 18 000 ₽. Итого 54 000 ₽ за один день сбора лидов + неделю аутрича.',
      },
    ],
  },
  {
    id: 'case-x8-roi',
    category: 'case',
    tag: 'Кейс',
    tagColor: '#22c55e',
    cover: 'linear-gradient(135deg, #052e16 0%, #14532d 40%, #166534 100%)',
    coverLabel: 'x8 ROI · 14 КЛИЕНТОВ',
    date: '5 мая 2026',
    readTime: '6 мин',
    title: 'x8 ROI за месяц: фрилансер закрыл 14 клиентов',
    excerpt: 'Дизайнер превью из Санкт-Петербурга вложил 4 900 ₽ в подписку Outforge, а через 30 дней получил 14 клиентов и 187 000 ₽ выручки.',
    content: [
      {
        type: 'lead',
        text: 'Артём — thumbnail-дизайнер с 2 годами опыта. До Outforge искал клиентов через биржи фриланса и редкие рекомендации. Средний доход: 35–50 тысяч рублей в месяц. После — история изменилась.',
      },
      {
        type: 'h2',
        text: 'Стартовая точка',
      },
      {
        type: 'p',
        text: 'Артём пришёл с конкретной задачей: найти каналы от 20K подписчиков, которые выпускают от 2 видео в неделю и при этом у них слабые превью. Именно такие каналы — его идеальный клиент.',
      },
      {
        type: 'list',
        items: [
          'Ниша: бизнес-каналы и личные бренды',
          'Фильтр: 20K–200K подписчиков, частота > 2 видео/нед',
          'Гео: Россия, Украина, Казахстан',
          'Контакт: только Telegram (для скорости ответа)',
        ],
      },
      {
        type: 'h2',
        text: 'Что он сделал',
      },
      {
        type: 'p',
        text: 'За первые 2 дня собрал 890 каналов, верифицировал 614 контактов. Написал персонализированное сообщение с конкретной критикой превью каждого канала — AI помог генерировать офферы в контексте.',
      },
      {
        type: 'callout',
        text: '📊 Конверсия ответов: 11.4% — это в 3× выше, чем стандартные cold DM без персонализации.',
      },
      {
        type: 'h2',
        text: 'Итоги месяца',
      },
      {
        type: 'p',
        text: 'Из 614 отправленных сообщений: 70 ответов → 22 созвона → 14 оплаченных заказов. Средний чек вырос с 8 000 до 13 400 ₽ — потому что клиенты приходили уже «прогретыми» персональным оффером.',
      },
      {
        type: 'p',
        text: 'Суммарная выручка за месяц: 187 600 ₽. Стоимость подписки: 4 900 ₽. ROI: x38. Артём продлил подписку на год вперёд.',
      },
    ],
  },
  {
    id: 'strategy-top5-niches',
    category: 'strategy',
    tag: 'Стратегия',
    tagColor: '#a855f7',
    cover: 'linear-gradient(135deg, #2e1065 0%, #4c1d95 40%, #6d28d9 100%)',
    coverLabel: 'ТОП-5 НИШИ 2026',
    date: '28 апр 2026',
    readTime: '10 мин',
    title: 'Топ-5 ниш для холодного аутрича в 2026 году',
    excerpt: 'Какие YouTube-ниши дают лучший отклик на холодные сообщения, сколько платят авторы и почему одни ниши выгорают, а другие остаются золотыми.',
    content: [
      {
        type: 'lead',
        text: 'Холодный аутрич работает не везде одинаково. Мы проанализировали 50 000+ кампаний клиентов Outforge и выявили 5 ниш, в которых конверсия в ответ стабильно выше 10%.',
      },
      {
        type: 'h2',
        text: '1. Бизнес и стартапы (конверсия 14.2%)',
      },
      {
        type: 'p',
        text: 'Предприниматели понимают ценность инвестиций. Если ты можешь показать, как улучшение контента повлияет на их выручку — они слушают. Средний бюджет на продакшн: 25 000–80 000 ₽/мес.',
      },
      {
        type: 'h2',
        text: '2. Личный бренд и коучинг (конверсия 12.8%)',
      },
      {
        type: 'p',
        text: 'Коучи продают себя. Для них упаковка — это не расход, а инвестиция в продажи. Они быстро считают: «хороший монтаж → больше доверия → больше заявок». Конверсия в оплату высокая.',
      },
      {
        type: 'h2',
        text: '3. Фитнес и ЗОЖ (конверсия 11.5%)',
      },
      {
        type: 'p',
        text: 'Огромное количество каналов в диапазоне 15K–100K. Авторы активны, публикуют часто, и при этом большинство работает без команды. Это твой целевой клиент.',
      },
      {
        type: 'list',
        items: [
          'Бизнес и стартапы — высокий чек, сложный оффер',
          'Личный бренд / коучинг — быстрые решения, хорошая конверсия',
          'Фитнес и ЗОЖ — большой объём, средний чек',
          'Кулинария — долгосрочные контракты, лояльные клиенты',
          'Образование и онлайн-курсы — растущий рынок, стабильный бюджет',
        ],
      },
      {
        type: 'h2',
        text: '4. Кулинария (конверсия 10.9%)',
      },
      {
        type: 'p',
        text: 'Кулинарные каналы часто ищут постоянного подрядчика, а не разового. Если закрываешь первый заказ хорошо — получаешь клиента на несколько месяцев вперёд.',
      },
      {
        type: 'h2',
        text: '5. Образование и онлайн-курсы (конверсия 10.3%)',
      },
      {
        type: 'p',
        text: 'Рынок онлайн-образования в СНГ растёт на 30% ежегодно. Авторы курсов понимают, что упаковка влияет на продажи — и готовы платить за качество.',
      },
      {
        type: 'callout',
        text: '🚀 Вывод: начни с бизнес-ниши или личного бренда, если хочешь быстрый первый заказ. Масштабируйся в фитнес и образование, когда отточишь скрипты.',
      },
    ],
  },

  // ── ГАЙДЫ ──────────────────────────────────────────────────────────
  {
    id: 'guide-first-client',
    category: 'guide',
    tag: 'Гайд',
    tagColor: '#3b82f6',
    cover: 'linear-gradient(135deg, #0c1445 0%, #1e3a8a 50%, #1d4ed8 100%)',
    coverLabel: 'ПЕРВЫЙ КЛИЕНТ',
    date: '10 мая 2026',
    readTime: '7 мин',
    title: 'Как найти первого клиента без портфолио',
    excerpt: 'Нет кейсов — нет клиентов? Это ловушка. Разбираем 4 стратегии старта, которые работают даже с пустым профилем.',
    content: [
      { type: 'lead', text: 'Каждый успешный фрилансер когда-то не имел ни одного кейса. Вот что реально помогает выйти из этого круга.' },
      { type: 'h2', text: 'Стратегия 1: бесплатный тест-проект' },
      { type: 'p', text: 'Предложи одному потенциальному клиенту сделать одно видео бесплатно. Не за отзыв — просто покажи уровень. Если понравится, он сам спросит о продолжении.' },
      { type: 'h2', text: 'Стратегия 2: переработай чужой контент' },
      { type: 'p', text: 'Найди YouTube-канал с плохим монтажом, перемонтируй один ролик и покажи рядом «до/после». Это лучшее портфолио — оно решает реальную проблему реального человека.' },
      { type: 'callout', text: '💡 Совет: выбирай канал с 5–30K подписчиков. Автор уже зарабатывает, но ещё не раздут штатом.' },
    ],
  },
  {
    id: 'guide-cold-dm',
    category: 'guide',
    tag: 'Гайд',
    tagColor: '#3b82f6',
    cover: 'linear-gradient(135deg, #0f172a 0%, #1e293b 45%, #334155 100%)',
    coverLabel: 'ХОЛОДНЫЙ DM',
    date: '8 мая 2026',
    readTime: '9 мин',
    title: 'Холодный DM: полный гайд от первого сообщения до оплаты',
    excerpt: 'Структура оффера, типичные ошибки и скрипты, которые дают ответ в 1 из 8 сообщений — вместо стандартных 1 из 50.',
    content: [
      { type: 'lead', text: 'Холодный DM — не спам. Это искусство предложить нужное, нужному человеку, в нужный момент.' },
      { type: 'h2', text: 'Структура сообщения, которое читают' },
      { type: 'p', text: 'Первые 2 строки: конкретная деталь про канал собеседника. Третья строка: что конкретно ты предлагаешь. Четвёртая: почему именно сейчас. Пятая: мягкий CTA без давления.' },
      { type: 'h2', text: 'Ошибки, которые убивают конверсию' },
      { type: 'list', items: ['Начинать с «Привет, меня зовут…»', 'Хвалить канал без конкретики', 'Слать прайс в первом сообщении', 'Писать больше 5 строк'] },
      { type: 'callout', text: '📊 Данные Outforge: сообщения до 4 строк получают ответ в 2.3× чаще длинных.' },
    ],
  },
  {
    id: 'guide-verification',
    category: 'guide',
    tag: 'Гайд',
    tagColor: '#3b82f6',
    cover: 'linear-gradient(135deg, #042f2e 0%, #134e4a 50%, #0f766e 100%)',
    coverLabel: 'ВЕРИФИКАЦИЯ × 1000',
    date: '6 мая 2026',
    readTime: '5 мин',
    title: 'Как верифицировать 1000 контактов за час',
    excerpt: 'Ручная проверка контактов убивает время. Объясняем, как автоматика отсеивает мёртвые аккаунты и почему это критично для доставляемости.',
    content: [
      { type: 'lead', text: 'Из каждых 100 YouTube-каналов в среднем 28–35 имеют нерабочие контакты. Отправлять туда — значит сжигать аккаунты впустую.' },
      { type: 'h2', text: 'Что проверяет верификация' },
      { type: 'list', items: ['Существует ли Telegram-аккаунт', 'Принимает ли он входящие сообщения', 'Не заблокирован ли', 'Не бот ли'] },
      { type: 'h2', text: 'Как это работает в Outforge' },
      { type: 'p', text: 'Outforge отправляет технический пинг через пул прокси и получает статус за секунды. 1000 контактов — менее 40 минут. Ни один из твоих аккаунтов не светится.' },
    ],
  },
  {
    id: 'guide-youtube-filters',
    category: 'guide',
    tag: 'Гайд',
    tagColor: '#3b82f6',
    cover: 'linear-gradient(135deg, #1e1b4b 0%, #312e81 45%, #4338ca 100%)',
    coverLabel: 'ФИЛЬТРЫ YOUTUBE',
    date: '3 мая 2026',
    readTime: '6 мин',
    title: 'Фильтры YouTube: какие настройки реально работают в 2026',
    excerpt: 'Диапазон подписчиков, частота публикаций, страна, язык — разбираем, как комбинировать фильтры для максимально точного попадания в целевого клиента.',
    content: [
      { type: 'lead', text: 'Не все каналы одинаково полезны как потенциальные клиенты. Правильные фильтры — это 80% успеха ещё до первого сообщения.' },
      { type: 'h2', text: 'Золотой диапазон подписчиков' },
      { type: 'p', text: '30K–150K — оптимальная зона. Канал уже зарабатывает на рекламе, автор ценит качество контента, но ещё не обложен менеджерами. Ты можешь дозвониться напрямую.' },
      { type: 'h2', text: 'Фильтр частоты' },
      { type: 'p', text: 'Канал, публикующий реже раза в 2 недели — неактивный. Ставь фильтр: минимум 3 видео за последние 30 дней.' },
      { type: 'callout', text: '💡 Комбо: 30K–150K подписчиков + последнее видео < 7 дней + ниша = качество 90%+' },
    ],
  },
  {
    id: 'guide-telegram-outreach',
    category: 'guide',
    tag: 'Гайд',
    tagColor: '#3b82f6',
    cover: 'linear-gradient(135deg, #0c2340 0%, #0369a1 50%, #0284c7 100%)',
    coverLabel: 'TG АУТРИЧ',
    date: '30 апр 2026',
    readTime: '8 мин',
    title: 'Аутрич через Telegram: полный пошаговый гайд',
    excerpt: 'Почему Telegram бьёт Email по конверсии в 4–6 раз, как настроить аккаунты, избежать блокировок и выстроить стабильный поток ответов.',
    content: [
      { type: 'lead', text: 'Email даёт 15–25% открываемости. Telegram — 70–90%. Разница огромная. Разбираем, как использовать это преимущество правильно.' },
      { type: 'h2', text: 'Почему именно Telegram' },
      { type: 'p', text: 'YouTube-блогеры держат Telegram для связи с аудиторией. Это значит, что они проверяют его каждый день. Твоё сообщение не потеряется в спаме, как email.' },
      { type: 'h2', text: 'Как не получить бан' },
      { type: 'list', items: ['Не более 30–40 новых диалогов в день с одного аккаунта', 'Прогрев аккаунта 2–3 недели перед массовой рассылкой', 'Разные временные интервалы между сообщениями', 'Персонализация каждого сообщения'] },
    ],
  },
  {
    id: 'guide-pricing',
    category: 'guide',
    tag: 'Гайд',
    tagColor: '#3b82f6',
    cover: 'linear-gradient(135deg, #1c0533 0%, #4a044e 50%, #86198f 100%)',
    coverLabel: 'ЦЕНООБРАЗОВАНИЕ',
    date: '27 апр 2026',
    readTime: '7 мин',
    title: 'Как поставить цену на свои услуги и не продешевить',
    excerpt: 'Большинство фрилансеров ставят цену «по рынку» — и проигрывают. Объясняем value-based pricing и как поднять чек в 2× без потери клиентов.',
    content: [
      { type: 'lead', text: 'Цена — это не стоимость твоего времени. Это стоимость результата для клиента. Поменяй формулу — поменяешь доход.' },
      { type: 'h2', text: 'Почему почасовая ставка вредна' },
      { type: 'p', text: 'Чем лучше ты работаешь, тем быстрее. Чем быстрее — тем меньше зарабатываешь по часам. Продуктивность наказывается. Переходи на проектное ценообразование.' },
      { type: 'callout', text: '📈 Фрилансеры, перешедшие на value-based, в среднем поднимают доход на 68% в первые 3 месяца.' },
    ],
  },
  {
    id: 'guide-follow-up',
    category: 'guide',
    tag: 'Гайд',
    tagColor: '#3b82f6',
    cover: 'linear-gradient(135deg, #0f172a 0%, #1e3a5f 50%, #1e40af 100%)',
    coverLabel: 'FOLLOW-UP',
    date: '24 апр 2026',
    readTime: '5 мин',
    title: 'Follow-up: как напоминать о себе, не раздражая',
    excerpt: '70% сделок закрываются не с первого, а с 3–5 касания. Как правильно возвращаться без агрессии и сохранять отношения.',
    content: [
      { type: 'lead', text: '«Просто хотел уточнить» — так не работает. Вот как делать follow-up так, чтобы тебе отвечали.' },
      { type: 'h2', text: 'Правило ценности в каждом касании' },
      { type: 'p', text: 'Каждое повторное сообщение должно нести новую ценность: статья, релевантный кейс, актуальная новость. Не напоминай о себе — давай повод для диалога.' },
      { type: 'h2', text: 'Тайминг follow-up' },
      { type: 'list', items: ['День 3: конкретная деталь про их новый контент', 'День 7: полезный инсайт из ниши', 'День 14: конкретный оффер с дедлайном', 'День 30: последнее касание, оставить дверь открытой'] },
    ],
  },
  {
    id: 'guide-niche',
    category: 'guide',
    tag: 'Гайд',
    tagColor: '#3b82f6',
    cover: 'linear-gradient(135deg, #0c2340 0%, #164e63 50%, #0891b2 100%)',
    coverLabel: 'ВЫБОР НИШИ',
    date: '21 апр 2026',
    readTime: '6 мин',
    title: 'Как выбрать нишу для фриланса: 7 критериев',
    excerpt: 'Работать для всех — значит не работать ни для кого. Как найти нишу, в которой тебя будут искать сами — без холодного аутрича.',
    content: [
      { type: 'lead', text: 'Нишевание пугает потерей клиентов. На практике — наоборот: узкая специализация приводит больше целевых запросов.' },
      { type: 'h2', text: '7 критериев выбора ниши' },
      { type: 'list', items: ['Достаточный размер рынка (1000+ активных каналов)', 'Готовность платить (монетизированная аудитория)', 'Частые заказы (2+ видео в неделю)', 'Твой интерес или экспертиза', 'Конкуренция не перегрета', 'Понятный болевой контекст', 'Масштабируемость'] },
      { type: 'callout', text: '🎯 Лучший критерий — ниша, в которой ты сам был бы клиентом. Ты знаешь боли изнутри.' },
    ],
  },
  {
    id: 'guide-proposal',
    category: 'guide',
    tag: 'Гайд',
    tagColor: '#3b82f6',
    cover: 'linear-gradient(135deg, #0f2027 0%, #203a43 50%, #2c5364 100%)',
    coverLabel: 'ОФФЕР-МАШИНА',
    date: '18 апр 2026',
    readTime: '8 мин',
    title: 'Как писать оффер, который читают до конца',
    excerpt: 'Структура убедительного предложения, 5 формул открывающей строки и почему конкретика важнее красивых слов.',
    content: [
      { type: 'lead', text: 'У тебя есть 3 секунды, чтобы удержать внимание. Первая строка решает всё.' },
      { type: 'h2', text: 'Формула PPPP' },
      { type: 'p', text: 'Picture → Promise → Proof → Push. Нарисуй ситуацию, пообещай результат, докажи примером, призови к действию.' },
      { type: 'h2', text: '5 формул первой строки' },
      { type: 'list', items: ['«Заметил, что [деталь] — у меня есть идея»', '«Ваш ролик про X набрал N просмотров, следующий может лучше»', '«Работаю с 12 каналами в вашей нише — вот что даёт результат»', '«Сделал для вас быстрый анализ — можно поделиться?»', '«Видел вашу прошлую коллаборацию — есть похожая идея»'] },
    ],
  },
  {
    id: 'guide-portfolio',
    category: 'guide',
    tag: 'Гайд',
    tagColor: '#3b82f6',
    cover: 'linear-gradient(135deg, #14041a 0%, #3b0764 50%, #6b21a8 100%)',
    coverLabel: 'ПОРТФОЛИО',
    date: '14 апр 2026',
    readTime: '6 мин',
    title: 'Портфолио которое продаёт: гайд для видеофрилансера',
    excerpt: 'Не папка с работами, а инструмент продаж. Как структурировать, что показывать первым и почему «до/после» работает лучше всего.',
    content: [
      { type: 'lead', text: 'Портфолио — это не архив. Это аргумент. Каждый элемент должен отвечать на вопрос клиента: «Что я с этого получу?»' },
      { type: 'h2', text: 'Структура продающего портфолио' },
      { type: 'list', items: ['3–5 лучших работ (не все)', 'Для каждой: ниша клиента + задача + результат', '«До/после» когда возможно', 'Отзыв клиента рядом с работой', 'Чёткий следующий шаг для просматривающего'] },
      { type: 'callout', text: '💡 Один точный кейс в нужной нише продаёт лучше, чем 20 разнородных работ.' },
    ],
  },
  {
    id: 'guide-automation',
    category: 'guide',
    tag: 'Гайд',
    tagColor: '#3b82f6',
    cover: 'linear-gradient(135deg, #0a0a1a 0%, #1a1a3e 45%, #2d2d7a 100%)',
    coverLabel: 'АВТОМАТИЗАЦИЯ',
    date: '11 апр 2026',
    readTime: '7 мин',
    title: 'Автоматизация аутрича: что делать руками, что — инструментами',
    excerpt: 'Где автоматика помогает, а где убивает конверсию. Карта решений для фрилансера, который хочет масштабироваться без потери качества.',
    content: [
      { type: 'lead', text: 'Автоматизировать всё — ошибка. Не автоматизировать ничего — тоже ошибка. Разбираем оптимальный баланс.' },
      { type: 'h2', text: 'Что автоматизировать' },
      { type: 'list', items: ['Парсинг YouTube-каналов по нишам', 'Верификация контактов', 'Первичная рассылка по шаблону', 'Планирование follow-up', 'Базовая аналитика ответов'] },
      { type: 'h2', text: 'Что делать только руками' },
      { type: 'list', items: ['Персонализацию первого сообщения', 'Ответы на входящие', 'Переговоры и согласование условий'] },
    ],
  },

  // ── КЕЙСЫ ──────────────────────────────────────────────────────────
  {
    id: 'case-montager-120k',
    category: 'case',
    tag: 'Кейс',
    tagColor: '#22c55e',
    cover: 'linear-gradient(135deg, #052e16 0%, #14532d 45%, #15803d 100%)',
    coverLabel: '120K ₽/МЕС · 3 МЕСЯЦА',
    date: '9 мая 2026',
    readTime: '7 мин',
    title: 'Монтажёр за 3 месяца вышел на 120K ₽/мес через холодный аутрич',
    excerpt: 'Денис работал на бирже за 5–8 тыс. за видео. Через 90 дней системного аутрича — 120K выручки, 6 постоянных клиентов и очередь на месяц вперёд.',
    content: [
      { type: 'lead', text: 'Денис — монтажёр из Екатеринбурга. До Outforge — фриланс на биржах с нестабильным доходом 40–60K. После — стабильные 120K и клиенты, которые сами продлевают контракты.' },
      { type: 'h2', text: 'Стартовая стратегия' },
      { type: 'p', text: 'Первый месяц: 200 каналов / неделю, фильтр 20–80K подписчиков, ниша «бизнес и финансы». 140 верифицированных контактов → 18 ответов → 4 первых клиента.' },
      { type: 'callout', text: '🎯 Итог: 6 постоянных клиентов × 20K ₽/мес = 120K. Рабочий день — 4–5 часов.' },
    ],
  },
  {
    id: 'case-designer-europe',
    category: 'case',
    tag: 'Кейс',
    tagColor: '#22c55e',
    cover: 'linear-gradient(135deg, #0c1f0c 0%, #1a3a1a 45%, #166534 100%)',
    coverLabel: 'КЛИЕНТЫ ИЗ ЕВРОПЫ',
    date: '7 мая 2026',
    readTime: '6 мин',
    title: 'Дизайнер превью нашёл клиентов из Европы через аутрич по русским каналам',
    excerpt: 'Катя искала YouTube-блогеров из Европы с русскоязычной аудиторией — и нашла. 7 клиентов, оплата в евро, работа без лишних барьеров.',
    content: [
      { type: 'lead', text: 'Катя хотела работать с зарубежными клиентами. Outforge помог собрать 300 русскоязычных YouTube-каналов из Европы за 2 дня.' },
      { type: 'h2', text: 'Фильтр гео + язык' },
      { type: 'p', text: 'Настройка: Германия, Франция, Нидерланды, русский язык, 10–50K подписчиков. «Эмигрантские» блогеры — зарабатывают в евро, общаются по-русски.' },
      { type: 'callout', text: '💶 Средний чек вырос с 3 000 ₽ до €150. ROI за первый месяц: x12.' },
    ],
  },
  {
    id: 'case-subtitles-niche',
    category: 'case',
    tag: 'Кейс',
    tagColor: '#22c55e',
    cover: 'linear-gradient(135deg, #0f2921 0%, #064e3b 45%, #047857 100%)',
    coverLabel: 'НИШЕВЫЙ АУТРИЧ',
    date: '4 мая 2026',
    readTime: '5 мин',
    title: 'Специалист по субтитрам нашёл нишу и закрыл 9 клиентов за месяц',
    excerpt: 'Субтитры — казалось бы, узкая услуга. Оказалось — огромный рынок. Как Алина нашла 200 каналов, которым реально нужна эта услуга.',
    content: [
      { type: 'lead', text: 'Алина добавляла субтитры к видео за 1 500 ₽. Она думала, что ниша слишком узкая. Outforge показал 1 200+ каналов, которые ещё не используют субтитры.' },
      { type: 'callout', text: '📈 9 клиентов × 4 500 ₽ (пакет 3 видео) = 40 500 ₽. Первый месяц.' },
    ],
  },
  {
    id: 'case-zero-to-first',
    category: 'case',
    tag: 'Кейс',
    tagColor: '#22c55e',
    cover: 'linear-gradient(135deg, #082f1c 0%, #064e3b 45%, #0d9488 100%)',
    coverLabel: 'НУЛЬ → КЛИЕНТ',
    date: '2 мая 2026',
    readTime: '5 мин',
    title: 'От нуля до первого клиента за 2 недели: честная история',
    excerpt: '28 лет, нет портфолио, нет клиентов, нет уверенности. Через 14 дней — первый оплаченный заказ на 12 000 ₽.',
    content: [
      { type: 'lead', text: 'Максим только начал осваивать монтаж. Решил не ждать «идеального момента» и запустил аутрич на следующей неделе после первого готового ролика.' },
      { type: 'h2', text: 'Что он сделал за 14 дней' },
      { type: 'list', items: ['День 1–2: собрал 150 каналов в нише «кулинария»', 'День 3: верифицировал 98 контактов', 'День 4–10: рассылка 10 сообщений/день', 'День 14: первая оплата 12 000 ₽'] },
      { type: 'callout', text: '🔥 «Мне не нужно было большое портфолио. Мне нужен был правильный канал с правильным запросом.»' },
    ],
  },
  {
    id: 'case-coaching-niche',
    category: 'case',
    tag: 'Кейс',
    tagColor: '#22c55e',
    cover: 'linear-gradient(135deg, #022c22 0%, #065f46 45%, #059669 100%)',
    coverLabel: '8 КЛИЕНТОВ · 3 НЕДЕЛИ',
    date: '29 апр 2026',
    readTime: '6 мин',
    title: 'Продюсер закрыл 8 клиентов-коучей за 3 недели',
    excerpt: 'Ниша «личный бренд и коучинг» — одна из самых горячих. Почему коучи легко соглашаются на сотрудничество и как правильно строить оффер.',
    content: [
      { type: 'lead', text: 'Ира — видеопродюсер. Когда она нацелилась на коучей с YouTube-каналами 15–60K, конверсия рассылки выросла до 14.3% — вдвое выше её среднего.' },
      { type: 'h2', text: 'Почему коучи конвертируются лучше' },
      { type: 'p', text: 'Коуч продаёт себя как бренд. Качество видео напрямую влияет на доверие к нему. Это инвестиция в продажи, не расход.' },
      { type: 'callout', text: '💰 8 клиентов × средний чек 18 000 ₽ = 144 000 ₽ за первые 3 недели.' },
    ],
  },
  {
    id: 'case-100-messages',
    category: 'case',
    tag: 'Кейс',
    tagColor: '#22c55e',
    cover: 'linear-gradient(135deg, #031a0e 0%, #04522e 45%, #16a34a 100%)',
    coverLabel: '100 СООБЩЕНИЙ',
    date: '26 апр 2026',
    readTime: '7 мин',
    title: '100 холодных сообщений: что реально вышло — честный отчёт',
    excerpt: 'Полная статистика одной реальной кампании: сколько ответов, сколько отказов, сколько в деньгах — без прикрас.',
    content: [
      { type: 'lead', text: 'Мы взяли реальную кампанию одного из пользователей Outforge и разложили по цифрам каждый этап — от первого сообщения до оплаты.' },
      { type: 'h2', text: 'Цифры' },
      { type: 'list', items: ['100 сообщений отправлено', '13 ответов (13%)', '6 конструктивных диалогов', '3 созвона', '2 оплаченных заказа на 28 000 ₽ суммарно'] },
      { type: 'callout', text: '📊 Это нормально — и именно так работает аутрич. ROI: 28K ₽ за 3 часа работы.' },
    ],
  },
  {
    id: 'case-fitness-niche',
    category: 'case',
    tag: 'Кейс',
    tagColor: '#22c55e',
    cover: 'linear-gradient(135deg, #062212 0%, #14532d 45%, #22c55e 100%)',
    coverLabel: 'ФИТНЕС-НИША',
    date: '23 апр 2026',
    readTime: '5 мин',
    title: 'Монтажёр специализировался на фитнес-каналах: что получилось',
    excerpt: 'Фитнес — не самая очевидная ниша для дорогого монтажа. Но именно там оказался постоянный клиент на 6 месяцев вперёд.',
    content: [
      { type: 'lead', text: 'Сергей написал 60 сообщений за 3 дня. Один клиент превратился в 6-месячный контракт на 15 000 ₽/мес.' },
      { type: 'h2', text: 'Почему фитнес работает' },
      { type: 'p', text: 'Фитнес-блогеры публикуют 3–5 раз в неделю, часто не имеют монтажёра и ищут на постоянку. Один закрытый клиент = многомесячный доход.' },
    ],
  },
  {
    id: 'case-first-year',
    category: 'case',
    tag: 'Кейс',
    tagColor: '#22c55e',
    cover: 'linear-gradient(135deg, #011210 0%, #0a3728 45%, #0f766e 100%)',
    coverLabel: 'ПЕРВЫЙ ГОД',
    date: '19 апр 2026',
    readTime: '9 мин',
    title: 'Первый год фриланса: честная история без прикрас',
    excerpt: 'Провалы, лучшие месяцы, что реально помогло и что стоило сделать иначе. Один год — один откровенный разбор.',
    content: [
      { type: 'lead', text: 'Оля ушла с офисной работы и начала фриланс-монтаж. Через год — своя команда и стабильные 200K. Но первые 4 месяца были очень тяжёлыми.' },
      { type: 'h2', text: 'Поворотный момент' },
      { type: 'p', text: 'Месяц 4: первый клиент из аутрича через Outforge — образовательный канал 45K подписчиков. Контракт на 3 месяца, 25K ₽/мес. После этого всё изменилось.' },
    ],
  },
  {
    id: 'case-automation-win',
    category: 'case',
    tag: 'Кейс',
    tagColor: '#22c55e',
    cover: 'linear-gradient(135deg, #021a12 0%, #064e3b 50%, #10b981 100%)',
    coverLabel: 'АВТОМАТИЗАЦИЯ',
    date: '15 апр 2026',
    readTime: '6 мин',
    title: 'Автоматизация спасла фрилансера от выгорания и потери клиентов',
    excerpt: 'Когда Паша занимался аутричем вручную, он тратил 4 часа в день — и всё равно терял потенциальных заказчиков. Outforge это исправил.',
    content: [
      { type: 'lead', text: 'Ручной аутрич — это монотонная работа. Ты копируешь контакты, проверяешь вручную, пишешь одно за другим. Через 2 месяца — выгорание.' },
      { type: 'h2', text: 'Что изменила автоматизация' },
      { type: 'list', items: ['С 4 часов до 40 минут в день на аутрич', 'Количество охваченных каналов выросло в 8×', 'Ноль пропущенных follow-up', 'Освободившееся время → на качество работы'] },
    ],
  },

  // ── СТРАТЕГИЯ ──────────────────────────────────────────────────────
  {
    id: 'strategy-positioning',
    category: 'strategy',
    tag: 'Стратегия',
    tagColor: '#a855f7',
    cover: 'linear-gradient(135deg, #1e0533 0%, #4c1d95 45%, #7c3aed 100%)',
    coverLabel: 'ПОЗИЦИОНИРОВАНИЕ',
    date: '8 мая 2026',
    readTime: '8 мин',
    title: 'Позиционирование: как выделиться среди тысяч фрилансеров',
    excerpt: 'Почему «опытный монтажёр» — плохое позиционирование, и как сформулировать УТП так, чтобы клиент сразу понял «это для меня».',
    content: [
      { type: 'lead', text: 'На рынке тысячи монтажёров. Но монтажёр для фитнес-каналов с быстрым дедлайном — один. Стань этим одним.' },
      { type: 'h2', text: 'Формула позиционирования' },
      { type: 'p', text: '[Услуга] + [для кого] + [результат/скорость/особенность]. Пример: «Монтаж для YouTube-коучей с доставкой за 24 часа и правками до утверждения».' },
      { type: 'callout', text: '💡 Чем уже позиционирование — тем выше чек. Нишевые специалисты берут в 2–3× дороже дженералистов.' },
    ],
  },
  {
    id: 'strategy-referral',
    category: 'strategy',
    tag: 'Стратегия',
    tagColor: '#a855f7',
    cover: 'linear-gradient(135deg, #1a0a3d 0%, #3730a3 45%, #6366f1 100%)',
    coverLabel: 'РЕФЕРАЛЫ',
    date: '5 мая 2026',
    readTime: '6 мин',
    title: 'Реферальная система: как сделать чтобы клиенты приводили клиентов',
    excerpt: 'Самые дешёвые лиды — это рекомендации. Как выстроить систему, при которой клиенты сами рассказывают о тебе коллегам.',
    content: [
      { type: 'lead', text: 'Реферальный клиент приходит тёплым. Конверсия в оплату — в 3–4× выше холодного аутрича.' },
      { type: 'h2', text: 'Три механики реферальной системы' },
      { type: 'list', items: ['Скидка рефералу: -10% на первый месяц', 'Бонус реферреру: бесплатное видео за каждого приведённого клиента', 'Партнёрство: взаимный обмен рекомендациями с другими фрилансерами'] },
    ],
  },
  {
    id: 'strategy-seasonal',
    category: 'strategy',
    tag: 'Стратегия',
    tagColor: '#a855f7',
    cover: 'linear-gradient(135deg, #1c1040 0%, #4338ca 45%, #818cf8 100%)',
    coverLabel: 'СЕЗОННОСТЬ',
    date: '2 мая 2026',
    readTime: '5 мин',
    title: 'Сезонные паттерны: когда лучше запускать аутрич',
    excerpt: 'Аутрич в понедельник утром или в пятницу вечером — не одно и то же. Разбираем, когда YouTube-блогеры наиболее восприимчивы к новым предложениям.',
    content: [
      { type: 'lead', text: 'Данные 80 000+ кампаний в Outforge: время и день недели влияют на отклик до 40%.' },
      { type: 'h2', text: 'Лучшие дни для рассылки' },
      { type: 'list', items: ['Вторник 10:00–12:00 — пик активности', 'Среда и четверг — стабильно хороши', 'Понедельник — плохо: люди в потоке задач', 'Пятница после 15:00 — мёртвая зона'] },
      { type: 'callout', text: '🗓 Январь, сентябрь и март — пиковые месяцы для запуска. Блогеры обновляют бюджеты.' },
    ],
  },
  {
    id: 'strategy-ab-test',
    category: 'strategy',
    tag: 'Стратегия',
    tagColor: '#a855f7',
    cover: 'linear-gradient(135deg, #1e103f 0%, #5b21b6 45%, #8b5cf6 100%)',
    coverLabel: 'A/B ТЕСТ',
    date: '29 апр 2026',
    readTime: '6 мин',
    title: 'A/B тест скриптов: как понять, что реально работает',
    excerpt: 'Интуиция — враг оптимизации. Как тестировать скрипты, варианты офферов и тайминг — и делать выводы на реальных данных.',
    content: [
      { type: 'lead', text: 'Разница между двумя вариантами первой строки может быть 3× по конверсии.' },
      { type: 'h2', text: 'Что тестировать в первую очередь' },
      { type: 'list', items: ['Первая строка (самый большой рычаг)', 'С вопроса или с утверждения', 'Упоминать цену или нет', 'Длина: 3 строки vs 6 строк', 'Эмодзи или без'] },
      { type: 'callout', text: '📊 Правило: минимум 50 отправок на каждый вариант, прежде чем делать выводы.' },
    ],
  },
  {
    id: 'strategy-upsell',
    category: 'strategy',
    tag: 'Стратегия',
    tagColor: '#a855f7',
    cover: 'linear-gradient(135deg, #160d3d 0%, #4c1d95 45%, #a855f7 100%)',
    coverLabel: 'АПСЕЙЛ',
    date: '25 апр 2026',
    readTime: '5 мин',
    title: 'Апсейл: как продавать больше текущим клиентам',
    excerpt: 'Привлечение нового клиента стоит в 5–7× дороже, чем повторная продажа текущему. Как выстроить систему повторных продаж без давления.',
    content: [
      { type: 'lead', text: 'Твой лучший потенциальный клиент — это тот, кто уже платит тебе.' },
      { type: 'h2', text: 'Три способа апсейла для видеофрилансера' },
      { type: 'list', items: ['Пакет: вместо 1 видео — 4 в месяц со скидкой', 'Смежная услуга: монтаж → + субтитры → + превью', 'Ретейнер: фиксированная плата за X видео в месяц'] },
    ],
  },
  {
    id: 'strategy-audience-research',
    category: 'strategy',
    tag: 'Стратегия',
    tagColor: '#a855f7',
    cover: 'linear-gradient(135deg, #170830 0%, #3b0764 45%, #9333ea 100%)',
    coverLabel: 'ИССЛЕДОВАНИЕ',
    date: '21 апр 2026',
    readTime: '7 мин',
    title: 'Исследование целевой аудитории перед рассылкой: как не стрелять вслепую',
    excerpt: 'Час исследования до запуска кампании экономит дни работы после. Как изучить нишу, понять боли и написать оффер, который попадает в точку.',
    content: [
      { type: 'lead', text: 'Самая частая ошибка: отправить 500 сообщений, не понимая, что реально болит у получателя. Результат: 1–2% ответов.' },
      { type: 'h2', text: 'Чеклист исследования перед запуском' },
      { type: 'list', items: ['Посмотри 5–10 каналов в нише', 'Прочитай комментарии под видео — там боли', 'Проверь, есть ли у них команда', 'Оцени частоту публикаций и вовлечённость'] },
    ],
  },
  {
    id: 'strategy-bundle',
    category: 'strategy',
    tag: 'Стратегия',
    tagColor: '#a855f7',
    cover: 'linear-gradient(135deg, #1a0a4a 0%, #5b21b6 45%, #c084fc 100%)',
    coverLabel: 'ПАКЕТЫ',
    date: '16 апр 2026',
    readTime: '6 мин',
    title: 'Пакетные предложения: как получать больше с одного клиента',
    excerpt: 'Разовые заказы — нестабильный доход. Пакеты и ретейнеры — это предсказуемость. Как правильно упаковать услуги в пакет, который продаёт себя сам.',
    content: [
      { type: 'lead', text: 'Фрилансер с 5 клиентами-ретейнерами стабильнее, чем с 30 разовыми заказчиками.' },
      { type: 'h2', text: 'Структура успешного пакета' },
      { type: 'list', items: ['Чёткое количество единиц (4 видео/мес)', 'Фиксированная цена — клиент планирует бюджет', 'Дедлайны в контракте', 'Небольшая скидка vs разовая цена (не более 15%)'] },
    ],
  },

  // ── ИНСТРУМЕНТЫ ──────────────────────────────────────────────────────
  {
    id: 'tools-stack',
    category: 'tools',
    tag: 'Инструменты',
    tagColor: '#f59e0b',
    cover: 'linear-gradient(135deg, #1c1200 0%, #78350f 45%, #d97706 100%)',
    coverLabel: 'СТЕК 2026',
    date: '11 мая 2026',
    readTime: '7 мин',
    title: 'Полный стек инструментов фрилансера-аутричера в 2026 году',
    excerpt: 'Что использовать для парсинга, верификации, рассылки, CRM и аналитики. Обзор 12 инструментов с оценками от практиков.',
    content: [
      { type: 'lead', text: 'Правильный стек инструментов экономит 15–20 часов в неделю.' },
      { type: 'h2', text: 'Основной стек' },
      { type: 'list', items: ['Outforge — парсинг YouTube + верификация + рассылка', 'Notion или Airtable — база клиентов', 'Calendly — запись на созвоны', 'Loom — видеообзоры для персонализированных офферов'] },
      { type: 'callout', text: '💡 Один инструмент делает одно хорошо. Не ищи «всё в одном» — ищи лучшее в каждой задаче.' },
    ],
  },
  {
    id: 'tools-crm-setup',
    category: 'tools',
    tag: 'Инструменты',
    tagColor: '#f59e0b',
    cover: 'linear-gradient(135deg, #1a0f00 0%, #92400e 45%, #b45309 100%)',
    coverLabel: 'CRM ЗА 1 ВЕЧЕР',
    date: '8 мая 2026',
    readTime: '6 мин',
    title: 'Как настроить CRM за один вечер и никогда не терять лиды',
    excerpt: 'Не нужен дорогой Salesforce. Простая система на 5 этапов воронки и 3 тега — этого достаточно для 95% фрилансеров.',
    content: [
      { type: 'lead', text: 'Потерянный лид — потерянные деньги. Простая CRM-воронка решает эту проблему раз и навсегда.' },
      { type: 'h2', text: 'Пять этапов воронки' },
      { type: 'list', items: ['Найден → Написано → Ответил → Созвон → Оплата'] },
      { type: 'callout', text: '📋 Каждое утро: пройтись по «горячим» и «дозревающим» — и сделать одно действие по каждому.' },
    ],
  },
  {
    id: 'tools-proxy',
    category: 'tools',
    tag: 'Инструменты',
    tagColor: '#f59e0b',
    cover: 'linear-gradient(135deg, #1c0d00 0%, #7c2d12 45%, #ea580c 100%)',
    coverLabel: 'ПРОКСИ',
    date: '5 мая 2026',
    readTime: '5 мин',
    title: 'Зачем нужны прокси в аутриче и как их правильно выбрать',
    excerpt: 'Аутрич без прокси — риск для аккаунтов. Объясняем, как прокси защищают от блокировок и почему нельзя экономить на этом.',
    content: [
      { type: 'lead', text: 'Telegram отслеживает паттерны отправки. Множество сообщений с одного IP — красный флаг.' },
      { type: 'h2', text: 'Типы прокси' },
      { type: 'list', items: ['Резидентные — лучшая защита', 'Мобильные — хороши для Telegram', 'Датацентровые — дешевле, но чаще банятся', 'Бесплатные — никогда'] },
      { type: 'callout', text: '⚠️ Правило: один аккаунт — один прокси. Не переиспользуй.' },
    ],
  },
  {
    id: 'tools-ai-messages',
    category: 'tools',
    tag: 'Инструменты',
    tagColor: '#f59e0b',
    cover: 'linear-gradient(135deg, #1a0e00 0%, #854d0e 45%, #ca8a04 100%)',
    coverLabel: 'AI ДЛЯ DM',
    date: '1 мая 2026',
    readTime: '6 мин',
    title: 'AI для написания офферов: как не звучать как робот',
    excerpt: 'AI ускоряет написание в 5–10 раз, но стандартные промпты дают безликий текст. Как использовать AI правильно — с нужным голосом и контекстом.',
    content: [
      { type: 'lead', text: 'ChatGPT напишет скрипт за 30 секунд. Но получится шаблонно. Секрет — правильный промпт с нужным контекстом.' },
      { type: 'h2', text: 'Промпт для хорошего AI-оффера' },
      { type: 'p', text: 'Включи: нишу получателя, конкретную деталь их канала, услугу, тон, объём (3–4 строки), запрет на клише.' },
      { type: 'callout', text: '🤖 AI пишет каркас, ты дописываешь одну конкретную деталь — и сообщение становится живым.' },
    ],
  },
  {
    id: 'tools-analytics',
    category: 'tools',
    tag: 'Инструменты',
    tagColor: '#f59e0b',
    cover: 'linear-gradient(135deg, #120a00 0%, #713f12 45%, #d97706 100%)',
    coverLabel: 'АНАЛИТИКА',
    date: '28 апр 2026',
    readTime: '5 мин',
    title: 'Аналитика рассылок: какие метрики смотреть и что они говорят',
    excerpt: 'Response rate, conversation rate, close rate — три числа, которые говорят всё о здоровье твоей аутрич-системы.',
    content: [
      { type: 'lead', text: 'Без аналитики аутрич — это азартная игра. С аналитикой — управляемый процесс.' },
      { type: 'h2', text: 'Три ключевых метрики' },
      { type: 'list', items: ['Response rate (цель: 8–15%) — качество скрипта', 'Conversation rate (цель: 50%+) — качество диалога', 'Close rate (цель: 30%+ от созвонов) — переговорный навык'] },
      { type: 'callout', text: '📈 Если response rate низкий — меняй первое сообщение. Если conversation rate низкий — меняй follow-up.' },
    ],
  },
  {
    id: 'tools-warmup',
    category: 'tools',
    tag: 'Инструменты',
    tagColor: '#f59e0b',
    cover: 'linear-gradient(135deg, #1a0800 0%, #92400e 50%, #f59e0b 100%)',
    coverLabel: 'ПРОГРЕВ',
    date: '24 апр 2026',
    readTime: '6 мин',
    title: 'Прогрев Telegram-аккаунтов: зачем это нужно и как делать правильно',
    excerpt: 'Новый аккаунт не должен сразу делать массовую рассылку. Почему прогрев критичен и как сократить его с 4 недель до 10 дней.',
    content: [
      { type: 'lead', text: 'Telegram видит паттерны. Аккаунт, который сразу начинает писать 50 незнакомцам — подозрительный.' },
      { type: 'h2', text: 'График прогрева' },
      { type: 'list', items: ['Дни 1–3: только личные диалоги', 'Дни 4–7: 5–10 новых диалогов в день', 'Дни 8–14: 15–20 диалогов', 'День 15+: рабочий режим до 40 диалогов/день'] },
    ],
  },
  {
    id: 'tools-calendar',
    category: 'tools',
    tag: 'Инструменты',
    tagColor: '#f59e0b',
    cover: 'linear-gradient(135deg, #1c1000 0%, #78350f 45%, #f59e0b 100%)',
    coverLabel: 'СИСТЕМА 90 ДНЕЙ',
    date: '20 апр 2026',
    readTime: '7 мин',
    title: 'Планирование аутрича: система на 90 дней для стабильного потока клиентов',
    excerpt: 'Спонтанный аутрич даёт спонтанный результат. Как выстроить 90-дневный план, при котором каждый месяц есть новые клиенты — предсказуемо.',
    content: [
      { type: 'lead', text: 'Главная проблема фрилансера: то густо, то пусто. Система 90 дней решает её через цикличное планирование.' },
      { type: 'h2', text: 'Структура цикла' },
      { type: 'list', items: ['Месяц 1: исследование + первый поток рассылки', 'Месяц 2: follow-up + закрытие + первые клиенты', 'Месяц 3: масштаб + запуск следующего цикла'] },
      { type: 'callout', text: '🔄 Запускай следующий цикл ещё до окончания предыдущего. Воронка не пустеет.' },
    ],
  },

  // ── ШАБЛОНЫ ──────────────────────────────────────────────────────
  {
    id: 'templates-dm-montage',
    category: 'templates',
    tag: 'Шаблон',
    tagColor: '#06b6d4',
    cover: 'linear-gradient(135deg, #042f33 0%, #164e63 45%, #0891b2 100%)',
    coverLabel: 'DM ДЛЯ МОНТАЖЁРА',
    date: '10 мая 2026',
    readTime: '4 мин',
    title: '3 готовых шаблона DM для монтажёра (с разбором)',
    excerpt: 'Скопируй, адаптируй, отправь. Три рабочих скрипта для холодного первого сообщения с объяснением, почему каждое слово стоит на своём месте.',
    content: [
      { type: 'lead', text: 'Возьми шаблон, добавь одну конкретную деталь про канал получателя — и конверсия вырастет кратно.' },
      { type: 'h2', text: 'Шаблон 1: короткий и прямой' },
      { type: 'p', text: '«[Имя], посмотрел ваш ролик про [тема]. Заметил, что [деталь]. У меня есть пара идей, как это улучшить — можно поделиться?»' },
      { type: 'h2', text: 'Шаблон 2: с социальным доказательством' },
      { type: 'p', text: '«Работаю монтажёром с каналами в [ниша]. Один из последних проектов вырос с 30K до 80K подписчиков. Вижу похожий потенциал у вашего канала — интересно?»' },
      { type: 'callout', text: '⚡ Первое слово — всегда имя получателя. +15–20% к вероятности прочтения.' },
    ],
  },
  {
    id: 'templates-dm-design',
    category: 'templates',
    tag: 'Шаблон',
    tagColor: '#06b6d4',
    cover: 'linear-gradient(135deg, #012027 0%, #0c4a6e 45%, #0ea5e9 100%)',
    coverLabel: 'DM ДЛЯ ДИЗАЙНЕРА',
    date: '7 мая 2026',
    readTime: '3 мин',
    title: 'Шаблоны DM для дизайнера превью с разбором ошибок',
    excerpt: 'Специфика ниши дизайна превью — предложение должно быть визуальным. Как это передать текстом и когда стоит прикладывать образец.',
    content: [
      { type: 'lead', text: 'Дизайнер превью продаёт результат глазами. Ваш оффер должно это транслировать — даже в текстовом DM.' },
      { type: 'h2', text: 'Главный приём' },
      { type: 'p', text: 'Предложи сделать бесплатный mockup одного превью. Клиент видит конкретный результат до оплаты.' },
      { type: 'callout', text: '🎨 «Могу сделать тестовый вариант превью для вашего последнего видео — бесплатно, без обязательств.»' },
    ],
  },
  {
    id: 'templates-follow-up',
    category: 'templates',
    tag: 'Шаблон',
    tagColor: '#06b6d4',
    cover: 'linear-gradient(135deg, #023344 0%, #075985 45%, #0369a1 100%)',
    coverLabel: 'FOLLOW-UP ×3',
    date: '4 мая 2026',
    readTime: '4 мин',
    title: '3 шаблона для follow-up: мягко, конкретно, с ценностью',
    excerpt: 'Возвращаться без раздражения — это навык. Три варианта follow-up для разных ситуаций: молчание, «подумаю», и «не актуально».',
    content: [
      { type: 'lead', text: 'Follow-up — не «вы ещё думаете?». Follow-up — это новый повод для диалога.' },
      { type: 'h2', text: 'Шаблон 1: после молчания (день 3–5)' },
      { type: 'p', text: '«[Имя], наткнулся на интересный кейс по [ваша ниша]. Кстати, моё предыдущее предложение ещё в силе — если актуально, напишите!»' },
      { type: 'h2', text: 'Шаблон 2: после «подумаю»' },
      { type: 'p', text: '«[Имя], если вопрос в бюджете — у меня есть более компактный формат для старта. Или могу прислать примеры работ по вашей теме?»' },
    ],
  },
  {
    id: 'templates-proposal',
    category: 'templates',
    tag: 'Шаблон',
    tagColor: '#06b6d4',
    cover: 'linear-gradient(135deg, #011a28 0%, #0c4a6e 45%, #0284c7 100%)',
    coverLabel: 'КП',
    date: '30 апр 2026',
    readTime: '5 мин',
    title: 'Шаблон коммерческого предложения для видеофрилансера',
    excerpt: 'Когда клиент просит «прислать КП» — это шанс. Готовый шаблон с правильной структурой, который закрывает вопросы раньше, чем их зададут.',
    content: [
      { type: 'lead', text: 'Хорошее КП отвечает на 4 вопроса: что именно, за сколько, как это выглядит, что будет дальше.' },
      { type: 'h2', text: 'Структура КП' },
      { type: 'list', items: ['Заголовок: конкретно под проект', 'Что входит: по пунктам, без воды', 'Стоимость: прозрачно, с вариантами', 'Примеры: 2–3 релевантные работы', 'Следующий шаг: «ответьте, и я подготовлю договор за 1 день»'] },
    ],
  },
  {
    id: 'templates-objections',
    category: 'templates',
    tag: 'Шаблон',
    tagColor: '#06b6d4',
    cover: 'linear-gradient(135deg, #012030 0%, #0369a1 45%, #38bdf8 100%)',
    coverLabel: 'ВОЗРАЖЕНИЯ',
    date: '26 апр 2026',
    readTime: '4 мин',
    title: 'Шаблоны ответов на 6 типичных возражений клиента',
    excerpt: '«Дорого», «сам справлюсь», «не сейчас», «нет бюджета» — готовые ответы, которые переводят возражение в диалог, а не в тупик.',
    content: [
      { type: 'lead', text: 'Возражение — это не отказ. Это запрос на дополнительную информацию.' },
      { type: 'h2', text: '«Дорого»' },
      { type: 'p', text: '«Понимаю. Можем начать с одного видео, чтобы вы видели результат до принятия решения о регулярном формате.»' },
      { type: 'h2', text: '«Не сейчас»' },
      { type: 'p', text: '«Когда лучше вернуться к этому? Запишу и напомню — чтобы не отвлекать раньше времени.»' },
      { type: 'callout', text: '💡 Всегда заканчивай на конкретном следующем шаге.' },
    ],
  },
  {
    id: 'templates-dm-coach',
    category: 'templates',
    tag: 'Шаблон',
    tagColor: '#06b6d4',
    cover: 'linear-gradient(135deg, #011c2a 0%, #0c4a6e 45%, #06b6d4 100%)',
    coverLabel: 'DM ДЛЯ ПРОДЮСЕРА',
    date: '22 апр 2026',
    readTime: '4 мин',
    title: 'Шаблоны DM для видеопродюсера и контент-менеджера',
    excerpt: 'Продюсер продаёт не монтаж, а результат. Как сформулировать оффер так, чтобы клиент понял: ты берёшь на себя всё — ему остаётся только сниматься.',
    content: [
      { type: 'lead', text: 'Продюсер — партнёр, который берёт на себя весь производственный процесс. Твой оффер должен это транслировать.' },
      { type: 'h2', text: 'Шаблон для продюсера' },
      { type: 'p', text: '«[Имя], у вас сильные темы и отличная экспертиза. Думаю, с правильной упаковкой вы могли бы расти в 2–3× быстрее. Я занимаюсь полным продакшном для [ниша] каналов. Можем поговорить 15 минут?»' },
    ],
  },

  // ── МЫШЛЕНИЕ ──────────────────────────────────────────────────────
  {
    id: 'mindset-rejection',
    category: 'mindset',
    tag: 'Мышление',
    tagColor: '#fb923c',
    cover: 'linear-gradient(135deg, #1c0a00 0%, #9a3412 45%, #fb923c 100%)',
    coverLabel: '50 ОТКАЗОВ',
    date: '9 мая 2026',
    readTime: '5 мин',
    title: 'Как не сдаться после 50 отказов: психология аутрича',
    excerpt: 'Отказ — нормальная часть процесса, а не оценка тебя. Как переосмыслить «нет», чтобы оно не разрушало мотивацию.',
    content: [
      { type: 'lead', text: 'Если ты получаешь 8–12% ответов — это хороший результат. Значит, 88% не отвечают. Важно понять: они не отказывают тебе как человеку.' },
      { type: 'h2', text: 'Почему не отвечают' },
      { type: 'list', items: ['Не увидели сообщение (занятость)', 'Не актуально именно сейчас', 'Уже есть подрядчик', 'Нет бюджета в этом месяце'] },
      { type: 'callout', text: '🧠 Каждое «нет» — это шаг ближе к «да». 50 отказов означает, что ты уже на полпути к следующему клиенту.' },
    ],
  },
  {
    id: 'mindset-burnout',
    category: 'mindset',
    tag: 'Мышление',
    tagColor: '#fb923c',
    cover: 'linear-gradient(135deg, #1a0600 0%, #7c2d12 45%, #ea580c 100%)',
    coverLabel: 'ВЫГОРАНИЕ',
    date: '6 мая 2026',
    readTime: '6 мин',
    title: 'Выгорание в аутриче: как распознать и что делать',
    excerpt: 'Аутрич — монотонный процесс. Признаки выгорания появляются незаметно. Как заметить вовремя и не дать ему убить продуктивность.',
    content: [
      { type: 'lead', text: 'Выгорание в аутриче выглядит иначе: ты не устаёшь от работы — ты устаёшь от бесконечных «нет».' },
      { type: 'h2', text: 'Признаки выгорания' },
      { type: 'list', items: ['Каждое новое сообщение кажется бессмысленным', 'Ты откладываешь рассылку', 'Пишешь копипастой без персонализации', 'Не радуешься ответам'] },
      { type: 'h2', text: 'Что помогает' },
      { type: 'list', items: ['Автоматизировать рутину', 'Делать перерывы между кампаниями', 'Менять нишу или скрипт раз в 3–4 недели', 'Считать деньги, а не отказы'] },
    ],
  },
  {
    id: 'mindset-consistency',
    category: 'mindset',
    tag: 'Мышление',
    tagColor: '#fb923c',
    cover: 'linear-gradient(135deg, #1a0a00 0%, #9a3412 50%, #f97316 100%)',
    coverLabel: 'ПОСЛЕДОВАТЕЛЬНОСТЬ',
    date: '3 мая 2026',
    readTime: '5 мин',
    title: 'Последовательность важнее таланта: почему стабильность побеждает',
    excerpt: '10 сообщений в день в течение месяца лучше, чем 300 за один день раз в квартал. Математика стабильного аутрича.',
    content: [
      { type: 'lead', text: 'Самые успешные аутричеры — не самые талантливые. Самые стабильные.' },
      { type: 'h2', text: 'Математика стабильности' },
      { type: 'p', text: '10 сообщений/день × 22 рабочих дня = 220 в месяц. При 10% ответов = 22 диалога. При 30% закрытии = ~7 новых клиентов. Каждый месяц.' },
      { type: 'callout', text: '🎯 Ключ не в «правильном моменте», а в «каждый день, независимо от настроения».' },
    ],
  },
  {
    id: 'mindset-fear',
    category: 'mindset',
    tag: 'Мышление',
    tagColor: '#fb923c',
    cover: 'linear-gradient(135deg, #180800 0%, #7c2d12 45%, #c2410c 100%)',
    coverLabel: 'СТРАХ КОНТАКТА',
    date: '30 апр 2026',
    readTime: '4 мин',
    title: 'Страх холодного контакта: откуда берётся и как преодолеть',
    excerpt: 'Почему мозг воспринимает холодное сообщение как угрозу, и простые техники, которые помогают начать действовать несмотря на страх.',
    content: [
      { type: 'lead', text: 'Страх перед холодным контактом — не слабость. Это эволюционный механизм. Но его можно перепрограммировать.' },
      { type: 'h2', text: 'Техника: «наихудший сценарий»' },
      { type: 'p', text: 'Что случится в худшем случае? Не ответят. Всё. Жизнь продолжается. Осознание безопасности отказа снимает 80% страха.' },
    ],
  },
  {
    id: 'mindset-goals',
    category: 'mindset',
    tag: 'Мышление',
    tagColor: '#fb923c',
    cover: 'linear-gradient(135deg, #1c0a00 0%, #9a3412 45%, #fb923c 100%)',
    coverLabel: 'ЦЕЛИ ДЛЯ ФРИЛАНСЕРА',
    date: '26 апр 2026',
    readTime: '6 мин',
    title: 'Постановка целей: система OKR адаптированная для соло-фрилансера',
    excerpt: 'OKR — инструмент Google и больших компаний. Как адаптировать его для одного человека и перестать «просто работать» без конкретных результатов.',
    content: [
      { type: 'lead', text: 'Работать много — не то же самое, что работать в нужном направлении. OKR помогает не потерять цель в ежедневной рутине.' },
      { type: 'h2', text: 'OKR для фрилансера: пример' },
      { type: 'p', text: 'Objective: выйти на 150K ₽/мес к концу квартала. Key Results: 3 новых постоянных клиента, средний чек > 30K, 0 заказов через биржи.' },
      { type: 'callout', text: '📋 Проверяй OKR каждую пятницу. 10 минут рефлексии экономят месяц потерянного направления.' },
    ],
  },
  {
    id: 'mindset-money',
    category: 'mindset',
    tag: 'Мышление',
    tagColor: '#fb923c',
    cover: 'linear-gradient(135deg, #1a0800 0%, #78350f 45%, #f59e0b 100%)',
    coverLabel: 'ДЕНЬГИ И ЦЕННИК',
    date: '22 апр 2026',
    readTime: '5 мин',
    title: 'Денежное мышление: почему фрилансеры не повышают ценник',
    excerpt: 'Синдром самозванца, страх потерять клиентов, неуверенность в своей ценности — три психологических барьера, которые удерживают доход на одном уровне.',
    content: [
      { type: 'lead', text: 'Технически хороший фрилансер часто зарабатывает меньше менее опытного — потому что не умеет говорить о цене.' },
      { type: 'h2', text: 'Три барьера к повышению ценника' },
      { type: 'list', items: ['«Клиенты уйдут» — уйдут не те, которые нужны', '«Я недостаточно хорош» — ценник не коррелирует с мастерством', '«На рынке дешевле» — неправда для нишевых специалистов'] },
      { type: 'callout', text: '💡 Подними ценник на 30% для следующего нового клиента. Старых не трогай. Посмотри, что будет.' },
    ],
  },
  {
    id: 'mindset-growth',
    category: 'mindset',
    tag: 'Мышление',
    tagColor: '#fb923c',
    cover: 'linear-gradient(135deg, #1c0900 0%, #c2410c 45%, #fb923c 100%)',
    coverLabel: 'МЫШЛЕНИЕ РОСТА',
    date: '17 апр 2026',
    readTime: '5 мин',
    title: 'Мышление роста для фрилансера: как перестать бояться масштабироваться',
    excerpt: 'Масштаб пугает: больше клиентов = больше ответственности. Но есть способ расти постепенно, не теряя контроль и качество.',
    content: [
      { type: 'lead', text: 'Большинство фрилансеров останавливаются на «комфортном» уровне дохода — не потому что не могут больше, а потому что боятся роста.' },
      { type: 'h2', text: 'Три принципа масштабирования без хаоса' },
      { type: 'list', items: ['Стандартизируй процессы раньше, чем начнёшь масштабироваться', 'Делегируй рутину — оставь себе главное', 'Рости на 20% в квартал, а не в 10× за месяц'] },
      { type: 'callout', text: '🚀 Масштаб — это не в 10 раз больше работы. Это в 10 раз лучше выстроенная система.' },
    ],
  },
];

function BlogCtaWidget({ onCta }) {
  return (
    <div className="blog-widget card">
      <div className="blog-widget-head">
        <span className="blog-widget-logo">
          <svg width="28" height="28" viewBox="0 0 36 36" fill="none">
            <defs><linearGradient id="wlg" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stopColor="#ffb347"/><stop offset=".55" stopColor="#ff7a18"/><stop offset="1" stopColor="#ff3d00"/></linearGradient></defs>
            <rect x="1" y="1" width="34" height="34" rx="10" fill="url(#wlg)"/>
            <path d="M11 23 18 9l7 14-7-4-7 4z" fill="#fff" fillOpacity=".95"/>
          </svg>
        </span>
        <div>
          <div className="blog-widget-name">OUTFORGE</div>
          <div className="blog-widget-sub">YouTube Lead Engine</div>
        </div>
      </div>
      <p className="blog-widget-desc">Находи YouTube-лиды на автопилоте и закрывай клиентов через холодный аутрич</p>
      <ol className="blog-widget-steps">
        <li><span>1</span> Парсинг YouTube по нишам и AI-запросам</li>
        <li><span>2</span> Верификация TG / Email контактов</li>
        <li><span>3</span> Авторассылка и CRM-инбокс</li>
      </ol>
      <button className="btn btn-primary btn-spark" style={{width:'100%', marginTop:4}} onClick={onCta}>
        Начать работу <A.Ico.arrow />
      </button>
      <div className="blog-widget-badges">
        <span>✓ Без карты</span>
        <span>✓ Готово за 3 мин</span>
      </div>
    </div>
  );
}

// ── Cover icons by category ──────────────────────────────────────
const COVER_ICONS = {
  // blog categories
  guide:    <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="1.8" strokeLinecap="round"><path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/></svg>,
  case:     <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="1.8" strokeLinecap="round"><path d="M3 3v18h18"/><path d="m7 16 4-5 4 4 5-7"/></svg>,
  strategy: <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="1.8" strokeLinecap="round"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="4"/><line x1="12" y1="2" x2="12" y2="4"/><line x1="12" y1="20" x2="12" y2="22"/><line x1="2" y1="12" x2="4" y2="12"/><line x1="20" y1="12" x2="22" y2="12"/></svg>,
  tools:    <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="1.8" strokeLinecap="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/></svg>,
  templates:<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="1.8" strokeLinecap="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="12" y2="17"/></svg>,
  mindset:  <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="1.8" strokeLinecap="round"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M15.09 14c.18-.98.65-1.74 1.41-2.5A4.65 4.65 0 0 0 18 8 6 6 0 0 0 6 8c0 1 .23 2.23 1.5 3.5A4.61 4.61 0 0 1 8.91 14"/></svg>,
  // features categories
  youtube:  <svg width="22" height="22" viewBox="0 0 24 24" fill="white" style={{opacity:.9}}><path d="M22.54 6.42a2.78 2.78 0 0 0-1.95-1.96C18.88 4 12 4 12 4s-6.88 0-8.59.46A2.78 2.78 0 0 0 1.46 6.42C1 8.13 1 12 1 12s0 3.87.46 5.58A2.78 2.78 0 0 0 3.41 19.54C5.12 20 12 20 12 20s6.88 0 8.59-.46a2.78 2.78 0 0 0 1.95-1.96C23 15.87 23 12 23 12s0-3.87-.46-5.58z"/><polygon points="9.75 15.02 15.5 12 9.75 8.98 9.75 15.02" fill="rgba(255,106,31,1)" /></svg>,
  verification:<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="1.8" strokeLinecap="round"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="m9 12 2 2 4-4" /></svg>,
  crm:      <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="1.8" strokeLinecap="round"><path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="m2 17 10 5 10-5"/><path d="m2 12 10 5 10-5"/></svg>,
  mailing:  <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="1.8" strokeLinecap="round"><path d="m22 2-7 20-4-9-9-4Z"/><path d="M22 2 11 13"/></svg>,
  warmup:   <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="1.8" strokeLinecap="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/></svg>,
  tg:       <svg width="22" height="22" viewBox="0 0 24 24" fill="white" style={{opacity:.9}}><path d="M20.665 3.717l-17.73 6.837c-1.21.486-1.203 1.161-.222 1.462l4.552 1.42 10.532-6.645c.498-.303.953-.14.579.192l-8.533 7.701h-.002l.002.001-.314 4.692c.46 0 .663-.211.921-.46l2.211-2.15 4.599 3.397c.848.467 1.457.227 1.668-.785l3.019-14.228c.309-1.239-.473-1.8-1.282-1.434z"/></svg>,
  tpl:      <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="1.8" strokeLinecap="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="12" y2="17"/></svg>,
  stats:    <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="1.8" strokeLinecap="round"><path d="M3 3v18h18"/><path d="M7 14l4-4 4 4 5-5"/></svg>,
};
function BlogCoverIcon({ category }) {
  const icon = COVER_ICONS[category] || COVER_ICONS.guide;
  return (
    <div className="blog-card-cover-icon">{icon}</div>
  );
}

function BlogListingView({ onSelect, activeCategory, setActiveCategory, onCta }) {
  const filtered = activeCategory === 'all'
    ? BLOG_ARTICLES
    : BLOG_ARTICLES.filter(a => a.category === activeCategory);

  return (
    <div className="blog-3col wrap">
      {/* Left: categories */}
      <aside className="blog-cats">
        <div className="blog-cats-label">Темы</div>
        {BLOG_CATEGORIES.map(cat => (
          <button key={cat.k}
            className={`blog-cat-item${activeCategory === cat.k ? ' is-active' : ''}`}
            onClick={() => setActiveCategory(cat.k)}>
            <A.Ico.layers style={{width:14,height:14,opacity:.5}} />
            {cat.l}
            {activeCategory === cat.k && <A.Ico.check style={{width:13,height:13,marginLeft:'auto',color:'var(--orange-400)'}} />}
          </button>
        ))}
      </aside>

      {/* Center: cards grid */}
      <div className="blog-center">
        <h1 className="blog-listing-title">Блог</h1>
        <div className="blog-cards-grid">
          {filtered.map(a => (
            <button key={a.id} className="blog-card card card-hover" onClick={() => onSelect(a.id)}>
              <div className="blog-card-cover" style={{background: a.cover}}>
                <svg className="blog-card-cover-deco" viewBox="0 0 400 140" preserveAspectRatio="xMidYMid slice" overflow="visible">
                  <circle cx="340" cy="-20" r="130" fill="white" opacity="0.09" />
                  <circle cx="20" cy="170" r="100" fill="white" opacity="0.07" />
                </svg>
                <BlogCoverIcon category={a.category} />
              </div>
              <div className="blog-card-body">
                <div className="blog-card-top">
                  <span className="blog-card-tag" style={{color: a.tagColor, background: a.tagColor+'18', border:'1px solid '+a.tagColor+'35'}}>{a.tag}</span>
                  <span className="blog-card-date">{a.date}</span>
                </div>
                <h3 className="blog-card-title">{a.title}</h3>
                <p className="blog-card-excerpt">{a.excerpt}</p>
                <div className="blog-card-footer">
                  <span className="blog-card-time">{a.readTime} чтения</span>
                  <span className="blog-card-read">Читать →</span>
                </div>
              </div>
            </button>
          ))}
        </div>
      </div>

      {/* Right: CTA widget */}
      <aside className="blog-right">
        <BlogCtaWidget onCta={onCta} />
      </aside>
    </div>
  );
}

function BlogArticleView({ article, onBack, onCta }) {
  const toc = article.content.filter(b => b.type === 'h2');
  return (
    <div className="blog-3col wrap">
      {/* Left: TOC */}
      <aside className="blog-toc-col">
        <button className="blog-toc-back" onClick={onBack}>
          <svg width="14" height="14" viewBox="0 0 16 16" fill="none">
            <path d="M10 3L5 8l5 5" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
          К статьям
        </button>
        {toc.length > 0 && (
          <>
            <div className="blog-toc-label">Содержание:</div>
            <ol className="blog-toc-list">
              {toc.map((b, i) => <li key={i}><span className="blog-toc-n">{i + 1}.</span>{b.text}</li>)}
            </ol>
          </>
        )}
      </aside>

      {/* Center: article */}
      <article className="blog-article-main" key={article.id}>
        <div className="blog-article-eyebrow">
          <span className="blog-article-tag" style={{color: article.tagColor, background: article.tagColor+'18', border:'1px solid '+article.tagColor+'35'}}>{article.tag}</span>
          <span className="blog-article-meta">{article.date} · {article.readTime} чтения</span>
        </div>
        <h1 className="blog-article-title">{article.title}</h1>
        <p className="blog-article-excerpt">{article.excerpt}</p>
        <hr className="divider" style={{margin:'32px 0'}} />
        <div className="blog-article-body">
          {article.content.map((block, i) => {
            if (block.type === 'lead') return <p key={i} className="blog-lead">{block.text}</p>;
            if (block.type === 'h2') return <h2 key={i} className="blog-h2">{block.text}</h2>;
            if (block.type === 'p') return <p key={i} className="blog-p">{block.text}</p>;
            if (block.type === 'callout') return <div key={i} className="blog-callout">{block.text}</div>;
            if (block.type === 'list') return (
              <ul key={i} className="blog-list">
                {block.items.map((item, j) => <li key={j}>{item}</li>)}
              </ul>
            );
            return null;
          })}
        </div>
      </article>

      {/* Right: CTA widget */}
      <aside className="blog-right">
        <BlogCtaWidget onCta={onCta} />
      </aside>
    </div>
  );
}

function BlogPage() {
  const { setBlogOpen, handleCta } = A.useApp();
  const [activeId, setActiveId] = React.useState(null);
  const [activeCategory, setActiveCategory] = React.useState('all');
  const close = React.useCallback(() => setBlogOpen(false), []);

  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') close(); };
    document.addEventListener('keydown', onKey);
    document.body.style.overflow = 'hidden';
    return () => { document.removeEventListener('keydown', onKey); document.body.style.overflow = ''; };
  }, []);

  const article = activeId ? BLOG_ARTICLES.find(a => a.id === activeId) : null;

  return ReactDOM.createPortal(
    <div className="tp-page tp-page-scroll">
      <div className="tp-bar">
        <button className="tp-back" onClick={close}>
          <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
            <path d="M10 3L5 8l5 5" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
          Назад
        </button>
        <div className="tp-bar-title">
          <span className="eyebrow" style={{padding:'4px 12px', fontSize:10}}>
            <span className="dot"></span>Блог
          </span>
          <span className="tp-bar-name">Outforge · Материалы</span>
        </div>
        <button className="tp-close" onClick={close} aria-label="Закрыть">
          <svg width="15" height="15" viewBox="0 0 15 15" fill="none">
            <path d="M2 2l11 11M13 2L2 13" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
          </svg>
        </button>
      </div>

      {article
        ? <BlogArticleView article={article} onBack={() => setActiveId(null)} onCta={handleCta} />
        : <BlogListingView onSelect={setActiveId} activeCategory={activeCategory} setActiveCategory={setActiveCategory} onCta={handleCta} />
      }
    </div>,
    document.body
  );
}

window.BlogPage = BlogPage;

// ============= FEATURES PAGE =============
const FEATURE_CATS = [
  { k: 'all',     l: 'Все функции' },
  { k: 'youtube', l: 'YouTube Leads' },
  { k: 'vrf',     l: 'Верификация' },
  { k: 'crm',     l: 'CRM' },
  { k: 'mailing', l: 'Рассылки' },
  { k: 'warmup',  l: 'Прогрев' },
  { k: 'tg',      l: 'TG Parser' },
  { k: 'tpl',     l: 'Шаблоны' },
  { k: 'stats',   l: 'Аналитика' },
];

const FEATURE_ARTICLES = [
  /* ── YouTube Leads ── */
  {
    id: 'ft-yt-1', category: 'youtube', tag: 'YouTube Leads', tagColor: '#FF6A1F',
    cover: 'linear-gradient(135deg, #1a0600 0%, #7c2d12 40%, #FF6A1F 100%)',
    coverLabel: '100+ ЛИДОВ / ДЕНЬ', date: '15 мая 2026', readTime: '7 мин',
    title: 'Как найти 100+ горячих лидов за один день',
    excerpt: 'Пошаговый разбор: как использовать YouTube-поиск, какие ниши работают и как не тратить время на нерелевантных блогеров.',
    content: [
      { type: 'lead', text: 'YouTube — это не просто видеохостинг. Это база данных из миллионов малых бизнесов и экспертов, которые сами рассказывают о своей нише. Для фрилансера это золотая жила.' },
      { type: 'h2', text: 'Кто твой идеальный лид' },
      { type: 'p', text: 'Горячий лид — канал с 3 000–100 000 подписчиков, который регулярно публикует контент и явно нуждается в твоих услугах. Маленький канал значит: человек серьёзен, но у него ещё нет ресурсов большого бренда — он открыт к аутсорсу.' },
      { type: 'h2', text: 'Как строить поисковый запрос' },
      { type: 'p', text: 'Ищи ключевые слова, которые описывают боль клиента: «как найти клиентов», «продвижение 2026», «продаю курсы». Так ты находишь тех, кто уже думает о своём росте.' },
      { type: 'list', items: ['5–10 разных запросов в рамках одной ниши', 'Включай географию: «бизнес 2026», «фриланс Россия»', 'Ищи каналы с указанными контактами в описании', 'Смотри на дату последнего видео — канал должен быть активен'] },
      { type: 'callout', text: '💡 Совет: начни с 3 разных запросов в одной нише. Сравни качество лидов и выбери самый конвертирующий.' },
    ],
  },
  {
    id: 'ft-yt-2', category: 'youtube', tag: 'YouTube Leads', tagColor: '#FF6A1F',
    cover: 'linear-gradient(135deg, #1a0600 0%, #9a3412 40%, #ea580c 100%)',
    coverLabel: 'ТОП НИШИ 2026', date: '12 мая 2026', readTime: '5 мин',
    title: 'Топ-ниши для аутрича через YouTube в 2026 году',
    excerpt: 'Где концентрируются самые отзывчивые и платёжеспособные клиенты на YouTube — с примерами запросов для каждой ниши.',
    content: [
      { type: 'lead', text: 'Не все ниши одинаково хороши для холодного аутрича. В некоторых — высокий Open Rate и быстрые ответы, в других — полная тишина.' },
      { type: 'h2', text: 'Самые горячие ниши' },
      { type: 'list', items: ['Коучи и консультанты — самая горячая аудитория. Открыты к инструментам, есть бюджет', 'Малый бизнес — кафе, салоны, строительство: нужны сайт, реклама, SMM', 'IT-предприниматели и SaaS — платят хорошо, решения принимают быстро', 'Образование — онлайн-школы и курсы: рынок растёт, клиенты лояльные', 'Строительство — владельцы с оборотом от 3 до 30 млн, конкуренция минимальная'] },
      { type: 'callout', text: '🎯 Выбери 1–2 ниши и тестируй 2 недели. Смотри на Reply Rate в аналитике. Лучшую масштабируй.' },
    ],
  },
  {
    id: 'ft-yt-3', category: 'youtube', tag: 'YouTube Leads', tagColor: '#FF6A1F',
    cover: 'linear-gradient(135deg, #150400 0%, #b45309 40%, #f59e0b 100%)',
    coverLabel: 'ФИЛЬТРЫ ПОИСКА', date: '10 мая 2026', readTime: '4 мин',
    title: 'Фильтры поиска: как отсеивать мусор и оставлять только горячих лидов',
    excerpt: 'Разбираем фильтры YouTube-парсера — подписчики, активность, гео — и объясняем как настроить их для максимального качества базы.',
    content: [
      { type: 'lead', text: 'Плохие фильтры — главная причина жалоб на «низкое качество лидов». На деле лиды нормальные — просто параметры поиска настроены неверно.' },
      { type: 'h2', text: 'Золотой диапазон подписчиков' },
      { type: 'p', text: '3 000–50 000 подписчиков. Меньше — часто любители без бюджета. Больше 50K — агентства уже обращаются напрямую, твоё письмо утонет.' },
      { type: 'h2', text: 'Активность и контакты' },
      { type: 'list', items: ['Последнее видео до 30 дней — максимально горячий лид', 'До 60 дней — ещё актуален', 'Больше 90 дней — пропускай', 'Нет email или Telegram в описании — скорее всего хобби-канал, пропускай'] },
      { type: 'callout', text: '💡 Создай 2 поисковых профиля: «горячие» (3K–30K, активны до 30 дней, есть контакты) и «тёплые» (30K–100K, до 60 дней). Тестируй оба.' },
    ],
  },
  /* ── Верификация ── */
  {
    id: 'ft-vrf-1', category: 'vrf', tag: 'Верификация', tagColor: '#38bdf8',
    cover: 'linear-gradient(135deg, #042f2e 0%, #164e63 40%, #0284c7 100%)',
    coverLabel: 'ЗАЩИТА РЕПУТАЦИИ', date: '14 мая 2026', readTime: '5 мин',
    title: 'Зачем верифицировать лиды — и что будет если пропустить этот шаг',
    excerpt: 'Один невалидный адрес не страшен. Тысяча — и твой домен попадает в блэклист. Объясняем, как работает верификация и почему это не опция.',
    content: [
      { type: 'lead', text: 'Из 500 найденных каналов около 30% не имеют рабочего контакта. Отправлять туда — значит получать Bounce Rate 30%, после чего все письма идут в спам.' },
      { type: 'h2', text: 'Что такое Bounce Rate и почему это важно' },
      { type: 'p', text: 'Bounce — недоставленное письмо. При Bounce Rate > 2% — предупреждение от провайдера. При 5% — ограничения. При 10% — полная блокировка домена.' },
      { type: 'h2', text: 'Статусы верификации в Outforge' },
      { type: 'list', items: ['Valid — можно отправлять, адрес существует', 'Risky — может быть ловушкой, отправляй осторожно', 'Invalid — адрес не существует, не отправляй', 'Unknown — не удалось проверить'] },
      { type: 'callout', text: '🛡️ В рассылку включай только Valid-адреса. Это кажется жёстким, но лучше 300 Valid, чем 500 с 30% отказов.' },
    ],
  },
  {
    id: 'ft-vrf-2', category: 'vrf', tag: 'Верификация', tagColor: '#38bdf8',
    cover: 'linear-gradient(135deg, #0c1445 0%, #1e3a8a 40%, #4361EE 100%)',
    coverLabel: 'ПРОКСИ × ЗАЩИТА', date: '11 мая 2026', readTime: '4 мин',
    title: 'Прокси и верификация: как проверять тысячи контактов без блокировок',
    excerpt: 'Верификация без прокси — гарантированный бан IP. Рассказываем как правильно настроить прокси-пул в Outforge для бесперебойной работы.',
    content: [
      { type: 'lead', text: 'Когда ты верифицируешь 1 000+ адресов с одного IP, сервера начинают блокировать запросы. Результат — ложные «Invalid» и неверная картина базы.' },
      { type: 'h2', text: 'Как работает прокси-верификация' },
      { type: 'p', text: 'Каждый запрос уходит с разного IP. Сервер видит «разных пользователей» и не блокирует проверку. Outforge ротирует прокси автоматически.' },
      { type: 'h2', text: 'Какие прокси использовать' },
      { type: 'list', items: ['Резидентные прокси — лучший выбор, выглядят как обычные пользователи', 'Датацентровые — дешевле, но чаще блокируются', 'Мобильные — самые надёжные, но дорогие', 'Бесплатные — никогда, ненадёжны и опасны'] },
      { type: 'callout', text: '⚡ Для проверки 10 000 контактов достаточно пула из 10–20 резидентных прокси.' },
    ],
  },
  /* ── CRM ── */
  {
    id: 'ft-crm-1', category: 'crm', tag: 'CRM', tagColor: '#a78bfa',
    cover: 'linear-gradient(135deg, #2e1065 0%, #4c1d95 40%, #7C3AED 100%)',
    coverLabel: 'CRM ВОРОНКА', date: '13 мая 2026', readTime: '6 мин',
    title: 'CRM-воронка для фрилансера: от холодного контакта до оплаченного счёта',
    excerpt: 'Как использовать CRM-воронку Outforge чтобы не терять клиентов на каждом этапе — и закрывать в 2 раза больше сделок без дополнительного аутрича.',
    content: [
      { type: 'lead', text: 'Большинство фрилансеров теряют клиентов не потому что плохо продают, а потому что не следят за контактами. CRM — система, которая не даёт этому случиться.' },
      { type: 'h2', text: 'Стадии воронки' },
      { type: 'list', items: ['Новый — лид добавлен, контакт ещё не установлен', 'Contacted — отправил сообщение, жду ответа', 'In Talk — ведём переговоры', 'Proposal — отправил КП', 'Won / Lost — закрыт или потерян'] },
      { type: 'h2', text: 'Главное правило работы с CRM' },
      { type: 'p', text: 'На стадии «Contacted» ставь напоминание на 3 дня. Нет ответа — отправляй follow-up. Написал 2 раза без ответа — переводи в «Cold» и забывай на 30 дней.' },
      { type: 'callout', text: '📋 Каждый понедельник: 15 минут на просмотр CRM. Кто завис дольше недели — пиши follow-up. Эта рутина сама по себе даёт +20% закрытых сделок.' },
    ],
  },
  {
    id: 'ft-crm-2', category: 'crm', tag: 'CRM', tagColor: '#a78bfa',
    cover: 'linear-gradient(135deg, #1e1b4b 0%, #3730a3 40%, #6366f1 100%)',
    coverLabel: 'СДЕЛКИ × ВХОДЯЩИЕ', date: '9 мая 2026', readTime: '4 мин',
    title: 'Сделки, контакты, входящие: как организовать CRM чтобы ничего не терять',
    excerpt: 'Разбираем разницу между контактом и сделкой, когда создавать сделку и как работать с входящими сообщениями в CRM Outforge.',
    content: [
      { type: 'lead', text: 'В CRM Outforge три ключевых объекта: Контакты, Сделки и Входящие. Большинство новичков путают их назначение.' },
      { type: 'h2', text: 'Контакт vs Сделка' },
      { type: 'p', text: 'Контакт — это человек. Сделка — конкретный проект или возможность. Один контакт может иметь несколько сделок. Создавай сделку, когда есть конкретный интерес: запрос на КП или «Интересно, расскажи подробнее».' },
      { type: 'h2', text: 'Как работают Входящие' },
      { type: 'p', text: 'Вкладка «Входящие» показывает все ответы на рассылки. Это приоритет #1 каждое утро. Скорость ответа в аутриче критична — первый час после ответа самый горячий.' },
      { type: 'callout', text: '💡 Используй теги: «горячий», «SEO», «разработка». Так можно быстро находить нужных контактов и персонализировать коммуникацию.' },
    ],
  },
  /* ── Рассылки ── */
  {
    id: 'ft-ml-1', category: 'mailing', tag: 'Рассылки', tagColor: '#34d399',
    cover: 'linear-gradient(135deg, #052e16 0%, #166534 40%, #16a34a 100%)',
    coverLabel: 'ПЕРВАЯ РАССЫЛКА', date: '16 мая 2026', readTime: '8 мин',
    title: 'Первая рассылка за 15 минут: от создания до запуска',
    excerpt: 'Пошаговый туториал: выбираем лидов, настраиваем кампанию, запускаем первую рассылку и следим за результатами. Всё по шагам.',
    content: [
      { type: 'lead', text: 'Настройка первой кампании пугает. На деле — это 15 минут работы.' },
      { type: 'h2', text: 'Шаг 1: Подготовь лидов' },
      { type: 'p', text: 'Парсинг → верификация → только Valid-контакты в кампанию. Минимум для первого теста: 50–100 лидов.' },
      { type: 'h2', text: 'Шаг 2: Создай кампанию' },
      { type: 'list', items: ['Название кампании (только для тебя)', 'Выбери лидов из базы', 'Выбери прогретый аккаунт для отправки', 'Выбери или создай шаблон письма', 'Расписание: 20–50 писем в день, 9:00–18:00 по рабочим дням', 'Нажми «Старт»'] },
      { type: 'h2', text: 'Шаг 3: Мониторинг' },
      { type: 'p', text: 'После запуска следи за Аналитикой рассылок. Если Bounce > 5% — останови и проверь базу.' },
      { type: 'callout', text: '🚀 Первая кампания — всегда тест. Не запускай сразу на всю базу. 50–100 писем, смотри на метрики, оптимизируй, масштабируй.' },
    ],
  },
  {
    id: 'ft-ml-2', category: 'mailing', tag: 'Рассылки', tagColor: '#34d399',
    cover: 'linear-gradient(135deg, #032620 0%, #0f766e 40%, #14b8a6 100%)',
    coverLabel: 'ФОРМУЛА ПИСЬМА', date: '14 мая 2026', readTime: '6 мин',
    title: 'Структура холодного письма: формула, которая работает',
    excerpt: 'Почему 90% холодных писем игнорируют — и как написать письмо, которое зацепит с первой строки. Разбираем структуру по формуле AIDA.',
    content: [
      { type: 'lead', text: 'Среднестатистический человек получает 100+ писем в день. На решение «читать или удалить» уходит 2–3 секунды. У тебя одна строка темы и одна строка превью.' },
      { type: 'h2', text: 'Тема письма' },
      { type: 'p', text: 'Избегай: «Сотрудничество», «Предложение», «Хочу помочь». Это шаблонный мусор. Используй конкретику: «Видел ваш последний ролик про Notion — есть идея».' },
      { type: 'h2', text: 'Структура AIDA' },
      { type: 'list', items: ['Attention: первое предложение — конкретная деталь о человеке', 'Interest: покажи, что понял его проблему', 'Desire: покажи результат, не услугу', 'Action: один конкретный следующий шаг'] },
      { type: 'callout', text: '📊 Пиши о клиенте, а не о себе. «Вы» должно встречаться в 3 раза чаще, чем «я».' },
    ],
  },
  {
    id: 'ft-ml-3', category: 'mailing', tag: 'Рассылки', tagColor: '#34d399',
    cover: 'linear-gradient(135deg, #042920 0%, #065f46 40%, #10b981 100%)',
    coverLabel: 'ЧАСТОТА × ВРЕМЯ', date: '11 мая 2026', readTime: '4 мин',
    title: 'Когда и сколько: оптимальная частота рассылок без попадания в спам',
    excerpt: 'Сколько писем в день безопасно, в какое время лучшая доставляемость, и как выстроить follow-up стратегию без навязчивости.',
    content: [
      { type: 'lead', text: 'Чем больше писем — тем больше клиентов? Нет. Превысил лимит — аккаунт заблокирован, все письма в спаме.' },
      { type: 'h2', text: 'Безопасные лимиты' },
      { type: 'list', items: ['Новый аккаунт (до 2 недель): 10–20 писем/день', 'После 2–4 недель прогрева: 30–50 писем/день', 'Прогретый месяц+: 50–100 писем/день'] },
      { type: 'h2', text: 'Лучшее время' },
      { type: 'p', text: 'Вторник, среда, четверг. Время: 9:00–11:00 и 14:00–16:00 по часовому поясу получателя. В Outforge настрой расписание в параметрах кампании.' },
      { type: 'callout', text: '💡 Follow-up: 80% ответов — на второе или третье письмо. Стандартная цепочка: письмо 1 → 3 дня → FU1 → 4 дня → FU2 → 7 дней → последнее.' },
    ],
  },
  /* ── Прогрев ── */
  {
    id: 'ft-wu-1', category: 'warmup', tag: 'Прогрев', tagColor: '#fbbf24',
    cover: 'linear-gradient(135deg, #1c0700 0%, #92400e 40%, #d97706 100%)',
    coverLabel: 'ПРОГРЕВ АККАУНТА', date: '13 мая 2026', readTime: '6 мин',
    title: 'Прогрев аккаунта: от нуля до 100 писем в день без попадания в спам',
    excerpt: 'Полный гайд по прогреву email-аккаунта для холодного аутрича. График по неделям, настройки в Outforge, признаки успешного прогрева.',
    content: [
      { type: 'lead', text: 'Прогрев — постепенное увеличение объёма исходящей почты с нового аккаунта. Без прогрева твои письма сразу идут в спам.' },
      { type: 'h2', text: 'График прогрева' },
      { type: 'list', items: ['Неделя 1: 5–10 писем/день. Только личные контакты', 'Неделя 2: 15–25 писем/день. Добавляй небольшой объём холодных', 'Неделя 3: 30–50 писем/день. Запускай тестовые кампании', 'Неделя 4+: 50–100 писем/день. Аккаунт прогрет'] },
      { type: 'h2', text: 'Автопрогрев в Outforge' },
      { type: 'p', text: 'Добавь аккаунт и включи автопрогрев. Система автоматически обменивается письмами с другими аккаунтами сети, получает ответы и наращивает объём. Тебе не нужно ничего делать вручную.' },
      { type: 'callout', text: '⚡ Не останавливай прогрев во время активных кампаний. 5–10 прогревочных писем в день — страховка от деградации репутации.' },
    ],
  },
  {
    id: 'ft-wu-2', category: 'warmup', tag: 'Прогрев', tagColor: '#fbbf24',
    cover: 'linear-gradient(135deg, #1a0600 0%, #78350f 40%, #f59e0b 100%)',
    coverLabel: 'ЧЕКЛИСТ ГОТОВНОСТИ', date: '9 мая 2026', readTime: '3 мин',
    title: 'Чеклист: когда аккаунт прогрет и можно запускать рассылку',
    excerpt: 'Когда можно переходить к полноценным рассылкам? Конкретный чеклист из 7 признаков — прогрет или нужно ещё подождать.',
    content: [
      { type: 'lead', text: 'Многие фрилансеры либо начинают рассылки слишком рано, либо прогревают слишком долго. Вот конкретные признаки готовности.' },
      { type: 'h2', text: 'Чеклист готовности' },
      { type: 'list', items: ['✓ Прогрев идёт минимум 14 дней', '✓ Open Rate прогревочных писем > 70%', '✓ Ни одно письмо не улетело в спам за последние 5 дней', '✓ Текущий объём > 30 писем без проблем', '✓ SPF, DKIM, DMARC настроены для домена', '✓ Аккаунт создан минимум 2–3 недели назад'] },
      { type: 'callout', text: '🚨 Стоп: не запускай если Bounce > 2% за 48 часов, прогрев < 10 дней или DKIM не настроен.' },
    ],
  },
  /* ── TG Parser ── */
  {
    id: 'ft-tg-1', category: 'tg', tag: 'TG Parser', tagColor: '#60a5fa',
    cover: 'linear-gradient(135deg, #0c1445 0%, #1e3a8a 40%, #4361EE 100%)',
    coverLabel: 'TG ПАРСЕР', date: '12 мая 2026', readTime: '5 мин',
    title: 'TG Parser: находим клиентов в Telegram без ручного поиска',
    excerpt: 'Как парсить участников Telegram-каналов, превращать их в лидов и правильно настраивать TG-кампании в Outforge.',
    content: [
      { type: 'lead', text: 'Telegram — второй по эффективности канал для холодного аутрича. Люди более открыты в мессенджере, скорость ответа выше, барьер входа ниже.' },
      { type: 'h2', text: 'Как работает TG Parser' },
      { type: 'p', text: 'Указываешь ссылку на канал или чат. Парсер собирает всех участников: имя, username, активность. Из активных участников формируется список лидов. Дальше — DM через свои TG-аккаунты.' },
      { type: 'h2', text: 'Какие каналы парсить' },
      { type: 'list', items: ['Тематические чаты по вашей нише (SEO, маркетологи, IT)', 'Чаты с заказами (fl.ru, freelancehunt Telegram-группы)', 'Каналы конкурентов — их аудитория = потенциальные клиенты', 'Бизнес-каналы с активной аудиторией'] },
      { type: 'callout', text: '💡 Парси только чаты, где ты сам участник. Активные участники чата — самые ценные лиды.' },
    ],
  },
  {
    id: 'ft-tg-2', category: 'tg', tag: 'TG Parser', tagColor: '#60a5fa',
    cover: 'linear-gradient(135deg, #0a1628 0%, #1e3a8a 40%, #3b82f6 100%)',
    coverLabel: '7 ТИПОВ КАНАЛОВ', date: '8 мая 2026', readTime: '4 мин',
    title: '7 типов Telegram-каналов, где концентрируются потенциальные заказчики',
    excerpt: 'Не все Telegram-каналы одинаково ценны для поиска клиентов. Вот конкретные типы и примеры — где найти платёжеспособную аудиторию.',
    content: [
      { type: 'lead', text: 'Telegram — огромная экосистема. Чтобы не тратить время на нерелевантных участников, нужно знать где искать.' },
      { type: 'list', items: ['1. Отраслевые чаты — маркетологи, SaaS-основатели, e-commerce', '2. Чаты с заказами на фриланс — конкретная потребность, высокая конверсия', '3. Каналы инфопредпринимателей — платёжеспособны, понимают ценность услуг', '4. Бизнес-клубы и нетворкинг — максимальная концентрация владельцев бизнеса', '5. HR и рекрутинг — для B2B контрактов', '6. Инвестиции и стартапы — для tech-компаний', '7. Каналы конкурентов — аудитория уже интересуется вашей нишей'] },
      { type: 'callout', text: '🎯 Стань активным участником чата до парсинга. Ты уже не совсем незнакомец для аудитории — конверсия растёт.' },
    ],
  },
  /* ── Шаблоны ── */
  {
    id: 'ft-tpl-1', category: 'tpl', tag: 'Шаблоны', tagColor: '#34d399',
    cover: 'linear-gradient(135deg, #022c22 0%, #065f46 40%, #10b981 100%)',
    coverLabel: '5 ШАБЛОНОВ 15%+', date: '15 мая 2026', readTime: '7 мин',
    title: '5 шаблонов холодного письма с конверсией 15%+',
    excerpt: 'Готовые шаблоны для разных ниш и каналов: коучи, e-commerce, IT, Telegram DM. Берите, адаптируйте, тестируйте.',
    content: [
      { type: 'lead', text: 'Хороший шаблон — это не текст, который ты скопировал и забыл. Это структура, которую ты адаптируешь под каждую нишу.' },
      { type: 'h2', text: 'Шаблон для коучей' },
      { type: 'p', text: 'Тема: «[Имя], заметил кое-что на вашем канале». Тело: «Посмотрел ваш ролик про [тема] — сильный кейс. Я помогаю [ниша]-экспертам находить 20–50 новых лидов в месяц без доп. контента. Последний клиент закрыл 8 учеников за 3 недели. Есть 15 минут на звонок?»' },
      { type: 'h2', text: 'Шаблон Telegram DM' },
      { type: 'p', text: 'Коротко: «Привет [Имя]! Видел тебя в [название чата]. Работаю с [ниша], помогаю [конкретный результат]. Интересно было бы поговорить? Без давления, просто покажу что делаем.»' },
      { type: 'callout', text: '💡 Добавь 1–2 персонализированных детали о получателе. Это увеличивает Reply Rate в 2–3 раза. В Outforge используй переменные: {{имя}}, {{канал}}, {{ниша}}.' },
    ],
  },
  {
    id: 'ft-tpl-2', category: 'tpl', tag: 'Шаблоны', tagColor: '#34d399',
    cover: 'linear-gradient(135deg, #022c22 0%, #047857 40%, #059669 100%)',
    coverLabel: 'ПЕРСОНАЛИЗАЦИЯ', date: '12 мая 2026', readTime: '4 мин',
    title: 'Персонализация: 30 секунд, которые утраивают ответы',
    excerpt: 'Один и тот же шаблон с персонализацией и без — разница в Reply Rate составляет 200–300%. Показываем что конкретно добавлять.',
    content: [
      { type: 'lead', text: 'Исследования HubSpot: персонализированные письма получают на 202% больше ответов. Но персонализация — это не «Привет, [Имя]!».' },
      { type: 'h2', text: 'Три уровня персонализации' },
      { type: 'list', items: ['Базовый (30 сек): имя, название канала, ниша — переменные в Outforge', 'Средний (2 мин): упоминание конкретного видео или поста', 'Глубокий (5 мин): связь их контента с их проблемой. Высший Reply Rate'] },
      { type: 'h2', text: 'Плохо vs хорошо' },
      { type: 'p', text: '❌ «Добрый день! Предлагаю услуги по digital-маркетингу для вашего бизнеса…» → ✓ «Привет, Антон! Посмотрел ваш последний ролик про масштабирование через партнёрства — сильный кейс с 40% ростом. Вопрос: вы уже автоматизировали привлечение партнёров?…»' },
      { type: 'callout', text: '⚡ Смотри канал 30 секунд и пиши что зацепило. Этого достаточно для отличной персонализации.' },
    ],
  },
  /* ── Аналитика ── */
  {
    id: 'ft-stats-1', category: 'stats', tag: 'Аналитика', tagColor: '#c084fc',
    cover: 'linear-gradient(135deg, #1a0533 0%, #7c3aed 40%, #a855f7 100%)',
    coverLabel: 'МЕТРИКИ АУТРИЧА', date: '14 мая 2026', readTime: '5 мин',
    title: 'Метрики аутрича: на что смотреть и что улучшать в первую очередь',
    excerpt: 'Open Rate, Reply Rate, Bounce Rate — что означают эти цифры, какие показатели нормальные и что делать когда они не дотягивают.',
    content: [
      { type: 'lead', text: 'Аналитика без понимания — просто цифры. Давай разберём что реально важно для фрилансера-аутричера.' },
      { type: 'h2', text: 'Ключевые метрики' },
      { type: 'list', items: ['Open Rate: норма 30–50%. Ниже — проблема с темой или попадание в спам', 'Reply Rate: норма 3–8%. Лучшие кампании дают 10–15%. Это метрика #1 для оптимизации', 'Bounce Rate: норма до 2%. При 5% — останови кампанию', 'Unsubscribe Rate: норма до 1%. Если выше — смягчи тон письма'] },
      { type: 'h2', text: 'Формула расчёта дохода' },
      { type: 'p', text: 'Reply Rate 5% × 1000 лидов = 50 диалогов. Конверсия диалог → клиент 10% = 5 новых клиентов за кампанию. Средний чек 30 000 ₽ = 150 000 ₽.' },
      { type: 'callout', text: '📊 Создай таблицу-трекер: каждую неделю записывай метрики. Через месяц увидишь тренды и поймёшь что улучшать — 10 минут в неделю.' },
    ],
  },
];

function FeaturesListingView({ onSelect, activeCategory, setActiveCategory, onCta }) {
  const filtered = activeCategory === 'all'
    ? FEATURE_ARTICLES
    : FEATURE_ARTICLES.filter(a => a.category === activeCategory);

  return (
    <div className="blog-3col wrap">
      <aside className="blog-cats">
        <div className="blog-cats-label">Функции</div>
        {FEATURE_CATS.map(cat => (
          <button key={cat.k}
            className={'blog-cat-item' + (activeCategory === cat.k ? ' is-active' : '')}
            onClick={() => setActiveCategory(cat.k)}>
            <A.Ico.layers style={{width:14,height:14,opacity:.5}} />
            {cat.l}
            {activeCategory === cat.k && <A.Ico.check style={{width:13,height:13,marginLeft:'auto',color:'var(--orange-400)'}} />}
          </button>
        ))}
      </aside>

      <div className="blog-center">
        <div style={{marginBottom:24}}>
          <h1 className="blog-listing-title">Обзор функций</h1>
          <p style={{margin:'6px 0 0',fontSize:14,color:'var(--text-2)',lineHeight:1.6}}>
            {FEATURE_ARTICLES.length} статей по всем функциям Outforge — для новичков и опытных аутричеров
          </p>
        </div>
        <div className="blog-cards-grid">
          {filtered.map(a => (
            <button key={a.id} className="blog-card card card-hover" onClick={() => onSelect(a.id)}>
              <div className="blog-card-cover" style={{background: a.cover}}>
                <svg className="blog-card-cover-deco" viewBox="0 0 400 140" preserveAspectRatio="xMidYMid slice" overflow="visible">
                  <circle cx="340" cy="-20" r="130" fill="white" opacity="0.09" />
                  <circle cx="20" cy="170" r="100" fill="white" opacity="0.07" />
                </svg>
                <BlogCoverIcon category={a.category} />
              </div>
              <div className="blog-card-body">
                <div className="blog-card-top">
                  <span className="blog-card-tag" style={{color: a.tagColor, background: a.tagColor+'18', border:'1px solid '+a.tagColor+'35'}}>{a.tag}</span>
                  <span className="blog-card-date">{a.date}</span>
                </div>
                <h3 className="blog-card-title">{a.title}</h3>
                <p className="blog-card-excerpt">{a.excerpt}</p>
                <div className="blog-card-footer">
                  <span className="blog-card-time">{a.readTime} чтения</span>
                  <span className="blog-card-read">Читать →</span>
                </div>
              </div>
            </button>
          ))}
        </div>
      </div>

      <aside className="blog-right">
        <BlogCtaWidget onCta={onCta} />
      </aside>
    </div>
  );
}

function FeaturesArticleView({ article, onBack, onCta }) {
  const toc = article.content.filter(b => b.type === 'h2');
  return (
    <div className="blog-3col wrap">
      <aside className="blog-toc-col">
        <button className="blog-toc-back" onClick={onBack}>
          <svg width="14" height="14" viewBox="0 0 16 16" fill="none">
            <path d="M10 3L5 8l5 5" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
          К статьям
        </button>
        {toc.length > 0 && (
          <>
            <div className="blog-toc-label">Содержание:</div>
            <ol className="blog-toc-list">
              {toc.map((b, i) => <li key={i}><span className="blog-toc-n">{i + 1}.</span>{b.text}</li>)}
            </ol>
          </>
        )}
      </aside>

      <article className="blog-article-main" key={article.id}>
        <div className="blog-article-eyebrow">
          <span className="blog-article-tag" style={{color: article.tagColor, background: article.tagColor+'18', border:'1px solid '+article.tagColor+'35'}}>{article.tag}</span>
          <span className="blog-article-meta">{article.date} · {article.readTime} чтения</span>
        </div>
        <h1 className="blog-article-title">{article.title}</h1>
        <p className="blog-article-excerpt">{article.excerpt}</p>
        <hr className="divider" style={{margin:'32px 0'}} />
        <div className="blog-article-body">
          {article.content.map((block, i) => {
            if (block.type === 'lead') return <p key={i} className="blog-lead">{block.text}</p>;
            if (block.type === 'h2') return <h2 key={i} className="blog-h2">{block.text}</h2>;
            if (block.type === 'p') return <p key={i} className="blog-p">{block.text}</p>;
            if (block.type === 'callout') return <div key={i} className="blog-callout">{block.text}</div>;
            if (block.type === 'list') return (
              <ul key={i} className="blog-list">
                {block.items.map((item, j) => <li key={j}>{item}</li>)}
              </ul>
            );
            return null;
          })}
        </div>
      </article>

      <aside className="blog-right">
        <BlogCtaWidget onCta={onCta} />
      </aside>
    </div>
  );
}

function FeaturesPage() {
  const { setFeaturesOpen, handleCta } = A.useApp();
  const [activeId, setActiveId] = React.useState(null);
  const [activeCategory, setActiveCategory] = React.useState('all');
  const close = React.useCallback(() => setFeaturesOpen(false), []);

  React.useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') close(); };
    document.addEventListener('keydown', onKey);
    document.body.style.overflow = 'hidden';
    return () => { document.removeEventListener('keydown', onKey); document.body.style.overflow = ''; };
  }, []);

  const article = activeId ? FEATURE_ARTICLES.find(a => a.id === activeId) : null;

  return ReactDOM.createPortal(
    <div className="tp-page tp-page-scroll">
      <div className="tp-bar">
        <button className="tp-back" onClick={close}>
          <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
            <path d="M10 3L5 8l5 5" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
          Назад
        </button>
        <div className="tp-bar-title">
          <span className="eyebrow" style={{padding:'4px 12px', fontSize:10}}>
            <span className="dot"></span>Функции
          </span>
          <span className="tp-bar-name">Outforge · Обзор функций</span>
        </div>
        <button className="tp-close" onClick={close} aria-label="Закрыть">
          <svg width="15" height="15" viewBox="0 0 15 15" fill="none">
            <path d="M2 2l11 11M13 2L2 13" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/>
          </svg>
        </button>
      </div>

      {article
        ? <FeaturesArticleView article={article} onBack={() => setActiveId(null)} onCta={handleCta} />
        : <FeaturesListingView onSelect={setActiveId} activeCategory={activeCategory} setActiveCategory={setActiveCategory} onCta={handleCta} />
      }
    </div>,
    document.body
  );
}

window.FeaturesPage = FeaturesPage;

// ============= WHY US =============
function WhyUs() {
  const { t } = A.useApp();
  const points = t('whyus.points');
  const icons = [A.Ico.net, A.Ico.shield, A.Ico.ai, A.Ico.inbox, A.Ico.bolt, A.Ico.spark];
  const ref = A.useReveal();
  return (
    <section id="why" className="whyus" data-screen-label="why">
      <div className="wrap">
        <div className="section-head reveal" ref={ref}>
          <span className="eyebrow"><span className="dot"></span>{t('whyus.eyebrow')}</span>
          <h2>{t('whyus.title1')} <span className="grad-text">{t('whyus.title2')}</span></h2>
        </div>
        <div className="why-grid">
          {points.map((p, i) => {
            const I = icons[i] || A.Ico.bolt;
            return (
              <div key={i} className={`why-card card card-hover reveal reveal-delay-${(i%3)+1}`}>
                <span className="why-icon"><I /></span>
                <h3 className="why-t">{p.t}</h3>
                <p className="why-d">{p.d}</p>
              </div>
            );
          })}
        </div>
      </div>
    </section>
  );
}

window.WhyUs = WhyUs;

// ============= STORIES =============
function Stories() {
  const { t } = A.useApp();
  const items = t('stories.items');
  const ref = A.useReveal();
  return (
    <section id="stories" className="stories" data-screen-label="stories">
      <div className="wrap">
        <div className="section-head reveal" ref={ref}>
          <span className="eyebrow"><span className="dot"></span>{t('stories.eyebrow')}</span>
          <h2>{t('stories.title1')} <span className="grad-text">{t('stories.title2')}</span></h2>
        </div>
        <div className="story-grid">
          {items.map((s, i) => (
            <div key={i} className={`story card reveal reveal-delay-${(i%3)+1}`}>
              <div className="story-h">
                <span className="story-avatar">{s.name.charAt(0)}</span>
                <div>
                  <div className="story-name">{s.name}</div>
                  <div className="story-days mono muted">{s.days}</div>
                </div>
              </div>
              <div className="story-graph">
                <div className="story-col">
                  <div className="story-label muted mono">ДО</div>
                  <div className="story-bar">
                    <div className="story-bar-fill before"></div>
                  </div>
                  <div className="story-val">{s.before}</div>
                </div>
                <div className="story-arrow"><A.Ico.arrow /></div>
                <div className="story-col">
                  <div className="story-label muted mono">ПОСЛЕ</div>
                  <div className="story-bar">
                    <div className="story-bar-fill after"></div>
                  </div>
                  <div className="story-val grad-text">{s.after}</div>
                </div>
              </div>
              <p className="story-text">«{s.t}»</p>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

window.Stories = Stories;

// ============= FAQ =============
function Faq() {
  const { t } = A.useApp();
  const items = t('faq.items');
  const [open, setOpen] = React.useState(0);
  const ref = A.useReveal();
  return (
    <section id="faq" className="faq" data-screen-label="faq">
      <div className="wrap">
        <div className="section-head reveal" ref={ref}>
          <span className="eyebrow"><span className="dot"></span>{t('faq.eyebrow')}</span>
          <h2>{t('faq.title1')}</h2>
        </div>
        <div className="faq-list">
          {items.map((it, i) => (
            <div key={i} className={`faq-item${open === i ? ' is-open' : ''}`}>
              <button className="faq-q" onClick={() => setOpen(open === i ? -1 : i)}>
                <span>{it.q}</span>
                <span className="faq-plus" aria-hidden="true"><span></span><span></span></span>
              </button>
              <div className="faq-a-wrap"><div className="faq-a">{it.a}</div></div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

window.Faq = Faq;

// ============= FINAL CTA =============
const FINAL_CTA_CARDS = [
  {
    icon: (
      <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
        <path d="M11 2L13.5 8H20L14.5 12L16.5 18.5L11 14.5L5.5 18.5L7.5 12L2 8H8.5L11 2Z" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round"/>
      </svg>
    ),
    t: "Быстрый старт",
    d: "Настройте и запустите первую кампанию за 15 минут — без технических знаний.",
  },
  {
    icon: (
      <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
        <circle cx="11" cy="11" r="9" stroke="currentColor" strokeWidth="1.6"/>
        <path d="M7 11L10 14L15 8" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
      </svg>
    ),
    t: "Верификация",
    d: "Прокси-пулы и TG-чекер отсекают мёртвые контакты — только живые лиды.",
  },
  {
    icon: (
      <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
        <path d="M3 17L8 12L12 15L16 9L19 12" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
        <rect x="3" y="3" width="16" height="16" rx="3" stroke="currentColor" strokeWidth="1.6"/>
      </svg>
    ),
    t: "Поддержка 24/7",
    d: "Помогаем с настройкой, ответим в Telegram — обычно в течение часа.",
  },
  {
    icon: (
      <svg width="22" height="22" viewBox="0 0 22 22" fill="none">
        <path d="M4 16L9 9L13 13L17 6" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
        <path d="M17 6H13M17 6V10" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/>
      </svg>
    ),
    t: "Реальные результаты",
    d: "Сотни команд уже закрывают сделки через YouTube-аутрич. ROI от 8× в первый месяц.",
  },
];

function FinalCta() {
  const { handleCta } = A.useApp();
  const ref = A.useReveal();
  const ctaRef = A.usePointerGlow();
  return (
    <section className="fcta-section" data-screen-label="final-cta">
      <div className="fcta-glow" aria-hidden="true"></div>
      <div className="wrap">
        <div className="fcta-inner reveal" ref={ref}>
          <div className="fcta-head">
            <span className="eyebrow fcta-eyebrow"><span className="dot"></span>Начните сегодня</span>
            <h2 className="fcta-title">Готов запустить лидов<br/>на <span className="grad-text">автопилоте?</span></h2>
            <p className="fcta-sub">Присоединяйтесь к командам, которые уже автоматизировали аутрич через YouTube, Telegram и Email с Outforge.</p>
          </div>

          <div className="fcta-cards">
            {FINAL_CTA_CARDS.map((c, i) => (
              <div key={i} className="fcta-card">
                <div className="fcta-card-icon">{c.icon}</div>
                <div className="fcta-card-t">{c.t}</div>
                <p className="fcta-card-d">{c.d}</p>
              </div>
            ))}
          </div>

          <div className="fcta-actions">
            <button ref={ctaRef} className="btn btn-primary btn-spark btn-lg" onClick={handleCta}>
              Начать прямо сейчас <A.Ico.arrow />
            </button>
            <button className="btn btn-ghost btn-lg" onClick={handleCta}>
              <svg width="16" height="16" viewBox="0 0 16 16" fill="none" style={{flexShrink:0}}>
                <circle cx="8" cy="8" r="7" stroke="currentColor" strokeWidth="1.4"/>
                <path d="M6.5 5.5L11 8L6.5 10.5V5.5Z" fill="currentColor"/>
              </svg>
              Смотреть демо интерфейса
            </button>
          </div>

          <div className="fcta-checks">
            {["Все функции доступны", "Гибкие тарифы", "Отменить в любое время"].map((l, i) => (
              <span key={i} className="fcta-check">
                <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
                  <circle cx="7" cy="7" r="6.5" stroke="currentColor"/>
                  <path d="M4.5 7L6.5 9L9.5 5" stroke="currentColor" strokeWidth="1.3" strokeLinecap="round" strokeLinejoin="round"/>
                </svg>
                {l}
              </span>
            ))}
          </div>
        </div>
      </div>
    </section>
  );
}

window.FinalCta = FinalCta;

// ============= FOOTER =============
function Footer() {
  const { setBlogOpen, setReviewsOpen, setReferralOpen } = A.useApp();
  return (
    <footer className="footer" data-screen-label="footer">
      <div className="wrap">
        <hr className="divider" style={{marginBottom: 28}} />
        <div className="footer-slim">
          <div className="footer-slim-brand">
            <span className="brand-mark brand-mark-sm">
              <svg width="24" height="24" viewBox="0 0 36 36">
                <defs><linearGradient id="lg-fs" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stopColor="#ffb347"/><stop offset=".55" stopColor="#ff7a18"/><stop offset="1" stopColor="#ff3d00"/></linearGradient></defs>
                <rect x="1" y="1" width="34" height="34" rx="10" fill="url(#lg-fs)"/>
                <path d="M11 23 18 9l7 14-7-4-7 4z" fill="#fff" fillOpacity=".95"/>
              </svg>
            </span>
            <span className="brand-name footer-brand-name">Outforge</span>
          </div>

          <div className="footer-links">
            <div className="footer-links-group">
              <span className="footer-links-label">Правовое</span>
              <a href="/privacy" className="footer-link">Политика конфиденциальности</a>
              <a href="/offer" className="footer-link">Публичная оферта</a>
            </div>
            <div className="footer-links-group">
              <span className="footer-links-label">Контакты</span>
              <a href="mailto:hello@outforge.ru" className="footer-link footer-link-icon">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none">
                  <rect x="2" y="4" width="20" height="16" rx="3" stroke="currentColor" strokeWidth="1.6"/>
                  <path d="M2 7l10 7 10-7" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
                </svg>
                hello@outforge.ru
              </a>
              <a href="https://t.me/outforge_support" target="_blank" className="footer-link footer-link-icon">
                <svg width="14" height="14" viewBox="0 0 24 24" fill="none">
                  <path d="M21.2 4.2L2.8 11.1c-1.1.4-1.1 1.1-.2 1.4l4.7 1.5 1.8 5.5c.2.6.3.8.8.8.4 0 .6-.2.9-.5l2.2-2.1 4.6 3.4c.8.5 1.4.2 1.6-.8l2.9-13.6c.3-1.2-.4-1.8-1.9-1.5z" stroke="currentColor" strokeWidth="1.6" strokeLinejoin="round"/>
                </svg>
                Telegram
              </a>
            </div>
            <div className="footer-links-group">
              <span className="footer-links-label">Сервис</span>
              <button className="footer-link footer-link-btn" onClick={() => setReferralOpen(true)}>Реферальная программа</button>
              <button className="footer-link footer-link-btn" onClick={() => setBlogOpen(true)}>Блог</button>
              <button className="footer-link footer-link-btn" onClick={() => setReviewsOpen(true)}>Отзывы</button>
            </div>
          </div>
        </div>

        <div className="footer-bottom">
          <span className="footer-copy-text">© 2025 Outforge — сервис поиска лидов на YouTube</span>
        </div>
      </div>
    </footer>
  );
}

window.Footer = Footer;

// ============= REFERRAL PAGE =============
function ReferralPage() {
  const { setReferralOpen } = A.useApp();
  const tiers = [
    { label: 'Старт', color: '#8b9cb0', pct: '20%', desc: 'За каждого привлечённого пользователя на тарифе Старт' },
    { label: 'Pro', color: '#ff7a18', pct: '25%', desc: 'За каждого привлечённого пользователя на тарифе Pro' },
    { label: 'Studio', color: '#ff4e00', pct: '30%', desc: 'За каждого привлечённого пользователя на тарифе Studio' },
  ];
  return (
    <div className="tp-page" onClick={e => e.target === e.currentTarget && setReferralOpen(false)}>
      <div className="tp-panel" style={{maxWidth: 680}}>
        <div className="tp-head">
          <div>
            <div className="tp-title">Реферальная программа</div>
            <div className="tp-sub">Зарабатывайте вместе с Outforge</div>
          </div>
          <button className="tp-close" onClick={() => setReferralOpen(false)}>
            <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
              <path d="M5 5l10 10M15 5L5 15" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/>
            </svg>
          </button>
        </div>

        <div className="tp-body">
          <div className="ref-hero">
            <svg width="56" height="56" viewBox="0 0 56 56" fill="none">
              <rect width="56" height="56" rx="16" fill="rgba(255,122,24,.12)"/>
              <path d="M18 28c0-2.2 1.8-4 4-4s4 1.8 4 4-1.8 4-4 4-4-1.8-4-4z" fill="#ff7a18"/>
              <path d="M30 20c0-2.2 1.8-4 4-4s4 1.8 4 4-1.8 4-4 4-4-1.8-4-4z" fill="#ff7a18" opacity=".7"/>
              <path d="M30 36c0-2.2 1.8-4 4-4s4 1.8 4 4-1.8 4-4 4-4-1.8-4-4z" fill="#ff7a18" opacity=".7"/>
              <path d="M26 26l8-6M26 30l8 6" stroke="#ff7a18" strokeWidth="1.6" strokeLinecap="round"/>
            </svg>
            <div>
              <div className="ref-hero-t">Приводите клиентов — получайте комиссию</div>
              <div className="ref-hero-d">Делитесь реферальной ссылкой с коллегами, агентствами и фрилансерами. Получайте до 30% от каждой оплаты — ежемесячно, пока клиент активен.</div>
            </div>
          </div>

          <div className="ref-tiers">
            {tiers.map((tier, i) => (
              <div key={i} className="ref-tier">
                <div className="ref-tier-badge" style={{background: tier.color + '22', color: tier.color}}>{tier.label}</div>
                <div className="ref-tier-pct" style={{color: tier.color}}>{tier.pct}</div>
                <div className="ref-tier-desc">{tier.desc}</div>
              </div>
            ))}
          </div>

          <div className="ref-how">
            <div className="ref-how-t">Как это работает</div>
            {[
              ['1', 'Зарегистрируйтесь и получите реферальную ссылку в личном кабинете'],
              ['2', 'Поделитесь ссылкой — в соцсетях, Telegram, на YouTube или лично'],
              ['3', 'Когда человек оплачивает подписку, вы получаете комиссию на баланс'],
              ['4', 'Выводите средства на карту от 1 000 ₽'],
            ].map(([n, text]) => (
              <div key={n} className="ref-step">
                <div className="ref-step-num">{n}</div>
                <div className="ref-step-text">{text}</div>
              </div>
            ))}
          </div>

          <div className="ref-cta">
            <button className="btn btn-primary" onClick={() => { window.location.href = '/login'; }}>
              Получить реферальную ссылку
            </button>
            <div className="ref-cta-note">Программа доступна для всех пользователей Outforge</div>
          </div>
        </div>
      </div>
    </div>
  );
}

window.ReferralPage = ReferralPage;
