Compare commits

..

1 Commits

Author SHA1 Message Date
ec3f175f72 Sync: update features from WordPress 2026-02-21 01:20:43 -05:00
8 changed files with 27 additions and 1105 deletions

View File

@@ -1,18 +1,17 @@
<?php
/**
/*
* Title: Features
* Slug: ots-signs/page-features
* Categories: oribi-pages
* Keywords: features, platform, content scheduling, cloud CMS, real-time data
* Post Types: page
* Slug: features
* Post Type: page
*/
?>
return <<<'ORIBI_SYNC_CONTENT'
<!-- wp:oribi/page-hero-animated {"label":"Platform","title":"Powerful Signage, Refreshingly Simple","description":"A cloud-native CMS that lets you publish content to any screen in seconds. Built-in scheduling, live data feeds, and real-time analytics — all wrapped in an interface your whole team can use."} /-->
<!-- wp:oribi/platform-section {"label":"Core Features","heading":"Everything You Need, Nothing You Don\u0027t","lead":"Create, schedule, and manage digital signage content from a single dashboard — whether you have one screen or one thousand."} -->
<!-- wp:oribi/platform-section {"label":"Core Features","heading":"Everything You Need, Nothing You Donu0027t","lead":"Create, schedule, and manage digital signage content from a single dashboard — whether you have one screen or one thousand."} -->
<!-- wp:oribi/platform-row {"heading":"One Dashboard for Every Display","description":"Manage your entire signage network from a single cloud-based console. Organise screens by location, group, or purpose. Push content updates across your whole estate in one click — no matter how many sites you operate.","btnText":"Get Started","btnUrl":"/contact"} /-->
<!-- wp:oribi/platform-row {"heading":"Scheduling That Runs Itself","description":"Set content to appear at the right time, in the right place, automatically. Day-parting, date ranges, and event-triggered playback let you plan weeks ahead while the platform handles the execution.","btnText":"See Pricing","btnUrl":"/pricing","reversed":true} /-->
<!-- wp:oribi/platform-row {"heading":"Works With Your Existing Screens","description":"Our player devices connect to any screen with an HDMI port — no proprietary hardware required. Already have displays? Plug in and go. Need a full setup? We offer bundled player-and-display packages too.","btnText":"View Devices","btnUrl":"/devices","tvStick":true} /-->
<!-- wp:oribi/platform-row {"heading":"Works With Your Existing Screens","description":"Our player devices connect to any screen with an HDMI port — no proprietary hardware required. Already have displays? Plug in and go. Need a full setup? We offer bundled player-and-display packages too.","btnText":"View Devices","btnUrl":"/devices"} /-->
<!-- wp:oribi/platform-row {"heading":"Live Data, Straight to Screen","description":"Pull in web dashboards, social feeds, KPIs, and real-time APIs directly to your displays. Content updates automatically — no manual refreshing, no extra steps. Turn any screen into a live information hub.","btnText":"Learn More","btnUrl":"/solutions","reversed":true} /-->
<!-- /wp:oribi/platform-section -->
@@ -21,14 +20,15 @@
<!-- wp:oribi/feature-card {"iconType":"fontawesome","faIcon":"fas fa-clock","title":"Intelligent Scheduling","description":"Day-parting, date-based playlists, and event triggers let you automate content rotation down to the minute."} /-->
<!-- wp:oribi/feature-card {"iconType":"fontawesome","faIcon":"fas fa-wifi","title":"Offline Playback","description":"Content is cached on the player device. If your connection drops, your displays keep running seamlessly until it returns."} /-->
<!-- wp:oribi/feature-card {"iconType":"fontawesome","faIcon":"fas fa-users","title":"Unlimited Users","description":"Invite your entire team at no extra cost. No per-seat charges, no access restrictions — on any plan."} /-->
<!-- wp:oribi/feature-card {"iconType":"fontawesome","faIcon":"fas fa-chart-pie","title":"Playback Analytics","description":"Track what\u0027s playing, where, and when. Proof of Play reporting, screen health monitoring, and content logs give you full visibility — with retention depth that scales with your plan."} /-->
<!-- wp:oribi/feature-card {"iconType":"fontawesome","faIcon":"fas fa-chart-pie","title":"Playback Analytics","description":"Track whatu0027s playing, where, and when. Proof of Play reporting, screen health monitoring, and content logs give you full visibility — with retention depth that scales with your plan."} /-->
<!-- wp:oribi/feature-card {"iconType":"fontawesome","faIcon":"fas fa-shield-halved","title":"Enterprise Security","description":"End-to-end encryption, two-factor authentication, secure boot hardware, and predefined user roles on every plan. Pro adds SSO via SAML or CAS, custom role definitions, and extended audit trails."} /-->
<!-- /wp:oribi/feature-section -->
<!-- wp:oribi/value-section {"variant":"normal","label":"Why Choose Us","heading":"Beyond the Software","lead":"Great signage takes more than a CMS. Here\u0027s what you get when you work with OTS Signs.","columns":3} -->
<!-- wp:oribi/value-section {"variant":"normal","label":"Why Choose Us","heading":"Beyond the Software","lead":"Great signage takes more than a CMS. Hereu0027s what you get when you work with OTS Signs.","columns":3} -->
<!-- wp:oribi/value-card {"iconType":"fontawesome","faIcon":"fas fa-camera","title":"In-House Creative","description":"Professional photography, video production, and graphic design — so every screen looks polished and on-brand from day one."} /-->
<!-- wp:oribi/value-card {"iconType":"fontawesome","faIcon":"fas fa-rocket","title":"Instant Publishing","description":"Upload your content and push it live across your entire network in seconds. No queues, no waiting, no complex approval chains."} /-->
<!-- wp:oribi/value-card {"iconType":"fontawesome","faIcon":"fas fa-headset","title":"Support That\u0027s There","description":"From initial setup to ongoing optimisation, our team is available when you need us — not hidden behind a ticket queue."} /-->
<!-- wp:oribi/value-card {"iconType":"fontawesome","faIcon":"fas fa-headset","title":"Support Thatu0027s There","description":"From initial setup to ongoing optimisation, our team is available when you need us — not hidden behind a ticket queue."} /-->
<!-- /wp:oribi/value-section -->
<!-- wp:oribi/cta-banner {"heading":"Try the Platform Yourself","text":"Get hands-on access to a live demo instance and see every feature in action — no commitment, no credit card.","btnText":"Request Demo","btnUrl":"/demo"} /-->
ORIBI_SYNC_CONTENT;

View File

@@ -11,9 +11,9 @@ return <<<'ORIBI_SYNC_CONTENT'
<!-- wp:oribi/platform-section {"label":"The Complete Package","heading":"Everything You Need for Engaging Digital Signage","lead":"High-quality visuals, real-time data, and reliable playback — all managed from one powerful platform."} -->
<!-- wp:oribi/platform-row {"heading":"Professional Content Creation","description":"Our in-house photography and video production services showcase your products, services, and environment with polished, engaging visuals. From digital menu boards to branded promotions, we create content that captures attention.","btnText":"See Features","btnUrl":"/features"} /-->
<!-- wp:oribi/platform-row {"heading":"Live Data u0026amp; Web Dashboards","description":"Integrate your existing web dashboards, social feeds, and real-time data sources directly to your displays. Bring your most important information to life on screen, automatically and effortlessly.","btnUrl":"/features","reversed":true,"isDashboard":true} /-->
<!-- wp:oribi/platform-row {"heading":"Live Data \u0026amp; Web Dashboards","description":"Integrate your existing web dashboards, social feeds, and real-time data sources directly to your displays. Bring your most important information to life on screen, automatically and effortlessly.","btnUrl":"/features","reversed":true} /-->
<!-- wp:oribi/platform-row {"heading":"Reliable on Any Screen","description":"Our intelligent player devices work on any screen with HDMI, and keep your message running even when the internet goes down. Enterprise-grade hardware designed for uninterrupted, always-on signage.","btnText":"View Devices","btnUrl":"/devices","deviceAnim":true} /-->
<!-- wp:oribi/platform-row {"heading":"Reliable on Any Screen","description":"Our intelligent player devices work on any screen with HDMI, and keep your message running even when the internet goes down. Enterprise-grade hardware designed for uninterrupted, always-on signage.","btnText":"View Devices","btnUrl":"/devices"} /-->
<!-- /wp:oribi/platform-section -->
<!-- wp:oribi/feature-section {"variant":"alt","label":"Who It's For","heading":"Solutions for Every Industry","lead":"Modern businesses need real-time communication. Digital signage helps you connect, inform, and engage.","columns":4} -->

View File

@@ -1,32 +1,28 @@
<?php
/*
/**
* Title: Pricing
* Slug: pricing
* Post Type: page
* Slug: ots-signs/page-pricing
* Categories: oribi-pages
* Keywords: pricing, plans, affordable, scalable, essentials, pro
* Post Types: page
*/
return <<<'ORIBI_SYNC_CONTENT'
?>
<!-- wp:oribi/page-hero-animated {"label":"Pricing","title":"Straightforward Pricing, No Surprises","description":"Every plan includes the full content engine. Scale your infrastructure, integrations, and support as you grow. No hidden fees, no per-user charges."} /-->
<!-- wp:oribi/value-section {"label":"Included on Every Plan","heading":"The Full Content Engine, From Day One","lead":"Whether you choose Essentials or Pro, your team gets the same powerful tools to create, schedule, and publish.","columns":4} -->
<!-- wp:oribi/value-section {"variant":"normal","label":"Included on Every Plan","heading":"The Full Content Engine, From Day One","lead":"Whether you choose Essentials or Pro, your team gets the same powerful tools to create, schedule, and publish.","columns":4} -->
<!-- wp:oribi/value-card {"iconType":"fontawesome","faIcon":"fas fa-clock","title":"Automated Scheduling","description":"Day-parting, date ranges, and recurring schedules — your content plays at exactly the right time, automatically."} /-->
<!-- wp:oribi/value-card {"iconType":"fontawesome","faIcon":"fas fa-rss","title":"Live Data to Screen","description":"Pull DataSets, RSS feeds, social widgets, and embedded HTML directly to your displays — updated in real time."} /-->
<!-- wp:oribi/value-card {"iconType":"fontawesome","faIcon":"fas fa-users","title":"Unlimited Team Access","description":"Invite everyone who needs access. No per-user fees, no seat limits, no gatekeeping."} /-->
<!-- wp:oribi/value-card {"iconType":"fontawesome","faIcon":"fas fa-rocket","title":"Instant Publishing","description":"Upload content and push it live across your network in seconds — not hours."} /-->
<!-- /wp:oribi/value-section -->
<!-- wp:oribi/pricing-section {"variant":"alt","label":"Choose Your Plan","heading":"Scale When Youu0027re Ready","lead":"Start with Essentials and upgrade seamlessly as your network grows. No disruption, no data loss."} -->
<!-- wp:oribi/pricing-card {"name":"Essentials","tagline":"The full content engine for growing networks","price":"$7","pricePer":"per screen / month · or $70/screen annually","features":["Up to 50 screens","Custom subdomain","Shared CMS instance","Content scheduling \u0026 day-parting","DataSets, RSS, social \u0026 embedded widgets","Menu boards \u0026 interactive layouts","Unlimited users with standard roles","Canva integration","Offline playback","Proof of Play analytics (30-day retention)","Email support (next-business-day)"]} /-->
<!-- wp:oribi/pricing-card {"name":"Pro","tagline":"Dedicated infrastructure, enterprise integrations \u0026 white-glove service","price":"Custom","pricePer":"tailored to your network size","features":["Unlimited screens","Custom domain","Dedicated CMS instance","Geo-location \u0026 weather-triggered scheduling","Dashboard Connector \u0026 custom API integrations","Video wall support","Ad campaigns \u0026 SSP monetisation","SSO (SAML/CAS) \u0026 custom user roles","Proof of Play analytics (12+ month retention)","Audience Reporting \u0026 scheduled PDF reports","Priority support (4-hour SLA) \u0026 account manager","Contractual SLA guarantee"],"btnText":"Contact Sales","featured":true,"badge":"Enterprise"} /-->
<!-- wp:oribi/pricing-section {"variant":"alt","label":"Choose Your Plan","heading":"Scale When You\u0027re Ready","lead":"Start with Essentials and upgrade seamlessly as your network grows. No disruption, no data loss."} -->
<!-- wp:oribi/pricing-card {"name":"Essentials","tagline":"The full content engine for growing networks","price":"$7","pricePer":"per screen / month · or $70/screen annually","features":["Up to 50 screens","Custom subdomain","Shared CMS instance","Content scheduling & day-parting","DataSets, RSS, social & embedded widgets","Menu boards & interactive layouts","Unlimited users with standard roles","Canva integration","Offline playback","Proof of Play analytics (30-day retention)","Email support (next-business-day)"],"btnText":"Get Started","btnUrl":"/contact"} /-->
<!-- wp:oribi/pricing-card {"name":"Pro","tagline":"Dedicated infrastructure, enterprise integrations & white-glove service","price":"Custom","pricePer":"tailored to your network size","features":["Unlimited screens","Custom domain","Dedicated CMS instance","Geo-location & weather-triggered scheduling","Dashboard Connector & custom API integrations","Video wall support","Ad campaigns & SSP monetisation","SSO (SAML/CAS) & custom user roles","Proof of Play analytics (12+ month retention)","Audience Reporting & scheduled PDF reports","In-house creative services included","Priority support (4-hour SLA) & account manager","Contractual SLA guarantee"],"btnText":"Contact Sales","btnUrl":"/contact","featured":true,"badge":"Enterprise"} /-->
<!-- /wp:oribi/pricing-section -->
<!-- wp:oribi/comparison-table {"label":"Plan Comparison","heading":"See Exactly Whatu0027s Included","lead":"A full breakdown of what you get on each plan — so there are no surprises.","columns":["Essentials","Pro"],"rows":[{"group":"Scale \u0026 Infrastructure"},{"feature":"Screen limit","values":["Up to 50","Unlimited"]},{"feature":"CMS instance","values":["Shared","Dedicated"]},{"feature":"Custom subdomain","values":[true,true]},{"feature":"Custom domain","values":[false,true]},{"group":"Content \u0026 Scheduling"},{"feature":"Day-parting \u0026 date scheduling","values":[true,true]},{"feature":"Playlists \u0026 campaigns","values":[true,true]},{"feature":"Menu boards","values":[true,true]},{"feature":"Interactive touchscreen actions","values":[true,true]},{"feature":"Overlay layouts","values":[true,true]},{"feature":"Geo-location scheduling","values":[false,true]},{"feature":"Weather-triggered scheduling","values":[false,true]},{"feature":"Video wall","values":[false,true]},{"feature":"Ad campaigns \u0026 plays-per-hour control","values":[false,true]},{"group":"Data \u0026 Integrations"},{"feature":"DataSets, RSS \u0026 tickers","values":[true,true]},{"feature":"Embedded HTML \u0026 web pages","values":[true,true]},{"feature":"Social feeds","values":[true,true]},{"feature":"Canva integration","values":[true,true]},{"feature":"Dashboard Connector","values":[false,true]},{"feature":"Custom API integrations","values":[false,true]},{"feature":"SSP Connector (ad monetisation)","values":[false,true]},{"group":"Analytics \u0026 Reporting"},{"feature":"Proof of Play reporting","values":["30-day retention","12+ month retention"]},{"feature":"Scheduled PDF reports","values":[false,true]},{"feature":"Audience Reporting","values":[false,true]},{"feature":"Display health monitoring","values":[true,true]},{"group":"Users \u0026 Security"},{"feature":"Unlimited user seats","values":[true,true]},{"feature":"Predefined roles (admin/editor/viewer)","values":[true,true]},{"feature":"Custom user roles \u0026 feature access","values":[false,true]},{"feature":"Two-factor authentication","values":[true,true]},{"feature":"SSO (SAML / CAS)","values":[false,true]},{"feature":"Audit trail","values":["7-day retention","Extended retention"]},{"group":"Display Management"},{"feature":"Screen power on/off control","values":[true,true]},{"feature":"Offline playback","values":[true,true]},{"feature":"Portrait / landscape","values":[true,true]},{"feature":"Email alerts (player offline)","values":[true,true]},{"feature":"Periodic screenshots","values":[false,true]},{"feature":"Display map view","values":[false,true]},{"feature":"Shell commands \u0026 RS232","values":[false,true]},{"group":"Support \u0026 Services"},{"feature":"Email support","values":["Next-business-day",true]},{"feature":"Priority support","values":[false,"4-hour SLA"]},{"feature":"Dedicated account manager","values":[false,true]},{"feature":"In-house creative services","values":[false,"Included hours"]},{"feature":"White-glove onboarding","values":[false,true]}]} /-->
<!-- wp:oribi/comparison-table {"variant":"normal","label":"Plan Comparison","heading":"See Exactly What\u0027s Included","lead":"A full breakdown of what you get on each plan — so there are no surprises.","columns":["Essentials","Pro"],"rows":[{"group":"Scale & Infrastructure"},{"feature":"Screen limit","values":["Up to 50","Unlimited"]},{"feature":"CMS instance","values":["Shared","Dedicated"]},{"feature":"Custom subdomain","values":[true,true]},{"feature":"Custom domain","values":[false,true]},{"group":"Content & Scheduling"},{"feature":"Day-parting & date scheduling","values":[true,true]},{"feature":"Playlists & campaigns","values":[true,true]},{"feature":"Menu boards","values":[true,true]},{"feature":"Interactive touchscreen actions","values":[true,true]},{"feature":"Overlay layouts","values":[true,true]},{"feature":"Geo-location scheduling","values":[false,true]},{"feature":"Weather-triggered scheduling","values":[false,true]},{"feature":"Video wall","values":[false,true]},{"feature":"Ad campaigns & plays-per-hour control","values":[false,true]},{"group":"Data & Integrations"},{"feature":"DataSets, RSS & tickers","values":[true,true]},{"feature":"Embedded HTML & web pages","values":[true,true]},{"feature":"Social feeds","values":[true,true]},{"feature":"Canva integration","values":[true,true]},{"feature":"Dashboard Connector","values":[false,true]},{"feature":"Custom API integrations","values":[false,true]},{"feature":"SSP Connector (ad monetisation)","values":[false,true]},{"group":"Analytics & Reporting"},{"feature":"Proof of Play reporting","values":["30-day retention","12+ month retention"]},{"feature":"Scheduled PDF reports","values":[false,true]},{"feature":"Audience Reporting","values":[false,true]},{"feature":"Display health monitoring","values":[true,true]},{"group":"Users & Security"},{"feature":"Unlimited user seats","values":[true,true]},{"feature":"Predefined roles (admin/editor/viewer)","values":[true,true]},{"feature":"Custom user roles & feature access","values":[false,true]},{"feature":"Two-factor authentication","values":[true,true]},{"feature":"SSO (SAML / CAS)","values":[false,true]},{"feature":"Audit trail","values":["7-day retention","Extended retention"]},{"group":"Display Management"},{"feature":"Screen power on/off control","values":[true,true]},{"feature":"Offline playback","values":[true,true]},{"feature":"Portrait / landscape","values":[true,true]},{"feature":"Email alerts (player offline)","values":[true,true]},{"feature":"Periodic screenshots","values":[false,true]},{"feature":"Display map view","values":[false,true]},{"feature":"Shell commands & RS232","values":[false,true]},{"group":"Support & Services"},{"feature":"Email support","values":["Next-business-day",true]},{"feature":"Priority support","values":[false,"4-hour SLA"]},{"feature":"Dedicated account manager","values":[false,true]},{"feature":"In-house creative services","values":[false,"Included hours"]},{"feature":"White-glove onboarding","values":[false,true]}]} /-->
<!-- wp:oribi/intro-section {"label":"Try Before You Commit","heading":"Want to Explore the Platform First?","description":"Request access to our live demo instance and take the full CMS for a spin — create content, set up schedules, and see exactly how it works. No credit card, no obligation."} /-->
<!-- wp:oribi/intro-section {"variant":"normal","label":"Try Before You Commit","heading":"Want to Explore the Platform First?","description":"Request access to our live demo instance and take the full CMS for a spin — create content, set up schedules, and see exactly how it works. No credit card, no obligation.","visual":""} /-->
<!-- wp:oribi/cta-banner {"heading":"Questions About Pricing?","text":"We're happy to walk you through the plans, build a custom quote, or set up a demo so you can see the value firsthand.","btnText":"Get in Touch","btnUrl":"/contact"} /-->
ORIBI_SYNC_CONTENT;

View File

@@ -1984,648 +1984,6 @@ p:last-child { margin-bottom: 0; }
.platform-row.reverse .platform-visual { order: unset; }
}
/* Dashboard visual - remove framed background */
.platform-visual.has-dashboard {
background: none !important;
border: none !important;
border-radius: 0;
aspect-ratio: unset;
padding: 0;
overflow: visible;
box-shadow: none;
}
/* ── Dashboard TV frame ────────────────────────── */
.dashboard-tv {
display: flex;
flex-direction: column;
align-items: center;
}
.dashboard-tv__body {
width: 100%;
max-width: 520px;
background: var(--color-bg-alt);
border: 4px solid var(--color-bg-alt);
border-radius: 6px 6px 4px 4px;
outline: 1px solid var(--color-border);
padding: 3px;
position: relative;
box-shadow: 0 14px 48px rgba(0,0,0,0.55);
}
.dashboard-tv__body::after {
content: '\25B6';
position: absolute;
bottom: -13px;
left: 50%;
transform: translateX(-50%);
font-size: 8px;
color: rgba(74,222,128,0.7);
}
.dashboard-tv__screen {
width: 100%;
aspect-ratio: 16/9;
background: #111;
border-radius: 2px;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
}
.dashboard-tv__screen .dashboard-chart {
width: 100%;
height: 100%;
display: block;
}
.dashboard-tv__feet {
display: flex;
justify-content: space-between;
width: 60%;
max-width: 300px;
}
.dashboard-tv__foot {
width: 12px;
height: 8px;
background: var(--color-bg-alt);
border: 1px solid var(--color-border);
border-radius: 0 0 4px 4px;
}
/* ── 10b. Device Animator ───────────────────────────────────── */
.platform-visual.has-anim {
background: none !important;
border: none !important;
border-radius: 0;
aspect-ratio: unset;
padding: 0;
overflow: visible;
position: relative;
font-size: inherit;
}
.da-stage {
position: absolute;
inset: 0;
}
/* Each device panel hidden by default, centred in stage */
.da-device {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(0.88);
opacity: 0;
display: flex;
flex-direction: column;
align-items: center;
transition: opacity 0.55s cubic-bezier(0.4,0,0.2,1),
transform 0.55s cubic-bezier(0.4,0,0.2,1);
will-change: opacity, transform;
}
.da-device.is-active {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
.da-device.is-leaving {
opacity: 0;
transform: translate(-50%, -50%) scale(1.07);
}
/* Screen surface */
.da-screen {
width: 100%;
height: 100%;
border-radius: 2px;
position: relative;
overflow: hidden;
background:
repeating-linear-gradient(
180deg,
transparent,
transparent 3px,
rgba(0,0,0,0.10) 3px,
rgba(0,0,0,0.10) 4px
),
linear-gradient(135deg, #0c1016 0%, #151c28 60%, #0c1016 100%);
}
.da-screen::before {
content: '';
position: absolute;
top: -20%;
left: -10%;
width: 60%;
height: 60%;
background: radial-gradient(ellipse, rgba(74,222,128,0.12) 0%, transparent 70%);
pointer-events: none;
}
@keyframes da-scan {
0% { top: -6%; opacity: 0; }
5% { opacity: 1; }
95% { opacity: 1; }
100% { top: 106%; opacity: 0; }
}
.da-screen::after {
content: '';
position: absolute;
left: 0;
width: 100%;
height: 3px;
background: linear-gradient(90deg, transparent, rgba(74,222,128,0.28), transparent);
animation: da-scan 3s linear infinite;
pointer-events: none;
}
/* Device label */
.da-label {
display: block;
margin-top: 11px;
font-size: 0.68rem;
font-weight: 600;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--color-text-muted);
text-align: center;
}
/* ── Tablet ────────────────────────────────────── */
.da-tablet .da-body {
width: 128px;
height: 194px;
background: var(--color-bg-alt);
border: 2px solid var(--color-border);
border-radius: 14px;
padding: 10px 8px 14px;
display: flex;
align-items: stretch;
position: relative;
box-shadow: 0 16px 48px rgba(0,0,0,0.50);
}
.da-tablet .da-body::before {
content: '';
position: absolute;
top: 5px;
left: 50%;
transform: translateX(-50%);
width: 6px;
height: 6px;
background: var(--color-border);
border-radius: 50%;
}
.da-tablet .da-body::after {
content: '';
position: absolute;
bottom: 5px;
left: 50%;
transform: translateX(-50%);
width: 36px;
height: 3px;
background: var(--color-border);
border-radius: 2px;
}
/* ── Small Monitor ─────────────────────────────── */
.da-monitor-sm .da-body {
width: 236px;
height: 146px;
background: var(--color-bg-alt);
border: 5px solid var(--color-bg-alt);
border-radius: 6px;
outline: 1px solid var(--color-border);
padding: 3px;
display: flex;
align-items: stretch;
position: relative;
box-shadow: 0 10px 36px rgba(0,0,0,0.50);
}
.da-monitor-sm .da-body::after {
content: '';
position: absolute;
bottom: -9px;
right: 8px;
width: 5px;
height: 5px;
background: var(--color-primary);
border-radius: 50%;
box-shadow: 0 0 5px var(--color-primary);
}
.da-monitor-sm .da-stand,
.da-monitor-lg .da-stand { display: flex; flex-direction: column; align-items: center; }
.da-monitor-sm .da-stem {
width: 14px;
height: 20px;
background: var(--color-bg-alt);
border-left: 1px solid var(--color-border);
border-right: 1px solid var(--color-border);
}
.da-monitor-sm .da-base {
width: 68px;
height: 5px;
background: var(--color-bg-alt);
border: 1px solid var(--color-border);
border-radius: 3px;
}
/* ── Large Monitor ─────────────────────────────── */
.da-monitor-lg .da-body {
width: 298px;
height: 177px;
background: var(--color-bg-alt);
border: 4px solid var(--color-bg-alt);
border-radius: 6px;
outline: 1px solid var(--color-border);
padding: 3px;
display: flex;
align-items: stretch;
position: relative;
box-shadow: 0 12px 40px rgba(0,0,0,0.50);
}
.da-monitor-lg .da-stem {
width: 16px;
height: 26px;
background: var(--color-bg-alt);
border-left: 1px solid var(--color-border);
border-right: 1px solid var(--color-border);
}
.da-monitor-lg .da-base {
width: 88px;
height: 5px;
background: var(--color-bg-alt);
border: 1px solid var(--color-border);
border-radius: 3px;
}
/* ── TV ────────────────────────────────────────── */
.da-tv .da-body {
width: 320px;
height: 188px;
background: var(--color-bg-alt);
border: 4px solid var(--color-bg-alt);
border-radius: 6px 6px 4px 4px;
outline: 1px solid var(--color-border);
padding: 3px;
display: flex;
align-items: stretch;
position: relative;
box-shadow: 0 14px 48px rgba(0,0,0,0.55);
}
.da-tv .da-body::after {
content: '\25B6';
position: absolute;
bottom: -13px;
left: 50%;
transform: translateX(-50%);
font-size: 8px;
color: rgba(74,222,128,0.7);
}
.da-tv .da-feet { display: flex; justify-content: space-between; width: 180px; }
.da-tv .da-foot {
width: 12px;
height: 8px;
background: var(--color-bg-alt);
border: 1px solid var(--color-border);
border-radius: 0 0 4px 4px;
}
/* ── Projector ─────────────────────────────────── */
.da-projector .da-proj-layout { display: flex; flex-direction: column; align-items: center; }
.da-projector .da-proj-body {
width: 156px;
height: 62px;
background: var(--color-bg-alt);
border: 1px solid var(--color-border);
border-radius: 10px 10px 8px 8px;
display: flex;
align-items: center;
padding: 0 14px;
gap: 10px;
box-shadow: 0 6px 20px rgba(0,0,0,0.45);
position: relative;
}
.da-projector .da-proj-body::after {
content: '';
position: absolute;
top: 8px;
right: 10px;
width: 7px;
height: 7px;
background: var(--color-primary);
border-radius: 50%;
box-shadow: 0 0 6px var(--color-primary);
}
.da-projector .da-proj-body::before {
content: '';
position: absolute;
right: 10px;
bottom: 8px;
width: 28px;
height: 8px;
background: repeating-linear-gradient(
90deg,
var(--color-border) 0px,
var(--color-border) 2px,
transparent 2px,
transparent 5px
);
border-radius: 1px;
}
.da-projector .da-lens {
width: 38px;
height: 38px;
background: #080c12;
border: 2px solid var(--color-border);
border-radius: 50%;
flex-shrink: 0;
position: relative;
box-shadow: inset 0 0 8px rgba(0,0,0,0.8);
}
.da-projector .da-lens::after {
content: '';
position: absolute;
inset: 5px;
background: radial-gradient(circle at 35% 35%, rgba(74,222,128,0.30) 0%, #080c12 65%);
border-radius: 50%;
}
.da-projector .da-beam {
width: 240px;
height: 50px;
clip-path: polygon(31% 0%, 69% 0%, 100% 100%, 0% 100%);
background: linear-gradient(
180deg,
rgba(74,222,128,0.07) 0%,
rgba(74,222,128,0.02) 100%
);
}
.da-projector .da-proj-screen {
width: 240px;
height: 72px;
background: var(--color-bg-alt);
border: 1px solid var(--color-border);
border-radius: 3px;
overflow: hidden;
padding: 3px;
box-shadow: 0 4px 14px rgba(0,0,0,0.40);
}
/* ── Video Wall (2×2) ──────────────────────────── */
.da-vwall .da-vwall-grid {
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 5px;
background: #0a0d12;
padding: 5px;
border: 1px solid var(--color-border);
border-radius: 4px;
box-shadow: 0 14px 48px rgba(0,0,0,0.60);
}
.da-vwall .da-panel {
width: 148px;
height: 90px;
background: var(--color-bg-alt);
border: 2px solid var(--color-bg-alt);
padding: 2px;
display: flex;
align-items: stretch;
overflow: hidden;
}
.da-vwall .da-panel:nth-child(2) .da-screen::after { animation-delay: -0.75s; }
.da-vwall .da-panel:nth-child(3) .da-screen::after { animation-delay: -1.5s; }
.da-vwall .da-panel:nth-child(4) .da-screen::after { animation-delay: -2.25s; }
/* ── Responsive scale-down ─────────────────────── */
@media (max-width: 900px) {
.da-device { transform: translate(-50%,-50%) scale(0.76); }
.da-device.is-active { transform: translate(-50%,-50%) scale(0.84); }
.da-device.is-leaving { transform: translate(-50%,-50%) scale(0.91); }
}
@media (max-width: 640px) {
.da-device { transform: translate(-50%,-50%) scale(0.56); }
.da-device.is-active { transform: translate(-50%,-50%) scale(0.64); }
.da-device.is-leaving { transform: translate(-50%,-50%) scale(0.70); }
}
@media (prefers-reduced-motion: reduce) {
.da-device { transition: none; }
.da-screen::after { animation: none; }
}
/* ── 10c. TV Stick Plug Animation ──────────────────────────── */
.platform-visual.has-tv-stick {
background: none !important;
border: none !important;
border-radius: 0;
aspect-ratio: unset;
padding: 0;
overflow: visible;
position: relative;
font-size: inherit;
}
.ts-stage {
position: relative;
width: 100%;
aspect-ratio: 4/3;
display: flex;
align-items: center;
justify-content: center;
}
/* ── TV ── */
.ts-tv {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
.ts-tv__body {
width: 320px;
height: 188px;
background: var(--color-bg-alt);
border: 4px solid var(--color-bg-alt);
border-radius: 6px 6px 4px 4px;
outline: 1px solid var(--color-border);
padding: 3px;
display: flex;
align-items: stretch;
position: relative;
box-shadow: 0 14px 48px rgba(0,0,0,0.55);
}
.ts-tv__screen {
width: 100%;
height: 100%;
border-radius: 2px;
position: relative;
overflow: hidden;
background:
repeating-linear-gradient(
180deg,
transparent,
transparent 3px,
rgba(0,0,0,0.10) 3px,
rgba(0,0,0,0.10) 4px
),
linear-gradient(135deg, #0c1016 0%, #151c28 60%, #0c1016 100%);
}
/* Subtle green ambient glow on screen */
.ts-tv__screen::before {
content: '';
position: absolute;
top: -20%;
left: -10%;
width: 60%;
height: 60%;
background: radial-gradient(ellipse, rgba(74,222,128,0.12) 0%, transparent 70%);
pointer-events: none;
}
/* Scan line on screen */
.ts-tv__screen::after {
content: '';
position: absolute;
left: 0;
width: 100%;
height: 3px;
background: linear-gradient(90deg, transparent, rgba(74,222,128,0.28), transparent);
animation: da-scan 3s linear infinite;
pointer-events: none;
}
/* Screen glow when stick plugs in */
.ts-stage.is-plugged .ts-tv__screen {
animation: ts-screen-glow 0.8s ease 0.1s both;
}
@keyframes ts-screen-glow {
0% { filter: brightness(1); }
40% { filter: brightness(1.25); }
100% { filter: brightness(1); }
}
/* HDMI port on back-right of TV */
.ts-tv__port {
position: absolute;
right: -6px;
top: 50%;
transform: translateY(-50%);
width: 6px;
height: 14px;
background: #1a1a1a;
border: 1px solid var(--color-border);
border-left: none;
border-radius: 0 2px 2px 0;
z-index: 1;
}
.ts-tv__port::before {
content: '';
position: absolute;
left: 0;
top: 2px;
width: 3px;
height: 8px;
background: #333;
border-radius: 0 1px 1px 0;
}
/* Feet */
.ts-tv__feet {
display: flex;
justify-content: space-between;
width: 180px;
}
.ts-tv__foot {
width: 12px;
height: 8px;
background: var(--color-bg-alt);
border: 1px solid var(--color-border);
border-radius: 0 0 4px 4px;
}
/* ── Stick device ── */
.ts-stick {
position: absolute;
right: -20px;
top: 50%;
transform: translateY(-50%) translateX(80px);
display: flex;
align-items: center;
opacity: 0;
z-index: 2;
}
.ts-stick__body {
width: 68px;
height: 26px;
background: linear-gradient(180deg, #f5f5f5, #e0e0e0);
border: 1px solid #ccc;
border-radius: 5px;
position: relative;
box-shadow:
0 2px 8px rgba(0,0,0,0.25),
inset 0 1px 0 rgba(255,255,255,0.6);
}
/* Brand logo area subtle inset */
.ts-stick__body::before {
content: '';
position: absolute;
top: 6px;
left: 10px;
width: 28px;
height: 12px;
background: rgba(0,0,0,0.04);
border-radius: 2px;
}
/* LED indicator */
.ts-stick__led {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 4px;
background: #999;
border-radius: 50%;
transition: background 0.4s ease, box-shadow 0.4s ease;
}
.ts-stage.is-plugged .ts-stick__led {
background: #4CAF50;
box-shadow: 0 0 6px rgba(76,175,80,0.8);
}
/* HDMI connector */
.ts-stick__connector {
width: 14px;
height: 10px;
background: linear-gradient(180deg, #888, #666);
border-radius: 0 2px 2px 0;
margin-left: -1px;
position: relative;
box-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
.ts-stick__connector::before {
content: '';
position: absolute;
left: 2px;
top: 2px;
width: 6px;
height: 6px;
background: #555;
border-radius: 1px;
}
/* ── Plug-in animation ── */
.ts-stage.is-animating .ts-stick {
animation: ts-slide-in 1.4s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}
@keyframes ts-slide-in {
0% { opacity: 0; transform: translateY(-50%) translateX(80px); }
20% { opacity: 1; transform: translateY(-50%) translateX(60px); }
70% { transform: translateY(-50%) translateX(8px); }
85% { transform: translateY(-50%) translateX(12px); }
100% { opacity: 1; transform: translateY(-50%) translateX(6px); }
}
/* Responsive scale-down */
@media (max-width: 900px) {
.ts-tv__body { width: 260px; height: 152px; }
.ts-tv__feet { width: 140px; }
.ts-stick__body { width: 56px; height: 22px; }
}
@media (max-width: 640px) {
.ts-tv__body { width: 200px; height: 118px; }
.ts-tv__feet { width: 110px; }
.ts-stick__body { width: 46px; height: 18px; }
.ts-stick__connector { width: 10px; height: 8px; }
.ts-stick__body::before { top: 4px; left: 6px; width: 20px; height: 8px; }
.ts-stick__led { width: 3px; height: 3px; right: 5px; }
}
@media (prefers-reduced-motion: reduce) {
.ts-stage .ts-stick {
opacity: 1;
transform: translateY(-50%) translateX(6px);
}
.ts-stage.is-animating .ts-stick { animation: none; }
.ts-stage.is-plugged .ts-tv__screen { animation: none; }
.ts-stage.is-plugged .ts-stick__led {
background: #4CAF50;
box-shadow: 0 0 6px rgba(76,175,80,0.8);
}
}
/* ── 11. Page Hero (inner pages) ───────────────────────────── */
.page-hero {
background: #111111;
@@ -2897,24 +2255,6 @@ p:last-child { margin-bottom: 0; }
0% { opacity: 1; }
100% { opacity: 1; }
}
/* ── Dashboard Chart ───────────────────────────────────── */
.dashboard-chart {
width: 100%;
height: auto;
display: block;
user-select: none;
overflow: visible;
}
/* Reduced-motion: JS already checks the media query, belt-and-suspenders */
@media (prefers-reduced-motion: reduce) {
.dashboard-chart .bar,
.dashboard-chart .pie-segment { transition: none !important; }
}
@media (max-width: 768px) {
.dashboard-chart-container { padding: 1rem; border-radius: var(--radius-md, 12px); }
}
.cta-banner {
position: relative;
overflow: hidden;
@@ -3266,7 +2606,7 @@ p:last-child { margin-bottom: 0; }
border-color: rgba(255,255,255,.25);
}
[data-theme="dark"] .platform-visual:not(.has-img):not(.has-dashboard) {
[data-theme="dark"] .platform-visual:not(.has-img) {
background: var(--color-bg-alt);
border-color: var(--color-border);
}

View File

@@ -1,157 +0,0 @@
/**
* Dashboard Chart Animator
* Gently animates SVG bar charts, line graph, and pie chart.
* Pauses off-screen via IntersectionObserver for performance.
*/
(function () {
'use strict';
var SPEED = 0.002; // phase increment per frame
var BAR_H = 120; // max bar height (SVG units)
var BAR_MIN = 0.15; // min bar ratio
var LINE_PTS = 8;
var LINE_W = 340; // line graph width in SVG units
var PIE_R = 55; // pie chart radius
var DARK = { text: '#E0E0E0', muted: '#9E9E9E', border: '#333', center: '#222' };
var LIGHT = { text: '#333333', muted: '#666666', border: '#E0E0E0', center: '#fff' };
function isDark() { return document.documentElement.getAttribute('data-theme') === 'dark'; }
function pal() { return isDark() ? DARK : LIGHT; }
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 makeState(svg) {
return {
svg: svg,
bars1: svg.querySelectorAll('#bars-group-1 .bar'),
bars2: svg.querySelectorAll('#bars-group-2 .bar'),
vals1: svg.querySelectorAll('#values-group-1 text'),
vals2: svg.querySelectorAll('#values-group-2 text'),
linePath: svg.querySelector('#line-path'),
lineFill: svg.querySelector('#line-fill'),
pieSegs: svg.querySelectorAll('.pie-segment'),
phase: Math.random() * Math.PI * 2,
paused: false,
themeN: 0
};
}
function updateBars(bars, vals, st, pct) {
for (var i = 0; i < bars.length; i++) {
var v = Math.max(BAR_MIN, wave(st.phase, i * 1.1));
var h = v * BAR_H;
bars[i].setAttribute('height', h);
bars[i].setAttribute('y', BAR_H - h);
}
for (var j = 0; j < Math.min(bars.length, vals.length); j++) {
var val = Math.max(BAR_MIN, wave(st.phase, j * 1.1));
vals[j].textContent = pct ? Math.round(val * 100) + '%' : Math.round(val * 5000);
}
}
function updateLine(st) {
if (!st.linePath) return;
var d = 'M';
for (var i = 0; i < LINE_PTS; i++) {
var x = (i / (LINE_PTS - 1)) * LINE_W;
var y = 25 + (1 - wave(st.phase * 0.8, i * 0.9)) * 110;
d += (i ? ' L' : '') + x.toFixed(1) + ',' + y.toFixed(1);
}
st.linePath.setAttribute('d', d);
if (st.lineFill) st.lineFill.setAttribute('d', d + ' L' + LINE_W + ',145 L0,145 Z');
}
/* Pie: each segment gently shifts its slice size, no spinning */
function updatePie(st) {
if (!st.pieSegs.length) return;
var n = st.pieSegs.length;
// Generate proportional weights that shift over time
var weights = [], total = 0;
for (var i = 0; i < n; i++) {
var w = 0.5 + wave(st.phase * 0.4, i * 2.0) * 0.5;
weights.push(w);
total += w;
}
// Convert to cumulative angles
var angle = 0;
for (var j = 0; j < n; j++) {
var sweep = (weights[j] / total) * 360;
var startA = angle * Math.PI / 180;
var endA = (angle + sweep) * Math.PI / 180;
// Large-arc flag needed when sweep > 180
var large = sweep > 180 ? 1 : 0;
var r = PIE_R;
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.pieSegs[j].querySelector('path');
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'
);
}
// Remove any rotation transform — segments are positioned by path geometry
st.pieSegs[j].removeAttribute('transform');
angle += sweep;
}
}
function applyTheme(st) {
var c = pal(), q = st.svg.querySelectorAll.bind(st.svg), all, k;
all = q('.ct'); for (k = 0; k < all.length; k++) all[k].setAttribute('fill', c.text);
all = q('.cl'); for (k = 0; k < all.length; k++) all[k].setAttribute('fill', c.muted);
all = q('.cv'); for (k = 0; k < all.length; k++) all[k].setAttribute('fill', c.text);
all = q('.grid-line'); for (k = 0; k < all.length; k++) all[k].setAttribute('stroke', c.border);
var cc = st.svg.querySelector('#pie-center');
if (cc) { cc.setAttribute('fill', c.center); cc.setAttribute('stroke', c.border); }
var pt = st.svg.querySelector('#pie-center-text');
if (pt) pt.setAttribute('fill', c.text);
}
function tick(st) {
if (!st.paused) {
st.phase += SPEED * 16;
updateBars(st.bars1, st.vals1, st, true);
updateBars(st.bars2, st.vals2, st, false);
updateLine(st);
updatePie(st);
if (++st.themeN > 30) { st.themeN = 0; applyTheme(st); }
}
requestAnimationFrame(function () { tick(st); });
}
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.svg);
}
function boot() {
var svgs = document.querySelectorAll('.dashboard-chart');
if (!svgs.length) return;
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
for (var i = 0; i < svgs.length; i++) {
if (svgs[i]._dbAnim) continue;
var st = makeState(svgs[i]);
svgs[i]._dbAnim = st;
applyTheme(st);
tick(st);
observe(st);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot);
} else {
boot();
}
})();

View File

@@ -610,99 +610,3 @@ document.addEventListener('DOMContentLoaded', () => {
paintBg();
start();
})();
/* ── Device Animator ──────────────────────────────────────────────────────── */
(function () {
const DEVICES = [
'da-tablet', 'da-monitor-sm', 'da-monitor-lg',
'da-tv', 'da-projector', 'da-vwall'
];
const DWELL = 2500; // ms each device is shown
document.querySelectorAll('.da-stage').forEach(function (stage) {
let current = 0;
let timer = null;
// Collect the 6 device panels
const panels = DEVICES.map(function (cls) {
return stage.querySelector('.' + cls);
});
function show(idx) {
panels.forEach(function (el, i) {
if (!el) return;
el.classList.toggle('is-active', i === idx);
el.classList.remove('is-leaving');
});
}
function advance() {
const leaving = current;
current = (current + 1) % DEVICES.length;
if (panels[leaving]) panels[leaving].classList.add('is-leaving');
show(current);
setTimeout(function () {
if (panels[leaving]) panels[leaving].classList.remove('is-leaving');
}, 600);
}
function startCycle() {
if (timer) return;
timer = setInterval(advance, DWELL);
}
function stopCycle() {
clearInterval(timer);
timer = null;
}
// Honour reduced-motion preference: show first device statically
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
show(0);
return;
}
show(0);
startCycle();
// Pause when scrolled out of view to save resources
if ('IntersectionObserver' in window) {
new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
e.isIntersecting ? startCycle() : stopCycle();
});
}, { threshold: 0.2 }).observe(stage);
}
});
})();
/* ── TV Stick Plug Animation ─────────────────────────────────────────────── */
(function () {
var stages = document.querySelectorAll('[data-tv-stick-anim]');
if (!stages.length) return;
// Honour reduced-motion: show plugged-in state immediately
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
stages.forEach(function (stage) {
stage.classList.add('is-plugged');
});
return;
}
if ('IntersectionObserver' in window) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
if (e.isIntersecting) {
var stage = e.target;
stage.classList.add('is-animating');
// Add plugged state after slide-in completes (1.4s)
setTimeout(function () {
stage.classList.add('is-plugged');
}, 1400);
io.unobserve(stage);
}
});
}, { threshold: 0.3 });
stages.forEach(function (stage) { io.observe(stage); });
}
})();

View File

@@ -553,8 +553,6 @@ add_action( 'init', function () {
'imgUrl' => [ 'type' => 'string', 'default' => '' ],
'imgAlt' => [ 'type' => 'string', 'default' => '' ],
'imgWidth' => [ 'type' => 'number', 'default' => 300 ],
'deviceAnim' => [ 'type' => 'boolean', 'default' => false ],
'tvStick' => [ 'type' => 'boolean', 'default' => false ],
],
'supports' => $block_supports,
'render_callback' => 'oribi_render_platform_row',
@@ -1340,128 +1338,7 @@ function oribi_render_platform_row( $a ) {
$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="#004225" 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="#004225" 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="#004225"/>
<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 ) {
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 ) {
$visual_html = wp_get_attachment_image( $img_id, 'full', false, [ 'style' => $img_style, 'alt' => $img_alt ] );
@@ -1469,35 +1346,6 @@ function oribi_render_platform_row( $a ) {
$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 = '<div class="da-stage" aria-hidden="true">';
$da .= '<div class="da-device da-tablet"><div class="da-body"><div class="da-screen"></div></div><span class="da-label">Tablet</span></div>';
$da .= '<div class="da-device da-monitor-sm"><div class="da-body"><div class="da-screen"></div></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"><div class="da-screen"></div></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"><div class="da-screen"></div></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"><div class="da-screen"></div></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"><div class="da-screen"></div></div><div class="da-panel"><div class="da-screen"></div></div><div class="da-panel"><div class="da-screen"></div></div><div class="da-panel"><div class="da-screen"></div></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"></div>';
$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';
} else {
$visual_html = oribi_render_icon( $a['visual'] ?? '' );
$visual_cls = 'platform-visual';

View File

@@ -29,15 +29,6 @@ add_action( 'wp_enqueue_scripts', function () {
true
);
// Dashboard chart animator - smooth continuous animations for dashboard cards
wp_enqueue_script(
'oribi-dashboard-animator',
ORIBI_URI . '/assets/js/dashboard-animator.js',
[],
ORIBI_VERSION . '.' . filemtime( ORIBI_DIR . '/assets/js/dashboard-animator.js' ),
true
);
// Localize AJAX endpoint for the contact form
wp_localize_script( 'oribi-main', 'oribiAjax', [
'url' => admin_url( 'admin-ajax.php' ),