Update script handle and description for gallery TV slideshow in enqueue.php

This commit is contained in:
Matt Batchelder
2026-02-21 14:11:56 -05:00
parent 2edbf9732b
commit 2826a3ec4a
6 changed files with 170 additions and 928 deletions

View File

@@ -10,12 +10,12 @@
<!-- wp:oribi/page-hero-animated {"label":"Industry Solutions","title":"Built for the Way You Work","description":"Every industry communicates differently. Our platform adapts to your environment — whether that's a hotel lobby, a shop floor, a boardroom, or a lecture hall."} /-->
<!-- wp:oribi/platform-section {"label":"Industries We Serve","heading":"One Platform, Tailored to Your Sector","lead":"We've worked with businesses across six key industries. Here's how our platform fits into each one."} -->
<!-- wp:oribi/platform-row {"heading":"Hospitality","description":"Digital menu boards that update with your POS, lobby displays that guide guests, and promotional screens that drive upsells in bars and restaurants. Create a polished guest experience from the moment they walk in — and keep it fresh without touching a single printed sign.","btnText":"Get Started","btnUrl":"/contact","industryHospitality":true} /-->
<!-- wp:oribi/platform-row {"heading":"Retail","description":"Launch promotions across every store instantly, spotlight seasonal products, and guide shoppers with in-store wayfinding. Retailers using digital signage see up to 30% more sales. Our platform makes it simple to keep content current, targeted, and data-driven across every location.","btnText":"See Pricing","btnUrl":"/pricing","reversed":true,"industryRetail":true} /-->
<!-- wp:oribi/platform-row {"heading":"Corporate Office","description":"Meeting room displays with native Microsoft Teams integration, company-wide announcement boards, and live KPI dashboards in common areas. Turn your office into a connected, well-informed workplace where important information is always visible.","btnText":"Learn More","btnUrl":"/features","industryCorporate":true} /-->
<!-- wp:oribi/platform-row {"heading":"Education","description":"Timetable displays, campus wayfinding, emergency alerts, and event boards — all managed centrally. Keep students, faculty, and visitors informed across multiple buildings without the overhead of maintaining individual screens.","btnText":"Contact Us","btnUrl":"/contact","reversed":true,"industryEducation":true} /-->
<!-- wp:oribi/platform-row {"heading":"Outdoor Marketplace","description":"From farmers' markets to seasonal fairs, digital signage adds a professional edge without losing the character of your venue. Weather-resistant display options and built-in offline playback ensure your screens perform reliably, rain or shine.","btnText":"Get a Quote","btnUrl":"/contact","industryOutdoor":true} /-->
<!-- wp:oribi/platform-row {"heading":"Live Data Displays","description":"Bring your web dashboards, real-time KPIs, and operational data to large-format screens. Ideal for operations centres, trading floors, and management war rooms where critical information needs to be visible to the entire team at a glance.","btnText":"See Features","btnUrl":"/features","reversed":true,"industryLiveData":true} /-->
<!-- wp:oribi/platform-row {"heading":"Hospitality","description":"Digital menu boards that update with your POS, lobby displays that guide guests, and promotional screens that drive upsells in bars and restaurants. Create a polished guest experience from the moment they walk in — and keep it fresh without touching a single printed sign.","btnText":"Get Started","btnUrl":"/contact","galleryIds":[]} /-->
<!-- wp:oribi/platform-row {"heading":"Retail","description":"Launch promotions across every store instantly, spotlight seasonal products, and guide shoppers with in-store wayfinding. Retailers using digital signage see up to 30% more sales. Our platform makes it simple to keep content current, targeted, and data-driven across every location.","btnText":"See Pricing","btnUrl":"/pricing","reversed":true,"galleryIds":[]} /-->
<!-- wp:oribi/platform-row {"heading":"Corporate Office","description":"Meeting room displays with native Microsoft Teams integration, company-wide announcement boards, and live KPI dashboards in common areas. Turn your office into a connected, well-informed workplace where important information is always visible.","btnText":"Learn More","btnUrl":"/features","galleryIds":[]} /-->
<!-- wp:oribi/platform-row {"heading":"Education","description":"Timetable displays, campus wayfinding, emergency alerts, and event boards — all managed centrally. Keep students, faculty, and visitors informed across multiple buildings without the overhead of maintaining individual screens.","btnText":"Contact Us","btnUrl":"/contact","reversed":true,"galleryIds":[]} /-->
<!-- wp:oribi/platform-row {"heading":"Outdoor Marketplace","description":"From farmers' markets to seasonal fairs, digital signage adds a professional edge without losing the character of your venue. Weather-resistant display options and built-in offline playback ensure your screens perform reliably, rain or shine.","btnText":"Get a Quote","btnUrl":"/contact","galleryIds":[]} /-->
<!-- wp:oribi/platform-row {"heading":"Live Data Displays","description":"Bring your web dashboards, real-time KPIs, and operational data to large-format screens. Ideal for operations centres, trading floors, and management war rooms where critical information needs to be visible to the entire team at a glance.","btnText":"See Features","btnUrl":"/features","reversed":true,"galleryIds":[]} /-->
<!-- /wp:oribi/platform-section -->
<!-- wp:oribi/stat-section {"variant":"alt","label":"By the Numbers","heading":"The Results Speak for Themselves","lead":"Businesses that invest in digital signage see measurable returns across every metric that matters.","columns":4} -->

View File

@@ -5424,10 +5424,10 @@ p:last-child { margin-bottom: 0; }
}
/* ═══════════════════════════════════════════════════════════════
INDUSTRY MOCKUP ANIMATIONS (.platform-visual.has-industry)
GALLERY TV SLIDESHOW (.platform-visual.has-gallery-tv)
═══════════════════════════════════════════════════════════════ */
.platform-visual.has-industry {
.platform-visual.has-gallery-tv {
background: none !important;
border: none !important;
border-radius: 0;
@@ -5438,93 +5438,76 @@ p:last-child { margin-bottom: 0; }
font-size: inherit;
}
.ind-stage {
.gtv-stage {
width: 100%;
max-width: 480px;
max-width: 520px;
margin: 0 auto;
}
.ind-stage svg {
/* TV frame (mirrors dashboard-tv pattern) */
.gtv-tv {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
height: auto;
}
.gtv-tv__body {
width: 100%;
background: #111;
border: 4px solid #111;
border-radius: 6px 6px 4px 4px;
outline: 1px solid #000;
padding: 3px;
position: relative;
box-shadow: 0 14px 48px rgba(0,0,0,0.55);
}
.gtv-tv__screen {
width: 100%;
aspect-ratio: 16/9;
background: var(--color-bg);
border-radius: 2px;
overflow: hidden;
position: relative;
}
.gtv-tv__feet {
display: flex;
justify-content: space-between;
width: 60%;
max-width: 300px;
}
.gtv-tv__foot {
width: 12px;
height: 8px;
background: #111;
border: 1px solid #000;
border-radius: 0 0 4px 4px;
}
/* Slides */
.gtv-slides {
position: relative;
width: 100%;
height: 100%;
}
.gtv-slide {
position: absolute;
inset: 0;
opacity: 0;
transition: opacity 0.8s ease;
}
.gtv-slide.is-active {
opacity: 1;
}
.gtv-slide img {
width: 100%;
height: 100%;
object-fit: cover;
display: block;
border-radius: var(--radius-md);
box-shadow: 0 8px 32px rgba(0,0,0,.3);
}
/* Subtle glow around active screens */
.ind-stage svg rect[fill="#1c2333"] {
transition: filter .6s ease;
}
/* Animated content transitions */
.ind-menu-price,
.ind-sale-tag,
.ind-footfall-val,
.ind-kpi-val,
.ind-meet-status,
.ind-alert-text,
.ind-weather-icon,
.ind-weather-temp,
.ind-busy-label,
.ind-ld-val,
.ind-ld-alert-text {
transition: opacity .3s ease;
}
.ind-menu-bar,
.ind-rev-bar,
.ind-vendor-bar,
.ind-ld-bar {
transition: height .4s ease, y .4s ease;
}
.ind-product-slot {
transition: stroke-width .3s ease;
}
.ind-sched-slot {
transition: opacity .4s ease, stroke .3s ease;
}
.ind-wf-dot {
transition: opacity .5s ease;
}
.ind-alert-bar {
transition: opacity .4s ease;
}
.ind-meet-dot,
.ind-busy-dot,
.ind-ld-alert {
transition: fill .4s ease;
}
.ind-corp-line,
.ind-ld-line {
transition: d .3s ease;
}
.ind-ld-pie {
transition: d .4s ease;
}
/* ── Reduced-motion overrides for industry animations ── */
/* ── Reduced-motion overrides for gallery TV ── */
@media (prefers-reduced-motion: reduce) {
.ind-menu-bar,
.ind-rev-bar,
.ind-vendor-bar,
.ind-ld-bar,
.ind-product-slot,
.ind-sched-slot,
.ind-wf-dot,
.ind-alert-bar,
.ind-corp-line,
.ind-ld-line,
.ind-ld-pie,
.ind-meet-dot,
.ind-busy-dot,
.ind-ld-alert {
.gtv-slide {
transition: none !important;
}
}

View File

@@ -1,380 +1,56 @@
/**
* Industry Mockup Animator
* Animates SVG environment mockups for each industry on the solutions page.
* Each mockup shows devices in real-world use with animated screen content.
* Gallery TV Slideshow
* Cycles through images inside gallery-TV blocks.
* Pauses off-screen via IntersectionObserver for performance.
* Respects prefers-reduced-motion.
*/
(function () {
'use strict';
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
var SPEED = 0.0018;
/* ── Utility ────────────────────────────────────────────── */
function wave(t, off) {
return Math.max(0, Math.min(1,
0.55 + Math.sin(t + off) * 0.25 + Math.sin(t * 1.8 + off * 1.3) * 0.15
));
}
function lerp(a, b, t) { return a + (b - a) * t; }
function isDark() {
return document.documentElement.getAttribute('data-theme') === 'dark';
}
/* ── Hospitality ────────────────────────────────────────── */
function initHospitality(svg, st) {
st.menuPrices = svg.querySelectorAll('.ind-menu-price');
st.menuBars = svg.querySelectorAll('.ind-menu-bar');
st.promoBanner = svg.querySelector('.ind-promo-text');
st.promoPhase = 0;
st.promos = ['HAPPY HOUR 5-7PM', 'NEW: SUMMER MENU', '20% OFF DESSERTS', 'LIVE MUSIC FRI'];
}
function tickHospitality(st) {
// Animate menu prices (shimmer)
for (var i = 0; i < st.menuPrices.length; i++) {
var v = wave(st.phase, i * 1.4);
var price = (5 + Math.round(v * 20)).toFixed(2);
st.menuPrices[i].textContent = '$' + price;
}
// Animate popularity bars
for (var j = 0; j < st.menuBars.length; j++) {
var bv = wave(st.phase * 0.6, j * 1.8);
st.menuBars[j].setAttribute('width', Math.round(bv * 50 + 10));
}
// Cycle promo banner
st.promoPhase += SPEED * 0.3;
if (st.promoPhase > 1) {
st.promoPhase = 0;
if (st.promoBanner) {
var idx = Math.floor(Math.random() * st.promos.length);
st.promoBanner.textContent = st.promos[idx];
}
}
}
/* ── Retail ─────────────────────────────────────────────── */
function initRetail(svg, st) {
st.saleTags = svg.querySelectorAll('.ind-sale-tag');
st.footfall = svg.querySelector('.ind-footfall-val');
st.revBars = svg.querySelectorAll('.ind-rev-bar');
st.productSlots = svg.querySelectorAll('.ind-product-slot');
st.productPhase = 0;
}
function tickRetail(st) {
// Sale tags pulse opacity
for (var i = 0; i < st.saleTags.length; i++) {
var op = 0.5 + wave(st.phase * 1.2, i * 2.0) * 0.5;
st.saleTags[i].setAttribute('opacity', op.toFixed(2));
}
// Footfall counter
if (st.footfall) {
var count = Math.round(wave(st.phase * 0.3, 0) * 450 + 50);
st.footfall.textContent = count;
}
// Revenue bars
for (var j = 0; j < st.revBars.length; j++) {
var rv = wave(st.phase * 0.5, j * 1.5);
var h = Math.round(rv * 35 + 5);
st.revBars[j].setAttribute('height', h);
st.revBars[j].setAttribute('y', 40 - h);
}
// Product slots cycle highlight
st.productPhase += SPEED * 0.15;
if (st.productPhase > 1) {
st.productPhase = 0;
var active = Math.floor(Math.random() * st.productSlots.length);
for (var k = 0; k < st.productSlots.length; k++) {
st.productSlots[k].setAttribute('stroke-width', k === active ? '2' : '0');
}
}
}
/* ── Corporate ──────────────────────────────────────────── */
function initCorporate(svg, st) {
st.kpiVals = svg.querySelectorAll('.ind-kpi-val');
st.kpiArrows = svg.querySelectorAll('.ind-kpi-arrow');
st.meetStatus = svg.querySelector('.ind-meet-status');
st.meetDot = svg.querySelector('.ind-meet-dot');
st.meetPhase = 0;
st.statuses = ['Available', 'In Meeting', 'Reserved 2:30', 'Available'];
st.statusIdx = 0;
st.linePath = svg.querySelector('.ind-corp-line');
st.lineW = 140;
st.linePts = 6;
}
function tickCorporate(st) {
// KPI counters
for (var i = 0; i < st.kpiVals.length; i++) {
var kv = wave(st.phase * 0.6, i * 2.2);
var vals = [
Math.round(kv * 98) + '%',
Math.round(kv * 340 + 60),
Math.round(kv * 50 + 10) + 'ms'
];
if (vals[i]) st.kpiVals[i].textContent = vals[i];
}
// KPI arrows
for (var j = 0; j < st.kpiArrows.length; j++) {
var up = wave(st.phase * 0.6, j * 2.2) > 0.5;
st.kpiArrows[j].setAttribute('transform',
up ? 'rotate(0)' : 'rotate(180)');
st.kpiArrows[j].setAttribute('fill', up ? '#4CAF50' : '#ef4444');
}
// Meeting room status cycle
st.meetPhase += SPEED * 0.15;
if (st.meetPhase > 1) {
st.meetPhase = 0;
st.statusIdx = (st.statusIdx + 1) % st.statuses.length;
if (st.meetStatus) st.meetStatus.textContent = st.statuses[st.statusIdx];
if (st.meetDot) {
st.meetDot.setAttribute('fill',
st.statusIdx === 1 ? '#ef4444' : st.statusIdx === 2 ? '#f59e0b' : '#4CAF50');
}
}
// Line chart
if (st.linePath) {
var d = 'M';
for (var p = 0; p < st.linePts; p++) {
var x = (p / (st.linePts - 1)) * st.lineW;
var y = 10 + (1 - wave(st.phase * 0.8, p * 0.9)) * 30;
d += (p ? ' L' : '') + x.toFixed(1) + ',' + y.toFixed(1);
}
st.linePath.setAttribute('d', d);
}
}
/* ── Education ──────────────────────────────────────────── */
function initEducation(svg, st) {
st.schedSlots = svg.querySelectorAll('.ind-sched-slot');
st.alertBar = svg.querySelector('.ind-alert-bar');
st.alertText = svg.querySelector('.ind-alert-text');
st.alertPhase = 0;
st.alerts = ['Fire Drill 2:00 PM', 'Early Dismissal Fri', 'Gym Closed Today', 'Bus 12 Delayed'];
st.alertIdx = 0;
st.wayfindDots = svg.querySelectorAll('.ind-wf-dot');
st.wfActive = 0;
st.wfPhase = 0;
}
function tickEducation(st) {
// Schedule slots pulse (current class highlight)
for (var i = 0; i < st.schedSlots.length; i++) {
var isCurrent = (Math.floor(st.phase * 0.3) % st.schedSlots.length) === i;
st.schedSlots[i].setAttribute('opacity', isCurrent ? '1' : '0.5');
st.schedSlots[i].setAttribute('stroke', isCurrent ? '#D83302' : 'none');
st.schedSlots[i].setAttribute('stroke-width', isCurrent ? '1.5' : '0');
}
// Alert banner cycle
st.alertPhase += SPEED * 0.12;
if (st.alertPhase > 1) {
st.alertPhase = 0;
st.alertIdx = (st.alertIdx + 1) % st.alerts.length;
if (st.alertText) st.alertText.textContent = st.alerts[st.alertIdx];
}
// Alert bar pulse
if (st.alertBar) {
var ap = 0.6 + Math.sin(st.phase * 2) * 0.4;
st.alertBar.setAttribute('opacity', ap.toFixed(2));
}
// Wayfinding dot sequence
st.wfPhase += SPEED * 0.2;
if (st.wfPhase > 1) {
st.wfPhase = 0;
st.wfActive = (st.wfActive + 1) % Math.max(st.wayfindDots.length, 1);
}
for (var w = 0; w < st.wayfindDots.length; w++) {
st.wayfindDots[w].setAttribute('opacity', w === st.wfActive ? '1' : '0.2');
}
}
/* ── Outdoor Marketplace ────────────────────────────────── */
function initOutdoor(svg, st) {
st.weatherIcon = svg.querySelector('.ind-weather-icon');
st.weatherTemp = svg.querySelector('.ind-weather-temp');
st.weatherPhase = 0;
st.weathers = [
{ icon: '\u2600', temp: '24°C' },
{ icon: '\u26C5', temp: '19°C' },
{ icon: '\u2601', temp: '16°C' },
{ icon: '\u2600', temp: '22°C' }
];
st.weatherIdx = 0;
st.vendorBars = svg.querySelectorAll('.ind-vendor-bar');
st.busyDot = svg.querySelector('.ind-busy-dot');
st.busyLabel = svg.querySelector('.ind-busy-label');
}
function tickOutdoor(st) {
// Weather cycle
st.weatherPhase += SPEED * 0.1;
if (st.weatherPhase > 1) {
st.weatherPhase = 0;
st.weatherIdx = (st.weatherIdx + 1) % st.weathers.length;
var w = st.weathers[st.weatherIdx];
if (st.weatherIcon) st.weatherIcon.textContent = w.icon;
if (st.weatherTemp) st.weatherTemp.textContent = w.temp;
}
// Vendor activity bars
for (var i = 0; i < st.vendorBars.length; i++) {
var bv = wave(st.phase * 0.5, i * 1.6);
var h = Math.round(bv * 25 + 3);
st.vendorBars[i].setAttribute('height', h);
st.vendorBars[i].setAttribute('y', 28 - h);
}
// Busy indicator pulse
if (st.busyDot) {
var busy = wave(st.phase * 0.3, 0) > 0.6;
st.busyDot.setAttribute('fill', busy ? '#ef4444' : '#4CAF50');
if (st.busyLabel) st.busyLabel.textContent = busy ? 'Busy' : 'Quiet';
}
}
/* ── Live Data Displays ─────────────────────────────────── */
function initLiveData(svg, st) {
st.ldBars = svg.querySelectorAll('.ind-ld-bar');
st.ldVals = svg.querySelectorAll('.ind-ld-val');
st.ldLine = svg.querySelector('.ind-ld-line');
st.ldLineW = 110;
st.ldLinePts = 8;
st.ldPieSegs = svg.querySelectorAll('.ind-ld-pie');
st.ldPieR = 22;
st.ldAlertDot = svg.querySelector('.ind-ld-alert');
st.ldAlertText = svg.querySelector('.ind-ld-alert-text');
st.ldAlertPhase = 0;
st.ldAlerts = ['All Systems OK', 'CPU: 72%', 'Latency: 12ms', 'Queue: 340'];
st.ldAlertIdx = 0;
}
function tickLiveData(st) {
// Bars animate
for (var i = 0; i < st.ldBars.length; i++) {
var bv = wave(st.phase, i * 1.1);
var h = Math.round(bv * 30 + 5);
st.ldBars[i].setAttribute('height', h);
st.ldBars[i].setAttribute('y', 35 - h);
}
// Values update
for (var v = 0; v < st.ldVals.length; v++) {
var val = wave(st.phase, v * 1.1);
st.ldVals[v].textContent = Math.round(val * 5000);
}
// Line chart
if (st.ldLine) {
var d = 'M';
for (var p = 0; p < st.ldLinePts; p++) {
var x = (p / (st.ldLinePts - 1)) * st.ldLineW;
var y = 5 + (1 - wave(st.phase * 0.8, p * 0.9)) * 30;
d += (p ? ' L' : '') + x.toFixed(1) + ',' + y.toFixed(1);
}
st.ldLine.setAttribute('d', d);
}
// Pie chart
if (st.ldPieSegs.length) {
var n = st.ldPieSegs.length;
var weights = [], total = 0;
for (var pi = 0; pi < n; pi++) {
var pw = 0.5 + wave(st.phase * 0.4, pi * 2.0) * 0.5;
weights.push(pw);
total += pw;
}
var angle = 0;
for (var pj = 0; pj < n; pj++) {
var sweep = (weights[pj] / total) * 360;
var startA = angle * Math.PI / 180;
var endA = (angle + sweep) * Math.PI / 180;
var large = sweep > 180 ? 1 : 0;
var r = st.ldPieR;
var x1 = Math.sin(startA) * r, y1 = -Math.cos(startA) * r;
var x2 = Math.sin(endA) * r, y2 = -Math.cos(endA) * r;
var path = st.ldPieSegs[pj];
if (path) {
path.setAttribute('d',
'M0,0 L' + x1.toFixed(2) + ',' + y1.toFixed(2) +
' A' + r + ',' + r + ' 0 ' + large + ',1 ' +
x2.toFixed(2) + ',' + y2.toFixed(2) + ' Z');
}
angle += sweep;
}
}
// Alert text cycle
st.ldAlertPhase += SPEED * 0.12;
if (st.ldAlertPhase > 1) {
st.ldAlertPhase = 0;
st.ldAlertIdx = (st.ldAlertIdx + 1) % st.ldAlerts.length;
if (st.ldAlertText) st.ldAlertText.textContent = st.ldAlerts[st.ldAlertIdx];
if (st.ldAlertDot) {
st.ldAlertDot.setAttribute('fill', st.ldAlertIdx === 0 ? '#4CAF50' : '#f59e0b');
}
}
}
/* ── Registry ───────────────────────────────────────────── */
var INDUSTRIES = {
hospitality: { init: initHospitality, tick: tickHospitality },
retail: { init: initRetail, tick: tickRetail },
corporate: { init: initCorporate, tick: tickCorporate },
education: { init: initEducation, tick: tickEducation },
outdoor: { init: initOutdoor, tick: tickOutdoor },
livedata: { init: initLiveData, tick: tickLiveData }
};
/* ── Main loop ──────────────────────────────────────────── */
var instances = [];
function tickAll() {
for (var i = 0; i < instances.length; i++) {
var st = instances[i];
if (st.paused) continue;
st.phase += SPEED * 16;
st.handler.tick(st);
}
requestAnimationFrame(tickAll);
}
function observe(st) {
if (!('IntersectionObserver' in window)) return;
new IntersectionObserver(function (entries) {
for (var i = 0; i < entries.length; i++) {
st.paused = !entries[i].isIntersecting;
}
}, { rootMargin: '200px', threshold: 0.05 }).observe(st.el);
}
var INTERVAL = 4000; // ms between slides
function boot() {
var els = document.querySelectorAll('[data-industry-anim]');
if (!els.length) return;
var stages = document.querySelectorAll('[data-gtv-slideshow]');
if (!stages.length) return;
for (var i = 0; i < els.length; i++) {
var el = els[i];
var type = el.getAttribute('data-industry-anim');
var handler = INDUSTRIES[type];
if (!handler) continue;
if (el._indAnim) continue;
/* Honour reduced-motion show first slide only */
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
var svg = el.querySelector('svg');
if (!svg) continue;
for (var i = 0; i < stages.length; i++) {
initSlideshow(stages[i]);
}
}
var st = {
el: el,
svg: svg,
handler: handler,
phase: Math.random() * Math.PI * 2,
paused: false
};
function initSlideshow(stage) {
var slides = stage.querySelectorAll('.gtv-slide');
if (slides.length < 2) return;
handler.init(svg, st);
observe(st);
el._indAnim = st;
instances.push(st);
var state = { current: 0, paused: false, timer: null };
/* IntersectionObserver pause when off-screen */
if ('IntersectionObserver' in window) {
new IntersectionObserver(function (entries) {
for (var j = 0; j < entries.length; j++) {
state.paused = !entries[j].isIntersecting;
}
if (!state.paused && !state.timer) {
state.timer = setInterval(function () { advance(slides, state); }, INTERVAL);
} else if (state.paused && state.timer) {
clearInterval(state.timer);
state.timer = null;
}
}, { rootMargin: '200px', threshold: 0.05 }).observe(stage);
}
if (instances.length) requestAnimationFrame(tickAll);
/* Start cycling */
state.timer = setInterval(function () { advance(slides, state); }, INTERVAL);
}
function advance(slides, state) {
if (state.paused) return;
slides[state.current].classList.remove('is-active');
state.current = (state.current + 1) % slides.length;
slides[state.current].classList.add('is-active');
}
if (document.readyState === 'loading') {

View File

@@ -1181,6 +1181,7 @@ reg('oribi/platform-row', {
imgAlt: { type: 'string', default: '' },
imgWidth: { type: 'number', default: 300 },
cameraAnim: { type: 'boolean', default: false },
galleryIds: { type: 'array', default: [] },
},
edit: function (props) {
var a = props.attributes, s = props.setAttributes;
@@ -1193,6 +1194,31 @@ reg('oribi/platform-row', {
el(TG, { label: 'Reversed', checked: !!a.reversed, onChange: function(v){s({reversed:v});} }),
el(TG, { label: 'Camera Animation', checked: !!a.cameraAnim, onChange: function(v){s({cameraAnim:v});} })
),
el(PB, { title: 'Gallery TV Slideshow', initialOpen: false },
el(MUC, null,
el(MU, {
onSelect: function(media) {
s({ galleryIds: media.map(function(m){ return m.id; }) });
},
allowedTypes: ['image'],
gallery: true,
multiple: true,
value: a.galleryIds || [],
render: function(ref) {
return el(Frag, null,
a.galleryIds && a.galleryIds.length
? el('div', { style: { marginBottom: '8px' } },
el('p', { style: { margin: '0 0 4px' } }, a.galleryIds.length + ' image(s) selected'),
el(Btn, { variant: 'link', isDestructive: true, onClick: function(){ s({ galleryIds: [] }); } }, 'Clear gallery')
)
: null,
el(Btn, { onClick: ref.open, variant: 'secondary', __next40pxDefaultSize: true },
a.galleryIds && a.galleryIds.length ? 'Edit gallery' : 'Select images for TV slideshow')
);
}
})
)
),
el(PB, { title: 'Visual Image', initialOpen: false },
el(MUC, null,
el(MU, {

View File

@@ -558,12 +558,7 @@ add_action( 'init', function () {
'cameraAnim' => [ 'type' => 'boolean', 'default' => false ],
'neverGoesDark'=> [ 'type' => 'boolean', 'default' => false ],
'brandedAnim' => [ 'type' => 'boolean', 'default' => false ],
'industryHospitality' => [ 'type' => 'boolean', 'default' => false ],
'industryRetail' => [ 'type' => 'boolean', 'default' => false ],
'industryCorporate' => [ 'type' => 'boolean', 'default' => false ],
'industryEducation' => [ 'type' => 'boolean', 'default' => false ],
'industryOutdoor' => [ 'type' => 'boolean', 'default' => false ],
'industryLiveData' => [ 'type' => 'boolean', 'default' => false ],
'galleryIds' => [ 'type' => 'array', 'default' => [], 'items' => [ 'type' => 'number' ] ],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_platform_row',
@@ -1795,475 +1790,38 @@ function oribi_render_platform_row( $a ) {
$visual_html = $ca;
$visual_cls = 'platform-visual has-camera';
/* ── Industry: Hospitality ─────────────────────────────── */
} elseif ( ! empty( $a['industryHospitality'] ) ) {
$visual_html = '<div class="ind-stage" data-industry-anim="hospitality" aria-hidden="true">'
. '<svg viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Hospitality signage environment">'
/* ── Background: restaurant counter area ── */
. '<rect x="0" y="0" width="400" height="300" rx="8" fill="#1a1a2e" />'
. '<rect x="0" y="240" width="400" height="60" rx="0" fill="#12121f" />'
/* ── Wall-mounted menu board (large display) ── */
. '<g transform="translate(20,20)">'
. '<rect x="0" y="0" width="220" height="150" rx="4" fill="#111" stroke="#333" stroke-width="1.5" />'
. '<rect x="6" y="6" width="208" height="138" rx="2" fill="#1c2333" />'
/* Menu header */
. '<rect x="14" y="14" width="80" height="8" rx="2" fill="#D83302" opacity=".8" />'
. '<text x="14" y="35" font-size="7" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif">TODAY\'S SPECIALS</text>'
/* Menu items with prices */
. '<text x="14" y="52" font-size="8" fill="rgba(255,255,255,.7)" font-family="Inter,sans-serif">Grilled Salmon</text>'
. '<text class="ind-menu-price" x="192" y="52" font-size="8" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="end">$18.50</text>'
. '<rect class="ind-menu-bar" x="14" y="55" width="40" height="3" rx="1" fill="#D83302" opacity=".4" />'
. '<text x="14" y="72" font-size="8" fill="rgba(255,255,255,.7)" font-family="Inter,sans-serif">Wagyu Burger</text>'
. '<text class="ind-menu-price" x="192" y="72" font-size="8" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="end">$22.00</text>'
. '<rect class="ind-menu-bar" x="14" y="75" width="55" height="3" rx="1" fill="#D83302" opacity=".4" />'
. '<text x="14" y="92" font-size="8" fill="rgba(255,255,255,.7)" font-family="Inter,sans-serif">Caesar Salad</text>'
. '<text class="ind-menu-price" x="192" y="92" font-size="8" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="end">$12.00</text>'
. '<rect class="ind-menu-bar" x="14" y="95" width="30" height="3" rx="1" fill="#D83302" opacity=".4" />'
. '<text x="14" y="112" font-size="8" fill="rgba(255,255,255,.7)" font-family="Inter,sans-serif">Pasta Carbonara</text>'
. '<text class="ind-menu-price" x="192" y="112" font-size="8" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="end">$15.00</text>'
. '<rect class="ind-menu-bar" x="14" y="115" width="45" height="3" rx="1" fill="#D83302" opacity=".4" />'
/* Promo banner */
. '<rect x="6" y="126" width="208" height="18" fill="#D83302" opacity=".15" />'
. '<text class="ind-promo-text" x="110" y="138" font-size="7" fill="#D83302" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600">HAPPY HOUR 5-7PM</text>'
. '</g>'
/* ── Tablet on counter (POS integration) ── */
. '<g transform="translate(270,60)">'
. '<rect x="0" y="0" width="100" height="140" rx="6" fill="#222" stroke="#444" stroke-width="1" />'
. '<rect x="6" y="8" width="88" height="110" rx="2" fill="#1c2333" />'
. '<circle cx="50" cy="132" r="4" fill="#333" />'
/* POS mini-UI */
. '<rect x="12" y="16" width="76" height="6" rx="1" fill="#00757c" opacity=".6" />'
. '<text x="50" y="38" font-size="6" fill="rgba(255,255,255,.5)" font-family="Inter,sans-serif" text-anchor="middle">Order #47</text>'
. '<rect x="12" y="44" width="76" height="1" fill="rgba(255,255,255,.1)" />'
. '<text x="12" y="56" font-size="5.5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif">1x Salmon</text>'
. '<text x="88" y="56" font-size="5.5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif" text-anchor="end">$18.50</text>'
. '<text x="12" y="66" font-size="5.5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif">2x Lager</text>'
. '<text x="88" y="66" font-size="5.5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif" text-anchor="end">$14.00</text>'
. '<rect x="12" y="74" width="76" height="1" fill="rgba(255,255,255,.1)" />'
. '<text x="12" y="86" font-size="6" fill="rgba(255,255,255,.7)" font-family="Inter,sans-serif" font-weight="600">Total</text>'
. '<text x="88" y="86" font-size="6" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="end" font-weight="600">$32.50</text>'
. '<rect x="12" y="96" width="76" height="16" rx="3" fill="#4CAF50" opacity=".8" />'
. '<text x="50" y="107" font-size="6" fill="#fff" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600">PAY</text>'
. '</g>'
/* ── Ambient: counter top ── */
. '<rect x="0" y="235" width="400" height="3" fill="#333" />'
/* ── Label: wall screen type ── */
. '<text x="130" y="185" font-size="7" fill="rgba(255,255,255,.25)" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600" letter-spacing=".1em">MENU BOARD</text>'
. '<text x="320" y="215" font-size="7" fill="rgba(255,255,255,.25)" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600" letter-spacing=".1em">POS TABLET</text>'
. '</svg></div>';
$visual_cls = 'platform-visual has-industry';
/* ── Industry: Retail ──────────────────────────────────── */
} elseif ( ! empty( $a['industryRetail'] ) ) {
$visual_html = '<div class="ind-stage" data-industry-anim="retail" aria-hidden="true">'
. '<svg viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Retail signage environment">'
. '<rect x="0" y="0" width="400" height="300" rx="8" fill="#1a1a2e" />'
/* ── Large overhead promo display ── */
. '<g transform="translate(20,15)">'
. '<rect x="0" y="0" width="240" height="130" rx="4" fill="#111" stroke="#333" stroke-width="1.5" />'
. '<rect x="5" y="5" width="230" height="120" rx="2" fill="#1c2333" />'
/* Promo content */
. '<rect x="12" y="12" width="60" height="8" rx="2" fill="#D83302" opacity=".8" />'
. '<text x="12" y="36" font-size="11" fill="rgba(255,255,255,.9)" font-family="Inter,sans-serif" font-weight="700">SPRING SALE</text>'
. '<text x="12" y="50" font-size="7" fill="rgba(255,255,255,.5)" font-family="Inter,sans-serif">Up to 40% off selected items</text>'
/* Product grid */
. '<g transform="translate(12,60)">'
. '<rect class="ind-product-slot" x="0" y="0" width="48" height="48" rx="3" fill="#252540" stroke="#D83302" stroke-width="0" />'
. '<rect x="6" y="6" width="36" height="24" rx="2" fill="#333" />'
. '<text class="ind-sale-tag" x="24" y="42" font-size="6" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="middle">$29.99</text>'
. '<rect class="ind-product-slot" x="56" y="0" width="48" height="48" rx="3" fill="#252540" stroke="#D83302" stroke-width="0" />'
. '<rect x="62" y="6" width="36" height="24" rx="2" fill="#333" />'
. '<text class="ind-sale-tag" x="80" y="42" font-size="6" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="middle">$49.99</text>'
. '<rect class="ind-product-slot" x="112" y="0" width="48" height="48" rx="3" fill="#252540" stroke="#D83302" stroke-width="0" />'
. '<rect x="118" y="6" width="36" height="24" rx="2" fill="#333" />'
. '<text class="ind-sale-tag" x="136" y="42" font-size="6" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="middle">$19.99</text>'
. '<rect class="ind-product-slot" x="168" y="0" width="48" height="48" rx="3" fill="#252540" stroke="#D83302" stroke-width="0" />'
. '<rect x="174" y="6" width="36" height="24" rx="2" fill="#333" />'
. '<text class="ind-sale-tag" x="192" y="42" font-size="6" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="middle">$39.99</text>'
. '</g>'
. '</g>'
/* ── Analytics monitor (small) ── */
. '<g transform="translate(280,15)">'
. '<rect x="0" y="0" width="100" height="80" rx="3" fill="#111" stroke="#333" stroke-width="1" />'
. '<rect x="4" y="4" width="92" height="72" rx="2" fill="#1c2333" />'
. '<text x="10" y="16" font-size="6" fill="rgba(255,255,255,.5)" font-family="Inter,sans-serif" font-weight="600">FOOTFALL</text>'
. '<text class="ind-footfall-val" x="10" y="32" font-size="14" fill="#D83302" font-family="Inter,sans-serif" font-weight="700">243</text>'
. '<text x="10" y="42" font-size="5" fill="rgba(255,255,255,.3)" font-family="Inter,sans-serif">visitors today</text>'
/* Revenue mini bars */
. '<g transform="translate(10,48)">'
. '<rect class="ind-rev-bar" x="0" y="35" width="10" height="5" fill="#4CAF50" />'
. '<rect class="ind-rev-bar" x="14" y="30" width="10" height="10" fill="#4CAF50" />'
. '<rect class="ind-rev-bar" x="28" y="25" width="10" height="15" fill="#4CAF50" />'
. '<rect class="ind-rev-bar" x="42" y="20" width="10" height="20" fill="#D83302" />'
. '<rect class="ind-rev-bar" x="56" y="28" width="10" height="12" fill="#4CAF50" />'
. '<rect class="ind-rev-bar" x="70" y="22" width="10" height="18" fill="#4CAF50" />'
. '</g>'
. '</g>'
/* ── Wayfinding pillar (floor standing) ── */
. '<g transform="translate(290,110)">'
. '<rect x="10" y="0" width="80" height="120" rx="3" fill="#111" stroke="#333" stroke-width="1" />'
. '<rect x="14" y="4" width="72" height="112" rx="2" fill="#1c2333" />'
. '<text x="50" y="18" font-size="6" fill="rgba(255,255,255,.5)" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600">FLOOR GUIDE</text>'
. '<text x="22" y="34" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">\u2190 Menswear</text>'
. '<text x="22" y="48" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">\u2192 Electronics</text>'
. '<text x="22" y="62" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">\u2191 Food Court</text>'
. '<text x="22" y="76" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">\u2190 Home &amp; Garden</text>'
. '<rect x="30" y="86" width="40" height="22" rx="3" fill="#D83302" opacity=".2" />'
. '<text x="50" y="100" font-size="6" fill="#D83302" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600">MAP</text>'
. '</g>'
/* ── Shelf edge (store floor) ── */
. '<rect x="0" y="248" width="270" height="52" fill="#12121f" />'
. '<rect x="20" y="250" width="230" height="4" fill="#333" />'
. '<text x="135" y="270" font-size="7" fill="rgba(255,255,255,.25)" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600" letter-spacing=".1em">PROMO DISPLAY</text>'
. '<text x="340" y="245" font-size="7" fill="rgba(255,255,255,.25)" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600" letter-spacing=".1em">WAYFINDING</text>'
. '</svg></div>';
$visual_cls = 'platform-visual has-industry';
/* ── Industry: Corporate Office ─────────────────────────── */
} elseif ( ! empty( $a['industryCorporate'] ) ) {
$visual_html = '<div class="ind-stage" data-industry-anim="corporate" aria-hidden="true">'
. '<svg viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Corporate office signage environment">'
. '<rect x="0" y="0" width="400" height="300" rx="8" fill="#1a1a2e" />'
/* ── Meeting room display (wall-mounted) ── */
. '<g transform="translate(20,20)">'
. '<rect x="0" y="0" width="170" height="120" rx="4" fill="#111" stroke="#333" stroke-width="1.5" />'
. '<rect x="5" y="5" width="160" height="110" rx="2" fill="#1c2333" />'
/* Teams-style header */
. '<rect x="10" y="10" width="150" height="14" rx="2" fill="#5b5fc7" opacity=".3" />'
. '<text x="18" y="20" font-size="6" fill="rgba(255,255,255,.8)" font-family="Inter,sans-serif" font-weight="600">\uD83D\uDCF9 Room: Boardroom A</text>'
/* Status */
. '<circle class="ind-meet-dot" cx="18" cy="38" r="4" fill="#4CAF50" />'
. '<text class="ind-meet-status" x="28" y="41" font-size="7" fill="rgba(255,255,255,.7)" font-family="Inter,sans-serif">Available</text>'
/* Schedule */
. '<text x="10" y="58" font-size="5.5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif">UPCOMING</text>'
. '<text x="10" y="70" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">10:00 - Sprint Planning</text>'
. '<text x="10" y="82" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">13:00 - Design Review</text>'
. '<text x="10" y="94" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">15:30 - All Hands</text>'
. '<rect x="10" y="100" width="150" height="10" rx="2" fill="#4CAF50" opacity=".15" />'
. '<text x="85" y="108" font-size="5.5" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="middle">Book Now</text>'
. '</g>'
/* ── KPI Dashboard (large monitor on stand) ── */
. '<g transform="translate(210,10)">'
. '<rect x="0" y="0" width="170" height="130" rx="4" fill="#111" stroke="#333" stroke-width="1.5" />'
. '<rect x="5" y="5" width="160" height="120" rx="2" fill="#1c2333" />'
. '<text x="12" y="18" font-size="6" fill="rgba(255,255,255,.5)" font-family="Inter,sans-serif" font-weight="600">LIVE DASHBOARD</text>'
/* KPI cards */
. '<g transform="translate(12,26)">'
. '<rect x="0" y="0" width="44" height="32" rx="2" fill="#252540" />'
. '<text x="22" y="11" font-size="5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif" text-anchor="middle">Uptime</text>'
. '<text class="ind-kpi-val" x="22" y="24" font-size="10" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="middle" font-weight="700">99%</text>'
. '</g>'
. '<g transform="translate(62,26)">'
. '<rect x="0" y="0" width="44" height="32" rx="2" fill="#252540" />'
. '<text x="22" y="11" font-size="5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif" text-anchor="middle">Users</text>'
. '<text class="ind-kpi-val" x="22" y="24" font-size="10" fill="#D83302" font-family="Inter,sans-serif" text-anchor="middle" font-weight="700">240</text>'
. '</g>'
. '<g transform="translate(112,26)">'
. '<rect x="0" y="0" width="44" height="32" rx="2" fill="#252540" />'
. '<text x="22" y="11" font-size="5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif" text-anchor="middle">Latency</text>'
. '<text class="ind-kpi-val" x="22" y="24" font-size="10" fill="#f59e0b" font-family="Inter,sans-serif" text-anchor="middle" font-weight="700">22ms</text>'
. '</g>'
/* Trend line */
. '<g transform="translate(12,66)">'
. '<text x="0" y="0" font-size="5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif">REQUESTS / MIN</text>'
. '<line x1="0" y1="10" x2="140" y2="10" stroke="rgba(255,255,255,.06)" stroke-width=".5" />'
. '<line x1="0" y1="25" x2="140" y2="25" stroke="rgba(255,255,255,.06)" stroke-width=".5" />'
. '<line x1="0" y1="40" x2="140" y2="40" stroke="rgba(255,255,255,.06)" stroke-width=".5" />'
. '<path class="ind-corp-line" d="M0,25 L140,25" stroke="#4CAF50" stroke-width="1.5" fill="none" stroke-linecap="round" />'
. '</g>'
/* Monitor stand */
. '<rect x="75" y="130" width="20" height="12" fill="#222" />'
. '<rect x="55" y="140" width="60" height="4" rx="2" fill="#333" />'
. '</g>'
/* ── Announcement ticker (lobby display) ── */
. '<g transform="translate(20,160)">'
. '<rect x="0" y="0" width="360" height="40" rx="4" fill="#111" stroke="#333" stroke-width="1" />'
. '<rect x="4" y="4" width="352" height="32" rx="2" fill="#0d1117" />'
. '<rect x="8" y="8" width="60" height="12" rx="2" fill="#D83302" opacity=".2" />'
. '<text x="38" y="17" font-size="6" fill="#D83302" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600">COMPANY NEWS</text>'
. '<text x="80" y="30" font-size="6.5" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">Q4 Results exceed forecast \u2022 New office opening in March \u2022 Hackathon registrations open</text>'
. '</g>'
/* Labels */
. '<text x="105" y="220" font-size="7" fill="rgba(255,255,255,.25)" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600" letter-spacing=".1em">MEETING ROOM</text>'
. '<text x="295" y="220" font-size="7" fill="rgba(255,255,255,.25)" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600" letter-spacing=".1em">KPI DASHBOARD</text>'
. '</svg></div>';
$visual_cls = 'platform-visual has-industry';
/* ── Industry: Education ───────────────────────────────── */
} elseif ( ! empty( $a['industryEducation'] ) ) {
$visual_html = '<div class="ind-stage" data-industry-anim="education" aria-hidden="true">'
. '<svg viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Education signage environment">'
. '<rect x="0" y="0" width="400" height="300" rx="8" fill="#1a1a2e" />'
/* ── Timetable display (large wall-mount) ── */
. '<g transform="translate(15,15)">'
. '<rect x="0" y="0" width="230" height="160" rx="4" fill="#111" stroke="#333" stroke-width="1.5" />'
. '<rect x="5" y="5" width="220" height="150" rx="2" fill="#1c2333" />'
. '<text x="12" y="18" font-size="7" fill="rgba(255,255,255,.5)" font-family="Inter,sans-serif" font-weight="600">DAILY TIMETABLE</text>'
. '<text x="200" y="18" font-size="6" fill="rgba(255,255,255,.3)" font-family="Inter,sans-serif" text-anchor="end">Tuesday</text>'
/* Column headers */
. '<g transform="translate(12,28)">'
. '<rect x="0" y="0" width="200" height="10" rx="1" fill="rgba(255,255,255,.05)" />'
. '<text x="6" y="7" font-size="5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif">TIME</text>'
. '<text x="50" y="7" font-size="5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif">ROOM</text>'
. '<text x="100" y="7" font-size="5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif">SUBJECT</text>'
. '<text x="170" y="7" font-size="5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif">STATUS</text>'
. '</g>'
/* Schedule rows */
. '<g transform="translate(12,44)">'
. '<rect class="ind-sched-slot" x="0" y="0" width="200" height="18" rx="2" fill="#252540" />'
. '<text x="6" y="12" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">09:00</text>'
. '<text x="50" y="12" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">B201</text>'
. '<text x="100" y="12" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">Mathematics</text>'
. '<circle cx="185" cy="9" r="3" fill="#4CAF50" />'
. '</g>'
. '<g transform="translate(12,66)">'
. '<rect class="ind-sched-slot" x="0" y="0" width="200" height="18" rx="2" fill="#252540" />'
. '<text x="6" y="12" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">10:30</text>'
. '<text x="50" y="12" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">Lab 3</text>'
. '<text x="100" y="12" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">Chemistry</text>'
. '<circle cx="185" cy="9" r="3" fill="#f59e0b" />'
. '</g>'
. '<g transform="translate(12,88)">'
. '<rect class="ind-sched-slot" x="0" y="0" width="200" height="18" rx="2" fill="#252540" />'
. '<text x="6" y="12" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">13:00</text>'
. '<text x="50" y="12" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">Hall A</text>'
. '<text x="100" y="12" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">Literature</text>'
. '<circle cx="185" cy="9" r="3" fill="#4CAF50" />'
. '</g>'
. '<g transform="translate(12,110)">'
. '<rect class="ind-sched-slot" x="0" y="0" width="200" height="18" rx="2" fill="#252540" />'
. '<text x="6" y="12" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">14:30</text>'
. '<text x="50" y="12" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">Gym</text>'
. '<text x="100" y="12" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">PE</text>'
. '<circle cx="185" cy="9" r="3" fill="#4CAF50" />'
. '</g>'
. '<g transform="translate(12,132)">'
. '<rect class="ind-sched-slot" x="0" y="0" width="200" height="18" rx="2" fill="#252540" />'
. '<text x="6" y="12" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">16:00</text>'
. '<text x="50" y="12" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">C105</text>'
. '<text x="100" y="12" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">Art</text>'
. '<circle cx="185" cy="9" r="3" fill="#4CAF50" />'
. '</g>'
. '</g>'
/* ── Campus wayfinding (floor directory) ── */
. '<g transform="translate(260,15)">'
. '<rect x="0" y="0" width="120" height="100" rx="3" fill="#111" stroke="#333" stroke-width="1" />'
. '<rect x="4" y="4" width="112" height="92" rx="2" fill="#1c2333" />'
. '<text x="60" y="16" font-size="6" fill="rgba(255,255,255,.5)" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600">CAMPUS MAP</text>'
/* Simplified building outlines with wayfinding dots */
. '<rect x="15" y="24" width="30" height="20" rx="2" fill="#252540" stroke="rgba(255,255,255,.1)" stroke-width=".5" />'
. '<rect x="55" y="28" width="25" height="16" rx="2" fill="#252540" stroke="rgba(255,255,255,.1)" stroke-width=".5" />'
. '<rect x="90" y="22" width="18" height="24" rx="2" fill="#252540" stroke="rgba(255,255,255,.1)" stroke-width=".5" />'
. '<rect x="30" y="54" width="45" height="18" rx="2" fill="#252540" stroke="rgba(255,255,255,.1)" stroke-width=".5" />'
. '<circle class="ind-wf-dot" cx="30" cy="34" r="3" fill="#D83302" />'
. '<circle class="ind-wf-dot" cx="67" cy="36" r="3" fill="#D83302" opacity=".2" />'
. '<circle class="ind-wf-dot" cx="99" cy="34" r="3" fill="#D83302" opacity=".2" />'
. '<circle class="ind-wf-dot" cx="52" cy="63" r="3" fill="#D83302" opacity=".2" />'
. '<text x="60" y="86" font-size="5" fill="rgba(255,255,255,.3)" font-family="Inter,sans-serif" text-anchor="middle">You are here \u2022</text>'
. '</g>'
/* ── Emergency alert banner ── */
. '<g transform="translate(260,130)">'
. '<rect class="ind-alert-bar" x="0" y="0" width="120" height="30" rx="3" fill="#ef4444" opacity=".7" />'
. '<text x="10" y="12" font-size="5" fill="#fff" font-family="Inter,sans-serif" font-weight="700">\u26A0 ALERT</text>'
. '<text class="ind-alert-text" x="10" y="23" font-size="5.5" fill="rgba(255,255,255,.9)" font-family="Inter,sans-serif">Fire Drill 2:00 PM</text>'
. '</g>'
/* Labels */
. '<text x="130" y="195" font-size="7" fill="rgba(255,255,255,.25)" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600" letter-spacing=".1em">TIMETABLE</text>'
. '<text x="320" y="180" font-size="7" fill="rgba(255,255,255,.25)" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600" letter-spacing=".1em">WAYFINDING</text>'
. '</svg></div>';
$visual_cls = 'platform-visual has-industry';
/* ── Industry: Outdoor Marketplace ─────────────────────── */
} elseif ( ! empty( $a['industryOutdoor'] ) ) {
$visual_html = '<div class="ind-stage" data-industry-anim="outdoor" aria-hidden="true">'
. '<svg viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Outdoor marketplace signage environment">'
. '<rect x="0" y="0" width="400" height="300" rx="8" fill="#1a1a2e" />'
/* ── Market stall canopy ── */
. '<path d="M30,100 L200,80 L370,100 L370,110 L30,110 Z" fill="#252540" />'
. '<path d="M30,100 L200,80 L370,100" fill="none" stroke="#D83302" stroke-width="1.5" opacity=".4" />'
/* ── Overhead digital sign (weather resistant) ── */
. '<g transform="translate(60,20)">'
. '<rect x="0" y="0" width="280" height="55" rx="4" fill="#111" stroke="#00757c" stroke-width="1.5" />'
. '<rect x="4" y="4" width="272" height="47" rx="2" fill="#1c2333" />'
/* Weather widget */
. '<text class="ind-weather-icon" x="22" y="32" font-size="20" text-anchor="middle">\u2600</text>'
. '<text class="ind-weather-temp" x="22" y="46" font-size="7" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif" text-anchor="middle">24\u00B0C</text>'
/* Market info */
. '<text x="55" y="18" font-size="6" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif" font-weight="600">GREENFIELD MARKET</text>'
. '<text x="55" y="30" font-size="8" fill="rgba(255,255,255,.8)" font-family="Inter,sans-serif" font-weight="700">Fresh Produce \u2022 Artisan Goods</text>'
. '<text x="55" y="42" font-size="6" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif">Open Today 8AM \u2013 4PM</text>'
/* Busy indicator */
. '<circle class="ind-busy-dot" cx="252" cy="25" r="5" fill="#4CAF50" />'
. '<text class="ind-busy-label" x="252" y="40" font-size="5" fill="rgba(255,255,255,.5)" font-family="Inter,sans-serif" text-anchor="middle">Quiet</text>'
. '</g>'
/* ── Vendor tablet (on counter) ── */
. '<g transform="translate(40,120)">'
. '<rect x="0" y="0" width="140" height="100" rx="4" fill="#111" stroke="#333" stroke-width="1" />'
. '<rect x="4" y="4" width="132" height="92" rx="2" fill="#1c2333" />'
. '<text x="10" y="16" font-size="6" fill="rgba(255,255,255,.5)" font-family="Inter,sans-serif" font-weight="600">VENDOR: FARM FRESH</text>'
. '<rect x="10" y="22" width="120" height="1" fill="rgba(255,255,255,.1)" />'
/* Product categories */
. '<text x="10" y="36" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">Tomatoes</text>'
. '<text x="110" y="36" font-size="6" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="end">$3/kg</text>'
. '<text x="10" y="48" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">Strawberries</text>'
. '<text x="110" y="48" font-size="6" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="end">$5/punnet</text>'
. '<text x="10" y="60" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">Sourdough</text>'
. '<text x="110" y="60" font-size="6" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="end">$8/loaf</text>'
/* Activity mini chart */
. '<text x="10" y="76" font-size="5" fill="rgba(255,255,255,.3)" font-family="Inter,sans-serif" font-weight="600">SALES TODAY</text>'
. '<g transform="translate(10,80)">'
. '<rect class="ind-vendor-bar" x="0" y="25" width="12" height="3" fill="#D83302" />'
. '<rect class="ind-vendor-bar" x="16" y="20" width="12" height="8" fill="#D83302" />'
. '<rect class="ind-vendor-bar" x="32" y="15" width="12" height="13" fill="#D83302" />'
. '<rect class="ind-vendor-bar" x="48" y="10" width="12" height="18" fill="#4CAF50" />'
. '<rect class="ind-vendor-bar" x="64" y="18" width="12" height="10" fill="#D83302" />'
. '<rect class="ind-vendor-bar" x="80" y="12" width="12" height="16" fill="#D83302" />'
. '<rect class="ind-vendor-bar" x="96" y="8" width="12" height="20" fill="#4CAF50" />'
. '</g>'
. '</g>'
/* ── Second display event board (standing) ── */
. '<g transform="translate(220,120)">'
. '<rect x="0" y="0" width="140" height="100" rx="3" fill="#111" stroke="#333" stroke-width="1" />'
. '<rect x="4" y="4" width="132" height="92" rx="2" fill="#1c2333" />'
. '<text x="70" y="16" font-size="6" fill="rgba(255,255,255,.5)" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600">UPCOMING EVENTS</text>'
. '<rect x="10" y="22" width="120" height="1" fill="rgba(255,255,255,.1)" />'
. '<text x="10" y="36" font-size="6" fill="#D83302" font-family="Inter,sans-serif">\u2022 Live Music 12:00</text>'
. '<text x="10" y="48" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">\u2022 Cooking Demo 1:30</text>'
. '<text x="10" y="60" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">\u2022 Kids Workshop 2:00</text>'
. '<text x="10" y="72" font-size="6" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">\u2022 Prize Draw 3:30</text>'
. '<rect x="10" y="78" width="120" height="12" rx="2" fill="#00757c" opacity=".2" />'
. '<text x="70" y="87" font-size="5.5" fill="#00757c" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600">VIEW FULL SCHEDULE</text>'
. '</g>'
/* Ground/grass hint */
. '<rect x="0" y="250" width="400" height="50" fill="#12121f" />'
. '<text x="110" y="245" font-size="7" fill="rgba(255,255,255,.25)" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600" letter-spacing=".1em">VENDOR DISPLAY</text>'
. '<text x="290" y="245" font-size="7" fill="rgba(255,255,255,.25)" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600" letter-spacing=".1em">EVENT BOARD</text>'
. '</svg></div>';
$visual_cls = 'platform-visual has-industry';
/* ── Industry: Live Data Displays ──────────────────────── */
} elseif ( ! empty( $a['industryLiveData'] ) ) {
$visual_html = '<div class="ind-stage" data-industry-anim="livedata" aria-hidden="true">'
. '<svg viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Live data display environment">'
. '<defs>'
. '<linearGradient id="indLdGrad" 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>'
. '</defs>'
. '<rect x="0" y="0" width="400" height="300" rx="8" fill="#1a1a2e" />'
/* ── Monitor 1: Bar chart (left) ── */
. '<g transform="translate(10,15)">'
. '<rect x="0" y="0" width="125" height="130" rx="4" fill="#111" stroke="#333" stroke-width="1.5" />'
. '<rect x="4" y="4" width="117" height="122" rx="2" fill="#1c2333" />'
. '<text x="10" y="16" font-size="5.5" fill="rgba(255,255,255,.5)" font-family="Inter,sans-serif" font-weight="600">THROUGHPUT</text>'
. '<g transform="translate(10,22)">'
. '<rect class="ind-ld-bar" x="0" y="30" width="12" height="5" fill="url(#indLdGrad)" />'
. '<rect class="ind-ld-bar" x="16" y="25" width="12" height="10" fill="url(#indLdGrad)" />'
. '<rect class="ind-ld-bar" x="32" y="20" width="12" height="15" fill="url(#indLdGrad)" />'
. '<rect class="ind-ld-bar" x="48" y="15" width="12" height="20" fill="url(#indLdGrad)" />'
. '<rect class="ind-ld-bar" x="64" y="22" width="12" height="13" fill="url(#indLdGrad)" />'
. '<rect class="ind-ld-bar" x="80" y="18" width="12" height="17" fill="url(#indLdGrad)" />'
. '<rect class="ind-ld-bar" x="96" y="12" width="12" height="23" fill="url(#indLdGrad)" />'
. '</g>'
. '<g transform="translate(10,62)">'
. '<text class="ind-ld-val" x="6" y="0" font-size="5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif" text-anchor="middle">0</text>'
. '<text class="ind-ld-val" x="22" y="0" font-size="5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif" text-anchor="middle">0</text>'
. '<text class="ind-ld-val" x="38" y="0" font-size="5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif" text-anchor="middle">0</text>'
. '<text class="ind-ld-val" x="54" y="0" font-size="5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif" text-anchor="middle">0</text>'
. '<text class="ind-ld-val" x="70" y="0" font-size="5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif" text-anchor="middle">0</text>'
. '<text class="ind-ld-val" x="86" y="0" font-size="5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif" text-anchor="middle">0</text>'
. '<text class="ind-ld-val" x="102" y="0" font-size="5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif" text-anchor="middle">0</text>'
. '</g>'
/* Status line */
. '<g transform="translate(10,76)">'
. '<circle class="ind-ld-alert" cx="4" cy="4" r="3" fill="#4CAF50" />'
. '<text class="ind-ld-alert-text" x="12" y="7" font-size="5.5" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">All Systems OK</text>'
. '</g>'
/* Line chart below */
. '<g transform="translate(10,90)">'
. '<text x="0" y="0" font-size="5" fill="rgba(255,255,255,.3)" font-family="Inter,sans-serif">LATENCY</text>'
. '<line x1="0" y1="8" x2="100" y2="8" stroke="rgba(255,255,255,.05)" stroke-width=".5" />'
. '<line x1="0" y1="20" x2="100" y2="20" stroke="rgba(255,255,255,.05)" stroke-width=".5" />'
. '<line x1="0" y1="32" x2="100" y2="32" stroke="rgba(255,255,255,.05)" stroke-width=".5" />'
. '<path class="ind-ld-line" d="M0,20 L110,20" stroke="#4CAF50" stroke-width="1.5" fill="none" stroke-linecap="round" />'
. '</g>'
/* Monitor stand */
. '<rect x="50" y="130" width="25" height="10" fill="#222" />'
. '<rect x="35" y="138" width="55" height="3" rx="1" fill="#333" />'
. '</g>'
/* ── Monitor 2: Pie chart + KPIs (center) ── */
. '<g transform="translate(145,15)">'
. '<rect x="0" y="0" width="110" height="130" rx="4" fill="#111" stroke="#333" stroke-width="1.5" />'
. '<rect x="4" y="4" width="102" height="122" rx="2" fill="#1c2333" />'
. '<text x="10" y="16" font-size="5.5" fill="rgba(255,255,255,.5)" font-family="Inter,sans-serif" font-weight="600">DISTRIBUTION</text>'
/* Pie chart */
. '<g transform="translate(55,56)">'
. '<path class="ind-ld-pie" d="M0,0 L0,-22 A22,22 0 0,1 15.56,-15.56 Z" fill="#D83302" opacity=".9"/>'
. '<path class="ind-ld-pie" d="M0,0 L15.56,-15.56 A22,22 0 0,1 22,0 Z" fill="#4CAF50" opacity=".8"/>'
. '<path class="ind-ld-pie" d="M0,0 L22,0 A22,22 0 0,1 0,22 Z" fill="#f59e0b" opacity=".7"/>'
. '<path class="ind-ld-pie" d="M0,0 L0,22 A22,22 0 0,1 -22,0 Z" fill="#5b5fc7" opacity=".7"/>'
. '<path class="ind-ld-pie" d="M0,0 L-22,0 A22,22 0 0,1 0,-22 Z" fill="#ef4444" opacity=".6"/>'
. '<circle cx="0" cy="0" r="10" fill="#1c2333" stroke="rgba(255,255,255,.1)" stroke-width=".5"/>'
. '</g>'
/* Legend */
. '<g transform="translate(10,88)">'
. '<rect x="0" y="0" width="5" height="5" fill="#D83302"/><text x="8" y="5" font-size="4.5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif">API</text>'
. '<rect x="30" y="0" width="5" height="5" fill="#4CAF50"/><text x="38" y="5" font-size="4.5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif">DB</text>'
. '<rect x="58" y="0" width="5" height="5" fill="#f59e0b"/><text x="66" y="5" font-size="4.5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif">Cache</text>'
. '<rect x="0" y="12" width="5" height="5" fill="#5b5fc7"/><text x="8" y="17" font-size="4.5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif">Queue</text>'
. '<rect x="40" y="12" width="5" height="5" fill="#ef4444"/><text x="48" y="17" font-size="4.5" fill="rgba(255,255,255,.4)" font-family="Inter,sans-serif">Worker</text>'
. '</g>'
/* Monitor stand */
. '<rect x="40" y="130" width="30" height="10" fill="#222" />'
. '<rect x="25" y="138" width="60" height="3" rx="1" fill="#333" />'
. '</g>'
/* ── Monitor 3: Alert feed (right) ── */
. '<g transform="translate(265,15)">'
. '<rect x="0" y="0" width="125" height="130" rx="4" fill="#111" stroke="#333" stroke-width="1.5" />'
. '<rect x="4" y="4" width="117" height="122" rx="2" fill="#1c2333" />'
. '<text x="10" y="16" font-size="5.5" fill="rgba(255,255,255,.5)" font-family="Inter,sans-serif" font-weight="600">SYSTEM STATUS</text>'
/* Status rows */
. '<g transform="translate(10,24)">'
. '<circle cx="5" cy="4" r="3" fill="#4CAF50"/>'
. '<text x="12" y="7" font-size="5.5" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">API Gateway</text>'
. '<text x="100" y="7" font-size="5" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="end">OK</text>'
. '</g>'
. '<g transform="translate(10,38)">'
. '<circle cx="5" cy="4" r="3" fill="#4CAF50"/>'
. '<text x="12" y="7" font-size="5.5" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">Database</text>'
. '<text x="100" y="7" font-size="5" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="end">OK</text>'
. '</g>'
. '<g transform="translate(10,52)">'
. '<circle cx="5" cy="4" r="3" fill="#f59e0b"/>'
. '<text x="12" y="7" font-size="5.5" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">Cache Layer</text>'
. '<text x="100" y="7" font-size="5" fill="#f59e0b" font-family="Inter,sans-serif" text-anchor="end">WARN</text>'
. '</g>'
. '<g transform="translate(10,66)">'
. '<circle cx="5" cy="4" r="3" fill="#4CAF50"/>'
. '<text x="12" y="7" font-size="5.5" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">CDN</text>'
. '<text x="100" y="7" font-size="5" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="end">OK</text>'
. '</g>'
. '<g transform="translate(10,80)">'
. '<circle cx="5" cy="4" r="3" fill="#4CAF50"/>'
. '<text x="12" y="7" font-size="5.5" fill="rgba(255,255,255,.6)" font-family="Inter,sans-serif">Workers</text>'
. '<text x="100" y="7" font-size="5" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="end">OK</text>'
. '</g>'
/* Uptime badge */
. '<rect x="10" y="95" width="97" height="18" rx="3" fill="#252540" />'
. '<text x="58" y="107" font-size="7" fill="#4CAF50" font-family="Inter,sans-serif" text-anchor="middle" font-weight="700">99.98% Uptime</text>'
/* Monitor stand */
. '<rect x="48" y="130" width="29" height="10" fill="#222" />'
. '<rect x="33" y="138" width="59" height="3" rx="1" fill="#333" />'
. '</g>'
/* ── Desk surface ── */
. '<rect x="0" y="160" width="400" height="4" fill="#333" />'
/* Labels */
. '<text x="72" y="180" font-size="7" fill="rgba(255,255,255,.25)" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600" letter-spacing=".1em">THROUGHPUT</text>'
. '<text x="200" y="180" font-size="7" fill="rgba(255,255,255,.25)" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600" letter-spacing=".1em">DISTRIBUTION</text>'
. '<text x="327" y="180" font-size="7" fill="rgba(255,255,255,.25)" font-family="Inter,sans-serif" text-anchor="middle" font-weight="600" letter-spacing=".1em">SYSTEM STATUS</text>'
. '</svg></div>';
$visual_cls = 'platform-visual has-industry';
/* ── 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'] ?? '' );
@@ -2283,7 +1841,6 @@ function oribi_render_platform_row( $a ) {
</div>
<?php return ob_get_clean();
}
/* ── Trust Section (parent - wraps child trust-item blocks) ────────────────── */
function oribi_render_trust_section( $a, $content ) {
ob_start(); ?>

View File

@@ -38,9 +38,9 @@ add_action( 'wp_enqueue_scripts', function () {
true
);
// Industry mockup animator - animated device mockups for solutions page
// Gallery TV slideshow - cycles images in TV-frame cards
wp_enqueue_script(
'oribi-industry-animator',
'oribi-gallery-tv',
ORIBI_URI . '/assets/js/industry-animator.js',
[],
ORIBI_VERSION . '.' . filemtime( ORIBI_DIR . '/assets/js/industry-animator.js' ),