intro updates

This commit is contained in:
Dorian
2026-05-10 15:26:07 +01:00
parent 63b1d4a30e
commit 068d333c8a
5 changed files with 325 additions and 45 deletions

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 122.88 116.82" style="enable-background:new 0 0 122.88 116.82" xml:space="preserve"><style type="text/css">.st0{fill-rule:evenodd;clip-rule:evenodd;}</style><g><path class="st0" d="M59.63,61.55h22.58l-7.87,15.24l13.4,0.24l-32.64,39.8l6.28-26.18H50.81L59.63,61.55L59.63,61.55z M44.2,71.76 c2.03,0,3.68,1.65,3.68,3.68c0,2.03-1.65,3.68-3.68,3.68H23.38l-0.46-0.04c-2.67-0.34-5.09-0.97-7.29-1.88 c-2.25-0.93-4.26-2.14-6.04-3.63H9.58c-1.68-1.4-3.15-2.99-4.4-4.72C1.84,64.25,0.04,58.63,0,53.03 c-0.04-5.66,1.72-11.29,5.52-15.85c1.23-1.48,2.68-2.84,4.34-4.04c1.93-1.4,4.14-2.58,6.64-3.55c1.72-0.67,3.56-1.23,5.5-1.68 c2.2-8.74,6.89-15.47,12.92-20.14c5.64-4.37,12.43-6.92,19.42-7.59c6.96-0.67,14.12,0.51,20.55,3.6 c7.37,3.54,13.43,9.56,17.11,16.87c1.6-0.25,3.2-0.38,4.79-0.36c6.72,0.05,13.2,2.45,18.3,7.95c5.31,5.72,7.88,14.14,7.79,21.82 c-0.07,6.31-1.77,12.59-5.25,17.22c-2.27,3.02-5.18,5.47-8.67,7.42c-3.36,1.88-7.28,3.31-11.68,4.33c-1.98,0.45-3.95-0.78-4.4-2.76 c-0.45-1.98,0.78-3.95,2.76-4.4c3.71-0.86,6.97-2.04,9.72-3.58c2.63-1.47,4.78-3.26,6.39-5.41c2.5-3.33,3.73-8.04,3.78-12.87 c0.06-5.07-1.18-10.16-3.59-13.86c-0.69-1.07-1.44-2.03-2.25-2.89c-3.61-3.89-8.19-5.59-12.95-5.62 c-3.46-0.02-7.02,0.81-10.41,2.31c-0.75,0.37-1.51,0.78-2.25,1.21c-2.25,1.32-4.48,2.93-6.74,4.78l-4.84-5.54 c1.67-1.55,3.48-2.96,5.4-4.21c1.53-1,3.13-1.88,4.77-2.65c0.66-0.33,1.33-0.64,2-0.93c-3.19-5.65-7.78-9.7-12.98-12.2 c-5.2-2.49-11.02-3.45-16.69-2.9c-5.63,0.54-11.1,2.59-15.62,6.1c-5.23,4.06-9.2,10.11-10.73,18.14l-0.48,2.51l-2.5,0.44 c-2.45,0.43-4.64,1.02-6.56,1.77c-1.86,0.72-3.52,1.61-4.97,2.66c-1.16,0.84-2.16,1.78-3.01,2.8c-2.63,3.15-3.85,7.1-3.82,11.1 c0.03,4.06,1.35,8.16,3.79,11.53c0.91,1.25,1.96,2.4,3.16,3.4l-0.01,0.01c1.2,1,2.58,1.83,4.13,2.47c1.53,0.63,3.22,1.08,5.09,1.34 H44.2L44.2,71.76z M57.07,85.6l6.59-18.76h10.23l-5.32,14.28l8.03,0.14l-13.51,20.22l4.03-15.88H57.07L57.07,85.6z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?><svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 98.47 122.88" style="enable-background:new 0 0 98.47 122.88" xml:space="preserve"><style type="text/css">.st0{fill-rule:evenodd;clip-rule:evenodd;}</style><g><path class="st0" d="M71.93,23.88c9.62,6.8,16.5,17.23,18.63,29.29h5.68c1.23,0,2.23,1.01,2.23,2.23v10.67 c0,1.22-1.01,2.23-2.23,2.23h-5.53c-1.95,12.43-8.93,23.2-18.78,30.16v18.77c0,3.11-2.54,5.65-5.64,5.65l-41.3,0 c-3.1,0-5.64-2.54-5.64-5.65V98.47C7.64,90.2,0,76.58,0,61.18c0-15.4,7.64-29.03,19.33-37.29V5.65c0-3.11,2.54-5.65,5.64-5.65h41.3 c3.1,0,5.64,2.54,5.64,5.65V23.88L71.93,23.88z M44.01,60.23c3.48,0,6.3,2.82,6.3,6.3c0,3.48-2.82,6.3-6.3,6.3 c-3.48,0-6.3-2.82-6.3-6.3C37.71,63.05,40.53,60.23,44.01,60.23L44.01,60.23z M52.85,68.01v-2.97c0-0.33,0.27-0.62,0.6-0.6 l22.36,1.52l-22.36,2.65C53.13,68.65,52.85,68.34,52.85,68.01L52.85,68.01L52.85,68.01z M41.35,56.31h5.31c0.33,0,0.6-0.27,0.6-0.6 l-3.08-22.35l-3.43,22.35C40.76,56.04,41.02,56.31,41.35,56.31L41.35,56.31L41.35,56.31z M45.03,24.38c0.31,0,0.62,0,0.93,0.01 h0.01c0.31,0.01,0.61,0.02,0.92,0.04h0.01c0.27,0.01,0.55,0.03,0.82,0.05l0.24,0.02c0.24,0.02,0.48,0.04,0.72,0.06l0.04,0l0.01,0 l0.06,0.01c0.26,0.03,0.52,0.06,0.78,0.09l0.12,0.02c0.26,0.03,0.52,0.07,0.78,0.11l0.14,0.02c0.26,0.04,0.52,0.08,0.78,0.13 l0.12,0.02c0.27,0.05,0.53,0.1,0.8,0.15l0.02,0.01c0.26,0.05,0.52,0.11,0.78,0.17l0.17,0.04c0.24,0.06,0.48,0.12,0.72,0.18 l0.16,0.04c0.24,0.06,0.48,0.13,0.72,0.2L55,25.77c0.26,0.07,0.51,0.15,0.76,0.23L55.79,26l0.06,0.02 c0.25,0.07,0.49,0.15,0.74,0.24l0.05,0.02l0.06,0.02c0.24,0.08,0.47,0.16,0.7,0.25l0.23,0.09c13.78,5.12,23.61,18.39,23.61,33.94 c0,16.22-10.68,29.95-25.38,34.55l-0.06,0.02l-0.02,0.01l-0.17,0.05l-0.33,0.1l-0.16,0.05l-0.04,0.01l-0.22,0.07l-0.57,0.15 l-0.17,0.04l-0.22,0.05l-0.55,0.14l-0.4,0.09l-0.17,0.04l-0.08,0.02l-0.54,0.11l-0.56,0.1l-0.2,0.04 c-0.26,0.05-0.53,0.09-0.79,0.13l-0.17,0.03c-0.2,0.03-0.4,0.06-0.6,0.08l-0.17,0.02l-0.02,0l-0.16,0.02l-0.54,0.06l-0.34,0.04 l-0.18,0.02h-0.01l-0.58,0.05l-0.03,0l-0.1,0.01l-0.66,0.04l-0.28,0.01c-0.25,0.01-0.5,0.02-0.76,0.03l-0.13,0 c-0.29,0.01-0.57,0.01-0.86,0.01c-0.29,0-0.58,0-0.86-0.01l-0.13,0c-0.25-0.01-0.51-0.02-0.76-0.03l-0.27-0.01l-0.66-0.04 l-0.1-0.01l-0.02,0c-0.2-0.01-0.39-0.03-0.59-0.05l-0.18-0.02l-0.33-0.04c-0.22-0.02-0.45-0.05-0.67-0.08l-0.04,0l-0.02,0 l-0.17-0.02l-0.62-0.09l-0.13-0.02c-0.26-0.04-0.55-0.09-0.81-0.13l-0.04-0.01l-0.13-0.02l-0.61-0.11l-0.25-0.05l-0.17-0.04 l-0.03-0.01l-0.28-0.06l-0.03-0.01l-0.17-0.04l-0.2-0.05l-0.64-0.16l-0.13-0.03l-0.04-0.01c-0.27-0.07-0.54-0.15-0.8-0.22 l-0.03-0.01l-0.01,0l-0.03-0.01C20.53,91.66,9.43,77.71,9.43,61.18c0-16.53,11.09-30.47,26.23-34.81l0.07-0.02l0.02,0 c0.27-0.08,0.54-0.15,0.81-0.22l0.03-0.01l0.03-0.01l0,0c0.26-0.06,0.53-0.13,0.79-0.19l0.12-0.03c0.54-0.12,1.08-0.24,1.62-0.33 l0.08-0.02l0.03,0c0.27-0.05,0.54-0.09,0.82-0.14l0.05-0.01l0.02,0l0.03,0c0.26-0.04,0.51-0.07,0.77-0.11l0.09-0.01l0.12-0.01 c0.25-0.03,0.5-0.06,0.75-0.08l0.05,0l0.01,0l0.03,0c0.25-0.02,0.5-0.05,0.75-0.07l0.22-0.02c0.28-0.02,0.55-0.04,0.83-0.05h0.01 c0.3-0.02,0.61-0.03,0.92-0.04h0.01c0.31-0.01,0.62-0.01,0.93-0.01L45.03,24.38L45.03,24.38z"/></g></svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 106.11 122.88"><defs><style>.cls-1{fill-rule:evenodd;}</style></defs><title>secure</title><path class="cls-1" d="M56.36,2.44A104.34,104.34,0,0,0,79.77,13.9a48.25,48.25,0,0,0,19.08,2.57l6.71-.61.33,6.74c1.23,24.79-2.77,46.33-11.16,63.32C86,103.6,72.58,116.37,55.35,122.85l-4.48,0c-16.84-6.15-30.16-18.57-39-36.47C3.62,69.58-.61,47.88.07,22l.18-6.65,6.61.34A64.65,64.65,0,0,0,28.23,13.5,60.59,60.59,0,0,0,48.92,2.79L52.51,0l3.85,2.44ZM52.93,19.3C66.46,27.88,78.68,31.94,89.17,31,91,68,77.32,96.28,53.07,105.41c-23.43-8.55-37.28-35.85-36.25-75,12.31.65,24.4-2,36.11-11.11ZM45.51,61.61a28.89,28.89,0,0,1,2.64,2.56,104.48,104.48,0,0,1,8.27-11.51c8.24-9.95,5.78-9.3,17.21-9.3L72,45.12a135.91,135.91,0,0,0-11.8,15.3,163.85,163.85,0,0,0-10.76,17.9l-1,1.91-.91-1.94a47.17,47.17,0,0,0-6.09-9.87,33.4,33.4,0,0,0-7.75-7.12c1.49-4.89,8.59-2.38,11.77.31Zm7.38-53.7c17.38,11,33.07,16.22,46.55,15,2.35,47.59-15.23,82.17-46.37,93.9C23,105.82,5.21,72.45,6.53,22.18,22.34,23,37.86,19.59,52.89,7.91Z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -228,13 +228,44 @@
loads in the same tab via sessionStorage. -->
<div class="intro-overlay" id="intro-overlay" aria-hidden="true">
<div class="intro-stage stage-1" data-stage="1">
<svg class="stage-icon icon-shield" viewBox="0 0 96 96" aria-hidden="true">
<circle class="badge" cx="48" cy="48" r="42"/>
<path class="glyph-stroke i-shield-outline" d="M48 20 C42.8 23.4 36 25.8 29.2 25.6 C28.5 48.2 35.4 67.2 48 75.6 C60.6 67.2 67.5 48.2 66.8 25.6 C60 25.8 53.2 23.4 48 20 Z"/>
<path class="glyph-fill i-secure-tick" d="M44.8 56.1 L38.9 50.1 L42 47 L45.1 50.2 L55.6 39.5 L58.8 42.6 Z"/>
</svg>
<span class="intro-text" data-i18n="intro_l1">Wisdom is to prepare</span>
</div>
<div class="intro-stage stage-2" data-stage="2">
<svg class="stage-icon icon-storm" viewBox="0 0 96 96" aria-hidden="true">
<circle class="badge" cx="48" cy="48" r="42"/>
<g class="glyph filled-icon" transform="translate(19.75 21.15) scale(0.46)">
<path class="i-cloud" d="M44.2,71.76 c2.03,0,3.68,1.65,3.68,3.68c0,2.03-1.65,3.68-3.68,3.68H23.38l-0.46-0.04c-2.67-0.34-5.09-0.97-7.29-1.88 c-2.25-0.93-4.26-2.14-6.04-3.63H9.58c-1.68-1.4-3.15-2.99-4.4-4.72C1.84,64.25,0.04,58.63,0,53.03 c-0.04-5.66,1.72-11.29,5.52-15.85c1.23-1.48,2.68-2.84,4.34-4.04c1.93-1.4,4.14-2.58,6.64-3.55c1.72-0.67,3.56-1.23,5.5-1.68 c2.2-8.74,6.89-15.47,12.92-20.14c5.64-4.37,12.43-6.92,19.42-7.59c6.96-0.67,14.12,0.51,20.55,3.6 c7.37,3.54,13.43,9.56,17.11,16.87c1.6-0.25,3.2-0.38,4.79-0.36c6.72,0.05,13.2,2.45,18.3,7.95c5.31,5.72,7.88,14.14,7.79,21.82 c-0.07,6.31-1.77,12.59-5.25,17.22c-2.27,3.02-5.18,5.47-8.67,7.42c-3.36,1.88-7.28,3.31-11.68,4.33c-1.98,0.45-3.95-0.78-4.4-2.76 c-0.45-1.98,0.78-3.95,2.76-4.4c3.71-0.86,6.97-2.04,9.72-3.58c2.63-1.47,4.78-3.26,6.39-5.41c2.5-3.33,3.73-8.04,3.78-12.87 c0.06-5.07-1.18-10.16-3.59-13.86c-0.69-1.07-1.44-2.03-2.25-2.89c-3.61-3.89-8.19-5.59-12.95-5.62 c-3.46-0.02-7.02,0.81-10.41,2.31c-0.75,0.37-1.51,0.78-2.25,1.21c-2.25,1.32-4.48,2.93-6.74,4.78l-4.84-5.54 c1.67-1.55,3.48-2.96,5.4-4.21c1.53-1,3.13-1.88,4.77-2.65c0.66-0.33,1.33-0.64,2-0.93c-3.19-5.65-7.78-9.7-12.98-12.2 c-5.2-2.49-11.02-3.45-16.69-2.9c-5.63,0.54-11.1,2.59-15.62,6.1c-5.23,4.06-9.2,10.11-10.73,18.14l-0.48,2.51l-2.5,0.44 c-2.45,0.43-4.64,1.02-6.56,1.77c-1.86,0.72-3.52,1.61-4.97,2.66c-1.16,0.84-2.16,1.78-3.01,2.8c-2.63,3.15-3.85,7.1-3.82,11.1 c0.03,4.06,1.35,8.16,3.79,11.53c0.91,1.25,1.96,2.4,3.16,3.4l-0.01,0.01c1.2,1,2.58,1.83,4.13,2.47c1.53,0.63,3.22,1.08,5.09,1.34 H44.2L44.2,71.76z"/>
</g>
<path class="glyph-fill i-lightning-flash" d="M47.2 49.5 L57.6 49.5 L54 56.5 L60.2 56.6 L45.2 74.9 L48.1 62.9 L43.2 62.9 Z"/>
</svg>
<span class="intro-text" data-i18n="intro_l2">Even if crisis is not here yet</span>
</div>
<div class="intro-stage stage-3" data-stage="3">
<span class="intro-text" data-i18n="intro_l3">Figure out your Plan B in less than two minutes.</span>
<!-- Clock face centred in the badge with no crown above it.
The minute hand sweeps 720° (two full revolutions) during
the second half of the sentence, ending back at the top. -->
<svg class="stage-icon icon-clock" viewBox="0 0 96 96" aria-hidden="true">
<circle class="badge" cx="48" cy="48" r="42"/>
<g class="glyph">
<!-- Face circle, centred in the badge. -->
<circle class="i-face" cx="48" cy="48" r="22"/>
<!-- Cardinal hour ticks. -->
<line class="i-tick" x1="48" y1="30" x2="48" y2="34"/>
<line class="i-tick" x1="66" y1="48" x2="62" y2="48"/>
<line class="i-tick" x1="48" y1="66" x2="48" y2="62"/>
<line class="i-tick" x1="30" y1="48" x2="34" y2="48"/>
<!-- Minute hand pointing to 12 rotates 720° over the line. -->
<line class="i-hand" x1="48" y1="48" x2="48" y2="32"/>
<!-- Center pin. -->
<circle class="i-pin" cx="48" cy="48" r="2.2"/>
</g>
</svg>
<span class="intro-text" data-i18n="intro_l3">Figure out your Plan B in less than 2 Minutes.</span>
</div>
</div>
@@ -613,8 +644,8 @@ const T = {
hero_sub: "Preparedness, refined.",
hero_cta: "Begin",
intro_l1: "Wisdom is to prepare",
intro_l2: "Even if crisis is not here yet",
intro_l3: "Figure out your Plan B in less than two minutes.",
intro_l2: "Even if\ncrisis is not here yet",
intro_l3: "Figure out your Plan B in less than 2 Minutes.",
stat_scenarios: "Scenarios",
stat_questions: "Questions",
stat_free: "Free Forever",
@@ -733,8 +764,8 @@ const T = {
hero_sub: "Vorsorge, verfeinert.",
hero_cta: "Beginnen",
intro_l1: "Weise ist, wer vorsorgt.",
intro_l2: "Auch wenn noch keine Krise da ist.",
intro_l3: "Finden Sie Ihren Plan B in weniger als zwei Minuten.",
intro_l2: "Auch wenn\nnoch keine Krise da ist.",
intro_l3: "Finden Sie Ihren Plan B in weniger als 2 Minuten.",
stat_scenarios: "Szenarien",
stat_questions: "Fragen",
stat_free: "Kostenlos",
@@ -1861,17 +1892,39 @@ async function playIntro() {
const text = textEl.textContent.trim()
textEl.innerHTML = ''
words = text.split(/\s+/)
// Per-stage emphasis. Stage 1 bolds "prepare" / "vorsorgt"; stage
// 2 bolds "crisis" / "krise"; stage 3 bolds the final two words
// ("2 Minutes." / "2 Minuten.") — both EN and DE land at the
// same word index. Word matching is case-insensitive and trailing
// punctuation is stripped before testing.
const emphasisRe = {
1: /^(prepare|vorsorgt)$/i,
2: /^(crisis|krise)$/i,
}[n] || null
const stripPunct = w => w.replace(/[.,;:!?]+$/, '')
// Stage 3 — bold "less than 2 Minutes." (last four words).
const boldFromIdx = (n === 3) ? 6 : Infinity
words.forEach((word, i) => {
const span = document.createElement('span')
span.className = 'word'
const isBold = i >= boldFromIdx || (emphasisRe && emphasisRe.test(stripPunct(word)))
span.className = 'word' + (isBold ? ' bold' : '')
span.style.animationDelay = (BASE + i * STAGGER) + 's'
span.textContent = word
textEl.appendChild(span)
// Stage 2 — insert a mobile-only line break after the leading
// conjunction so "crisis is not here yet" / "noch keine Krise
// da ist." sits on its own row on small viewports. Hidden on
// desktop via CSS.
if (n === 2 && /^(if|wenn)$/i.test(stripPunct(word))) {
textEl.appendChild(document.createElement('br'))
.className = 'mobile-break'
}
})
}
const entryMs = Math.round((BASE + (Math.max(0, words.length - 1)) * STAGGER + PER_WORD) * 1000)
// Stage 3 holds longest because it's the longest line.
const hold = n === 3 ? 1500 : 700
// Bigger hold on stage 1 so the opening line lingers; stage 3 (the
// longest sentence) holds the longest.
const hold = n === 3 ? 1500 : (n === 1 ? 1200 : 900)
stages.push({ sel: '.stage-' + n, enter: entryMs, hold })
}
const exit = 800 // matches the introOut (smoke out) duration

View File

@@ -90,7 +90,11 @@ body { background: #FAFAFA; color: var(--text); font-family: var(--font-body); f
.lang-btn.active { background: var(--red); color: #FFFFFF; }
/* ── APP ── */
.app { padding-top: 0; height: 100vh; height: 100dvh; display: flex; flex-direction: column; overflow: hidden; }
.app { padding-top: 0; height: 100vh; height: 100dvh; display: flex; flex-direction: column; overflow: hidden; position: relative; isolation: isolate; }
.app > :not(.page-bg-pattern) {
position: relative;
z-index: 1;
}
/* ── HERO ── */
.hero {
@@ -117,21 +121,13 @@ body { background: #FAFAFA; color: var(--text); font-family: var(--font-body); f
.page-bg-pattern {
position: fixed;
inset: 0;
/* z-index:-1 keeps the pattern below normal-flow content (hero / quiz
/ results) without forcing those sections into their own stacking
context. That matters because creating a context on .results-section
would trap the capture-modal (a position:fixed descendant) inside
it, and its z-index:200 wouldn't escape that context to sit above
the body-level site-header. Body bg is the canvas; the pattern
paints above the canvas and below positioned content. */
z-index: -1;
z-index: 0;
pointer-events: none;
user-select: none;
overflow: hidden;
background: #FAFAFA;
/* Parent is always visible — the intro overlay covers it during the
intro, and the per-row animations handle the staggered reveal once
intro-done lands. Nothing to fade on the wrapper. */
/* Parent is always visible. The intro overlay is translucent, so the
animated emboss pattern is already present behind the first line. */
opacity: 1;
}
.page-bg-pattern .bg-tilt {
@@ -178,28 +174,59 @@ body { background: #FAFAFA; color: var(--text); font-family: var(--font-body); f
sits inside the rotated .bg-tilt, so the slide is slightly off-axis
in screen space (matches the diagonal tilt). */
.page-bg-pattern .bg-row {
--row-delay: 0s;
opacity: 0;
will-change: opacity, transform;
}
.page-bg-pattern .bg-row:nth-child(odd) { transform: translateX(-160px); }
.page-bg-pattern .bg-row:nth-child(even) { transform: translateX( 160px); }
.page-bg-pattern .bg-row:nth-child(odd) {
transform: translateX(-160px);
animation:
bgRowSlideLeft 1.1s var(--row-delay) cubic-bezier(0.22, 0.61, 0.36, 1) forwards,
bgRowDriftLeft 14s calc(var(--row-delay) + 2.1s) ease-in-out infinite;
}
.page-bg-pattern .bg-row:nth-child(even) {
transform: translateX(160px);
animation:
bgRowSlideRight 1.1s var(--row-delay) cubic-bezier(0.22, 0.61, 0.36, 1) forwards,
bgRowDriftRight 14s calc(var(--row-delay) + 2.1s) ease-in-out infinite;
}
body:not(.intro-done) .page-bg-pattern .bg-row {
opacity: 1;
}
body:not(.intro-done) .page-bg-pattern .bg-row:nth-child(odd) {
animation: bgRowDriftLeft 14s ease-in-out infinite;
}
body:not(.intro-done) .page-bg-pattern .bg-row:nth-child(even) {
animation: bgRowDriftRight 14s ease-in-out infinite;
}
body:not(.intro-done) .page-bg-pattern .bg-row {
text-shadow:
-1px -1px 0 rgba(0, 0, 0, 0.075),
-2px -2px 1px rgba(0, 0, 0, 0.035),
1px 1px 0 rgba(255, 255, 255, 0.9);
}
body.intro-done .page-bg-pattern .bg-row:nth-child(odd) {
animation: bgRowSlideLeft 1.1s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
/* Simple slide-in per row, alternating direction, followed by a perpetual
alternating drift. This starts immediately so the pattern is visible
underneath the intro overlay from the first sentence onward. */
@keyframes bgRowDriftLeft {
0%, 100% { transform: translateX(0); }
50% { transform: translateX(-60px); }
}
body.intro-done .page-bg-pattern .bg-row:nth-child(even) {
animation: bgRowSlideRight 1.1s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
@keyframes bgRowDriftRight {
0%, 100% { transform: translateX(0); }
50% { transform: translateX(60px); }
}
body.intro-done .page-bg-pattern .bg-row:nth-child(1) { animation-delay: 0.00s; }
body.intro-done .page-bg-pattern .bg-row:nth-child(2) { animation-delay: 0.10s; }
body.intro-done .page-bg-pattern .bg-row:nth-child(3) { animation-delay: 0.20s; }
body.intro-done .page-bg-pattern .bg-row:nth-child(4) { animation-delay: 0.30s; }
body.intro-done .page-bg-pattern .bg-row:nth-child(5) { animation-delay: 0.40s; }
body.intro-done .page-bg-pattern .bg-row:nth-child(6) { animation-delay: 0.50s; }
body.intro-done .page-bg-pattern .bg-row:nth-child(7) { animation-delay: 0.60s; }
body.intro-done .page-bg-pattern .bg-row:nth-child(8) { animation-delay: 0.70s; }
body.intro-done .page-bg-pattern .bg-row:nth-child(9) { animation-delay: 0.80s; }
body.intro-done .page-bg-pattern .bg-row:nth-child(10) { animation-delay: 0.90s; }
.page-bg-pattern .bg-row:nth-child(1) { --row-delay: 0.00s; }
.page-bg-pattern .bg-row:nth-child(2) { --row-delay: 0.10s; }
.page-bg-pattern .bg-row:nth-child(3) { --row-delay: 0.20s; }
.page-bg-pattern .bg-row:nth-child(4) { --row-delay: 0.30s; }
.page-bg-pattern .bg-row:nth-child(5) { --row-delay: 0.40s; }
.page-bg-pattern .bg-row:nth-child(6) { --row-delay: 0.50s; }
.page-bg-pattern .bg-row:nth-child(7) { --row-delay: 0.60s; }
.page-bg-pattern .bg-row:nth-child(8) { --row-delay: 0.70s; }
.page-bg-pattern .bg-row:nth-child(9) { --row-delay: 0.80s; }
.page-bg-pattern .bg-row:nth-child(10) { --row-delay: 0.90s; }
@keyframes bgRowSlideLeft {
from { opacity: 0; transform: translateX(-160px); }
to { opacity: 1; transform: translateX(0); }
@@ -211,10 +238,7 @@ body.intro-done .page-bg-pattern .bg-row:nth-child(10) { animation-delay: 0.90s;
/* (Per-page bg override removed — every page now uses #FAFAFA.) */
/* No section-level stacking context needed — the pattern lives at
z-index:-1 so normal-flow sections paint above it automatically.
Avoiding a stacking context on .results-section also lets the
capture-modal (z-index:200) reach above the site-header. */
/* Hero copy sits above the fixed pattern layer. */
.hero-eyebrow {
position: relative; z-index: 1;
font-family: var(--font-body);
@@ -345,7 +369,7 @@ html[lang="en"] .hero h1 { font-size: calc((100vw - 32px) / 5.2); }
max-width: 520px;
margin: 0 auto 40px;
line-height: 1.6;
animation: fadeUp 1s 1.3s ease both;
animation: fadeUp 1s 2.0s ease both;
}
.cta-btn {
display: inline-flex;
@@ -360,7 +384,7 @@ html[lang="en"] .hero h1 { font-size: calc((100vw - 32px) / 5.2); }
border-radius: 0;
cursor: pointer;
transition: color 0.3s ease, var(--trans);
animation: fadeUp 1s 1.45s ease both;
animation: fadeUp 1s 2.45s ease both;
/* Lift above .hero::after radial fade so the button isn't washed out */
z-index: 2;
}
@@ -782,14 +806,26 @@ input[type=range]::-moz-range-thumb {
to dark for legibility on the light background. */
.rec-cards { animation: fadeUp 0.4s 0.15s ease both; }
.rec-card {
background: #F0F0F0;
/* Lighter card body — was #F0F0F0 (read as grey on the page); now
#FAFAFA so the product info sits on a near-white surface. The
header above takes the light sage tone as a visual section break. */
background: #FAFAFA;
border: 1px solid rgba(0,0,0,0.06);
border-radius: var(--radius);
margin-bottom: 12px;
overflow: hidden;
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); }
/* Header strip wears the same light sage as the .rp-hint banner so
the rec-card sections read as a coordinated family. */
.rec-header {
padding: 14px 16px;
display: flex;
align-items: center;
gap: 12px;
background: #E5F0E0;
border-bottom: 1px solid rgba(90,154,120,0.22);
}
/* 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. */
@@ -1143,7 +1179,7 @@ input[type=range]::-moz-range-thumb {
position: fixed;
inset: 0;
z-index: 1000;
background: #FAFAFA;
background: transparent;
display: flex;
align-items: center;
justify-content: center;
@@ -1157,12 +1193,177 @@ input[type=range]::-moz-range-thumb {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 28px;
text-align: center;
padding: 0 32px;
white-space: pre-line;
}
/* Stage icons — paint-style badge that visually persists across all
three stages (same circle, same paint filter, same position) so the
inner glyph appears to morph through it as stages crossfade. The
glyph itself draws in stroke-by-stroke per stage. Cream stroke on
dark green paint matches the CTA buttons. */
.stage-icon {
display: block;
width: clamp(72px, 13vw, 100px);
height: clamp(72px, 13vw, 100px);
fill: none;
opacity: 0;
transform: scale(0.78);
transform-origin: center;
}
.stage-icon .badge {
fill: #2a3010;
stroke: none;
filter: url(#paintGlossBtn);
-webkit-filter: url(#paintGlossBtn);
}
.stage-icon .glyph {
color: #f4ecd8;
stroke: currentColor;
/* Slightly thicker strokes so the cream icons read with confidence
against the dark green paint chip. */
stroke-width: 3.2;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
}
/* Per-element overrides — accent strokes (ridge, hour hand, side
buttons) are thinner so they read as detail rather than primary. */
.stage-icon .glyph .i-ridge { stroke-width: 1.4; opacity: 0.55; }
.stage-icon .glyph .i-side { stroke-width: 2.2; }
.stage-icon .glyph .i-hour { stroke-width: 2.6; }
.stage-icon .glyph .i-tick { stroke-width: 2.4; }
.stage-icon .glyph .i-pin { fill: currentColor; stroke: none; }
/* Bolder mark / bolt — the punctuation strokes inside each icon. */
.stage-icon .glyph .i-mark { stroke-width: 4.2; }
.stage-icon .glyph .i-bolt { stroke-width: 4.0; }
/* Glyph paths/lines/circles draw in stroke-by-stroke. pathLength: 1
normalises the dashoffset so each piece draws over the same time. */
.stage-icon .glyph path,
.stage-icon .glyph line,
.stage-icon .glyph circle {
stroke-dasharray: 1;
stroke-dashoffset: 1;
pathLength: 1;
}
.intro-stage.active .stage-icon {
opacity: 1;
transform: scale(1);
transition:
opacity 0.25s ease 0.05s,
transform 0.55s cubic-bezier(0.18, 0.89, 0.32, 1.18) 0.05s;
}
.intro-stage.active .stage-icon .glyph path,
.intro-stage.active .stage-icon .glyph line,
.intro-stage.active .stage-icon .glyph circle {
animation: introDraw 0.9s 0.1s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}
@keyframes introDraw {
to { stroke-dashoffset: 0; }
}
.stage-icon .filled-icon path {
fill: currentColor;
stroke: none;
opacity: 0;
transform: scale(0.84);
transform-box: fill-box;
transform-origin: center;
}
.intro-stage.active .stage-icon .filled-icon path {
animation: introGlyphPop 0.65s 0.18s cubic-bezier(0.18, 0.89, 0.32, 1.18) forwards;
}
@keyframes introGlyphPop {
to { opacity: 1; transform: scale(1); }
}
.stage-icon .glyph-stroke {
fill: none;
stroke: #fff8dc;
stroke-width: 4.2;
stroke-linecap: round;
stroke-linejoin: round;
stroke-dasharray: 1;
stroke-dashoffset: 1;
pathLength: 1;
opacity: 0;
filter: drop-shadow(0 0 5px rgba(255, 248, 220, 0.55));
}
.stage-icon .i-shield-outline {
stroke-width: 3.8;
}
.stage-icon .glyph-fill {
fill: #fff8dc;
stroke: none;
opacity: 0;
filter:
drop-shadow(0 0 5px rgba(255, 248, 220, 0.95))
drop-shadow(0 0 14px rgba(244, 236, 216, 0.7));
}
/* Stage 1 — the shield border appears first; the filled check ticks in
near the end of the sentence, after "prepare" has landed. */
.intro-stage.stage-1.active .i-shield-outline {
animation: introDraw 0.78s 0.18s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
opacity: 1;
}
.intro-stage.stage-1.active .i-secure-tick {
animation: tickFillIn 0.48s 2.35s cubic-bezier(0.18, 0.89, 0.32, 1.18) forwards;
transform-box: fill-box;
transform-origin: center;
}
@keyframes tickFillIn {
0% { opacity: 0; transform: scale(0.55) rotate(-8deg); }
65% { opacity: 1; transform: scale(1.13) rotate(0deg); }
100% { opacity: 1; transform: scale(1) rotate(0deg); }
}
/* Stage 2 — the supplied cloud icon appears first; the bolt flashes like
lightning once the crisis sentence is readable. */
.intro-stage.stage-2.active .i-lightning-flash {
animation: lightningStrike 1.08s 0.45s steps(1, end) infinite;
}
@keyframes lightningStrike {
0% { opacity: 0; }
7% { opacity: 1; }
12% { opacity: 0.16; }
18% { opacity: 1; }
25% { opacity: 0; }
42% { opacity: 0.92; }
50% { opacity: 0.18; }
64% { opacity: 1; }
72% { opacity: 0; }
100% { opacity: 0; }
}
/* Stage 3 — face + ticks draw, then the minute hand takes the sentence
timing to sweep two full rotations and land back at 12. */
.icon-clock .i-crown { animation-delay: 0.10s !important; }
.icon-clock .i-face { animation-delay: 0.20s !important; animation-duration: 0.7s !important; }
.icon-clock .i-tick { animation-delay: 0.70s !important; animation-duration: 0.3s !important; }
.icon-clock .i-hand {
transform-origin: 48px 48px;
animation-delay: 0.70s !important;
animation-duration: 0.35s !important;
}
.intro-stage.stage-3.active .icon-clock .i-hand {
animation: introDraw 0.35s 0.70s cubic-bezier(0.22, 0.61, 0.36, 1) forwards,
clockSweep 2.2s 1.35s linear forwards;
}
@keyframes clockSweep {
from { transform: rotate(0deg); }
to { transform: rotate(720deg); }
}
/* Stage exit — keep the badge/icon present while text dissolves, so the
circle feels continuous instead of outroing between sentences. */
.intro-stage.leaving .stage-icon {
opacity: 1;
transform: scale(1);
}
/* Intro text — uppercase Barlow at heading scale with wide tracking,
matching the "Preparedness. Refined." sub-line typeface. No paint
filter / no gradient text-fill (those tanked render performance during
@@ -1201,6 +1402,29 @@ input[type=range]::-moz-range-thumb {
was the lag source. */
will-change: opacity, transform;
}
/* Per-stage emphasis — JS adds .bold to specific words per intro line
("prepare" / "vorsorgt", "crisis" / "krise", and the closing clause
on stage 3). */
.intro-stage .intro-text > .word.bold {
font-weight: 700;
}
/* Stage 2 mobile line break — JS inserts <br class="mobile-break">
after the leading conjunction ("if" / "wenn") so the second clause
("crisis is not here yet" / "noch keine Krise da ist.") wraps onto
its own line on small viewports. Hidden on desktop via display:none
so the sentence renders on a single line. */
.intro-stage br.mobile-break { display: inline; }
@media (min-width: 768px) {
.intro-stage br.mobile-break { display: none; }
}
/* Mobile-only line break — JS inserts a <br class="mobile-break">
after "if" / "wenn" on stage 2. Display:none on wider viewports so
the line stays one row on desktop / tablet. */
.intro-stage .intro-text .mobile-break { display: inline; }
@media (min-width: 768px) {
.intro-stage .intro-text .mobile-break { display: none; }
}
.intro-stage.active .intro-text > .word {
animation: introWord 1.05s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}