diff --git a/assets/css/styles.css b/assets/css/styles.css index 0774368..c538eb1 100644 --- a/assets/css/styles.css +++ b/assets/css/styles.css @@ -1,3 +1,4 @@ +/* Layout */ body { display: flex; flex-direction: column; @@ -5,15 +6,64 @@ body { align-items: center; height: 100vh; margin: 0; + font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", + sans-serif; + transition: background-color 0.3s ease, color 0.3s ease; } -button { +/* Themes */ +body.light-theme { + background-color: #fdfdfd; + color: #111; +} + +body.dark-theme { + background-color: #050509; + color: #f5f5f5; +} + +/* Button */ +button#kinky-button { height: 50px; width: 200px; font-size: large; cursor: pointer; + border-radius: 8px; + border: none; + background: linear-gradient(135deg, #ff4b6e, #ff9f1c); + color: #fff; + font-weight: 600; + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25); + transition: transform 0.1s ease, box-shadow 0.1s ease, filter 0.15s ease; +} + +body.dark-theme button#kinky-button { + box-shadow: 0 10px 24px rgba(0, 0, 0, 0.5); +} + +button#kinky-button:hover { + filter: brightness(1.05); +} + +button#kinky-button:active { + transform: translateY(1px) scale(0.99); + box-shadow: 0 5px 14px rgba(0, 0, 0, 0.35); +} + +/* Click counter */ +#click-count { + font-size: 10px; + font-family: "Courier New", Courier, monospace; + font-weight: bold; + padding: 5px; + margin-top: 10px; + border: 1px solid currentColor; + border-radius: 5px; + text-align: center; + background-color: transparent; } +/* Footer */ footer { display: flex; flex-direction: row; @@ -22,6 +72,7 @@ footer { padding: 30px 10px; align-items: center; width: 100vw; + box-sizing: border-box; } footer section { @@ -40,36 +91,153 @@ footer section#footer-left { left: 40px; } -footer p#footer-desc { - font-size: small; -} - +footer p#footer-desc, footer p#copyright { font-size: small; } +/* Floating messages */ .floating-message { position: fixed; transform: translate(-50%, -50%); - color: black; font-size: 18px; pointer-events: none; opacity: 1; transition: opacity 2s ease; z-index: 1000; + max-width: 60vw; + text-align: center; + text-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); +} + +body.light-theme .floating-message { + color: #111; + text-shadow: 0 2px 4px rgba(255, 255, 255, 0.6); +} + +body.dark-theme .floating-message { + color: #f5f5f5; } .floating-message.fade-out { opacity: 0; } -#click-count { - font-size: 10px; - font-family: "Courier New", Courier, monospace; - font-weight: bold; - padding: 5px; +/* Settings toggle button */ +#settings-toggle { + position: fixed; + right: 16px; + bottom: 16px; + width: 40px; + height: 40px; + border-radius: 999px; + border: none; + font-size: 20px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + background: rgba(0, 0, 0, 0.08); + backdrop-filter: blur(10px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25); + transition: transform 0.15s ease, box-shadow 0.15s ease, + background-color 0.15s ease; + z-index: 1100; +} + +body.dark-theme #settings-toggle { + background: rgba(255, 255, 255, 0.04); + color: #f5f5f5; +} + +#settings-toggle:hover { + transform: translateY(-1px); + box-shadow: 0 6px 18px rgba(0, 0, 0, 0.3); +} + +#settings-toggle:active { + transform: translateY(0); + box-shadow: 0 3px 10px rgba(0, 0, 0, 0.25); +} + +/* Settings modal */ +#settings-modal { + position: fixed; + inset: 0; + display: none; + align-items: center; + justify-content: center; + z-index: 1200; +} + +#settings-modal.open { + display: flex; +} + +#settings-backdrop { + position: absolute; + inset: 0; + background: rgba(0, 0, 0, 0.5); +} + +#settings-content { + position: relative; + z-index: 1; + width: min(360px, 90vw); + border-radius: 16px; + padding: 20px 22px 18px; + box-shadow: 0 18px 45px rgba(0, 0, 0, 0.4); + background: #ffffff; + color: #111; +} + +body.dark-theme #settings-content { + background: #15151c; + color: #f5f5f5; +} + +#settings-content h2 { + margin: 0 0 12px; + font-size: 20px; +} + +.settings-group { margin-top: 10px; - border: 1px solid black; - border-radius: 5px; - text-align: center; + margin-bottom: 6px; +} + +.settings-group h3 { + margin: 6px 0; + font-size: 14px; + opacity: 0.8; } + +.settings-group label { + display: inline-flex; + align-items: center; + margin-right: 10px; + margin-bottom: 4px; + gap: 4px; + font-size: 13px; + cursor: pointer; +} + +.settings-actions { + margin-top: 12px; + display: flex; + justify-content: flex-end; +} + +#settings-close { + border-radius: 999px; + padding: 6px 16px; + border: none; + background: #e0e0e0; + cursor: pointer; + font-size: 13px; +} + +body.dark-theme #settings-close { + background: #2b2b38; + color: #f5f5f5; +} \ No newline at end of file diff --git a/assets/js/main.js b/assets/js/main.js index e567fe7..69be75a 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -34,12 +34,12 @@ const messages = [ "Is that it? I know you can do more than that...", "💦💦💦", "Oooh, that's tight...", - "Keep cliking, Daddy...", + "Keep clicking, Daddy...", "Click me like you mean it...", "This feels like heaven...", "Oooohhh, I'm getting close...", "Yes, right there...", - "I hope this is touchsrcreen...", + "I hope this is touchscreen...", "I feel a lot more than a button today...", "I'm almost there...", "Don't stop, Daddy...", @@ -54,45 +54,213 @@ const messages = [ "Oooh, I wasn't expecting that...", "UUUNNNHhhhhhhhhoohhh, I wasn't ready for this...", "🫦🫦🫦🫦 Oh my god...Ohhhh..." -]; - -let clicks = 0; -const button = document.querySelector("button"); - -button.addEventListener("click", () => { - clicks++; - document.getElementById("click-count").textContent = `Clicks: ${clicks}`; - - const randomMessage = - messages[Math.floor(Math.random() * messages.length)]; - - createFloatingMessage(randomMessage); -}); - -function createFloatingMessage(text) { + ]; + + let clicks = 0; + const button = document.querySelector("#kinky-button"); + const clickCount = document.getElementById("click-count"); + + /* --- Settings state --- */ + + const THEME_KEY = "kinky-theme"; + const TONE_KEY = "kinky-gender-tone"; + + function getStoredTheme() { + return localStorage.getItem(THEME_KEY) || "dark"; + } + + function getStoredTone() { + return localStorage.getItem(TONE_KEY) || "masculine"; + } + + function applyTheme(theme) { + const body = document.body; + body.classList.remove("light-theme", "dark-theme"); + body.classList.add(theme === "light" ? "light-theme" : "dark-theme"); + } + + function syncThemeRadios(theme) { + const radios = document.querySelectorAll('input[name="theme"]'); + radios.forEach(r => { + r.checked = r.value === theme; + }); + } + + function syncToneRadios(tone) { + const radios = document.querySelectorAll('input[name="gender-tone"]'); + radios.forEach(r => { + r.checked = r.value === tone; + }); + } + + function initSettings() { + const toggle = document.getElementById("settings-toggle"); + const modal = document.getElementById("settings-modal"); + const backdrop = document.getElementById("settings-backdrop"); + const closeBtn = document.getElementById("settings-close"); + + const storedTheme = getStoredTheme(); + const storedTone = getStoredTone(); + + applyTheme(storedTheme); + syncThemeRadios(storedTheme); + syncToneRadios(storedTone); + + toggle.addEventListener("click", () => { + modal.classList.add("open"); + modal.setAttribute("aria-hidden", "false"); + }); + + function closeModal() { + modal.classList.remove("open"); + modal.setAttribute("aria-hidden", "true"); + } + + backdrop.addEventListener("click", closeModal); + closeBtn.addEventListener("click", closeModal); + + document + .querySelectorAll('input[name="theme"]') + .forEach(radio => { + radio.addEventListener("change", () => { + const theme = radio.value; + applyTheme(theme); + localStorage.setItem(THEME_KEY, theme); + }); + }); + + document + .querySelectorAll('input[name="gender-tone"]') + .forEach(radio => { + radio.addEventListener("change", () => { + const tone = radio.value; + localStorage.setItem(TONE_KEY, tone); + }); + }); + } + + /* --- Message gender / tone transformation --- */ + + function transformMessage(baseText, tone) { + if (tone === "masculine") { + return baseText; + } + + let text = baseText; + + const feminineReplacements = [ + [/good\s+boy/gi, "good girl"], + [/boy/gi, "girl"], + [/daddy/gi, "mommy"], + [/Daddy/gi, "Mommy"], + [/king/gi, "queen"], + [/sir/gi, "ma'am"] + ]; + + const neutralReplacements = [ + [/good\s+boy/gi, "good baby"], + [/boy/gi, "babe"], + [/daddy/gi, "baby"], + [/Daddy/gi, "Baby"], + [/king/gi, "darling"], + [/sir/gi, "love"] + ]; + + const table = + tone === "feminine" ? feminineReplacements : neutralReplacements; + + table.forEach(([pattern, replacement]) => { + text = text.replace(pattern, replacement); + }); + + return text; + } + + /* --- Floating message positioning (avoid button) --- */ + + function getSafeRandomPosition(buttonRect) { + const centerX = window.innerWidth / 2; + const centerY = window.innerHeight / 2; + const maxOffsetX = Math.min(500, window.innerWidth / 2 - 20); + const maxOffsetY = Math.min(500, window.innerHeight / 2 - 40); + + const padding = 80; // "no-go" halo around the button for readability + let attempts = 0; + + while (attempts < 30) { + const offsetX = (Math.random() - 0.5) * 2 * maxOffsetX; + const offsetY = (Math.random() - 0.5) * 2 * maxOffsetY; + + let x = centerX + offsetX; + let y = centerY + offsetY; + + // Constrain to viewport-ish area + x = Math.max(40, Math.min(window.innerWidth - 40, x)); + y = Math.max(60, Math.min(window.innerHeight - 80, y)); + + const overlapsButton = + x > buttonRect.left - padding && + x < buttonRect.right + padding && + y > buttonRect.top - padding && + y < buttonRect.bottom + padding; + + if (!overlapsButton) { + return { x, y }; + } + + attempts++; + } + + // Fallback: just go near top of screen if we somehow failed + return { + x: centerX, + y: 80 + }; + } + + function createFloatingMessage(text) { const message = document.createElement("div"); message.classList.add("floating-message"); message.textContent = text; - + document.body.appendChild(message); - - // Position near center with randomness - const centerX = window.innerWidth / 2; - const centerY = window.innerHeight / 2; - - const offsetX = (Math.random() - 0.5) * 500; // ±250px range - const offsetY = (Math.random() - 0.5) * 500; - - message.style.left = `${centerX + offsetX}px`; - message.style.top = `${centerY + offsetY}px`; - - // Trigger fade out after slight delay + + const buttonRect = button.getBoundingClientRect(); + const { x, y } = getSafeRandomPosition(buttonRect); + + message.style.left = `${x}px`; + message.style.top = `${y}px`; + + // Trigger fade out setTimeout(() => { - message.classList.add("fade-out"); + message.classList.add("fade-out"); }, 50); - - // Remove from DOM after animation ends + message.addEventListener("transitionend", () => { - message.remove(); + message.remove(); }); -} + } + + /* --- Main click logic --- */ + + button.addEventListener("click", () => { + clicks++; + clickCount.textContent = `Clicks: ${clicks}`; + + const randomMessage = + messages[Math.floor(Math.random() * messages.length)]; + + const tone = getStoredTone(); + const transformed = transformMessage(randomMessage, tone); + + createFloatingMessage(transformed); + }); + + /* --- Init --- */ + + document.addEventListener("DOMContentLoaded", () => { + initSettings(); + + // Ensure a theme is set on first load + applyTheme(getStoredTheme()); + }); \ No newline at end of file diff --git a/index.html b/index.html index 74fb477..549eda8 100644 --- a/index.html +++ b/index.html @@ -13,21 +13,68 @@
- +Clicks: 0
+ + + + + + + + + + - - -