// instruction.jsx — Step 2: Видео-инструкция от Дениса с главами + полными текстами из ТЗ

const INSTRUCTION_CHAPTERS = [
  {
    t: "Вступление",
    d: "1:24",
    s: "Денис рассказывает, зачем смотреть эту инструкцию и как она устроена.",
    body: [
      "Всем привет! Это Денис и Kiwi Education. Мы решили записать эту инструкцию, чтобы упростить нам ход консультации и улучшить её продуктивность.",
      "Скорее всего, вы знаете общую базовую информацию о системе переезда в Новую Зеландию, но я всё же рекомендую посмотреть эту небольшую, но ёмкую инструкцию. Она поможет расставить всё по полочкам и даст ответы на фундаментальные вопросы.",
      "Слушайте внимательно, читайте, смотрите, ставьте на паузу — как вам удобнее. И не обращайте внимания, если я меняюсь от видео к видео или даже старею — это всё для обновления кусочков, дабы держать вас в курсе самого актуального. Погнали.",
    ],
  },
  {
    t: "Гражданство и паспорт",
    d: "2:15",
    s: "Верхняя ступень лестницы — что нужно, чтобы получить паспорт Новой Зеландии.",
    body: [
      "Давайте представим всю иммиграционную систему Новой Зеландии в виде лестницы. И начнём с самой верхней ступени — получения гражданства и новозеландского паспорта.",
      "Чтобы претендовать на гражданство, необходимо прожить в Новой Зеландии не менее 5 лет в статусе резидента — то есть имея резидентскую визу. Это местный аналог постоянного места жительства, ПМЖ.",
      "При этом важно соблюдать условие по пребыванию в стране: не выезжать за пределы Новой Зеландии более 125 дней в течение каждого из этих 5 лет. Если больше — срок получения гражданства будет сдвигаться.",
      "После выполнения этих условий вы можете подать заявление на получение гражданства, а затем получить и паспорт Новой Зеландии.",
    ],
  },
  {
    t: "Варианты получения резидентской визы",
    d: "2:40",
    s: "Инвестиционная, бизнес и профессиональная категории — кратко о каждой.",
    body: [
      "Теперь опустимся на ступень ниже — к получению резидентства. Существует несколько основных категорий получения резидентской визы.",
      "Инвестиционная категория предполагает вложение от 5 миллионов новозеландских долларов в экономику страны на несколько лет.",
      "Бизнес-категория обычно требует инвестиций от 1 миллиона долларов в покупку существующего бизнеса или доли в новозеландской компании.",
      "Если вам интересно узнать подробнее об этих вариантах, сообщите нам — мы свяжем вас с профессиональным иммиграционным адвайзером или юристом, который сможет подробно проконсультировать именно по вашей ситуации.",
      "Далее мы сосредоточимся на профессиональной категории иммиграции, так как именно она подходит большинству специалистов.",
    ],
  },
  {
    t: "Профессиональная категория",
    d: "6:12",
    s: "Три пути к резидентству: Green List, балльная система и опыт работы.",
    body: [
      "Податься на резидентскую визу по профессиональной категории можно до 55 лет включительно. То есть заявление необходимо подать до того, как вам исполнится 56 лет.",
      "Главный критерий — работа в Новой Зеландии у аккредитованного работодателя. Это не любой работодатель в стране, а только тот, который получил официальное разрешение на найм иностранных специалистов. Однако таких компаний в Новой Зеландии достаточно много.",
      "Также работа должна считаться квалифицированной — обычно это означает соответствие вашему образованию или подтверждённому опыту. В некоторых случаях требуется минимум 3 или 5 лет официально подтверждённого опыта в профессии.",
      { h: "1. Через список востребованных специальностей — Green List" },
      "Если вы работаете по профессии из этого списка, вы можете подаваться на резидентство практически сразу после получения подходящей работы. Однако работа должна полностью соответствовать требованиям списка — по образованию, лицензиям, уровню зарплаты и другим критериям.",
      "Недостаточно просто работать по профессии — конкретная должность и условия работы должны точно подходить под требования Green List. На практике это получается не всегда, поэтому первая работа в Новой Зеландии может и не соответствовать этим условиям.",
      { h: "2. Через балльную систему" },
      "Для получения резидентства необходимо набрать 6 баллов. Баллы начисляются за образование (полученное где-либо) и за опыт работы (полученный уже в Новой Зеландии).",
      "С августа 2026 года применяются следующие правила:",
      { ul: ["бакалавр — 4 балла;","магистратура — 5 баллов;","PhD, или докторская степень — 6 баллов."] },
      "Дополнительно за каждый год квалифицированной работы в Новой Зеландии начисляется ещё 1 балл. Некоторые дипломы оцениваются индивидуально — например, диплом специалиста может быть приравнен как к бакалавру, так и к магистратуре.",
      "Соответственно:",
      { ul: ["если у вас бакалавр — обычно потребуется 2 года работы в Новой Зеландии;","если магистратура — достаточно 1 года;","если PhD — можно подаваться на резидентство сразу после получения работы."] },
      "Также с августа 2026 года планируются дополнительные изменения: выпускники новозеландского бакалавриата смогут получать 5 баллов вместо 4 (достаточно 1 года работы), а выпускники новозеландской магистратуры смогут сразу получать все необходимые 6 баллов — после окончания программы нужно будет только найти работу.",
      { h: "3. Через опыт работы" },
      "Для некоторых специальностей существуют отдельные категории, где ключевой критерий — подтверждённый опыт:",
      { ul: ["по техническим и рабочим специальностям — 4 года опыта, включая минимум 1,5 года в Новой Зеландии;","для высококвалифицированной работы — 5 лет опыта, из которых минимум 2 года в Новой Зеландии с зарплатой чуть выше медианной."] },
      "Независимо от пути основной критерий остаётся неизменным — наличие квалифицированной работы в Новой Зеландии.",
    ],
  },
  {
    t: "Работа в Новой Зеландии",
    d: "4:32",
    s: "Три способа найти работу: удалённо, на месте по визитёрке, или через обучение.",
    body: [
      "Опустимся ещё на одну ступень ниже — к поиску работы. По сути, существует три основных варианта.",
      { h: "1. Искать работу удалённо" },
      "Попытать удачу, находясь за пределами Новой Зеландии. Такой путь возможен, но вероятность успеха низкая. Большинство работодателей не готовы помогать иностранным кандидатам с переездом и оформлением рабочей визы, не познакомившись с человеком лично.",
      "Тем не менее такие случаи бывают — особенно если у вас универсальная техническая профессия и сильный опыт работы.",
      { h: "2. Приехать в Новую Зеландию и искать работу на месте" },
      "Можно приехать в страну по Visitor Visa или на курсы английского. Шансы повышаются, потому что вы можете лично посещать собеседования. На студенческой визе может быть возможность подрабатывать — это помогает познакомиться с работодателями.",
      "Однако риски всё ещё высокие. Многие работодатели не хотят заниматься оформлением рабочей визы. Именно поэтому около 95% вакансий содержат пометку «Подавайтесь только если у вас уже есть право на работу в Новой Зеландии».",
      { h: "3. Профессиональное обучение" },
      "Самый распространённый путь среди иммигрантов. Обучение на программе длительностью от одного года даёт право на post-study work visa — рабочую визу после учёбы.",
      "Имея местный диплом и оформленную рабочую визу, вы становитесь намного более привлекательным кандидатом — работодателю уже не нужно заниматься вашей визой.",
    ],
  },
  {
    t: "Профессиональное обучение",
    d: "5:48",
    s: "Уровни образования в НЗ: от дипломов до магистратуры — что какую визу даёт.",
    body: [
      "Опускаемся ещё на одну ступень — к профессиональному обучению. Именно обучение сегодня — один из самых популярных способов переезда, потому что оно даёт местную квалификацию, право на работу и в дальнейшем — возможность претендовать на резидентство.",
      { h: "Дипломы и сертификаты уровней 4–6" },
      "Аналог технического или средне-специального образования. Обучение длится от 1 до 2 лет. После окончания можно получить рабочую визу на полгода, год или два года, в зависимости от программы.",
      { h: "Bachelor Degree — бакалавриат, уровень 7" },
      "Обычно 3 или 4 года. После окончания выпускник получает post-study work visa на тот же срок, сколько длилась учёба. После трёхлетнего бакалавриата — рабочая виза на 3 года.",
      { h: "Graduate Diploma — уровень 7" },
      "Программа переквалификации или подтверждения уже имеющейся квалификации. Обучение обычно 1 год, рабочая виза тоже на 1 год.",
      { h: "Postgraduate Diploma — уровень 8" },
      "Повышение квалификации. Программа 1 год, рабочая виза на 1 год. Важное преимущество: postgraduate diploma даёт 5 баллов для резидентства, поэтому достаточно проработать 1 год в Новой Зеландии, чтобы набрать необходимые 6 баллов.",
      { h: "Master Degree — магистратура, уровень 9" },
      "Обучение год, полтора или два. После окончания выдают рабочую визу сразу на 3 года — этого достаточно, чтобы спокойно найти работу и начать путь к резидентству.",
      "Кроме того, магистратура позволяет получить все необходимые 6 баллов для подачи на резидентство (при условии, что у вас уже есть предыдущее бакалаврское образование). Именно поэтому магистратура сейчас — один из самых популярных и удобных вариантов переезда.",
    ],
  },
  {
    t: "Семейные визы и дети",
    d: "3:05",
    s: "Как партнёр студента получает рабочую визу и где дети учатся бесплатно.",
    body: [
      "Также важно понимать, какие возможности обучение даёт для семьи.",
      "Чтобы партнёр студента мог получить открытую рабочую визу, основной студент должен:",
      { ul: ["либо учиться на программе 7 или 8 уровня по специальности из Green List;","либо обучаться в магистратуре любого направления."] },
      "Если один из прямых родителей или официальных опекунов имеет рабочую визу, дети могут бесплатно учиться в государственной школе Новой Зеландии.",
      "Часто схема выглядит так: один из супругов учится, второй получает право на работу, а дети бесплатно ходят в школу.",
      "Если же у родителя нет рабочей визы, учёба в школе может стоить в среднем 15 000 – 20 000 NZD в год. Детские садики — около 8–10 NZD в час, но с 3 до 5 лет по правительственным субсидиям до 20 часов в неделю бесплатно.",
    ],
  },
  {
    t: "Типы учебных заведений и бюджеты",
    d: "5:20",
    s: "Университеты, политехники и частные институты — разница в престиже, среде и цене.",
    body: [
      "Все цены далее — в новозеландских долларах и по стандартной стоимости обучения, без учёта скидок. При этом многие учебные заведения предоставляют нам скидки до 10 000 NZD, а иногда и больше.",
      { h: "1. Университеты" },
      "Самая престижная и качественная категория. Университеты предлагают: самый широкий выбор специальностей, больше предметов внутри программ, сильнейший преподавательский состав, лучшую инфраструктуру и — самое главное — тесные связи с профессиональной средой.",
      "Именно связи и нетворкинг часто становятся главным преимуществом университетского образования. В университетах учится около 10% иностранцев, остальные — местные. Это создаёт максимально полезную среду для интеграции.",
      "Это и самый дорогой вариант:",
      { ul: ["бакалавриат и многие другие программы — около 35 000 NZD в год;","магистратура — чаще всего 50 000 – 70 000 NZD за всю программу (1–1,5 года)."] },
      { h: "2. Политехники и государственные институты" },
      "По престижности немного уступают университетам, выбор программ чуть меньше, но качество остаётся хорошим — это государственные заведения. Около 20% студентов — иностранцы.",
      { ul: ["Дипломы и бакалавриат — 25 000 – 30 000 NZD в год;","магистратура — примерно 40 000 – 45 000 NZD за программу."] },
      { h: "3. Частные институты" },
      "Ниже престижность, меньше выбор программ, более простое качество обучения. Большинство студентов — иностранцы (в некоторых институтах до 99%). Но визовые преимущества те же: рабочие визы, баллы и так далее.",
      "Главное преимущество — стоимость:",
      { ul: ["Дипломы и бакалавриат — около 20 000 – 25 000 NZD в год;","магистратура — примерно 30 000 – 35 000 NZD за программу."] },
    ],
  },
  {
    t: "Важность престижности заведения",
    d: "3:42",
    s: "На что на самом деле смотрят работодатели — и где престиж вуза реально важен.",
    body: [
      "Для большинства работодателей престижность вуза — далеко не самый главный фактор.",
      "На первом месте — местный опыт работы. Он показывает, что человек адаптировался к жизни в Новой Зеландии, понимает местную рабочую культуру и способен эффективно работать.",
      "Второй важный фактор — рекомендации и профессиональные связи. Нетворкинг во время учёбы может оказывать огромное влияние на поиск работы. Часто именно знакомства и контакты помогают найти первую работу гораздо быстрее.",
      "Далее работодатели смотрят на конкретные профессиональные навыки, общий опыт работы (включая опыт в других странах), и только затем — на уровень учебного заведения.",
      "При этом статистика показывает, что выпускники более престижных университетов в среднем находят работу быстрее. Связано это с профессиональными связями университетов, контактами преподавателей, карьерными центрами и окружением.",
      "Однако это не означает, что выпускники более простых заведений не могут успешно найти работу — многие студенты политехников и частных институтов также успешно трудоустраиваются.",
      "Но в некоторых профессиях престижность заведения играет более заметную роль — например, в академической сфере, научных специальностях, части инженерных направлений и медицине.",
    ],
  },
  {
    t: "Английский язык",
    d: "4:55",
    s: "Какой уровень нужен для поступления и как готовиться — IELTS, Duolingo, Филиппины.",
    body: [
      { h: "Требования по английскому языку" },
      "Для программ до 7 уровня обычно требуется IELTS Academic 6.0 и не ниже 5.5 по каждому модулю. Для программ 8 уровня и выше — IELTS Academic 6.5 и не ниже 6.0 по каждому модулю.",
      "Вместо IELTS могут подойти PTE, TOEFL, Cambridge и некоторые другие международные экзамены. К сожалению, сейчас большинство таких экзаменов нельзя сдать в России, поэтому обычно приходится выезжать за границу.",
      { h: "Онлайн-тест Duolingo" },
      "Duolingo English Test можно сдавать полностью онлайн, прямо из дома. Он проще, дешевле и удобнее. Но принимают его не все заведения — только университеты, да и то не все из них.",
      { h: "Как подготовиться" },
      "Самостоятельно дома (если хватает силы воли), на языковых курсах поблизости или онлайн. У нас есть собственная онлайн-школа английского — готовим к международным экзаменам, подтягиваем общий английский, помогаем выйти на необходимый уровень для поступления.",
      { h: "Изучение английского на Филиппинах" },
      "Популярный вариант — обходится дешевле, чем курсы в Новой Зеландии, при этом предлагает очень интенсивную практику. Средняя стоимость — около 2000 USD в месяц, включая обучение, проживание, питание и дополнительные расходы.",
      { h: "Языковые курсы в Новой Зеландии" },
      "Стоимость 250–500 NZD в неделю в зависимости от школы и продолжительности. Плюс расходы на проживание. Позволяют не только улучшать английский, но и познакомиться со страной, адаптироваться, иногда параллельно искать работу.",
      "Большинство школ начинают обучение каждый понедельник, продолжительность — от нескольких недель до любого срока.",
    ],
  },
  {
    t: "Итоги системы",
    d: "1:55",
    s: "Вся иммиграционная цепочка целиком — от английского до паспорта.",
    body: [
      "Вся система иммиграции в Новую Зеландию выстраивается в довольно понятную последовательную цепочку:",
      { ul: [
        "Сначала — английский язык.",
        "Затем — профессиональное обучение в Новой Зеландии.",
        "После этого — получение post-study work visa, рабочей визы после учёбы.",
        "Далее — поиск квалифицированной работы.",
        "После того как нашли работу, начинается путь к резидентству.",
      ]},
      "В зависимости от вашей ситуации и количества баллов податься на резидентство можно сразу после получения работы, через 1 год работы или через 2 года.",
      "А после получения резидентства остаётся прожить в стране ещё 5 лет, чтобы получить гражданство Новой Зеландии и затем паспорт.",
    ],
  },
  {
    t: "Финансы",
    d: "5:30",
    s: "Что хочет увидеть иммиграционный офицер: достаточность, происхождение и доступ к деньгам.",
    body: [
      "При рассмотрении студенческой визы иммиграционного офицера обычно интересуют три основных момента.",
      { h: "1. Достаточность средств" },
      "Хватает ли у вас денег на весь период обучения и проживания. Оплачивать учёбу заранее обычно не требуется — это происходит уже после предварительного одобрения визы.",
      "На этапе подачи нужно просто показать наличие средств. Деньги должны быть на банковском счёте в лицензированном банке. Криптокошельки, брокерские счета и подобные активы, как правило, не подходят.",
      "Общая сумма формируется из стоимости обучения, минимальных расходов на проживание и денег на перелёты.",
      "На проживание закладывается:",
      { ul: ["около 20 000 NZD в год на основного заявителя;","примерно по 5 000 NZD в год на каждого члена семьи."] },
      "Если программа длится 1,5 или 2 года, суммы пропорционально увеличиваются. Дополнительно нужно показать деньги на авиабилеты — примерно 2 500 NZD на человека на перелёт туда и обратно.",
      { h: "2. Происхождение средств" },
      "Офицер должен понимать, что деньги действительно ваши и не были заняты на короткий срок. Подтверждением могут служить:",
      { ul: ["длительные накопления на банковских счетах;","подтверждённые доходы от зарплаты или бизнеса;","продажа имущества;","помощь и спонсорство родственников."] },
      "Документы: банковские выписки, налоговые документы, договоры продажи имущества, письма поддержки от родственников и спонсоров.",
      { h: "3. Доступ к средствам" },
      "Сможете ли вы реально пользоваться этими деньгами. Если вы в стране с санкционными ограничениями — важно иметь счёт в банке, не подпадающем под ограничения.",
      "Для граждан России сейчас чаще используются банки ближнего зарубежья: Казахстана, Кыргызстана, Узбекистана, Таджикистана и других стран региона.",
      "Некоторые российские банки помогают открывать счета в зарубежных банках-партнёрах удалённо — если нужна помощь, обратитесь к нам.",
      "На счетах нужно показать наличие всех средств, либо хотя бы денег на проживание и билеты. А на учёбу можно оставить и на санкционных счетах.",
    ],
  },
  {
    t: "Оплата обучения и переводы в НЗ",
    d: "4:10",
    s: "SWIFT, международные карты, банки-посредники и криптовалюта — что работает.",
    body: [
      { h: "1. SWIFT-переводы" },
      "Самый простой и понятный вариант. Если ваш зарубежный счёт поддерживает SWIFT, вы можете оплачивать обучение напрямую в учебное заведение, а также переводить деньги на свой счёт в новозеландском банке.",
      { h: "2. Оплата международной картой" },
      "Многие учебные заведения принимают Visa и MasterCard. Если ваш банк выпускает такие карты и они работают за границей, вы можете оплачивать обучение, использовать карту для повседневных расходов и снимать наличные. Даже с учётом комиссий вариант часто удобен.",
      { h: "3. Помощь банков и посредников" },
      "Некоторые банки и финансовые партнёры помогают с оплатой за рубежом. Схема: вы переводите рубли банку, а партнёрская зарубежная компания оплачивает обучение от своего имени. Такие услуги предоставляют некоторые банки, которые также помогают открывать зарубежные счета.",
      { h: "4. Использование криптовалюты" },
      "В Новой Зеландии криптовалюта легальна и её также можно использовать. Но финансовые проверки сохраняются — нужно подтвердить происхождение, законность получения и факт того, что это не новый доход, полученный уже в НЗ.",
      "Существует официальный сервис, через который можно обменивать криптовалюту на обычные деньги и выводить на банковский счёт. Для этого тоже потребуется проверка происхождения и подтверждающие документы.",
    ],
  },
  {
    t: "Жильё",
    d: "3:18",
    s: "Где жить в первые недели и как искать постоянное жильё.",
    body: [
      { h: "1. Временное жильё на первое время" },
      "Самый распространённый вариант — снять временное жильё на первые одну-две недели через Booking.com, Airbnb или другие сервисы краткосрочной аренды.",
      "После приезда вы спокойно начинаете искать постоянное жильё. Большинство арендодателей предпочитают знакомиться с жильцами лично перед подписанием договора — им важно увидеть вас, понять, кто вы, убедиться, что вы надёжный жилец.",
      "Вам самим тоже желательно посмотреть жильё своими глазами: проверить, насколько оно сухое и тёплое, удобно ли расположено, подходит ли район.",
      { h: "2. Помощь от учебных заведений" },
      "Некоторые заведения помогают студентам — студенческие общежития или homestay (проживание в англоязычной семье). Homestay особенно популярен среди студентов языковых курсов и тех, кто хочет быстрее адаптироваться.",
      { h: "3. Русскоязычные сообщества и чаты" },
      "Многие ищут жильё через русскоязычные чаты и сообщества в Новой Зеландии. Там часто публикуются объявления от людей, которые сдают комнаты, ищут соседей или помогают новым приезжающим. Многие местные русскоязычные жители сами проходили через переезд и относятся к студентам лояльно.",
    ],
  },
  {
    t: "Медицина",
    d: "2:48",
    s: "Страховка для студента, для семьи и медицина после рабочей визы.",
    body: [
      "Для иностранного студента наличие медицинской страховки — обязательное условие. Страховка покрывает не только медицинские расходы, но и многие другие ситуации: отмену или перенос рейсов, дополнительные расходы на перелёты, ущерб имуществу, вашу личную ответственность.",
      "Например, если в арендованном жилье по вашей вине повреждено имущество — часть расходов может покрываться страховкой.",
      "Стоимость студенческой страховки — около 700–800 NZD в год на человека.",
      { h: "Страховка для семьи" },
      "Для членов семьи страховка формально не обязательна, но мы настоятельно рекомендуем её оформлять, пока вы в Новой Зеландии по семейным визам. В это время бесплатная государственная медицина обычно недоступна.",
      { h: "Медицина после получения рабочей визы" },
      "Обладатели рабочих виз получают доступ к государственной системе здравоохранения — практически на тех же условиях, что граждане и резиденты. Это «условно-бесплатная» медицина:",
      { ul: [
        "приём у GP (General Practitioner — врача общей практики) — около 60–80 NZD;",
        "после этого врач может направить вас на дальнейшее лечение, которое будет бесплатным или частично субсидированным;",
        "экстренная медицинская помощь покрывается системой здравоохранения;",
        "большинство рецептурных лекарств стоят всего около 5 NZD.",
      ]},
    ],
  },
  {
    t: "Визовые нюансы",
    d: "3:50",
    s: "Миф про «доказать возврат на Родину» и что такое «якоря» в анкете.",
    body: [
      "Существует распространённое заблуждение, что при подаче на студенческую визу нужно обязательно доказывать, что вы точно вернётесь на Родину. На практике это не совсем так.",
      "Главная задача офицера — убедиться, что вам есть куда вернуться, у вас есть стабильная жизненная база вне Новой Зеландии и вы не окажетесь в полной неопределённости, если что-то пойдёт не по плану.",
      "Именно поэтому важно показать так называемые «якоря» — факторы, связывающие вас со страной проживания или другой страной с долгосрочным визовым статусом. Такими якорями могут быть:",
      { ul: ["недвижимость;","доля в собственности;","регистрация по месту жительства;","автомобиль или другое имущество;","действующий бизнес;","работа;","семья;","или другие устойчивые связи."] },
      "Наличие официальной работы на момент подачи часто воспринимается как плюс. Также плюсом может быть семья, которая остаётся дома и ожидает вашего возвращения.",
      "Многое зависит от типа поездки. Если вы едете только на краткосрочные языковые курсы без дальнейших образовательных планов — действительно стоит продемонстрировать работу, карьерные планы дома и причины вернуться.",
      "Если вы подаётесь на длительное профессиональное обучение, то нет ничего плохого в том, чтобы честно показывать дальнейший план: закончить обучение, получить post-study work visa и применить полученные знания на практике в Новой Зеландии. Для Новой Зеландии это нормальный и понятный путь, предусмотренный самой системой.",
    ],
  },
  {
    t: "Наши услуги",
    d: "4:40",
    s: "Что входит в пакет «Оформление» за 699 USD — от подбора до помощи после переезда.",
    body: [
      "У нас есть пакет «Оформление» — сопровождение студента «под ключ» от выбора программы до помощи уже после переезда. Всё делается онлайн: поступление, подача на визы. Не нужно даже отправлять физически паспорта — визы выпускаются в цифровом виде.",
      "Начинать подбор программ и поступление можно заранее — даже без нужного уровня английского или собранных денег. В этом случае делается условное приглашение, а результаты тестов высылаются позже. Многие процессы запускаются параллельно.",
      "Стоимость услуги — 699 USD. В неё входит:",
      { h: "1. Подбор учебного заведения" },
      "Вы указываете пожелания: город, специальность, уровень заведения, бюджет. На основании этого мы готовим подборку наиболее подходящих вариантов.",
      { h: "2. Поступление в учебное заведение" },
      "Отправляем подробные инструкции по документам. Проверяем их, сертифицируем, приводим всё в нужный формат и подаём заявку. Также стараемся получить максимальные скидки там, где это возможно.",
      { h: "3. Подготовка студенческой визы" },
      "Пока ждём ответ от заведения, начинаем готовить документы на визу: предоставляем инструкции, помогаем собрать, проверяем, оформляем и подаём через наш официальный аккаунт Immigration New Zealand. Полностью сопровождаем коммуникацию с INZ.",
      "На данный момент процент одобрения виз у наших клиентов — около 92%. Для сравнения: при самостоятельной подаче статистика по многим рынкам — около 53%.",
      { h: "4. Помощь после приезда" },
      "Рассказываем, как подготовиться к поездке, подсказываем первые шаги, помогаем с поиском жилья, объясняем, как найти первую работу или подработку.",
      "Также можем выступать референсом при аренде жилья — арендодатели часто просят контакты предыдущих арендодателей, а у новых студентов их обычно нет. Мы подтверждаем, что студент надёжный.",
      { h: "Семейные визы отдельно" },
      "Мы образовательные агенты, поэтому официально помогаем именно по студенческим визам. Для семейных виз: можно податься самостоятельно (мы подскажем инструкции и источники) либо обратиться к лицензированному иммиграционному адвайзеру или юристу. Обычно оформление семейных виз через специалиста стоит около 2 500 NZD.",
    ],
  },
];

function ChapterBody({ body }) {
  return (
    <>
      {body.map((node, i) => {
        if (typeof node === "string") return <p key={i} className="ins-p">{node}</p>;
        if (node.h) return <h4 key={i} className="ins-h">{node.h}</h4>;
        if (node.ul) return (
          <ul key={i} className="ins-ul">
            {node.ul.map((li, j) => <li key={j}>{li}</li>)}
          </ul>
        );
        return null;
      })}
    </>
  );
}

function SetupInstruction({ onNext, onBack }) {
  const [content, setContent] = React.useState(null);
  const [chapters, setChapters] = React.useState(FALLBACK_CHAPTERS);
  const [active, setActive] = React.useState(0);
  const [playing, setPlaying] = React.useState(false);
  const [expanded, setExpanded] = React.useState({}); // chapter index -> bool (accordion: at most one)
  const [textOnly, setTextOnly] = React.useState(false);
  const [fullscreen, setFullscreen] = React.useState(null); // chapter index when fullscreen player is shown
  const [isMobile, setIsMobile] = React.useState(false);

  React.useEffect(() => {
    fetch("/api/instruction")
      .then(r => r.ok ? r.json() : null)
      .then(d => { if (d) setContent(d); })
      .catch(() => {});
    fetch("/api/instruction/chapters")
      .then(r => r.ok ? r.json() : null)
      .then(d => { if (Array.isArray(d) && d.length > 0) setChapters(d); })
      .catch(() => {});
  }, []);

  React.useEffect(() => {
    const mq = window.matchMedia("(max-width: 980px)");
    const upd = () => {
      setIsMobile(mq.matches);
      // When switching back to portrait/mobile, stop the inline player so
      // the iframe (which doesn't self-resize on orientation change) doesn't
      // break the layout. Mobile users use FullscreenPlayer anyway.
      if (mq.matches) setPlaying(false);
    };
    upd();
    mq.addEventListener("change", upd);
    return () => mq.removeEventListener("change", upd);
  }, []);

  const chapter = chapters[active];
  const totalDur = chapters.reduce((acc, c) => {
    if (!c.duration) return acc;
    const parts = c.duration.split(":");
    if (parts.length !== 2) return acc;
    const [m, s] = parts.map(Number);
    return acc + (m || 0) * 60 + (s || 0);
  }, 0);
  const totalMin = Math.round(totalDur / 60);

  // Accordion toggle — collapses any other open chapter
  const toggle = (i) => setExpanded((o) => (o[i] ? {} : { [i]: true }));

  const selectChapter = (i) => {
    setActive(i);
    setPlaying(false);
    // accordion: only this chapter open
    setExpanded({ [i]: true });
    // scroll the right column to that chapter
    requestAnimationFrame(() => {
      const el = document.getElementById(`ins-ch-${i}`);
      if (el) el.scrollIntoView({ block: "start", behavior: "smooth" });
    });
  };

  // "Смотреть" button: on mobile → fullscreen modal; on desktop → just select (sticky player)
  const watchChapter = (i) => {
    if (isMobile) {
      setActive(i);
      setFullscreen(i);
    } else {
      selectChapter(i);
      setPlaying(true);
      // scroll to top so sticky player is visible
      window.scrollTo({ top: 0, behavior: "smooth" });
    }
  };

  return (
    <div className="anim-in">
      {content === null ? (
        <>
          <div className="ins-ske ins-ske-title mt-16"/>
          <div className="ins-ske ins-ske-desc mt-16"/>
          <div className="ins-ske ins-ske-desc-2 mt-6"/>
        </>
      ) : (
        <>
          <h1 className="h1 mt-16">{content.title}</h1>
          <p className="lead mt-16" style={{maxWidth:640}}>{content.description}</p>
        </>
      )}

      <div className="hstack gap-12 mt-16" style={{maxWidth:920, flexWrap:"wrap"}}>
        {totalMin > 0 && <span className="pill"><Icon.Clock style={{width:12,height:12}}/> ≈ {totalMin} минут целиком</span>}
        <span className="pill" style={{background:"#fff",color:"var(--ink-2)",border:"1px solid var(--line)"}}>{chapters.length} глав</span>
        <span className="pill" style={{background:"#fff",color:"var(--ink-2)",border:"1px solid var(--line)"}}>RU · с английскими терминами</span>
        <button className="chip" style={{marginLeft:"auto",background: textOnly?"var(--brand)":"#fff", color: textOnly?"#fff":"var(--ink-2)", borderColor: textOnly?"var(--brand)":"var(--line)"}}
                onClick={() => setTextOnly((v)=>!v)}>
          <Icon.Doc style={{width:13,height:13}}/> {textOnly ? "Скрыть видео" : "Только текст"}
        </button>
      </div>

      <div className={`ins-grid mt-24 ${textOnly?"text-only":""}`}>
        {/* LEFT — sticky video player + chapter list */}
        {!textOnly && (
          <aside className="ins-left">
            <div className="ins-player" onClick={() => isMobile ? watchChapter(active) : setPlaying((p) => !p)}>
              <div className="ins-player-bg" style={{
                background: `linear-gradient(135deg, color-mix(in srgb, var(--brand) 50%, #0f172a) 0%, #0b1220 100%)`
              }}/>
              <div className="ins-player-noise"/>

              {playing && chapters[active]?.embedUrl ? (
                <iframe
                  src={chapters[active].embedUrl + "?autoplay=1"}
                  allow="autoplay; fullscreen; picture-in-picture; encrypted-media; screen-wake-lock"
                  style={{position:"absolute",inset:0,width:"100%",height:"100%",border:0,borderRadius:14}}
                />
              ) : playing ? (
                <div className="ins-playing">
                  <span style={{fontSize:13,opacity:.6,color:"#fff"}}>Видео скоро появится</span>
                </div>
              ) : (
                <>
                  <div className="ins-play"><div className="ins-play-tri"/></div>
                  <div className="ins-player-meta">
                    <div className="ins-host">
                      <div className="ins-avatar">ДВ</div>
                      <div>
                        <b>Денис Власин</b>
                        <small>Kiwi Education · Окленд, NZ</small>
                      </div>
                    </div>
                    <div className="ins-now">
                      <small>Глава {active + 1}</small>
                      <b>{chapter.title}</b>
                    </div>
                  </div>
                </>
              )}

              {chapter.duration && <div className="ins-duration">{chapter.duration}</div>}
            </div>

            <div className="ins-controls">
              <button className="ins-ctrl" onClick={(e)=>{e.stopPropagation(); selectChapter(Math.max(0, active-1));}} disabled={active===0}>
                <Icon.ArrowLeft style={{width:14,height:14}}/> Пред.
              </button>
              <div className="ins-progress">
                <div className="ins-progress-fill" style={{width:`${((active+1)/chapters.length)*100}%`}}/>
              </div>
              <button className="ins-ctrl" onClick={(e)=>{e.stopPropagation(); selectChapter(Math.min(chapters.length-1, active+1));}} disabled={active===chapters.length-1}>
                След. <Icon.Arrow style={{width:14,height:14}}/>
              </button>
            </div>

            <div className="ins-chaplist">
              <div className="ins-chaplist-title">Все главы</div>
              <div className="ins-chaplist-scroll">
                {chapters.map((c, i) => (
                  <button key={i} className={`ins-chap-item ${i===active?"active":""}`} onClick={() => selectChapter(i)}>
                    <span className="ins-chap-num">{String(i+1).padStart(2,"0")}</span>
                    <span className="ins-chap-t">{c.title}</span>
                    <span className="ins-chap-d">{c.duration || ""}</span>
                  </button>
                ))}
              </div>
            </div>
          </aside>
        )}

        {/* RIGHT — chapter cards with summaries + expand */}
        <div className="ins-right">
          {chapters.map((c, i) => {
            const isOpen = !!expanded[i];
            const isActive = i === active;
            return (
              <article key={i} id={`ins-ch-${i}`} className={`ins-card ${isActive?"active":""}`}>
                <header className="ins-card-hd">
                  <div className="ins-card-num">{String(i+1).padStart(2,"0")}</div>
                  <div className="grow">
                    <h3 className="ins-card-t">{c.title}</h3>
                    <div className="ins-card-meta">
                      <Icon.Video style={{width:12,height:12,opacity:.6}}/> {c.duration || "—"}
                      {!textOnly && (
                        <button className="ins-mini" onClick={(e) => { e.stopPropagation(); watchChapter(i); }} title="Смотреть это видео">
                          {isActive && !isMobile ? "Сейчас играет" : "Смотреть"}
                        </button>
                      )}
                    </div>
                  </div>
                </header>

                <p className="ins-summary">{c.shortDescription}</p>

                {isOpen && (
                  <div className="ins-full anim-in">
                    <div className="ins-full-divider"/>
                    <ChapterBody body={c.body}/>
                  </div>
                )}

                <button className="ins-expand" onClick={() => toggle(i)}>
                  {isOpen ? (
                    <><Icon.ArrowLeft style={{width:14,height:14,transform:"rotate(90deg)"}}/> Свернуть</>
                  ) : (
                    <>Развернуть полный текст <Icon.Arrow style={{width:14,height:14,transform:"rotate(90deg)"}}/></>
                  )}
                </button>
              </article>
            );
          })}
        </div>
      </div>

      <div className="hstack mt-32" style={{gap:12, justifyContent:"space-between", maxWidth: textOnly ? 820 : 1180}}>
        <button className="btn btn-ghost" onClick={onBack}><Icon.ArrowLeft style={{width:14,height:14}}/> Назад</button>
        <button className="btn btn-primary btn-lg" onClick={onNext}>
          Перейти к анкете <Icon.Arrow style={{width:16,height:16}}/>
        </button>
      </div>

      {fullscreen !== null && (
        <FullscreenPlayer
          chapters={chapters}
          active={fullscreen}
          setActive={setFullscreen}
          onClose={() => { setFullscreen(null); setPlaying(false); }}
          onPickAndExpand={(i) => { setActive(i); }}
          onCloseAndRead={(i) => {
            setFullscreen(null);
            setPlaying(false);
            setActive(i);
            setExpanded({ [i]: true });
            setTimeout(() => {
              const el = document.getElementById(`ins-ch-${i}`);
              if (el) el.scrollIntoView({ block: "start", behavior: "smooth" });
            }, 80);
          }}
        />
      )}

      <style>{`
        @keyframes ins-shimmer{0%{background-position:-600px 0}100%{background-position:600px 0}}
        .ins-ske{border-radius:6px;background:linear-gradient(90deg,#e2e8f0 25%,#f1f5f9 50%,#e2e8f0 75%);background-size:1200px 100%;animation:ins-shimmer 1.4s ease-in-out infinite}
        .ins-ske-title{height:36px;width:260px;max-width:100%}
        .ins-ske-desc{height:18px;width:100%;max-width:640px}
        .ins-ske-desc-2{height:18px;width:75%;max-width:480px}
        .mt-6{margin-top:6px}

        .ins-grid{display:grid;grid-template-columns:380px 1fr;gap:28px;align-items:flex-start;max-width:1180px}
        .ins-grid.text-only{grid-template-columns:1fr;max-width:820px}
        @media (max-width:980px){.ins-grid{grid-template-columns:1fr}}

        /* LEFT */
        .ins-left{position:sticky;top:24px;display:flex;flex-direction:column;gap:14px}
        @media (max-width:980px){.ins-left{position:static}}

        .ins-player{position:relative;aspect-ratio:16/9;border-radius:14px;overflow:hidden;cursor:pointer;color:#fff;box-shadow:var(--shadow-md)}
        .ins-player-bg{position:absolute;inset:0}
        .ins-player-noise{position:absolute;inset:0;background:radial-gradient(circle at 30% 40%, color-mix(in srgb, var(--brand) 40%, transparent), transparent 50%), radial-gradient(circle at 70% 70%, rgba(255,255,255,.08), transparent 40%)}
        .ins-play{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:64px;height:64px;border-radius:50%;background:rgba(255,255,255,.96);display:grid;place-items:center;box-shadow:0 10px 30px rgba(0,0,0,.35);transition:transform .2s;z-index:2}
        .ins-player:hover .ins-play{transform:translate(-50%,-50%) scale(1.06)}
        .ins-play-tri{width:0;height:0;border-left:16px solid var(--brand);border-top:10px solid transparent;border-bottom:10px solid transparent;margin-left:4px}
        .ins-player-meta{position:absolute;left:16px;right:16px;bottom:14px;z-index:2;display:flex;justify-content:space-between;align-items:flex-end;gap:12px}
        .ins-host{display:flex;align-items:center;gap:10px}
        .ins-avatar{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,#fff,rgba(255,255,255,.6));color:var(--brand-deep);display:grid;place-items:center;font-size:12.5px;font-weight:800;letter-spacing:.02em;flex-shrink:0;box-shadow:0 4px 10px rgba(0,0,0,.2)}
        .ins-host b{display:block;font-size:13.5px;font-weight:600;line-height:1.1}
        .ins-host small{display:block;font-size:11.5px;opacity:.7;margin-top:2px}
        .ins-now{text-align:right;max-width:160px}
        .ins-now small{display:block;font-size:10.5px;text-transform:uppercase;letter-spacing:.1em;opacity:.7;font-weight:600}
        .ins-now b{display:block;font-size:13px;font-weight:600;margin-top:2px;line-height:1.25}
        .ins-duration{position:absolute;top:12px;right:12px;background:rgba(0,0,0,.5);color:#fff;font-size:11.5px;padding:3px 8px;border-radius:5px;font-weight:600;font-variant-numeric:tabular-nums;z-index:2;backdrop-filter:blur(6px)}
        .ins-playing{position:absolute;inset:0;display:grid;place-items:center;background:rgba(0,0,0,.4);flex-direction:column}

        .ins-controls{display:flex;align-items:center;gap:10px}
        .ins-ctrl{height:34px;padding:0 10px;font-size:12.5px;font-weight:600;color:var(--ink-2);background:#fff;border:1px solid var(--line);border-radius:8px;cursor:pointer;display:flex;align-items:center;gap:5px;transition:all .12s}
        .ins-ctrl:not(:disabled):hover{border-color:var(--brand);color:var(--brand)}
        .ins-ctrl:disabled{opacity:.4;cursor:not-allowed}
        .ins-progress{flex:1;height:4px;background:var(--line);border-radius:2px;overflow:hidden}
        .ins-progress-fill{height:100%;background:var(--brand);transition:width .3s ease;border-radius:2px}

        .ins-chaplist{background:#fff;border:1px solid var(--line);border-radius:12px;overflow:hidden}
        .ins-chaplist-title{padding:12px 14px;font-size:11.5px;font-weight:700;color:var(--ink-3);letter-spacing:.08em;text-transform:uppercase;border-bottom:1px solid var(--line-2)}
        .ins-chaplist-scroll{max-height:360px;overflow-y:auto;padding:6px}
        .ins-chap-item{display:flex;align-items:center;gap:10px;padding:9px 10px;border:0;background:transparent;cursor:pointer;width:100%;text-align:left;border-radius:7px;font-family:inherit;transition:background .1s}
        .ins-chap-item:hover{background:var(--bg-tint)}
        .ins-chap-item.active{background:var(--brand-soft)}
        .ins-chap-num{flex-shrink:0;font-size:11px;font-weight:700;color:var(--ink-3);font-variant-numeric:tabular-nums;width:20px}
        .ins-chap-item.active .ins-chap-num{color:var(--brand)}
        .ins-chap-t{flex:1;font-size:13px;font-weight:500;color:var(--ink-2);line-height:1.3}
        .ins-chap-item.active .ins-chap-t{color:var(--brand-deep);font-weight:600}
        .ins-chap-d{flex-shrink:0;font-size:11px;color:var(--ink-3);font-variant-numeric:tabular-nums}

        /* RIGHT */
        .ins-right{display:flex;flex-direction:column;gap:14px}
        .ins-card{background:#fff;border:1px solid var(--line);border-radius:12px;padding:20px 22px;transition:border-color .15s, box-shadow .15s}
        .ins-card.active{border-color:color-mix(in srgb, var(--brand) 45%, var(--line));box-shadow:0 0 0 3px color-mix(in srgb, var(--brand) 10%, transparent)}
        .ins-card-hd{display:flex;align-items:flex-start;gap:14px;margin-bottom:10px}
        .ins-card-num{flex-shrink:0;width:34px;height:34px;border-radius:8px;background:var(--brand-soft);color:var(--brand-deep);display:grid;place-items:center;font-size:12.5px;font-weight:700;font-variant-numeric:tabular-nums;letter-spacing:.02em}
        .ins-card.active .ins-card-num{background:var(--brand);color:#fff}
        .ins-card-t{font-size:17px;font-weight:700;color:var(--ink);letter-spacing:-.005em;margin:0;line-height:1.3}
        .ins-card-meta{display:flex;align-items:center;gap:10px;margin-top:4px;font-size:12.5px;color:var(--ink-3);font-weight:500}
        .ins-mini{height:24px;padding:0 9px;font-size:11.5px;font-weight:600;color:var(--brand);background:var(--brand-soft);border:0;border-radius:6px;cursor:pointer;font-family:inherit;transition:background .12s}
        .ins-mini:hover{background:color-mix(in srgb, var(--brand) 18%, white)}
        .ins-card.active .ins-mini{background:var(--brand);color:#fff}

        .ins-summary{margin:0;font-size:14.5px;color:var(--ink-2);line-height:1.55}

        .ins-expand{margin-top:14px;background:transparent;border:0;padding:6px 0;color:var(--brand);font-size:13px;font-weight:600;cursor:pointer;font-family:inherit;display:inline-flex;align-items:center;gap:6px}
        .ins-expand:hover{color:var(--brand-deep);text-decoration:underline}

        .ins-full{margin-top:14px}
        .ins-full-divider{height:1px;background:var(--line-2);margin-bottom:14px}
        .ins-p{margin:0 0 10px;font-size:14.5px;color:var(--ink-2);line-height:1.65}
        .ins-p:last-child{margin-bottom:0}
        .ins-h{margin:18px 0 8px;font-size:14.5px;font-weight:700;color:var(--ink);letter-spacing:-.005em}
        .ins-h:first-child{margin-top:0}
        .ins-ul{margin:0 0 10px;padding-left:20px;font-size:14.5px;color:var(--ink-2);line-height:1.65}
        .ins-ul li{margin-bottom:4px}
      `}</style>
    </div>
  );
}

const FALLBACK_CHAPTERS = INSTRUCTION_CHAPTERS.map(c => ({
  title: c.t,
  duration: c.d,
  shortDescription: c.s,
  body: c.body,
  embedUrl: null,
}));

window.SetupInstruction = SetupInstruction;

/* ───── Fullscreen video player ─────
   Mobile-first: tapping "Смотреть" on a chapter opens this overlay covering
   the whole viewport. Includes chapter switcher, fake play state, close.
*/
function FullscreenPlayer({ chapters, active, setActive, onClose, onPickAndExpand, onCloseAndRead }) {
  const [showList, setShowList] = React.useState(false);
  const [videoLoading, setVideoLoading] = React.useState(true);
  const stageInnerRef = React.useRef(null);
  const chapter = chapters[active];
  const currentVideo = chapters[active]?.embedUrl ? chapters[active] : null;

  React.useEffect(() => { setVideoLoading(true); }, [active]);

  // Force reflow of the video container on orientation change without
  // remounting the iframe (which would restart the video).
  React.useEffect(() => {
    const reflow = () => {
      const el = stageInnerRef.current;
      if (!el) return;
      // Briefly hide → force reflow → restore. Imperceptible but fixes
      // the stuck-iframe-dimensions bug on mobile orientation change.
      el.style.visibility = "hidden";
      void el.offsetWidth; // trigger reflow
      el.style.visibility = "";
    };
    window.addEventListener("orientationchange", reflow);
    screen.orientation?.addEventListener("change", reflow);
    return () => {
      window.removeEventListener("orientationchange", reflow);
      screen.orientation?.removeEventListener("change", reflow);
    };
  }, []);

  React.useEffect(() => {
    document.body.style.overflow = "hidden";
    const onKey = (e) => {
      if (e.key === "Escape") onClose();
      if (e.key === "ArrowLeft" && active > 0) setActive(active - 1);
      if (e.key === "ArrowRight" && active < chapters.length - 1) setActive(active + 1);
    };
    window.addEventListener("keydown", onKey);
    return () => {
      document.body.style.overflow = "";
      window.removeEventListener("keydown", onKey);
    };
  }, [active, chapters.length, onClose, setActive]);

  const pick = (i) => {
    setActive(i);
    setShowList(false);
    onPickAndExpand && onPickAndExpand(i);
  };

  const closeAndExpand = () => {
    onCloseAndRead && onCloseAndRead(active);
  };

  return (
    <div className="fsp" role="dialog" aria-modal="true">
      <div className="fsp-bg" onClick={onClose}/>

      <button className="fsp-close" onClick={onClose} aria-label="Закрыть">×</button>

      <div className="fsp-stage">
        <div className="fsp-stage-bg"/>
        <div className="fsp-stage-noise"/>

        <div className="fsp-stage-inner" ref={stageInnerRef}>
          {/* No ?autoplay=1 — avoids the Kinescope SDK handshake timeout (2 s) that
              freezes playback on mobile. Kinescope shows its own native play button;
              the user's direct tap on it is always trusted by the browser. */}
          {currentVideo ? (
            <>
              <iframe
                key={active}
                src={currentVideo.embedUrl}
                allow="fullscreen; picture-in-picture; encrypted-media; screen-wake-lock"
                style={{position:"absolute",inset:0,width:"100%",height:"100%",border:0}}
                onLoad={() => setVideoLoading(false)}
              />
              {videoLoading && (
                <div style={{position:"absolute",inset:0,display:"flex",alignItems:"center",
                  justifyContent:"center",background:"rgba(10,13,20,.85)",zIndex:5,pointerEvents:"none"}}>
                  <span style={{display:"inline-block",width:44,height:44,
                    border:"3px solid rgba(255,255,255,.2)",borderTopColor:"#fff",
                    borderRadius:"50%",animation:"spin .8s linear infinite"}}/>
                </div>
              )}
            </>
          ) : (
            <div className="fsp-playing">
              <span style={{fontSize:13,opacity:.6,color:"#fff",textAlign:"center"}}>Видео скоро появится</span>
            </div>
          )}
        </div>

        <div className="fsp-top">
          <div className="fsp-chip">Глава {active + 1} из {chapters.length}</div>
          {chapter.duration && <div className="fsp-chip">{chapter.duration}</div>}
        </div>
      </div>

      <div className="fsp-bottom">
        <div className="fsp-title">
          <small>Сейчас играет</small>
          <b>{chapter.title}</b>
        </div>
        <p className="fsp-sum">{chapter.shortDescription}</p>

        <div className="fsp-nav">
          <button className="fsp-nav-btn" onClick={() => pick(Math.max(0, active-1))} disabled={active===0}>
            <Icon.ArrowLeft style={{width:14,height:14}}/> Пред.
          </button>
          <button className="fsp-read" onClick={closeAndExpand}>
            <Icon.Doc style={{width:14,height:14}}/> Читать текст
          </button>
          <button className="fsp-nav-btn" onClick={() => pick(Math.min(chapters.length-1, active+1))} disabled={active===chapters.length-1}>
            След. <Icon.Arrow style={{width:14,height:14}}/>
          </button>
        </div>

        <button className="fsp-listbtn" onClick={() => setShowList(true)}>
          <span className="fsp-listbtn-ico">
            <span></span><span></span><span></span>
          </span>
          Все главы · {chapters.length}
        </button>
      </div>

      {showList && (
        <div className="fsp-sheet anim-in" onClick={(e)=>e.stopPropagation()}>
          <div className="fsp-sheet-bg" onClick={()=>setShowList(false)}/>
          <div className="fsp-sheet-panel">
            <div className="fsp-sheet-grab"/>
            <div className="fsp-sheet-hd">
              <b>Все главы</b>
              <span>{chapters.length} глав · {(chapters.reduce((a,c)=>{if(!c.duration)return a;const[m,s]=c.duration.split(":").map(Number);return a+(m||0)*60+(s||0);},0)/60)|0} мин</span>
              <button className="fsp-sheet-close" onClick={()=>setShowList(false)} aria-label="Закрыть">×</button>
            </div>
            <div className="fsp-sheet-list">
              {chapters.map((c, i) => (
                <button key={i} className={"fsp-sheet-item" + (i===active?" active":"")} onClick={()=>pick(i)}>
                  <span className="fsp-sheet-n">{String(i+1).padStart(2,"0")}</span>
                  <span className="fsp-sheet-body">
                    <b>{c.title}</b>
                    <small>{c.shortDescription}</small>
                  </span>
                  <span className="fsp-sheet-d">{c.duration || ""}</span>
                </button>
              ))}
            </div>
          </div>
        </div>
      )}

      <div className="fsp-chaplist-removed" style={{display:"none"}}></div>

      <style>{`
        .fsp{position:fixed;inset:0;z-index:99000;background:#0a0d14;
             height:100dvh;width:100dvw; /* dvh/dvw recalculate after orientation change */
             color:#fff;animation:fadeIn .2s ease;display:flex;flex-direction:column}
        .fsp-bg{display:none}

        .fsp-close{
          position:absolute;top:12px;right:12px;z-index:5;
          width:40px;height:40px;border-radius:50%;
          background:rgba(255,255,255,.12);color:#fff;border:0;
          font-size:24px;line-height:1;cursor:pointer;
          backdrop-filter:blur(10px);
        }
        .fsp-close:hover{background:rgba(255,255,255,.2)}

        .fsp-stage{
          position:relative;width:100%;flex:1;min-height:0;
          display:flex;align-items:center;justify-content:center;
          cursor:pointer;overflow:hidden;
        }
        .fsp-stage-inner{
          position:relative;width:100%;
          aspect-ratio:16/9;max-height:100%;overflow:hidden;
        }
        .fsp-stage-bg{
          position:absolute;inset:0;
          background:linear-gradient(135deg, color-mix(in srgb, var(--brand) 40%, #0f172a) 0%, #060810 100%);
        }
        .fsp-stage-noise{
          position:absolute;inset:0;
          background:
            radial-gradient(circle at 25% 30%, color-mix(in srgb, var(--brand) 35%, transparent), transparent 50%),
            radial-gradient(circle at 75% 70%, rgba(255,255,255,.06), transparent 45%);
        }

        .fsp-play{
          position:relative;z-index:2;
          width:84px;height:84px;border-radius:50%;
          background:rgba(255,255,255,.96);display:grid;place-items:center;
          box-shadow:0 10px 40px rgba(0,0,0,.5);
          transition:transform .2s;
        }
        .fsp-stage:hover .fsp-play{transform:scale(1.06)}
        .fsp-play-tri{width:0;height:0;border-left:22px solid var(--brand);
          border-top:14px solid transparent;border-bottom:14px solid transparent;margin-left:5px}
        .fsp-playing{position:relative;z-index:2;display:flex;flex-direction:column;align-items:center}

        .fsp-top{
          position:absolute;top:14px;left:14px;display:flex;gap:8px;z-index:3;
        }
        .fsp-chip{
          padding:4px 10px;border-radius:999px;font-size:11.5px;font-weight:600;
          background:rgba(0,0,0,.5);backdrop-filter:blur(8px);letter-spacing:.02em;
        }

        .fsp-bottom{
          padding:14px 18px 16px;
          background:#0d1119;
          border-top:1px solid rgba(255,255,255,.08);
          flex-shrink:0;
        }
        .fsp-title small{display:block;font-size:11px;text-transform:uppercase;letter-spacing:.12em;opacity:.7;font-weight:600;margin-bottom:4px}
        .fsp-title b{display:block;font-size:19px;font-weight:700;letter-spacing:-.01em;line-height:1.25}
        .fsp-sum{margin:8px 0 14px;font-size:13.5px;color:rgba(255,255,255,.78);line-height:1.5;max-width:680px}

        .fsp-nav{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
        .fsp-nav-btn{
          height:38px;padding:0 12px;font-size:13px;font-weight:600;
          color:#fff;background:rgba(255,255,255,.12);border:0;border-radius:9px;
          cursor:pointer;display:inline-flex;align-items:center;gap:6px;
          font-family:inherit;transition:background .12s;
          backdrop-filter:blur(8px);
        }
        .fsp-nav-btn:not(:disabled):hover{background:rgba(255,255,255,.22)}
        .fsp-nav-btn:disabled{opacity:.35;cursor:not-allowed}
        .fsp-read{
          height:38px;padding:0 14px;font-size:13px;font-weight:600;
          color:var(--brand-deep);background:#fff;border:0;border-radius:9px;
          cursor:pointer;display:inline-flex;align-items:center;gap:6px;
          font-family:inherit;
        }
        .fsp-read:hover{background:color-mix(in srgb, var(--brand) 14%, #fff)}

        /* "All chapters" button */
        .fsp-listbtn{
          margin-top:10px;width:100%;height:42px;border-radius:10px;
          background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.14);
          color:#fff;font-size:13.5px;font-weight:600;letter-spacing:-.005em;
          cursor:pointer;font-family:inherit;
          display:inline-flex;align-items:center;justify-content:center;gap:10px;
          transition:background .15s, border-color .15s;
          backdrop-filter:blur(8px);
        }
        .fsp-listbtn:hover{background:rgba(255,255,255,.16);border-color:rgba(255,255,255,.22)}
        .fsp-listbtn-ico{display:inline-flex;flex-direction:column;gap:2.5px;align-items:center;justify-content:center}
        .fsp-listbtn-ico span{display:block;width:14px;height:2px;background:#fff;border-radius:1px;opacity:.85}

        /* Bottom sheet — chapter list */
        .fsp-sheet{
          position:fixed;inset:0;z-index:5;
          display:flex;flex-direction:column;justify-content:flex-end;
        }
        .fsp-sheet-bg{
          position:absolute;inset:0;background:rgba(0,0,0,.5);
          backdrop-filter:blur(4px);animation:fadeIn .2s ease;
        }
        .fsp-sheet-panel{
          position:relative;background:#10141f;
          border-radius:16px 16px 0 0;border-top:1px solid rgba(255,255,255,.08);
          max-height:80vh;display:flex;flex-direction:column;
          animation:sheetUp .25s cubic-bezier(.2,.8,.2,1);
          box-shadow:0 -20px 60px rgba(0,0,0,.5);
        }
        @keyframes sheetUp{from{transform:translateY(100%)}to{transform:translateY(0)}}
        .fsp-sheet-grab{
          width:36px;height:4px;border-radius:2px;
          background:rgba(255,255,255,.25);margin:8px auto 0;flex-shrink:0;
        }
        .fsp-sheet-hd{
          padding:12px 18px 12px;display:flex;align-items:center;gap:8px;
          border-bottom:1px solid rgba(255,255,255,.06);flex-shrink:0;
        }
        .fsp-sheet-hd b{font-size:16px;font-weight:700;letter-spacing:-.01em}
        .fsp-sheet-hd span{font-size:12px;color:rgba(255,255,255,.5);font-weight:500}
        .fsp-sheet-close{
          margin-left:auto;width:32px;height:32px;border-radius:50%;
          background:rgba(255,255,255,.08);color:#fff;border:0;
          font-size:20px;line-height:1;cursor:pointer;
        }
        .fsp-sheet-close:hover{background:rgba(255,255,255,.16)}
        .fsp-sheet-list{
          overflow-y:auto;-webkit-overflow-scrolling:touch;
          padding:8px;flex:1;
        }
        .fsp-sheet-item{
          width:100%;display:flex;align-items:flex-start;gap:12px;
          padding:12px 14px;border-radius:10px;border:0;
          background:transparent;color:#fff;cursor:pointer;
          font-family:inherit;text-align:left;
          transition:background .12s;
        }
        .fsp-sheet-item:hover{background:rgba(255,255,255,.05)}
        .fsp-sheet-item.active{background:color-mix(in srgb, var(--brand) 18%, transparent)}
        .fsp-sheet-n{
          flex-shrink:0;width:28px;font-size:11.5px;font-weight:700;
          color:rgba(255,255,255,.5);font-variant-numeric:tabular-nums;
          letter-spacing:.02em;padding-top:2px;
        }
        .fsp-sheet-item.active .fsp-sheet-n{color:var(--brand)}
        .fsp-sheet-body{flex:1;min-width:0}
        .fsp-sheet-body b{
          display:block;font-size:14px;font-weight:600;
          letter-spacing:-.005em;line-height:1.3;margin-bottom:3px;
        }
        .fsp-sheet-body small{
          display:block;font-size:12px;color:rgba(255,255,255,.55);
          line-height:1.45;
          display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
        }
        .fsp-sheet-d{
          flex-shrink:0;font-size:11.5px;color:rgba(255,255,255,.5);
          font-variant-numeric:tabular-nums;padding-top:3px;
        }

        /* Landscape phone — pack tighter */
        @media (max-height:480px){
          .fsp-play{width:64px;height:64px}
          .fsp-play-tri{border-left-width:18px;border-top-width:11px;border-bottom-width:11px}
          .fsp-bottom{padding:10px 14px 10px}
          .fsp-title b{font-size:16px}
          .fsp-sum{font-size:12.5px;margin:4px 0 10px;-webkit-line-clamp:2;display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden}
          .fsp-listbtn{margin-top:6px;height:36px;font-size:12.5px}
          .fsp-sheet-panel{max-height:95vh}
        }

        /* Larger screens — keep modal but with margin */
        @media (min-width:981px){
          .fsp{padding:24px}
          .fsp-stage{border-radius:14px 14px 0 0}
          .fsp-bottom{border-radius:0 0 14px 14px}
        }
      `}</style>
    </div>
  );
}
