fix: Blog-Routen + modernes Design + §400-Hinweis

- Blog-Route nutzt jetzt _ARTIKEL-Mapping (statt falscher Dateinamen-Konstruktion)
- Vollstaendiges CSS-Redesign: White Cards, Shadows, Sticky-Header, Gradient-Hero
- Kontakt-CTA-Banner auf jeder Seite vor dem Footer
- §400 BGB Vermittlungshinweis im Footer aller Seiten
- FAQ mit details/summary-Akkordeon + CSS-Indikatoren
- Verbesserte Typografie und Spacing
This commit is contained in:
Hermes Agent
2026-06-25 18:20:05 +02:00
parent 7da1649998
commit b86cb1742a
+251 -331
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""IT-Hilfe-Sofort.de — Flask-Webapp für IT-Dienstleistungen im Gesundheitswesen.""" """IT-Hilfe-Sofort.de — Flask-Webapp fuer IT-Dienstleistungen im Gesundheitswesen."""
import os import os
import re import re
@@ -24,6 +24,25 @@ def _base_template(title: str, content: str, active: str = "") -> str:
Returns: Returns:
Vollstaendiges HTML-Dokument als String. Vollstaendiges HTML-Dokument als String.
""" """
# Kontakt-Banner (auf jeder Seite)
kontakt_banner = """
<section class="cta-banner">
<div class="cta-inner">
<div class="cta-text">
<h2>Bereit fuer die digitale Zukunft?</h2>
<p>Kostenlose Erstberatung — wir finden die passende IT-Loesung fuer Ihre Einrichtung.</p>
</div>
<a href="/kontakt" class="btn-primary btn-large">Jetzt Beratungsgespraech vereinbaren</a>
</div>
</section>"""
# Footer mit §400-Hinweis
footer_vermittlung = """
<p style="margin-top:1rem;font-size:0.8rem;color:#6B7280;max-width:600px;margin-left:auto;margin-right:auto">
* Wir vermitteln IT-Dienstleistungen und -Produkte. Es faellt eine Vermittlungsgebuehr
gemaess § 400 BGB an — diese fuehrt nicht zu hoeheren Preisen fuer Sie.
</p>"""
return f"""<!DOCTYPE html> return f"""<!DOCTYPE html>
<html lang="de"> <html lang="de">
<head> <head>
@@ -32,49 +51,95 @@ def _base_template(title: str, content: str, active: str = "") -> str:
<title>{title} | IT-Hilfe-Sofort.de</title> <title>{title} | IT-Hilfe-Sofort.de</title>
<style> <style>
*,*::before,*::after{{box-sizing:border-box;margin:0;padding:0}} *,*::before,*::after{{box-sizing:border-box;margin:0;padding:0}}
body{{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#F9FAFB;color:#1F2937;line-height:1.6}} body{{font-family:'Inter',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#FAFBFC;color:#1a1a2e;line-height:1.7;font-size:16px}}
.site-header{{background:#1F2937;padding:1rem 1.5rem;display:flex;align-items:center;justify-content:space-between}} .site-header{{background:#fff;border-bottom:1px solid #e8ecf1;padding:0 2rem;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:100;height:64px;box-shadow:0 1px 3px rgba(0,0,0,0.04)}}
.site-header .logo{{color:#F9FAFB;font-weight:700;font-size:1.05rem;letter-spacing:-0.01em;text-decoration:none}} .site-header .logo{{color:#20228a;font-weight:800;font-size:1.05rem;letter-spacing:-0.01em;text-decoration:none;display:flex;align-items:center;gap:0.5rem}}
.nav-link{{color:#9CA3AF;text-decoration:none;font-size:0.9rem;padding:0.5rem}} .site-header .logo::before{{content:'';display:inline-block;width:28px;height:28px;background:linear-gradient(135deg,#20228a,#3b4fd4);border-radius:6px}}
.nav-link:hover,.nav-link.active{{color:#F9FAFB}} .nav-link{{color:#4a5568;text-decoration:none;font-size:0.9rem;padding:0.5rem 0.75rem;border-radius:6px;transition:all 0.15s;font-weight:500}}
.menu-toggle{{background:none;border:none;color:#9CA3AF;font-size:0.9rem;font-weight:600;cursor:pointer;padding:0.5rem;display:none}} .nav-link:hover,.nav-link.active{{color:#20228a;background:#f0f2ff}}
.hero{{background:linear-gradient(135deg,#20228a,#161e6e);color:#fff;padding:5rem 1.5rem;text-align:center}} .menu-toggle{{background:none;border:none;color:#4a5568;font-size:0.9rem;font-weight:600;cursor:pointer;padding:0.5rem;display:none}}
.hero h1{{font-size:2.2rem;font-weight:800;letter-spacing:-0.02em;line-height:1.2;margin-bottom:1rem}} .hero{{background:linear-gradient(135deg,#20228a 0%,#2d3496 40%,#1a1e6e 100%);color:#fff;padding:6rem 2rem;text-align:center;position:relative;overflow:hidden}}
.hero p{{color:rgba(255,255,255,0.85);font-size:1.1rem;max-width:600px;margin:0 auto}} .hero::after{{content:'';position:absolute;top:-50%;right:-20%;width:500px;height:500px;background:rgba(255,255,255,0.03);border-radius:50%;pointer-events:none}}
.card{{background:#fff;border:1px solid #E5E7EB;border-radius:8px;padding:2rem}} .hero h1{{font-size:2.8rem;font-weight:800;letter-spacing:-0.03em;line-height:1.15;margin-bottom:1.25rem;position:relative;z-index:1}}
.card-grid{{display:grid;grid-template-columns:1fr;gap:1.5rem;max-width:1100px;margin:0 auto;padding:0 1.5rem}} .hero p{{color:rgba(255,255,255,0.85);font-size:1.15rem;max-width:650px;margin:0 auto;position:relative;z-index:1;line-height:1.6}}
.hero .btn-primary{{margin-top:2rem;position:relative;z-index:1}}
.card{{background:#fff;border:1px solid #e8ecf1;border-radius:12px;padding:2rem;transition:box-shadow 0.2s,transform 0.2s}}
.card:hover{{box-shadow:0 8px 30px rgba(32,34,138,0.08);transform:translateY(-2px)}}
.card h2,.card h3{{font-size:1.15rem;font-weight:700;margin-bottom:0.75rem;color:#20228a}}
.card p{{color:#4a5568;font-size:0.95rem;line-height:1.65}}
.card-grid{{display:grid;grid-template-columns:1fr;gap:1.5rem;max-width:1100px;margin:0 auto;padding:0 2rem}}
@media(min-width:768px){{.card-grid{{grid-template-columns:repeat(3,1fr)}}}} @media(min-width:768px){{.card-grid{{grid-template-columns:repeat(3,1fr)}}}}
.btn-primary{{display:inline-block;background:#20228a;color:#fff;border:none;border-radius:6px;padding:0.8rem 2rem;font-size:0.95rem;font-weight:600;text-decoration:none;cursor:pointer;transition:background 0.2s}} @media(min-width:768px){{.card-grid-4{{grid-template-columns:repeat(2,1fr)}}}}
.btn-primary:hover{{background:#161e6e}} @media(min-width:1024px){{.card-grid-4{{grid-template-columns:repeat(4,1fr)}}}}
.content{{max-width:720px;margin:2rem auto;padding:0 1.5rem}} .btn-primary{{display:inline-block;background:#20228a;color:#fff;border:none;border-radius:8px;padding:0.8rem 2rem;font-size:0.95rem;font-weight:600;text-decoration:none;cursor:pointer;transition:all 0.2s;box-shadow:0 2px 8px rgba(32,34,138,0.2)}}
.content h1{{font-size:2rem;font-weight:800;margin-bottom:1rem}} .btn-primary:hover{{background:#161e6e;box-shadow:0 4px 16px rgba(32,34,138,0.3);transform:translateY(-1px)}}
.content h2{{font-size:1.5rem;font-weight:700;margin:2rem 0 1rem}} .btn-large{{padding:1rem 2.5rem;font-size:1.05rem}}
.content p{{margin-bottom:1rem;line-height:1.7}} .content{{max-width:760px;margin:3rem auto;padding:0 2rem}}
.site-footer{{background:#1F2937;color:#9CA3AF;padding:3rem 1.5rem;font-size:0.85rem;text-align:center}} .content h1{{font-size:2.2rem;font-weight:800;letter-spacing:-0.02em;margin-bottom:1rem;color:#1a1a2e}}
.site-footer a{{color:#D1D5DB;text-decoration:none;margin:0 0.5rem}} .content h2{{font-size:1.5rem;font-weight:700;margin:2.5rem 0 1rem;color:#20228a}}
.site-footer a:hover{{color:#F9FAFB}} .content h3{{font-size:1.15rem;font-weight:700;margin:1.5rem 0 0.75rem;color:#1a1a2e}}
.section{{padding:4rem 1.5rem}} .content p{{margin-bottom:1.25rem;line-height:1.75;color:#4a5568}}
.section h2{{font-size:1.8rem;font-weight:700;text-align:center;margin-bottom:2rem}} .content ul,.content ol{{margin:1rem 0 1.5rem 1.5rem;color:#4a5568}}
@media(max-width:767px){{.menu-toggle{{display:block}}nav#mainNav{{display:none}}nav#mainNav.open{{display:flex;flex-direction:column;position:fixed;top:56px;left:0;right:0;background:#1F2937;padding:1rem;gap:0;z-index:1000}}}} .content li{{margin-bottom:0.5rem;line-height:1.65}}
.section{{padding:5rem 2rem}}
.section h2{{font-size:2rem;font-weight:800;text-align:center;margin-bottom:2.5rem;letter-spacing:-0.02em;color:#1a1a2e}}
.section-sub{{color:#6b7280;text-align:center;max-width:600px;margin:-1.5rem auto 2.5rem;font-size:1.05rem}}
.site-footer{{background:#1a1a2e;color:#94a3b8;padding:3rem 2rem;font-size:0.9rem;text-align:center}}
.site-footer a{{color:#cbd5e1;text-decoration:none;margin:0 0.75rem;transition:color 0.15s}}
.site-footer a:hover{{color:#fff}}
.cta-banner{{background:linear-gradient(135deg,#f0f2ff,#e8ecf5);padding:4rem 2rem;border-top:1px solid #e8ecf1;border-bottom:1px solid #e8ecf1}}
.cta-inner{{max-width:900px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;gap:2rem;flex-wrap:wrap}}
.cta-text h2{{font-size:1.5rem;font-weight:700;color:#1a1a2e;margin-bottom:0.5rem}}
.cta-text p{{color:#4a5568;font-size:1rem}}
.article-layout{{display:grid;grid-template-columns:1fr 280px;gap:3rem;max-width:1100px;margin:0 auto;padding:3rem 2rem}}
@media(max-width:860px){{.article-layout{{grid-template-columns:1fr}}}}
.article-content{{max-width:680px}}
.article-content img{{max-width:100%;border-radius:8px;margin:1.5rem 0}}
.article-sidebar{{background:#fff;border:1px solid #e8ecf1;border-radius:12px;padding:1.5rem;align-self:start;position:sticky;top:80px}}
.sidebar-title{{font-size:0.85rem;font-weight:700;text-transform:uppercase;letter-spacing:0.05em;color:#94a3b8;margin-bottom:1rem}}
.sidebar-link{{display:block;color:#20228a;text-decoration:none;padding:0.5rem 0;font-size:0.9rem;font-weight:500;border-bottom:1px solid #f0f2f5;transition:color 0.15s}}
.sidebar-link:last-child{{border-bottom:none}}
.sidebar-link:hover{{color:#161e6e}}
.faq-item{{background:#fff;border:1px solid #e8ecf1;border-radius:10px;padding:1.25rem 1.5rem;margin-bottom:0.75rem;cursor:pointer;transition:box-shadow 0.15s}}
.faq-item:hover{{box-shadow:0 4px 12px rgba(0,0,0,0.04)}}
.faq-item summary{{font-weight:600;color:#1a1a2e;list-style:none;display:flex;justify-content:space-between;align-items:center}}
.faq-item summary::after{{content:'+';font-size:1.3rem;color:#20228a;font-weight:400}}
.faq-item[open] summary::after{{content:'-'}}
.faq-item .answer{{margin-top:0.75rem;color:#4a5568;line-height:1.65}}
.badge{{display:inline-block;background:#f0f2ff;color:#20228a;font-size:0.8rem;font-weight:600;padding:0.25rem 0.75rem;border-radius:20px}}
.contact-info{{background:#fff;border:1px solid #e8ecf1;border-radius:12px;padding:2rem}}
.contact-info h3{{margin-bottom:1rem}}
.contact-info p{{margin-bottom:0.5rem}}
@media(max-width:767px){{
.menu-toggle{{display:block}}
nav#mainNav{{display:none}}
nav#mainNav.open{{display:flex;flex-direction:column;position:fixed;top:64px;left:0;right:0;background:#fff;padding:1rem;gap:0.25rem;z-index:1000;box-shadow:0 4px 12px rgba(0,0,0,0.08)}}
.hero h1{{font-size:2rem}}
.hero{{padding:4rem 1.25rem}}
.cta-inner{{flex-direction:column;text-align:center}}
.content h1{{font-size:1.75rem}}
}}
</style> </style>
</head> </head>
<body> <body>
<header class="site-header"> <header class="site-header">
<a href="/" class="logo">IT-Hilfe-Sofort.de</a> <a href="/" class="logo">IT-Hilfe-Sofort.de</a>
<nav id="mainNav" style="align-items:center;gap:0.5rem"> <button class="menu-toggle" onclick="document.getElementById('mainNav').classList.toggle('open')">Menü</button>
<nav id="mainNav" style="display:flex;align-items:center;gap:0.25rem">
<a href="/" class="nav-link{" active" if active == "home" else ""}">Startseite</a> <a href="/" class="nav-link{" active" if active == "home" else ""}">Startseite</a>
<a href="/blog/telematik-vorschriften" class="nav-link{" active" if active == "blog" else ""}">Blog</a> <a href="/blog/telematik-vorschriften" class="nav-link{" active" if active == "blog" else ""}">Blog</a>
<a href="/leistungen/pflegedienste" class="nav-link{" active" if active == "leistungen" else ""}">Leistungen</a> <a href="/leistungen/pflegedienste" class="nav-link{" active" if active == "leistungen" else ""}">Leistungen</a>
<a href="/it-service/hardware" class="nav-link{" active" if active == "it-service" else ""}">IT-Service</a> <a href="/it-service/hardware" class="nav-link{" active" if active == "it-service" else ""}">IT-Service</a>
<a href="/ueber-uns" class="nav-link{" active" if active == "ueber-uns" else ""}">Über uns</a> <a href="/ueber-uns" class="nav-link{" active" if active == "ueber-uns" else ""}">Über uns</a>
<a href="/kontakt" class="btn-primary" style="padding:0.5rem 1.2rem;font-size:0.85rem">Kontakt</a> <a href="/kontakt" class="btn-primary" style="padding:0.55rem 1.25rem;font-size:0.85rem;margin-left:0.5rem">Kontakt</a>
</nav> </nav>
<button class="menu-toggle" onclick="document.getElementById('mainNav').classList.toggle('open')">Menü</button>
</header> </header>
<main>{content}</main> <main>{content}</main>
{kontakt_banner}
<footer class="site-footer"> <footer class="site-footer">
<p>IT-Hilfe-Sofort.de — Spezialisiert auf Pflegeeinrichtungen</p> <p><strong style="color:#e2e8f0">IT-Hilfe-Sofort.de</strong> — Spezialisiert auf Pflegeeinrichtungen</p>
<p style="margin-top:0.5rem"><a href="/impressum">Impressum</a> <a href="/datenschutz">Datenschutz</a></p> <p style="margin-top:0.5rem"><a href="/impressum">Impressum</a> · <a href="/datenschutz">Datenschutz</a> · <a href="/faq">FAQ</a></p>
{footer_vermittlung}
</footer> </footer>
</body> </body>
</html>""" </html>"""
@@ -85,27 +150,56 @@ def index() -> str:
"""Startseite.""" """Startseite."""
content = """ content = """
<section class="hero"> <section class="hero">
<h1>IT-Hilfer das Gesundheitswesen</h1> <h1>IT-Hilfe fuer das Gesundheitswesen</h1>
<p>Sicher, schnell, spezialisiert auf Pflegeinrichtungen.</p> <p>Sicher, schnell, spezialisiert auf Pflegeeinrichtungen — mit persoenlicher Beratung und massgeschneiderten Loesungen.</p>
<p style="margin-top:2rem"><a href="/kontakt" class="btn-primary">Beratungsgespräch vereinbaren</a></p> <a href="/kontakt" class="btn-primary btn-large">Kostenloses Beratungsgespraech</a>
</section> </section>
<section class="section"> <section class="section">
<h2>Unsere Werte</h2> <h2>Warum IT-Hilfe-Sofort?</h2>
<p class="section-sub">Wir verstehen die besonderen Anforderungen im Gesundheitswesen und liefern passgenaue IT-Loesungen.</p>
<div class="card-grid"> <div class="card-grid">
<div class="card"><h2>DSGVO-konform</h2><p>Ihre Patientendaten in sicheren Händen — mit lückenloser Dokumentation und zertifizierten Prozessen.</p></div> <div class="card">
<div class="card"><h2>24/7 Support</h2><p>Wir sind da, wenn Sie uns brauchen — mit garantierter Reaktionszeit und persönlichem Ansprechpartner.</p></div> <h2>DSGVO-konform</h2>
<div class="card"><h2>Förderfähig</h2><p>KfW-Anträge stellen wir gemeinsam — digitale Infrastruktur ohne finanzielle Hürden.</p></div> <p>Patientendaten in absolut sicheren Haenden — mit zertifizierten Prozessen und Hosting in Deutschland.</p>
</div>
<div class="card">
<h2>Schnelle Reaktion</h2>
<p>Garantierte Reaktionszeiten mit persoenlichem Ansprechpartner — weil im Gesundheitswesen jede Minute zaehlt.</p>
</div>
<div class="card">
<h2>Foerderfaehig</h2>
<p>KfW-Antraege stellen wir gemeinsam — digitale Infrastruktur ohne finanzielle Huerden.</p>
</div>
</div> </div>
</section> </section>
<section class="section" style="background:#fff"> <section class="section" style="background:#fff">
<h2>Unsere Leistungen</h2> <h2>Unsere Leistungen</h2>
<p class="section-sub">Vom Pflegedienst bis zur Tagespflege — wir haben die passende IT-Loesung.</p>
<div class="card-grid"> <div class="card-grid">
<div class="card"><h2>Pflegedienste</h2><p>Tourenplanung, mobile Datenerfassung, Abrechnung.</p></div> <div class="card">
<div class="card"><h2>Pflegeheime</h2><p>Heimverwaltung, Biometrie, Dokumentation.</p></div> <h2>Pflegedienste</h2>
<div class="card"><h2>Tagespflegen</h2><p>Terminbuchung, Fahrdienst, Aktivierungsangebote.</p></div> <p>Tourenplanung, mobile Datenerfassung und Abrechnung aus einer Hand.</p>
<div class="card"><h2>Hardware</h2><p>Server, Thin Clients, Drucker — Beschaftung & Einrichtung.</p></div> </div>
<div class="card"><h2>Netzwerk</h2><p>WLAN-Ausleuchtung, VPN, Firewall, Netzwerksegmentierung.</p></div> <div class="card">
<div class="card"><h2>Support</h2><p>SLA-Stufen, Fernwartung, Monitoring — maßgeschneidert.</p></div> <h2>Pflegeheime</h2>
<p>Heimverwaltung, Biometrie und digitale Dokumentationssysteme.</p>
</div>
<div class="card">
<h2>Tagespflegen</h2>
<p>Terminbuchung, Fahrdienst und Aktivierungsangebote digital organisiert.</p>
</div>
<div class="card">
<h2>Hardware</h2>
<p>Server, Thin Clients und Drucker — Beschaffung, Einrichtung und Wartung.</p>
</div>
<div class="card">
<h2>Netzwerk</h2>
<p>WLAN-Ausleuchtung, VPN, Firewall und Netzwerksegmentierung.</p>
</div>
<div class="card">
<h2>IT-Support</h2>
<p>SLA-Stufen, Fernwartung und 24/7-Monitoring — massgeschneidert.</p>
</div>
</div> </div>
</section> </section>
""" """
@@ -121,121 +215,37 @@ def impressum() -> str:
@app.route("/datenschutz") @app.route("/datenschutz")
def datenschutz() -> str: def datenschutz() -> str:
"""Datenschutzerklärung.""" """Datenschutzerklaerung."""
content = '<div class="content"><h1>Datenschutzerklärung</h1><p>Platzhalter — vollständige Datenschutzerklärung folgt.</p></div>' content = '<div class="content"><h1>Datenschutzerklaerung</h1><p>Platzhalter — vollstaendige Datenschutzerklaerung folgt.</p></div>'
return _base_template("Datenschutz", content) return _base_template("Datenschutz", content)
# ── Blog-Artikel-Datenbank ─────────────────────────────── # ── Blog-Artikel-Datenbank ───────────────────────────────
_ARTIKEL = [ _ARTIKEL = [
( ("/blog/telematik-vorschriften", "TI-Anschlusspflicht 2025", "artikel-01-ti-anschlusspflicht.html"),
"/blog/telematik-vorschriften", ("/blog/telematik-vorschriften", "Elektronische Abrechnung ab 12/2026", "artikel-02-elektronische-abrechnung.html"),
"TI-Anschlusspflicht 2025", ("/blog/telematik-vorschriften", "SMC-B-Karte — 33% Abdeckung", "artikel-03-smc-b-karte.html"),
"artikel-01-ti-anschlusspflicht.html", ("/blog/dsgvo-it-sicherheit", "46,9 Mio. EUR DSGVO-Bussgelder", "artikel-04-dsgvo-bussgelder.html"),
), ("/blog/dsgvo-it-sicherheit", "BSI: 16 Schwachstellen Pflegesoftware", "artikel-05-bsi-schwachstellen.html"),
( ("/blog/dsgvo-it-sicherheit", "138 Sicherheitsvorfaelle Gesundheitswesen", "artikel-06-sicherheitsvorfaelle.html"),
"/blog/telematik-vorschriften", ("/blog/telematik-vorschriften", "NIS-2 Registrierungsfrist", "artikel-07-nis2.html"),
"Elektronische Abrechnung ab 12/2026", ("/blog/fachkraeftemangel", "200.000 unbesetzte Pflegestellen", "artikel-08-fachkraeftemangel.html"),
"artikel-02-elektronische-abrechnung.html", ("/blog/fachkraeftemangel", "62% Altenpflege — Personalplanung", "artikel-09-personalplanung.html"),
), ("/blog/fachkraeftemangel", "KI-Sprachassistent spart 27% Zeit", "artikel-10-ki-sprachassistent.html"),
( ("/blog/foerderung-finanzierung", "Bis 12.000 EUR Digitalisierungszuschuss", "artikel-11-digitalisierungszuschuss.html"),
"/blog/telematik-vorschriften", ("/blog/foerderung-finanzierung", "DiPA — 70 EUR monatlich ab 2026", "artikel-12-dipa.html"),
"SMC-B-Karte — 33% Abdeckung", ("/blog/foerderung-finanzierung", "Pflegeversicherung 1 Mrd. Defizit", "artikel-13-pflegeversicherung.html"),
"artikel-03-smc-b-karte.html", ("/blog/qualitaet-pruefung", "QPR ambulant ab Juli 2026", "artikel-14-qpr-ambulant.html"),
), ("/blog/qualitaet-pruefung", "PeBeM verpflichtend ab 2026", "artikel-15-pebem.html"),
( ("/blog/qualitaet-pruefung", "MDK-Pruefung digital bestehen", "artikel-16-mdk-pruefung.html"),
"/blog/dsgvo-it-sicherheit",
"46,9 Mio. EUR DSGVO-Bugelder",
"artikel-04-dsgvo-bussgelder.html",
),
(
"/blog/dsgvo-it-sicherheit",
"BSI: 16 Schwachstellen Pflegesoftware",
"artikel-05-bsi-schwachstellen.html",
),
(
"/blog/dsgvo-it-sicherheit",
"138 Sicherheitsvorfaelle Gesundheitswesen",
"artikel-06-sicherheitsvorfaelle.html",
),
(
"/blog/telematik-vorschriften",
"NIS-2 Registrierungsfrist",
"artikel-07-nis2.html",
),
(
"/blog/fachkraeftemangel",
"200.000 unbesetzte Pflegestellen",
"artikel-08-fachkraeftemangel.html",
),
(
"/blog/fachkraeftemangel",
"62% Altenpflege — Personalplanung",
"artikel-09-personalplanung.html",
),
(
"/blog/fachkraeftemangel",
"KI-Sprachassistent spart 27% Zeit",
"artikel-10-ki-sprachassistent.html",
),
(
"/blog/foerderung-finanzierung",
"Bis 12.000 EUR Digitalisierungszuschuss",
"artikel-11-digitalisierungszuschuss.html",
),
(
"/blog/foerderung-finanzierung",
"DiPA — 70 EUR monatlich ab 2026",
"artikel-12-dipa.html",
),
(
"/blog/foerderung-finanzierung",
"Pflegeversicherung 1 Mrd. Defizit",
"artikel-13-pflegeversicherung.html",
),
(
"/blog/qualitaet-pruefung",
"QPR ambulant ab Juli 2026",
"artikel-14-qpr-ambulant.html",
),
(
"/blog/qualitaet-pruefung",
"PeBeM verpflichtend ab 2026",
"artikel-15-pebem.html",
),
(
"/blog/qualitaet-pruefung",
"MDK-Pruefung digital bestehen",
"artikel-16-mdk-pruefung.html",
),
("/blog/telematik-vorschriften", "ePA fuer alle — Opt-out", "artikel-17-epa.html"), ("/blog/telematik-vorschriften", "ePA fuer alle — Opt-out", "artikel-17-epa.html"),
( ("/blog/dsgvo-it-sicherheit", "Cloud-Hosting Deutschland", "artikel-18-cloud-hosting.html"),
"/blog/dsgvo-it-sicherheit", ("/blog/praxis-tipps", "Tablets fuer die Pflege — Top 5", "artikel-19-tablets.html"),
"Cloud-Hosting Deutschland",
"artikel-18-cloud-hosting.html",
),
(
"/blog/praxis-tipps",
"Tablets fuer die Pflege — Top 5",
"artikel-19-tablets.html",
),
("/blog/praxis-tipps", "WLAN im Pflegeheim", "artikel-20-wlan.html"), ("/blog/praxis-tipps", "WLAN im Pflegeheim", "artikel-20-wlan.html"),
("/blog/praxis-tipps", "Offline-Dokumentation", "artikel-21-offline.html"), ("/blog/praxis-tipps", "Offline-Dokumentation", "artikel-21-offline.html"),
( ("/blog/dsgvo-it-sicherheit", "E-Mail-Verschluesselung", "artikel-22-email-verschluesselung.html"),
"/blog/dsgvo-it-sicherheit", ("/blog/praxis-tipps", "Medikationsplan digital", "artikel-23-medikationsplan.html"),
"E-Mail-Verschluesselung", ("/blog/praxis-tipps", "IT-Notfallplan 10-Punkte", "artikel-24-it-notfallplan.html"),
"artikel-22-email-verschluesselung.html",
),
(
"/blog/praxis-tipps",
"Medikationsplan digital",
"artikel-23-medikationsplan.html",
),
(
"/blog/praxis-tipps",
"IT-Notfallplan 10-Punkte",
"artikel-24-it-notfallplan.html",
),
] ]
# ── Leistungen-Daten ───────────────────────────────────── # ── Leistungen-Daten ─────────────────────────────────────
@@ -243,52 +253,25 @@ _LEISTUNGEN = {
"pflegedienste": { "pflegedienste": {
"titel": "IT-Loesungen fuer ambulante Pflegedienste", "titel": "IT-Loesungen fuer ambulante Pflegedienste",
"cards": [ "cards": [
( ("Tourenplanung", "Digitale Tourenplanung und mobile Datenerfassung vor Ort. Effizientere Routen und Dokumentation auf dem Tablet."),
"Tourenplanung", ("Mobile Dokumentation", "Echtzeit-Dokumentation von Pflegemassnahmen direkt im Einsatz. Synchronisiert mit der zentralen Verwaltung."),
"Digitale Tourenplanung und mobile Datenerfassung vor Ort. Effizientere Routen und Dokumentation auf dem Tablet.", ("Abrechnung und Reporting", "Automatisierte Abrechnung und Auswertungen. Weniger Verwaltungszeit, mehr Zeit fuer Patienten."),
),
(
"Mobile Dokumentation",
"Echtzeit-Dokumentation von Pflegemaßnahmen direkt im Einsatz. Synchronisiert mit der zentralen Verwaltung.",
),
(
"Abrechnung und Reporting",
"Automatisierte Abrechnung und Auswertungen. Weniger Verwaltungszeit, mehr Zeit fuer Patienten.",
),
], ],
}, },
"pflegeheime": { "pflegeheime": {
"titel": "IT-Loesungen fuer stationaere Pflegeheime", "titel": "IT-Loesungen fuer stationaere Pflegeheime",
"cards": [ "cards": [
( ("Heimverwaltung", "Digitale Heimpleitung: Dienstplanung, Raummanagement, Budget-Controller. Alles in einer Plattform."),
"Heimverwaltung", ("Biometrie und Zutrittssysteme", "Fingerabdruck- und Gesichtserkennung fuer sicheren Zutritt. DSGVO-konform und benutzerfreundlich."),
"Digitale Heimpleitung: Dienstplanung, Raummanagement, Budget-Controller. Alles in einer Plattform.", ("Dokumentationssysteme", "Komplette digitale Dokumentation von Pflegeplanung bis Medikationsueberwachung. Vollstaendig auditfaehig."),
),
(
"Biometrie und Zutrittssysteme",
"Fingerabdruck- und Gesichtserkennung fuer sicheren Zutritt. DSGVO-konform und benutzerfreundlich.",
),
(
"Dokumentationssysteme",
"Komplette digitale Dokumentation von Pflegeplanung bis Medikationsueberwachung. Vollstaendig auditfahig.",
),
], ],
}, },
"tagespflegen": { "tagespflegen": {
"titel": "IT-Loesungen fuer Tagespflegen", "titel": "IT-Loesungen fuer Tagespflegen",
"cards": [ "cards": [
( ("Terminbuchung", "Online-Terminbuchung mit Kalendersynchronisation. Patienten koennen Termine direkt vereinbaren."),
"Terminbuchung", ("Fahrdienstverwaltung", "Organisation von Fahrdiensten und Kurieren. Automatische Zuweisung und Echtzeit-Tracking."),
"Online-Terminbuchung mit Kalendersynchronisation. Patienten koennen Termine direkt vereinbaren.", ("Aktivierungsangebote", "Digitales Planungs- und Monitoring-System fuer Aktivitaeten und Therapie-Programme im Tagesverlauf."),
),
(
"Fahrdienstverwaltung",
"Organisation von Fahrdiensten und Kurieren. Automatische Zuweisung und Echtzeit-Tracking.",
),
(
"Aktivierungsangebote",
"Digitales Planungs- und Monitoring-System fuer Aktivitaeten und Therapie-Programme im Tagesverlauf.",
),
], ],
}, },
} }
@@ -298,81 +281,36 @@ _IT_SERVICE = {
"hardware": { "hardware": {
"titel": "IT-Hardware fuer das Gesundheitswesen", "titel": "IT-Hardware fuer das Gesundheitswesen",
"cards": [ "cards": [
( ("Server und Workstations", "Auswahl, Installation und Wartung von Servern und Workstations. DSGVO-konform und skalierbar."),
"Server und Workstations", ("Thin Clients und Endgeraete", "Kosteneffiziente Thin Clients mit zentralem Management. Einfache Administration, hohe Sicherheit."),
"Auswahl, Installation und Wartung von Servern und Workstations. DSGVO-konform und skalierbar.", ("Drucker und Peripherie", "Medizinische Geraete und Drucker mit Spezialtreibern. Integration in bestehende Netzwerke."),
), ("Beschaffung und Consulting", "Unabhaengige Beratung zur Hardware-Auswahl. Gutes Preis-Leistungs-Verhaeltnis garantiert."),
(
"Thin Clients und Endgeraete",
"Kosteneffiziente Thin Clients mit zentralem Management. Einfache Administration, hohe Sicherheit.",
),
(
"Drucker und Peripherie",
"Medizinische Geraete und Drucker mit Spezialtreibern. Integration in bestehende Netzwerke.",
),
(
"Beschaffung und Consulting",
"Unabhaengige Beratung zur Hardware-Auswahl. Gutes Preis-Leistungs-Verhaeltnis garantiert.",
),
], ],
}, },
"netzwerk": { "netzwerk": {
"titel": "IT-Netzwerk fuer Pflegeeinrichtungen", "titel": "IT-Netzwerk fuer Pflegeeinrichtungen",
"cards": [ "cards": [
( ("WLAN-Ausleuchtung", "Professionelle WLAN-Planung und -Implementierung fuer komplette Einrichtungen. Lueckenlose Abdeckung."),
"WLAN-Ausleuchtung", ("VPN und Remote-Zugang", "Verschluesselte Verbindung fuer mobiles Arbeiten. Sichere Fernwartung und Fernadministration."),
"Professionelle WLAN-Planung und -Implementierung fuer komplette Einrichtungen. Lueckenlose Abdeckung.", ("Firewall und Segmentierung", "Zwei-Wege-Firewall mit medizinischen Geraete-Portabilitaet. Trennung von Betriebs- und Pflegedaten."),
), ("Netzwerkmonitoring", "24/7 Ueberwachung aller Netzwerk-Komponenten. Proaktive Stoerungserkennung und automatische Eskalation."),
(
"VPN und Remote-Zugang",
"Verschluesselte Verbindung fuer mobiles Arbeiten. Sichere Fernwartung und Fernadministration.",
),
(
"Firewall und Segmentierung",
"Zwei-Wege-Firewall mit medizinischen Geraete-Portabilitaet. Trennung von Betriebs- und Pflegedaten.",
),
(
"Netzwerkmonitoring",
"24/7 Ueberwachung aller Netzwerk-Komponenten. Proaktive Stoerungserkennung und automatische Eskalation.",
),
], ],
}, },
"email-kommunikation": { "email-kommunikation": {
"titel": "E-Mail und Kommunikation", "titel": "E-Mail und Kommunikation",
"cards": [ "cards": [
( ("Microsoft 365", "Vollstaendige Implementierung und Verwaltung von Microsoft 365. Cloud-Hosting in Deutschland verfuegbar."),
"Microsoft 365", ("Exchange und VoIP", "E-Mail-Server mit Exchange-Integration und VoIP-Telefonie. Nahtlose Kommunikation aller Abteilungen."),
"Vollstaendige Implementierung und Verwaltung von Microsoft 365. Cloud-Hosting in Deutschland verfuegbar.", ("Spam-Schutz und Archivierung", "Mehrschichtiger Spam-Schutz und automatische Archivierung fuer Compliance und Aufbewahrungspflichten."),
),
(
"Exchange und VoIP",
"E-Mail-Server mit Exchange-Integration und VoIP-Telefonie. Nahtlose Kommunikation aller Abteilungen.",
),
(
"Spam-Schutz und Archivierung",
"Mehrschichtiger Spam-Schutz und automatische Archivierung fuer Compliance und Aufbewahrungspflichten.",
),
], ],
}, },
"support-wartung": { "support-wartung": {
"titel": "IT-Support und Wartung", "titel": "IT-Support und Wartung",
"cards": [ "cards": [
( ("SLA-Stufen", "Flexible Service-Level-Agreements: von Standard bis Mission-Critical. Garantierte Reaktionszeiten fuer alle Stoerungen."),
"SLA-Stufen", ("Reaktionszeiten", "Erste Antwort innerhalb von 30 Minuten. Vollstaendige Loesung innerhalb von 4 Stunden bei schwerwiegenden Problemen."),
"Flexible Service-Level-Agreements: von Standard bis Mission-Critical. Garantierte Reaktionszeiten fuer alle Stoerungen.", ("Fernwartung und Hosting", "Sichere Fernwartung mit Echtzeit-Desktop-Zugriff. DSGVO-konforme Hostung aller Systeme."),
), ("Monitoring und Wartung", "Automatisiertes Monitoring aller IT-Komponenten. Regelmaessige Sicherheitsupdates und Wartung in Einweisungszeiten."),
(
"Reaktionszeiten",
"Erste Antwort innerhalb von 30 Minuten. Vollstaendige Loesung innerhalb von 4 Stunden bei schwerwiegenden Problemen.",
),
(
"Fernwartung und Hosting",
"Sichere Fernwartung mit Echtzeit-Desktop-Zugriff. DSGVO-konforme Hostung aller Systeme.",
),
(
"Monitoring und Wartung",
"Automatisiertes Monitoring aller IT-Komponenten. Regelmassige Sicherheitsupdates und Wartung in Einweisungszeiten.",
),
], ],
}, },
} }
@@ -389,7 +327,7 @@ def _sidebar(routes: list[tuple[str, str, str]]) -> str:
for route, titel, _datei in routes: for route, titel, _datei in routes:
links.append('<a href="' + route + '" class="sidebar-link">' + titel + "</a>") links.append('<a href="' + route + '" class="sidebar-link">' + titel + "</a>")
return ( return (
"<div class='article-sidebar'><h4 class='sidebar-title'>Weitere Artikel</h4>" "<div class='article-sidebar'><div class='sidebar-title'>Weitere Artikel</div>"
+ "".join(links) + "".join(links)
+ "</div>" + "</div>"
) )
@@ -397,22 +335,64 @@ def _sidebar(routes: list[tuple[str, str, str]]) -> str:
# ── Helper: Karten bauen ───────────────────────────────── # ── Helper: Karten bauen ─────────────────────────────────
def _cards(titel: str, beschreibung: str) -> str: def _cards(titel: str, beschreibung: str) -> str:
"""Erstelle eine Service-Karte. """Erstelle eine Service-Karte."""
Args:
titel: Titel der Karte.
beschreibung: Beschreibungstext.
"""
return '<div class="card"><h3>' + titel + "</h3><p>" + beschreibung + "</p></div>" return '<div class="card"><h3>' + titel + "</h3><p>" + beschreibung + "</p></div>"
# ── Blog-Route (FIX: Nutzt _ARTIKEL-Mapping) ─────────────
@app.route("/blog/<path:slug>")
def blog(slug: str) -> str:
"""Blog-Artikel — nutzt _ARTIKEL-Mapping fuer korrekte Dateinamen."""
route_key = "/blog/" + slug
# Finde ALLE Artikel dieser Sektion
sektions_artikel = [(r, t, d) for r, t, d in _ARTIKEL if r == route_key]
if sektions_artikel:
# Ersten Artikel als Hauptinhalt laden
_, haupt_titel, datei = sektions_artikel[0]
pfad = ARTIKEL_DIR / datei
if pfad.exists():
inhalt = pfad.read_text(encoding="utf-8")
match = re.search(r"<body[^>]*>(.*?)</body>", inhalt, re.DOTALL | re.IGNORECASE)
inhalt = match.group(1) if match else inhalt
else:
inhalt = "<p>Artikeltext folgt in Kuerze.</p>"
else:
inhalt = "<p>Artikel nicht gefunden.</p>"
sektions_artikel = _ARTIKEL
haupt_titel = slug.replace("-", " ").title()
# Seitenleiste: alle Artikel dieser Sektion
sidebar = _sidebar(sektions_artikel)
content = (
"<div class='article-layout'><div class='article-content'><h1>"
+ haupt_titel
+ "</h1>"
+ inhalt
+ "</div>"
+ sidebar
+ "</div>"
)
return _base_template("Blog: " + haupt_titel, content, active="blog")
# ── Weitere Routen ─────────────────────────────────────── # ── Weitere Routen ───────────────────────────────────────
@app.route("/ueber-uns") @app.route("/ueber-uns")
def ueber_uns() -> str: def ueber_uns() -> str:
"""Ueber uns.""" """Ueber uns."""
content = '<div class="content"><h1>Ueber uns</h1><p>Daniel Krause ist IT-Berater im Gesundheitswesen. Werte: Transparenz, Schnelligkeit, DSGVO-Compliance.</p></div>' content = (
'<div class="content">'
"<h1>Ueber uns</h1>"
"<p>Daniel Krause ist IT-Berater im Gesundheitswesen mit langjaehriger Erfahrung in der Digitalisierung von Pflegeeinrichtungen.</p>"
"<h2>Unsere Werte</h2>"
'<div class="card-grid" style="max-width:100%;padding:0">'
'<div class="card"><h3>Transparenz</h3><p>Klare Kommunikation und faire Preise — keine versteckten Kosten.</p></div>'
'<div class="card"><h3>Schnelligkeit</h3><p>Reaktionszeit unter 30 Minuten — weil im Gesundheitswesen Zeit zaehlt.</p></div>'
'<div class="card"><h3>DSGVO-Compliance</h3><p>Hosting in Deutschland, Ende-zu-Ende-Verschluesselung und regelmaessige Audits.</p></div>'
"</div></div>"
)
return _base_template("Ueber uns", content) return _base_template("Ueber uns", content)
@@ -424,10 +404,11 @@ def kontakt() -> str:
"<h1>Kontakt</h1>" "<h1>Kontakt</h1>"
'<div style="display:grid;gap:2rem">' '<div style="display:grid;gap:2rem">'
'<div class="contact-info">' '<div class="contact-info">'
"<p>Wir beraten Sie gerne persoenlich. Sprechen Sie uns an!</p>" "<h3>Wir beraten Sie gerne persoenlich</h3>"
"<p><strong>E-Mail:</strong> info@it-hilfe-sofort.de</p>" "<p>Sprechen Sie uns an — wir finden die passende IT-Loesung fuer Ihre Einrichtung.</p>"
'<p style="margin-top:1rem"><strong>E-Mail:</strong> info@it-hilfe-sofort.de</p>'
"</div>" "</div>"
'<div style="border:1px solid #E5E7EB;border-radius:8px;overflow:hidden">' '<div style="border:1px solid #e8ecf1;border-radius:12px;overflow:hidden;box-shadow:0 4px 12px rgba(0,0,0,0.04)">'
'<iframe src="https://crm.companyhub.io/widget/form/iEDHWbU9iII32UkhXj2b" style="width:100%;height:946px;border:none" title="Kontaktformular"></iframe>' '<iframe src="https://crm.companyhub.io/widget/form/iEDHWbU9iII32UkhXj2b" style="width:100%;height:946px;border:none" title="Kontaktformular"></iframe>'
"</div>" "</div>"
"</div></div>" "</div></div>"
@@ -441,72 +422,23 @@ def faq() -> str:
fragen: list[tuple[str, str]] = [ fragen: list[tuple[str, str]] = [
("Wie schnell sind Sie vor Ort?", "Innerhalb von 24—48 Stunden."), ("Wie schnell sind Sie vor Ort?", "Innerhalb von 24—48 Stunden."),
("Was kostet der IT-Support?", "Ab 29 EUR/Monat pro Geraet."), ("Was kostet der IT-Support?", "Ab 29 EUR/Monat pro Geraet."),
( ("Uebernehmen Sie die komplette IT?", "Ja, von der Installation bis zum 24/7-Monitoring."),
"Uebernehmen Sie die komplette IT?", ("Wie stellen Sie DSGVO-Compliance sicher?", "Hosting in Deutschland, Verschluesselung, Zugangskontrollen und regelmaessige Audits."),
"Ja, von der Installation bis zum Monitoring.", ("Bieten Sie auch Schulungen an?", "Ja, fuer alle Mitarbeiter Ihrer Einrichtung — vor Ort oder remote."),
), ("Wie laeuft das Onboarding ab?", "Kostenlose Erstberatung, dann Umsetzung in 2—4 Wochen."),
( ("Koennen Sie auf bestehende Systeme aufsetzen?", "Ja, wir arbeiten mit allen gaengigen Pflegesoftwaren."),
"Wie stellen Sie DSGVO-Compliance sicher?", ("Gibt es einen Notfallservice?", "Ja, 24/7 Notfallservice fuer kritische Stoerungen."),
"Hosting in Deutschland, Verschlusselung, Zugangskontrollen.",
),
(
"Bieten Sie auch Schulungen an?",
"Ja, fuer alle Mitarbeiter Ihrer Einrichtung.",
),
(
"Wie laeuft der Onboarding ab?",
"Kostenlose Erstberatung, dann Umsetzung in 2—4 Wochen.",
),
(
"Koennen Sie auf bestehende Systeme aufsetzen?",
"Ja, wir arbeiten mit allen gaeingen Pflegesoftwaren.",
),
(
"Gibt es einen Notfallservice?",
"Ja, 24/7 Notfallservice fuer kritische Stoerungen.",
),
] ]
fragen_html_parts: list[str] = [] fragen_html_parts: list[str] = []
for f, t in fragen: for f, t in fragen:
fragen_html_parts.append( fragen_html_parts.append(
"<div class='faq-item'><summary>" "<details class='faq-item'><summary>" + f + "</summary><div class='answer'>" + t + "</div></details>"
+ f
+ "</summary><div class='answer'>"
+ t
+ "</div></div>"
) )
fragen_html = "".join(fragen_html_parts) fragen_html = "".join(fragen_html_parts)
content = "<div class='content'><h1>Haeufige Fragen</h1>" + fragen_html + "</div>" content = "<div class='content'><h1>Haeufige Fragen</h1><p style='color:#6b7280;margin-bottom:2rem'>Hier finden Sie Antworten auf die haeufigsten Fragen. Falls Ihre Frage nicht dabei ist — <a href='/kontakt' style='color:#20228a'>kontaktieren Sie uns direkt</a>.</p>" + fragen_html + "</div>"
return _base_template("FAQ", content) return _base_template("FAQ", content)
@app.route("/blog/<path:slug>")
def blog(slug: str) -> str:
"""Blog-Artikel."""
datei = "artikel-" + slug + ".html"
pfad = ARTIKEL_DIR / datei
if pfad.exists():
inhalt = pfad.read_text(encoding="utf-8")
# Extrahiere Body-Inhalt aus dem Artikel-HTML
match = re.search(r"<body[^>]*>(.*?)</body>", inhalt, re.DOTALL | re.IGNORECASE)
inhalt = match.group(1) if match else inhalt
else:
inhalt = "<p>Artikel nicht gefunden.</p>"
titel = slug.replace("-", " ").title()
content = (
"<div class='article-layout'><div class='article-content'>" + inhalt + "</div>"
)
# Seitenleiste: alle anderen Artikel derselben Sektion
sektion_routen = [
(r, t, d) for r, t, d in _ARTIKEL if r == "/blog/" + slug.replace("-", "")
]
if not sektion_routen:
sektion_routen = _ARTIKEL
sidebar = _sidebar(sektion_routen)
content += sidebar + "</div>"
return _base_template("Blog: " + titel, content, active="blog")
@app.route("/leistungen/<path:slug>") @app.route("/leistungen/<path:slug>")
def leistungen(slug: str) -> str: def leistungen(slug: str) -> str:
"""Leistungs-Seite.""" """Leistungs-Seite."""
@@ -515,14 +447,8 @@ def leistungen(slug: str) -> str:
content = "<div class='content'><h1>Leistung nicht gefunden</h1></div>" content = "<div class='content'><h1>Leistung nicht gefunden</h1></div>"
return _base_template("Leistungen", content) return _base_template("Leistungen", content)
cards_items = [_cards(titel, beschreibung) for titel, beschreibung in data["cards"]] cards_items = [_cards(titel, beschreibung) for titel, beschreibung in data["cards"]]
cards_all = ( cards_all = '<div class="card-grid" style="margin-top:2rem">' + "".join(cards_items) + "</div>"
'<div class="card-grid" style="margin-top:2rem">' content = "<div class='content'><h1>" + data["titel"] + "</h1><p style='color:#6b7280;margin-bottom:1rem'>Massgeschneiderte IT-Loesungen fuer Ihre Einrichtung.</p>" + cards_all + "</div>"
+ "".join(cards_items)
+ "</div>"
)
content = (
"<div class='content'><h1>" + data["titel"] + "</h1>" + cards_all + "</div>"
)
return _base_template(data["titel"], content, active="leistungen") return _base_template(data["titel"], content, active="leistungen")
@@ -534,14 +460,8 @@ def it_service(slug: str) -> str:
content = "<div class='content'><h1>Service nicht gefunden</h1></div>" content = "<div class='content'><h1>Service nicht gefunden</h1></div>"
return _base_template("IT-Service", content) return _base_template("IT-Service", content)
cards_items = [_cards(titel, beschreibung) for titel, beschreibung in data["cards"]] cards_items = [_cards(titel, beschreibung) for titel, beschreibung in data["cards"]]
cards_all = ( cards_all = '<div class="card-grid card-grid-4" style="margin-top:2rem">' + "".join(cards_items) + "</div>"
'<div class="card-grid" style="margin-top:2rem">' content = "<div class='content' style='max-width:1100px'><h1>" + data["titel"] + "</h1><p style='color:#6b7280;margin-bottom:1rem'>Professionelle IT-Infrastruktur aus einer Hand.</p>" + cards_all + "</div>"
+ "".join(cards_items)
+ "</div>"
)
content = (
"<div class='content'><h1>" + data["titel"] + "</h1>" + cards_all + "</div>"
)
return _base_template(data["titel"], content, active="it-service") return _base_template(data["titel"], content, active="it-service")