diff --git a/Dockerfile b/Dockerfile
index 0feb6fb..78eb1a9 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,10 +1,12 @@
FROM eclipse-temurin:21-jre
+ARG JAR_FILE=target/*.jar
+
# Zeitzone auf Berlin setzen und 24h-Format konfigurieren
ENV TZ=Europe/Berlin
ENV LC_TIME=de_DE.UTF-8
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
-COPY target/*.jar app.jar
+COPY ${JAR_FILE} app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar", "--spring.profiles.active=production"]
diff --git a/UI_GUIDELINES.md b/STYLEGUIDE.md
similarity index 90%
rename from UI_GUIDELINES.md
rename to STYLEGUIDE.md
index 1111591..1481a33 100644
--- a/UI_GUIDELINES.md
+++ b/STYLEGUIDE.md
@@ -2,7 +2,7 @@
> Gilt für alle Views unter `src/main/java/de/assecutor/votianlt/pages/view/`
> Theme-Datei: `src/main/frontend/themes/votian-modern/styles.css`
-> Stand: UI-Änderungen bis 20.03.2026 berücksichtigt (`ViewToolbar`-Migrationen, Landing-/Dashboard-Updates, TabSheet-Dialoge, Message-/Statistics-Layouts)
+> Stand: UI-Änderungen bis 23.03.2026 berücksichtigt (`Landing-Hero-CTA/Demo-Button`, `ViewToolbar`-Migrationen, Landing-/Dashboard-Updates, TabSheet-Dialoge, Message-/Statistics-Layouts)
---
@@ -950,6 +950,34 @@ body:has(.landing-view) {
}
```
+### Anonymer Header
+
+- Im anonymen Zustand enthält die Header-Navigation nur `Login`, `Registrieren` und die Sprachwahl.
+- Öffentliche Primäraktionen gehören nicht in die Header-Leiste, sondern in das Hero-Panel.
+- Der `Demo`-Einstieg wird deshalb nicht als zusätzlicher Header-Button geführt.
+
+```java
+private Component createAnonymousNavigation() {
+ HorizontalLayout navButtons = new HorizontalLayout();
+ navButtons.setSpacing(true);
+ navButtons.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
+ navButtons.addClassName("landing-nav");
+
+ Button loginBtn = new Button(getTranslation("start.button.login"), event -> login());
+ loginBtn.addThemeVariants(ButtonVariant.LUMO_CONTRAST);
+ loginBtn.addClassName("landing-nav-button");
+
+ Button registerBtn = new Button(getTranslation("start.button.register"), event -> register());
+ registerBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
+ registerBtn.addClassName("landing-nav-button");
+
+ Button languageBtn = createLanguageSelector();
+
+ navButtons.add(loginBtn, registerBtn, languageBtn);
+ return navButtons;
+}
+```
+
### App-Section vs. Footer
CTA-Text und Slogan liegen auf der Landing Page jetzt im `app-overview-panel`, nicht mehr im Footer.
@@ -973,7 +1001,7 @@ CTA-Text und Slogan liegen auf der Landing Page jetzt im `app-overview-panel`, n
.hero-panel {
position: relative;
overflow: hidden;
- min-height: 340px;
+ min-height: 520px;
justify-content: center;
text-align: center;
border-radius: 34px;
@@ -991,6 +1019,68 @@ CTA-Text und Slogan liegen auf der Landing Page jetzt im `app-overview-panel`, n
color: rgba(226,232,240,0.92);
font-size: clamp(1rem, 2vw, 1.15rem);
}
+.hero-actions {
+ gap: 1rem;
+ align-items: center;
+}
+.hero-action-group {
+ gap: 0.45rem;
+ align-items: center;
+}
+.hero-choice-hint {
+ max-width: 420px;
+ margin: 0;
+ color: rgba(226,232,240,0.82);
+ font-size: 0.95rem;
+ line-height: 1.55;
+ text-align: center;
+}
+```
+
+### Hero-CTA-Regeln
+
+- Öffentliche CTAs werden in der Hero-Kachel vertikal gestapelt und nicht über Header und Hero verteilt.
+- Reihenfolge auf der Landing Page: zuerst `Demo`, danach `Jetzt kostenlos testen`.
+- Beide CTA-Buttons verwenden dieselbe Primärdarstellung: `LUMO_PRIMARY`, `LUMO_LARGE`, `setWidthFull()`, Pill-Optik.
+- Jeder CTA bekommt direkt darunter einen eigenen Erklärungstext.
+- Keine kombinierten Erklärungstexte für mehrere Buttons verwenden; Hinweise immer 1:1 an den jeweiligen CTA binden.
+
+```java
+Button demoButton = new Button(getTranslation("start.button.demo"), event -> loginDemo());
+demoButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_LARGE);
+demoButton.addClassNames("hero-cta", "hero-demo-cta");
+demoButton.setWidthFull();
+
+Button ctaButton = new Button(getTranslation("cta.freetest"), event -> register());
+ctaButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_LARGE);
+ctaButton.addClassName("hero-cta");
+ctaButton.setWidthFull();
+
+Paragraph demoHint = new Paragraph(getTranslation("start.hero.demo.hint"));
+demoHint.addClassName("hero-choice-hint");
+
+Paragraph trialHint = new Paragraph(getTranslation("start.hero.trial.hint"));
+trialHint.addClassName("hero-choice-hint");
+
+VerticalLayout demoAction = new VerticalLayout(demoButton, demoHint);
+demoAction.addClassName("hero-action-group");
+
+VerticalLayout trialAction = new VerticalLayout(ctaButton, trialHint);
+trialAction.addClassName("hero-action-group");
+
+VerticalLayout heroActions = new VerticalLayout();
+heroActions.setPadding(false);
+heroActions.setSpacing(false);
+heroActions.setWidthFull();
+heroActions.setMaxWidth("420px");
+heroActions.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
+heroActions.addClassName("hero-actions");
+heroActions.add(demoAction, trialAction);
+```
+
+```properties
+start.hero.demo.hint=Demo startet sofort mit vorbereiteten Beispieldaten.
+start.hero.trial.hint="Jetzt kostenlos testen" erstellt Ihren eigenen Account für den kostenlosen Probemonat.
```
---
diff --git a/docker_push.sh b/docker_push.sh
index 1ab2a37..822c4a5 100755
--- a/docker_push.sh
+++ b/docker_push.sh
@@ -1,6 +1,79 @@
-# 1. Login (mit deinen Credentials)
-echo "G8m0T3vz" | docker login registry.assecutor.org -u adsg --password-stdin
+#!/usr/bin/env bash
-# Dann ganz normal pushen
-docker buildx build --platform linux/amd64 -t registry.assecutor.org/votianlt:0.9.9 --push .
+set -euo pipefail
+readonly SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
+readonly REGISTRY_IMAGE="registry.assecutor.org/votianlt"
+
+usage() {
+ cat <<'EOF'
+Verwendung:
+ ./docker_push.sh [x.y.z]
+
+Beispiel:
+ ./docker_push.sh 0.9.13
+ ./docker_push.sh
+
+Voraussetzungen:
+ - Docker Buildx ist installiert
+ - Login zur Registry wurde bereits ausgeführt:
+ docker login registry.assecutor.org
+
+Ohne Versionsargument wird automatisch die Version aus der pom.xml verwendet.
+EOF
+}
+
+fail() {
+ echo "Fehler: $*" >&2
+ exit 1
+}
+
+require_command() {
+ command -v "$1" >/dev/null 2>&1 || fail "'$1' wurde nicht gefunden."
+}
+
+resolve_pom_version() {
+ [[ -x "./mvnw" ]] || fail "'./mvnw' wurde nicht gefunden oder ist nicht ausführbar."
+
+ local version
+ version="$(
+ ./mvnw -q -DforceStdout help:evaluate -Dexpression=project.version \
+ | awk 'NF { last = $0 } END { print last }'
+ )"
+
+ [[ -n "${version}" ]] || fail "Version konnte nicht aus der pom.xml ermittelt werden."
+ echo "${version}"
+}
+
+VERSION="${1:-$(resolve_pom_version)}"
+
+if [[ "${VERSION}" == "-h" || "${VERSION}" == "--help" ]]; then
+ usage
+ exit 0
+fi
+
+if [[ ! "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+ fail "Versionsnummer muss das Format x.y.z haben."
+fi
+
+require_command docker
+docker buildx version >/dev/null 2>&1 || fail "Docker Buildx ist nicht verfügbar."
+
+cd "${SCRIPT_DIR}"
+
+echo "Verwende Release-Version ${VERSION}."
+echo "Baue Production-JAR für Version ${VERSION} ..."
+./mvnw -Pproduction -DskipTests -Drevision="${VERSION}" package
+
+JAR_FILE="target/votianlt-${VERSION}.jar"
+[[ -f "${JAR_FILE}" ]] || fail "Release-JAR wurde nicht gefunden: ${JAR_FILE}"
+
+echo "Pushe Image ${REGISTRY_IMAGE}:${VERSION} ..."
+docker buildx build \
+ --platform linux/amd64 \
+ --build-arg "JAR_FILE=${JAR_FILE}" \
+ -t "${REGISTRY_IMAGE}:${VERSION}" \
+ --push \
+ .
+
+echo "Fertig: ${REGISTRY_IMAGE}:${VERSION}"
diff --git a/pom.xml b/pom.xml
index a18e51f..4dc4006 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,11 +6,12 @@
de.assecutor.votianlt
votianlt
- 0.9.12
+ ${revision}
jar
+ 0.9.12
21
21
21
diff --git a/src/main/bundles/prod.bundle b/src/main/bundles/prod.bundle
index 6230507..76ac393 100644
Binary files a/src/main/bundles/prod.bundle and b/src/main/bundles/prod.bundle differ
diff --git a/src/main/frontend/themes/votian-modern/styles.css b/src/main/frontend/themes/votian-modern/styles.css
index 7cdc5b7..912891a 100644
--- a/src/main/frontend/themes/votian-modern/styles.css
+++ b/src/main/frontend/themes/votian-modern/styles.css
@@ -361,7 +361,7 @@ vaadin-vertical-layout.admin-form-view {
.hero-panel {
position: relative;
overflow: hidden;
- min-height: 420px;
+ min-height: 520px;
justify-content: center;
text-align: center;
border-radius: 34px;