PageSourceSearch

https://meteozentrale.de/app/

html meteozentrale.de collected 2026-10-02 17:50:49 UTC 80,889 bytes, 1,374 lines download raw bytes

1<!doctype html>
2<html lang="de">
3<head>
4  <meta charset="utf-8">
5  <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
6  <title>Meteozentrale App: Wetter, Warnungen &amp; Regenradar</title>
7  <meta name="description" content="Wetter für Ihren Ort: aktuelle Warnungen, Regenradar, Pollen und 14-Tage-Trend. Meteozentrale-App kostenlos im Browser öffnen und Ihren Wetterort speichern.">
8  <meta name="theme-color" content="#07111f">
9  <meta name="robots" content="index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1">
10  <meta name="application-name" content="Meteozentrale App">
11  <meta name="mobile-web-app-capable" content="yes">
12  <meta name="apple-mobile-web-app-capable" content="yes">
13  <meta name="apple-mobile-web-app-title" content="Meteozentrale App">
14  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
15  <meta property="og:type" content="website">
16  <meta property="og:locale" content="de_DE">
17  <meta property="og:site_name" content="Meteozentrale">
18  <meta property="og:title" content="Meteozentrale App: Wetter, Warnungen &amp; Regenradar">
19  <meta property="og:description" content="Wetter für Ihren Ort: aktuelle Warnungen, Regenradar, Pollen und 14-Tage-Trend. Meteozentrale-App kostenlos im Browser öffnen und Ihren Wetterort speichern.">
20  <meta property="og:url" content="https://meteozentrale.de/app/">
21  <meta property="og:image" content="https://meteozentrale.de/assets/pwa/screenshot-app-home.png">
22  <meta property="og:image:width" content="1280">
23  <meta property="og:image:height" content="720">
24  <meta property="og:image:alt" content="Meteozentrale App Wetter-Cockpit mit Live-Lage und Warnsignalen">
25  <meta name="twitter:card" content="summary_large_image">
26  <meta name="twitter:title" content="Meteozentrale App: Wetter, Warnungen &amp; Regenradar">
27  <meta name="twitter:description" content="Wetter für Ihren Ort: aktuelle Warnungen, Regenradar, Pollen und 14-Tage-Trend. Meteozentrale-App kostenlos im Browser öffnen und Ihren Wetterort speichern.">
28  <meta name="twitter:image" content="https://meteozentrale.de/assets/pwa/screenshot-app-home.png">
29  <link rel="canonical" href="https://meteozentrale.de/app/">
30  <link rel="alternate" type="text/plain" title="KI-Kurzprofil der Meteozentrale App" href="/llms.txt">
31  <link rel="manifest" href="/manifest.webmanifest">
32  <link rel="apple-touch-icon" href="/assets/pwa/icons/meteozentrale-app-apple-180.png">
33  <link rel="stylesheet" href="/assets/pwa/app-v51.css?v=51.3.0">
34  
34<script type="application/ld+json">
35{
36    "@context": "https://schema.org",
37    "@graph": [
38        {
39            "@type": "Organization",
40            "@id": "https://meteozentrale.de/#organization",
41            "name": "Meteozentrale",
42            "url": "https://meteozentrale.de/",
43            "logo": {
44                "@type": "ImageObject",
45                "url": "https://meteozentrale.de/assets/pwa/icons/meteozentrale-app-512.png",
46                "width": 512,
47                "height": 512
48            }
49        },
50        {
51            "@type": "WebSite",
52            "@id": "https://meteozentrale.de/#website",
53            "name": "Meteozentrale.de",
54            "url": "https://meteozentrale.de/",
55            "inLanguage": "de-DE",
56            "publisher": {
57                "@id": "https://meteozentrale.de/#organization"
58            },
59            "potentialAction": {
60                "@type": "SearchAction",
61                "target": "https://meteozentrale.de/?s={search_term_string}",
62                "query-input": "required name=search_term_string"
63            }
64        },
65        {
66            "@type": "WebPage",
67            "@id": "https://meteozentrale.de/app/#webpage",
68            "url": "https://meteozentrale.de/app/",
69            "name": "Meteozentrale App: Wetter, Warnungen & Regenradar",
70            "description": "Wetter für Ihren Ort: aktuelle Warnungen, Regenradar, Pollen und 14-Tage-Trend. Meteozentrale-App kostenlos im Browser öffnen und Ihren Wetterort speichern.",
71            "isPartOf": {
72                "@id": "https://meteozentrale.de/#website"
73            },
74            "primaryImageOfPage": {
75                "@id": "https://meteozentrale.de/app/#primaryimage"
76            },
77            "mainEntity": {
78                "@id": "https://meteozentrale.de/app/#webapp"
79            },
80            "dateModified": "2026-10-02T19:50:47+02:00",
81            "inLanguage": "de-DE"
82        },
83        {
84            "@type": "ImageObject",
85            "@id": "https://meteozentrale.de/app/#primaryimage",
86            "url": "https://meteozentrale.de/assets/pwa/screenshot-app-home.png",
87            "width": 1280,
88            "height": 720,
89            "caption": "Meteozentrale App Wetter-Cockpit mit Live-Lage, Warnsignalen und Ortswetter."
90        },
91        {
92            "@type": [
93                "WebApplication",
94                "SoftwareApplication"
95            ],
96            "@id": "https://meteozentrale.de/app/#webapp",
97            "name": "Meteozentrale App",
98            "alternateName": [
99                "Meteozentrale PWA",
100                "Meteozentrale Mein Wetter"
101            ],
102            "url": "https://meteozentrale.de/app/",
103            "installUrl": "https://meteozentrale.de/app/",
104            "description": "Wetter für Ihren Ort: aktuelle Warnungen, Regenradar, Pollen und 14-Tage-Trend. Meteozentrale-App kostenlos im Browser öffnen und Ihren Wetterort speichern.",
105            "image": "https://meteozentrale.de/assets/pwa/screenshot-app-home.png",
106            "thumbnailUrl": "https://meteozentrale.de/assets/pwa/icons/meteozentrale-app-512.png",
107            "applicationCategory": "LifestyleApplication",
108            "applicationSubCategory": "WeatherApplication",
109            "operatingSystem": "Android, iOS, Windows, macOS, Linux",
110            "browserRequirements": "Moderner Browser mit JavaScript, LocalStorage, Web-App-Manifest und optionalem Standortzugriff.",
111            "softwareVersion": "81.8",
112            "isAccessibleForFree": true,
113            "inLanguage": "de-DE",
114            "publisher": {
115                "@id": "https://meteozentrale.de/#organization"
116            },
117            "provider": {
118                "@id": "https://meteozentrale.de/#organization"
119            },
120            "offers": {
121                "@type": "Offer",
122                "price": "0",
123                "priceCurrency": "EUR",
124                "availability": "https://schema.org/InStock",
125                "url": "https://meteozentrale.de/app/"
126            },
127            "featureList": [
128                "Installierbare Progressive Web App ohne Play-Store-Pflicht",
129                "Gespeicherter Wetter-Ort mit direkten Meteozentrale-Modulen",
130                "Live-Lage mit Datenstand, Warnsignalen und regionalem Warnzentrum",
131                "SkyGuard Stormtracker mit konservativem Radar-/Ortsabgleich ohne Zelltrajektorien- oder Ankunftsgarantie",
132                "72-Stunden-Pollenprognose und mehrstufiger Live-Daten-Fallback mit klar gekennzeichneter Reserve",
133                "MOSMIX-Wahrscheinlichkeiten, Dauerregen, Schnee, Glätte, Frost und regionale Warnzentrum-Karte",
134                "Optionaler Agrarblick mit modellierter Bodenfeuchte, Wasserbilanz, Spritzfenster und witterungsbedingter Befahrbarkeit",
135                "Optionale Push-Nachrichten und datensparsame Analytics mit Consent"
136            ],
137            "screenshot": [
138                {
139                    "@type": "ImageObject",
140                    "url": "https://meteozentrale.de/assets/pwa/screenshot-app-home.png",
141                    "width": 1280,
142                    "height": 720
143                }
144            ],
145            "audience": {
146                "@type": "Audience",
147                "audienceType": "Menschen, Landwirtschaft, Kommunen, Einsatzplanung und Wetterinteressierte in Deutschland"
148            },
149            "about": [
150                {
151                    "@type": "Thing",
152                    "name": "Wetterwarnung"
153                },
154                {
155                    "@type": "Thing",
156                    "name": "Unwetter"
157                },
158                {
159                    "@type": "Thing",
160                    "name": "Regenradar"
161                },
162                {
163                    "@type": "Thing",
164                    "name": "Agrarwetter"
165                },
166                {
167                    "@type": "Thing",
168                    "name": "Progressive Web App"
169                }
170            ]
171        }
172    ]
173}  </script>
173
174  
174<script>
175    window.mzLoadWebpushr = function(){
176      if (window.MZ_WEBPUSHR_LOAD_PROMISE) return window.MZ_WEBPUSHR_LOAD_PROMISE;
177      window.MZ_WEBPUSHR_LOAD_PROMISE = new Promise(function(resolve, reject){
178        window.webpushr = window.webpushr || function(){ (window.webpushr.q = window.webpushr.q || []).push(arguments); };
179        window.webpushr('setup', {'key':'BHaKHu4UT8Uuju1yAHj_XONQiF1yPVFcW1kigczkHkaRwbR-hJsaZNaCBivfBwcaQTLtPZdo24cA0PiLDFhcuVg'});
180        var js = document.createElement('script');
181        js.id = 'webpushr-jssdk';
182        js.async = true;
183        js.src = 'https://cdn.webpushr.com/app.min.js';
184        js.onload = function(){ window.MZ_WEBPUSHR_READY = true; resolve(true); };
185        js.onerror = function(){ reject(new Error('Push-Dienst konnte nicht geladen werden.')); };
186        document.head.appendChild(js);
187      });
188      return window.MZ_WEBPUSHR_LOAD_PROMISE;
189    };
190  </script>
190
191  
191<script>
192    window.MZ_ANALYTICS_ID = "G-1MMGHXXRHP";
193  window.MZ_APP_VERSION = 'v81.8-bericht-interaktion';
194    window.dataLayer = window.dataLayer || [];
195    function gtag(){ window.dataLayer.push(arguments); }
196
197    (function(){
198      var storedConsent = null;
199      try { storedConsent = localStorage.getItem('mz_analytics_consent_v1'); } catch(e) {}
200      var analyticsConsent = storedConsent === 'granted' ? 'granted' : 'denied';
201
202      gtag('consent', 'default', {
203        'analytics_storage': analyticsConsent,
204        'ad_storage': 'denied',
205        'ad_user_data': 'denied',
206        'ad_personalization': 'denied',
207        'wait_for_update': 500
208      });
209      gtag('js', new Date());
210      gtag('config', window.MZ_ANALYTICS_ID, {
211        'send_page_view': false,
212        'page_title': "Meteozentrale App: Wetter, Warnungen & Regenradar",
213        'page_path': location.pathname + location.search,
214        'page_location': location.href,
215        'app_version': window.MZ_APP_VERSION,
216        'transport_type': 'beacon'
217      });
218    })();
219  </script>
vendor: 66 bytes, lines 219-220
219
220  <script async src="https://www.googletagmanager.com/gtag/js?id=
220G-1MMGHXXRHP
vendor: 14 bytes, lines 220-221
220"></script>
221  
221<style>
222    .mz-analytics-consent{position:fixed;left:max(12px,env(safe-area-inset-left));right:max(12px,env(safe-area-inset-right));bottom:max(12px,env(safe-area-inset-bottom));z-index:99999;display:none;gap:12px;align-items:center;justify-content:space-between;border:1px solid rgba(148,163,184,.28);background:linear-gradient(180deg,rgba(15,23,42,.97),rgba(2,6,23,.97));box-shadow:0 18px 55px rgba(0,0,0,.45);border-radius:22px;padding:14px;color:#e5f7ff;font-family:Inter,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}.mz-analytics-consent.is-visible{display:flex}.mz-analytics-consent strong{display:block;margin-bottom:3px}.mz-analytics-consent span{display:block;color:#b6c5d8;font-size:13px;line-height:1.45}.mz-analytics-consent .mz-ac-actions{display:flex;gap:8px;flex:0 0 auto}.mz-analytics-consent button{border:1px solid rgba(148,163,184,.28);border-radius:999px;padding:10px 13px;font-weight:900;cursor:pointer}.mz-analytics-consent [data-analytics-accept]{background:#22d3ee;color:#04111d;border-color:transparent}.mz-analytics-consent [data-analytics-decline]{background:rgba(255,255,255,.06);color:#e5f7ff}prompticon3,promptpopup,#webpushr-notification-card-wrapper{display:none!important}@media(max-width:640px){.mz-analytics-consent{align-items:stretch;flex-direction:column;bottom:calc(78px + env(safe-area-inset-bottom))}.mz-analytics-consent .mz-ac-actions{display:grid;grid-template-columns:1fr 1fr}.mz-analytics-consent button{width:100%;min-height:44px}}
223    .quick-place-panel-v19{grid-template-columns:minmax(0,1fr)}.quick-place-panel-v19 .quick-actions{grid-template-columns:repeat(4,minmax(0,1fr));width:100%}.quick-place-panel-v19 .quick-actions a{min-width:0;white-space:normal}.quick-place-panel-v19 p{max-width:72ch}@media(max-width:700px){.quick-place-panel-v19 .quick-actions{grid-template-columns:repeat(2,minmax(0,1fr))}}
224    .mz-community-invite{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;margin:0 0 16px;border:1px solid rgba(34,211,238,.35);border-radius:14px;background:rgba(8,80,101,.28);font-size:14px}.mz-community-invite span{color:#d0dfeb}.mz-community-invite a{display:inline-flex;align-items:center;min-height:36px;color:#67e8f9;font-weight:800;text-decoration:none}.mz-community-invite a:focus-visible{outline:3px solid #fbbf24;outline-offset:3px}
225    .mz-community-panel{display:flex;align-items:center;justify-content:space-between;gap:24px;margin:0 0 20px;padding:24px;border:1px solid rgba(34,211,238,.42);border-radius:24px;background:linear-gradient(120deg,rgba(8,80,101,.48),rgba(15,23,42,.95));color:#e5f7ff}
226    .mz-community-copy{min-width:0;max-width:720px}.mz-community-copy h2{margin:8px 0;font-size:clamp(21px,3vw,28px);line-height:1.2}.mz-community-copy p{margin:0 0 10px;color:#d0dfeb;line-height:1.55}.mz-community-copy small{color:#aebfd0}
227    .mz-community-actions{display:flex;flex-direction:column;gap:10px;flex:0 0 auto}.mz-community-actions a{display:flex;align-items:center;justify-content:center;gap:8px;min-height:46px;padding:12px 16px;border:1px solid rgba(148,163,184,.35);border-radius:14px;color:#e5f7ff;text-decoration:none;font-weight:700;text-align:center}.mz-community-actions .mz-community-primary{background:#22d3ee;border-color:#22d3ee;color:#04111d}.quick-actions a.mz-community-quick{border-color:#22d3ee;color:#a5f3fc}.mz-community-actions a:focus-visible,.mz-community-quick:focus-visible{outline:3px solid #fbbf24;outline-offset:3px}
228    @media(max-width:760px){.mz-community-panel{align-items:stretch;flex-direction:column;gap:16px;padding:20px}.mz-community-actions{width:100%}}
229  </style>
230  <!-- Wetterberichte: isolierte WordPress-Leseansicht, 2026-09-08 -->
231  <link rel="stylesheet" href="/app/mz-articles-v2.css?v=20260908-2">
232<style>
233
234/* Report views and likes. */
235.mz-report-engagement{margin:22px 0;padding:16px;border:1px solid #355169;border-radius:16px;background:linear-gradient(135deg,#101f2e,#0d1824);box-shadow:0 12px 28px rgba(0,0,0,.18)}
236.mz-report-engagement-actions{display:flex;align-items:stretch;gap:12px;flex-wrap:wrap}
237.mz-report-view-stat,.mz-report-like{display:inline-flex;align-items:center;justify-content:center;gap:8px;min-height:46px;padding:10px 14px;border-radius:13px;font-size:15px;font-weight:750}
238.mz-report-view-stat{border:1px solid #3b5268;background:#132536;color:#d9e8f4}
239.mz-report-view-stat strong,.mz-report-like strong{font-variant-numeric:tabular-nums;font-size:16px}
240.mz-report-engagement-icon{font-size:18px;line-height:1}
241.mz-report-like{border:1px solid #735064;background:#291b28;color:#ffe7f0;cursor:pointer;transition:background .18s ease,border-color .18s ease,transform .18s ease}
242.mz-report-like:hover{border-color:#f472a7;background:#3a2032;transform:translateY(-1px)}
243.mz-report-like.is-liked,.mz-report-like[aria-pressed="true"]{border-color:#fb7185;background:#5b2035;color:#fff}
244.mz-report-like-heart{font-size:21px;line-height:1;color:#fb7185}.mz-report-like.is-liked .mz-report-like-heart{color:#fff}
245.mz-report-engagement-status{margin:10px 2px 0!important;color:#9fb4c5!important;font-size:12px!important;line-height:1.45!important}
246.mz-report-engagement-status[data-tone="success"]{color:#86efac!important}.mz-report-engagement-status[data-tone="error"]{color:#fda4af!important}
247@media(max-width:600px){.mz-report-engagement{padding:14px}.mz-report-engagement-actions{display:grid;grid-template-columns:1fr 1fr}.mz-report-view-stat,.mz-report-like{min-width:0;padding:10px 8px;font-size:13px}}
248
249</style>
250</head>
251<body>
252  <div class="mz-analytics-consent" data-analytics-consent role="dialog" aria-live="polite" aria-label="Analyse-Einstellungen">
253    <div>
254      <strong>App besser machen?</strong>
255      <span>Mit deiner Zustimmung messen wir anonymisierte Nutzung: welche Module geöffnet werden, ob die Lagekarte hilft und wo die App verbessert werden muss. GPS-Koordinaten werden nicht an Analytics gesendet.</span>
256    </div>
257    <div class="mz-ac-actions">
258      <button type="button" data-analytics-decline>Nein</button>
259      <button type="button" data-analytics-accept>Einverstanden</button>
260    </div>
261  </div>
262  <div class="mz-app" id="app-start" data-place-root>
263    <header class="app-top">
264      <a class="brand" href="/app/" aria-label="Meteozentrale App Startseite">
265        <span class="brand-dot" aria-hidden="true"></span>
266        <span><span class="brand-title">Meteozentrale</span><br><span class="brand-sub">Mein Wetter</span></span>
267      </a>
268      <div class="top-actions">
269        <button class="install-btn" type="button" data-install-app>Installieren</button>
270        <button class="view-settings-btn" type="button" data-view-settings-open aria-haspopup="dialog">Einstellungen</button>
271        <a class="pill website-link" href="/">Zur Website</a>
272      </div>
273    </header>
274
275    <nav class="app-bottom-nav" aria-label="Hauptnavigation">
276      <a href="#app-start"><span aria-hidden="true">⌂</span><b>Start</b></a>
277      <a data-module="meteoz" data-needs-place href="/meteo/meteoz.php"><span aria-hidden="true">⚠</span><b>Warnungen</b></a>
278      <a data-module="forecast5" data-needs-place href="/meteo/5tage.php"><span aria-hidden="true">▦</span><b>5 Tage</b></a>
279      <a data-module="meteogram" data-needs-place href="/meteo/meteogram.php"><span aria-hidden="true">📈</span><b>14 Tage</b></a>
280      <button type="button" data-main-menu-open><span aria-hidden="true">☷</span><b>Menü</b></button>
281    </nav>
282
283    <div class="browser-notice" data-browser-notice hidden>
284      <strong>Bitte im normalen Browser oeffnen</strong>
285      <span>Facebook, Instagram und manche Messenger blockieren GPS, Push und App-Installation. Oeffne diesen Link in Safari, Chrome oder dem Huawei-Browser.</span>
286      <a data-browser-url href="/app/">App-Link</a>
287    </div>
288
289    <div class="mz-community-invite mz-weather-content"><span><span aria-hidden="true">📷</span> Dein Wetter zählt</span><a data-community-entry="kopf" href="/wettermelder/#mitmachen">Jetzt melden →</a></div>
290
291    <section class="start-grid-v19" aria-label="Persönliches Wetter und Warnungen">
292      <section class="live-start-v11 live-start-v18 live-start-v19" data-live-root data-live-version="19" data-live-state="empty" aria-label="Live-Lage für gespeicherten Ort">
293        <article class="live-main">
294          <div class="live-kicker-row">
295            <span class="eyebrow live-eyebrow">Dein Wetter</span>
296            <span class="live-updated" data-live-updated>Ort speichern, dann laden wir den ersten Blick.</span>
297          </div>
298          <div class="live-topline">
299            <span class="live-badge" data-live-level>Warnstatus</span>
300            <span class="live-place-pill">für <strong data-place-name>deinen Ort</strong></span>
301          </div>
302          <div class="platinum-status" data-platinum-status data-platinum-level="empty">
303            <div class="platinum-main">
304              <span class="platinum-kicker" data-platinum-kicker>Warnstatus</span>
305              <strong data-platinum-title>Speichere deinen Ort für den persönlichen Wetterblick.</strong>
306              <p data-platinum-copy>Danach siehst du sofort, ob die Lage grün bleibt oder ob Wetter, Umwelt oder Mobilität wirklich wichtig werden.</p>
307            </div>
308            <div class="platinum-metrics" aria-label="Kurzstatus">
309              <span><b data-platinum-signal>wartet</b><small>Status</small></span>
310              <span><b data-platinum-window>nach Ort</b><small>Zeitfenster</small></span>
311              <span><b data-platinum-update>--</b><small>Stand</small></span>
312            </div>
313            <div class="platinum-actions">
314              <a data-platinum-primary data-module="hours24" data-needs-place href="/meteo/24stunden.php">Ortswetter</a>
315              <a data-platinum-secondary data-module="skyguard" data-needs-place href="/meteo/">SkyGuard</a>
316            </div>
317          </div>
318          <div class="live-radar-note" data-live-radar-note hidden>
319            <span>Regenradar</span>
320            <strong data-live-radar-note-text>Der Sofortblick wird geladen</strong>
321          </div>
322          <div class="focus-headline" data-focus-headline>
323            <span>Lageblick</span>
324            <strong>Was heute wirklich wichtig ist</strong>
325          </div>
326          <div class="signal-board" data-signal-board aria-label="Parameterstatus">
327            <a class="signal-card is-empty" data-signal-card="warning" data-module="meteoz" data-needs-place href="/meteo/meteoz.php">
328              <span>⚠️ Warnung</span><strong data-signal-state="warning">wartet</strong><small data-signal-detail="warning">Ort speichern</small>
329            </a>
330            <a class="signal-card is-empty" data-signal-card="thunder" data-module="meteoz" data-needs-place href="/meteo/meteoz.php">
331              <span>⛈️ Gewitter</span><strong data-signal-state="thunder">wartet</strong><small data-signal-detail="thunder">Ort speichern</small>
332            </a>
333            <a class="signal-card is-empty" data-signal-card="rain" data-module="niederschlag" data-needs-place href="/niederschlag/">
334              <span>🌧️ Regen</span><strong data-signal-state="rain">wartet</strong><small data-signal-detail="rain">Ort speichern</small>
335            </a>
336            <a class="signal-card is-empty" data-signal-card="rain_radar" data-module="niederschlag" data-needs-place href="/niederschlag/">
337              <span>📡 Regenradar</span><strong data-signal-state="rain_radar">wartet</strong><small data-signal-detail="rain_radar">Ort speichern</small>
338            </a>
339            <a class="signal-card is-empty" data-signal-card="flash_flood" data-module="niederschlag" data-needs-place href="/niederschlag/">
340              <span>🌊 Sturzflut</span><strong data-signal-state="flash_flood">wartet</strong><small data-signal-detail="flash_flood">Ort speichern</small>
341            </a>
342            <a class="signal-card is-empty" data-signal-card="continuous_rain" data-module="niederschlag" data-needs-place href="/niederschlag/">
343              <span>☔ Dauerregen</span><strong data-signal-state="continuous_rain">wartet</strong><small data-signal-detail="continuous_rain">Ort speichern</small>
344            </a>
345            <a class="signal-card is-empty" data-signal-card="wind" data-module="sturm" data-needs-place href="/sturmwarnung/">
346              <span>🌪️ Wind</span><strong data-signal-state="wind">wartet</strong><small data-signal-detail="wind">Ort speichern</small>
347            </a>
348            <a class="signal-card is-empty" data-signal-card="heat" data-module="meteoz" data-needs-place href="/meteo/meteoz.php">
349              <span>🌡️ Wärme</span><strong data-signal-state="heat">wartet</strong><small data-signal-detail="heat">Ort speichern</small>
350            </a>
351            <a class="signal-card is-empty" data-signal-card="snow" data-module="hours24" data-needs-place href="/meteo/24stunden.php">
352              <span>❄️ Schnee</span><strong data-signal-state="snow">wartet</strong><small data-signal-detail="snow">Ort speichern</small>
353            </a>
354            <a class="signal-card is-empty" data-signal-card="ice" data-module="meteoz" data-needs-place href="/meteo/meteoz.php">
355              <span>🧊 Glätte</span><strong data-signal-state="ice">wartet</strong><small data-signal-detail="ice">Ort speichern</small>
356            </a>
357            <a class="signal-card is-empty" data-signal-card="frost" data-module="hours24" data-needs-place href="/meteo/24stunden.php">
358              <span>🥶 Frost</span><strong data-signal-state="frost">wartet</strong><small data-signal-detail="frost">Ort speichern</small>
359            </a>
360          </div>
361          <button type="button" class="signal-toggle" data-signal-toggle aria-expanded="false">Weitere Wetterdetails anzeigen</button>
362          <div class="live-sources" data-live-sources hidden>
363            <span>Regenradar:</span>
364            <a data-live-radar-source href="https://morgenwirdes.de/" target="_blank" rel="noopener noreferrer">Morgenwirdes.de</a>
365            <span>Datenquelle: Deutscher Wetterdienst</span>
366          </div>
367          <div class="live-reasons" data-live-reasons></div>
368          <div class="live-actions">
369            <a class="live-primary-link" data-live-primary data-module="hours24" data-needs-place href="/meteo/24stunden.php">Tagesverlauf öffnen</a>
370            <a class="live-secondary-link" data-module="skyguard" data-needs-place href="/meteo/">SkyGuard live</a>
371            <a class="live-secondary-link" data-module="meteoz" data-needs-place href="/meteo/meteoz.php">Unwetter-Index</a>
372          </div>
373        </article>
374        <aside class="live-side">
375          <div class="live-kpi live-current">
376            <span>Jetzt</span>
377            <strong data-live-temp>—</strong>
378            <small data-live-weather>Live-Wetter wird geladen</small>
379          </div>
380          <div class="live-kpi">
381            <span>Regen</span>
382            <strong data-live-rain>—</strong>
383            <small data-live-rain-sub>nächste Stunden</small>
384          </div>
385          <div class="live-kpi">
386            <span>Wind</span>
387            <strong data-live-gust>—</strong>
388            <small data-live-gust-sub>Windspitzen</small>
389          </div>
390          <div class="live-kpi">
391            <span>Warnungen</span>
392            <strong data-live-warning>—</strong>
393            <small data-live-warning-sub>Radar/Warnlage prüfen</small>
394          </div>
395        </aside>
396        <article class="live-report" aria-label="Textlicher Lagebericht">
397          <div class="live-report-head"><span>Textlicher Lagebericht</span><small>für <strong data-place-name>deinen Ort</strong></small></div>
398          <h2 data-live-headline>Noch keine Live-Lage geladen.</h2>
399          <p data-live-summary>Speichere einen Ort oder nutze GPS. Danach erscheint hier eine verständliche Zusammenfassung der aktuellen und kommenden Wetterlage.</p>
400        </article>
401        <div class="live-timeline" data-live-timeline aria-label="Nächste sechs Stunden"></div>
402      </section>
403
404      <aside class="place-card place-card-v19" id="mein-ort" data-place-search aria-label="Mein gespeicherter Wetterort">
405        <div class="place-status"><span class="pulse-dot"></span><span>Mein Wetter-Ort</span></div>
406        <h2 data-place-name>Noch kein Ort gespeichert</h2>
407        <p data-place-sub>Speichere einen Ort oder nutze GPS. Danach öffnen sich alle Wetterwerkzeuge direkt für diesen Ort.</p>
408        <form class="place-form" data-place-form>
409          <input name="ort" autocomplete="postal-code address-level2" placeholder="Ort oder PLZ, z. B. Büdingen, 63654 oder Köln" aria-label="Ort oder PLZ speichern">
410          <button type="submit" data-place-submit>Ort speichern</button>
411        </form>
412        <div class="place-results" data-place-results hidden></div>
413        <div class="place-actions">
414          <button type="button" data-place-gps>GPS nutzen</button>
415          <button type="button" data-place-manager-open>Orte verwalten <span data-place-count-badge>0</span></button>
416        </div>
417        <p class="location-message" data-location-message hidden></p>
418        <div class="privacy-note">Speicherung erfolgt lokal auf diesem Gerät – mit zusätzlicher lokaler Sicherung, damit dein Ort nach einem Neustart erhalten bleibt. Nur wenn du CellWarn-Push für ausgewählte Orte aktivierst, werden deren Koordinaten zusätzlich auf Meteozentrale gespeichert.</div>
419      </aside>
420    </section>
421
422    <section class="mz-community-panel mz-weather-content" aria-labelledby="mz-community-title" id="app-wettermelder">
423      <div class="mz-community-copy">
424        <span class="eyebrow small">Wettermelder · Gemeinsam das Wetter beobachten</span>
425        <h2 id="mz-community-title">Wie ist das Wetter gerade bei dir?</h2>
426        <p>Sonne, Regen oder ein besonderer Himmel? Teile deine Beobachtung mit der Community. Auch eine kurze Meldung ohne Foto hilft.</p>
427        <small>Entdecke freigegebene Meldungen und Fotos aus den letzten 24 Stunden.</small>
428      </div>
429      <div class="mz-community-actions">
430        <a class="mz-community-primary" data-community-entry="start_melden" href="/wettermelder/#mitmachen"><span aria-hidden="true">📷</span> Jetzt Wetter melden</a>
431        <a data-community-entry="start_ansehen" href="/wettermelder/">Meldungen &amp; Fotos ansehen →</a>
432      </div>
433    </section>
434
435    <section class="quick-place-panel quick-place-panel-v19" aria-label="Schnellzugriff">
436      <div>
437        <span class="eyebrow small">Schnellzugriff</span>
438        <h2>Was möchtest du wissen?</h2>
439        <p>Die wichtigsten Bereiche bleiben schnell erreichbar. Die App hebt zusätzlich automatisch hervor, was für deinen Ort gerade wirklich zählt.</p>
440      </div>
441      <div class="quick-actions" data-priority-actions>
442        <a class="mz-community-quick" data-community-entry="schnellzugriff" href="/wettermelder/#mitmachen"><span aria-hidden="true">📷</span> Wetter melden</a>
443        <a data-module="meteoz" data-needs-place href="/meteo/meteoz.php"><span aria-hidden="true">⚠</span> Warnungen</a>
444        <a data-module="niederschlag" data-needs-place href="/niederschlag/"><span aria-hidden="true">◉</span> Radar &amp; Regen</a>
445        <a data-module="forecast5" data-needs-place href="/meteo/5tage.php"><span aria-hidden="true">▦</span> 5 Tage</a>
446        <a data-module="meteogram" data-needs-place href="/meteo/meteogram.php"><span aria-hidden="true">📈</span> 14 Tage</a>
447        <a href="#regionale-lage" data-security-panel-open><span aria-hidden="true">⌖</span>
447 Karte</a>
448        <a href="#app-alltagsblick"><span aria-hidden="true">☀</span> Alltag</a>
449        <a href="#app-agrarblick" data-agro-open aria-controls="app-agrarblick" aria-expanded="false"><span aria-hidden="true">🌱</span> Agrar</a>
450      </div>
451    </section>
452
453    <section class="agro-panel" id="app-agrarblick" data-agro-root data-agro-state="empty" data-agro-level="0" hidden aria-label="Agrarwetter für den gespeicherten Ort" aria-live="polite">
454      <div class="agro-head">
455        <div>
456          <span class="eyebrow small agro-eyebrow">Agrarblick</span>
457          <h2>Was ist auf dem Feld wirklich wichtig?</h2>
458        </div>
459        <div class="agro-head-actions">
460          <small data-agro-place>nach Ort</small>
461          <button type="button" class="agro-close" data-agro-close aria-label="Agrarblick schließen">Schließen</button>
462        </div>
463      </div>
464      <div class="agro-focus">
465        <div class="agro-focus-copy">
466          <span data-agro-kicker>Landwirtschaftliche Entscheidungshilfe</span>
467          <h3 data-agro-headline>Ort speichern, dann bewertet die App das nächste Arbeitsfenster.</h3>
468          <p data-agro-summary>Du siehst zuerst nur die wichtigste Aussage. Bodenmodell, Wasserbilanz und Wetterdetails bleiben darunter einklappbar.</p>
469        </div>
470        <div class="agro-confidence">
471          <span>Einordnung</span>
472          <strong data-agro-confidence>wartet auf Ort</strong>
473          <small>Modellwerte ersetzen keine Schlagkontrolle.</small>
474        </div>
475      </div>
476      <div class="agro-decisions" aria-label="Agrar-Entscheidungen">
477        <article data-agro-card="spray">
478          <span>Pflanzenschutz</span>
479          <strong data-agro-spray-status>—</strong>
480          <small data-agro-spray-detail>Wetterfenster nach Ortswahl</small>
481        </article>
482        <article data-agro-card="traffic">
483          <span>Befahren</span>
484          <strong data-agro-traffic-status>—</strong>
485          <small data-agro-traffic-detail>Witterungsrisiko nach Ortswahl</small>
486        </article>
487        <article data-agro-card="water">
488          <span>Wasserbilanz 24h</span>
489          <strong data-agro-water-status>—</strong>
490          <small data-agro-water-detail>Niederschlag minus Referenzverdunstung</small>
491        </article>
492      </div>
493      <button type="button" class="agro-details-toggle" data-agro-details-toggle aria-expanded="false">Boden &amp; Wasser ansehen</button>
494      <div class="agro-details" data-agro-details hidden>
495        <article>
496          <span>Oberboden 3–9 cm</span>
497          <strong data-agro-soil-surface>—</strong>
498          <small data-agro-soil-surface-detail>standortnaher Modellwert</small>
499        </article>
500        <article>
501          <span>Boden 9–81 cm</span>
502          <strong data-agro-soil-root>—</strong>
503          <small data-agro-soil-root-detail>nicht kulturspezifisch</small>
504        </article>
505        <article>
506          <span>Bodentemperatur 6 cm</span>
507          <strong data-agro-soil-temp>—</strong>
508          <small>für Aussaat und Entwicklung</small>
509        </article>
510        <article>
511          <span>Referenzverdunstung</span>
512          <strong data-agro-et0>—</strong>
513          <small>ET₀ der nächsten 24 Stunden</small>
514        </article>
515      </div>
516      <p class="agro-disclaimer" data-agro-disclaimer>Bodenfeuchte und Befahrbarkeit sind Modellorientierungen ohne Schlag-, Bodenart- oder Sensorkalibrierung. Vor-Ort-Prüfung und Produktauflagen gehen immer vor.</p>
517      <p class="agro-source">Wetter- und Bodenmodell über <a href="https://open-meteo.com/" target="_blank" rel="noopener noreferrer">Open-Meteo</a>.</p>
518    </section>
519
520
521    <section class="security-map-panel" id="regionale-lage" data-security-map-root data-radius="50" aria-label="Regionale Lagekarte">
522      <div class="security-map-head">
523        <div>
524          <span class="eyebrow small">Warnzentrum im Umkreis</span>
525          <h2>Welche Warnlage gilt rund um <span data-place-name>deinen Ort</span>?</h2>
526          <p data-security-summary>Speichere einen Ort. Danach zeigt die App nur relevante Meldungen aus deiner Umgebung: Bevölkerungsschutz, Hochwasser, Polizei, Wetter ab Orange, Luftqualität und Bahn.</p>
527        </div>
528        <button type="button" class="section-toggle map-panel-toggle" data-security-panel-toggle aria-expanded="false">Karte anzeigen</button>
529      </div>
530      <div class="security-map-body" data-security-panel-body hidden>
531        <div class="security-map-actions">
532          <button type="button" data-security-radius="25">25 km</button>
533          <button type="button" data-security-radius="50" aria-pressed="true">50 km</button>
534          <button type="button" data-security-radius="100">100 km</button>
535          <button type="button" data-security-details-toggle aria-expanded="false">Meldungsdetails</button>
536          <button type="button" data-security-districts aria-pressed="false">Landkreise</button>
537          <a data-security-open data-module="warnzentrum" data-needs-place href="/warnzentrum/">
537Regionales Warnzentrum öffnen</a>
538        </div>
539      </div>
540      <div class="security-map-shell">
541        <div class="security-map" data-security-map aria-label="Karte für Bund, Hochwasser, Polizei, Wetter und optionale Landkreise"></div>
542        <div class="security-map-overlay" data-security-empty>
543          <strong>Ort speichern, Karte startet automatisch.</strong>
544          <span>Die App zeigt dann deinen Ort, den Umkreis, relevante Meldungen und optional die Landkreise zur Orientierung.</span>
545        </div>
546      </div>
547      <div class="security-map-legend" aria-label="Kartenlegende">
548        <span class="cat-civil">● Bund / Bevölkerungsschutz</span>
549        <span class="cat-flood">● Hochwasser</span>
550        <span class="cat-police">● Polizei</span>
551        <span class="cat-weather">● Wetter</span>
552        <span class="cat-air">● Luftqualität</span>
553        <span class="cat-rail">● Bahn</span>
554        <span class="cat-home">◎ gespeicherter Ort</span>
555      </div>
556      <div class="security-map-list" data-security-list data-security-details hidden>
557        <article class="security-map-card is-empty">
558          <strong>Noch kein Ort gespeichert</strong>
559          <span>Danach wird die Karte direkt im App-Index sichtbar mit deinem regionalen Kartenausschnitt geladen.</span>
560        </article>
561      </div>
562      </div>
563    </section>
564
565    <section class="saved-panel saved-panel-v31">
566      <div class="section-head compact">
567        <div><span class="eyebrow small">Ortsauswahl</span><h2><span data-place-name>Noch kein Ort gespeichert</span></h2><p><span data-place-count>0 Orte gespeichert</span> · Zuhause, Arbeit, Familie oder Urlaub bleiben schnell erreichbar.</p></div>
568        <button type="button" class="section-toggle" data-place-manager-open>Ort wechseln oder verwalten</button>
569      </div>
570    </section>
571
572    <div class="section-head module-head-v37" id="weitere-dienste" data-service-head>
573      <div>
574        <span class="eyebrow small">Live-Dienste</span>
575        <h2>Jetzt sinnvolle Dienste</h2>
576        <p data-service-headline>Die App sortiert die wichtigsten Bereiche nach deiner aktuellen Lage. Kein Katalog, sondern der nächste sinnvolle Klick.</p>
577      </div>
578      <button type="button" class="section-toggle service-toggle" data-modules-toggle aria-expanded="false">Alle Dienste kompakt</button>
579    </div>
580
581    <section class="grid module-grid-v31 service-grid-v37" data-module-grid aria-label="Meteozentrale App Module">
582                      <a class="tile service-tile sky" data-module="skyguard" data-needs-place href="/app/">
583          <span class="tag">SkyGuard</span>
584          <div class="service-tile-main">
585            <div class="icon" aria-hidden="true">🛡️</div>
586            <div class="service-tile-copy">
587              <h3>Radar &amp; Gewitter</h3>
588              <p>Radarzellen, Blitze und Warnflächen mit konservativem Radar-/Ortsabgleich. Keine Zelltrajektorien- oder Ankunftsgarantie.</p>
589            </div>
590          </div>
591          <div class="service-live-row">
592            <small class="module-status" data-module-status>Status nach Ort</small>
593            <small class="module-place" data-module-place>Ort wählen</small>          </div>
594          <small class="module-reason" data-module-reason>Live-Einordnung folgt nach Ortswahl.</small>
595        </a>
596                      <a class="tile service-tile " data-module="hours24" data-needs-place href="/app/">
597          <span class="tag">Heute</span>
598          <div class="service-tile-main">
599            <div class="icon" aria-hidden="true">🕒</div>
600            <div class="service-tile-copy">
601              <h3>Wetter heute</h3>
602              <p>Der verständliche Stundenverlauf mit Temperatur, Regen und Wind.</p>
603            </div>
604          </div>
605          <div class="service-live-row">
606            <small class="module-status" data-module-status>Status nach Ort</small>
607            <small class="module-place" data-module-place>Ort wählen</small>          </div>
608          <small class="module-reason" data-module-reason>Live-Einordnung folgt nach Ortswahl.</small>
609        </a>
610                      <a class="tile service-tile " data-module="forecast5" data-needs-place href="/app/">
611          <span class="tag">5 Tage</span>
612          <div class="service-tile-main">
613            <div class="icon" aria-hidden="true">🗓️</div>
614            <div class="service-tile-copy">
615              <h3>5-Tage-Prognose</h3>
616              <p>Tagestrend, Temperatur, Niederschlag und Wind für die kommenden fünf Tage.</p>
617            </div>
618          </div>
619          <div class="service-live-row">
620            <small class="module-status" data-module-status>Status nach Ort</small>
621            <small class="module-place" data-module-place>Ort wählen</small>          </div>
622          <small class="module-reason" data-module-reason>Live-Einordnung folgt nach Ortswahl.</small>
623        </a>
624                      <a class="tile service-tile " data-module="meteogram" data-needs-place href="/app/">
625          <span class="tag">Ausblick</span>
626          <div class="service-tile-main">
627            <div class="icon" aria-hidden="true">📈</div>
628            <div class="service-tile-copy">
629              <h3>14-Tage-Trend</h3>
630              <p>Wie sich das Wetter in den kommenden zwei Wochen entwickelt.</p>
631            </div>
632          </div>
633          <div class="service-live-row">
634            <small class="module-status" data-module-status>Status nach Ort</small>
635            <small class="module-place" data-module-place>Ort wählen</small>          </div>
636          <small class="module-reason" data-module-reason>Live-Einordnung folgt nach Ortswahl.</small>
637        </a>
638                      <a class="tile service-tile " data-module="niederschlag" data-needs-place href="/app/">
639          <span class="tag">Radar</span>
640          <div class="service-tile-main">
641            <div class="icon" aria-hidden="true">🌧️</div>
642            <div class="service-tile-copy">
643              <h3>Radar &amp; Regen</h3>
644              <p>Regenradar, Starkregen, Dauerregen und Regensummen.</p>
645            </div>
646          </div>
647          <div class="service-live-row">
648            <small class="module-status" data-module-status>Status nach Ort</small>
649            <small class="module-place" data-module-place>Ort wählen</small>          </div>
650          <small class="module-reason" data-module-reason>Live-Einordnung folgt nach Ortswahl.</small>
651        </a>
652                      <a class="tile service-tile " data-module="sturm" data-needs-place href="/app/">
653          <span class="tag">Wind</span>
654          <div class="service-tile-main">
655            <div class="icon" aria-hidden="true">🌪️</div>
656            <div class="service-tile-copy">
657              <h3>Wind &amp; Sturm</h3>
658              <p>Böen, Windspitzen und mögliche Sturmwarnungen.</p>
659            </div>
660          </div>
661          <div class="service-live-row">
662            <small class="module-status" data-module-status>Status nach Ort</small>
663            <small class="module-place" data-module-place>Ort wählen</small>          </div>
664          <small class="module-reason" data-module-reason>Live-Einordnung folgt nach Ortswahl.</small>
665        </a>
666                      <a class="tile service-tile " data-module="meteoz" data-needs-place href="/app/">
667          <span class="tag">Warnung</span>
668          <div class="service-tile-main">
669            <div class="icon" aria-hidden="true">⚠️</div>
670            <div class="service-tile-copy">
671              <h3>Warnungen</h3>
672              <p>Amtliche Warnungen und eine verständliche Einschätzung der Wettergefahren.</p>
673            </div>
674          </div>
675          <div class="service-live-row">
676            <small class="module-status" data-module-status>Status nach Ort</small>
677            <small class="module-place" data-module-place>Ort wählen</small>          </div>
678          <small class="module-reason" data-module-reason>Live-Einordnung folgt nach Ortswahl.</small>
679        </a>
680                      <a class="tile service-tile " data-module="models"  href="/app/">
681          <span class="tag">Profi</span>
682          <div class="service-tile-main">
683            <div class="icon" aria-hidden="true">🛰️</div>
684            <div class="service-tile-copy">
685              <h3>Karten</h3>
686              <p>
686Modellkarten und vertiefende Daten für Wetterinteressierte.</p>
687            </div>
688          </div>
689          <div class="service-live-row">
690            <small class="module-status" data-module-status>Status nach Ort</small>
691                      </div>
692          <small class="module-reason" data-module-reason>Live-Einordnung folgt nach Ortswahl.</small>
693        </a>
694                      <a class="tile service-tile warnhub" data-module="warnzentrum" data-needs-place href="/app/">
695          <span class="tag">Umgebung</span>
696          <div class="service-tile-main">
697            <div class="icon" aria-hidden="true">&#128225;</div>
698            <div class="service-tile-copy">
699              <h3>Warnzentrum</h3>
700              <p>Nur relevante Meldungen: Bevölkerungsschutz, Hochwasser, Polizei, Wetter ab Orange, Luftqualität und Bahnlage.</p>
701            </div>
702          </div>
703          <div class="service-live-row">
704            <small class="module-status" data-module-status>Status nach Ort</small>
705            <small class="module-place" data-module-place>Ort wählen</small>          </div>
706          <small class="module-reason" data-module-reason>Live-Einordnung folgt nach Ortswahl.</small>
707        </a>
708          </section>
709
710    <section class="wide-panel probability-panel probability-panel-v31" id="wahrscheinlichkeiten" data-probability-root aria-label="Wahrscheinlichkeitsblick">
711      <article class="panel probability-card">
712        <div class="probability-head">
713          <div>
714            <span class="eyebrow small">Für Wetterinteressierte</span>
715            <h3>Modelle &amp; Wahrscheinlichkeiten</h3>
716          </div>
717          <div class="probability-summary-actions"><p data-probability-summary>Vertiefende Modelldaten – nur wenn du genauer einsteigen möchtest.</p><button type="button" class="section-toggle" data-probability-toggle aria-expanded="false">Fachdaten anzeigen</button></div>
718        </div>
719        <div class="probability-grid" data-probability-grid hidden></div>
720        <div class="probability-source" data-probability-details hidden>
721          <span>MOSMIX-Wahrscheinlichkeiten über</span>
722          <a href="https://morgenwirdes.de/" target="_blank" rel="noopener noreferrer">Morgenwirdes.de</a>
723          <span>Datenquelle: Deutscher Wetterdienst</span>
724        </div>
725      </article>
726    </section>
727
728    <div class="app-dialog" data-main-menu hidden role="dialog" aria-modal="true" aria-labelledby="main-menu-title">
729      <div class="app-dialog-backdrop" data-dialog-close></div>
730      <section class="app-dialog-card main-menu-card">
731        <div class="app-dialog-head"><div><span class="eyebrow small">Meteozentrale</span><h2 id="main-menu-title">Alle Bereiche</h2></div><button type="button" data-dialog-close aria-label="Schließen">×</button></div>
732        <p>Wähle direkt, was du wissen möchtest. Fachbegriffe stehen nur noch als Zusatz dabei.</p>
733        <button type="button" class="menu-place-action" data-place-manager-open><span>⌖</span><strong><b data-place-name>Ort auswählen</b><small>Ort suchen, GPS verwenden oder gespeicherte Orte wechseln</small></strong></button>
734        <nav class="main-menu-grid" aria-label="Alle Wetterbereiche">
735          <a data-menu-link href="/wettermelder/"><span>📷</span><strong>Wettermelder<small>Wetter melden · Fotos · Community-Karte</small></strong></a>
736          <a data-menu-link data-module="meteoz" data-needs-place href="/meteo/meteoz.php"><span>⚠️</span><strong>Warnungen<small>Gefahren verständlich erklärt</small></strong></a>
737          <a data-menu-link data-module="hours24" data-needs-place href="/meteo/24stunden.php"><span>🕒</span><strong>Wetter heute<small>Stündliche Vorhersage</small></strong></a>
738          <a data-menu-link data-module="forecast5" data-needs-place href="/meteo/5tage.php"><span>🗓️</span><strong>5-Tage-Prognose<small>Der kompakte Tagestrend</small></strong></a>
739          <a data-menu-link data-module="niederschlag" data-needs-place href="/niederschlag/"><span>🌧️</span><strong>Radar &amp; Regen<small>Regen, Starkregen, Dauerregen</small></strong></a>
740          <a data-menu-link data-module="meteogram" data-needs-place href="/meteo/meteogram.php"><span>📈</span><strong>14-Tage-Trend<small>Der weitere Ausblick</small></strong></a>
741          <a data-menu-link data-module="skyguard" data-needs-place href="/meteo/"><span>⛈️</span><strong>Gewitter &amp; Blitze<small>SkyGuard Stormtracker</small></strong></a>
742          <a data-menu-link data-module="sturm" data-needs-place href="/sturmwarnung/"><span>🌪️</span><strong>Wind &amp; Sturm<small>Böen und Sturmgefahr</small></strong></a>
743          <a data-menu-link data-module="warnzentrum" data-needs-place href="/warnzentrum/"><span>📡</span><strong>Warnzentrum<small>Bund, Hochwasser, Polizei, Wetter ab Orange</small></strong></a>
744          <a data-menu-link href="#app-alltagsblick"><span>☀️</span><strong>Alltagsblick<small>Temperaturtrend, Luftdruck, Wäsche und Luft</small></strong></a>
745          <a data-menu-link data-agro-open aria-controls="app-agrarblick" aria-expanded="false" href="#app-agrarblick"><span>🌱</span><strong>Agrarblick<small>Boden, Spritzfenster und Befahrbarkeit</small></strong></a>
746          <a data-menu-link href="#regionale-lage" data-security-panel-open><span>🗺️</span><strong>Karte<small>Meldungen in deiner Umgebung</small></strong></a>
747          <a data-menu-link data-module="models" href="/app/"><span>🛰️</span><strong>Modelle &amp; Fachdaten<small>Für Wetterinteressierte</small></strong></a>
748        </nav>
749        <button type="button" class="menu-settings-action" data-menu-settings-open>App, Push und Ansicht einstellen</button>
750      </section>
751    </div>
752
753    <div class="app-dialog" data-place-manager hidden role="dialog" aria-modal="true" aria-labelledby="place-manager-title">
754      <div class="app-dialog-backdrop" data-dialog-close></div>
755      <section class="app-dialog-card place-manager-card">
756        <div class="app-dialog-head"><div><span class="eyebrow small">Ort &amp; GPS</span><h2 id="place-manager-title">Welches Wetter möchtest du sehen?</h2></div><button type="button" data-dialog-close aria-label="Schließen">×</button></div>
757        <div class="dialog-place-search" data-place-search>
758          <h3>Anderen Ort suchen</h3>
759          <p>Gib einen Ortsnamen oder eine Postleitzahl ein.</p>
760          <form class="place-form" data-place-form>
761            <input name="ort" autocomplete="postal-code address-level2" placeholder="Ort oder PLZ, z. B. Köln oder 63654" aria-label="Anderen Ort suchen">
762            <button type="submit" data-place-submit>Suchen</button>
763          </form>
764          <div class="place-results" data-place-results hidden></div>
765          <div class="dialog-location-divider"><span>oder</span></div>
766          <button type="button" class="dialog-gps-button" data-place-gps><span>⌖</span><strong>Meinen aktuellen Standort verwenden</strong></button>
767          <p class="location-message" data-location-message hidden></p>
768          <small class="dialog-privacy">Die Standortfreigabe erfolgt über deinen Browser. Orte werden nur auf diesem Gerät gespeichert.</small>
769        </div>
770        <div class="dialog-saved-head"><h3>Gespeicherte Orte</h3><span data-place-count>0 Orte gespeichert</span></div>
771        <p class="dialog-saved-help">Tippe auf einen Ort, um ihn sofort als Hauptort zu verwenden.</p>
772        <div class="saved-list saved-list-dialog" data-place-list></div>
773        <div class="dialog-actions"><button type="button" data-place-clear>Aktiven Hauptort entfernen</button></div>
774      </section>
775    </div>
776
777    <div class="app-dialog" data-install-guide hidden role="dialog" aria-modal="true" aria-labelledby="install-guide-title">
778      <div class="app-dialog-backdrop" data-dialog-close></div>
779      <section class="app-dialog-card install-guide-card">
780        <div class="app-dialog-head"><div><span class="eyebrow small">iPhone und iPad</span><h2 id="install-guide-title">Meteozentrale zum Home-Bildschirm</h2></div><button type="button" data-dialog-close aria-label="Schließen">×</button></div>
781        <ol class="install-guide-steps">
782          <li><span>1</span><div><strong>In Safari öffnen</strong><p>Falls du aus Facebook, Instagram oder einem Messenger kommst, öffne diese Seite zuerst in Safari.</p></div></li>
783          <li><span>2</span><div><strong>Teilen antippen</strong><p>Tippe in der Safari-Leiste auf das Teilen-Symbol.</p></div></li>
784          <li><span>3</span><div><strong>Zum Home-Bildschirm</strong><p>Wähle „Zum Home-Bildschirm“ und bestätige anschließend mit „Hinzufügen“.</p></div></li>
785        </ol>
786        <p class="install-guide-note">Danach startet die Meteozentrale wie eine App direkt vom Home-Bildschirm.</p>
787      </section>
788    </div>
789
790    <div class="app-dialog" data-view-settings hidden role="dialog" aria-modal="true" aria-labelledby="view-settings-title">
791      <div class="app-dialog-backdrop" data-dialog-close></div>
792      <section class="app-dialog-card view-settings-card">
793        <div class="app-dialog-head"><div><span class="eyebrow small">Einstellungen</span><h2 id="view-settings-title">Deine Startseite</h2></div><button type="button" data-dialog-close aria-label="Schließen">×</button></div>
794        <p>Warnstatus, Farbwechsel und Vorhersage bleiben übersichtlich. Zusätzliche Fachbereiche kannst du hier ein- oder ausblenden.</p>
795        <div class="settings-action"><span><strong>Relevante Push-Warnungen</strong><small>Persönliche Warnungen werden über die exakte amtliche DWD-Warnzelle zugestellt. Profilregistrierung und tatsächliche Geräte-Zustellung werden getrennt geprüft.</small><small id="mz-push-inline-status" style="display:block;margin-top:6px;color:#93c5fd">Push-Status wird geprüft …</small></span><div style="display:flex;flex-wrap:wrap;gap:8px;justify-content:flex-end"><button class="push-btn" id="webpushr-subscription-button" type="button" data-push-settings>Pushs aktivieren</button><button class="push-btn" id="mz-webpushr-unsubscribe" type="button" data-push-disable style="background:#ef4444;color:#fff;border:1px solid rgba(239,68,68,.35)">Pushs abbestellen</button></div></div>
796        <div class="settings-action settings-storage-action"><span><strong>Ortsspeicher auf diesem Gerät</strong><small>Dein Hauptort wird lokal gespeichert und zusätzlich lokal gesichert. Das hilft besonders nach Neustart der App.</small><small data-storage-status style="display:block;margin-top:6px;color:#93c5fd">Ortsspeicher wird geprüft …</small></span><button class="push-btn" type="button" data-place-storage-check>Speicher prüfen</button></div>
797        <div class="settings-action app-info-action"><span><strong>App-Info · Version 81.8</strong><small>Neu: Aufrufe und Likes direkt in den Wetterberichten. Wetterberichte findest du weiterhin im eigenen Reiter. Artikel-Push ist noch in Vorbereitung.</small><small>Persönliche Wetterwarnungen trennen jetzt Profilregistrierung, echten Geräte-Test und nachvollziehbaren Versandstatus.</small><small>Live-Wetter: Open-Meteo/DWD-Modelle · Pollen und Luft: CAMS Europe über Open-Meteo · Browser-Ersatz und lokale Reserve werden sichtbar gekennzeichnet; Warnungen, Radar und SkyGuard müssen dabei separat live geprüft werden. Der Radar-/Ortsabgleich garantiert weder Zelltrajektorien noch eine Ankunft am Ort.</small></span><a class="pill" href="/app/?mz_refresh=81.8" aria-label="Meteozentrale App Version 81.8 neu laden">Nach Update neu laden</a></div>
798        <label class="view-option"><input type="checkbox" data-view-preference="probabilities" checked><span><strong>Wahrscheinlichkeitsblick</strong><small>MOSMIX-Bereich am Seitenende anzeigen</small></span></label>
799        <label class="view-option"><input type="checkbox" data-view-preference="allTools"><span><strong>Alle Wetterdienste kompakt</strong><small>Statt der wichtigsten Live-Dienste die gesamte Dienstliste kompakt anzeigen</small></span></label>
800        <label class="view-option"><input type="checkbox" data-view-preference="mapDetails"><span><strong>Meldungsdetails zur Karte</strong><small>Zusätzliche Meldungen unter der aufgeklappten Karte zeigen</small></span></label>
801        <div class="settings-action settings-agro-action"><span><strong>Agrarblick bei Bedarf</strong><small>Bodenmodell, Wasserbilanz, Spritzfenster und Befahrbarkeit werden nur für die aktuelle Ansicht geöffnet.</small></span><button type="button" class="push-btn" data-agro-open aria-controls="app-agrarblick" aria-expanded="false">Agrarblick öffnen</button></div>
802      </section>
803    </div>
804
805    <p class="footer">© 2026 Meteozentrale · App-Version 81.8 · Stand 02.10.2026, 19:50 · Ort, GPS, Warnstatus, 5-Tage-Prognose, 14-Tage-Meteogramm, optionaler Agrarblick, Pollen, konservativer SkyGuard-Radar-/Ortsabgleich, Lagebericht und alle Wetterdienste verständlich erreichbar; keine Zelltrajektorien- oder Ankunftsgarantie.</p>
806  </div>
807  
807<script src="/assets/pwa/mz-location-core.js?v=20" defer></script>
807
808  
808<script src="/assets/pwa/app-v51.js?v=51.3.1" defer></script>
808
809  
809<script src="/app/mz-push-preferences.js?v=73.0" defer></script>
809
810  
810<script src="/app/mz-cellwarn-optin.js?v=20260928-2" defer></script>
810
811
812  
812<script>
813  (function(){
814    'use strict';
815    var STATUS_ID = 'mz-push-inline-status';
816    var UNSUB_BTN_ID = 'mz-webpushr-unsubscribe';
817    var SUB_BTN_ID = 'webpushr-subscription-button';
818    var USER_OFF_KEY = 'mz_push_user_unsubscribed_v1';
819
820    function $(id){ return document.getElementById(id); }
821    function setStatus(text, tone){
822      var el = $(STATUS_ID);
823      if (!el) return;
824      el.textContent = text;
825      el.style.color = tone === 'ok' ? '#86efac' : tone === 'warn' ? '#fbbf24' : tone === 'bad' ? '#fca5a5' : '#93c5fd';
826    }
827    function permissionState(){
828      try { return ('Notification' in window) ? Notification.permission : 'unsupported'; }
829      catch(e){ return 'unknown'; }
830    }
831    async function getPushSubscriptions(){
832      var out = [];
833      try {
834        if (!('serviceWorker' in navigator)) return out;
835        var regs = await navigator.serviceWorker.getRegistrations();
836        for (var i = 0; i < regs.length; i++) {
837          var reg = regs[i];
838          if (!reg || !reg.pushManager) continue;
839          var sub = await reg.pushManager.getSubscription();
840          if (sub) out.push({reg: reg, sub: sub});
841        }
842      } catch(e) {}
843      return out;
844    }
845    function cleanupWebpushrLocalTraces(){
846      try {
847        Object.keys(localStorage).forEach(function(k){
848          if (String(k).toLowerCase().indexOf('webpushr') !== -1) localStorage.removeItem(k);
849        });
850        localStorage.setItem(USER_OFF_KEY, '1');
851      } catch(e) {}
852      try {
853        Object.keys(sessionStorage).forEach(function(k){
854          if (String(k).toLowerCase().indexOf('webpushr') !== -1) sessionStorage.removeItem(k);
855        });
856      } catch(e) {}
857      ['_webpushrId','_webpushr_f_id','_webpushr_first_optin'].forEach(function(name){
858        try {
859          document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/;';
860          document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; domain=.meteozentrale.de;';
861        } catch(e) {}
862      });
863    }
864    async function refreshPushStatus(){
865      var perm = permissionState();
866      var subs = await getPushSubscriptions();
867      if (perm === 'denied') {
868        setStatus('Push ist im Browser blockiert. Bitte in den Browser-Einstellungen wieder erlauben.', 'bad');
869      } else if (subs.length > 0) {
870        try { localStorage.setItem(USER_OFF_KEY, '0'); } catch(e) {}
871        setStatus('Die Browser-Subscription ist aktiv. Die tatsächliche Zustellung kannst du unten mit dem persönlichen Test-Push prüfen.', 'ok');
872      } else if (perm === 'granted') {
873        setStatus('Browser erlaubt Push, aber aktuell ist keine aktive Push-Subscription vorhanden. Bei Bedarf Pushs erneut aktivieren.', 'warn');
874      } else {
875        setStatus('Pushs sind noch nicht aktiv. Tippe auf „Pushs aktivieren“ und erlaube Benachrichtigungen.', 'warn');
876      }
877    }
878    async function unsubscribePush(){
879      var ok = window.confirm(
880        'Pushs auf diesem Gerät wirklich abbestellen?\n\n' +
881        'Du erhältst dann keine Meteozentrale-Pushs mehr, kannst sie aber jederzeit wieder aktivieren.'
882      );
883      if (!ok) return;
884      setStatus('Pushs werden abbestellt …', 'warn');
885      var count = 0;
886      var profileDisabled = false;
887      var cellwarnDisabled = false;
888      try {
889        if (window.MZWarnProfile && typeof window.MZWarnProfile.disable === 'function') {
890          profileDisabled = await window.MZWarnProfile.disable();
891        }
892      } catch(e) {}
893      try {
894        if (window.MZCellWarn && typeof window.MZCellWarn.disable === 'function') {
895          cellwarnDisabled = await window.MZCellWarn.disable();
896        }
897      } catch(e) {}
898      try {
899        var subs = await getPushSubscriptions();
900        for (var i = 0; i < subs.length; i++) {
901          try {
902            var done = await subs[i].sub.unsubscribe();
903            if (done) count++;
904          } catch(e) {}
905        }
906      } catch(e) {}
907      cleanupWebpushrLocalTraces();
908      if (count > 0 && profileDisabled && cellwarnDisabled) setStatus('Pushs, Warnprofil und CellWarn wurden auf diesem Gerät vollständig deaktiviert.', 'ok');
909      else if (count > 0) setStatus('Browser-Push wurde abbestellt. Die Deaktivierung des Serverprofils konnte nicht bestätigt werden.', 'warn');
910      else setStatus('Keine aktive Push-Subscription gefunden. Pushs sind auf diesem Gerät nicht aktiv.', 'ok');
911      setTimeout(refreshPushStatus, 800);
912      setTimeout(refreshPushStatus, 2500);
913    }
914    function bind(){
915      var off = $(UNSUB_BTN_ID);
916      if (off) off.addEventListener('click', unsubscribePush);
917      var sub = $(SUB_BTN_ID);
918      if (sub) sub.addEventListener('click', function(e){
919        try { localStorage.setItem(USER_OFF_KEY, '0'); } catch(e) {}
920        if (!window.MZ_WEBPUSHR_READY && typeof window.mzLoadWebpushr === 'function') {
921          e.preventDefault();
922          e.stopImmediatePropagation();
923          setStatus('Push-Dienst wird geladen …', 'warn');
924          window.mzLoadWebpushr().then(function(){
925            setStatus('Push-Anmeldung wird geöffnet. Bitte Benachrichtigungen erlauben, falls der Browser fragt.', 'warn');
926            setTimeout(function(){ sub.click(); }, 250);
927          }).catch(function(){ setStatus('Push-Dienst ist gerade nicht erreichbar. Bitte später erneut versuchen.', 'bad'); });
928          return;
929        }
930        setStatus('Push-Anmeldung wird geöffnet. Bitte Benachrichtigungen erlauben, falls der Browser fragt.', 'warn');
931        setTimeout(refreshPushStatus, 3500);
932        setTimeout(refreshPushStatus, 8000);
933      }, true);
934      window.addEventListener('focus', refreshPushStatus);
935      document.addEventListener('visibilitychange', function(){ if (document.visibilityState === 'visible') refreshPushStatus(); });
936      refreshPushStatus();
937    }
938    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bind); else bind();
939  })();
940  </script>
940
941  
941<script>
942  (function(){
943    'use strict';
944    var GA_ID = window.MZ_ANALYTICS_ID || 'G-1MMGHXXRHP';
945    var APP_VERSION = window.MZ_APP_VERSION || 'v81.2-app-quality-hotfix';
946    var CONSENT_KEY = 'mz_analytics_consent_v1';
947    var pageViewSent = false;
948    var placeLoadedSent = false;
949
950    function storageGet(key){ try { return localStorage.getItem(key); } catch(e){ return null; } }
951    function storageSet(key, value){ try { localStorage.setItem(key, value); } catch(e){} }
952    function hasConsent(){ return storageGet(CONSENT_KEY) === 'granted'; }
953    function isStandalone(){ return (window.matchMedia && window.matchMedia('(display-mode: standalone)').matches) || window.navigator.standalone === true; }
954    function clean(value, max){ return String(value || '').replace(/\s+/g, ' ').trim().slice(0, max || 90); }
955    function capabilityParams(){
956      return {
957        manifest_link: document.querySelector('link[rel="manifest"]') ? 'yes' : 'no',
958        service_worker_supported: 'serviceWorker' in navigator ? 'yes' : 'no',
959        push_supported: 'PushManager' in window ? 'yes' : 'no',
960        notification_supported: 'Notification' in window ? 'yes' : 'no',
961        standalone: isStandalone() ? 'yes' : 'no',
962        canonical_url: clean(document.querySelector('link[rel="canonical"]') ? document.querySelector('link[rel="canonical"]').href : '', 160)
963      };
964    }
965    function currentPlace(){
966      try {
967        if (window.MZLocationCore && typeof window.MZLocationCore.primary === 'function') return window.MZLocationCore.primary();
968      } catch(e) {}
969      try { return JSON.parse(localStorage.getItem('mz_app_primary_place_v1') || 'null'); } catch(e){ return null; }
970    }
971    function placeParams(){
972      var p = currentPlace() || {};
973      var has = !!(p && (p.name || p.display_name || p.slug));
974      return {
975        has_place: has ? 'yes' : 'no',
976        place_name: has ? clean(p.name || String(p.display_name || '').split(',')[0], 60) : '',
977        place_state: clean(p.admin1 || '', 50),
978        place_country: clean(p.country_code || p.countryCode || '', 8),
979        place_source: clean(p.source || '', 40)
980      };
981    }
982    function send(eventName, params){
983      if (!hasConsent() || typeof window.gtag !== 'function') return;
984      var payload = Object.assign({
985        app_area: 'pwa_app_index',
986        app_version: APP_VERSION,
987        app_mode: isStandalone() ? 'standalone' : 'browser',
988        app_kind: 'progressive_web_app',
989        app_install_channel: 'web_install',
990        app_schema: 'software_application_web_application'
991      }, placeParams(), params || {});
992      delete payload.latitude;
993      delete payload.longitude;
994      delete payload.lat;
995      delete payload.lon;
996      window.gtag('event', eventName, payload);
997    }
998    function sendPageView(reason){
999      if (!hasConsent() || pageViewSent || typeof window.gtag !== 'function') return;
1000      pageViewSent = true;
1001      window.gtag('event', 'page_view', Object.assign({
1002        page_title: document.title,
1003        page_location: location.href,
1004        page_path: location.pathname + location.search,
1005        page_referrer: document.referrer || '',
1006        app_area: 'pwa_app_index',
1007        app_version: APP_VERSION,
1008        app_mode: isStandalone() ? 'standalone' : 'browser',
1009        app_kind: 'progressive_web_app',
1010        app_install_channel: 'web_install',
1011        app_schema: 'software_application_web_application',
1012        view_reason: reason || 'load'
1013      }, placeParams()));
1014      send('app_open', {open_reason: reason || 'load'});
1015      send('app_capabilities', Object.assign({capability_reason: reason || 'load'}, capabilityParams()));
1016    }
1017    function updateConsent(value){
1018      storageSet(CONSENT_KEY, value);
1019      if (typeof window.gtag === 'function') {
1020        window.gtag('consent', 'update', {
1021          'analytics_storage': value === 'granted' ? 'granted' : 'denied',
1022          'ad_storage': 'denied',
1023          'ad_user_data': 'denied',
1024          'ad_personalization': 'denied'
1025        });
1026      }
1027      var banner = document.querySelector('[data-analytics-consent]');
1028      if (banner) banner.classList.remove('is-visible');
1029      if (value === 'granted') {
1030        sendPageView('consent_granted');
1031        send('analytics_consent_accept');
1032      }
1033    }
1034    function initConsentBanner(){
1035      var stored = storageGet(CONSENT_KEY);
1036      var banner = document.querySelector('[data-analytics-consent]');
1037      if (!stored && banner) banner.classList.add('is-visible');
1038      if (stored === 'granted') sendPageView('stored_consent');
1039      document.addEventListener('click', function(e){
1040        if (e.target.closest('[data-analytics-accept]')) updateConsent('granted');
1041        if (e.target.closest('[data-analytics-decline]')) updateConsent('denied');
1042      });
1043    }
1044    function closestText(el){
1045      if (!el) return '';
1046      var h = el.querySelector('h1,h2,h3,strong,.tag');
1047      return clean((h && h.textContent) || el.textContent || '', 80);
1048    }
1049    function bindEvents(){
1050      document.addEventListener('submit', function(e){
1051        if (e.target && e.target.matches('[data-place-form]')) {
1052          var input = e.target.querySelector('input[name="ort"]');
1053          send('place_search_submit', {search_length: input && input.value ? String(input.value).trim().length : 0});
1054        }
1055      }, true);
1056      document.addEventListener('click', function(e){
1057        var radius = e.target.closest('[data-security-radius]');
1058        if (radius) send('security_radius_change', {radius_km: clean(radius.getAttribute('data-security-radius'), 8)});
1059
1060        if (e.target.closest('[data-place-gps]')) send('gps_click');
1061        if (e.target.closest('[data-place-refresh]')) send('place_links_refresh');
1062        if (e.target.closest('[data-place-clear]')) send('place_clear');
1063        if (e.target.closest('[data-install-app]')) send('install_button_click');
1064        if (e.target.closest('[data-push-settings],#webpushr-subscription-button')) send('push_button_click');
1065        if (e.target.closest('[data-push-disable],#mz-webpushr-unsubscribe')) send('push_disable_click');
1066        if (e.target.closest('[data-security-open]')) send('regional_warnzentrum_open', {entry: 'security_map_button'});
1067
1068        var communityEl = e.target.closest('[data-community-entry]');
1069        if (communityEl) send('wettermelder_open', {entry: clean(communityEl.getAttribute('data-community-entry'), 40)});
1070
1071        var moduleEl = e.target.closest('[data-module]');
1072        if (moduleEl) {
1073          send('module_open', {
1074            module_name: clean(moduleEl.getAttribute('data-module'), 40),
1075            module_label: closestText(moduleEl),
1076            needs_place: moduleEl.hasAttribute('data-needs-place') ? 'yes' : 'no',
1077            target_url: clean(moduleEl.getAttribute('href') || '', 
1077160)
1078          });
1079        }
1080      }, true);
1081      window.addEventListener('beforeinstallprompt', function(){ send('pwa_install_prompt_available'); });
1082      window.addEventListener('appinstalled', function(){ send('pwa_installed'); });
1083      window.addEventListener('mz-place-updated', function(ev){
1084        var p = ev && ev.detail && ev.detail.place;
1085        send('place_saved', {
1086          place_name: p ? clean(p.name || '', 60) : '',
1087          place_state: p ? clean(p.admin1 || '', 50) : '',
1088          place_source: p ? clean(p.source || '', 40) : 'unknown'
1089        });
1090      });
1091      document.addEventListener('visibilitychange', function(){
1092        if (document.visibilityState === 'visible') send('app_visible');
1093      });
1094    }
1095    function watchPlaceLoaded(){
1096      setTimeout(function(){
1097        var p = currentPlace();
1098        if (p && !placeLoadedSent) {
1099          placeLoadedSent = true;
1100          send('place_loaded', {place_source: clean(p.source || '', 40)});
1101        }
1102      }, 1600);
1103    }
1104    initConsentBanner();
1105    bindEvents();
1106    watchPlaceLoaded();
1107    window.MZAnalytics = {track: send, pageView: sendPageView, setConsent: updateConsent};
1108  })();
1109  </script>
vendor: 1 bytes, line 1109
1109
1110<script>
1111/* Report views and likes for the Meteozentrale PWA. Uses the existing WordPress counters. */
1112(() => {
1113  'use strict';
1114
1115  const CONFIG_URL = '/app/index.php';
1116  const VIEW_DELAY_MS = 10000;
1117  const VIEW_KEY_PREFIX = 'mz_app_report_view_v1_';
1118  const numberFormat = new Intl.NumberFormat('de-DE');
1119
1120  let activePostId = 0;
1121  let viewInterval = 0;
1122  let configPromise = null;
1123  let configTime = 0;
1124  const viewed = new Set();
1125
1126  function element(tag, className, text) {
1127    const node = document.createElement(tag);
1128    if (className) node.className = className;
1129    if (text !== undefined) node.textContent = text;
1130    return node;
1131  }
1132
1133  function getConfig() {
1134    if (Date.now() - configTime > 300000) configPromise = null;
1135    if (!configPromise) {
1136      configTime = Date.now();
1137      configPromise = fetch(CONFIG_URL, {
1138        method: 'POST',
1139        body: new URLSearchParams({mz_article_config: '1'}),
1140        credentials: 'same-origin',
1141        cache: 'no-store',
1142        headers: {'Accept': 'application/json'}
1143      })
1144        .then(response => {
1145          if (!response.ok) throw new Error('interaction_config_unavailable');
1146          return response.json();
1147        })
1148        .then(config => {
1149          if (!config?.success || !config?.ajaxUrl || !config?.viewNonce || !config?.reactionNonce) {
1150            throw new Error('interaction_config_invalid');
1151          }
1152          return config;
1153        })
1154        .catch(error => {
1155          configPromise = null;
1156          throw error;
1157        });
1158    }
1159    return configPromise;
1160  }
1161
1162  async function ajax(config, payload) {
1163    const response = await fetch(config.ajaxUrl, {
1164      method: 'POST',
1165      credentials: 'same-origin',
1166      cache: 'no-store',
1167      headers: {
1168        'Accept': 'application/json',
1169        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
1170      },
1171      body: new URLSearchParams(payload).toString()
1172    });
1173    const result = await response.json().catch(() => null);
1174    if (!response.ok || !result?.success) throw new Error('interaction_request_failed');
1175    return result.data || {};
1176  }
1177
1178  function wasViewed(postId) {
1179    if (viewed.has(postId)) return true;
1180    try { return sessionStorage.getItem(VIEW_KEY_PREFIX + postId) === '1'; }
1181    catch { return false; }
1182  }
1183
1184  function rememberView(postId) {
1185    viewed.add(postId);
1186    try { sessionStorage.setItem(VIEW_KEY_PREFIX + postId, '1'); }
1187    catch {}
1188  }
1189
1190  function setViewCount(card, stats) {
1191    const count = card.querySelector('[data-report-views-count]');
1192    const stat = card.querySelector('[data-report-views]');
1193    if (!count || !stat) return;
1194    count.textContent = numberFormat.format(Math.max(0, Number(stats?.total) || 0));
1195    stat.title = `${numberFormat.format(Math.max(0, Number(stats?.today) || 0))} heute`;
1196  }
1197
1198  function setLikeState(card, stats) {
1199    const button = card.querySelector('[data-report-like]');
1200    const count = card.querySelector('[data-report-likes-count]');
1201    if (!button || !count) return;
1202    const liked = stats?.user_reaction === 'like';
1203    const likes = Math.max(0, Number(stats?.counts?.like) || 0);
1204    button.setAttribute('aria-pressed', liked ? 'true' : 'false');
1205    button.classList.toggle('is-liked', liked);
1206    count.textContent = numberFormat.format(likes);
1207    button.setAttribute('aria-label', `${liked ? 'Bericht gefällt dir' : 'Bericht gefällt mir'} · ${numberFormat.format(likes)} Likes`);
1208  }
1209
1210  function setStatus(card, text, tone = '') {
1211    const status = card.querySelector('[data-report-interaction-status]');
1212    if (!status) return;
1213    status.textContent = text;
1214    status.dataset.tone = tone;
1215  }
1216
1217  async function loadStats(postId, card) {
1218    try {
1219      const config = await getConfig();
1220      if (activePostId !== postId || !card.isConnected) return;
1221      const [views, reactions] = await Promise.allSettled([
1222        ajax(config, {
1223          action: 'mz_trendometer_views_stats',
1224          nonce: config.viewNonce,
1225          id: String(postId)
1226        }),
1227        ajax(config, {
1228          action: 'mz_article_reactions_stats',
1229          nonce: config.reactionNonce,
1230          post_id: String(postId)
1231        })
1232      ]);
1233      if (activePostId !== postId || !card.isConnected) return;
1234      if (views.status === 'fulfilled') setViewCount(card, views.value);
1235      if (reactions.status === 'fulfilled') setLikeState(card, reactions.value);
1236      if (views.status === 'rejected' && reactions.status === 'rejected') {
1237        setStatus(card, 'Aufrufe und Likes sind gerade nicht verfügbar.', 'error');
1238      }
1239    } catch {
1240      if (activePostId === postId && card.isConnected) {
1241        setStatus(card, 'Aufrufe und Likes sind gerade nicht verfügbar.', 'error');
1242      }
1243    }
1244  }
1245
1246  async function trackView(postId, card) {
1247    if (wasViewed(postId) || activePostId !== postId || !card.isConnected) return;
1248    rememberView(postId);
1249    try {
1250      const config = await getConfig();
1251      const stats = await ajax(config, {
1252        action: 'mz_trendometer_track',
1253        nonce: config.viewNonce,
1254        type: 'post',
1255        id: String(postId)
1256      });
1257      if (activePostId === postId && card.isConnected) setViewCount(card, stats);
1258      window.MZAnalytics?.track?.('report_view_counted', {post_id: postId});
1259    } catch {
1260      // An uncertain response may already have been counted; do not duplicate it.
1261    }
1262  }
1263
1264  function scheduleView(postId, card) {
1265    clearInterval(viewInterval);
1266    if (wasViewed(postId)) return;
1267    let visibleTime = 0;
1268    let lastTick = performance.now();
1269    viewInterval = window.setInterval(() => {
1270      const now = performance.now();
1271      if (activePostId !== postId || !card.isConnected || card.closest('[hidden]')) {
1272        clearInterval(viewInterval);
1273        return;
1274      }
1275      if (document.visibilityState === 'visible') visibleTime += Math.min(now - lastTick, 600);
1276      lastTick = now;
1277      if (visibleTime >= VIEW_DELAY_MS) {
1278        clearInterval(viewInterval);
1279        trackView(postId, card);
1280      }
1281    }, 500);
1282  }
1283
1284  async function likeReport(postId, card) {
1285    const button = card.querySelector('[data-report-like]');
1286    if (!button || button.disabled) return;
1287    if (button.getAttribute('aria-pressed') === 'true') {
1288      setStatus(card, 'Dein Like ist bereits gespeichert.', 'success');
1289      return;
1290    }
1291    button.disabled = true;
1292    setStatus(card, 'Like wird gespeichert …');
1293    try {
1294      const config = await getConfig();
1295      const stats = await ajax(config, {
1296        action: 'mz_article_react',
1297        nonce: config.reactionNonce,
1298        post_id: String(postId),
1299        reaction: 'like'
1300      });
1301      if (activePostId !== postId || !card.isConnected) return;
1302      setLikeState(card, stats);
1303      setStatus(card, stats.message || 'Danke! Dein Like ist gespeichert.', 'success');
1304      window.MZAnalytics?.track?.('report_like', {post_id: postId});
1305    } catch {
1306      if (activePostId === postId && card.isConnected) {
1307        setStatus(card, 'Der Like konnte gerade nicht gespeichert werden.', 'error');
1308      }
1309    } finally {
1310      if (button.isConnected) button.disabled = false;
1311    }
1312  }
1313
1314  function createCard(postId) {
1315    const card = element('section', 'mz-report-engagement');
1316    card.setAttribute('aria-label', 'Aufrufe und Like');
1317
1318    const views = element('div', 'mz-report-view-stat');
1319    views.dataset.reportViews = '';
1320    views.append(
1321      element('span', 'mz-report-engagement-icon', '👁'),
1322      element('strong', '', '–'),
1323      element('span', '', 'Aufrufe')
1324    );
1325    views.querySelector('strong').dataset.reportViewsCount = '';
1326
1327    const like = element('button', 'mz-report-like');
1328    like.type = 'button';
1329    like.dataset.reportLike = '';
1330    like.setAttribute('aria-pressed', 'false');
1331    like.setAttribute('aria-label', 'Bericht gefällt mir');
1332    like.append(
1333      element('span', 'mz-report-like-heart', '♥'),
1334      element('span', '', 'Gefällt mir'),
1335      element('strong', '', '–')
1336    );
1337    like.querySelector('strong').dataset.reportLikesCount = '';
1338    like.addEventListener('click', () => likeReport(postId, card));
1339
1340    const status = element('p', 'mz-report-engagement-status', 'Aufrufe und Likes aus App und Website.');
1341    status.dataset.reportInteractionStatus = '';
1342    status.setAttribute('role', 'status');
1343
1344    const actions = element('div', 'mz-report-engagement-actions');
1345    actions.append(views, like);
1346    card.append(actions, status);
1347    return card;
1348  }
1349
1350  window.addEventListener('mz:article-read', event => {
1351    const postId = Number(event.detail?.id);
1352    if (!Number.isSafeInteger(postId) || postId < 1) return;
1353    const reader = document.querySelector('.mz-report-reader');
1354    const body = reader?.querySelector('.mz-report-body');
1355    if (!reader || !body) return;
1356
1357    activePostId = postId;
1358    clearInterval(viewInterval);
1359    reader.querySelector('.mz-report-engagement')?.remove();
1360    const card = createCard(postId);
1361    body.insertAdjacentElement('beforebegin', card);
1362    loadStats(postId, card);
1363    scheduleView(postId, card);
1364  });
1365
1366  window.addEventListener('pagehide', () => clearInterval(viewInterval), {once: true});
1367})();
1368
1369</script>
1369
1370  
1370<script src="/app/mz-articles-v2.js?v=20260908-2" defer></script>
1370
1371  
1371<script src="/app/mz-article-preferences-v1.js?v=20260908-2" defer></script>
vendor: 1 bytes, line 1371
1371
1372<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"63195a54b95243a9afd8e8f18b56fb7b","r":1,"spa":2}' crossorigin="anonymous"></script>
1372
1373</body>
1374</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.