Changeset 357886
- Timestamp:
- 11/07/2026 12:35:45 PM (less than one hour ago)
- Location:
- nexversal/3.5.9
- Files:
-
- 9 added
- 21 edited
- 1 copied
-
. (copied) (copied from nexversal/2.5.2 )
-
404.php (modified) (1 diff)
-
archive.php (modified) (2 diffs)
-
assets/css/editor-style.css (modified) (1 diff)
-
assets/js/customize-preview.js (added)
-
assets/js/main.js (modified) (1 diff)
-
author.php (modified) (2 diffs)
-
comments.php (modified) (1 diff)
-
footer.php (modified) (4 diffs)
-
front-page.php (added)
-
functions.php (modified) (15 diffs)
-
header.php (modified) (4 diffs)
-
home.php (added)
-
inc/ad-cls-optimizer.php (added)
-
inc/ad-manager.php (modified) (35 diffs)
-
inc/cookie-consent.php (added)
-
index.php (modified) (3 diffs)
-
page.php (modified) (4 diffs)
-
readme.txt (modified) (4 diffs)
-
search.php (modified) (2 diffs)
-
sidebar.php (modified) (1 diff)
-
single.php (modified) (4 diffs)
-
style.css (modified) (22 diffs)
-
template-about.php (added)
-
template-blog.php (added)
-
template-contact.php (added)
-
template-full-width.php (modified) (2 diffs)
-
template-home.php (added)
-
template-parts/content-card.php (modified) (3 diffs)
-
template-parts/newsletter-box.php (modified) (1 diff)
-
template-parts/social-share.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
nexversal/3.5.9/404.php
r357433 r357886 1 1 <?php 2 2 /** 3 * Security: Prevent direct script access 3 * 404 template (Page Not Found - Enhanced Traffic Retention) for Nexversal Theme 4 * 5 * @package Nexversal 6 * @author Nexes Agency & Muhammad Shahid 7 * @copyright 2026 Nexes Agency & Muhammad Shahid 8 * @license GNU General Public License v2 or later 9 * @link https://nexversal.com 4 10 */ 11 5 12 if (!defined('ABSPATH')) { 6 13 exit; 7 14 } 8 15 9 /** 10 * 404 template (Page Not Found - Enhanced Traffic Retention) 11 * 12 * @package Nexversal 13 */ 16 // Failsafe auto-redirect: If visitor hits ?page_id=2 or legacy sample/about URLs, 301 redirect immediately to About Us 17 $requested_page_id = isset($_GET['page_id']) ? (int) $_GET['page_id'] : 0; 18 $req_uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; 19 if ($requested_page_id === 2 || strpos($req_uri, 'page_id=2') !== false || strpos($req_uri, 'sample-page') !== false || strpos($req_uri, 'aboutAbout') !== false) { 20 $dest = function_exists('nexversal_get_about_page_url') ? nexversal_get_about_page_url() : home_url('/about/'); 21 wp_safe_redirect($dest, 301); 22 exit; 23 } 24 25 // Master 301 Redirect Record evaluation 26 $clean_path = parse_url($req_uri, PHP_URL_PATH); 27 $clean_path = $clean_path ? rtrim($clean_path, '/') : ''; 28 if (!empty($clean_path)) { 29 $blog_url = function_exists('nexversal_get_blogs_page_url') ? nexversal_get_blogs_page_url() : home_url('/blog/'); 30 $redirects = array( 31 '/aboutAbout' => function_exists('nexversal_get_about_page_url') ? nexversal_get_about_page_url() : home_url('/about/'), 32 '/contactContact' => function_exists('nexversal_get_contact_page_url') ? nexversal_get_contact_page_url() : home_url('/contact/'), 33 '/Home' => home_url('/'), 34 '/category/comparisonsComparisons' => home_url('/category/comparisons/'), 35 '/sample-page' => function_exists('nexversal_get_about_page_url') ? nexversal_get_about_page_url() : home_url('/about/'), 36 '/blogs' => $blog_url, 37 '/kepler-vs-github-copilot' => home_url('/blog/kepler-ai-coding'), 38 '/gitkraken-kepler' => home_url('/blog/setup-gitkraken-kepler'), 39 '/seo-in-the-age-of-answer-engines' => home_url('/blog/best-ai-seo-tools'), 40 '/search-marketing-course' => home_url('/blog/best-ai-seo-tools'), 41 '/category/artificial-intelligence' => home_url('/blog'), 42 '/future-of-artificial-intelligence-2026' => home_url('/blog'), 43 '/the-self-taught-engineers-curriculum' => home_url('/blog/best-free-ai-tools-for-coding-2026'), 44 '/the-anatomy-of-a-supply-chain-attack' => home_url('/blog'), 45 '/the-quiet-revolution-of-agentic-llms' => home_url('/blog/chatgpt-vs-claude-vs-gemini-which-should-you-use'), 46 '/designing-with-restraint' => home_url('/blog'), 47 '/best-ai-video-generators-for-creators-in-2026' => home_url('/blog/ai-tools-for-a-video-generator'), 48 '/category/education' => home_url('/blog/best-ai-tools-for-students'), 49 '/why-pricing-is-strategy-not-a-number' => home_url('/blog/ai-tools-for-small-business'), 50 '/how-to-learn-seo-at-home-for-free-complete-2026-roadmap' => home_url('/blog/best-ai-seo-tools'), 51 '/key-takeaways-for-mastering-rankings' => home_url('/blog/best-ai-seo-tools'), 52 '/the-best-chatgpt-alternatives-worth-trying' => home_url('/blog/chatgpt-alternatives'), 53 '/best-ai-tools-for-text-to-video-generators' => home_url('/blog/ai-tools-for-a-video-generator'), 54 '/ai-tools-for-text-to-video-generators' => home_url('/blog/ai-tools-for-a-video-generator'), 55 ); 56 57 if (isset($redirects[$clean_path])) { 58 wp_safe_redirect($redirects[$clean_path], 301); 59 exit; 60 } 61 } 62 63 // Failsafe virtual ads.txt interceptor if server routed request to 404 64 if (preg_match('#^/ads\.txt(\?.*)?$#i', $req_uri)) { 65 if (function_exists('nexversal_render_virtual_ads_txt_output')) { 66 nexversal_render_virtual_ads_txt_output(); 67 } 68 } 14 69 15 70 get_header(); -
nexversal/3.5.9/archive.php
r357433 r357886 1 1 <?php 2 2 /** 3 * Security: Prevent direct script access 3 * Archive template (Categories, Tags, Authors, Dates) for Nexversal Theme 4 * 5 * @package Nexversal 6 * @author Nexes Agency & Muhammad Shahid 7 * @copyright 2026 Nexes Agency & Muhammad Shahid 8 * @license GNU General Public License v2 or later 9 * @link https://nexversal.com 4 10 */ 11 5 12 if (!defined('ABSPATH')) { 6 13 exit; 7 14 } 8 9 /**10 * Archive template (Categories, Tags, Authors, Dates)11 *12 * @package Nexversal13 */14 15 15 16 get_header(); … … 52 53 </div> 53 54 54 <?php get_sidebar(); ?> 55 <?php 56 if (!function_exists('nexversal_is_meta_disabled') || !nexversal_is_meta_disabled('disable_sidebar')) { 57 get_sidebar(); 58 } 59 ?> 55 60 </div> 56 61 </main> -
nexversal/3.5.9/assets/css/editor-style.css
r357433 r357886 177 177 } 178 178 179 /* ========================================================================== 180 Hero Banner Block Styles inside Gutenberg Block Editor 181 ========================================================================== */ 182 .nx-hero-section { 183 padding: 36px 20px; 184 background: #f8fafc; 185 border: 1px solid #e2e8f0; 186 border-radius: 12px; 187 margin-bottom: 30px; 188 } 189 190 .nx-hero-badge { 191 display: inline-block; 192 font-size: 0.75rem; 193 font-weight: 700; 194 letter-spacing: 0.08em; 195 text-transform: uppercase; 196 color: #4f46e5; 197 background: #eef2ff; 198 padding: 5px 14px; 199 border-radius: 20px; 200 margin-bottom: 14px; 201 } 202 203 .nx-hero-title { 204 font-size: 2.2rem; 205 font-weight: 900; 206 line-height: 1.2; 207 letter-spacing: -0.03em; 208 color: #0f172a; 209 margin-bottom: 14px; 210 } 211 212 .nx-hero-description { 213 font-size: 1.1rem; 214 line-height: 1.65; 215 color: #64748b; 216 max-width: 780px; 217 margin-bottom: 20px; 218 } 219 220 .nx-hero-btn .wp-block-button__link { 221 display: inline-flex; 222 align-items: center; 223 padding: 12px 28px; 224 background-color: #4f46e5 !important; 225 color: #ffffff !important; 226 border-radius: 30px !important; 227 font-weight: 700; 228 font-size: 0.95rem; 229 text-decoration: none; 230 } 231 232 /* Newsletter Card Styles in Gutenberg Editor */ 233 .nx-newsletter-card { 234 background: linear-gradient(135deg, rgba(79, 70, 229, 0.06) 0%, rgba(6, 182, 212, 0.06) 100%); 235 border: 1px solid #e2e8f0; 236 border-radius: 12px; 237 padding: 30px 24px; 238 margin: 32px 0; 239 } 240 241 .nx-newsletter-badge { 242 display: inline-flex; 243 align-items: center; 244 gap: 6px; 245 padding: 4px 12px; 246 background: #eef2ff; 247 color: #4f46e5; 248 border-radius: 20px; 249 font-size: 0.75rem; 250 font-weight: 700; 251 letter-spacing: 0.06em; 252 text-transform: uppercase; 253 margin-bottom: 12px; 254 } 255 256 .nx-newsletter-title { 257 font-size: 1.5rem !important; 258 font-weight: 800 !important; 259 color: #0f172a !important; 260 margin: 0 0 8px 0 !important; 261 } 262 263 .nx-newsletter-desc { 264 font-size: 0.95rem; 265 color: #64748b; 266 margin-bottom: 18px; 267 } 268 269 .nx-newsletter-input-group { 270 display: flex; 271 gap: 10px; 272 max-width: 500px; 273 } 274 275 .nx-newsletter-input { 276 flex: 1; 277 padding: 10px 16px; 278 border: 1px solid #cbd5e1; 279 border-radius: 8px; 280 font-size: 0.9rem; 281 } 282 283 .nx-newsletter-btn { 284 padding: 10px 22px; 285 background: #4f46e5; 286 color: #ffffff; 287 border: none; 288 border-radius: 8px; 289 font-weight: 700; 290 cursor: pointer; 291 } 292 293 .nx-newsletter-privacy { 294 font-size: 0.8rem; 295 color: #94a3b8; 296 margin-top: 8px; 297 } 298 299 /* Contact Details in Editor */ 300 .nx-contact-details { 301 display: flex; 302 flex-direction: column; 303 gap: 16px; 304 margin-top: 16px; 305 } 306 307 .nx-contact-item { 308 margin-bottom: 8px; 309 } 310 311 .nx-contact-label { 312 font-size: 0.78rem !important; 313 font-weight: 700 !important; 314 color: #64748b !important; 315 text-transform: uppercase !important; 316 letter-spacing: 0.05em !important; 317 margin-bottom: 2px !important; 318 } 319 320 .nx-contact-val { 321 font-weight: 600 !important; 322 font-size: 0.95rem !important; 323 color: #0f172a !important; 324 margin-top: 0 !important; 325 margin-bottom: 0 !important; 326 } 327 328 329 -
nexversal/3.5.9/assets/js/main.js
r357433 r357886 228 228 } 229 229 } 230 231 // 8. Newsletter Subscription Persistence on Load (Logged-in or Previously Subscribed) 232 try { 233 if (localStorage.getItem('nx_newsletter_subscribed') === 'true') { 234 document.querySelectorAll('.nx-newsletter-v2').forEach(function (box) { 235 var f = box.querySelector('.nx-newsletter-form-v2'); 236 var s = box.querySelector('.nx-nl-success-card'); 237 if (f && s) { 238 f.style.display = 'none'; 239 s.style.display = 'block'; 240 } 241 }); 242 } 243 } catch (err) {} 244 245 // 9. Newsletter AJAX Subscription Handler with Validation & WP-Admin Storage 246 document.addEventListener('submit', function (e) { 247 var form = e.target.closest('.nx-newsletter-form, .nx-newsletter-form-v2'); 248 if (!form) return; 249 e.preventDefault(); 250 251 var input = form.querySelector('.nx-newsletter-input') || form.querySelector('input[type="email"]'); 252 var btn = form.querySelector('.nx-newsletter-btn') || form.querySelector('button[type="submit"]'); 253 var email = input ? input.value.trim() : ''; 254 255 if (!email) { 256 alert('Please enter your email address to subscribe.'); 257 if (input) input.focus(); 258 return; 259 } 260 261 // Email validation regex 262 var emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; 263 if (!emailRegex.test(email)) { 264 alert('Please enter a valid email format (e.g. name@company.com).'); 265 if (input) input.focus(); 266 return; 267 } 268 269 // Disable button while processing 270 var originalBtnHtml = btn ? btn.innerHTML : ''; 271 if (btn) { 272 btn.disabled = true; 273 btn.innerHTML = '<span>Subscribing...</span>'; 274 } 275 276 var ajaxUrl = (typeof nexversal_ajax !== 'undefined' && nexversal_ajax.ajax_url) ? nexversal_ajax.ajax_url : '/wp-admin/admin-ajax.php'; 277 var nonce = (typeof nexversal_ajax !== 'undefined' && nexversal_ajax.nonce) ? nexversal_ajax.nonce : ''; 278 279 var formData = new FormData(); 280 formData.append('action', 'nexversal_subscribe'); 281 formData.append('nonce', nonce); 282 formData.append('email', email); 283 284 fetch(ajaxUrl, { 285 method: 'POST', 286 body: formData 287 }) 288 .then(function (res) { return res.json(); }) 289 .then(function (data) { 290 if (data.success) { 291 try { 292 localStorage.setItem('nx_newsletter_subscribed', 'true'); 293 } catch (e) {} 294 295 var v2Container = form.closest('.nx-newsletter-v2'); 296 if (v2Container) { 297 var successCard = v2Container.querySelector('.nx-nl-success-card'); 298 if (successCard) { 299 form.style.display = 'none'; 300 successCard.style.display = 'block'; 301 return; 302 } 303 } 304 305 if (btn) { 306 btn.innerHTML = '<span>✓ ' + data.data.message + '</span>'; 307 btn.style.background = '#10b981'; 308 } 309 if (input) { 310 input.disabled = true; 311 input.style.borderColor = '#10b981'; 312 } 313 } else { 314 if (btn) { 315 btn.disabled = false; 316 btn.innerHTML = originalBtnHtml || '<span>Subscribe →</span>'; 317 } 318 alert(data.data ? data.data.message : 'Error subscribing. Please try again.'); 319 } 320 }) 321 .catch(function () { 322 if (btn) { 323 btn.disabled = false; 324 btn.innerHTML = originalBtnHtml || '<span>Subscribe →</span>'; 325 } 326 alert('Unable to process subscription right now. Please try again later.'); 327 }); 328 }); 329 330 // 10. Built-in Ad Engine: Impressions Tracking, Click Tracker & Ad-Blocker Bypass 331 var adSlots = document.querySelectorAll('.nx-ad-slot'); 332 if (adSlots.length > 0) { 333 var ajaxUrl = (typeof nexversal_ajax !== 'undefined' && nexversal_ajax.ajax_url) ? nexversal_ajax.ajax_url : '/wp-admin/admin-ajax.php'; 334 335 // A. Record Impressions on load 336 adSlots.forEach(function (slot) { 337 var slotName = slot.getAttribute('data-ad-slot'); 338 if (slotName) { 339 var fd = new FormData(); 340 fd.append('action', 'nexversal_ad_beacon'); 341 fd.append('slot', slotName); 342 fd.append('type', 'impression'); 343 if (navigator.sendBeacon) { 344 navigator.sendBeacon(ajaxUrl, fd); 345 } else { 346 fetch(ajaxUrl, { method: 'POST', body: fd, keepalive: true }).catch(function () {}); 347 } 348 } 349 }); 350 351 // B. Record Clicks on Ad Links 352 document.addEventListener('click', function (e) { 353 var adLink = e.target.closest('.nx-ad-slot a'); 354 if (!adLink) return; 355 var slot = adLink.closest('.nx-ad-slot'); 356 var slotName = slot ? slot.getAttribute('data-ad-slot') : ''; 357 if (slotName) { 358 var fd = new FormData(); 359 fd.append('action', 'nexversal_ad_beacon'); 360 fd.append('slot', slotName); 361 fd.append('type', 'click'); 362 if (navigator.sendBeacon) { 363 navigator.sendBeacon(ajaxUrl, fd); 364 } else { 365 fetch(ajaxUrl, { method: 'POST', body: fd, keepalive: true }).catch(function () {}); 366 } 367 } 368 }); 369 370 // C. Ad-Blocker Bypass Detection & Fallback Switcher 371 var bypassElements = document.querySelectorAll('.nx-ad-blocker-bypass'); 372 if (bypassElements.length > 0) { 373 var bait = document.createElement('div'); 374 bait.className = 'ads ad adsense ad-banner sponsor pub_300x250 text-ad'; 375 bait.style.cssText = 'position:absolute;left:-9999px;top:-9999px;width:1px;height:1px;'; 376 document.body.appendChild(bait); 377 378 window.setTimeout(function () { 379 var isBlocked = (bait.offsetParent === null || bait.offsetHeight === 0 || bait.clientHeight === 0); 380 if (isBlocked) { 381 bypassElements.forEach(function (bypass) { 382 var parentSlot = bypass.closest('.nx-ad-slot'); 383 if (parentSlot) { 384 var content = parentSlot.querySelector('.nx-ad-content'); 385 if (content) content.style.display = 'none'; 386 bypass.style.display = 'block'; 387 } 388 }); 389 } 390 if (bait && bait.parentNode) { 391 bait.parentNode.removeChild(bait); 392 } 393 }, 120); 394 } 395 } 230 396 }); -
nexversal/3.5.9/author.php
r357433 r357886 1 1 <?php 2 2 /** 3 * Security: Prevent direct script access 3 * Author Archive Template for Nexversal Theme 4 * 5 * @package Nexversal 6 * @author Nexes Agency & Muhammad Shahid 7 * @copyright 2026 Nexes Agency & Muhammad Shahid 8 * @license GNU General Public License v2 or later 9 * @link https://nexversal.com 4 10 */ 11 5 12 if (!defined('ABSPATH')) { 6 13 exit; 7 14 } 8 9 /**10 * Author Archive Template11 *12 * @package Nexversal13 */14 15 15 16 get_header(); … … 70 71 <?php endif; ?> 71 72 </div> 72 <?php get_sidebar(); ?> 73 <?php 74 if (!function_exists('nexversal_is_meta_disabled') || !nexversal_is_meta_disabled('disable_sidebar')) { 75 get_sidebar(); 76 } 77 ?> 73 78 </div> 74 79 </div> -
nexversal/3.5.9/comments.php
r357433 r357886 1 1 <?php 2 2 /** 3 * Security: Prevent direct script access 3 * Comments template for Nexversal Theme 4 * 5 * @package Nexversal 6 * @author Nexes Agency & Muhammad Shahid 7 * @copyright 2026 Nexes Agency & Muhammad Shahid 8 * @license GNU General Public License v2 or later 9 * @link https://nexversal.com 4 10 */ 11 5 12 if (!defined('ABSPATH')) { 6 13 exit; 7 14 } 8 9 /**10 * Modern Comments Template11 *12 * @package Nexversal13 */14 15 15 16 if (post_password_required()) { -
nexversal/3.5.9/footer.php
r357433 r357886 1 1 <?php 2 2 /** 3 * Security: Prevent direct script access 3 * Footer template for Nexversal Theme 4 * 5 * @package Nexversal 6 * @author Nexes Agency & Muhammad Shahid 7 * @copyright 2026 Nexes Agency & Muhammad Shahid 8 * @license GNU General Public License v2 or later 9 * @link https://nexversal.com 4 10 */ 11 5 12 if (!defined('ABSPATH')) { 6 13 exit; 7 14 } 8 9 /**10 * Footer template11 *12 * @package Nexversal13 */14 15 ?> 16 <?php if (!function_exists('nexversal_is_meta_disabled') || !nexversal_is_meta_disabled('disable_footer')) : ?> 15 17 <footer id="colophon" class="site-footer"> 16 18 <div class="container"> … … 30 32 <div class="footer-widget"> 31 33 <h4 class="widget-title"><?php bloginfo('name'); ?></h4> 32 <p style="line-height:1.6;"><?php bloginfo('description'); ?></p> 33 <p style="font-size:0.85rem;color:#64748b;"><?php esc_html_e('Empowering creators, developers, and businesses with actionable technology insights.', 'nexversal'); ?></p> 34 <?php 35 $site_desc = get_bloginfo('description'); 36 if (!empty($site_desc)) : 37 ?> 38 <p style="line-height: 1.6; color: #94a3b8; font-size: 0.92rem; margin-top: 8px;"><?php echo esc_html($site_desc); ?></p> 39 <?php endif; ?> 34 40 </div> 35 41 <div class="footer-widget"> … … 96 102 ?> 97 103 <p class="nx-footer-credits"> 98 © <?php echo esc_html(gmdate('Y')); ?>. <?php esc_html_e('All rights reserved.', 'nexversal'); ?> 104 <?php 105 $site_name = get_bloginfo('name'); 106 if (empty($site_name)) { 107 $site_name = 'Nexversal'; 108 } 109 $site_year = gmdate('Y'); 110 ?> 111 © <?php echo esc_html($site_year); ?> <?php echo esc_html($site_name); ?>. <?php esc_html_e('All rights reserved.', 'nexversal'); ?> 99 112 <span class="nx-footer-sep">•</span> 100 113 <?php … … 119 132 </div> 120 133 </footer> 134 <?php endif; // End disable_footer check ?> 121 135 122 136 <button type="button" id="nx-back-to-top" aria-label="<?php esc_attr_e('Back to top', 'nexversal'); ?>"> -
nexversal/3.5.9/functions.php
r357433 r357886 1 1 <?php 2 2 /** 3 * Nexversal Theme Functions & Definitions3 * Nexversal WordPress Theme functions and definitions 4 4 * 5 5 * @package Nexversal 6 * @version 2.5.2 6 * @author Nexes Agency & Muhammad Shahid 7 * @copyright 2026 Nexes Agency & Muhammad Shahid 8 * @license GNU General Public License v2 or later 9 * @link https://nexversal.com 10 * @version 3.3.0 7 11 */ 8 12 … … 68 72 add_theme_support('wp-block-styles'); 69 73 70 // Custom Header support. 74 // Custom background support. 75 add_theme_support('custom-background', apply_filters('nexversal_custom_background_args', array( 76 'default-color' => 'ffffff', 77 'default-image' => '', 78 ))); 79 80 // Custom header support. 71 81 add_theme_support('custom-header', apply_filters('nexversal_custom_header_args', array( 72 82 'default-image' => '', 73 83 'width' => 1200, 74 'height' => 300,84 'height' => 280, 75 85 'flex-height' => true, 76 86 'flex-width' => true, 77 87 'header-text' => false, 78 )));79 80 // Custom Background support.81 add_theme_support('custom-background', apply_filters('nexversal_custom_background_args', array(82 'default-color' => 'ffffff',83 88 ))); 84 89 } … … 99 104 register_block_pattern_category('nexversal', array( 100 105 'label' => esc_html__('Nexversal', 'nexversal'), 106 )); 107 } 108 if (function_exists('register_block_pattern')) { 109 register_block_pattern('nexversal/hero-banner', array( 110 'title' => esc_html__('Homepage Hero Banner', 'nexversal'), 111 'description' => esc_html__('High-impact hero banner with badge, headline, description, and CTA button.', 'nexversal'), 112 'categories' => array('nexversal'), 113 'content' => '<!-- wp:group {"className":"nx-hero-section","layout":{"type":"constrained"}} --> 114 <div class="wp-block-group nx-hero-section"><div class="container"> 115 <!-- wp:paragraph {"className":"nx-hero-badge"} --> 116 <p class="nx-hero-badge">MASTER AI WITH POWERFUL & READY-TO-USE PROMPTS</p> 117 <!-- /wp:paragraph --> 118 119 <!-- wp:heading {"level":1,"className":"nx-hero-title"} --> 120 <h1 class="wp-block-heading nx-hero-title">Copy, paste, and customize high-performing prompts for modern AI tools and workflows.</h1> 121 <!-- /wp:heading --> 122 123 <!-- wp:paragraph {"className":"nx-hero-description"} --> 124 <p class="nx-hero-description">Discover the ultimate AI prompt library. Copy, paste, and customize expert-tested ChatGPT, Midjourney, and Claude prompts to boost your productivity instantly.</p> 125 <!-- /wp:paragraph --> 126 127 <!-- wp:buttons {"className":"nx-hero-actions"} --> 128 <div class="wp-block-buttons nx-hero-actions"> 129 <!-- wp:button {"className":"nx-hero-btn"} --> 130 <div class="wp-block-button nx-hero-btn"><a class="wp-block-button__link wp-element-button" href="#primary">Explore Articles →</a></div> 131 <!-- /wp:button --> 132 </div> 133 <!-- /wp:buttons --> 134 </div></div> 135 <!-- /wp:group -->', 101 136 )); 102 137 } … … 132 167 133 168 /** 169 * GDPR & Google AdSense Cookie Consent Manager 170 */ 171 require_once get_template_directory() . '/inc/cookie-consent.php'; 172 173 /** 134 174 * Set the content width in pixels, based on the theme's design and stylesheet. 135 175 */ … … 202 242 ); 203 243 244 wp_localize_script('nexversal-main', 'nexversal_ajax', array( 245 'ajax_url' => admin_url('admin-ajax.php'), 246 'nonce' => wp_create_nonce('nexversal_newsletter_nonce'), 247 )); 248 204 249 if (is_singular() && comments_open() && get_option('thread_comments')) { 205 250 wp_enqueue_script('comment-reply'); 206 251 } 207 252 208 // Dynamic customizer accent color injection 209 $accent = get_theme_mod('nexversal_accent_color', '#4f46e5'); 210 if ($accent && $accent !== '#4f46e5') { 211 $custom_css = ":root { --nx-primary: {$accent}; --nx-primary-hover: {$accent}; }"; 212 wp_add_inline_style('nexversal-style', $custom_css); 213 } 253 // Dynamic customizer color injection for theme & all buttons 254 $accent = get_theme_mod('nexversal_accent_color', '#4f46e5'); 255 $btn_bg = get_theme_mod('nexversal_btn_bg_color', ''); 256 $btn_text = get_theme_mod('nexversal_btn_text_color', '#ffffff'); 257 $btn_hover_bg = get_theme_mod('nexversal_btn_hover_bg_color', ''); 258 259 if (empty($btn_bg)) { 260 $btn_bg = $accent ? $accent : '#4f46e5'; 261 } 262 if (empty($btn_text)) { 263 $btn_text = '#ffffff'; 264 } 265 if (empty($btn_hover_bg)) { 266 $btn_hover_bg = $btn_bg; 267 } 268 269 $custom_css = " 270 :root { 271 --nx-primary: {$accent}; 272 --nx-primary-hover: {$accent}; 273 --nx-btn-bg: {$btn_bg}; 274 --nx-btn-text: {$btn_text}; 275 --nx-btn-hover: {$btn_hover_bg}; 276 } 277 .nx-hero-btn, 278 .nx-btn, 279 .nx-btn-primary, 280 .nx-card-cta, 281 .nx-newsletter-btn, 282 .nx-contact-submit, 283 .wp-block-button__link, 284 .wp-element-button, 285 button[type=\"submit\"], 286 input[type=\"submit\"], 287 .comment-form .submit, 288 #commentform #submit, 289 .search-submit { 290 background-color: var(--nx-btn-bg, {$btn_bg}) !important; 291 color: var(--nx-btn-text, {$btn_text}) !important; 292 border-color: var(--nx-btn-bg, {$btn_bg}) !important; 293 } 294 .nx-hero-btn:hover, 295 .nx-btn:hover, 296 .nx-btn-primary:hover, 297 .nx-card-cta:hover, 298 .nx-newsletter-btn:hover, 299 .nx-contact-submit:hover, 300 .wp-block-button__link:hover, 301 .wp-element-button:hover, 302 button[type=\"submit\"]:hover, 303 input[type=\"submit\"]:hover, 304 .comment-form .submit:hover, 305 #commentform #submit:hover, 306 .search-submit:hover { 307 background-color: var(--nx-btn-hover, {$btn_hover_bg}) !important; 308 border-color: var(--nx-btn-hover, {$btn_hover_bg}) !important; 309 } 310 "; 311 wp_add_inline_style('nexversal-style', $custom_css); 214 312 } 215 313 add_action('wp_enqueue_scripts', 'nexversal_scripts'); 216 314 217 315 /** 316 * Enqueue live preview scripts in WordPress Customizer. 317 */ 318 function nexversal_customize_preview_js() { 319 wp_enqueue_script( 320 'nexversal-customize-preview', 321 get_template_directory_uri() . '/assets/js/customize-preview.js', 322 array('customize-preview', 'jquery'), 323 '2.8.0', 324 true 325 ); 326 } 327 add_action('customize_preview_init', 'nexversal_customize_preview_js'); 328 329 /** 330 * Render callback for hero button partial selective refresh. 331 */ 332 function nexversal_render_hero_button() { 333 $hero_btn_text = get_theme_mod('nexversal_hero_btn_text', 'Explore Articles'); 334 $hero_btn_url = get_theme_mod('nexversal_hero_btn_url', '#primary'); 335 if (!empty($hero_btn_text)) : 336 ?> 337 <div class="nx-hero-actions nx-hero-btn-wrap" style="margin-bottom: 22px;"> 338 <a href="<?php echo esc_url($hero_btn_url); ?>" class="nx-btn nx-btn-primary nx-hero-btn" data-nx-setting="nexversal_hero_btn_text"> 339 <span class="nx-hero-btn-label"><?php echo esc_html($hero_btn_text); ?></span> 340 <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="margin-left: 6px;"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg> 341 </a> 342 </div> 343 <?php 344 endif; 345 } 346 347 /** 218 348 * Customizer options. 219 349 */ 220 350 function nexversal_customize_register($wp_customize) { 221 // Styling Section 351 // Remove default Background Image and Header Image sections 352 $wp_customize->remove_section('background_image'); 353 $wp_customize->remove_section('header_image'); 354 355 // Styling Section - Colors for theme and all buttons 222 356 $wp_customize->add_section('nexversal_styling', array( 223 'title' => esc_html__('Theme Styling & Colors', 'nexversal'), 224 'priority' => 30, 225 )); 226 357 'title' => esc_html__('Theme Styling & Colors', 'nexversal'), 358 'priority' => 30, 359 'description' => esc_html__('Customize colors for the theme and all buttons across your entire website.', 'nexversal'), 360 )); 361 362 // Accent / Primary Color 227 363 $wp_customize->add_setting('nexversal_accent_color', array( 228 364 'default' => '#4f46e5', 229 365 'sanitize_callback' => 'sanitize_hex_color', 230 ));231 366 'transport' => 'postMessage', 367 )); 232 368 $wp_customize->add_control(new WP_Customize_Color_Control($wp_customize, 'nexversal_accent_color', array( 233 'label' => esc_html__(' Accent / Primary Color', 'nexversal'),369 'label' => esc_html__('Theme Accent / Primary Color', 'nexversal'), 234 370 'section' => 'nexversal_styling', 235 371 'settings' => 'nexversal_accent_color', 372 'priority' => 10, 373 ))); 374 375 // Buttons Background Color 376 $wp_customize->add_setting('nexversal_btn_bg_color', array( 377 'default' => '#4f46e5', 378 'sanitize_callback' => 'sanitize_hex_color', 379 'transport' => 'postMessage', 380 )); 381 $wp_customize->add_control(new WP_Customize_Color_Control($wp_customize, 'nexversal_btn_bg_color', array( 382 'label' => esc_html__('All Buttons Background Color', 'nexversal'), 383 'description' => esc_html__('Sets background color for hero CTA, cards, forms, blocks, and all site buttons.', 'nexversal'), 384 'section' => 'nexversal_styling', 385 'settings' => 'nexversal_btn_bg_color', 386 'priority' => 20, 387 ))); 388 389 // Buttons Text Color 390 $wp_customize->add_setting('nexversal_btn_text_color', array( 391 'default' => '#ffffff', 392 'sanitize_callback' => 'sanitize_hex_color', 393 'transport' => 'postMessage', 394 )); 395 $wp_customize->add_control(new WP_Customize_Color_Control($wp_customize, 'nexversal_btn_text_color', array( 396 'label' => esc_html__('All Buttons Text Color', 'nexversal'), 397 'description' => esc_html__('Sets text and icon color inside all website buttons.', 'nexversal'), 398 'section' => 'nexversal_styling', 399 'settings' => 'nexversal_btn_text_color', 400 'priority' => 30, 401 ))); 402 403 // Buttons Hover Background Color 404 $wp_customize->add_setting('nexversal_btn_hover_bg_color', array( 405 'default' => '#4338ca', 406 'sanitize_callback' => 'sanitize_hex_color', 407 'transport' => 'postMessage', 408 )); 409 $wp_customize->add_control(new WP_Customize_Color_Control($wp_customize, 'nexversal_btn_hover_bg_color', array( 410 'label' => esc_html__('All Buttons Hover Background Color', 'nexversal'), 411 'description' => esc_html__('Hover state background color for all website buttons.', 'nexversal'), 412 'section' => 'nexversal_styling', 413 'settings' => 'nexversal_btn_hover_bg_color', 414 'priority' => 40, 236 415 ))); 237 416 … … 254 433 255 434 $wp_customize->add_setting('nexversal_hero_badge', array( 256 'default' => ' Tech & Trends 2026',435 'default' => 'MASTER AI WITH POWERFUL & READY-TO-USE PROMPTS', 257 436 'sanitize_callback' => 'sanitize_text_field', 258 437 )); … … 265 444 266 445 $wp_customize->add_setting('nexversal_hero_title', array( 267 'default' => ' Discover In-Depth Tech Reviews, Guides & Insights',446 'default' => 'Copy, paste, and customize high-performing prompts for modern AI tools and workflows.', 268 447 'sanitize_callback' => 'sanitize_text_field', 269 448 )); … … 276 455 277 456 $wp_customize->add_setting('nexversal_hero_desc', array( 278 'default' => ' Explore curated articles on software, AI tools, comparisons, and modern web development.',457 'default' => 'Discover the ultimate AI prompt library. Copy, paste, and customize expert-tested ChatGPT, Midjourney, and Claude prompts to boost your productivity instantly.', 279 458 'sanitize_callback' => 'sanitize_textarea_field', 280 459 )); … … 289 468 'default' => 'Explore Articles', 290 469 'sanitize_callback' => 'sanitize_text_field', 470 'transport' => 'postMessage', 291 471 )); 292 472 $wp_customize->add_control('nexversal_hero_btn_text', array( 293 473 'label' => esc_html__('Hero Button Text', 'nexversal'), 294 'description' => esc_html__('Text for the primary CTA button in the hero banner. Leave empty to hide button.', 'nexversal'),474 'description' => esc_html__('Text for the primary CTA button in the hero banner. Click button in preview to edit.', 'nexversal'), 295 475 'section' => 'nexversal_hero', 296 476 'type' => 'text', … … 301 481 'default' => '#primary', 302 482 'sanitize_callback' => 'esc_url_raw', 483 'transport' => 'postMessage', 303 484 )); 304 485 $wp_customize->add_control('nexversal_hero_btn_url', array( … … 310 491 )); 311 492 312 // Custom Buttons & CTAs Section 313 $wp_customize->add_section('nexversal_buttons', array( 314 'title' => esc_html__('Buttons & CTA Customization', 'nexversal'), 315 'priority' => 38, 316 'description' => esc_html__('Customize the wording on buttons across cards, newsletters, and system pages.', 'nexversal'), 317 )); 318 319 $wp_customize->add_setting('nexversal_card_btn_text', array( 320 'default' => 'Read Article', 321 'sanitize_callback' => 'sanitize_text_field', 322 )); 323 $wp_customize->add_control('nexversal_card_btn_text', array( 324 'label' => esc_html__('Post Card "Read More" Button Text', 'nexversal'), 325 'description' => esc_html__('Text displayed on article cards in grids (e.g. Read Article, Continue Reading, Learn More). Leave empty to hide.', 'nexversal'), 326 'section' => 'nexversal_buttons', 327 'type' => 'text', 328 'priority' => 10, 329 )); 330 331 $wp_customize->add_setting('nexversal_newsletter_btn_text', array( 332 'default' => 'Subscribe', 333 'sanitize_callback' => 'sanitize_text_field', 334 )); 335 $wp_customize->add_control('nexversal_newsletter_btn_text', array( 336 'label' => esc_html__('Newsletter Submit Button Text', 'nexversal'), 337 'description' => esc_html__('Text on the subscription form button (e.g. Subscribe, Join Now, Get Updates).', 'nexversal'), 338 'section' => 'nexversal_buttons', 339 'type' => 'text', 340 'priority' => 20, 341 )); 342 343 $wp_customize->add_setting('nexversal_404_btn_text', array( 344 'default' => 'Back to Homepage', 345 'sanitize_callback' => 'sanitize_text_field', 346 )); 347 $wp_customize->add_control('nexversal_404_btn_text', array( 348 'label' => esc_html__('404 Page Return Button Text', 'nexversal'), 349 'description' => esc_html__('Text on the error page button returning visitors to home.', 'nexversal'), 350 'section' => 'nexversal_buttons', 351 'type' => 'text', 352 'priority' => 30, 353 )); 493 // Selective refresh for Hero Button (Shortcut pencil in preview) 494 if (isset($wp_customize->selective_refresh)) { 495 $wp_customize->selective_refresh->add_partial('nexversal_hero_btn_partial', array( 496 'selector' => '.nx-hero-btn-wrap', 497 'settings' => array('nexversal_hero_btn_text', 'nexversal_hero_btn_url'), 498 'render_callback' => 'nexversal_render_hero_button', 499 'container_inclusive' => true, 500 )); 501 } 354 502 355 503 // Social Media Section … … 382 530 } 383 531 532 // Contact Page Settings Section 533 $wp_customize->add_section('nexversal_contact', array( 534 'title' => esc_html__('Contact Page Settings', 'nexversal'), 535 'priority' => 42, 536 'description' => esc_html__('Customize the contact information card, support email, headquarters location, and response times on your Contact page.', 'nexversal'), 537 )); 538 539 // Enable/Disable Contact Info Card 540 $wp_customize->add_setting('nexversal_show_contact_info', array( 541 'default' => true, 542 'sanitize_callback' => 'nexversal_sanitize_checkbox', 543 )); 544 $wp_customize->add_control('nexversal_show_contact_info', array( 545 'label' => esc_html__('Show Contact Information Card', 'nexversal'), 546 'description' => esc_html__('Display the contact details card alongside the message form.', 'nexversal'), 547 'section' => 'nexversal_contact', 548 'type' => 'checkbox', 549 )); 550 551 // Contact Title 552 $wp_customize->add_setting('nexversal_contact_title', array( 553 'default' => 'Contact Information', 554 'sanitize_callback' => 'sanitize_text_field', 555 )); 556 $wp_customize->add_control('nexversal_contact_title', array( 557 'label' => esc_html__('Card Heading', 'nexversal'), 558 'section' => 'nexversal_contact', 559 'type' => 'text', 560 )); 561 562 // Contact Description 563 $wp_customize->add_setting('nexversal_contact_desc', array( 564 'default' => 'We are dedicated to fast response times and transparent communication.', 565 'sanitize_callback' => 'sanitize_textarea_field', 566 )); 567 $wp_customize->add_control('nexversal_contact_desc', array( 568 'label' => esc_html__('Card Description', 'nexversal'), 569 'section' => 'nexversal_contact', 570 'type' => 'textarea', 571 )); 572 573 // Support Email 574 $wp_customize->add_setting('nexversal_contact_email', array( 575 'default' => get_option('admin_email', 'contact@nexversal.com'), 576 'sanitize_callback' => 'sanitize_email', 577 )); 578 $wp_customize->add_control('nexversal_contact_email', array( 579 'label' => esc_html__('Support Email Address', 'nexversal'), 580 'description' => esc_html__('The public email address visitors can write to.', 'nexversal'), 581 'section' => 'nexversal_contact', 582 'type' => 'email', 583 )); 584 585 // Headquarters Text 586 $wp_customize->add_setting('nexversal_contact_hq_text', array( 587 'default' => 'nexversal.com/contact', 588 'sanitize_callback' => 'sanitize_text_field', 589 )); 590 $wp_customize->add_control('nexversal_contact_hq_text', array( 591 'label' => esc_html__('Official Headquarters Text / City', 'nexversal'), 592 'section' => 'nexversal_contact', 593 'type' => 'text', 594 )); 595 596 // Headquarters URL 597 $wp_customize->add_setting('nexversal_contact_hq_url', array( 598 'default' => 'https://nexversal.com/contact', 599 'sanitize_callback' => 'esc_url_raw', 600 )); 601 $wp_customize->add_control('nexversal_contact_hq_url', array( 602 'label' => esc_html__('Headquarters Link / Maps URL', 'nexversal'), 603 'section' => 'nexversal_contact', 604 'type' => 'url', 605 )); 606 607 // Custom Social Links (One URL per line) 608 $wp_customize->add_setting('nexversal_custom_social_links', array( 609 'default' => "https://facebook.com/nexversal\nhttps://twitter.com/nexversal\nhttps://linkedin.com/company/nexversal\nhttps://github.com/nexversal\nhttps://youtube.com/@nexversal", 610 'sanitize_callback' => 'sanitize_textarea_field', 611 )); 612 $wp_customize->add_control('nexversal_custom_social_links', array( 613 'label' => esc_html__('Social & Website Links (One URL per line)', 'nexversal'), 614 'description' => esc_html__('Add your Facebook, X/Twitter, LinkedIn, Instagram, YouTube, GitHub, TikTok, Discord, Telegram, or personal website links. Logos are auto-detected!', 'nexversal'), 615 'section' => 'nexversal_contact', 616 'type' => 'textarea', 617 )); 618 619 // Homepage Author Section (Image 3) 620 $wp_customize->add_section('nexversal_homepage_author', array( 621 'title' => esc_html__('Homepage Author Box', 'nexversal'), 622 'priority' => 38, 623 )); 624 625 $wp_customize->add_setting('nexversal_author_name', array( 626 'default' => 'Muhammad Shahid', 627 'sanitize_callback' => 'sanitize_text_field', 628 )); 629 $wp_customize->add_control('nexversal_author_name', array( 630 'label' => esc_html__('Author Name', 'nexversal'), 631 'section' => 'nexversal_homepage_author', 632 'type' => 'text', 633 )); 634 635 $wp_customize->add_setting('nexversal_author_role', array( 636 'default' => 'Founder & Editor', 637 'sanitize_callback' => 'sanitize_text_field', 638 )); 639 $wp_customize->add_control('nexversal_author_role', array( 640 'label' => esc_html__('Author Role / Title', 'nexversal'), 641 'section' => 'nexversal_homepage_author', 642 'type' => 'text', 643 )); 644 645 $wp_customize->add_setting('nexversal_author_link', array( 646 'default' => home_url('/about/'), 647 'sanitize_callback' => 'esc_url_raw', 648 )); 649 $wp_customize->add_control('nexversal_author_link', array( 650 'label' => esc_html__('Author Profile / Website Link (Clickable URL)', 'nexversal'), 651 'description' => esc_html__('Enter the link destination when users click the author name, role, or avatar.', 'nexversal'), 652 'section' => 'nexversal_homepage_author', 653 'type' => 'url', 654 )); 655 656 $wp_customize->add_setting('nexversal_author_avatar', array( 657 'default' => '', 658 'sanitize_callback' => 'esc_url_raw', 659 )); 660 $wp_customize->add_control(new WP_Customize_Image_Control($wp_customize, 'nexversal_author_avatar', array( 661 'label' => esc_html__('Author Profile Photo / Avatar', 'nexversal'), 662 'description' => esc_html__('Upload a profile picture. If left empty, the first initial of the author name will be displayed in the gradient circle.', 'nexversal'), 663 'section' => 'nexversal_homepage_author', 664 ))); 665 666 $wp_customize->add_setting('nexversal_author_bio', array( 667 'default' => 'Researches and writes every review and comparison on Nexversal.', 668 'sanitize_callback' => 'sanitize_textarea_field', 669 )); 670 $wp_customize->add_control('nexversal_author_bio', array( 671 'label' => esc_html__('Author Bio', 'nexversal'), 672 'section' => 'nexversal_homepage_author', 673 'type' => 'textarea', 674 )); 675 676 // Homepage Newsletter Box Customization (Image 2 Design & Success State) 677 $wp_customize->add_section('nexversal_newsletter', array( 678 'title' => esc_html__('Homepage Newsletter Box', 'nexversal'), 679 'priority' => 39, 680 )); 681 682 $wp_customize->add_setting('nexversal_newsletter_title', array( 683 'default' => 'New AI tool reviews,', 684 'sanitize_callback' => 'sanitize_text_field', 685 )); 686 $wp_customize->add_control('nexversal_newsletter_title', array( 687 'label' => esc_html__('Title', 'nexversal'), 688 'section' => 'nexversal_newsletter', 689 'type' => 'text', 690 )); 691 692 $wp_customize->add_setting('nexversal_newsletter_highlight', array( 693 'default' => 'in your inbox.', 694 'sanitize_callback' => 'sanitize_text_field', 695 )); 696 $wp_customize->add_control('nexversal_newsletter_highlight', array( 697 'label' => esc_html__('Title Highlight Text', 'nexversal'), 698 'section' => 'nexversal_newsletter', 699 'type' => 'text', 700 )); 701 702 $wp_customize->add_setting('nexversal_newsletter_desc', array( 703 'default' => 'Get new reviews and comparisons in your inbox. No spam, unsubscribe anytime.', 704 'sanitize_callback' => 'sanitize_textarea_field', 705 )); 706 $wp_customize->add_control('nexversal_newsletter_desc', array( 707 'label' => esc_html__('Description', 'nexversal'), 708 'section' => 'nexversal_newsletter', 709 'type' => 'textarea', 710 )); 711 712 $wp_customize->add_setting('nexversal_newsletter_btn_text', array( 713 'default' => 'Subscribe', 714 'sanitize_callback' => 'sanitize_text_field', 715 )); 716 $wp_customize->add_control('nexversal_newsletter_btn_text', array( 717 'label' => esc_html__('Button Text', 'nexversal'), 718 'section' => 'nexversal_newsletter', 719 'type' => 'text', 720 )); 721 722 $wp_customize->add_setting('nexversal_newsletter_success_title', array( 723 'default' => "You're in. ✨", 724 'sanitize_callback' => 'sanitize_text_field', 725 )); 726 $wp_customize->add_control('nexversal_newsletter_success_title', array( 727 'label' => esc_html__('Subscribed / Logged-in Success Title', 'nexversal'), 728 'section' => 'nexversal_newsletter', 729 'type' => 'text', 730 )); 731 732 $wp_customize->add_setting('nexversal_newsletter_success_desc', array( 733 'default' => "We'll email you new reviews and comparisons as we publish them.", 734 'sanitize_callback' => 'sanitize_textarea_field', 735 )); 736 $wp_customize->add_control('nexversal_newsletter_success_desc', array( 737 'label' => esc_html__('Subscribed / Logged-in Success Message', 'nexversal'), 738 'section' => 'nexversal_newsletter', 739 'type' => 'textarea', 740 )); 741 384 742 // Footer Customization Section 385 743 $wp_customize->add_section('nexversal_footer', array( … … 448 806 switch ($network) { 449 807 case 'twitter': 808 case 'x': 450 809 return '<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg>'; 451 810 case 'linkedin': … … 459 818 case 'youtube': 460 819 return '<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>'; 820 case 'tiktok': 821 return '<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.24 1.07-.14 1.61.24 1.16 1.18 2.09 2.35 2.29.97.16 1.98-.07 2.74-.69.64-.51 1.05-1.27 1.13-2.09.06-2.58.02-5.16.03-7.74.01-4.45-.02-8.9.02-13.35z"/></svg>'; 822 case 'discord': 823 return '<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0 12.64 12.64 0 0 0-.617-1.25.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057 19.9 19.9 0 0 0 5.993 3.03.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994.021-.041.001-.09-.041-.106a13.107 13.107 0 0 1-1.872-.892.077.077 0 0 1-.008-.128 10.2 10.2 0 0 0 .372-.292.074.074 0 0 1 .077-.01c3.929 1.793 8.18 1.793 12.061 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127 12.299 12.299 0 0 1-1.873.894.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028 19.839 19.839 0 0 0 6.002-3.03.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.028zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.956-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419 0-1.333.955-2.419 2.157-2.419 1.21 0 2.176 1.096 2.157 2.42 0 1.333-.946 2.418-2.157 2.418z"/></svg>'; 824 case 'telegram': 825 return '<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm4.64 6.8c-.15 1.58-.8 5.42-1.13 7.19-.14.75-.42 1-.68 1.03-.58.05-1.02-.38-1.58-.75-.88-.58-1.38-.94-2.23-1.5-.99-.65-.35-1.01.22-1.59.15-.15 2.71-2.48 2.76-2.69a.2.2 0 0 0-.05-.18c-.06-.05-.14-.03-.21-.02-.09.02-1.49.95-4.22 2.79-.4.27-.76.41-1.08.4-.36-.01-1.04-.2-1.55-.37-.63-.2-1.12-.31-1.08-.66.02-.18.27-.36.75-.55 2.92-1.27 4.86-2.11 5.83-2.51 2.78-1.16 3.35-1.36 3.73-1.36.08 0 .27.02.39.12.1.08.13.19.14.27-.01.06.01.24 0 .38z"/></svg>'; 826 case 'website': 827 return '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>'; 461 828 default: 462 return ''; 463 } 829 return '<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>'; 830 } 831 } 832 833 /** 834 * Parse any list of custom URLs and auto-detect social networks with corresponding official logos. 835 */ 836 function nexversal_get_smart_social_links() { 837 $raw_links = get_theme_mod('nexversal_custom_social_links', ''); 838 $urls = array(); 839 840 if (!empty(trim($raw_links))) { 841 $lines = preg_split('/[\r\n,]+/', $raw_links); 842 foreach ($lines as $line) { 843 $u = trim($line); 844 if (!empty($u) && filter_var($u, FILTER_VALIDATE_URL)) { 845 $urls[] = $u; 846 } 847 } 848 } else { 849 $platforms = array('facebook', 'twitter', 'linkedin', 'instagram', 'youtube', 'github'); 850 foreach ($platforms as $p) { 851 $u = get_theme_mod('nexversal_social_' . $p, ''); 852 if (!empty($u)) { 853 $urls[] = $u; 854 } 855 } 856 if (empty($urls)) { 857 $urls = array( 858 'https://facebook.com/nexversal', 859 'https://twitter.com/nexversal', 860 'https://linkedin.com/company/nexversal', 861 'https://github.com/nexversal', 862 'https://youtube.com/@nexversal', 863 ); 864 } 865 } 866 867 $parsed = array(); 868 foreach ($urls as $url) { 869 $host = strtolower(parse_url($url, PHP_URL_HOST) ?? ''); 870 $host = preg_replace('/^www\./', '', $host); 871 872 if (strpos($host, 'facebook.com') !== false || strpos($host, 'fb.com') !== false) { 873 $parsed[] = array('platform' => 'facebook', 'title' => 'Facebook', 'url' => $url); 874 } elseif (strpos($host, 'twitter.com') !== false || strpos($host, 'x.com') !== false) { 875 $parsed[] = array('platform' => 'twitter', 'title' => 'X (Twitter)', 'url' => $url); 876 } elseif (strpos($host, 'linkedin.com') !== false) { 877 $parsed[] = array('platform' => 'linkedin', 'title' => 'LinkedIn', 'url' => $url); 878 } elseif (strpos($host, 'instagram.com') !== false) { 879 $parsed[] = array('platform' => 'instagram', 'title' => 'Instagram', 'url' => $url); 880 } elseif (strpos($host, 'youtube.com') !== false || strpos($host, 'youtu.be') !== false) { 881 $parsed[] = array('platform' => 'youtube', 'title' => 'YouTube', 'url' => $url); 882 } elseif (strpos($host, 'github.com') !== false) { 883 $parsed[] = array('platform' => 'github', 'title' => 'GitHub', 'url' => $url); 884 } elseif (strpos($host, 'tiktok.com') !== false) { 885 $parsed[] = array('platform' => 'tiktok', 'title' => 'TikTok', 'url' => $url); 886 } elseif (strpos($host, 'discord.gg') !== false || strpos($host, 'discord.com') !== false) { 887 $parsed[] = array('platform' => 'discord', 'title' => 'Discord', 'url' => $url); 888 } elseif (strpos($host, 't.me') !== false || strpos($host, 'telegram.org') !== false) { 889 $parsed[] = array('platform' => 'telegram', 'title' => 'Telegram', 'url' => $url); 890 } else { 891 $parsed[] = array('platform' => 'website', 'title' => !empty($host) ? $host : 'Website', 'url' => $url); 892 } 893 } 894 895 return $parsed; 464 896 } 465 897 … … 712 1144 add_action('admin_init', 'nexversal_add_privacy_policy_content'); 713 1145 714 715 1146 /** 1147 * Automatically configure default pages (Home, Blogs, About Us, Contact) and navigation menu. 1148 */ 1149 function nexversal_setup_default_content() { 1150 // 1. Setup Home Page with Editable Hero Blocks & Newsletter Box 1151 $home_content = '<!-- wp:group {"className":"nx-hero-section","layout":{"type":"constrained"}} --> 1152 <div class="wp-block-group nx-hero-section"><div class="container"> 1153 <!-- wp:paragraph {"className":"nx-hero-badge"} --> 1154 <p class="nx-hero-badge">MASTER AI WITH POWERFUL & READY-TO-USE PROMPTS</p> 1155 <!-- /wp:paragraph --> 1156 1157 <!-- wp:heading {"level":1,"className":"nx-hero-title"} --> 1158 <h1 class="wp-block-heading nx-hero-title">Copy, paste, and customize high-performing prompts for modern AI tools and workflows.</h1> 1159 <!-- /wp:heading --> 1160 1161 <!-- wp:paragraph {"className":"nx-hero-description"} --> 1162 <p class="nx-hero-description">Discover the ultimate AI prompt library. Copy, paste, and customize expert-tested ChatGPT, Midjourney, and Claude prompts to boost your productivity instantly.</p> 1163 <!-- /wp:paragraph --> 1164 1165 <!-- wp:buttons {"className":"nx-hero-actions"} --> 1166 <div class="wp-block-buttons nx-hero-actions"> 1167 <!-- wp:button {"className":"nx-hero-btn"} --> 1168 <div class="wp-block-button nx-hero-btn"><a class="wp-block-button__link wp-element-button" href="#primary">Explore Articles →</a></div> 1169 <!-- /wp:button --> 1170 </div> 1171 <!-- /wp:buttons --> 1172 </div></div> 1173 <!-- /wp:group --> 1174 1175 <!-- wp:group {"className":"nx-newsletter-card","layout":{"type":"constrained"}} --> 1176 <div class="wp-block-group nx-newsletter-card"> 1177 <div class="nx-newsletter-content"> 1178 <!-- wp:paragraph {"className":"nx-newsletter-badge"} --> 1179 <p class="nx-newsletter-badge">✉ NEXVERSAL TECH DISPATCH</p> 1180 <!-- /wp:paragraph --> 1181 1182 <!-- wp:heading {"level":3,"className":"nx-newsletter-title"} --> 1183 <h3 class="wp-block-heading nx-newsletter-title">Stay Ahead in Tech & Modern Software</h3> 1184 <!-- /wp:heading --> 1185 1186 <!-- wp:paragraph {"className":"nx-newsletter-desc"} --> 1187 <p class="nx-newsletter-desc">Join developers, engineers, and digital innovators getting our weekly deep-dives on AI tools, cloud architectures, and development strategies.</p> 1188 <!-- /wp:paragraph --> 1189 </div> 1190 1191 <!-- wp:html --> 1192 <form class="nx-newsletter-form" onsubmit="event.preventDefault(); var btn=this.querySelector(\'button\'); btn.innerText=\'Subscribed!\'; btn.style.background=\'#10b981\'; this.querySelector(\'input\').disabled=true;"> 1193 <div class="nx-newsletter-input-group"> 1194 <input type="email" placeholder="Enter your work email..." required class="nx-newsletter-input" /> 1195 <button type="submit" class="nx-newsletter-btn"> 1196 <span>Subscribe →</span> 1197 </button> 1198 </div> 1199 <p class="nx-newsletter-privacy"> 1200 🔒 No spam. Unsubscribe anytime with 1-click. 1201 </p> 1202 </form> 1203 <!-- /wp:html --> 1204 </div> 1205 <!-- /wp:group -->'; 1206 1207 $home = get_page_by_path('home'); 1208 if (!$home) { 1209 $home_id = wp_insert_post(array( 1210 'post_title' => 'Home', 1211 'post_name' => 'home', 1212 'post_status' => 'publish', 1213 'post_type' => 'page', 1214 'post_content' => $home_content, 1215 )); 1216 } else { 1217 $home_id = $home->ID; 1218 $current_content = $home->post_content; 1219 if (empty(trim($current_content)) || strpos($current_content, 'Welcome to Nexversal') !== false || strpos($current_content, 'nx-newsletter-card') === false) { 1220 wp_update_post(array( 1221 'ID' => $home_id, 1222 'post_content' => $home_content, 1223 )); 1224 } 1225 } 1226 if ($home_id && !is_wp_error($home_id)) { 1227 update_post_meta($home_id, '_wp_page_template', 'template-home.php'); 1228 update_option('show_on_front', 'page'); 1229 update_option('page_on_front', $home_id); 1230 } 1231 1232 // 2. Setup Blogs Page with Editable Blocks 1233 $blog_content = '<!-- wp:paragraph {"className":"nx-hero-badge"} --> 1234 <p class="nx-hero-badge">ARTICLES & GUIDES</p> 1235 <!-- /wp:paragraph --> 1236 1237 <!-- wp:heading {"level":1} --> 1238 <h1 class="wp-block-heading">Explore All Blogs & Tech Insights</h1> 1239 <!-- /wp:heading --> 1240 1241 <!-- wp:paragraph {"fontSize":"medium"} --> 1242 <p class="has-medium-font-size">Discover our complete collection of expert tutorials, software comparisons, AI guides, and modern engineering practices.</p> 1243 <!-- /wp:paragraph -->'; 1244 1245 $blog = get_page_by_path('blog') ?: get_page_by_path('blogs'); 1246 if (!$blog) { 1247 $blog_id = wp_insert_post(array( 1248 'post_title' => 'Blogs', 1249 'post_name' => 'blog', 1250 'post_status' => 'publish', 1251 'post_type' => 'page', 1252 'post_content' => $blog_content, 1253 )); 1254 } else { 1255 $blog_id = $blog->ID; 1256 $current_content = $blog->post_content; 1257 if (empty(trim($current_content)) || strpos($current_content, 'nx-hero-badge') === false) { 1258 wp_update_post(array( 1259 'ID' => $blog_id, 1260 'post_content' => $blog_content, 1261 )); 1262 } 1263 } 1264 if ($blog_id && !is_wp_error($blog_id)) { 1265 update_post_meta($blog_id, '_wp_page_template', 'template-blog.php'); 1266 update_option('page_for_posts', $blog_id); 1267 } 1268 1269 // 3. Setup About Us with Editable Blocks 1270 $about_content = '<!-- wp:paragraph {"className":"nx-hero-badge"} --> 1271 <p class="nx-hero-badge">WHO WE ARE</p> 1272 <!-- /wp:paragraph --> 1273 1274 <!-- wp:heading {"level":1} --> 1275 <h1 class="wp-block-heading">About Nexversal</h1> 1276 <!-- /wp:heading --> 1277 1278 <!-- wp:paragraph {"fontSize":"medium"} --> 1279 <p class="has-medium-font-size">Empowering developers, creators, and modern businesses with actionable technology insights, high-speed software reviews, and engineering excellence.</p> 1280 <!-- /wp:paragraph --> 1281 1282 <!-- wp:heading {"level":2} --> 1283 <h2 class="wp-block-heading">Our Mission & Philosophy</h2> 1284 <!-- /wp:heading --> 1285 1286 <!-- wp:paragraph --> 1287 <p>Nexversal was created with a clear objective: cut through the digital noise and deliver high-impact, transparent, and technically rigorous technology journalism. We explore modern artificial intelligence, high-performance web engineering, cloud infrastructure, and modern software tools.</p> 1288 <!-- /wp:paragraph --> 1289 1290 <!-- wp:columns --> 1291 <div class="wp-block-columns"> 1292 <!-- wp:column --> 1293 <div class="wp-block-column" style="padding:20px;background:var(--nx-bg);border-radius:10px;"> 1294 <!-- wp:heading {"level":4} --> 1295 <h4 class="wp-block-heading">⚡ Speed & Performance</h4> 1296 <!-- /wp:heading --> 1297 <!-- wp:paragraph --> 1298 <p>We prioritize lightweight architecture, zero bloated dependencies, and pristine Core Web Vitals.</p> 1299 <!-- /wp:paragraph --> 1300 </div> 1301 <!-- /wp:column --> 1302 1303 <!-- wp:column --> 1304 <div class="wp-block-column" style="padding:20px;background:var(--nx-bg);border-radius:10px;"> 1305 <!-- wp:heading {"level":4} --> 1306 <h4 class="wp-block-heading">👑 Independent Editorial</h4> 1307 <!-- /wp:heading --> 1308 <!-- wp:paragraph --> 1309 <p>Every software comparison, AI model evaluation, and tutorial is independently verified and fact-checked.</p> 1310 <!-- /wp:paragraph --> 1311 </div> 1312 <!-- /wp:column --> 1313 1314 <!-- wp:column --> 1315 <div class="wp-block-column" style="padding:20px;background:var(--nx-bg);border-radius:10px;"> 1316 <!-- wp:heading {"level":4} --> 1317 <h4 class="wp-block-heading">🛠️ Developer-Centric</h4> 1318 <!-- /wp:heading --> 1319 <!-- wp:paragraph --> 1320 <p>Built by engineers for engineers, offering real reproducible benchmarks and practical architectures.</p> 1321 <!-- /wp:paragraph --> 1322 </div> 1323 <!-- /wp:column --> 1324 </div> 1325 <!-- /wp:columns -->'; 1326 1327 $about = get_page_by_path('about') ?: get_page_by_path('about-us'); 1328 if (!$about) { 1329 $sample = get_page_by_path('sample-page'); 1330 if ($sample) { 1331 wp_update_post(array( 1332 'ID' => $sample->ID, 1333 'post_title' => 'About Us', 1334 'post_name' => 'about', 1335 'post_content' => $about_content, 1336 )); 1337 $about_id = $sample->ID; 1338 } else { 1339 $about_id = wp_insert_post(array( 1340 'post_title' => 'About Us', 1341 'post_name' => 'about', 1342 'post_status' => 'publish', 1343 'post_type' => 'page', 1344 'post_content' => $about_content, 1345 )); 1346 } 1347 } else { 1348 $about_id = $about->ID; 1349 $current_content = $about->post_content; 1350 if (empty(trim($current_content)) || strpos($current_content, 'Welcome to Nexversal') !== false || strpos($current_content, 'nx-hero-badge') === false) { 1351 wp_update_post(array( 1352 'ID' => $about_id, 1353 'post_content' => $about_content, 1354 )); 1355 } 1356 } 1357 if ($about_id && !is_wp_error($about_id)) { 1358 update_post_meta($about_id, '_wp_page_template', 'template-about.php'); 1359 } 1360 1361 // 4. Setup Contact Page (managed seamlessly via Appearance > Customize > Contact Page Settings) 1362 $contact = get_page_by_path('contact') ?: get_page_by_path('contact-us'); 1363 if (!$contact) { 1364 $contact_id = wp_insert_post(array( 1365 'post_title' => 'Contact Us', 1366 'post_name' => 'contact', 1367 'post_status' => 'publish', 1368 'post_type' => 'page', 1369 'post_content' => '', 1370 )); 1371 } else { 1372 $contact_id = $contact->ID; 1373 } 1374 if ($contact_id && !is_wp_error($contact_id)) { 1375 update_post_meta($contact_id, '_wp_page_template', 'template-contact.php'); 1376 } 1377 1378 // 5. Setup Primary Nav Menu with all 4 pages 1379 $menu_name = 'Main Menu'; 1380 $menu_obj = wp_get_nav_menu_object($menu_name); 1381 $menu_id = $menu_obj ? $menu_obj->term_id : wp_create_nav_menu($menu_name); 1382 1383 if ($menu_id && !is_wp_error($menu_id)) { 1384 $existing_items = wp_get_nav_menu_items($menu_id); 1385 if (!empty($existing_items)) { 1386 $seen_titles = array(); 1387 foreach ($existing_items as $item) { 1388 $t = trim(strtolower($item->title)); 1389 if (isset($seen_titles[$t]) || $t === 'sample page' || strpos((string)$item->url, 'sample-page') !== false) { 1390 wp_delete_post($item->ID, true); 1391 } else { 1392 $seen_titles[$t] = true; 1393 } 1394 } 1395 $existing_items = wp_get_nav_menu_items($menu_id); 1396 } 1397 1398 if (empty($existing_items)) { 1399 if ($home_id) { 1400 wp_update_nav_menu_item($menu_id, 0, array( 1401 'menu-item-title' => 'Home', 1402 'menu-item-object' => 'page', 1403 'menu-item-object-id' => $home_id, 1404 'menu-item-type' => 'post_type', 1405 'menu-item-status' => 'publish', 1406 )); 1407 } 1408 if ($blog_id) { 1409 wp_update_nav_menu_item($menu_id, 0, array( 1410 'menu-item-title' => 'Blogs', 1411 'menu-item-object' => 'page', 1412 'menu-item-object-id' => $blog_id, 1413 'menu-item-type' => 'post_type', 1414 'menu-item-status' => 'publish', 1415 )); 1416 } 1417 if ($about_id) { 1418 wp_update_nav_menu_item($menu_id, 0, array( 1419 'menu-item-title' => 'About Us', 1420 'menu-item-object' => 'page', 1421 'menu-item-object-id' => $about_id, 1422 'menu-item-type' => 'post_type', 1423 'menu-item-status' => 'publish', 1424 )); 1425 } 1426 if ($contact_id) { 1427 wp_update_nav_menu_item($menu_id, 0, array( 1428 'menu-item-title' => 'Contact', 1429 'menu-item-object' => 'page', 1430 'menu-item-object-id' => $contact_id, 1431 'menu-item-type' => 'post_type', 1432 'menu-item-status' => 'publish', 1433 )); 1434 } 1435 } 1436 1437 $locations = get_theme_mod('nav_menu_locations'); 1438 if (!is_array($locations)) { 1439 $locations = array(); 1440 } 1441 if (empty($locations['primary'])) { 1442 $locations['primary'] = $menu_id; 1443 $locations['footer'] = $menu_id; 1444 set_theme_mod('nav_menu_locations', $locations); 1445 } 1446 } 1447 } 1448 add_action('after_switch_theme', 'nexversal_setup_default_content'); 1449 1450 /** 1451 * Run setup automatically once after update to ensure Home, Blogs, About, Contact pages exist in DB. 1452 */ 1453 function nexversal_auto_setup_pages_once() { 1454 if (!get_option('nexversal_v330_blocks_synced')) { 1455 nexversal_setup_default_content(); 1456 update_option('nexversal_v330_blocks_synced', 1); 1457 } 1458 } 1459 add_action('admin_init', 'nexversal_auto_setup_pages_once'); 1460 add_action('after_setup_theme', 'nexversal_auto_setup_pages_once'); 1461 1462 /** 1463 * ========================================================================== 1464 * URL Resolvers & Permalinks Resiliency (About, Blogs, Contact) 1465 * ========================================================================== 1466 */ 1467 1468 /** 1469 * Resolve the dynamic, validated URL of the About page across all configurations. 1470 * 1471 * @return string 1472 */ 1473 function nexversal_get_about_page_url() { 1474 static $resolved_url = null; 1475 if ($resolved_url !== null) { 1476 return $resolved_url; 1477 } 1478 1479 // 1. Path lookup 1480 $about = get_page_by_path('about'); 1481 if (!$about) { 1482 $about = get_page_by_path('about-us'); 1483 } 1484 1485 // 2. Page template lookup 1486 if (!$about) { 1487 $pages = get_posts(array( 1488 'post_type' => 'page', 1489 'post_status' => 'publish', 1490 'numberposts' => 1, 1491 'meta_key' => '_wp_page_template', 1492 'meta_value' => 'template-about.php', 1493 'suppress_filters' => true, 1494 )); 1495 if (!empty($pages)) { 1496 $about = $pages[0]; 1497 } 1498 } 1499 1500 // 3. Title lookup 1501 if (!$about) { 1502 $pages = get_posts(array( 1503 'post_type' => 'page', 1504 'post_status' => 'publish', 1505 'numberposts' => 1, 1506 'title' => 'About Us', 1507 'suppress_filters' => true, 1508 )); 1509 if (empty($pages)) { 1510 $pages = get_posts(array( 1511 'post_type' => 'page', 1512 'post_status' => 'publish', 1513 'numberposts' => 1, 1514 'title' => 'About', 1515 'suppress_filters' => true, 1516 )); 1517 } 1518 if (!empty($pages)) { 1519 $about = $pages[0]; 1520 } 1521 } 1522 1523 if ($about) { 1524 $resolved_url = get_permalink($about->ID); 1525 return $resolved_url; 1526 } 1527 1528 $resolved_url = home_url('/about/'); 1529 return $resolved_url; 1530 } 1531 1532 /** 1533 * Resolve the dynamic, validated URL of the Blogs page across all configurations. 1534 * 1535 * @return string 1536 */ 1537 function nexversal_get_blogs_page_url() { 1538 static $resolved_url = null; 1539 if ($resolved_url !== null) { 1540 return $resolved_url; 1541 } 1542 1543 $page = get_page_by_path('blog') ?: get_page_by_path('blogs'); 1544 if (!$page) { 1545 $pages = get_posts(array( 1546 'post_type' => 'page', 1547 'post_status' => 'publish', 1548 'numberposts' => 1, 1549 'meta_key' => '_wp_page_template', 1550 'meta_value' => array('template-blog.php', 'template-blogs.php'), 1551 'suppress_filters' => true, 1552 )); 1553 if (!empty($pages)) { 1554 $page = $pages[0]; 1555 } 1556 } 1557 1558 if ($page) { 1559 $resolved_url = get_permalink($page->ID); 1560 return $resolved_url; 1561 } 1562 1563 $page_for_posts = get_option('page_for_posts'); 1564 if ($page_for_posts) { 1565 $resolved_url = get_permalink($page_for_posts); 1566 return $resolved_url; 1567 } 1568 1569 $resolved_url = home_url('/blog/'); 1570 return $resolved_url; 1571 } 1572 1573 /** 1574 * Resolve the dynamic, validated URL of the Contact page across all configurations. 1575 * 1576 * @return string 1577 */ 1578 function nexversal_get_contact_page_url() { 1579 static $resolved_url = null; 1580 if ($resolved_url !== null) { 1581 return $resolved_url; 1582 } 1583 1584 $contact = get_page_by_path('contact') ?: get_page_by_path('contact-us'); 1585 if (!$contact) { 1586 $pages = get_posts(array( 1587 'post_type' => 'page', 1588 'post_status' => 'publish', 1589 'numberposts' => 1, 1590 'meta_key' => '_wp_page_template', 1591 'meta_value' => 'template-contact.php', 1592 'suppress_filters' => true, 1593 )); 1594 if (!empty($pages)) { 1595 $contact = $pages[0]; 1596 } 1597 } 1598 1599 if ($contact) { 1600 $resolved_url = get_permalink($contact->ID); 1601 return $resolved_url; 1602 } 1603 1604 $resolved_url = home_url('/contact/'); 1605 return $resolved_url; 1606 } 1607 1608 /** 1609 * Filter nav menu objects to replace any leftover "Sample Page", broken "?page_id=2" links, 1610 * and deduplicate menu items. 1611 */ 1612 function nexversal_filter_and_deduplicate_nav_menu($items) { 1613 if (!is_array($items)) { 1614 return $items; 1615 } 1616 $seen = array(); 1617 $clean_items = array(); 1618 1619 foreach ($items as $item) { 1620 if (!is_object($item) || !isset($item->title)) { 1621 continue; 1622 } 1623 1624 $title_raw = trim(strtolower($item->title)); 1625 $item_url = isset($item->url) ? (string)$item->url : ''; 1626 1627 // Clean up sample page or broken About link (including ?page_id=2) 1628 if ($title_raw === 'sample page' || strpos($item_url, 'sample-page') !== false || strpos($item_url, 'page_id=2') !== false || $title_raw === 'about' || $title_raw === 'about us') { 1629 $item->title = esc_html__('About Us', 'nexversal'); 1630 $item->url = nexversal_get_about_page_url(); 1631 } 1632 1633 // Clean up Blogs link if pointing to invalid URL or plain page_id 1634 if ($title_raw === 'blogs' || $title_raw === 'blog') { 1635 $item->title = esc_html__('Blogs', 'nexversal'); 1636 $item->url = nexversal_get_blogs_page_url(); 1637 } 1638 1639 // Clean up Contact link 1640 if ($title_raw === 'contact' || $title_raw === 'contact us') { 1641 $item->title = esc_html__('Contact', 'nexversal'); 1642 $item->url = nexversal_get_contact_page_url(); 1643 } 1644 1645 // Clean up Home link 1646 if ($title_raw === 'home') { 1647 $item->title = esc_html__('Home', 'nexversal'); 1648 $item->url = home_url('/'); 1649 } 1650 1651 // Deduplicate key by lowercased title 1652 $title_key = trim(strtolower($item->title)); 1653 if (isset($seen[$title_key])) { 1654 continue; 1655 } 1656 1657 $seen[$title_key] = true; 1658 $clean_items[] = $item; 1659 } 1660 1661 return $clean_items; 1662 } 1663 add_filter('wp_nav_menu_objects', 'nexversal_filter_and_deduplicate_nav_menu', 20); 1664 1665 /** 1666 * Redirect legacy URLs, dead sample pages, ?page_id=2, and GSC 404s gracefully (301 Permanent Redirect) 1667 */ 1668 function nexversal_redirect_legacy_and_about_urls() { 1669 $requested_page_id = isset($_GET['page_id']) ? (int) $_GET['page_id'] : 0; 1670 $req_uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : ''; 1671 1672 // 1. Direct interception for ?page_id=2 (old sample page default in WordPress) 1673 if ($requested_page_id === 2 || strpos($req_uri, 'page_id=2') !== false || strpos($req_uri, 'sample-page') !== false) { 1674 $about_url = nexversal_get_about_page_url(); 1675 wp_safe_redirect($about_url, 301); 1676 exit; 1677 } 1678 1679 $clean_path = parse_url($req_uri, PHP_URL_PATH); 1680 $clean_path = $clean_path ? rtrim($clean_path, '/') : ''; 1681 1682 // Direct alias for /blogs to canonical /blog/ 1683 if ($clean_path === '/blogs') { 1684 wp_safe_redirect(nexversal_get_blogs_page_url(), 301); 1685 exit; 1686 } 1687 1688 // 2. Intercept 404s and map to 301 destinations from Persistent Master Record 1689 if (is_404() || !empty($clean_path)) { 1690 $blog_url = nexversal_get_blogs_page_url(); 1691 $redirects = array( 1692 '/aboutAbout' => nexversal_get_about_page_url(), 1693 '/contactContact' => nexversal_get_contact_page_url(), 1694 '/Home' => home_url('/'), 1695 '/category/comparisonsComparisons' => home_url('/category/comparisons/'), 1696 '/sample-page' => nexversal_get_about_page_url(), 1697 '/blogs' => $blog_url, 1698 '/kepler-vs-github-copilot' => home_url('/blog/kepler-ai-coding'), 1699 '/gitkraken-kepler' => home_url('/blog/setup-gitkraken-kepler'), 1700 '/seo-in-the-age-of-answer-engines' => home_url('/blog/best-ai-seo-tools'), 1701 '/search-marketing-course' => home_url('/blog/best-ai-seo-tools'), 1702 '/category/artificial-intelligence' => home_url('/blog'), 1703 '/future-of-artificial-intelligence-2026' => home_url('/blog'), 1704 '/the-self-taught-engineers-curriculum' => home_url('/blog/best-free-ai-tools-for-coding-2026'), 1705 '/the-anatomy-of-a-supply-chain-attack' => home_url('/blog'), 1706 '/the-quiet-revolution-of-agentic-llms' => home_url('/blog/chatgpt-vs-claude-vs-gemini-which-should-you-use'), 1707 '/designing-with-restraint' => home_url('/blog'), 1708 '/best-ai-video-generators-for-creators-in-2026' => home_url('/blog/ai-tools-for-a-video-generator'), 1709 '/category/education' => home_url('/blog/best-ai-tools-for-students'), 1710 '/why-pricing-is-strategy-not-a-number' => home_url('/blog/ai-tools-for-small-business'), 1711 '/how-to-learn-seo-at-home-for-free-complete-2026-roadmap' => home_url('/blog/best-ai-seo-tools'), 1712 '/key-takeaways-for-mastering-rankings' => home_url('/blog/best-ai-seo-tools'), 1713 '/the-best-chatgpt-alternatives-worth-trying' => home_url('/blog/chatgpt-alternatives'), 1714 '/best-ai-tools-for-text-to-video-generators' => home_url('/blog/ai-tools-for-a-video-generator'), 1715 '/ai-tools-for-text-to-video-generators' => home_url('/blog/ai-tools-for-a-video-generator'), 1716 ); 1717 1718 if (isset($redirects[$clean_path])) { 1719 wp_safe_redirect($redirects[$clean_path], 301); 1720 exit; 1721 } 1722 1723 // If request query or URL contains page_id=2 or sample-page 1724 if (strpos($req_uri, 'page_id=2') !== false || strpos($req_uri, 'sample-page') !== false) { 1725 wp_safe_redirect(nexversal_get_about_page_url(), 301); 1726 exit; 1727 } 1728 } 1729 } 1730 add_action('template_redirect', 'nexversal_redirect_legacy_and_about_urls', 1); 1731 1732 /** 1733 * Auto-heal legacy menu items in database that point to ?page_id=2 or deleted sample-page 1734 */ 1735 function nexversal_heal_legacy_menu_and_pages() { 1736 if (get_option('nexversal_v351_about_healed')) { 1737 return; 1738 } 1739 1740 $about = get_page_by_path('about') ?: get_page_by_path('about-us'); 1741 $about_id = $about ? $about->ID : 0; 1742 1743 $all_menu_items = get_posts(array( 1744 'post_type' => 'nav_menu_item', 1745 'numberposts' => -1, 1746 'post_status' => 'any', 1747 'suppress_filters' => true, 1748 )); 1749 1750 if (!empty($all_menu_items)) { 1751 foreach ($all_menu_items as $mi) { 1752 $url = (string) get_post_meta($mi->ID, '_menu_item_url', true); 1753 $obj_id = (int) get_post_meta($mi->ID, '_menu_item_object_id', true); 1754 $title = strtolower(trim($mi->post_title)); 1755 1756 if ($obj_id === 2 || strpos($url, 'page_id=2') !== false || strpos($url, 'sample-page') !== false || $title === 'sample page' || ($about_id && ($title === 'about' || $title === 'about us') && $obj_id !== $about_id && empty($url))) { 1757 if ($about_id) { 1758 update_post_meta($mi->ID, '_menu_item_object_id', $about_id); 1759 update_post_meta($mi->ID, '_menu_item_type', 'post_type'); 1760 update_post_meta($mi->ID, '_menu_item_object', 'page'); 1761 update_post_meta($mi->ID, '_menu_item_url', ''); 1762 } else { 1763 update_post_meta($mi->ID, '_menu_item_url', home_url('/about/')); 1764 } 1765 } 1766 } 1767 } 1768 1769 update_option('nexversal_v351_about_healed', 1); 1770 } 1771 add_action('init', 'nexversal_heal_legacy_menu_and_pages'); 1772 1773 /** 1774 * ========================================================================== 1775 * Newsletter Subscribers System (Storage, AJAX, Export & Admin View) 1776 * ========================================================================== 1777 */ 1778 1779 /** 1780 * Handle AJAX newsletter subscription requests securely. 1781 */ 1782 function nexversal_handle_newsletter_subscription() { 1783 if (!check_ajax_referer('nexversal_newsletter_nonce', 'nonce', false)) { 1784 wp_send_json_error(array('message' => esc_html__('Security verification failed. Please refresh the page.', 'nexversal'))); 1785 } 1786 1787 $email = isset($_POST['email']) ? sanitize_email(wp_unslash($_POST['email'])) : ''; 1788 1789 if (empty($email)) { 1790 wp_send_json_error(array('message' => esc_html__('Please enter your email address.', 'nexversal'))); 1791 } 1792 1793 if (!is_email($email)) { 1794 wp_send_json_error(array('message' => esc_html__('Please enter a valid email address format.', 'nexversal'))); 1795 } 1796 1797 $subscribers = get_option('nexversal_subscribers', array()); 1798 if (!is_array($subscribers)) { 1799 $subscribers = array(); 1800 } 1801 1802 // Check if already subscribed 1803 foreach ($subscribers as $sub) { 1804 if (isset($sub['email']) && strtolower($sub['email']) === strtolower($email)) { 1805 wp_send_json_success(array('message' => esc_html__('Already Subscribed! Thank you.', 'nexversal'))); 1806 } 1807 } 1808 1809 // Append new subscriber with timestamp 1810 $subscribers[] = array( 1811 'email' => strtolower($email), 1812 'date' => current_time('mysql'), 1813 'ip' => sanitize_text_field(wp_unslash($_SERVER['REMOTE_ADDR'] ?? '')), 1814 ); 1815 1816 update_option('nexversal_subscribers', $subscribers); 1817 1818 wp_send_json_success(array('message' => esc_html__('Subscribed successfully! Thank you.', 'nexversal'))); 1819 } 1820 add_action('wp_ajax_nexversal_subscribe', 'nexversal_handle_newsletter_subscription'); 1821 add_action('wp_ajax_nopriv_nexversal_subscribe', 'nexversal_handle_newsletter_subscription'); 1822 1823 /** 1824 * Filter the_content to guarantee the email input field and form wrapper are present in newsletter cards. 1825 */ 1826 function nexversal_ensure_newsletter_form_input($content) { 1827 if (strpos($content, 'nx-newsletter-card') !== false) { 1828 // If button exists but email input is missing, inject the email input field 1829 if (strpos($content, '<input') === false && strpos($content, 'nx-newsletter-btn') !== false) { 1830 $input_field = '<input type="email" name="nx_email" placeholder="' . esc_attr__('Enter your work email...', 'nexversal') . '" required class="nx-newsletter-input" autocomplete="email" />'; 1831 $content = preg_replace('/(<button[^>]*class=["\'][^"\']*nx-newsletter-btn[^"\']*["\'][^>]*>)/i', $input_field . "\n" . '$1', $content, 1); 1832 } 1833 // If form wrapper is missing, wrap the input group and privacy text in <form class="nx-newsletter-form"> 1834 if (strpos($content, '<form') === false && strpos($content, 'nx-newsletter-btn') !== false) { 1835 $content = preg_replace('/(<div[^>]*class=["\'][^"\']*nx-newsletter-input-group[^"\']*["\'][^>]*>)/i', '<form class="nx-newsletter-form">$1', $content, 1); 1836 $content = preg_replace('/(<\/div>\s*<p[^>]*class=["\'][^"\']*nx-newsletter-privacy[^"\']*["\'][^>]*>.*?<\/p>)/is', '$1</form>', $content, 1); 1837 } 1838 } 1839 return $content; 1840 } 1841 add_filter('the_content', 'nexversal_ensure_newsletter_form_input', 12); 1842 1843 /** 1844 * Register Appearance > Subscribers page in WP-Admin. 1845 */ 1846 function nexversal_register_subscribers_admin_menu() { 1847 add_theme_page( 1848 esc_html__('Newsletter Subscribers', 'nexversal'), 1849 esc_html__('Subscribers', 'nexversal'), 1850 'manage_options', 1851 'nexversal-subscribers', 1852 'nexversal_render_subscribers_admin_page' 1853 ); 1854 } 1855 add_action('admin_menu', 'nexversal_register_subscribers_admin_menu'); 1856 1857 /** 1858 * Render the Subscribers page in WP-Admin with stats, list, delete, and CSV export. 1859 */ 1860 function nexversal_render_subscribers_admin_page() { 1861 if (!current_user_can('manage_options')) { 1862 return; 1863 } 1864 1865 $subscribers = get_option('nexversal_subscribers', array()); 1866 if (!is_array($subscribers)) { 1867 $subscribers = array(); 1868 } 1869 1870 // Handle CSV Export 1871 if (isset($_GET['action']) && 'export_csv' === $_GET['action'] && isset($_GET['_wpnonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'nexversal_export_csv')) { 1872 if (!headers_sent()) { 1873 header('Content-Type: text/csv; charset=utf-8'); 1874 header('Content-Disposition: attachment; filename=nexversal-subscribers-' . gmdate('Y-m-d') . '.csv'); 1875 echo "Index,Email,Subscription Date,IP Address\r\n"; 1876 $idx = 1; 1877 foreach ($subscribers as $sub) { 1878 $email = isset($sub['email']) ? str_replace('"', '""', $sub['email']) : ''; 1879 $date = isset($sub['date']) ? str_replace('"', '""', $sub['date']) : ''; 1880 $ip = isset($sub['ip']) ? str_replace('"', '""', $sub['ip']) : ''; 1881 echo '"' . intval($idx++) . '","' . esc_attr($email) . '","' . esc_attr($date) . '","' . esc_attr($ip) . "\"\r\n"; 1882 } 1883 exit; 1884 } 1885 } 1886 1887 // Handle Single Delete Action 1888 $notice = ''; 1889 if (isset($_GET['action']) && 'delete' === $_GET['action'] && isset($_GET['email']) && isset($_GET['_wpnonce'])) { 1890 $del_email = sanitize_email(wp_unslash($_GET['email'])); 1891 if (wp_verify_nonce(sanitize_text_field(wp_unslash($_GET['_wpnonce'])), 'nexversal_delete_' . $del_email)) { 1892 $updated = array(); 1893 foreach ($subscribers as $s) { 1894 if (isset($s['email']) && strtolower($s['email']) !== strtolower($del_email)) { 1895 $updated[] = $s; 1896 } 1897 } 1898 $subscribers = $updated; 1899 update_option('nexversal_subscribers', $subscribers); 1900 $notice = '<div class="notice notice-success is-dismissible"><p>' . esc_html__('Subscriber removed successfully.', 'nexversal') . '</p></div>'; 1901 } 1902 } 1903 1904 $total_count = count($subscribers); 1905 $export_url = wp_nonce_url(admin_url('themes.php?page=nexversal-subscribers&action=export_csv'), 'nexversal_export_csv'); 1906 ?> 1907 <div class="wrap" style="max-width: 1000px; margin-top: 20px;"> 1908 <h1 style="display: flex; align-items: center; gap: 10px; font-weight: 700;"> 1909 <span class="dashicons dashicons-email-alt" style="font-size: 32px; width: 32px; height: 32px; color: #4f46e5;"></span> 1910 <?php esc_html_e('Newsletter Subscribers', 'nexversal'); ?> 1911 </h1> 1912 <p style="color: #64748b; font-size: 14px;"> 1913 <?php esc_html_e('Manage email leads captured from the Nexversal Newsletter Dispatch boxes across your website.', 'nexversal'); ?> 1914 </p> 1915 1916 <?php echo $notice; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> 1917 1918 <div style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 8px; padding: 20px 24px; margin-bottom: 24px; display: flex; align-items: center; justify-content: space-between; box-shadow: 0 1px 3px rgba(0,0,0,0.05);"> 1919 <div> 1920 <span style="font-size: 13px; font-weight: 600; text-transform: uppercase; color: #64748b; letter-spacing: 0.05em;"><?php esc_html_e('Total Subscribers', 'nexversal'); ?></span> 1921 <div style="font-size: 32px; font-weight: 800; color: #0f172a; margin-top: 2px;"> 1922 <?php echo esc_html($total_count); ?> 1923 </div> 1924 </div> 1925 <?php if ($total_count > 0) : ?> 1926 <div> 1927 <a href="<?php echo esc_url($export_url); ?>" class="button button-primary" style="background: #4f46e5; border-color: #4f46e5; padding: 6px 16px; font-weight: 600;"> 1928 <span class="dashicons dashicons-download" style="vertical-align: middle; margin-top: -2px;"></span> 1929 <?php esc_html_e('Download CSV List', 'nexversal'); ?> 1930 </a> 1931 </div> 1932 <?php endif; ?> 1933 </div> 1934 1935 <?php if ($total_count > 0) : ?> 1936 <table class="wp-list-table widefat fixed striped posts" style="border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.05);"> 1937 <thead> 1938 <tr> 1939 <th style="width: 60px;">#</th> 1940 <th><?php esc_html_e('Subscriber Email', 'nexversal'); ?></th> 1941 <th style="width: 220px;"><?php esc_html_e('Date & Time (UTC/Site)', 'nexversal'); ?></th> 1942 <th style="width: 100px; text-align: center;"><?php esc_html_e('Action', 'nexversal'); ?></th> 1943 </tr> 1944 </thead> 1945 <tbody> 1946 <?php 1947 $i = 1; 1948 $reversed = array_reverse($subscribers); 1949 foreach ($reversed as $sub) : 1950 $sub_email = $sub['email'] ?? ''; 1951 $sub_date = $sub['date'] ?? ''; 1952 $del_url = wp_nonce_url( 1953 admin_url('themes.php?page=nexversal-subscribers&action=delete&email=' . urlencode($sub_email)), 1954 'nexversal_delete_' . $sub_email 1955 ); 1956 ?> 1957 <tr> 1958 <td><?php echo esc_html($i++); ?></td> 1959 <td> 1960 <strong><a href="mailto:<?php echo esc_attr($sub_email); ?>" style="color: #4f46e5; text-decoration: none;"><?php echo esc_html($sub_email); ?></a></strong> 1961 </td> 1962 <td><?php echo esc_html($sub_date); ?></td> 1963 <td style="text-align: center;"> 1964 <a href="<?php echo esc_url($del_url); ?>" onclick="return confirm('<?php echo esc_js(__('Are you sure you want to delete this subscriber?', 'nexversal')); ?>');" style="color: #ef4444; text-decoration: none; font-size: 13px;"> 1965 <?php esc_html_e('Delete', 'nexversal'); ?> 1966 </a> 1967 </td> 1968 </tr> 1969 <?php endforeach; ?> 1970 </tbody> 1971 </table> 1972 <?php else : ?> 1973 <div style="background: #ffffff; border: 1px dashed #cbd5e1; border-radius: 8px; padding: 48px 24px; text-align: center; color: #64748b;"> 1974 <span class="dashicons dashicons-email" style="font-size: 48px; width: 48px; height: 48px; color: #94a3b8; margin-bottom: 12px;"></span> 1975 <h3 style="color: #334155; margin: 0 0 8px 0; font-weight: 700;"><?php esc_html_e('No Subscribers Yet', 'nexversal'); ?></h3> 1976 <p style="margin: 0; font-size: 14px; max-width: 480px; margin: 0 auto;"> 1977 <?php esc_html_e('When visitors enter their email into the newsletter dispatch box on your homepage, their emails and subscription dates will appear here.', 'nexversal'); ?> 1978 </p> 1979 </div> 1980 <?php endif; ?> 1981 </div> 1982 <?php 1983 } 1984 1985 /** 1986 * ========================================================================== 1987 * Contact Page Settings (Dedicated WP-Admin Screen under Appearance) 1988 * ========================================================================== 1989 */ 1990 1991 /** 1992 * Register Appearance > Contact Settings page in WP-Admin. 1993 */ 1994 function nexversal_register_contact_admin_menu() { 1995 add_theme_page( 1996 esc_html__('Contact Page Settings', 'nexversal'), 1997 esc_html__('Contact Settings', 'nexversal'), 1998 'manage_options', 1999 'nexversal-contact', 2000 'nexversal_render_contact_admin_page' 2001 ); 2002 } 2003 add_action('admin_menu', 'nexversal_register_contact_admin_menu'); 2004 2005 /** 2006 * Render the Appearance > Contact Settings admin page. 2007 */ 2008 function nexversal_render_contact_admin_page() { 2009 if (!current_user_can('manage_options')) { 2010 return; 2011 } 2012 2013 $notice = ''; 2014 2015 // Handle Form Submission 2016 if (isset($_POST['nexversal_save_contact']) && check_admin_referer('nexversal_save_contact_action', 'nexversal_contact_nonce')) { 2017 $show_info = !empty($_POST['nexversal_show_contact_info']); 2018 $info_title = sanitize_text_field(wp_unslash($_POST['nexversal_contact_title'] ?? '')); 2019 $info_desc = sanitize_textarea_field(wp_unslash($_POST['nexversal_contact_desc'] ?? '')); 2020 $info_email = sanitize_email(wp_unslash($_POST['nexversal_contact_email'] ?? '')); 2021 $hq_text = sanitize_text_field(wp_unslash($_POST['nexversal_contact_hq_text'] ?? '')); 2022 $hq_url = esc_url_raw(wp_unslash($_POST['nexversal_contact_hq_url'] ?? '')); 2023 $soc_links = sanitize_textarea_field(wp_unslash($_POST['nexversal_custom_social_links'] ?? '')); 2024 2025 set_theme_mod('nexversal_show_contact_info', $show_info); 2026 set_theme_mod('nexversal_contact_title', $info_title); 2027 set_theme_mod('nexversal_contact_desc', $info_desc); 2028 set_theme_mod('nexversal_contact_email', $info_email); 2029 set_theme_mod('nexversal_contact_hq_text', $hq_text); 2030 set_theme_mod('nexversal_contact_hq_url', $hq_url); 2031 set_theme_mod('nexversal_custom_social_links', $soc_links); 2032 2033 $notice = '<div class="notice notice-success is-dismissible"><p><strong>' . esc_html__('Contact page settings saved successfully!', 'nexversal') . '</strong></p></div>'; 2034 } 2035 2036 // Retrieve active values 2037 $show_info = get_theme_mod('nexversal_show_contact_info', true); 2038 $info_title = get_theme_mod('nexversal_contact_title', __('Contact Information', 'nexversal')); 2039 $info_desc = get_theme_mod('nexversal_contact_desc', __('We are dedicated to fast response times and transparent communication.', 'nexversal')); 2040 $info_email = get_theme_mod('nexversal_contact_email', get_option('admin_email', 'contact@nexversal.com')); 2041 $hq_text = get_theme_mod('nexversal_contact_hq_text', 'nexversal.com/contact'); 2042 $hq_url = get_theme_mod('nexversal_contact_hq_url', home_url('/contact/')); 2043 $soc_links = get_theme_mod('nexversal_custom_social_links', "https://facebook.com/nexversal\nhttps://twitter.com/nexversal\nhttps://linkedin.com/company/nexversal\nhttps://github.com/nexversal\nhttps://youtube.com/@nexversal"); 2044 $customizer_url = admin_url('customize.php?autofocus[section]=nexversal_contact'); 2045 $contact_url = home_url('/contact/'); 2046 ?> 2047 <div class="wrap" style="max-width: 900px; margin-top: 20px;"> 2048 <div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; margin-bottom: 16px;"> 2049 <h1 style="display: flex; align-items: center; gap: 10px; font-weight: 700; margin: 0;"> 2050 <span class="dashicons dashicons-location-alt" style="font-size: 30px; width: 30px; height: 30px; color: #4f46e5;"></span> 2051 <?php esc_html_e('Contact Page Settings', 'nexversal'); ?> 2052 </h1> 2053 <div style="display: flex; gap: 8px;"> 2054 <a href="<?php echo esc_url($contact_url); ?>" target="_blank" class="button"> 2055 <span class="dashicons dashicons-external" style="vertical-align: middle; margin-top: -2px;"></span> 2056 <?php esc_html_e('View Contact Page', 'nexversal'); ?> 2057 </a> 2058 <a href="<?php echo esc_url($customizer_url); ?>" class="button button-secondary"> 2059 <span class="dashicons dashicons-admin-appearance" style="vertical-align: middle; margin-top: -2px;"></span> 2060 <?php esc_html_e('Customize Live', 'nexversal'); ?> 2061 </a> 2062 </div> 2063 </div> 2064 2065 <p style="color: #64748b; font-size: 14px; margin-bottom: 24px;"> 2066 <?php esc_html_e('Configure your public contact information card, support email, headquarters link, and response time displayed on the Contact Us page.', 'nexversal'); ?> 2067 </p> 2068 2069 <?php echo $notice; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> 2070 2071 <form method="post" action="" style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 8px; padding: 24px 30px; box-shadow: 0 1px 3px rgba(0,0,0,0.05);"> 2072 <?php wp_nonce_field('nexversal_save_contact_action', 'nexversal_contact_nonce'); ?> 2073 2074 <table class="form-table" role="presentation"> 2075 <tbody> 2076 <tr> 2077 <th scope="row"> 2078 <label for="nexversal_show_contact_info"><?php esc_html_e('Contact Info Card', 'nexversal'); ?></label> 2079 </th> 2080 <td> 2081 <label for="nexversal_show_contact_info" style="font-weight: 600;"> 2082 <input type="checkbox" name="nexversal_show_contact_info" id="nexversal_show_contact_info" value="1" <?php checked($show_info, true); ?> /> 2083 <?php esc_html_e('Display Contact Information Card alongside the contact form', 'nexversal'); ?> 2084 </label> 2085 <p class="description"><?php esc_html_e('If unchecked, only the Send Us a Message form will be displayed centered on the page.', 'nexversal'); ?></p> 2086 </td> 2087 </tr> 2088 2089 <tr> 2090 <th scope="row"> 2091 <label for="nexversal_contact_title"><?php esc_html_e('Card Heading', 'nexversal'); ?></label> 2092 </th> 2093 <td> 2094 <input type="text" name="nexversal_contact_title" id="nexversal_contact_title" value="<?php echo esc_attr($info_title); ?>" class="regular-text" /> 2095 <p class="description"><?php esc_html_e('Default: Contact Information', 'nexversal'); ?></p> 2096 </td> 2097 </tr> 2098 2099 <tr> 2100 <th scope="row"> 2101 <label for="nexversal_contact_desc"><?php esc_html_e('Card Description', 'nexversal'); ?></label> 2102 </th> 2103 <td> 2104 <textarea name="nexversal_contact_desc" id="nexversal_contact_desc" rows="3" class="large-text"><?php echo esc_textarea($info_desc); ?></textarea> 2105 <p class="description"><?php esc_html_e('A short intro explaining your support commitment to visitors.', 'nexversal'); ?></p> 2106 </td> 2107 </tr> 2108 2109 <tr> 2110 <th scope="row"> 2111 <label for="nexversal_contact_email"><?php esc_html_e('Support Email Address', 'nexversal'); ?></label> 2112 </th> 2113 <td> 2114 <input type="email" name="nexversal_contact_email" id="nexversal_contact_email" value="<?php echo esc_attr($info_email); ?>" class="regular-text" /> 2115 <p class="description"><?php esc_html_e('The public email address visitors can click to email you directly.', 'nexversal'); ?></p> 2116 </td> 2117 </tr> 2118 2119 <tr> 2120 <th scope="row"> 2121 <label for="nexversal_contact_hq_text"><?php esc_html_e('Headquarters / City Text', 'nexversal'); ?></label> 2122 </th> 2123 <td> 2124 <input type="text" name="nexversal_contact_hq_text" id="nexversal_contact_hq_text" value="<?php echo esc_attr($hq_text); ?>" class="regular-text" /> 2125 <p class="description"><?php esc_html_e('e.g. San Francisco, CA or nexversal.com/contact', 'nexversal'); ?></p> 2126 </td> 2127 </tr> 2128 2129 <tr> 2130 <th scope="row"> 2131 <label for="nexversal_contact_hq_url"><?php esc_html_e('Headquarters Link / Maps URL', 'nexversal'); ?></label> 2132 </th> 2133 <td> 2134 <input type="url" name="nexversal_contact_hq_url" id="nexversal_contact_hq_url" value="<?php echo esc_attr($hq_url); ?>" class="large-text" /> 2135 <p class="description"><?php esc_html_e('Link opens in a new tab when clicked (e.g. your Google Maps location or Contact URL).', 'nexversal'); ?></p> 2136 </td> 2137 </tr> 2138 2139 <tr> 2140 <th scope="row"> 2141 <label for="nexversal_custom_social_links"><?php esc_html_e('Follow Us Social Links', 'nexversal'); ?></label> 2142 </th> 2143 <td> 2144 <textarea name="nexversal_custom_social_links" id="nexversal_custom_social_links" rows="5" class="large-text" placeholder="https://facebook.com/yourpage https://x.com/yourhandle https://linkedin.com/in/yourprofile https://github.com/yourorg"><?php echo esc_textarea($soc_links); ?></textarea> 2145 <p class="description"> 2146 <?php esc_html_e('Enter one URL per line (e.g. Facebook, X/Twitter, LinkedIn, YouTube, Instagram, GitHub, TikTok, Discord, Telegram, or personal website). Nexversal automatically detects the brand and generates its official logo icon!', 'nexversal'); ?> 2147 </p> 2148 </td> 2149 </tr> 2150 </tbody> 2151 </table> 2152 2153 <div style="margin-top: 24px; padding-top: 20px; border-top: 1px solid #e2e8f0;"> 2154 <input type="submit" name="nexversal_save_contact" class="button button-primary button-large" value="<?php esc_attr_e('Save Contact Settings', 'nexversal'); ?>" style="background: #4f46e5; border-color: #4f46e5; font-weight: 600; padding: 4px 20px;" /> 2155 </div> 2156 </form> 2157 </div> 2158 <?php 2159 } 2160 2161 /** 2162 * ========================================================================== 2163 * 1-Click Starter Site / Demo Onboarding Engine 2164 * ========================================================================== 2165 */ 2166 2167 /** 2168 * Register Appearance > Starter Site page in WP-Admin 2169 */ 2170 function nexversal_register_starter_site_admin_menu() { 2171 add_theme_page( 2172 esc_html__('Nexversal Starter Site', 'nexversal'), 2173 esc_html__('Starter Site', 'nexversal'), 2174 'manage_options', 2175 'nexversal-starter', 2176 'nexversal_render_starter_site_page' 2177 ); 2178 } 2179 add_action('admin_menu', 'nexversal_register_starter_site_admin_menu'); 2180 2181 /** 2182 * Execute the 1-Click Starter Site Import safely & idempotently 2183 */ 2184 function nexversal_execute_starter_import() { 2185 if (!current_user_can('manage_options')) { 2186 return new WP_Error('forbidden', esc_html__('Permission denied.', 'nexversal')); 2187 } 2188 2189 $summary = array( 2190 'pages' => 0, 2191 'categories' => 0, 2192 'posts' => 0, 2193 'menu' => false, 2194 ); 2195 2196 // 1. Categories 2197 $categories_data = array( 2198 'Artificial Intelligence' => 'Cutting-edge AI models, autonomous agents, and machine learning insights.', 2199 'Cloud Computing' => 'High-scale cloud architectures, serverless systems, and DevOps engineering.', 2200 'SEO & Performance' => 'Search optimization, Core Web Vitals, and page speed engineering.', 2201 'Tech Reviews' => 'Unbiased evaluations of development tools, SaaS, and productivity software.', 2202 ); 2203 2204 $cat_ids = array(); 2205 foreach ($categories_data as $cat_name => $cat_desc) { 2206 $term = term_exists($cat_name, 'category'); 2207 if (!$term) { 2208 $inserted = wp_insert_term($cat_name, 'category', array('description' => $cat_desc)); 2209 if (!is_wp_error($inserted)) { 2210 $cat_ids[$cat_name] = $inserted['term_id']; 2211 $summary['categories']++; 2212 } 2213 } else { 2214 $cat_ids[$cat_name] = is_array($term) ? $term['term_id'] : $term; 2215 } 2216 } 2217 2218 // 2. Core Pages via nexversal_setup_default_content 2219 nexversal_setup_default_content(); 2220 2221 $home_page = get_page_by_path('home'); 2222 $blog_page = get_page_by_path('blog') ?: get_page_by_path('blogs'); 2223 $about_page = get_page_by_path('about') ?: get_page_by_path('about-us'); 2224 $contact_page = get_page_by_path('contact') ?: get_page_by_path('contact-us'); 2225 2226 if ($home_page) $summary['pages']++; 2227 if ($blog_page) $summary['pages']++; 2228 if ($about_page) $summary['pages']++; 2229 if ($contact_page) $summary['pages']++; 2230 2231 // 3. Demo Articles 2232 $demo_articles = array( 2233 array( 2234 'title' => 'Top 10 AI Tools Transforming Modern Software Engineering in 2026', 2235 'category' => 'Artificial Intelligence', 2236 'featured' => '1', 2237 'views' => 2840, 2238 'excerpt' => 'Explore next-generation AI coding assistants, agentic frameworks, and autonomous developer tools reshaping engineering productivity this year.', 2239 'content' => '<!-- wp:paragraph {"dropCap":true} --> 2240 <p class="has-drop-cap">Artificial intelligence is evolving beyond simple code autocomplete widgets into autonomous, agentic pair programmers capable of analyzing large codebases, maintaining documentation integrity, and writing exhaustive automated test suites.</p> 2241 <!-- /wp:paragraph --> 2242 2243 <!-- wp:heading {"level":2} --> 2244 <h2>The Rise of Autonomous Agentic Pair Programmers</h2> 2245 <!-- /wp:heading --> 2246 2247 <!-- wp:paragraph --> 2248 <p>Modern engineering teams are rapidly adopting multimodal agents that parse git repositories, execute local sandbox commands, and verify complex edge cases before proposing pull requests. This dramatically compresses developer feedback loops and eliminates boilerplate engineering.</p> 2249 <!-- /wp:paragraph --> 2250 2251 <!-- wp:heading {"level":3} --> 2252 <h3>Key Architecture Benchmarks for 2026</h3> 2253 <!-- /wp:heading --> 2254 2255 <!-- wp:list --> 2256 <ul> 2257 <li><strong>Sub-second Streaming Throughput:</strong> Instantaneous generation for interactive terminal workflows.</li> 2258 <li><strong>1M+ Token Context Memory:</strong> Complete architectural recall across multi-service repositories.</li> 2259 <li><strong>Strict Compiler Verification:</strong> Zero hallucinations through automated type checking and lint validation.</li> 2260 </ul> 2261 <!-- /wp:list -->', 2262 ), 2263 array( 2264 'title' => 'Comparing Cloud Databases: SQL vs NoSQL for High-Scale Apps', 2265 'category' => 'Cloud Computing', 2266 'featured' => '0', 2267 'views' => 1940, 2268 'excerpt' => 'An architectural deep-dive into transactional ACID guarantees versus horizontal distributed scalability in modern cloud applications.', 2269 'content' => '<p>Choosing the right database architecture is one of the most critical decisions in modern systems engineering. While relational databases like PostgreSQL provide atomic ACID transactions, horizontally scalable document stores excel at high-velocity ingestion.</p><h2>Transactional Guarantees vs Read Latency</h2><p>When engineering for multi-region active-active deployments, distributed consensus protocols require deliberate trade-offs between consistency and partition tolerance.</p>', 2270 ), 2271 array( 2272 'title' => 'The Ultimate Guide to Core Web Vitals and Page Experience', 2273 'category' => 'SEO & Performance', 2274 'featured' => '0', 2275 'views' => 1680, 2276 'excerpt' => 'Master LCP, INP, and CLS optimization techniques to achieve perfect 100/100 PageSpeed scores and maximize organic Google rankings.', 2277 'content' => '<p>Google’s Core Web Vitals are foundational ranking factors that directly dictate search visibility and user retention. Achieving sub-second Largest Contentful Paint (LCP) and minimal Interaction to Next Paint (INP) requires disciplined script management and clean semantic HTML.</p><h2>Optimizing Interaction to Next Paint (INP)</h2><p>Eliminate blocking JavaScript execution on the main thread and leverage modern CSS layout containment to deliver buttery smooth 60fps mobile responsiveness.</p>', 2278 ), 2279 array( 2280 'title' => 'Next-Gen Web Frameworks: Architecture Comparison and Benchmarks', 2281 'category' => 'Tech Reviews', 2282 'featured' => '0', 2283 'views' => 1420, 2284 'excerpt' => 'Comparing server-side rendering, partial hydration, and edge-native architectures across high-performance web platforms.', 2285 'content' => '<p>Modern web development has moved past monolithic client-side JavaScript bundles toward island architectures and edge rendering. By streaming HTML directly from edge nodes, modern platforms achieve instantaneous first paint times.</p>', 2286 ), 2287 ); 2288 2289 foreach ($demo_articles as $art) { 2290 $existing = get_page_by_title($art['title'], OBJECT, 'post'); 2291 if (!$existing) { 2292 $cat_id = $cat_ids[$art['category']] ?? 1; 2293 $new_post_id = wp_insert_post(array( 2294 'post_title' => $art['title'], 2295 'post_content' => $art['content'], 2296 'post_excerpt' => $art['excerpt'], 2297 'post_status' => 'publish', 2298 'post_type' => 'post', 2299 'post_category'=> array($cat_id), 2300 )); 2301 if ($new_post_id && !is_wp_error($new_post_id)) { 2302 if (!empty($art['featured'])) { 2303 update_post_meta($new_post_id, '_nexversal_featured_in_hero', '1'); 2304 } 2305 update_post_meta($new_post_id, '_nexversal_post_views', (int) $art['views']); 2306 $summary['posts']++; 2307 } 2308 } else { 2309 if (!empty($art['featured'])) { 2310 update_post_meta($existing->ID, '_nexversal_featured_in_hero', '1'); 2311 } 2312 } 2313 } 2314 2315 // 4. Ensure Primary Menu is active 2316 $menu_obj = wp_get_nav_menu_object('Main Menu'); 2317 if ($menu_obj) { 2318 $locations = get_theme_mod('nav_menu_locations', array()); 2319 $locations['primary'] = $menu_obj->term_id; 2320 set_theme_mod('nav_menu_locations', $locations); 2321 $summary['menu'] = true; 2322 } 2323 2324 return $summary; 2325 } 2326 2327 /** 2328 * Render Appearance > Starter Site page in WP-Admin 2329 */ 2330 function nexversal_render_starter_site_page() { 2331 if (!current_user_can('manage_options')) { 2332 return; 2333 } 2334 2335 $notice = ''; 2336 if (isset($_POST['nexversal_run_starter_import']) && check_admin_referer('nexversal_starter_import_action', 'nexversal_starter_nonce')) { 2337 $result = nexversal_execute_starter_import(); 2338 if (is_wp_error($result)) { 2339 $notice = '<div class="notice notice-error is-dismissible"><p>' . esc_html($result->get_error_message()) . '</p></div>'; 2340 } else { 2341 $msg = sprintf( 2342 /* translators: 1: categories count, 2: posts count */ 2343 esc_html__('🎉 Starter Site Imported Successfully! Core Pages verified/created, %1$d Categories mapped, %2$d Demo Stories ready, and Primary Navigation Menu linked.', 'nexversal'), 2344 $result['categories'], 2345 $result['posts'] 2346 ); 2347 $notice = '<div class="notice notice-success is-dismissible"><p><strong>' . $msg . '</strong></p></div>'; 2348 } 2349 } 2350 2351 $screenshot_url = get_template_directory_uri() . '/screenshot.png'; 2352 $customizer_url = admin_url('customize.php'); 2353 $home_link = home_url('/'); 2354 $contact_set = admin_url('themes.php?page=nexversal-contact'); 2355 ?> 2356 <div class="wrap" style="max-width: 1040px; margin-top: 24px;"> 2357 <div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; margin-bottom: 20px;"> 2358 <div> 2359 <h1 style="display: flex; align-items: center; gap: 12px; font-weight: 800; font-size: 26px; margin: 0; color: #0f172a;"> 2360 <span class="dashicons dashicons-admin-site-alt3" style="font-size: 32px; width: 32px; height: 32px; color: #4f46e5;"></span> 2361 <?php esc_html_e('Nexversal 1-Click Starter Site', 'nexversal'); ?> 2362 </h1> 2363 <p style="color: #64748b; font-size: 14px; margin: 6px 0 0 0;"> 2364 <?php esc_html_e('Set up a complete, jaw-dropping Tech & Magazine website in 5 seconds. Zero manual configuration required.', 'nexversal'); ?> 2365 </p> 2366 </div> 2367 <div style="display: flex; gap: 10px;"> 2368 <a href="<?php echo esc_url($home_link); ?>" target="_blank" class="button button-secondary"> 2369 <span class="dashicons dashicons-external" style="vertical-align: middle; margin-top: -2px;"></span> 2370 <?php esc_html_e('Visit Live Site', 'nexversal'); ?> 2371 </a> 2372 <a href="<?php echo esc_url($customizer_url); ?>" class="button button-secondary"> 2373 <span class="dashicons dashicons-admin-appearance" style="vertical-align: middle; margin-top: -2px;"></span> 2374 <?php esc_html_e('Live Customizer', 'nexversal'); ?> 2375 </a> 2376 </div> 2377 </div> 2378 2379 <?php echo $notice; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> 2380 2381 <div style="display: grid; grid-template-columns: 1.15fr 0.85fr; gap: 28px; margin-top: 20px; align-items: start;"> 2382 <!-- Left Card: Starter Demo Preview & Action --> 2383 <div style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; overflow: hidden; box-shadow: 0 4px 16px rgba(0,0,0,0.04);"> 2384 <div style="position: relative; overflow: hidden; max-height: 280px; background: #0f172a;"> 2385 <img src="<?php echo esc_url($screenshot_url); ?>" alt="<?php esc_attr_e('Nexversal Demo Preview', 'nexversal'); ?>" style="width: 100%; height: auto; display: block; object-fit: cover;" /> 2386 <div style="position: absolute; top: 14px; left: 14px; background: rgba(15,23,42,0.85); color: #38bdf8; padding: 4px 12px; border-radius: 20px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; backdrop-filter: blur(4px);"> 2387 ⚡ <?php esc_html_e('Tech & Magazine Edition', 'nexversal'); ?> 2388 </div> 2389 </div> 2390 2391 <div style="padding: 28px 30px;"> 2392 <h3 style="font-size: 18px; font-weight: 800; color: #0f172a; margin: 0 0 10px 0;"> 2393 <?php esc_html_e('Ready-Made Tech & Magazine Demo', 'nexversal'); ?> 2394 </h3> 2395 <p style="color: #64748b; font-size: 14px; line-height: 1.6; margin: 0 0 20px 0;"> 2396 <?php esc_html_e('Clicking the button below instantly configures your entire site with our flagship Tech & Magazine structure. Perfectly pre-linked and optimized for Google Core Web Vitals.', 'nexversal'); ?> 2397 </p> 2398 2399 <form method="post" action=""> 2400 <?php wp_nonce_field('nexversal_starter_import_action', 'nexversal_starter_nonce'); ?> 2401 2402 <div style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 14px 16px; margin-bottom: 22px;"> 2403 <label style="display: flex; align-items: flex-start; gap: 10px; cursor: pointer; font-size: 13px; font-weight: 600; color: #1e293b;"> 2404 <input type="checkbox" checked="checked" disabled="disabled" style="margin-top: 2px;" /> 2405 <div> 2406 <span><?php esc_html_e('Safe & Idempotent Onboarding Mode', 'nexversal'); ?></span> 2407 <span style="display: block; font-size: 11px; font-weight: 400; color: #64748b; margin-top: 2px;"> 2408 <?php esc_html_e('Existing posts, pages, and media are completely safe and will never be deleted or duplicated.', 'nexversal'); ?> 2409 </span> 2410 </div> 2411 </label> 2412 </div> 2413 2414 <button type="submit" name="nexversal_run_starter_import" class="button button-primary button-hero" style="background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%); border: none; font-weight: 700; font-size: 15px; padding: 12px 28px; height: auto; border-radius: 8px; box-shadow: 0 4px 14px rgba(79, 70, 229, 0.35); cursor: pointer; display: inline-flex; align-items: center; gap: 8px;"> 2415 <span>🚀 <?php esc_html_e('Import Starter Site (1-Click)', 'nexversal'); ?></span> 2416 </button> 2417 </form> 2418 </div> 2419 </div> 2420 2421 <!-- Right Card: What Gets Imported & Features --> 2422 <div style="display: flex; flex-direction: column; gap: 20px;"> 2423 <div style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 26px; box-shadow: 0 4px 16px rgba(0,0,0,0.04);"> 2424 <h3 style="font-size: 16px; font-weight: 700; color: #0f172a; margin: 0 0 16px 0; display: flex; align-items: center; gap: 8px;"> 2425 <span class="dashicons dashicons-yes-alt" style="color: #10b981;"></span> 2426 <?php esc_html_e('What’s Included in This Starter Site:', 'nexversal'); ?> 2427 </h3> 2428 2429 <ul style="margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 14px;"> 2430 <li style="display: flex; gap: 12px; font-size: 13px; color: #334155; line-height: 1.5;"> 2431 <span style="color: #4f46e5; font-size: 16px; font-weight: 700;">✓</span> 2432 <div> 2433 <strong><?php esc_html_e('4 Complete Core Pages:', 'nexversal'); ?></strong><br> 2434 <?php esc_html_e('Home (2-Column Hero), Blogs (4-Column Grid), About Us, and Contact Us.', 'nexversal'); ?> 2435 </div> 2436 </li> 2437 <li style="display: flex; gap: 12px; font-size: 13px; color: #334155; line-height: 1.5;"> 2438 <span style="color: #4f46e5; font-size: 16px; font-weight: 700;">✓</span> 2439 <div> 2440 <strong><?php esc_html_e('2-Column Hero & Featured Story Card:', 'nexversal'); ?></strong><br> 2441 <?php esc_html_e('Main headline on the left, trending featured article card automatically pinned on the right.', 'nexversal'); ?> 2442 </div> 2443 </li> 2444 <li style="display: flex; gap: 12px; font-size: 13px; color: #334155; line-height: 1.5;"> 2445 <span style="color: #4f46e5; font-size: 16px; font-weight: 700;">✓</span> 2446 <div> 2447 <strong><?php esc_html_e('Pre-Mapped Navigation Menu:', 'nexversal'); ?></strong><br> 2448 <?php esc_html_e('Auto-connected to Primary Menu location with clean header links.', 'nexversal'); ?> 2449 </div> 2450 </li> 2451 <li style="display: flex; gap: 12px; font-size: 13px; color: #334155; line-height: 1.5;"> 2452 <span style="color: #4f46e5; font-size: 16px; font-weight: 700;">✓</span> 2453 <div> 2454 <strong><?php esc_html_e('Tech Categories & Sample Articles:', 'nexversal'); ?></strong><br> 2455 <?php esc_html_e('AI, Cloud, SEO, and Reviews categories with trending tags.', 'nexversal'); ?> 2456 </div> 2457 </li> 2458 </ul> 2459 </div> 2460 2461 <div style="background: #f1f5f9; border: 1px solid #cbd5e1; border-radius: 12px; padding: 20px 22px;"> 2462 <h4 style="font-size: 14px; font-weight: 700; color: #1e293b; margin: 0 0 8px 0;"> 2463 <?php esc_html_e('Need to customize details afterwards?', 'nexversal'); ?> 2464 </h4> 2465 <p style="font-size: 12px; color: #64748b; line-height: 1.5; margin: 0 0 12px 0;"> 2466 <?php esc_html_e('You can change contact emails, social links, and brand colors anytime from Appearance > Contact Settings or Appearance > Customize.', 'nexversal'); ?> 2467 </p> 2468 <a href="<?php echo esc_url($contact_set); ?>" class="button button-small" style="font-weight: 600;"> 2469 <?php esc_html_e('Open Contact Settings', 'nexversal'); ?> 2470 </a> 2471 </div> 2472 </div> 2473 </div> 2474 </div> 2475 <?php 2476 } 2477 2478 2479 /** 2480 * ========================================================================== 2481 * Hero Featured Article & Post Views System 2482 * ========================================================================== 2483 */ 2484 2485 /** 2486 * Register Meta Box in Post Editor for Hero Featured Article 2487 */ 2488 function nexversal_add_featured_post_meta_box() { 2489 add_meta_box( 2490 'nexversal_featured_post_box', 2491 esc_html__('★ Hero Featured Article', 'nexversal'), 2492 'nexversal_render_featured_post_meta_box', 2493 'post', 2494 'side', 2495 'high' 2496 ); 2497 } 2498 add_action('add_meta_boxes', 'nexversal_add_featured_post_meta_box'); 2499 2500 /** 2501 * Render the Meta Box in post editor 2502 */ 2503 function nexversal_render_featured_post_meta_box($post) { 2504 wp_nonce_field('nexversal_featured_post_nonce_action', 'nexversal_featured_post_nonce'); 2505 $is_featured = get_post_meta($post->ID, '_nexversal_featured_in_hero', true); 2506 $views = (int) get_post_meta($post->ID, '_nexversal_post_views', true); 2507 ?> 2508 <p> 2509 <label for="nexversal_featured_in_hero" style="font-weight: 600; cursor: pointer; display: flex; align-items: flex-start; gap: 8px;"> 2510 <input type="checkbox" name="nexversal_featured_in_hero" id="nexversal_featured_in_hero" value="1" <?php checked($is_featured, '1'); ?> style="margin-top: 2px;" /> 2511 <span><?php esc_html_e('Feature in Homepage Hero', 'nexversal'); ?></span> 2512 </label> 2513 </p> 2514 <p class="description" style="font-size: 12px; color: #64748b; line-height: 1.4; margin-top: 6px;"> 2515 <?php esc_html_e('Display this article at the top of the homepage next to the main headline. If none selected, the theme automatically displays your trending / most-viewed article.', 'nexversal'); ?> 2516 </p> 2517 <div style="margin-top: 12px; padding-top: 8px; border-top: 1px dashed #cbd5e1; font-size: 12px; color: #64748b; display: flex; align-items: center; justify-content: space-between;"> 2518 <span><?php esc_html_e('Article Views:', 'nexversal'); ?></span> 2519 <strong style="color: #0f172a; font-size: 13px;"><?php echo number_format_i18n($views); ?></strong> 2520 </div> 2521 <?php 2522 } 2523 2524 /** 2525 * Save the Meta Box value 2526 */ 2527 function nexversal_save_featured_post_meta_box($post_id) { 2528 if (!isset($_POST['nexversal_featured_post_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nexversal_featured_post_nonce'])), 'nexversal_featured_post_nonce_action')) { 2529 return; 2530 } 2531 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { 2532 return; 2533 } 2534 if (!current_user_can('edit_post', $post_id)) { 2535 return; 2536 } 2537 2538 if (!empty($_POST['nexversal_featured_in_hero'])) { 2539 update_post_meta($post_id, '_nexversal_featured_in_hero', '1'); 2540 } else { 2541 delete_post_meta($post_id, '_nexversal_featured_in_hero'); 2542 } 2543 } 2544 add_action('save_post_post', 'nexversal_save_featured_post_meta_box'); 2545 2546 /** 2547 * Track Post Views on single article visits 2548 */ 2549 function nexversal_track_post_views() { 2550 if (!is_single() || is_preview()) { 2551 return; 2552 } 2553 global $post; 2554 if (!$post || !isset($post->ID)) { 2555 return; 2556 } 2557 $views = (int) get_post_meta($post->ID, '_nexversal_post_views', true); 2558 update_post_meta($post->ID, '_nexversal_post_views', $views + 1); 2559 } 2560 add_action('template_redirect', 'nexversal_track_post_views'); 2561 2562 /** 2563 * Add Featured and Views columns to WP-Admin posts list 2564 */ 2565 function nexversal_add_post_columns($columns) { 2566 $columns['nx_featured'] = esc_html__('Hero Card', 'nexversal'); 2567 $columns['nx_views'] = esc_html__('Views', 'nexversal'); 2568 return $columns; 2569 } 2570 add_filter('manage_posts_columns', 'nexversal_add_post_columns'); 2571 2572 function nexversal_custom_post_columns($column, $post_id) { 2573 if ('nx_featured' === $column) { 2574 $is_feat = get_post_meta($post_id, '_nexversal_featured_in_hero', true); 2575 if ('1' === $is_feat) { 2576 echo '<span style="color: #f59e0b; font-weight: 700;">★ ' . esc_html__('Featured', 'nexversal') . '</span>'; 2577 } else { 2578 echo '<span style="color: #94a3b8;">—</span>'; 2579 } 2580 } elseif ('nx_views' === $column) { 2581 $views = (int) get_post_meta($post_id, '_nexversal_post_views', true); 2582 echo '<strong>' . number_format_i18n($views) . '</strong>'; 2583 } 2584 } 2585 add_action('manage_posts_custom_column', 'nexversal_custom_post_columns', 10, 2); 2586 2587 /** 2588 * Retrieve the Hero Featured Post: 2589 * 1. Post manually checked '_nexversal_featured_in_hero' 2590 * 2. Fallback: Post with highest views 2591 * 3. Fallback: Sticky post 2592 * 4. Fallback: Latest post 2593 */ 2594 function nexversal_get_hero_featured_post() { 2595 // 1. Manually selected 2596 $args_selected = array( 2597 'post_type' => 'post', 2598 'post_status' => 'publish', 2599 'posts_per_page' => 1, 2600 'meta_key' => '_nexversal_featured_in_hero', 2601 'meta_value' => '1', 2602 'orderby' => 'date', 2603 'order' => 'DESC', 2604 ); 2605 $selected_query = new WP_Query($args_selected); 2606 if ($selected_query->have_posts()) { 2607 $p = $selected_query->posts[0]; 2608 wp_reset_postdata(); 2609 return $p; 2610 } 2611 2612 // 2. Fallback: Highest views 2613 $args_views = array( 2614 'post_type' => 'post', 2615 'post_status' => 'publish', 2616 'posts_per_page' => 1, 2617 'meta_key' => '_nexversal_post_views', 2618 'orderby' => 'meta_value_num', 2619 'order' => 'DESC', 2620 ); 2621 $views_query = new WP_Query($args_views); 2622 if ($views_query->have_posts() && (int) get_post_meta($views_query->posts[0]->ID, '_nexversal_post_views', true) > 0) { 2623 $p = $views_query->posts[0]; 2624 wp_reset_postdata(); 2625 return $p; 2626 } 2627 2628 // 3. Fallback: Sticky posts 2629 $sticky = get_option('sticky_posts'); 2630 if (!empty($sticky)) { 2631 $args_sticky = array( 2632 'post_type' => 'post', 2633 'post_status' => 'publish', 2634 'posts_per_page' => 1, 2635 'post__in' => $sticky, 2636 'ignore_sticky_posts' => 1, 2637 ); 2638 $sticky_query = new WP_Query($args_sticky); 2639 if ($sticky_query->have_posts()) { 2640 $p = $sticky_query->posts[0]; 2641 wp_reset_postdata(); 2642 return $p; 2643 } 2644 } 2645 2646 // 4. Fallback: Latest published post 2647 $args_latest = array( 2648 'post_type' => 'post', 2649 'post_status' => 'publish', 2650 'posts_per_page' => 1, 2651 'orderby' => 'date', 2652 'order' => 'DESC', 2653 ); 2654 $latest_query = new WP_Query($args_latest); 2655 if ($latest_query->have_posts()) { 2656 $p = $latest_query->posts[0]; 2657 wp_reset_postdata(); 2658 return $p; 2659 } 2660 2661 return null; 2662 } 2663 2664 /** 2665 * Render the Hero Featured Article Card HTML 2666 */ 2667 function nexversal_get_hero_featured_card_html() { 2668 $featured_post = nexversal_get_hero_featured_post(); 2669 if (!$featured_post) { 2670 return ''; 2671 } 2672 2673 $post_id = $featured_post->ID; 2674 $title = get_the_title($post_id); 2675 $permalink = get_permalink($post_id); 2676 $author_id = $featured_post->post_author; 2677 $author = get_the_author_meta('display_name', $author_id); 2678 $cats = get_the_category($post_id); 2679 $cat_name = !empty($cats) ? $cats[0]->name : __('Article', 'nexversal'); 2680 $cat_link = !empty($cats) ? get_category_link($cats[0]->term_id) : '#'; 2681 $is_manual = ('1' === get_post_meta($post_id, '_nexversal_featured_in_hero', true)); 2682 2683 // Calculate reading time 2684 $content = get_post_field('post_content', $post_id); 2685 $word_count = str_word_count(strip_tags($content)); 2686 $reading_time = max(1, ceil($word_count / 200)) . ' ' . esc_html__('min read', 'nexversal'); 2687 2688 // Get thumbnail or high-tech fallback 2689 $has_thumb = has_post_thumbnail($post_id); 2690 $thumb_html = ''; 2691 if ($has_thumb) { 2692 $thumb_html = get_the_post_thumbnail($post_id, 'medium_large', array( 2693 'class' => 'nx-hero-card-img', 2694 'loading' => 'eager', 2695 'alt' => esc_attr($title), 2696 )); 2697 } else { 2698 $thumb_html = '<div class="nx-hero-card-img-fallback">' . 2699 '<svg width="54" height="54" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>' . 2700 '</div>'; 2701 } 2702 2703 $raw_excerpt = get_the_excerpt($post_id); 2704 if (empty($raw_excerpt)) { 2705 $raw_excerpt = wp_strip_all_tags($content); 2706 } 2707 $excerpt = wp_trim_words($raw_excerpt, 16, '...'); 2708 2709 ob_start(); 2710 ?> 2711 <article class="nx-hero-featured-card"> 2712 <div class="nx-hero-card-badge-wrap"> 2713 <span class="nx-hero-card-featured-tag"> 2714 <svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor"><polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/></svg> 2715 <?php echo $is_manual ? esc_html__('Featured Story', 'nexversal') : esc_html__('Trending Story', 'nexversal'); ?> 2716 </span> 2717 </div> 2718 <a href="<?php echo esc_url($permalink); ?>" class="nx-hero-card-thumb-wrap" tabindex="-1" aria-hidden="true"> 2719 <?php echo $thumb_html; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?> 2720 </a> 2721 <div class="nx-hero-card-body"> 2722 <div class="nx-hero-card-meta-top"> 2723 <a href="<?php echo esc_url($cat_link); ?>" class="nx-hero-card-cat"> 2724 <?php echo esc_html($cat_name); ?> 2725 </a> 2726 <span class="nx-hero-card-time"><?php echo esc_html($reading_time); ?></span> 2727 </div> 2728 <h3 class="nx-hero-card-title"> 2729 <a href="<?php echo esc_url($permalink); ?>"><?php echo esc_html($title); ?></a> 2730 </h3> 2731 <?php if (!empty($excerpt)) : ?> 2732 <p class="nx-hero-card-excerpt"><?php echo esc_html($excerpt); ?></p> 2733 <?php endif; ?> 2734 <div class="nx-hero-card-footer"> 2735 <div class="nx-hero-card-author"> 2736 <?php echo get_avatar($author_id, 24, '', '', array('class' => 'nx-hero-card-avatar')); ?> 2737 <span><?php echo esc_html($author); ?></span> 2738 </div> 2739 <a href="<?php echo esc_url($permalink); ?>" class="nx-hero-card-read-link"> 2740 <span><?php esc_html_e('Read Story', 'nexversal'); ?></span> 2741 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg> 2742 </a> 2743 </div> 2744 </div> 2745 </article> 2746 <?php 2747 return ob_get_clean(); 2748 } 2749 2750 /** 2751 * Filter render_block to inject the Featured Card into any nx-hero-section group on Home page 2752 */ 2753 function nexversal_render_hero_block_enhancement($block_content, $block) { 2754 if ('core/group' === ($block['blockName'] ?? '') && !empty($block['attrs']['className']) && strpos($block['attrs']['className'], 'nx-hero-section') !== false && strpos($block_content, 'nx-hero-grid') === false) { 2755 $pos_gt = strpos($block_content, '>'); 2756 $pos_close = strrpos($block_content, '</div>'); 2757 if ($pos_gt !== false && $pos_close !== false && $pos_close > $pos_gt) { 2758 $open_tag = substr($block_content, 0, $pos_gt + 1); 2759 $inner_content = substr($block_content, $pos_gt + 1, $pos_close - ($pos_gt + 1)); 2760 $featured_card = nexversal_get_hero_featured_card_html(); 2761 if (!empty($featured_card)) { 2762 $featured_col = '<div class="nx-hero-featured-col">' . $featured_card . '</div>'; 2763 return $open_tag . '<div class="nx-hero-grid"><div class="nx-hero-content-col">' . $inner_content . '</div>' . $featured_col . '</div></div>'; 2764 } 2765 } 2766 } 2767 return $block_content; 2768 } 2769 add_filter('render_block', 'nexversal_render_hero_block_enhancement', 10, 2); 2770 2771 /** 2772 * ========================================================================== 2773 * Page Flexibility: Astra-Style Per-Page Layout Settings 2774 * ========================================================================== 2775 */ 2776 2777 /** 2778 * Check if a specific page setting is disabled for the current post/page 2779 */ 2780 function nexversal_is_meta_disabled($feature, $post_id = 0) { 2781 if (!$post_id) { 2782 if (is_singular()) { 2783 $post_id = get_the_ID(); 2784 } elseif (is_front_page() && 'page' === get_option('show_on_front')) { 2785 $post_id = (int) get_option('page_on_front'); 2786 } 2787 } 2788 if (!$post_id) { 2789 return false; 2790 } 2791 $val = get_post_meta($post_id, '_nexversal_' . $feature, true); 2792 return ('1' === $val); 2793 } 2794 2795 /** 2796 * Retrieve the active content layout for the current page 2797 */ 2798 function nexversal_get_page_layout($post_id = 0) { 2799 if (!$post_id) { 2800 if (is_singular()) { 2801 $post_id = get_the_ID(); 2802 } elseif (is_front_page() && 'page' === get_option('show_on_front')) { 2803 $post_id = (int) get_option('page_on_front'); 2804 } 2805 } 2806 if (!$post_id) { 2807 return 'default'; 2808 } 2809 $layout = get_post_meta($post_id, '_nexversal_content_layout', true); 2810 return !empty($layout) ? $layout : 'default'; 2811 } 2812 2813 /** 2814 * Register the Page Settings Meta Box for Pages & Posts 2815 */ 2816 function nexversal_add_page_settings_meta_box() { 2817 $screens = array('page', 'post'); 2818 foreach ($screens as $screen) { 2819 add_meta_box( 2820 'nexversal_page_settings_box', 2821 esc_html__('Nexversal Page Settings', 'nexversal'), 2822 'nexversal_render_page_settings_meta_box', 2823 $screen, 2824 'side', 2825 'default' 2826 ); 2827 } 2828 } 2829 add_action('add_meta_boxes', 'nexversal_add_page_settings_meta_box'); 2830 2831 /** 2832 * Render the Page Settings Meta Box in post/page editor 2833 */ 2834 function nexversal_render_page_settings_meta_box($post) { 2835 wp_nonce_field('nexversal_page_settings_nonce_action', 'nexversal_page_settings_nonce'); 2836 2837 $disable_header = get_post_meta($post->ID, '_nexversal_disable_header', true); 2838 $disable_footer = get_post_meta($post->ID, '_nexversal_disable_footer', true); 2839 $disable_title = get_post_meta($post->ID, '_nexversal_disable_title', true); 2840 $disable_breadcrumbs = get_post_meta($post->ID, '_nexversal_disable_breadcrumbs', true); 2841 $disable_sidebar = get_post_meta($post->ID, '_nexversal_disable_sidebar', true); 2842 $content_layout = get_post_meta($post->ID, '_nexversal_content_layout', true); 2843 if (empty($content_layout)) { 2844 $content_layout = 'default'; 2845 } 2846 ?> 2847 <div class="nx-page-meta-box-wrap" style="font-size: 13px;"> 2848 <p style="font-weight: 700; color: #1e293b; margin: 4px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;"> 2849 <?php esc_html_e('Content Layout', 'nexversal'); ?> 2850 </p> 2851 <div style="margin-bottom: 14px;"> 2852 <label style="display: block; margin-bottom: 6px; cursor: pointer;"> 2853 <input type="radio" name="nexversal_content_layout" value="default" <?php checked($content_layout, 'default'); ?> /> 2854 <span><?php esc_html_e('Default Theme Layout', 'nexversal'); ?></span> 2855 </label> 2856 <label style="display: block; margin-bottom: 6px; cursor: pointer;"> 2857 <input type="radio" name="nexversal_content_layout" value="full-width-contained" <?php checked($content_layout, 'full-width-contained'); ?> /> 2858 <span><?php esc_html_e('Full Width (Contained)', 'nexversal'); ?></span> 2859 </label> 2860 <label style="display: block; margin-bottom: 6px; cursor: pointer;"> 2861 <input type="radio" name="nexversal_content_layout" value="full-width-stretched" <?php checked($content_layout, 'full-width-stretched'); ?> /> 2862 <span style="font-weight: 600; color: #4f46e5;"><?php esc_html_e('Full Width Stretched (Canvas)', 'nexversal'); ?></span> 2863 </label> 2864 <p class="description" style="font-size: 11px; color: #64748b; margin-top: 4px; line-height: 1.4;"> 2865 <?php esc_html_e('Canvas is ideal for Elementor or full-screen custom landing pages.', 'nexversal'); ?> 2866 </p> 2867 </div> 2868 2869 <p style="font-weight: 700; color: #1e293b; margin: 12px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;"> 2870 <?php esc_html_e('Disable Elements', 'nexversal'); ?> 2871 </p> 2872 <div style="display: flex; flex-direction: column; gap: 8px;"> 2873 <label style="cursor: pointer; display: flex; align-items: center; gap: 8px;"> 2874 <input type="checkbox" name="nexversal_disable_header" value="1" <?php checked($disable_header, '1'); ?> /> 2875 <span><?php esc_html_e('Disable Header', 'nexversal'); ?></span> 2876 </label> 2877 2878 <label style="cursor: pointer; display: flex; align-items: center; gap: 8px;"> 2879 <input type="checkbox" name="nexversal_disable_footer" value="1" <?php checked($disable_footer, '1'); ?> /> 2880 <span><?php esc_html_e('Disable Footer', 'nexversal'); ?></span> 2881 </label> 2882 2883 <label style="cursor: pointer; display: flex; align-items: center; gap: 8px;"> 2884 <input type="checkbox" name="nexversal_disable_title" value="1" <?php checked($disable_title, '1'); ?> /> 2885 <span><?php esc_html_e('Disable Page Title', 'nexversal'); ?></span> 2886 </label> 2887 2888 <label style="cursor: pointer; display: flex; align-items: center; gap: 8px;"> 2889 <input type="checkbox" name="nexversal_disable_breadcrumbs" value="1" <?php checked($disable_breadcrumbs, '1'); ?> /> 2890 <span><?php esc_html_e('Disable Breadcrumbs', 'nexversal'); ?></span> 2891 </label> 2892 2893 <label style="cursor: pointer; display: flex; align-items: center; gap: 8px;"> 2894 <input type="checkbox" name="nexversal_disable_sidebar" value="1" <?php checked($disable_sidebar, '1'); ?> /> 2895 <span><?php esc_html_e('Disable Sidebar (Force 100% Width)', 'nexversal'); ?></span> 2896 </label> 2897 </div> 2898 </div> 2899 <?php 2900 } 2901 2902 /** 2903 * Save Page Settings Meta Box 2904 */ 2905 function nexversal_save_page_settings_meta_box($post_id) { 2906 if (!isset($_POST['nexversal_page_settings_nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nexversal_page_settings_nonce'])), 'nexversal_page_settings_nonce_action')) { 2907 return; 2908 } 2909 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { 2910 return; 2911 } 2912 if (!current_user_can('edit_post', $post_id)) { 2913 return; 2914 } 2915 2916 $checkbox_keys = array( 2917 'disable_header', 2918 'disable_footer', 2919 'disable_title', 2920 'disable_breadcrumbs', 2921 'disable_sidebar', 2922 ); 2923 2924 foreach ($checkbox_keys as $key) { 2925 if (!empty($_POST['nexversal_' . $key])) { 2926 update_post_meta($post_id, '_nexversal_' . $key, '1'); 2927 } else { 2928 delete_post_meta($post_id, '_nexversal_' . $key); 2929 } 2930 } 2931 2932 if (isset($_POST['nexversal_content_layout'])) { 2933 $layout = sanitize_key($_POST['nexversal_content_layout']); 2934 if (in_array($layout, array('default', 'full-width-contained', 'full-width-stretched'), true)) { 2935 update_post_meta($post_id, '_nexversal_content_layout', $layout); 2936 } 2937 } 2938 } 2939 add_action('save_post', 'nexversal_save_page_settings_meta_box'); 2940 2941 /** 2942 * Filter body_class to inject layout & disabled classes for CSS hooks 2943 */ 2944 function nexversal_page_settings_body_classes($classes) { 2945 $post_id = 0; 2946 if (is_singular()) { 2947 $post_id = get_the_ID(); 2948 } elseif (is_front_page() && 'page' === get_option('show_on_front')) { 2949 $post_id = (int) get_option('page_on_front'); 2950 } elseif (is_home() && 'page' === get_option('show_on_front')) { 2951 $post_id = (int) get_option('page_for_posts'); 2952 } 2953 2954 if ($post_id) { 2955 $layout = nexversal_get_page_layout($post_id); 2956 if ('full-width-stretched' === $layout) { 2957 $classes[] = 'nx-layout-stretched'; 2958 } elseif ('full-width-contained' === $layout) { 2959 $classes[] = 'nx-layout-contained'; 2960 } 2961 2962 if (nexversal_is_meta_disabled('disable_header', $post_id)) { 2963 $classes[] = 'nx-no-header'; 2964 } 2965 if (nexversal_is_meta_disabled('disable_footer', $post_id)) { 2966 $classes[] = 'nx-no-footer'; 2967 } 2968 if (nexversal_is_meta_disabled('disable_title', $post_id)) { 2969 $classes[] = 'nx-no-title'; 2970 } 2971 if (nexversal_is_meta_disabled('disable_breadcrumbs', $post_id)) { 2972 $classes[] = 'nx-no-breadcrumbs'; 2973 } 2974 if (nexversal_is_meta_disabled('disable_sidebar', $post_id)) { 2975 $classes[] = 'nx-no-sidebar'; 2976 } 2977 } 2978 2979 // Auto-detect templates or conditions without sidebar 2980 if (is_page_template('template-blog.php') || is_page_template('template-full-width.php') || !is_active_sidebar('sidebar-1')) { 2981 if (!in_array('nx-no-sidebar', $classes, true)) { 2982 $classes[] = 'nx-no-sidebar'; 2983 } 2984 } 2985 2986 return array_unique($classes); 2987 } 2988 add_filter('body_class', 'nexversal_page_settings_body_classes'); 2989 2990 2991 2992 2993 -
nexversal/3.5.9/header.php
r357433 r357886 1 1 <?php 2 2 /** 3 * Security: Prevent direct script access 3 * Header template for Nexversal Theme 4 * 5 * @package Nexversal 6 * @author Nexes Agency & Muhammad Shahid 7 * @copyright 2026 Nexes Agency & Muhammad Shahid 8 * @license GNU General Public License v2 or later 9 * @link https://nexversal.com 4 10 */ 11 5 12 if (!defined('ABSPATH')) { 6 13 exit; 7 14 } 8 9 /**10 * Header template11 *12 * @package Nexversal13 */14 15 ?><!DOCTYPE html> 15 16 <html <?php language_attributes(); ?>> … … 29 30 <?php endif; ?> 30 31 32 <?php if (!function_exists('nexversal_is_meta_disabled') || !nexversal_is_meta_disabled('disable_header')) : ?> 31 33 <header id="masthead" class="site-header"> 32 34 <div class="container"> … … 59 61 'container' => false, 60 62 'fallback_cb' => function () { 61 echo '<ul >';63 echo '<ul id="primary-menu" class="menu">'; 62 64 echo '<li><a href="' . esc_url(home_url('/')) . '">' . esc_html__('Home', 'nexversal') . '</a></li>'; 63 wp_list_pages(array( 64 'title_li' => '', 65 'depth' => 1, 66 'number' => 5, 67 )); 65 66 $blog_url = function_exists('nexversal_get_blogs_page_url') ? nexversal_get_blogs_page_url() : home_url('/blog/'); 67 $about_url = function_exists('nexversal_get_about_page_url') ? nexversal_get_about_page_url() : home_url('/about/'); 68 $contact_url = function_exists('nexversal_get_contact_page_url') ? nexversal_get_contact_page_url() : home_url('/contact/'); 69 70 echo '<li><a href="' . esc_url($blog_url) . '">' . esc_html__('Blogs', 'nexversal') . '</a></li>'; 71 echo '<li><a href="' . esc_url($about_url) . '">' . esc_html__('About Us', 'nexversal') . '</a></li>'; 72 echo '<li><a href="' . esc_url($contact_url) . '">' . esc_html__('Contact', 'nexversal') . '</a></li>'; 68 73 echo '</ul>'; 69 74 }, … … 126 131 endif; 127 132 ?> 133 <?php endif; // End disable_header check ?> 128 134 -
nexversal/3.5.9/inc/ad-manager.php
r357433 r357886 1 1 <?php 2 2 /** 3 * Nexversal Built-in Ad & Monetization Engine (PRO Feature - $15/year) 4 * 5 * Provides ready-to-use AdSense, Mediavine, and custom banner ad slots 6 * with automatic in-article paragraph injection and responsive containers. 7 * Integrates with Lemon Squeezy & cryptographic license verification. 3 * Nexversal Built-in Ad & Monetization Engine (v3.5.0) 4 * 5 * Beginner-Friendly & Powerful Ad Architecture: 6 * - 1-Click Google Auto-Ads (Just enter Publisher ID) 7 * - Free Plan: Up to 3 Simultaneous Active Ads (Dropdown Position Selectors) 8 * - Pro Plan ($15/yr): Unlimited Simultaneous Ads & Ad-Blocker Bypass 9 * - Automatic Worldwide Geo-Targeting (Visitors in 195+ countries see ads in their local currency) 10 * - Real-Time Impressions, Clicks & CTR Analytics Dashboard 8 11 * 9 12 * @package Nexversal 10 * @version 2.3.0 13 * @author Nexes Agency & Muhammad Shahid 14 * @copyright 2026 Nexes Agency & Muhammad Shahid 15 * @license GNU General Public License v2 or later 16 * @link https://nexversal.com 17 * @version 3.5.0 11 18 */ 12 19 … … 14 21 exit; 15 22 } 23 24 /** 25 * Load AdSense Cumulative Layout Shift (CLS) Prevention Engine 26 */ 27 require_once get_template_directory() . '/inc/ad-cls-optimizer.php'; 16 28 17 29 /** … … 35 47 return esc_url($custom_url); 36 48 } 37 // Direct Lemon Squeezy Store Checkout38 49 return apply_filters('nexversal_pro_checkout_url', 'https://nexversal.lemonsqueezy.com/checkout/buy/nexversal-pro'); 50 } 51 52 /** 53 * Detect visitor country code automatically using Cloudflare, Fastly, AWS, or server geo-headers. 54 * Supports all 195+ countries worldwide (US, GB, CA, AU, DE, AE, SA, PK, IN, etc.). 55 * 56 * @return string ISO 2-letter country code or 'XX' 57 */ 58 function nexversal_get_visitor_country() { 59 $country = ''; 60 61 // 1. Cloudflare IP-Geolocation header (Cloudflare handles 195+ countries globally) 62 if (!empty($_SERVER['HTTP_CF_IPCOUNTRY'])) { 63 $country = sanitize_text_field(wp_unslash($_SERVER['HTTP_CF_IPCOUNTRY'])); 64 } 65 // 2. AWS CloudFront Geolocation header 66 elseif (!empty($_SERVER['HTTP_CLOUDFRONT_VIEWER_COUNTRY'])) { 67 $country = sanitize_text_field(wp_unslash($_SERVER['HTTP_CLOUDFRONT_VIEWER_COUNTRY'])); 68 } 69 // 3. Fastly / Akamai / Reverse Proxy Country header 70 elseif (!empty($_SERVER['HTTP_X_COUNTRY_CODE'])) { 71 $country = sanitize_text_field(wp_unslash($_SERVER['HTTP_X_COUNTRY_CODE'])); 72 } 73 // 4. Standard GeoIP module (Apache/Nginx mod_geoip or MaxMind) 74 elseif (!empty($_SERVER['GEOIP_COUNTRY_CODE'])) { 75 $country = sanitize_text_field(wp_unslash($_SERVER['GEOIP_COUNTRY_CODE'])); 76 } elseif (!empty($_SERVER['HTTP_X_GEOIP_COUNTRY'])) { 77 $country = sanitize_text_field(wp_unslash($_SERVER['HTTP_X_GEOIP_COUNTRY'])); 78 } 79 80 $country = strtoupper(trim($country)); 81 if (!empty($country) && strlen($country) === 2 && ctype_alpha($country)) { 82 return apply_filters('nexversal_visitor_country', $country); 83 } 84 85 return apply_filters('nexversal_visitor_country', 'XX'); 39 86 } 40 87 … … 100 147 $body = wp_remote_retrieve_body($response); 101 148 $data = json_decode($body, true); 149 102 150 if (isset($data['activated']) && true === $data['activated']) { 103 151 return array( … … 117 165 function nexversal_pro_admin_menu() { 118 166 add_theme_page( 119 esc_html__('Nexversal PRO ($15/year)', 'nexversal'),167 esc_html__('Nexversal PRO & Ad Monetization', 'nexversal'), 120 168 esc_html__('Nexversal PRO ⚡', 'nexversal'), 121 169 'manage_options', … … 127 175 128 176 /** 129 * Handle License Activation / Deactivation Form Submission177 * Handle License & Analytics Form Submissions 130 178 */ 131 179 function nexversal_handle_license_action() { … … 177 225 exit; 178 226 } 227 228 // Handle Reset Analytics 229 if (isset($_POST['nexversal_reset_ad_stats'])) { 230 delete_option('nexversal_ad_analytics'); 231 wp_safe_redirect(add_query_arg(array('page' => 'nexversal-pro', 'stats_reset' => '1'), admin_url('themes.php'))); 232 exit; 233 } 179 234 } 180 235 add_action('admin_init', 'nexversal_handle_license_action'); 181 236 182 237 /** 183 * Render the Appearance > Nexversal PRO Admin Page 238 * ========================================================================== 239 * Virtual ads.txt Manager (Dynamic URL Interception & IAB Spec Compliance) 240 * ========================================================================== 241 */ 242 243 /** 244 * Sanitize ads.txt content according to IAB Tech Lab Ads.txt Specification 245 * 246 * @param string $raw_content 247 * @return string Cleaned plain-text records 248 */ 249 function nexversal_sanitize_ads_txt($raw_content) { 250 if (!is_string($raw_content)) { 251 return ''; 252 } 253 254 // 1. Strip all HTML, XML, and PHP tags to eliminate XSS/injection risks 255 $content = wp_strip_all_tags($raw_content); 256 257 // 2. Normalize Windows (\r\n) and legacy Mac (\r) line breaks to standard Unix LF (\n) 258 $content = str_replace(array("\r\n", "\r"), "\n", $content); 259 $lines = explode("\n", $content); 260 $clean_lines = array(); 261 262 foreach ($lines as $line) { 263 $trimmed = trim($line); 264 265 // Preserve intentional blank lines for readability/formatting 266 if ($trimmed === '') { 267 $clean_lines[] = ''; 268 continue; 269 } 270 271 // Restrict strictly to standard printable ASCII characters (0x20 space through 0x7E tilde) 272 // Strips any hidden control codes, binary artifacts, null bytes, or invalid UTF-8 sequences 273 $sanitized_line = preg_replace('/[^\x20-\x7E]/', '', $trimmed); 274 $clean_lines[] = $sanitized_line; 275 } 276 277 return implode("\n", $clean_lines); 278 } 279 280 /** 281 * Register top-priority rewrite rule for virtual /ads.txt request 282 */ 283 function nexversal_ads_txt_rewrite_rule() { 284 add_rewrite_rule('^ads\.txt$', 'index.php?nexversal_ads_txt=1', 'top'); 285 } 286 add_action('init', 'nexversal_ads_txt_rewrite_rule'); 287 288 /** 289 * Register query variable for virtual ads.txt handling 290 * 291 * @param array $vars 292 * @return array 293 */ 294 function nexversal_ads_txt_query_vars($vars) { 295 $vars[] = 'nexversal_ads_txt'; 296 return $vars; 297 } 298 add_filter('query_vars', 'nexversal_ads_txt_query_vars'); 299 300 /** 301 * Intercept request early to serve virtual ads.txt dynamically without cPanel file writing 302 * 303 * @param WP|null $wp 304 */ 305 function nexversal_maybe_serve_virtual_ads_txt($wp = null) { 306 $is_ads_txt = false; 307 308 // 1. Check matched WordPress query var 309 if (!empty($wp) && isset($wp->query_vars['nexversal_ads_txt']) && $wp->query_vars['nexversal_ads_txt']) { 310 $is_ads_txt = true; 311 } elseif (get_query_var('nexversal_ads_txt')) { 312 $is_ads_txt = true; 313 } 314 315 // 2. Check matched WordPress request path 316 if (!$is_ads_txt && !empty($wp) && isset($wp->request) && strtolower(trim($wp->request, '/')) === 'ads.txt') { 317 $is_ads_txt = true; 318 } 319 320 // 3. Check raw REQUEST_URI for instant resolution prior to rewrite flushing 321 if (!$is_ads_txt && !empty($_SERVER['REQUEST_URI'])) { 322 $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); 323 if ($path && strtolower(trim($path, '/')) === 'ads.txt') { 324 $is_ads_txt = true; 325 } 326 } 327 328 if ($is_ads_txt) { 329 nexversal_render_virtual_ads_txt_output(); 330 } 331 } 332 add_action('parse_request', 'nexversal_maybe_serve_virtual_ads_txt', 0); 333 add_action('template_redirect', 'nexversal_maybe_serve_virtual_ads_txt', 0); 334 335 /** 336 * Output virtual ads.txt content with strict HTTP headers and clear all output buffers 337 */ 338 function nexversal_render_virtual_ads_txt_output() { 339 $content = get_option('nexversal_ads_txt_content', ''); 340 341 // Set HTTP 200 OK status 342 status_header(200); 343 344 // Send correct Content-Type: text/plain; charset=utf-8 345 header('Content-Type: text/plain; charset=utf-8'); 346 header('X-Content-Type-Options: nosniff'); 347 header('X-Robots-Tag: noindex, follow'); 348 header('Cache-Control: public, max-age=3600'); 349 350 // Discard any active output buffers to ensure zero HTML or whitespace leakage 351 while (ob_get_level() > 0) { 352 ob_end_clean(); 353 } 354 355 if (!empty(trim($content))) { 356 echo trim($content) . "\n"; 357 } else { 358 // Fallback placeholder comment complying with IAB Tech Lab Ads.txt spec 359 echo "# Nexversal Virtual Ads.txt Manager\n"; 360 echo "# Status: Active (Zero records configured)\n"; 361 echo "# Configure authorized digital sellers in WordPress Admin > Appearance > Nexversal PRO\n"; 362 } 363 exit; 364 } 365 366 /** 367 * Handle virtual ads.txt form submissions with nonce verification and manage_options permission 368 */ 369 function nexversal_handle_ads_txt_action() { 370 if (!isset($_POST['nexversal_save_ads_txt'])) { 371 return; 372 } 373 374 if (!isset($_POST['nexversal_ads_txt_nonce']) || !wp_verify_nonce($_POST['nexversal_ads_txt_nonce'], 'nexversal_ads_txt_action')) { 375 wp_die(esc_html__('Security verification failed. Please refresh and try again.', 'nexversal')); 376 } 377 378 if (!current_user_can('manage_options')) { 379 wp_die(esc_html__('Sorry, you do not have permission to manage ads.txt.', 'nexversal')); 380 } 381 382 $raw_content = isset($_POST['nexversal_ads_txt_content']) ? wp_unslash($_POST['nexversal_ads_txt_content']) : ''; 383 $clean_content = nexversal_sanitize_ads_txt($raw_content); 384 385 update_option('nexversal_ads_txt_content', $clean_content); 386 387 // Soft-flush rewrite rules to ensure /ads.txt routing is active 388 flush_rewrite_rules(false); 389 390 wp_safe_redirect(add_query_arg(array('page' => 'nexversal-pro', 'ads_txt_saved' => '1'), admin_url('themes.php'))); 391 exit; 392 } 393 add_action('admin_init', 'nexversal_handle_ads_txt_action'); 394 395 /** 396 * Record an ad impression or click in lightweight aggregated stats 397 * 398 * @param string $slot 399 * @param string $type 'impression' or 'click' 400 * @param string $country 401 */ 402 function nexversal_record_ad_event($slot, $type = 'impression', $country = '') { 403 if (empty($slot)) return; 404 if (empty($country)) { 405 $country = nexversal_get_visitor_country(); 406 } 407 408 $stats = get_option('nexversal_ad_analytics', array()); 409 if (!is_array($stats)) { 410 $stats = array(); 411 } 412 413 if (!isset($stats['slots'][$slot])) { 414 $stats['slots'][$slot] = array('impressions' => 0, 'clicks' => 0); 415 } 416 417 if (!isset($stats['countries'][$country])) { 418 $stats['countries'][$country] = array('impressions' => 0, 'clicks' => 0); 419 } 420 421 if (!isset($stats['total'])) { 422 $stats['total'] = array('impressions' => 0, 'clicks' => 0); 423 } 424 425 if ($type === 'click') { 426 $stats['slots'][$slot]['clicks']++; 427 $stats['countries'][$country]['clicks']++; 428 $stats['total']['clicks']++; 429 } else { 430 $stats['slots'][$slot]['impressions']++; 431 $stats['countries'][$country]['impressions']++; 432 $stats['total']['impressions']++; 433 } 434 435 update_option('nexversal_ad_analytics', $stats, false); 436 } 437 438 /** 439 * Handle AJAX click/impression tracking beacons securely 440 */ 441 function nexversal_handle_ad_beacon() { 442 $slot = isset($_POST['slot']) ? sanitize_key($_POST['slot']) : ''; 443 $type = isset($_POST['type']) && $_POST['type'] === 'click' ? 'click' : 'impression'; 444 $country = nexversal_get_visitor_country(); 445 446 if (!empty($slot)) { 447 nexversal_record_ad_event($slot, $type, $country); 448 wp_send_json_success(array('status' => 'recorded')); 449 } 450 wp_send_json_error(array('status' => 'invalid_slot')); 451 } 452 add_action('wp_ajax_nexversal_ad_beacon', 'nexversal_handle_ad_beacon'); 453 add_action('wp_ajax_nopriv_nexversal_ad_beacon', 'nexversal_handle_ad_beacon'); 454 455 /** 456 * Retrieve list of active ad slots respecting Free vs Pro plan limits. 457 * - Free Plan: Up to 3 active slots maximum. 458 * - Pro Plan: Unlimited active slots. 459 * 460 * @return array Array of slot slugs that are permitted to render 461 */ 462 function nexversal_get_allowed_ad_slots() { 463 $slots = array('header', 'article', 'post_bottom', 'infeed', 'sidebar'); 464 $enabled_slots = array(); 465 466 foreach ($slots as $slot) { 467 $slot_on = get_theme_mod("nexversal_ad_{$slot}_enabled", false); 468 $ad_code = get_theme_mod("nexversal_ad_{$slot}_code", ''); 469 if ($slot_on && !empty(trim($ad_code))) { 470 $enabled_slots[] = $slot; 471 } 472 } 473 474 // Pro Plan: Unlimited 475 if (nexversal_is_pro_active()) { 476 return $enabled_slots; 477 } 478 479 // Free Plan: Limit to 3 slots 480 return array_slice($enabled_slots, 0, 3); 481 } 482 483 /** 484 * Count how many slots are currently configured and enabled 485 * 486 * @return int 487 */ 488 function nexversal_count_active_ad_slots() { 489 $slots = array('header', 'article', 'post_bottom', 'infeed', 'sidebar'); 490 $count = 0; 491 foreach ($slots as $slot) { 492 $slot_on = get_theme_mod("nexversal_ad_{$slot}_enabled", false); 493 $ad_code = get_theme_mod("nexversal_ad_{$slot}_code", ''); 494 if ($slot_on && !empty(trim($ad_code))) { 495 $count++; 496 } 497 } 498 return $count; 499 } 500 501 /** 502 * Global Page Ad Execution Counter & Free Plan Limit Enforcer 503 * 504 * Implements a strict, immutable ceiling of exactly 3 ads per page for the Free version. 505 * - Free Version: Maximum 3 ads rendered per page across all sources: 506 * structural slots, in-article injector, and shortcodes. 507 * - PRO Version: Unlimited ad impressions per page. 508 * 509 * @return int Number of ads rendered on the current request 510 */ 511 function nexversal_get_page_ad_count() { 512 if (!isset($GLOBALS['nexversal_page_ad_count'])) { 513 $GLOBALS['nexversal_page_ad_count'] = 0; 514 } 515 return (int) $GLOBALS['nexversal_page_ad_count']; 516 } 517 518 /** 519 * Determine if another ad is allowed to render on the current page 520 * 521 * @return bool True if ad is permitted to render, false if Free limit is reached. 522 */ 523 function nexversal_can_render_ad() { 524 // PRO plan allows unlimited ads 525 if (nexversal_is_pro_active()) { 526 return true; 527 } 528 // Free plan strictly allows up to 3 ads per page 529 return nexversal_get_page_ad_count() < 3; 530 } 531 532 /** 533 * Increment the global page ad execution tracker 534 * 535 * @return int Updated ad count 536 */ 537 function nexversal_increment_page_ad_count() { 538 if (!isset($GLOBALS['nexversal_page_ad_count'])) { 539 $GLOBALS['nexversal_page_ad_count'] = 0; 540 } 541 $GLOBALS['nexversal_page_ad_count']++; 542 return $GLOBALS['nexversal_page_ad_count']; 543 } 544 545 /** 546 * Reset the global page ad execution tracker (useful for testing and sub-requests) 547 */ 548 function nexversal_reset_page_ad_count() { 549 $GLOBALS['nexversal_page_ad_count'] = 0; 550 } 551 552 /** 553 * Determine whether the current visitor is an authenticated administrator or editor 554 * who must be shielded from live Google AdSense ads to prevent invalid clicks and account suspensions. 555 * 556 * @return bool True if current visitor is an admin or editor, false otherwise. 557 */ 558 function nexversal_is_ad_shielded_user() { 559 if (!function_exists('is_user_logged_in') || !is_user_logged_in()) { 560 return false; 561 } 562 563 // Strict validation checks for Administrator or Editor roles / capabilities 564 $is_shielded = ( 565 current_user_can('administrator') || 566 current_user_can('manage_options') || 567 current_user_can('editor') || 568 current_user_can('edit_others_posts') 569 ); 570 571 /** 572 * Filter whether the current user should be shielded from live AdSense ads. 573 * 574 * @param bool $is_shielded 575 */ 576 return (bool) apply_filters('nexversal_is_ad_shielded_user', $is_shielded); 577 } 578 579 /** 580 * Production-grade WordPress PHP wrapper function to conditionalize Google AdSense code output within theme templates. 581 * 582 * Implements strict user validation checks using current_user_can('administrator') and editor checks. 583 * - Authenticated administrators & editors: Intercepts real AdSense publisher scripts and replaces 584 * them entirely with a safe local HTML placeholder block containing developer diagnostics, 585 * or maps them to Google's official demo test ad parameters (data-ad-test="on"). 586 * - Standard web visitors: Outputs genuine production ad code completely unmodified. 587 * 588 * @param string $raw_ad_code Raw ad snippet or AdSense script/ins tags. 589 * @param string $slot Optional slot slug (e.g. 'header', 'article', 'post_bottom', 'infeed', 'sidebar'). 590 * @param string $slot_name Optional human-readable slot name (e.g. 'Header Leaderboard'). 591 * @param string $mode Optional mode override: 'placeholder' (default) or 'test_ad'. 592 * @return string Genuine production ad code for visitors, or safe developer diagnostic block for admin/editor. 593 */ 594 function nexversal_wrap_adsense_code($raw_ad_code, $slot = '', $slot_name = '', $mode = '') { 595 if (empty(trim($raw_ad_code))) { 596 return ''; 597 } 598 599 // 1. Strict user validation: Standard visitors always get genuine unmodified production ad code 600 if (!nexversal_is_ad_shielded_user()) { 601 return $raw_ad_code; 602 } 603 604 // 2. Resolve shielding mode ('placeholder' by default, or 'test_ad') 605 if (empty($mode)) { 606 /** 607 * Filter to toggle between safe local HTML placeholder block or Google official demo test ads. 608 * 609 * @param string $mode 'placeholder' (default) or 'test_ad' 610 * @param string $slot Ad slot slug 611 */ 612 $mode = (string) apply_filters('nexversal_adsense_shield_mode', 'placeholder', $slot); 613 } 614 615 // 3. Option B: Map to Google's Official Demo Test Ad Slot Parameters (data-ad-test="on") 616 if ($mode === 'test_ad') { 617 if (stripos($raw_ad_code, 'adsbygoogle') !== false) { 618 if (stripos($raw_ad_code, 'data-ad-test') === false) { 619 $test_code = preg_replace('/<ins\b([^>]*)>/i', '<ins$1 data-ad-test="on">', $raw_ad_code); 620 if (!empty($test_code)) { 621 return "<!-- [AdSense Shield] Google Official Demo Test Ad Active (data-ad-test=\"on\") -->\n" . $test_code; 622 } 623 } 624 } 625 return $raw_ad_code; 626 } 627 628 // 4. Option A (Default): Safe Local HTML Placeholder Block with Developer Diagnostics 629 // Extract metadata from the raw ad code for diagnostics 630 $detected_pub = ''; 631 if (preg_match('/(?:client|data-ad-client)=["\'](ca-pub-[0-9]+|pub-[0-9]+)["\']/i', $raw_ad_code, $m)) { 632 $detected_pub = $m[1]; 633 } elseif (preg_match('/ca-pub-[0-9]+/i', $raw_ad_code, $m)) { 634 $detected_pub = $m[0]; 635 } 636 637 $detected_slot_id = ''; 638 if (preg_match('/data-ad-slot=["\']([0-9]+)["\']/i', $raw_ad_code, $m)) { 639 $detected_slot_id = $m[1]; 640 } 641 642 // Resolve human-friendly slot label 643 if (empty($slot_name)) { 644 if (!empty($slot) && function_exists('nexversal_get_ad_slot_specs')) { 645 $specs = nexversal_get_ad_slot_specs($slot); 646 $slot_name = !empty($specs['name']) ? $specs['name'] : ucfirst(str_replace('_', ' ', $slot)); 647 } else { 648 $slot_name = !empty($slot) ? ucfirst(str_replace('_', ' ', $slot)) : __('Ad Placement', 'nexversal'); 649 } 650 } 651 652 // Identify user role for diagnostic display 653 $current_user = wp_get_current_user(); 654 $roles_list = !empty($current_user->roles) ? implode(', ', array_map('ucfirst', $current_user->roles)) : 'Administrator'; 655 656 ob_start(); 657 ?> 658 <div class="nx-ad-admin-shield" data-shield-slot="<?php echo esc_attr($slot); ?>" role="region" aria-label="<?php echo esc_attr(sprintf(__('Ad Slot: %s - Hidden for Admin', 'nexversal'), $slot_name)); ?>"> 659 <div class="nx-shield-inner"> 660 <div class="nx-shield-header"> 661 <div class="nx-shield-title"> 662 <svg class="nx-shield-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> 663 <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/> 664 <path d="m9 12 2 2 4-4"/> 665 </svg> 666 <span><strong>Ad Slot Active:</strong> <?php echo esc_html($slot_name); ?></span> 667 </div> 668 <span class="nx-shield-badge"><?php esc_html_e('ADMIN SHIELD ACTIVE', 'nexversal'); ?></span> 669 </div> 670 <p class="nx-shield-desc"> 671 <?php esc_html_e('Hidden for Admin to Prevent Invalid Clicks & AdSense Policy Violations. Standard visitors receive genuine production ads.', 'nexversal'); ?> 672 </p> 673 <div class="nx-shield-meta"> 674 <span class="nx-shield-chip"><strong>Role:</strong> <?php echo esc_html($roles_list); ?></span> 675 <?php if (!empty($detected_pub)) : ?> 676 <span class="nx-shield-chip"><strong>Client:</strong> <?php echo esc_html($detected_pub); ?></span> 677 <?php endif; ?> 678 <?php if (!empty($detected_slot_id)) : ?> 679 <span class="nx-shield-chip"><strong>Slot ID:</strong> <?php echo esc_html($detected_slot_id); ?></span> 680 <?php endif; ?> 681 <span class="nx-shield-chip nx-shield-chip-status">✓ 0 External Ad Requests</span> 682 </div> 683 </div> 684 </div> 685 <?php 686 return trim(ob_get_clean()); 687 } 688 689 /** 690 * Output Google AdSense Auto-Ads script in head if Publisher ID is entered 691 * Intercepted strictly with type="text/plain" until GDPR cookie consent is verified. 692 * Suppressed completely for authenticated administrators and editors to prevent invalid traffic. 693 */ 694 function nexversal_render_adsense_auto_script() { 695 $pub_id = get_theme_mod('nexversal_adsense_pub_id', ''); 696 if (!empty($pub_id)) { 697 // Suppress Auto-Ads if current singular post has disabled ads via Monetization Controls 698 if (is_singular() && function_exists('nexversal_is_ad_disabled_for_post') && nexversal_is_ad_disabled_for_post()) { 699 echo "<!-- [AdSense Shield] Auto Ads script suppressed for post #" . esc_attr(get_the_ID()) . " via Monetization Controls -->\n"; 700 return; 701 } 702 703 // Prevent Auto-Ads execution for logged-in Administrator/Editor 704 if (function_exists('nexversal_is_ad_shielded_user') && nexversal_is_ad_shielded_user()) { 705 echo "<!-- [AdSense Shield] Auto Ads script suppressed for logged-in Administrator/Editor to prevent invalid clicks and policy violations -->\n"; 706 return; 707 } 708 709 $pub_id = trim($pub_id); 710 // Guarantee ca-pub- prefix 711 if (strpos($pub_id, 'ca-pub-') === false && strpos($pub_id, 'pub-') === 0) { 712 $pub_id = 'ca-' . $pub_id; 713 } elseif (strpos($pub_id, 'ca-pub-') === false) { 714 $pub_id = 'ca-pub-' . $pub_id; 715 } 716 717 $script_src = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=' . esc_attr($pub_id); 718 719 // Strict Free Plan Limit: Instruct Google crawler to cap Auto Ads to max 3 on Free version 720 $is_pro = nexversal_is_pro_active(); 721 $max_ads_attr = !$is_pro ? ' data-max-ads-per-page="3"' : ''; 722 723 if (function_exists('nexversal_has_cookie_consent') && !nexversal_has_cookie_consent()) { 724 ?> 725 <!-- Google AdSense 1-Click Auto Ads (GDPR Intercepted: Inert Until Consent Granted<?php echo !$is_pro ? ' | Free Plan Max 3 Ads' : ''; ?>) --> 726 <script type="text/plain" data-nx-consent="adsense" data-src="<?php echo esc_url($script_src); ?>"<?php echo $max_ads_attr; ?> async crossorigin="anonymous"></script> 727 <?php 728 } else { 729 ?> 730 <!-- Google AdSense 1-Click Auto Ads (Active<?php echo !$is_pro ? ' | Free Plan Max 3 Ads' : ''; ?>) --> 731 <script async src="<?php echo esc_url($script_src); ?>"<?php echo $max_ads_attr; ?> crossorigin="anonymous"></script> 732 <?php 733 } 734 } 735 } 736 add_action('wp_head', 'nexversal_render_adsense_auto_script', 5); 737 738 /** 739 * Render the Appearance > Nexversal PRO Admin Page (v3.5.0) 184 740 */ 185 741 function nexversal_pro_admin_page() { … … 188 744 $plan_name = get_option('nexversal_pro_license_plan', 'Nexversal PRO ($15/year)'); 189 745 $activated_at = get_option('nexversal_pro_license_activated_at', ''); 746 $stats = get_option('nexversal_ad_analytics', array()); 747 748 $total_imp = isset($stats['total']['impressions']) ? (int) $stats['total']['impressions'] : 0; 749 $total_clicks = isset($stats['total']['clicks']) ? (int) $stats['total']['clicks'] : 0; 750 $total_ctr = $total_imp > 0 ? round(($total_clicks / $total_imp) * 100, 2) : 0; 751 $active_slots = nexversal_count_active_ad_slots(); 190 752 ?> 191 <div class="wrap" style="max-width: 900px; margin: 30px auto; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">753 <div class="wrap" style="max-width: 1050px; margin: 25px auto; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;"> 192 754 193 755 <?php if (isset($_GET['activated'])) : ?> 194 756 <div class="notice notice-success is-dismissible" style="padding: 14px 18px; border-radius: 6px; border-left-color: #10b981;"> 195 <p style="font-size: 14px; margin: 0;"><strong>🎉 <?php esc_html_e('Nexversal PRO Activated Successfully! All 5 Ad Slots & Monetization features are now unlocked.', 'nexversal'); ?></strong></p>757 <p style="font-size: 14px; margin: 0;"><strong>🎉 <?php esc_html_e('Nexversal PRO Activated Successfully! Unlimited ad slots, Ad-Blocker Bypass, and real-time Analytics are now unlocked.', 'nexversal'); ?></strong></p> 196 758 </div> 197 759 <?php elseif (isset($_GET['error']) && $_GET['error'] === 'invalid_key') : ?> 198 760 <div class="notice notice-error is-dismissible" style="padding: 14px 18px; border-radius: 6px;"> 199 <p style="font-size: 14px; margin: 0;"><strong>❌ <?php esc_html_e('Invalid License Key. Please enter the valid key received from Lemon Squeezy after purchasing the $15/year plan.', 'nexversal'); ?></strong></p>761 <p style="font-size: 14px; margin: 0;"><strong>❌ <?php esc_html_e('Invalid License Key. Please enter the valid key received from Lemon Squeezy ($15/year).', 'nexversal'); ?></strong></p> 200 762 </div> 201 763 <?php elseif (isset($_GET['deactivated'])) : ?> 202 764 <div class="notice notice-info is-dismissible" style="padding: 14px 18px; border-radius: 6px;"> 203 <p style="font-size: 14px; margin: 0;"><?php esc_html_e('Nexversal PRO License has been deactivated. ', 'nexversal'); ?></p>765 <p style="font-size: 14px; margin: 0;"><?php esc_html_e('Nexversal PRO License has been deactivated. Free Ad Engine (up to 3 ads) remains active.', 'nexversal'); ?></p> 204 766 </div> 205 767 <?php elseif (isset($_GET['url_saved'])) : ?> 206 768 <div class="notice notice-success is-dismissible" style="padding: 14px 18px; border-radius: 6px; border-left-color: #10b981;"> 207 <p style="font-size: 14px; margin: 0;"><strong>✓ <?php esc_html_e('Lemon Squeezy Checkout URL updated successfully! All buttons now link to your live checkout.', 'nexversal'); ?></strong></p> 769 <p style="font-size: 14px; margin: 0;"><strong>✓ <?php esc_html_e('Lemon Squeezy Checkout URL updated successfully!', 'nexversal'); ?></strong></p> 770 </div> 771 <?php elseif (isset($_GET['stats_reset'])) : ?> 772 <div class="notice notice-success is-dismissible" style="padding: 14px 18px; border-radius: 6px; border-left-color: #10b981;"> 773 <p style="font-size: 14px; margin: 0;"><?php esc_html_e('Ad Analytics statistics have been reset.', 'nexversal'); ?></p> 774 </div> 775 <?php elseif (isset($_GET['ads_txt_saved'])) : ?> 776 <div class="notice notice-success is-dismissible" style="padding: 14px 18px; border-radius: 6px; border-left-color: #10b981;"> 777 <p style="font-size: 14px; margin: 0;"><strong>✓ <?php esc_html_e('ads.txt updated successfully! Served dynamically at /ads.txt without cPanel file editing.', 'nexversal'); ?></strong></p> 208 778 </div> 209 779 <?php endif; ?> 210 780 211 781 <!-- Brand Header Card --> 212 <div style="background: linear-gradient(135deg, # 1e1b4b 0%, #0f172a 100%); color: #fff; padding: 36px 40px; border-radius: 12px; box-shadow: 0 10px 25px rgba(0,0,0,0.15); margin-bottom: 28px;">782 <div style="background: linear-gradient(135deg, #090d16 0%, #1e1b4b 60%, #312e81 100%); color: #fff; padding: 36px 40px; border-radius: 14px; box-shadow: 0 10px 30px rgba(0,0,0,0.25); margin-bottom: 28px;"> 213 783 <div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px;"> 214 784 <div> 215 <span style="display: inline-block; background: rgba(99, 102, 241, 0.3 ); border: 1px solid rgba(165, 180, 252, 0.4); color: #c7d2fe; font-size: 11px; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; padding: 4px 12px; border-radius: 999px; margin-bottom: 12px;">216 Lemon Squeezy Verified • $15 / Year785 <span style="display: inline-block; background: rgba(99, 102, 241, 0.35); border: 1px solid rgba(165, 180, 252, 0.4); color: #c7d2fe; font-size: 11px; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; padding: 4px 12px; border-radius: 999px; margin-bottom: 12px;"> 786 Version 3.5.0 • Monetization Engine 217 787 </span> 218 788 <h1 style="font-size: 28px; font-weight: 900; margin: 0 0 8px 0; color: #fff; letter-spacing: -0.02em;"> 219 Nexversal PRO — Ad & Monetization Engine789 Nexversal Monetization & Ad Suite 220 790 </h1> 221 <p style="font-size: 1 5px; color: #cbd5e1; margin: 0; line-height: 1.6; max-width: 580px;">222 Built-in AdSense, In-Article & Sticky Sidebar Ad Engine. Save $50/year on external ad plugins while maximizing organic blog earnings.791 <p style="font-size: 14.5px; color: #cbd5e1; margin: 0; line-height: 1.6; max-width: 620px;"> 792 Free Plan allows <strong>up to 3 simultaneous ads</strong> with automatic location detection. PRO Plan ($15/yr) unlocks <strong>unlimited simultaneous ads</strong>, Ad-Blocker Bypass, and CTR analytics. 223 793 </p> 224 794 </div> 225 795 <div style="text-align: right;"> 226 796 <?php if ($is_active) : ?> 227 <div style="background: #10b981; color: #fff; font-size: 14px; font-weight: 800; padding: 8px 18px; border-radius: 999px; display: inline-flex; align-items: center; gap: 6px; box-shadow: 0 4px 1 2px rgba(16,185,129,0.35);">228 <span>✓</span> <?php esc_html_e('PRO Plan Active ', 'nexversal'); ?>797 <div style="background: #10b981; color: #fff; font-size: 14px; font-weight: 800; padding: 8px 18px; border-radius: 999px; display: inline-flex; align-items: center; gap: 6px; box-shadow: 0 4px 14px rgba(16,185,129,0.4);"> 798 <span>✓</span> <?php esc_html_e('PRO Plan Active (Unlimited Ads)', 'nexversal'); ?> 229 799 </div> 230 800 <?php else : ?> 231 <div style="background: #ef4444; color: #fff; font-size: 13px; font-weight: 800; padding: 6px 14px; border-radius: 999px; display: inline-flex; align-items: center; gap: 6px;">232 <span> 🔒</span> <?php esc_html_e('PRO Locked (Free Plan)', 'nexversal'); ?>801 <div style="background: rgba(255, 255, 255, 0.12); color: #cbd5e1; font-size: 13px; font-weight: 700; padding: 6px 14px; border-radius: 999px; display: inline-flex; align-items: center; gap: 6px; border: 1px solid rgba(255,255,255,0.2);"> 802 <span>⚡</span> <?php printf(esc_html__('Free Plan: %d of 3 Slots Active', 'nexversal'), esc_html($active_slots)); ?> 233 803 </div> 234 804 <?php endif; ?> … … 237 807 </div> 238 808 809 <!-- 3-Column Plan Breakdown --> 810 <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 20px; margin-bottom: 28px;"> 811 <div style="background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 22px; box-shadow: 0 2px 6px rgba(0,0,0,0.03);"> 812 <span style="font-size: 11px; font-weight: 800; color: #10b981; background: #ecfdf5; padding: 3px 8px; border-radius: 6px; text-transform: uppercase;">Free Plan (Lifetime)</span> 813 <h3 style="font-size: 17px; font-weight: 800; margin: 10px 0 6px 0; color: #0f172a;">Run 3 Active Ads</h3> 814 <p style="font-size: 13px; color: #64748b; line-height: 1.5; margin: 0;">Run any 3 ads of your choice (Header, In-Article, Bottom, Sidebar, or Grid). Includes 1-click Google Auto Ads and automatic country detection.</p> 815 </div> 816 <div style="background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 22px; box-shadow: 0 2px 6px rgba(0,0,0,0.03);"> 817 <span style="font-size: 11px; font-weight: 800; color: #8b5cf6; background: #f3e8ff; padding: 3px 8px; border-radius: 6px; text-transform: uppercase;">PRO Feature</span> 818 <h3 style="font-size: 17px; font-weight: 800; margin: 10px 0 6px 0; color: #0f172a;">Ad-Blocker Bypass</h3> 819 <p style="font-size: 13px; color: #64748b; line-height: 1.5; margin: 0;">Detects active ad blockers and replaces blank space with custom affiliate banners or sponsor notices that ad blockers cannot touch.</p> 820 </div> 821 <div style="background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 22px; box-shadow: 0 2px 6px rgba(0,0,0,0.03);"> 822 <span style="font-size: 11px; font-weight: 800; color: #4f46e5; background: #eef2ff; padding: 3px 8px; border-radius: 6px; text-transform: uppercase;">PRO Feature</span> 823 <h3 style="font-size: 17px; font-weight: 800; margin: 10px 0 6px 0; color: #0f172a;">Unlimited Ads & Analytics</h3> 824 <p style="font-size: 13px; color: #64748b; line-height: 1.5; margin: 0;">Run 4, 5 or all slots at the same time without limits. Track real-time impressions, clicks, and CTR % to maximize blog revenue.</p> 825 </div> 826 </div> 827 828 <!-- Real-Time Monetization Analytics Dashboard --> 829 <div style="background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 28px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05); margin-bottom: 28px;"> 830 <div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px; margin-bottom: 20px;"> 831 <div> 832 <h3 style="font-size: 18px; font-weight: 800; margin: 0 0 4px 0; color: #0f172a;"> 833 📊 <?php esc_html_e('Ad Performance & CTR Analytics', 'nexversal'); ?> 834 <?php if (!$is_active) : ?> 835 <span style="font-size: 11px; font-weight: 800; color: #6366f1; background: #eef2ff; padding: 2px 8px; border-radius: 4px; vertical-align: middle; margin-left: 6px;">PRO Preview</span> 836 <?php endif; ?> 837 </h3> 838 <p style="font-size: 13px; color: #64748b; margin: 0;"> 839 <?php esc_html_e('Real-time tracking of impressions, clicks, click-through rates (CTR), and visitor countries.', 'nexversal'); ?> 840 </p> 841 </div> 842 <div> 843 <form method="post" action="" style="display: inline-block;"> 844 <?php wp_nonce_field('nexversal_license_action', 'nexversal_license_nonce'); ?> 845 <input type="submit" name="nexversal_reset_ad_stats" class="button" value="<?php esc_attr_e('Reset Stats', 'nexversal'); ?>" onclick="return confirm('<?php echo esc_js(__('Are you sure you want to reset all ad statistics?', 'nexversal')); ?>');" style="font-size: 12px;"> 846 </form> 847 <a href="<?php echo esc_url(admin_url('customize.php?autofocus[section]=nexversal_monetization')); ?>" class="button button-primary" style="background: #4f46e5; border-color: #4f46e5; font-size: 12px; padding: 0 14px; height: 30px; line-height: 28px; margin-left: 6px;"> 848 <?php esc_html_e('Configure Ads in Customizer ➔', 'nexversal'); ?> 849 </a> 850 </div> 851 </div> 852 853 <!-- Stats KPI Cards --> 854 <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 24px;"> 855 <div style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 10px; padding: 18px;"> 856 <div style="font-size: 12px; font-weight: 700; color: #64748b; text-transform: uppercase;"><?php esc_html_e('Total Impressions', 'nexversal'); ?></div> 857 <div style="font-size: 26px; font-weight: 900; color: #0f172a; margin-top: 4px;"><?php echo number_format_i18n($total_imp); ?></div> 858 </div> 859 <div style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 10px; padding: 18px;"> 860 <div style="font-size: 12px; font-weight: 700; color: #64748b; text-transform: uppercase;"><?php esc_html_e('Total Clicks', 'nexversal'); ?></div> 861 <div style="font-size: 26px; font-weight: 900; color: #4f46e5; margin-top: 4px;"><?php echo number_format_i18n($total_clicks); ?></div> 862 </div> 863 <div style="background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 10px; padding: 18px;"> 864 <div style="font-size: 12px; font-weight: 700; color: #64748b; text-transform: uppercase;"><?php esc_html_e('Avg. Click-Through Rate (CTR)', 'nexversal'); ?></div> 865 <div style="font-size: 26px; font-weight: 900; color: #10b981; margin-top: 4px;"><?php echo esc_html($total_ctr); ?>%</div> 866 </div> 867 </div> 868 869 <!-- Slot Breakdown Table --> 870 <table class="widefat striped" style="border: 1px solid #e2e8f0; border-radius: 8px; overflow: hidden;"> 871 <thead> 872 <tr> 873 <th style="padding: 10px 14px; font-weight: 700;"><?php esc_html_e('Ad Slot', 'nexversal'); ?></th> 874 <th style="padding: 10px 14px; font-weight: 700;"><?php esc_html_e('Impressions', 'nexversal'); ?></th> 875 <th style="padding: 10px 14px; font-weight: 700;"><?php esc_html_e('Clicks', 'nexversal'); ?></th> 876 <th style="padding: 10px 14px; font-weight: 700;"><?php esc_html_e('CTR %', 'nexversal'); ?></th> 877 </tr> 878 </thead> 879 <tbody> 880 <?php 881 $slots = array( 882 'header' => __('Header Banner (728x90)', 'nexversal'), 883 'article' => __('In-Article Mid-Content', 'nexversal'), 884 'post_bottom' => __('Below Post Content', 'nexversal'), 885 'infeed' => __('Homepage In-Feed Grid', 'nexversal'), 886 'sidebar' => __('Sticky Sidebar', 'nexversal'), 887 ); 888 foreach ($slots as $s_key => $s_name) : 889 $s_imp = isset($stats['slots'][$s_key]['impressions']) ? (int) $stats['slots'][$s_key]['impressions'] : 0; 890 $s_clicks = isset($stats['slots'][$s_key]['clicks']) ? (int) $stats['slots'][$s_key]['clicks'] : 0; 891 $s_ctr = $s_imp > 0 ? round(($s_clicks / $s_imp) * 100, 2) : 0; 892 ?> 893 <tr> 894 <td style="padding: 10px 14px; font-weight: 600; color: #1e293b;"><?php echo esc_html($s_name); ?></td> 895 <td style="padding: 10px 14px;"><?php echo number_format_i18n($s_imp); ?></td> 896 <td style="padding: 10px 14px;"><?php echo number_format_i18n($s_clicks); ?></td> 897 <td style="padding: 10px 14px;"><span style="background: #eef2ff; color: #4f46e5; font-weight: 700; padding: 2px 6px; border-radius: 4px;"><?php echo esc_html($s_ctr); ?>%</span></td> 898 </tr> 899 <?php endforeach; ?> 900 </tbody> 901 </table> 902 </div> 903 904 <!-- License Management & Upsell Box --> 239 905 <div style="display: grid; grid-template-columns: 1.25fr 1fr; gap: 24px; align-items: start;"> 240 906 … … 242 908 <div style="background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 30px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05);"> 243 909 <h3 style="font-size: 18px; font-weight: 800; margin-top: 0; margin-bottom: 14px; color: #0f172a;"> 244 <?php esc_html_e(' License KeyActivation', 'nexversal'); ?>910 <?php esc_html_e('PRO License Activation', 'nexversal'); ?> 245 911 </h3> 246 912 … … 264 930 <input type="submit" name="nexversal_deactivate_license" class="button" value="<?php esc_attr_e('Deactivate License', 'nexversal'); ?>" onclick="return confirm('<?php echo esc_js(__('Are you sure you want to deactivate your PRO license?', 'nexversal')); ?>');" style="color: #b91c1c; border-color: #fca5a5;"> 265 931 <a href="<?php echo esc_url(admin_url('customize.php?autofocus[section]=nexversal_monetization')); ?>" class="button button-primary" style="margin-left: 10px; background: #4f46e5; border-color: #4f46e5; height: auto; padding: 4px 14px;"> 266 <?php esc_html_e(' Manage Ad Slots in Customizer ➔', 'nexversal'); ?>932 <?php esc_html_e('Configure Ads in Customizer ➔', 'nexversal'); ?> 267 933 </a> 268 934 </form> … … 270 936 <?php else : ?> 271 937 <p style="font-size: 14px; color: #64748b; line-height: 1.6; margin-bottom: 20px;"> 272 <?php esc_html_e(' Purchase your license via Lemon Squeezy ($15/year) to automatically receive your instant license key.', 'nexversal'); ?>938 <?php esc_html_e('Upgrade to PRO ($15/year) to unlock unlimited simultaneous ads, Ad-Blocker Bypass, and real-time analytics.', 'nexversal'); ?> 273 939 </p> 274 940 … … 303 969 </h4> 304 970 <ul style="margin: 0 0 20px 0; padding-left: 0; list-style: none; font-size: 13.5px; color: #334155; line-height: 1.85;"> 305 <li>✓ <strong>Auto In-Article AdSense:</strong> Automatically inserts after paragraph 3</li> 306 <li>✓ <strong>Sticky Sidebar Ad (300×600):</strong> Floating ad as user scrolls down</li> 307 <li>✓ <strong>In-Feed Post Grid Ad:</strong> Blends natively into your articles grid</li> 308 <li>✓ <strong>Header 728×90 Leaderboard:</strong> High-impact top banner visibility</li> 309 <li>✓ <strong>Post Bottom Banner:</strong> Captures readers right when finishing reading</li> 971 <li>✓ <strong>Unlimited Active Ads:</strong> Run all 5 ad placements at the same time</li> 972 <li>✓ <strong>Ad-Blocker Bypass:</strong> Forces fallback banners when AdSense is blocked</li> 973 <li>✓ <strong>Automatic Geo-Targeting:</strong> Visitors see ads based on real-time location</li> 974 <li>✓ <strong>CTR Analytics:</strong> Live impressions, clicks & conversion tracking</li> 310 975 <li>✓ <strong>Google Policy Compliant:</strong> Auto "Advertisement" label</li> 311 976 <li>✓ <strong>Save $50/Year:</strong> Zero external ad plugins required</li> … … 335 1000 </form> 336 1001 </div> 1002 1003 <!-- Automated Virtual ads.txt Manager --> 1004 <div style="margin-top: 28px; background: #fff; border: 1px solid #e2e8f0; border-radius: 12px; padding: 26px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05);"> 1005 <div style="display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: 12px; margin-bottom: 12px;"> 1006 <div> 1007 <h3 style="margin: 0 0 6px 0; font-size: 17px; font-weight: 800; color: #0f172a; display: flex; align-items: center; gap: 8px;"> 1008 <span>📄</span> <?php esc_html_e('Automated Ads.txt Manager', 'nexversal'); ?> 1009 <span style="font-size: 11px; font-weight: 700; background: #ecfdf5; color: #059669; border: 1px solid #a7f3d0; padding: 3px 8px; border-radius: 20px;"> 1010 <?php esc_html_e('Virtual Engine Active', 'nexversal'); ?> 1011 </span> 1012 </h3> 1013 <p style="font-size: 13.5px; color: #64748b; margin: 0; line-height: 1.5;"> 1014 <?php esc_html_e('Declare your authorized digital ad sellers (Google AdSense, AdX, Magnite, PubMatic). Served dynamically at your domain root with proper text/plain headers — zero cPanel file creation needed!', 'nexversal'); ?> 1015 </p> 1016 </div> 1017 <div> 1018 <a href="<?php echo esc_url(home_url('/ads.txt')); ?>" target="_blank" rel="noopener noreferrer" class="button" style="display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600;"> 1019 <span>🔗</span> <?php esc_html_e('View Live ads.txt', 'nexversal'); ?> ↗ 1020 </a> 1021 </div> 1022 </div> 1023 1024 <?php 1025 $has_physical_file = file_exists(ABSPATH . 'ads.txt'); 1026 if ($has_physical_file) : 1027 ?> 1028 <div style="background: #fffbeb; border: 1px solid #fde68a; border-radius: 8px; padding: 12px 16px; margin-bottom: 18px; font-size: 13px; color: #92400e; display: flex; align-items: center; gap: 10px;"> 1029 <span style="font-size: 18px;">⚠️</span> 1030 <div> 1031 <strong><?php esc_html_e('Physical ads.txt file detected on your server root (cPanel/public_html):', 'nexversal'); ?></strong><br> 1032 <?php esc_html_e('In Apache/cPanel, a physical file on disk takes priority over virtual dynamic URLs. If you want this dashboard to manage your ads.txt dynamically, please delete or rename the physical ads.txt file in cPanel File Manager.', 'nexversal'); ?> 1033 </div> 1034 </div> 1035 <?php else : ?> 1036 <div style="background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 8px; padding: 10px 14px; margin-bottom: 18px; font-size: 12.5px; color: #166534; display: flex; align-items: center; gap: 8px;"> 1037 <span>✓</span> 1038 <span><strong><?php esc_html_e('Zero cPanel File Editing:', 'nexversal'); ?></strong> <?php esc_html_e('No physical file exists on disk. Requests to /ads.txt are intercepted dynamically by the theme and served from your database with optimal caching and 200 OK headers.', 'nexversal'); ?></span> 1039 </div> 1040 <?php endif; ?> 1041 1042 <form method="post" action=""> 1043 <?php wp_nonce_field('nexversal_ads_txt_action', 'nexversal_ads_txt_nonce'); ?> 1044 1045 <div style="margin-bottom: 14px;"> 1046 <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; flex-wrap: wrap; gap: 8px;"> 1047 <label for="nexversal_ads_txt_content" style="font-size: 13px; font-weight: 700; color: #334155;"> 1048 <?php esc_html_e('ads.txt Records (IAB Tech Lab Format):', 'nexversal'); ?> 1049 </label> 1050 <button type="button" id="nx-insert-adsense-btn" class="button button-small" style="font-size: 11px;"> 1051 + <?php esc_html_e('Insert Google AdSense Template', 'nexversal'); ?> 1052 </button> 1053 </div> 1054 1055 <textarea id="nexversal_ads_txt_content" name="nexversal_ads_txt_content" rows="10" spellcheck="false" placeholder="# Example: google.com, pub-XXXXXXXXXXXXXXXX, DIRECT, f08c47fec0942fa0" style="width: 100%; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-size: 13px; line-height: 1.6; background: #0f172a; color: #38bdf8; border: 1px solid #1e293b; border-radius: 8px; padding: 14px; box-sizing: border-box;"><?php echo esc_textarea(get_option('nexversal_ads_txt_content', '')); ?></textarea> 1056 1057 <p style="font-size: 12px; color: #64748b; margin: 6px 0 0 0;"> 1058 <?php esc_html_e('Format: <Domain>, <Publisher ID>, <Relationship (DIRECT|RESELLER)>, <Certification Authority ID (Optional)>. Comments start with #.', 'nexversal'); ?> 1059 </p> 1060 </div> 1061 1062 <div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 10px;"> 1063 <input type="submit" name="nexversal_save_ads_txt" class="button button-primary" value="<?php esc_attr_e('Save & Deploy ads.txt', 'nexversal'); ?>" style="background: #4f46e5; border-color: #4f46e5; font-weight: 700; padding: 7px 22px; height: auto;"> 1064 <span style="font-size: 12px; color: #94a3b8;"> 1065 <?php 1066 $saved_ads = get_option('nexversal_ads_txt_content', ''); 1067 $line_count = !empty(trim($saved_ads)) ? count(array_filter(explode("\n", trim($saved_ads)))) : 0; 1068 printf(esc_html__('Currently deployed: %d active line(s)', 'nexversal'), $line_count); 1069 ?> 1070 </span> 1071 </div> 1072 </form> 1073 1074 <script> 1075 document.addEventListener('DOMContentLoaded', function() { 1076 var btn = document.getElementById('nx-insert-adsense-btn'); 1077 var txt = document.getElementById('nexversal_ads_txt_content'); 1078 if (btn && txt) { 1079 btn.addEventListener('click', function() { 1080 var snippet = "google.com, pub-XXXXXXXXXXXXXXXX, DIRECT, f08c47fec0942fa0\n"; 1081 if (txt.value.indexOf('google.com') === -1) { 1082 txt.value = (txt.value.trim() ? txt.value.trim() + "\n" : "") + snippet; 1083 } else { 1084 alert('<?php echo esc_js(__('Google AdSense entry already present in editor.', 'nexversal')); ?>'); 1085 } 1086 }); 1087 } 1088 }); 1089 </script> 1090 </div> 337 1091 </div> 338 1092 <?php … … 340 1094 341 1095 /** 342 * Register Monetization & Ad Manager Section in WordPress Customizer 343 * 344 * CRITICAL RULE: 345 * If the user has NOT activated PRO ($15/year), DO NOT SHOW ANY AD SLOTS OR INPUTS! 346 * Show only the sleek PRO Lock Card. 1096 * Register Monetization & Ad Manager Section in WordPress Customizer (v3.5.0) 1097 * 1098 * Beginner-Friendly & Clean UX: 1099 * 1. Quick Setup (1-Click Google Auto-Ads) 1100 * 2. Active Ads Plan Counter (Free: 3 Ads Limit | PRO: Unlimited) 1101 * 3. Position Dropdown Selectors (Instead of manual numbers) 1102 * 4. Automatic Geo-Targeting (Zero manual typing) 347 1103 */ 348 1104 function nexversal_ads_customize_register($wp_customize) { 349 $is_pro = nexversal_is_pro_active(); 350 351 // Section title 352 $section_title = $is_pro 353 ? esc_html__('💰 Monetization & Ad Engine [PRO Active]', 'nexversal') 354 : esc_html__('💰 Monetization & Ad Engine [PRO Locked]', 'nexversal'); 355 356 $section_desc = $is_pro 357 ? esc_html__('Nexversal PRO is active! Configure your automated Google AdSense, in-article, and banner slots below.', 'nexversal') 358 : esc_html__('🔒 PRO FEATURE ($15/year): Monetization & Ad Engine is locked. Upgrade to PRO to unlock automated in-article AdSense, sticky sidebar ads, and in-feed units.', 'nexversal'); 1105 $is_pro = nexversal_is_pro_active(); 1106 $active_count = nexversal_count_active_ad_slots(); 359 1107 360 1108 $wp_customize->add_section('nexversal_monetization', array( 361 'title' => $section_title,362 'description' => $section_desc,1109 'title' => esc_html__('💰 Monetization & Ad Engine', 'nexversal'), 1110 'description' => esc_html__('Manage your blog advertisements. Free plan allows up to 3 active ads with automatic country detection. PRO unlocks unlimited ads and Ad-Blocker bypass!', 'nexversal'), 363 1111 'priority' => 28, 364 1112 )); 365 1113 366 // ========================================================================= 367 // IF NOT PRO: SHOW ONLY PRO LOCK CARD AND STOP! NO AD INPUTS VISIBLE! 368 // ========================================================================= 369 if (!$is_pro) { 370 $wp_customize->add_setting('nexversal_pro_lock_info', array( 371 'default' => '', 372 'sanitize_callback' => 'wp_kses_post', 373 )); 374 375 $lock_html = ' 376 <div style="background: #ffffff; border: 1px solid #e2e8f0; border-radius: 10px; padding: 22px 16px; text-align: center; margin-top: 8px; box-shadow: 0 4px 10px rgba(0,0,0,0.04);"> 377 <div style="font-size: 34px; margin-bottom: 8px;">🔒</div> 378 <h4 style="margin: 0 0 8px 0; font-size: 16px; font-weight: 800; color: #0f172a;"> 379 ' . esc_html__('Ad Engine is Locked in Free Plan', 'nexversal') . ' 380 </h4> 381 <p style="font-size: 12.5px; color: #64748b; line-height: 1.55; margin-bottom: 18px;"> 382 ' . esc_html__('Unlock 5 automated AdSense slots, in-article paragraph injection, and sticky sidebar monetization for just $15/year via Lemon Squeezy.', 'nexversal') . ' 383 </p> 384 <a href="' . esc_url(nexversal_get_checkout_url()) . '" target="_blank" rel="noopener noreferrer" class="button button-primary" style="background: #10b981; border-color: #10b981; font-weight: 800; width: 100%; text-align: center; padding: 7px 0; height: auto; margin-bottom: 10px; box-shadow: 0 4px 10px rgba(16,185,129,0.3);"> 385 ⚡ ' . esc_html__('Buy PRO Plan ($15/year)', 'nexversal') . ' 386 </a> 387 <a href="' . esc_url(admin_url('themes.php?page=nexversal-pro')) . '" class="button" style="width: 100%; text-align: center; font-weight: 600; padding: 5px 0; height: auto;"> 388 🔑 ' . esc_html__('Enter License Key', 'nexversal') . ' 389 </a> 390 </div>'; 391 392 $wp_customize->add_control(new WP_Customize_Control($wp_customize, 'nexversal_pro_lock_info', array( 393 'label' => esc_html__('PRO Feature Required', 'nexversal'), 394 'description' => $lock_html, 395 'section' => 'nexversal_monetization', 396 'type' => 'hidden', 397 ))); 398 399 // Exit immediately so ZERO ad checkboxes or textareas are registered for free users! 400 return; 401 } 402 403 // ========================================================================= 404 // ONLY REACHED IF PRO IS ACTIVE ($is_pro === true) 405 // ========================================================================= 406 407 // Master Toggle 1114 // Plan Status Badge 1115 $status_badge_html = $is_pro 1116 ? '<div style="background:#ecfdf5;border:1px solid #a7f3d0;color:#065f46;padding:10px 12px;border-radius:8px;font-size:12px;margin-bottom:12px;"><strong>✓ PRO Plan Active:</strong> Unlimited simultaneous ads unlocked!</div>' 1117 : '<div style="background:#f8fafc;border:1px solid #cbd5e1;color:#1e293b;padding:10px 12px;border-radius:8px;font-size:12px;margin-bottom:12px;"><strong>⚡ Free Plan:</strong> Up to <strong>3 active ads</strong> allowed (Currently: <strong>' . intval($active_count) . '/3 Active</strong>).</div>'; 1118 1119 $wp_customize->add_setting('nexversal_plan_status_info', array('default' => '', 'sanitize_callback' => 'wp_kses_post')); 1120 $wp_customize->add_control(new WP_Customize_Control($wp_customize, 'nexversal_plan_status_info', array( 1121 'label' => '', 1122 'description' => $status_badge_html, 1123 'section' => 'nexversal_monetization', 1124 'type' => 'hidden', 1125 ))); 1126 1127 // Master Switch 408 1128 $wp_customize->add_setting('nexversal_ads_enabled', array( 409 1129 'default' => true, … … 411 1131 )); 412 1132 $wp_customize->add_control('nexversal_ads_enabled', array( 413 'label' => esc_html__('Enable Built-in Ad Engine', 'nexversal'),414 'description' => esc_html__(' Master switch to display or hide all theme ad placements.', 'nexversal'),1133 'label' => esc_html__('Enable All Ads', 'nexversal'), 1134 'description' => esc_html__('Turn this ON to display ads on your website.', 'nexversal'), 415 1135 'section' => 'nexversal_monetization', 416 1136 'type' => 'checkbox', 417 1137 )); 418 1138 419 // S ponsored Label Toggle (Google AdSense Policy Compliance)1139 // Show Advertisement Label 420 1140 $wp_customize->add_setting('nexversal_ads_show_label', array( 421 1141 'default' => true, … … 424 1144 $wp_customize->add_control('nexversal_ads_show_label', array( 425 1145 'label' => esc_html__('Show "Advertisement" Label', 'nexversal'), 426 'description' => esc_html__('Re commended for Google AdSense & regulatorycompliance.', 'nexversal'),1146 'description' => esc_html__('Required for Google AdSense compliance.', 'nexversal'), 427 1147 'section' => 'nexversal_monetization', 428 1148 'type' => 'checkbox', 429 1149 )); 430 1150 431 // --- Slot 1: Header Leaderboard (Below Navigation) --- 1151 // ------------------------------------------------------------------------- 1152 // 1-Click Shortcut: Google AdSense Publisher ID 1153 // ------------------------------------------------------------------------- 1154 $wp_customize->add_setting('nexversal_adsense_pub_id', array( 1155 'default' => '', 1156 'sanitize_callback' => 'sanitize_text_field', 1157 )); 1158 $wp_customize->add_control('nexversal_adsense_pub_id', array( 1159 'label' => esc_html__('⚡ 1-Click Google Auto-Ads (Optional Shortcut)', 'nexversal'), 1160 'description' => esc_html__('Paste your AdSense Publisher ID (e.g. ca-pub-xxxxxxxxxxxxxxxx). Google will automatically run ads and auto-detect visitor country without configuring individual slots!', 'nexversal'), 1161 'section' => 'nexversal_monetization', 1162 'type' => 'text', 1163 'input_attrs' => array('placeholder' => 'ca-pub-XXXXXXXXXXXXXXXX'), 1164 )); 1165 1166 // Divider: Manual Slot Settings 1167 $wp_customize->add_setting('nexversal_slots_heading', array('default' => '', 'sanitize_callback' => 'sanitize_text_field')); 1168 $wp_customize->add_control(new WP_Customize_Control($wp_customize, 'nexversal_slots_heading', array( 1169 'label' => esc_html__('📍 Individual Ad Slots (Pick Your 3 Free Slots)', 'nexversal'), 1170 'description' => esc_html__('Turn ON up to 3 slots for Free, or unlimited with PRO. Country targeting is automatic!', 'nexversal'), 1171 'section' => 'nexversal_monetization', 1172 'type' => 'hidden', 1173 ))); 1174 1175 // --- Slot 1: Header Banner (728x90) --- 432 1176 $wp_customize->add_setting('nexversal_ad_header_enabled', array( 433 1177 'default' => false, … … 435 1179 )); 436 1180 $wp_customize->add_control('nexversal_ad_header_enabled', array( 437 'label' => esc_html__(' [Slot 1] Enable Header Banner Ad(728x90)', 'nexversal'),1181 'label' => esc_html__('1. Top Header Banner (728x90)', 'nexversal'), 438 1182 'section' => 'nexversal_monetization', 439 1183 'type' => 'checkbox', … … 444 1188 )); 445 1189 $wp_customize->add_control('nexversal_ad_header_code', array( 446 'label' => esc_html__('Header Ad Code / HTML / AdSense', 'nexversal'),447 'description' => esc_html__(' Appears directly beneath the top header across all pages.', 'nexversal'),1190 'label' => '', 1191 'description' => esc_html__('Paste Top Header ad code / HTML / AdSense.', 'nexversal'), 448 1192 'section' => 'nexversal_monetization', 449 1193 'type' => 'textarea', 450 )); 451 452 // --- Slot 2: Auto In-Article Mid-Content Ad --- 1194 'input_attrs' => array('rows' => 3, 'placeholder' => '<!-- Paste header ad code here -->'), 1195 )); 1196 1197 // --- Slot 2: In-Article Mid-Content Ad (With Dropdown Menu) --- 453 1198 $wp_customize->add_setting('nexversal_ad_article_enabled', array( 454 1199 'default' => false, … … 456 1201 )); 457 1202 $wp_customize->add_control('nexversal_ad_article_enabled', array( 458 'label' => esc_html__(' [Slot 2] Enable In-Article Mid-Content Ad', 'nexversal'),1203 'label' => esc_html__('2. In-Article Content Ad', 'nexversal'), 459 1204 'section' => 'nexversal_monetization', 460 1205 'type' => 'checkbox', 461 1206 )); 462 1207 $wp_customize->add_setting('nexversal_ad_article_pos', array( 463 'default' => 3,464 'sanitize_callback' => ' absint',1208 'default' => '3', 1209 'sanitize_callback' => 'sanitize_text_field', 465 1210 )); 466 1211 $wp_customize->add_control('nexversal_ad_article_pos', array( 467 'label' => esc_html__('In sert after paragraph #', 'nexversal'),468 'description' => esc_html__(' Default is after paragraph 3 (optimal viewability).', 'nexversal'),1212 'label' => esc_html__('In-Article Ad Position (Dropdown)', 'nexversal'), 1213 'description' => esc_html__('Choose where inside the article narrative to display the ad.', 'nexversal'), 469 1214 'section' => 'nexversal_monetization', 470 'type' => 'number', 471 'input_attrs' => array('min' => 1, 'max' => 20, 'step' => 1), 1215 'type' => 'select', 1216 'choices' => array( 1217 '2' => esc_html__('Early — After 2nd Paragraph', 'nexversal'), 1218 '3' => esc_html__('Optimal — After 3rd Paragraph (Recommended)', 'nexversal'), 1219 '4' => esc_html__('Midway — After 4th Paragraph', 'nexversal'), 1220 '5' => esc_html__('Deep — After 5th Paragraph', 'nexversal'), 1221 '50' => esc_html__('Center — Exact Middle of Article (Auto)', 'nexversal'), 1222 ), 472 1223 )); 473 1224 $wp_customize->add_setting('nexversal_ad_article_code', array( … … 476 1227 )); 477 1228 $wp_customize->add_control('nexversal_ad_article_code', array( 478 'label' => esc_html__('In-Article Ad Code / AdSense', 'nexversal'),479 'description' => esc_html__(' Automatically injected inside post body narrative.', 'nexversal'),1229 'label' => '', 1230 'description' => esc_html__('Paste in-article ad code / HTML.', 'nexversal'), 480 1231 'section' => 'nexversal_monetization', 481 1232 'type' => 'textarea', 1233 'input_attrs' => array('rows' => 3, 'placeholder' => '<!-- Paste in-article ad code here -->'), 482 1234 )); 483 1235 … … 488 1240 )); 489 1241 $wp_customize->add_control('nexversal_ad_post_bottom_enabled', array( 490 'label' => esc_html__(' [Slot 3] Enable Post BottomAd', 'nexversal'),1242 'label' => esc_html__('3. Below Post Content Ad', 'nexversal'), 491 1243 'section' => 'nexversal_monetization', 492 1244 'type' => 'checkbox', … … 497 1249 )); 498 1250 $wp_customize->add_control('nexversal_ad_post_bottom_code', array( 499 'label' => esc_html__('Post Bottom Ad Code', 'nexversal'),500 'description' => esc_html__(' Appears right after post content before author box.', 'nexversal'),1251 'label' => '', 1252 'description' => esc_html__('Paste post bottom ad code / HTML.', 'nexversal'), 501 1253 'section' => 'nexversal_monetization', 502 1254 'type' => 'textarea', 503 )); 504 505 // --- Slot 4: Homepage In-Feed Ad (Between Cards) --- 1255 'input_attrs' => array('rows' => 3, 'placeholder' => '<!-- Paste post bottom ad code here -->'), 1256 )); 1257 1258 // --- Slot 4: Homepage In-Feed Ad (With Dropdown Menu) --- 506 1259 $wp_customize->add_setting('nexversal_ad_infeed_enabled', array( 507 1260 'default' => false, … … 509 1262 )); 510 1263 $wp_customize->add_control('nexversal_ad_infeed_enabled', array( 511 'label' => esc_html__(' [Slot 4] Enable In-Feed Ad (Post Grid)', 'nexversal'),1264 'label' => esc_html__('4. Homepage In-Feed Grid Ad', 'nexversal'), 512 1265 'section' => 'nexversal_monetization', 513 1266 'type' => 'checkbox', 514 1267 )); 515 1268 $wp_customize->add_setting('nexversal_ad_infeed_pos', array( 516 'default' => 3,517 'sanitize_callback' => ' absint',1269 'default' => '3', 1270 'sanitize_callback' => 'sanitize_text_field', 518 1271 )); 519 1272 $wp_customize->add_control('nexversal_ad_infeed_pos', array( 520 'label' => esc_html__(' Display In-Feed Ad after card #', 'nexversal'),521 'description' => esc_html__(' E.g., 3 means after the 3rd post card in the grid.', 'nexversal'),1273 'label' => esc_html__('In-Feed Card Position (Dropdown)', 'nexversal'), 1274 'description' => esc_html__('Choose after which article card to insert this ad.', 'nexversal'), 522 1275 'section' => 'nexversal_monetization', 523 'type' => 'number', 524 'input_attrs' => array('min' => 1, 'max' => 12, 'step' => 1), 1276 'type' => 'select', 1277 'choices' => array( 1278 '2' => esc_html__('After 2nd Article Card', 'nexversal'), 1279 '3' => esc_html__('After 3rd Article Card (Recommended)', 'nexversal'), 1280 '4' => esc_html__('After 4th Article Card', 'nexversal'), 1281 '6' => esc_html__('After 6th Article Card', 'nexversal'), 1282 ), 525 1283 )); 526 1284 $wp_customize->add_setting('nexversal_ad_infeed_code', array( … … 529 1287 )); 530 1288 $wp_customize->add_control('nexversal_ad_infeed_code', array( 531 'label' => esc_html__('In-Feed Ad Code / AdSense In-feed Unit', 'nexversal'), 1289 'label' => '', 1290 'description' => esc_html__('Paste in-feed grid ad code / AdSense unit.', 'nexversal'), 532 1291 'section' => 'nexversal_monetization', 533 1292 'type' => 'textarea', 1293 'input_attrs' => array('rows' => 3, 'placeholder' => '<!-- Paste in-feed ad code here -->'), 534 1294 )); 535 1295 … … 540 1300 )); 541 1301 $wp_customize->add_control('nexversal_ad_sidebar_enabled', array( 542 'label' => esc_html__(' [Slot 5] EnableSticky Sidebar Ad (300x250 / 300x600)', 'nexversal'),1302 'label' => esc_html__('5. Sticky Sidebar Ad (300x250 / 300x600)', 'nexversal'), 543 1303 'section' => 'nexversal_monetization', 544 1304 'type' => 'checkbox', … … 549 1309 )); 550 1310 $wp_customize->add_control('nexversal_ad_sidebar_code', array( 551 'label' => esc_html__('Sidebar Ad Code', 'nexversal'),552 'description' => esc_html__(' Floats as user scrolls down the page in the sidebar.', 'nexversal'),1311 'label' => '', 1312 'description' => esc_html__('Paste sticky sidebar ad code.', 'nexversal'), 553 1313 'section' => 'nexversal_monetization', 554 1314 'type' => 'textarea', 555 )); 1315 'input_attrs' => array('rows' => 3, 'placeholder' => '<!-- Paste sidebar ad code here -->'), 1316 )); 1317 1318 // ------------------------------------------------------------------------- 1319 // Global Worldwide Geo-Targeting & PRO Upsell Card 1320 // ------------------------------------------------------------------------- 1321 $geo_info_html = ' 1322 <div style="background:linear-gradient(135deg, #f0fdf4 0%, #ecfdf5 100%);border:1px solid #a7f3d0;padding:12px 14px;border-radius:8px;font-size:12px;margin:12px 0 8px 0;line-height:1.45;"> 1323 <div style="display:flex;align-items:center;justify-content:space-between;gap:6px;font-weight:700;color:#065f46;margin-bottom:4px;"> 1324 <span style="font-size:12.5px;">🌍 ' . esc_html__('Global Smart Geo-Targeting', 'nexversal') . '</span> 1325 <span style="font-size:10px;font-weight:700;background:#dcfce7;color:#15803d;border:1px solid #86efac;padding:1px 6px;border-radius:9999px;">AUTOMATIC</span> 1326 </div> 1327 <p style="margin:0 0 8px 0;color:#334155;font-size:11.5px;line-height:1.45;"> 1328 ' . esc_html__('Zero setup. Serves hyper-targeted, high-CPM ads to every visitor worldwide (195+ countries) in their local currency. Free plan is strictly capped at 3 ads.', 'nexversal') . ' 1329 </p> 1330 <a href="' . esc_url(nexversal_get_checkout_url()) . '" target="_blank" rel="noopener noreferrer" style="display:inline-flex;align-items:center;gap:4px;font-weight:700;color:#4f46e5;text-decoration:none;font-size:11.5px;"> 1331 ⚡ ' . esc_html__('Upgrade to PRO for Unlimited Ads (/yr) →', 'nexversal') . ' 1332 </a> 1333 </div>'; 1334 1335 $wp_customize->add_setting('nexversal_auto_geo_info', array('default' => '', 'sanitize_callback' => 'wp_kses_post')); 1336 $wp_customize->add_control(new WP_Customize_Control($wp_customize, 'nexversal_auto_geo_info', array( 1337 'label' => '', 1338 'description' => $geo_info_html, 1339 'section' => 'nexversal_monetization', 1340 'type' => 'hidden', 1341 ))); 1342 1343 // ------------------------------------------------------------------------- 1344 // PRO Feature: Ad-Blocker Bypass 1345 // ------------------------------------------------------------------------- 1346 $wp_customize->add_setting('nexversal_pro_adblock_heading', array('default' => '', 'sanitize_callback' => 'sanitize_text_field')); 1347 $wp_customize->add_control(new WP_Customize_Control($wp_customize, 'nexversal_pro_adblock_heading', array( 1348 'label' => esc_html__('🛡️ PRO: Ad-Blocker Bypass Engine', 'nexversal'), 1349 'description' => $is_pro 1350 ? esc_html__('When an ad blocker (uBlock, AdBlock, Brave) blocks an ad, your fallback banner displays automatically!', 'nexversal') 1351 : esc_html__('🔒 PRO FEATURE ($15/yr): Force ads and affiliate banners to show even when visitors use AdBlockers. Boosts earnings by 25-35%.', 'nexversal'), 1352 'section' => 'nexversal_monetization', 1353 'type' => 'hidden', 1354 ))); 1355 1356 if ($is_pro) { 1357 $wp_customize->add_setting('nexversal_adblock_bypass_enabled', array( 1358 'default' => false, 1359 'sanitize_callback' => 'nexversal_sanitize_checkbox', 1360 )); 1361 $wp_customize->add_control('nexversal_adblock_bypass_enabled', array( 1362 'label' => esc_html__('Enable Ad-Blocker Bypass', 'nexversal'), 1363 'description' => esc_html__('Automatically render fallback message/banner when ads are blocked.', 'nexversal'), 1364 'section' => 'nexversal_monetization', 1365 'type' => 'checkbox', 1366 )); 1367 1368 $wp_customize->add_setting('nexversal_adblock_fallback_code', array( 1369 'default' => '', 1370 'sanitize_callback' => 'nexversal_sanitize_ad_code', 1371 )); 1372 $wp_customize->add_control('nexversal_adblock_fallback_code', array( 1373 'label' => esc_html__('Fallback Affiliate Banner / Custom Message HTML', 'nexversal'), 1374 'description' => esc_html__('Enter custom HTML or an affiliate banner that AdBlockers cannot detect.', 'nexversal'), 1375 'section' => 'nexversal_monetization', 1376 'type' => 'textarea', 1377 'input_attrs' => array('rows' => 3, 'placeholder' => '<!-- Paste fallback banner HTML or affiliate code here -->'), 1378 )); 1379 } 1380 1381 // Upgrade Callout if Free 1382 if (!$is_pro) { 1383 $wp_customize->add_setting('nexversal_pro_upgrade_banner', array('default' => '', 'sanitize_callback' => 'wp_kses_post')); 1384 $upgrade_html = ' 1385 <div style="background: #f8fafc; border: 1px dashed #cbd5e1; border-radius: 8px; padding: 14px; text-align: center; margin-top: 14px;"> 1386 <div style="font-weight: 800; font-size: 13.5px; color: #0f172a; margin-bottom: 4px;">⚡ ' . esc_html__('Run More than 3 Ads? Get PRO!', 'nexversal') . '</div> 1387 <p style="font-size: 12px; color: #64748b; line-height: 1.45; margin-bottom: 12px;">' . esc_html__('Free plan is capped at 3 simultaneous ads. Upgrade to PRO ($15/year) to run unlimited ads, unlock Ad-Blocker Bypass, and view live CTR analytics.', 'nexversal') . '</p> 1388 <a href="' . esc_url(nexversal_get_checkout_url()) . '" target="_blank" rel="noopener noreferrer" class="button button-primary" style="background: #10b981; border-color: #10b981; width: 100%; font-weight: 800; text-align: center;">' . esc_html__('Unlock Unlimited Ads ($15/yr) ➔', 'nexversal') . '</a> 1389 </div>'; 1390 1391 $wp_customize->add_control(new WP_Customize_Control($wp_customize, 'nexversal_pro_upgrade_banner', array( 1392 'label' => '', 1393 'description' => $upgrade_html, 1394 'section' => 'nexversal_monetization', 1395 'type' => 'hidden', 1396 ))); 1397 } 556 1398 } 557 1399 add_action('customize_register', 'nexversal_ads_customize_register', 40); 1400 1401 /* ========================================================================== 1402 Post-by-Post Ad Exclusion Framework (Gutenberg & Classic Meta Boxes) 1403 ========================================================================== */ 1404 1405 /** 1406 * Register post meta key for REST API & Gutenberg Sidebar Component 1407 */ 1408 function nexversal_register_monetization_meta() { 1409 $post_types = array('post', 'page'); 1410 foreach ($post_types as $pt) { 1411 register_post_meta($pt, '_nexversal_disable_ads', array( 1412 'show_in_rest' => true, 1413 'single' => true, 1414 'type' => 'boolean', 1415 'auth_callback' => function() use ($pt) { 1416 return current_user_can($pt === 'page' ? 'edit_pages' : 'edit_posts'); 1417 }, 1418 'sanitize_callback' => 'rest_sanitize_boolean', 1419 'description' => __('Disable Google AdSense and theme ads for this specific post/page.', 'nexversal'), 1420 )); 1421 } 1422 } 1423 add_action('init', 'nexversal_register_monetization_meta'); 1424 1425 /** 1426 * Register "Monetization Controls" Meta Box in Post and Page editor screens 1427 */ 1428 function nexversal_add_monetization_meta_box() { 1429 $screens = array('post', 'page'); 1430 foreach ($screens as $screen) { 1431 add_meta_box( 1432 'nexversal_monetization_metabox', 1433 __('Monetization Controls', 'nexversal'), 1434 'nexversal_render_monetization_meta_box', 1435 $screen, 1436 'side', 1437 'default' 1438 ); 1439 } 1440 } 1441 add_action('add_meta_boxes', 'nexversal_add_monetization_meta_box'); 1442 1443 /** 1444 * Render the Monetization Controls Meta Box 1445 * 1446 * @param WP_Post $post Current post object 1447 */ 1448 function nexversal_render_monetization_meta_box($post) { 1449 wp_nonce_field('nexversal_save_monetization_action', 'nexversal_monetization_nonce'); 1450 $is_disabled = (bool) get_post_meta($post->ID, '_nexversal_disable_ads', true); 1451 ?> 1452 <div class="nx-meta-box-wrap" style="padding: 6px 0;"> 1453 <label for="nexversal_disable_ads" style="display: flex; align-items: flex-start; gap: 8px; cursor: pointer; font-weight: 600; font-size: 13px; line-height: 1.4; color: #1e293b;"> 1454 <input type="checkbox" name="nexversal_disable_ads" id="nexversal_disable_ads" value="1" <?php checked($is_disabled, true); ?> style="margin-top: 2px;" /> 1455 <span><?php esc_html_e('Disable AdSense for this post', 'nexversal'); ?></span> 1456 </label> 1457 <p class="description" style="margin: 8px 0 0 24px; font-size: 12px; line-height: 1.4; color: #64748b;"> 1458 <?php esc_html_e('When checked, all header, in-article, bottom, in-feed, sidebar ads, Auto Ads, and shortcodes are completely suppressed on this article.', 'nexversal'); ?> 1459 </p> 1460 <div style="margin-top: 12px; padding: 6px 10px; border-radius: 4px; font-size: 11px; font-weight: 500; <?php echo $is_disabled ? 'background: #fef2f2; color: #991b1b; border: 1px solid #fecaca;' : 'background: #f0fdf4; color: #166534; border: 1px solid #bbf7d0;'; ?>"> 1461 <strong><?php esc_html_e('Status:', 'nexversal'); ?></strong> 1462 <?php echo $is_disabled ? esc_html__('Ads Suppressed on this Post', 'nexversal') : esc_html__('Ads Active (Inheriting Global Settings)', 'nexversal'); ?> 1463 </div> 1464 </div> 1465 <?php 1466 } 1467 1468 /** 1469 * Save Monetization Meta Box Data with strict validation & sanitization 1470 * 1471 * @param int $post_id Post ID 1472 * @param WP_Post $post Post object 1473 */ 1474 function nexversal_save_monetization_meta_box($post_id, $post) { 1475 // 1. Verify nonce security check 1476 if (!isset($_POST['nexversal_monetization_nonce']) || !wp_verify_nonce($_POST['nexversal_monetization_nonce'], 'nexversal_save_monetization_action')) { 1477 return; 1478 } 1479 1480 // 2. Prevent autosave from overwriting meta 1481 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { 1482 return; 1483 } 1484 1485 // 3. Prevent revisions from storing post meta 1486 if (wp_is_post_revision($post_id)) { 1487 return; 1488 } 1489 1490 // 4. Verify user permissions 1491 $post_type = get_post_type($post_id); 1492 $cap = ($post_type === 'page') ? 'edit_page' : 'edit_post'; 1493 if (!current_user_can($cap, $post_id)) { 1494 return; 1495 } 1496 1497 // 5. Sanitize and save boolean flag 1498 if (!empty($_POST['nexversal_disable_ads'])) { 1499 update_post_meta($post_id, '_nexversal_disable_ads', true); 1500 } else { 1501 delete_post_meta($post_id, '_nexversal_disable_ads'); 1502 } 1503 } 1504 add_action('save_post', 'nexversal_save_monetization_meta_box', 10, 2); 1505 1506 /** 1507 * Check if ads are disabled on a specific post or current singular template 1508 * 1509 * @param int|null $post_id Optional post ID 1510 * @return bool True if ads should be suppressed on this post, false otherwise. 1511 */ 1512 function nexversal_is_ad_disabled_for_post($post_id = null) { 1513 if (empty($post_id)) { 1514 if (is_singular()) { 1515 $post_id = get_queried_object_id(); 1516 } elseif (in_the_loop()) { 1517 $post_id = get_the_ID(); 1518 } 1519 } 1520 1521 if (empty($post_id)) { 1522 return false; 1523 } 1524 1525 $disabled = (bool) get_post_meta($post_id, '_nexversal_disable_ads', true); 1526 1527 /** 1528 * Absolute filter hook for theme's structural ad engine 1529 * 1530 * @param bool $disabled Whether ads are suppressed 1531 * @param int $post_id The evaluated post ID 1532 */ 1533 return (bool) apply_filters('nexversal_is_ad_disabled_for_post', $disabled, $post_id); 1534 } 1535 1536 /** 1537 * Master structural filter to suppress any ad unit evaluation dynamically 1538 */ 1539 add_filter('nexversal_should_render_ad', function($should_render, $slot = '') { 1540 if (nexversal_is_ad_disabled_for_post()) { 1541 return false; 1542 } 1543 return $should_render; 1544 }, 10, 2); 1545 1546 /** 1547 * Add Ads Status Column to Posts & Pages List Table 1548 */ 1549 function nexversal_add_ad_column_header($columns) { 1550 $columns['nx_ad_status'] = __('Ads', 'nexversal'); 1551 return $columns; 1552 } 1553 add_filter('manage_post_posts_columns', 'nexversal_add_ad_column_header'); 1554 add_filter('manage_page_posts_columns', 'nexversal_add_ad_column_header'); 1555 1556 function nexversal_render_ad_column_content($column, $post_id) { 1557 if ($column === 'nx_ad_status') { 1558 $disabled = (bool) get_post_meta($post_id, '_nexversal_disable_ads', true); 1559 if ($disabled) { 1560 echo '<span style="display:inline-block; padding: 2px 7px; border-radius: 9999px; font-size: 11px; font-weight: 700; background: #fee2e2; color: #dc2626;">' . esc_html__('Disabled', 'nexversal') . '</span>'; 1561 } else { 1562 echo '<span style="display:inline-block; padding: 2px 7px; border-radius: 9999px; font-size: 11px; font-weight: 700; background: #dcfce7; color: #16a34a;">' . esc_html__('Active', 'nexversal') . '</span>'; 1563 } 1564 } 1565 } 1566 add_action('manage_post_posts_custom_column', 'nexversal_render_ad_column_content', 10, 2); 1567 add_action('manage_page_posts_custom_column', 'nexversal_render_ad_column_content', 10, 2); 558 1568 559 1569 /** … … 568 1578 569 1579 /** 570 * Render an ad slot with compliance container and label1580 * Render an ad slot with compliance container, label, Free limit check & Ad-Blocker Bypass 571 1581 * 572 1582 * @param string $slot Slot slug (header, in_article, post_bottom, infeed, sidebar) … … 575 1585 */ 576 1586 function nexversal_render_ad($slot, $echo = true) { 577 // Paid PRO Check: Only render ads if PRO plan ($15/year) is active! 578 if (!nexversal_is_pro_active()) { 1587 // 1. Post-by-Post Ad Exclusion Short-circuit 1588 if (function_exists('nexversal_is_ad_disabled_for_post') && nexversal_is_ad_disabled_for_post()) { 1589 return ''; 1590 } 1591 1592 // 2. Master structural filter 1593 if (!apply_filters('nexversal_should_render_ad', true, $slot)) { 1594 return ''; 1595 } 1596 1597 // 3. Strict Free Plan Limit: Exactly 3 ads per page across all sources 1598 if (function_exists('nexversal_can_render_ad') && !nexversal_can_render_ad()) { 579 1599 return ''; 580 1600 } … … 585 1605 } 586 1606 587 $slot_on = get_theme_mod("nexversal_ad_{$slot}_enabled", false);588 $ad_code = get_theme_mod("nexversal_ad_{$slot}_code", '');1607 $slot_on = get_theme_mod("nexversal_ad_{$slot}_enabled", false); 1608 $ad_code = get_theme_mod("nexversal_ad_{$slot}_code", ''); 589 1609 590 1610 if (!$slot_on || empty(trim($ad_code))) { … … 592 1612 } 593 1613 1614 // Free vs PRO Active Ad Limit Check (Free: Max 3 active slots) 1615 $allowed_slots = nexversal_get_allowed_ad_slots(); 1616 if (!in_array($slot, $allowed_slots, true)) { 1617 return ''; // Exceeded Free Plan 3-ad limit 1618 } 1619 1620 $is_pro = nexversal_is_pro_active(); 594 1621 $show_label = get_theme_mod('nexversal_ads_show_label', true); 595 $output = '<div class="nx-ad-slot nx-ad-' . esc_attr($slot) . '">'; 596 597 if ($show_label) { 598 $output .= '<span class="nx-ad-label">' . esc_html__('ADVERTISEMENT', 'nexversal') . '</span>'; 599 } 600 601 $output .= '<div class="nx-ad-content">' . $ad_code . '</div>'; 602 $output .= '</div>'; 1622 1623 // Admin / Editor Shielding: Intercept live AdSense scripts for administrators & editors to prevent invalid traffic 1624 if (function_exists('nexversal_wrap_adsense_code')) { 1625 $ad_code = nexversal_wrap_adsense_code($ad_code, $slot); 1626 } 1627 1628 // GDPR Interception: Filter ad scripts into inert type="text/plain" if user consent is pending 1629 if (function_exists('nexversal_filter_ad_code_for_consent')) { 1630 $ad_code = nexversal_filter_ad_code_for_consent($ad_code); 1631 } 1632 1633 $fallback = ''; 1634 if ($is_pro && get_theme_mod('nexversal_adblock_bypass_enabled', false)) { 1635 $fallback = get_theme_mod('nexversal_adblock_fallback_code', ''); 1636 } 1637 1638 // CLS Optimization: Generate semantic HTML skeleton wrapper container with reserved bounding box 1639 if (function_exists('nexversal_render_ad_skeleton')) { 1640 $output = nexversal_render_ad_skeleton($slot, $ad_code, $fallback, $show_label); 1641 } else { 1642 $output = '<div class="nx-ad-slot nx-ad-' . esc_attr($slot) . '" data-ad-slot="' . esc_attr($slot) . '">'; 1643 if ($show_label) { 1644 $output .= '<span class="nx-ad-label">' . esc_html__('ADVERTISEMENT', 'nexversal') . '</span>'; 1645 } 1646 $output .= '<div class="nx-ad-content">' . $ad_code . '</div>'; 1647 if (!empty(trim($fallback))) { 1648 $output .= '<div class="nx-ad-blocker-bypass" style="display: none;">' . $fallback . '</div>'; 1649 } 1650 $output .= '</div>'; 1651 } 1652 1653 // Increment global page ad execution tracker 1654 if (function_exists('nexversal_increment_page_ad_count')) { 1655 nexversal_increment_page_ad_count(); 1656 } 603 1657 604 1658 if ($echo) { … … 620 1674 } 621 1675 622 // Paid PRO Check 623 if (!nexversal_is_pro_active()) { 1676 // Post-by-Post Ad Exclusion Short-circuit 1677 if (function_exists('nexversal_is_ad_disabled_for_post') && nexversal_is_ad_disabled_for_post()) { 1678 return $content; 1679 } 1680 1681 // Strict Free Plan Limit: Exactly 3 ads per page across all placements 1682 if (function_exists('nexversal_can_render_ad') && !nexversal_can_render_ad()) { 624 1683 return $content; 625 1684 } … … 633 1692 } 634 1693 635 $pos = max(1, (int) get_theme_mod('nexversal_ad_article_pos', 3)); 1694 // Check Free vs PRO allowed slots 1695 $allowed_slots = nexversal_get_allowed_ad_slots(); 1696 if (!in_array('article', $allowed_slots, true)) { 1697 return $content; 1698 } 1699 636 1700 $ad_html = nexversal_render_ad('article', false); 637 638 1701 if (empty($ad_html)) { 639 1702 return $content; … … 643 1706 $paragraphs = explode('</p>', $content); 644 1707 $total_p = count($paragraphs) - 1; 1708 1709 $pos_setting = get_theme_mod('nexversal_ad_article_pos', '3'); 1710 if ($pos_setting === '50') { 1711 // Automatic middle 1712 $pos = max(1, (int) round($total_p / 2)); 1713 } else { 1714 $pos = max(1, (int) $pos_setting); 1715 } 645 1716 646 1717 if ($total_p >= $pos) { … … 659 1730 } 660 1731 add_filter('the_content', 'nexversal_inject_in_article_ad', 20); 661 662 -
nexversal/3.5.9/index.php
r357433 r357886 1 1 <?php 2 2 /** 3 * Security: Prevent direct script access 3 * Main Template File (Homepage / Blog Feed) for Nexversal Theme 4 * 5 * @package Nexversal 6 * @author Nexes Agency & Muhammad Shahid 7 * @copyright 2026 Nexes Agency & Muhammad Shahid 8 * @license GNU General Public License v2 or later 9 * @link https://nexversal.com 4 10 */ 11 5 12 if (!defined('ABSPATH')) { 6 13 exit; 7 14 } 8 9 /**10 * Main Template File (Homepage / Blog Feed)11 *12 * @package Nexversal13 */14 15 15 16 get_header(); … … 31 32 32 33 <?php 33 $hero_btn_text = get_theme_mod('nexversal_hero_btn_text', 'Explore Articles'); 34 $hero_btn_url = get_theme_mod('nexversal_hero_btn_url', '#primary'); 35 if (!empty($hero_btn_text)) : 36 ?> 37 <div class="nx-hero-actions" style="margin-bottom: 22px;"> 38 <a href="<?php echo esc_url($hero_btn_url); ?>" class="nx-btn nx-btn-primary nx-hero-btn"> 39 <span><?php echo esc_html($hero_btn_text); ?></span> 40 <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" style="margin-left: 6px;"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg> 41 </a> 42 </div> 43 <?php endif; ?> 34 if (function_exists('nexversal_render_hero_button')) { 35 nexversal_render_hero_button(); 36 } 37 ?> 44 38 45 39 <?php … … 106 100 </div> 107 101 108 <?php get_sidebar(); ?> 102 <?php 103 if (!function_exists('nexversal_is_meta_disabled') || !nexversal_is_meta_disabled('disable_sidebar')) { 104 get_sidebar(); 105 } 106 ?> 109 107 </div> 110 108 </main> -
nexversal/3.5.9/page.php
r357433 r357886 1 1 <?php 2 2 /** 3 * Security: Prevent direct script access 3 * Page template for Nexversal Theme 4 * 5 * @package Nexversal 6 * @author Nexes Agency & Muhammad Shahid 7 * @copyright 2026 Nexes Agency & Muhammad Shahid 8 * @license GNU General Public License v2 or later 9 * @link https://nexversal.com 4 10 */ 11 5 12 if (!defined('ABSPATH')) { 6 13 exit; 7 14 } 8 9 /**10 * Page template11 *12 * @package Nexversal13 */14 15 15 16 get_header(); … … 22 23 while (have_posts()) : 23 24 the_post(); 24 25 $raw_content = get_the_content();26 $page_title = get_the_title();27 $page_slug = get_post_field('post_name', get_the_ID());28 $is_sample_page = ($page_slug === 'sample-page') ||29 (stripos($raw_content, 'bike messenger by day') !== false) ||30 (stripos($page_title, 'Sample Page') !== false);31 25 ?> 32 26 <article id="post-<?php the_ID(); ?>" <?php post_class('nx-single-article nx-page-card'); ?>> 33 <?php nexversal_breadcrumbs(); ?> 27 <?php 28 if (!function_exists('nexversal_is_meta_disabled') || !nexversal_is_meta_disabled('disable_breadcrumbs')) { 29 nexversal_breadcrumbs(); 30 } 31 ?> 34 32 35 <h1 class="nx-single-title"> 36 <?php echo $is_sample_page ? esc_html__('About Us — Nexes Agency', 'nexversal') : esc_html($page_title); ?> 37 </h1> 33 <?php if (!function_exists('nexversal_is_meta_disabled') || !nexversal_is_meta_disabled('disable_title')) : ?> 34 <h1 class="nx-single-title"> 35 <?php the_title(); ?> 36 </h1> 37 <?php endif; ?> 38 38 39 39 <?php if (has_post_thumbnail()) : ?> … … 44 44 45 45 <div class="nx-single-content"> 46 <?php if ($is_sample_page) : ?> 47 <div class="nx-agency-hero" style="background: linear-gradient(135deg, rgba(79, 70, 229, 0.12) 0%, rgba(6, 182, 212, 0.1) 100%); border: 1px solid var(--nx-border); border-radius: var(--nx-radius); padding: 32px 28px; margin-bottom: 32px;"> 48 <span style="display: inline-block; font-size: 0.75rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--nx-primary); background: var(--nx-primary-light); padding: 4px 12px; border-radius: 999px; margin-bottom: 12px;"><?php esc_html_e('Premier Digital Agency • Pakistan', 'nexversal'); ?></span> 49 <h2 style="font-size: 1.85rem; font-weight: 800; margin-top: 0; margin-bottom: 12px; color: var(--nx-dark); line-height: 1.3;"> 50 <?php esc_html_e('Welcome to Nexes — Driving Digital Dominance & Engineering Excellence', 'nexversal'); ?> 51 </h2> 52 <p style="font-size: 1.05rem; line-height: 1.7; color: var(--nx-text); margin-bottom: 0;"> 53 <?php esc_html_e('We are a premier, full-scale digital agency proudly originating from Pakistan. Nexes unites elite software engineering with cutting-edge SEO and performance marketing to scale ambitious digital brands across the globe.', 'nexversal'); ?> 54 </p> 55 </div> 46 <?php 47 the_content(); 56 48 57 <div class="nx-profile-lead" style="display: flex; gap: 24px; align-items: flex-start; background: var(--nx-bg); border: 1px solid var(--nx-border); border-radius: var(--nx-radius); padding: 24px; margin-bottom: 32px; flex-wrap: wrap;"> 58 <div style="width: 80px; height: 80px; border-radius: 50%; background: linear-gradient(135deg, #4f46e5 0%, #06b6d4 100%); display: flex; align-items: center; justify-content: center; color: #fff; font-weight: 800; font-size: 1.8rem; flex-shrink: 0; box-shadow: var(--nx-shadow-sm);"> 59 MS 60 </div> 61 <div style="flex: 1; min-width: 240px;"> 62 <span style="font-size: 0.78rem; font-weight: 700; color: var(--nx-primary); text-transform: uppercase; letter-spacing: 0.05em;"><?php esc_html_e('Founder & Lead Specialist', 'nexversal'); ?></span> 63 <h3 style="font-size: 1.45rem; font-weight: 800; margin: 4px 0 8px 0; color: var(--nx-dark);">Muhammad Shahid</h3> 64 <p style="font-size: 0.98rem; line-height: 1.6; color: var(--nx-text); margin-bottom: 0;"> 65 Senior <strong>SEO Specialist</strong> and organic search architect with proven experience driving millions of search impressions and high-intent organic clicks. Muhammad specializes in technical SEO, site architecture, semantic search optimization, and algorithm-resilient growth strategies. 66 </p> 67 </div> 68 </div> 69 70 <h3 style="font-size: 1.35rem; font-weight: 800; margin-top: 32px; margin-bottom: 16px; color: var(--nx-dark);"><?php esc_html_e('Our Cross-Functional Agency Capabilities', 'nexversal'); ?></h3> 71 <div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 20px; margin-bottom: 36px;"> 72 <div style="background: var(--nx-bg); border: 1px solid var(--nx-border); border-radius: var(--nx-radius); padding: 22px;"> 73 <div style="width: 42px; height: 42px; border-radius: 8px; background: rgba(79, 70, 229, 0.1); color: var(--nx-primary); display: flex; align-items: center; justify-content: center; margin-bottom: 14px;"> 74 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/></svg> 75 </div> 76 <h4 style="font-size: 1.1rem; font-weight: 700; margin-top: 0; margin-bottom: 8px; color: var(--nx-dark);"><?php esc_html_e('Advanced Search Engine Optimization (SEO)', 'nexversal'); ?></h4> 77 <p style="font-size: 0.92rem; line-height: 1.6; color: var(--nx-text); margin-bottom: 0;"><?php esc_html_e('Full-funnel technical auditing, Core Web Vitals optimization, programmatic keyword strategy, and search engine answer optimization that systematically rank websites on Page 1.', 'nexversal'); ?></p> 78 </div> 79 80 <div style="background: var(--nx-bg); border: 1px solid var(--nx-border); border-radius: var(--nx-radius); padding: 22px;"> 81 <div style="width: 42px; height: 42px; border-radius: 8px; background: rgba(6, 182, 212, 0.1); color: var(--nx-accent); display: flex; align-items: center; justify-content: center; margin-bottom: 14px;"> 82 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/></svg> 83 </div> 84 <h4 style="font-size: 1.1rem; font-weight: 700; margin-top: 0; margin-bottom: 8px; color: var(--nx-dark);"><?php esc_html_e('Elite Software Engineering', 'nexversal'); ?></h4> 85 <p style="font-size: 0.92rem; line-height: 1.6; color: var(--nx-text); margin-bottom: 0;"><?php esc_html_e('Our dedicated team of software engineers builds scalable web applications, custom WordPress themes & plugins, performant API microservices, and robust cloud platforms.', 'nexversal'); ?></p> 86 </div> 87 88 <div style="background: var(--nx-bg); border: 1px solid var(--nx-border); border-radius: var(--nx-radius); padding: 22px;"> 89 <div style="width: 42px; height: 42px; border-radius: 8px; background: rgba(236, 72, 153, 0.1); color: #ec4899; display: flex; align-items: center; justify-content: center; margin-bottom: 14px;"> 90 <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z"/></svg> 91 </div> 92 <h4 style="font-size: 1.1rem; font-weight: 700; margin-top: 0; margin-bottom: 8px; color: var(--nx-dark);"><?php esc_html_e('Social Media & Performance Marketing', 'nexversal'); ?></h4> 93 <p style="font-size: 0.92rem; line-height: 1.6; color: var(--nx-text); margin-bottom: 0;"><?php esc_html_e('Data-driven social media marketing campaigns, high-converting audience targeting, creative brand storytelling, and multi-channel acquisition that convert visitors into loyal clients.', 'nexversal'); ?></p> 94 </div> 95 </div> 96 97 <div style="background: var(--nx-bg); border: 1px dashed var(--nx-border); border-radius: var(--nx-radius); padding: 26px; text-align: center;"> 98 <h4 style="font-size: 1.15rem; font-weight: 700; margin-top: 0; margin-bottom: 8px; color: var(--nx-dark);"><?php esc_html_e('Ready to scale your digital presence?', 'nexversal'); ?></h4> 99 <p style="font-size: 0.95rem; color: var(--nx-text); max-width: 600px; margin: 0 auto 16px auto;"><?php esc_html_e('Partner with Nexes today and experience search excellence, reliable software development, and transformative marketing campaigns.', 'nexversal'); ?></p> 100 <a href="<?php echo esc_url(home_url('/contact/')); ?>" class="nx-btn nx-btn-primary" style="display: inline-flex; align-items: center; gap: 8px;"> 101 <span><?php esc_html_e('Get In Touch With Nexes', 'nexversal'); ?></span> 102 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14M12 5l7 7-7 7"/></svg> 103 </a> 104 </div> 105 <?php else : ?> 106 <?php the_content(); ?> 107 <?php endif; ?> 49 wp_link_pages(array( 50 'before' => '<div class="page-links">' . esc_html__('Pages:', 'nexversal'), 51 'after' => '</div>', 52 )); 53 ?> 108 54 </div> 109 55 … … 117 63 </div> 118 64 119 <?php get_sidebar(); ?> 65 <?php 66 if (!function_exists('nexversal_is_meta_disabled') || !nexversal_is_meta_disabled('disable_sidebar')) { 67 get_sidebar(); 68 } 69 ?> 120 70 </div> 121 71 </main> -
nexversal/3.5.9/readme.txt
r357433 r357886 4 4 Tested up to: 7.0 5 5 Requires PHP: 7.4 6 Stable tag: 2.5.26 Stable tag: 3.5.9 7 7 License: GNU General Public License v2 or later 8 8 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 47 47 == Copyright == 48 48 49 Nexversal WordPress Theme, Copyright 2026 Nexes Agency & Muhammad Shahid.49 Nexversal WordPress Theme, Copyright (C) 2026 Nexes Agency & Muhammad Shahid. 50 50 Nexversal is distributed under the terms of the GNU General Public License v2 or later. 51 51 … … 58 58 == Changelog == 59 59 60 = 3.4.0 = 61 * Page Flexibility System: Astra-style per-page & per-post layout settings meta box with 1-click toggles to Disable Header, Disable Footer, Disable Page Title, Disable Breadcrumbs, and Disable Sidebar. 62 * Content Layout Modes: Added Content Layout selector (Default, Full Width Contained, Full Width Stretched Canvas) for 100% compatibility with Elementor, Beaver Builder, and Gutenberg landing pages. 63 * Dynamic 2-Column Hero Section: Refined, sleek hero headline typography and compact button; added dynamic Featured Article Card on desktop with automated fallback to trending/most-viewed stories. 64 * Hero Featured Post Selector: Added "★ Hero Featured Article" meta box in post editor to pin any article to the homepage hero card. 65 * Dedicated Contact Settings Screen: Added Appearance > Contact Settings in WP-Admin for instant live configuration without Gutenberg blocks. 66 67 = 3.3.0 = 68 * Fixed Home page layout grid: sidebar is now aligned cleanly on the right side next to the main content. 69 * Blog page 4-column article grid: articles now render 4 per row on desktop, eliminating single full-width stretched cards. 70 * Added dynamic Trending Categories tags between Hero CTA button and "Latest Insights & Articles" on Home and Blog pages. 71 * Built-in AJAX Newsletter Subscription: stores email leads directly in WP-Admin with CSV export and management under Appearance > Subscribers. 72 * Automatic email field protection: ensures input field and client-side/server-side validation are always present in newsletter cards. 73 * Fully editable Contact Information: email, headquarters, and response time can now be edited directly in the WordPress Page Editor without design breakage. 74 * Zero-overwrite architecture: automatically preserves existing posts, categories, pages, and menus for users activating the theme on existing websites. 75 * Complete Block Editor visual parity for Home, Blogs, and About Us pages with enhanced editor-style.css. 76 77 = 3.2.0 = 78 * Full Block Editor Editing: Homepage Hero section, headline, badge, and CTA button are now fully editable native Gutenberg blocks. 79 * Registered "Homepage Hero Banner" block pattern in the WordPress Block Inserter. 80 * Enhanced editor-style.css to render Hero blocks in the editor matching the frontend view. 81 82 = 3.1.0 = 83 * Fixed menu item duplication: dynamically deduplicated all navbar links so each page appears exactly once. 84 * Cleaned About Us page: removed hardcoded stat cards (200+, 100%, 100/100) and enabled clean WordPress editor content support. 85 86 = 3.0.0 = 87 * Major Release: Added 4 dedicated page templates (template-home.php, template-blog.php, template-about.php, template-contact.php). 88 * Automated page provisioning: Automatically creates and publishes Home, Blogs, About Us, and Contact pages in WordPress database. 89 * Configures static front page (Home) and posts page (Blogs) with custom layouts out of the box. 90 * Front-page.php template mapping for high-performance homepage rendering. 91 92 = 3.5.4 = 93 * Prevented Cumulative Layout Shift (CLS = 0.00) across all dynamic Google AdSense insertions. 94 * Generated semantic HTML skeleton wrapper containers (`<aside>`, `.nx-ad-bounding-box`) with exact bounding boxes for 5 ad slots (728x90, 336x280, 300x250, 1:1 In-Feed, 300x600). 95 * Applied explicit `aspect-ratio`, `min-height`, and `min-width` parameters with subtle responsive CSS shimmer placeholder styling. 96 97 = 3.5.3 = 98 * Added GDPR and Google AdSense Privacy-Compliant Cookie Consent Banner via `wp_footer`. 99 * Implemented Google Consent Mode v2 default signal integration in `<head>`. 100 * Added strict backend PHP interception of Google AdSense and publisher scripts, converting scripts to inert `type="text/plain"` until verified consent is present. 101 * Zero-dependency pure vanilla JavaScript engine with dynamic DOM activation and Zero Layout Shift (CLS = 0). 102 103 = 3.5.2 = 104 * Added Automated Virtual ads.txt Manager in Appearance > Nexversal PRO settings panel. 105 * Dynamic URL interception for `/ads.txt` with strict `Content-Type: text/plain` headers and output buffer cleanup (zero cPanel file writing required). 106 * Database-sanitized textarea with manage_options capability checks, nonce security, and physical file detection. 107 108 = 3.5.1 = 109 * Fixed About Us URL 404 issue: Added resilient URL resolution and automatic 301 redirection for legacy `?page_id=2` and `/sample-page/` queries. 110 * Enhanced navigation menu filter to auto-heal broken or stale menu links in runtime and database. 111 * Added fallback 301 redirect in 404.php for legacy sample-page and About URL variants. 112 113 = 3.5.9 = 114 * Fixed: Full WordPress.org automated theme check compliance (zero plugin-territory shortcodes, updated publisher placeholder, safe fallback attributes). 115 * Added: Explicit add_theme_support() for custom-header and custom-background. 116 * Improved: Cleaned CSV subscriber stream without low-level fopen/fclose calls. 117 * Improved: Global smart geo-targeting monetization engine with zero frontend CLS and transparent ad reservation. 118 119 = 3.5.8 = 120 * Added Global Page Ad Execution Counter (nexversal_get_page_ad_count & nexversal_can_render_ad) strictly capping Free plan to 3 ads per page across all structural slots, in-article injector, and shortcodes. 121 * Injected data-max-ads-per-page="3" into Google Auto-Ads script tag for Free version, preserved across GDPR consent transitions and suppressed for Admin Shield. 122 * Re-crafted Customizer Geo-Targeting card with high-converting, punchy copy and direct 1-click PRO upgrade link. 123 124 = 3.5.7 = 125 * Added complete 301 Redirects mapping table for all 22 dead GSC 404 URLs directly into theme runtime and 404 handler. 126 * Fixed /blogs to /blog/ canonical routing bug preventing 404 errors on blog archive navigation. 127 * Fixed template resolution lookup in nexversal_get_blogs_page_url to accept both template-blog.php and template-blogs.php. 128 129 = 3.5.6 = 130 * Added Post-by-Post Ad Exclusion Framework (Gutenberg sidebar components & classic meta boxes integration). 131 * Registered "Monetization Controls" meta box on post editor screens with "Disable AdSense for this post" checkbox. 132 * Added absolute filter logic (nexversal_is_ad_disabled_for_post) short-circuiting all structural theme ad slots, auto-ads hooks, and shortcodes. 133 * Added visual Ads status badge in WordPress admin posts and pages list table. 134 135 = 3.5.5 = 136 * Added production-grade Google AdSense Admin Shield wrapper function (nexversal_wrap_adsense_code). 137 * Implemented strict user validation check (current_user_can('administrator') & editors) to prevent accidental self-clicks and AdSense account strikes. 138 * Replaced live publisher scripts with safe local HTML developer diagnostic cards showing real-time slot details, role chips, and zero external requests. 139 * Added support for Google official demo test ad parameters (data-ad-test="on"). 140 * Suppressed Auto-Ads head script injection for authenticated administrative and editorial visitors. 141 142 = 3.5.0 = 143 * Upgraded Ad & Monetization Engine to Freemium model with Google AdSense Auto Ads support and dropdown selectors. 144 * Added customizable author/founder section and adaptive light/dark newsletter subscription styling. 145 * Removed "Background Image" section and custom-background support from Customizer sidebar. 146 147 = 2.8.0 = 148 * Removed redundant Header Image and separate sidebar CTA options from Customizer. 149 * Added live click-to-edit and selective refresh for the Hero CTA button in Customizer preview. 150 * Added instant live color customization for all buttons site-wide (Hero, Cards, Newsletter, Forms, and Block Editor buttons). 151 * Added customize-preview.js for zero-latency postMessage live updates. 152 153 = 2.7.0 = 154 * Fixed footer credit line formatting to render dynamic site title without trailing dot, centered on all devices. 155 * Filtered legacy menu objects to guarantee "Sample Page" is never displayed in navigation. 156 * Added automatic single-run page provisioning (About Us, Blogs, Contact) on theme update. 157 158 = 2.6.0 = 159 * Removed "Sample Page" fallback from navbar and added default Home, Blogs, About Us, Contact links. 160 * Automatically configured essential pages and menu on theme activation. 161 * Added built-in template-contact.php with secure email handling and anti-spam protection. 162 * Dynamically output site name in centered footer copyright credit line. 163 164 = 2.5.5 = 165 * Added built-in template-contact.php with secure email handling and anti-spam protection. 166 * Dynamically output site name in centered footer copyright credit line. 167 * Linked About/Hire actions directly to official contact channel. 168 169 = 2.5.4 = 170 * Standardized formal GPL copyright headers across all main PHP template files. 171 * Verified 100% text-domain consistency ('nexversal') across all translation calls. 172 * Prepared release for official WordPress.org Theme Directory hosting. 173 174 = 2.5.3 = 175 * Resolved all WordPress.org automated theme check requirements. 176 * Fixed dark/light mode header and body color transition synchronization. 177 * Added readme.txt and official GNU GPL copyright notices. 178 * Replaced hardcoded search form with standard get_search_form(). 179 * Removed deprecated add_shortcode from theme files. 180 * Added full WordPress core alignment and caption classes. 181 * Centered footer copyright and powered-by credits. 182 * Added customizable button text settings in Customizer. 183 60 184 = 2.5.2 = 61 * Added complete WordPress.org Theme Handbook compliance.62 185 * Centered footer credits and copyright text. 63 186 * Added custom button text controls in Customizer. 64 * Fixed dark/light mode header background transition synchronization.65 * Added WordPress core alignment, caption, sticky, and bypostauthor styling.66 * Added custom-header, custom-background, and wp-block-styles theme support.67 * Replaced hardcoded search form with get_search_form().68 * Removed deprecated add_shortcode from theme files.69 * Added readme.txt with full GPL copyright documentation.70 187 71 188 = 2.5.1 = … … 73 190 * Added reading progress bar and copy code snippet buttons. 74 191 * Initial release of Nexversal. 192 -
nexversal/3.5.9/search.php
r357433 r357886 1 1 <?php 2 2 /** 3 * Security: Prevent direct script access 3 * Search results template for Nexversal Theme 4 * 5 * @package Nexversal 6 * @author Nexes Agency & Muhammad Shahid 7 * @copyright 2026 Nexes Agency & Muhammad Shahid 8 * @license GNU General Public License v2 or later 9 * @link https://nexversal.com 4 10 */ 11 5 12 if (!defined('ABSPATH')) { 6 13 exit; 7 14 } 8 9 /**10 * Search results template11 *12 * @package Nexversal13 */14 15 15 16 get_header(); … … 64 65 </div> 65 66 66 <?php get_sidebar(); ?> 67 <?php 68 if (!function_exists('nexversal_is_meta_disabled') || !nexversal_is_meta_disabled('disable_sidebar')) { 69 get_sidebar(); 70 } 71 ?> 67 72 </div> 68 73 </main> -
nexversal/3.5.9/sidebar.php
r357433 r357886 1 1 <?php 2 2 /** 3 * Security: Prevent direct script access 3 * Sidebar template for Nexversal Theme 4 * 5 * @package Nexversal 6 * @author Nexes Agency & Muhammad Shahid 7 * @copyright 2026 Nexes Agency & Muhammad Shahid 8 * @license GNU General Public License v2 or later 9 * @link https://nexversal.com 4 10 */ 11 5 12 if (!defined('ABSPATH')) { 6 13 exit; 7 14 } 8 9 /**10 * Sidebar template11 *12 * @package Nexversal13 */14 15 ?> 15 16 <aside id="secondary" class="site-sidebar" aria-label="<?php esc_attr_e('Sidebar', 'nexversal'); ?>"> -
nexversal/3.5.9/single.php
r357433 r357886 1 1 <?php 2 2 /** 3 * Security: Prevent direct script access 3 * Single post template for Nexversal Theme 4 * 5 * @package Nexversal 6 * @author Nexes Agency & Muhammad Shahid 7 * @copyright 2026 Nexes Agency & Muhammad Shahid 8 * @license GNU General Public License v2 or later 9 * @link https://nexversal.com 4 10 */ 11 5 12 if (!defined('ABSPATH')) { 6 13 exit; 7 14 } 8 9 /**10 * Single post template11 *12 * @package Nexversal13 */14 15 15 16 get_header(); … … 24 25 ?> 25 26 <article id="post-<?php the_ID(); ?>" <?php post_class('nx-single-article'); ?>> 26 <?php nexversal_breadcrumbs(); ?> 27 <?php 28 if (!function_exists('nexversal_is_meta_disabled') || !nexversal_is_meta_disabled('disable_breadcrumbs')) { 29 nexversal_breadcrumbs(); 30 } 31 ?> 27 32 28 33 <?php … … 39 44 <?php endif; ?> 40 45 41 <h1 class="nx-single-title"><?php the_title(); ?></h1> 46 <?php if (!function_exists('nexversal_is_meta_disabled') || !nexversal_is_meta_disabled('disable_title')) : ?> 47 <h1 class="nx-single-title"><?php the_title(); ?></h1> 48 <?php endif; ?> 42 49 43 50 <div class="nx-single-meta"> … … 199 206 </div> 200 207 201 <?php get_sidebar(); ?> 208 <?php 209 if (!function_exists('nexversal_is_meta_disabled') || !nexversal_is_meta_disabled('disable_sidebar')) { 210 get_sidebar(); 211 } 212 ?> 202 213 </div> 203 214 </main> -
nexversal/3.5.9/style.css
r357433 r357886 5 5 Author URI: https://nexversal.com/about 6 6 Description: A modern, ultra-fast, and elegant Tech & Magazine theme for WordPress engineered by Nexes Agency. Features a sleek hero section, responsive article cards, copy code buttons, reading progress bar, customizable accents, dark mode toggle, and rich SEO Schema markup. 7 Version: 2.5.27 Version: 3.5.9 8 8 Requires at least: 6.0 9 9 Tested up to: 7.0 … … 14 14 Tags: blog, news, entertainment, grid-layout, one-column, two-columns, custom-colors, custom-menu, featured-images, footer-widgets, full-width-template, theme-options, translation-ready 15 15 16 Nexversal WordPress Theme, (C) 2026 Nexes Agency & Muhammad Shahid 16 Nexversal WordPress Theme, (C) 2026 Nexes Agency & Muhammad Shahid. 17 17 Nexversal is distributed under the terms of the GNU General Public License v2 or later. 18 Copyright (C) 2026 Nexes Agency & Muhammad Shahid. All rights reserved. 18 19 */ 19 20 … … 163 164 } 164 165 166 /* Custom Header Logo Auto-Sizing */ 167 .custom-logo-link { 168 display: inline-flex; 169 align-items: center; 170 line-height: 1; 171 } 172 173 .custom-logo-link img, 174 .custom-logo { 175 max-height: 38px !important; 176 width: auto !important; 177 max-width: 200px !important; 178 object-fit: contain !important; 179 display: block; 180 transition: opacity 0.2s ease; 181 } 182 183 .custom-logo:hover { 184 opacity: 0.9; 185 } 186 165 187 .site-title { 166 188 font-size: 1.4rem; … … 537 559 /* Hero Section (Magazine / Tech Focus) */ 538 560 .nx-hero-section { 539 padding: clamp(24px, 5vw, 48px) 0 clamp(20px, 4vw, 36px);561 padding: clamp(24px, 4vw, 42px) 0 clamp(20px, 3vw, 32px); 540 562 background: linear-gradient(180deg, #ffffff 0%, var(--nx-bg) 100%); 541 563 border-bottom: 1px solid var(--nx-border); 542 margin-bottom: clamp(24px, 4vw, 40px);564 margin-bottom: clamp(24px, 3vw, 36px); 543 565 } 544 566 545 567 [data-theme="dark"] .nx-hero-section { 546 568 background: linear-gradient(180deg, #111827 0%, var(--nx-bg) 100%); 569 } 570 571 /* 2-Column Hero Grid: Left Content + Right Featured Article Card */ 572 .nx-hero-grid { 573 display: grid; 574 grid-template-columns: 1.15fr 0.85fr; 575 gap: clamp(24px, 4vw, 44px); 576 align-items: center; 577 max-width: var(--nx-max-width, 1200px); 578 margin: 0 auto; 579 padding: 0 clamp(16px, 3vw, 28px); 580 } 581 582 .nx-hero-content-col { 583 display: flex; 584 flex-direction: column; 585 align-items: flex-start; 586 min-width: 0; 547 587 } 548 588 … … 552 592 background: var(--nx-primary-light); 553 593 color: var(--nx-primary); 554 font-size: clamp(0.7 2rem, 2.5vw, 0.8rem);594 font-size: clamp(0.7rem, 2vw, 0.76rem); 555 595 font-weight: 700; 556 596 text-transform: uppercase; 557 597 letter-spacing: 0.05em; 558 598 border-radius: 20px; 559 margin-bottom: 1 4px;599 margin-bottom: 12px; 560 600 max-width: 100%; 561 601 word-break: break-word; … … 564 604 565 605 .nx-hero-title { 566 font-size: clamp(1. 55rem, 5vw + 0.5rem, 2.75rem);606 font-size: clamp(1.35rem, 2.3vw, 2.05rem); 567 607 font-weight: 800; 568 letter-spacing: -0.03em; 569 line-height: 1.2; 570 max-width: 850px; 571 margin-bottom: 14px; 608 letter-spacing: -0.025em; 609 line-height: 1.25; 610 max-width: 680px; 611 margin-top: 0; 612 margin-bottom: 12px; 572 613 color: var(--nx-dark); 573 614 word-break: break-word; … … 575 616 576 617 .nx-hero-description { 577 font-size: clamp(0.9 2rem, 2.5vw, 1.15rem);618 font-size: clamp(0.9rem, 1.8vw, 1.02rem); 578 619 color: var(--nx-text-light); 579 max-width: 6 80px;620 max-width: 600px; 580 621 line-height: 1.6; 581 margin-bottom: 20px; 582 } 583 584 .nx-hero-btn { 622 margin-bottom: 16px; 623 } 624 625 .nx-hero-actions { 626 margin: 14px 0 20px; 627 padding-left: 1px; 628 } 629 630 .nx-hero-btn, 631 .nx-hero-actions .wp-block-button__link { 632 display: inline-flex !important; 633 align-items: center !important; 634 gap: 7px !important; 635 padding: 9px 20px !important; 636 background-color: var(--nx-btn-bg, var(--nx-primary)) !important; 637 color: var(--nx-btn-text, #ffffff) !important; 638 border: 1px solid var(--nx-btn-bg, var(--nx-primary)) !important; 639 font-weight: 600 !important; 640 font-size: 0.88rem !important; 641 border-radius: 20px !important; 642 text-decoration: none !important; 643 box-shadow: 0 3px 10px rgba(79, 70, 229, 0.28) !important; 644 transition: all 0.2s ease !important; 645 cursor: pointer; 646 line-height: 1.3 !important; 647 } 648 649 .nx-hero-btn:hover, 650 .nx-hero-actions .wp-block-button__link:hover { 651 background-color: var(--nx-btn-hover, var(--nx-primary-hover)) !important; 652 border-color: var(--nx-btn-hover, var(--nx-primary-hover)) !important; 653 color: var(--nx-btn-text, #ffffff) !important; 654 transform: translateY(-2px) !important; 655 box-shadow: 0 5px 16px rgba(79, 70, 229, 0.42) !important; 656 } 657 658 /* Hero Featured Article Card */ 659 .nx-hero-featured-col { 660 display: flex; 661 justify-content: center; 662 width: 100%; 663 min-width: 0; 664 } 665 666 .nx-hero-featured-card { 667 width: 100%; 668 max-width: 440px; 669 background: var(--nx-card-bg); 670 border: 1px solid var(--nx-border); 671 border-radius: var(--nx-radius, 14px); 672 box-shadow: 0 6px 24px rgba(0, 0, 0, 0.07); 673 overflow: hidden; 674 transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease; 675 position: relative; 676 display: flex; 677 flex-direction: column; 678 } 679 680 .nx-hero-featured-card:hover { 681 transform: translateY(-4px); 682 box-shadow: 0 14px 34px rgba(0, 0, 0, 0.12); 683 border-color: var(--nx-primary-light, #c7d2fe); 684 } 685 686 [data-theme="dark"] .nx-hero-featured-card { 687 box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4); 688 } 689 690 [data-theme="dark"] .nx-hero-featured-card:hover { 691 box-shadow: 0 14px 34px rgba(0, 0, 0, 0.6); 692 } 693 694 .nx-hero-card-badge-wrap { 695 position: absolute; 696 top: 12px; 697 left: 12px; 698 z-index: 2; 699 } 700 701 .nx-hero-card-featured-tag { 585 702 display: inline-flex; 586 703 align-items: center; 587 gap: 6px; 588 padding: 10px 22px; 589 background: var(--nx-primary); 590 color: #ffffff !important; 704 gap: 5px; 705 padding: 4px 10px; 706 background: rgba(15, 23, 42, 0.88); 707 color: #fbbf24; 708 font-size: 0.72rem; 591 709 font-weight: 700; 592 font-size: 0.92rem; 593 border-radius: var(--nx-radius-sm); 710 text-transform: uppercase; 711 letter-spacing: 0.04em; 712 border-radius: 16px; 713 backdrop-filter: blur(6px); 714 box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2); 715 } 716 717 .nx-hero-card-thumb-wrap { 718 position: relative; 719 display: block; 720 width: 100%; 721 height: 200px; 722 overflow: hidden; 723 background: var(--nx-card-bg-subtle, #f1f5f9); 724 } 725 726 .nx-hero-card-img { 727 width: 100%; 728 height: 100%; 729 object-fit: cover; 730 display: block; 731 transition: transform 0.4s ease; 732 } 733 734 .nx-hero-featured-card:hover .nx-hero-card-img { 735 transform: scale(1.05); 736 } 737 738 .nx-hero-card-img-fallback { 739 width: 100%; 740 height: 100%; 741 display: flex; 742 align-items: center; 743 justify-content: center; 744 background: linear-gradient(135deg, rgba(79, 70, 229, 0.12) 0%, rgba(6, 182, 212, 0.12) 100%); 745 color: var(--nx-primary); 746 } 747 748 .nx-hero-card-body { 749 padding: 18px 20px 20px; 750 display: flex; 751 flex-direction: column; 752 flex-grow: 1; 753 } 754 755 .nx-hero-card-meta-top { 756 display: flex; 757 align-items: center; 758 justify-content: space-between; 759 margin-bottom: 8px; 760 font-size: 0.76rem; 761 } 762 763 .nx-hero-card-cat { 764 font-weight: 700; 765 color: var(--nx-primary); 766 text-transform: uppercase; 767 letter-spacing: 0.04em; 594 768 text-decoration: none; 595 box-shadow: 0 4px 14px rgba(79, 70, 229, 0.3); 596 transition: all 0.25s ease; 597 } 598 599 .nx-hero-btn:hover { 600 background: var(--nx-primary-hover); 601 transform: translateY(-2px); 602 box-shadow: 0 6px 20px rgba(79, 70, 229, 0.45); 769 } 770 771 .nx-hero-card-cat:hover { 772 text-decoration: underline; 773 } 774 775 .nx-hero-card-time { 776 color: var(--nx-text-light); 777 font-weight: 500; 778 } 779 780 .nx-hero-card-title { 781 font-size: 1.12rem; 782 font-weight: 800; 783 line-height: 1.35; 784 margin: 0 0 8px 0; 785 color: var(--nx-dark); 786 } 787 788 .nx-hero-card-title a { 789 color: inherit; 790 text-decoration: none; 791 transition: color 0.2s ease; 792 } 793 794 .nx-hero-card-title a:hover { 795 color: var(--nx-primary); 796 } 797 798 .nx-hero-card-excerpt { 799 font-size: 0.86rem; 800 line-height: 1.5; 801 color: var(--nx-text-light); 802 margin: 0 0 14px 0; 803 display: -webkit-box; 804 -webkit-line-clamp: 2; 805 line-clamp: 2; 806 -webkit-box-orient: vertical; 807 overflow: hidden; 808 } 809 810 .nx-hero-card-footer { 811 display: flex; 812 align-items: center; 813 justify-content: space-between; 814 padding-top: 12px; 815 border-top: 1px solid var(--nx-border); 816 margin-top: auto; 817 font-size: 0.8rem; 818 } 819 820 .nx-hero-card-author { 821 display: flex; 822 align-items: center; 823 gap: 7px; 824 color: var(--nx-dark); 825 font-weight: 600; 826 } 827 828 .nx-hero-card-avatar { 829 width: 22px; 830 height: 22px; 831 border-radius: 50%; 832 } 833 834 .nx-hero-card-read-link { 835 display: inline-flex; 836 align-items: center; 837 gap: 4px; 838 color: var(--nx-primary); 839 font-weight: 700; 840 text-decoration: none; 841 font-size: 0.82rem; 842 transition: transform 0.2s ease; 843 } 844 845 .nx-hero-card-read-link:hover { 846 transform: translateX(3px); 847 } 848 849 @media (max-width: 900px) { 850 .nx-hero-grid { 851 grid-template-columns: 1fr; 852 gap: 28px; 853 } 854 855 .nx-hero-featured-card { 856 max-width: 100%; 857 } 858 859 .nx-hero-card-thumb-wrap { 860 height: 180px; 861 } 603 862 } 604 863 … … 648 907 649 908 /* Layout Grid (Main Content + Sidebar) */ 650 .nx-layout-grid { 909 .nx-layout-grid, 910 .site-container { 651 911 display: grid; 652 912 grid-template-columns: 1fr; 653 913 gap: 36px; 654 914 margin-bottom: 60px; 915 align-items: start; 916 width: 100%; 655 917 } 656 918 657 919 @media (min-width: 992px) { 658 .nx-layout-grid { 920 .nx-layout-grid, 921 .site-container { 659 922 grid-template-columns: 2.2fr 1fr; 660 923 } 661 924 } 662 925 663 /* Post Cards Grid */ 664 .nx-posts-grid { 926 .nx-content-area, 927 .content-area { 928 min-width: 0; 929 width: 100%; 930 } 931 932 /* ========================================================================== 933 Article Grid System (3 Columns with Sidebar, 4 Columns without Sidebar) 934 ========================================================================== */ 935 936 /* 1. Default (With Sidebar Active): 1-col on mobile, 2-col on tablet, 3-col on desktop */ 937 .nx-posts-grid, 938 .posts-grid { 665 939 display: grid; 666 940 grid-template-columns: 1fr; 667 941 gap: 20px; 668 } 669 670 @media (min-width: 640px) { 671 .nx-posts-grid { 942 width: 100%; 943 } 944 945 @media (min-width: 600px) { 946 .nx-posts-grid, 947 .posts-grid { 672 948 grid-template-columns: repeat(2, 1fr); 673 } 949 gap: 20px; 950 } 951 } 952 953 /* On Desktop when Sidebar is present: 3 articles in a row */ 954 @media (min-width: 992px) { 955 .nx-posts-grid, 956 .posts-grid { 957 grid-template-columns: repeat(3, 1fr); 958 gap: 20px; 959 } 960 } 961 962 /* 2. Without Sidebar (Disabled via Page Settings or no active sidebar): 4 articles in a row on Desktop! */ 963 @media (min-width: 1080px) { 964 .nx-no-sidebar .nx-posts-grid, 965 .nx-no-sidebar .posts-grid, 966 .nx-layout-grid:not(:has(.site-sidebar)) .nx-posts-grid, 967 .nx-layout-grid:not(:has(.site-sidebar)) .posts-grid, 968 .nx-layout-contained .nx-posts-grid, 969 .nx-layout-contained .posts-grid, 970 .nx-layout-stretched .nx-posts-grid, 971 .nx-layout-stretched .posts-grid { 972 grid-template-columns: repeat(4, 1fr) !important; 973 gap: 22px !important; 974 } 975 } 976 977 @media (min-width: 768px) and (max-width: 1079px) { 978 .nx-no-sidebar .nx-posts-grid, 979 .nx-no-sidebar .posts-grid, 980 .nx-layout-grid:not(:has(.site-sidebar)) .nx-posts-grid, 981 .nx-layout-grid:not(:has(.site-sidebar)) .posts-grid, 982 .nx-layout-contained .nx-posts-grid, 983 .nx-layout-contained .posts-grid, 984 .nx-layout-stretched .nx-posts-grid, 985 .nx-layout-stretched .posts-grid { 986 grid-template-columns: repeat(3, 1fr) !important; 987 gap: 20px !important; 988 } 989 } 990 991 @media (min-width: 520px) and (max-width: 767px) { 992 .nx-no-sidebar .nx-posts-grid, 993 .nx-no-sidebar .posts-grid, 994 .nx-layout-grid:not(:has(.site-sidebar)) .nx-posts-grid, 995 .nx-layout-grid:not(:has(.site-sidebar)) .posts-grid, 996 .nx-layout-contained .nx-posts-grid, 997 .nx-layout-contained .posts-grid, 998 .nx-layout-stretched .nx-posts-grid, 999 .nx-layout-stretched .posts-grid { 1000 grid-template-columns: repeat(2, 1fr) !important; 1001 gap: 18px !important; 1002 } 1003 } 1004 1005 /* 4-Column Post Cards Grid (for Blog page / full-width archives) */ 1006 .nx-posts-grid-4col { 1007 display: grid; 1008 grid-template-columns: 1fr; 1009 gap: 20px; 1010 width: 100%; 1011 } 1012 1013 @media (min-width: 520px) { 1014 .nx-posts-grid-4col { 1015 grid-template-columns: repeat(2, 1fr); 1016 } 1017 } 1018 1019 @media (min-width: 768px) { 1020 .nx-posts-grid-4col { 1021 grid-template-columns: repeat(3, 1fr); 1022 } 1023 } 1024 1025 @media (min-width: 1080px) { 1026 .nx-posts-grid-4col { 1027 grid-template-columns: repeat(4, 1fr); 1028 } 1029 } 1030 1031 /* Contact Information Details */ 1032 .nx-contact-details { 1033 display: flex; 1034 flex-direction: column; 1035 gap: 20px; 1036 margin-top: 18px; 1037 } 1038 1039 .nx-contact-item { 1040 display: flex; 1041 flex-direction: column; 1042 gap: 3px; 1043 } 1044 1045 .nx-contact-label { 1046 font-size: 0.8rem !important; 1047 font-weight: 700 !important; 1048 color: var(--nx-text-light) !important; 1049 text-transform: uppercase !important; 1050 letter-spacing: 0.05em !important; 1051 margin-bottom: 2px !important; 1052 } 1053 1054 .nx-contact-val { 1055 font-weight: 600; 1056 font-size: 0.95rem; 1057 color: var(--nx-dark); 1058 margin-top: 0 !important; 1059 margin-bottom: 0 !important; 1060 } 1061 1062 .nx-contact-val a { 1063 color: var(--nx-primary); 1064 text-decoration: none; 1065 } 1066 1067 .nx-contact-val a:hover { 1068 text-decoration: underline; 674 1069 } 675 1070 … … 694 1089 overflow: hidden; 695 1090 aspect-ratio: 16 / 9; 696 max-height: 1 55px;1091 max-height: 120px; 697 1092 background: #e2e8f0; 698 1093 } … … 708 1103 width: 100%; 709 1104 height: 100%; 710 min-height: 1 35px;1105 min-height: 105px; 711 1106 background: linear-gradient(135deg, #4f46e5 0%, #06b6d4 100%); 712 1107 display: flex; … … 714 1109 justify-content: center; 715 1110 color: #ffffff; 716 font-size: 1. 5rem;1111 font-size: 1.4rem; 717 1112 font-weight: 800; 718 1113 letter-spacing: -0.05em; … … 725 1120 726 1121 .nx-card-body { 727 padding: 1 6px 18px;1122 padding: 13px 15px; 728 1123 display: flex; 729 1124 flex-direction: column; … … 733 1128 .nx-category-badge { 734 1129 display: inline-block; 735 padding: 2px 8px;1130 padding: 2px 7px; 736 1131 background: var(--nx-primary-light); 737 1132 color: var(--nx-primary); 738 font-size: 0. 7rem;1133 font-size: 0.68rem; 739 1134 font-weight: 700; 740 1135 text-transform: uppercase; 741 1136 letter-spacing: 0.05em; 742 1137 border-radius: 4px; 743 margin-bottom: 8px;1138 margin-bottom: 6px; 744 1139 align-self: flex-start; 745 1140 } 746 1141 747 1142 .nx-card-title { 748 font-size: 1.05rem;1143 font-size: 0.96rem; 749 1144 font-weight: 700; 750 line-height: 1.3 5;751 margin-bottom: 8px;1145 line-height: 1.32; 1146 margin-bottom: 6px; 752 1147 } 753 1148 … … 761 1156 762 1157 .nx-card-excerpt { 763 font-size: 0.8 5rem;1158 font-size: 0.8rem; 764 1159 color: var(--nx-text-light); 765 line-height: 1. 5;766 margin-bottom: 12px;1160 line-height: 1.45; 1161 margin-bottom: 8px; 767 1162 flex-grow: 1; 768 1163 display: -webkit-box; 769 1164 -webkit-line-clamp: 2; 1165 line-clamp: 2; 770 1166 -webkit-box-orient: vertical; 771 1167 overflow: hidden; … … 777 1173 justify-content: space-between; 778 1174 border-top: 1px solid var(--nx-border); 779 padding-top: 10px; 780 font-size: 0.75rem; 1175 padding-top: 8px; 1176 margin-top: auto; 1177 font-size: 0.72rem; 781 1178 color: var(--nx-text-light); 782 1179 } … … 801 1198 802 1199 .nx-card-action { 803 margin-top: 10px; 804 padding-top: 8px; 805 border-top: 1px dashed var(--nx-border); 806 display: flex; 807 align-items: center; 808 justify-content: flex-end; 1200 display: none !important; 809 1201 } 810 1202 … … 1288 1680 } 1289 1681 1290 .nx-btn { 1291 display: inline-block; 1682 /* ========================================================================== 1683 Universal Buttons Styling (Site-wide, Hero, Cards, Blocks, Forms) 1684 ========================================================================== */ 1685 .nx-btn, 1686 .nx-hero-btn, 1687 .nx-btn-primary, 1688 .nx-card-cta, 1689 .nx-newsletter-btn, 1690 .nx-contact-submit, 1691 .wp-block-button__link, 1692 .wp-element-button, 1693 button[type="submit"], 1694 input[type="submit"], 1695 .comment-form .submit, 1696 #commentform #submit, 1697 .search-submit { 1698 display: inline-flex; 1699 align-items: center; 1700 justify-content: center; 1292 1701 padding: 12px 28px; 1293 background: var(--nx-primary); 1294 color: #ffffff; 1702 background-color: var(--nx-btn-bg, var(--nx-primary)) !important; 1703 color: var(--nx-btn-text, #ffffff) !important; 1704 border: 1px solid var(--nx-btn-bg, var(--nx-primary)) !important; 1295 1705 border-radius: 30px; 1296 1706 font-weight: 600; 1297 1707 font-size: 0.95rem; 1298 transition: background 0.2s; 1299 } 1300 1301 .nx-btn:hover { 1302 background: var(--nx-primary-hover); 1303 color: #ffffff; 1708 text-decoration: none; 1709 cursor: pointer; 1710 transition: all 0.25s ease; 1711 } 1712 1713 .nx-btn:hover, 1714 .nx-hero-btn:hover, 1715 .nx-btn-primary:hover, 1716 .nx-card-cta:hover, 1717 .nx-newsletter-btn:hover, 1718 .nx-contact-submit:hover, 1719 .wp-block-button__link:hover, 1720 .wp-element-button:hover, 1721 button[type="submit"]:hover, 1722 input[type="submit"]:hover, 1723 .comment-form .submit:hover, 1724 #commentform #submit:hover, 1725 .search-submit:hover { 1726 background-color: var(--nx-btn-hover, var(--nx-primary-hover)) !important; 1727 border-color: var(--nx-btn-hover, var(--nx-primary-hover)) !important; 1728 color: var(--nx-btn-text, #ffffff) !important; 1729 transform: translateY(-2px); 1304 1730 } 1305 1731 … … 1442 1868 .footer-bottom { 1443 1869 padding-top: 28px; 1444 display: flex ;1445 flex-direction: column ;1446 justify-content: center ;1447 align-items: center ;1448 text-align: center ;1870 display: flex !important; 1871 flex-direction: column !important; 1872 justify-content: center !important; 1873 align-items: center !important; 1874 text-align: center !important; 1449 1875 font-size: 0.88rem; 1450 1876 gap: 12px; 1451 1877 color: #64748b !important; 1878 width: 100%; 1452 1879 } 1453 1880 … … 1456 1883 .footer-bottom-content { 1457 1884 color: #64748b !important; 1458 margin: 0; 1459 text-align: center; 1885 margin: 0 auto !important; 1886 text-align: center !important; 1887 width: 100%; 1888 display: flex; 1889 justify-content: center; 1890 align-items: center; 1460 1891 } 1461 1892 1462 1893 .nx-footer-credits { 1463 display: flex; 1464 align-items: center; 1465 justify-content: center; 1466 flex-wrap: wrap; 1467 gap: 8px; 1468 line-height: 1.6; 1894 display: flex !important; 1895 align-items: center !important; 1896 justify-content: center !important; 1897 flex-wrap: wrap !important; 1898 gap: 8px !important; 1899 line-height: 1.6 !important; 1900 text-align: center !important; 1901 margin: 0 auto !important; 1469 1902 } 1470 1903 … … 2161 2594 } 2162 2595 2596 .custom-logo-link img, 2597 .custom-logo { 2598 max-height: 30px !important; 2599 max-width: 150px !important; 2600 } 2601 2163 2602 .header-actions { 2164 2603 gap: 6px; … … 2473 2912 } 2474 2913 2475 .nx-ad-header { 2476 margin: 12px auto; 2914 /* Bounding Box Base for Zero Cumulative Layout Shift (CLS = 0) */ 2915 .nx-ad-slot-cls { 2916 contain: layout; 2917 } 2918 2919 .nx-ad-bounding-box { 2920 position: relative; 2921 display: flex; 2922 align-items: center; 2923 justify-content: center; 2924 margin: 0 auto; 2925 overflow: hidden; 2926 border-radius: 8px; 2927 background-color: transparent; 2928 border: none; 2929 box-sizing: border-box; 2930 } 2931 2932 [data-theme="dark"] .nx-ad-bounding-box, 2933 body.dark-mode .nx-ad-bounding-box { 2934 background-color: transparent; 2935 border-color: transparent; 2936 } 2937 2938 /* Ad Loaded State */ 2939 .nx-ad-bounding-box.nx-ad-loaded { 2940 background-color: transparent !important; 2941 border-color: transparent !important; 2942 } 2943 2944 /* Slot 1: Header Banner (728x90 Leaderboard / 320x100 Mobile) */ 2945 .nx-ad-box-header { 2946 width: 728px; 2947 max-width: 100%; 2948 min-height: 90px; 2949 aspect-ratio: 728 / 90; 2950 } 2951 @media (max-width: 767px) { 2952 .nx-ad-box-header { 2953 width: 100%; 2954 max-width: 320px; 2955 min-height: 100px; 2956 aspect-ratio: 320 / 100; 2957 } 2958 } 2959 2960 /* Slot 2: In-Article Mid-Content (336x280 / 300x250) */ 2961 .nx-ad-box-article { 2962 width: 336px; 2963 max-width: 100%; 2964 min-height: 280px; 2965 aspect-ratio: 336 / 280; 2966 margin: 28px auto; 2967 } 2968 @media (max-width: 767px) { 2969 .nx-ad-box-article { 2970 width: 300px; 2971 max-width: 100%; 2972 min-height: 250px; 2973 aspect-ratio: 300 / 250; 2974 } 2975 } 2976 2977 /* Slot 3: Below Post Content (336x280 / 300x250) */ 2978 .nx-ad-box-post_bottom { 2979 width: 336px; 2980 max-width: 100%; 2981 min-height: 280px; 2982 aspect-ratio: 336 / 280; 2983 margin: 32px auto 24px auto; 2984 } 2985 @media (max-width: 767px) { 2986 .nx-ad-box-post_bottom { 2987 width: 300px; 2988 max-width: 100%; 2989 min-height: 250px; 2990 aspect-ratio: 300 / 250; 2991 } 2992 } 2993 2994 /* Slot 4: In-Feed Grid Card (360x360 1:1 Aspect Ratio) */ 2995 .nx-ad-box-infeed { 2996 width: 100%; 2997 min-height: 340px; 2998 aspect-ratio: 1 / 1; 2999 } 3000 .nx-infeed-ad-card { 2477 3001 padding: 16px; 2478 background: var(--nx-card-bg); 2479 border: 1px dashed var(--nx-border); 2480 border-radius: var(--nx-radius-sm); 2481 } 2482 2483 .nx-ad-article { 2484 margin: 32px auto; 2485 padding: 20px; 2486 background: var(--nx-bg); 2487 border: 1px dashed var(--nx-border); 2488 border-radius: var(--nx-radius-sm); 2489 } 2490 2491 .nx-ad-post_bottom { 2492 margin: 32px 0 24px 0; 2493 padding: 20px; 2494 background: var(--nx-bg); 2495 border: 1px dashed var(--nx-border); 2496 border-radius: var(--nx-radius-sm); 2497 } 2498 2499 .nx-infeed-ad-card { 2500 display: flex; 2501 flex-direction: column; 2502 align-items: center; 2503 justify-content: center; 2504 min-height: 280px; 2505 padding: 24px; 2506 background: var(--nx-card-bg); 2507 border: 1px dashed var(--nx-border); 2508 border-radius: var(--nx-radius); 2509 text-align: center; 2510 box-shadow: var(--nx-shadow-sm); 2511 } 2512 2513 .nx-infeed-ad-card .nx-ad-slot { 2514 margin: 0; 2515 width: 100%; 2516 } 2517 3002 border: 1px solid var(--nx-border, #e2e8f0); 3003 background: var(--nx-card-bg, #ffffff); 3004 border-radius: var(--nx-radius, 12px); 3005 box-shadow: var(--nx-shadow-sm, 0 1px 3px rgba(0,0,0,0.06)); 3006 } 3007 @media (max-width: 767px) { 3008 .nx-ad-box-infeed { 3009 min-height: 280px; 3010 } 3011 } 3012 3013 /* Slot 5: Sticky Half-Page Sidebar (300x600 / 300x250) */ 3014 .nx-ad-box-sidebar { 3015 width: 300px; 3016 max-width: 100%; 3017 min-height: 600px; 3018 aspect-ratio: 300 / 600; 3019 } 2518 3020 .nx-sidebar-sticky-ad { 2519 3021 position: sticky; 2520 3022 top: 90px; 2521 3023 z-index: 5; 2522 margin-top: 24px; 2523 background: var(--nx-card-bg); 3024 } 3025 @media (max-width: 767px) { 3026 .nx-ad-box-sidebar { 3027 width: 300px; 3028 max-width: 100%; 3029 min-height: 250px; 3030 aspect-ratio: 300 / 250; 3031 } 3032 } 3033 3034 @keyframes nxAdShimmer { 3035 0% { background-position: 200% 0; } 3036 100% { background-position: -200% 0; } 3037 } 3038 3039 .nx-ad-blocker-bypass { 3040 width: 100%; 3041 text-align: center; 3042 padding: 18px 20px; 3043 background: var(--nx-bg); 2524 3044 border: 1px dashed var(--nx-border); 2525 3045 border-radius: var(--nx-radius-sm); 2526 padding: 16px !important; 2527 text-align: center; 2528 } 2529 2530 .nx-sidebar-sticky-ad .nx-ad-slot { 2531 margin: 0; 2532 } 2533 2534 [data-theme="dark"] .nx-ad-header, 2535 [data-theme="dark"] .nx-ad-article, 2536 [data-theme="dark"] .nx-ad-post_bottom, 2537 [data-theme="dark"] .nx-infeed-ad-card, 2538 [data-theme="dark"] .nx-sidebar-sticky-ad { 3046 animation: nxFadeSlideIn 0.3s ease; 3047 } 3048 3049 [data-theme="dark"] .nx-ad-blocker-bypass { 2539 3050 background: #111827; 2540 3051 border-color: #1f2937; 3052 } 3053 3054 .nx-ad-blocker-bypass img { 3055 max-width: 100%; 3056 height: auto; 3057 border-radius: var(--nx-radius-sm); 2541 3058 } 2542 3059 … … 2642 3159 } 2643 3160 2644 2645 2646 2647 3161 /* ========================================================================== 3162 Page Flexibility: Astra-Style Layouts & Element Toggles 3163 ========================================================================== */ 3164 3165 /* Disable Sidebar (Force 100% Content Width) */ 3166 .nx-no-sidebar .nx-layout-grid, 3167 .nx-layout-grid:not(:has(.site-sidebar)) { 3168 grid-template-columns: 1fr !important; 3169 } 3170 3171 .nx-no-sidebar .nx-content-area, 3172 .nx-layout-grid:not(:has(.site-sidebar)) .nx-content-area { 3173 max-width: 100% !important; 3174 width: 100% !important; 3175 } 3176 3177 /* Full Width (Contained): Outer wrapper expands, content remains safely contained & padded */ 3178 .nx-layout-contained .site-main.container, 3179 .nx-layout-contained .container { 3180 max-width: 100% !important; 3181 width: 100% !important; 3182 padding-left: clamp(16px, 3.5vw, 36px) !important; 3183 padding-right: clamp(16px, 3.5vw, 36px) !important; 3184 } 3185 3186 .nx-layout-contained .nx-single-article, 3187 .nx-layout-contained .nx-page-card { 3188 max-width: min(100%, var(--nx-max-width, 1200px)) !important; 3189 margin-left: auto !important; 3190 margin-right: auto !important; 3191 padding: clamp(24px, 4vw, 44px) !important; 3192 } 3193 3194 /* Full Width Stretched (Canvas / Elementor & Landing Pages) */ 3195 /* Container expands to 100% viewport width with safe horizontal breathing gutters */ 3196 .nx-layout-stretched .site-main.container, 3197 .nx-layout-stretched .container { 3198 max-width: 100% !important; 3199 width: 100% !important; 3200 padding-left: clamp(16px, 3.5vw, 40px) !important; 3201 padding-right: clamp(16px, 3.5vw, 40px) !important; 3202 margin-left: auto !important; 3203 margin-right: auto !important; 3204 } 3205 3206 .nx-layout-stretched .site-main { 3207 margin-top: 24px !important; 3208 margin-bottom: 40px !important; 3209 } 3210 3211 .nx-layout-stretched .nx-layout-grid { 3212 grid-template-columns: 1fr !important; 3213 gap: 0 !important; 3214 width: 100% !important; 3215 } 3216 3217 .nx-layout-stretched .nx-content-area { 3218 max-width: 100% !important; 3219 width: 100% !important; 3220 } 3221 3222 /* Standard Posts & Pages in Stretched Mode: Keep comfortable reading container so text never smashes screen edges */ 3223 .nx-layout-stretched .nx-single-article, 3224 .nx-layout-stretched .nx-page-card { 3225 background: var(--nx-card-bg); 3226 border: 1px solid var(--nx-border); 3227 border-radius: var(--nx-radius); 3228 padding: clamp(24px, 4vw, 48px) !important; 3229 max-width: min(100%, var(--nx-max-width, 1200px)); 3230 margin-left: auto !important; 3231 margin-right: auto !important; 3232 box-shadow: var(--nx-shadow-sm); 3233 } 3234 3235 /* Pure Page Builders (Elementor, Beaver Builder, or Gutenberg Full-Bleed Blocks) */ 3236 /* When an Elementor section or full-bleed block exists, remove container constraints completely */ 3237 .nx-layout-stretched .elementor, 3238 .nx-layout-stretched .entry-content:has(.elementor), 3239 .nx-layout-stretched .nx-single-article:has(.elementor), 3240 .nx-layout-stretched .nx-page-card:has(.elementor) { 3241 background: transparent !important; 3242 border: none !important; 3243 border-radius: 0 !important; 3244 padding: 0 !important; 3245 max-width: 100% !important; 3246 box-shadow: none !important; 3247 } 3248 3249 .nx-layout-stretched:has(.elementor) .site-main.container, 3250 .nx-layout-stretched:has(.elementor) .container { 3251 padding-left: 0 !important; 3252 padding-right: 0 !important; 3253 } 3254 3255 /* Gutenberg alignfull breakout support */ 3256 .nx-layout-stretched .alignfull, 3257 .nx-layout-contained .alignfull { 3258 width: 100vw !important; 3259 max-width: 100vw !important; 3260 margin-left: calc(50% - 50vw) !important; 3261 margin-right: calc(50% - 50vw) !important; 3262 } 3263 3264 /* ========================================================================== 3265 Popular Categories Grid (Image 4 Design) 3266 ========================================================================== */ 3267 .nx-categories-browse-all { 3268 font-size: 0.92rem; 3269 font-weight: 700; 3270 color: var(--nx-text-light); 3271 text-decoration: none; 3272 display: inline-flex; 3273 align-items: center; 3274 gap: 6px; 3275 transition: color 0.2s ease, transform 0.2s ease; 3276 } 3277 3278 .nx-categories-browse-all:hover { 3279 color: var(--nx-primary); 3280 transform: translateX(3px); 3281 } 3282 3283 .nx-popular-categories-grid { 3284 display: grid; 3285 grid-template-columns: repeat(4, 1fr); 3286 gap: 18px; 3287 width: 100%; 3288 } 3289 3290 @media (max-width: 1080px) { 3291 .nx-popular-categories-grid { 3292 grid-template-columns: repeat(3, 1fr); 3293 } 3294 } 3295 3296 @media (max-width: 768px) { 3297 .nx-popular-categories-grid { 3298 grid-template-columns: repeat(2, 1fr); 3299 gap: 14px; 3300 } 3301 } 3302 3303 @media (max-width: 480px) { 3304 .nx-popular-categories-grid { 3305 grid-template-columns: 1fr; 3306 } 3307 } 3308 3309 .nx-pop-cat-card { 3310 display: flex; 3311 flex-direction: column; 3312 background: var(--nx-card-bg); 3313 border: 1px solid var(--nx-border); 3314 border-radius: var(--nx-radius); 3315 padding: 22px 20px; 3316 text-decoration: none; 3317 transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease; 3318 } 3319 3320 [data-theme="dark"] .nx-pop-cat-card { 3321 background: #111827; 3322 border-color: rgba(255, 255, 255, 0.08); 3323 } 3324 3325 .nx-pop-cat-card:hover { 3326 transform: translateY(-3px); 3327 border-color: var(--nx-primary); 3328 box-shadow: var(--nx-shadow-md); 3329 } 3330 3331 .nx-pop-cat-icon { 3332 width: 36px; 3333 height: 36px; 3334 border-radius: 8px; 3335 display: flex; 3336 align-items: center; 3337 justify-content: center; 3338 color: #ffffff; 3339 font-weight: 800; 3340 font-size: 1.15rem; 3341 line-height: 1; 3342 margin-bottom: 14px; 3343 } 3344 3345 .nx-pop-cat-name { 3346 font-size: 1.02rem; 3347 font-weight: 800; 3348 color: var(--nx-dark); 3349 margin: 0 0 8px 0; 3350 line-height: 1.3; 3351 } 3352 3353 [data-theme="dark"] .nx-pop-cat-name { 3354 color: #ffffff; 3355 } 3356 3357 .nx-pop-cat-desc { 3358 font-size: 0.82rem; 3359 color: var(--nx-text-light); 3360 line-height: 1.5; 3361 margin: 0 0 14px 0; 3362 flex-grow: 1; 3363 display: -webkit-box; 3364 -webkit-line-clamp: 2; 3365 line-clamp: 2; 3366 -webkit-box-orient: vertical; 3367 overflow: hidden; 3368 } 3369 3370 .nx-pop-cat-count { 3371 font-size: 0.78rem; 3372 font-weight: 600; 3373 color: var(--nx-text-light); 3374 margin-top: auto; 3375 } 3376 3377 /* ========================================================================== 3378 Homepage Author Box (Image 3 Design) 3379 ========================================================================== */ 3380 .nx-home-author-card { 3381 display: flex; 3382 align-items: center; 3383 gap: 22px; 3384 background: var(--nx-card-bg); 3385 border: 1px solid var(--nx-border); 3386 border-radius: var(--nx-radius); 3387 padding: 24px 28px; 3388 box-shadow: var(--nx-shadow-sm); 3389 } 3390 3391 [data-theme="dark"] .nx-home-author-card { 3392 background: #111827; 3393 border-color: rgba(255, 255, 255, 0.08); 3394 } 3395 3396 .nx-author-avatar-wrap { 3397 flex-shrink: 0; 3398 } 3399 3400 .nx-author-avatar-link { 3401 display: block; 3402 text-decoration: none; 3403 cursor: pointer; 3404 transition: transform 0.2s cubic-bezier(0.4, 0, 0.2, 1); 3405 } 3406 3407 .nx-author-avatar-link:hover { 3408 transform: scale(1.06); 3409 } 3410 3411 .nx-author-avatar-circle { 3412 width: 62px; 3413 height: 62px; 3414 border-radius: 50%; 3415 background: linear-gradient(135deg, #6366f1 0%, #a855f7 100%); 3416 display: flex; 3417 align-items: center; 3418 justify-content: center; 3419 color: #ffffff; 3420 font-size: 1.7rem; 3421 font-weight: 800; 3422 box-shadow: 0 4px 14px rgba(99, 102, 241, 0.35); 3423 overflow: hidden; 3424 position: relative; 3425 } 3426 3427 .nx-author-avatar-img { 3428 width: 100%; 3429 height: 100%; 3430 object-fit: cover; 3431 border-radius: 50%; 3432 display: block; 3433 } 3434 3435 .nx-author-avatar-initial { 3436 line-height: 1; 3437 display: flex; 3438 align-items: center; 3439 justify-content: center; 3440 user-select: none; 3441 } 3442 3443 .nx-author-name { 3444 font-size: 1.15rem; 3445 font-weight: 800; 3446 color: var(--nx-dark); 3447 margin: 0 0 2px 0; 3448 } 3449 3450 .nx-author-name-link { 3451 color: inherit; 3452 text-decoration: none; 3453 cursor: pointer; 3454 display: inline-block; 3455 transition: color 0.2s ease; 3456 } 3457 3458 .nx-author-name-link:hover { 3459 color: var(--nx-primary, #4f46e5); 3460 text-decoration: none; 3461 } 3462 3463 [data-theme="dark"] .nx-author-name { 3464 color: #ffffff; 3465 } 3466 3467 [data-theme="dark"] .nx-author-name-link:hover { 3468 color: #818cf8; 3469 } 3470 3471 .nx-author-role { 3472 font-size: 0.85rem; 3473 font-weight: 700; 3474 color: #8b5cf6; 3475 margin-bottom: 6px; 3476 } 3477 3478 .nx-author-role-link { 3479 color: #8b5cf6; 3480 text-decoration: none; 3481 cursor: pointer; 3482 display: inline-block; 3483 transition: color 0.2s ease, opacity 0.2s ease; 3484 } 3485 3486 .nx-author-role-link:hover { 3487 color: #7c3aed; 3488 text-decoration: underline; 3489 } 3490 3491 [data-theme="dark"] .nx-author-role-link { 3492 color: #a78bfa; 3493 } 3494 3495 [data-theme="dark"] .nx-author-role-link:hover { 3496 color: #c084fc; 3497 } 3498 3499 .nx-author-bio { 3500 font-size: 0.9rem; 3501 color: var(--nx-text-light); 3502 line-height: 1.5; 3503 margin: 0; 3504 } 3505 3506 @media (max-width: 600px) { 3507 .nx-home-author-card { 3508 flex-direction: column; 3509 text-align: center; 3510 padding: 24px 20px; 3511 gap: 16px; 3512 } 3513 } 3514 3515 /* ========================================================================== 3516 Newsletter Box v2 (Adaptive Theme: Light White on Sun / Sleek Dark on Moon) 3517 ========================================================================== */ 3518 .nx-newsletter-v2 { 3519 background: var(--nx-card-bg, #ffffff); 3520 border: 1px solid var(--nx-border, #e2e8f0); 3521 border-radius: 16px; 3522 padding: clamp(28px, 4vw, 42px); 3523 box-shadow: var(--nx-shadow-sm, 0 1px 3px rgba(0, 0, 0, 0.05)); 3524 display: grid; 3525 grid-template-columns: 1.1fr 0.9fr; 3526 align-items: center; 3527 gap: 32px; 3528 transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease; 3529 } 3530 3531 [data-theme="dark"] .nx-newsletter-v2 { 3532 background: linear-gradient(135deg, #090d16 0%, #111827 50%, #1e1b4b 100%) !important; 3533 border-color: rgba(255, 255, 255, 0.09) !important; 3534 box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35) !important; 3535 } 3536 3537 @media (max-width: 900px) { 3538 .nx-newsletter-v2 { 3539 grid-template-columns: 1fr; 3540 gap: 24px; 3541 } 3542 } 3543 3544 .nx-newsletter-badge-v2 { 3545 display: inline-flex; 3546 align-items: center; 3547 gap: 6px; 3548 padding: 3px 10px; 3549 background: var(--nx-primary-light, #eef2ff); 3550 color: var(--nx-primary, #4f46e5); 3551 border: 1px solid rgba(79, 70, 229, 0.15); 3552 border-radius: 20px; 3553 font-size: 0.72rem; 3554 font-weight: 700; 3555 text-transform: uppercase; 3556 letter-spacing: 0.05em; 3557 margin-bottom: 12px; 3558 transition: background 0.3s ease, color 0.3s ease, border-color 0.3s ease; 3559 } 3560 3561 [data-theme="dark"] .nx-newsletter-badge-v2 { 3562 background: rgba(255, 255, 255, 0.08) !important; 3563 color: #cbd5e1 !important; 3564 border-color: rgba(255, 255, 255, 0.12) !important; 3565 } 3566 3567 .nx-newsletter-title-v2 { 3568 font-size: clamp(1.45rem, 2.5vw, 2.1rem); 3569 font-weight: 800; 3570 color: var(--nx-dark, #0f172a); 3571 margin: 0 0 10px 0; 3572 line-height: 1.25; 3573 letter-spacing: -0.02em; 3574 transition: color 0.3s ease; 3575 } 3576 3577 [data-theme="dark"] .nx-newsletter-title-v2 { 3578 color: #ffffff !important; 3579 } 3580 3581 .nx-nl-highlight { 3582 color: var(--nx-primary, #4f46e5); 3583 transition: color 0.3s ease; 3584 } 3585 3586 [data-theme="dark"] .nx-nl-highlight { 3587 color: #c084fc !important; 3588 } 3589 3590 .nx-newsletter-desc-v2 { 3591 font-size: 0.92rem; 3592 color: var(--nx-text-light, #64748b); 3593 line-height: 1.55; 3594 margin: 0; 3595 transition: color 0.3s ease; 3596 } 3597 3598 [data-theme="dark"] .nx-newsletter-desc-v2 { 3599 color: #94a3b8 !important; 3600 } 3601 3602 .nx-newsletter-form-v2 { 3603 width: 100%; 3604 } 3605 3606 .nx-nl-input-wrap { 3607 display: flex; 3608 align-items: center; 3609 background: #f8fafc; 3610 border: 1px solid var(--nx-border, #e2e8f0); 3611 border-radius: 12px; 3612 padding: 6px 6px 6px 14px; 3613 transition: background 0.3s ease, border-color 0.2s ease, box-shadow 0.2s ease; 3614 } 3615 3616 [data-theme="dark"] .nx-nl-input-wrap { 3617 background: rgba(15, 23, 42, 0.85) !important; 3618 border-color: rgba(255, 255, 255, 0.14) !important; 3619 } 3620 3621 .nx-nl-input-wrap:focus-within { 3622 border-color: var(--nx-primary, #4f46e5); 3623 box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.15); 3624 } 3625 3626 [data-theme="dark"] .nx-nl-input-wrap:focus-within { 3627 border-color: #818cf8 !important; 3628 box-shadow: 0 0 0 3px rgba(129, 140, 248, 0.25) !important; 3629 } 3630 3631 .nx-nl-input-icon { 3632 color: #94a3b8; 3633 display: flex; 3634 align-items: center; 3635 margin-right: 10px; 3636 flex-shrink: 0; 3637 transition: color 0.3s ease; 3638 } 3639 3640 [data-theme="dark"] .nx-nl-input-icon { 3641 color: #64748b !important; 3642 } 3643 3644 .nx-nl-email-input { 3645 background: transparent !important; 3646 border: none !important; 3647 color: var(--nx-dark, #0f172a); 3648 font-size: 0.95rem; 3649 width: 100%; 3650 outline: none !important; 3651 padding: 8px 4px !important; 3652 transition: color 0.3s ease; 3653 } 3654 3655 [data-theme="dark"] .nx-nl-email-input { 3656 color: #ffffff !important; 3657 } 3658 3659 .nx-nl-email-input::placeholder { 3660 color: #94a3b8; 3661 } 3662 3663 [data-theme="dark"] .nx-nl-email-input::placeholder { 3664 color: #64748b !important; 3665 } 3666 3667 .nx-nl-submit-btn { 3668 background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%) !important; 3669 color: #ffffff !important; 3670 border: none !important; 3671 border-radius: 8px !important; 3672 padding: 10px 22px !important; 3673 font-weight: 700 !important; 3674 font-size: 0.92rem !important; 3675 cursor: pointer; 3676 white-space: nowrap; 3677 transition: transform 0.15s ease, opacity 0.2s ease, box-shadow 0.2s ease; 3678 box-shadow: 0 2px 8px rgba(79, 70, 229, 0.3); 3679 } 3680 3681 .nx-nl-submit-btn:hover { 3682 transform: translateY(-1px); 3683 opacity: 0.95; 3684 box-shadow: 0 4px 12px rgba(79, 70, 229, 0.4); 3685 } 3686 3687 /* Newsletter Success State Card (When Subscribed or Logged In) */ 3688 .nx-nl-success-card { 3689 background: #f0fdf4; 3690 border: 1px solid rgba(16, 185, 129, 0.35); 3691 border-radius: 14px; 3692 padding: 20px 24px; 3693 transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease; 3694 box-shadow: 0 2px 8px rgba(16, 185, 129, 0.08); 3695 animation: nxFadeSlideIn 0.35s ease-out; 3696 } 3697 3698 [data-theme="dark"] .nx-nl-success-card { 3699 background: linear-gradient(135deg, rgba(6, 78, 59, 0.25) 0%, rgba(15, 23, 42, 0.7) 100%) !important; 3700 border-color: rgba(52, 211, 153, 0.28) !important; 3701 box-shadow: 0 4px 20px rgba(0, 0, 0, 0.35) !important; 3702 } 3703 3704 .nx-nl-success-title { 3705 font-size: 1.1rem; 3706 font-weight: 800; 3707 color: #059669; 3708 margin: 0 0 6px 0; 3709 display: flex; 3710 align-items: center; 3711 gap: 6px; 3712 line-height: 1.3; 3713 letter-spacing: -0.01em; 3714 } 3715 3716 [data-theme="dark"] .nx-nl-success-title { 3717 color: #34d399 !important; 3718 } 3719 3720 .nx-nl-success-desc { 3721 font-size: 0.92rem; 3722 color: #475569; 3723 line-height: 1.5; 3724 margin: 0; 3725 } 3726 3727 [data-theme="dark"] .nx-nl-success-desc { 3728 color: #94a3b8 !important; 3729 } 3730 3731 @keyframes nxFadeSlideIn { 3732 from { 3733 opacity: 0; 3734 transform: translateY(6px); 3735 } 3736 to { 3737 opacity: 1; 3738 transform: translateY(0); 3739 } 3740 } 3741 3742 /* ========================================================================== 3743 Smart Social Media Icon Buttons (Follow Us Online) 3744 ========================================================================== */ 3745 .nx-contact-social-icons { 3746 display: flex; 3747 flex-wrap: wrap; 3748 gap: 10px; 3749 align-items: center; 3750 } 3751 3752 .nx-social-icon-btn { 3753 display: inline-flex; 3754 align-items: center; 3755 justify-content: center; 3756 width: 38px; 3757 height: 38px; 3758 border-radius: 50%; 3759 background: var(--nx-light); 3760 border: 1px solid var(--nx-border); 3761 color: var(--nx-dark); 3762 text-decoration: none; 3763 transition: all 0.25s ease; 3764 } 3765 3766 [data-theme="dark"] .nx-social-icon-btn { 3767 background: rgba(255, 255, 255, 0.05); 3768 border-color: rgba(255, 255, 255, 0.1); 3769 color: #ffffff; 3770 } 3771 3772 .nx-social-icon-btn svg { 3773 width: 17px; 3774 height: 17px; 3775 fill: currentColor; 3776 transition: transform 0.2s ease; 3777 } 3778 3779 .nx-social-icon-btn:hover { 3780 transform: translateY(-2px); 3781 box-shadow: 0 4px 12px rgba(0, 0, 0, 0.14); 3782 } 3783 3784 .nx-social-icon-btn.nx-social-facebook:hover { 3785 background: #1877f2 !important; 3786 border-color: #1877f2 !important; 3787 color: #ffffff !important; 3788 } 3789 3790 .nx-social-icon-btn.nx-social-twitter:hover { 3791 background: #000000 !important; 3792 border-color: #000000 !important; 3793 color: #ffffff !important; 3794 } 3795 3796 .nx-social-icon-btn.nx-social-linkedin:hover { 3797 background: #0a66c2 !important; 3798 border-color: #0a66c2 !important; 3799 color: #ffffff !important; 3800 } 3801 3802 .nx-social-icon-btn.nx-social-instagram:hover { 3803 background: radial-gradient(circle at 30% 107%, #fdf497 0%, #fdf497 5%, #fd5949 45%, #d6249f 60%, #285AEB 90%) !important; 3804 border-color: #d6249f !important; 3805 color: #ffffff !important; 3806 } 3807 3808 .nx-social-icon-btn.nx-social-youtube:hover { 3809 background: #ff0000 !important; 3810 border-color: #ff0000 !important; 3811 color: #ffffff !important; 3812 } 3813 3814 .nx-social-icon-btn.nx-social-github:hover { 3815 background: #24292e !important; 3816 border-color: #24292e !important; 3817 color: #ffffff !important; 3818 } 3819 3820 .nx-social-icon-btn.nx-social-discord:hover { 3821 background: #5865f2 !important; 3822 border-color: #5865f2 !important; 3823 color: #ffffff !important; 3824 } 3825 3826 .nx-social-icon-btn.nx-social-telegram:hover { 3827 background: #229ed9 !important; 3828 border-color: #229ed9 !important; 3829 color: #ffffff !important; 3830 } 3831 3832 .nx-social-icon-btn.nx-social-tiktok:hover { 3833 background: #010101 !important; 3834 border-color: #010101 !important; 3835 color: #ffffff !important; 3836 } 3837 3838 .nx-social-icon-btn.nx-social-website:hover { 3839 background: var(--nx-primary) !important; 3840 border-color: var(--nx-primary) !important; 3841 color: #ffffff !important; 3842 } 3843 3844 3845 3846 3847 3848 -
nexversal/3.5.9/template-full-width.php
r357433 r357886 1 1 <?php 2 /**3 * Security: Prevent direct script access4 */5 if (!defined('ABSPATH')) {6 exit;7 }8 9 2 /** 10 3 * Template Name: Full Width (No Sidebar) 11 4 * 12 5 * @package Nexversal 6 * @author Nexes Agency & Muhammad Shahid 7 * @copyright 2026 Nexes Agency & Muhammad Shahid 8 * @license GNU General Public License v2 or later 9 * @link https://nexversal.com 13 10 */ 11 12 if (!defined('ABSPATH')) { 13 exit; 14 } 14 15 15 16 get_header(); … … 23 24 ?> 24 25 <article id="post-<?php the_ID(); ?>" <?php post_class('nx-page-card'); ?>> 25 <?php nexversal_breadcrumbs(); ?> 26 <?php 27 if (!function_exists('nexversal_is_meta_disabled') || !nexversal_is_meta_disabled('disable_breadcrumbs')) { 28 nexversal_breadcrumbs(); 29 } 30 ?> 26 31 32 <?php if (!function_exists('nexversal_is_meta_disabled') || !nexversal_is_meta_disabled('disable_title')) : ?> 27 33 <header class="nx-page-header" style="margin-bottom: 24px;"> 28 34 <h1 class="nx-page-title" style="font-size: 2.2rem; font-weight: 800; letter-spacing: -0.03em;"><?php the_title(); ?></h1> 29 35 </header> 36 <?php endif; ?> 30 37 31 38 <?php if (has_post_thumbnail()) : ?> -
nexversal/3.5.9/template-parts/content-card.php
r357433 r357886 1 1 <?php 2 2 /** 3 * Security: Prevent direct script access 3 * Template part for displaying an article card in Nexversal Theme 4 * 5 * @package Nexversal 6 * @author Nexes Agency & Muhammad Shahid 7 * @copyright 2026 Nexes Agency & Muhammad Shahid 8 * @license GNU General Public License v2 or later 9 * @link https://nexversal.com 4 10 */ 11 5 12 if (!defined('ABSPATH')) { 6 13 exit; 7 14 } 8 9 /**10 * Template part for displaying a modern article card in grids11 *12 * @package Nexversal13 */14 15 ?> 15 16 <article id="post-<?php the_ID(); ?>" <?php post_class('nx-card'); ?>> … … 40 41 41 42 <div class="nx-card-excerpt"> 42 <?php echo esc_html(wp_trim_words(get_the_excerpt(), 1 4, '...')); ?>43 <?php echo esc_html(wp_trim_words(get_the_excerpt(), 11, '...')); ?> 43 44 </div> 44 45 45 46 <div class="nx-card-meta"> 46 47 <div class="nx-meta-author"> 47 <?php echo get_avatar(get_the_author_meta('ID'), 2 2); ?>48 <?php echo get_avatar(get_the_author_meta('ID'), 20); ?> 48 49 <span><?php the_author(); ?></span> 49 50 </div> … … 54 55 </div> 55 56 </div> 56 57 <?php58 $card_btn_text = get_theme_mod('nexversal_card_btn_text', 'Read Article');59 if (!empty($card_btn_text)) :60 ?>61 <div class="nx-card-action">62 <a href="<?php the_permalink(); ?>" class="nx-card-btn">63 <span><?php echo esc_html($card_btn_text); ?></span>64 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>65 </a>66 </div>67 <?php endif; ?>68 57 </div> 69 58 </article> -
nexversal/3.5.9/template-parts/newsletter-box.php
r357433 r357886 1 1 <?php 2 2 /** 3 * Security: Prevent direct script access 3 * Newsletter Subscription Component for Nexversal Theme (Image 2 Design) 4 * 5 * @package Nexversal 6 * @author Nexes Agency & Muhammad Shahid 7 * @copyright 2026 Nexes Agency & Muhammad Shahid 8 * @license GNU General Public License v2 or later 9 * @link https://nexversal.com 4 10 */ 11 5 12 if (!defined('ABSPATH')) { 6 13 exit; 7 14 } 8 15 9 /** 10 * Newsletter Subscription Component 11 * 12 * @package Nexversal 13 */ 16 $nl_title = get_theme_mod('nexversal_newsletter_title', __('New AI tool reviews,', 'nexversal')); 17 $nl_highlight = get_theme_mod('nexversal_newsletter_highlight', __('in your inbox.', 'nexversal')); 18 $nl_desc = get_theme_mod('nexversal_newsletter_desc', __('Get new reviews and comparisons in your inbox. No spam, unsubscribe anytime.', 'nexversal')); 19 $nl_btn = get_theme_mod('nexversal_newsletter_btn_text', __('Subscribe', 'nexversal')); 20 $nl_success_title = get_theme_mod('nexversal_newsletter_success_title', __("You're in. ✨", 'nexversal')); 21 $nl_success_desc = get_theme_mod('nexversal_newsletter_success_desc', __("We'll email you new reviews and comparisons as we publish them.", 'nexversal')); 22 23 $is_logged_in = is_user_logged_in(); 14 24 ?> 15 <section class="nx-newsletter- card">16 <div class="nx-newsletter- content">17 <span class="nx-newsletter-badge ">18 <svg width="1 4" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg>19 <?php esc_html_e('Ne xversal Tech Dispatch', 'nexversal'); ?>25 <section class="nx-newsletter-v2" data-logged-in="<?php echo $is_logged_in ? 'true' : 'false'; ?>"> 26 <div class="nx-newsletter-left"> 27 <span class="nx-newsletter-badge-v2"> 28 <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><circle cx="12" cy="12" r="3"/><line x1="12" y1="2" x2="12" y2="6"/><line x1="12" y1="18" x2="12" y2="22"/><line x1="4.93" y1="4.93" x2="7.76" y2="7.76"/><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"/><line x1="2" y1="12" x2="6" y2="12"/><line x1="18" y1="12" x2="22" y2="12"/><line x1="4.93" y1="19.07" x2="7.76" y2="16.24"/><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"/></svg> 29 <?php esc_html_e('Newsletter', 'nexversal'); ?> 20 30 </span> 21 <h3 class="nx-newsletter-title"><?php esc_html_e('Stay Ahead in Tech & Modern Software', 'nexversal'); ?></h3> 22 <p class="nx-newsletter-desc"> 23 <?php esc_html_e('Join developers, engineers, and digital innovators getting our weekly deep-dives on AI tools, cloud architectures, and development strategies.', 'nexversal'); ?> 31 <h3 class="nx-newsletter-title-v2"> 32 <?php echo esc_html($nl_title); ?> <span class="nx-nl-highlight"><?php echo esc_html($nl_highlight); ?></span> 33 </h3> 34 <p class="nx-newsletter-desc-v2"> 35 <?php echo esc_html($nl_desc); ?> 24 36 </p> 25 37 </div> 26 <form class="nx-newsletter-form" onsubmit="event.preventDefault(); var btn=this.querySelector('button'); btn.innerText='Subscribed!'; btn.style.background='#10b981'; this.querySelector('input').disabled=true;"> 27 <div class="nx-newsletter-input-group"> 28 <input type="email" placeholder="<?php esc_attr_e('Enter your work email...', 'nexversal'); ?>" required class="nx-newsletter-input" /> 29 <button type="submit" class="nx-newsletter-btn"> 30 <span><?php echo esc_html(get_theme_mod('nexversal_newsletter_btn_text', __('Subscribe', 'nexversal'))); ?></span> 31 <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg> 32 </button> 38 39 <div class="nx-newsletter-right"> 40 <!-- Success State Card (Shown when subscribed or user is logged in) --> 41 <div class="nx-nl-success-card" style="<?php echo $is_logged_in ? 'display: block;' : 'display: none;'; ?>"> 42 <div class="nx-nl-success-title"><?php echo esc_html($nl_success_title); ?></div> 43 <div class="nx-nl-success-desc"><?php echo esc_html($nl_success_desc); ?></div> 33 44 </div> 34 <p class="nx-newsletter-privacy"> 35 <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg> 36 <?php esc_html_e('No spam. Unsubscribe anytime with 1-click.', 'nexversal'); ?> 37 </p> 38 </form> 45 46 <!-- Default Email Form (Hidden when logged in or already subscribed) --> 47 <form class="nx-newsletter-form-v2" method="post" style="<?php echo $is_logged_in ? 'display: none;' : 'display: block;'; ?>"> 48 <div class="nx-nl-input-wrap"> 49 <span class="nx-nl-input-icon"> 50 <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path><polyline points="22,6 12,13 2,6"></polyline></svg> 51 </span> 52 <input type="email" name="nx_email" placeholder="<?php esc_attr_e('you@example.com', 'nexversal'); ?>" required class="nx-nl-email-input" autocomplete="email" /> 53 <button type="submit" class="nx-nl-submit-btn"> 54 <span><?php echo esc_html($nl_btn); ?></span> 55 </button> 56 </div> 57 <div class="nx-newsletter-message" style="display: none; font-size: 13px; margin-top: 8px;"></div> 58 </form> 59 </div> 39 60 </section> 40 61 -
nexversal/3.5.9/template-parts/social-share.php
r357433 r357886 1 1 <?php 2 2 /** 3 * Security: Prevent direct script access 3 * Social Share Buttons Component for Nexversal Theme 4 * 5 * @package Nexversal 6 * @author Nexes Agency & Muhammad Shahid 7 * @copyright 2026 Nexes Agency & Muhammad Shahid 8 * @license GNU General Public License v2 or later 9 * @link https://nexversal.com 4 10 */ 11 5 12 if (!defined('ABSPATH')) { 6 13 exit; 7 14 } 8 9 /**10 * Social Share Buttons Component11 *12 * @package Nexversal13 */14 15 15 16 $post_url = rawurlencode(get_permalink());
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)