import tkinter as tk
from tkinter import ttk, messagebox
import json
import os
import threading
import requests
import re
from PIL import Image, ImageTk

BG_MAIN = "#f5f5f5"      
BG_PANEL = "#ffffff"     
FG_TEXT = "#111827"      
FG_MUTED = "#4b5563"    
ACCENT_BLUE = "#2563eb"
ACCENT_ORANGE = "#f97316"
ACCENT_GREEN = "#16a34a"


# ======================
# "СЕРВЕР" ПРОГРЕССА (локальный JSON)
# ======================

PROGRESS_FILE = "progress_store.json"

def server_load_progress(name: str):
    if not os.path.exists(PROGRESS_FILE):
        return 0, set()
    try:
        with open(PROGRESS_FILE, "r", encoding="utf-8") as f:
            data = json.load(f)
        user = data.get(name)
        if isinstance(user, dict):
            score = user.get("score", 0)
            tabs = set(user.get("tabs", []))
            return score, tabs
        if isinstance(user, int):
            return user, set()
        return 0, set()
    except Exception:
        return 0, set()


def server_save_progress(name: str, score: int, tabs_completed: set[int]):
    data = {}
    if os.path.exists(PROGRESS_FILE):
        try:
            with open(PROGRESS_FILE, "r", encoding="utf-8") as f:
                data = json.load(f)
        except Exception:
            data = {}
    data[name] = {
        "score": score,
        "tabs": sorted(tabs_completed),
    }
    with open(PROGRESS_FILE, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)

# ======================
# ДАННЫЕ ДЛЯ ТАБЛИЦЫ ЭЛЕМЕНТОВ
# ======================

ELEMENTS_INFO = {
    "H": "Водород — самый лёгкий и распространённый элемент во Вселенной. Входит в состав воды (H₂O).",
    "He": "Гелий впервые обнаружили не на Земле, а в спектре Солнца, задолго до находки в земной атмосфере.",
    "Li": "Литий — самый лёгкий металл, его применяют в аккумуляторах смартфонов и ноутбуков.",
    "Be": "Бериллий делает сплавы очень жёсткими и лёгкими, поэтому его используют в авиации и космической технике.",
    "B": "Бор входит в состав боросиликатного стекла, из которого делают прочную лабораторную посуду и стекло для духовок.",
    "C": "Углерод образует тысячи соединений и лежит в основе всей органической жизни на Земле.",
    "N": "Азот составляет около 78% воздуха, но в чистом виде мы его не ощущаем — он почти инертен при обычных условиях.",
    "O": "Кислород поддерживает дыхание и горение, а в виде озона защищает нас от жёсткого ультрафиолета.",
    "F": "Фтор — самый реакционноспособный неметалл, способен воспламенять многие вещества при комнатной температуре.",
    "Ne": "Неон даёт яркое оранжево‑красное свечение в газоразрядных лампах, поэтому его используют в рекламных вывесках.",
    "Na": "Натрий в чистом виде взрывоопасен в воде, но в виде поваренной соли жизненно важен для нервов и мышц.",
    "Mg": "Магний при горении излучает ослепительно яркий белый свет, поэтому его раньше использовали во вспышках фотокамер.",
    "Al": "Алюминий легче стали, но достаточно прочен, и из него делают корпуса самолётов, велосипедов и банок.",
    "Si": "Кремний — основа микросхем и солнечных батарей, его называют «сердцем» современной электроники.",
    "P": "Фосфор может светиться в темноте, а его соединения входят в состав ДНК и костной ткани.",
    "S": "Сера придаёт запах «тухлых яиц» сероводороду, но её соединения важны для резины и лекарств.",
    "Cl": "Хлор в чистом виде ядовит, однако его соединения используют для дезинфекции воды и отбеливания тканей.",
    "Ar": "Аргон — инертный газ, которым заполняют лампочки, чтобы их нити накала не сгорали слишком быстро.",
    "K": "Калий необходим для работы сердца и нервной системы, а его соли используют как удобрения.",
    "Ca": "Кальций укрепляет кости и зубы, а карбонат кальция образует известняк, мрамор и раковины.",
    "Sc": "Скандий используют в дорогих спортивных велосипедах и прожекторах благодаря прочным и лёгким сплавам.",
    "Ti": "Титан не ржавеет в морской воде и в теле человека, поэтому из него делают импланты и корпуса подлодок.",
    "V": "Ванадий в малых добавках делает сталь гораздо прочнее и устойчивее к износу.",
    "Cr": "Хром придаёт стали нержавеющие свойства и используется для блестящих хромированных покрытий.",
    "Mn": "Марганец улучшает свойства стали, а его соединения дают ярко‑фиолетовый цвет растворам.",
    "Fe": "Железо — основа стали и ключевой элемент гемоглобина, который переносит кислород в крови.",
    "Co": "Кобальт входит в состав мощных магнитов и придаёт стеклу и керамике насыщенный синий цвет.",
    "Ni": "Никель устойчив к коррозии, поэтому его используют для монет и защитных покрытий.",
    "Cu": "Медь — отличный проводник электричества, поэтому её широко применяют в проводах и кабелях.",
    "Zn": "Цинк защищает сталь от ржавчины при оцинковке и входит в состав многих ферментов.",
    "Ga": "Галлий может плавиться прямо в руке, а его соединения используются в светодиодах и лазерах.",
    "Ge": "Германий был важным материалом для первых транзисторов и до сих пор используется в оптике и электронике.",
    "As": "Мышьяк известен как яд, но в микродозах его соединения применяют в медицине и полупроводниках.",
    "Se": "Селен помогает создавать светочувствительные элементы и входит в состав некоторых витаминов.",
    "Br": "Бром — единственный неметалл, который при комнатной температуре является тяжёлой бурой жидкостью.",
    "Kr": "Криптон применяют в газоразрядных лампах и некоторых лазерах для яркого белого света.",
    "Rb": "Рубидий очень реактивен и используется в атомных часах и экспериментах с холодными атомами.",
    "Sr": "Стронций придаёт фейерверкам красивый ярко‑красный цвет.",
    "Y": "Иттрий входит в состав некоторых сверхпроводников и красных люминофоров в экранах.",
    "Zr": "Цирконий почти не поглощает нейтроны, поэтому его используют в оболочках твэлов в ядерных реакторах.",
    "Nb": "Ниобий добавляют к стали для повышения прочности и используют в сверхпроводящих магнитах.",
    "Mo": "Молибден улучшает жаропрочность сталей и применяется в катализаторах нефтехимии.",
    "Tc": "Технеций — первый искусственно полученный элемент, его радиоизотопы используют в диагностике органов.",
    "Ru": "Рутений добавляют к платине для повышения твёрдости и используют в электронике.",
    "Rh": "Родий — один из самых дорогих металлов, применяется в каталитических нейтрализаторах выхлопных газов.",
    "Pd": "Палладий активно поглощает водород и используется в авто‑катализаторах и электронике.",
    "Ag": "Серебро обладает лучшей электропроводностью среди металлов и мягким антисептическим действием.",
    "Cd": "Кадмий токсичен, но его соединения применяют в аккумуляторах и ярких жёлтых пигментах.",
    "In": "Индий входит в состав прозрачных проводящих покрытий на экранах смартфонов и мониторов.",
    "Sn": "Олово используют для лужения железа в жестяных банках и для получения бронзы в сплаве с медью.",
    "Sb": "Сурьма придаёт сплавам твёрдость и используется в огнезащитных материалах.",
    "Te": "Теллур в малых количествах сильно изменяет свойства сплавов и применяется в солнечных батареях.",
    "I": "Йод необходим для работы щитовидной железы и входит в состав йодной настойки для обработки ран.",
    "Xe": "Ксенон используют в ярких автомобильных фарах и некоторых видах анестезии.",
    "Cs": "Цезий применяют в самых точных атомных часах, определяющих стандарт времени.",
    "Ba": "Барий даёт зелёное свечение фейерверкам, а его сульфат используют в рентгенодиагностике.",
    "La": "Лантан повышает яркость ламп и используется в оптическом стекле и аккумуляторах.",
    "Ce": "Церий входит в сплавы для «огнива» зажигалок и в порошки для полировки стёкол.",
    "Pr": "Празеодим придаёт стеклу и керамике приятный зеленоватый оттенок.",
    "Nd": "Неодим образует чрезвычайно сильные магниты, используемые в наушниках и жёстких дисках.",
    "Pm": "Прометий радиоактивен и встречается в природе в следовых количествах, его используют в светоизлучающих устройствах.",
    "Sm": "Самарий входит в состав магнитов и применяется в управлении реактивными двигателями.",
    "Eu": "Европий даёт яркое красное свечение в люминофорах телевизионных и компьютерных экранов.",
    "Gd": "Гадолиний используют как контрастное вещество при МРТ‑исследованиях.",
    "Tb": "Тербий даёт зелёное свечение люминофоров и интересен эффектом магнитокалорического охлаждения.",
    "Dy": "Диспрозий улучшает термостойкость магнитов, используемых в ветроэнергетике и электромобилях.",
    "Ho": "Гольмий обладает сильной магнитной способностью и применяется в некоторых лазерах и магнитных материалах.",
    "Er": "Эрбий придаёт стеклу розово‑фиолетовый цвет и используется в усилителях оптоволоконной связи.",
    "Tm": "Тулий — один из самых редких элементов, его применяют в медицинских лазерах.",
    "Yb": "Иттербий используют в волоконных лазерах и некоторых типах атомных часов.",
    "Lu": "Лютеций применяют в детекторах радиации и медицинской визуализации.",
    "Hf": "Гафний устойчив к высоким температурам и используется в ядерных реакторах и микросхемах.",
    "Ta": "Тантал не боится коррозии, поэтому из него делают конденсаторы и медицинские инструменты.",
    "W": "Вольфрам имеет одну из самых высоких температур плавления и раньше использовался в нитях ламп накаливания.",
    "Re": "Рений добавляют в жаропрочные сплавы для турбин реактивных двигателей.",
    "Os": "Осмий — один из самых плотных металлов, его сплавы применяют в износостойких контактах.",
    "Ir": "Иридий устойчив к коррозии даже в кипящей кислоте и используется в платиновых сплавах и свечах зажигания.",
    "Pt": "Платина ценится как благородный металл и используется в ювелирных изделиях и каталитических нейтрализаторах.",
    "Au": "Золото не ржавеет и хорошо проводит ток, поэтому его применяют и в ювелирке, и в электронике.",
    "Hg": "Ртуть — единственный металл, жидкий при комнатной температуре; её раньше использовали в термометрах.",
    "Tl": "Таллий токсичен, но его соединения применяют в оптике и инфракрасных детекторах.",
    "Pb": "Свинец хорошо защищает от радиации и раньше широко использовался в трубах и красках.",
    "Bi": "Висмут менее токсичен, чем многие тяжёлые металлы, и применяется в лекарствах и легкоплавких сплавах.",
    "Po": "Полоний сильно радиоактивен и встречается в природе в очень малых количествах.",
    "At": "Астат — крайне редкий и радиоактивный галоген, его общее количество в земной коре оценивают в граммах.",
    "Rn": "Радон — радиоактивный благородный газ, который может накапливаться в подвалах и шахтах.",
    "Fr": "Франций — один из самых редких и нестабильных элементов, в природе его практически нет в заметных количествах.",
    "Ra": "Радий исторически применяли в светящейся краске для циферблатов, но из‑за радиоактивности от этого отказались.",
    "Ac": "Актиний светится в темноте из‑за собственной радиоактивности и используется в научных исследованиях.",
    "Th": "Торий рассматривают как возможное топливо для перспективных ядерных реакторов.",
    "Pa": "Протактиний чрезвычайно редок и в основном интересен учёным как объект исследований.",
    "U": "Уран — основное топливо для атомных электростанций и источник энергии в ядерных реакторах.",
    "Np": "Нептуний — искусственный элемент, который может служить сырьём для получения плутония.",
    "Pu": "Плутоний используется в ядерном оружии и радиоизотопных генераторах для космических аппаратов.",
    "Am": "Америций входит в состав датчиков дыма в бытовых пожарных извещателях.",
    "Cm": "Кюрий назван в честь супругов Кюри и используется как мощный источник альфа‑излучения.",
    "Bk": "Берклий — искусственный элемент, полученный в ядерных реакторах и ускорителях.",
    "Cf": "Калифорний применяют как интенсивный источник нейтронов в анализе материалов.",
    "Es": "Эйнштейний назван в честь Альберта Эйнштейна и существует только в микроскопических количествах.",
    "Fm": "Фермий обнаружили в продуктах термоядерного взрыва и изучают лишь в лабораториях.",
    "Md": "Менделевий назван в честь Дмитрия Менделеева и существует только как лабораторный элемент.",
    "No": "Нобелий получил имя в честь Альфреда Нобеля и используется лишь в научных исследованиях.",
    "Lr": "Лорансий назван в честь Эрнеста Лоуренса, создателя циклотронных ускорителей.",
    "Rf": "Резерфордий носит имя Эрнеста Резерфорда и относится к трансакиноидам.",
    "Db": "Дубний назван в честь российского научного центра в Дубне, где его изучали.",
    "Sg": "Сиборгий увековечивает имя химика Гленна Сиборга, работавшего с трансурановыми элементами.",
    "Bh": "Борий назван в честь Нильса Бора, одного из создателей современной атомной модели.",
    "Hs": "Хассий получил имя от немецкой земли Гессен, где расположен центр его исследований.",
    "Mt": "Мейтнерий назван в честь физика Лизы Мейтнер, участвовавшей в открытии ядерного деления.",
    "Ds": "Дармштадтий получил имя от города Дармштадт в Германии, где его впервые синтезировали.",
    "Rg": "Рентгений назван в честь Вильгельма Рентгена, открывшего рентгеновские лучи.",
    "Cn": "Коперниций увековечивает имя астронома Николая Коперника.",
    "Nh": "Нихоний назван по поэтическому названию Японии — «Нихон», страна восходящего солнца.",
    "Fl": "Флеровий получил имя в честь Флёровской лаборатории ядерных реакций в Дубне.",
    "Mc": "Московий назван в честь Московской области, где расположен Объединённый институт ядерных исследований.",
    "Lv": "Ливерморий увековечивает Ливерморскую национальную лабораторию в США.",
    "Ts": "Теннессин назван по американскому штату Теннесси, участвовавшему в его открытии.",
    "Og": "Оганесон назван в честь российского физика Юрия Оганесяна, исследователя сверхтяжёлых элементов.",
    "  ": "Группа лантаноидов от Лантана до Лютеция.",
    " ": "Группа актиноидов от Актиния до Лорансия."
}

PERIODIC_LAYOUT = [
    ["H", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "He"],
    ["Li", "Be", "", "", "", "", "", "", "", "", "", "", "B", "C", "N", "O", "F", "Ne"],
    ["Na", "Mg", "", "", "", "", "", "", "", "", "", "", "Al", "Si", "P", "S", "Cl", "Ar"],
    ["K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr"],
    ["Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe"],
    ["Cs", "Ba", "  ", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn"],
    ["Fr", "Ra", " ", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", "Rg", "Cn", "Nh", "Fl", "Mc", "Lv", "Ts", "Og"],
    ["", "", "", "La", "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu"],
    ["", "", "", "Ac", "Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", "Md", "No", "Lr"]
]

ELEMENT_COLORS = {}

def add_group(symbols, color):
    for s in symbols:
        ELEMENT_COLORS[s] = color

add_group(["H", "C", "N", "O", "P", "S", "Se"], "#a3e3a1")
add_group(["He", "Ne", "Ar", "Kr", "Xe", "Rn", "Og"], "#a1e9ff")
add_group(["Li", "Na", "K", "Rb", "Cs", "Fr"], "#ff6b6b")
add_group(["Be", "Mg", "Ca", "Sr", "Ba", "Ra"], "#ffe28a")
add_group(["F", "Cl", "Br", "I", "At", "Ts"], "#FFFC5D")
add_group([
    "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn",
    "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd",
    "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg",
    "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", "Rg", "Cn"
], "#E48F6F")
add_group(["  ", "La", "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu"], "#E66CDE")
add_group([" ", "Ac", "Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", "Md", "No", "Lr"], "#DE0076")
add_group(["Al", "Ga", "In", "Sn", "Tl", "Pb", "Bi", "Nh", "Fl", "Mc", "Lv"], "#616161")
add_group(["B", "Si", "Ge", "As", "Sb", "Te", "Po"], "#877146")

DEFAULT_ELEMENT_BG = "#111827"

# ======================
# ВЕЩЕСТВА (3-я вкладка)
# ======================

SIMPLE_SUBSTANCES = [
    ("Вода", "H₂O",
     "Вода необходима для жизни и используется повсеместно для питья, готовки, уборки и других нужд."),
    ("Сахар", "C₁₂H₂₂O₁₁",
     "Сахароза или столовый сахар используется как подсластитель в пище и напитках."),
    ("Пищевая сода", "NaHCO₃",
     "Пищевая сода используется в кулинарии как разрыхлитель, а также в быту и для уборки."),
    ("Уксусная кислота", "CH₃COOH",
     "Уксусная кислота (5–9%) как столовый уксус — для кулинарии и консервирования."),
    ("Соль", "NaCl",
     "Поваренная соль — приправа и консервант."),
    ("Этиловый спирт", "C₂H₅OH",
     "Этиловый спирт — в напитках, дезинфекции и медицине."),
    ("Аммиак", "NH₃",
     "Аммиак — чистящее средство и компонент удобрений."),
    ("Перекись водорода", "H₂O₂",
     "Перекись водорода — антисептик и отбеливатель."),
    ("Мыло", "C₁₇H₃₅COONa",
     "Мыло — соли жирных кислот, для мытья и чистки."),
    ("Марганцовка", "KMnO₄",
     "Марганцовка — дезинфицирующее средство и окислитель."),
    ("Мел", "CaCO₃",
     "Мел / мрамор / известняк — в строительстве, кормах, мелках."),
]


# ======================
# БАНК СОЕДИНЕНИЙ ДЛЯ СОБСТВЕННОГО ЭКСПЕРИМЕНТА (20 шт.)
# ======================

COMPOUND_BANK = [
    ("Water", "H2O", "Вода — универсальный растворитель, продукт реакции водорода с кислородом."),
    ("Carbon dioxide", "CO2", "Углекислый газ — продукт горения и дыхания, выделяется при реакциях кислот с карбонатами."),
    ("Methane", "CH4", "Метан — основной компонент природного газа, продукт разложения органики без доступа кислорода."),
    ("Ammonia", "NH3", "Аммиак — получают из азота и водорода по процессу Габера-Боша."),
    ("Sodium chloride", "NaCl", "Поваренная соль — продукт нейтрализации соляной кислоты щёлочью или растворения каменной соли."),
    ("Hydrochloric acid", "HCl", "Соляная кислота — раствор хлороводорода в воде, получают из хлора и водорода."),
    ("Sulfuric acid", "H2SO4", "Серная кислота — сильная кислота, получают окислением SO2 и поглощением водой."),
    ("Nitric acid", "HNO3", "Азотная кислота — продукт окисления аммиака (процесс Оствальда)."),
    ("Ethanol", "C2H5OH", "Этиловый спирт — получают брожением сахаров или гидратацией этилена."),
    ("Glucose", "C6H12O6", "Глюкоза — основной сахар в крови, образуется при фотосинтезе из CO2 и воды."),
    ("Sodium hydroxide", "NaOH", "Едкий натр — получают электролизом растворов хлорида натрия."),
    ("Calcium carbonate", "CaCO3", "Карбонат кальция — в известняке и мраморе, выделяется из растворов при взаимодействии Ca2+ и CO3 2-."),
    ("Sodium bicarbonate", "NaHCO3", "Пищевая сода — продукт взаимодействия CO2 с раствором карбоната натрия."),
    ("Acetic acid", "CH3COOH", "Уксусная кислота — получают окислением этанола или синтезом из метанола и CO."),
    ("Sodium acetate", "CH3COONa", "Ацетат натрия — продукт нейтрализации уксусной кислоты щёлочью или содой."),
    ("Calcium hydroxide", "Ca(OH)2", "Гашёная известь — получается при взаимодействии оксида кальция с водой."),
    ("Magnesium sulfate", "MgSO4", "Сульфат магния — часто в виде семиводного кристаллогидрата, получают из минералов."),
    ("Copper sulfate", "CuSO4", "Медный купорос — получают растворением меди в серной кислоте с окислителем."),
    ("Sodium sulfate", "Na2SO4", "Сульфат натрия — образуется при нейтрализации серной кислоты растворами щелочей."),
    ("Potassium permanganate", "KMnO4", "Перманганат калия — сильный окислитель, получают электрохимическими методами."),
]


# ======================
# ЭКСПЕРИМЕНТЫ (4-я вкладка, укороченный список)
# ======================

EXPERIMENTS = [
    (
        "Эксперимент 1. «Стреляющая» бутылочка",
        "Реагенты: мрамор (карбонат кальция, CaCO₃) или мел (CaCO₃), "
        "соляная кислота (хлороводород, HCl).\n\n"
        "Способ обработки: в бутылку из‑под вина кладут кусочки мрамора или мела, "
        "приливают разведённую соляную кислоту и закрывают пробкой (не слишком туго). "
        "Бутылку заворачивают в полотенце. Через несколько минут пробка вылетает из‑за давления "
        "углекислого газа.\n\n"
        "Уравнение реакции:\nCaCO₃ + 2HCl → CaCl₂ + CO₂↑ + H₂O",
        ["Ca", "C", "O", "H", "Cl"],
        ["соединение", "разложение", "обмен"]
    ),
    (
        "Эксперимент 2. Танец «бабочек»",
        "Реагенты: уксусная кислота (CH₃COOH), гидрокарбонат натрия (NaHCO₃), "
        "углекислый газ (CO₂).\n\n"
        "Способ обработки: в банку наливают уксусную кислоту так, чтобы нижний конец "
        "воронки не доставал до поверхности кислоты на 1 см. Через воронку бросают таблетки "
        "гидрокарбоната натрия, а в воронку помещают «бабочек» из папиросной бумаги. "
        "Они начинают «танцевать» в струе углекислого газа.\n\n"
        "Уравнение реакции:\nNaHCO₃ + CH₃COOH → CH₃COONa + CO₂↑ + H₂O",
        ["Na", "H", "C", "O"],
        ["обмен", "соединение"]
    ),
    (
        "Эксперимент 3. Йод и крахмал",
        "Реагенты: раствор йода в йодиде калия (I₂/KI), крахмал (полисахарид на основе "
        "глюкозы, общая формула (C₆H₁₀O₅)n), разбавленная соляная кислота (HCl).\n\n"
        "Способ обработки: в стакан с водой добавляют несколько капель раствора йода и 1–2 мл "
        "соляной кислоты, затем около 0,5 мл раствора крахмала. Жидкость окрашивается в синий цвет. "
        "При нагревании обесцвечивается, при охлаждении снова синеет.\n\n"
        "Объяснение: образуется комплексное соединение крахмала с йодом.",
        ["I", "K", "H", "Cl", "C", "O"],
        ["соединение", "нагревание", "охлаждение"]
    ),
    (
        "Эксперимент 4. Плавающий картофель",
        "Реагенты: картофель (смесь воды, крахмала (общая формула (C₆H₁₀O₅)n) и других веществ), вода (H₂O), "
        "хлорид натрия (NaCl).\n\n"
        "Способ обработки: в литровую банку до половины наливают воды и кладут клубень картофеля — "
        "он тонет. Добавляют насыщенный раствор хлорида натрия — картофель всплывает. При добавлении "
        "чистой воды снова опускается на дно.\n\n"
        "Объяснение: плотность насыщенного раствора NaCl выше плотности воды и картофеля.",
        ["Na", "Cl", "H", "O", "C"],
        ["смешивание"]
    ),
    (
        "Эксперимент 5. Несгораемая бумага",
        "Реагенты: калийная селитра (нитрат калия, KNO₃), газетная бумага (смесь целлюлозы "
        "(C₆H₁₀O₅)n и добавок).\n\n"
        "Способ обработки: бумагу опускают на 5–7 минут в насыщенный раствор KNO₃, затем сушат. "
        "При внесении в пламя горелки бумага не горит, а тлеет.\n\n"
        "Объяснение: селитра разлагается с выделением кислорода, который поддерживает тление, но не горение.",
        ["K", "N", "O", "C", "H"],
        ["нагревание", "разложение"]
    ),
    (
        "Эксперимент 6. «Серебряный лес»",
        "Реагенты: медь (Cu), нитрат серебра (AgNO₃).\n\n"
        "Способ обработки: в пробирку помещают обезжиренный кусочек меди и приливают раствор AgNO₃ (1:10). "
        "Через несколько часов на меди появляются кристаллы серебра.\n\n"
        "Уравнение реакции:\nCu + 2AgNO₃ → Cu(NO₃)₂ + 2Ag↓",
        ["Cu", "Ag", "N", "O"],
        ["замещение"]
    ),
    (
        "Эксперимент 7. Свинцовая «шуба»",
        "Реагенты: цинк (Zn), хлорид олова(II) (SnCl₂).\n\n"
        "Способ обработки: цинковую фигурку опускают в раствор SnCl₂. Начинается реакция, и цинк "
        "покрывается иглами олова.\n\n"
        "Уравнение реакции:\nZn + SnCl₂ → ZnCl₂ + Sn↓",
        ["Zn", "Sn", "Cl"],
        ["замещение"]
    ),
    (
        "Эксперимент 8. Алхимическое «золото»",
        "Реагенты: нитрат свинца(II) (Pb(NO₃)₂), иодид калия (KI), иодид свинца(II) (PbI₂).\n\n"
        "Способ обработки: к 20 мл раствора Pb(NO₃)₂ (0,25 моль/л) приливают 40 мл раствора KI "
        "(0,5 моль/л). При охлаждении выпадают золотистые кристаллы иодида свинца.\n\n"
        "Уравнение реакции:\nPb(NO₃)₂ + 2KI → PbI₂↓ + 2KNO₃",
        ["Pb", "K", "I", "N", "O"],
        ["обмен", "соединение"]
    ),
    (
        "Эксперимент 9. Дым без огня",
        "Реагенты: аммиак (NH₃), хлороводород (HCl), хлорид аммония (NH₄Cl).\n\n"
        "Способ обработки: смешивают два газа в колбе. Появляется густой белый дым — "
        "кристаллы хлорида аммония.\n\n"
        "Уравнение реакции:\nNH₃ + HCl → NH₄Cl↓",
        ["N", "H", "Cl"],
        ["соединение"]
    ),
    (
        "Эксперимент 10. Выращивание кристаллов соли",
        "Реагенты: хлорид натрия (NaCl) или медный купорос (пентагидрат сульфата меди(II), CuSO₄·5H₂O), "
        "вода (H₂O).\n\n"
        "Способ обработки: готовят насыщенный раствор соли, опускают в него нитку или проволоку. "
        "Через несколько дней на них вырастают кристаллы.\n\n"
        "Объяснение: при испарении воды раствор становится пересыщенным, и соль кристаллизуется на затравке.",
        ["Na", "Cl", "Cu", "S", "O", "H"],
        ["смешивание", "испарение"]
    ),
    (
        "Эксперимент 11. Окрашивание пламени",
        "Реагенты: поваренная соль (NaCl), соляная кислота (HCl), медная проволока (Cu), вода (H₂O).\n\n"
        "Способ обработки: смоченный в воде конец медной проволоки окунают в соль и вносят в пламя горелки. "
        "Пламя окрашивается в жёлтый цвет (из‑за ионов натрия).\n\n"
        "Объяснение: ионы металлов при нагревании испускают свет определённой длины волны.",
        ["Na", "Cl", "H", "O", "Cu"],
        ["нагревание"]
    ),
    (
        "Эксперимент 12. Секретное послание молоком",
        "Реагенты: молоко (смесь воды (H₂O), белков, жиров и сахаров, например лактозы C₁₂H₂₂O₁₁).\n\n"
        "Способ обработки: молоком пишут или рисуют на бумаге. После высыхания проглаживают утюгом "
        "или держат над пламенем. Надпись темнеет из‑за пригорания белка и сахаров.\n\n"
        "Объяснение: органические вещества молока пригорают при более низкой температуре, чем бумага.",
        ["C", "H", "O", "N"],
        ["нагревание"]
    ),
]

# ======================
# PUBCHEM: по имени вещества (PUG-REST)
# ======================

RU_EN_DICT = {
    "вода": "Aqua",
    "водичка": "Aqua",

    "воздух": "Aer",

    "соль": "Natrii chloridum",
    "поваренная соль": "Natrii chloridum",
    "поваренная": "Natrii chloridum",

    "пищевая сода": "Natrii hydrogencarbonas",
    "сода": "Natrii hydrogencarbonas",
    "гидрокарбонат натрия": "Natrii hydrogencarbonas",

    "уксус": "Acidum aceticum",
    "уксусная кислота": "Acidum aceticum",

    "спирт": "Aethanolum",
    "этиловый спирт": "Aethanolum",

    "аммиак": "Ammonii hydroxidum",
    "нашатырный спирт": "Ammonii hydroxidum",

    "перекись водорода": "Hydrogenii peroxidum",

    "марганцовка": "Kalii permanganas",

    "мел": "Calcii carbonas",
    "мрамор": "Calcii carbonas",
    "известняк": "Calcii carbonas",

    "медный купорос": "Cupri sulfas",

    "серная кислота": "Acidum sulfuricum",
    "азотная кислота": "Acidum nitricum",
    "соляная кислота": "Acidum hydrochloricum",

    "глюкоза": "Glucosum",

    "кислород": "Oxygenium",
    "углекислый газ": "Dioxidum carbonicum",
}

def translate_ru_to_en(text: str) -> str:
    t = text.strip().lower()
    return RU_EN_DICT.get(t, text.strip())

def search_substance_online(query: str) -> str:
    """
    Поиск вещества в PubChem по имени через PUG‑REST.
    Поддерживает ввод на русском: сначала пробует перевести ru→en, затем ищет. [web:17][web:23][web:27]
    """
    if not query:
        return "Введите название вещества, например: вода, спирт, соль или water, ethanol, salt."

    raw_query = query.strip()

    if re.search(r"[А-Яа-яЁё]", raw_query):
        eng = translate_ru_to_en(raw_query)
        search_name = eng
        lang_note = f"Введено по‑русски: «{raw_query}». Перевод для поиска: «{eng}».\n\n"
    else:
        search_name = raw_query
        lang_note = ""

    try:
        base = "https://pubchem.ncbi.nlm.nih.gov/rest/pug"
        url = f"{base}/compound/name/{search_name}/property/MolecularFormula,MolecularWeight,CanonicalSMILES/JSON"
        r = requests.get(url, timeout=10)
        if r.status_code != 200:
            return (lang_note +
                    "Не удалось найти данные в PubChem. Попробуйте другое название (часто лучше латинское).")

        data = r.json()
        props = data["PropertyTable"]["Properties"][0]
        formula = props.get("MolecularFormula", "—")
        mw = props.get("MolecularWeight", "—")
        smiles = props.get("CanonicalSMILES", "—")

        text = (
            f"{lang_note}"
            "Найдено вещество в PubChem:\n\n"
            f"Формула: {formula}\n"
            f"Молекулярная масса: {mw}\n"
            f"SMILES: {smiles}\n\n"
            "Источник: PubChem (PUG‑REST)."
        )
        return text
    except Exception:
        return (lang_note +
                "Нет доступа к PubChem или произошла ошибка. Попробуйте позже или другое название.")

def parse_elements_from_formula(formula: str) -> set:
    """
    Выделяет коды элементов (H, He, C, Na и т.п.) из формулы, возвращает множество.
    Пример: 'C12H22O11' -> {'C', 'H', 'O'}.
    """
    if not formula:
        return set()
    tokens = re.findall(r"[A-Z][a-z]?", formula)
    return set(tokens)


def pubchem_quick_properties(name: str):
    """
    Упрощённая функция: вернуть (формула, молекулярная масса) по имени.
    Если не нашли — вернуть (None, None). [web:17][web:23][web:27]
    """
    try:
        base = "https://pubchem.ncbi.nlm.nih.gov/rest/pug"
        url = f"{base}/compound/name/{name}/property/MolecularFormula,MolecularWeight/JSON"
        r = requests.get(url, timeout=10)
        if r.status_code != 200:
            return None, None
        data = r.json()
        props = data["PropertyTable"]["Properties"][0]
        formula = props.get("MolecularFormula", None)
        mw = props.get("MolecularWeight", None)
        return formula, mw
    except Exception:
        return None, None

# ======================
# ПРИЛОЖЕНИЕ
# ======================

class AlchemistApp(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Лаборатория алхимика")
        self.configure(bg=BG_MAIN)
        self.state("zoomed")

        self.student_name = ""
        self.progress = 0

        self.tabs_completed = set()
        self.completed_experiments = set()

        self.protocol("WM_DELETE_WINDOW", self.on_app_close)

        self._build_intro_name_dialog()

    def on_app_close(self):
        if self.student_name:
            server_save_progress(self.student_name, self.progress, self.tabs_completed)
        self.destroy()

    # --------- знакомство ---------

    def _build_intro_name_dialog(self):
        dialog = tk.Toplevel(self)
        dialog.title("Добро пожаловать!")
        dialog.configure(bg=BG_MAIN)
        dialog.transient(self)
        dialog.grab_set()
        dialog.resizable(False, False)

        dialog.update_idletasks()
        w, h = 1200, 550
        sw = self.winfo_screenwidth()
        sh = self.winfo_screenheight()
        x = (sw - w) // 2
        y = (sh - h) // 2
        dialog.geometry(f"{w}x{h}+{x}+{y}")

        container = tk.Frame(dialog, bg=BG_MAIN)
        container.pack(fill="both", expand=True, padx=20, pady=20)
        container.columnconfigure(0, weight=1)
        container.columnconfigure(1, weight=0)
        container.rowconfigure(0, weight=1)
        container.rowconfigure(1, weight=0)

        img_path = os.path.join(os.path.dirname(__file__), "Mendeleev.png")
        self.mendeleev_img = tk.PhotoImage(file=img_path)
        img_label = tk.Label(container, image=self.mendeleev_img, bg=BG_MAIN)
        img_label.grid(row=0, column=1, rowspan=2, sticky="n")
        
        text = (
            "Добро пожаловать, юный исследователь химии!\n\n"
            "Меня зовут Дмитрий Иванович Менделеев. Возможно, ты уже слышал моё имя — "
            "я посвятил жизнь изучению тайн природы, стремился понять, как устроены вещества, "
            "из которых состоит наш мир, и как они взаимодействуют друг с другом.\n\n"
            "Сегодня я стану твоим проводником в удивительном мире химических реакций и превращений. "
            "Вместе мы:\n"
            "- познакомимся с удивительными свойствами элементов;\n"
            "- разгадаем секреты химических соединений;\n"
            "- увидим, как одни вещества превращаются в другие;\n"
            "- научимся понимать язык природы, записанный формулами и уравнениями.\n\n"
            "Готов начать наше путешествие? Тогда впиши своё имя в поле и нажимай кнопку "
            "«Вперёд, к знаниям!»"
        )

        lbl_text = tk.Label(
            container,
            text=text,
            justify="left",
            wraplength=520,
            fg=FG_TEXT,
            bg=BG_MAIN,
            font=("Segoe UI", 11)
        )
        lbl_text.grid(row=0, column=0, sticky="nw")

        bottom = tk.Frame(container, bg=BG_MAIN)
        bottom.grid(row=1, column=0, sticky="sew", pady=(20, 0))
        bottom.columnconfigure(0, weight=1)
        bottom.columnconfigure(1, weight=0)

        name_label = tk.Label(bottom, text="Твоё имя:", fg=FG_TEXT, bg=BG_MAIN,
                            font=("Segoe UI", 11))
        name_label.grid(row=0, column=0, sticky="w")

        entry = tk.Entry(
            bottom,
            bg=BG_PANEL,
            fg=FG_TEXT,
            insertbackground=FG_TEXT,
            relief="solid",
            highlightthickness=1,
            highlightbackground="#d1d5db",
            font=("Segoe UI", 11)
        )
        entry.grid(row=1, column=0, sticky="ew", pady=(4, 0))
        entry.focus_set()

        def start(event=None):
            name = entry.get().strip()
            if not name:
                messagebox.showwarning(
                "Имя",
                "Пожалуйста, впиши своё имя, чтобы мы могли отслеживать твой прогресс.")
                return
            self.student_name = name
            self.progress, self.tabs_completed = server_load_progress(self.student_name)
            dialog.destroy()
            self._build_main_ui()

        btn = tk.Button(
        bottom,
        text="Вперёд, к знаниям!",
        command=start,
        bg=ACCENT_ORANGE,
        fg="white",
        font=("Segoe UI", 11, "bold"),
        relief="flat",
        padx=10,
        pady=6
        )
        btn.grid(row=1, column=1, padx=(10, 0), sticky="e")

        dialog.bind("<Return>", start)

    # --------- главное окно ---------

    def _build_main_ui(self):
        top = tk.Frame(self, bg=BG_MAIN)
        top.pack(side="top", fill="x", pady=(5, 0))

        self.lbl_student = tk.Label(
            top, text=f"Ученик: {self.student_name}",
            fg=FG_TEXT, bg=BG_MAIN, font=("Segoe UI", 11, "bold")
        )
        self.lbl_student.pack(side="left", padx=10)

        self.lbl_progress = tk.Label(
            top, text=f"Прогресс: {self.progress} баллов",
            fg=FG_TEXT, bg=BG_MAIN, font=("Segoe UI", 11, "bold")
        )
        self.lbl_progress.pack(side="right", padx=10)

        self.btn_help = tk.Button(
            top, text="Help",
            command=self.open_help_window,
            fg=FG_TEXT, bg=BG_MAIN,
            relief="flat", font=("Segoe UI", 9, "bold"),
            padx=8, pady=2
        )
        self.btn_help.pack(side="right", padx=(0, 5))

        self.btn_exit = tk.Button(
            top, text="Выход",
            command=self.on_app_close,
            bg="#bd4646", fg="#f9fafb",
            relief="flat", font=("Segoe UI", 9, "bold"),
            padx=8, pady=2
        )
        self.btn_exit.pack(side="right", padx=(0, 10))

        style = ttk.Style()
        style.theme_use("clam")
        style.configure("TNotebook", background=BG_MAIN, borderwidth=0)
        style.configure("TNotebook.Tab", background=BG_PANEL, foreground=FG_MUTED)
        style.map("TNotebook.Tab", background=[("selected", ACCENT_BLUE)], foreground=[("selected", "#ffffff")]
        )


        notebook = ttk.Notebook(self)
        notebook.pack(fill="both", expand=True, padx=10, pady=10)

        self.tab1 = tk.Frame(notebook, bg=BG_MAIN)
        self.tab2 = tk.Frame(notebook, bg=BG_MAIN)
        self.tab3 = tk.Frame(notebook, bg=BG_MAIN )
        self.tab4 = tk.Frame(notebook, bg=BG_MAIN)

        notebook.add(self.tab1, text="Химия вокруг нас")
        notebook.add(self.tab2, text="Магия химических превращений")
        notebook.add(self.tab3, text="Формулы веществ")
        notebook.add(self.tab4, text="Лаборатория алхимика")

        notebook.bind("<<NotebookTabChanged>>", self.on_tab_changed)

        author_frame = tk.Frame(self, bg=BG_MAIN)
        author_frame.pack(side="bottom", fill="x", padx=10, pady=(0, 5))

        author_label = tk.Label(
            author_frame,
            text="Автор: Матвей Гайдамаков",
            fg=FG_MUTED, bg=BG_MAIN,
            font=("Segoe UI", 9, "italic")
        )
        author_label.pack(side="right")

        self._build_tab1()
        self._build_tab2()
        self._build_tab3()
        self._build_tab4()

    def open_help_window(self):
        win = tk.Toplevel(self)
        win.title("Подсказка по программе")
        win.configure(bg=BG_MAIN)

        win.update_idletasks()
        w, h = 700, 500
        sw = self.winfo_screenwidth()
        sh = self.winfo_screenheight()
        x = (sw - w) // 2
        y = (sh - h) // 2
        win.geometry(f"{w}x{h}+{x}+{y}")
        win.resizable(False, False)

        title = tk.Label(
            win,
            text="Как пользоваться программой «Лаборатория алхимика»",
            fg=FG_MUTED, bg=BG_MAIN,
            font=("Segoe UI", 11, "bold"),
            wraplength=580, justify="left"
        )
        title.pack(fill="x", pady=(10, 5), padx=10)

        text = (
            "1. Введите своё имя в стартовом окне и нажмите Enter или кнопку "
            "«Вперед к знаниям» — это нужно для сохранения вашего прогресса.\n\n"
            "2. Вкладка «Химия вокруг нас»:\n"
            "   • Читайте текст слева.\n"
            "   • Нажимайте на элементы в таблице справа, чтобы узнать про них подробнее.\n\n"
            "3. Вкладка «Магия химических превращений»:\n"
            "   • Нажимайте на кнопки типов реакций, чтобы увидеть примеры.\n"
            "   • В блоке «Как получают новые вещества?» выбирайте способы и читайте описания.\n\n"
            "4. Вкладка «Формулы веществ»:\n"
            "   • Нажимайте на кнопки с названиями веществ, чтобы увидеть формулы и применение.\n"
            "   • Внизу вводите название вещества (можно по‑русски) и нажимайте «Искать», "
            "чтобы получить информацию из интернета.\n\n"
            "5. Вкладка «Лаборатория алхимика»:\n"
            "   • Выберите опыт из списка — откроется окно с описанием и интерактивным заданием.\n"
            "   • В окне опыта щёлкайте по элементам в «шкафу» и выбирайте типы превращений, "
            "затем нажимайте «Провести опыт».\n"
            "   • Кнопка «Провести собственный эксперимент» открывает окно, где можно самому "
            "подбирать элементы и смотреть, какие вещества из них получаются.\n\n"
            "6. Ваш прогресс (баллы) сохраняется автоматически. Закрывайте программу обычной "
            "кнопкой «Выход» вверху справа."
        )

        txt = tk.Text(
            win,
            wrap="word",
            bg=BG_PANEL, fg=FG_TEXT,
            font=("Segoe UI", 10),
            relief="flat"
        )
        txt.pack(fill="both", expand=True, padx=10, pady=(0, 10))
        txt.insert("1.0", text)
        txt.config(state="disabled")

        btn_close = tk.Button(
            win, text="Закрыть",
            command=win.destroy,
            bg=BG_MAIN, fg=FG_TEXT,
            relief="flat", font=("Segoe UI", 9, "bold"),
            padx=8, pady=2
        )
        btn_close.place(relx=1.0, rely=1.0, x=-10, y=-10, anchor="se")


    def update_progress(self, delta: int):
        self.progress += delta
        if self.progress < 0:
            self.progress = 0
        self.lbl_progress.config(text=f"Прогресс: {self.progress} баллов")
        threading.Thread(
            target=server_save_progress,
            args=(self.student_name, self.progress, self.tabs_completed),
            daemon=True
        ).start()

    def show_element_info(self, symbol: str, label_widget: tk.Label):
        info = ELEMENTS_INFO.get(symbol, "Информация об этом элементе пока не добавлена.")
        label_widget.config(text=f"{symbol}: {info}")

    def on_tab_changed(self, event):
        notebook = event.widget
        idx = notebook.index("current")
        if idx not in self.tabs_completed:
            self.tabs_completed.add(idx)
            self.update_progress(5)

    # --------- вкладка 1 ---------

    def _build_tab1(self):
        text = (
            "Я приглашаю тебя в удивительное путешествие по миру химических элементов — "
            "и, конечно, мы познакомимся с моей таблицей!\n\n"
            "В 1869 году я решил разобраться, как устроены разные вещества. "
            "На тот момент науке было известно 63 химических элемента — самые простые вещества, "
            "из которых состоит всё вокруг.\n\n"
            "Я был упорен и внимателен, записал сведения о каждом элементе на отдельные карточки: "
            "название, атомный вес, свойства. Затем долго раскладывал их, словно в игре или пасьянсе, "
            "и искал закономерности. И вот что мне удалось обнаружить: \n"
            "• элементы можно выстроить по порядку — от самых лёгких к более тяжёлым;\n"
            "• свойства элементов повторяются через определённые промежутки — как дни недели или времена года.\n\n"
            "Так родилась знаменитая таблица! Она не только собрала все известные элементы, "
            "но и помогла предсказать существование новых. Я оставил в таблице пустые клеточки и описал, "
            "какими будут ещё не открытые элементы. И время подтвердило мою правоту!\n\n"
            "Позвольте поделиться с вами несколькими любопытными наблюдениями:\n"
            "• Сегодня в таблице уже 118 элементов! Из них около 94 встречаются в природе, "
            "остальные созданы учёными в лабораториях и существуют очень недолго.\n"
            "• Один из элементов назвали менделевий — представьте, в мою честь!\n"
            "• Гелий сначала нашли не на Земле, а на Солнце (его название так и переводится — "
            "«солнечный»). Только потом его обнаружили и у нас.\n\n"
            "Моя таблица — это как карта сокровищ для химиков. Она помогает учёным создавать новые материалы, "
            "лекарства и многое другое. Может быть, когда‑нибудь и ты сделаешь какое‑нибудь удивительное открытие!\n\n"
            "Хочешь узнать про какой‑то элемент подробнее? Нажимай на него в таблице — я с радостью расскажу!"
        )

        top = tk.Frame(self.tab1, bg=BG_MAIN)
        top.pack(side="top", fill="both", expand=True, padx=10, pady=10)

        top.rowconfigure(0, weight=1)
        top.columnconfigure(0, weight=2)
        top.columnconfigure(1, weight=1)

        txt = tk.Text(
            top,
            wrap="word",
            bg=BG_MAIN,
            fg=FG_TEXT,
            font=("Segoe UI", 11),
            relief="flat"
        )
        txt.insert("1.0", text)
        txt.config(state="disabled")
        txt.grid(row=0, column=0, sticky="nsew", padx=(0, 10))

        right = tk.Frame(top, bg=BG_MAIN)
        right.grid(row=0, column=1, sticky="nsew")
        right.rowconfigure(1, weight=1)
        right.columnconfigure(0, weight=1)

        lbl_title = tk.Label(
            right,
            text="Выбери элемент в таблице",
            fg=FG_TEXT,
            bg=BG_MAIN,
            font=("Segoe UI", 11, "bold"),
            anchor="w",
            justify="left"
        )
        lbl_title.grid(row=0, column=0, sticky="w", pady=(0, 10))

        tbl_frame = tk.Frame(right, bg=BG_MAIN)
        tbl_frame.grid(row=1, column=0, sticky="n")

        lbl_info = tk.Label(
            right,
            text="",
            fg=FG_TEXT,
            bg=BG_MAIN,
            font=("Segoe UI", 10, "bold"),
            justify="center",
            wraplength=400,
            anchor="center"
        )
        lbl_info.grid(row=2, column=0, sticky="n", pady=(2, 0))

        for r, row_layout in enumerate(PERIODIC_LAYOUT):
            for c, sym in enumerate(row_layout):
                if not sym:
                    continue
                bg_color = ELEMENT_COLORS.get(sym, DEFAULT_ELEMENT_BG)
                btn = tk.Button(
                    tbl_frame,
                    text=sym,
                    width=3,
                    bg=bg_color,
                    fg=FG_TEXT,
                    activebackground=ACCENT_BLUE,
                    activeforeground="#FBF2F2",
                    relief="flat",
                    font=("Segoe UI", 9, "bold"),
                    command=lambda s=sym: self.show_element_info(s, lbl_info)
                )
                btn.grid(row=r, column=c, padx=1, pady=1)

        bottom_right = tk.Frame(right, bg=BG_MAIN)
        bottom_right.grid(row=3, column=0, sticky="ew", pady=(4, 0))
        bottom_right.columnconfigure(0, weight=1)
        bottom_right.columnconfigure(1, weight=1)

        legend_frame = tk.Frame(bottom_right, bg=BG_MAIN)
        legend_frame.grid(row=0, column=0, sticky="nw")

        def add_legend_row(parent, color, text_legend):
            row = tk.Frame(parent, bg=BG_MAIN)
            row.pack(anchor="w", pady=1)
            swatch = tk.Label(row, bg=color, width=2, height=1, relief="flat")
            swatch.pack(side="left", padx=(0, 6))
            lbl = tk.Label(
                row,
                text=text_legend,
                fg=FG_TEXT,
                bg=BG_MAIN,
                font=("Segoe UI", 9),
                justify="left"
            )
            lbl.pack(side="left")

        add_legend_row(legend_frame, "#a3e3a1", "Неметаллы")
        add_legend_row(legend_frame, "#ff6b6b", "Щёлочные металлы")
        add_legend_row(legend_frame, "#ffe28a", "Щёлочноземельные металлы")
        add_legend_row(legend_frame, "#E66CDE", "Лантаноиды")
        add_legend_row(legend_frame, "#D72264", "Актиноиды")
        add_legend_row(legend_frame, "#E48F6F", "Переходные металлы")
        add_legend_row(legend_frame, "#616161", "Постпереходные металлы")
        add_legend_row(legend_frame, "#877146", "Полуметаллы")
        add_legend_row(legend_frame, "#FFFC5D", "Галогены")
        add_legend_row(legend_frame, "#a1e9ff", "Благородные газы")

        explain_frame = tk.Frame(bottom_right, bg=BG_MAIN)
        explain_frame.grid(row=0, column=1, sticky="ne", padx=(10, 0))

        text_trends = (
            "Как изменяются свойства в таблице Менделеева:\n"
            "• Слева направо атомный радиус уменьшается, а энергия ионизации и "
            "электроотрицательность растут.\n"
            "• Сверху вниз атомный радиус увеличивается, а энергия ионизации и "
            "электроотрицательность обычно уменьшаются.\n"
            "• Металлические свойства усиливаются вниз и к левому краю, "
            "неметаллические — вверх и к правому краю."
        )

        lbl_trends = tk.Label(
            explain_frame,
            text=text_trends,
            fg=FG_TEXT,
            bg=BG_MAIN,
            font=("Segoe UI", 11),
            justify="left",
            wraplength=350
        )
        lbl_trends.pack(side="top", anchor="w")

    # --------- вкладка 2 ---------
    def _build_tab2(self):
        frame = tk.Frame(self.tab2, bg=BG_MAIN)
        frame.pack(fill="both", expand=True, padx=10, pady=10)

        frame.columnconfigure(0, weight=3)
        frame.columnconfigure(1, weight=2)
        frame.rowconfigure(0, weight=1)

        left = tk.Frame(frame, bg=BG_MAIN)
        left.grid(row=0, column=0, sticky="nsew", padx=(0, 10))

        right = tk.Frame(frame, bg=BG_MAIN)
        right.grid(row=0, column=1, sticky="nsew", padx=(10, 0))

        text_intro = (
            "Позволь мне рассказать тебе несколько любопытных фактов о чудесных превращениях веществ — "
            "то есть о химических реакциях.\n\n"
            "Химическая реакция — это не магия, а процесс, в ходе которого одни вещества "
            "превращаются в другие. Атомы при этом перестраиваются, образуя новые связи, "
            "но сами по себе не исчезают и не появляются из ниоткуда.\n\n"
            "Когда горит свеча или костёр, ржавеет железо, бродит тесто или работает твой организм — "
            "везде идут химические реакции.\n\n"
            "Существует несколько основных типов таких превращений. Нажимай на кнопки ниже, "
            "чтобы узнать о каждом из них подробнее."
        )

        txt = tk.Text(
            left,
            wrap="word",
            fg=FG_TEXT,
            bg=BG_MAIN,
            font=("Segoe UI", 11),
            relief="flat",
            height=10
        )
        txt.insert("1.0", text_intro)
        txt.config(state="disabled")
        txt.pack(fill="x")

        info = tk.Label(
            left,
            text="Выбери тип реакции, чтобы увидеть примеры.",
            fg=FG_TEXT,
            bg=BG_MAIN,
            font=("Segoe UI", 11, "bold"),
            wraplength=800,
            justify="left"
        )
        info.pack(fill="x", pady=10)

        buttons_frame = tk.Frame(left, bg=BG_MAIN)
        buttons_frame.pack(fill="x", pady=5)

        def show_text(kind: str):
            if kind == "соединение":
                t = (
                    "Реакции соединения — когда из нескольких простых или сложных веществ "
                    "получается одно более сложное.\n"
                    "Пример: железо соединяется с серой, и получается сульфид железа:\n"
                    "Fe + S → FeS."
                )
            elif kind == "разложение":
                t = (
                    "Реакции разложения — одно сложное вещество распадается на несколько более простых.\n"
                    "Например, при нагревании известняка (CaCO₃) образуются негашёная известь (CaO) "
                    "и углекислый газ (CO₂):\n"
                    "CaCO₃ → CaO + CO₂."
                )
            elif kind == "замещение":
                t = (
                    "Реакции замещения — атомы простого вещества «вытесняют» атомы одного из элементов "
                    "в сложном веществе.\n"
                    "Пример: взаимодействие цинка с соляной кислотой, при котором выделяется водород:\n"
                    "Zn + 2HCl → ZnCl₂ + H₂."
                )
            else:
                t = (
                    "Реакции обмена — два сложных вещества обмениваются составными частями, "
                    "образуя два новых.\n"
                    "Например: MgO + H₂SO₄ → MgSO₄ + H₂O\n"
                    "или NaHCO₃ + CH₃COOH → CH₃COONa + CO₂ + H₂O."
                )
            info.config(text=t)

        def make_kind_button(parent, text, kind, color):
            return tk.Button(
                parent,
                text=text,
                command=lambda k=kind: show_text(k),
                fg="#ffffff",
                bg=color,
                activebackground=color,
                activeforeground="#ffffff",
                relief="flat",
                font=("Segoe UI", 11, "bold"),
                padx=14,
                pady=6
            )

        make_kind_button(buttons_frame, "Реакции соединения", "соединение", "#22c55e").pack(side="left", padx=6, pady=4)
        make_kind_button(buttons_frame, "Реакции разложения", "разложение", "#f97316").pack(side="left", padx=6, pady=4)
        make_kind_button(buttons_frame, "Реакции замещения", "замещение", "#3b82f6").pack(side="left", padx=6, pady=4)
        make_kind_button(buttons_frame, "Реакции обмена", "обмен", "#a855f7").pack(side="left", padx=6, pady=4)

        intro2_text = (
            "А как же учёные получают новые вещества? "
            "Существует несколько распространённых способов, каждый из которых помогает "
            "запускать или ускорять химические реакции.\n\n"
            "Нажимай на кнопки ниже, чтобы узнать, как работают нагревание, свет, электрический ток, "
            "катализаторы и простое смешивание веществ."
        )

        intro2 = tk.Label(
            left,
            text=intro2_text,
            fg=FG_TEXT,
            bg=BG_MAIN,
            font=("Segoe UI", 11),
            justify="left",
            wraplength=800
        )
        intro2.pack(fill="x", pady=(16, 6))

        ways_frame = tk.Frame(left, bg=BG_MAIN)
        ways_frame.pack(fill="x", pady=5)

        ways_info = tk.Label(
            left,
            text="Выбери способ, чтобы увидеть описание.",
            fg=FG_TEXT,
            bg=BG_MAIN,
            font=("Segoe UI", 10, "bold"),
            wraplength=800,
            justify="left"
        )
        ways_info.pack(fill="x", pady=(5, 0))

        def show_way(kind: str):
            if kind == "Нагревание":
                t = (
                    "Нагревание — многие реакции идут только при высокой температуре.\n"
                    "Например, чтобы получить сталь из железной руды, её плавят в огромных печах."
                )
            elif kind == "Освещение":
                t = (
                    "Освещение (облучение светом) — некоторые реакции идут только на свету.\n"
                    "Самый важный пример — фотосинтез в растениях: под действием солнечных лучей "
                    "углекислый газ и вода превращаются в глюкозу и кислород."
                )
            elif kind == "Электрический ток":
                t = (
                    "Действие электрического тока — электролиз позволяет разложить воду на кислород и водород "
                    "или получить чистые металлы из их соединений (например, алюминий из руды)."
                )
            elif kind == "Катализаторы":
                t = (
                    "Катализаторы — особые вещества, которые ускоряют реакцию, но сами в ней практически не расходуются.\n"
                    "Можно представить их как лупу, помогающую быстрее разжечь костёр: она не сгорает, "
                    "но делает процесс эффективнее."
                )
            else:
                t = (
                    "Смешивание — иногда достаточно просто соединить два вещества, и начинается «волшебство».\n"
                    "Если слить вместе два прозрачных раствора, может выпасть цветной осадок "
                    "или начаться выделение газа."
                )
            ways_info.config(text=t)

        way_colors = {
            "Нагревание": "#f97316",
            "Освещение": "#eab308",
            "Электрический ток": "#3b82f6",
            "Катализаторы": "#22c55e",
            "Смешивание": "#a855f7",
        }

        for title in ["Нагревание", "Освещение", "Электрический ток", "Катализаторы", "Смешивание"]:
            tk.Button(
                ways_frame,
                text=title,
                command=lambda t=title: show_way(t),
                fg="#ffffff",
                bg=way_colors[title],
                activebackground=way_colors[title],
                activeforeground="#ffffff",
                relief="flat",
                font=("Segoe UI", 11, "bold"),
                padx=14,
                pady=6
            ).pack(side="left", padx=6, pady=4)

        right.rowconfigure(0, weight=1)
        right.rowconfigure(1, weight=0)

        try:
            img_path = os.path.join(os.path.dirname(__file__), "Khimicheskie-reaktsii.png")
            img = Image.open(img_path)

            target_width = 500
            w, h = img.size
            scale = target_width / w
            target_height = int(h * scale)

            img = img.resize((target_width, target_height), Image.LANCZOS)
            self.chem_img = ImageTk.PhotoImage(img)

            img_label = tk.Label(right, image=self.chem_img, bg=BG_MAIN)
            img_label.grid(row=0, column=0, sticky="n", pady=(0, 10))
        except Exception as e:
            print("Ошибка загрузки Khimicheskie-reaktsii.png:", e)

        quote_text = (
            "«Помните: каждая реакция подчиняется строгим законам природы, и моя таблица поможет вам "
            "понять эти закономерности. Экспериментируйте, наблюдайте, размышляйте — и, быть может, "
            "именно вы откроете что‑то совершенно новое!\n"
            "С наилучшими пожеланиями и верой в ваш талант,\n"
            "Дмитрий Иванович Менделеев»"
        )

        quote = tk.Label(
            right,
            text=quote_text,
            fg=FG_MUTED,
            bg=BG_MAIN,
            font=("Segoe UI", 10, "italic"),
            justify="left",
            wraplength=360
        )
        quote.grid(row=1, column=0, sticky="nw")

    # --------- вкладка 3 ---------

    def _build_tab3(self):
        frame = tk.Frame(self.tab3, bg=BG_MAIN)
        frame.pack(fill="both", expand=True, padx=10, pady=10)

        text = (
            "Прежде, чем перейти к экспериментам, давай посмотрим, из каких элементов состоят "
            "самые простые вещества вокруг нас, и узнаем их химические формулы.\n\n"
            "Нажимай на кнопку с названием вещества и узнавай о нём больше."
        )

        lbl = tk.Label(
            frame,
            text=text,
            fg=FG_TEXT,
            bg=BG_MAIN,
            font=("Segoe UI", 12),
            wraplength=1000,
            justify="left"
        )
        lbl.pack(fill="x", pady=(0, 12))

        grid = tk.Frame(frame, bg=BG_MAIN)
        grid.pack(fill="x")

        def show_substance_info(title, formula, desc):
            messagebox.showinfo(title, f"{title}\nФормула: {formula}\n\n{desc}")

        cols = 3
        for i, (name, formula, desc) in enumerate(SIMPLE_SUBSTANCES):
            r = i // cols
            c = i % cols
            btn = tk.Button(
                grid,
                text=name,
                bg=ACCENT_BLUE if c == 0 else ACCENT_GREEN if c == 1 else ACCENT_ORANGE,
                fg="white",
                relief="flat",
                font=("Segoe UI", 11, "bold"),
                width=20,
                height=2,
                command=lambda n=name, f=formula, d=desc: show_substance_info(n, f, d)
            )
            btn.grid(row=r, column=c, padx=4, pady=4, sticky="nsew")

        for c in range(cols):
            grid.columnconfigure(c, weight=1)

        for r in range((len(SIMPLE_SUBSTANCES) + cols - 1) // cols):
            grid.rowconfigure(r, weight=0)

        search_frame = tk.LabelFrame(
            frame,
            text="Поиск вещества в интернете",
            fg=FG_MUTED,
            bg=BG_MAIN,
            font=("Segoe UI", 11, "bold")
        )
        search_frame.pack(fill="x", pady=18)

        lbl2 = tk.Label(
            search_frame,
            text=(
                "Теперь ты можешь ввести любое другое вещество в строку поиска "
                "и получить о нём информацию (латинское название обычно работает лучше)."
            ),
            fg=FG_TEXT,
            bg=BG_MAIN,
            font=("Segoe UI", 11),
            wraplength=1000,
            justify="left"
        )
        lbl2.pack(fill="x", pady=(6, 6))

        row = tk.Frame(search_frame, bg=BG_MAIN)
        row.pack(fill="x", pady=5)

        entry = tk.Entry(
            row,
            bg=BG_PANEL,
            fg=FG_TEXT,
            insertbackground=FG_TEXT,
            relief="solid",
            highlightthickness=1,
            highlightbackground="#d1d5db",
            font=("Segoe UI", 11)
        )
        entry.pack(side="left", fill="x", expand=True, padx=(0, 6))
        entry.insert(0, "water")

        result = tk.Text(
            search_frame,
            height=12,
            wrap="word",
            bg=BG_PANEL,
            fg=FG_TEXT,
            font=("Segoe UI", 11),
            relief="solid",
            highlightthickness=1,
            highlightbackground="#e5e7eb"
        )
        result.pack(fill="x", pady=(6, 6))
        result.config(state="disabled")

        def do_search():
            q = entry.get().strip()
            result.config(state="normal")
            result.delete("1.0", "end")
            result.insert("1.0", "Поиск...\n")
            result.config(state="disabled")

            def worker():
                text = search_substance_online(q)
                result.config(state="normal")
                result.delete("1.0", "end")
                result.insert("1.0", text)
                result.config(state="disabled")

            threading.Thread(target=worker, daemon=True).start()

        btn_search = tk.Button(
            row,
            text="Искать",
            command=do_search,
            bg=ACCENT_GREEN,
            fg="white",
            relief="flat",
            font=("Segoe UI", 11, "bold"),
            padx=10,
            pady=4
        )
        btn_search.pack(side="right")

    # --------- вкладка 4 ---------

    def _build_tab4(self):
        frame = tk.Frame(self.tab4, bg=BG_MAIN)
        frame.pack(fill="both", expand=True, padx=10, pady=10)

        text = (
            "Теперь настала пора применить полученные знания на практике. "
            "Давай проведём серию увлекательных экспериментов!\n\n"
            "Нажимай на кнопку с названием опыта, собирай нужные элементы, "
            "выбирай тип химического превращения и смотри на результат."
        )
        lbl = tk.Label(
            frame,
            text=text,
            fg=FG_TEXT,
            bg=BG_MAIN,
            font=("Segoe UI", 12),
            wraplength=1000,
            justify="left"
        )
        lbl.pack(fill="x", pady=(0, 12))

        exp_frame = tk.Frame(frame, bg=BG_MAIN)
        exp_frame.pack(fill="x", pady=5)

        self.experiment_buttons = {}

        colors = [ACCENT_BLUE, ACCENT_ORANGE, ACCENT_GREEN, "#a855f7", "#eab308"]
        cols = 3

        for idx, (name, desc, elements, kinds) in enumerate(EXPERIMENTS):
            color = colors[idx % len(colors)]
            r = idx // cols
            c = idx % cols

            btn = tk.Button(
                exp_frame,
                text=name,
                bg=color,
                fg="white",
                relief="flat",
                font=("Segoe UI", 11, "bold"),
                padx=10,
                pady=6,
                wraplength=220,
                justify="left",
                anchor="w",
                command=lambda n=name, d=desc, e=elements, k=kinds: self.open_experiment(n, d, e, k)
            )
            btn.grid(row=r, column=c, padx=6, pady=4, sticky="nsew")
            self.experiment_buttons[name] = btn

            if name in getattr(self, "completed_experiments", set()):
                btn.config(text="✔ " + name)

        for c in range(cols):
            exp_frame.columnconfigure(c, weight=1)

            if name in getattr(self, "completed_experiments", set()):
                btn.config(text="✔ " + name)

        own_btn = tk.Button(
            frame,
            text="Провести собственный эксперимент",
            bg=ACCENT_ORANGE,
            fg="white",
            relief="flat",
            font=("Segoe UI", 12, "bold"),
            padx=10,
            pady=8,
            command=self.open_own_experiment
        )
        own_btn.pack(side="bottom", pady=10)

    # --------- окно эксперимента ---------

    def open_experiment(self, name, description, good_elements, good_kinds):
        win = tk.Toplevel(self)
        win.title(name)
        win.configure(bg=BG_MAIN)
        win.state("zoomed")

        lbl = tk.Label(
            win,
            text=description,
            fg=FG_TEXT,
            bg=BG_MAIN,
            font=("Segoe UI", 12),
            wraplength=950,
            justify="left"
        )
        lbl.pack(fill="x", pady=10, padx=10)

        btn_close = tk.Button(
            win,
            text="Закрыть",
            command=win.destroy,
            bg="#991b1b",
            fg="white",
            relief="flat",
            font=("Segoe UI", 10, "bold"),
            padx=8,
            pady=2
        )
        btn_close.pack(anchor="ne", padx=10, pady=(0, 5))

        main = tk.Frame(win, bg=BG_MAIN)
        main.pack(fill="both", expand=True, padx=10, pady=10)

        main.columnconfigure(0, weight=1)
        main.columnconfigure(1, weight=1)
        main.rowconfigure(0, weight=1)
        main.rowconfigure(1, weight=1)

        box_frame = tk.LabelFrame(
            main,
            text="Выбери элементы для опыта",
            fg=FG_MUTED,
            bg=BG_MAIN,
            font=("Segoe UI", 11, "bold")
        )
        box_frame.grid(row=0, column=0, sticky="nsew", padx=(0, 5), pady=(0, 5))

        chosen_elements = []

        def add_element(sym):
            chosen_elements.append(sym)
            list_el.config(text=", ".join(chosen_elements) or "Элементы не выбраны")

        tbl = tk.Frame(box_frame, bg=BG_MAIN)
        tbl.pack(fill="both", expand=True, padx=5, pady=5)

        for r, row in enumerate(PERIODIC_LAYOUT):
            for c, sym in enumerate(row):
                if not sym:
                    continue
                bg_color = ELEMENT_COLORS.get(sym, DEFAULT_ELEMENT_BG)
                btn = tk.Button(
                    tbl,
                    text=sym,
                    bg=bg_color,
                    fg=FG_TEXT,
                    activebackground=ACCENT_BLUE,
                    activeforeground="white",
                    relief="flat",
                    width=3,
                    command=lambda s=sym: add_element(s)
                )
                btn.grid(row=r, column=c, padx=1, pady=1)

        trans_frame = tk.LabelFrame(
            main,
            text="Выбери тип(ы) химического превращения",
            fg=FG_MUTED,
            bg=BG_MAIN,
            font=("Segoe UI", 11, "bold")
        )
        trans_frame.grid(row=1, column=0, sticky="nsew", padx=(0, 5), pady=(5, 0))

        kinds_var = {}
        for kind in ["соединение", "разложение", "замещение", "обмен", "нагревание", "охлаждение", "смешивание"]:
            var = tk.BooleanVar(value=False)
            chk = tk.Checkbutton(
                trans_frame,
                text=kind.capitalize(),
                variable=var,
                fg=FG_TEXT,
                bg=BG_MAIN,
                selectcolor=BG_PANEL,
                activebackground=BG_MAIN,
                font=("Segoe UI", 11)
            )
            chk.pack(anchor="w")
            kinds_var[kind] = var

        result_frame = tk.LabelFrame(
            main,
            text="Результат опыта",
            fg=FG_MUTED,
            bg=BG_MAIN,
            font=("Segoe UI", 11, "bold")
        )
        result_frame.grid(row=0, column=1, rowspan=2, sticky="nsew", padx=(5, 0), pady=(0, 0))

        result_frame.rowconfigure(0, weight=0)
        result_frame.rowconfigure(1, weight=0)
        result_frame.rowconfigure(2, weight=1)

        lbl_result = tk.Label(
            result_frame,
            text="Собери опыт и нажми «Провести опыт».",
            fg=FG_TEXT,
            bg=BG_MAIN,
            font=("Segoe UI", 12),
            wraplength=450,
            justify="center"
        )
        lbl_result.grid(row=0, column=0, columnspan=2, sticky="ew", pady=5)

        list_el = tk.Label(
            result_frame,
            text="Элементы не выбраны",
            fg=ACCENT_BLUE,
            bg=BG_MAIN,
            font=("Segoe UI", 11)
        )
        list_el.grid(row=1, column=0, columnspan=2, sticky="ew")

        def do_experiment():
            chosen_kinds = [k for k, v in kinds_var.items() if v.get()]
            ok = True

            if not chosen_elements or not chosen_kinds:
                ok = False
            else:
                for e in good_elements:
                    if e not in chosen_elements:
                        ok = False
                        break
                if ok and not any(k in good_kinds for k in chosen_kinds):
                    ok = False

            if ok:
                lbl_result.config(text="Опыт проведён успешно! Молодец!", fg=ACCENT_GREEN)

                if name not in self.completed_experiments:
                    self.completed_experiments.add(name)
                    self.update_progress(5)

                btn_main = self.experiment_buttons.get(name)
                if btn_main is not None and not btn_main.cget("text").startswith("✔"):
                    btn_main.config(text="✔ " + name)

                server_save_progress(self.student_name, self.progress, self.tabs_completed)
                win.after(5000, win.destroy)
            else:
                lbl_result.config(
                    text="Что-то пока не сходится. Попробуй ещё раз: подбери другие элементы или типы превращений.",
                    fg=ACCENT_ORANGE
                )

        btn_run = tk.Button(
            result_frame,
            text="Провести опыт",
            command=do_experiment,
            bg=ACCENT_GREEN,
            fg="white",
            relief="flat",
            font=("Segoe UI", 12, "bold"),
            padx=10,
            pady=4
        )
        btn_run.grid(row=2, column=0, sticky="e", padx=(0, 5), pady=5)

        def clear_experiment():
            chosen_elements.clear()
            list_el.config(text="Элементы не выбраны")
            for v in kinds_var.values():
                v.set(False)
            lbl_result.config(
                text="Собери опыт и нажми «Провести опыт».",
                fg=FG_TEXT
            )

        btn_clear = tk.Button(
            result_frame,
            text="Сбросить",
            command=clear_experiment,
            bg="#1f2937",
            fg="white",
            relief="flat",
            font=("Segoe UI", 11, "bold"),
            padx=10,
            pady=4
        )
        btn_clear.grid(row=2, column=1, sticky="w", padx=(5, 0), pady=5)

    # --------- собственный эксперимент ---------

    def open_own_experiment(self):
        win = tk.Toplevel(self)
        win.title("Собственный эксперимент")
        win.configure(bg=BG_MAIN)
        win.state("zoomed")

        lbl_top = tk.Label(
            win,
            text=(
                "В этом окне ты можешь провести собственный эксперимент: "
                "выбрать элементы из «шкафа», указать типы химических превращений "
                "и посмотреть, какие вещества можно из них получить.\n\n"
                "Программе помогают данные из PubChem, поэтому лучше вводить названия "
                "веществ на латинском или английском языке."
            ),
            fg=FG_TEXT,
            bg=BG_MAIN,
            font=("Segoe UI", 12),
            wraplength=950,
            justify="left"
        )
        lbl_top.pack(fill="x", pady=10, padx=10)

        btn_close = tk.Button(
            win,
            text="Закрыть",
            command=win.destroy,
            bg="#991b1b",
            fg="white",
            relief="flat",
            font=("Segoe UI", 10, "bold"),
            padx=8,
            pady=2
        )
        btn_close.pack(anchor="ne", padx=10, pady=(0, 5))

        main = tk.Frame(win, bg=BG_MAIN)
        main.pack(fill="both", expand=True, padx=10, pady=10)

        main.columnconfigure(0, weight=1)
        main.columnconfigure(1, weight=1)
        main.rowconfigure(0, weight=1)
        main.rowconfigure(1, weight=1)

        box_frame = tk.LabelFrame(
            main,
            text="Выбери элементы для собственного опыта",
            fg=FG_MUTED,
            bg=BG_MAIN,
            font=("Segoe UI", 11, "bold")
        )
        box_frame.grid(row=0, column=0, sticky="nsew", padx=(0, 5), pady=(0, 5))

        own_elements = []

        def add_element(sym):
            own_elements.append(sym)
            lbl_el.config(text=", ".join(own_elements) or "Элементы не выбраны")

        tbl = tk.Frame(box_frame, bg=BG_MAIN)
        tbl.pack(fill="both", expand=True, padx=5, pady=5)

        for r, row in enumerate(PERIODIC_LAYOUT):
            for c, sym in enumerate(row):
                if not sym:
                    continue
                bg_color = ELEMENT_COLORS.get(sym, DEFAULT_ELEMENT_BG)
                btn = tk.Button(
                    tbl,
                    text=sym,
                    bg=bg_color,
                    fg=FG_TEXT,
                    activebackground=ACCENT_BLUE,
                    activeforeground="white",
                    relief="flat",
                    width=3,
                    command=lambda s=sym: add_element(s)
                )
                btn.grid(row=r, column=c, padx=1, pady=1)

        trans_frame = tk.LabelFrame(
            main,
            text="Выбери тип(ы) химического превращения",
            fg=FG_MUTED,
            bg=BG_MAIN,
            font=("Segoe UI", 11, "bold")
        )
        trans_frame.grid(row=1, column=0, sticky="nsew", padx=(0, 5), pady=(5, 0))

        kinds_var = {}
        for kind in ["соединение", "разложение", "замещение", "обмен",
                    "нагревание", "охлаждение", "смешивание"]:
            var = tk.BooleanVar(value=False)
            chk = tk.Checkbutton(
                trans_frame,
                text=kind.capitalize(),
                variable=var,
                fg=FG_TEXT,
                bg=BG_MAIN,
                selectcolor=BG_PANEL,
                activebackground=BG_MAIN,
                font=("Segoe UI", 11)
            )
            chk.pack(anchor="w")
            kinds_var[kind] = var

        result_frame = tk.LabelFrame(
            main,
            text="Результат собственного опыта",
            fg=FG_MUTED,
            bg=BG_MAIN,
            font=("Segoe UI", 11, "bold")
        )
        result_frame.grid(row=0, column=1, rowspan=2, sticky="nsew", padx=(5, 0), pady=(0, 0))

        result_frame.rowconfigure(0, weight=0)
        result_frame.rowconfigure(1, weight=1)
        result_frame.rowconfigure(2, weight=0)

        lbl_el = tk.Label(
            result_frame,
            text="Элементы не выбраны",
            fg=ACCENT_BLUE,
            bg=BG_MAIN,
            font=("Segoe UI", 11)
        )
        lbl_el.grid(row=0, column=0, columnspan=2, sticky="ew", pady=(5, 5))

        txt_result = tk.Text(
            result_frame,
            height=12,
            wrap="word",
            bg=BG_PANEL,
            fg=FG_TEXT,
            font=("Segoe UI", 11),
            relief="solid",
            highlightthickness=1,
            highlightbackground="#e5e7eb"
        )
        txt_result.grid(row=1, column=0, columnspan=2, sticky="nsew", padx=5, pady=(0, 5))
        txt_result.insert(
            "1.0",
            "Здесь появится описание возможных веществ и подсказки по данным PubChem.\n\n"
            "Выбери элементы и типы превращений, затем нажми «Запустить эксперимент»."
        )
        txt_result.config(state="disabled")

        def run_own_experiment():
            chosen_set = set(own_elements)
            chosen_kinds = [k for k, v in kinds_var.items() if v.get()]

            if not chosen_set:
                message = "Элементы не выбраны. Добавь хотя бы один элемент из таблицы."
            else:
                header = f"Вы выбрали элементы: {', '.join(sorted(chosen_set))}.\n"
                if chosen_kinds:
                    header += f"Типы превращений: {', '.join(chosen_kinds)}.\n\n"
                else:
                    header += "Типы превращений не выбраны.\n\n"

                suitable = []
                for name, formula_raw, desc in COMPOUND_BANK:
                    clean_formula = re.sub(r"[^A-Za-z]", "", formula_raw)
                    els = parse_elements_from_formula(clean_formula)
                    if els and els.issubset(chosen_set):
                        suitable.append((name, formula_raw, desc))

                if not suitable:
                    message = header + (
                        "Пока по выбранным элементам трудно предложить конкретные вещества.\n"
                        "Попробуй добавить другие элементы или изменить их набор."
                    )
                else:
                    parts = [header]
                    for name, formula_raw, desc in suitable:
                        formula_pubchem, mw = pubchem_quick_properties(name)
                        if formula_pubchem is None:
                            formula_pubchem = formula_raw
                        if mw is None:
                            line = f"{name} ({formula_pubchem})\n{desc}\n"
                        else:
                            line = (
                                f"{name} ({formula_pubchem})\n"
                                f"Молекулярная масса: {mw}\n{desc}\n"
                            )
                        parts.append(line)
                    message = "\n".join(parts)

            txt_result.config(state="normal")
            txt_result.delete("1.0", "end")
            txt_result.insert("1.0", message)
            txt_result.config(state="disabled")

        btn_run = tk.Button(
            result_frame,
            text="Запустить эксперимент",
            command=run_own_experiment,
            bg=ACCENT_GREEN,
            fg="white",
            relief="flat",
            font=("Segoe UI", 12, "bold"),
            padx=10,
            pady=4
        )
        btn_run.grid(row=2, column=0, sticky="e", padx=(0, 5), pady=5)

        def clear_own():
            own_elements.clear()
            lbl_el.config(text="Элементы не выбраны")
            for v in kinds_var.values():
                v.set(False)
            txt_result.config(state="normal")
            txt_result.delete("1.0", "end")
            txt_result.insert(
                "1.0",
                "Здесь появится описание возможных веществ и подсказки по данным PubChem.\n\n"
                "Выбери элементы и типы превращений, затем нажми «Запустить эксперимент»."
            )
            txt_result.config(state="disabled")

        btn_clear = tk.Button(
            result_frame,
            text="Сбросить",
            command=clear_own,
            bg="#1f2937",
            fg="white",
            relief="flat",
            font=("Segoe UI", 11, "bold"),
            padx=10,
            pady=4
        )
        btn_clear.grid(row=2, column=1, sticky="w", padx=(5, 0), pady=5)

if __name__ == "__main__":
    app = AlchemistApp()
    app.mainloop()