{"id":262936,"date":"2026-02-05T14:08:44","date_gmt":"2026-02-05T13:08:44","guid":{"rendered":"https:\/\/othal.de\/262936-2\/"},"modified":"2026-05-12T13:51:27","modified_gmt":"2026-05-12T11:51:27","slug":"262936-2","status":"publish","type":"page","link":"https:\/\/othal.de\/cs\/262936-2\/","title":{"rendered":""},"content":{"rendered":"\n\n\n\n  <meta charset=\"utf-8\"\/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"\/>\n  <title>Status OTHAL \u2013 WhatsApp Generator<\/title>\n\n  <style>\n  html, body { width:100%; max-width:100%; overflow-x:hidden; }\n  \n    \/* ========= OTHAL APP THEME ========= *\/\n    #othal-status-app{\n      --radius:16px;\n      --shadow: 0 10px 34px rgba(0,0,0,.28);\n      --ok:#2dd4bf;\n      --warn:#fbbf24;\n      --bad:#fb7185;\n\n      --bg:#0b1220;\n      --text:#eaf0ff;\n      --muted:#9fb0d0;\n      --line:rgba(255,255,255,.12);\n\n      --card: linear-gradient(180deg, rgba(255,255,255,.030), rgba(255,255,255,.012));\n      --card-status: linear-gradient(180deg, rgba(255,255,255,.038), rgba(255,255,255,.012));\n      --card-urgent: linear-gradient(180deg, rgba(255,255,255,.040), rgba(255,255,255,.012));\n\n      --input: rgba(255,255,255,.05);\n      --input2: rgba(0,0,0,.10);\n      --btn: rgba(255,255,255,.05);\n\n      --btnPrimaryBorder: rgba(255,255,255,.18);\n      --btnPrimaryBg: rgba(255,255,255,.08);\n\n      color: var(--text);\n      background: var(--bg);\n      padding: 0;\n      margin: 0;\n    }\n\n    #othal-status-app .othalStatusWrap{\n      width:100%;\n      margin:0;\n      padding:14px;\n      background: var(--bg);\n      border-radius: 18px;\n      min-height: 100vh;\n    }\n\n    #othal-status-app *{ box-sizing:border-box; }\n    #othal-status-app h1, #othal-status-app h2, #othal-status-app p, #othal-status-app label{ font-family:inherit; }\n\n    #othal-status-app .othalHeader{\n      padding:10px 0 14px;\n      display:flex;\n      align-items:flex-start;\n      justify-content:space-between;\n      gap:14px;\n    }\n\n    #othal-status-app .headLeft h1{\n      margin:0 0 6px;\n      font-size:24px;\n      letter-spacing:.2px;\n      color:#fff !important;\n    }\n\n    #othal-status-app .sub{ margin:0; color:var(--muted); font-size:13px; line-height:1.35; }\n\n    #othal-status-app .headRight{\n      display:flex;\n      align-items:center;\n      gap:12px;\n      flex-wrap:wrap;\n      justify-content:flex-end;\n    }\n\n    #othal-status-app .logo{ height:34px; width:auto; display:block; opacity:.98; }\n\n    #othal-status-app .othalMain{\n      display:grid;\n      grid-template-columns: 1.1fr .9fr;\n      gap:14px;\n    }\n\n    @media (max-width: 980px){\n      #othal-status-app .othalHeader{ flex-direction:column; align-items:stretch; }\n      #othal-status-app .headRight{ justify-content:flex-start; }\n      #othal-status-app .othalMain{ grid-template-columns: 1fr; }\n    }\n\n    #othal-status-app .card{\n      background: var(--card);\n      border:1px solid var(--line);\n      border-radius:var(--radius);\n      padding:14px;\n      box-shadow: var(--shadow);\n    }\n\n    #othal-status-app .card.statusCard{ background: var(--card-status); }\n    #othal-status-app .card.urgentCard{ background: var(--card-urgent); }\n    #othal-status-app .card.full{ grid-column: 1 \/ -1; }\n\n    #othal-status-app .card h2{\n      font-size:16px;\n      margin:0 0 10px;\n      font-weight:800;\n      color:#fff !important;\n    }\n\n    #othal-status-app .row{ display:flex; gap:10px; flex-wrap:wrap; align-items:center; }\n    #othal-status-app label{ font-size:12px; color:var(--muted); display:block; margin:0 0 6px; }\n\n    #othal-status-app input[type=\"text\"],\n    #othal-status-app input[type=\"date\"],\n    #othal-status-app input[type=\"datetime-local\"],\n    #othal-status-app textarea{\n      width:100%;\n      background: var(--input);\n      border:1px solid var(--line);\n      color:var(--text);\n      border-radius:12px;\n      padding:10px 11px;\n      outline:none;\n      font-family: inherit;\n    }\n\n    #othal-status-app textarea{ min-height:120px; resize:vertical; }\n    #othal-status-app #output, #othal-status-app #urgentOutput{ overflow:hidden; resize:none; height:auto; min-height:220px; }\n\n    #othal-status-app .grid{\n      display:grid;\n      grid-template-columns: 1fr 260px;\n      gap:10px;\n      align-items:center;\n      padding:10px;\n      border-radius:12px;\n      border:1px solid var(--line);\n      background: var(--input2);\n      margin-bottom:10px;\n    }\n\n    @media (max-width: 640px){\n      #othal-status-app .grid{ grid-template-columns: 1fr; }\n    }\n\n    #othal-status-app .name{ font-size:13px; line-height:1.25; }\n\n    #othal-status-app .btn{\n      border:1px solid var(--line);\n      background: var(--btn);\n      color:var(--text);\n      border-radius:12px;\n      padding:10px 12px;\n      font-size:12px;\n      cursor:pointer;\n      transition:.15s ease;\n      font-family: inherit;\n      white-space:nowrap;\n    }\n\n    #othal-status-app .btn:hover{ transform: translateY(-1px); }\n\n    #othal-status-app .btn.primary{\n      border-color: var(--btnPrimaryBorder);\n      background: var(--btnPrimaryBg);\n    }\n\n    #othal-status-app .pill{\n      display:inline-flex;\n      align-items:center;\n      gap:6px;\n      border:1px solid var(--line);\n      padding:6px 10px;\n      border-radius:999px;\n      font-size:11px;\n      color:var(--muted);\n      background: var(--btn);\n    }\n\n    #othal-status-app .dot{ width:8px;height:8px;border-radius:999px; background: var(--warn); display:inline-block; }\n    #othal-status-app .dot.ok{ background: var(--ok); }\n    #othal-status-app .dot.warn{ background: var(--warn); }\n    #othal-status-app .dot.bad{ background: var(--bad); }\n\n    #othal-status-app .footer-note{ margin-top:10px; color:var(--muted); font-size:11px; line-height:1.35; }\n    #othal-status-app .mini{ font-size:11px; color:var(--muted); }\n    #othal-status-app .rightCol{ display:flex; flex-direction:column; gap:14px; }\n\n    #othal-status-app .metaLine{\n      margin-top:6px;\n      font-size:12px;\n      color: var(--muted);\n      display:flex;\n      gap:10px;\n      flex-wrap:wrap;\n      align-items:center;\n    }\n    #othal-status-app .metaBadge{\n      border:1px solid var(--line);\n      background: var(--btn);\n      padding:6px 10px;\n      border-radius:999px;\n      font-size:11px;\n      color: var(--muted);\n    }\n\n    \/* Read-only Status Chips *\/\n    #othal-status-app .statusBox{\n      display:flex;\n      align-items:center;\n      justify-content:flex-end;\n      gap:10px;\n      flex-wrap:wrap;\n      font-size:12px;\n      color: var(--muted);\n    }\n    #othal-status-app .statusTag{\n      display:inline-flex;\n      align-items:center;\n      gap:8px;\n      border:1px solid var(--line);\n      background: rgba(255,255,255,.04);\n      padding:8px 10px;\n      border-radius:999px;\n      white-space:nowrap;\n    }\n    #othal-status-app .statusTag b{ color: var(--text); font-weight:800; }\n\n    \/* ========= PASSWORD GATE ========= *\/\n    #othal-status-gate{\n      position:fixed; inset:0;\n      display:flex; align-items:center; justify-content:center;\n      padding:18px;\n      background: rgba(0,0,0,.55);\n      backdrop-filter: blur(8px);\n      z-index: 9999;\n    }\n    #othal-status-gate .gateCard{\n      width: min(520px, 100%);\n      border-radius: 16px;\n      border: 1px solid rgba(255,255,255,.18);\n      background: rgba(17,24,39,.92);\n      color: #eaf0ff;\n      box-shadow: 0 18px 60px rgba(0,0,0,.45);\n      padding: 16px;\n      font-family: inherit;\n    }\n    #othal-status-gate .gateTitle{ font-size: 18px; font-weight: 900; margin-bottom: 6px; color:#fff !important; }\n    #othal-status-gate .gateSub{ font-size: 13px; opacity:.85; margin-bottom: 12px; }\n    #othal-status-gate .gateRow{ display:flex; gap:10px; align-items:center; }\n    #othal-status-gate input{\n      flex:1;\n      border-radius:12px;\n      border:1px solid rgba(255,255,255,.18);\n      background: rgba(255,255,255,.06);\n      color:#fff;\n      padding:10px 11px;\n      outline:none;\n      font-family: inherit;\n    }\n    #othal-status-gate .gateMsg{ margin-top:10px; font-size: 12px; min-height: 18px; }\n    #othal-status-gate .gateHint{ margin-top:8px; font-size: 11px; opacity: .7; line-height: 1.35; }\n    \n    \/* ========= Collapsible Sections (5\u20139) ========= *\/\n    \n#othal-status-app details.card{\n  padding: 0;            \/* wir steuern Innenabstand \u00fcber content *\/\n}\n\n#othal-status-app details.card > summary{\n  list-style: none;\n  cursor: pointer;\n  user-select: none;\n  padding: 14px;\n  border-radius: var(--radius);\n  display:flex;\n  align-items:center;\n  justify-content:space-between;\n  gap:12px;\n}\n\n#othal-status-app details.card > summary::-webkit-details-marker{ display:none; }\n\n#othal-status-app .sumTitle{\n  display:flex;\n  flex-direction:column;\n  gap:4px;\n}\n#othal-status-app .sumTitle b{\n  font-size:16px;\n  font-weight:900;\n  color:#fff;\n}\n#othal-status-app .sumTitle span{\n  font-size:11px;\n  color: var(--muted);\n}\n\n#othal-status-app .chev{\n  font-size:14px;\n  color: var(--muted);\n  transition: transform .15s ease;\n}\n\n#othal-status-app details[open] .chev{ transform: rotate(180deg); }\n\n#othal-status-app .detailsContent{\n  padding: 0 14px 14px;\n  border-top: 1px solid var(--line);\n}\n  <\/style>\n\n\n\n  <!-- APP (versteckt bis Passwort ok) -->\n  <div id=\"othal-status-app\" style=\"display:none;\">\n    <div class=\"othalStatusWrap\" autocomplete=\"off\">\n      <header class=\"othalHeader\">\n        <div class=\"headLeft\">\n          <h1>Status OTHAL \u2013 WhatsApp Generator<\/h1>\n          <p class=\"sub\">\u00c4nderungen\/R\u00fcckfragen >> Erik Schulze +4916094992803 \/ erik.schulze@othal.de<\/p>\n\n          <div class=\"metaLine\">\n            <span class=\"metaBadge\" id=\"metaFeratel\">Letzter Status von: \u2013<\/span>\n            <span class=\"metaBadge\" id=\"metaSnow\">Schnee: \u2013<\/span>\n            <span class=\"metaBadge\" id=\"metaWeather\">Wetter: \u2013<\/span>\n          <\/div>\n\n          <div class=\"mini\" id=\"liveMeta\"><\/div>\n        <\/div>\n\n        <div class=\"headRight\">\n          <button class=\"btn\" id=\"liveFetchBtn\" type=\"button\">Live-Daten aktualisieren<\/button>\n          <label class=\"mini\" style=\"display:flex; align-items:center; gap:8px; user-select:none;\">\n            <input id=\"liveAuto\" type=\"checkbox\" checked=\"checked\"\/>\n            Auto (60s)\n          <\/label>\n          <img decoding=\"async\" class=\"logo\" alt=\"OTHAL Logo\" src=\"https:\/\/othal.de\/wp-content\/uploads\/2023\/04\/Logo-LGO_Popup.png\"\/>\n          <button class=\"btn\" id=\"logoutBtn\" type=\"button\" title=\"Sperren \/ Passwort erneut abfragen\">Sperren<\/button>\n        <\/div>\n      <\/header>\n\n      <main class=\"othalMain\">\n        <section class=\"card statusCard\">\n          <h2>1) Basis \u2013 Wetter (automatisch) &#038; Schnee (manuell)<\/h2>\n\n          <div class=\"row\">\n            <div style=\"flex:1; min-width:220px;\">\n              <label for=\"location\">Ort \/ Gebiet<\/label>\n              <input id=\"location\" type=\"text\" value=\"Oberwiesenthal \/ Fichtelberg\"\/>\n            <\/div>\n            <div style=\"width:200px; min-width:170px;\">\n              <label for=\"date\">Datum<\/label>\n              <input id=\"date\" type=\"date\"\/>\n            <\/div>\n          <\/div>\n\n          <div class=\"row\" style=\"margin-top:10px;\">\n            <div style=\"flex:1; min-width:220px;\">\n              <label for=\"weather\">Aktuelles Wetter (Tagesvorschau)<\/label>\n              <input id=\"weather\" type=\"text\" placeholder=\"Wird automatisch bef\u00fcllt\u2026\"\/>\n              <div class=\"mini\" id=\"weatherMeta\"><\/div>\n            <\/div>\n            <div style=\"width:240px; min-width:220px;\">\n              <label> <\/label>\n              <button class=\"btn primary\" id=\"fetchWeather\" style=\"width:100%;\" type=\"button\">Wetter holen<\/button>\n            <\/div>\n          <\/div>\n\n          <!-- MANUELLER SCHNEE -->\n          <div class=\"row\" style=\"margin-top:10px;\">\n            <div style=\"flex:1; min-width:160px;\">\n              <label for=\"snowValley\">Schneeh\u00f6he Tal (cm)<\/label>\n              <input id=\"snowValley\" type=\"text\" inputmode=\"numeric\" placeholder=\"z.B. 20\"\/>\n            <\/div>\n\n            <div style=\"flex:1; min-width:160px;\">\n              <label for=\"snowMountain\">Schneeh\u00f6he Berg (cm)<\/label>\n              <input id=\"snowMountain\" type=\"text\" inputmode=\"numeric\" placeholder=\"z.B. 80\"\/>\n            <\/div>\n\n            <div style=\"flex:1; min-width:220px;\">\n              <label for=\"snowLast\">Letzter Schneefall (Datum)<\/label>\n              <input id=\"snowLast\" type=\"date\"\/>\n              <div class=\"mini\" id=\"snowMeta\">Manuelle Eingabe<\/div>\n            <\/div>\n          <\/div>\n\n          <div class=\"row\" style=\"margin-top:10px;\">\n            <div style=\"flex:1; min-width:220px;\">\n              <label for=\"notes\">Hinweise (optional)<\/label>\n             <input id=\"notes\" type=\"text\" autocomplete=\"off\" autocorrect=\"off\" autocapitalize=\"off\" spellcheck=\"false\" placeholder=\"Hinweise (optional)\"\/>            <\/div>\n          <\/div>\n\n          <div class=\"row\" style=\"margin-top:10px;\">\n            <button class=\"btn\" id=\"reset\" type=\"button\">Reset (Hinweise\/Events\/Eilmeldung)<\/button>\n          <\/div>\n\n          <p class=\"footer-note\">\n            Statusdaten: Feratel (read-only) \u2022 Wetter: Open-Meteo \u2022 Schnee: manuelle Eingabe.\n          <\/p>\n        <\/section>\n\n        <section class=\"rightCol\">\n          <section class=\"card statusCard\">\n            <h2>2) WhatsApp-Text<\/h2>\n\n            <div class=\"row\" style=\"justify-content:space-between; margin-bottom:8px;\">\n              <span class=\"pill\"><span class=\"dot\" id=\"statusDot\"><\/span><span id=\"statusSummary\">Pisten: \u2013<\/span><\/span>\n              <button class=\"btn primary\" id=\"copy\" type=\"button\">Kopieren<\/button>\n            <\/div>\n\n            <p class=\"footer-note\" id=\"copyHint\" style=\"margin-top:0; margin-bottom:10px;\"><\/p>\n            <textarea id=\"output\" readonly=\"readonly\"><\/textarea>\n          <\/section>\n\n          <section class=\"card urgentCard\">\n            <h2>3) Eilmeldung<\/h2>\n\n            <div class=\"row\">\n              <div style=\"flex:1; min-width:210px;\">\n                <label for=\"urgentDatetime\">Datum &#038; Uhrzeit<\/label>\n                <input id=\"urgentDatetime\" type=\"datetime-local\"\/>\n                <div class=\"mini\">Wird automatisch gesetzt. Optional anpassen.<\/div>\n              <\/div>\n              <div style=\"width:200px; min-width:190px;\">\n                <label> <\/label>\n                <button class=\"btn\" id=\"urgentNow\" style=\"width:100%;\" type=\"button\">Zeit = jetzt<\/button>\n              <\/div>\n            <\/div>\n\n            <div class=\"row\" style=\"margin-top:10px;\">\n              <div style=\"flex:1; min-width:210px;\">\n                <label for=\"urgentText\">Eilmeldung (Freitext)<\/label>\n                <textarea id=\"urgentText\" placeholder=\"z.B. Schwebebahn wegen starkem Wind vor\u00fcbergehend au\u00dfer Betrieb. Update folgt.\"><\/textarea>\n                <div class=\"mini\">Kurz &#038; klar: Ursache + Status + ggf. Ausblick.<\/div>\n              <\/div>\n            <\/div>\n\n            <div class=\"row\" style=\"margin-top:10px; gap:10px;\">\n              <button class=\"btn primary\" id=\"urgentCopy\" type=\"button\">Eilmeldung kopieren<\/button>\n              <button class=\"btn\" id=\"urgentClear\" type=\"button\">Leeren<\/button>\n            <\/div>\n\n            <label style=\"margin-top:10px;\">Eilmeldung Output<\/label>\n            <textarea id=\"urgentOutput\" readonly=\"readonly\"><\/textarea>\n\n            <p class=\"footer-note\" id=\"urgentHint\"><\/p>\n          <\/section>\n        <\/section>\n\n        <!-- \u2705 Events hochgezogen -->\n        <section class=\"card statusCard full\">\n          <h2>4) Events &#038; Vorschau<\/h2>\n          <div class=\"row\">\n            <div style=\"flex:1; min-width:260px;\">\n              <label for=\"eventsToday\">Events heute (Freitext)<\/label>\n              <textarea id=\"eventsToday\" placeholder=\"z.B. 18:00 Uhr Eisdisko | 20:30 Uhr Nachtskilauf Start | \u2026\"><\/textarea>\n            <\/div>\n            <div style=\"flex:1; min-width:260px;\">\n              <label for=\"eventsPreview\">Eventvorschau (Freitext)<\/label>\n              <textarea id=\"eventsPreview\" placeholder=\"z.B. Sa: DJ Night | So: Familienrennen | N\u00e4chste Woche: \u2026\"><\/textarea>\n            <\/div>\n          <\/div>\n        <\/section>\n\n        <details class=\"card statusCard full\" id=\"sec5\">\n  <summary>\n    <div class=\"sumTitle\">\n      <b>5) Pistenstatus (Feratel)<\/b>\n      <span>Zum Anzeigen aufklappen<\/span>\n    <\/div>\n    <span class=\"chev\">\u25bc<\/span>\n  <\/summary>\n  <div class=\"detailsContent\">\n    <div id=\"pisteList\"><\/div>\n  <\/div>\n<\/details>\n\n        <details class=\"card statusCard full\" id=\"sec6\">\n  <summary>\n    <div class=\"sumTitle\">\n      <b>6) Liftanlagen (Feratel)<\/b>\n      <span>Zum Anzeigen aufklappen<\/span>\n    <\/div>\n    <span class=\"chev\">\u25bc<\/span>\n  <\/summary>\n  <div class=\"detailsContent\">\n    <div id=\"liftList\"><\/div>\n  <\/div>\n<\/details>\n\n        <details class=\"card statusCard full\" id=\"sec7\">\n  <summary>\n    <div class=\"sumTitle\">\n      <b>7) Erlebnisangebote (Feratel)<\/b>\n      <span>Zum Anzeigen aufklappen<\/span>\n    <\/div>\n    <span class=\"chev\">\u25bc<\/span>\n  <\/summary>\n  <div class=\"detailsContent\">\n    <div id=\"expList\"><\/div>\n  <\/div>\n<\/details>\n\n       <details class=\"card statusCard full\" id=\"sec8\">\n  <summary>\n    <div class=\"sumTitle\">\n      <b>8) Wanderwege &#038; Loipen (Feratel)<\/b>\n      <span>Zum Anzeigen aufklappen<\/span>\n    <\/div>\n    <span class=\"chev\">\u25bc<\/span>\n  <\/summary>\n  <div class=\"detailsContent\">\n    <div id=\"attrList\"><\/div>\n  <\/div>\n<\/details>\n\n       <details class=\"card statusCard full\" id=\"sec9\">\n  <summary>\n    <div class=\"sumTitle\">\n      <b>9) Transport (Feratel)<\/b>\n      <span>Zum Anzeigen aufklappen<\/span>\n    <\/div>\n    <span class=\"chev\">\u25bc<\/span>\n  <\/summary>\n  <div class=\"detailsContent\">\n    <div id=\"transportList\"><\/div>\n  <\/div>\n<\/details>\n\n      <\/main>\n    <\/div>\n  <\/div>\n\n  <!-- PASSWORD GATE -->\n  <div id=\"othal-status-gate\" aria-modal=\"true\" role=\"dialog\">\n    <div class=\"gateCard\">\n      <div class=\"gateTitle\">Status OTHAL \u2013 Zugriff<\/div>\n      <div class=\"gateSub\">Bitte Passwort eingeben, um den Status-Generator zu \u00f6ffnen.<\/div>\n\n      <div class=\"gateRow\">\n        <input id=\"othalGatePw\" type=\"password\" inputmode=\"numeric\" autocomplete=\"off\" placeholder=\"Passwort\"\/>\n        <button class=\"btn primary\" id=\"othalGateBtn\" type=\"button\">\u00d6ffnen<\/button>\n      <\/div>\n\n      <div class=\"gateMsg\" id=\"othalGateMsg\"><\/div>\n      <div class=\"gateHint\">Tipp: Zus\u00e4tzlich kann die WP-Seite als \u201ePasswortgesch\u00fctzt\u201c gesetzt werden.<\/div>\n    <\/div>\n  <\/div>\n\n  <script>\n  (function(){\n    \/**\n     * =========================\n     * KONFIG\n     * =========================\n     *\/\n    const PASSWORD = \"1215\";\n    const STORAGE_KEY = \"othal_status_authed_v1\";\n    const DEFAULT_NOTES = \"Bitte beachten: Der Status einzelner Anlagen und Pisten kann sich kurzfristig \u00e4ndern!\";\n\n    \/\/ Feratel XML\n    const FERATEL_URL = \"https:\/\/cpsnetxml.feratel.com\/cpsnet20\/internet\/?id=FINET,114,100\";\n\n    \/**\n     * OPTIONAL: Proxy, wenn CORS blockiert (z.B. Cloudflare Worker)\n     * const PROXY_PREFIX = \"https:\/\/dein-proxy.example\/fetch?url=\";\n     *\/\n    const PROXY_PREFIX = \"\"; \/\/ <= hier setzen, falls n\u00f6tig\n\n    \/\/ Open-Meteo Koordinaten Oberwiesenthal\n    const OWS_LAT = 50.419167;\n    const OWS_LON = 12.970833;\n\n    \/**\n     * =========================\n     * DOM\n     * =========================\n     *\/\n    const app = document.getElementById(\"othal-status-app\");\n    const gate = document.getElementById(\"othal-status-gate\");\n    const gatePw = document.getElementById(\"othalGatePw\");\n    const gateBtn = document.getElementById(\"othalGateBtn\");\n    const gateMsg = document.getElementById(\"othalGateMsg\");\n    const logoutBtn = document.getElementById(\"logoutBtn\");\n\n    const elLiveFetchBtn = document.getElementById(\"liveFetchBtn\");\n    const elLiveAuto = document.getElementById(\"liveAuto\");\n    const elLiveMeta = document.getElementById(\"liveMeta\");\n\n    const elMetaFeratel = document.getElementById(\"metaFeratel\");\n    const elMetaSnow = document.getElementById(\"metaSnow\");\n    const elMetaWeather = document.getElementById(\"metaWeather\");\n\n    const elDate = document.getElementById(\"date\");\n    const elLocation = document.getElementById(\"location\");\n\n    const elWeather = document.getElementById(\"weather\");\n    const elWeatherMeta = document.getElementById(\"weatherMeta\");\n    const elFetchWeather = document.getElementById(\"fetchWeather\");\n\n    \/\/ MANUELLER SCHNEE\n    const elSnowValley = document.getElementById(\"snowValley\");\n    const elSnowMountain = document.getElementById(\"snowMountain\");\n    const elSnowLast = document.getElementById(\"snowLast\");\n    const elSnowMeta = document.getElementById(\"snowMeta\");\n\n    const elNotes = document.getElementById(\"notes\");\n\n    const elPisteList = document.getElementById(\"pisteList\");\n    const elLiftList  = document.getElementById(\"liftList\");\n    const elAttrList  = document.getElementById(\"attrList\");\n    const elExpList   = document.getElementById(\"expList\");\n    const elTransportList = document.getElementById(\"transportList\");\n\n    const elEventsToday = document.getElementById(\"eventsToday\");\n    const elEventsPreview = document.getElementById(\"eventsPreview\");\n\n    const elOutput = document.getElementById(\"output\");\n    const elCopyHint = document.getElementById(\"copyHint\");\n    const elStatusSummary = document.getElementById(\"statusSummary\");\n    const elStatusDot = document.getElementById(\"statusDot\");\n\n    const elUrgentDatetime = document.getElementById(\"urgentDatetime\");\n    const elUrgentNow = document.getElementById(\"urgentNow\");\n    const elUrgentText = document.getElementById(\"urgentText\");\n    const elUrgentOutput = document.getElementById(\"urgentOutput\");\n    const elUrgentCopy = document.getElementById(\"urgentCopy\");\n    const elUrgentClear = document.getElementById(\"urgentClear\");\n    const elUrgentHint = document.getElementById(\"urgentHint\");\n\n    const elReset = document.getElementById(\"reset\");\n\n    \/**\n     * =========================\n     * STATE\n     * =========================\n     *\/\n    let PISTES = [];\n    let LIFTS = [];\n    let ROUTES = [];       \/\/ Wanderwege &#038; Loipen (ohne Funpark\/Rodeln)\n    let EXPERIENCES = [];  \/\/ Erlebnisangebote (inkl. Funpark + Rodeln)\n    let TRANSPORT = [];    \/\/ Skibus\n\n    const state = { pistes:{}, lifts:{}, routes:{}, exp:{}, transport:{} };\n\n    let feratelUpdatedRaw = \"\";\n\n    \/**\n     * =========================\n     * HELPERS\n     * =========================\n     *\/\n    function autoGrow(el){ if(el){ el.style.height=\"auto\"; el.style.height=(el.scrollHeight+2)+\"px\"; } }\n    function pad2(n){ return String(n).padStart(2,\"0\"); }\n    function toDatetimeLocal(d){ return `${d.getFullYear()}-${pad2(d.getMonth()+1)}-${pad2(d.getDate())}T${pad2(d.getHours())}:${pad2(d.getMinutes())}`; }\n    function formatDateDE(iso){ if(!iso) return \"\"; const [y,m,d] = iso.split(\"-\"); return `${d}.${m}.${y}`; }\n\n    function datetimeLocalToDE(dtLocal){\n      if(!dtLocal || !dtLocal.includes(\"T\")) return \"\";\n      const [date, time] = dtLocal.split(\"T\");\n      const [y,m,d] = date.split(\"-\");\n      const [hh,mi] = time.split(\":\");\n      return `${d}.${m}.${y} ${hh}:${mi} Uhr`;\n    }\n\n    function weatherCodeDE(code){\n      const map = {\n        0:\"Klar\",1:\"Meist klar\",2:\"Teils bew\u00f6lkt\",3:\"Bew\u00f6lkt\",\n        45:\"Nebel\",48:\"Nebel (Reif)\",\n        51:\"Niesel (leicht)\",53:\"Niesel (m\u00e4\u00dfig)\",55:\"Niesel (stark)\",\n        56:\"Gefr. Niesel (leicht)\",57:\"Gefr. Niesel (stark)\",\n        61:\"Regen (leicht)\",63:\"Regen (m\u00e4\u00dfig)\",65:\"Regen (stark)\",\n        66:\"Gefr. Regen (leicht)\",67:\"Gefr. Regen (stark)\",\n        71:\"Schnee (leicht)\",73:\"Schnee (m\u00e4\u00dfig)\",75:\"Schnee (stark)\",\n        77:\"Schneegriesel\",\n        80:\"Schauer (leicht)\",81:\"Schauer (m\u00e4\u00dfig)\",82:\"Schauer (heftig)\",\n        85:\"Schneeschauer (leicht)\",86:\"Schneeschauer (stark)\",\n        95:\"Gewitter\",96:\"Gewitter mit Hagel (leicht)\",99:\"Gewitter mit Hagel (stark)\"\n      };\n      return map[code] ?? `Wettercode ${code}`;\n    }\n\n    function buildSnowManual(){\n      const v = (elSnowValley?.value || \"\").trim();\n      const b = (elSnowMountain?.value || \"\").trim();\n      const lastIso = (elSnowLast?.value || \"\").trim();\n\n      const parts = [];\n      if(v) parts.push(`Tal: ${v} cm`);\n      if(b) parts.push(`Berg: ${b} cm`);\n\n      const lastDE = formatDateDE(lastIso);\n      if(lastDE) parts.push(`letzter Schneefall: ${lastDE}`);\n\n      const ui = parts.length ? parts.join(\" \u2022 \") : \"\";\n      const wa = ui ? `\u2744\ufe0f Schneelage: ${ui}` : \"\";\n\n      return { ui, wa };\n    }\n\n    function mapStatusToOpenClosed(s){\n      const t = (s || \"\").toLowerCase();\n      if(t.includes(\"offen\")) return \"open\";\n      if(t.includes(\"gesperrt\") || t.includes(\"geschlossen\")) return \"closed\";\n      return null;\n    }\n\n    \/\/ \u2705 Pisten-Gelb bei \"teilweise befahrbar\" (oder \u00e4hnlich)\n    function mapStatusToAmpel(s){\n      const t = (s || \"\").toLowerCase();\n      if(t.includes(\"teilweise\") || t.includes(\"eingeschr\") || t.includes(\"bedingt\") || t.includes(\"partial\")) return \"yellow\";\n      if(t.includes(\"offen\")) return \"green\";\n      if(t.includes(\"gesperrt\") || t.includes(\"geschlossen\")) return \"red\";\n      return null;\n    }\n\n    function summarizePistes(){\n      const vals = Object.values(state.pistes || {});\n      const chosen = vals.filter(v => v !== null && v !== undefined).length;\n      const g = vals.filter(v => v === \"green\").length;\n      const y = vals.filter(v => v === \"yellow\").length;\n      const r = vals.filter(v => v === \"red\").length;\n      return { chosen, g, y, r };\n    }\n\n    function updateSummaryPill(){\n      const { chosen, g, y, r } = summarizePistes();\n      if(chosen === 0){\n        elStatusSummary.textContent = \"Pisten: \u2013\";\n        elStatusDot.className = \"dot warn\";\n        return;\n      }\n      elStatusSummary.textContent = `Pisten: ${g} Gr\u00fcn \/ ${y} Gelb \/ ${r} Rot`;\n      elStatusDot.className = r > 0 ? \"dot bad\" : y > 0 ? \"dot warn\" : \"dot ok\";\n    }\n\n    function text(el, sel){\n      const n = el.querySelector(sel);\n      return n ? (n.textContent || \"\").trim() : \"\";\n    }\n\n    function norm(s){\n      return (s || \"\").toLowerCase().replace(\/\\s+\/g,\" \").trim();\n    }\n\n    \/\/ explizit: diese sollen in Erlebnisangebote\n    function isForcedExperienceInfra(name){\n  const n = norm(name);\n  return (\n    n.includes(\"othal-coaster\") ||\n    n.includes(\"othal coaster\") ||\n    n.includes(\"fly line\") ||\n    n.includes(\"rodelhang\") ||\n    n.includes(\"rodelstrecke\") ||\n    n.includes(\"funpark\") ||\n    n.includes(\"kunsteisbahn\")    \/\/ \u2705 NEU\n  );\n}\n    function isTransportInfra(name, typekrz, typename){\n      const n = norm(name);\n      const s = `${name} ${typekrz} ${typename}`.toLowerCase();\n      return n.includes(\"skibus\") || s.includes(\"skibus\") || n.includes(\"ski bus\") || s.includes(\"ski bus\");\n    }\n\n    function isExperienceInfra(name, typekrz, typename){\n      if(isForcedExperienceInfra(name)) return true;\n      const s = `${name} ${typekrz} ${typename}`.toLowerCase();\n      if(s.includes(\"nachtskifahren\") || s.includes(\"flutlicht\") || s.includes(\"eisdisko\")) return true;\n      return false;\n    }\n\n    function resetDynamicLists(){\n      PISTES = [];\n      LIFTS = [];\n      ROUTES = [];\n      EXPERIENCES = [];\n      TRANSPORT = [];\n      state.pistes = {};\n      state.lifts = {};\n      state.routes = {};\n      state.exp = {};\n      state.transport = {};\n    }\n\n    function upsertItem(arr, id, name){\n      if(arr.find(x => x.id === id)) return;\n      arr.push({ id, name });\n    }\n\n    \/\/ \u2705 EINZIGE FUNKTIONALE \u00c4NDERUNG: Kunsteisbahn aus allen Listen entfernen -> Erlebnisangebote\n    function moveKunsteisbahnToExperiences(){\n      const isKB = (name) => norm(name).includes(\"kunsteisbahn\");\n\n      \/\/ LIFTS -> EXPERIENCES\n      for(let i = LIFTS.length - 1; i >= 0; i--){\n        const it = LIFTS[i];\n        if(!isKB(it.name)) continue;\n\n        if(!EXPERIENCES.find(x => x.id === it.id)) EXPERIENCES.push(it);\n        state.exp[it.id] = state.lifts[it.id];\n        delete state.lifts[it.id];\n        LIFTS.splice(i, 1);\n      }\n\n      \/\/ ROUTES -> EXPERIENCES\n      for(let i = ROUTES.length - 1; i >= 0; i--){\n        const it = ROUTES[i];\n        if(!isKB(it.name)) continue;\n\n        if(!EXPERIENCES.find(x => x.id === it.id)) EXPERIENCES.push(it);\n        state.exp[it.id] = state.routes[it.id];\n        delete state.routes[it.id];\n        ROUTES.splice(i, 1);\n      }\n\n      \/\/ TRANSPORT -> EXPERIENCES (nur zur Sicherheit)\n      for(let i = TRANSPORT.length - 1; i >= 0; i--){\n        const it = TRANSPORT[i];\n        if(!isKB(it.name)) continue;\n\n        if(!EXPERIENCES.find(x => x.id === it.id)) EXPERIENCES.push(it);\n        state.exp[it.id] = state.transport[it.id];\n        delete state.transport[it.id];\n        TRANSPORT.splice(i, 1);\n      }\n    }\n    \n    function isOpen(v){ return v === \"open\"; }\n    \n    function isSkibusRunning(){\n  \/\/ Es reicht, wenn EIN Skibus-Eintrag offen ist\n  return TRANSPORT.some(t => state.transport[t.id] === \"open\");\n}\n\n\/\/ erkennt \"5,6 km\", \"5.6km\", \"16,0km\", \"2,3km\" etc.\nfunction extractKmFromName(name){\n  const s = (name || \"\").toLowerCase();\n  const m = s.match(\/(\\d+(?:[.,]\\d+)?)\\s*(km|kilometer)\\b\/);\n  if(!m) return null;\n  const num = parseFloat(m[1].replace(\",\", \".\"));\n  return Number.isFinite(num) ? num : null;\n}\n\nfunction formatKm(km){\n  const v = Math.round(km * 10) \/ 10; \/\/ 1 Nachkommastelle\n  const str = (v % 1 === 0) ? String(Math.round(v)) : String(v).replace(\".\", \",\");\n  return `${str} km`;\n}\n\n\/\/ Klassifikation nach deinem Feratel-Pattern:\n\/\/ Lx = Loipe, Sx = Skiwanderweg (z\u00e4hlen wir zu Loipen-km), Wx = Winterwanderweg (Wanderwege-km)\nfunction classifyRoute(name){\n  const s = (name || \"\").trim().toLowerCase();\n\n  \/\/ Prefix-Logik (stabil bei deinen Beispielen)\n  if(\/^l\\d+\\b\/.test(s)) return \"loipe\";\n  if(\/^s\\d+\\b\/.test(s)) return \"loipe\";    \/\/ Skiwanderweg -> Loipen km\n  if(\/^w\\d+\\b\/.test(s)) return \"wander\";\n\n  \/\/ Fallback \u00fcber Keywords\n  if(s.includes(\"loipe\") || s.includes(\"skiwander\") || s.includes(\"langlauf\")) return \"loipe\";\n  if(s.includes(\"wanderweg\") || s.includes(\"schutzweg\") || s.includes(\"fu\u00dfg\u00e4nger\")) return \"wander\";\n\n  \/\/ Unklar -> Wander (konservativ)\n  return \"wander\";\n}\n\nfunction buildDynamicIntroText(){\n  \/\/ \"x von x Anlagen\": ich nehme Anlagen = Liftanlagen\n  const totalLifts = LIFTS.length;\n  const openLifts = LIFTS.filter(l => isOpen(state.lifts[l.id])).length;\n\n  \/\/ Erlebnisangebote offen (Namenliste)\n  const openExperiences = EXPERIENCES\n    .filter(e => isOpen(state.exp[e.id]))\n    .map(e => e.name);\n\n  \/\/ Events (Freitext, wenn bef\u00fcllt)\n  const eventsToday = (elEventsToday?.value || \"\").trim();\n  const eventsTodayInline = eventsToday\n    ? eventsToday.replace(\/\\n+\/g, \" | \").replace(\/\\s+\\|\\s+\/g, \" | \")\n    : \"\";\n\n  \/\/ km-Summen\n  let openWanderKm = 0, openLoipeKm = 0;\n  let openWanderCount = 0, openLoipeCount = 0;\n  let openWanderNoKm = 0, openLoipeNoKm = 0;\n\n  ROUTES.forEach(r => {\n    if(!isOpen(state.routes[r.id])) return;\n\n    const km = extractKmFromName(r.name);\n    const kind = classifyRoute(r.name);\n\n    if(kind === \"loipe\"){\n      openLoipeCount++;\n      if(km !== null) openLoipeKm += km; else openLoipeNoKm++;\n    }else{\n      openWanderCount++;\n      if(km !== null) openWanderKm += km; else openWanderNoKm++;\n    }\n  });\n\n  const parts = [];\n  parts.push(\"Guten Morgen und herzlich Willkommen am Fichtelberg.\");\n\n  if(totalLifts > 0){\n    parts.push(`F\u00fcr Sie sind heute ${openLifts} von ${totalLifts} Anlagen in Betrieb.`);\n  }\n\n  if(openExperiences.length){\n    parts.push(`*Erlebnisangebote heute:* ${openExperiences.join(\", \")}.`);\n  }\n\n  if(eventsTodayInline){\n    parts.push(`*Events heute:* ${eventsTodayInline}.`);\n  }\n\n  if(openWanderCount > 0){\n    parts.push(\n      openWanderKm > 0\n        ? `*Wanderwege:* ${formatKm(openWanderKm)} ge\u00f6ffnet.`\n        : `*Wanderwege:* ${openWanderCount} Strecken ge\u00f6ffnet.`\n    );\n  }\n\n  if(openLoipeCount > 0){\n    parts.push(\n      openLoipeKm > 0\n        ? `*Loipen:* ${formatKm(openLoipeKm)} ge\u00f6ffnet.`\n        : `*Loipen:* ${openLoipeCount} Strecken ge\u00f6ffnet.`\n    );\n  }\n\n\/\/ Skibus (nur wenn er f\u00e4hrt)\nif(isSkibusRunning()){\n  parts.push(\"Der Skibus vom Fichtelberg zum Keilberg f\u00e4hrt heute von 9:30 Uhr bis 16:30 Uhr.\");\n}\n\n  return parts.join(\" \");\n}\n\n    \/**\n     * =========================\n     * RENDER (READ-ONLY)\n     * =========================\n     *\/\n    function renderAmpel(container, items, key){\n      container.innerHTML = \"\";\n      items.forEach(item => {\n        const wrap = document.createElement(\"div\");\n        wrap.className = \"grid\";\n\n        const left = document.createElement(\"div\");\n        left.innerHTML = `<div class=\"name\"><b>${item.name}<\/script><span class=\"et_bloom_bottom_trigger\"><\/span>","protected":false},"excerpt":{"rendered":"<p>Status OTHAL \u2013 WhatsApp Generator Status OTHAL \u2013 WhatsApp Generator \u00c4nderungen\/R\u00fcckfragen >> Erik Schulze +4916094992803 \/ erik.schulze@othal.de Letzter Status von: \u2013 Schnee: \u2013 Wetter: \u2013 Live-Daten aktualisieren Auto (60s) Sperren 1) Basis \u2013 Wetter (automatisch) &#038; Schnee (manuell) Ort \/ Gebiet Datum Aktuelles Wetter (Tagesvorschau) Wetter holen Schneeh\u00f6he Tal (cm) Schneeh\u00f6he Berg (cm) Letzter Schneefall [&hellip;]<\/p>\n","protected":false},"author":5,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"page-template-blank.php","meta":{"_acf_changed":false,"_et_pb_use_builder":"off","_et_pb_old_content":"","_et_gb_content_width":"","inline_featured_image":false,"footnotes":""},"class_list":["post-262936","page","type-page","status-publish","hentry"],"acf":[],"_links":{"self":[{"href":"https:\/\/othal.de\/cs\/wp-json\/wp\/v2\/pages\/262936","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/othal.de\/cs\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/othal.de\/cs\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/othal.de\/cs\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/othal.de\/cs\/wp-json\/wp\/v2\/comments?post=262936"}],"version-history":[{"count":1,"href":"https:\/\/othal.de\/cs\/wp-json\/wp\/v2\/pages\/262936\/revisions"}],"predecessor-version":[{"id":262939,"href":"https:\/\/othal.de\/cs\/wp-json\/wp\/v2\/pages\/262936\/revisions\/262939"}],"wp:attachment":[{"href":"https:\/\/othal.de\/cs\/wp-json\/wp\/v2\/media?parent=262936"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}