Kaynağa Gözat

Allgemeine Verbesserungen

Leonard Schulz 1 yıl önce
ebeveyn
işleme
6595555184

+ 1 - 1
src/main/java/com/tsystems/inwo/vgl/view/FreibetragZeile.java

@@ -1,4 +1,4 @@
 package com.tsystems.inwo.vgl.view;
 
 public record FreibetragZeile(String symbol, String symbolClass, String rowClass,
-                              String artHtml, String frequenzHtml, String betragHtml, String fristHtml) {}
+                              String typ, String artHtml, String frequenzHtml, String betragHtml, String fristHtml) {}

+ 18 - 2
src/main/java/com/tsystems/inwo/vgl/web/VergleichController.java

@@ -23,9 +23,17 @@ public class VergleichController {
     }
 
     @GetMapping("/test")
-    public String test(Model model) {
+    public String test(
+            @RequestParam(required = false) String kunde,
+            @RequestHeader(value = "X-Kunde", required = false) String kundeHeader,
+            Model model) {
+        
+        // Kunde aus Parameter oder Header ermitteln
+        String activeKunde = kunde != null ? kunde : kundeHeader;
+        
         model.addAttribute("validate", true);
         model.addAttribute("schemaName", "haushalt-einkommen");
+        model.addAttribute("activeKunde", activeKunde);
         return "test";
     }
 
@@ -39,8 +47,13 @@ public class VergleichController {
             @RequestParam(required = false) String leftZeitraumId,
             @RequestParam(required = false) String rightZeitraumId,
             @RequestParam(required = false) String personId,
+            @RequestParam(required = false) String kunde,
+            @RequestHeader(value = "X-Kunde", required = false) String kundeHeader,
             Model model) throws Exception {
 
+        // Kunde aus Parameter oder Header ermitteln
+        String activeKunde = kunde != null ? kunde : kundeHeader;
+
         JsonNode left = om.readTree(leftJson);
         JsonNode right = om.readTree(rightJson);
 
@@ -130,6 +143,7 @@ public class VergleichController {
         model.addAttribute("einkommenZeilen", einkommenZeilen);
         model.addAttribute("freibetragZeilen", freibetragZeilen);
         model.addAttribute("kitaZeilen", kitaZeilen);
+        model.addAttribute("activeKunde", activeKunde);
         return "vergleich";
     }
 
@@ -751,9 +765,11 @@ public class VergleichController {
                         return !fristDatum.isEmpty() ? FormatUtil.date(fristDatum) : "";
                     }, hasChanges);
                 
+                String typ = primaryItem.path("typ").asText("F"); // Default to "F" if not specified
+                
                 result.add(new FreibetragZeile(
                     symbol, symbolClass, rowClass,
-                    artHtml, frequenzHtml, betragHtml, fristHtml
+                    typ, artHtml, frequenzHtml, betragHtml, fristHtml
                 ));
             }
         }

+ 6 - 0
src/main/resources/schemas/haushalt-einkommen.schema.json

@@ -261,6 +261,11 @@
         "id": {
           "type": "string"
         },
+        "typ": {
+          "type": "string",
+          "enum": ["F", "P"],
+          "description": "F = Freibetrag, P = Pauschaler Abzug"
+        },
         "art": {
           "type": "string",
           "description": "z.B. Erwerbstätigenfreibetrag, Alleinerziehendenfreibetrag"
@@ -283,6 +288,7 @@
       },
       "required": [
         "id",
+        "typ",
         "art",
         "betrag"
       ]

+ 9 - 2
src/main/resources/static/example-alt.json

@@ -39,7 +39,11 @@
                   { "id": "E1", "art": "NS - Eink. aus nichtselbst. Tätigkeit", "frequenz": 12, "betrag": 800000, "zuschlag": 0, "abschlag": 0, "werbungskosten": 0 },
                   { "id": "E3", "art": "Nebenjob (Minijob)", "frequenz": 12, "betrag": 100000 }
                 ],
-                "freibetraege": []
+                "freibetraege": [
+                  { "id": "P1", "typ": "P", "art": "Krankenkasse", "frequenz": 12, "betrag": 80000 },
+                  { "id": "P2", "typ": "P", "art": "Steuern", "frequenz": 12, "betrag": 60000 },
+                  { "id": "P3", "typ": "P", "art": "Rentenversicherung", "frequenz": 12, "betrag": 45000 }
+                ]
               },
               {
                 "rolleImHaushalt": "Partnerin",
@@ -57,7 +61,10 @@
                   { "id": "E2", "art": "NS - Eink. aus nichtselbst. Tätigkeit", "frequenz": 12, "betrag": 600000 }
                 ],
                 "freibetraege": [
-                  { "id": "F1", "art": "Erwerbstätigenfreibetrag", "frequenz": 12, "betrag": 10000 }
+                  { "id": "F1", "typ": "F", "art": "Erwerbstätigenfreibetrag", "frequenz": 12, "betrag": 10000 },
+                  { "id": "P4", "typ": "P", "art": "Krankenkasse", "frequenz": 12, "betrag": 60000 },
+                  { "id": "P5", "typ": "P", "art": "Steuern", "frequenz": 12, "betrag": 45000 },
+                  { "id": "P6", "typ": "P", "art": "Rentenversicherung", "frequenz": 12, "betrag": 36000 }
                 ]
               }
             ]

+ 9 - 1
src/main/resources/static/example-neu.json

@@ -39,6 +39,11 @@
                 },
                 "einkommen": [
                   { "id": "E1", "art": "NS - Eink. aus nichtselbst. Tätigkeit", "frequenz": 12, "betrag": 1000000, "werbungskosten": 10000 }
+                ],
+                "freibetraege": [
+                  { "id": "P1", "typ": "P", "art": "Krankenkasse", "frequenz": 12, "betrag": 100000 },
+                  { "id": "P2", "typ": "P", "art": "Steuern", "frequenz": 12, "betrag": 75000 },
+                  { "id": "P3", "typ": "P", "art": "Rentenversicherung", "frequenz": 12, "betrag": 55000 }
                 ]
               },
               {
@@ -57,7 +62,10 @@
                   { "id": "E2", "art": "NS - Eink. aus nichtselbst. Tätigkeit", "frequenz": 12, "betrag": 620000, "werbungskosten": 10000 }
                 ],
                 "freibetraege": [
-                  { "id": "F1", "art": "Erwerbstätigenfreibetrag", "frequenz": 12, "betrag": 12000 }
+                  { "id": "F1", "typ": "F", "art": "Erwerbstätigenfreibetrag", "frequenz": 12, "betrag": 12000 },
+                  { "id": "P4", "typ": "P", "art": "Krankenkasse", "frequenz": 12, "betrag": 62000 },
+                  { "id": "P5", "typ": "P", "art": "Steuern", "frequenz": 12, "betrag": 47000 },
+                  { "id": "P6", "typ": "P", "art": "Rentenversicherung", "frequenz": 12, "betrag": 37000 }
                 ],
                 "kinderbetreuung": [
                   { "id": "K1", "kindRef": "P3", "betrag": 10000 }

BIN
src/main/resources/static/images/berlin.jpg


+ 718 - 0
src/main/resources/static/styles-accessible.css

@@ -0,0 +1,718 @@
+:root {
+  /* Standard Farben mit verbessertem Kontrast */
+  --rot-hell: #ffe6e6;
+  --gelb-hell: #fff8db;
+  --gruen-hell: #e8f5e8;
+  --rot: #b30000;      /* Verbessert von #800000 für besseren Kontrast */
+  --gelb: #9d5502;     /* Verbessert von #784801 für besseren Kontrast */
+  --gruen: #004d26;    /* Verbessert von #006633 für besseren Kontrast */
+  --weiss: #ffffff;
+  --schwarz: #000000;
+  --grau-hell: #f7f9fa;
+  --grau-stark: #d7d8d9;
+  --berlin-rot: #e20613;
+  --diwo-blau: #145c9e;
+  --diwo-blau-hell: #cfe8fc;
+  
+  /* Focus Ring Farbe für bessere Sichtbarkeit */
+  --focus-color: #0066cc;
+  --focus-outline: 3px solid var(--focus-color);
+}
+
+/* Ampelfarben (Klassische Ampel) mit besserem Kontrast */
+:root[data-color-scheme="ampel"] {
+  --rot-hell: #ffe6e6;
+  --gelb-hell: #fffacc;
+  --gruen-hell: #e6ffe6;
+  --rot: #990000;      /* Verbessert für besseren Kontrast */
+  --gelb: #996600;     /* Verbessert für besseren Kontrast */
+  --gruen: #006600;    /* Verbessert für besseren Kontrast */
+}
+
+/* Alternative Farben mit besserem Kontrast */
+:root[data-color-scheme="alternative"] {
+  --rot-hell: #ffe6f5;
+  --gelb-hell: #ffeccc;
+  --gruen-hell: #e6f9ff;
+  --rot: #cc0044;      /* Verbessert für besseren Kontrast */
+  --gelb: #b8620e;     /* Verbessert für besseren Kontrast */
+  --gruen: #008a8a;    /* Verbessert für besseren Kontrast */
+}
+
+* {
+  box-sizing: border-box;
+}
+
+body {
+  margin: 0;
+  padding: 16px;
+  background: var(--grau-hell);
+  color: var(--schwarz);
+  font: 14px/1.5 system-ui, sans-serif; /* Verbesserte Zeilenhöhe für bessere Lesbarkeit */
+}
+
+/* Focus-Stile für bessere Keyboard-Navigation */
+*:focus {
+  outline: var(--focus-outline);
+  outline-offset: 2px;
+}
+
+/* Verbesserter Focus für interaktive Elemente */
+button:focus,
+select:focus,
+input:focus,
+.person-row:focus,
+.section-header:focus {
+  outline: var(--focus-outline);
+  outline-offset: 2px;
+}
+
+h1, h2, h3 {
+  margin: 0.2rem 0 0.6rem;
+  line-height: 1.3; /* Verbesserte Zeilenhöhe für Überschriften */
+}
+
+.muted {
+  opacity: 0.8;
+}
+
+.row {
+  display: flex;
+  gap: 0.5rem;
+  align-items: center;
+}
+
+.grid-2 {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 12px;
+}
+
+.grid-2 .full {
+  grid-column: 1/-1;
+}
+
+textarea,
+input,
+select {
+  width: 100%;
+  padding: 0.5rem;
+  background: var(--weiss);
+  color: var(--schwarz);
+  border: 2px solid var(--grau-stark); /* Dickerer Border für bessere Sichtbarkeit */
+  border-radius: 4px;
+  font-size: 14px; /* Explizite Schriftgröße für bessere Lesbarkeit */
+}
+
+/* Hover-Effekte für bessere Interaktivität */
+textarea:hover,
+input:hover,
+select:hover {
+  border-color: var(--diwo-blau);
+}
+
+button {
+  padding: 0.5rem 0.9rem;
+  border: 2px solid var(--grau-stark);
+  background: #fff;
+  cursor: pointer;
+  font-size: 14px;
+  min-height: 44px; /* Mindesthöhe für Touch-Geräte */
+  transition: all 0.2s ease;
+}
+
+button:hover {
+  background: var(--grau-hell);
+  border-color: var(--diwo-blau);
+}
+
+button:active {
+  transform: translateY(1px);
+}
+
+.error-box {
+  background: var(--rot-hell);
+  border-left: 4px solid var(--rot);
+  padding: 0.6rem;
+  margin: 0.6rem 0;
+  border-radius: 4px;
+}
+
+/* DIWO Header Styles */
+.diwo-header {
+  background: var(--weiss);
+  border: 1px solid var(--grau-stark);
+  padding: 12px 16px;
+  margin-bottom: 16px;
+  border-radius: 4px;
+}
+
+.header-row-1 {
+  display: flex;
+  align-items: center;
+  gap: 20px;
+  margin-bottom: 8px;
+  padding-bottom: 8px;
+  border-bottom: 1px solid var(--grau-stark);
+}
+
+.kunde-logo {
+  width: auto;
+  height: 50px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.kunde-logo-container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  height: 100%;
+}
+
+.kunde-logo-image {
+  max-width: auto;
+  max-height: 50px;
+  object-fit: contain;
+  border-radius: 4px;
+  border: 1px solid var(--grau-stark); /* Border für bessere Definition */
+}
+
+.diwo-title {
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+}
+
+.diwo-title h1 {
+  font-size: 24px;
+  font-weight: bold;
+  margin: 0;
+  color: var(--diwo-blau);
+}
+
+.diwo-title h2 {
+  font-size: 18px;
+  font-weight: normal;
+  margin: 0;
+  color: var(--schwarz);
+}
+
+.page-title {
+  font-size: 16px;
+  font-weight: bold;
+  margin-left: auto;
+}
+
+.header-row-2 {
+  display: grid;
+  grid-template-columns: auto 1fr auto;
+  gap: 20px;
+  margin-bottom: 8px;
+  align-items: center;
+}
+
+.aktenzeichen {
+  font-weight: bold;
+  font-size: 14px;
+}
+
+.antragsteller-info {
+  text-align: center;
+  font-weight: bold;
+}
+
+.location-info {
+  text-align: right;
+  font-size: 13px;
+}
+
+.header-row-3 {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-top: 8px;
+  padding-top: 8px;
+  border-top: 1px solid var(--grau-stark);
+}
+
+.vorgang-info {
+  font-size: 13px;
+}
+
+.zeitraum-controls {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.zeitraum-form {
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.zeitraum-form label {
+  font-size: 13px;
+  font-weight: bold;
+  white-space: nowrap;
+}
+
+.zeitraum-select {
+  width: 200px;
+  padding: 4px 8px;
+  font-size: 13px;
+  min-height: 32px; /* Mindesthöhe für bessere Bedienbarkeit */
+}
+
+.wechseln-btn {
+  padding: 4px 8px;
+  font-size: 12px;
+  min-width: 32px;
+  min-height: 32px; /* Mindesthöhe für Touch-Geräte */
+}
+
+/* Vergleichslegende */
+.vergleich-legende {
+  display: flex;
+  align-items: center;
+  gap: 12px;
+  margin: 12px 0;
+  padding: 8px 12px;
+  background: var(--grau-hell);
+  border: 1px solid var(--grau-stark);
+  border-radius: 4px;
+  font-size: 13px;
+}
+
+.legende-item {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  padding: 4px 8px;
+  border-radius: 3px;
+}
+
+.legende-item:has(.plus) {
+  background: var(--gruen-hell);
+  border: 1px solid var(--gruen); /* Border für bessere Definition */
+}
+
+.legende-item:has(.minus) {
+  background: var(--rot-hell);
+  border: 1px solid var(--rot);
+}
+
+.legende-item:has(.warn) {
+  background: var(--gelb-hell);
+  border: 1px solid var(--gelb);
+}
+
+.legende-title {
+  margin-right: 8px;
+  font-size: 13px;
+}
+
+.legende-spacer {
+  color: var(--grau-stark);
+  font-weight: bold;
+}
+
+.selected-indicator-text {
+  border: 3px solid var(--diwo-blau);
+  padding: 2px 4px;
+  border-radius: 2px;
+  display: inline-block;
+  background: var(--diwo-blau-hell);
+}
+
+table {
+  width: 100%;
+  border-collapse: collapse;
+  background: var(--weiss);
+  border: 1px solid var(--grau-stark);
+  margin: 0.6rem 0 1rem;
+}
+
+th, td {
+  border: 1px solid var(--grau-stark);
+  padding: 0.5rem;
+  vertical-align: top;
+  text-align: left; /* Explizite Textausrichtung */
+}
+
+th {
+  background: var(--grau-stark);
+  position: sticky;
+  top: 0;
+  font-weight: bold;
+  color: var(--schwarz); /* Sicherstellen dass Text lesbar ist */
+}
+
+.sym {
+  width: 2.5rem;
+  min-width: 2.5rem;
+  max-width: 2.5rem;
+  text-align: center;
+  font-weight: 700;
+  font-size: 16px; /* Größere Symbole für bessere Sichtbarkeit */
+}
+
+.sym.plus {
+  color: var(--gruen);
+}
+
+.sym.minus {
+  color: var(--rot);
+}
+
+.sym.warn {
+  color: var(--gelb);
+}
+
+/* Verbesserte Diff-Zeilen mit Patterns für Farbenblinde */
+.diff-row-added {
+  background: var(--gruen-hell);
+  background-image: repeating-linear-gradient(
+    45deg,
+    transparent,
+    transparent 5px,
+    rgba(0, 77, 38, 0.1) 5px,
+    rgba(0, 77, 38, 0.1) 10px
+  );
+}
+
+.diff-row-removed {
+  background: var(--rot-hell);
+  background-image: repeating-linear-gradient(
+    -45deg,
+    transparent,
+    transparent 5px,
+    rgba(179, 0, 0, 0.1) 5px,
+    rgba(179, 0, 0, 0.1) 10px
+  );
+}
+
+.cell-changed,
+td:has(.cell-changed) {
+  background: var(--gelb-hell);
+  background-image: repeating-linear-gradient(
+    90deg,
+    transparent,
+    transparent 3px,
+    rgba(157, 85, 2, 0.1) 3px,
+    rgba(157, 85, 2, 0.1) 6px
+  );
+}
+
+/* Verbessertes Icon für geänderte Zellen */
+.cell-changed:not(.sym)::before {
+  content: "! ";
+  color: var(--gelb);
+  font-weight: bold;
+  font-size: 0.9em;
+  margin-right: 4px;
+}
+
+.old {
+  display: block;
+  font-size: 0.85em;
+  margin-top: 0.2rem;
+  opacity: 0.9;
+  text-decoration: line-through;
+}
+
+.header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 8px;
+}
+
+.legend {
+  margin: 0.5rem 0 1rem;
+}
+
+/* Person Selection - Blue outline for selected person */
+.person-selected {
+  outline: 3px solid var(--diwo-blau);
+  outline-offset: -3px;
+  background: var(--diwo-blau-hell);
+}
+
+/* Clickable table rows mit verbesserter Accessibility */
+.person-row {
+  cursor: pointer;
+  transition: background-color 0.2s ease;
+  position: relative;
+}
+
+.person-row:hover {
+  background-color: var(--diwo-blau-hell);
+}
+
+.person-row:focus {
+  background-color: var(--diwo-blau-hell);
+}
+
+/* Skip to content für Screen Reader */
+.skip-link {
+  position: absolute;
+  top: -40px;
+  left: 6px;
+  background: var(--diwo-blau);
+  color: var(--weiss);
+  padding: 8px;
+  text-decoration: none;
+  border-radius: 4px;
+  z-index: 1000;
+}
+
+.skip-link:focus {
+  top: 6px;
+}
+
+.table-action {
+  padding: 0.25rem 0.5rem;
+  border: 1px solid var(--grau-stark);
+  background: #fff;
+  cursor: pointer;
+  font-size: 0.85rem;
+  min-height: 32px;
+}
+
+.table-action:hover {
+  filter: brightness(0.98);
+}
+
+/* Farbwahl in der Legende */
+.color-picker-container {
+  position: relative;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  margin-left: auto;
+}
+
+.color-picker-toggle {
+  background: none;
+  border: 2px solid var(--grau-stark);
+  cursor: pointer;
+  font-size: 16px;
+  padding: 4px;
+  border-radius: 4px;
+  transition: all 0.2s ease;
+  min-height: 32px;
+  min-width: 32px;
+}
+
+.color-picker-toggle:hover {
+  background: var(--grau-stark);
+  border-color: var(--diwo-blau);
+}
+
+.color-picker-popup {
+  position: absolute;
+  top: 100%;
+  right: 0;
+  background: var(--weiss);
+  border: 2px solid var(--grau-stark);
+  border-radius: 4px;
+  padding: 12px;
+  box-shadow: 0 4px 12px rgba(20, 92, 158, 0.2);
+  z-index: 1000;
+  font-size: 13px;
+  min-width: 160px;
+}
+
+.color-picker-popup.hidden {
+  display: none;
+}
+
+.color-picker-popup label {
+  font-weight: bold;
+  margin-bottom: 6px;
+  display: block;
+}
+
+.color-scheme-select {
+  width: 100%;
+  padding: 6px 8px;
+  font-size: 13px;
+  border: 2px solid var(--grau-stark);
+  border-radius: 4px;
+  min-height: 32px;
+}
+
+/* Responsive Layout für Freibeträge und Kinderbetreuung */
+.freibetrag-container {
+  display: flex;
+  gap: 16px;
+  margin: 0.6rem 0 1rem;
+}
+
+.freibetrag-section {
+  flex: 1;
+  min-width: 0;
+}
+
+.freibetrag-section h3 {
+  margin: 0 0 0.6rem;
+  font-size: 16px;
+}
+
+.kinderbetreuung-container {
+  max-width: 50%;
+}
+
+/* Bei kleinen Bildschirmen: Untereinander anordnen */
+@media (max-width: 1200px) {
+  .freibetrag-container {
+    flex-direction: column;
+  }
+
+  .kinderbetreuung-container {
+    max-width: 100%;
+  }
+}
+
+/* Ausklappbare Sektionen */
+.section-container {
+  margin: 0.6rem 0 1rem;
+}
+
+.section-header {
+  background: var(--grau-stark);
+  padding: 12px 16px;
+  border: 2px solid var(--grau-stark);
+  border-radius: 4px 4px 0 0;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  user-select: none;
+  transition: all 0.2s ease;
+  min-height: 44px; /* Mindesthöhe für Touch-Geräte */
+}
+
+.section-header:hover {
+  background: #c5c6c7;
+  border-color: var(--diwo-blau);
+}
+
+.section-header:focus {
+  background: #c5c6c7;
+  border-color: var(--diwo-blau);
+}
+
+.section-header h3 {
+  margin: 0;
+  font-size: 16px;
+  font-weight: bold;
+}
+
+.toggle-icon {
+  font-size: 14px;
+  transition: transform 0.2s ease;
+  font-weight: bold;
+}
+
+.toggle-icon.collapsed {
+  transform: rotate(-90deg);
+}
+
+.section-content {
+  border: 2px solid var(--grau-stark);
+  border-top: none;
+  border-radius: 0 0 4px 4px;
+  transition: max-height 0.3s ease;
+  overflow: hidden;
+}
+
+.section-content.collapsed {
+  display: none;
+}
+
+.section-content table {
+  margin: 0;
+  border: none;
+}
+
+/* Screen Reader only content */
+.sr-only {
+  position: absolute;
+  width: 1px;
+  height: 1px;
+  padding: 0;
+  margin: -1px;
+  overflow: hidden;
+  clip: rect(0, 0, 0, 0);
+  white-space: nowrap;
+  border: 0;
+}
+
+/* High Contrast Mode Support */
+@media (prefers-contrast: high) {
+  :root {
+    --rot: #000000;
+    --gelb: #000000;
+    --gruen: #000000;
+    --grau-stark: #000000;
+  }
+  
+  .sym {
+    font-weight: 900;
+    text-shadow: 1px 1px 1px rgba(255, 255, 255, 0.5);
+  }
+  
+  button, input, select {
+    border-width: 3px;
+  }
+}
+
+/* Reduced Motion Support */
+@media (prefers-reduced-motion: reduce) {
+  *, *::before, *::after {
+    animation-duration: 0.01ms !important;
+    animation-iteration-count: 1 !important;
+    transition-duration: 0.01ms !important;
+  }
+  
+  .toggle-icon {
+    transition: none;
+  }
+  
+  .section-content {
+    transition: none;
+  }
+}
+
+/* Print Styles */
+@media print {
+  .color-picker-container,
+  .zeitraum-controls,
+  .section-header {
+    display: none;
+  }
+  
+  .section-content {
+    display: block !important;
+  }
+  
+  .diff-row-added::after {
+    content: " [HINZUGEFÜGT]";
+    font-weight: bold;
+  }
+  
+  .diff-row-removed::after {
+    content: " [GELÖSCHT]";
+    font-weight: bold;
+  }
+  
+  .cell-changed::after {
+    content: " [GEÄNDERT]";
+    font-weight: bold;
+  }
+}

+ 351 - 45
src/main/resources/static/styles.css

@@ -1,19 +1,92 @@
-:root{
-  --rot-hell:#FFD6D6; --gelb-hell:#FFF5CC; --gruen-hell:#D6F5D6;
-  --rot:#800000; --gelb:#784801; --gruen:#006633;
-  --weiss:#FFFFFF; --schwarz:#000000; --grau-hell:#F7F9FA; --grau-stark:#D7D8D9;
-  --berlin-rot:#E20613; --diwo-blau:#0052A5;
-}
-*{box-sizing:border-box}
-body{margin:0;padding:16px;background:var(--grau-hell);color:var(--schwarz);font:14px/1.4 system-ui,sans-serif}
-h1,h2,h3{margin:.2rem 0 .6rem}
-.muted{opacity:.8}
-.row{display:flex;gap:.5rem;align-items:center}
-.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:12px}
-.grid-2 .full{grid-column:1/-1}
-textarea,input,select{width:100%;padding:.5rem;background:var(--weiss);color:var(--schwarz);border:1px solid var(--grau-stark);border-radius:4px}
-button{padding:.5rem .9rem;border:1px solid var(--grau-stark);background:#fff;cursor:pointer}
-.error-box{background:var(--rot-hell);border-left:4px solid var(--rot);padding:.6rem;margin:.6rem 0}
+:root {
+  /* Standard Farben (Original) */
+  --rot-hell: #ffd6d6;
+  --gelb-hell: #fff5cc;
+  --gruen-hell: #d6f5d6;
+  --rot: #800000;
+  --gelb: #784801;
+  --gruen: #006633;
+  --weiss: #ffffff;
+  --schwarz: #000000;
+  --grau-hell: #f7f9fa;
+  --grau-stark: #d7d8d9;
+  --berlin-rot: #e20613;
+  --diwo-blau: #145c9e;
+  --diwo-blau-hell: #cfe8fc;
+}
+
+/* Ampelfarben (Klassische Ampel) */
+:root[data-color-scheme="ampel"] {
+  --rot-hell: #ffcccc;
+  --gelb-hell: #ffff99;
+  --gruen-hell: #ccffcc;
+  --rot: #920000;
+  --gelb: #8b7700;
+  --gruen: #006b00;
+}
+
+/* Alternative Farben (komplett andere Kombination) */
+:root[data-color-scheme="alternative"] {
+  --rot-hell: #ffd2f0;
+  --gelb-hell: #ffe1b3;
+  --gruen-hell: #c6f8ff;
+  --rot: #ff0055;
+  --gelb: #ca7a02;
+  --gruen: #00a8ad;
+}
+* {
+  box-sizing: border-box;
+}
+body {
+  margin: 0;
+  padding: 16px;
+  background: var(--grau-hell);
+  color: var(--schwarz);
+  font: 14px/1.4 system-ui, sans-serif;
+}
+h1,
+h2,
+h3 {
+  margin: 0.2rem 0 0.6rem;
+}
+.muted {
+  opacity: 0.8;
+}
+.row {
+  display: flex;
+  gap: 0.5rem;
+  align-items: center;
+}
+.grid-2 {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 12px;
+}
+.grid-2 .full {
+  grid-column: 1/-1;
+}
+textarea,
+input,
+select {
+  width: 100%;
+  padding: 0.5rem;
+  background: var(--weiss);
+  color: var(--schwarz);
+  border: 1px solid var(--grau-stark);
+  border-radius: 4px;
+}
+button {
+  padding: 0.5rem 0.9rem;
+  border: 1px solid var(--grau-stark);
+  background: #fff;
+  cursor: pointer;
+}
+.error-box {
+  background: var(--rot-hell);
+  border-left: 4px solid var(--rot);
+  padding: 0.6rem;
+  margin: 0.6rem 0;
+}
 
 /* DIWO Header Styles */
 .diwo-header {
@@ -32,24 +105,27 @@ button{padding:.5rem .9rem;border:1px solid var(--grau-stark);background:#fff;cu
   border-bottom: 1px solid var(--grau-stark);
 }
 
-.berlin-logo {
+.kunde-logo {
+  width: auto;
+  height: 50px;
   display: flex;
   align-items: center;
-  gap: 6px;
-  background: var(--berlin-rot);
-  color: var(--weiss);
-  padding: 4px 8px;
-  font-weight: bold;
-  font-size: 12px;
+  justify-content: center;
 }
 
-.berlin-text {
-  font-size: 12px;
-  font-weight: bold;
+.kunde-logo-container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  height: 100%;
 }
 
-.berlin-bear {
-  font-size: 14px;
+.kunde-logo-image {
+  max-width: auto;
+  max-height: 50px;
+  object-fit: contain;
+  border-radius: 4px;
 }
 
 .diwo-title {
@@ -161,6 +237,25 @@ button{padding:.5rem .9rem;border:1px solid var(--grau-stark);background:#fff;cu
   display: flex;
   align-items: center;
   gap: 4px;
+  padding: 4px 8px;
+  border-radius: 3px;
+}
+
+.legende-item:has(.plus) {
+  background: var(--gruen-hell);
+}
+
+.legende-item:has(.minus) {
+  background: var(--rot-hell);
+}
+
+.legende-item:has(.warn) {
+  background: var(--gelb-hell);
+}
+
+.legende-title {
+  margin-right: 8px;
+  font-size: 13px;
 }
 
 .legende-spacer {
@@ -168,22 +263,84 @@ button{padding:.5rem .9rem;border:1px solid var(--grau-stark);background:#fff;cu
   font-weight: bold;
 }
 
-table{width:100%;border-collapse:collapse;background:var(--weiss);border:1px solid var(--grau-stark);margin:.6rem 0 1rem}
-th,td{border:1px solid var(--grau-stark);padding:.5rem;vertical-align:top}
-th{background:var(--grau-stark);position:sticky;top:0}
-.sym{width:2.5rem;text-align:center;font-weight:700}
-.sym.plus{color:var(--gruen)}
-.sym.minus{color:var(--rot)}
-.sym.warn{color:var(--gelb)}
-.diff-row-added{background:var(--gruen-hell)}
-.diff-row-removed{background:var(--rot-hell)}
-.cell-changed,td:has(.cell-changed){background:var(--gelb-hell)}
-.old{display:block;font-size:.85em;margin-top:.2rem;opacity:.9}
-.header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px}
-.legend{margin:.5rem 0 1rem}
+.selected-indicator-text {
+  border: 3px solid var(--diwo-blau);
+  padding: 2px 4px;
+  border-radius: 2px;
+  display: inline-block;
+}
+
+table {
+  width: 100%;
+  border-collapse: collapse;
+  background: var(--weiss);
+  border: 1px solid var(--grau-stark);
+  margin: 0.6rem 0 1rem;
+}
+th,
+td {
+  border: 1px solid var(--grau-stark);
+  padding: 0.5rem;
+  vertical-align: top;
+}
+th {
+  background: var(--grau-stark);
+  position: sticky;
+  top: 0;
+}
+.sym {
+  width: 2.5rem;
+  min-width: 2.5rem;
+  max-width: 2.5rem;
+  text-align: center;
+  font-weight: 700;
+}
+.sym.plus {
+  color: var(--gruen);
+}
+.sym.minus {
+  color: var(--rot);
+}
+.sym.warn {
+  color: var(--gelb);
+}
+.diff-row-added {
+  background: var(--gruen-hell);
+}
+.diff-row-removed {
+  background: var(--rot-hell);
+}
+.cell-changed,
+td:has(.cell-changed) {
+  background: var(--gelb-hell);
+}
+.cell-changed:not(.sym)::before {
+  content: "! ";
+  color: var(--gelb);
+  font-weight: bold;
+  font-size: 0.9em;
+}
+.old {
+  display: block;
+  font-size: 0.85em;
+  margin-top: 0.2rem;
+  opacity: 0.9;
+}
+.header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 8px;
+}
+.legend {
+  margin: 0.5rem 0 1rem;
+}
 
 /* Person Selection - Blue outline for selected person */
-.person-selected{outline:3px solid var(--diwo-blau);outline-offset:-3px}
+.person-selected {
+  outline: 3px solid var(--diwo-blau);
+  outline-offset: -3px;
+}
 
 /* Clickable table rows */
 .person-row {
@@ -192,8 +349,157 @@ th{background:var(--grau-stark);position:sticky;top:0}
 }
 
 .person-row:hover {
-  background-color: rgba(0, 82, 165, 0.1);
+  background-color: var(--diwo-blau-hell);
+}
+
+.table-action {
+  padding: 0.25rem 0.5rem;
+  border: 1px solid var(--grau-stark);
+  background: #fff;
+  cursor: pointer;
+  font-size: 0.85rem;
+}
+.table-action:hover {
+  filter: brightness(0.98);
+}
+
+/* Farbwahl in der Legende */
+.color-picker-container {
+  position: relative;
+  display: flex;
+  align-items: center;
+  gap: 4px;
+  margin-left: auto;
+}
+
+.color-picker-toggle {
+  background: none;
+  border: none;
+  cursor: pointer;
+  font-size: 16px;
+  padding: 2px;
+  border-radius: 3px;
+  transition: background-color 0.2s;
+}
+
+.color-picker-toggle:hover {
+  background: var(--grau-stark);
+}
+
+.color-picker-popup {
+  position: absolute;
+  top: 100%;
+  right: 0;
+  background: var(--weiss);
+  border: 1px solid var(--grau-stark);
+  border-radius: 4px;
+  padding: 8px;
+  box-shadow: 0 2px 8px var(--diwo-blau-hell);
+  z-index: 1000;
+  font-size: 12px;
+  min-width: 140px;
+}
+
+.color-picker-popup.hidden {
+  display: none;
+}
+
+.color-picker-popup label {
+  font-weight: bold;
+  margin-bottom: 4px;
+  display: block;
+}
+
+.color-scheme-select {
+  width: 100%;
+  padding: 4px 6px;
+  font-size: 12px;
+  border: 1px solid var(--grau-stark);
+  border-radius: 3px;
+}
+
+/* Responsive Layout für Freibeträge und Kinderbetreuung */
+.freibetrag-container {
+  display: flex;
+  gap: 16px;
+  margin: 0.6rem 0 1rem;
+}
+
+.freibetrag-section {
+  flex: 1;
+  min-width: 0;
+}
+
+.freibetrag-section h3 {
+  margin: 0 0 0.6rem;
+  font-size: 16px;
+}
+
+.kinderbetreuung-container {
+  max-width: 50%;
+}
+
+/* Bei kleinen Bildschirmen: Untereinander anordnen */
+@media (max-width: 1200px) {
+  .freibetrag-container {
+    flex-direction: column;
+  }
+
+  .kinderbetreuung-container {
+    max-width: 100%;
+  }
 }
 
-.table-action{padding:.25rem .5rem;border:1px solid var(--grau-stark);background:#fff;cursor:pointer;font-size:.85rem}
-.table-action:hover{filter:brightness(0.98)}
+/* Ausklappbare Sektionen */
+.section-container {
+  margin: 0.6rem 0 1rem;
+}
+
+.section-header {
+  background: var(--grau-stark);
+  padding: 8px 12px;
+  border: 1px solid var(--grau-stark);
+  border-radius: 4px 4px 0 0;
+  cursor: pointer;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+  user-select: none;
+  transition: background-color 0.2s ease;
+}
+
+.section-header:hover {
+  background: #c5c6c7;
+}
+
+.section-header h3 {
+  margin: 0;
+  font-size: 16px;
+  font-weight: bold;
+}
+
+.toggle-icon {
+  font-size: 12px;
+  transition: transform 0.2s ease;
+}
+
+.toggle-icon.collapsed {
+  transform: rotate(-90deg);
+}
+
+.section-content {
+  border: 1px solid var(--grau-stark);
+  border-top: none;
+  border-radius: 0 0 4px 4px;
+  transition: max-height 0.3s ease;
+  overflow: hidden;
+}
+
+.section-content.collapsed {
+  display: none;
+}
+
+.section-content table {
+  margin: 0;
+  border: none;
+}

+ 7 - 0
src/main/resources/templates/test.html

@@ -20,6 +20,12 @@
     <div class="full row">
       <label><input type="checkbox" name="validate" th:checked="${validate ?: true}"> Gegen Schema validieren</label>
       <label>Schema-Name: <input type="text" name="schemaName" th:value="${schemaName ?: 'haushalt-einkommen'}"></label>
+      <label>Kunde/Bundesland: 
+        <select name="kunde">
+          <option value="">-- Kein Logo --</option>
+          <option value="Berlin">Berlin</option>
+        </select>
+      </label>
       <button type="submit">Vergleichen & anzeigen</button>
     </div>
   </form>
@@ -32,6 +38,7 @@
       <input type="hidden" name="rightJson" th:value="${rightJson}"/>
       <input type="hidden" name="validate" th:value="${validate}"/>
       <input type="hidden" name="schemaName" th:value="${schemaName}"/>
+      <input type="hidden" name="kunde" th:value="${activeKunde}"/>
 
       <label>Vorgang (links):
         <select name="leftVorgangId">

+ 513 - 0
src/main/resources/templates/vergleich-accessible.html

@@ -0,0 +1,513 @@
+<!DOCTYPE html>
+<html lang="de" xmlns:th="http://www.thymeleaf.org">
+<head>
+    <meta charset="UTF-8"/>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
+    <title>DIWO Vergleich – Haushaltsmitglieder/Einkommen</title>
+    <link rel="stylesheet" href="/styles.css"/>
+</head>
+<body>
+
+  <header class="diwo-header" role="banner">
+    <div class="header-row-1">
+      <div class="kunde-logo">
+        <div th:if="${activeKunde != null and !#strings.isEmpty(activeKunde)}" class="kunde-logo-container">
+          <img th:src="@{'/images/' + ${#strings.toLowerCase(activeKunde)} + '.jpg'}" 
+               th:alt="${activeKunde != null and !#strings.isEmpty(activeKunde) ? activeKunde + ' Logo' : 'Kunden-Logo'}" 
+               class="kunde-logo-image"
+               th:onerror="'this.style.display=\\'none\\''"/>
+        </div>
+      </div>
+      <div class="diwo-title">
+        <h1>DIWO</h1>
+        <h2>Vergleich</h2>
+      </div>
+      <div class="page-title" role="heading" aria-level="2">Haushaltsmitglieder/Einkommen</div>
+    </div>
+    
+    <div class="header-row-2">
+      <div class="aktenzeichen" th:if="${stammakteRight != null and !#strings.isEmpty(stammakteRight.aktenzeichen)}" role="contentinfo" aria-label="Aktenzeichen">
+        <span th:text="${stammakteRight.aktenzeichen}">01/01/119979</span>
+      </div>
+      <div class="aktenzeichen" th:if="${(stammakteRight == null or #strings.isEmpty(stammakteRight.aktenzeichen)) and stammvorgangRight != null}" role="contentinfo" aria-label="Aktenzeichen">
+        <span th:text="${stammvorgangRight.aktenzeichen}">01/01/119979</span>
+      </div>
+      
+      <div class="antragsteller-info" th:if="${stammakteRight != null}" role="contentinfo" aria-label="Antragsteller-Informationen">
+        <span th:if="${!#strings.isEmpty(stammakteRight.antragstellername)}" th:text="${stammakteRight.antragstellername}">Müller, Tobias</span>
+        <span th:if="${!#strings.isEmpty(stammakteRight.antragstellerGeburtsdatum)}"> - <span th:text="${stammakteRight.antragstellerGeburtsdatum}">01.01.2000</span></span>
+      </div>
+      
+      <div class="location-info" th:if="${stammakteRight != null and !#strings.isEmpty(stammakteRight.anschrift)}" role="contentinfo" aria-label="Anschrift">
+        <span th:text="${stammakteRight.anschrift}">Unter den Linden 1, 10117 Berlin</span>
+      </div>
+    </div>
+    
+    <div class="header-row-3">
+      <div class="vorgang-info" th:if="${stammvorgangRight != null}" role="contentinfo" aria-label="Vorgang in Bearbeitung">
+        <span>Vorgang in der Bearbeitung: </span>
+        <span th:if="${!#strings.isEmpty(stammvorgangRight.bemerkung)}" th:text="${stammvorgangRight.bemerkung}">Weiterleistungsantrag</span>
+        <span th:if="${#strings.isEmpty(stammvorgangRight.bemerkung)}">-</span>
+        <span th:if="${!#strings.isEmpty(stammvorgangRight.beginn) and !#strings.isEmpty(stammvorgangRight.ende)}">
+          (<span th:text="${stammvorgangRight.beginn}">01.01.2026</span> - <span th:text="${stammvorgangRight.ende}">31.12.2027</span>)
+        </span>
+      </div>
+      
+      <div class="zeitraum-controls">
+        <form method="post" action="/vergleich" class="zeitraum-form" role="form" aria-label="Zeitraum wechseln">
+          <input type="hidden" name="schemaName" th:value="${schemaName}"/>
+          <input type="hidden" name="validate" th:value="${validate}"/>
+          <input type="hidden" name="leftJson" th:value="${leftJson}"/>
+          <input type="hidden" name="rightJson" th:value="${rightJson}"/>
+          <input type="hidden" name="leftVorgangId" th:value="${leftVorgangId}"/>
+          <input type="hidden" name="leftZeitraumId" th:value="${leftZeitraumId}"/>
+          <input type="hidden" name="kunde" th:value="${activeKunde}"/>
+          
+          <label for="rightZeitraumId">Zeitraum:</label>
+          <select name="rightZeitraumId" id="rightZeitraumId" class="zeitraum-select" aria-describedby="zeitraum-hint">
+            <option th:each="z : ${zeitraeumeRight}" th:value="${z.id}"
+                    th:selected="${z.id == rightZeitraumId}"
+                    th:text="${z.beginn} + ' - ' + ${z.ende}"></option>
+          </select>
+          <div id="zeitraum-hint" class="sr-only">Wählen Sie einen anderen Zeitraum für den Vergleich aus</div>
+          <button type="submit" class="wechseln-btn" aria-label="Zeitraum wechseln">▼</button>
+        </form>
+      </div>
+    </div>
+  </header>
+
+  <!-- Vergleichslegende -->
+  <section class="vergleich-legende" role="complementary" aria-labelledby="legende-title">
+    <div class="legende-title" id="legende-title">
+      <strong>Legende:</strong>
+    </div>
+    <div class="legende-item">
+      <span class="sym plus" aria-label="Hinzugefügt">+</span> 
+      <span>hinzugefügt (<span id="addedColorName">grün</span>)</span>
+    </div>
+    <div class="legende-spacer" aria-hidden="true">|</div>
+    <div class="legende-item">
+      <span class="sym minus" aria-label="Gelöscht">−</span> 
+      <span>gelöscht (<span id="removedColorName">rot</span>)</span>
+    </div>
+    <div class="legende-spacer" aria-hidden="true">|</div>
+    <div class="legende-item">
+      <span class="sym warn" aria-label="Geändert">!</span> 
+      <span>geändert (<span id="changedColorName">gelb</span>)</span>
+    </div>
+    <div class="legende-spacer" aria-hidden="true">|</div>
+    <div class="legende-item">
+      <span class="selected-indicator-text">ausgewählte Person (blauer Rahmen)</span>
+    </div>
+    
+    <!-- Farbwahl -->
+    <div class="color-picker-container">
+      <button class="color-picker-toggle" onclick="toggleColorPicker()" aria-label="Farbschema-Einstellungen öffnen" aria-expanded="false" aria-controls="colorPickerPopup">⚙️</button>
+      <div class="color-picker-popup hidden" id="colorPickerPopup" role="dialog" aria-labelledby="color-picker-label">
+        <label id="color-picker-label" for="colorSchemeSelect">Farbwahl:</label>
+        <select id="colorSchemeSelect" class="color-scheme-select" onchange="changeColorScheme(this.value)" aria-describedby="color-scheme-hint">
+          <option value="standard">Standard</option>
+          <option value="ampel">Ampelfarben (neon)</option>
+          <option value="alternative">Pastell</option>
+        </select>
+        <div id="color-scheme-hint" class="sr-only">Ändern Sie das Farbschema für bessere Sichtbarkeit</div>
+      </div>
+    </div>
+  </section>
+
+  <!-- Haushaltsmitglieder - klickbare Zeilen -->
+  <main role="main">
+    <div class="section-container">
+      <div class="section-header" onclick="toggleSection('haushaltsmitglieder')" role="button" tabindex="0" 
+           onkeypress="handleKeyPress(event, 'haushaltsmitglieder')" aria-expanded="true" aria-controls="haushaltsmitglieder-content">
+        <span class="toggle-icon" id="haushaltsmitglieder-icon" aria-hidden="true">▼</span>
+        <h3>Haushaltsmitglieder</h3>
+      </div>
+      <div class="section-content" id="haushaltsmitglieder-content">
+        <table role="table" aria-labelledby="haushaltsmitglieder-caption">
+          <caption id="haushaltsmitglieder-caption" class="sr-only">
+            Vergleich der Haushaltsmitglieder zwischen zwei Zeiträumen. Klicken Sie auf eine Zeile, um Details zu dieser Person anzuzeigen.
+          </caption>
+          <thead>
+            <tr>
+              <th scope="col">Änderung</th>
+              <th scope="col">Name</th>
+              <th scope="col">Geburtsdatum</th>
+              <th scope="col">Familienstand</th>
+              <th scope="col">Geschlecht</th>
+              <th scope="col">Verwandtschaft</th>
+              <th scope="col">Sozialstellung</th>
+              <th scope="col">Kind</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr th:each="r : ${mitgliederRows}"
+                th:class="'person-row ' + ${r.rowClass} + ((${r.personId} == ${personId}) ? ' person-selected' : '')"
+                th:data-person-id="${r.personId}"
+                role="button"
+                tabindex="0"
+                th:aria-label="'Person auswählen: ' + ${r.nameHtml}"
+                onkeypress="handleRowKeyPress(event, this)">
+              <td class="sym" th:classappend="' ' + ${r.symbolClass}" th:text="${r.symbol}" 
+                  th:aria-label="${r.symbol == '+' ? 'Hinzugefügt' : (r.symbol == '-' ? 'Gelöscht' : (r.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
+              <td th:utext="${r.nameHtml}"></td>
+              <td th:utext="${r.geburtsdatumHtml}"></td>
+              <td th:utext="${r.familienstandHtml}"></td>
+              <td th:utext="${r.geschlechtHtml}"></td>
+              <td th:utext="${r.verwandtschaftHtml}"></td>
+              <td th:utext="${r.sozialstellungHtml}"></td>
+              <td th:utext="${r.istKindHtml}"></td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+    </div>
+
+    <!-- Hidden form for person selection -->
+    <form id="personForm" method="post" action="/vergleich" style="display: none;" aria-hidden="true">
+      <input type="hidden" name="leftJson" th:value="${leftJson}"/>
+      <input type="hidden" name="rightJson" th:value="${rightJson}"/>
+      <input type="hidden" name="validate" th:value="${validate}"/>
+      <input type="hidden" name="schemaName" th:value="${schemaName}"/>
+      <input type="hidden" name="leftVorgangId" th:value="${leftVorgangId}"/>
+      <input type="hidden" name="leftZeitraumId" th:value="${leftZeitraumId}"/>
+      <input type="hidden" name="rightZeitraumId" th:value="${rightZeitraumId}"/>
+      <input type="hidden" name="kunde" th:value="${activeKunde}"/>
+      <input type="hidden" name="personId" id="selectedPersonId"/>
+    </form>
+
+    <h2>Details zu <span th:if="${personLabel != null and !#strings.isEmpty(personLabel) and personLabel != '(keine Person)'}"><span th:text="${personLabel}"> - </span></span></h2>
+    
+    <!-- Einkommen -->
+    <div class="section-container">
+      <div class="section-header" onclick="toggleSection('einkommen')" role="button" tabindex="0" 
+           onkeypress="handleKeyPress(event, 'einkommen')" aria-expanded="true" aria-controls="einkommen-content">
+        <span class="toggle-icon" id="einkommen-icon" aria-hidden="true">▼</span>
+        <h3>Einkommen</h3>
+      </div>
+      <div class="section-content" id="einkommen-content">
+        <table role="table" aria-labelledby="einkommen-caption">
+          <caption id="einkommen-caption" class="sr-only">
+            Vergleich der Einkommensdaten zwischen zwei Zeiträumen
+          </caption>
+          <thead>
+            <tr>
+              <th scope="col">Änderung</th>
+              <th scope="col">Art</th>
+              <th scope="col">Frequenz</th>
+              <th scope="col">Betrag</th>
+              <th scope="col">Zuschlag</th>
+              <th scope="col">Abschlag</th>
+              <th scope="col">Werbungskosten</th>
+              <th scope="col">Berechnungsart</th>
+            </tr>
+          </thead>
+          <tbody>
+            <tr th:each="z : ${einkommenZeilen}" th:class="${z.rowClass}">
+              <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"
+                  th:aria-label="${z.symbol == '+' ? 'Hinzugefügt' : (z.symbol == '-' ? 'Gelöscht' : (z.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
+              <td th:utext="${z.artHtml}"></td>
+              <td th:utext="${z.frequenzHtml}"></td>
+              <td th:utext="${z.betragHtml}"></td>
+              <td th:utext="${z.zuschlagHtml}"></td>
+              <td th:utext="${z.abschlagHtml}"></td>
+              <td th:utext="${z.werbungskostenHtml}"></td>
+              <td th:utext="${z.berechnungsartHtml}"></td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+    </div>
+
+    <div class="freibetrag-container">
+      <div class="freibetrag-section">
+        <div class="section-container">
+          <div class="section-header" onclick="toggleSection('pauschale')" role="button" tabindex="0" 
+               onkeypress="handleKeyPress(event, 'pauschale')" aria-expanded="true" aria-controls="pauschale-content">
+            <span class="toggle-icon" id="pauschale-icon" aria-hidden="true">▼</span>
+            <h3>Pauschale Abzüge</h3>
+          </div>
+          <div class="section-content" id="pauschale-content">
+            <table role="table" aria-labelledby="pauschale-caption">
+              <caption id="pauschale-caption" class="sr-only">
+                Vergleich der pauschalen Abzüge zwischen zwei Zeiträumen
+              </caption>
+              <thead>
+                <tr>
+                  <th scope="col">Änderung</th>
+                  <th scope="col">Art</th>
+                  <th scope="col">Frequenz</th>
+                  <th scope="col">Betrag</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr th:each="z : ${freibetragZeilen}" th:if="${z.typ == 'P'}" th:class="${z.rowClass}">
+                  <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"
+                      th:aria-label="${z.symbol == '+' ? 'Hinzugefügt' : (z.symbol == '-' ? 'Gelöscht' : (z.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
+                  <td th:utext="${z.artHtml}"></td>
+                  <td th:utext="${z.frequenzHtml}"></td>
+                  <td th:utext="${z.betragHtml}"></td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+        </div>
+      </div>
+      
+      <div class="freibetrag-section">
+        <div class="section-container">
+          <div class="section-header" onclick="toggleSection('freibetraege')" role="button" tabindex="0" 
+               onkeypress="handleKeyPress(event, 'freibetraege')" aria-expanded="true" aria-controls="freibetraege-content">
+            <span class="toggle-icon" id="freibetraege-icon" aria-hidden="true">▼</span>
+            <h3>Freibeträge</h3>
+          </div>
+          <div class="section-content" id="freibetraege-content">
+            <table role="table" aria-labelledby="freibetraege-caption">
+              <caption id="freibetraege-caption" class="sr-only">
+                Vergleich der Freibeträge zwischen zwei Zeiträumen
+              </caption>
+              <thead>
+                <tr>
+                  <th scope="col">Änderung</th>
+                  <th scope="col">Art</th>
+                  <th scope="col">Frequenz</th>
+                  <th scope="col">Betrag</th>
+                  <th scope="col">Frist</th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr th:each="z : ${freibetragZeilen}" th:if="${z.typ == 'F'}" th:class="${z.rowClass}">
+                  <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"
+                      th:aria-label="${z.symbol == '+' ? 'Hinzugefügt' : (z.symbol == '-' ? 'Gelöscht' : (z.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
+                  <td th:utext="${z.artHtml}"></td>
+                  <td th:utext="${z.frequenzHtml}"></td>
+                  <td th:utext="${z.betragHtml}"></td>
+                  <td th:utext="${z.fristHtml}"></td>
+                </tr>
+              </tbody>
+            </table>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- Kinderbetreuungskosten -->
+    <div class="kinderbetreuung-container">
+      <div class="section-container">
+        <div class="section-header" onclick="toggleSection('kinderbetreuung')" role="button" tabindex="0" 
+             onkeypress="handleKeyPress(event, 'kinderbetreuung')" aria-expanded="true" aria-controls="kinderbetreuung-content">
+          <span class="toggle-icon" id="kinderbetreuung-icon" aria-hidden="true">▼</span>
+          <h3>Kinderbetreuungskosten</h3>
+        </div>
+        <div class="section-content" id="kinderbetreuung-content">
+          <table role="table" aria-labelledby="kinderbetreuung-caption">
+            <caption id="kinderbetreuung-caption" class="sr-only">
+              Vergleich der Kinderbetreuungskosten zwischen zwei Zeiträumen
+            </caption>
+            <thead>
+              <tr>
+                <th scope="col">Änderung</th>
+                <th scope="col">Kind</th>
+                <th scope="col">Betrag</th>
+              </tr>
+            </thead>
+            <tbody>
+              <tr th:each="z : ${kitaZeilen}" th:class="${z.rowClass}">
+                <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"
+                    th:aria-label="${z.symbol == '+' ? 'Hinzugefügt' : (z.symbol == '-' ? 'Gelöscht' : (z.symbol == '!' ? 'Geändert' : 'Unverändert'))}"></td>
+                <td th:utext="${z.kindNameHtml}"></td>
+                <td th:utext="${z.betragHtml}"></td>
+              </tr>
+            </tbody>
+          </table>
+        </div>
+      </div>
+    </div>
+  </main>
+
+  <script>
+    // Cookie Verwaltung
+    function setCookie(name, value, days) {
+      const expires = new Date();
+      expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000));
+      document.cookie = name + '=' + value + ';expires=' + expires.toUTCString() + ';path=/';
+    }
+
+    function getCookie(name) {
+      const nameEQ = name + '=';
+      const ca = document.cookie.split(';');
+      for(let i = 0; i < ca.length; i++) {
+        let c = ca[i];
+        while (c.charAt(0) === ' ') c = c.substring(1, c.length);
+        if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
+      }
+      return null;
+    }
+
+    // Keyboard support for toggles
+    function handleKeyPress(event, sectionId) {
+      if (event.key === 'Enter' || event.key === ' ') {
+        event.preventDefault();
+        toggleSection(sectionId);
+      }
+    }
+
+    // Keyboard support for table rows
+    function handleRowKeyPress(event, row) {
+      if (event.key === 'Enter' || event.key === ' ') {
+        event.preventDefault();
+        row.click();
+      }
+    }
+
+    // Farbwahl-Funktionen
+    function toggleColorPicker() {
+      const popup = document.getElementById('colorPickerPopup');
+      const button = document.querySelector('.color-picker-toggle');
+      const isHidden = popup.classList.contains('hidden');
+      
+      popup.classList.toggle('hidden');
+      button.setAttribute('aria-expanded', !isHidden);
+      
+      if (!isHidden) {
+        // Focus the select when opening
+        document.getElementById('colorSchemeSelect').focus();
+      }
+    }
+
+    function updateColorNames(scheme) {
+      const colorNames = {
+        'standard': { added: 'grün', removed: 'rot', changed: 'gelb' },
+        'ampel': { added: 'grün', removed: 'rot', changed: 'gelb' },
+        'alternative': { added: 'blau', removed: 'pink', changed: 'orange' }
+      };
+      
+      const names = colorNames[scheme] || colorNames['standard'];
+      
+      document.getElementById('addedColorName').textContent = names.added;
+      document.getElementById('removedColorName').textContent = names.removed;
+      document.getElementById('changedColorName').textContent = names.changed;
+    }
+
+    function changeColorScheme(scheme) {
+      const html = document.documentElement;
+      
+      // Entferne alle existierenden Farbschema-Attribute
+      html.removeAttribute('data-color-scheme');
+      
+      // Setze das neue Farbschema (außer für "standard")
+      if (scheme !== 'standard') {
+        html.setAttribute('data-color-scheme', scheme);
+      }
+      
+      // Aktualisiere die Farbnamen in der Legende
+      updateColorNames(scheme);
+      
+      // Speichere die Auswahl im Cookie (365 Tage gültig)
+      setCookie('colorScheme', scheme, 365);
+      
+      // Schließe das Popup nach der Auswahl
+      const popup = document.getElementById('colorPickerPopup');
+      const button = document.querySelector('.color-picker-toggle');
+      popup.classList.add('hidden');
+      button.setAttribute('aria-expanded', 'false');
+    }
+
+    function initializeColorScheme() {
+      // Lade das gespeicherte Farbschema aus dem Cookie (Standard: "standard" = Ihre ursprünglichen Farben)
+      const savedScheme = getCookie('colorScheme') || 'standard';
+      
+      // Setze das Farbschema
+      const html = document.documentElement;
+      if (savedScheme !== 'standard') {
+        html.setAttribute('data-color-scheme', savedScheme);
+      }
+      
+      // Aktualisiere die Farbnamen
+      updateColorNames(savedScheme);
+      
+      // Setze den Wert in der Dropdown-Box
+      const select = document.getElementById('colorSchemeSelect');
+      if (select) {
+        select.value = savedScheme;
+      }
+    }
+
+    // Escape key support for popup
+    document.addEventListener('keydown', function(event) {
+      if (event.key === 'Escape') {
+        const popup = document.getElementById('colorPickerPopup');
+        const button = document.querySelector('.color-picker-toggle');
+        if (!popup.classList.contains('hidden')) {
+          popup.classList.add('hidden');
+          button.setAttribute('aria-expanded', 'false');
+          button.focus();
+        }
+      }
+    });
+
+    // Schließe das Popup wenn außerhalb geklickt wird
+    document.addEventListener('click', function(event) {
+      const container = document.querySelector('.color-picker-container');
+      const popup = document.getElementById('colorPickerPopup');
+      const button = document.querySelector('.color-picker-toggle');
+      
+      if (container && !container.contains(event.target)) {
+        popup.classList.add('hidden');
+        button.setAttribute('aria-expanded', 'false');
+      }
+    });
+
+    // Toggle-Funktion für ausklappbare Sektionen
+    function toggleSection(sectionId) {
+      const content = document.getElementById(sectionId + '-content');
+      const icon = document.getElementById(sectionId + '-icon');
+      const header = content.parentElement.querySelector('.section-header');
+      
+      if (content.classList.contains('collapsed')) {
+        content.classList.remove('collapsed');
+        icon.classList.remove('collapsed');
+        icon.textContent = '▼';
+        header.setAttribute('aria-expanded', 'true');
+      } else {
+        content.classList.add('collapsed');
+        icon.classList.add('collapsed');
+        icon.textContent = '▶';
+        header.setAttribute('aria-expanded', 'false');
+      }
+    }
+
+    document.addEventListener('DOMContentLoaded', function() {
+      // Initialisiere das Farbschema
+      initializeColorScheme();
+      
+      // Add click event listeners to all person rows
+      const personRows = document.querySelectorAll('.person-row');
+      personRows.forEach(function(row) {
+        row.addEventListener('click', function() {
+          const personId = this.dataset.personId;
+          if (personId) {
+            document.getElementById('selectedPersonId').value = personId;
+            document.getElementById('personForm').submit();
+          }
+        });
+      });
+    });
+  </script>
+
+  <!-- Screen reader only styles -->
+  <style>
+    .sr-only {
+      position: absolute;
+      width: 1px;
+      height: 1px;
+      padding: 0;
+      margin: -1px;
+      overflow: hidden;
+      clip: rect(0, 0, 0, 0);
+      white-space: nowrap;
+      border: 0;
+    }
+  </style>
+</body>
+</html>

+ 266 - 75
src/main/resources/templates/vergleich.html

@@ -2,11 +2,16 @@
 <html lang="de" xmlns:th="http://www.thymeleaf.org">
 <head><meta charset="UTF-8"/><title>DIWO Vergleich – Haushaltsmitglieder/Einkommen</title><link rel="stylesheet" href="/styles.css"/></head>
 <body>
+
   <header class="diwo-header">
     <div class="header-row-1">
-      <div class="berlin-logo">
-        <span class="berlin-text">BERLIN</span>
-        <span class="berlin-bear">🐻</span>
+      <div class="kunde-logo">
+        <div th:if="${activeKunde != null and !#strings.isEmpty(activeKunde)}" class="kunde-logo-container">
+          <img th:src="@{'/images/' + ${#strings.toLowerCase(activeKunde)} + '.jpg'}" 
+               th:alt="${activeKunde} + ' Logo'" 
+               class="kunde-logo-image"
+               th:onerror="'this.style.display=\\'none\\''"/>
+        </div>
       </div>
       <div class="diwo-title">
         <h1>DIWO</h1>
@@ -41,7 +46,6 @@
         <span th:if="${!#strings.isEmpty(stammvorgangRight.beginn) and !#strings.isEmpty(stammvorgangRight.ende)}">
           (<span th:text="${stammvorgangRight.beginn}">01.01.2026</span> - <span th:text="${stammvorgangRight.ende}">31.12.2027</span>)
         </span>
-        <span> - umzustellen</span>
       </div>
       
       <div class="zeitraum-controls">
@@ -52,6 +56,7 @@
           <input type="hidden" name="rightJson" th:value="${rightJson}"/>
           <input type="hidden" name="leftVorgangId" th:value="${leftVorgangId}"/>
           <input type="hidden" name="leftZeitraumId" th:value="${leftZeitraumId}"/>
+          <input type="hidden" name="kunde" th:value="${activeKunde}"/>
           
           <label>Zeitraum:</label>
           <select name="rightZeitraumId" class="zeitraum-select">
@@ -67,43 +72,70 @@
 
   <!-- Vergleichslegende -->
   <section class="vergleich-legende">
+    <div class="legende-title">
+      <strong>Legende:</strong>
+    </div>
+    <div class="legende-item">
+      <span class="sym plus">+</span> <span>hinzugefügt (<span id="addedColorName">grün</span>)</span>
+    </div>
+    <div class="legende-spacer">|</div>
     <div class="legende-item">
-      <span class="sym plus">+</span> <span>hinzugefügt (grün)</span>
+      <span class="sym minus">−</span> <span>gelöscht (<span id="removedColorName">rot</span>)</span>
     </div>
     <div class="legende-spacer">|</div>
     <div class="legende-item">
-      <span class="sym minus">−</span> <span>gelöscht (rot)</span>
+      <span class="sym warn">!</span> <span>geändert (<span id="changedColorName">gelb</span>)</span>
     </div>
     <div class="legende-spacer">|</div>
     <div class="legende-item">
-      <span class="sym warn">!</span> <span>geändert (gelb)</span>
+      <span class="selected-indicator-text">ausgewählte Person (blauer Rahmen)</span>
+    </div>
+    
+    <!-- Farbwahl -->
+    <div class="color-picker-container">
+      <button class="color-picker-toggle" onclick="toggleColorPicker()">⚙️</button>
+      <div class="color-picker-popup hidden" id="colorPickerPopup">
+        <label>Farbwahl:</label>
+        <select id="colorSchemeSelect" class="color-scheme-select" onchange="changeColorScheme(this.value)">
+          <option value="standard">Standard</option>
+          <option value="ampel">Ampelfarben (neon)</option>
+          <option value="alternative">Pastell</option>
+        </select>
+      </div>
     </div>
   </section>
 
   <!-- Haushaltsmitglieder - klickbare Zeilen -->
-  <h2>Haushaltsmitglieder</h2>
-  <table>
-    <thead>
-      <tr>
-        <th>Δ</th><th>Name</th><th>Geburtsdatum</th><th>Familienstand</th>
-        <th>Geschlecht</th><th>Verwandtschaft</th><th>Sozialstellung</th><th>Kind</th>
-      </tr>
-    </thead>
-    <tbody>
-      <tr th:each="r : ${mitgliederRows}"
-          th:class="'person-row ' + ${r.rowClass} + ((${r.personId} == ${personId}) ? ' person-selected' : '')"
-          th:data-person-id="${r.personId}">
-        <td class="sym" th:classappend="' ' + ${r.symbolClass}" th:text="${r.symbol}"></td>
-        <td th:utext="${r.nameHtml}"></td>
-        <td th:utext="${r.geburtsdatumHtml}"></td>
-        <td th:utext="${r.familienstandHtml}"></td>
-        <td th:utext="${r.geschlechtHtml}"></td>
-        <td th:utext="${r.verwandtschaftHtml}"></td>
-        <td th:utext="${r.sozialstellungHtml}"></td>
-        <td th:utext="${r.istKindHtml}"></td>
-      </tr>
-    </tbody>
-  </table>
+  <div class="section-container">
+    <div class="section-header" onclick="toggleSection('haushaltsmitglieder')">
+      <span class="toggle-icon" id="haushaltsmitglieder-icon">▼</span>
+      <h3>Haushaltsmitglieder</h3>
+    </div>
+    <div class="section-content" id="haushaltsmitglieder-content">
+      <table>
+        <thead>
+          <tr>
+            <th>Δ</th><th>Name</th><th>Geburtsdatum</th><th>Familienstand</th>
+            <th>Geschlecht</th><th>Verwandtschaft</th><th>Sozialstellung</th><th>Kind</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr th:each="r : ${mitgliederRows}"
+              th:class="'person-row ' + ${r.rowClass} + ((${r.personId} == ${personId}) ? ' person-selected' : '')"
+              th:data-person-id="${r.personId}">
+            <td class="sym" th:classappend="' ' + ${r.symbolClass}" th:text="${r.symbol}"></td>
+            <td th:utext="${r.nameHtml}"></td>
+            <td th:utext="${r.geburtsdatumHtml}"></td>
+            <td th:utext="${r.familienstandHtml}"></td>
+            <td th:utext="${r.geschlechtHtml}"></td>
+            <td th:utext="${r.verwandtschaftHtml}"></td>
+            <td th:utext="${r.sozialstellungHtml}"></td>
+            <td th:utext="${r.istKindHtml}"></td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+  </div>
 
   <!-- Hidden form for person selection -->
   <form id="personForm" method="post" action="/vergleich" style="display: none;">
@@ -114,57 +146,216 @@
     <input type="hidden" name="leftVorgangId" th:value="${leftVorgangId}"/>
     <input type="hidden" name="leftZeitraumId" th:value="${leftZeitraumId}"/>
     <input type="hidden" name="rightZeitraumId" th:value="${rightZeitraumId}"/>
+    <input type="hidden" name="kunde" th:value="${activeKunde}"/>
     <input type="hidden" name="personId" id="selectedPersonId"/>
   </form>
 
-  <h2>Details zur Person</h2>
-  <h3 th:text="${personLabel}">—</h3>
-
-  <h2>Einkommen</h2>
-  <table>
-    <thead><tr><th>Δ</th><th>Art</th><th>Frequenz</th><th>Betrag</th><th>Zuschlag</th><th>Abschlag</th><th>Werbungskosten</th><th>Berechnungsart</th></tr></thead>
-    <tbody>
-      <tr th:each="z : ${einkommenZeilen}" th:class="${z.rowClass}">
-        <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"></td>
-        <td th:utext="${z.artHtml}"></td>
-        <td th:utext="${z.frequenzHtml}"></td>
-        <td th:utext="${z.betragHtml}"></td>
-        <td th:utext="${z.zuschlagHtml}"></td>
-        <td th:utext="${z.abschlagHtml}"></td>
-        <td th:utext="${z.werbungskostenHtml}"></td>
-        <td th:utext="${z.berechnungsartHtml}"></td>
-      </tr>
-    </tbody>
-  </table>
-
-  <h2>Freibeträge</h2>
-  <table>
-    <thead><tr><th>Δ</th><th>Art</th><th>Frequenz</th><th>Betrag</th><th>Frist</th></tr></thead>
-    <tbody>
-      <tr th:each="z : ${freibetragZeilen}" th:class="${z.rowClass}">
-        <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"></td>
-        <td th:utext="${z.artHtml}"></td>
-        <td th:utext="${z.frequenzHtml}"></td>
-        <td th:utext="${z.betragHtml}"></td>
-        <td th:utext="${z.fristHtml}"></td>
-      </tr>
-    </tbody>
-  </table>
-
-  <h2>Kinderbetreuungskosten</h2>
-  <table>
-    <thead><tr><th>Δ</th><th>Kind</th><th>Betrag</th></tr></thead>
-    <tbody>
-      <tr th:each="z : ${kitaZeilen}" th:class="${z.rowClass}">
-        <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"></td>
-        <td th:utext="${z.kindNameHtml}"></td>
-        <td th:utext="${z.betragHtml}"></td>
-      </tr>
-    </tbody>
-  </table>
+  <h2>Details zu <span th:if="${personLabel != null and !#strings.isEmpty(personLabel) and personLabel != '(keine Person)'}"><span th:text="${personLabel}"> - </span></span></h2>
+  <!-- Einkommen -->
+  <div class="section-container">
+    <div class="section-header" onclick="toggleSection('einkommen')">
+      <span class="toggle-icon" id="einkommen-icon">▼</span>
+      <h3>Einkommen</h3>
+    </div>
+    <div class="section-content" id="einkommen-content">
+      <table>
+        <thead><tr><th>Δ</th><th>Art</th><th>Frequenz</th><th>Betrag</th><th>Zuschlag</th><th>Abschlag</th><th>Werbungskosten</th><th>Berechnungsart</th></tr></thead>
+        <tbody>
+          <tr th:each="z : ${einkommenZeilen}" th:class="${z.rowClass}">
+            <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"></td>
+            <td th:utext="${z.artHtml}"></td>
+            <td th:utext="${z.frequenzHtml}"></td>
+            <td th:utext="${z.betragHtml}"></td>
+            <td th:utext="${z.zuschlagHtml}"></td>
+            <td th:utext="${z.abschlagHtml}"></td>
+            <td th:utext="${z.werbungskostenHtml}"></td>
+            <td th:utext="${z.berechnungsartHtml}"></td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+  </div>
+
+  <div class="freibetrag-container">
+    <div class="freibetrag-section">
+      <div class="section-container">
+        <div class="section-header" onclick="toggleSection('pauschale')">
+          <span class="toggle-icon" id="pauschale-icon">▼</span>
+          <h3>Pauschale Abzüge</h3>
+        </div>
+        <div class="section-content" id="pauschale-content">
+          <table>
+            <thead><tr><th>Δ</th><th>Art</th><th>Frequenz</th><th>Betrag</th></tr></thead>
+            <tbody>
+              <tr th:each="z : ${freibetragZeilen}" th:if="${z.typ == 'P'}" th:class="${z.rowClass}">
+                <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"></td>
+                <td th:utext="${z.artHtml}"></td>
+                <td th:utext="${z.frequenzHtml}"></td>
+                <td th:utext="${z.betragHtml}"></td>
+              </tr>
+            </tbody>
+          </table>
+        </div>
+      </div>
+    </div>
+    
+    <div class="freibetrag-section">
+      <div class="section-container">
+        <div class="section-header" onclick="toggleSection('freibetraege')">
+          <span class="toggle-icon" id="freibetraege-icon">▼</span>
+          <h3>Freibeträge</h3>
+        </div>
+        <div class="section-content" id="freibetraege-content">
+          <table>
+            <thead><tr><th>Δ</th><th>Art</th><th>Frequenz</th><th>Betrag</th><th>Frist</th></tr></thead>
+            <tbody>
+              <tr th:each="z : ${freibetragZeilen}" th:if="${z.typ == 'F'}" th:class="${z.rowClass}">
+                <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"></td>
+                <td th:utext="${z.artHtml}"></td>
+                <td th:utext="${z.frequenzHtml}"></td>
+                <td th:utext="${z.betragHtml}"></td>
+                <td th:utext="${z.fristHtml}"></td>
+              </tr>
+            </tbody>
+          </table>
+        </div>
+      </div>
+    </div>
+  </div>
+
+  <!-- Kinderbetreuungskosten -->
+  <div class="kinderbetreuung-container">
+    <div class="section-container">
+      <div class="section-header" onclick="toggleSection('kinderbetreuung')">
+        <span class="toggle-icon" id="kinderbetreuung-icon">▼</span>
+        <h3>Kinderbetreuungskosten</h3>
+      </div>
+      <div class="section-content" id="kinderbetreuung-content">
+        <table>
+          <thead><tr><th>Δ</th><th>Kind</th><th>Betrag</th></tr></thead>
+          <tbody>
+            <tr th:each="z : ${kitaZeilen}" th:class="${z.rowClass}">
+              <td class="sym" th:classappend="' ' + ${z.symbolClass}" th:text="${z.symbol}"></td>
+              <td th:utext="${z.kindNameHtml}"></td>
+              <td th:utext="${z.betragHtml}"></td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+    </div>
+  </div>
 
   <script>
+    // Cookie Verwaltung
+    function setCookie(name, value, days) {
+      const expires = new Date();
+      expires.setTime(expires.getTime() + (days * 24 * 60 * 60 * 1000));
+      document.cookie = name + '=' + value + ';expires=' + expires.toUTCString() + ';path=/';
+    }
+
+    function getCookie(name) {
+      const nameEQ = name + '=';
+      const ca = document.cookie.split(';');
+      for(let i = 0; i < ca.length; i++) {
+        let c = ca[i];
+        while (c.charAt(0) === ' ') c = c.substring(1, c.length);
+        if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
+      }
+      return null;
+    }
+
+    // Farbwahl-Funktionen
+    function toggleColorPicker() {
+      const popup = document.getElementById('colorPickerPopup');
+      popup.classList.toggle('hidden');
+    }
+
+    function updateColorNames(scheme) {
+      const colorNames = {
+        'standard': { added: 'grün', removed: 'rot', changed: 'gelb' },
+        'ampel': { added: 'grün', removed: 'rot', changed: 'gelb' },
+        'alternative': { added: 'blau', removed: 'pink', changed: 'orange' }
+      };
+      
+      const names = colorNames[scheme] || colorNames['standard'];
+      
+      document.getElementById('addedColorName').textContent = names.added;
+      document.getElementById('removedColorName').textContent = names.removed;
+      document.getElementById('changedColorName').textContent = names.changed;
+    }
+
+    function changeColorScheme(scheme) {
+      const html = document.documentElement;
+      
+      // Entferne alle existierenden Farbschema-Attribute
+      html.removeAttribute('data-color-scheme');
+      
+      // Setze das neue Farbschema (außer für "standard")
+      if (scheme !== 'standard') {
+        html.setAttribute('data-color-scheme', scheme);
+      }
+      
+      // Aktualisiere die Farbnamen in der Legende
+      updateColorNames(scheme);
+      
+      // Speichere die Auswahl im Cookie (365 Tage gültig)
+      setCookie('colorScheme', scheme, 365);
+      
+      // Schließe das Popup nach der Auswahl
+      const popup = document.getElementById('colorPickerPopup');
+      popup.classList.add('hidden');
+    }
+
+    function initializeColorScheme() {
+      // Lade das gespeicherte Farbschema aus dem Cookie (Standard: "standard" = Ihre ursprünglichen Farben)
+      const savedScheme = getCookie('colorScheme') || 'standard';
+      
+      // Setze das Farbschema
+      const html = document.documentElement;
+      if (savedScheme !== 'standard') {
+        html.setAttribute('data-color-scheme', savedScheme);
+      }
+      
+      // Aktualisiere die Farbnamen
+      updateColorNames(savedScheme);
+      
+      // Setze den Wert in der Dropdown-Box
+      const select = document.getElementById('colorSchemeSelect');
+      if (select) {
+        select.value = savedScheme;
+      }
+    }
+
+    // Schließe das Popup wenn außerhalb geklickt wird
+    document.addEventListener('click', function(event) {
+      const container = document.querySelector('.color-picker-container');
+      const popup = document.getElementById('colorPickerPopup');
+      
+      if (container && !container.contains(event.target)) {
+        popup.classList.add('hidden');
+      }
+    });
+
+    // Toggle-Funktion für ausklappbare Sektionen
+    function toggleSection(sectionId) {
+      const content = document.getElementById(sectionId + '-content');
+      const icon = document.getElementById(sectionId + '-icon');
+      
+      if (content.classList.contains('collapsed')) {
+        content.classList.remove('collapsed');
+        icon.classList.remove('collapsed');
+        icon.textContent = '▼';
+      } else {
+        content.classList.add('collapsed');
+        icon.classList.add('collapsed');
+        icon.textContent = '▶';
+      }
+    }
+
     document.addEventListener('DOMContentLoaded', function() {
+      // Initialisiere das Farbschema
+      initializeColorScheme();
+      
       // Add click event listeners to all person rows
       const personRows = document.querySelectorAll('.person-row');
       personRows.forEach(function(row) {