diff --git a/pages/about.php b/pages/about.php index 38a5e04..c5e2a30 100644 --- a/pages/about.php +++ b/pages/about.php @@ -1,30 +1,36 @@ + +return <<<'ORIBI_SYNC_CONTENT' - + + - + - - + + + + - + + + + + @@ -33,3 +39,4 @@ +ORIBI_SYNC_CONTENT; diff --git a/pages/contact.php b/pages/contact.php index 988145c..38a8c3e 100644 --- a/pages/contact.php +++ b/pages/contact.php @@ -1,20 +1,22 @@ - - +return <<<'ORIBI_SYNC_CONTENT' + + + + + + - - + + @@ -26,3 +28,4 @@ +ORIBI_SYNC_CONTENT; diff --git a/pages/demo.php b/pages/demo.php index eb9627a..8bf6f72 100644 --- a/pages/demo.php +++ b/pages/demo.php @@ -1,25 +1,31 @@ - - +return <<<'ORIBI_SYNC_CONTENT' + + + + - + + + - - + + + + + +ORIBI_SYNC_CONTENT; diff --git a/pages/design.php b/pages/design.php new file mode 100644 index 0000000..9e61fc7 --- /dev/null +++ b/pages/design.php @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +ORIBI_SYNC_CONTENT; diff --git a/pages/devices.php b/pages/devices.php index a1d6ca9..8a8cde4 100644 --- a/pages/devices.php +++ b/pages/devices.php @@ -1,27 +1,31 @@ + +return <<<'ORIBI_SYNC_CONTENT' - - - - + + + + + + - - - - - - - + + + + + + + + + + @@ -30,10 +34,30 @@ - + +
+
+
+
+

Pre-Configured OTS Players

+

Every player device ordered through OTS arrives pre-configured and already paired with your Command Center. Plug in power and HDMI, connect to your network, and your screens are ready to publish.

+
+
+

Bring Your Own Player (BYO)

+

Already have compatible media players? We can onboard BYO hardware and connect it to your Command Center. Our team will confirm compatibility requirements and provide setup guidance before rollout.

+
+
+
+
+ + + + + +ORIBI_SYNC_CONTENT; diff --git a/pages/faq.php b/pages/faq.php index 1e58473..fa063e2 100644 --- a/pages/faq.php +++ b/pages/faq.php @@ -1,37 +1,46 @@ - - - +return <<<'ORIBI_SYNC_CONTENT' + + + + + + - + + - - + + + + + - + + + + - + +ORIBI_SYNC_CONTENT; diff --git a/pages/features.php b/pages/features.php index c689349..702abe9 100644 --- a/pages/features.php +++ b/pages/features.php @@ -1,33 +1,51 @@ - - - - - +return <<<'ORIBI_SYNC_CONTENT' + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -39,10 +57,25 @@ - - - - + + + + + + + + + + + + + + + + + + - + +ORIBI_SYNC_CONTENT; diff --git a/pages/home.php b/pages/home.php index 87d2ad3..b2f101b 100644 --- a/pages/home.php +++ b/pages/home.php @@ -6,14 +6,16 @@ */ return <<<'ORIBI_SYNC_CONTENT' - + - - + + + + - + diff --git a/pages/pricing.php b/pages/pricing.php index 62c5d54..60407d1 100644 --- a/pages/pricing.php +++ b/pages/pricing.php @@ -9,22 +9,22 @@ return <<<'ORIBI_SYNC_CONTENT' - + - + - + - - + + - + - + diff --git a/pages/resources.php b/pages/resources.php index 6bd2b91..950a884 100644 --- a/pages/resources.php +++ b/pages/resources.php @@ -1,13 +1,12 @@ - + +return <<<'ORIBI_SYNC_CONTENT' + @@ -40,3 +39,4 @@ +ORIBI_SYNC_CONTENT; diff --git a/pages/solutions.php b/pages/solutions.php index e702a4e..7ac9587 100644 --- a/pages/solutions.php +++ b/pages/solutions.php @@ -1,20 +1,24 @@ + +return <<<'ORIBI_SYNC_CONTENT' - + + - - + + + + + + @@ -22,10 +26,14 @@ - - - - + + + + + + + - + +ORIBI_SYNC_CONTENT; diff --git a/theme/assets/css/main.css b/theme/assets/css/main.css index 518c377..d87a104 100644 --- a/theme/assets/css/main.css +++ b/theme/assets/css/main.css @@ -5,11 +5,11 @@ /* ── 1. CSS Variables ───────────────────────────────────────── */ :root, [data-theme="light"] { - /* ── Light palette - British Racing Green + Monochrome ── */ - --color-primary: var(--wp--preset--color--primary, #004225); - --color-primary-dk: var(--wp--preset--color--primary-dk, #002E1A); - --color-primary-lt: var(--wp--preset--color--primary-lt, #E8F5E9); - --color-primary-rgb: 0,66,37; + /* ── Light palette - Orange Primary + Green Accent ── */ + --color-primary: var(--wp--preset--color--primary, #D83302); + --color-primary-dk: var(--wp--preset--color--primary-dk, #A22702); + --color-primary-lt: var(--wp--preset--color--primary-lt, #FFF0EB); + --color-primary-rgb: 216,51,2; --color-accent: var(--wp--preset--color--accent, #4CAF50); --color-accent-dk: var(--wp--preset--color--accent-dk, #388E3C); --color-accent-lt: var(--wp--preset--color--accent-lt, #E8F5E9); @@ -52,10 +52,10 @@ /* ── Dark Mode ────────────────────────────────────────────── */ [data-theme="dark"] { - --color-primary: var(--wp--custom--dark--primary, #4CAF50); - --color-primary-dk: var(--wp--custom--dark--primary-dk, #004225); - --color-primary-lt: var(--wp--custom--dark--primary-lt, rgba(0,66,37,.15)); - --color-primary-rgb: 76,175,80; + --color-primary: var(--wp--custom--dark--primary, #FF6B3D); + --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-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)); @@ -97,6 +97,25 @@ img, video { max-width: 100%; display: block; } a { color: inherit; text-decoration: none; transition: color var(--transition); } a:hover { color: var(--color-primary); } +/* ── Scroll-reveal animation classes ───────────────────────── */ +.scroll-hidden { + opacity: 0 !important; + transform: translateY(24px) !important; + /* Prevent transition: all on card elements from animating the hide, + which would cause a visible fade-out flicker and race conditions + with IntersectionObserver — especially after Gutenberg re-saves + inject inline styles onto the block element. */ + transition: none !important; +} +.scroll-visible { + opacity: 1; + transform: translateY(0); + /* !important ensures this wins over transition: all on .oribi-card + (same selector specificity, but .oribi-card appears later in the + stylesheet and would otherwise override this). */ + transition: opacity .5s ease, transform .5s ease !important; +} + /* Smooth theme transition */ body, .site-header, @@ -104,9 +123,15 @@ body, .pricing-card, .contact-form-wrap, .platform-visual, +.platform-text h3, +.platform-text p, .form-group input, .form-group textarea, -.form-group select { transition: background var(--transition), color var(--transition), border-color var(--transition), box-shadow var(--transition); } +.form-group select, +.comparison-table-wrap, +.comparison-table thead th, +.comparison-table tbody td, +.comparison-group-row td { transition: background var(--transition), color var(--transition), border-color var(--transition), box-shadow var(--transition); } ul { list-style: none; } button { font-family: inherit; cursor: pointer; border: none; background: none; } @@ -258,9 +283,6 @@ p:last-child { margin-bottom: 0; } backdrop-filter: blur(8px); } -.site-header.scrolled .logo-text { color: var(--color-heading); } -.site-header.scrolled .nav-menu a { color: var(--header-scrolled-text); } - .header-inner { display: flex; align-items: center; @@ -273,8 +295,9 @@ p:last-child { margin-bottom: 0; } display: flex; align-items: center; flex-shrink: 0; - max-height: 60px; + max-height: 80px; overflow: hidden; + gap: 1.25rem; } .site-logo a { display: flex; align-items: center; } .custom-logo-link { display: flex; align-items: center; } @@ -282,10 +305,10 @@ p:last-child { margin-bottom: 0; } .custom-logo-link img, .custom-logo, .header-inner img { - max-height: 60px !important; + max-height: 80px !important; width: auto !important; height: auto !important; - max-width: 220px !important; + max-width: 260px !important; display: block !important; object-fit: contain; } @@ -296,12 +319,14 @@ p:last-child { margin-bottom: 0; } letter-spacing: -.01em; transition: color var(--transition); } -.logo-text strong { font-weight: 800; color: var(--color-accent); } +.logo-text strong { font-weight: 800; color: var(--color-primary); margin-right: .25em; } -/* When header is over light background (inner pages) */ -.page-header-light .site-header { background: var(--color-bg); box-shadow: var(--shadow-sm); } -.page-header-light .logo-text { color: var(--color-heading); } -.page-header-light .nav-menu a { color: var(--color-text); } +/* When header is over a light hero (set by JS) — unscrolled only */ +.site-header.over-light-hero:not(.scrolled) .nav-menu a { color: var(--color-text); } +.site-header.over-light-hero:not(.scrolled) .nav-menu a:hover, +.site-header.over-light-hero:not(.scrolled) .nav-menu .current-menu-item > a { color: var(--color-primary); } +.site-header.over-light-hero:not(.scrolled) .logo-text { color: var(--color-heading); } +.site-header.over-light-hero:not(.scrolled) .nav-toggle span { background: var(--color-heading); } /* Nav */ .site-nav { margin-left: auto; } @@ -318,12 +343,12 @@ p:last-child { margin-bottom: 0; } padding-bottom: 2px; } -/* Light mode: nav text over hero needs to be dark */ -[data-theme="light"] .nav-menu a { color: var(--color-text); } -[data-theme="light"] .nav-menu a:hover, -[data-theme="light"] .nav-menu .current-menu-item > a { color: var(--color-primary); } -[data-theme="light"] .logo-text { color: var(--color-heading); } -[data-theme="light"] .nav-toggle span { background: var(--color-heading); } +/* Scrolled: switch to theme-appropriate text on solid bg */ +.site-header.scrolled .nav-menu a { color: var(--header-scrolled-text); } +.site-header.scrolled .nav-menu a:hover, +.site-header.scrolled .nav-menu .current-menu-item > a { color: var(--color-primary); } +.site-header.scrolled .logo-text { color: var(--color-heading); } +.site-header.scrolled .nav-toggle span { background: var(--color-heading); } .nav-menu a::after { content: ''; position: absolute; @@ -339,10 +364,6 @@ p:last-child { margin-bottom: 0; } .nav-menu a:hover::after, .nav-menu .current-menu-item > a::after { width: 100%; } -.site-header.scrolled .nav-menu a { color: var(--header-scrolled-text); } -.site-header.scrolled .nav-menu a:hover, -.site-header.scrolled .nav-menu .current-menu-item > a { color: var(--color-primary); } - /* ── Dropdown sub-menu ─────────────────────────────────────── */ .nav-menu > li { position: relative; @@ -472,7 +493,6 @@ p:last-child { margin-bottom: 0; } border-radius: 2px; transition: all var(--transition); } -.site-header.scrolled .nav-toggle span { background: var(--color-heading); } .nav-toggle.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); } .nav-toggle.open span:nth-child(2) { opacity: 0; } .nav-toggle.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); } @@ -486,8 +506,16 @@ p:last-child { margin-bottom: 0; } top: 0; left: 0; right: 0; bottom: 0; background: var(--color-dark); z-index: 99; + overflow: hidden; + } + .site-nav.open .nav-menu { padding: 5rem 2rem 2rem; + height: 100vh; overflow-y: auto; + -webkit-overflow-scrolling: touch; + } + body.menu-open { + overflow: hidden; } .site-nav.open .nav-menu { flex-direction: column; @@ -560,7 +588,7 @@ p:last-child { margin-bottom: 0; } animation: dc-breathe 8s ease-in-out infinite; pointer-events: none; display: block; - will-change: transform; + will-change: filter; } .hero-overlay { @@ -1957,7 +1985,7 @@ p:last-child { margin-bottom: 0; } .platform-row.reverse .platform-text { order: 2; } .platform-row.reverse .platform-visual { order: 1; } -.platform-text h3 { font-size: 1.5rem; margin-bottom: 1rem; } +.platform-text h3 { font-size: 1.5rem; margin-bottom: 1rem; color: var(--color-heading); } .platform-text p { color: var(--color-text-muted); } .platform-visual { @@ -2061,6 +2089,7 @@ p:last-child { margin-bottom: 0; } border: none !important; border-radius: 0; aspect-ratio: unset; + min-height: 300px; padding: 0; overflow: visible; position: relative; @@ -2135,6 +2164,91 @@ p:last-child { margin-bottom: 0; } animation: da-scan 3s linear infinite; pointer-events: none; } + +.da-promo { + position: absolute; + inset: 0; + z-index: 1; + padding: 6% 7%; + display: flex; + flex-direction: column; + gap: 6%; +} + +.da-promo__top { + display: flex; + align-items: center; + gap: 6px; +} + +.da-promo__dot { + width: 7px; + height: 7px; + border-radius: 50%; + background: #f97316; + box-shadow: 0 0 6px rgba(249,115,22,.55); + animation: da-dot-pulse 2.8s ease-in-out infinite; +} + +.da-promo__brand { + width: 40%; + height: 5px; + border-radius: 999px; + background: linear-gradient(90deg, rgba(249,115,22,.95), rgba(251,191,36,.75)); +} + +.da-promo__hero { + height: 30%; + 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%); +} + +.da-promo__row { + display: flex; + align-items: center; + gap: 6px; +} + +.da-promo__line { + display: block; + height: 4px; + border-radius: 999px; + background: rgba(241,245,249,.70); +} + +.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__ticker { + margin-top: auto; + display: flex; + align-items: center; + gap: 5px; + animation: da-ticker 6.5s linear infinite; +} + +.da-promo__chip { + width: 34%; + height: 8px; + border-radius: 999px; + background: linear-gradient(90deg, rgba(74,222,128,.72), rgba(56,189,248,.72)); + flex: 0 0 auto; +} + +@keyframes da-dot-pulse { + 0%, 100% { transform: scale(1); opacity: .92; } + 50% { transform: scale(1.22); opacity: 1; } +} + +@keyframes da-ticker { + 0% { transform: translateX(0); } + 50% { transform: translateX(-8%); } + 100% { transform: translateX(0); } +} /* Device label */ .da-label { display: block; @@ -2385,6 +2499,9 @@ p:last-child { margin-bottom: 0; } .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; } +.da-vwall .da-panel:nth-child(2) .da-promo { filter: hue-rotate(32deg); } +.da-vwall .da-panel:nth-child(3) .da-promo { filter: hue-rotate(-18deg); } +.da-vwall .da-panel:nth-child(4) .da-promo { filter: hue-rotate(58deg); } /* ── Responsive scale-down ─────────────────────── */ @media (max-width: 900px) { .da-device { transform: translate(-50%,-50%) scale(0.76); } @@ -2399,6 +2516,8 @@ p:last-child { margin-bottom: 0; } @media (prefers-reduced-motion: reduce) { .da-device { transition: none; } .da-screen::after { animation: none; } + .da-promo__dot, + .da-promo__ticker { animation: none; } } /* ── 10c. TV Stick Plug Animation ──────────────────────────── */ @@ -2411,6 +2530,10 @@ p:last-child { margin-bottom: 0; } overflow: visible; position: relative; font-size: inherit; + display: flex; + align-items: center; + justify-content: center; + align-self: center; } .ts-stage { position: relative; @@ -2419,6 +2542,9 @@ p:last-child { margin-bottom: 0; } display: flex; align-items: center; justify-content: center; + /* Offset center leftward to account for the stick protruding on the right, + so the TV+stick combo appears visually centered in the column */ + padding-right: 90px; } /* ── TV ── */ .ts-tv { @@ -2428,8 +2554,8 @@ p:last-child { margin-bottom: 0; } position: relative; } .ts-tv__body { - width: 320px; - height: 188px; + width: 400px; + height: 235px; background: #111; border: 4px solid #111; border-radius: 6px 6px 4px 4px; @@ -2487,6 +2613,266 @@ p:last-child { margin-bottom: 0; } 40% { filter: brightness(1.25); } 100% { filter: brightness(1); } } +/* ── Promotional slides inside screen ── */ +.ts-slides { + position: absolute; + inset: 0; + opacity: 0; + transition: opacity 0.6s ease; + z-index: 1; +} +.ts-stage.is-playing .ts-slides { opacity: 1; } +.ts-slide { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 12%; + opacity: 0; + transition: opacity 0.7s ease; +} +.ts-slide.is-active { opacity: 1; } + +/* ═══════════════════════════════════════════════════════════ + Slide 1 — Menu Board + ═══════════════════════════════════════════════════════════ */ +.ts-slide--menu { + background: linear-gradient(170deg, #1a1206 0%, #0d0a04 100%); + padding: 8%; +} +.ts-menu { width: 100%; height: 100%; display: flex; flex-direction: column; } +.ts-menu__header { + display: flex; + align-items: center; + gap: 6px; + margin-bottom: 6px; + padding-bottom: 4px; + border-bottom: 1px solid rgba(255,200,100,0.25); +} +.ts-menu__logo { + width: 10px; + height: 10px; + border-radius: 50%; + background: #e6a730; + flex-shrink: 0; +} +.ts-menu__title { + width: 50%; + height: 5px; + background: rgba(255,220,150,0.8); + border-radius: 1px; +} +.ts-menu__cols { + display: flex; + gap: 10px; + flex: 1; +} +.ts-menu__col { + flex: 1; + display: flex; + flex-direction: column; + gap: 4px; +} +.ts-menu__cat { + width: 55%; + height: 4px; + background: #e6a730; + border-radius: 1px; + margin-bottom: 1px; +} +.ts-menu__item { + display: flex; + align-items: center; + gap: 2px; +} +.ts-menu__name { + width: 40%; + height: 3px; + background: rgba(255,255,255,0.6); + border-radius: 1px; + flex-shrink: 0; +} +.ts-menu__dots { + flex: 1; + height: 0; + border-bottom: 1px dotted rgba(255,255,255,0.18); +} +.ts-menu__price { + width: 14%; + height: 3px; + background: rgba(255,220,150,0.7); + border-radius: 1px; + flex-shrink: 0; +} + +/* ═══════════════════════════════════════════════════════════ + Slide 2 — Wayfinding Sign + ═══════════════════════════════════════════════════════════ */ +.ts-slide--wayfind { + background: linear-gradient(170deg, #0c1a2e 0%, #081220 100%); + padding: 8%; +} +.ts-wf { width: 100%; height: 100%; display: flex; flex-direction: column; } +.ts-wf__header { + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 8px; + padding-bottom: 5px; + border-bottom: 1px solid rgba(74,222,128,0.2); +} +.ts-wf__building { + width: 60%; + height: 5px; + background: rgba(255,255,255,0.7); + border-radius: 1px; +} +.ts-wf__rows { + display: flex; + flex-direction: column; + gap: 5px; + flex: 1; + justify-content: center; +} +.ts-wf__row { + display: flex; + align-items: center; + gap: 5px; + padding: 3px 4px; + background: rgba(255,255,255,0.04); + border-radius: 2px; +} +.ts-wf__arrow { + width: 0; + height: 0; + flex-shrink: 0; +} +.ts-wf__arrow--left { + border-top: 3px solid transparent; + border-bottom: 3px solid transparent; + border-right: 5px solid #4ade80; +} +.ts-wf__arrow--right { + border-top: 3px solid transparent; + border-bottom: 3px solid transparent; + border-left: 5px solid #4ade80; +} +.ts-wf__arrow--up { + border-left: 3px solid transparent; + border-right: 3px solid transparent; + border-bottom: 5px solid #4ade80; +} +.ts-wf__label { + height: 3px; + background: rgba(255,255,255,0.55); + border-radius: 1px; + flex: 1; +} +.ts-wf__label--w1 { max-width: 60%; } +.ts-wf__label--w2 { max-width: 45%; } +.ts-wf__label--w3 { max-width: 70%; } +.ts-wf__label--w4 { max-width: 50%; } +.ts-wf__floor { + width: 14px; + height: 8px; + background: rgba(74,222,128,0.15); + border: 1px solid rgba(74,222,128,0.3); + border-radius: 2px; + flex-shrink: 0; + margin-left: auto; +} + +/* ═══════════════════════════════════════════════════════════ + Slide 3 — Schedule / Timetable + ═══════════════════════════════════════════════════════════ */ +.ts-slide--sched { + background: linear-gradient(170deg, #111827 0%, #0a0f1a 100%); + padding: 8%; +} +.ts-sched { width: 100%; height: 100%; display: flex; flex-direction: column; } +.ts-sched__header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 6px; + padding-bottom: 4px; + border-bottom: 1px solid rgba(74,222,128,0.2); +} +.ts-sched__title { + width: 35%; + height: 5px; + background: rgba(255,255,255,0.75); + border-radius: 1px; +} +.ts-sched__date { + width: 20%; + height: 4px; + background: rgba(255,255,255,0.3); + border-radius: 1px; +} +.ts-sched__table { + display: flex; + flex-direction: column; + gap: 3px; + flex: 1; +} +.ts-sched__hrow, +.ts-sched__row { + display: flex; + gap: 3px; +} +.ts-sched__hrow { + margin-bottom: 2px; +} +.ts-sched__hcell { + flex: 1; + height: 3px; + background: rgba(255,255,255,0.25); + border-radius: 1px; +} +.ts-sched__hcell:first-child { flex: 0.5; } +.ts-sched__row { + align-items: stretch; +} +.ts-sched__time { + flex: 0.5; + height: 10px; + display: flex; + align-items: center; +} +.ts-sched__time::before { + content: ''; + width: 80%; + height: 3px; + background: rgba(255,255,255,0.3); + border-radius: 1px; +} +.ts-sched__cell { + flex: 1; + height: 10px; + background: rgba(255,255,255,0.04); + border-radius: 2px; + border: 1px solid rgba(255,255,255,0.06); +} +.ts-sched__event { + flex: 1; + height: 10px; + border-radius: 2px; +} +.ts-sched__event--a { + background: rgba(74,222,128,0.25); + border: 1px solid rgba(74,222,128,0.4); +} +.ts-sched__event--b { + background: rgba(96,165,250,0.25); + border: 1px solid rgba(96,165,250,0.4); +} +.ts-sched__event--c { + background: rgba(251,191,36,0.25); + border: 1px solid rgba(251,191,36,0.4); +} /* HDMI port on back-right of TV */ .ts-tv__port { position: absolute; @@ -2515,7 +2901,7 @@ p:last-child { margin-bottom: 0; } .ts-tv__feet { display: flex; justify-content: space-between; - width: 180px; + width: 224px; } .ts-tv__foot { width: 12px; @@ -2529,15 +2915,15 @@ p:last-child { margin-bottom: 0; } position: absolute; right: -20px; top: 50%; - transform: translateY(-50%) translateX(80px); + transform: translateY(-50%) translateX(80px) scaleX(-1); display: flex; align-items: center; opacity: 0; z-index: 2; } .ts-stick__body { - width: 68px; - height: 26px; + width: 84px; + height: 32px; background: linear-gradient(180deg, #f5f5f5, #e0e0e0); border: 1px solid #ccc; border-radius: 5px; @@ -2575,8 +2961,8 @@ p:last-child { margin-bottom: 0; } } /* HDMI connector */ .ts-stick__connector { - width: 14px; - height: 10px; + width: 18px; + height: 14px; background: linear-gradient(180deg, #888, #666); border-radius: 0 2px 2px 0; margin-left: -1px; @@ -2588,8 +2974,8 @@ p:last-child { margin-bottom: 0; } position: absolute; left: 2px; top: 2px; - width: 6px; - height: 6px; + width: 8px; + height: 8px; background: #555; border-radius: 1px; } @@ -2598,30 +2984,32 @@ p:last-child { margin-bottom: 0; } 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); } + 0% { opacity: 0; transform: translateY(-50%) translateX(80px) scaleX(-1); } + 20% { opacity: 1; transform: translateY(-50%) translateX(60px) scaleX(-1); } + 70% { transform: translateY(-50%) translateX(8px) scaleX(-1); } + 85% { transform: translateY(-50%) translateX(12px) scaleX(-1); } + 100% { opacity: 1; transform: translateY(-50%) translateX(6px) scaleX(-1); } } /* 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; } + .ts-tv__body { width: 320px; height: 188px; } + .ts-tv__feet { width: 178px; } + .ts-stick__body { width: 68px; height: 26px; } + .ts-stage { padding-right: 72px; } } @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-tv__body { width: 240px; height: 142px; } + .ts-tv__feet { width: 134px; } + .ts-stick__body { width: 52px; height: 20px; } + .ts-stick__connector { width: 12px; height: 10px; } + .ts-stick__body::before { top: 4px; left: 6px; width: 22px; height: 10px; } .ts-stick__led { width: 3px; height: 3px; right: 5px; } + .ts-stage { padding-right: 56px; } } @media (prefers-reduced-motion: reduce) { .ts-stage .ts-stick { opacity: 1; - transform: translateY(-50%) translateX(6px); + transform: translateY(-50%) translateX(6px) scaleX(-1); } .ts-stage.is-animating .ts-stick { animation: none; } .ts-stage.is-plugged .ts-tv__screen { animation: none; } @@ -2658,15 +3046,20 @@ p:last-child { margin-bottom: 0; } 100% { transform: translateY(var(--p-ty, -100vh)) translateX(var(--p-tx, 40px)) scale(var(--p-scale-end, 0.3)); opacity: 0; } } +@keyframes glow-appear { + from { opacity: 0; filter: blur(80px); transform: translate(-50%, -50%) scale(0.2); } + to { opacity: 0.65; filter: blur(22px); transform: translate(-50%, -50%) scale(0.85); } +} + @keyframes glow-drift { - 0% { transform: translate(-50%, -50%) scale(1); opacity: 0.35; } - 15% { transform: translate(-20%, -30%) scale(1.15); opacity: 0.45; } - 30% { transform: translate(-65%, -70%) scale(0.9); opacity: 0.3; } - 45% { transform: translate(-80%, -40%) scale(1.1); opacity: 0.5; } - 60% { transform: translate(-30%, -75%) scale(0.95); opacity: 0.35; } - 75% { transform: translate(-70%, -25%) scale(1.2); opacity: 0.45; } - 90% { transform: translate(-40%, -60%) scale(1.05); opacity: 0.4; } - 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.35; } + 0% { transform: translate(-50%, -50%) scale(0.85); opacity: 0.6; } + 15% { transform: translate(-20%, -30%) scale(1.35); opacity: 0.75; } + 30% { transform: translate(-65%, -70%) scale(0.65); opacity: 0.5; } + 45% { transform: translate(-80%, -40%) scale(1.45); opacity: 0.8; } + 60% { transform: translate(-30%, -75%) scale(0.7); opacity: 0.55; } + 75% { transform: translate(-70%, -25%) scale(1.5); opacity: 0.75; } + 90% { transform: translate(-40%, -60%) scale(0.8); opacity: 0.65; } + 100% { transform: translate(-50%, -50%) scale(0.85); opacity: 0.6; } } /* Particle container */ @@ -2706,14 +3099,16 @@ p:last-child { margin-bottom: 0; } position: absolute; top: 50%; left: 50%; - width: 700px; - height: 700px; + width: 520px; + height: 520px; border-radius: 50%; - background: radial-gradient(circle, rgba(var(--color-primary-rgb), 0.18) 0%, rgba(var(--color-primary-rgb), 0.06) 50%, transparent 70%); + background: radial-gradient(circle, rgba(var(--color-primary-rgb), 0.32) 0%, rgba(var(--color-primary-rgb), 0.14) 45%, rgba(var(--color-primary-rgb), 0.04) 70%, transparent 90%); z-index: 1; pointer-events: none; - filter: blur(40px); - animation: glow-drift 20s ease-in-out infinite; + filter: blur(22px); + animation: + glow-appear 0.45s cubic-bezier(0.16, 1, 0.3, 1) forwards, + glow-drift 20s 0.45s ease-in-out infinite; } /* Animated hero layout - centered single-column content */ @@ -2745,10 +3140,32 @@ p:last-child { margin-bottom: 0; } justify-content: center; } +/* Keep secondary CTA visible on light homepage hero */ +.hero-animated .btn-ghost { + border-color: rgba(var(--color-primary-rgb), .35); + color: var(--color-text); + background: rgba(255,255,255,.55); +} + +.hero-animated .btn-ghost:hover { + border-color: rgba(var(--color-primary-rgb), .6); + background: rgba(var(--color-primary-rgb), .08); + color: var(--color-text); +} + .hero-animated__content .hero-stats { justify-content: center; } +/* Homepage flow: avoid oversized gap before use-cases section */ +.hero-animated { + min-height: auto; +} + +.hero-animated + .use-cases-section { + padding-top: clamp(2rem, 4vw, 3.5rem); +} + .hero-stats--three { display: flex; gap: 3rem; @@ -2769,12 +3186,14 @@ p:last-child { margin-bottom: 0; } .hero-particle, .hero-animated__glow { animation: none; - opacity: var(--p-opacity, 0.2); + opacity: 0.6; + transform: translate(-50%, -50%) scale(0.85); + filter: blur(22px); } } @media (max-width: 768px) { - .hero-animated__glow { width: 400px; height: 400px; } + .hero-animated__glow { width: 340px; height: 340px; } .hero-animated__inner { padding-block: 7rem 4rem; } .hero-stats--three { gap: 1.5rem; flex-wrap: wrap; justify-content: center; } } @@ -2938,6 +3357,44 @@ p:last-child { margin-bottom: 0; } .cta-banner h2 { color: #fff; margin-bottom: 1rem; } .cta-banner p { color: rgba(255,255,255,.85); margin-bottom: 2rem; font-size: 1.1rem; } +.devices-split-card { + display: grid; + grid-template-columns: 1fr 1fr; + border-radius: var(--radius-xl); + overflow: hidden; + border: 1px solid var(--color-border); + background: var(--card-bg); +} + +.devices-split-card__panel { + padding: clamp(1.5rem, 3vw, 2.5rem); +} + +.devices-split-card__panel h3 { + margin-bottom: .85rem; +} + +.devices-split-card__panel p { + color: var(--color-text-muted); + line-height: 1.75; +} + +.devices-split-card__panel--brand { + background: var(--color-primary); + color: var(--color-bg); +} + +.devices-split-card__panel--brand h3, +.devices-split-card__panel--brand p { + color: inherit; +} + +@media (max-width: 768px) { + .devices-split-card { + grid-template-columns: 1fr; + } +} + /* ── 15. Footer ────────────────────────────────────────────── */ .site-footer { background: #111111; @@ -3108,14 +3565,18 @@ p:last-child { margin-bottom: 0; } .comparison-feature-col { min-width: 220px; } .comparison-feature-name { color: var(--color-text); } .comparison-group-row td { - background: var(--color-bg-alt); + background: var(--color-primary); font-weight: 700; - font-size: .85rem; + font-size: .82rem; text-transform: uppercase; - letter-spacing: .05em; - color: var(--color-primary); + letter-spacing: .08em; + color: #fff; text-align: left !important; - padding: .75rem 1.25rem; + padding: .85rem 1.25rem; + border-bottom-color: var(--color-primary) !important; +} +.comparison-group-row td:first-child { + border-left: 4px solid var(--color-accent); } .comparison-yes { color: var(--color-accent); @@ -3247,7 +3708,7 @@ p:last-child { margin-bottom: 0; } [data-theme="dark"] .hero-animated { background: #0D0D0D; } [data-theme="dark"] .page-hero-animated { background: #0D0D0D; } [data-theme="dark"] .hero-animated__glow { - background: radial-gradient(circle, rgba(var(--color-primary-rgb), 0.22) 0%, rgba(var(--color-primary-rgb), 0.08) 50%, transparent 70%); + background: radial-gradient(circle, rgba(var(--color-primary-rgb), 0.40) 0%, rgba(var(--color-primary-rgb), 0.18) 45%, rgba(var(--color-primary-rgb), 0.06) 70%, transparent 90%); } [data-theme="dark"] .site-footer { background: #0D0D0D; } @@ -3255,7 +3716,7 @@ p:last-child { margin-bottom: 0; } [data-theme="dark"] .site-nav.open { background: #111111; } [data-theme="dark"] .logo-text { color: #F5F5F5; } -[data-theme="dark"] .logo-text strong { color: var(--color-accent); } +[data-theme="dark"] .logo-text strong { color: var(--color-primary); } [data-theme="dark"] .nav-menu a { color: rgba(255,255,255,.8); } [data-theme="dark"] .nav-menu a:hover, @@ -3271,6 +3732,16 @@ p:last-child { margin-bottom: 0; } border-color: rgba(255,255,255,.25); } +[data-theme="dark"] .hero-animated .btn-ghost { + color: #fff; + background: transparent; +} + +[data-theme="dark"] .hero-animated .btn-ghost:hover { + background: rgba(255,255,255,.1); + color: #fff; +} + [data-theme="dark"] .platform-visual:not(.has-img):not(.has-dashboard) { background: var(--color-bg-alt); border-color: var(--color-border); @@ -3354,6 +3825,9 @@ p:last-child { margin-bottom: 0; } Photo camera (left) + product scene (centre) + VHS camcorder on tripod (right) ══════════════════════════════════════════════════════════════════════════════ */ .platform-visual.has-camera { + --cam-cycle: 9s; + --cam-rec-cycle: 1.4s; + --cam-lens-cycle: 4s; background: none !important; border: none !important; border-radius: 0; @@ -3372,7 +3846,7 @@ p:last-child { margin-bottom: 0; } flex-direction: row; align-items: center; justify-content: center; - gap: 12px; + gap: 6px; padding-bottom: 4px; } @@ -3392,7 +3866,7 @@ p:last-child { margin-bottom: 0; } background-size: 18px 18px; } -/* ── Shared subject shapes ── */ +/* ── Rotating product items: shoe, food, laptop ── */ .cam-subject { position: absolute; inset: 0; @@ -3401,29 +3875,77 @@ p:last-child { margin-bottom: 0; } justify-content: center; opacity: 0; } -.cam-subject::before { +.cam-subject::before, +.cam-subject::after { content: ''; - display: block; - border-radius: 8px; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); } -.cam-subject--1 { animation: cam-subject-1 9s ease-in-out infinite; } -.cam-subject--1::before { - width: 40px; height: 58px; - background: linear-gradient(135deg, #064e3b 0%, #4ade80 100%); - box-shadow: 0 0 18px rgba(74,222,128,.4); + +.cam-subject--shoe { animation: cam-subject-1 var(--cam-cycle) ease-in-out infinite; } +.cam-subject--shoe::before { + width: 76px; + height: 28px; + border-radius: 16px 20px 10px 14px; + background: linear-gradient(145deg, #312e81 0%, #4f46e5 45%, #818cf8 100%); + box-shadow: + 0 0 14px rgba(129,140,248,.25), + inset 0 -2px 0 rgba(0,0,0,.22), + inset 0 2px 0 rgba(255,255,255,.18); } -.cam-subject--2 { animation: cam-subject-2 9s ease-in-out infinite; } -.cam-subject--2::before { - width: 32px; height: 68px; - background: linear-gradient(160deg, #78350f 0%, #fbbf24 100%); - border-radius: 6px 6px 12px 12px; - box-shadow: 0 0 18px rgba(251,191,36,.35); +.cam-subject--shoe::after { + width: 80px; + height: 8px; + border-radius: 999px; + background: #e5e7eb; + transform: translate(-50%, calc(-50% + 13px)); + box-shadow: inset 0 -1px 0 rgba(0,0,0,.2); } -.cam-subject--3 { animation: cam-subject-3 9s ease-in-out infinite; } -.cam-subject--3::before { - width: 74px; height: 50px; - background: linear-gradient(135deg, #1e1b4b 0%, #818cf8 100%); - box-shadow: 0 0 18px rgba(129,140,248,.35); + +.cam-subject--food { animation: cam-subject-2 var(--cam-cycle) ease-in-out infinite; } +.cam-subject--food::before { + width: 68px; + height: 46px; + border-radius: 22px 22px 14px 14px; + background: + linear-gradient(to bottom, + #fbbf24 0 34%, + #65a30d 34% 43%, + #7c2d12 43% 60%, + #f59e0b 60% 100%); + box-shadow: + 0 0 14px rgba(251,191,36,.28), + inset 0 1px 0 rgba(255,255,255,.35); +} +.cam-subject--food::after { + width: 16px; + height: 16px; + border-radius: 50%; + background: rgba(255,255,255,.26); + transform: translate(calc(-50% - 14px), calc(-50% - 10px)); +} + +.cam-subject--laptop { animation: cam-subject-3 var(--cam-cycle) ease-in-out infinite; } +.cam-subject--laptop::before { + width: 74px; + height: 44px; + border-radius: 6px; + background: + linear-gradient(165deg, #0f172a 0%, #1e293b 100%); + border: 2px solid #94a3b8; + box-shadow: + 0 0 16px rgba(148,163,184,.25), + inset 0 0 0 2px rgba(15,23,42,.7); +} +.cam-subject--laptop::after { + width: 90px; + height: 10px; + border-radius: 4px; + background: linear-gradient(180deg, #d1d5db, #9ca3af); + transform: translate(-50%, calc(-50% + 28px)); + box-shadow: inset 0 1px 0 rgba(255,255,255,.5); } /* Flash overlay */ @@ -3433,7 +3955,7 @@ p:last-child { margin-bottom: 0; } background: #fff; opacity: 0; pointer-events: none; - animation: cam-flash-pulse 9s linear infinite; + animation: cam-flash-pulse var(--cam-cycle) linear infinite; } /* CRT scanlines from the video cam */ .cam-vid-overlay { @@ -3482,7 +4004,7 @@ p:last-child { margin-bottom: 0; } top: 2px; left: 4px; right: 4px; bottom: 2px; background: #c8c870; border-radius: 1px; - animation: pc-flash-glow 9s linear infinite; + animation: pc-flash-glow var(--cam-cycle) linear infinite; } /* Top strip */ .pc-top { @@ -3506,7 +4028,7 @@ p:last-child { margin-bottom: 0; } border-radius: 50%; border: 2px solid var(--color-border); box-shadow: 0 0 0 1px rgba(255,255,255,.15), inset 0 1px 0 rgba(255,255,255,.1); - animation: pc-shutter 9s linear infinite; + animation: pc-shutter var(--cam-cycle) linear infinite; transform-origin: center; } .pc-viewfinder { @@ -3594,9 +4116,9 @@ p:last-child { margin-bottom: 0; } height: 38px; border-radius: 1px; } -.pc-print--1 { left: 0; top: 0; transform-origin: bottom center; animation: pc-print-1 9s linear infinite; } -.pc-print--2 { left: 18px; top: 0; transform-origin: bottom center; animation: pc-print-2 9s linear infinite; } -.pc-print--3 { left: 36px; top: 0; transform-origin: bottom center; animation: pc-print-3 9s linear infinite; } +.pc-print--1 { left: 0; top: 0; transform-origin: bottom center; animation: pc-print-1 var(--cam-cycle) linear infinite; } +.pc-print--2 { left: 18px; top: 0; transform-origin: bottom center; animation: pc-print-2 var(--cam-cycle) linear infinite; } +.pc-print--3 { left: 36px; top: 0; transform-origin: bottom center; animation: pc-print-3 var(--cam-cycle) linear infinite; } .pc-print--1 .pc-print__img { background: linear-gradient(135deg, #064e3b, #4ade80); } .pc-print--2 .pc-print__img { background: linear-gradient(160deg, #78350f, #fbbf24); } .pc-print--3 .pc-print__img { background: linear-gradient(135deg, #1e1b4b, #818cf8); } @@ -3645,18 +4167,18 @@ p:last-child { margin-bottom: 0; } background: var(--color-bg-alt); border: 1px solid var(--color-border); border-top: 1px solid rgba(255,255,255,.12); - border-radius: 0 4px 4px 0; + border-radius: 4px 0 0 4px; position: relative; display: flex; align-items: center; overflow: visible; box-shadow: inset 0 -1px 0 rgba(0,0,0,.3); } -/* Lens barrel — extends LEFT toward the scene */ +/* Lens barrel — extends RIGHT */ .vc-lens-barrel { position: absolute; - left: -34px; - top: 50%; + right: -34px; + top: calc(50% - 1px); transform: translateY(-50%); width: 38px; height: 38px; border-radius: 50%; @@ -3666,7 +4188,7 @@ p:last-child { margin-bottom: 0; } display: flex; align-items: center; justify-content: center; - animation: vc-lens-zoom 4s ease-in-out infinite; + animation: vc-lens-zoom var(--cam-lens-cycle) ease-in-out infinite; } .vc-lens-tip { width: 26px; height: 26px; @@ -3705,24 +4227,25 @@ p:last-child { margin-bottom: 0; } /* REC indicator */ .vc-rec-light { position: absolute; - top: 8px; left: 16px; + top: 8px; + right: 16px; width: 7px; height: 7px; border-radius: 50%; background: #ef4444; box-shadow: 0 0 8px #ef4444, 0 0 16px rgba(239,68,68,.4); - animation: vc-rec-blink 1.4s ease-in-out infinite; + animation: vc-rec-blink var(--cam-rec-cycle) ease-in-out infinite; } -/* Eyepiece — extends RIGHT */ +/* Eyepiece — extends LEFT */ .vc-eyepiece { position: absolute; - right: -14px; + left: -14px; top: 50%; transform: translateY(-50%); width: 14px; height: 18px; background: var(--color-bg-alt); border: 1px solid var(--color-border); - border-left: none; - border-radius: 0 4px 4px 0; + border-right: none; + border-radius: 4px 0 0 4px; display: flex; align-items: center; justify-content: center; @@ -3738,7 +4261,7 @@ p:last-child { margin-bottom: 0; } .vc-body::after { content: ''; position: absolute; - right: 6px; top: 7px; + left: 6px; top: 7px; width: 28px; height: 34px; background: rgba(0,0,0,.2); border: 1px solid var(--color-border); @@ -3860,12 +4383,20 @@ p:last-child { margin-bottom: 0; } 50% { transform: translateY(-50%) scale(1.08); } } +/* ── Responsive scale-down ── */ +@media (max-width: 900px) { + .cam-stage { transform: scale(0.85); transform-origin: center center; } +} +@media (max-width: 640px) { + .cam-stage { transform: scale(0.72); transform-origin: center center; } +} + /* ── Reduced-motion ── */ @media (prefers-reduced-motion: reduce) { - .cam-subject--2, .cam-subject--3, + .cam-subject--food, .cam-subject--laptop, .cam-flash-overlay, .pc-print--2, .pc-print--3 { animation: none !important; opacity: 0 !important; } - .cam-subject--1 { animation: none !important; opacity: 1 !important; } + .cam-subject--shoe { animation: none !important; opacity: 1 !important; } .pc-shutter-btn, .pc-flash-unit::after, .vc-lens-barrel, .vc-rec-light { animation: none !important; } .pc-print--1 { @@ -3874,3 +4405,1444 @@ p:last-child { margin-bottom: 0; } transform: rotate(-12deg) translateY(0) !important; } } + +/* ═══════════════════════════════════════════════════════════════════════ + USE CASES SHOWCASE (uc-*) + Four animated circles in a rounded-border track beneath the hero. + ═══════════════════════════════════════════════════════════════════════ */ + +.uc-track { + display: flex; + justify-content: space-between; + align-items: flex-start; + gap: 1.5rem; + border: 1px solid var(--color-border); + border-radius: var(--radius-xl, 1.25rem); + padding: 2.5rem 3rem; + background: var(--color-bg-alt); +} + +.uc-item { + display: flex; + flex-direction: column; + align-items: center; + flex: 1; +} + +.uc-circle { + width: 158px; + height: 158px; + border-radius: 50%; + border: 1.5px solid var(--color-border); + overflow: hidden; + position: relative; + background: var(--color-bg); + flex-shrink: 0; +} + +.uc-item-body { + margin-top: 1.1rem; + text-align: center; +} +.uc-item-title { + font-size: .875rem; + font-weight: 600; + color: var(--color-heading); +} +.uc-item-desc { + font-size: .75rem; + color: var(--color-muted); + margin-top: .3rem; + line-height: 1.45; + max-width: 160px; +} + +/* ── Shared inner wrapper ─────────────────────────────────────────────── */ +.uc-inner { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 18px; + box-sizing: border-box; + gap: 6px; +} + +/* ══════════════════════════════════ + 1 — MENU BOARD + ══════════════════════════════════ */ +.uc-inner--menu { gap: 0; padding: 14px 18px; } + +.uc-menu-header { + width: 100%; + height: 16px; + border-radius: 3px; + background: var(--color-primary); + margin-bottom: 10px; + flex-shrink: 0; +} +.uc-menu-row { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + padding: 5px 4px; + border-radius: 3px; + gap: 6px; + flex-shrink: 0; +} +.uc-menu-row + .uc-menu-row { margin-top: 4px; } + +.uc-menu-divider { + width: 100%; + height: 1px; + background: var(--color-border); + margin: 6px 0; + flex-shrink: 0; +} +.uc-menu-name { + height: 7px; + flex: 1; + border-radius: 3px; + background: var(--color-border); +} +.uc-menu-name--sm { max-width: 55%; } +.uc-menu-price { + height: 7px; + width: 22px; + border-radius: 3px; + background: var(--color-border); + flex-shrink: 0; +} +.uc-menu-row--highlight { + background: color-mix(in srgb, var(--color-primary) 15%, transparent); + animation: uc-menu-highlight 3s ease-in-out infinite; +} +.uc-menu-row--highlight .uc-menu-name, +.uc-menu-row--highlight .uc-menu-price { + background: var(--color-primary); + animation: uc-menu-price-update 3s ease-in-out infinite; +} + +@keyframes uc-menu-highlight { + 0%, 100% { background: color-mix(in srgb, var(--color-primary) 15%, transparent); } + 45%, 55% { background: color-mix(in srgb, var(--color-accent) 25%, transparent); } +} +@keyframes uc-menu-price-update { + 0%, 38% { opacity: 1; } + 42% { opacity: 0; } + 48% { opacity: 1; } + 52% { opacity: 0; } + 58%, 100%{ opacity: 1; } +} + +/* ══════════════════════════════════ + 2 — EVENT DISPLAY + ══════════════════════════════════ */ +.uc-inner--event { + gap: 0; + padding: 16px 18px; + position: relative; +} + +.uc-event-header { + width: 100%; + height: 13px; + border-radius: 3px; + background: var(--color-border); + margin-bottom: 10px; + flex-shrink: 0; + display: flex; + align-items: center; + padding: 0 6px; + font-size: 9px; + font-weight: 700; + color: var(--color-text); + letter-spacing: 0.5px; +} +.uc-event-row { + width: 100%; + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; +} +.uc-event-row + .uc-event-row { margin-top: 7px; } +.uc-event-time { + height: 10px; + width: 28px; + border-radius: 2px; + background: var(--color-border); + flex-shrink: 0; + font-size: 7px; + display: flex; + align-items: center; + justify-content: center; + color: var(--color-muted); + font-weight: 600; +} +.uc-event-title { + height: 14px; + flex: 1; + border-radius: 3px; + background: var(--color-border); + position: relative; + overflow: hidden; + font-size: 8px; + display: flex; + align-items: center; + padding: 0 6px; + color: var(--color-muted); + font-weight: 500; + white-space: nowrap; + text-overflow: ellipsis; +} +.uc-event-title::after { + content: ''; + position: absolute; + inset: 0; + background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,.2) 50%, transparent 100%); + transform: translateX(-150%); + pointer-events: none; +} +.uc-event-title--active { + background: var(--color-primary); + color: #fff; +} +.uc-event-title--accent { + background: color-mix(in srgb, var(--color-accent) 55%, var(--color-border)); + color: var(--color-text); +} +.uc-event-title--active::after { animation: uc-event-shimmer 2.2s linear infinite; } +.uc-event-title--accent::after { animation: uc-event-shimmer 2.2s linear infinite 1.1s; } + +.uc-event-cursor { + position: absolute; + top: 32px; + left: 0; + right: 0; + height: 2px; + background: var(--color-primary); + opacity: .85; + pointer-events: none; + box-shadow: 0 0 8px rgba(var(--color-primary-rgb), 0.4); + animation: uc-event-time-progress 20s linear infinite; +} +.uc-event-cursor::before { + content: ''; + position: absolute; + left: -6px; + top: -4px; + width: 12px; + height: 10px; + background: var(--color-primary); + border-radius: 50%; + box-shadow: 0 0 8px rgba(var(--color-primary-rgb), 0.6); +} + +@keyframes uc-event-shimmer { + 0% { transform: translateX(-150%); } + 100% { transform: translateX(250%); } +} +@keyframes uc-event-time-progress { + 0% { top: 32px; } + 100% { top: calc(100% - 10px); } +} + +/* ══════════════════════════════════ + 3 — OFFICE DASHBOARD + ══════════════════════════════════ */ +.uc-inner--dashboard { + justify-content: flex-end; + padding: 18px 16px 12px; +} +.uc-db-bars { + display: flex; + align-items: stretch; + gap: 9px; + width: 100%; + height: 80px; +} +.uc-db-bar { + flex: 1; + border-radius: 4px 4px 0 0; + transform-origin: bottom; + transform: scaleY(.55); +} +.uc-db-bar--1, .uc-db-bar--3 { background: var(--color-primary); } +.uc-db-bar--2, .uc-db-bar--4 { background: color-mix(in srgb, var(--color-primary) 45%, var(--color-border)); } + +.uc-db-bar--1 { animation: uc-db-scale-1 3s ease-in-out infinite; } +.uc-db-bar--2 { animation: uc-db-scale-2 3s ease-in-out infinite .4s; } +.uc-db-bar--3 { animation: uc-db-scale-3 3s ease-in-out infinite .8s; } +.uc-db-bar--4 { animation: uc-db-scale-4 3s ease-in-out infinite 1.2s; } + +.uc-db-baseline { + width: 100%; + height: 1px; + background: var(--color-border); + flex-shrink: 0; + margin-bottom: 6px; +} +.uc-db-labels { + display: flex; + gap: 9px; + width: 100%; + flex-shrink: 0; +} +.uc-db-lbl { + flex: 1; + height: 5px; + border-radius: 2px; + background: var(--color-border); + opacity: .6; +} + +@keyframes uc-db-scale-1 { + 0%, 100% { transform: scaleY(.55); } + 40% { transform: scaleY(.82); } + 70% { transform: scaleY(.42); } +} +@keyframes uc-db-scale-2 { + 0%, 100% { transform: scaleY(.70); } + 40% { transform: scaleY(.38); } + 70% { transform: scaleY(.78); } +} +@keyframes uc-db-scale-3 { + 0%, 100% { transform: scaleY(.40); } + 40% { transform: scaleY(.68); } + 70% { transform: scaleY(.58); } +} +@keyframes uc-db-scale-4 { + 0%, 100% { transform: scaleY(.62); } + 40% { transform: scaleY(.50); } + 70% { transform: scaleY(.88); } +} + +/* ══════════════════════════════════ + 4 — WAYFINDING + ══════════════════════════════════ */ +.uc-inner--wayfinding { padding: 0; } + +/* Corridor lines */ +.uc-wf-corridor { + position: absolute; + background: var(--color-border); +} +.uc-wf-corridor--h { + height: 8px; + width: 70%; + top: 50%; + left: 15%; + transform: translateY(-50%); + border-radius: 2px; +} +.uc-wf-corridor--v { + width: 8px; + height: 54%; + left: 50%; + top: 23%; + transform: translateX(-50%); + border-radius: 2px; +} + +/* Rooms */ +.uc-wf-room { + position: absolute; + border: 1.5px solid var(--color-border); + border-radius: 3px; + background: var(--color-bg-alt); +} +.uc-wf-room--1 { width: 32px; height: 28px; top: 17%; left: 17%; } +.uc-wf-room--2 { width: 28px; height: 26px; top: 17%; right: 17%; } +.uc-wf-room--3 { width: 30px; height: 28px; bottom: 17%; left: 50%; transform: translateX(-50%); } + +/* Location dot */ +.uc-wf-dot { + position: absolute; + width: 12px; + height: 12px; + border-radius: 50%; + background: var(--color-primary); + top: 50%; + left: 36%; + transform: translate(-50%, -50%); +} +.uc-wf-dot::after { + content: ''; + position: absolute; + inset: -5px; + border-radius: 50%; + border: 1.5px solid var(--color-primary); + animation: uc-wf-pulse 2s ease-out infinite; +} + +/* Direction arrow */ +.uc-wf-arrow { + position: absolute; + top: 50%; + left: 36%; + transform: translate(-50%, calc(-50% - 18px)); + width: 0; + height: 0; + border-left: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 11px solid var(--color-accent); + animation: uc-wf-arrow-bob 1.8s ease-in-out infinite; +} + +@keyframes uc-wf-pulse { + 0% { transform: scale(1); opacity: .7; } + 70% { transform: scale(2.4); opacity: 0; } + 100% { transform: scale(1); opacity: 0; } +} +@keyframes uc-wf-arrow-bob { + 0%, 100% { transform: translate(-50%, calc(-50% - 18px)); } + 50% { transform: translate(-50%, calc(-50% - 24px)); } +} + +/* ── Responsive ──────────────────────────────────────────── */ +@media (max-width: 900px) { + .uc-track { flex-wrap: wrap; justify-content: center; gap: 2rem; padding: 2.5rem 2rem; } + .uc-item { width: calc(50% - 1rem); flex: none; } +} +@media (max-width: 500px) { + .uc-track { padding: 2rem 1.5rem; } + .uc-item { width: 100%; } + .uc-item-desc { max-width: none; } +} + +/* ── Reduced-motion overrides ────────────────────────────── */ +@media (prefers-reduced-motion: reduce) { + .uc-menu-row--highlight, + .uc-menu-row--highlight .uc-menu-name, + .uc-menu-row--highlight .uc-menu-price, + .uc-event-title--active::after, + .uc-event-title--accent::after, + .uc-event-cursor, + .uc-db-bar--1, .uc-db-bar--2, .uc-db-bar--3, .uc-db-bar--4, + .uc-wf-dot::after, + .uc-wf-arrow { animation: none !important; } + /* Static bar heights when animation is off */ + .uc-db-bar--1 { transform: scaleY(.55) !important; } + .uc-db-bar--2 { transform: scaleY(.70) !important; } + .uc-db-bar--3 { transform: scaleY(.40) !important; } + .uc-db-bar--4 { transform: scaleY(.62) !important; } +} + +/* ═══════════════════════════════════════════════════════════ + NEVER GOES DARK ANIMATION (.platform-visual.has-ngd) + Sequence (10 s loop): + 0 – 30 % : Connected – dots travel player → cloud, cloud green + 30 – 45 % : Breaking – line turns red, × icon fades in + 45 – 72 % : Offline – × visible, ✓ on player, TV still plays + 72 – 85 % : Reconnect – × fades, dots resume, cloud back to green + 85 – 100% : Connected – steady state before next loop + ═══════════════════════════════════════════════════════════ */ + +/* ── Wrapper overrides ─────────────────────────────────────── */ +.platform-visual.has-ngd { + background: none !important; + border: none !important; + border-radius: 0; + aspect-ratio: unset; + padding: 0; + overflow: visible; + position: relative; + font-size: inherit; +} + +/* ── Stage ─────────────────────────────────────────────────── */ +.ngd-stage { + position: relative; + width: 100%; + max-width: 560px; + aspect-ratio: 1/1; + margin: 0 auto; +} + +/* ── TV ─────────────────────────────────────────────────────── */ +.ngd-tv { + position: absolute; + left: 20px; + top: 72px; + display: flex; + flex-direction: column; + align-items: center; +} +.ngd-tv__body { + width: 400px; + height: 235px; + background: #111; + border: 4px solid #111; + border-radius: 6px 6px 4px 4px; + outline: 1px solid #000; + padding: 3px; + display: flex; + align-items: stretch; + position: relative; + box-shadow: 0 14px 48px rgba(0,0,0,.55); +} +/* HDMI port stub on right side */ +.ngd-tv__port { + position: absolute; + right: -6px; + top: 110px; + width: 6px; + height: 14px; + background: #1a1a1a; + border: 1px solid #000; + border-left: none; + border-radius: 0 2px 2px 0; +} +.ngd-tv__port::before { + content: ''; + position: absolute; + left: 0; + top: 2px; + width: 3px; + height: 8px; + background: #333; + border-radius: 0 1px 1px 0; +} +.ngd-tv__screen { + --ngd-menu-scale-x: 1.8182; + --ngd-menu-scale-y: 1.4506; + width: 100%; + height: 100%; + border-radius: 2px; + position: relative; + overflow: hidden; + background: + repeating-linear-gradient( + 180deg, + transparent, transparent 3px, + rgba(0,0,0,.10) 3px, rgba(0,0,0,.10) 4px + ), + linear-gradient(135deg, #0c1016 0%, #151c28 60%, #0c1016 100%); +} +/* Scanline sweep */ +.ngd-tv__screen::after { + content: ''; + position: absolute; + left: 0; width: 100%; height: 3px; + background: linear-gradient(90deg, transparent, rgba(74,222,128,.22), transparent); + animation: da-scan 4s linear infinite; + pointer-events: none; +} +/* TV feet */ +.ngd-tv__feet { + display: flex; + justify-content: space-between; + width: 224px; +} +.ngd-tv__foot { + width: 12px; + height: 8px; + background: #111; + border: 1px solid #000; + border-radius: 0 0 4px 4px; +} + +/* ── Menu board content (always playing) ───────────────────── */ +.ngd-menu { + position: absolute; + left: 0; + top: 0; + width: 220px; + height: 162px; + transform: scale(var(--ngd-menu-scale-x), var(--ngd-menu-scale-y)); + transform-origin: top left; + display: flex; + flex-direction: column; + padding: 7px 6px 4px; + gap: 3px; +} +.ngd-menu__hd { + display: flex; + align-items: center; + gap: 5px; + padding-bottom: 4px; + border-bottom: 1px solid rgba(255,200,80,.18); + flex-shrink: 0; +} +.ngd-menu__logo { + width: 9px; height: 9px; + background: #4CAF50; + border-radius: 50%; + flex-shrink: 0; +} +.ngd-menu__ttl { + flex: 1; height: 5px; + background: rgba(255,200,80,.45); + border-radius: 2px; + max-width: 55px; +} +.ngd-menu__cols { + display: flex; + gap: 6px; + flex: 1; +} +.ngd-menu__col { + flex: 1; + display: flex; + flex-direction: column; + gap: 4px; +} +.ngd-menu__cat { + height: 4px; + background: rgba(76,175,80,.5); + border-radius: 1px; + margin-bottom: 1px; +} +.ngd-menu__row { + display: flex; + align-items: center; + gap: 3px; + padding: 2px 3px; + border-radius: 2px; + transition: background .3s; +} +.ngd-menu__row--hl { + background: rgba(76,175,80,.14); + animation: ngd-hl-pulse 1.75s ease-in-out infinite; +} +.ngd-menu__name { + flex: 1; height: 4px; + background: rgba(255,255,255,.25); + border-radius: 1px; +} +.ngd-menu__row--hl .ngd-menu__name { + background: rgba(76,175,80,.55); +} +.ngd-menu__price { + width: 16px; height: 4px; + background: rgba(255,200,80,.4); + border-radius: 1px; +} +.ngd-menu__row--hl .ngd-menu__price { + background: rgba(255,200,80,.7); +} +/* Scrolling ticker at bottom */ +.ngd-menu__ticker { + height: 6px; + background: rgba(76,175,80,.12); + border-radius: 1px; + overflow: hidden; + flex-shrink: 0; +} +.ngd-menu__ticker-inner { + width: 250%; + height: 100%; + background: repeating-linear-gradient( + 90deg, + rgba(76,175,80,.5) 0px, rgba(76,175,80,.5) 30px, + transparent 30px, transparent 50px + ); + animation: ngd-ticker 2s linear infinite; +} + +/* ── Player device (stick plugged into TV right-side HDMI port) ── */ +.ngd-player { + position: absolute; + /* TV: left(20) + border(4) + width(400) + border(4) = 428px */ + left: 428px; + /* TV top(72) + border(4) + port.top(110) + port half-h(7) = 193px + stick h=32px → top = 193 − 16 = 177px */ + top: 177px; + display: flex; + flex-direction: row; + align-items: center; +} +/* HDMI connector plug — left side, inserts into TV port */ +.ngd-player__connector { + width: 18px; + height: 14px; + background: linear-gradient(180deg, #888, #666); + border-radius: 2px 0 0 2px; + position: relative; + box-shadow: 0 1px 3px rgba(0,0,0,.3); + flex-shrink: 0; + z-index: 1; +} +.ngd-player__connector::before { + content: ''; + position: absolute; + right: 2px; + top: 2px; + width: 8px; + height: 8px; + background: #555; + border-radius: 1px; +} +/* Main stick body — white gradient capsule */ +.ngd-player__body { + width: 84px; + height: 32px; + background: linear-gradient(180deg, #f5f5f5, #e0e0e0); + border: 1px solid #ccc; + border-radius: 0 5px 5px 0; + position: relative; + box-shadow: 0 2px 8px rgba(0,0,0,.25), inset 0 1px 0 rgba(255,255,255,.6); +} +/* Brand logo area subtle inset */ +.ngd-player__body::before { + content: ''; + position: absolute; + top: 6px; + left: 8px; + width: 28px; + height: 12px; + background: rgba(0,0,0,.04); + border-radius: 2px; +} +/* LED indicator — right side of body */ +.ngd-player__led { + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + width: 4px; + height: 4px; + border-radius: 50%; + background: #4CAF50; + box-shadow: 0 0 6px rgba(76,175,80,.8); + animation: ngd-led 5s ease-in-out infinite; +} +/* Checkmark badge — visible only during offline phase */ +.ngd-player__check { + position: absolute; + top: -14px; + right: -11px; + width: 24px; height: 24px; + opacity: 0; + transform: scale(0.4); + transform-origin: center; + animation: ngd-check-show 5s ease-in-out infinite; + filter: drop-shadow(0 0 4px rgba(76,175,80,.6)); +} + +/* ── Signal wrap (globe + vertical line to player) ─────────── */ +.ngd-signal-wrap { + position: absolute; + /* body centre X: player left(428) + connector(18) + body half(42) = 488 + globe width 88px → half = 44 → left = 488 − 44 = 444px */ + left: 444px; + top: 22px; + width: 88px; + display: flex; + flex-direction: column; + align-items: center; +} +.ngd-cloud { + width: 88px; + flex-shrink: 0; +} +.ngd-cloud__svg { + width: 100%; + height: auto; + display: block; +} +.ngd-cloud__path { + stroke: #4CAF50; + opacity: .85; + animation: ngd-cloud-color 5s ease-in-out infinite; +} +/* Vertical signal line */ +.ngd-signal-line { + width: 3px; + height: 92px; + background: rgba(76,175,80,.35); + border-radius: 2px; + position: relative; + overflow: visible; + animation: ngd-line-col 5s ease-in-out infinite; +} +/* Dots container — hidden during break phase */ +.ngd-signal__dots { + position: absolute; + inset: 0; + overflow: hidden; + animation: ngd-dots-vis 5s linear infinite; +} +.ngd-signal__dot { + position: absolute; + left: 50%; + transform: translateX(-50%); + width: 5px; height: 5px; + border-radius: 50%; + background: #4CAF50; + box-shadow: 0 0 5px rgba(76,175,80,.8); + animation: ngd-dot-up 0.9s ease-in-out infinite; +} +.ngd-signal__dot--2 { animation-delay: -0.6s; } +.ngd-signal__dot--3 { animation-delay: -1.2s; } +/* Break × badge — centered on line */ +.ngd-signal__break { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%) scale(0); + width: 18px; height: 18px; + opacity: 0; + animation: ngd-break-show 5s ease-in-out infinite; + filter: drop-shadow(0 0 4px rgba(239,68,68,.5)); +} + +/* ═══════════════════════════════════════════════════════════ + KEYFRAMES (10 s cycle) + ═══════════════════════════════════════════════════════════ */ + +/* Ticker scroll */ +@keyframes ngd-ticker { + from { transform: translateX(0); } + to { transform: translateX(-50%); } +} + +/* Highlighted menu row pulsing */ +@keyframes ngd-hl-pulse { + 0%, 100% { background: rgba(76,175,80,.14); } + 50% { background: rgba(76,175,80,.28); } +} + +/* Player LED: green → amber → red during disconnect → back to green */ +@keyframes ngd-led { + 0%, 29% { background: #4CAF50; box-shadow: 0 0 6px rgba(76,175,80,.8); } + 33% { background: #f59e0b; box-shadow: 0 0 5px rgba(245,158,11,.6); } + 38%, 71% { background: #ef4444; box-shadow: 0 0 4px rgba(239,68,68,.4); } + 75% { background: #4CAF50; box-shadow: 0 0 6px rgba(76,175,80,.8); } + 100% { background: #4CAF50; box-shadow: 0 0 6px rgba(76,175,80,.8); } +} + +/* Cloud stroke: green → red → green */ +@keyframes ngd-cloud-color { + 0%, 29% { stroke: #4CAF50; opacity: .85; } + 36%, 71% { stroke: #ef4444; opacity: 1; } + 76%, 100% { stroke: #4CAF50; opacity: .85; } +} + +/* Signal line colour */ +@keyframes ngd-line-col { + 0%, 29% { background: rgba(76,175,80,.35); } + 36%, 71% { background: rgba(239,68,68,.45); } + 76%, 100% { background: rgba(76,175,80,.35); } +} + +/* Dots container: visible during connected phases only */ +@keyframes ngd-dots-vis { + 0%, 29% { opacity: 1; } + 33% { opacity: 0; } + 71% { opacity: 0; } + 75%, 100% { opacity: 1; } +} + +/* Single dot travelling bottom → top */ +@keyframes ngd-dot-up { + 0% { bottom: 1px; opacity: 0; } + 8% { opacity: 1; } + 85% { opacity: 1; } + 100% { bottom: calc(100% - 5px); opacity: 0; } +} + +/* Break × badge: appears when disconnected */ +@keyframes ngd-break-show { + 0%, 29% { opacity: 0; transform: translate(-50%,-50%) scale(0); } + 38%, 71% { opacity: 1; transform: translate(-50%,-50%) scale(1); } + 76%, 100% { opacity: 0; transform: translate(-50%,-50%) scale(0); } +} + +/* Checkmark: appears when offline, confirming local playback */ +@keyframes ngd-check-show { + 0%, 44% { opacity: 0; transform: scale(0.4); } + 52%, 71% { opacity: 1; transform: scale(1); } + 77%, 100% { opacity: 0; transform: scale(0.4); } +} + +/* ── Responsive ─────────────────────────────────────────────── */ +@media (max-width: 640px) { + .ngd-stage { max-width: 320px; } + /* Scale at 84% (320/380) */ + .ngd-tv { left: 17px; top: 76px; } + .ngd-tv__body { width: 185px; height: 136px; } + .ngd-tv__screen { --ngd-menu-scale-x: 0.8409; --ngd-menu-scale-y: 0.8395; } + /* port at body midpoint: 136/2 − 7 = 61 */ + .ngd-tv__port { top: 61px; } + .ngd-tv__feet { gap: 60px; } + .ngd-tv__foot { width: 24px; } + /* TV right: 17+4+185+4=210; port centre-y: 76+4+61+7=148; player top: 148−13=135 */ + .ngd-player { left: 210px; top: 135px; } + .ngd-player__connector { width: 12px; height: 9px; } + .ngd-player__body { width: 68px; height: 24px; } + .ngd-player__body::before { top: 5px; left: 6px; width: 24px; height: 10px; } + .ngd-player__led { width: 3px; height: 3px; right: 6px; } + /* body centre-x: 210+12+34=256; globe 72px → half=36 → wrap left=220 */ + .ngd-signal-wrap { left: 220px; top: 18px; width: 72px; } + /* line: wrap.top(18)+globe_h(~52)=70 to player.top(135) → 65px */ + .ngd-signal-line { height: 65px; } +} + +/* ── Reduced-motion overrides ───────────────────────────────── */ +@media (prefers-reduced-motion: reduce) { + .ngd-tv__screen::after, + .ngd-menu__ticker-inner, + .ngd-menu__row--hl, + .ngd-player__led, + .ngd-signal__dot, + .ngd-signal__dots, + .ngd-signal__break, + .ngd-cloud__path, + .ngd-signal-line, + .ngd-player__check { animation: none !important; } + /* Static fallback states */ + .ngd-player__led { background: #4CAF50; box-shadow: 0 0 5px rgba(76,175,80,.6); } + .ngd-cloud__path { stroke: #4CAF50; opacity: .8; } + .ngd-signal__dots { opacity: 1; } +} + +/* ═══════════════════════════════════════════════════════════ + BRANDED DISPLAY ANIMATION (.platform-visual.has-branded) + Sequence (8 s loop per screen, staggered by 0.5 s): + 0 – 25 % : Boot splash — logo fades in centred on dark screen + 25 – 40 % : Transition — logo shrinks to header, brand bar grows + 40 – 90 % : Content — UI blocks slide in beneath brand header + 90 – 100% : Reset — fade out, loop + ═══════════════════════════════════════════════════════════ */ + +/* ── Wrapper overrides ─────────────────────────────────────── */ +.platform-visual.has-branded { + background: none !important; + border: none !important; + border-radius: 0; + aspect-ratio: unset; + padding: 0; + overflow: visible; + position: relative; + font-size: inherit; + display: flex; + align-items: center; + justify-content: center; + align-self: center; +} + +/* ── Stage ─────────────────────────────────────────────────── */ +.bd-stage { + position: relative; + width: 100%; + max-width: 500px; + aspect-ratio: 4/3; + margin: 0 auto; + display: flex; + align-items: center; + justify-content: center; + gap: 22px; +} + +/* ── Shared device styles ─────────────────────────────────── */ +.bd-device { + display: flex; + flex-direction: column; + align-items: center; + position: relative; +} +.bd-device__body { + background: #111; + border: 3px solid #111; + border-radius: 5px; + outline: 1px solid #000; + padding: 2px; + display: flex; + align-items: stretch; + position: relative; + box-shadow: 0 10px 32px rgba(0,0,0,.50); +} +.bd-device__screen { + width: 100%; + height: 100%; + border-radius: 2px; + position: relative; + overflow: hidden; + background: linear-gradient(135deg, #0c1016 0%, #151c28 60%, #0c1016 100%); +} +.bd-device__label { + font-size: 10px; + color: rgba(255,255,255,.45); + margin-bottom: 6px; + letter-spacing: .5px; + text-transform: uppercase; + order: -1; +} + +/* ── Tablet (portrait kiosk) ──────────────────────────────── */ +.bd-device--tablet .bd-device__body { + width: 96px; + height: 144px; + border-radius: 8px; +} +.bd-device--tablet .bd-device__screen { border-radius: 4px; } +.bd-device--tablet { animation-delay: 0s; } + +/* ── Wall display (wide landscape) ────────────────────────── */ +.bd-device--wall .bd-device__body { + width: 216px; + height: 130px; +} +.bd-mount { + width: 7px; + height: 22px; + background: #222; + margin-top: -1px; + border-radius: 0 0 2px 2px; +} +.bd-device--wall { animation-delay: 0.5s; } + +/* ── Interactive tablet on table ───────────────────────────── */ +.bd-device--interactive .bd-device__body { + width: 132px; + height: 89px; + border-radius: 6px; + transform: perspective(400px) rotateX(25deg); + transform-origin: bottom center; +} +.bd-device--interactive .bd-device__screen { border-radius: 3px; } +.bd-table { + width: 156px; + height: 10px; + background: linear-gradient(180deg, #2a2a2a, #1a1a1a); + border-radius: 3px; + margin-top: -2px; + box-shadow: 0 4px 12px rgba(0,0,0,.4); +} +.bd-device--interactive { animation-delay: 1s; } + +/* ── Boot splash overlay ──────────────────────────────────── */ +.bd-splash { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + z-index: 2; + animation: bd-splash 8s ease-in-out infinite; +} +.bd-device--wall .bd-splash { animation-delay: 0.5s; } +.bd-device--interactive .bd-splash { animation-delay: 1s; } + +.bd-splash__logo { + width: 28px; + height: 28px; + background: var(--color-primary); + border-radius: 6px; + box-shadow: 0 0 20px rgba(var(--color-primary-rgb),.5), 0 0 50px rgba(var(--color-primary-rgb),.15); + animation: bd-logo-pulse 8s ease-in-out infinite; +} +.bd-device--wall .bd-splash__logo { + width: 32px; + height: 32px; + animation-delay: 0.5s; +} +.bd-device--interactive .bd-splash__logo { + animation-delay: 1s; +} + +/* ── Branded UI chrome ────────────────────────────────────── */ +.bd-ui { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + padding: 0; + opacity: 0; + z-index: 1; + animation: bd-ui-show 8s ease-in-out infinite; +} +.bd-device--wall .bd-ui { animation-delay: 0.5s; } +.bd-device--interactive .bd-ui { animation-delay: 1s; } + +/* Brand header bar */ +.bd-ui__header { + display: flex; + align-items: center; + gap: 4px; + padding: 4px 5px; + background: rgba(var(--color-primary-rgb),.15); + border-bottom: 1px solid rgba(var(--color-primary-rgb),.25); + flex-shrink: 0; +} +.bd-ui__logo { + width: 10px; + height: 10px; + background: var(--color-primary); + border-radius: 3px; + flex-shrink: 0; +} +.bd-ui__brand-bar { + flex: 1; + height: 4px; + background: rgba(var(--color-primary-rgb),.4); + border-radius: 2px; + max-width: 50px; + animation: bd-bar-grow 8s ease-in-out infinite; +} +.bd-device--wall .bd-ui__brand-bar { animation-delay: 0.5s; } +.bd-device--interactive .bd-ui__brand-bar { animation-delay: 1s; } + +/* Content area */ +.bd-ui__content { + flex: 1; + display: flex; + flex-direction: column; + gap: 4px; + padding: 6px 5px; + animation: bd-content-slide 8s ease-in-out infinite; +} +.bd-device--wall .bd-ui__content { animation-delay: 0.5s; } +.bd-device--interactive .bd-ui__content { animation-delay: 1s; } + +.bd-ui__block { + border-radius: 2px; +} +.bd-ui__block--txt { + height: 4px; + background: rgba(255,255,255,.18); +} +.bd-ui__block--short { + width: 60%; +} +.bd-ui__block--img { + flex: 1; + min-height: 16px; + background: linear-gradient( + 135deg, + rgba(var(--color-primary-rgb),.10) 0%, + rgba(var(--color-primary-rgb),.05) 100% + ); + border: 1px solid rgba(var(--color-primary-rgb),.12); +} + +/* ── Promotional content layouts ─────────────────────── */ +.bd-promo { + flex: 1; + display: flex; + flex-direction: column; + gap: 3px; +} + +/* ── Kiosk: Welcome / check-in screen ── */ +.bd-promo--welcome { + align-items: center; + text-align: center; + gap: 4px; +} +.bd-promo__hero { + width: 100%; + flex: 1; + min-height: 28px; + background: linear-gradient(135deg, rgba(var(--color-primary-rgb),.18) 0%, rgba(var(--color-primary-rgb),.06) 100%); + border-radius: 2px; +} +.bd-promo__heading { + width: 70%; + height: 5px; + background: rgba(255,255,255,.3); + border-radius: 1px; +} +.bd-promo__text { + width: 85%; + height: 3px; + background: rgba(255,255,255,.14); + border-radius: 1px; +} +.bd-promo__btn { + width: 45%; + height: 8px; + background: var(--color-primary); + border-radius: 3px; + margin-top: 2px; +} + +/* ── Wall display: Sale / featured product ── */ +.bd-promo--sale { + gap: 0; +} +.bd-promo__cols { + display: flex; + gap: 5px; + flex: 1; +} +.bd-promo__visual { + flex: 1; + background: linear-gradient(160deg, rgba(var(--color-primary-rgb),.20) 0%, rgba(var(--color-primary-rgb),.06) 100%); + border-radius: 2px; + min-height: 24px; +} +.bd-promo__info { + flex: 1; + display: flex; + flex-direction: column; + gap: 3px; + justify-content: center; +} +.bd-promo__badge { + width: 28px; + height: 6px; + background: rgba(239,68,68,.7); + border-radius: 2px; +} +.bd-promo--sale .bd-promo__heading { + width: 90%; + height: 4px; +} +.bd-promo--sale .bd-promo__text { + width: 100%; + height: 3px; +} +.bd-promo__text--short { + width: 65% !important; +} +.bd-promo__price { + width: 36px; + height: 6px; + background: rgba(255,200,80,.6); + border-radius: 1px; + margin-top: 1px; +} + +/* ── Interactive: Touch menu / category grid ── */ +.bd-promo--menu { + gap: 3px; +} +.bd-promo--menu .bd-promo__heading { + width: 55%; + height: 4px; +} +.bd-promo__grid { + flex: 1; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 3px; +} +.bd-promo__tile { + background: rgba(255,255,255,.06); + border-radius: 2px; + display: flex; + flex-direction: column; + overflow: hidden; +} +.bd-promo__tile-img { + flex: 1; + background: linear-gradient(135deg, rgba(var(--color-primary-rgb),.14) 0%, rgba(var(--color-primary-rgb),.04) 100%); +} +.bd-promo__tile-lbl { + height: 4px; + margin: 2px 3px; + background: rgba(255,255,255,.18); + border-radius: 1px; +} + +/* ═══════════════════════════════════════════════════════════ + KEYFRAMES (8 s cycle) + ═══════════════════════════════════════════════════════════ */ + +/* Splash: visible 0–30%, fades out 30–40%, hidden rest */ +@keyframes bd-splash { + 0% { opacity: 0; } + 5% { opacity: 1; } + 25% { opacity: 1; } + 35% { opacity: 0; } + 100% { opacity: 0; } +} + +/* Logo pulse glow during splash */ +@keyframes bd-logo-pulse { + 0%, 25% { transform: scale(1); box-shadow: 0 0 20px rgba(var(--color-primary-rgb),.5), 0 0 50px rgba(var(--color-primary-rgb),.15); } + 12% { transform: scale(1.08); box-shadow: 0 0 28px rgba(var(--color-primary-rgb),.7), 0 0 60px rgba(var(--color-primary-rgb),.25); } + 35%, 100% { transform: scale(0.6); opacity: 0; } +} + +/* UI chrome: hidden 0–28%, fades in 28–38%, visible until 88%, fades out */ +@keyframes bd-ui-show { + 0%, 28% { opacity: 0; } + 38% { opacity: 1; } + 88% { opacity: 1; } + 96%, 100% { opacity: 0; } +} + +/* Brand bar grows in from 0 width */ +@keyframes bd-bar-grow { + 0%, 30% { transform: scaleX(0); transform-origin: left; } + 42% { transform: scaleX(1); transform-origin: left; } + 88% { transform: scaleX(1); } + 96%, 100% { transform: scaleX(0); } +} + +/* Content blocks slide up into view */ +@keyframes bd-content-slide { + 0%, 35% { opacity: 0; transform: translateY(10px); } + 48% { opacity: 1; transform: translateY(0); } + 88% { opacity: 1; transform: translateY(0); } + 96%, 100% { opacity: 0; transform: translateY(4px); } +} + +/* ── Responsive ─────────────────────────────────────────────── */ +@media (max-width: 900px) { + .bd-stage { max-width: 430px; gap: 14px; } + .bd-device--tablet .bd-device__body { width: 76px; height: 114px; } + .bd-device--wall .bd-device__body { width: 170px; height: 102px; } + .bd-device--interactive .bd-device__body { width: 101px; height: 67px; } + .bd-table { width: 120px; } +} +@media (max-width: 640px) { + .bd-stage { max-width: 340px; gap: 10px; } + .bd-device--tablet .bd-device__body { width: 60px; height: 90px; } + .bd-device--wall .bd-device__body { width: 138px; height: 83px; } + .bd-device--interactive .bd-device__body { width: 83px; height: 55px; } + .bd-device__label { font-size: 9px; } + .bd-table { width: 98px; height: 7px; } +} + +/* ── Reduced-motion overrides ───────────────────────────────── */ +@media (prefers-reduced-motion: reduce) { + .bd-splash, + .bd-splash__logo, + .bd-ui, + .bd-ui__brand-bar, + .bd-ui__content { animation: none !important; } + /* Static fallback: show branded UI */ + .bd-splash { opacity: 0; } + .bd-ui { opacity: 1; } + .bd-ui__brand-bar { transform: scaleX(1); } + .bd-ui__content { opacity: 1; transform: translateY(0); } +} + +/* ═══════════════════════════════════════════════════════════════ + GALLERY TV SLIDESHOW (.platform-visual.has-gallery-tv) + ═══════════════════════════════════════════════════════════════ */ + +.platform-visual.has-gallery-tv { + background: none !important; + border: none !important; + border-radius: 0; + aspect-ratio: unset; + padding: 0; + overflow: visible; + box-shadow: none; + font-size: inherit; +} + +.gtv-stage { + width: 100%; + max-width: 520px; + margin: 0 auto; +} + +/* TV frame (mirrors dashboard-tv pattern) */ +.gtv-tv { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; +} +.gtv-tv__body { + width: 100%; + background: #111; + border: 5px solid #1a1a1a; + border-radius: 8px 8px 4px 4px; + outline: 2px solid #000; + padding: 4px; + position: relative; + box-shadow: + 0 14px 48px rgba(0,0,0,0.6), + 0 0 0 1px rgba(255,255,255,0.04), + inset 0 1px 0 rgba(255,255,255,0.06); +} +.gtv-tv__body::after { + content: '\25CF'; + position: absolute; + bottom: -14px; + left: 50%; + transform: translateX(-50%); + font-size: 7px; + color: rgba(74,222,128,0.8); + filter: drop-shadow(0 0 3px rgba(74,222,128,0.5)); +} +.gtv-tv__screen { + width: 100%; + aspect-ratio: 16/9; + background: var(--color-bg); + border-radius: 2px; + overflow: hidden; + position: relative; +} +.gtv-tv__feet { + display: flex; + justify-content: space-between; + width: 60%; + max-width: 300px; +} +.gtv-tv__foot { + width: 12px; + height: 8px; + background: #111; + border: 1px solid #000; + border-radius: 0 0 4px 4px; +} + +/* Slides */ +.gtv-slides { + position: relative; + width: 100%; + height: 100%; +} +.gtv-slide { + position: absolute; + inset: 0; + opacity: 0; + transition: opacity 0.8s ease; +} +.gtv-slide.is-active { + opacity: 1; +} +.gtv-slide img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +/* ── Reduced-motion overrides for gallery TV ── */ +@media (prefers-reduced-motion: reduce) { + .gtv-slide { + transition: none !important; + } +} + +/* ══════════════════════════════════════════════════════════════════════════════ + VIDEO EDITOR ANIMATOR (.platform-visual.has-video-editor) + Laptop frame with dark editor UI, animated timeline playhead and preview. + ══════════════════════════════════════════════════════════════════════════════ */ +.platform-visual.has-video-editor { + background: none !important; + border: none !important; + border-radius: 0; + aspect-ratio: unset; + padding: 0; + overflow: visible; + display: flex; + align-items: center; + justify-content: center; + min-height: 200px; +} + +.ve-stage { + width: 100%; + max-width: 560px; +} + +.ve-svg { + width: 100%; + height: auto; + display: block; + filter: drop-shadow(0 8px 28px rgba(0, 0, 0, 0.18)); +} diff --git a/theme/assets/js/industry-animator.js b/theme/assets/js/industry-animator.js new file mode 100644 index 0000000..45e6ae5 --- /dev/null +++ b/theme/assets/js/industry-animator.js @@ -0,0 +1,61 @@ +/** + * Gallery TV Slideshow + * Cycles through images inside gallery-TV blocks. + * Pauses off-screen via IntersectionObserver for performance. + * Respects prefers-reduced-motion. + */ +(function () { + 'use strict'; + + var INTERVAL = 4000; // ms between slides + + function boot() { + var stages = document.querySelectorAll('[data-gtv-slideshow]'); + if (!stages.length) return; + + /* Honour reduced-motion – show first slide only */ + if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; + + for (var i = 0; i < stages.length; i++) { + initSlideshow(stages[i]); + } + } + + function initSlideshow(stage) { + var slides = stage.querySelectorAll('.gtv-slide'); + if (slides.length < 2) return; + + var state = { current: 0, paused: false, timer: null }; + + /* IntersectionObserver – pause when off-screen */ + if ('IntersectionObserver' in window) { + new IntersectionObserver(function (entries) { + for (var j = 0; j < entries.length; j++) { + state.paused = !entries[j].isIntersecting; + } + if (!state.paused && !state.timer) { + state.timer = setInterval(function () { advance(slides, state); }, INTERVAL); + } else if (state.paused && state.timer) { + clearInterval(state.timer); + state.timer = null; + } + }, { rootMargin: '200px', threshold: 0.05 }).observe(stage); + } + + /* Start cycling */ + state.timer = setInterval(function () { advance(slides, state); }, INTERVAL); + } + + function advance(slides, state) { + if (state.paused) return; + slides[state.current].classList.remove('is-active'); + state.current = (state.current + 1) % slides.length; + slides[state.current].classList.add('is-active'); + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', boot); + } else { + boot(); + } +})(); diff --git a/theme/assets/js/main.js b/theme/assets/js/main.js index 91b6773..80f324f 100644 --- a/theme/assets/js/main.js +++ b/theme/assets/js/main.js @@ -17,6 +17,19 @@ document.addEventListener('DOMContentLoaded', () => { window.addEventListener('scroll', () => { header.classList.toggle('scrolled', window.scrollY > 40); }, { passive: true }); + + /* Detect whether the hero beneath the header has a light background. + .hero (homepage) is white in light mode; .page-hero stays dark. + Re-evaluate when the theme toggle changes data-theme. */ + function updateHeroContrast() { + const isLight = document.documentElement.getAttribute('data-theme') !== 'dark'; + const hasLightHero = isLight && document.querySelector('.hero') && !document.querySelector('.page-hero'); + header.classList.toggle('over-light-hero', !!hasLightHero); + } + updateHeroContrast(); + new MutationObserver(updateHeroContrast).observe(document.documentElement, { + attributes: true, attributeFilter: ['data-theme'] + }); } /* ── Mobile nav toggle ──────────────────────────────────── */ @@ -26,6 +39,7 @@ document.addEventListener('DOMContentLoaded', () => { toggle.addEventListener('click', () => { toggle.classList.toggle('open'); nav.classList.toggle('open'); + document.body.classList.toggle('menu-open'); const expanded = toggle.getAttribute('aria-expanded') === 'true'; toggle.setAttribute('aria-expanded', !expanded); }); @@ -119,18 +133,33 @@ document.addEventListener('DOMContentLoaded', () => { /* ── Animate cards on scroll ────────────────────────────── */ const cards = document.querySelectorAll('.oribi-card, .feature-card, .industry-card, .pricing-card, .value-card, .platform-row'); - if (cards.length && 'IntersectionObserver' in window) { - cards.forEach(c => { c.style.opacity = '0'; c.style.transform = 'translateY(24px)'; c.style.transition = 'opacity .5s ease, transform .5s ease'; }); - const io = new IntersectionObserver((entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - entry.target.style.opacity = '1'; - entry.target.style.transform = 'translateY(0)'; - io.unobserve(entry.target); + if (cards.length && 'IntersectionObserver' in window && + !window.matchMedia('(prefers-reduced-motion: reduce)').matches) { + cards.forEach(c => c.classList.add('scroll-hidden')); + /* Use rAF to ensure the class is applied before observing – avoids + Safari quirk where elements already in-viewport don't fire. */ + requestAnimationFrame(() => { + const io = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.classList.remove('scroll-hidden'); + entry.target.classList.add('scroll-visible'); + io.unobserve(entry.target); + } + }); + }, { threshold: 0.05, rootMargin: '0px 0px 80px 0px' }); + cards.forEach(c => io.observe(c)); + }); + /* Safety net: reveal any still-hidden elements after 4 s so content + is never permanently invisible (e.g. iOS Safari edge-cases). */ + setTimeout(() => { + cards.forEach(c => { + if (c.classList.contains('scroll-hidden')) { + c.classList.remove('scroll-hidden'); + c.classList.add('scroll-visible'); } }); - }, { threshold: 0.1 }); - cards.forEach(c => io.observe(c)); + }, 4000); } }); @@ -685,6 +714,7 @@ document.addEventListener('DOMContentLoaded', () => { if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { stages.forEach(function (stage) { stage.classList.add('is-plugged'); + startTsSlides(stage); }); return; } @@ -698,6 +728,10 @@ document.addEventListener('DOMContentLoaded', () => { // Add plugged state after slide-in completes (1.4s) setTimeout(function () { stage.classList.add('is-plugged'); + // Start promotional slide cycling after screen glow (0.9s) + setTimeout(function () { + startTsSlides(stage); + }, 900); }, 1400); io.unobserve(stage); } @@ -705,4 +739,17 @@ document.addEventListener('DOMContentLoaded', () => { }, { threshold: 0.3 }); stages.forEach(function (stage) { io.observe(stage); }); } + + function startTsSlides(stage) { + var slides = stage.querySelectorAll('.ts-slide'); + if (!slides.length) return; + var current = 0; + slides[0].classList.add('is-active'); + stage.classList.add('is-playing'); + setInterval(function () { + slides[current].classList.remove('is-active'); + current = (current + 1) % slides.length; + slides[current].classList.add('is-active'); + }, 3000); + } })(); diff --git a/theme/assets/js/video-editor-animator.js b/theme/assets/js/video-editor-animator.js new file mode 100644 index 0000000..21939f0 --- /dev/null +++ b/theme/assets/js/video-editor-animator.js @@ -0,0 +1,110 @@ +/** + * Video Editor Timeline Animator + * Animates playhead scrubbing across the timeline and video preview crossfades. + * Mirrors the structure and conventions of dashboard-animator.js. + */ +(function () { + 'use strict'; + + var DURATION = 10000; // ms for one full playhead sweep (left → right, then loop) + var X_MIN = 104; // leftmost playhead centre (SVG units) + var X_MAX = 504; // rightmost playhead centre (end of clip area) + + function makeState(svg) { + return { + svg: svg, + playheadLine: svg.querySelector('#ve-playhead-line'), + playheadHead: svg.querySelector('#ve-playhead-head'), + scene1: svg.querySelector('#ve-scene-1'), + scene2: svg.querySelector('#ve-scene-2'), + scene3: svg.querySelector('#ve-scene-3'), + innerScreen: svg.querySelector('#ve-inner-screen'), + timecode: svg.querySelector('#ve-timecode'), + scrubPct: 0, + lastTime: performance.now(), + paused: false + }; + } + + function lerp(a, b, t) { return a + (b - a) * t; } + + function updatePlayhead(st) { + var xC = lerp(X_MIN, X_MAX, st.scrubPct); + if (st.playheadLine) st.playheadLine.setAttribute('x', (xC - 1).toFixed(1)); + if (st.playheadHead) st.playheadHead.setAttribute('transform', 'translate(' + xC.toFixed(1) + ',234)'); + if (st.timecode) { + var s = Math.floor(st.scrubPct * 10); + st.timecode.textContent = '0:' + (s < 10 ? '0' + s : s); + } + } + + function updateScenes(st) { + var p = st.scrubPct; + var o1, o2, o3, fade; + if (p < 0.30) { + o1 = 1; o2 = 0; o3 = 0; + } else if (p < 0.40) { + fade = (p - 0.30) / 0.10; o1 = 1 - fade; o2 = fade; o3 = 0; + } else if (p < 0.65) { + o1 = 0; o2 = 1; o3 = 0; + } else if (p < 0.75) { + fade = (p - 0.65) / 0.10; o1 = 0; o2 = 1 - fade; o3 = fade; + } else { + o1 = 0; o2 = 0; o3 = 1; + } + if (st.scene1) st.scene1.setAttribute('opacity', o1.toFixed(3)); + if (st.scene2) st.scene2.setAttribute('opacity', o2.toFixed(3)); + if (st.scene3) st.scene3.setAttribute('opacity', o3.toFixed(3)); + if (st.innerScreen) { + st.innerScreen.setAttribute('fill', + p < 0.38 ? 'url(#ve-scr-warm)' : + p < 0.72 ? 'url(#ve-scr-cold)' : + 'url(#ve-scr-go)' + ); + } + } + + function loop(st, now) { + if (!st.paused) { + var delta = now - st.lastTime; + st.lastTime = now; + st.scrubPct += delta / DURATION; + if (st.scrubPct >= 1) st.scrubPct -= 1; + updatePlayhead(st); + updateScenes(st); + } else { + st.lastTime = now; // keep fresh so there is no jump on resume + } + requestAnimationFrame(function (t) { loop(st, t); }); + } + + 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('.ve-svg'); + if (!svgs.length) return; + if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; + for (var i = 0; i < svgs.length; i++) { + (function (svg) { + if (svg._veAnim) return; + var st = makeState(svg); + svg._veAnim = st; + observe(st); + requestAnimationFrame(function (t) { loop(st, t); }); + })(svgs[i]); + } + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', boot); + } else { + boot(); + } +})(); diff --git a/theme/blocks/editor.js b/theme/blocks/editor.js index 4fda89c..a8c8bdc 100644 --- a/theme/blocks/editor.js +++ b/theme/blocks/editor.js @@ -1170,28 +1170,105 @@ reg('oribi/platform-row', { parent: ['oribi/platform-section'], supports: { html: false, reusable: false }, attributes: { - heading: { type: 'string', default: '' }, - description: { type: 'string', default: '' }, - btnText: { type: 'string', default: 'Learn More' }, - btnUrl: { type: 'string', default: '' }, - visual: { type: 'string', default: '' }, - reversed: { type: 'boolean', default: false }, - imgId: { type: 'number', default: 0 }, - imgUrl: { type: 'string', default: '' }, - imgAlt: { type: 'string', default: '' }, - imgWidth: { type: 'number', default: 300 }, - cameraAnim: { type: 'boolean', default: false }, + heading: { type: 'string', default: '' }, + description: { type: 'string', default: '' }, + btnText: { type: 'string', default: 'Learn More' }, + btnUrl: { type: 'string', default: '' }, + visual: { type: 'string', default: '' }, + reversed: { type: 'boolean', default: false }, + imgId: { type: 'number', default: 0 }, + imgUrl: { type: 'string', default: '' }, + imgAlt: { type: 'string', default: '' }, + imgWidth: { type: 'number', default: 300 }, + isDashboard: { type: 'boolean', default: false }, + deviceAnim: { type: 'boolean', default: false }, + tvStick: { type: 'boolean', default: false }, + cameraAnim: { type: 'boolean', default: false }, + neverGoesDark: { type: 'boolean', default: false }, + brandedAnim: { type: 'boolean', default: false }, + galleryIds: { type: 'array', default: [] }, }, edit: function (props) { var a = props.attributes, s = props.setAttributes; var imgW = a.imgWidth || 300; + + /* ── Animation HTML strings (mirror PHP render, used via dangerouslySetInnerHTML) ── */ + var DA_SCREEN = '
'; + var DA_HTML = ''; + + var TS_MI = '
'; + var TS_COL = '
' + TS_MI + TS_MI + TS_MI + '
'; + var TS_HTML = ''; + + var NGD_ROW = '
'; + var NGD_ROWH = '
'; + var NGD_HTML = ''; + + var BD_SPLASH = '
'; + var BD_HDR = '
'; + var BD_HTML = ''; + + var DB_HTML = '
PerformanceAPICacheDBQueueWorkerRequests/secReadWriteUpdateDeleteTraffic TrendDistribution
'; + return el(Frag, null, el(IC, null, el(PB, { title: 'Row Settings' }, el(TC, { label: 'Visual (emoji)', value: a.visual, onChange: function(v){s({visual:v});} }), el(TC, { label: 'Button URL', value: a.btnUrl, onChange: function(v){s({btnUrl:v});} }), el(TG, { label: 'Reversed', checked: !!a.reversed, onChange: function(v){s({reversed:v});} }), - el(TG, { label: 'Camera Animation', checked: !!a.cameraAnim, onChange: function(v){s({cameraAnim:v});} }) + el(TG, { label: 'Dashboard Animation', checked: !!a.isDashboard, onChange: function(v){s({isDashboard:v});} }), + el(TG, { label: 'Device Animation', checked: !!a.deviceAnim, onChange: function(v){s({deviceAnim:v});} }), + el(TG, { label: 'TV Stick Animation', checked: !!a.tvStick, onChange: function(v){s({tvStick:v});} }), + el(TG, { label: 'Camera Animation', checked: !!a.cameraAnim, onChange: function(v){s({cameraAnim:v});} }), + el(TG, { label: 'Never Goes Dark', checked: !!a.neverGoesDark, onChange: function(v){s({neverGoesDark:v});} }), + el(TG, { label: 'Branded Display', checked: !!a.brandedAnim, onChange: function(v){s({brandedAnim:v});} }) + ), + el(PB, { title: 'Gallery TV Slideshow', initialOpen: false }, + el(MUC, null, + el(MU, { + onSelect: function(media) { + s({ galleryIds: media.map(function(m){ return m.id; }) }); + }, + allowedTypes: ['image'], + gallery: true, + multiple: true, + value: a.galleryIds || [], + render: function(ref) { + return el(Frag, null, + a.galleryIds && a.galleryIds.length + ? el('div', { style: { marginBottom: '8px' } }, + el('p', { style: { margin: '0 0 4px' } }, a.galleryIds.length + ' image(s) selected'), + el(Btn, { variant: 'link', isDestructive: true, onClick: function(){ s({ galleryIds: [] }); } }, 'Clear gallery') + ) + : null, + el(Btn, { onClick: ref.open, variant: 'secondary', __next40pxDefaultSize: true }, + a.galleryIds && a.galleryIds.length ? 'Edit gallery' : 'Select images for TV slideshow') + ); + } + }) + ) ), el(PB, { title: 'Visual Image', initialOpen: false }, el(MUC, null, @@ -1222,7 +1299,9 @@ reg('oribi/platform-row', { a.btnUrl ? el(RT, { tagName: 'span', className: 'btn btn-outline mt-3', value: a.btnText, onChange: function(v){s({btnText:v});}, placeholder: 'Button...' }) : null ), - a.cameraAnim + a.isDashboard + ? el('div', { className: 'platform-visual has-dashboard', dangerouslySetInnerHTML: { __html: DB_HTML } }) + : a.cameraAnim ? el('div', { className: 'platform-visual has-camera' }, el('div', { className: 'cam-stage', 'aria-hidden': 'true' }, // Photo camera (left) @@ -1282,6 +1361,14 @@ reg('oribi/platform-row', { ) ) ) + : a.deviceAnim + ? el('div', { className: 'platform-visual has-anim', dangerouslySetInnerHTML: { __html: DA_HTML } }) + : a.tvStick + ? el('div', { className: 'platform-visual has-tv-stick', dangerouslySetInnerHTML: { __html: TS_HTML } }) + : a.neverGoesDark + ? el('div', { className: 'platform-visual has-ngd', dangerouslySetInnerHTML: { __html: NGD_HTML } }) + : a.brandedAnim + ? el('div', { className: 'platform-visual has-branded', dangerouslySetInnerHTML: { __html: BD_HTML } }) : a.imgUrl ? el('div', { className: 'platform-visual has-img' }, el('img', { src: a.imgUrl, style: { width: imgW + 'px', maxWidth: '100%', height: 'auto', borderRadius: '4px', objectFit: 'contain', display: 'block', marginInline: 'auto' } }) @@ -1618,6 +1705,74 @@ reg('oribi/comparison-table', { var a = props.attributes, s = props.setAttributes; var cols = a.columns || []; var rows = a.rows || []; + + /* ── Column helpers ─────────────────────────────────────── */ + function updateCol(idx, val) { + var c = cols.slice(); c[idx] = val; s({ columns: c }); + } + function addCol() { + var newRows = rows.map(function(r) { + if (r.group) return r; + return Object.assign({}, r, { values: (r.values || []).concat([false]) }); + }); + s({ columns: cols.concat(['Plan']), rows: newRows }); + } + function removeCol(idx) { + var c = cols.slice(); c.splice(idx, 1); + var newRows = rows.map(function(r) { + if (r.group) return r; + var v = (r.values || []).slice(); v.splice(idx, 1); + return Object.assign({}, r, { values: v }); + }); + s({ columns: c, rows: newRows }); + } + + /* ── Row helpers ────────────────────────────────────────── */ + function updateRow(idx, key, val) { + var r = rows.slice(); + r[idx] = Object.assign({}, r[idx]); + r[idx][key] = val; + s({ rows: r }); + } + function updateCell(ri, ci, val) { + var r = rows.slice(); + r[ri] = Object.assign({}, r[ri]); + var v = (r[ri].values || []).slice(); + v[ci] = val; + r[ri].values = v; + s({ rows: r }); + } + function toggleCell(ri, ci) { + var val = (rows[ri].values || [])[ci]; + updateCell(ri, ci, val === true ? false : val === false ? true : true); + } + function switchCellToText(ri, ci) { updateCell(ri, ci, ''); } + function addFeatureRow() { + var vals = cols.map(function() { return false; }); + s({ rows: rows.concat([{ feature: 'New feature', values: vals }]) }); + } + function addGroupRow() { + s({ rows: rows.concat([{ group: 'New Group' }]) }); + } + function removeRow(idx) { + var r = rows.slice(); r.splice(idx, 1); s({ rows: r }); + } + function moveRow(idx, dir) { + var t = idx + dir; + if (t < 0 || t >= rows.length) return; + var r = rows.slice(); + var tmp = r[idx]; r[idx] = r[t]; r[t] = tmp; + s({ rows: r }); + } + + /* ── Inline styles for editor controls ──────────────────── */ + var inputStyle = { width: '100%', padding: '4px 6px', border: '1px solid #ddd', borderRadius: '3px', fontSize: '13px', background: 'transparent', boxSizing: 'border-box' }; + var thInputStyle = Object.assign({}, inputStyle, { fontWeight: 600, textAlign: 'center' }); + var smallBtnStyle = { background: 'none', border: 'none', cursor: 'pointer', padding: '2px 4px', fontSize: '11px', lineHeight: 1, opacity: 0.6, verticalAlign: 'middle' }; + var rowCtrlStyle = { whiteSpace: 'nowrap', width: '1%', padding: '4px', verticalAlign: 'middle', border: 'none', background: 'transparent' }; + var cellBtnStyle = { background: 'none', border: '1px solid #ddd', borderRadius: '3px', cursor: 'pointer', padding: '2px 6px', fontSize: '13px', lineHeight: '1.4', margin: '0 1px' }; + + /* ── Render ─────────────────────────────────────────────── */ return el(Frag, null, el(IC, null, el(PB, { title: 'Table Settings' }, @@ -1625,6 +1780,21 @@ reg('oribi/comparison-table', { { label: 'Normal', value: 'normal' }, { label: 'Alternate', value: 'alt' } ], onChange: function(v){s({variant:v});} }), el(TC, { label: 'Label', value: a.label, onChange: function(v){s({label:v});} }) + ), + el(PB, { title: 'Columns', initialOpen: false }, + cols.map(function(col, i) { + return el('div', { key: i, style: { display: 'flex', gap: '4px', marginBottom: '8px', alignItems: 'center' } }, + el(TC, { label: '', value: col, onChange: function(v){ updateCol(i, v); }, style: { flex: 1 } }), + el(Btn, { isDestructive: true, isSmall: true, onClick: function(){ removeCol(i); } }, '\u2715') + ); + }), + el(Btn, { isSecondary: true, isSmall: true, onClick: addCol }, '+ Add Column') + ), + el(PB, { title: 'Add Rows', initialOpen: false }, + el('div', { style: { display: 'flex', gap: '8px' } }, + el(Btn, { isSecondary: true, isSmall: true, onClick: addFeatureRow }, '+ Feature Row'), + el(Btn, { isSecondary: true, isSmall: true, onClick: addGroupRow }, '+ Group Row') + ) ) ), el('section', { className: a.variant === 'alt' ? 'section section-alt' : 'section' }, @@ -1639,26 +1809,62 @@ reg('oribi/comparison-table', { el('thead', null, el('tr', null, el('th', { className: 'comparison-feature-col' }, 'Feature'), - cols.map(function(col, i) { return el('th', { key: i }, col); }) + cols.map(function(col, i) { + return el('th', { key: i }, + el('input', { type: 'text', value: col, style: thInputStyle, onChange: function(e){ updateCol(i, e.target.value); } }) + ); + }), + el('th', { style: rowCtrlStyle }) ) ), el('tbody', null, - rows.map(function(row, i) { + rows.map(function(row, ri) { if (row.group) { - return el('tr', { key: i, className: 'comparison-group-row' }, - el('td', { colSpan: cols.length + 1 }, row.group) + return el('tr', { key: ri, className: 'comparison-group-row' }, + el('td', { colSpan: cols.length + 1 }, + el('input', { type: 'text', value: row.group, style: Object.assign({}, inputStyle, { fontWeight: 700 }), onChange: function(e){ updateRow(ri, 'group', e.target.value); } }) + ), + el('td', { style: rowCtrlStyle }, + el('button', { style: smallBtnStyle, onClick: function(){ moveRow(ri, -1); }, title: 'Move up' }, '\u25B2'), + el('button', { style: smallBtnStyle, onClick: function(){ moveRow(ri, 1); }, title: 'Move down' }, '\u25BC'), + el('button', { style: Object.assign({}, smallBtnStyle, { color: '#b00' }), onClick: function(){ removeRow(ri); }, title: 'Remove row' }, '\u2715') + ) ); } - return el('tr', { key: i }, - el('td', { className: 'comparison-feature-name' }, row.feature || ''), - (row.values || []).map(function(val, j) { - return el('td', { key: j, className: 'comparison-cell' }, - val === true ? '\u2713' : val === false ? '\u2014' : String(val) + return el('tr', { key: ri }, + el('td', { className: 'comparison-feature-name' }, + el('input', { type: 'text', value: row.feature || '', style: inputStyle, placeholder: 'Feature name\u2026', onChange: function(e){ updateRow(ri, 'feature', e.target.value); } }) + ), + (row.values || []).map(function(val, ci) { + if (typeof val === 'boolean') { + return el('td', { key: ci, className: 'comparison-cell', style: { textAlign: 'center' } }, + el('button', { style: Object.assign({}, cellBtnStyle, { color: val ? '#2e7d32' : '#c62828' }), onClick: function(){ toggleCell(ri, ci); }, title: 'Toggle \u2713/\u2717' }, + val ? '\u2713' : '\u2717' + ), + el('button', { style: Object.assign({}, smallBtnStyle, { fontSize: '10px' }), onClick: function(){ switchCellToText(ri, ci); }, title: 'Switch to text' }, 'Aa') + ); + } + return el('td', { key: ci, className: 'comparison-cell' }, + el('div', { style: { display: 'flex', alignItems: 'center', gap: '2px' } }, + el('input', { type: 'text', value: String(val), style: Object.assign({}, inputStyle, { flex: 1, minWidth: '60px' }), placeholder: 'Value\u2026', onChange: function(e){ updateCell(ri, ci, e.target.value); } }), + el('button', { style: Object.assign({}, smallBtnStyle, { color: '#2e7d32' }), onClick: function(){ updateCell(ri, ci, true); }, title: 'Set as \u2713' }, '\u2713'), + el('button', { style: Object.assign({}, smallBtnStyle, { color: '#c62828' }), onClick: function(){ updateCell(ri, ci, false); }, title: 'Set as \u2717' }, '\u2717') + ) ); - }) + }), + el('td', { style: rowCtrlStyle }, + el('button', { style: smallBtnStyle, onClick: function(){ moveRow(ri, -1); }, title: 'Move up' }, '\u25B2'), + el('button', { style: smallBtnStyle, onClick: function(){ moveRow(ri, 1); }, title: 'Move down' }, '\u25BC'), + el('button', { style: Object.assign({}, smallBtnStyle, { color: '#b00' }), onClick: function(){ removeRow(ri); }, title: 'Remove row' }, '\u2715') + ) ); }) ) + ), + el('div', { style: { display: 'flex', gap: '8px', marginTop: '12px', justifyContent: 'center' } }, + el(Btn, { isSecondary: true, isSmall: true, onClick: addFeatureRow }, '+ Feature Row'), + el(Btn, { isSecondary: true, isSmall: true, onClick: addGroupRow }, '+ Group Row'), + el(Btn, { isSecondary: true, isSmall: true, onClick: addCol }, '+ Column') ) ) ) diff --git a/theme/blocks/index.php b/theme/blocks/index.php index 9f4bab8..cf382cb 100644 --- a/theme/blocks/index.php +++ b/theme/blocks/index.php @@ -553,9 +553,13 @@ 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 ], - 'cameraAnim' => [ 'type' => 'boolean', 'default' => false ], + 'isDashboard' => [ 'type' => 'boolean', 'default' => false ], + 'deviceAnim' => [ 'type' => 'boolean', 'default' => false ], + 'tvStick' => [ 'type' => 'boolean', 'default' => false ], + 'cameraAnim' => [ 'type' => 'boolean', 'default' => false ], + 'neverGoesDark'=> [ 'type' => 'boolean', 'default' => false ], + 'brandedAnim' => [ 'type' => 'boolean', 'default' => false ], + 'galleryIds' => [ 'type' => 'array', 'default' => [], 'items' => [ 'type' => 'number' ] ], ], 'supports' => $block_supports, 'render_callback' => 'oribi_render_platform_row', @@ -671,9 +675,8 @@ function oribi_render_site_header() {