diff --git a/curriculum/challenges/german/14-responsive-web-design-22/build-a-product-landing-page-project/build-a-product-landing-page.md b/curriculum/challenges/german/14-responsive-web-design-22/build-a-product-landing-page-project/build-a-product-landing-page.md
new file mode 100644
index 00000000000..fa4add5724a
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/build-a-product-landing-page-project/build-a-product-landing-page.md
@@ -0,0 +1,467 @@
+---
+id: 587d78af367417b2b2512b04
+title: Erstelle eine Landingpage für ein Produkt
+challengeType: 14
+forumTopicId: 301144
+dashedName: build-a-product-landing-page
+---
+
+# --description--
+
+**Aufgabe:** Erstelle eine Anwendung, die eine ähnliche Funktionalität wie https://product-landing-page.freecodecamp.rocks aufweist.
+
+**User Stories:**
+
+1. Deine Produkt-Landingpage sollte ein `header`-Element mit einer entsprechenden `id="header"` haben
+1. Du kannst ein Bild innerhalb des `header`-Elements mit einer entsprechenden `id="header-img"` (Ein Logo würde hier ein gutes Bild abgeben) sehen
+1. Innerhalb des `#header`-Elements siehst du ein `nav`-Element mit dazugehöriger `id="nav-bar"`
+1. Du kannst mindestens drei anklickbare Elemente innerhalb des `nav`-Elements sehen, jedes mit der `nav-link`-Klasse
+1. Wenn du den `.nav-link`-Button im `nav`-Element drückst, wirst du zu dem entsprechenden Bereich auf der Landingpage geführt
+1. Du kannst ein eingebettetes Produktvideo mit `id="video"` ansehen
+1. Deine Landingpage sollte über ein `form`-Element mit entsprechender `id="form"` verfügen
+1. Innerhalb des Formulars gibt es ein `input`-Feld mit `id="email"`, in das du deine E-Mail-Adresse eingeben kannst
+1. Das `#email`-Eingabefeld sollte über einen Platzhaltertext verfügen, um Nutzer über den Zweck des Felds aufzuklären
+1. Das `#email`-Eingabefeld findet durch HTML5-Validierung heraus, ob es sich beim eingegebenen Text auch wirklich um eine E-Mail-Adresse handelt
+1. Innerhalb des Formulars gibt es ein Feld zum Abschicken des `input` mit einer entsprechenden `id="submit"`
+1. Wenn du auf das `#submit`-Element klickst, wird die E-Mail an eine statische Seite weitergeleitet (Verwende diese Pseudo-URL: `https://www.freecodecamp.com/email-submit`)
+1. Die Navigationsleiste sollte sich immer am oberen Rand des Ansichtsfensters befinden
+1. Deine Produkt-Landingpage sollte mindestens eine Medienabfrage (Media Query) haben
+1. Deine Produkt-Landingpage sollte die CSS-Flexbox mindestens einmal verwenden
+
+Erfülle die folgenden User Stories und bestehe alle Tests, um dieses Projekt abzuschließen. Gib dem Ganzen deinen persönlichen Stil. Viel Spaß beim Programmieren!
+
+**Hinweis:** Füge unbedingt `` in dein HTML ein, um dein Stylesheet zu verlinken und dein CSS anzuwenden
+
+# --hints--
+
+Du solltest ein `header`-Element mit einer `id` von `header` haben.
+
+```js
+const el = document.getElementById('header')
+assert(!!el && el.tagName === 'HEADER')
+```
+
+Du solltest ein `img`-Element mit einer `id` von `header-img` haben.
+
+```js
+const el = document.getElementById('header-img')
+assert(!!el && el.tagName === 'IMG')
+```
+
+Dein `#header-img` sollte ein Nachfahre (descendant) des `#header` sein.
+
+```js
+const els = document.querySelectorAll('#header #header-img')
+assert(els.length > 0)
+```
+
+Dein `#header-img` sollte über ein `src`-Attribut verfügen.
+
+```js
+const el = document.getElementById('header-img')
+assert(!!el && !!el.src)
+```
+
+Die `src` deines `#header-img` sollte auf einen gültigen URL-Wert (beginnend mit `http`) gesetzt sein.
+
+```js
+const el = document.getElementById('header-img')
+assert(!!el && /^http/.test(el.src))
+```
+
+Du solltest ein `nav`-Element mit einer `id` von `nav-bar` haben.
+
+```js
+const el = document.getElementById('nav-bar')
+assert(!!el && el.tagName === 'NAV')
+```
+
+Deine `#nav-bar` sollte ein Nachfahre des `#header` sein.
+
+```js
+const els = document.querySelectorAll('#header #nav-bar')
+assert(els.length > 0)
+```
+
+Du solltest mindestens 3 `.nav-link`-Elemente innerhalb des `#nav-bar` haben.
+
+```js
+const els = document.querySelectorAll('#nav-bar .nav-link')
+assert(els.length >= 3)
+```
+
+Jedes `.nav-link`-Element sollte über ein `href`-Attribut verfügen.
+
+```js
+const els = document.querySelectorAll('.nav-link')
+els.forEach(el => {
+ if (!el.href) assert(false)
+})
+assert(els.length > 0)
+```
+
+Jedes `.nav-link`-Element sollte mit einem entsprechenden Element auf der Landingpage verknüpft sein (hat ein `href` mit dem Wert der ID eines anderen Elements, wie bspw. `#footer`).
+
+```js
+const els = document.querySelectorAll('.nav-link')
+els.forEach(el => {
+ const linkDestination = el.getAttribute('href').slice(1)
+ if (!document.getElementById(linkDestination)) assert(false)
+})
+assert(els.length > 0)
+```
+
+Du solltest ein `video`- oder `iframe`-Element mit einer `id` von `video` haben.
+
+```js
+const el = document.getElementById('video')
+assert(!!el && (el.tagName === 'VIDEO' || el.tagName === 'IFRAME'))
+```
+
+Dein `#video` sollte über ein `src`-Attribut verfügen.
+
+```js
+let el = document.getElementById('video')
+const sourceNode = el.children;
+let sourceElement = null;
+if (sourceNode.length) {
+ sourceElement = [...video.children].filter(el => el.localName === 'source')[0];
+}
+if (sourceElement) {
+ el = sourceElement;
+}
+assert(el.hasAttribute('src'));
+```
+
+Du solltest ein `form` Element mit einer `id` von `form` haben.
+
+```js
+const el = document.getElementById('form')
+assert(!!el && el.tagName === 'FORM')
+```
+
+Dein `input`-Element sollte über eine `id` von `email` verfügen.
+
+```js
+const el = document.getElementById('email')
+assert(!!el && el.tagName === 'INPUT')
+```
+
+Deine `#email` sollte ein Nachfahre der `#form` sein.
+
+```js
+const els = document.querySelectorAll('#form #email')
+assert(els.length > 0)
+```
+
+Deine `#email` sollte über ein `placeholder`-Attribut mit entsprechendem Text verfügen.
+
+```js
+const el = document.getElementById('email')
+assert(!!el && !!el.placeholder && el.placeholder.length > 0)
+```
+
+Deine `#email` sollte HTML5-Validierung verwenden, indem du `type` auf `email` setzt.
+
+```js
+const el = document.getElementById('email')
+assert(!!el && el.type === 'email')
+```
+
+Dein `input`-Element sollte über eine `id` von `submit` verfügen.
+
+```js
+const el = document.getElementById('submit')
+assert(!!el && el.tagName === 'INPUT')
+```
+
+Dein `#submit` sollte ein Nachfahre der `#form` sein.
+
+```js
+const els = document.querySelectorAll('#form #submit')
+assert(els.length > 0)
+```
+
+Dein `#submit` sollte einen `type` von `submit` haben.
+
+```js
+const el = document.getElementById('submit')
+assert(!!el && el.type === 'submit')
+```
+
+Deine `#form` sollte über ein `action`-Attribut von `https://www.freecodecamp.com/email-submit` verfügen.
+
+```js
+const el = document.getElementById('form')
+assert(!!el && el.action === 'https://www.freecodecamp.com/email-submit')
+```
+
+Deine `#email` sollte ein `name` Attribut von `email` haben.
+
+```js
+const el = document.getElementById('email')
+assert(!!el && el.name === 'email')
+```
+
+Dein `#nav-bar` sollte immer oben im Viewport liegen.
+
+```js
+(async () => {
+ const timeout = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds));
+
+ const header = document.getElementById('header');
+ const headerChildren = header.children;
+ const navbarCandidates = [header, ...headerChildren];
+
+ // Return smallest top position of all navbar candidates
+ const getNavbarPosition = (candidates = []) => {
+ return candidates.reduce(
+ (min, candidate) =>
+ Math.min(min, Math.abs(candidate?.getBoundingClientRect().top)),
+ Infinity
+ );
+ };
+ assert.approximately(
+ getNavbarPosition(navbarCandidates),
+ 0,
+ 15,
+ '#header or one of its children should be at the top of the viewport '
+ );
+
+ window.scroll(0, 500);
+ await timeout(1);
+
+ assert.approximately(
+ getNavbarPosition(navbarCandidates),
+ 0,
+ 15,
+ '#header or one of its children should be at the top of the ' +
+ 'viewport even after scrolling '
+ );
+
+ window.scroll(0, 0);
+})();
+```
+
+Deine Produkt-Landingpage sollte mindestens eine Medienabfrage (Media Query) verwenden.
+
+```js
+const htmlSourceAttr = Array.from(document.querySelectorAll('source')).map(el => el.getAttribute('media'))
+const cssCheck = new __helpers.CSSHelp(document).getCSSRules('media')
+assert(cssCheck.length > 0 || htmlSourceAttr.length > 0);
+```
+
+Deine Produktseite sollte mindestens einmal die CSS-Flexbox verwenden.
+
+```js
+const stylesheet = new __helpers.CSSHelp(document).getStyleSheet()
+const cssRules = new __helpers.CSSHelp(document).styleSheetToCssRulesArray(stylesheet)
+const usesFlex = cssRules.find(rule => {
+ return rule.style?.display === 'flex' || rule.style?.display === 'inline-flex'
+})
+assert(usesFlex)
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+```
+
+```css
+
+```
+
+## --solutions--
+
+```html
+
+
+
+
+
+ Product Landing Page
+
+
+
+
+
+
+
+ Pokemon Daycare Service
+
+
+
What we offer
+
+
+
+
+
Guaranteed friendly and loving staff!
+
+
+
+
+
+
+ Comfortable environment for Pokemon to explore and play!
+
+
+
+
+
+
+
+ Multiple membership plans to fit your lifestyle!
+
+
+
+
+
Check us out!
+ A sneak peek into our facility:
+
+
+
+
+
Membership Plans
+
+
+ Basic Membership
+
+
One Pokemon
+
Food and berries provided
+
+ $9.99/month
+
+
+ Silver Membership
+
+
Up to Three Pokemon
+
Food and berries provided
+
Grooming and accessories included
+
+ $19.99/month
+
+
+ Gold Membership
+
+
Up to six Pokemon!
+
Food and berries provided
+
Grooming and accessories included
+
Personal training for each Pokemon
+
Breeding and egg hatching
+
+ $29.99/month
+
+
+
+
+
+
+
+
+```
+
+```css
+body {
+ background-color: #3a3240;
+ color: white;
+}
+main {
+ max-width: 750px;
+ margin: 50px auto;
+}
+input {
+ background-color: #92869c;
+}
+a:not(.nav-link) {
+ color: white;
+}
+#header-img {
+ max-height: 25px;
+}
+#nav-bar {
+ position: fixed;
+ width: 100%;
+ text-align: center;
+ top: 0%;
+ background-color: #92869c;
+}
+h1 {
+ text-align: center;
+}
+body {
+ text-align: center;
+}
+footer {
+ text-align: center;
+}
+#bullet {
+ max-height: 25px;
+}
+.flex-here {
+ display: flex;
+ justify-content: center;
+}
+.flex-left {
+ height: 25px;
+}
+.flex-mem {
+ display: flex;
+ justify-content: center;
+}
+.flex-mem-box {
+ background-color: #92869c;
+ border-color: black;
+ border-width: 5px;
+ border-style: solid;
+ margin: 10px;
+ padding: 10px;
+ color: black;
+}
+@media (max-width: 350px) {
+ #video {
+ width: 300;
+ height: 200;
+ }
+}
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-basic-css-by-building-a-cafe-menu/5f3c866d0fc037f7311b4ac8.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-basic-css-by-building-a-cafe-menu/5f3c866d0fc037f7311b4ac8.md
new file mode 100644
index 00000000000..9090f318108
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-basic-css-by-building-a-cafe-menu/5f3c866d0fc037f7311b4ac8.md
@@ -0,0 +1,107 @@
+---
+id: 5f3c866d0fc037f7311b4ac8
+title: Schritt 39
+challengeType: 0
+dashedName: step-39
+---
+
+# --description--
+
+Das ist näher dran, aber der Preis ist nicht auf der rechten Seite geblieben. Das liegt daran, dass `inline-block`-Elemente nur die Breite ihres Inhalts annehmen. Füge, um sie etwas breiter zu machen, eine `width`-Eigenschaft den Klassenselektoren `flavor` und `price` hinzu, die jeweils einen Wert von `50%` haben.
+
+# --hints--
+
+Du solltest die `width`-Eigenschaft auf `50%` in deinem `.flavor`-Selektor setzen.
+
+```js
+const flavorWidth = new __helpers.CSSHelp(document).getStyle('.flavor')?.getPropertyValue('width');
+assert(flavorWidth === '50%');
+```
+
+Du solltest die `width`-Eigenschaft auf `50%` in deinem `.price`-Selektor setzen.
+
+```js
+const priceWidth = new __helpers.CSSHelp(document).getStyle('.price')?.getPropertyValue('width');
+assert(priceWidth === '50%');
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+ Cafe Menu
+
+
+
+
+
+
CAMPER CAFE
+
Est. 2020
+
+
+
+
Coffee
+
+
French Vanilla
+
3.00
+
+
+
Caramel Macchiato
+
3.75
+
+
+
Pumpkin Spice
+
3.50
+
+
+
Hazelnut
+
4.00
+
+
+
Mocha
+
4.50
+
+
+
+
+
+
+```
+
+```css
+body {
+ background-image: url(https://cdn.freecodecamp.org/curriculum/css-cafe/beans.jpg);
+}
+
+h1, h2, p {
+ text-align: center;
+}
+
+.menu {
+ width: 80%;
+ background-color: burlywood;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.item p {
+ display: inline-block;
+}
+
+--fcc-editable-region--
+.flavor {
+ text-align: left;
+}
+
+.price {
+ text-align: right;
+}
+--fcc-editable-region--
+```
+
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-basic-css-by-building-a-cafe-menu/5f3c866d28d7ad0de6470505.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-basic-css-by-building-a-cafe-menu/5f3c866d28d7ad0de6470505.md
new file mode 100644
index 00000000000..551432cc671
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-basic-css-by-building-a-cafe-menu/5f3c866d28d7ad0de6470505.md
@@ -0,0 +1,100 @@
+---
+id: 5f3c866d28d7ad0de6470505
+title: Schritt 33
+challengeType: 0
+dashedName: step-33
+---
+
+# --description--
+
+Die Sorten und Preise sind derzeit übereinander angeordnet und mit ihren jeweiligen `p`-Elementen zentriert. Es wäre schön, wenn die Sorte auf der linken und der Preis auf der rechten Seite wäre.
+
+Füge den Klassennamen `flavor` dem `p`-Element mit `French Vanilla` hinzu.
+
+# --hints--
+
+Du solltest die Klasse `flavor` deinem `p`-Element hinzufügen.
+
+```js
+assert(code.match(/
/i));
+```
+
+Du solltest nur ein Element mit der Klasse `flavor` haben.
+
+```js
+assert($('.flavor').length === 1);
+```
+
+Deine `flavor`-Klasse sollte dem `p`-Element mit dem Text `French Vanilla` zugewiesen sein.
+
+```js
+assert($('.flavor')[0].innerText.match(/French Vanilla/i));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+ Cafe Menu
+
+
+
+
+
+
CAMPER CAFE
+
Est. 2020
+
+
+
+
Coffee
+
+--fcc-editable-region--
+
French Vanilla
+
3.00
+--fcc-editable-region--
+
+
+
Caramel Macchiato
+
3.75
+
+
+
Pumpkin Spice
+
3.50
+
+
+
Hazelnut
+
4.00
+
+
+
Mocha
+
4.50
+
+
+
+
+
+
+```
+
+```css
+body {
+ background-image: url(https://cdn.freecodecamp.org/curriculum/css-cafe/beans.jpg);
+}
+
+h1, h2, p {
+ text-align: center;
+}
+
+.menu {
+ width: 80%;
+ background-color: burlywood;
+ margin-left: auto;
+ margin-right: auto;
+}
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-basic-css-by-building-a-cafe-menu/5f3c866d5414453fc2d7b480.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-basic-css-by-building-a-cafe-menu/5f3c866d5414453fc2d7b480.md
new file mode 100644
index 00000000000..1713b5d1c7a
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-basic-css-by-building-a-cafe-menu/5f3c866d5414453fc2d7b480.md
@@ -0,0 +1,128 @@
+---
+id: 5f3c866d5414453fc2d7b480
+title: Schritt 32
+challengeType: 0
+dashedName: step-32
+---
+
+# --description--
+
+Füge unterhalb des existierenden Paares Kaffee/Preis folgende Kaffee- und Preisangaben mithilfe eines `article`-Elements mit zwei verschachtelten `p`-Elementen hinzu. Wie zuvor sollte der Text des ersten `p`-Elements die Kaffeesorte und des zweiten `p`-Elements die Preise enthalten.
+
+```bash
+Caramel Macchiato 3.75
+Pumpkin Spice 3.50
+Hazelnut 4.00
+Mocha 4.50
+```
+
+# --hints--
+
+Du solltest fünf `article`-Elemente haben.
+
+```js
+assert($('article').length === 5);
+```
+
+Jedes `article`-Element sollte zwei `p`-Elemente haben.
+
+```js
+const articles = $('article');
+assert(articles[0].children.length === 2);
+assert(articles[1].children.length === 2);
+assert(articles[2].children.length === 2);
+assert(articles[3].children.length === 2);
+assert(articles[4].children.length === 2);
+```
+
+Dein erstes `article`-Element sollte `p`-Elemente mit den Texten `French Vanilla` und `3.00` enthalten.
+
+```js
+const children = $('article')[0].children;
+assert(children[0].innerText.match(/French Vanilla/i));
+assert(children[1].innerText.match(/3\.00/i));
+```
+
+Dein zweites `article`-Element sollte `p`-Elemente mit den Texten `Caramel Macchiato` und `3.75` enthalten.
+
+```js
+const children = $('article')[1].children;
+assert(children[0].innerText.match(/Caramel Macchiato/i));
+assert(children[1].innerText.match(/3\.75/i));
+```
+
+Dein drittes `article`-Element sollte `p`-Elemente mit den Texten `Pumpkin Spice` und `3.50` enthalten.
+
+```js
+const children = $('article')[2].children;
+assert(children[0].innerText.match(/Pumpkin Spice/i));
+assert(children[1].innerText.match(/3\.50/i));
+```
+
+Dein viertes `article`-Element sollte `p`-Elemente mit den Texten `Hazelnut` und `4.00` enthalten.
+
+```js
+const children = $('article')[3].children;
+assert(children[0].innerText.match(/Hazelnut/i));
+assert(children[1].innerText.match(/4\.00/i));
+```
+
+Dein fünftes `article`-Element sollte `p`-Elemente mit den Texten `Mocha` und `4.50` enthalten.
+
+```js
+const children = $('article')[4].children;
+assert(children[0].innerText.match(/Mocha/i));
+assert(children[1].innerText.match(/4\.50/i));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+ Cafe Menu
+
+
+
+
+
+
CAMPER CAFE
+
Est. 2020
+
+
+
+
Coffee
+--fcc-editable-region--
+
+
French Vanilla
+
3.00
+
+--fcc-editable-region--
+
+
+
+
+
+```
+
+```css
+body {
+ background-image: url(https://cdn.freecodecamp.org/curriculum/css-cafe/beans.jpg);
+}
+
+h1, h2, p {
+ text-align: center;
+}
+
+.menu {
+ width: 80%;
+ background-color: burlywood;
+ margin-left: auto;
+ margin-right: auto;
+}
+```
diff --git a/curriculum/challenges/german/14-responsive-web-design-22/learn-basic-css-by-building-a-cafe-menu/5f3c866daec9a49519871816.md b/curriculum/challenges/german/14-responsive-web-design-22/learn-basic-css-by-building-a-cafe-menu/5f3c866daec9a49519871816.md
new file mode 100644
index 00000000000..3b25e50b540
--- /dev/null
+++ b/curriculum/challenges/german/14-responsive-web-design-22/learn-basic-css-by-building-a-cafe-menu/5f3c866daec9a49519871816.md
@@ -0,0 +1,88 @@
+---
+id: 5f3c866daec9a49519871816
+title: Schritt 31
+challengeType: 0
+dashedName: step-31
+---
+
+# --description--
+
+`article`-Elemente enthalten in der Regel mehrere Elemente mit verwandten Informationen. In diesem Fall wird es eine Kaffeesorte und einen Preis für dieses Aroma enthalten. Füge zwei `p`-Elemente deinem `article`-Element hinzu. Der erste Text sollte `French Vanilla` sein, der zweite `3.00`.
+
+# --hints--
+
+Du solltest das existierende Element `article` nicht verändern.
+
+```js
+assert($('article').length === 1);
+```
+
+Dein `article`-Element sollte zwei `p`-Elemente enthalten.
+
+```js
+assert($('article').children('p').length === 2);
+```
+
+Dein erstes `p`-Element sollte den Text `French Vanilla` enthalten.
+
+```js
+const firstP = $('article').children('p')[0];
+assert(firstP.innerText.match(/French Vanilla/i));
+```
+
+Dein zweites `p`-Element sollte den Text `3.00` haben.
+
+```js
+const secondP = $('article').children('p')[1];
+assert(secondP.innerText.match(/3\.00/i));
+```
+
+# --seed--
+
+## --seed-contents--
+
+```html
+
+
+
+
+
+ Cafe Menu
+
+
+
+