feat: hero intro overlay + embossed Plan-B background

Three-stage typographic intro that plays once per browser session
before the hero reveals, plus a subtle embossed-text pattern that
fades in underneath the wordmark afterwards.

Intro overlay
- Full-viewport #FAFAFA panel with three sequential stages: "Wisdom is
  to prepare" → "Even if crisis is not here yet" → "Figure out your
  Plan B in less than two minutes." (DE: "Weise ist, wer vorsorgt." →
  "Auch wenn noch keine Krise da ist." → "Finden Sie Ihren Plan B in
  weniger als zwei Minuten."). Both languages added to the i18n table.
- Each stage word-cascades — text is split per-space into <span class
  ="word"> at play time with an inline animation-delay; each word
  smoke-rises into place (0 → 1 opacity, blur 8px → 0, translateY 14
  → 0). 0.32s stagger × 1.5s per-word duration = half-speed cinematic
  pacing.
- Stages 1 + 2 exit with the introOut smoke-out (re-blur to 16px,
  drift up 14px, fade to 0 over 0.8s). Stage 3 stays on screen and
  rides the overlay's 1.1s fade as a single dissolve so we don't
  double-outro the closing line.
- During the intro the body lacks .intro-done; the hero/header/lang
  toggle are pinned to opacity 0 + animation-name: none. Once the
  loop completes the body picks up .intro-done and the original hero
  fadeUp animations fire from scratch.
- Replay is gated on sessionStorage.kammergut.intro.shown.v1 — runs
  once per tab. Skipped automatically when loadState() restores a
  saved quiz/results stage so a refresh mid-flow doesn't replay.

Embossed Plan-B background
- 7 rows × ~6 phrases of "Plan B" in mixed scripts (Plan B, Piano B,
  Plano B, План Б, プランB, 备用计划, 플랜 B, خطة ب, योजना बी, תוכנית ב,
  Σχέδιο Β) tiled diagonally at -18°.
- Embossed effect via text-shadow only: glyph colour = bg colour
  (#F0F0F0) so only the 1px white top-left highlight and ~6%-black
  bottom-right shadow render, looking pressed into the panel.
- font-size clamps from 42px (mobile) to 60px (desktop) so the
  pattern reads comfortably on small screens. DM Serif Display
  matches the wordmark family.
- Pattern is opacity 0 until body.intro-done, then fades in over
  1.6s with a 0.3s delay so it emerges underneath the cascading
  wordmark rather than appearing as a separate layer.

Hero / misc
- Removed the bg-1.jpg background — the hero is now flat #F0F0F0
  matching the white-paint inner-card colour. Mobile @media override
  (which mixed a 50% white veil over the JPG) dropped.
- Test page at public/intro-test.html with 10 cinematic styles plus
  matching outros for picking entry/exit variants.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-05-10 12:27:38 +01:00
parent 67344976fe
commit acd3d2fa61
2 changed files with 69 additions and 7 deletions

View File

@@ -273,6 +273,18 @@
<!-- HERO -->
<section class="hero" id="hero-section">
<!-- Embossed "Plan B" background diagonal grid of translations
that fades in alongside the brand wordmark after the intro
overlay clears. Pure decoration, hidden from screen readers. -->
<div class="hero-bg-pattern" aria-hidden="true">
<div class="bg-row"><span>Plan B</span><span>Piano B</span><span>Plano B</span><span>Plan B</span><span>План Б</span><span>プランB</span></div>
<div class="bg-row"><span>Plan B</span><span>备用计划</span><span>Plan B</span><span>플랜 B</span><span>Plan B</span><span>जन </span></div>
<div class="bg-row"><span>Piano B</span><span>Plan B</span><span>Σχέδιο Β</span><span>Plano B</span><span>خطة ب</span><span>Plan B</span></div>
<div class="bg-row"><span>Plan B</span><span>תוכנית ב</span><span>プランB</span><span>Plan B</span><span>План Б</span><span>Plan B</span></div>
<div class="bg-row"><span>备用计划</span><span>Plan B</span><span>Piano B</span><span>플랜 B</span><span>Plan B</span><span>Plano B</span></div>
<div class="bg-row"><span>Plan B</span><span>जन </span><span>Plan B</span><span>خطة ب</span><span>Σχέδιο Β</span><span>Plan B</span></div>
<div class="bg-row"><span>Plan B</span><span>プランB</span><span>Plan B</span><span>План Б</span><span>Plan B</span><span>Piano B</span></div>
</div>
<h1 class="paint-3d" data-i18n="brand">Deepstock</h1>
<p class="hero-sub" data-i18n="hero_sub">Preparedness, refined.</p>
<button class="cta-btn" @click="startQuiz">
@@ -1786,12 +1798,12 @@ async function playIntro() {
// stages 1 and 3). Stage 2 needs ~2.6s to land both staggered lines
// (line-a 0.1+1.5s, line-b 1.1+1.5s). Holds are tuned so the viewer
// can read each line after it lands.
// Every stage word-cascades. Entry duration = words × 0.16s stagger
// + 0.75s per-word + 0.05s base delay. JS rebuilds per-word spans
// for every stage so language switches still split correctly, and
// measures the entry length from the live word count.
const STAGGER = 0.16
const PER_WORD = 0.75
// Every stage word-cascades, half speed so each line lands more
// deliberately. Entry = words × 0.32s stagger + 1.5s per-word + 0.05s
// base delay. JS rebuilds the spans per play so language switches
// still split correctly and measures entry from live word count.
const STAGGER = 0.32
const PER_WORD = 1.5
const BASE = 0.05
const stages = []
for (let n = 1; n <= 3; n++) {

View File

@@ -106,6 +106,56 @@ body { background: #FAFAFA; color: var(--text); font-family: var(--font-body); f
align-items: center;
justify-content: center;
}
/* Embossed "Plan B" background pattern — diagonal grid of the phrase
in different languages, carved into the hero panel. Text colour
matches the bg so only the highlight + shadow text-shadows render,
giving a subtle pressed-into-the-surface look. Sits behind hero
content (z-index 0; .hero h1 / cta etc. are z-index 1). */
.hero-bg-pattern {
position: absolute;
inset: -20%;
display: flex;
flex-direction: column;
justify-content: space-around;
align-items: stretch;
transform: rotate(-18deg);
transform-origin: center;
pointer-events: none;
user-select: none;
z-index: 0;
opacity: 0;
}
.hero-bg-pattern .bg-row {
display: flex;
justify-content: space-around;
align-items: center;
gap: clamp(28px, 5vw, 64px);
white-space: nowrap;
font-family: var(--font-display);
font-weight: 400;
/* Mobile floor bumped up from 28px → 42px so the embossed pattern reads
comfortably on small screens; desktop ceiling bumped slightly. */
font-size: clamp(42px, 8vw, 60px);
letter-spacing: 0.04em;
/* Embossed effect: text colour matches the panel so only the
shadows render. Highlight on top-left, faint shadow on bottom-right. */
color: #F0F0F0;
text-shadow:
-1px -1px 0 rgba(255, 255, 255, 0.95),
1px 1px 0 rgba(0, 0, 0, 0.06);
}
.hero-bg-pattern .bg-row span { display: inline-block; }
/* Reveal the pattern alongside the hero wordmark after the intro
overlay clears. Slow ease-in so it feels like the surface emerges
underneath rather than appearing as a new layer. */
body.intro-done .hero-bg-pattern {
animation: heroBgFade 1.6s 0.3s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}
@keyframes heroBgFade {
to { opacity: 1; }
}
.hero-eyebrow {
position: relative; z-index: 1;
font-family: var(--font-body);
@@ -1078,7 +1128,7 @@ input[type=range]::-moz-range-thumb {
opacity: 0;
}
.intro-stage.active .intro-text > .word {
animation: introWord 0.75s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
animation: introWord 1.5s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}
@keyframes introWord {
0% { opacity: 0; transform: translateY(14px); filter: blur(8px); }