feat: Musik global, Dennis-Teil auf eigene Seite, Mobile-Autoplay
- MusicPlayer ins Layout verschoben (läuft auf allen Seiten) - Mobile-Autoplay: Desktop startet stumm + fade-in bei Scroll, Mobile wartet auf ersten Touch und startet dann mit Fade-In - Dennis-Perspektive auf eigene Seite /meine-oma ausgelagert, dezenter Link "Von Dennis" am Ende der Tribute-Sektion - "Berge von Essen" entfernt - "Jacky und Niklas" → "ihre Enkelin" / "Meine Schwester" Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+7
-1
@@ -1,5 +1,6 @@
|
||||
import type { Metadata } from 'next'
|
||||
import { Cormorant_Garamond, Lora } from 'next/font/google'
|
||||
import GlobalMusicPlayer from '@/components/GlobalMusicPlayer'
|
||||
import './globals.css'
|
||||
|
||||
const cormorant = Cormorant_Garamond({
|
||||
@@ -18,6 +19,8 @@ const lora = Lora({
|
||||
display: 'swap',
|
||||
})
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: 'In Erinnerung an Maria Malejka',
|
||||
description:
|
||||
@@ -36,7 +39,10 @@ export default function RootLayout({
|
||||
}) {
|
||||
return (
|
||||
<html lang="de" className={`${cormorant.variable} ${lora.variable}`}>
|
||||
<body className="font-lora antialiased">{children}</body>
|
||||
<body className="font-lora antialiased">
|
||||
{children}
|
||||
<GlobalMusicPlayer />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
'use client'
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
import { ArrowLeft } from 'lucide-react'
|
||||
|
||||
const fade = {
|
||||
initial: { opacity: 0, y: 20 },
|
||||
whileInView: { opacity: 1, y: 0 },
|
||||
viewport: { once: true },
|
||||
transition: { duration: 0.8 },
|
||||
}
|
||||
|
||||
export default function MeineOmaPage() {
|
||||
return (
|
||||
<main className="min-h-screen bg-cream">
|
||||
<div className="max-w-2xl mx-auto px-6 py-20 sm:py-28">
|
||||
|
||||
<a
|
||||
href="/"
|
||||
className="inline-flex items-center gap-1.5 text-warm-brown-light/50 hover:text-warm-gold text-sm font-lora transition-colors mb-16"
|
||||
>
|
||||
<ArrowLeft size={14} />
|
||||
Zurück
|
||||
</a>
|
||||
|
||||
<motion.div {...fade} className="text-center mb-16">
|
||||
<p className="text-warm-gold/50 text-xs tracking-[0.5em] uppercase font-lora mb-4">
|
||||
Von Dennis
|
||||
</p>
|
||||
<h1 className="font-cormorant italic text-5xl sm:text-6xl text-warm-brown mb-4">
|
||||
Meine Oma
|
||||
</h1>
|
||||
<div className="flex items-center justify-center gap-4">
|
||||
<div className="h-px w-16 bg-warm-gold/30" />
|
||||
<span className="text-warm-gold/40 text-lg">✽</span>
|
||||
<div className="h-px w-16 bg-warm-gold/30" />
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<div className="space-y-8 font-lora text-warm-brown/80 text-base sm:text-lg leading-relaxed">
|
||||
<motion.p {...fade}>
|
||||
Ich kam aus der Schule und das Essen stand schon da. Jedes Mal.
|
||||
Rahmsauce. Ich weiß nicht, wie oft ich die gegessen habe, aber
|
||||
es war nie genug. Ich würde alles dafür geben, noch einmal an
|
||||
ihrem Küchentisch zu sitzen.
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Oma roch nach Oma. Ich weiß nicht, wie ich das anders beschreiben
|
||||
soll. Nicht nach Parfum. Nicht nach irgendwas, das man kaufen kann.
|
||||
Einfach nach ihr. Wenn ich an sie denke, ist das Erste, was
|
||||
kommt, dieses Gefühl. Diese Wärme. Der Geruch von ihrem Haus.
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Bei ihr war es immer heiß. Immer. Der Kamin lief, die Heizung
|
||||
lief, man hat geschwitzt und es war trotzdem schön. An Weihnachten
|
||||
war der Karpfen in der Badewanne und die Geschenke im Wohnzimmer.
|
||||
Aber die Tür blieb zu. Erst essen, dann Geschenke. Das war Gesetz.
|
||||
Und wir haben uns jedes Mal gefreut, als wären wir fünf.
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Wenn Pico bei ihr war, hab ich nicht mehr existiert. Sie hat mit
|
||||
ihm geredet, ihn gefüttert, ihn verwöhnt. Ich saß daneben und
|
||||
war Luft. Aber das war okay. Weil sie so glücklich war dabei.
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Pico ist letztes Jahr gestorben. Und jetzt Oma. Ich stelle mir
|
||||
vor, wie sie irgendwo sitzt und er neben ihr liegt und sie ihm
|
||||
wieder irgendwas erzählt, was er nicht versteht. Und er hört
|
||||
trotzdem zu.
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Sie war stur. Richtig stur. Deswegen hatte sie auch den
|
||||
Oberschenkelhalsbruch. Weil sie alles alleine machen wollte.
|
||||
Weil sie sich nichts sagen lassen hat. Man konnte sich aufregen.
|
||||
Aber im Nachhinein war das auch das, was sie so stark gemacht hat.
|
||||
</motion.p>
|
||||
|
||||
<motion.div
|
||||
{...fade}
|
||||
className="border-l-2 border-warm-gold/30 pl-6 py-2 my-12"
|
||||
>
|
||||
<p className="font-cormorant italic text-2xl sm:text-3xl text-warm-brown/70 leading-snug">
|
||||
Ich habe es dir viel zu selten gesagt, Oma.
|
||||
<br />
|
||||
Aber ich habe dich geliebt. Sehr.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Das letzte Mal hab ich sie im Krankenhaus besucht. In der
|
||||
Geriatrie. Sie lief schon wieder, es ging ihr gut. Sie hat
|
||||
im Aufenthaltsraum Blumenvasen gesehen und meinte, die könnte
|
||||
man ja mitnehmen. Ich hab gelacht. So war sie halt.
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Ihr Zimmer war gegenüber von dem Zimmer, in dem meine Mutter
|
||||
wochenlang gelegen hatte, als sie mit mir schwanger war. Mama
|
||||
durfte sich kaum bewegen damals. Im selben Flur, in dem ich auf
|
||||
die Welt kam, ist Oma gegangen. Das lässt mich nicht los.
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Am 10. Februar, um 13:13 Uhr, schrieb mir meine Schwester
|
||||
auf Telegram: <span className="italic text-warm-brown">„Sie
|
||||
möchten die Reanimation abbrechen."</span>
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Meine Schwester ist schwanger. Sie hat den Ultraschall mit in
|
||||
den Sarg gelegt. Ein Kind, das Oma nie treffen wird, aber das
|
||||
in ihrem Haus aufwachsen wird. In den Wänden, die nach Oma
|
||||
riechen. In der Küche, in der die Rahmsauce stand.
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Diese Seite ist für dich. Damit ich nicht vergesse. Damit
|
||||
niemand vergisst.
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<motion.div
|
||||
{...fade}
|
||||
className="mt-20 text-center"
|
||||
>
|
||||
<div className="flex items-center justify-center gap-4 mb-8">
|
||||
<div className="h-px w-12 bg-warm-gold/20" />
|
||||
<span className="text-warm-gold/30">✽</span>
|
||||
<div className="h-px w-12 bg-warm-gold/20" />
|
||||
</div>
|
||||
<a
|
||||
href="/"
|
||||
className="font-lora text-warm-brown-light/40 hover:text-warm-gold text-sm transition-colors"
|
||||
>
|
||||
Zurück zur Gedenkseite
|
||||
</a>
|
||||
</motion.div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import PhotoSlideshow from '@/components/PhotoSlideshow'
|
||||
import PhotoGallery from '@/components/PhotoGallery'
|
||||
import MemorySection from '@/components/MemorySection'
|
||||
import WriteSection from '@/components/WriteSection'
|
||||
import MusicPlayer from '@/components/MusicPlayer'
|
||||
import VideoGallery from '@/components/VideoGallery'
|
||||
import TributeSection from '@/components/TributeSection'
|
||||
|
||||
@@ -25,9 +24,6 @@ export default async function HomePage() {
|
||||
const videos = plain<MediaItem>(
|
||||
db.prepare("SELECT * FROM media WHERE type = 'video' ORDER BY sort_order, created_at").all()
|
||||
)
|
||||
const music = plain<MediaItem>(
|
||||
db.prepare("SELECT * FROM media WHERE type = 'music' ORDER BY sort_order, created_at").all()
|
||||
)
|
||||
const memories = plain<Memory>(
|
||||
db.prepare('SELECT * FROM memories ORDER BY created_at DESC').all()
|
||||
)
|
||||
@@ -93,9 +89,6 @@ export default async function HomePage() {
|
||||
{/* Videos */}
|
||||
<VideoGallery videos={videos} />
|
||||
|
||||
{/* Floating music player */}
|
||||
<MusicPlayer tracks={music} />
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="py-12 text-center border-t border-warm-border bg-amber-50/30">
|
||||
<div className="max-w-lg mx-auto px-4">
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { getDb } from '@/lib/db'
|
||||
import type { MediaItem } from '@/lib/types'
|
||||
import MusicPlayer from './MusicPlayer'
|
||||
|
||||
function plain<T>(rows: unknown[]): T[] {
|
||||
return JSON.parse(JSON.stringify(rows))
|
||||
}
|
||||
|
||||
export default function GlobalMusicPlayer() {
|
||||
const db = getDb()
|
||||
const music = plain<MediaItem>(
|
||||
db.prepare("SELECT * FROM media WHERE type = 'music' ORDER BY sort_order, created_at").all()
|
||||
)
|
||||
return <MusicPlayer tracks={music} />
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import type { MediaItem } from '@/lib/types'
|
||||
const TAIL_SKIP = 10
|
||||
const CROSSFADE_DURATION = 3
|
||||
const VOLUME = 0.4
|
||||
const FADE_IN_DURATION = 2000 // ms to fade in after first interaction
|
||||
const FADE_IN_MS = 2000
|
||||
|
||||
export default function MusicPlayer({ tracks }: { tracks: MediaItem[] }) {
|
||||
const track = tracks[0] ?? null
|
||||
@@ -17,7 +17,7 @@ export default function MusicPlayer({ tracks }: { tracks: MediaItem[] }) {
|
||||
const audioB = useRef<HTMLAudioElement>(null)
|
||||
const activeRef = useRef<'A' | 'B'>('A')
|
||||
const fadingRef = useRef(false)
|
||||
const unmutedRef = useRef(false)
|
||||
const startedRef = useRef(false)
|
||||
|
||||
const [userMuted, setUserMuted] = useState(false)
|
||||
const [playing, setPlaying] = useState(false)
|
||||
@@ -31,12 +31,7 @@ export default function MusicPlayer({ tracks }: { tracks: MediaItem[] }) {
|
||||
[],
|
||||
)
|
||||
|
||||
const getVolume = useCallback(
|
||||
() => (userMuted ? 0 : VOLUME),
|
||||
[userMuted],
|
||||
)
|
||||
|
||||
// Crossfade to loop back
|
||||
// ── Crossfade loop ──────────────────────────────────────────────────────
|
||||
const crossfade = useCallback(() => {
|
||||
if (fadingRef.current) return
|
||||
fadingRef.current = true
|
||||
@@ -51,7 +46,7 @@ export default function MusicPlayer({ tracks }: { tracks: MediaItem[] }) {
|
||||
|
||||
const startTime = performance.now()
|
||||
const outStartVol = out.volume
|
||||
const targetVol = getVolume()
|
||||
const targetVol = userMuted ? 0 : VOLUME
|
||||
|
||||
const step = () => {
|
||||
const t = Math.min((performance.now() - startTime) / (CROSSFADE_DURATION * 1000), 1)
|
||||
@@ -66,9 +61,9 @@ export default function MusicPlayer({ tracks }: { tracks: MediaItem[] }) {
|
||||
}
|
||||
}
|
||||
requestAnimationFrame(step)
|
||||
}, [getVolume, getActive, getInactive])
|
||||
}, [userMuted, getActive, getInactive])
|
||||
|
||||
// Monitor playback for crossfade trigger
|
||||
// Monitor for crossfade trigger
|
||||
useEffect(() => {
|
||||
if (!playing || !src) return
|
||||
let id: number
|
||||
@@ -86,7 +81,7 @@ export default function MusicPlayer({ tracks }: { tracks: MediaItem[] }) {
|
||||
return () => cancelAnimationFrame(id)
|
||||
}, [playing, src, getActive, crossfade])
|
||||
|
||||
// Fallback: if audio ends without crossfade, restart
|
||||
// Fallback if crossfade misses
|
||||
const handleEnded = useCallback(() => {
|
||||
const a = getActive()
|
||||
if (a) {
|
||||
@@ -95,50 +90,81 @@ export default function MusicPlayer({ tracks }: { tracks: MediaItem[] }) {
|
||||
}
|
||||
}, [getActive])
|
||||
|
||||
// Sync volume when user toggles mute
|
||||
// Sync volume on mute toggle
|
||||
useEffect(() => {
|
||||
if (!unmutedRef.current) return
|
||||
const vol = getVolume()
|
||||
const vol = userMuted ? 0 : VOLUME
|
||||
const a = getActive()
|
||||
const b = getInactive()
|
||||
if (a && !fadingRef.current) a.volume = vol
|
||||
if (b && !fadingRef.current && !b.paused) b.volume = vol
|
||||
}, [userMuted, getVolume, getActive, getInactive])
|
||||
}, [userMuted, getActive, getInactive])
|
||||
|
||||
// Autoplay strategy:
|
||||
// 1. Start muted (browsers allow this)
|
||||
// 2. On first user interaction (scroll/click/touch/key), fade volume in
|
||||
// ── Autoplay strategy ───────────────────────────────────────────────────
|
||||
// Mobile Safari needs play() called *directly* inside a user gesture.
|
||||
// Desktop Chrome allows muted autoplay (volume=0).
|
||||
//
|
||||
// Strategy:
|
||||
// 1. Try silent autoplay immediately (desktop)
|
||||
// 2. On first touch/click, call play() directly in the handler (mobile)
|
||||
// 3. Then fade volume in over 2s
|
||||
useEffect(() => {
|
||||
if (!src) return
|
||||
const a = audioA.current
|
||||
if (!a) return
|
||||
|
||||
// Start muted playback immediately
|
||||
const startAndFade = (a: HTMLAudioElement) => {
|
||||
if (startedRef.current) return
|
||||
startedRef.current = true
|
||||
a.volume = 0
|
||||
a.play().then(() => setPlaying(true)).catch(() => {})
|
||||
a.play().then(() => {
|
||||
setPlaying(true)
|
||||
// Fade in
|
||||
const t0 = performance.now()
|
||||
const fade = () => {
|
||||
const t = Math.min((performance.now() - t0) / FADE_IN_MS, 1)
|
||||
a.volume = VOLUME * t
|
||||
if (t < 1) requestAnimationFrame(fade)
|
||||
}
|
||||
requestAnimationFrame(fade)
|
||||
}).catch(() => {
|
||||
startedRef.current = false
|
||||
})
|
||||
}
|
||||
|
||||
// Fade in on first interaction
|
||||
// Attempt 1: silent autoplay (works on desktop)
|
||||
const a = audioA.current
|
||||
if (a) {
|
||||
a.volume = 0
|
||||
a.play().then(() => {
|
||||
startedRef.current = true
|
||||
setPlaying(true)
|
||||
// Already playing silently, fade in on first interaction
|
||||
const fadeIn = () => {
|
||||
if (unmutedRef.current) return
|
||||
unmutedRef.current = true
|
||||
|
||||
if (!startedRef.current) return
|
||||
const t0 = performance.now()
|
||||
const fade = () => {
|
||||
const active = activeRef.current === 'A' ? audioA.current : audioB.current
|
||||
if (!active) return
|
||||
|
||||
const startTime = performance.now()
|
||||
const step = () => {
|
||||
const t = Math.min((performance.now() - startTime) / FADE_IN_DURATION, 1)
|
||||
active.volume = VOLUME * t
|
||||
if (t < 1) requestAnimationFrame(step)
|
||||
const t = Math.min((performance.now() - t0) / FADE_IN_MS, 1)
|
||||
if (!fadingRef.current) active.volume = VOLUME * t
|
||||
if (t < 1) requestAnimationFrame(fade)
|
||||
}
|
||||
requestAnimationFrame(step)
|
||||
requestAnimationFrame(fade)
|
||||
}
|
||||
|
||||
const events = ['scroll', 'click', 'touchstart', 'keydown'] as const
|
||||
events.forEach((e) => window.addEventListener(e, fadeIn, { once: true, passive: true }))
|
||||
|
||||
return () => {
|
||||
events.forEach((e) => window.removeEventListener(e, fadeIn))
|
||||
}).catch(() => {
|
||||
// Attempt 2: autoplay blocked (mobile) → wait for user gesture
|
||||
// play() MUST be called synchronously inside the handler
|
||||
const onGesture = () => {
|
||||
const el = audioA.current
|
||||
if (el) startAndFade(el)
|
||||
// Clean up other listeners
|
||||
gestureEvents.forEach((e) => document.removeEventListener(e, onGesture))
|
||||
}
|
||||
const gestureEvents = ['click', 'touchstart', 'touchend', 'keydown'] as const
|
||||
gestureEvents.forEach((e) =>
|
||||
document.addEventListener(e, onGesture, { once: true, passive: true })
|
||||
)
|
||||
})
|
||||
}
|
||||
}, [src])
|
||||
|
||||
|
||||
@@ -13,8 +13,6 @@ export default function TributeSection() {
|
||||
return (
|
||||
<section id="ueber-oma" className="py-20 sm:py-28">
|
||||
<div className="max-w-2xl mx-auto px-6">
|
||||
|
||||
{/* ── Family perspective ─────────────────────────────────── */}
|
||||
<motion.div {...fade} className="text-center mb-16">
|
||||
<p className="text-warm-gold/50 text-xs tracking-[0.5em] uppercase font-lora mb-4">
|
||||
Für immer in unseren Herzen
|
||||
@@ -56,8 +54,8 @@ export default function TributeSection() {
|
||||
Weihnachten bei Oma und Opa war jedes Jahr das Gleiche, und genau
|
||||
das hat es so besonders gemacht. Der Kamin lief. Der Karpfen
|
||||
schwamm in der Badewanne. Die Geschenke lagen im Wohnzimmer,
|
||||
aber die Tür blieb zu, bis alle fertig gegessen hatten. Berge
|
||||
von Essen. Und sie mittendrin, glücklich wenn alle satt waren.
|
||||
aber die Tür blieb zu, bis alle fertig gegessen hatten.
|
||||
Und sie mittendrin, glücklich wenn alle satt waren.
|
||||
Sie hat es geliebt, zu geben.
|
||||
</motion.p>
|
||||
|
||||
@@ -76,8 +74,8 @@ export default function TributeSection() {
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Ihr Haus, in dem so viel passiert ist, wird jetzt von Jacky und
|
||||
Niklas weitergeführt. Bald wird dort ein neues Leben beginnen.
|
||||
Ihr Haus, in dem so viel passiert ist, wird jetzt von ihrer
|
||||
Enkelin weitergeführt. Bald wird dort ein neues Leben beginnen.
|
||||
Ein Kind, das seine Uroma nie kennenlernen wird. Aber dessen
|
||||
Ultraschallbild bei ihr im Sarg liegt. So nah, wie es nur geht.
|
||||
</motion.p>
|
||||
@@ -113,116 +111,7 @@ export default function TributeSection() {
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
{/* Divider between family and personal */}
|
||||
<motion.div {...fade} className="my-24 flex items-center justify-center gap-4">
|
||||
<div className="h-px w-20 bg-warm-gold/15" />
|
||||
<span className="text-warm-gold/25 text-sm">✽✽✽</span>
|
||||
<div className="h-px w-20 bg-warm-gold/15" />
|
||||
</motion.div>
|
||||
|
||||
{/* ── Dennis's perspective ───────────────────────────────── */}
|
||||
<motion.div {...fade} className="text-center mb-16">
|
||||
<p className="text-warm-gold/50 text-xs tracking-[0.5em] uppercase font-lora mb-4">
|
||||
Von Dennis
|
||||
</p>
|
||||
<h2 className="font-cormorant italic text-5xl sm:text-6xl text-warm-brown mb-4">
|
||||
Meine Oma
|
||||
</h2>
|
||||
<div className="flex items-center justify-center gap-4">
|
||||
<div className="h-px w-16 bg-warm-gold/30" />
|
||||
<span className="text-warm-gold/40 text-lg">✽</span>
|
||||
<div className="h-px w-16 bg-warm-gold/30" />
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
<div className="space-y-8 font-lora text-warm-brown/80 text-base sm:text-lg leading-relaxed">
|
||||
<motion.p {...fade}>
|
||||
Ich kam aus der Schule und das Essen stand schon da. Jedes Mal.
|
||||
Rahmsauce. Ich weiß nicht, wie oft ich die gegessen habe, aber
|
||||
es war nie genug. Ich würde alles dafür geben, noch einmal an
|
||||
ihrem Küchentisch zu sitzen.
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Oma roch nach Oma. Ich weiß nicht, wie ich das anders beschreiben
|
||||
soll. Nicht nach Parfum. Nicht nach irgendwas, das man kaufen kann.
|
||||
Einfach nach ihr. Wenn ich an sie denke, ist das Erste, was
|
||||
kommt, dieses Gefühl. Diese Wärme. Der Geruch von ihrem Haus.
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Bei ihr war es immer heiß. Immer. Der Kamin lief, die Heizung
|
||||
lief, man hat geschwitzt und es war trotzdem schön. An Weihnachten
|
||||
war der Karpfen in der Badewanne und die Geschenke im Wohnzimmer.
|
||||
Aber die Tür blieb zu. Erst essen, dann Geschenke. Das war Gesetz.
|
||||
Und wir haben uns jedes Mal gefreut, als wären wir fünf.
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Wenn Pico bei ihr war, hab ich nicht mehr existiert. Sie hat mit
|
||||
ihm geredet, ihn gefüttert, ihn verwöhnt. Ich saß daneben und
|
||||
war Luft. Aber das war okay. Weil sie so glücklich war dabei.
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Pico ist letztes Jahr gestorben. Und jetzt Oma. Ich stelle mir
|
||||
vor, wie sie irgendwo sitzt und er neben ihr liegt und sie ihm
|
||||
wieder irgendwas erzählt, was er nicht versteht. Und er hört
|
||||
trotzdem zu.
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Sie war stur. Richtig stur. Deswegen hatte sie auch den
|
||||
Oberschenkelhalsbruch. Weil sie alles alleine machen wollte.
|
||||
Weil sie sich nichts sagen lassen hat. Man konnte sich aufregen.
|
||||
Aber im Nachhinein war das auch das, was sie so stark gemacht hat.
|
||||
</motion.p>
|
||||
|
||||
<motion.div
|
||||
{...fade}
|
||||
className="border-l-2 border-warm-gold/30 pl-6 py-2 my-12"
|
||||
>
|
||||
<p className="font-cormorant italic text-2xl sm:text-3xl text-warm-brown/70 leading-snug">
|
||||
Ich habe es dir viel zu selten gesagt, Oma.
|
||||
<br />
|
||||
Aber ich habe dich geliebt. Sehr.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Das letzte Mal hab ich sie im Krankenhaus besucht. In der
|
||||
Geriatrie. Sie lief schon wieder, es ging ihr gut. Sie hat
|
||||
im Aufenthaltsraum Blumenvasen gesehen und meinte, die könnte
|
||||
man ja mitnehmen. Ich hab gelacht. So war sie halt.
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Ihr Zimmer war gegenüber von dem Zimmer, in dem meine Mutter
|
||||
wochenlang gelegen hatte, als sie mit mir schwanger war. Mama
|
||||
durfte sich kaum bewegen damals. Im selben Flur, in dem ich auf
|
||||
die Welt kam, ist Oma gegangen. Das lässt mich nicht los.
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Am 10. Februar, um 13:13 Uhr, schrieb mir meine Schwester
|
||||
auf Telegram: <span className="italic text-warm-brown">„Sie
|
||||
möchten die Reanimation abbrechen."</span>
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Jacky ist schwanger. Sie hat den Ultraschall mit in den Sarg
|
||||
gelegt. Ein Kind, das Oma nie treffen wird, aber das in ihrem
|
||||
Haus aufwachsen wird. In den Wänden, die nach Oma riechen. In
|
||||
der Küche, in der die Rahmsauce stand.
|
||||
</motion.p>
|
||||
|
||||
<motion.p {...fade}>
|
||||
Diese Seite ist für dich. Damit ich nicht vergesse. Damit
|
||||
niemand vergisst.
|
||||
</motion.p>
|
||||
</div>
|
||||
|
||||
{/* Closing details */}
|
||||
{/* Closing */}
|
||||
<motion.div
|
||||
{...fade}
|
||||
className="mt-20 text-center"
|
||||
@@ -241,6 +130,13 @@ export default function TributeSection() {
|
||||
<p className="font-lora text-warm-brown-light/30 text-xs mt-3 tracking-wide">
|
||||
Beerdigung am 20. Februar 2026
|
||||
</p>
|
||||
|
||||
<a
|
||||
href="/meine-oma"
|
||||
className="inline-block mt-10 font-cormorant italic text-warm-brown-light/30 hover:text-warm-gold/60 text-sm transition-colors duration-300"
|
||||
>
|
||||
Von Dennis
|
||||
</a>
|
||||
</motion.div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user