{
  "data": {
    "slug": "a6d1a896",
    "url": "https://andreashop.sk",
    "domain": "andreashop.sk",
    "overall_score": 59,
    "scores_json": {
      "seo": {
        "score": 73,
        "checks": [
          {
            "i18n": {
              "key": "seo.meta-title.pass",
              "params": {
                "chars": 50,
                "title": "Andreashop - Internetový obchod pre Vašu domácnosť"
              }
            },
            "name": "Meta Title",
            "value": "50 chars — \"Andreashop - Internetový obchod pre Vašu domácnosť\"",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "seo.meta-desc.pass",
              "params": {
                "chars": 158
              }
            },
            "name": "Meta Description",
            "value": "158 chars",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "seo.h1.fail"
            },
            "name": "H1 Heading",
            "status": "fail",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add exactly one H1 tag containing your primary keyword. It should be the most prominent heading on the page.",
            "whyMatters": "The H1 is a primary content signal for search engines. Pages without an H1 rank lower for target keywords."
          },
          {
            "i18n": {
              "key": "seo.h2.pass",
              "params": {
                "count": 2
              }
            },
            "name": "Content Structure (H2 Headings)",
            "value": "2 H2 subheadings found",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "seo.og.fail"
            },
            "name": "Open Graph Tags",
            "status": "fail",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add og:title, og:description, og:image (1200×630px), and og:type meta tags to every page.",
            "whyMatters": "Without OG tags, social platforms show auto-generated previews. Proper OG tags produce clean, clickable link cards."
          },
          {
            "i18n": {
              "key": "seo.twitter.pass",
              "params": {
                "card": "summary_large_image"
              }
            },
            "name": "Twitter/X Cards",
            "value": "Card type: summary_large_image",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "seo.canonical.pass",
              "params": {
                "url": "https://www.andreashop.sk/"
              }
            },
            "name": "Canonical URL",
            "value": "https://www.andreashop.sk/",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "seo.jsonld.pass",
              "params": {
                "count": 3,
                "types": "WebSite, SearchAction, BreadcrumbList"
              }
            },
            "name": "Structured Data (JSON-LD)",
            "value": "3 block(s): WebSite, SearchAction, BreadcrumbList",
            "status": "pass",
            "evidence": {
              "source": "schema"
            }
          },
          {
            "i18n": {
              "key": "seo.jsonld-valid.pass",
              "params": {
                "count": 3
              }
            },
            "name": "JSON-LD Validity",
            "value": "3 block(s) parse cleanly",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "seo.robots.pass-sitemap"
            },
            "name": "robots.txt",
            "value": "Present, references sitemap",
            "status": "pass",
            "evidence": {
              "source": "file-probe"
            }
          },
          {
            "i18n": {
              "key": "seo.sitemap.pass-count",
              "params": {
                "count": 10
              }
            },
            "name": "XML Sitemap",
            "value": "Found with ~10+ URLs",
            "status": "pass",
            "evidence": {
              "source": "file-probe"
            }
          },
          {
            "i18n": {
              "key": "seo.html-lang.pass",
              "params": {
                "lang": "sk"
              }
            },
            "name": "HTML Language Attribute",
            "value": "lang=\"sk\"",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "seo.img-alt.pass",
              "params": {
                "rate": 99,
                "total": 606
              }
            },
            "name": "Image Alt Attributes",
            "value": "99% of 606 images have alt text",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "seo.text-ratio.info",
              "params": {
                "ratio": 3,
                "words": 2685
              }
            },
            "name": "Text-to-HTML Ratio",
            "value": "3% ratio but 2685 words — content is substantial; the low ratio is markup/inline-JS bloat, not thin content",
            "status": "info",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Your text content is sufficient. To raise the ratio, move inline <script>/<style> to external files and trim template bloat — this is a performance/cleanliness win, not a content gap.",
            "whyMatters": "Text-to-HTML ratio is only a thin-content signal when actual word count is also low. With 500+ words, search engines have plenty to rank on."
          },
          {
            "i18n": {
              "key": "seo.favicon.pass"
            },
            "name": "Favicon",
            "value": "Favicon detected",
            "status": "pass",
            "evidence": {
              "source": "file-probe"
            }
          },
          {
            "i18n": {
              "key": "seo.img-format.warning-none",
              "params": {
                "jpg": 215,
                "png": 105
              }
            },
            "name": "Image Format Optimization",
            "value": "0% next-gen formats — 215 JPEG, 105 PNG images",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Convert all images to WebP format. Most CMS platforms have plugins for automatic WebP conversion. Use AVIF for even better compression.",
            "whyMatters": "Your images use legacy formats only. Switching to WebP typically reduces page weight by 30-50% — one of the highest-impact performance optimizations."
          },
          {
            "i18n": {
              "key": "seo.semantic.fail",
              "params": {
                "count": 1
              }
            },
            "name": "Semantic HTML Structure",
            "value": "1/6 semantic elements — poor structure",
            "status": "fail",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Your page lacks semantic HTML5 elements. Replace generic <div> wrappers with <header>, <nav>, <main>, <article>, <aside>, and <footer> to give search engines and screen readers proper content signals.",
            "whyMatters": "Without semantic structure, search engines treat your page as an undifferentiated blob of content. Semantic HTML is essential for featured snippets, voice search, and AI-powered search."
          },
          {
            "i18n": {
              "key": "seo.canonical-consistency.warning",
              "params": {
                "url": "https://www.andreashop.sk/"
              }
            },
            "name": "Canonical URL Consistency",
            "value": "Canonical points to different URL: https://www.andreashop.sk/",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Your canonical URL doesn't match the current page URL. Ensure the canonical points to the preferred version (with or without trailing slash, www vs non-www). Inconsistent canonicals confuse search engines.",
            "whyMatters": "A mismatched canonical tells Google this page is a duplicate of another URL. If unintentional, Google may ignore this page entirely in favor of the canonical target."
          },
          {
            "i18n": {
              "key": "seo.content-depth.pass",
              "params": {
                "words": 2685
              }
            },
            "name": "Content Depth",
            "value": "2685 words — sufficient content",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "seo.deep-heading.warning"
            },
            "name": "Deep Heading Hierarchy",
            "value": "No H3 subheadings for long content",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add H3 subheadings under your H2 sections to create a deeper content hierarchy. This helps search engines understand sub-topics and improves featured snippet eligibility.",
            "whyMatters": "Deep heading hierarchies (H2→H3→H4) help Google build a content outline. Pages with three or more heading levels rank for more keywords than flat-structured pages."
          },
          {
            "i18n": {
              "key": "seo.internal-links.pass",
              "params": {
                "count": 133
              }
            },
            "name": "Internal Linking",
            "value": "133 internal links — strong site navigation",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "seo.dead-links.warning",
              "params": {
                "hash": 6,
                "empty": 14,
                "total": 22,
                "jsvoid": 2
              }
            },
            "name": "Empty/Dead Links",
            "value": "22 dead links (14 empty, 6 hash-only, 2 javascript:void)",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Replace empty href='', href='#', and href='javascript:void(0)' with actual URLs. If interactive, use <button> instead of <a>.",
            "whyMatters": "Dead links waste crawl budget and confuse search engines. They also create poor user experience — users click expecting navigation and nothing happens."
          },
          {
            "i18n": {
              "key": "seo.a11y.warning",
              "params": {
                "missing": "ARIA labels, skip navigation link",
                "signals": 2
              }
            },
            "name": "Accessibility Fundamentals",
            "value": "2/4 a11y signals — missing: ARIA labels, skip navigation link",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add: ARIA landmark roles (role='navigation', role='main'), aria-label on interactive elements, a 'Skip to content' link, and lang attribute on <html>.",
            "whyMatters": "The European Accessibility Act (EAA) takes effect June 2025 for e-commerce. Non-compliant sites face fines. Accessibility also improves SEO, as Google treats it as a ranking signal."
          },
          {
            "i18n": {
              "key": "seo.img-dimensions.warning",
              "params": {
                "rate": 0
              }
            },
            "name": "Image Dimension Attributes",
            "value": "Only 0% of images have width/height attributes",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add width and height attributes to all <img> tags. This prevents layout shifts (CLS) and helps browsers allocate space before images load.",
            "whyMatters": "Missing image dimensions are a leading cause of Cumulative Layout Shift. Google uses CLS as a Core Web Vital ranking factor."
          }
        ]
      },
      "gdpr": {
        "score": 74,
        "checks": [
          {
            "i18n": {
              "key": "gdpr.cmp.info",
              "params": {
                "gtmNote": " A Google Tag Manager container is present, and CMPs/consent are very often loaded through GTM at runtime — which our server-side HTML scan can't see. Verify in the browser (Cookiebot/OneTrust/Usercentrics + Consent Mode fire on load) or expose the loader in the initial HTML."
              }
            },
            "name": "Cookie Consent Banner (CMP)",
            "value": "No CMP in server HTML, but GTM is present — consent banner may be injected at runtime (not verifiable server-side)",
            "status": "info",
            "evidence": {
              "source": "HTML-heuristic",
              "measured": "1 GTM container(s) detected"
            },
            "howToFix": "Confirm a consent management platform actually loads and blocks non-essential cookies until opt-in. A Google Tag Manager container is present, and CMPs/consent are very often loaded through GTM at runtime — which our server-side HTML scan can't see. Verify in the browser (Cookiebot/OneTrust/Usercentrics + Consent Mode fire on load) or expose the loader in the initial HTML.",
            "legalRefs": {
              "gdpr": [
                "Art. 6(1)(a)",
                "Art. 7"
              ],
              "skLaw": [
                "§ 14"
              ],
              "verified": [
                {
                  "title": "Zákonnosť spracúvania",
                  "source": "Zdroj: EUR-Lex (eur-lex.europa.eu), © Európska únia, 1998–2026. Autentické je iba znenie publikované v elektronickom Úradnom vestníku Európskej únie.",
                  "excerpt": "Article 6 — Zákonnosť spracúvania 1. Spracúvanie je zákonné iba vtedy a iba v tom rozsahu, keď je splnená aspoň jedna z týchto podmienok: Písmeno f) prvého pododseku sa nevzťahuje na spracúvanie vykonávané orgánmi verejnej moci pri výkone i",
                  "citation": "čl. 6 nariadenia (EÚ) 2016/679",
                  "jurisdiction": "eu"
                },
                {
                  "title": "Podmienky vyjadrenia súhlasu",
                  "source": "Zdroj: EUR-Lex (eur-lex.europa.eu), © Európska únia, 1998–2026. Autentické je iba znenie publikované v elektronickom Úradnom vestníku Európskej únie.",
                  "excerpt": "Article 7 — Podmienky vyjadrenia súhlasu 1. Ak je spracúvanie založené na súhlase, prevádzkovateľ musí vedieť preukázať, že dotknutá osoba vyjadrila súhlas so spracúvaním svojich osobných údajov. 2. Ak dá dotknutá osoba súhlas v rámci písom",
                  "citation": "čl. 7 nariadenia (EÚ) 2016/679",
                  "jurisdiction": "eu"
                },
                {
                  "title": "Podmienky poskytnutia súhlasu so spracúvaním osobných údajov",
                  "excerpt": "§ 14 Podmienky poskytnutia súhlasu so spracúvaním osobných údajov (1) Ak je spracúvanie osobných údajov založené na súhlase dotknutej osoby, prevádzkovateľ je povinný kedykoľvek vedieť preukázať, že dotknutá osoba poskytla súhlas so spracúv",
                  "citation": "§14 zákona č. 18/2018 Z.z.",
                  "jurisdiction": "sk"
                }
              ]
            },
            "confidence": "low",
            "whyMatters": "EU regulators enforce cookie consent (CNIL fined Google €150M). If the CMP is GTM-injected this scan can't confirm it — verify manually."
          },
          {
            "i18n": {
              "key": "gdpr.tracking.info",
              "params": {
                "count": 1,
                "gtmNote": " A Google Tag Manager container is present, and CMPs/consent are very often loaded through GTM at runtime — which our server-side HTML scan can't see. Verify in the browser (Cookiebot/OneTrust/Usercentrics + Consent Mode fire on load) or expose the loader in the initial HTML.",
                "trackers": "Google Analytics/GTM"
              }
            },
            "name": "Tracking Scripts & Consent",
            "value": "1 tracker(s) detected (Google Analytics/GTM); no CMP visible in server HTML but GTM is present",
            "status": "info",
            "evidence": {
              "source": "HTML-heuristic",
              "measured": "1 GTM container(s) detected"
            },
            "howToFix": "Confirm these trackers are gated behind consent (GTM consent mode / CMP script blocking) before they fire. A Google Tag Manager container is present, and CMPs/consent are very often loaded through GTM at runtime — which our server-side HTML scan can't see. Verify in the browser (Cookiebot/OneTrust/Usercentrics + Consent Mode fire on load) or expose the loader in the initial HTML.",
            "legalRefs": {
              "gdpr": [
                "Art. 6(1)(a)",
                "Art. 7",
                "EDPB Opinion 5/2019"
              ],
              "skLaw": [
                "§ 14"
              ],
              "verified": [
                {
                  "title": "Zákonnosť spracúvania",
                  "source": "Zdroj: EUR-Lex (eur-lex.europa.eu), © Európska únia, 1998–2026. Autentické je iba znenie publikované v elektronickom Úradnom vestníku Európskej únie.",
                  "excerpt": "Article 6 — Zákonnosť spracúvania 1. Spracúvanie je zákonné iba vtedy a iba v tom rozsahu, keď je splnená aspoň jedna z týchto podmienok: Písmeno f) prvého pododseku sa nevzťahuje na spracúvanie vykonávané orgánmi verejnej moci pri výkone i",
                  "citation": "čl. 6 nariadenia (EÚ) 2016/679",
                  "jurisdiction": "eu"
                },
                {
                  "title": "Podmienky vyjadrenia súhlasu",
                  "source": "Zdroj: EUR-Lex (eur-lex.europa.eu), © Európska únia, 1998–2026. Autentické je iba znenie publikované v elektronickom Úradnom vestníku Európskej únie.",
                  "excerpt": "Article 7 — Podmienky vyjadrenia súhlasu 1. Ak je spracúvanie založené na súhlase, prevádzkovateľ musí vedieť preukázať, že dotknutá osoba vyjadrila súhlas so spracúvaním svojich osobných údajov. 2. Ak dá dotknutá osoba súhlas v rámci písom",
                  "citation": "čl. 7 nariadenia (EÚ) 2016/679",
                  "jurisdiction": "eu"
                },
                {
                  "title": "Podmienky poskytnutia súhlasu so spracúvaním osobných údajov",
                  "excerpt": "§ 14 Podmienky poskytnutia súhlasu so spracúvaním osobných údajov (1) Ak je spracúvanie osobných údajov založené na súhlase dotknutej osoby, prevádzkovateľ je povinný kedykoľvek vedieť preukázať, že dotknutá osoba poskytla súhlas so spracúv",
                  "citation": "§14 zákona č. 18/2018 Z.z.",
                  "jurisdiction": "sk"
                }
              ]
            },
            "confidence": "low",
            "whyMatters": "Loading tracking before consent is a direct GDPR/ePrivacy violation, but GTM-managed consent gating isn't visible server-side — verify in the browser."
          },
          {
            "i18n": {
              "key": "gdpr.consent-mode.info",
              "params": {
                "gtmNote": " A Google Tag Manager container is present, and CMPs/consent are very often loaded through GTM at runtime — which our server-side HTML scan can't see. Verify in the browser (Cookiebot/OneTrust/Usercentrics + Consent Mode fire on load) or expose the loader in the initial HTML."
              }
            },
            "name": "Google Consent Mode v2",
            "value": "No Consent Mode signals in server HTML, but GTM is present — Consent Mode is commonly configured inside GTM",
            "status": "info",
            "evidence": {
              "source": "HTML-heuristic",
              "measured": "1 GTM container(s) detected"
            },
            "howToFix": "Confirm Google Consent Mode v2 default state (ad_storage/analytics_storage denied) is set in your GTM container. A Google Tag Manager container is present, and CMPs/consent are very often loaded through GTM at runtime — which our server-side HTML scan can't see. Verify in the browser (Cookiebot/OneTrust/Usercentrics + Consent Mode fire on load) or expose the loader in the initial HTML.",
            "legalRefs": {
              "gdpr": [
                "Art. 6(1)(a)",
                "Art. 7"
              ],
              "skLaw": [
                "§ 14"
              ],
              "verified": [
                {
                  "title": "Zákonnosť spracúvania",
                  "source": "Zdroj: EUR-Lex (eur-lex.europa.eu), © Európska únia, 1998–2026. Autentické je iba znenie publikované v elektronickom Úradnom vestníku Európskej únie.",
                  "excerpt": "Article 6 — Zákonnosť spracúvania 1. Spracúvanie je zákonné iba vtedy a iba v tom rozsahu, keď je splnená aspoň jedna z týchto podmienok: Písmeno f) prvého pododseku sa nevzťahuje na spracúvanie vykonávané orgánmi verejnej moci pri výkone i",
                  "citation": "čl. 6 nariadenia (EÚ) 2016/679",
                  "jurisdiction": "eu"
                },
                {
                  "title": "Podmienky vyjadrenia súhlasu",
                  "source": "Zdroj: EUR-Lex (eur-lex.europa.eu), © Európska únia, 1998–2026. Autentické je iba znenie publikované v elektronickom Úradnom vestníku Európskej únie.",
                  "excerpt": "Article 7 — Podmienky vyjadrenia súhlasu 1. Ak je spracúvanie založené na súhlase, prevádzkovateľ musí vedieť preukázať, že dotknutá osoba vyjadrila súhlas so spracúvaním svojich osobných údajov. 2. Ak dá dotknutá osoba súhlas v rámci písom",
                  "citation": "čl. 7 nariadenia (EÚ) 2016/679",
                  "jurisdiction": "eu"
                },
                {
                  "title": "Podmienky poskytnutia súhlasu so spracúvaním osobných údajov",
                  "excerpt": "§ 14 Podmienky poskytnutia súhlasu so spracúvaním osobných údajov (1) Ak je spracúvanie osobných údajov založené na súhlase dotknutej osoby, prevádzkovateľ je povinný kedykoľvek vedieť preukázať, že dotknutá osoba poskytla súhlas so spracúv",
                  "citation": "§14 zákona č. 18/2018 Z.z.",
                  "jurisdiction": "sk"
                }
              ]
            },
            "confidence": "low",
            "whyMatters": "Google requires Consent Mode v2 for EU traffic since March 2024, but GTM-side configuration is invisible to a server-side HTML scan — verify manually."
          },
          {
            "i18n": {
              "key": "gdpr.privacy-policy.pass",
              "params": {
                "url": "https://policies.google.com/privacy?hl=sk"
              }
            },
            "name": "Privacy Policy Page",
            "value": "https://policies.google.com/privacy?hl=sk",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "legalRefs": {
              "gdpr": [
                "Art. 12",
                "Art. 13",
                "Art. 14"
              ],
              "skLaw": [
                "§ 19",
                "§ 20"
              ],
              "verified": [
                {
                  "title": "Transparentnosť informácií, oznámenia a postupy výkonu práv dotknutej osoby",
                  "source": "Zdroj: EUR-Lex (eur-lex.europa.eu), © Európska únia, 1998–2026. Autentické je iba znenie publikované v elektronickom Úradnom vestníku Európskej únie.",
                  "excerpt": "Article 12 — Transparentnosť informácií, oznámenia a postupy výkonu práv dotknutej osoby 3. Prevádzkovateľ poskytne dotknutej osobe informácie o opatreniach, ktoré sa prijali na základe žiadosti podľa článkov 15 až 22, bez zbytočného odklad",
                  "citation": "čl. 12 nariadenia (EÚ) 2016/679",
                  "jurisdiction": "eu"
                },
                {
                  "title": "Informácie, ktoré sa majú poskytovať pri získavaní osobných údajov od dotknutej osoby",
                  "source": "Zdroj: EUR-Lex (eur-lex.europa.eu), © Európska únia, 1998–2026. Autentické je iba znenie publikované v elektronickom Úradnom vestníku Európskej únie.",
                  "excerpt": "Article 13 — Informácie, ktoré sa majú poskytovať pri získavaní osobných údajov od dotknutej osoby 2. Okrem informácií, ktoré sa uvádzajú v odseku 1, prevádzkovateľ poskytne dotknutej osobe pri získavaní osobných údajov tieto ďalšie informá",
                  "citation": "čl. 13 nariadenia (EÚ) 2016/679",
                  "jurisdiction": "eu"
                },
                {
                  "title": "Informácie, ktoré sa majú poskytnúť, ak osobné údaje neboli získané od dotknutej osoby",
                  "source": "Zdroj: EUR-Lex (eur-lex.europa.eu), © Európska únia, 1998–2026. Autentické je iba znenie publikované v elektronickom Úradnom vestníku Európskej únie.",
                  "excerpt": "Article 14 — Informácie, ktoré sa majú poskytnúť, ak osobné údaje neboli získané od dotknutej osoby 2. Okrem informácií uvedených v odseku 1 prevádzkovateľ poskytne dotknutej osobe tieto ďalšie informácie potrebné na zabezpečenie spravodliv",
                  "citation": "čl. 14 nariadenia (EÚ) 2016/679",
                  "jurisdiction": "eu"
                },
                {
                  "title": "Poskytované informácie, ak osobné údaje sú získané od dotknutej osoby",
                  "excerpt": "§ 19 Poskytované informácie, ak osobné údaje sú získané od dotknutej osoby (4) Odseky 1 až 3 sa neuplatňujú v rozsahu, v akom boli informácie dotknutej osobe poskytnuté pred spracúvaním osobných údajov.",
                  "citation": "§19 ods. 4 zákona č. 18/2018 Z.z.",
                  "jurisdiction": "sk"
                },
                {
                  "title": "Poskytované informácie, ak osobné údaje nie sú získané od dotknutej osoby",
                  "excerpt": "§ 20 Poskytované informácie, ak osobné údaje nie sú získané od dotknutej osoby (5) d) ak osobné údaje musia zostať dôverné na základe povinnosti mlčanlivosti podľa osobitného predpisu. 15 )",
                  "citation": "§20 ods. 5 písm. d) zákona č. 18/2018 Z.z.",
                  "jurisdiction": "sk"
                }
              ]
            }
          },
          {
            "i18n": {
              "key": "gdpr.cookie-policy.warning"
            },
            "name": "Cookie Policy",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Create a separate cookie policy page listing every cookie by: name, provider, purpose, category (necessary/analytics/marketing), and expiration. Most CMPs auto-generate this.",
            "legalRefs": {
              "gdpr": [
                "Art. 12",
                "Art. 13(1)(c)-(e)"
              ],
              "skLaw": [
                "§ 19"
              ],
              "verified": [
                {
                  "title": "Transparentnosť informácií, oznámenia a postupy výkonu práv dotknutej osoby",
                  "source": "Zdroj: EUR-Lex (eur-lex.europa.eu), © Európska únia, 1998–2026. Autentické je iba znenie publikované v elektronickom Úradnom vestníku Európskej únie.",
                  "excerpt": "Article 12 — Transparentnosť informácií, oznámenia a postupy výkonu práv dotknutej osoby 3. Prevádzkovateľ poskytne dotknutej osobe informácie o opatreniach, ktoré sa prijali na základe žiadosti podľa článkov 15 až 22, bez zbytočného odklad",
                  "citation": "čl. 12 nariadenia (EÚ) 2016/679",
                  "jurisdiction": "eu"
                },
                {
                  "title": "Informácie, ktoré sa majú poskytovať pri získavaní osobných údajov od dotknutej osoby",
                  "source": "Zdroj: EUR-Lex (eur-lex.europa.eu), © Európska únia, 1998–2026. Autentické je iba znenie publikované v elektronickom Úradnom vestníku Európskej únie.",
                  "excerpt": "Article 13 — Informácie, ktoré sa majú poskytovať pri získavaní osobných údajov od dotknutej osoby 2. Okrem informácií, ktoré sa uvádzajú v odseku 1, prevádzkovateľ poskytne dotknutej osobe pri získavaní osobných údajov tieto ďalšie informá",
                  "citation": "čl. 13 nariadenia (EÚ) 2016/679",
                  "jurisdiction": "eu"
                },
                {
                  "title": "Poskytované informácie, ak osobné údaje sú získané od dotknutej osoby",
                  "excerpt": "§ 19 Poskytované informácie, ak osobné údaje sú získané od dotknutej osoby (4) Odseky 1 až 3 sa neuplatňujú v rozsahu, v akom boli informácie dotknutej osobe poskytnuté pred spracúvaním osobných údajov.",
                  "citation": "§19 ods. 4 zákona č. 18/2018 Z.z.",
                  "jurisdiction": "sk"
                }
              ]
            },
            "whyMatters": "The ePrivacy Directive requires transparent cookie disclosure. Vague statements like 'we use cookies for functionality' don't meet the specificity requirement."
          },
          {
            "i18n": {
              "key": "gdpr.imprint.warning"
            },
            "name": "Legal Contact / Imprint Page",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add an imprint/about-us page with: company name, registered address, contact email, VAT number, and trade register info. In Germany/Austria/Switzerland this is legally required (Impressumspflicht).",
            "whyMatters": "In DACH countries, a missing Impressum can trigger fines and competitor cease-and-desist letters. For all EU stores, identifying the data controller is a GDPR Article 13 requirement."
          },
          {
            "i18n": {
              "key": "gdpr.terms.pass",
              "params": {
                "url": "/obchodne-podmienky-a274"
              }
            },
            "name": "Terms & Conditions Page",
            "value": "/obchodne-podmienky-a274",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "gdpr.mixed-content.pass"
            },
            "name": "Data Encryption (No Mixed Content)",
            "value": "All resources loaded over HTTPS",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "gdpr.third-party.pass",
              "params": {
                "count": 4
              }
            },
            "name": "Third-party Data Sharing",
            "value": "4 third-party domain(s)",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "gdpr.intl-transfers.warning",
              "params": {
                "count": 1,
                "trackers": "Google Analytics/GTM"
              }
            },
            "name": "International Data Transfers",
            "value": "1 US-based tracker(s) without consent: Google Analytics/GTM",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "US-based trackers transfer personal data outside the EU. Under GDPR, this requires: 1) User consent via CMP, 2) Standard Contractual Clauses (SCCs) with each provider, 3) Data Transfer Impact Assessment.",
            "whyMatters": "The EU-US Data Privacy Framework covers some transfers, but loading US trackers without consent remains a violation. Austrian and French DPAs have ruled Google Analytics non-compliant without proper safeguards."
          },
          {
            "i18n": {
              "key": "gdpr.erasure.warning"
            },
            "name": "Right to Erasure (Data Deletion)",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Provide a clear mechanism for users to request data deletion — either a dedicated page, a form, or explicit instructions in your privacy policy. Include a 'Delete my account' option in user settings.",
            "legalRefs": {
              "gdpr": [
                "Art. 17"
              ],
              "skLaw": [
                "§ 23"
              ],
              "verified": [
                {
                  "title": "Právo na vymazanie (právo „na zabudnutie“)",
                  "source": "Zdroj: EUR-Lex (eur-lex.europa.eu), © Európska únia, 1998–2026. Autentické je iba znenie publikované v elektronickom Úradnom vestníku Európskej únie.",
                  "excerpt": "Article 17 — Právo na vymazanie (právo „na zabudnutie“) 1. Dotknutá osoba má tiež právo dosiahnuť u prevádzkovateľa bez zbytočného odkladu vymazanie osobných údajov, ktoré sa jej týkajú, a prevádzkovateľ je povinný bez zbytočného odkladu vy",
                  "citation": "čl. 17 nariadenia (EÚ) 2016/679",
                  "jurisdiction": "eu"
                },
                {
                  "title": "Právo na výmaz osobných údajov",
                  "excerpt": "§ 23 Právo na výmaz osobných údajov (1) Dotknutá osoba má právo na to, aby prevádzkovateľ bez zbytočného odkladu vymazal osobné údaje, ktoré sa jej týkajú.",
                  "citation": "§23 ods. 1 zákona č. 18/2018 Z.z.",
                  "jurisdiction": "sk"
                }
              ]
            },
            "whyMatters": "GDPR Article 17 gives users the 'right to be forgotten.' EU regulators expect a clear, accessible process, and obstructing erasure requests is a documented enforcement finding."
          },
          {
            "i18n": {
              "key": "gdpr.dpo.pass"
            },
            "name": "Data Protection Officer Contact",
            "value": "DPO / data protection contact found",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "gdpr.withdrawal.warning"
            },
            "name": "Withdrawal of Consent Mechanism",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Provide a clear way to withdraw consent: an 'unsubscribe' link in emails, a 'cookie settings' button in the footer, and a 'revoke consent' section in your privacy policy.",
            "legalRefs": {
              "gdpr": [
                "Art. 7(3)"
              ],
              "skLaw": [
                "§ 14(4)"
              ],
              "verified": [
                {
                  "title": "Podmienky vyjadrenia súhlasu",
                  "source": "Zdroj: EUR-Lex (eur-lex.europa.eu), © Európska únia, 1998–2026. Autentické je iba znenie publikované v elektronickom Úradnom vestníku Európskej únie.",
                  "excerpt": "Article 7 — Podmienky vyjadrenia súhlasu 1. Ak je spracúvanie založené na súhlase, prevádzkovateľ musí vedieť preukázať, že dotknutá osoba vyjadrila súhlas so spracúvaním svojich osobných údajov. 2. Ak dá dotknutá osoba súhlas v rámci písom",
                  "citation": "čl. 7 nariadenia (EÚ) 2016/679",
                  "jurisdiction": "eu"
                },
                {
                  "title": "Podmienky poskytnutia súhlasu so spracúvaním osobných údajov",
                  "excerpt": "§ 14 Podmienky poskytnutia súhlasu so spracúvaním osobných údajov (1) Ak je spracúvanie osobných údajov založené na súhlase dotknutej osoby, prevádzkovateľ je povinný kedykoľvek vedieť preukázať, že dotknutá osoba poskytla súhlas so spracúv",
                  "citation": "§14 zákona č. 18/2018 Z.z.",
                  "jurisdiction": "sk"
                }
              ]
            },
            "whyMatters": "GDPR Article 7(3): 'It shall be as easy to withdraw as to give consent.' If users can subscribe in one click, unsubscribing must be equally simple. Missing this is a common regulatory finding."
          }
        ]
      },
      "nis2": {
        "score": -1,
        "checks": [
          {
            "i18n": {
              "key": "nis2.scope.unknown"
            },
            "name": "NIS2 Compliance",
            "value": "Scope undetermined — IČO/company enrichment unavailable for this domain.",
            "status": "info",
            "howToFix": "",
            "whyMatters": ""
          }
        ]
      },
      "mobile": {
        "score": 58,
        "checks": [
          {
            "i18n": {
              "key": "mob.viewport.pass"
            },
            "name": "Viewport Configuration",
            "value": "width=device-width, initial-scale=1",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "mob.perf.warning",
              "params": {
                "score": 53
              }
            },
            "name": "Mobile Performance Score",
            "value": "53/100 (target: 90+)",
            "status": "warning",
            "evidence": {
              "source": "PSI-lab"
            },
            "howToFix": "Optimize for mobile: compress images to WebP, defer non-critical JS, reduce CSS file size. Mobile CPUs are several times slower than desktop — what's fast on desktop is slow on mobile.",
            "whyMatters": "Most e-commerce traffic is mobile. Google ranks based on mobile performance, not desktop."
          },
          {
            "i18n": {
              "key": "mob.taptarget.warning",
              "params": {
                "detail": "Some tappable targets are too small or too close together (Lighthouse tap-targets audit)"
              }
            },
            "name": "Touch Target Size",
            "value": "Some tappable targets are too small or too close together (Lighthouse tap-targets audit)",
            "status": "warning",
            "evidence": {
              "source": "PSI-lab",
              "measured": "Some tappable targets are too small or too close together (Lighthouse tap-targets audit)"
            },
            "howToFix": "Ensure ALL interactive elements (buttons, links, form fields) are at least 48×48px with 8px minimum spacing between them. Pay special attention to: navigation menus, filter buttons, product variant selectors, and footer links.",
            "whyMatters": "Small tap targets cause mis-taps on mobile. In e-commerce, a mis-tap on 'Remove from cart' instead of 'Checkout' directly loses revenue."
          },
          {
            "i18n": {
              "key": "mob.fontsize.warning",
              "params": {
                "detail": "Some text renders below the 12px legibility threshold (Lighthouse font-size audit)"
              }
            },
            "name": "Font Size Readability",
            "value": "Some text renders below the 12px legibility threshold (Lighthouse font-size audit)",
            "status": "warning",
            "evidence": {
              "source": "PSI-lab",
              "measured": "Some text renders below the 12px legibility threshold (Lighthouse font-size audit)"
            },
            "howToFix": "Google's font-size audit flags text under 12px. Find the small text (often footer fine-print, captions, or cookie notices) and raise it; aim for a 16px+ body base with relative units (rem/em) so all text scales legibly on mobile.",
            "whyMatters": "Text below the legibility threshold forces mobile users to pinch-zoom. This measures the actual rendered page (Lighthouse), so it catches sub-12px nodes even when the base font looks fine."
          },
          {
            "i18n": {
              "key": "mob.contentwidth.pass"
            },
            "name": "Content Fits Viewport",
            "value": "No horizontal scrolling needed",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "mob.responsive.pass",
              "params": {
                "techniques": "Media queries"
              }
            },
            "name": "Responsive Design Techniques",
            "value": "Media queries detected",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "mob.themecolor.warning"
            },
            "name": "Theme Color",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add <meta name='theme-color' content='#your-brand-color'> to match your brand. Browsers use this to color the address bar, task switcher, and PWA chrome.",
            "whyMatters": "Theme-color creates a polished, branded mobile experience. It makes your site look native and professional."
          },
          {
            "i18n": {
              "key": "mob.nav.warning"
            },
            "name": "Mobile Navigation (Semantic)",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Wrap your navigation in a <nav> element. This helps mobile screen readers offer 'skip to navigation' and improves voice navigation (e.g., 'Siri, show me the menu').",
            "whyMatters": "Semantic <nav> elements are essential for mobile accessibility. Screen readers use them to let users jump directly to navigation — critical on small screens where content is long."
          },
          {
            "i18n": {
              "key": "mob.srcset.warning",
              "params": {
                "count": 606
              }
            },
            "name": "Responsive Images (srcset)",
            "value": "606 images without responsive sizing",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add srcset and sizes attributes to <img> tags to serve appropriately sized images for each screen. Mobile devices shouldn't download 1920px desktop images.",
            "whyMatters": "Without srcset, mobile users download full-size desktop images, wasting bandwidth. Responsive images are the single biggest mobile performance win for image-heavy sites."
          },
          {
            "i18n": {
              "key": "mob.inputtypes.warning",
              "params": {
                "issues": "phone fields use type='text' instead of type='tel'"
              }
            },
            "name": "Form Input Types",
            "value": "phone fields use type='text' instead of type='tel'",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Use semantic input types: type='email' for email (shows @ keyboard), type='tel' for phone (shows number pad), type='search' for search (shows search button). These trigger optimized mobile keyboards.",
            "whyMatters": "Correct input types show specialized mobile keyboards — email keyboard with @, phone with number pad. This reduces input errors and speeds up form completion."
          },
          {
            "i18n": {
              "key": "mob.stickycta.warning"
            },
            "name": "Mobile Sticky CTA",
            "value": "Add-to-cart button found but no sticky/fixed positioning detected",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add a sticky add-to-cart bar at the bottom of mobile screens. Use position: sticky or position: fixed with bottom: 0. The CTA should always be visible without scrolling.",
            "whyMatters": "Mobile users scroll extensively. A sticky add-to-cart bar keeps the primary action visible. Without it, users must scroll back up to purchase — many won't."
          },
          {
            "i18n": {
              "key": "mob.textoverflow.warning"
            },
            "name": "Text Overflow Handling",
            "value": "No word-break/overflow-wrap CSS detected",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add 'overflow-wrap: break-word' to your body or main content container. Without it, long URLs, product SKUs, or German compound words can break mobile layouts.",
            "whyMatters": "Long strings without word-break cause horizontal overflow on mobile — a common cause of 'content wider than viewport' failures. Compound words and URLs are frequent culprits."
          },
          {
            "i18n": {
              "key": "mob.payments.pass",
              "params": {
                "list": "Apple Pay",
                "count": 1
              }
            },
            "name": "Payment Methods Detected",
            "value": "1 method(s): Apple Pay",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "mob.express.warning",
              "params": {
                "missing": "Google Pay",
                "present": "Apple Pay"
              }
            },
            "name": "Express Checkout (Apple Pay + Google Pay)",
            "value": "Iba Apple Pay detekovaný — chýba Google Pay",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Pridaj Google Pay cez Stripe, Adyen alebo Mollie. Mobile conversion rate rastie keď je express checkout dostupný.",
            "whyMatters": "iOS/Android majú 50/50 share. Jeden bez druhého znamená stratený revenue na polovici mobilných používateľov."
          },
          {
            "i18n": {
              "key": "mob.wcaglabels.fail",
              "params": {
                "rate": 14,
                "total": 7,
                "missing": 6,
                "withLabel": 1
              }
            },
            "name": "Form Input Labels (WCAG 3.3.2)",
            "value": "Iba 1/7 inputs má label (14%)",
            "status": "fail",
            "fixLink": {
              "url": "https://www.inger.sk/#contact",
              "label": "Accessibility audit — Inger"
            },
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "6 input elementov nemá label. Každý input musí mať priradený <label for=\"id\">Text</label> alebo aria-label. Placeholder NIE je label (WCAG 3.3.2). Ak je checkout/registrácia formulár — toto znižuje konverziu a porušuje EN 301 549 (EAA 2026).",
            "whyMatters": "EAA 2026 (European Accessibility Act) vstupuje do platnosti 28.6.2025. E-shopy nad 10 zamestnancov alebo €2M obrat musia byť WCAG 2.1 AA kompatibilné — chýbajúce labely sú jedna z najčastejších žalovateľných chýb."
          },
          {
            "i18n": {
              "key": "mob.wcagheading.warning",
              "params": {
                "skips": "h2→h4"
              }
            },
            "name": "Heading Hierarchy (WCAG 1.3.1)",
            "value": "Preskočené úrovne: h2→h4",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Dodržuj poradie nadpisov h1 → h2 → h3 → h4 bez preskočenia. Screen readers používajú hierarchiu nadpisov na navigáciu. Ak potrebuješ menšie písmo ale rovnakú úroveň, použi CSS triedu, nie nižší heading tag.",
            "whyMatters": "Preskočené heading levely (napr. h1 priamo na h3) zlomia navigáciu pre screen reader používateľov a signalizujú Googlu zlú štruktúru dokumentu. Aj SEO je negatívne ovplyvnené."
          },
          {
            "i18n": {
              "key": "mob.wcaglinks.fail",
              "params": {
                "pct": 22,
                "count": 220,
                "total": 992
              }
            },
            "name": "Link Text Quality (WCAG 2.4.4)",
            "value": "220/992 odkazov (22%) má vágny text",
            "status": "fail",
            "fixLink": {
              "url": "https://www.inger.sk/#contact",
              "label": "Audit accessibility — Inger"
            },
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Vysoké % generických odkazov ('click here', 'read more', 'tu', 'viac'). Každý odkaz musí byť pochopiteľný mimo kontextu — 'Detail produktu Zimná bunda' namiesto len 'Detail'.",
            "whyMatters": "Nadmerný počet vágnych odkazov porušuje WCAG 2.4.4 (accessibility) aj SEO best practices (Google používa anchor text pre pochopenie cieľovej stránky). EAA 2026 compliance riziko."
          }
        ]
      },
      "company": null,
      "modules": [],
      "security": {
        "score": 53,
        "checks": [
          {
            "i18n": {
              "key": "sec.ssl.pass"
            },
            "name": "SSL/TLS Certificate",
            "value": "Valid HTTPS connection established",
            "status": "pass",
            "evidence": {
              "source": "SSL"
            }
          },
          {
            "i18n": {
              "key": "sec.dnssec.warning"
            },
            "name": "DNSSEC",
            "value": "No DNSKEY records — zone is unsigned",
            "status": "warning",
            "fixLink": {
              "url": "https://www.inger.sk/#contact",
              "label": "DNSSEC setup help →"
            },
            "evidence": {
              "source": "DNS"
            },
            "howToFix": "Enable DNSSEC at your DNS host (most modern registrars offer 1-click activation: Cloudflare, Route 53, Google Cloud DNS, web.sk, websupport.sk). Verify via dnsviz.net afterwards.",
            "whyMatters": "Without DNSSEC, attackers controlling intermediate resolvers can forge responses for your domain — sending users to phishing sites with valid HTTPS that match your name. EU national CSIRTs (SK-CERT, NÚKIB) recommend DNSSEC for all in-scope entities."
          },
          {
            "i18n": {
              "key": "sec.caa.warning"
            },
            "name": "CAA DNS Record",
            "value": "No CAA records — any CA can issue certificates for this domain",
            "status": "warning",
            "evidence": {
              "source": "DNS"
            },
            "howToFix": "Publish CAA TXT records pinning your CA. For Let's Encrypt: `0 issue \"letsencrypt.org\"`. For multiple CAs add additional `0 issue \"...\"` records. Add `0 iodef \"mailto:security@yourdomain.tld\"` for misissuance reports.",
            "whyMatters": "CAA records limit which Certificate Authorities can issue certificates for your domain. Without CAA, a compromised or rogue CA can issue valid certs that browsers will trust — a documented breach pattern (DigiNotar 2011, Symantec 2017)."
          },
          {
            "i18n": {
              "key": "sec.https-redirect.pass"
            },
            "name": "HTTP → HTTPS Redirect",
            "value": "HTTP properly redirects to HTTPS",
            "status": "pass",
            "evidence": {
              "source": "HTTP-header"
            }
          },
          {
            "i18n": {
              "key": "sec.hsts.fail"
            },
            "name": "HSTS (Strict-Transport-Security)",
            "status": "fail",
            "evidence": {
              "source": "HTTP-header"
            },
            "howToFix": "Add header: Strict-Transport-Security: max-age=31536000; includeSubDomains; preload — then submit to hstspreload.org.",
            "whyMatters": "Without HSTS, attackers can intercept the first HTTP request and downgrade the connection to steal session cookies on public WiFi."
          },
          {
            "i18n": {
              "key": "sec.csp.fail"
            },
            "name": "Content-Security-Policy (CSP)",
            "status": "fail",
            "fixLink": {
              "url": "https://www.inger.sk/#contact",
              "label": "Get Security Hardening →"
            },
            "evidence": {
              "source": "HTTP-header"
            },
            "howToFix": "Implement a CSP header. Start with: Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: — then gradually tighten.",
            "whyMatters": "CSP is a strong defense against XSS attacks. Without it, any injected script runs with full privileges. CSP blocks inline script injection."
          },
          {
            "i18n": {
              "key": "sec.clickjacking.fail"
            },
            "name": "Clickjacking Protection",
            "status": "fail",
            "evidence": {
              "source": "HTTP-header"
            },
            "howToFix": "Add X-Frame-Options: DENY (or SAMEORIGIN if iframes are needed). Better: use CSP frame-ancestors 'self'.",
            "whyMatters": "Clickjacking overlays your site in a hidden iframe. Attackers trick users into clicking buttons (like 'Confirm Purchase') without knowing it."
          },
          {
            "i18n": {
              "key": "sec.x-content-type.warning"
            },
            "name": "X-Content-Type-Options",
            "status": "warning",
            "evidence": {
              "source": "HTTP-header"
            },
            "howToFix": "Add header: X-Content-Type-Options: nosniff",
            "whyMatters": "Without nosniff, browsers may execute uploaded files as scripts. An attacker could upload a .jpg that's actually JavaScript and trick the browser into running it."
          },
          {
            "i18n": {
              "key": "sec.referrer-policy.pass",
              "params": {
                "detail": "no-referrer-when-downgrade"
              }
            },
            "name": "Referrer-Policy",
            "value": "no-referrer-when-downgrade",
            "status": "pass",
            "evidence": {
              "source": "HTTP-header"
            }
          },
          {
            "i18n": {
              "key": "sec.permissions-policy.warning"
            },
            "name": "Permissions-Policy",
            "status": "warning",
            "evidence": {
              "source": "HTTP-header"
            },
            "howToFix": "Add: Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=() — disable APIs your site doesn't need.",
            "whyMatters": "Without Permissions-Policy, any third-party script (ads, analytics, chat widgets) can access camera, microphone, and geolocation without your knowledge."
          },
          {
            "i18n": {
              "key": "sec.cookie-flags.warning",
              "params": {
                "count": 1,
                "missing": "1 missing Secure, 1 missing SameSite"
              }
            },
            "name": "Cookie Security Flags",
            "value": "1 cookie(s): 1 missing Secure, 1 missing SameSite",
            "status": "warning",
            "evidence": {
              "source": "HTTP-header"
            },
            "howToFix": "Set all cookies with: Secure (HTTPS only), HttpOnly (no JS access), SameSite=Lax or Strict (CSRF protection). Session cookies MUST have all three.",
            "whyMatters": "Missing Secure flag = cookies sent over HTTP (stealable on WiFi). Missing HttpOnly = cookies readable by XSS. Missing SameSite = vulnerable to CSRF attacks."
          },
          {
            "i18n": {
              "key": "sec.tech-disclosure.pass",
              "params": {
                "detail": "nginx"
              }
            },
            "name": "Technology Disclosure",
            "value": "Server: nginx (no version)",
            "status": "pass",
            "evidence": {
              "source": "HTTP-header"
            }
          },
          {
            "i18n": {
              "key": "sec.sri.warning",
              "params": {
                "eligible": 6,
                "exemptNote": " (1 auto-updating provider script(s) excluded — SRI not applicable)",
                "withIntegrity": 0
              }
            },
            "name": "Subresource Integrity (SRI)",
            "value": "Only 0/6 SRI-eligible third-party scripts have integrity hashes (1 auto-updating provider script(s) excluded — SRI not applicable)",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add integrity='sha384-...' and crossorigin='anonymous' to version-pinned third-party <script> tags (use srihash.org). Auto-updating provider scripts (analytics, payment SDKs, consent tools) are correctly excluded — they can't use SRI.",
            "whyMatters": "Without SRI, if a version-pinned third-party CDN is compromised, attackers can inject malicious code into your site — the same vector as the British Airways Magecart breach."
          },
          {
            "i18n": {
              "key": "sec.security-txt.warning"
            },
            "name": "security.txt (RFC 9116)",
            "status": "warning",
            "evidence": {
              "source": "file-probe"
            },
            "howToFix": "Create /.well-known/security.txt with Contact, Expires, and Preferred-Languages fields. See securitytxt.org for the generator.",
            "whyMatters": "security.txt lets ethical hackers report vulnerabilities responsibly. Without it, they may disclose publicly or not report at all."
          },
          {
            "i18n": {
              "key": "sec.server-version.pass",
              "params": {
                "name": "Nginx"
              }
            },
            "name": "Server Version Disclosure",
            "value": "Nginx — version hidden",
            "status": "pass",
            "evidence": {
              "source": "HTTP-header"
            }
          },
          {
            "i18n": {
              "key": "sec.cdn.warning"
            },
            "name": "CDN / WAF Protection",
            "status": "warning",
            "evidence": {
              "source": "HTTP-header"
            },
            "howToFix": "Add a CDN/WAF like Cloudflare (free tier), Sucuri, or Fastly. They provide DDoS protection, bot filtering, and SSL management.",
            "whyMatters": "Without a CDN/WAF, your origin server is directly exposed to DDoS attacks, bot traffic, and brute-force attempts."
          },
          {
            "i18n": {
              "key": "sec.iframe-sandbox.warning",
              "params": {
                "total": 15,
                "untrusted": 15,
                "trustedNote": ""
              }
            },
            "name": "Iframe Sandboxing",
            "value": "15/15 unknown iframe(s) without sandbox attribute",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add a sandbox attribute to non-provider <iframe> elements. Use sandbox='allow-scripts allow-same-origin' for third-party embeds. Trusted media/payment embeds (YouTube, Maps, Stripe, reCAPTCHA) are correctly excluded — they need full privileges to work.",
            "confidence": "low",
            "whyMatters": "Unsandboxed iframes from unknown sources can access your DOM, run scripts, and navigate the top window. They should be sandboxed to prevent clickjacking and XSS."
          },
          {
            "i18n": {
              "key": "sec.password-autocomplete.pass",
              "params": {
                "count": 1
              }
            },
            "name": "Password Field Security",
            "value": "1 password field(s) — allow password manager autofill",
            "status": "pass",
            "evidence": {
              "source": "HTTP-header"
            }
          }
        ]
      },
      "tech_stack": [
        {
          "name": "jQuery",
          "category": "js-library"
        },
        {
          "name": "Font Awesome",
          "category": "js-library"
        },
        {
          "name": "Swiper",
          "category": "js-library"
        },
        {
          "name": "reCAPTCHA",
          "category": "js-library"
        },
        {
          "name": "Nginx",
          "category": "server"
        }
      ],
      "performance": {
        "score": 58,
        "checks": [
          {
            "i18n": {
              "key": "perf.ttfb.pass",
              "params": {
                "ms": 127
              }
            },
            "name": "Server Response Time (TTFB)",
            "value": "127ms",
            "status": "pass",
            "evidence": {
              "source": "PSI-lab"
            }
          },
          {
            "i18n": {
              "key": "perf.fcp.pass",
              "params": {
                "s": "1.03"
              }
            },
            "name": "First Contentful Paint (FCP)",
            "value": "1.03s",
            "status": "pass",
            "evidence": {
              "source": "PSI-lab"
            }
          },
          {
            "i18n": {
              "key": "perf.lcp.warning",
              "params": {
                "s": "2.61"
              }
            },
            "name": "Largest Contentful Paint (LCP)",
            "value": "2.61s (good: <2.5s) — Core Web Vital",
            "status": "warning",
            "evidence": {
              "source": "PSI-lab"
            },
            "howToFix": "Optimize your largest element (usually hero image or product image): preload it with <link rel='preload'>, use WebP/AVIF format, set explicit width/height, and serve from CDN.",
            "whyMatters": "LCP is a Core Web Vital that directly impacts Google rankings. Sites failing LCP are demoted in search results. The most common cause of slow LCP is unoptimized hero images."
          },
          {
            "i18n": {
              "key": "perf.tbt.warning",
              "params": {
                "ms": 560
              }
            },
            "name": "Total Blocking Time (TBT)",
            "value": "560ms (good: <200ms)",
            "status": "warning",
            "evidence": {
              "source": "PSI-lab"
            },
            "howToFix": "Reduce JavaScript execution: defer non-critical scripts, code-split large bundles, remove unused plugins. Third-party scripts (analytics, chat, ads) are often the biggest offenders.",
            "whyMatters": "TBT measures how long the main thread is blocked. During this time, clicks and taps don't respond — your store feels frozen. This directly impacts perceived quality."
          },
          {
            "i18n": {
              "key": "perf.cls.pass",
              "params": {
                "cls": "0.000"
              }
            },
            "name": "Cumulative Layout Shift (CLS)",
            "value": "0.000 — Core Web Vital ✓",
            "status": "pass",
            "evidence": {
              "source": "PSI-lab"
            }
          },
          {
            "i18n": {
              "key": "perf.speed-index.warning",
              "params": {
                "s": "3.56"
              }
            },
            "name": "Speed Index",
            "value": "3.56s (good: <3.4s)",
            "status": "warning",
            "evidence": {
              "source": "PSI-lab"
            },
            "howToFix": "Speed Index measures visual completeness over time. Improve it by: prioritizing above-fold content, preloading critical resources, and minimizing render-blocking assets.",
            "whyMatters": "Speed Index captures the overall visual loading experience. A slow Speed Index means users watch content load piece by piece instead of seeing a complete page."
          },
          {
            "i18n": {
              "key": "perf.page-weight.fail",
              "params": {
                "mb": "4.1",
                "requests": 334
              }
            },
            "name": "Total Page Weight",
            "value": "4.1 MB, 334 requests — too heavy!",
            "status": "fail",
            "fixLink": {
              "url": "https://www.inger.sk/#contact",
              "label": "Get Performance Optimization →"
            },
            "evidence": {
              "source": "PSI-lab"
            },
            "howToFix": "Critical: your page is over 3 MB. 1) Convert all images to WebP/AVIF, 2) Lazy load everything below the fold, 3) Remove unused plugins, 4) Combine and minify CSS/JS, 5) Enable brotli compression.",
            "whyMatters": "Pages over 3 MB take 12+ seconds on 3G. The average e-commerce page is around 2 MB — you're well above that. Even small latency increases measurably reduce sales."
          },
          {
            "i18n": {
              "key": "perf.render-blocking.info"
            },
            "name": "Render-blocking Resources",
            "value": "Not measured — PageSpeed did not return the render-blocking audit for this URL",
            "status": "info",
            "evidence": {
              "source": "PSI-lab"
            },
            "howToFix": "Re-run the scan, or test directly at PageSpeed Insights. This metric needs a successful Lighthouse lab run."
          },
          {
            "i18n": {
              "key": "perf.unused-code.fail",
              "params": {
                "kb": 1109
              }
            },
            "name": "Unused Code (CSS + JS)",
            "value": "1109 KB wasted on unused code!",
            "status": "fail",
            "evidence": {
              "source": "PSI-lab"
            },
            "howToFix": "You're loading 1109 KB of code that isn't used on this page. 1) Audit plugins and remove unused ones, 2) Use code-splitting for page-specific JS, 3) Run PurgeCSS on your stylesheets.",
            "whyMatters": "Over 200 KB of unused code significantly slows parsing and execution. This is one of the easiest performance wins — removing dead code requires no trade-offs."
          },
          {
            "i18n": {
              "key": "perf.text-compression.pass"
            },
            "name": "Text Compression (gzip/brotli)",
            "value": "All text resources properly compressed",
            "status": "pass",
            "evidence": {
              "source": "HTTP-header"
            }
          },
          {
            "i18n": {
              "key": "perf.resource-hints.warning"
            },
            "name": "Resource Hints (Preload/Preconnect)",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add resource hints: <link rel='preconnect' href='https://fonts.googleapis.com'> for third-party origins, <link rel='preload' as='image' href='hero.webp'> for critical resources.",
            "whyMatters": "Preconnect saves 100-500ms per third-party origin by establishing connections early. Preload starts downloading critical resources before the browser discovers them in CSS/JS."
          },
          {
            "i18n": {
              "key": "perf.lazy-load.warning",
              "params": {
                "total": 606
              }
            },
            "name": "Lazy Loading",
            "value": "606 images without lazy loading",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add loading='lazy' to all images below the fold. Keep the hero/LCP image eager-loaded. Native lazy loading is supported by all modern browsers.",
            "whyMatters": "Without lazy loading, ALL images download on page load — even those never scrolled to. On a product page with 20 images, lazy loading can save most of the initial download."
          },
          {
            "i18n": {
              "key": "perf.cache-control.warning",
              "params": {
                "header": "no-store, no-cache, must-revalidate"
              }
            },
            "name": "Cache-Control Strategy",
            "value": "no-store, no-cache, must-revalidate",
            "status": "warning",
            "evidence": {
              "source": "HTTP-header"
            },
            "howToFix": "Set appropriate cache headers: static assets should have max-age=31536000 with versioned filenames. HTML pages can use max-age=0 with ETag for revalidation.",
            "whyMatters": "no-cache/no-store forces browsers to re-download resources on every visit. Repeat visitors load your entire site from scratch every time."
          },
          {
            "i18n": {
              "key": "perf.script-strategy.warning",
              "params": {
                "rate": 43,
                "total": 7
              }
            },
            "name": "Script Loading Strategy",
            "value": "Only 43% of 7 scripts use async/defer",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add 'defer' to scripts that don't need to run immediately, 'async' for independent scripts. Use type='module' for modern ES modules. Only critical inline scripts should be synchronous.",
            "whyMatters": "Synchronous scripts block HTML parsing — the browser stops rendering until each script downloads and executes. Async/defer allows parallel downloading without blocking."
          },
          {
            "i18n": {
              "key": "perf.css-count.warning",
              "params": {
                "count": 7
              }
            },
            "name": "CSS File Count",
            "value": "7 CSS files loaded",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Consolidate CSS files by bundling them into 1-3 files. Each CSS file is a separate HTTP request that blocks rendering until downloaded.",
            "whyMatters": "Each render-blocking CSS file adds network latency. Consolidating CSS from 8 to 2 files can save 200-400ms on first load."
          },
          {
            "i18n": {
              "key": "perf.critical-css.warning",
              "params": {
                "count": 7
              }
            },
            "name": "Critical CSS Strategy",
            "value": "7 CSS files without critical CSS extraction",
            "status": "warning",
            "evidence": {
              "source": "PSI-lab"
            },
            "howToFix": "Extract critical above-the-fold CSS and inline it in <head>. Load remaining CSS asynchronously: <link rel='preload' href='styles.css' as='style' onload='this.rel=\"stylesheet\"'>.",
            "whyMatters": "Render-blocking CSS delays first paint. Inlining critical CSS eliminates the render-blocking round trip — the biggest FCP improvement for CSS-heavy sites."
          },
          {
            "i18n": {
              "key": "perf.connection-hints.warning",
              "params": {
                "count": 4,
                "domains": "sub3.tpd.sk, www.google.com, cdn.luigisbox.com"
              }
            },
            "name": "Connection Hint Coverage",
            "value": "4 external domains without preconnect",
            "status": "warning",
            "evidence": {
              "source": "PSI-lab"
            },
            "howToFix": "Add <link rel='preconnect'> for key third-party domains: sub3.tpd.sk, www.google.com, cdn.luigisbox.com. Preconnect saves 100-300ms per domain by starting DNS+TCP+TLS early.",
            "whyMatters": "Third-party connections require DNS lookup, TCP handshake, and TLS negotiation. Preconnect performs these in parallel with HTML parsing, saving 100-300ms per origin."
          }
        ]
      },
      "ai_readiness": {
        "score": 58,
        "checks": [
          {
            "i18n": {
              "key": "air.bot-access.pass",
              "params": {
                "detail": "No AI bot restrictions (allowed by default)"
              }
            },
            "name": "AI Bot Access Policy",
            "value": "No AI bot restrictions (allowed by default)",
            "status": "pass",
            "evidence": {
              "source": "file-probe"
            }
          },
          {
            "i18n": {
              "key": "air.llms-txt.fail"
            },
            "name": "llms.txt (AI Site Descriptor)",
            "value": "Not present — not required for AI visibility",
            "status": "info",
            "fixLink": {
              "url": "https://llmstxt.org",
              "label": "About llms.txt →"
            },
            "evidence": {
              "source": "file-probe"
            },
            "howToFix": "Optional: /llms.txt (a Markdown site descriptor per llmstxt.org) is future-proofing, mainly consumed by coding/IDE agents. It does NOT affect whether ChatGPT/Perplexity/Gemini cite your store. Prioritize AI-crawler access, server-rendered content, statistics and cited sources instead.",
            "whyMatters": "No answer engine consumes llms.txt for AI-search citation today (2026) — adoption is ~8.7% and Google declined to support it. It's optional future-proofing, mainly read by coding/IDE agents. Prioritize AI-crawler access, server-rendered content, statistics and cited sources instead. See /ai-readiness-methodology.md."
          },
          {
            "i18n": {
              "key": "air.llms-full.warning"
            },
            "name": "llms-full.txt (Complete AI Content)",
            "value": "Not present — not required for AI visibility",
            "status": "info",
            "evidence": {
              "source": "file-probe"
            },
            "whyMatters": "No answer engine consumes llms.txt for AI-search citation today (2026) — adoption is ~8.7% and Google declined to support it. It's optional future-proofing, mainly read by coding/IDE agents. Prioritize AI-crawler access, server-rendered content, statistics and cited sources instead. See /ai-readiness-methodology.md."
          },
          {
            "i18n": {
              "key": "air.content-access.pass",
              "params": {
                "ratio": "2.9",
                "words": 2685
              }
            },
            "name": "Content Accessibility for AI",
            "value": "2685 words in raw HTML (2.9% text ratio) — readable by AI crawlers without executing JS",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "air.schema-foundation.warning-multi",
              "params": {
                "note": "",
                "count": 2,
                "types": "WebSite, BreadcrumbList"
              }
            },
            "name": "Structured Data Foundation",
            "value": "2 distinct schema types (WebSite, BreadcrumbList) — add more for comprehensive AI coverage",
            "status": "warning",
            "evidence": {
              "source": "schema"
            },
            "howToFix": "Expand your structured data with genuinely different types: Organization, Product, BreadcrumbList, WebSite with SearchAction, and FAQPage. Adding synonyms of the same entity (Store + LocalBusiness + Organization) does not help — variety of meaning does.",
            "whyMatters": "AI assistants (ChatGPT, Perplexity, Google AI Overviews) synthesize answers from structured data. Comprehensive schema markup makes your store easier to cite."
          },
          {
            "i18n": {
              "key": "air.org-schema.fail"
            },
            "name": "Organization Schema + Entity Linking",
            "status": "fail",
            "evidence": {
              "source": "schema"
            },
            "howToFix": "Add Organization (or LocalBusiness for physical stores) schema with: name, logo, url, description, contactPoint, address, and sameAs linking to all your official profiles (LinkedIn, Facebook, Wikipedia if available).",
            "whyMatters": "Organization schema is the foundation of your AI identity. Without it, AI assistants can't confidently attribute information to your brand, verify your legitimacy, or show your Knowledge Panel."
          },
          {
            "i18n": {
              "key": "air.faq-schema.warning"
            },
            "name": "FAQ Schema (Direct AI Answers)",
            "status": "warning",
            "evidence": {
              "source": "schema"
            },
            "howToFix": "Add FAQPage schema to every product page and category page. Include 3-5 Q&As per page covering: product specifications, shipping, returns, usage instructions. Format: question (full sentence) + answer (75-150 words).",
            "whyMatters": "ChatGPT, Perplexity, and Google AI Overviews pull FAQ answers verbatim. FAQ schema is one of the fastest ways to get your content cited by AI."
          },
          {
            "i18n": {
              "key": "air.breadcrumb.pass"
            },
            "name": "Breadcrumb Schema",
            "value": "BreadcrumbList structured data found — clear navigation hierarchy",
            "status": "pass",
            "evidence": {
              "source": "schema"
            }
          },
          {
            "i18n": {
              "key": "air.sitesearch.pass"
            },
            "name": "Site Search Schema (SearchAction)",
            "value": "WebSite SearchAction configured — AI can search your store",
            "status": "pass",
            "evidence": {
              "source": "schema"
            }
          },
          {
            "i18n": {
              "key": "air.content-depth.pass",
              "params": {
                "words": 2685
              }
            },
            "name": "Content Depth for AI",
            "value": "2685 words — rich content for AI analysis and citation",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "air.answer-first.warning"
            },
            "name": "Answer-First Content Format",
            "value": "Content doesn't start with a strong summary paragraph",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Place your most important information in the first 100 words of the page. Use the BLUF method (Bottom Line Up Front): start with what the product IS and why it matters, then elaborate.",
            "whyMatters": "AI assistants extract content from the first 100 words to generate summaries. Most AI systems read top-down, so answer-first pages get cited more often."
          },
          {
            "i18n": {
              "key": "air.headings.fail",
              "params": {
                "h1": 0,
                "h2": 2,
                "h3": 0
              }
            },
            "name": "Heading Hierarchy for AI",
            "value": "0 H1, 2 H2, 0 H3 — poor structure",
            "status": "fail",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Use exactly 1 H1 (page title), then organize content with H2 sections and H3 subsections. Each heading should describe the content that follows. Never skip heading levels (H1→H3 without H2).",
            "whyMatters": "AI extracts information based on heading structure. Pages with proper H1→H2→H3 hierarchy are parsed more accurately by ChatGPT, Perplexity, and Google AI Overviews. Without it, AI may misinterpret your content."
          },
          {
            "i18n": {
              "key": "air.semantic-html.fail"
            },
            "name": "Semantic HTML Structure",
            "status": "fail",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Replace <div> wrappers with: <header> (site header), <nav> (navigation), <main> (primary content), <article> (self-contained content), <aside> (sidebar), <footer>. This is the foundation of AI-readable HTML.",
            "whyMatters": "Without semantic HTML, AI must guess content boundaries. This leads to navigation text mixed into product descriptions, footer content cited as main content, and overall poor AI understanding of your pages."
          },
          {
            "i18n": {
              "key": "air.structured-content.warning-partial",
              "params": {
                "what": "25 lists",
                "missing": "comparison tables"
              }
            },
            "name": "Structured Content (Lists & Tables)",
            "value": "25 lists found — consider adding comparison tables",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add comparison tables to your content. Use <ul>/<ol> for feature lists, specifications, and benefits. Use <table> for product comparisons, pricing tiers, and specifications. AI extracts structured content much faster than paragraphs.",
            "whyMatters": "AI models are biased toward extracting data from HTML lists and tables. Perplexity and ChatGPT pull bullet points and table data with higher accuracy than paragraph text."
          },
          {
            "i18n": {
              "key": "air.freshness.fail"
            },
            "name": "Content Freshness Signals",
            "status": "fail",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add dateModified and datePublished to your JSON-LD schema, and display a visible 'Last updated' date on the page. Update content quarterly at minimum. AI heavily favors fresh, maintained content.",
            "whyMatters": "With no freshness signals, AI assumes your content is stale. ChatGPT and Perplexity both weight recency in their citation algorithms. Competitors who show recent updates will be cited instead of your static pages."
          },
          {
            "i18n": {
              "key": "air.entity-clarity.fail"
            },
            "name": "Entity Clarity & Brand Signals",
            "status": "fail",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Your brand has weak entity signals. Add: 1) Complete OG meta tags, 2) Organization schema with sameAs linking to all official profiles, 3) Consistent NAP (Name, Address, Phone) across the web.",
            "whyMatters": "AI assistants must be confident about entity identity before making recommendations. Without clear brand signals, AI defaults to better-known competitors."
          },
          {
            "i18n": {
              "key": "air.eeat.warning-noarticle"
            },
            "name": "Author Expertise Signals (E-E-A-T)",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "For content pages (blog, guides, about): add Article/BlogPosting schema with author property linking to Person schema. Include the author's jobTitle, credentials, and social profiles.",
            "whyMatters": "AI models weight author expertise heavily. Pages from identified experts get cited more than anonymous content. This is especially important for product guides, reviews, and advice content."
          },
          {
            "i18n": {
              "key": "air.heureka-feed.pass",
              "params": {
                "url": "/heureka.xml",
                "count": 775
              }
            },
            "name": "Heureka XML Feed",
            "value": "775+ items at /heureka.xml — all required fields present",
            "status": "pass",
            "evidence": {
              "source": "file-probe"
            }
          },
          {
            "i18n": {
              "key": "air.heureka-quality.pass",
              "params": {
                "detail": "EAN 100% · IMG 100% · DESC 100% · 31 cat · 0.1–699 € (n=50)"
              }
            },
            "name": "Heureka Feed Quality (sampled)",
            "value": "EAN 100% · IMG 100% · DESC 100% · 31 cat · 0.1–699 € (n=50)",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "whyMatters": "Heureka uses EAN to match items to its master catalog. 90%+ EAN and image coverage means your items rank in price comparisons rather than as orphan listings."
          },
          {
            "i18n": {
              "key": "air.extractable.warning-short",
              "params": {
                "avg": 8,
                "count": 4
              }
            },
            "name": "Extractable Answer Blocks",
            "value": "4 paragraphs, avg 8 words — too short for citation",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Optimal paragraphs for AI citation are 40-80 words. Break long paragraphs into focused, self-contained answer blocks. Each should make one clear point that AI can extract and quote.",
            "whyMatters": "AI extracts individual paragraphs as answer snippets — dense walls of text get skipped. Focused 40-80 word paragraphs are the most citable."
          },
          {
            "i18n": {
              "key": "air.statistics.pass",
              "params": {
                "count": 17
              }
            },
            "name": "Statistics & Data Presence",
            "value": "17 data points found — strong citation magnet",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "air.section-length.warning",
              "params": {
                "avg": 437,
                "ratio": 0
              }
            },
            "name": "Section Length Optimization",
            "value": "Avg section: 437 words — only 0% in 80-200 word optimal range",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Restructure content into sections of 120-180 words between H2/H3 headings. Each section should cover one topic completely. Split sections over 300 words, expand sections under 80 words.",
            "whyMatters": "For Google AI Overviews, 100-150 words per section is the sweet spot — long sections get skipped and very short ones lack substance."
          },
          {
            "i18n": {
              "key": "air.qa-headings.warning-none"
            },
            "name": "Q&A Format Headings",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add H2/H3 headings phrased as questions your customers ask: 'How much does shipping cost?', 'What sizes are available?', 'How do I return an item?' Follow each with a direct, concise answer.",
            "whyMatters": "Q&A content format matches how people query AI assistants. Without question-format headings, your content is harder for AI to map to user queries."
          },
          {
            "i18n": {
              "key": "air.question-coverage.pass",
              "params": {
                "n": 5,
                "labels": "price, shipping, returns/refunds, availability, payment"
              }
            },
            "name": "Common Question Coverage",
            "value": "Answers 5/5 key shopper questions: price, shipping, returns/refunds, availability, payment",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "air.internal-links.pass",
              "params": {
                "rate": 50
              }
            },
            "name": "Internal Link Density",
            "value": "50 contextual internal links per 1,000 words — strong knowledge graph signal",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "air.canonical.warning-different",
              "params": {
                "url": "https://www.andreashop.sk/"
              }
            },
            "name": "Canonical Tag for AI Deduplication",
            "value": "Canonical points to different URL: https://www.andreashop.sk/",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Verify this canonical is intentional. AI models cluster near-duplicate URLs and choose one representative page. If canonical points to a different URL, AI will only index that target URL, not this page.",
            "whyMatters": "AI search engines (ChatGPT, Perplexity, Bing Copilot) use canonicals to deduplicate content. A wrong canonical means AI may cite the wrong page version or ignore this page entirely."
          },
          {
            "i18n": {
              "key": "air.kg-readiness.warning",
              "params": {
                "count": 1,
                "missing": "@id in JSON-LD, sameAs links (Wikipedia, LinkedIn), Organization schema"
              }
            },
            "name": "Knowledge Graph Readiness",
            "value": "1/4 signals — missing: @id in JSON-LD, sameAs links (Wikipedia, LinkedIn), Organization schema",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add: @id in JSON-LD, sameAs links (Wikipedia, LinkedIn), Organization schema. Use @id in JSON-LD to create a unique node identifier. Ensure your brand name is identical in title, schema, and OG tags. Link to Wikipedia/Wikidata via sameAs.",
            "whyMatters": "@id creates a persistent entity identifier that connects your schema across pages and platforms, which is how AI builds a verified brand entity."
          },
          {
            "i18n": {
              "key": "air.readability.warning-high-technical",
              "params": {
                "grade": 15
              }
            },
            "name": "Content Readability for AI",
            "value": "Grade 15 — too complex for broad AI citation (technical threshold: 14)",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Simplify sentences (target 15-20 words average), use common words, break complex ideas into shorter paragraphs. AI extracts content for general audiences — if it's too academic, AI skips it.",
            "whyMatters": "Content above grade 12 readability is harder for AI to extract as clear, concise answers. Pages using clear headers and approachable language are cited more often."
          },
          {
            "i18n": {
              "key": "air.alt-text.fail",
              "params": {
                "pct": 45,
                "poor": 330,
                "missing": 3
              }
            },
            "name": "Image Alt Text Quality for AI",
            "value": "Only 45% quality alt text — 3 missing, 330 poor",
            "status": "fail",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Audit all images: add descriptive alt text (3-15 words) to every <img>. Include product names, features, materials, colors. This is critical for visual AI search and accessibility compliance.",
            "whyMatters": "Poor alt text means your product images are invisible to AI visual search (Google Lens, Bing Visual Search). This is also an accessibility requirement (WCAG 2.1 AA) — many regions enforce this legally."
          },
          {
            "i18n": {
              "key": "air.expert-quotes.warning-none"
            },
            "name": "Expert Quotations & Citations",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add 2-3 expert quotes or data citations per major page. Use <blockquote> for quotes and link to authoritative sources (.gov, .edu, Wikipedia, industry reports).",
            "whyMatters": "Content without citations or expert quotes appears unverified to AI. AI assistants prefer content backed by named sources, data references, and expert opinions."
          },
          {
            "i18n": {
              "key": "air.ai-txt.warning"
            },
            "name": "ai.txt (AI Permissions)",
            "value": "Not present — optional; AI-bot permissions are enforced via robots.txt, not ai.txt",
            "status": "info",
            "evidence": {
              "source": "file-probe"
            },
            "whyMatters": "ai.txt is an emerging fine-grained AI-permissions proposal not yet honored by the major engines. The robots.txt AI-bot rules are the signal that actually gates crawler access."
          },
          {
            "i18n": {
              "key": "air.webmcp.warning"
            },
            "name": "WebMCP Agentic Readiness",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "WebMCP (W3C Community Group standard, Chrome 146+) lets pages declare structured tools for AI agents. Add toolname and tooldescription attributes to <form> elements, or include a <script type='application/webmcp+json'> manifest.",
            "whyMatters": "WebMCP is how AI agents will interact with your store (search products, add to cart, check availability). Google and Microsoft are co-developing this standard."
          },
          {
            "i18n": {
              "key": "air.boilerplate.warning-nomain"
            },
            "name": "Content-to-Boilerplate Ratio",
            "value": "No <main> or <article> elements — AI can't isolate content from boilerplate",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add <main> around your primary content and <article> around self-contained content blocks. This creates clear boundaries for AI content extraction.",
            "whyMatters": "Without semantic containers, AI crawlers must guess where content starts and navigation ends. This leads to poor content extraction and fewer citations."
          },
          {
            "i18n": {
              "key": "air.social-proof.info-noncommercial",
              "params": {
                "detected": "None detected."
              }
            },
            "name": "Social Proof (Testimonials / Case Studies)",
            "value": "None detected. For a non-commercial/informational site, testimonials and case studies are optional",
            "status": "info",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Optional: member stories, event photos/recaps, partner logos, or press mentions build trust and give AI assistants more to cite about your organisation — but they don't gate credibility the way they do for a B2B vendor.",
            "confidence": "low",
            "whyMatters": "Client testimonials and 'trusted by' logos are a B2B sales-cycle lever. A non-profit, club, or informational site isn't selling a service, so their absence isn't a credibility gap — hence informational, not scored."
          },
          {
            "name": "Agent-Commerce Readiness",
            "value": "69/100 — čiastočne pripravené (Prístup 100 · Porozumenie 25 · Objaviteľnosť 100 · Transakcia 50)",
            "status": "info",
            "whyMatters": "Či dokáže autonómny nákupný AI agent (ChatGPT operator, Perplexity, budúce agentické asistenty) na vašom obchode: prísť dnu, prečítať a porozumieť produktom, objaviť celý katalóg a konať (kôš, podmienky). Toto meria málokto — a pre SK/CZ shopy nikto. Skóre spája AI-bot prístup, Product schema, produktové feedy (Merchant/Heureka/Zboží), WebMCP a nákupné podmienky do jedného agent-first pohľadu. Nadväzuje na pripravovaný verejný MCP konektor (\"naskenuj tento obchod\")."
          },
          {
            "name": "Agent: Prístup — dostane sa agent dnu",
            "value": "100/100 (2 signály/-ov)",
            "status": "info"
          },
          {
            "name": "Agent: Porozumenie — rozumie produktom",
            "value": "25/100 (2 signály/-ov)",
            "status": "info"
          },
          {
            "name": "Agent: Objaviteľnosť — nájde celý katalóg",
            "value": "100/100 (3 signály/-ov)",
            "status": "info"
          },
          {
            "name": "Agent: Transakcia — vie konať (kôš/podmienky)",
            "value": "50/100 (1 signál)",
            "status": "info"
          }
        ]
      },
      "phaseTimings": {
        "tail": 576,
        "total": 7609,
        "phase1": 4841,
        "preflight": 1937,
        "phase1.dns": 102,
        "phase1.html": 977,
        "phase1.files": 4838,
        "phase1.zbozi": 1004,
        "phase1.headers": 1494,
        "phase1.heureka": 1843,
        "phase1.merchant": 1828
      },
      "accessibility": {
        "score": 33,
        "checks": [
          {
            "i18n": {
              "key": "a11y.lang.pass",
              "params": {
                "lang": "sk"
              }
            },
            "name": "Page Language",
            "value": "<html lang=\"sk\"> is set",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "a11y.alt.warning",
              "params": {
                "total": 606,
                "missing": 7
              }
            },
            "name": "Image Alt Text",
            "value": "7 of 606 images missing alt text",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add alt text to the remaining images (alt=\"\" for decorative ones).",
            "whyMatters": "Every image missing alt text is content a screen-reader user cannot access (WCAG 1.1.1 / EN 301 549 § 9.1.1.1)."
          },
          {
            "i18n": {
              "key": "a11y.forms.fail",
              "params": {
                "total": 7,
                "unlabeled": 6
              }
            },
            "name": "Form Labels",
            "value": "6 of 7 form inputs have no associated label",
            "status": "fail",
            "weight": 2,
            "fixLink": {
              "url": "https://www.inger.sk/#contact",
              "label": "Accessibility audit — Inger →"
            },
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Associate every input with a <label for> (or aria-label / aria-labelledby). Placeholders are not labels.",
            "whyMatters": "Unlabelled fields leave screen-reader users guessing what to type — search, newsletter and checkout forms become unusable. EN 301 549 § 9.1.3.1 / 9.4.1.2 (WCAG 1.3.1 / 4.1.2)."
          },
          {
            "i18n": {
              "key": "a11y.headings.no-h1"
            },
            "name": "Heading Structure",
            "value": "The page has no <h1> heading",
            "status": "fail",
            "weight": 2,
            "fixLink": {
              "url": "https://www.inger.sk/#contact",
              "label": "Accessibility audit — Inger →"
            },
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add exactly one <h1> describing the page, then use <h2>/<h3> in order without skipping levels.",
            "whyMatters": "Screen-reader users navigate by headings; a missing or broken heading outline forces them to read linearly. EN 301 549 § 9.1.3.1 / 9.2.4.6 (WCAG 1.3.1 / 2.4.6)."
          },
          {
            "i18n": {
              "key": "a11y.links.vague",
              "params": {
                "total": 992,
                "vague": 220
              }
            },
            "name": "Link Text",
            "value": "220 of 992 links use vague text (\"click here\", \"read more\", \"here\")",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Rewrite links to describe their destination (\"View delivery options\" not \"click here\").",
            "confidence": "low",
            "whyMatters": "Screen-reader users often pull links out of context into a list; \"click here\" ×20 tells them nothing. EN 301 549 § 9.2.4.4 (WCAG 2.4.4)."
          },
          {
            "i18n": {
              "key": "a11y.landmarks.no-main"
            },
            "name": "Landmark Regions",
            "value": "No <main> landmark region found",
            "status": "fail",
            "weight": 1,
            "fixLink": {
              "url": "https://www.inger.sk/#contact",
              "label": "Accessibility audit — Inger →"
            },
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Wrap the primary content in <main> and use <nav>, <header>, <footer> for the page regions.",
            "whyMatters": "Landmarks let assistive-tech users jump straight to the main content and skip repeated navigation. EN 301 549 § 9.1.3.1 (WCAG 1.3.1)."
          },
          {
            "i18n": {
              "key": "a11y.zoom.pass"
            },
            "name": "Zoom & Scaling",
            "value": "Pinch-zoom is not disabled",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "a11y.skiplink.missing"
            },
            "name": "Skip Link",
            "value": "No skip-to-content link detected",
            "status": "warning",
            "weight": 1,
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Add a visually-hidden \"Skip to content\" link as the first focusable element, targeting <main id=\"main\">.",
            "confidence": "low",
            "whyMatters": "Keyboard and screen-reader users otherwise tab through the whole menu on every page. EN 301 549 § 9.2.4.1 (WCAG 2.4.1)."
          },
          {
            "i18n": {
              "key": "a11y.legal-basis"
            },
            "name": "EAA Legal Basis",
            "value": "In scope for the European Accessibility Act (in force 28.6.2025): SK zákon 351/2022 Z. z., CZ zákon 424/2023 Sb., Dir. (EU) 2019/882 — assessed against EN 301 549. Inger provides EAA remediation audits.",
            "status": "info",
            "fixLink": {
              "url": "https://www.inger.sk/#contact",
              "label": "Accessibility audit — Inger →"
            },
            "howToFix": "",
            "isUpsell": true,
            "whyMatters": ""
          }
        ]
      },
      "vulnerability": {
        "score": 59,
        "checks": [
          {
            "i18n": {
              "key": "vuln.cms-version.pass"
            },
            "name": "CMS Version Disclosure",
            "value": "No generator tag — CMS identity hidden",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "vuln.sensitive-files.pass"
            },
            "name": "Sensitive Files Exposed",
            "value": ".env, .git, composer.json — all properly blocked",
            "status": "pass",
            "evidence": {
              "source": "file-probe"
            }
          },
          {
            "i18n": {
              "key": "vuln.install-script.pass"
            },
            "name": "Install Script Exposed",
            "value": "No /install/ or /setup/ paths accessible",
            "status": "pass",
            "evidence": {
              "source": "file-probe"
            }
          },
          {
            "i18n": {
              "key": "vuln.directory-listing.pass"
            },
            "name": "Directory Listing",
            "value": "Disabled — file structure hidden",
            "status": "pass",
            "evidence": {
              "source": "file-probe"
            }
          },
          {
            "i18n": {
              "key": "vuln.admin-url.fail",
              "params": {
                "paths": "/admin"
              }
            },
            "name": "Admin Panel at Default URL",
            "value": "Accessible at: /admin",
            "status": "fail",
            "evidence": {
              "source": "file-probe"
            },
            "howToFix": "Rename your admin directory to a random, non-guessable path (e.g., /admin-x7k9m2). Add IP whitelisting via .htaccess, implement 2FA, and set up brute-force protection (fail2ban or similar).",
            "whyMatters": "Default admin URLs receive thousands of automated brute-force attempts daily. Botnets target /admin, /wp-admin, /administrator relentlessly. A renamed path blocks 99% of automated attacks."
          },
          {
            "i18n": {
              "key": "vuln.debug-mode.pass"
            },
            "name": "Debug Mode / Error Exposure",
            "value": "No debug indicators found in page output",
            "status": "pass",
            "evidence": {
              "source": "HTML-heuristic"
            }
          },
          {
            "i18n": {
              "key": "vuln.csrf.warning",
              "params": {
                "count": 39,
                "withCsrf": 37
              }
            },
            "name": "Form CSRF Protection",
            "value": "37/39 forms have CSRF tokens",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Ensure ALL forms include a CSRF token. In PrestaShop, use {$csrf_token} in templates. In WordPress, use wp_nonce_field().",
            "confidence": "low",
            "whyMatters": "Forms without CSRF tokens allow cross-site request forgery — an attacker's site can submit orders, change settings, or modify data on behalf of your logged-in users."
          },
          {
            "i18n": {
              "key": "vuln.spf.pass",
              "params": {
                "record": "v=spf1 mx include:mailgun.org include:_spf.vshosting.cloud ip4:93.185.102.135 ip"
              }
            },
            "name": "SPF Record (Email Security)",
            "value": "SPF configured: v=spf1 mx include:mailgun.org include:_spf.vshosting.cloud ip4:93.185.102.135 ip",
            "status": "pass",
            "evidence": {
              "source": "DNS"
            }
          },
          {
            "i18n": {
              "key": "vuln.dmarc.pass",
              "params": {
                "policy": "quarantine"
              }
            },
            "name": "DMARC Policy (Email Auth)",
            "value": "DMARC enforced: p=quarantine",
            "status": "pass",
            "evidence": {
              "source": "DNS"
            }
          },
          {
            "i18n": {
              "key": "vuln.dkim.pass",
              "params": {
                "selectors": "default"
              }
            },
            "name": "DKIM Signing (Email Auth)",
            "value": "DKIM configured (selectors: default)",
            "status": "pass",
            "evidence": {
              "source": "file-probe"
            }
          },
          {
            "i18n": {
              "key": "vuln.bimi.info"
            },
            "name": "BIMI Brand Indicators",
            "status": "info",
            "evidence": {
              "source": "file-probe"
            },
            "howToFix": "Once DMARC is set to p=quarantine or p=reject with 100% enforcement, publish a BIMI record: v=BIMI1; l=https://yourdomain.com/logo.svg — adds your brand logo to recipient inboxes in Gmail and Yahoo.",
            "whyMatters": "BIMI requires a VMC (Verified Mark Certificate) but adds your brand logo to recipient inboxes as a trust signal. Optional but high-impact for brand-focused shops."
          },
          {
            "i18n": {
              "key": "vuln.cross-origin.warning"
            },
            "name": "Cross-Origin Isolation",
            "status": "warning",
            "evidence": {
              "source": "HTTP-header"
            },
            "howToFix": "Add Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Resource-Policy: same-origin headers. These protect against Spectre-type side-channel attacks.",
            "whyMatters": "Without cross-origin isolation headers, your site is vulnerable to Spectre attacks that can leak sensitive data across browser tabs. These headers are required for SharedArrayBuffer and high-resolution timers."
          },
          {
            "i18n": {
              "key": "vuln.mixed-content.warning",
              "params": {
                "urls": "http://www.riesenia.com/",
                "count": 1
              }
            },
            "name": "Mixed Content Resources",
            "value": "1 HTTP resource(s) on HTTPS page",
            "status": "warning",
            "evidence": {
              "source": "file-probe"
            },
            "howToFix": "Replace HTTP URLs with HTTPS: http://www.riesenia.com/",
            "whyMatters": "Mixed content allows man-in-the-middle attacks on specific resources. Browsers increasingly block mixed content, breaking images and scripts on your site."
          },
          {
            "i18n": {
              "key": "vuln.inline-handlers.warning",
              "params": {
                "count": 37
              }
            },
            "name": "Inline Event Handlers",
            "value": "37 inline event handlers (onclick, onmouseover, etc.)",
            "status": "warning",
            "evidence": {
              "source": "HTML-heuristic"
            },
            "howToFix": "Move inline event handlers to external JavaScript files using addEventListener(). Inline handlers prevent proper CSP implementation (require 'unsafe-inline') and increase XSS attack surface.",
            "whyMatters": "Inline event handlers are a legacy pattern that prevents Content Security Policy enforcement. They also make XSS attacks easier — injected HTML attributes can execute JavaScript immediately."
          },
          {
            "i18n": {
              "key": "vuln.login-form.warning-autocomplete"
            },
            "name": "Login Form Security",
            "value": "Login form blocks password manager autofill",
            "status": "warning",
            "evidence": {
              "source": "file-probe"
            },
            "howToFix": "Remove autocomplete='off' from login forms. Password managers improve security by enabling unique, complex passwords per site.",
            "whyMatters": "NIST guidelines recommend allowing password autofill. Blocking it forces users to choose weak, memorable passwords — a leading cause of credential compromise."
          },
          {
            "i18n": {
              "key": "vuln.payment-page.fail",
              "params": {
                "issues": "No HSTS, No CSP, No X-Content-Type-Options, No clickjacking protection"
              }
            },
            "name": "Payment Page Security",
            "value": "Payment page missing: No HSTS, No CSP, No X-Content-Type-Options, No clickjacking protection",
            "status": "fail",
            "fixLink": {
              "url": "https://www.inger.sk/#contact",
              "label": "Get PCI Compliance Audit →"
            },
            "evidence": {
              "source": "file-probe"
            },
            "howToFix": "CRITICAL: Your payment page is missing security headers: No HSTS, No CSP, No X-Content-Type-Options, No clickjacking protection. PCI DSS Requirement 6.5 mandates protection against common vulnerabilities on pages handling card data.",
            "whyMatters": "Payment pages without proper security headers violate PCI DSS and make card data theft significantly easier. Payment processors can impose monthly fines for non-compliance."
          },
          {
            "i18n": {
              "key": "vuln.clickjacking.fail"
            },
            "name": "Clickjacking on Sensitive Page",
            "value": "Login/payment page without X-Frame-Options or CSP frame-ancestors",
            "status": "fail",
            "evidence": {
              "source": "file-probe"
            },
            "howToFix": "URGENT: Add X-Frame-Options: DENY and CSP frame-ancestors 'none' to pages with login forms or payment fields. Attackers can overlay your page in a transparent iframe.",
            "whyMatters": "Clickjacking on payment/login pages is a high-severity vulnerability. Users unknowingly submit credentials or payments through invisible iframes. PCI DSS requires frame-busting on payment pages."
          }
        ]
      },
      "executive_summary": {
        "en": "Your site has fundamental security and accessibility issues. Prioritize adding HTTPS protection (HSTS) across all pages. On the bright side, your SEO and GDPR compliance are solid.",
        "sk": "Váš web má základné problémy s bezpečnosťou a dostupnosťou. Prioritne pridajte HTTPS ochranu (HSTS) na všetky stránky. Naopak, SEO a GDPR compliance sú na dobrej úrovni."
      },
      "opendata_security": null
    },
    "created_at": "2026-07-28T06:26:54.951465+00:00",
    "status": "complete",
    "platform_detected": null,
    "company_ico": null,
    "company_name": null,
    "company_country": null,
    "company_nace": null,
    "company_size": null,
    "nis2_scope": null,
    "nis2_sector": null,
    "company_risk_score": null,
    "company_risk_level": null
  }
}