Compare commits

...

2 Commits

Author SHA1 Message Date
3f47532e2d impressum und Texte 2026-04-09 16:37:36 +02:00
ea4e5d9296 home schön gemacht 2026-04-06 22:10:52 +02:00
10 changed files with 640 additions and 91 deletions

View File

@ -30,6 +30,7 @@ from security import (
contentmanager_required
)
from logging_config import setup_logging
from datetime import datetime
app = Flask(__name__)
@ -284,7 +285,13 @@ def startup_checks():
@app.route("/")
@app.route("/home")
def home():
return render_page("home", "Home")
return render_template(
"index.html",
page_title="Info",
active_page="home",
vat_label=COUNTRY_VAT_LABELS.get(session.get("country", "DE")),
**get_current_user()
)
@app.route("/preise")
@ -304,13 +311,10 @@ def allgemein():
"allgemein.html",
page_title="Info",
active_page="allgemein",
vat_label=COUNTRY_VAT_LABELS.get(session.get("country", "DE")),
**get_current_user()
)
# @app.route("/allgemein")
# @login_required
# def allgemein():
# return render_page("allgemein", "Allgemein")
@app.route("/login", methods=["GET", "POST"])
@ -494,6 +498,7 @@ def admin_mandanten():
name = request.form.get("name", "").strip()
kontakt_email = request.form.get("kontakt_email", "").strip()
level = request.form.get("level", "0").strip()
selected_groups = request.form.getlist("groups")
admin_name = request.form.get("admin_name", "").strip()
admin_email = request.form.get("admin_email", "").strip().lower()
@ -527,6 +532,8 @@ def admin_mandanten():
error_message = "Die beiden Admin-Passwörter stimmen nicht überein."
elif len(admin_password) < 8:
error_message = "Das Admin Passwort muss mindestens 8 Zeichen lang sein."
elif not selected_groups:
error_message = "Mindestens die Admin Gruppe muss ausgewählt werden."
if error_message:
cur.execute("""
@ -557,6 +564,7 @@ def admin_mandanten():
"level": level,
"admin_name": admin_name,
"admin_email": admin_email,
"groups": selected_groups,
}
**get_current_user()
@ -580,19 +588,16 @@ def admin_mandanten():
new_mandant_id = cur.fetchone()[0]
# Standardgruppen für den neuen Mandanten
cur.execute("""
INSERT INTO app_group (mandant_id, group_name)
VALUES (%s, %s)
RETURNING id
""", (new_mandant_id, "Useradministration"))
useradmin_group_id = cur.fetchone()[0]
group_ids = {}
cur.execute("""
INSERT INTO app_group (mandant_id, group_name)
VALUES (%s, %s)
RETURNING id
""", (new_mandant_id, "Contentmanager"))
contentmanager_group_id = cur.fetchone()[0]
for group_name in selected_groups:
cur.execute("""
INSERT INTO app_group (mandant_id, group_name)
VALUES (%s, %s)
RETURNING id
""", (new_mandant_id, group_name))
group_ids[group_name] = cur.fetchone()[0]
# erster Admin-User
admin_password_hash = generate_password_hash(admin_password)
@ -604,16 +609,12 @@ def admin_mandanten():
""", (admin_email, admin_name, new_mandant_id, admin_password_hash, 1))
new_admin_user_id = cur.fetchone()[0]
# User beiden Gruppen zuordnen
cur.execute("""
INSERT INTO user_group (user_id, group_id, mandant_id)
VALUES (%s, %s, %s)
""", (new_admin_user_id, useradmin_group_id, new_mandant_id))
cur.execute("""
INSERT INTO user_group (user_id, group_id, mandant_id)
VALUES (%s, %s, %s)
""", (new_admin_user_id, contentmanager_group_id, new_mandant_id))
# User den gewählten Gruppen zuordnen
for group_id in group_ids.values():
cur.execute("""
INSERT INTO user_group (user_id, group_id, mandant_id)
VALUES (%s, %s, %s)
""", (new_admin_user_id, group_id, new_mandant_id))
conn.commit()
@ -1603,3 +1604,11 @@ def admin_courses():
form_values=form_values,
**get_current_user()
)
@app.route("/impressum")
def impressum():
return render_template("impressum.html", **get_current_user())
@app.route("/datenschutz")
def datenschutz():
return render_template("datenschutz.html", **get_current_user())

View File

@ -1,5 +1,6 @@
from functools import wraps
from flask import session, redirect, url_for, request, abort
from datetime import datetime
from db import get_connection
@ -71,6 +72,7 @@ def get_current_user():
"is_user_admin": user_is_user_admin() if session.get("user_id") else False,
"is_contentmanager": user_is_contentmanager() if session.get("user_id") else False,
"country": country,
"current_year": datetime.now().year,
}

View File

@ -77,5 +77,21 @@
</section>
</main>
<footer class="site-footer">
<div class="footer-inner">
<div class="footer-left">
<strong>Compliance Verification</strong><br>
© {{ current_year }} Alle Rechte vorbehalten
</div>
<div class="footer-links">
<a href="/impressum">Impressum</a>
<a href="/datenschutz">Datenschutz</a>
</div>
</div>
</footer>
</body>
</html>

View File

@ -0,0 +1,164 @@
{% extends "base.html" %}
{% block content %}
<div class="content-box">
<h1>Impressum & Datenschutz</h1>
<div class="two-col responsive-stack">
<!-- LINK: IMPRESSUM -->
<div class="legal-section">
<h2>Impressum</h2>
<p>
<strong>Rechtsanwaltskanzlei Neubauer Neubauer Legal</strong><br>
Dr. Anja Maria Neubauer, Rechtsanwältin<br>
Meisenweg 3<br>
51149 Köln<br>
Deutschland
</p>
<p>
E-Mail: <a href="mailto:kanzlei@neubauer.legal">kanzlei@neubauer.legal</a><br>
Telefon: 0800 888 2 880
</p>
<p>
Umsatzsteuer-Identifikationsnummer: DE227436885
</p>
<p>
<strong>Aufsichtsbehörde und Anwaltskammer:</strong><br>
Rechtsanwaltskammer Köln<br>
Körperschaft des öffentlichen Rechts<br>
Riehler Str. 30, 50668 Köln<br>
Telefon: +49 (0)221 973010-0<br>
Fax: +49 (0)221 973010-50<br>
E-Mail: kontakt@rak-koeln.de
</p>
<p>
<strong>Berufsbezeichnung und berufsrechtliche Vorschriften:</strong><br>
Die Berufsbezeichnung „Rechtsanwältin” wurde in der Bundesrepublik Deutschland verliehen.
</p>
<ul>
<li>Bundesrechtsanwaltsordnung (BRAO)</li>
<li>Berufsordnung für Rechtsanwälte (BORA)</li>
<li>Rechtsanwaltsvergütungsgesetz (RVG)</li>
<li>Fachanwaltsordnung (FAO)</li>
<li>CCBE-Verhaltenskodex für europäische Rechtsanwälte</li>
<li>Gesetz über die Tätigkeit europäischer Rechtsanwälte in Deutschland (EuRAG)</li>
</ul>
<p>
Diese Vorschriften sind unter <a href="https://www.brak.de" target="_blank">www.brak.de</a> unter „Berufsrecht” verfügbar.
</p>
<p>
<strong>Berufshaftpflichtversicherung:</strong><br>
mailo Versicherung AG<br>
Riehler Straße 1<br>
50668 Köln, Deutschland
</p>
<p>
<strong>Gebietsabdeckung:</strong><br>
Weltweit
</p>
<p>
<strong>Online-Streitbeilegung (ODR):</strong><br>
<a href="https://ec.europa.eu/consumers/odr" target="_blank">
https://ec.europa.eu/consumers/odr
</a>
</p>
<p>
<strong>Hinweis gemäß § 36 VSBG:</strong><br>
Die Kanzlei Neubauer nimmt nicht an Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle teil und ist auch nicht dazu verpflichtet.
</p>
</div>
<!-- RECHTS: DATENSCHUTZ -->
<div class="legal-section">
<h2>Datenschutzerklärung</h2>
<p>
<strong>Verantwortlicher</strong><br>
Neubauer Legal<br>
Dr. Anja Maria Neubauer, Rechtsanwältin<br>
Meisenweg 3<br>
51149 Köln, Deutschland<br>
E-Mail: kanzlei@neubauer.legal
</p>
<p>
<strong>Allgemeine Informationen zur Datenverarbeitung</strong><br>
Personenbezogene Daten werden nur in dem Umfang verarbeitet, wie es für die Funktionalität dieser Website und die angebotenen Dienste erforderlich ist.
</p>
<p>
<strong>Websitezugriff und Logfiles</strong><br>
Beim Zugriff auf diese Website werden automatisch Daten wie Browsertyp, IP-Adresse, Datum und Uhrzeit des Zugriffs erfasst.
</p>
<p>
Diese Daten werden nicht mit anderen personenbezogenen Daten verknüpft und innerhalb von 7 Tagen anonymisiert.
</p>
<p>
<strong>Cookies</strong><br>
Diese Website verwendet keine Tracking-Cookies. Es werden nur technisch notwendige Cookies eingesetzt.
</p>
<p>
<strong>Rechte der betroffenen Personen</strong>
</p>
<ul>
<li>Recht auf Auskunft (Art. 15 DSGVO)</li>
<li>Recht auf Berichtigung (Art. 16 DSGVO)</li>
<li>Recht auf Löschung (Art. 17 DSGVO)</li>
<li>Recht auf Einschränkung (Art. 18 DSGVO)</li>
<li>Widerspruchsrecht (Art. 21 DSGVO)</li>
<li>Recht auf Datenübertragbarkeit (Art. 20 DSGVO)</li>
</ul>
<p>
<strong>Kontakt per E-Mail</strong><br>
Daten werden ausschließlich zur Bearbeitung der Anfrage verwendet und anschließend gelöscht.
</p>
<p>
<strong>Datenschutzbeauftragte</strong><br>
Rechtsanwältin Anja Neubauer<br>
E-Mail: kanzlei@neubauer.legal
</p>
<p>
<strong>Keine Weitergabe an Dritte</strong><br>
Es erfolgt keine Weitergabe personenbezogener Daten an Dritte, außer wenn gesetzlich erforderlich.
</p>
<p>
<strong>Änderungen</strong><br>
Diese Datenschutzerklärung kann angepasst werden. Es gilt die jeweils aktuelle Version.
</p>
</div>
</div>
<div class="legal-footer">
© 2026 NEUBAUER legal
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,164 @@
{% extends "base.html" %}
{% block content %}
<div class="content-box">
<h1>Impressum & Datenschutz</h1>
<div class="two-col responsive-stack">
<!-- LINK: IMPRESSUM -->
<div class="legal-section">
<h2>Impressum</h2>
<p>
<strong>Rechtsanwaltskanzlei Neubauer Neubauer Legal</strong><br>
Dr. Anja Maria Neubauer, Rechtsanwältin<br>
Meisenweg 3<br>
51149 Köln<br>
Deutschland
</p>
<p>
E-Mail: <a href="mailto:kanzlei@neubauer.legal">kanzlei@neubauer.legal</a><br>
Telefon: 0800 888 2 880
</p>
<p>
Umsatzsteuer-Identifikationsnummer: DE227436885
</p>
<p>
<strong>Aufsichtsbehörde und Anwaltskammer:</strong><br>
Rechtsanwaltskammer Köln<br>
Körperschaft des öffentlichen Rechts<br>
Riehler Str. 30, 50668 Köln<br>
Telefon: +49 (0)221 973010-0<br>
Fax: +49 (0)221 973010-50<br>
E-Mail: kontakt@rak-koeln.de
</p>
<p>
<strong>Berufsbezeichnung und berufsrechtliche Vorschriften:</strong><br>
Die Berufsbezeichnung „Rechtsanwältin” wurde in der Bundesrepublik Deutschland verliehen.
</p>
<ul>
<li>Bundesrechtsanwaltsordnung (BRAO)</li>
<li>Berufsordnung für Rechtsanwälte (BORA)</li>
<li>Rechtsanwaltsvergütungsgesetz (RVG)</li>
<li>Fachanwaltsordnung (FAO)</li>
<li>CCBE-Verhaltenskodex für europäische Rechtsanwälte</li>
<li>Gesetz über die Tätigkeit europäischer Rechtsanwälte in Deutschland (EuRAG)</li>
</ul>
<p>
Diese Vorschriften sind unter <a href="https://www.brak.de" target="_blank">www.brak.de</a> unter „Berufsrecht” verfügbar.
</p>
<p>
<strong>Berufshaftpflichtversicherung:</strong><br>
mailo Versicherung AG<br>
Riehler Straße 1<br>
50668 Köln, Deutschland
</p>
<p>
<strong>Gebietsabdeckung:</strong><br>
Weltweit
</p>
<p>
<strong>Online-Streitbeilegung (ODR):</strong><br>
<a href="https://ec.europa.eu/consumers/odr" target="_blank">
https://ec.europa.eu/consumers/odr
</a>
</p>
<p>
<strong>Hinweis gemäß § 36 VSBG:</strong><br>
Die Kanzlei Neubauer nimmt nicht an Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle teil und ist auch nicht dazu verpflichtet.
</p>
</div>
<!-- RECHTS: DATENSCHUTZ -->
<div class="legal-section">
<h2>Datenschutzerklärung</h2>
<p>
<strong>Verantwortlicher</strong><br>
Neubauer Legal<br>
Dr. Anja Maria Neubauer, Rechtsanwältin<br>
Meisenweg 3<br>
51149 Köln, Deutschland<br>
E-Mail: kanzlei@neubauer.legal
</p>
<p>
<strong>Allgemeine Informationen zur Datenverarbeitung</strong><br>
Personenbezogene Daten werden nur in dem Umfang verarbeitet, wie es für die Funktionalität dieser Website und die angebotenen Dienste erforderlich ist.
</p>
<p>
<strong>Websitezugriff und Logfiles</strong><br>
Beim Zugriff auf diese Website werden automatisch Daten wie Browsertyp, IP-Adresse, Datum und Uhrzeit des Zugriffs erfasst.
</p>
<p>
Diese Daten werden nicht mit anderen personenbezogenen Daten verknüpft und innerhalb von 7 Tagen anonymisiert.
</p>
<p>
<strong>Cookies</strong><br>
Diese Website verwendet keine Tracking-Cookies. Es werden nur technisch notwendige Cookies eingesetzt.
</p>
<p>
<strong>Rechte der betroffenen Personen</strong>
</p>
<ul>
<li>Recht auf Auskunft (Art. 15 DSGVO)</li>
<li>Recht auf Berichtigung (Art. 16 DSGVO)</li>
<li>Recht auf Löschung (Art. 17 DSGVO)</li>
<li>Recht auf Einschränkung (Art. 18 DSGVO)</li>
<li>Widerspruchsrecht (Art. 21 DSGVO)</li>
<li>Recht auf Datenübertragbarkeit (Art. 20 DSGVO)</li>
</ul>
<p>
<strong>Kontakt per E-Mail</strong><br>
Daten werden ausschließlich zur Bearbeitung der Anfrage verwendet und anschließend gelöscht.
</p>
<p>
<strong>Datenschutzbeauftragte</strong><br>
Rechtsanwältin Anja Neubauer<br>
E-Mail: kanzlei@neubauer.legal
</p>
<p>
<strong>Keine Weitergabe an Dritte</strong><br>
Es erfolgt keine Weitergabe personenbezogener Daten an Dritte, außer wenn gesetzlich erforderlich.
</p>
<p>
<strong>Änderungen</strong><br>
Diese Datenschutzerklärung kann angepasst werden. Es gilt die jeweils aktuelle Version.
</p>
</div>
</div>
<div class="legal-footer">
© 2026 NEUBAUER legal
</div>
</div>
{% endblock %}

View File

@ -1,53 +1,5 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ page_title }}</title>
<link rel="stylesheet" href="/styles/site.css">
</head>
<body>
<header class="site-header">
<div class="header-inner">
<div class="logo-area">
<a href="/home">
<img src="/images/Logo-Compliance-Verification-bg-1.png" alt="Logo" class="site-logo">
</a>
</div>
{% extends "base.html" %}
<nav class="top-nav">
<a href="/home" class="{% if active_page == 'home' %}active{% endif %}">Home</a>
<a href="/preise" class="{% if active_page == 'preise' %}active{% endif %}">Preise</a>
<a href="/allgemein" class="{% if active_page == 'allgemein' %}active{% endif %}">Allgemein</a>
{% if is_logged_in %}
<div class="user-menu">
<button class="user-menu-toggle" type="button">{{ user_name }} ▾</button>
<div class="user-menu-dropdown">
<a href="/profil">Profil</a>
{% if is_admin %}
<a href="/admin/mandanten">Admin</a>
{% endif %}
<a href="/logout">Logout</a>
</div>
</div>
{% else %}
<a href="/login" class="{% if active_page == 'login' %}active{% endif %}">Login</a>
{% endif %}
</nav>
</div>
</header>
<main class="content-area">
<section class="content-box">
{% if active_page == "home" %}
{% include "partials/home_content.html" %}
{% elif active_page == "preise" %}
{% include "partials/preise_content.html" %}
{% elif active_page == "allgemein" %}
{% include "partials/allgemein_content.html" %}
{% endif %}
</section>
</main>
</body>
</html>
{% block content %}
{% include "partials/home_content.html" %}
{% endblock %}

View File

@ -1,6 +1,112 @@
<div class="hero-box">
<h1>Compliance Verification</h1>
<p>Willkommen auf Ihrem Portal zur geregelten Nutzung von KI in Ihrem Unternehmen.</p>
<p>Besuche insgesamt: {{ visit_count }}</p>
<p>Für die Seiten <strong>Preise</strong> und <strong>Allgemein</strong> ist eine Anmeldung erforderlich.</p>
</div>
<!-- HERO -->
<section class="hero-box">
<h1>Compliance. Digitalisierung. Zukunftssicher.</h1>
<p>
Wir unterstützen Unternehmen dabei, regulatorische Anforderungen effizient umzusetzen
und gleichzeitig die Chancen moderner Technologien optimal zu nutzen.
</p>
<div class="hero-actions">
<a href="/preise" class="btn-primary">Leistungen ansehen</a>
<a href="/kontakt" class="btn-secondary">Kontakt aufnehmen</a>
</div>
</section>
<!-- LEISTUNGEN -->
<section class="section">
<h2>Unsere Leistungen</h2>
<div class="card-grid">
<div class="card">
<h3>Compliance & Regulierung</h3>
<p>
Umsetzung gesetzlicher Anforderungen wie EU AI Act, Datenschutz und Governance.
</p>
</div>
<div class="card">
<h3>KI & Digitalisierung</h3>
<p>
Strukturierte Einführung von KI-Systemen mit klaren Prozessen und Richtlinien.
</p>
</div>
<div class="card">
<h3>Schulung</h3>
<p>
Aufbau von Wissen im Unternehmen mit Trainings und nachvollziehbarer Dokumentation.
</p>
</div>
</div>
</section>
<!-- WARUM -->
<section class="section alt">
<div class="two-col">
<div>
<h2>Warum Compliance Verification?</h2>
<ul class="check-list">
<li>Rechtssicherheit für Ihr Unternehmen</li>
<li>Klare Dokumentation und Nachweise</li>
<li>Strukturierte Umsetzung von KI-Richtlinien</li>
<li>Effiziente Schulung Ihrer Mitarbeitenden</li>
</ul>
</div>
<div class="image-panel">
<img src="/images/schulung.png" alt="Schulung">
</div>
</div>
</section>
<!-- MODULE -->
<section class="section">
<h2>Unsere Module</h2>
<div class="card-grid">
<div class="card">
<h3>Modul A Basis</h3>
<p>Grundlagen für alle Mitarbeitenden zur sicheren Nutzung von KI.</p>
</div>
<div class="card">
<h3>Modul B Compliance</h3>
<p>Erweiterte Anforderungen für Fachbereiche und Organisation.</p>
</div>
<div class="card">
<h3>Modul C Governance</h3>
<p>Ganzheitliche Steuerung und Absicherung im Unternehmen.</p>
</div>
</div>
</section>
<!-- ABLAUF -->
<section class="section alt">
<h2>So funktioniert es</h2>
<div class="steps-grid">
<div class="step-card">
<h3>Analyse</h3>
<p>Wir analysieren Ihre aktuelle Situation und Anforderungen.</p>
</div>
<div class="step-card">
<h3>Umsetzung</h3>
<p>Einführung von Prozessen, Richtlinien und Schulungen.</p>
</div>
<div class="step-card">
<h3>Verifikation</h3>
<p>Nachweisbare Compliance und nachhaltige Absicherung.</p>
</div>
</div>
</section>
<!-- CTA -->
<section class="section cta-section">
<h2>Bereit für den nächsten Schritt?</h2>
<p>
Starten Sie jetzt mit der strukturierten Umsetzung Ihrer KI-Compliance.
</p>
<a href="/preise" class="btn-primary">Jetzt starten</a>
</section>

View File

@ -0,0 +1,6 @@
<div class="hero-box">
<h1>Compliance Verification</h1>
<p>Willkommen auf Ihrem Portal zur geregelten Nutzung von KI in Ihrem Unternehmen.</p>
<p>Besuche insgesamt: {{ visit_count }}</p>
<p>Für die Seiten <strong>Preise</strong> und <strong>Allgemein</strong> ist eine Anmeldung erforderlich.</p>
</div>

View File

@ -4,20 +4,25 @@ set -euo pipefail
SRC_ROOT="/Volumes/MacBook SD/Projekte/compliance-verification/app/flask-postgres"
DST_ROOT="/Volumes/docker/flask-postgres"
NAS_USER="BKolb"
NAS_HOST="192.168.0.10"
CONTAINER_NAME="flask_web"
echo "Starte Deployment..."
echo "Starte Deployment für compliance-verification ..."
# Quellen prüfen
[ -d "$SRC_ROOT/app" ] || { echo "Quelle app fehlt: $SRC_ROOT/app"; exit 1; }
[ -d "$SRC_ROOT/images" ] || { echo "Quelle images fehlt: $SRC_ROOT/images"; exit 1; }
[ -d "$SRC_ROOT/styles" ] || { echo "Quelle styles fehlt: $SRC_ROOT/styles"; exit 1; }
# Ziele prüfen
[ -d "$DST_ROOT/app" ] || { echo "Ziel app fehlt: $DST_ROOT/app"; exit 1; }
[ -d "$DST_ROOT/images" ] || { echo "Ziel images fehlt: $DST_ROOT/images"; exit 1; }
[ -d "$DST_ROOT/styles" ] || { echo "Ziel styles fehlt: $DST_ROOT/styles"; exit 1; }
# Schutz: andere Projekte / DB dürfen nicht angefasst werden
[ -d "$DST_ROOT/app-unternehmen" ] && echo "Schutz aktiv: $DST_ROOT/app-unternehmen wird nicht angerührt."
[ -d "$DST_ROOT/unternehmen" ] && echo "Schutz aktiv: $DST_ROOT/unternehmen wird nicht angerührt."
[ -d "$DST_ROOT/db" ] && echo "Schutz aktiv: $DST_ROOT/db wird nicht angerührt."
echo "Synchronisiere app/ ..."
rsync -av --delete \
--exclude '.DS_Store' \
@ -28,6 +33,9 @@ rsync -av --delete \
--exclude 'styles/' \
--exclude 'files/' \
--exclude 'Dockerfile.txt' \
--exclude 'app-unternehmen/' \
--exclude 'unternehmen/' \
--exclude 'db/' \
"$SRC_ROOT/app/" "$DST_ROOT/app/"
echo "Synchronisiere images/ ..."
@ -43,9 +51,13 @@ rsync -av --delete \
--exclude '._*' \
"$SRC_ROOT/styles/" "$DST_ROOT/styles/"
echo "files/ wird bewusst nicht angefasst."
echo "Diese Pfade werden bewusst NICHT angefasst:"
echo " - $DST_ROOT/files"
echo " - $DST_ROOT/app-unternehmen"
echo " - $DST_ROOT/unternehmen"
echo " - $DST_ROOT/db"
echo "Starte Container manuell neu ..."
#ssh "${NAS_USER}@${NAS_HOST}" "/usr/bin/docker restart ${CONTAINER_NAME}"
# docker restart "${CONTAINER_NAME}"
echo "Deployment abgeschlossen."

View File

@ -817,3 +817,121 @@ button {
.progress-green {
background: #2e7d32;
}
/* ===============================
Home
=============================== */
.section {
margin: 40px 0;
}
.section.alt {
background: #f6f8fb;
padding: 30px;
border-radius: 16px;
}
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 20px;
}
.card {
background: #fff;
padding: 20px;
border-radius: 14px;
border: 1px solid #e3e8ef;
}
.hero-actions {
margin-top: 20px;
display: flex;
gap: 12px;
}
.cta-section {
text-align: center;
}
.checkbox-group {
display: flex;
gap: 20px;
margin-top: 8px;
}
.checkbox-group label {
display: flex;
align-items: center;
gap: 6px;
font-weight: normal;
}
/* ===============================
Footer
=============================== */
.site-footer {
margin-top: 60px;
padding: 20px 30px;
background: #0f172a;
color: #cbd5e1;
font-size: 14px;
}
.footer-inner {
max-width: 1200px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
}
.footer-links {
display: flex;
gap: 20px;
}
.footer-links a {
color: #cbd5e1;
text-decoration: none;
}
.footer-links a:hover {
text-decoration: underline;
}
/* ===============================
Impressum / Legal
=============================== */
.legal-section {
padding: 10px 20px;
}
.legal-section h2 {
margin-top: 0;
}
.legal-section ul {
padding-left: 18px;
}
.legal-footer {
margin-top: 40px;
text-align: center;
font-size: 14px;
color: #64748b;
}
.responsive-stack {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 30px;
}
@media (max-width: 768px) {
.responsive-stack {
grid-template-columns: 1fr;
}
}