/**
 * Agency Pulse — Admin Menu Organizer — Builder UI
 *
 * Self-contained IIFE. All data supplied via aamoData (wp_localize_script).
 *
 * Initialisation order on DOM ready:
 *   1. restoreExpandState  — collapse all sub-lists, re-open saved ones
 *   2. initSortable        — activate drag-and-drop on top list + sub-lists
 *   3. bindEvents          — wire all click/change handlers
 *
 * PHP already renders items in saved config order (build_merged_list), so JS
 * does not need to reorder the DOM on load — only expand/collapse state is
 * a client-side concern restored from localStorage.
 *
 * @package Agency_Pulse_Admin_Menu
 * @since   1.0.0
 */

/* global aamoData, jQuery */
( function ( $, data ) {
	'use strict';

	// Complete WordPress Dashicons list (suffix only; 'dashicons-' is prepended at render time).
	var AAMO_DASHICONS = [
		// Administration
		'admin-appearance','admin-collapse','admin-comments','admin-customizer',
		'admin-generic','admin-home','admin-links','admin-media','admin-multisite',
		'admin-network','admin-page','admin-plugins','admin-post','admin-settings',
		'admin-site','admin-site-alt','admin-site-alt2','admin-site-alt3',
		'admin-tools','admin-users',
		// Welcome screen
		'welcome-add-page','welcome-comments','welcome-learn-more','welcome-view-site',
		'welcome-widgets-menus','welcome-write-blog',
		// Post formats
		'format-aside','format-audio','format-chat','format-gallery','format-image',
		'format-links','format-quote','format-standard','format-status','format-video',
		// Media
		'media-archive','media-audio','media-code','media-default','media-document',
		'media-interactive','media-spreadsheet','media-text','media-video',
		// Image editing
		'image-crop','image-filter','image-flip-horizontal','image-flip-vertical',
		'image-rotate','image-rotate-left','image-rotate-right',
		// Notifications / post status / taxonomies
		'bell','warning','post-status','category','tag','tagcloud',
		// Ratings / stars
		'star-empty','star-filled','star-half',
		// Users / groups
		'businessman','groups','id','id-alt','nametag','universal-access','universal-access-alt',
		// Views / lists
		'archive','index-card','layout','list-view','grid-view','excerpt-view',
		// Arrows
		'arrow-down','arrow-down-alt','arrow-down-alt2',
		'arrow-left','arrow-left-alt','arrow-left-alt2',
		'arrow-right','arrow-right-alt','arrow-right-alt2',
		'arrow-up','arrow-up-alt','arrow-up-alt2',
		// Social
		'facebook','facebook-alt','google','instagram','reddit','spotify',
		'twitter','xing','youtube',
		// Editor
		'editor-aligncenter','editor-alignleft','editor-alignright','editor-bold',
		'editor-break','editor-code','editor-contract','editor-customchar',
		'editor-expand','editor-help','editor-indent','editor-insertmore',
		'editor-italic','editor-justify','editor-kitchensink','editor-ltr',
		'editor-ol','editor-ol-rtl','editor-outdent','editor-paragraph',
		'editor-paste-text','editor-paste-word','editor-quote',
		'editor-removeformatting','editor-rtl','editor-spellcheck',
		'editor-strikethrough','editor-table','editor-textcolor','editor-ul',
		'editor-underline','editor-unlink','editor-video',
		// BuddyPress / bbPress
		'buddicons-activity','buddicons-bbpress-logo','buddicons-buddypress-logo',
		'buddicons-community','buddicons-forums','buddicons-friends',
		'buddicons-groups','buddicons-help','buddicons-loader','buddicons-pm',
		'buddicons-replies','buddicons-topics','buddicons-tracking',
		// Misc / generic
		'align-center','align-full-width','align-left','align-none',
		'align-pull-left','align-pull-right','align-right','align-wide',
		'analytics','art','awards','backup','book','book-alt','bookmarks',
		'button','calculator','calendar','calendar-alt','camera','carrot',
		'cart','chart-area','chart-bar','chart-line','chart-pie',
		'clipboard','clock','cloud','coffee','color-picker',
		'controls-back','controls-forward','controls-pause','controls-play',
		'controls-repeat','controls-skipback','controls-skipforward',
		'controls-volumemax','controls-volumemin',
		'dashboard','database','desktop','dismiss','download','edit',
		'ellipsis','email','email-alt','email-alt2',
		'embed-audio','embed-generic','embed-photo','embed-post','embed-video',
		'empty-cart','external','feedback','filter','flag','forms',
		'fullscreen-alt','fullscreen-exit-alt','games','hammer','heart',
		'hidden','html','humor','info','keyboard','laptop','leftright',
		'lightbulb','location','location-alt','lock','lock-duplicate',
		'marker','megaphone','menu','menu-alt','menu-alt2','menu-alt3',
		'microphone','migrate','minus','money','money-alt','move','music',
		'networking','no','no-alt','open-folder','palmtree','paperclip',
		'performance','phone','piggy-bank','playlist-audio','playlist-video',
		'plus','plus-alt','plus-alt2','portfolio','pressthis','products',
		'rss','search','share','share-alt','share-alt2','shield','shield-alt',
		'shortcode','smartphone','smiley','sort','sos','store',
		'superhero','superhero-alt','tablet','text','text-page',
		'tickets-alt','tide','translation','undo','update','update-alt',
		'upload','video-alt','video-alt2','video-alt3','visibility',
		'wordpress','wordpress-alt','wrench',
	];

	var Builder = {

		$list:            null,
		$saveBtn:         null,
		$resetBtn:        null,
		$addContainerBtn: null,
		$status:          null,
		$configSelect:    null,
		isDirty:          false,

		// -----------------------------------------------------------------
		// Initialisation
		// -----------------------------------------------------------------

		init: function () {
			this.$list            = $( '#aamo-sortable' );
			this.$saveBtn         = $( '#aamo-save' );
			this.$resetBtn        = $( '#aamo-reset' );
			this.$addContainerBtn = $( '#aamo-add-container' );
			this.$status          = $( '#aamo-status' );
			this.$configSelect    = $( '#aamo-config-select' );
			this.$pickerTarget    = null;

			if ( ! this.$list.length ) {
				return;
			}

			this.restoreExpandState();
			this.initSortable();
			this.bindEvents();
			this.initLabelEditing();
			this.initIconPicker();
			this.initIconEditing();
		},

		// -----------------------------------------------------------------
		// Expand / collapse
		// -----------------------------------------------------------------

		/**
		 * Collapse all sub-item lists, then re-open any that were saved as
		 * expanded in localStorage for the current config.
		 */
		restoreExpandState: function () {
			var saved = this.loadExpandState();

			// Start with everything collapsed.
			this.$list.find( '.aamo-submenu-list' ).addClass( 'aamo-collapsed' );

			// Re-open saved expanded items.
			var self = this;
			this.$list.find( '> .aamo-menu-item' ).each( function () {
				if ( saved[ $( this ).data( 'slug' ) ] ) {
					self.openItem( $( this ), /* save= */ false );
				}
			} );
		},

		openItem: function ( $item, doSave ) {
			$item.addClass( 'aamo-expanded' );
			var $sub = $item.children( '.aamo-submenu-list' );
			$sub.removeClass( 'aamo-collapsed' );
			if ( $sub.data( 'ui-sortable' ) ) {
				$sub.sortable( 'enable' );
			}
			if ( doSave !== false ) {
				this.saveExpandState();
			}
		},

		closeItem: function ( $item, doSave ) {
			$item.removeClass( 'aamo-expanded' );
			var $sub = $item.children( '.aamo-submenu-list' );
			$sub.addClass( 'aamo-collapsed' );
			if ( $sub.data( 'ui-sortable' ) ) {
				$sub.sortable( 'disable' );
			}
			if ( doSave !== false ) {
				this.saveExpandState();
			}
		},

		toggleExpand: function ( $item ) {
			if ( $item.hasClass( 'aamo-expanded' ) ) {
				this.closeItem( $item );
			} else {
				this.openItem( $item );
			}
		},

		loadExpandState: function () {
			try {
				return JSON.parse(
					localStorage.getItem( 'aamo_expand_' + data.activeConfigId ) || '{}'
				);
			} catch ( e ) {
				return {};
			}
		},

		saveExpandState: function () {
			var state = {};
			this.$list.find( '> .aamo-menu-item.aamo-expanded' ).each( function () {
				state[ $( this ).data( 'slug' ) ] = true;
			} );
			try {
				localStorage.setItem(
					'aamo_expand_' + data.activeConfigId,
					JSON.stringify( state )
				);
			} catch ( e ) {}
		},

		// -----------------------------------------------------------------
		// Drag-and-drop
		// -----------------------------------------------------------------

		initSortable: function () {
			var self = this;

			// Top-level list — separators and custom containers participate as normal items.
			this.$list.sortable( {
				handle:      '.aamo-handle',
				placeholder: 'aamo-sortable-placeholder',
				axis:        'y',
				items:       '> .aamo-menu-item',
				update: function () {
					self.markUnsaved();
				},
			} );

			// Sub-item lists — connected for cross-container drag.
			this.$list.find( '.aamo-submenu-list' ).each( function () {
				self.initSubListSortable( $( this ) );
			} );
		},

		initSubListSortable: function ( $subList ) {
			var self = this;
			$subList.sortable( {
				handle:      '.aamo-handle',
				placeholder: 'aamo-sortable-placeholder',
				connectWith: '.aamo-submenu-list',
				items:       '> .aamo-submenu-item',
				start: function ( event, ui ) {
					// Stamp the origin parent before the item moves.
					var $originParent = $subList.closest( '.aamo-menu-item' );
					ui.item.data( '_drag-origin', $originParent.data( 'slug' ) );
					ui.item.data( '_drag-origin-is-custom', !! $originParent.data( 'custom-container' ) );
				},
				receive: function ( event, ui ) {
					// Item arrived from a different parent list.
					var $newParent  = $subList.closest( '.aamo-menu-item' );
					var newIsCustom = !! $newParent.data( 'custom-container' );
					ui.item.attr( 'data-parent-slug', $newParent.data( 'slug' ) );
					if ( newIsCustom ) {
						// Moving into a custom container — record original native parent
						// only if one is not already set (preserve across container moves).
						if ( ! ui.item.attr( 'data-original-parent' ) ) {
							ui.item.attr( 'data-original-parent', ui.item.data( '_drag-origin' ) );
						}
					} else {
						// Moved back to a native parent — clear original-parent marker.
						ui.item.removeAttr( 'data-original-parent' );
					}
					self.markUnsaved();
				},
				update: function ( event, ui ) {
					// Rearranged within the same list (ui.sender is null).
					if ( ! ui.sender ) {
						self.markUnsaved();
					}
				},
			} );

			// Disable sortable on collapsed lists so they cannot receive drops.
			var $parent = $subList.closest( '.aamo-menu-item' );
			if ( $subList.hasClass( 'aamo-collapsed' ) && ! $parent.hasClass( 'aamo-expanded' ) ) {
				$subList.sortable( 'disable' );
			}
		},

		// -----------------------------------------------------------------
		// Visibility toggle
		// -----------------------------------------------------------------

		toggleVisibility: function ( $item, $btn ) {
			var nowHidden = ! $item.hasClass( 'aamo-item-hidden' );
			$item.toggleClass( 'aamo-item-hidden', nowHidden );

			var $icon = $btn.find( '.dashicons' );
			$icon.toggleClass( 'dashicons-visibility', ! nowHidden );
			$icon.toggleClass( 'dashicons-hidden', nowHidden );
			$btn.attr( 'aria-label', nowHidden ? data.i18n.showItem : data.i18n.hideItem );

			this.markUnsaved();
		},

		// -----------------------------------------------------------------
		// Custom container creation
		// -----------------------------------------------------------------

		addContainer: function () {
			var name = window.prompt( data.i18n.containerName, '' );
			if ( ! name || ! name.trim() ) {
				return;
			}
			name = name.trim();

			var slug      = 'aamo_container_' + Date.now();
			var safeTitle = $( '<div>' ).text( name ).html();
			var safeBadge = $( '<div>' ).text( data.i18n.containerBadge ).html();
			var safeSlug  = $( '<div>' ).text( slug ).html();

			var $item = $( [
				'<li class="aamo-menu-item aamo-custom-container"',
					' data-slug="' + slug + '"',
					' data-hidden="false"',
					' data-custom-container="true"',
					' data-custom-label="' + safeTitle + '"',
					' data-icon="dashicons-category"',
					' data-original-icon="">',
					'<span class="aamo-handle dashicons dashicons-menu"></span>',
					'<button type="button" class="aamo-expand-btn" aria-expanded="false">',
						'<span class="dashicons dashicons-arrow-right-alt2"></span>',
					'</button>',
					'<span class="aamo-item-icon dashicons dashicons-category" title="' + ( data.i18n.dblClickIcon || 'dbl click to change icon' ) + '"></span>',
					'<span class="aamo-container-badge">' + safeBadge + '</span>',
					'<button type="button" class="aamo-label-edit-btn" title="' + ( data.i18n.editLabel || 'Rename label' ) + '" aria-label="' + ( data.i18n.editLabel || 'Rename label' ) + '">',
						'<span class="dashicons dashicons-edit"></span>',
					'</button>',
					'<span class="aamo-title-group">',
						'<span class="aamo-item-title">' + safeTitle + '</span>',
					'</span>',
					'<span class="aamo-item-slug">' + safeSlug + '</span>',
					'<button type="button" class="aamo-visibility-btn" aria-label="' + data.i18n.hideItem + '">',
						'<span class="dashicons dashicons-visibility"></span>',
					'</button>',
					'<ul class="aamo-submenu-list aamo-collapsed"></ul>',
				'</li>',
			].join( '' ) );

			this.$list.append( $item );
			this.initSubListSortable( $item.find( '.aamo-submenu-list' ) );
			this.$list.sortable( 'refresh' );
			this.markUnsaved();
		},

		removeContainer: function ( $item ) {
			var self     = this;
			var $children = $item.find( '> .aamo-submenu-list > .aamo-submenu-item' );

			$children.each( function () {
				var $child         = $( this );
				var originalParent = $child.attr( 'data-original-parent' );
				if ( ! originalParent ) {
					return; // No known origin — discard with the container.
				}
				var $target = self.$list.find(
					'> .aamo-menu-item[data-slug="' + originalParent + '"]'
				);
				if ( ! $target.length ) {
					return; // Original parent not in list — discard.
				}
				var $subList = $target.find( '> .aamo-submenu-list' );
				if ( ! $subList.length ) {
					$subList = $( '<ul class="aamo-submenu-list aamo-collapsed"></ul>' );
					$target.append( $subList );
					self.initSubListSortable( $subList );
					// Replace expand placeholder with a real expand button.
					$target.find( '.aamo-expand-placeholder' ).replaceWith(
						'<button type="button" class="aamo-expand-btn" aria-expanded="false">'
						+ '<span class="dashicons dashicons-arrow-right-alt2"></span></button>'
					);
				}
				$child.removeAttr( 'data-original-parent' );
				$subList.append( $child );
				$subList.sortable( 'refresh' );
			} );

			$item.remove();
			this.$list.sortable( 'refresh' );
			this.markUnsaved();
		},

		// -----------------------------------------------------------------
		// Config selector
		// -----------------------------------------------------------------

		onConfigChange: function () {
			this.redirectToConfig( this.$configSelect.val() );
		},

		newConfig: function () {
			var name = window.prompt( data.i18n.newConfigName, '' );
			if ( ! name || ! name.trim() ) {
				return;
			}
			var self = this;
			this.post( data.actions.createConfig, { name: name.trim() }, {
				success: function ( resp ) {
					if ( resp.data && resp.data.config_id ) {
						self.isDirty = false;
						self.redirectToConfig( resp.data.config_id );
					} else {
						self.showError( resp );
					}
				},
			} );
		},

		renameConfig: function () {
			var current = this.$configSelect.find( ':selected' ).text().trim();
			var name    = window.prompt( data.i18n.renamePrompt, current );
			if ( ! name || ! name.trim() || name.trim() === current ) {
				return;
			}
			name = name.trim();
			var self = this;
			this.post( data.actions.renameConfig, { config_id: data.activeConfigId, name: name }, {
				success: function ( resp ) {
					if ( resp.success ) {
						self.$configSelect.find( ':selected' ).text( name );
						self.$status.text( data.i18n.saved );
					} else {
						self.showError( resp );
					}
				},
			} );
		},

		duplicateConfig: function () {
			var current = this.$configSelect.find( ':selected' ).text().trim();
			var name    = window.prompt( data.i18n.duplicatePrompt, data.i18n.copyOf + ' ' + current );
			if ( ! name || ! name.trim() ) {
				return;
			}
			var self = this;
			this.post( data.actions.duplicateConfig, { config_id: data.activeConfigId, new_name: name.trim() }, {
				success: function ( resp ) {
					if ( resp.data && resp.data.config_id ) {
						self.isDirty = false;
						self.redirectToConfig( resp.data.config_id );
					} else {
						self.showError( resp );
					}
				},
			} );
		},

		deleteConfig: function () {
			if ( ! window.confirm( data.i18n.confirmDelete ) ) {
				return;
			}
			var self = this;
			this.post( data.actions.deleteConfig, { config_id: data.activeConfigId }, {
				success: function ( resp ) {
					if ( resp.success ) {
						self.isDirty = false;
						self.redirectToConfig( '' );
					} else {
						self.showError( resp );
					}
				},
			} );
		},

		// -----------------------------------------------------------------
		// Config state collection
		// -----------------------------------------------------------------

		collectConfig: function () {
			var config = [];

			this.$list.find( '> .aamo-menu-item' ).each( function ( index ) {
				var $item    = $( this );
				var children = [];

				$item.find( '> .aamo-submenu-list > .aamo-submenu-item' ).each( function ( ci ) {
					var $child = $( this );
					children.push( {
						slug:     $child.data( 'slug' ),
						position: ci,
						label:    $child.data( 'custom-label' ) || null,
						hidden:   $child.hasClass( 'aamo-item-hidden' ),
					} );
				} );

				config.push( {
					slug:             $item.data( 'slug' ),
					position:         index,
					label:            $item.data( 'custom-label' ) || null,
					hidden:           $item.hasClass( 'aamo-item-hidden' ),
					separator:        $item.hasClass( 'aamo-separator' ),
					custom_container: $item.data( 'custom-container' ) === true
					                  || $item.data( 'custom-container' ) === 'true',
					icon:             ( function () {
						var cur = $item.data( 'icon' ) || '';
						var orig = $item.data( 'original-icon' ) || '';
						return ( cur && cur !== orig ) ? cur : null;
					}() ),
					children:         children,
				} );
			} );

			return config;
		},

		// -----------------------------------------------------------------
		// Save / Reset
		// -----------------------------------------------------------------

		saveConfig: function () {
			var self   = this;
			var config = this.collectConfig();

			this.$saveBtn.prop( 'disabled', true );
			this.$status.text( data.i18n.saving );

			this.post( data.actions.saveConfig, {
				config_id: data.activeConfigId,
				config:    JSON.stringify( config ),
			}, {
				success: function ( resp ) {
					if ( resp.success ) {
						self.isDirty = false;
						window.location.reload();
					} else {
						self.showError( resp );
					}
				},
				always: function () {
					self.$saveBtn.prop( 'disabled', false );
				},
			} );
		},

		resetConfig: function () {
			if ( ! window.confirm( data.i18n.confirmReset ) ) {
				return;
			}
			var self = this;
			this.post( data.actions.resetConfig, { config_id: data.activeConfigId }, {
				success: function ( resp ) {
					if ( resp.success ) {
						self.isDirty = false;
						window.location.reload();
					} else {
						self.showError( resp );
					}
				},
			} );
		},

		// -----------------------------------------------------------------
		// Event binding
		// -----------------------------------------------------------------

		bindEvents: function () {
			var self = this;

			// Expand / collapse.
			this.$list.on( 'click', '.aamo-expand-btn', function ( e ) {
				e.stopPropagation();
				self.toggleExpand( $( this ).closest( '.aamo-menu-item' ) );
			} );

			// Visibility toggle.
			this.$list.on( 'click', '.aamo-visibility-btn', function ( e ) {
				e.stopPropagation();
				var $btn  = $( this );
				var $item = $btn.closest( '.aamo-menu-item, .aamo-submenu-item' );
				if ( $item.hasClass( 'aamo-item-locked' ) || $item.hasClass( 'aamo-subitem-locked' ) ) { return; }
				self.toggleVisibility( $item, $btn );
			} );

			// Config selector dropdown.
			this.$configSelect.on( 'change', function () {
				self.onConfigChange();
			} );

			// Config management buttons.
			$( '#aamo-new-config' ).on(       'click', function () { self.newConfig(); } );
			$( '#aamo-rename-config' ).on(    'click', function () { self.renameConfig(); } );
			$( '#aamo-duplicate-config' ).on( 'click', function () { self.duplicateConfig(); } );
			$( '#aamo-delete-config' ).on(    'click', function () { self.deleteConfig(); } );

			// Add container.
			this.$addContainerBtn.on( 'click', function () {
				self.addContainer();
			} );

			// Remove custom container.
			this.$list.on( 'click', '.aamo-remove-container-btn', function ( e ) {
				e.stopPropagation();
				self.removeContainer( $( this ).closest( '.aamo-menu-item' ) );
			} );

			// Save / Reset.
			this.$saveBtn.on(   'click', function () { self.saveConfig(); } );
			this.$resetBtn.on(  'click', function () { self.resetConfig(); } );

			// Warn on page leave with unsaved changes.
			$( window ).on( 'beforeunload', function ( e ) {
				if ( self.isDirty ) {
					var msg = data.i18n.unsavedWarning;
					e.returnValue = msg;
					return msg;
				}
			} );
		},

		// -----------------------------------------------------------------
		// Inline label editing
		// -----------------------------------------------------------------

		/**
		 * Delegate dblclick on any .aamo-item-title that is not already in edit mode.
		 */
		initLabelEditing: function () {
			var self = this;
			// Double-click on the title text.
			this.$list.on(
				'dblclick',
				'.aamo-menu-item .aamo-item-title, .aamo-submenu-item .aamo-item-title',
				function ( e ) {
					e.stopPropagation();
					var $row = $( this ).closest( '.aamo-menu-item, .aamo-submenu-item' );
					if ( $row.hasClass( 'aamo-item-locked' ) || $row.hasClass( 'aamo-subitem-locked' ) ) { return; }
					if ( $row.find( '.aamo-label-input' ).length ) {
						return;
					}
					self.enterLabelEdit( $( this ) );
				}
			);
			// Click on the pencil edit button.
			this.$list.on(
				'click',
				'.aamo-label-edit-btn',
				function ( e ) {
					e.stopPropagation();
					var $row = $( this ).closest( '.aamo-menu-item, .aamo-submenu-item' );
					if ( $row.hasClass( 'aamo-item-locked' ) || $row.hasClass( 'aamo-subitem-locked' ) ) { return; }
					if ( $row.find( '.aamo-label-input' ).length ) {
						return;
					}
					self.enterLabelEdit( $row.children( '.aamo-title-group' ).children( '.aamo-item-title' ) );
				}
			);

			// Click on the original-title hint to restore the original label.
			this.$list.on(
				'click',
				'.aamo-original-title',
				function ( e ) {
					e.stopPropagation();
					var $subtitle     = $( this );
					var $titleGroup   = $subtitle.closest( '.aamo-title-group' );
					var $row          = $subtitle.closest( '.aamo-menu-item, .aamo-submenu-item' );
					var originalTitle = $row.data( 'original-title' ) || '';

					self.cancelAnyOpenEdit();

					$row.removeData( 'custom-label' ).removeAttr( 'data-custom-label' );
					$titleGroup.children( '.aamo-item-title' ).text( originalTitle );
					$subtitle.remove();
					self.markUnsaved();
				}
			);
		},

		/**
		 * Cancel any currently active label edit without saving.
		 *
		 * Called at the start of every enterLabelEdit() so at most one edit is
		 * open at a time.
		 */
		cancelAnyOpenEdit: function () {
			var $openInput = this.$list.find( '.aamo-label-input' );
			if ( ! $openInput.length ) {
				return;
			}
			var $titleSpan = $openInput.closest( '.aamo-title-group' ).children( '.aamo-item-title' );
			this.cancelLabelEdit( $openInput, $titleSpan );
		},

		/**
		 * Replace the title span with an inline input + confirm button.
		 *
		 * @param {jQuery} $titleSpan The .aamo-item-title span for the row being edited.
		 */
		enterLabelEdit: function ( $titleSpan ) {
			// Fix 3: ensure single element regardless of how the caller was built.
			$titleSpan = $titleSpan.first();

			// Fix 7: cancel any other open edit before starting this one.
			this.cancelAnyOpenEdit();

			var self        = this;
			var currentText = $titleSpan.text();
			var $titleGroup = $titleSpan.parent();   // Fix 2: always the direct parent
			var $row        = $titleSpan.closest( '.aamo-menu-item, .aamo-submenu-item' );
			var sizeAttr    = Math.max( currentText.length + 5, 8 );

			var $input = $( '<input>', {
				type:         'text',
				'class':      'aamo-label-input',
				val:          currentText,
				size:         sizeAttr,
				'aria-label': data.i18n.editLabel || 'Edit label',
			} );
			var $confirm = $( '<button>', {
				type:    'button',
				'class': 'aamo-label-confirm',
				title:   data.i18n.saveLabel || 'Save label',
				html:    '&#10003;',
			} );

			// Fix 5: wrap input + confirm so the confirm circle can be absolutely
			// positioned, and so the wrapper prevents full-width flex stretching.
			var $inputWrap = $( '<span>', { 'class': 'aamo-label-input-wrap' } );
			$inputWrap.append( $input ).append( $confirm );

			$row.addClass( 'aamo-editing' );
			$titleSpan.hide();
			$titleGroup.append( $inputWrap );
			$input.trigger( 'focus' ).trigger( 'select' );

			// Fix 8: Enter cancels (same as Escape); ✓ button is the only save path.
			$input.on( 'keydown', function ( e ) {
				if ( 13 === e.which || 27 === e.which ) {
					e.preventDefault();
					self.cancelLabelEdit( $input, $titleSpan );
				}
			} );

			$confirm.on( 'click', function ( e ) {
				e.stopPropagation();
				self.confirmLabelEdit( $input, $titleSpan );
			} );
		},

		/**
		 * Commit the edited label value.
		 *
		 * If the new value equals the original WordPress title (or is blank),
		 * the custom label is cleared.  Otherwise it is set.
		 *
		 * @param {jQuery} $input     The live text input.
		 * @param {jQuery} $titleSpan The hidden .aamo-item-title span.
		 */
		confirmLabelEdit: function ( $input, $titleSpan ) {
			var newValue      = $input.val().trim();
			// Derive group and row before removing the input from the DOM.
			var $titleGroup   = $input.closest( '.aamo-title-group' );
			var $row          = $input.closest( '.aamo-menu-item, .aamo-submenu-item' );
			var originalTitle = $row.data( 'original-title' ) || '';
			var $subtitle     = $titleGroup.find( '.aamo-original-title' );

			// Fix 5: remove the whole wrapper (input + confirm circle) in one step.
			$input.parent().remove();
			$row.removeClass( 'aamo-editing' );
			$titleSpan.show();

			if ( '' === newValue || newValue === originalTitle ) {
				$row.removeData( 'custom-label' ).removeAttr( 'data-custom-label' );
				$titleSpan.text( originalTitle || newValue );
				$subtitle.remove();
			} else {
				$row.attr( 'data-custom-label', newValue ).data( 'custom-label', newValue );
				$titleSpan.text( newValue );
				if ( ! $subtitle.length && originalTitle ) {
					$titleGroup.append(
						$( '<span>', {
							'class': 'aamo-original-title',
							text:    originalTitle,
							title:   data.i18n.restoreLabel || 'click to restore',
						} )
					);
				}
			}

			this.markUnsaved();
		},

		/**
		 * Discard the edit — restore original span without marking unsaved.
		 *
		 * @param {jQuery} $input     The live text input.
		 * @param {jQuery} $titleSpan The hidden .aamo-item-title span.
		 */
		cancelLabelEdit: function ( $input, $titleSpan ) {
			// Derive row before removing the input from the DOM.
			var $row = $input.closest( '.aamo-menu-item, .aamo-submenu-item' );
			// Fix 5: remove the whole wrapper in one step.
			$input.parent().remove();
			$row.removeClass( 'aamo-editing' );
			$titleSpan.show();
		},

		// -----------------------------------------------------------------
		// Icon picker
		// -----------------------------------------------------------------

		/**
		 * Build the static icon grid once on init.
		 * Each cell carries data-icon="dashicons-{name}" and shows the icon symbol.
		 */
		initIconPicker: function () {
			var self  = this;
			var $grid = $( '#aamo-icon-grid' );

			if ( ! $grid.length ) {
				return;
			}

			$.each( AAMO_DASHICONS, function ( i, name ) {
				var icon = 'dashicons-' + name;
				$grid.append(
					$( '<button>', {
						type:    'button',
						'class': 'aamo-icon-cell',
						'data-icon': icon,
						title:   name,
					} ).append( $( '<span>', { 'class': 'dashicons ' + icon } ) )
				);
			} );

			// Filter input.
			$( '#aamo-icon-search' ).on( 'input', function () {
				var q = $( this ).val().toLowerCase().trim();
				$grid.find( '.aamo-icon-cell' ).each( function () {
					var name = $( this ).data( 'icon' ).replace( 'dashicons-', '' );
					$( this ).toggleClass( 'is-hidden', !! ( q && name.indexOf( q ) === -1 ) );
				} );
			} );

			// Icon selected.
			$grid.on( 'click', '.aamo-icon-cell', function () {
				var icon = $( this ).data( 'icon' );
				if ( self.$pickerTarget ) {
					self.applyIcon( self.$pickerTarget, icon );
				}
				self.closeIconPicker();
			} );

			// Close button.
			$( '#aamo-icon-picker .aamo-icon-picker-close' ).on( 'click', function () {
				self.closeIconPicker();
			} );

			// Escape key.
			$( document ).on( 'keydown.aamo-picker', function ( e ) {
				if ( 27 === e.which && $( '#aamo-icon-picker' ).hasClass( 'is-open' ) ) {
					self.closeIconPicker();
				}
			} );

			// Click outside.
			$( document ).on( 'click.aamo-picker', function ( e ) {
				if ( ! $( '#aamo-icon-picker' ).hasClass( 'is-open' ) ) {
					return;
				}
				if ( ! $( e.target ).closest( '#aamo-icon-picker, .aamo-item-icon' ).length ) {
					self.closeIconPicker();
				}
			} );
		},

		/**
		 * Open the icon picker positioned near the clicked icon element.
		 *
		 * @param {jQuery} $item The .aamo-menu-item row whose icon was double-clicked.
		 * @param {jQuery} $iconEl The .aamo-item-icon span that was clicked.
		 */
		openIconPicker: function ( $item, $iconEl ) {
			var currentIcon = $item.data( 'icon' ) || '';
			var $picker     = $( '#aamo-icon-picker' );

			this.$pickerTarget = $item;

			// Mark currently selected icon in grid.
			$picker.find( '.aamo-icon-cell' )
				.removeClass( 'is-selected' )
				.filter( '[data-icon="' + currentIcon + '"]' )
				.addClass( 'is-selected' );

			// Reset filter.
			$( '#aamo-icon-search' ).val( '' );
			$picker.find( '.aamo-icon-cell' ).removeClass( 'is-hidden' );

			// Scroll selected cell into view within the grid.
			var $sel = $picker.find( '.aamo-icon-cell.is-selected' );
			if ( $sel.length ) {
				var grid = $picker.find( '.aamo-icon-picker-grid' )[0];
				if ( grid ) {
					grid.scrollTop = $sel[0].offsetTop - 40;
				}
			}

			// Position near the icon element.
			var rect = $iconEl[0].getBoundingClientRect();
			var top  = rect.bottom + 4;
			var left = rect.left;

			// Keep within viewport.
			var pickerW = 340;
			var vpW     = $( window ).width();
			if ( left + pickerW > vpW - 8 ) {
				left = vpW - pickerW - 8;
			}

			$picker.css( { top: top, left: left } ).addClass( 'is-open' );
			$( '#aamo-icon-search' ).trigger( 'focus' );
		},

		closeIconPicker: function () {
			$( '#aamo-icon-picker' ).removeClass( 'is-open' );
			this.$pickerTarget = null;
		},

		/**
		 * Apply a new icon to a row, update the DOM, and manage the restore indicator.
		 *
		 * @param {jQuery} $item The .aamo-menu-item row.
		 * @param {string} icon  The new icon value (e.g. 'dashicons-admin-post').
		 */
		applyIcon: function ( $item, icon ) {
			var originalIcon = $item.data( 'original-icon' ) || '';

			// Persist new icon in both jQuery data cache and HTML attribute.
			// .data() updates the cache (what collectConfig reads via .data('icon'));
			// .attr() updates the attribute (persists across jQuery cache clears).
			$item.data( 'icon', icon ).attr( 'data-icon', icon );

			// Re-render the main icon span.
			var $icon = $item.children( '.aamo-item-icon' );
			$icon.removeClass().addClass( 'aamo-item-icon' ).removeAttr( 'style' )
				.attr( 'title', data.i18n.dblClickIcon || 'dbl click to change icon' );
			if ( icon.indexOf( 'dashicons-' ) === 0 ) {
				$icon.addClass( 'dashicons ' + icon );
			} else if ( icon && 'none' !== icon ) {
				$icon.css( 'background-image', 'url(' + icon + ')' );
			}

			// Remove any existing restore indicator.
			$item.children( '.aamo-original-icon' ).remove();

			// Add restore indicator when custom icon differs from original.
			if ( originalIcon && icon !== originalIcon ) {
				var $restore = $( '<span>', {
					'class': 'aamo-original-icon',
					title:   data.i18n.restoreLabel || 'click to restore',
				} );
				if ( originalIcon.indexOf( 'dashicons-' ) === 0 ) {
					$restore.addClass( 'dashicons ' + originalIcon );
				} else {
					$restore.css( 'background-image', 'url(' + originalIcon + ')' );
				}
				$icon.after( $restore );
			}

			this.markUnsaved();
		},

		/**
		 * Restore the original WordPress-registered icon on a row.
		 *
		 * @param {jQuery} $item The .aamo-menu-item row.
		 */
		restoreIcon: function ( $item ) {
			var originalIcon = $item.data( 'original-icon' ) || '';
			if ( originalIcon ) {
				this.applyIcon( $item, originalIcon );
			}
		},

		/**
		 * Wire up double-click on icons to open picker, and click on restore indicator.
		 */
		initIconEditing: function () {
			var self = this;

			// Double-click the icon to open picker (top-level items only).
			this.$list.on(
				'dblclick',
				'.aamo-menu-item > .aamo-item-icon',
				function ( e ) {
					e.stopPropagation();
					var $item = $( this ).closest( '.aamo-menu-item' );
					self.cancelAnyOpenEdit();
					self.openIconPicker( $item, $( this ) );
				}
			);

			// Click restore indicator to revert to original icon.
			this.$list.on(
				'click',
				'.aamo-menu-item > .aamo-original-icon',
				function ( e ) {
					e.stopPropagation();
					self.restoreIcon( $( this ).closest( '.aamo-menu-item' ) );
				}
			);
		},

		// -----------------------------------------------------------------
		// Utilities
		// -----------------------------------------------------------------

		markUnsaved: function () {
			this.isDirty = true;
			this.$status.text( data.i18n.unsavedChanges );
		},

		markSaved: function () {
			this.isDirty = false;
			this.$status.text( data.i18n.saved );
		},

		showError: function ( resp ) {
			var msg = ( resp && resp.data && resp.data.message )
				? resp.data.message
				: data.i18n.error;
			this.$status.text( msg );
		},

		/**
		 * Post to the AJAX endpoint and call success/fail/always callbacks.
		 *
		 * @param {string} action    AJAX action name.
		 * @param {Object} extraData Additional POST fields.
		 * @param {Object} callbacks Optional: { success, fail, always }.
		 */
		post: function ( action, extraData, callbacks ) {
			var self     = this;
			var cb       = callbacks || {};
			var postData = $.extend( { action: action, nonce: data.nonce }, extraData );

			$.post( data.ajaxUrl, postData )
				.done( function ( resp ) {
					if ( cb.success ) { cb.success( resp ); }
				} )
				.fail( function () {
					if ( cb.fail ) {
						cb.fail();
					} else {
						self.$status.text( data.i18n.error );
					}
				} )
				.always( function () {
					if ( cb.always ) { cb.always(); }
				} );
		},

		redirectToConfig: function ( configId ) {
			var url = data.builderUrl;
			if ( configId ) {
				url += '&config_id=' + encodeURIComponent( configId );
			}
			window.location.href = url;
		},
	};

	$( function () {
		Builder.init();
	} );

} )( jQuery, window.aamoData || {} );

/* global jQuery */
( function ( $ ) {
	'use strict';

	var TABS = {
		'#tab-builder': { tab: '#aamo-tab-builder', panel: '#aamo-panel-builder' },
		'#tab-roles':        { tab: '#aamo-tab-roles',        panel: '#aamo-panel-roles'        },
		'#tab-role-manager': { tab: '#aamo-tab-role-manager', panel: '#aamo-panel-role-manager' },
	};
	var DEFAULT_HASH = '#tab-builder';

	function activateTab( hash ) {
		var target = TABS[ hash ] || TABS[ DEFAULT_HASH ];
		$.each( TABS, function ( h, pair ) {
			$( pair.tab ).toggleClass( 'nav-tab-active', pair === target );
			$( pair.panel ).toggleClass( 'is-active', pair === target );
		} );
	}

	$( function () {
		activateTab( window.location.hash );
		$( '.nav-tab-wrapper' ).on( 'click', '.nav-tab', function ( e ) {
			e.preventDefault();
			var hash = $( this ).attr( 'href' );
			window.location.hash = hash;
			activateTab( hash );
		} );
	} );
} )( jQuery );

/* =============================================================================
   Role Manager — Plugin Access widget
   ============================================================================= */

( function ( $, data ) {
	'use strict';

	if ( ! data ) { return; }

	// ── Helpers ────────────────────────────────────────────────────────────────

	function escHtml( str ) {
		return $( '<div>' ).text( String( str ) ).html();
	}

	function escAttr( str ) {
		return String( str )
			.replace( /&/g, '&amp;' )
			.replace( /"/g, '&quot;' )
			.replace( /</g, '&lt;' )
			.replace( />/g, '&gt;' );
	}

	// ── Render access list ─────────────────────────────────────────────────────

	function renderAccessList( accessData ) {
		var roles = accessData.roles || [];
		var users = accessData.users || [];
		var $current = $( '.aamo-access-current' );

		if ( ! roles.length && ! users.length ) {
			$current.find( '.aamo-access-list, .aamo-no-access' ).replaceWith(
				'<p class="description aamo-no-access">' + escHtml( data.i18n.noAccess ) + '</p>'
			);
			return;
		}

		var html = '<ul class="aamo-access-list">';

		roles.forEach( function ( role ) {
			var isBuiltin  = !! role.is_builtin;
			var isAdmin    = ( role.slug === 'administrator' );
			var badge      = isBuiltin ? data.i18n.builtinRole : data.i18n.roleLabel;
			var warnCls    = isAdmin ? ' aamo-revoke-warn' : '';
			var warnTitle  = isAdmin ? ' title="' + escAttr( data.i18n.adminWarn ) + '"' : '';
			html += '<li class="aamo-access-entry' + ( isBuiltin ? ' aamo-access-builtin' : '' ) + '" data-role="' + escAttr( role.slug ) + '">';
			html += '<span class="aamo-access-icon dashicons dashicons-groups"></span>';
			html += '<span class="aamo-access-name">' + escHtml( role.name ) + '</span>';
			html += '<span class="aamo-access-badge">' + escHtml( badge ) + '</span>';
			html += '<button type="button" class="aamo-revoke-role button button-small' + warnCls + '"' + warnTitle + ' data-role="' + escAttr( role.slug ) + '">' + escHtml( data.i18n.revoke ) + '</button>';
			html += '</li>';
		} );

		users.forEach( function ( user ) {
			html += '<li class="aamo-access-entry aamo-access-user" data-user-id="' + escAttr( String( user.id ) ) + '">';
			html += '<span class="aamo-access-icon dashicons dashicons-admin-users"></span>';
			html += '<span class="aamo-access-name">' + escHtml( user.display_name ) + '</span>';
			html += '<span class="aamo-access-email">&lt;' + escHtml( user.email ) + '&gt;</span>';
			html += '<span class="aamo-access-badge aamo-access-badge-user">' + escHtml( data.i18n.userLabel ) + '</span>';
			html += '<button type="button" class="aamo-revoke-user button button-small" data-user-id="' + escAttr( String( user.id ) ) + '">' + escHtml( data.i18n.revoke ) + '</button>';
			html += '</li>';
		} );

		html += '</ul>';
		$current.find( '.aamo-access-list, .aamo-no-access' ).replaceWith( html );

		// Disable already-granted roles in the grant dropdown.
		var grantedSlugs = roles.map( function ( r ) { return r.slug; } );
		$( '#aamo-grant-role-select option' ).each( function () {
			var isGranted = grantedSlugs.indexOf( $( this ).val() ) !== -1;
			$( this ).prop( 'disabled', isGranted );
		} );
		var $sel = $( '#aamo-grant-role-select' );
		if ( $sel.find( ':selected' ).prop( 'disabled' ) ) {
			$sel.val( '' );
		}
	}

	// ── Status helper ──────────────────────────────────────────────────────────

	function showRmStatus( msg, isError ) {
		var $s = $( '#aamo-rm-status' );
		$s.text( msg ).removeClass( 'aamo-status-success aamo-status-error' )
			.addClass( isError ? 'aamo-status-error' : 'aamo-status-success' );
		clearTimeout( $s.data( 'rmTimer' ) );
		$s.data( 'rmTimer', setTimeout( function () { $s.text( '' ); }, 4000 ) );
	}

	// ── AJAX helper ────────────────────────────────────────────────────────────

	function doRmAjax( action, payload, $btn ) {
		$btn.prop( 'disabled', true );
		$.post( data.ajaxUrl, $.extend( { action: action, nonce: data.nonce }, payload ) )
			.done( function ( res ) {
				if ( res.success ) {
					renderAccessList( res.data );
					showRmStatus( data.i18n.saved, false );
				} else {
					showRmStatus( ( res.data && res.data.message ) || data.i18n.error, true );
				}
			} )
			.fail( function () {
				showRmStatus( data.i18n.error, true );
			} )
			.always( function () {
				$btn.prop( 'disabled', false );
			} );
	}

	// ── Event binding ──────────────────────────────────────────────────────────

	$( function () {
		var $panel = $( '#aamo-panel-role-manager' );
		if ( ! $panel.length ) { return; }

		// Grant to role.
		$panel.on( 'click', '#aamo-grant-role', function () {
			var slug = $( '#aamo-grant-role-select' ).val();
			if ( ! slug ) { return; }
			doRmAjax( data.actions.grantRole, { role: slug }, $( this ) );
		} );

		// Grant to user.
		$panel.on( 'click', '#aamo-grant-user', function () {
			var userId = $.trim( $( '#aamo-grant-user-id' ).val() );
			if ( ! userId ) {
				showRmStatus( data.i18n.userRequired, true );
				return;
			}
			doRmAjax( data.actions.grantUser, { user_id: userId }, $( this ) );
		} );

		// Revoke role.
		$panel.on( 'click', '.aamo-revoke-role', function () {
			var slug = $( this ).data( 'role' );
			if ( slug === 'administrator' && ! window.confirm( data.i18n.adminWarnConfirm ) ) {
				return;
			}
			doRmAjax( data.actions.revokeRole, { role: slug }, $( this ) );
		} );

		// Revoke user.
		$panel.on( 'click', '.aamo-revoke-user', function () {
			doRmAjax( data.actions.revokeUser, { user_id: $( this ).data( 'user-id' ) }, $( this ) );
		} );
	} );

} )( jQuery, typeof aamoRoleManagerData !== 'undefined' ? aamoRoleManagerData : null );

/* =============================================================================
   Role Manager — User search autocomplete
   ============================================================================= */

( function ( $, data ) {
	'use strict';

	if ( ! data || ! data.actions || ! data.actions.searchUsers ) { return; }

	var searchTimer = null;
	var MIN_CHARS   = 2;
	var DEBOUNCE_MS = 300;

	function closeSuggestions() {
		$( '#aamo-user-suggestions' ).hide().empty();
		$( '#aamo-grant-user-search' ).attr( 'aria-expanded', 'false' );
	}

	function renderSuggestions( users ) {
		var $list = $( '#aamo-user-suggestions' );
		$list.empty();
		if ( ! users.length ) {
			$list.append( $( '<div class="aamo-suggestion-empty">' ).text( data.i18n.noResults ) );
			$list.show();
			return;
		}
		users.forEach( function ( user ) {
			var $item = $( '<div class="aamo-suggestion-item" tabindex="0">' )
				.attr( 'data-id', user.id )
				.attr( 'data-name', user.display_name );
			$item.append( $( '<span class="aamo-sug-name">' ).text( user.display_name ) );
			$item.append( $( '<span class="aamo-sug-email">' ).text( ' <' + user.email + '>' ) );
			$list.append( $item );
		} );
		$list.show();
		$( '#aamo-grant-user-search' ).attr( 'aria-expanded', 'true' );
	}

	function doSearch( term ) {
		var $list = $( '#aamo-user-suggestions' );
		$list.empty()
			.append( $( '<div class="aamo-suggestion-empty">' ).text( data.i18n.searching ) )
			.show();
		$.post( data.ajaxUrl, { action: data.actions.searchUsers, nonce: data.nonce, term: term } )
			.done( function ( res ) {
				if ( res.success ) {
					renderSuggestions( res.data );
				} else {
					closeSuggestions();
				}
			} )
			.fail( function () { closeSuggestions(); } );
	}

	$( function () {
		var $panel = $( '#aamo-panel-role-manager' );
		if ( ! $panel.length ) { return; }

		// Debounced input handler.
		$panel.on( 'input', '#aamo-grant-user-search', function () {
			var term = $.trim( $( this ).val() );
			$( '#aamo-grant-user-id' ).val( '' ); // clear stored ID when user types
			clearTimeout( searchTimer );
			if ( term.length < MIN_CHARS ) {
				closeSuggestions();
				return;
			}
			searchTimer = setTimeout( function () { doSearch( term ); }, DEBOUNCE_MS );
		} );

		// Click a suggestion: fill input and store user ID.
		$panel.on( 'click keydown', '.aamo-suggestion-item', function ( e ) {
			if ( e.type === 'keydown' && e.which !== 13 ) { return; }
			var $item = $( this );
			$( '#aamo-grant-user-search' ).val( $item.data( 'name' ) );
			$( '#aamo-grant-user-id' ).val( $item.data( 'id' ) );
			closeSuggestions();
			$( '#aamo-grant-user' ).focus();
		} );

		// Close suggestions when clicking outside the widget.
		$( document ).on( 'click', function ( e ) {
			if ( ! $( e.target ).closest( '.aamo-user-search-wrap' ).length ) {
				closeSuggestions();
			}
		} );

		// Clear stored ID and suggestion list when the search field is cleared.
		$panel.on( 'change', '#aamo-grant-user-search', function () {
			if ( ! $.trim( $( this ).val() ) ) {
				$( '#aamo-grant-user-id' ).val( '' );
				closeSuggestions();
			}
		} );
	} );

} )( jQuery, typeof aamoRoleManagerData !== 'undefined' ? aamoRoleManagerData : null );

// =============================================================================
// Role Manager — Role list selection
// =============================================================================
( function( $ ) {
	'use strict';

	var $roleList   = $( '#aamo-role-list' );
	var $capsPanel  = $( '#aamo-role-caps-panel' );
	var selectedSlug = null;

	if ( ! $roleList.length ) { return; }

	// Click or Enter on a role row selects it.
	$roleList.on( 'click keydown', '.aamo-role-item', function( e ) {
		if ( e.type === 'keydown' && e.which !== 13 && e.which !== 32 ) { return; }
		e.preventDefault();

		var $item = $( this );
		var slug  = $item.data( 'slug' );

		if ( slug === selectedSlug ) { return; }
		selectedSlug = slug;

		$roleList.find( '.aamo-role-item' ).removeClass( 'aamo-role-selected aamo-role-admin-selected' );
		$item.addClass( 'aamo-role-selected' );
		if ( slug === 'administrator' ) {
			$item.addClass( 'aamo-role-admin-selected' );
		}

		$capsPanel.trigger( 'aamo:loadcaps', [ slug ] );
	} );

	// Clone / Delete buttons stop propagation so they don't trigger row selection.
	$roleList.on( 'click', '.aamo-clone-role, .aamo-delete-role', function( e ) {
		e.stopPropagation();
		// Handlers wired in Phases I and K.
	} );

} )( jQuery );

// =============================================================================
// Role Manager — Capability Editor (Phase F)
// =============================================================================
( function( $ ) {
	'use strict';

	var data       = window.aamoRoleManagerData || {};
	var $capsPanel = $( '#aamo-role-caps-panel' );

	if ( ! $capsPanel.length || ! data.actions || ! data.actions.getCaps ) { return; }

	var currentSlug = null;

	function escHtml( str ) {
		return $( '<div>' ).text( String( str ) ).html();
	}

	function renderCapsPanel( res ) {
		var roleName = escHtml( res.role_name );
		var isAdmin  = res.is_builtin && res.slug === 'administrator';
		var html     = '<div class="aamo-caps-header"><h3>' + roleName + '</h3>';

		if ( isAdmin ) {
			html += '<div class="aamo-caps-admin-warn notice notice-warning inline"><p>' +
				escHtml( data.i18n.adminCapWarn || '' ) + '</p></div>';
		}
		html += '</div>';

		html += '<div class="aamo-caps-search-wrap">' +
			'<input type="text" class="aamo-caps-search" placeholder="' +
			escHtml( data.i18n.searchCaps || 'Search capabilities…' ) + '">' +
			'</div>';

		html += '<div class="aamo-caps-groups">';
		if ( ! res.groups || ! res.groups.length ) {
			html += '<p class="description">' + escHtml( data.i18n.noCaps || 'No capabilities.' ) + '</p>';
		} else {
			$.each( res.groups, function( i, group ) {
				html += '<div class="aamo-caps-group">';
				html += '<button type="button" class="aamo-caps-group-toggle">' +
					'<span class="dashicons dashicons-arrow-down-alt2"></span> ' +
					escHtml( group.label ) +
					' <span class="aamo-group-count">(' + group.caps.length + ')</span>' +
					'</button>';
				html += '<ul class="aamo-caps-group-body">';
				$.each( group.caps, function( j, cap ) {
					html += '<li class="aamo-cap-row' + ( cap.granted ? '' : ' aamo-cap-ungrant' ) + '" data-cap="' + escHtml( cap.name ) + '">';
					html += '<label><input type="checkbox" class="aamo-cap-check" data-cap="' + escHtml( cap.name ) + '"' + ( cap.granted ? ' checked' : '' ) + '> ' +
						'<span class="aamo-cap-name">' + escHtml( cap.name ) + '</span></label>';
					html += '</li>';
				} );
				html += '</ul></div>';
			} );
		}
		html += '</div>'; // .aamo-caps-groups

		html += '<div class="aamo-caps-add-wrap">' +
			'<span class="aamo-add-cap-label">' + escHtml( data.i18n.addCap || 'Add Capability' ) + '</span>' +
			'<div class="aamo-add-cap-field-wrap">' +
			'<input type="text" class="aamo-add-cap-input" placeholder="' + escHtml( data.i18n.addCapPlaceholder || 'new_capability_slug' ) + '" autocomplete="off">' +
			'<ul class="aamo-add-cap-suggestions" hidden></ul>' +
			'</div>' +
			'<button type="button" class="aamo-add-cap-btn button button-secondary">' + escHtml( data.i18n.addCap || 'Add Capability' ) + '</button>' +
			'<span class="aamo-add-cap-error"></span>' +
			'</div>';

		html += '<div class="aamo-caps-save-bar">' +
			'<button type="button" class="aamo-save-caps button button-primary">' +
			escHtml( data.i18n.saveChanges || 'Save Changes' ) +
			'</button>' +
			' <span class="aamo-caps-save-status" aria-live="polite"></span>' +
			'</div>';

		$capsPanel.removeClass( 'aamo-caps-empty aamo-caps-loading' ).html( html );
	}

	function loadRoleCaps( slug ) {
		currentSlug = slug;
		$capsPanel
			.removeClass( 'aamo-caps-empty' )
			.addClass( 'aamo-caps-loading' )
			.html( '<span class="spinner is-active aamo-caps-spinner"></span>' );

		$.post( data.ajaxUrl, { action: data.actions.getCaps, nonce: data.nonce, slug: slug } )
			.done( function( res ) {
				if ( slug !== currentSlug ) { return; }
				if ( res && res.success ) {
					renderCapsPanel( res.data );
				} else {
					var msg = ( res && res.data && res.data.message ) ? res.data.message : ( data.i18n.error || 'Error.' );
					$capsPanel.removeClass( 'aamo-caps-loading' )
						.html( '<p class="description">' + escHtml( msg ) + '</p>' );
				}
			} )
			.fail( function() {
				if ( slug !== currentSlug ) { return; }
				$capsPanel.removeClass( 'aamo-caps-loading' )
					.html( '<p class="description">' + escHtml( data.i18n.error || 'Error.' ) + '</p>' );
			} );
	}

	// Respond to role selection from Phase E IIFE.
	$capsPanel.on( 'aamo:loadcaps', function( e, slug ) {
		loadRoleCaps( slug );
	} );

	// Collapsible group headers.
	$capsPanel.on( 'click', '.aamo-caps-group-toggle', function() {
		$( this ).closest( '.aamo-caps-group' ).toggleClass( 'aamo-group-collapsed' );
	} );

	// Real-time cap search filter.
	$capsPanel.on( 'input', '.aamo-caps-search', function() {
		var term = $( this ).val().toLowerCase();
		$capsPanel.find( '.aamo-cap-row' ).each( function() {
			$( this ).toggle( ! term || $( this ).data( 'cap' ).toLowerCase().indexOf( term ) !== -1 );
		} );
		$capsPanel.find( '.aamo-caps-group' ).each( function() {
			$( this ).toggle( $( this ).find( '.aamo-cap-row:visible' ).length > 0 );
		} );
	} );

	// Save Changes.
	$capsPanel.on( 'click', '.aamo-save-caps', function() {
		var $btn    = $( this );
		var $bar    = $btn.closest( '.aamo-caps-save-bar' );
		var $status = $bar.find( '.aamo-caps-save-status' );
		var caps    = {};

		$capsPanel.find( '.aamo-cap-check' ).each( function() {
			caps[ $( this ).data( 'cap' ) ] = this.checked ? 1 : 0;
		} );

		$btn.prop( 'disabled', true );
		$bar.find( '.spinner' ).remove();
		$btn.after( '<span class="spinner is-active"></span>' );
		$status.text( '' ).removeClass( 'aamo-status-success aamo-status-error' );

		$.post( data.ajaxUrl, {
			action : data.actions.saveCaps,
			nonce  : data.nonce,
			slug   : currentSlug,
			caps   : JSON.stringify( caps ),
		} )
		.done( function( res ) {
			$bar.find( '.spinner' ).remove();
			$btn.prop( 'disabled', false );
			if ( res && res.success ) {
				$status.text( data.i18n.saved || 'Saved.' ).addClass( 'aamo-status-success' );
				if ( res.data && res.data.cap_count !== undefined ) {
					var count = parseInt( res.data.cap_count, 10 );
					var label = count === 1 ? '1 cap' : count + ' caps';
					$( '#aamo-role-list .aamo-role-item[data-slug="' + currentSlug + '"] .aamo-role-cap-count' ).text( label );
				}
				setTimeout( function() { $status.text( '' ).removeClass( 'aamo-status-success' ); }, 4000 );
			} else {
				var msg = ( res && res.data && res.data.message ) ? res.data.message : ( data.i18n.error || 'Error.' );
				$status.text( msg ).addClass( 'aamo-status-error' );
			}
		} )
		.fail( function() {
			$bar.find( '.spinner' ).remove();
			$btn.prop( 'disabled', false );
			$status.text( data.i18n.error || 'Error.' ).addClass( 'aamo-status-error' );
		} );
	} );

	// Add Custom Capability.
	$capsPanel.on( 'click', '.aamo-add-cap-btn', function() {
		var $input = $capsPanel.find( '.aamo-add-cap-input' );
		var $error = $capsPanel.find( '.aamo-add-cap-error' );
		var slug   = $.trim( $input.val() ).toLowerCase().replace( /[^a-z0-9_]/g, '_' ).replace( /^_+|_+$/g, '' );

		$error.text( '' );

		if ( ! slug ) {
			$error.text( data.i18n.addCapEmpty || 'Please enter a capability slug.' );
			return;
		}
		if ( ! /^[a-z][a-z0-9_]*$/.test( slug ) ) {
			$error.text( data.i18n.addCapInvalid || 'Invalid slug.' );
			return;
		}
		var exists = false;
		$capsPanel.find( '.aamo-cap-check' ).each( function() {
			if ( $( this ).data( 'cap' ) === slug ) { exists = true; return false; }
		} );
		if ( exists ) {
			$error.text( data.i18n.addCapDuplicate || 'Capability already exists.' );
			return;
		}

		// Find the last group (Other Capabilities) or create one.
		var $lastGroup = $capsPanel.find( '.aamo-caps-group' ).last();
		if ( ! $lastGroup.length ) {
			$capsPanel.find( '.aamo-caps-groups' ).append(
				'<div class="aamo-caps-group">' +
				'<button type="button" class="aamo-caps-group-toggle">' +
				'<span class="dashicons dashicons-arrow-down-alt2"></span> ' +
				escHtml( data.i18n.otherGroup || 'Other Capabilities' ) +
				' <span class="aamo-group-count">(0)</span></button>' +
				'<ul class="aamo-caps-group-body"></ul></div>'
			);
			$lastGroup = $capsPanel.find( '.aamo-caps-group' ).last();
		}
		$lastGroup.find( '.aamo-caps-group-body' ).append(
			'<li class="aamo-cap-row" data-cap="' + escHtml( slug ) + '">' +
			'<label><input type="checkbox" class="aamo-cap-check" data-cap="' + escHtml( slug ) + '" checked> ' +
			'<span class="aamo-cap-name">' + escHtml( slug ) + '</span></label></li>'
		);
		$lastGroup.find( '.aamo-group-count' ).text( '(' + $lastGroup.find( '.aamo-cap-row' ).length + ')' );
		$input.val( '' ).focus();
	} );

} )( jQuery );

// =============================================================================
// Role Manager — Clone Role (Phase I)
// =============================================================================
( function( $ ) {
	'use strict';

	var data     = window.aamoRoleManagerData || {};
	var $overlay = $( '#aamo-clone-modal' );
	var $list    = $( '#aamo-role-list' );

	if ( ! $overlay.length || ! data.actions || ! data.actions.cloneRole ) { return; }

	var sourceSlug = null;

	function slugify( name ) {
		return name.toLowerCase()
			.replace( /[^a-z0-9]+/g, '_' )
			.replace( /^_+|_+$/g, '' )
			.substring( 0, 60 );
	}

	function escHtml( str ) {
		return $( '<div>' ).text( String( str ) ).html();
	}

	function openModal( slug, sourceName ) {
		sourceSlug = slug;
		var cloneName = sourceName + ' (Copy)';
		$overlay.find( '#aamo-clone-modal-title' ).text( 'Clone Role: ' + sourceName );
		$overlay.find( '#aamo-clone-source-desc' ).text( 'Copying all capabilities from: ' + sourceName );
		$overlay.find( '#aamo-clone-name' ).val( cloneName );
		$overlay.find( '#aamo-clone-slug' ).val( slugify( cloneName ) );
		$overlay.find( '#aamo-clone-slug-error, #aamo-clone-error' ).text( '' );
		$overlay.find( '.spinner' ).removeClass( 'is-active' );
		$overlay.find( '#aamo-clone-submit' ).prop( 'disabled', false ).text( 'Clone Role' );
		$overlay.removeAttr( 'hidden' );
		setTimeout( function() { $overlay.find( '#aamo-clone-name' ).trigger( 'focus' ); }, 50 );
	}

	function closeModal() {
		$overlay.attr( 'hidden', '' );
		sourceSlug = null;
	}

	// Open on Clone button click in role list.
	$list.on( 'click', '.aamo-clone-role', function( e ) {
		e.stopPropagation();
		var $item = $( this ).closest( '.aamo-role-item' );
		openModal( $item.data( 'slug' ), $item.find( '.aamo-role-name' ).text() );
	} );

	// Auto-generate slug from name as user types.
	$overlay.on( 'input', '#aamo-clone-name', function() {
		$overlay.find( '#aamo-clone-slug' ).val( slugify( $( this ).val() ) );
		$overlay.find( '#aamo-clone-slug-error' ).text( '' );
	} );

	// Validate slug format as user edits it.
	$overlay.on( 'input', '#aamo-clone-slug', function() {
		var slug = $( this ).val();
		if ( slug && ! /^[a-z][a-z0-9_]*$/.test( slug ) ) {
			$overlay.find( '#aamo-clone-slug-error' ).text( data.i18n.slugInvalid || 'Invalid slug.' );
		} else {
			$overlay.find( '#aamo-clone-slug-error' ).text( '' );
		}
	} );

	// Close handlers.
	$overlay.on( 'click', '.aamo-modal-close, .aamo-modal-cancel', closeModal );
	$overlay.on( 'click', function( e ) {
		if ( $( e.target ).is( $overlay ) ) { closeModal(); }
	} );
	$( document ).on( 'keydown.aamo-clone', function( e ) {
		if ( e.which === 27 && ! $overlay.attr( 'hidden' ) ) { closeModal(); }
	} );

	// Submit.
	$overlay.on( 'click', '#aamo-clone-submit', function() {
		var $btn    = $( this );
		var newName = $.trim( $overlay.find( '#aamo-clone-name' ).val() );
		var newSlug = $.trim( $overlay.find( '#aamo-clone-slug' ).val() );

		$overlay.find( '#aamo-clone-error, #aamo-clone-slug-error' ).text( '' );

		if ( ! newName ) {
			$overlay.find( '#aamo-clone-error' ).text( data.i18n.nameRequired || 'Name is required.' );
			return;
		}
		if ( ! newSlug || ! /^[a-z][a-z0-9_]*$/.test( newSlug ) ) {
			$overlay.find( '#aamo-clone-slug-error' ).text( data.i18n.slugInvalid || 'Invalid slug.' );
			return;
		}

		$btn.prop( 'disabled', true );
		$overlay.find( '.spinner' ).addClass( 'is-active' );

		$.post( data.ajaxUrl, {
			action      : data.actions.cloneRole,
			nonce       : data.nonce,
			source_slug : sourceSlug,
			new_name    : newName,
			new_slug    : newSlug,
		} )
		.done( function( res ) {
			$overlay.find( '.spinner' ).removeClass( 'is-active' );
			$btn.prop( 'disabled', false );

			if ( res && res.success ) {
				var d        = res.data;
				var capCount = d.cap_count === 1 ? '1 cap' : d.cap_count + ' caps';
				var $newItem = $( '<li class="aamo-role-item" tabindex="0"></li>' )
					.attr( 'data-slug', d.slug )
					.html(
						'<span class="aamo-role-name">' + escHtml( d.name ) + '</span>' +
						'<span class="aamo-role-meta">' +
							'<span class="aamo-role-cap-count">' + escHtml( capCount ) + '</span>' +
						'</span>' +
						'<span class="aamo-role-actions">' +
							'<button type="button" class="aamo-clone-role button button-small" data-slug="' + escHtml( d.slug ) + '" title="Clone this role">Clone</button>' +
							'<button type="button" class="aamo-delete-role button button-small button-link-delete" data-slug="' + escHtml( d.slug ) + '" title="Delete this role">Delete</button>' +
						'</span>'
					);
				$list.append( $newItem );
				closeModal();
				$newItem.trigger( 'click' ); // auto-select to load its caps
			} else {
				var msg = ( res && res.data && res.data.message ) ? res.data.message : ( data.i18n.error || 'Error.' );
				if ( msg.toLowerCase().indexOf( 'slug' ) !== -1 || msg.toLowerCase().indexOf( 'already' ) !== -1 ) {
					$overlay.find( '#aamo-clone-slug-error' ).text( msg );
				} else {
					$overlay.find( '#aamo-clone-error' ).text( msg );
				}
			}
		} )
		.fail( function() {
			$overlay.find( '.spinner' ).removeClass( 'is-active' );
			$btn.prop( 'disabled', false );
			$overlay.find( '#aamo-clone-error' ).text( data.i18n.error || 'Error.' );
		} );
	} );

	// New Role from scratch — opens the same modal with empty source.
	$( '#aamo-new-role' ).on( 'click', function() {
		sourceSlug = '';
		$overlay.find( '#aamo-clone-modal-title' ).text( data.i18n.newRole || 'New Role' );
		$overlay.find( '#aamo-clone-source-desc' ).text( data.i18n.newRoleDesc || 'The new role will start with no capabilities.' );
		$overlay.find( '#aamo-clone-name, #aamo-clone-slug' ).val( '' );
		$overlay.find( '#aamo-clone-slug-error, #aamo-clone-error' ).text( '' );
		$overlay.find( '.spinner' ).removeClass( 'is-active' );
		$overlay.find( '#aamo-clone-submit' ).prop( 'disabled', false ).text( data.i18n.createRole || 'Create Role' );
		$overlay.removeAttr( 'hidden' );
		setTimeout( function() { $overlay.find( '#aamo-clone-name' ).trigger( 'focus' ); }, 50 );
	} );

} )( jQuery );

// =============================================================================
// Role Manager — Delete Role (Phase K)
// =============================================================================
( function( $ ) {
	'use strict';

	var data       = window.aamoRoleManagerData || {};
	var $overlay   = $( '#aamo-delete-modal' );
	var $list      = $( '#aamo-role-list' );
	var $capsPanel = $( '#aamo-role-caps-panel' );

	if ( ! $overlay.length || ! data.actions || ! data.actions.deleteRole ) { return; }

	var targetSlug = null;
	var targetName = null;

	function escHtml( str ) {
		return $( '<div>' ).text( String( str ) ).html();
	}

	function openDeleteModal( slug, name, userCount ) {
		targetSlug = slug;
		targetName = name;

		$overlay.find( '#aamo-delete-modal-title' ).text( ( data.i18n.deleteRole || 'Delete Role' ) + ': ' + name );
		$overlay.find( '#aamo-delete-confirm-input' ).val( '' );
		$overlay.find( '#aamo-delete-error' ).text( '' );
		$overlay.find( '#aamo-delete-submit' ).prop( 'disabled', true );
		$overlay.find( '.spinner' ).removeClass( 'is-active' );

		// User count warning message.
		var countMsg = userCount > 0
			? userCount + ' ' + ( userCount === 1 ? 'user is' : 'users are' ) + ' assigned this role. ' + ( data.i18n.deleteWarning || '' )
			: data.i18n.deleteWarning || '';
		$overlay.find( '#aamo-delete-user-count' ).text( countMsg );

		$overlay.removeAttr( 'hidden' );
		setTimeout( function() { $overlay.find( '#aamo-delete-confirm-input' ).trigger( 'focus' ); }, 50 );
	}

	function closeDeleteModal() {
		$overlay.attr( 'hidden', '' );
		targetSlug = null;
		targetName = null;
	}

	// Open on Delete button click.
	$list.on( 'click', '.aamo-delete-role', function( e ) {
		e.stopPropagation();
		var $item     = $( this ).closest( '.aamo-role-item' );
		var slug      = $item.data( 'slug' );
		var name      = $item.find( '.aamo-role-name' ).text();
		var userCount = parseInt( $item.data( 'userCount' ) || '0', 10 );
		openDeleteModal( slug, name, userCount );
	} );

	// Enable Delete button only when typed name matches exactly.
	$overlay.on( 'input', '#aamo-delete-confirm-input', function() {
		$overlay.find( '#aamo-delete-submit' ).prop( 'disabled', $( this ).val() !== targetName );
	} );

	// Close handlers.
	$overlay.on( 'click', '.aamo-modal-close, .aamo-modal-cancel', closeDeleteModal );
	$overlay.on( 'click', function( e ) {
		if ( $( e.target ).is( $overlay ) ) { closeDeleteModal(); }
	} );
	$( document ).on( 'keydown.aamo-delete', function( e ) {
		if ( e.which === 27 && ! $overlay.attr( 'hidden' ) ) { closeDeleteModal(); }
	} );

	// Submit.
	$overlay.on( 'click', '#aamo-delete-submit', function() {
		if ( $( this ).prop( 'disabled' ) ) { return; }

		var $btn = $( this );
		$btn.prop( 'disabled', true );
		$overlay.find( '.spinner' ).addClass( 'is-active' );
		$overlay.find( '#aamo-delete-error' ).text( '' );

		$.post( data.ajaxUrl, {
			action : data.actions.deleteRole,
			nonce  : data.nonce,
			slug   : targetSlug,
		} )
		.done( function( res ) {
			$overlay.find( '.spinner' ).removeClass( 'is-active' );
			if ( res && res.success ) {
				$list.find( '.aamo-role-item[data-slug="' + targetSlug + '"]' ).remove();
				if ( $capsPanel.length ) {
					$capsPanel.addClass( 'aamo-caps-empty' ).html(
						'<p class="description">' + escHtml( data.i18n.selectRole || 'Select a role on the left.' ) + '</p>'
					);
				}
				closeDeleteModal();
			} else {
				$btn.prop( 'disabled', false );
				var msg = ( res && res.data && res.data.message ) ? res.data.message : ( data.i18n.error || 'Error.' );
				$overlay.find( '#aamo-delete-error' ).text( msg );
			}
		} )
		.fail( function() {
			$overlay.find( '.spinner' ).removeClass( 'is-active' );
			$btn.prop( 'disabled', false );
			$overlay.find( '#aamo-delete-error' ).text( data.i18n.error || 'Error.' );
		} );
	} );

} )( jQuery );

/* ── Phase N — Add Capability slug type-ahead ──────────────────────────────── */
( function( $ ) {
	'use strict';

	var data       = ( typeof aamoRoleManagerData !== 'undefined' ) ? aamoRoleManagerData : {};
	var allCaps    = Array.isArray( data.allCaps ) ? data.allCaps : [];
	var $capsPanel = $( '#aamo-role-caps-panel' );
	var activeIdx  = -1;

	function getExistingCaps() {
		var existing = {};
		$capsPanel.find( '.aamo-cap-check' ).each( function() {
			existing[ $( this ).val() ] = true;
		} );
		return existing;
	}

	function renderSuggestions( $input ) {
		var query   = $input.val().trim().toLowerCase();
		var $wrap   = $input.closest( '.aamo-add-cap-field-wrap' );
		var $list   = $wrap.find( '.aamo-add-cap-suggestions' );

		if ( ! query ) {
			$list.prop( 'hidden', true ).empty();
			activeIdx = -1;
			return;
		}

		var existing = getExistingCaps();
		var matches  = allCaps.filter( function( cap ) {
			return cap.indexOf( query ) !== -1 && ! existing[ cap ];
		} ).slice( 0, 10 );

		if ( ! matches.length ) {
			$list.prop( 'hidden', true ).empty();
			activeIdx = -1;
			return;
		}

		$list.empty();
		$.each( matches, function( i, cap ) {
			$list.append(
				$( '<li class="aamo-add-cap-sug-item" tabindex="-1"></li>' )
					.attr( 'data-cap', cap )
					.text( cap )
			);
		} );
		$list.prop( 'hidden', false );
		activeIdx = -1;
	}

	function selectSuggestion( $input, cap ) {
		$input.val( cap );
		$input.closest( '.aamo-add-cap-field-wrap' )
			.find( '.aamo-add-cap-suggestions' )
			.prop( 'hidden', true ).empty();
		activeIdx = -1;
	}

	function highlightItem( $list, idx ) {
		var $items = $list.find( '.aamo-add-cap-sug-item' );
		$items.removeClass( 'aamo-sug-active' );
		if ( idx >= 0 && idx < $items.length ) {
			$items.eq( idx ).addClass( 'aamo-sug-active' );
		}
	}

	// Type to filter
	$capsPanel.on( 'input', '.aamo-add-cap-input', function() {
		renderSuggestions( $( this ) );
	} );

	// Arrow / Enter / Escape navigation
	$capsPanel.on( 'keydown', '.aamo-add-cap-input', function( e ) {
		var $input = $( this );
		var $wrap  = $input.closest( '.aamo-add-cap-field-wrap' );
		var $list  = $wrap.find( '.aamo-add-cap-suggestions' );
		var $items = $list.find( '.aamo-add-cap-sug-item' );
		var count  = $items.length;

		if ( $list.prop( 'hidden' ) || ! count ) {
			return;
		}

		if ( e.key === 'ArrowDown' ) {
			e.preventDefault();
			activeIdx = Math.min( activeIdx + 1, count - 1 );
			highlightItem( $list, activeIdx );
		} else if ( e.key === 'ArrowUp' ) {
			e.preventDefault();
			activeIdx = Math.max( activeIdx - 1, 0 );
			highlightItem( $list, activeIdx );
		} else if ( ( e.key === 'Enter' || e.key === 'Tab' ) && activeIdx >= 0 ) {
			e.preventDefault();
			selectSuggestion( $input, $items.eq( activeIdx ).attr( 'data-cap' ) );
		} else if ( e.key === 'Escape' ) {
			$list.prop( 'hidden', true ).empty();
			activeIdx = -1;
		}
	} );

	// Click a suggestion
	$capsPanel.on( 'mousedown', '.aamo-add-cap-sug-item', function( e ) {
		e.preventDefault(); // keep input focus
		var $input = $( this ).closest( '.aamo-add-cap-field-wrap' ).find( '.aamo-add-cap-input' );
		selectSuggestion( $input, $( this ).attr( 'data-cap' ) );
	} );

	// Hide on blur (delay lets mousedown fire first)
	$capsPanel.on( 'blur', '.aamo-add-cap-input', function() {
		var $wrap = $( this ).closest( '.aamo-add-cap-field-wrap' );
		setTimeout( function() {
			$wrap.find( '.aamo-add-cap-suggestions' ).prop( 'hidden', true ).empty();
			activeIdx = -1;
		}, 150 );
	} );

} )( jQuery );
