Files
OTSSigns-Website/theme/blocks/index.php

3423 lines
180 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* OTS Theme - Custom Gutenberg Blocks (InnerBlocks Architecture)
*
* Blocks:
* Standalone: hero, page-hero, cta-banner, intro-section, contact-section
* Parent/child pairs:
* feature-section → feature-card
* value-section → value-card
* addon-section → addon-card
* image-section → image-card
* stat-section → stat-card
* link-section → link-card
* pricing-section → pricing-card
* platform-section → platform-row
* trust-section → trust-item
* faq-section → faq-item
*
* Parent blocks use InnerBlocks (save → InnerBlocks.Content, PHP wraps $content).
* Child blocks are dynamic (save → null, own render_callback).
*/
if (!defined('ABSPATH'))
exit;
/* ══════════════════════════════════════════════════════════════════════════════
SHARED HELPERS
══════════════════════════════════════════════════════════════════════════════ */
/**
* Shared image attributes for all card blocks.
* Spread into each card's 'attributes' array.
*/
function oribi_card_image_attributes()
{
return [
'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'],
];
}
/**
* Build card image HTML from shared image attributes.
*
* @param array $a Block attributes (must include imgId, imgUrl, etc.)
* @return array [ 'html' => string, 'position' => string, 'card_class' => string ]
*/
function oribi_card_image_html($a)
{
$img_id = !empty($a['imgId']) ? intval($a['imgId']) : 0;
$img_url = !empty($a['imgUrl']) ? $a['imgUrl'] : '';
$img_alt = !empty($a['imgAlt']) ? $a['imgAlt'] : '';
$img_w = !empty($a['imgWidth']) ? intval($a['imgWidth']) : 80;
$img_h = !empty($a['imgHeight']) ? intval($a['imgHeight']) : 0;
$img_fit = !empty($a['imgFit']) ? $a['imgFit'] : 'contain';
$img_pos = !empty($a['imgPosition']) ? $a['imgPosition'] : 'top';
$result = [
'html' => '',
'position' => $img_pos,
'card_class' => '',
];
if (!$img_url) {
return $result;
}
$result['card_class'] = 'img-' . $img_pos;
// Build inline style for dimensions
$styles = [];
if ($img_pos !== 'background') {
if ($img_w > 0)
$styles[] = 'width:' . $img_w . 'px';
$styles[] = 'max-width:100%';
if ($img_h > 0) {
$styles[] = 'height:' . $img_h . 'px';
}
else {
$styles[] = 'height:auto';
}
}
$style_str = implode(';', $styles);
$fit_class = 'oribi-card-img oribi-card-img--' . ($img_pos === 'background' ? 'cover' : esc_attr($img_fit));
if ($img_id) {
$result['html'] = wp_get_attachment_image($img_id, 'large', false, [
'class' => $fit_class,
'style' => $style_str,
'alt' => $img_alt,
]);
}
else {
$result['html'] = '<img class="' . esc_attr($fit_class) . '" src="' . esc_url($img_url)
. '" alt="' . esc_attr($img_alt)
. '" style="' . esc_attr($style_str) . '">';
}
// Wrap in container
$result['html'] = '<div class="oribi-card-img-wrap">' . $result['html'] . '</div>';
return $result;
}
/**
* Shared icon attributes added to every card block that supports an icon.
*/
function oribi_card_icon_attributes()
{
return [
'icon' => ['type' => 'string', 'default' => ''],
'iconType' => ['type' => 'string', 'default' => 'emoji'],
'faIcon' => ['type' => 'string', 'default' => ''],
];
}
/**
* Render an icon from block attributes.
*
* Accepts either:
* - iconType = 'fontawesome' + faIcon = 'fas fa-cloud' → <i class="fas fa-cloud"></i>
* - iconType = 'emoji' + icon = 'text' -> text (escaped)
*
* Legacy: if $icon_or_attrs is a plain string it behaves like before.
*
* @param array|string $icon_or_attrs Block attributes array, or legacy icon string.
* @return string Rendered HTML.
*/
function oribi_render_icon($icon_or_attrs)
{
// Legacy string call
if (is_string($icon_or_attrs)) {
$icon = $icon_or_attrs;
if (empty($icon))
return '';
if (preg_match('/^fa[srldb]\s+fa-/', $icon)) {
return '<i class="' . esc_attr($icon) . '"></i>';
}
return wp_kses_post($icon);
}
// Array (block attributes)
$a = $icon_or_attrs;
$iconType = !empty($a['iconType']) ? $a['iconType'] : 'emoji';
if ($iconType === 'fontawesome') {
$fa = !empty($a['faIcon']) ? trim($a['faIcon']) : '';
if (empty($fa))
return '';
return '<i class="' . esc_attr($fa) . '" aria-hidden="true"></i>';
}
// emoji / text
$icon = !empty($a['icon']) ? $a['icon'] : '';
if (empty($icon))
return '';
return wp_kses_post($icon);
}
/**
* Return true when the given block attributes specify a non-empty icon.
*/
function oribi_has_icon($a)
{
$iconType = !empty($a['iconType']) ? $a['iconType'] : 'emoji';
if ($iconType === 'fontawesome') {
return !empty($a['faIcon']);
}
return !empty($a['icon']);
}
/**
* Render a standard card section wrapper.
*
* @param array $a Block attributes (variant, label, heading, lead, columns).
* @param string $content InnerBlocks rendered HTML.
* @param string $grid_class CSS class for the grid container (e.g. 'grid').
* @param int $default_cols Default column count if not set in attributes.
* @return string Rendered HTML.
*/
function oribi_render_card_section($a, $content, $grid_class = 'grid', $default_cols = 3)
{
$cls = (!empty($a['variant']) && $a['variant'] === 'alt') ? 'section section-alt' : 'section';
$cols = !empty($a['columns']) ? intval($a['columns']) : $default_cols;
$grid = $grid_class . '-' . $cols;
ob_start(); ?>
<section class="<?php echo esc_attr($cls); ?>">
<div class="container">
<div class="section-header">
<?php if (!empty($a['label'])): ?><span class="section-label"><?php echo esc_html($a['label']); ?></span><?php
endif; ?>
<h2><?php echo wp_kses_post($a['heading'] ?? ''); ?></h2>
<?php if (!empty($a['lead'])): ?><p class="lead"><?php echo wp_kses_post($a['lead']); ?></p><?php
endif; ?>
</div>
<div class="<?php echo esc_attr($grid); ?>">
<?php echo $content; ?>
</div>
</div>
</section>
<?php return ob_get_clean();
}
/**
* Standard section attributes shared by all card section wrappers.
*/
function oribi_card_section_attributes($default_cols = 3)
{
return [
'variant' => ['type' => 'string', 'default' => 'normal'],
'label' => ['type' => 'string', 'default' => ''],
'heading' => ['type' => 'string', 'default' => ''],
'lead' => ['type' => 'string', 'default' => ''],
'columns' => ['type' => 'number', 'default' => $default_cols],
];
}
/* ── Block category ────────────────────────────────────────────────────────── */
add_filter('block_categories_all', function ($cats) {
array_unshift($cats, ['slug' => 'oribi', 'title' => 'OTS Theme']);
return $cats;
});
/* ── Enqueue editor assets ─────────────────────────────────────────────────── */
add_action('enqueue_block_editor_assets', function () {
$dir = get_template_directory();
$uri = get_template_directory_uri();
wp_enqueue_script(
'oribi-blocks',
$uri . '/blocks/editor.js',
['wp-blocks', 'wp-element', 'wp-block-editor', 'wp-components', 'wp-i18n'],
filemtime($dir . '/blocks/editor.js'),
true
);
wp_enqueue_style(
'oribi-blocks-editor',
$uri . '/blocks/editor.css',
['wp-edit-blocks'],
filemtime($dir . '/blocks/editor.css')
);
});
/* ── Register all blocks ───────────────────────────────────────────────────── */
add_action('init', function () {
/* Shared supports - exposes color pickers and font-size selector in the
block inspector for every Oribi block. Individual blocks can override
these by merging their own array if needed. */
$block_supports = [
'color' => [
'text' => true,
'background' => true,
'link' => true,
],
'typography' => [
'fontSize' => true,
'lineHeight' => true,
],
'spacing' => [
'padding' => true,
'margin' => true,
],
];
/* ── TEMPLATE-PART HELPER BLOCKS ──────────────────────────────────────── */
register_block_type('oribi/site-header', [
'render_callback' => 'oribi_render_site_header',
]);
register_block_type('oribi/site-footer', [
'render_callback' => 'oribi_render_site_footer',
]);
/* ── STANDALONE BLOCKS ─────────────────────────────────────────────────── */
register_block_type('oribi/hero', [
'attributes' => [
'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'],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_hero',
]);
register_block_type('oribi/page-hero', [
'attributes' => [
'label' => ['type' => 'string', 'default' => ''],
'title' => ['type' => 'string', 'default' => ''],
'description' => ['type' => 'string', 'default' => ''],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_page_hero',
]);
register_block_type('oribi/cta-banner', [
'attributes' => [
'heading' => ['type' => 'string', 'default' => ''],
'text' => ['type' => 'string', 'default' => ''],
'btnText' => ['type' => 'string', 'default' => ''],
'btnUrl' => ['type' => 'string', 'default' => ''],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_cta_banner',
]);
register_block_type('oribi/intro-section', [
'attributes' => [
'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],
'cloudAnim' => ['type' => 'boolean', 'default' => false],
'imgId' => ['type' => 'number', 'default' => 0],
'imgUrl' => ['type' => 'string', 'default' => ''],
'imgAlt' => ['type' => 'string', 'default' => ''],
'imgWidth' => ['type' => 'number', 'default' => 280],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_intro_section',
]);
register_block_type('oribi/contact-section', [
'attributes' => [
'heading' => ['type' => 'string', 'default' => "Let's Talk"],
'lead' => ['type' => 'string', 'default' => ''],
'email' => ['type' => 'string', 'default' => 'solutions@oribi-tech.com'],
'supportUrl' => ['type' => 'string', 'default' => 'https://portal.oribi-tech.com/helpdesk/technical-support-1'],
'portalUrl' => ['type' => 'string', 'default' => 'https://portal.oribi-tech.com/'],
'location' => ['type' => 'string', 'default' => 'Saratoga Springs, Upstate New York'],
'formHeading' => ['type' => 'string', 'default' => 'Want to Learn More?'],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_contact_section',
]);
/* ── PARENT / CHILD PAIRS ──────────────────────────────────────────────── */
/* Feature Section (parent) */
register_block_type('oribi/feature-section', [
'attributes' => oribi_card_section_attributes(3),
'supports' => $block_supports,
'render_callback' => 'oribi_render_feature_section',
]);
/* Feature Card (child) */
register_block_type('oribi/feature-card', [
'attributes' => array_merge(
[
'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],
],
oribi_card_image_attributes()
),
'supports' => $block_supports,
'render_callback' => 'oribi_render_feature_card',
]);
/* Value Section (parent) */
register_block_type('oribi/value-section', [
'attributes' => oribi_card_section_attributes(3),
'supports' => $block_supports,
'render_callback' => 'oribi_render_value_section',
]);
/* Value Card (child) */
register_block_type('oribi/value-card', [
'attributes' => array_merge(
[
'icon' => ['type' => 'string', 'default' => ''],
'iconType' => ['type' => 'string', 'default' => 'emoji'],
'faIcon' => ['type' => 'string', 'default' => ''],
'title' => ['type' => 'string', 'default' => ''],
'description' => ['type' => 'string', 'default' => ''],
],
oribi_card_image_attributes()
),
'supports' => $block_supports,
'render_callback' => 'oribi_render_value_card',
]);
/* Addon Section (parent) */
register_block_type('oribi/addon-section', [
'attributes' => oribi_card_section_attributes(3),
'supports' => $block_supports,
'render_callback' => 'oribi_render_addon_section',
]);
/* Addon Card (child) */
register_block_type('oribi/addon-card', [
'attributes' => array_merge(
[
'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' => ''],
],
oribi_card_image_attributes()
),
'supports' => $block_supports,
'render_callback' => 'oribi_render_addon_card',
]);
/* Image Section (parent) */
register_block_type('oribi/image-section', [
'attributes' => oribi_card_section_attributes(3),
'supports' => $block_supports,
'render_callback' => 'oribi_render_image_section',
]);
/* Image Card (child) */
register_block_type('oribi/image-card', [
'attributes' => array_merge(
[
'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' => ''],
],
oribi_card_image_attributes()
),
'supports' => $block_supports,
'render_callback' => 'oribi_render_image_card',
]);
/* Stat Section (parent) */
register_block_type('oribi/stat-section', [
'attributes' => oribi_card_section_attributes(3),
'supports' => $block_supports,
'render_callback' => 'oribi_render_stat_section',
]);
/* Stat Card (child) */
register_block_type('oribi/stat-card', [
'attributes' => array_merge(
[
'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' => ''],
],
oribi_card_image_attributes()
),
'supports' => $block_supports,
'render_callback' => 'oribi_render_stat_card',
]);
/* Link Section (parent) */
register_block_type('oribi/link-section', [
'attributes' => oribi_card_section_attributes(3),
'supports' => $block_supports,
'render_callback' => 'oribi_render_link_section',
]);
/* Link Card (child) */
register_block_type('oribi/link-card', [
'attributes' => array_merge(
[
'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' => ''],
],
oribi_card_image_attributes()
),
'supports' => $block_supports,
'render_callback' => 'oribi_render_link_card',
]);
/* Pricing Section (parent) */
register_block_type('oribi/pricing-section', [
'attributes' => [
'variant' => ['type' => 'string', 'default' => 'normal'],
'label' => ['type' => 'string', 'default' => ''],
'heading' => ['type' => 'string', 'default' => ''],
'lead' => ['type' => 'string', 'default' => ''],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_pricing_section',
]);
/* Pricing Card (child) */
register_block_type('oribi/pricing-card', [
'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],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_pricing_card',
]);
/* Platform Section (parent) */
register_block_type('oribi/platform-section', [
'attributes' => [
'label' => ['type' => 'string', 'default' => ''],
'heading' => ['type' => 'string', 'default' => ''],
'lead' => ['type' => 'string', 'default' => ''],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_platform_section',
]);
/* Platform Row (child) */
register_block_type('oribi/platform-row', [
'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],
'isDashboard' => ['type' => 'boolean', 'default' => false],
'deviceAnim' => ['type' => 'boolean', 'default' => false],
'tvStick' => ['type' => 'boolean', 'default' => false],
'cameraAnim' => ['type' => 'boolean', 'default' => false],
'neverGoesDark' => ['type' => 'boolean', 'default' => false],
'brandedAnim' => ['type' => 'boolean', 'default' => false],
'hospitalityAnim' => ['type' => 'boolean', 'default' => false],
'retailAnim' => ['type' => 'boolean', 'default' => false],
'corporateAnim' => ['type' => 'boolean', 'default' => false],
'educationAnim' => ['type' => 'boolean', 'default' => false],
'outdoorAnim' => ['type' => 'boolean', 'default' => false],
'liveDataAnim' => ['type' => 'boolean', 'default' => false],
'healthcareAnim' => ['type' => 'boolean', 'default' => false],
'transitAnim' => ['type' => 'boolean', 'default' => false],
'fitnessAnim' => ['type' => 'boolean', 'default' => false],
'lobbyAnim' => ['type' => 'boolean', 'default' => false],
'conferenceAnim' => ['type' => 'boolean', 'default' => false],
'dayPartAnim' => ['type' => 'boolean', 'default' => false],
'wayfindAnim' => ['type' => 'boolean', 'default' => false],
'storefrontAnim' => ['type' => 'boolean', 'default' => false],
'announcementAnim' => ['type' => 'boolean', 'default' => false],
'campusWayfindAnim' => ['type' => 'boolean', 'default' => false],
'emergencyAnim' => ['type' => 'boolean', 'default' => false],
'enclosureAnim' => ['type' => 'boolean', 'default' => false],
'brightnessAnim' => ['type' => 'boolean', 'default' => false],
'cellularAnim' => ['type' => 'boolean', 'default' => false],
'designerAnim' => ['type' => 'boolean', 'default' => false],
'mediaLibraryAnim' => ['type' => 'boolean', 'default' => false],
'publishAnim' => ['type' => 'boolean', 'default' => false],
'screenGroupsAnim' => ['type' => 'boolean', 'default' => false],
'monitoringAnim' => ['type' => 'boolean', 'default' => false],
'patientWayfindAnim' => ['type' => 'boolean', 'default' => false],
'waitingRoomAnim' => ['type' => 'boolean', 'default' => false],
'multiZoneAnim' => ['type' => 'boolean', 'default' => false],
'membershipAnim' => ['type' => 'boolean', 'default' => false],
'videoMotionAnim' => ['type' => 'boolean', 'default' => false],
'brandLayoutAnim' => ['type' => 'boolean', 'default' => false],
'menuBoardAnim' => ['type' => 'boolean', 'default' => false],
'galleryIds' => ['type' => 'array', 'default' => [], 'items' => ['type' => 'number']],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_platform_row',
]);
/* FAQ Section (parent) */
register_block_type('oribi/faq-section', [
'attributes' => [
'variant' => ['type' => 'string', 'default' => 'normal'],
'label' => ['type' => 'string', 'default' => ''],
'heading' => ['type' => 'string', 'default' => ''],
'lead' => ['type' => 'string', 'default' => ''],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_faq_section',
]);
/* FAQ Item (child) */
register_block_type('oribi/faq-item', [
'attributes' => [
'question' => ['type' => 'string', 'default' => ''],
'answer' => ['type' => 'string', 'default' => ''],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_faq_item',
]);
/* Comparison Table */
register_block_type('oribi/comparison-table', [
'attributes' => [
'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' => []],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_comparison_table',
]);
/* Trust Section (parent) */
register_block_type('oribi/trust-section', [
'attributes' => [
'label' => ['type' => 'string', 'default' => ''],
'heading' => ['type' => 'string', 'default' => ''],
'lead' => ['type' => 'string', 'default' => ''],
'btnText' => ['type' => 'string', 'default' => ''],
'btnUrl' => ['type' => 'string', 'default' => ''],
'btnSub' => ['type' => 'string', 'default' => ''],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_trust_section',
]);
/* Trust Item (child) */
register_block_type('oribi/trust-item', [
'attributes' => [
'heading' => ['type' => 'string', 'default' => ''],
'description' => ['type' => 'string', 'default' => ''],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_trust_item',
]);
/* ── ANIMATED HERO BLOCKS (OTS Signs) ─────────────────────────────────── */
/* Animated Hero - full homepage hero with particle background */
register_block_type('oribi/hero-animated', [
'attributes' => [
'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' => ''],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_hero_animated',
]);
/* Animated Page Hero - inner pages with particle background */
register_block_type('oribi/page-hero-animated', [
'attributes' => [
'label' => ['type' => 'string', 'default' => ''],
'title' => ['type' => 'string', 'default' => ''],
'description' => ['type' => 'string', 'default' => ''],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_page_hero_animated',
]);
});
/* ══════════════════════════════════════════════════════════════════════════════
RENDER CALLBACKS
══════════════════════════════════════════════════════════════════════════════ */
/* ── Site Header ───────────────────────────────────────────────────────────── */
function oribi_render_site_header()
{
$has_logo = has_custom_logo();
ob_start(); ?>
<header class="site-header" id="site-header">
<div class="container header-inner">
<div class="site-logo">
<?php if ($has_logo): ?>
<?php the_custom_logo(); ?>
<?php
endif; ?>
<a href="<?php echo esc_url(home_url('/')); ?>" class="logo-text"><strong>OTS</strong> Signs</a>
</div>
<button class="nav-toggle" id="nav-toggle" aria-expanded="false" aria-label="<?php esc_attr_e('Toggle navigation', 'ots-theme'); ?>">
<span></span><span></span><span></span>
</button>
<nav class="site-nav" id="site-nav">
<?php
wp_nav_menu([
'theme_location' => 'primary',
'container' => false,
'menu_class' => 'nav-menu',
'depth' => 2,
'fallback_cb' => 'oribi_fallback_menu',
]);
?>
</nav>
</div>
</header>
<?php return ob_get_clean();
}
/** Fallback menu when no WP menu is assigned. */
function oribi_fallback_menu()
{
echo '<ul class="nav-menu">';
echo '<li><a href="' . esc_url(home_url('/solutions')) . '">Solutions</a></li>';
echo '<li><a href="' . esc_url(home_url('/features')) . '">Features</a></li>';
echo '<li><a href="' . esc_url(home_url('/pricing')) . '">Pricing</a></li>';
echo '<li><a href="' . esc_url(home_url('/partners')) . '">Partners</a></li>';
echo '<li><a href="' . esc_url(home_url('/about')) . '">About</a></li>';
echo '<li class="nav-contact"><a href="' . esc_url(home_url('/contact')) . '" class="btn btn-primary">Contact</a></li>';
echo '</ul>';
}
/* ── Site Footer ───────────────────────────────────────────────────────────── */
function oribi_render_site_footer()
{
$year = gmdate('Y');
ob_start(); ?>
<footer class="site-footer">
<div class="container">
<div class="footer-inner">
<div class="footer-brand">
<div class="footer-logo-section">
<?php if (has_custom_logo()): ?>
<?php the_custom_logo(); ?>
<?php
else: ?>
<div class="logo-text"><strong>Oribi</strong> Tech</div>
<?php
endif; ?>
<div class="footer-company-name">
<a href="<?php echo esc_url(home_url('/')); ?>" class="logo-text"><strong>OTS</strong> Signs</a>
</div>
</div>
<p class="footer-tagline">Digital signage solutions that communicate, engage, and grow your business.</p>
<p class="footer-location">An <a href="https://oribi-tech.com">Oribi Technology Services</a> Company</p>
</div>
<div class="footer-links">
<div class="footer-col">
<h4>Platform</h4>
<ul>
<li><a href="<?php echo esc_url(home_url('/features')); ?>">Features</a></li>
<li><a href="<?php echo esc_url(home_url('/pricing')); ?>">Pricing</a></li>
<li><a href="<?php echo esc_url(home_url('/devices')); ?>">Devices</a></li>
<li><a href="<?php echo esc_url(home_url('/demo')); ?>">Demo</a></li>
<li><a href="<?php echo esc_url(home_url('/security')); ?>">Security</a></li>
</ul>
</div>
<div class="footer-col">
<h4>Solutions</h4>
<ul>
<li><a href="<?php echo esc_url(home_url('/solutions')); ?>">Industries</a></li>
<li><a href="<?php echo esc_url(home_url('/kiosks')); ?>">Interactive Kiosks</a></li>
<li><a href="<?php echo esc_url(home_url('/outdoor')); ?>">Outdoor Signage</a></li>
<li><a href="<?php echo esc_url(home_url('/partners')); ?>">Partners</a></li>
</ul>
</div>
<div class="footer-col">
<h4>Company</h4>
<ul>
<li><a href="<?php echo esc_url(home_url('/about')); ?>">About</a></li>
<li><a href="<?php echo esc_url(home_url('/resources')); ?>">Resources</a></li>
<li><a href="<?php echo esc_url(home_url('/faq')); ?>">FAQ</a></li>
</ul>
</div>
<div class="footer-col">
<h4>Connect</h4>
<ul>
<li><a href="<?php echo esc_url(home_url('/contact')); ?>">Contact</a></li>
<li><a href="https://ots-signs.com/portal" target="_blank" rel="noopener">Client Portal</a></li>
<li><a href="mailto:hello@ots-signs.com">Email Us</a></li>
</ul>
</div>
</div>
</div>
<div class="footer-bottom">
<p>&copy; <?php echo esc_html($year); ?> Oribi Technology Services. All rights reserved.</p>
<button id="theme-toggle" class="theme-toggle" aria-label="<?php esc_attr_e('Switch to dark mode', 'ots-theme'); ?>">
<svg class="theme-toggle-icon sun-icon" viewBox="0 0 20 20" fill="currentColor"><path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z"/></svg>
<svg class="theme-toggle-icon moon-icon" viewBox="0 0 20 20" fill="currentColor"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"/></svg>
</button>
</div>
</div>
</footer>
<button id="scroll-top" class="scroll-top" aria-label="<?php esc_attr_e('Scroll to top', 'ots-theme'); ?>">&#8593;</button>
<?php return ob_get_clean();
}
/** Helper: highlight a word in a string. */
function oribi_highlight($text, $word)
{
if (!$word)
return wp_kses_post($text);
return str_replace(
esc_html($word),
'<span class="highlight">' . esc_html($word) . '</span>',
wp_kses_post($text)
);
}
/* ── Hero ──────────────────────────────────────────────────────────────────── */
function oribi_render_hero($a)
{
ob_start();
?>
<section class="hero">
<div class="container hero-inner">
<div class="hero-content">
<span class="hero-label"><?php echo wp_kses_post($a['label']); ?></span>
<h1 class="hero-title"><?php echo oribi_highlight($a['title'], $a['highlightWord']); ?></h1>
<p class="hero-description"><?php echo wp_kses_post($a['description']); ?></p>
<div class="btn-group">
<a href="<?php echo esc_url($a['primaryBtnUrl']); ?>" class="btn btn-primary btn-lg"><?php echo esc_html($a['primaryBtnText']); ?></a>
<?php if ($a['secondaryBtnUrl']): ?>
<a href="<?php echo esc_url($a['secondaryBtnUrl']); ?>" target="_blank" rel="noopener" class="btn btn-ghost btn-lg"><?php echo esc_html($a['secondaryBtnText']); ?> &rarr;</a>
<?php
endif; ?>
</div>
<div class="hero-stats">
<div><div class="hero-stat-value"><?php echo esc_html($a['stat1Value']); ?></div><div class="hero-stat-label"><?php echo esc_html($a['stat1Label']); ?></div></div>
<div><div class="hero-stat-value"><?php echo esc_html($a['stat2Value']); ?></div><div class="hero-stat-label"><?php echo esc_html($a['stat2Label']); ?></div></div>
</div>
</div>
<div class="hero-visual">
<div class="hero-devices">
<!-- Device 1: Laptop -->
<div class="hero-device hero-device--laptop">
<div class="hero-device__frame">
<div class="hero-device__screen">
<div class="hero-device__screen-content">
<div class="hero-device__app-bars">
<div></div><div></div><div></div><div></div>
</div>
</div>
</div>
<div class="hero-device__base"></div>
</div>
<ul class="hero-device__services">
<li class="svc svc--1"><span class="svc__dot"></span> <?php echo esc_html($a['svcLaptop1']); ?></li>
<li class="svc svc--2"><span class="svc__dot"></span> <?php echo esc_html($a['svcLaptop2']); ?></li>
<li class="svc svc--3"><span class="svc__dot"></span> <?php echo esc_html($a['svcLaptop3']); ?></li>
</ul>
</div>
<!-- Device 2: Cloud (365Care) -->
<div class="hero-device hero-device--cloud">
<div class="hero-device__frame">
<div class="hero-device__cloud-icon">
<svg viewBox="0 0 56 40" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<path d="M44.8 16.8C44.8 8.4 38 1.6 29.6 1.6c-6.8 0-12.6 4.4-14.6 10.6C8.4 13 3.2 18.6 3.2 25.4c0 7.4 6 13.4 13.4 13.4h27c5.8 0 10.4-4.6 10.4-10.4 0-5.4-4.2-9.8-9.2-10.2z" stroke="var(--color-accent)" stroke-width="2" fill="none"/>
</svg>
<span class="hero-device__cloud-label">365</span>
</div>
</div>
<ul class="hero-device__services">
<li class="svc svc--1"><span class="svc__dot"></span> <?php echo esc_html($a['svcCloud1']); ?></li>
<li class="svc svc--2"><span class="svc__dot"></span> <?php echo esc_html($a['svcCloud2']); ?></li>
<li class="svc svc--3"><span class="svc__dot"></span> <?php echo esc_html($a['svcCloud3']); ?></li>
</ul>
</div>
<!-- Device 3: Desktop Monitor -->
<div class="hero-device hero-device--desktop">
<div class="hero-device__frame">
<div class="hero-device__screen">
<div class="hero-device__screen-content">
<div class="hero-device__dash-row">
<div class="hero-device__dash-card"></div>
<div class="hero-device__dash-card"></div>
</div>
<div class="hero-device__dash-bar"></div>
<div class="hero-device__dash-bar hero-device__dash-bar--short"></div>
</div>
</div>
<div class="hero-device__stand"></div>
<div class="hero-device__stand-base"></div>
</div>
<ul class="hero-device__services">
<li class="svc svc--1"><span class="svc__dot"></span> <?php echo esc_html($a['svcDesktop1']); ?></li>
<li class="svc svc--2"><span class="svc__dot"></span> <?php echo esc_html($a['svcDesktop2']); ?></li>
<li class="svc svc--3"><span class="svc__dot"></span> <?php echo esc_html($a['svcDesktop3']); ?></li>
</ul>
</div>
<!-- Device 4: Phone -->
<div class="hero-device hero-device--phone">
<div class="hero-device__frame">
<div class="hero-device__screen">
<div class="hero-device__screen-content">
<div class="hero-device__notif">
<span class="hero-device__notif-icon">✓</span>
<span class="hero-device__notif-text">Secure</span>
</div>
</div>
</div>
</div>
<ul class="hero-device__services">
<li class="svc svc--1"><span class="svc__dot"></span> <?php echo esc_html($a['svcPhone1']); ?></li>
<li class="svc svc--2"><span class="svc__dot"></span> <?php echo esc_html($a['svcPhone2']); ?></li>
</ul>
</div>
</div>
</div>
</div>
</section>
<?php return ob_get_clean();
}
/* ── Page Hero ─────────────────────────────────────────────────────────────── */
function oribi_render_page_hero($a)
{
ob_start(); ?>
<section class="page-hero">
<canvas id="dc-canvas" class="dc-canvas" aria-hidden="true"></canvas>
<div class="hero-overlay"></div>
<div class="container">
<?php if ($a['label']): ?><span class="hero-label"><?php echo wp_kses_post($a['label']); ?></span><?php
endif; ?>
<h1><?php echo wp_kses_post($a['title']); ?></h1>
<p class="lead"><?php echo wp_kses_post($a['description']); ?></p>
</div>
</section>
<?php return ob_get_clean();
}
/* ── CTA Banner ────────────────────────────────────────────────────────────── */
function oribi_render_cta_banner($a)
{
ob_start(); ?>
<section class="cta-banner">
<div class="container text-center">
<h2><?php echo wp_kses_post($a['heading']); ?></h2>
<p><?php echo wp_kses_post($a['text']); ?></p>
<a href="<?php echo esc_url($a['btnUrl']); ?>" class="btn btn-primary btn-lg" style="background:#fff;color:var(--color-primary);"><?php echo esc_html($a['btnText']); ?></a>
</div>
</section>
<?php return ob_get_clean();
}
/* ── Intro Section ─────────────────────────────────────────────────────────── */
function oribi_render_intro_section($a)
{
$cls = $a['variant'] === 'alt' ? 'section section-alt' : 'section';
$rev = $a['reversed'] ? ' style="direction:rtl;"' : '';
$ltr = $a['reversed'] ? ' style="direction:ltr;"' : '';
ob_start(); ?>
<section class="<?php echo esc_attr($cls); ?>">
<div class="container">
<div class="about-intro"<?php echo $rev; ?>>
<div<?php echo $ltr; ?>>
<?php if ($a['label']): ?><span class="section-label"><?php echo esc_html($a['label']); ?></span><?php
endif; ?>
<h2 style="margin-bottom:1.5rem;"><?php echo wp_kses_post($a['heading']); ?></h2>
<p class="lead"><?php echo wp_kses_post($a['description']); ?></p>
</div>
<div class="about-intro-visual<?php echo !empty($a['cloudAnim']) ? ' has-cloud-anim' : ''; ?>"<?php echo $ltr; ?>>
<?php
if (!empty($a['cloudAnim'])) {
echo '<div class="ds-anim-container">';
echo '<div class="ds-tv"><i class="fas fa-tv" aria-hidden="true"></i><div class="ds-tv-screen"></div></div>';
echo '<div class="ds-line"><div class="ds-packet ds-packet-1"></div><div class="ds-packet ds-packet-2"></div><div class="ds-packet ds-packet-3"></div></div>';
echo '<div class="ds-cloud"><i class="fas fa-cloud" aria-hidden="true"></i></div>';
echo '</div>';
}
else {
echo wp_kses_post($a['visual']);
}
?>
</div>
</div>
</div>
</section>
<?php return ob_get_clean();
}
/* ── Contact Section ───────────────────────────────────────────────────────── */
function oribi_render_contact_section($a)
{
ob_start(); ?>
<section class="section">
<div class="container">
<div class="contact-layout">
<div class="contact-info">
<h2><?php echo wp_kses_post($a['heading']); ?></h2>
<p class="lead"><?php echo wp_kses_post($a['lead']); ?></p>
<div class="contact-method"><div class="contact-method-icon"><i class="fas fa-envelope" aria-hidden="true"></i></div><div><div class="contact-method-label">Email Us</div><div class="contact-method-value"><a href="mailto:<?php echo esc_attr($a['email']); ?>"><?php echo esc_html($a['email']); ?></a></div></div></div>
<div class="contact-method"><div class="contact-method-icon"><i class="fas fa-headset" aria-hidden="true"></i></div><div><div class="contact-method-label">Existing Customer Support</div><div class="contact-method-value"><a href="<?php echo esc_url($a['supportUrl']); ?>" target="_blank" rel="noopener">Open a Support Ticket</a></div></div></div>
<div class="contact-method"><div class="contact-method-icon"><i class="fas fa-globe" aria-hidden="true"></i></div><div><div class="contact-method-label">Client Portal</div><div class="contact-method-value"><a href="<?php echo esc_url($a['portalUrl']); ?>" target="_blank" rel="noopener"><?php echo esc_html(wp_parse_url($a['portalUrl'], PHP_URL_HOST)); ?></a></div></div></div>
<div class="contact-method"><div class="contact-method-icon"><i class="fas fa-map-marker-alt" aria-hidden="true"></i></div><div><div class="contact-method-label">Location</div><div class="contact-method-value"><?php echo esc_html($a['location']); ?></div></div></div>
</div>
<div class="contact-form-wrap">
<h3 style="margin-bottom:1.5rem;"><?php echo esc_html($a['formHeading']); ?></h3>
<form id="contact-form">
<div class="form-row">
<div class="form-group"><label for="cf-name">Name <span class="req">*</span></label><input type="text" id="cf-name" name="name" required></div>
<div class="form-group"><label for="cf-email">Email <span class="req">*</span></label><input type="email" id="cf-email" name="email" required></div>
</div>
<div class="form-group">
<label for="cf-interest">Interested In</label>
<select id="cf-interest" name="interest">
<option value="">Select a topic&hellip;</option>
<option value="Digital Signage">Digital Signage Solutions</option>
<option value="Content Creation">Content Creation</option>
<option value="Hardware">Player Devices &amp; Hardware</option>
<option value="Pricing">Pricing &amp; Plans</option>
<option value="Demo">Request a Demo</option>
<option value="Support">Technical Support</option>
<option value="Other">Other / Not sure</option>
</select>
</div>
<div class="form-group"><label for="cf-message">Message <span class="req">*</span></label><textarea id="cf-message" name="message" required></textarea></div>
<button type="submit" class="btn btn-primary btn-lg" style="width:100%;justify-content:center;">Submit</button>
<div class="form-notice" id="form-notice"></div>
</form>
</div>
</div>
</div>
</section>
<?php return ob_get_clean();
}
/* ── Feature Section (parent - wraps child feature-card blocks) ────────────── */
function oribi_render_feature_section($a, $content)
{
return oribi_render_card_section($a, $content, 'grid', 3);
}
/* ── Feature Card (child - renders one card) ───────────────────────────────── */
function oribi_render_feature_card($a)
{
$tag = !empty($a['url']) ? 'a' : 'div';
$href = !empty($a['url']) ? ' href="' . esc_url($a['url']) . '"' : '';
$link_cls = !empty($a['url']) ? ' feature-card-link' : '';
$center = !empty($a['centered']);
$img = oribi_card_image_html($a);
$img_cls = $img['card_class'] ? ' ' . $img['card_class'] : '';
ob_start();
if ($img['html'] && $img['position'] === 'left'): ?>
<<?php echo $tag . $href; ?> class="oribi-card<?php echo esc_attr($link_cls . $img_cls); ?>">
<?php echo $img['html']; ?>
<div class="oribi-card-body">
<h3><?php echo wp_kses_post($a['title']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
</div>
</<?php echo $tag; ?>>
<?php
elseif ($img['html'] && $img['position'] === 'background'): ?>
<<?php echo $tag . $href; ?> class="oribi-card<?php echo esc_attr($link_cls . $img_cls); ?>">
<?php echo $img['html']; ?>
<div class="oribi-card-body">
<?php if (oribi_has_icon($a)): ?><div class="feature-icon"<?php echo $center ? ' style="margin-inline:auto;"' : ''; ?>><?php echo oribi_render_icon($a); ?></div><?php
endif; ?>
<h3><?php echo wp_kses_post($a['title']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
</div>
</<?php echo $tag; ?>>
<?php
else: ?>
<<?php echo $tag . $href; ?> class="oribi-card<?php echo esc_attr($link_cls . $img_cls); ?><?php echo $center ? ' text-center' : ''; ?>">
<?php if ($img['html'] && ($img['position'] === 'top' || $img['position'] === 'replace-icon')): ?>
<?php echo $img['html']; ?>
<?php
endif; ?>
<?php if ((!$img['html'] || $img['position'] !== 'replace-icon') && oribi_has_icon($a)): ?>
<div class="feature-icon"<?php echo $center ? ' style="margin-inline:auto;"' : ''; ?>><?php echo oribi_render_icon($a); ?></div>
<?php
endif; ?>
<h3><?php echo wp_kses_post($a['title']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
</<?php echo $tag; ?>>
<?php
endif;
return ob_get_clean();
}
/* ── Value Section ─────────────────────────────────────────────────────────── */
function oribi_render_value_section($a, $content)
{
return oribi_render_card_section($a, $content, 'grid', 3);
}
/* ── Value Card ────────────────────────────────────────────────────────────── */
function oribi_render_value_card($a)
{
$img = oribi_card_image_html($a);
$img_cls = $img['card_class'] ? ' ' . $img['card_class'] : '';
ob_start();
if ($img['html'] && $img['position'] === 'background'): ?>
<div class="oribi-card value-card<?php echo esc_attr($img_cls); ?>">
<?php echo $img['html']; ?>
<div class="oribi-card-body">
<?php if (oribi_has_icon($a)): ?><div class="value-icon"><?php echo oribi_render_icon($a); ?></div><?php
endif; ?>
<h3><?php echo wp_kses_post($a['title']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
</div>
</div>
<?php
elseif ($img['html'] && $img['position'] === 'left'): ?>
<div class="oribi-card value-card<?php echo esc_attr($img_cls); ?>">
<?php echo $img['html']; ?>
<div class="oribi-card-body">
<h3><?php echo wp_kses_post($a['title']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
</div>
</div>
<?php
else: ?>
<div class="oribi-card value-card<?php echo esc_attr($img_cls); ?>">
<?php if ($img['html'] && ($img['position'] === 'top' || $img['position'] === 'replace-icon'))
echo $img['html']; ?>
<?php if ((!$img['html'] || $img['position'] !== 'replace-icon') && oribi_has_icon($a)): ?>
<div class="value-icon"><?php echo oribi_render_icon($a); ?></div>
<?php
endif; ?>
<h3><?php echo wp_kses_post($a['title']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
</div>
<?php
endif;
return ob_get_clean();
}
/* ── Addon Section ─────────────────────────────────────────────────────────── */
function oribi_render_addon_section($a, $content)
{
return oribi_render_card_section($a, $content, 'grid', 3);
}
/* ── Addon Card ────────────────────────────────────────────────────────────── */
function oribi_render_addon_card($a)
{
$img = oribi_card_image_html($a);
$img_cls = $img['card_class'] ? ' ' . $img['card_class'] : '';
ob_start();
if ($img['html'] && $img['position'] === 'left'): ?>
<div class="oribi-card addon-card<?php echo esc_attr($img_cls); ?>">
<?php echo $img['html']; ?>
<div class="oribi-card-body">
<?php if (!empty($a['tag'])): ?><span class="addon-tag"><?php echo esc_html($a['tag']); ?></span><?php
endif; ?>
<h3><?php echo wp_kses_post($a['title']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
</div>
</div>
<?php
elseif ($img['html'] && $img['position'] === 'background'): ?>
<div class="oribi-card addon-card<?php echo esc_attr($img_cls); ?>">
<?php echo $img['html']; ?>
<div class="oribi-card-body">
<?php if (oribi_has_icon($a)): ?><div class="feature-icon"><?php echo oribi_render_icon($a); ?></div><?php
endif; ?>
<?php if (!empty($a['tag'])): ?><span class="addon-tag"><?php echo esc_html($a['tag']); ?></span><?php
endif; ?>
<h3><?php echo wp_kses_post($a['title']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
</div>
</div>
<?php
else: ?>
<div class="oribi-card addon-card<?php echo esc_attr($img_cls); ?>">
<?php if ($img['html'] && ($img['position'] === 'top' || $img['position'] === 'replace-icon'))
echo $img['html']; ?>
<?php if ((!$img['html'] || $img['position'] !== 'replace-icon') && oribi_has_icon($a)): ?>
<div class="feature-icon"><?php echo oribi_render_icon($a); ?></div>
<?php
endif; ?>
<?php if (!empty($a['tag'])): ?><span class="addon-tag"><?php echo esc_html($a['tag']); ?></span><?php
endif; ?>
<h3><?php echo wp_kses_post($a['title']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
</div>
<?php
endif;
return ob_get_clean();
}
/* ── Image Section ─────────────────────────────────────────────────────────── */
function oribi_render_image_section($a, $content)
{
return oribi_render_card_section($a, $content, 'grid', 3);
}
/* ── Image Card ────────────────────────────────────────────────────────────── */
function oribi_render_image_card($a)
{
$tag = !empty($a['url']) ? 'a' : 'div';
$href = !empty($a['url']) ? ' href="' . esc_url($a['url']) . '"' : '';
$link_cls = !empty($a['url']) ? ' feature-card-link' : '';
$img = oribi_card_image_html($a);
$img_cls = $img['card_class'] ? ' ' . $img['card_class'] : '';
ob_start();
if ($img['html'] && $img['position'] === 'left'): ?>
<<?php echo $tag . $href; ?> class="oribi-card image-card<?php echo esc_attr($link_cls . $img_cls); ?>">
<?php echo $img['html']; ?>
<div class="oribi-card-body">
<h3><?php echo wp_kses_post($a['title']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
</div>
</<?php echo $tag; ?>>
<?php
elseif ($img['html'] && $img['position'] === 'background'): ?>
<<?php echo $tag . $href; ?> class="oribi-card image-card<?php echo esc_attr($link_cls . $img_cls); ?>">
<?php echo $img['html']; ?>
<div class="oribi-card-body">
<?php if (oribi_has_icon($a)): ?><div class="feature-icon"><?php echo oribi_render_icon($a); ?></div><?php
endif; ?>
<h3><?php echo wp_kses_post($a['title']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
</div>
</<?php echo $tag; ?>>
<?php
else: ?>
<<?php echo $tag . $href; ?> class="oribi-card image-card<?php echo esc_attr($link_cls . $img_cls); ?>">
<?php if ($img['html'] && ($img['position'] === 'top' || $img['position'] === 'replace-icon'))
echo $img['html']; ?>
<?php if ((!$img['html'] || $img['position'] !== 'replace-icon') && oribi_has_icon($a)): ?>
<div class="feature-icon"><?php echo oribi_render_icon($a); ?></div>
<?php
endif; ?>
<div class="oribi-card-body">
<h3><?php echo wp_kses_post($a['title']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
</div>
</<?php echo $tag; ?>>
<?php
endif;
return ob_get_clean();
}
/* ── Stat Section ──────────────────────────────────────────────────────────── */
function oribi_render_stat_section($a, $content)
{
return oribi_render_card_section($a, $content, 'grid', 3);
}
/* ── Stat Card ─────────────────────────────────────────────────────────────── */
function oribi_render_stat_card($a)
{
$img = oribi_card_image_html($a);
$img_cls = $img['card_class'] ? ' ' . $img['card_class'] : '';
ob_start();
if ($img['html'] && $img['position'] === 'background'): ?>
<div class="oribi-card stat-card<?php echo esc_attr($img_cls); ?>">
<?php echo $img['html']; ?>
<div class="oribi-card-body">
<?php if (oribi_has_icon($a)): ?><div class="feature-icon"><?php echo oribi_render_icon($a); ?></div><?php
endif; ?>
<div class="stat-value"><?php echo esc_html($a['value'] ?? ''); ?></div>
<div class="stat-label"><?php echo esc_html($a['label'] ?? ''); ?></div>
<?php if (!empty($a['description'])): ?><p><?php echo wp_kses_post($a['description']); ?></p><?php
endif; ?>
</div>
</div>
<?php
elseif ($img['html'] && $img['position'] === 'left'): ?>
<div class="oribi-card stat-card<?php echo esc_attr($img_cls); ?>">
<?php echo $img['html']; ?>
<div class="oribi-card-body">
<div class="stat-value"><?php echo esc_html($a['value'] ?? ''); ?></div>
<div class="stat-label"><?php echo esc_html($a['label'] ?? ''); ?></div>
<?php if (!empty($a['description'])): ?><p><?php echo wp_kses_post($a['description']); ?></p><?php
endif; ?>
</div>
</div>
<?php
else: ?>
<div class="oribi-card stat-card<?php echo esc_attr($img_cls); ?>">
<?php if ($img['html'])
echo $img['html']; ?>
<?php if (oribi_has_icon($a)): ?><div class="feature-icon"><?php echo oribi_render_icon($a); ?></div><?php
endif; ?>
<div class="stat-value"><?php echo esc_html($a['value'] ?? ''); ?></div>
<div class="stat-label"><?php echo esc_html($a['label'] ?? ''); ?></div>
<?php if (!empty($a['description'])): ?><p><?php echo wp_kses_post($a['description']); ?></p><?php
endif; ?>
</div>
<?php
endif;
return ob_get_clean();
}
/* ── Link Section ──────────────────────────────────────────────────────────── */
function oribi_render_link_section($a, $content)
{
return oribi_render_card_section($a, $content, 'grid', 3);
}
/* ── Link Card ─────────────────────────────────────────────────────────────── */
function oribi_render_link_card($a)
{
$img = oribi_card_image_html($a);
$img_cls = $img['card_class'] ? ' ' . $img['card_class'] : '';
$cta = '';
if (!empty($a['linkUrl'])) {
$cta = '<a href="' . esc_url($a['linkUrl']) . '" class="link-card-cta">'
. esc_html($a['linkText'] ?? 'Learn More') . '</a>';
}
ob_start();
if ($img['html'] && $img['position'] === 'left'): ?>
<div class="oribi-card link-card<?php echo esc_attr($img_cls); ?>">
<?php echo $img['html']; ?>
<div class="oribi-card-body">
<?php if (oribi_has_icon($a)): ?><div class="feature-icon"><?php echo oribi_render_icon($a); ?></div><?php
endif; ?>
<h3><?php echo wp_kses_post($a['title']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
<?php echo $cta; ?>
</div>
</div>
<?php
elseif ($img['html'] && $img['position'] === 'background'): ?>
<div class="oribi-card link-card<?php echo esc_attr($img_cls); ?>">
<?php echo $img['html']; ?>
<div class="oribi-card-body">
<?php if (oribi_has_icon($a)): ?><div class="feature-icon"><?php echo oribi_render_icon($a); ?></div><?php
endif; ?>
<h3><?php echo wp_kses_post($a['title']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
<?php echo $cta; ?>
</div>
</div>
<?php
else: ?>
<div class="oribi-card link-card<?php echo esc_attr($img_cls); ?>">
<?php if ($img['html'] && ($img['position'] === 'top' || $img['position'] === 'replace-icon'))
echo $img['html']; ?>
<?php if ((!$img['html'] || $img['position'] !== 'replace-icon') && oribi_has_icon($a)): ?>
<div class="feature-icon"><?php echo oribi_render_icon($a); ?></div>
<?php
endif; ?>
<h3><?php echo wp_kses_post($a['title']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
<?php echo $cta; ?>
</div>
<?php
endif;
return ob_get_clean();
}
/* ── Pricing Section (parent - wraps child pricing-card blocks) ────────────── */
function oribi_render_pricing_section($a, $content, $block)
{
$cls = $a['variant'] === 'alt' ? 'section section-alt' : 'section';
$count = count($block->inner_blocks);
ob_start(); ?>
<section class="<?php echo esc_attr($cls); ?>">
<div class="container">
<div class="section-header">
<?php if ($a['label']): ?><span class="section-label"><?php echo esc_html($a['label']); ?></span><?php
endif; ?>
<h2><?php echo wp_kses_post($a['heading']); ?></h2>
<?php if ($a['lead']): ?><p class="lead"><?php echo wp_kses_post($a['lead']); ?></p><?php
endif; ?>
</div>
<div class="pricing-grid pricing-grid-<?php echo intval($count); ?>">
<?php echo $content; ?>
</div>
</div>
</section>
<?php return ob_get_clean();
}
/* ── Pricing Card (child - renders one pricing tier) ───────────────────────── */
function oribi_render_pricing_card($a)
{
$featured = !empty($a['featured']);
$img_id = !empty($a['imgId']) ? intval($a['imgId']) : 0;
$img_url = !empty($a['imgUrl']) ? $a['imgUrl'] : '';
$img_alt = !empty($a['imgAlt']) ? $a['imgAlt'] : '';
$img_w = !empty($a['imgWidth']) ? intval($a['imgWidth']) : 80;
$img_html = '';
if ($img_url) {
$img_style = 'width:' . $img_w . 'px;max-width:100%;height:auto;border-radius:var(--radius-sm);object-fit:contain;display:block;margin-inline:auto;';
if ($img_id) {
$img_html = wp_get_attachment_image($img_id, 'full', false, [
'class' => 'card-image',
'style' => $img_style,
'alt' => $img_alt,
]);
}
else {
$img_html = '<img class="card-image" src="' . esc_url($img_url) . '" alt="' . esc_attr($img_alt) . '" style="' . esc_attr($img_style) . '">';
}
}
ob_start(); ?>
<div class="pricing-card<?php echo $featured ? ' featured' : ''; ?>">
<?php if ($featured && !empty($a['badge'])): ?>
<span class="pricing-badge"><?php echo esc_html($a['badge']); ?></span>
<?php
endif; ?>
<?php if ($img_html): ?>
<div class="card-image-wrap" style="text-align:center;margin-bottom:1.25rem;"><?php echo $img_html; ?></div>
<?php
endif; ?>
<?php if (oribi_has_icon($a)): ?><div class="feature-icon" style="margin-inline:auto;"><?php echo oribi_render_icon($a); ?></div><?php
endif; ?>
<div class="pricing-name"><?php echo esc_html($a['name']); ?></div>
<p class="pricing-tagline"><?php echo wp_kses_post($a['tagline']); ?></p>
<?php if (!empty($a['price'])): ?>
<div class="pricing-price">
<div class="pricing-amount"><?php echo wp_kses_post($a['price']); ?></div>
<?php if (!empty($a['pricePer'])): ?><div class="pricing-per"><?php echo wp_kses_post($a['pricePer']); ?></div><?php
endif; ?>
</div>
<?php
endif; ?>
<ul class="pricing-features">
<?php foreach (($a['features'] ?? []) as $f): ?>
<li><span class="pricing-check">&#10003;</span> <?php echo wp_kses_post($f); ?></li>
<?php
endforeach; ?>
</ul>
<a href="<?php echo esc_url($a['btnUrl'] ?? ''); ?>" class="btn <?php echo $featured ? 'btn-primary' : 'btn-outline'; ?>" style="width:100%;justify-content:center;"><?php echo esc_html($a['btnText'] ?? 'Get Started'); ?></a>
</div>
<?php return ob_get_clean();
}
/* ── Platform Section (parent - wraps child platform-row blocks) ───────────── */
function oribi_render_platform_section($a, $content)
{
ob_start(); ?>
<section class="section">
<div class="container">
<div class="section-header">
<?php if ($a['label']): ?><span class="section-label"><?php echo esc_html($a['label']); ?></span><?php
endif; ?>
<h2><?php echo wp_kses_post($a['heading']); ?></h2>
<?php if ($a['lead']): ?><p class="lead"><?php echo wp_kses_post($a['lead']); ?></p><?php
endif; ?>
</div>
<?php echo $content; ?>
</div>
</section>
<?php return ob_get_clean();
}
/* ── Platform Row (child - renders one service row) ────────────────────────── */
function oribi_render_camera_animation()
{
return <<<'HTML'
<div class="ve-stage" aria-hidden="true"><svg viewBox="0 0 540 360" xmlns="http://www.w3.org/2000/svg" class="ve-svg" role="img" aria-label="Animated video editor timeline">
<defs>
<clipPath id="ve-preview-clip">
<rect x="54" y="36" width="256" height="184"/>
</clipPath>
<linearGradient id="ve-sg-warm" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#3d0800"/>
<stop offset="50%" stop-color="#a02500"/>
<stop offset="100%" stop-color="#1d0400"/>
</linearGradient>
<linearGradient id="ve-sg-cold" x1="100%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#0a1f3d"/>
<stop offset="50%" stop-color="#0e3d6e"/>
<stop offset="100%" stop-color="#06111e"/>
</linearGradient>
<linearGradient id="ve-sg-go" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#07180d"/>
<stop offset="50%" stop-color="#1c6038"/>
<stop offset="100%" stop-color="#040e07"/>
</linearGradient>
<linearGradient id="ve-scr-warm" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#ff9500"/>
<stop offset="100%" stop-color="#ff4500"/>
</linearGradient>
<linearGradient id="ve-scr-cold" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#1a8aff"/>
<stop offset="100%" stop-color="#0055cc"/>
</linearGradient>
<linearGradient id="ve-scr-go" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#00cc66"/>
<stop offset="100%" stop-color="#008844"/>
</linearGradient>
<pattern id="ve-scanlines" x="0" y="0" width="1" height="3" patternUnits="userSpaceOnUse">
<rect width="1" height="1" fill="rgba(0,0,0,0.18)"/>
</pattern>
</defs>
<!-- Laptop lid outer shell -->
<rect x="20" y="5" width="500" height="300" rx="12" fill="#d2d2d0" stroke="#b0aca8" stroke-width="1.5"/>
<rect x="28" y="12" width="484" height="280" rx="8" fill="#1c1c1c"/>
<rect x="32" y="16" width="476" height="272" rx="5" fill="#0e1117"/>
<!-- Laptop base -->
<rect x="10" y="305" width="520" height="26" rx="5" fill="#c8c8c8" stroke="#b0ada8" stroke-width="0.8"/>
<rect x="80" y="301" width="380" height="5" rx="2" fill="#a8a6a2"/>
<rect x="205" y="311" width="130" height="13" rx="5" fill="#b8b5b0" stroke="#a0a09a" stroke-width="0.7"/>
<rect x="38" y="306" width="155" height="18" rx="2" fill="#bcbab6" opacity="0.45"/>
<rect x="347" y="306" width="155" height="18" rx="2" fill="#bcbab6" opacity="0.45"/>
<!-- Editor title bar -->
<rect x="32" y="16" width="476" height="20" rx="4" fill="#1e2229"/>
<circle cx="50" cy="26" r="5" fill="#FF5F56"/>
<circle cx="66" cy="26" r="5" fill="#FFBD2E"/>
<circle cx="82" cy="26" r="5" fill="#27C93F"/>
<rect x="180" y="21" width="180" height="10" rx="3" fill="#3a3f48"/>
<rect x="100" y="22" width="28" height="8" rx="2" fill="#2e333c"/>
<rect x="132" y="22" width="24" height="8" rx="2" fill="#2e333c"/>
<rect x="160" y="22" width="16" height="8" rx="2" fill="#2e333c"/>
<!-- Left toolbar -->
<rect x="32" y="36" width="22" height="184" fill="#181c22"/>
<line x1="54" y1="36" x2="54" y2="220" stroke="#2a2e38" stroke-width="1"/>
<rect x="36" y="46" width="14" height="14" rx="2" fill="#3a4152"/>
<rect x="36" y="66" width="14" height="14" rx="2" fill="#3a4152"/>
<rect x="36" y="86" width="14" height="14" rx="2" fill="#3a4152"/>
<rect x="36" y="106" width="14" height="14" rx="2" fill="#3a4152"/>
<rect x="36" y="126" width="14" height="14" rx="2" fill="#3a4152"/>
<rect x="33" y="46" width="3" height="14" rx="1" fill="#D83302"/>
<!-- Preview pane -->
<rect x="54" y="36" width="256" height="184" fill="#0a0b0e"/>
<g clip-path="url(#ve-preview-clip)">
<rect id="ve-scene-1" x="54" y="36" width="256" height="184" fill="url(#ve-sg-warm)" opacity="1"/>
<rect id="ve-scene-2" x="54" y="36" width="256" height="184" fill="url(#ve-sg-cold)" opacity="0"/>
<rect id="ve-scene-3" x="54" y="36" width="256" height="184" fill="url(#ve-sg-go)" opacity="0"/>
<!-- Laptop in the preview video -->
<rect x="102" y="52" width="156" height="106" rx="5" fill="#1c1c1c" stroke="#2e2e2e" stroke-width="1"/>
<rect x="106" y="56" width="148" height="98" rx="3" fill="#111111"/>
<rect id="ve-inner-screen" x="108" y="58" width="144" height="94" rx="1" fill="url(#ve-scr-warm)"/>
<polygon points="108,58 160,58 108,88" fill="white" opacity="0.04"/>
<rect x="108" y="58" width="144" height="94" fill="url(#ve-scanlines)" opacity="0.45"/>
<circle cx="180" cy="55" r="2.5" fill="#232323" stroke="#2e2e2e" stroke-width="0.5"/>
<rect x="92" y="158" width="176" height="17" rx="2" fill="#222222" stroke="#2d2d2d" stroke-width="0.5"/>
<rect x="150" y="162" width="60" height="9" rx="3" fill="#2a2a2a" stroke="#353535" stroke-width="0.5"/>
<rect x="97" y="159" width="166" height="1" fill="#2c2c2c"/>
<line x1="54" y1="175" x2="310" y2="175" stroke="#1c1c1c" stroke-width="2"/>
<rect x="54" y="175" width="256" height="45" fill="#040507" opacity="0.75"/>
<rect x="54" y="36" width="256" height="184" fill="url(#ve-scanlines)" opacity="0.25"/>
</g>
<rect x="54" y="36" width="256" height="184" fill="none" stroke="#2a2e38" stroke-width="0.5"/>
<line x1="310" y1="36" x2="310" y2="220" stroke="#2a2e38" stroke-width="1"/>
<!-- Inspector / Properties panel -->
<rect x="310" y="36" width="198" height="184" fill="#141619"/>
<rect x="310" y="36" width="198" height="20" fill="#1a1e25"/>
<rect x="318" y="42" width="80" height="8" rx="2" fill="#3a3f48"/>
<rect x="318" y="64" width="50" height="7" rx="2" fill="#2a2e38"/>
<rect x="378" y="64" width="82" height="7" rx="2" fill="#333a45"/>
<rect x="318" y="78" width="45" height="7" rx="2" fill="#2a2e38"/>
<rect x="378" y="78" width="96" height="7" rx="2" fill="#333a45"/>
<rect x="318" y="92" width="35" height="7" rx="2" fill="#2a2e38"/>
<rect x="378" y="92" width="62" height="7" rx="2" fill="#333a45"/>
<rect x="318" y="106" width="55" height="7" rx="2" fill="#2a2e38"/>
<rect x="378" y="106" width="72" height="7" rx="2" fill="#333a45"/>
<rect x="318" y="120" width="40" height="7" rx="2" fill="#2a2e38"/>
<rect x="378" y="120" width="54" height="7" rx="2" fill="#333a45"/>
<line x1="318" y1="136" x2="498" y2="136" stroke="#2a2e38" stroke-width="0.5"/>
<rect x="310" y="136" width="198" height="12" fill="#1a1e25"/>
<rect x="318" y="140" width="60" height="6" rx="2" fill="#2a2e38"/>
<rect x="318" y="150" width="32" height="24" rx="1" fill="#C0390A" opacity="0.8"/>
<rect x="354" y="150" width="32" height="24" rx="1" fill="#1a52c8" opacity="0.8"/>
<rect x="390" y="150" width="32" height="24" rx="1" fill="#1a7a3d" opacity="0.8"/>
<rect x="426" y="150" width="32" height="24" rx="1" fill="#c07800" opacity="0.8"/>
<rect x="462" y="150" width="32" height="24" rx="1" fill="#7030d0" opacity="0.8"/>
<!-- Timeline section -->
<rect x="32" y="220" width="476" height="68" fill="#161616"/>
<line x1="32" y1="220" x2="508" y2="220" stroke="#2a2e38" stroke-width="1"/>
<rect x="32" y="220" width="476" height="14" fill="#1a1e24"/>
<rect x="32" y="220" width="72" height="68" fill="#1a1e24"/>
<line x1="104" y1="220" x2="104" y2="288" stroke="#2a2e38" stroke-width="1"/>
<!-- Ruler ticks and labels -->
<line x1="104" y1="220" x2="104" y2="232" stroke="#4a4e58" stroke-width="1"/>
<text x="105" y="231" fill="#565a64" font-size="8" font-family="monospace">0:00</text>
<line x1="184" y1="220" x2="184" y2="232" stroke="#4a4e58" stroke-width="1"/>
<text x="185" y="231" fill="#565a64" font-size="8" font-family="monospace">0:02</text>
<line x1="264" y1="220" x2="264" y2="232" stroke="#4a4e58" stroke-width="1"/>
<text x="265" y="231" fill="#565a64" font-size="8" font-family="monospace">0:04</text>
<line x1="344" y1="220" x2="344" y2="232" stroke="#4a4e58" stroke-width="1"/>
<text x="345" y="231" fill="#565a64" font-size="8" font-family="monospace">0:06</text>
<line x1="424" y1="220" x2="424" y2="232" stroke="#4a4e58" stroke-width="1"/>
<text x="425" y="231" fill="#565a64" font-size="8" font-family="monospace">0:08</text>
<line x1="144" y1="220" x2="144" y2="226" stroke="#2e3240" stroke-width="0.5"/>
<line x1="224" y1="220" x2="224" y2="226" stroke="#2e3240" stroke-width="0.5"/>
<line x1="304" y1="220" x2="304" y2="226" stroke="#2e3240" stroke-width="0.5"/>
<line x1="384" y1="220" x2="384" y2="226" stroke="#2e3240" stroke-width="0.5"/>
<line x1="464" y1="220" x2="464" y2="226" stroke="#2e3240" stroke-width="0.5"/>
<!-- Track label colour strips -->
<rect x="36" y="237" width="56" height="14" rx="2" fill="#283040"/>
<rect x="36" y="255" width="56" height="14" rx="2" fill="#203020"/>
<rect x="36" y="273" width="56" height="14" rx="2" fill="#302818"/>
<!-- Track 1: VIDEO clips -->
<rect x="104" y="234" width="404" height="18" fill="#1a1a22"/>
<rect x="104" y="235" width="88" height="16" rx="2" fill="#2563EB"/>
<rect x="104" y="235" width="88" height="5" rx="2" fill="rgba(255,255,255,0.07)"/>
<rect x="196" y="235" width="66" height="16" rx="2" fill="#1D4ED8"/>
<rect x="196" y="235" width="66" height="5" rx="2" fill="rgba(255,255,255,0.07)"/>
<rect x="266" y="235" width="106" height="16" rx="2" fill="#2563EB"/>
<rect x="266" y="235" width="106" height="5" rx="2" fill="rgba(255,255,255,0.07)"/>
<rect x="376" y="235" width="88" height="16" rx="2" fill="#1E40AF"/>
<rect x="376" y="235" width="88" height="5" rx="2" fill="rgba(255,255,255,0.07)"/>
<!-- Track 2: AUDIO/MUSIC clips -->
<rect x="104" y="252" width="404" height="18" fill="#141c12"/>
<rect x="104" y="253" width="178" height="16" rx="2" fill="#15803D"/>
<rect x="104" y="253" width="178" height="5" rx="2" fill="rgba(255,255,255,0.06)"/>
<!-- Inline waveform bars -->
<rect x="108" y="259" width="2" height="5" fill="rgba(255,255,255,0.22)"/>
<rect x="112" y="257" width="2" height="8" fill="rgba(255,255,255,0.22)"/>
<rect x="116" y="260" width="2" height="4" fill="rgba(255,255,255,0.22)"/>
<rect x="120" y="257" width="2" height="7" fill="rgba(255,255,255,0.22)"/>
<rect x="124" y="260" width="2" height="5" fill="rgba(255,255,255,0.22)"/>
<rect x="128" y="257" width="2" height="7" fill="rgba(255,255,255,0.22)"/>
<rect x="132" y="259" width="2" height="5" fill="rgba(255,255,255,0.22)"/>
<rect x="136" y="257" width="2" height="8" fill="rgba(255,255,255,0.22)"/>
<rect x="140" y="260" width="2" height="4" fill="rgba(255,255,255,0.22)"/>
<rect x="144" y="258" width="2" height="7" fill="rgba(255,255,255,0.22)"/>
<rect x="148" y="261" width="2" height="4" fill="rgba(255,255,255,0.22)"/>
<rect x="152" y="258" width="2" height="6" fill="rgba(255,255,255,0.22)"/>
<rect x="156" y="259" width="2" height="5" fill="rgba(255,255,255,0.22)"/>
<rect x="160" y="257" width="2" height="7" fill="rgba(255,255,255,0.22)"/>
<rect x="164" y="259" width="2" height="5" fill="rgba(255,255,255,0.22)"/>
<rect x="168" y="257" width="2" height="8" fill="rgba(255,255,255,0.22)"/>
<rect x="172" y="261" width="2" height="4" fill="rgba(255,255,255,0.22)"/>
<rect x="176" y="259" width="2" height="5" fill="rgba(255,255,255,0.22)"/>
<rect x="286" y="253" width="148" height="16" rx="2" fill="#166534"/>
<rect x="286" y="253" width="148" height="5" rx="2" fill="rgba(255,255,255,0.06)"/>
<rect x="438" y="253" width="66" height="16" rx="2" fill="#15803D"/>
<rect x="438" y="253" width="66" height="5" rx="2" fill="rgba(255,255,255,0.06)"/>
<!-- Track 3: GFX / TEXT clips -->
<rect x="104" y="270" width="404" height="18" fill="#181710"/>
<rect x="120" y="271" width="60" height="16" rx="2" fill="#D97706"/>
<rect x="120" y="271" width="60" height="5" rx="2" fill="rgba(255,255,255,0.07)"/>
<rect x="226" y="271" width="52" height="16" rx="2" fill="#B45309"/>
<rect x="226" y="271" width="52" height="5" rx="2" fill="rgba(255,255,255,0.07)"/>
<rect x="334" y="271" width="78" height="16" rx="2" fill="#D97706"/>
<rect x="334" y="271" width="78" height="5" rx="2" fill="rgba(255,255,255,0.07)"/>
<rect x="452" y="271" width="50" height="16" rx="2" fill="#B45309"/>
<rect x="452" y="271" width="50" height="5" rx="2" fill="rgba(255,255,255,0.07)"/>
<!-- Animated playhead -->
<polygon id="ve-playhead-head" points="0,-8 -6,-2 6,-2" transform="translate(104,234)" fill="#FF4500"/>
<rect id="ve-playhead-line" x="103" y="220" width="2" height="68" fill="#FF4500" opacity="0.92"/>
<!-- Timecode display -->
<text id="ve-timecode" x="36" y="230" fill="#8a8e98" font-size="8" font-family="monospace">0:00</text>
</svg></div>
HTML;
}
function oribi_render_platform_row($a)
{
$rev = !empty($a['reversed']) ? ' reverse' : '';
$img_id = !empty($a['imgId']) ? intval($a['imgId']) : 0;
$img_url = !empty($a['imgUrl']) ? $a['imgUrl'] : '';
$img_alt = !empty($a['imgAlt']) ? $a['imgAlt'] : '';
$img_w = !empty($a['imgWidth']) ? intval($a['imgWidth']) : 300;
// Only render animated dashboard when explicitly flagged
$is_dashboard = !empty($a['isDashboard']);
if ($is_dashboard) {
// Render animated dashboard chart SVG
// Text uses class hooks: .ct = title, .cl = label, .cv = value
// JS will dynamically set fill colours based on data-theme
$visual_html = '<div class="dashboard-tv" data-dashboard-container="true">
<div class="dashboard-tv__body">
<div class="dashboard-tv__screen">
<svg viewBox="0 0 800 450" xmlns="http://www.w3.org/2000/svg" class="dashboard-chart" role="img" aria-label="Animated dashboard charts">
<defs>
<linearGradient id="barGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#D83302" stop-opacity="1"/>
<stop offset="100%" stop-color="#4CAF50" stop-opacity=".8"/>
</linearGradient>
<linearGradient id="lineGradient" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" stop-color="#4CAF50" stop-opacity=".3"/>
<stop offset="100%" stop-color="#4CAF50" stop-opacity="0"/>
</linearGradient>
</defs>
<!-- ── Top-left: Performance bars ── -->
<g transform="translate(35,20)">
<text class="ct" x="0" y="0" font-size="14" font-weight="600" fill="#333">Performance</text>
<g id="bars-group-1" transform="translate(0,25)">
<rect class="bar" x="0" y="120" width="28" height="0" fill="url(#barGradient)"/>
<rect class="bar" x="40" y="120" width="28" height="0" fill="url(#barGradient)"/>
<rect class="bar" x="80" y="120" width="28" height="0" fill="url(#barGradient)"/>
<rect class="bar" x="120" y="120" width="28" height="0" fill="url(#barGradient)"/>
<rect class="bar" x="160" y="120" width="28" height="0" fill="url(#barGradient)"/>
</g>
<g transform="translate(0,152)">
<text class="cl" x="14" y="0" font-size="10" text-anchor="middle" fill="#666">API</text>
<text class="cl" x="54" y="0" font-size="10" text-anchor="middle" fill="#666">Cache</text>
<text class="cl" x="94" y="0" font-size="10" text-anchor="middle" fill="#666">DB</text>
<text class="cl" x="134" y="0" font-size="10" text-anchor="middle" fill="#666">Queue</text>
<text class="cl" x="174" y="0" font-size="10" text-anchor="middle" fill="#666">Worker</text>
</g>
<g id="values-group-1" transform="translate(0,168)">
<text class="cv" x="14" y="0" font-size="11" font-weight="600" text-anchor="middle" fill="#333">0%</text>
<text class="cv" x="54" y="0" font-size="11" font-weight="600" text-anchor="middle" fill="#333">0%</text>
<text class="cv" x="94" y="0" font-size="11" font-weight="600" text-anchor="middle" fill="#333">0%</text>
<text class="cv" x="134" y="0" font-size="11" font-weight="600" text-anchor="middle" fill="#333">0%</text>
<text class="cv" x="174" y="0" font-size="11" font-weight="600" text-anchor="middle" fill="#333">0%</text>
</g>
</g>
<!-- ── Top-right: Requests/sec bars ── -->
<g transform="translate(430,20)">
<text class="ct" x="0" y="0" font-size="14" font-weight="600" fill="#333">Requests/sec</text>
<g id="bars-group-2" transform="translate(0,25)">
<rect class="bar" x="0" y="120" width="28" height="0" fill="url(#barGradient)"/>
<rect class="bar" x="40" y="120" width="28" height="0" fill="url(#barGradient)"/>
<rect class="bar" x="80" y="120" width="28" height="0" fill="url(#barGradient)"/>
<rect class="bar" x="120" y="120" width="28" height="0" fill="url(#barGradient)"/>
</g>
<g transform="translate(0,152)">
<text class="cl" x="14" y="0" font-size="10" text-anchor="middle" fill="#666">Read</text>
<text class="cl" x="54" y="0" font-size="10" text-anchor="middle" fill="#666">Write</text>
<text class="cl" x="94" y="0" font-size="10" text-anchor="middle" fill="#666">Update</text>
<text class="cl" x="134" y="0" font-size="10" text-anchor="middle" fill="#666">Delete</text>
</g>
<g id="values-group-2" transform="translate(0,168)">
<text class="cv" x="14" y="0" font-size="11" font-weight="600" text-anchor="middle" fill="#333">0</text>
<text class="cv" x="54" y="0" font-size="11" font-weight="600" text-anchor="middle" fill="#333">0</text>
<text class="cv" x="94" y="0" font-size="11" font-weight="600" text-anchor="middle" fill="#333">0</text>
<text class="cv" x="134" y="0" font-size="11" font-weight="600" text-anchor="middle" fill="#333">0</text>
</g>
</g>
<!-- ── Bottom-left: Traffic Trend line ── -->
<g id="line-graph" transform="translate(35,245)">
<text class="ct" x="0" y="0" font-size="14" font-weight="600" fill="#333">Traffic Trend</text>
<g transform="translate(0,25)">
<line class="grid-line" x1="0" y1="0" x2="340" y2="0" stroke="#E0E0E0" stroke-width=".5"/>
<line class="grid-line" x1="0" y1="40" x2="340" y2="40" stroke="#E0E0E0" stroke-width=".5"/>
<line class="grid-line" x1="0" y1="80" x2="340" y2="80" stroke="#E0E0E0" stroke-width=".5"/>
<line class="grid-line" x1="0" y1="120" x2="340" y2="120" stroke="#E0E0E0" stroke-width=".5"/>
</g>
<g transform="translate(0,25)">
<path id="line-fill" d="M0,80 L340,80 L340,145 L0,145 Z" fill="url(#lineGradient)"/>
<path id="line-path" d="M0,80 L340,80" stroke="#4CAF50" stroke-width="2.5" fill="none" stroke-linecap="round"/>
</g>
</g>
<!-- ── Bottom-right: Distribution pie ── -->
<g transform="translate(490,245)">
<text class="ct" x="100" y="0" font-size="14" font-weight="600" text-anchor="middle" fill="#333">Distribution</text>
<g transform="translate(100,90)">
<g class="pie-segment" transform="rotate(0)">
<path d="M0,0 L0,-55 A55,55 0 0,1 38.89,-38.89 Z" fill="#D83302" opacity=".9"/>
</g>
<g class="pie-segment" transform="rotate(90)">
<path d="M0,0 L0,-55 A55,55 0 0,1 38.89,-38.89 Z" fill="#4CAF50" opacity=".8"/>
</g>
<g class="pie-segment" transform="rotate(180)">
<path d="M0,0 L0,-55 A55,55 0 0,1 38.89,-38.89 Z" fill="#f59e0b" opacity=".7"/>
</g>
<g class="pie-segment" transform="rotate(270)">
<path d="M0,0 L0,-55 A55,55 0 0,1 38.89,-38.89 Z" fill="#ef4444" opacity=".7"/>
</g>
<circle id="pie-center" cx="0" cy="0" r="22" fill="#fff" stroke="#E0E0E0" stroke-width="1"/>
<text id="pie-center-text" x="0" y="5" text-anchor="middle" font-size="13" font-weight="600" fill="#333">100%</text>
</g>
<g transform="translate(30,170)">
<rect x="0" y="0" width="8" height="8" fill="#D83302"/>
<text class="cl" x="12" y="8" font-size="11" fill="#666">Service A</text>
<rect x="0" y="15" width="8" height="8" fill="#4CAF50"/>
<text class="cl" x="12" y="23" font-size="11" fill="#666">Service B</text>
<rect x="100" y="0" width="8" height="8" fill="#f59e0b"/>
<text class="cl" x="112" y="8" font-size="11" fill="#666">Service C</text>
<rect x="100" y="15" width="8" height="8" fill="#ef4444"/>
<text class="cl" x="112" y="23" font-size="11" fill="#666">Service D</text>
</g>
</g>
</svg>
</div></div>
<div class="dashboard-tv__feet"><div class="dashboard-tv__foot"></div><div class="dashboard-tv__foot"></div></div>
</div>';
$visual_cls = 'platform-visual has-dashboard';
}
elseif ($img_url) {
$img_style = 'width:' . $img_w . 'px;max-width:100%;height:auto;border-radius:var(--radius-sm);object-fit:contain;display:block;margin-inline:auto;';
if ($img_id) {
$visual_html = wp_get_attachment_image($img_id, 'full', false, ['style' => $img_style, 'alt' => $img_alt]);
}
else {
$visual_html = '<img src="' . esc_url($img_url) . '" alt="' . esc_attr($img_alt) . '" style="' . esc_attr($img_style) . '">';
}
$visual_cls = 'platform-visual has-img';
}
elseif (!empty($a['deviceAnim'])) {
$da_screen = '<div class="da-screen"><div class="da-promo"><div class="da-promo__top"><span class="da-promo__dot"></span><span class="da-promo__brand"></span></div><div class="da-promo__hero"></div><div class="da-promo__row"><span class="da-promo__line da-promo__line--lg"></span><span class="da-promo__line da-promo__line--sm"></span></div><div class="da-promo__row"><span class="da-promo__line da-promo__line--md"></span><span class="da-promo__line da-promo__line--xs"></span></div><div class="da-promo__ticker"><span class="da-promo__chip"></span><span class="da-promo__chip"></span><span class="da-promo__chip"></span></div></div></div>';
$da = '<div class="da-stage" aria-hidden="true">';
$da .= '<div class="da-device da-tablet"><div class="da-body">' . $da_screen . '</div><span class="da-label">Tablet</span></div>';
$da .= '<div class="da-device da-monitor-sm"><div class="da-body">' . $da_screen . '</div><div class="da-stand"><div class="da-stem"></div><div class="da-base"></div></div><span class="da-label">Small Monitor</span></div>';
$da .= '<div class="da-device da-monitor-lg"><div class="da-body">' . $da_screen . '</div><div class="da-stand"><div class="da-stem"></div><div class="da-base"></div></div><span class="da-label">Large Monitor</span></div>';
$da .= '<div class="da-device da-tv"><div class="da-body">' . $da_screen . '</div><div class="da-feet"><div class="da-foot"></div><div class="da-foot"></div></div><span class="da-label">TV</span></div>';
$da .= '<div class="da-device da-projector"><div class="da-proj-layout"><div class="da-proj-body"><div class="da-lens"></div></div><div class="da-beam"></div><div class="da-proj-screen">' . $da_screen . '</div></div><span class="da-label">Projector</span></div>';
$da .= '<div class="da-device da-vwall"><div class="da-vwall-grid"><div class="da-panel">' . $da_screen . '</div><div class="da-panel">' . $da_screen . '</div><div class="da-panel">' . $da_screen . '</div><div class="da-panel">' . $da_screen . '</div></div><span class="da-label">Video Wall</span></div>';
$da .= '</div>';
$visual_html = $da;
$visual_cls = 'platform-visual has-anim';
}
elseif (!empty($a['tvStick'])) {
$ts = '<div class="ts-stage" data-tv-stick-anim aria-hidden="true">';
$ts .= '<div class="ts-tv">';
$ts .= '<div class="ts-tv__body">';
$ts .= '<div class="ts-tv__screen">';
$ts .= '<div class="ts-slides">';
// Slide 1 — Menu Board
$ts .= '<div class="ts-slide ts-slide--menu">';
$ts .= '<div class="ts-menu">';
$ts .= '<div class="ts-menu__header"><div class="ts-menu__logo"></div><div class="ts-menu__title"></div></div>';
$ts .= '<div class="ts-menu__cols">';
$ts .= '<div class="ts-menu__col">';
$ts .= '<div class="ts-menu__cat"></div>';
$ts .= '<div class="ts-menu__item"><span class="ts-menu__name"></span><span class="ts-menu__dots"></span><span class="ts-menu__price"></span></div>';
$ts .= '<div class="ts-menu__item"><span class="ts-menu__name"></span><span class="ts-menu__dots"></span><span class="ts-menu__price"></span></div>';
$ts .= '<div class="ts-menu__item"><span class="ts-menu__name"></span><span class="ts-menu__dots"></span><span class="ts-menu__price"></span></div>';
$ts .= '</div>';
$ts .= '<div class="ts-menu__col">';
$ts .= '<div class="ts-menu__cat"></div>';
$ts .= '<div class="ts-menu__item"><span class="ts-menu__name"></span><span class="ts-menu__dots"></span><span class="ts-menu__price"></span></div>';
$ts .= '<div class="ts-menu__item"><span class="ts-menu__name"></span><span class="ts-menu__dots"></span><span class="ts-menu__price"></span></div>';
$ts .= '<div class="ts-menu__item"><span class="ts-menu__name"></span><span class="ts-menu__dots"></span><span class="ts-menu__price"></span></div>';
$ts .= '</div>';
$ts .= '</div>';
$ts .= '</div>';
$ts .= '</div>';
// Slide 2 — Wayfinding Sign
$ts .= '<div class="ts-slide ts-slide--wayfind">';
$ts .= '<div class="ts-wf">';
$ts .= '<div class="ts-wf__header"><div class="ts-wf__building"></div></div>';
$ts .= '<div class="ts-wf__rows">';
$ts .= '<div class="ts-wf__row"><span class="ts-wf__arrow ts-wf__arrow--left"></span><span class="ts-wf__label ts-wf__label--w1"></span><span class="ts-wf__floor"></span></div>';
$ts .= '<div class="ts-wf__row"><span class="ts-wf__arrow ts-wf__arrow--right"></span><span class="ts-wf__label ts-wf__label--w2"></span><span class="ts-wf__floor"></span></div>';
$ts .= '<div class="ts-wf__row"><span class="ts-wf__arrow ts-wf__arrow--up"></span><span class="ts-wf__label ts-wf__label--w3"></span><span class="ts-wf__floor"></span></div>';
$ts .= '<div class="ts-wf__row"><span class="ts-wf__arrow ts-wf__arrow--left"></span><span class="ts-wf__label ts-wf__label--w4"></span><span class="ts-wf__floor"></span></div>';
$ts .= '</div>';
$ts .= '</div>';
$ts .= '</div>';
// Slide 3 — Schedule / Timetable
$ts .= '<div class="ts-slide ts-slide--sched">';
$ts .= '<div class="ts-sched">';
$ts .= '<div class="ts-sched__header"><div class="ts-sched__title"></div><div class="ts-sched__date"></div></div>';
$ts .= '<div class="ts-sched__table">';
$ts .= '<div class="ts-sched__hrow"><span class="ts-sched__hcell"></span><span class="ts-sched__hcell"></span><span class="ts-sched__hcell"></span><span class="ts-sched__hcell"></span></div>';
$ts .= '<div class="ts-sched__row"><span class="ts-sched__time"></span><span class="ts-sched__event ts-sched__event--a"></span><span class="ts-sched__cell"></span><span class="ts-sched__cell"></span></div>';
$ts .= '<div class="ts-sched__row"><span class="ts-sched__time"></span><span class="ts-sched__cell"></span><span class="ts-sched__event ts-sched__event--b"></span><span class="ts-sched__cell"></span></div>';
$ts .= '<div class="ts-sched__row"><span class="ts-sched__time"></span><span class="ts-sched__cell"></span><span class="ts-sched__cell"></span><span class="ts-sched__event ts-sched__event--c"></span></div>';
$ts .= '<div class="ts-sched__row"><span class="ts-sched__time"></span><span class="ts-sched__event ts-sched__event--a"></span><span class="ts-sched__event ts-sched__event--b"></span><span class="ts-sched__cell"></span></div>';
$ts .= '</div>';
$ts .= '</div>';
$ts .= '</div>';
$ts .= '</div>'; // ts-slides
$ts .= '</div>'; // ts-tv__screen
$ts .= '<div class="ts-tv__port"></div>';
$ts .= '</div>';
$ts .= '<div class="ts-tv__feet"><div class="ts-tv__foot"></div><div class="ts-tv__foot"></div></div>';
$ts .= '</div>';
$ts .= '<div class="ts-stick">';
$ts .= '<div class="ts-stick__body">';
$ts .= '<div class="ts-stick__led"></div>';
$ts .= '</div>';
$ts .= '<div class="ts-stick__connector"></div>';
$ts .= '</div>';
$ts .= '</div>';
$visual_html = $ts;
$visual_cls = 'platform-visual has-tv-stick';
}
elseif (!empty($a['neverGoesDark'])) {
/* ── Never Goes Dark: player + TV + cloud connection/break animation ── */
$ngd = '<div class="ngd-stage" aria-hidden="true">';
/* TV */
$ngd .= '<div class="ngd-tv">';
$ngd .= '<div class="ngd-tv__body">';
$ngd .= '<div class="ngd-tv__screen">';
$ngd .= '<div class="ngd-menu">';
$ngd .= '<div class="ngd-menu__hd">';
$ngd .= '<div class="ngd-menu__logo"></div>';
$ngd .= '<div class="ngd-menu__ttl"></div>';
$ngd .= '</div>';
$ngd .= '<div class="ngd-menu__cols">';
$ngd .= '<div class="ngd-menu__col">';
$ngd .= '<div class="ngd-menu__cat"></div>';
$ngd .= '<div class="ngd-menu__row"><span class="ngd-menu__name"></span><span class="ngd-menu__price"></span></div>';
$ngd .= '<div class="ngd-menu__row ngd-menu__row--hl"><span class="ngd-menu__name"></span><span class="ngd-menu__price"></span></div>';
$ngd .= '<div class="ngd-menu__row"><span class="ngd-menu__name"></span><span class="ngd-menu__price"></span></div>';
$ngd .= '</div>';
$ngd .= '<div class="ngd-menu__col">';
$ngd .= '<div class="ngd-menu__cat"></div>';
$ngd .= '<div class="ngd-menu__row"><span class="ngd-menu__name"></span><span class="ngd-menu__price"></span></div>';
$ngd .= '<div class="ngd-menu__row"><span class="ngd-menu__name"></span><span class="ngd-menu__price"></span></div>';
$ngd .= '<div class="ngd-menu__row ngd-menu__row--hl"><span class="ngd-menu__name"></span><span class="ngd-menu__price"></span></div>';
$ngd .= '</div>';
$ngd .= '</div>'; /* cols */
$ngd .= '<div class="ngd-menu__ticker"><div class="ngd-menu__ticker-inner"></div></div>';
$ngd .= '</div>'; /* ngd-menu */
$ngd .= '</div>'; /* ngd-tv__screen */
$ngd .= '<div class="ngd-tv__port"></div>';
$ngd .= '</div>'; /* ngd-tv__body */
$ngd .= '<div class="ngd-tv__feet"><div class="ngd-tv__foot"></div><div class="ngd-tv__foot"></div></div>';
$ngd .= '</div>'; /* ngd-tv */
/* Player device (stick style, plugged into TV right-side HDMI port) */
$ngd .= '<div class="ngd-player">';
$ngd .= '<div class="ngd-player__connector"></div>';
$ngd .= '<div class="ngd-player__body">';
$ngd .= '<div class="ngd-player__led"></div>';
$ngd .= '</div>';
$ngd .= '<div class="ngd-player__check">';
$ngd .= '<svg viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">';
$ngd .= '<circle cx="11" cy="11" r="10" stroke="#4CAF50" stroke-width="1.5"/>';
$ngd .= '<polyline points="6,11 9.5,14.5 16,7.5" stroke="#4CAF50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>';
$ngd .= '</svg>';
$ngd .= '</div>';
$ngd .= '</div>'; /* ngd-player */
/* Signal wrap: vertical line connecting player to globe above */
$ngd .= '<div class="ngd-signal-wrap">';
$ngd .= '<div class="ngd-cloud">';
$ngd .= '<svg class="ngd-cloud__svg" viewBox="0 0 64 46" fill="none" xmlns="http://www.w3.org/2000/svg">';
$ngd .= '<circle class="ngd-cloud__path" cx="32" cy="23" r="14" stroke-width="2.2" fill="none"/>';
$ngd .= '<path class="ngd-cloud__path" d="M18.5 23C23 20 41 20 45.5 23" stroke-width="1.9" stroke-linecap="round" fill="none"/>';
$ngd .= '<path class="ngd-cloud__path" d="M20.5 29C25.5 31 38.5 31 43.5 29" stroke-width="1.9" stroke-linecap="round" fill="none"/>';
$ngd .= '<path class="ngd-cloud__path" d="M24.5 12C21.5 16.5 21.5 29.5 24.5 34" stroke-width="1.9" stroke-linecap="round" fill="none"/>';
$ngd .= '<path class="ngd-cloud__path" d="M39.5 12C42.5 16.5 42.5 29.5 39.5 34" stroke-width="1.9" stroke-linecap="round" fill="none"/>';
$ngd .= '</svg>';
$ngd .= '</div>';
$ngd .= '<div class="ngd-signal-line">';
$ngd .= '<div class="ngd-signal__dots">';
$ngd .= '<div class="ngd-signal__dot ngd-signal__dot--1"></div>';
$ngd .= '<div class="ngd-signal__dot ngd-signal__dot--2"></div>';
$ngd .= '<div class="ngd-signal__dot ngd-signal__dot--3"></div>';
$ngd .= '</div>';
$ngd .= '<div class="ngd-signal__break">';
$ngd .= '<svg viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">';
$ngd .= '<circle cx="9" cy="9" r="8" fill="rgba(239,68,68,0.12)" stroke="#ef4444" stroke-width="1.2"/>';
$ngd .= '<line x1="5.5" y1="5.5" x2="12.5" y2="12.5" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/>';
$ngd .= '<line x1="12.5" y1="5.5" x2="5.5" y2="12.5" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/>';
$ngd .= '</svg>';
$ngd .= '</div>';
$ngd .= '</div>'; /* ngd-signal-line */
$ngd .= '</div>'; /* ngd-signal-wrap */
$ngd .= '</div>'; /* ngd-stage */
$visual_html = $ngd;
$visual_cls = 'platform-visual has-ngd';
}
elseif (!empty($a['brandedAnim'])) {
/* ── Custom Display Solutions: branded screens cascade animation ── */
$bd = '<div class="bd-stage" aria-hidden="true">';
/* ── Screen 1: Tablet (portrait kiosk) ── */
$bd .= '<div class="bd-device bd-device--tablet">';
$bd .= '<span class="bd-device__label">Kiosk</span>';
$bd .= '<div class="bd-device__body">';
$bd .= '<div class="bd-device__screen">';
$bd .= '<div class="bd-splash">';
$bd .= '<div class="bd-splash__logo"></div>';
$bd .= '</div>';
$bd .= '<div class="bd-ui">';
$bd .= '<div class="bd-ui__header"><div class="bd-ui__logo"></div><div class="bd-ui__brand-bar"></div></div>';
$bd .= '<div class="bd-ui__content">';
$bd .= '<div class="bd-promo bd-promo--welcome">';
$bd .= '<div class="bd-promo__hero"></div>';
$bd .= '<div class="bd-promo__heading"></div>';
$bd .= '<div class="bd-promo__text"></div>';
$bd .= '<div class="bd-promo__btn"></div>';
$bd .= '</div>';
$bd .= '</div>';
$bd .= '</div>';
$bd .= '</div>';
$bd .= '</div>';
$bd .= '</div>';
/* ── Screen 2: Wide wall-mount (landscape) ── */
$bd .= '<div class="bd-device bd-device--wall">';
$bd .= '<span class="bd-device__label">Wall Display</span>';
$bd .= '<div class="bd-device__body">';
$bd .= '<div class="bd-device__screen">';
$bd .= '<div class="bd-splash">';
$bd .= '<div class="bd-splash__logo"></div>';
$bd .= '</div>';
$bd .= '<div class="bd-ui">';
$bd .= '<div class="bd-ui__header"><div class="bd-ui__logo"></div><div class="bd-ui__brand-bar"></div></div>';
$bd .= '<div class="bd-ui__content">';
$bd .= '<div class="bd-promo bd-promo--sale">';
$bd .= '<div class="bd-promo__cols">';
$bd .= '<div class="bd-promo__visual"></div>';
$bd .= '<div class="bd-promo__info">';
$bd .= '<div class="bd-promo__badge"></div>';
$bd .= '<div class="bd-promo__heading"></div>';
$bd .= '<div class="bd-promo__text"></div>';
$bd .= '<div class="bd-promo__text bd-promo__text--short"></div>';
$bd .= '<div class="bd-promo__price"></div>';
$bd .= '</div>';
$bd .= '</div>';
$bd .= '</div>';
$bd .= '</div>';
$bd .= '</div>';
$bd .= '</div>';
$bd .= '</div>';
$bd .= '<div class="bd-mount"></div>';
$bd .= '</div>';
/* ── Screen 3: Interactive tablet on table ── */
$bd .= '<div class="bd-device bd-device--interactive">';
$bd .= '<span class="bd-device__label">Interactive</span>';
$bd .= '<div class="bd-device__body">';
$bd .= '<div class="bd-device__screen">';
$bd .= '<div class="bd-splash">';
$bd .= '<div class="bd-splash__logo"></div>';
$bd .= '</div>';
$bd .= '<div class="bd-ui">';
$bd .= '<div class="bd-ui__header"><div class="bd-ui__logo"></div><div class="bd-ui__brand-bar"></div></div>';
$bd .= '<div class="bd-ui__content">';
$bd .= '<div class="bd-promo bd-promo--menu">';
$bd .= '<div class="bd-promo__heading"></div>';
$bd .= '<div class="bd-promo__grid">';
$bd .= '<div class="bd-promo__tile"><div class="bd-promo__tile-img"></div><div class="bd-promo__tile-lbl"></div></div>';
$bd .= '<div class="bd-promo__tile"><div class="bd-promo__tile-img"></div><div class="bd-promo__tile-lbl"></div></div>';
$bd .= '<div class="bd-promo__tile"><div class="bd-promo__tile-img"></div><div class="bd-promo__tile-lbl"></div></div>';
$bd .= '<div class="bd-promo__tile"><div class="bd-promo__tile-img"></div><div class="bd-promo__tile-lbl"></div></div>';
$bd .= '</div>';
$bd .= '</div>';
$bd .= '</div>';
$bd .= '</div>';
$bd .= '</div>';
$bd .= '</div>';
$bd .= '<div class="bd-table"></div>';
$bd .= '</div>';
$bd .= '</div>'; /* bd-stage */
$visual_html = $bd;
$visual_cls = 'platform-visual has-branded';
}
elseif (!empty($a['retailAnim'])) {
/* ── Retail Sign: TV cycling 3 promo slides ── */
$ra = '<div class="retail-stage" aria-hidden="true">';
$ra .= '<div class="retail-tv">';
$ra .= '<div class="retail-tv__body">';
$ra .= '<div class="retail-tv__screen">';
$ra .= '<div class="retail-slides">';
// Slide 1: Flash Sale
$ra .= '<div class="retail-slide retail-slide--sale">';
$ra .= '<div class="retail-promo">';
$ra .= '<div class="retail-promo__eyebrow">Today Only</div>';
$ra .= '<div class="retail-promo__headline">Flash Sale</div>';
$ra .= '<div class="retail-promo__badge">Up to 40% Off</div>';
$ra .= '<div class="retail-promo__items">';
$ra .= '<div class="retail-promo__item"><span class="retail-promo__dot"></span><span class="retail-promo__lbl">Select Apparel</span></div>';
$ra .= '<div class="retail-promo__item"><span class="retail-promo__dot"></span><span class="retail-promo__lbl">Footwear Range</span></div>';
$ra .= '<div class="retail-promo__item"><span class="retail-promo__dot"></span><span class="retail-promo__lbl">Accessories</span></div>';
$ra .= '</div>';
$ra .= '<div class="retail-promo__cta">In-Store Only</div>';
$ra .= '</div>';
$ra .= '</div>';
// Slide 2: New Arrivals
$ra .= '<div class="retail-slide retail-slide--new">';
$ra .= '<div class="retail-promo">';
$ra .= '<div class="retail-promo__eyebrow">Just Landed</div>';
$ra .= '<div class="retail-promo__headline">New In Store</div>';
$ra .= '<div class="retail-promo__grid">';
$ra .= '<div class="retail-promo__swatch retail-promo__swatch--a"></div>';
$ra .= '<div class="retail-promo__swatch retail-promo__swatch--b"></div>';
$ra .= '<div class="retail-promo__swatch retail-promo__swatch--c"></div>';
$ra .= '<div class="retail-promo__swatch retail-promo__swatch--d"></div>';
$ra .= '</div>';
$ra .= '<div class="retail-promo__sub">Spring/Summer Collection</div>';
$ra .= '</div>';
$ra .= '</div>';
// Slide 3: Loyalty Rewards
$ra .= '<div class="retail-slide retail-slide--loyalty">';
$ra .= '<div class="retail-promo">';
$ra .= '<div class="retail-promo__eyebrow">Member Exclusive</div>';
$ra .= '<div class="retail-promo__headline">Earn Points</div>';
$ra .= '<div class="retail-promo__points">';
$ra .= '<div class="retail-promo__pts-val">2x</div>';
$ra .= '<div class="retail-promo__pts-lbl">Points This Weekend</div>';
$ra .= '</div>';
$ra .= '<div class="retail-promo__bar"><div class="retail-promo__bar-fill"></div></div>';
$ra .= '<div class="retail-promo__sub">Scan your card at checkout</div>';
$ra .= '</div>';
$ra .= '</div>';
$ra .= '</div>'; // retail-slides
$ra .= '</div>'; // retail-tv__screen
$ra .= '</div>'; // retail-tv__body
$ra .= '<div class="retail-tv__feet"><div class="retail-tv__foot"></div><div class="retail-tv__foot"></div></div>';
$ra .= '</div>'; // retail-tv
$ra .= '</div>'; // retail-stage
$visual_html = $ra;
$visual_cls = 'platform-visual has-retail';
}
elseif (!empty($a['corporateAnim'])) {
/* ── Corporate: Meeting room door panel ── */
$ca = '<div class="corp-stage" aria-hidden="true">';
$ca .= '<div class="corp-panel">';
$ca .= '<div class="corp-panel__header">';
$ca .= '<div class="corp-panel__room">Boardroom A</div>';
$ca .= '<div class="corp-panel__status corp-panel__status--busy"><span class="corp-panel__dot"></span>In Use</div>';
$ca .= '</div>';
$ca .= '<div class="corp-panel__meeting">';
$ca .= '<div class="corp-panel__meeting-name">Q2 Strategy Review</div>';
$ca .= '<div class="corp-panel__meeting-time">10:00 11:30</div>';
$ca .= '<div class="corp-panel__organiser">Sarah Mitchell</div>';
$ca .= '</div>';
$ca .= '<div class="corp-panel__timeline">';
$ca .= '<div class="corp-panel__tl-track"><div class="corp-panel__tl-fill"></div><div class="corp-panel__tl-now"></div></div>';
$ca .= '<div class="corp-panel__tl-labels"><span>09:00</span><span>12:00</span><span>17:00</span></div>';
$ca .= '</div>';
$ca .= '<div class="corp-panel__next">';
$ca .= '<div class="corp-panel__next-lbl">Next</div>';
$ca .= '<div class="corp-panel__next-name">Design Sprint Planning</div>';
$ca .= '<div class="corp-panel__next-time">13:00 14:00</div>';
$ca .= '</div>';
$ca .= '<div class="corp-panel__teams"><div class="corp-panel__teams-icon"></div><span>Teams Meeting Active</span></div>';
$ca .= '</div>'; // corp-panel
$ca .= '</div>'; // corp-stage
$visual_html = $ca;
$visual_cls = 'platform-visual has-corporate';
}
elseif (!empty($a['educationAnim'])) {
/* ── Education: Campus schedule board ── */
$ea = '<div class="edu-stage" aria-hidden="true">';
$ea .= '<div class="edu-board">';
$ea .= '<div class="edu-board__header">';
$ea .= '<div class="edu-board__title">Today\'s Timetable</div>';
$ea .= '<div class="edu-board__date">Monday, 16 Mar</div>';
$ea .= '</div>';
$ea .= '<div class="edu-board__rows">';
$rows = [
['time' => '09:00', 'subject' => 'Advanced Mathematics', 'room' => 'B204', 'state' => 'done'],
['time' => '10:30', 'subject' => 'Physics Lab', 'room' => 'Lab 3', 'state' => 'now'],
['time' => '13:00', 'subject' => 'English Literature', 'room' => 'A101', 'state' => ''],
['time' => '14:30', 'subject' => 'Computer Science', 'room' => 'IT Suite', 'state' => ''],
['time' => '16:00', 'subject' => 'Art & Design', 'room' => 'Studio 1', 'state' => ''],
];
foreach ($rows as $row) {
$state_cls = $row['state'] ? ' edu-row--' . $row['state'] : '';
$ea .= '<div class="edu-board__row' . $state_cls . '">';
$ea .= '<span class="edu-row__time">' . esc_html($row['time']) . '</span>';
$ea .= '<span class="edu-row__subject">' . esc_html($row['subject']) . '</span>';
$ea .= '<span class="edu-row__room">' . esc_html($row['room']) . '</span>';
if ($row['state'] === 'now') {
$ea .= '<span class="edu-row__badge">Now</span>';
}
$ea .= '</div>';
}
$ea .= '</div>'; // edu-board__rows
$ea .= '<div class="edu-board__alert">';
$ea .= '<span class="edu-alert__icon">!</span>';
$ea .= '<span class="edu-alert__txt">Fire drill scheduled — 15:45 today</span>';
$ea .= '</div>';
$ea .= '</div>'; // edu-board
$ea .= '</div>'; // edu-stage
$visual_html = $ea;
$visual_cls = 'platform-visual has-education';
}
elseif (!empty($a['outdoorAnim'])) {
/* ── Outdoor Marketplace: wide board cycling 2 slides ── */
$oa = '<div class="outdoor-stage" aria-hidden="true">';
$oa .= '<div class="outdoor-board">';
$oa .= '<div class="outdoor-board__screen">';
$oa .= '<div class="outdoor-slides">';
// Slide 1: Market Info
$oa .= '<div class="outdoor-slide outdoor-slide--info">';
$oa .= '<div class="outdoor-info">';
$oa .= '<div class="outdoor-info__header">';
$oa .= '<div class="outdoor-info__name">Riverside Market</div>';
$oa .= '<div class="outdoor-info__weather"><span class="outdoor-info__temp">18°</span><span class="outdoor-info__cond">Mostly Sunny</span></div>';
$oa .= '</div>';
$oa .= '<div class="outdoor-info__details">';
$oa .= '<div class="outdoor-info__row"><span class="outdoor-info__icon outdoor-info__icon--clock"></span><span>Open 8am 3pm</span></div>';
$oa .= '<div class="outdoor-info__row"><span class="outdoor-info__icon outdoor-info__icon--pin"></span><span>Victoria Embankment</span></div>';
$oa .= '<div class="outdoor-info__row"><span class="outdoor-info__icon outdoor-info__icon--stall"></span><span>42 Stallholders Today</span></div>';
$oa .= '</div>';
$oa .= '</div>';
$oa .= '</div>';
// Slide 2: Stall Directory
$oa .= '<div class="outdoor-slide outdoor-slide--directory">';
$oa .= '<div class="outdoor-dir">';
$oa .= '<div class="outdoor-dir__title">Stall Directory</div>';
$oa .= '<div class="outdoor-dir__grid">';
$stalls = [['A1A5', 'Produce & Veg'], ['B1B4', 'Artisan Bakery'], ['C1C6', 'Street Food'], ['D1D3', 'Crafts & Gifts']];
foreach ($stalls as $s) {
$oa .= '<div class="outdoor-dir__cell"><span class="outdoor-dir__zone">' . esc_html($s[0]) . '</span><span class="outdoor-dir__cat">' . esc_html($s[1]) . '</span></div>';
}
$oa .= '</div>';
$oa .= '</div>';
$oa .= '</div>';
$oa .= '</div>'; // outdoor-slides
$oa .= '</div>'; // outdoor-board__screen
$oa .= '<div class="outdoor-board__bezel"></div>';
$oa .= '</div>'; // outdoor-board
$oa .= '</div>'; // outdoor-stage
$visual_html = $oa;
$visual_cls = 'platform-visual has-outdoor';
}
elseif (!empty($a['liveDataAnim'])) {
/* ── Live Data: Operations centre KPI board ── */
$la = '<div class="ld-stage" aria-hidden="true">';
$la .= '<div class="ld-board">';
$la .= '<div class="ld-board__header">';
$la .= '<div class="ld-board__title">Operations Dashboard</div>';
$la .= '<div class="ld-board__live"><span class="ld-board__live-dot"></span>LIVE</div>';
$la .= '</div>';
$la .= '<div class="ld-kpis">';
$kpis = [
['id' => 'ld-orders', 'label' => 'Orders / hr', 'value' => '1,847', 'trend' => 'up'],
['id' => 'ld-uptime', 'label' => 'Uptime', 'value' => '99.97%', 'trend' => 'up'],
['id' => 'ld-alerts', 'label' => 'Active Alerts', 'value' => '3', 'trend' => 'down'],
['id' => 'ld-latency', 'label' => 'Avg Latency', 'value' => '42ms', 'trend' => 'neutral'],
];
foreach ($kpis as $k) {
$la .= '<div class="ld-kpi ld-kpi--' . esc_attr($k['trend']) . '">';
$la .= '<div class="ld-kpi__label">' . esc_html($k['label']) . '</div>';
$la .= '<div class="ld-kpi__value" id="' . esc_attr($k['id']) . '">' . esc_html($k['value']) . '</div>';
$la .= '<div class="ld-kpi__trend"></div>';
$la .= '</div>';
}
$la .= '</div>'; // ld-kpis
$la .= '<div class="ld-chart">';
$la .= '<svg class="ld-sparkline" viewBox="0 0 260 60" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">';
$la .= '<defs><linearGradient id="ld-grad" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#4CAF50" stop-opacity=".35"/><stop offset="100%" stop-color="#4CAF50" stop-opacity="0"/></linearGradient></defs>';
$la .= '<path id="ld-fill-path" d="M0,40 L0,40 L260,40 Z" fill="url(#ld-grad)"/>';
$la .= '<path id="ld-line-path" d="M0,40 L260,40" stroke="#4CAF50" stroke-width="2" fill="none" stroke-linejoin="round"/>';
$la .= '</svg>';
$la .= '<div class="ld-chart__label">Orders / hr — last 60 min</div>';
$la .= '</div>'; // ld-chart
$la .= '<div class="ld-status">';
$services = [
['API Gateway', 'ok'],
['Payment Proc.', 'ok'],
['Warehouse Feed', 'warn'],
['CDN', 'ok'],
];
foreach ($services as $svc) {
$la .= '<div class="ld-svc ld-svc--' . esc_attr($svc[1]) . '"><span class="ld-svc__dot"></span><span class="ld-svc__name">' . esc_html($svc[0]) . '</span></div>';
}
$la .= '</div>'; // ld-status
$la .= '</div>'; // ld-board
$la .= '</div>'; // ld-stage
$visual_html = $la;
$visual_cls = 'platform-visual has-live-data';
}
elseif (!empty($a['healthcareAnim'])) {
/* ── Healthcare: Queue management display ── */
$hca = '<div class="hc-stage" aria-hidden="true">';
$hca .= '<div class="hc-board">';
$hca .= '<div class="hc-board__header">';
$hca .= '<div class="hc-board__logo"></div>';
$hca .= '<div class="hc-board__title">Patient Queue</div>';
$hca .= '</div>';
$hca .= '<div class="hc-now">';
$hca .= '<div class="hc-now__lbl">Now Serving</div>';
$hca .= '<div class="hc-now__number hc-ticker">A042</div>';
$hca .= '<div class="hc-now__counter">Counter 3 — Dr. Patel</div>';
$hca .= '</div>';
$hca .= '<div class="hc-counters">';
$counters = [
['id' => 'C1', 'doctor' => 'Dr. Patel', 'ticket' => 'A042', 'wait' => '0 min', 'state' => 'active'],
['id' => 'C2', 'doctor' => 'Dr. Okonkwo', 'ticket' => 'B018', 'wait' => '4 min', 'state' => 'active'],
['id' => 'C3', 'doctor' => 'Dr. Williams', 'ticket' => 'A041', 'wait' => '8 min', 'state' => 'active'],
['id' => 'C4', 'doctor' => 'Dr. Nguyen', 'ticket' => '—', 'wait' => 'Closed', 'state' => 'closed'],
];
foreach ($counters as $c) {
$hca .= '<div class="hc-counter hc-counter--' . esc_attr($c['state']) . '">';
$hca .= '<span class="hc-counter__id">' . esc_html($c['id']) . '</span>';
$hca .= '<span class="hc-counter__doctor">' . esc_html($c['doctor']) . '</span>';
$hca .= '<span class="hc-counter__ticket">' . esc_html($c['ticket']) . '</span>';
$hca .= '<span class="hc-counter__wait">' . esc_html($c['wait']) . '</span>';
$hca .= '</div>';
}
$hca .= '</div>'; // hc-counters
$hca .= '<div class="hc-board__footer">Take a seat — your number will be called</div>';
$hca .= '</div>'; // hc-board
$hca .= '</div>'; // hc-stage
$visual_html = $hca;
$visual_cls = 'platform-visual has-healthcare';
}
elseif (!empty($a['transitAnim'])) {
/* ── Transit: Split-flap departure board ── */
$ta = '<div class="transit-stage" aria-hidden="true">';
$ta .= '<div class="transit-board">';
$ta .= '<div class="transit-board__header">';
$ta .= '<div class="transit-board__title">Departures</div>';
$ta .= '<div class="transit-board__clock" id="transit-clock">--:--</div>';
$ta .= '</div>';
$ta .= '<div class="transit-board__cols">';
$ta .= '<span class="transit-col-hd">Time</span><span class="transit-col-hd">Destination</span><span class="transit-col-hd">Platform</span><span class="transit-col-hd">Status</span>';
$ta .= '</div>';
$ta .= '<div class="transit-rows" id="transit-rows">';
$departures = [
['time' => '10:14', 'destination' => 'London Victoria', 'platform' => '2', 'status' => 'On Time', 'status_cls' => 'on-time'],
['time' => '10:22', 'destination' => 'Brighton', 'platform' => '4', 'status' => 'On Time', 'status_cls' => 'on-time'],
['time' => '10:31', 'destination' => 'Gatwick Airport', 'platform' => '1', 'status' => 'Delayed', 'status_cls' => 'delayed'],
['time' => '10:45', 'destination' => 'London Bridge', 'platform' => '3', 'status' => 'On Time', 'status_cls' => 'on-time'],
['time' => '11:02', 'destination' => 'East Croydon', 'platform' => '2', 'status' => 'On Time', 'status_cls' => 'on-time'],
];
foreach ($departures as $d) {
$ta .= '<div class="transit-row">';
$ta .= '<span class="transit-cell transit-cell--time">' . esc_html($d['time']) . '</span>';
$ta .= '<span class="transit-cell transit-cell--dest">';
// Each character gets a flap span for the CSS flip animation
$dest = esc_html($d['destination']);
foreach (str_split($dest) as $ch) {
$ta .= '<span class="transit-flap">' . ($ch === ' ' ? '&nbsp;' : htmlspecialchars($ch)) . '</span>';
}
$ta .= '</span>';
$ta .= '<span class="transit-cell transit-cell--plat">' . esc_html($d['platform']) . '</span>';
$ta .= '<span class="transit-cell transit-cell--status transit-status--' . esc_attr($d['status_cls']) . '">' . esc_html($d['status']) . '</span>';
$ta .= '</div>';
}
$ta .= '</div>'; // transit-rows
$ta .= '</div>'; // transit-board
$ta .= '</div>'; // transit-stage
$visual_html = $ta;
$visual_cls = 'platform-visual has-transit';
}
elseif (!empty($a['fitnessAnim'])) {
/* ── Fitness: Class schedule with live capacity bar ── */
$fa = '<div class="fit-stage" aria-hidden="true">';
$fa .= '<div class="fit-board">';
$fa .= '<div class="fit-board__header">';
$fa .= '<div class="fit-board__logo"></div>';
$fa .= '<div class="fit-board__title">Today\'s Classes</div>';
$fa .= '</div>';
$fa .= '<div class="fit-now">';
$fa .= '<div class="fit-now__badge">LIVE NOW</div>';
$fa .= '<div class="fit-now__name">HIIT Circuit</div>';
$fa .= '<div class="fit-now__detail">Studio 1 &nbsp;·&nbsp; Coach: Emma T &nbsp;·&nbsp; Ends 10:45</div>';
$fa .= '<div class="fit-now__capacity">';
$fa .= '<span class="fit-cap__lbl">Capacity</span>';
$fa .= '<div class="fit-cap__track"><div class="fit-cap__fill"></div></div>';
$fa .= '<span class="fit-cap__val">24/30</span>';
$fa .= '</div>';
$fa .= '</div>';
$fa .= '<div class="fit-upcoming">';
$classes = [
['time' => '11:00', 'name' => 'Yoga Flow', 'coach' => 'Sarah K', 'spaces' => 6],
['time' => '12:15', 'name' => 'Spin & Burn', 'coach' => 'James R', 'spaces' => 2],
['time' => '13:30', 'name' => 'Pilates Core', 'coach' => 'Lisa M', 'spaces' => 12],
];
foreach ($classes as $cls) {
$full_cls = $cls['spaces'] <= 3 ? ' fit-class--filling' : '';
$fa .= '<div class="fit-class' . $full_cls . '">';
$fa .= '<span class="fit-class__time">' . esc_html($cls['time']) . '</span>';
$fa .= '<span class="fit-class__name">' . esc_html($cls['name']) . '</span>';
$fa .= '<span class="fit-class__coach">' . esc_html($cls['coach']) . '</span>';
$fa .= '<span class="fit-class__spaces">' . esc_html($cls['spaces']) . ' spaces</span>';
$fa .= '</div>';
}
$fa .= '</div>'; // fit-upcoming
$fa .= '</div>'; // fit-board
$fa .= '</div>'; // fit-stage
$visual_html = $fa;
$visual_cls = 'platform-visual has-fitness';
}
elseif (!empty($a['lobbyAnim'])) {
/* ── Hotel Lobby Welcome Display ── */
$lb = '<div class="lobby-stage" aria-hidden="true">';
$lb .= '<div class="lobby-screen">';
$lb .= '<div class="lobby-hdr"><span class="lobby-hdr__logo">&#9670; Grand Hotel</span><span class="lobby-hdr__time">3:42 PM</span></div>';
$lb .= '<div class="lobby-slides">';
$lb .= '<div class="lobby-slide lobby-slide--welcome"><div class="lobby-welcome__msg">Welcome</div><div class="lobby-welcome__sub">We hope you enjoy your stay</div></div>';
$lb .= '<div class="lobby-slide lobby-slide--amenities"><div class="lobby-amen__title">Hotel Amenities</div><div class="lobby-amen__grid"><div class="lobby-amen__item">&#9737; Pool</div><div class="lobby-amen__item">&#9883; Spa</div><div class="lobby-amen__item">&#9832; Restaurant</div><div class="lobby-amen__item">&#8982; Gym</div></div></div>';
$lb .= '<div class="lobby-slide lobby-slide--events"><div class="lobby-events__title">Today\'s Events</div><div class="lobby-events__list"><div class="lobby-events__item"><span class="lobby-events__time">10:00</span><span class="lobby-events__name">Yoga on the Terrace</span></div><div class="lobby-events__item"><span class="lobby-events__time">14:00</span><span class="lobby-events__name">Wine Tasting</span></div><div class="lobby-events__item"><span class="lobby-events__time">19:00</span><span class="lobby-events__name">Live Jazz — Lobby Bar</span></div></div></div>';
$lb .= '</div>';
$lb .= '<div class="lobby-wifi">Wi-Fi: <strong>GrandGuest</strong></div>';
$lb .= '</div>';
$lb .= '</div>';
$visual_html = $lb;
$visual_cls = 'platform-visual has-lobby';
}
elseif (!empty($a['conferenceAnim'])) {
/* ── Conference Room Door Panel ── */
$cf = '<div class="conf-stage" aria-hidden="true">';
$cf .= '<div class="conf-panel">';
$cf .= '<div class="conf-panel__room">Boardroom A</div>';
$cf .= '<div class="conf-panel__status"><span class="conf-status__dot"></span>In Use</div>';
$cf .= '<div class="conf-panel__current"><div class="conf-current__label">Current Session</div><div class="conf-current__title">Q1 Strategy Review</div><div class="conf-current__meta">09:00 10:30 &nbsp;·&nbsp; Sarah Mitchell</div></div>';
$cf .= '<div class="conf-panel__timeline">';
$cf .= '<div class="conf-tl__bar"><div class="conf-tl__fill"></div></div>';
$cf .= '</div>';
$cf .= '<div class="conf-panel__next"><span class="conf-next__label">Next:</span> <span class="conf-next__title">Design Sprint Kickoff</span> <span class="conf-next__time">11:00</span></div>';
$cf .= '</div>';
$cf .= '</div>';
$visual_html = $cf;
$visual_cls = 'platform-visual has-conference';
}
elseif (!empty($a['dayPartAnim'])) {
/* ── Day-Part Retail Promo Display (JS-driven clock) ── */
$dp = '<div class="daypart-stage" aria-hidden="true">';
$dp .= '<div class="daypart-screen">';
$dp .= '<div class="daypart-hdr"><span class="daypart-hdr__clock" data-daypart-clock>12:00</span><span class="daypart-hdr__badge" data-daypart-badge>Afternoon</span></div>';
$dp .= '<div class="daypart-slides">';
$dp .= '<div class="daypart-slide daypart-slide--morning"><div class="daypart-promo__tag">Morning</div><div class="daypart-promo__title">Breakfast Deals</div><div class="daypart-promo__sub">Fresh pastries &amp; coffee combos from $4.99</div></div>';
$dp .= '<div class="daypart-slide daypart-slide--afternoon"><div class="daypart-promo__tag">Afternoon</div><div class="daypart-promo__title">Lunch Specials</div><div class="daypart-promo__sub">Meal deals &amp; combo offers until 4 PM</div></div>';
$dp .= '<div class="daypart-slide daypart-slide--evening"><div class="daypart-promo__tag">Evening</div><div class="daypart-promo__title">Happy Hour</div><div class="daypart-promo__sub">2-for-1 drinks &amp; 20% off appetisers</div></div>';
$dp .= '</div>';
$dp .= '</div>';
$dp .= '</div>';
$visual_html = $dp;
$visual_cls = 'platform-visual has-daypart';
}
elseif (!empty($a['wayfindAnim'])) {
/* ── In-Store Wayfinding Kiosk ── */
$wf = '<div class="wayfind-stage" aria-hidden="true">';
$wf .= '<div class="wayfind-kiosk">';
$wf .= '<div class="wayfind-kiosk__search"><span class="wayfind-search__icon">&#128269;</span><span class="wayfind-search__text">Search departments...</span></div>';
$wf .= '<div class="wayfind-kiosk__map">';
$wf .= '<div class="wayfind-zone wayfind-zone--a">Fashion</div>';
$wf .= '<div class="wayfind-zone wayfind-zone--b">Electronics</div>';
$wf .= '<div class="wayfind-zone wayfind-zone--c">Home</div>';
$wf .= '<div class="wayfind-zone wayfind-zone--d">Food Hall</div>';
$wf .= '<div class="wayfind-pin"><span class="wayfind-pin__dot"></span><span class="wayfind-pin__label">You are here</span></div>';
$wf .= '</div>';
$wf .= '<div class="wayfind-kiosk__dir">';
$wf .= '<div class="wayfind-dir__item"><span class="wayfind-dir__arrow">&#8593;</span> Fashion — Level 1</div>';
$wf .= '<div class="wayfind-dir__item"><span class="wayfind-dir__arrow">&#8594;</span> Electronics — Level 2</div>';
$wf .= '<div class="wayfind-dir__item"><span class="wayfind-dir__arrow">&#8595;</span> Food Hall — Ground</div>';
$wf .= '</div>';
$wf .= '</div>';
$wf .= '</div>';
$visual_html = $wf;
$visual_cls = 'platform-visual has-wayfind';
}
elseif (!empty($a['storefrontAnim'])) {
/* ── Window / Storefront Display ── */
$sf = '<div class="store-stage" aria-hidden="true">';
$sf .= '<div class="store-window">';
$sf .= '<div class="store-window__sun"></div>';
$sf .= '<div class="store-window__screen">';
$sf .= '<div class="store-slides">';
$sf .= '<div class="store-slide store-slide--new"><div class="store-slide__tag">New Arrivals</div><div class="store-slide__title">Spring Collection</div><div class="store-slide__cta">Shop Now &#8594;</div></div>';
$sf .= '<div class="store-slide store-slide--sale"><div class="store-slide__tag">Limited Time</div><div class="store-slide__title">Up to 50% Off</div><div class="store-slide__cta">View Offers &#8594;</div></div>';
$sf .= '<div class="store-slide store-slide--hours"><div class="store-slide__tag">Opening Hours</div><div class="store-slide__title">MonSat 9am9pm</div><div class="store-slide__sub">Sunday 10am6pm</div></div>';
$sf .= '</div>';
$sf .= '</div>';
$sf .= '<div class="store-window__glare"></div>';
$sf .= '</div>';
$sf .= '</div>';
$visual_html = $sf;
$visual_cls = 'platform-visual has-storefront';
}
elseif (!empty($a['announcementAnim'])) {
/* ── Corporate Announcement Board ── */
$an = '<div class="announce-stage" aria-hidden="true">';
$an .= '<div class="announce-board">';
$an .= '<div class="announce-hdr"><span class="announce-hdr__title">Company Updates</span><span class="announce-hdr__live">&#9679; Live</span></div>';
$an .= '<div class="announce-cards">';
$an .= '<div class="announce-card announce-card--urgent"><div class="announce-card__badge">Urgent</div><div class="announce-card__title">Fire Drill — 2 PM Today</div><div class="announce-card__body">All staff evacuate via nearest exit. Assembly point: Car Park B.</div></div>';
$an .= '<div class="announce-card announce-card--celebration"><div class="announce-card__badge">Celebration</div><div class="announce-card__title">Welcome New Starters!</div><div class="announce-card__body">Say hello to 5 new team members joining Engineering &amp; Design.</div></div>';
$an .= '<div class="announce-card announce-card--hr"><div class="announce-card__badge">HR Notice</div><div class="announce-card__title">Benefits Enrolment Open</div><div class="announce-card__body">Deadline: March 31. Visit the HR portal to review your options.</div></div>';
$an .= '</div>';
$an .= '</div>';
$an .= '</div>';
$visual_html = $an;
$visual_cls = 'platform-visual has-announcement';
}
elseif (!empty($a['campusWayfindAnim'])) {
/* ── Campus Wayfinding Kiosk ── */
$cw = '<div class="campus-stage" aria-hidden="true">';
$cw .= '<div class="campus-kiosk">';
$cw .= '<div class="campus-kiosk__hdr">Campus Directory</div>';
$cw .= '<div class="campus-kiosk__search"><span class="campus-search__icon">&#128269;</span><span class="campus-search__text">Find a building...</span></div>';
$cw .= '<div class="campus-kiosk__map">';
$cw .= '<div class="campus-bldg campus-bldg--lib">Library</div>';
$cw .= '<div class="campus-bldg campus-bldg--sci">Science</div>';
$cw .= '<div class="campus-bldg campus-bldg--arts">Arts</div>';
$cw .= '<div class="campus-bldg campus-bldg--sports">Sports</div>';
$cw .= '<div class="campus-pin"><span class="campus-pin__dot"></span></div>';
$cw .= '</div>';
$cw .= '<div class="campus-kiosk__legend"><span class="campus-legend__dot campus-legend__dot--you"></span> You are here</div>';
$cw .= '</div>';
$cw .= '</div>';
$visual_html = $cw;
$visual_cls = 'platform-visual has-campus-wayfind';
}
elseif (!empty($a['emergencyAnim'])) {
/* ── Emergency Override Alert ── */
$em = '<div class="emerg-stage" aria-hidden="true">';
$em .= '<div class="emerg-screen">';
$em .= '<div class="emerg-screen__normal">';
$em .= '<div class="emerg-normal__hdr">Campus Notices</div>';
$em .= '<div class="emerg-normal__item">Library open until 10 PM</div>';
$em .= '<div class="emerg-normal__item">Café special: Flat white $3</div>';
$em .= '</div>';
$em .= '<div class="emerg-screen__alert">';
$em .= '<div class="emerg-alert__icon">&#9888;</div>';
$em .= '<div class="emerg-alert__title">EMERGENCY ALERT</div>';
$em .= '<div class="emerg-alert__msg">Building lockdown in effect. Remain indoors. Follow staff instructions.</div>';
$em .= '<div class="emerg-alert__pulse"></div>';
$em .= '</div>';
$em .= '</div>';
$em .= '</div>';
$visual_html = $em;
$visual_cls = 'platform-visual has-emergency';
}
elseif (!empty($a['enclosureAnim'])) {
/* ── Outdoor Weather-Resistant Enclosure ── */
$en = '<div class="encl-stage" aria-hidden="true">';
$en .= '<div class="encl-rain">';
for ($i = 0; $i < 12; $i++) {
$en .= '<div class="encl-rain__drop"></div>';
}
$en .= '</div>';
$en .= '<div class="encl-unit">';
$en .= '<div class="encl-unit__body">';
$en .= '<div class="encl-unit__screen"><div class="encl-screen__content">Your Content Here</div></div>';
$en .= '</div>';
$en .= '<div class="encl-unit__badge">IP65</div>';
$en .= '<div class="encl-unit__temp"><span class="encl-temp__icon">&#127777;</span> <span class="encl-temp__range">-20°C to 50°C</span></div>';
$en .= '</div>';
$en .= '</div>';
$visual_html = $en;
$visual_cls = 'platform-visual has-enclosure';
}
elseif (!empty($a['brightnessAnim'])) {
/* ── High-Brightness Comparison ── */
$br = '<div class="bright-stage" aria-hidden="true">';
$br .= '<div class="bright-sun"></div>';
$br .= '<div class="bright-compare">';
$br .= '<div class="bright-panel bright-panel--indoor"><div class="bright-panel__screen bright-panel__screen--dim"><span class="bright-panel__text">Hello World</span></div><div class="bright-panel__label">Indoor 350 nits</div></div>';
$br .= '<div class="bright-vs">vs</div>';
$br .= '<div class="bright-panel bright-panel--outdoor"><div class="bright-panel__screen bright-panel__screen--vivid"><span class="bright-panel__text">Hello World</span></div><div class="bright-panel__label">Outdoor 2,500+ nits</div></div>';
$br .= '</div>';
$br .= '</div>';
$visual_html = $br;
$visual_cls = 'platform-visual has-brightness';
}
elseif (!empty($a['cellularAnim'])) {
/* ── Cellular Connectivity Tower ── */
$cl = '<div class="cell-stage" aria-hidden="true">';
$cl .= '<div class="cell-tower">';
$cl .= '<div class="cell-tower__mast"></div>';
$cl .= '<div class="cell-tower__base"></div>';
$cl .= '<div class="cell-wave cell-wave--1"></div>';
$cl .= '<div class="cell-wave cell-wave--2"></div>';
$cl .= '<div class="cell-wave cell-wave--3"></div>';
$cl .= '</div>';
$cl .= '<div class="cell-badge">4G / 5G</div>';
$cl .= '<div class="cell-signal"><div class="cell-signal__bar cell-signal__bar--1"></div><div class="cell-signal__bar cell-signal__bar--2"></div><div class="cell-signal__bar cell-signal__bar--3"></div><div class="cell-signal__bar cell-signal__bar--4"></div></div>';
$cl .= '<div class="cell-status">Connected &nbsp;&#9679;</div>';
$cl .= '</div>';
$visual_html = $cl;
$visual_cls = 'platform-visual has-cellular';
}
elseif (!empty($a['designerAnim'])) {
/* ── Drag-and-Drop Editor Canvas ── */
$ds = '<div class="designer-stage" aria-hidden="true">';
$ds .= '<div class="designer-editor">';
$ds .= '<div class="designer-toolbar"><span class="designer-tool">T</span><span class="designer-tool">&#9633;</span><span class="designer-tool">&#9654;</span><span class="designer-tool">&#8943;</span></div>';
$ds .= '<div class="designer-canvas">';
$ds .= '<div class="designer-widget designer-widget--text"><span class="designer-widget__label">Heading</span></div>';
$ds .= '<div class="designer-widget designer-widget--img"><span class="designer-widget__label">Image</span></div>';
$ds .= '<div class="designer-widget designer-widget--ticker"><span class="designer-widget__label">Ticker</span></div>';
$ds .= '<div class="designer-widget designer-widget--video"><span class="designer-widget__label">Video</span></div>';
$ds .= '</div>';
$ds .= '<div class="designer-layers"><div class="designer-layers__title">Layers</div><div class="designer-layer">Heading</div><div class="designer-layer">Image</div><div class="designer-layer">Ticker</div><div class="designer-layer">Video</div></div>';
$ds .= '</div>';
$ds .= '</div>';
$visual_html = $ds;
$visual_cls = 'platform-visual has-designer';
}
elseif (!empty($a['mediaLibraryAnim'])) {
/* ── Media Library Grid ── */
$ml = '<div class="medialib-stage" aria-hidden="true">';
$ml .= '<div class="medialib-panel">';
$ml .= '<div class="medialib-hdr"><span class="medialib-hdr__title">Media Library</span><span class="medialib-hdr__count">24 assets</span></div>';
$ml .= '<div class="medialib-grid">';
$ml .= '<div class="medialib-thumb medialib-thumb--img"><span class="medialib-thumb__icon">&#128247;</span></div>';
$ml .= '<div class="medialib-thumb medialib-thumb--vid"><span class="medialib-thumb__icon">&#9654;</span></div>';
$ml .= '<div class="medialib-thumb medialib-thumb--pdf"><span class="medialib-thumb__icon">PDF</span></div>';
$ml .= '<div class="medialib-thumb medialib-thumb--ppt"><span class="medialib-thumb__icon">PPT</span></div>';
$ml .= '<div class="medialib-thumb medialib-thumb--img2"><span class="medialib-thumb__icon">&#128247;</span></div>';
$ml .= '<div class="medialib-thumb medialib-thumb--audio"><span class="medialib-thumb__icon">&#9835;</span></div>';
$ml .= '</div>';
$ml .= '<div class="medialib-upload"><div class="medialib-upload__bar"></div><span class="medialib-upload__text">Uploading...</span></div>';
$ml .= '</div>';
$ml .= '</div>';
$visual_html = $ml;
$visual_cls = 'platform-visual has-media-library';
}
elseif (!empty($a['publishAnim'])) {
/* ── Draft → Publish Workflow ── */
$pb = '<div class="publish-stage" aria-hidden="true">';
$pb .= '<div class="publish-flow">';
$pb .= '<div class="publish-step publish-step--draft"><span class="publish-step__badge">Draft</span><span class="publish-step__label">Edit safely</span></div>';
$pb .= '<div class="publish-arrow">&#8594;</div>';
$pb .= '<div class="publish-step publish-step--review"><span class="publish-step__badge">Review</span><span class="publish-step__label">Preview on screen</span></div>';
$pb .= '<div class="publish-arrow">&#8594;</div>';
$pb .= '<div class="publish-step publish-step--live"><span class="publish-step__badge">Live</span><span class="publish-step__label">Instant publish</span></div>';
$pb .= '</div>';
$pb .= '<div class="publish-bar"><div class="publish-bar__fill"></div></div>';
$pb .= '<div class="publish-status">Publishing to 48 screens...</div>';
$pb .= '</div>';
$visual_html = $pb;
$visual_cls = 'platform-visual has-publish';
}
elseif (!empty($a['screenGroupsAnim'])) {
/* ── Screen Group Hierarchy Tree ── */
$sg = '<div class="groups-stage" aria-hidden="true">';
$sg .= '<div class="groups-tree">';
$sg .= '<div class="groups-node groups-node--root"><span class="groups-node__icon">&#9679;</span><span class="groups-node__name">HQ</span><span class="groups-node__count">48</span></div>';
$sg .= '<div class="groups-branch">';
$sg .= '<div class="groups-node groups-node--region"><span class="groups-node__icon">&#9679;</span><span class="groups-node__name">London</span><span class="groups-node__count">24</span></div>';
$sg .= '<div class="groups-branch groups-branch--deep">';
$sg .= '<div class="groups-node groups-node--site"><span class="groups-node__icon">&#9679;</span><span class="groups-node__name">Oxford St</span><span class="groups-node__count">12</span></div>';
$sg .= '<div class="groups-node groups-node--site"><span class="groups-node__icon">&#9679;</span><span class="groups-node__name">Canary Wharf</span><span class="groups-node__count">12</span></div>';
$sg .= '</div>';
$sg .= '<div class="groups-node groups-node--region"><span class="groups-node__icon">&#9679;</span><span class="groups-node__name">Manchester</span><span class="groups-node__count">24</span></div>';
$sg .= '</div>';
$sg .= '</div>';
$sg .= '<div class="groups-push"><div class="groups-push__ripple"></div><span class="groups-push__label">Push Update</span></div>';
$sg .= '</div>';
$visual_html = $sg;
$visual_cls = 'platform-visual has-screen-groups';
}
elseif (!empty($a['monitoringAnim'])) {
/* ── Remote Monitoring Dashboard ── */
$mo = '<div class="monitor-stage" aria-hidden="true">';
$mo .= '<div class="monitor-dash">';
$mo .= '<div class="monitor-hdr"><span class="monitor-hdr__title">Display Status</span><span class="monitor-hdr__count">48/48 Online</span></div>';
$mo .= '<div class="monitor-grid">';
$mo .= '<div class="monitor-tile monitor-tile--ok"><span class="monitor-tile__dot"></span><span class="monitor-tile__name">Lobby-01</span></div>';
$mo .= '<div class="monitor-tile monitor-tile--ok"><span class="monitor-tile__dot"></span><span class="monitor-tile__name">Lobby-02</span></div>';
$mo .= '<div class="monitor-tile monitor-tile--warn"><span class="monitor-tile__dot"></span><span class="monitor-tile__name">Floor3-01</span></div>';
$mo .= '<div class="monitor-tile monitor-tile--ok"><span class="monitor-tile__dot"></span><span class="monitor-tile__name">Café-01</span></div>';
$mo .= '<div class="monitor-tile monitor-tile--ok"><span class="monitor-tile__dot"></span><span class="monitor-tile__name">Conf-A</span></div>';
$mo .= '<div class="monitor-tile monitor-tile--ok"><span class="monitor-tile__dot"></span><span class="monitor-tile__name">Conf-B</span></div>';
$mo .= '</div>';
$mo .= '<div class="monitor-screenshot"><div class="monitor-screenshot__img"></div><span class="monitor-screenshot__label">Live Screenshot — Lobby-01</span></div>';
$mo .= '</div>';
$mo .= '</div>';
$visual_html = $mo;
$visual_cls = 'platform-visual has-monitoring';
}
elseif (!empty($a['patientWayfindAnim'])) {
/* ── Hospital Patient Wayfinding Kiosk ── */
$pw = '<div class="pwayfind-stage" aria-hidden="true">';
$pw .= '<div class="pwayfind-kiosk">';
$pw .= '<div class="pwayfind-hdr"><span class="pwayfind-hdr__title">Find Department</span><span class="pwayfind-hdr__a11y">&#9855;</span></div>';
$pw .= '<div class="pwayfind-search"><span class="pwayfind-search__icon">&#128269;</span><span class="pwayfind-search__text">Cardiology...</span></div>';
$pw .= '<div class="pwayfind-result">';
$pw .= '<div class="pwayfind-result__dept">Cardiology — Level 3, Wing B</div>';
$pw .= '<div class="pwayfind-result__route"><div class="pwayfind-route__line"></div><div class="pwayfind-route__start">You are here</div><div class="pwayfind-route__end">Cardiology</div></div>';
$pw .= '</div>';
$pw .= '<div class="pwayfind-quick"><span class="pwayfind-quick__item">A&amp;E</span><span class="pwayfind-quick__item">Pharmacy</span><span class="pwayfind-quick__item">Radiology</span></div>';
$pw .= '</div>';
$pw .= '</div>';
$visual_html = $pw;
$visual_cls = 'platform-visual has-patient-wayfind';
}
elseif (!empty($a['waitingRoomAnim'])) {
/* ── Waiting Room Information Display ── */
$wr = '<div class="waitroom-stage" aria-hidden="true">';
$wr .= '<div class="waitroom-tv">';
$wr .= '<div class="waitroom-tv__body">';
$wr .= '<div class="waitroom-tv__screen">';
$wr .= '<div class="waitroom-slides">';
$wr .= '<div class="waitroom-slide waitroom-slide--tips"><div class="waitroom-tips__title">Health Tip</div><div class="waitroom-tips__text">Regular hand washing reduces infection spread by up to 50%</div></div>';
$wr .= '<div class="waitroom-slide waitroom-slide--wait"><div class="waitroom-wait__title">Estimated Wait</div><div class="waitroom-wait__value">~15 min</div><div class="waitroom-wait__queue">Position: 4 of 12</div></div>';
$wr .= '<div class="waitroom-slide waitroom-slide--info"><div class="waitroom-info__title">Facility Hours</div><div class="waitroom-info__text">MonFri: 8am 8pm<br>Sat: 9am 1pm</div></div>';
$wr .= '</div>';
$wr .= '</div>';
$wr .= '</div>';
$wr .= '<div class="waitroom-tv__feet"><div class="waitroom-tv__foot"></div><div class="waitroom-tv__foot"></div></div>';
$wr .= '</div>';
$wr .= '</div>';
$visual_html = $wr;
$visual_cls = 'platform-visual has-waiting-room';
}
elseif (!empty($a['multiZoneAnim'])) {
/* ── Multi-Zone Layout Display ── */
$mz = '<div class="mzone-stage" aria-hidden="true">';
$mz .= '<div class="mzone-tv">';
$mz .= '<div class="mzone-tv__body">';
$mz .= '<div class="mzone-tv__screen">';
$mz .= '<div class="mzone-layout">';
$mz .= '<div class="mzone-main"><div class="mzone-main__label">&#9654; Live TV</div></div>';
$mz .= '<div class="mzone-side"><div class="mzone-side__title">Class Schedule</div><div class="mzone-side__item">10:00 Spin</div><div class="mzone-side__item">11:15 Yoga</div><div class="mzone-side__item">12:30 HIIT</div></div>';
$mz .= '<div class="mzone-ticker">&#9733; Member of the Month: Alex R &nbsp;&nbsp;|&nbsp;&nbsp; Gym closes 10 PM tonight &nbsp;&nbsp;|&nbsp;&nbsp; New PT packages available</div>';
$mz .= '</div>';
$mz .= '</div>';
$mz .= '</div>';
$mz .= '<div class="mzone-tv__feet"><div class="mzone-tv__foot"></div><div class="mzone-tv__foot"></div></div>';
$mz .= '</div>';
$mz .= '</div>';
$visual_html = $mz;
$visual_cls = 'platform-visual has-multi-zone';
}
elseif (!empty($a['membershipAnim'])) {
/* ── Seasonal Membership Promo Cycling ── */
$mb = '<div class="member-stage" aria-hidden="true">';
$mb .= '<div class="member-screen">';
$mb .= '<div class="member-slides">';
$mb .= '<div class="member-slide member-slide--jan"><div class="member-slide__tag">January Offer</div><div class="member-slide__title">New Year, New You</div><div class="member-slide__price">Join from $29/mo</div><div class="member-slide__cta">Sign Up Today &#8594;</div></div>';
$mb .= '<div class="member-slide member-slide--summer"><div class="member-slide__tag">Summer Special</div><div class="member-slide__title">Get Beach Ready</div><div class="member-slide__price">3 months for $79</div><div class="member-slide__cta">Claim Offer &#8594;</div></div>';
$mb .= '<div class="member-slide member-slide--sept"><div class="member-slide__tag">Back to Fitness</div><div class="member-slide__title">Bring a Friend Free</div><div class="member-slide__price">This week only</div><div class="member-slide__cta">Book Now &#8594;</div></div>';
$mb .= '</div>';
$mb .= '</div>';
$mb .= '</div>';
$visual_html = $mb;
$visual_cls = 'platform-visual has-membership';
}
elseif (!empty($a['hospitalityAnim'])) {
/* ── Hospitality Sign: Rotating Menu (Breakfast, Lunch, Dinner) ── */
$ha = '<div class="hosp-stage" aria-hidden="true">';
$ha .= '<div class="hosp-tv">';
$ha .= '<div class="hosp-tv__body">';
$ha .= '<div class="hosp-tv__screen">';
// POS Sync Overlay (Pulse)
$ha .= '<div class="hosp-pos"><div class="hosp-pos__dot"></div><div class="hosp-pos__txt">POS Syncing...</div></div>';
$ha .= '<div class="hosp-slides">';
// Slide 1: Breakfast
$ha .= '<div class="hosp-slide hosp-slide--breakfast">';
$ha .= '<div class="hosp-menu">';
$ha .= '<div class="hosp-menu__hd"><div class="hosp-menu__badge">Breakfast</div><div class="hosp-menu__time">6am 11am</div></div>';
$ha .= '<div class="hosp-menu__title">Morning Classics</div>';
$ha .= '<div class="hosp-menu__items">';
$ha .= '<div class="hosp-menu__item"><div class="hosp-menu__info"><div class="hosp-menu__name">Eggs Benedict</div><div class="hosp-menu__desc">Hollandaise, smoked ham</div></div><div class="hosp-menu__price">$16</div></div>';
$ha .= '<div class="hosp-menu__item"><div class="hosp-menu__info"><div class="hosp-menu__name">Avocado Toast</div><div class="hosp-menu__desc">Sourdough, chili flakes</div></div><div class="hosp-menu__price">$14</div></div>';
$ha .= '<div class="hosp-menu__item"><div class="hosp-menu__info"><div class="hosp-menu__name">Buttermilk Stacks</div><div class="hosp-menu__desc">Maple syrup, berries</div></div><div class="hosp-menu__price">$12</div></div>';
$ha .= '</div>';
$ha .= '</div>';
$ha .= '</div>';
// Slide 2: Lunch
$ha .= '<div class="hosp-slide hosp-slide--lunch">';
$ha .= '<div class="hosp-menu">';
$ha .= '<div class="hosp-menu__hd"><div class="hosp-menu__badge">Lunch</div><div class="hosp-menu__time">12pm 4pm</div></div>';
$ha .= '<div class="hosp-menu__title">Fresh & Seasonal</div>';
$ha .= '<div class="hosp-menu__items">';
$ha .= '<div class="hosp-menu__item"><div class="hosp-menu__info"><div class="hosp-menu__name">Wagyu Burger</div><div class="hosp-menu__desc">Brioche, truffle aioli</div></div><div class="hosp-menu__price">$22</div></div>';
$ha .= '<div class="hosp-menu__item"><div class="hosp-menu__info"><div class="hosp-menu__name">Harvest Bowl</div><div class="hosp-menu__desc">Quinoa, roasted veg</div></div><div class="hosp-menu__price">$18</div></div>';
$ha .= '<div class="hosp-menu__item"><div class="hosp-menu__info"><div class="hosp-menu__name">Grilled Salmon</div><div class="hosp-menu__desc">Lemon, asparagus</div></div><div class="hosp-menu__price">$24</div></div>';
$ha .= '</div>';
$ha .= '</div>';
$ha .= '</div>';
// Slide 3: Dinner
$ha .= '<div class="hosp-slide hosp-slide--dinner">';
$ha .= '<div class="hosp-menu">';
$ha .= '<div class="hosp-menu__hd"><div class="hosp-menu__badge">Dinner</div><div class="hosp-menu__time">5pm late</div></div>';
$ha .= '<div class="hosp-menu__title">Evening Specials</div>';
$ha .= '<div class="hosp-menu__items">';
$ha .= '<div class="hosp-menu__item"><div class="hosp-menu__info"><div class="hosp-menu__name">Ribeye Steak</div><div class="hosp-menu__desc">Garlic butter, frites</div></div><div class="hosp-menu__price">$38</div></div>';
$ha .= '<div class="hosp-menu__item"><div class="hosp-menu__info"><div class="hosp-menu__name">Lobster Pasta</div><div class="hosp-menu__desc">Bisque, fresh herbs</div></div><div class="hosp-menu__price">$32</div></div>';
$ha .= '<div class="hosp-menu__item"><div class="hosp-menu__info"><div class="hosp-menu__name">Rack of Lamb</div><div class="hosp-menu__desc">Mint jus, pea purée</div></div><div class="hosp-menu__price">$36</div></div>';
$ha .= '</div>';
$ha .= '</div>';
$ha .= '</div>';
$ha .= '</div>'; // hosp-slides
$ha .= '</div>'; // hosp-tv__screen
$ha .= '</div>'; // hosp-tv__body
$ha .= '<div class="hosp-tv__feet"><div class="hosp-tv__foot"></div><div class="hosp-tv__foot"></div></div>';
$ha .= '</div>'; // hosp-tv
$ha .= '</div>'; // hosp-stage
$visual_html = $ha;
$visual_cls = 'platform-visual has-hospitality';
}
elseif (!empty($a['videoMotionAnim'])) {
/* ── Video & Motion Graphics: TV with kinetic text, promo, loop preview ── */
$vm = '<div class="vidmo-stage" aria-hidden="true">';
$vm .= '<div class="vidmo-tv">';
$vm .= '<div class="vidmo-tv__body">';
$vm .= '<div class="vidmo-tv__screen">';
$vm .= '<div class="vidmo-slides">';
/* Slide 1: Kinetic typography */
$vm .= '<div class="vidmo-slide vidmo-slide--kinetic">';
$vm .= '<div class="vidmo-kinetic">';
$vm .= '<span class="vidmo-kinetic__word vidmo-kinetic__word--1">Engage</span>';
$vm .= '<span class="vidmo-kinetic__word vidmo-kinetic__word--2">Inform</span>';
$vm .= '<span class="vidmo-kinetic__word vidmo-kinetic__word--3">Convert</span>';
$vm .= '</div>';
$vm .= '</div>';
/* Slide 2: Promo graphic */
$vm .= '<div class="vidmo-slide vidmo-slide--promo">';
$vm .= '<div class="vidmo-promo">';
$vm .= '<div class="vidmo-promo__bg"></div>';
$vm .= '<div class="vidmo-promo__label">Summer Campaign</div>';
$vm .= '<div class="vidmo-promo__title">Your Brand, Animated</div>';
$vm .= '<div class="vidmo-promo__sub">15s promotional loop</div>';
$vm .= '</div>';
$vm .= '</div>';
/* Slide 3: Loop format */
$vm .= '<div class="vidmo-slide vidmo-slide--loop">';
$vm .= '<div class="vidmo-loop">';
$vm .= '<div class="vidmo-loop__icon">&#x25B6;</div>';
$vm .= '<div class="vidmo-loop__text">Screen-Ready Formats</div>';
$vm .= '<div class="vidmo-loop__formats">';
$vm .= '<span class="vidmo-loop__fmt">MP4</span>';
$vm .= '<span class="vidmo-loop__fmt">WEBM</span>';
$vm .= '<span class="vidmo-loop__fmt">GIF</span>';
$vm .= '</div>';
$vm .= '</div>';
$vm .= '</div>';
$vm .= '</div>'; // vidmo-slides
$vm .= '<div class="vidmo-progress"><div class="vidmo-progress__bar"></div></div>';
$vm .= '</div>'; // vidmo-tv__screen
$vm .= '</div>'; // vidmo-tv__body
$vm .= '<div class="vidmo-tv__feet"><div class="vidmo-tv__foot"></div><div class="vidmo-tv__foot"></div></div>';
$vm .= '</div>'; // vidmo-tv
$vm .= '</div>'; // vidmo-stage
$visual_html = $vm;
$visual_cls = 'platform-visual has-video-motion';
}
elseif (!empty($a['brandLayoutAnim'])) {
/* ── Branded Layout Design: template builder with brand guide ── */
$bl = '<div class="blay-stage" aria-hidden="true">';
$bl .= '<div class="blay-editor">';
/* Toolbar */
$bl .= '<div class="blay-toolbar">';
$bl .= '<div class="blay-toolbar__dots">';
$bl .= '<span class="blay-toolbar__dot blay-toolbar__dot--r"></span>';
$bl .= '<span class="blay-toolbar__dot blay-toolbar__dot--y"></span>';
$bl .= '<span class="blay-toolbar__dot blay-toolbar__dot--g"></span>';
$bl .= '</div>';
$bl .= '<div class="blay-toolbar__title">Layout Editor</div>';
$bl .= '</div>';
/* Body: canvas + panel */
$bl .= '<div class="blay-body">';
/* Canvas */
$bl .= '<div class="blay-canvas">';
$bl .= '<div class="blay-preview">';
$bl .= '<div class="blay-el blay-el--logo"></div>';
$bl .= '<div class="blay-el blay-el--hero"></div>';
$bl .= '<div class="blay-el blay-el--headline"></div>';
$bl .= '<div class="blay-el blay-el--body"></div>';
$bl .= '<div class="blay-el blay-el--body2"></div>';
$bl .= '<div class="blay-el blay-el--cta"></div>';
$bl .= '<div class="blay-el blay-el--accent"></div>';
$bl .= '</div>';
$bl .= '</div>';
/* Brand guide panel */
$bl .= '<div class="blay-panel">';
$bl .= '<div class="blay-panel__title">Brand Guide</div>';
$bl .= '<div class="blay-colors">';
$bl .= '<div class="blay-color blay-color--1"></div>';
$bl .= '<div class="blay-color blay-color--2"></div>';
$bl .= '<div class="blay-color blay-color--3"></div>';
$bl .= '<div class="blay-color blay-color--4"></div>';
$bl .= '</div>';
$bl .= '<div class="blay-fonts">';
$bl .= '<div class="blay-font"><span class="blay-font__label">Heading</span><span class="blay-font__sample">Aa</span></div>';
$bl .= '<div class="blay-font"><span class="blay-font__label">Body</span><span class="blay-font__sample blay-font__sample--body">Aa Bb Cc</span></div>';
$bl .= '</div>';
$bl .= '<div class="blay-spacing">';
$bl .= '<div class="blay-spacing__label">Spacing</div>';
$bl .= '<div class="blay-spacing__bars">';
$bl .= '<div class="blay-spacing__bar blay-spacing__bar--sm"></div>';
$bl .= '<div class="blay-spacing__bar blay-spacing__bar--md"></div>';
$bl .= '<div class="blay-spacing__bar blay-spacing__bar--lg"></div>';
$bl .= '<div class="blay-spacing__bar blay-spacing__bar--xl"></div>';
$bl .= '</div>';
$bl .= '</div>';
$bl .= '</div>';
$bl .= '</div>'; // blay-body
$bl .= '</div>'; // blay-editor
$bl .= '</div>'; // blay-stage
$visual_html = $bl;
$visual_cls = 'platform-visual has-brand-layout';
}
elseif (!empty($a['menuBoardAnim'])) {
/* ── Digital Menu Board: TV with rotating menu categories ── */
$mb = '<div class="menu-stage" aria-hidden="true">';
$mb .= '<div class="menu-tv">';
$mb .= '<div class="menu-tv__body">';
$mb .= '<div class="menu-tv__screen">';
/* Live badge */
$mb .= '<div class="menu-live"><span class="menu-live__dot"></span><span class="menu-live__text">LIVE</span></div>';
$mb .= '<div class="menu-slides">';
/* Slide 1: Mains */
$mb .= '<div class="menu-slide menu-slide--mains">';
$mb .= '<div class="menu-content">';
$mb .= '<div class="menu-content__hd"><span class="menu-content__badge">Lunch</span><span class="menu-content__time">11:30 AM</span></div>';
$mb .= '<div class="menu-content__title">Mains</div>';
$mb .= '<div class="menu-content__items">';
$mb .= '<div class="menu-content__item"><div><div class="menu-content__name">Grilled Chicken</div><div class="menu-content__desc">Herb-crusted, seasonal veg</div></div><div class="menu-content__price">$16.50</div></div>';
$mb .= '<div class="menu-content__item"><div><div class="menu-content__name">Wild Salmon</div><div class="menu-content__desc">Pan-seared, lemon butter</div></div><div class="menu-content__price">$22.00</div></div>';
$mb .= '<div class="menu-content__item"><div><div class="menu-content__name">Mushroom Risotto</div><div class="menu-content__desc">Truffle oil, parmesan</div></div><div class="menu-content__price">$14.00</div></div>';
$mb .= '</div>';
$mb .= '</div>';
$mb .= '</div>';
/* Slide 2: Drinks */
$mb .= '<div class="menu-slide menu-slide--drinks">';
$mb .= '<div class="menu-content">';
$mb .= '<div class="menu-content__hd"><span class="menu-content__badge">Drinks</span><span class="menu-content__time">11:30 AM</span></div>';
$mb .= '<div class="menu-content__title">Beverages</div>';
$mb .= '<div class="menu-content__items">';
$mb .= '<div class="menu-content__item"><div><div class="menu-content__name">Flat White</div><div class="menu-content__desc">Double shot, oat option</div></div><div class="menu-content__price">$5.50</div></div>';
$mb .= '<div class="menu-content__item"><div><div class="menu-content__name">Fresh Juice</div><div class="menu-content__desc">Orange, apple or green</div></div><div class="menu-content__price">$7.00</div></div>';
$mb .= '<div class="menu-content__item"><div><div class="menu-content__name">Sparkling Water</div><div class="menu-content__desc">750ml bottle</div></div><div class="menu-content__price">$4.00</div></div>';
$mb .= '</div>';
$mb .= '</div>';
$mb .= '</div>';
/* Slide 3: Desserts */
$mb .= '<div class="menu-slide menu-slide--dessert">';
$mb .= '<div class="menu-content">';
$mb .= '<div class="menu-content__hd"><span class="menu-content__badge">Dessert</span><span class="menu-content__time">11:30 AM</span></div>';
$mb .= '<div class="menu-content__title">Something Sweet</div>';
$mb .= '<div class="menu-content__items">';
$mb .= '<div class="menu-content__item"><div><div class="menu-content__name">Chocolate Fondant</div><div class="menu-content__desc">Molten centre, vanilla ice cream</div></div><div class="menu-content__price">$12.00</div></div>';
$mb .= '<div class="menu-content__item"><div><div class="menu-content__name">Cr&egrave;me Br&ucirc;l&eacute;e</div><div class="menu-content__desc">Classic vanilla, caramelised top</div></div><div class="menu-content__price">$10.00</div></div>';
$mb .= '<div class="menu-content__item"><div><div class="menu-content__name">Seasonal Fruit</div><div class="menu-content__desc">Fresh selection, honey drizzle</div></div><div class="menu-content__price">$8.00</div></div>';
$mb .= '</div>';
$mb .= '</div>';
$mb .= '</div>';
$mb .= '</div>'; // menu-slides
$mb .= '</div>'; // menu-tv__screen
$mb .= '</div>'; // menu-tv__body
$mb .= '<div class="menu-tv__feet"><div class="menu-tv__foot"></div><div class="menu-tv__foot"></div></div>';
$mb .= '</div>'; // menu-tv
$mb .= '</div>'; // menu-stage
$visual_html = $mb;
$visual_cls = 'platform-visual has-menu-board';
}
elseif (!empty($a['cameraAnim'])) {
$visual_html = oribi_render_camera_animation();
$visual_cls = 'platform-visual has-video-editor';
/* ── Gallery TV Slideshow ───────────────────────────────── */
}
elseif (!empty($a['galleryIds']) && is_array($a['galleryIds']) && count($a['galleryIds']) > 0) {
$slides = '';
$count = 0;
foreach ($a['galleryIds'] as $gid) {
$gid = intval($gid);
if (!$gid)
continue;
$url = wp_get_attachment_url($gid);
$alt = get_post_meta($gid, '_wp_attachment_image_alt', true);
if (!$url)
continue;
$active = $count === 0 ? ' is-active' : '';
$slides .= '<div class="gtv-slide' . $active . '">';
$slides .= '<img src="' . esc_url($url) . '" alt="' . esc_attr($alt) . '" loading="lazy" />';
$slides .= '</div>';
$count++;
}
if ($count > 0) {
$visual_html = '<div class="gtv-stage" data-gtv-slideshow aria-hidden="true">';
$visual_html .= '<div class="gtv-tv">';
$visual_html .= '<div class="gtv-tv__body">';
$visual_html .= '<div class="gtv-tv__screen">';
$visual_html .= '<div class="gtv-slides">' . $slides . '</div>';
$visual_html .= '</div>'; // screen
$visual_html .= '</div>'; // body
$visual_html .= '<div class="gtv-tv__feet"><div class="gtv-tv__foot"></div><div class="gtv-tv__foot"></div></div>';
$visual_html .= '</div>'; // tv
$visual_html .= '</div>'; // stage
$visual_cls = 'platform-visual has-gallery-tv';
}
else {
$visual_html = oribi_render_icon($a['visual'] ?? '');
$visual_cls = 'platform-visual';
}
}
else {
$visual_html = oribi_render_icon($a['visual'] ?? '');
$visual_cls = 'platform-visual';
}
ob_start(); ?>
<div class="platform-row<?php echo $rev; ?>">
<div class="platform-text">
<h3><?php echo wp_kses_post($a['heading']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
<?php if (!empty($a['btnUrl'])): ?>
<a href="<?php echo esc_url($a['btnUrl']); ?>" class="btn btn-outline mt-3"><?php echo esc_html($a['btnText'] ?? 'Learn More'); ?> &rarr;</a>
<?php
endif; ?>
</div>
<div class="<?php echo esc_attr($visual_cls); ?>"><?php echo $visual_html; ?></div>
</div>
<?php return ob_get_clean();
}
/* ── Trust Section (parent - wraps child trust-item blocks) ────────────────── */
function oribi_render_trust_section($a, $content)
{
ob_start(); ?>
<section class="section">
<div class="container">
<div class="section-header">
<?php if ($a['label']): ?><span class="section-label"><?php echo esc_html($a['label']); ?></span><?php
endif; ?>
<h2><?php echo wp_kses_post($a['heading']); ?></h2>
<?php if ($a['lead']): ?><p class="lead"><?php echo wp_kses_post($a['lead']); ?></p><?php
endif; ?>
</div>
<div class="grid-2" style="align-items:center;">
<div style="display:flex;flex-direction:column;gap:1.5rem;">
<?php echo $content; ?>
</div>
<div style="text-align:center;">
<a href="<?php echo esc_url($a['btnUrl']); ?>" target="_blank" rel="noopener" class="btn btn-primary btn-lg"><?php echo esc_html($a['btnText']); ?></a>
<?php if ($a['btnSub']): ?><p class="lead mt-2" style="font-size:.9rem;"><?php echo esc_html($a['btnSub']); ?></p><?php
endif; ?>
</div>
</div>
</div>
</section>
<?php return ob_get_clean();
}
/* ── Trust Item (child - renders one heading + description pair) ────────────── */
function oribi_render_trust_item($a)
{
ob_start(); ?>
<div class="trust-item">
<h3 style="margin-bottom:1rem;"><?php echo wp_kses_post($a['heading']); ?></h3>
<p><?php echo wp_kses_post($a['description']); ?></p>
</div>
<?php return ob_get_clean();
}
/* ── FAQ Section (parent - wraps child faq-item blocks) ────────────────────── */
function oribi_render_faq_section($a, $content)
{
$cls = $a['variant'] === 'alt' ? 'section section-alt' : 'section';
ob_start(); ?>
<section class="<?php echo esc_attr($cls); ?>">
<div class="container">
<div class="section-header">
<?php if ($a['label']): ?><span class="section-label"><?php echo esc_html($a['label']); ?></span><?php
endif; ?>
<h2><?php echo wp_kses_post($a['heading']); ?></h2>
<?php if ($a['lead']): ?><p class="lead"><?php echo wp_kses_post($a['lead']); ?></p><?php
endif; ?>
</div>
<div class="faq-list">
<?php echo $content; ?>
</div>
</div>
</section>
<?php return ob_get_clean();
}
/* ── FAQ Item (child - renders one accordion item) ─────────────────────────── */
function oribi_render_faq_item($a)
{
ob_start(); ?>
<details class="faq-item">
<summary class="faq-question"><?php echo wp_kses_post($a['question']); ?><span class="faq-toggle" aria-hidden="true"></span></summary>
<div class="faq-answer"><p><?php echo wp_kses_post($a['answer']); ?></p></div>
</details>
<?php return ob_get_clean();
}
/* ── Comparison Table (standalone - renders a feature matrix) ──────────────── */
function oribi_render_comparison_table($a)
{
$cls = $a['variant'] === 'alt' ? 'section section-alt' : 'section';
$cols = $a['columns'] ?? [];
$rows = $a['rows'] ?? [];
ob_start(); ?>
<section class="<?php echo esc_attr($cls); ?>">
<div class="container">
<div class="section-header">
<?php if ($a['label']): ?><span class="section-label"><?php echo esc_html($a['label']); ?></span><?php
endif; ?>
<h2><?php echo wp_kses_post($a['heading']); ?></h2>
<?php if ($a['lead']): ?><p class="lead"><?php echo wp_kses_post($a['lead']); ?></p><?php
endif; ?>
</div>
<div class="comparison-table-wrap">
<table class="comparison-table">
<thead>
<tr>
<th class="comparison-feature-col">Feature</th>
<?php foreach ($cols as $col): ?>
<th><?php echo esc_html($col); ?></th>
<?php
endforeach; ?>
</tr>
</thead>
<tbody>
<?php foreach ($rows as $row):
$is_group = !empty($row['group']);
?>
<?php if ($is_group): ?>
<tr class="comparison-group-row">
<td colspan="<?php echo count($cols) + 1; ?>"><?php echo esc_html($row['group']); ?></td>
</tr>
<?php
else: ?>
<tr>
<td class="comparison-feature-name"><?php echo wp_kses_post($row['feature'] ?? ''); ?></td>
<?php foreach (($row['values'] ?? []) as $val): ?>
<td class="comparison-cell"><?php
if ($val === true)
echo '<span class="comparison-yes">&#10003;</span>';
elseif ($val === false)
echo '<span class="comparison-no">&#10007;</span>';
else
echo wp_kses_post($val);
?></td>
<?php
endforeach; ?>
</tr>
<?php
endif; ?>
<?php
endforeach; ?>
</tbody>
</table>
</div>
</div>
</section>
<?php return ob_get_clean();
}
/* ══════════════════════════════════════════════════════════════════════════════
ANIMATED HERO BLOCKS (OTS Signs)
══════════════════════════════════════════════════════════════════════════════ */
/**
* Generate particle divs for the animated hero background.
* Each particle gets a unique modifier class for staggered animation.
*
* @param int $count Number of particles (default 12).
* @return string Rendered HTML.
*/
function oribi_render_particles($count = 12)
{
$html = '<div class="hero-particles" aria-hidden="true">';
for ($i = 1; $i <= $count; $i++) {
$html .= '<div class="hero-particle hero-particle--' . $i . '"></div>';
}
$html .= '</div>';
return $html;
}
/* ── Animated Hero (homepage) ──────────────────────────────────────────────── */
function oribi_render_hero_animated($a)
{
ob_start();
?>
<section class="hero hero-animated">
<?php echo oribi_render_particles(12); ?>
<div class="hero-animated__glow"></div>
<div class="container hero-animated__inner">
<div class="hero-animated__content">
<?php if ($a['label']): ?>
<span class="hero-label"><?php echo wp_kses_post($a['label']); ?></span>
<?php
endif; ?>
<h1 class="hero-title"><?php echo oribi_highlight($a['title'], $a['highlightWord']); ?></h1>
<p class="hero-description"><?php echo wp_kses_post($a['description']); ?></p>
<div class="btn-group">
<?php if ($a['primaryBtnText']): ?>
<a href="<?php echo esc_url($a['primaryBtnUrl']); ?>" class="btn btn-primary btn-lg"><?php echo esc_html($a['primaryBtnText']); ?></a>
<?php
endif; ?>
<?php if ($a['secondaryBtnText']): ?>
<a href="<?php echo esc_url($a['secondaryBtnUrl']); ?>" class="btn btn-ghost btn-lg"><?php echo esc_html($a['secondaryBtnText']); ?> &rarr;</a>
<?php
endif; ?>
</div>
<?php if ($a['stat1Value'] || $a['stat2Value'] || $a['stat3Value']): ?>
<div class="hero-stats hero-stats--three">
<?php if ($a['stat1Value']): ?>
<div><div class="hero-stat-value"><?php echo esc_html($a['stat1Value']); ?></div><div class="hero-stat-label"><?php echo esc_html($a['stat1Label']); ?></div></div>
<?php
endif; ?>
<?php if ($a['stat2Value']): ?>
<div><div class="hero-stat-value"><?php echo esc_html($a['stat2Value']); ?></div><div class="hero-stat-label"><?php echo esc_html($a['stat2Label']); ?></div></div>
<?php
endif; ?>
<?php if ($a['stat3Value']): ?>
<div><div class="hero-stat-value"><?php echo esc_html($a['stat3Value']); ?></div><div class="hero-stat-label"><?php echo esc_html($a['stat3Label']); ?></div></div>
<?php
endif; ?>
</div>
<?php
endif; ?>
</div>
</div>
</section>
<?php return ob_get_clean();
}
/* ── Animated Page Hero (inner pages) ──────────────────────────────────────── */
function oribi_render_page_hero_animated($a)
{
ob_start(); ?>
<section class="page-hero page-hero-animated">
<?php echo oribi_render_particles(8); ?>
<div class="hero-animated__glow"></div>
<div class="hero-overlay"></div>
<div class="container">
<?php if ($a['label']): ?><span class="hero-label"><?php echo wp_kses_post($a['label']); ?></span><?php
endif; ?>
<h1><?php echo wp_kses_post($a['title']); ?></h1>
<p class="lead"><?php echo wp_kses_post($a['description']); ?></p>
</div>
</section>
<?php return ob_get_clean();
}
/* ── Use Cases Showcase ────────────────────────────────────────────────── */
function oribi_uc_anim_inner($mod)
{
switch ($mod) {
case 'menu':
return '
<div class="uc-inner uc-inner--menu" aria-hidden="true">
<div class="uc-menu-header"></div>
<div class="uc-menu-row"><span class="uc-menu-name"></span><span class="uc-menu-price"></span></div>
<div class="uc-menu-row uc-menu-row--highlight"><span class="uc-menu-name"></span><span class="uc-menu-price"></span></div>
<div class="uc-menu-row"><span class="uc-menu-name uc-menu-name--sm"></span><span class="uc-menu-price"></span></div>
<div class="uc-menu-row"><span class="uc-menu-name"></span><span class="uc-menu-price"></span></div>
<div class="uc-menu-divider"></div>
<div class="uc-menu-row"><span class="uc-menu-name uc-menu-name--sm"></span><span class="uc-menu-price"></span></div>
</div>';
case 'event':
return '
<div class="uc-inner uc-inner--event" aria-hidden="true">
<div class="uc-event-cursor"></div>
<div class="uc-event-header">Schedule</div>
<div class="uc-event-row">
<div class="uc-event-time">9:00</div>
<div class="uc-event-title uc-event-title--active">Keynote Speech</div>
</div>
<div class="uc-event-row">
<div class="uc-event-time">10:30</div>
<div class="uc-event-title">Panel Discussion</div>
</div>
<div class="uc-event-row">
<div class="uc-event-time">11:45</div>
<div class="uc-event-title uc-event-title--accent">Coffee Break</div>
</div>
<div class="uc-event-row">
<div class="uc-event-time">12:30</div>
<div class="uc-event-title">Networking Lunch</div>
</div>
</div>';
case 'dashboard':
return '
<div class="uc-inner uc-inner--dashboard" aria-hidden="true">
<div class="uc-db-bars">
<div class="uc-db-bar uc-db-bar--1"></div>
<div class="uc-db-bar uc-db-bar--2"></div>
<div class="uc-db-bar uc-db-bar--3"></div>
<div class="uc-db-bar uc-db-bar--4"></div>
</div>
<div class="uc-db-baseline"></div>
<div class="uc-db-labels">
<div class="uc-db-lbl"></div>
<div class="uc-db-lbl"></div>
<div class="uc-db-lbl"></div>
<div class="uc-db-lbl"></div>
</div>
</div>';
case 'wayfinding':
return '
<div class="uc-inner uc-inner--wayfinding" aria-hidden="true">
<div class="uc-wf-corridor uc-wf-corridor--h"></div>
<div class="uc-wf-corridor uc-wf-corridor--v"></div>
<div class="uc-wf-room uc-wf-room--1"></div>
<div class="uc-wf-room uc-wf-room--2"></div>
<div class="uc-wf-room uc-wf-room--3"></div>
<div class="uc-wf-dot"></div>
<div class="uc-wf-arrow"></div>
</div>';
default:
return '';
}
}
function oribi_render_use_cases($a)
{
$cases = [
['title' => $a['case1Title'], 'desc' => $a['case1Desc'], 'mod' => 'menu'],
['title' => $a['case2Title'], 'desc' => $a['case2Desc'], 'mod' => 'event'],
['title' => $a['case3Title'], 'desc' => $a['case3Desc'], 'mod' => 'dashboard'],
['title' => $a['case4Title'], 'desc' => $a['case4Desc'], 'mod' => 'wayfinding'],
];
ob_start(); ?>
<section class="section use-cases-section">
<div class="container">
<?php if ($a['label'] || $a['heading'] || $a['lead']): ?>
<div class="section-header">
<?php if ($a['label']): ?><span class="section-label"><?php echo esc_html($a['label']); ?></span><?php
endif; ?>
<?php if ($a['heading']): ?><h2><?php echo wp_kses_post($a['heading']); ?></h2><?php
endif; ?>
<?php if ($a['lead']): ?><p class="lead"><?php echo wp_kses_post($a['lead']); ?></p><?php
endif; ?>
</div>
<?php
endif; ?>
<div class="uc-track">
<?php foreach ($cases as $c): ?>
<div class="uc-item">
<div class="uc-circle uc-anim--<?php echo esc_attr($c['mod']); ?>">
<?php echo oribi_uc_anim_inner($c['mod']); ?>
</div>
<div class="uc-item-body">
<div class="uc-item-title"><?php echo esc_html($c['title']); ?></div>
<?php if ($c['desc']): ?><p class="uc-item-desc"><?php echo esc_html($c['desc']); ?></p><?php
endif; ?>
</div>
</div>
<?php
endforeach; ?>
</div>
</div>
</section>
<?php return ob_get_clean();
}