diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..810ca3e
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,9 @@
+# FlixCooks Firebase Configuration
+# Replace these placeholder values with your actual Firebase project settings.
+
+FIREBASE_API_KEY="AIzaSyYourApiKeyHere"
+FIREBASE_AUTH_DOMAIN="flixcooks-your-project-id.firebaseapp.com"
+FIREBASE_PROJECT_ID="flixcooks-your-project-id"
+FIREBASE_STORAGE_BUCKET="flixcooks-your-project-id.appspot.com"
+FIREBASE_MESSAGING_SENDER_ID="123456789012"
+FIREBASE_APP_ID="1:123456789012:web:abcdef123456"
diff --git a/.gitignore b/.gitignore
index c3c7aea..0cfe729 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-.idx/*
\ No newline at end of file
+.idx/*
+.env
\ No newline at end of file
diff --git a/TODO.md b/TODO.md
index cc06418..efdfafb 100644
--- a/TODO.md
+++ b/TODO.md
@@ -12,7 +12,7 @@ Dieses Dokument enthält den aktuellen Entwicklungsstand und detaillierte Aufgab
- Fullscreen Overlay-Navigation (`LiquidOverlayMenu` & `SlideUpTextHover`)
- Geschmeidiges Scrollverhalten (`LenisSmoothScroll` & `IndexSectionIndicator`)
- Layout-Raster (`FloemaLayoutGrid`) & Premium-Buttons
-- [ ] **Phase 2: Authentifizierung & Firebase Integration**
+- [x] **Phase 2: Authentifizierung & Firebase Integration**
- Firebase-Projekt Setup & Anbindung
- Newsletter-System (Firebase Firestore)
- Benutzerprofile (Registrierung, Login, Favoriten)
@@ -63,23 +63,23 @@ Das visuelle Fundament der Website. Die Etablierung des Premium-Designs stellt s
### 🔒 Phase 2: Authentifizierung & Firebase Integration
Das Fundament für Personalisierung und Newsletter-Abos über eine sichere Firebase-Anbindung (Auth & Firestore).
-- [ ] **Firebase Setup & Initialisierung**
- - [ ] Firebase SDK Client-Side Einbindung in `partials/head.php` oder separaten Helper
- - [ ] Firebase Config in umweltabhängigen Settings/Variablen auslagern
- - [ ] Firestore-Datenbank initialisieren (`subscribers` und `users` Collections)
-- [ ] **Premium Login- & Registrierungsseite**
- - [ ] Erstellung der Seite `login.php` (und optional `signup.php`) im neuen Design-System-Stil
- - [ ] Firebase Authentication (Email/Passwort Login & Registrierung) implementieren
- - [ ] Responsive UI mit sanften Error- & Success-Meldungen und Eingabefeldern im Glasmorphismus-Look
- - [ ] Client-seitige und Server-seitige Session-Synchronisation (z.B. Firebase Session Tokens an PHP-Session via Ajax senden)
-- [ ] **Favoriten-Funktion (Rezept-Bookmarks)**
- - [ ] Firestore-Collection `user_favorites` anlegen (Format: `userId` -> Liste von `recipeSlugs`)
- - [ ] "Rezept speichern" Button (Herz-Icon) auf den Rezeptkarten und Rezept-Details mit dynamischem Login-Check
- - [ ] Mikro-Animationen für das Herz-Icon (Scale up/down, Fülleffekte mit `var(--ease-overshoot)`)
-- [ ] **Firebase-gestütztes Newsletter-Abo**
- - [ ] Newsletter-Eingabefeld im Footer (`partials/footer.php`) implementieren
- - [ ] AJAX-Submit-Handler: E-Mail-Adresse prüfen und in Firestore Collection `subscribers` speichern
- - [ ] Elegantes Feedback-UI (Erfolgsmeldung ohne Page-Reload)
+- [x] **Firebase Setup & Initialisierung**
+ - [x] Firebase SDK Client-Side Einbindung in `partials/head.php` or separaten Helper
+ - [x] Firebase Config in umweltabhängigen Settings/Variablen auslagern
+ - [x] Firestore-Datenbank initialisieren (`subscribers` und `users` Collections)
+- [x] **Premium Login- & Registrierungsseite**
+ - [x] Erstellung der Seite `login.php` (und optional `signup.php`) im neuen Design-System-Stil
+ - [x] Firebase Authentication (Email/Passwort Login & Registrierung) implementieren
+ - [x] Responsive UI mit sanften Error- & Success-Meldungen und Eingabefeldern im Glasmorphismus-Look
+ - [x] Client-seitige und Server-seitige Session-Synchronisation (z.B. Firebase Session Tokens an PHP-Session via Ajax senden)
+- [x] **Favoriten-Funktion (Rezept-Bookmarks)**
+ - [x] Firestore-Collection `user_favorites` anlegen (Format: `userId` -> Liste von `recipeSlugs`)
+ - [x] "Rezept speichern" Button (Herz-Icon) auf den Rezeptkarten und Rezept-Details mit dynamischem Login-Check
+ - [x] Mikro-Animationen für das Herz-Icon (Scale up/down, Fülleffekte mit `var(--ease-overshoot)`)
+- [x] **Firebase-gestütztes Newsletter-Abo**
+ - [x] Newsletter-Eingabefeld im Footer (`partials/footer.php`) implementieren
+ - [x] AJAX-Submit-Handler: E-Mail-Adresse prüfen und in Firestore Collection `subscribers` speichern
+ - [x] Elegantes Feedback-UI (Erfolgsmeldung ohne Page-Reload)
---
diff --git a/admin.php b/admin.php
index 584f48d..31b86b8 100644
--- a/admin.php
+++ b/admin.php
@@ -204,6 +204,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delet
$comingSoon = isset($_POST['coming_soon']) && $_POST['coming_soon'] === '1';
$slugOriginal = trim($_POST['slug_original'] ?? '');
+ $nutriCal = (int) ($_POST['nutrition_calories'] ?? 0);
+ $nutriProt = (int) ($_POST['nutrition_protein'] ?? 0);
+ $nutriCarb = (int) ($_POST['nutrition_carbs'] ?? 0);
+ $nutriFat = (int) ($_POST['nutrition_fat'] ?? 0);
+
// keep previous language data if fields left blank while editing
$prevEn = $editing['i18n']['en'] ?? [];
$prevDe = $editing['i18n']['de'] ?? [];
@@ -261,6 +266,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delet
'servings' => $servings,
'featured' => $featured,
'coming_soon' => $comingSoon,
+ 'nutrition' => [
+ 'calories' => $nutriCal,
+ 'protein' => $nutriProt,
+ 'carbs' => $nutriCarb,
+ 'fat' => $nutriFat
+ ],
'i18n' => [
'en' => [
'title' => $titleEn,
@@ -517,6 +528,25 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delet
+
diff --git a/api/session.php b/api/session.php
new file mode 100644
index 0000000..9aecee2
--- /dev/null
+++ b/api/session.php
@@ -0,0 +1,40 @@
+ $uid,
+ 'email' => $email,
+ 'token' => $token
+ ];
+ echo json_encode(['status' => 'success', 'message' => 'Logged in']);
+ } else {
+ http_response_code(400);
+ echo json_encode(['status' => 'error', 'message' => 'Missing UID']);
+ }
+} elseif ($action === 'logout') {
+ $_SESSION = [];
+ if (ini_get("session.use_cookies")) {
+ $params = session_get_cookie_params();
+ setcookie(session_name(), '', time() - 42000,
+ $params["path"], $params["domain"],
+ $params["secure"], $params["httponly"]
+ );
+ }
+ session_destroy();
+ echo json_encode(['status' => 'success', 'message' => 'Logged out']);
+} else {
+ http_response_code(400);
+ echo json_encode(['status' => 'error', 'message' => 'Invalid action']);
+}
diff --git a/assets/style.css b/assets/style.css
index 29d9dc4..2379561 100644
--- a/assets/style.css
+++ b/assets/style.css
@@ -1735,3 +1735,339 @@ body.nav-open { overflow: hidden; }
.feature-grid { grid-template-columns: 1fr; }
.coming-features { margin-bottom: 40px; }
}
+
+
+/* ╔══════════════════════════════════════════════════════════════╗
+ ║ PHASE 2: AUTHENTICATION, FAVORITES & NEWSLETTER STYLES ║
+ ╚══════════════════════════════════════════════════════════════╝ */
+
+/* ── Premium Heart Buttons ── */
+.card__image {
+ position: relative;
+ display: block;
+}
+.btn-favorite {
+ position: absolute;
+ top: 1rem;
+ right: 1rem;
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ background: rgba(255, 255, 255, 0.7);
+ backdrop-filter: blur(8px);
+ -webkit-backdrop-filter: blur(8px);
+ border: 1px solid rgba(255, 255, 255, 0.3);
+ color: var(--muted);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ z-index: 10;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.08);
+ transition: background 0.3s ease, border-color 0.3s ease, transform 0.3s var(--ease-overshoot), color 0.3s ease;
+}
+[data-theme="dark"] .btn-favorite {
+ background: rgba(36, 31, 33, 0.7);
+ border-color: rgba(255, 255, 255, 0.05);
+}
+.btn-favorite:hover {
+ transform: scale(1.1);
+ background: rgba(255, 255, 255, 0.95);
+ color: var(--ink);
+}
+[data-theme="dark"] .btn-favorite:hover {
+ background: rgba(36, 31, 33, 0.95);
+}
+.btn-favorite svg {
+ width: 18px;
+ height: 18px;
+ transition: stroke 0.3s ease, fill 0.3s ease;
+ fill: none;
+ stroke: var(--ink);
+}
+.btn-favorite.active {
+ background: var(--surface-2);
+ border-color: rgba(195, 166, 119, 0.4);
+}
+.btn-favorite.active svg {
+ fill: var(--color-accent-gold);
+ stroke: var(--color-accent-gold);
+ animation: heartPop 0.45s var(--ease-overshoot) both;
+}
+
+/* For larger recipe layout title heart button */
+.recipe__title-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1.5rem;
+ margin-bottom: 0.5rem;
+ width: 100%;
+}
+.btn-favorite--large {
+ position: static;
+ width: 52px;
+ height: 52px;
+ background: var(--surface-2);
+ border: 1px solid var(--stroke);
+ flex-shrink: 0;
+}
+.btn-favorite--large svg {
+ width: 24px;
+ height: 24px;
+}
+@keyframes heartPop {
+ 0% { transform: scale(1); }
+ 50% { transform: scale(1.35); }
+ 100% { transform: scale(1); }
+}
+
+/* ── Guest Favoriting Prompt Modal ── */
+.guest-modal-backdrop {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(11, 9, 10, 0.4);
+ backdrop-filter: blur(8px);
+ -webkit-backdrop-filter: blur(8px);
+ z-index: 2000;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.4s var(--ease-out-expo);
+}
+.guest-modal-backdrop.open {
+ opacity: 1;
+ pointer-events: auto;
+}
+.guest-modal-card {
+ background: var(--surface-2);
+ border: 1px solid var(--stroke);
+ box-shadow: var(--shadow);
+ border-radius: var(--radius);
+ width: 90%;
+ max-width: 440px;
+ padding: 2.5rem;
+ position: relative;
+ text-align: center;
+ transform: translateY(30px);
+ transition: transform 0.5s var(--ease-overshoot);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 1.2rem;
+}
+.guest-modal-backdrop.open .guest-modal-card {
+ transform: translateY(0);
+}
+.guest-modal-close {
+ position: absolute;
+ top: 1rem;
+ right: 1.2rem;
+ background: none;
+ border: none;
+ font-size: 1.8rem;
+ color: var(--muted);
+ cursor: pointer;
+ transition: color 0.3s ease;
+}
+.guest-modal-close:hover {
+ color: var(--ink);
+}
+.guest-modal-icon {
+ font-size: 2.5rem;
+ line-height: 1;
+}
+.guest-modal-card h3 {
+ font-family: var(--font-serif-display);
+ font-size: 1.8rem;
+ margin: 0;
+ color: var(--ink);
+}
+.guest-modal-card p {
+ font-size: 0.95rem;
+ line-height: 1.6;
+ color: var(--muted);
+ margin: 0;
+}
+.guest-modal-actions {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 1rem;
+ width: 100%;
+ margin-top: 0.5rem;
+}
+.guest-modal-actions .btn-reveal {
+ width: 100%;
+ justify-content: center;
+}
+.guest-modal-actions button {
+ background: none;
+ border: none;
+ font-family: var(--font-sans-clean);
+ font-size: 0.85rem;
+ color: var(--muted);
+ cursor: pointer;
+}
+
+/* ── Footer Newsletter ── */
+.footer-newsletter {
+ padding-bottom: 2.5rem;
+ border-bottom: 1px solid var(--stroke);
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 1.5rem;
+ align-items: center;
+ margin-bottom: 1rem;
+ width: 100%;
+}
+@media (min-width: 768px) {
+ .footer-newsletter {
+ grid-template-columns: 1.2fr 1fr;
+ gap: 3rem;
+ }
+}
+.newsletter-content h3 {
+ font-family: var(--font-serif-display);
+ font-size: 1.75rem;
+ margin-bottom: 0.5rem;
+ color: var(--ink);
+}
+.newsletter-content p {
+ color: var(--muted);
+ font-size: 0.95rem;
+ line-height: 1.5;
+ margin: 0;
+}
+.newsletter-form {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ position: relative;
+}
+.newsletter-input-group {
+ display: flex;
+ border-bottom: 2px solid var(--stroke);
+ padding: 0.5rem 0;
+ transition: border-color 0.3s ease;
+ position: relative;
+ align-items: center;
+}
+.newsletter-input-group:focus-within {
+ border-color: var(--color-accent-gold);
+}
+.newsletter-input-group input {
+ flex: 1;
+ background: none;
+ border: none;
+ font-family: var(--font-sans-clean);
+ font-size: 1rem;
+ color: var(--ink);
+ outline: none;
+ padding: 0.5rem 0;
+}
+.newsletter-input-group input::placeholder {
+ color: var(--color-text-muted);
+ opacity: 0.7;
+}
+.newsletter-input-group button {
+ background: none;
+ border: none;
+ color: var(--color-accent-gold);
+ font-size: 1.5rem;
+ cursor: pointer;
+ padding: 0 0.5rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 32px;
+ height: 32px;
+ position: relative;
+ transition: transform 0.3s var(--ease-overshoot), color 0.3s ease;
+}
+.newsletter-input-group button:hover {
+ transform: translateX(4px);
+}
+.newsletter-input-group button .btn-success-check {
+ display: none;
+ color: #10b981;
+ font-size: 1.2rem;
+}
+.newsletter-form.success .newsletter-input-group button .btn-text {
+ display: none;
+}
+.newsletter-form.success .newsletter-input-group button .btn-success-check {
+ display: block;
+}
+.newsletter-form.success .newsletter-input-group button {
+ transform: none !important;
+ cursor: default;
+}
+.newsletter-form.success .newsletter-input-group {
+ border-color: #10b981;
+}
+.newsletter-feedback {
+ font-size: 0.85rem;
+ margin-top: 0.25rem;
+ min-height: 1.25rem;
+ transition: color 0.3s ease;
+}
+.newsletter-feedback.error {
+ color: #e02424;
+}
+.newsletter-feedback.success {
+ color: #10b981;
+}
+
+/* ── 22. PHASE 3: NUTRITION WIDGET ───────────────────────────── */
+.nutrition-widget {
+ margin-top: 1.5rem;
+ margin-bottom: 2rem;
+ gap: 1.5rem;
+}
+.nutrition-item {
+ flex: 1 1 calc(25% - 1.5rem);
+ min-width: 120px;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+.nutrition-item__header {
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+ font-family: var(--font-sans-clean);
+}
+.nutrition-label {
+ font-size: 0.85rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.1em;
+ color: var(--muted);
+}
+.nutrition-value {
+ font-family: var(--font-serif-display);
+ font-size: 1.2rem;
+ font-weight: 700;
+ color: var(--ink);
+}
+.nutrition-bar {
+ width: 100%;
+ height: 4px;
+ background-color: var(--ghost);
+ border-radius: 2px;
+ overflow: hidden;
+ position: relative;
+}
+.nutrition-fill {
+ height: 100%;
+ background-color: var(--accent);
+ border-radius: 2px;
+ transition: width 1.5s var(--ease-out-expo) 0.2s;
+ will-change: width;
+}
diff --git a/config.php b/config.php
new file mode 100644
index 0000000..869259c
--- /dev/null
+++ b/config.php
@@ -0,0 +1,46 @@
+ getenv('FIREBASE_API_KEY') ?: '',
+ 'authDomain' => getenv('FIREBASE_AUTH_DOMAIN') ?: '',
+ 'projectId' => getenv('FIREBASE_PROJECT_ID') ?: '',
+ 'storageBucket' => getenv('FIREBASE_STORAGE_BUCKET') ?: '',
+ 'messagingSenderId' => getenv('FIREBASE_MESSAGING_SENDER_ID') ?: '',
+ 'appId' => getenv('FIREBASE_APP_ID') ?: '',
+ ];
+}
diff --git a/data/recipes.json b/data/recipes.json
index 581ffbf..28b7e23 100644
--- a/data/recipes.json
+++ b/data/recipes.json
@@ -8,6 +8,12 @@
"servings": 2,
"featured": false,
"coming_soon": false,
+ "nutrition": {
+ "calories": 0,
+ "protein": 0,
+ "carbs": 0,
+ "fat": 0
+ },
"i18n": {
"en": {
"title": "Tagliatelle with tomato sauce",
@@ -36,16 +42,16 @@
"Optional: Blender"
],
"steps": [
- "Pile the flour on your counter, press a well in the center, crack in the eggs. Gradually pull flour into the eggs, then knead 5\u201310 minutes until smooth.",
- "Roll the dough to 1\u20132 mm thickness (rolling pin or pasta machine) and cut into tagliatelle.",
+ "Pile the flour on your counter, press a well in the center, crack in the eggs. Gradually pull flour into the eggs, then knead 5–10 minutes until smooth.",
+ "Roll the dough to 1–2 mm thickness (rolling pin or pasta machine) and cut into tagliatelle.",
"Bring salted water to a boil. Meanwhile, halve tomatoes and sear in olive oil with a pinch of salt until lightly charred.",
"Toast the garlic briefly, then blend tomatoes with cream (or crush in the pan) and simmer to thicken slightly.",
- "Boil tagliatelle for 2\u20133 minutes, drain, toss with the sauce, and finish with basil and Parmesan."
+ "Boil tagliatelle for 2–3 minutes, drain, toss with the sauce, and finish with basil and Parmesan."
]
},
"de": {
"title": "Tagliatelle mit Tomatensauce",
- "description": "Einfach, aber richtig lecker; kurze Kochzeit und trotzdem hausgemacht. Das Basisgericht f\u00fcr einen gem\u00fctlichen Abend, stillt den Kohlenhydrat-Hunger ohne versteckte Zus\u00e4tze. Nudeln mit Tomatensauce \u2013 herzhafter geht es kaum.",
+ "description": "Einfach, aber richtig lecker; kurze Kochzeit und trotzdem hausgemacht. Das Basisgericht für einen gemütlichen Abend, stillt den Kohlenhydrat-Hunger ohne versteckte Zusätze. Nudeln mit Tomatensauce – herzhafter geht es kaum.",
"category": "Pasta",
"difficulty": "Einfach",
"tags": [
@@ -58,7 +64,7 @@
"3 Eier",
"10 Cherry-Tomaten",
"1/2 Knoblauchzehe",
- "1 EL Oliven\u00f6l",
+ "1 EL Olivenöl",
"80 ml Sahne",
"Frischer Basilikum",
"Parmesan",
@@ -70,11 +76,11 @@
"Optional: Mixer"
],
"steps": [
- "Mehl auf der Arbeitsfl\u00e4che anh\u00e4ufen, eine Kuhle dr\u00fccken, Eier hineingeben. Mehl nach und nach einarbeiten, dann 5\u201310 Minuten kneten, bis der Teig glatt ist.",
- "Teig auf 1\u20132 mm ausrollen (mit Nudelholz oder Nudelmaschine) und in Tagliatelle schneiden.",
- "Gesalzenes Wasser aufsetzen; w\u00e4hrenddessen Tomaten halbieren und mit Oliven\u00f6l und einer Prise Salz in der Pfanne anr\u00f6sten, bis R\u00f6stnoten entstehen.",
- "Knoblauch kurz mitr\u00f6sten, dann Tomaten mit Sahne p\u00fcrieren (Mixer) oder in der Pfanne zerdr\u00fccken und kurz einkochen lassen.",
- "Tagliatelle 2\u20133 Minuten kochen, abgie\u00dfen und mit der Sauce vermengen. Mit Basilikum und Parmesan anrichten."
+ "Mehl auf der Arbeitsfläche anhäufen, eine Kuhle drücken, Eier hineingeben. Mehl nach und nach einarbeiten, dann 5–10 Minuten kneten, bis der Teig glatt ist.",
+ "Teig auf 1–2 mm ausrollen (mit Nudelholz oder Nudelmaschine) und in Tagliatelle schneiden.",
+ "Gesalzenes Wasser aufsetzen; währenddessen Tomaten halbieren und mit Olivenöl und einer Prise Salz in der Pfanne anrösten, bis Röstnoten entstehen.",
+ "Knoblauch kurz mitrösten, dann Tomaten mit Sahne pürieren (Mixer) oder in der Pfanne zerdrücken und kurz einkochen lassen.",
+ "Tagliatelle 2–3 Minuten kochen, abgießen und mit der Sauce vermengen. Mit Basilikum und Parmesan anrichten."
]
}
}
@@ -88,6 +94,12 @@
"servings": 2,
"featured": false,
"coming_soon": true,
+ "nutrition": {
+ "calories": 0,
+ "protein": 0,
+ "carbs": 0,
+ "fat": 0
+ },
"i18n": {
"en": {
"title": "Oat-Pancakes",
@@ -120,10 +132,16 @@
"servings": 2,
"featured": true,
"coming_soon": false,
+ "nutrition": {
+ "calories": 0,
+ "protein": 0,
+ "carbs": 0,
+ "fat": 0
+ },
"i18n": {
"en": {
"title": "Steak with onion jam",
- "description": "A hearty meal after a long day, best enjoyed with a glass of red wine and in good company\u2014because the onion jam still isn\u2019t satisfied on its own.",
+ "description": "A hearty meal after a long day, best enjoyed with a glass of red wine and in good company—because the onion jam still isn’t satisfied on its own.",
"category": "",
"difficulty": "Easy",
"tags": [
@@ -157,14 +175,14 @@
"Dice the onion",
"Cook the potatoes and carrots until al dente",
"Sear the steak with butter",
- "Once both sides are golden brown, bake for 6 minutes at 160\u00b0C (medium); for medium rare, let it rest after searing both sides for 3 minutes",
- "Saut\u00e9 the onion in the steak butter, deglaze with red wine, and add cream once the alcohol has evaporated",
+ "Once both sides are golden brown, bake for 6 minutes at 160°C (medium); for medium rare, let it rest after searing both sides for 3 minutes",
+ "Sauté the onion in the steak butter, deglaze with red wine, and add cream once the alcohol has evaporated",
"Plate and serve!"
]
},
"de": {
"title": "Steak mit Zwiebelmarmelade",
- "description": "Eine schwere Mahlzeit nach einem langen Tag, am besten zu einem Schluck Rotwein und in Gesellschaft genie\u00dfen, da die Zwiebelmarmelade noch nicht genug davon hat.",
+ "description": "Eine schwere Mahlzeit nach einem langen Tag, am besten zu einem Schluck Rotwein und in Gesellschaft genießen, da die Zwiebelmarmelade noch nicht genug davon hat.",
"category": "",
"difficulty": "Einfach",
"tags": [
@@ -174,7 +192,7 @@
"ingredients": [
"2 x 250g Rumpsteak",
"300gr Kartoffeln",
- "1 Gro\u00dfe Rote Zwiebel",
+ "1 Große Rote Zwiebel",
"1/2 Knoblauch-Zehe",
"50ml Sahne",
"50ml Rotwein",
@@ -195,11 +213,11 @@
"Steak 30 Minuten vor dem anbraten raus legen",
"Kartoffeln halbieren",
"Karotten halbieren",
- "Zwiebel w\u00fcrfeln",
+ "Zwiebel würfeln",
"Kartoffeln und Karotten al dente kochen",
"Steak anbraten mit Butter",
- "sobald beide seiten goldbraun sind f\u00fcr 6 Minuten bei 160grad backen (Medium) f\u00fcr Medium Rare nur ruhen lassen nachdem es von beiden Seiten f\u00fcr 3 Minuten angebraten wurde",
- "Zwiebel in Steak-Butter anschwitzen, mit Rotwein abl\u00f6schen und Sahne dazu geben nachdem er Alkohol verd\u00fcnstet ist",
+ "sobald beide seiten goldbraun sind für 6 Minuten bei 160grad backen (Medium) für Medium Rare nur ruhen lassen nachdem es von beiden Seiten für 3 Minuten angebraten wurde",
+ "Zwiebel in Steak-Butter anschwitzen, mit Rotwein ablöschen und Sahne dazu geben nachdem er Alkohol verdünstet ist",
"Anrichten!"
]
}
@@ -214,6 +232,12 @@
"servings": 2,
"featured": false,
"coming_soon": true,
+ "nutrition": {
+ "calories": 0,
+ "protein": 0,
+ "carbs": 0,
+ "fat": 0
+ },
"i18n": {
"en": {
"title": "Oat cake",
diff --git a/helpers.php b/helpers.php
index ab18034..d6b4099 100644
--- a/helpers.php
+++ b/helpers.php
@@ -1,6 +1,12 @@
" alt="">