feat: green-paint icon system for results page

Replace the remaining emoji indicators on the post-quiz / assessment
side with inline SVG line-icons (Lucide-style, vendored as path
strings) sitting in dark-green-paint containers that match the CTA
button family.

Icons
- 13-icon set in ICON_PATHS — droplet, sprout, zap, cart, flame, leaf,
  package, radio, brain, lock, check, mail, shield. Single-stroke
  paths, currentColor for stroke so each surface tints them.
- icon(name, size) returns the SVG markup; iconForEmoji() maps the
  legacy emoji values used by the rec-cards / timeline data tables to
  the icon names so the data didn't need a wholesale rewrite.

Surfaces re-skinned
- Recommendation card chips (.rec-icon): 40×40 dark-green-paint chip
  with the paintGlossBtn filter, warm-cream icon stroke. Per-card
  inline category colour (`card.color`) dropped — every chip now reads
  as one family.
- Timeline dots (.tl-dot): same green paint, 50% radius. Per-step
  rainbow colours dropped.
- Affiliate button: tiny inline cart icon next to "View on Amazon".
- Narrative ("Your Personal Analysis"): brain icon in green replaces
  the 🧠 emoji header.
- Protein-offer badge: lock icon prefix replaces 🔒.
- Capture-success state: 56px green check-circle replaces , and the
  spam-folder note becomes a flex row with a mail icon — translation
  keys cleaned of the inline 📬 prefix.

Form-field emojis (e.g. "🥩 Protein access (optional)"), country flags,
and the in-form privacy 🔒 stay as-is per the explicit ask: "in
assessment, not the form fields that have them earlier".

Other tidy-up
- Scenario tabs: dropped 💀⚠️📈🌿 prefixes from tab_s1–4 (en + de) and
  the inline template defaults.
- Risk banner: removed the 🔴🟠🟡🟢 dot from the eyebrow line — the
  painted card colour already conveys severity.
- Panel headers stripped of emoji: "Recommendations" / "Empfehlungen",
  "Budget Plan" / "Budgetplan", "Action Timeline" / "Aktionsplan"; the
  📊 prefix dropped from the inner budget-meter title.
- "Tap a scenario to compare" hint moved out of the collapsed panel
  header into the expanded body, restyled as a centred full-width
  light-green paint banner.
- Modifier (⚙) toggle hidden via a single display:none rule — styles
  preserved so we can re-enable later.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-05-10 10:03:28 +01:00
parent eae55ddc3a
commit 302c8aae83
2 changed files with 223 additions and 78 deletions

View File

@@ -316,7 +316,7 @@
<!-- PROTEIN OFFER shown only if protein_access === uncertain -->
<div class="protein-offer" id="protein-offer-section">
<div class="protein-offer-badge">🔒 <span data-i18n="protein_offer_badge">Exclusive DACH Region Priority Access</span></div>
<div class="protein-offer-badge"><span class="badge-icon" v-html="icon('lock', 12)"></span><span data-i18n="protein_offer_badge">Exclusive DACH Region Priority Access</span></div>
<div class="protein-offer-title" data-i18n="protein_offer_title">Secure your high-grade animal protein source</div>
<div class="protein-offer-body" data-i18n="protein_offer_body">You indicated that your protein supply may not be secure in a crisis. We have identified an exclusive, verified source of high-grade animal protein available with priority access for Deepstock members in Germany, Austria, and Switzerland.</div>
<a class="protein-offer-btn" href="PROTEIN_OFFER_URL" target="_blank" rel="noopener noreferrer">
@@ -328,7 +328,7 @@
<!-- AI NARRATIVE SECTION -->
<div class="narrative-section" id="narrative-section">
<div class="narrative-header">
<span style="font-size:20px">🧠</span>
<span class="narrative-icon" v-html="icon('brain', 20)"></span>
<div class="narrative-title" data-i18n="narrative_title">Your Personal Analysis</div>
<div class="narrative-tag" data-i18n="narrative_tag">AI Generated</div>
</div>
@@ -359,11 +359,12 @@
<div class="form-modal-body" id="form-modal-body">
<!-- Success state replaces the form after submit -->
<div class="capture-success hidden" id="capture-success">
<div style="font-size:36px;margin-bottom:8px;text-align:center"></div>
<div class="success-check" v-html="icon('check', 40)"></div>
<div style="font-family:var(--font-display);font-weight:800;font-size:22px;color:var(--green-bright);margin-bottom:8px;text-align:center" data-i18n="success_title">Plan sent!</div>
<div style="font-size:14px;color:var(--text-dim);line-height:1.6;text-align:center" data-i18n="success_text">Check your inbox your personalised plan is on its way.</div>
<div style="margin-top:14px;padding:12px 16px;background:rgba(212,168,32,0.1);border:1px solid rgba(212,168,32,0.35);border-radius:8px;text-align:center">
<div style="font-size:13px;color:#D4A820;line-height:1.7" data-i18n="success_spam">📬 Don't see it? Check your <strong>spam or junk folder</strong>. If it's there, please mark it as <strong>"Not Spam"</strong> this ensures future updates reach your inbox directly, and helps us improve deliverability for everyone.</div>
<div class="success-spam">
<span class="success-spam-icon" v-html="icon('mail', 16)"></span>
<div data-i18n="success_spam">Don't see it? Check your <strong>spam or junk folder</strong>. If it's there, please mark it as <strong>"Not Spam"</strong> this ensures future updates reach your inbox directly, and helps us improve deliverability for everyone.</div>
</div>
<div style="margin-top:14px;text-align:center">
<button class="narrative-cta-btn" @click="closeAndScrollToRecs" data-i18n="success_explore">Explore Recommendations</button>
@@ -484,16 +485,16 @@
<details class="result-panel reveal-section" id="panel-recs" open>
<summary class="result-panel-header">
<span class="rp-title" data-i18n="panel_recs">📋 Your Recommendations</span>
<span class="rp-hint" data-i18n="panel_recs_hint">Tap a scenario to compare</span>
<span class="rp-title" data-i18n="panel_recs">Recommendations</span>
<span class="rp-chevron" aria-hidden="true">▾</span>
</summary>
<div class="result-panel-body">
<div class="rp-hint" data-i18n="panel_recs_hint">Tap a scenario to compare</div>
<div class="scenario-tabs" id="scenario-tabs">
<button class="s-tab" id="stab-1" data-s="1" @click="showScenario(1)"><span data-i18n="tab_s1">💀 Total</span></button>
<button class="s-tab" id="stab-2" data-s="2" @click="showScenario(2)"><span data-i18n="tab_s2">⚠️ Partial</span></button>
<button class="s-tab" id="stab-3" data-s="3" @click="showScenario(3)"><span data-i18n="tab_s3">📈 Inflation</span></button>
<button class="s-tab" id="stab-4" data-s="4" @click="showScenario(4)"><span data-i18n="tab_s4">🌿 Food</span></button>
<button class="s-tab" id="stab-1" data-s="1" @click="showScenario(1)"><span data-i18n="tab_s1">Total</span></button>
<button class="s-tab" id="stab-2" data-s="2" @click="showScenario(2)"><span data-i18n="tab_s2">Partial</span></button>
<button class="s-tab" id="stab-3" data-s="3" @click="showScenario(3)"><span data-i18n="tab_s3">Inflation</span></button>
<button class="s-tab" id="stab-4" data-s="4" @click="showScenario(4)"><span data-i18n="tab_s4">Food</span></button>
</div>
<div class="rec-cards" id="rec-cards-container"></div>
</div>
@@ -501,7 +502,7 @@
<details class="result-panel reveal-section" id="panel-budget">
<summary class="result-panel-header">
<span class="rp-title" data-i18n="panel_budget">💰 Budget Plan</span>
<span class="rp-title" data-i18n="panel_budget">Budget Plan</span>
<span class="rp-chevron" aria-hidden="true">▾</span>
</summary>
<div class="result-panel-body">
@@ -511,7 +512,7 @@
<details class="result-panel reveal-section" id="panel-timeline">
<summary class="result-panel-header">
<span class="rp-title" data-i18n="panel_timeline">Action Timeline</span>
<span class="rp-title" data-i18n="panel_timeline">Action Timeline</span>
<span class="rp-chevron" aria-hidden="true">▾</span>
</summary>
<div class="result-panel-body">
@@ -568,20 +569,20 @@ const T = {
pill_3: "Hyperinflation",
pill_4: "Food Shortage",
progress_label: "Assessment Progress",
tab_s1: "💀 Total\nCollapse",
tab_s2: "⚠️ Partial\nCollapse",
tab_s3: "📈 Hyper-\ninflation",
tab_s4: "🌿 Food\nShortage",
tab_s1: "Total\nCollapse",
tab_s2: "Partial\nCollapse",
tab_s3: "Hyper\ninflation",
tab_s4: "Food\nShortage",
timeline_title: "⏱ Your Action Timeline",
panel_recs: "📋 Your Recommendations",
panel_recs: "Recommendations",
panel_recs_hint: "Tap a scenario to compare",
panel_budget: "💰 Budget Plan",
panel_timeline: "Action Timeline",
panel_budget: "Budget Plan",
panel_timeline: "Action Timeline",
restart_btn: "Retake Assessment",
about_title: "Why Deepstock?",
about_text: "Built by preparedness researchers and city-dwelling practitioners. Every recommendation is sourced, tested, and city-apartment-compatible.",
affiliate_note: "* This site uses affiliate links. When you purchase through our links, we may earn a commission at no extra cost to you. This helps keep the platform free.",
budget_title: "📊 Budget Allocation",
budget_title: "Budget Allocation",
risk_label: "Preparedness Assessment",
risk_critical_title: "CRITICAL",
risk_critical_desc: "You have almost no buffer. A 48-hour supply disruption would put your household in danger.",
@@ -622,7 +623,7 @@ const T = {
capture_header:"Personal Details",
cta_scroll:"Receive Plan by email",
success_explore:"Explore Recommendations",
success_spam:"📬 Don't see it? Check your spam or junk folder. If it's there, please mark it as Not Spam — this ensures future updates reach your inbox directly, and helps us improve deliverability for everyone.",
success_spam:"Don't see it? Check your spam or junk folder. If it's there, please mark it as Not Spam — this ensures future updates reach your inbox directly, and helps us improve deliverability for everyone.",
view_amazon: "View on Amazon",
bcat_water: "Water",
bcat_food: "Food",
@@ -685,20 +686,20 @@ const T = {
pill_3: "Hyperinflation",
pill_4: "Lebensmittelkrise",
progress_label: "Fortschritt",
tab_s1: "💀 Totaler\nKollaps",
tab_s2: "⚠️ Partieller\nKollaps",
tab_s3: "📈 Hyper-\ninflation",
tab_s4: "🌿 Lebens-\nmittelkrise",
tab_s1: "Totaler\nKollaps",
tab_s2: "Partieller\nKollaps",
tab_s3: "Hyper\ninflation",
tab_s4: "Lebens\nmittelkrise",
timeline_title: "⏱ Dein Aktionsplan",
panel_recs: "📋 Deine Empfehlungen",
panel_recs: "Empfehlungen",
panel_recs_hint: "Szenario antippen zum Vergleichen",
panel_budget: "💰 Budgetplan",
panel_timeline: "Aktionsplan",
panel_budget: "Budgetplan",
panel_timeline: "Aktionsplan",
restart_btn: "Neu starten",
about_title: "Warum Kammergut?",
about_text: "Entwickelt von Vorsorge-Forschern und Stadtbewohnern. Jede Empfehlung ist recherchiert, getestet und für Stadtwohnungen geeignet.",
affiliate_note: "* Diese Website nutzt Affiliate-Links. Beim Kauf über unsere Links erhalten wir eine Provision ohne Mehrkosten für dich. So bleibt die Plattform kostenlos.",
budget_title: "📊 Budgetverteilung",
budget_title: "Budgetverteilung",
risk_label: "Vorsorge-Bewertung",
risk_critical_title: "KRITISCH",
risk_critical_desc: "Du hast fast keinen Puffer. Eine 48-stündige Versorgungsunterbrechung würde deinen Haushalt gefährden.",
@@ -739,7 +740,7 @@ const T = {
capture_header:"Persönliche Daten",
cta_scroll:"Plan per E-Mail erhalten",
success_explore:"Empfehlungen ansehen",
success_spam:"📬 Nicht erhalten? Schau in deinen Spam-Ordner. Falls du es dort findest, markiere es bitte als Kein Spam — so landen zukünftige Updates direkt in deinem Posteingang und du hilfst uns die Zustellbarkeit für alle zu verbessern.",
success_spam:"Nicht erhalten? Schau in deinen Spam-Ordner. Falls du es dort findest, markiere es bitte als Kein Spam — so landen zukünftige Updates direkt in deinem Posteingang und du hilfst uns die Zustellbarkeit für alle zu verbessern.",
view_amazon: "Bei Amazon ansehen",
bcat_water: "Wasser",
bcat_food: "Lebensmittel",
@@ -933,6 +934,44 @@ const QUESTIONS = [
},
]
// ══════════════════════════════════════
// ICONS — Lucide-style line icons, inlined so we don't pull a runtime
// dependency. Stroke colour comes from CSS (currentColor) so each
// surface (rec card, timeline dot, narrative header, etc.) can pick
// its own tint while still feeling like one icon family.
// Form-field emojis (🥩 etc.) intentionally stay as emoji per request.
// ══════════════════════════════════════
const ICON_PATHS = {
droplet: '<path d="M12 2.69l5.66 5.66a8 8 0 1 1-11.31 0z"/>',
sprout: '<path d="M7 20h10"/><path d="M10 20c5.5-2.5.8-6.4 3-10"/><path d="M9.5 9.4c1.1.8 1.8 2.2 2.3 3.7-2 .4-3.5.4-4.8-.3-1.2-.6-2.3-1.9-3-4.2 2.8-.5 4.4 0 5.5.8z"/><path d="M14.1 6a7 7 0 0 0-1.1 4c1.9-.1 3.3-.6 4.3-1.4 1-1 1.6-2.3 1.7-4.6-2.7.1-4 1-4.9 2z"/>',
zap: '<path d="M13 2 3 14h9l-1 8 10-12h-9l1-8z"/>',
cart: '<circle cx="9" cy="20" r="1.5"/><circle cx="18" cy="20" r="1.5"/><path d="M2.5 3h2.5l2.7 13.4a2 2 0 0 0 2 1.6h9a2 2 0 0 0 2-1.6L22.5 7H6"/>',
flame: '<path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.4-.5-2-1-3-1.1-2.1-.2-4 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.2.4-2.3 1-3a2.5 2.5 0 0 0 2.5 2.5z"/>',
leaf: '<path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.5 19.2 3c1.4 9.3-3.6 19.4-8.2 17z"/><path d="M2 21c0-3 1.85-5.36 5.08-6"/>',
package: '<path d="M16.5 9.4 7.55 4.24"/><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><path d="M3.27 6.96 12 12.01l8.73-5.05"/><path d="M12 22.08V12"/>',
radio: '<circle cx="12" cy="12" r="2"/><path d="M4.9 19.1A10 10 0 0 1 4.9 4.9"/><path d="M7.8 16.2a6 6 0 0 1 0-8.5"/><path d="M16.2 7.8a6 6 0 0 1 0 8.5"/><path d="M19.1 4.9a10 10 0 0 1 0 14.2"/>',
brain: '<path d="M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44A2.5 2.5 0 0 1 4.5 17a2.5 2.5 0 0 1-1.32-4.24A3 3 0 0 1 4.5 7a2.5 2.5 0 0 1 2.04-4.04A2.5 2.5 0 0 1 9.5 2z"/><path d="M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44A2.5 2.5 0 0 0 19.5 17a2.5 2.5 0 0 0 1.32-4.24A3 3 0 0 0 19.5 7a2.5 2.5 0 0 0-2.04-4.04A2.5 2.5 0 0 0 14.5 2z"/>',
lock: '<rect width="18" height="11" x="3" y="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
check: '<path d="M20 6 9 17l-5-5"/>',
mail: '<rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a2 2 0 0 1-2.06 0L2 7"/>',
shield: '<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>',
}
function icon(name, size = 22) {
const paths = ICON_PATHS[name] || ICON_PATHS.shield
return `<svg class="icon" width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${paths}</svg>`
}
// Map the emoji we used to live with onto an icon name so the existing
// rec-card / timeline data tables don't need a wholesale rewrite.
const EMOJI_TO_ICON = {
'💧': 'droplet',
'🌾': 'sprout', '🥦': 'leaf', '🌱': 'sprout',
'⚡': 'zap', '🕯️': 'flame',
'🛒': 'cart', '📦': 'package',
'📡': 'radio', '🧠': 'brain', '🔒': 'lock',
'✅': 'check', '📬': 'mail',
}
function iconForEmoji(emoji, size) { return icon(EMOJI_TO_ICON[emoji] || 'shield', size) }
// ══════════════════════════════════════
// STATE
// ══════════════════════════════════════
@@ -1159,7 +1198,7 @@ function getScenarioRecs(scenario) {
1: {
label: { en: 'Total Supply Collapse', de: 'Totaler Versorgungskollaps' },
cards: [
{ icon: '💧', cat: { en: 'Water', de: 'Wasser' }, title: { en: 'Atmospheric Water Generator', de: 'Atmosphärischer Wassergenerator' },
{ icon: 'droplet', cat: { en: 'Water', de: 'Wasser' }, title: { en: 'Atmospheric Water Generator', de: 'Atmosphärischer Wassergenerator' },
priority: { en: 'CRITICAL', de: 'KRITISCH' }, pCls: 'p-critical', color: '#1A4A5A',
items: [
{ name: { en: 'Solaris WaterGen A10 (10L/day)', de: 'Solaris WaterGen A10 (10L/Tag)' },
@@ -1169,7 +1208,7 @@ function getScenarioRecs(scenario) {
why: { en: 'AWG needs power. Store 3-day drinking buffer for power outage gaps.', de: 'AWG benötigt Strom. 3-Tage-Trinkwasserpuffer für Stromausfall-Lücken.' },
cost: '~€30', link: `https://www.${amzDomain}/s?k=food+grade+jerrycan+5L` },
]},
{ icon: '🌾', cat: { en: 'Food', de: 'Lebensmittel' }, title: { en: 'Caloric Base + Indoor Growing', de: 'Kalorienbasis + Indoor-Anbau' },
{ icon: 'sprout', cat: { en: 'Food', de: 'Lebensmittel' }, title: { en: 'Caloric Base + Indoor Growing', de: 'Kalorienbasis + Indoor-Anbau' },
priority: { en: 'CRITICAL', de: 'KRITISCH' }, pCls: 'p-critical', color: '#3A1A00',
items: [
{ name: { en: `White rice 25kg x${Math.max(2,Math.round(n))} bags`, de: `Weißer Reis 25kg x${Math.max(2,Math.round(n))} Säcke` },
@@ -1182,7 +1221,7 @@ function getScenarioRecs(scenario) {
why: { en: 'Fresh Vitamin C in 3 days. Zero equipment. Fastest food possible.', de: 'Frisches Vitamin C in 3 Tagen. Keine Ausrüstung. Die schnellste Nahrung überhaupt.' },
cost: '~€18', link: `https://www.${amzDomain}/s?k=sprouting+seeds+kit` },
]},
{ icon: '', cat: { en: 'Energy', de: 'Energie' }, title: { en: 'Solar Power System', de: 'Solar-Energiesystem' },
{ icon: 'zap', cat: { en: 'Energy', de: 'Energie' }, title: { en: 'Solar Power System', de: 'Solar-Energiesystem' },
priority: { en: 'CRITICAL', de: 'KRITISCH' }, pCls: 'p-critical', color: '#1A1A3A',
items: [
{ name: { en: 'Portable solar generator 500Wh (EcoFlow RIVER 2)', de: 'Tragbarer Solar-Generator 500Wh (EcoFlow RIVER 2)' },
@@ -1197,7 +1236,7 @@ function getScenarioRecs(scenario) {
2: {
label: { en: 'Partial Supply Collapse', de: 'Partieller Versorgungskollaps' },
cards: [
{ icon: '💧', cat: { en: 'Water', de: 'Wasser' }, title: { en: 'Water Buffer + Filtration', de: 'Wasserpuffer + Filtration' },
{ icon: 'droplet', cat: { en: 'Water', de: 'Wasser' }, title: { en: 'Water Buffer + Filtration', de: 'Wasserpuffer + Filtration' },
priority: { en: 'CRITICAL', de: 'KRITISCH' }, pCls: 'p-critical', color: '#1A3A4A',
items: [
{ name: { en: '5L food-grade jerrycans x10 (50L)', de: '5L Lebensmittelkanister x10 (50L)' },
@@ -1207,7 +1246,7 @@ function getScenarioRecs(scenario) {
why: { en: 'Gravity-fed, no electricity. Purifies any water source. Last-resort security.', de: 'Schwerkraftbetrieben, kein Strom. Reinigt jede Wasserquelle. Letzte Sicherheitslinie.' },
cost: '~€280', link: `https://www.${amzDomain}/s?k=Big+Berkey+water+filter` },
]},
{ icon: '🛒', cat: { en: 'Food', de: 'Lebensmittel' }, title: { en: '4-Week Pantry Reserve', de: '4-Wochen-Vorratskammer' },
{ icon: 'cart', cat: { en: 'Food', de: 'Lebensmittel' }, title: { en: '4-Week Pantry Reserve', de: '4-Wochen-Vorratskammer' },
priority: { en: 'HIGH', de: 'HOCH' }, pCls: 'p-high', color: '#2A1500',
items: [
{ name: { en: 'Rice, lentils, beans, oats — 4-week supply', de: 'Reis, Linsen, Bohnen, Hafer — 4-Wochen-Vorrat' },
@@ -1217,7 +1256,7 @@ function getScenarioRecs(scenario) {
why: { en: 'Fresh produce disappears first in partial collapse. Sprouts give Vitamin C in 3 days.', de: 'Frische Produkte verschwinden zuerst. Sprossen liefern Vitamin C in 3 Tagen.' },
cost: '~€18', link: `https://www.${amzDomain}/s?k=sprouting+seeds+lentil+mung` },
]},
{ icon: '🕯️', cat: { en: 'Energy', de: 'Energie' }, title: { en: 'Power & Heat Backup', de: 'Strom- & Wärme-Backup' },
{ icon: 'flame', cat: { en: 'Energy', de: 'Energie' }, title: { en: 'Power & Heat Backup', de: 'Strom- & Wärme-Backup' },
priority: { en: 'HIGH', de: 'HOCH' }, pCls: 'p-high', color: '#1A1A2A',
items: [
{ name: { en: 'Solar power bank 40,000mAh + 20W panel', de: 'Solar-Powerbank 40.000mAh + 20W-Panel' },
@@ -1232,7 +1271,7 @@ function getScenarioRecs(scenario) {
3: {
label: { en: 'Hyperinflation', de: 'Hyperinflation' },
cards: [
{ icon: '🌾', cat: { en: 'Food Hedge', de: 'Lebensmittel-Absicherung' }, title: { en: 'Buy Now While Prices Are Low', de: 'Jetzt kaufen, solange Preise niedrig sind' },
{ icon: 'sprout', cat: { en: 'Food Hedge', de: 'Lebensmittel-Absicherung' }, title: { en: 'Buy Now While Prices Are Low', de: 'Jetzt kaufen, solange Preise niedrig sind' },
priority: { en: 'CRITICAL', de: 'KRITISCH' }, pCls: 'p-critical', color: '#2A1A00',
items: [
{ name: { en: `Rice 25kg x${Math.max(4,Math.round(n*1.5))} bags (buy immediately)`, de: `Reis 25kg x${Math.max(4,Math.round(n*1.5))} Säcke (sofort kaufen)` },
@@ -1245,7 +1284,7 @@ function getScenarioRecs(scenario) {
why: { en: 'Historically proven inflation hedge. Divisible and recognisable for local barter.', de: 'Historisch bewährte Inflationsabsicherung. Teilbar und für lokalen Tauschhandel erkennbar.' },
cost: '~€3035/coin', link: `https://www.${amzDomain}/s?k=silver+coin+1+oz` },
]},
{ icon: '🌿', cat: { en: 'Self-Sufficiency', de: 'Selbstversorgung' }, title: { en: 'Grow Your Own — City', de: 'Selbst anbauen — Stadt' },
{ icon: 'leaf', cat: { en: 'Self-Sufficiency', de: 'Selbstversorgung' }, title: { en: 'Grow Your Own — City', de: 'Selbst anbauen — Stadt' },
priority: { en: 'HIGH', de: 'HOCH' }, pCls: 'p-high', color: '#0A1A0A',
items: [
{ name: { en: 'Indoor hydroponic tower (30 pods)', de: 'Indoor-Hydroponik-Turm (30 Pods)' },
@@ -1260,7 +1299,7 @@ function getScenarioRecs(scenario) {
4: {
label: { en: 'Food Shortage', de: 'Lebensmittelkrise' },
cards: [
{ icon: '🥦', cat: { en: 'Fresh Food', de: 'Frische Lebensmittel' }, title: { en: 'Indoor Food Production', de: 'Indoor-Lebensmittelproduktion' },
{ icon: 'leaf', cat: { en: 'Fresh Food', de: 'Frische Lebensmittel' }, title: { en: 'Indoor Food Production', de: 'Indoor-Lebensmittelproduktion' },
priority: { en: 'HIGH', de: 'HOCH' }, pCls: 'p-high', color: '#0A2A0A',
items: [
{ name: { en: '30-pod hydroponic tower', de: '30-Pod-Hydroponik-Turm' },
@@ -1270,7 +1309,7 @@ function getScenarioRecs(scenario) {
why: { en: 'During shortages, fresh produce disappears first. Lentil/mung sprouts ready in 3 days, any time.', de: 'Bei Engpässen verschwinden frische Produkte zuerst. Linsen-/Mungbohnen-Sprossen in 3 Tagen fertig.' },
cost: '~€25', link: `https://www.${amzDomain}/s?k=sprouting+seeds+kit+jars` },
]},
{ icon: '📦', cat: { en: 'Storage', de: 'Vorratshaltung' }, title: { en: '90-Day Food Reserve', de: '90-Tage-Lebensmittelvorrat' },
{ icon: 'package', cat: { en: 'Storage', de: 'Vorratshaltung' }, title: { en: '90-Day Food Reserve', de: '90-Tage-Lebensmittelvorrat' },
priority: { en: 'HIGH', de: 'HOCH' }, pCls: 'p-high', color: '#1A1200',
items: [
{ name: { en: 'Rice + legumes 90-day supply (buy now)', de: 'Reis + Hülsenfrüchte 90-Tage-Vorrat (jetzt kaufen)' },
@@ -1288,17 +1327,17 @@ function getScenarioRecs(scenario) {
function renderRiskBanner() {
const score = riskScore
let riskCls, riskTitle, riskDesc, riskEmoji
if (score >= 8) { riskCls='risk-critical'; riskEmoji='🔴'; riskTitle=t('risk_critical_title'); riskDesc=t('risk_critical_desc'); riskLevelStr='CRITICAL' }
else if (score >= 5) { riskCls='risk-high'; riskEmoji='🟠'; riskTitle=t('risk_high_title'); riskDesc=t('risk_high_desc'); riskLevelStr='HIGH RISK' }
else if (score >= 3) { riskCls='risk-medium'; riskEmoji='🟡'; riskTitle=t('risk_medium_title'); riskDesc=t('risk_medium_desc'); riskLevelStr='MODERATE' }
else { riskCls='risk-low'; riskEmoji='🟢'; riskTitle=t('risk_low_title'); riskDesc=t('risk_low_desc'); riskLevelStr='PREPARED' }
let riskCls, riskTitle, riskDesc
if (score >= 8) { riskCls='risk-critical'; riskTitle=t('risk_critical_title'); riskDesc=t('risk_critical_desc'); riskLevelStr='CRITICAL' }
else if (score >= 5) { riskCls='risk-high'; riskTitle=t('risk_high_title'); riskDesc=t('risk_high_desc'); riskLevelStr='HIGH RISK' }
else if (score >= 3) { riskCls='risk-medium'; riskTitle=t('risk_medium_title'); riskDesc=t('risk_medium_desc'); riskLevelStr='MODERATE' }
else { riskCls='risk-low'; riskTitle=t('risk_low_title'); riskDesc=t('risk_low_desc'); riskLevelStr='PREPARED' }
const el = document.getElementById('risk-banner-container')
if (!el) return
el.innerHTML = `
<div class="risk-banner ${riskCls}">
<div class="risk-level">${riskEmoji} ${t('risk_label')}</div>
<div class="risk-level">${t('risk_label')}</div>
<div class="risk-title">${riskTitle}</div>
<div class="risk-desc">${riskDesc}</div>
</div>`
@@ -1364,7 +1403,7 @@ function showScenario(n) {
const viewTxt = t('view_amazon')
html += `<div class="rec-card" style="animation-delay:${ci*0.08}s">
<div class="rec-header">
<div class="rec-icon" style="background:${card.color}20;border:1px solid ${card.color}40">${card.icon}</div>
<div class="rec-icon">${icon(card.icon, 22)}</div>
<div>
<div class="rec-cat">${card.cat[currentLang]}</div>
<div class="rec-title">${card.title[currentLang]}</div>
@@ -1378,7 +1417,7 @@ function showScenario(n) {
<div class="item-why">${item.why[currentLang]}</div>
<div style="display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px">
<div class="item-cost">${item.cost}</div>
${item.link ? `<a class="affiliate-btn" href="${item.link}" target="_blank" rel="noopener noreferrer">🛒 ${viewTxt}</a>` : ''}
${item.link ? `<a class="affiliate-btn" href="${item.link}" target="_blank" rel="noopener noreferrer">${icon('cart', 14)} ${viewTxt}</a>` : ''}
</div>
</div>`
})
@@ -1413,18 +1452,17 @@ function renderBudgetMeter() {
}
function renderTimeline() {
const colors = ['#E03A3A','#E07B20','#D4A820','#28A060','#4A8FE0']
const items = [
{ when: t('tl_week1'), action: t('tl_a1'), cost: '~€25', emoji: '🌱' },
{ when: t('tl_week2'), action: t('tl_a2'), cost: `~€${Math.round(getN()*20)}`, emoji: '🛒' },
{ when: t('tl_month1'), action: t('tl_a3'), cost: '~€520', emoji: '💧' },
{ when: t('tl_month2'), action: t('tl_a4'), cost: '~€700', emoji: '' },
{ when: t('tl_month3'), action: t('tl_a5'), cost: '~€600', emoji: '📡' },
{ when: t('tl_week1'), action: t('tl_a1'), cost: '~€25', icon: 'sprout' },
{ when: t('tl_week2'), action: t('tl_a2'), cost: `~€${Math.round(getN()*20)}`, icon: 'cart' },
{ when: t('tl_month1'), action: t('tl_a3'), cost: '~€520', icon: 'droplet' },
{ when: t('tl_month2'), action: t('tl_a4'), cost: '~€700', icon: 'zap' },
{ when: t('tl_month3'), action: t('tl_a5'), cost: '~€600', icon: 'radio' },
]
let html = ''
items.forEach((item, i) => {
items.forEach(item => {
html += `<div class="tl-item">
<div class="tl-dot" style="border-color:${colors[i]};color:${colors[i]}">${item.emoji}</div>
<div class="tl-dot">${icon(item.icon, 16)}</div>
<div class="tl-content">
<div class="tl-when">${item.when}</div>
<div class="tl-action">${item.action}</div>

View File

@@ -649,8 +649,29 @@ input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 22px;
box-shadow: 0 3px 6px rgba(0,0,0,0.06), inset 0 1px 0 rgba(255,255,255,0.7);
}
.rec-header { padding: 14px 16px; display: flex; align-items: center; gap: 12px; border-bottom: 1px solid rgba(0,0,0,0.06); }
.rec-icon { width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; font-size: 20px; flex-shrink: 0; }
.rec-cat { font-family: var(--font-mono); font-size: 10px; color: var(--text-dim); letter-spacing: 0.12em; text-transform: uppercase; }
/* Green-paint icon chip — matches the painted CTA buttons. ::before
carries the dark-green fill with the gloss filter; the icon SVG inside
inherits the warm-cream stroke colour via currentColor. */
.rec-icon {
position: relative;
isolation: isolate;
width: 40px; height: 40px;
border-radius: 10px;
display: flex; align-items: center; justify-content: center;
flex-shrink: 0;
color: #f4ecd8;
}
.rec-icon::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
z-index: -1;
background: #2a3010;
filter: url(#paintGlossBtn);
-webkit-filter: url(#paintGlossBtn);
}
.rec-cat { font-family: var(--font-mono); font-size:14px; color: var(--text-dim); letter-spacing: 0.12em; text-transform: uppercase; }
.rec-title { font-family: var(--font-display); font-weight: 400; font-size: 18px; color: var(--text); line-height: 1.2; }
.priority-badge { margin-left: auto; padding: 4px 10px; border-radius: 99px; font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.1em; font-weight: 700; flex-shrink: 0; }
.p-critical { background: rgba(90,154,120,0.10); color: var(--red); border: 1px solid rgba(90,154,120,0.30); }
@@ -698,10 +719,32 @@ input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 22px;
.tl-item { display: flex; gap: 16px; padding-bottom: 16px; position: relative; }
.tl-item:last-child { padding-bottom: 0; }
.tl-item:not(:last-child)::before { content: ''; position: absolute; left: 15px; top: 32px; bottom: 0; width: 2px; background: rgba(0,0,0,0.10); }
.tl-dot { width: 32px; height: 32px; border-radius: 50%; background: #F0F0F0; border: 2px solid; display: flex; align-items: center; justify-content: center; font-size: 14px; flex-shrink: 0; position: relative; z-index: 1; }
.tl-when { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-dim); margin-bottom: 4px; }
.tl-action { font-size: 14px; color: var(--text); line-height: 1.5; }
.tl-cost { font-family: var(--font-mono); font-size: 12px; color: var(--green-bright); margin-top: 4px; }
/* Timeline dot — green-paint circle that matches the rec-icon chip but
round. Same painted ::before with a slightly larger size so the icon
inside reads at small viewport sizes. */
.tl-dot {
position: relative;
isolation: isolate;
width: 32px; height: 32px;
border-radius: 50%;
display: flex; align-items: center; justify-content: center;
flex-shrink: 0;
z-index: 1;
color: #f4ecd8;
}
.tl-dot::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
z-index: -1;
background: #2a3010;
filter: url(#paintGlossBtn);
-webkit-filter: url(#paintGlossBtn);
}
.tl-when { font-family: var(--font-mono); font-size:14px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-dim); margin-bottom: 4px; }
.tl-action { font-size:16px; color: var(--text); line-height: 1.5; }
.tl-cost { font-family: var(--font-mono); font-size:15px; color: var(--green-bright); margin-top: 4px; }
/* ── RESULT PANELS — collapsible accordions for progressive disclosure.
Recommendations open by default; Budget and Timeline collapsed so the
@@ -738,16 +781,80 @@ input[type=range]::-webkit-slider-thumb { -webkit-appearance: none; width: 22px;
letter-spacing: 0.02em;
line-height: 1.3;
}
.rp-hint {
font-size: 10px;
color: var(--text-dim);
font-family: var(--font-mono);
letter-spacing: 0.08em;
text-transform: uppercase;
flex-shrink: 0;
/* ── ICONS ── inline SVG line-icons inherit colour via currentColor.
Sized via the width/height attribute on the <svg>; vertical-align
keeps the icon flush with its sibling text in flex/inline contexts. */
.icon { display: inline-block; vertical-align: middle; }
/* Narrative ("Your Personal Analysis") — green icon next to the title. */
.narrative-icon { display: inline-flex; color: var(--green-bright); }
/* Protein-offer badge — small lock chip prefixing the eyebrow text. */
.protein-offer-badge .badge-icon {
display: inline-flex;
align-items: center;
color: var(--green-bright);
margin-right: 4px;
vertical-align: -2px;
}
@media (max-width: 480px) {
.rp-hint { display: none; }
/* Success state inside the modal — large check + small mail prefix on
the spam-folder note. */
.success-check {
width: 56px; height: 56px;
border-radius: 50%;
background: rgba(90,154,120,0.10);
border: 1px solid rgba(90,154,120,0.30);
color: var(--green-bright);
display: flex; align-items: center; justify-content: center;
margin: 0 auto 10px;
}
.success-spam {
margin-top: 14px;
padding: 12px 16px;
background: rgba(212,168,32,0.1);
border: 1px solid rgba(212,168,32,0.35);
border-radius: 8px;
display: flex;
gap: 10px;
align-items: flex-start;
text-align: left;
}
.success-spam-icon {
flex-shrink: 0;
display: inline-flex;
color: #D4A820;
margin-top: 1px;
}
.success-spam > div {
font-size: 14px;
color: #D4A820;
line-height: 1.7;
}
/* Affiliate button — small cart icon flush with the label. */
.affiliate-btn .icon { color: currentColor; }
/* In-body hint above the scenario tabs — full-width sage-green paint
banner matching the protein-offer / risk-low look so it reads as a
small action prompt rather than a footnote. Center-aligned with a
touch more weight than the previous mono caption. */
.rp-hint {
display: block;
width: 100%;
text-align: center;
font-family: var(--font-body);
font-weight: 400;
font-size: 12px;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--text);
background: #E5F0E0;
border: 1px solid rgba(90,154,120,0.30);
border-radius: var(--radius);
padding: 10px 14px;
margin: 0 0 12px;
box-shadow: 0 3px 6px rgba(0,0,0,0.06), inset 0 1px 0 rgba(255,255,255,0.7);
}
.rp-chevron {
font-size: 14px;
@@ -1114,7 +1221,7 @@ body.mod-open .mod-panel { display: block; }
.narrative-tag {
margin-left: auto;
font-family: var(--font-mono);
font-size: 10px;
font-size: 11px;
color: var(--red);
letter-spacing: 0.12em;
text-transform: uppercase;
@@ -1175,7 +1282,7 @@ body.mod-open .mod-panel { display: block; }
text-align: center;
padding: 12px;
font-family: var(--font-mono);
font-size: 11px;
font-size: 10px;
color: var(--text-dim);
letter-spacing: 0.1em;
animation: pulse 2s infinite;
@@ -1290,7 +1397,7 @@ body.mod-open .mod-panel { display: block; }
padding: 14px 24px;
font-family: var(--font-body);
font-weight: 600;
font-size: 14px;
font-size: 13px;
letter-spacing: 0.1em;
text-transform: uppercase;
cursor: pointer;
@@ -1337,7 +1444,7 @@ body.mod-open .mod-panel { display: block; }
border-radius: 4px;
padding: 4px 10px;
font-family: var(--font-mono);
font-size: 10px;
font-size: 11px;
color: var(--green-bright);
letter-spacing: 0.12em;
text-transform: uppercase;
@@ -1508,7 +1615,7 @@ body.mod-open .mod-panel { display: block; }
.form-modal-close {
font-family: var(--font-body);
font-weight: 400;
font-size: 13px;
font-size: 14px;
letter-spacing: 0.2em;
text-transform: uppercase;
}
@@ -1624,7 +1731,7 @@ body.mod-open .mod-panel { display: block; }
/* In-form privacy note — sits between the protein field and newsletter
checkbox (above the fixed footer submit). */
.form-privacy {
font-size: 11px;
font-size: 10px;
color: var(--muted);
text-align: center;
line-height: 1.7;