const $s = (selector, root = document) => root.querySelector(selector);
const $$s = (selector, root = document) => [...root.querySelectorAll(selector)];
const reduced = matchMedia("(prefers-reduced-motion: reduce)");
const motion = () =>
  !reduced.matches &&
  $s("#motion-toggle")?.getAttribute("aria-pressed") !== "true";
const content = window.CLUB_CONTENT || {};

const heroClaims = [
  { text: 'Применяли ИИ с лета 2024 и сделали ИИ-агента в начале 2026', date: 'ЛЕТО 2024 → НАЧАЛО 2026', mark: 'AI', cue: 'Пост / эфир Клуба об ИИ' },
  { text: 'Купили BTC по 16 500 в декабре 2022 или по 61 000 летом 2026', date: 'ДЕКАБРЬ 2022 → ЛЕТО 2026', mark: '₿', cue: 'Пост / эфир Клуба о BTC' },
  { text: 'Закупали доллары по 55 р. в 2022 или по 70 р. в 2026', date: '2022 → 2026', mark: '$', cue: 'Пост / эфир Клуба о валюте' },
  { text: 'Продали золото в январе 2026, с результатом до x2,5 от входа', date: 'ЯНВАРЬ 2026', mark: 'Au', cue: 'Пост / эфир Клуба о золоте' }
];
const proofClaims = [
  {
    title: 'Bitcoin: рост в 6,5 раза',
    body: 'С осени 2022, когда Bitcoin стоил <strong>$17–19 тыс.</strong>, Эван неоднократно аргументировал ожидание дальнейшего роста BTC до <strong>$100 тыс.</strong> Резиденты Клуба использовали эту информацию для формирования позиции в BTC, который вырастал до <strong>$126 тыс.</strong> — это рост в <strong>6,5 раза.</strong>',
    date: 'ОСЕНЬ 2022 → РОСТ BTC', mark: '₿', cue: 'Криптовалютный рынок на платформе'
  },
  {
    title: 'ИИ и агенты — раньше большинства',
    body: '<strong>28 июня 2024</strong> на платформе Клуба состоялся первый спецэфир на тему ИИ, а <strong>2 апреля 2026</strong> Эван провел практический эфир по ИИ-агентам, их созданию и применению, с реальными кейсами, проектами и скиллами. Резиденты Клуба начали использовать ИИ и создавать своих агентов <strong>раньше, чем 99% людей на Земле.</strong>',
    date: 'ИЮНЬ 2024 → АПРЕЛЬ 2026', mark: 'AI', cue: 'Архив эфира об ИИ'
  },
  {
    title: 'Четыре идеи на рыночном эфире',
    body: '<strong>30 апреля 2025</strong> на рыночном эфире в Клубе были выделены акции добытчиков золота (GDX), добытчиков серебра (SIL), Nvidia (NVDA) и Tesla (TSLA). К текущей дате они выросли на <strong>+118%, +101%, +108% и +110%</strong> соответственно.',
    date: '30 АПРЕЛЯ 2025', mark: '&#8599;', cue: 'Библиотека исследований Клуба'
  },
  {
    title: 'Золото: от $178 до $443',
    body: 'Про недооцененность золота Эван регулярно говорил в Клубе, а <strong>14 октября 2023</strong> записал об этом часовое видео — тогда GLD стоил <strong>$178.</strong> О фиксации золота был пост в Клубе <strong>21 января 2026</strong>, когда GLD стоил <strong>$443: рост +148%.</strong>',
    date: 'ОКТЯБРЬ 2023 → ЯНВАРЬ 2026', mark: 'Au', cue: 'Архив видео о золоте'
  },
  {
    title: 'ETH: вход и фиксация',
    body: '<strong>4 апреля 2025</strong>, на пике паники на рынках, Эван провел экстренное включение для Клуба и объяснил, почему ETH по <strong>$1400–1800</strong> выглядит особенно привлекательно. В августе того же года он объяснил, почему ETH по <strong>$4000–4800</strong> логично фиксировать: рост <strong>≈ 2,5 раза за 4 месяца.</strong>',
    date: 'АПРЕЛЬ → АВГУСТ 2025', mark: 'Ξ', cue: 'Архив включения о рынке'
  }
];
const parts = [
  ['Аналитика и идеи Эвана', 'По активам на фондовом, криптовалютном и сырьевом рынке — понятными словами, с четкой аргументацией и упором на крепкие, но недооцененные активы с потенциалом кратного роста'],
  ['100+ исследований, материалов и эфиров', 'Авторские аналитические материалы, исследования и эфиры Эвана по рынкам и финансовому миру, искусственному интеллекту, биохакингу и поведенческой экономике, полезные как новичкам, так и опытным'],
  ['ИИ-лаборатория Эвана', 'Вход в ИИ-лабораторию Эвана, в которой разрабатываются пошаговые инструкции и гайды по применению ИИ в жизни/работе/бизнесе, алгоритмы создания ИИ-агентов, готовые ИИ-навыки под важные задачи и прототипы для ИИ-аугументаций'],
  ['Прямые эфиры на самые важные темы', 'Регулярные разборы происходящего на рынках и в мире на прямых эфирах от Эвана, с его аналитикой событий, дальнейших перспектив, ответами на самые важные вопросы и конкретным планом действий'],
  ['Продвинутые ИИ-инструменты', 'Доступ к разработанным ИИ-инструментам, позволяющим видеть больше других и которые использует сам Эван в своей работе — детектор асимметричных возможностей, ИИ-анализатор новостей, выявитель трендов и другие'],
  ['Авторские графики с пояснениями', 'Не имеющие аналогов синтезированные и производные графики по важнейшим активам на рынках — с разметкой, ключевыми уровнями и комментариями от Эвана, а также интерактивная система уведомлений'],
  ['Сообщество и живые встречи', 'Резиденты Клуба — это взрослые люди со всех уголков планеты, с которыми вам будет интересно, полезно, комфортно и с которыми вы сможете познакомиться лично на масштабных и ярких живых встречах Клуба Мультипликатор']
];
const platformMedia = [
  ['https://fs.getcourse.ru/fileservice/file/download/a/352026/sc/270/h/5eff3b7555378d9f3fd2ef97e1d1845b.webp', 'Разбор рынков и сложной фазы'],
  ['https://fs.getcourse.ru/fileservice/file/download/a/352026/sc/184/h/b7e30152d28566abbc2a15856c4dac67.webp', 'Библиотека исследований'],
  ['https://fs.getcourse.ru/fileservice/file/download/a/352026/sc/281/h/2d4d3c60d19cc24e621480f33eec8aed.webp', 'Инструкция по созданию ИИ-агента'],
  ['https://fs.getcourse.ru/fileservice/file/download/a/352026/sc/15/h/3010ed80145fda4d7261f7968975874a.webp', 'Стратегический эфир Эвана с графиком рынка'],
  ['https://fs.getcourse.ru/fileservice/file/download/a/352026/sc/501/h/7d77088218686917ef79b6791138efa0.webp', 'ИИ-синтез ключевых новостей платформы'],
  ['https://fs.getcourse.ru/fileservice/file/download/a/352026/sc/56/h/705441fe10ea983651f31c9f42e05e97.webp', 'Авторские графики и показатели платформы'],
  ['https://fs.getcourse.ru/fileservice/file/download/a/352026/sc/418/h/1366e3ac40e43ceced3aa4d2fc2d7ed4.webp', 'Встреча сообщества Клуба']
];

function proofMedia(item, i) {
  return `<button class="proof-media" type="button" aria-label="Увеличить изображение результата ${i + 1}"><div class="media-top"><span>МУЛЬТИПЛИКАТОР / АРХИВ</span><span>${String(i + 1).padStart(2, "0")} / ${String(proofClaims.length).padStart(2, "0")}</span></div><div class="media-center"><span>${item.mark}</span><small>${item.cue}</small></div></button>`;
}
function renderProofs() {
  $s("#proof-list").innerHTML = proofClaims
    .map(
      (item, i) =>
        `<article class="proof reveal" data-proof="${i}"><div class="proof-text"><span class="proof-number">${String(i + 1).padStart(2, "0")} / ${String(proofClaims.length).padStart(2, "0")}</span><h3>${item.title}</h3><p class="proof-body">${item.body}</p><p class="proof-meta">${item.date}</p></div><div class="proof-mobile">${proofMedia(item, i)}</div></article>`,
    )
    .join("");
  $s("#proof-stack").innerHTML = proofClaims
    .map(
      (item, i) =>
        `<div class="proof-sheet" data-sheet="${i}">${proofMedia(item, i)}</div>${i < proofClaims.length - 1 ? '<div class="proof-gap" aria-hidden="true"></div>' : ""}`,
    )
    .join("");
}
function renderParts() {
  $s("#system-parts").innerHTML = parts
    .map(([title, desc], i) => {
      const media = platformMedia[i];
      const visual = media
        ? `<button class="platform-v2-open" type="button" aria-label="Увеличить изображение: ${title}"><span class="platform-v2-screen"><img src="${media[0]}" alt="${media[1]}" loading="lazy" decoding="async"></span></button>`
        : `<div class="platform-v2-pending" role="img" aria-label="Скриншот авторских графиков ожидается"><div class="platform-v2-pending-grid" aria-hidden="true"></div><span>АВТОРСКИЕ ГРАФИКИ</span><strong>Скриншот<br>готовится</strong></div>`;
      return `<article class="platform-v2-card" data-part="${i}" data-title="${title}" data-description="${desc}" aria-label="${i + 1} из 7: ${title}">${visual}</article>`;
    })
    .join("");
}
renderProofs();
renderParts();
const platformLightbox = $s("#lightbox");
const platformLightboxItems = platformMedia
  .map(
    (media, part) =>
      media && {
        part,
        src: media[0],
        alt: media[1],
        caption: parts[part][0],
        live: part === 3,
      },
  )
  .filter(Boolean);
const proofLightboxItems = (Array.isArray(content.proofs) ? content.proofs : [])
  .slice(0, proofClaims.length)
  .filter((item) => item?.src && item.alt)
  .map((item) => ({
    src: item.src,
    alt: item.alt,
    caption: [item.date, item.source, item.caption].filter(Boolean).join(" · "),
  }));
let lightboxItems = [];
let lightboxIndex = 0;
let lightboxOpener = null;
function renderLightbox() {
  const item = lightboxItems[lightboxIndex];
  if (!item) return;
  $s("#lightbox-image").src = item.src;
  $s("#lightbox-image").alt = item.alt;
  $s("#lightbox-caption").textContent = item.caption || item.alt;
  $s("#lightbox-count").textContent =
    `${String(lightboxIndex + 1).padStart(2, "0")} / ${String(lightboxItems.length).padStart(2, "0")}`;
  $s("#lightbox-navigation").hidden = lightboxItems.length < 2;
  platformLightbox.classList.toggle("is-platform-live", Boolean(item.live));
}
function openLightbox(items, index, opener) {
  lightboxItems = items;
  lightboxIndex = index;
  lightboxOpener = opener;
  renderLightbox();
  platformLightbox.showModal();
}
function stepLightbox(delta) {
  if (!platformLightbox.open || lightboxItems.length < 2) return;
  lightboxIndex =
    (lightboxIndex + delta + lightboxItems.length) % lightboxItems.length;
  renderLightbox();
}
$s("#lightbox-prev")?.addEventListener("click", () => stepLightbox(-1));
$s("#lightbox-next")?.addEventListener("click", () => stepLightbox(1));
platformLightbox?.addEventListener("keydown", (event) => {
  if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
  event.preventDefault();
  stepLightbox(event.key === "ArrowLeft" ? -1 : 1);
});
platformLightbox?.addEventListener("close", () => lightboxOpener?.focus());
let lightboxTouchX = 0;
$s("#lightbox-image")?.addEventListener(
  "touchstart",
  (event) => {
    lightboxTouchX = event.changedTouches[0].clientX;
  },
  { passive: true },
);
$s("#lightbox-image")?.addEventListener(
  "touchend",
  (event) => {
    const delta = event.changedTouches[0].clientX - lightboxTouchX;
    if (Math.abs(delta) > 45) stepLightbox(delta < 0 ? 1 : -1);
  },
  { passive: true },
);
/*$s("#system-parts").addEventListener("click", (event) => {
  const button = event.target.closest(".platform-v2-open");
  if (
    !button ||
    button.disabled ||
    !$s("#platform").classList.contains("is-image-visible")
  )
    return;
  const card = button.closest(".platform-v2-card");
  const index = platformLightboxItems.findIndex(
    (item) => item.part === Number(card.dataset.part),
  );
  if (index >= 0) openLightbox(platformLightboxItems, index, button);
});*/
if (Array.isArray(content.proofs))
  content.proofs.slice(0, proofClaims.length).forEach((asset, i) => {
    if (!asset?.src || !asset.alt) return;
    $$s(
      `[data-proof="${i}"] .proof-media, [data-sheet="${i}"] .proof-media`,
    ).forEach((media) => {
      const image = document.createElement("img");
      image.src = asset.src;
      image.alt = asset.alt;
      image.loading = "lazy";
      const label = document.createElement("span");
      label.className = "proof-image-caption";
      label.textContent = asset.preview || asset.caption || "";
      media.setAttribute("aria-label", `Увеличить изображение: ${asset.alt}`);
      /*media.addEventListener("click", () => {
        const index = proofLightboxItems.findIndex(
          (item) => item.src === asset.src,
        );
        if (index >= 0) openLightbox(proofLightboxItems, index, media);
      });*/
      media.replaceChildren(image, label);
      media.classList.add("has-proof");
    });
  });

// The source claims rotate without visible controls. Numerals are the only accent.
let claimIndex = 0,
  claimTimer = 0,
  claimPaused = false,
  claimAnimating = false,
  claimInitialized = false,
  swapTimer = 0;
const claimText = $s("#claim-text");
function claimMarkup(index) {
  return heroClaims[index].text.replace(
    /(\+250%|16 500|61 000|2024|2026|2022|55 р\.|70 р\.)/g,
    "<strong>$1</strong>",
  );
}
function showClaim(next) {
  if (claimAnimating) {
    clearTimeout(swapTimer);
    claimAnimating = false;
    claimText.classList.remove("changing");
  }
  claimIndex = (next + heroClaims.length) % heroClaims.length;
  const swap = () => {
    claimText.innerHTML = claimMarkup(claimIndex);
    claimText.classList.remove("changing");
    claimAnimating = false;
  };
  if (motion() && claimInitialized) {
    claimAnimating = true;
    claimText.classList.add("changing");
    swapTimer = setTimeout(swap, 240);
  } else swap();
  claimInitialized = true;
  restartClaimTimer();
}
function restartClaimTimer() {
  clearTimeout(claimTimer);
  if (motion() && !claimPaused && !document.hidden)
    claimTimer = setTimeout(() => showClaim(claimIndex + 1), 4000);
}
$s(".claim-stage").addEventListener("mouseenter", () => {
  claimPaused = true;
  restartClaimTimer();
});
$s(".claim-stage").addEventListener("mouseleave", () => {
  claimPaused = false;
  restartClaimTimer();
});
document.addEventListener("visibilitychange", restartClaimTimer);
reduced.addEventListener("change", restartClaimTimer);
showClaim(0);

// Reveals are cues to reading order, never gates for the actual text.
const revealObserver = new IntersectionObserver(
  (entries) =>
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        entry.target.classList.add("is-visible");
        revealObserver.unobserve(entry.target);
      }
    }),
  { threshold: 0.13 },
);
$$s(".price-card").forEach((el) => el.classList.add("will-reveal"));
$$s(".proof,.price-card,.letter-page").forEach((el) =>
  revealObserver.observe(el),
);
const letterSection = $s("#letter");
letterSection.classList.add("is-prepared");
revealObserver.observe(letterSection);
$$s(".member-pass").forEach((pass) => {
  const dots = pass.querySelector(".pass-dots");
  pass.addEventListener(
    "pointermove",
    (event) => {
      const rect = dots.getBoundingClientRect();
      dots.style.setProperty(
        "--dot-x",
        `${(((event.clientX - rect.left) / rect.width) * 100).toFixed(1)}%`,
      );
      dots.style.setProperty(
        "--dot-y",
        `${(((event.clientY - rect.top) / rect.height) * 100).toFixed(1)}%`,
      );
    },
    { passive: true },
  );
  pass.addEventListener("pointerleave", () => {
    dots.style.removeProperty("--dot-x");
    dots.style.removeProperty("--dot-y");
  });
});
document.addEventListener("club:motion", restartClaimTimer);

// Optional verified assets can be supplied as window.CLUB_CONTENT in a separate file.
if (Array.isArray(content.reviews) && content.reviews.length) {
  const reviews = content.reviews.filter((item) => item.text && item.name);
  // Author levels, ratings, and dates verified against the supplied Yandex Maps reviews.
  const reviewDetails = {
    Владислав: { level: 3, rating: 5, date: "3 июня" },
    "Ольга К": { level: 2, rating: 5, date: "17 августа 2025" },
    "Инесса Колоколова": { level: 14, rating: 5, date: "9 июня" },
    "К А": { level: 3, rating: 5, date: "11 сентября" },
    "Александр К.": { level: 7, rating: 5, date: "6 октября" },
    "Софья Т.": { level: 6, rating: 5, date: "19 апреля" },
    "Дамир Рахимьянов": { level: 4, rating: 5, date: "4 августа" },
    Лилия: { level: 5, rating: 5, date: "31 июля" },
    "Leonid Provorov": { level: 4, rating: 5, date: "29 июня" },
    "Любовь С.": { level: 3, rating: 5, date: "23 сентября" },
    "В М": { level: 10, rating: 5, date: "7 апреля" },
    "Petr Samonchev": { level: 5, rating: 5, date: "6 августа 2025" },
    Абый: { level: 4, rating: 5, date: "26 июля 2025" },
    "Сергей Белецкий": { level: 3, rating: 5, date: "22 июля" },
    "Fff V.": { level: 3, rating: 5, date: "16 сентября" },
    "Павел П.": { level: 2, rating: 5, date: "29 сентября" },
  };
  const authorLevel = (item) =>
    reviewDetails[item.name]?.level
      ? `Знаток города ${reviewDetails[item.name].level} уровня`
      : "Отзыв на Яндекс Картах";
  function reviewMeta(item) {
    const details = reviewDetails[item.name];
    const meta = document.createElement("div");
    meta.className = "review-meta";
    if (details?.rating) {
      const stars = document.createElement("span");
      stars.className = "review-stars";
      stars.textContent = "★".repeat(details.rating);
      stars.setAttribute("aria-label", `Оценка: ${details.rating} из 5`);
      meta.append(stars);
    }
    if (details?.date) {
      const date = document.createElement("span");
      date.className = "review-date";
      date.textContent = details.date;
      meta.append(date);
    }
    if (!details) {
      const source = document.createElement("span");
      source.className = "review-date";
      source.textContent = "Отзыв на Яндекс Картах";
      meta.append(source);
    }
    return meta;
  }
  const stage = $s(".reviews-stage");
  const toolbar = document.createElement("div");
  toolbar.className = "review-toolbar";
  const intro = document.createElement("p");
  intro.className = "review-intro";
  intro.textContent = "Личный опыт и истории Резидентов Клуба Мультипликатор";
  const controls = document.createElement("div");
  controls.className = "review-controls";
  const previous = document.createElement("button");
  previous.type = "button";
  previous.className = "review-arrow";
  previous.innerHTML =
    '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg>';
  previous.setAttribute("aria-label", "Предыдущие отзывы");
  const next = document.createElement("button");
  next.type = "button";
  next.className = "review-arrow";
  next.innerHTML =
    '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m9 18 6-6-6-6"/></svg>';
  next.setAttribute("aria-label", "Следующие отзывы");
  controls.append(previous, next);
  toolbar.append(intro);
  const rail = document.createElement("div");
  rail.className = "review-rail";
  rail.tabIndex = 0;
  rail.setAttribute("role", "region");
  rail.setAttribute("aria-label", "Отзывы резидентов Клуба");
  const railShell = document.createElement("div");
  railShell.className = "review-rail-shell";
  railShell.append(rail, controls);
  const pagination = document.createElement("div");
  pagination.className = "review-pagination";
  pagination.setAttribute("role", "group");
  pagination.setAttribute("aria-label", "Страницы отзывов");
  const dialog = document.createElement("dialog");
  dialog.className = "review-dialog";
  dialog.setAttribute("aria-labelledby", "review-dialog-name");
  const dialogHead = document.createElement("div");
  dialogHead.className = "review-dialog-head";
  const dialogAvatar = document.createElement("img");
  dialogAvatar.className = "review-avatar";
  dialogAvatar.alt = "";
  dialogAvatar.width = 56;
  dialogAvatar.height = 56;
  const dialogIdentity = document.createElement("div");
  const dialogName = document.createElement("h3");
  dialogName.id = "review-dialog-name";
  const dialogSource = document.createElement("span");
  dialogSource.className = "review-source";
  dialogIdentity.append(dialogName, dialogSource);
  const close = document.createElement("button");
  close.type = "button";
  close.className = "review-dialog-close";
  close.textContent = "×";
  close.setAttribute("aria-label", "Закрыть отзыв");
  close.addEventListener("click", () => dialog.close());
  dialogHead.append(dialogAvatar, dialogIdentity);
  const dialogQuote = document.createElement("blockquote");
  dialogQuote.className = "review-dialog-quote";
  const dialogMeta = document.createElement("div");
  dialogMeta.className = "review-dialog-meta";
  const dialogPanel = document.createElement("div");
  dialogPanel.className = "modal-surface review-dialog-surface";
  const reviewNav = document.createElement("div");
  reviewNav.className = "review-dialog-navigation";
  reviewNav.setAttribute("role", "group");
  reviewNav.setAttribute("aria-label", "Просмотр отзывов");
  const reviewCount = document.createElement("span");
  reviewCount.className = "modal-navigation-count";
  reviewCount.setAttribute("aria-live", "polite");
  const reviewPrev = document.createElement("button");
  reviewPrev.type = "button";
  reviewPrev.className = "icon-button";
  reviewPrev.setAttribute("aria-label", "Предыдущий отзыв");
  reviewPrev.innerHTML =
    '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m14 5-7 7 7 7"/></svg>';
  const reviewNext = document.createElement("button");
  reviewNext.type = "button";
  reviewNext.className = "icon-button";
  reviewNext.setAttribute("aria-label", "Следующий отзыв");
  reviewNext.innerHTML =
    '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m10 5 7 7-7 7"/></svg>';
  reviewNav.append(reviewCount, reviewPrev, reviewNext);
  dialogPanel.append(dialogHead, dialogMeta, dialogQuote, reviewNav);
  dialog.append(close, dialogPanel);
  let openingButton;
  let reviewIndex = 0;
  function renderReview(index) {
    reviewIndex = (index + reviews.length) % reviews.length;
    const item = reviews[reviewIndex];
    dialogAvatar.src = item.avatar;
    dialogName.textContent = item.name;
    dialogSource.textContent = authorLevel(item);
    dialogMeta.replaceChildren(reviewMeta(item));
    dialogQuote.textContent = item.text;
    reviewCount.textContent = `${String(reviewIndex + 1).padStart(2, "0")} / ${String(reviews.length).padStart(2, "0")}`;
    reviewNav.hidden = reviews.length < 2;
    dialogPanel.scrollTop = 0;
  }
  reviewPrev.addEventListener("click", () => renderReview(reviewIndex - 1));
  reviewNext.addEventListener("click", () => renderReview(reviewIndex + 1));
  dialog.addEventListener("keydown", (event) => {
    if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
    event.preventDefault();
    renderReview(reviewIndex + (event.key === "ArrowLeft" ? -1 : 1));
  });
  dialog.addEventListener("close", () => openingButton?.focus());
  reviews.forEach((item, index) => {
    const card = document.createElement("article");
    card.className = "review-card";
    const top = document.createElement("div");
    top.className = "review-card-top";
    const avatar = document.createElement("img");
    avatar.className = "review-avatar";
    avatar.src = item.avatar;
    avatar.alt = "";
    avatar.width = 56;
    avatar.height = 56;
    avatar.loading = "lazy";
    const identity = document.createElement("div");
    identity.className = "review-identity";
    const name = document.createElement("h3");
    name.textContent = item.name;
    const source = document.createElement("span");
    source.className = "review-source";
    source.textContent = authorLevel(item);
    identity.append(name, source);
    const provider = document.createElement("span");
    provider.className = "review-provider";
    provider.innerHTML =
      '<span class="review-provider-mark" aria-hidden="true">●</span>Яндекс Карты';
    top.append(avatar, identity, provider);
    const meta = reviewMeta(item);
    const quote = document.createElement("blockquote");
    quote.className = "review-card-quote";
    const plain = item.text.replace(/\s+/g, " ").trim();
    const cutoff =
      plain.length > 390 ? plain.lastIndexOf(" ", 390) : plain.length;
    quote.textContent =
      plain.slice(0, cutoff) + (cutoff < plain.length ? "…" : "");
    const footer = document.createElement("div");
    footer.className = "review-card-footer";
    const read = document.createElement("button");
    read.type = "button";
    read.className = "review-read";
    read.textContent = "Читать отзыв";
    read.setAttribute("aria-label", `Читать полный отзыв: ${item.name}`);
    read.addEventListener("click", () => {
      openingButton = read;
      renderReview(index);
      dialog.showModal();
      close.focus();
    });
    footer.append(read);
    card.append(top, meta, quote, footer);
    rail.append(card);
  });
  let activeReview = 0;
  const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
  const reviewCards = [...rail.children];
  const dots = reviews.map((_, index) => {
    const dot = document.createElement("button");
    dot.type = "button";
    dot.className = "review-dot";
    dot.setAttribute(
      "aria-label",
      `Показать отзыв ${index + 1}: ${reviews[index].name}`,
    );
    dot.addEventListener("click", () => goTo(index));
    pagination.append(dot);
    return dot;
  });
  function goTo(index) {
    activeReview = clamp(index, 0, reviews.length - 1);
    previous.disabled = activeReview === 0;
    next.disabled = activeReview === reviews.length - 1;
    reviewCards.forEach((card, cardIndex) => {
      const offset = cardIndex - activeReview;
      const visible = Math.abs(offset) <= 1;
      card.classList.toggle("is-active", offset === 0);
      card.classList.toggle("is-before", offset === -1);
      card.classList.toggle("is-after", offset === 1);
      card.setAttribute("aria-hidden", String(!visible));
      card.inert = !visible;
      if (visible && offset !== 0) {
        card.setAttribute(
          "aria-label",
          `Следующий отзыв: ${reviews[cardIndex].name}`,
        );
      } else card.removeAttribute("aria-label");
    });
    dots.forEach((dot, dotIndex) => {
      const selected = dotIndex === activeReview;
      dot.classList.toggle("is-active", selected);
      dot.setAttribute("aria-current", String(selected));
    });
  }
  previous.addEventListener("click", () => goTo(activeReview - 1));
  next.addEventListener("click", () => goTo(activeReview + 1));
  reviewCards.forEach((card, index) =>
    card.addEventListener("click", (event) => {
      if (index !== activeReview && !event.target.closest("button"))
        goTo(index);
    }),
  );
  rail.addEventListener("keydown", (event) => {
    if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
      event.preventDefault();
      goTo(activeReview + (event.key === "ArrowLeft" ? -1 : 1));
    }
  });
  let touchX = null;
  rail.addEventListener(
    "touchstart",
    (event) => {
      touchX = event.changedTouches[0]?.clientX ?? null;
    },
    { passive: true },
  );
  rail.addEventListener(
    "touchend",
    (event) => {
      if (touchX === null) return;
      const delta = event.changedTouches[0].clientX - touchX;
      if (Math.abs(delta) > 45) goTo(activeReview + (delta < 0 ? 1 : -1));
      touchX = null;
    },
    { passive: true },
  );
  stage.replaceChildren(toolbar, railShell, pagination);
  stage.classList.add("has-reviews");
  stage.after(dialog);
  goTo(0);
}
const photos = Array.isArray(content.meetingPhotos)
  ? content.meetingPhotos.filter((p) => p.src && p.alt)
  : [];
const galleryDisplay = $s("#gallery-display");
const gallerySlides = Array.from(
  { length: Math.ceil(photos.length / 6) },
  (_, i) => photos.slice(i * 6, i * 6 + 6),
);
let galleryIndex = 0;
const galleryDots = gallerySlides.map((_, index) => {
  const dot = document.createElement("button");
  dot.type = "button";
  dot.className = "gallery-dot review-dot";
  dot.setAttribute(
    "aria-label",
    `Показать фотографии ${index * 6 + 1}–${Math.min((index + 1) * 6, photos.length)}`,
  );
  dot.addEventListener("click", () => showGallerySlide(index));
  $s("#gallery-pagination").append(dot);
  return dot;
});
function showGallerySlide(index) {
  if (!gallerySlides.length || index < 0 || index >= gallerySlides.length)
    return;
  galleryIndex = index;
  const sheet = document.createElement("div");
  sheet.className = "gallery-sheet";
  sheet.setAttribute(
    "aria-label",
    `Фотографии ${index * 6 + 1}–${Math.min((index + 1) * 6, photos.length)} из ${photos.length}`,
  );
  gallerySlides[index].forEach((photo, position) => {
    const figure = document.createElement("figure");
    figure.className = "gallery-photo";
    const image = document.createElement("img");
    image.src = photo.src;
    image.alt = photo.alt;
    image.dataset.photo = photo.file;
    image.loading = "lazy";
    image.style.setProperty("--photo-order", position);
    figure.append(image);
    sheet.append(figure);
  });
  galleryDisplay.replaceChildren(sheet);
  $s("#gallery-count").textContent =
    `${String(index + 1).padStart(2, "0")} / ${String(gallerySlides.length).padStart(2, "0")}`;
  $s("#gallery-prev").disabled = index === 0;
  $s("#gallery-next").disabled = index === gallerySlides.length - 1;
  galleryDots.forEach((dot, position) => {
    const selected = position === index;
    dot.classList.toggle("is-active", selected);
    dot.setAttribute("aria-current", String(selected));
  });
}
showGallerySlide(0);
$s("#gallery-prev").addEventListener("click", () =>
  showGallerySlide(galleryIndex - 1),
);
$s("#gallery-next").addEventListener("click", () =>
  showGallerySlide(galleryIndex + 1),
);
galleryDisplay.addEventListener("keydown", (event) => {
  if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
    event.preventDefault();
    showGallerySlide(galleryIndex + (event.key === "ArrowLeft" ? -1 : 1));
  }
});
$s("#lightbox-close")?.addEventListener("click", () => $s("#lightbox").close());
let swipeStart = 0;
galleryDisplay.addEventListener(
  "touchstart",
  (e) => {
    swipeStart = e.changedTouches[0].clientX;
  },
  { passive: true },
);
galleryDisplay.addEventListener(
  "touchend",
  (e) => {
    if (Math.abs(e.changedTouches[0].clientX - swipeStart) > 40)
      showGallerySlide(
        galleryIndex + (e.changedTouches[0].clientX < swipeStart ? 1 : -1),
      );
  },
  { passive: true },
);
if (Array.isArray(content.founderLetter) && content.founderLetter.length) {
  const page = $s(".letter-page");
  page.querySelectorAll("p").forEach((p) => p.remove());
  content.founderLetter.forEach((paragraph, index) => {
    const p = document.createElement("p");
    p.textContent = paragraph;
    p.style.setProperty("--letter-line", index);
    $s(".letter-sign").before(p);
  });
}
if (content.founderPhoto?.src) {
  const image = document.createElement("img");
  image.src = content.founderPhoto.src;
  image.alt = content.founderPhoto.alt || "Эван Голованов за работой";
  image.loading = "lazy";
  $s(".letter-photo-slot").replaceWith(image);
  image.className = "founder-image";
}
if (Array.isArray(content.faq) && content.faq.length) {
  const list = $s(".faq-topics");
  list.replaceChildren();
  content.faq.forEach(([question, answer]) => {
    if (!question || !answer) return;
    const details = document.createElement("details"),
      summary = document.createElement("summary"),
      p = document.createElement("p");
    summary.textContent = question;
    p.textContent = answer;
    details.append(summary, p);
    list.append(details);
  });
}

// A plan selection opens the same request form from the pass or its CTA.
const joinDialog = $s("#join-dialog");
const joinForm = $s("#join-form");
const joinStatus = $s("#join-status");
const planCards = $$s(".pricing .price-card");
let selectedPlan = null;
let joinOpener = null;
function openJoin(card, opener) {
  selectedPlan =
    card.dataset.tier === "half"
      ? { term: "6 месяцев", price: "49 900 ₽" }
      : { term: "12 месяцев", price: "69 900 ₽" };
  joinOpener = opener;
  joinStatus.textContent = "";
  joinStatus.classList.remove("error");
  joinDialog.showModal();
  $s("#join-name").focus();
}
planCards.forEach((card) => {
  const button = card.querySelector(".plan-join");
    const ltModalId = card.getAttribute("data-modal-id");
  button.addEventListener("click", (event) => {
    event.stopPropagation();
    ltShowModalBlock(ltModalId);
  });
  card.addEventListener("click", (event) => {
    if (event.target.closest("a, button, input, textarea, select")) return;
    ltShowModalBlock(ltModalId);
  });
});
joinDialog?.addEventListener("close", () => joinOpener?.focus());
joinForm?.addEventListener("submit", async (event) => {
  event.preventDefault();
  joinStatus.classList.remove("error");
  if (!joinForm.reportValidity()) return;
  const values = Object.fromEntries(new FormData(joinForm));
  values.plan = selectedPlan?.term || "12 месяцев";
  values.price = selectedPlan?.price || "69 900 ₽";
  const endpoint = content.joinEndpoint;
  if (endpoint) {
    const submit = joinForm.querySelector('[type="submit"]');
    submit.disabled = true;
    joinStatus.textContent = "Отправляем заявку…";
    try {
      const response = await fetch(endpoint, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(values),
      });
      if (!response.ok) throw new Error("delivery-failed");
      joinForm.reset();
      joinStatus.textContent =
        "Заявка отправлена. Специалист Клуба свяжется с вами.";
    } catch {
      joinStatus.textContent =
        "Не удалось отправить заявку. Свяжитесь с Клубом по телефону +7 499 301 79 00.";
      joinStatus.classList.add("error");
    } finally {
      submit.disabled = false;
    }
    return;
  }
  const subject = encodeURIComponent(
    `Заявка в Клуб Мультипликатор — ${values.plan}`,
  );
  const body = encodeURIComponent(
    `Здравствуйте! Хочу вступить в Клуб Мультипликатор.\nСрок: ${values.plan}\nСтоимость: ${values.price}\nИмя: ${values.name}\nТелефон: ${values.phone}\nЭл. почта: ${values.email}`,
  );
  joinStatus.textContent =
    "Откроется почтовое приложение с вашей заявкой. Проверьте письмо и отправьте его.";
  location.href = `mailto:hello@multievan.ru?subject=${subject}&body=${body}`;
});
$s("#dialog-close")?.addEventListener("click", () => joinDialog.close());
[joinDialog, $s("#lightbox")].forEach((dialog) =>
  dialog?.addEventListener("click", (e) => {
    if (e.target === dialog) dialog.close();
  }),
);

const form = $s("#contact-form"),
  status = $s("#form-status");
if (content.consultationEndpoint)
  form.dataset.endpoint = content.consultationEndpoint;
const legalDialog = $s("#legal-dialog");
const legalTitles = {
  offer: "Лицензионный договор-оферта",
  privacy: "Политика обработки персональных данных",
};
let legalOpener = null;
/*$$s(".footer-legal [data-legal]").forEach((button) =>
  button.addEventListener("click", () => {
    const key = button.dataset.legal;
    legalOpener = button;
    $s("#legal-dialog-title").textContent = legalTitles[key];
    const body = $s("#legal-dialog-body");
    body.replaceChildren();
    const publishedText = content.legalText?.[key];
    const paragraphs = Array.isArray(publishedText)
      ? publishedText.filter(Boolean)
      : [];
    if (paragraphs.length) {
      paragraphs.forEach((paragraph) => {
        const p = document.createElement("p");
        p.textContent = paragraph;
        body.append(p);
      });
    } else {
      const p = document.createElement("p");
      p.className = "legal-pending";
      p.textContent =
        "Утверждённая редакция документа пока не загружена в эту версию сайта. Запросите действующий текст у команды Клуба до оформления участия: hello@multievan.ru.";
      body.append(p);
    }
    legalDialog.showModal();
    $s("#legal-dialog-close").focus();
  }),
);*/
/*$s("#legal-dialog-close").addEventListener("click", () => legalDialog.close());
legalDialog.addEventListener("click", (event) => {
  if (event.target === legalDialog) legalDialog.close();
});
legalDialog.addEventListener("close", () => legalOpener?.focus());
*/
/* form.addEventListener('submit', async event => {
  event.preventDefault(); status.classList.remove('error');
  const endpoint = form.dataset.endpoint;
  if (!endpoint) {
    status.textContent = 'Отправка формы пока не подключена. Свяжитесь с Клубом по адресу hello@multievan.ru или по телефону +7 499 301 79 00.';
    status.classList.add('error'); return;
  }
  const values = Object.fromEntries(new FormData(form));
  if (!String(values.name || '').trim() || !String(values.question || '').trim() || (!String(values.phone || '').trim() && !String(values.email || '').trim())) {
    status.textContent = 'Укажите имя, вопрос и телефон или эл. почту.'; status.classList.add('error'); return;
  }
  if (values.email && !form.elements.email.checkValidity()) {
    status.textContent = 'Проверьте адрес электронной почты.'; status.classList.add('error'); return;
  }
  const submit = form.querySelector('[type="submit"]'); submit.disabled = true; status.textContent = 'Отправляем запрос…';
  try {
    const response = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(values) });
    if (!response.ok) throw new Error('delivery-failed');
    form.reset(); status.textContent = 'Запрос принят. Специалист Клуба свяжется с вами.';
  } catch {
    status.textContent = 'Не удалось отправить запрос. Проверьте соединение или свяжитесь с Клубом напрямую.'; status.classList.add('error');
  } finally { submit.disabled = false; }
}); */
const heroJoin = $s(".hero-actions .primary");
const plans = $s("#plans");
function jumpToPlans(event) {
  event.preventDefault();
  history.replaceState(null, "", "#plans");
  scrollTo({
    top: scrollY + plans.getBoundingClientRect().top,
    behavior: "instant",
  });
  updateScroll();
  const cards = $$s(".pricing .price-card");
  if (motion()) {
    cards.forEach((card) => card.classList.remove("is-visible"));
    void plans.offsetHeight;
    requestAnimationFrame(() =>
      cards.forEach((card) => card.classList.add("is-visible")),
    );
  } else cards.forEach((card) => card.classList.add("is-visible"));
}
heroJoin.addEventListener("click", jumpToPlans);
$$s(".v3-section-cta").forEach((link) =>
  link.addEventListener("click", jumpToPlans),
);
function updateScroll() {
  document.body.classList.toggle('has-scrolled', scrollY > 4);
  const backToTop = $('#back-to-top');
  /*if (backToTop) {
    backToTop.classList.toggle('is-visible', scrollY > innerHeight * .6);
    const available = Math.max(1, document.documentElement.scrollHeight - innerHeight);
    backToTop.style.setProperty('--mp-scroll-progress', `${Math.min(360, scrollY / available * 360)}deg`);
  }*/
}
/*$('#back-to-top')?.addEventListener('click', () => scrollTo({ top: 0, behavior: reduced.matches ? 'instant' : 'smooth' }));
addEventListener('scroll', updateScroll, { passive: true });
addEventListener('resize', updateScroll, { passive: true });
updateScroll();*/
$s("#year").textContent = new Date().getFullYear();
