getreadystack commited on
Commit
52cef6a
·
verified ·
1 Parent(s): 1c5b85b

Upload index.html with huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +76 -18
index.html CHANGED
@@ -1,19 +1,77 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html><html lang="en"><meta charset="utf-8">
2
+ <meta name="viewport" content="width=device-width,initial-scale=1">
3
+ <title>Pre-Consent Tracker Audit for EU Client Websites</title>
4
+ <meta name="description" content="Finds the analytics, pixels, fonts and embeds that fire before your cookie banner is answered - in the source, before you deploy. 24 rules.">
5
+ <style>
6
+ :root{color-scheme:light dark}
7
+ body{margin:0;font:16px/1.6 system-ui,-apple-system,"Segoe UI",sans-serif;
8
+ background:#fbfbfa;color:#16161d}
9
+ @media(prefers-color-scheme:dark){body{background:#14141a;color:#e8e8ea}}
10
+ .wrap{max-width:880px;margin:0 auto;padding:28px 20px 64px}
11
+ h1{font-size:1.7rem;line-height:1.25;margin:.2em 0 .3em}
12
+ p.sub{margin:0 0 1.4em;opacity:.8}
13
+ textarea{width:100%;min-height:230px;font:13px/1.5 ui-monospace,Menlo,Consolas,monospace;
14
+ padding:12px;border:1px solid #c9c9d1;border-radius:10px;background:transparent;color:inherit;
15
+ box-sizing:border-box}
16
+ button{font:600 15px/1 inherit;padding:12px 22px;border:0;border-radius:10px;
17
+ background:#1d4ed8;color:#fff;cursor:pointer;margin:12px 0}
18
+ button:hover{background:#1a43bd}
19
+ table{border-collapse:collapse;width:100%;margin-top:14px;font-size:14px}
20
+ th,td{border-bottom:1px solid #d8d8e0;padding:8px 6px;text-align:left;vertical-align:top}
21
+ th{font-weight:600;opacity:.75}
22
+ td.n{width:64px;font:13px ui-monospace,monospace;opacity:.7}
23
+ .ok{padding:14px;border-radius:10px;background:#e8f5ec;color:#14532d;margin-top:14px}
24
+ @media(prefers-color-scheme:dark){.ok{background:#15321f;color:#bbf7d0}}
25
+ .next{margin-top:30px;padding:18px;border:1px solid #d8d8e0;border-radius:12px}
26
+ .next h2{font-size:1.05rem;margin:0 0 .5em}
27
+ .next ul{margin:.3em 0 1em 1.1em;padding:0}
28
+ a.buy{display:inline-block;font:600 15px/1 inherit;padding:13px 22px;border-radius:10px;background:#1d4ed8;color:#fff;text-decoration:none}
29
+ a.buy:hover{background:#1a43bd}
30
+ p.till{font-size:13.5px;opacity:.8;margin:.9em 0 .2em}
31
+ </style>
32
+ <script defer data-auto-init src="https://cdn.jsdelivr.net/npm/@polar-sh/checkout@latest/dist/embed.global.js"></script>
33
+ <div class="wrap">
34
+ <h1>Pre-Consent Tracker Audit for EU Client Websites</h1>
35
+ <p class="sub">Finds the analytics, pixels, fonts and embeds that fire before your cookie banner is answered - in the source, before you deploy. 24 rules.</p>
36
+ <textarea id="src" placeholder="Paste an HTML, JS, JSX, Vue or PHP template here"></textarea>
37
+ <button id="go">Audit this code</button>
38
+ <div id="out"></div>
39
+ <div class="next"><h2>When one clean file is not the job any more</h2>
40
+ <p>Sweep every file in the client repository in one pass, keep it clean on every save, write the dated CSV/JSON/HTML evidence report you hand to the client or the DPO, fail the CI build on a new violation, and apply the mechanical fixes in place.</p>
41
+ <ul><li>Audit the whole client repository</li><li>Dated evidence report (CSV, JSON or HTML)</li><li>Re-audit on every save</li><li>Machine-readable output for CI</li><li>Apply the mechanical fixes</li></ul>
42
+ <p class="till">$29 once &middot; one licence key per person or team seat &middot; 7-day full refund. Consent scanners bill $10&ndash;$55 per domain <em>every month</em>, and only see the page after it is live.</p>
43
+ <p><a class="buy" href="https://buy.polar.sh/polar_cl_GLS2NunfF1vyIe7l2lyyErVSUzDSWPxuCzqQg13lMym" data-polar-checkout data-polar-checkout-theme="light">Get the full version &mdash; $29</a></p>
44
+ </div>
45
+ </div>
46
+ <script>
47
+ var RULES=[{"pattern": "(<\\s*script)(?![^>]*text/plain)([^>]*src=[\"\\'][^\"\\']*googletagmanager\\.com)", "flags": "i", "message": "Google Tag Manager loads on page render, so it runs before the visitor answers the banner. Every tag inside the container inherits that timing.", "sev": "error", "fix": "Block it until consent: add type=\"text/plain\" data-cookieconsent=\"statistics\" so your CMP unblocks it after opt-in, or inject the tag from the consent callback.", "replace": "$1 type=\"text/plain\" data-cookieconsent=\"statistics\"$2"}, {"pattern": "(<\\s*script)(?![^>]*text/plain)([^>]*src=[\"\\'][^\"\\']*connect\\.facebook\\.net)", "flags": "i", "message": "The Meta pixel loads on page render, so it runs before the visitor answers the banner. It writes _fbp and sends the visitor IP to Meta.", "sev": "error", "fix": "Block it until consent: add type=\"text/plain\" data-cookieconsent=\"marketing\" so your CMP unblocks it after opt-in, or inject the tag from the consent callback.", "replace": "$1 type=\"text/plain\" data-cookieconsent=\"marketing\"$2"}, {"pattern": "(<\\s*script)(?![^>]*text/plain)([^>]*src=[\"\\'][^\"\\']*static\\.hotjar\\.com)", "flags": "i", "message": "Hotjar loads on page render, so it runs before the visitor answers the banner. Session replay records the visitor from the first frame.", "sev": "error", "fix": "Block it until consent: add type=\"text/plain\" data-cookieconsent=\"statistics\" so your CMP unblocks it after opt-in, or inject the tag from the consent callback.", "replace": "$1 type=\"text/plain\" data-cookieconsent=\"statistics\"$2"}, {"pattern": "(<\\s*script)(?![^>]*text/plain)([^>]*src=[\"\\'][^\"\\']*clarity\\.ms)", "flags": "i", "message": "Microsoft Clarity loads on page render, so it runs before the visitor answers the banner. Clarity replays sessions and needs consent before the script starts.", "sev": "error", "fix": "Block it until consent: add type=\"text/plain\" data-cookieconsent=\"statistics\" so your CMP unblocks it after opt-in, or inject the tag from the consent callback.", "replace": "$1 type=\"text/plain\" data-cookieconsent=\"statistics\"$2"}, {"pattern": "(<\\s*script)(?![^>]*text/plain)([^>]*src=[\"\\'][^\"\\']*snap\\.licdn\\.com)", "flags": "i", "message": "The LinkedIn Insight Tag loads on page render, so it runs before the visitor answers the banner. It drops li_sugr and li_fat_id on load.", "sev": "error", "fix": "Block it until consent: add type=\"text/plain\" data-cookieconsent=\"marketing\" so your CMP unblocks it after opt-in, or inject the tag from the consent callback.", "replace": "$1 type=\"text/plain\" data-cookieconsent=\"marketing\"$2"}, {"pattern": "(<\\s*script)(?![^>]*text/plain)([^>]*src=[\"\\'][^\"\\']*cdn\\.segment\\.com)", "flags": "i", "message": "Segment analytics.js loads on page render, so it runs before the visitor answers the banner. It fans the visitor out to every destination you have enabled.", "sev": "error", "fix": "Block it until consent: add type=\"text/plain\" data-cookieconsent=\"statistics\" so your CMP unblocks it after opt-in, or inject the tag from the consent callback.", "replace": "$1 type=\"text/plain\" data-cookieconsent=\"statistics\"$2"}, {"pattern": "(<\\s*script)(?![^>]*text/plain)([^>]*src=[\"\\'][^\"\\']*(?:googleadservices\\.com|doubleclick\\.net))", "flags": "i", "message": "A Google Ads tag loads on page render, so it runs before the visitor answers the banner. Conversion and remarketing cookies are never strictly necessary.", "sev": "error", "fix": "Block it until consent: add type=\"text/plain\" data-cookieconsent=\"marketing\" so your CMP unblocks it after opt-in, or inject the tag from the consent callback.", "replace": "$1 type=\"text/plain\" data-cookieconsent=\"marketing\"$2"}, {"pattern": "gtag\\(\\s*[\"\\']config[\"\\']\\s*,\\s*[\"\\']G-", "flags": "i", "message": "gtag(\"config\",\"G-...\") sends a page_view to Google Analytics the moment this line runs. That is the pre-consent hit regulators quote in cookie decisions.", "sev": "error", "fix": "Move the config call inside the callback that runs after analytics consent is granted."}, {"pattern": "fbq\\(\\s*[\"\\']init[\"\\']", "flags": "i", "message": "fbq(\"init\", ...) starts Meta tracking on this line, ahead of any consent check.", "sev": "error", "fix": "Call fbq(\"init\") only after the marketing category is granted."}, {"pattern": "ttq\\.load\\(", "flags": "i", "message": "ttq.load() starts the TikTok pixel here, before the banner is answered.", "sev": "error", "fix": "Load the TikTok pixel from the consent callback."}, {"pattern": "[\"\\']clarity[\"\\']\\s*,\\s*[\"\\']script[\"\\']", "flags": "i", "message": "This is the inline Microsoft Clarity loader. It injects the replay script itself, so a CMP that only blocks external tags will miss it.", "sev": "error", "fix": "Run the loader from the consent callback instead of on page load."}, {"pattern": "(consent[\"\\']\\s*,\\s*[\"\\']default[\"\\'][^\\n]*?)granted", "flags": "i", "message": "Google Consent Mode defaults to \"granted\". In the EU the default must be \"denied\" until the visitor opts in, otherwise consent mode changes nothing.", "sev": "error", "fix": "Set the storage keys to \"denied\" in the default call and grant them in the update call.", "replace": "$1denied"}, {"pattern": "(?:ad_storage|analytics_storage|ad_user_data|ad_personalization)[\"\\']?\\s*:\\s*[\"\\']granted", "flags": "i", "message": "A consent-mode storage key is set to \"granted\" here. Check that this is the update after opt-in and not the default state.", "sev": "warn", "fix": "Keep every storage key \"denied\" in the default call."}, {"pattern": "<link[^>]+href=[\"\\'][^\"\\']*fonts\\.(?:googleapis|gstatic)\\.com", "flags": "i", "message": "Google Fonts is pulled from Google servers. The Munich court held that handing the visitor IP to Google this way is not strictly necessary, so it needs consent - or self-hosting.", "sev": "error", "fix": "Download the woff2 files and serve them from your own domain."}, {"pattern": "@import[^\\n]*fonts\\.googleapis\\.com", "flags": "i", "message": "A stylesheet @imports Google Fonts, which reaches Google before any banner renders.", "sev": "error", "fix": "Self-host the font files and drop the @import."}, {"pattern": "(<iframe[^>]*src=[\"\\'][^\"\\']*)(?:www\\.)?youtube\\.com/embed", "flags": "i", "message": "A youtube.com/embed iframe sets Google advertising cookies as soon as the page renders. The youtube-nocookie host does not.", "sev": "warn", "fix": "Swap the host to www.youtube-nocookie.com.", "replace": "$1www.youtube-nocookie.com/embed"}, {"pattern": "player\\.vimeo\\.com/video/\\d+(?![^\"\\'\\s]*dnt=1)", "flags": "i", "message": "This Vimeo embed carries no dnt=1, so Vimeo tracks the visitor on load.", "sev": "warn", "fix": "Append dnt=1 to the embed URL."}, {"pattern": "(?:maps\\.googleapis\\.com|google\\.com/maps/embed)", "flags": "i", "message": "A Google Maps embed sends the visitor IP to Google on load. Supervisory authorities treat that as a transfer that needs consent.", "sev": "warn", "fix": "Show a static image and load the real map on click, after consent."}, {"pattern": "www\\.google\\.com/recaptcha/api\\.js", "flags": "i", "message": "reCAPTCHA loads Google code on every page it sits on and profiles the visitor, not just the form submission.", "sev": "warn", "fix": "Load it when the form is focused, and write down why it is strictly necessary."}, {"pattern": "(?:widget\\.intercom\\.io|client\\.crisp\\.chat|embed\\.tawk\\.to)", "flags": "i", "message": "A chat widget pulls third-party code and cookies on page load. Unless the visitor asked for the chat, it waits for consent.", "sev": "warn", "fix": "Load the widget on click, from the consent callback."}, {"pattern": "(?:browser\\.sentry-cdn\\.com|datadoghq-browser-agent|sessionReplaySampleRate|replaysSessionSampleRate)", "flags": "i", "message": "Session replay is switched on. Replay records what the visitor types and points at, which needs consent before the SDK starts.", "sev": "warn", "fix": "Keep the replay sample rate at 0 until the statistics category is granted."}, {"pattern": "document\\.cookie\\s*=", "flags": "i", "message": "This line writes a cookie directly. Anything that is not strictly necessary to deliver the page has to wait for consent, and the choice has to be recorded.", "sev": "warn", "fix": "Wrap the write in your consent check, or drop the cookie."}, {"pattern": "(?:localStorage|sessionStorage)\\.setItem\\(\\s*[\"\\'][^\"\\']*(?:_ga|_gid|utm|visitor|analytics|track|fbp)", "flags": "i", "message": "Storing an identifier in web storage is access to terminal equipment under ePrivacy, exactly like a cookie, so it needs the same consent.", "sev": "warn", "fix": "Write the identifier only after the visitor opts in."}, {"pattern": "navigator\\.sendBeacon\\(", "flags": "i", "message": "sendBeacon posts data without waiting for a response. Check that this call cannot run before the consent state is known.", "sev": "info", "fix": "Gate the call on the stored consent state."}];
48
+ function scan(t){var L=String(t).split(/\r?\n/),h=[],i,r,re;
49
+ for(i=0;i<L.length;i++){for(var k=0;k<RULES.length;k++){r=RULES[k];
50
+ try{re=new RegExp(r.pattern,r.flags||'')}catch(e){continue}
51
+ if(re.test(L[i]))h.push({line:i+1,msg:r.message})}}
52
+ return h}
53
+ document.getElementById('go').onclick=function(){
54
+ var t=document.getElementById('src').value,o=document.getElementById('out');
55
+ if(!t.trim()){o.innerHTML='';return}
56
+ var h=scan(t);
57
+ if(!h.length){o.innerHTML='<div class="ok">Nothing fires before consent here</div>';return}
58
+ var s='<table><tr><th>Line</th><th>Finding</th></tr>';
59
+ for(var i=0;i<h.length;i++){s+='<tr><td class="n">'+h[i].line+'</td><td>'+
60
+ String(h[i].msg).replace(/&/g,'&amp;').replace(/</g,'&lt;')+'</td></tr>'}
61
+ o.innerHTML=s+'</table>'};
62
+ </script>
63
  </html>
64
+
65
+ <section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get one email when this rule changes</b><div style="opacity:.78;margin:.35rem 0 .8rem">We watch the regulation and vendor sources behind Pre-Consent Tracker Audit for EU Client Websites every day. When a rule changes, you get a single email with what changed and the updated check. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Notify me</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">You're on the list. One email when it changes.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"consent-audit-eu",lang:"en",source:"rule_alert"})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script></section>
66
+
67
+
68
+ <section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://www.npmjs.com/package/@readystack/consent-audit-eu" rel="noopener" data-yj-install-door="npm" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'consent-audit-eu',src:'npm',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">npm</a><a href="https://www.npmjs.com/package/@readystack/consent-audit-eu" rel="noopener" data-yj-install-door="mcp" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'consent-audit-eu',src:'mcp',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">MCP server (npx)</a><a href="https://hub.docker.com/r/getreadystack/consent-audit-eu" rel="noopener" data-yj-install-door="docker" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'consent-audit-eu',src:'docker',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Docker Hub</a><pre style="margin:.5rem 0 0;padding:.6rem .8rem;background:#f3f5f8;border-radius:8px;font-size:.9rem;overflow:auto">npx @readystack/consent-audit-eu &lt;file&gt;
69
+ npx @readystack/consent-audit-eu --mcp
70
+ docker run --rm -v &quot;$PWD:/w&quot; getreadystack/consent-audit-eu /w</pre><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://buy.polar.sh/polar_cl_l6iN1uWt0FwWu7tBsczD0jWpP2vxFM54Wdwqb3KPi1G" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section>
71
+ <section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the complete version <span class="yj-price">$29</span></h2><p style="margin:.25rem 0 1rem;color:#41505f">This page is the working piece. The full pack has everything below.</p><p style="margin:0 0 1rem;color:#41505f">Finds the analytics, pixels, fonts and embeds that fire before your cookie banner is answered - in the source, before you deploy. 24 rules.</p><p style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">Consent scanners are billed per domain every month - CookieYes $10-$55 per domain per month, Cookiebot around EUR 30 since it doubled its base price in August 2025 - and they only see the page after it is live.</p><a href="https://buy.polar.sh/polar_cl_GLS2NunfF1vyIe7l2lyyErVSUzDSWPxuCzqQg13lMym" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $29</a></section>
72
+ <section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">More from ReadyStack</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Ready-to-use files. One-time download, no subscription.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/en/p/build-6-procreate-lettering-brushes-that-feel-like-ink-the-e"><span class="yj-rel-t">Build 6 Procreate Lettering Brushes That Feel Like Ink — The Exact-Settings Wo</span><span class="yj-rel-p">$39</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/real-estate-video-luts-for-interior-walkthroughs"><span class="yj-rel-t">Real-Estate Video LUTs for Interior Walkthroughs and Listing Exteriors</span><span class="yj-rel-p">$100</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/wordpress-ada-compliance-plugin"><span class="yj-rel-t">ADA Compliance Plugin for WordPress: Title II Grievance Form</span><span class="yj-rel-p">$60</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/ats-resume-template"><span class="yj-rel-t">ATS Resume Template — 6 Fill-In Word Documents for US Job Applications (Manage</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/docker-image-for-development"><span class="yj-rel-t">Docker Image for Development — Node.js 22 + TypeScript Dev Container with Post</span><span class="yj-rel-p">$19</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/wedding-photo-presets"><span class="yj-rel-t">Wedding Photographer Presets for Full-Day Wedding Galleries</span><span class="yj-rel-p">$44</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section><!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script><div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div>
73
+
74
+
75
+ <script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||''),src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}catch(e){}})();</script>
76
+
77
+ <script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use',src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script>