From 88a373da804756f051437f956a70070dff7579f4 Mon Sep 17 00:00:00 2001 From: Dorian Date: Tue, 17 Mar 2026 00:51:25 +0000 Subject: [PATCH] feat: cinematic viewport-filling splash tagline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Logo draws in, shrinks up, then words slam in one-by-one: EVERYTHING / YOU / LOVE / IS A — each with blur-to-sharp animation. PSYOP lands last in italic serif (Georgia) with Bitcoin orange, oversized at 15vw, with a bounce-slam keyframe. Responsive: 4-line layout on mobile, 2-line on desktop. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/splash/LogoSplash.vue | 182 ++++++++++++++++++++++----- 1 file changed, 148 insertions(+), 34 deletions(-) diff --git a/src/components/splash/LogoSplash.vue b/src/components/splash/LogoSplash.vue index 7777785..272b3a7 100644 --- a/src/components/splash/LogoSplash.vue +++ b/src/components/splash/LogoSplash.vue @@ -5,6 +5,7 @@ const emit = defineEmits<{ complete: [] }>() const isAnimating = ref(false) const showTagline = ref(false) +const showPsyop = ref(false) const isFading = ref(false) const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches @@ -13,28 +14,34 @@ onMounted(() => { if (prefersReducedMotion) { isAnimating.value = true showTagline.value = true + showPsyop.value = true setTimeout(() => emit('complete'), 800) return } + // Phase 1: Logo draws in requestAnimationFrame(() => { isAnimating.value = true }) - // Logo finishes at ~1700ms (6 paths * 200ms stagger + 700ms draw) - // Show tagline after logo is fully revealed + // Phase 2: Logo shrinks up, tagline words slam in one by one setTimeout(() => { showTagline.value = true - }, 1900) + }, 1800) - // Start fading out after tagline has been visible + // Phase 3: "PSYOP" lands last with extra punch + setTimeout(() => { + showPsyop.value = true + }, 2800) + + // Phase 4: Hold, then fade setTimeout(() => { isFading.value = true - }, 3800) + }, 4200) setTimeout(() => { emit('complete') - }, 4200) + }, 4700) }) @@ -43,7 +50,7 @@ onMounted(() => { class="splash-overlay" :class="{ 'splash-fade-out': isFading }" > -
+
{ -

- EVERYTHING YOU LOVE IS A PSYOP -

+
+ EVERYTHING + YOU + LOVE + IS A + PSYOP +
@@ -79,7 +91,8 @@ onMounted(() => { align-items: center; justify-content: center; background: var(--bg-primary); - transition: opacity 400ms ease; + transition: opacity 500ms ease; + overflow: hidden; } .splash-fade-out { @@ -91,12 +104,24 @@ onMounted(() => { display: flex; flex-direction: column; align-items: center; - gap: 2.5rem; + gap: 2rem; + transition: transform 600ms cubic-bezier(0.16, 1, 0.3, 1); +} + +.splash-content.logo-shrunk { + transform: translateY(-8vh); } .splash-logo { - width: min(70vw, 400px); + width: min(60vw, 340px); height: auto; + transition: transform 600ms cubic-bezier(0.16, 1, 0.3, 1), + opacity 600ms ease; +} + +.logo-shrunk .splash-logo { + transform: scale(0.7); + opacity: 0.6; } .logo-path { @@ -122,22 +147,107 @@ onMounted(() => { to { opacity: 1; } } -.tagline { - font-family: system-ui, -apple-system, 'Helvetica Neue', sans-serif; - font-size: clamp(0.75rem, 2.5vw, 1.125rem); - font-weight: 900; - letter-spacing: 0.25em; - text-transform: uppercase; - color: var(--text-primary); - opacity: 0; - transform: translateY(12px); - transition: opacity 600ms ease, transform 600ms cubic-bezier(0.16, 1, 0.3, 1); - white-space: nowrap; +/* --- Tagline --- */ +.tagline-block { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0 0.4em; + width: 90vw; + max-width: 900px; + text-align: center; + line-height: 1.05; } -.tagline.visible { - opacity: 1; - transform: translateY(0); +.word { + font-family: system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif; + font-size: clamp(2.5rem, 10vw, 7rem); + font-weight: 900; + color: var(--text-primary); + text-transform: uppercase; + letter-spacing: -0.02em; + opacity: 0; + transform: translateY(40px) scale(1.1); + filter: blur(8px); +} + +.tagline-block.visible .word { + animation: slamIn 400ms cubic-bezier(0.16, 1, 0.3, 1) calc(var(--w) * 150ms) forwards; +} + +@keyframes slamIn { + 0% { + opacity: 0; + transform: translateY(40px) scale(1.1); + filter: blur(8px); + } + 60% { + opacity: 1; + transform: translateY(-4px) scale(1.0); + filter: blur(0); + } + 100% { + opacity: 1; + transform: translateY(0) scale(1.0); + filter: blur(0); + } +} + +/* PSYOP — different font, extra punch */ +.psyop { + font-family: 'Georgia', 'Times New Roman', 'Playfair Display', serif; + font-style: italic; + color: var(--accent); + display: block; + width: 100%; + font-size: clamp(3.5rem, 15vw, 10rem); + letter-spacing: 0.08em; + margin-top: 0.1em; + transform: translateY(60px) scale(1.3); + filter: blur(12px); +} + +.psyop.landed { + animation: psyopSlam 600ms cubic-bezier(0.16, 1, 0.3, 1) forwards; +} + +@keyframes psyopSlam { + 0% { + opacity: 0; + transform: translateY(60px) scale(1.3); + filter: blur(12px); + } + 40% { + opacity: 1; + transform: translateY(-6px) scale(1.02); + filter: blur(0); + } + 55% { + transform: translateY(2px) scale(0.99); + } + 70% { + transform: translateY(-1px) scale(1.0); + } + 100% { + opacity: 1; + transform: translateY(0) scale(1.0); + filter: blur(0); + } +} + +/* Mobile: 4-line layout */ +@media (max-width: 600px) { + .word { + font-size: clamp(2rem, 14vw, 4rem); + } + + .psyop { + font-size: clamp(3rem, 20vw, 5.5rem); + } + + .tagline-block { + width: 95vw; + } } @media (prefers-reduced-motion: reduce) { @@ -146,14 +256,18 @@ onMounted(() => { opacity: 1; } - .tagline { + .word, + .psyop { opacity: 1; transform: none; - transition: none; + filter: none; + animation: none !important; } - .splash-overlay { - transition-duration: 0ms; + .splash-overlay, + .splash-content, + .splash-logo { + transition: none; } }