<?php

/**
 * @package QuickStockForWooCommerce
 * @license GPL-2.0-or-later (https://www.gnu.org/licenses/gpl-2.0.html)
 */
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
class QSFW_Admin_Page {
    public static function render() {
        $mode = QSFW_Columns::get_mode();
        // Einziger Aufrufpunkt: „zuletzt besuchter Tab" wird genau einmal pro
        // echtem Seitenaufruf gemerkt, nicht mehr beiläufig in jedem get_mode().
        QSFW_Columns::remember_mode( $mode );
        $tab_reset = array(
            'paged'                => false,
            'stock_sort'           => false,
            'hb_q'                 => false,
            'cat_filter'           => false,
            'stock_start_date'     => false,
            'apply_preset'         => false,
            'save_preset_name'     => false,
            'set_default_preset'   => false,
            'clear_default_preset' => false,
            'preset_delete'        => false,
            'settings_subtab'      => false,
            'edit_preset'          => false,
            'log_q'                => false,
            'log_from'             => false,
            'log_to'               => false,
            'log_source'           => false,
            'log_paged'            => false,
            'po'                   => false,
            'po_status'            => false,
        );
        $url_reorder = add_query_arg( array(
            'mode' => 'reorder',
        ) + $tab_reset );
        $url_all = add_query_arg( array(
            'mode' => 'all',
        ) + $tab_reset );
        $url_po = add_query_arg( array(
            'mode' => 'po',
        ) + $tab_reset );
        $url_verlauf = add_query_arg( array(
            'mode' => 'verlauf',
        ) + $tab_reset );
        $url_settings = add_query_arg( array(
            'mode' => 'settings',
        ) + $tab_reset );
        echo '<div class="wrap qsfw-admin-wrap">';
        // WordPress verschiebt fremde Admin-Notices (andere Plugins, WooCommerce)
        // per wp-admin/js/common.js VOR den .wp-header-end-Marker – oder, wenn der
        // fehlt, an die erste <h1>/<h2> in .wrap. Damit die Meldungen ZUOBERST
        // landen (über Logo und Hauptnavigation), steht der Marker ganz am Anfang
        // von .wrap. WP versteckt das <hr> selbst (.wp-header-end { visibility:hidden }).
        echo '<hr class="wp-header-end">';
        echo '<div class="qsfw-app-header">';
        echo '<div class="qsfw-brand">';
        echo '<span class="qsfw-brand-mark"><img src="' . esc_url( QSFW_PLUGIN_URL . 'logos/icon-128x128.png' ) . '" alt="" width="48" height="48"></span>';
        echo '<span class="qsfw-brand-copy"><span class="qsfw-brand-name">QuickStock</span><span class="qsfw-brand-sub">for WooCommerce</span></span>';
        echo '</div>';
        echo '<div class="qsfw-header-meta">';
        if ( function_exists( 'qsfw_is_pro' ) && qsfw_is_pro() ) {
            echo '<span class="qsfw-plan-pill">';
            QSFW_Renderer::echo_icon( 'crown' );
            echo 'PRO</span>';
        }
        echo '<span class="qsfw-version-pill">v' . esc_html( QSFW_VERSION ) . '</span>';
        echo '</div>';
        echo '</div>';
        // Aktiv-Klassen vorab, damit im echo keine Ternaries stehen.
        $cls_all = ( $mode === 'all' ? 'qsfw-mainnav-item is-active' : 'qsfw-mainnav-item' );
        $cls_reorder = ( $mode === 'reorder' ? 'qsfw-mainnav-item is-active' : 'qsfw-mainnav-item' );
        $cls_po = ( in_array( $mode, array('po', 'po-draft'), true ) ? 'qsfw-mainnav-item is-active' : 'qsfw-mainnav-item' );
        $cls_verlauf = ( $mode === 'verlauf' ? 'qsfw-mainnav-item is-active' : 'qsfw-mainnav-item' );
        $cls_settings = ( $mode === 'settings' ? 'qsfw-settings-nav is-active' : 'qsfw-settings-nav' );
        echo '<div class="qsfw-mainnav-row">';
        echo '<nav class="qsfw-mainnav" aria-label="QuickStock">';
        echo '<a href="' . esc_url( $url_all ) . '" class="' . esc_attr( $cls_all ) . '">';
        QSFW_Renderer::echo_icon( 'box' );
        echo '<span>' . esc_html__( 'All Products', 'quickstock-for-woocommerce' ) . '</span></a>';
        echo '<a href="' . esc_url( $url_reorder ) . '" class="' . esc_attr( $cls_reorder ) . '">';
        QSFW_Renderer::echo_icon( 'cart' );
        echo '<span>' . esc_html__( 'Reorder', 'quickstock-for-woocommerce' ) . '</span></a>';
        echo '<a href="' . esc_url( $url_po ) . '" class="' . esc_attr( $cls_po ) . '">';
        QSFW_Renderer::echo_icon( 'clipboard' );
        echo '<span>' . esc_html__( 'Purchase Orders', 'quickstock-for-woocommerce' );
        if ( !qsfw_can_use( 'po' ) ) {
            QSFW_Renderer::echo_pro_tab_marker();
        }
        echo '</span></a>';
        echo '<a href="' . esc_url( $url_verlauf ) . '" class="' . esc_attr( $cls_verlauf ) . '">';
        QSFW_Renderer::echo_icon( 'history' );
        echo '<span>' . esc_html__( 'History', 'quickstock-for-woocommerce' ) . '</span></a>';
        echo '</nav>';
        echo '<a href="' . esc_url( $url_settings ) . '" class="' . esc_attr( $cls_settings ) . '">';
        QSFW_Renderer::echo_icon( 'gear' );
        echo '<span>' . esc_html__( 'Settings', 'quickstock-for-woocommerce' ) . '</span></a>';
        echo '</div>';
        if ( $mode === 'verlauf' ) {
            self::render_log();
            echo '</div>';
            return;
        }
        if ( $mode === 'po' ) {
            self::render_po_upsell();
            echo '</div>';
            return;
        }
        if ( $mode === 'po-draft' ) {
            self::render_po_upsell();
            echo '</div>';
            return;
        }
        if ( $mode === 'settings' ) {
            require_once QSFW_PLUGIN_DIR . 'includes/class-settings-page.php';
            QSFW_Settings_Page::render();
            echo '</div>';
            return;
        }
        self::render_table( $mode );
        echo '</div>';
    }

    /**
     * Gibt einen sortierbaren Spaltenkopf des Verlauf-Tabs direkt aus.
     *
     * Frueher eine lokale Closure, deren Rueckgabewert per echo ausgegeben
     * wurde – das flaggt der Escaping-Sniff unabhaengig vom Inhalt. Markup
     * unveraendert: der Pfeil fliesst inline im Text, nie ueberdeckend.
     *
     * @param string $key      Spaltenschluessel.
     * @param string $label    Bereits uebersetzte Beschriftung.
     * @param string $width    Breite in Pixeln; '' = keine.
     * @param array|false $cur_sort Aktive Sortierung aus QSFW_Log::parse_sort().
     * @param array  $log_cols Sortierbare Spalten aus QSFW_Log::sortable_columns().
     */
    private static function echo_log_sort_th(
        $key,
        $label,
        $width,
        $cur_sort,
        $log_cols
    ) {
        $type = $log_cols[$key]['type'];
        if ( $type === 'num' ) {
            $asc_lbl = __( '↑ Smallest first', 'quickstock-for-woocommerce' );
            $desc_lbl = __( '↓ Largest first', 'quickstock-for-woocommerce' );
        } elseif ( $type === 'date' ) {
            $asc_lbl = __( '↑ Oldest first', 'quickstock-for-woocommerce' );
            $desc_lbl = __( '↓ Newest first', 'quickstock-for-woocommerce' );
        } else {
            $asc_lbl = __( '↑ A → Z', 'quickstock-for-woocommerce' );
            $desc_lbl = __( '↓ Z → A', 'quickstock-for-woocommerce' );
        }
        $act_dir = ( $cur_sort && $cur_sort['key'] === $key ? $cur_sort['dir'] : '' );
        $th_class = 'col-log-' . $key . ' hb-th-sortable' . (( $act_dir ? ' hb-sorted' : '' ));
        $btn_class = 'hb-sort-toggle' . (( $act_dir ? ' is-active' : '' ));
        $asc_class = 'hb-sort-opt' . (( $act_dir === 'asc' ? ' is-active' : '' ));
        $des_class = 'hb-sort-opt' . (( $act_dir === 'desc' ? ' is-active' : '' ));
        echo '<th';
        if ( $width !== '' ) {
            echo ' width="' . esc_attr( (string) absint( $width ) ) . '"';
        }
        echo ' class="' . esc_attr( $th_class ) . '">';
        echo '<span class="hb-th-inner">';
        echo '<span class="hb-th-label">' . esc_html( $label ) . '</span>';
        echo '<button type="button" class="' . esc_attr( $btn_class ) . '" data-key="' . esc_attr( $key ) . '" aria-label="' . esc_attr__( 'Sort options', 'quickstock-for-woocommerce' ) . '" title="' . esc_attr__( 'Sort', 'quickstock-for-woocommerce' ) . '">';
        if ( $act_dir === 'asc' ) {
            echo '▲';
        } elseif ( $act_dir === 'desc' ) {
            echo '▼';
        } else {
            echo '⇅';
        }
        echo '</button>';
        echo '<span class="hb-sort-menu" role="menu">';
        echo '<a href="#" role="menuitem" class="' . esc_attr( $asc_class ) . '" data-param="log_sort" data-paged-param="log_paged" data-sort="logcol__' . esc_attr( $key ) . '__asc">' . esc_html( $asc_lbl ) . '</a>';
        echo '<a href="#" role="menuitem" class="' . esc_attr( $des_class ) . '" data-param="log_sort" data-paged-param="log_paged" data-sort="logcol__' . esc_attr( $key ) . '__desc">' . esc_html( $desc_lbl ) . '</a>';
        echo '</span></span></th>';
    }

    /** Gibt den Beginn einer Tabellen-Kopfzeile mit optionaler Ganzzahlbreite aus. */
    private static function render_table_header_open( $width, $class ) {
        echo '<th';
        if ( (int) $width > 0 ) {
            echo ' width="' . esc_attr( (string) absint( $width ) ) . '"';
        }
        echo ' class="' . esc_attr( $class ) . '">';
    }

    private static function render_table( $mode ) {
        // render_table() verarbeitet keine Formulare mehr. Alles, was schreibt,
        // läuft nonce-geprüft via admin_init und leitet danach um:
        //   - Cockpit-Formular + „Ansicht als Preset speichern"
        //     -> QSFW_Columns::maybe_handle_view_post()
        //   - apply/set_default/clear_default/delete
        //     -> QSFW_Presets::maybe_handle_tab_actions()
        QSFW_Presets::process_apply( $mode );
        $sold_days = max( 1, (int) QSFW_Settings_Page::get_general_setting( 'reorder_sold_days' ) );
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- reiner Anzeigeparameter; Schreiben erfolgt ausschliesslich im nonce-geprueften PRG-Handler
        $date = ( isset( $_REQUEST['stock_start_date'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['stock_start_date'] ) ) : wp_date( 'Y-m-d', strtotime( '-' . $sold_days . ' days' ) ) );
        $sort = QSFW_Columns::get_sort( $mode );
        $per_page_setting = QSFW_Columns::get_per_page();
        $status_filter = QSFW_Columns::get_status_filter();
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- reiner Anzeigeparameter ohne Zustandsaenderung
        $category_filter = ( isset( $_REQUEST['cat_filter'] ) ? intval( $_REQUEST['cat_filter'] ) : 0 );
        $visible_cols = QSFW_Columns::get_visible();
        $active_cols = QSFW_Columns::get_active( $mode );
        $enabled_cols = QSFW_Columns::get_enabled();
        $sql_sort = ( $sort === 'open_first' ? 'stock_asc' : $sort );
        $adv_filters = QSFW_Filters::get_active( $mode );
        // Variationen gruppieren? Nur im Alle-Produkte-Tab und wenn aktiviert.
        // Dann kommen die variablen Eltern mit in die Query und werden zu
        // aufklappbaren Gruppen-Köpfen gebündelt (Paginierung zählt Gruppen).
        $grouping = $mode === 'all' && (int) QSFW_Settings_Page::get_general_setting( 'group_variations', 1 ) === 1;
        $results = QSFW_Data::get_stock_data(
            $date,
            $sql_sort,
            $mode,
            $status_filter,
            $category_filter,
            $visible_cols,
            $adv_filters,
            $grouping
        );
        if ( $grouping ) {
            $results = QSFW_Data::group_variations( $results );
        }
        $checks = get_option( 'qsfw_stock_checks', array() );
        if ( $sort === 'open_first' ) {
            $offen = array();
            $erledigt = array();
            foreach ( $results as $r ) {
                if ( isset( $checks[$r->ID] ) ) {
                    $erledigt[] = $r;
                } else {
                    $offen[] = $r;
                }
            }
            $results = array_merge( $offen, $erledigt );
        }
        $total_count = count( $results );
        $per_page = ( $per_page_setting === 'all' ? max( 1, $total_count ) : intval( $per_page_setting ) );
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- reine GET-Paginierung ohne Schreibzugriff
        $current_page = max( 1, intval( $_GET['paged'] ?? 1 ) );
        $display = array_slice( $results, ($current_page - 1) * $per_page, $per_page );
        $status_labels = array(
            'publish' => __( 'Published', 'quickstock-for-woocommerce' ),
            'draft'   => __( 'Draft', 'quickstock-for-woocommerce' ),
            'private' => __( 'Private', 'quickstock-for-woocommerce' ),
            'pending' => __( 'Pending', 'quickstock-for-woocommerce' ),
        );
        // Nur die ENABLED Spalten als Optionen im Dropdown
        $all_cols = QSFW_Columns::get_all();
        $optional_cols = array();
        foreach ( $enabled_cols as $key ) {
            if ( isset( $all_cols[$key] ) ) {
                $optional_cols[$key] = $all_cols[$key]['label'];
            }
        }
        // Aktivierte optionale Spalten in aktueller Reihenfolge (für sortierbare Spalten-Liste)
        $col_groups = QSFW_Columns::get_groups();
        $current_order = QSFW_Columns::get_order();
        $ordered_enabled = array();
        foreach ( $current_order as $k ) {
            if ( in_array( $k, $enabled_cols, true ) ) {
                $ordered_enabled[] = $k;
            }
        }
        foreach ( $enabled_cols as $k ) {
            if ( !in_array( $k, $ordered_enabled, true ) ) {
                $ordered_enabled[] = $k;
            }
        }
        echo '<div class="hb-filter-container">';
        echo '<button type="button" id="qsfw-cockpit-toggle"><span class="qsfw-cockpit-chevron">▾</span> Cockpit</button>';
        // POST statt GET: Das Formular schreibt Ansichts-State (Spalten,
        // Sortierung, Filter) in user_meta. Als GET war das per URL ohne Nonce
        // auslösbar. Verarbeitet von QSFW_Columns::maybe_handle_view_post(),
        // das nonce-prüft und auf eine saubere GET-URL zurückleitet.
        // 'page' steht in der Action-URL (nicht als Hidden), damit $_GET['page']
        // im Handler greift – gleiches Muster wie #qsfw-po-draft-form.
        echo '<form id="hb-filter-form" method="post" action="' . esc_url( admin_url( 'admin.php?page=quickstock-for-woocommerce' ) ) . '">';
        echo '<input type="hidden" name="qsfw_view_submitted" value="1" />';
        wp_nonce_field( 'qsfw_view_state', '_wpnonce', true );
        // Zweiter Nonce nur für „Ansicht als Preset speichern" – schreibt die
        // globale Option und wird deshalb strenger geprüft als die User-Prefs.
        wp_nonce_field( 'qsfw_preset_save', '_qsfw_preset_nonce', false );
        echo '<input type="hidden" name="mode" value="' . esc_attr( $mode ) . '" />';
        echo '<input type="hidden" id="hb-tab-col-order" name="col_order" value="' . esc_attr( implode( ',', $ordered_enabled ) ) . '" />';
        echo '<input type="hidden" id="hb-filter-save-preset-name" name="save_preset_name" value="" />';
        $presets = QSFW_Presets::all();
        $active_preset = QSFW_Presets::get_active_id( $mode );
        $default_preset = QSFW_Presets::get_default_for( $mode );
        $is_modified = QSFW_Presets::is_modified( $mode );
        $active_label = '—';
        if ( $active_preset && isset( $presets[$active_preset] ) ) {
            $active_label = $presets[$active_preset]['name'];
            if ( $is_modified ) {
                $active_label .= ' ' . __( '(modified)', 'quickstock-for-woocommerce' );
            }
        }
        // Etiketten-Vorlagen vorab laden – sie gehören in die „Drucken & Export"-
        // Zeile (Gruppe B) weiter unten.
        $label_templates = QSFW_Label_Templates::all();
        echo '<div class="hb-row hb-row-1">';
        // === Gruppe A: „Liste" – definiert, was in der Tabelle steht ===
        echo '<div class="hb-filters qsfw-tbgroup qsfw-tbgroup--list">';
        // Ansicht (Preset-Dropdown)
        echo '<div class="qsfw-field">';
        echo '<span class="qsfw-field-cap">' . esc_html__( 'View', 'quickstock-for-woocommerce' ) . '</span>';
        echo '<details class="hb-dropdown hb-preset-dropdown">';
        echo '<summary>📑 ' . esc_html( $active_label ) . '</summary>';
        echo '<div class="hb-dropdown-panel">';
        if ( empty( $presets ) ) {
            echo '<em style="font-size:11px;color:#999;">' . esc_html__( 'No presets yet. Save the current view below.', 'quickstock-for-woocommerce' ) . '</em>';
        } else {
            echo '<div class="hb-preset-scroll">';
            foreach ( $presets as $pid => $p ) {
                $url = wp_nonce_url( add_query_arg( array(
                    'page'         => 'quickstock-for-woocommerce',
                    'mode'         => $mode,
                    'apply_preset' => $pid,
                ), admin_url( 'admin.php' ) ), 'qsfw_apply_preset_' . $pid );
                $mark = ( $pid === $active_preset && !$is_modified ? '✓ ' : '' );
                echo '<div class="hb-preset-row' . (( $pid === $active_preset ? ' is-active' : '' )) . '">';
                echo '<a class="hb-preset-opt" href="' . esc_url( $url ) . '">' . esc_html( $mark . $p['name'] ) . '</a>';
                if ( $pid === $active_preset && $is_modified ) {
                    /* translators: %s is the preset name */
                    $upd_title = sprintf( __( 'Overwrite preset "%s" with the current view', 'quickstock-for-woocommerce' ), $p['name'] );
                    echo '<button type="button" class="hb-preset-updbtn" data-preset-name="' . esc_attr( $p['name'] ) . '"' . ' title="' . esc_attr( $upd_title ) . '">💾</button>';
                }
                if ( $pid === $default_preset ) {
                    $def_url = wp_nonce_url( add_query_arg( array(
                        'page'                 => 'quickstock-for-woocommerce',
                        'mode'                 => $mode,
                        'clear_default_preset' => '1',
                    ), admin_url( 'admin.php' ) ), 'qsfw_preset_default_' . $mode . '_clear' );
                    echo '<a class="hb-preset-defbtn is-default" href="' . esc_url( $def_url ) . '" title="' . esc_attr__( 'Clear default for this tab', 'quickstock-for-woocommerce' ) . '">★</a>';
                } else {
                    $def_url = wp_nonce_url( add_query_arg( array(
                        'page'               => 'quickstock-for-woocommerce',
                        'mode'               => $mode,
                        'set_default_preset' => $pid,
                    ), admin_url( 'admin.php' ) ), 'qsfw_preset_default_' . $mode . '_' . $pid );
                    echo '<a class="hb-preset-defbtn" href="' . esc_url( $def_url ) . '" title="' . esc_attr__( 'Set as default for this tab', 'quickstock-for-woocommerce' ) . '">☆</a>';
                }
                $del_url = wp_nonce_url( add_query_arg( array(
                    'page'          => 'quickstock-for-woocommerce',
                    'mode'          => $mode,
                    'preset_delete' => $pid,
                ), admin_url( 'admin.php' ) ), 'qsfw_preset_delete_' . $pid );
                echo '<a class="hb-preset-delbtn hb-preset-delete" href="' . esc_url( $del_url ) . '" title="' . esc_attr__( 'Delete preset', 'quickstock-for-woocommerce' ) . '">🗑</a>';
                echo '</div>';
            }
            echo '</div>';
            // .hb-preset-scroll
            echo '<div class="hb-preset-foot"><a href="' . esc_url( add_query_arg( array(
                'mode'            => 'settings',
                'settings_subtab' => 'presets',
            ) ) ) . '">' . esc_html__( 'Manage presets →', 'quickstock-for-woocommerce' ) . '</a></div>';
        }
        echo '<div class="hb-preset-save-row">';
        echo '<input type="text" id="hb-save-preset-name" placeholder="' . esc_attr__( 'Name this view…', 'quickstock-for-woocommerce' ) . '" autocomplete="off" />';
        echo '<button type="button" id="hb-save-preset-btn" class="button button-small" title="' . esc_attr__( 'Save current view as preset', 'quickstock-for-woocommerce' ) . '">💾</button>';
        echo '</div>';
        echo '</div></details>';
        echo '</div>';
        // .qsfw-field (Ansicht)
        // Verkäufe seit (Datum) – nur Nachbestellung. Filtert die Liste auf
        // Produkte, die seit diesem Datum tatsächlich verkauft wurden.
        if ( $mode === 'reorder' ) {
            echo '<div class="qsfw-field">';
            echo '<span class="qsfw-field-cap" title="' . esc_attr__( 'Only products sold since this date appear in the list', 'quickstock-for-woocommerce' ) . '">' . esc_html__( 'Sold since', 'quickstock-for-woocommerce' ) . '</span>';
            echo '<input type="date" name="stock_start_date" value="' . esc_attr( $date ) . '" />';
            echo '</div>';
        }
        // Reihenfolge (Sortierung). Schlankes Dropdown: nur Sortierungen ohne
        // Spalten-Äquivalent. Alles andere läuft über die Sortier-Menüs in den
        // Spaltenköpfen.
        echo '<div class="qsfw-field">';
        echo '<span class="qsfw-field-cap">' . esc_html__( 'Sort', 'quickstock-for-woocommerce' ) . '</span>';
        $col_sort_active = QSFW_Columns::parse_column_sort( $sort );
        echo '<select name="stock_sort" class="hb-special-sort" title="' . esc_attr__( 'Sort', 'quickstock-for-woocommerce' ) . '">';
        if ( $col_sort_active !== null ) {
            echo '<option value="' . esc_attr( $sort ) . '" selected>↕ ' . esc_html__( 'Column sort', 'quickstock-for-woocommerce' ) . '</option>';
        }
        if ( $mode === 'reorder' ) {
            echo '<option value="open_first" ' . selected( $sort, 'open_first', false ) . '>📋 ' . esc_html__( 'Open first', 'quickstock-for-woocommerce' ) . '</option>';
        } else {
            echo '<option value="newest" ' . selected( $sort, 'newest', false ) . '>🆕 ' . esc_html__( 'Newest first', 'quickstock-for-woocommerce' ) . '</option>';
            echo '<option value="oldest" ' . selected( $sort, 'oldest', false ) . '>🕐 ' . esc_html__( 'Oldest first', 'quickstock-for-woocommerce' ) . '</option>';
        }
        echo '</select>';
        echo '</div>';
        // .qsfw-field (Reihenfolge)
        // pro Seite
        echo '<div class="qsfw-field">';
        echo '<span class="qsfw-field-cap">' . esc_html__( 'Per page', 'quickstock-for-woocommerce' ) . '</span>';
        echo '<select name="per_page" title="' . esc_attr__( 'Products per page', 'quickstock-for-woocommerce' ) . '">';
        foreach ( array(
            '25',
            '50',
            '100',
            '200',
            'all'
        ) as $opt ) {
            if ( $opt === 'all' ) {
                $label = __( 'All', 'quickstock-for-woocommerce' );
            } else {
                /* translators: %s is the number of products per page (e.g. "25 / page") */
                $label = sprintf( __( '%s / page', 'quickstock-for-woocommerce' ), $opt );
            }
            echo '<option value="' . esc_attr( $opt ) . '" ' . selected( $per_page_setting, $opt, false ) . '>' . esc_html( $label ) . '</option>';
        }
        echo '</select>';
        echo '</div>';
        // .qsfw-field (pro Seite)
        // Erweiterte Filter (Aktions-Button, selbsterklärend → ohne Caption,
        // richtet sich am unteren Rand der Feld-Reihe aus)
        $adv_count = QSFW_Filters::active_count();
        echo '<div class="qsfw-field qsfw-field--nocap qsfw-filter-actions">';
        echo '<button type="button" id="hb-adv-btn" class="button">⚙️ ' . esc_html__( 'Advanced filters', 'quickstock-for-woocommerce' );
        if ( $adv_count > 0 ) {
            echo '<span class="hb-adv-badge">' . intval( $adv_count ) . '</span>';
        }
        echo '</button>';
        // Bewusst ein nonce-gesicherter Server-Link (kein JS): wirkt auch dann,
        // wenn gerade „keine Produkte gefunden" angezeigt wird und damit die
        // Spaltenköpfe mitsamt Wertfilter-Trichter fehlen.
        echo '<a href="' . esc_url( QSFW_Filters::reset_url( $mode ) ) . '" id="hb-reset-filters" class="button qsfw-reset-filters' . (( $adv_count > 0 ? ' is-active' : '' )) . '" title="' . esc_attr__( 'Reset all filters, including the per-column value search', 'quickstock-for-woocommerce' ) . '">↺ ' . esc_html__( 'Reset filters', 'quickstock-for-woocommerce' ) . '</a>';
        echo '</div>';
        // Spalten
        echo '<div class="qsfw-field">';
        echo '<span class="qsfw-field-cap">' . esc_html__( 'Columns', 'quickstock-for-woocommerce' ) . '</span>';
        echo '<details class="hb-dropdown hb-col-dropdown">';
        echo '<summary>' . count( $visible_cols ) . '/' . count( $optional_cols ) . '</summary>';
        echo '<div class="hb-dropdown-panel">';
        if ( empty( $optional_cols ) ) {
            echo '<em style="font-size:11px;color:#999;">' . esc_html__( 'No columns enabled in the library.', 'quickstock-for-woocommerce' ) . ' <a href="' . esc_url( add_query_arg( 'mode', 'settings' ) ) . '">' . esc_html__( 'Go to settings', 'quickstock-for-woocommerce' ) . '</a></em>';
        } else {
            echo '<ul id="hb-col-dropdown-sortable" class="hb-sortable-list hb-col-sortable-list">';
            foreach ( $ordered_enabled as $key ) {
                if ( !isset( $all_cols[$key] ) ) {
                    continue;
                }
                $cfg = $all_cols[$key];
                $glabel = ( isset( $col_groups[$cfg['group']] ) ? $col_groups[$cfg['group']] : '' );
                echo '<li class="hb-sortable-item" data-key="' . esc_attr( $key ) . '">';
                echo '<span class="hb-drag-handle" title="' . esc_attr__( 'Drag to reorder', 'quickstock-for-woocommerce' ) . '">⋮⋮</span>';
                echo '<label class="hb-col-check-lbl"><input type="checkbox" class="hb-col-check" name="visible_cols[]" value="' . esc_attr( $key ) . '" ' . checked( in_array( $key, $visible_cols, true ), true, false ) . ' /></label>';
                echo '<span class="hb-item-label">' . esc_html( $cfg['label'] ) . '</span>';
                echo '<span class="hb-item-group">' . esc_html( $glabel ) . '</span>';
                echo '</li>';
            }
            echo '</ul>';
        }
        echo '</div></details>';
        echo '</div>';
        // .qsfw-field (Spalten)
        echo '</div>';
        // .hb-filters / qsfw-tbgroup--list (Gruppe A)
        // === Gruppe C: Status – Gesamtzahl + Häkchen leeren (rechts) ===
        // Der „Häkchen leeren"-Button ergibt nur im Nachbestell-Modus Sinn:
        // bei „Alle Produkte" lassen sich gar keine „erledigt"-Haken setzen.
        echo '<div class="hb-actions">';
        echo '<strong>' . esc_html__( 'Total:', 'quickstock-for-woocommerce' ) . ' ' . esc_html( $total_count ) . '</strong>';
        if ( $mode === 'reorder' ) {
            echo ' <span class="hb-actions-sep">·</span> <button type="button" id="reset-checks" class="button" title="' . esc_attr__( 'Clear all the “done” checkmarks in the list', 'quickstock-for-woocommerce' ) . '">🧹 ' . esc_html__( 'Clear checkmarks', 'quickstock-for-woocommerce' ) . '</button>';
        }
        echo '</div>';
        echo '</div>';
        // .hb-row-1
        // === Gruppe B: „Drucken & Export" – eigene, optisch abgesetzte Zeile ===
        // Nachbestellungs-PDF für Lieferanten (ganze gefilterte Liste, ohne
        // bereits als „erledigt" abgehakte Produkte) + Etiketten-Vorlage.
        // PDF-/CSV-Export ist Pro-gated (siehe qsfw_can_use('reorder_pdf') unten).
        $suppliers_list = array();
        $csv_suppliers = array();
        // Etikett-<option>s einmal aufbauen – das Select sitzt je nach Modus in
        // einer anderen Zeile (siehe unten), aber immer mit derselben ID, daher
        // baut nur EINE Instanz davon. Bei „Alle Produkte" bleibt es in der
        // Druck-Zeile; bei „Nachbestellung" wandert es in die Suchzeile, sonst
        // ist die Druck-Zeile überladen und die Suchzeile fast leer.
        $label_options_html = '';
        if ( !empty( $label_templates ) ) {
            $label_default = QSFW_Label_Templates::get_default_id();
            foreach ( $label_templates as $tpl_id => $tpl ) {
                $tpl_label = $tpl['name'];
                if ( !empty( $tpl['width_mm'] ) && !empty( $tpl['height_mm'] ) ) {
                    $tpl_label .= ' (' . rtrim( rtrim( (string) $tpl['width_mm'], '0' ), '.' ) . ' × ' . rtrim( rtrim( (string) $tpl['height_mm'], '0' ), '.' ) . ' mm)';
                }
                $label_options_html .= '<option value="' . esc_attr( $tpl_id ) . '" ' . selected( $label_default, $tpl_id, false ) . '>🏷️ ' . esc_html( $tpl_label ) . '</option>';
            }
        }
        if ( $mode === 'reorder' || !empty( $label_templates ) ) {
            echo '<div class="hb-row hb-row-output qsfw-tbgroup qsfw-tbgroup--output">';
            if ( $mode === 'reorder' ) {
                // Pro-Gate: PDF- und Schnellbestell-CSV-Export nur im Pro-Plan;
                // im Free-Plan dezenter Hinweis statt der Export-Steuerelemente.
                if ( !qsfw_is_pro() ) {
                    echo '<div class="qsfw-output-group qsfw-output-group--pro qsfw-field qsfw-field--nocap">';
                    echo wp_kses_post( QSFW_Renderer::pro_badge( __( 'Reorder PDF & quick-order CSV export', 'quickstock-for-woocommerce' ) ) );
                    echo '</div>';
                }
            }
            // Etikett nur bei „Alle Produkte" hier in der Druck-Zeile (bei
            // „Nachbestellung" sitzt es in der Suchzeile, s. u.).
            if ( $mode !== 'reorder' && $label_options_html !== '' ) {
                echo '<div class="qsfw-output-group qsfw-output-group--label">';
                echo '<div class="qsfw-field">';
                echo '<span class="qsfw-field-cap">' . esc_html__( 'Label', 'quickstock-for-woocommerce' ) . '</span>';
                echo '<select id="qsfw-label-tpl-select" title="' . esc_attr__( 'Label template for printing', 'quickstock-for-woocommerce' ) . '">' . wp_kses( $label_options_html, array(
                    'option' => array(
                        'value'    => true,
                        'selected' => true,
                    ),
                ) ) . '</select>';
                echo '</div>';
                echo '</div>';
                // .qsfw-output-group--label
            }
            echo '</div>';
            // .hb-row-output
        }
        echo '<div class="hb-row hb-row-2">';
        if ( $mode === 'all' ) {
            $product_cats = get_terms( array(
                'taxonomy'   => 'product_cat',
                'hide_empty' => false,
                'orderby'    => 'name',
            ) );
            echo '<select name="cat_filter" class="hb-cat-select">';
            echo '<option value="0">' . esc_html__( '— All categories —', 'quickstock-for-woocommerce' ) . '</option>';
            if ( !is_wp_error( $product_cats ) ) {
                foreach ( $product_cats as $cat ) {
                    echo '<option value="' . esc_attr( $cat->term_id ) . '" ' . selected( $category_filter, $cat->term_id, false ) . '>' . esc_html( html_entity_decode( $cat->name, ENT_QUOTES, 'UTF-8' ) ) . ' (' . esc_html( $cat->count ) . ')</option>';
                }
            }
            echo '</select>';
        }
        $search_placeholder = ( $mode === 'all' ? __( '🔍 Search all products…', 'quickstock-for-woocommerce' ) : __( '🔍 Search SKU or product name…', 'quickstock-for-woocommerce' ) );
        echo '<input type="search" id="stock-search" placeholder="' . esc_attr( $search_placeholder ) . '" autocomplete="off" />';
        // Etikett-Auswahl bei „Nachbestellung" hier (statt in der Druck-Zeile),
        // passend zur Suchzeilen-Höhe, ohne Caption – analog Kategorie/Suche.
        if ( $mode === 'reorder' && $label_options_html !== '' ) {
            echo '<select id="qsfw-label-tpl-select" class="hb-row2-label" title="' . esc_attr__( 'Label template for printing', 'quickstock-for-woocommerce' ) . '">' . wp_kses( $label_options_html, array(
                'option' => array(
                    'value'    => true,
                    'selected' => true,
                ),
            ) ) . '</select>';
        }
        echo '<span id="search-status"></span>';
        echo '</div>';
        self::render_adv_drawer(
            $mode,
            $status_filter,
            $status_labels,
            $adv_filters
        );
        echo '</form>';
        echo '</div>';
        if ( $display ) {
            $sortable_map = QSFW_Columns::sortable_columns();
            $cur_col_sort = QSFW_Columns::parse_column_sort( $sort );
            $filterable = QSFW_Filters::value_filterable();
            $colf_active = QSFW_Filters::get_col_filters( $mode );
            echo '<table class="wp-list-table widefat fixed striped"><thead><tr>';
            foreach ( $active_cols as $key => $cfg ) {
                if ( $key === 'bulk_select' ) {
                    self::render_table_header_open( $cfg['width'] ?? 0, 'col-bulk_select' );
                    echo '<input type="checkbox" id="hb-bulk-all" title="' . esc_attr__( 'Select all on this page', 'quickstock-for-woocommerce' ) . '"></th>';
                    continue;
                }
                $header_label = ( !empty( $cfg['short'] ) ? $cfg['short'] : $cfg['label'] );
                $kses = array(
                    'br' => array(),
                );
                $is_sortable = isset( $sortable_map[$key] );
                $is_filterable = isset( $filterable[$key] );
                if ( !$is_sortable && !$is_filterable ) {
                    // Gleiche Innen-Struktur wie sortierbare Köpfe (nur ohne Pfeil/Menü):
                    // so bricht der Titel an Wortgrenzen um (statt hinter die Nachbar-
                    // spalte zu laufen) UND startet auf identischer Höhe (18px-Zeilenbox).
                    self::render_table_header_open( $cfg['width'] ?? 0, 'col-' . $key );
                    echo '<span class="hb-th-inner"><span class="hb-th-label">' . wp_kses( $header_label, $kses ) . '</span></span>';
                    echo '</th>';
                    continue;
                }
                $act_dir = ( $is_sortable && $cur_col_sort && $cur_col_sort['key'] === $key ? $cur_col_sort['dir'] : '' );
                $has_filter = $is_filterable && !empty( $colf_active[$key] );
                // Toggle-Icon: aktiver Filter → Trichter, aktive Sortierung → Pfeil,
                // sonst neutraler Dropdown-Caret.
                if ( $has_filter ) {
                    $arrow = '▾';
                } elseif ( $act_dir === 'asc' ) {
                    $arrow = '▲';
                } elseif ( $act_dir === 'desc' ) {
                    $arrow = '▼';
                } else {
                    $arrow = '▾';
                }
                $th_class = 'col-' . $key . ' hb-th-sortable' . (( $act_dir ? ' hb-sorted' : '' )) . (( $has_filter ? ' hb-filtered' : '' ));
                self::render_table_header_open( $cfg['width'] ?? 0, $th_class );
                echo '<span class="hb-th-inner">';
                echo '<span class="hb-th-label">' . wp_kses( $header_label, $kses ) . '</span>';
                $btn_cls = 'hb-sort-toggle' . (( $act_dir ? ' is-active' : '' )) . (( $has_filter ? ' is-filtered' : '' ));
                echo '<button type="button" class="' . esc_attr( $btn_cls ) . '" data-key="' . esc_attr( $key ) . '" aria-label="' . esc_attr__( 'Column options', 'quickstock-for-woocommerce' ) . '" title="' . esc_attr__( 'Sort & filter', 'quickstock-for-woocommerce' ) . '">' . esc_html( $arrow ) . '</button>';
                echo '<span class="hb-sort-menu hb-col-menu" role="menu">';
                if ( $is_sortable ) {
                    $type = $sortable_map[$key];
                    if ( $type === 'num' ) {
                        $asc_lbl = __( '↑ Smallest first', 'quickstock-for-woocommerce' );
                        $desc_lbl = __( '↓ Largest first', 'quickstock-for-woocommerce' );
                    } elseif ( $type === 'date' ) {
                        $asc_lbl = __( '↑ Oldest first', 'quickstock-for-woocommerce' );
                        $desc_lbl = __( '↓ Newest first', 'quickstock-for-woocommerce' );
                    } else {
                        $asc_lbl = __( '↑ A → Z', 'quickstock-for-woocommerce' );
                        $desc_lbl = __( '↓ Z → A', 'quickstock-for-woocommerce' );
                    }
                    echo '<a href="#" role="menuitem" class="hb-sort-opt' . (( $act_dir === 'asc' ? ' is-active' : '' )) . '" data-sort="col__' . esc_attr( $key ) . '__asc">' . esc_html( $asc_lbl ) . '</a>';
                    echo '<a href="#" role="menuitem" class="hb-sort-opt' . (( $act_dir === 'desc' ? ' is-active' : '' )) . '" data-sort="col__' . esc_attr( $key ) . '__desc">' . esc_html( $desc_lbl ) . '</a>';
                }
                if ( $is_filterable ) {
                    if ( $is_sortable ) {
                        echo '<div class="hb-colf-sep"></div>';
                    }
                    $active_json = ( !empty( $colf_active[$key] ) ? wp_json_encode( $colf_active[$key] ) : '' );
                    echo '<div class="hb-colf" data-col="' . esc_attr( $key ) . '"' . (( $active_json ? ' data-active="' . esc_attr( $active_json ) . '"' : '' )) . '>';
                    echo '<input type="search" class="hb-colf-search" placeholder="' . esc_attr__( 'Search values…', 'quickstock-for-woocommerce' ) . '" autocomplete="off" />';
                    echo '<div class="hb-colf-contains" hidden></div>';
                    echo '<label class="hb-colf-all"><input type="checkbox" class="hb-colf-all-cb" /> ' . esc_html__( '(Select all)', 'quickstock-for-woocommerce' ) . '</label>';
                    echo '<ul class="hb-colf-list"><li class="hb-colf-note">' . esc_html__( 'Loading…', 'quickstock-for-woocommerce' ) . '</li></ul>';
                    echo '<div class="hb-colf-foot">';
                    echo '<button type="button" class="button button-primary button-small hb-colf-apply">' . esc_html__( 'Apply', 'quickstock-for-woocommerce' ) . '</button>';
                    echo '<button type="button" class="button button-small hb-colf-clear">' . esc_html__( 'Clear', 'quickstock-for-woocommerce' ) . '</button>';
                    echo '</div>';
                    echo '</div>';
                }
                echo '</span>';
                echo '</span>';
                echo '</th>';
            }
            echo '</tr></thead><tbody id="stock-tbody">';
            // echo_row() gibt das Zeilen-Markup feldbezogen escaped direkt aus.
            foreach ( $display as $p ) {
                if ( $grouping && !empty( $p->qsfw_is_parent ) ) {
                    QSFW_Renderer::echo_row(
                        $p,
                        $checks,
                        $active_cols,
                        'parent'
                    );
                    foreach ( $p->qsfw_children as $child ) {
                        QSFW_Renderer::echo_row(
                            $child,
                            $checks,
                            $active_cols,
                            'child'
                        );
                    }
                } else {
                    QSFW_Renderer::echo_row( $p, $checks, $active_cols );
                }
            }
            echo '</tbody></table>';
            $total_pages = ceil( $total_count / $per_page );
            if ( $total_pages > 1 ) {
                // stock_sort/per_page gehören NICHT in die Paginierungs-URL:
                // sie stehen in user_meta und wären hier inert. Schlimmer noch –
                // als GET-Parameter würden sie den Schreibzweig der Getter
                // auslösen, also genau den nonce-losen Pfad, den der Umbau
                // schliesst. Nur reine Anzeige-Parameter bleiben.
                $base_url = add_query_arg( array(
                    'page'             => 'quickstock-for-woocommerce',
                    'mode'             => $mode,
                    'stock_start_date' => $date,
                    'cat_filter'       => $category_filter,
                    'paged'            => '%#%',
                ), admin_url( 'admin.php' ) );
                echo '<div class="tablenav"><div class="tablenav-pages">';
                echo wp_kses_post( paginate_links( array(
                    'base'      => $base_url,
                    'format'    => '',
                    'prev_text' => '&laquo;',
                    'next_text' => '&raquo;',
                    'total'     => $total_pages,
                    'current'   => $current_page,
                ) ) );
                echo '</div></div>';
            }
        } else {
            $msg = ( $mode === 'all' ? __( 'No products found.', 'quickstock-for-woocommerce' ) : __( 'Nothing needs reordering right now. 🎉', 'quickstock-for-woocommerce' ) );
            echo '<div style="background:#fff;padding:30px;text-align:center;border:1px solid #ccd0d4;color:#666;">' . esc_html( $msg ) . '</div>';
        }
        if ( $mode === 'all' ) {
            self::render_bulk_ui();
        }
        if ( $mode === 'reorder' ) {
            self::render_reorder_bulk_ui();
        }
    }

    /**
     * Slide-in „Erweiterte Filter"-Drawer. Markup liegt innerhalb des
     * #hb-filter-form, alle Felder werden bei „Filter anwenden" mitgesendet.
     */
    private static function render_adv_drawer(
        $mode,
        $status_filter,
        $status_labels,
        $adv_filters
    ) {
        $avail = QSFW_Filters::available();
        $groups = QSFW_Columns::get_groups();
        // Verfügbare Felder nach Gruppe bündeln
        $by_group = array();
        foreach ( $avail as $col => $def ) {
            $by_group[$def['group']][$col] = $def;
        }
        echo '<div id="hb-adv-backdrop" aria-hidden="true"></div>';
        echo '<aside id="hb-adv-drawer" aria-hidden="true" aria-label="' . esc_attr__( 'Advanced filters', 'quickstock-for-woocommerce' ) . '">';
        echo '<div class="hb-adv-head"><strong>⚙️ ' . esc_html__( 'Advanced filters', 'quickstock-for-woocommerce' ) . '</strong>';
        echo '<button type="button" id="hb-adv-close" class="hb-adv-x" aria-label="' . esc_attr__( 'Close', 'quickstock-for-woocommerce' ) . '">×</button></div>';
        // Kein 'adv_filter_submitted'-Marker mehr nötig: Der Drawer liegt im
        // #hb-filter-form, seine Felder gehen bei jedem Cockpit-Submit mit, und
        // maybe_handle_view_post() speichert die erweiterten Filter ohnehin
        // immer mit.
        echo '<div class="hb-adv-body">';
        // Status-Sektion (Sonderfall, nutzt die unveränderte status_filter[]-Mechanik)
        echo '<div class="hb-adv-section"><h4>' . esc_html__( 'Status', 'quickstock-for-woocommerce' ) . '</h4>';
        foreach ( $status_labels as $key => $label ) {
            echo '<label class="hb-adv-cb"><input type="checkbox" name="status_filter[]" value="' . esc_attr( $key ) . '"' . checked( in_array( $key, $status_filter, true ), true, false ) . ' /> ' . esc_html( $label ) . '</label>';
        }
        echo '</div>';
        // Kategorien-Sektion direkt nach Status (Include/Exclude mit Multi-Select).
        // Wird hier separat gerendert und aus der nachfolgenden Gruppen-Iteration ausgenommen.
        if ( isset( $avail['product_cat'] ) ) {
            $cat_def = $avail['product_cat'];
            $cat_cur = ( isset( $adv_filters['product_cat'] ) ? $adv_filters['product_cat'] : null );
            $cur_mode = ( is_array( $cat_cur ) && isset( $cat_cur['mode'] ) && $cat_cur['mode'] === 'ex' ? 'ex' : 'in' );
            $cur_ids = ( is_array( $cat_cur ) && isset( $cat_cur['ids'] ) ? array_map( 'intval', (array) $cat_cur['ids'] ) : array() );
            echo '<div class="hb-adv-section"><h4>' . esc_html__( 'Categories', 'quickstock-for-woocommerce' ) . '</h4>';
            if ( empty( $cat_def['terms'] ) ) {
                echo '<em class="hb-adv-empty">' . esc_html__( 'No entries', 'quickstock-for-woocommerce' ) . '</em>';
            } else {
                echo '<div class="hb-adv-mode">';
                echo '<label class="hb-adv-rb"><input type="radio" name="adv[product_cat][mode]" value="in"' . (( $cur_mode === 'in' ? ' checked' : '' )) . ' /> ' . esc_html__( 'Include only these', 'quickstock-for-woocommerce' ) . '</label>';
                echo '<label class="hb-adv-rb"><input type="radio" name="adv[product_cat][mode]" value="ex"' . (( $cur_mode === 'ex' ? ' checked' : '' )) . ' /> ' . esc_html__( 'All except these', 'quickstock-for-woocommerce' ) . '</label>';
                echo '</div>';
                echo '<select class="hb-cat-multi" name="adv[product_cat][ids][]" multiple="multiple" data-placeholder="' . esc_attr__( 'Pick categories…', 'quickstock-for-woocommerce' ) . '" style="width:100%;">';
                foreach ( $cat_def['terms'] as $t ) {
                    echo '<option value="' . intval( $t['id'] ) . '"' . selected( in_array( (int) $t['id'], $cur_ids, true ), true, false ) . '>' . esc_html( $t['name'] ) . '</option>';
                }
                echo '</select>';
            }
            echo '</div>';
            // Aus der Gruppen-Iteration entfernen, damit es nicht doppelt erscheint.
            if ( isset( $by_group[$cat_def['group']]['product_cat'] ) ) {
                unset($by_group[$cat_def['group']]['product_cat']);
            }
        }
        foreach ( $groups as $gkey => $glabel ) {
            if ( empty( $by_group[$gkey] ) ) {
                continue;
            }
            echo '<div class="hb-adv-section"><h4>' . esc_html( $glabel ) . '</h4>';
            foreach ( $by_group[$gkey] as $col => $def ) {
                $cur = ( isset( $adv_filters[$col] ) ? $adv_filters[$col] : null );
                echo '<div class="hb-adv-field"><label class="hb-adv-flabel">' . esc_html( $def['label'] ) . '</label>';
                if ( $def['kind'] === 'num_range' || $def['kind'] === 'date_range' ) {
                    $type = ( $def['kind'] === 'num_range' ? 'number' : 'date' );
                    $step = ( $def['kind'] === 'num_range' ? 'any' : '1' );
                    $vmin = ( is_array( $cur ) && isset( $cur['min'] ) ? $cur['min'] : '' );
                    $vmax = ( is_array( $cur ) && isset( $cur['max'] ) ? $cur['max'] : '' );
                    echo '<div class="hb-adv-range">';
                    echo '<input type="' . esc_attr( $type ) . '" step="' . esc_attr( $step ) . '" name="adv[' . esc_attr( $col ) . '][min]" placeholder="' . esc_attr__( 'from', 'quickstock-for-woocommerce' ) . '" value="' . esc_attr( $vmin ) . '" />';
                    echo '<span>–</span>';
                    echo '<input type="' . esc_attr( $type ) . '" step="' . esc_attr( $step ) . '" name="adv[' . esc_attr( $col ) . '][max]" placeholder="' . esc_attr__( 'to', 'quickstock-for-woocommerce' ) . '" value="' . esc_attr( $vmax ) . '" />';
                    echo '</div>';
                } elseif ( $def['kind'] === 'enum' ) {
                    $sel = ( is_array( $cur ) ? $cur : array() );
                    foreach ( $def['options'] as $ov => $ol ) {
                        echo '<label class="hb-adv-cb"><input type="checkbox" name="adv[' . esc_attr( $col ) . '][]" value="' . esc_attr( $ov ) . '"' . checked( in_array( (string) $ov, array_map( 'strval', $sel ), true ), true, false ) . ' /> ' . esc_html( $ol ) . '</label>';
                    }
                } elseif ( $def['kind'] === 'terms' ) {
                    $sel = ( is_array( $cur ) ? array_map( 'intval', $cur ) : array() );
                    if ( empty( $def['terms'] ) ) {
                        echo '<em class="hb-adv-empty">' . esc_html__( 'No entries', 'quickstock-for-woocommerce' ) . '</em>';
                    } else {
                        echo '<div class="hb-adv-termbox">';
                        foreach ( $def['terms'] as $t ) {
                            echo '<label class="hb-adv-cb"><input type="checkbox" name="adv[' . esc_attr( $col ) . '][]" value="' . intval( $t['id'] ) . '"' . checked( in_array( (int) $t['id'], $sel, true ), true, false ) . ' /> ' . esc_html( $t['name'] ) . '</label>';
                        }
                        echo '</div>';
                    }
                } elseif ( $def['kind'] === 'terms_inc_exc' ) {
                    $cur_mode = ( is_array( $cur ) && isset( $cur['mode'] ) && $cur['mode'] === 'ex' ? 'ex' : 'in' );
                    $cur_ids = ( is_array( $cur ) && isset( $cur['ids'] ) ? array_map( 'intval', (array) $cur['ids'] ) : array() );
                    if ( empty( $def['terms'] ) ) {
                        echo '<em class="hb-adv-empty">' . esc_html__( 'No entries', 'quickstock-for-woocommerce' ) . '</em>';
                    } else {
                        echo '<div class="hb-adv-mode">';
                        echo '<label class="hb-adv-rb"><input type="radio" name="adv[' . esc_attr( $col ) . '][mode]" value="in"' . (( $cur_mode === 'in' ? ' checked' : '' )) . ' /> ' . esc_html__( 'Include only these', 'quickstock-for-woocommerce' ) . '</label>';
                        echo '<label class="hb-adv-rb"><input type="radio" name="adv[' . esc_attr( $col ) . '][mode]" value="ex"' . (( $cur_mode === 'ex' ? ' checked' : '' )) . ' /> ' . esc_html__( 'All except these', 'quickstock-for-woocommerce' ) . '</label>';
                        echo '</div>';
                        echo '<select class="hb-cat-multi" name="adv[' . esc_attr( $col ) . '][ids][]" multiple="multiple" data-placeholder="' . esc_attr__( 'Pick categories…', 'quickstock-for-woocommerce' ) . '" style="width:100%;">';
                        foreach ( $def['terms'] as $t ) {
                            echo '<option value="' . intval( $t['id'] ) . '"' . selected( in_array( (int) $t['id'], $cur_ids, true ), true, false ) . '>' . esc_html( $t['name'] ) . '</option>';
                        }
                        echo '</select>';
                    }
                } elseif ( $def['kind'] === 'text' ) {
                    $tv = ( is_array( $cur ) ? '' : (string) $cur );
                    echo '<input type="text" class="hb-adv-text" name="adv[' . esc_attr( $col ) . ']" placeholder="' . esc_attr__( 'contains…', 'quickstock-for-woocommerce' ) . '" value="' . esc_attr( $tv ) . '" />';
                }
                echo '</div>';
            }
            echo '</div>';
        }
        echo '</div>';
        // .hb-adv-body
        echo '<div class="hb-adv-foot">';
        echo '<button type="submit" class="button button-primary">' . esc_html__( 'Apply filters', 'quickstock-for-woocommerce' ) . '</button>';
        echo '<button type="button" id="hb-adv-reset" class="button" data-reset-url="' . esc_url( QSFW_Filters::reset_url( $mode ) ) . '">' . esc_html__( 'Reset', 'quickstock-for-woocommerce' ) . '</button>';
        echo '</div>';
        echo '</aside>';
    }

    /**
     * Sticky Bulk-Aktionsleiste + Bestätigungs-/Fortschritts-Modal +
     * verstecktes CSV-Export-Formular. Nur im "Alle Produkte"-Tab.
     * Werte-Felder und Term-Listen baut admin.js aus QSFW.bulk* auf.
     */
    /**
     * Tab „📜 Verlauf": protokollierte Bestandsänderungen mit Filtern,
     * Paginierung und CSV-Export.
     */
    private static function render_log() {
        // Aktive Filter aus der URL
        // phpcs:disable WordPress.Security.NonceVerification.Recommended -- reine GET-Filter fuer die schreibfreie Verlaufsansicht
        $q = ( isset( $_GET['log_q'] ) ? sanitize_text_field( wp_unslash( $_GET['log_q'] ) ) : '' );
        $from = ( isset( $_GET['log_from'] ) ? sanitize_text_field( wp_unslash( $_GET['log_from'] ) ) : '' );
        $to = ( isset( $_GET['log_to'] ) ? sanitize_text_field( wp_unslash( $_GET['log_to'] ) ) : '' );
        $source = ( isset( $_GET['log_source'] ) ? sanitize_key( wp_unslash( $_GET['log_source'] ) ) : '' );
        if ( !in_array( $source, array('manual', 'order', 'po'), true ) ) {
            $source = '';
        }
        $paged = max( 1, intval( $_GET['log_paged'] ?? 1 ) );
        $per_page = 50;
        $sort = ( isset( $_GET['log_sort'] ) ? sanitize_text_field( wp_unslash( $_GET['log_sort'] ) ) : '' );
        // phpcs:enable WordPress.Security.NonceVerification.Recommended
        if ( QSFW_Log::parse_sort( $sort ) === null ) {
            $sort = '';
        }
        if ( !QSFW_Log::is_enabled() ) {
            $set_url = admin_url( 'admin.php?page=quickstock-for-woocommerce&mode=settings&settings_subtab=general' );
            echo '<div style="background:#fff;padding:30px;text-align:center;border:1px solid #ccd0d4;color:#666;margin-top:16px;">';
            echo esc_html__( 'Stock history is disabled.', 'quickstock-for-woocommerce' ) . ' ';
            echo '<a href="' . esc_url( $set_url ) . '">' . esc_html__( 'Enable it in settings →', 'quickstock-for-woocommerce' ) . '</a>';
            echo '</div>';
            return;
        }
        $res = QSFW_Log::query( compact(
            'q',
            'from',
            'to',
            'source',
            'sort',
            'per_page',
            'paged'
        ) );
        $rows = $res['rows'];
        $total = $res['total'];
        $retention = (int) QSFW_Settings_Page::get_general_setting( 'log_retention_days', 90 );
        if ( $retention > 0 ) {
            /* translators: %d is the number of retention days for log entries */
            $ret_txt = sprintf( _n(
                'Retention: %d day',
                'Retention: %d days',
                $retention,
                'quickstock-for-woocommerce'
            ), $retention );
        } else {
            $ret_txt = __( 'Retention: unlimited', 'quickstock-for-woocommerce' );
        }
        // Filter-Formular (GET, Seiten-Reload)
        echo '<div class="hb-filter-container" style="margin-top:12px;">';
        echo '<form method="get" action="' . esc_url( admin_url( 'admin.php' ) ) . '">';
        echo '<input type="hidden" name="page" value="quickstock-for-woocommerce" />';
        echo '<input type="hidden" name="mode" value="verlauf" />';
        echo '<div class="hb-row hb-row-1"><div class="hb-filters">';
        echo '<input type="search" name="log_q" value="' . esc_attr( $q ) . '" placeholder="' . esc_attr__( '🔍 Product, ID or order #', 'quickstock-for-woocommerce' ) . '" style="min-width:240px;" />';
        echo '<label style="font-size:12px;color:#666;">' . esc_html__( 'from', 'quickstock-for-woocommerce' ) . ' <input type="date" name="log_from" value="' . esc_attr( $from ) . '" /></label>';
        echo '<label style="font-size:12px;color:#666;">' . esc_html__( 'to', 'quickstock-for-woocommerce' ) . ' <input type="date" name="log_to" value="' . esc_attr( $to ) . '" /></label>';
        echo '<select name="log_source">';
        echo '<option value="">' . esc_html__( '— All sources —', 'quickstock-for-woocommerce' ) . '</option>';
        echo '<option value="manual" ' . selected( $source, 'manual', false ) . '>✏️ ' . esc_html__( 'Manual', 'quickstock-for-woocommerce' ) . '</option>';
        echo '<option value="order" ' . selected( $source, 'order', false ) . '>🛒 ' . esc_html__( 'Order', 'quickstock-for-woocommerce' ) . '</option>';
        echo '<option value="po" ' . selected( $source, 'po', false ) . '>📦 ' . esc_html__( 'Goods receipt', 'quickstock-for-woocommerce' ) . '</option>';
        echo '</select>';
        echo '<button type="submit" class="button button-primary">' . esc_html__( 'Filter', 'quickstock-for-woocommerce' ) . '</button>';
        echo '<a class="button" href="' . esc_url( admin_url( 'admin.php?page=quickstock-for-woocommerce&mode=verlauf' ) ) . '">' . esc_html__( 'Reset', 'quickstock-for-woocommerce' ) . '</a>';
        echo '</div>';
        echo '<div class="hb-actions">';
        echo '<strong>' . esc_html__( 'Total:', 'quickstock-for-woocommerce' ) . ' ' . intval( $total ) . '</strong> ';
        $export_url = wp_nonce_url( add_query_arg( array(
            'action'     => 'qsfw_log_export',
            'log_q'      => $q,
            'log_from'   => $from,
            'log_to'     => $to,
            'log_source' => $source,
        ), admin_url( 'admin-ajax.php' ) ), 'qsfw_log_export' );
        echo '| <a class="button" href="' . esc_url( $export_url ) . '">⬇️ ' . esc_html__( 'CSV export', 'quickstock-for-woocommerce' ) . '</a>';
        echo '</div>';
        echo '</div>';
        // .hb-row
        echo '<p style="color:#666;font-size:11px;margin:6px 0 0;">' . esc_html( $ret_txt ) . '</p>';
        echo '</form></div>';
        if ( empty( $rows ) ) {
            echo '<div style="background:#fff;padding:30px;text-align:center;border:1px solid #ccd0d4;color:#666;">' . esc_html__( 'No history entries found.', 'quickstock-for-woocommerce' ) . '</div>';
            return;
        }
        $hpos = qsfw_is_hpos_enabled();
        $cur_sort = QSFW_Log::parse_sort( $sort );
        $log_cols = QSFW_Log::sortable_columns();
        echo '<table class="wp-list-table widefat fixed striped"><thead><tr>';
        self::echo_log_sort_th(
            'date',
            __( 'Date', 'quickstock-for-woocommerce' ),
            '150',
            $cur_sort,
            $log_cols
        );
        self::echo_log_sort_th(
            'product',
            __( 'Product', 'quickstock-for-woocommerce' ),
            '',
            $cur_sort,
            $log_cols
        );
        self::echo_log_sort_th(
            'qfrom',
            __( 'Before', 'quickstock-for-woocommerce' ),
            '90',
            $cur_sort,
            $log_cols
        );
        self::echo_log_sort_th(
            'qto',
            __( 'After', 'quickstock-for-woocommerce' ),
            '90',
            $cur_sort,
            $log_cols
        );
        self::echo_log_sort_th(
            'delta',
            __( 'Change', 'quickstock-for-woocommerce' ),
            '80',
            $cur_sort,
            $log_cols
        );
        self::echo_log_sort_th(
            'source',
            __( 'Source', 'quickstock-for-woocommerce' ),
            '160',
            $cur_sort,
            $log_cols
        );
        self::echo_log_sort_th(
            'user',
            __( 'User', 'quickstock-for-woocommerce' ),
            '140',
            $cur_sort,
            $log_cols
        );
        echo '</tr></thead><tbody>';
        foreach ( $rows as $r ) {
            // Datum
            $ts = strtotime( $r->created_at );
            $date = ( $ts ? date_i18n( 'd.m.Y H:i', $ts ) : esc_html( $r->created_at ) );
            // Produkt (Name + Link, ggf. Variations-Hinweis)
            $name = ( $r->product_name !== '' ? $r->product_name : '#' . $r->product_id );
            $edit = ( $r->product_id ? get_edit_post_link( $r->product_id ) : '' );
            $prod = ( $edit ? '<a href="' . esc_url( $edit ) . '">' . esc_html( $name ) . '</a>' : esc_html( $name ) );
            if ( $r->variation_id ) {
                /* translators: %d is the WooCommerce variation ID */
                $prod .= ' <span style="color:#888;font-size:11px;">' . sprintf( esc_html__( '(Variation #%d)', 'quickstock-for-woocommerce' ), intval( $r->variation_id ) ) . '</span>';
            }
            // Vorher / Nachher / Δ
            $v_from = ( $r->qty_from === null ? '—' : $r->qty_from );
            $v_to = ( $r->qty_to === null ? '—' : $r->qty_to );
            if ( $r->delta === null ) {
                $delta = '—';
            } elseif ( (int) $r->delta > 0 ) {
                $delta = '<span style="color:#1a7f37;font-weight:600;">+' . intval( $r->delta ) . '</span>';
            } elseif ( (int) $r->delta < 0 ) {
                $delta = '<span style="color:#c1392b;font-weight:600;">' . intval( $r->delta ) . '</span>';
            } else {
                $delta = '0';
            }
            // Quelle (+ Bestell-/PO-Link). Bei Wareneingang ('po') steht in der
            // order_id-Spalte die Einkaufsbestellungs-ID → Link auf das PO-Detail.
            $src = QSFW_Log::source_label( $r->source );
            if ( $r->source === 'order' && $r->order_id ) {
                $order_url = ( $hpos ? admin_url( 'admin.php?page=wc-orders&action=edit&id=' . intval( $r->order_id ) ) : admin_url( 'post.php?post=' . intval( $r->order_id ) . '&action=edit' ) );
                $src .= ' <a href="' . esc_url( $order_url ) . '">#' . intval( $r->order_id ) . '</a>';
            } elseif ( $r->source === 'po' && $r->order_id ) {
                $po_url = admin_url( 'admin.php?page=quickstock-for-woocommerce&mode=po&po=' . intval( $r->order_id ) );
                $src .= ' <a href="' . esc_url( $po_url ) . '">#' . intval( $r->order_id ) . '</a>';
            }
            echo '<tr>';
            echo '<td>' . esc_html( $date ) . '</td>';
            echo '<td>' . wp_kses( $prod, array(
                'a'    => array(
                    'href' => true,
                ),
                'span' => array(
                    'style' => true,
                ),
            ) ) . '</td>';
            echo '<td>' . esc_html( $v_from ) . '</td>';
            echo '<td>' . esc_html( $v_to ) . '</td>';
            echo '<td>' . wp_kses( $delta, array(
                'span' => array(
                    'style' => true,
                ),
            ) ) . '</td>';
            echo '<td>' . wp_kses( $src, array(
                'a' => array(
                    'href' => true,
                ),
            ) ) . '</td>';
            echo '<td>' . esc_html( QSFW_Ajax::log_user_name( $r->user_id ) ) . '</td>';
            echo '</tr>';
        }
        echo '</tbody></table>';
        // Paginierung
        $total_pages = (int) ceil( $total / $per_page );
        if ( $total_pages > 1 ) {
            $base_url = add_query_arg( array(
                'page'       => 'quickstock-for-woocommerce',
                'mode'       => 'verlauf',
                'log_q'      => $q,
                'log_from'   => $from,
                'log_to'     => $to,
                'log_source' => $source,
                'log_sort'   => $sort,
                'log_paged'  => '%#%',
            ), admin_url( 'admin.php' ) );
            echo '<div class="tablenav"><div class="tablenav-pages">';
            echo wp_kses_post( paginate_links( array(
                'base'      => $base_url,
                'format'    => '',
                'prev_text' => '&laquo;',
                'next_text' => '&raquo;',
                'total'     => $total_pages,
                'current'   => $paged,
            ) ) );
            echo '</div></div>';
        }
    }

    private static function render_bulk_ui() {
        $cf_options = '';
        // Lieferanten-Zuordnung als Bulk-Aktion nur anbieten, wenn überhaupt
        // Lieferanten angelegt sind (sonst hätte das Dropdown keine Werte) – und
        // nur im Pro-Plan (SRM ist Pro; Server-Guard sitzt in apply_field).
        $has_suppliers = false;
        // Taxonomie-Aktionen nur für aktivierte + existierende Taxonomien
        $tax_avail = QSFW_Columns::available_bulk_taxonomies();
        $tax_labels = array(
            'shipping_class' => array(array('shipping_class', __( 'Set shipping class …', 'quickstock-for-woocommerce' ))),
            'product_cat'    => array(array('cat_add', __( 'Add category …', 'quickstock-for-woocommerce' )), array('cat_remove', __( 'Remove category …', 'quickstock-for-woocommerce' ))),
        );
        $tax_options = '';
        foreach ( $tax_avail as $op => $m ) {
            if ( !isset( $tax_labels[$op] ) ) {
                continue;
            }
            foreach ( $tax_labels[$op] as $opt ) {
                $tax_options .= '<option value="' . esc_attr( $opt[0] ) . '">' . esc_html( $opt[1] ) . '</option>';
            }
        }
        ?>
        <div id="hb-bulk-bar" aria-hidden="true">
            <div class="hb-bulk-inner">
                <span class="hb-bulk-sel"><strong id="hb-bulk-count">0</strong>&nbsp;<?php 
        esc_html_e( 'selected', 'quickstock-for-woocommerce' );
        ?></span>

                <select id="hb-bulk-op">
                    <option value=""><?php 
        esc_html_e( '— Choose action —', 'quickstock-for-woocommerce' );
        ?></option>
                    <optgroup label="<?php 
        esc_attr_e( 'Stock', 'quickstock-for-woocommerce' );
        ?>">
                        <option value="stock_set"><?php 
        esc_html_e( 'Set stock quantity …', 'quickstock-for-woocommerce' );
        ?></option>
                        <option value="stock_inc"><?php 
        esc_html_e( 'Adjust stock (+/−) …', 'quickstock-for-woocommerce' );
        ?></option>
                        <option value="manage_stock_on"><?php 
        esc_html_e( 'Enable stock management', 'quickstock-for-woocommerce' );
        ?></option>
                        <option value="manage_stock_off"><?php 
        esc_html_e( 'Disable stock management', 'quickstock-for-woocommerce' );
        ?></option>
                        <?php 
        ?>
                        <option value="limit_set"><?php 
        esc_html_e( 'Set low stock threshold …', 'quickstock-for-woocommerce' );
        ?></option>
                        <option value="restock_set"><?php 
        esc_html_e( 'Set restock-to …', 'quickstock-for-woocommerce' );
        ?></option>
                    </optgroup>
                    <optgroup label="<?php 
        esc_attr_e( 'Status', 'quickstock-for-woocommerce' );
        ?>">
                        <option value="stock_status"><?php 
        esc_html_e( 'Set stock status …', 'quickstock-for-woocommerce' );
        ?></option>
                        <option value="backorders"><?php 
        esc_html_e( 'Set backorders …', 'quickstock-for-woocommerce' );
        ?></option>
                        <option value="post_status"><?php 
        esc_html_e( 'Set post status …', 'quickstock-for-woocommerce' );
        ?></option>
                    </optgroup>
                    <?php 
        if ( $tax_options !== '' ) {
            ?>
                    <optgroup label="<?php 
            esc_attr_e( 'Taxonomies', 'quickstock-for-woocommerce' );
            ?>">
                        <?php 
            echo wp_kses( $tax_options, array(
                'option' => array(
                    'value' => true,
                ),
            ) );
            ?>
                    </optgroup>
                    <?php 
        }
        ?>
                    <optgroup label="<?php 
        esc_attr_e( 'Prices', 'quickstock-for-woocommerce' );
        ?>">
                        <option value="sale_clear"><?php 
        esc_html_e( 'Clear sale price', 'quickstock-for-woocommerce' );
        ?></option>
                        <option value="sale_set"><?php 
        esc_html_e( 'Set sale price …', 'quickstock-for-woocommerce' );
        ?></option>
                        <option value="sale_dates"><?php 
        esc_html_e( 'Set sale schedule …', 'quickstock-for-woocommerce' );
        ?></option>
                        <option value="sale_dates_clear"><?php 
        esc_html_e( 'Clear sale schedule', 'quickstock-for-woocommerce' );
        ?></option>
                        <option value="price_set"><?php 
        esc_html_e( 'Set regular price …', 'quickstock-for-woocommerce' );
        ?></option>
                    </optgroup>
                    <?php 
        ?>
                    <?php 
        if ( $cf_options !== '' ) {
            ?>
                    <optgroup label="<?php 
            esc_attr_e( 'Custom fields', 'quickstock-for-woocommerce' );
            ?>">
                        <?php 
            echo wp_kses( $cf_options, array(
                'option' => array(
                    'value' => true,
                ),
            ) );
            ?>
                    </optgroup>
                    <?php 
        }
        ?>
                    <optgroup label="<?php 
        esc_attr_e( 'Export', 'quickstock-for-woocommerce' );
        ?>">
                        <option value="export_csv"><?php 
        esc_html_e( 'Export selection as CSV', 'quickstock-for-woocommerce' );
        ?></option>
                    </optgroup>
                    <optgroup label="<?php 
        esc_attr_e( 'Labels', 'quickstock-for-woocommerce' );
        ?>">
                        <option value="label_print"><?php 
        esc_html_e( 'Print labels …', 'quickstock-for-woocommerce' );
        ?></option>
                    </optgroup>
                    <optgroup label="<?php 
        esc_attr_e( 'Actions', 'quickstock-for-woocommerce' );
        ?>">
                        <option value="trash"><?php 
        esc_html_e( 'Move to trash', 'quickstock-for-woocommerce' );
        ?></option>
                    </optgroup>
                </select>

                <span id="hb-bulk-value"></span>

                <button type="button" class="button button-primary" id="hb-bulk-apply"><?php 
        esc_html_e( 'Apply', 'quickstock-for-woocommerce' );
        ?></button>
                <button type="button" class="button" id="hb-bulk-cancel"><?php 
        esc_html_e( 'Cancel', 'quickstock-for-woocommerce' );
        ?></button>
            </div>
        </div>

        <div id="hb-bulk-modal" aria-hidden="true">
            <div class="hb-bulk-modal-box">
                <p id="hb-bulk-modal-msg"></p>
                <div id="hb-bulk-modal-opts"></div>
                <div id="hb-bulk-progress" style="display:none;">
                    <div class="hb-bulk-pbar"><div class="hb-bulk-pfill"></div></div>
                    <p class="hb-bulk-pinfo"><span id="hb-bulk-pdone">0</span> / <span id="hb-bulk-ptotal">0</span></p>
                </div>
                <div id="hb-bulk-modal-actions">
                    <button type="button" class="button button-primary" id="hb-bulk-confirm"><?php 
        esc_html_e( 'Yes, run', 'quickstock-for-woocommerce' );
        ?></button>
                    <button type="button" class="button" id="hb-bulk-abort"><?php 
        esc_html_e( 'Cancel', 'quickstock-for-woocommerce' );
        ?></button>
                </div>
                <div id="hb-bulk-result" style="display:none;">
                    <p id="hb-bulk-result-msg"></p>
                    <button type="button" class="button button-primary" id="hb-bulk-done"><?php 
        esc_html_e( 'Close & reload', 'quickstock-for-woocommerce' );
        ?></button>
                </div>
            </div>
        </div>

        <form id="hb-bulk-export-form" method="post" target="_blank"
              action="<?php 
        echo esc_url( admin_url( 'admin-ajax.php' ) );
        ?>" style="display:none;">
            <input type="hidden" name="action" value="qsfw_bulk_export" />
            <?php 
        wp_nonce_field( 'qsfw_bulk_export' );
        ?>
            <input type="hidden" name="ids" id="hb-bulk-export-ids" value="" />
        </form>
        <?php 
    }

    private static function render_reorder_bulk_ui() {
        ?>
        <div id="hb-reorder-bulk-bar" aria-hidden="true">
            <div class="hb-bulk-inner">
                <span class="hb-bulk-sel">
                    <strong id="hb-reorder-bulk-count">0</strong>&nbsp;<?php 
        esc_html_e( 'selected', 'quickstock-for-woocommerce' );
        ?>
                </span>
                <select id="hb-reorder-bulk-op">
                    <option value=""><?php 
        esc_html_e( '— Choose action —', 'quickstock-for-woocommerce' );
        ?></option>
                    <optgroup label="<?php 
        esc_attr_e( 'Reorder', 'quickstock-for-woocommerce' );
        ?>">
                        <option value="check_set"><?php 
        esc_html_e( 'Mark as done', 'quickstock-for-woocommerce' );
        ?></option>
                        <option value="check_unset"><?php 
        esc_html_e( 'Unmark as done', 'quickstock-for-woocommerce' );
        ?></option>
                    </optgroup>
                    <?php 
        // Pro-Gate: PO-Aktion nur freigeschaltet anbieten, sonst als deaktivierten Teaser.
        ?>
                    <optgroup label="<?php 
        esc_attr_e( 'Purchase Orders', 'quickstock-for-woocommerce' );
        ?>">
                        <?php 
        if ( !qsfw_is_pro() ) {
            ?>
                        <option value="" disabled>💎 <?php 
            esc_html_e( 'Create purchase order (Pro)', 'quickstock-for-woocommerce' );
            ?></option>
                        <?php 
        }
        ?>
                        <?php 
        ?>
                    </optgroup>
                </select>
                <button type="button" class="button button-primary" id="hb-reorder-bulk-apply"><?php 
        esc_html_e( 'Apply', 'quickstock-for-woocommerce' );
        ?></button>
                <button type="button" class="button" id="hb-reorder-bulk-cancel"><?php 
        esc_html_e( 'Cancel', 'quickstock-for-woocommerce' );
        ?></button>
            </div>
        </div>
        <?php 
    }

    /** Upsell-Panel für den PO-Tab im Free-Plan (statt der Verwaltung). */
    private static function render_po_upsell() {
        QSFW_Renderer::echo_pro_upsell_panel( '📥', __( 'Purchase Orders', 'quickstock-for-woocommerce' ), __( 'Turn your reorder list into purchase orders, send them to suppliers as PDF or CSV, and book goods receipts straight into stock — available in QuickStock Pro.', 'quickstock-for-woocommerce' ) );
    }

}
