fix: mobile mountain animation + white text on dark backgrounds

- MountainClimb: mobile SVG viewport now follows the person (pan/zoom)
- MountainClimb: restored desktop SVG, removed card-only mobile fallback
- page.tsx: inline styles for all text in mc-dark-bg sections (white)
- page.tsx: remove "Nur noch 8 frei" badge
- page.tsx: fix negative copy ("kein neues Wissen") → positive umsetzungsfokus

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Athena 2026-05-05 10:37:50 +02:00
parent 9f09b4a3e7
commit 79f03d429d
2 changed files with 128 additions and 22 deletions

View file

@ -98,6 +98,11 @@ export default function MountainClimb() {
const summitCardX = Math.min(Math.max(summitRawCX, 8), VW - SUMMIT_W - 8); const summitCardX = Math.min(Math.max(summitRawCX, 8), VW - SUMMIT_W - 8);
const summitCardY = Math.max(wp.y - SUMMIT_H - 120, 4); const summitCardY = Math.max(wp.y - SUMMIT_H - 120, 4);
// Mobile pan: viewport 380×440 follows the person
const MOB_W = 380, MOB_H = 440;
const mobPanX = Math.min(0, Math.max(-(VW - MOB_W), -(wp.x - MOB_W / 2)));
const mobPanY = Math.min(0, Math.max(-(VH - MOB_H), -(wp.y - Math.round(MOB_H * 0.68))));
const replay = () => { const replay = () => {
started.current = true; started.current = true;
setStep(0); setStep(0);
@ -117,7 +122,7 @@ export default function MountainClimb() {
</h2> </h2>
</div> </div>
{/* ── Mountain animation (md+) ── */} {/* ── Mountain animation (desktop only) ── */}
<div <div
className="hidden md:block" className="hidden md:block"
onClick={() => !atSummit && setPaused((p) => !p)} onClick={() => !atSummit && setPaused((p) => !p)}
@ -315,20 +320,121 @@ export default function MountainClimb() {
)} )}
</div> </div>
{/* ── Mobile fallback: numbered list ── */} {/* ── Mobile: same SVG, viewport follows the person ── */}
<div className="md:hidden space-y-3"> <div
{STEPS.map((s, i) => ( className="md:hidden"
<div key={i} className="mc-card p-4 flex items-start gap-3"> onClick={() => !atSummit && setPaused((p) => !p)}
<span className="w-7 h-7 rounded-full bg-gradient-to-br from-indigo-500 to-violet-500 text-white text-xs font-bold flex items-center justify-center flex-shrink-0 mt-0.5"> style={{ cursor: atSummit ? "default" : "pointer" }}
{i + 1} >
</span> <svg
<div> viewBox={`0 0 ${MOB_W} ${MOB_H}`}
<p className="text-slate-700 text-sm font-semibold leading-snug">{s.title}</p> className="w-full rounded-2xl"
<p className="text-slate-500 text-xs leading-relaxed mt-0.5">{s.sub}</p> style={{ display: "block", overflow: "hidden", background: "linear-gradient(160deg, #f5f4ff, #f4f7fa)" }}
</div> aria-hidden="true"
>
<defs>
<linearGradient id="mgFillMob" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#e0e7ff" stopOpacity="0.55" />
<stop offset="100%" stopColor="#ede9fe" stopOpacity="0.12" />
</linearGradient>
</defs>
{/* Pan group — smoothly follows the person */}
<g style={{ transform: `translate(${mobPanX}px, ${mobPanY}px)`, transition: "transform 0.75s cubic-bezier(0.4,0.2,0.2,1)" }}>
{/* Mountain */}
<path d={FILL} fill="url(#mgFillMob)" />
<path d={RIDGE} fill="none" stroke="#a5b4fc" strokeWidth="2.5" strokeLinecap="round" />
{/* Dots */}
{WP.map((w, i) => (
<circle key={i} cx={w.x} cy={w.y}
r={step >= 0 && i <= step ? 5.5 : 3.5}
fill={step >= 0 && i <= step ? "#6366f1" : "#c7d2fe"}
style={{ transition: "r 0.3s, fill 0.3s" }}
/>
))}
{/* Stick figure */}
{step >= 0 && (
<g
transform={`translate(${wp.x}, ${wp.y}) rotate(${wp.lean})`}
style={{ transition: "transform 0.75s cubic-bezier(0.4,0.2,0.2,1)" }}
>
<g transform="scale(3)" className={atSummit ? "mc-bounce" : ""}
style={{ transformBox: "fill-box", transformOrigin: "50% 100%" }}>
<circle cx="0" cy="-29" r="7.5" fill="none" stroke="#4f46e5" strokeWidth="0.5" />
<line x1="0" y1="-21" x2="0" y2="-3" stroke="#4f46e5" strokeWidth="0.5" strokeLinecap="round" />
<line x1="0" y1="-16" x2="-10" y2="-8" stroke="#4f46e5" strokeWidth="0.5" strokeLinecap="round" />
<line x1="0" y1="-16" x2="10" y2="-24" stroke="#4f46e5" strokeWidth="0.5" strokeLinecap="round" />
<line x1="0" y1="-3" x2="-7" y2="10" stroke="#4f46e5" strokeWidth="0.5" strokeLinecap="round" />
<line x1="0" y1="-3" x2="7" y2="10" stroke="#4f46e5" strokeWidth="0.5" strokeLinecap="round" />
{atSummit && (
<g className="mc-fade-in" transform="translate(9, -25)">
<line x1="0" y1="0" x2="0" y2="-22" stroke="#6366f1" strokeWidth="0.5" strokeLinecap="round" />
<polygon points="0,-22 18,-15 0,-8" fill="#6366f1" />
</g>
)}
</g>
</g>
)}
{/* Step card */}
{step >= 0 && !atSummit && (
<>
<line x1={cardX + CARD_W / 2} y1={cardY + CARD_H} x2={wp.x} y2={wp.y - 90}
stroke="#c7d2fe" strokeWidth="1" strokeDasharray="4 3" />
<foreignObject key={step} x={cardX} y={cardY} width={CARD_W} height={CARD_H}
className="mc-fade-in" style={{ overflow: "visible" }}>
<div className="bg-white rounded-xl border border-indigo-100 shadow-lg" style={{ padding: "10px 12px" }}>
<div style={{ fontSize: "9px", fontWeight: 800, letterSpacing: "0.12em", textTransform: "uppercase", color: "#6366f1", marginBottom: "4px" }}>
Schritt {step + 1} / {STEPS.length}
</div>
<div style={{ fontSize: "13px", color: "#0f172a", fontWeight: 700, lineHeight: 1.3, marginBottom: "4px" }}>
{STEPS[step].title}
</div>
<div style={{ fontSize: "11px", color: "#64748b", lineHeight: 1.4 }}>
{STEPS[step].sub}
</div>
</div>
</foreignObject>
</>
)}
{/* Summit card */}
{atSummit && (
<>
<line x1={summitCardX + SUMMIT_W / 2} y1={summitCardY + SUMMIT_H} x2={wp.x} y2={wp.y - 90}
stroke="#a5b4fc" strokeWidth="1.5" strokeDasharray="4 3" />
<foreignObject key="summit-mob" x={summitCardX} y={summitCardY} width={SUMMIT_W} height={SUMMIT_H}
className="mc-fade-in" style={{ overflow: "visible" }}>
<div style={{ background: "linear-gradient(135deg, #6366f1, #8b5cf6)", borderRadius: "14px", padding: "16px 18px", boxShadow: "0 8px 32px rgba(99,102,241,0.45)", textAlign: "center" }}>
<div style={{ fontSize: "15px", fontWeight: 800, color: "#ffffff", lineHeight: 1.35 }}>
Ich habe jetzt ein Kundengewinnungssystem
</div>
</div>
</foreignObject>
</>
)}
</g>
</svg>
{/* Hints */}
{step >= 0 && !atSummit && (
<p className="text-center text-xs text-slate-400 mt-2 select-none">
{paused ? "▶ Tippen zum Fortsetzen" : "⏸ Tippen zum Pausieren"}
</p>
)}
{atSummit && (
<div className="text-center mt-3">
<button onClick={replay} className="text-sm text-indigo-600 border border-indigo-200 rounded-full px-5 py-2 hover:bg-indigo-50 transition-colors">
Nochmal ansehen
</button>
</div> </div>
))} )}
</div> </div>
</div> </div>
</section> </section>
); );

View file

@ -82,7 +82,7 @@ export default function Home() {
<div className="flex flex-col items-center gap-4"> <div className="flex flex-col items-center gap-4">
<Btn xl>Meinen Platz sichern </Btn> <Btn xl>Meinen Platz sichern </Btn>
<p className="text-sm text-slate-500"> <p className="text-sm text-slate-500">
Exklusive Kleingruppe · Maximal 10 Plätze · Nur noch 8 frei Exklusive Kleingruppe · Maximal 10 Plätze
</p> </p>
</div> </div>
</div> </div>
@ -157,7 +157,7 @@ export default function Home() {
Das <strong className="text-slate-900">8-Wochen Kundengewinnungs-System</strong> macht genau das: Marketing, Vertrieb und Social Media in einem System, das Woche für Woche aufgebaut wird. Das <strong className="text-slate-900">8-Wochen Kundengewinnungs-System</strong> macht genau das: Marketing, Vertrieb und Social Media in einem System, das Woche für Woche aufgebaut wird.
</p> </p>
<p className="text-slate-800 font-semibold text-lg leading-relaxed"> <p className="text-slate-800 font-semibold text-lg leading-relaxed">
Du baust kein neues Wissen auf. Du nimmst alles was du hast und bringst es in eine Struktur, die planbar die richtigen Kunden anzieht. Kein Theorie-Overload. Du nimmst alles was du hast und setzt es endlich in eine Struktur um, die planbar die richtigen Kunden anzieht.
</p> </p>
</div> </div>
</section> </section>
@ -167,10 +167,10 @@ export default function Home() {
*/} */}
<section className="mc-dark-bg py-16 text-center"> <section className="mc-dark-bg py-16 text-center">
<div className="max-w-2xl mx-auto px-6 space-y-5"> <div className="max-w-2xl mx-auto px-6 space-y-5">
<h2 className="text-2xl md:text-3xl font-extrabold text-white"> <h2 className="text-2xl md:text-3xl font-extrabold" style={{ color: "#ffffff" }}>
Nur noch <span className="bg-gradient-to-r from-indigo-400 to-violet-400 bg-clip-text text-transparent">8 Plätze</span> verfügbar. Sichere dir jetzt deinen Platz.
</h2> </h2>
<p className="text-slate-400">Start: 1. Juli 2027 · Pilotpreis: 500 (statt 3.000 )</p> <p style={{ color: "#e2e8f0" }}>Start: 1. Juli 2027 · Pilotpreis: 500 (statt 3.000 )</p>
<Btn xl>Meinen Platz sichern </Btn> <Btn xl>Meinen Platz sichern </Btn>
</div> </div>
</section> </section>
@ -410,20 +410,20 @@ export default function Home() {
*/} */}
<section className="mc-dark-bg py-24 text-center"> <section className="mc-dark-bg py-24 text-center">
<div className="max-w-2xl mx-auto px-6 space-y-7"> <div className="max-w-2xl mx-auto px-6 space-y-7">
<h2 className="text-3xl md:text-5xl font-extrabold text-white leading-tight"> <h2 className="text-3xl md:text-5xl font-extrabold leading-tight" style={{ color: "#ffffff" }}>
Dein Wissen ist da.{" "} Dein Wissen ist da.{" "}
<span className="bg-gradient-to-r from-indigo-400 to-violet-400 bg-clip-text text-transparent"> <span className="bg-gradient-to-r from-indigo-400 to-violet-400 bg-clip-text text-transparent">
Jetzt bekommt es die Struktur die Kunden bringt. Jetzt bekommt es die Struktur die Kunden bringt.
</span> </span>
</h2> </h2>
<p className="text-slate-400">Maximal 10 Plätze · Start: 1. Juli 2027</p> <p style={{ color: "#e2e8f0" }}>Maximal 10 Plätze · Start: 1. Juli 2027</p>
<Btn xl>Meinen Platz sichern </Btn> <Btn xl>Meinen Platz sichern </Btn>
<p className="text-sm text-slate-500">Ein kurzes Gespräch du entscheidest danach.</p> <p className="text-sm" style={{ color: "#cbd5e1" }}>Ein kurzes Gespräch du entscheidest danach.</p>
</div> </div>
</section> </section>
{/* FOOTER */} {/* FOOTER */}
<footer className="mc-nav-bg py-8 text-center text-slate-400 text-xs"> <footer className="mc-nav-bg py-8 text-center text-xs" style={{ color: "#cbd5e1" }}>
© 2026 Market Compass · Katja Pestereva © 2026 Market Compass · Katja Pestereva
</footer> </footer>