<?php
/**
 * Icon Box element.
 *
 * An icon (or image) with a title, text and optional link. The icon can sit on
 * top of or beside the content, be sized and badged (background / shape /
 * border), and react on hover. Supports a corner badge and a flexible link
 * (none / title / button / whole box). A staple "features" element that
 * WPBakery does not ship as a single component.
 *
 * @package CraftAddonsForWPBakery
 */

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

/**
 * Class CAFW_Element_Icon_Box
 */
class CAFW_Element_Icon_Box extends CAFW_Element_Base {

	/**
	 * Supported icon libraries: param suffix => default glyph class.
	 *
	 * @var array
	 */
	private static $libraries = array(
		'fontawesome' => 'fa fa-solid fa-star',
		'openiconic'  => 'vc-oi vc-oi-dial',
		'typicons'    => 'typcn typcn-adjust-brightness',
		'entypo'      => 'entypo-icon entypo-icon-note',
		'linecons'    => 'vc_li vc_li-heart',
	);

	/**
	 * {@inheritDoc}
	 */
	public function get_shortcode_tag() {
		return 'cafw_icon_box';
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_label() {
		return esc_html__( 'Icon Box', 'craft-addons-for-wpbakery' );
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_description() {
		return esc_html__( 'Icon with title, text and link.', 'craft-addons-for-wpbakery' );
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_admin_icon() {
		return 'dashicons-grid-view';
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_editor_icon() {
		return 'cafw-vc-icon cafw-vc-icon-icon-box';
	}

	/**
	 * {@inheritDoc}
	 */
	protected function get_defaults() {
		return array(
			'media_type'        => 'fontawesome',
			'icon'              => '', // legacy single-icon value (back-compat).
			'icon_fontawesome'  => 'fa fa-solid fa-star',
			'icon_openiconic'   => '',
			'icon_typicons'     => '',
			'icon_entypo'       => '',
			'icon_linecons'     => '',
			'image'             => '',
			'image_width'       => '64',
			'title'             => esc_html__( 'Feature title', 'craft-addons-for-wpbakery' ),
			'text'              => esc_html__( 'A short description of this feature goes here.', 'craft-addons-for-wpbakery' ),
			'link_type'         => 'box',
			'link_url'          => '',
			'link_blank'        => '',
			'link_nofollow'     => '',
			'link_opts'         => '', // legacy combined value (back-compat).
			'link'              => '', // legacy vc_link value (back-compat).
			'button_text'       => esc_html__( 'Learn more', 'craft-addons-for-wpbakery' ),
			'icon_position'     => 'top',
			'align'             => 'center',
			'gap'               => '',
			'icon_color'        => '#502bf8',
			'icon_size'         => '40',
			'icon_bg'           => '#ece8ff',
			'icon_padding'      => '18',
			'icon_radius'       => '50',
			'icon_border_color' => '',
			'icon_border_width' => '',
			'badge_enable'      => '',
			'badge_text'        => 'New',
			'badge_position'    => 'right',
			'badge_distance'    => '',
			'badge_bg'          => '#ef4444',
			'badge_color'       => '#ffffff',
			'button_bg'         => '#502bf8',
			'button_color'      => '#ffffff',
			'button_radius'     => '6',
			'button_hover_bg'   => '',
			'button_hover_color' => '',
			'box_hover'         => 'none',
			'icon_hover_color'  => '',
			'icon_bg_hover'     => '',
			'title_hover_color' => '',
			'title_font'        => '',
			'title_google_font' => '',
			'text_font'         => '',
			'text_google_font'  => '',
		);
	}

	/**
	 * Build the per-library icon picker params (only the selected one shows).
	 *
	 * @return array
	 */
	private function icon_pickers() {
		$labels = array(
			'fontawesome' => esc_html__( 'Icon (Font Awesome)', 'craft-addons-for-wpbakery' ),
			'openiconic'  => esc_html__( 'Icon (Open Iconic)', 'craft-addons-for-wpbakery' ),
			'typicons'    => esc_html__( 'Icon (Typicons)', 'craft-addons-for-wpbakery' ),
			'entypo'      => esc_html__( 'Icon (Entypo)', 'craft-addons-for-wpbakery' ),
			'linecons'    => esc_html__( 'Icon (Linecons)', 'craft-addons-for-wpbakery' ),
		);

		$pickers = array();
		foreach ( self::$libraries as $lib => $default ) {
			$pickers[] = array(
				'type'       => 'iconpicker',
				'heading'    => $labels[ $lib ],
				'param_name' => 'icon_' . $lib,
				'value'      => $default,
				'settings'   => array(
					'emptyIcon'    => false,
					'type'         => $lib,
					'iconsPerPage' => 200,
				),
				'dependency' => array(
					'element' => 'media_type',
					'value'   => $lib,
				),
			);
		}
		return $pickers;
	}

	/**
	 * {@inheritDoc}
	 */
	public function get_params() {
		// Two tabs only: General (content & structure) and Styles (appearance).
		$styles = esc_html__( 'Styles', 'craft-addons-for-wpbakery' );

		$button_dep = array(
			'element' => 'link_type',
			'value'   => 'button',
		);
		$badge_dep = array(
			'element' => 'badge_enable',
			'value'   => 'yes',
		);

		return array_merge(
			array(
				array(
					'type'       => 'dropdown',
					'heading'    => esc_html__( 'Media type', 'craft-addons-for-wpbakery' ),
					'param_name' => 'media_type',
					'value'      => array(
						esc_html__( 'Font Awesome', 'craft-addons-for-wpbakery' ) => 'fontawesome',
						esc_html__( 'Open Iconic', 'craft-addons-for-wpbakery' )  => 'openiconic',
						esc_html__( 'Typicons', 'craft-addons-for-wpbakery' )     => 'typicons',
						esc_html__( 'Entypo', 'craft-addons-for-wpbakery' )       => 'entypo',
						esc_html__( 'Linecons', 'craft-addons-for-wpbakery' )     => 'linecons',
						esc_html__( 'Image', 'craft-addons-for-wpbakery' )        => 'image',
					),
					'std'        => 'fontawesome',
				),
			),
			$this->icon_pickers(),
			array(
				array(
					'type'             => 'attach_image',
					'heading'          => esc_html__( 'Image', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'image',
					'dependency'       => array(
						'element' => 'media_type',
						'value'   => 'image',
					),
				),
				array(
					'type'             => 'textfield',
					'heading'          => esc_html__( 'Image width (px)', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'image_width',
					'value'            => '64',
					'dependency'       => array(
						'element' => 'media_type',
						'value'   => 'image',
					),
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
				array(
					'type'        => 'textfield',
					'heading'     => esc_html__( 'Title', 'craft-addons-for-wpbakery' ),
					'param_name'  => 'title',
					'value'       => esc_html__( 'Feature title', 'craft-addons-for-wpbakery' ),
					'admin_label' => true,
				),
				array(
					'type'       => 'textarea',
					'heading'    => esc_html__( 'Text', 'craft-addons-for-wpbakery' ),
					'param_name' => 'text',
					'value'      => esc_html__( 'A short description of this feature goes here.', 'craft-addons-for-wpbakery' ),
				),
				array(
					'type'       => 'dropdown',
					'heading'    => esc_html__( 'Link type', 'craft-addons-for-wpbakery' ),
					'param_name' => 'link_type',
					'value'      => array(
						esc_html__( 'None', 'craft-addons-for-wpbakery' )         => 'none',
						esc_html__( 'Title', 'craft-addons-for-wpbakery' )        => 'title',
						esc_html__( 'Button', 'craft-addons-for-wpbakery' )       => 'button',
						esc_html__( 'Whole box', 'craft-addons-for-wpbakery' )    => 'box',
					),
					'std'        => 'box',
				),
				array(
					'type'        => 'textfield',
					'heading'     => esc_html__( 'Link URL', 'craft-addons-for-wpbakery' ),
					'param_name'  => 'link_url',
					'description' => esc_html__( 'Paste the destination URL (e.g. https://example.com or /about).', 'craft-addons-for-wpbakery' ),
					'dependency'  => array(
						'element' => 'link_type',
						'value'   => array( 'title', 'button', 'box' ),
					),
				),
				array(
					'type'             => 'checkbox',
					'heading'          => esc_html__( 'Open in a new tab', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'link_blank',
					'value'            => array( esc_html__( 'Yes', 'craft-addons-for-wpbakery' ) => 'blank' ),
					'dependency'       => array(
						'element' => 'link_type',
						'value'   => array( 'title', 'button', 'box' ),
					),
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
				array(
					'type'             => 'checkbox',
					'heading'          => esc_html__( 'Add nofollow', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'link_nofollow',
					'value'            => array( esc_html__( 'Yes', 'craft-addons-for-wpbakery' ) => 'nofollow' ),
					'dependency'       => array(
						'element' => 'link_type',
						'value'   => array( 'title', 'button', 'box' ),
					),
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
				array(
					'type'        => 'textfield',
					'heading'     => esc_html__( 'Button text', 'craft-addons-for-wpbakery' ),
					'param_name'  => 'button_text',
					'value'       => esc_html__( 'Learn more', 'craft-addons-for-wpbakery' ),
					'dependency'  => $button_dep,
				),
				array(
					'type'             => 'dropdown',
					'heading'          => esc_html__( 'Icon position', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'icon_position',
					'value'            => array(
						esc_html__( 'Top', 'craft-addons-for-wpbakery' )   => 'top',
						esc_html__( 'Left', 'craft-addons-for-wpbakery' )  => 'left',
						esc_html__( 'Right', 'craft-addons-for-wpbakery' ) => 'right',
					),
					'std'              => 'top',
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
				array(
					'type'             => 'dropdown',
					'heading'          => esc_html__( 'Text alignment', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'align',
					'value'            => array(
						esc_html__( 'Center', 'craft-addons-for-wpbakery' ) => 'center',
						esc_html__( 'Left', 'craft-addons-for-wpbakery' )   => 'left',
						esc_html__( 'Right', 'craft-addons-for-wpbakery' )  => 'right',
					),
					'std'              => 'center',
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
				array(
					'type'             => 'textfield',
					'heading'          => esc_html__( 'Gap (px)', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'gap',
					'description'      => esc_html__( 'Even spacing between the icon, title, text and button.', 'craft-addons-for-wpbakery' ),
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
				// --- Icon (Styles): colours, sizes (2 x 2), then its hover colours ---
				array(
					'type'             => 'colorpicker',
					'heading'          => esc_html__( 'Icon color', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'icon_color',
					'value'            => '#502bf8',
					'group'            => $styles,
					'edit_field_class' => 'vc_col-sm-4 vc_column',
				),
				array(
					'type'             => 'colorpicker',
					'heading'          => esc_html__( 'Icon background', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'icon_bg',
					'value'            => '#ece8ff',
					'description'      => esc_html__( 'Optional - gives the icon a coloured badge.', 'craft-addons-for-wpbakery' ),
					'group'            => $styles,
					'edit_field_class' => 'vc_col-sm-4 vc_column',
				),
				array(
					'type'             => 'colorpicker',
					'heading'          => esc_html__( 'Icon border color', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'icon_border_color',
					'group'            => $styles,
					'edit_field_class' => 'vc_col-sm-4 vc_column',
				),
				array(
					'type'             => 'textfield',
					'heading'          => esc_html__( 'Icon size (px)', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'icon_size',
					'value'            => '40',
					'group'            => $styles,
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
				array(
					'type'             => 'textfield',
					'heading'          => esc_html__( 'Icon padding (px)', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'icon_padding',
					'value'            => '18',
					'group'            => $styles,
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
				array(
					'type'             => 'textfield',
					'heading'          => esc_html__( 'Icon radius (px)', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'icon_radius',
					'value'            => '50',
					'group'            => $styles,
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
				array(
					'type'             => 'textfield',
					'heading'          => esc_html__( 'Icon border width (px)', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'icon_border_width',
					'group'            => $styles,
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
				array(
					'type'             => 'colorpicker',
					'heading'          => esc_html__( 'Icon color (hover)', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'icon_hover_color',
					'group'            => $styles,
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
				array(
					'type'             => 'colorpicker',
					'heading'          => esc_html__( 'Icon background (hover)', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'icon_bg_hover',
					'group'            => $styles,
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
				// --- Badge (General: a simple on/off toggle, then its content) ---
				array(
					'type'             => 'checkbox',
					'heading'          => esc_html__( 'Badge', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'badge_enable',
					'value'            => array( esc_html__( 'Show a corner badge', 'craft-addons-for-wpbakery' ) => 'yes' ),
					'edit_field_class' => 'vc_col-xs-12 vc_column cafw-bg-sep',
				),
				array(
					'type'        => 'textfield',
					'heading'     => esc_html__( 'Badge text', 'craft-addons-for-wpbakery' ),
					'param_name'  => 'badge_text',
					'value'       => 'New',
					'dependency'  => $badge_dep,
				),
				array(
					'type'             => 'dropdown',
					'heading'          => esc_html__( 'Badge position', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'badge_position',
					'value'            => array(
						esc_html__( 'Right', 'craft-addons-for-wpbakery' ) => 'right',
						esc_html__( 'Left', 'craft-addons-for-wpbakery' )  => 'left',
					),
					'std'              => 'right',
					'dependency'       => $badge_dep,
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
				array(
					'type'             => 'textfield',
					'heading'          => esc_html__( 'Badge distance (px)', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'badge_distance',
					'description'      => esc_html__( 'Offset from the corner.', 'craft-addons-for-wpbakery' ),
					'dependency'       => $badge_dep,
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
			),
			// --- Title / Text (Styles): typography plus the title hover colour ---
			$this->typography_params(
				'title',
				esc_html__( 'Title', 'craft-addons-for-wpbakery' ),
				array(
					'tag' => true,
					'sep' => true,
				)
			),
			array(
				array(
					'type'             => 'colorpicker',
					'heading'          => esc_html__( 'Title color (hover)', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'title_hover_color',
					'group'            => $styles,
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
			),
			$this->typography_params( 'text', esc_html__( 'Text', 'craft-addons-for-wpbakery' ), array( 'sep' => true ) ),
			array(
				// --- Button (Styles, shown when Link type = Button) ---
				array(
					'type'             => 'colorpicker',
					'heading'          => esc_html__( 'Button background', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'button_bg',
					'value'            => '#502bf8',
					'group'            => $styles,
					'dependency'       => $button_dep,
					'edit_field_class' => 'vc_col-sm-6 vc_column cafw-bg-sep',
				),
				array(
					'type'             => 'colorpicker',
					'heading'          => esc_html__( 'Button text color', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'button_color',
					'value'            => '#ffffff',
					'group'            => $styles,
					'dependency'       => $button_dep,
					'edit_field_class' => 'vc_col-sm-6 vc_column cafw-bg-sep',
				),
				array(
					'type'             => 'textfield',
					'heading'          => esc_html__( 'Button radius (px)', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'button_radius',
					'value'            => '6',
					'group'            => $styles,
					'dependency'       => $button_dep,
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
				array(
					'type'             => 'colorpicker',
					'heading'          => esc_html__( 'Button background (hover)', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'button_hover_bg',
					'group'            => $styles,
					'dependency'       => $button_dep,
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
				array(
					'type'             => 'colorpicker',
					'heading'          => esc_html__( 'Button text color (hover)', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'button_hover_color',
					'group'            => $styles,
					'dependency'       => $button_dep,
					'edit_field_class' => 'vc_col-sm-6 vc_column',
				),
				// --- Badge colours (Styles, shown when the badge is on) ---
				array(
					'type'             => 'colorpicker',
					'heading'          => esc_html__( 'Badge background', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'badge_bg',
					'value'            => '#ef4444',
					'group'            => $styles,
					'dependency'       => $badge_dep,
					'edit_field_class' => 'vc_col-sm-6 vc_column cafw-bg-sep',
				),
				array(
					'type'             => 'colorpicker',
					'heading'          => esc_html__( 'Badge text color', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'badge_color',
					'value'            => '#ffffff',
					'group'            => $styles,
					'dependency'       => $badge_dep,
					'edit_field_class' => 'vc_col-sm-6 vc_column cafw-bg-sep',
				),
				// --- Box (Styles): whole-box hover behaviour. Background, border,
				// radius and spacing live in the Design Options tab. ---
				array(
					'type'             => 'dropdown',
					'heading'          => esc_html__( 'Box hover effect', 'craft-addons-for-wpbakery' ),
					'param_name'       => 'box_hover',
					'value'            => array(
						esc_html__( 'None', 'craft-addons-for-wpbakery' )          => 'none',
						esc_html__( 'Lift', 'craft-addons-for-wpbakery' )          => 'lift',
						esc_html__( 'Lift + shadow', 'craft-addons-for-wpbakery' ) => 'shadow',
					),
					'std'              => 'none',
					'group'            => $styles,
					'edit_field_class' => 'vc_col-xs-12 vc_column cafw-bg-sep',
				),
			)
		);
	}

	/**
	 * Resolve the chosen icon glyph class for the selected library (honouring an
	 * older single "icon" value for back-compat).
	 *
	 * @param array  $atts  Shortcode atts.
	 * @param string $media Media/library type.
	 * @return string
	 */
	private function resolve_icon( $atts, $media ) {
		$param = 'icon_' . $media;
		if ( isset( $atts[ $param ] ) && '' !== $atts[ $param ] ) {
			return $atts[ $param ];
		}
		return isset( $atts['icon'] ) ? (string) $atts['icon'] : '';
	}

	/**
	 * Build the open/target/rel attributes string for a resolved link.
	 *
	 * @param array $link vc_build_link() result.
	 * @return string
	 */
	private function link_attrs( $link ) {
		$out = ' href="' . esc_url( $link['url'] ) . '"';
		if ( ! empty( $link['target'] ) ) {
			$out .= ' target="' . esc_attr( $link['target'] ) . '"';
		}
		if ( ! empty( $link['rel'] ) ) {
			$out .= ' rel="' . esc_attr( $link['rel'] ) . '"';
		}
		return $out;
	}

	/**
	 * {@inheritDoc}
	 */
	public function render( $atts, $content, $el_id ) {
		$media    = array_key_exists( $atts['media_type'], self::$libraries ) || 'image' === $atts['media_type'] ? $atts['media_type'] : 'fontawesome';
		$is_image = ( 'image' === $media );
		$align    = in_array( $atts['align'], array( 'center', 'left', 'right' ), true ) ? $atts['align'] : 'center';
		$position = in_array( $atts['icon_position'], array( 'top', 'left', 'right' ), true ) ? $atts['icon_position'] : 'top';

		// Build the link from the simple URL + options fields, falling back to an
		// older vc_link value for back-compat.
		$url = trim( (string) $atts['link_url'] );
		if ( '' !== $url ) {
			$legacy   = (string) $atts['link_opts']; // older combined checkbox.
			$blank    = false !== strpos( (string) $atts['link_blank'], 'blank' ) || false !== strpos( $legacy, 'blank' );
			$nofollow = false !== strpos( (string) $atts['link_nofollow'], 'nofollow' ) || false !== strpos( $legacy, 'nofollow' );
			$link     = array(
				'url'    => $url,
				'target' => $blank ? '_blank' : '',
				'rel'    => $nofollow ? 'nofollow' : '',
			);
		} elseif ( ! empty( $atts['link'] ) && function_exists( 'vc_build_link' ) ) {
			$link = vc_build_link( $atts['link'] );
		} else {
			$link = array(
				'url'    => '',
				'target' => '',
				'rel'    => '',
			);
		}
		$link_type = in_array( $atts['link_type'], array( 'none', 'title', 'button', 'box' ), true ) ? $atts['link_type'] : 'box';
		$has_url   = ! empty( $link['url'] );

		// Resolve the media (icon glyph or image markup).
		$icon     = '';
		$img_html = '';
		if ( $is_image ) {
			$img_id = absint( $atts['image'] );
			if ( $img_id ) {
				$width    = absint( $atts['image_width'] );
				$img_html = wp_get_attachment_image(
					$img_id,
					'medium',
					false,
					array(
						'class' => 'cafw-icon-box__img',
						'style' => $width ? 'width:' . $width . 'px;height:auto' : '',
					)
				);
			}
		} else {
			$icon = $this->resolve_icon( $atts, $media );
			if ( '' !== $icon && function_exists( 'vc_icon_element_fonts_enqueue' ) ) {
				vc_icon_element_fonts_enqueue( $media );
			}
		}
		$has_media = ( '' !== $icon || '' !== $img_html );

		// Badge / icon styles on the media wrapper.
		$media_styles = array();
		if ( ! $is_image ) {
			$ic = sanitize_hex_color( $atts['icon_color'] );
			if ( $ic ) {
				$media_styles['color'] = $ic;
			}
			$isize = absint( $atts['icon_size'] );
			if ( $isize ) {
				$media_styles['font-size'] = $isize . 'px';
			}
		}
		$ibg = sanitize_hex_color( $atts['icon_bg'] );
		if ( $ibg ) {
			$media_styles['background-color'] = $ibg;
		}
		if ( '' !== trim( (string) $atts['icon_padding'] ) ) {
			$media_styles['padding'] = absint( $atts['icon_padding'] ) . 'px';
		}
		if ( '' !== trim( (string) $atts['icon_radius'] ) ) {
			$media_styles['border-radius'] = absint( $atts['icon_radius'] ) . 'px';
		}
		$ibw = absint( $atts['icon_border_width'] );
		$ibc = sanitize_hex_color( $atts['icon_border_color'] );
		if ( $ibw && $ibc ) {
			$media_styles['border'] = $ibw . 'px solid ' . $ibc;
		}

		// A visible background or border makes the wrapper a "shape" - keep it
		// square (see CSS) so a large radius gives a true circle even when the
		// glyph or image itself is not square.
		$media_class = 'cafw-icon-box__media';
		if ( $ibg || ( $ibw && $ibc ) ) {
			$media_class .= ' cafw-icon-box__media--shaped';
		}

		// Even gap between the icon, title, text and button (root + content flex).
		$root_styles    = array();
		$content_styles = array();
		if ( '' !== trim( (string) $atts['gap'] ) ) {
			$gap                    = absint( $atts['gap'] ) . 'px';
			$root_styles['gap']     = $gap;
			$content_styles['gap']  = $gap;
		}

		// Badge (only when the toggle is on and there is text).
		$badge_html = '';
		if ( false !== strpos( (string) $atts['badge_enable'], 'yes' ) && '' !== trim( (string) $atts['badge_text'] ) ) {
			$bpos    = 'left' === $atts['badge_position'] ? 'left' : 'right';
			$bstyles = array();
			$bbg     = sanitize_hex_color( $atts['badge_bg'] );
			if ( $bbg ) {
				$bstyles['background-color'] = $bbg;
			}
			$bcolor = sanitize_hex_color( $atts['badge_color'] );
			if ( $bcolor ) {
				$bstyles['color'] = $bcolor;
			}
			if ( '' !== trim( (string) $atts['badge_distance'] ) ) {
				$d                = absint( $atts['badge_distance'] ) . 'px';
				$bstyles['top']   = $d;
				$bstyles[ $bpos ] = $d;
			}
			$badge_html = '<span class="cafw-icon-box__badge cafw-icon-box__badge--' . $bpos . '"'
				. $this->style_attr( $bstyles ) . '>' . esc_html( $atts['badge_text'] ) . '</span>';
		}

		// Button (only when Link type = Button).
		$button_html = '';
		$show_button = ( 'button' === $link_type && $has_url && '' !== trim( (string) $atts['button_text'] ) );
		if ( $show_button ) {
			$btn_styles = array();
			$bg         = sanitize_hex_color( $atts['button_bg'] );
			if ( $bg ) {
				$btn_styles['background-color'] = $bg;
			}
			$bc = sanitize_hex_color( $atts['button_color'] );
			if ( $bc ) {
				$btn_styles['color'] = $bc;
			}
			if ( '' !== trim( (string) $atts['button_radius'] ) ) {
				$btn_styles['border-radius'] = absint( $atts['button_radius'] ) . 'px';
			}
			$button_html = '<a class="cafw-icon-box__button"' . $this->link_attrs( $link )
				. $this->style_attr( $btn_styles ) . '>' . esc_html( $atts['button_text'] ) . '</a>';
		}

		// Hover colours / button hover, emitted as a scoped style block.
		$hover = '';
		$ihc   = sanitize_hex_color( $atts['icon_hover_color'] );
		$ibgh  = sanitize_hex_color( $atts['icon_bg_hover'] );
		$thc   = sanitize_hex_color( $atts['title_hover_color'] );
		if ( $ihc ) {
			$hover .= '#' . $el_id . ':hover .cafw-icon-box__media{color:' . $ihc . '!important}';
		}
		if ( $ibgh ) {
			$hover .= '#' . $el_id . ':hover .cafw-icon-box__media{background-color:' . $ibgh . '!important}';
		}
		if ( $thc ) {
			$hover .= '#' . $el_id . ':hover .cafw-icon-box__title{color:' . $thc . '!important}';
		}
		if ( $show_button ) {
			$bhbg = sanitize_hex_color( $atts['button_hover_bg'] );
			$bhc  = sanitize_hex_color( $atts['button_hover_color'] );
			if ( $bhbg ) {
				$hover .= '#' . $el_id . ' .cafw-icon-box__button:hover{background-color:' . $bhbg . '!important}';
			}
			if ( $bhc ) {
				$hover .= '#' . $el_id . ' .cafw-icon-box__button:hover{color:' . $bhc . '!important}';
			}
		}

		$hover_class = '';
		if ( 'lift' === $atts['box_hover'] ) {
			$hover_class = ' cafw-icon-box--hover-lift';
		} elseif ( 'shadow' === $atts['box_hover'] ) {
			$hover_class = ' cafw-icon-box--hover-lift cafw-icon-box--hover-shadow';
		}

		$title_type = $this->part_typography( $atts, 'title' );
		$text_type  = $this->part_typography( $atts, 'text' );
		$title_tag  = in_array( $title_type['tag'], array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'div' ), true ) ? $title_type['tag'] : 'h3';

		// Title (optionally a link).
		$title_inner = esc_html( $atts['title'] );
		if ( 'title' === $link_type && $has_url ) {
			$title_inner = '<a' . $this->link_attrs( $link ) . '>' . esc_html( $atts['title'] ) . '</a>';
		}

		$base_class = trim(
			'cafw-icon-box cafw-icon-box--' . $position . ' cafw-icon-box--align-' . $align . $hover_class
			. ' ' . CAFW_Element_Base::design_options_class( $atts )
			. ' ' . $atts['el_class']
		);
		$base_class = preg_replace( '/\s+/', ' ', $base_class );

		$has_content = ( '' !== $atts['title'] || '' !== $atts['text'] || $show_button );

		if ( '' !== $hover ) {
			CAFW_Element_Base::add_inline_style( $hover );
		}

		ob_start();
		?>
		<div id="<?php echo esc_attr( $el_id ); ?>" class="<?php echo esc_attr( $base_class ); ?>"<?php echo $this->style_attr( $root_styles ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- style_attr escapes internally. ?>>
			<?php echo $badge_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- badge text esc_html'd, styles escaped. ?>

			<?php if ( $has_media ) : ?>
				<div class="<?php echo esc_attr( $media_class ); ?>"<?php echo $this->style_attr( $media_styles ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- style_attr escapes internally. ?>>
					<?php
					if ( $is_image ) {
						echo $img_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- wp_get_attachment_image returns safe markup.
					} else {
						?>
						<span class="<?php echo esc_attr( $icon ); ?>"></span>
						<?php
					}
					?>
				</div>
			<?php endif; ?>

			<?php if ( $has_content ) : ?>
				<div class="cafw-icon-box__content"<?php echo $this->style_attr( $content_styles ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- style_attr escapes internally. ?>>
					<?php if ( '' !== $atts['title'] ) : ?>
						<<?php echo esc_attr( $title_tag ); ?> class="cafw-icon-box__title"<?php echo $this->style_attr( $title_type['styles'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- style_attr escapes internally. ?>><?php echo $title_inner; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- title esc_html'd, link attrs escaped. ?></<?php echo esc_attr( $title_tag ); ?>>
					<?php endif; ?>

					<?php if ( '' !== $atts['text'] ) : ?>
						<div class="cafw-icon-box__text"<?php echo $this->style_attr( $text_type['styles'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- style_attr escapes internally. ?>><?php echo wp_kses_post( wpautop( $atts['text'] ) ); ?></div>
					<?php endif; ?>

					<?php echo $button_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- button text esc_html'd, attrs/styles escaped. ?>
				</div>
			<?php endif; ?>

			<?php if ( 'box' === $link_type && $has_url ) : ?>
				<a class="cafw-icon-box__link"<?php echo $this->link_attrs( $link ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- link_attrs escapes internally. ?>><span class="screen-reader-text"><?php echo esc_html( $atts['title'] ); ?></span></a>
			<?php endif; ?>
		</div>
		<?php
		return ob_get_clean();
	}
}
