{"id":222,"date":"2025-05-27T22:15:05","date_gmt":"2025-05-27T22:15:05","guid":{"rendered":"https:\/\/plzprofi.de\/?page_id=222"},"modified":"2026-05-06T16:25:17","modified_gmt":"2026-05-06T16:25:17","slug":"de","status":"publish","type":"page","link":"https:\/\/plzprofi.de\/hu\/eu-laender\/de\/","title":{"rendered":"Deutschland Umkreissuche"},"content":{"rendered":"\n<link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet\/dist\/leaflet.css\" \/>\n\n<style>\n  :root { font-size: 90%; }\n\n  \/* === Grundlayout === *\/\n  body{\n    background:#050814;\n    color:#ffffff;\n    font-family:system-ui,-apple-system,BlinkMacSystemFont,\"Segoe UI\",sans-serif;\n  }\n\n  \/* =========================\n     HERO (DE)\n     ========================= *\/\n  .plzprofi-hero{\n    margin: 32px 16px 24px;\n    padding: 48px 32px 40px;\n    border-radius: 28px;\n    border: 1px solid rgba(148, 163, 184, 0.18);\n    box-shadow:\n      0 30px 80px rgba(15, 23, 42, 0.9),\n      0 0 0 1px rgba(15, 23, 42, 0.6);\n    overflow: hidden;\n    position: relative;\n    background: #050814;\n  }\n\n  \/* DE Flag as SVG background (stretched) *\/\n  .plzprofi-hero::before{\n    content:\"\";\n    position:absolute;\n    inset:-2px;\n    background-image:\n      url(\"data:image\/svg+xml,%3Csvg xmlns='http:\/\/www.w3.org\/2000\/svg' viewBox='0 0 900 600'%3E%3Crect width='900' height='200' fill='%23000000'\/%3E%3Crect y='200' width='900' height='200' fill='%23dd0000'\/%3E%3Crect y='400' width='900' height='200' fill='%23ffce00'\/%3E%3C\/svg%3E\");\n    background-size: cover;\n    background-position: center;\n    opacity: 0.70;\n    filter: saturate(1.2) contrast(1.1);\n    transform: scale(1.02);\n  }\n\n  \/* Shadow\/Glow overlay *\/\n  .plzprofi-hero::after{\n    content:\"\";\n    position:absolute;\n    inset:0;\n    background:\n      radial-gradient(circle at top left, rgba(59,130,246,0.25), transparent 55%),\n      radial-gradient(circle at top right, rgba(168,85,247,0.18), transparent 55%),\n      linear-gradient(90deg, rgba(5,8,20,0.95) 0%, rgba(5,8,20,0.7) 45%, rgba(5,8,20,0.95) 100%);\n    pointer-events:none;\n  }\n\n  .plzprofi-hero-inner{\n    position: relative;\n    z-index: 1;\n    max-width: 1280px;\n    margin: 0 auto;\n    display: flex;\n    gap: 48px;\n    align-items: center;\n  }\n\n  .plzprofi-hero-left{ flex:1 1 52%; }\n  .plzprofi-hero-right{\n    flex:1 1 48%;\n    display:flex;\n    justify-content:center;\n  }\n\n  .hero-kicker{\n    display:inline-flex;\n    align-items:center;\n    gap: 8px;\n    padding: 4px 10px;\n    border-radius: 999px;\n    font-size: 11px;\n    letter-spacing: .08em;\n    text-transform: uppercase;\n    background: rgba(15, 23, 42, 0.8);\n    border: 1px solid rgba(56, 189, 248, 0.35);\n    color: #7dd3fc;\n    margin-bottom: 14px;\n  }\n\n  .hero-kicker::before{\n    content:\"\";\n    width: 6px;\n    height: 6px;\n    border-radius: 999px;\n    background:#22c55e;\n    box-shadow: 0 0 0 0 rgba(34,197,94,0.9);\n    animation: plzprofi-pulse-dot 1.6s infinite ease-out;\n  }\n\n  @keyframes plzprofi-pulse-dot{\n    0%   { box-shadow: 0 0 0 0 rgba(34,197,94,0.9); transform: scale(1); }\n    70%  { box-shadow: 0 0 0 10px rgba(34,197,94,0); transform: scale(1.1); }\n    100% { box-shadow: 0 0 0 0 rgba(34,197,94,0); transform: scale(1); }\n  }\n\n  .plzprofi-hero-left h1{\n    font-size: clamp(32px, 4vw, 40px);\n    line-height: 1.1;\n    margin: 0 0 14px;\n  }\n\n  .plzprofi-hero-left p{\n    margin: 0 0 24px;\n    font-size: 15px;\n    max-width: 42rem;\n    color: #e5e7eb;\n    opacity: 0.95;\n  }\n\n  .hero-btn{\n    display:inline-flex;\n    align-items:center;\n    justify-content:center;\n    gap: 8px;\n    padding: 12px 26px;\n    border-radius: 999px;\n    font-size: 14px;\n    font-weight: 500;\n    color:#ffffff;\n    text-decoration:none;\n    background: linear-gradient(135deg, #2563eb, #1d4ed8);\n    box-shadow: 0 18px 40px rgba(37,99,235,0.55);\n    border:none;\n    cursor:pointer;\n    transition: transform .15s ease, box-shadow .15s ease, filter .15s ease;\n  }\n  .hero-btn:hover{\n    transform: translateY(-1px);\n    filter: brightness(1.05);\n    box-shadow: 0 22px 46px rgba(37,99,235,0.75);\n  }\n\n  .hero-card{\n    width: 100%;\n    max-width: 520px;\n    aspect-ratio: 4 \/ 3;\n    border-radius: 24px;\n    position: relative;\n    overflow: hidden;\n    background:\n      radial-gradient(circle at center, rgba(59,130,246,0.20), rgba(15,23,42,0.98)),\n      #020617;\n    box-shadow:\n      0 24px 80px rgba(15,23,42,0.9),\n      0 0 0 1px rgba(148,163,184,0.28);\n  }\n\n  .hero-card::before{\n    content:\"\";\n    position:absolute;\n    inset:0;\n    background-image: url(\"https:\/\/plzprofi.de\/wp-content\/uploads\/2025\/11\/DE-AT-Map-bearbeitet.png\");\n    background-size: cover;\n    background-position: center;\n    opacity: 0.95;\n    mix-blend-mode: screen;\n    filter: contrast(1.05) saturate(1.05);\n  }\n\n  .hero-card::after{\n    content:\"\";\n    position:absolute;\n    inset:0;\n    background: radial-gradient(circle at center, rgba(56,189,248,0.38), transparent 60%);\n    mix-blend-mode: soft-light;\n  }\n\n  .hero-tag{\n    position:absolute;\n    left: 16px;\n    top: 16px;\n    padding: 6px 11px;\n    border-radius: 999px;\n    font-size: 11px;\n    text-transform: uppercase;\n    letter-spacing: .08em;\n    background: rgba(15,23,42,0.9);\n    border: 1px solid rgba(96,165,250,0.55);\n    color: #bfdbfe;\n    backdrop-filter: blur(6px);\n    z-index: 2;\n  }\n\n  @media (max-width: 960px){\n    .plzprofi-hero-inner{ flex-direction: column; align-items: flex-start; }\n    .plzprofi-hero-right{ width:100%; }\n    .hero-card{ max-width:100%; }\n  }\n\n  \/* === Tool Layout === *\/\n  .wrapper { max-width: 1280px; margin: 0 auto; padding: 16px 16px 40px; }\n\n  .panel {\n    position: relative; text-align: center; padding: 8px; display: flex; gap: 10px;\n    align-items: center; justify-content: center; flex-wrap: wrap;\n  }\n\n  label, input, button, select { font-size: 14px; }\n  input[type=\"text\"] { width: 240px; padding: 6px 8px; border: none; border-radius: 4px; }\n  input[type=\"number\"] { width: 64px; padding: 5px 6px; border-radius: 4px; border: none; }\n  input[type=\"range\"] { vertical-align: middle; width: 140px; -webkit-appearance: none; height: 6px; background: #444; border-radius: 3px; outline: none; accent-color: #0af; }\n  input[type=\"range\"]::-webkit-slider-thumb { -webkit-appearance: none; width: 16px; height: 16px; background: #00bfff; border-radius: 50%; border: 2px solid #fff; cursor: pointer; }\n  #radiusLabel { margin: 0 5px; display: inline-block; min-width: 30px; }\n\n  button { padding: 6px 10px; background: #2a2a2a; color: #fff; border: none; cursor: pointer; border-radius: 6px; }\n  button:hover { background: #333; }\n  #btnSearch { background: #3b82f6; font-weight: bold; padding: 8px 16px;}\n  #btnSearch:hover { background: #2563eb; }\n\n  .seg { display: inline-flex; background: #2a2a2a; border-radius: 999px; overflow: hidden; }\n  .seg-btn { padding: 6px 12px; border-radius: 999px; background: transparent; border: none; color: #e5e7eb; cursor: pointer; display: inline-flex; align-items: center; gap: 8px;}\n  .seg-btn.active { background: #00bfff; color: #00121a; }\n  \n  #countryIndicator, #countryIndicator * { translate: no; cursor: default; }\n  #datasetBar{ display:none; }\n\n  \/* === NEU: Speed Gadget & Result Badges === *\/\n  .result-badges { display: flex; gap: 4px; align-items: center; }\n  .badge { padding: 3px 6px; border-radius: 999px; background: rgba(0, 191, 255, 0.12); border: 1px solid rgba(0, 191, 255, 0.35); color: #d7f6ff; font-size: 11px; white-space: nowrap; }\n  \n  \/* Stra\u00dfen-Badge im grauen Grundzustand *\/\n  .badge-road {\n    background: rgba(148, 163, 184, 0.1) !important;\n    border: 1px solid rgba(148, 163, 184, 0.2) !important;\n    color: #94a3b8 !important;\n    transition: all 0.3s ease;\n    cursor: default;\n  }\n  \n  \/* Stra\u00dfen-Badge nach Berechnung (Gr\u00fcn) *\/\n  .badge-road.active {\n    background: rgba(34, 197, 94, 0.15) !important;\n    border-color: rgba(34, 197, 94, 0.5) !important;\n    color: #4ade80 !important;\n    font-weight: bold;\n  }\n\n  \/* Zeit-Badge - ist standardm\u00e4\u00dfig versteckt und reiht sich dann ein *\/\n  .badge-time {\n    background: rgba(234, 179, 8, 0.15) !important;\n    border: 1px solid rgba(234, 179, 8, 0.5) !important;\n    color: #fde047 !important;\n    font-size: 11px;\n    font-weight: bold;\n    padding: 3px 6px;\n    border-radius: 999px;\n    white-space: nowrap;\n    display: none;\n    align-items: center;\n    gap: 3px;\n  }\n  .badge-time.active {\n    display: inline-flex;\n  }\n\n  \/* Legenden-Box *\/\n  .legend-box {\n    display: flex; justify-content: space-around; font-size: 11px; color: #94a3b8; \n    background: #1f2937; padding: 8px; border-radius: 6px; margin: 0 12px 10px 12px;\n    border: 1px solid rgba(255,255,255,0.05);\n  }\n  .legend-item { display: flex; align-items: center; gap: 6px; font-weight: 500; }\n  .legend-dot { width: 8px; height: 8px; border-radius: 50%; }\n\n  .loading-dots:after {\n    content: '...';\n    animation: plzprofi-dots 1.5s steps(5, end) infinite;\n  }\n  @keyframes plzprofi-dots {\n    0%, 20% { color: rgba(0,0,0,0); text-shadow: .25em 0 0 rgba(0,0,0,0), .5em 0 0 rgba(0,0,0,0); }\n    40% { color: #4ade80; text-shadow: .25em 0 0 rgba(0,0,0,0), .5em 0 0 rgba(0,0,0,0); }\n    60% { text-shadow: .25em 0 0 #4ade80, .5em 0 0 rgba(0,0,0,0); }\n    80%, 100% { text-shadow: .25em 0 0 #4ade80, .5em 0 0 #4ade80; }\n  }\n\n  #main { display: flex; justify-content: center; align-items: flex-start; gap: 16px; margin-top: 16px; }\n\n  \/* === Linke Spalte (Kompass & Verlauf) === *\/\n  #leftCol { width: 220px; display: flex; flex-direction: column; gap: 16px; flex-shrink: 0; }\n\n  .card { background: #111827; border-radius: 10px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.2); padding: 12px; }\n  .card h3 { margin: 0 0 10px 0; font-size: 15px; border-bottom: 1px solid rgba(255,255,255,0.1); padding-bottom: 6px; }\n\n  .compass-module {\n    display: flex; align-items: center; gap: 12px; background: #1f2937; padding: 10px;\n    border-radius: 8px; border: 1px solid rgba(148, 163, 184, 0.1); justify-content: flex-start;\n  }\n\n  .compass-canvas-wrap {\n    position: relative; width: 64px; height: 64px; border-radius: 50%;\n    background: radial-gradient(circle, #0f172a 0%, #020617 100%);\n    box-shadow: inset 0 0 10px rgba(0, 191, 255, 0.2); border: 2px solid #334155; cursor: pointer; flex-shrink: 0;\n  }\n  #compassCanvas { position: absolute; top: 0; left: 0; border-radius: 50%; width: 100%; height: 100%; }\n\n  .compass-controls { display: flex; flex-direction: column; gap: 8px; flex: 1; align-items: flex-start; }\n  \n  .compass-toggle-group {\n    display: inline-flex; background: #111827; border-radius: 999px; overflow: hidden; border: 1px solid rgba(148,163,184,0.2);\n  }\n  \n  .compass-toggle-btn {\n    display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; font-size: 11px; \n    border: none; background: transparent; color: #64748b; cursor: pointer; transition: 0.2s;\n  }\n  #btnCompOff.active { background: rgba(239, 68, 68, 0.15); color: #ef4444; font-weight: bold; }\n  #btnCompOn.active { background: rgba(34, 197, 94, 0.15); color: #22c55e; font-weight: bold; }\n\n  .compass-invert-btn {\n    display: inline-flex; align-items: center; gap: 4px; padding: 4px 8px; font-size: 11px; font-weight: 500;\n    background: #111827; color: #94a3b8; border: 1px solid rgba(148, 163, 184, 0.2); border-radius: 6px;\n    cursor: pointer; transition: all 0.2s;\n  }\n  .compass-invert-btn:hover { background: #334155; color: #fff; }\n  .compass-invert-btn.active { background: rgba(255, 68, 68, 0.15); border-color: #ff4444; color: #ff4444; }\n\n  .compass-quick-btns { display: flex; gap: 4px; margin-top: 10px; }\n  .compass-quick-btns button {\n    flex: 1; padding: 6px 0; background: #1f2937; font-size: 11px; font-weight: bold; color: #94a3b8;\n    border: 1px solid rgba(148, 163, 184, 0.2); transition: all 0.2s; border-radius: 4px;\n  }\n  .compass-quick-btns button:hover { background: #334155; color: #fff; }\n  .compass-quick-btns button.active { background: rgba(0, 191, 255, 0.15); border-color: #00bfff; color: #00bfff; }\n\n  #compassDegreeDisplay { font-size: 11px; color: #94a3b8; text-align: center; margin-top: 6px; background: #1f2937; padding: 4px; border-radius: 4px; }\n\n  #history-panel { width: 100%; box-sizing: border-box; }\n  #history-hint { margin: 0 0 8px; font-size: 11px; opacity: 0.65; }\n  #history-list { list-style: none; padding: 0; margin: 8px 0 0; }\n  #history-list li { \n    background: #1f2937; padding: 8px; border-radius: 6px; margin-bottom: 6px; \n    text-align: left; cursor: pointer; font-size: 13px;\n    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n  }\n  #history-list li:hover { background: #374151; }\n  #clear-history { width: 100%; box-sizing: border-box; background: #003366; margin-top: 10px; }\n\n  \/* === Map & Results (Mitte & Rechts) === *\/\n  #mapWrap { position: relative; width: 620px; height: 480px; border-radius: 10px; overflow: hidden; flex-shrink: 0; }\n  #map { width: 100%; height: 100%; }\n\n  #mapLoading {\n    position: absolute; inset: 0; background: rgba(0, 0, 0, 0.45);\n    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;\n    pointer-events: none; opacity: 0; transition: opacity 0.25s ease; z-index: 500;\n  }\n  #mapLoading.active { opacity: 1; pointer-events: auto; }\n  \n  #resultsCol { width: 380px; display: flex; flex-direction: column; gap: 10px; flex-shrink: 0; }\n  #results { height: 420px; overflow: auto; padding: 0; }\n  #results .header { position: sticky; top: 0; background: #020617; z-index: 1; padding: 10px 12px; border-bottom: 1px solid rgba(255, 255, 255, 0.1); }\n  .result-list { margin: 0; padding: 0; list-style: none; }\n  .result-item { display: flex; justify-content: space-between; align-items: center; padding: 12px; gap: 10px; border-bottom: 1px solid rgba(255, 255, 255, 0.06); }\n  .result-item:hover { background: #1f2937; }\n  .result-left { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px; flex: 1; }\n\n  \/* Copy Button Style *\/\n  .copy-plz {\n    cursor: pointer; color: #38bdf8; position: relative; transition: color 0.2s;\n    display: inline-flex; align-items: center;\n  }\n  .copy-plz:hover { color: #bae6fd; text-decoration: underline; }\n  .copy-plz::before {\n    content: '\ud83d\udccb'; font-size: 11px; margin-right: 5px; opacity: 0.4; transition: opacity 0.2s;\n  }\n  .copy-plz:hover::before { opacity: 1; }\n  .copy-plz.copied { color: #22c55e; text-decoration: none; }\n  .copy-plz.copied::before { content: '\u2705'; opacity: 1; }\n\n  .exp-panel { display: none; margin-top: 6px; }\n  .exp-panel.open { display: block; }\n  #controls .row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-bottom: 6px; font-size: 13px; }\n\n  #candidateBox {\n    position: absolute; left: 50%; transform: translateX(-50%); top: 56px; width: 380px; max-height: 300px;\n    overflow: auto; background: #222; border: 1px solid #333; border-radius: 8px; display: none; z-index: 9999; text-align: left;\n  }\n  #candidateBox.open { display: block; }\n  #candidateBox .cand { padding: 10px 12px; cursor: pointer; border-bottom: 1px solid rgba(255, 255, 255, 0.06); }\n  #candidateBox .cand:hover { background: #2b2b2b; }\n\n  @media (max-width: 1200px) {\n    #main { flex-direction: column; align-items: center; }\n    #leftCol, #resultsCol, #mapWrap { width: 100%; max-width: 800px; }\n    #leftCol { flex-direction: row; flex-wrap: wrap; }\n    .card { flex: 1; min-width: 300px; }\n  }\n\n  \/* =========================\n     INFO & FAQ FOOTER (DE)\n     ========================= *\/\n  .plzprofi-undersearch {\n    margin-top: 48px; padding: 40px 16px 40px; border-top: 1px solid rgba(148, 163, 184, 0.1);\n  }\n  .plzprofi-undersearch-head { margin: 0 0 32px 0; text-align: center; }\n  .plzprofi-undersearch-head h2 { margin: 0 0 8px 0; font-size: 28px; }\n  .plzprofi-undersearch-sub { margin: 0; color: #94a3b8; font-size: 15px; max-width: 600px; margin: 0 auto; line-height: 1.5; }\n\n  .plzprofi-undersearch-grid {\n    display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px; margin-bottom: 48px; max-width: 1280px; margin-left: auto; margin-right: auto;\n  }\n  .plzprofi-undersearch-card {\n    border-radius: 16px; padding: 24px; border: 1px solid rgba(148, 163, 184, 0.15);\n    background: radial-gradient(circle at top left, rgba(59, 130, 246, 0.08), transparent 60%), #0b1220;\n    box-shadow: 0 10px 30px rgba(0,0,0,0.2); transition: transform 0.2s ease;\n  }\n  .plzprofi-undersearch-card:hover { transform: translateY(-3px); border-color: rgba(56, 189, 248, 0.3); }\n  .plzprofi-chip {\n    display: inline-flex; align-items: center; padding: 4px 10px; border-radius: 999px;\n    font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; background: rgba(56, 189, 248, 0.15);\n    color: #38bdf8; margin-bottom: 12px; font-weight: bold;\n  }\n  .plzprofi-undersearch-card h3 { margin: 0 0 12px 0; font-size: 18px; color: #f8fafc; }\n  .plzprofi-undersearch-card p { margin: 0; color: #cbd5e1; font-size: 14px; line-height: 1.6; }\n\n  \/* == EU L\u00c4NDER SEKTION == *\/\n  .plzprofi-eu-network {\n    background: #0b1220; border-radius: 20px; border: 1px solid rgba(148, 163, 184, 0.15);\n    padding: 32px; max-width: 1280px; margin: 0 auto 48px; text-align: center;\n  }\n  .plzprofi-eu-network h3 { margin: 0 0 12px 0; font-size: 22px; color: #ffffff; }\n  .plzprofi-eu-network p { color: #94a3b8; font-size: 15px; margin: 0 auto 24px; max-width: 600px; line-height: 1.5; }\n  \n  .eu-links-grid { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; }\n  .eu-country-btn {\n    display: inline-flex; align-items: center; gap: 8px; padding: 10px 20px;\n    background: #1f2937; border: 1px solid rgba(148, 163, 184, 0.2); border-radius: 12px;\n    color: #e2e8f0; text-decoration: none; font-weight: 500; font-size: 14px; transition: all 0.2s ease;\n  }\n  .eu-country-btn:hover { background: #334155; border-color: #38bdf8; color: #fff; transform: translateY(-2px); }\n  .eu-country-btn.highlight { background: rgba(37, 99, 235, 0.2); border-color: rgba(37, 99, 235, 0.5); color: #60a5fa; }\n  .eu-country-btn.highlight:hover { background: rgba(37, 99, 235, 0.4); color: #fff; }\n\n  \/* == FAQ Sektion == *\/\n  .plzprofi-faq-section { max-width: 1280px; margin: 0 auto; background: transparent; }\n  .plzprofi-faq-section h3 { margin: 0 0 24px 0; font-size: 22px; text-align: center; }\n  .plzprofi-faq-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(400px, 1fr)); gap: 16px; margin-bottom: 32px; }\n  \n  .plzprofi-faq-item {\n    border-radius: 12px; border: 1px solid rgba(148, 163, 184, 0.15); background: #0b1220; padding: 20px; transition: background 0.2s;\n  }\n  .plzprofi-faq-item:hover { background: #111827; }\n  .plzprofi-faq-item summary { cursor: pointer; font-weight: 600; list-style: none; color: #e2e8f0; font-size: 15px; position: relative; padding-right: 20px; }\n  .plzprofi-faq-item summary::-webkit-details-marker { display: none; }\n  .plzprofi-faq-item summary::after { content: \"\u25bc\"; position: absolute; right: 0; top: 0; font-size: 12px; color: #64748b; }\n  .plzprofi-faq-item[open] summary::after { content: \"\u25b2\"; color: #38bdf8; }\n  .plzprofi-faq-body { margin-top: 12px; color: #94a3b8; font-size: 14px; line-height: 1.6; border-top: 1px solid rgba(255,255,255,0.05); padding-top: 12px; }\n  .faq-cta-wrap { text-align: center; }\n<\/style>\n\n<div class=\"plzprofi-hero\">\n  <div class=\"plzprofi-hero-inner\">\n    <div class=\"plzprofi-hero-left\">\n      <span class=\"hero-kicker\">PLZ Profi \u00b7 Umkreissuche<\/span>\n      <h1>Deutschland (DE) \u00b7 Umkreissuche f\u00fcr Postleitzahlen &#038; Orte<\/h1>\n      <p>Suchen Sie in Deutschland nach Orten und Postleitzahlen im gew\u00fcnschten Radius. Unser System verarbeitet 5-stellige Eingaben (z.B. <strong>04109<\/strong>) oder Ortsnamen sekundenschnell.<\/p>\n      <a class=\"hero-btn\" href=\"#plzprofi-tool\">Jetzt Suche starten<\/a>\n    <\/div>\n\n    <div class=\"plzprofi-hero-right\">\n      <div class=\"hero-card\" aria-hidden=\"true\">\n        <div class=\"hero-tag\">Kartensuche \u00b7 DE<\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script async src=\"https:\/\/pagead2.googlesyndication.com\/pagead\/js\/adsbygoogle.js?client=ca-pub-1199789787588897\"\n     crossorigin=\"anonymous\"><\/script>\n<!-- Header Banner DE -->\n<ins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-1199789787588897\"\n     data-ad-slot=\"1299157882\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"><\/ins>\n<script>\n     (adsbygoogle = window.adsbygoogle || []).push({});\n<\/script>\n\n<div id=\"plzprofi-tool\" class=\"wrapper\">\n  <div class=\"panel\">\n    <div class=\"seg notranslate\" id=\"countryIndicator\" translate=\"no\">\n      <button class=\"seg-btn active notranslate\" translate=\"no\" type=\"button\" aria-label=\"Aktives Land: DE\">\n        <span class=\"flag\" aria-hidden=\"true\">\ud83c\udde9\ud83c\uddea<\/span> DE\n      <\/button>\n    <\/div>\n\n    <div id=\"datasetBar\">\n      <label for=\"datasetUrl\">GeoJSON URL:<\/label>\n      <input type=\"text\" id=\"datasetUrl\" value=\"https:\/\/plzprofi.de\/wp-content\/uploads\/geo\/plz-de-short.geojson\" \/>\n      <button id=\"btnReload\" type=\"button\">Neu laden<\/button>\n    <\/div>\n\n    <label for=\"searchInput\">PLZ oder Ort:<\/label>\n    <input type=\"text\" id=\"searchInput\" placeholder=\"z.B. 20095 oder Hamburg\" \/>\n    <button id=\"btnCandidates\" title=\"Ortsauswahl \u00f6ffnen\" type=\"button\">Ortsauswahl \u25bc<\/button>\n\n    <label for=\"radiusInput\">Radius (1\u2013100 km):<\/label>\n    <input type=\"number\" id=\"radiusNumber\" min=\"1\" max=\"100\" value=\"30\" \/>\n    <span id=\"radiusLabel\">30<\/span> km\n    <input type=\"range\" id=\"radiusInput\" min=\"1\" max=\"100\" value=\"30\" \/>\n    <button id=\"btnSearch\" type=\"button\">Suchen<\/button>\n\n    <div id=\"candidateBox\" aria-label=\"Ortsauswahl\"><\/div>\n  <\/div>\n\n  <div id=\"main\">\n    \n    <div id=\"leftCol\">\n      <div class=\"card\" id=\"compassCard\">\n        <h3>Kompass<\/h3>\n        <div class=\"compass-module\">\n          <div class=\"compass-canvas-wrap\" id=\"compassWrap\" title=\"Klicken &#038; Ziehen zum Rotieren\">\n            <canvas id=\"compassCanvas\" width=\"64\" height=\"64\"><\/canvas>\n          <\/div>\n          <div class=\"compass-controls\">\n            <div class=\"compass-toggle-group\">\n              <button type=\"button\" class=\"compass-toggle-btn active\" id=\"btnCompOff\">\n                <svg viewBox=\"0 0 24 24\" width=\"10\" height=\"10\" stroke=\"currentColor\" stroke-width=\"2.5\" fill=\"none\"><path d=\"M18.36 6.64a9 9 0 1 1-12.73 0\"><\/path><line x1=\"12\" y1=\"2\" x2=\"12\" y2=\"12\"><\/line><\/svg>\n                Aus\n              <\/button>\n              <button type=\"button\" class=\"compass-toggle-btn\" id=\"btnCompOn\">\n                <svg viewBox=\"0 0 24 24\" width=\"10\" height=\"10\" stroke=\"currentColor\" stroke-width=\"2.5\" fill=\"none\"><path d=\"M18.36 6.64a9 9 0 1 1-12.73 0\"><\/path><line x1=\"12\" y1=\"2\" x2=\"12\" y2=\"12\"><\/line><\/svg>\n                Ein\n              <\/button>\n            <\/div>\n            <button type=\"button\" class=\"compass-invert-btn\" id=\"btnCompInvert\">\n              <span>\ud83d\udd04<\/span> Umkehren\n            <\/button>\n          <\/div>\n        <\/div>\n        <div id=\"compassDegreeDisplay\">Winkel: 0\u00b0 (Norden)<\/div>\n        <div class=\"compass-quick-btns\">\n          <button type=\"button\" data-dir=\"0\">N<\/button>\n          <button type=\"button\" data-dir=\"45\">NO<\/button>\n          <button type=\"button\" data-dir=\"90\">O<\/button>\n          <button type=\"button\" data-dir=\"135\">SO<\/button>\n        <\/div>\n        <div class=\"compass-quick-btns\" style=\"margin-top:4px;\">\n          <button type=\"button\" data-dir=\"180\">S<\/button>\n          <button type=\"button\" data-dir=\"225\">SW<\/button>\n          <button type=\"button\" data-dir=\"270\">W<\/button>\n          <button type=\"button\" data-dir=\"315\">NW<\/button>\n        <\/div>\n      <\/div>\n\n      <div id=\"history-panel\" class=\"card\">\n        <h3>Suchverlauf<\/h3>\n        <div id=\"history-hint\">Wird lokal im Browser gespeichert<\/div>\n        <ul id=\"history-list\"><\/ul>\n        <button id=\"clear-history\">Verlauf l\u00f6schen<\/button>\n      <\/div>\n    <\/div>\n\n    <div id=\"mapWrap\">\n      <div id=\"map\"><\/div>\n      <div id=\"mapLoading\">\n        <span style=\"color:white; font-size:14px; background: rgba(0,0,0,0.8); padding: 8px 16px; border-radius: 8px;\">Kartendaten laden&#8230;<\/span>\n      <\/div>\n    <\/div>\n\n    <div id=\"resultsCol\">\n      <div id=\"controls\" class=\"card\">\n        <div class=\"row\">\n          <label for=\"sortSelect\">Sortierung:<\/label>\n          <select id=\"sortSelect\" style=\"flex:1;\">\n            <option value=\"dist-asc\" selected>Distanz \u2191<\/option>\n            <option value=\"plz-asc\">PLZ \u2191<\/option>\n            <option value=\"name-asc\">Ort A\u2013Z<\/option>\n          <\/select>\n        <\/div>\n        <div class=\"row\">\n          <label for=\"limitSelect\">Umfang:<\/label>\n          <select id=\"limitSelect\" style=\"flex:1;\">\n            <option value=\"top10\" selected>Top 10<\/option>\n            <option value=\"top20\">Top 20<\/option>\n            <option value=\"all\">Alle<\/option>\n          <\/select>\n        <\/div>\n        <div class=\"row exp-wrap\" style=\"margin-top: 10px;\">\n          <button class=\"exp-toggle\" id=\"expToggle\" style=\"width:100%\">Export \u25bc<\/button>\n          <div class=\"exp-panel\" id=\"expPanel\">\n            <div class=\"row\" style=\"margin-top:4px; justify-content: center;\">\n              <button id=\"btnExportCsv\">CSV<\/button>\n              <button id=\"btnExportXlsx\">XLSX<\/button>\n              <button id=\"btnExportPdf\">PDF<\/button>\n              <button id=\"btnExportTxt\">TXT<\/button>\n            <\/div>\n            <div style=\"font-size:11px; opacity:0.6; margin-top:6px; text-align:center;\">\u2022 Exportiert die sichtbare Liste<\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"routing-section\" style=\"margin-top: 16px; padding-top: 16px; border-top: 1px solid rgba(255,255,255,0.08);\">\n          <div class=\"row\" style=\"justify-content: space-between; margin-bottom: 8px;\">\n            <label for=\"speedSlider\" style=\"font-size: 13px; color: #94a3b8; font-weight: bold;\">\u00d8 Fahrgeschwindigkeit:<\/label>\n            <div style=\"font-size: 14px; font-weight: bold; color: #38bdf8;\"><span id=\"speedLabel\">60<\/span> km\/h<\/div>\n          <\/div>\n          <input type=\"range\" id=\"speedSlider\" min=\"30\" max=\"130\" value=\"60\" step=\"1\" style=\"width: 100%; margin-bottom: 12px; accent-color: #10b981;\">\n          <button id=\"btnCalcRoute\" style=\"width: 100%; background: #10b981; color: #022c22; border-radius: 6px; padding: 10px; font-weight: bold; font-size: 14px; transition: background 0.2s;\">\n            \ud83d\ude97 Fahrstrecke berechnen\n          <\/button>\n        <\/div>\n      <\/div>\n\n      <div id=\"results\" class=\"card\">\n        <div class=\"header\" style=\"padding-bottom: 4px;\">\n          <div id=\"resTitle\"><strong>Ergebnisse<\/strong><\/div>\n          <div id=\"resMeta\" style=\"font-size: 12px; opacity: 0.8;\">Bitte Suche ausf\u00fchren.<\/div>\n        <\/div>\n        \n        <div class=\"legend-box\">\n          <span class=\"legend-item\"><div class=\"legend-dot\" style=\"background:#fde047;\"><\/div> Zeit<\/span>\n          <span class=\"legend-item\"><div class=\"legend-dot\" style=\"background:#4ade80;\"><\/div> Strecke<\/span>\n          <span class=\"legend-item\"><div class=\"legend-dot\" style=\"background:#38bdf8;\"><\/div> Luft<\/span>\n        <\/div>\n\n        <ul class=\"result-list\" id=\"resList\"><\/ul>\n      <\/div>\n    <\/div>\n  <\/div>\n\n<script async src=\"https:\/\/pagead2.googlesyndication.com\/pagead\/js\/adsbygoogle.js?client=ca-pub-1199789787588897\"\n     crossorigin=\"anonymous\"><\/script>\n<!-- Footer Banner DE -->\n<ins class=\"adsbygoogle\"\n     style=\"display:block\"\n     data-ad-client=\"ca-pub-1199789787588897\"\n     data-ad-slot=\"8068580361\"\n     data-ad-format=\"auto\"\n     data-full-width-responsive=\"true\"><\/ins>\n<script>\n     (adsbygoogle = window.adsbygoogle || []).push({});\n<\/script>\n\n  <section class=\"plzprofi-undersearch\">\n    <header class=\"plzprofi-undersearch-head\">\n      <h2>Kurzinfos f\u00fcr die Deutschland-Disposition<\/h2>\n      <p class=\"plzprofi-undersearch-sub\">Schnelle Gebietsplanung im gr\u00f6\u00dften Logistiknetzwerk Europas.<\/p>\n    <\/header>\n\n    <div class=\"plzprofi-undersearch-grid\">\n      <article class=\"plzprofi-undersearch-card\">\n        <span class=\"plzprofi-chip\">Logistik-Hubs<\/span>\n        <h3>Wirtschaftszentren<\/h3>\n        <p>Deutschland hat extrem dichte Wirtschaftsregionen. Mit der PLZ-Radius-Suche grenzt du Hotspots wie das <strong>Ruhrgebiet, Rhein-Main, Hamburg, M\u00fcnchen oder Berlin<\/strong> pr\u00e4zise ein. Nutze den Kompass, um Ballungszentren zielgerichtet in eine Richtung abzufahren.<\/p>\n      <\/article>\n\n      <article class=\"plzprofi-undersearch-card\">\n        <span class=\"plzprofi-chip\">Workflow<\/span>\n        <h3>1-Klick PLZ Kopie<\/h3>\n        <p>Spare dir das l\u00e4stige Markieren! Klicke in der Ergebnisliste einfach auf die gew\u00fcnschte Postleitzahl. Die 5-stellige Nummer (inkl. f\u00fchrender Null im Osten, z.B. 04109) liegt sofort fertig formatiert in deiner Zwischenablage f\u00fcr Timocom &#038; Co.<\/p>\n      <\/article>\n\n      <article class=\"plzprofi-undersearch-card\">\n        <span class=\"plzprofi-chip\">Organisation<\/span>\n        <h3>Daten-Export &#038; Vorplanung<\/h3>\n        <p>Lade dir dein spezifisches Einzugsgebiet mit einem Klick als <strong>Excel (XLSX), CSV oder PDF<\/strong> herunter, um es an Frachtf\u00fchrer weiterzuleiten oder direkt in dein hauseigenes Transport Management System (TMS) zu importieren.<\/p>\n      <\/article>\n    <\/div>\n\n    <div class=\"plzprofi-eu-network\">\n      <h3>PLZ-Suche f\u00fcr ganz Europa<\/h3>\n      <p>Internationale Touren erfordern verl\u00e4ssliche Daten \u00fcber die Grenzen hinaus. Greife direkt auf unsere anderen L\u00e4ndersuchen zu.<\/p>\n      <div class=\"eu-links-grid\">\n        <a href=\"https:\/\/plzprofi.de\/eu-laender\/at\/\" class=\"eu-country-btn\"><span class=\"flag\">\ud83c\udde6\ud83c\uddf9<\/span> \u00d6sterreich<\/a>\n        <a href=\"https:\/\/plzprofi.de\/eu-laender\/cz\/\" class=\"eu-country-btn\"><span class=\"flag\">\ud83c\udde8\ud83c\uddff<\/span> Tschechien<\/a>\n        <a href=\"https:\/\/plzprofi.de\/eu-laender\/es\/\" class=\"eu-country-btn\"><span class=\"flag\">\ud83c\uddea\ud83c\uddf8<\/span> Spanien<\/a>\n        <a href=\"\/eu-laender\/\" class=\"eu-country-btn highlight\">\ud83c\udf0d Alle EU-L\u00e4nder ansehen<\/a>\n      <\/div>\n    <\/div>\n\n    <div class=\"plzprofi-faq-section\">\n      <h3>H\u00e4ufig gestellte Fragen (Deutschland)<\/h3>\n      <div class=\"plzprofi-faq-grid\">\n        <details class=\"plzprofi-faq-item\">\n          <summary>Werden Postleitzahlen mit f\u00fchrender Null (z.B. Dresden) korrekt erkannt?<\/summary>\n          <div class=\"plzprofi-faq-body\">Ja! Unser System behandelt deutsche Postleitzahlen als Text-Strings (nicht als reine Nummern). Somit gehen PLZs im Osten (z.B. 01067 Dresden oder 04109 Leipzig) niemals &#8222;kaputt&#8220; und werden beim Kopieren und im Excel-Export samt der Null \u00fcbernommen.<\/div>\n        <\/details>\n\n        <details class=\"plzprofi-faq-item\">\n          <summary>Warum verschwinden die Marker auf der Karte, wenn ich &#8222;Alle&#8220; anw\u00e4hle?<\/summary>\n          <div class=\"plzprofi-faq-body\">Das ist eine Performance- und Schutzfunktion. Im Ruhrgebiet kann ein 100km-Radius extrem viele Treffer liefern. Wenn wir diese alle zeichnen, wird die Karte unleserlich. Daher zeigt die Karte bei &#8222;Alle&#8220; nur den Start-Pin, w\u00e4hrend dir die rechte Liste alles haargenau auflistet.<\/div>\n        <\/details>\n\n        <details class=\"plzprofi-faq-item\">\n          <summary>Wie nutze ich die &#8222;Umkehren&#8220;-Funktion am Kompass?<\/summary>\n          <div class=\"plzprofi-faq-body\">Der rote &#8222;Umkehren&#8220;-Modus ist ideal f\u00fcr Ausschlussverfahren. Du planst eine Tour ab Frankfurt, willst aber nicht ins Gebirge Richtung S\u00fcden? Richte den Kompass nach S\u00fcden aus und klicke &#8222;Umkehren&#8220;. Du siehst nun alle Treffer \u2013 <em>au\u00dfer<\/em> jenen im definierten S\u00fcd-Bereich.<\/div>\n        <\/details>\n      <\/div>\n\n      <div class=\"faq-cta-wrap\" style=\"margin-top: 32px;\">\n        <a href=\"\/faq\/\" class=\"hero-btn\" style=\"background: transparent; border: 1px solid rgba(56, 189, 248, 0.4); box-shadow: none; color: #38bdf8;\">\n          <span>Zur vollst\u00e4ndigen FAQ<\/span>\n        <\/a>\n      <\/div>\n    <\/div>\n  <\/section>\n<\/div>\n\n<script src=\"https:\/\/unpkg.com\/leaflet\/dist\/leaflet.js\"><\/script>\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/xlsx@0.18.5\/dist\/xlsx.full.min.js\"><\/script>\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/jspdf@2.5.1\/dist\/jspdf.umd.min.js\"><\/script>\n\n<script>\n  \/\/ === GLOBALE KOPIER FUNKTION ===\n  window.copyPLZ = function(plz, element) {\n    navigator.clipboard.writeText(plz).then(() => {\n      element.classList.add('copied');\n      setTimeout(() => element.classList.remove('copied'), 1500);\n    }).catch(err => {\n      const ta = document.createElement('textarea'); ta.value = plz;\n      document.body.appendChild(ta); ta.select(); document.execCommand('copy'); document.body.removeChild(ta);\n      element.classList.add('copied'); setTimeout(() => element.classList.remove('copied'), 1500);\n    });\n  };\n\n  \/\/ ==== SETUP ====\n  const DEFAULT_VIEW = { lat: 51.1657, lon: 10.4515, zoom: 6 };\n  const datasetUrlEl = document.getElementById(\"datasetUrl\");\n\n  let map = L.map(\"map\").setView([DEFAULT_VIEW.lat, DEFAULT_VIEW.lon], DEFAULT_VIEW.zoom);\n  L.tileLayer(\"https:\/\/{s}.tile.openstreetmap.org\/{z}\/{x}\/{y}.png\", { attribution: \"\u00a9 OpenStreetMap\" }).addTo(map);\n\n  let markerLayerGroup = L.layerGroup().addTo(map);\n  let circleLayer;\n\n  const resultIcon = new L.Icon({ iconUrl: \"https:\/\/unpkg.com\/leaflet@1.7.1\/dist\/images\/marker-icon.png\", iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowUrl: \"https:\/\/unpkg.com\/leaflet@1.7.1\/dist\/images\/marker-shadow.png\", shadowSize: [41, 41] });\n  const searchIcon = new L.Icon({ iconUrl: \"https:\/\/unpkg.com\/leaflet@1.7.1\/dist\/images\/marker-icon.png\", iconSize: [33, 54], iconAnchor: [16, 54], popupAnchor: [1, -40], shadowUrl: \"https:\/\/unpkg.com\/leaflet@1.7.1\/dist\/images\/marker-shadow.png\", shadowSize: [54, 54] });\n\n  let geoData = [], geoLoaded = false, allMatches = [], viewResults = [], lastCenter = null, searchActive = false;\n\n  \/\/ === KOMPASS LOGIK ===\n  const compassCanvas = document.getElementById(\"compassCanvas\"), ctx = compassCanvas.getContext(\"2d\");\n  const compassDegreeDisplay = document.getElementById(\"compassDegreeDisplay\");\n  const quickBtns = document.querySelectorAll(\".compass-quick-btns button\");\n  const btnCompOff = document.getElementById(\"btnCompOff\"), btnCompOn = document.getElementById(\"btnCompOn\"), btnCompInvert = document.getElementById(\"btnCompInvert\");\n\n  let isCompassActive = false, isCompassInverted = false, compassAngle = 0, isDraggingCompass = false, compassConeWidth = 90;\n\n  function setCompassActive(state) {\n    isCompassActive = state;\n    if(state) { btnCompOn.classList.add('active'); btnCompOff.classList.remove('active'); } \n    else { btnCompOff.classList.add('active'); btnCompOn.classList.remove('active'); }\n    drawCompass(); updateQuickBtnUI(); if(searchActive) applySortAndRender();\n  }\n\n  btnCompOn.addEventListener('click', () => setCompassActive(true));\n  btnCompOff.addEventListener('click', () => setCompassActive(false));\n  btnCompInvert.addEventListener('click', () => {\n    isCompassInverted = !isCompassInverted; btnCompInvert.classList.toggle('active', isCompassInverted);\n    drawCompass(); if(searchActive) applySortAndRender();\n  });\n\n  function getDirectionName(deg) {\n    if (deg >= 337.5 || deg < 22.5) return \"Norden\"; if (deg >= 22.5 && deg < 67.5) return \"Nord-Ost\";\n    if (deg >= 67.5 && deg < 112.5) return \"Osten\"; if (deg >= 112.5 && deg < 157.5) return \"S\u00fcd-Ost\";\n    if (deg >= 157.5 && deg < 202.5) return \"S\u00fcden\"; if (deg >= 202.5 && deg < 247.5) return \"S\u00fcd-West\";\n    if (deg >= 247.5 && deg < 292.5) return \"Westen\"; if (deg >= 292.5 && deg < 337.5) return \"Nord-West\"; return \"\";\n  }\n\n  function drawCompass() {\n    const w = compassCanvas.width, h = compassCanvas.height, cx = w\/2, cy = h\/2, r = w\/2 - 2;\n    ctx.clearRect(0, 0, w, h); ctx.strokeStyle = \"rgba(148, 163, 184, 0.2)\"; ctx.lineWidth = 1; ctx.beginPath();\n    ctx.moveTo(cx, 0); ctx.lineTo(cx, h); ctx.moveTo(0, cy); ctx.lineTo(w, cy); ctx.stroke();\n    ctx.fillStyle = \"rgba(148, 163, 184, 0.7)\"; ctx.font = \"10px sans-serif\"; ctx.textAlign = \"center\"; ctx.textBaseline = \"middle\";\n    ctx.fillText(\"N\", cx, 8); ctx.fillText(\"S\", cx, h - 8); ctx.fillText(\"W\", 8, cy); ctx.fillText(\"O\", w - 8, cy);\n\n    if (isCompassActive) {\n      const startAngle = (compassAngle - compassConeWidth \/ 2 - 90) * Math.PI \/ 180;\n      const endAngle = (compassAngle + compassConeWidth \/ 2 - 90) * Math.PI \/ 180;\n      ctx.beginPath(); ctx.moveTo(cx, cy); ctx.arc(cx, cy, r, startAngle, endAngle); ctx.closePath();\n      const grad = ctx.createRadialGradient(cx, cy, 0, cx, cy, r);\n      grad.addColorStop(0, \"rgba(0, 191, 255, 0.1)\"); grad.addColorStop(1, \"rgba(0, 191, 255, 0.6)\");\n      ctx.fillStyle = isCompassInverted ? \"rgba(255, 68, 68, 0.4)\" : grad; ctx.fill();\n      ctx.strokeStyle = isCompassInverted ? \"#ff4444\" : \"#00bfff\"; ctx.lineWidth = 2; ctx.stroke();\n    }\n    compassDegreeDisplay.textContent = isCompassActive ? `Winkel: ${Math.round(compassAngle)}\u00b0 (${getDirectionName(compassAngle)})` : \"Kompass deaktiviert\";\n  }\n\n  function updateQuickBtnUI() { quickBtns.forEach(b => b.classList.toggle('active', isCompassActive && Math.abs(compassAngle - parseInt(b.dataset.dir)) < 2)); }\n  function handleCompassInteraction(e) {\n    if (!isDraggingCompass) return;\n    const rect = compassCanvas.getBoundingClientRect(), clientX = e.touches ? e.touches[0].clientX : e.clientX, clientY = e.touches ? e.touches[0].clientY : e.clientY;\n    compassAngle = (Math.atan2(clientX - rect.left - rect.width \/ 2, -(clientY - rect.top - rect.height \/ 2)) * 180 \/ Math.PI + 360) % 360;\n    if(!isCompassActive) setCompassActive(true); else { drawCompass(); updateQuickBtnUI(); if (searchActive) applySortAndRender(); }\n  }\n\n  compassCanvas.addEventListener('mousedown', (e) => { isDraggingCompass = true; handleCompassInteraction(e); });\n  window.addEventListener('mouseup', () => { isDraggingCompass = false; });\n  compassCanvas.addEventListener('mousemove', handleCompassInteraction);\n  quickBtns.forEach(btn => { btn.addEventListener('click', (e) => { compassAngle = parseInt(e.target.dataset.dir); setCompassActive(true); }); });\n  drawCompass();\n\n  \/\/ === DE SPEZIFISCHE FUNKTIONEN ===\n  function normalizeText(str) { return (str || \"\").toString().normalize(\"NFD\").replace(\/\\p{Diacritic}\/gu, \"\").toLowerCase().trim(); }\n  function normalizePostcodeDE(input) {\n    const s = String(input || \"\").trim().replace(\/[\\s-]+\/g, \"\");\n    return \/^\\d{4,5}$\/.test(s) ? s.padStart(5, '0') : null;\n  }\n  function formatPostcodeDE(plz) {\n    const s = String(plz || \"\").trim().replace(\/[\\s-]+\/g, \"\");\n    return \/^\\d{4,5}$\/.test(s) ? s.padStart(5, '0') : String(plz || \"\");\n  }\n  function safeOrt(f) {\n    const raw = (f.properties.ort || f.properties.note || f.properties.name || \"\").toString().trim(), plz = (f.properties.plz || \"\").toString();\n    return raw.replace(new RegExp(\"^\" + plz + \"\\\\s*\"), \"\").trim();\n  }\n\n  \/\/ === GEOMETRIE ===\n  function getCenter(coords, type) {\n    if (type === \"Point\") return [coords[1], coords[0]];\n    if (type === \"MultiPoint\") { let la=0, lo=0; coords.forEach(c => {lo+=c[0]; la+=c[1];}); return [la\/coords.length, lo\/coords.length]; }\n    let arr = (type === \"Polygon\") ? coords[0] || [] : (type === \"MultiPolygon\" ? (coords || []).flat(1) : (coords || []).flat(2));\n    let la = 0, lo = 0; arr.forEach(c => { lo += c[0]; la += c[1]; });\n    return [la \/ (arr.length || 1), lo \/ (arr.length || 1)];\n  }\n  function haversine(lat1, lon1, lat2, lon2) {\n    const R = 6371, toRad = x => x * Math.PI \/ 180, dLat = toRad(lat2 - lat1), dLon = toRad(lon2 - lon1);\n    const a = Math.sin(dLat \/ 2) ** 2 + Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLon \/ 2) ** 2;\n    return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n  }\n  function getBearing(lat1, lon1, lat2, lon2) {\n    const toRad = x => x * Math.PI \/ 180, toDeg = x => x * 180 \/ Math.PI;\n    const dLon = toRad(lon2 - lon1), y = Math.sin(dLon) * Math.cos(toRad(lat2));\n    const x = Math.cos(toRad(lat1)) * Math.sin(toRad(lat2)) - Math.sin(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.cos(dLon);\n    return (toDeg(Math.atan2(y, x)) + 360) % 360;\n  }\n\n  \/\/ === LADE LOGIK ===\n  async function loadDataset(url) {\n    geoLoaded = false; geoData = []; searchActive = false; lastCenter = null; allMatches = []; viewResults = [];\n    markerLayerGroup.clearLayers(); if (circleLayer) map.removeLayer(circleLayer);\n    document.getElementById(\"resTitle\").innerHTML = \"<strong>Ergebnisse<\/strong>\";\n    document.getElementById(\"resMeta\").textContent = \"Daten werden geladen\u2026\"; document.getElementById(\"resList\").innerHTML = \"\";\n    document.getElementById(\"mapLoading\").classList.add(\"active\");\n\n    try {\n      const r = await fetch(url, { cache: \"no-store\" });\n      if (!r.ok) throw new Error(\"HTTP \" + r.status);\n      const data = await r.json();\n      let feats;\n      if (Array.isArray(data)) feats = data; else if (Array.isArray(data.features)) feats = data.features;\n      else if (data.type === \"FeatureCollection\" && Array.isArray(data.features)) feats = data.features;\n      geoData = feats || []; geoLoaded = true; document.getElementById(\"resMeta\").textContent = \"Bitte Suche ausf\u00fchren.\";\n    } catch (err) {\n      console.error(\"GeoJSON-Fehler\", err); document.getElementById(\"resMeta\").textContent = \"Fehler beim Laden.\";\n    } finally { document.getElementById(\"mapLoading\").classList.remove(\"active\"); }\n  }\n  document.getElementById(\"btnReload\").addEventListener(\"click\", () => loadDataset(datasetUrlEl.value.trim()));\n\n  \/\/ === SUCHE ===\n  const candBox = document.getElementById(\"candidateBox\"), btnCandidates = document.getElementById(\"btnCandidates\");\n  let candidatesOpen = false;\n  function toggleCandidates(open) {\n    candidatesOpen = (open !== undefined) ? open : !candidatesOpen;\n    candBox.classList.toggle(\"open\", candidatesOpen); btnCandidates.textContent = candidatesOpen ? \"Ortsauswahl \u25b2\" : \"Ortsauswahl \u25bc\";\n  }\n  document.getElementById(\"searchInput\").addEventListener(\"keydown\", e => { if (e.key === \"Enter\") document.getElementById(\"btnSearch\").click(); });\n  btnCandidates.addEventListener(\"click\", () => { renderCandidates(candidatesFor(document.getElementById(\"searchInput\").value.trim())); toggleCandidates(); });\n\n  function candidatesFor(inputRaw) {\n    const plzNorm = normalizePostcodeDE(inputRaw), qText = normalizeText(inputRaw);\n    return (geoData || []).filter(f => {\n      const normOrt = normalizeText(safeOrt(f));\n      if (plzNorm) return String(f.properties.plz || \"\").padStart(5, '0') === plzNorm;\n      return normOrt === qText || normOrt.split(\/[\\s,]+\/).includes(qText);\n    }).sort((a, b) => String(a.properties.plz || \"\").localeCompare(String(b.properties.plz || \"\")));\n  }\n\n  function renderCandidates(list) {\n    if (!list.length) { candBox.classList.remove(\"open\"); btnCandidates.textContent = \"Ortsauswahl \u25bc\"; return; }\n    candBox.innerHTML = list.slice(0, 80).map((c, i) => `<div class=\"cand\" data-i=\"${i}\"><strong>${formatPostcodeDE(c.properties.plz)}<\/strong> ${safeOrt(c)}<\/div>`).join(\"\");\n    candBox.querySelectorAll(\".cand\").forEach(el => el.addEventListener(\"click\", () => { toggleCandidates(false); runSearch(list[parseInt(el.getAttribute(\"data-i\"), 10)], getRadius(), document.getElementById(\"searchInput\").value.trim()); }));\n  }\n\n  function bestCandidate(cands, inputRaw) {\n    const plzNorm = normalizePostcodeDE(inputRaw);\n    if (plzNorm) { const hit = cands.find(f => String(f.properties.plz || \"\").padStart(5, '0') === plzNorm); if (hit) return hit; }\n    const inorm = normalizeText(inputRaw), exactOrt = cands.find(f => normalizeText(safeOrt(f)) === inorm); if (exactOrt) return exactOrt;\n    const tokenHit = cands.find(f => normalizeText(safeOrt(f)).split(\/[\\s,]+\/).includes(inorm)); return tokenHit || cands[0];\n  }\n\n  function getRadius() { return Math.min(100, Math.max(1, Number(document.getElementById(\"radiusInput\").value) || 30)); }\n\n  document.getElementById(\"btnSearch\").addEventListener(\"click\", () => {\n    toggleCandidates(false); const inputRaw = document.getElementById(\"searchInput\").value.trim(), radius = getRadius();\n    if (!inputRaw || isNaN(radius)) return alert(\"Bitte g\u00fcltige PLZ oder Ortsnamen eingeben.\");\n    if (!geoLoaded) return alert(\"Kartendaten laden noch...\");\n    const cands = candidatesFor(inputRaw); if (!cands.length) return alert(\"Ort nicht gefunden.\");\n    runSearch(bestCandidate(cands, inputRaw), radius, inputRaw);\n  });\n\n  function runSearch(refFeature, radius, inputRaw) {\nif (typeof gtag === 'function') gtag('event', 'search_executed', { country: 'DE', radius: radius, search_term: inputRaw });\n    const [centerLat, centerLon] = getCenter(refFeature.geometry.coordinates, refFeature.geometry.type);\n    lastCenter = { centerLat, centerLon, ref: refFeature };\n    if (circleLayer) map.removeLayer(circleLayer);\n    circleLayer = L.circle([centerLat, centerLon], { radius: radius * 1000, color: \"#1b5e20\", fillColor: \"#81c784\", fillOpacity: 0.15, weight: 2 }).addTo(map);\n    map.fitBounds(circleLayer.getBounds(), { padding: [24, 24] });\n    recomputeMatches(radius); applySortAndRender();\n    saveSearch({ q: inputRaw, plz: refFeature.properties.plz || \"\", ort: safeOrt(refFeature), radius: radius }); searchActive = true;\n    \n    \/\/ Setze Berechnen-Button zur\u00fcck\n    const btnCalc = document.getElementById(\"btnCalcRoute\");\n    btnCalc.innerHTML = \"\ud83d\ude97 Fahrstrecke berechnen\";\n    btnCalc.style.background = \"#10b981\";\n  }\n\n  function recomputeMatches(radius) {\n    if (!lastCenter) return; const { centerLat, centerLon, ref } = lastCenter, targetNameNorm = normalizeText(safeOrt(ref));\n    allMatches = (geoData || []).map(f => {\n      const [lat, lon] = getCenter(f.geometry.coordinates, f.geometry.type); if (!isFinite(lat) || !isFinite(lon)) return null;\n      const dist = haversine(centerLat, centerLon, lat, lon), bearing = getBearing(centerLat, centerLon, lat, lon);\n      const rawPlz = formatPostcodeDE(f.properties.plz || \"\");\n      return { plz: rawPlz, ort: safeOrt(f), lat, lon, dist, bearing };\n    }).filter(p => p && p.dist <= radius).filter(p => normalizeText(p.ort) !== targetNameNorm);\n  }\n\n  function limitByPrefix(arr, n) {\n    const unique = new Map();\n    for (const x of arr) { const prefix = x.plz.slice(0, 2); if (!unique.has(prefix)) unique.set(prefix, x); if (unique.size >= n) break; }\n    return Array.from(unique.values());\n  }\n\n  \/\/ === OSRM SERVER ROUTING ABFRAGE (Nur DE) ===\n  async function getRoadDistance(lat1, lon1, lat2, lon2) {\n      try {\n          const url = `https:\/\/routing.api.plzprofi.de\/route\/v1\/driving\/${lon1},${lat1};${lon2},${lat2}?overview=false`;\n          const response = await fetch(url);\n          if (response.status === 429) return { error: 'limit' };\n          const data = await response.json();\n          return data.code === 'Ok' ? { dist: data.routes[0].distance \/ 1000 } : null;\n      } catch (e) { return null; }\n  }\n\n  \/\/ === UI UPDATE LOGIK: SLIDER LIVE-UPDATE ===\n  const speedSlider = document.getElementById(\"speedSlider\");\n  const speedLabel = document.getElementById(\"speedLabel\");\n  \n  speedSlider.addEventListener(\"input\", (e) => {\n    const speed = e.target.value;\n    speedLabel.innerText = speed;\n    \n    \/\/ Passe die Zeit-Badges live an\n    viewResults.slice(0, 10).forEach((p, i) => {\n        if (p.roadDist) {\n            const timeBadge = document.getElementById(`time-${i}`);\n            if (timeBadge) {\n                const hours = p.roadDist \/ parseInt(speed);\n                const h = Math.floor(hours), m = Math.round((hours - h) * 60);\n                timeBadge.innerHTML = `\u23f1 ${h > 0 ? h+'h ' : ''}${m}m`;\n            }\n        }\n    });\n  });\n\n  \/\/ === FAHRSTRECKEN-BUTTON LOGIK (L\u00f6st Server-Abfrage aus) ===\n  document.getElementById(\"btnCalcRoute\").addEventListener(\"click\", async () => {\n      if (!lastCenter || !viewResults.length) return alert(\"Bitte starte zuerst eine normale Suche!\");\n\n      const btn = document.getElementById(\"btnCalcRoute\");\n      btn.innerHTML = '\u23f3 Berechne Fahrstrecken...';\n      btn.style.background = \"#d97706\"; \/\/ Orangener Status\n      btn.disabled = true;\n\n      const speed = parseInt(speedSlider.value);\n\n      \/\/ Wir fetchen alle 10 parallel f\u00fcr maximalen Speed\n      const fetchPromises = viewResults.slice(0, 10).map(async (p, i) => {\n          const roadBadge = document.getElementById(`road-${i}`);\n          if (!roadBadge) return;\n          \n          roadBadge.innerHTML = `<span class=\"loading-dots\"><\/span>`;\n          \n          const data = await getRoadDistance(lastCenter.centerLat, lastCenter.centerLon, p.lat, p.lon);\n          \n          if (data && data.error === 'limit') {\n              roadBadge.innerHTML = \"\u26a0\ufe0f Limit\";\n          } else if (data) {\n              p.roadDist = data.dist; \/\/ Sichern f\u00fcr den Live-Slider\n              \n              const hours = data.dist \/ speed;\n              const h = Math.floor(hours), m = Math.round((hours - h) * 60);\n              \n              roadBadge.innerHTML = `${data.dist.toFixed(1)} km`;\n              roadBadge.classList.add('active'); \/\/ F\u00e4rbt es gr\u00fcn\n              \n              \/\/ Blende das gelbe Zeit-Badge ein\n              const timeBadge = document.getElementById(`time-${i}`);\n              if(timeBadge) {\n                 timeBadge.innerHTML = `\u23f1 ${h > 0 ? h+'h ' : ''}${m}m`;\n                 timeBadge.classList.add('active');\n              }\n              \n          } else {\n              roadBadge.innerHTML = \"Fehler\";\n          }\n      });\n\n      await Promise.all(fetchPromises);\n      \n      btn.innerHTML = '\u2705 Erfolgreich berechnet';\n      btn.style.background = \"#059669\"; \/\/ Wieder gr\u00fcn\n      setTimeout(() => { btn.disabled = false; }, 1000);\n  });\n\n  function applySortAndRender() {\n    if (!lastCenter) return; let filteredMatches = allMatches;\n    if (isCompassActive) {\n      filteredMatches = allMatches.filter(p => {\n        let diff = Math.abs(p.bearing - compassAngle); if (diff > 180) diff = 360 - diff;\n        return isCompassInverted ? !(diff <= (compassConeWidth \/ 2)) : (diff <= (compassConeWidth \/ 2));\n      });\n    }\n\n    const sortVal = document.getElementById(\"sortSelect\").value;\n    if(sortVal === \"dist-asc\") filteredMatches.sort((a,b) => a.dist - b.dist);\n    else if(sortVal === \"plz-asc\") filteredMatches.sort((a,b) => a.plz.localeCompare(b.plz));\n    else if(sortVal === \"name-asc\") filteredMatches.sort((a,b) => a.ort.localeCompare(b.ort));\n\n    const limitVal = document.getElementById(\"limitSelect\").value;\n    viewResults = (limitVal === \"top10\") ? limitByPrefix(filteredMatches, 10) : ((limitVal === \"top20\") ? limitByPrefix(filteredMatches, 20) : filteredMatches);\n\n    \/\/ L\u00f6sche eventuell alte \"roadDist\" Werte bei neuer Sortierung\n    viewResults.forEach(p => p.roadDist = null);\n\n    const cityName = safeOrt(lastCenter.ref) || \"\", radiusNow = getRadius();\n    document.getElementById(\"resTitle\").innerHTML = `<strong>Ort: ${formatPostcodeDE(lastCenter.ref.properties?.plz)} ${cityName}<\/strong>`;\n    \n    \/\/ VISUELLE KOMPASS-WARNUNG\n    let compassBadge = \"\";\n    if (isCompassActive) {\n      if (isCompassInverted) {\n        compassBadge = ` &nbsp;|&nbsp; <span style=\"color: #ef4444; font-weight: bold;\">\ud83e\udded Kompass: Umgekehrt<\/span>`;\n      } else {\n        compassBadge = ` &nbsp;|&nbsp; <span style=\"color: #22c55e; font-weight: bold;\">\ud83e\udded Kompass aktiv<\/span>`;\n      }\n    }\n    \n    document.getElementById(\"resMeta\").innerHTML = `${viewResults.length} Treffer im Umkreis (${radiusNow} km)${compassBadge}`;\n\n    document.getElementById(\"resList\").innerHTML = viewResults.map((p, i) => {\n      let roadBadgeHtml = '';\n      let timeBadgeHtml = `<span class=\"badge badge-time\" id=\"time-${i}\"><\/span>`; \/\/ Platzhalter f\u00fcr Zeit\n      \n      if (i < 10) {\n        roadBadgeHtml = `<span class=\"badge badge-road\" id=\"road-${i}\" title=\"Klicke auf 'Fahrstrecke berechnen'\">--- km<\/span>`;\n      } else {\n        roadBadgeHtml = `<span class=\"badge badge-road\" style=\"cursor: default;\" title=\"Wird nur f\u00fcr die Top 10 berechnet\">--- km<\/span>`;\n      }\n\n      return `<li class=\"result-item\">\n         <div class=\"result-left\">\n           <strong class=\"copy-plz\" title=\"PLZ kopieren\" onclick=\"copyPLZ('${p.plz}', this)\">${formatPostcodeDE(p.plz)}<\/strong> ${p.ort}\n         <\/div>\n         <div class=\"result-badges\">\n           ${timeBadgeHtml}\n           ${roadBadgeHtml}\n           <span class=\"badge\" title=\"Luftlinie\">${p.dist.toFixed(1)} km<\/span>\n         <\/div>\n       <\/li>`\n    }).join(\"\");\n\n    markerLayerGroup.clearLayers();\n    markerLayerGroup.addLayer(L.marker([lastCenter.centerLat, lastCenter.centerLon], { icon: searchIcon }).bindPopup(`Gesuchter Ort: <strong>${formatPostcodeDE(lastCenter.ref.properties.plz)}<\/strong> ${cityName}`));\n    \n    if (limitVal !== 'all') {\n      viewResults.forEach(p => markerLayerGroup.addLayer(L.marker([p.lat, p.lon], { icon: resultIcon }).bindPopup(`<strong>${formatPostcodeDE(p.plz)}<\/strong> ${p.ort}`)));\n    }\n\n    \/\/ Setze Berechnen-Button zur\u00fcck\n    const btnCalc = document.getElementById(\"btnCalcRoute\");\n    if(btnCalc) {\n        btnCalc.innerHTML = \"\ud83d\ude97 Fahrstrecke berechnen\";\n        btnCalc.style.background = \"#10b981\";\n    }\n  }\n\n  \/\/ === LIVE SYNC ===\n  document.getElementById(\"radiusInput\").addEventListener(\"input\", (e) => {\n    document.getElementById(\"radiusNumber\").value = e.target.value; document.getElementById(\"radiusLabel\").innerText = e.target.value;\n    if (searchActive && circleLayer) { circleLayer.setRadius(e.target.value * 1000); recomputeMatches(e.target.value); applySortAndRender(); }\n  });\n  document.getElementById(\"radiusNumber\").addEventListener(\"input\", (e) => {\n    let val = Math.min(100, Math.max(1, Number(e.target.value) || 30));\n    document.getElementById(\"radiusInput\").value = val; document.getElementById(\"radiusLabel\").innerText = val;\n    if (searchActive && circleLayer) { circleLayer.setRadius(val * 1000); recomputeMatches(val); applySortAndRender(); }\n  });\n  document.getElementById(\"sortSelect\").addEventListener(\"change\", () => { if(searchActive) applySortAndRender(); });\n  document.getElementById(\"limitSelect\").addEventListener(\"change\", () => { if(searchActive) applySortAndRender(); });\n\n  \/\/ === HISTORY ===\n  function historyKey() { return \"plzHistory_DE\"; }\n  function saveSearch(entry) {\n    if (!entry.plz && !entry.q) return; \n    let key = historyKey(), h = JSON.parse(localStorage.getItem(key) || \"[]\");\n    h = h.filter(e => !(e.plz === entry.plz && e.radius === entry.radius)); \n    h.unshift(entry); if (h.length > 10) h = h.slice(0, 10);\n    localStorage.setItem(key, JSON.stringify(h)); renderHistory();\n  }\n  function renderHistory() {\n    const list = document.getElementById(\"history-list\"); list.innerHTML = \"\"; \n    const h = JSON.parse(localStorage.getItem(historyKey()) || \"[]\");\n    for (const e of h) {\n      const li = document.createElement(\"li\"), dispName = `${formatPostcodeDE(e.plz || e.q)} ${e.ort || ''}`.trim();\n      li.textContent = `${dispName} (${e.radius} km)`; li.title = li.textContent;\n      li.onclick = () => { \n        document.getElementById(\"searchInput\").value = e.plz || e.q; document.getElementById(\"radiusInput\").value = e.radius; \n        document.getElementById(\"radiusNumber\").value = e.radius; document.getElementById(\"radiusLabel\").innerText = e.radius; \n        document.getElementById(\"btnSearch\").click(); \n      };\n      list.appendChild(li);\n    }\n  }\n  document.getElementById(\"clear-history\").addEventListener(\"click\", () => { localStorage.removeItem(historyKey()); renderHistory(); });\n\n  \/\/ === EXPORT ===\n  document.getElementById(\"expToggle\").addEventListener(\"click\", () => { document.getElementById(\"expPanel\").classList.toggle(\"open\"); });\n  function downloadBlob(blob, filename) {\n    const url = URL.createObjectURL(blob), a = document.createElement(\"a\");\n    a.href = url; a.download = filename; document.body.appendChild(a); a.click(); a.remove(); setTimeout(() => URL.revokeObjectURL(url), 1000);\n  }\n  function exportMeta() {\n    const r = getRadius(), limMap = { top10: \"Top 10\", top20: \"Top 20\", all: \"Alle\" };\n    let city = \"\"; if (lastCenter && lastCenter.ref) { city = (formatPostcodeDE(lastCenter.ref.properties.plz) + \" \" + safeOrt(lastCenter.ref)).trim(); }\n    return { title: `PLZ Profi DE \u00b7 Radius ${r} km \u00b7 Umfang ${limMap[document.getElementById(\"limitSelect\").value] || \"Alle\"}`, city };\n  }\n  function visibleColumns() { return { byDist: (document.getElementById(\"sortSelect\").value === \"dist-asc\"), header: (document.getElementById(\"sortSelect\").value === \"dist-asc\") ? [\"PLZ\", \"Ort\", \"Distanz_km\"] : [\"PLZ\", \"Ort\"] }; }\n\n  document.getElementById(\"btnExportCsv\").addEventListener(\"click\", () => {\n    if (!viewResults.length) return; const meta = exportMeta(), cols = visibleColumns();\n    const rows = viewResults.map(r => cols.byDist ? [formatPostcodeDE(r.plz), r.ort, r.dist.toFixed(2)] : [formatPostcodeDE(r.plz), r.ort]);\n    const csv = [[meta.title], meta.city ? [`Gesuchter Ort: ${meta.city}`] : [], [], cols.header, ...rows].filter(r => r.length > 0).map(r => r.map(x => String(x).replace(\/\"\/g, '\"\"')).map(x => `\"${x}\"`).join(\",\")).join(\"\\n\");\n    downloadBlob(new Blob([csv], { type: \"text\/csv;charset=utf-8;\" }), `plzprofi_DE_${Date.now()}.csv`);\n  });\n  document.getElementById(\"btnExportXlsx\").addEventListener(\"click\", () => {\n    if (!viewResults.length) return; const meta = exportMeta(), cols = visibleColumns(), sheet = [[meta.title]];\n    if (meta.city) sheet.push([`Gesuchter Ort: ${meta.city}`]); sheet.push([]); sheet.push(cols.header);\n    viewResults.forEach(r => sheet.push(cols.byDist ? [formatPostcodeDE(r.plz), r.ort, Number(r.dist.toFixed(2))] : [formatPostcodeDE(r.plz), r.ort]));\n    const ws = XLSX.utils.aoa_to_sheet(sheet), wb = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(wb, ws, \"Ergebnisse\"); XLSX.writeFile(wb, `plzprofi_DE_${Date.now()}.xlsx`);\n  });\n  document.getElementById(\"btnExportPdf\").addEventListener(\"click\", () => {\n    if (!viewResults.length) return; const meta = exportMeta(), cols = visibleColumns(), { jsPDF } = window.jspdf, doc = new jsPDF({ unit: \"pt\", format: \"a4\" });\n    doc.setFontSize(12); doc.text(meta.title, 40, 40); let y = 58; if (meta.city) { doc.setFontSize(11); doc.text(`Gesuchter Ort: ${meta.city}`, 40, y); y += 18; }\n    doc.setFontSize(10); doc.text(cols.byDist ? \"PLZ    Ort                               Dist(km)\" : \"PLZ    Ort\", 40, y); y += 14;\n    for (const r of viewResults) {\n      const line = cols.byDist ? `${formatPostcodeDE(r.plz).padEnd(6)}  ${(r.ort || \"\").padEnd(30)}  ${r.dist.toFixed(1).padStart(7)}` : `${formatPostcodeDE(r.plz).padEnd(6)}  ${(r.ort || \"\")}`;\n      doc.text(line.substring(0, 95), 40, y); y += 14; if (y > 780) { doc.addPage(); y = 40; }\n    }\n    doc.save(`plzprofi_DE_${Date.now()}.pdf`);\n  });\n  document.getElementById(\"btnExportTxt\").addEventListener(\"click\", () => {\n    if (!viewResults.length) return; const meta = exportMeta(), cols = visibleColumns();\n    const lines = [meta.title, meta.city ? `Gesuchter Ort: ${meta.city}` : \"\", \"\", cols.byDist ? \"PLZ | Ort | Distanz(km)\" : \"PLZ | Ort\", ...viewResults.map(r => cols.byDist ? `${formatPostcodeDE(r.plz)} | ${r.ort} | ${r.dist.toFixed(1)}` : `${formatPostcodeDE(r.plz)} | ${r.ort}`)].filter(l => l !== \"\").join(\"\\n\");\n    downloadBlob(new Blob([lines], { type: \"text\/plain;charset=utf-8;\" }), `plzprofi_DE_${Date.now()}.txt`);\n  });\n\n  \/\/ init\n  (function init() { document.getElementById(\"radiusLabel\").innerText = getRadius(); loadDataset(datasetUrlEl.value.trim() || \"plz-de-short.geojson\"); renderHistory(); })();\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>PLZ Profi \u00b7 Umkreissuche Deutschland (DE) \u00b7 Umkreissuche f\u00fcr Postleitzahlen &#038; Orte Suchen Sie in Deutschland nach Orten und Postleitzahlen im gew\u00fcnschten Radius. Unser System verarbeitet 5-stellige Eingaben (z.B. 04109) oder Ortsnamen sekundenschnell. Jetzt Suche starten Kartensuche \u00b7 DE \ud83c\udde9\ud83c\uddea DE GeoJSON URL: Neu laden PLZ oder Ort: Ortsauswahl \u25bc Radius (1\u2013100 km): 30 km [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":387,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-222","page","type-page","status-publish","hentry"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/plzprofi.de\/hu\/wp-json\/wp\/v2\/pages\/222","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/plzprofi.de\/hu\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/plzprofi.de\/hu\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/plzprofi.de\/hu\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/plzprofi.de\/hu\/wp-json\/wp\/v2\/comments?post=222"}],"version-history":[{"count":15,"href":"https:\/\/plzprofi.de\/hu\/wp-json\/wp\/v2\/pages\/222\/revisions"}],"predecessor-version":[{"id":695,"href":"https:\/\/plzprofi.de\/hu\/wp-json\/wp\/v2\/pages\/222\/revisions\/695"}],"up":[{"embeddable":true,"href":"https:\/\/plzprofi.de\/hu\/wp-json\/wp\/v2\/pages\/387"}],"wp:attachment":[{"href":"https:\/\/plzprofi.de\/hu\/wp-json\/wp\/v2\/media?parent=222"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}