['label' => __('Modern (indigo)', 'siteaudithub-case-studies'), 'primary' => '#4F46E5', 'accent' => '#06B6D4', 'heading' => '#111827', 'text' => '#374151', 'bg' => '#FFFFFF', 'card' => '#FFFFFF', 'soft' => '#F5F6FB', 'up' => '#047857', 'down' => '#B42318', 'on' => '#FFFFFF'], 'minimal' => ['label' => __('Minimal (mono)', 'siteaudithub-case-studies'), 'primary' => '#111111', 'accent' => '#6B7280', 'heading' => '#111111', 'text' => '#3F3F46', 'bg' => '#FFFFFF', 'card' => '#FFFFFF', 'soft' => '#F5F5F5', 'up' => '#047857', 'down' => '#B42318', 'on' => '#FFFFFF'], 'bold' => ['label' => __('Bold (purple & gold)', 'siteaudithub-case-studies'), 'primary' => '#6D28D9', 'accent' => '#F59E0B', 'heading' => '#1F1147', 'text' => '#3B3551', 'bg' => '#FFFFFF', 'card' => '#FFFFFF', 'soft' => '#F6F2FF', 'up' => '#047857', 'down' => '#B42318', 'on' => '#FFFFFF'], 'corporate' => ['label' => __('Corporate (navy)', 'siteaudithub-case-studies'), 'primary' => '#1E3A8A', 'accent' => '#D4A017', 'heading' => '#0F1B3D', 'text' => '#334155', 'bg' => '#FFFFFF', 'card' => '#FFFFFF', 'soft' => '#F1F5FB', 'up' => '#047857', 'down' => '#B42318', 'on' => '#FFFFFF'], 'fresh' => ['label' => __('Fresh (green)', 'siteaudithub-case-studies'), 'primary' => '#065F46', 'accent' => '#2563EB', 'heading' => '#0B2A22', 'text' => '#2B443D', 'bg' => '#FFFFFF', 'card' => '#FFFFFF', 'soft' => '#EEF8F4', 'up' => '#047857', 'down' => '#B42318', 'on' => '#FFFFFF'], 'dark' => ['label' => __('Dark', 'siteaudithub-case-studies'), 'primary' => '#818CF8', 'accent' => '#34D399', 'heading' => '#F3F4F6', 'text' => '#C7CBD4', 'bg' => '#0F1117', 'card' => '#171A23', 'soft' => '#1C2030', 'up' => '#34D399', 'down' => '#F87171', 'on' => '#0F1117'], ]; } /** * Font choices: the theme's fonts and fonts added through WordPress's * Font Library (Appearance → Fonts), plus safe system stacks. * SiteAuditHub Case Studies never loads fonts from external servers. */ function sahcs_font_choices() { $choices = [ 'inherit' => __('Same as my theme', 'siteaudithub-case-studies'), 'system-sans' => __('System sans-serif', 'siteaudithub-case-studies'), 'system-serif' => __('System serif', 'siteaudithub-case-studies'), ]; if (function_exists('wp_get_global_settings')) { $families = wp_get_global_settings(['typography', 'fontFamilies']); if (is_array($families)) { foreach (['theme', 'custom', 'default'] as $origin) { if (empty($families[$origin]) || !is_array($families[$origin])) { continue; } foreach ($families[$origin] as $f) { if (!empty($f['slug']) && !empty($f['name'])) { $choices['wp:' . sanitize_key($f['slug'])] = $f['name']; } } } } } return $choices; } function sahcs_font_css($choice) { switch ($choice) { case 'system-sans': return "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif"; case 'system-serif': return "Georgia, 'Times New Roman', Times, serif"; case 'inherit': case '': return 'inherit'; } if (strpos($choice, 'wp:') === 0) { return 'var(--wp--preset--font-family--' . sanitize_key(substr($choice, 3)) . ', inherit)'; } return 'inherit'; } /** Relative luminance (0 = black, 1 = white) of a #rrggbb colour. */ function sahcs_luminance($hex) { $hex = ltrim((string) $hex, '#'); if (strlen($hex) === 3) { $hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2]; } if (!preg_match('/^[0-9a-fA-F]{6}$/', $hex)) { return 0.5; } $c = []; foreach ([0, 2, 4] as $i) { $v = hexdec(substr($hex, $i, 2)) / 255; $c[] = $v <= 0.03928 ? $v / 12.92 : pow(($v + 0.055) / 1.055, 2.4); } return 0.2126 * $c[0] + 0.7152 * $c[1] + 0.0722 * $c[2]; } /** * Colours for a dark theme background: light text, see-through cards and * brand colours brightened enough to read on dark. */ function sahcs_dark_surface_vars($s, $p) { $bright = function ($color) { // Dark or mid colours are mixed with white so text and links stay readable. return sahcs_luminance($color) >= 0.28 ? $color : 'color-mix(in srgb,' . $color . ' 62%,#ffffff)'; }; $primary = !empty($s['color_primary']) ? $s['color_primary'] : $p['primary']; $accent = !empty($s['color_accent']) ? $s['color_accent'] : $p['accent']; return [ '--sahcs-primary' => $bright($primary), '--sahcs-accent' => $bright($accent), '--sahcs-heading' => '#F3F4F6', '--sahcs-text' => '#CBD5E1', '--sahcs-bg' => '#0F172A', '--sahcs-card' => 'rgba(255,255,255,.045)', '--sahcs-soft' => 'rgba(255,255,255,.06)', '--sahcs-up' => '#34D399', '--sahcs-down' => '#F87171', '--sahcs-on-primary' => '#0B1220', '--sahcs-card-shadow' => '0 12px 32px -16px rgba(0,0,0,.6)', '--sahcs-card-border' => '1px solid rgba(255,255,255,.09)', 'color-scheme' => 'dark', ]; } /** Builds the CSS custom properties from the saved design settings. */ function sahcs_design_css() { $s = sahcs_get_settings(); $presets = sahcs_design_presets(); $p = isset($presets[$s['design_preset']]) ? $presets[$s['design_preset']] : $presets['modern']; $pick = function ($key, $preset_key) use ($s, $p) { return !empty($s[$key]) ? $s[$key] : $p[$preset_key]; }; $vars = [ '--sahcs-primary' => $pick('color_primary', 'primary'), '--sahcs-accent' => $pick('color_accent', 'accent'), '--sahcs-heading' => $pick('color_heading', 'heading'), '--sahcs-text' => $pick('color_text', 'text'), '--sahcs-bg' => $pick('color_bg', 'bg'), '--sahcs-card' => $pick('color_card', 'card'), '--sahcs-soft' => $p['soft'], '--sahcs-up' => $p['up'], '--sahcs-down' => $p['down'], '--sahcs-on-primary' => $p['on'], '--sahcs-font-heading' => sahcs_font_css($s['font_heading']), '--sahcs-font-body' => sahcs_font_css($s['font_body']), '--sahcs-size' => (int) $s['base_size'] . 'px', // Typography: desktop and phone sizes (unitless px), blended fluidly in between. '--sahcs-p-d' => (int) $s['base_size'], '--sahcs-p-m' => (int) $s['p_size_m'], '--sahcs-h1-d' => (int) $s['h1_size'], '--sahcs-h1-m' => (int) $s['h1_size_m'], '--sahcs-h2-d' => (int) $s['h2_size'], '--sahcs-h2-m' => (int) $s['h2_size_m'], '--sahcs-h3-d' => (int) $s['h3_size'], '--sahcs-h3-m' => (int) $s['h3_size_m'], '--sahcs-radius' => (int) $s['radius'] . 'px', '--sahcs-width' => (int) $s['content_width'] . 'px', ]; $css = '.sahcs-scope{'; foreach ($vars as $k => $v) { $css .= $k . ':' . $v . ';'; } $css .= '}'; if ($s['card_style'] === 'border') { $css .= '.sahcs-scope{--sahcs-card-shadow:none;--sahcs-card-border:1px solid color-mix(in srgb,var(--sahcs-text) 16%,transparent);}'; } elseif ($s['card_style'] === 'flat') { $css .= '.sahcs-scope{--sahcs-card-shadow:none;--sahcs-card-border:0;}'; } // Page background: only painted when chosen (or with the Dark style), // otherwise the theme's own background shows through. if (!empty($s['color_bg'])) { $css .= '.sahcs-single,.sahcs-archive{--sahcs-page-bg:' . $s['color_bg'] . ';}'; } elseif ($s['design_preset'] === 'dark') { $css .= '.sahcs-single,.sahcs-archive{--sahcs-page-bg:' . $p['bg'] . ';}.sahcs-scope{color-scheme:dark;}'; // Shortcode output placed on a light page gets its own dark panel. $css .= '.sahcs-scope:not(.sahcs-single):not(.sahcs-archive):not(.sahcs-single .sahcs-scope):not(.sahcs-archive .sahcs-scope):not(.sahcs-scope .sahcs-scope){background:var(--sahcs-bg);color:var(--sahcs-text);padding:clamp(16px,3vw,28px);border-radius:var(--sahcs-radius);}'; $css .= '.sahcs-embed__link.sahcs-scope{padding-block:12px;}'; $css .= '.sahcs-scope .sahcs-cta--gradient .sahcs-button--light{color:' . $p['on'] . ';}'; } // The theme's own background is dark: blend in with it. $tone = isset($s['site_tone']) ? $s['site_tone'] : 'auto'; if ($s['design_preset'] !== 'dark' && $tone !== 'light') { $dark = ''; foreach (sahcs_dark_surface_vars($s, $p) as $k => $v) { $dark .= $k . ':' . $v . ';'; } // "auto": front-end script adds .sahcs-tone-dark when the page behind is dark. $sel = $tone === 'dark' ? '.sahcs-scope' : '.sahcs-scope.sahcs-tone-dark,.sahcs-tone-dark .sahcs-scope'; $css .= $sel . '{' . $dark . '}'; $light_btn = $tone === 'dark' ? '.sahcs-scope .sahcs-cta--gradient .sahcs-button--light' : '.sahcs-tone-dark .sahcs-cta--gradient .sahcs-button--light,.sahcs-tone-dark.sahcs-cta--gradient .sahcs-button--light'; $css .= $light_btn . '{color:#0B1220;}'; if ($tone === 'dark') { $css .= '.sahcs-single,.sahcs-archive{--sahcs-page-bg:transparent;}'; } } // One theme colour: gradients become the main colour (solid). if (isset($s['use_gradients']) && !$s['use_gradients']) { $css .= '.sahcs-scope,.sahcs-scope.sahcs-tone-dark,.sahcs-tone-dark .sahcs-scope{--sahcs-accent:var(--sahcs-primary);}'; } // No decorative dots and soft shapes. if (isset($s['decorations']) && !$s['decorations']) { $css .= '.sahcs-scope .sahcs-glance__decor,.sahcs-scope .sahcs-testimonial__mark{display:none}' . '.sahcs-scope .sahcs-team__photo-wrap::before,.sahcs-scope .sahcs-team__list::before{content:none;display:none}'; } return $css; } add_action('init', function () { wp_register_style('sahcs-front', SAHCS_URL . 'assets/css/front.css', [], sahcs_asset_ver('assets/css/front.css')); wp_add_inline_style('sahcs-front', sahcs_design_css()); wp_register_script('sahcs-front', SAHCS_URL . 'assets/js/front.js', [], sahcs_asset_ver('assets/js/front.js'), ['in_footer' => true, 'strategy' => 'defer']); $st = sahcs_get_settings(); wp_localize_script('sahcs-front', 'sahcsFront', [ 'tone' => ($st['design_preset'] !== 'dark' && $st['site_tone'] === 'auto') ? 'auto' : 'fixed', 'i18n' => [ 'close' => __('Close', 'siteaudithub-case-studies'), 'next' => __('Next image', 'siteaudithub-case-studies'), 'previous' => __('Previous image', 'siteaudithub-case-studies'), 'loading' => __('Loading…', 'siteaudithub-case-studies'), ], ]); }, 6); /** * Tiny script printed right after the page wrapper opens, so it runs before * the page is painted: it measures a fixed/sticky theme header (so content * and the table of contents are never hidden under it) and, when the site * background setting is "auto", switches to light text on dark themes. */ function sahcs_page_script() { $s = sahcs_get_settings(); $auto = ($s['design_preset'] !== 'dark' && $s['site_tone'] === 'auto') ? 'true' : 'false'; // Written without "&" on purpose: block themes run wptexturize over the // template, which would turn "&&" into "&&" and break the script. $js = '(function(w){if(!w)return;if(!w.getBoundingClientRect)return;var d=document,root=d.documentElement,auto=' . $auto . ';' . 'function rgb(c){var m=String(c).match(/rgba?\\(([^)]+)\\)/);if(!m)return null;var p=m[1].split(/[\\s,\\/]+/).filter(Boolean).map(parseFloat);if(p.length>3){if(p[3]<0.5)return null;}return p;}' . 'function lum(p){var c=[p[0],p[1],p[2]].map(function(v){v/=255;return v<=0.03928?v/12.92:Math.pow((v+0.055)/1.055,2.4);});return 0.2126*c[0]+0.7152*c[1]+0.0722*c[2];}' . 'function bg(el){for(;el;el=el.parentElement){if(el.nodeType!==1)break;var cs=getComputedStyle(el),p=rgb(cs.backgroundColor);if(p)return lum(p);var im=cs.backgroundImage;if(im){if(im!=="none"){var a=im.match(/rgba?\\([^)]+\\)/g)||[],t=0,n=0;a.forEach(function(c){var q=rgb(c);if(q){t+=lum(q);n++;}});if(n)return t/n;}}}return 1;}' . 'if(auto){var own=rgb(getComputedStyle(w).backgroundColor);if((own?lum(own):bg(w.parentElement))<0.3)w.classList.add("sahcs-tone-dark");}' . 'var ab=d.getElementById("wpadminbar"),top=(ab?getComputedStyle(ab).position==="fixed":false)?ab.offsetHeight:0,h=0,fixed=false,el=d.elementFromPoint(window.innerWidth/2,top+1);' . 'for(;el;el=el.parentElement){if(el===d.body||el===root)break;var pos=getComputedStyle(el).position;if(pos==="fixed"||pos==="sticky"){var r=el.getBoundingClientRect();if(r.top<=top+1){if(r.height<220){if(r.width>=window.innerWidth*0.6){h=Math.round(r.bottom-top);fixed=pos==="fixed";}}}break;}}' . 'root.style.setProperty("--sahcs-header-h",h+"px");' . 'if(fixed){var push=Math.ceil(top+h-(w.getBoundingClientRect().top+window.scrollY));if(push>0)w.style.setProperty("--sahcs-push",push+"px");}' . '})(document.currentScript?document.currentScript.parentNode:null);'; // Tell caching / optimisation plugins not to delay or move this tiny script // (WP Rocket, LiteSpeed, Autoptimize, SiteGround, Cloudflare Rocket Loader). return wp_get_inline_script_tag($js, [ 'data-no-optimize' => '1', 'data-no-defer' => '1', 'data-no-minify' => '1', 'data-noptimize' => '1', 'data-cfasync' => 'false', 'nowprocket' => true, ]); } /** Call this whenever SiteAuditHub Case Studies output is printed on a page. */ function sahcs_enqueue_front() { wp_enqueue_style('sahcs-front'); wp_enqueue_script('sahcs-front'); } /** * Whether this page will show SiteAuditHub Case Studies output, so the styles can load in * the (no flash of unstyled content). Also true inside page-builder * editors and previews, which render shortcodes in the background. */ function sahcs_page_needs_assets() { if (is_singular(SAHCS_CPT) || is_post_type_archive(SAHCS_CPT) || is_tax(SAHCS_TAX)) { return true; } // Page-builder editors / previews (read-only flags in the address). foreach (['elementor-preview', 'et_fb', 'vc_editable', 'fl_builder', 'bricks', 'ct_builder', 'brizy-edit-iframe'] as $flag) { if (isset($_GET[$flag])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- only checks that a flag is present return true; } } $post = is_singular() ? get_queried_object() : null; if ($post instanceof WP_Post) { if (strpos((string) $post->post_content, 'siteaudithub-case-studies') !== false) { return true; // block or shortcode in the normal content } // Content saved by page builders in their own fields. foreach (['_elementor_data', '_fl_builder_data', '_bricks_page_content_2', 'ct_builder_json'] as $key) { $data = get_post_meta($post->ID, $key, true); if ($data && strpos(is_string($data) ? $data : (string) wp_json_encode($data), 'siteaudithub-case-studies') !== false) { return true; } } } /** Force-load the styles (e.g. when a shortcode sits in a header or footer template). */ return (bool) apply_filters('sahcs_load_assets', false); } add_action('wp_enqueue_scripts', function () { if (sahcs_page_needs_assets()) { sahcs_enqueue_front(); } });