<?php
/*
  * @package Softech_Addons_for_Elementor
 */

namespace Softech_Addons_for_Elementor\Widgets;

if ( ! defined( 'ABSPATH' ) ) exit;

if ( ! class_exists( '\\Softech_Addons_for_Elementor\\Includes\\Softech_Post_Query_Manager' ) ) {
    require_once SITS_ELEMENTOR_ADDONS_PLUGIN_PATH . 'includes/class-post-query-manager.php';
}

use Elementor\Controls_Manager;
use Elementor\Group_Control_Typography;
use Elementor\Group_Control_Border;
use Elementor\Group_Control_Box_Shadow;
use Elementor\Group_Control_Background;
use Elementor\Repeater;
use Softech_Addons_for_Elementor\Includes\Softech_Post_Query_Manager;


class sitsel_post_timeline extends \Elementor\Widget_Base {

    private const CACHE_GROUP         = 'sitsel_timeline';
    private const CACHE_EXPIRATION    = HOUR_IN_SECONDS;
    private const EXCLUDED_POST_TYPES = [
        'attachment','elementor_library','wp_block',
        'elementor_snippet','elementor_font','elementor_icons',
        'floating-elements','floating_elements',
    ];

    // ── Identity ──────────────────────────────────────────────────────────
   public function get_name()
    {
        return 'sitsel_post_timeline';
    }
    public function get_title()
    {
        return __('SITS Post Timeline', 'softech-addons-for-elementor');
    }
    public function get_icon(): string      { return 'eicon-time-line'; }
    public function get_categories(): array { return [ 'sits-category' ]; }
    public function get_keywords(): array   { return [ 'timeline','posts','taxonomy','history','vertical','horizontal' ]; }

    // ── Constructor — H-4 FIX: register assets once here, not in get_style_depends ──
    public function __construct( $data = [], $args = null ) {
        parent::__construct( $data, $args );
        $this->register_widget_assets();
    }

    // ── Assets ────────────────────────────────────────────────────────────
    // H-4 FIX: get_style_depends no longer calls register_widget_assets()
    public function get_style_depends(): array  { return [ 'sitsel-timeline-css' ]; }
    public function get_script_depends(): array { return [ 'sitsel-timeline-js' ]; }

    private function register_widget_assets(): void {
        $url = SITS_ELEMENTOR_ADDONS_PLUGIN_URL;
        $ver = SITS_ELEMENTOR_ADDONS_VERSION;
        if ( ! wp_style_is( 'sitsel-timeline-css', 'registered' ) ) {
            wp_register_style( 'sitsel-timeline-css', $url . 'assets/css/sitsel-timeline.css', [], $ver );
        }
        if ( ! wp_script_is( 'sitsel-timeline-js', 'registered' ) ) {
            wp_register_script( 'sitsel-timeline-js', $url . 'assets/js/sitsel-timeline.js', [], $ver, true );
        }
    }

    // ── Helpers ───────────────────────────────────────────────────────────
    private function get_post_types(): array {
        $result = [];
        foreach ( get_post_types( [ 'public' => true ], 'objects' ) as $pt ) {
            if ( ! in_array( $pt->name, self::EXCLUDED_POST_TYPES, true ) ) {
                $result[ $pt->name ] = $pt->label;
            }
        }
        return $result;
    }

    private function get_taxonomies(): array {
        $exclude = [ 'post_format','elementor_library_category','elementor_library_tag' ];
        $result  = [];
        foreach ( get_taxonomies( [ 'public' => true ], 'objects' ) as $tax ) {
            if ( ! in_array( $tax->name, $exclude, true ) ) {
                $result[ $tax->name ] = $tax->label;
            }
        }
        return $result;
    }

    /**
     * C-2 FIX: is_admin() guard prevents frontend execution.
     * Uses set_transient() for cross-request persistence instead of wp_cache_set().
     */
    private function get_posts_for_select(): array {
        if ( ! is_admin() ) return [];

        $key    = 'sitsel_tl_posts_select';
        $cached = get_transient( $key );
        if ( false !== $cached ) return $cached;

        $result = [];
        $posts  = get_posts( [
            'numberposts' => 200,
            'post_type'   => 'any',
            'post_status' => 'publish',
            'fields'      => 'ids',
        ] );
        foreach ( $posts as $id ) {
            $result[ $id ] = get_the_title( $id );
        }
        set_transient( $key, $result, self::CACHE_EXPIRATION );
        return $result;
    }

    /**
     * H-1 FIX: Added is_admin() guard + transient caching.
     */
    private function get_categories_for_select(): array {
        if ( ! is_admin() ) return [];

        $key    = 'sitsel_tl_categories_select';
        $cached = get_transient( $key );
        if ( false !== $cached ) return $cached;

        $result = [];
        foreach ( get_categories( [ 'hide_empty' => false ] ) as $cat ) {
            $result[ $cat->slug ] = $cat->name;
        }
        set_transient( $key, $result, self::CACHE_EXPIRATION );
        return $result;
    }

    /**
     * H-1 FIX: Added is_admin() guard + transient caching.
     */
    private function get_tags_for_select(): array {
        if ( ! is_admin() ) return [];

        $key    = 'sitsel_tl_tags_select';
        $cached = get_transient( $key );
        if ( false !== $cached ) return $cached;

        $result = [];
        foreach ( get_tags( [ 'hide_empty' => false ] ) as $tag ) {
            $result[ $tag->slug ] = $tag->name;
        }
        set_transient( $key, $result, self::CACHE_EXPIRATION );
        return $result;
    }

    // ── Controls ──────────────────────────────────────────────────────────
    protected function register_controls(): void {
        $this->register_content_tab();
        $this->register_style_tab();
    }

    private function register_content_tab(): void {
     
        $this->start_controls_section('content_section', [
    'label' => esc_html__( 'Timeline Content', 'softech-addons-for-elementor' ),
]);

    $this->add_control('content_source_type', [
        'label'   => esc_html__( 'Content Source', 'softech-addons-for-elementor' ),
        'type'    => Controls_Manager::SELECT,
        'default' => 'manual',
        'options' => [
            'manual'  => esc_html__( 'Manual Input', 'softech-addons-for-elementor' ),
            'dynamic' => esc_html__( 'Dynamic', 'softech-addons-for-elementor' ),
        ],
    ]);

    $this->end_controls_section();
        Softech_Post_Query_Manager::register_controls( $this, [
    'source_control' => 'content_source_type', // only show when dynamic mode is selected
    'section_id'     => 'section_query',
    'section_label'  => esc_html__( 'Query', 'softech-addons-for-elementor' ),
    'mode'           => 'full',
] );

        $this->register_layout_section();
        $this->register_card_elements_section();
    }


    private function register_layout_section(): void {
        $this->start_controls_section( 'section_layout', [
            'label' => esc_html__( 'Layout & Style', 'softech-addons-for-elementor' ),
            'tab'   => Controls_Manager::TAB_CONTENT,
        ] );

        $this->add_control( 'timeline_style', [
            'label'   => esc_html__( 'Timeline Style', 'softech-addons-for-elementor' ),
            'type'    => Controls_Manager::SELECT,
            'default' => 'zig-zag',
            'options' => [
                'zig-zag'           => esc_html__( 'Zig-Zag', 'softech-addons-for-elementor' ),
                'line-left'         => esc_html__( 'Line Left', 'softech-addons-for-elementor' ),
                'line-right'        => esc_html__( 'Line Right', 'softech-addons-for-elementor' ),
                'line-top-carousel' => esc_html__( 'Line Top – Carousel', 'softech-addons-for-elementor' ),
            ],
        ] );

        $this->add_control( 'image_resolution', [
            'label'   => esc_html__( 'Image Resolution', 'softech-addons-for-elementor' ),
            'type'    => Controls_Manager::SELECT,
            'default' => 'large',
            'options' => [
                'thumbnail' => esc_html__( 'Thumbnail', 'softech-addons-for-elementor' ),
                'medium'    => esc_html__( 'Medium', 'softech-addons-for-elementor' ),
                'large'     => esc_html__( 'Large', 'softech-addons-for-elementor' ),
                'full'      => esc_html__( 'Full', 'softech-addons-for-elementor' ),
            ],
        ] );

        $this->add_control( 'show_extra_label', [
            'label'        => esc_html__( 'Show Extra Label', 'softech-addons-for-elementor' ),
            'type'         => Controls_Manager::SWITCHER,
            'return_value' => 'yes', 'default' => 'yes',
        ] );

        $this->add_control( 'extra_label_source', [
            'label'     => esc_html__( 'Extra Label Source', 'softech-addons-for-elementor' ),
            'type'      => Controls_Manager::SELECT,
            'default'   => 'publish_date',
            'options'   => [
                'publish_date'  => esc_html__( 'Publish Date', 'softech-addons-for-elementor' ),
                'modified_date' => esc_html__( 'Modified Date', 'softech-addons-for-elementor' ),
                'custom_field'  => esc_html__( 'Custom Field', 'softech-addons-for-elementor' ),
            ],
            'condition' => [ 'show_extra_label' => 'yes' ],
        ] );

        $this->add_control( 'date_format', [
            'label'   => esc_html__( 'Date Format', 'softech-addons-for-elementor' ),
            'type'    => Controls_Manager::SELECT,
            'default' => 'F j, Y',
            'options' => [
                'F j, Y' => wp_date( 'F j, Y' ),
                'd/m/Y'  => wp_date( 'd/m/Y' ),
                'm/d/Y'  => wp_date( 'm/d/Y' ),
                'Y-m-d'  => wp_date( 'Y-m-d' ),
                'M j, Y' => wp_date( 'M j, Y' ),
                'custom' => esc_html__( 'Custom', 'softech-addons-for-elementor' ),
            ],
            'condition' => [ 'extra_label_source' => [ 'publish_date', 'modified_date' ] ],
        ] );

        $this->add_control( 'date_format_custom', [
            'label'       => esc_html__( 'Custom Date Format', 'softech-addons-for-elementor' ),
            'type'        => Controls_Manager::TEXT,
            'default'     => 'd M Y',
            'placeholder' => 'd M Y',
            'condition'   => [ 'date_format' => 'custom' ],
        ] );

        $this->add_control( 'extra_label_custom_key', [
            'label'       => esc_html__( 'Custom Field Key', 'softech-addons-for-elementor' ),
            'type'        => Controls_Manager::TEXT,
            'placeholder' => 'my_meta_key',
            'condition'   => [ 'show_extra_label' => 'yes', 'extra_label_source' => 'custom_field' ],
        ] );

        $this->add_control( 'entrance_animation', [
            'label'   => esc_html__( 'Entrance Animation', 'softech-addons-for-elementor' ),
            'type'    => Controls_Manager::SELECT,
            'default' => 'fade',
            'options' => [
                'none'        => esc_html__( 'None', 'softech-addons-for-elementor' ),
                'fade'        => esc_html__( 'Fade', 'softech-addons-for-elementor' ),
                'slide-up'    => esc_html__( 'Slide Up', 'softech-addons-for-elementor' ),
                'slide-left'  => esc_html__( 'Slide Left', 'softech-addons-for-elementor' ),
                'slide-right' => esc_html__( 'Slide Right', 'softech-addons-for-elementor' ),
                'zoom'        => esc_html__( 'Zoom', 'softech-addons-for-elementor' ),
            ],
            'condition' => [
                'timeline_style!'      => 'line-top-carousel', 
            ],
        ] );

        $this->add_control( 'animation_offset', [
            'label'     => esc_html__( 'Animation Offset', 'softech-addons-for-elementor' ),
            'type'      => Controls_Manager::NUMBER,
            'default'   => 150, 'min' => 0, 'max' => 500,
            'condition' => [ 'entrance_animation!' => 'none' ,'timeline_style!'      => 'line-top-carousel'],
        ] );

        $this->add_control( 'animation_duration', [
            'label'     => esc_html__( 'Animation Duration (ms)', 'softech-addons-for-elementor' ),
            'type'      => Controls_Manager::NUMBER,
            'default'   => 600, 'min' => 100, 'max' => 3000, 'step' => 100,
            'condition' => [ 'entrance_animation!' => 'none' ],
        ] );

        $this->add_control( 'date_grouping', [
            'label'   => esc_html__( 'Group by Date', 'softech-addons-for-elementor' ),
            'type'    => Controls_Manager::SELECT,
            'default' => 'none',
            'options' => [
                'none'  => esc_html__( 'None', 'softech-addons-for-elementor' ),
                'year'  => esc_html__( 'Year', 'softech-addons-for-elementor' ),
                'month' => esc_html__( 'Month & Year', 'softech-addons-for-elementor' ),
            ],
           'condition' => [
                'choose_template_type' => Softech_Post_Query_Manager::get_post_type_keys(),
                'timeline_style!'      => 'line-top-carousel', 
            ],
        ] );

        $this->add_control( 'show_connector_line', [
            'label'        => esc_html__( 'Show Connector Line', 'softech-addons-for-elementor' ),
            'type'         => Controls_Manager::SWITCHER,
            'return_value' => 'yes', 'default' => 'yes',
        ] );

        $this->add_control( 'show_dot', [
            'label'        => esc_html__( 'Show Dot / Icon on Line', 'softech-addons-for-elementor' ),
            'type'         => Controls_Manager::SWITCHER,
            'return_value' => 'yes', 'default' => 'yes',
        ] );

        $this->add_control( 'dot_icon', [
            'label'     => esc_html__( 'Dot Icon', 'softech-addons-for-elementor' ),
            'type'      => Controls_Manager::ICONS,
            'default'   => [ 'value' => 'fas fa-clock', 'library' => 'fa-solid' ],
            'condition' => [ 'show_dot' => 'yes' ],
        ] );

        $this->end_controls_section();
    }

    private function register_card_elements_section(): void {
        $this->start_controls_section( 'section_card_elements', [
            'label' => esc_html__( 'Card Elements', 'softech-addons-for-elementor' ),
            'tab'   => Controls_Manager::TAB_CONTENT,
        ] );

        // Custom repeater
        $custom_repeater = new Repeater();
        $custom_repeater->add_control( 'item_title',       [ 'label' => esc_html__( 'Title', 'softech-addons-for-elementor' ),       'type' => Controls_Manager::TEXT,    'default' => esc_html__( 'Timeline Item', 'softech-addons-for-elementor' ), 'label_block' => true ] );
        $custom_repeater->add_control( 'item_date',        [ 'label' => esc_html__( 'Date / Label', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::TEXT,    'default' => wp_date( 'F j, Y' ) ] );
        $custom_repeater->add_control( 'item_description', [ 'label' => esc_html__( 'Description', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::TEXTAREA,'rows' => 4, 'default' => '' ] );
        $custom_repeater->add_control( 'item_image',       [ 'label' => esc_html__( 'Image', 'softech-addons-for-elementor' ),       'type' => Controls_Manager::MEDIA,   'default' => [ 'url' => '' ] ] );
        $custom_repeater->add_control( 'item_icon',        [ 'label' => esc_html__( 'Icon', 'softech-addons-for-elementor' ),        'type' => Controls_Manager::ICONS,   'default' => [ 'value' => 'fas fa-star', 'library' => 'fa-solid' ] ] );
        $custom_repeater->add_control( 'item_link',        [ 'label' => esc_html__( 'Link', 'softech-addons-for-elementor' ),        'type' => Controls_Manager::URL,     'placeholder' => 'https://', 'show_external' => true ] );
        $custom_repeater->add_control( 'item_btn_label',   [ 'label' => esc_html__( 'Button Label', 'softech-addons-for-elementor' ),'type' => Controls_Manager::TEXT,    'default' => esc_html__( 'Read More', 'softech-addons-for-elementor' ) ] );

        $this->add_control( 'custom_timeline_items', [
            'label'       => esc_html__( 'Timeline Items', 'softech-addons-for-elementor' ),
            'type'        => Controls_Manager::REPEATER,
            'fields'      => $custom_repeater->get_controls(),
            'title_field' => '{{{ item_title }}}',
            'default'     => [
                 [ 'item_title' => 'First Event',  'item_date' => '2024'  , 'item_description' => 'First event description' ],
                [ 'item_title' => 'Second Event', 'item_date' => '2025' , 'item_description' => 'Second event description' ],
                [ 'item_title' => 'Third Event',  'item_date' => '2026' , 'item_description' => 'Third event description' ],
            ],
            'condition' => [ 'content_source_type' => 'manual' ],
        ] );

        // Post card repeater
        $post_repeater = new Repeater();
        $post_repeater->add_control( 'element_type', [
            'label'   => esc_html__( 'Element', 'softech-addons-for-elementor' ),
            'type'    => Controls_Manager::SELECT,
            'default' => 'title',
            'options' => [
                'title'        => esc_html__( 'Title', 'softech-addons-for-elementor' ),
                'date'         => esc_html__( 'Date', 'softech-addons-for-elementor' ),
                'excerpt'      => esc_html__( 'Excerpt', 'softech-addons-for-elementor' ),
                'thumbnail'    => esc_html__( 'Featured Image', 'softech-addons-for-elementor' ),
                'author'       => esc_html__( 'Author', 'softech-addons-for-elementor' ),
                'category'     => esc_html__( 'Categories', 'softech-addons-for-elementor' ),
                'tags'         => esc_html__( 'Tags', 'softech-addons-for-elementor' ),
                'read_more'    => esc_html__( 'Read More Button', 'softech-addons-for-elementor' ),
                'custom_field' => esc_html__( 'Custom Field (Meta)', 'softech-addons-for-elementor' ),
            ],
        ] );
        $post_repeater->add_control( 'title_tag',        [ 'label' => esc_html__( 'HTML Tag', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'h3', 'options' => [ 'h1'=>'H1','h2'=>'H2','h3'=>'H3','h4'=>'H4','h5'=>'H5','h6'=>'H6','div'=>'div','span'=>'span','p'=>'p' ], 'condition' => [ 'element_type' => 'title' ] ] );
        $post_repeater->add_control( 'read_more_text',   [ 'label' => esc_html__( 'Button Label', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::TEXT, 'default' => esc_html__( 'Read More', 'softech-addons-for-elementor' ), 'condition' => [ 'element_type' => 'read_more' ] ] );
        $post_repeater->add_control( 'custom_field_key', [ 'label' => esc_html__( 'Meta Key', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::TEXT, 'label_block' => true, 'placeholder' => 'my_custom_field', 'condition' => [ 'element_type' => 'custom_field' ] ] );
        $post_repeater->add_control( 'excerpt_words',    [ 'label' => esc_html__( 'Excerpt Word Limit', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::NUMBER, 'default' => 20, 'min' => 1, 'max' => 200, 'condition' => [ 'element_type' => 'excerpt' ] ] );

        $this->add_control( 'post_card_elements', [
            'label'       => esc_html__( 'Post Card Elements', 'softech-addons-for-elementor' ),
            'type'        => Controls_Manager::REPEATER,
            'fields'      => $post_repeater->get_controls(),
            'title_field' => '{{{ element_type }}}',
            'default'     => [
                [ 'element_type' => 'thumbnail' ],
                [ 'element_type' => 'title' ],
                [ 'element_type' => 'excerpt' ],
                [ 'element_type' => 'read_more' ],
            ],
           'condition' => [
                'content_source_type' => 'dynamic',
                'choose_template_type' => Softech_Post_Query_Manager::get_post_type_keys(),
            ],
        ] );

        // Taxonomy card repeater
        $tax_repeater = new Repeater();
        $tax_repeater->add_control( 'element_type', [
            'label'   => esc_html__( 'Element', 'softech-addons-for-elementor' ),
            'type'    => Controls_Manager::SELECT,
            'default' => 'term_name',
            'options' => [
                'term_name'        => esc_html__( 'Term Name', 'softech-addons-for-elementor' ),
                'term_description' => esc_html__( 'Term Description', 'softech-addons-for-elementor' ),
                'term_count'       => esc_html__( 'Post Count', 'softech-addons-for-elementor' ),
                'term_link'        => esc_html__( 'Term Link Button', 'softech-addons-for-elementor' ),
                'acf_field'        => esc_html__( 'ACF Field', 'softech-addons-for-elementor' ),
            ],
        ] );
        $tax_repeater->add_control( 'term_name_tag',  [ 'label' => esc_html__( 'HTML Tag', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::SELECT, 'default' => 'h3', 'options' => [ 'h2'=>'H2','h3'=>'H3','h4'=>'H4','h5'=>'H5','div'=>'div','span'=>'span' ], 'condition' => [ 'element_type' => 'term_name' ] ] );
        $tax_repeater->add_control( 'term_link_label',[ 'label' => esc_html__( 'Button Label', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::TEXT, 'default' => esc_html__( 'View All', 'softech-addons-for-elementor' ), 'condition' => [ 'element_type' => 'term_link' ] ] );
        $tax_repeater->add_control( 'acf_field_key',  [ 'label' => esc_html__( 'ACF Field Key / Name', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::TEXT, 'label_block' => true, 'placeholder' => 'term_image', 'condition' => [ 'element_type' => 'acf_field' ] ] );
        $tax_repeater->add_control( 'desc_words',     [ 'label' => esc_html__( 'Description Word Limit', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::NUMBER, 'default' => 20, 'min' => 1, 'max' => 200, 'condition' => [ 'element_type' => 'term_description' ] ] );

        $this->add_control( 'tax_card_elements', [
            'label'       => esc_html__( 'Taxonomy Card Elements', 'softech-addons-for-elementor' ),
            'type'        => Controls_Manager::REPEATER,
            'fields'      => $tax_repeater->get_controls(),
            'title_field' => '{{{ element_type }}}',
            'default'     => [
                [ 'element_type' => 'term_name' ],
                [ 'element_type' => 'term_description' ],
                [ 'element_type' => 'term_count' ],
                [ 'element_type' => 'term_link' ],
            ],
           'condition' => [
                'content_source_type' => 'dynamic',
                'choose_template_type' => Softech_Post_Query_Manager::get_taxonomy_type_keys(),
            ],
        ] );

        $this->end_controls_section();
    }

    // ── Style Tab ─────────────────────────────────────────────────────────
    private function register_style_tab(): void {
        $this->register_line_style_section();
        $this->register_dot_style_section();
        $this->register_date_label_style_section();
        $this->register_card_style_section();
        $this->register_title_style_section();
        $this->register_excerpt_style_section();
        $this->register_thumbnail_style_section();
        $this->register_meta_style_section();
        $this->register_button_style_section();
        $this->register_pagination_style_section();
    }

    private function register_line_style_section(): void {
        $this->start_controls_section( 'style_line', [
            'label'     => esc_html__( 'Connector Line', 'softech-addons-for-elementor' ),
            'tab'       => Controls_Manager::TAB_STYLE,
            'condition' => [ 'show_connector_line' => 'yes' ],
        ] );
        $this->add_control( 'line_color',      [ 'label' => esc_html__( 'Line Color', 'softech-addons-for-elementor' ),      'type' => Controls_Manager::COLOR, 'default' => '#cccccc', 'selectors' => [ '{{WRAPPER}} .sitsel-tl-line' => 'background-color: {{VALUE}};',  ] ] );
        $this->add_control( 'line_fill_color', [ 'label' => esc_html__( 'Line Fill Color', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::COLOR, 'default' => '#0f3d4a', 'selectors' => [
            '{{WRAPPER}} .sitsel-tl-line::before'      => 'background-color: {{VALUE}};',
            // custom property for use elsewhere (dots, borders etc.)
           
            '{{WRAPPER}} .sitsel-change-border-color' => 'border-color: {{VALUE}};',
        ] ] );

        // Optional speed control; lower values cause the coloured portion to
        // progress more slowly (0.5 = half speed, 1 = same behaviour as Royal
        // Addons).  The value is output as a data-* attribute so the frontend
        // script can read it without additional inline styles.
        $this->add_control( 'line_fill_speed', [
            'label' => esc_html__( 'Fill Speed', 'softech-addons-for-elementor' ),
            'type'  => Controls_Manager::SLIDER,
            // default behaviour mirrors Royal Addons exactly
            'default' => [ 'size' => 1.0 ],
            'range' => [ 'px' => [ 'min' => 0.1, 'max' => 2, 'step' => 0.05 ] ],
            'selectors' => [], // no CSS; used only in render()
            'description' => esc_html__( 'Lower values slow the line animation.', 'softech-addons-for-elementor' ),
        ] );
        $this->add_responsive_control( 'line_width', [ 'label' => esc_html__( 'Line Width (px)', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::SLIDER, 'default' => [ 'size' => 5, 'unit' => 'px' ], 'range' => [ 'px' => [ 'min' => 1, 'max' => 20 ] ], 'selectors' => [ '{{WRAPPER}} .sitsel-tl-line' => 'width: {{SIZE}}{{UNIT}};', '{{WRAPPER}} .sitsel-tl-horizontal .sitsel-tl-line' => 'height: {{SIZE}}{{UNIT}}; width: 100%;', '{{WRAPPER}}' => '--tl-line-width: {{SIZE}}{{UNIT}};' ] ] );
        $this->end_controls_section();
    }

    private function register_dot_style_section(): void {
        $this->start_controls_section( 'style_dot', [
            'label'     => esc_html__( 'Dot / Icon', 'softech-addons-for-elementor' ),
            'tab'       => Controls_Manager::TAB_STYLE,
            'condition' => [ 'show_dot' => 'yes' ],
        ] );
        $this->add_responsive_control( 'dot_box_width', [ 'label' => esc_html__( 'Dot Width', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::SLIDER, 'default' => [ 'size' => 35, 'unit' => 'px' ], 'range' => [ 'px' => [ 'min' => 8, 'max' => 100 ] ], 'selectors' => [ '{{WRAPPER}} .sitsel-tline-dot' => 'width: {{SIZE}}{{UNIT}}; height: {{SIZE}}{{UNIT}};', '{{WRAPPER}}' => '--tl-dot-size: {{SIZE}}{{UNIT}};' ] ] );
        $this->add_responsive_control( 'dot_size',      [ 'label' => esc_html__( 'Icon Size', 'softech-addons-for-elementor' ),  'type' => Controls_Manager::SLIDER, 'default' => [ 'size' => 18, 'unit' => 'px' ], 'range' => [ 'px' => [ 'min' => 8, 'max' => 60 ] ],  'selectors' => [ '{{WRAPPER}} .sitsel-tline-dot svg' => 'width: {{SIZE}}{{UNIT}}; height: {{SIZE}}{{UNIT}};' ] ] );
        $this->add_control( 'dot_color',          [ 'label' => esc_html__( 'Icon / Dot Color', 'softech-addons-for-elementor' ),    'type' => Controls_Manager::COLOR, 'default' => '#ffffff', 'selectors' => [ '{{WRAPPER}} .sitsel-tline-dot' => 'color: {{VALUE}};', '{{WRAPPER}} .sitsel-tline-dot svg' => 'fill: {{VALUE}}; stroke: {{VALUE}};' ] ] );
        $this->add_control( 'dot_active_color',   [ 'label' => esc_html__( 'Active Icon / Dot Color', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::COLOR, 'default' => '#0f3d4a', 'selectors' => [ '{{WRAPPER}} .sitsel-tline-dot--active' => 'color: {{VALUE}};', '{{WRAPPER}} .sitsel-tline-dot--active svg' => 'fill: {{VALUE}}; stroke: {{VALUE}};' ] ] );
        $this->add_control( 'dot_bg_color',        [ 'label' => esc_html__( 'Dot Background', 'softech-addons-for-elementor' ),       'type' => Controls_Manager::COLOR, 'default' => '#0f3d4a', 'selectors' => [ '{{WRAPPER}} .sitsel-tline-dot' => 'background-color: {{VALUE}};' ] ] );
        $this->add_control( 'dot_active_bg_color', [ 'label' => esc_html__( 'Active Dot Background', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::COLOR, 'default' => '#fff',    'selectors' => [ '{{WRAPPER}} .sitsel-tline-dot--active' => 'background-color: {{VALUE}};' ] ] );
        $this->add_responsive_control( 'dot_border_radius', [ 'label' => esc_html__( 'Border Radius', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'default' => [ 'top' => '50', 'right' => '50', 'bottom' => '50', 'left' => '50', 'unit' => '%' ], 'size_units' => [ 'px', '%' ], 'selectors' => [ '{{WRAPPER}} .sitsel-tline-dot' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};' ] ] );
        $this->add_group_control( Group_Control_Box_Shadow::get_type(), [ 'name' => 'dot_shadow', 'selector' => '{{WRAPPER}} .sitsel-tline-dot' ] );
        $this->end_controls_section();
    }

    private function register_date_label_style_section(): void {
        $this->start_controls_section( 'style_date_label', [ 'label' => esc_html__( 'Date Label', 'softech-addons-for-elementor' ), 'tab' => Controls_Manager::TAB_STYLE ] );
        $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'date_label_typography', 'selector' => '{{WRAPPER}} .sitsel-tl-extra-label' ] );
        $this->add_control( 'date_label_color',   [ 'label' => esc_html__( 'Color', 'softech-addons-for-elementor' ),      'type' => Controls_Manager::COLOR, 'default' => '#0f3d4a',             'selectors' => [ '{{WRAPPER}} .sitsel-tl-extra-label' => 'color: {{VALUE}};' ] ] );
        $this->add_control( 'date_label_bg',      [ 'label' => esc_html__( 'Background', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::COLOR, 'default' => 'rgba(0,115,170,0.08)', 'selectors' => [ '{{WRAPPER}} .sitsel-tl-extra-label' => 'background-color: {{VALUE}};' ] ] );
        $this->add_responsive_control( 'date_label_padding', [ 'label' => esc_html__( 'Padding', 'softech-addons-for-elementor' ),       'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px','em','%' ], 'default' => [ 'top'=>'4','right'=>'12','bottom'=>'4','left'=>'12','unit'=>'px' ], 'selectors' => [ '{{WRAPPER}} .sitsel-tl-extra-label' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};' ] ] );
        $this->add_responsive_control( 'date_label_radius',  [ 'label' => esc_html__( 'Border Radius', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px','%' ],       'default' => [ 'top'=>'20','right'=>'20','bottom'=>'20','left'=>'20','unit'=>'px' ], 'selectors' => [ '{{WRAPPER}} .sitsel-tl-extra-label' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};' ] ] );
        $this->end_controls_section();
    }

    private function register_card_style_section(): void {
        $this->start_controls_section( 'style_card', [ 'label' => esc_html__( 'Card', 'softech-addons-for-elementor' ), 'tab' => Controls_Manager::TAB_STYLE ] );
        $this->add_group_control( Group_Control_Background::get_type(), [ 'name' => 'card_background', 'types' => [ 'classic','gradient' ], 'fields_options' => ['background' => [ 'default' => 'classic',], 'color' => [ 'default' => '#ffffff', ],],'selector' => '{{WRAPPER}} .sitsel-tl-card' ] );
        $this->add_group_control(
        Group_Control_Border::get_type(),
        [
            'name'     => 'card_border',
            'selector' => '{{WRAPPER}} .sitsel-tl-card',
            'fields_options' => [
                'border' => [
                    'default' => 'solid',
                ],
                'width' => [
                    'default' => [
                        'top'    => '1',
                        'right'  => '1',
                        'bottom' => '1',
                        'left'   => '1',
                        'unit'   => 'px',
                    ],
                ],
                'color' => [
                    'default' => '#cccccc',
                ],
            ],
        ]
    );
        
        $this->add_responsive_control( 'card_radius',  [ 'label' => esc_html__( 'Border Radius', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px','%' ], 'default' => [ 'top'=>'8','right'=>'8','bottom'=>'8','left'=>'8','unit'=>'px' ], 'selectors' => [ '{{WRAPPER}} .sitsel-tl-card' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', '{{WRAPPER}}' => '--tl-card-radius: {{TOP}}{{UNIT}};' ] ] );
        $this->add_group_control( Group_Control_Box_Shadow::get_type(), [ 'name' => 'card_shadow',     'selector' => '{{WRAPPER}} .sitsel-tl-card' ] );
        $this->add_responsive_control( 'card_padding', [ 'label' => esc_html__( 'Padding', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px','em','%' ], 'default' => [ 'top'=>'20','right'=>'20','bottom'=>'20','left'=>'20','unit'=>'px' ], 'selectors' => [ '{{WRAPPER}} .sitsel-tl-card' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', '{{WRAPPER}}' => '--tl-card-pad: {{TOP}}{{UNIT}};' ] ] );
        $this->add_responsive_control( 'card_margin',  [ 'label' => esc_html__( 'Margin', 'softech-addons-for-elementor' ),  'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px','em','%' ], 'selectors' => [ '{{WRAPPER}} .sitsel-tl-card' => 'margin: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};' ] ] );
        $this->end_controls_section();
    }

    private function register_title_style_section(): void {
        $this->start_controls_section( 'style_title', [ 'label' => esc_html__( 'Title', 'softech-addons-for-elementor' ), 'tab' => Controls_Manager::TAB_STYLE ] );
        $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'title_typography', 'selector' => '{{WRAPPER}} .sitsel-tl-title a, {{WRAPPER}} .sitsel-tl-title' ] );
        $this->start_controls_tabs( 'tabs_title_color' );
            $this->start_controls_tab( 'tab_title_normal', [ 'label' => esc_html__( 'Normal', 'softech-addons-for-elementor' ) ] );
            $this->add_control( 'title_color',       [ 'label' => esc_html__( 'Color', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .sitsel-tl-title a'       => 'color: {{VALUE}};' ] ] );
            $this->end_controls_tab();
            $this->start_controls_tab( 'tab_title_hover', [ 'label' => esc_html__( 'Hover', 'softech-addons-for-elementor' ) ] );
            $this->add_control( 'title_hover_color', [ 'label' => esc_html__( 'Color', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .sitsel-tl-title a:hover' => 'color: {{VALUE}};' ] ] );
            $this->end_controls_tab();
        $this->end_controls_tabs();
        $this->end_controls_section();
    }

    private function register_excerpt_style_section(): void {
        $this->start_controls_section( 'style_excerpt', [ 'label' => esc_html__( 'Excerpt / Description', 'softech-addons-for-elementor' ), 'tab' => Controls_Manager::TAB_STYLE ] );
        $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'excerpt_typography', 'selector' => '{{WRAPPER}} .sitsel-tl-excerpt' ] );
        $this->add_control( 'excerpt_color', [ 'label' => esc_html__( 'Color', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .sitsel-tl-excerpt' => 'color: {{VALUE}};' ] ] );
        $this->end_controls_section();
    }

    private function register_thumbnail_style_section(): void {
        $this->start_controls_section( 'style_thumbnail', [ 'label' => esc_html__( 'Featured Image', 'softech-addons-for-elementor' ), 'tab' => Controls_Manager::TAB_STYLE ] );
        $this->add_responsive_control( 'thumbnail_height', [ 'label' => esc_html__( 'Height', 'softech-addons-for-elementor' ),        'type' => Controls_Manager::SLIDER, 'size_units' => [ 'px','vh' ], 'default' => [ 'size' => 200, 'unit' => 'px' ], 'range' => [ 'px' => [ 'max' => 600 ] ], 'selectors' => [ '{{WRAPPER}} .sitsel-tl-thumbnail img' => 'height: {{SIZE}}{{UNIT}}; object-fit: cover;' ] ] );
        $this->add_responsive_control( 'thumbnail_radius',  [ 'label' => esc_html__( 'Border Radius', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px','%' ], 'selectors' => [ '{{WRAPPER}} .sitsel-tl-thumbnail img' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};' ] ] );
        $this->end_controls_section();
    }

    private function register_meta_style_section(): void {
        $this->start_controls_section( 'style_meta', [ 'label' => esc_html__( 'Meta (Date / Author / Cat / Tags)', 'softech-addons-for-elementor' ), 'tab' => Controls_Manager::TAB_STYLE ] );
        $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'meta_typography', 'selector' => '{{WRAPPER}} .sitsel-tl-meta' ] );
        $this->add_control( 'meta_color', [ 'label' => esc_html__( 'Color', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .sitsel-tl-meta' => 'color: {{VALUE}};' ] ] );
        $this->end_controls_section();
    }

    private function register_button_style_section(): void {
        $this->start_controls_section( 'style_button', [ 'label' => esc_html__( 'Button', 'softech-addons-for-elementor' ), 'tab' => Controls_Manager::TAB_STYLE ] );
        $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'button_typography', 'selector' => '{{WRAPPER}} .sitsel-tl-btn' ] );
        $this->start_controls_tabs( 'tabs_btn' );
            $this->start_controls_tab( 'tab_btn_normal', [ 'label' => esc_html__( 'Normal', 'softech-addons-for-elementor' ) ] );
            $this->add_control( 'btn_text_color',   [ 'label' => esc_html__( 'Text Color', 'softech-addons-for-elementor' ),   'type' => Controls_Manager::COLOR, 'default' => '#fff',    'selectors' => [ '{{WRAPPER}} .sitsel-tl-btn'       => 'color: {{VALUE}};' ] ] );
            $this->add_control( 'btn_bg_color',     [ 'label' => esc_html__( 'Background', 'softech-addons-for-elementor' ),   'type' => Controls_Manager::COLOR, 'default' => '#0f3d4a', 'selectors' => [ '{{WRAPPER}} .sitsel-tl-btn'       => 'background-color: {{VALUE}};' ] ] );
            $this->add_control( 'btn_border_color', [ 'label' => esc_html__( 'Border Color', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::COLOR, 'default' => '#0f3d4a', 'selectors' => [ '{{WRAPPER}} .sitsel-tl-btn'       => 'border-color: {{VALUE}};' ] ] );
            $this->end_controls_tab();
            $this->start_controls_tab( 'tab_btn_hover', [ 'label' => esc_html__( 'Hover', 'softech-addons-for-elementor' ) ] );
            $this->add_control( 'btn_text_hover_color',   [ 'label' => esc_html__( 'Text Color', 'softech-addons-for-elementor' ),   'type' => Controls_Manager::COLOR, 'default' => '#0f3d4a', 'selectors' => [ '{{WRAPPER}} .sitsel-tl-btn:hover' => 'color: {{VALUE}};' ] ] );
            $this->add_control( 'btn_bg_hover_color',     [ 'label' => esc_html__( 'Background', 'softech-addons-for-elementor' ),   'type' => Controls_Manager::COLOR, 'default' => '#fff',    'selectors' => [ '{{WRAPPER}} .sitsel-tl-btn:hover' => 'background-color: {{VALUE}};' ] ] );
            $this->add_control( 'btn_border_hover_color', [ 'label' => esc_html__( 'Border Color', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::COLOR, 'default' => '#0f3d4a', 'selectors' => [ '{{WRAPPER}} .sitsel-tl-btn:hover' => 'border-color: {{VALUE}};' ] ] );
            $this->add_control( 'btn_hover_transition',   [ 'label' => esc_html__( 'Transition Duration (s)', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::SLIDER, 'default' => [ 'size' => 0.3 ], 'range' => [ 'px' => [ 'min' => 0.1, 'max' => 3, 'step' => 0.1 ] ], 'selectors' => [ '{{WRAPPER}} .sitsel-tl-btn:hover' => '--tl-btn-hover-transition: {{SIZE}}s;' ] ] );
            $this->end_controls_tab();
        $this->end_controls_tabs();
        $this->add_group_control( Group_Control_Border::get_type(), [
            'name'           => 'btn_border',
            'selector'       => '{{WRAPPER}} .sitsel-tl-btn',
            'fields_options' => [
                'border' => [ 'default' => 'solid' ],
                'width'  => [ 'default' => [ 'top' => '1', 'right' => '1', 'bottom' => '1', 'left' => '1' ] ],
                'color'  => [ 'default' => '#0f3d4a' ],
            ],
        ] );
        $this->add_responsive_control( 'btn_radius',  [ 'label' => esc_html__( 'Border Radius', 'softech-addons-for-elementor' ), 'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px','%' ], 'selectors' => [ '{{WRAPPER}} .sitsel-tl-btn' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};' ] ] );
        $this->add_responsive_control( 'btn_padding', [ 'label' => esc_html__( 'Padding', 'softech-addons-for-elementor' ),       'type' => Controls_Manager::DIMENSIONS, 'size_units' => [ 'px','em' ], 'default' => [ 'top'=>'8','right'=>'20','bottom'=>'8','left'=>'20','unit'=>'px' ], 'selectors' => [ '{{WRAPPER}} .sitsel-tl-btn' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};' ] ] );
        $this->end_controls_section();
    }

    private function register_pagination_style_section(): void {
        $this->start_controls_section( 'style_pagination', [
            'label'     => esc_html__( 'Pagination', 'softech-addons-for-elementor' ),
            'tab'       => Controls_Manager::TAB_STYLE,
            'condition' => [ 'show_pagination' => 'yes' ],
        ] );
        $this->add_group_control( Group_Control_Typography::get_type(), [ 'name' => 'pagination_typography', 'selector' => '{{WRAPPER}} .sitsel-tl-pagination a.page-numbers' ] );
        $this->add_control( 'pagination_color',        [ 'label' => esc_html__( 'Color', 'softech-addons-for-elementor' ),           'type' => Controls_Manager::COLOR, 'selectors' => [ '{{WRAPPER}} .sitsel-tl-pagination .page-numbers'         => 'color: {{VALUE}};' ] ] );
        $this->add_control( 'pagination_active_bg',    [ 'label' => esc_html__( 'Active Background', 'softech-addons-for-elementor' ),'type' => Controls_Manager::COLOR, 'default' => '#0f3d4a' , 'selectors' => [ '{{WRAPPER}} .sitsel-tl-pagination .page-numbers.current' => 'background-color: {{VALUE}};' ] ] );
        $this->add_control( 'pagination_active_color', [ 'label' => esc_html__( 'Active Color', 'softech-addons-for-elementor' ),     'type' => Controls_Manager::COLOR, 'default' => '#fff' , 'selectors' => [ '{{WRAPPER}} .sitsel-tl-pagination .page-numbers.current' => 'color: {{VALUE}};' ] ] );
        $this->end_controls_section();
    }

    // ── Render ────────────────────────────────────────────────────────────
    protected function render(): void {
        $settings = $this->get_settings_for_display();
        $template_type = $settings['choose_template_type'] ?? 'post';
        $is_taxonomy   = in_array( $template_type, Softech_Post_Query_Manager::get_taxonomy_type_keys(), true );
        // determine source; manual input should render custom timeline
        $source = $is_taxonomy ? 'taxonomy' : ( $settings['content_source_type'] ?? 'dynamic' );
        $style    = sanitize_html_class( $settings['timeline_style'] ?? 'zig-zag' );

        $classes = implode( ' ', array_filter( [
            'sitsel-tl-wrapper',
            'sitsel-tl-' . $style,
            ( 'yes' === ( $settings['show_connector_line'] ?? '' ) ) ? 'has-line' : '',
            ( 'yes' === ( $settings['show_dot']            ?? '' ) ) ? 'has-dot'  : '',
        ] ) );

        // add fill-speed attribute if user has adjusted the slider;
        // controls output an array value with `size`, so pull that out.
        $speed_attr = '';
        if ( isset( $settings['line_fill_speed']['size'] ) ) {
            $speed_attr = ' data-fill-speed="' . esc_attr( $settings['line_fill_speed']['size'] ) . '"';
        }

       printf(
    '<div class="%s" data-animation="%s" data-offset="%d" data-duration="%d"%s>',
    esc_attr( $classes ),
    esc_attr( $settings['entrance_animation'] ?? 'fade' ),
    absint( $settings['animation_offset'] ?? 150 ),
    absint( $settings['animation_duration'] ?? 600 ),
    isset( $settings['line_fill_speed']['size'] )
        ? ' data-fill-speed="' . esc_attr( absint( $settings['line_fill_speed']['size'] ) ) . '"'
        : ''
);

        switch ( $source ) {
            case 'manual':   // manual means user entered custom items
                $this->render_custom_timeline( $settings );
                break;
            case 'taxonomy':
                $this->render_taxonomy_timeline( $settings );
                break;
            default:
                $this->render_post_timeline( $settings );
                break;
        }

        echo '</div><!-- .sitsel-tl-wrapper -->';
    }

    // ── Post timeline ─────────────────────────────────────────────────────
    private function render_post_timeline( array $settings ): void {
      
        $paged = Softech_Post_Query_Manager::get_paged(); 
        $query = new \WP_Query( Softech_Post_Query_Manager::build_query_args( $settings, $paged ) );

        // L-3 FIX: fall back to translatable default if setting is empty
        $no_posts_text = ! empty( $settings['not_found_text'] )
    ? $settings['not_found_text']
    : __( 'No items found.', 'softech-addons-for-elementor' );

        if ( ! $query->have_posts() ) {
            echo '<p class="sitsel-tl-no-posts">' . esc_html( $no_posts_text ) . '</p>';
            wp_reset_postdata();
            return;
        }

        $grouping      = $settings['date_grouping'] ?? 'none';
        $index         = 0;
        $current_group = '';

        echo '<div class="sitsel-tl-items">';

        while ( $query->have_posts() ) {
            $query->the_post();
            $post_id = get_the_ID();

            if ( 'none' !== $grouping ) {
                $group_label = $this->get_group_label( $post_id, $grouping );
                if ( $group_label !== $current_group ) {
                    if ( '' !== $current_group ) echo '</div><!-- .sitsel-tl-group -->';
                    $current_group = $group_label;
                    echo '<div class="sitsel-tl-group"><div class="sitsel-tl-group-label">' . esc_html( $group_label ) . '</div>';
                }
            }

            $side = ( 0 === $index % 2 ) ? 'left' : 'right';
            echo '<div class="sitsel-tl-item sitsel-tl-item--' . esc_attr( $side ) . ' sitsel-tl-item--' . absint( $index ) . '">';

            if ( 'yes' === ( $settings['show_dot']         ?? '' ) ) $this->render_dot( $settings );
            if ( 'yes' === ( $settings['show_extra_label'] ?? '' ) ) $this->render_extra_label( $settings, $post_id );

            echo '<div class="sitsel-tl-card">';
            $this->render_post_card_elements( $settings, $post_id );
            echo '</div></div><!-- .sitsel-tl-card / .sitsel-tl-item -->';

            $index++;
        }

        if ( 'none' !== $grouping && '' !== $current_group ) echo '</div><!-- .sitsel-tl-group -->';
        if ( 'yes' === ( $settings['show_connector_line'] ?? '' ) ) echo '<div class="sitsel-tl-line" aria-hidden="true"></div>';
        echo '</div><!-- .sitsel-tl-items -->';

        if ( 'yes' === ( $settings['show_pagination'] ?? '' ) ) $this->render_pagination( $query->max_num_pages, $paged );

        wp_reset_postdata();
    }

    private function render_extra_label( array $settings, int $post_id ): void {
        $source = $settings['extra_label_source'] ?? 'publish_date';
        $fmt    = ( 'custom' === ( $settings['date_format'] ?? '' ) )
            ? sanitize_text_field( $settings['date_format_custom'] ?? 'F j, Y' )
            : sanitize_text_field( $settings['date_format']        ?? 'F j, Y' );

        $label = '';
        switch ( $source ) {
            case 'modified_date':
                $label = get_the_modified_date( $fmt, $post_id );
                break;
            case 'custom_field':
                $key = sanitize_key( $settings['extra_label_custom_key'] ?? '' );
                if ( $key ) $label = (string) get_post_meta( $post_id, $key, true );
                break;
            default:
                $label = get_the_date( $fmt, $post_id );
                break;
        }

        if ( $label ) {
            echo '<div class="sitsel-tl-extra-label">' . esc_html( $label ) . '</div>';
        }
    }

  

    private function get_group_label( int $post_id, string $grouping ): string {
        $ts = get_post_time( 'U', false, $post_id );
        if ( false === $ts || ! is_numeric( $ts ) ) return '';
        return 'year' === $grouping ? wp_date( 'Y', (int) $ts ) : wp_date( 'F Y', (int) $ts );
    }

    private function render_post_card_elements( array $settings, int $post_id ): void {
        $elements = $settings['post_card_elements'] ?? [];
        if ( empty( $elements ) || ! is_array( $elements ) ) {
            $elements = [
                [ 'element_type' => 'thumbnail' ],
                [ 'element_type' => 'date' ],
                [ 'element_type' => 'title' ],
                [ 'element_type' => 'excerpt' ],
                [ 'element_type' => 'read_more' ],
            ];
        }

        $allowed_tags = [ 'h1','h2','h3','h4','h5','h6','div','span','p' ];
        $fmt = ( 'custom' === ( $settings['date_format'] ?? '' ) )
            ? sanitize_text_field( $settings['date_format_custom'] ?? 'F j, Y' )
            : sanitize_text_field( $settings['date_format']        ?? 'F j, Y' );

        foreach ( $elements as $el ) {
            switch ( $el['element_type'] ?? '' ) {

                case 'thumbnail':
                    $this->render_post_thumbnail( $post_id, sanitize_key( $settings['image_resolution'] ?? 'large' ) );
                    break;

                case 'date':
                    echo '<div class="sitsel-tl-meta sitsel-tl-date">' . esc_html( get_the_date( $fmt, $post_id ) ) . '</div>';
                    break;

                case 'title':
                    $tag = in_array( $el['title_tag'] ?? 'h3', $allowed_tags, true ) ? $el['title_tag'] : 'h3';
                    printf(
                        '<%1$s class="sitsel-tl-title"><a href="%2$s">%3$s</a></%1$s>',
                        esc_attr( $tag ),
                        esc_url( get_permalink( $post_id ) ),
                        esc_html( get_the_title( $post_id ) )
                    );
                    break;

                case 'excerpt':
                    echo '<div class="sitsel-tl-excerpt">'
                        . esc_html( wp_trim_words( get_the_excerpt( $post_id ), absint( $el['excerpt_words'] ?? 20 ), '…' ) )
                        . '</div>';
                    break;

                case 'author':
                    // H-3 FIX: get_userdata() is cache-warm after update_post_author_cache in query args
                    $author_id = (int) get_post_field( 'post_author', $post_id );
                    $author    = get_userdata( $author_id );
                    echo '<div class="sitsel-tl-meta sitsel-tl-author">'
                        . esc_html( $author ? $author->display_name : '' )
                        . '</div>';
                    break;

                case 'category':
                    $this->render_post_terms( $post_id, 'category', 'sitsel-tl-categories' );
                    break;

                case 'tags':
                    $this->render_post_terms( $post_id, 'post_tag', 'sitsel-tl-tags' );
                    break;

                case 'read_more':
                    printf(
                        '<div class="sitsel-tl-readmore"><a class="sitsel-tl-btn" href="%s">%s</a></div>',
                        esc_url( get_permalink( $post_id ) ),
                        esc_html( sanitize_text_field( $el['read_more_text'] ?? __( 'Read More', 'softech-addons-for-elementor' ) ) )
                    );
                    break;

                case 'custom_field':
                    $key = sanitize_key( $el['custom_field_key'] ?? '' );
                    if ( $key ) {
                        $val = get_post_meta( $post_id, $key, true );
                        if ( ! empty( $val ) ) {
                            echo '<div class="sitsel-tl-meta sitsel-tl-custom-field">' . esc_html( (string) $val ) . '</div>';
                        }
                    }
                    break;
            }
        }
    }

    private function render_post_thumbnail( int $post_id, string $size = 'large' ): void {
        $img = get_the_post_thumbnail_url( $post_id, $size );
        if ( ! $img ) return;
        printf(
            '<div class="sitsel-tl-thumbnail"><a href="%s"><img src="%s" alt="%s" loading="lazy" decoding="async" /></a></div>',
            esc_url( get_permalink( $post_id ) ),
            esc_url( $img ),
            esc_attr( get_the_title( $post_id ) )
        );
    }

    /**
     * M-1 FIX: removed redundant wp_kses_post() — names are already individually esc_html'd,
     * so implode() result is safe to echo directly.
     */
   private function render_post_terms( int $post_id, string $taxonomy, string $css_class ): void {
    $terms = get_the_terms( $post_id, $taxonomy );

    if ( empty( $terms ) || is_wp_error( $terms ) ) {
        return;
    }

    echo '<div class="sitsel-tl-meta ' . esc_attr( $css_class ) . '">';

    $first = true;
    foreach ( $terms as $term ) {
        if ( ! $first ) {
            echo ', ';
        }
        echo esc_html( $term->name );
        $first = false;
    }

    echo '</div>';
}

    // ── Taxonomy timeline ─────────────────────────────────────────────────
    private function render_taxonomy_timeline( array $settings ): void {
        $taxonomy = sanitize_key( $settings['source_post_taxonomy'] ?? 'category' );

        // L-3 FIX: translatable fallback
       $no_posts_text = ! empty( $settings['not_found_text'] )
    ? $settings['not_found_text']
    : __( 'No items found.', 'softech-addons-for-elementor' );

        if ( ! taxonomy_exists( $taxonomy ) ) {
            echo '<p class="sitsel-tl-no-posts">' . esc_html( $no_posts_text ) . '</p>';
            return;
        }

       $paged  = Softech_Post_Query_Manager::get_paged();
        $result = Softech_Post_Query_Manager::build_taxonomy_query( $settings, $paged );
        $terms       = $result['terms'];
        $total_pages = $result['total_pages'];

        if ( empty( $terms ) ) {
            echo '<p class="sitsel-tl-no-posts">' . esc_html( $no_posts_text ) . '</p>';
            return;
        }

        $index = 0;
        echo '<div class="sitsel-tl-items">';

        foreach ( $terms as $term ) {
            if ( ! ( $term instanceof \WP_Term ) ) { $index++; continue; }

            $side = ( 0 === $index % 2 ) ? 'left' : 'right';
            echo '<div class="sitsel-tl-item sitsel-tl-item--' . esc_attr( $side ) . ' sitsel-tl-item--' . absint( $index ) . '">';
            if ( 'yes' === ( $settings['show_dot'] ?? '' ) ) $this->render_dot( $settings );
            echo '<div class="sitsel-tl-card">';
            $this->render_taxonomy_card_elements( $settings, $term );
            echo '</div></div><!-- .sitsel-tl-card / .sitsel-tl-item -->';
            $index++;
        }

        if ( 'yes' === ( $settings['show_connector_line'] ?? '' ) ) echo '<div class="sitsel-tl-line" aria-hidden="true"></div>';
        echo '</div><!-- .sitsel-tl-items -->';

        if ( 'yes' === ( $settings['show_pagination'] ?? '' ) ) $this->render_pagination( $total_pages, $paged );
    }

    private function render_taxonomy_card_elements( array $settings, \WP_Term $term ): void {
        $elements          = $settings['tax_card_elements'] ?? [];
        $allowed_name_tags = [ 'h2','h3','h4','h5','div','span' ];

        if ( empty( $elements ) || ! is_array( $elements ) ) return;

        foreach ( $elements as $el ) {
            switch ( $el['element_type'] ?? '' ) {
                case 'term_name':
                    $tag  = in_array( $el['term_name_tag'] ?? 'h3', $allowed_name_tags, true ) ? $el['term_name_tag'] : 'h3';
                    $link = get_term_link( $term );
                    printf(
                        '<%1$s class="sitsel-tl-title"><a href="%2$s">%3$s</a></%1$s>',
                        esc_attr( $tag ),
                        esc_url( is_wp_error( $link ) ? '#' : $link ),
                        esc_html( $term->name )
                    );
                    break;

                case 'term_description':
                    $desc = wp_trim_words( $term->description, absint( $el['desc_words'] ?? 20 ), '…' );
                    if ( $desc ) echo '<div class="sitsel-tl-excerpt">' . esc_html( $desc ) . '</div>';
                    break;

                case 'term_count':
                    echo '<div class="sitsel-tl-meta sitsel-tl-term-count">';
                    /* translators: %d = number of posts */
                    printf( esc_html( _n( '%d Post', '%d Posts', $term->count, 'softech-addons-for-elementor' ) ), absint( $term->count ) );
                    echo '</div>';
                    break;

                case 'term_link':
                    $label = sanitize_text_field( $el['term_link_label'] ?? __( 'View All', 'softech-addons-for-elementor' ) );
                    $link  = get_term_link( $term );
                    printf(
                        '<div class="sitsel-tl-readmore"><a class="sitsel-tl-btn" href="%s">%s</a></div>',
                        esc_url( is_wp_error( $link ) ? '#' : $link ),
                        esc_html( $label )
                    );
                    break;

                case 'acf_field':
                    $key = sanitize_text_field( $el['acf_field_key'] ?? '' );
                    if ( $key && function_exists( 'get_field' ) ) {
                        $val = get_field( $key, $term );
                        if ( ! empty( $val ) && is_scalar( $val ) ) {
                            echo '<div class="sitsel-tl-meta sitsel-tl-acf">' . esc_html( (string) $val ) . '</div>';
                        }
                    }
                    break;
            }
        }
    }

    // ── Custom timeline ───────────────────────────────────────────────────
    private function render_custom_timeline( array $settings ): void {
        $items = $settings['custom_timeline_items'] ?? [];

        // L-3 FIX: translatable fallback
        $no_posts_text = ! empty( $settings['not_found_text'] )
    ? $settings['not_found_text']
    : __( 'No items found.', 'softech-addons-for-elementor' );

        if ( empty( $items ) ) {
            echo '<p class="sitsel-tl-no-posts">' . esc_html( $no_posts_text ) . '</p>';
            return;
        }

        $index = 0;
        echo '<div class="sitsel-tl-items">';

        foreach ( $items as $item ) {
            $side        = ( 0 === $index % 2 ) ? 'left' : 'right';
            $is_external = ! empty( $item['item_link']['is_external'] );

            echo '<div class="sitsel-tl-item sitsel-tl-item--' . esc_attr( $side ) . '">';

            // if ( 'yes' === ( $settings['show_dot'] ?? '' ) ) $this->render_dot( $settings );
            if ( 'yes' === ( $settings['show_dot'] ?? '' ) ) {
            $card_icon = ! empty( $item['item_icon']['value'] ) ? $item['item_icon'] : ( $settings['dot_icon'] ?? [] );
            echo '<div class="sitsel-tline-dot" aria-hidden="true">';
            \Elementor\Icons_Manager::render_icon( $card_icon, [] );
            echo '</div>';
        }
            if ( ! empty( $item['item_date'] ) ) {
                echo '<div class="sitsel-tl-extra-label">' . esc_html( $item['item_date'] ) . '</div>';
            }

            echo '<div class="sitsel-tl-card">';

            if ( ! empty( $item['item_image']['url'] ) ) {
                printf(
                    '<div class="sitsel-tl-thumbnail"><img src="%s" alt="%s" loading="lazy" decoding="async" /></div>',
                    esc_url( $item['item_image']['url'] ),
                    esc_attr( $item['item_title'] ?? '' )
                );
            }

            // C-1 FIX: removed esc_attr($target) / esc_attr($rel) — those double-escaped the quotes.
            // Now output attributes conditionally inline with no pre-built quoted strings.
            if ( ! empty( $item['item_title'] ) ) {
                $has_link = ! empty( $item['item_link']['url'] );
                echo '<h3 class="sitsel-tl-title">';
                if ( $has_link ) {
                    echo '<a href="' . esc_url( $item['item_link']['url'] ) . '"'
                        . ( $is_external ? ' target="_blank" rel="noopener noreferrer"' : '' ) . '>';
                }
                echo esc_html( $item['item_title'] );
                if ( $has_link ) { echo '</a>'; }
                echo '</h3>';
            }

            if ( ! empty( $item['item_description'] ) ) {
                echo '<div class="sitsel-tl-excerpt">' . wp_kses_post( $item['item_description'] ) . '</div>';
            }

            // if ( ! empty( $item['item_btn_label'] ) && ! empty( $item['item_link']['url'] ) ) {
            //     echo '<div class="sitsel-tl-readmore"><a class="sitsel-tl-btn" href="' . esc_url( $item['item_link']['url'] ) . '"'
            //         . ( $is_external ? ' target="_blank" rel="noopener noreferrer"' : '' )
            //         . '>' . esc_html( $item['item_btn_label'] ) . '</a></div>';
            // }
            if ( ! empty( $item['item_btn_label'] ) ) {
                $has_url = ! empty( $item['item_link']['url'] );
                echo '<div class="sitsel-tl-readmore">';
                if ( $has_url ) {
                    echo '<a class="sitsel-tl-btn" href="' . esc_url( $item['item_link']['url'] ) . '"'
                        . ( $is_external ? ' target="_blank" rel="noopener noreferrer"' : '' ) . '>';
                } else {
                    echo '<span class="sitsel-tl-btn">';
                }
                echo esc_html( $item['item_btn_label'] );
                echo $has_url ? '</a>' : '</span>';
                echo '</div>';
            }

            echo '</div></div><!-- .sitsel-tl-card / .sitsel-tl-item -->';
            $index++;
        }

        if ( 'yes' === ( $settings['show_connector_line'] ?? '' ) ) echo '<div class="sitsel-tl-line" aria-hidden="true"></div>';
        echo '</div><!-- .sitsel-tl-items -->';
    }

    // ── Shared helpers ────────────────────────────────────────────────────

    /**
     * L-1 FIX: removed aria-hidden from render_icon args — parent div already has aria-hidden="true".
     */
    private function render_dot( array $settings ): void {
        $icon = $settings['dot_icon'] ?? [];
        if ( empty( $icon['value'] ) ) return;

        echo '<div class="sitsel-tline-dot" aria-hidden="true">';
        \Elementor\Icons_Manager::render_icon( $icon, [] );
        echo '</div>';
    }

    private function get_paged(): int {
        $paged = (int) get_query_var( 'paged' );
        if ( ! $paged && is_singular() ) {
            $paged = (int) get_query_var( 'page' );
        }
        return max( 1, $paged );
    }

    private function render_pagination( int $total_pages, int $paged ): void {
        if ( $total_pages <= 1 ) return;

        $big  = 999999999;
        $base = str_replace( $big, '%#%', esc_url( get_pagenum_link( $big ) ) );
        $fmt  = get_option( 'permalink_structure' ) ? 'page/%#%/' : '?paged=%#%';

        $links = paginate_links( [
            'base'      => $base,
            'format'    => $fmt,
            'current'   => max( 1, $paged ),
            'total'     => $total_pages,
            'type'      => 'list',
            'prev_text' => esc_html__( '« Prev', 'softech-addons-for-elementor' ),
            'next_text' => esc_html__( 'Next »', 'softech-addons-for-elementor' ),
        ] );

        if ( $links ) {
            echo '<nav class="sitsel-tl-pagination" aria-label="' . esc_attr__( 'Timeline pagination', 'softech-addons-for-elementor' ) . '">'
                . wp_kses_post( $links )
                . '</nav>';
        }
    }
}