✦WHAT HAS HAPPENED!
RECENT VENUE
GPOSES
WELCOME TO THE COAST
The Sunset Coast is a community built around late nights, loud music and lively people- A home for DJs, creators, performers, photographers, hosts and anyone looking for somewhere to belongYou don't need to know anyoneYou don't need to be someoneyou don't need to know what you are looking forJust come to the coastWe'll take it from there
✦WHAT'S
HAPPENING!
RECENT POLAROIDS
OUR STORY
The Sunset Coast was not built overnight. Like all things worth building, it took dedicated work and hard choices.These hardships became new opportunities. These days we build, we experiment, we get things wrong- but we don't stop working towards one goal.To bring something special to Materia and bridge the DCs together.Sometimes we all just need a safe place we can belong. I know we did.
so we made one
THE CURRENT
THE NINE HELLS
REPENT
Oct 30th 8:00pm AEST
Tonight, the Coast abandons inhibitions.
The pleasures of the damned are now ours to indulge in.For when the Eclipse comes, we revel with the devils.
Confess your sins
Courtesans
NSFW+
Live DJs
Giveaways and prizes to be won!
SUNSET TV
Youtube coming soon
Our events are about creating memories
Take your time
Viewers can watch either viaDiscord or Youtube
Located at: Materia Sophia Mist W18 P60
Shoreline is where the Coast comes alive. A void build- Warship that has crashed into a reef built for late nights, loud music and whatever chaos washes ashore. From themed nights and competitions, to DJs, games and the next innovations the owners can think of, no two nights at Shoreline are ever quite the same as the venues design shifts with the theme!
WARD 18
(Southwest Subdivision)
SUNSET GPOSE STUDIO
SUNSET LEISURE HOUSE
THE YUKIKAZE
THE SUNSET PARABLE
ZEN HOUSE
PICNIC CAFE
THE RESTRICTED SECTION
THE BLUEQO'TE BARHOP
MIDNIGHT ZONE
SEA AND STARS
Located at: Materia Sophia Mist W18 P59
Slices of life and nature, as well as all your green screening needs in one location. Explore a range of immersive backdrops, find the perfect setting or request one and we can see what we can do~Currently no book required, go perfect your pose in the perfect setting
Located at: Materia Sophia Mist W18 P58
A state of the art movie theatre featuring the newest visual magic FFXIV has to offer. But turn left at the candy bar and you will find a real treat. A massage parlor located in the void. A full day of fun and relaxation await!
Located at: Materia Sophia Mist W18 P57
Come take a cruise on the warship of Shoreline before it's fateful crash. The Yukikaze reimagined, and restored to its former glory!
Located at: Materia Sophia Mist W18 P56
A remarkably simple premise really:
Enter a room. Leave the room.
Enter another room- which is regrettably, the same room.
Now you might THINK there is a subtle difference.
There isn't.
Choose another door- the room again.
Eventually, through perserverance, intiution, blind luck, or the increasingly desperate act of choosing doors. You might find your way out.
Surely.
It's a maze.
We never said it was a fair one.
Located at: Materia Sophia Mist W18 P55
Designed to be a safe zone during a festival, step through the lush nature corridor, and come find a place built to catch your breath. Take your time, it's all going to be ok♥
We will ensure it.
Located at: Materia Sophia Mist W18 P54
The second festival safe space, a cafe front, with saloon doors that take you to the lower area- a perfect hangout spot to lay out a picnic blanket and let your troubles fade.
Seek out staff if you need a sandwhich and head pats.
Located at: Materia Sophia Mist W18 P53
Devised by one of our particularly cheeky twins, this jump puzzle features a journey through the written language so dastardly you might just turn to some tech for assistance- not because you need it or anything~
simply out of spite.
Located at: Materia Sophia Mist W18 P52
Jump puzzles got you down?
We get it. The other twin will sort you out!
Just head to the club in the sky.....
by way of......
an even harder jump puzzle.
Drink responsibly!
Located at: Materia Sophia Mist W18 P47
A stunning exhibition home that captures the serenity of being in the ocean after dark, follow the glow through the black waters and lose yourself to our midnight invitation.
leave the shore.
Come skinny dip in the void~
Located at: Materia Sophia Mist W18 P46
A replica of Mizu's home on Sephirot. A void build with a grand pool, luxurious bed, observation deck and ...dance..area.. nod nod
A wholesome place to hold a hand or two.
EXPLORE
MATERIA SOPHIA MIST WARD 18 SW SUBDIVISION!
WIFI INFORMATION
PSYNC WIFIUsername: SunsetCoast
Password: no password
LIGHTLESS SYNCUsername: SunsetCoast
Password: SunsetCoast
(function () { 'use strict'; /* ===================================================== RETURN ICON ===================================================== */ const iconImage = 'https://files.catbox.moe/5km2dn.png'; /* ===================================================== INITIALISE ===================================================== */ function initialiseEclipsePortal() { let portal = document.getElementById('sunsetEclipsePortal'); if (!portal) { portal = document.createElement('a'); portal.id = 'sunsetEclipsePortal'; const image = document.createElement('img'); image.alt = 'Return to Eclipse homepage'; portal.appendChild(image); document.body.appendChild(portal); } /* ===================================================== CONFIGURE PORTAL ===================================================== */ const image = portal.querySelector('img'); image.src = iconImage; portal.href = '#eclipse'; portal.setAttribute( 'aria-label', 'Return to Eclipse homepage' ); /* ===================================================== UPDATE VISIBILITY ===================================================== */ function updatePortal() { const section = window.location.hash .replace('#', '') .toLowerCase(); if (section === 'court') { portal.classList.add('iconOnly'); } else { portal.classList.remove('iconOnly'); } } /* ===================================================== EVENT LISTENERS ===================================================== */ updatePortal(); window.addEventListener( 'hashchange', updatePortal ); window.addEventListener( 'pageshow', updatePortal ); } /* ===================================================== START ===================================================== */ if (document.readyState === 'loading') { document.addEventListener( 'DOMContentLoaded', initialiseEclipsePortal ); } else { initialiseEclipsePortal(); } })();
(function () { "use strict"; /* ===================================================== CONFIGURATION ===================================================== */ const JADE_HOVER_IMAGE = 'https://files.catbox.moe/js01jo.png'; const HOVER_SCALE = 1; const HOVER_OFFSET_X = 0; const HOVER_OFFSET_Y = 0; /* ===================================================== INITIALISE ===================================================== */ function setupJadeTransformation() { const jade = document.getElementById('jadeSFW'); if (!jade) { setTimeout(setupJadeTransformation, 150); return; } const originalImage = jade.matches('img') ? jade : jade.querySelector('img'); if (!originalImage) { setTimeout(setupJadeTransformation, 150); return; } /* Prevent duplicate setup */ if (jade.dataset.jadeHoverV4Ready === '1') { return; } jade.dataset.jadeHoverV4Ready = '1'; /* ===================================================== REMOVE PREVIOUS OVERLAY ===================================================== */ const oldOverlay = document.getElementById('jadeHoverOverlay'); if (oldOverlay) { oldOverlay.remove(); } /* Reset original image */ originalImage.style.opacity = '1'; /* ===================================================== CREATE ALTERNATE IMAGE ===================================================== */ const overlay = document.createElement('img'); overlay.id = 'jadeHoverOverlay'; overlay.src = JADE_HOVER_IMAGE; overlay.alt = ''; overlay.draggable = false; overlay.setAttribute( 'aria-hidden', 'true' ); document.body.appendChild(overlay); /* ===================================================== POSITION ALTERNATE IMAGE ===================================================== */ function positionJadeHover() { const rect = originalImage.getBoundingClientRect(); if (!rect.width || !rect.height) { return; } const width = rect.width * HOVER_SCALE; const height = rect.height * HOVER_SCALE; overlay.style.width = width + 'px'; overlay.style.height = height + 'px'; overlay.style.left = ( rect.left + (rect.width - width) / 2 + HOVER_OFFSET_X ) + 'px'; overlay.style.top = ( rect.top + (rect.height - height) / 2 + HOVER_OFFSET_Y ) + 'px'; } /* ===================================================== INSTANT TRANSFORMATION ===================================================== */ let transformed = false; function showJade() { positionJadeHover(); overlay.classList.add( 'jade-hover-active' ); /* Keep the original image underneath. No crossfade. */ transformed = true; } function hideJade() { overlay.classList.remove( 'jade-hover-active' ); transformed = false; } /* ===================================================== DESKTOP HOVER ===================================================== */ jade.addEventListener( 'mouseenter', function () { if (window.matchMedia('(hover: hover)').matches) { showJade(); } } ); jade.addEventListener( 'mouseleave', function () { if (window.matchMedia('(hover: hover)').matches) { hideJade(); } } ); /* ===================================================== MOBILE TAP ===================================================== */ jade.addEventListener( 'click', function (event) { if (window.matchMedia('(hover: hover)').matches) { return; } event.preventDefault(); if (transformed) { hideJade(); } else { showJade(); } } ); /* ===================================================== POSITION UPDATES ===================================================== */ window.addEventListener( 'resize', positionJadeHover ); window.addEventListener( 'scroll', positionJadeHover, { passive: true } ); /* ===================================================== SECTION CHANGES ===================================================== */ window.addEventListener( 'hashchange', function () { hideJade(); requestAnimationFrame(positionJadeHover); setTimeout(positionJadeHover, 150); setTimeout(positionJadeHover, 500); } ); /* ===================================================== INITIAL POSITION ===================================================== */ overlay.addEventListener( 'load', positionJadeHover ); positionJadeHover(); setTimeout(positionJadeHover, 250); setTimeout(positionJadeHover, 750); setTimeout(positionJadeHover, 1500); console.log( 'Sunset Coast: Jade Transformation V4 ready.' ); } /* ===================================================== START ===================================================== */ setupJadeTransformation(); document.addEventListener( 'DOMContentLoaded', setupJadeTransformation ); window.addEventListener( 'load', setupJadeTransformation ); })();
(function () { const MENU_IMAGE_URL = 'https://files.catbox.moe/3b3cvp.png'; function setupPleasureMenu() { const trigger = document.getElementById('pleasureMenuButton'); if (!trigger) { setTimeout(setupPleasureMenu, 100); return; } if (trigger.dataset.pleasureMenuReady === '1') { return; } trigger.dataset.pleasureMenuReady = '1'; /* CREATE BACKDROP */ const backdrop = document.createElement('div'); backdrop.id = 'pleasureMenuBackdrop'; document.body.appendChild(backdrop); /* CREATE MODAL */ const modal = document.createElement('div'); modal.id = 'pleasureMenuModal'; modal.setAttribute('role', 'dialog'); modal.setAttribute('aria-modal', 'true'); modal.setAttribute('aria-label', 'The Pleasure Menu'); modal.setAttribute('aria-hidden', 'true'); /* MENU IMAGE */ const menuImage = document.createElement('img'); menuImage.id = 'pleasureMenuImage'; menuImage.src = MENU_IMAGE_URL; menuImage.alt = 'Sunset Coast Eclipse Pleasure Menu'; modal.appendChild(menuImage); /* CLOSE BUTTON */ const closeButton = document.createElement('button'); closeButton.id = 'pleasureMenuClose'; closeButton.type = 'button'; closeButton.innerHTML = '×'; closeButton.setAttribute( 'aria-label', 'Close Pleasure Menu' ); modal.appendChild(closeButton); document.body.appendChild(modal); /* OPEN */ function openMenu() { backdrop.classList.add('menu-open'); modal.classList.add('menu-open'); modal.setAttribute('aria-hidden', 'false'); document.body.style.overflow = 'hidden'; closeButton.focus(); } /* CLOSE */ function closeMenu() { backdrop.classList.remove('menu-open'); modal.classList.remove('menu-open'); modal.setAttribute('aria-hidden', 'true'); document.body.style.overflow = ''; } /* TRIGGER */ trigger.addEventListener('click', function (event) { event.preventDefault(); openMenu(); }); /* CLOSE EVENTS */ closeButton.addEventListener('click', closeMenu); backdrop.addEventListener('click', closeMenu); document.addEventListener('keydown', function (event) { if ( event.key === 'Escape' && modal.classList.contains('menu-open') ) { closeMenu(); } }); console.log('Sunset Coast: Pleasure Menu ready.'); } setupPleasureMenu(); document.addEventListener('DOMContentLoaded', setupPleasureMenu); window.addEventListener('load', setupPleasureMenu); })();
/* ========================================================= SUNSET COAST — NAVIGATION V4 Main Navigation + Feature Poster + Interactive Map ========================================================= */ (function () { 'use strict'; /* ========================================================= PAGE DEFINITIONS ========================================================= */ const pages = { coast: '#thecoastPanel, .coastPage', staff: '.staffPage', venues: '.venuePage', map: '.mapPage', wifi: '.wifiPage', home: '.landingScreen, #landingScreen' }; const allSections = Object.values(pages).join(', ') + ', .menuPage'; /* ========================================================= VISIBILITY ========================================================= */ function setVisible(element, visible) { element.classList.remove( 'channel-in', 'channel-out' ); if (visible) { element.style.removeProperty('display'); } else { element.style.display = 'none'; } } /* ========================================================= NAVIGATE ========================================================= */ function navigate(page) { if (!pages[page]) return; document.querySelectorAll(allSections) .forEach(function (element) { setVisible(element, false); }); document.querySelectorAll(pages[page]) .forEach(function (element) { setVisible(element, true); }); if (page === 'home') { if (typeof hideSunsetHome === 'function') { hideSunsetHome(); } } else { if (typeof showSunsetHome === 'function') { showSunsetHome(); } } window.scrollTo({ top: 0, behavior: 'instant' }); console.log('[SUNSET] Opened:', page); } /* ========================================================= MAIN NAVIGATION BUTTONS ONLY ========================================================= */ document.addEventListener('click', function (event) { const button = event.target.closest( '#buttons01 a, #buttons03 a' ); if (!button) return; const label = button.textContent.trim().toUpperCase(); let page = null; if (label.includes('COAST')) { page = 'coast'; } else if (label.includes('STAFF')) { page = 'staff'; } else if (label.includes('VENUES')) { page = 'venues'; } else if (label.includes('MAP')) { page = 'map'; } else if (label.includes('WIFI')) { page = 'wifi'; } if (!page) return; event.preventDefault(); event.stopImmediatePropagation(); navigate(page); }, true); /* ========================================================= HOME BUTTON ========================================================= */ document.addEventListener('click', function (event) { const button = event.target.closest('#sunsetHomeButton'); if (!button) return; event.preventDefault(); event.stopImmediatePropagation(); navigate('home'); }, true); /* ========================================================= FEATURE POSTER Leave the existing poster click handler alone. It manages its own section transition and scroll. ========================================================= */ /* ========================================================= MAP HITBOXES Leave the existing map click handlers alone. They manage venue transitions and scrolling. ========================================================= */ /* ========================================================= GLOBAL NAVIGATION FUNCTION ========================================================= */ window.sunsetNavigate = navigate; /* ========================================================= INITIALISE ========================================================= */ function initialise() { const hash = window.location.hash.toLowerCase(); /* Preserve Eclipse and Courtesans sections */ if ( hash === '#eclipse' || hash === '#court' ) { return; } navigate('home'); } if (document.readyState === 'loading') { document.addEventListener( 'DOMContentLoaded', initialise ); } else { initialise(); } console.log( '[SUNSET] Navigation V4 ready.' ); })();
(function () { /* ================================================= CONFIGURATION ================================================= */ const JADE_SPECIALS_IMAGE = 'https://files.catbox.moe/cribx0.png'; /* ================================================= INITIALISE ================================================= */ function setupJadeHeart() { const panel = document.getElementById('jadeProfilePanel'); if (!panel) { setTimeout(setupJadeHeart, 150); return; } if (document.getElementById('jadeHeartButton')) { return; } /* ================================================= CREATE ANCHOR — SAME AS WORKING V5 ================================================= */ const anchor = document.createElement('div'); anchor.id = 'jadeHeartAnchor'; panel.parentNode.insertBefore(anchor, panel); anchor.appendChild(panel); /* ================================================= CREATE HEART ================================================= */ const heart = document.createElement('button'); heart.id = 'jadeHeartButton'; heart.type = 'button'; heart.textContent = '♥'; heart.setAttribute( 'aria-label', "View Jade's Specials" ); anchor.appendChild(heart); /* ================================================= CREATE POPUP BACKDROP ================================================= */ const backdrop = document.createElement('div'); backdrop.id = 'jadeSpecialsBackdrop'; document.body.appendChild(backdrop); /* ================================================= CREATE POPUP WINDOW ================================================= */ const modal = document.createElement('div'); modal.id = 'jadeSpecialsModal'; modal.setAttribute('role', 'dialog'); modal.setAttribute('aria-modal', 'true'); modal.setAttribute('aria-label', "Jade's Specials"); modal.setAttribute('aria-hidden', 'true'); /* MENU IMAGE */ const menuImage = document.createElement('img'); menuImage.id = 'jadeSpecialsImage'; menuImage.src = JADE_SPECIALS_IMAGE; menuImage.alt = "Jade Redbee's Specials Menu"; modal.appendChild(menuImage); /* CLOSE BUTTON */ const closeButton = document.createElement('button'); closeButton.id = 'jadeSpecialsClose'; closeButton.type = 'button'; closeButton.innerHTML = '×'; closeButton.setAttribute( 'aria-label', "Close Jade's Specials" ); modal.appendChild(closeButton); document.body.appendChild(modal); /* ================================================= OPEN POPUP ================================================= */ let previousFocus = null; let previousOverflow = ''; function openSpecials() { previousFocus = document.activeElement; previousOverflow = document.body.style.overflow; backdrop.classList.add('jade-open'); modal.classList.add('jade-open'); modal.setAttribute('aria-hidden', 'false'); document.body.style.overflow = 'hidden'; closeButton.focus(); } /* ================================================= CLOSE POPUP ================================================= */ function closeSpecials() { backdrop.classList.remove('jade-open'); modal.classList.remove('jade-open'); modal.setAttribute('aria-hidden', 'true'); document.body.style.overflow = previousOverflow; if (previousFocus && previousFocus.focus) { previousFocus.focus(); } } /* ================================================= HEART CLICK ================================================= */ heart.addEventListener('click', function (event) { event.preventDefault(); openSpecials(); }); /* ================================================= CLOSE EVENTS ================================================= */ closeButton.addEventListener( 'click', closeSpecials ); backdrop.addEventListener( 'click', closeSpecials ); document.addEventListener( 'keydown', function (event) { if ( event.key === 'Escape' && modal.classList.contains('jade-open') ) { closeSpecials(); } } ); console.log( 'Sunset Coast: Jade Specials V6 ready.' ); } /* ================================================= START ================================================= */ setupJadeHeart(); document.addEventListener( 'DOMContentLoaded', setupJadeHeart ); window.addEventListener( 'load', setupJadeHeart ); })();
(function () { "use strict"; const HOVER_IMAGE = 'https://files.catbox.moe/473ztj.png'; const HOVER_SCALE = 1.08; const HOVER_OFFSET_X = 0; const HOVER_OFFSET_Y = 15; function setupSiphoV12() { const sfw = document.getElementById('siphoSFW'); const panel = document.getElementById('siphoProfilePanel'); if (!sfw || !panel) { setTimeout(setupSiphoV12, 150); return; } const originalImage = sfw.matches('img') ? sfw : sfw.querySelector('img'); if (!originalImage) { setTimeout(setupSiphoV12, 150); return; } if (sfw.dataset.siphoV12Ready === '1') { return; } sfw.dataset.siphoV12Ready = '1'; /* ========================================= REMOVE OLD WRAPPER IF PRESENT ========================================= */ const oldAnchor = document.getElementById('siphoHeartAnchor'); if (oldAnchor && oldAnchor.contains(panel)) { oldAnchor.parentNode.insertBefore( panel, oldAnchor ); oldAnchor.remove(); } /* ========================================= HOVER IMAGE ========================================= */ const oldOverlay = document.getElementById('siphoHoverOverlay'); if (oldOverlay) { oldOverlay.remove(); } const overlay = document.createElement('img'); overlay.id = 'siphoHoverOverlay'; overlay.src = HOVER_IMAGE; overlay.alt = ''; overlay.setAttribute( 'aria-hidden', 'true' ); document.body.appendChild(overlay); /* ========================================= HEART BUTTON ========================================= */ let heart = document.getElementById('siphoHeartButton'); if (!heart) { heart = document.createElement('button'); heart.id = 'siphoHeartButton'; heart.type = 'button'; heart.textContent = '♥'; heart.setAttribute( 'aria-label', "View Sipho's Specials" ); } document.body.appendChild(heart); /* ========================================= POSITIONING ========================================= */ function positionElements() { const characterRect = originalImage.getBoundingClientRect(); if (characterRect.width && characterRect.height) { const width = characterRect.width * HOVER_SCALE; const height = characterRect.height * HOVER_SCALE; overlay.style.width = width + 'px'; overlay.style.height = height + 'px'; overlay.style.left = ( characterRect.left + (characterRect.width - width) / 2 + HOVER_OFFSET_X ) + 'px'; overlay.style.top = ( characterRect.top + (characterRect.height - height) / 2 + HOVER_OFFSET_Y ) + 'px'; } const panelRect = panel.getBoundingClientRect(); if (!panelRect.width || !panelRect.height) { heart.style.visibility = 'hidden'; return; } const isVisible = window.getComputedStyle(panel).display !== 'none' && panelRect.bottom > 0 && panelRect.top < window.innerHeight; heart.style.visibility = isVisible ? 'visible' : 'hidden'; heart.style.left = ( panelRect.left + panelRect.width * 0.79 ) + 'px'; heart.style.top = ( panelRect.top + panelRect.height * 0.65 ) + 'px'; } /* ========================================= CHARACTER TRANSFORMATION ========================================= */ let transformed = false; function showTransformation() { positionElements(); overlay.classList.add( 'sipho-hover-active' ); originalImage.style.opacity = '0'; transformed = true; } function hideTransformation() { overlay.classList.remove( 'sipho-hover-active' ); originalImage.style.opacity = '1'; transformed = false; } sfw.addEventListener('mouseenter', function () { if (window.matchMedia('(hover: hover)').matches) { showTransformation(); } }); sfw.addEventListener('mouseleave', function () { if (window.matchMedia('(hover: hover)').matches) { hideTransformation(); } }); sfw.addEventListener('click', function (event) { if (window.matchMedia('(hover: hover)').matches) { return; } event.preventDefault(); if (transformed) { hideTransformation(); } else { showTransformation(); } }); /* ========================================= RESIZE / SCROLL / SECTION CHANGES ========================================= */ window.addEventListener( 'resize', positionElements ); window.addEventListener( 'scroll', positionElements, { passive: true } ); window.addEventListener('hashchange', function () { hideTransformation(); requestAnimationFrame(positionElements); setTimeout(positionElements, 150); }); overlay.addEventListener( 'load', positionElements ); positionElements(); setTimeout(positionElements, 250); setTimeout(positionElements, 750); console.log( 'Sunset Coast: Sipho V12 ready.' ); } setupSiphoV12(); document.addEventListener( 'DOMContentLoaded', setupSiphoV12 ); window.addEventListener( 'load', setupSiphoV12 ); })();
(function () { "use strict"; /* ===================================================== CONFIGURATION ===================================================== */ const SIPHO_MENU_IMAGE = 'https://files.catbox.moe/4nuhdj.png'; /* ===================================================== INITIALISE ===================================================== */ function setupSiphoV7() { const heart = document.getElementById('siphoHeartButton'); if (!heart) { setTimeout(setupSiphoV7, 150); return; } /* Prevent duplicate initialisation */ if (heart.dataset.siphoV7Ready === '1') { return; } heart.dataset.siphoV7Ready = '1'; /* ===================================================== CREATE ISOLATED BACKDROP ===================================================== */ let backdrop = document.getElementById('siphoV7Backdrop'); if (!backdrop) { backdrop = document.createElement('div'); backdrop.id = 'siphoV7Backdrop'; document.body.appendChild(backdrop); } /* ===================================================== CREATE ISOLATED MODAL ===================================================== */ let modal = document.getElementById('siphoV7Modal'); if (!modal) { modal = document.createElement('div'); modal.id = 'siphoV7Modal'; document.body.appendChild(modal); } modal.setAttribute('role', 'dialog'); modal.setAttribute('aria-modal', 'true'); modal.setAttribute( 'aria-label', "Sipho's Specials" ); modal.setAttribute('aria-hidden', 'true'); /* ===================================================== CREATE MENU IMAGE ===================================================== */ modal.innerHTML = ''; const menuImage = document.createElement('img'); menuImage.id = 'siphoV7Image'; menuImage.src = SIPHO_MENU_IMAGE; menuImage.alt = "Sipho's Specials Menu"; menuImage.loading = 'eager'; modal.appendChild(menuImage); /* ===================================================== CREATE CLOSE BUTTON ===================================================== */ const closeButton = document.createElement('button'); closeButton.id = 'siphoV7Close'; closeButton.type = 'button'; closeButton.innerHTML = '×'; closeButton.setAttribute( 'aria-label', "Close Sipho's Specials" ); modal.appendChild(closeButton); /* ===================================================== OPEN / CLOSE ===================================================== */ let previousOverflow = ''; let previousFocus = null; function openSpecials() { previousOverflow = document.body.style.overflow; previousFocus = document.activeElement; backdrop.classList.add('sipho-v7-open'); modal.classList.add('sipho-v7-open'); modal.setAttribute('aria-hidden', 'false'); document.body.style.overflow = 'hidden'; closeButton.focus(); } function closeSpecials() { if (!modal.classList.contains('sipho-v7-open')) { return; } backdrop.classList.remove('sipho-v7-open'); modal.classList.remove('sipho-v7-open'); modal.setAttribute('aria-hidden', 'true'); document.body.style.overflow = previousOverflow; if ( previousFocus && typeof previousFocus.focus === 'function' ) { previousFocus.focus(); } } /* ===================================================== INTERCEPT HEART CLICK Capture phase runs before ordinary click handlers. stopImmediatePropagation prevents older bubble-phase Sipho handlers from opening their placeholder popup. The heart itself is NOT replaced or repositioned. ===================================================== */ heart.addEventListener( 'click', function (event) { event.preventDefault(); event.stopImmediatePropagation(); openSpecials(); }, true ); /* ===================================================== CLOSE EVENTS ===================================================== */ closeButton.addEventListener( 'click', function (event) { event.preventDefault(); closeSpecials(); } ); backdrop.addEventListener( 'click', closeSpecials ); document.addEventListener( 'keydown', function (event) { if ( event.key === 'Escape' && modal.classList.contains('sipho-v7-open') ) { closeSpecials(); } } ); /* ===================================================== CLOSE ON SECTION CHANGE ===================================================== */ window.addEventListener( 'hashchange', closeSpecials ); /* ===================================================== READY ===================================================== */ console.log( 'Sunset Coast: Sipho Specials Popup V7 ready.' ); } /* ===================================================== START ===================================================== */ setupSiphoV7(); document.addEventListener( 'DOMContentLoaded', setupSiphoV7 ); window.addEventListener( 'load', setupSiphoV7 ); })();
(function () { "use strict"; /* ===================================================== CONFIGURATION ===================================================== */ const RUBY_HOVER_IMAGE = 'https://files.catbox.moe/e9thcs.png'; /* Adjust these only if the alternate image needs positioning corrections. */ const HOVER_SCALE = 1; const HOVER_OFFSET_X = 0; const HOVER_OFFSET_Y = 0; /* ===================================================== INITIALISE ===================================================== */ function setupRubyTransformation() { const ruby = document.getElementById('rubySFW'); if (!ruby) { setTimeout(setupRubyTransformation, 150); return; } const originalImage = ruby.matches('img') ? ruby : ruby.querySelector('img'); if (!originalImage) { setTimeout(setupRubyTransformation, 150); return; } /* Prevent duplicate setup */ if (ruby.dataset.rubyHoverV1Ready === '1') { return; } ruby.dataset.rubyHoverV1Ready = '1'; /* ===================================================== REMOVE PREVIOUS OVERLAY ===================================================== */ const oldOverlay = document.getElementById('rubyHoverOverlay'); if (oldOverlay) { oldOverlay.remove(); } /* ===================================================== CREATE ALTERNATE IMAGE ===================================================== */ const overlay = document.createElement('img'); overlay.id = 'rubyHoverOverlay'; overlay.src = RUBY_HOVER_IMAGE; overlay.alt = ''; overlay.draggable = false; overlay.setAttribute( 'aria-hidden', 'true' ); document.body.appendChild(overlay); /* ===================================================== POSITION ALTERNATE IMAGE ===================================================== */ function positionRubyHover() { const rect = originalImage.getBoundingClientRect(); if (!rect.width || !rect.height) { return; } const width = rect.width * HOVER_SCALE; const height = rect.height * HOVER_SCALE; overlay.style.width = width + 'px'; overlay.style.height = height + 'px'; overlay.style.left = ( rect.left + (rect.width - width) / 2 + HOVER_OFFSET_X ) + 'px'; overlay.style.top = ( rect.top + (rect.height - height) / 2 + HOVER_OFFSET_Y ) + 'px'; } /* ===================================================== INSTANT TRANSFORMATION ===================================================== */ let transformed = false; function showRuby() { positionRubyHover(); overlay.classList.add( 'ruby-hover-active' ); transformed = true; } function hideRuby() { overlay.classList.remove( 'ruby-hover-active' ); transformed = false; } /* ===================================================== DESKTOP HOVER ===================================================== */ ruby.addEventListener( 'mouseenter', function () { if (window.matchMedia('(hover: hover)').matches) { showRuby(); } } ); ruby.addEventListener( 'mouseleave', function () { if (window.matchMedia('(hover: hover)').matches) { hideRuby(); } } ); /* ===================================================== MOBILE TAP ===================================================== */ ruby.addEventListener( 'click', function (event) { if (window.matchMedia('(hover: hover)').matches) { return; } event.preventDefault(); if (transformed) { hideRuby(); } else { showRuby(); } } ); /* ===================================================== POSITION UPDATES ===================================================== */ window.addEventListener( 'resize', positionRubyHover ); window.addEventListener( 'scroll', positionRubyHover, { passive: true } ); /* ===================================================== SECTION CHANGES ===================================================== */ window.addEventListener( 'hashchange', function () { hideRuby(); requestAnimationFrame(positionRubyHover); setTimeout(positionRubyHover, 150); setTimeout(positionRubyHover, 500); } ); /* ===================================================== INITIAL POSITION ===================================================== */ overlay.addEventListener( 'load', positionRubyHover ); positionRubyHover(); setTimeout(positionRubyHover, 250); setTimeout(positionRubyHover, 750); setTimeout(positionRubyHover, 1500); console.log( 'Sunset Coast: Ruby Transformation V1 ready.' ); } /* ===================================================== START ===================================================== */ setupRubyTransformation(); document.addEventListener( 'DOMContentLoaded', setupRubyTransformation ); window.addEventListener( 'load', setupRubyTransformation ); })();
(function () { "use strict"; /* ===================================================== INITIALISE RUBY HEART ===================================================== */ function setupRubyHeart() { const panel = document.getElementById('rubyProfilePanel'); if (!panel) { setTimeout(setupRubyHeart, 150); return; } /* Prevent duplicate heart creation */ if (document.getElementById('rubyHeartButton')) { return; } /* ===================================================== CREATE PROFILE ANCHOR ===================================================== */ const anchor = document.createElement('div'); anchor.id = 'rubyHeartAnchor'; /* Wrap Ruby's profile in a positioned container. This means the heart scrolls naturally with the profile instead of being repositioned by JavaScript. */ panel.parentNode.insertBefore(anchor, panel); anchor.appendChild(panel); /* ===================================================== CREATE HEART BUTTON ===================================================== */ const heart = document.createElement('button'); heart.id = 'rubyHeartButton'; heart.type = 'button'; heart.textContent = '♥'; heart.setAttribute( 'aria-label', "View Ruby's Specials" ); heart.setAttribute( 'title', "Ruby's Specials" ); anchor.appendChild(heart); /* ===================================================== HEART CLICK ===================================================== */ /* The heart is ready for Ruby's Specials menu. Once Ruby's menu PNG is available, we'll connect the popup here. For now, clicking the heart won't open a placeholder or interfere with the other courtesans. */ /* ===================================================== READY ===================================================== */ console.log( 'Sunset Coast: Ruby Specials Heart V1 ready.' ); } /* ===================================================== START ===================================================== */ setupRubyHeart(); document.addEventListener( 'DOMContentLoaded', setupRubyHeart ); window.addEventListener( 'load', setupRubyHeart ); })();
(function () { "use strict"; /* ===================================================== CONFIGURATION ===================================================== */ const RUBY_SPECIALS_IMAGE = 'https://files.catbox.moe/cvybux.png'; /* ===================================================== INITIALISE ===================================================== */ function setupRubySpecials() { /* Find heart created by Ruby Heart V1 */ const heart = document.getElementById('rubyHeartButton'); if (!heart) { setTimeout(setupRubySpecials, 150); return; } /* Prevent duplicate setup */ if (heart.dataset.rubySpecialsReady === '1') { return; } heart.dataset.rubySpecialsReady = '1'; /* ===================================================== CREATE BACKDROP ===================================================== */ let backdrop = document.getElementById('rubySpecialsBackdrop'); if (!backdrop) { backdrop = document.createElement('div'); backdrop.id = 'rubySpecialsBackdrop'; document.body.appendChild(backdrop); } /* ===================================================== CREATE MODAL ===================================================== */ let modal = document.getElementById('rubySpecialsModal'); if (!modal) { modal = document.createElement('div'); modal.id = 'rubySpecialsModal'; document.body.appendChild(modal); } modal.setAttribute('role', 'dialog'); modal.setAttribute('aria-modal', 'true'); modal.setAttribute('aria-label', "Ruby's Specials"); modal.setAttribute('aria-hidden', 'true'); /* ===================================================== CREATE MENU IMAGE ===================================================== */ let menuImage = document.getElementById('rubySpecialsImage'); if (!menuImage) { menuImage = document.createElement('img'); menuImage.id = 'rubySpecialsImage'; menuImage.alt = "Ruby Redbee's Specials Menu"; modal.appendChild(menuImage); } menuImage.src = RUBY_SPECIALS_IMAGE; menuImage.loading = 'eager'; /* ===================================================== CREATE CLOSE BUTTON ===================================================== */ let closeButton = document.getElementById('rubySpecialsClose'); if (!closeButton) { closeButton = document.createElement('button'); closeButton.id = 'rubySpecialsClose'; closeButton.type = 'button'; closeButton.innerHTML = '×'; closeButton.setAttribute( 'aria-label', "Close Ruby's Specials" ); modal.appendChild(closeButton); } /* ===================================================== OPEN / CLOSE ===================================================== */ let previousOverflow = ''; let previousFocus = null; function openSpecials() { previousOverflow = document.body.style.overflow; previousFocus = document.activeElement; backdrop.classList.add('ruby-open'); modal.classList.add('ruby-open'); modal.setAttribute( 'aria-hidden', 'false' ); document.body.style.overflow = 'hidden'; closeButton.focus(); } function closeSpecials() { if (!modal.classList.contains('ruby-open')) { return; } backdrop.classList.remove('ruby-open'); modal.classList.remove('ruby-open'); modal.setAttribute( 'aria-hidden', 'true' ); document.body.style.overflow = previousOverflow; if ( previousFocus && typeof previousFocus.focus === 'function' ) { previousFocus.focus(); } } /* ===================================================== HEART CLICK ===================================================== */ heart.addEventListener( 'click', function (event) { event.preventDefault(); openSpecials(); } ); /* ===================================================== CLOSE EVENTS ===================================================== */ closeButton.addEventListener( 'click', closeSpecials ); backdrop.addEventListener( 'click', closeSpecials ); document.addEventListener( 'keydown', function (event) { if ( event.key === 'Escape' && modal.classList.contains('ruby-open') ) { closeSpecials(); } } ); /* ===================================================== CLOSE WHEN CHANGING SECTIONS ===================================================== */ window.addEventListener( 'hashchange', closeSpecials ); /* ===================================================== READY ===================================================== */ console.log( 'Sunset Coast: Ruby Specials Popup V1 ready.' ); } /* ===================================================== START ===================================================== */ setupRubySpecials(); document.addEventListener( 'DOMContentLoaded', setupRubySpecials ); window.addEventListener( 'load', setupRubySpecials ); })();
(function () { function initialiseEclipseBackground() { let background = document.getElementById('eclipseBackground'); if (!background) { background = document.createElement('div'); background.id = 'eclipseBackground'; document.body.appendChild(background); } /* ALL SECTIONS USING ECLIPSE BACKGROUND */ const eclipseSections = [ 'eclipse', 'court' ]; function updateBackground() { const section = window.location.hash .replace('#', '') .toLowerCase(); const eclipseActive = eclipseSections.includes(section); document.body.classList.toggle( 'eclipseMode', eclipseActive ); } /* INITIAL CHECK */ updateBackground(); /* SECTION NAVIGATION */ window.addEventListener( 'hashchange', updateBackground ); /* BROWSER BACK / FORWARD */ window.addEventListener( 'pageshow', updateBackground ); } if (document.readyState === 'loading') { document.addEventListener( 'DOMContentLoaded', initialiseEclipseBackground ); } else { initialiseEclipseBackground(); } })();