У меня вот так:
import streamlit as st
import pandas as pd
from datetime import datetime
import io
import numpy as np
import matplotlib.pyplot as plt
import plotly.graph_objects as go
# from fpdf import FPDF
# from fpdf import FPDF as FPDF2
# import base64
from io import BytesIO
from matplotlib.backends.backend_pdf import PdfPages
# Убедитесь, что xlsxwriter установлен (pip install xlsxwriter)
# import xlsxwriter

st.set_page_config(page_title="Ретроспектива СКН", layout="wide")

# Пример данных (Ваши данные остаются без изменений)
data = {
    'Цех': ['Цех 1', 'Цех 1', 'Цех 2', 'Цех 2', 'Цех 2', 'Цех 1', 'Цех 1', 'Цех 2', 'Цех 2', 'Цех 2'],
    'Агрегат': ['Агрегат 1', 'Агрегат 2', 'Агрегат 1', 'Агрегат 2', 'Агрегат 2', 'Агрегат 1', 'Агрегат 2', 'Агрегат 1', 'Агрегат 2', 'Агрегат 2'],
    'Параметр': ['Параметр 1', 'Параметр 2', 'Параметр 1', 'Параметр 3', 'Параметр 3', 'Параметр 1', 'Параметр 2', 'Параметр 1', 'Параметр 3', 'Параметр 3'],
    'Правило': ['Правило 1', 'Правило 2', 'Правило 3', 'Правило 4', 'Правило 5', 'Правило 4', 'Правило 3', 'Правило 2', 'Правило 4', 'Правило 4'],
    'Дефект': ['Дефект 1', 'Дефект 2', 'Дефект 3', 'Дефект 4', 'Дефект 5', 'Дефект 3', 'Дефект 3', 'Дефект 3', 'Дефект 3', 'Дефект 3'],
    'СКН': ['СКН 1', 'СКН 2', 'СКН 3', 'СКН 4', 'СКН 5', 'СКН 2', 'СКН 4', 'СКН 5', 'СКН 2', 'СКН 4'],
    'Нижняя граница': [10, 20, 15, 5, 25, 15, 30, 5, 10, 12],
    'Верхняя граница': [100, 200, 150, 50, 250, 100, 200, 150, 50, 250],
    'Дата ввода': [datetime(2023, 1, 1), datetime(2023, 2, 1), datetime(2023, 3, 1), datetime(2023, 4, 1), datetime(2023, 2, 1),
                   datetime(2023, 1, 1), datetime(2023, 2, 1), datetime(2023, 3, 1), datetime(2023, 4, 1), datetime(2023, 2, 1)],
    'Состояние': [True, False, False, True, True, True, True, False, False, False],
    'Марка': ["08пс", "Ст3пс", "Ст2пс", "08пс", "09Г2С", "20", "Ст1пс", "А36", "08пс", "08пс"],
    'Вид продукции': ["Сляб литой", "Рулон г/к", "Обрезь", "Лист г/к", "Рулон г/к", "Рулон-подкат г/к", "Рулон г/к", "Сляб литой", "Лист г/к", "Обрезь"]
}

df = pd.DataFrame(data)
DEFAULT_THICKNESS = 20.0 # Значение по умолчанию для толщины

# --- Ваши функции (multiselect_with_select_all, excel_output, filter_initial_data) остаются без изменений ---
# (Я скрыл их для краткости, но они должны быть в вашем коде)
def multiselect_with_select_all(container, title, options, key):
    """Helper function for multiselect with a 'Select All' checkbox."""
    c1, c2 = container.columns([10, 3])
    c2.markdown("")
    c2.markdown("")
    select_box_container = c1.empty()
    checkbox = c2.checkbox("Выбрать все", key=key)
    multiselect_key = f"{key}_multiselect"

    if checkbox:
        results = select_box_container.multiselect(title + " (Все выбраны)", options, options, disabled=True, key=multiselect_key)
    else:
        default_selection = st.session_state.get(f"{multiselect_key}_default", [])
        if not default_selection and st.session_state.get(multiselect_key) is not None:
             default_selection = st.session_state[multiselect_key]
        results = select_box_container.multiselect(title, options, default=default_selection, key=multiselect_key)
        st.session_state[f"{multiselect_key}_default"] = results
    st.session_state[f"{key}_selected_values"] = results
    return results

def excel_output(df_to_export):
    """Converts a DataFrame to an Excel file in bytes."""
    output = io.BytesIO()
    with pd.ExcelWriter(output, engine='xlsxwriter') as writer:
        df_to_export.to_excel(writer, index=False, sheet_name='СКН')
    output.seek(0)
    return output

@st.cache_data(ttl=600)
def filter_initial_data(data_df, shops, aggregates, defects, parameters, skns):
    filtered_df = data_df.copy()
    if shops: filtered_df = filtered_df[filtered_df['Цех'].isin(shops)]
    if aggregates: filtered_df = filtered_df[filtered_df['Агрегат'].isin(aggregates)]
    if defects: filtered_df = filtered_df[filtered_df['Дефект'].isin(defects)]
    if parameters: filtered_df = filtered_df[filtered_df['Параметр'].isin(parameters)]
    if skns: filtered_df = filtered_df[filtered_df['СКН'].isin(skns)]
    return filtered_df

# --- Инициализация Session State ---
if 'excluded_skn' not in st.session_state:
    st.session_state.excluded_skn = pd.DataFrame(columns=df.columns)
if 'records' not in st.session_state:
    st.session_state.records = pd.DataFrame(columns=["Правило", "Параметр", "Марка", "Толщина", "Вид продукции", "Нижняя граница", "Верхняя граница", "Дефект"])
if 'current_index' not in st.session_state:
    st.session_state.current_index = 1
if 'down_lim' not in st.session_state:
    st.session_state.down_lim = {}
if 'up_lim' not in st.session_state:
    st.session_state.up_lim = {}
if 'edit_index' not in st.session_state:
    st.session_state.edit_index = None
# Добавляем состояние для отслеживания ошибок в форме добавления
if 'form_add_error' not in st.session_state:
    st.session_state.form_add_error = None

# ==================================================================
# --- Функция Callback для добавления и очистки (НОВАЯ) ---
# ==================================================================
def add_and_clear_form():
    """
    Эта функция вызывается ПЕРЕД перезапуском скрипта при нажатии кнопки 'Добавить'.
    Она считывает значения из session_state, обрабатывает их и очищает ключи.
    """
    # 1. Считываем текущие значения виджетов из session_state по их ключам
    param = st.session_state.new_param
    marka = st.session_state.new_marka
    defect = st.session_state.new_defect
    prod = st.session_state.new_prod
    thickness = st.session_state.new_thickness
    down_lim = st.session_state.down_lim
    up_lim = st.session_state.up_lim

    # 2. Валидация
    if not (param and marka and defect and prod):
        st.session_state.form_add_error = "Пожалуйста, заполните все поля (Параметр, Марка, Дефект, Вид продукции)."
        return # Выходим, если есть ошибка

    # 3. Обработка данных (Добавление записей)
    records_to_add = []
    for p in param:
        # Убедимся, что границы для параметра существуют (на случай редких сбоев синхронизации)
        if p in down_lim and p in up_lim:
            new_record = {
                "Правило": f"Правило {st.session_state.current_index}",
                "Параметр": p,
                "Марка": "; ".join(marka),
                "Толщина": thickness,
                "Вид продукции": "; ".join(prod),
                "Нижняя граница": down_lim[p],
                "Верхняя граница": up_lim[p],
                "Дефект": "; ".join(defect)
            }
            records_to_add.append(new_record)

    if records_to_add:
        st.session_state.records = pd.concat([st.session_state.records, pd.DataFrame(records_to_add)], ignore_index=True)
        st.session_state.current_index += 1

        # 4. Очистка формы (Сброс ключей в session_state)
        # Так как это callback, мы можем безопасно изменять эти значения
        st.session_state.new_param = []
        st.session_state.new_marka = []
        st.session_state.new_defect = []
        st.session_state.new_prod = []
        st.session_state.new_thickness = DEFAULT_THICKNESS
        st.session_state.down_lim = {}
        st.session_state.up_lim = {}
        st.session_state.form_add_error = None # Сбрасываем ошибку
    else:
         # Это может произойти, если параметры были выбраны, но слайдеры не успели записать границы
        st.session_state.form_add_error = "Не удалось добавить записи. Проверьте границы параметров."

def save_records_to_pdf(df):
    """Альтернативный вариант с использованием matplotlib для создания PDF"""
    from matplotlib.backends.backend_pdf import PdfPages
    import matplotlib.pyplot as plt

    fig, ax = plt.subplots(figsize=(12, 4))
    ax.axis('tight')
    ax.axis('off')

    # Создаем таблицу
    table_data = [list(df.columns)] + df.values.tolist()
    table = ax.table(cellText=table_data, colLabels=None, cellLoc='center', loc='center')

    # Настройка стиля таблицы
    table.auto_set_font_size(False)
    table.set_fontsize(10)
    table.scale(1, 1.5)

    # Сохраняем в PDF
    pdf_buffer = BytesIO()
    with PdfPages(pdf_buffer) as pdf:
        pdf.savefig(fig, bbox_inches='tight')
    plt.close(fig)

    pdf_buffer.seek(0)
    return pdf_buffer

def save_plots_to_pdf():
    """Сохраняет графики из раздела моделирования в PDF"""
    # Создаем PDF файл
    pdf_buffer = BytesIO()

    with PdfPages(pdf_buffer) as pdf:
        # Создаем фигуры для каждого графика
        fig, axs = plt.subplots(3, 2, figsize=(15, 18))
        fig.suptitle("Результаты моделирования", fontsize=16)

        # Графики "БЫЛО"
        axs[0, 0].bar(months, df_bilo['перебраковка'], color='#4A91FF')
        axs[0, 0].set_title('Перебраковка (БЫЛО)')
        axs[0, 0].tick_params(axis='x', rotation=45)

        axs[1, 0].bar(months, df_bilo['недобраковка'], color='#AEFF7D')
        axs[1, 0].set_title('Недобраковка (БЫЛО)')
        axs[1, 0].tick_params(axis='x', rotation=45)

        axs[2, 0].bar(months, df_bilo['нарушения'], color='#F4D877')
        axs[2, 0].set_title('Количество нарушений (БЫЛО)')
        axs[2, 0].tick_params(axis='x', rotation=45)

        # Графики "СТАЛО"
        axs[0, 1].bar(months, df_stalo['перебраковка'], color='#4A5AFF')
        axs[0, 1].set_title('Перебраковка (СТАЛО)')
        axs[0, 1].tick_params(axis='x', rotation=45)

        axs[1, 1].bar(months, df_stalo['недобраковка'], color='#4AFF7D')
        axs[1, 1].set_title('Недобраковка (СТАЛО)')
        axs[1, 1].tick_params(axis='x', rotation=45)

        axs[2, 1].bar(months, df_stalo['нарушения'], color='#FFD84A')
        axs[2, 1].set_title('Количество нарушений (СТАЛО)')
        axs[2, 1].tick_params(axis='x', rotation=45)

        plt.tight_layout()
        pdf.savefig(fig)
        plt.close(fig)

    pdf_buffer.seek(0)
    return pdf_buffer

# ==================================================================
# --- Конец Callback ---
# ==================================================================


# --- Логика фильтрации и отображения существующих СКН (Ваш код без изменений) ---
# (Скрыто для краткости)
st.markdown("<h1 style='text-align: center;'>Добрый день!</h1>", unsafe_allow_html=True)
c1, c2, c3 = st.columns(3)
selected_shop = multiselect_with_select_all(c1, "Выберите цех", df['Цех'].unique(), key="end_departures")
selected_aggregate = multiselect_with_select_all(c2, "Выберите агрегат", df['Агрегат'].unique(), key="end_units")
expander_filters = st.expander("Дополнительные фильтры", expanded=True)
C1, C2, C3 = expander_filters.columns(3)
selected_defect = multiselect_with_select_all(C1, "Дефект", df["Дефект"].unique(), key="end_defects")
selected_parametr = multiselect_with_select_all(C2, "Параметр", df["Параметр"].unique(), key="end_params")
selected_skn_filter = multiselect_with_select_all(C3, "СКН (фильтр)", df["СКН"].unique(), key="end_rules")

current_selected_shops = st.session_state.get("end_departures_selected_values", [])
current_selected_aggregates = st.session_state.get("end_units_selected_values", [])
current_selected_defects = st.session_state.get("end_defects_selected_values", [])
current_selected_parameters = st.session_state.get("end_params_selected_values", [])
current_selected_skns_filter = st.session_state.get("end_rules_selected_values", [])

current_filtered_pool = filter_initial_data(df, current_selected_shops, current_selected_aggregates, current_selected_defects, current_selected_parameters, current_selected_skns_filter)

active_indices = current_filtered_pool.index.difference(st.session_state.excluded_skn.index)
excluded_indices_in_pool = current_filtered_pool.index.intersection(st.session_state.excluded_skn.index)

currently_active_df = current_filtered_pool.loc[active_indices].copy()
display_excluded_df = current_filtered_pool.loc[excluded_indices_in_pool].copy()

st.markdown("---")
expander_active = st.expander("Информация", expanded=True)
expander_active.markdown("<h3 style='text-align: center;'>Действующие СКН</h3>", unsafe_allow_html=True)

if currently_active_df.empty:
    expander_active.info('Нет действующих СКН, удовлетворяющих выбранным данным.')
    active_skn_options = {}
else:
    active_skn_options = {f"{row['СКН']} (Цех: {row['Цех']}, Агрегат: {row['Агрегат']})": idx
                          for idx, row in currently_active_df.iterrows()}
    for original_idx, row in currently_active_df.iterrows():
        col1, col2 = expander_active.columns([9, 1])
        with col1:
            st.dataframe(row.to_frame().T, hide_index=True, use_container_width=True)
        with col2:
            if st.button("🔴 Отключить", key=f"disable_{original_idx}"):
                st.session_state.excluded_skn = pd.concat([st.session_state.excluded_skn, row.to_frame().T])
                st.rerun()
    if expander_active.download_button(label="Выгрузить действующие СКН в MS Excel", data=excel_output(currently_active_df), file_name="Ретроспектива СКН. Действующие.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"):
        expander_active.success("Информация о действующих СКН выгружена")

expander_active.markdown("<h3 style='text-align: center;'>Отключенные СКН</h3>", unsafe_allow_html=True)

# (Логика отключения/включения СКН также остается без изменений)
if active_skn_options != {}:
    selected_skn_to_disable = expander_active.selectbox(
        "Выберите СКН для отключения",
        options=[""] + list(active_skn_options.keys()),
        index=0,
        key="disable_skn_selectbox"
    )
    if selected_skn_to_disable:
        selected_idx = active_skn_options[selected_skn_to_disable]
        selected_row = currently_active_df.loc[selected_idx]
        st.session_state.excluded_skn = pd.concat([st.session_state.excluded_skn, selected_row.to_frame().T])
        st.rerun()

for original_idx, row in display_excluded_df.iterrows():
    col1, col2 = expander_active.columns([9, 1])
    with col1:
        st.dataframe(
            row.to_frame().T.style.applymap(lambda x: 'background-color: #ffcccc', subset=pd.IndexSlice[:, :]),
            hide_index=True,
            use_container_width=True
        )
    with col2:
        if st.button("🟢 Включить", key=f"enable_{original_idx}"):
            st.session_state.excluded_skn = st.session_state.excluded_skn.drop(original_idx)
            st.rerun()

if not display_excluded_df.empty:
    if expander_active.download_button(label="Выгрузить отключенные СКН в MS Excel", data=excel_output(display_excluded_df), file_name="Ретроспектива СКН. Отключенные.xlsx", mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"):
        expander_active.success("Информация об отключенных СКН выгружена")


# ==================================================================
# --- Раздел Добавления (ИЗМЕНЕННЫЙ) ---
# ==================================================================

expander2 = st.expander("Добавление", expanded=True)
expander2.markdown("<h3 style='text-align: center;'>Правила нового СКН</h3>", unsafe_allow_html=True)

# --- Отображение ошибок (если они были установлены в callback) ---
if st.session_state.form_add_error:
    expander2.error(st.session_state.form_add_error)

# --- Форма добавления (без st.form) ---
c1, c2, c3, c4, c5 = expander2.columns(5)

# Виджеты создаются здесь. Они автоматически считывают значения из session_state по ключу.
# Если callback очистил ключи, виджеты будут пустыми.
param = c1.multiselect("Параметр", df["Параметр"].unique(), key="new_param")
marka = c2.multiselect("Марка", df["Марка"].unique(), key="new_marka")
defect = c3.multiselect("Дефект", df["Дефект"].unique(), key="new_defect")
prod = c4.multiselect("Вид продукции", df["Вид продукции"].unique(), key="new_prod")
thickness = c5.slider("Толщина, мм", 0., 700., key="new_thickness")

# Логика отображения слайдеров (остается как было, для динамического обновления)
# Очистка старых значений, если параметр был убран из multiselect
for p in list(st.session_state.down_lim.keys()):
    if p not in param:
        st.session_state.down_lim.pop(p)
        st.session_state.up_lim.pop(p)
        # Также удаляем ключи виджетов слайдеров
        if f"down_{p}" in st.session_state:
            del st.session_state[f"down_{p}"]
        if f"up_{p}" in st.session_state:
            del st.session_state[f"up_{p}"]


# Для каждого выбранного параметра показываем слайдеры
if param:
    expander2.markdown("---")
    expander2.markdown("#### Границы выбранных параметров:")

for p in param:
    expander2.write(f"<u>Настройки для **{p}**:</u>", unsafe_allow_html=True)  # f"<u>Настройки для {p}:</u>", unsafe_allow_html=True
    c11, c12 = expander2.columns(2)

    # Инициализация значений по умолчанию, если они еще не установлены
    if p not in st.session_state.down_lim:
        st.session_state.down_lim[p] = 20
    if p not in st.session_state.up_lim:
        st.session_state.up_lim[p] = 100

    # Отображение слайдеров и обновление session_state при их изменении
    st.session_state.down_lim[p] = c11.slider(
        f"Нижняя граница для {p}", 0, 300, st.session_state.down_lim[p], key=f"down_{p}"
    )
    st.session_state.up_lim[p] = c12.slider(
        f"Верхняя граница для {p}", 0, 900, st.session_state.up_lim[p], key=f"up_{p}"
    )

# Кнопка добавления (ИЗМЕНЕНО: используется on_click)
# Мы больше не используем if st.button(...), так как вся логика теперь в callback.
expander2.button("✅ Добавить", on_click=add_and_clear_form)


# ==================================================================
# --- Конец измененного раздела ---
# ==================================================================


# --- Отображение и редактирование (Ваш код остается без изменений) ---
if not st.session_state.records.empty:
    expander2.markdown("---")
    expander2.markdown("<h3 style='text-align: center;'>Добавленные записи</h3>", unsafe_allow_html=True)
    for idx, row in st.session_state.records.iterrows():
        col1, col2, col3 = expander2.columns([8, 1, 1])
        col1.dataframe(row.to_frame().T, use_container_width=True)
        if col2.button("✏️ Редактировать", key=f"edit_{idx}"):
            st.session_state.edit_index = idx
            st.session_state.edit_data = row.to_dict()
            st.rerun() # Добавил rerun, чтобы форма редактирования появилась сразу
        if col3.button("❌ Удалить", key=f"delete_{idx}"):
            st.session_state.records = st.session_state.records.drop(idx).reset_index(drop=True)
            st.session_state.edit_index = None
            st.rerun()

    # --- Форма редактирования ---
    if st.session_state.edit_index is not None:
        edit_row = st.session_state.edit_data
        with expander2.form(f"edit_form_{st.session_state.edit_index}"):
            st.markdown(f"**Редактирование правила: {edit_row['Правило']}**")
            c1, c2, c3 = st.columns(3)
            try:
                param_index = list(df["Параметр"].unique()).index(edit_row["Параметр"])
            except ValueError:
                param_index = 0

            new_param = c1.selectbox("Параметр", df["Параметр"].unique(), index=param_index)
            new_marka = c1.multiselect("Марка", df["Марка"].unique(), default=edit_row["Марка"].split("; "))
            new_thickness = c2.number_input("Толщина", value=float(edit_row["Толщина"]), min_value=0.0)
            new_prod = c2.multiselect("Вид продукции", df["Вид продукции"].unique(), default=edit_row["Вид продукции"].split("; "))
            new_down_lim = c3.number_input("Нижняя граница", value=int(edit_row["Нижняя граница"]), min_value=0)
            new_up_lim = c3.number_input("Верхняя граница", value=int(edit_row["Верхняя граница"]), min_value=0)
            new_defect = c3.multiselect("Дефект", df["Дефект"].unique(), default=edit_row["Дефект"].split("; "))

            b1, b2, b3 = st.columns([1,1,4])
            save_btn = b1.form_submit_button("💾 Сохранить изменения")
            cancel_btn = b2.form_submit_button("❌ Отмена")

            if save_btn:
                st.session_state.records.at[st.session_state.edit_index, "Параметр"] = new_param
                # ... (остальные поля обновления) ...
                st.session_state.records.at[st.session_state.edit_index, "Марка"] = "; ".join(new_marka)
                st.session_state.records.at[st.session_state.edit_index, "Толщина"] = new_thickness
                st.session_state.records.at[st.session_state.edit_index, "Вид продукции"] = "; ".join(new_prod)
                st.session_state.records.at[st.session_state.edit_index, "Нижняя граница"] = new_down_lim
                st.session_state.records.at[st.session_state.edit_index, "Верхняя граница"] = new_up_lim
                st.session_state.records.at[st.session_state.edit_index, "Дефект"] = "; ".join(new_defect)
                st.session_state.edit_index = None
                st.rerun()
            if cancel_btn:
                st.session_state.edit_index = None
                st.rerun()


months = pd.date_range('2023-01-01', periods=12, freq='M').strftime('%b %Y')
np.random.seed(42)
df_bilo = pd.DataFrame({
    'перебраковка': np.random.randint(10, 50, size=12),
    'недобраковка': np.random.randint(5, 30, size=12),
    'нарушения': np.random.randint(20, 60, size=12)
}, index=months)
df_stalo = pd.DataFrame({
    'перебраковка': np.random.randint(5, 40, size=12),
    'недобраковка': np.random.randint(2, 20, size=12),
    'нарушения': np.random.randint(10, 40, size=12)
}, index=months)

if "show_modeling" not in st.session_state:
    st.session_state.show_modeling = False

if st.button("🚀 **Запустить моделирование**", use_container_width=True):
    st.session_state.show_modeling = True

if st.session_state.show_modeling:
    with st.expander("Результаты моделирования", expanded=True):
        col1, col2, col3 = st.columns([3, 3, 1])

        # Цвета для графиков
        color_bilo = "#4A91FF"      # синий
        color_stalo = "#4A5AFF"     # зеленый
        color_bilo2 = "#AEFF7D"     # оранжевый
        color_stalo2 = "#4AFF7D"    # красный
        color_bilo3 = "#F4D877"     # фиолетовый
        color_stalo3 = "#FFD84A"    # коричневый

        # Первый столбец — БЫЛО
        with col1:
            st.markdown("### БЫЛО")
            st.markdown(f"<u>**Перебраковка**</u>", unsafe_allow_html=True)
            fig1 = go.Figure(go.Bar(x=months, y=df_bilo['перебраковка'], marker_color=color_bilo))
            fig1.update_layout(height=200, margin=dict(l=10, r=10, t=30, b=10))
            st.plotly_chart(fig1, use_container_width=True)
            st.write(f"<div style='text-align: right;'>Среднее за год: <strong>{round(df_bilo['перебраковка'].mean(), 2)}</strong></div>", unsafe_allow_html=True)

            st.markdown(f"<u>**Недобраковка**</u>", unsafe_allow_html=True)
            fig2 = go.Figure(go.Bar(x=months, y=df_bilo['недобраковка'], marker_color=color_bilo2))
            fig2.update_layout(height=200, margin=dict(l=10, r=10, t=30, b=10))
            st.plotly_chart(fig2, use_container_width=True)
            st.write(
                f"<div style='text-align: right;'>Среднее за год: <strong>{round(df_bilo['недобраковка'].mean(), 2)}</strong></div>",
                unsafe_allow_html=True)

            st.markdown(f"<u>**Количество нарушений**</u>", unsafe_allow_html=True)
            fig3 = go.Figure(go.Bar(x=months, y=df_bilo['нарушения'], marker_color=color_bilo3))
            fig3.update_layout(height=200, margin=dict(l=10, r=10, t=30, b=10))
            st.plotly_chart(fig3, use_container_width=True)
            st.write(
                f"<div style='text-align: right;'>Среднее за год: <strong>{round(df_bilo['нарушения'].mean(), 2)}</strong></div>",
                unsafe_allow_html=True)

        # Второй столбец — СТАЛО
        with col2:
            st.markdown("### СТАЛО")
            st.markdown(f"<u>**Перебраковка**</u>", unsafe_allow_html=True)
            fig4 = go.Figure(go.Bar(x=months, y=df_stalo['перебраковка'], marker_color=color_stalo))
            fig4.update_layout(height=200, margin=dict(l=10, r=10, t=30, b=10))
            st.plotly_chart(fig4, use_container_width=True)
            st.write(
                f"<div style='text-align: right;'>Среднее за год: <strong>{round(df_stalo['перебраковка'].mean(), 2)}</strong></div>",
                unsafe_allow_html=True)

            st.markdown(f"<u>**Недобраковка**</u>", unsafe_allow_html=True)
            fig5 = go.Figure(go.Bar(x=months, y=df_stalo['недобраковка'], marker_color=color_stalo2))
            fig5.update_layout(height=200, margin=dict(l=10, r=10, t=30, b=10))
            st.plotly_chart(fig5, use_container_width=True)
            st.write(
                f"<div style='text-align: right;'>Среднее за год: <strong>{round(df_stalo['недобраковка'].mean(), 2)}</strong></div>",
                unsafe_allow_html=True)

            st.markdown(f"<u>**Количество нарушений**</u>", unsafe_allow_html=True)
            fig6 = go.Figure(go.Bar(x=months, y=df_stalo['нарушения'], marker_color=color_stalo3))
            fig6.update_layout(height=200, margin=dict(l=10, r=10, t=30, b=10))
            st.plotly_chart(fig6, use_container_width=True)
            st.write(
                f"<div style='text-align: right;'>Среднее за год: <strong>{round(df_stalo['нарушения'].mean(), 2)}</strong></div>",
                unsafe_allow_html=True)

        # Третий столбец — фильтры и кнопки
        with col3:
            st.markdown("### Фильтры")
            departure_otchet = st.multiselect("Цех", df["Цех"].unique())
            unit_otchet = st.multiselect("Агрегат", df["Агрегат"].unique())
            defect_otchet = st.multiselect("Дефект", df["Дефект"].unique())
            skn_otchet = st.multiselect("СКН", df["СКН"].unique())
            st.markdown("### Действия")

            # Кнопка для сохранения правил в PDF
            if st.button("📑 Сохранить правила в PDF"):
                if not st.session_state.records.empty:
                    pdf_bytes = save_records_to_pdf(st.session_state.records)
                    st.download_button(
                        label="Скачать PDF",
                        data=pdf_bytes,
                        file_name="Правила_СКН.pdf",
                        mime="application/pdf"
                    )
                else:
                    st.warning("Нет данных для сохранения в PDF")

            # Кнопка для сохранения отчета в PDF
            if st.button("📊 Сохранить отчет в PDF"):
                pdf_bytes = save_plots_to_pdf()
                st.download_button(
                    label="Скачать PDF",
                    data=pdf_bytes,
                    file_name="Отчет_моделирование.pdf",
                    mime="application/pdf"
                )

            # Оставляем остальные кнопки без изменений
            if st.button("💾 Сохранить детализацию по УЕ в Excel"):
                # Ваша существующая логика для Excel
                pass
В раздел Правила нового СКН требуется внести изменения, поделить не на 5, а на 6 столбцов изначально добавить параметр Ширина, аналогично параметру Толщина, соответственно изменить все связанные блоки