// ============================================================
//  Digital Slap — Design System (shared across all RU pages)
//  Палитра, шрифты, шапка, sticky CTA, футер, компоненты
// ============================================================

// Design tokens — «Импульс» v1.0 (lavender-led, coral pulse, cream text)
const DS_ACCENT = {
  bg:       "#B9A3F2",   // lavender — PRIMARY accent (links, data, emphasis)
  bright:   "#CBB6F7",   // lavender-bright — highlights / light fields
  violet:   "#8B6BE6",   // deep violet — gradient end / on-cream accent
  ink:      "#17131F",   // text on lavender fields (violet-graphite)
  coral:    "#FF5C3D",   // «pulse» — live dot / alert / negative metric — sparingly
  gradient: "linear-gradient(135deg, #CBB6F7 0%, #A88EEE 100%)",
};
const DS_THEME = {
  bg: "#0B0B0C", surface: "#151518", surface2: "#17131F",
  line: "rgba(255,255,255,0.08)", line2: "rgba(255,255,255,0.14)",
  fg: "#F1EDE3", dim: "#9A98A0", dim2: "#5E5C57", muted: "#8C8A85",
};

const DS_CSS_VARS = {
  "--bg": DS_THEME.bg, "--surface": DS_THEME.surface, "--surface2": DS_THEME.surface2,
  "--line": DS_THEME.line, "--line2": DS_THEME.line2,
  "--fg": DS_THEME.fg, "--dim": DS_THEME.dim, "--dim2": DS_THEME.dim2, "--muted": DS_THEME.muted,
  "--accent": DS_ACCENT.bg, "--accent-bright": DS_ACCENT.bright, "--accent-ink": DS_ACCENT.ink,
  "--violet": DS_ACCENT.violet, "--coral": DS_ACCENT.coral,
};

const DS_NAV = [
  { href: "/ru/", label: "Главная" },
  { href: "/ru/smm", label: "SMM под ключ" },
  { href: "/ru/meta-target", label: "Meta таргет" },
  { href: "/ru/google-reklama", label: "Google реклама" },
  { href: "/ru/landings", label: "Лендинги" },
];

const DS_CONTACTS = {
  whatsapp: "+420 777 199 116",
  whatsappLink: "https://wa.me/420777199116",
  telegram: "@LappoS",
  telegramLink: "https://t.me/LappoS",
  email: "hello@digital-slap.com",
  emailLink: "mailto:hello@digital-slap.com",
  calendly: "https://calendly.com/lappo-stepan/introductory-call",
};

const DS_CTA = {
  primary: "Бесплатная консультация",
  short: "Обсудить проект",
};

// ---------- primitives ----------
function DSArrow({ size = 16 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" style={{ flex: "none" }}>
      <path d="M5 12h14M13 6l6 6-6 6" stroke="currentColor" strokeWidth="2" strokeLinecap="square"/>
    </svg>
  );
}

function DSCheck() {
  return (
    <svg width="22" height="22" viewBox="0 0 24 24" fill="none" style={{ flex: "none", marginTop: 3 }}>
      <circle cx="12" cy="12" r="11" fill={DS_ACCENT.bg}/>
      <path d="M7 12.5l3.5 3.5L17 9" stroke={DS_ACCENT.ink} strokeWidth="2.5" strokeLinecap="square" fill="none"/>
    </svg>
  );
}

function DSLogo({ size = 32 }) {
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
      <div style={{ position: "relative", flex: "none" }}>
        <div style={{
          width: size, height: size, borderRadius: 8,
          background: DS_THEME.surface2, border: `1.5px solid ${DS_ACCENT.bg}`,
          boxShadow: "0 0 28px rgba(185,163,242,.3)",
          color: DS_ACCENT.bright, display: "grid", placeItems: "center",
          fontFamily: "'IBM Plex Mono', ui-monospace, monospace", fontWeight: 700,
          fontSize: size * 0.5, letterSpacing: "-.02em",
        }}>D</div>
        <span style={{
          position: "absolute", top: size * 0.16, right: size * 0.16, width: 5, height: 5,
          borderRadius: 999, background: DS_ACCENT.coral,
          boxShadow: "0 0 8px rgba(255,92,61,.6)",
        }}/>
      </div>
      <div style={{
        fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontWeight: 700,
        letterSpacing: "-.01em", fontSize: 15, color: DS_THEME.fg,
      }}>
        DIGITAL <span style={{ color: DS_ACCENT.bg }}>SLAP<span style={{ color: DS_ACCENT.bright }}>_</span></span>
      </div>
    </div>
  );
}

// ---------- Переключатель языка ----------
// Зеркальный адрес считаем от текущего пути: /ru/landings → /landings
function DSLangSwitch({ big = false }) {
  const [href, setHref] = React.useState("/");
  React.useEffect(() => {
    let p = window.location.pathname.replace(/\.html$/, "");
    if (p === "/ru" || p === "/ru/") { setHref("/"); return; }
    if (p.startsWith("/ru/")) { setHref(p.slice(3)); return; }
    setHref("/");            // страницы без чешской пары ведут на главную
  }, []);
  const base = {
    fontFamily: "'IBM Plex Mono', ui-monospace, monospace",
    fontSize: big ? 15 : 12, fontWeight: 500, letterSpacing: ".1em",
    textDecoration: "none", padding: big ? "4px 2px" : "2px 0",
  };
  return (
    <div style={{ display: "inline-flex", alignItems: "center", gap: big ? 10 : 7 }}>
      <span style={{ ...base, color: DS_ACCENT.bg }}>RU</span>
      <span style={{ ...base, color: DS_THEME.dim2 }}>/</span>
      <a href={href} style={{ ...base, color: DS_THEME.dim, transition: "color .2s ease" }}
         onMouseEnter={e => e.currentTarget.style.color = DS_THEME.fg}
         onMouseLeave={e => e.currentTarget.style.color = DS_THEME.dim}
         aria-label="Přepnout na češtinu">CZ</a>
    </div>
  );
}

// ---------- CTA button ----------
// По умолчанию ведёт к форме внизу страницы (секция id="Контакты").
// Если передан href с http(s) — открывается в новой вкладке как внешняя ссылка.
function DSButton({ href = "#Контакты", children = DS_CTA.primary, size = "md", variant = "primary", target }) {
  const sizes = {
    sm: { pad: "12px 18px", fs: 13, gap: 10, arrowBox: 22, arrowSize: 10 },
    md: { pad: "16px 24px", fs: 14, gap: 12, arrowBox: 28, arrowSize: 12 },
    lg: { pad: "24px 38px", fs: 18, gap: 16, arrowBox: 40, arrowSize: 17 },
  };
  const s = sizes[size] || sizes.md;
  const primary = variant === "primary";
  const isExternal = /^https?:\/\//.test(href);
  const isAnchor = href && href.startsWith("#");

  function onClick(e) {
    if (!isAnchor) return;
    const id = decodeURIComponent(href.slice(1));
    // секции обёрнуты в <section id="..."> через DSSection; fallback на форму внутри
    const el = document.getElementById(id)
            || document.querySelector(`[id="${id}"]`)
            || document.querySelector(`a[name="${id}"]`);
    if (!el) return;
    e.preventDefault();
    const header = document.querySelector("[data-ds-header]");
    const offset = (header ? header.getBoundingClientRect().height : 0) + 16;
    const top = el.getBoundingClientRect().top + window.scrollY - offset;
    window.scrollTo({ top, behavior: "smooth" });
    history.replaceState(null, "", href);
  }

  return (
    <a href={href}
       onClick={onClick}
       target={target || (isExternal ? "_blank" : undefined)}
       rel={isExternal ? "noopener" : undefined}
       className={`ds-btn ds-btn-${size} ${primary ? "ds-btn-primary" : "ds-btn-ghost"}`}
       style={{
      display: "inline-flex", alignItems: "center", gap: s.gap,
      padding: s.pad, borderRadius: 999,
      background: primary ? DS_ACCENT.bg : "transparent",
      color: primary ? DS_ACCENT.ink : DS_THEME.fg,
      border: primary ? "none" : `1px solid ${DS_THEME.line2}`,
      fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontWeight: 800,
      fontSize: s.fs, letterSpacing: ".04em", textTransform: "uppercase",
      whiteSpace: "nowrap",
      boxShadow: primary ? `0 10px 30px -8px ${DS_ACCENT.bg}66` : "none",
    }}>
      {children}
      <span className="ds-btn-arrow" style={{
        width: s.arrowBox, height: s.arrowBox, borderRadius: 999,
        background: primary ? DS_ACCENT.ink : DS_THEME.fg,
        color: primary ? DS_ACCENT.bg : DS_THEME.bg,
        display: "grid", placeItems: "center", flexShrink: 0,
      }}>
        <DSArrow size={s.arrowSize}/>
      </span>
    </a>
  );
}

// ---------- Eyebrow label ----------
function DSEyebrow({ children }) {
  return (
    <div style={{
      display: "inline-flex", alignItems: "center", gap: 10,
      fontFamily: "'IBM Plex Mono', ui-monospace, monospace", fontSize: 11.5, fontWeight: 500,
      letterSpacing: ".2em", textTransform: "uppercase", color: DS_THEME.dim,
    }}>
      <span style={{ width: 24, height: 1, background: DS_ACCENT.bg }}/>
      <span>{children}</span>
    </div>
  );
}

// ---------- Reveal on scroll ----------
function DSReveal({ children, delay = 0, as = "div", style = {}, ...rest }) {
  const ref = React.useRef(null);
  const [shown, setShown] = React.useState(false);
  React.useEffect(() => {
    const el = ref.current; if (!el) return;
    if (typeof IntersectionObserver === "undefined") { setShown(true); return; }
    const obs = new IntersectionObserver(entries => {
      entries.forEach(e => { if (e.isIntersecting) { setShown(true); obs.disconnect(); } });
    }, { rootMargin: "-8% 0px" });
    obs.observe(el);
    // Страховка: при мгновенном прыжке по якорю (#Контакты) наблюдатель иногда не
    // срабатывает, и блок остаётся с opacity:0 навсегда — человек видит пустой экран
    // вместо формы. Раз в полсекунды сверяем сами; как только блок в зоне видимости,
    // показываем его. Ловля 20.09.2026 на мобильном.
    let timer = 0;
    const stop = () => { if (timer) clearInterval(timer); obs.disconnect(); };
    const check = () => {
      const node = ref.current; if (!node) return;
      const r = node.getBoundingClientRect();
      if (r.top < window.innerHeight && r.bottom > 0) { setShown(true); stop(); }
    };
    timer = setInterval(check, 500);
    window.addEventListener("hashchange", check);
    return () => { stop(); window.removeEventListener("hashchange", check); };
  }, []);
  const Tag = as;
  return (
    <Tag ref={ref} style={{
      opacity: shown ? 1 : 0,
      transform: shown ? "translateY(0)" : "translateY(16px)",
      transition: `opacity .7s cubic-bezier(.2,.6,.2,1) ${delay}s, transform .7s cubic-bezier(.2,.6,.2,1) ${delay}s`,
      ...style,
    }} {...rest}>{children}</Tag>
  );
}

// ---------- Section wrapper ----------
function DSSection({ id, children, style = {}, pad = "clamp(60px, 9vh, 120px)", bg = "transparent" }) {
  return (
    <section id={id} data-screen-label={id} style={{
      background: bg, padding: `${pad} clamp(32px, 7vw, 120px)`,
      borderTop: `1px solid ${DS_THEME.line}`, ...style,
    }}>
      <div style={{ maxWidth: 1320, margin: "0 auto" }}>
        {children}
      </div>
    </section>
  );
}

// ---------- Header ----------
function DSHeader({ active, hideNav = false, ctaLabel, ctaHref, hideBottomCta = false, revealOnScroll = false }) {
  const [scrolled, setScrolled] = React.useState(false);
  const [revealed, setRevealed] = React.useState(false);
  React.useEffect(() => {
    let raf = 0;
    const apply = () => {
      raf = 0;
      const y = window.scrollY;
      const s = y > 20;
      const r = y > window.innerHeight * 0.55;
      setScrolled((p) => (p !== s ? s : p));
      setRevealed((p) => (p !== r ? r : p));
    };
    // rAF-троттлинг: не дёргаем setState на каждый scroll-кадр, и обновляем стейт только при смене значения
    const onScroll = () => { if (!raf) raf = requestAnimationFrame(apply); };
    apply();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll, { passive: true });
    return () => { if (raf) cancelAnimationFrame(raf); window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onScroll); };
  }, []);
  // На reveal-страницах шапка (лого + CTA) скрыта на первом экране и появляется при скролле вниз.
  const activeScroll = revealOnScroll ? revealed : scrolled;
  const barActive = revealOnScroll ? revealed : true;

  const [menuOpen, setMenuOpen] = React.useState(false);
  // Попап с формой открылся — мобильное меню закрываем, иначе оно останется под ним.
  React.useEffect(() => {
    const close = () => setMenuOpen(false);
    window.addEventListener("ds-form-open", close);
    return () => window.removeEventListener("ds-form-open", close);
  }, []);

  // Нижняя мобильная CTA прячется, когда форма «Контакты» уже на экране —
  // чтобы не перекрывать собственную кнопку формы и не дублировать действие.
  const [atContact, setAtContact] = React.useState(false);
  React.useEffect(() => {
    const el = document.getElementById("Контакты");
    if (!el) return;
    const obs = new IntersectionObserver(
      ([e]) => setAtContact(e.isIntersecting),
      { rootMargin: "0px 0px -35% 0px" }
    );
    obs.observe(el);
    return () => obs.disconnect();
  }, []);

  const bottomVisible = activeScroll && !menuOpen && !atContact;

  return (
    <>
      <header data-ds-header style={{
        position: revealOnScroll ? "fixed" : "sticky", top: 0, left: 0, right: 0, zIndex: 50,
        background: activeScroll ? "rgba(10,10,10,.85)" : "transparent",
        backdropFilter: activeScroll ? "blur(14px) saturate(1.2)" : "none",
        WebkitBackdropFilter: activeScroll ? "blur(14px) saturate(1.2)" : "none",
        borderBottom: activeScroll ? `1px solid ${DS_THEME.line}` : "1px solid transparent",
        pointerEvents: barActive ? "auto" : "none",
        transition: "background .25s ease, border-color .25s ease",
      }}>
        <div style={{
          maxWidth: 1400, margin: "0 auto",
          padding: "18px clamp(32px, 7vw, 120px)",
          display: "flex", alignItems: "center", justifyContent: "space-between", gap: 24,
          opacity: barActive ? 1 : 0,
          transform: barActive ? "translateY(0)" : "translateY(-10px)",
          transition: "opacity .3s ease, transform .3s ease",
        }}>
          <a href="/ru/" style={{ display: "flex", alignItems: "center" }}>
            <DSLogo/>
          </a>

          {/* desktop nav */}
          {!hideNav && (
            <nav className="ds-nav-desktop" style={{
              display: "flex", alignItems: "center", gap: 28,
            }}>
              {DS_NAV.map(item => {
                const isActive = active === item.href;
                return (
                  <a key={item.href} href={item.href} style={{
                    fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontSize: 13, fontWeight: 500,
                    letterSpacing: "-.005em",
                    color: isActive ? DS_THEME.fg : DS_THEME.dim,
                    borderBottom: isActive ? `1px solid ${DS_ACCENT.bg}` : "1px solid transparent",
                    paddingBottom: 2,
                  }}>{item.label}</a>
                );
              })}
            </nav>
          )}

          <div className="ds-cta-desktop" style={{ display: "flex", alignItems: "center", gap: 26 }}>
            <DSLangSwitch/>
            <DSButton size="md" href={ctaHref || "#Контакты"}>{ctaLabel || DS_CTA.short}</DSButton>
          </div>

          {/* mobile burger */}
          {!hideNav && (
            <button className="ds-burger" onClick={() => setMenuOpen(true)} style={{
              display: "none",
              background: "transparent", border: `1px solid ${DS_THEME.line2}`,
              width: 44, height: 44, borderRadius: 10, color: DS_THEME.fg, cursor: "pointer",
              padding: 0,
            }} aria-label="Открыть меню">
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" style={{ margin: "0 auto", display: "block" }}>
                <path d="M4 7h16M4 12h16M4 17h16" stroke="currentColor" strokeWidth="2" strokeLinecap="square"/>
              </svg>
            </button>
          )}
        </div>
      </header>

      {/* mobile fullscreen menu */}
      {!hideNav && menuOpen && (
        <div
          onClick={(e) => {
            /* Закрываем меню когда кликнули по ссылке или кнопке внутри:
               тогда пользователь сразу видит, куда его перенесло (форма, раздел) */
            if (e.target.closest("a, button")) setMenuOpen(false);
          }}
          style={{
          position: "fixed", inset: 0, zIndex: 200,
          background: DS_THEME.bg,
          display: "flex", flexDirection: "column",
          padding: "24px clamp(20px, 5vw, 40px)",
        }}>
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
            <DSLogo/>
            <button onClick={() => setMenuOpen(false)} style={{
              background: "transparent", border: `1px solid ${DS_THEME.line2}`,
              width: 44, height: 44, borderRadius: 10, color: DS_THEME.fg, cursor: "pointer", padding: 0,
            }} aria-label="Закрыть меню">
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" style={{ margin: "0 auto", display: "block" }}>
                <path d="M6 6l12 12M18 6L6 18" stroke="currentColor" strokeWidth="2" strokeLinecap="square"/>
              </svg>
            </button>
          </div>
          <nav style={{
            marginTop: 56, display: "flex", flexDirection: "column", gap: 0,
            fontFamily: "'IBM Plex Sans', system-ui, sans-serif",
          }}>
            {DS_NAV.map(item => (
              <a key={item.href} href={item.href} style={{
                fontSize: 32, fontWeight: 700, letterSpacing: "-.02em",
                color: active === item.href ? DS_ACCENT.bg : DS_THEME.fg,
                padding: "18px 0", borderBottom: `1px solid ${DS_THEME.line}`,
              }}>{item.label}</a>
            ))}
          </nav>
          <div style={{ marginTop: "auto", paddingTop: 32 }}>
            <div style={{ marginBottom: 22 }}><DSLangSwitch big/></div>
            <DSButton size="lg">{DS_CTA.primary}</DSButton>
          </div>
        </div>
      )}

      {/* Мобильная нижняя CTA — крупная, «под пальцем». Появляется при скролле,
          прячется когда форма «Контакты» на экране. Видима только на мобиле (CSS).
          На юр-страницах (оферта/политика/privacy/VOP) скрыта: там нет секции
          id="Контакты", поэтому плашка не пряталась бы и перекрывала текст внизу,
          плюс её ссылка вела бы в никуда. См. hideBottomCta. */}
      {!hideBottomCta && (
        <div className={`ds-bottom-cta${bottomVisible ? " visible" : ""}`} aria-hidden={!bottomVisible}>
          <DSButton size="lg" href={ctaHref || "#Контакты"}>{ctaLabel || DS_CTA.short}</DSButton>
        </div>
      )}
    </>
  );
}

// sticky CTA removed — header already has a fixed CTA

// ---------- Footer ----------
function DSFooter({ hideNav = false }) {
  return (
    <footer style={{
      borderTop: `1px solid ${DS_THEME.line}`,
      padding: "clamp(60px, 8vh, 96px) clamp(32px, 7vw, 120px) 32px",
      background: DS_THEME.bg,
    }}>
      <div style={{ maxWidth: 1320, margin: "0 auto" }}>

        {/* grid */}
        <div style={{
          display: "grid",
          gridTemplateColumns: hideNav ? "1.4fr 1fr 1fr" : "1.2fr 1fr 1fr 1fr",
          gap: 40,
        }} className="ds-footer-grid">
          <div>
            <DSLogo/>
            <p style={{
              fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontSize: 14, lineHeight: 1.55,
              color: DS_THEME.dim, margin: "18px 0 0", maxWidth: 320,
            }}>
              Приводим клиентов бизнесу. Реклама, контент, сайты.
            </p>
            <p style={{
              fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontSize: 14, lineHeight: 1.55,
              color: DS_THEME.dim, margin: "14px 0 0", maxWidth: 320,
            }}>
              Офис: Italská 2581/67, 120 00 Praha 2<br/>
              <a href={"tel:" + DS_CONTACTS.whatsapp.replace(/\s/g, "")}
                 style={{ color: DS_THEME.dim, textDecoration: "none" }}>{DS_CONTACTS.whatsapp}</a>
            </p>
          </div>
          {!hideNav && (
            <FooterCol title="Страницы" items={[
              { href: "/ru/", label: "Главная" },
              { href: "/ru/smm", label: "SMM под ключ" },
              { href: "/ru/meta-target", label: "Meta таргет" },
              { href: "/ru/google-reklama", label: "Google реклама" },
              { href: "/ru/landings", label: "Лендинги" },
            ]}/>
          )}
          <FooterCol title="Контакты" items={[
            { href: DS_CONTACTS.whatsappLink, label: "WhatsApp · " + DS_CONTACTS.whatsapp, ext: true },
            { href: DS_CONTACTS.telegramLink, label: "Telegram · " + DS_CONTACTS.telegram, ext: true },
            { href: DS_CONTACTS.emailLink, label: DS_CONTACTS.email },
          ]}/>
          <FooterCol title="Юридическое" items={[
            { href: "/ru/policy", label: "Политика конфиденциальности" },
            { href: "/ru/vop", label: "VOP · Договор оферты" },
          ]}/>
        </div>

        <div style={{
          marginTop: 56, paddingTop: 24, borderTop: `1px solid ${DS_THEME.line}`,
          display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 16,
          fontFamily: "'IBM Plex Mono', ui-monospace, monospace", fontSize: 11, letterSpacing: ".14em",
          textTransform: "uppercase", color: DS_THEME.dim2,
        }}>
          <div>© 2026 Vzdělávací Hub s.r.o. · IČO 23590939 · Korunní 2569/108, 101 00 Praha 10</div>
          <div>Digital-slap.com · RU · CZ · EN</div>
        </div>
      </div>
    </footer>
  );
}

function FooterCol({ title, items }) {
  return (
    <div>
      <div style={{
        fontFamily: "'IBM Plex Mono', ui-monospace, monospace", fontSize: 10.5,
        letterSpacing: ".2em", textTransform: "uppercase", color: DS_THEME.dim2,
        marginBottom: 16,
      }}>{title}</div>
      <ul style={{ listStyle: "none", padding: 0, margin: 0, display: "flex", flexDirection: "column", gap: 10 }}>
        {items.map(i => (
          <li key={i.href + i.label}>
            <a href={i.href} target={i.ext ? "_blank" : undefined} rel={i.ext ? "noopener" : undefined} style={{
              fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontSize: 14, fontWeight: 500,
              color: DS_THEME.fg, opacity: .85,
            }}>{i.label}</a>
          </li>
        ))}
      </ul>
    </div>
  );
}

// ---------- Page shell ----------
function DSPage({ active, children, hideNav = false, ctaLabel, ctaHref, hideBottomCta = false, revealOnScroll = false }) {
  return (
    <div style={{ ...DS_CSS_VARS, background: DS_THEME.bg, color: DS_THEME.fg, minHeight: "100vh" }}>
      <DSHeader active={active} hideNav={hideNav} ctaLabel={ctaLabel} ctaHref={ctaHref} hideBottomCta={hideBottomCta} revealOnScroll={revealOnScroll}/>
      <main>{children}</main>
      <DSFooter hideNav={hideNav}/>
    </div>
  );
}

// ---------- Contact form (shared between Home & Stub pages) ----------
const DS_FORM_ENDPOINT = "https://script.google.com/macros/s/AKfycbxS7L3UWdXIh3wWR5iipaGLKOcQiBjp7Y52k5O4tf2vfT9btx-IqW1JDcACfQYPxZSI7g/exec";
// Форма DSContactSection шлёт в свою Pages Function: она отвечает сразу, а в Apps Script
// пересылает в фоне с повторами (functions/api/site-lead.js). Раньше ждали Apps Script 3–6 с.
const DS_LEAD_ENDPOINT = "/api/site-lead";

function DSFormField({ label, name, type = "text", required, textarea, rows, placeholder }) {
  const [focus, setFocus] = React.useState(false);
  const shared = {
    width: "100%", padding: "15px 16px",
    background: focus ? "rgba(255,255,255,.09)" : "rgba(255,255,255,.05)",
    border: `1px solid ${focus ? DS_ACCENT.bg : "rgba(255,255,255,.12)"}`,
    borderRadius: 12,
    color: DS_THEME.fg, fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontSize: 16,
    outline: "none", resize: textarea ? "vertical" : "none",
    boxShadow: focus ? "0 0 0 3px rgba(185,163,242,.14)" : "none",
    transition: "border-color .2s ease, background .2s ease, box-shadow .2s ease",
  };
  return (
    <label style={{ display: "flex", flexDirection: "column", gap: 8 }}>
      <span style={{
        fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontSize: 12, fontWeight: 600,
        letterSpacing: ".16em", textTransform: "uppercase", color: DS_THEME.dim,
      }}>{label}{required && <span style={{ color: DS_ACCENT.bg }}> *</span>}</span>
      {textarea
        ? <textarea name={name} rows={rows || 3} placeholder={placeholder} required={required}
                    style={shared} onFocus={() => setFocus(true)} onBlur={() => setFocus(false)}/>
        : <input name={name} type={type} placeholder={placeholder} required={required}
                 style={shared} onFocus={() => setFocus(true)} onBlur={() => setFocus(false)}/>}
    </label>
  );
}

// Если заявку уже приняла страховка ds-form-guard (React тогда ещё не смонтировался),
// показываем подтверждение сразу, иначе человек увидел бы снова пустую форму и решил,
// что отправка не прошла. Метка одноразовая: читаем и тут же убираем.
function dsTakeGuardSent() {
  try {
    if (sessionStorage.getItem("ds_form_sent") === "1") {
      sessionStorage.removeItem("ds_form_sent");
      return true;
    }
  } catch (e) {}
  return false;
}

// ---------- Попап с формой ----------
// Все ссылки на #Контакты (CTA-кнопки, кнопка в шапке) открывают форму поверх страницы,
// а не прокручивают вниз. Сама форма та же, что в секции: при открытом попапе она
// переносится в портал, в секции на это время пусто.
function DSFormModal({ open, onClose, title, closeLabel, children }) {
  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    const prev = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    document.addEventListener("keydown", onKey);
    return () => { document.body.style.overflow = prev; document.removeEventListener("keydown", onKey); };
  }, [open]);
  if (!open || typeof document === "undefined") return null;
  return ReactDOM.createPortal(
    <div onClick={onClose} className="ds-form-modal" style={{
      position: "fixed", inset: 0, zIndex: 1000, overflowY: "auto",
      background: "rgba(8,8,10,.78)", backdropFilter: "blur(10px)", WebkitBackdropFilter: "blur(10px)",
      display: "flex", justifyContent: "center", alignItems: "flex-start",
      padding: "clamp(16px, 6vh, 72px) 16px",
    }}>
      <div onClick={(e) => e.stopPropagation()} role="dialog" aria-modal="true" style={{
        width: "100%", maxWidth: 560, margin: "auto 0", position: "relative",
        background: DS_THEME.surface, borderRadius: 28, border: `1px solid ${DS_THEME.line2}`,
        boxShadow: "0 40px 120px -30px rgba(0,0,0,.8), 0 0 60px -20px rgba(185,163,242,.25)",
        padding: "clamp(22px, 3vw, 32px) clamp(14px, 2vw, 22px) clamp(14px, 2vw, 22px)",
      }}>
        <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 16, padding: "0 clamp(8px, 1.4vw, 14px) 18px" }}>
          <h3 style={{
            fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontWeight: 700,
            fontSize: "clamp(20px, 2.4vw, 28px)", lineHeight: 1.05, letterSpacing: "-.02em",
            textTransform: "uppercase", margin: 0,
          }}>{title}</h3>
          <button type="button" onClick={onClose} aria-label={closeLabel} style={{
            width: 40, height: 40, flex: "none", borderRadius: 999, cursor: "pointer",
            background: "transparent", border: `1px solid ${DS_THEME.line2}`, color: DS_THEME.fg,
            display: "grid", placeItems: "center",
          }}>
            <svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M1 1l12 12M13 1L1 13" stroke="currentColor" strokeWidth="1.6"/></svg>
          </button>
        </div>
        {children}
      </div>
    </div>,
    document.body
  );
}

function DSContactSection({ sourceLabel = "digital-slap.com", hideEmail = false, intro, singleStep = false, heading, submitLabel = "Отправить" }) {
  const [status, setStatus] = React.useState(() => dsTakeGuardSent() ? "sent" : "idle"); // idle | sending | sent | error
  const [errMsg, setErrMsg] = React.useState("");
  const [step, setStep] = React.useState(1); // 2-шаговая форма: 1 — контакты, 2 — о ситуации
  const formRef = React.useRef(null);
  const step1Sent = React.useRef(false);

  function postLead(payload) {
    if (!DS_FORM_ENDPOINT || DS_FORM_ENDPOINT.startsWith("__")) return Promise.resolve();
    const body = new URLSearchParams();
    Object.entries(payload).forEach(([k, v]) => body.append(k, v));
    // Шаг 1 уходит в фоне — пользователь его не ждёт. Ответ всё равно проверяем
    // и при сбое повторяем один раз, чтобы ранний контакт не потерялся молча.
    const send = () => fetch(DS_LEAD_ENDPOINT, { method: "POST", body: new URLSearchParams(body) })
      .then((res) => { if (!res.ok) throw new Error("HTTP " + res.status); return res.text(); })
      .then((t) => { const d = JSON.parse(t); if (!d || d.ok !== true) throw new Error("backend rejected"); return d; });
    return send().catch(() => send());
  }

  function fieldVal(name) {
    if (!formRef.current) return "";
    const fd = new FormData(formRef.current);
    return (fd.get(name) || "").toString().trim();
  }
  function goNext() {
    if (!fieldVal("name") || !fieldVal("phone") || !fieldVal("url")) {
      setErrMsg("Заполните имя, телефон и ссылку на сайт или соцсети.");
      setStatus("error");
      return;
    }
    // Ранний захват контактов: уходит сразу после шага 1, чтобы лид не потерялся, если шаг 2 не закончат.
    if (!step1Sent.current) {
      step1Sent.current = true;
      postLead({
        name: fieldVal("name"), phone: fieldVal("phone"), email: fieldVal("email"),
        url: fieldVal("url"), about: "(шаг 1 — контакты получены, ситуацию ещё заполняют)",
        source: sourceLabel + " · шаг 1 · " + window.location.href, ua: navigator.userAgent, ts: new Date().toISOString(),
      }).catch(() => {});
    }
    setErrMsg(""); setStatus("idle"); setStep(2);
  }

  // При смене шага прокручиваем форму к началу (иначе на мобильном шаг 2 оказывается выше экрана)
  const firstStepChange = React.useRef(true);
  React.useEffect(() => {
    if (firstStepChange.current) { firstStepChange.current = false; return; }
    if (formRef.current) formRef.current.scrollIntoView({ behavior: "smooth", block: "start" });
  }, [step]);

  // После отправки экран успеха короче формы и на мобильном оказывается выше вьюпорта —
  // подскролливаем к нему, чтобы кнопка перехода в Telegram была видна сразу.
  React.useEffect(() => {
    if (status === "sent" && formRef.current) {
      formRef.current.scrollIntoView({ behavior: "smooth", block: "center" });
    }
  }, [status]);

  async function onSubmit(e) {
    e.preventDefault();
    if (status === "sending") return;
    const fd = new FormData(formRef.current);
    if (fd.get("website")) { setStatus("sent"); return; } // honeypot
    if (!fieldVal("name") || !fieldVal("phone") || !fieldVal("url")) {
      setStep(1); setErrMsg("Заполните имя, телефон и ссылку."); setStatus("error"); return;
    }
    if (!fieldVal("about")) {
      setErrMsg("Расскажите в двух словах о вашей ситуации."); setStatus("error"); return;
    }
    const payload = {
      name:   fd.get("name")  || "",
      phone:  fd.get("phone") || "",
      email:  fd.get("email") || "",
      url:    fd.get("url")   || "",
      about:  fd.get("about") || "",
      source: sourceLabel + " · " + window.location.href,
      ua:     navigator.userAgent,
      ts:     new Date().toISOString(),
    };
    setStatus("sending"); setErrMsg("");
    if (!DS_FORM_ENDPOINT || DS_FORM_ENDPOINT.startsWith("__")) {
      setTimeout(() => setStatus("sent"), 600);
      return;
    }
    try {
      const body = new URLSearchParams();
      Object.entries(payload).forEach(([k, v]) => body.append(k, v));
      // Ждём подтверждения {"ok":true} от своей функции (она отвечает за доли секунды). Раньше стоял mode:"no-cors" —
      // браузер не давал прочитать ответ, и форма рапортовала об успехе даже когда заявка терялась.
      const res = await fetch(DS_LEAD_ENDPOINT, { method: "POST", body });
      if (!res.ok) throw new Error("HTTP " + res.status);
      const data = JSON.parse(await res.text());
      if (!data || data.ok !== true) throw new Error("backend rejected");
      setStatus("sent");
    } catch (err) {
      console.error(err);
      setErrMsg("Не удалось отправить. Напишите, пожалуйста, в WhatsApp или Telegram — мы ответим в течение дня.");
      setStatus("error");
    }
  }

  const contactsList = [
    { label: "WhatsApp", val: DS_CONTACTS.whatsapp, href: DS_CONTACTS.whatsappLink, ext: true },
    { label: "Telegram", val: DS_CONTACTS.telegram, href: DS_CONTACTS.telegramLink, ext: true },
    { label: "Email",    val: DS_CONTACTS.email,    href: DS_CONTACTS.emailLink },
  ];

  // Клики по любым ссылкам на #Контакты открывают попап вместо прокрутки.
  // capture — опережаем DSButton.onClick, который иначе скроллит к секции.
  const [modal, setModal] = React.useState(false);
  React.useEffect(() => {
    function onAnchorClick(e) {
      const a = e.target.closest && e.target.closest("a[href]");
      if (!a) return;
      let h = a.getAttribute("href") || "";
      try { h = decodeURIComponent(h); } catch (_) {}
      if (h !== "#Контакты") return;
      e.preventDefault(); e.stopPropagation();
      setModal(true);
      window.dispatchEvent(new Event("ds-form-open"));
    }
    document.addEventListener("click", onAnchorClick, true);
    return () => document.removeEventListener("click", onAnchorClick, true);
  }, []);

  const formEl = (
          <form ref={formRef} onSubmit={onSubmit} style={{
            display: "flex", flexDirection: "column", gap: 18,
            background: "radial-gradient(130% 90% at 88% -12%, rgba(185,163,242,.14), transparent 55%), rgba(255,255,255,.04)",
            padding: "clamp(30px, 3.2vw, 44px)",
            borderRadius: 24, border: "1px solid rgba(255,255,255,.10)",
            boxShadow: "0 40px 100px -45px rgba(0,0,0,.6)",
          }}>
            {status === "sent" ? (
              <div style={{
                padding: "40px 0", textAlign: "center",
                display: "flex", flexDirection: "column", alignItems: "center", gap: 20,
              }}>
                <div style={{
                  width: 64, height: 64, borderRadius: 999, background: DS_ACCENT.bg,
                  display: "grid", placeItems: "center",
                }}>
                  <svg width="28" height="28" viewBox="0 0 24 24" fill="none">
                    <path d="M5 12l5 5L20 7" stroke={DS_ACCENT.ink} strokeWidth="2.5" strokeLinecap="square"/>
                  </svg>
                </div>
                <h3 style={{
                  fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontWeight: 700, fontSize: 24,
                  letterSpacing: "-.02em", textTransform: "uppercase",
                }}>Заявка получена</h3>
                    <p style={{
                      fontFamily: "'IBM Plex Sans', system-ui, sans-serif", color: DS_THEME.dim,
                      margin: 0, maxWidth: 380, lineHeight: 1.55,
                    }}>
                      Остался последний шаг — напишите мне в Telegram, чтобы я закрепил вашу заявку
                      и мы договорились о времени созвона.
                    </p>
                    <a href={DS_CONTACTS.telegramLink} target="_blank" rel="noopener" style={{
                      marginTop: 4, width: "100%", padding: "20px",
                      background: DS_ACCENT.gradient, color: DS_ACCENT.ink, borderRadius: 999,
                      fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontWeight: 800, fontSize: 15,
                      letterSpacing: ".04em", textTransform: "uppercase",
                      display: "inline-flex", justifyContent: "center", alignItems: "center", gap: 12,
                    }}>
                      Написать в Telegram
                      <span style={{
                        width: 30, height: 30, borderRadius: 999, background: DS_ACCENT.ink, color: DS_ACCENT.bg,
                        display: "grid", placeItems: "center", flex: "none",
                      }}><DSArrow size={14}/></span>
                    </a>
              </div>
            ) : (
              <>
                <input type="text" name="website" tabIndex={-1} autoComplete="off" aria-hidden="true"
                       style={{ position: "absolute", width: 1, height: 1, padding: 0, margin: -1, overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap", border: 0, opacity: 0, pointerEvents: "none" }}/>

                {!singleStep && (
                  <div style={{
                    fontFamily: "'IBM Plex Mono', ui-monospace, monospace", fontSize: 12, fontWeight: 700,
                    letterSpacing: ".18em", textTransform: "uppercase", color: DS_THEME.dim2,
                  }}>Шаг {step} из 2</div>
                )}

                {/* Одноэтапная форма: все поля + ситуация сразу */}
                {singleStep && (
                  <div style={{ display: "flex", flexDirection: "column", gap: 18 }}>
                    <DSFormField label="Ваше имя" name="name" required/>
                    <DSFormField label="Номер, привязанный к Telegram или WhatsApp" name="phone" type="tel" required placeholder="+420 ..."/>
                    {!hideEmail && <DSFormField label="Email" name="email" type="email"/>}
                    <DSFormField label="Сайт или Instagram бизнеса" name="url" required/>
                    <DSFormField label="Расскажите о вашей ситуации" name="about" required
                                 placeholder="Что за бизнес и ниша, какие проблемы, что хотите получить"
                                 textarea rows={4}/>
                    {status === "error" && (
                      <div style={{
                        padding: "12px 14px", borderRadius: 10,
                        background: "rgba(255,122,92,.08)", border: "1px solid rgba(255,122,92,.4)",
                        fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontSize: 13, color: "#FF9A80", lineHeight: 1.5,
                      }}>{errMsg}</div>
                    )}
                    <button type="submit" disabled={status === "sending"} style={{
                      marginTop: 12, width: "100%", padding: "20px",
                      background: status === "sending" ? DS_ACCENT.bg : DS_ACCENT.gradient,
                      color: DS_ACCENT.ink, border: "none", borderRadius: 999,
                      fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontWeight: 800, fontSize: 15,
                      letterSpacing: ".04em", textTransform: "uppercase",
                      cursor: status === "sending" ? "wait" : "pointer",
                      display: "inline-flex", justifyContent: "center", alignItems: "center", gap: 12,
                      opacity: status === "sending" ? .8 : 1,
                      transition: "background .2s ease, opacity .2s ease",
                    }}>
                      {status === "sending" ? "Отправляем…" : submitLabel}
                      {status !== "sending" && (
                        <span style={{
                          width: 30, height: 30, borderRadius: 999, background: DS_ACCENT.ink, color: DS_ACCENT.bg,
                          display: "grid", placeItems: "center", flex: "none",
                        }}><DSArrow size={14}/></span>
                      )}
                    </button>
                    <p style={{
                      fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontSize: 12, fontWeight: 500,
                      color: DS_THEME.dim2, margin: 0, textAlign: "center", lineHeight: 1.5,
                    }}>
                      Нажимая кнопку, вы соглашаетесь с <a href="/policy.html" style={{
                        color: DS_THEME.dim, borderBottom: `1px solid ${DS_THEME.dim2}`,
                      }}>политикой конфиденциальности</a>
                    </p>
                  </div>
                )}

                {!singleStep && (<>
                {/* Шаг 1 — контакты */}
                <div style={{ display: step === 1 ? "flex" : "none", flexDirection: "column", gap: 18 }}>
                  <DSFormField label="Ваше имя" name="name" required={step === 1}/>
                  <DSFormField label="Номер, привязанный к Telegram или WhatsApp" name="phone" type="tel" required={step === 1} placeholder="+420 ..."/>
                  {!hideEmail && <DSFormField label="Email" name="email" type="email"/>}
                  <DSFormField label="Сайт или Instagram бизнеса" name="url" required={step === 1}/>
                  {status === "error" && (
                    <div style={{
                      padding: "12px 14px", borderRadius: 10,
                      background: "rgba(255,122,92,.08)", border: "1px solid rgba(255,122,92,.4)",
                      fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontSize: 13, color: "#FF9A80", lineHeight: 1.5,
                    }}>{errMsg}</div>
                  )}
                  <button type="button" onClick={goNext} style={{
                    marginTop: 12, width: "100%", padding: "20px",
                    background: DS_ACCENT.gradient, color: DS_ACCENT.ink, border: "none", borderRadius: 999,
                    fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontWeight: 800, fontSize: 15,
                    letterSpacing: ".04em", textTransform: "uppercase", cursor: "pointer",
                    display: "inline-flex", justifyContent: "center", alignItems: "center", gap: 12,
                  }}>
                    Далее
                    <span style={{
                      width: 30, height: 30, borderRadius: 999, background: DS_ACCENT.ink, color: DS_ACCENT.bg,
                      display: "grid", placeItems: "center",
                    }}><DSArrow size={14}/></span>
                  </button>
                </div>

                {/* Шаг 2 — о ситуации */}
                <div style={{ display: step === 2 ? "flex" : "none", flexDirection: "column", gap: 18 }}>
                  <DSFormField label="Расскажите о вашей ситуации" name="about" required={step === 2}
                               placeholder="Что за бизнес и ниша, какие проблемы, что хотите получить"
                               textarea rows={4}/>
                  {status === "error" && (
                    <div style={{
                      padding: "12px 14px", borderRadius: 10,
                      background: "rgba(255,122,92,.08)", border: "1px solid rgba(255,122,92,.4)",
                      fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontSize: 13, color: "#FF9A80", lineHeight: 1.5,
                    }}>{errMsg}</div>
                  )}
                  <div style={{ display: "flex", gap: 12, alignItems: "stretch", marginTop: 12 }}>
                    <button type="button" aria-label="Назад" onClick={() => { setErrMsg(""); setStatus("idle"); setStep(1); }} style={{
                      width: 62, flex: "none", padding: 0, background: "transparent", color: DS_THEME.dim,
                      border: `1px solid ${DS_THEME.line2}`, borderRadius: 999, cursor: "pointer",
                      display: "grid", placeItems: "center", fontSize: 20, lineHeight: 1,
                    }}>←</button>
                    <button type="submit" disabled={status === "sending"} style={{
                      flex: 1, minWidth: 0, padding: "20px",
                      background: status === "sending" ? DS_ACCENT.bg : DS_ACCENT.gradient,
                      color: DS_ACCENT.ink, border: "none", borderRadius: 999,
                      fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontWeight: 800, fontSize: 15,
                      letterSpacing: ".04em", textTransform: "uppercase",
                      cursor: status === "sending" ? "wait" : "pointer",
                      display: "inline-flex", justifyContent: "center", alignItems: "center", gap: 12,
                      opacity: status === "sending" ? .8 : 1,
                      transition: "background .2s ease, opacity .2s ease",
                    }}>
                      {status === "sending" ? "Отправляем…" : submitLabel}
                      {status !== "sending" && (
                        <span style={{
                          width: 30, height: 30, borderRadius: 999, background: DS_ACCENT.ink, color: DS_ACCENT.bg,
                          display: "grid", placeItems: "center", flex: "none",
                        }}><DSArrow size={14}/></span>
                      )}
                    </button>
                  </div>
                  <p style={{
                    fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontSize: 12, fontWeight: 500,
                    color: DS_THEME.dim2, margin: 0, textAlign: "center", lineHeight: 1.5,
                  }}>
                    Нажимая кнопку, вы соглашаетесь с <a href="/policy.html" style={{
                      color: DS_THEME.dim, borderBottom: `1px solid ${DS_THEME.dim2}`,
                    }}>политикой конфиденциальности</a>
                  </p>
                </div>
                </>)}
              </>
            )}
          </form>
  );

  return (
    <DSSection id="Контакты" bg={DS_THEME.surface} pad="clamp(80px, 12vh, 160px)">
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 72, alignItems: "start" }} className="contact-grid">
        <div style={{ position: "sticky", top: 120 }} className="contact-left">
          <DSReveal delay={0.05}>
            <h2 style={{
              fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontWeight: 800,
              fontSize: "clamp(26px, 4.4vw, 64px)", lineHeight: .96, letterSpacing: "-.035em",
              wordSpacing: ".1em", textTransform: "uppercase", margin: "20px 0 0",
            }}>
              {heading || (<>
                Заполнить форму,<br/>
                чтобы <em style={{
                  fontFamily: "'Playfair Display', Georgia, serif", fontStyle: "italic", fontWeight: 500,
                  textTransform: "none", letterSpacing: "-.02em", color: DS_ACCENT.bg,
                }}>обсудить<br/>проект</em>
              </>)}
            </h2>
          </DSReveal>
          <DSReveal delay={0.1}>
            <p style={{
              marginTop: 28, fontFamily: "'IBM Plex Sans', system-ui, sans-serif",
              fontSize: "clamp(15px, 1.2vw, 18px)", lineHeight: 1.6, maxWidth: 460,
              color: DS_THEME.dim, margin: "28px 0 0",
            }}>
              {intro || "Встретимся с вами на 30 минут по видеозвонку. Разберём вашу ситуацию в цифрах, покажу слабые места в маркетинге и варианты, как их починить."}
            </p>
          </DSReveal>
          <DSReveal delay={0.18}>
            <div style={{ marginTop: 32, display: "flex", flexWrap: "wrap", gap: "10px 22px" }}>
              {["Без обязательств", "Отвечаю в течение дня", "Разбор бесплатный"].map((b, i) => (
                <span key={i} style={{ display: "inline-flex", alignItems: "center", gap: 8, fontFamily: "'IBM Plex Mono', ui-monospace, monospace", fontSize: 11.5, fontWeight: 500, letterSpacing: ".08em", textTransform: "uppercase", color: DS_THEME.dim }}>
                  <span style={{ width: 6, height: 6, borderRadius: 999, background: DS_ACCENT.bg, flex: "none" }}/>
                  {b}
                </span>
              ))}
            </div>
          </DSReveal>
          <DSReveal delay={0.15} className="contacts-inline">
            <div style={{
              marginTop: 40, display: "flex", flexDirection: "column",
              fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontSize: 15,
            }}>
              {contactsList.map((c, i, arr) => (
                <a key={i} href={c.href} target={c.ext ? "_blank" : undefined} rel={c.ext ? "noopener" : undefined} style={{
                  display: "flex", alignItems: "center", gap: 12, padding: "18px 0",
                  borderTop: `1px solid ${DS_THEME.line2}`,
                  borderBottom: i === arr.length - 1 ? `1px solid ${DS_THEME.line2}` : "none",
                }}>
                  <span style={{
                    fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontSize: 12, fontWeight: 600,
                    letterSpacing: ".16em", textTransform: "uppercase", color: DS_THEME.dim, width: 120,
                  }}>{c.label}</span>
                  <span style={{ color: DS_THEME.fg }}>{c.val}</span>
                </a>
              ))}
            </div>
          </DSReveal>
        </div>

        <DSReveal delay={0.1}>
          {modal ? null : formEl}
        </DSReveal>

        {/* Mobile-only: дубликат контактов под формой */}
        <DSReveal delay={0.2} className="contacts-mobile">
          <div style={{
            display: "flex", flexDirection: "column",
            fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontSize: 15,
          }}>
            <div style={{
              fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontSize: 12, fontWeight: 600,
              letterSpacing: ".18em", textTransform: "uppercase", color: DS_THEME.dim,
              marginBottom: 16,
            }}>Или напишите нам напрямую</div>
            {contactsList.map((c, i, arr) => (
              <a key={i} href={c.href} target={c.ext ? "_blank" : undefined} rel={c.ext ? "noopener" : undefined} style={{
                display: "flex", alignItems: "center", gap: 12, padding: "18px 0",
                borderTop: `1px solid ${DS_THEME.line2}`,
                borderBottom: i === arr.length - 1 ? `1px solid ${DS_THEME.line2}` : "none",
              }}>
                <span style={{
                  fontFamily: "'IBM Plex Sans', system-ui, sans-serif", fontSize: 12, fontWeight: 600,
                  letterSpacing: ".16em", textTransform: "uppercase", color: DS_THEME.dim, width: 120,
                }}>{c.label}</span>
                <span style={{ color: DS_THEME.fg }}>{c.val}</span>
              </a>
            ))}
          </div>
        </DSReveal>
      </div>
      <DSFormModal open={modal} onClose={() => setModal(false)} title="Обсудить проект" closeLabel="Закрыть">
        {formEl}
      </DSFormModal>
    </DSSection>
  );
}

// export to window so page scripts can use them
Object.assign(window, {
  DS_ACCENT, DS_THEME, DS_CSS_VARS, DS_NAV, DS_CONTACTS, DS_CTA,
  DSArrow, DSCheck, DSLogo, DSButton, DSLangSwitch, DSEyebrow, DSSection,
  DSHeader, DSFooter, DSPage, DSReveal, DSContactSection, DSFormField, DSFormModal,
});
