Add feature animations and update styles for kiosks and about pages

- Introduced `featuresAnim` option in editor and index files to enable feature ticker animations.
- Updated rendering logic to include feature animations in the about section.
- Added new JavaScript files for handling animations on the about page and kiosks page.
- Adjusted CSS classes to accommodate new animations and ensure proper visual representation.
- Changed primary color in theme defaults for a more cohesive design.
This commit is contained in:
Matt Batchelder
2026-04-09 11:06:17 -04:00
parent 6f16a698f5
commit 942f02a1c1
11 changed files with 685 additions and 28 deletions

View File

@@ -16,7 +16,7 @@ return <<<'ORIBI_SYNC_CONTENT'
<!-- wp:oribi/value-card {"iconType":"fontawesome","faIcon":"fas fa-people-group","title":"A Team That Ships","description":"Our team spans cloud engineering, hardware, creative production, and customer success. We handle every layer of your signage deployment in-house."} /-->
<!-- /wp:oribi/value-section -->
<!-- wp:oribi/intro-section {"variant":"alt","heading":"Full-Featured Signage. No Enterprise Price Tag.","description":"Too many providers reserve real features for enterprise contracts. At OTS Signs, every customer gets the same platform: OTS Signs Command Center, intelligent scheduling, live data integration, and professional content services. We handle the complexity so you can focus on running your business.\u003cbr\u003e- Cloud Content Management\u003cbr\u003e- Intelligent Scheduling\u003cbr\u003e- Live Data Integration \u003cbr\u003e- Content that looks outstanding on screen","reversed":true} /-->
<!-- wp:oribi/intro-section {"variant":"alt","heading":"Full-Featured Signage. No Enterprise Price Tag.","description":"Too many providers reserve real features for enterprise contracts. At OTS Signs, every customer gets the same platform: OTS Signs Command Center, intelligent scheduling, live data integration, and professional content services. We handle the complexity so you can focus on running your business.\u003cbr\u003e- Cloud Content Management\u003cbr\u003e- Intelligent Scheduling\u003cbr\u003e- Live Data Integration \u003cbr\u003e- Content that looks outstanding on screen","reversed":true,"featuresAnim":true} /-->
<!-- wp:oribi/value-section {"heading":"Software, Hardware, and Creative — In One Team","lead":"Software alone isn't enough. We pair our platform with hands-on services that make the difference.","columns":3} -->
<!-- wp:oribi/value-card {"iconType":"fontawesome","faIcon":"fas fa-camera","title":"Studio-Quality Creative","description":"Photography, videography, motion graphics, and graphic design produced in-house. Your screens always look polished, on-brand, and impossible to ignore. Available as included hours on Pro, or quoted separately for Essentials customers."} /-->

View File

@@ -10,9 +10,9 @@
<!-- wp:oribi/page-hero-animated {"title":"Interactive Kiosks — Wayfinding, Self-Check-In, and Product Lookup","description":"Touchscreen kiosks for wayfinding, self-check-in, product lookup, and self-ordering - powered by the same platform you use for all your digital signage."} /-->
<!-- wp:oribi/platform-section {"heading":"Interactive Signage, No Custom Development","lead":"Our platform's interactive mode transforms any compatible touchscreen into a self-service kiosk - configured entirely from the Command Center."} -->
<!-- wp:oribi/platform-row {"heading":"Wayfinding \u0026 Directories","description":"Guide visitors through buildings, campuses, and venues with interactive maps and searchable directories. Visitors tap to find rooms, departments, or points of interest - with turn-by-turn guidance on screen. Ideal for hospitals, universities, corporate offices, and transport hubs.","btnText":"See Features","btnUrl":"/features"} /-->
<!-- wp:oribi/platform-row {"heading":"Self-Check-In \u0026 Registration","description":"Replace front-desk queues with touchscreen check-in flows. Visitors, patients, and guests register on arrival, receive queue numbers, and get directed to the right location - all without staff intervention. Frees your team to focus on the people who need personal attention.","btnText":"See Pricing","btnUrl":"/pricing","reversed":true} /-->
<!-- wp:oribi/platform-row {"heading":"Product Lookup \u0026 Self-Ordering","description":"Let customers browse your catalogue, check availability, and place orders directly from in-store kiosks. Sync with your POS system to keep prices and stock levels accurate in real time. Perfect for retail, QSR, and hospitality environments.","btnText":"Get a Quote","btnUrl":"/contact"} /-->
<!-- wp:oribi/platform-row {"heading":"Wayfinding \u0026 Directories","description":"Guide visitors through buildings, campuses, and venues with interactive maps and searchable directories. Visitors tap to find rooms, departments, or points of interest - with turn-by-turn guidance on screen. Ideal for hospitals, universities, corporate offices, and transport hubs.","btnText":"See Features","btnUrl":"/features","wayfindAnim":true} /-->
<!-- wp:oribi/platform-row {"heading":"Self-Check-In \u0026 Registration","description":"Replace front-desk queues with touchscreen check-in flows. Visitors, patients, and guests register on arrival, receive queue numbers, and get directed to the right location - all without staff intervention. Frees your team to focus on the people who need personal attention.","btnText":"See Pricing","btnUrl":"/pricing","reversed":true,"selfCheckInAnim":true} /-->
<!-- wp:oribi/platform-row {"heading":"Product Lookup \u0026 Self-Ordering","description":"Let customers browse your catalogue, check availability, and place orders directly from in-store kiosks. Sync with your POS system to keep prices and stock levels accurate in real time. Perfect for retail, QSR, and hospitality environments.","btnText":"Get a Quote","btnUrl":"/contact","selfOrderAnim":true} /-->
<!-- /wp:oribi/platform-section -->
<!-- wp:oribi/feature-section {"variant":"alt","heading":"Kiosks for Every Environment","lead":"Interactive displays solve different problems in different industries. Here\u0027s how businesses use them.","columns":3} -->

View File

@@ -29,7 +29,7 @@ return <<<'ORIBI_SYNC_CONTENT'
<!-- wp:oribi/pricing-card {"name":"Growth","tagline":"Static + motion, fast turns","price":"$89","pricePer":"per screen / month","features":["Everything in Essentials","4 graphics OR 1 short video/month","Up to 8 schedule changes/month","Motion up to 30s","Priority 1\u20132 biz day turnaround","2 revision rounds on motion"],"btnText":"Get Started","btnUrl":"/contact","featured":true,"badge":"Most Popular"} /-->
<!-- wp:oribi/pricing-card {"name":"Pro","tagline":"Campaigns \u0026 planning","price":"$129","pricePer":"per screen / month","features":["Everything in Growth","Custom content filmed on site (1 hr/screen pooled)","6 graphics OR 2 short videos/month","Unlimited schedule changes","Monthly content planning call","Editable source files (AI/AE)","Fastest SLA: 1 biz day static"],"btnText":"Get Started","btnUrl":"/contact"} /-->
<!-- wp:oribi/pricing-card {"name":"Pro","tagline":"Campaigns \u0026 planning","price":"$109","pricePer":"per screen / month","features":["Everything in Growth","Custom content filmed on site (1 hr/screen pooled)","6 graphics OR 2 short videos/month","Unlimited schedule changes","Monthly content planning call","Editable source files (AI/AE)","Fastest SLA: 1 biz day static"],"btnText":"Get Started","btnUrl":"/contact"} /-->
<!-- /wp:oribi/pricing-section -->
<!-- wp:oribi/comparison-table {"heading":"See Exactly What\u2019s Included","lead":"A full breakdown of what you get on each plan \u2014 no surprises.","columns":["Monitor","Manage","Essentials","Growth","Pro"],"rows":[{"group":"Platform"},{"feature":"Full Command Center access","values":[true,true,true,true,true]},{"feature":"Brand kit on file","values":[true,true,true,true,true]},{"feature":"Screen health monitoring","values":[true,true,true,true,true]},{"group":"Scheduling \u0026 Management"},{"feature":"OTS manages scheduling","values":[false,true,true,true,true]},{"feature":"Schedule changes","values":["Unlimited","Up to 2\/week","Up to 4\/month","Up to 8\/month","Unlimited"]},{"group":"Creative Services"},{"feature":"New graphics/month","values":[false,false,"2 static","4 or 1 video","6 or 2 videos"]},{"feature":"Motion content","values":[false,false,false,"Up to 30s","Up to 30s"]},{"feature":"Graphics pool across screens","values":[false,false,true,true,true]},{"feature":"On-site filming","values":[false,false,false,false,"1 hr/screen (pooled)"]},{"feature":"Monthly content planning call","values":[false,false,false,false,true]},{"feature":"Editable source files (AI/AE)","values":[false,false,false,false,true]},{"group":"Revisions \u0026 Turnaround"},{"feature":"Revision rounds","values":[false,false,"1 round","2 rounds (motion)","2 rounds"]},{"feature":"Turnaround time","values":[false,false,"2\u20133 biz days","1\u20132 biz days","1 biz day"]},{"group":"Support"},{"feature":"Email support response","values":["2\u20133 biz days","2\u20133 biz days","2\u20133 biz days","Priority","Fastest SLA"]}]} /-->

View File

@@ -52,10 +52,10 @@
/* ── Dark Mode ────────────────────────────────────────────── */
[data-theme="dark"] {
--color-primary: var(--wp--custom--dark--primary, #FF6B3D);
--color-primary: var(--wp--custom--dark--primary, #D83302);
--color-primary-dk: var(--wp--custom--dark--primary-dk, #D83302);
--color-primary-lt: var(--wp--custom--dark--primary-lt, rgba(216,51,2,.15));
--color-primary-rgb: 255,107,61;
--color-primary-rgb: 216,51,2;
--color-accent: var(--wp--custom--dark--accent, #66BB6A);
--color-accent-dk: var(--wp--custom--dark--accent-dk, #388E3C);
--color-accent-lt: var(--wp--custom--dark--accent-lt, rgba(76,175,80,.12));
@@ -2723,8 +2723,8 @@ p:last-child { margin-bottom: 0; }
width: 7px;
height: 7px;
border-radius: 50%;
background: #f97316;
box-shadow: 0 0 6px rgba(249,115,22,.55);
background: #D83302;
box-shadow: 0 0 6px rgba(216,51,2,.55);
animation: da-dot-pulse 2.8s ease-in-out infinite;
}
@@ -2732,7 +2732,7 @@ p:last-child { margin-bottom: 0; }
width: 40%;
height: 5px;
border-radius: 999px;
background: linear-gradient(90deg, rgba(249,115,22,.95), rgba(251,191,36,.75));
background: linear-gradient(90deg, rgba(216,51,2,.95), rgba(251,191,36,.75));
}
.da-promo__hero {
@@ -2740,7 +2740,7 @@ p:last-child { margin-bottom: 0; }
border-radius: 4px;
background:
linear-gradient(160deg, rgba(2,132,199,.38), rgba(15,23,42,.08) 58%),
linear-gradient(115deg, rgba(249,115,22,.42), rgba(249,115,22,.08) 62%);
linear-gradient(115deg, rgba(216,51,2,.42), rgba(216,51,2,.08) 62%);
}
.da-promo__row {
@@ -2759,7 +2759,7 @@ p:last-child { margin-bottom: 0; }
.da-promo__line--lg { width: 56%; }
.da-promo__line--md { width: 46%; }
.da-promo__line--sm { width: 24%; background: rgba(74,222,128,.72); }
.da-promo__line--xs { width: 19%; background: rgba(249,115,22,.72); }
.da-promo__line--xs { width: 19%; background: rgba(216,51,2,.72); }
.da-promo__ticker {
margin-top: auto;
@@ -7566,7 +7566,7 @@ p:last-child { margin-bottom: 0; }
top: 0;
bottom: 0;
width: 42%;
background: linear-gradient(90deg, var(--color-primary), #ff6b35);
background: linear-gradient(90deg, var(--color-primary), #D83302);
border-radius: 4px;
animation: corp-tl-progress 12s linear infinite;
}
@@ -8535,7 +8535,7 @@ p:last-child { margin-bottom: 0; }
width: 26px;
height: 26px;
border-radius: 6px;
background: linear-gradient(135deg, var(--color-primary), #ff6b35);
background: linear-gradient(135deg, var(--color-primary), #D83302);
flex-shrink: 0;
}
@@ -8606,7 +8606,7 @@ p:last-child { margin-bottom: 0; }
.fit-cap__fill {
height: 100%;
width: 80%;
background: linear-gradient(90deg, var(--color-primary), #ff6b35);
background: linear-gradient(90deg, var(--color-primary), #D83302);
border-radius: 4px;
animation: fit-cap-breathe 3s ease-in-out infinite alternate;
}
@@ -11431,7 +11431,7 @@ p:last-child { margin-bottom: 0; }
text-align: center;
flex-shrink: 0;
}
.api-method--post { background: rgba(249,115,22,0.2); color: #fb923c; }
.api-method--post { background: rgba(216,51,2,0.2); color: #D83302; }
.api-method--get { background: rgba(74,222,128,0.15); color: #4ade80; }
.api-method--patch { background: rgba(96,165,250,0.15); color: #60a5fa; }
.api-endpoint {
@@ -11522,12 +11522,12 @@ p:last-child { margin-bottom: 0; }
50% { opacity: 0.8; box-shadow: 0 0 0 3px rgba(74,222,128,0); }
}
.cs-dot--orange {
background: #fb923c;
background: #D83302;
animation: cs-dot-orange-pulse 1.4s ease-in-out infinite;
}
@keyframes cs-dot-orange-pulse {
0%,100% { opacity: 1; box-shadow: 0 0 0 0 rgba(251,146,60,0.5); }
50% { opacity: 0.7; box-shadow: 0 0 0 3px rgba(251,146,60,0); }
0%,100% { opacity: 1; box-shadow: 0 0 0 0 rgba(216,51,2,0.5); }
50% { opacity: 0.7; box-shadow: 0 0 0 3px rgba(216,51,2,0); }
}
/* ── Scene: Margins (Partner Tier Progress) ─────────────────── */
@@ -11809,7 +11809,7 @@ p:last-child { margin-bottom: 0; }
color: rgba(255,255,255,0.3);
}
.cs-sla-timer {
color: #fb923c;
color: #D83302;
font-weight: 700;
font-family: monospace;
animation: cs-sla-tick 60s linear infinite;
@@ -11831,7 +11831,7 @@ p:last-child { margin-bottom: 0; }
gap: 6px;
font-size: 8.5px;
font-weight: 600;
color: #fb923c;
color: #D83302;
}
/* ── Scene: Early Access Roadmap ────────────────────────────── */
@@ -11875,7 +11875,7 @@ p:last-child { margin-bottom: 0; }
flex-shrink: 0;
letter-spacing: 0.3px;
}
.cs-feat__tag--beta { background: rgba(249,115,22,0.2); color: #fb923c; }
.cs-feat__tag--beta { background: rgba(216,51,2,0.2); color: #D83302; }
.cs-feat__tag--early { background: rgba(var(--color-primary-rgb),0.18); color: var(--color-primary); }
.cs-feat__tag--q3 { background: rgba(255,255,255,0.07); color: rgba(255,255,255,0.35); }
@@ -11968,7 +11968,7 @@ p:last-child { margin-bottom: 0; }
flex-shrink: 0;
}
.cs-device:nth-child(3) .cs-device__uptime {
color: #fb923c;
color: #D83302;
}
/* ── Scene: API Embed / SDK ─────────────────────────────────── */
@@ -12046,3 +12046,318 @@ p:last-child { margin-bottom: 0; }
.cs-code__line--5 { animation: none; opacity: 1; }
.cs-code__cursor { animation: none; }
}
/* ── Feature Ticker Animation ────────────────────────────────── */
.about-intro-visual.has-features-anim {
background: none;
box-shadow: none;
aspect-ratio: unset;
font-size: inherit;
color: inherit;
}
.features-stage {
display: flex;
flex-direction: column;
gap: 0.375rem;
width: 100%;
max-width: 380px;
margin: 0 auto;
background: #0d1117;
border-radius: var(--radius-lg, 12px);
padding: 1.25rem;
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.35);
}
.feat-item {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.6rem 0.75rem;
border-radius: var(--radius-md, 8px);
background: transparent;
transition: background 0.35s ease, transform 0.35s ease;
}
.feat-item.is-active {
background: rgba(var(--color-primary-rgb), 0.12);
transform: translateX(3px);
}
.feat-icon {
width: 30px;
height: 30px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.06);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-size: 0.75rem;
color: rgba(255, 255, 255, 0.3);
transition: background 0.35s ease, color 0.35s ease;
}
.feat-item.is-active .feat-icon {
background: rgba(var(--color-primary-rgb), 0.2);
color: var(--color-primary);
}
.feat-label {
flex: 1;
font-size: 0.8rem;
font-weight: 500;
color: rgba(255, 255, 255, 0.3);
transition: color 0.35s ease;
letter-spacing: 0.01em;
line-height: 1.3;
}
.feat-item.is-active .feat-label {
color: rgba(255, 255, 255, 0.9);
}
.feat-check {
width: 18px;
height: 18px;
border-radius: 50%;
border: 1.5px solid rgba(255, 255, 255, 0.12);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-size: 0.5rem;
color: transparent;
transition: background 0.35s ease, border-color 0.35s ease, color 0.35s ease;
}
.feat-item.is-active .feat-check {
background: var(--color-primary);
border-color: var(--color-primary);
color: #fff;
}
@media (prefers-reduced-motion: reduce) {
.feat-item {
transition: none;
transform: none;
background: rgba(255, 255, 255, 0.04);
}
.feat-item.is-active { transform: none; }
.feat-label { color: rgba(255, 255, 255, 0.65); }
.feat-check {
background: rgba(255, 255, 255, 0.15);
border-color: rgba(255, 255, 255, 0.25);
}
}
/* ═══════════════════════════════════════════════════════════
SELF CHECK-IN KIOSK ANIMATION
Used on the kiosks page platform-row selfCheckInAnim
═══════════════════════════════════════════════════════════ */
.checkin-stage { width: 100%; max-width: 400px; margin: 0 auto; }
.checkin-kiosk {
background: #0f1117; border: 1px solid #1e2435; border-radius: 12px; overflow: hidden;
box-shadow: 0 20px 60px rgba(0,0,0,0.5), 0 0 0 1px rgba(255,255,255,0.04); padding: 16px;
}
.checkin-kiosk__hdr {
display: flex; align-items: center; justify-content: space-between;
padding-bottom: 12px; margin-bottom: 14px;
border-bottom: 1px solid rgba(255,255,255,0.07);
}
.checkin-hdr__title { font-size: 13px; font-weight: 600; color: rgba(255,255,255,0.85); }
.checkin-hdr__badge {
font-size: 9px; font-weight: 600; letter-spacing: 0.5px; text-transform: uppercase;
background: rgba(var(--color-primary-rgb), 0.15); color: var(--color-primary);
border: 1px solid rgba(var(--color-primary-rgb), 0.25); border-radius: 4px; padding: 3px 7px;
animation: checkin-badge-pulse 3s ease-in-out infinite;
}
@keyframes checkin-badge-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.55; }
}
/* Scene container scenes stack absolutely, JS toggles .is-active */
.checkin-scenes { position: relative; min-height: 178px; }
.checkin-scene {
position: absolute; inset: 0;
opacity: 0; transition: opacity 0.45s ease;
pointer-events: none;
}
.checkin-scene.is-active { opacity: 1; pointer-events: auto; }
/* Scene 1: name lookup */
.checkin-search {
display: flex; align-items: center; gap: 8px;
background: rgba(255,255,255,0.06); border-radius: 8px;
padding: 10px 14px; margin-bottom: 12px;
}
.checkin-search__icon { font-size: 13px; }
.checkin-search__text { font-size: 12px; color: rgba(255,255,255,0.4); }
.checkin-result { display: flex; flex-direction: column; gap: 6px; }
.checkin-result__row {
display: flex; align-items: center; justify-content: space-between;
background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.07);
border-radius: 7px; padding: 10px 12px;
transition: background 0.2s;
}
.checkin-result__row:first-child {
border-color: rgba(var(--color-primary-rgb), 0.35);
background: rgba(var(--color-primary-rgb), 0.07);
animation: checkin-row-glow 2.8s ease-in-out infinite;
}
@keyframes checkin-row-glow {
0%, 100% { box-shadow: none; }
50% { box-shadow: 0 0 0 2px rgba(var(--color-primary-rgb), 0.18); }
}
.checkin-result__name { font-size: 12px; font-weight: 600; color: rgba(255,255,255,0.85); }
.checkin-result__detail { font-size: 10px; color: rgba(255,255,255,0.4); }
/* Scene 2: confirmation */
.checkin-confirm {
background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08);
border-radius: 9px; padding: 14px; margin-bottom: 12px; text-align: center;
}
.checkin-confirm__avatar {
width: 42px; height: 42px; border-radius: 50%;
background: rgba(var(--color-primary-rgb), 0.18);
border: 2px solid rgba(var(--color-primary-rgb), 0.35);
display: flex; align-items: center; justify-content: center; margin: 0 auto 8px;
font-size: 12px; font-weight: 700; color: var(--color-primary);
animation: checkin-avatar-in 0.4s ease-out backwards;
}
@keyframes checkin-avatar-in {
from { transform: scale(0.7); opacity: 0; }
to { transform: scale(1); opacity: 1; }
}
.checkin-confirm__name { font-size: 14px; font-weight: 700; color: rgba(255,255,255,0.9); margin-bottom: 4px; }
.checkin-confirm__appt { font-size: 11px; color: rgba(255,255,255,0.5); margin-bottom: 4px; }
.checkin-confirm__host { font-size: 10px; color: rgba(255,255,255,0.35); }
.checkin-btn {
width: 100%; padding: 12px; border: none; border-radius: 8px; cursor: pointer;
background: var(--color-primary); color: #fff;
font-size: 13px; font-weight: 700; letter-spacing: 0.3px;
animation: checkin-btn-pulse 2s ease-in-out infinite;
}
@keyframes checkin-btn-pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(var(--color-primary-rgb), 0.5); }
50% { box-shadow: 0 0 0 8px rgba(var(--color-primary-rgb), 0); }
}
/* Scene 3: queue ticket */
.checkin-ticket {
background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.08);
border-radius: 9px; padding: 20px 14px; text-align: center;
border-top: 3px solid #22c55e;
}
.checkin-ticket__label { font-size: 10px; color: #22c55e; font-weight: 600; letter-spacing: 0.5px; text-transform: uppercase; margin-bottom: 6px; }
.checkin-ticket__number {
font-size: 36px; font-weight: 800; color: rgba(255,255,255,0.95); line-height: 1; margin-bottom: 8px;
animation: checkin-number-in 0.5s cubic-bezier(0.22, 0.61, 0.36, 1) backwards;
}
@keyframes checkin-number-in {
from { transform: scale(0.6) translateY(10px); opacity: 0; }
to { transform: scale(1) translateY(0); opacity: 1; }
}
.checkin-ticket__detail { font-size: 12px; color: rgba(255,255,255,0.55); margin-bottom: 10px; }
.checkin-ticket__status { font-size: 10px; color: rgba(255,255,255,0.35); }
.checkin-ticket__status::before { content: ''; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: #22c55e; margin-right: 5px; vertical-align: middle; animation: checkin-dot-blink 1.4s ease-in-out infinite; }
@keyframes checkin-dot-blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
@media (prefers-reduced-motion: reduce) {
.checkin-badge-pulse,
.checkin-scene { transition: none; }
.checkin-scene.is-active { opacity: 1; position: static; }
.checkin-scene:not(.is-active) { display: none; }
.checkin-hdr__badge,
.checkin-result__row:first-child,
.checkin-btn,
.checkin-confirm__avatar,
.checkin-ticket__number,
.checkin-ticket__status::before { animation: none; }
}
/* ═══════════════════════════════════════════════════════════
SELF-ORDER / PRODUCT LOOKUP KIOSK ANIMATION
Used on the kiosks page platform-row selfOrderAnim
═══════════════════════════════════════════════════════════ */
.selforder-stage { width: 100%; max-width: 420px; margin: 0 auto; }
.selforder-kiosk {
background: #0f1117; border: 1px solid #1e2435; border-radius: 12px; overflow: hidden;
box-shadow: 0 20px 60px rgba(0,0,0,0.5), 0 0 0 1px rgba(255,255,255,0.04); padding: 16px;
}
.selforder-kiosk__hdr {
display: flex; align-items: center; justify-content: space-between;
padding-bottom: 12px; margin-bottom: 12px;
border-bottom: 1px solid rgba(255,255,255,0.07);
}
.selforder-hdr__title { font-size: 13px; font-weight: 600; color: rgba(255,255,255,0.85); }
.selforder-hdr__basket { font-size: 11px; color: rgba(255,255,255,0.45); }
.selforder-body { display: flex; gap: 10px; margin-bottom: 12px; }
/* Category sidebar */
.selforder-cats { display: flex; flex-direction: column; gap: 4px; flex-shrink: 0; width: 94px; }
.selforder-cat {
font-size: 10px; font-weight: 600; padding: 8px 10px; border-radius: 6px;
color: rgba(255,255,255,0.45); background: rgba(255,255,255,0.04);
border: 1px solid rgba(255,255,255,0.06); cursor: pointer;
transition: background 0.3s ease, color 0.3s ease, border-color 0.3s ease;
}
.selforder-cat.is-active {
background: rgba(var(--color-primary-rgb), 0.14);
color: var(--color-primary); border-color: rgba(var(--color-primary-rgb), 0.3);
}
.selforder-cat.is-active::before {
content: '▶ '; font-size: 7px; vertical-align: middle;
}
/* Product list */
.selforder-products { flex: 1; display: flex; flex-direction: column; gap: 5px; overflow: hidden; }
.selforder-product {
display: flex; align-items: center; gap: 6px;
padding: 8px 10px; border-radius: 6px;
background: rgba(255,255,255,0.04); border: 1px solid rgba(255,255,255,0.06);
opacity: 0; max-height: 0; overflow: hidden;
transition: opacity 0.3s ease, max-height 0.3s ease, border-color 0.2s ease, background 0.2s ease;
}
.selforder-product.is-visible {
opacity: 1; max-height: 48px;
}
.selforder-product.is-highlighted {
border-color: rgba(var(--color-primary-rgb), 0.35);
background: rgba(var(--color-primary-rgb), 0.07);
box-shadow: 0 0 0 2px rgba(var(--color-primary-rgb), 0.1);
}
.selforder-product__name { font-size: 11px; font-weight: 600; color: rgba(255,255,255,0.8); flex: 1; }
.selforder-product__price { font-size: 11px; font-weight: 700; color: var(--color-primary); flex-shrink: 0; }
.selforder-product__stock {
font-size: 9px; color: rgba(255,255,255,0.35); flex-shrink: 0;
border: 1px solid rgba(255,255,255,0.1); border-radius: 3px; padding: 1px 5px;
}
/* Add to order button */
.selforder-add {
width: 100%; padding: 11px; border: none; border-radius: 8px; cursor: pointer;
background: rgba(var(--color-primary-rgb), 0.15);
border: 1px solid rgba(var(--color-primary-rgb), 0.3);
color: var(--color-primary); font-size: 12px; font-weight: 700;
transition: background 0.25s ease, opacity 0.25s ease;
}
.selforder-add:disabled { opacity: 0.35; cursor: default; }
.selforder-add.is-enabled {
animation: selforder-add-glow 2s ease-in-out infinite;
}
@keyframes selforder-add-glow {
0%, 100% { box-shadow: none; }
50% { box-shadow: 0 0 0 4px rgba(var(--color-primary-rgb), 0.18); }
}
@media (prefers-reduced-motion: reduce) {
.selforder-cat { transition: none; }
.selforder-product { transition: none; max-height: none; }
.selforder-product.is-visible { opacity: 1; }
.selforder-add.is-enabled { animation: none; }
}

View File

@@ -0,0 +1,64 @@
/**
* About Page Animator
* Cycles an is-active highlight through feature rows in .features-stage.
* Respects prefers-reduced-motion and pauses via IntersectionObserver.
* Mirrors the patterns and conventions of solutions-animator.js.
*/
(function () {
'use strict';
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
var CYCLE_MS = 2000;
function initFeatures(stage) {
var items = stage.querySelectorAll('.feat-item');
if (!items.length) return;
var state = { idx: 0, timer: null };
function advance() {
items[state.idx].classList.remove('is-active');
state.idx = (state.idx + 1) % items.length;
items[state.idx].classList.add('is-active');
}
function startTimer() {
if (state.timer) return;
state.timer = setInterval(advance, CYCLE_MS);
}
function stopTimer() {
clearInterval(state.timer);
state.timer = null;
}
items[0].classList.add('is-active');
if ('IntersectionObserver' in window) {
new IntersectionObserver(function (entries) {
for (var i = 0; i < entries.length; i++) {
entries[i].isIntersecting ? startTimer() : stopTimer();
}
}, { rootMargin: '200px', threshold: 0.05 }).observe(stage);
} else {
startTimer();
}
}
function boot() {
var stages = document.querySelectorAll('.features-stage');
if (!stages.length) return;
for (var i = 0; i < stages.length; i++) {
if (stages[i]._featAnim) continue;
stages[i]._featAnim = true;
initFeatures(stages[i]);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot);
} else {
boot();
}
})();

View File

@@ -0,0 +1,191 @@
/**
* Kiosks Page Animators
* 1. Self Check-In — cycles through three visitor check-in scenarios
* 2. Self Order — cycles active category + highlighted product in a catalogue kiosk
*
* Both respect prefers-reduced-motion and pause via IntersectionObserver.
* Mirrors the patterns and conventions of solutions-animator.js.
*/
/* ── 1. Self Check-In Kiosk Animator ───────────────────────────────────── */
(function () {
'use strict';
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
var CYCLE_MS = 3500; /* ms per scene */
function initCheckin(stage) {
var scenes = stage.querySelectorAll('.checkin-scene');
if (!scenes.length) return;
var state = { idx: 0, timer: null, paused: false };
/* Show the first scene immediately */
scenes[0].classList.add('is-active');
function advance() {
if (state.paused) return;
scenes[state.idx].classList.remove('is-active');
state.idx = (state.idx + 1) % scenes.length;
scenes[state.idx].classList.add('is-active');
}
function startTimer() {
if (state.timer) return;
state.timer = setInterval(advance, CYCLE_MS);
}
function stopTimer() {
clearInterval(state.timer);
state.timer = null;
}
if ('IntersectionObserver' in window) {
new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
state.paused = !e.isIntersecting;
e.isIntersecting ? startTimer() : stopTimer();
});
}, { rootMargin: '200px', threshold: 0.05 }).observe(stage);
}
startTimer();
}
function boot() {
var stages = document.querySelectorAll('.checkin-stage');
if (!stages.length) return;
for (var i = 0; i < stages.length; i++) {
if (stages[i]._checkinAnim) continue;
stages[i]._checkinAnim = true;
initCheckin(stages[i]);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot);
} else {
boot();
}
}());
/* ── 2. Self-Order / Product Lookup Kiosk Animator ─────────────────────── */
(function () {
'use strict';
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
/* In reduced-motion: just show all products for the first category */
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('.selforder-stage').forEach(function (stage) {
var first = stage.querySelector('.selforder-cat');
if (first) first.classList.add('is-active');
var cat = first ? first.getAttribute('data-so-cat') : null;
if (cat) {
stage.querySelectorAll('.selforder-product[data-so-cat="' + cat + '"]').forEach(function (p) {
p.classList.add('is-visible');
});
}
});
});
return;
}
/* Category rotation order */
var CATS = ['electronics', 'clothing', 'food', 'home'];
var CYCLE_MS = 4000; /* ms per category */
/* Which product index within the visible set to highlight each cycle */
var HIGHLIGHT_IDX = [0, 1, 0, 1];
function initSelfOrder(stage) {
var catEls = stage.querySelectorAll('.selforder-cat');
var productEls = stage.querySelectorAll('.selforder-product');
var addBtn = stage.querySelector('.selforder-add');
if (!catEls.length || !productEls.length) return;
var state = { idx: 0, timer: null, paused: false };
function showCategory(catIdx) {
var cat = CATS[catIdx % CATS.length];
/* Update category tabs */
for (var i = 0; i < catEls.length; i++) {
catEls[i].classList.toggle('is-active', catEls[i].getAttribute('data-so-cat') === cat);
}
/* Show matching products, hide others */
var visible = [];
for (var j = 0; j < productEls.length; j++) {
var matches = productEls[j].getAttribute('data-so-cat') === cat;
productEls[j].classList.toggle('is-visible', matches);
productEls[j].classList.remove('is-highlighted');
if (matches) visible.push(productEls[j]);
}
/* Highlight one product after a short delay (simulates tap) */
var hlIdx = HIGHLIGHT_IDX[catIdx % HIGHLIGHT_IDX.length];
var toHighlight = visible[hlIdx % (visible.length || 1)];
if (toHighlight) {
setTimeout(function () {
toHighlight.classList.add('is-highlighted');
if (addBtn) {
addBtn.removeAttribute('disabled');
addBtn.classList.add('is-enabled');
}
}, 900);
}
/* Reset button when next cycle starts */
if (addBtn) {
addBtn.setAttribute('disabled', '');
addBtn.classList.remove('is-enabled');
}
}
/* Show first category immediately */
showCategory(0);
function advance() {
if (state.paused) return;
state.idx++;
showCategory(state.idx);
}
function startTimer() {
if (state.timer) return;
state.timer = setInterval(advance, CYCLE_MS);
}
function stopTimer() {
clearInterval(state.timer);
state.timer = null;
}
if ('IntersectionObserver' in window) {
new IntersectionObserver(function (entries) {
entries.forEach(function (e) {
state.paused = !e.isIntersecting;
e.isIntersecting ? startTimer() : stopTimer();
});
}, { rootMargin: '200px', threshold: 0.05 }).observe(stage);
}
startTimer();
}
function boot() {
var stages = document.querySelectorAll('.selforder-stage');
if (!stages.length) return;
for (var i = 0; i < stages.length; i++) {
if (stages[i]._selforderAnim) continue;
stages[i]._selforderAnim = true;
initSelfOrder(stages[i]);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', boot);
} else {
boot();
}
}());

View File

@@ -608,6 +608,7 @@
cloudAnim: { type: 'boolean', default: false },
bundleAnim: { type: 'boolean', default: false },
demoAnim: { type: 'boolean', default: false },
featuresAnim: { type: 'boolean', default: false },
imgId: { type: 'number', default: 0 },
imgUrl: { type: 'string', default: '' },
imgAlt: { type: 'string', default: '' },
@@ -651,7 +652,8 @@
el(TG, { label: 'Reversed layout', checked: a.reversed, onChange: function (v) { s({ reversed: v }); } }),
el(TG, { label: 'Cloud Server Animation', checked: a.cloudAnim, onChange: function (v) { s({ cloudAnim: v }); } }),
el(TG, { label: 'Player + Display Bundle Animation', checked: !!a.bundleAnim, onChange: function (v) { s({ bundleAnim: v }); } }),
el(TG, { label: 'Command Center Demo Animation', checked: !!a.demoAnim, onChange: function (v) { s({ demoAnim: v }); } })
el(TG, { label: 'Command Center Demo Animation', checked: !!a.demoAnim, onChange: function (v) { s({ demoAnim: v }); } }),
el(TG, { label: 'Feature Ticker Animation', checked: !!a.featuresAnim, onChange: function (v) { s({ featuresAnim: v }); } })
),
el(PB, { title: 'Visual Image', initialOpen: false },
el(MUC, null,
@@ -685,7 +687,7 @@
el(RT, { tagName: 'h2', style: { marginBottom: '1.5rem' }, value: a.heading, onChange: function (v) { s({ heading: v }); }, placeholder: 'Heading...' }),
el(RT, { tagName: 'p', className: 'lead', value: a.description, onChange: function (v) { s({ description: v }); }, placeholder: 'Description...' })
),
el('div', { className: 'about-intro-visual' + (a.cloudAnim ? ' has-cloud-anim' : (a.bundleAnim ? ' has-pkg-anim' : (a.demoAnim ? ' has-demo-anim' : (a.imgUrl ? ' has-img' : '')))), style: a.reversed ? { direction: 'ltr' } : {} }, visualContent)
el('div', { className: 'about-intro-visual' + (a.cloudAnim ? ' has-cloud-anim' : (a.bundleAnim ? ' has-pkg-anim' : (a.demoAnim ? ' has-demo-anim' : (a.featuresAnim ? ' has-features-anim' : (a.imgUrl ? ' has-img' : ''))))), style: a.reversed ? { direction: 'ltr' } : {} }, visualContent)
)
)
)

View File

@@ -342,6 +342,7 @@ add_action('init', function () {
'cloudAnim' => ['type' => 'boolean', 'default' => false],
'bundleAnim' => ['type' => 'boolean', 'default' => false],
'demoAnim' => ['type' => 'boolean', 'default' => false],
'featuresAnim' => ['type' => 'boolean', 'default' => false],
'imgId' => ['type' => 'number', 'default' => 0],
'imgUrl' => ['type' => 'string', 'default' => ''],
'imgAlt' => ['type' => 'string', 'default' => ''],
@@ -615,6 +616,8 @@ add_action('init', function () {
'whitelabelAnim' => ['type' => 'boolean', 'default' => false],
'resellerAnim' => ['type' => 'boolean', 'default' => false],
'apiAnim' => ['type' => 'boolean', 'default' => false],
'selfCheckInAnim' => ['type' => 'boolean', 'default' => false],
'selfOrderAnim' => ['type' => 'boolean', 'default' => false],
'galleryIds' => ['type' => 'array', 'default' => [], 'items' => ['type' => 'number']],
],
'supports' => $block_supports,
@@ -1012,7 +1015,7 @@ function oribi_render_intro_section($a)
<h2 style="margin-bottom:1.5rem;"><?php echo wp_kses_post($a['heading']); ?></h2>
<p class="lead"><?php echo wp_kses_post($a['description']); ?></p>
</div>
<div class="about-intro-visual<?php if (!empty($a['cloudAnim'])) echo ' has-cloud-anim'; elseif (!empty($a['bundleAnim'])) echo ' has-pkg-anim'; elseif (!empty($a['demoAnim'])) echo ' has-demo-anim'; ?>"<?php echo $ltr; ?>>
<div class="about-intro-visual<?php if (!empty($a['cloudAnim'])) echo ' has-cloud-anim'; elseif (!empty($a['bundleAnim'])) echo ' has-pkg-anim'; elseif (!empty($a['demoAnim'])) echo ' has-demo-anim'; elseif (!empty($a['featuresAnim'])) echo ' has-features-anim'; ?>"<?php echo $ltr; ?>>
<?php
if (!empty($a['cloudAnim'])) {
echo '<div class="ds-anim-container">';
@@ -1093,6 +1096,15 @@ function oribi_render_intro_section($a)
$cc .= '</div>'; // .cc-stage
echo $cc;
}
elseif (!empty($a['featuresAnim'])) {
$feat = '<div class="features-stage" aria-hidden="true">';
$feat .= '<div class="feat-item"><span class="feat-icon"><i class="fas fa-cloud" aria-hidden="true"></i></span><span class="feat-label">Cloud Content Management</span><span class="feat-check"><i class="fas fa-check" aria-hidden="true"></i></span></div>';
$feat .= '<div class="feat-item"><span class="feat-icon"><i class="fas fa-calendar-alt" aria-hidden="true"></i></span><span class="feat-label">Intelligent Scheduling</span><span class="feat-check"><i class="fas fa-check" aria-hidden="true"></i></span></div>';
$feat .= '<div class="feat-item"><span class="feat-icon"><i class="fas fa-chart-line" aria-hidden="true"></i></span><span class="feat-label">Live Data Integration</span><span class="feat-check"><i class="fas fa-check" aria-hidden="true"></i></span></div>';
$feat .= '<div class="feat-item"><span class="feat-icon"><i class="fas fa-star" aria-hidden="true"></i></span><span class="feat-label">Outstanding on Screen</span><span class="feat-check"><i class="fas fa-check" aria-hidden="true"></i></span></div>';
$feat .= '</div>';
echo $feat;
}
else {
echo wp_kses_post($a['visual']);
}
@@ -3634,6 +3646,61 @@ function oribi_render_platform_row($a)
$visual_html = $ap;
$visual_cls = 'platform-visual has-api';
}
elseif (!empty($a['selfCheckInAnim'])) {
/* ── Self Check-In Kiosk ── */
$sc = '<div class="checkin-stage" aria-hidden="true">';
$sc .= '<div class="checkin-kiosk">';
$sc .= '<div class="checkin-kiosk__hdr"><span class="checkin-hdr__title">Visitor Check-In</span><span class="checkin-hdr__badge">Touch to Start</span></div>';
$sc .= '<div class="checkin-scenes">';
/* Scene 1: name lookup */
$sc .= '<div class="checkin-scene checkin-scene--lookup">';
$sc .= '<div class="checkin-search"><span class="checkin-search__icon">&#128269;</span><span class="checkin-search__text">Enter your name...</span></div>';
$sc .= '<div class="checkin-result"><div class="checkin-result__row"><span class="checkin-result__name">James Wilson</span><span class="checkin-result__detail">10:30 &#183; Meeting Room C</span></div><div class="checkin-result__row"><span class="checkin-result__name">Sarah Chen</span><span class="checkin-result__detail">11:00 &#183; Board Room</span></div><div class="checkin-result__row"><span class="checkin-result__name">Marcus Lee</span><span class="checkin-result__detail">11:15 &#183; Desk 4B</span></div></div>';
$sc .= '</div>'; /* scene lookup */
/* Scene 2: confirmation */
$sc .= '<div class="checkin-scene checkin-scene--confirm">';
$sc .= '<div class="checkin-confirm"><div class="checkin-confirm__avatar">JW</div><div class="checkin-confirm__name">James Wilson</div><div class="checkin-confirm__appt">10:30 &#183; Meeting Room C &#183; Level 2</div><div class="checkin-confirm__host">Host: Emma Turner</div></div>';
$sc .= '<button class="checkin-btn">&#10003; Check In</button>';
$sc .= '</div>'; /* scene confirm */
/* Scene 3: queue ticket */
$sc .= '<div class="checkin-scene checkin-scene--ticket">';
$sc .= '<div class="checkin-ticket"><div class="checkin-ticket__label">You&#39;re Checked In</div><div class="checkin-ticket__number">Q-047</div><div class="checkin-ticket__detail">Head to Meeting Room C, Level 2</div><div class="checkin-ticket__status">&#9679; Emma Turner has been notified</div></div>';
$sc .= '</div>'; /* scene ticket */
$sc .= '</div>'; /* checkin-scenes */
$sc .= '</div>'; /* checkin-kiosk */
$sc .= '</div>'; /* checkin-stage */
$visual_html = $sc;
$visual_cls = 'platform-visual has-checkin';
}
elseif (!empty($a['selfOrderAnim'])) {
/* ── Product Lookup & Self-Ordering Kiosk ── */
$so = '<div class="selforder-stage" aria-hidden="true">';
$so .= '<div class="selforder-kiosk">';
$so .= '<div class="selforder-kiosk__hdr"><span class="selforder-hdr__title">Browse &amp; Order</span><span class="selforder-hdr__basket">&#128722; 0</span></div>';
$so .= '<div class="selforder-body">';
$so .= '<div class="selforder-cats">';
$so .= '<div class="selforder-cat selforder-cat--electronics" data-so-cat="electronics">Electronics</div>';
$so .= '<div class="selforder-cat selforder-cat--clothing" data-so-cat="clothing">Clothing</div>';
$so .= '<div class="selforder-cat selforder-cat--food" data-so-cat="food">Food &amp; Drink</div>';
$so .= '<div class="selforder-cat selforder-cat--home" data-so-cat="home">Home</div>';
$so .= '</div>';
$so .= '<div class="selforder-products">';
$so .= '<div class="selforder-product" data-so-cat="electronics"><span class="selforder-product__name">Wireless Headphones</span><span class="selforder-product__price">&pound;49.99</span><span class="selforder-product__stock">In Stock</span></div>';
$so .= '<div class="selforder-product" data-so-cat="electronics"><span class="selforder-product__name">USB-C Hub</span><span class="selforder-product__price">&pound;29.99</span><span class="selforder-product__stock">In Stock</span></div>';
$so .= '<div class="selforder-product" data-so-cat="clothing"><span class="selforder-product__name">Merino Pullover</span><span class="selforder-product__price">&pound;64.00</span><span class="selforder-product__stock">In Stock</span></div>';
$so .= '<div class="selforder-product" data-so-cat="clothing"><span class="selforder-product__name">Slim Chinos</span><span class="selforder-product__price">&pound;44.00</span><span class="selforder-product__stock">Low Stock</span></div>';
$so .= '<div class="selforder-product" data-so-cat="food"><span class="selforder-product__name">Cold Brew Coffee</span><span class="selforder-product__price">&pound;3.50</span><span class="selforder-product__stock">Available</span></div>';
$so .= '<div class="selforder-product" data-so-cat="food"><span class="selforder-product__name">Smoked Salmon Bagel</span><span class="selforder-product__price">&pound;7.25</span><span class="selforder-product__stock">Available</span></div>';
$so .= '<div class="selforder-product" data-so-cat="home"><span class="selforder-product__name">Desk Lamp</span><span class="selforder-product__price">&pound;34.99</span><span class="selforder-product__stock">In Stock</span></div>';
$so .= '<div class="selforder-product" data-so-cat="home"><span class="selforder-product__name">Storage Basket</span><span class="selforder-product__price">&pound;18.00</span><span class="selforder-product__stock">In Stock</span></div>';
$so .= '</div>'; /* selforder-products */
$so .= '</div>'; /* selforder-body */
$so .= '<button class="selforder-add" disabled>&#43; Add to Order</button>';
$so .= '</div>'; /* selforder-kiosk */
$so .= '</div>'; /* selforder-stage */
$visual_html = $so;
$visual_cls = 'platform-visual has-selforder';
}
else {
$visual_html = oribi_render_icon($a['visual'] ?? '');
$visual_cls = 'platform-visual';

View File

@@ -74,6 +74,24 @@ add_action( 'wp_enqueue_scripts', function () {
true
);
// About page animator - feature ticker cycling highlight
wp_enqueue_script(
'oribi-about-animator',
ORIBI_URI . '/assets/js/about-animator.js',
[],
ORIBI_VERSION . '.' . filemtime( ORIBI_DIR . '/assets/js/about-animator.js' ),
true
);
// Kiosks page animators - self check-in scenario cycling and product catalogue kiosk
wp_enqueue_script(
'oribi-kiosks-animator',
ORIBI_URI . '/assets/js/kiosks-animator.js',
[],
ORIBI_VERSION . '.' . filemtime( ORIBI_DIR . '/assets/js/kiosks-animator.js' ),
true
);
// Localize AJAX endpoint for the contact form
wp_localize_script( 'oribi-main', 'oribiAjax', [
'url' => admin_url( 'admin-ajax.php' ),

View File

@@ -39,7 +39,7 @@ function oribi_get_theme_defaults() {
'color_bg_alt' => '#F5F5F5',
/* ── Dark-mode colour palette ───────────────────────── */
'dark_primary' => '#FF6B3D',
'dark_primary' => '#D83302',
'dark_primary_dk' => '#D83302',
'dark_primary_lt' => 'rgba(216,51,2,0.15)',
'dark_accent' => '#66BB6A',

View File

@@ -85,7 +85,7 @@
},
"custom": {
"dark": {
"primary": "#FF6B3D",
"primary": "#D83302",
"primary-dk": "#D83302",
"primary-lt": "rgba(216,51,2,0.15)",
"accent": "#66BB6A",