commit 9fddd43a2cf6690cb2ce97ab98ccdc12f559e36a Author: Matt Batchelder Date: Thu Feb 19 09:10:01 2026 -0500 Add custom page templates for Home, Contact, and Services - Created a new Contact Page template with a contact form and information section. - Developed a Home Page template featuring a hero section, core capabilities, and pricing details. - Introduced a Services Page template outlining core services and industry solutions. - Added a default page template for standard pages without a custom template. - Implemented a single post template for displaying individual blog posts. - Created a style.css file for theme metadata and styling. diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..e39be2c Binary files /dev/null and b/.DS_Store differ diff --git a/ots-signs-import.xml b/ots-signs-import.xml new file mode 100644 index 0000000..b8392b0 --- /dev/null +++ b/ots-signs-import.xml @@ -0,0 +1,179 @@ + + + + + OTS Signs + https://ots-signs.com + The Complete Digital Signage Solution + Thu, 19 Feb 2026 00:00:00 +0000 + en-US + 1.2 + https://ots-signs.com + https://ots-signs.com + + + 1 + + + + + + + + + + Home + https://ots-signs.com/ + Thu, 19 Feb 2026 00:00:00 +0000 + + + + 2 + + + + + + + 0 + 0 + + + 0 + + dynamic communication tool for your business.]]> + + + + + + + + + + + + + + + + + + + + + + + + About + https://ots-signs.com/about/ + Thu, 19 Feb 2026 00:00:00 +0000 + + + + 3 + + + + + + + 0 + 1 + + + 0 + + + + + + + + + + + + + + + Services + https://ots-signs.com/services/ + Thu, 19 Feb 2026 00:00:00 +0000 + + + + 4 + + + + + + + 0 + 2 + + + 0 + + + + + + + + + + + + + + + + Contact + https://ots-signs.com/contact/ + Thu, 19 Feb 2026 00:00:00 +0000 + + + + 5 + + + + + + + 0 + 3 + + + 0 + + + + + + + + + + + + + + + diff --git a/ots-signs-setup-plugin.zip b/ots-signs-setup-plugin.zip new file mode 100644 index 0000000..460cae4 Binary files /dev/null and b/ots-signs-setup-plugin.zip differ diff --git a/ots-signs-theme.zip b/ots-signs-theme.zip new file mode 100644 index 0000000..69049bb Binary files /dev/null and b/ots-signs-theme.zip differ diff --git a/wp-content/.DS_Store b/wp-content/.DS_Store new file mode 100644 index 0000000..71ebf76 Binary files /dev/null and b/wp-content/.DS_Store differ diff --git a/wp-content/plugins/ots-signs-setup/ots-signs-setup.php b/wp-content/plugins/ots-signs-setup/ots-signs-setup.php new file mode 100644 index 0000000..0749b36 --- /dev/null +++ b/wp-content/plugins/ots-signs-setup/ots-signs-setup.php @@ -0,0 +1,139 @@ +ID ); + } + + // 4. Permalinks — set to post name + update_option( 'permalink_structure', '/%postname%/' ); + flush_rewrite_rules(); + + // 5. Build the Primary navigation menu + ots_build_primary_menu(); + + // 6. Disable comments site-wide (marketing site) + update_option( 'default_comment_status', 'closed' ); + update_option( 'default_ping_status', 'closed' ); + + // 7. Set timezone + update_option( 'timezone_string', 'America/New_York' ); + + // 8. Mark setup as done + update_option( 'ots_setup_done', '1' ); +} + +function ots_build_primary_menu() { + // Delete existing menu with same name if any + $existing = get_term_by( 'name', 'Primary Menu', 'nav_menu' ); + if ( $existing ) { + wp_delete_nav_menu( $existing->term_id ); + } + + $menu_id = wp_create_nav_menu( 'Primary Menu' ); + if ( is_wp_error( $menu_id ) ) return; + + $menu_pages = [ + [ 'slug' => 'home', 'label' => 'Home' ], + [ 'slug' => 'services', 'label' => 'Services' ], + [ 'slug' => 'about', 'label' => 'About' ], + [ 'slug' => 'contact', 'label' => 'Contact' ], + ]; + + foreach ( $menu_pages as $item ) { + $page = get_page_by_path( $item['slug'] ); + if ( ! $page ) continue; + + wp_update_nav_menu_item( $menu_id, 0, [ + 'menu-item-title' => $item['label'], + 'menu-item-object-id' => $page->ID, + 'menu-item-object' => 'page', + 'menu-item-type' => 'post_type', + 'menu-item-status' => 'publish', + ] ); + } + + // Assign menu to the 'primary' theme location + $locations = get_theme_mod( 'nav_menu_locations', [] ); + $locations['primary'] = $menu_id; + set_theme_mod( 'nav_menu_locations', $locations ); +} + +// ─── Admin notice ───────────────────────────────────────────────────────────── +add_action( 'admin_notices', 'ots_setup_admin_notice' ); + +function ots_setup_admin_notice() { + if ( ! current_user_can( 'manage_options' ) ) return; + + $done = get_option( 'ots_setup_done' ); + ?> +
+ +

✓ OTS Signs Setup Complete!

+

+ Your site has been configured:
+ • Front page set to Home
+ • Primary navigation menu created (Home, Services, About, Contact)
+ • Permalink structure set to /%postname%/
+ • Comments disabled site-wide +

+

+ Go to Plugins to delete this setup plugin +  View Site +

+ +

OTS Signs – Site Setup Required

+

+ The OTS Signs setup plugin is installed but hasn't run yet. + Make sure you have imported ots-signs-import.xml via + Tools → Import → WordPress first, then deactivate and reactivate this plugin. +

+

+ Go to Import +

+ +
+ Re-run Setup'; + array_unshift( $links, $run_link ); + return $links; +} + +add_action( 'admin_init', 'ots_maybe_rerun_setup' ); + +function ots_maybe_rerun_setup() { + if ( ! isset( $_GET['ots_rerun_setup'] ) ) return; + if ( ! current_user_can( 'manage_options' ) ) return; + if ( ! wp_verify_nonce( $_GET['_wpnonce'], 'ots_rerun' ) ) return; + + ots_setup_run(); + + wp_redirect( admin_url( 'plugins.php?ots_setup_reran=1' ) ); + exit; +} diff --git a/wp-content/themes/ots-signs.zip b/wp-content/themes/ots-signs.zip new file mode 100644 index 0000000..f5d3613 Binary files /dev/null and b/wp-content/themes/ots-signs.zip differ diff --git a/wp-content/themes/ots-signs/404.php b/wp-content/themes/ots-signs/404.php new file mode 100644 index 0000000..a0803d0 --- /dev/null +++ b/wp-content/themes/ots-signs/404.php @@ -0,0 +1,23 @@ + + +
+ +
+
+
🔍
+

404

+

Page Not Found

+

The page you're looking for doesn't exist or has been moved.

+ +
+
+ + diff --git a/wp-content/themes/ots-signs/assets/css/main.css b/wp-content/themes/ots-signs/assets/css/main.css new file mode 100644 index 0000000..f7963fd --- /dev/null +++ b/wp-content/themes/ots-signs/assets/css/main.css @@ -0,0 +1,1042 @@ +/* ============================================================= + OTS Signs — Main Stylesheet + ============================================================= */ + +/* ── 1. CSS Variables ───────────────────────────────────────── */ +:root { + --color-primary: #0A74DA; + --color-primary-dk: #0860b8; + --color-primary-lt: #e8f2fd; + --color-accent: #00C6AE; + --color-dark: #0D1321; + --color-dark-2: #1a2236; + --color-text: #2d3748; + --color-text-muted: #718096; + --color-border: #e2e8f0; + --color-bg: #ffffff; + --color-bg-alt: #f7f9fc; + --color-bg-dark: #0D1321; + + --font-sans: 'Inter', system-ui, -apple-system, sans-serif; + + --radius-sm: 6px; + --radius-md: 12px; + --radius-lg: 20px; + --radius-xl: 32px; + + --shadow-sm: 0 1px 3px rgba(0,0,0,.08); + --shadow-md: 0 4px 16px rgba(0,0,0,.10); + --shadow-lg: 0 12px 40px rgba(0,0,0,.14); + + --transition: 0.2s ease; + + --container-max: 1200px; + --container-pad: clamp(1rem, 5vw, 2rem); +} + +/* ── 2. Reset / Base ────────────────────────────────────────── */ +*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } + +html { scroll-behavior: smooth; font-size: 16px; } + +body { + font-family: var(--font-sans); + color: var(--color-text); + background: var(--color-bg); + line-height: 1.65; + -webkit-font-smoothing: antialiased; +} + +img, video { max-width: 100%; display: block; } +a { color: inherit; text-decoration: none; transition: color var(--transition); } +a:hover { color: var(--color-primary); } +ul { list-style: none; } +button { font-family: inherit; cursor: pointer; border: none; background: none; } + +/* ── 3. Layout Utilities ───────────────────────────────────── */ +.container { + width: 100%; + max-width: var(--container-max); + margin-inline: auto; + padding-inline: var(--container-pad); +} + +.section { + padding-block: clamp(4rem, 8vw, 7rem); +} + +.section-alt { background: var(--color-bg-alt); } +.section-dark { + background: var(--color-bg-dark); + color: #fff; +} + +.section-header { + text-align: center; + max-width: 680px; + margin-inline: auto; + margin-bottom: 3.5rem; +} +.section-header .section-label { + display: inline-block; + font-size: .75rem; + font-weight: 700; + letter-spacing: .1em; + text-transform: uppercase; + color: var(--color-primary); + margin-bottom: .75rem; +} +.section-dark .section-header .section-label { color: var(--color-accent); } + +.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: 2rem; } +.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 2rem; } +.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.5rem; } + +@media (max-width: 960px) { + .grid-3 { grid-template-columns: repeat(2, 1fr); } + .grid-4 { grid-template-columns: repeat(2, 1fr); } +} +@media (max-width: 640px) { + .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } +} + +/* ── 4. Typography ─────────────────────────────────────────── */ +h1, h2, h3, h4, h5, h6 { + font-weight: 700; + line-height: 1.2; + color: var(--color-dark); +} +.section-dark h1, +.section-dark h2, +.section-dark h3 { color: #fff; } + +h1 { font-size: clamp(2rem, 5vw, 3.5rem); } +h2 { font-size: clamp(1.6rem, 3.5vw, 2.4rem); } +h3 { font-size: clamp(1.1rem, 2vw, 1.35rem); } +h4 { font-size: 1rem; } + +p { margin-bottom: 1rem; } +p:last-child { margin-bottom: 0; } + +.lead { + font-size: clamp(1rem, 2vw, 1.2rem); + color: var(--color-text-muted); + line-height: 1.75; +} +.section-dark .lead { color: rgba(255,255,255,.75); } + +/* ── 5. Buttons ────────────────────────────────────────────── */ +.btn { + display: inline-flex; + align-items: center; + gap: .5rem; + padding: .8rem 1.75rem; + border-radius: var(--radius-sm); + font-size: .95rem; + font-weight: 600; + transition: all var(--transition); + white-space: nowrap; + cursor: pointer; +} + +.btn-primary { + background: var(--color-primary); + color: #fff; +} +.btn-primary:hover { + background: var(--color-primary-dk); + color: #fff; + transform: translateY(-1px); + box-shadow: 0 6px 20px rgba(10,116,218,.3); +} + +.btn-outline { + border: 2px solid var(--color-primary); + color: var(--color-primary); + background: transparent; +} +.btn-outline:hover { + background: var(--color-primary); + color: #fff; + transform: translateY(-1px); +} + +.btn-ghost { + border: 2px solid rgba(255,255,255,.35); + color: #fff; + background: transparent; +} +.btn-ghost:hover { + background: rgba(255,255,255,.1); + border-color: rgba(255,255,255,.6); + color: #fff; +} + +.btn-sm { padding: .55rem 1.2rem; font-size: .875rem; } +.btn-lg { padding: 1rem 2.25rem; font-size: 1.05rem; } + +.btn-group { + display: flex; + align-items: center; + gap: 1rem; + flex-wrap: wrap; +} + +/* ── 6. Header ─────────────────────────────────────────────── */ +.site-header { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 100; + padding-block: 1rem; + background: transparent; + transition: background var(--transition), box-shadow var(--transition), padding var(--transition); + overflow: hidden; +} +.site-header.scrolled { + background: rgba(255,255,255,.97); + box-shadow: var(--shadow-sm); + padding-block: .65rem; + backdrop-filter: blur(8px); +} + +.site-header.scrolled .logo-text { color: var(--color-dark); } +.site-header.scrolled .nav-menu a { color: var(--color-text); } + +.header-inner { + display: flex; + align-items: center; + gap: 2rem; + height: 50px; + overflow: hidden; + position: relative; +} + +/* Logo */ +.site-logo { + display: flex; + align-items: center; + flex-shrink: 0; + max-height: 40px; + overflow: hidden; +} +.site-logo a { display: flex; align-items: center; } +.custom-logo-link { display: flex; align-items: center; } +.site-logo img, +.custom-logo-link img, +.custom-logo, +.header-inner img { + max-height: 40px !important; + max-width: 180px !important; + width: auto !important; + height: auto !important; + display: block !important; + object-fit: contain; + position: static !important; + float: none !important; +} +/* Ensure SVG logos get explicit dimensions (SVGs without intrinsic size can overflow) */ +.site-logo img[src$=".svg"], +.custom-logo-link img[src$=".svg"], +img.custom-logo[src$=".svg"] { + height: 36px !important; + width: auto !important; +} + +/* Nuclear logo containment — clip-path fallback for overflow edge cases */ +.logo-constraint { + max-height: 40px !important; + max-width: 180px !important; + overflow: hidden !important; + clip-path: inset(0) !important; +} +.site-header .site-logo { + max-height: 40px !important; + overflow: hidden !important; + clip-path: inset(0) !important; +} +.site-header img, +.site-header .custom-logo { + max-height: 40px !important; + max-width: 180px !important; + width: auto !important; + height: auto !important; + object-fit: contain !important; + clip-path: inset(0) !important; +} +.logo-text { + font-size: 1.35rem; + font-weight: 300; + color: #fff; + letter-spacing: -.01em; + transition: color var(--transition); +} +.logo-text strong { font-weight: 800; color: var(--color-accent); } + +/* When header is over light background (services, about, contact pages) */ +.page-header-light .site-header { background: var(--color-bg); box-shadow: var(--shadow-sm); } +.page-header-light .logo-text { color: var(--color-dark); } +.page-header-light .nav-menu a { color: var(--color-text); } + +/* Nav */ +.site-nav { margin-left: auto; } +.nav-menu { + display: flex; + align-items: center; + gap: 2rem; +} +.nav-menu a { + font-size: .9rem; + font-weight: 500; + color: rgba(255,255,255,.85); + position: relative; + padding-bottom: 2px; +} +.nav-menu a::after { + content: ''; + position: absolute; + bottom: -2px; + left: 0; + width: 0; + height: 2px; + background: var(--color-accent); + transition: width var(--transition); +} +.nav-menu a:hover, +.nav-menu .current-menu-item > a { color: #fff; } +.nav-menu a:hover::after, +.nav-menu .current-menu-item > a::after { width: 100%; } + +.site-header.scrolled .nav-menu a { color: var(--color-text-muted); } +.site-header.scrolled .nav-menu a:hover, +.site-header.scrolled .nav-menu .current-menu-item > a { color: var(--color-primary); } + +.header-cta { margin-left: 1rem; } + +/* Mobile toggle */ +.nav-toggle { + display: none; + flex-direction: column; + gap: 5px; + margin-left: auto; + padding: 4px; +} +.nav-toggle span { + display: block; + width: 24px; + height: 2px; + background: #fff; + border-radius: 2px; + transition: all var(--transition); +} +.site-header.scrolled .nav-toggle span { background: var(--color-dark); } +.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); } + +@media (max-width: 768px) { + .nav-toggle { display: flex; } + .site-nav, .header-cta { display: none; } + .site-nav.open { + display: block; + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + background: var(--color-dark); + z-index: 99; + padding: 5rem 2rem 2rem; + } + .site-nav.open .nav-menu { + flex-direction: column; + align-items: flex-start; + gap: 1.5rem; + } + .site-nav.open .nav-menu a { + color: #fff; + font-size: 1.25rem; + } +} + +/* ── 7. Hero ───────────────────────────────────────────────── */ +.hero { + position: relative; + min-height: 100vh; + display: flex; + align-items: center; + overflow: hidden; + background: linear-gradient(135deg, var(--color-dark) 0%, var(--color-dark-2) 50%, #0a2550 100%); + color: #fff; +} + +.hero-bg-grid { + position: absolute; + inset: 0; + background-image: + linear-gradient(rgba(255,255,255,.04) 1px, transparent 1px), + linear-gradient(90deg, rgba(255,255,255,.04) 1px, transparent 1px); + background-size: 60px 60px; + opacity: .6; +} + +.hero-glow { + position: absolute; + width: 700px; + height: 700px; + border-radius: 50%; + background: radial-gradient(circle, rgba(10,116,218,.35) 0%, transparent 70%); + top: -20%; + right: -10%; + pointer-events: none; +} +.hero-glow-2 { + position: absolute; + width: 500px; + height: 500px; + border-radius: 50%; + background: radial-gradient(circle, rgba(0,198,174,.18) 0%, transparent 70%); + bottom: -10%; + left: 5%; + pointer-events: none; +} + +.hero-inner { + position: relative; + z-index: 2; + display: grid; + grid-template-columns: 1fr 1fr; + gap: 3rem; + align-items: center; + padding-block: 8rem 5rem; +} + +.hero-label { + display: inline-flex; + align-items: center; + gap: .5rem; + background: rgba(0,198,174,.15); + border: 1px solid rgba(0,198,174,.35); + color: var(--color-accent); + font-size: .75rem; + font-weight: 700; + letter-spacing: .1em; + text-transform: uppercase; + padding: .4rem 1rem; + border-radius: 100px; + margin-bottom: 1.5rem; +} + +.hero-title { + font-size: clamp(2.2rem, 5vw, 3.8rem); + font-weight: 900; + line-height: 1.1; + color: #fff; + margin-bottom: 1.5rem; +} +.hero-title .highlight { + color: var(--color-accent); +} + +.hero-description { + font-size: 1.1rem; + color: rgba(255,255,255,.75); + max-width: 520px; + margin-bottom: 2.5rem; + line-height: 1.8; +} + +.hero-stats { + display: flex; + gap: 2.5rem; + margin-top: 3rem; + padding-top: 2rem; + border-top: 1px solid rgba(255,255,255,.1); +} +.hero-stat-value { + font-size: 1.75rem; + font-weight: 800; + color: #fff; +} +.hero-stat-label { + font-size: .8rem; + color: rgba(255,255,255,.55); + margin-top: .2rem; +} + +/* Hero visual */ +.hero-visual { + display: flex; + justify-content: center; + align-items: center; +} +.hero-screen-mock { + position: relative; + width: 100%; + max-width: 480px; +} +.hero-screen-outer { + background: #1a2236; + border-radius: var(--radius-lg); + border: 2px solid rgba(255,255,255,.1); + padding: 1.5rem; + box-shadow: var(--shadow-lg), 0 0 0 1px rgba(255,255,255,.05); + position: relative; + overflow: hidden; +} +.hero-screen-bar { + display: flex; + gap: .5rem; + margin-bottom: 1rem; +} +.hero-screen-bar span { + width: 10px; height: 10px; + border-radius: 50%; +} +.hero-screen-bar span:nth-child(1) { background: #ff5f57; } +.hero-screen-bar span:nth-child(2) { background: #febc2e; } +.hero-screen-bar span:nth-child(3) { background: #28c840; } +.hero-screen-content { + background: #0d1321; + border-radius: var(--radius-md); + overflow: hidden; + aspect-ratio: 16/9; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 1rem; + padding: 1.5rem; +} +.hero-screen-logo { + font-size: 1.5rem; + font-weight: 900; + color: #fff; + letter-spacing: -.02em; +} +.hero-screen-logo span { color: var(--color-accent); } +.hero-screen-tagline { + font-size: .7rem; + color: rgba(255,255,255,.5); + letter-spacing: .12em; + text-transform: uppercase; +} +.hero-screen-bars { + display: flex; + gap: .4rem; + align-items: flex-end; + margin-top: .5rem; +} +.hero-screen-bars div { + width: 10px; + border-radius: 3px 3px 0 0; + background: var(--color-primary); + opacity: .8; + animation: bar-grow 1.5s ease-in-out infinite alternate; +} +.hero-screen-bars div:nth-child(1) { height: 20px; animation-delay: 0s; } +.hero-screen-bars div:nth-child(2) { height: 35px; animation-delay: .15s; background: var(--color-accent); } +.hero-screen-bars div:nth-child(3) { height: 25px; animation-delay: .3s; } +.hero-screen-bars div:nth-child(4) { height: 42px; animation-delay: .45s; background: var(--color-accent); } +.hero-screen-bars div:nth-child(5) { height: 18px; animation-delay: .6s; } +.hero-screen-bars div:nth-child(6) { height: 30px; animation-delay: .75s; } + +@keyframes bar-grow { + from { opacity: .5; transform: scaleY(.8); } + to { opacity: 1; transform: scaleY(1); } +} + +/* Floating badges */ +.hero-badge { + position: absolute; + background: rgba(255,255,255,.95); + border-radius: var(--radius-sm); + padding: .5rem .9rem; + box-shadow: var(--shadow-md); + display: flex; + align-items: center; + gap: .5rem; + font-size: .78rem; + font-weight: 600; + color: var(--color-dark); + backdrop-filter: blur(4px); +} +.hero-badge-dot { + width: 8px; height: 8px; + border-radius: 50%; + background: var(--color-accent); + animation: pulse 2s ease-in-out infinite; +} +@keyframes pulse { + 0%, 100% { transform: scale(1); opacity: 1; } + 50% { transform: scale(1.3); opacity: .7; } +} +.hero-badge-1 { bottom: 20px; left: -30px; } +.hero-badge-2 { top: 20px; right: -30px; } + +@media (max-width: 900px) { + .hero-inner { grid-template-columns: 1fr; text-align: center; } + .hero-description { margin-inline: auto; } + .btn-group { justify-content: center; } + .hero-stats { justify-content: center; } + .hero-visual { margin-top: 2rem; } + .hero-badge { display: none; } +} + +/* ── 8. Feature Cards ──────────────────────────────────────── */ +.feature-card { + background: var(--color-bg); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + padding: 2rem; + transition: all var(--transition); +} +.feature-card:hover { + box-shadow: var(--shadow-md); + transform: translateY(-4px); + border-color: var(--color-primary-lt); +} +.feature-icon { + width: 52px; height: 52px; + background: var(--color-primary-lt); + border-radius: var(--radius-sm); + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 1.25rem; + font-size: 1.4rem; +} +.feature-card h3 { margin-bottom: .5rem; font-size: 1.1rem; } +.feature-card p { color: var(--color-text-muted); font-size: .95rem; } + +/* ── 9. Industry Cards ─────────────────────────────────────── */ +.industry-card { + background: var(--color-dark-2); + border: 1px solid rgba(255,255,255,.08); + border-radius: var(--radius-md); + padding: 2rem; + transition: all var(--transition); + cursor: default; +} +.industry-card:hover { + border-color: var(--color-accent); + transform: translateY(-4px); + box-shadow: 0 8px 32px rgba(0,198,174,.15); +} +.industry-card-icon { + font-size: 2rem; + margin-bottom: 1rem; +} +.industry-card h3 { color: #fff; margin-bottom: .5rem; font-size: 1.05rem; } +.industry-card p { color: rgba(255,255,255,.6); font-size: .9rem; } + +/* ── 10. Pricing ───────────────────────────────────────────── */ +.pricing-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 2rem; + max-width: 800px; + margin-inline: auto; +} +.pricing-card { + background: var(--color-bg); + border: 2px solid var(--color-border); + border-radius: var(--radius-lg); + padding: 2.5rem; + position: relative; + transition: all var(--transition); +} +.pricing-card.featured { + border-color: var(--color-primary); + box-shadow: 0 0 0 4px rgba(10,116,218,.1); +} +.pricing-badge { + position: absolute; + top: -14px; + left: 50%; + transform: translateX(-50%); + background: var(--color-primary); + color: #fff; + font-size: .72rem; + font-weight: 700; + letter-spacing: .1em; + text-transform: uppercase; + padding: .35rem 1rem; + border-radius: 100px; +} +.pricing-name { font-size: 1.25rem; font-weight: 700; margin-bottom: .5rem; } +.pricing-price { margin-bottom: 1.5rem; } +.pricing-amount { + font-size: 2.5rem; + font-weight: 900; + color: var(--color-dark); + line-height: 1; +} +.pricing-amount sup { font-size: 1.25rem; vertical-align: super; } +.pricing-per { font-size: .85rem; color: var(--color-text-muted); margin-top: .35rem; } +.pricing-features { + list-style: none; + margin-bottom: 2rem; +} +.pricing-features li { + display: flex; + align-items: flex-start; + gap: .6rem; + font-size: .9rem; + padding: .5rem 0; + border-bottom: 1px solid var(--color-border); + color: var(--color-text); +} +.pricing-features li:last-child { border-bottom: none; } +.pricing-check { color: var(--color-accent); font-size: 1rem; flex-shrink: 0; margin-top: 1px; } + +@media (max-width: 640px) { + .pricing-grid { grid-template-columns: 1fr; } +} + +/* ── 11. Platform Feature Rows ─────────────────────────────── */ +.platform-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; + align-items: center; + padding-block: 2rem; +} +.platform-row + .platform-row { + border-top: 1px solid var(--color-border); + margin-top: 2rem; +} +.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 p { color: var(--color-text-muted); } + +.platform-visual { + background: var(--color-bg-alt); + border-radius: var(--radius-md); + aspect-ratio: 4/3; + display: flex; + align-items: center; + justify-content: center; + font-size: 4rem; + border: 1px solid var(--color-border); +} + +@media (max-width: 768px) { + .platform-row { grid-template-columns: 1fr; } + .platform-row.reverse .platform-text, + .platform-row.reverse .platform-visual { order: unset; } +} + +/* ── 12. Page Hero (inner pages) ───────────────────────────── */ +.page-hero { + background: linear-gradient(135deg, var(--color-dark) 0%, #0a2550 100%); + color: #fff; + padding: 8rem 0 5rem; + text-align: center; + position: relative; + overflow: hidden; +} +.page-hero::before { + content: ''; + position: absolute; + inset: 0; + background-image: + linear-gradient(rgba(255,255,255,.03) 1px, transparent 1px), + linear-gradient(90deg, rgba(255,255,255,.03) 1px, transparent 1px); + background-size: 50px 50px; +} +.page-hero .container { position: relative; z-index: 2; } +.page-hero h1 { color: #fff; margin-bottom: 1rem; } +.page-hero .lead { color: rgba(255,255,255,.7); } + +/* ── 13. Service page cards ────────────────────────────────── */ +.service-industry-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 1.5rem; +} +.service-industry-card { + background: var(--color-bg); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + padding: 2rem; + transition: all var(--transition); +} +.service-industry-card:hover { + box-shadow: var(--shadow-md); + transform: translateY(-4px); + border-color: var(--color-primary); +} +.service-industry-card .industry-icon { + font-size: 2.5rem; + margin-bottom: 1rem; +} +.service-industry-card h3 { margin-bottom: .5rem; } +.service-industry-card p { color: var(--color-text-muted); font-size: .9rem; } + +@media (max-width: 900px) { + .service-industry-grid { grid-template-columns: repeat(2, 1fr); } +} +@media (max-width: 560px) { + .service-industry-grid { grid-template-columns: 1fr; } +} + +/* ── 14. About page ────────────────────────────────────────── */ +.about-intro { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 4rem; + align-items: center; +} +.about-intro-visual { + background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-accent) 100%); + border-radius: var(--radius-xl); + aspect-ratio: 1; + max-width: 420px; + display: flex; + align-items: center; + justify-content: center; + font-size: 6rem; + color: #fff; + box-shadow: var(--shadow-lg); +} +@media (max-width: 768px) { + .about-intro { grid-template-columns: 1fr; } + .about-intro-visual { max-width: 280px; margin-inline: auto; } +} + +.value-card { + text-align: center; + padding: 2rem; +} +.value-icon { + width: 64px; height: 64px; + background: var(--color-primary-lt); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-inline: auto; + margin-bottom: 1.25rem; + font-size: 1.5rem; +} + +/* ── 15. Contact ────────────────────────────────────────────── */ +.contact-layout { + display: grid; + grid-template-columns: 1fr 1.6fr; + gap: 4rem; + align-items: start; +} +@media (max-width: 768px) { + .contact-layout { grid-template-columns: 1fr; } +} + +.contact-info h2 { margin-bottom: 1rem; } +.contact-info .lead { margin-bottom: 2rem; } + +.contact-method { + display: flex; + align-items: flex-start; + gap: 1rem; + margin-bottom: 1.5rem; +} +.contact-method-icon { + width: 44px; height: 44px; + background: var(--color-primary-lt); + border-radius: var(--radius-sm); + display: flex; + align-items: center; + justify-content: center; + font-size: 1.1rem; + flex-shrink: 0; +} +.contact-method-label { font-size: .8rem; color: var(--color-text-muted); } +.contact-method-value { font-weight: 600; color: var(--color-dark); font-size: .95rem; } + +.contact-form-wrap { + background: var(--color-bg); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + padding: 2.5rem; + box-shadow: var(--shadow-sm); +} +.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; } +@media (max-width: 560px) { .form-row { grid-template-columns: 1fr; } } + +.form-group { margin-bottom: 1.25rem; } +.form-group label { + display: block; + font-size: .85rem; + font-weight: 600; + color: var(--color-text); + margin-bottom: .4rem; +} +.form-group label .req { color: var(--color-primary); } + +.form-group input, +.form-group textarea, +.form-group select { + width: 100%; + padding: .75rem 1rem; + font-family: var(--font-sans); + font-size: .95rem; + color: var(--color-text); + background: var(--color-bg-alt); + border: 1.5px solid var(--color-border); + border-radius: var(--radius-sm); + transition: border-color var(--transition), box-shadow var(--transition); + outline: none; + resize: vertical; +} +.form-group input:focus, +.form-group textarea:focus, +.form-group select:focus { + border-color: var(--color-primary); + box-shadow: 0 0 0 3px rgba(10,116,218,.12); + background: #fff; +} +.form-group textarea { min-height: 140px; } + +.form-notice { + margin-top: 1rem; + font-size: .85rem; + padding: .75rem 1rem; + border-radius: var(--radius-sm); + display: none; +} +.form-notice.success { background: #f0fdf4; color: #16a34a; border: 1px solid #bbf7d0; display: block; } +.form-notice.error { background: #fef2f2; color: #dc2626; border: 1px solid #fecaca; display: block; } + +/* ── 16. Demo CTA Banner ───────────────────────────────────── */ +.cta-banner { + background: linear-gradient(135deg, var(--color-primary) 0%, #0556a8 100%); + color: #fff; + padding-block: 5rem; + text-align: center; +} +.cta-banner h2 { color: #fff; margin-bottom: 1rem; } +.cta-banner p { color: rgba(255,255,255,.8); margin-bottom: 2rem; font-size: 1.1rem; } + +/* ── 17. Footer ────────────────────────────────────────────── */ +.site-footer { + background: var(--color-dark); + color: rgba(255,255,255,.7); + padding-top: 4rem; +} +.footer-inner { + display: grid; + grid-template-columns: 1.5fr 1fr 1fr 1fr; + gap: 2rem; + padding-bottom: 3rem; + border-bottom: 1px solid rgba(255,255,255,.08); +} +@media (max-width: 900px) { + .footer-inner { grid-template-columns: 1fr 1fr; } + .footer-brand { grid-column: 1 / -1; } +} +@media (max-width: 480px) { + .footer-inner { grid-template-columns: 1fr; } +} + +.footer-brand .logo-text { font-size: 1.5rem; color: #fff; } +.footer-tagline { font-size: .9rem; color: rgba(255,255,255,.5); margin-top: .75rem; margin-bottom: .5rem; } +.footer-parent { font-size: .8rem; color: rgba(255,255,255,.4); } +.footer-parent a { color: var(--color-accent); } +.footer-parent a:hover { color: #fff; } + +.footer-links { display: contents; } +.footer-col h4 { + font-size: .8rem; + font-weight: 700; + letter-spacing: .1em; + text-transform: uppercase; + color: rgba(255,255,255,.4); + margin-bottom: 1rem; +} +.footer-col ul { display: flex; flex-direction: column; gap: .6rem; } +.footer-col a { + font-size: .875rem; + color: rgba(255,255,255,.6); + transition: color var(--transition); +} +.footer-col a:hover { color: #fff; } + +.footer-bottom { + padding-block: 1.5rem; +} +.footer-bottom p { + font-size: .8rem; + color: rgba(255,255,255,.3); + text-align: center; + margin: 0; +} + +/* ── 18. Misc ───────────────────────────────────────────────── */ +.text-center { text-align: center; } +.text-primary { color: var(--color-primary); } +.text-accent { color: var(--color-accent); } +.mt-1 { margin-top: .5rem; } +.mt-2 { margin-top: 1rem; } +.mt-3 { margin-top: 1.5rem; } +.mt-4 { margin-top: 2rem; } +.mb-4 { margin-bottom: 2rem; } + +/* Demo link pill */ +.demo-link { + display: inline-flex; + align-items: center; + gap: .5rem; + background: rgba(0,198,174,.12); + border: 1px solid rgba(0,198,174,.35); + color: var(--color-accent); + font-size: .85rem; + font-weight: 600; + padding: .45rem 1.1rem; + border-radius: 100px; + transition: all var(--transition); + margin-bottom: 2rem; +} +.demo-link:hover { + background: rgba(0,198,174,.25); + color: var(--color-accent); +} + +/* Divider */ +.divider { + border: none; + border-top: 1px solid var(--color-border); + margin-block: 3rem; +} + +/* Screen reader only */ +.sr-only { + position: absolute; + width: 1px; height: 1px; + margin: -1px; padding: 0; + overflow: hidden; clip: rect(0,0,0,0); + white-space: nowrap; border: 0; +} + +/* Scroll to top */ +.scroll-top { + position: fixed; + bottom: 2rem; + right: 2rem; + width: 44px; height: 44px; + background: var(--color-primary); + color: #fff; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.2rem; + box-shadow: var(--shadow-md); + opacity: 0; + transform: translateY(10px); + transition: all var(--transition); + z-index: 50; + cursor: pointer; +} +.scroll-top.visible { opacity: 1; transform: translateY(0); } +.scroll-top:hover { background: var(--color-primary-dk); color: #fff; } diff --git a/wp-content/themes/ots-signs/assets/js/main.js b/wp-content/themes/ots-signs/assets/js/main.js new file mode 100644 index 0000000..b52e806 --- /dev/null +++ b/wp-content/themes/ots-signs/assets/js/main.js @@ -0,0 +1,142 @@ +/** + * OTS Signs — Main JavaScript + */ + +( function () { + 'use strict'; + + // ── Sticky header ───────────────────────────────────────────────────────── + const header = document.getElementById( 'site-header' ); + if ( header ) { + const onScroll = () => { + header.classList.toggle( 'scrolled', window.scrollY > 40 ); + }; + window.addEventListener( 'scroll', onScroll, { passive: true } ); + onScroll(); // run on init + } + + // ── Mobile nav toggle ────────────────────────────────────────────────────── + const navToggle = document.getElementById( 'nav-toggle' ); + const siteNav = document.getElementById( 'site-nav' ); + if ( navToggle && siteNav ) { + navToggle.addEventListener( 'click', () => { + const isOpen = siteNav.classList.toggle( 'open' ); + navToggle.classList.toggle( 'open', isOpen ); + navToggle.setAttribute( 'aria-expanded', isOpen ); + document.body.style.overflow = isOpen ? 'hidden' : ''; + } ); + + // Close on nav link click + siteNav.querySelectorAll( 'a' ).forEach( link => { + link.addEventListener( 'click', () => { + siteNav.classList.remove( 'open' ); + navToggle.classList.remove( 'open' ); + navToggle.setAttribute( 'aria-expanded', 'false' ); + document.body.style.overflow = ''; + } ); + } ); + } + + // ── Scroll-to-top button ────────────────────────────────────────────────── + const scrollTopBtn = document.querySelector( '.scroll-top' ); + if ( scrollTopBtn ) { + window.addEventListener( 'scroll', () => { + scrollTopBtn.classList.toggle( 'visible', window.scrollY > 400 ); + }, { passive: true } ); + scrollTopBtn.addEventListener( 'click', () => { + window.scrollTo( { top: 0, behavior: 'smooth' } ); + } ); + } + + // ── Contact form AJAX ───────────────────────────────────────────────────── + const contactForm = document.getElementById( 'contact-form' ); + if ( contactForm ) { + const submitBtn = document.getElementById( 'contact-submit' ); + const btnText = submitBtn ? submitBtn.querySelector( '.btn-text' ) : null; + const btnLoading = submitBtn ? submitBtn.querySelector( '.btn-loading' ) : null; + const notice = document.getElementById( 'form-notice' ); + + contactForm.addEventListener( 'submit', async ( e ) => { + e.preventDefault(); + + // Basic client-side validation + const name = contactForm.querySelector( '[name="name"]' ); + const email = contactForm.querySelector( '[name="email"]' ); + const message = contactForm.querySelector( '[name="message"]' ); + + if ( ! name.value.trim() || ! email.value.trim() || ! message.value.trim() ) { + showNotice( 'error', 'Please fill in all required fields.' ); + return; + } + + // Set loading state + if ( submitBtn ) submitBtn.disabled = true; + if ( btnText ) btnText.style.display = 'none'; + if ( btnLoading ) btnLoading.style.display = 'inline'; + if ( notice ) notice.className = 'form-notice'; + + try { + const formData = new FormData( contactForm ); + formData.append( 'action', 'ots_contact' ); + formData.append( 'nonce', otsAjax.nonce ); + + const response = await fetch( otsAjax.url, { + method: 'POST', + body: formData, + credentials: 'same-origin', + } ); + + const data = await response.json(); + + if ( data.success ) { + showNotice( 'success', data.data.message || 'Message sent successfully!' ); + contactForm.reset(); + } else { + showNotice( 'error', data.data.message || 'Something went wrong. Please try again.' ); + } + } catch ( err ) { + showNotice( 'error', 'Network error. Please check your connection and try again.' ); + } finally { + if ( submitBtn ) submitBtn.disabled = false; + if ( btnText ) btnText.style.display = 'inline'; + if ( btnLoading ) btnLoading.style.display = 'none'; + } + } ); + + function showNotice( type, message ) { + if ( ! notice ) return; + notice.textContent = message; + notice.className = 'form-notice ' + type; + notice.style.display = 'block'; + notice.scrollIntoView( { behavior: 'smooth', block: 'nearest' } ); + } + } + + // ── Animate cards on scroll (intersection observer) ────────────────────── + if ( 'IntersectionObserver' in window ) { + const cards = document.querySelectorAll( '.feature-card, .industry-card, .value-card, .service-industry-card, .pricing-card' ); + const style = document.createElement( 'style' ); + style.textContent = ` + .animate-card { opacity: 0; transform: translateY(24px); transition: opacity 0.5s ease, transform 0.5s ease; } + .animate-card.visible { opacity: 1; transform: translateY(0); } + `; + document.head.appendChild( style ); + + cards.forEach( ( card, i ) => { + card.classList.add( 'animate-card' ); + card.style.transitionDelay = ( ( i % 4 ) * 80 ) + 'ms'; + } ); + + const observer = new IntersectionObserver( ( entries ) => { + entries.forEach( entry => { + if ( entry.isIntersecting ) { + entry.target.classList.add( 'visible' ); + observer.unobserve( entry.target ); + } + } ); + }, { threshold: 0.1 } ); + + cards.forEach( card => observer.observe( card ) ); + } + +} )(); diff --git a/wp-content/themes/ots-signs/footer.php b/wp-content/themes/ots-signs/footer.php new file mode 100644 index 0000000..932ff9a --- /dev/null +++ b/wp-content/themes/ots-signs/footer.php @@ -0,0 +1,53 @@ + + + + + + + diff --git a/wp-content/themes/ots-signs/functions.php b/wp-content/themes/ots-signs/functions.php new file mode 100644 index 0000000..e0f1569 --- /dev/null +++ b/wp-content/themes/ots-signs/functions.php @@ -0,0 +1,336 @@ + 60, + 'width' => 200, + 'flex-height' => true, + 'flex-width' => true, + ] ); + + register_nav_menus( [ + 'primary' => __( 'Primary Menu', 'ots-signs' ), + 'footer' => __( 'Footer Menu', 'ots-signs' ), + ] ); +} +add_action( 'after_setup_theme', 'ots_setup' ); + +// ─── Fallback Menu ──────────────────────────────────────────────────────────── +if ( ! function_exists( 'ots_fallback_menu' ) ) { + function ots_fallback_menu() { + echo ''; + } +} + +// ─── Enqueue Assets ─────────────────────────────────────────────────────────── +function ots_enqueue_assets() { + // Google Fonts + wp_enqueue_style( + 'ots-fonts', + 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap', + [], + null + ); + + // Main stylesheet — use filemtime for cache-busting during development + wp_enqueue_style( + 'ots-main', + OTS_THEME_URI . '/assets/css/main.css', + [ 'ots-fonts' ], + filemtime( OTS_THEME_DIR . '/assets/css/main.css' ) + ); + + // Main JS — use filemtime for cache-busting during development + wp_enqueue_script( + 'ots-main', + OTS_THEME_URI . '/assets/js/main.js', + [], + filemtime( OTS_THEME_DIR . '/assets/js/main.js' ), + true + ); + + // Pass ajaxurl and nonce to JS + wp_localize_script( 'ots-main', 'otsAjax', [ + 'url' => admin_url( 'admin-ajax.php' ), + 'nonce' => wp_create_nonce( 'ots_contact_nonce' ), + ] ); +} +add_action( 'wp_enqueue_scripts', 'ots_enqueue_assets' ); + +// ─── Constrain Custom Logo Output ───────────────────────────────────────── +/** + * Force the custom logo image to have inline size constraints and use a + * smaller image size so the browser never paints the full-resolution upload. + */ +add_filter( 'get_custom_logo', 'ots_constrain_custom_logo' ); + +function ots_constrain_custom_logo( $html ) { + if ( empty( $html ) ) { + return $html; + } + + // Swap the full-size src for the 'medium' version (much smaller file) + $custom_logo_id = get_theme_mod( 'custom_logo' ); + if ( $custom_logo_id ) { + $medium = wp_get_attachment_image_url( $custom_logo_id, 'medium' ); + $full = wp_get_attachment_image_url( $custom_logo_id, 'full' ); + if ( $medium && $full && $medium !== $full ) { + $html = str_replace( esc_url( $full ), esc_url( $medium ), $html ); + } + } + + // Inject inline max-height directly on the tag + $html = preg_replace( + '/ 'Please fill in all required fields.' ] ); + } + + if ( ! is_email( $email ) ) { + wp_send_json_error( [ 'message' => 'Please enter a valid email address.' ] ); + } + + $to = get_option( 'admin_email' ); + $subject = 'New Contact Form Submission from ' . $name; + $body = "Name: {$name}\n"; + $body .= "Email: {$email}\n"; + if ( $phone ) $body .= "Phone: {$phone}\n"; + if ( $company ) $body .= "Company: {$company}\n"; + $body .= "\nMessage:\n{$message}"; + + $headers = [ + 'Content-Type: text/plain; charset=UTF-8', + 'Reply-To: ' . $name . ' <' . $email . '>', + ]; + + $sent = wp_mail( $to, $subject, $body, $headers ); + + if ( $sent ) { + wp_send_json_success( [ 'message' => 'Thanks! We\'ll be in touch shortly.' ] ); + } else { + wp_send_json_error( [ 'message' => 'Sorry, there was a problem sending your message. Please try again.' ] ); + } +} +add_action( 'wp_ajax_ots_contact', 'ots_handle_contact' ); +add_action( 'wp_ajax_nopriv_ots_contact', 'ots_handle_contact' ); + +// ─── Custom Page Templates ───────────────────────────────────────────────── +function ots_body_classes( $classes ) { + $classes[] = 'ots-theme'; + return $classes; +} +add_filter( 'body_class', 'ots_body_classes' ); + +// ─── Excerpt length ──────────────────────────────────────────────────────── +function ots_excerpt_length( $length ) { + return 25; +} +add_filter( 'excerpt_length', 'ots_excerpt_length' ); + +// ─── Meta Boxes ──────────────────────────────────────────────────────────── +/** + * Each page template gets its own meta box with the key + * marketing copy fields. All fields fall back to sensible + * defaults so the site works without any editing. + */ +add_action( 'add_meta_boxes', 'ots_register_meta_boxes' ); + +function ots_register_meta_boxes() { + $screens = [ 'page' ]; + + add_meta_box( + 'ots_page_hero', + 'Page Hero Content', + 'ots_render_hero_meta_box', + $screens, + 'normal', + 'high' + ); +} + +function ots_render_hero_meta_box( $post ) { + wp_nonce_field( 'ots_save_meta', 'ots_meta_nonce' ); + + $template = get_page_template_slug( $post->ID ); + $fields = ots_get_meta_fields_for_template( $template ); + + if ( empty( $fields ) ) { + echo '

No editable fields for this page template.

'; + return; + } + + echo ''; + + echo ''; + foreach ( $fields as $field ) { + if ( $field['type'] === 'section' ) { + echo ''; + continue; + } + $value = get_post_meta( $post->ID, $field['key'], true ); + if ( $value === '' ) $value = $field['default'] ?? ''; + echo ''; + echo ''; + echo ''; + } + echo '
' . esc_html( $field['label'] ) . '
'; + if ( $field['type'] === 'textarea' ) { + echo ''; + } else { + echo ''; + } + if ( ! empty( $field['hint'] ) ) { + echo '
' . esc_html( $field['hint'] ) . '
'; + } + echo '
'; + + echo '

+ Fields left blank will use the default content shown above as placeholder text. +

'; +} + +function ots_get_meta_fields_for_template( $template ) { + $templates = [ + 'page-home.php' => [ + [ 'type' => 'section', 'label' => 'Hero Section' ], + [ 'key' => 'hero_title', 'label' => 'Hero Title', 'type' => 'textarea', 'default' => 'Turn any screen into a dynamic communication tool for your business.', 'hint' => 'Wrap a keyword in to colour it teal.' ], + [ 'key' => 'hero_description', 'label' => 'Hero Description', 'type' => 'textarea', 'default' => 'The complete package for engaging digital signage. From eye-catching retail displays to dynamic informational screens, we craft tailored solutions that capture attention and deliver your message.' ], + [ 'key' => 'hero_cta_primary_label', 'label' => 'Primary CTA Label', 'type' => 'text', 'default' => 'Get in Touch' ], + [ 'key' => 'hero_cta_secondary_label', 'label' => 'Secondary CTA Label', 'type' => 'text', 'default' => 'View Demo' ], + [ 'type' => 'section', 'label' => 'Hero Stats' ], + [ 'key' => 'stat_1_value', 'label' => 'Stat 1 Value', 'type' => 'text', 'default' => '500+' ], + [ 'key' => 'stat_1_label', 'label' => 'Stat 1 Label', 'type' => 'text', 'default' => 'Screens Managed' ], + [ 'key' => 'stat_2_value', 'label' => 'Stat 2 Value', 'type' => 'text', 'default' => '6' ], + [ 'key' => 'stat_2_label', 'label' => 'Stat 2 Label', 'type' => 'text', 'default' => 'Industry Verticals' ], + [ 'key' => 'stat_3_value', 'label' => 'Stat 3 Value', 'type' => 'text', 'default' => '99.9%' ], + [ 'key' => 'stat_3_label', 'label' => 'Stat 3 Label', 'type' => 'text', 'default' => 'Uptime SLA' ], + [ 'type' => 'section', 'label' => 'Features Section' ], + [ 'key' => 'features_heading', 'label' => 'Features Heading', 'type' => 'text', 'default' => 'Everything You Need for Engaging Digital Signage' ], + [ 'key' => 'features_subheading', 'label' => 'Features Subheading', 'type' => 'textarea', 'default' => 'We provide a complete, end-to-end digital signage solution — from hardware to content management to live data integrations.' ], + [ 'type' => 'section', 'label' => 'Industries Section' ], + [ 'key' => 'industries_heading', 'label' => 'Industries Heading', 'type' => 'text', 'default' => 'Our Services Empower Your Brand' ], + [ 'key' => 'industries_subheading', 'label' => 'Industries Subheading', 'type' => 'textarea', 'default' => 'Modern businesses need real-time communication. Digital signage helps you connect with your audience where it matters most.' ], + [ 'type' => 'section', 'label' => 'Pricing Section' ], + [ 'key' => 'pricing_heading', 'label' => 'Pricing Heading', 'type' => 'text', 'default' => 'Affordable Solutions, Scalable Options' ], + [ 'key' => 'pricing_subheading', 'label' => 'Pricing Subheading', 'type' => 'textarea', 'default' => 'All plans include content scheduling/day-parting, live data integrations, unlimited user seats, and the ability to publish content to your signs in minutes.' ], + [ 'type' => 'section', 'label' => 'CTA Banner' ], + [ 'key' => 'cta_heading', 'label' => 'CTA Heading', 'type' => 'text', 'default' => 'Ready to Transform Your Screens?' ], + [ 'key' => 'cta_description', 'label' => 'CTA Description', 'type' => 'textarea', 'default' => 'Get in touch today and discover how OTS Signs can revolutionize the way you communicate with your audience.' ], + ], + 'page-about.php' => [ + [ 'type' => 'section', 'label' => 'Page Hero' ], + [ 'key' => 'hero_title', 'label' => 'Page Title', 'type' => 'text', 'default' => 'The Complete Digital Signage Solution' ], + [ 'key' => 'hero_description', 'label' => 'Page Subtitle', 'type' => 'textarea', 'default' => 'We help businesses of all sizes connect with their audiences through powerful, easy-to-manage digital signage.' ], + [ 'type' => 'section', 'label' => 'Who We Are Section' ], + [ 'key' => 'about_heading', 'label' => 'Section Heading', 'type' => 'text', 'default' => 'Bringing Your Message to Life on Every Screen' ], + [ 'key' => 'about_intro', 'label' => 'Intro Paragraph', 'type' => 'textarea', 'default' => 'OTS Signs is a digital signage company dedicated to transforming how businesses communicate with their audiences. We offer a complete, end-to-end solution — from the hardware players to our cloud management platform to custom content creation.' ], + [ 'key' => 'about_body', 'label' => 'Body Paragraph', 'type' => 'textarea', 'default' => 'Digital signage is the modern way to connect with your audience, transforming any screen into a way to communicate and engage. Whether you need an eye-catching retail display, a dynamic menu board, or enterprise-grade live data dashboards, we deliver solutions that capture attention and get results.' ], + [ 'type' => 'section', 'label' => 'CTA Banner' ], + [ 'key' => 'cta_heading', 'label' => 'CTA Heading', 'type' => 'text', 'default' => "Let's Build Something Great Together" ], + [ 'key' => 'cta_description', 'label' => 'CTA Description', 'type' => 'textarea', 'default' => 'Ready to see what OTS Signs can do for your business? Get in touch or explore our demo platform.' ], + ], + 'page-services.php' => [ + [ 'type' => 'section', 'label' => 'Page Hero' ], + [ 'key' => 'hero_title', 'label' => 'Page Title', 'type' => 'text', 'default' => 'Services & Solutions' ], + [ 'key' => 'hero_description', 'label' => 'Page Subtitle', 'type' => 'textarea', 'default' => 'From content creation to cloud management to live data integrations — we deliver everything you need for a complete digital signage deployment.' ], + [ 'type' => 'section', 'label' => 'Core Services Section' ], + [ 'key' => 'services_heading', 'label' => 'Section Heading', 'type' => 'text', 'default' => 'The Complete Package for Engaging Digital Signage' ], + [ 'key' => 'services_subheading', 'label' => 'Section Subheading', 'type' => 'textarea', 'default' => 'Every service we offer is designed to make your digital signage as impactful, reliable, and easy to manage as possible.' ], + [ 'type' => 'section', 'label' => 'Pricing Section' ], + [ 'key' => 'pricing_heading', 'label' => 'Pricing Heading', 'type' => 'text', 'default' => 'Affordable Solutions, Scalable Options' ], + [ 'key' => 'pricing_subheading', 'label' => 'Pricing Subheading', 'type' => 'textarea', 'default' => 'Simple, transparent pricing with no hidden fees. Choose the plan that fits your business size.' ], + [ 'type' => 'section', 'label' => 'CTA Banner' ], + [ 'key' => 'cta_heading', 'label' => 'CTA Heading', 'type' => 'text', 'default' => 'Want to See How Our Platform Works?' ], + [ 'key' => 'cta_description', 'label' => 'CTA Description', 'type' => 'textarea', 'default' => 'Request access to our demo instance and explore the OTS Signs platform hands-on.' ], + ], + 'page-contact.php' => [ + [ 'type' => 'section', 'label' => 'Page Hero' ], + [ 'key' => 'hero_title', 'label' => 'Page Title', 'type' => 'text', 'default' => 'Get in Touch' ], + [ 'key' => 'hero_description', 'label' => 'Page Subtitle', 'type' => 'textarea', 'default' => "Ready to transform your screens? We'd love to hear about your project. Drop us a message and we'll get back to you promptly." ], + [ 'type' => 'section', 'label' => 'Contact Info' ], + [ 'key' => 'contact_heading', 'label' => 'Section Heading', 'type' => 'text', 'default' => "Let's Start the Conversation" ], + [ 'key' => 'contact_subheading', 'label' => 'Section Subheading', 'type' => 'textarea', 'default' => "Whether you're looking for a quote, want to see a demo, or just have questions — we're here to help." ], + [ 'key' => 'contact_email', 'label' => 'Contact Email', 'type' => 'text', 'default' => 'info@ots-signs.com' ], + [ 'type' => 'section', 'label' => 'Demo CTA Section' ], + [ 'key' => 'demo_heading', 'label' => 'Demo Heading', 'type' => 'text', 'default' => 'Explore Our Demo Platform' ], + [ 'key' => 'demo_description', 'label' => 'Demo Description', 'type' => 'textarea', 'default' => 'See the OTS Signs CMS in action. Request access to our live demo instance and experience the platform for yourself before making any commitment.' ], + ], + ]; + + return $templates[ $template ] ?? []; +} + +// Save meta fields +add_action( 'save_post_page', 'ots_save_meta_fields' ); + +function ots_save_meta_fields( $post_id ) { + if ( ! isset( $_POST['ots_meta_nonce'] ) ) return; + if ( ! wp_verify_nonce( $_POST['ots_meta_nonce'], 'ots_save_meta' ) ) return; + if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) return; + if ( ! current_user_can( 'edit_page', $post_id ) ) return; + + if ( ! isset( $_POST['ots_meta'] ) || ! is_array( $_POST['ots_meta'] ) ) return; + + foreach ( $_POST['ots_meta'] as $key => $value ) { + $key = sanitize_key( $key ); + // Allow basic HTML (spans, br, strong, em) for rich text fields + $allowed = array_merge( wp_kses_allowed_html( 'post' ), [] ); + update_post_meta( $post_id, $key, wp_kses_post( $value ) ); + } +} + +/** + * Helper: get a meta value with a default fallback. + */ +function ots_meta( $post_id, $key, $default = '' ) { + $value = get_post_meta( $post_id, $key, true ); + return ( $value !== '' && $value !== false ) ? $value : $default; +} diff --git a/wp-content/themes/ots-signs/header.php b/wp-content/themes/ots-signs/header.php new file mode 100644 index 0000000..ddfba57 --- /dev/null +++ b/wp-content/themes/ots-signs/header.php @@ -0,0 +1,47 @@ + +> + + + + + + +> + + + diff --git a/wp-content/themes/ots-signs/index.php b/wp-content/themes/ots-signs/index.php new file mode 100644 index 0000000..ad1e680 --- /dev/null +++ b/wp-content/themes/ots-signs/index.php @@ -0,0 +1,45 @@ + + +
+ +
+
+

+
+
+ +
+
+ +
+ + + +
+
+ +
+ +

No posts found.

+ +
+
+ + diff --git a/wp-content/themes/ots-signs/page-about.php b/wp-content/themes/ots-signs/page-about.php new file mode 100644 index 0000000..2ab67d7 --- /dev/null +++ b/wp-content/themes/ots-signs/page-about.php @@ -0,0 +1,149 @@ + + +
+ + +
+
+ + +

+

+
+
+ + +
+
+
+
📷
+
+ + +

+

+

+

OTS Signs is a service by Oribi Technology Services, backed by a team of technology professionals passionate about modern communication.

+
+
+
+
+ + +
+
+
+ +

What Sets Us Apart

+

We don't just sell screens — we deliver a complete communication platform built for reliability, ease of use, and real business results.

+
+ +
+
+
+

Easy to Use

+

Our cloud CMS is designed for any skill level. Publish content to your signs in minutes, no technical expertise required.

+
+
+
📈
+

Scalable Platform

+

Whether you manage one screen or 500+, our platform scales effortlessly with your business without sacrificing performance.

+
+
+
🔒
+

Enterprise Security

+

Enterprise-grade security safeguards your content and network with rock-solid reliability and uptime you can count on.

+
+
+
🌐
+

Always Connected

+

Our intelligent player devices keep your content playing even without internet — so your message never goes dark.

+
+
+
🌟
+

Professional Content

+

We offer professional photography and video production to ensure your signage looks polished, engaging, and on-brand.

+
+
+
🤝
+

Ongoing Support

+

We're your digital signage partner — not just a vendor. We're here to help you get the most out of your investment.

+
+
+
+
+ + +
+
+
+ +

Cloud-Native Infrastructure Built for Performance

+

Our application platform lets you publish content instantly across any network, with built-in real-time analytics and a secure, cloud-native architecture.

+
+ +
+
+
💻
+

Cloud CMS

+

Manage all your screens from a single, intuitive dashboard. Schedule content, monitor displays, and push updates in real time from anywhere in the world.

+
+
+
📊
+

Real-Time Analytics

+

Track what's playing, when, and on which screens. Get actionable insights to optimize your content strategy and maximize engagement.

+
+
+
🔌
+

Flexible Hardware

+

Our players work with any HDMI screen, making integration into your existing setup seamless. Or get a complete bundled solution including a commercial display.

+
+
+
+

Live Data Pipelines

+

Connect your web dashboards, APIs, or data feeds to display live metrics, prices, news, or any dynamic content automatically on your screens.

+
+
+
+
+ + +
+
+ +

+

+ +
+
+ + diff --git a/wp-content/themes/ots-signs/page-contact.php b/wp-content/themes/ots-signs/page-contact.php new file mode 100644 index 0000000..d5a32f1 --- /dev/null +++ b/wp-content/themes/ots-signs/page-contact.php @@ -0,0 +1,163 @@ + + +
+ + +
+
+ + +

+

+
+
+ + +
+
+
+ + +
+ + +

+

+ +
+
💌
+
+
Email
+
+
+
+ +
+
🌐
+
+
Demo Platform
+ +
+
+ +
+
🏠
+
+
Company
+ +
+
+ +
+ +

What to Expect

+
    +
  • + 01 + We'll review your message and respond within one business day. +
  • +
  • + 02 + We'll schedule a quick discovery call to understand your needs. +
  • +
  • + 03 + We'll prepare a tailored proposal and walk you through the platform. +
  • +
+
+ + +
+

Send Us a Message

+ +
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ + +
+ + + + +
+
+ +
+
+
+ + +
+
+ + +

+

+ Request Demo Access → +
+
+ + diff --git a/wp-content/themes/ots-signs/page-home.php b/wp-content/themes/ots-signs/page-home.php new file mode 100644 index 0000000..5f402af --- /dev/null +++ b/wp-content/themes/ots-signs/page-home.php @@ -0,0 +1,294 @@ + + + +
+
+
+
+
+ + dynamic communication tool for your business.' ); + $hero_desc = ots_meta( $id, 'hero_description', 'The complete package for engaging digital signage. From eye-catching retail displays to dynamic informational screens, we craft tailored solutions that capture attention and deliver your message.' ); + $cta_primary = ots_meta( $id, 'hero_cta_primary_label', 'Get in Touch' ); + $cta_secondary = ots_meta( $id, 'hero_cta_secondary_label', 'View Demo' ); + $stat_1_val = ots_meta( $id, 'stat_1_value', '500+' ); + $stat_1_lbl = ots_meta( $id, 'stat_1_label', 'Screens Managed' ); + $stat_2_val = ots_meta( $id, 'stat_2_value', '6' ); + $stat_2_lbl = ots_meta( $id, 'stat_2_label', 'Industry Verticals' ); + $stat_3_val = ots_meta( $id, 'stat_3_value', '99.9%' ); + $stat_3_lbl = ots_meta( $id, 'stat_3_label', 'Uptime SLA' ); + ?> +
+ ● Digital Signage Solutions +

+

+
+ + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+ Live content publishing +
+
+
+ +
+
+ +
Live Data Dashboard
+
+
+
+
+
+
+ Offline playback enabled +
+
+
+ +
+
+ + +
+
+ +
+ +

+

+
+ +
+
+
📷
+

Showcase Your Products

+

Professional photography and video production services to showcase your products and services with high-quality, polished visuals.

+
+
+
📊
+

Live Data Integrations

+

Integrate your existing web dashboards and real-time data with our platform to display up-to-the-minute metrics and insights.

+
+
+
🍽
+

Digital Menu Boards

+

Make your menu as appealing as your product. We incorporate your branding with high-quality photography to showcase your offerings.

+
+
+
🔋
+

Offline Playback

+

Our intelligent player devices keep your message on screen even when the internet isn't available — so communication never stops.

+
+
+
+
+ + +
+
+ +
+ +

+

+
+ +
+
+
🏦
+

Hospitality

+

Showcase menus, promotions, and special events while guiding guests through lobbies, restaurants, and bars.

+
+
+
🛍
+

Retail

+

Drive product upsells, announce flash-sales, and offer in-store navigation with fresh, eye-catching displays.

+
+
+
🌃
+

Outdoor Marketplace

+

Whether it's a weekly farmers market or seasonal fair, digital signage adds a modern, professional touch without losing the charm.

+
+
+
🏢
+

Corporate Office

+

Enhance your meeting experience with Teams meeting rooms. Communicate schedules, company news, and employee alerts.

+
+
+
🔴
+

Live Data Displays

+

Connect existing web dashboards and real-time data to our platform. Bring key metrics and insights to life in real-time.

+
+
+
🏫
+

Education

+

Broadcast class schedules, announcements, and interactive learning content in campuses and auditoriums.

+
+
+
+
+ + +
+
+
+ +

Technology Designed to Work for You

+

Our application platform lets you publish content instantly across any network, with built-in real-time analytics and a secure, cloud-native architecture.

+
+ +
+
+
+

Manage All Your Screens in One Place

+

Our cloud CMS offers the power and flexibility to manage digital displays for businesses of all sizes. Whether you're a single location or a multi-site enterprise, our digital signage network is built to grow with you.

+
+
💻
+
+ +
+
+

Dynamic Content Scheduling

+

Effortlessly schedule your content for maximum impact. Plan ahead by setting content to play at specific times, daypart, or trigger based on real-world events. Automate your messaging to ensure the right information is displayed at the right time.

+
+
📅
+
+ +
+
+

Use Existing Screens

+

Our digital signage players work on any screen with HDMI, so you can easily integrate them into your current setup. We also offer bundled packages that include both the player and a high-quality commercial-grade display.

+
+
📺
+
+ +
+
+

Secure & Reliable

+

Enterprise-grade security safeguards your content and network, while our advanced hosting platform guarantees rock-solid reliability. Our intelligent player devices continue playing content even when offline, so your communication never misses a beat.

+
+
🔒
+
+
+
+
+ + +
+
+ +
+ +

+

+
+ +
+
+
Essentials
+
+
$7
+
per screen / month — or $70 / screen annually
+
+
    +
  • Up to 20 screens
  • +
  • Content scheduling & day-parting
  • +
  • Live data integrations
  • +
  • Unlimited user seats
  • +
  • Custom subdomain
  • +
  • Shared CMS server
  • +
+ Get Started +
+ + +
+ +

+ Want to see the platform in action? + Request access to our demo instance → +

+
+
+ + +
+
+ +

+

+ +
+
+ + diff --git a/wp-content/themes/ots-signs/page-services.php b/wp-content/themes/ots-signs/page-services.php new file mode 100644 index 0000000..921addf --- /dev/null +++ b/wp-content/themes/ots-signs/page-services.php @@ -0,0 +1,264 @@ + + +
+ + +
+
+ + +

+

+
+
+ + +
+
+ +
+ +

+

+
+ +
+
+
+ +

Showcase Your Products

+

High-quality visuals make all the difference. We offer professional photography and video production services to showcase your products, services, or environment. We ensure your content looks polished and engaging — because great signage starts with great content.

+ Get a Quote +
+
📷
+
+ +
+
+ +

Live Data Integrations

+

Integrate your existing web dashboards and real-time data with our platform to bring your most important information to life on digital signage. We make it easy to display up-to-the-minute data and insights — stock prices, KPIs, queue lengths, weather, and more.

+ Learn More +
+
📊
+
+ +
+
+ +

Digital Menu Boards

+

Make your menu as appealing as your product. We incorporate your branding and include high-quality photography to showcase your product in the most enticing way possible. Update menu items, prices, and specials instantly from our cloud CMS — no reprinting required.

+ See Examples +
+
🍽
+
+ +
+
+ +

Offline Playback Support

+

Our intelligent player devices are engineered to keep your message on screen, even when the internet isn't available. So you can rely on them in locations where connecting to the internet is difficult or impossible. Content keeps playing, always.

+ View Devices +
+
🔋
+
+
+
+
+ + +
+
+
+ +

Solutions for Every Industry

+

We understand that every industry has different communication needs. Our platform is flexible enough to serve them all.

+
+ +
+
+
🏦
+

Hospitality

+

Showcase menus, promotions, and special events while guiding guests through lobbies, restaurants, and bars. Create a memorable first impression with polished, dynamic signage throughout your venue.

+
+ +
+
🛍
+

Retail

+

Drive product upsells, announce flash-sales, and offer in-store navigation with fresh, eye-catching displays. Update content instantly in response to stock levels, seasonal campaigns, or breaking promotions.

+
+ +
+
🌃
+

Outdoor Marketplace

+

Whether it's a weekly farmers market or seasonal fairs, digital signage adds a modern, professional touch without losing the charm of the market experience. Perfect for weather-resilient, portable signage solutions.

+
+ +
+
🏢
+

Corporate Office

+

Enhance your meeting experience with Teams-integrated meeting room displays. Communicate meeting schedules, company news, and employee alerts with live dashboards and internal communication screens.

+
+ +
+
🔴
+

Live Data Displays

+

Connect your existing web dashboards and real-time data to our platform for customized digital signage. We bring your key metrics and insights to life in real-time — ideal for trading floors, operations centres, and control rooms.

+
+ +
+
🏫
+

Education

+

Broadcast class schedules, announcements, and interactive learning content in campuses and auditoriums. Keep students and staff informed with centrally managed screens across your entire campus.

+
+
+
+
+ + +
+
+
+ +

Built-In Features Included with Every Plan

+

Every OTS Signs subscription comes loaded with powerful features to help you manage, schedule, and optimize your content.

+
+ +
+
+
📅
+

Content Scheduling

+

Schedule content to play at specific times, or daypart your messaging to show the right content at the right time of day.

+
+
+
📊
+

Live Data Feeds

+

Connect any web-accessible data source to your signage for automated, real-time content updates without manual intervention.

+
+
+
👥
+

Unlimited Users

+

Invite your whole team. Every plan includes unlimited user seats so everyone who needs access can contribute.

+
+
+
+

Instant Publishing

+

Publish content to your signs in minutes. No complex deployments, no waiting — changes appear on screen immediately.

+
+
+
🔋
+

Offline Playback

+

Intelligent caching means your screens never go dark, even during internet outages or in connectivity-limited locations.

+
+
+
🔒
+

Enterprise Security

+

Role-based access control, encrypted connections, and enterprise-grade infrastructure keep your content and data safe.

+
+
+
💻
+

Cloud Management

+

Manage your entire network of screens from any browser on any device — no software installation required.

+
+
+
📺
+

Any Screen

+

Works with any HDMI-compatible display. Transition existing screens into smart signage with our compact player devices.

+
+
+
+
+ + +
+
+ +
+ +

+

+
+ +
+
+
Essentials
+
+
$7
+
per screen / month — or $70 / screen annually
+
+
    +
  • Up to 20 screens
  • +
  • Content scheduling & day-parting
  • +
  • Live data integrations
  • +
  • Unlimited user seats
  • +
  • Custom subdomain
  • +
  • Shared CMS server
  • +
+ Get Started +
+ + +
+
+
+ + +
+ +
+ + diff --git a/wp-content/themes/ots-signs/page.php b/wp-content/themes/ots-signs/page.php new file mode 100644 index 0000000..4fb7782 --- /dev/null +++ b/wp-content/themes/ots-signs/page.php @@ -0,0 +1,34 @@ + + +
+ +
+
+

+
+
+ +
+
+ +
+ +
+ +
+
+ + diff --git a/wp-content/themes/ots-signs/single.php b/wp-content/themes/ots-signs/single.php new file mode 100644 index 0000000..01eb8fb --- /dev/null +++ b/wp-content/themes/ots-signs/single.php @@ -0,0 +1,37 @@ + + +
+ +
+
+

+

+
+
+ +
+
+
+
+ +
+
+
+ ← Back to Home +
+
+
+ + diff --git a/wp-content/themes/ots-signs/style.css b/wp-content/themes/ots-signs/style.css new file mode 100644 index 0000000..55ac783 --- /dev/null +++ b/wp-content/themes/ots-signs/style.css @@ -0,0 +1,12 @@ +/* + Theme Name: OTS Signs + Theme URI: https://ots-signs.com + Author: OTS Signs + Author URI: https://ots-signs.com + Description: A clean, modern marketing theme for OTS Signs digital signage company. + Version: 1.0.0 + License: GNU General Public License v2 or later + License URI: http://www.gnu.org/licenses/gpl-2.0.html + Text Domain: ots-signs + Tags: digital-signage, marketing, modern, business +*/