
function panelraSafeMessage(value, fallback){
    if (typeof value === 'string') {
        return value;
    }
    if (value && typeof value === 'object') {
        if (typeof value.message === 'string') {
            return value.message;
        }
        if (typeof value.data === 'string') {
            return value.data;
        }
        if (value.data && typeof value.data.message === 'string') {
            return value.data.message;
        }
        if (value.responseJSON && typeof value.responseJSON.message === 'string') {
            return value.responseJSON.message;
        }
        if (value.responseJSON && value.responseJSON.data && typeof value.responseJSON.data.message === 'string') {
            return value.responseJSON.data.message;
        }
    }
    return fallback || '';
}

(function($) {
'use strict';

var cfg         = window.pnlrdbBuilder || {};
var layout = Object.assign(
	{ enabled:false, hide_wp:false, hide_screen_opts:false, hide_help:false, hide_notices:false, hide_title:false, defaults_initialized:false, sections:[] },
	cfg.layout || {}
);
var blockTypes  = cfg.blockTypes || {};
var templates   = cfg.templates || {};
var allRoles    = cfg.roles || {};
var actionChoices = cfg.actionChoices || [];
var i18n        = cfg.i18n || {};
var selectedId  = null;
var selectedType = null; // 'block' | 'section'
var idCtr       = 0;
var dragBlockType = null;

function uid() { return 'x' + (++idCtr) + '_' + Date.now(); }

/* ============================================================
   INIT
============================================================ */
function init() {
	renderAll();
	bindTopControls();
	bindSidebarDrag();
	bindAddSection();
	bindSave();
	bindTemplates();
	bindInspectorClose();
	$(document).on('click', function(e) {
		if (!$(e.target).closest('.ap773-cs-block,.ap773-cs-section,.ap773-bl-inspector').length) {
			deselect();
		}
	});
}

/* ============================================================
   RENDER
============================================================ */
function renderAll() {
	var root = $('#ap773-sections-root').empty();
	if (!layout.sections || !layout.sections.length) {
		$('#ap773-canvas-empty').show();
		return;
	}
	$('#ap773-canvas-empty').hide();
	layout.sections.forEach(function(sec) { root.append(renderSection(sec)); });
	applyRolePreview($('#ap773-role-preview').val());
}

function renderSection(sec) {
	var cols = sec.cols || 1;
	var rolesHtml = rolesLabel(sec.roles);

	var $sec = $('<div class="ap773-cs-section" data-sec-id="'+esc(sec.id)+'">' +
		'<div class="ap773-cs-sec-header">' +
			'<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18"/></svg>' +
			'<span class="ap773-cs-sec-title">' + esc(sec.title || (i18n.section || 'Section')) + ' &mdash; ' + cols + ' ' + (cols===1? esc(i18n.column || 'column') : esc(i18n.columnsPlural || 'columns')) + '</span>' +
			'<span class="ap773-cs-sec-roles" data-sec-id="'+esc(sec.id)+'">'+rolesHtml+'</span>' +
			'<div class="ap773-cs-sec-actions">' +
				iconBtn('up','<polyline points="18 15 12 9 6 15"/>', (i18n.moveUp || 'Move up')) +
				iconBtn('down','<polyline points="6 9 12 15 18 9"/>', (i18n.moveDown || 'Move down')) +
				iconBtn('settings','<path d="M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z"/><circle cx="12" cy="12" r="3"/>', (i18n.sectionSettings || 'Section settings')) +
				iconBtn('del-sec','<polyline points="3 6 5 6 21 6"/><path d="M19 6l-1 14H6L5 6"/><path d="M10 11v6M14 11v6"/><path d="M9 6V4h6v2"/>', (i18n.deleteSection || 'Delete section'), 'del') +
			'</div>' +
		'</div>' +
		'<div class="ap773-cs-grid ap773-cs-grid--'+cols+'"></div>' +
		'</div>');

	var $grid = $sec.find('.ap773-cs-grid');
	for (var c = 0; c < cols; c++) {
		var $col = $('<div class="ap773-cs-col" data-col="'+c+'" data-sec-id="'+esc(sec.id)+'"></div>');
		var blocksInCol = (sec.blocks||[]).filter(function(b){ return (b.col||0) === c; });
		blocksInCol.forEach(function(b){ $col.append(renderBlock(b)); });
		bindColDrop($col);
		$grid.append($col);
	}

	// Section header actions
	$sec.find('[data-action="up"]').on('click', function(e){ e.stopPropagation(); moveSectionDir(sec.id,-1); });
	$sec.find('[data-action="down"]').on('click', function(e){ e.stopPropagation(); moveSectionDir(sec.id,1); });
	$sec.find('[data-action="settings"]').on('click', function(e){ e.stopPropagation(); selectSection(sec.id); });
	$sec.find('[data-action="del-sec"]').on('click', function(e){ e.stopPropagation(); if(confirm(i18n.confirmSecDel)) deleteSection(sec.id); });

	return $sec;
}

function renderBlock(b) {
	var def = blockTypes[b.type] || {};
	var _isText = (b.type === 'heading' || b.type === 'text');
	var _blCls = _isText ? 'ap773-cs-block is-text-block' : 'ap773-cs-block is-media-block';
	var $bl = $('<div class="'+_blCls+'" data-block-id="'+esc(b.id)+'" data-sec-id="'+esc(b.secId||'')+'">' +
		'<div class="ap773-cs-block-header">' +
			'<span class="ap773-cs-block-icon ap773-bl-icon--'+esc(b.type)+'">'+( def.icon||'' )+'</span>' +
			'<span class="ap773-cs-block-label">'+esc(def.label||b.type)+'</span>' +
			'<div class="ap773-cs-block-actions">' +
				iconBtn('up-bl','<polyline points="18 15 12 9 6 15"/>',(i18n.moveUp || 'Move up')) +
				iconBtn('down-bl','<polyline points="6 9 12 15 18 9"/>',(i18n.moveDown || 'Move down')) +
				iconBtn('del-bl','<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>',(i18n.deleteBlock || 'Delete block'),'del') +
			'</div>' +
		'</div>' +
		'<div class="ap773-cs-block-preview">'+blockPreview(b)+'</div>' +

		'</div>');

	$bl.on('click', function(e){ e.stopPropagation(); selectBlock(b.id); });
	$bl.find('[data-action="up-bl"]').on('click', function(e){ e.stopPropagation(); moveBlockInCol(b.id,-1); });
	$bl.find('[data-action="down-bl"]').on('click', function(e){ e.stopPropagation(); moveBlockInCol(b.id,1); });
	$bl.find('[data-action="del-bl"]').on('click', function(e){ e.stopPropagation(); if(confirm(i18n.confirmDel)) deleteBlock(b.id); });

	return $bl;
}

function blockPreview(b) {
	var d = b.data || {};
	var a = d.align || 'left';
	switch(b.type) {
		case 'heading':
			return '<div class="prev-heading" style="font-size:'+(d.level==='h1'?'16px':d.level==='h3'?'12px':'14px')+';text-align:'+a+'">'+esc(d.text||'Heading')+'</div>';
		case 'text':
			return '<div style="color:#64748b;text-align:'+a+'">'+esc((d.content||'').replace(/<[^>]+>/g,'').substring(0,60))+(( d.content||'').length>60?'…':'')+'</div>';
		case 'button':
			var cls=d.style==='secondary'?'prev-btn-s':'prev-btn-p';
			return '<div style="text-align:'+a+'"><span class="prev-btn '+cls+'">'+esc(d.label||'Button')+'</span></div>';
		case 'image':
			if (d.url) {
				return '<div style="text-align:'+a+';padding:4px"><img src="'+esc(d.url)+'" alt="'+esc(d.alt||'')+'" style="max-width:100%;max-height:80px;border-radius:4px;display:inline-block"></div>';
			}
			return '<div style="color:#94a3b8;text-align:'+a+';font-size:11px">&#x1F5BC; No image selected</div>';
		case 'video':
			var vTitle = d.title || 'Video tutorial';
			var vSub = d.subtitle || 'Add a short description for this video.';
			return '<div style="text-align:'+a+'"><div class="prev-video" style="display:inline-flex;flex-direction:column;gap:3px;max-width:100%"><span><svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="6" width="15" height="12" rx="2"/><path d="M17 9l5-3v12l-5-3V9z"/></svg>&nbsp;'+esc(vTitle)+'</span><small style="color:#64748b;font-weight:600">'+esc(vSub)+'</small></div></div>';
		case 'divider':
			return '<div class="prev-divider"></div>';
		case 'stats':
			return '<div class="prev-stats" style="justify-content:'+(a==='center'?'center':a==='right'?'flex-end':'flex-start')+'">'+['Posts','Pages','Comments'].map(function(l){ return '<span class="prev-stat-chip">'+l+'</span>'; }).join('')+'</div>';
		case 'hero':
			return '<div class="prev-hero prev-hero--'+esc(d.theme||'blue')+'"><strong>'+esc(d.title||'Hero title')+'</strong><span>'+esc((d.content||'').replace(/<[^>]+>/g,'').substring(0,70))+'</span></div>';
		case 'link_list':
			return '<div class="prev-list"><strong>'+esc(d.title||'Link list')+'</strong><span>'+esc((d.items||'').split('\n').length)+' links</span></div>';
		case 'checklist':
			return '<div class="prev-list"><strong>'+esc(d.title||'Checklist')+'</strong><span>'+esc((d.items||'').split('\n').length)+' items</span></div>';
		case 'quick_actions':
			return '<div class="prev-list"><strong>'+esc(d.title||'Quick actions')+'</strong><span>'+esc((d.items||'').split('\n').length)+' actions</span></div>';
		default:
			return '<em style="color:#94a3b8;font-size:11px">'+esc(b.type)+'</em>';
	}
}

/* ============================================================
   SELECTION & INSPECTOR
============================================================ */
function selectBlock(blockId) {
	deselect();
	selectedId = blockId; selectedType = 'block';
	$('[data-block-id="'+blockId+'"]').addClass('is-selected');
	var b = findBlock(blockId);
	if (!b) return;
	var def = blockTypes[b.type] || {};
	$('#ap773-insp-title').text(def.label || b.type);
	$('#ap773-insp-body').html(buildBlockInspector(b));
	$('#ap773-inspector').show();
}

function selectSection(secId) {
	deselect();
	selectedId = secId; selectedType = 'section';
	$('[data-sec-id="'+secId+'"].ap773-cs-section').addClass('is-selected');
	var sec = findSection(secId);
	if (!sec) return;
	$('#ap773-insp-title').text(i18n.sectionSettings);
	$('#ap773-insp-body').html(buildSectionInspector(sec));
	$('#ap773-inspector').show();
}

function deselect() {
	selectedId = null; selectedType = null;
	$('.ap773-cs-block,.ap773-cs-section').removeClass('is-selected');
}

function buildBlockInspector(b) {
	var d = b.data || {};
	var html = '';

	switch(b.type) {
		case 'heading':
			html += ifield('Text','text','text', esc(d.text||''));
			html += ifield('Level','level','select', d.level||'h2', '', [['h1','H1 — Large'],['h2','H2 — Medium'],['h3','H3 — Small'],['h4','H4 — Small']]);
			html += ifield('Alignment','align','select', d.align||'left', '', [['left','Left'],['center','Center'],['right','Right']]);
			break;
		case 'text':
			html += ifield('Content','content','textarea', esc(d.content||''));
			html += ifield('Alignment','align','select', d.align||'left', '', [['left','Left'],['center','Center'],['right','Right']]);
			break;
		case 'button':
			html += ifield('Label','label','text', esc(d.label||''));
			html += ifield('URL','url','url', esc(d.url||'#'));
			html += ifield('Style','style','select', d.style||'primary', '', [['primary','Primary (filled)'],['secondary','Secondary (outline)'],['link','Link style']]);
			html += ifield('Alignment','align','select', d.align||'left', '', [['left','Left'],['center','Center'],['right','Right']]);
			html += icheckbox('Open in new tab','new_tab', !!d.new_tab);
			break;
		case 'image':
			html += ifield('Image URL','url','url', esc(d.url||''));
			html += '<button type="button" class="ap773-btn ap773-btn--secondary" style="font-size:11px;padding:5px 10px;margin-top:-2px" onclick="ap773PickMedia(\''+b.id+'\')">'+esc(i18n.chooseMedia || 'Choose from Media Library')+'</button>';
			html += ifield('Alt text','alt','text', esc(d.alt||''));
			html += ifield('Caption','caption','text', esc(d.caption||''));
			html += ifield('Alignment','align','select', d.align||'left', '', [['left','Left'],['center','Center'],['right','Right']]);
			break;
		case 'video':
			html += ifield('YouTube / Vimeo URL','url','url', esc(d.url||''));
			html += ifield('Video title','title','text', esc(d.title||''));
			html += ifield('Video subtitle','subtitle','text', esc(d.subtitle||''));
			html += ifield('Duration label','duration','text', esc(d.duration||''));
			html += ifield('Alignment','align','select', d.align||'left', '', [['left','Left'],['center','Center'],['right','Right']]);
			break;
		case 'stats':
			html += '<div class="ap773-insp-sub">'+esc(i18n.showCounters || 'Show counters')+'</div>';
			['posts','pages','comments','users','media'].forEach(function(k){
				html += icheckbox(k.charAt(0).toUpperCase()+k.slice(1),'show_'+k, d['show_'+k]!==false);
			});
			html += ifield('Alignment','align','select', d.align||'left', '', [['left','Left'],['center','Center'],['right','Right']]);
			break;
		case 'hero':
			html += ifield('Eyebrow','eyebrow','text', esc(d.eyebrow||''));
			html += ifield('Title','title','text', esc(d.title||''));
			html += ifield('Text','content','textarea', esc(d.content||''));
			html += ifield('Theme','theme','select', d.theme||'blue', '', [['purple','Purple'],['blue','Blue'],['green','Green'],['softgreen','Soft green']]);
			html += ifield('Icon','icon','select', d.icon||'spark', '', [['spark','Spark'],['play','Play'],['chart','Chart'],['user','User'],['support','Support'],['shield','Shield']]);
			html += '<div class="ap773-insp-sep"></div>';
			html += '<div class="ap773-insp-sub">Optional CTA / support details</div>';
			html += ifield('Button label','button_label','text', esc(d.button_label||''));
			html += ifield('Button URL','button_url','url', esc(d.button_url||''));
			html += ifield('Support email','support_email','text', esc(d.support_email||''));
			html += ifield('Support phone','support_phone','text', esc(d.support_phone||''));
			break;
		case 'link_list':
			html += ifield('Title','title','text', esc(d.title||''));
			html += buildActionEditor(b, 'links');
			break;
		case 'checklist':
			html += ifield('Title','title','text', esc(d.title||''));
			html += '<div class="ap773-insp-help">One item per line. You can also use icon|label.</div>';
			html += ifield('Items','items','textarea', esc(d.items||''));
			html += ifield('Style','style','select', d.style||'check', '', [['check','Checklist'],['status','Status']]);
			break;
		case 'quick_actions':
			html += ifield('Title','title','text', esc(d.title||''));
			html += buildActionEditor(b, 'actions');
			break;
		case 'divider':
			html += '<p style="font-size:12px;color:#94a3b8">'+esc(i18n.dividerDesc || 'A horizontal divider line.')+'</p>';
			break;
	}

	var parentSec = findSectionByBlock(b.id);
	if (parentSec && parentSec.layout === 'grid12') {
		html += '<div class="ap773-insp-sep"></div>';
		html += ifield('Grid width','span','select', String(b.span || 12), '', [['3','1/4 width'],['4','1/3 width'],['5','5/12 width'],['6','1/2 width'],['7','7/12 width'],['8','2/3 width'],['9','3/4 width'],['12','Full width']]);
	}

	return html;
}

function buildSectionInspector(sec) {
	var html = '';
	html += '<div class="ap773-insp-sub">Layout</div>';
	html += '<div class="ap773-if"><label>Grid mode</label><select onchange="ap773SetSectionLayout(\''+sec.id+'\',this.value)"><option value="columns"'+((sec.layout||'columns')==='columns'?' selected':'')+'>Classic columns</option><option value="grid12"'+(sec.layout==='grid12'?' selected':'')+'>Flexible 12-column grid</option></select></div>';
	html += '<div class="ap773-if"><label>Spacing</label><select onchange="ap773SetSectionGap(\''+sec.id+'\',this.value)"><option value="compact"'+(sec.gap==='compact'?' selected':'')+'>Compact</option><option value="normal"'+((sec.gap||'normal')==='normal'?' selected':'')+'>Normal</option><option value="large"'+(sec.gap==='large'?' selected':'')+'>Large</option></select></div>';
	html += '<div class="ap773-insp-sep"></div>';
	html += '<div class="ap773-insp-sub">Section header</div>';
	html += '<div class="ap773-if"><label>Title</label><input type="text" value="'+esc(sec.title||'')+'" onchange="ap773SetSectionField(\''+sec.id+'\',\'title\',this.value)"></div>';
	html += '<div class="ap773-if"><label>CTA label</label><input type="text" value="'+esc(sec.cta_label||'')+'" onchange="ap773SetSectionField(\''+sec.id+'\',\'cta_label\',this.value)"></div>';
	html += '<div class="ap773-if"><label>CTA URL</label><input type="url" value="'+esc(sec.cta_url||'')+'" onchange="ap773SetSectionField(\''+sec.id+'\',\'cta_url\',this.value)"></div>';
	html += '<div class="ap773-insp-sep"></div>';
	html += '<div class="ap773-insp-sub">'+i18n.columns+'</div>';
	html += '<div class="ap773-cols-btns">';
	[1,2,3].forEach(function(n){
		var _svg={1:'<svg width="24" height="16" viewBox="0 0 24 16"><rect x="1" y="1" width="22" height="14" rx="2" stroke="currentColor" stroke-width="1.5" fill="none"/></svg>',2:'<svg width="24" height="16" viewBox="0 0 24 16"><rect x="1" y="1" width="10" height="14" rx="2" stroke="currentColor" stroke-width="1.5" fill="none"/><rect x="13" y="1" width="10" height="14" rx="2" stroke="currentColor" stroke-width="1.5" fill="none"/></svg>',3:'<svg width="24" height="16" viewBox="0 0 24 16"><rect x="1" y="1" width="6" height="14" rx="2" stroke="currentColor" stroke-width="1.5" fill="none"/><rect x="9" y="1" width="6" height="14" rx="2" stroke="currentColor" stroke-width="1.5" fill="none"/><rect x="17" y="1" width="6" height="14" rx="2" stroke="currentColor" stroke-width="1.5" fill="none"/></svg>'};
		html += '<button type="button" class="ap773-col-btn'+(sec.cols===n?' active':'')+'" title="'+n+' col'+(n>1?'s':'')+'" style="display:flex;flex-direction:column;align-items:center;gap:3px;padding:6px 4px" onclick="ap773SetCols(\''+sec.id+'\','+n+')">'+_svg[n]+'<span style="font-size:10px">'+n+'</span></button>';
	});
	html += '</div>';
	html += '<div class="ap773-insp-sep"></div>';
	html += '<div class="ap773-insp-sub">'+i18n.visibleToRoles+'</div>';
	html += buildRoleChecks(sec.roles, 'section', sec.id);
	return html;
}

function buildRoleChecks(roles, entityType, entityId) {
	var html = '<div class="ap773-role-checks">';
	var isAll = !roles || roles.length === 0 || roles.indexOf('all') !== -1;
	html += '<label class="ap773-role-check"><input type="checkbox" '+(isAll?'checked':'')+ ' onchange="ap773ToggleAllRoles(\''+entityId+'\',\''+entityType+'\',this.checked)"> <em>'+esc(i18n.allRolesLabel || 'All roles')+'</em></label>';
	Object.keys(allRoles).forEach(function(slug){
		var checked = isAll || roles.indexOf(slug) !== -1;
		var _n = (allRoles && typeof allRoles[slug] === 'string') ? allRoles[slug] : slug;
		html += '<label class="ap773-role-check"><input type="checkbox" '+(checked?'checked':'')+ ' data-role="'+slug+'" onchange="ap773ToggleRole(\''+entityId+'\',\''+entityType+'\',\''+slug+'\',this.checked)"> '+esc(_n)+'</label>';
	});
	html += '</div>';
	return html;
}

function ifield(label, key, type, val, placeholder, options) {
	var html = '<div class="ap773-if"><label>'+esc(label)+'</label>';
	if (type === 'select') {
		html += '<select onchange="ap773UpdateBlock(\''+selectedId+'\',\''+key+'\',this.value)">';
		(options||[]).forEach(function(o){ html += '<option value="'+esc(o[0])+'"'+(val===o[0]?' selected':'')+'>'+esc(o[1])+'</option>'; });
		html += '</select>';
	} else if (type === 'textarea') {
		html += '<textarea onchange="ap773UpdateBlock(\''+selectedId+'\',\''+key+'\',this.value)">'+val+'</textarea>';
	} else {
		html += '<input type="'+type+'" value="'+val+'" placeholder="'+(placeholder||'')+'" onchange="ap773UpdateBlock(\''+selectedId+'\',\''+key+'\',this.value)">';
	}
	html += '</div>';
	return html;
}

function icheckbox(label, key, checked) {
	return '<label class="ap773-role-check" style="margin:2px 0"><input type="checkbox" '+(checked?'checked':'')+' onchange="ap773UpdateBlock(\''+selectedId+'\',\''+key+'\',this.checked)"> '+esc(label)+'</label>';
}


function normalizeActionItems(data) {
	data = data || {};
	var raw = [];
	if (Array.isArray(data.items_data)) {
		raw = data.items_data;
	} else if (typeof data.items === 'string' && data.items.length) {
		raw = data.items.split(/\r\n|\r|\n/).map(function(line){
			var parts = String(line || '').split('|');
			if (!String(line || '').trim()) return null;
			if (parts.length === 1) return {icon:'✓', label:parts[0].trim(), url:'', source:'manual'};
			return {icon:(parts[0] || '•').trim(), label:(parts[1] || '').trim(), url:(parts[2] || '').trim(), source:'manual'};
		}).filter(Boolean);
	}
	return raw.map(function(item){
		item = item || {};
		return {
			icon: item.icon || '•',
			label: item.label || '',
			url: item.url || '',
			source: item.source || 'manual',
			choice: item.choice || ''
		};
	});
}

function syncActionItems(block) {
	if (!block || !block.data) return;
	var items = normalizeActionItems(block.data);
	block.data.items_data = items;
	block.data.items = items.map(function(item){
		return (item.icon || '•') + '|' + (item.label || '') + '|' + (item.url || '');
	}).join('\n');
}

function actionChoiceOptions(selectedUrl) {
	var html = '<option value="">'+esc(i18n.choosePreset || 'Choose preset/page…')+'</option>';
	(actionChoices || []).forEach(function(group){
		html += '<optgroup label="'+esc(group.label || '')+'">';
		(group.items || []).forEach(function(item){
			var selected = (selectedUrl && item.url === selectedUrl) ? ' selected' : '';
			html += '<option value="'+esc(item.url || '')+'" data-label="'+esc(item.label || '')+'" data-icon="'+esc(item.icon || '')+'"'+selected+'>'+esc(item.label || '')+'</option>';
		});
		html += '</optgroup>';
	});
	return html;
}

function buildActionEditor(block, context) {
	var items = normalizeActionItems(block.data || {});
	if (!items.length) {
		items = [{icon:'🔗',label:'New action',url:'#',source:'manual'}];
	}
	block.data.items_data = items;
	syncActionItems(block);
	var title = context === 'actions' ? (i18n.actions || 'Actions') : (i18n.links || 'Links');
	var html = '<div class="ap773-actions-editor">';
	html += '<div class="ap773-insp-help">'+esc(i18n.actionEditorHelp || 'Preconfigured buttons are ready to use. You can edit the label, icon and URL, choose a site page/content, or enter a manual link.')+'</div>';
	html += '<div class="ap773-actions-editor-head"><strong>'+esc(title)+'</strong><button type="button" class="ap773-mini-btn" onclick="ap773AddActionItem(\''+block.id+'\')">+'+esc(i18n.addAction || 'Add action')+'</button></div>';
	items.forEach(function(item, index){
		html += '<div class="ap773-action-row" data-action-index="'+index+'">';
		html += '<div class="ap773-action-row-top"><span class="ap773-action-grip">☰</span><strong>'+esc(item.label || ('Action '+(index+1)))+'</strong><button type="button" class="ap773-action-remove" onclick="ap773RemoveActionItem(\''+block.id+'\','+index+')">×</button></div>';
		html += '<label>Preset / site content<select onchange="ap773ApplyActionChoice(\''+block.id+'\','+index+',this)">'+actionChoiceOptions(item.url)+'</select></label>';
		html += '<div class="ap773-action-row-grid">';
		html += '<label>Icon<input type="text" value="'+esc(item.icon || '')+'" onchange="ap773UpdateActionItem(\''+block.id+'\','+index+',\'icon\',this.value)"></label>';
		html += '<label>Label<input type="text" value="'+esc(item.label || '')+'" onchange="ap773UpdateActionItem(\''+block.id+'\','+index+',\'label\',this.value)"></label>';
		html += '</div>';
		html += '<label>Manual URL<input type="url" value="'+esc(item.url || '')+'" placeholder="https://… or /wp-admin/…" onchange="ap773UpdateActionItem(\''+block.id+'\','+index+',\'url\',this.value)"></label>';
		html += '</div>';
	});
	html += '</div>';
	return html;
}

/* ============================================================
   LAYOUT MUTATIONS
============================================================ */
window.ap773UpdateBlock = function(blockId, key, val) {
	var b = findBlock(blockId);
	if (!b) return;
	if (key === 'span') {
		b.span = parseInt(val || 12, 10);
		renderAll();
		selectBlock(blockId);
		return;
	}
	b.data = b.data || {};
	b.data[key] = val;
	refreshBlockEl(b);
};

window.ap773AddActionItem = function(blockId) {
	var b = findBlock(blockId);
	if (!b) return;
	b.data = b.data || {};
	var items = normalizeActionItems(b.data);
	items.push({icon:'🔗', label:(i18n.newAction || 'New action'), url:'#', source:'manual'});
	b.data.items_data = items;
	syncActionItems(b);
	refreshBlockEl(b);
	selectBlock(blockId);
};

window.ap773RemoveActionItem = function(blockId, index) {
	var b = findBlock(blockId);
	if (!b) return;
	b.data = b.data || {};
	var items = normalizeActionItems(b.data);
	items.splice(index, 1);
	b.data.items_data = items;
	syncActionItems(b);
	refreshBlockEl(b);
	selectBlock(blockId);
};

window.ap773UpdateActionItem = function(blockId, index, key, value) {
	var b = findBlock(blockId);
	if (!b) return;
	b.data = b.data || {};
	var items = normalizeActionItems(b.data);
	items[index] = items[index] || {icon:'🔗', label:'', url:'#', source:'manual'};
	items[index][key] = value;
	if (key === 'url') items[index].source = 'manual';
	b.data.items_data = items;
	syncActionItems(b);
	refreshBlockEl(b);
};

window.ap773ApplyActionChoice = function(blockId, index, selectEl) {
	var b = findBlock(blockId);
	if (!b) return;
	var opt = selectEl.options[selectEl.selectedIndex];
	if (!opt || !opt.value) return;
	b.data = b.data || {};
	var items = normalizeActionItems(b.data);
	items[index] = items[index] || {icon:'🔗', label:'', url:'#', source:'preset'};
	items[index].url = opt.value;
	items[index].label = opt.getAttribute('data-label') || items[index].label || '';
	items[index].icon = opt.getAttribute('data-icon') || items[index].icon || '🔗';
	items[index].source = 'preset';
	b.data.items_data = items;
	syncActionItems(b);
	refreshBlockEl(b);
	selectBlock(blockId);
};

window.ap773SetCols = function(secId, cols) {
	var sec = findSection(secId);
	if (!sec) return;
	sec.cols = parseInt(cols, 10);
	// Reassign blocks to first column if their column index no longer exists.
	(sec.blocks||[]).forEach(function(b){ if ((b.col||0) >= cols) b.col = 0; });
	renderAll();
	selectSection(secId);
};

window.ap773SetSectionLayout = function(secId, mode) {
	var sec = findSection(secId);
	if (!sec) return;
	sec.layout = (mode === 'grid12') ? 'grid12' : 'columns';
	if (sec.layout === 'grid12') {
		(sec.blocks||[]).forEach(function(b){ b.span = parseInt(b.span || 12, 10); });
	}
	renderAll();
	selectSection(secId);
};

window.ap773SetSectionGap = function(secId, gap) {
	var sec = findSection(secId);
	if (!sec) return;
	sec.gap = ['compact','normal','large'].indexOf(gap) !== -1 ? gap : 'normal';
	renderAll();
	selectSection(secId);
};

window.ap773SetSectionField = function(secId, key, value) {
	var sec = findSection(secId);
	if (!sec) return;
	if (['title','cta_label','cta_url'].indexOf(key) === -1) return;
	sec[key] = value;
	renderAll();
	selectSection(secId);
};

window.ap773ToggleAllRoles = function(id, type, checked) {
	var entity = type === 'section' ? findSection(id) : findBlock(id);
	if (!entity) return;
	if (checked) {
		// "All roles" checked → set to ['all'] (all individual roles also shown as checked)
		entity.roles = ['all'];
	} else {
		// "All roles" unchecked → deselect everything (no roles = visible to nobody)
		entity.roles = [];
	}
	refreshEntityRoles(id, type);
};

window.ap773ToggleRole = function(id, type, role, checked) {
	var entity = type === 'section' ? findSection(id) : findBlock(id);
	if (!entity) return;
	var slugs = Object.keys(allRoles);
	// If currently 'all', expand to full list of individual slugs first
	if (entity.roles.indexOf('all') !== -1) {
		entity.roles = slugs.slice();
	}
	if (checked) {
		// Add this role
		if (entity.roles.indexOf(role) === -1) { entity.roles.push(role); }
		// If now all roles are selected, collapse back to ['all']
		if (entity.roles.length === slugs.length) { entity.roles = ['all']; }
	} else {
		// Remove this role — also auto-unchecks 'All roles'
		entity.roles = entity.roles.filter(function(r) { return r !== role; });
		// Never collapse to ['all'] since we just removed one
	}
	refreshEntityRoles(id, type);
};

window.ap773PickMedia = function(blockId) {
	var frame = wp.media({ title: (i18n.selectImage || 'Select image'), button: { text: (i18n.useThisImage || 'Use this image') }, multiple: false, library: { type: 'image' } });
	frame.on('select', function(){
		var att = frame.state().get('selection').first().toJSON();
		ap773UpdateBlock(blockId, 'url', att.url);
		ap773UpdateBlock(blockId, 'alt', att.alt || att.title || '');
		var b = findBlock(blockId); if (b) selectBlock(blockId);
	});
	frame.open();
};

window.ap773PickMedia = window.ap773PickMedia || function(){};

function addSection() {
	var sec = { id: uid(), cols: 1, layout: 'columns', gap: 'normal', roles: ['all'], blocks: [] };
	layout.sections = layout.sections || [];
	layout.sections.push(sec);
	renderAll();
	selectSection(sec.id);
}

function deleteSection(secId) {
	layout.sections = layout.sections.filter(function(s){ return s.id !== secId; });
	renderAll();
	deselect();
}

function moveSectionDir(secId, dir) {
	var idx = layout.sections.findIndex(function(s){ return s.id === secId; });
	var ni = idx + dir;
	if (ni < 0 || ni >= layout.sections.length) return;
	var tmp = layout.sections[idx]; layout.sections[idx] = layout.sections[ni]; layout.sections[ni] = tmp;
	renderAll();
}

function addBlockToSection(secId, col, type) {
	var sec = findSection(secId);
	if (!sec) return;
	var b = { id: uid(), type: type, col: col, data: defaultData(type) };
	sec.blocks = sec.blocks || [];
	sec.blocks.push(b);
	renderAll();
	selectBlock(b.id);
}

function deleteBlock(blockId) {
	layout.sections.forEach(function(sec){
		sec.blocks = (sec.blocks||[]).filter(function(b){ return b.id !== blockId; });
	});
	renderAll();
	deselect();
}

function moveBlockInCol(blockId, dir) {
	layout.sections.forEach(function(sec){
		var col = (findBlock(blockId)||{}).col || 0;
		var colBlocks = (sec.blocks||[]).filter(function(b){ return b.col === col; });
		var idx = colBlocks.findIndex(function(b){ return b.id === blockId; });
		var ni = idx + dir;
		if (idx === -1 || ni < 0 || ni >= colBlocks.length) return;
		var allIdx1 = sec.blocks.indexOf(colBlocks[idx]);
		var allIdx2 = sec.blocks.indexOf(colBlocks[ni]);
		if (allIdx1 === -1 || allIdx2 === -1) return;
		var tmp = sec.blocks[allIdx1]; sec.blocks[allIdx1] = sec.blocks[allIdx2]; sec.blocks[allIdx2] = tmp;
	});
	renderAll();
}


/* ============================================================
   DASHBOARD TEMPLATES
============================================================ */
function bindTemplates() {
	$(document).on('click', '.ap773-use-template', function(){
		var templateId = $(this).data('template-id');
		installTemplate(templateId);
	});
}

function installTemplate(templateId) {
	var tpl = templates ? templates[templateId] : null;
	if (!tpl || !tpl.sections) return;

	if (layout.sections && layout.sections.length) {
		if (!confirm(i18n.confirmTemplateReplace || 'This will replace your current dashboard layout. Continue?')) {
			return;
		}
	}

	layout.enabled = true;
	layout.hide_wp = true;
	layout.hide_screen_opts = true;
	layout.hide_help = true;
	layout.hide_notices = true;
	layout.hide_title = true;
	layout.defaults_initialized = true;
	layout.sections = cloneTemplateSections(tpl.sections);

	$('#ap773-builder-enabled').prop('checked', true);
	$('#ap773-opt-hide-wp').prop('checked', true);
	$('#ap773-opt-hide-screen').prop('checked', true);
	$('#ap773-opt-hide-help').prop('checked', true);
	$('#ap773-opt-hide-notices').prop('checked', true);
	$('#ap773-opt-hide-title').prop('checked', true);
	$('#ap773-builder-opts,#ap773-builder-shell').css({ opacity:'1', 'pointer-events':'auto' });

	renderAll();
	deselect();
	saveLayout(i18n.templateInstalled || 'Template installed. You can now customize it with the Dashboard Builder.');
}

function cloneTemplateSections(sections) {
	return (sections || []).map(function(sec){
		return {
			id: sec.id || uid(),
			cols: parseInt(sec.cols || 1, 10),
			layout: sec.layout || 'columns',
			gap: sec.gap || 'normal',
			roles: (sec.roles || ['all']).slice(),
			blocks: (sec.blocks || []).map(function(block){
				return {
					id: block.id || uid(),
					type: block.type,
					col: parseInt(block.col || 0, 10),
					span: parseInt(block.span || 12, 10),
					roles: (block.roles || ['all']).slice(),
					data: JSON.parse(JSON.stringify(block.data || {}))
				};
			})
		};
	});
}

/* ============================================================
   DEFAULTS
============================================================ */
function defaultData(type) {
	var defaults = {
		heading: { text: (i18n.defaultHeading || 'Welcome to your dashboard'), level:'h2', align:'left' },
		text:    { content: (i18n.defaultText || 'Add your text here.'), align:'left' },
		button:  { label: (i18n.defaultButton || 'Click here'), url:'#', style:'primary', new_tab:false, align:'left' },
		image:   { url:'', alt:'', caption:'', align:'left' },
		divider: {},
		video:   { url:'', title:'', subtitle:'', duration:'', align:'left' },
		stats:   { show_posts:true, show_pages:true, show_comments:true, show_users:false, show_media:false, align:'left' },
		hero:    { eyebrow:'Welcome', title:'Hero title', content:'Add a short dashboard message here.', theme:'blue', icon:'spark' },
		link_list: { title:'Useful Links', items_data:[{icon:'🌐',label:'Visit website',url:'/',source:'manual'},{icon:'✏️',label:'Edit pages',url:'/wp-admin/edit.php?post_type=page',source:'manual'}] },
		checklist: { title:'Checklist', items:'First item\nSecond item\nThird item', style:'check' },
		quick_actions: { title:'Quick Actions', items_data:[{icon:'✏️',label:'Add new post',url:'/wp-admin/post-new.php',source:'manual'},{icon:'🖼️',label:'Upload media',url:'/wp-admin/media-new.php',source:'manual'}] },
		file:    { label:'', url:'', filename:'', size:'' },
		form:    { shortcode:'' },
		html:    { code:'<p>Your HTML here</p>' },
	};
	return defaults[type] || {};
}

/* ============================================================
   DRAG & DROP — sidebar → canvas column
============================================================ */
function bindSidebarDrag() {
	$(document).on('dragstart', '.ap773-bl-block', function(e){
		dragBlockType = $(this).data('block-type');
		$(this).addClass('is-dragging');
	});
	$(document).on('dragend', '.ap773-bl-block', function(){
		$('.ap773-bl-block').removeClass('is-dragging');
		dragBlockType = null;
	});
}

function bindColDrop($col) {
	$col[0].addEventListener('dragover', function(e){ e.preventDefault(); $col.addClass('drag-over'); });
	$col[0].addEventListener('dragleave', function(){ $col.removeClass('drag-over'); });
	$col[0].addEventListener('drop', function(e){
		e.preventDefault();
		$col.removeClass('drag-over');
		if (!dragBlockType) return;
		var secId = $col.data('sec-id');
		var colIdx = parseInt($col.data('col'), 10);
		addBlockToSection(secId, colIdx, dragBlockType);
		dragBlockType = null;
	});
}

/* ============================================================
   ROLE PREVIEW FILTER
============================================================ */
function updateRolePreviewBanner(role) {
	var $b = $('#ap773-role-preview-banner');
	if (!role || role === 'all') { $b.css('display','none'); return; }
	$b.find('.ap773-rpb-role').text($('#ap773-role-preview option:selected').text());
	$b.css('display','flex');
}

function applyRolePreview(role) {
	if (role === 'all') {
		$('.ap773-cs-section,.ap773-cs-block').removeClass('role-hidden');
		return;
	}
	layout.sections.forEach(function(sec){
		var secEl = $('[data-sec-id="'+sec.id+'"].ap773-cs-section');
		var secVis = isVisible(sec.roles, role);
		secEl.toggleClass('role-hidden', !secVis);

	});
}

function isVisible(roles, role) {
	if (!roles || !roles.length || roles.indexOf('all') !== -1) return true;
	return roles.indexOf(role) !== -1;
}

/* ============================================================
   REFRESH HELPERS (avoid full re-render when possible)
============================================================ */
function refreshBlockEl(b) {
	var $el = $('[data-block-id="'+b.id+'"]');
	$el.find('.ap773-cs-block-preview').html(blockPreview(b));
}

function refreshEntityRoles(id, type) {
	var entity = type === 'section' ? findSection(id) : findBlock(id);
	if (!entity) return;
	if (type === 'section') {
		// Update roles badge in section header
		$('.ap773-cs-sec-roles[data-sec-id="'+id+'"]').html(rolesLabel(entity.roles));
		// Update inspector checkboxes visually WITHOUT rebuilding the DOM.
		// We directly set each checkbox's checked state so the user sees the change.
		// Using setTimeout(0) so the current checkbox event finishes first.
		setTimeout(function() {
			if (selectedId !== id || selectedType !== 'section') return;
			var isAll = entity.roles.indexOf('all') !== -1;
			// Update "All roles" checkbox
			$('#ap773-insp-body input[onchange*="ap773ToggleAllRoles"]').prop('checked', isAll);
			// Update individual role checkboxes
			$('#ap773-insp-body input[data-role]').each(function() {
				var slug = $(this).data('role');
				$(this).prop('checked', isAll || entity.roles.indexOf(slug) !== -1);
			});
		}, 0);
	}
}

/* ============================================================
   FINDERS
============================================================ */
function findSection(secId) {
	return (layout.sections||[]).find(function(s){ return s.id === secId; });
}
function findBlock(blockId) {
	var found = null;
	(layout.sections||[]).forEach(function(sec){
		(sec.blocks||[]).forEach(function(b){ if (b.id === blockId) found = b; });
	});
	return found;
}

function findSectionByBlock(blockId) {
	var found = null;
	(layout.sections||[]).forEach(function(sec){
		(sec.blocks||[]).forEach(function(b){ if (b.id === blockId) found = sec; });
	});
	return found;
}

/* ============================================================
   UI HELPERS
============================================================ */
function rolesLabel(roles) {
	if (!roles || !roles.length || roles.indexOf('all') !== -1) {
		return '<span class="ap773-role-chip ap773-role-chip-all">' + esc(cfg.allRolesLabel || 'All roles') + '</span>';
	}
	return roles.map(function(r) {
		var name = (allRoles && typeof allRoles[r] === 'string') ? allRoles[r] : r;
		return '<span class="ap773-role-chip ap773-role-chip-role">' + esc(name) + '</span>';
	}).join('');
}

function iconBtn(action, svgPaths, title, extraClass) {
	return '<button type="button" class="ap773-icon-btn'+(extraClass?' '+extraClass:'')+'" data-action="'+action+'" title="'+esc(title)+'"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">'+svgPaths+'</svg></button>';
}

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

function applyFirstEnableDefaults() {
	layout.hide_wp = true;
	layout.hide_screen_opts = true;
	layout.hide_help = true;
	layout.hide_notices = true;
	layout.hide_title = true;
	layout.defaults_initialized = true;

	$('#ap773-opt-hide-wp').prop('checked', true);
	$('#ap773-opt-hide-screen').prop('checked', true);
	$('#ap773-opt-hide-help').prop('checked', true);
	$('#ap773-opt-hide-notices').prop('checked', true);
	$('#ap773-opt-hide-title').prop('checked', true);
}

function syncTopControlsToLayout() {
	layout.enabled = $('#ap773-builder-enabled').is(':checked');
	layout.hide_wp = $('#ap773-opt-hide-wp').is(':checked');
	layout.hide_screen_opts = $('#ap773-opt-hide-screen').is(':checked');
	layout.hide_help = $('#ap773-opt-hide-help').is(':checked');
	layout.hide_notices = $('#ap773-opt-hide-notices').is(':checked');
	layout.hide_title = $('#ap773-opt-hide-title').is(':checked');
}

/* ============================================================
   TOP CONTROLS BINDING
============================================================ */
function bindTopControls() {
	$('#ap773-builder-enabled').on('change', function(){
		layout.enabled = this.checked;

		if (this.checked && !layout.defaults_initialized) {
			applyFirstEnableDefaults();
		}

		var disable = !this.checked;
		$('#ap773-builder-opts,#ap773-builder-shell').css({ opacity: disable?'0.45':'1', 'pointer-events': disable?'none':'auto' });
	});
	$('#ap773-opt-hide-wp').on('change',     function(){ layout.hide_wp = this.checked; });
	$('#ap773-opt-hide-screen').on('change', function(){ layout.hide_screen_opts = this.checked; });
	$('#ap773-opt-hide-help').on('change',   function(){ layout.hide_help = this.checked; });
	$('#ap773-opt-hide-notices').on('change', function(){ layout.hide_notices = this.checked; });
	$('#ap773-opt-hide-title').on('change', function(){ layout.hide_title = this.checked; });
	$('#ap773-role-preview').on('change', function(){
		applyRolePreview(this.value);
		try { localStorage.setItem('ap773_role_preview', this.value); } catch(e){}
		updateRolePreviewBanner(this.value);
	});
	// Restore persisted role preview selection
	try {
		var _rp = localStorage.getItem('ap773_role_preview');
		if (_rp) { $('#ap773-role-preview').val(_rp); applyRolePreview(_rp); updateRolePreviewBanner(_rp); }
	} catch(e){}
}

function bindAddSection() {
	$('#ap773-add-section').on('click', addSection);
}

function bindInspectorClose() {
	$('#ap773-insp-close').on('click', function(){ $('#ap773-inspector').hide(); deselect(); });
}

/* ============================================================
   SAVE
============================================================ */
function bindSave() {
	$('#ap773-save-btn').on('click', function(e) {
		e.preventDefault();
		saveLayout();
	});
}

function saveLayout(successMessage) {
	if (typeof successMessage !== 'string') {
		successMessage = '';
	}
	syncTopControlsToLayout();

	var $btn = $('#ap773-save-btn');
	var $status = $('#ap773-save-status');
	
	if ( ! cfg.ajaxUrl && typeof ajaxurl === 'undefined' ) {
		$status.text('AJAX URL not available').removeClass('success').addClass('error');
		console.error('Panelra builder: ajaxUrl missing', cfg);
		return;
	}

	$btn.prop('disabled', true);
	$status.text(i18n.saving).removeClass('error success');

	$.ajax({
	url: cfg.ajaxUrl || (typeof ajaxurl !== 'undefined' ? ajaxurl : ''),
	type: 'POST',
	dataType: 'json',
	data: {
		action: 'pnlrdb_save_layout',
		nonce: cfg.nonce,
		layout: JSON.stringify(layout)
	}
})
	.done(function(resp) {
		$btn.prop('disabled', false);

		if (resp && resp.success) {
			var msg = successMessage || ((resp.data && resp.data.message) ? resp.data.message : i18n.saved);
			$status.text(msg).removeClass('error').addClass('success');
			setTimeout(function(){ $status.text('').removeClass('success'); }, 3000);
		} else {
			var msg = (resp && resp.data && resp.data.message) ? resp.data.message : i18n.saveError;
			$status.text(msg).removeClass('success').addClass('error');
			console.error('Panelra builder save error:', resp);
		}
	})
	.fail(function(xhr) {
		$btn.prop('disabled', false);

		var msg = i18n.saveError;

		if (xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) {
			msg = xhr.responseJSON.data.message;
		} else if (xhr.responseText) {
			console.error('Panelra builder raw response:', xhr.responseText);
		}

		$status.text(msg).removeClass('success').addClass('error');
		console.error('Panelra builder AJAX failed:', xhr);
	});
}

/* ============================================================
   BOOT
============================================================ */
$(document).ready(init);

})(jQuery);
