Mursey
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
“`
:root {
–ink: #0f0d0a;
–paper: #f5f0e8;
–cream: #ede6d6;
–rust: #b84c2b;
–gold: #c9963a;
–muted: #7a7060;
–serif: ‘Playfair Display’, Georgia, serif;
–mono: ‘Courier Prime’, ‘Courier New’, monospace;
}
html { scroll-behavior: smooth; }
body {
background: var(–paper);
color: var(–ink);
font-family: var(–mono);
overflow-x: hidden;
cursor: none;
}
/* Custom cursor */
.cursor {
position: fixed;
width: 12px; height: 12px;
background: var(–rust);
border-radius: 50%;
pointer-events: none;
z-index: 9999;
transform: translate(-50%, -50%);
transition: transform 0.1s, width 0.2s, height 0.2s;
mix-blend-mode: multiply;
}
.cursor-ring {
position: fixed;
width: 36px; height: 36px;
border: 1.5px solid var(–rust);
border-radius: 50%;
pointer-events: none;
z-index: 9998;
transform: translate(-50%, -50%);
transition: transform 0.15s ease-out, width 0.3s, height 0.3s, opacity 0.3s;
opacity: 0.5;
}
/* Noise overlay */
body::before {
content: ”;
position: fixed;
inset: 0;
background-image: url(“data:image/svg+xml,%3Csvg viewBox=’0 0 200 200′ xmlns=’http://www.w3.org/2000/svg’%3E%3Cfilter id=’n’%3E%3CfeTurbulence type=’fractalNoise’ baseFrequency=’0.75′ numOctaves=’4′ stitchTiles=’stitch’/%3E%3C/filter%3E%3Crect width=’100%25′ height=’100%25′ filter=’url(%23n)’ opacity=’0.04’/%3E%3C/svg%3E”);
pointer-events: none;
z-index: 1;
opacity: 0.4;
}
/* ── NAV ── */
nav {
position: fixed;
top: 0; left: 0; right: 0;
z-index: 100;
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.4rem 3rem;
mix-blend-mode: multiply;
}
.nav-logo {
font-family: var(–serif);
font-size: 1.1rem;
font-weight: 900;
letter-spacing: 0.25em;
text-transform: uppercase;
color: var(–ink);
text-decoration: none;
}
.nav-links { display: flex; gap: 2.5rem; list-style: none; }
.nav-links a {
font-size: 0.7rem;
letter-spacing: 0.2em;
text-transform: uppercase;
text-decoration: none;
color: var(–muted);
transition: color 0.2s;
}
.nav-links a:hover { color: var(–rust); }
/* ── HERO ── */
#hero {
min-height: 100vh;
display: grid;
grid-template-columns: 1fr 1fr;
position: relative;
overflow: hidden;
}
.hero-left {
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 8rem 3rem 5rem 3rem;
position: relative;
z-index: 2;
}
.hero-tag {
font-size: 0.65rem;
letter-spacing: 0.3em;
text-transform: uppercase;
color: var(–rust);
margin-bottom: 1.5rem;
opacity: 0;
animation: fadeUp 0.8s 0.3s forwards;
}
.hero-title {
font-family: var(–serif);
font-size: clamp(5rem, 9vw, 8.5rem);
font-weight: 900;
font-style: italic;
line-height: 0.88;
color: var(–ink);
opacity: 0;
animation: fadeUp 0.9s 0.5s forwards;
}
.hero-title span {
display: block;
color: var(–rust);
}
.hero-sub {
margin-top: 2rem;
font-size: 0.75rem;
letter-spacing: 0.15em;
line-height: 1.8;
color: var(–muted);
max-width: 320px;
opacity: 0;
animation: fadeUp 0.8s 0.8s forwards;
}
.hero-cta {
margin-top: 3rem;
display: inline-flex;
align-items: center;
gap: 1rem;
font-size: 0.7rem;
letter-spacing: 0.2em;
text-transform: uppercase;
text-decoration: none;
color: var(–ink);
border-bottom: 1px solid var(–ink);
padding-bottom: 0.3rem;
opacity: 0;
animation: fadeUp 0.8s 1s forwards;
transition: color 0.2s, border-color 0.2s;
}
.hero-cta:hover { color: var(–rust); border-color: var(–rust); }
.hero-cta::after { content: ‘→’; }
.hero-right {
position: relative;
background: var(–cream);
overflow: hidden;
opacity: 0;
animation: fadeIn 1.2s 0.4s forwards;
}
/* Decorative score lines */
.hero-right::before {
content: ”;
position: absolute;
inset: 0;
background-image: repeating-linear-gradient(
0deg,
transparent,
transparent 28px,
rgba(15,13,10,0.07) 28px,
rgba(15,13,10,0.07) 29px
);
pointer-events: none;
}
.hero-art {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
}
.hero-art svg {
width: 60%;
opacity: 0.13;
}
.hero-number {
position: absolute;
bottom: 3rem;
right: 3rem;
font-family: var(–serif);
font-size: 10rem;
font-weight: 900;
color: var(–ink);
opacity: 0.04;
line-height: 1;
user-select: none;
}
/* Vertical rule */
.v-rule {
position: absolute;
left: 50%;
top: 10%;
bottom: 10%;
width: 1px;
background: var(–ink);
opacity: 0.12;
}
/* Scroll indicator */
.scroll-hint {
position: absolute;
bottom: 2.5rem;
left: 3rem;
display: flex;
align-items: center;
gap: 0.8rem;
font-size: 0.6rem;
letter-spacing: 0.25em;
text-transform: uppercase;
color: var(–muted);
opacity: 0;
animation: fadeIn 1s 1.4s forwards;
}
.scroll-line {
width: 40px;
height: 1px;
background: var(–muted);
position: relative;
overflow: hidden;
}
.scroll-line::after {
content: ”;
position: absolute;
left: -100%;
top: 0;
width: 100%;
height: 100%;
background: var(–rust);
animation: scanLine 2s 1.8s infinite;
}
/* ── SECTION BASE ── */
section {
position: relative;
z-index: 2;
}
.section-label {
font-size: 0.6rem;
letter-spacing: 0.35em;
text-transform: uppercase;
color: var(–rust);
margin-bottom: 1rem;
}
.section-title {
font-family: var(–serif);
font-size: clamp(2.5rem, 4vw, 3.8rem);
font-weight: 700;
line-height: 1.1;
color: var(–ink);
}
/* ── ABOUT ── */
#about {
padding: 9rem 3rem;
display: grid;
grid-template-columns: 1fr 2fr;
gap: 6rem;
align-items: start;
border-top: 1px solid rgba(15,13,10,0.12);
}
.about-left { position: sticky; top: 8rem; }
.about-body {
font-size: 1rem;
line-height: 1.9;
color: var(–ink);
font-family: var(–mono);
}
.about-body p { margin-bottom: 1.5rem; }
.members {
margin-top: 3.5rem;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
}
.member-card {
padding: 1.8rem;
border: 1px solid rgba(15,13,10,0.12);
position: relative;
background: var(–cream);
transition: transform 0.3s, box-shadow 0.3s;
}
.member-card:hover {
transform: translateY(-4px);
box-shadow: 6px 6px 0 var(–rust);
}
.member-card::before {
content: attr(data-num);
position: absolute;
top: 1rem; right: 1.2rem;
font-family: var(–serif);
font-size: 3rem;
font-weight: 900;
color: var(–ink);
opacity: 0.05;
}
.member-name {
font-family: var(–serif);
font-size: 1.3rem;
font-weight: 700;
font-style: italic;
margin-bottom: 0.4rem;
}
.member-role {
font-size: 0.65rem;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(–rust);
}
/* ── PORTFOLIO ── */
#portfolio {
padding: 9rem 3rem;
border-top: 1px solid rgba(15,13,10,0.12);
background: var(–ink);
color: var(–paper);
}
#portfolio .section-label { color: var(–gold); }
#portfolio .section-title { color: var(–paper); }
.portfolio-header {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: 4rem;
}
.portfolio-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5px;
}
.portfolio-item {
aspect-ratio: 4/3;
background: #1a1814;
position: relative;
overflow: hidden;
cursor: none;
}
.portfolio-item:first-child {
grid-column: span 2;
aspect-ratio: auto;
}
.portfolio-bg {
position: absolute;
inset: 0;
background: linear-gradient(135deg, #1e1b15, #2a2520);
transition: transform 0.6s ease;
}
/* Musical note pattern per card */
.portfolio-item:nth-child(1) .portfolio-bg { background: linear-gradient(135deg, #1e1b15 0%, #2e2318 100%); }
.portfolio-item:nth-child(2) .portfolio-bg { background: linear-gradient(135deg, #181c1e 0%, #1e2828 100%); }
.portfolio-item:nth-child(3) .portfolio-bg { background: linear-gradient(135deg, #1e1518 0%, #2a1e20 100%); }
.portfolio-item:nth-child(4) .portfolio-bg { background: linear-gradient(135deg, #181618 0%, #241824 100%); }
.portfolio-item:hover .portfolio-bg { transform: scale(1.04); }
.portfolio-lines {
position: absolute;
inset: 0;
background-image: repeating-linear-gradient(
0deg,
transparent,
transparent 22px,
rgba(255,255,255,0.04) 22px,
rgba(255,255,255,0.04) 23px
);
}
.portfolio-content {
position: absolute;
inset: 0;
padding: 2rem;
display: flex;
flex-direction: column;
justify-content: flex-end;
background: linear-gradient(to top, rgba(0,0,0,0.7) 0%, transparent 60%);
}
.portfolio-type {
font-size: 0.58rem;
letter-spacing: 0.3em;
text-transform: uppercase;
color: var(–gold);
margin-bottom: 0.5rem;
}
.portfolio-name {
font-family: var(–serif);
font-size: 1.5rem;
font-weight: 700;
font-style: italic;
color: var(–paper);
margin-bottom: 0.3rem;
}
.portfolio-detail {
font-size: 0.65rem;
letter-spacing: 0.1em;
color: rgba(245,240,232,0.5);
}
/* Large decorative note */
.deco-note {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 8rem;
opacity: 0.04;
pointer-events: none;
user-select: none;
transition: opacity 0.4s;
}
.portfolio-item:hover .deco-note { opacity: 0.07; }
/* ── CONTACT ── */
#contact {
padding: 9rem 3rem;
border-top: 1px solid rgba(15,13,10,0.12);
display: grid;
grid-template-columns: 1fr 1fr;
gap: 6rem;
align-items: center;
}
.contact-left .section-title {
margin-bottom: 2rem;
}
.contact-intro {
font-size: 0.85rem;
line-height: 1.9;
color: var(–muted);
max-width: 380px;
}
.contact-form {
display: flex;
flex-direction: column;
gap: 0;
}
.form-row {
border-top: 1px solid rgba(15,13,10,0.15);
display: flex;
align-items: stretch;
}
.form-row:last-of-type { border-bottom: 1px solid rgba(15,13,10,0.15); }
.form-label {
font-size: 0.58rem;
letter-spacing: 0.25em;
text-transform: uppercase;
color: var(–muted);
min-width: 80px;
padding: 1.2rem 0;
display: flex;
align-items: center;
}
.form-input {
flex: 1;
background: transparent;
border: none;
outline: none;
font-family: var(–mono);
font-size: 0.85rem;
color: var(–ink);
padding: 1.2rem 1rem;
resize: none;
}
.form-input::placeholder { color: rgba(15,13,10,0.25); }
.form-input:focus {
background: rgba(184,76,43,0.03);
}
.form-submit {
margin-top: 2.5rem;
display: inline-flex;
align-items: center;
gap: 1rem;
background: var(–ink);
color: var(–paper);
border: none;
padding: 1.1rem 2.5rem;
font-family: var(–mono);
font-size: 0.7rem;
letter-spacing: 0.2em;
text-transform: uppercase;
cursor: none;
transition: background 0.2s, transform 0.15s;
align-self: flex-start;
}
.form-submit:hover { background: var(–rust); transform: translateX(4px); }
.form-submit::after { content: ‘→’; }
/* ── FOOTER ── */
footer {
padding: 2.5rem 3rem;
border-top: 1px solid rgba(15,13,10,0.12);
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
z-index: 2;
}
.footer-name {
font-family: var(–serif);
font-size: 0.9rem;
font-weight: 700;
font-style: italic;
color: var(–ink);
}
.footer-copy {
font-size: 0.6rem;
letter-spacing: 0.15em;
color: var(–muted);
}
/* ── ANIMATIONS ── */
@keyframes fadeUp {
from { opacity: 0; transform: translateY(24px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes scanLine {
from { left: -100%; }
to { left: 100%; }
}
/* Scroll reveal */
.reveal {
opacity: 0;
transform: translateY(30px);
transition: opacity 0.7s ease, transform 0.7s ease;
}
.reveal.visible {
opacity: 1;
transform: translateY(0);
}
/* ── LISTEN / MUSIC PLAYER ── */
#listen {
padding: 9rem 3rem;
border-top: 1px solid rgba(15,13,10,0.12);
background: var(–cream);
}
.listen-header {
display: flex;
justify-content: space-between;
align-items: flex-end;
margin-bottom: 4rem;
}
.player-wrap {
display: grid;
grid-template-columns: 1fr 1.6fr;
gap: 3rem;
align-items: start;
}
/* Track list */
.tracklist {
border-top: 1px solid rgba(15,13,10,0.15);
}
.track-row {
display: flex;
align-items: center;
gap: 1.2rem;
padding: 1.1rem 0;
border-bottom: 1px solid rgba(15,13,10,0.1);
cursor: none;
transition: background 0.2s;
position: relative;
}
.track-row:hover { background: rgba(184,76,43,0.04); }
.track-row.active { background: rgba(184,76,43,0.06); }
.track-row.active .track-title { color: var(–rust); }
.track-num {
font-family: var(–serif);
font-size: 0.75rem;
color: var(–muted);
min-width: 1.8rem;
text-align: right;
font-style: italic;
transition: opacity 0.2s;
}
.track-row.active .track-num,
.track-row:hover .track-num { opacity: 0; }
.track-play-icon {
position: absolute;
left: 0;
font-size: 0.7rem;
color: var(–rust);
opacity: 0;
transition: opacity 0.2s;
}
.track-row:hover .track-play-icon,
.track-row.active .track-play-icon { opacity: 1; }
.track-row.active.playing .track-play-icon::after { content: ‘▐▐’; }
.track-row.active:not(.playing) .track-play-icon::after { content: ‘▶’; }
.track-row:not(.active):hover .track-play-icon::after { content: ‘▶’; }
.track-info { flex: 1; }
.track-title {
font-family: var(–serif);
font-size: 1rem;
font-weight: 700;
font-style: italic;
color: var(–ink);
transition: color 0.2s;
}
.track-meta {
font-size: 0.6rem;
letter-spacing: 0.15em;
text-transform: uppercase;
color: var(–muted);
margin-top: 0.2rem;
}
.track-duration {
font-size: 0.65rem;
color: var(–muted);
font-family: var(–mono);
}
/* Player card */
.player-card {
background: var(–ink);
color: var(–paper);
padding: 2.5rem;
position: sticky;
top: 8rem;
}
.player-now-label {
font-size: 0.58rem;
letter-spacing: 0.3em;
text-transform: uppercase;
color: var(–gold);
margin-bottom: 1.5rem;
}
.player-track-title {
font-family: var(–serif);
font-size: 1.8rem;
font-weight: 700;
font-style: italic;
line-height: 1.15;
margin-bottom: 0.4rem;
}
.player-track-meta {
font-size: 0.65rem;
letter-spacing: 0.15em;
text-transform: uppercase;
color: rgba(245,240,232,0.4);
margin-bottom: 2rem;
}
/* Waveform visualizer (canvas) */
.waveform-wrap {
margin-bottom: 1rem;
position: relative;
height: 60px;
cursor: none;
}
#waveCanvas {
width: 100%;
height: 100%;
display: block;
}
/* Progress bar */
.progress-wrap {
position: relative;
height: 2px;
background: rgba(245,240,232,0.15);
margin-bottom: 0.6rem;
cursor: none;
}
.progress-fill {
height: 100%;
background: var(–rust);
width: 0%;
transition: width 0.3s linear;
pointer-events: none;
}
.progress-handle {
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
width: 10px; height: 10px;
background: var(–paper);
border-radius: 50%;
left: 0%;
transition: left 0.3s linear;
pointer-events: none;
}
.time-row {
display: flex;
justify-content: space-between;
font-size: 0.6rem;
color: rgba(245,240,232,0.4);
margin-bottom: 2rem;
font-family: var(–mono);
}
/* Controls */
.player-controls {
display: flex;
align-items: center;
justify-content: center;
gap: 2rem;
}
.ctrl-btn {
background: none;
border: none;
color: rgba(245,240,232,0.5);
font-size: 1rem;
cursor: none;
padding: 0.4rem;
transition: color 0.2s, transform 0.15s;
line-height: 1;
}
.ctrl-btn:hover { color: var(–paper); transform: scale(1.1); }
.ctrl-play {
width: 52px; height: 52px;
border-radius: 50%;
border: 1.5px solid rgba(245,240,232,0.3) !important;
display: flex; align-items: center; justify-content: center;
font-size: 1.1rem !important;
color: var(–paper) !important;
transition: border-color 0.2s, background 0.2s, transform 0.15s !important;
}
.ctrl-play:hover { border-color: var(–rust) !important; background: rgba(184,76,43,0.15) !important; }
/* Volume */
.volume-row {
display: flex;
align-items: center;
gap: 0.8rem;
margin-top: 1.8rem;
}
.vol-icon { font-size: 0.75rem; color: rgba(245,240,232,0.4); }
.vol-slider {
flex: 1;
-webkit-appearance: none;
height: 2px;
background: rgba(245,240,232,0.15);
outline: none;
cursor: none;
}
.vol-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 10px; height: 10px;
background: var(–paper);
border-radius: 50%;
cursor: none;
}
/* ── RESPONSIVE ── */
@media (max-width: 900px) {
#hero { grid-template-columns: 1fr; }
.hero-right { display: none; }
.v-rule { display: none; }
#about { grid-template-columns: 1fr; gap: 3rem; }
.about-left { position: static; }
.members { grid-template-columns: 1fr; }
.portfolio-grid { grid-template-columns: 1fr; }
.portfolio-item:first-child { grid-column: span 1; }
#contact { grid-template-columns: 1fr; gap: 3rem; }
nav { padding: 1.2rem 1.5rem; }
#hero, #about, #portfolio, #listen, #contact { padding-left: 1.5rem; padding-right: 1.5rem; }
.player-wrap { grid-template-columns: 1fr; }
.player-card { position: static; }
footer { padding: 2rem 1.5rem; }
}
“`
Two voices. One sound.
Mursey
Guitar. Vocals. Percussion.
Acoustic duo forging something raw,
intimate, and entirely their own.
Scroll
The Band
Who
we are
Mursey is the creative partnership of Harry Hussey and Carolynn — two guitarists, two voices, one shared musical vision. Built on layered acoustics and close-knit harmonies, their sound lives somewhere between folk intimacy and something harder to name.
Together they write, arrange, and perform everything themselves — no backing tracks, no fuss. Just the instruments, the voices, and whatever a room can hold.
“`
Harry Hussey
Guitar & Vocals
Carolynn
Guitar, Vocals & Percussion
“`
Discography & Shows
The Work
“`
EP · 2024
Still Water
6 tracks — self-recorded, self-released
Live · 2024
The Barn Sessions
Acoustic live set · filmed
Single · 2023
Hollow Road
Debut release
Touring
On the Road
Available for bookings & shows
“`
Tracks
Listen
“`
Now Playing
Still Water
0:00
“`
Get in Touch
Book
or connect
For show bookings, collaborations, press inquiries, or just to say hello — send a message and we’ll be in touch.
“`
“`
// Custom cursor
const cursor = document.getElementById(‘cursor’);
const ring = document.getElementById(‘cursorRing’);
let mx = 0, my = 0, rx = 0, ry = 0;
document.addEventListener(‘mousemove’, e => {
mx = e.clientX; my = e.clientY;
cursor.style.left = mx + ‘px’;
cursor.style.top = my + ‘px’;
});
(function animRing() {
rx += (mx – rx) * 0.12;
ry += (my – ry) * 0.12;
ring.style.left = rx + ‘px’;
ring.style.top = ry + ‘px’;
requestAnimationFrame(animRing);
})();
document.querySelectorAll(‘a, button, input, textarea’).forEach(el => {
el.addEventListener(‘mouseenter’, () => {
cursor.style.width = ’20px’;
cursor.style.height = ’20px’;
ring.style.width = ’56px’;
ring.style.height = ’56px’;
ring.style.opacity = ‘0.8’;
});
el.addEventListener(‘mouseleave’, () => {
cursor.style.width = ’12px’;
cursor.style.height = ’12px’;
ring.style.width = ’36px’;
ring.style.height = ’36px’;
ring.style.opacity = ‘0.5’;
});
});
// Scroll reveal
const reveals = document.querySelectorAll(‘.reveal’);
const obs = new IntersectionObserver(entries => {
entries.forEach(e => {
if (e.isIntersecting) {
e.target.classList.add(‘visible’);
obs.unobserve(e.target);
}
});
}, { threshold: 0.15 });
reveals.forEach(r => obs.observe(r));
// ── MUSIC PLAYER ──
const tracks = [
{ title: ‘Still Water’, meta: ‘EP · 2024’, src: null, duration: ‘3:42’ },
{ title: ‘Hollow Road’, meta: ‘Single · 2023’, src: null, duration: ‘4:11’ },
{ title: ‘Low Light’, meta: ‘EP · 2024’, src: null, duration: ‘3:28’ },
{ title: ‘Riverbed’, meta: ‘EP · 2024’, src: null, duration: ‘5:03’ },
{ title: ‘Paper Walls’, meta: ‘EP · 2024’, src: null, duration: ‘3:55’ },
{ title: ‘The Long Way’, meta: ‘EP · 2024’, src: null, duration: ‘4:30’ },
];
let currentIdx = 0;
let isPlaying = false;
const audio = new Audio();
audio.volume = 0.8;
// Build tracklist
const tl = document.getElementById(‘tracklist’);
tracks.forEach((t, i) => {
const row = document.createElement(‘div’);
row.className = ‘track-row’ + (i === 0 ? ‘ active’ : ”);
row.dataset.idx = i;
row.innerHTML = `
${String(i+1).padStart(2,’0′)}
${t.duration}
`;
row.addEventListener(‘click’, () => selectTrack(i));
tl.appendChild(row);
});
function selectTrack(idx) {
currentIdx = idx;
const t = tracks[idx];
document.getElementById(‘playerTitle’).textContent = t.title;
document.getElementById(‘playerMeta’).textContent = t.meta;
document.getElementById(‘timeTotal’).textContent = t.duration;
document.getElementById(‘progressFill’).style.width = ‘0%’;
document.getElementById(‘progressHandle’).style.left = ‘0%’;
document.getElementById(‘timeCurrent’).textContent = ‘0:00’;
// Update active row
document.querySelectorAll(‘.track-row’).forEach((r,i) => {
r.classList.toggle(‘active’, i === idx);
r.classList.remove(‘playing’);
});
if (t.src) {
audio.src = t.src;
if (isPlaying) audio.play();
} else {
// Demo mode: simulate playback with fake progress
audio.src = ”;
if (isPlaying) startFakePlay();
}
drawWave(idx);
}
// Play / pause
const btnPlay = document.getElementById(‘btnPlay’);
btnPlay.addEventListener(‘click’, togglePlay);
function togglePlay() {
isPlaying = !isPlaying;
btnPlay.innerHTML = isPlaying ? ‘▮▮’ : ‘▶’;
const activeRow = document.querySelector(‘.track-row.active’);
if (isPlaying) {
activeRow && activeRow.classList.add(‘playing’);
if (tracks[currentIdx].src) { audio.play(); }
else { startFakePlay(); }
} else {
activeRow && activeRow.classList.remove(‘playing’);
audio.pause();
clearInterval(fakeTimer);
}
}
document.getElementById(‘btnPrev’).addEventListener(‘click’, () => {
selectTrack((currentIdx – 1 + tracks.length) % tracks.length);
});
document.getElementById(‘btnNext’).addEventListener(‘click’, () => {
selectTrack((currentIdx + 1) % tracks.length);
});
// Volume
document.getElementById(‘volSlider’).addEventListener(‘input’, e => {
audio.volume = parseFloat(e.target.value);
});
// Real audio progress
audio.addEventListener(‘timeupdate’, () => {
if (!audio.duration) return;
const pct = (audio.currentTime / audio.duration) * 100;
document.getElementById(‘progressFill’).style.width = pct + ‘%’;
document.getElementById(‘progressHandle’).style.left = pct + ‘%’;
document.getElementById(‘timeCurrent’).textContent = fmtTime(audio.currentTime);
});
audio.addEventListener(‘ended’, () => {
selectTrack((currentIdx + 1) % tracks.length);
});
// Seek on progress bar click
document.getElementById(‘progressWrap’).addEventListener(‘click’, e => {
const rect = e.currentTarget.getBoundingClientRect();
const pct = (e.clientX – rect.left) / rect.width;
if (audio.duration) {
audio.currentTime = pct * audio.duration;
} else {
fakeProgress = pct * fakeDuration;
}
});
// Fake playback for demo (no real audio file)
let fakeTimer, fakeProgress = 0, fakeDuration = 0;
function startFakePlay() {
clearInterval(fakeTimer);
// Parse duration string to seconds
const parts = tracks[currentIdx].duration.split(‘:’);
fakeDuration = parseInt(parts[0]) * 60 + parseInt(parts[1]);
document.getElementById(‘timeTotal’).textContent = tracks[currentIdx].duration;
fakeTimer = setInterval(() => {
if (!isPlaying) { clearInterval(fakeTimer); return; }
fakeProgress += 0.5;
if (fakeProgress >= fakeDuration) {
fakeProgress = 0;
clearInterval(fakeTimer);
isPlaying = false;
btnPlay.innerHTML = ‘▶’;
document.querySelector(‘.track-row.active’)?.classList.remove(‘playing’);
return;
}
const pct = (fakeProgress / fakeDuration) * 100;
document.getElementById(‘progressFill’).style.width = pct + ‘%’;
document.getElementById(‘progressHandle’).style.left = pct + ‘%’;
document.getElementById(‘timeCurrent’).textContent = fmtTime(fakeProgress);
}, 500);
}
function fmtTime(s) {
const m = Math.floor(s / 60);
const sec = Math.floor(s % 60);
return m + ‘:’ + String(sec).padStart(2,’0′);
}
// Waveform canvas
const canvas = document.getElementById(‘waveCanvas’);
const ctx2d = canvas.getContext(‘2d’);
function drawWave(seed) {
canvas.width = canvas.offsetWidth * window.devicePixelRatio;
canvas.height = canvas.offsetHeight * window.devicePixelRatio;
ctx2d.clearRect(0, 0, canvas.width, canvas.height);
const bars = 60;
const barW = canvas.width / bars;
const h = canvas.height;
// Seeded pseudo-random heights
for (let i = 0; i < bars; i++) {
const rng = Math.abs(Math.sin((seed * 7 + i) * 9.3)) * 0.7 + 0.1;
const barH = rng * h * 0.85;
const x = i * barW + barW * 0.15;
const y = (h – barH) / 2;
ctx2d.fillStyle = i drawWave(currentIdx));
function handleSubmit() {
const btn = document.querySelector(‘.form-submit’);
btn.textContent = ‘Sent ✓’;
btn.style.background = ‘#4a7c59’;
btn.style.pointerEvents = ‘none’;
}