/** * OTS Theme - Custom Block Editor Scripts (InnerBlocks Architecture) * * 13 blocks: 5 standalone + 4 parent/child pairs. * Parent blocks use InnerBlocks for child items. * Child blocks are dynamic (save -> null) with inline RichText editing. */ (function (wp) { 'use strict'; var el = wp.element.createElement; var Frag = wp.element.Fragment; var reg = wp.blocks.registerBlockType; var RT = wp.blockEditor.RichText; var IC = wp.blockEditor.InspectorControls; var IB = wp.blockEditor.InnerBlocks; var PB = wp.components.PanelBody; var TC = wp.components.TextControl; var TA = wp.components.TextareaControl; var SC = wp.components.SelectControl; var TG = wp.components.ToggleControl; var RC = wp.components.RangeControl; var Btn = wp.components.Button; var MUC = wp.blockEditor.MediaUploadCheck; var MU = wp.blockEditor.MediaUpload; var useS = wp.element.useState; /* ── Simple array helpers (for pricing-card features) ────────────────── */ function arrSet(arr, i, val) { var c = arr.slice(); c[i] = val; return c; } function arrAdd(arr, val) { return arr.concat([val]); } function arrRm(arr, i) { var c = arr.slice(); c.splice(i, 1); return c; } /* ── Shared card image attributes ────────────────────────────────────── */ var CARD_IMAGE_ATTRS = { imgId: { type: 'number', default: 0 }, imgUrl: { type: 'string', default: '' }, imgAlt: { type: 'string', default: '' }, imgWidth: { type: 'number', default: 80 }, imgHeight: { type: 'number', default: 0 }, imgFit: { type: 'string', default: 'contain' }, imgPosition: { type: 'string', default: 'top' } }; /* ── Shared icon attributes ──────────────────────────────────────────── */ var CARD_ICON_ATTRS = { icon: { type: 'string', default: '' }, iconType: { type: 'string', default: 'emoji' }, faIcon: { type: 'string', default: '' } }; /** * Build icon InspectorControls: a toggle that switches between emoji and * Font Awesome mode, with the appropriate text input shown beneath it. */ /* ── Font Awesome icon catalogue ─────────────────────────────────────── */ // Format: ['s' = fas solid | 'b' = fab brand, 'icon-name'] var FA_ICONS = [ // Navigation ['s','angle-down'],['s','angle-left'],['s','angle-right'],['s','angle-up'], ['s','angles-down'],['s','angles-left'],['s','angles-right'],['s','angles-up'], ['s','arrow-down'],['s','arrow-left'],['s','arrow-right'],['s','arrow-up'], ['s','arrow-rotate-left'],['s','arrow-rotate-right'],['s','arrows-rotate'], ['s','arrows-up-down-left-right'],['s','caret-down'],['s','caret-left'], ['s','caret-right'],['s','caret-up'],['s','chevron-down'],['s','chevron-left'], ['s','chevron-right'],['s','chevron-up'],['s','circle-arrow-down'], ['s','circle-arrow-left'],['s','circle-arrow-right'],['s','circle-arrow-up'], // Interface ['s','asterisk'],['s','at'],['s','ban'],['s','bars'],['s','bell'],['s','bell-slash'], ['s','bolt'],['s','check'],['s','check-double'],['s','circle-check'], ['s','circle-exclamation'],['s','circle-info'],['s','circle-minus'], ['s','circle-plus'],['s','circle-question'],['s','circle-xmark'], ['s','ellipsis'],['s','ellipsis-vertical'],['s','eye'],['s','eye-slash'], ['s','filter'],['s','grip'],['s','grip-vertical'],['s','hashtag'],['s','key'], ['s','lock'],['s','lock-open'],['s','magnifying-glass'],['s','minus'],['s','plus'], ['s','power-off'],['s','rotate'],['s','shield'],['s','shield-check'], ['s','shield-halved'],['s','sliders'],['s','sort'],['s','sort-down'],['s','sort-up'], ['s','spinner'],['s','star'],['s','star-half-stroke'],['s','toggle-off'], ['s','toggle-on'],['s','xmark'], // Text editing ['s','align-center'],['s','align-justify'],['s','align-left'],['s','align-right'], ['s','bold'],['s','code'],['s','crop'],['s','eraser'],['s','highlight'], ['s','indent'],['s','italic'],['s','link'],['s','link-slash'],['s','list'], ['s','list-check'],['s','list-ol'],['s','list-ul'],['s','outdent'],['s','pen'], ['s','pen-to-square'],['s','pencil'],['s','quote-left'],['s','quote-right'], ['s','scissors'],['s','strikethrough'],['s','subscript'],['s','superscript'], ['s','table'],['s','table-list'],['s','underline'], // Files and documents ['s','book'],['s','book-open'],['s','bookmark'],['s','box'],['s','box-archive'], ['s','boxes-stacked'],['s','clipboard'],['s','clipboard-check'], ['s','clipboard-list'],['s','copy'],['s','database'],['s','download'], ['s','envelope'],['s','envelope-open'],['s','file'],['s','file-arrow-down'], ['s','file-arrow-up'],['s','file-code'],['s','file-excel'],['s','file-image'], ['s','file-lines'],['s','file-pdf'],['s','file-powerpoint'],['s','file-word'], ['s','file-zipper'],['s','floppy-disk'],['s','folder'],['s','folder-minus'], ['s','folder-open'],['s','folder-plus'],['s','inbox'],['s','newspaper'], ['s','paper-plane'],['s','paperclip'],['s','print'],['s','share'], ['s','share-nodes'],['s','upload'], // Technology ['s','barcode'],['s','bug'],['s','cloud'],['s','cloud-arrow-down'], ['s','cloud-arrow-up'],['s','code-branch'],['s','cpu'],['s','desktop'], ['s','display'],['s','ethernet'],['s','gear'],['s','gears'],['s','hard-drive'], ['s','keyboard'],['s','laptop'],['s','memory'],['s','microchip'],['s','mobile'], ['s','mobile-screen'],['s','network-wired'],['s','plug'],['s','qrcode'], ['s','robot'],['s','satellite'],['s','satellite-dish'],['s','server'], ['s','tablet'],['s','terminal'],['s','toolbox'],['s','wifi'],['s','wrench'], // Media and communication ['s','backward'],['s','backward-fast'],['s','camera'],['s','camera-retro'], ['s','clapperboard'],['s','comment'],['s','comment-dots'],['s','comments'], ['s','film'],['s','forward'],['s','forward-fast'],['s','headphones'], ['s','message'],['s','microphone'],['s','microphone-slash'],['s','music'], ['s','pause'],['s','phone'],['s','phone-slash'],['s','phone-volume'], ['s','photo-film'],['s','play'],['s','radio'],['s','repeat'],['s','rss'], ['s','shuffle'],['s','stop'],['s','tv'],['s','video'],['s','video-slash'], ['s','voicemail'],['s','volume-high'],['s','volume-low'],['s','volume-xmark'], // People ['s','address-book'],['s','address-card'],['s','baby'],['s','child'], ['s','circle-user'],['s','people-group'],['s','person'],['s','person-running'], ['s','person-walking'],['s','thumbs-down'],['s','thumbs-up'],['s','user'], ['s','user-check'],['s','user-group'],['s','user-minus'],['s','user-plus'], ['s','user-slash'],['s','user-tie'],['s','users'], // Location and maps ['s','building'],['s','buildings'],['s','city'],['s','compass'], ['s','earth-americas'],['s','earth-asia'],['s','earth-europe'],['s','flag'], ['s','globe'],['s','house'],['s','location-dot'],['s','location-pin'], ['s','map'],['s','map-pin'],['s','mountain'],['s','road'],['s','route'], ['s','signs-post'], // Business and finance ['s','award'],['s','briefcase'],['s','bullhorn'],['s','bullseye'], ['s','calendar'],['s','calendar-check'],['s','calendar-days'], ['s','calendar-minus'],['s','calendar-plus'],['s','certificate'], ['s','chart-bar'],['s','chart-column'],['s','chart-line'],['s','chart-pie'], ['s','chart-simple'],['s','clock'],['s','coins'],['s','credit-card'], ['s','crown'],['s','gem'],['s','gift'],['s','handshake'],['s','medal'], ['s','money-bill'],['s','money-bill-wave'],['s','piggy-bank'],['s','receipt'], ['s','stopwatch'],['s','suitcase'],['s','tag'],['s','tags'],['s','timer'], ['s','trophy'],['s','truck'],['s','wallet'], // Nature and weather ['s','fire'],['s','fire-flame-curved'],['s','leaf'],['s','moon'], ['s','seedling'],['s','snowflake'],['s','sun'],['s','thermometer'], ['s','tree'],['s','water'],['s','wind'], // Health and medical ['s','bandage'],['s','bone'],['s','brain'],['s','dna'],['s','heart'], ['s','heart-pulse'],['s','hospital'],['s','pills'],['s','stethoscope'], ['s','syringe'],['s','tooth'],['s','virus'],['s','wheelchair'], // Brands ['b','android'],['b','angular'],['b','apple'],['b','aws'],['b','bitbucket'], ['b','bootstrap'],['b','chrome'],['b','css3'],['b','discord'],['b','docker'], ['b','dribbble'],['b','dropbox'],['b','facebook'],['b','facebook-f'], ['b','figma'],['b','firefox'],['b','git'],['b','github'],['b','gitlab'], ['b','google'],['b','html5'],['b','instagram'],['b','java'],['b','linkedin'], ['b','linux'],['b','microsoft'],['b','node-js'],['b','npm'],['b','php'], ['b','python'],['b','react'],['b','slack'],['b','square-js'],['b','tiktok'], ['b','vuejs'],['b','whatsapp'],['b','windows'],['b','wordpress'], ['b','x-twitter'],['b','youtube'] ]; /* ── Icon picker component ───────────────────────────────────────────── */ function IconPicker(props) { var value = props.value || ''; var onChange = props.onChange; var qs = useS(''); var query = qs[0], setQuery = qs[1]; var lower = query.toLowerCase().replace(/\s+/g, '-'); var filtered = query ? FA_ICONS.filter(function(ic){ return ic[1].indexOf(lower) !== -1; }) : FA_ICONS; return el('div', { className: 'oribi-icon-picker' }, // Current selection row value ? el('div', { className: 'oribi-icon-current' }, el('i', { className: value, 'aria-hidden': 'true' }), el('span', { className: 'oribi-icon-current-label' }, value), el('button', { className: 'oribi-icon-clear', type: 'button', onClick: function(){ onChange(''); } }, '\u2715 Clear') ) : null, // Search input el('input', { type: 'search', className: 'oribi-icon-search', placeholder: 'Search ' + FA_ICONS.length + ' icons…', value: query, onChange: function(e){ setQuery(e.target.value); } }), // Result count badge (only when filtering) query ? el('div', { className: 'oribi-icon-count' }, filtered.length + ' result' + (filtered.length === 1 ? '' : 's')) : null, // Icon grid el('div', { className: 'oribi-icon-grid' }, filtered.length === 0 ? el('div', { className: 'oribi-icon-empty' }, 'No icons found.') : filtered.map(function(ic) { var prefix = ic[0] === 'b' ? 'fab' : 'fas'; var cls = prefix + ' fa-' + ic[1]; var active = cls === value; return el('button', { key: cls, type: 'button', title: ic[1], className: 'oribi-icon-cell' + (active ? ' is-active' : ''), onClick: function(){ onChange(cls); } }, el('i', { className: cls, 'aria-hidden': 'true' }), el('span', null, ic[1]) ); }) ) ); } function iconControls(a, s) { var useFa = a.iconType === 'fontawesome'; return el(Frag, null, el(TG, { label: 'Use Font Awesome icon', checked: useFa, onChange: function(v){ s({ iconType: v ? 'fontawesome' : 'emoji' }); } }), useFa ? el(IconPicker, { value: a.faIcon || '', onChange: function(v){ s({ faIcon: v }); } }) : el(TC, { label: 'Icon (emoji)', value: a.icon || '', onChange: function(v){ s({ icon: v }); } }) ); } /** * Return the element to render in the card preview for the current icon state. * cssClass - the wrapper class, e.g. 'feature-icon' or 'value-icon' * extraStyle - optional inline style object for the wrapper */ function iconPreview(a, cssClass, extraStyle) { var useFa = a.iconType === 'fontawesome'; var hasIcon = useFa ? !!a.faIcon : !!a.icon; if (!hasIcon) return null; var child = useFa ? el('i', { className: a.faIcon || '', 'aria-hidden': 'true' }) : a.icon; return el('div', { className: cssClass, style: extraStyle || {} }, child); } /** Build shared Card Image InspectorControls panel. */ function cardImageControls(a, s) { var imgW = a.imgWidth || 80; var imgH = a.imgHeight || 0; var imgPos = a.imgPosition || 'top'; var imgFit = a.imgFit || 'contain'; return el(PB, { title: 'Card Image', initialOpen: false }, el(MUC, null, el(MU, { onSelect: function(media){ s({ imgId: media.id, imgUrl: media.url, imgAlt: media.alt || '' }); }, allowedTypes: ['image'], value: a.imgId || 0, render: function(ref) { return el(Frag, null, a.imgUrl ? el('div', { style: { marginBottom: '8px' } }, el('img', { src: a.imgUrl, style: { maxWidth: '100%', height: 'auto', borderRadius: '4px', display: 'block', marginBottom: '6px' } }), el(Btn, { variant: 'link', isDestructive: true, onClick: function(){ s({ imgId: 0, imgUrl: '', imgAlt: '' }); } }, 'Remove image') ) : null, el(Btn, { onClick: ref.open, variant: 'secondary', __next40pxDefaultSize: true }, a.imgUrl ? 'Replace image' : 'Select from Media Library') ); } }) ), a.imgUrl ? el(Frag, null, el(RC, { label: 'Width (px)', value: imgW, min: 20, max: 600, step: 4, onChange: function(v){ s({ imgWidth: v }); } }), el(RC, { label: 'Height (px) - 0 = auto', value: imgH, min: 0, max: 600, step: 4, onChange: function(v){ s({ imgHeight: v }); } }), el(TG, { label: 'Scale to fill (cover)', checked: imgFit === 'cover', onChange: function(v){ s({ imgFit: v ? 'cover' : 'contain' }); } }), el(SC, { label: 'Position', value: imgPos, options: [ { label: 'Above content', value: 'top' }, { label: 'Left of content', value: 'left' }, { label: 'Replace icon', value: 'replace-icon' }, { label: 'Background', value: 'background' } ], onChange: function(v){ s({ imgPosition: v }); } }) ) : null ); } /** Build an image preview element for the editor. */ function cardImagePreview(a) { if (!a.imgUrl) return null; var imgW = a.imgWidth || 80; var imgH = a.imgHeight || 0; var imgFit = a.imgFit || 'contain'; var style = { width: imgW + 'px', maxWidth: '100%', height: imgH > 0 ? imgH + 'px' : 'auto', borderRadius: '4px', objectFit: imgFit, display: 'block' }; return el('div', { className: 'oribi-card-img-wrap', style: { marginBottom: '1.25rem' } }, el('img', { src: a.imgUrl, className: 'oribi-card-img oribi-card-img--' + imgFit, style: style }) ); } /** Build a shared card section edit component. */ function createCardSectionEdit(allowedBlocks, defaultTemplate, label) { return function(props) { var a = props.attributes, s = props.setAttributes; return el(Frag, null, el(IC, null, el(PB, { title: 'Section Settings' }, el(SC, { label: 'Background', value: a.variant, options: [ { label: 'Normal', value: 'normal' }, { label: 'Alternate', value: 'alt' } ], onChange: function(v){s({variant:v});} }), el(RC, { label: 'Columns', value: a.columns, min: 1, max: 4, onChange: function(v){s({columns:v});} }), el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} }) ) ), el('section', { className: a.variant === 'alt' ? 'section section-alt' : 'section' }, el('div', { className: 'container' }, el('div', { className: 'section-header' }, a.label ? el('span', { className: 'section-label' }, a.label) : null, el(RT, { tagName: 'h2', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Section heading...' }), el(RT, { tagName: 'p', className: 'lead', value: a.lead, onChange: function(v){s({lead:v});}, placeholder: 'Lead text...' }) ), el('div', { className: 'grid-' + a.columns }, el(IB, { allowedBlocks: allowedBlocks, template: defaultTemplate, templateLock: false }) ) ) ) ); }; } /** Standard section attributes for JS. */ var SECTION_ATTRS = { align: { type: 'string', default: 'full' }, variant: { type: 'string', default: 'normal' }, label: { type: 'string', default: '' }, heading: { type: 'string', default: '' }, lead: { type: 'string', default: '' }, columns: { type: 'number', default: 3 } }; /* ═══════════════════════════════════════════════════════════════════════ STANDALONE BLOCKS (unchanged architecture) ═══════════════════════════════════════════════════════════════════════ */ /* 1. HERO ─────────────────────────────────────────────────────────────── */ reg('oribi/hero', { title: 'Oribi Hero', icon: 'cover-image', category: 'oribi', supports: { align: ['full'], html: false }, attributes: { align: { type: 'string', default: 'full' }, label: { type: 'string', default: '' }, title: { type: 'string', default: '' }, highlightWord: { type: 'string', default: '' }, description: { type: 'string', default: '' }, primaryBtnText: { type: 'string', default: 'Get in Touch' }, primaryBtnUrl: { type: 'string', default: '/contact' }, secondaryBtnText: { type: 'string', default: '' }, secondaryBtnUrl: { type: 'string', default: '' }, stat1Value: { type: 'string', default: '' }, stat1Label: { type: 'string', default: '' }, stat2Value: { type: 'string', default: '' }, stat2Label: { type: 'string', default: '' }, svcLaptop1: { type: 'string', default: 'Data Backup' }, svcLaptop2: { type: 'string', default: 'Endpoint Security' }, svcLaptop3: { type: 'string', default: 'Patch Management' }, svcCloud1: { type: 'string', default: 'Email Protection' }, svcCloud2: { type: 'string', default: 'License Management' }, svcCloud3: { type: 'string', default: 'Cloud Backup' }, svcDesktop1: { type: 'string', default: 'Network Monitoring' }, svcDesktop2: { type: 'string', default: 'Threat Detection' }, svcDesktop3: { type: 'string', default: 'Cloud Management' }, svcPhone1: { type: 'string', default: 'Mobile Security' }, svcPhone2: { type: 'string', default: 'Data Encryption' }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; return el(Frag, null, el(IC, null, el(PB, { title: 'Highlight' }, el(TC, { label: 'Word to highlight in title', value: a.highlightWord, onChange: function(v){s({highlightWord:v});} }) ), el(PB, { title: 'Primary Button' }, el(TC, { label: 'URL', value: a.primaryBtnUrl, onChange: function(v){s({primaryBtnUrl:v});} }) ), el(PB, { title: 'Secondary Button', initialOpen: false }, el(TC, { label: 'URL', value: a.secondaryBtnUrl, onChange: function(v){s({secondaryBtnUrl:v});} }) ) ), el('section', { className: 'hero' }, el('div', { className: 'container hero-inner' }, el('div', { className: 'hero-content' }, el(RT, { tagName: 'span', className: 'hero-label', value: a.label, onChange: function(v){s({label:v});}, placeholder: '\u25CF Label text', allowedFormats: [] }), el(RT, { tagName: 'h1', className: 'hero-title', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Hero title...' }), el(RT, { tagName: 'p', className: 'hero-description', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }), el('div', { className: 'btn-group' }, el(RT, { tagName: 'span', className: 'btn btn-primary btn-lg', value: a.primaryBtnText, onChange: function(v){s({primaryBtnText:v});}, placeholder: 'Button', allowedFormats: [] }), el(RT, { tagName: 'span', className: 'btn btn-ghost btn-lg', value: a.secondaryBtnText, onChange: function(v){s({secondaryBtnText:v});}, placeholder: 'Secondary button', allowedFormats: [] }) ), el('div', { className: 'hero-stats' }, el('div', null, el(RT, { tagName: 'div', className: 'hero-stat-value', value: a.stat1Value, onChange: function(v){s({stat1Value:v});}, placeholder: '\u2014', allowedFormats: [] }), el(RT, { tagName: 'div', className: 'hero-stat-label', value: a.stat1Label, onChange: function(v){s({stat1Label:v});}, placeholder: 'Stat label', allowedFormats: [] }) ), el('div', null, el(RT, { tagName: 'div', className: 'hero-stat-value', value: a.stat2Value, onChange: function(v){s({stat2Value:v});}, placeholder: '\u2014', allowedFormats: [] }), el(RT, { tagName: 'div', className: 'hero-stat-label', value: a.stat2Label, onChange: function(v){s({stat2Label:v});}, placeholder: 'Stat label', allowedFormats: [] }) ) ) ), el('div', { className: 'hero-visual' }, el('div', { className: 'hero-devices' }, el('div', { className: 'hero-device hero-device--laptop', style: { opacity: 1 } }, el('div', { className: 'hero-device__frame' }, el('div', { className: 'hero-device__screen' }, el('div', { className: 'hero-device__screen-content' }, el('div', { className: 'hero-device__app-bars' }, el('div'),el('div'),el('div'),el('div')) ) ), el('div', { className: 'hero-device__base' }) ), el('ul', { className: 'hero-device__services' }, el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Data Backup'), el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Endpoint Security'), el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Patch Management') ) ), el('div', { className: 'hero-device hero-device--cloud', style: { opacity: 1 } }, el('div', { className: 'hero-device__frame' }, el('div', { className: 'hero-device__cloud-icon' }, el('span', { className: 'hero-device__cloud-label' }, '365') ) ), el('ul', { className: 'hero-device__services' }, el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Email Protection'), el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' License Management'), el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Cloud Backup') ) ), el('div', { className: 'hero-device hero-device--desktop', style: { opacity: 1 } }, el('div', { className: 'hero-device__frame' }, el('div', { className: 'hero-device__screen' }, el('div', { className: 'hero-device__screen-content' }, el('div', { className: 'hero-device__dash-row' }, el('div',{className:'hero-device__dash-card'}), el('div',{className:'hero-device__dash-card'})), el('div', { className: 'hero-device__dash-bar' }), el('div', { className: 'hero-device__dash-bar hero-device__dash-bar--short' }) ) ), el('div', { className: 'hero-device__stand' }), el('div', { className: 'hero-device__stand-base' }) ), el('ul', { className: 'hero-device__services' }, el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Network Monitoring'), el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Threat Detection'), el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Cloud Management') ) ), el('div', { className: 'hero-device hero-device--phone', style: { opacity: 1 } }, el('div', { className: 'hero-device__frame' }, el('div', { className: 'hero-device__screen' }, el('div', { className: 'hero-device__screen-content' }, el('div', { className: 'hero-device__notif' }, el('span', { className: 'hero-device__notif-icon' }, '\u2713'), el('span', { className: 'hero-device__notif-text' }, 'Secure') ) ) ) ), el('ul', { className: 'hero-device__services' }, el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Mobile Security'), el('li', { className: 'svc', style: { opacity: 1 } }, el('span',{className:'svc__dot'}), ' Data Encryption') ) ) ) ) ) ) ); }, save: function () { return null; } }); /* 2. PAGE HERO ────────────────────────────────────────────────────────── */ reg('oribi/page-hero', { title: 'Oribi Page Hero', icon: 'flag', category: 'oribi', supports: { align: ['full'], html: false }, attributes: { align: { type: 'string', default: 'full' }, label: { type: 'string', default: '' }, title: { type: 'string', default: '' }, description: { type: 'string', default: '' }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; return el(Frag, null, el(IC, null, el(PB, { title: 'Settings' }, el(TC, { label: 'Label (optional)', value: a.label, onChange: function(v){s({label:v});} }) ) ), el('section', { className: 'page-hero' }, el('div', { className: 'container' }, a.label ? el('span', { className: 'hero-label' }, a.label) : null, el(RT, { tagName: 'h1', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Page title...' }), el(RT, { tagName: 'p', className: 'lead', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }) ) ) ); }, save: function () { return null; } }); /* 5. CTA BANNER ───────────────────────────────────────────────────────── */ reg('oribi/cta-banner', { title: 'Oribi CTA Banner', icon: 'megaphone', category: 'oribi', supports: { align: ['full'], html: false }, attributes: { align: { type: 'string', default: 'full' }, heading: { type: 'string', default: '' }, text: { type: 'string', default: '' }, btnText: { type: 'string', default: '' }, btnUrl: { type: 'string', default: '' }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; return el(Frag, null, el(IC, null, el(PB, { title: 'Button' }, el(TC, { label: 'URL', value: a.btnUrl, onChange: function(v){s({btnUrl:v});} }) ) ), el('section', { className: 'cta-banner' }, el('div', { className: 'container text-center' }, el(RT, { tagName: 'h2', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'CTA heading...' }), el(RT, { tagName: 'p', value: a.text, onChange: function(v){s({text:v});}, placeholder: 'CTA text...' }), el(RT, { tagName: 'span', className: 'btn btn-primary btn-lg', style: { background: '#fff', color: 'var(--color-primary)' }, value: a.btnText, onChange: function(v){s({btnText:v});}, placeholder: 'Button text...' }) ) ) ); }, save: function () { return null; } }); /* 6. INTRO SECTION ────────────────────────────────────────────────────── */ reg('oribi/intro-section', { title: 'Oribi Intro Section', icon: 'id', category: 'oribi', supports: { align: ['full'], html: false }, attributes: { align: { type: 'string', default: 'full' }, variant: { type: 'string', default: 'normal' }, label: { type: 'string', default: '' }, heading: { type: 'string', default: '' }, description: { type: 'string', default: '' }, visual: { type: 'string', default: '' }, reversed: { type: 'boolean', default: false }, imgId: { type: 'number', default: 0 }, imgUrl: { type: 'string', default: '' }, imgAlt: { type: 'string', default: '' }, imgWidth: { type: 'number', default: 280 }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; var imgW = a.imgWidth || 280; var visualContent = a.imgUrl ? el('img', { src: a.imgUrl, style: { width: imgW + 'px', maxWidth: '100%', height: 'auto', borderRadius: '8px', objectFit: 'contain', display: 'block' } }) : (a.visual || '\uD83D\uDCBB'); return el(Frag, null, el(IC, null, el(PB, { title: 'Settings' }, el(SC, { label: 'Background', value: a.variant, options: [ { label: 'Normal', value: 'normal' }, { label: 'Alternate', value: 'alt' } ], onChange: function(v){s({variant:v});} }), el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} }), el(TC, { label: 'Visual (emoji or text)', value: a.visual, onChange: function(v){s({visual:v});} }), el(TG, { label: 'Reversed layout', checked: a.reversed, onChange: function(v){s({reversed:v});} }) ), el(PB, { title: 'Visual Image', initialOpen: false }, el(MUC, null, el(MU, { onSelect: function(media){ s({ imgId: media.id, imgUrl: media.url, imgAlt: media.alt || '' }); }, allowedTypes: ['image'], value: a.imgId || 0, render: function(ref) { return el(Frag, null, a.imgUrl ? el('div', { style: { marginBottom: '8px' } }, el('img', { src: a.imgUrl, style: { maxWidth: '100%', height: 'auto', borderRadius: '4px', display: 'block', marginBottom: '6px' } }), el(Btn, { variant: 'link', isDestructive: true, onClick: function(){ s({ imgId: 0, imgUrl: '', imgAlt: '' }); } }, 'Remove image') ) : null, el(Btn, { onClick: ref.open, variant: 'secondary', __next40pxDefaultSize: true }, a.imgUrl ? 'Replace image' : 'Select from Media Library') ); } }) ), a.imgUrl ? el(RC, { label: 'Width (px)', value: imgW, min: 50, max: 420, step: 4, onChange: function(v){ s({ imgWidth: v }); } }) : null ) ), el('section', { className: a.variant === 'alt' ? 'section section-alt' : 'section' }, el('div', { className: 'container' }, el('div', { className: 'about-intro', style: a.reversed ? { direction: 'rtl' } : {} }, el('div', { style: a.reversed ? { direction: 'ltr' } : {} }, a.label ? el('span', { className: 'section-label' }, a.label) : null, el(RT, { tagName: 'h2', style: { marginBottom: '1.5rem' }, value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Heading...' }), el(RT, { tagName: 'p', className: 'lead', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }) ), el('div', { className: 'about-intro-visual' + (a.imgUrl ? ' has-img' : ''), style: a.reversed ? { direction: 'ltr' } : {} }, visualContent) ) ) ) ); }, save: function () { return null; } }); /* 8. CONTACT SECTION ──────────────────────────────────────────────────── */ reg('oribi/contact-section', { title: 'Oribi Contact', icon: 'email', category: 'oribi', supports: { align: ['full'], html: false }, attributes: { align: { type: 'string', default: 'full' }, heading: { type: 'string', default: "Let's Talk" }, lead: { type: 'string', default: '' }, email: { type: 'string', default: 'solutions@oribi-tech.com' }, supportUrl: { type: 'string', default: '' }, portalUrl: { type: 'string', default: '' }, location: { type: 'string', default: 'Saratoga Springs, Upstate New York' }, formHeading: { type: 'string', default: 'Want to Learn More?' }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; return el(Frag, null, el(IC, null, el(PB, { title: 'Contact Settings' }, el(TC, { label: 'Email', value: a.email, onChange: function(v){s({email:v});} }), el(TC, { label: 'Support URL', value: a.supportUrl, onChange: function(v){s({supportUrl:v});} }), el(TC, { label: 'Portal URL', value: a.portalUrl, onChange: function(v){s({portalUrl:v});} }), el(TC, { label: 'Location', value: a.location, onChange: function(v){s({location:v});} }), el(TC, { label: 'Form Heading', value: a.formHeading, onChange: function(v){s({formHeading:v});} }) ) ), el('section', { className: 'section' }, el('div', { className: 'container' }, el('div', { className: 'contact-layout' }, el('div', { className: 'contact-info' }, el(RT, { tagName: 'h2', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Heading...' }), el(RT, { tagName: 'p', className: 'lead', value: a.lead, onChange: function(v){s({lead:v});}, placeholder: 'Lead text...' }), el('div', { className: 'contact-method' }, el('div',{className:'contact-method-icon'},'\uD83D\uDCE7'), el('div',null, el('div',{className:'contact-method-label'},'Email Us'), el('div',{className:'contact-method-value'}, a.email)) ), el('div', { className: 'contact-method' }, el('div',{className:'contact-method-icon'},'\uD83C\uDFAB'), el('div',null, el('div',{className:'contact-method-label'},'Support'), el('div',{className:'contact-method-value'}, 'Open a Support Ticket')) ), el('div', { className: 'contact-method' }, el('div',{className:'contact-method-icon'},'\uD83C\uDF0E'), el('div',null, el('div',{className:'contact-method-label'},'Client Portal'), el('div',{className:'contact-method-value'}, 'portal.oribi-tech.com')) ), el('div', { className: 'contact-method' }, el('div',{className:'contact-method-icon'},'\uD83D\uDCCD'), el('div',null, el('div',{className:'contact-method-label'},'Location'), el('div',{className:'contact-method-value'}, a.location)) ) ), el('div', { className: 'contact-form-wrap' }, el('h3', { style: { marginBottom: '1.5rem' } }, a.formHeading), el('div', { style: { padding: '2rem', background: 'var(--color-bg-alt)', borderRadius: 'var(--radius-md)', textAlign: 'center', color: 'var(--color-text-muted)' } }, 'Contact form renders on the live site' ) ) ) ) ) ); }, save: function () { return null; } }); /* ═══════════════════════════════════════════════════════════════════════ CHILD BLOCKS (each renders one item inside a parent) ═══════════════════════════════════════════════════════════════════════ */ /* ── Feature Card ─────────────────────────────────────────────────────── */ reg('oribi/feature-card', { title: 'Feature Card', icon: 'screenoptions', category: 'oribi', parent: ['oribi/feature-section'], supports: { html: false, reusable: false }, attributes: Object.assign({}, { icon: { type: 'string', default: '' }, iconType: { type: 'string', default: 'emoji' }, faIcon: { type: 'string', default: '' }, title: { type: 'string', default: '' }, description: { type: 'string', default: '' }, url: { type: 'string', default: '' }, centered: { type: 'boolean', default: false } }, CARD_IMAGE_ATTRS), edit: function (props) { var a = props.attributes, s = props.setAttributes; var imgPos = a.imgPosition || 'top'; var imgPrev = cardImagePreview(a); var centeredStyle = a.centered ? { marginInline: 'auto' } : {}; var cardPreview; if ( a.imgUrl && imgPos === 'left' ) { cardPreview = el('div', { className: 'oribi-card img-left' }, imgPrev, el('div', { className: 'oribi-card-body' }, el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Card title...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Card description...' }) ) ); } else if ( a.imgUrl && imgPos === 'background' ) { cardPreview = el('div', { className: 'oribi-card img-bg', style: { backgroundImage: 'url(' + a.imgUrl + ')', backgroundSize: 'cover', backgroundPosition: 'center', color: '#fff' } }, el('div', { className: 'oribi-card-body' }, iconPreview(a, 'feature-icon', centeredStyle), el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Card title...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Card description...' }) ) ); } else { var showIconPrev = !a.imgUrl || imgPos !== 'replace-icon'; cardPreview = el('div', { className: 'oribi-card' + (a.centered ? ' text-center' : '') + (a.imgUrl ? ' img-' + imgPos : '') }, a.imgUrl && (imgPos === 'top' || imgPos === 'replace-icon') ? imgPrev : null, showIconPrev ? iconPreview(a, 'feature-icon', centeredStyle) : null, el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Card title...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Card description...' }) ); } return el(Frag, null, el(IC, null, el(PB, { title: 'Card Settings' }, iconControls(a, s), el(TC, { label: 'URL (optional)', value: a.url || '', onChange: function(v){s({url:v});} }), el(TG, { label: 'Centered', checked: !!a.centered, onChange: function(v){s({centered:v});} }) ), cardImageControls(a, s) ), cardPreview ); }, save: function () { return null; } }); /* ── Value Card ───────────────────────────────────────────────────────── */ reg('oribi/value-card', { title: 'Value Card', icon: 'heart', category: 'oribi', parent: ['oribi/value-section'], supports: { html: false, reusable: false }, attributes: Object.assign({}, { icon: { type: 'string', default: '' }, iconType: { type: 'string', default: 'emoji' }, faIcon: { type: 'string', default: '' }, title: { type: 'string', default: '' }, description: { type: 'string', default: '' } }, CARD_IMAGE_ATTRS), edit: function (props) { var a = props.attributes, s = props.setAttributes; var imgPos = a.imgPosition || 'top'; var imgPrev = cardImagePreview(a); var showIconPrev = !a.imgUrl || imgPos !== 'replace-icon'; var body; if ( a.imgUrl && imgPos === 'left' ) { body = el('div', { className: 'oribi-card value-card img-left' }, imgPrev, el('div', { className: 'oribi-card-body' }, el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }) ) ); } else if ( a.imgUrl && imgPos === 'background' ) { body = el('div', { className: 'oribi-card value-card img-bg', style: { backgroundImage: 'url(' + a.imgUrl + ')', backgroundSize: 'cover', backgroundPosition: 'center', color: '#fff' } }, el('div', { className: 'oribi-card-body' }, showIconPrev ? iconPreview(a, 'value-icon') : null, el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }) ) ); } else { body = el('div', { className: 'oribi-card value-card' + (a.imgUrl ? ' img-' + imgPos : '') }, a.imgUrl && (imgPos === 'top' || imgPos === 'replace-icon') ? imgPrev : null, showIconPrev ? iconPreview(a, 'value-icon') : null, el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }) ); } return el(Frag, null, el(IC, null, el(PB, { title: 'Card Settings' }, iconControls(a, s) ), cardImageControls(a, s) ), body ); }, save: function () { return null; } }); /* ── Addon Card ───────────────────────────────────────────────────────── */ reg('oribi/addon-card', { title: 'Addon Card', icon: 'plus-alt2', category: 'oribi', parent: ['oribi/addon-section'], supports: { html: false, reusable: false }, attributes: Object.assign({}, { icon: { type: 'string', default: '' }, iconType: { type: 'string', default: 'emoji' }, faIcon: { type: 'string', default: '' }, title: { type: 'string', default: '' }, description: { type: 'string', default: '' }, tag: { type: 'string', default: '' } }, CARD_IMAGE_ATTRS), edit: function (props) { var a = props.attributes, s = props.setAttributes; var imgPos = a.imgPosition || 'top'; var imgPrev = cardImagePreview(a); var showIconPrev = !a.imgUrl || imgPos !== 'replace-icon'; var body; if ( a.imgUrl && imgPos === 'left' ) { body = el('div', { className: 'oribi-card img-left' }, imgPrev, el('div', { className: 'oribi-card-body' }, a.tag ? el('span', { className: 'addon-tag' }, a.tag) : null, el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }) ) ); } else if ( a.imgUrl && imgPos === 'background' ) { body = el('div', { className: 'oribi-card img-bg', style: { backgroundImage: 'url(' + a.imgUrl + ')', backgroundSize: 'cover', backgroundPosition: 'center', color: '#fff' } }, el('div', { className: 'oribi-card-body' }, showIconPrev ? iconPreview(a, 'feature-icon') : null, a.tag ? el('span', { className: 'addon-tag' }, a.tag) : null, el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }) ) ); } else { body = el('div', { className: 'oribi-card' + (a.imgUrl ? ' img-' + imgPos : '') }, a.imgUrl && (imgPos === 'top' || imgPos === 'replace-icon') ? imgPrev : null, showIconPrev ? iconPreview(a, 'feature-icon') : null, a.tag ? el('span', { className: 'addon-tag' }, a.tag) : null, el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }) ); } return el(Frag, null, el(IC, null, el(PB, { title: 'Card Settings' }, iconControls(a, s), el(TC, { label: 'Tag / Badge (optional)', value: a.tag || '', onChange: function(v){s({tag:v});} }) ), cardImageControls(a, s) ), body ); }, save: function () { return null; } }); /* ── Image Card ───────────────────────────────────────────────────────── */ reg('oribi/image-card', { title: 'Image Card', icon: 'format-image', category: 'oribi', parent: ['oribi/image-section'], supports: { html: false, reusable: false }, attributes: Object.assign({}, { icon: { type: 'string', default: '' }, iconType: { type: 'string', default: 'emoji' }, faIcon: { type: 'string', default: '' }, title: { type: 'string', default: '' }, description: { type: 'string', default: '' }, url: { type: 'string', default: '' } }, CARD_IMAGE_ATTRS), edit: function (props) { var a = props.attributes, s = props.setAttributes; var imgPos = a.imgPosition || 'top'; var imgPrev = cardImagePreview(a); var showIconPrev = !a.imgUrl || imgPos !== 'replace-icon'; return el(Frag, null, el(IC, null, el(PB, { title: 'Card Settings' }, iconControls(a, s), el(TC, { label: 'URL (optional)', value: a.url || '', onChange: function(v){s({url:v});} }) ), cardImageControls(a, s) ), el('div', { className: 'oribi-card image-card' + (a.imgUrl ? ' img-' + imgPos : '') }, a.imgUrl && (imgPos === 'top' || imgPos === 'replace-icon') ? imgPrev : null, showIconPrev ? iconPreview(a, 'feature-icon') : null, el('div', { className: 'oribi-card-body' }, el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }) ) ) ); }, save: function () { return null; } }); /* ── Stat Card ────────────────────────────────────────────────────────── */ reg('oribi/stat-card', { title: 'Stat Card', icon: 'chart-bar', category: 'oribi', parent: ['oribi/stat-section'], supports: { html: false, reusable: false }, attributes: Object.assign({}, { icon: { type: 'string', default: '' }, iconType: { type: 'string', default: 'emoji' }, faIcon: { type: 'string', default: '' }, value: { type: 'string', default: '' }, label: { type: 'string', default: '' }, description: { type: 'string', default: '' } }, CARD_IMAGE_ATTRS), edit: function (props) { var a = props.attributes, s = props.setAttributes; var imgPrev = cardImagePreview(a); var imgPos = a.imgPosition || 'top'; var body; if ( a.imgUrl && imgPos === 'background' ) { body = el('div', { className: 'oribi-card stat-card img-bg', style: { backgroundImage: 'url(' + a.imgUrl + ')', backgroundSize: 'cover', backgroundPosition: 'center', color: '#fff' } }, el('div', { className: 'oribi-card-body' }, iconPreview(a, 'feature-icon'), el(RT, { tagName: 'div', className: 'stat-value', value: a.value, onChange: function(v){s({value:v});}, placeholder: '99.9%' }), el(RT, { tagName: 'div', className: 'stat-label', value: a.label, onChange: function(v){s({label:v});}, placeholder: 'Label...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description (optional)...' }) ) ); } else { body = el('div', { className: 'oribi-card stat-card' + (a.imgUrl ? ' img-' + imgPos : '') }, (a.imgUrl && imgPos !== 'replace-icon') ? imgPrev : null, iconPreview(a, 'feature-icon'), el(RT, { tagName: 'div', className: 'stat-value', value: a.value, onChange: function(v){s({value:v});}, placeholder: '99.9%' }), el(RT, { tagName: 'div', className: 'stat-label', value: a.label, onChange: function(v){s({label:v});}, placeholder: 'Label...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description (optional)...' }) ); } return el(Frag, null, el(IC, null, el(PB, { title: 'Card Settings' }, iconControls(a, s) ), cardImageControls(a, s) ), body ); }, save: function () { return null; } }); /* ── Link Card ────────────────────────────────────────────────────────── */ reg('oribi/link-card', { title: 'Link Card', icon: 'admin-links', category: 'oribi', parent: ['oribi/link-section'], supports: { html: false, reusable: false }, attributes: Object.assign({}, { icon: { type: 'string', default: '' }, iconType: { type: 'string', default: 'emoji' }, faIcon: { type: 'string', default: '' }, title: { type: 'string', default: '' }, description: { type: 'string', default: '' }, linkText: { type: 'string', default: '' }, linkUrl: { type: 'string', default: '' } }, CARD_IMAGE_ATTRS), edit: function (props) { var a = props.attributes, s = props.setAttributes; var imgPos = a.imgPosition || 'top'; var imgPrev = cardImagePreview(a); var showIconPrev = !a.imgUrl || imgPos !== 'replace-icon'; var body; if ( a.imgUrl && imgPos === 'left' ) { body = el('div', { className: 'oribi-card link-card img-left' }, imgPrev, el('div', { className: 'oribi-card-body' }, showIconPrev ? iconPreview(a, 'feature-icon') : null, el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }), el(RT, { tagName: 'span', className: 'link-card-cta', value: a.linkText, onChange: function(v){s({linkText:v});}, placeholder: 'Link text → ...' }) ) ); } else if ( a.imgUrl && imgPos === 'background' ) { body = el('div', { className: 'oribi-card link-card img-bg', style: { backgroundImage: 'url(' + a.imgUrl + ')', backgroundSize: 'cover', backgroundPosition: 'center', color: '#fff' } }, el('div', { className: 'oribi-card-body' }, showIconPrev ? iconPreview(a, 'feature-icon') : null, el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }), el(RT, { tagName: 'span', className: 'link-card-cta', value: a.linkText, onChange: function(v){s({linkText:v});}, placeholder: 'Link text → ...' }) ) ); } else { body = el('div', { className: 'oribi-card link-card' + (a.imgUrl ? ' img-' + imgPos : '') }, (a.imgUrl && (imgPos === 'top' || imgPos === 'replace-icon')) ? imgPrev : null, showIconPrev ? iconPreview(a, 'feature-icon') : null, el(RT, { tagName: 'h3', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Title...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }), el(RT, { tagName: 'span', className: 'link-card-cta', value: a.linkText, onChange: function(v){s({linkText:v});}, placeholder: 'Link text → ...' }) ); } return el(Frag, null, el(IC, null, el(PB, { title: 'Card Settings' }, iconControls(a, s), el(TC, { label: 'Link URL', value: a.linkUrl || '', onChange: function(v){s({linkUrl:v});} }) ), cardImageControls(a, s) ), body ); }, save: function () { return null; } }); /* ── Pricing Card ─────────────────────────────────────────────────────── */ reg('oribi/pricing-card', { title: 'Pricing Card', icon: 'money-alt', category: 'oribi', parent: ['oribi/pricing-section'], supports: { html: false, reusable: false }, attributes: { icon: { type: 'string', default: '' }, iconType: { type: 'string', default: 'emoji' }, faIcon: { type: 'string', default: '' }, name: { type: 'string', default: '' }, tagline: { type: 'string', default: '' }, price: { type: 'string', default: '' }, pricePer: { type: 'string', default: '' }, features: { type: 'array', default: [] }, btnText: { type: 'string', default: 'Get Started' }, btnUrl: { type: 'string', default: '/contact' }, featured: { type: 'boolean', default: false }, badge: { type: 'string', default: '' }, imgId: { type: 'number', default: 0 }, imgUrl: { type: 'string', default: '' }, imgAlt: { type: 'string', default: '' }, imgWidth: { type: 'number', default: 80 }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; var features = a.features || []; var imgW = a.imgWidth || 80; return el(Frag, null, el(IC, null, el(PB, { title: 'Card Settings' }, iconControls(a, s), el(TC, { label: 'Button URL', value: a.btnUrl, onChange: function(v){s({btnUrl:v});} }), el(TG, { label: 'Featured', checked: !!a.featured, onChange: function(v){s({featured:v});} }), a.featured ? el(TC, { label: 'Badge Text', value: a.badge, onChange: function(v){s({badge:v});} }) : null ), el(PB, { title: 'Card Image', initialOpen: false }, el(MUC, null, el(MU, { onSelect: function(media){ s({ imgId: media.id, imgUrl: media.url, imgAlt: media.alt || '' }); }, allowedTypes: ['image'], value: a.imgId || 0, render: function(ref) { return el(Frag, null, a.imgUrl ? el('div', { style: { marginBottom: '8px' } }, el('img', { src: a.imgUrl, style: { maxWidth: '100%', height: 'auto', borderRadius: '4px', display: 'block', marginBottom: '6px' } }), el(Btn, { variant: 'link', isDestructive: true, onClick: function(){ s({ imgId: 0, imgUrl: '', imgAlt: '' }); } }, 'Remove image') ) : null, el(Btn, { onClick: ref.open, variant: 'secondary', __next40pxDefaultSize: true }, a.imgUrl ? 'Replace image' : 'Select from Media Library') ); } }) ), a.imgUrl ? el(RC, { label: 'Width (px)', value: imgW, min: 20, max: 400, step: 4, onChange: function(v){ s({ imgWidth: v }); } }) : null ) ), el('div', { className: 'pricing-card' + (a.featured ? ' featured' : '') }, a.featured && a.badge ? el(RT, { tagName: 'span', className: 'pricing-badge', value: a.badge, onChange: function(v){s({badge:v});}, placeholder: 'Badge...' }) : null, a.imgUrl ? el('div', { style: { textAlign: 'center', marginBottom: '1.25rem' } }, el('img', { src: a.imgUrl, style: { width: imgW + 'px', maxWidth: '100%', height: 'auto', borderRadius: '4px', objectFit: 'contain' } }) ) : null, iconPreview(a, 'feature-icon', { marginInline: 'auto' }), el(RT, { tagName: 'div', className: 'pricing-name', value: a.name, onChange: function(v){s({name:v});}, placeholder: 'Plan name...' }), el(RT, { tagName: 'p', className: 'pricing-tagline', value: a.tagline, onChange: function(v){s({tagline:v});}, placeholder: 'Tagline...' }), a.price || a.pricePer ? el('div', { className: 'pricing-price' }, el(RT, { tagName: 'div', className: 'pricing-amount', value: a.price || '', onChange: function(v){s({price:v});}, placeholder: '$0' }), el(RT, { tagName: 'div', className: 'pricing-per', value: a.pricePer || '', onChange: function(v){s({pricePer:v});}, placeholder: 'per screen / month' }) ) : el('div', { className: 'pricing-price' }, el(RT, { tagName: 'div', className: 'pricing-amount', value: '', onChange: function(v){s({price:v});}, placeholder: '$0' }), el(RT, { tagName: 'div', className: 'pricing-per', value: '', onChange: function(v){s({pricePer:v});}, placeholder: 'per screen / month' }) ), el('ul', { className: 'pricing-features' }, features.map(function (f, fi) { return el('li', { key: fi, style: { display: 'flex', alignItems: 'center', gap: '4px' } }, el('span', { className: 'pricing-check' }, '\u2713'), el(RT, { tagName: 'span', style: { flex: 1, minWidth: 0 }, value: f, onChange: function(v){ s({features: arrSet(features, fi, v)}); }, placeholder: 'Feature...' }), el(Btn, { isSmall: true, isDestructive: true, style: { minWidth: '20px', padding: 0, height: '20px', flexShrink: 0 }, onClick: function(){ s({features: arrRm(features, fi)}); } }, '\u2715') ); }), el('li', { style: { listStyle: 'none', marginTop: '4px' } }, el(Btn, { isSmall: true, variant: 'secondary', onClick: function(){ s({features: arrAdd(features, '')}); } }, '+ Feature') ) ), el(RT, { tagName: 'span', className: 'btn ' + (a.featured ? 'btn-primary' : 'btn-outline'), style: { width: '100%', justifyContent: 'center', cursor: 'text' }, value: a.btnText || '', onChange: function(v){s({btnText:v});}, placeholder: 'Button text...' }) ) ); }, save: function () { return null; } }); /* ── Platform Row ─────────────────────────────────────────────────────── */ reg('oribi/platform-row', { title: 'Platform Row', icon: 'slides', category: 'oribi', parent: ['oribi/platform-section'], supports: { html: false, reusable: false }, attributes: { heading: { type: 'string', default: '' }, description: { type: 'string', default: '' }, btnText: { type: 'string', default: 'Learn More' }, btnUrl: { type: 'string', default: '' }, visual: { type: 'string', default: '' }, reversed: { type: 'boolean', default: false }, imgId: { type: 'number', default: 0 }, imgUrl: { type: 'string', default: '' }, imgAlt: { type: 'string', default: '' }, imgWidth: { type: 'number', default: 300 }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; var imgW = a.imgWidth || 300; return el(Frag, null, el(IC, null, el(PB, { title: 'Row Settings' }, el(TC, { label: 'Visual (emoji)', value: a.visual, onChange: function(v){s({visual:v});} }), el(TC, { label: 'Button URL', value: a.btnUrl, onChange: function(v){s({btnUrl:v});} }), el(TG, { label: 'Reversed', checked: !!a.reversed, onChange: function(v){s({reversed:v});} }) ), el(PB, { title: 'Visual Image', initialOpen: false }, el(MUC, null, el(MU, { onSelect: function(media){ s({ imgId: media.id, imgUrl: media.url, imgAlt: media.alt || '' }); }, allowedTypes: ['image'], value: a.imgId || 0, render: function(ref) { return el(Frag, null, a.imgUrl ? el('div', { style: { marginBottom: '8px' } }, el('img', { src: a.imgUrl, style: { maxWidth: '100%', height: 'auto', borderRadius: '4px', display: 'block', marginBottom: '6px' } }), el(Btn, { variant: 'link', isDestructive: true, onClick: function(){ s({ imgId: 0, imgUrl: '', imgAlt: '' }); } }, 'Remove image') ) : null, el(Btn, { onClick: ref.open, variant: 'secondary', __next40pxDefaultSize: true }, a.imgUrl ? 'Replace image' : 'Select from Media Library') ); } }) ), a.imgUrl ? el(RC, { label: 'Width (px)', value: imgW, min: 50, max: 600, step: 4, onChange: function(v){ s({ imgWidth: v }); } }) : null ) ), el('div', { className: 'platform-row' + (a.reversed ? ' reverse' : '') }, el('div', { className: 'platform-text' }, el(RT, { tagName: 'h3', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Service name...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Service description...' }), a.btnUrl ? el(RT, { tagName: 'span', className: 'btn btn-outline mt-3', value: a.btnText, onChange: function(v){s({btnText:v});}, placeholder: 'Button...' }) : null ), a.imgUrl ? el('div', { className: 'platform-visual has-img' }, el('img', { src: a.imgUrl, style: { width: imgW + 'px', maxWidth: '100%', height: 'auto', borderRadius: '4px', objectFit: 'contain', display: 'block', marginInline: 'auto' } }) ) : el('div', { className: 'platform-visual' }, a.visual || '\uD83D\uDCBB') ) ); }, save: function () { return null; } }); /* ── Trust Item ───────────────────────────────────────────────────────── */ reg('oribi/trust-item', { title: 'Trust Item', icon: 'shield', category: 'oribi', parent: ['oribi/trust-section'], supports: { html: false, reusable: false }, attributes: { heading: { type: 'string', default: '' }, description: { type: 'string', default: '' }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; return el('div', { className: 'trust-item' }, el(RT, { tagName: 'h3', style: { marginBottom: '1rem' }, value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Sub-heading...' }), el(RT, { tagName: 'p', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }) ); }, save: function () { return null; } }); /* ═══════════════════════════════════════════════════════════════════════ PARENT BLOCKS (use InnerBlocks for child items) ═══════════════════════════════════════════════════════════════════════ */ /* 3. FEATURE SECTION ──────────────────────────────────────────────────── */ reg('oribi/feature-section', { title: 'Oribi Feature Section', icon: 'grid-view', category: 'oribi', supports: { align: ['full'], html: false }, attributes: SECTION_ATTRS, edit: createCardSectionEdit(['oribi/feature-card'], [['oribi/feature-card', {}]], 'Feature Card'), save: function () { return el(IB.Content); } }); /* VALUE SECTION ────────────────────────────────────────────────────────── */ reg('oribi/value-section', { title: 'Oribi Value Section', icon: 'heart', category: 'oribi', supports: { align: ['full'], html: false }, attributes: SECTION_ATTRS, edit: createCardSectionEdit(['oribi/value-card'], [['oribi/value-card', {}]], 'Value Card'), save: function () { return el(IB.Content); } }); /* ADDON SECTION ────────────────────────────────────────────────────────── */ reg('oribi/addon-section', { title: 'Oribi Addon Section', icon: 'plus-alt2', category: 'oribi', supports: { align: ['full'], html: false }, attributes: SECTION_ATTRS, edit: createCardSectionEdit(['oribi/addon-card'], [['oribi/addon-card', {}]], 'Addon Card'), save: function () { return el(IB.Content); } }); /* IMAGE SECTION ────────────────────────────────────────────────────────── */ reg('oribi/image-section', { title: 'Oribi Image Section', icon: 'format-image', category: 'oribi', supports: { align: ['full'], html: false }, attributes: SECTION_ATTRS, edit: createCardSectionEdit(['oribi/image-card'], [['oribi/image-card', {}]], 'Image Card'), save: function () { return el(IB.Content); } }); /* STAT SECTION ─────────────────────────────────────────────────────────── */ reg('oribi/stat-section', { title: 'Oribi Stat Section', icon: 'chart-bar', category: 'oribi', supports: { align: ['full'], html: false }, attributes: SECTION_ATTRS, edit: createCardSectionEdit(['oribi/stat-card'], [['oribi/stat-card', {}]], 'Stat Card'), save: function () { return el(IB.Content); } }); /* LINK SECTION ─────────────────────────────────────────────────────────── */ reg('oribi/link-section', { title: 'Oribi Link Section', icon: 'admin-links', category: 'oribi', supports: { align: ['full'], html: false }, attributes: SECTION_ATTRS, edit: createCardSectionEdit(['oribi/link-card'], [['oribi/link-card', {}]], 'Link Card'), save: function () { return el(IB.Content); } }); /* 4. PRICING SECTION ──────────────────────────────────────────────────── */ reg('oribi/pricing-section', { title: 'Oribi Pricing', icon: 'money-alt', category: 'oribi', supports: { align: ['full'], html: false }, attributes: { align: { type: 'string', default: 'full' }, variant: { type: 'string', default: 'normal' }, label: { type: 'string', default: '' }, heading: { type: 'string', default: '' }, lead: { type: 'string', default: '' }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; return el(Frag, null, el(IC, null, el(PB, { title: 'Section Settings' }, el(SC, { label: 'Background', value: a.variant, options: [ { label: 'Normal', value: 'normal' }, { label: 'Alternate', value: 'alt' } ], onChange: function(v){s({variant:v});} }), el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} }) ) ), el('section', { className: a.variant === 'alt' ? 'section section-alt' : 'section' }, el('div', { className: 'container' }, el('div', { className: 'section-header' }, a.label ? el('span', { className: 'section-label' }, a.label) : null, el(RT, { tagName: 'h2', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Pricing heading...' }), el(RT, { tagName: 'p', className: 'lead', value: a.lead, onChange: function(v){s({lead:v});}, placeholder: 'Lead text...' }) ), el('div', { className: 'pricing-grid' }, el(IB, { allowedBlocks: ['oribi/pricing-card'], template: [ ['oribi/pricing-card', { name: 'Essentials' }], ['oribi/pricing-card', { name: 'Pro', featured: true, badge: 'Most Popular' }], ['oribi/pricing-card', { name: 'Enterprise' }] ], templateLock: false }) ) ) ) ); }, save: function () { return el(IB.Content); } }); /* 7. PLATFORM SECTION ─────────────────────────────────────────────────── */ reg('oribi/platform-section', { title: 'Oribi Platform Section', icon: 'slides', category: 'oribi', supports: { align: ['full'], html: false }, attributes: { align: { type: 'string', default: 'full' }, label: { type: 'string', default: '' }, heading: { type: 'string', default: '' }, lead: { type: 'string', default: '' }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; return el(Frag, null, el(IC, null, el(PB, { title: 'Section' }, el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} }) ) ), el('section', { className: 'section' }, el('div', { className: 'container' }, el('div', { className: 'section-header' }, a.label ? el('span', { className: 'section-label' }, a.label) : null, el(RT, { tagName: 'h2', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Section heading...' }), el(RT, { tagName: 'p', className: 'lead', value: a.lead, onChange: function(v){s({lead:v});}, placeholder: 'Lead text...' }) ), el(IB, { allowedBlocks: ['oribi/platform-row'], template: [['oribi/platform-row', {}]], templateLock: false }) ) ) ); }, save: function () { return el(IB.Content); } }); /* 9. TRUST SECTION ────────────────────────────────────────────────────── */ reg('oribi/trust-section', { title: 'Oribi Trust Section', icon: 'shield', category: 'oribi', supports: { align: ['full'], html: false }, attributes: { align: { type: 'string', default: 'full' }, label: { type: 'string', default: '' }, heading: { type: 'string', default: '' }, lead: { type: 'string', default: '' }, btnText: { type: 'string', default: '' }, btnUrl: { type: 'string', default: '' }, btnSub: { type: 'string', default: '' }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; return el(Frag, null, el(IC, null, el(PB, { title: 'Settings' }, el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} }), el(TC, { label: 'Button Text', value: a.btnText, onChange: function(v){s({btnText:v});} }), el(TC, { label: 'Button URL', value: a.btnUrl, onChange: function(v){s({btnUrl:v});} }), el(TC, { label: 'Button Subtext', value: a.btnSub, onChange: function(v){s({btnSub:v});} }) ) ), el('section', { className: 'section' }, el('div', { className: 'container' }, el('div', { className: 'section-header' }, a.label ? el('span', { className: 'section-label' }, a.label) : null, el(RT, { tagName: 'h2', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Heading...' }), el(RT, { tagName: 'p', className: 'lead', value: a.lead, onChange: function(v){s({lead:v});}, placeholder: 'Lead text...' }) ), el('div', { className: 'grid-2', style: { alignItems: 'center' } }, el('div', { style: { display: 'flex', flexDirection: 'column', gap: '1.5rem' } }, el(IB, { allowedBlocks: ['oribi/trust-item'], template: [['oribi/trust-item', {}]], templateLock: false }) ), el('div', { style: { textAlign: 'center' } }, el('span', { className: 'btn btn-primary btn-lg' }, a.btnText || 'Button'), a.btnSub ? el('p', { className: 'lead mt-2', style: { fontSize: '.9rem' } }, a.btnSub) : null ) ) ) ) ); }, save: function () { return el(IB.Content); } }); /* 10. FAQ SECTION ─────────────────────────────────────────────────────── */ reg('oribi/faq-section', { title: 'Oribi FAQ Section', icon: 'editor-help', category: 'oribi', supports: { align: ['full'], html: false }, attributes: { align: { type: 'string', default: 'full' }, variant: { type: 'string', default: 'normal' }, label: { type: 'string', default: '' }, heading: { type: 'string', default: '' }, lead: { type: 'string', default: '' }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; return el(Frag, null, el(IC, null, el(PB, { title: 'Section Settings' }, el(SC, { label: 'Background', value: a.variant, options: [ { label: 'Normal', value: 'normal' }, { label: 'Alternate', value: 'alt' } ], onChange: function(v){s({variant:v});} }), el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} }) ) ), el('section', { className: a.variant === 'alt' ? 'section section-alt' : 'section' }, el('div', { className: 'container' }, el('div', { className: 'section-header' }, a.label ? el('span', { className: 'section-label' }, a.label) : null, el(RT, { tagName: 'h2', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'FAQ heading...' }), el(RT, { tagName: 'p', className: 'lead', value: a.lead, onChange: function(v){s({lead:v});}, placeholder: 'Lead text...' }) ), el('div', { className: 'faq-list' }, el(IB, { allowedBlocks: ['oribi/faq-item'], template: [['oribi/faq-item', {}]], templateLock: false }) ) ) ) ); }, save: function () { return el(IB.Content); } }); /* FAQ ITEM (child) ─────────────────────────────────────────────────────── */ reg('oribi/faq-item', { title: 'Oribi FAQ Item', icon: 'editor-help', category: 'oribi', parent: ['oribi/faq-section'], supports: { html: false, reusable: false }, attributes: { question: { type: 'string', default: '' }, answer: { type: 'string', default: '' }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; return el('details', { className: 'faq-item', open: true }, el('summary', { className: 'faq-question' }, el(RT, { tagName: 'span', value: a.question, onChange: function(v){s({question:v});}, placeholder: 'Question...' }) ), el('div', { className: 'faq-answer' }, el(RT, { tagName: 'p', value: a.answer, onChange: function(v){s({answer:v});}, placeholder: 'Answer...' }) ) ); }, save: function () { return null; } }); /* 11. COMPARISON TABLE (standalone) ───────────────────────────────────── */ reg('oribi/comparison-table', { title: 'Oribi Comparison Table', icon: 'editor-table', category: 'oribi', supports: { align: ['full'], html: false }, attributes: { align: { type: 'string', default: 'full' }, variant: { type: 'string', default: 'normal' }, label: { type: 'string', default: '' }, heading: { type: 'string', default: '' }, lead: { type: 'string', default: '' }, columns: { type: 'array', default: [] }, rows: { type: 'array', default: [] }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; var cols = a.columns || []; var rows = a.rows || []; return el(Frag, null, el(IC, null, el(PB, { title: 'Table Settings' }, el(SC, { label: 'Background', value: a.variant, options: [ { label: 'Normal', value: 'normal' }, { label: 'Alternate', value: 'alt' } ], onChange: function(v){s({variant:v});} }), el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} }) ) ), el('section', { className: a.variant === 'alt' ? 'section section-alt' : 'section' }, el('div', { className: 'container' }, el('div', { className: 'section-header' }, a.label ? el('span', { className: 'section-label' }, a.label) : null, el(RT, { tagName: 'h2', value: a.heading, onChange: function(v){s({heading:v});}, placeholder: 'Table heading...' }), el(RT, { tagName: 'p', className: 'lead', value: a.lead, onChange: function(v){s({lead:v});}, placeholder: 'Lead text...' }) ), el('div', { className: 'comparison-table-wrap' }, el('table', { className: 'comparison-table' }, el('thead', null, el('tr', null, el('th', { className: 'comparison-feature-col' }, 'Feature'), cols.map(function(col, i) { return el('th', { key: i }, col); }) ) ), el('tbody', null, rows.map(function(row, i) { if (row.group) { return el('tr', { key: i, className: 'comparison-group-row' }, el('td', { colSpan: cols.length + 1 }, row.group) ); } return el('tr', { key: i }, el('td', { className: 'comparison-feature-name' }, row.feature || ''), (row.values || []).map(function(val, j) { return el('td', { key: j, className: 'comparison-cell' }, val === true ? '\u2713' : val === false ? '\u2014' : String(val) ); }) ); }) ) ) ) ) ) ); }, save: function () { return null; } }); /* ═══════════════════════════════════════════════════════════════════════ TEMPLATE-PART HELPER BLOCKS ═══════════════════════════════════════════════════════════════════════ */ /* ═══════════════════════════════════════════════════════════════════════ ANIMATED HERO BLOCKS (OTS Signs) ═══════════════════════════════════════════════════════════════════════ */ /* ANIMATED HERO ───────────────────────────────────────────────────────── */ reg('oribi/hero-animated', { title: 'Animated Hero', icon: 'admin-site-alt3', category: 'oribi', supports: { align: ['full'], html: false }, attributes: { align: { type: 'string', default: 'full' }, label: { type: 'string', default: '' }, title: { type: 'string', default: '' }, highlightWord: { type: 'string', default: '' }, description: { type: 'string', default: '' }, primaryBtnText: { type: 'string', default: 'Get Started' }, primaryBtnUrl: { type: 'string', default: '/contact' }, secondaryBtnText: { type: 'string', default: '' }, secondaryBtnUrl: { type: 'string', default: '' }, stat1Value: { type: 'string', default: '' }, stat1Label: { type: 'string', default: '' }, stat2Value: { type: 'string', default: '' }, stat2Label: { type: 'string', default: '' }, stat3Value: { type: 'string', default: '' }, stat3Label: { type: 'string', default: '' }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; // Build particle elements for editor preview var particles = []; for (var i = 1; i <= 12; i++) { particles.push(el('div', { key: 'p' + i, className: 'hero-particle hero-particle--' + i })); } return el(Frag, null, el(IC, null, el(PB, { title: 'Highlight' }, el(TC, { label: 'Word to highlight in title', value: a.highlightWord, onChange: function(v){s({highlightWord:v});} }) ), el(PB, { title: 'Primary Button' }, el(TC, { label: 'URL', value: a.primaryBtnUrl, onChange: function(v){s({primaryBtnUrl:v});} }) ), el(PB, { title: 'Secondary Button', initialOpen: false }, el(TC, { label: 'URL', value: a.secondaryBtnUrl, onChange: function(v){s({secondaryBtnUrl:v});} }) ) ), el('section', { className: 'hero hero-animated' }, el('div', { className: 'hero-particles', 'aria-hidden': 'true' }, particles), el('div', { className: 'hero-animated__glow' }), el('div', { className: 'container hero-animated__inner' }, el('div', { className: 'hero-animated__content' }, el(RT, { tagName: 'span', className: 'hero-label', value: a.label, onChange: function(v){s({label:v});}, placeholder: '\u25CF Label text', allowedFormats: [] }), el(RT, { tagName: 'h1', className: 'hero-title', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Hero title...' }), el(RT, { tagName: 'p', className: 'hero-description', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }), el('div', { className: 'btn-group' }, el(RT, { tagName: 'span', className: 'btn btn-primary btn-lg', value: a.primaryBtnText, onChange: function(v){s({primaryBtnText:v});}, placeholder: 'Button', allowedFormats: [] }), el(RT, { tagName: 'span', className: 'btn btn-ghost btn-lg', value: a.secondaryBtnText, onChange: function(v){s({secondaryBtnText:v});}, placeholder: 'Secondary button', allowedFormats: [] }) ), el('div', { className: 'hero-stats hero-stats--three' }, el('div', null, el(RT, { tagName: 'div', className: 'hero-stat-value', value: a.stat1Value, onChange: function(v){s({stat1Value:v});}, placeholder: '\u2014', allowedFormats: [] }), el(RT, { tagName: 'div', className: 'hero-stat-label', value: a.stat1Label, onChange: function(v){s({stat1Label:v});}, placeholder: 'Stat label', allowedFormats: [] }) ), el('div', null, el(RT, { tagName: 'div', className: 'hero-stat-value', value: a.stat2Value, onChange: function(v){s({stat2Value:v});}, placeholder: '\u2014', allowedFormats: [] }), el(RT, { tagName: 'div', className: 'hero-stat-label', value: a.stat2Label, onChange: function(v){s({stat2Label:v});}, placeholder: 'Stat label', allowedFormats: [] }) ), el('div', null, el(RT, { tagName: 'div', className: 'hero-stat-value', value: a.stat3Value, onChange: function(v){s({stat3Value:v});}, placeholder: '\u2014', allowedFormats: [] }), el(RT, { tagName: 'div', className: 'hero-stat-label', value: a.stat3Label, onChange: function(v){s({stat3Label:v});}, placeholder: 'Stat label', allowedFormats: [] }) ) ) ) ) ) ); }, save: function () { return null; } }); /* ANIMATED PAGE HERO ──────────────────────────────────────────────────── */ reg('oribi/page-hero-animated', { title: 'Animated Page Hero', icon: 'flag', category: 'oribi', supports: { align: ['full'], html: false }, attributes: { align: { type: 'string', default: 'full' }, label: { type: 'string', default: '' }, title: { type: 'string', default: '' }, description: { type: 'string', default: '' }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; var particles = []; for (var i = 1; i <= 8; i++) { particles.push(el('div', { key: 'p' + i, className: 'hero-particle hero-particle--' + i })); } return el(Frag, null, el(IC, null, el(PB, { title: 'Settings' }, el(TC, { label: 'Label (optional)', value: a.label, onChange: function(v){s({label:v});} }) ) ), el('section', { className: 'page-hero page-hero-animated' }, el('div', { className: 'hero-particles', 'aria-hidden': 'true' }, particles), el('div', { className: 'hero-animated__glow' }), el('div', { className: 'hero-overlay' }), el('div', { className: 'container' }, a.label ? el('span', { className: 'hero-label' }, a.label) : null, el(RT, { tagName: 'h1', value: a.title, onChange: function(v){s({title:v});}, placeholder: 'Page title...' }), el(RT, { tagName: 'p', className: 'lead', value: a.description, onChange: function(v){s({description:v});}, placeholder: 'Description...' }) ) ) ); }, save: function () { return null; } }); /* ═══════════════════════════════════════════════════════════════════════ TEMPLATE-PART HELPER BLOCKS ═══════════════════════════════════════════════════════════════════════ */ reg('oribi/site-header', { title: 'Oribi Site Header', icon: 'admin-home', category: 'oribi', supports: { html: false, multiple: false, reusable: false }, edit: function () { return el('div', { style: { background: '#111111', color: '#fff', padding: '20px 24px', borderRadius: '8px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' } }, el('div', { style: { display: 'flex', alignItems: 'center', gap: '8px' } }, el('strong', { style: { fontSize: '1.2rem' } }, 'Oribi'), el('span', { style: { fontSize: '1.2rem', fontWeight: 300 } }, 'Tech') ), el('div', { style: { display: 'flex', gap: '1.5rem', fontSize: '.9rem', opacity: 0.7 } }, el('span', null, 'Services'), el('span', null, 'About'), el('span', null, 'FAQ'), el('span', null, 'Contact') ) ); }, save: function () { return null; } }); reg('oribi/site-footer', { title: 'Oribi Site Footer', icon: 'admin-home', category: 'oribi', supports: { html: false, multiple: false, reusable: false }, edit: function () { return el('div', { style: { background: '#111111', color: '#fff', padding: '24px', borderRadius: '8px', textAlign: 'center' } }, el('strong', { style: { fontSize: '1.1rem' } }, 'OTS Theme - Site Footer'), el('p', { style: { opacity: 0.5, margin: '8px 0 0', fontSize: '.85rem' } }, 'Brand · Service Links · Company Links · Connect · Copyright') ); }, save: function () { return null; } }); })(window.wp);