diff --git a/index.html b/index.html
index ee77274..82cc220 100644
--- a/index.html
+++ b/index.html
@@ -3,10 +3,84 @@
-Plan-B — Survival Preparedness Advisor
+
+
+Plan-B — AI-Assisted Crisis Preparedness Advisor
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/images/apple-touch-icon.png b/public/images/apple-touch-icon.png
new file mode 100644
index 0000000..36f3f7b
Binary files /dev/null and b/public/images/apple-touch-icon.png differ
diff --git a/public/images/favicon-16.png b/public/images/favicon-16.png
new file mode 100644
index 0000000..443d53f
Binary files /dev/null and b/public/images/favicon-16.png differ
diff --git a/public/images/favicon-32.png b/public/images/favicon-32.png
new file mode 100644
index 0000000..e574d8e
Binary files /dev/null and b/public/images/favicon-32.png differ
diff --git a/public/images/favicon.svg b/public/images/favicon.svg
new file mode 100644
index 0000000..5c8fa51
--- /dev/null
+++ b/public/images/favicon.svg
@@ -0,0 +1,7 @@
+
diff --git a/public/images/icon-192.png b/public/images/icon-192.png
new file mode 100644
index 0000000..047cd2a
Binary files /dev/null and b/public/images/icon-192.png differ
diff --git a/public/images/icon-512.png b/public/images/icon-512.png
new file mode 100644
index 0000000..ad191a2
Binary files /dev/null and b/public/images/icon-512.png differ
diff --git a/public/images/og-card.svg b/public/images/og-card.svg
new file mode 100644
index 0000000..6c30dfb
--- /dev/null
+++ b/public/images/og-card.svg
@@ -0,0 +1,42 @@
+
diff --git a/public/images/og-image.png b/public/images/og-image.png
new file mode 100644
index 0000000..4bff805
Binary files /dev/null and b/public/images/og-image.png differ
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100644
index 0000000..e42ec87
--- /dev/null
+++ b/public/robots.txt
@@ -0,0 +1,4 @@
+User-agent: *
+Allow: /
+
+Sitemap: https://plan-b.now/sitemap.xml
diff --git a/public/site.webmanifest b/public/site.webmanifest
new file mode 100644
index 0000000..485ec14
--- /dev/null
+++ b/public/site.webmanifest
@@ -0,0 +1,16 @@
+{
+ "name": "Plan-B — Crisis Preparedness Advisor",
+ "short_name": "Plan-B",
+ "description": "AI-assisted crisis-preparedness advisor for urban households.",
+ "start_url": "/",
+ "scope": "/",
+ "display": "standalone",
+ "background_color": "#FAFAFA",
+ "theme_color": "#5A9A78",
+ "lang": "en",
+ "icons": [
+ { "src": "/images/icon-192.png", "sizes": "192x192", "type": "image/png" },
+ { "src": "/images/icon-512.png", "sizes": "512x512", "type": "image/png" },
+ { "src": "/images/favicon.svg", "sizes": "any", "type": "image/svg+xml", "purpose": "any" }
+ ]
+}
diff --git a/public/sitemap.xml b/public/sitemap.xml
new file mode 100644
index 0000000..77e9b2e
--- /dev/null
+++ b/public/sitemap.xml
@@ -0,0 +1,12 @@
+
+
+
+ https://plan-b.now/
+ weekly
+ 1.0
+
+
+
+
+
diff --git a/src/App.vue b/src/App.vue
index 840ba20..4daea9d 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -639,7 +639,9 @@ import { onMounted } from 'vue'
const T = {
en: {
brand: "Plan-B",
- page_title: "Plan-B — Survival Preparedness Advisor",
+ page_title: "Plan-B — AI-Assisted Crisis Preparedness Advisor",
+ meta_description: "Free AI-assisted crisis-preparedness advisor for urban households. Assess your readiness across 9 collapse scenarios and get a personalised survival plan in minutes.",
+ og_locale: "en_US",
hero_eyebrow: "Crisis Preparedness Advisor",
hero_sub: "AI-Assisted Preparedness for Urban Households",
hero_cta: "Begin",
@@ -761,7 +763,9 @@ const T = {
},
de: {
brand: "Plan-B",
- page_title: "Plan-B — Krisenvorsorge-Berater",
+ page_title: "Plan-B — KI-gestützter Krisenvorsorge-Berater",
+ meta_description: "Kostenloser KI-gestützter Krisenvorsorge-Berater für urbane Haushalte. Bewerte deine Vorsorge über 9 Krisenszenarien und erhalte in Minuten einen personalisierten Survival-Plan.",
+ og_locale: "de_DE",
hero_eyebrow: "⚡ Krisenvorsorge-Berater",
hero_sub: "KI-gestützte Vorsorge für urbane Haushalte",
hero_cta: "Beginnen",
@@ -899,6 +903,7 @@ function setLang(lang) {
if (T[lang][key]) el.textContent = T[lang][key]
})
if (T[lang].page_title) document.title = T[lang].page_title
+ syncMeta(lang)
document.querySelectorAll('[data-ph-' + lang + ']').forEach(el => {
el.placeholder = el.getAttribute('data-ph-' + lang)
})
@@ -913,6 +918,31 @@ function setLang(lang) {
function t(key) { return T[currentLang][key] || T['en'][key] || key }
+// Keep SEO-relevant tags in sync with the active language. The static
+// tags in index.html are the English defaults (what non-JS social scrapers
+// read); this updates them for the browser tab and JS-executing crawlers
+// (e.g. Googlebot) when the user toggles EN/DE on the single-URL SPA.
+function syncMeta(lang) {
+ const tr = T[lang] || T.en
+ const setAttr = (selector, attr, val) => {
+ const el = document.head.querySelector(selector)
+ if (el && val) el.setAttribute(attr, val)
+ }
+ if (tr.meta_description) {
+ setAttr('meta[name="description"]', 'content', tr.meta_description)
+ setAttr('meta[property="og:description"]', 'content', tr.meta_description)
+ setAttr('meta[name="twitter:description"]', 'content', tr.meta_description)
+ }
+ if (tr.page_title) {
+ setAttr('meta[property="og:title"]', 'content', tr.page_title)
+ setAttr('meta[name="twitter:title"]', 'content', tr.page_title)
+ }
+ if (tr.og_locale) {
+ setAttr('meta[property="og:locale"]', 'content', tr.og_locale)
+ setAttr('meta[property="og:locale:alternate"]', 'content', lang === 'de' ? 'en_US' : 'de_DE')
+ }
+}
+
function syncPreferredLanguageField() {
const field = document.getElementById('f_pref_lang')
if (field) field.value = currentLang