Listen to this article · 13 min listen

For content creators, the digital stage is both an opportunity and a battlefield. You pour hours into crafting compelling videos, insightful articles, or engaging podcasts, only to see them languish in obscurity. The core problem? A disconnect between your brilliant content and how search engines understand and present it. This often boils down to a lack of strategic on-page SEO, which prevents your hard work from reaching the audience it deserves. But what if there was a repeatable, step-by-step process to ensure your creator content consistently ranks?

Key Takeaways

  • Conduct in-depth keyword research, focusing on long-tail and intent-based phrases, to identify specific audience queries with search volume above 100 per month.
  • Structure your content with clear H2 and H3 headings, incorporating your primary keyword and related terms naturally within the first 150 words and throughout the body.
  • Optimize all visual and auditory assets by using descriptive file names, alt text for images, and transcripts for videos/audio, ensuring accessibility and search engine readability.
  • Implement internal linking to relevant past content and strategic external links to authoritative sources, aiming for 3-5 internal links and 1-2 external links per 1000 words.
  • Regularly monitor content performance using Google Search Console and refine your on-page elements based on click-through rates, impressions, and keyword rankings.

The Cost of Ignoring On-Page SEO: What Went Wrong First

I’ve seen it time and again. Talented creators, bursting with ideas, publish content with an almost religious fervor, believing that quality alone will win the day. While quality is non-negotiable, it’s rarely sufficient. Their initial approach, often, is a scattershot method: pick a topic they like, write about it, and hit publish. There’s no keyword research, no thought given to meta descriptions, no proper heading structure. They might share it on social media a few times, get an initial spike in traffic, and then watch as their masterpiece slowly fades into the internet’s abyss.

A client of mine, let’s call her Sarah, was a prime example. She ran a fantastic blog about sustainable living. Her articles were well-researched, deeply personal, and offered genuinely valuable advice. Yet, her traffic plateaued at around 5,000 unique visitors a month for nearly two years. Her content was fantastic, but it wasn’t discoverable. When I first looked at her site, I noticed a consistent pattern: titles were often catchy but devoid of keywords, images had generic file names like “IMG_001.jpg,” and her meta descriptions were simply the first few sentences of her articles. She was relying on word-of-mouth and social shares, which are great, but they don’t build evergreen traffic the way search engines do. She was effectively whispering into a megaphone that wasn’t plugged in.

The biggest mistake creators make early on is treating SEO as an afterthought, or worse, as a “hack” they can apply later. It’s not. It’s foundational. Without it, even the most brilliant piece of content is like a billboard in the desert. Nobody sees it. We need to shift from a “build it and they will come” mentality to a “build it for search engines AND they will come” strategy.

The Creator’s On-Page SEO Checklist: A Step-by-Step Optimization Journey

Optimizing creator content for search engines isn’t rocket science, but it does require discipline and a systematic approach. Here’s the checklist I use with my clients, broken down into actionable steps.

Step 1: Intent-Driven Keyword Research and Topic Clustering

This is where it all begins. Before you write a single word or record a single frame, you must understand what your audience is searching for. I use a combination of tools like Ahrefs and Semrush to identify high-volume, low-competition keywords, but also to understand user intent. Don’t just look for single keywords; focus on long-tail keywords and questions. For instance, instead of “vegan recipes,” consider “easy vegan dinner recipes for beginners with nutritional info.”

  • Identify Primary and Secondary Keywords: For each piece of content, pinpoint one primary keyword phrase and 2-3 secondary, related phrases. The primary keyword should have a search volume of at least 100 per month and a reasonable difficulty score for your domain authority.
  • Analyze Search Intent: Is the user looking for information (informational), trying to buy something (commercial), comparing options (investigational), or trying to go to a specific site (navigational)? Your content must align with this intent. A HubSpot report from 2024 emphasized that aligning content with user intent significantly increases conversion rates across various metrics.
  • Build Topic Clusters: Instead of creating standalone pieces, think in terms of content clusters. Have a central “pillar page” that broadly covers a topic (e.g., “Complete Guide to Digital Marketing”) and then create several “cluster content” pieces that dive deep into specific sub-topics (e.g., “Facebook Ads Strategies,” “Email Marketing Automation,” “SEO for Small Businesses“), all linking back to the pillar. This signals to search engines your authority on a subject.

My advice? Spend a solid 20% of your content creation time on this step. It’s that important. Skipping it is like building a house without a blueprint. You might get something, but it won’t be structurally sound.

Step 2: Crafting Compelling and Keyword-Rich Titles and Meta Descriptions

Your title tag and meta description are your content’s storefront window. They are often the first, and sometimes only, impression a searcher gets. Get them right.

  • Title Tag ( tag):</strong> This should include your primary keyword, ideally at the beginning, and be under 60 characters to avoid truncation in search results. Make it descriptive and enticing. For a video on composting, a title like “DIY Composting for Beginners: Reduce Waste & Boost Your Garden” is far better than “Composting Guide.”</li> <li><strong>Meta Description:</strong> This is your sales pitch. Keep it under 160 characters. Include your primary and possibly a secondary keyword naturally. More importantly, make it benefit-oriented and action-provoking. Tell the user exactly what they’ll gain by clicking. I usually recommend including a call to action, even a soft one, like “Learn how…” or “Discover tips for…”</li> <li><strong>URL Structure:</strong> Keep your URLs clean, concise, and keyword-rich. For example, <code>yourdomain.com/diy-composting-beginners</code> is excellent. Avoid long, numerical, or overly complex URLs.</li> </ul> <p>I once worked with a travel blogger who had incredible content about unique destinations. Her titles, however, were often just the name of the city. We changed “Paris” to “Hidden Gems of Paris: A Local’s Guide to Authentic Experiences” and saw a 30% jump in click-through rates within two months. Small changes, massive impact.</p> <h3>Step 3: On-Page Content Optimization: Structure, Readability, and Keywords</h3> <p>Once a user clicks, your content needs to deliver. But it also needs to be structured in a way that search engines can easily crawl and understand.</p> <ul> <li><strong>Heading Structure (H1, H2, H3):</strong> Your main topic is implicitly the H1 (your article title). Use <strong>H2 headings</strong> for main sections and <strong>H3 headings</strong> for subsections. Incorporate your primary keyword in at least one H2, and related secondary keywords in other H2s and H3s. This creates a logical hierarchy.</li> <li><strong>Keyword Placement:</strong> Place your primary keyword naturally within the first 100-150 words of your content. Distribute it and its semantic variations throughout the body, but never force it. Keyword stuffing is a relic of the past and will hurt you.</li> <li><strong>Content Length and Depth:</strong> For most informational queries, longer, more comprehensive content tends to rank better. Aim for at least 1,500 words for blog posts and articles. For video, aim for a minimum of 8-10 minutes, ensuring you cover the topic thoroughly. A 2025 study by <a href="https://www.nielsen.com/insights/2025-digital-content-consumption-report/" target="_blank" rel="noopener">Nielsen</a> found that users spent an average of 3.5 minutes longer on content over 1,800 words compared to shorter pieces, indicating higher engagement.</li> <li><strong>Readability:</strong> Use short paragraphs, bullet points, numbered lists, and bold text to break up your content. Tools like <a href="https://yoast.com/readability-checker/" target="_blank" rel="noopener">Yoast SEO</a> (for WordPress) or Hemingway Editor can help you assess and improve readability scores. Complex sentences deter readers and signal lower quality to algorithms.</li> </ul> <p>This is where many creators stumble. They write a dense wall of text, or they don’t break up their points logically. Remember, people scan online content. Make it easy for them.</p> <h3>Step 4: Media Optimization and Accessibility</h3> <p>Images, videos, and audio are critical for engagement, but they also offer significant SEO opportunities.</p> <ul> <li><strong>Image Optimization:</strong> <ul> <li><strong>File Names:</strong> Before uploading, name your image files descriptively with keywords (e.g., <code>sustainable-gardening-compost-bin.jpg</code> instead of <code>DSC00123.jpg</code>).</li> <li><strong>Alt Text:</strong> Provide descriptive <strong>alt text</strong> for every image. This helps search engines understand the image content and is crucial for accessibility. Imagine describing the image to someone who can’t see it.</li> <li><strong>Compression:</strong> Compress images to reduce file size without sacrificing quality. Large images slow down your page, which harms user experience and SEO. Tools like <a href="https://tinypng.com/" target="_blank" rel="noopener">TinyPNG</a> are fantastic for this.</li> </ul> </li> <li><strong>Video and Audio Optimization:</strong> <ul> <li><strong>Transcripts:</strong> Always provide a full transcript for your videos and audio content. This makes your content searchable and accessible.</li> <li><strong>Captions/Subtitles:</strong> Implement accurate captions. Google can “read” these, helping them understand your content.</li> <li><strong>Video Schema Markup:</strong> While a bit more advanced, implementing video schema markup can help search engines display your videos with rich snippets, increasing visibility.</li> </ul> </li> </ul> <p>I had a client who was a chef, and his recipe blog was visually stunning. But every image was huge, and named things like “IMG_4567.jpeg”. We spent a week renaming and compressing his images and adding proper alt text. Within three months, his image search traffic quadrupled. It was low-hanging fruit, but incredibly effective.</p> <aside class="related-callout"><span class="related-callout-label">Related Reading</span><a href="https://mediaexposurehub.com/visual-storytelling-myths-for-creator-brands-2026/">Visual Storytelling Myths for Creator Brands 2026</a></p> <p class="related-callout-excerpt">Learn how to effectively use visuals in your content strategy, beyond just SEO, to connect with your audience.</p> </aside> <h3>Step 5: Internal and External Linking Strategies</h3> <p>Links are the highways of the internet. They pass authority and help users and search engines navigate your content.</p> <ul> <li><strong>Internal Linking:</strong> Link to other relevant content within your own site. This helps search engines discover your pages, passes “link equity” between them, and keeps users on your site longer. Aim for 3-5 internal links per 1,000 words of content. Use descriptive <strong>anchor text</strong> (the visible, clickable text of a hyperlink) that includes relevant keywords, not just “click here.”</li> <li><strong>External Linking:</strong> Link out to authoritative, high-quality sources when you cite statistics, research, or definitions. This boosts your credibility and provides additional value to your readers. I typically aim for 1-2 external links to reputable sources per 1,000 words. Just be sure these links open in a new tab (<code>target="_blank" rel="noopener"</code>) so users don’t leave your site entirely. According to <a href="https://www.iab.com/insights/digital-ad-spend-report-2026/" target="_blank" rel="noopener">IAB’s 2026 Digital Ad Spend Report</a>, content that demonstrates clear external validation through authoritative links often sees higher organic search rankings.</li> </ul> <p>Here’s what nobody tells you: many creators are scared to link out because they think it sends traffic away. That’s a short-sighted view. Linking to quality sources actually enhances your own content’s value and trustworthiness. It’s a signal to search engines that you’re part of a broader, credible information ecosystem.</p> <h2 id="section-4">Measurable Results: The Payoff of Diligent Optimization</h2> <p>Implementing this on-page SEO checklist isn’t a magic bullet for overnight success, but it provides a consistent, compounding return on investment. With Sarah, the sustainable living blogger, after we systematically applied this checklist to her entire content library over six months, the results were undeniable. Her organic traffic soared from 5,000 to over 25,000 unique visitors per month. Her keyword rankings for her target long-tail phrases moved from page 3-5 to the top 3 positions. This translated directly into a 40% increase in affiliate revenue and a significant boost in her email list subscribers.</p> <p>We saw similar success with a small local business that specialized in artisanal baked goods in the Decatur Square area. Their previous website was practically invisible. We optimized their “best sourdough bread in Decatur” page, incorporating local keywords, optimizing images of their various breads, and creating internal links to their “<a href="https://mediaexposurehub.com/artists-master-local-seo-in-2026/">catering for Atlanta events</a>” page. Within four months, they ranked #1 for “sourdough Decatur GA” and saw a 15% increase in foot traffic to their physical location on Ponce de Leon Avenue. These aren’t abstract gains; these are tangible business outcomes.</p> <p>The beauty of on-page SEO is that it gives you control. Unlike off-page factors (like backlinks from other sites), you can directly influence every element on your page. By diligently following this creator checklist, you’re not just hoping for visibility; you’re actively building it, piece by piece, ensuring your valuable content reaches the eyes and ears it was intended for.</p> <p>The consistent application of <a href="https://mediaexposurehub.com/artist-discoverability-niche-seo-secrets-for-2026/">on-page SEO principles</a> is the single most impactful action creators can take to ensure their content’s long-term discoverability and success.</p> <div class="faq-section"> <div class="faq-item"> <h3 class="faq-question">How often should I review and update my old content for on-page SEO?</h3> <div class="faq-answer"> <p>I recommend a comprehensive review of your top 20% performing content every 6-12 months. For other content, a lighter touch-up every 12-18 months to ensure keywords are still relevant, links are active, and information is up-to-date. Google values fresh, relevant content.</p> </div> </div> <div class="faq-item"> <h3 class="faq-question">Is keyword density still an important factor for on-page SEO?</h3> <div class="faq-answer"> <p>No, not in the way it was in the early 2010s. Trying to hit a specific keyword density percentage is an outdated and harmful practice that can lead to keyword stuffing. Instead, focus on naturally integrating your primary keyword and its semantic variations throughout your content, ensuring readability and value for the user. Search engines are far more sophisticated now and understand context.</p> </div> </div> <div class="faq-item"> <h3 class="faq-question">What’s the difference between a title tag and an H1 heading?</h3> <div class="faq-answer"> <p>The title tag is an HTML element that defines the title of your web page, appearing in the browser tab and as the main clickable link in search engine results. The H1 heading is an on-page heading that typically serves as the main title of your content visible to users on the page itself. While they are often similar, they serve different technical and user experience purposes. Make sure your title tag is concise for search results, and your H1 is engaging for your on-page readers.</p> </div> </div> <div class="faq-item"> <h3 class="faq-question">Should I use AI tools for generating meta descriptions and alt text?</h3> <div class="faq-answer"> <p>AI tools can be a helpful starting point for generating ideas for meta descriptions and alt text, saving you time. However, I strongly advise against using them without human review and refinement. AI-generated content can sometimes lack nuance, specificity, or the human touch that makes a description truly compelling or accurate. Always review, edit, and personalize anything an AI tool produces.</p> </div> </div> <div class="faq-item"> <h3 class="faq-question">How important is page speed for on-page SEO?</h3> <div class="faq-answer"> <p>Page speed is incredibly important. While technically more of a technical SEO factor, it directly impacts user experience, which in turn influences on-page SEO metrics like bounce rate and time on page. Google has confirmed page speed as a ranking factor. Slow pages frustrate users and can lead to higher bounce rates, signaling to search engines that your content might not be high quality. Optimize image sizes, use efficient hosting, and consider a Content Delivery Network (CDN) to ensure fast loading times.</p> </div> </div> </div> </div> <div class="benqu-feedback" id="benqu-feedback"> <span class="benqu-feedback-q">Was this article helpful?</span> <button type="button" data-vote="yes" aria-label="Yes"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 9V5a3 3 0 00-3-3l-4 9v11h11.28a2 2 0 002-1.7l1.38-9a2 2 0 00-2-2.3H14z"/><path d="M7 22H4a2 2 0 01-2-2v-7a2 2 0 012-2h3"/></svg> Yes </button> <button type="button" data-vote="no" aria-label="No"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 15v4a3 3 0 003 3l4-9V2H5.72a2 2 0 00-2 1.7l-1.38 9a2 2 0 002 2.3H10z"/><path d="M17 2h2.67A2.31 2.31 0 0122 4v7a2.31 2.31 0 01-2.33 2H17"/></svg> No </button> </div> <script> (function(){ var fb = document.getElementById('benqu-feedback'); if(!fb) return; fb.querySelectorAll('button').forEach(function(btn){ btn.addEventListener('click', function(){ var vote = this.dataset.vote; fetch('/wp-json/satellite/v1/feedback', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({post_id:14885, vote: vote}) }).catch(function(){}); fb.innerHTML = '<span class="benqu-feedback-thanks">Thanks for your feedback!</span>'; }); }); })(); </script> <div class="benqu-author-bio"> <div> <img fetchpriority="low" loading="lazy" alt='' src='https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/03/mediaexpfionadavis-headshot-1-150x150.png' srcset='https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/03/mediaexpfionadavis-headshot-1-300x300.png 2x' class='avatar avatar-96 photo' height='96' width='96' decoding='async'/> </div> <div> <h3 class="benqu-author-name"> <a href="https://mediaexposurehub.com/author/mediaexpfionadavis/"> Ashley Valenzuela </a> </h3> <div class="benqu-author-title">Senior Director of Marketing Innovation</div> <span class="benqu-author-credentials">Certified Marketing Management Professional (CMMP)</span> <p class="benqu-author-desc">Ashley Valenzuela is a seasoned Marketing Strategist with over a decade of experience driving growth for both B2B and B2C companies. She currently serves as the Senior Director of Marketing Innovation at Stellar Solutions Group, where she spearheads the development and implementation of cutting-edge marketing campaigns. Prior to Stellar Solutions, Ashley honed her skills at Innovate Marketing Partners, focusing on data-driven strategies. A recognized thought leader in the field, Ashley is passionate about leveraging technology to enhance customer engagement and brand loyalty. Notably, she led the team that achieved a 300% increase in lead generation for Stellar Solutions' flagship product in just one quarter.</p> <div class="benqu-author-links"> <a class="benqu-author-link" href="https://www.marketingcertification.com" target="_blank" rel="noopener noreferrer"> Credentials </a> <span class="benqu-author-experience">12+ years experience</span> </div> </div> </div> </div> <aside class="benqu-sidebar"> <div class="benqu-sidebar-block"> <h3 class="benqu-sidebar-title">Popular Posts</h3> <article class="benqu-card-numbered"> <div class="benqu-card-num">01</div> <a class="benqu-card-image" href="https://mediaexposurehub.com/brandwatch-2026-social-listening-powers-marketing-teams/" aria-label="Brandwatch 2026: Social Listening Powers Marketing Teams"> <img width="300" height="200" src="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/04/brandwatch-2026-social-listening-powers-marketing-teams-featured-300x200.webp?v=1776118710" class="attachment-satellite-thumb size-satellite-thumb wp-post-image" alt="Brandwatch 2026: Social Listening Powers Marketing Teams" loading="lazy" decoding="async" srcset="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/04/brandwatch-2026-social-listening-powers-marketing-teams-featured-300x200.webp?v=1776118710 300w, https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/04/brandwatch-2026-social-listening-powers-marketing-teams-featured-600x400.webp?v=1776118710 600w" sizes="auto, (max-width: 300px) 100vw, 300px" /> </a> <div> <a class="benqu-pill" href="https://mediaexposurehub.com/category/branding/" style="background:#17c0d6;margin-bottom:6px;"> Branding </a> <h4 class="benqu-card-title"> <a href="https://mediaexposurehub.com/brandwatch-2026-social-listening-powers-marketing-teams/">Brandwatch 2026: Social Listening Powers Marketing Teams</a> </h4> <div class="benqu-card-meta" style="margin-top:4px;"> <time datetime="2026-04-10T20:56:15+00:00"> Apr 10, 2026 </time> </div> </div> </article> <article class="benqu-card-numbered"> <div class="benqu-card-num">02</div> <a class="benqu-card-image" href="https://mediaexposurehub.com/meta-business-suite-grow-your-audience-in-2026/" aria-label="Meta Business Suite: Grow Your Audience in 2026"> <img width="300" height="200" src="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/04/meta-business-suite-grow-your-audience-in-2026-featured-300x200.webp?v=1777581661" class="attachment-satellite-thumb size-satellite-thumb wp-post-image" alt="Meta Business Suite: Grow Your Audience in 2026" loading="lazy" decoding="async" srcset="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/04/meta-business-suite-grow-your-audience-in-2026-featured-300x200.webp 300w, https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/04/meta-business-suite-grow-your-audience-in-2026-featured-600x400.webp 600w" sizes="auto, (max-width: 300px) 100vw, 300px" /> </a> <div> <a class="benqu-pill" href="https://mediaexposurehub.com/category/social-media/" style="background:#2eb85c;margin-bottom:6px;"> Social Media </a> <h4 class="benqu-card-title"> <a href="https://mediaexposurehub.com/meta-business-suite-grow-your-audience-in-2026/">Meta Business Suite: Grow Your Audience in 2026</a> </h4> <div class="benqu-card-meta" style="margin-top:4px;"> <time datetime="2026-04-28T09:36:00+00:00"> Apr 28, 2026 </time> </div> </div> </article> <article class="benqu-card-numbered"> <div class="benqu-card-num">03</div> <a class="benqu-card-image" href="https://mediaexposurehub.com/musician-marketing-real-2026-success-stories/" aria-label="Musician Marketing: Real 2026 Success Stories"> <img width="300" height="200" src="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/03/musician-marketing-real-2026-success-stories-featured-300x200.webp?v=1773765335" class="attachment-satellite-thumb size-satellite-thumb wp-post-image" alt="Musician Marketing: Real 2026 Success Stories" loading="lazy" decoding="async" srcset="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/03/musician-marketing-real-2026-success-stories-featured-300x200.webp?v=1773765335 300w, https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/03/musician-marketing-real-2026-success-stories-featured-600x400.webp?v=1773765335 600w" sizes="auto, (max-width: 300px) 100vw, 300px" /> </a> <div> <a class="benqu-pill" href="https://mediaexposurehub.com/category/digital-marketing/" style="background:#00b8a9;margin-bottom:6px;"> Digital Marketing </a> <h4 class="benqu-card-title"> <a href="https://mediaexposurehub.com/musician-marketing-real-2026-success-stories/">Musician Marketing: Real 2026 Success Stories</a> </h4> <div class="benqu-card-meta" style="margin-top:4px;"> <time datetime="2026-03-17T16:35:36+00:00"> Mar 17, 2026 </time> </div> </div> </article> <article class="benqu-card-numbered"> <div class="benqu-card-num">04</div> <a class="benqu-card-image" href="https://mediaexposurehub.com/creator-economy-marketing-trends-for-2026/" aria-label="Creator Economy: Marketing Trends for 2026"> <img width="300" height="200" src="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/03/creator-economy-marketing-trends-for-2026-featured-300x200.webp?v=1774534317" class="attachment-satellite-thumb size-satellite-thumb wp-post-image" alt="Creator Economy: Marketing Trends for 2026" loading="lazy" decoding="async" srcset="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/03/creator-economy-marketing-trends-for-2026-featured-300x200.webp?v=1774534317 300w, https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/03/creator-economy-marketing-trends-for-2026-featured-600x400.webp?v=1774534317 600w" sizes="auto, (max-width: 300px) 100vw, 300px" /> </a> <div> <a class="benqu-pill" href="https://mediaexposurehub.com/category/branding/" style="background:#17c0d6;margin-bottom:6px;"> Branding </a> <h4 class="benqu-card-title"> <a href="https://mediaexposurehub.com/creator-economy-marketing-trends-for-2026/">Creator Economy: Marketing Trends for 2026</a> </h4> <div class="benqu-card-meta" style="margin-top:4px;"> <time datetime="2026-03-26T14:11:58+00:00"> Mar 26, 2026 </time> </div> </div> </article> <article class="benqu-card-numbered"> <div class="benqu-card-num">05</div> <a class="benqu-card-image" href="https://mediaexposurehub.com/audience-building-in-2026-creators-guide/" aria-label="Audience Building in 2026: Creator’s Guide"> <img width="300" height="200" src="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/03/audience-building-in-2026-creator-s-guide-featured-300x200.webp?v=1774790785" class="attachment-satellite-thumb size-satellite-thumb wp-post-image" alt="Audience Building in 2026: Creator’s Guide" loading="lazy" decoding="async" srcset="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/03/audience-building-in-2026-creator-s-guide-featured-300x200.webp?v=1774790785 300w, https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/03/audience-building-in-2026-creator-s-guide-featured-600x400.webp?v=1774790785 600w" sizes="auto, (max-width: 300px) 100vw, 300px" /> </a> <div> <a class="benqu-pill" href="https://mediaexposurehub.com/category/content-strategy/" style="background:#e8543f;margin-bottom:6px;"> Content Strategy </a> <h4 class="benqu-card-title"> <a href="https://mediaexposurehub.com/audience-building-in-2026-creators-guide/">Audience Building in 2026: Creator’s Guide</a> </h4> <div class="benqu-card-meta" style="margin-top:4px;"> <time datetime="2026-03-29T13:26:26+00:00"> Mar 29, 2026 </time> </div> </div> </article> </div> </aside> </div> <section class="benqu-related"> <h2 class="benqu-related-title">Related Articles</h2> <div class="benqu-grid benqu-grid-3"> <article class="benqu-card"> <div class="benqu-card-media"> <a class="benqu-card-image" href="https://mediaexposurehub.com/mobile-seo-master-gsc-for-2026-visibility/" aria-label="Mobile SEO: Master GSC for 2026 Visibility"> <img width="600" height="400" src="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/08/mobile-seo-master-gsc-for-2026-visibility-featured-600x400.webp?v=1786695779" class="attachment-satellite-card size-satellite-card wp-post-image" alt="Mobile SEO: Master GSC for 2026 Visibility" loading="lazy" decoding="async" srcset="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/08/mobile-seo-master-gsc-for-2026-visibility-featured-600x400.webp 600w, https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/08/mobile-seo-master-gsc-for-2026-visibility-featured-300x200.webp 300w, https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/08/mobile-seo-master-gsc-for-2026-visibility-featured-400x267.webp 400w" sizes="auto, (max-width: 600px) 100vw, 600px" /> </a> <a class="benqu-pill benqu-pill--overlay" href="https://mediaexposurehub.com/category/seo-sem/" style="background:#f5a623"> SEO & SEM </a> </div> <div class="benqu-card-body"> <h3 class="benqu-card-title"> <a href="https://mediaexposurehub.com/mobile-seo-master-gsc-for-2026-visibility/">Mobile SEO: Master GSC for 2026 Visibility</a> </h3> <p class="benqu-card-excerpt"> Listen to this article · 13 min listen1.0xAudio playback not supported in this browser.Reaching audiences on their smartphones isn’t just an option anymore, it’s a mandate. With… </p> <div class="benqu-card-meta"> <span class="by">By</span> <strong>Ashley Valenzuela</strong> <span class="dot">·</span> <time datetime="2026-08-14T07:15:51+00:00"> Aug 14, 2026 </time> <span class="dot">·</span> <span>3 Views</span> </div> </div> </article> <article class="benqu-card"> <div class="benqu-card-media"> <a class="benqu-card-image" href="https://mediaexposurehub.com/website-speed-seo-5-must-know-fixes-for-2026/" aria-label="Website Speed SEO: 5 Must-Know Fixes for 2026"> <img width="600" height="400" src="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/08/website-speed-seo-5-must-know-fixes-for-2026-featured-600x400.webp?v=1786662165" class="attachment-satellite-card size-satellite-card wp-post-image" alt="Website Speed SEO: 5 Must-Know Fixes for 2026" loading="lazy" decoding="async" srcset="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/08/website-speed-seo-5-must-know-fixes-for-2026-featured-600x400.webp 600w, https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/08/website-speed-seo-5-must-know-fixes-for-2026-featured-300x200.webp 300w, https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/08/website-speed-seo-5-must-know-fixes-for-2026-featured-400x267.webp 400w" sizes="auto, (max-width: 600px) 100vw, 600px" /> </a> <a class="benqu-pill benqu-pill--overlay" href="https://mediaexposurehub.com/category/seo-sem/" style="background:#f5a623"> SEO & SEM </a> </div> <div class="benqu-card-body"> <h3 class="benqu-card-title"> <a href="https://mediaexposurehub.com/website-speed-seo-5-must-know-fixes-for-2026/">Website Speed SEO: 5 Must-Know Fixes for 2026</a> </h3> <p class="benqu-card-excerpt"> Listen to this article · 11 min listen1.0xAudio playback not supported in this browser.In the competitive digital arena, the speed of your website isn’t just a technical… </p> <div class="benqu-card-meta"> <span class="by">By</span> <strong>Dillon Cooper</strong> <span class="dot">·</span> <time datetime="2026-08-13T18:00:16+00:00"> Aug 13, 2026 </time> <span class="dot">·</span> <span>8 Views</span> </div> </div> </article> <article class="benqu-card"> <div class="benqu-card-media"> <a class="benqu-card-image" href="https://mediaexposurehub.com/brandwatch-2026-social-listening-powers-marketing-teams/" aria-label="Brandwatch 2026: Social Listening Powers Marketing Teams"> <img width="600" height="400" src="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/04/brandwatch-2026-social-listening-powers-marketing-teams-featured-600x400.webp?v=1776118710" class="attachment-satellite-card size-satellite-card wp-post-image" alt="Brandwatch 2026: Social Listening Powers Marketing Teams" loading="lazy" decoding="async" srcset="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/04/brandwatch-2026-social-listening-powers-marketing-teams-featured-600x400.webp?v=1776118710 600w, https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/04/brandwatch-2026-social-listening-powers-marketing-teams-featured-300x200.webp?v=1776118710 300w" sizes="auto, (max-width: 600px) 100vw, 600px" /> </a> <a class="benqu-pill benqu-pill--overlay" href="https://mediaexposurehub.com/category/branding/" style="background:#17c0d6"> Branding </a> </div> <div class="benqu-card-body"> <h3 class="benqu-card-title"> <a href="https://mediaexposurehub.com/brandwatch-2026-social-listening-powers-marketing-teams/">Brandwatch 2026: Social Listening Powers Marketing Teams</a> </h3> <p class="benqu-card-excerpt"> Listen to this article · 9 min listen1.0xAudio playback not supported in this browser. Key Takeaways You can now use AI-powered sentiment analysis in Brandwatch’s 2026 platform… </p> <div class="benqu-card-meta"> <span class="by">By</span> <strong>Ashley White</strong> <span class="dot">·</span> <time datetime="2026-04-10T20:56:15+00:00"> Apr 10, 2026 </time> <span class="dot">·</span> <span>411 Views</span> </div> </div> </article> </div> </section> </div> </article> <div class="benqu-read-next" id="benqu-read-next"> <span class="benqu-read-next-label">Read Next</span> <a class="benqu-read-next-title" href="https://mediaexposurehub.com/google-ads-for-courses-2026-profit-strategies/"> Google Ads for Courses: 2026 Profit Strategies </a> <a class="benqu-read-next-cta" href="https://mediaexposurehub.com/google-ads-for-courses-2026-profit-strategies/" aria-label="Read next article">→</a> </div> <script> (function(){ var bar = document.getElementById('benqu-read-next'); if(!bar) return; var shown = false; window.addEventListener('scroll', function(){ if(shown) return; var d = document.documentElement; var pct = (d.scrollTop / (d.scrollHeight - d.clientHeight)) * 100; if (pct > 60) { bar.classList.add('visible'); shown = true; } }, {passive: true}); })(); </script> </div><!-- /.benqu-site-main --> <footer class="benqu-footer" role="contentinfo"> <div class="benqu-container"> <div class="benqu-footer-grid"> <div> <div class="benqu-footer-brand">Media Exposure Hub</div><p class="benqu-footer-desc">Expert insights, guides, and stories about marketing</p> </div> <div> <h4>Explore</h4> <ul> <li><a href="https://mediaexposurehub.com/category/digital-marketing/">Digital Marketing</a></li><li><a href="https://mediaexposurehub.com/category/content-strategy/">Content Strategy</a></li><li><a href="https://mediaexposurehub.com/category/marketing-innovation/">Marketing Innovation</a></li><li><a href="https://mediaexposurehub.com/category/marketing-analytics/">Marketing Analytics</a></li><li><a href="https://mediaexposurehub.com/category/seo-sem/">SEO & SEM</a></li><li><a href="https://mediaexposurehub.com/category/social-media/">Social Media</a></li><li><a href="https://mediaexposurehub.com/about/">About</a></li><li><a href="https://mediaexposurehub.com/contact/">Contact</a></li> </ul> </div> <div> <h4>Popular Posts</h4> <div class="benqu-footer-popular"> <div class="benqu-footer-popular-item"> <a class="benqu-card-image" href="https://mediaexposurehub.com/brandwatch-2026-social-listening-powers-marketing-teams/"> <img width="300" height="200" src="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/04/brandwatch-2026-social-listening-powers-marketing-teams-featured-300x200.webp?v=1776118710" class="attachment-satellite-thumb size-satellite-thumb wp-post-image" alt="" decoding="async" loading="lazy" srcset="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/04/brandwatch-2026-social-listening-powers-marketing-teams-featured-300x200.webp?v=1776118710 300w, https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/04/brandwatch-2026-social-listening-powers-marketing-teams-featured-600x400.webp?v=1776118710 600w" sizes="auto, (max-width: 300px) 100vw, 300px" /> </a> <div> <h5 class="benqu-card-title"> <a href="https://mediaexposurehub.com/brandwatch-2026-social-listening-powers-marketing-teams/">Brandwatch 2026: Social Listening Powers Marketing Teams</a> </h5> <div class="benqu-card-meta"> <time datetime="2026-04-10T20:56:15+00:00"> Apr 10, 2026 </time> </div> </div> </div> <div class="benqu-footer-popular-item"> <a class="benqu-card-image" href="https://mediaexposurehub.com/meta-business-suite-grow-your-audience-in-2026/"> <img width="300" height="200" src="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/04/meta-business-suite-grow-your-audience-in-2026-featured-300x200.webp?v=1777581661" class="attachment-satellite-thumb size-satellite-thumb wp-post-image" alt="" decoding="async" loading="lazy" srcset="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/04/meta-business-suite-grow-your-audience-in-2026-featured-300x200.webp 300w, https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/04/meta-business-suite-grow-your-audience-in-2026-featured-600x400.webp 600w" sizes="auto, (max-width: 300px) 100vw, 300px" /> </a> <div> <h5 class="benqu-card-title"> <a href="https://mediaexposurehub.com/meta-business-suite-grow-your-audience-in-2026/">Meta Business Suite: Grow Your Audience in 2026</a> </h5> <div class="benqu-card-meta"> <time datetime="2026-04-28T09:36:00+00:00"> Apr 28, 2026 </time> </div> </div> </div> <div class="benqu-footer-popular-item"> <a class="benqu-card-image" href="https://mediaexposurehub.com/musician-marketing-real-2026-success-stories/"> <img width="300" height="200" src="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/03/musician-marketing-real-2026-success-stories-featured-300x200.webp?v=1773765335" class="attachment-satellite-thumb size-satellite-thumb wp-post-image" alt="" decoding="async" loading="lazy" srcset="https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/03/musician-marketing-real-2026-success-stories-featured-300x200.webp?v=1773765335 300w, https://mediaexposurehub.com/wp-content/uploads/sites/54/2026/03/musician-marketing-real-2026-success-stories-featured-600x400.webp?v=1773765335 600w" sizes="auto, (max-width: 300px) 100vw, 300px" /> </a> <div> <h5 class="benqu-card-title"> <a href="https://mediaexposurehub.com/musician-marketing-real-2026-success-stories/">Musician Marketing: Real 2026 Success Stories</a> </h5> <div class="benqu-card-meta"> <time datetime="2026-03-17T16:35:36+00:00"> Mar 17, 2026 </time> </div> </div> </div> </div> </div> <div> <h4>Our Authors</h4> <ul class="benqu-footer-authors"> <li><a href="https://mediaexposurehub.com/author/mediaexpjamilaadebayo/">Devin Dominguez</a></li> <li><a href="https://mediaexposurehub.com/author/mediaexpramonvega/">Diana Yoder</a></li> <li><a href="https://mediaexposurehub.com/author/mediaexppriyavaswani/">Priya Vaswani</a></li> <li><a href="https://mediaexposurehub.com/author/mediaexpsunitadesai/">Brian Watson</a></li> <li><a href="https://mediaexposurehub.com/author/mediaexpmayavaldez/">Dominique Turner</a></li> <li><a href="https://mediaexposurehub.com/author/mediaexplenachen/">Dillon Cooper</a></li> <li><a href="https://mediaexposurehub.com/author/mediaexpanikachauhan/">Anika Chauhan</a></li> <li><a href="https://mediaexposurehub.com/author/mediaexphannahtaylor/">Ashley Wells</a></li> <li><a href="https://mediaexposurehub.com/author/mediaexpkaikimura/">Diana Moore</a></li> <li><a href="https://mediaexposurehub.com/author/mediaexpjaviermontoya/">Desiree Garrison</a></li> <li><a href="https://mediaexposurehub.com/author/mediaexpaishakhan/">Diane Kelly</a></li> <li><a href="https://mediaexposurehub.com/author/mediaexpkeishawashington/">Devin Lee</a></li> <li><a href="https://mediaexposurehub.com/author/mediaexpalicesmith/">Ashley Shields</a></li> <li><a href="https://mediaexposurehub.com/author/mediaexpzarachen/">Diamond Quinn</a></li> <li><a href="https://mediaexposurehub.com/author/mediaexpfionadavis/">Ashley Valenzuela</a></li> <li><a href="https://mediaexposurehub.com/author/mediaexpmorganreed/">Desiree Smith</a></li> </ul> </div> </div> <div class="benqu-footer-bottom"> <div>© 2026 <strong>Media Exposure Hub</strong>. All rights reserved.</div> <nav class="benqu-footer-nav"> </nav> </div> </div> </footer> <script> (function(){ // Even without a sticky CTA, the nav "Free Consultation" button (if // rendered) still needs to trigger the existing lead-popup path. document.querySelectorAll('[data-benqu-cta-trigger]').forEach(function(t){ t.addEventListener('click', function (e) { e.preventDefault(); try { if (window.LawyerLeadFormV2 && window.LawyerLeadFormV2.open) window.LawyerLeadFormV2.open('benqu_cta'); else if (window.AeoLeadPopup && window.AeoLeadPopup.open) window.AeoLeadPopup.open('benqu_cta'); else if (window.ExitIntentPopup && window.ExitIntentPopup.open) window.ExitIntentPopup.open('benqu_cta'); else document.dispatchEvent(new CustomEvent('satellite:open-lead-popup', {detail: {source: 'benqu_cta'}})); } catch(err){} }); }); })(); </script> <script id="sat-tts-script"> (function(){ if (!('speechSynthesis' in window) || !('SpeechSynthesisUtterance' in window)) { document.querySelectorAll('.sat-tts-player').forEach(function(el){ el.setAttribute('data-state', 'unsupported'); var t = el.querySelector('.sat-tts-title-text'); var i18n = el.querySelector('.sat-tts-i18n'); if (t && i18n) t.textContent = i18n.dataset.unsupported; }); return; } var player = document.querySelector('.sat-tts-player[data-sat-tts]'); if (!player) return; var contentRoot = document.querySelector('.post-content') || document.querySelector('article .single-post-main') || document.querySelector('article'); if (!contentRoot) return; var toggleBtn = player.querySelector('.sat-tts-toggle'); var rateBtn = player.querySelector('.sat-tts-rate'); var titleText = player.querySelector('.sat-tts-title-text'); var progressEl = player.querySelector('.sat-tts-progress-fill'); var i18n = player.querySelector('.sat-tts-i18n'); var STORAGE_KEY = 'sat_tts_v1_' + (location.pathname || '/'); var RATE_CYCLE = [1, 1.25, 1.5, 2, 0.85]; // ── Build chunks ───────────────────────────────────────────── // Strip HTML to a clean, sequential text array. We skip elements // that read awkwardly aloud (figures, embedded video/audio, the // related-callout sidebars, FAQ schema-heavy sections, code). function buildChunks(root) { var clone = root.cloneNode(true); // Remove things we never want spoken. clone.querySelectorAll( 'script,style,figure,iframe,video,audio,svg,noscript,' + 'aside,.related-callout,.sidebar-share,.sat-tts-player,' + '.article-feedback,.author-bio,.post-tags,.read-next-bar,' + '.post-navigation,.related-posts,form,nav,.toc-container' ).forEach(function(n){ n.parentNode && n.parentNode.removeChild(n); }); var blockSel = 'h1,h2,h3,h4,h5,h6,p,li,blockquote,td,th,dt,dd'; var blocks = clone.querySelectorAll(blockSel); var chunks = []; blocks.forEach(function(b){ var t = (b.textContent || '').replace(/\s+/g, ' ').trim(); if (!t) return; // Long paragraphs: break on sentence boundaries so Chrome // doesn't silently drop after ~15s of a single utterance. if (t.length > 220) { var sentences = t.match(/[^.!?]+[.!?]+(?:\s|$)|[^.!?]+$/g) || [t]; var buf = ''; sentences.forEach(function(s){ s = s.trim(); if (!s) return; if ((buf + ' ' + s).trim().length > 220 && buf) { chunks.push(buf.trim()); buf = s; } else { buf = (buf ? buf + ' ' : '') + s; } }); if (buf.trim()) chunks.push(buf.trim()); } else { chunks.push(t); } }); return chunks; } var chunks = buildChunks(contentRoot); if (chunks.length === 0) return; var totalChars = chunks.reduce(function(a,c){ return a + c.length; }, 0); // ── State machine ─────────────────────────────────────────── var state = { playing: false, paused: false, chunkIndex: 0, charsSpoken: 0, rate: 1, voice: null, currentUtter: null, }; try { var saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || 'null'); if (saved && typeof saved.chunkIndex === 'number' && saved.chunkIndex < chunks.length) { state.chunkIndex = saved.chunkIndex; state.charsSpoken = saved.charsSpoken || 0; state.rate = saved.rate || 1; } } catch (e) {} applyRate(state.rate); updateProgress(); // ── Voice selection ───────────────────────────────────────── // speechSynthesis populates voices async on most browsers. The // picker: // 1. filters by html lang prefix (en/es/...) // 2. drops novelty/character voices (Apple ships ~30 of them // and they sort BEFORE the natural ones on macOS, which is // why an unguarded pool[0] fallback ends up reading // Spanish articles in Eddy/Flo/Reed instead of Mónica) // 3. ranks survivors by an explicit per-language preference // list of high-quality voices, with a fallback that prefers // `localService` (built-in premium) over remote voices. var NOVELTY_NAME_RE = /^(Albert|Bad News|Bahh|Bells|Boing|Bubbles|Cellos|Deranged|Good News|Hysterical|Pipe Organ|Trinoids|Whisper|Wobble|Zarvox|Eddy|Flo|Grandma|Grandpa|Jester|Junior|Kathy|Organ|Princess|Ralph|Reed|Rocko|Sandy|Shelley|Superstar|Vicki|Victoria|Bahh|Boing|Cellos)\b/i; var PREFERRED_BY_LANG = { es: [ // macOS / iOS premium Spanish voices (best quality) /^M[oó]nica/i, // es-ES, very natural /^Paulina/i, // es-MX, very natural /^Jorge\b/i, // es-ES /^Diego\b/i, // es-AR /^Juan\b/i, // Microsoft Edge / Windows neural Spanish voices /Microsoft.*\b(Elvira|Dalia|Alvaro|Jorge|Helena|Sabina)\b.*Online/i, /Microsoft.*\b(Elvira|Dalia|Alvaro)\b/i, // Google Spanish (Chrome desktop, Android) /^Google\s+espa[ñn]ol(?:\s+de\s+(?:M[eé]xico|Estados Unidos))?$/i, /^Google\s+espa[ñn]ol/i, ], en: [ /^Google\s.*(US|UK|English)/i, /^Microsoft.*(Aria|Jenny|Guy|Ryan|Davis)\b.*Online/i, /^Microsoft.*(Aria|Jenny|Guy|Ryan|Davis)\b/i, /^Samantha$/i, /^Alex$/i, /English/i, ], }; function scoreVoice(v, preferredList) { for (var i = 0; i < preferredList.length; i++) { if (preferredList[i].test(v.name)) { // Earlier list entries score higher. localService gets a // small bump so a built-in voice wins over a remote one // when both match the same pattern. return 1000 - i * 10 + (v.localService ? 1 : 0); } } // No name match — still prefer non-novelty + localService. return (v.localService ? 1 : 0); } function pickVoice() { var voices = speechSynthesis.getVoices() || []; if (voices.length === 0) return null; var lang = (document.documentElement.lang || 'en').toLowerCase().split('-')[0]; var pool = voices.filter(function(v){ return (v.lang || '').toLowerCase().indexOf(lang) === 0; }); if (pool.length === 0) pool = voices; // Strip novelty voices unless that leaves us with nothing. var filtered = pool.filter(function(v){ return !NOVELTY_NAME_RE.test(v.name); }); if (filtered.length > 0) pool = filtered; var preferredList = PREFERRED_BY_LANG[lang] || PREFERRED_BY_LANG.en; var best = null, bestScore = -Infinity; pool.forEach(function(v){ var s = scoreVoice(v, preferredList); if (s > bestScore) { bestScore = s; best = v; } }); return best || pool[0]; } if (typeof speechSynthesis.addEventListener === 'function') { speechSynthesis.addEventListener('voiceschanged', function(){ state.voice = pickVoice(); }); } state.voice = pickVoice(); // ── Utterance queue ───────────────────────────────────────── function speakNext() { if (state.chunkIndex >= chunks.length) { stopAll(true); return; } var u = new SpeechSynthesisUtterance(chunks[state.chunkIndex]); u.rate = state.rate; u.pitch = 1; u.volume = 1; // Setting `lang` explicitly is what triggers the Spanish // synthesis backend on Chrome/Edge (which shipped neural // Spanish voices that aren't always exposed via getVoices // until referenced). When `voice` is set we prefer the // voice's own lang to avoid a mismatch. if (state.voice) { u.voice = state.voice; if (state.voice.lang) u.lang = state.voice.lang; } else { u.lang = (document.documentElement.lang || 'en'); } u.onend = function() { if (!state.playing) return; state.charsSpoken += chunks[state.chunkIndex].length; state.chunkIndex += 1; persist(); updateProgress(); if (state.chunkIndex < chunks.length) { speakNext(); } else { stopAll(true); } }; u.onerror = function(ev) { if (ev && ev.error === 'interrupted') return; state.playing = false; state.paused = false; state.currentUtter = null; renderState(); }; u.onboundary = function(ev) { if (ev && typeof ev.charIndex === 'number') { var partial = state.charsSpoken + Math.min(ev.charIndex, chunks[state.chunkIndex].length); var pct = Math.min(100, (partial / totalChars) * 100); progressEl.style.width = pct + '%'; } }; state.currentUtter = u; speechSynthesis.speak(u); } function play() { if (state.paused) { speechSynthesis.resume(); state.paused = false; state.playing = true; renderState(); return; } // Some browsers leave the queue stuck after a previous error; // cancel before starting fresh. try { speechSynthesis.cancel(); } catch (e) {} state.playing = true; state.paused = false; renderState(); speakNext(); } function pause() { if (!state.playing) return; try { speechSynthesis.pause(); } catch (e) {} state.paused = true; state.playing = false; persist(); renderState(); } function stopAll(reset) { try { speechSynthesis.cancel(); } catch (e) {} state.playing = false; state.paused = false; state.currentUtter = null; if (reset) { state.chunkIndex = 0; state.charsSpoken = 0; progressEl.style.width = '0%'; try { localStorage.removeItem(STORAGE_KEY); } catch (e) {} } renderState(); } // ── Rate cycle ───────────────────────────────────────────── function applyRate(r) { state.rate = r; rateBtn.textContent = r.toFixed(2).replace(/\.?0+$/, '') + 'x'; persist(); } rateBtn.addEventListener('click', function(){ var i = RATE_CYCLE.indexOf(state.rate); var next = RATE_CYCLE[(i + 1) % RATE_CYCLE.length]; applyRate(next); // If currently playing, restart the current chunk at the // new rate (Web Speech doesn't let you change rate mid-utter). if (state.playing && state.currentUtter) { stopAll(false); state.playing = true; renderState(); speakNext(); } }); toggleBtn.addEventListener('click', function(){ if (state.playing) { pause(); } else { play(); } }); // ── Chrome 15-second cutoff workaround ───────────────────── // Chrome stops Web Speech after ~15s of continuous output. We // ping pause+resume every 10s while playing to keep the queue // alive. Harmless on browsers that don't need it. setInterval(function(){ if (state.playing && !state.paused) { try { speechSynthesis.pause(); speechSynthesis.resume(); } catch (e) {} } }, 10000); // ── Persistence + UI ─────────────────────────────────────── function persist() { try { localStorage.setItem(STORAGE_KEY, JSON.stringify({ chunkIndex: state.chunkIndex, charsSpoken: state.charsSpoken, rate: state.rate, })); } catch (e) {} } function updateProgress() { var pct = totalChars ? Math.min(100, (state.charsSpoken / totalChars) * 100) : 0; progressEl.style.width = pct + '%'; } function renderState() { if (!i18n) return; if (state.playing) { player.setAttribute('data-state', 'playing'); titleText.textContent = i18n.dataset.playing; toggleBtn.setAttribute('aria-label', 'Pause'); } else if (state.paused) { player.setAttribute('data-state', 'paused'); titleText.textContent = i18n.dataset.paused; toggleBtn.setAttribute('aria-label', 'Resume'); } else { player.removeAttribute('data-state'); titleText.textContent = i18n.dataset.listen; toggleBtn.setAttribute('aria-label', 'Play'); } } // ── GA4 listen events ───────────────────────────────────── // Fire a small custom event the first time a visitor presses // play, so we can answer "does the listen button move // engagement?" from the analytics dashboard. var firedFirstPlay = false; toggleBtn.addEventListener('click', function(){ if (firedFirstPlay) return; if (typeof window.gtag === 'function') { try { window.gtag('event', 'tts_listen_play', { event_category: 'engagement', event_label: location.pathname, value: 1, }); } catch (e) {} } firedFirstPlay = true; }); // Stop speech when the visitor leaves the page so it doesn't // continue narrating in the background after navigation. window.addEventListener('beforeunload', function(){ try { speechSynthesis.cancel(); } catch (e) {} }); })(); </script> <script type="speculationrules"> {"prefetch":[{"source":"document","where":{"and":[{"href_matches":"/*"},{"not":{"href_matches":["/wp-*.php","/wp-admin/*","/wp-content/uploads/sites/54/*","/wp-content/*","/wp-content/plugins/*","/wp-content/themes/satellite-benqu/*","/*\\?(.+)"]}},{"not":{"selector_matches":"a[rel~=\"nofollow\"]"}},{"not":{"selector_matches":".no-prefetch, .no-prefetch a"}}]},"eagerness":"conservative"}]} </script> <script src="https://mediaexposurehub.com/wp-content/themes/satellite-benqu/assets/main.js?ver=1.3.8" id="satellite-benqu-main-js"></script> <link rel="preconnect" href="https://fonts.googleapis.com" /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> <link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=DM+Mono:wght@400;500&display=swap" onload="this.onload=null;this.rel='stylesheet'"> <noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=DM+Mono:wght@400;500&display=swap"></noscript> <style id="aeo-popup-styles"> #aeo-popup-root { --accent: #E8471A; --accent-dim: #C93C12; --accent-mid: #F07040; --accent-soft: #F9A07A; --card-bg: linear-gradient(105deg, #ffffff 0%, #fff4f0 40%, #ffeee7 70%, #ffffff 100%); --card-fade: #fff8f5; --bar-bg: linear-gradient(105deg, #ffffff 0%, #fff4f0 40%, #ffeee7 70%, #ffffff 100%); --cta-grad: linear-gradient(90deg, #E8471A 0%, #F07040 50%, #E8471A 100%); --cta-text: #ffffff; --shadow-rgb: 232,71,26; --ink: #111111; --ink-soft: #555555; --ink-faint: #999999; --border: #E2E4DC; --banner-h: 88px; /* "Google Sans" is in the stack as the brand fallback for installs that have it locally (e.g. internal Google environments); Inter is the loaded webfont and the de-facto free substitute. */ --font-display: 'Inter', 'Google Sans', system-ui, sans-serif; --font-body: 'Inter', 'Google Sans', system-ui, sans-serif; --font-mono: 'DM Mono', ui-monospace, Menlo, monospace; } #aeo-popup-root, #aeo-popup-root * { box-sizing: border-box; } @keyframes aeoPulse { 0%,100% { opacity:1; transform:scale(1); } 50% { opacity:.4; transform:scale(.7); } } @keyframes aeoCtaShimmer { 0% { background-position:0% 0%; } 50% { background-position:100% 0%; } 100% { background-position:0% 0%; } } @keyframes aeoBannerBreathe{ 0% { background-position:0% 50%; } 50% { background-position:100% 50%; } 100% { background-position:0% 50%; } } @keyframes aeoCardSlideUp { to { transform: translateY(0); opacity: 1; } } @keyframes aeoCardSlideDown{ to { transform: translateY(calc(100% + 32px)); opacity: 0; } } @keyframes aeoBarSlideUp { to { transform: translateY(0); opacity: 1; } } @keyframes aeoScTicker { 0% { transform: translateX(0); } 100% { transform: translateX(-33.3333%); } } /* ── STICKY CARD (bottom-left, initial state) ────────────────────── Default state is hidden -- JS adds .aeo-iframe-ready on the root once the modal iframe has finished loading in the background (or after a safety timeout), so the slide-up coincides with the funnel being click-ready. */ #aeo-popup-root .sticky-card { position: fixed; bottom: 24px; left: 24px; z-index: 9990; width: 340px; border-radius: 16px; overflow: hidden; border: 1px solid var(--border); box-shadow: 0 8px 40px rgba(var(--shadow-rgb),0.15), 0 2px 8px rgba(0,0,0,0.07); background: var(--card-bg); background-size: 300% 300%; transform: translateY(calc(100% + 32px)); opacity: 0; } #aeo-popup-root.aeo-iframe-ready .sticky-card { animation: aeoCardSlideUp 0.6s cubic-bezier(0.22,1,0.36,1) forwards, aeoBannerBreathe 5s ease-in-out 0.6s infinite; } #aeo-popup-root.aeo-skip-card .sticky-card { display: none; } #aeo-popup-root.aeo-lead-done .sticky-card, #aeo-popup-root.aeo-lead-done .sticky-banner { display: none; } #aeo-popup-root .sticky-card.hiding { animation: aeoCardSlideDown 0.4s cubic-bezier(0.4,0,1,1) forwards !important; } /* ── Border Beam ─────────────────────────────────────── A bright accent-coloured "comet" travels around the card perimeter. Implemented as a single ::before pseudo-element: 1. conic-gradient paints the comet (transparent for ~75% of the circle, then a soft -> bright -> soft fade across the last ~100°), 2. mask-composite:exclude cuts the inside out so only a 2px ring remains -- the comet appears to glide along the border, 3. @property animates the gradient's `from` angle so the rotation is on the gradient itself, not via transform (no sub-pixel shimmer, no GPU compositing cost, and the corners stay sharp). Browsers without @property (older Firefox <128, older Safari <16.4) render the gradient statically -- graceful degradation. */ @property --aeo-beam-angle { syntax: '<angle>'; initial-value: 0deg; inherits: false; } #aeo-popup-root .sticky-card::before { content: ''; position: absolute; inset: 0; border-radius: inherit; padding: 2px; background: conic-gradient( from var(--aeo-beam-angle), transparent 0deg, transparent 250deg, var(--accent-soft) 305deg, var(--accent) 340deg, var(--accent-soft) 360deg ); -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor; mask-composite: exclude; pointer-events: none; z-index: 1; opacity: 0; transition: opacity .4s ease; } #aeo-popup-root.aeo-iframe-ready .sticky-card::before { opacity: 1; animation: aeoBeamRotate 5s linear infinite; } @keyframes aeoBeamRotate { to { --aeo-beam-angle: 360deg; } } #aeo-popup-root .sc-inner { padding: 18px 18px 20px; } #aeo-popup-root .sc-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 14px; } #aeo-popup-root .sc-label { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent-dim); display: flex; align-items: center; gap: 6px; } #aeo-popup-root .sc-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); animation: aeoPulse 2s ease-in-out infinite; flex-shrink: 0; } #aeo-popup-root .sc-close { width: 26px; height: 26px; border-radius: 50%; border: 1px solid var(--border); background: transparent; cursor: pointer; display: flex; align-items: center; justify-content: center; color: var(--ink-faint); font-size: 16px; line-height: 1; transition: border-color .2s, color .2s, background .2s; flex-shrink: 0; padding: 0; } #aeo-popup-root .sc-close:hover { border-color: var(--ink); color: var(--ink); background: #F5F6F2; } #aeo-popup-root .sc-headline { font-family: var(--font-display); font-size: 19px; font-weight: 800; color: var(--ink); letter-spacing: -0.4px; line-height: 1.25; margin: 0 0 8px; } #aeo-popup-root .sc-sub { font-size: 13px; color: var(--ink-faint); line-height: 1.5; margin: 0 0 12px; font-family: var(--font-body); } #aeo-popup-root .sc-ticker-wrap { position: relative; overflow: hidden; margin-bottom: 14px; height: 28px; } #aeo-popup-root .sc-ticker-wrap::before, #aeo-popup-root .sc-ticker-wrap::after { content: ''; position: absolute; inset-block: 0; width: 20px; z-index: 2; pointer-events: none; } #aeo-popup-root .sc-ticker-wrap::before { left: 0; background: linear-gradient(to right, var(--card-fade), transparent); } #aeo-popup-root .sc-ticker-wrap::after { right: 0; background: linear-gradient(to left, var(--card-fade), transparent); } #aeo-popup-root .sc-ticker { /* Marquee math (do not change without revisiting both this rule AND the aeoScTicker keyframe + the JS that builds the chip list): - Each chip is 28px wide with an 8px trailing margin = 36px slot. - We render 3 copies of the 6-engine ticker = 18 chips = 648px. - One copy width = 6 * 36 = 216px = exactly 1/3 of the total. - The keyframe translates -33.3333% (= -216px), so the moment the first copy finishes scrolling out, the second copy is already occupying its exact original position. Seamless loop. We use margin-right on the chip (not flex gap) on purpose: flex gap only sits *between* siblings, which is one short for the cycle to line up with translateX. margin-right counts on every chip, so the total width is N*(chip+gap) and -1/N translation hits the boundary cleanly. Three copies is also what keeps the right edge of the card from ever going empty mid-scroll -- a single copy (216px) is narrower than the visible card content area, so doubled content would leave a gap on the right at the end of every cycle. */ display: flex; width: max-content; animation: aeoScTicker 18s linear infinite; align-items: center; height: 100%; } #aeo-popup-root .sc-logo-chip { width: 28px; height: 28px; border-radius: 8px; flex-shrink: 0; margin-right: 8px; display: flex; align-items: center; justify-content: center; overflow: hidden; /* Faint inset border. On dark/coloured chips it's invisible; on the white Copilot chip it's what keeps the icon from floating against the light card background. */ box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06); } #aeo-popup-root .sc-logo-chip img { width: 18px; height: 18px; object-fit: contain; display: block; filter: drop-shadow(0 1px 1px rgba(0,0,0,0.15)); } /* Copilot-specific override: Microsoft's multicolor ribbon mark has finer detail than any of the other engines' marks (which are single- colour silhouettes), and its lighter gradient tips (cyan, yellow, pink) wash out at small sizes on a white chip. Bumping the image to nearly fill the 28px chip restores legibility. We target by [title] so future engine additions don't accidentally pick this up. */ #aeo-popup-root .sc-logo-chip[title="Copilot"] img { width: 24px; height: 24px; /* No drop-shadow on the Copilot icon -- the soft glow muddies the gradient ribbon. */ filter: none; } #aeo-popup-root .sc-cta { display: flex; align-items: center; justify-content: center; gap: 6px; width: 100%; font-family: var(--font-body); font-size: 14px; font-weight: 600; color: var(--cta-text); background: var(--cta-grad); background-size: 200% 100%; border: none; border-radius: 100px; padding: 11px 20px; cursor: pointer; text-decoration: none; animation: aeoCtaShimmer 3s ease-in-out 2.4s infinite; transition: transform .15s, box-shadow .2s; } #aeo-popup-root .sc-cta:hover { transform: scale(1.02); box-shadow: 0 4px 20px rgba(var(--shadow-rgb),0.4); animation-play-state: paused; } #aeo-popup-root .sc-cta .arrow { font-size: 15px; transition: transform .2s; } #aeo-popup-root .sc-cta:hover .arrow { transform: translateX(3px); } /* ── STICKY BAR (after card dismissed) ──────────────────────────── */ #aeo-popup-root .sticky-banner { position: fixed; bottom: 0; left: 0; right: 0; z-index: 9990; transform: translateY(100%); opacity: 0; pointer-events: none; background: var(--bar-bg); background-size: 300% 300%; border-top: 1px solid var(--border); box-shadow: 0 -4px 32px rgba(var(--shadow-rgb),0.08), 0 -1px 0 rgba(var(--shadow-rgb),0.15); } #aeo-popup-root .sticky-banner.visible { pointer-events: all; animation: aeoBarSlideUp 0.5s cubic-bezier(0.22,1,0.36,1) forwards, aeoBannerBreathe 5s ease-in-out 0.5s infinite; } #aeo-popup-root .sticky-banner::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 2px; background: linear-gradient(90deg, var(--accent) 0%, var(--accent-soft) 60%, transparent 100%); } #aeo-popup-root .banner-inner { max-width: 1240px; margin: 0 auto; padding: 0 32px; height: var(--banner-h); display: flex; align-items: center; gap: 24px; } #aeo-popup-root .banner-label { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent-dim); white-space: nowrap; flex-shrink: 0; display: flex; align-items: center; gap: 6px; } #aeo-popup-root .banner-label::before { content: ''; display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); animation: aeoPulse 2s ease-in-out infinite; } #aeo-popup-root .banner-divider-v { width: 1px; height: 32px; background: var(--border); flex-shrink: 0; } #aeo-popup-root .banner-headline { font-family: var(--font-display); font-size: 15px; font-weight: 800; color: var(--ink); letter-spacing: -0.3px; flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin: 0; } #aeo-popup-root .banner-sub { font-size: 13px; color: var(--ink-faint); flex-shrink: 0; white-space: nowrap; font-family: var(--font-body); } #aeo-popup-root .banner-cta { display: inline-flex; align-items: center; gap: 6px; font-family: var(--font-body); font-size: 14px; font-weight: 600; color: var(--cta-text); background: var(--cta-grad); background-size: 200% 100%; border: none; border-radius: 100px; padding: 11px 28px; cursor: pointer; text-decoration: none; white-space: nowrap; flex-shrink: 0; animation: aeoCtaShimmer 3s ease-in-out 0.5s infinite; transition: transform .15s, box-shadow .2s; } #aeo-popup-root .banner-cta:hover { transform: scale(1.03); box-shadow: 0 4px 20px rgba(var(--shadow-rgb),0.4); animation-play-state: paused; } #aeo-popup-root .banner-cta .arrow { font-size: 16px; line-height: 1; transition: transform .2s; } #aeo-popup-root .banner-cta:hover .arrow { transform: translateX(3px); } /* ── IFRAME MODAL (full-screen takeover) ────────────────────────── */ /* Matches the Verdict V2 full-page lead capture pattern: edge-to-edge shell, no card chrome, just a translucent floating top bar with "Back to article" and a close pill. The AEO funnel's own branding inside the iframe carries the visual weight. */ #aeo-popup-root .aeo-modal { position: fixed; inset: 0; z-index: 10000; background: #ffffff; display: flex; flex-direction: column; opacity: 0; pointer-events: none; transition: opacity .25s ease; } #aeo-popup-root .aeo-modal.open { opacity: 1; pointer-events: all; } #aeo-popup-root .aeo-modal-backdrop { display: none; } #aeo-popup-root .aeo-modal-shell { position: relative; z-index: 1; display: flex; flex-direction: column; width: 100%; height: 100%; background: #ffffff; } #aeo-popup-root .aeo-modal-bar { position: relative; z-index: 3; display: flex; align-items: center; justify-content: space-between; padding: 12px 20px; background: #ffffff; border-bottom: 1px solid rgba(0,0,0,0.06); flex-shrink: 0; } #aeo-popup-root .aeo-modal-back { display: inline-flex; align-items: center; gap: 6px; font-family: var(--font-body); font-size: 13px; font-weight: 500; color: var(--ink-soft); background: transparent; border: none; cursor: pointer; padding: 6px 8px; border-radius: 8px; transition: background .15s, color .15s; } #aeo-popup-root .aeo-modal-back:hover { background: #F0F1EC; color: var(--ink); } #aeo-popup-root .aeo-modal-iframe-wrap { position: relative; flex: 1; background: #ffffff; overflow: hidden; } #aeo-popup-root .aeo-modal-iframe { width: 100%; height: 100%; border: 0; display: block; background: #ffffff; } #aeo-popup-root .aeo-modal-loading { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-faint); background: #ffffff; transition: opacity .3s ease; z-index: 1; pointer-events: none; } #aeo-popup-root .aeo-modal-iframe-wrap.loaded .aeo-modal-loading { opacity: 0; } #aeo-popup-root .aeo-modal-loading .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); animation: aeoPulse 1.2s ease-in-out infinite; margin-right: 8px; } /* lock background scroll when modal is open */ body.aeo-modal-open { overflow: hidden; } /* Bar collapses to headline + CTA on narrower viewports, but the card keeps its 340px bottom-left footprint all the way down to phone widths. Real phones (≤480px) get the full-width card so the CTA stays tappable. */ @media (max-width: 900px) { #aeo-popup-root .banner-inner { padding: 0 16px; gap: 12px; } #aeo-popup-root .banner-label, #aeo-popup-root .banner-divider-v, #aeo-popup-root .banner-sub { display: none; } #aeo-popup-root .banner-headline { font-size: 13px; } #aeo-popup-root .banner-cta { font-size: 13px; padding: 9px 18px; } } @media (max-width: 480px) { #aeo-popup-root .sticky-card { left: 12px; bottom: 12px; width: calc(100vw - 24px); } } @media (prefers-reduced-motion: reduce) { #aeo-popup-root .sticky-card, #aeo-popup-root .sticky-banner.visible, #aeo-popup-root .sc-cta, #aeo-popup-root .banner-cta { animation: none !important; } #aeo-popup-root.aeo-iframe-ready .sticky-card { transform: none; opacity: 1; } } </style> <div id="aeo-popup-root" data-variant="small-report" data-domain="mediaexposurehub.com"> <div class="sticky-card" id="aeoStickyCard" role="complementary" aria-label="AI Visibility Radar — Free AEO Report"> <div class="sc-inner"> <div class="sc-header"> <span class="sc-label"><span class="sc-dot"></span>AI VISIBILITY RADAR</span> <button type="button" class="sc-close" id="aeoCardClose" aria-label="Dismiss">×</button> </div> <p class="sc-headline">Are AI engines recommending your brand?</p> <p class="sc-sub">Check your score on 5 AI engines — instantly.</p> <div class="sc-ticker-wrap"><div class="sc-ticker" id="aeoScTicker"></div></div> <a href="#" class="sc-cta" id="aeoCardCta"> Check for Free <span class="arrow">→</span> </a> </div> </div> <div class="sticky-banner" id="aeoStickyBanner" role="complementary" aria-label="AI Visibility Radar — Free AEO Report"> <div class="banner-inner"> <span class="banner-label">AI VISIBILITY RADAR</span> <div class="banner-divider-v"></div> <p class="banner-headline">Are AI engines recommending your brand?</p> <span class="banner-sub">Check your score on 5 AI engines — instantly.</span> <a href="#" class="banner-cta" id="aeoBarCta"> Check for Free <span class="arrow">→</span> </a> </div> </div> <div class="aeo-modal" id="aeoModal" role="dialog" aria-modal="true" aria-label="AI Visibility Radar — Free AEO Report" aria-hidden="true"> <div class="aeo-modal-backdrop" data-aeo-close></div> <div class="aeo-modal-shell"> <header class="aeo-modal-bar"> <button type="button" class="aeo-modal-back" data-aeo-close> <span aria-hidden="true">←</span> Back to article </button> </header> <div class="aeo-modal-iframe-wrap" id="aeoModalIframeWrap"> <div class="aeo-modal-loading"><span class="dot"></span>Loading report…</div> <iframe id="aeoModalIframe" class="aeo-modal-iframe" title="AI Visibility Radar — Free AEO Report" referrerpolicy="no-referrer-when-downgrade" allow="clipboard-write" src="https://aeo.prod-mobtools.com/aeo/small-report?embed=1&utm_source=mediaexposurehub.com&utm_medium=satellite&utm_campaign=aeo-popup&utm_content=small-report"></iframe> </div> </div> </div> </div> <script id="aeo-popup-script"> (function () { var root = document.getElementById('aeo-popup-root'); if (!root) return; var card = document.getElementById('aeoStickyCard'); var bar = document.getElementById('aeoStickyBanner'); var modal = document.getElementById('aeoModal'); var iframe = document.getElementById('aeoModalIframe'); var wrap = document.getElementById('aeoModalIframeWrap'); var closeBtn = document.getElementById('aeoCardClose'); var cardCta = document.getElementById('aeoCardCta'); var barCta = document.getElementById('aeoBarCta'); var ticker = document.getElementById('aeoScTicker'); /* ---------- analytics -------------------------------- Posts to the existing /wp-json/satellite/v1/lead-event proxy, which forwards to the backend's POST /api/leads/event. Events here are prefixed with "aeo_" so the lawyer funnel chart on the same dashboard never picks them up. ----------------------------------------------------- */ var AEO_DOMAIN = "mediaexposurehub.com"; var AEO_VARIANT = "small-report"; var AEO_NICHE = "marketing"; var AEO_EVENT_URL = "https:\/\/mediaexposurehub.com\/wp-json\/satellite\/v1\/lead-event"; var AEO_EVENT_NONCE = "06431426ab"; /* localStorage visitor_id so unique-clicker counts on the dashboard are stable across sessions on the same browser. UUIDv4-ish via random bytes; no PII, just a stable handle for COUNT(DISTINCT) on the backend. */ function aeoGetVisitorId() { try { var KEY = 'aeo_vid'; var existing = localStorage.getItem(KEY); if (existing) return existing; var b = (crypto && crypto.getRandomValues) ? crypto.getRandomValues(new Uint8Array(16)) : (function () { var arr = []; for (var i=0;i<16;i++) arr.push(Math.floor(Math.random()*256)); return arr; })(); b[6] = (b[6] & 0x0f) | 0x40; // version 4 b[8] = (b[8] & 0x3f) | 0x80; // variant var hex = Array.prototype.map.call(b, function (x) { return ('0' + x.toString(16)).slice(-2); }).join(''); var id = hex.slice(0,8)+'-'+hex.slice(8,12)+'-'+hex.slice(12,16)+'-'+hex.slice(16,20)+'-'+hex.slice(20,32); localStorage.setItem(KEY, id); return id; } catch (e) { return ''; // private mode / disabled storage -- silently drop } } var AEO_VISITOR_ID = aeoGetVisitorId(); function aeoTrack(eventType, extraMeta) { try { var meta = { variant: AEO_VARIANT, niche: AEO_NICHE }; if (extraMeta) for (var k in extraMeta) if (Object.prototype.hasOwnProperty.call(extraMeta, k)) meta[k] = extraMeta[k]; var payload = { domain: AEO_DOMAIN, event_type: eventType, visitor_id: AEO_VISITOR_ID, page_url: location.href, metadata_json: JSON.stringify(meta) }; var body = JSON.stringify(payload); if (navigator.sendBeacon) { navigator.sendBeacon( AEO_EVENT_URL + '?_wpnonce=' + encodeURIComponent(AEO_EVENT_NONCE), new Blob([body], { type: 'application/json' }) ); } else { fetch(AEO_EVENT_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': AEO_EVENT_NONCE }, body: body, keepalive: true }); } } catch (e) { /* analytics must never break the popup */ } } /* ---------- cookies ---------- */ function readCookie(name) { var m = document.cookie.match('(?:^|;)\\s*' + name + '=([^;]*)'); return m ? decodeURIComponent(m[1]) : ''; } function writeCookie(name, value, days) { var d = new Date(); d.setTime(d.getTime() + days * 24 * 60 * 60 * 1000); document.cookie = name + '=' + encodeURIComponent(value) + '; expires=' + d.toUTCString() + '; path=/; SameSite=Lax'; } /* Cookie names are versioned. An earlier iteration of this script set aeo_lead_done from a 15s dwell-time heuristic (since removed); any tester who left the modal open during that period now carries a 30-day cookie that silently hides the popup forever. Bumping the name to _v2 retires that stale state without forcing anyone to clear browser cookies. */ var CARD_DISMISSED = 'aeo_card_dismissed_v2'; var LEAD_DONE = 'aeo_lead_done_v2'; if (readCookie(LEAD_DONE) === '1') { root.classList.add('aeo-lead-done'); return; } /* ---------- reveal the card (or bar) ---------- The iframe is preloading in the background regardless of card state, so we don't gate reveal on it -- we just slide the card up on a quick fixed timer so the popup is visible reliably on every refresh, even when the cross-origin iframe's load event is slow or never fires. By the time the user finishes reading the article and clicks the CTA, the iframe is virtually always fully loaded, so clicks still open the modal instantly. */ var revealed = false; var dismissedBefore = readCookie(CARD_DISMISSED) === '1'; if (dismissedBefore) root.classList.add('aeo-skip-card'); function reveal() { if (revealed) return; revealed = true; if (dismissedBefore) { if (bar) bar.classList.add('visible'); aeoTrack('aeo_bar_impression', { source: 'bar' }); } else { root.classList.add('aeo-iframe-ready'); aeoTrack('aeo_impression', { source: 'card' }); } } /* 600ms is long enough to feel intentional (not jarring on first paint), short enough that it always wins against a slow cross-origin iframe. */ setTimeout(reveal, 600); /* ---------- ticker ---------- Three full copies of the chip list, not two. The aeoScTicker keyframe translates by exactly one copy's width (-33.3333%) so the second copy slides into the first's position seamlessly; the third copy is there to keep the right edge of the card filled while the second is doing the heavy lifting -- one copy alone (216px) is narrower than the visible card content area, so a doubled ticker leaves a visible gap at the right edge near the end of every animation cycle. */ var TOOLS = [{"name":"Gemini","bg":"#1A73E8","logo":"https:\/\/mediaexposurehub.com\/wp-content\/mu-plugins\/aeo-assets\/logos\/gemini.svg"},{"name":"Grok","bg":"#111111","logo":"https:\/\/mediaexposurehub.com\/wp-content\/mu-plugins\/aeo-assets\/logos\/grok.svg"},{"name":"Perplexity","bg":"#1FB8CD","logo":"https:\/\/mediaexposurehub.com\/wp-content\/mu-plugins\/aeo-assets\/logos\/perplexity.svg"},{"name":"ChatGPT","bg":"#10A37F","logo":"https:\/\/mediaexposurehub.com\/wp-content\/mu-plugins\/aeo-assets\/logos\/chatgpt.svg"},{"name":"Copilot","bg":"#FFFFFF","logo":"https:\/\/mediaexposurehub.com\/wp-content\/mu-plugins\/aeo-assets\/logos\/copilot.svg"},{"name":"Claude","bg":"#D97757","logo":"https:\/\/mediaexposurehub.com\/wp-content\/mu-plugins\/aeo-assets\/logos\/claude.svg"}]; if (ticker) { var tripled = TOOLS.concat(TOOLS).concat(TOOLS); ticker.innerHTML = tripled.map(function (t) { return '<div class="sc-logo-chip" title="' + t.name + '" style="background:' + t.bg + ';">' + '<img src="' + t.logo + '" alt="' + t.name + '" loading="lazy" width="18" height="18">' + '</div>'; }).join(''); } /* ---------- card -> bar dismissal ---------- The slide-down animation is 0.4s. We use a matched setTimeout instead of animationend because Chrome occasionally swallows the event when another animation (the breathing background) is in flight on the same element. */ if (closeBtn) { closeBtn.addEventListener('click', function () { writeCookie(CARD_DISMISSED, '1', 30); aeoTrack('aeo_card_dismiss', { source: 'card' }); card.classList.add('hiding'); setTimeout(function () { card.style.display = 'none'; if (bar) bar.classList.add('visible'); }, 420); }); } /* ---------- modal open / close ---------- */ if (iframe && wrap) { if (iframe.complete) wrap.classList.add('loaded'); else iframe.addEventListener('load', function () { wrap.classList.add('loaded'); }, { once: true }); } function swapCardToBar() { if (!card || card.style.display === 'none' || card.classList.contains('hiding')) return; card.classList.add('hiding'); setTimeout(function () { card.style.display = 'none'; if (bar) bar.classList.add('visible'); }, 420); } function openModal(e) { if (e) e.preventDefault(); if (!modal) return; modal.setAttribute('aria-hidden', 'false'); requestAnimationFrame(function () { modal.classList.add('open'); }); document.body.classList.add('aeo-modal-open'); } function closeModal() { if (!modal || !modal.classList.contains('open')) return; modal.classList.remove('open'); document.body.classList.remove('aeo-modal-open'); setTimeout(function () { modal.setAttribute('aria-hidden', 'true'); }, 320); aeoTrack('aeo_modal_close'); /* If the card was still showing behind the modal, collapse it to the bar so the user has an obvious re-entry point. No cookie is set here -- only the explicit X on the card persists dismissal across page loads. */ swapCardToBar(); } if (cardCta) cardCta.addEventListener('click', function (e) { aeoTrack('aeo_cta_click', { source: 'card' }); openModal(e); }); if (barCta) barCta.addEventListener('click', function (e) { aeoTrack('aeo_cta_click', { source: 'bar' }); openModal(e); }); /* Generic trigger: any element with [data-aeo-open] opens the same modal. The in-article banner (aeo-inline-banner.php) uses this so it doesn't have to duplicate the modal HTML/JS -- it just renders a CTA with data-aeo-open + data-aeo-source="inline" and rides on the popup infrastructure that's already in the footer. Source is read from data-aeo-source so we can split CTR by surface in the analytics dashboard (card / bar / inline / future surfaces). */ document.querySelectorAll('[data-aeo-open]').forEach(function (el) { el.addEventListener('click', function (e) { var source = el.getAttribute('data-aeo-source') || 'external'; aeoTrack('aeo_cta_click', { source: source }); openModal(e); }); }); if (modal) { modal.querySelectorAll('[data-aeo-close]').forEach(function (el) { el.addEventListener('click', closeModal); }); } document.addEventListener('keydown', function (e) { if (e.key === 'Escape' && modal && modal.classList.contains('open')) closeModal(); }); /* ---------- future postMessage handshake (stub) ---------- Listens for a "lead-complete" event from the iframe so we can flip the LEAD_DONE cookie immediately on real submission (not on dwell time). Wire this up on the AEO side and remove the dwell-time fallback above. */ window.addEventListener('message', function (e) { if (!e || !e.origin || e.origin.indexOf('aeo.prod-mobtools.com') === -1) return; var data = e.data || {}; if (data && data.type === 'aeo:lead-complete') { writeCookie(LEAD_DONE, '1', 30); root.classList.add('aeo-lead-done'); closeModal(); } }); })(); </script> <script id="satellite-cta-fallback">(function(){ function ready(fn){ if(document.readyState!=='loading'){fn();} else {document.addEventListener('DOMContentLoaded',fn);} } ready(function(){ document.querySelectorAll('[class*="-sticky-cta"]').forEach(function(el){ var b = el.querySelector('[class*="-sticky-cta-btn"]'); if (b && /subscribe/i.test(b.textContent)) { el.remove(); } }); }); document.addEventListener('satellite:open-lead-popup', function(){ var has = (window.LawyerLeadFormV2&&window.LawyerLeadFormV2.open)||(window.AeoLeadPopup&&window.AeoLeadPopup.open)||(window.ExitIntentPopup&&window.ExitIntentPopup.open); if (!has) { window.location.href = 'https://mediaexposurehub.com/contact/'; } }); })();</script> <script> (function(){ var bar = document.getElementById('reading-progress'); if(!bar) return; var article = document.querySelector('.post-content'); if(!article) return; var milestones = {25:false,50:false,75:false,100:false}; var startTime = Date.now(); function sendGA4(name, params){ if (window.gtag) window.gtag('event', name, params); } window.addEventListener('scroll', function(){ var rect = article.getBoundingClientRect(); var total = article.offsetHeight - window.innerHeight; var progress = Math.min(100, Math.max(0, (-rect.top / total) * 100)); bar.style.width = progress + '%'; var pct = Math.floor(progress); [25,50,75,100].forEach(function(m){ if (pct >= m && !milestones[m]) { milestones[m] = true; sendGA4('scroll_depth', {percent: m, reading_seconds: Math.round((Date.now()-startTime)/1000)}); } }); }, {passive:true}); function sendReadingTime(){ var seconds = Math.round((Date.now()-startTime)/1000); if (seconds > 3) sendGA4('reading_time', {seconds: seconds}); } document.addEventListener('visibilitychange', function(){ if(document.visibilityState==='hidden') sendReadingTime(); }); window.addEventListener('beforeunload', sendReadingTime); })(); </script> <script> (function(){ var toc = document.querySelector('.toc-container'); if(!toc) return; var title = toc.querySelector('.toc-title'); if(window.innerWidth <= 768 && title) { toc.classList.add('toc-collapsed'); title.addEventListener('click', function(){ toc.classList.toggle('toc-collapsed'); }); } var links = toc.querySelectorAll('.toc-list a'); var sections = []; links.forEach(function(a){ var id = a.getAttribute('href'); if(id) { var el = document.querySelector(id); if(el) sections.push({el:el,link:a}); } }); if(!sections.length) return; var raf; window.addEventListener('scroll', function(){ if(raf) return; raf = requestAnimationFrame(function(){ raf = null; var scrollY = window.scrollY + 120; var active = sections[0]; for(var i=0;i<sections.length;i++){ if(sections[i].el.offsetTop <= scrollY) active = sections[i]; } links.forEach(function(l){ l.classList.remove('toc-active'); }); if(active) active.link.classList.add('toc-active'); }); }, {passive:true}); })(); </script> </body> </html>