diff --git a/BewerbungWebseite/Models/SiteContent/AboutSection.cs b/BewerbungWebseite/Models/SiteContent/AboutSection.cs
new file mode 100644
index 0000000..7f0f6e0
--- /dev/null
+++ b/BewerbungWebseite/Models/SiteContent/AboutSection.cs
@@ -0,0 +1,41 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace BewerbungsSeite.Models.SiteContent;
+
+/// Bereich "Ueber mich" (section id="about").
+public class AboutSection
+{
+ [StringLength(ContentLimits.Url, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Image { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.AltText, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string ImageAlt { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.Kicker, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Kicker { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.Heading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string TitleLine1 { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.Heading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string TitleLine2 { get; set; } = string.Empty;
+
+ /// Hervorgehobener Teil der zweiten Titelzeile.
+ [StringLength(ContentLimits.Heading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string TitleLine2Highlight { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.ParagraphText, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Text { get; set; } = string.Empty;
+
+ public List Facts { get; set; } = new();
+}
+
+/// Eine Zeile der Kurzuebersicht rechts unter dem Text.
+public class FactItem
+{
+ [StringLength(ContentLimits.FactLabel, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Label { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.FactValue, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Value { get; set; } = string.Empty;
+}
diff --git a/BewerbungWebseite/Models/SiteContent/ContactSection.cs b/BewerbungWebseite/Models/SiteContent/ContactSection.cs
new file mode 100644
index 0000000..02cdfbb
--- /dev/null
+++ b/BewerbungWebseite/Models/SiteContent/ContactSection.cs
@@ -0,0 +1,43 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace BewerbungsSeite.Models.SiteContent;
+
+///
+/// Bereich "Kontakt" (section id="contact"). Enthaelt nur die Texte;
+/// das Formular selbst bleibt Bestandteil der Seite und ist nicht editierbar.
+///
+public class ContactSection
+{
+ [StringLength(ContentLimits.Kicker, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Kicker { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.Heading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Title { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.Heading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string TitleHighlight { get; set; } = string.Empty;
+
+ public List InfoBoxes { get; set; } = new();
+
+ /// Einleitungstext oberhalb der Formularfelder.
+ [StringLength(ContentLimits.FormIntro, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string FormHeading { get; set; } = string.Empty;
+}
+
+/// Eine Kontaktangabe (Standort, Telefon, E-Mail, ...).
+public class ContactInfoItem
+{
+ /// CSS-Klasse des Icons, z. B. "flaticon-call".
+ [StringLength(ContentLimits.IconClass, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Icon { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.FactLabel, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Title { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.FactValue, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Text { get; set; } = string.Empty;
+
+ /// Optional: macht den Text zum Link, z. B. "tel:0797367355".
+ [StringLength(ContentLimits.Url, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string LinkUrl { get; set; } = string.Empty;
+}
diff --git a/BewerbungWebseite/Models/SiteContent/DocumentsSection.cs b/BewerbungWebseite/Models/SiteContent/DocumentsSection.cs
new file mode 100644
index 0000000..ff22adb
--- /dev/null
+++ b/BewerbungWebseite/Models/SiteContent/DocumentsSection.cs
@@ -0,0 +1,34 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace BewerbungsSeite.Models.SiteContent;
+
+/// Bereich "Dokumente und Zertifikate" (section id="documents").
+public class DocumentsSection
+{
+ [StringLength(ContentLimits.Kicker, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Kicker { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.Heading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Title { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.Heading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string TitleHighlight { get; set; } = string.Empty;
+
+ public List Items { get; set; } = new();
+}
+
+/// Ein Dokument bzw. Zertifikat mit Link auf die PDF-Datei.
+public class DocumentItem
+{
+ [StringLength(ContentLimits.CardTitle, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Title { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.ParagraphText, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Description { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.Url, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string FileUrl { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.ButtonLabel, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string LinkText { get; set; } = string.Empty;
+}
diff --git a/BewerbungWebseite/Models/SiteContent/ExperienceSection.cs b/BewerbungWebseite/Models/SiteContent/ExperienceSection.cs
new file mode 100644
index 0000000..c048db0
--- /dev/null
+++ b/BewerbungWebseite/Models/SiteContent/ExperienceSection.cs
@@ -0,0 +1,43 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace BewerbungsSeite.Models.SiteContent;
+
+/// Bereich "Berufliche Erfahrung" (section id="experience").
+public class ExperienceSection
+{
+ [StringLength(ContentLimits.Kicker, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Kicker { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.Heading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Title { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.Heading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string TitleHighlight { get; set; } = string.Empty;
+
+ public List Items { get; set; } = new();
+
+ [StringLength(ContentLimits.ButtonLabel, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string ToggleMoreText { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.ButtonLabel, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string ToggleLessText { get; set; } = string.Empty;
+}
+
+/// Ein Eintrag der Erfahrungs-Timeline.
+public class ExperienceItem
+{
+ [StringLength(ContentLimits.CardTitle, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Title { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.Meta, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Meta { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.DetailHeading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string DetailHeading { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.ParagraphText, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Text { get; set; } = string.Empty;
+
+ /// true = erst nach Klick auf "Mehr erfahren" sichtbar.
+ public bool Collapsed { get; set; }
+}
diff --git a/BewerbungWebseite/Models/SiteContent/GoalsSection.cs b/BewerbungWebseite/Models/SiteContent/GoalsSection.cs
new file mode 100644
index 0000000..54f0963
--- /dev/null
+++ b/BewerbungWebseite/Models/SiteContent/GoalsSection.cs
@@ -0,0 +1,35 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace BewerbungsSeite.Models.SiteContent;
+
+/// Bereich "Meine Ziele" (section id="goals").
+public class GoalsSection
+{
+ [StringLength(ContentLimits.Kicker, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Kicker { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.Heading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Title { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.Heading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string TitleHighlight { get; set; } = string.Empty;
+
+ public List Items { get; set; } = new();
+}
+
+/// Eine Ziel-Kachel.
+public class GoalItem
+{
+ /// CSS-Klasse des Icons, z. B. "flaticon-growth".
+ [StringLength(ContentLimits.IconClass, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Icon { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.TileTitle, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Title { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.TileText, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Text { get; set; } = string.Empty;
+
+ /// true = hervorgehobene Kachel (CSS-Klasse "active").
+ public bool Highlighted { get; set; }
+}
diff --git a/BewerbungWebseite/Models/SiteContent/HeroSection.cs b/BewerbungWebseite/Models/SiteContent/HeroSection.cs
new file mode 100644
index 0000000..2cb22e8
--- /dev/null
+++ b/BewerbungWebseite/Models/SiteContent/HeroSection.cs
@@ -0,0 +1,45 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace BewerbungsSeite.Models.SiteContent;
+
+/// Startbereich der Seite (section id="hero").
+public class HeroSection : IValidatableObject
+{
+ [StringLength(ContentLimits.Kicker, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Kicker { get; set; } = string.Empty;
+
+ /// Grosse Titelzeilen. Die letzte Zeile wird farblich hervorgehoben.
+ public List TitleLines { get; set; } = new();
+
+ [StringLength(ContentLimits.ParagraphText, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Intro { get; set; } = string.Empty;
+
+ public LinkItem PrimaryButton { get; set; } = new();
+ public LinkItem SecondaryButton { get; set; } = new();
+
+ /// Kleine Links unter den Buttons (LinkedIn, E-Mail, ...).
+ public List SocialLinks { get; set; } = new();
+
+ [StringLength(ContentLimits.Url, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string PortraitImage { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.AltText, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string PortraitAlt { get; set; } = string.Empty;
+
+ ///
+ /// Prueft die Titelzeilen einzeln: [StringLength] laesst sich nicht direkt
+ /// auf eine List<string> anwenden, deshalb hier von Hand.
+ ///
+ public IEnumerable Validate(ValidationContext validationContext)
+ {
+ for (var i = 0; i < TitleLines.Count; i++)
+ {
+ if (TitleLines[i]?.Length > ContentLimits.Heading)
+ {
+ yield return new ValidationResult(
+ $"Titelzeile {i + 1} darf höchstens {ContentLimits.Heading} Zeichen lang sein.",
+ [nameof(TitleLines)]);
+ }
+ }
+ }
+}
diff --git a/BewerbungWebseite/Models/SiteContent/ProjectsSection.cs b/BewerbungWebseite/Models/SiteContent/ProjectsSection.cs
new file mode 100644
index 0000000..2cb0b51
--- /dev/null
+++ b/BewerbungWebseite/Models/SiteContent/ProjectsSection.cs
@@ -0,0 +1,46 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace BewerbungsSeite.Models.SiteContent;
+
+/// Bereich "Projekte" (section id="projects").
+public class ProjectsSection
+{
+ [StringLength(ContentLimits.Kicker, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Kicker { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.Heading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Title { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.Heading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string TitleHighlight { get; set; } = string.Empty;
+
+ public List Items { get; set; } = new();
+}
+
+/// Eine Projektkarte.
+public class ProjectItem
+{
+ [StringLength(ContentLimits.Url, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Image { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.AltText, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string ImageAlt { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.CardTitle, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Title { get; set; } = string.Empty;
+
+ /// Optionaler fett gesetzter Auftakt vor der Beschreibung, z. B. "Ziel:".
+ [StringLength(ContentLimits.DetailHeading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string DescriptionLead { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.ParagraphText, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Description { get; set; } = string.Empty;
+
+ /// Zwischentitel mit Text ("TECH:", "Meine Arbeit:", ...).
+ public List Details { get; set; } = new();
+
+ public List Links { get; set; } = new();
+
+ /// true = breite Karte ueber die volle Breite mit Bild links.
+ public bool Wide { get; set; }
+}
diff --git a/BewerbungWebseite/Models/SiteContent/SkillsSection.cs b/BewerbungWebseite/Models/SiteContent/SkillsSection.cs
new file mode 100644
index 0000000..d2ae924
--- /dev/null
+++ b/BewerbungWebseite/Models/SiteContent/SkillsSection.cs
@@ -0,0 +1,39 @@
+using System.ComponentModel.DataAnnotations;
+
+namespace BewerbungsSeite.Models.SiteContent;
+
+/// Bereich "Kompetenzen" (section id="skills").
+public class SkillsSection
+{
+ [StringLength(ContentLimits.Kicker, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Kicker { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.Heading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Title { get; set; } = string.Empty;
+
+ /// Hervorgehobener Teil des Titels.
+ [StringLength(ContentLimits.Heading, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string TitleHighlight { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.ParagraphText, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Intro { get; set; } = string.Empty;
+
+ public List Items { get; set; } = new();
+}
+
+/// Eine Kompetenz-Kachel.
+public class SkillItem
+{
+ /// CSS-Klasse des Icons, z. B. "flaticon-pen".
+ [StringLength(ContentLimits.IconClass, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Icon { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.TileTitle, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Title { get; set; } = string.Empty;
+
+ [StringLength(ContentLimits.TileText, ErrorMessage = ContentLimits.TooLongMessage)]
+ public string Text { get; set; } = string.Empty;
+
+ /// true = hervorgehobene Kachel (CSS-Klasse "active").
+ public bool Highlighted { get; set; }
+}