<?php
/**
 * Sifency Light Beam Widget
 *
 * A decorative light beam / spotlight element with:
 *  - 8 anchor positions (top-left, top-center, top-right,
 *    middle-left, middle-right, bottom-left, bottom-center, bottom-right)
 *  - Controllable beam width, length, color, opacity, blur
 *  - Scroll-driven direction animation
 *  - Mouse-tracking direction animation
 *  - Both interaction modes can be combined or used independently
 *
 * @package Sifency_Addons
 * @since   1.0.0
 *
 * FIX NOTES (v1.2):
 *  - All CSS custom properties are now written via an inline <style> block scoped
 *    to a unique #slb-{id} selector. This is required because JS reparents
 *    .slb-wrapper OUT of {{WRAPPER}}, breaking Elementor's {{WRAPPER}} selectors.
 *  - Responsive values (desktop/tablet/mobile) are handled inside the inline <style>
 *    using standard media queries written from Elementor responsive settings.
 *  - Elementor 'selectors' are only used for properties that stay INSIDE {{WRAPPER}}
 *    (e.g. the widget container itself — nothing here qualifies after reparenting).
 *  - Mouse tracking formula rewritten: beam follows the mouse angle from anchor point
 *    directly, producing a clean, predictable sweep.
 *  - base_angle JS sync: CSS var is applied to wrapper for reference but GSAP reads
 *    cfg.baseAngle directly from data-config (the single source of truth).
 */

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

use Sifency\Classes\Sifency_Addons_Widget_Base;
use Elementor\Controls_Manager;
use Elementor\Group_Control_Background;
use Sifency\Traits\Sifency_Addons_Help_Controls;
use Sifency\Traits\Sifency_Addons_Custom_CSS_Controls;

class Sifency_Addons_Light_Beam extends Sifency_Addons_Widget_Base {
	use Sifency_Addons_Help_Controls;
	use Sifency_Addons_Custom_CSS_Controls;

	public function __construct( $data = [], $args = null ) {
		parent::__construct( $data, $args );
		$this->wg_config = [
			'name'           => 'sifency-light-beam',
			'title'          => esc_html__( 'Sifency Light Beam', 'sifency-addons' ),
			'icon'           => 'eicon-shape',
			'category'       => [ 'sifency-creative-widgets' ],
			'script_depends' => [ 'gsap', 'gsap-scrolltrigger', 'sifency-light-beam-script' ],
			'style_depends'  => [ 'sifency-light-beam-style' ],
		];
	}

	// ─── Helper: extract responsive slider value safely ───────────────────
	private function get_slider( $settings, string $key, $fallback = 0 ): float {
		return isset( $settings[ $key ]['size'] ) && '' !== $settings[ $key ]['size']
			? (float) $settings[ $key ]['size']
			: (float) $fallback;
	}

	protected function register_controls(): void {

		// ─── Beam Source / Anchor ─────────────────────────────────────────
		$this->start_controls_section( 'section_beam_source', [
			'label' => esc_html__( 'Beam Source', 'sifency-addons' ),
		] );

		$this->add_control( 'anchor_position', [
			'label'   => esc_html__( 'Anchor Position', 'sifency-addons' ),
			'type'    => Controls_Manager::SELECT,
			'options' => [
				'top-left'      => esc_html__( 'Top Left',      'sifency-addons' ),
				'top-center'    => esc_html__( 'Top Center',    'sifency-addons' ),
				'top-right'     => esc_html__( 'Top Right',     'sifency-addons' ),
				'middle-left'   => esc_html__( 'Middle Left',   'sifency-addons' ),
				'middle-right'  => esc_html__( 'Middle Right',  'sifency-addons' ),
				'bottom-left'   => esc_html__( 'Bottom Left',   'sifency-addons' ),
				'bottom-center' => esc_html__( 'Bottom Center', 'sifency-addons' ),
				'bottom-right'  => esc_html__( 'Bottom Right',  'sifency-addons' ),
			],
			'default' => 'top-left',
		] );

		// Offset X — responsive. No Elementor 'selectors' because .slb-wrapper
		// is reparented out of {{WRAPPER}}. Values are written via inline <style>.
		$this->add_responsive_control( 'anchor_offset_x', [
			'label'   => esc_html__( 'Anchor Offset X (%)', 'sifency-addons' ),
			'type'    => Controls_Manager::SLIDER,
			'range'   => [ 'px' => [ 'min' => -30, 'max' => 30 ] ],
			'default' => [ 'size' => 0 ],
		] );

		$this->add_responsive_control( 'anchor_offset_y', [
			'label'   => esc_html__( 'Anchor Offset Y (%)', 'sifency-addons' ),
			'type'    => Controls_Manager::SLIDER,
			'range'   => [ 'px' => [ 'min' => -30, 'max' => 30 ] ],
			'default' => [ 'size' => 0 ],
		] );

		$this->end_controls_section();

		// ─── Beam Geometry ────────────────────────────────────────────────
		$this->start_controls_section( 'section_beam_shape', [
			'label' => esc_html__( 'Beam Shape', 'sifency-addons' ),
		] );

		$this->add_responsive_control( 'beam_start_width', [
			'label'   => esc_html__( 'Start Width (px)', 'sifency-addons' ),
			'type'    => Controls_Manager::SLIDER,
			'range'   => [ 'px' => [ 'min' => 0, 'max' => 200 ] ],
			'default' => [ 'size' => 0 ],
		] );

		$this->add_responsive_control( 'beam_end_width', [
			'label'   => esc_html__( 'End Width (px)', 'sifency-addons' ),
			'type'    => Controls_Manager::SLIDER,
			'range'   => [ 'px' => [ 'min' => 10, 'max' => 1000 ] ],
			'default' => [ 'size' => 400 ],
		] );

		$this->add_responsive_control( 'beam_length', [
			'label'   => esc_html__( 'Beam Length (px)', 'sifency-addons' ),
			'type'    => Controls_Manager::SLIDER,
			'range'   => [ 'px' => [ 'min' => 100, 'max' => 2000 ] ],
			'default' => [ 'size' => 800 ],
		] );

		$this->add_control( 'base_angle', [
			'label'   => esc_html__( 'Base Direction (deg)', 'sifency-addons' ),
			'type'    => Controls_Manager::SLIDER,
			'range'   => [ 'px' => [ 'min' => -180, 'max' => 180 ] ],
			'default' => [ 'size' => 135 ],
		] );

		$this->end_controls_section();

		// ─── Beam Appearance ──────────────────────────────────────────────
		$this->start_controls_section( 'section_beam_style_content', [
			'label' => esc_html__( 'Beam Appearance', 'sifency-addons' ),
		] );

		// ── Beam Fill — supports solid color, gradient, or image ────
		$this->add_group_control( Group_Control_Background::get_type(), [
			'name'     => 'beam_background',
			'label'    => esc_html__( 'Beam Fill', 'sifency-addons' ),
			'types'    => [ 'classic', 'gradient' ],
			'selector' => '', // intentionally empty — written via inline <style>
			// 'classic' type maps to background-color + background-image.
			// 'gradient' maps to background: linear-gradient(...).
			// Both are read from $settings in build_inline_styles().
		] );

		$this->add_control( 'beam_opacity', [
			'label'   => esc_html__( 'Opacity', 'sifency-addons' ),
			'type'    => Controls_Manager::SLIDER,
			'range'   => [ 'px' => [ 'min' => 0, 'max' => 1, 'step' => 0.01 ] ],
			'default' => [ 'size' => 0.85 ],
		] );

		$this->add_control( 'beam_blur', [
			'label'   => esc_html__( 'Soft Edge Blur (px)', 'sifency-addons' ),
			'type'    => Controls_Manager::SLIDER,
			'range'   => [ 'px' => [ 'min' => 0, 'max' => 80 ] ],
			'default' => [ 'size' => 0 ],
		] );

		// ── Fade / Mask Controls ──────────────────────────────────────────
		$this->add_control( 'fade_heading', [
			'label'     => esc_html__( 'Fade (Tip → Base)', 'sifency-addons' ),
			'type'      => Controls_Manager::HEADING,
			'separator' => 'before',
		] );

		$this->add_control( 'fade_tip_opacity', [
			'label'       => esc_html__( 'Tip Opacity', 'sifency-addons' ),
			'description' => esc_html__( 'Opacity at the very tip of the beam (0 = invisible).', 'sifency-addons' ),
			'type'        => Controls_Manager::SLIDER,
			'range'       => [ 'px' => [ 'min' => 0, 'max' => 1, 'step' => 0.01 ] ],
			'default'     => [ 'size' => 0 ],
		] );

		$this->add_control( 'fade_mid_stop', [
			'label'       => esc_html__( 'Mid-point (%)', 'sifency-addons' ),
			'description' => esc_html__( 'Where the mid-opacity stop sits along the beam length.', 'sifency-addons' ),
			'type'        => Controls_Manager::SLIDER,
			'range'       => [ 'px' => [ 'min' => 0, 'max' => 50 ] ],
			'default'     => [ 'size' => 5 ],
		] );

		$this->add_control( 'fade_mid_opacity', [
			'label'   => esc_html__( 'Mid Opacity', 'sifency-addons' ),
			'type'    => Controls_Manager::SLIDER,
			'range'   => [ 'px' => [ 'min' => 0, 'max' => 1, 'step' => 0.01 ] ],
			'default' => [ 'size' => 0.5 ],
		] );

		$this->add_control( 'fade_base_opacity', [
			'label'       => esc_html__( 'Base Opacity', 'sifency-addons' ),
			'description' => esc_html__( 'Opacity at the wide spread end of the beam (1 = fully opaque).', 'sifency-addons' ),
			'type'        => Controls_Manager::SLIDER,
			'range'       => [ 'px' => [ 'min' => 0, 'max' => 1, 'step' => 0.01 ] ],
			'default'     => [ 'size' => 1 ],
		] );

		$this->add_control( 'blend_mode', [
			'label'   => esc_html__( 'Blend Mode', 'sifency-addons' ),
			'type'    => Controls_Manager::SELECT,
			'options' => [
				'normal'      => esc_html__( 'Normal',      'sifency-addons' ),
				'screen'      => esc_html__( 'Screen',      'sifency-addons' ),
				'overlay'     => esc_html__( 'Overlay',     'sifency-addons' ),
				'lighten'     => esc_html__( 'Lighten',     'sifency-addons' ),
				'multiply'    => esc_html__( 'Multiply',    'sifency-addons' ),
				'color-dodge' => esc_html__( 'Color Dodge', 'sifency-addons' ),
			],
			'default' => 'normal',
		] );

		$this->add_control( 'z_index', [
			'label'   => esc_html__( 'Z-Index', 'sifency-addons' ),
			'type'    => Controls_Manager::NUMBER,
			'default' => 0,
		] );

		$this->end_controls_section();

		// ─── Interaction — Scroll ─────────────────────────────────────────
		$this->start_controls_section( 'section_scroll', [
			'label' => esc_html__( 'Scroll Interaction', 'sifency-addons' ),
		] );

		$this->add_control( 'scroll_enabled', [
			'label'        => esc_html__( 'Enable Scroll Rotation', 'sifency-addons' ),
			'type'         => Controls_Manager::SWITCHER,
			'default'      => 'yes',
			'return_value' => 'yes',
		] );

		$this->add_control( 'scroll_start_angle', [
			'label'     => esc_html__( 'Start Angle (deg)', 'sifency-addons' ),
			'type'      => Controls_Manager::SLIDER,
			'range'     => [ 'px' => [ 'min' => -360, 'max' => 360 ] ],
			'default'   => [ 'size' => -20 ],
			'condition' => [ 'scroll_enabled' => 'yes' ],
		] );

		$this->add_control( 'scroll_end_angle', [
			'label'     => esc_html__( 'End Angle (deg)', 'sifency-addons' ),
			'type'      => Controls_Manager::SLIDER,
			'range'     => [ 'px' => [ 'min' => -360, 'max' => 360 ] ],
			'default'   => [ 'size' => 40 ],
			'condition' => [ 'scroll_enabled' => 'yes' ],
		] );

		$this->add_control( 'scroll_scrub', [
			'label'       => esc_html__( 'Scrub Smoothness', 'sifency-addons' ),
			'description' => esc_html__( '1 = instant, higher = smoother lag.', 'sifency-addons' ),
			'type'        => Controls_Manager::SLIDER,
			'range'       => [ 'px' => [ 'min' => 0.5, 'max' => 5, 'step' => 0.5 ] ],
			'default'     => [ 'size' => 1 ],
			'condition'   => [ 'scroll_enabled' => 'yes' ],
		] );

		$this->add_control( 'scroll_trigger_start', [
			'label'       => esc_html__( 'Trigger Start', 'sifency-addons' ),
			'description' => esc_html__( 'ScrollTrigger start, e.g. "top 80%".', 'sifency-addons' ),
			'type'        => Controls_Manager::TEXT,
			'default'     => 'top 90%',
			'condition'   => [ 'scroll_enabled' => 'yes' ],
		] );

		$this->add_control( 'scroll_trigger_end', [
			'label'       => esc_html__( 'Trigger End', 'sifency-addons' ),
			'description' => esc_html__( 'ScrollTrigger end, e.g. "bottom 10%".', 'sifency-addons' ),
			'type'        => Controls_Manager::TEXT,
			'default'     => 'bottom 10%',
			'condition'   => [ 'scroll_enabled' => 'yes' ],
		] );

		$this->add_control( 'scroll_opacity_enabled', [
			'label'        => esc_html__( 'Also Animate Opacity on Scroll', 'sifency-addons' ),
			'type'         => Controls_Manager::SWITCHER,
			'default'      => '',
			'return_value' => 'yes',
			'condition'    => [ 'scroll_enabled' => 'yes' ],
		] );

		$this->add_control( 'scroll_opacity_from', [
			'label'     => esc_html__( 'Opacity From', 'sifency-addons' ),
			'type'      => Controls_Manager::SLIDER,
			'range'     => [ 'px' => [ 'min' => 0, 'max' => 1, 'step' => 0.01 ] ],
			'default'   => [ 'size' => 0 ],
			'condition' => [ 'scroll_enabled' => 'yes', 'scroll_opacity_enabled' => 'yes' ],
		] );

		$this->add_control( 'scroll_opacity_to', [
			'label'     => esc_html__( 'Opacity To', 'sifency-addons' ),
			'type'      => Controls_Manager::SLIDER,
			'range'     => [ 'px' => [ 'min' => 0, 'max' => 1, 'step' => 0.01 ] ],
			'default'   => [ 'size' => 1 ],
			'condition' => [ 'scroll_enabled' => 'yes', 'scroll_opacity_enabled' => 'yes' ],
		] );

		$this->end_controls_section();

		// ─── Interaction — Mouse ──────────────────────────────────────────
		$this->start_controls_section( 'section_mouse', [
			'label' => esc_html__( 'Mouse Interaction', 'sifency-addons' ),
		] );

		$this->add_control( 'mouse_enabled', [
			'label'        => esc_html__( 'Enable Mouse Tracking', 'sifency-addons' ),
			'type'         => Controls_Manager::SWITCHER,
			'default'      => 'yes',
			'return_value' => 'yes',
		] );

		$this->add_control( 'mouse_scope', [
			'label'     => esc_html__( 'Track Mouse On', 'sifency-addons' ),
			'type'      => Controls_Manager::SELECT,
			'options'   => [
				'widget' => esc_html__( 'Widget Area Only', 'sifency-addons' ),
				'window' => esc_html__( 'Entire Window',   'sifency-addons' ),
				'parent' => esc_html__( 'Parent Section',  'sifency-addons' ),
			],
			'default'   => 'window',
			'condition' => [ 'mouse_enabled' => 'yes' ],
		] );

		$this->add_control( 'mouse_intensity', [
			'label'       => esc_html__( 'Rotation Intensity (deg)', 'sifency-addons' ),
			'description' => esc_html__( 'Max degrees the beam rotates toward the mouse.', 'sifency-addons' ),
			'type'        => Controls_Manager::SLIDER,
			'range'       => [ 'px' => [ 'min' => 5, 'max' => 180 ] ],
			'default'     => [ 'size' => 60 ],
			'condition'   => [ 'mouse_enabled' => 'yes' ],
		] );

		$this->add_control( 'mouse_ease', [
			'label'     => esc_html__( 'Ease / Lag (0–1)', 'sifency-addons' ),
			'type'      => Controls_Manager::SLIDER,
			'range'     => [ 'px' => [ 'min' => 0.01, 'max' => 1, 'step' => 0.01 ] ],
			'default'   => [ 'size' => 0.15 ],
			'condition' => [ 'mouse_enabled' => 'yes' ],
		] );

		$this->add_control( 'mouse_invert', [
			'label'        => esc_html__( 'Invert Direction', 'sifency-addons' ),
			'type'         => Controls_Manager::SWITCHER,
			'default'      => '',
			'return_value' => 'yes',
			'condition'    => [ 'mouse_enabled' => 'yes' ],
		] );

		$this->add_control( 'mouse_reset_on_leave', [
			'label'        => esc_html__( 'Reset on Mouse Leave', 'sifency-addons' ),
			'type'         => Controls_Manager::SWITCHER,
			'default'      => 'yes',
			'return_value' => 'yes',
			'condition'    => [ 'mouse_enabled' => 'yes' ],
		] );

		$this->end_controls_section();

		// ─── Help & Custom CSS ────────────────────────────────────────────
		$this->add_sifency_addons_help_controls();
		$this->add_Sifency_Addons_Custom_CSS_Controls();
	}

	// ─── Build inline CSS vars scoped to the unique wrapper ID ───────────
	private function build_inline_styles( string $uid, array $settings ): string {
		$s = $settings;

		// ── Background (Group_Control_Background) ────────────────────
		// Group_Control_Background stores its output differently by type:
		//   classic  → beam_background_color (hex/rgba), beam_background_image (url)
		//   gradient → beam_background_color (first stop), beam_background_color_b,
		//              beam_background_color_stop, beam_background_color_b_stop,
		//              beam_background_gradient_type, beam_background_gradient_angle
		$bg_type = $s['beam_background_background'] ?? 'classic';

		if ( 'gradient' === $bg_type ) {
			$c1       = $s['beam_background_color']          ?? '#FF4500';
			$c2       = $s['beam_background_color_b']        ?? 'rgba(255,69,0,0)';
			$stop1    = $s['beam_background_color_stop']['size']   ?? 0;
			$stop2    = $s['beam_background_color_b_stop']['size'] ?? 100;
			$g_type   = $s['beam_background_gradient_type']  ?? 'linear';
			$g_angle  = $s['beam_background_gradient_angle']['size'] ?? 180;

			if ( 'radial' === $g_type ) {
				$bg_value = "radial-gradient(circle,{$c1} {$stop1}%,{$c2} {$stop2}%)";
			} else {
				$bg_value = "linear-gradient({$g_angle}deg,{$c1} {$stop1}%,{$c2} {$stop2}%)";
			}
		} else {
			// Classic: solid color (image is not practical for a beam shape)
			$bg_color = $s['beam_background_color'] ?? '#FF4500';
			$bg_value = ! empty( $bg_color ) ? $bg_color : '#FF4500';
		}

		// ── Scalar appearance values ─────────────────────────────────
		$opacity    = (float) ( $s['beam_opacity']['size'] ?? 0.85 );
		$blur       = (float) ( $s['beam_blur']['size']    ?? 0 );
		$blend      = sanitize_key( $s['blend_mode'] ?? 'normal' );
		$z_index    = (int) ( $s['z_index'] ?? 0 );
		$base_angle = $this->get_slider( $s, 'base_angle', 135 );

		// ── Fade / mask values ───────────────────────────────────────
		$mask_tip_op  = (float) ( $s['fade_tip_opacity']['size']  ?? 0 );
		$mask_mid_op  = (float) ( $s['fade_mid_opacity']['size']  ?? 0.5 );
		$mask_mid_st  = (float) ( $s['fade_mid_stop']['size']     ?? 5 );
		$mask_base_op = (float) ( $s['fade_base_opacity']['size'] ?? 1 );

		// Build the mask gradient string
		$mask_gradient = sprintf(
			'linear-gradient(to bottom,rgba(0,0,0,%s) 0%%,rgba(0,0,0,%s) %s%%,rgba(0,0,0,%s) 100%%)',
			$mask_tip_op,
			$mask_mid_op,
			$mask_mid_st,
			$mask_base_op
		);

		// ── Responsive helper: returns [desktop, tablet, mobile] ─────
		$rv = function ( string $key, $fb ) use ( $s ) {
			$d = isset( $s[ $key ]['size'] ) && '' !== $s[ $key ]['size']
				? (float) $s[ $key ]['size'] : (float) $fb;
			$t = isset( $s[ $key . '_tablet' ]['size'] ) && '' !== $s[ $key . '_tablet' ]['size']
				? (float) $s[ $key . '_tablet' ]['size'] : null;
			$m = isset( $s[ $key . '_mobile' ]['size'] ) && '' !== $s[ $key . '_mobile' ]['size']
				? (float) $s[ $key . '_mobile' ]['size'] : null;
			return [ $d, $t, $m ];
		};

		[ $sw_d, $sw_t, $sw_m ] = $rv( 'beam_start_width', 0 );
		[ $ew_d, $ew_t, $ew_m ] = $rv( 'beam_end_width', 400 );
		[ $bl_d, $bl_t, $bl_m ] = $rv( 'beam_length', 800 );
		[ $ax_d, $ax_t, $ax_m ] = $rv( 'anchor_offset_x', 0 );
		[ $ay_d, $ay_t, $ay_m ] = $rv( 'anchor_offset_y', 0 );

		// ── Build CSS ────────────────────────────────────────────────
		$css = '';

		// Wrapper CSS vars (desktop/base)
		$css .= "#slb-{$uid}{";
		$css .= "--slb-start-width:{$sw_d}px;";
		$css .= "--slb-end-width:{$ew_d}px;";
		$css .= "--slb-length:{$bl_d}px;";
		$css .= "--slb-base-angle:{$base_angle}deg;";
		$css .= "--slb-anchor-x:{$ax_d}%;";
		$css .= "--slb-anchor-y:{$ay_d}%;";
		$css .= "z-index:{$z_index};";
		$css .= '}';

		// Beam element
		$css .= "#slb-{$uid} .slb-beam{";
		$css .= "opacity:{$opacity};";
		if ( $blur > 0 ) {
			$css .= "filter:blur({$blur}px);";
		}
		$css .= "mix-blend-mode:{$blend};";
		$css .= '}';

		// Beam inner — background fill + mask fade
		$css .= "#slb-{$uid} .slb-beam-inner{";
		$css .= "background:{$bg_value};";
		$css .= "-webkit-mask-image:{$mask_gradient};";
		$css .= "mask-image:{$mask_gradient};";
		$css .= '}';

		// Tablet overrides
		$tablet_vars = '';
		if ( null !== $sw_t ) { $tablet_vars .= "--slb-start-width:{$sw_t}px;"; }
		if ( null !== $ew_t ) { $tablet_vars .= "--slb-end-width:{$ew_t}px;"; }
		if ( null !== $bl_t ) { $tablet_vars .= "--slb-length:{$bl_t}px;"; }
		if ( null !== $ax_t ) { $tablet_vars .= "--slb-anchor-x:{$ax_t}%;"; }
		if ( null !== $ay_t ) { $tablet_vars .= "--slb-anchor-y:{$ay_t}%;"; }
		if ( '' !== $tablet_vars ) {
			$css .= "@media(max-width:1024px){#slb-{$uid}{{$tablet_vars}}}";
		}

		// Mobile overrides
		$mobile_vars = '';
		if ( null !== $sw_m ) { $mobile_vars .= "--slb-start-width:{$sw_m}px;"; }
		if ( null !== $ew_m ) { $mobile_vars .= "--slb-end-width:{$ew_m}px;"; }
		if ( null !== $bl_m ) { $mobile_vars .= "--slb-length:{$bl_m}px;"; }
		if ( null !== $ax_m ) { $mobile_vars .= "--slb-anchor-x:{$ax_m}%;"; }
		if ( null !== $ay_m ) { $mobile_vars .= "--slb-anchor-y:{$ay_m}%;"; }
		if ( '' !== $mobile_vars ) {
			$css .= "@media(max-width:767px){#slb-{$uid}{{$mobile_vars}}}";
		}

		return $css;
	}

	protected function render_dom( array $context ): void {
		$this->sifency_addons_render_custom_css();
		$settings = $this->get_settings_for_display();

		$anchor     = esc_attr( $settings['anchor_position'] ?? 'top-left' );
		$base_angle = $this->get_slider( $settings, 'base_angle', 135 );

		// Unique ID so the scoped CSS and JS both target this instance only
		$uid = 'slb-' . $this->get_id();

		// ── Build data-config for JS ──────────────────────────────────
		$config = wp_json_encode( [
			// Identity
			'uid'           => $uid,
			// Beam
			'anchor'        => $anchor,
			'baseAngle'     => $base_angle,
			// Scroll
			'scrollEnabled'        => ( 'yes' === ( $settings['scroll_enabled'] ?? 'yes' ) ),
			'scrollStartAngle'     => $this->get_slider( $settings, 'scroll_start_angle', -20 ),
			'scrollEndAngle'       => $this->get_slider( $settings, 'scroll_end_angle', 40 ),
			'scrollScrub'          => $this->get_slider( $settings, 'scroll_scrub', 1 ),
			'scrollTriggerStart'   => sanitize_text_field( $settings['scroll_trigger_start'] ?? 'top 90%' ),
			'scrollTriggerEnd'     => sanitize_text_field( $settings['scroll_trigger_end'] ?? 'bottom 10%' ),
			'scrollOpacityEnabled' => ( 'yes' === ( $settings['scroll_opacity_enabled'] ?? '' ) ),
			'scrollOpacityFrom'    => $this->get_slider( $settings, 'scroll_opacity_from', 0 ),
			'scrollOpacityTo'      => $this->get_slider( $settings, 'scroll_opacity_to', 1 ),
			// Mouse
			'mouseEnabled'      => ( 'yes' === ( $settings['mouse_enabled'] ?? 'yes' ) ),
			'mouseScope'        => esc_attr( $settings['mouse_scope'] ?? 'window' ),
			'mouseIntensity'    => $this->get_slider( $settings, 'mouse_intensity', 60 ),
			'mouseEase'         => $this->get_slider( $settings, 'mouse_ease', 0.15 ),
			'mouseInvert'       => ( 'yes' === ( $settings['mouse_invert'] ?? '' ) ),
			'mouseResetOnLeave' => ( 'yes' === ( $settings['mouse_reset_on_leave'] ?? 'yes' ) ),
		] );

		// ── Inline scoped CSS ─────────────────────────────────────────
		// All CSS vars are scoped to #slb-{widget_id} so they survive
		// JS reparenting the .slb-wrapper out of {{WRAPPER}}.
		$inline_css = $this->build_inline_styles( $this->get_id(), $settings );

		?>
		<style id="slb-style-<?php echo esc_attr( $this->get_id() ); ?>"><?php echo $inline_css; // Constructed from safe values — no user HTML ?></style>

		<div id="<?php echo esc_attr( $uid ); ?>"
			 class="slb-wrapper slb-anchor-<?php echo esc_attr( $anchor ); ?>"
			 data-config='<?php echo esc_attr( $config ); ?>'>
			<div class="slb-beam">
				<div class="slb-beam-inner"></div>
			</div>
		</div>
		<?php
	}
}