What does Google say about SEO? /
Quick SEO Quiz

Test your SEO knowledge in 5 questions

Less than a minute. Find out how much you really know about Google search.

🕒 ~1 min 🎯 5 questions

Official statement

No matter what technology is used (JSON-LD, microdata), you must ensure that the same content is provided to Googlebot and your users. Follow the specific guidelines for news in the product forums to optimize your visibility in Google News.
22:24
🎥 Source video

Extracted from a Google Search Central video

⏱ 53:37 💬 EN 📅 13/10/2016 ✂ 10 statements
Watch on YouTube (22:24) →
Other statements from this video 9
  1. 1:35 Les méta descriptions ont-elles encore un impact réel sur le SEO ?
  2. 2:40 L'ancre de lien est-elle vraiment prioritaire pour le crawl et le positionnement ?
  3. 4:13 Faut-il vraiment traduire les meta descriptions en hindi pour ranker sur Google India ?
  4. 5:33 Pourquoi les ancres « Cliquez ici » nuisent-elles vraiment à votre SEO ?
  5. 10:05 Les outils AdWords sont-ils vraiment adaptés à la recherche de mots-clés SEO en hindi ?
  6. 15:06 Les mots-clés longue traîne améliorent-ils vraiment la pertinence SEO ?
  7. 20:03 Faut-il adapter son contenu SEO à la langue que l'audience tape, même si ce n'est pas celle qu'elle parle ?
  8. 23:31 Les balises alt impactent-elles vraiment le classement organique de vos pages ?
  9. 50:34 Les PBN sont-ils vraiment détectés par Google ou peut-on encore passer entre les mailles ?
📅
Official statement from (9 years ago)
TL;DR

Google requires that the Schema.org marked content is strictly identical to what users can see, regardless of the syntax used. This rule aims to prevent manipulations where rich data misrepresent the actual content of the page. For news, Google refers to specific guidelines in its product forums, suggesting that the eligibility criteria for Google News go far beyond simple technical markup.

What you need to understand

What does this requirement for consistency really entail?

Google mandates that any structured data inserted into your HTML code must accurately reflect what the user sees on their screen. If your Schema.org Article markup indicates a title, publication date, author, or summary, these elements must exactly match what appears in the body of the page.

This rule applies regardless of the chosen syntax: JSON-LD, microdata, RDFa. Google treats these three formats equally. The issue is not the format, but the accuracy of the metadata.

Why is there such an insistence on consistency between content and markup?

For a long time, Google has fought against abuses where publishers inserted fanciful information into their structured data to artificially inflate their visibility in rich snippets. A flashy title in Schema.org that's invisible to the reader, an unjustified 5-star rating, a fictitious author: these are manipulations the algorithm is trying to detect.

For news, the stakes are twofold. On one hand, ensuring that rich results (especially in Google News) display reliable information. On the other, preventing non-journalistic sites from passing themselves off as legitimate media through deceptive markup.

Where can I find these specific guidelines for news?

Google remains deliberately vague in this statement. It directs users to 'the product forums' without a direct link or centralized documentation. In practice, you need to search in the Publisher Center Help and the official Google News guidelines.

These resources specify the eligibility criteria for Google News: publication frequency, editorial expertise, author transparency, adherence to E-E-A-T. Markup alone is never enough. A site can have perfect Schema and still remain invisible in Google News if it does not meet these qualitative conditions.

  • Strict matching: each structured data point must have its visible equivalent on the page.
  • Format indifferent: JSON-LD, microdata, or RDFa, Google treats all these formats the same way if the content is identical.
  • Priority on editorial guidelines: technical markup never compensates for a deficit in editorial quality or authority for Google News.
  • Scattered documentation: specific guidelines for news are not centralized in one guide but spread across Search Central, Publisher Center, and forums.
  • Vigilance against abuses: Google closely monitors discrepancies between metadata and actual content, especially for media.

SEO Expert opinion

Is this guideline really enforced by Google's algorithm?

Yes, but with important nuances depending on the type of content. For news articles, Google has indeed strengthened its checks after massive abuses in rich snippets. Cases of manual penalties for discrepancies between content and markup are documented, particularly through messages in Search Console.

However, automatic detection remains imperfect in certain edge cases. A site may occasionally get away with minor discrepancies (synonyms, slight rewording) without immediate sanction. But pushing this limit is risky: algorithm updates regularly correct these vulnerabilities. [To verify]: Google has never published a precise tolerance threshold for semantic discrepancies.

Why doesn't Google provide centralized documentation for news?

Good question. Google's strategy is to maintain a degree of opacity about the eligibility criteria for Google News to avoid over-optimization. By dispersing the guidelines across multiple sources (Search Central, Publisher Center, forums), Google complicates the task for sites that are just trying to check boxes without real editorial substance.

This is frustrating for SEO practitioners, but consistent with the E-E-A-T philosophy. Google wants publishers to focus on journalistic quality rather than technical compliance. Schema.org markup is merely a facilitator, never a ranking accelerator in itself.

Should JSON-LD be prioritized for news just like elsewhere?

In practice, JSON-LD has emerged as the de facto standard for 95% of media implementations. Not because Google technically favors it (it does not), but because it simplifies maintenance and validation with tools like the Rich Results Test.

Microdata remains valid but weighs down HTML and complicates CMS migrations. RDFa is nearly abandoned in French-speaking news. If you are starting from scratch, JSON-LD is the rational choice: easier to debug, simpler to generate dynamically, and with fewer risks of errors during template redesigns.

Warning: Some CMSs automatically insert Schema.org without your knowledge. Ensure that WordPress plugins (Yoast, Rank Math) or Drupal modules do not generate contradictory structured data with your custom markup. Google hates duplicates and inconsistencies between multiple JSON-LD blocks on the same page.

Practical impact and recommendations

How can I check that my markup adheres to this consistency rule?

First validate using Google's Rich Results Test, but don't stop there. This tool detects syntax errors and missing properties, but not always semantic discrepancies between your JSON-LD and the visible content.

Next, conduct a simple human test: display your page in private browsing mode, manually extract the title, author, date, and summary from the visible DOM. Compare line by line with your JSON-LD. If a bot were to scrape these two sources separately, would it obtain exactly the same information?

What technical errors most often lead to these discrepancies?

Poorly configured dynamic templates top the list. Your CMS generates an optimized SEO title in the tag, a different display title in the <h1>, and your JSON-LD pulls yet another version from a custom field. Result: three versions of the same title, and Google is left unsure which one to trust.</p><p>The second frequent pitfall: <strong>publication dates vs update dates</strong>. Your article displays 'Updated on March 15' at the top of the page, but your Schema.org still indicates the initial publication date (January 12) in datePublished without specifying dateModified. Google sees this as an inconsistency.</p><h3>What strategy should be adopted specifically for Google News?</h3><p>Beyond markup, focus on the <strong>editorial signals</strong> that Google values: clear bylines with links to author pages, detailed 'About' sections, transparent legal mentions, and visible editorial contact. The Schema.org Author and Publisher should point to real and verifiable entities.</p><p>Also, use <strong>NewsArticle rather than Article</strong> for your news content. Systematically provide the properties headline, image, datePublished, dateModified, author (with name and url), and publisher (with name and logo). These metadata condition the eligibility for mobile rich results and Google Discover.</p><ul class="checklist"><li>Audit all article templates to verify the visible title matches the Schema headline</li><li>Implement NewsArticle with minimal properties (headline, image, datePublished, author, publisher)</li><li>Always add dateModified when an article is updated, and display this date on the front</li><li>Validate with Rich Results Test + manual check of the visible rendering vs JSON-LD</li><li>Check that SEO plugins do not generate duplicate or contradictory Schema.org</li><li>Create structured author pages with Schema Person to strengthen editorial authority</li></ul><div class="summary">The consistency between visible content and structured data is non-negotiable for Google, especially in news. Technically, it can be verified with automated tools, but a manual check remains essential to avoid subtle semantic discrepancies. For Google News, perfect markup never replaces quality editorial signals: transparency, expertise, authority. If these intertwined optimizations (technical, editorial, structural) seem complex to orchestrate, an SEO agency specialized in media can assist you in auditing your current implementation and building a coherent strategy across all these dimensions.</div></div> </div> <!-- FAQ --> <div class="section-card"> <h2>❓ Frequently Asked Questions</h2> <div class="faq-list"> <details class="faq-item"> <summary class="faq-question">Google pénalise-t-il vraiment les sites dont le Schema.org diverge du contenu visible ?</summary> <div class="faq-answer">Oui, mais souvent via une simple perte d'éligibilité aux rich snippets plutôt qu'une pénalité de ranking classique. Dans les cas extrêmes (manipulation flagrante), des actions manuelles peuvent survenir, notifiées dans Search Console.</div> </details> <details class="faq-item"> <summary class="faq-question">Faut-il absolument utiliser JSON-LD pour la presse ou microdata fonctionne-t-il aussi bien ?</summary> <div class="faq-answer">Les deux formats sont techniquement équivalents pour Google. JSON-LD est simplement plus pratique à maintenir et à valider. Si votre CMS gère déjà du microdata propre, inutile de tout migrer, sauf si vous rencontrez des bugs récurrents.</div> </details> <details class="faq-item"> <summary class="faq-question">Où trouver les fameuses directives spécifiques pour la presse mentionnées par Google ?</summary> <div class="faq-answer">Google ne fournit pas un seul document centralisé. Consultez le Publisher Center Help, les Google News Content Policies, et les discussions dans le Google Search Central Help Community. La documentation est volontairement fragmentée.</div> </details> <details class="faq-item"> <summary class="faq-question">Peut-on avoir plusieurs blocs JSON-LD sur une même page article sans risque ?</summary> <div class="faq-answer">Oui, à condition que ces blocs ne se contredisent pas. Vous pouvez par exemple combiner NewsArticle, BreadcrumbList et Organization. Mais deux blocs NewsArticle avec des headlines différentes déclencheront une alerte.</div> </details> <details class="faq-item"> <summary class="faq-question">Le balisage Schema.org suffit-il pour apparaître dans Google Actualités ?</summary> <div class="faq-answer">Non, absolument pas. Google Actualités évalue d'abord la qualité éditoriale, la fréquence de publication, l'expertise des auteurs, et la transparence du site. Le balisage technique est nécessaire mais jamais suffisant.</div> </details> </div> </div> <!-- Tags SEO --> <div class="seo-tags-block"> <span class="seo-tags-label">🏷 Related Topics</span> <div class="seo-tags"> <a href="/en/?q=structured+data" class="seo-tag-pill">structured data</a> <a href="/en/?q=schema.org" class="seo-tag-pill">schema.org</a> <a href="/en/?q=JSON-LD" class="seo-tag-pill">JSON-LD</a> <a href="/en/?q=Google+Actualit%C3%A9s" class="seo-tag-pill">Google Actualités</a> <a href="/en/?q=rich+snippets" class="seo-tag-pill">rich snippets</a> <a href="/en/?q=NewsArticle" class="seo-tag-pill">NewsArticle</a> <a href="/en/?q=E-E-A-T" class="seo-tag-pill">E-E-A-T</a> <a href="/en/?q=concordance+contenu" class="seo-tag-pill">concordance contenu</a> </div> </div> <!-- Categories --> <div class="declaration-tags"> <a href="/en/c/contenu" class="tag" style="background: #10b98120; color: #10b981;">Content</a> <a href="/en/c/crawl-indexation" class="tag" style="background: #3b82f620; color: #3b82f6;">Crawl & Indexing</a> <a href="/en/c/donnees-structurees" class="tag" style="background: #ec489920; color: #ec4899;">Structured Data</a> <a href="/en/c/e-commerce" class="tag" style="background: #84cc1620; color: #84cc16;">E-commerce</a> <a href="/en/c/javascript-technique" class="tag" style="background: #eab30820; color: #eab308;">JavaScript & Technical SEO</a> <a href="/en/c/pagination-structure" class="tag" style="background: #64748b20; color: #64748b;">Pagination & Structure</a> </div> <!-- Syntheses thematiques liees --> <!-- Autres declarations de la meme video YouTube --> <div class="section-card yt-same-video"> <h2> 🎥 From the same video <span class="yt-same-count">9</span> </h2> <p class="yt-same-intro"> Other SEO insights extracted from this same Google Search Central video · duration 53 min · published on 13/10/2016 </p> <div class="related-grid yt-same-grid"> <a href="/en/d/utilisation-des-meta-tags-pour-le-seo" class="related-item yt-same-item"> <div class="title">Les méta descriptions ont-elles encore un impact réel sur le SEO ?</div> <div class="meta"> <span class="yt-same-ts">⏱ 1:35</span> </div> </a> <a href="/en/d/importance-des-ancres-de-lien-anchor-text" class="related-item yt-same-item"> <div class="title">L'ancre de lien est-elle vraiment prioritaire pour le crawl et le positionnement ?</div> <div class="meta"> <span class="yt-same-ts">⏱ 2:40</span> </div> </a> <a href="/en/d/langue-des-meta-tags-pour-le-contenu-en-hindi" class="related-item yt-same-item"> <div class="title">Faut-il vraiment traduire les meta descriptions en hindi pour ranker sur Google India ?</div> <div class="meta"> <span class="yt-same-ts">⏱ 4:13</span> </div> </a> <a href="/en/d/exemples-de-mauvais-anchor-text" class="related-item yt-same-item"> <div class="title">Pourquoi les ancres « Cliquez ici » nuisent-elles vraiment à votre SEO ?</div> <div class="meta"> <span class="yt-same-ts">⏱ 5:33</span> </div> </a> <a href="/en/d/recherche-de-mots-cles-pour-le-contenu-en-hindi" class="related-item yt-same-item"> <div class="title">Les outils AdWords sont-ils vraiment adaptés à la recherche de mots-clés SEO en hindi ?</div> <div class="meta"> <span class="yt-same-ts">⏱ 10:05</span> </div> </a> <a href="/en/d/bienfaits-des-mots-cles-longue-traine" class="related-item yt-same-item"> <div class="title">Les mots-clés longue traîne améliorent-ils vraiment la pertinence SEO ?</div> <div class="meta"> <span class="yt-same-ts">⏱ 15:06</span> </div> </a> <a href="/en/d/compatibilite-de-langage-pour-le-contenu-seo" class="related-item yt-same-item"> <div class="title">Faut-il adapter son contenu SEO à la langue que l'audience tape, même si ce n'est pas celle qu'elle parle ?</div> <div class="meta"> <span class="yt-same-ts">⏱ 20:03</span> </div> </a> <a href="/en/d/impact-des-alt-tags-sur-le-classement-des-images" class="related-item yt-same-item"> <div class="title">Les balises alt impactent-elles vraiment le classement organique de vos pages ?</div> <div class="meta"> <span class="yt-same-ts">⏱ 23:31</span> </div> </a> <a href="/en/d/risque-des-reseaux-de-blogs-prives-pbn" class="related-item yt-same-item"> <div class="title">Les PBN sont-ils vraiment détectés par Google ou peut-on encore passer entre les mailles ?</div> <div class="meta"> <span class="yt-same-ts">⏱ 50:34</span> </div> </a> </div> <a href="https://www.youtube.com/watch?v=7TtdIZbAmCs" target="_blank" rel="noopener" class="yt-same-source-link"> 🎥 Watch the full video on YouTube → </a> </div> <!-- Declarations similaires --> <div class="section-card"> <h2>Related statements</h2> <div class="related-grid"> <a href="/en/d/the-indexing-api-is-overwhelmed-by-bloggers" class="related-item"> <div class="title">Is Google's indexing API becoming unusable due to abuse?</div> <div class="meta"> John Mueller · May 2026 · <span class="stars">★★★</span> </div> </a> <a href="/en/d/seo-workflow-automation" class="related-item"> <div class="title">Is AI truly simplifying SEO workflows or are there critical technical risks lurking beneath the surface?</div> <div class="meta"> John Mueller · May 2026 · <span class="stars">★★</span> </div> </a> <a href="/en/d/check-the-technical-structure-to-optimize-seo" class="related-item"> <div class="title">Do AI-generated sites need a specific technical setup to rank well?</div> <div class="meta"> John Mueller · May 2026 · <span class="stars">★★★</span> </div> </a> <a href="/en/d/decrease-in-indexing-on-google-search-move-along-there-s-nothing-to-see" class="related-item"> <div class="title">Is the massive deindexing on Google Search real or just a bug in the Search Console?</div> <div class="meta"> John Mueller · May 2026 · <span class="stars">★★</span> </div> </a> <a href="/en/d/google-search-adaptation-to-ai-competitiveness" class="related-item"> <div class="title">How is Google Search restructuring its engine to counter the rise of generative AI?</div> <div class="meta"> Nikola Todorovic · May 2026 · <span class="stars">★★★</span> </div> </a> <a href="/en/d/the-importance-of-valuable-content-for-seo" class="related-item"> <div class="title">Is AI really changing the rules for valuable content in SEO?</div> <div class="meta"> Nikola Todorovic · May 2026 · <span class="stars">★★★</span> </div> </a> </div> </div> <!-- Prev/Next --> <div class="prev-next"> <a href="/en/d/risks-of-private-blog-networks-pbn"> <div class="nav-label">« Previous</div> <div class="nav-title">Risks of Private Blog Networks (PBN)...</div> </a> <a href="/en/d/validation-of-interstitials-for-legal-reasons" style="text-align: right;"> <div class="nav-label">Next »</div> <div class="nav-title">Validation of Interstitials for Legal Reasons...</div> </a> </div> <div class="back-link"> <a href="/en/" class="btn">« Back to results</a> </div> <!-- Boutons de partage sociaux (bas de page) --> <div class="share-block" data-entity-type="declaration" data-entity-id="11913" data-share-lang="en"> <div class="share-block-label"> <span class="share-icon" aria-hidden="true">🔗</span> <span>Share this article</span> </div> <div class="share-buttons"> <a class="share-btn share-btn-facebook" href="https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fseoclaims.com%2Fen%2Fd%2Fstructured-data-and-the-use-of-schema-for-news" target="_blank" rel="noopener nofollow" data-network="facebook" aria-label="Share on Facebook" title="Share on Facebook"> <svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path fill="currentColor" d="M22 12.06C22 6.5 17.52 2 12 2S2 6.5 2 12.06c0 5 3.66 9.13 8.44 9.88v-6.99H7.9v-2.89h2.54V9.85c0-2.51 1.49-3.89 3.78-3.89 1.09 0 2.24.2 2.24.2v2.46h-1.26c-1.24 0-1.63.77-1.63 1.56v1.88h2.78l-.45 2.89h-2.33v6.99C18.34 21.19 22 17.06 22 12.06z"/></svg> <span class="share-btn-text">Facebook</span> </a> <a class="share-btn share-btn-twitter" href="https://twitter.com/intent/tweet?text=According%20to%20Google%20%22Structured%20Data%20for%20News%3A%20Why%20is%20Google%20so%20insistent%20on%20matching%20marked%20content%20to%20visible%20content%3F%22%0A%0Ahttps%3A%2F%2Fseoclaims.com%2Fen%2Fd%2Fstructured-data-and-the-use-of-schema-for-news%0A%0APlease%20RT%21" target="_blank" rel="noopener nofollow" data-network="twitter" aria-label="Share on X" title="Share on X"> <svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true"><path fill="currentColor" d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/></svg> <span class="share-btn-text">X</span> </a> <a class="share-btn share-btn-linkedin" href="https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fseoclaims.com%2Fen%2Fd%2Fstructured-data-and-the-use-of-schema-for-news" target="_blank" rel="noopener nofollow" data-network="linkedin" aria-label="Share on LinkedIn" title="Share on LinkedIn"> <svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path fill="currentColor" d="M20.45 20.45h-3.55v-5.57c0-1.33-.03-3.04-1.85-3.04-1.85 0-2.13 1.45-2.13 2.94v5.67H9.36V9h3.41v1.56h.05c.48-.9 1.64-1.85 3.38-1.85 3.61 0 4.28 2.38 4.28 5.47v6.27zM5.34 7.43a2.06 2.06 0 1 1 0-4.12 2.06 2.06 0 0 1 0 4.12zm1.78 13.02H3.56V9h3.56v11.45zM22.22 0H1.77C.79 0 0 .77 0 1.72v20.56C0 23.23.79 24 1.77 24h20.45c.98 0 1.78-.77 1.78-1.72V1.72C24 .77 23.2 0 22.22 0z"/></svg> <span class="share-btn-text">LinkedIn</span> </a> <a class="share-btn share-btn-email" href="mailto:?subject=According%20to%20Google%20%22Structured%20Data%20for%20News%3A%20Why%20is%20Google%20so%20insistent%20on%20matching%20marked%20content%20to%20visible%20content%3F%22&body=According%20to%20Google%20%22Structured%20Data%20for%20News%3A%20Why%20is%20Google%20so%20insistent%20on%20matching%20marked%20content%20to%20visible%20content%3F%22%0A%0AGoogle%20requires%20that%20the%20Schema.org%20marked%20content%20is%20strictly%20identical%20to%20what%20users%20can%20see%2C%20regardless%20of%20the%20syntax%20used.%20This%20rule%20aims%20to%20prevent%20manipul%0A%0Ahttps%3A%2F%2Fseoclaims.com%2Fen%2Fd%2Fstructured-data-and-the-use-of-schema-for-news%0A%0APlease%20RT%21" data-network="email" aria-label="Share by email" title="Share by email"> <svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path fill="currentColor" d="M2 4h20c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H2c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2zm10 7L2.4 6h19.2L12 11zm0 2L2 7v11h20V7l-10 6z"/></svg> <span class="share-btn-text">Email</span> </a> <button type="button" class="share-btn share-btn-copy" data-network="copy" data-copy-url="https://seoclaims.com/en/d/structured-data-and-the-use-of-schema-for-news" data-label-copy="Copy link" data-label-copied="Link copied!" aria-label="Copy link" title="Copy link"> <svg viewBox="0 0 24 24" width="18" height="18" aria-hidden="true"><path fill="currentColor" d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg> <span class="share-btn-text">Copy link</span> </button> </div> </div> <script> (function(){ var blocks = document.querySelectorAll('.share-block:not([data-share-init])'); blocks.forEach(function(block){ block.setAttribute('data-share-init', '1'); var entityType = block.dataset.entityType; var entityId = block.dataset.entityId; var shareLang = block.dataset.shareLang || 'fr'; function track(network){ try { var fd = new FormData(); fd.append('entity_type', entityType); fd.append('entity_id', entityId); fd.append('network', network); fd.append('lang', shareLang); if (navigator.sendBeacon) { navigator.sendBeacon('https://seoclaims.com/api/share-track.php', fd); } else { fetch('https://seoclaims.com/api/share-track.php', { method:'POST', body: fd, keepalive: true }); } } catch(e){} } block.querySelectorAll('.share-btn').forEach(function(btn){ var network = btn.dataset.network; if (network === 'copy') { btn.addEventListener('click', function(){ var url = btn.dataset.copyUrl; var done = function(){ var txt = btn.querySelector('.share-btn-text'); var orig = btn.dataset.labelCopy; if (txt) txt.textContent = btn.dataset.labelCopied; btn.classList.add('share-btn-success'); setTimeout(function(){ if (txt) txt.textContent = orig; btn.classList.remove('share-btn-success'); }, 1800); }; if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(url).then(done).catch(function(){ window.prompt(btn.dataset.labelCopy, url); }); } else { var ta = document.createElement('textarea'); ta.value = url; ta.style.position='fixed'; ta.style.left='-9999px'; document.body.appendChild(ta); ta.select(); try { document.execCommand('copy'); done(); } catch(e){ window.prompt(btn.dataset.labelCopy, url); } document.body.removeChild(ta); } track('copy'); }); return; } btn.addEventListener('click', function(e){ track(network); if (network === 'facebook' || network === 'twitter' || network === 'linkedin') { e.preventDefault(); window.open(btn.href, 'shareWindow', 'width=620,height=620,resizable=yes,scrollbars=yes'); } // email : laisse le mailto: par defaut }); }); }); })(); </script> </div> <style> .comments-section { max-width: 860px; margin: 40px auto; padding: 0 20px; } .comments-title { font-size: 1.3rem; font-weight: 700; color: #1e293b; margin: 0 0 20px; padding-bottom: 12px; border-bottom: 2px solid #f1f5f9; } .comments-count { font-weight: 400; font-size: .9rem; color: #94a3b8; } .comments-empty { color: #94a3b8; font-style: italic; margin-bottom: 24px; } .comments-list { margin-bottom: 28px; } .comment-item { padding: 16px 0; border-bottom: 1px solid #f1f5f9; } .comment-item:last-child { border-bottom: none; } .comment-pending { background: #fffbeb; border-left: 3px solid #f59e0b; padding-left: 14px; border-radius: 6px; margin: 4px 0; } .comment-pending-badge { display: inline-block; font-size: .7rem; font-weight: 600; color: #92400e; background: #fef3c7; padding: 1px 8px; border-radius: 10px; margin-left: 8px; } .comment-meta { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; } .comment-author { font-weight: 600; font-size: .95rem; color: #1e293b; } .comment-date { font-size: .8rem; color: #94a3b8; } .comment-body { font-size: .93rem; line-height: 1.6; color: #475569; } .comment-form { background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 12px; padding: 24px; position: relative; } .comment-form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; } .comment-form-field { margin-bottom: 12px; } .comment-form-field label { display: block; font-size: .85rem; font-weight: 600; color: #475569; margin-bottom: 4px; } .comment-form-field input, .comment-form-field textarea { width: 100%; padding: 10px 12px; border: 1px solid #d1d5db; border-radius: 8px; font-size: .9rem; font-family: inherit; background: #fff; transition: border-color .2s; box-sizing: border-box; } .comment-form-field input:focus, .comment-form-field textarea:focus { outline: none; border-color: #1a73e8; box-shadow: 0 0 0 3px rgba(26, 115, 232, .1); } .comment-form-field textarea { resize: vertical; min-height: 100px; } .comment-form-charcount { font-size: .75rem; color: #94a3b8; text-align: right; margin-top: 4px; } .comment-form-footer { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; } .comment-submit-btn { padding: 10px 24px; background: #1a73e8; color: #fff; border: none; border-radius: 8px; font-size: .9rem; font-weight: 600; cursor: pointer; transition: opacity .2s; } .comment-submit-btn:hover { opacity: .88; } .comment-submit-btn:disabled { opacity: .5; cursor: default; } .comment-form-notice { font-size: .8rem; color: #94a3b8; font-style: italic; } .comment-feedback { margin-top: 12px; padding: 10px 14px; border-radius: 8px; font-size: .9rem; font-weight: 500; } .comment-feedback-success { background: #d1fae5; color: #065f46; } .comment-feedback-error { background: #fee2e2; color: #991b1b; } @media (max-width: 768px) { .comment-form-row { grid-template-columns: 1fr; gap: 0; } .comments-section { padding: 0 16px; } } </style> <section class="comments-section" id="comments"> <h3 class="comments-title">💬 Comments <span class="comments-count">(0)</span></h3> <div class="comments-list" id="comments-list"> <p class="comments-empty" id="comments-empty">Be the first to comment.</p> </div> <form class="comment-form" id="comment-form" novalidate> <input type="hidden" name="page_type" value="declaration"> <input type="hidden" name="page_id" value="11913"> <input type="hidden" name="lang" value="en"> <!-- Honeypot anti-spam --> <div style="position:absolute;left:-9999px;" aria-hidden="true"> <label for="website">Do not fill this field</label> <input type="text" id="website" name="website" tabindex="-1" autocomplete="off"> </div> <div class="comment-form-row"> <div class="comment-form-field"> <label for="comment-name">Name or alias *</label> <input type="text" id="comment-name" name="author_name" required maxlength="100" autocomplete="name"> </div> <div class="comment-form-field"> <label for="comment-email">Email (optional, not published)</label> <input type="email" id="comment-email" name="author_email" maxlength="255" autocomplete="email"> </div> </div> <div class="comment-form-field"> <label for="comment-content">Your comment *</label> <textarea id="comment-content" name="content" required maxlength="2000" rows="4"></textarea> <div class="comment-form-charcount"><span id="comment-chars">2000</span> characters remaining</div> </div> <div class="comment-form-footer"> <button type="submit" class="comment-submit-btn">Post comment</button> <span class="comment-form-notice">Comments are moderated before publication.</span> </div> <div id="comment-feedback" class="comment-feedback" style="display:none;"></div> </form> </section> <script> (function() { var form = document.getElementById('comment-form'); if (!form) return; var textarea = document.getElementById('comment-content'); var charsEl = document.getElementById('comment-chars'); var feedback = document.getElementById('comment-feedback'); var API_URL = 'https://seoclaims.com/api/comments.php'; // Compteur de caracteres textarea.addEventListener('input', function() { var remaining = 2000 - this.value.length; charsEl.textContent = remaining; charsEl.style.color = remaining < 100 ? '#dc2626' : ''; }); form.addEventListener('submit', function(e) { e.preventDefault(); var btn = form.querySelector('.comment-submit-btn'); btn.disabled = true; var origText = btn.textContent; btn.textContent = '...'; feedback.style.display = 'none'; var data = { action: 'submit', page_type: form.querySelector('[name="page_type"]').value, page_id: parseInt(form.querySelector('[name="page_id"]').value), author_name: form.querySelector('[name="author_name"]').value.trim(), author_email: form.querySelector('[name="author_email"]').value.trim(), content: form.querySelector('[name="content"]').value.trim(), lang: form.querySelector('[name="lang"]').value, website: form.querySelector('[name="website"]').value }; fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) .then(function(r) { return r.json(); }) .then(function(res) { if (res.success) { feedback.className = 'comment-feedback comment-feedback-success'; feedback.textContent = res.message; feedback.style.display = 'block'; // Inserer le commentaire en ligne (en attente de moderation) var list = document.getElementById('comments-list'); var empty = document.getElementById('comments-empty'); if (empty) empty.remove(); var pendingLabel = 'Awaiting moderation'; var now = new Date(); var day = String(now.getDate()).padStart(2,'0'); var month = String(now.getMonth()+1).padStart(2,'0'); var dateStr = day + '/' + month + '/' + now.getFullYear(); var div = document.createElement('div'); div.className = 'comment-item comment-pending'; div.innerHTML = '<div class="comment-meta">' + '<span class="comment-author">' + data.author_name.replace(/</g,'<') + '</span>' + '<span class="comment-date">' + dateStr + '</span>' + '<span class="comment-pending-badge">' + pendingLabel + '</span>' + '</div>' + '<div class="comment-body">' + data.content.replace(/</g,'<').replace(/\n/g,'<br>') + '</div>'; list.appendChild(div); form.querySelector('[name="content"]').value = ''; form.querySelector('[name="author_name"]').value = ''; form.querySelector('[name="author_email"]').value = ''; charsEl.textContent = '2000'; } else { feedback.className = 'comment-feedback comment-feedback-error'; feedback.textContent = res.error; feedback.style.display = 'block'; btn.disabled = false; btn.textContent = origText; } }) .catch(function() { feedback.className = 'comment-feedback comment-feedback-error'; feedback.textContent = 'Erreur réseau'; feedback.style.display = 'block'; btn.disabled = false; btn.textContent = origText; }); }); })(); </script> <section class="nl-inline-section"> <div class="nl-inline-inner"> <div class="nl-inline-icon">🔔</div> <div class="nl-inline-text"> <h2 class="nl-inline-headline">Get real-time analysis of the latest Google SEO declarations</h2> <p class="nl-inline-sub">Be the first to know every time a new official Google statement drops — with full expert analysis.</p> </div> <div class="nl-inline-action"> <form class="nl-inline-form" id="nl-inline-form" novalidate> <input type="email" name="email" placeholder="Your email address" required autocomplete="email"> <button type="submit">Subscribe for free</button> </form> <div class="nl-inline-privacy">No spam. Unsubscribe in one click.</div> </div> </div> </section> <style> .nl-inline-section { background: linear-gradient(135deg, #0f172a 0%, #1e3a5f 50%, #0f172a 100%); padding: 52px 24px; margin: 0; } .nl-inline-inner { max-width: 960px; margin: 0 auto; display: grid; grid-template-columns: auto 1fr auto; gap: 24px 32px; align-items: center; } .nl-inline-icon { font-size: 40px; line-height: 1; } .nl-inline-headline { font-size: clamp(16px, 2vw, 20px); font-weight: 700; color: #f0f9ff; margin: 0 0 6px; line-height: 1.35; } .nl-inline-sub { font-size: 14px; color: #7fb8d8; margin: 0; line-height: 1.55; } .nl-inline-action { min-width: 300px; } .nl-inline-form { display: flex; gap: 8px; } .nl-inline-form input[type="email"] { flex: 1; padding: 11px 14px; border: 1px solid rgba(14,165,233,.4); border-radius: 8px; background: rgba(255,255,255,.07); color: #f0f9ff; font-size: 14px; outline: none; min-width: 0; transition: border-color .2s, background .2s; } .nl-inline-form input[type="email"]::placeholder { color: #5a91b0; } .nl-inline-form input[type="email"]:focus { border-color: #0ea5e9; background: rgba(255,255,255,.12); } .nl-inline-form button { padding: 11px 18px; background: linear-gradient(135deg, #0ea5e9, #7c3aed); color: #fff; border: none; border-radius: 8px; font-size: 14px; font-weight: 600; cursor: pointer; white-space: nowrap; transition: opacity .2s; } .nl-inline-form button:hover { opacity: .88; } .nl-inline-form button:disabled { opacity: .55; cursor: default; } .nl-inline-privacy { font-size: 12px; color: #4a7a96; margin-top: 7px; text-align: center; } @media (max-width: 768px) { .nl-inline-inner { grid-template-columns: auto 1fr; grid-template-rows: auto auto; } .nl-inline-action { grid-column: 1 / -1; min-width: 0; } .nl-inline-form { flex-wrap: wrap; } .nl-inline-form input[type="email"] { min-width: 200px; } } </style> <script> (function() { var form = document.getElementById('nl-inline-form'); if (!form) return; var LANG = 'en'; var API_URL = 'https://seoclaims.com/api/subscribe'; var COOKIE = 'nl_subscribed'; var MSG = { success: 'You\'re in! You\'ll receive the next analyses.', already: 'You are already subscribed.', error: 'Something went wrong, please try again.', }; function getCookie(n) { return document.cookie.split(';').some(function(c) { return c.trim().startsWith(n + '='); }); } function setCookie(n, days) { var d = new Date(); d.setDate(d.getDate() + days); document.cookie = n + '=1;expires=' + d.toUTCString() + ';path=/;SameSite=Lax'; } // Masquer si deja inscrit if (getCookie(COOKIE)) { var section = form.closest('.nl-inline-section'); if (section) section.style.display = 'none'; return; } form.addEventListener('submit', function(e) { e.preventDefault(); var email = form.querySelector('input[type="email"]').value.trim(); var btn = form.querySelector('button'); btn.disabled = true; var orig = btn.textContent; btn.textContent = '...'; fetch(API_URL, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: email, lang: LANG }) }) .then(function(r) { return r.json(); }) .then(function(data) { if (data.success) { setCookie(COOKIE, 365); form.innerHTML = '<div style="color:#0d9488;font-weight:600;font-size:15px;padding:10px 0;">✓ ' + (data.already ? MSG.already : MSG.success) + '</div>'; // Masquer aussi banniere footer et popup si presents var banner = document.getElementById('nl-banner'); var popup = document.getElementById('nl-popup'); if (banner) banner.style.display = 'none'; if (popup) popup.style.display = 'none'; } else { btn.disabled = false; btn.textContent = orig; var err = form.querySelector('.nl-inline-err'); if (!err) { err = document.createElement('div'); err.className = 'nl-inline-err'; err.style.cssText = 'color:#dc2626;font-size:13px;margin-top:6px;'; form.parentNode.insertBefore(err, form.nextSibling); } err.textContent = data.error || MSG.error; } }) .catch(function() { btn.disabled = false; btn.textContent = orig; }); }); })(); </script> <footer class="site-footer"> <div class="footer-inner"> <!-- ====== Row 1 : About + Navigation + Resources ====== --> <div class="footer-top"> <div> <div class="footer-brand"> <picture> <source srcset="https://seoclaims.com/assets/logo.webp" type="image/webp"> <img src="https://seoclaims.com/assets/logo.png" alt="SEO Declarations" class="footer-logo" loading="lazy" width="132" height="72"> </picture> </div> <p>SEO Claims collects, analyzes and translates <a href="/en/declarations">official Google statements</a> about search engine optimization, sourced from <a href="/en/articles">published articles</a> and <a href="/en/videos">YouTube videos</a> by Google Search Central. Each statement is enriched with <a href="/en/declarations">AI analysis</a>, classified by <a href="/en/declarations">SEO category</a> and attributed to its <a href="/en/speaker/john-mueller">author</a>. An essential tool for SEO professionals who want to know exactly what Google recommends.</p> </div> <div> <div class="footer-heading">Navigation</div> <nav class="footer-nav"> <a href="/en/declarations" class="footer-link">Statements</a> <a href="/en/labs/" class="footer-link">Labs SEO</a> <a href="/en/speaker/john-mueller" class="footer-link">Authors</a> <a href="/en/sitemap-declarations" class="footer-link">Sitemap</a> <a href="/en/rss.xml" class="footer-link footer-link-rss" rel="alternate" type="application/rss+xml"> <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 8 8" fill="#f26522" aria-hidden="true" style="vertical-align:-2px;margin-right:6px;"> <rect width="8" height="8" rx="1.5" fill="#f26522"/> <circle cx="2" cy="6" r="1" fill="#fff"/> <path d="M1 4a3 3 0 0 1 3 3h1a4 4 0 0 0-4-4z" fill="#fff"/> <path d="M1 2a5 5 0 0 1 5 5h1a6 6 0 0 0-6-6z" fill="#fff"/> </svg>RSS feed </a> <a href="/en/top-seo-agencies-france" class="footer-link">Top SEO Agencies</a> <a href="/en/legal" class="footer-link">Legal Notice</a> </nav> </div> <div> <div class="footer-heading">Resources</div> <nav class="footer-nav"> <a href="https://search.google.com/search-console" target="_blank" rel="noopener" class="footer-link">Google Search Console</a> <a href="https://pagespeed.web.dev/" target="_blank" rel="noopener" class="footer-link">PageSpeed Insights</a> <a href="https://search.google.com/test/rich-results" target="_blank" rel="noopener" class="footer-link">Rich Results Test</a> <a href="https://developer.chrome.com/docs/lighthouse/" target="_blank" rel="noopener" class="footer-link">Lighthouse</a> <a href="https://developers.google.com/search/docs" target="_blank" rel="noopener" class="footer-link">Google Search Guidelines</a> <a href="/en/google-tools" class="footer-link" style="color: #60a5fa; font-weight: 600;">All Google Tools →</a> </nav> </div> </div> <!-- ====== Row 2 : 3 piliers SEO — Top 5 categories par vues ====== --> <div class="footer-pillars"> <!-- Semantique --> <details class="footer-pillar-details" open> <summary class="footer-pillar-title"> <span style="display:flex;align-items:center;gap:8px;"> <span class="footer-pillar-dot" style="background: #60a5fa;"></span> <span class="footer-pillar-label" style="color: #60a5fa;">Semantic</span> </span> </summary> <nav class="footer-pillar-nav"> <a href="/en/c/ia-seo" class="footer-pillar-link"> <span>AI & SEO</span> <span class="footer-pillar-count">11313</span> </a> <a href="/en/c/contenu" class="footer-pillar-link"> <span>Content</span> <span class="footer-pillar-count">6385</span> </a> <a href="/en/c/nom-domaine" class="footer-pillar-link"> <span>Domain Name</span> <span class="footer-pillar-count">2828</span> </a> <a href="/en/c/pdf-fichiers" class="footer-pillar-link"> <span>PDF & Files</span> <span class="footer-pillar-count">497</span> </a> <a href="/en/c/discover-actualites" class="footer-pillar-link"> <span>Discover & News</span> <span class="footer-pillar-count">343</span> </a> </nav> </details> <!-- Technique --> <details class="footer-pillar-details" open> <summary class="footer-pillar-title"> <span style="display:flex;align-items:center;gap:8px;"> <span class="footer-pillar-dot" style="background: #34d399;"></span> <span class="footer-pillar-label" style="color: #34d399;">Technical</span> </span> </summary> <nav class="footer-pillar-nav"> <a href="/en/c/anciennete-historique" class="footer-pillar-link"> <span>Domain Age & History</span> <span class="footer-pillar-count">7948</span> </a> <a href="/en/c/crawl-indexation" class="footer-pillar-link"> <span>Crawl & Indexing</span> <span class="footer-pillar-count">5320</span> </a> <a href="/en/c/javascript-technique" class="footer-pillar-link"> <span>JavaScript & Technical SEO</span> <span class="footer-pillar-count">3612</span> </a> <a href="/en/c/search-console" class="footer-pillar-link"> <span>Search Console</span> <span class="footer-pillar-count">2512</span> </a> <a href="/en/c/performance-web" class="footer-pillar-link"> <span>Web Performance</span> <span class="footer-pillar-count">105</span> </a> </nav> </details> <!-- Autorite --> <details class="footer-pillar-details" open> <summary class="footer-pillar-title"> <span style="display:flex;align-items:center;gap:8px;"> <span class="footer-pillar-dot" style="background: #fbbf24;"></span> <span class="footer-pillar-label" style="color: #fbbf24;">Authority</span> </span> </summary> <nav class="footer-pillar-nav"> <a href="/en/c/liens-backlinks" class="footer-pillar-link"> <span>Links & Backlinks</span> <span class="footer-pillar-count">2076</span> </a> <a href="/en/c/penalites-spam" class="footer-pillar-link"> <span>Penalties & Spam</span> <span class="footer-pillar-count">845</span> </a> <a href="/en/c/reseaux-sociaux" class="footer-pillar-link"> <span>Social Media</span> <span class="footer-pillar-count">541</span> </a> <a href="/en/c/algorithmes" class="footer-pillar-link"> <span>Algorithms</span> <span class="footer-pillar-count">416</span> </a> <a href="/en/c/recherche-locale" class="footer-pillar-link"> <span>Local Search</span> <span class="footer-pillar-count">116</span> </a> </nav> </details> </div> <!-- ====== Row 3 : Dernieres prises de parole ====== --> <div class="footer-popular"> <div class="footer-popular-title">Latest Google statements on SEO</div> <div class="footer-popular-grid"> <a href="/en/d/l-api-d-indexation-est-submergee-par-les-blogueurs" class="footer-popular-link"> <span class="footer-popular-meta"> <span class="footer-popular-date">May 2026</span> <span class="footer-popular-speaker">John Mueller</span> </span> <span class="footer-popular-text">L'API d'indexation Google devient-elle inutilisable à cause des abus ?</span> </a> <a href="/en/d/projets-web-simplifies-grace-au-vibe-coding" class="footer-popular-link"> <span class="footer-popular-meta"> <span class="footer-popular-date">May 2026</span> <span class="footer-popular-speaker">Martin Splitt</span> </span> <span class="footer-popular-text">Le 'vibe coding' IA peut-il vraiment accélérer vos projets web SEO ?</span> </a> <a href="/en/d/interactions-vocales-pour-la-gestion-de-sites-web" class="footer-popular-link"> <span class="footer-popular-meta"> <span class="footer-popular-date">May 2026</span> <span class="footer-popular-speaker">John Mueller</span> </span> <span class="footer-popular-text">La gestion vocale des sites web va-t-elle changer la donne pour le SEO ?</span> </a> <a href="/en/d/utilisation-de-l-ia-pour-l-automatisation-des-tests" class="footer-popular-link"> <span class="footer-popular-meta"> <span class="footer-popular-date">May 2026</span> <span class="footer-popular-speaker">Martin Splitt</span> </span> <span class="footer-popular-text">Comment l'IA peut-elle automatiser vos tests SEO sans coder ?</span> </a> <a href="/en/d/l-automatisation-des-flux-de-travail-seo" class="footer-popular-link"> <span class="footer-popular-meta"> <span class="footer-popular-date">May 2026</span> <span class="footer-popular-speaker">John Mueller</span> </span> <span class="footer-popular-text">L'IA simplifie-t-elle vraiment les workflows SEO ou masque-t-elle des ri…</span> </a> <a href="/en/d/verifier-la-structure-technique-pour-optimiser-le-seo" class="footer-popular-link"> <span class="footer-popular-meta"> <span class="footer-popular-date">May 2026</span> <span class="footer-popular-speaker">John Mueller</span> </span> <span class="footer-popular-text">Les sites générés par IA doivent-ils avoir une configuration technique p…</span> </a> <a href="/en/d/les-sites-generes-par-l-ia-peuvent-etre-difficiles-a-reconnaitre" class="footer-popular-link"> <span class="footer-popular-meta"> <span class="footer-popular-date">May 2026</span> <span class="footer-popular-speaker">John Mueller</span> </span> <span class="footer-popular-text">Les sites générés par IA sont-ils vraiment indétectables pour Google ?</span> </a> <a href="/en/d/baisse-d-indexation-sur-google-search-circulez-il-n-y-a-rien-a-voir" class="footer-popular-link"> <span class="footer-popular-meta"> <span class="footer-popular-date">May 2026</span> <span class="footer-popular-speaker">John Mueller</span> </span> <span class="footer-popular-text">La désindexation massive sur Google Search est-elle réelle ou juste un b…</span> </a> </div> </div> <!-- ====== Row 4 : Copyright ====== --> <div class="footer-bottom"> <span>© 2026 SEO Declarations. All rights reserved.</span> <span>This site is not affiliated with Google. Statements presented are from public Google communications.</span> </div> </div> </footer> <!-- ====== Web Push (cloche flottante + modal opt-in 3 topics) ====== --> <script> window.SEOC_PUSH = { vapidPublicKey: "BO7R09jNPfEwItp-0Yf5Q2JFcpxUIEaPrI_J8GwcXAxYdFhYrMpeYZxRk1fv1ZPOjuU1D5B9LsKs987gSDNuh5k", lang: "en", labels: {"bell":"Notifications","title":"🚀 Be the first to know what Google announces","intro":"While other SEOs find out about Google's statements <strong>weeks after publication<\/strong>, you'll know <strong>within minutes<\/strong>. Articles, Search Central videos, Labs analyses — all curated, translated and summarized for you.","fomo":"⚡ Speed = competitive edge. The SEOs who react first win.","social_proof":"⭐ Join the SEO pros who never miss a Google statement","no_spam":"✓ Zero spam, we commit","no_spam_detail":"Max 4 push\/day, never before 7am or after 22h. 1-click unsubscribe. No marketing, no partner, no third-party.","t_declarations":"📰 New Google SEO statements (Abondance)","t_videos":"🎥 New YouTube Search Central videos","t_labs":"🧪 New Labs SEO analyses","cancel":"Maybe later","confirm":"🔔 Yes, keep me informed","note":"You stay in control : choose your topics and unsubscribe anytime from the bell icon.","close":"Close","success":"✓ Notifications enabled","error":"Error, please retry","prefs_title":"Notification preferences","unsub":"Disable all","save":"Save"}}; </script> <script src="https://seoclaims.com/assets/push-public.js?v=1778774824" defer></script> <!-- ====== Popup Newsletter (une fois par session) ====== --> <div class="nl-popup-overlay" id="nl-popup" style="display:none;" role="dialog" aria-modal="true" aria-label="Get a complete real-time analysis of the latest Google SEO declarations"> <div class="nl-popup-box"> <button class="nl-popup-close" id="nl-popup-close" aria-label="Close">×</button> <div class="nl-popup-header"> <div class="nl-popup-badge">Stay ahead</div> <h3 class="nl-popup-title">Get a complete real-time analysis of the latest Google SEO declarations</h3> <p class="nl-popup-sub">Be the first to know every time a new official Google SEO statement drops, with full analysis included.</p> </div> <form class="nl-form nl-popup-form" data-lang="en" id="nl-popup-form"> <div class="nl-popup-input-group"> <input type="email" name="email" placeholder="Your email address" required autocomplete="email"> <button type="submit">Subscribe for free</button> </div> </form> <div class="nl-popup-trust"> <span class="nl-popup-trust-icon">🔒</span> <span>No spam. Unsubscribe in one click.</span> </div> </div> </div> <style> /* === Newsletter Popup === */ .nl-popup-overlay { position: fixed; inset: 0; background: rgba(15,23,42,.6); backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px); z-index: 9999; display: flex; align-items: center; justify-content: center; padding: 20px; animation: nlFadeIn .3s ease; } .nl-popup-box { background: linear-gradient(165deg, #ffffff 0%, #f8fafc 100%); border-radius: 20px; padding: 40px 36px 32px; max-width: 460px; width: 100%; position: relative; box-shadow: 0 25px 80px rgba(0,0,0,.2), 0 0 0 1px rgba(0,0,0,.05); animation: nlSlideUp .4s cubic-bezier(.16,1,.3,1); text-align: center; } .nl-popup-close { position: absolute; top: 12px; right: 14px; background: none; border: none; font-size: 22px; color: #94a3b8; cursor: pointer; line-height: 1; padding: 6px 10px; border-radius: 8px; transition: color .2s, background .2s; } .nl-popup-close:hover { color: #374151; background: #f1f5f9; } .nl-popup-header { margin-bottom: 24px; } .nl-popup-badge { display: inline-block; background: linear-gradient(135deg, #0ea5e9, #7c3aed); color: #fff; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .08em; padding: 5px 14px; border-radius: 20px; margin-bottom: 16px; } .nl-popup-title { font-size: 22px; font-weight: 800; color: #0f172a; line-height: 1.3; margin: 0 0 10px; } .nl-popup-sub { font-size: 14px; color: #64748b; line-height: 1.6; margin: 0; } .nl-popup-input-group { display: flex; gap: 0; border-radius: 12px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,.08), 0 0 0 1px rgba(0,0,0,.06); } .nl-popup-form input[type="email"] { flex: 1; padding: 14px 16px; background: #fff; color: #1e293b; border: none; font-size: 15px; outline: none; min-width: 0; } .nl-popup-form input[type="email"]::placeholder { color: #94a3b8; } .nl-popup-form button { padding: 14px 22px; background: linear-gradient(135deg, #0ea5e9, #6d28d9); color: #fff; border: none; font-size: 14px; font-weight: 700; cursor: pointer; white-space: nowrap; transition: opacity .2s; } .nl-popup-form button:hover { opacity: .9; } .nl-popup-form button:disabled { opacity: .5; cursor: default; } .nl-popup-trust { display: flex; align-items: center; justify-content: center; gap: 6px; margin-top: 14px; font-size: 12px; color: #94a3b8; } .nl-popup-trust-icon { font-size: 13px; } .nl-popup-msg { padding: 16px; border-radius: 12px; font-weight: 600; font-size: 15px; margin-top: 4px; } .nl-popup-msg.success { background: #ecfdf5; color: #059669; } .nl-popup-msg.error { background: #fef2f2; color: #dc2626; } @media (max-width: 480px) { .nl-popup-box { padding: 32px 20px 24px; } .nl-popup-input-group { flex-direction: column; border-radius: 12px; } .nl-popup-form input[type="email"] { border-bottom: 1px solid #e2e8f0; } .nl-popup-form button { padding: 14px; border-radius: 0 0 12px 12px; } .nl-popup-title { font-size: 19px; } } @keyframes nlFadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes nlSlideUp { from { transform: translateY(30px) scale(.97); opacity: 0; } to { transform: translateY(0) scale(1); opacity: 1; } } </style> <script> (function() { var LANG = 'en'; var API_URL = 'https://seoclaims.com/api/subscribe'; var COOKIE = 'nl_subscribed'; var SESSION_KEY = 'nl_popup_shown'; var MSG = { success: 'You\'re in! Check your inbox.', already: 'You are already subscribed.', error: 'Something went wrong, please try again.', }; function getCookie(n) { return document.cookie.split(';').some(c => c.trim().startsWith(n + '=')); } function setCookie(n, days) { var d = new Date(); d.setDate(d.getDate() + days); document.cookie = n + '=1;expires=' + d.toUTCString() + ';path=/;SameSite=Lax'; } function closePopup(popup) { popup.style.opacity = '0'; setTimeout(function() { popup.style.display = 'none'; }, 300); sessionStorage.setItem(SESSION_KEY, '1'); } function showMsg(container, text, type) { var existing = container.querySelector('.nl-popup-msg'); if (existing) existing.remove(); var el = document.createElement('div'); el.className = 'nl-popup-msg ' + type; el.textContent = text; container.appendChild(el); } document.addEventListener('DOMContentLoaded', function() { var popup = document.getElementById('nl-popup'); var popupForm = document.getElementById('nl-popup-form'); var closeBtn = document.getElementById('nl-popup-close'); if (!popup || !popupForm) return; // Ne pas afficher si : deja inscrit OU deja vu cette session if (getCookie(COOKIE) || sessionStorage.getItem(SESSION_KEY)) return; // Sequence : push d'abord (premier ecran), newsletter ensuite (>=10s ou 30% scroll) // mais JAMAIS empilee par-dessus le modal push : on attend qu'il soit resolu. var nlShown = false; var nlReady = false; // 10s ou 30% scroll atteint var pushResolved = (typeof Notification === 'undefined') || Notification.permission !== 'default'; function showNlPopup() { if (nlShown || !nlReady || !pushResolved) return; nlShown = true; popup.style.display = 'flex'; sessionStorage.setItem(SESSION_KEY, '1'); } document.addEventListener('seoc-push-resolved', function() { pushResolved = true; showNlPopup(); }); var scrollHandler = function() { var pct = window.scrollY / (document.body.scrollHeight - window.innerHeight); if (pct > 0.3) { nlReady = true; showNlPopup(); window.removeEventListener('scroll', scrollHandler); } }; window.addEventListener('scroll', scrollHandler, {passive: true}); setTimeout(function() { nlReady = true; showNlPopup(); }, 10000); closeBtn.addEventListener('click', function() { closePopup(popup); }); popup.addEventListener('click', function(e) { if (e.target === popup) closePopup(popup); }); document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && popup.style.display === 'flex') closePopup(popup); }); popupForm.addEventListener('submit', function(e) { e.preventDefault(); var email = popupForm.querySelector('input[type="email"]').value.trim(); var btn = popupForm.querySelector('button[type="submit"]'); btn.disabled = true; var origText = btn.textContent; btn.textContent = '...'; fetch(API_URL, { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({email: email, lang: LANG}) }) .then(function(r) { return r.json(); }) .then(function(data) { btn.disabled = false; btn.textContent = origText; if (data.success) { setCookie(COOKIE, 365); popupForm.style.display = 'none'; showMsg(popup.querySelector('.nl-popup-box'), data.already ? MSG.already : MSG.success, 'success'); // Masquer aussi le bloc inline si present var inline = document.querySelector('.nl-inline-section'); if (inline) inline.style.display = 'none'; setTimeout(function() { closePopup(popup); }, 2500); } else { showMsg(popup.querySelector('.nl-popup-box'), data.error || MSG.error, 'error'); } }) .catch(function() { btn.disabled = false; btn.textContent = origText; showMsg(popup.querySelector('.nl-popup-box'), MSG.error, 'error'); }); }); }); })(); </script> <!-- WebSite + Organization Schema --> <script type="application/ld+json"> { "@context": "https://schema.org", "@graph": [ { "@type": "WebSite", "@id": "https://seoclaims.com/#website", "name": "SEO Declarations", "url": "https://seoclaims.com", "inLanguage": [ "fr-FR", "en" ], "publisher": { "@id": "https://seoclaims.com/#organization" }, "potentialAction": { "@type": "SearchAction", "target": { "@type": "EntryPoint", "urlTemplate": "https://seoclaims.com/?q={search_term_string}" }, "query-input": "required name=search_term_string" } }, { "@type": "Organization", "@id": "https://seoclaims.com/#organization", "name": "SEO Declarations", "url": "https://seoclaims.com", "logo": { "@type": "ImageObject", "url": "https://seoclaims.com/assets/logo.png", "width": 512, "height": 512 }, "description": "Search engine for official Google SEO declarations, bilingual analysis and AI-enriched context.", "knowsAbout": [ "Search Engine Optimization", "Google Search", "Core Web Vitals", "Technical SEO", "Content SEO", "Link building", "Structured data", "AI search optimization" ], "sameAs": [] } ] }</script> <!-- ==================== CHATBOT WIDGET ==================== --> <button id="cbw-launcher" type="button" aria-label="Ask the SEO assistant a question" title="Ask the SEO assistant a question"> <svg viewBox="0 0 24 24" width="26" height="26" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path> </svg> <span class="cbw-launcher-label">SEO Assistant</span> <span class="cbw-launcher-pulse" aria-hidden="true"></span> </button> <div id="cbw-panel" role="dialog" aria-label="SEO Assistant" aria-hidden="true"> <div class="cbw-head"> <div class="cbw-head-txt"> <strong>SEO Assistant</strong> <span>Powered by official Google declarations</span> </div> <div class="cbw-head-actions"> <a href="https://seoclaims.com/en/chatbot" class="cbw-icon-btn" title="Open full version" aria-label="Open full version"> <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"></path><path d="M10 14L21 3"></path><path d="M21 14v5a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5"></path></svg> </a> <button type="button" id="cbw-close" class="cbw-icon-btn" title="Close" aria-label="Close"> <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6L6 18"></path><path d="M6 6l12 12"></path></svg> </button> </div> </div> <div class="cbw-body" id="cbw-messages" aria-live="polite"> <div class="cbw-msg assistant cbw-welcome">Hi! Ask me anything about SEO and Google — I answer with cited sources from official declarations.</div> </div> <form class="cbw-form" id="cbw-form" autocomplete="off"> <textarea id="cbw-input" rows="1" placeholder="Ask a question..." maxlength="500" required></textarea> <button type="submit" id="cbw-send" aria-label="Send"> <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg> </button> </form> </div> <style> #cbw-launcher { position: fixed; bottom: 24px; right: 24px; z-index: 9998; display: flex; align-items: center; gap: 8px; padding: 14px 20px 14px 16px; background: linear-gradient(135deg, #7c3aed 0%, #5b21b6 100%); color: #fff; border: none; border-radius: 999px; box-shadow: 0 8px 24px rgba(124,58,237,.35); font-family: inherit; font-size: .93rem; font-weight: 600; cursor: pointer; transition: transform .15s ease, box-shadow .15s ease; } #cbw-launcher:hover { transform: translateY(-2px); box-shadow: 0 12px 28px rgba(124,58,237,.45); } #cbw-launcher svg { flex-shrink: 0; } .cbw-launcher-label { white-space: nowrap; } .cbw-launcher-pulse { position: absolute; top: -2px; right: -2px; width: 12px; height: 12px; background: #10b981; border: 2px solid #fff; border-radius: 50%; animation: cbwPulse 2s infinite; } @keyframes cbwPulse { 0%,100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.3); opacity: .7; } } #cbw-panel { position: fixed; bottom: 24px; right: 24px; z-index: 9999; width: 400px; max-width: calc(100vw - 32px); height: 560px; max-height: calc(100vh - 48px); background: #fff; border-radius: 16px; box-shadow: 0 20px 60px rgba(0,0,0,.2); display: grid; grid-template-rows: auto 1fr auto; overflow: hidden; transform: translateY(20px) scale(.96); opacity: 0; pointer-events: none; transition: transform .22s ease, opacity .22s ease; font-family: inherit; } #cbw-panel[aria-hidden="false"] { transform: translateY(0) scale(1); opacity: 1; pointer-events: auto; } #cbw-panel[aria-hidden="false"] ~ #cbw-launcher, body.cbw-open #cbw-launcher { opacity: 0; pointer-events: none; transform: scale(.8); } .cbw-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 16px; background: linear-gradient(135deg, #7c3aed, #5b21b6); color: #fff; } .cbw-head-txt strong { display: block; font-size: .97rem; line-height: 1.2; } .cbw-head-txt span { display: block; font-size: .75rem; opacity: .85; margin-top: 2px; } .cbw-head-actions { display: flex; gap: 6px; } .cbw-icon-btn { width: 32px; height: 32px; display: inline-flex; align-items: center; justify-content: center; background: rgba(255,255,255,.12); color: #fff; border: none; border-radius: 8px; cursor: pointer; text-decoration: none; transition: background .15s; } .cbw-icon-btn:hover { background: rgba(255,255,255,.22); } .cbw-body { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 12px; background: #fafafa; } .cbw-msg { max-width: 92%; padding: 10px 14px; border-radius: 12px; font-size: .9rem; line-height: 1.55; animation: cbwIn .2s ease-out; } @keyframes cbwIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: translateY(0); } } .cbw-msg.user { align-self: flex-end; background: linear-gradient(135deg, #1a73e8, #1557b0); color: #fff; border-bottom-right-radius: 3px; } .cbw-msg.assistant { align-self: flex-start; background: #fff; border: 1px solid #e2e8f0; border-bottom-left-radius: 3px; color: #1e293b; } .cbw-msg.assistant.error { background: #fef2f2; border-color: #fecaca; color: #991b1b; } .cbw-msg.cbw-welcome { background: linear-gradient(135deg, #f3e8ff, #ede9fe); border-color: #c4b5fd; color: #5b21b6; } .cbw-citation { display: inline-block; background: #ede9fe; color: #5b21b6; font-size: .75rem; font-weight: 700; padding: 0 6px; border-radius: 8px; margin: 0 2px; text-decoration: none; vertical-align: 1px; } .cbw-hint { display: inline-block; background: #fef3c7; color: #92400e; font-style: normal; font-size: .82rem; padding: 1px 7px; border-radius: 6px; margin-left: 4px; font-weight: 500; } .cbw-citation:hover { background: #ddd6fe; } .cbw-sources { margin-top: 10px; padding-top: 9px; border-top: 1px dashed #cbd5e1; } .cbw-sources-title { font-size: .7rem; text-transform: uppercase; letter-spacing: .4px; font-weight: 700; color: #64748b; margin-bottom: 6px; } .cbw-src { display: block; padding: 8px 10px; background: #fff; border: 1px solid #e2e8f0; border-left: 3px solid #7c3aed; border-radius: 6px; margin-bottom: 5px; font-size: .78rem; color: inherit; text-decoration: none; transition: transform .12s; } .cbw-src:hover { transform: translateX(2px); border-color: #7c3aed; } .cbw-src strong { display: block; color: #1e293b; margin-bottom: 1px; font-size: .8rem; line-height: 1.3; } .cbw-src .cbw-src-meta { color: #64748b; font-size: .7rem; } .cbw-src .cbw-src-id { display: inline-block; background: #ede9fe; color: #5b21b6; font-family: monospace; font-size: .68rem; font-weight: 700; padding: 0 5px; border-radius: 4px; margin-right: 4px; } .cbw-typing { display: flex; gap: 4px; padding: 10px 14px; } .cbw-typing span { width: 7px; height: 7px; border-radius: 50%; background: #7c3aed; opacity: .5; animation: cbwTyping 1.2s infinite; } .cbw-typing span:nth-child(2) { animation-delay: .15s; } .cbw-typing span:nth-child(3) { animation-delay: .3s; } @keyframes cbwTyping { 0%,80%,100% { transform: scale(.7); opacity: .4; } 40% { transform: scale(1); opacity: 1; } } .cbw-form { display: flex; gap: 8px; padding: 12px; background: #fff; border-top: 1px solid #e2e8f0; } .cbw-form textarea { flex: 1; padding: 10px 12px; border: 1px solid #cbd5e1; border-radius: 10px; font-size: .9rem; font-family: inherit; resize: none; min-height: 40px; max-height: 110px; outline: none; transition: border-color .15s; } .cbw-form textarea:focus { border-color: #7c3aed; box-shadow: 0 0 0 3px rgba(124,58,237,.12); } .cbw-form button { width: 42px; height: 42px; display: inline-flex; align-items: center; justify-content: center; background: linear-gradient(135deg, #7c3aed, #5b21b6); color: #fff; border: none; border-radius: 10px; cursor: pointer; flex-shrink: 0; } .cbw-form button:disabled { opacity: .5; cursor: not-allowed; } @media (max-width: 640px) { #cbw-launcher { bottom: 16px; right: 16px; padding: 12px 16px 12px 14px; font-size: .85rem; } .cbw-launcher-label { display: none; } #cbw-launcher { padding: 14px; } #cbw-panel { top: 0; bottom: 0; left: 0; right: 0; width: 100vw; max-width: 100vw; /* dvh = dynamic viewport height : se reduit quand le clavier mobile s'ouvre */ /* fallback vh pour navigateurs tres anciens */ height: 100vh; height: 100dvh; max-height: 100vh; max-height: 100dvh; border-radius: 0; } /* Laisse le clavier rogner l'espace du body, pas de la frame entiere */ .cbw-body { overscroll-behavior: contain; } } @media print { #cbw-launcher, #cbw-panel { display: none !important; } } </style> <script> (function() { const LANG = "en"; const T = {"sending":"...","send":"Send","sources":"Sources","rate_limit":"Limit of 10 questions per hour reached.","error":"Error. Please try again.","blocked":"API error, please contact an administrator.","too_short":"Question too short."}; const launcher = document.getElementById('cbw-launcher'); const panel = document.getElementById('cbw-panel'); const closeBtn = document.getElementById('cbw-close'); const msgsEl = document.getElementById('cbw-messages'); const formEl = document.getElementById('cbw-form'); const inputEl = document.getElementById('cbw-input'); const sendBtn = document.getElementById('cbw-send'); // Sync panel height to the visual viewport (keyboard-aware) sur mobile // Necessaire car Chrome Android n'honore pas toujours 100dvh correctement // quand le clavier virtuel s'ouvre : la panel reste a 100vh et le contenu // du haut est pousse hors ecran. function syncViewport() { if (!window.visualViewport) return; if (panel.getAttribute('aria-hidden') !== 'false') return; if (window.innerWidth > 640) { panel.style.height = ''; return; } panel.style.height = window.visualViewport.height + 'px'; } function open() { panel.setAttribute('aria-hidden', 'false'); document.body.classList.add('cbw-open'); syncViewport(); if (window.visualViewport) { window.visualViewport.addEventListener('resize', syncViewport); window.visualViewport.addEventListener('scroll', syncViewport); } // Afficher le message de bienvenue au chargement msgsEl.scrollTop = 0; setTimeout(() => inputEl.focus(), 200); } function close() { panel.setAttribute('aria-hidden', 'true'); document.body.classList.remove('cbw-open'); panel.style.height = ''; if (window.visualViewport) { window.visualViewport.removeEventListener('resize', syncViewport); window.visualViewport.removeEventListener('scroll', syncViewport); } } launcher.addEventListener('click', open); closeBtn.addEventListener('click', close); document.addEventListener('keydown', e => { if (e.key === 'Escape' && panel.getAttribute('aria-hidden') === 'false') close(); }); function esc(s) { return String(s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); } function renderAnswer(answer, sources, queryId) { const byId = {}; (sources || []).forEach(s => { byId[s.id] = s; }); let html = esc(answer).replace(/\[#(\d+)\]/g, (m, id) => { const s = byId[id]; if (!s) return ''; return '<a class="cbw-citation" href="' + esc(s.url) + '" target="_blank" rel="noopener" data-qid="' + (queryId||0) + '" data-sid="' + id + '" data-kind="citation">#' + id + '</a>'; }); // Markdown italique *texte* -> <em>texte</em> (non greedy, non multiligne) html = html.replace(/\*([^*\n]+?)\*/g, '<em class="cbw-hint">$1</em>'); return html.replace(/\n/g, '<br>'); } function renderSources(sources, queryId) { if (!sources || !sources.length) return ''; let out = '<div class="cbw-sources"><div class="cbw-sources-title">' + esc(T.sources) + '</div>'; sources.forEach(s => { const meta = [s.speaker, s.date].filter(Boolean).join(' · '); out += '<a class="cbw-src" href="' + esc(s.url) + '" target="_blank" rel="noopener" data-qid="' + (queryId||0) + '" data-sid="' + s.id + '" data-kind="source_card">' + '<strong><span class="cbw-src-id">#' + s.id + '</span>' + esc(s.title || '') + '</strong>' + '<div class="cbw-src-meta">' + meta + '</div></a>'; }); return out + '</div>'; } // Beacon de clic : non bloquant, n'empeche pas la navigation function logClick(qid, sid, kind) { if (!qid || !sid) return; const payload = JSON.stringify({ query_id: qid, source_id: sid, kind }); try { if (navigator.sendBeacon) { navigator.sendBeacon('/api/chatbot-click', new Blob([payload], {type: 'application/json'})); } else { fetch('/api/chatbot-click', { method: 'POST', headers: {'Content-Type':'application/json'}, body: payload, keepalive: true }).catch(() => {}); } } catch (e) {} } // Delegation d'evenement sur le conteneur messages msgsEl.addEventListener('click', function(e) { const a = e.target.closest('a[data-qid][data-sid]'); if (!a) return; logClick(parseInt(a.dataset.qid, 10), parseInt(a.dataset.sid, 10), a.dataset.kind || 'source_card'); }); function addUserMsg(txt) { const d = document.createElement('div'); d.className = 'cbw-msg user'; d.innerHTML = esc(txt).replace(/\n/g, '<br>'); msgsEl.appendChild(d); scroll(); } function addBot(data, isError) { const d = document.createElement('div'); d.className = 'cbw-msg assistant' + (isError ? ' error' : ''); if (typeof data === 'string') d.innerHTML = esc(data); else { const qid = data.query_id || 0; d.innerHTML = renderAnswer(data.answer || '', data.sources || [], qid) + renderSources(data.sources || [], qid); } msgsEl.appendChild(d); // Pas de scroll : on laisse l'utilisateur sur sa question, il scrolle lui-meme pour lire la reponse } function showTyping() { const d = document.createElement('div'); d.className = 'cbw-msg assistant'; d.id = 'cbw-typing'; d.innerHTML = '<div class="cbw-typing"><span></span><span></span><span></span></div>'; msgsEl.appendChild(d); // Pas de scroll sur l'indicateur de frappe non plus } function hideTyping() { const t = document.getElementById('cbw-typing'); if (t) t.remove(); } function scroll() { msgsEl.scrollTop = msgsEl.scrollHeight; } async function ask(question, origin) { origin = origin || 'widget'; addUserMsg(question); inputEl.value = ''; inputEl.style.height = 'auto'; sendBtn.disabled = true; showTyping(); try { const resp = await fetch('/api/chatbot', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ question, lang: LANG, referer: window.location.pathname, origin }) }); hideTyping(); const data = await resp.json(); if (!resp.ok) { let msg = T.error; if (data.error === 'rate_limit') msg = T.rate_limit; else if (data.error === 'blocked') msg = T.blocked; addBot(msg, true); } else { addBot(data, false); } } catch (e) { hideTyping(); addBot(T.error, true); } finally { sendBtn.disabled = false; inputEl.focus(); } } // API publique pour declencher le chatbot depuis d'autres pages // Usage : window.ChatbotSEO.ask("Ma question", "search_fallback") window.ChatbotSEO = { open, close, ask: function(question, origin) { if (!question || question.length < 3) return; open(); // Laisser l'animation d'ouverture se lancer puis envoyer setTimeout(() => ask(question, origin || 'search_fallback'), 350); } }; inputEl.addEventListener('input', function() { this.style.height = 'auto'; this.style.height = Math.min(this.scrollHeight, 110) + 'px'; }); inputEl.addEventListener('keydown', function(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); formEl.requestSubmit(); } }); formEl.addEventListener('submit', function(e) { e.preventDefault(); const q = inputEl.value.trim(); if (q.length < 3) { addBot(T.too_short, true); return; } ask(q); }); })(); </script> <nav class="mobile-nav" aria-label="Mobile navigation"> <a href="/en/" class="mobile-nav-item"> <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/></svg> <span>Search</span> </a> <a href="/en/declarations" class="mobile-nav-item"> <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg> <span>Categories</span> </a> <a href="/en/?browse=1&sort=date_desc" class="mobile-nav-item"> <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg> <span>Recent</span> </a> <a href="/" class="mobile-nav-item mobile-nav-lang"> <svg width="22" height="16" viewBox="0 0 640 480"> <rect width="213" height="480" fill="#002654"/><rect x="213" width="214" height="480" fill="#fff"/><rect x="427" width="213" height="480" fill="#ce1126"/> </svg> <span>FR</span> </a> </nav> <!-- Lightbox (self-contained) --> <div id="lightbox" style="display:none;position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,.9);align-items:center;justify-content:center;padding:16px;cursor:zoom-out;" onclick="closeLightbox()"> <button onclick="closeLightbox();event.stopPropagation();" style="position:absolute;top:12px;right:16px;z-index:100000;width:44px;height:44px;border:none;background:rgba(255,255,255,.2);color:#fff;font-size:1.6rem;border-radius:50%;cursor:pointer;" aria-label="Fermer">×</button> <img id="lightbox-img" src="" alt="" style="max-width:95%;max-height:90vh;border-radius:8px;box-shadow:0 8px 40px rgba(0,0,0,.5);" onclick="event.stopPropagation()"> </div> <script> function openLightbox(src){ var lb=document.getElementById('lightbox'); document.getElementById('lightbox-img').src=src; lb.style.display='flex'; document.body.style.overflow='hidden'; } function closeLightbox(){ document.getElementById('lightbox').style.display='none'; document.body.style.overflow=''; } document.addEventListener('keydown',function(e){if(e.key==='Escape')closeLightbox();}); function ytPlay(card) { var vid = card.dataset.vid; var ts = parseInt(card.dataset.ts) || 0; card.classList.add('playing'); card.innerHTML = '<iframe src="https://www.youtube-nocookie.com/embed/' + vid + '?autoplay=1&rel=0&modestbranding=1' + (ts > 0 ? '&start=' + ts : '') + '" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>'; } </script> </body> </html>