Differences
This shows you the differences between two versions of the page.
| Both sides previous revision Previous revision Next revision | Previous revision | ||
| start [2026/06/11 07:58] – jumpstartadmin | start [2026/06/11 21:28] (current) – jumpstartadmin | ||
|---|---|---|---|
| Line 4: | Line 4: | ||
| @font-face { font-family: | @font-face { font-family: | ||
| - | .js-wrap { position: fixed; top: 50px; left: 0; width: 100vw; height: | + | /* Grid fills full viewport — navbar floats over it */ |
| + | .js-wrap { | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | | ||
| + | } | ||
| - | /* Scanline overlay | + | /* Scanline overlay */ |
| .js-wrap:: | .js-wrap:: | ||
| content: ''; | content: ''; | ||
| Line 20: | Line 33: | ||
| transparent 3px | transparent 3px | ||
| ); | ); | ||
| + | } | ||
| + | |||
| + | /* Edge vignette */ | ||
| + | .js-wrap:: | ||
| + | content: ''; | ||
| + | position: absolute; | ||
| + | inset: 0; | ||
| + | z-index: 101; | ||
| + | pointer-events: | ||
| + | background: | ||
| + | linear-gradient(to right, rgba(10, | ||
| + | linear-gradient(to bottom, rgba(10, | ||
| } | } | ||
| Line 32: | Line 57: | ||
| .js-card: | .js-card: | ||
| - | .js-label { position: absolute; left: 0; width: 100%; z-index: 3; padding: 16px; font-family: | + | .js-label { position: absolute; left: 0; width: 100%; z-index: 3; padding: 16px; font-family: |
| - | .js-card: | + | .js-card: |
| - | + | ||
| - | /* Center frame border at intersection */ | + | |
| - | .js-center-frame { | + | |
| - | position: fixed; | + | |
| - | top: 50%; | + | |
| - | left: 50%; | + | |
| - | transform: translate(-50%, -50%); | + | |
| - | width: 340px; | + | |
| - | height: 160px; | + | |
| - | z-index: 1001; | + | |
| - | pointer-events: | + | |
| - | animation: fadeIn | + | |
| - | } | + | |
| - | + | ||
| - | .js-center-frame:: | + | |
| - | .js-center-frame:: | + | |
| - | content: ''; | + | |
| - | position: absolute; | + | |
| - | inset: | + | |
| - | border: | + | |
| - | border-radius: | + | |
| - | } | + | |
| - | + | ||
| - | .js-center-frame:: | + | |
| - | inset: -4px; | + | |
| - | border-color: | + | |
| - | border-radius: | + | |
| - | } | + | |
| - | + | ||
| - | /* Corner accents */ | + | |
| - | .js-corner { | + | |
| - | position: absolute; | + | |
| - | width: 12px; | + | |
| - | height: 12px; | + | |
| - | border-color: | + | |
| - | border-style: | + | |
| - | animation: fadeIn 0.4s ease 1.2s both; | + | |
| - | } | + | |
| - | .js-corner.tl { top: -1px; left: -1px; border-width: | + | |
| - | .js-corner.tr { top: -1px; right: | + | |
| - | .js-corner.bl { bottom: | + | |
| - | .js-corner.br { bottom: -1px; right: -1px; border-width: | + | |
| /* Dark vignette behind title */ | /* Dark vignette behind title */ | ||
| Line 84: | Line 67: | ||
| left: 50%; | left: 50%; | ||
| transform: translate(-50%, | transform: translate(-50%, | ||
| - | width: | + | width: |
| - | height: | + | height: |
| z-index: 1000; | z-index: 1000; | ||
| pointer-events: | pointer-events: | ||
| - | background: radial-gradient(ellipse at center, rgba(10, | + | background: radial-gradient(ellipse at center, rgba(10, |
| animation: fadeIn 0.8s ease 0.2s both; | animation: fadeIn 0.8s ease 0.2s both; | ||
| } | } | ||
| - | /* Title */ | + | /* Title — centered in full viewport |
| .js-title { | .js-title { | ||
| position: fixed; | position: fixed; | ||
| - | top: 50px; | + | top: 0; |
| left: 0; | left: 0; | ||
| width: 100vw; | width: 100vw; | ||
| - | height: | + | height: 100vh; |
| z-index: 1002; | z-index: 1002; | ||
| pointer-events: | pointer-events: | ||
| Line 114: | Line 97: | ||
| } | } | ||
| - | .js-title-sub { font-family: | + | .js-title-sub { font-family: |
| - | .js-title-main { font-family: | + | .js-title-main { font-family: |
| .js-title-divider { display: flex; align-items: | .js-title-divider { display: flex; align-items: | ||
| Line 122: | Line 105: | ||
| .js-title-divider-diamond { width: 5px; height: 5px; background: rgba(184, | .js-title-divider-diamond { width: 5px; height: 5px; background: rgba(184, | ||
| - | /* Particle canvas */ | + | /* Particle canvas |
| - | # | + | # |
| @keyframes revealUp { from { opacity: 0; transform: translateY(20px); | @keyframes revealUp { from { opacity: 0; transform: translateY(20px); | ||
| Line 137: | Line 120: | ||
| .js-card: | .js-card: | ||
| .js-card: | .js-card: | ||
| + | |||
| + | /* VHS chromatic aberration — JS controlled */ | ||
| + | .js-card { | ||
| + | position: relative; | ||
| + | display: block; | ||
| + | overflow: hidden; | ||
| + | text-decoration: | ||
| + | transition: filter 0.25s ease !important; | ||
| + | } | ||
| + | |||
| + | .js-card.js-fading { | ||
| + | filter: saturate(0.55) brightness(0.92) !important; | ||
| + | } | ||
| + | |||
| + | /* Scanline overlay on fading cards */ | ||
| + | .js-card.js-fading:: | ||
| + | content: '' | ||
| + | position: absolute !important; | ||
| + | inset: 0 !important; | ||
| + | z-index: 12 !important; | ||
| + | pointer-events: | ||
| + | background: repeating-linear-gradient( | ||
| + | 0deg, | ||
| + | transparent 0px, | ||
| + | transparent 3px, | ||
| + | rgba(0, 0, 0, 0.08) 3px, | ||
| + | rgba(0, 0, 0, 0.08) 4px | ||
| + | ) !important; | ||
| + | } | ||
| + | |||
| + | /* RGB canvas overlay */ | ||
| + | .js-rgb-canvas { | ||
| + | position: absolute; | ||
| + | inset: 0; | ||
| + | z-index: 11; | ||
| + | pointer-events: | ||
| + | opacity: 0; | ||
| + | transition: opacity 0.6s ease; | ||
| + | } | ||
| + | .js-card.js-fading .js-rgb-canvas { | ||
| + | opacity: 1; | ||
| + | } | ||
| + | |||
| + | /* Block card hover effects when title zone is active */ | ||
| + | .js-wrap.js-title-active .js-card: | ||
| + | transform: scale(1) !important; | ||
| + | filter: brightness(0.6) saturate(0.8) !important; | ||
| + | } | ||
| + | .js-wrap.js-title-active .js-card: | ||
| + | opacity: 1 !important; | ||
| + | } | ||
| + | .js-wrap.js-title-active .js-card: | ||
| + | top: 50% !important; | ||
| + | transform: translateY(-50%) !important; | ||
| + | letter-spacing: | ||
| + | color: #EDE8F5 !important; | ||
| + | text-shadow: | ||
| + | opacity: 0.7 !important; | ||
| + | } | ||
| + | .js-wrap.js-title-active .js-card: | ||
| + | transform: translateX(-50%) scaleX(0) !important; | ||
| + | } | ||
| + | .js-wrap.js-title-active .js-card: | ||
| + | opacity: 0 !important; | ||
| + | } | ||
| + | |||
| + | /* Etched label underline */ | ||
| + | .js-label:: | ||
| + | content: ''; | ||
| + | position: absolute; | ||
| + | bottom: 6px; | ||
| + | left: 50%; | ||
| + | transform: translateX(-50%) scaleX(0); | ||
| + | width: 80%; | ||
| + | height: 1px; | ||
| + | background: linear-gradient(90deg, | ||
| + | transition: transform 0.4s ease; | ||
| + | } | ||
| + | .js-card: | ||
| + | transform: translateX(-50%) scaleX(1); | ||
| + | } | ||
| + | |||
| + | /* Film grain overlay on label hover */ | ||
| + | .js-label:: | ||
| + | content: ''; | ||
| + | position: absolute; | ||
| + | inset: 0; | ||
| + | background: repeating-linear-gradient( | ||
| + | 0deg, | ||
| + | transparent, | ||
| + | transparent 2px, | ||
| + | rgba(0, | ||
| + | rgba(0, | ||
| + | ); | ||
| + | pointer-events: | ||
| + | opacity: 0; | ||
| + | transition: opacity 0.4s ease; | ||
| + | } | ||
| + | .js-card: | ||
| + | opacity: 1; | ||
| + | } | ||
| </ | </ | ||
| - | <!-- Vignette behind title --> | ||
| <div class=" | <div class=" | ||
| - | <!-- Center frame --> | ||
| - | <div class=" | ||
| - | <div class=" | ||
| - | <div class=" | ||
| - | <div class=" | ||
| - | <div class=" | ||
| - | </ | ||
| - | |||
| - | <!-- Title --> | ||
| <div class=" | <div class=" | ||
| <div class=" | <div class=" | ||
| Line 161: | Line 235: | ||
| </ | </ | ||
| - | <!-- Particle canvas --> | ||
| <canvas id=" | <canvas id=" | ||
| Line 201: | Line 274: | ||
| < | < | ||
| - | // Particle system | ||
| const canvas = document.getElementById(' | const canvas = document.getElementById(' | ||
| const ctx = canvas.getContext(' | const ctx = canvas.getContext(' | ||
| Line 209: | Line 281: | ||
| const cx = canvas.width / 2; | const cx = canvas.width / 2; | ||
| const cy = canvas.height / 2; | const cy = canvas.height / 2; | ||
| - | |||
| const particles = []; | const particles = []; | ||
| Line 258: | Line 329: | ||
| canvas.height = window.innerHeight; | canvas.height = window.innerHeight; | ||
| }); | }); | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | // VHS RGB chromatic aberration using canvas | ||
| + | (function() { | ||
| + | const cards = document.querySelectorAll(' | ||
| + | const wrap = document.querySelector(' | ||
| + | const titleEl = document.querySelector(' | ||
| + | let activeCard = null; | ||
| + | let fadeTimeout = null; | ||
| + | const canvases = new Map(); | ||
| + | const animFrames = new Map(); | ||
| + | |||
| + | // Create a canvas overlay for each card | ||
| + | cards.forEach(card => { | ||
| + | const canvas = document.createElement(' | ||
| + | canvas.className = ' | ||
| + | card.appendChild(canvas); | ||
| + | canvases.set(card, | ||
| + | }); | ||
| + | |||
| + | function drawRGB(card) { | ||
| + | const canvas = canvases.get(card); | ||
| + | const video = card.querySelector(' | ||
| + | if (!video || !canvas) return; | ||
| + | |||
| + | const w = card.offsetWidth; | ||
| + | const h = card.offsetHeight; | ||
| + | canvas.width = w; | ||
| + | canvas.height = h; | ||
| + | const ctx = canvas.getContext(' | ||
| + | |||
| + | function frame() { | ||
| + | if (!card.classList.contains(' | ||
| + | ctx.clearRect(0, | ||
| + | animFrames.delete(card); | ||
| + | return; | ||
| + | } | ||
| + | ctx.clearRect(0, | ||
| + | |||
| + | ctx.save(); | ||
| + | ctx.globalCompositeOperation = ' | ||
| + | ctx.globalAlpha = 0.18; | ||
| + | ctx.filter = ' | ||
| + | ctx.drawImage(video, | ||
| + | ctx.restore(); | ||
| + | |||
| + | ctx.save(); | ||
| + | ctx.globalCompositeOperation = ' | ||
| + | ctx.globalAlpha = 0.14; | ||
| + | ctx.filter = ' | ||
| + | ctx.drawImage(video, | ||
| + | ctx.restore(); | ||
| + | |||
| + | animFrames.set(card, | ||
| + | } | ||
| + | |||
| + | if (!animFrames.has(card)) { | ||
| + | animFrames.set(card, | ||
| + | } | ||
| + | } | ||
| + | |||
| + | function isTitleZone(e) { | ||
| + | const sub = document.querySelector(' | ||
| + | const main = document.querySelector(' | ||
| + | const divider = document.querySelector(' | ||
| + | if (!sub || !main) return false; | ||
| + | const top = sub.getBoundingClientRect().top - 20; | ||
| + | const bottom = (divider || main).getBoundingClientRect().bottom + 20; | ||
| + | const left = main.getBoundingClientRect().left - 40; | ||
| + | const right = main.getBoundingClientRect().right + 40; | ||
| + | return e.clientX >= left && e.clientX <= right && | ||
| + | | ||
| + | } | ||
| + | |||
| + | function setTitleGlow(on) { | ||
| + | if (!titleEl) return; | ||
| + | if (on) { | ||
| + | titleEl.style.transition = ' | ||
| + | titleEl.style.textShadow = '0 0 120px rgba(184, | ||
| + | titleEl.style.color = '# | ||
| + | } else { | ||
| + | titleEl.style.transition = ' | ||
| + | titleEl.style.textShadow = ''; | ||
| + | titleEl.style.color = ''; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | function setFading(hoveredCard) { | ||
| + | if (activeCard === hoveredCard) return; | ||
| + | activeCard = hoveredCard; | ||
| + | setTitleGlow(false); | ||
| + | wrap.classList.remove(' | ||
| + | cards.forEach(card => { | ||
| + | if (card !== hoveredCard) { | ||
| + | card.classList.add(' | ||
| + | drawRGB(card); | ||
| + | } else { | ||
| + | card.classList.remove(' | ||
| + | } | ||
| + | }); | ||
| + | } | ||
| + | |||
| + | function clearFading() { | ||
| + | activeCard = null; | ||
| + | setTitleGlow(false); | ||
| + | wrap.classList.remove(' | ||
| + | cards.forEach(card => card.classList.remove(' | ||
| + | } | ||
| + | |||
| + | function enterTitleZone() { | ||
| + | activeCard = ' | ||
| + | setTitleGlow(true); | ||
| + | wrap.classList.add(' | ||
| + | cards.forEach(card => card.classList.remove(' | ||
| + | } | ||
| + | |||
| + | wrap.addEventListener(' | ||
| + | if (isTitleZone(e)) { | ||
| + | clearTimeout(fadeTimeout); | ||
| + | if (activeCard !== ' | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | // Find which card the mouse is over | ||
| + | let overCard = null; | ||
| + | cards.forEach(card => { | ||
| + | const rect = card.getBoundingClientRect(); | ||
| + | if (e.clientX >= rect.left && e.clientX <= rect.right && | ||
| + | e.clientY >= rect.top && e.clientY <= rect.bottom) { | ||
| + | overCard = card; | ||
| + | } | ||
| + | }); | ||
| + | |||
| + | if (overCard) { | ||
| + | clearTimeout(fadeTimeout); | ||
| + | setFading(overCard); | ||
| + | } | ||
| + | }); | ||
| + | |||
| + | wrap.addEventListener(' | ||
| + | clearTimeout(fadeTimeout); | ||
| + | fadeTimeout = setTimeout(clearFading, | ||
| + | }); | ||
| + | })(); | ||
| </ | </ | ||
| </ | </ | ||