// pages.jsx — production navigation, footer, page templates and route content.
// Mobile-first with desktop expansions. Uses tokens (T, TX) and motion (Reveal,
// Diamond, Kicker, ArchMark, Counter) from shared.jsx.

// ─────────────────────────────────────────────────────────────
// Viewport hook (mobile-first breakpoint at 960)
// ─────────────────────────────────────────────────────────────
function useIsDesktop(breakpoint = 960) {
  const [d, setD] = React.useState(() => typeof window !== 'undefined' && window.innerWidth >= breakpoint);
  React.useEffect(() => {
    const on = () => setD(window.innerWidth >= breakpoint);
    window.addEventListener('resize', on);
    return () => window.removeEventListener('resize', on);
  }, [breakpoint]);
  return d;
}

// ─────────────────────────────────────────────────────────────
// Route map for nav items / dropdowns
// ─────────────────────────────────────────────────────────────
// Primary nav. Specialists is pinned right after Home so it's always
// the first contentful entry; Documentation is no longer here — the
// /docs hub is reachable from the About page and from the footer.
const NAV_PRIMARY = [
  { key: 'home',        to: '/',            ru: 'Главная',     en: 'Home' },
  { key: 'specialists', to: '/specialists', ru: 'Команда',     en: 'Team' },
  { key: 'services',    to: '/services',    ru: 'Практики',    en: 'Practices',  drop: 'services' },
  { key: 'crypto',      to: '/crypto',      ru: 'Крипто',      en: 'Crypto' },
  { key: 'forex',       to: '/forex',       ru: 'Forex',       en: 'Forex' },
  { key: 'business',    to: '/business',    ru: 'Бизнес',      en: 'Business' },
  { key: 'about',       to: '/about',       ru: 'О нас',       en: 'About' },
  { key: 'contact',     to: '/contact',     ru: 'Контакты',    en: 'Contact' },
];

const SERVICES_DROP = [
  { to: '/services/crypto-recovery',            ru: 'Возврат криптовалют',          en: 'Crypto Recovery' },
  { to: '/services/forex-disputes',             ru: 'Споры с Forex-брокерами',      en: 'Forex & Broker Disputes' },
  { to: '/services/investment-fraud',           ru: 'Инвестиционное мошенничество', en: 'Investment Fraud' },
  { to: '/services/broker-withdrawal-disputes', ru: 'Споры по выводу средств',      en: 'Withdrawal Disputes' },
  { to: '/services/b2b-disputes',               ru: 'B2B финансовые споры',         en: 'B2B Disputes' },
  { to: '/services/chargeback',                 ru: 'Chargeback / платёжные споры', en: 'Chargeback Support' },
];

const DOCS_DROP = [
  { to: '/docs/company',            ru: 'Информация о компании',  en: 'Company information' },
  { to: '/docs/kvk',                ru: 'KVK регистрация',        en: 'KVK registration' },
  { to: '/docs/case-review-policy', ru: 'Политика разбора дела',  en: 'Case-review policy' },
  { to: '/docs/client-checklist',   ru: 'Чеклист документов',     en: 'Client checklist' },
  { to: '/docs/complaints',         ru: 'Процедура жалоб',        en: 'Complaints' },
  { to: '/docs/risk-disclosure',    ru: 'Раскрытие рисков',       en: 'Risk disclosure' },
  { to: '/privacy',                 ru: 'Конфиденциальность',     en: 'Privacy' },
  { to: '/terms',                   ru: 'Условия обращения',      en: 'Engagement terms' },
];

function navLabel(item, lang) { return item[lang] || item.ru; }

// Cheap inline RU/EN switch for short UI strings that are not worth a full DICT entry.
function tx(lang, ru, en) { return lang === 'en' ? en : ru; }

// ─────────────────────────────────────────────────────────────
// Production navigation — desktop with dropdowns
// ─────────────────────────────────────────────────────────────
function ProdUtil() {
  const { lang, setLang, t } = useLang();
  return (
    <div style={{
      background: T.ink, color: 'rgba(255,255,255,0.78)',
      padding: '10px 22px', display: 'flex', justifyContent: 'space-between', alignItems: 'center',
      ...TX.mono, fontSize: 10, letterSpacing: '0.14em', gap: 18,
    }}>
      <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{t.util.city}</span>
      <span role="group" aria-label={lang === 'ru' ? 'Переключатель языка' : 'Language switcher'} style={{
        display: 'inline-flex', alignItems: 'center', border: '1px solid rgba(255,255,255,0.32)', padding: 2, flexShrink: 0,
      }}>
        {['ru', 'en'].map((L) => (
          <button key={L} onClick={() => setLang(L)} aria-pressed={lang === L} aria-label={L === 'ru' ? 'Русский' : 'English'} style={{
            ...TX.mono, fontSize: 11, letterSpacing: '0.14em', cursor: 'pointer',
            padding: '5px 11px', border: 'none', fontFamily: 'inherit',
            background: lang === L ? T.gold : 'transparent',
            color: lang === L ? T.ink : 'rgba(255,255,255,0.88)',
            fontWeight: lang === L ? 600 : 500,
            transition: 'background .15s ease, color .15s ease',
          }}>{L.toUpperCase()}</button>
        ))}
      </span>
    </div>
  );
}

function Wordmark({ light = false }) {
  const c = light ? T.white : T.ink;
  return (
    <Link to="/" style={{ display: 'flex', alignItems: 'center', gap: 12, textDecoration: 'none' }} ariaLabel="De Vries & Partners — homepage">
      <BrandMark size={22} color={T.ink} accent={T.gold} light={light} />
      <span style={{ fontSize: 14, fontWeight: 600, letterSpacing: '0.18em', color: c, textTransform: 'uppercase' }}>
        DE VRIES<span style={{ color: T.gold }}> · </span>PARTNERS
      </span>
    </Link>
  );
}

function DesktopNav({ activePath }) {
  const { lang, t } = useLang();
  const [open, setOpen] = React.useState('');
  return (
    <div onMouseLeave={() => setOpen('')} style={{
      position: 'sticky', top: 0, zIndex: 50, background: T.white, borderBottom: `1px solid ${T.hair}`,
    }}>
      <ProdUtil />
      <div style={{
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        padding: '18px 56px', maxWidth: 1320, margin: '0 auto', gap: 32,
      }}>
        <Wordmark />
        <nav style={{ display: 'flex', alignItems: 'center', gap: 24 }} aria-label={lang === 'ru' ? 'Главная навигация' : 'Primary navigation'}>
          {NAV_PRIMARY.map((it) => {
            const isActive = it.to === activePath || (it.drop && activePath.startsWith(it.to + '/'));
            const isHovered = !!it.drop && open === it.drop;
            const label = navLabel(it, lang);
            return (
              <div key={it.key} onMouseEnter={() => setOpen(it.drop || '')}>
                <Link to={it.to} ariaLabel={label} style={{
                  color: isActive ? T.ink : T.body,
                  fontSize: 14, fontWeight: isActive ? 600 : 500, textDecoration: 'none',
                  padding: '10px 0', display: 'inline-flex', alignItems: 'center', gap: 6,
                  borderBottom: (isHovered || isActive) ? `2px solid ${T.gold}` : '2px solid transparent',
                  transition: 'border-color .2s ease, color .2s ease',
                }} aria-current={isActive ? 'page' : undefined}>
                  {label}{it.drop && <span style={{ fontSize: 9, color: T.mute }}>▾</span>}
                </Link>
              </div>
            );
          })}
        </nav>
        <Link to="/contact" style={{
          background: T.ink, color: T.white, textDecoration: 'none',
          padding: '14px 22px', fontSize: 12, fontWeight: 600, letterSpacing: '0.04em',
          textTransform: 'uppercase', display: 'inline-flex', alignItems: 'center', gap: 10,
          transition: 'background .2s ease',
        }} onMouseEnter={(e) => { e.currentTarget.style.background = T.gold; e.currentTarget.style.color = T.ink; }}
           onMouseLeave={(e) => { e.currentTarget.style.background = T.ink; e.currentTarget.style.color = T.white; }}>
          {t.ctaPrimary} <span>→</span>
        </Link>
      </div>
      {open === 'services' && <DropPanel title={t.sec.practices.title} items={SERVICES_DROP} lang={lang} />}
      {open === 'docs'     && <DropPanel title={t.docs && t.docs.title || 'Документация'} items={DOCS_DROP} lang={lang} />}
    </div>
  );
}

function DropPanel({ title, items, lang }) {
  return (
    <div style={{
      position: 'absolute', left: 0, right: 0, top: '100%',
      background: T.paper, borderBottom: `1px solid ${T.hair}`, animation: 'fadeDown .2s ease',
    }}>
      <div style={{
        maxWidth: 1320, margin: '0 auto', padding: '32px 56px',
        display: 'grid', gridTemplateColumns: '1fr 2fr', gap: 56,
      }}>
        <div>
          <Kicker>{lang === 'ru' ? 'Разделы' : 'Sections'}</Kicker>
          <div style={{ ...TX.display, fontSize: 28, marginTop: 14, color: T.ink, maxWidth: 320 }}>{title}</div>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', borderTop: `1px solid ${T.hair2}` }}>
          {items.map((d, i) => (
            <Link key={d.to} to={d.to} style={{
              padding: '14px 16px', display: 'flex', justifyContent: 'space-between', alignItems: 'center',
              color: T.ink, textDecoration: 'none', fontSize: 14, borderBottom: `1px solid ${T.hair2}`,
              borderRight: i % 2 === 0 ? `1px solid ${T.hair2}` : 'none',
            }}>
              <span style={{ display: 'flex', gap: 12 }}>
                <span style={{ ...TX.mono, fontSize: 9, color: T.mute }}>{String(i + 1).padStart(2, '0')}</span>
                {navLabel(d, lang)}
              </span>
              <span style={{ color: T.gold }}>→</span>
            </Link>
          ))}
        </div>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Production navigation — mobile sheet
// ─────────────────────────────────────────────────────────────
function MobileNav({ activePath }) {
  const { lang, t } = useLang();
  const [open, setOpen] = React.useState(false);
  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('keydown', onKey);
    return () => document.removeEventListener('keydown', onKey);
  }, [open]);
  return (
    <>
      <ProdUtil />
      <div style={{
        position: 'sticky', top: 0, zIndex: 50, background: T.white,
        borderBottom: `1px solid ${T.hair}`,
        display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '14px 18px',
      }}>
        <Wordmark />
        <button onClick={() => setOpen(!open)}
          aria-label={open ? (lang === 'ru' ? 'Закрыть меню' : 'Close menu') : (lang === 'ru' ? 'Открыть меню' : 'Open menu')}
          aria-expanded={open}
          style={{
            width: 48, height: 48, border: `1px solid ${T.ink}`, background: T.white, cursor: 'pointer',
            display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', gap: 5,
          }}>
          <span style={{ width: 18, height: 2, background: T.ink }} />
          <span style={{ width: 18, height: 2, background: T.ink }} />
        </button>
      </div>
      {open && <MobileNavSheet onClose={() => setOpen(false)} activePath={activePath} />}
    </>
  );
}

function MobileNavSheet({ onClose, activePath }) {
  const { lang, setLang, t } = useLang();
  const [openSec, setOpenSec] = React.useState('services');
  return (
    <div style={{ position: 'fixed', inset: 0, zIndex: 60, background: 'rgba(11,15,20,0.6)', animation: 'fadeIn .25s ease' }} onClick={onClose}>
      <div onClick={(e) => e.stopPropagation()} style={{
        position: 'absolute', top: 0, right: 0, bottom: 0, width: '94%', maxWidth: 400,
        background: T.white, padding: '20px 22px 90px', overflowY: 'auto',
        boxShadow: '-30px 0 80px rgba(11,15,20,0.32)',
        animation: 'slideIn .3s cubic-bezier(.2,.7,.2,1)',
      }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 22 }}>
          <span style={{ display: 'inline-flex', alignItems: 'center', border: `1px solid ${T.hair2}`, padding: 1 }}>
            {['ru', 'en'].map((L) => (
              <button key={L} onClick={() => setLang(L)} style={{
                ...TX.mono, fontSize: 10, letterSpacing: '0.12em', cursor: 'pointer',
                padding: '4px 8px', border: 'none', background: lang === L ? T.ink : 'transparent',
                color: lang === L ? T.white : T.ink, fontFamily: 'inherit',
              }}>{L.toUpperCase()}</button>
            ))}
          </span>
          <button onClick={onClose} style={{
            width: 42, height: 42, border: `1px solid ${T.ink}`, background: T.white, cursor: 'pointer', fontSize: 16,
          }}>✕</button>
        </div>
        {NAV_PRIMARY.map((it) => {
          const sub = it.drop === 'services' ? SERVICES_DROP
                    : it.drop === 'docs'     ? DOCS_DROP
                    : null;
          const isActive = it.to === activePath;
          return (
            <div key={it.key} style={{ borderBottom: `1px solid ${T.hair}`, padding: '12px 0' }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <Link to={it.to} onClick={() => !sub && onClose()} style={{
                  flex: 1, padding: '4px 0', cursor: 'pointer', fontSize: 18, fontWeight: isActive ? 600 : 500,
                  color: T.ink, textDecoration: 'none', letterSpacing: '-0.01em',
                }}>
                  {navLabel(it, lang)}
                </Link>
                {sub && (
                  <button onClick={() => setOpenSec(openSec === it.drop ? '' : it.drop)} style={{
                    width: 36, height: 36, background: 'transparent', border: 'none', cursor: 'pointer',
                    color: T.gold, ...TX.mono, fontSize: 16,
                  }}>{openSec === it.drop ? '−' : '+'}</button>
                )}
              </div>
              {sub && openSec === it.drop && (
                <div style={{ marginTop: 8, display: 'grid', gap: 2 }}>
                  {sub.map((k, i) => (
                    <Link key={k.to} to={k.to} onClick={onClose} style={{
                      color: T.ink2, fontSize: 14, textDecoration: 'none', padding: '12px 14px',
                      display: 'flex', justifyContent: 'space-between', alignItems: 'center',
                      background: T.paper, borderLeft: `2px solid ${T.gold}`,
                    }}>
                      <span><span style={{ ...TX.mono, fontSize: 9, color: T.mute, marginRight: 10 }}>{String(i + 1).padStart(2, '0')}</span>{navLabel(k, lang)}</span>
                      <span style={{ color: T.mute }}>→</span>
                    </Link>
                  ))}
                </div>
              )}
            </div>
          );
        })}
        <Link to="/contact" onClick={onClose} style={{
          display: 'block', textAlign: 'center', textDecoration: 'none',
          marginTop: 28, padding: '18px', background: T.ink, color: T.white,
          ...TX.mono, fontSize: 12, letterSpacing: '0.08em',
        }}>{t.ctaPrimary} →</Link>
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Production footer — premium institutional shell
// ─────────────────────────────────────────────────────────────
function ProdFooter() {
  const { lang, t } = useLang();
  const isDesktop = useIsDesktop(720);
  const sitemap = [
    [t.nav[0], '/'], [t.nav[1], '/services'], [t.nav[2], '/crypto'], [t.nav[3], '/forex'],
    [t.nav[4], '/business'], [t.nav[5], '/about'], [t.nav[6], '/specialists'], [t.nav[7], '/contact'],
  ];
  return (
    <footer style={{
      background: T.graphite, color: T.white, position: 'relative', overflow: 'hidden',
    }}>
      <Diamond size={260} bottom="-110px" right="-90px" color={T.gold} opacity={0.10} dur={26} />
      <Diamond size={140} top="40%"     left="-50px"   color={T.steel} opacity={0.18} fill dur={22} delay={1} />

      {/* Contact CTA band */}
      <div style={{
        borderBottom: '1px solid rgba(255,255,255,0.16)',
        padding: isDesktop ? '40px 56px' : '28px 22px',
      }}>
        <div style={{
          maxWidth: 1320, margin: '0 auto',
          display: 'grid', gridTemplateColumns: isDesktop ? '1.6fr auto' : '1fr',
          gap: isDesktop ? 40 : 22, alignItems: 'center',
        }}>
          <div>
            <div style={{ ...TX.mono, fontSize: 10, color: T.gold, marginBottom: 10 }}>{(t.footerCta.kicker || '').toUpperCase()}</div>
            <div style={{
              ...TX.display, fontSize: isDesktop ? 28 : 22, color: T.white, marginBottom: 8, maxWidth: 720, lineHeight: 1.15,
            }}>{t.footerCta.title}</div>
            <p style={{ fontSize: 14, color: 'rgba(255,255,255,0.72)', lineHeight: 1.6, margin: 0, maxWidth: 720 }}>{t.footerCta.sub}</p>
          </div>
          <Link to="/contact" style={{
            display: 'inline-flex', alignItems: 'center', gap: 12, justifySelf: isDesktop ? 'end' : 'start',
            background: T.gold, color: T.ink, textDecoration: 'none',
            padding: '18px 26px', ...TX.mono, fontSize: 12, letterSpacing: '0.08em',
            transition: 'transform .2s ease',
          }} onMouseEnter={(e) => { e.currentTarget.style.transform = 'translateX(4px)'; }}
             onMouseLeave={(e) => { e.currentTarget.style.transform = 'translateX(0)'; }}>
            {t.footerCta.btn}
          </Link>
        </div>
      </div>

      {/* Five-column identity / contact / channels / sitemap / legal */}
      <div style={{
        padding: isDesktop ? '52px 56px 28px' : '36px 22px 22px',
      }}>
        <div style={{ maxWidth: 1320, margin: '0 auto' }}>
          <div style={{
            display: 'grid',
            gridTemplateColumns: isDesktop ? '1.6fr 1fr 1fr 1fr 1fr' : '1fr',
            gap: isDesktop ? 28 : 24, marginBottom: 30,
          }}>
            {/* Identity */}
            <div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 14 }}>
                <BrandMark size={26} color={T.white} accent={T.gold} light />
                <span style={{ fontSize: 15, fontWeight: 600, letterSpacing: '0.18em', color: T.white, textTransform: 'uppercase' }}>
                  DE VRIES<span style={{ color: T.gold }}> · </span>PARTNERS
                </span>
              </div>
              <address style={{ fontStyle: 'normal', fontSize: 13, color: 'rgba(255,255,255,0.78)', lineHeight: 1.7, marginBottom: 14 }}>
                De Vries &amp; Partners B.V.<br />
                Hoofdvestiging<br />
                Waspik, Benedenkerkstraat 1<br />
                {lang === 'ru' ? 'Нидерланды' : 'Netherlands'}
              </address>
              <div style={{ ...TX.mono, fontSize: 10, color: 'rgba(255,255,255,0.55)', lineHeight: 1.7 }}>
                KVK 27164831<br />
                {lang === 'ru' ? 'РЕГ. 13.01.2012' : 'REG. 13.01.2012'}
              </div>
            </div>

            <FooterCol title={lang === 'ru' ? 'Связь' : 'Contact'} rows={t.footer.contactRows.filter(([k]) => !/^(Адрес|Address|Страна|Country)$/i.test(k))} />
            <FooterCol title={lang === 'ru' ? 'Каналы' : 'Channels'} rows={t.footer.channelRows} />
            <FooterLinkCol title={lang === 'ru' ? 'Карта сайта' : 'Sitemap'} links={sitemap} />
            <FooterLinkCol title={t.legalCol.title} links={t.legalCol.links} />
          </div>

          {/* Bottom legal band */}
          <div style={{
            borderTop: '1px solid rgba(255,255,255,0.16)', paddingTop: 18,
            display: 'grid', gridTemplateColumns: isDesktop ? '2fr 1fr' : '1fr', gap: 18,
          }}>
            <p style={{ fontSize: 11, color: 'rgba(255,255,255,0.48)', lineHeight: 1.6, margin: 0, maxWidth: 760 }}>
              {t.footer.legal}
            </p>
            <div style={{ ...TX.mono, fontSize: 9, color: 'rgba(255,255,255,0.42)', textAlign: isDesktop ? 'right' : 'left', lineHeight: 1.7, alignSelf: 'end' }}>
              {t.footer.copy.toUpperCase()}<br />
              {(lang === 'ru' ? 'МАТЕРИАЛЫ САЙТА НЕ ЯВЛЯЮТСЯ ЮРИДИЧЕСКОЙ КОНСУЛЬТАЦИЕЙ' : 'NOTHING ON THIS SITE IS LEGAL ADVICE')}
            </div>
          </div>
        </div>
      </div>
    </footer>
  );
}

function FooterCol({ title, rows }) {
  return (
    <div>
      <div style={{ ...TX.mono, fontSize: 10, color: T.gold, marginBottom: 12, letterSpacing: '0.14em' }}>{title.toUpperCase()}</div>
      {rows.map((row) => {
        const k = row[0], v = row[1], href = row[2];
        return (
          <div key={k} style={{ fontSize: 13, color: 'rgba(255,255,255,0.78)', padding: '4px 0', lineHeight: 1.55 }}>
            <span style={{ ...TX.mono, fontSize: 9, color: 'rgba(255,255,255,0.45)', marginRight: 8 }}>{k.toUpperCase()}</span>
            {href ? (
              <a href={href}
                 target={href.startsWith('http') ? '_blank' : undefined}
                 rel={href.startsWith('http') ? 'noopener noreferrer' : undefined}
                 style={{ color: 'rgba(255,255,255,0.92)', textDecoration: 'none', borderBottom: '1px solid transparent', transition: 'border-color .15s ease, color .15s ease' }}
                 onMouseEnter={(e) => { e.currentTarget.style.borderBottomColor = T.gold; e.currentTarget.style.color = T.gold; }}
                 onMouseLeave={(e) => { e.currentTarget.style.borderBottomColor = 'transparent'; e.currentTarget.style.color = 'rgba(255,255,255,0.92)'; }}>
                {v}
              </a>
            ) : v}
          </div>
        );
      })}
    </div>
  );
}

function FooterLinkCol({ title, links }) {
  return (
    <div>
      <div style={{ ...TX.mono, fontSize: 10, color: T.gold, marginBottom: 12, letterSpacing: '0.14em' }}>{title.toUpperCase()}</div>
      <div style={{ display: 'grid', gap: 6 }}>
        {links.map(([label, to]) => (
          <Link key={to} to={to} style={{
            display: 'inline-block', fontSize: 13, color: 'rgba(255,255,255,0.78)',
            padding: '3px 0', textDecoration: 'none', transition: 'color .15s ease',
          }} onMouseEnter={(e) => { e.currentTarget.style.color = T.gold; }}
             onMouseLeave={(e) => { e.currentTarget.style.color = 'rgba(255,255,255,0.78)'; }}>{label}</Link>
        ))}
      </div>
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// Trust strip — five operating principles, shown above inner pages
// ─────────────────────────────────────────────────────────────
function TrustStrip() {
  const { t } = useLang();
  const isDesktop = useIsDesktop();
  return (
    <section aria-label={t.trustStrip.kicker} style={{
      background: T.paper, borderBottom: `1px solid ${T.hair}`,
      padding: isDesktop ? '22px 56px' : '18px 22px',
    }}>
      <div style={{ maxWidth: 1320, margin: '0 auto' }}>
        <div style={{ ...TX.mono, fontSize: 10, color: T.gold, marginBottom: 14 }}>{t.trustStrip.kicker.toUpperCase()}</div>
        <div style={{
          display: 'grid', gridTemplateColumns: isDesktop ? 'repeat(5, 1fr)' : '1fr',
          gap: isDesktop ? 0 : 12,
          borderTop: `1px solid ${T.hair2}`,
        }}>
          {t.trustStrip.items.map(([k, v], i) => (
            <div key={i} style={{
              padding: isDesktop ? '16px 18px 4px' : '12px 0',
              borderRight: isDesktop && i < t.trustStrip.items.length - 1 ? `1px solid ${T.hair2}` : 'none',
              display: 'grid', gridTemplateColumns: '20px 1fr', gap: 12,
            }}>
              <span aria-hidden style={{
                width: 10, height: 10, border: `1px solid ${T.gold}`, transform: 'rotate(45deg)',
                display: 'inline-block', marginTop: 6,
              }} />
              <div>
                <div style={{ fontSize: 14, fontWeight: 600, color: T.ink, marginBottom: 4, letterSpacing: '-0.01em' }}>{k}</div>
                <div style={{ fontSize: 12, color: T.body, lineHeight: 1.5 }}>{v}</div>
              </div>
            </div>
          ))}
        </div>
      </div>
    </section>
  );
}

// ─────────────────────────────────────────────────────────────
// Page shell (nav + content + footer + sticky mobile CTA)
// ─────────────────────────────────────────────────────────────
function PageShell({ children, activePath, showTrust = true }) {
  const isDesktop = useIsDesktop();
  return (
    <div style={{
      minHeight: '100vh', background: T.white, color: T.ink,
      fontFamily: '"Inter", system-ui, sans-serif',
      fontSize: isDesktop ? 17 : 16, lineHeight: 1.6, letterSpacing: '-0.005em',
    }}>
      {isDesktop ? <DesktopNav activePath={activePath} /> : <MobileNav activePath={activePath} />}
      <main>{children}</main>
      {showTrust && <TrustStrip />}
      <ProdFooter />
      {!isDesktop && <StickyMobileCTA />}
    </div>
  );
}

function StickyMobileCTA() {
  const { t } = useLang();
  const [show, setShow] = React.useState(false);
  React.useEffect(() => {
    const on = () => setShow(window.scrollY > 600);
    window.addEventListener('scroll', on, { passive: true });
    return () => window.removeEventListener('scroll', on);
  }, []);
  if (!show) return null;
  return (
    <Link to="/contact" style={{
      position: 'fixed', left: 12, right: 12, bottom: 12, zIndex: 40,
      background: T.ink, color: T.white, textAlign: 'center', textDecoration: 'none',
      padding: '16px', ...TX.mono, fontSize: 12, letterSpacing: '0.08em',
      boxShadow: '0 12px 30px rgba(11,15,20,0.35)',
    }}>{t.ctaPrimary} →</Link>
  );
}

// ─────────────────────────────────────────────────────────────
// Reusable page primitives
// ─────────────────────────────────────────────────────────────
const PAD_DESKTOP = '0 56px';
const PAD_MOBILE = '0 22px';

function PageBreadcrumbs({ trail }) {
  const isDesktop = useIsDesktop();
  return (
    <div style={{
      padding: isDesktop ? '20px 56px' : '14px 22px',
      borderBottom: `1px solid ${T.hair}`,
      display: 'flex', gap: 10, flexWrap: 'wrap',
      ...TX.mono, fontSize: 10, color: T.mute,
    }}>
      {trail.map((node, i) => (
        <React.Fragment key={i}>
          {node.to && i < trail.length - 1
            ? <Link to={node.to} style={{ color: T.mute, textDecoration: 'none' }}>{node.label}</Link>
            : <span style={{ color: i === trail.length - 1 ? T.gold : T.mute }}>{node.label}</span>}
          {i < trail.length - 1 && <span>/</span>}
        </React.Fragment>
      ))}
    </div>
  );
}

function PageHero({ num, kicker, title, sub, badges }) {
  const isDesktop = useIsDesktop();
  return (
    <section style={{
      padding: isDesktop ? '72px 56px 84px' : '40px 22px 52px',
      background: T.white, position: 'relative', overflow: 'hidden',
      borderBottom: `1px solid ${T.hair}`,
    }}>
      <Diamond size={isDesktop ? 320 : 180} top="-80px" right="-60px" color={T.gold} opacity={0.12} dur={26} />
      <Diamond size={isDesktop ? 120 : 70} bottom="-30px" left="55%" color={T.gold} opacity={0.4} fill dur={18} delay={1} />
      <div style={{
        display: 'grid',
        gridTemplateColumns: isDesktop && badges ? '1fr 360px' : '1fr',
        gap: 40, alignItems: 'end',
      }}>
        <div>
          <div style={{ ...TX.mono, fontSize: 11, color: T.gold, marginBottom: 14 }}>§ {num} · {kicker}</div>
          <Reveal>
            <h1 style={{
              ...TX.display,
              fontSize: isDesktop ? 56 : 36, lineHeight: 1.04,
              margin: '0 0 22px', color: T.ink, textWrap: 'balance', maxWidth: 820,
            }}>{title}</h1>
          </Reveal>
          <Reveal delay={120}>
            <p style={{ fontSize: isDesktop ? 17 : 15, color: T.body, lineHeight: 1.6, margin: 0, maxWidth: 700 }}>{sub}</p>
          </Reveal>
        </div>
        {badges && (
          <Reveal delay={200}>
            <div style={{
              background: T.ink, color: T.white, padding: '22px 22px 26px',
              position: 'relative', overflow: 'hidden',
            }}>
              <Diamond size={140} top="-30px" right="-30px" color={T.gold} opacity={0.4} fill dur={18} />
              <div style={{ ...TX.mono, fontSize: 9, color: T.gold, marginBottom: 14 }}>КРАТКАЯ СПРАВКА</div>
              {badges.map(([k, v]) => (
                <div key={k} style={{
                  display: 'grid', gridTemplateColumns: '120px 1fr', gap: 12, padding: '8px 0',
                  borderBottom: '1px solid rgba(255,255,255,0.14)', fontSize: 13,
                }}>
                  <span style={{ ...TX.mono, fontSize: 9, color: 'rgba(255,255,255,0.55)' }}>{k}</span>
                  <span style={{ color: T.white, fontWeight: 500 }}>{v}</span>
                </div>
              ))}
            </div>
          </Reveal>
        )}
      </div>
    </section>
  );
}

function Section({ num, kicker, title, intro, children, dark = false, dense = false }) {
  const isDesktop = useIsDesktop();
  const bg = dark ? T.ink : T.white;
  const tx = dark ? T.white : T.ink;
  const intColor = dark ? 'rgba(255,255,255,0.78)' : T.body;
  return (
    <section style={{
      background: bg, color: tx,
      padding: isDesktop ? (dense ? '56px 56px' : '76px 56px') : (dense ? '36px 22px' : '48px 22px'),
      borderBottom: `1px solid ${dark ? 'rgba(255,255,255,0.12)' : T.hair}`,
      position: 'relative', overflow: 'hidden',
    }}>
      <div style={{ maxWidth: 1320, margin: '0 auto' }}>
        <div style={{ ...TX.mono, fontSize: 10, color: T.gold, marginBottom: 10 }}>§ {num} · {kicker}</div>
        <Reveal>
          <h2 style={{
            ...TX.display, fontSize: isDesktop ? 36 : 26, lineHeight: 1.1,
            margin: '0 0 16px', color: tx, maxWidth: 820,
          }}>{title}</h2>
        </Reveal>
        {intro && (
          <Reveal delay={100}>
            <p style={{ fontSize: isDesktop ? 16 : 15, color: intColor, lineHeight: 1.6, margin: '0 0 32px', maxWidth: 720 }}>{intro}</p>
          </Reveal>
        )}
        {children}
      </div>
    </section>
  );
}

function NumberedList({ items, dark = false }) {
  const isDesktop = useIsDesktop();
  return (
    <div style={{
      display: 'grid',
      gridTemplateColumns: isDesktop ? 'repeat(2, 1fr)' : '1fr',
      borderTop: `1px solid ${dark ? 'rgba(255,255,255,0.12)' : T.hair}`,
    }}>
      {items.map(([num, title, body], i) => (
        <Reveal key={i} delay={i * 60}>
          <div style={{
            padding: isDesktop ? '24px 28px' : '18px 4px',
            borderBottom: `1px solid ${dark ? 'rgba(255,255,255,0.12)' : T.hair}`,
            borderRight: isDesktop && i % 2 === 0 ? `1px solid ${dark ? 'rgba(255,255,255,0.12)' : T.hair}` : 'none',
            display: 'grid', gridTemplateColumns: '52px 1fr', gap: 16,
          }}>
            <span style={{ ...TX.mono, fontSize: 11, color: T.gold, paddingTop: 4 }}>{num}</span>
            <div>
              <div style={{ fontSize: 16, fontWeight: 600, marginBottom: 6, color: dark ? T.white : T.ink, letterSpacing: '-0.01em' }}>{title}</div>
              <div style={{ fontSize: 14, color: dark ? 'rgba(255,255,255,0.7)' : T.body, lineHeight: 1.55 }}>{body}</div>
            </div>
          </div>
        </Reveal>
      ))}
    </div>
  );
}

function CardGrid({ items, columns }) {
  const isDesktop = useIsDesktop();
  return (
    <div style={{
      display: 'grid',
      gridTemplateColumns: isDesktop ? `repeat(${columns || 3}, 1fr)` : '1fr',
      gap: 0, border: `1px solid ${T.hair}`,
    }}>
      {items.map((it, i) => {
        const Wrap = it.to ? Link : 'div';
        const wrapProps = it.to ? { to: it.to } : {};
        return (
          <Reveal key={i} delay={i * 60}>
            <Wrap {...wrapProps} style={{
              display: 'block', textDecoration: 'none', color: 'inherit',
              padding: isDesktop ? '28px 28px' : '22px 22px',
              borderRight: isDesktop && (i + 1) % (columns || 3) !== 0 ? `1px solid ${T.hair}` : 'none',
              borderBottom: `1px solid ${T.hair}`, height: '100%',
              position: 'relative', overflow: 'hidden',
              transition: 'background .25s ease, transform .35s cubic-bezier(.2,.7,.2,1)',
            }} onMouseEnter={(e) => {
              if (!it.to) return;
              e.currentTarget.style.background = T.paper;
              e.currentTarget.style.transform = 'translateY(-2px)';
            }} onMouseLeave={(e) => {
              if (!it.to) return;
              e.currentTarget.style.background = 'transparent';
              e.currentTarget.style.transform = 'translateY(0)';
            }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 12 }}>
                <div style={{ ...TX.mono, fontSize: 9, color: T.gold }}>{(it.tag || '').toUpperCase()}</div>
                {it.to && <span aria-hidden style={{ width: 8, height: 8, border: `1px solid ${T.gold}`, transform: 'rotate(45deg)', display: 'inline-block' }} />}
              </div>
              <div style={{ fontSize: 18, fontWeight: 600, marginBottom: 8, color: T.ink, letterSpacing: '-0.01em' }}>{it.title}</div>
              <div style={{ fontSize: 14, color: T.body, lineHeight: 1.55, marginBottom: 14 }}>{it.body}</div>
              {it.to && (
                <span style={{
                  ...TX.mono, fontSize: 10, color: T.ink,
                  display: 'inline-flex', alignItems: 'center', gap: 8,
                  borderBottom: `1px solid ${T.gold}`, paddingBottom: 4,
                }}>{(it.cta || 'ПОДРОБНЕЕ').toUpperCase()} →</span>
              )}
            </Wrap>
          </Reveal>
        );
      })}
    </div>
  );
}

function CTABlock() {
  const { t } = useLang();
  const isDesktop = useIsDesktop();
  return (
    <section style={{
      background: T.ink, color: T.white,
      padding: isDesktop ? '76px 56px' : '52px 22px',
      position: 'relative', overflow: 'hidden',
    }}>
      <Diamond size={300} top="-100px" left="-100px" color={T.gold} opacity={0.18} dur={22} />
      <Diamond size={140} bottom="-40px" right="10%" color={T.gold} opacity={0.5} fill dur={18} />
      <div style={{
        maxWidth: 1320, margin: '0 auto',
        display: 'grid', gridTemplateColumns: isDesktop ? '1.4fr 1fr' : '1fr', gap: 40, alignItems: 'end',
      }}>
        <div>
          <div style={{ ...TX.mono, fontSize: 10, color: T.gold, marginBottom: 14 }}>§ 09 · {t.sec.cta.kicker.toUpperCase()}</div>
          <h2 style={{
            ...TX.display, fontSize: isDesktop ? 48 : 32, color: T.white,
            margin: '0 0 16px', maxWidth: 720, lineHeight: 1.05,
          }}>{t.sec.cta.title}</h2>
          <p style={{ fontSize: 16, color: 'rgba(255,255,255,0.78)', lineHeight: 1.6, maxWidth: 580, margin: 0 }}>
            {t.sec.cta.intro}
          </p>
        </div>
        <div>
          {t.ctaSide.map(([k, v]) => (
            <div key={k} style={{
              display: 'grid', gridTemplateColumns: '160px 1fr', gap: 12, padding: '10px 0',
              borderBottom: '1px solid rgba(255,255,255,0.16)', fontSize: 13,
            }}>
              <span style={{ ...TX.mono, fontSize: 10, color: 'rgba(255,255,255,0.55)' }}>{k.toUpperCase()}</span>
              <span style={{ color: T.white }}>{v}</span>
            </div>
          ))}
          <Link to="/contact" style={{
            display: 'inline-flex', alignItems: 'center', gap: 14, marginTop: 20,
            background: T.gold, color: T.ink, textDecoration: 'none',
            padding: '18px 28px', ...TX.mono, fontSize: 12, letterSpacing: '0.08em',
          }}>{t.ctaPrimary.toUpperCase()} →</Link>
        </div>
      </div>
    </section>
  );
}

// ─────────────────────────────────────────────────────────────
// HOMEPAGE — uses canvas-built mobile / desktop homepages
// ─────────────────────────────────────────────────────────────
function HomePage() {
  const isDesktop = useIsDesktop();
  // The canvas homepages include their own nav and footer. We hide our PageShell
  // chrome on the home route to avoid duplication and show the full art-directed
  // experience that the design team approved.
  return (
    <div style={{
      minHeight: '100vh', background: T.white, color: T.ink,
      fontFamily: '"Inter", system-ui, sans-serif', fontSize: 16, lineHeight: 1.55, letterSpacing: '-0.005em',
    }}>
      {isDesktop ? <DesktopHomepage /> : <MobileHomepage />}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// /services — index of practice areas
// ─────────────────────────────────────────────────────────────
// SERVICE_DATA holds detail-page content for the six practice routes
// under /services/. The shape is `{ ru: {...slugs}, en: {...slugs} }`
// — consumers select by current language. Until 2026-04-30 the EN
// branch was missing, so EN visitors saw RU content; now both branches
// carry the same six slugs with parallel copy.
const SERVICE_DATA = {
  ru: {
    'crypto-recovery': {
      num: '01', kicker: 'Возврат криптовалют',
      title: 'Возврат криптоактивов: трассировка, биржи, требования.',
      sub: 'On-chain трассировка по основным сетям, взаимодействие с комплаенс-службами бирж, документирование схемы депозита, кастоди и дальнейших перемещений. Каждое дело ведётся именованным специалистом от приёма до закрытия.',
      badges: [
        ['ТИП ДЕЛ', 'Криптомошенничество, поддельные «советники»'],
        ['ЭВИДЕНЦИЯ', 'Адреса кошельков, TXID, переписка'],
        ['КАНАЛЫ', 'Биржевой комплаенс, KYC-привязка'],
        ['ПЕРВЫЙ ОТКЛИК', '2 рабочих дня'],
      ],
      method: [
        ['I',   'Сбор адресов и хэшей',         'Депозитные адреса, исходящие транзакции, временные метки.'],
        ['II',  'Кластерный анализ',            'Сопоставление кошельков, выявление кастодиальных точек.'],
        ['III', 'Биржевая привязка',            'Запросы в комплаенс с приложением KYC-доказательной базы.'],
        ['IV',  'Подача требования',            'Структурированное обращение по факту мошенничества.'],
        ['V',   'Сопровождение и эскалация',    'Контроль ответов, переключение канала при необходимости.'],
      ],
      evidence: ['Адреса кошельков (отправитель, получатель)', 'Хэши транзакций (TXID)', 'Скриншоты платформы и баланса', 'Переписка в Telegram / WhatsApp', 'Подтверждения переводов из банка', 'Информация о «советнике» (имена, контакты)'],
    },
    'forex-disputes': {
      num: '02', kicker: 'Forex-споры',
      title: 'Споры с Forex- и CFD-брокерами по выводу и условиям.',
      sub: 'Розничные Forex- и CFD-платформы нередко затягивают вывод, ретроактивно применяют бонусные условия или замораживают счёт после депозита. Мы документируем закономерности и подаём структурированные обращения по доступным каналам.',
      badges: [
        ['ТИП ДЕЛ', 'Отказ в выводе, заморозка счёта, бонусные ловушки'],
        ['ЭВИДЕНЦИЯ', 'Скрины тикетов, T&C, выписки'],
        ['КАНАЛЫ', 'Карточный чарджбэк, регулятор, гражданский путь'],
        ['ПЕРВЫЙ ОТКЛИК', '2 рабочих дня'],
      ],
      method: [
        ['I',   'Хронология счёта',         'Депозиты, бонусы, заявки на вывод и отказы.'],
        ['II',  'Анализ T&C и бонусов',     'Поиск ретроактивных условий, кабальных пунктов.'],
        ['III', 'Сопоставление кодов',      'Подбор кода чарджбэка по эпизодам, где применимо.'],
        ['IV',  'Подача жалоб',             'Регулятор, банк-эмитент, процессор, гражданский путь.'],
        ['V',   'Сопровождение',            'Контроль ответов, повторное представление по чарджбэку.'],
      ],
      evidence: ['Скриншоты дашборда, балансов, тикетов', 'Подтверждения депозитов (карта, перевод, крипто)', 'Бонусные условия и пользовательское соглашение', 'Переписка со службой поддержки', 'Выписки по карте / счёту', 'KYC-формы и паспортные сканы (при наличии)'],
    },
    'investment-fraud': {
      num: '03', kicker: 'Инвестиционное мошенничество',
      title: 'Синтетические платформы, сигнальные группы, неуполномоченные «советники».',
      sub: 'Синтетические дашборды с фиктивной доходностью, схемы «персональных трейдеров», группы в мессенджерах с обещаниями результата. Дело ведётся как сборка доказательной базы и подача обращений по доступным каналам.',
      badges: [
        ['ТИП ДЕЛ', 'Поддельные платформы, скам-«советники»'],
        ['ЭВИДЕНЦИЯ', 'Скриншоты дашборда, чаты, переводы'],
        ['КАНАЛЫ', 'Биржа, банк, регулятор, чарджбэк'],
        ['ПЕРВЫЙ ОТКЛИК', '2 рабочих дня'],
      ],
      method: [
        ['I',   'Картирование схемы',     'Кто, где, какие платформы, кто оператор средств.'],
        ['II',  'Сборка доказательств',   'Скриншоты, переписка, подтверждения переводов.'],
        ['III', 'Выбор канала',           'Чарджбэк / биржа / регулятор / гражданский путь.'],
        ['IV',  'Подача обращений',       'Письменные требования с приложениями.'],
        ['V',   'Сопровождение',          'Контроль ответов, переключение канала.'],
      ],
      evidence: ['Скриншоты «личного кабинета» и баланса', 'Чаты с «советником» и группой', 'Подтверждения переводов и пополнений', 'Адреса кошельков получателя (если крипто)', 'Условия и оферты «инвестпрограммы»', 'Информация о платформе (домен, контакты)'],
    },
    'broker-withdrawal-disputes': {
      num: '04', kicker: 'Споры по выводу средств',
      title: 'Целевое вмешательство, когда брокер задерживает или отказывает в выводе.',
      sub: 'Когда платформа затягивает заявку на вывод, ссылается на дополнительный KYC после депозита или применяет бонусные ограничения постфактум — мы документируем ход переписки и подаём обращения по доступным каналам.',
      badges: [
        ['ТИП ДЕЛ', 'Затягивание вывода, KYC-предлог, заморозка'],
        ['ЭВИДЕНЦИЯ', 'Тикеты, скрины заявок, T&C'],
        ['КАНАЛЫ', 'Карточный чарджбэк, регулятор, банк'],
        ['ПЕРВЫЙ ОТКЛИК', '2 рабочих дня'],
      ],
      method: [
        ['I',   'Хронология заявок',      'Когда подана заявка, что отвечает поддержка, какие сроки.'],
        ['II',  'Анализ оснований отказа','Корректность ссылок на T&C, бонусы, KYC.'],
        ['III', 'Выбор канала',           'Чарджбэк, регулятор, банк, гражданский путь.'],
        ['IV',  'Подача обращений',       'Структурированное письменное требование.'],
        ['V',   'Сопровождение',          'Контроль ответов, эскалация в случае молчания.'],
      ],
      evidence: ['Скриншоты заявок на вывод и отказов', 'Тикеты в службе поддержки', 'Бонусные условия и пользовательское соглашение', 'Подтверждения депозитов', 'KYC-документы, отправленные платформе', 'Выписки по картам / счёту'],
    },
    'b2b-disputes': {
      num: '05', kicker: 'Корпоративные споры',
      title: 'B2B-споры: перенаправленные переводы, фрод по счетам, неисполнение обязательств.',
      sub: 'Корпоративные споры включают перехваченные SWIFT-переводы (BEC), подменные платёжные реквизиты, фиктивные инвойсы, неисполнение обязательств контрагентом. Сопровождение ведётся в координации с банками отправителя и получателя.',
      badges: [
        ['ТИП ДЕЛ', 'BEC, подмена реквизитов, неисполнение'],
        ['ЭВИДЕНЦИЯ', 'SWIFT, инвойсы, e-mail, договоры'],
        ['КАНАЛЫ', 'Банк отправителя/получателя, регулятор, civil'],
        ['ПЕРВЫЙ ОТКЛИК', '2 рабочих дня'],
      ],
      method: [
        ['I',   'Реконструкция перевода', 'SWIFT-цепочка, банки-корреспонденты, реквизиты.'],
        ['II',  'Анализ переписки',       'Когда и где была подмена, какие почтовые домены.'],
        ['III', 'Recall / Hold',          'Запрос отзыва или удержания через банк отправителя.'],
        ['IV',  'Жалоба и иск',           'Регуляторные жалобы, подготовка гражданского иска.'],
        ['V',   'Сопровождение',          'Контроль ответов, эскалация по корреспондентам.'],
      ],
      evidence: ['SWIFT-копии (MT103/MT199)', 'Инвойсы и договоры', 'E-mail-переписка с приложенными заголовками', 'Скриншоты подменных доменов', 'Документы по KYC контрагента', 'Внутренние процедуры платежей (если применимо)'],
    },
    'chargeback': {
      num: '06', kicker: 'Chargeback / платёжные споры',
      title: 'Чарджбэк-сопровождение: коды, возражения, повторное представление.',
      sub: 'Подготовка чарджбэков по основным кодам карточных сетей (Visa, Mastercard) и анализ возражений мерчанта. Сопровождение re-presentment, координация с банком-эмитентом и процессором.',
      badges: [
        ['ТИП ДЕЛ', 'Услуга не оказана, фрод, дубликаты'],
        ['ЭВИДЕНЦИЯ', 'Выписки, договор, переписка'],
        ['КАНАЛЫ', 'Банк-эмитент, карточная сеть'],
        ['ПЕРВЫЙ ОТКЛИК', '2 рабочих дня'],
      ],
      method: [
        ['I',   'Подбор кода',            'Сопоставление случая с reason code.'],
        ['II',  'Сборка пакета',          'Выписка, договор/оферта, доказательства неисполнения.'],
        ['III', 'Подача в банк-эмитент',  'Письменное обращение с приложениями.'],
        ['IV',  'Анализ возражений',      'Если мерчант возражает — повторное представление.'],
        ['V',   'Эскалация в сеть',       'Если применимо — арбитраж карточной сети.'],
      ],
      evidence: ['Выписка по карте за период', 'Договор / оферта мерчанта', 'Переписка с мерчантом', 'Скриншоты транзакции и описаний', 'Подтверждения попыток связаться с мерчантом', 'Документы об аннулировании, если есть'],
    },
  },
  en: {
    'crypto-recovery': {
      num: '01', kicker: 'Crypto Recovery',
      title: 'Crypto-asset recovery: tracing, exchanges, claims.',
      sub: 'On-chain tracing across major networks, engagement with exchange compliance teams, documentation of deposit, custody, and onward-flow patterns. Each case is owned by a named specialist from intake through closure.',
      badges: [
        ['CASE TYPE', 'Crypto fraud, fake "advisors"'],
        ['EVIDENCE', 'Wallet addresses, TXIDs, correspondence'],
        ['CHANNELS', 'Exchange compliance, KYC anchoring'],
        ['FIRST RESPONSE', '2 business days'],
      ],
      method: [
        ['I',   'Address & hash collection',  'Deposit addresses, outgoing transactions, timestamps.'],
        ['II',  'Cluster analysis',           'Wallet correlation, identification of custodial endpoints.'],
        ['III', 'Exchange anchoring',         'Compliance requests with the KYC evidence package attached.'],
        ['IV',  'Filing the claim',           'A structured submission documenting the fraud.'],
        ['V',   'Follow-up & escalation',     'Tracking responses; switching channels when needed.'],
      ],
      evidence: ['Wallet addresses (sender, receiver)', 'Transaction hashes (TXIDs)', 'Platform and balance screenshots', 'Telegram / WhatsApp correspondence', 'Bank transfer confirmations', '"Advisor" details (names, contacts)'],
    },
    'forex-disputes': {
      num: '02', kicker: 'Forex disputes',
      title: 'Forex and CFD broker disputes — withdrawals and terms.',
      sub: 'Retail Forex and CFD platforms often stall withdrawals, apply bonus terms retroactively, or freeze accounts after deposit. We document the patterns and file structured submissions through available channels.',
      badges: [
        ['CASE TYPE', 'Withdrawal refusal, account freeze, bonus traps'],
        ['EVIDENCE', 'Ticket screenshots, T&Cs, statements'],
        ['CHANNELS', 'Card chargeback, regulator, civil'],
        ['FIRST RESPONSE', '2 business days'],
      ],
      method: [
        ['I',   'Account timeline',         'Deposits, bonuses, withdrawal requests, refusals.'],
        ['II',  'T&C and bonus review',     'Hunt for retroactive terms and onerous clauses.'],
        ['III', 'Reason-code mapping',      'Pick a chargeback reason code per episode where applicable.'],
        ['IV',  'Filing complaints',        'Regulator, issuing bank, processor, civil track.'],
        ['V',   'Follow-up',                'Track responses; re-presentment in chargeback if needed.'],
      ],
      evidence: ['Dashboard, balance, ticket screenshots', 'Deposit confirmations (card, wire, crypto)', 'Bonus terms and the user agreement', 'Correspondence with support', 'Card / account statements', 'KYC forms and ID scans (if available)'],
    },
    'investment-fraud': {
      num: '03', kicker: 'Investment Fraud',
      title: 'Synthetic platforms, signal groups, unauthorised "advisors".',
      sub: 'Synthetic dashboards showing fictitious returns, "personal trader" schemes, messenger groups promising results. Casework is built as evidence assembly and submission to the available channels.',
      badges: [
        ['CASE TYPE', 'Fake platforms, scam "advisors"'],
        ['EVIDENCE', 'Dashboard screenshots, chats, transfers'],
        ['CHANNELS', 'Exchange, bank, regulator, chargeback'],
        ['FIRST RESPONSE', '2 business days'],
      ],
      method: [
        ['I',   'Mapping the scheme',     'Who, where, which platforms, who controls the funds.'],
        ['II',  'Evidence assembly',      'Screenshots, correspondence, transfer confirmations.'],
        ['III', 'Channel selection',      'Chargeback / exchange / regulator / civil.'],
        ['IV',  'Submissions',            'Written claims with attached exhibits.'],
        ['V',   'Follow-up',              'Track responses; switch channels when needed.'],
      ],
      evidence: ['"Personal cabinet" and balance screenshots', 'Chats with the "advisor" and group', 'Transfer and top-up confirmations', 'Recipient wallet addresses (if crypto)', '"Investment programme" terms and offers', 'Platform information (domain, contacts)'],
    },
    'broker-withdrawal-disputes': {
      num: '04', kicker: 'Withdrawal Disputes',
      title: 'Targeted intervention when a broker stalls or denies a withdrawal.',
      sub: 'When a platform delays a withdrawal request, cites post-deposit KYC, or applies bonus restrictions retroactively, we document the correspondence and file submissions through the available channels.',
      badges: [
        ['CASE TYPE', 'Withdrawal stalling, KYC pretext, freeze'],
        ['EVIDENCE', 'Tickets, request screenshots, T&Cs'],
        ['CHANNELS', 'Card chargeback, regulator, bank'],
        ['FIRST RESPONSE', '2 business days'],
      ],
      method: [
        ['I',   'Request timeline',        'When the request was filed, support replies, deadlines.'],
        ['II',  'Refusal-grounds analysis','Correctness of T&C, bonus and KYC references.'],
        ['III', 'Channel selection',      'Chargeback, regulator, bank, civil.'],
        ['IV',  'Submissions',            'A structured written demand.'],
        ['V',   'Follow-up',              'Track responses; escalate on silence.'],
      ],
      evidence: ['Withdrawal-request and refusal screenshots', 'Support tickets', 'Bonus terms and the user agreement', 'Deposit confirmations', 'KYC documents sent to the platform', 'Card / account statements'],
    },
    'b2b-disputes': {
      num: '05', kicker: 'Corporate disputes',
      title: 'B2B disputes: misdirected wires, invoice fraud, non-delivery.',
      sub: 'Corporate disputes include intercepted SWIFT wires (BEC), substituted payment details, fake invoices, and counterparty non-delivery. Casework is coordinated with both sender and recipient banks.',
      badges: [
        ['CASE TYPE', 'BEC, detail substitution, non-delivery'],
        ['EVIDENCE', 'SWIFT, invoices, emails, contracts'],
        ['CHANNELS', 'Sender / recipient bank, regulator, civil'],
        ['FIRST RESPONSE', '2 business days'],
      ],
      method: [
        ['I',   'Wire reconstruction',    'SWIFT chain, correspondent banks, beneficiary details.'],
        ['II',  'Correspondence review',  'When and where the substitution happened; mail domains.'],
        ['III', 'Recall / Hold',          'Recall or hold request through the sender bank.'],
        ['IV',  'Complaint and claim',    'Regulator complaints; civil-claim preparation.'],
        ['V',   'Follow-up',              'Track responses; correspondent-level escalation.'],
      ],
      evidence: ['SWIFT copies (MT103/MT199)', 'Invoices and contracts', 'Email correspondence with full headers', 'Spoofed-domain screenshots', 'Counterparty KYC documents', 'Internal payment procedures (where applicable)'],
    },
    'chargeback': {
      num: '06', kicker: 'Chargeback / payment disputes',
      title: 'Chargeback support: codes, rebuttals, re-presentment.',
      sub: 'Chargeback preparation under the major card-network reason codes (Visa, Mastercard) and merchant rebuttal review. Re-presentment support, coordinated with the issuing bank and processor.',
      badges: [
        ['CASE TYPE', 'Service not rendered, fraud, duplicates'],
        ['EVIDENCE', 'Statements, contract, correspondence'],
        ['CHANNELS', 'Issuing bank, card network'],
        ['FIRST RESPONSE', '2 business days'],
      ],
      method: [
        ['I',   'Reason-code selection',  'Match the case to a reason code.'],
        ['II',  'Package assembly',       'Statement, contract / offer, non-delivery evidence.'],
        ['III', 'Submission to issuer',   'Written submission with exhibits.'],
        ['IV',  'Rebuttal review',        'If the merchant pushes back — re-presentment.'],
        ['V',   'Network escalation',     'Where applicable — card-network arbitration.'],
      ],
      evidence: ['Card statement for the period', 'Merchant contract / offer', 'Correspondence with the merchant', 'Transaction and description screenshots', 'Proof of attempts to contact the merchant', 'Cancellation documents, if any'],
    },
  },
};

function ServicesIndexPage() {
  const { t, lang } = useLang();
  const data = SERVICE_DATA[lang] || SERVICE_DATA.ru;
  const items = Object.entries(data).map(([slug, d]) => ({
    tag: '§ ' + d.num, title: d.kicker,
    body: d.sub.split('.')[0] + '.',
    to: '/services/' + slug, cta: tx(lang, 'Перейти', 'Open'),
  }));
  return (
    <>
      <PageBreadcrumbs trail={[{ to: '/', label: t.crumb.home }, { label: t.crumb.services }]} />
      <PageHero
        num="01" kicker={tx(lang, 'Практики', 'Practices')}
        title={tx(lang, 'Шесть направлений практики возврата.', 'Six directions of recovery practice.')}
        sub={tx(lang,
          'Каждое направление следует единой методике, основанной на доказательствах, и адаптируется под конкретный финансовый механизм. Дела ведут именованные специалисты от приёма до завершения.',
          'Each direction follows the same evidence-first method, adapted to the specific financial mechanism. Cases stay with named specialists from intake through resolution.')}
      />
      <Section num="01.1"
        kicker={tx(lang, 'Карта направлений', 'Map of directions')}
        title={tx(lang, 'Куда обратиться по типу дела.', 'Where to start, by case type.')}
        intro={tx(lang,
          'Если не уверены, к какому направлению относится ваше дело, начните с разбора через форму контактов — мы определим маршрут.',
          'If unsure which direction fits your case, start with a review via the contact form — we will identify the route.')}>
        <CardGrid items={items} columns={3} />
      </Section>
      <Section num="01.2"
        kicker={tx(lang, 'Единый метод', 'Unified method')}
        title={tx(lang, 'Маршрут одинаков, меняется доказательная база.', 'The route is the same — the evidence changes.')}>
        <NumberedList items={t.method} />
      </Section>
      <CTABlock />
    </>
  );
}

function PracticeDetailPage({ slug }) {
  const { t, lang } = useLang();
  const d = (SERVICE_DATA[lang] || SERVICE_DATA.ru)[slug];
  if (!d) return <NotFoundPage />;
  return (
    <>
      <PageBreadcrumbs trail={[
        { to: '/', label: t.crumb.home },
        { to: '/services', label: t.crumb.services },
        { label: d.kicker },
      ]} />
      <PageHero num={d.num} kicker={d.kicker} title={d.title} sub={d.sub} badges={d.badges} />
      <Section num={d.num + '.1'}
        kicker={tx(lang, 'Метод', 'Method')}
        title={tx(lang, 'Пятиступенчатый маршрут дела.', 'A five-stage case route.')}
        intro={tx(lang,
          'Маршрут постоянен. Меняется доказательная база, контрагент и канал возврата.',
          'The route is constant. What changes is the evidence, the counterparty, and the recovery channel.')}>
        <NumberedList items={d.method} />
      </Section>
      <Section num={d.num + '.2'}
        kicker={tx(lang, 'Доказательная база', 'Evidence base')}
        title={tx(lang, 'Что нужно собрать перед разбором.', 'What to assemble before review.')}
        intro={tx(lang,
          'Чем полнее комплект — тем точнее оценка. Пробелы, как правило, удаётся восполнить совместно на этапе разбора.',
          'The fuller the file, the more accurate the assessment. Gaps are usually filled together during review.')} dense>
        <ul style={{
          margin: 0, padding: 0, listStyle: 'none',
          display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: 0,
          border: `1px solid ${T.hair}`,
        }}>
          {d.evidence.map((e, i) => (
            <li key={i} style={{ padding: '14px 18px', borderBottom: `1px solid ${T.hair}`, fontSize: 14, color: T.body, display: 'flex', gap: 12 }}>
              <span style={{ ...TX.mono, fontSize: 10, color: T.gold, marginTop: 3 }}>{String(i + 1).padStart(2, '0')}</span>
              {e}
            </li>
          ))}
        </ul>
      </Section>
      <CTABlock />
    </>
  );
}

// ─────────────────────────────────────────────────────────────
// /crypto, /forex, /business — pillar pages
// ─────────────────────────────────────────────────────────────
// PILLAR_DATA — overview pages for /crypto, /forex, /business. Same
// dual-language shape as SERVICE_DATA. Card body / cta strings are
// per-language so footer-cards under hero stop showing RU on EN routes.
const PILLAR_DATA = {
  ru: {
    crypto: {
      num: '03', kicker: 'Крипто',
      title: 'Крипто-споры: депозит, кастоди, перемещение, биржа, требование.',
      sub: 'Большинство криптоспоров укладывается в одну форму: бесшовное окно депозита и затруднённое окно вывода. Наша работа — задокументировать этот разрыв, привязать движение средств к KYC-точкам и подать структурированное требование.',
      badges: [
        ['ТЕМАТИКА', 'On-chain трассировка, биржи, KYC'],
        ['ТИПЫ ДЕЛ', 'Скам-платформы, сигнальные группы, поддельные «биржи»'],
        ['КАНАЛЫ', 'Биржевой комплаенс, гражданский путь, регулятор'],
      ],
      cards: [
        { tag: 'Связанная практика', title: 'Возврат криптовалют', body: 'Полный маршрут возврата: трассировка, биржевая привязка, требование.', to: '/services/crypto-recovery', cta: 'Перейти' },
        { tag: 'Связанная практика', title: 'Инвестиционное мошенничество', body: 'Синтетические платформы и схемы «персональных трейдеров» с криптодепозитами.', to: '/services/investment-fraud', cta: 'Перейти' },
        { tag: 'Специалисты', title: 'Хэши транзакций — гид', body: 'Что такое TXID, как читать цепочку и какие данные нужны для трассировки.', to: '/specialists', cta: 'Раздел' },
      ],
      facts: [
        ['Сети', 'BTC, ETH, USDT (TRC20/ERC20), BNB, TRON и др.'],
        ['Вход в KYC', 'Custodial-биржа — точка привязки к личности'],
        ['Не возвращаемое', 'Self-custody без KYC, миксеры, P2P без подписки'],
        ['Время реакции', 'Чем раньше — тем шире коридор вмешательства'],
      ],
    },
    forex: {
      num: '03', kicker: 'Forex',
      title: 'Forex и CFD: депозит, капитал, маржа, вывод, отказ.',
      sub: 'У большинства Forex/CFD-споров повторяющаяся форма. Депозит проходит без трения, после прибыли начинаются «дополнительные KYC», ретроактивные бонусные оговорки и затягивание вывода. Мы фиксируем закономерность и подаём обращения по доступным каналам.',
      badges: [
        ['ТЕМАТИКА', 'Розничные Forex / CFD, бонусные ловушки'],
        ['ТИПЫ ДЕЛ', 'Отказ в выводе, заморозка, ретроактивные T&C'],
        ['КАНАЛЫ', 'Карточный чарджбэк, регулятор, банк'],
      ],
      cards: [
        { tag: 'Связанная практика', title: 'Forex-споры', body: 'Сборка доказательной базы и подача обращений в банк/регулятор.', to: '/services/forex-disputes', cta: 'Перейти' },
        { tag: 'Связанная практика', title: 'Споры по выводу средств', body: 'Целевое вмешательство, когда брокер задерживает или отказывает в выводе.', to: '/services/broker-withdrawal-disputes', cta: 'Перейти' },
        { tag: 'Специалисты', title: 'Признаки брокерского мошенничества', body: 'Какие паттерны должны насторожить ещё до депозита.', to: '/specialists/broker-scam-signs', cta: 'Раздел' },
      ],
      facts: [
        ['Регуляторы', 'AFM, BaFin, FCA, CySEC, CONSOB и др.'],
        ['Карточные сети', 'Visa / Mastercard reason codes'],
        ['Не возвращаемое', 'Криптодепозит без KYC + ушедший на вывод'],
        ['Время реакции', 'Чарджбэк — окна 60–120 дней по картам'],
      ],
    },
    business: {
      num: '03', kicker: 'Бизнес',
      title: 'Корпоративные споры: SWIFT, инвойсы, контрагенты.',
      sub: 'B2B-споры — отдельная категория с собственной доказательной базой и каналами. Перехват SWIFT-перевода (BEC), подмена реквизитов, фиктивные инвойсы и неисполнение обязательств контрагентом ведутся в координации с банками и регуляторами.',
      badges: [
        ['ТЕМАТИКА', 'BEC, фрод по счетам, неисполнение'],
        ['ТИПЫ ДЕЛ', 'Перехват переводов, подменные домены, фиктивные подрядчики'],
        ['КАНАЛЫ', 'Банк-отправитель, банк-получатель, регулятор, civil'],
      ],
      cards: [
        { tag: 'Связанная практика', title: 'B2B финансовые споры', body: 'Реконструкция перевода, recall, регуляторные жалобы и civil-путь.', to: '/services/b2b-disputes', cta: 'Перейти' },
        { tag: 'Связанная практика', title: 'Чарджбэк-сопровождение', body: 'Подготовка по основным кодам карточных сетей и анализ возражений.', to: '/services/chargeback', cta: 'Перейти' },
        { tag: 'Специалисты', title: 'Какие доказательства нужны', body: 'Документы и формат, в котором они должны быть представлены.', to: '/specialists/evidence', cta: 'Раздел' },
      ],
      facts: [
        ['Каналы переводов', 'SWIFT (MT103/MT199), SEPA, FedWire, корреспонденты'],
        ['Сроки recall', 'Эффективны в первые дни после исполнения'],
        ['Доказательная база', 'Полная цепочка e-mail с заголовками, договоры, инвойсы'],
        ['Координация', 'Банк отправителя + банк получателя + регулятор'],
      ],
    },
  },
  en: {
    crypto: {
      num: '03', kicker: 'Crypto',
      title: 'Crypto disputes: deposit, custody, transfer, exchange, claim.',
      sub: 'Most crypto disputes share a single shape: a frictionless deposit window and a difficult withdrawal window. Our work is to document that gap, anchor the movement of funds to KYC checkpoints, and file a structured claim.',
      badges: [
        ['THEME', 'On-chain tracing, exchanges, KYC'],
        ['CASE TYPES', 'Scam platforms, signal groups, fake "exchanges"'],
        ['CHANNELS', 'Exchange compliance, civil track, regulator'],
      ],
      cards: [
        { tag: 'Related practice', title: 'Crypto Recovery', body: 'The full recovery route: tracing, exchange anchoring, the claim.', to: '/services/crypto-recovery', cta: 'Open' },
        { tag: 'Related practice', title: 'Investment Fraud', body: 'Synthetic platforms and "personal trader" schemes with crypto deposits.', to: '/services/investment-fraud', cta: 'Open' },
        { tag: 'Specialists', title: 'Transaction hashes — guide', body: 'What a TXID is, how to read the chain, and what data tracing needs.', to: '/specialists', cta: 'Section' },
      ],
      facts: [
        ['Networks', 'BTC, ETH, USDT (TRC20/ERC20), BNB, TRON and others'],
        ['KYC anchor', 'A custodial exchange ties funds to an identity'],
        ['Not recoverable', 'Self-custody without KYC, mixers, P2P without sign-up'],
        ['Reaction time', 'The earlier — the wider the intervention window'],
      ],
    },
    forex: {
      num: '03', kicker: 'Forex',
      title: 'Forex and CFD: deposit, capital, margin, withdrawal, refusal.',
      sub: 'Most Forex/CFD disputes follow a recurring pattern. The deposit goes through smoothly; once profit appears, "extra KYC", retroactive bonus clauses, and withdrawal stalling kick in. We fix the pattern and file submissions through the available channels.',
      badges: [
        ['THEME', 'Retail Forex / CFD, bonus traps'],
        ['CASE TYPES', 'Withdrawal refusal, account freeze, retroactive T&C'],
        ['CHANNELS', 'Card chargeback, regulator, bank'],
      ],
      cards: [
        { tag: 'Related practice', title: 'Forex disputes', body: 'Evidence assembly and submissions to the bank / regulator.', to: '/services/forex-disputes', cta: 'Open' },
        { tag: 'Related practice', title: 'Withdrawal disputes', body: 'Targeted intervention when a broker stalls or denies a withdrawal.', to: '/services/broker-withdrawal-disputes', cta: 'Open' },
        { tag: 'Specialists', title: 'Broker warning signs', body: 'Patterns that should warn you before deposit.', to: '/specialists/broker-scam-signs', cta: 'Section' },
      ],
      facts: [
        ['Regulators', 'AFM, BaFin, FCA, CySEC, CONSOB and others'],
        ['Card networks', 'Visa / Mastercard reason codes'],
        ['Not recoverable', 'Crypto deposit without KYC, then withdrawn out'],
        ['Reaction time', 'Chargeback — typical 60–120 day windows on cards'],
      ],
    },
    business: {
      num: '03', kicker: 'Business',
      title: 'Corporate disputes: SWIFT, invoices, counterparties.',
      sub: 'B2B disputes are a separate category with their own evidence base and channels. Intercepted SWIFT wires (BEC), substituted payment details, fake invoices, and counterparty non-delivery are handled in coordination with banks and regulators.',
      badges: [
        ['THEME', 'BEC, account fraud, non-delivery'],
        ['CASE TYPES', 'Wire interception, look-alike domains, fake contractors'],
        ['CHANNELS', 'Sender bank, recipient bank, regulator, civil'],
      ],
      cards: [
        { tag: 'Related practice', title: 'B2B Financial Disputes', body: 'Wire reconstruction, recall, regulator complaints and the civil track.', to: '/services/b2b-disputes', cta: 'Open' },
        { tag: 'Related practice', title: 'Chargeback support', body: 'Preparation under major card-network reason codes and rebuttal review.', to: '/services/chargeback', cta: 'Open' },
        { tag: 'Specialists', title: 'Evidence standards', body: 'Documents and the format they should be submitted in.', to: '/specialists/evidence', cta: 'Section' },
      ],
      facts: [
        ['Wire channels', 'SWIFT (MT103/MT199), SEPA, FedWire, correspondents'],
        ['Recall windows', 'Most effective in the first days after execution'],
        ['Evidence base', 'Full email chain with headers, contracts, invoices'],
        ['Coordination', 'Sender bank + recipient bank + regulator'],
      ],
    },
  },
};

function PillarPageX({ slug }) {
  const { t, lang } = useLang();
  const d = (PILLAR_DATA[lang] || PILLAR_DATA.ru)[slug];
  if (!d) return <NotFoundPage />;
  return (
    <>
      <PageBreadcrumbs trail={[{ to: '/', label: t.crumb.home }, { label: t.crumb[slug] }]} />
      <PageHero num={d.num} kicker={d.kicker} title={d.title} sub={d.sub} badges={d.badges} />
      <Section num={d.num + '.1'}
        kicker={tx(lang, 'Связанные направления', 'Related directions')}
        title={tx(lang, 'Куда дальше.', 'Where next.')}
        intro={tx(lang,
          'Эти направления чаще всего применяются к делам этой темы.',
          'These directions most often apply to cases in this area.')}>
        <CardGrid items={d.cards} columns={3} />
      </Section>
      <Section num={d.num + '.2'}
        kicker={tx(lang, 'Опорные факты', 'Key facts')}
        title={tx(lang, 'Что важно знать на старте.', 'What to know at the start.')} dark>
        <div style={{
          display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))',
          borderTop: `1px solid rgba(255,255,255,0.16)`,
        }}>
          {d.facts.map(([k, v], i) => (
            <Reveal key={i} delay={i * 60}>
              <div style={{
                padding: '22px 26px', borderRight: `1px solid rgba(255,255,255,0.16)`,
                borderBottom: `1px solid rgba(255,255,255,0.16)`,
              }}>
                <div style={{ ...TX.mono, fontSize: 9, color: T.gold, marginBottom: 8 }}>{k.toUpperCase()}</div>
                <div style={{ fontSize: 15, color: 'rgba(255,255,255,0.86)', lineHeight: 1.5 }}>{v}</div>
              </div>
            </Reveal>
          ))}
        </div>
      </Section>
      <CTABlock />
    </>
  );
}

// ─────────────────────────────────────────────────────────────
// /about
// ─────────────────────────────────────────────────────────────
function AboutPageX() {
  const { t, lang } = useLang();
  return (
    <>
      <PageBreadcrumbs trail={[{ to: '/', label: t.crumb.home }, { label: t.crumb.about }]} />
      <PageHero
        num="06" kicker={tx(lang, 'О компании', 'About')}
        title={tx(lang,
          'De Vries & Partners — структурированная практика возврата.',
          'De Vries & Partners — a structured recovery practice.')}
        sub={tx(lang,
          'Зарегистрированы в Нидерландах с 2012 года. Hoofdvestiging — Waspik, Benedenkerkstraat 1. KVK 27164831. Практика построена вокруг международных финансовых споров и единого подхода, основанного на доказательствах.',
          'Registered in the Netherlands since 2012. Head office at Waspik, Benedenkerkstraat 1. KVK 27164831. The practice is built around international financial disputes and a single evidence-first method.')}
        badges={[
          [tx(lang, 'ЮРИД. НАИМЕНОВАНИЕ', 'LEGAL NAME'), CONTACT.legalName],
          [tx(lang, 'ТИП', 'TYPE'), 'Hoofdvestiging'],
          [tx(lang, 'АДРЕС', 'ADDRESS'), CONTACT.address],
          ['KVK', CONTACT.kvk],
          [tx(lang, 'РЕГИСТРАЦИЯ', 'REGISTERED'), lang === 'en' ? CONTACT.registeredEn : CONTACT.registered],
        ]}
      />
      <Section num="06.1"
        kicker={tx(lang, 'Опорные принципы', 'Core principles')}
        title={tx(lang, 'Три рамки, в которых ведётся практика.', 'Three frames the practice operates within.')}
        intro={tx(lang,
          'Принципы — не декларации, а правила, по которым проверяется любая стадия дела.',
          'Principles are not declarations but rules a case can be checked against at any stage.')}>
        <CardGrid columns={3} items={t.about.pillars.map(([k, v]) => ({ tag: k, title: k, body: v }))} />
      </Section>
      <Section num="06.2"
        kicker={tx(lang, 'Регистрация', 'Registration')}
        title={tx(lang, 'Открытые данные о компании.', 'Open company data.')}
        intro={tx(lang,
          'Все сведения соответствуют записи в Торговом реестре Нидерландов.',
          'Every entry corresponds to the record in the Dutch Trade Register.')} dense>
        <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr)', border: `1px solid ${T.hair}` }}>
          {t.about.ledger.map(([k, v], i) => (
            <div key={i} style={{
              display: 'grid', gridTemplateColumns: '240px 1fr', gap: 16,
              padding: '14px 22px', borderBottom: `1px solid ${T.hair}`,
              fontSize: 14, color: T.body,
            }}>
              <span style={{ ...TX.mono, fontSize: 10, color: T.mute }}>{k.toUpperCase()}</span>
              <span style={{ color: T.ink }}>{v}</span>
            </div>
          ))}
        </div>
      </Section>
      <Section num="06.3"
        kicker={tx(lang, 'Почему клиенты выбирают нас', 'Why clients choose us')}
        title={tx(lang, 'Шесть оснований для обращения.', 'Six grounds for engagement.')}
        intro={tx(lang,
          'Каждое основание — рабочее правило, не маркетинговое заявление.',
          'Each one is an operating rule, not a marketing claim.')}>
        <NumberedList items={t.why} />
      </Section>
      <Section num="06.4"
        kicker={tx(lang, 'Документация', 'Documentation')}
        title={tx(lang, 'Документы и политики компании.', 'Company documents and policies.')}
        intro={tx(lang,
          'Раздел документации перенесён сюда: вся открытая правовая часть в одном месте, без отдельной вкладки в шапке.',
          'Documentation now lives here: the open legal materials are in one place, no separate header tab.')} dense>
        <CardGrid columns={2} items={DOCS_DROP.map((d) => ({
          tag: tx(lang, 'Документ', 'Document'),
          title: navLabel(d, lang),
          body: tx(lang, 'Открытый материал · ссылка на раздел', 'Open material · link to section'),
          to: d.to,
          cta: tx(lang, 'Открыть', 'Open'),
        }))} />
      </Section>
      <CTABlock />
    </>
  );
}

// ─────────────────────────────────────────────────────────────
// /specialists — team grid + open materials
// ─────────────────────────────────────────────────────────────
// One card per specialist: portrait, name, role, country, brief.
// Native <details> handles the expandable mini-portfolio so the
// browser owns toggling state (keyboard accessible, doesn't interact
// with React layout, doesn't cut scroll). VIPs get a gold border, a
// soft gold-tinted shadow and a small "TOP EXPERT" pill — premium
// without being loud.
function SpecialistCard({ m, i }) {
  const { lang } = useLang();
  const more   = tx(lang, 'Подробнее', 'More details');
  const less   = tx(lang, 'Свернуть', 'Hide');
  const vipLbl = tx(lang, 'ТОП-СПЕЦИАЛИСТ', 'TOP EXPERT');
  return (
    <details className="dvp-spec-card" style={{
      background: T.white,
      border: m.vip ? `2px solid ${T.gold}` : `1px solid ${T.hair}`,
      boxShadow: m.vip ? '0 22px 48px rgba(164,136,79,0.14)' : 'none',
      position: 'relative',
      transition: 'box-shadow .25s ease, transform .25s ease',
    }}>
      {m.vip && (
        <div aria-label={vipLbl} style={{
          position: 'absolute', top: 12, right: 12, zIndex: 2,
          background: T.gold, color: T.ink,
          padding: '5px 10px',
          ...TX.mono, fontSize: 9, letterSpacing: '0.14em',
          boxShadow: '0 4px 12px rgba(164,136,79,0.32)',
        }}>{vipLbl}</div>
      )}
      <summary style={{ cursor: 'pointer', display: 'block', padding: 0, listStyle: 'none' }}>
        <Portrait gender={m.gender} initials={m.initials} i={i} accentBg={m.vip} photo={m.photo} />
        <div style={{ padding: '18px 18px 14px', borderTop: `1px solid ${T.hair}` }}>
          <div style={{ ...TX.mono, fontSize: 10, color: T.gold, marginBottom: 6, letterSpacing: '0.12em' }}>
            {m.country}
          </div>
          <div style={{ fontSize: 18, fontWeight: 600, letterSpacing: '-0.015em', lineHeight: 1.25, color: T.ink }}>
            {m.name}
          </div>
          <div style={{ fontSize: 13, color: T.ink, marginTop: 6 }}>{m.role}</div>
          <div style={{ fontSize: 13, color: T.mute, lineHeight: 1.55, marginTop: 8 }}>{m.brief}</div>
          <div className="dvp-spec-toggle" style={{
            ...TX.mono, fontSize: 9, color: T.gold, marginTop: 14, letterSpacing: '0.12em',
          }}>
            <span data-more>{more} →</span>
            <span data-less style={{ display: 'none' }}>{less} ↑</span>
          </div>
        </div>
      </summary>
      <div style={{
        padding: '0 18px 22px',
        fontSize: 13, color: T.body, lineHeight: 1.65,
        borderTop: `1px dashed ${T.hair}`,
      }}>
        <div style={{ paddingTop: 14 }}>{m.portfolio}</div>
      </div>
    </details>
  );
}

function TeamGrid({ items }) {
  return (
    <div style={{
      display: 'grid',
      gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))',
      gap: 18,
    }}>
      {items.map((m, i) => <SpecialistCard key={m.name} m={m} i={i} />)}
    </div>
  );
}

function SpecialistsIndexPage() {
  const { t, lang } = useLang();
  // Landing on /specialists ("Команда" menu item) should reveal the
  // team grid first — the user clicked the team tab, so put photos
  // in front of them. Hero stays mounted (one quick scroll back to
  // see context). The 80 ms delay lets the new route's render settle
  // after the global scroll-to-top in useRoute.
  React.useEffect(() => {
    const id = setTimeout(() => {
      const el = document.getElementById('team-section');
      if (el && typeof el.scrollIntoView === 'function') {
        el.scrollIntoView({ block: 'start', behavior: 'auto' });
      }
    }, 80);
    return () => clearTimeout(id);
  }, []);
  const open = tx(lang, 'Открыть', 'Open');
  const go = tx(lang, 'Перейти', 'Open');
  const items = lang === 'en' ? [
    { tag: 'Guide',     title: 'How recovery works',         body: 'What happens between intake and closure: five steps in detail.', to: '/specialists/how-recovery-works', cta: open },
    { tag: 'Standards', title: 'Evidence standards',         body: 'The documents we analyse, and the format in which they should be prepared.', to: '/specialists/evidence', cta: open },
    { tag: 'Pre-check', title: 'Broker warning signs',       body: 'Patterns that should warn before deposit and during the first days after.', to: '/specialists/broker-scam-signs', cta: open },
    { tag: 'FAQ',       title: 'Common questions',           body: 'Direct answers, no overselling.', to: '/faq', cta: go },
  ] : [
    { tag: 'Гид',       title: 'Как работает возврат',       body: 'Что происходит между заявкой и закрытием дела: пять шагов в деталях.', to: '/specialists/how-recovery-works', cta: open },
    { tag: 'Стандарты', title: 'Какие доказательства нужны', body: 'Документы, которые мы анализируем, и формат, в котором их стоит подготовить.', to: '/specialists/evidence', cta: open },
    { tag: 'Пред-чек',  title: 'Признаки брокерского мошенничества', body: 'Паттерны, которые должны насторожить ещё до депозита и в первые дни после.', to: '/specialists/broker-scam-signs', cta: open },
    { tag: 'FAQ',       title: 'Частые вопросы',             body: 'Прямые ответы — без приукрашивания.', to: '/faq', cta: go },
  ];
  const vipCount = (t.team || []).filter(m => m.vip).length;
  const totalCount = (t.team || []).length;
  return (
    <>
      <PageBreadcrumbs trail={[{ to: '/', label: t.crumb.home }, { label: t.crumb.specialists }]} />
      <PageHero
        num="07" kicker={tx(lang, 'Специалисты', 'Specialists')}
        title={tx(lang, 'Команда специалистов и открытые материалы.', 'The specialist team and open materials.')}
        sub={tx(lang,
          'Дела закрепляются за конкретными людьми от приёма до закрытия. Ниже — состав команды и открытая часть знаний, которые специалисты используют в работе.',
          'Cases stay with named people from intake through closure. Below — the team and the open part of the knowledge they use on cases.')}
      />
      <div id="team-section">
        <Section num="07.1"
          kicker={tx(lang, 'Команда', 'Team')}
          title={tx(lang, `${totalCount} специалистов · ${vipCount} ведущих экспертов.`, `${totalCount} specialists · ${vipCount} top experts.`)}
          intro={tx(lang,
            'Каждое дело закрепляется за конкретным специалистом от приёма до закрытия. Раскройте карточку, чтобы увидеть короткое мини-досье.',
            'Each case is owned by a named specialist from intake through closure. Expand a card to see a short mini-dossier.')}>
          <TeamGrid items={t.team} />
        </Section>
      </div>
      <Section num="07.2"
        kicker={tx(lang, 'Разделы', 'Sections')}
        title={tx(lang, 'Открытые материалы.', 'Open materials.')}
        intro={tx(lang,
          'Если не знаете, с чего начать — раздел «Как работает возврат» даёт общий маршрут.',
          'If you are unsure where to begin, "How recovery works" gives the overall route.')}>
        <CardGrid items={items} columns={2} />
      </Section>
      <CTABlock />
    </>
  );
}

// ─────────────────────────────────────────────────────────────
// /specialists/* articles
// ─────────────────────────────────────────────────────────────
// ARTICLE_DATA — long-form copy for /specialists/<slug>. Same dual-
// language shape so the EN visitor doesn't see RU prose.
const ARTICLE_DATA = {
  ru: {
    'how-recovery-works': {
      num: '07.1', kicker: 'Гид',
      title: 'Как работает возврат: пять шагов в деталях.',
      sub: 'Маршрут возврата един для всех дел — меняется доказательная база и канал подачи. Этот раздел разбирает каждый шаг по существу, без обещаний результата.',
      blocks: [
        ['I — Конфиденциальный приём',
          'Первичная заявка через защищённую форму. Документы хранятся под контролем доступа и доступны только закреплённым специалистам. На этом шаге мы не оцениваем шансы — только собираем материалы.'],
        ['II — Разбор дела',
          'Специалист изучает материалы и формирует структурированную оценку реалистичных путей возврата. Если путь не виден — это будет сказано прямо. Никаких «да, обязательно вернём».'],
        ['III — Заключение соглашения',
          'Условия фиксируются письменно: стоимость, этапы, пороги, основания для прекращения. До подписания — никаких работ за счёт клиента.'],
        ['IV — Сборка и подача',
          'Хронология, выписки, переписка и транзакционные данные приводятся к подаче. Канал выбирается под дело: чарджбэк, регулятор, биржа, гражданский путь.'],
        ['V — Сопровождение и закрытие',
          'Контроль ответов контрагентов. Переключение канала, если путь закрылся. Письменное закрытие с фиксацией результата и передачей материалов клиенту.'],
      ],
    },
    'evidence': {
      num: '07.2', kicker: 'Стандарты',
      title: 'Какие доказательства нужны и как их готовить.',
      sub: 'Полнота комплекта определяет точность оценки и шансы по делу. Этот раздел перечисляет типовые документы по основным направлениям и объясняет требования к их формату.',
      blocks: [
        ['Карточные дела (чарджбэк)',
          'Выписка по карте за период, договор/оферта мерчанта, переписка с поддержкой, скриншоты транзакции. Файлы — PDF/PNG, без редактирования. Дата каждого документа важна.'],
        ['Forex / CFD-споры',
          'Скриншоты дашборда и тикетов, бонусные T&C на момент депозита, KYC-формы, переписка с менеджером, выписки по способам пополнения и попыткам вывода.'],
        ['Криптодела',
          'Адреса кошельков (отправитель и получатель), TXID каждой транзакции, скриншоты платформы и баланса, чаты с «советником», подтверждения переводов из обменника или банка.'],
        ['B2B-споры',
          'SWIFT-копии (MT103/MT199), полные e-mail с заголовками, инвойсы, договоры, документы KYC контрагента, внутренние процедуры платежей (если применимо).'],
        ['Общие требования',
          'Файлы — оригиналы или сканы без правок. Скриншоты — со временем и URL. Желательно — сводная хронология в свободной форме (что и когда произошло).'],
      ],
    },
    'broker-scam-signs': {
      num: '07.3', kicker: 'Пред-чек',
      title: 'Признаки брокерского мошенничества.',
      sub: 'Эти паттерны не доказывают мошенничество сами по себе, но в совокупности дают повод остановиться и проверить лицензию, юрисдикцию и историю платформы перед депозитом.',
      blocks: [
        ['Давление на скорость',
          'Менеджер настаивает на немедленном депозите «пока не закрылось окно сделки». Лицензированный брокер не торопит клиента и не зависит от его сегодняшнего пополнения.'],
        ['Бонусы при депозите',
          'Любой бонус, который «привязывает» средства до выполнения объёма торгов, — повод изучить условия. Часто бонусные T&C ретроактивно блокируют вывод.'],
        ['Личный «трейдер» / «советник»',
          'Сторонний человек, который торгует за вас или подсказывает сделки в Telegram/WhatsApp. Это не лицензированная услуга и почти всегда — часть схемы.'],
        ['Прибыль до KYC',
          'Платформа позволяет торговать без полного KYC, а при попытке вывода требует «довести верификацию». Это типичный приём затягивания.'],
        ['Регуляторные ярлыки без реквизитов',
          'Логотипы FCA/CySEC/AFM на сайте без номера лицензии и юрлица — повод проверить регулятор напрямую. Реальная лицензия привязана к конкретному юрлицу.'],
        ['Невозможность связаться вне платформы',
          'Только чат, только тикеты, никаких юридических реквизитов в подвале сайта. Лицензированный брокер обязан раскрывать юрлицо и адрес.'],
      ],
    },
  },
  en: {
    'how-recovery-works': {
      num: '07.1', kicker: 'Guide',
      title: 'How recovery works: five steps in detail.',
      sub: 'The recovery route is the same for every case — what changes is the evidence base and the submission channel. This section walks through each step on the merits, with no result promises.',
      blocks: [
        ['I — Confidential intake',
          'Initial submission through a secure form. Files are kept under access controls and visible only to assigned specialists. We do not assess chances at this step — only gather materials.'],
        ['II — Case review',
          'A specialist reviews the file and frames a structured assessment of realistic recovery paths. If a path is not visible, we say so directly. No "yes, you will definitely get it back".'],
        ['III — Engagement letter',
          'Terms are set out in writing: cost, milestones, thresholds, termination grounds. No work is done at the client’s expense before signing.'],
        ['IV — Assembly and submission',
          'Timelines, statements, correspondence, and transaction data are brought to a submission-ready state. The channel is selected per case: chargeback, regulator, exchange, civil.'],
        ['V — Follow-up and closure',
          'Counterparty responses are tracked. Channel switching if a path closes. Written closure with the outcome recorded and case materials returned to the client.'],
      ],
    },
    'evidence': {
      num: '07.2', kicker: 'Standards',
      title: 'Which evidence is needed and how to prepare it.',
      sub: 'How complete the file is determines the accuracy of assessment and the case chances. This section lists typical documents for each direction and explains formatting requirements.',
      blocks: [
        ['Card cases (chargeback)',
          'Card statement for the period, merchant contract / offer, correspondence with support, transaction screenshots. Files in PDF / PNG, unedited. The date of each document matters.'],
        ['Forex / CFD disputes',
          'Dashboard and ticket screenshots, bonus T&Cs as of deposit time, KYC forms, manager correspondence, statements covering deposit methods and withdrawal attempts.'],
        ['Crypto cases',
          'Wallet addresses (sender and receiver), TXIDs for every transaction, platform and balance screenshots, chats with the "advisor", transfer confirmations from the exchanger or bank.'],
        ['B2B disputes',
          'SWIFT copies (MT103/MT199), full emails with headers, invoices, contracts, counterparty KYC documents, internal payment procedures (where applicable).'],
        ['General requirements',
          'Files in originals or unedited scans. Screenshots with timestamps and URLs. A free-form summary timeline (what happened and when) is welcome.'],
      ],
    },
    'broker-scam-signs': {
      num: '07.3', kicker: 'Pre-check',
      title: 'Broker warning signs.',
      sub: 'These patterns do not prove fraud on their own, but together they are reason enough to pause and check the licence, jurisdiction, and history of the platform before depositing.',
      blocks: [
        ['Pressure on speed',
          'A manager insists on an immediate deposit "before the trade window closes". A licensed broker does not rush the client and does not depend on today’s top-up.'],
        ['Bonuses on deposit',
          'Any bonus that "ties" funds until a trading-volume target is met is reason to study the terms. Bonus T&Cs often retroactively block withdrawals.'],
        ['Personal "trader" / "advisor"',
          'A third party who trades on your behalf or feeds you trades on Telegram / WhatsApp. This is not a licensed service and is almost always part of the scheme.'],
        ['Profit before KYC',
          'The platform lets you trade without full KYC, then on withdrawal demands "complete verification". This is a textbook stalling tactic.'],
        ['Regulator labels without details',
          'FCA / CySEC / AFM logos on the site with no licence number or legal entity — reason to verify with the regulator directly. A real licence is tied to a specific entity.'],
        ['No off-platform contact',
          'Chat-only, tickets-only, no legal details in the site footer. A licensed broker is required to disclose the legal entity and address.'],
      ],
    },
  },
};

function SpecialistsArticlePage({ slug }) {
  const { t, lang } = useLang();
  const d = (ARTICLE_DATA[lang] || ARTICLE_DATA.ru)[slug];
  if (!d) return <NotFoundPage />;
  return (
    <>
      <PageBreadcrumbs trail={[
        { to: '/', label: t.crumb.home },
        { to: '/specialists', label: t.crumb.specialists },
        { label: d.kicker },
      ]} />
      <PageHero num={d.num} kicker={d.kicker} title={d.title} sub={d.sub} />
      <Section num={d.num + '.1'}
        kicker={tx(lang, 'По шагам', 'Step by step')}
        title={tx(lang, 'Что важно знать.', 'What to know.')}>
        <div style={{ display: 'grid', gap: 0, border: `1px solid ${T.hair}` }}>
          {d.blocks.map(([k, v], i) => (
            <Reveal key={i} delay={i * 60}>
              <div style={{
                display: 'grid', gridTemplateColumns: 'minmax(0, 1fr)', gap: 8,
                padding: '20px 24px', borderBottom: `1px solid ${T.hair}`,
              }}>
                <div style={{ ...TX.mono, fontSize: 10, color: T.gold }}>{k.toUpperCase()}</div>
                <div style={{ fontSize: 15, color: T.body, lineHeight: 1.6 }}>{v}</div>
              </div>
            </Reveal>
          ))}
        </div>
      </Section>
      <CTABlock />
    </>
  );
}

// ─────────────────────────────────────────────────────────────
// /faq
// ─────────────────────────────────────────────────────────────
function FaqPage() {
  const { t, lang } = useLang();
  const isDesktop = useIsDesktop();
  const [openIdx, setOpenIdx] = React.useState(0);
  return (
    <>
      <PageBreadcrumbs trail={[{ to: '/', label: t.crumb.home }, { label: t.crumb.faq }]} />
      <PageHero
        num="08" kicker={tx(lang, 'Частые вопросы', 'Common questions')}
        title={t.sec.faq.title}
        sub={t.sec.faq.intro}
      />
      <Section num="08.1" kicker="FAQ"
        title={tx(lang, 'Прямые ответы.', 'Direct answers.')}
        intro={tx(lang,
          'Если вашего вопроса здесь нет — напишите. Мы ответим напрямую, в том числе когда ответ — «нет».',
          'If your question is not here, write to us. We respond directly, even when the answer is no.')}>
        <div style={{ border: `1px solid ${T.hair}` }}>
          {t.faq.map(([q, a], i) => (
            <div key={i} style={{ borderBottom: `1px solid ${T.hair}` }}>
              <button onClick={() => setOpenIdx(openIdx === i ? -1 : i)} style={{
                width: '100%', textAlign: 'left', padding: isDesktop ? '20px 28px' : '16px 18px',
                background: 'transparent', border: 'none', cursor: 'pointer',
                display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 16,
                fontFamily: 'inherit', color: T.ink, fontSize: isDesktop ? 17 : 15, fontWeight: 500,
              }}>
                <span style={{ display: 'flex', gap: 16, alignItems: 'baseline' }}>
                  <span style={{ ...TX.mono, fontSize: 10, color: T.gold }}>{String(i + 1).padStart(2, '0')}</span>
                  {q}
                </span>
                <span style={{ color: T.gold, ...TX.mono, fontSize: 14 }}>{openIdx === i ? '−' : '+'}</span>
              </button>
              {openIdx === i && (
                <div style={{
                  padding: isDesktop ? '0 28px 22px 70px' : '0 18px 18px 18px',
                  fontSize: 15, color: T.body, lineHeight: 1.6,
                }}>{a}</div>
              )}
            </div>
          ))}
        </div>
      </Section>
      <CTABlock />
    </>
  );
}

// ─────────────────────────────────────────────────────────────
// /contact
// ─────────────────────────────────────────────────────────────
function ContactPageX() {
  const { t, lang } = useLang();
  const isDesktop = useIsDesktop();
  const fl = t.formLabels;
  const { form, setField, errors, status, serverError, submit } = useLeadForm({ type: fl.types[0] });
  const fld = setField;
  const sending = status === 'sending';
  const sent    = status === 'sent';

  const c = t.contact;
  return (
    <>
      <PageBreadcrumbs trail={[{ to: '/', label: t.crumb.home }, { label: t.crumb.contact }]} />
      <PageHero
        num="08" kicker={c.kicker || 'Контакты'}
        title={c.title || t.secX.contact.title}
        sub={c.sub || t.secX.contact.intro}
        badges={[
          [lang === 'en' ? 'ADDRESS' : 'АДРЕС', CONTACT.address],
          [lang === 'en' ? 'COUNTRY' : 'СТРАНА', lang === 'en' ? CONTACT.countryEn : CONTACT.country],
          ['KVK', CONTACT.kvk],
          [(c.hoursLabel || 'Часы').toUpperCase(), c.hours],
          ['E-MAIL', CONTACT.email],
        ]}
      />
      <Section num="08.1" kicker={c.channelsTitle || 'Способы связи'} title={c.title || 'Несколько каналов на выбор.'} intro={c.replyNote || 'Все обращения конфиденциальны. Первый отклик — в течение двух рабочих дней по делу.'}>
        <div style={{
          display: 'grid', gridTemplateColumns: isDesktop ? 'repeat(3, 1fr)' : '1fr',
          border: `1px solid ${T.hair}`, marginBottom: isDesktop ? 18 : 14,
        }}>
          {[
            { label: c.tgLabel || 'Telegram',  href: CONTACT.telegram, sub: CONTACT.telegramText, icon: '✈' },
            { label: c.waLabel || 'WhatsApp',  href: CONTACT.whatsapp, sub: CONTACT.whatsappText, icon: '✆' },
            { label: c.mailLabel || 'E-mail',  href: CONTACT.emailHref, sub: CONTACT.email,        icon: '✉' },
          ].map((ch, i) => (
            <a key={i} href={ch.href} target={ch.href.startsWith('mailto:') ? undefined : '_blank'} rel="noopener noreferrer" style={{
              display: 'grid', gridTemplateColumns: '40px 1fr 22px', alignItems: 'center', gap: 14,
              padding: '22px 24px',
              borderRight: isDesktop && i < 2 ? `1px solid ${T.hair}` : 'none',
              borderBottom: !isDesktop && i < 2 ? `1px solid ${T.hair}` : 'none',
              color: T.ink, textDecoration: 'none',
              transition: 'background .15s ease',
            }} onMouseEnter={(e) => { e.currentTarget.style.background = T.paper; }}
               onMouseLeave={(e) => { e.currentTarget.style.background = 'transparent'; }}>
              <span aria-hidden style={{
                width: 38, height: 38, border: `1px solid ${T.gold}`, transform: 'rotate(45deg)',
                display: 'flex', alignItems: 'center', justifyContent: 'center', color: T.gold,
              }}><span style={{ transform: 'rotate(-45deg)', fontSize: 14 }}>{ch.icon}</span></span>
              <div style={{ overflow: 'hidden' }}>
                <div style={{ fontSize: 15, fontWeight: 600, color: T.ink, letterSpacing: '-0.01em' }}>{ch.label}</div>
                <div style={{ fontSize: 12, color: T.mute, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{ch.sub}</div>
              </div>
              <span style={{ color: T.gold, fontSize: 18 }}>→</span>
            </a>
          ))}
        </div>
        <div style={{
          display: 'grid', gridTemplateColumns: isDesktop ? 'repeat(3, 1fr)' : '1fr',
          border: `1px solid ${T.hair}`,
        }}>
          {t.contact.blocks.map(([k, v], i) => (
            <div key={i} style={{
              padding: '24px 28px',
              borderRight: isDesktop && i < 2 ? `1px solid ${T.hair}` : 'none',
              borderBottom: `1px solid ${T.hair}`,
            }}>
              <div style={{ ...TX.mono, fontSize: 10, color: T.gold, marginBottom: 10 }}>{k.toUpperCase()}</div>
              <div style={{ fontSize: 14, color: T.ink, whiteSpace: 'pre-line', lineHeight: 1.6 }}>{v}</div>
            </div>
          ))}
        </div>
      </Section>
      <Section num="08.2"
        kicker={tx(lang, 'Форма приёма дела', 'Case-intake form')}
        title={tx(lang, 'Конфиденциальный разбор.', 'Confidential review.')}
        intro={tx(lang,
          'Несколько строк о деле и контактные данные. Специалист ответит в течение двух рабочих дней.',
          'A few lines about the case and your contact details. A specialist will respond within two business days.')}>
        {sent ? (
          <div style={{ background: T.paper, padding: '28px 28px', border: `1px solid ${T.hair}`, maxWidth: 720 }}>
            <div style={{ ...TX.mono, fontSize: 10, color: T.gold, marginBottom: 8 }}>{fl.sentMark}</div>
            <div style={{ fontSize: 18, fontWeight: 600, marginBottom: 8, color: T.ink }}>{fl.sentTitle}</div>
            <div style={{ fontSize: 14, color: T.body, lineHeight: 1.6 }}>{fl.sentBody}</div>
          </div>
        ) : (
          <form onSubmit={submit} noValidate style={{
            display: 'grid', gridTemplateColumns: isDesktop ? 'repeat(2, 1fr)' : '1fr',
            gap: 0, border: `1px solid ${T.hair}`,
          }} aria-busy={sending}>
            {[
              ['name',   fl.name,    fl.placeholders.name,    'text',  true],
              ['phone',  fl.phone,   fl.placeholders.phone,   'tel',   true],
              ['email',  fl.email,   fl.placeholders.email,   'email', false],
              ['country',fl.country, fl.placeholders.country, 'text',  false],
              ['amount', fl.amount,  fl.placeholders.amount,  'text',  false],
            ].map(([k, label, ph, type, required], i) => (
              <label key={k} style={{
                display: 'grid', gap: 4, padding: '14px 22px',
                borderBottom: `1px solid ${T.hair}`,
                borderRight: isDesktop && i % 2 === 0 ? `1px solid ${T.hair}` : 'none',
                background: errors[k] ? 'rgba(140,58,46,0.04)' : 'transparent',
              }}>
                <span style={{ ...TX.mono, fontSize: 9, color: T.mute, display: 'flex', justifyContent: 'space-between', gap: 8 }}>
                  <span>{label.toUpperCase()}</span>
                  <span style={{ color: required ? T.gold : T.hair2 }}>{(required ? fl.required : fl.optional).toUpperCase()}</span>
                </span>
                <input
                  type={type} value={form[k] || ''} onChange={(e) => fld(k, e.target.value)} placeholder={ph}
                  required={required} disabled={sending}
                  aria-invalid={errors[k] ? 'true' : 'false'}
                  autoComplete={k === 'name' ? 'name' : k === 'email' ? 'email' : k === 'phone' ? 'tel' : 'off'}
                  style={{
                    border: 'none', outline: 'none', fontSize: 16, fontFamily: 'inherit',
                    padding: '8px 0', background: 'transparent',
                    color: errors[k] ? T.brick || '#8C3A2E' : T.ink,
                  }}
                />
                {errors[k] && (
                  <span role="alert" style={{ ...TX.mono, fontSize: 9, color: '#8C3A2E', letterSpacing: '0.08em' }}>
                    {fl.errors[k] || fl.errors.validation}
                  </span>
                )}
              </label>
            ))}
            <label style={{ display: 'grid', gap: 6, padding: '14px 22px', borderBottom: `1px solid ${T.hair}`, gridColumn: isDesktop ? '1 / 3' : '1' }}>
              <span style={{ ...TX.mono, fontSize: 9, color: T.mute }}>{fl.type.toUpperCase()}</span>
              <select value={form.type} onChange={(e) => fld('type', e.target.value)} disabled={sending}
                style={{ border: 'none', outline: 'none', fontSize: 16, fontFamily: 'inherit', padding: '8px 0', background: 'transparent', color: T.ink }}>
                {fl.types.map((typ) => <option key={typ} value={typ}>{typ}</option>)}
              </select>
            </label>
            <label style={{
              display: 'grid', gap: 4, padding: '14px 22px', borderBottom: `1px solid ${T.hair}`,
              gridColumn: isDesktop ? '1 / 3' : '1',
              background: errors.desc ? 'rgba(140,58,46,0.04)' : 'transparent',
            }}>
              <span style={{ ...TX.mono, fontSize: 9, color: T.mute, display: 'flex', justifyContent: 'space-between', gap: 8 }}>
                <span>{fl.desc.toUpperCase()}</span>
                <span style={{ color: T.gold }}>{fl.required.toUpperCase()}</span>
              </span>
              <textarea value={form.desc} onChange={(e) => fld('desc', e.target.value)} placeholder={fl.placeholders.desc} rows={5}
                required disabled={sending}
                aria-invalid={errors.desc ? 'true' : 'false'}
                style={{
                  border: 'none', outline: 'none', fontSize: 16, fontFamily: 'inherit',
                  padding: '8px 0', background: 'transparent',
                  color: errors.desc ? '#8C3A2E' : T.ink, resize: 'vertical',
                }} />
              {errors.desc && (
                <span role="alert" style={{ ...TX.mono, fontSize: 9, color: '#8C3A2E', letterSpacing: '0.08em' }}>
                  {fl.errors.desc}
                </span>
              )}
            </label>
            {/* Honeypot — visually hidden, screen-reader hidden, autocomplete off. */}
            <label aria-hidden="true" style={{ position: 'absolute', left: '-9999px', width: 1, height: 1, overflow: 'hidden' }}>
              {fl.honey.label}
              <input type="text" tabIndex={-1} autoComplete="off"
                value={form.company || ''} onChange={(e) => fld('company', e.target.value)}
                placeholder={fl.honey.placeholder} />
            </label>
            {serverError && status === 'error' && (
              <div role="alert" style={{
                gridColumn: isDesktop ? '1 / 3' : '1', padding: '12px 22px',
                background: 'rgba(140,58,46,0.06)', borderBottom: `1px solid ${T.hair}`,
                fontSize: 13, color: '#8C3A2E',
              }}>
                {fl.errors[serverError] || fl.errors.server}
              </div>
            )}
            <div style={{ gridColumn: isDesktop ? '1 / 3' : '1', padding: '20px 22px', display: 'flex', flexDirection: isDesktop ? 'row' : 'column', justifyContent: 'space-between', gap: 18, alignItems: isDesktop ? 'center' : 'flex-start' }}>
              <span style={{ fontSize: 12, color: T.mute, maxWidth: 560, lineHeight: 1.5 }}>{fl.legal}</span>
              <button type="submit" disabled={sending} style={{
                background: sending ? T.mute : T.ink, color: T.white, border: 'none',
                padding: '16px 26px', ...TX.mono, fontSize: 12, letterSpacing: '0.08em',
                cursor: sending ? 'wait' : 'pointer', fontFamily: 'inherit',
                transition: 'background .15s ease',
              }}>{sending ? fl.sending : (status === 'error' ? fl.retry : fl.submit)}</button>
            </div>
          </form>
        )}
      </Section>
    </>
  );
}

// ─────────────────────────────────────────────────────────────
// /privacy /terms /security — institutional placeholders
// ─────────────────────────────────────────────────────────────
function LegalPage({ slug }) {
  const { t } = useLang();
  const d = t.legalPages[slug];
  if (!d) return <NotFoundPage />;
  const num = slug === 'privacy' ? '10' : slug === 'terms' ? '11' : '12';
  return (
    <>
      <PageBreadcrumbs trail={[{ to: '/', label: t.crumb.home }, { label: t.crumb[slug] }]} />
      <PageHero num={num} kicker={d.kicker} title={d.title} sub={d.sub} />
      <Section num={num + '.1'} kicker={d.kicker} title={d.title.replace(/\.$/, '')}>
        <div style={{ display: 'grid', gap: 0, border: `1px solid ${T.hair}` }}>
          {d.sections.map(([k, v], i) => (
            <Reveal key={i} delay={i * 60}>
              <div style={{
                display: 'grid', gridTemplateColumns: 'minmax(0, 1fr)', gap: 8,
                padding: '20px 24px', borderBottom: `1px solid ${T.hair}`,
              }}>
                <div style={{ ...TX.mono, fontSize: 10, color: T.gold }}>{k.toUpperCase()}</div>
                <div style={{ fontSize: 15, color: T.body, lineHeight: 1.6 }}>{v}</div>
              </div>
            </Reveal>
          ))}
        </div>
      </Section>
      <CTABlock />
    </>
  );
}

// ─────────────────────────────────────────────────────────────
// /docs — documentation index + per-doc pages
// ─────────────────────────────────────────────────────────────
function DocsIndexPage() {
  const { t, lang } = useLang();
  const isDesktop = useIsDesktop();
  const docs = t.docs;
  const groups = [
    { key: 'company', label: docs.groupCompany },
    { key: 'client',  label: docs.groupClient },
    { key: 'legal',   label: docs.groupLegal },
  ];
  return (
    <>
      <PageBreadcrumbs trail={[{ to: '/', label: t.crumb.home }, { label: t.crumb.docs }]} />
      <PageHero
        num="09" kicker={docs.kicker}
        title={docs.title}
        sub={docs.sub}
        badges={[
          ['KVK',                                     CONTACT.kvk],
          [tx(lang, 'ЮРЛИЦО', 'LEGAL ENTITY'),         CONTACT.legalName],
          [tx(lang, 'АДРЕС',  'ADDRESS'),              CONTACT.address],
          [tx(lang, 'СТРАНА', 'COUNTRY'),              lang === 'en' ? CONTACT.countryEn : CONTACT.country],
          [tx(lang, 'РЕГ.',   'REG.'),                 lang === 'en' ? CONTACT.registeredEn : CONTACT.registered],
        ]}
      />
      {groups.map((g, gi) => {
        const items = docs.cards.filter((c) => c.group === g.key);
        return (
          <Section key={g.key} num={'09.' + (gi + 1)} kicker={g.label} title={g.label}>
            <div style={{
              display: 'grid',
              gridTemplateColumns: isDesktop ? 'repeat(3, 1fr)' : '1fr',
              border: `1px solid ${T.hair}`,
            }}>
              {items.map((c, i) => {
                const to = c.external || ('/docs/' + c.slug);
                const cols = isDesktop ? 3 : 1;
                return (
                  <Reveal key={c.slug} delay={i * 60}>
                    <Link to={to} style={{
                      display: 'flex', flexDirection: 'column', gap: 12,
                      padding: '26px 26px',
                      borderRight: isDesktop && (i + 1) % cols !== 0 ? `1px solid ${T.hair}` : 'none',
                      borderBottom: `1px solid ${T.hair}`,
                      color: T.ink, textDecoration: 'none', height: '100%',
                      transition: 'background .15s ease, transform .25s ease',
                      position: 'relative', overflow: 'hidden',
                    }} onMouseEnter={(e) => {
                      e.currentTarget.style.background = T.paper;
                      e.currentTarget.style.transform = 'translateY(-2px)';
                    }} onMouseLeave={(e) => {
                      e.currentTarget.style.background = 'transparent';
                      e.currentTarget.style.transform = 'translateY(0)';
                    }}>
                      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
                        <span style={{ ...TX.mono, fontSize: 10, color: T.gold }}>§ {c.tag}</span>
                        <span aria-hidden style={{ width: 9, height: 9, border: `1px solid ${T.gold}`, transform: 'rotate(45deg)' }} />
                      </div>
                      <div style={{ fontSize: 18, fontWeight: 600, color: T.ink, letterSpacing: '-0.01em', lineHeight: 1.25 }}>{c.title}</div>
                      <div style={{ fontSize: 13, color: T.body, lineHeight: 1.55 }}>{c.body}</div>
                      <div style={{ marginTop: 'auto', display: 'flex', justifyContent: 'space-between', alignItems: 'center', paddingTop: 14 }}>
                        <span style={{ ...TX.mono, fontSize: 10, color: T.ink, borderBottom: `1px solid ${T.gold}`, paddingBottom: 2 }}>{(docs.cta || 'OPEN').toUpperCase()}</span>
                        <span style={{ color: T.gold }}>→</span>
                      </div>
                    </Link>
                  </Reveal>
                );
              })}
            </div>
          </Section>
        );
      })}
      <CTABlock />
    </>
  );
}

function DocPage({ slug }) {
  const { t } = useLang();
  const docs = t.docs;
  const d = docs && docs.pages && docs.pages[slug];
  if (!d) return <NotFoundPage />;
  const cardMeta = (docs.cards || []).find((c) => c.slug === slug);
  const tag = (cardMeta && cardMeta.tag) || '—';
  return (
    <>
      <PageBreadcrumbs trail={[
        { to: '/', label: t.crumb.home },
        { to: '/docs', label: t.crumb.docs },
        { label: d.kicker },
      ]} />
      <PageHero num={'09.' + tag} kicker={d.kicker} title={d.title} sub={d.sub} />
      <Section num={'09.' + tag + '.1'} kicker={d.kicker} title={d.title.replace(/\.$/, '')}>
        <div style={{ display: 'grid', gap: 0, border: `1px solid ${T.hair}` }}>
          {d.sections.map(([k, v], i) => (
            <Reveal key={i} delay={i * 50}>
              <div style={{
                display: 'grid', gridTemplateColumns: 'minmax(0, 1fr)', gap: 8,
                padding: '20px 24px', borderBottom: `1px solid ${T.hair}`,
              }}>
                <div style={{ ...TX.mono, fontSize: 10, color: T.gold }}>{k.toUpperCase()}</div>
                <div style={{ fontSize: 15, color: T.body, lineHeight: 1.65, whiteSpace: 'pre-line' }}>{v}</div>
              </div>
            </Reveal>
          ))}
        </div>
        {d.note && (
          <p style={{
            marginTop: 22, padding: '14px 18px', background: T.paper,
            borderLeft: `3px solid ${T.gold}`, fontSize: 13, color: T.body, lineHeight: 1.6, maxWidth: 860,
          }}>{d.note}</p>
        )}
      </Section>
      <CTABlock />
    </>
  );
}

// ─────────────────────────────────────────────────────────────
// 404
// ─────────────────────────────────────────────────────────────
function NotFoundPage() {
  const { lang } = useLang();
  return (
    <>
      <PageHero num="—" kicker="404"
        title={tx(lang, 'Страница не найдена.', 'Page not found.')}
        sub={tx(lang,
          'Возможно, ссылка устарела или адрес введён с ошибкой. Перейдите к разделу «Практики» или вернитесь на главную.',
          'The link may be outdated or mistyped. Go to "Practices" or return to the home page.')} />
      <Section num="—"
        kicker={tx(lang, 'Куда дальше', 'Where next')}
        title={tx(lang, 'Полезные разделы.', 'Useful sections.')}>
        <CardGrid columns={3} items={lang === 'en' ? [
          { tag: 'Home',          title: 'Return to home',    body: 'Overview of the practice and recovery directions.', to: '/',         cta: 'Home' },
          { tag: 'Practices',     title: 'All directions',    body: 'Six recovery practices with route descriptions.',   to: '/services', cta: 'Open' },
          { tag: 'Contact',       title: 'Reach the office',  body: 'Address, registration, intake form.',               to: '/contact',  cta: 'Open' },
        ] : [
          { tag: 'Главная',   title: 'Вернуться на главную', body: 'Обзор практики и направлений возврата.', to: '/', cta: 'На главную' },
          { tag: 'Практики',  title: 'Все направления',      body: 'Шесть практик возврата с описанием маршрута.', to: '/services', cta: 'Открыть' },
          { tag: 'Контакты',  title: 'Связаться с офисом',   body: 'Адрес, реквизиты, форма приёма дела.', to: '/contact', cta: 'Открыть' },
        ]} />
      </Section>
      <CTABlock />
    </>
  );
}

// ─────────────────────────────────────────────────────────────
// Route resolver
// ─────────────────────────────────────────────────────────────
function resolvePage(path) {
  if (path === '/' || path === '') return { kind: 'home' };
  if (path === '/services') return { kind: 'services' };
  const sm = /^\/services\/([a-z0-9-]+)$/.exec(path);
  if (sm) return { kind: 'service', slug: sm[1] };
  if (path === '/crypto') return { kind: 'pillar', slug: 'crypto' };
  if (path === '/forex') return { kind: 'pillar', slug: 'forex' };
  if (path === '/business') return { kind: 'pillar', slug: 'business' };
  if (path === '/about') return { kind: 'about' };
  if (path === '/specialists') return { kind: 'specialists' };
  const am = /^\/specialists\/([a-z0-9-]+)$/.exec(path);
  if (am) return { kind: 'article', slug: am[1] };
  if (path === '/faq') return { kind: 'faq' };
  if (path === '/contact') return { kind: 'contact' };
  if (path === '/privacy') return { kind: 'legal', slug: 'privacy' };
  if (path === '/terms') return { kind: 'legal', slug: 'terms' };
  if (path === '/security') return { kind: 'legal', slug: 'security' };
  if (path === '/docs') return { kind: 'docs' };
  const dm = /^\/docs\/([a-z0-9-]+)$/.exec(path);
  if (dm) {
    const slug = dm[1];
    // Legacy three live as their own top-level routes; documentation drop links to them.
    if (slug === 'privacy' || slug === 'terms' || slug === 'security') return { kind: 'legal', slug };
    return { kind: 'doc', slug };
  }
  return { kind: '404' };
}

function PageBody({ path }) {
  const r = resolvePage(path);
  switch (r.kind) {
    case 'home':      return <HomePage />;
    case 'services':  return <ServicesIndexPage />;
    case 'service':   return <PracticeDetailPage slug={r.slug} />;
    case 'pillar':    return <PillarPageX slug={r.slug} />;
    case 'about':     return <AboutPageX />;
    case 'specialists': return <SpecialistsIndexPage />;
    case 'article':     return <SpecialistsArticlePage slug={r.slug} />;
    case 'faq':       return <FaqPage />;
    case 'contact':   return <ContactPageX />;
    case 'legal':     return <LegalPage slug={r.slug} />;
    case 'docs':      return <DocsIndexPage />;
    case 'doc':       return <DocPage slug={r.slug} />;
    default:          return <NotFoundPage />;
  }
}

Object.assign(window, {
  useIsDesktop, PageShell, PageBody, NAV_PRIMARY, SERVICES_DROP, DOCS_DROP,
  resolvePage,
});
