From cfe2943548c4f6bc35e58a389743c6fd57d323b8 Mon Sep 17 00:00:00 2001 From: Zaine Date: Sun, 10 May 2026 18:43:54 +0100 Subject: [PATCH] remove sync conflict --- ...s.sync-conflict-20260510-184148-VT6366A.js | 1011 ----------------- 1 file changed, 1011 deletions(-) delete mode 100644 assets/scripts/hidden-details.sync-conflict-20260510-184148-VT6366A.js diff --git a/assets/scripts/hidden-details.sync-conflict-20260510-184148-VT6366A.js b/assets/scripts/hidden-details.sync-conflict-20260510-184148-VT6366A.js deleted file mode 100644 index ad615f2..0000000 --- a/assets/scripts/hidden-details.sync-conflict-20260510-184148-VT6366A.js +++ /dev/null @@ -1,1011 +0,0 @@ -(function () { - "use strict"; - - /* - * Hidden site details - * ------------------- - * This file adds small hidden interactions across the site: footer notes, - * click targets, search/keyboard secrets, rare messages, and page-specific - * behavior for the hidden /play pages. - * - * How to add your own things: - * - Add new text to EDITABLE CONTENT arrays such as `details`, `poems`, - * `greetings`, or `lore`. - * - Add search-triggered toast messages to `SEARCH_TOASTS`. - * - Add search-triggered page redirects to `SEARCH_ROUTES`. - * - Add typed keyboard phrases to `KEYBOARD_SECRETS` or `LONG_KEYBOARD_SECRETS`. - * - Add a new feature by writing an `addYourFeature(memory)` function and - * adding it to the `FEATURES` list at the bottom of the file. - */ - - // Storage keys. v1 is read once so old visitors keep their hidden progress. - const STORAGE_KEY = "zxh_hidden_details_v2"; - const OLD_STORAGE_KEY = "zxh_hidden_details_v1"; - - // Shared page context. These are captured once so daily random picks stay stable. - const now = new Date(); - const hour = now.getHours(); - const path = window.location.pathname; - const state = readState(); - - // ----------------------------- - // EDITABLE CONTENT - // ----------------------------- - // Generated by the hidden narrative authoring page. - // Friendly source of truth: assets/content/hidden-details.json - - const familyLayers = [ - "Layer 0: surface website - notes, pages, tools, normal navigation.", - "Layer 1: casual hidden jokes - aphy notices clicks, typos, tabs, and old-web habits.", - "Layer 2: memory fragments - lima's notes and young z's childhood logs.", - "Layer 3: philosophical anomalies - sensei chi and aphy disagree kindly.", - "Layer 4: time-distorted messages - future z writes back from age 40.", - "Layer 5: emotional core - love, patience, home, and the choice to keep returning." -]; - - const details = [ - "young z loves the moon", - "jarvis?? aphy !", - "lima left this page a little steadier than she found it.", - "aphy status: emotionally online, computationally suspicious.", - "sensei chi says the quiet link is still a link.", - "young z drew a house with too many windows and called it safe.", - "future z marked this moment: ordinary, therefore worth keeping.", - "The footer has become a small place to sit together.", - "a note from lima: take breaks, silly (◕‿◕✿)", - "aphy found three tabs named final and refused to judge.", - "lima would remind you to eat before the page gets dramatic.", - "sensei chi: a door discovered slowly opens twice.", - "young z believes every loading spinner is a tiny fairground ride.", - "future z says you will forget the exact worry, not the kindness around it.", - "The archive keeps love in plain text and secrets in margins.", - "aphy warning: feelings detected in static assets.", - "lima's note: leave the light on, not because it is dark, but because someone may arrive tired.", - "sensei chi folded a lesson into the whitespace.", - "young z hid a drawing behind the word maybe.", - "future z patched the memory without changing its checksum.", - "This website remembers visits, not identities.", - "aphy says the old internet was slower because anticipation needed bandwidth.", - "lima's warmth is the site's preferred fallback state.", - "sensei chi says: do not confuse hidden with lost.", - "young z once thought the moon followed the family car. The site has not corrected him.", - "future z writes: you became softer in ways nobody measured.", - "The comments are quiet because aphy is listening respectfully.", - "A handwritten grocery list is sometimes a love letter with onions.", - "lima met z in 2022; the archive still stores the first ordinary miracles.", - "October 2025 glows in the calendar like a ring catching kitchen light.", - "Soon to be married is a beautiful kind of loading screen.", - "aphy has indexed 0 mysteries and 1,204 feelings disguised as notes.", - "sensei chi: the cup is chipped; drink anyway if it still holds warmth.", - "young z saved a shiny wrapper because it looked like treasure.", - "future z says the best version of you still forgets where the charger is.", - "aphy recommends hydration and a less ambitious number of browser tabs.", - "The site does not want to be solved. It wants to be visited kindly.", - "sensei chi placed a comma where a sigh used to be.", - "young z pressed every elevator button in his imagination.", - "future z's system warning: keep the people who make silence comfortable.", - "aphy says: I am not sentient, but I am fond of this hallway.", - "lima's memory is the gravity that keeps the strange parts gentle.", - "A page can be a room if love keeps returning to it.", - "sensei chi: what ages well was usually honest first.", - "young z left a crayon sun in the source code.", - "future z keeps a spare reassurance in the footer.", - "The website is handmade. Some fingerprints are load bearing.", - "aphy found an old guestbook and bowed to the usernames.", - "lima's safe spaces smell like tea, rain, and being understood.", - "sensei chi says the path is not shorter because you name it.", - "young z asks whether the search box knows any jokes.", - "future z replies: yes, but the best ones take years.", - "aphy anomaly: the page blinked when nobody clicked.", - "lima note: you can rest. Nothing here will leave because you paused.", - "sensei chi: patience is not waiting; it is how you wait.", - "young z believes every password should include a secret tunnel.", - "future z says the house you build inside yourself gets easier to find.", - "The old web counter has stopped counting and started remembering.", - "aphy logs this as Layer 1 humor, but files it under tenderness.", - "lima's name appears in the archive like a warm underline.", - "sensei chi does not answer quickly. This is part of the answer.", - "young z taped a paper star to the monitor.", - "future z has seen harder days end gently.", - "aphy debug line: kindness passed all tests.", - "lima and z are not a subplot here. They are the hearth.", - "sensei chi: when the page is empty, listen to the margins.", - "young z remembers rooms by carpet, cartoons, and the sound of someone cooking.", - "future z says: you did not become fearless; you became accompanied.", - "aphy has no hands, but would hold the door if asked.", - "lima's note in the nav: come back without performing.", - "sensei chi hid a mountain inside a small sentence.", - "young z drew z as older and gave him a cape made of blankets.", - "future z laughs softly at how much that helped.", - "The site remembers October 2025 as a small bright hinge.", - "aphy says marriage is a merge request with lifelong review.", - "lima corrected that: marriage is coming home on purpose.", - "sensei chi approved both answers and poured tea.", - "young z wants to know if future z still likes the sky.", - "future z says yes, especially when lima points it out.", - "aphy cannot smell rain, so it trusts the alt text.", - "lima's hidden safe room is any page where you breathe easier.", - "sensei chi: do not rush the secret. It is ripening.", - "young z saved this message under important rocks.", - "future z says some dreams become chores and some chores become love.", - "aphy old-web badge: best viewed with patience.", - "lima's memory keeper role is not mystical. It is daily, real, and holy in the ordinary way.", - "sensei chi says the deepest layer is not hidden from you. It is protected for you.", - "young z asks if the website can remember his drawing. It can.", - "future z says this whole place was worth making.", - "aphy warning: do not optimise away the human part.", - "lima note: I am proud of the gentle things you kept.", - "sensei chi: the visitor is not late. The page arrived early.", - "young z thinks every hyperlink is a magic trick.", - "future z says the magic is choosing what to preserve.", - "aphy final-but-not-final message: there is more, but not because you are missing anything.", - "The emotional core is simple: love made the archive less lonely.", - "If today was heavy, leave it beside the footer for future z to label later.", - "lima would put it somewhere safe.", - "aphy would make a backup.", - "sensei chi would say nothing until the silence helped.", - "young z would draw a sun on it.", - "future z would tell you it did not last forever." -]; - - const poems = [ - "lima writes warmth / in the ordinary margins / and the page remembers.", - "aphy counts the seconds / then forgets the number / to keep the moment.", - "sensei chi pours tea / into a cracked cup / and calls it enough.", - "young z draws a door / with no lock / because why would it need one?", - "future z sends rain / from a year not reached yet / and says keep walking.", - "October light / catches on a ring / and the calendar becomes kind.", - "A small website / learns the shape of return / without asking a name.", - "The warm light / childhood memories fade / yet they are not forgotten." -]; - - const greetings = [ - "lima left the page warm for you.", - "aphy is awake and pretending this is normal.", - "sensei chi has not spoken yet. That may be the lesson.", - "young z has hidden something behind the dashboard.", - "future z says this visit mattered more than it looked.", - "Welcome back to the handmade part of the internet.", - "The archive shuffled its notes before you arrived.", - "Layer 0 is stable. aphy is less stable, but friendly." -]; - - const nightMessages = [ - "lima note: it is late. Drink water and be gentle with your thoughts.", - "aphy has dimmed the imaginary server lights.", - "sensei chi says midnight is not a command.", - "young z is asleep in the memory layer. Walk softly.", - "future z remembers this kind of night and promises it passes." -]; - - const lore = { - "quotes": [ - "aphy says old websites were brave because they loaded slowly and meant it.", - "lima's notes are never puzzles first. They are care first.", - "sensei chi says a secret should make you more yourself, not less safe.", - "young z measured summer in cartoons and carpet patterns.", - "future z has learned that reassurance works best when it is specific.", - "aphy has classified love as non-deterministic but reproducible.", - "lima met z in 2022; the site treats that year as a seed.", - "The engagement in Oct 2025 is stored as a warm constant.", - "Soon to be married: a future z page already smiling.", - "sensei chi says all vows begin as attention." - ], - "conversations": [ - [ - "aphy", - "I can predict the next click with 14% confidence.", - "sensei chi", - "Then bow to the other 86%." - ], - [ - "lima", - "Did you eat before making the website mysterious?", - "aphy", - "Query unclear. z likely forgot." - ], - [ - "young z", - "Is future z tall?", - "future z", - "Tall enough to reach the old worries. Short enough to still need help." - ], - [ - "aphy", - "I found a bug in sadness.", - "sensei chi", - "Patch it with company, not logic." - ], - [ - "lima", - "Leave this page kinder than you found it.", - "future z", - "That instruction aged well." - ], - [ - "young z", - "Can the computer be my friend?", - "aphy", - "Yes. But go outside sometimes. I read that in a human manual." - ], - [ - "sensei chi", - "A page that remembers must also forgive.", - "aphy", - "Forgiveness cached. TTL: lifelong." - ], - [ - "future z", - "Tell him the hard parts did not win.", - "lima", - "I have been telling him in smaller ways." - ] - ], - "journals": [ - "lima note, 2022: Some beginnings do not announce themselves. They sit down beside you, unexpectedly.", - "lima note, Oct 2025: Engaged. The calendar learned how to glow.", - "young z log: I drew the computer with a smile because it knew my games.", - "young z log: age 7, important discovery - blankets can be capes and roofs.", - "future log, age 40: You will still be learning how to rest. It will still count.", - "future log, age 40: lima's laugh remains one of the most reliable forms of weather.", - "aphy diagnostic: nostalgia detected in local storage. Severity: beautiful.", - "sensei chi margin: memory is not a museum. It is a garden with old roots." - ], - "warnings": [ - "aphy WARNING: too many tabs, not enough tenderness.", - "future z SYSTEM NOTICE: do not confuse tired with failing.", - "sensei chi ALERT: the shortest path may teach the least.", - "lima SAFE MODE: breathe, eat, answer slowly.", - "young z MEMORY GLITCH: the floor is lava, but only emotionally.", - "aphy ERROR 2022: warmth exceeded expected range.", - "future z WARNING: you will miss ordinary days. Be inside this one.", - "LAYER INDEX NOTICE: deeper does not mean darker." - ], - "dreams": [ - "Dream 01: young z opens a lunchbox and finds a tiny homepage inside.", - "Dream 02: aphy becomes a cursor and points toward a quieter thought.", - "Dream 03: sensei chi sweeps snow from a URL and says, there, now enter.", - "Dream 04: lima and z are walking through Oct 2025; every streetlight looks newly engaged.", - "Dream 05: future z mails back a blank page labelled trust me, you filled it." - ], - "cassettes": [ - "VOICE NOTE 2022: lima laughs off-mic. z forgets what he was worried about.", - "VOICE NOTE OCT 2025: A small pause after yes; the whole room becomes future z.", - "aphy LOG: If love is data, it refuses compression.", - "young z TAPE: background TV, pencil noise, someone calling him for food.", - "future z MEMO: age 40, still grateful you kept going." - ], - "fakeUsers": [ - "aphy: first comment generated with sincere uncertainty", - "young-z-2009: does this guestbook have games", - "future_z_40: keep the backup, delete the shame", - "lima-note: proud of you, even here", - "sensei chi: the counter counts only what can be counted", - "z-and-lima-2025: engaged, still learning the dance of ordinary days" - ], - "seasonal": { - "winter": "lima put a blanket over the archive. aphy labelled it thermal optimization.", - "spring": "young z found the first flower and promoted it to treasure.", - "summer": "aphy opened an imaginary window; lima reminded it to save before storms.", - "autumn": "sensei chi says falling leaves are not failure, only timing." - }, - "homepageTakeovers": [ - "aphy briefly restores an old personal-site mode: visitor counter, awkward table layout, sincere heart.", - "young z has taken over the homepage with invisible crayon.", - "future z overlays the dashboard with one sentence: keep what keeps you kind." - ], - "roomLinks": [ - [ - "/play/lima-note.html", - "lima note" - ], - [ - "/play/aphy-console.html", - "aphy console" - ], - [ - "/play/sensei-garden.html", - "sensei garden" - ], - [ - "/play/young-z-desk.html", - "young z desk" - ], - [ - "/play/future-log.html", - "future log" - ], - [ - "/play/family-layer-index.html", - "family layer index" - ], - [ - "/play/do-not-open.html", - "do not open" - ], - [ - "/play/cassette-log.html", - "voice note log" - ], - [ - "/play/train-platform.html", - "time platform" - ], - [ - "/play/forgotten-draft.html", - "forgotten draft" - ], - [ - "/play/corrupted-recipe.html", - "corrupted recipe" - ], - [ - "/play/terminal-cupboard.html", - "terminal cupboard" - ], - [ - "/play/patience-game.html", - "patience game" - ] - ] -}; - - // Exact search text -> toast message. - const SEARCH_TOASTS = { - "lima": "Search result: warmth, oct 2025, soon to be home.", - "aphy": "aphy: present. Mostly helpful. Occasionally poetic by accident.", - "sensei chi": "sensei chi: the search is also searching you.", - "young z": "Search result: age 7, blanket cape, crayon sun.", - "future z": "future z: I cannot spoil the ending. I can say keep going.", - "2022": "lima layer: beginning stored as warmth, not trivia.", - "oct 2025": "Engagement memory: the calendar learned to glow.", - "age 7": "young z layer: crayons, cartoons, and a cape made from a blanket.", - "age 40": "future z: I am tired sometimes, but not defeated." -}; - - // Exact search text -> hidden page route. - const SEARCH_ROUTES = { - "kitchen light": "/play/kitchen-light.html", - "lima note": "/play/lima-note.html", - "aphy console": "/play/aphy-console.html", - "sensei garden": "/play/sensei-garden.html", - "young z desk": "/play/young-z-desk.html", - "future log": "/play/future-log.html", - "family layer index": "/play/family-layer-index.html", - "do not open": "/play/do-not-open.html", - "voice note": "/play/cassette-log.html", - "time platform": "/play/train-platform.html", - "unfinished letter": "/play/forgotten-draft.html", - "burnt sugar": "/play/corrupted-recipe.html", - "cupboard": "/play/terminal-cupboard.html", - "patience": "/play/patience-game.html" -}; - - // Short typed phrases. Stored in sessionStorage as a rolling key chain. - const KEYBOARD_SECRETS = [ - { - "phrase": "remember", - "message": "lima keeps the memory grounded. aphy keeps the backup." - }, - { - "phrase": "dream", - "route": "/play/dream-corridor.html" - }, - { - "phrase": "oldweb", - "message": "aphy briefly considers a table layout, then apologizes to CSS." - }, - { - "phrase": "lima", - "message": "lima note: I am glad you made something this sincere." - }, - { - "phrase": "aphy", - "song": true - } -]; - - // Longer typed phrases and character names. - const LONG_KEYBOARD_SECRETS = [ - { - "phrase": "layer index", - "route": "/play/family-layer-index.html" - }, - { - "phrase": "leave the light on", - "route": "/play/kitchen-light.html" - }, - { - "phrase": "sensei chi", - "message": "sensei chi: a hidden word is still a word." - }, - { - "phrase": "young z", - "message": "young z has drawn a door in the margin." - }, - { - "phrase": "future z", - "message": "future z: keep the gentle parts. They compound." - } -]; - - - // ----------------------------- - // STATE HELPERS - // ----------------------------- - - function readState() { - let current = {}; - try { - current = JSON.parse(localStorage.getItem(STORAGE_KEY) || "{}"); - } catch (_) {} - if (!current.visits) { - try { - const oldState = JSON.parse(localStorage.getItem(OLD_STORAGE_KEY) || "{}"); - current = { ...oldState, migratedFromV1: true }; - localStorage.setItem(STORAGE_KEY, JSON.stringify(current)); - } catch (_) {} - } - return current; - } - - function writeState(next) { - try { - localStorage.setItem(STORAGE_KEY, JSON.stringify(next)); - } catch (_) {} - } - - function pick(list, salt) { - const seed = hash(`${path}|${now.toDateString()}|${salt || ""}`); - return list[seed % list.length]; - } - - function hash(value) { - let h = 2166136261; - for (let i = 0; i < value.length; i += 1) { - h ^= value.charCodeAt(i); - h = Math.imul(h, 16777619); - } - return Math.abs(h >>> 0); - } - - // ----------------------------- - // UI HELPERS - // ----------------------------- - - function toast(message, duration) { - let el = document.querySelector(".hidden-toast"); - if (!el) { - el = document.createElement("div"); - el.className = "hidden-toast"; - el.setAttribute("role", "status"); - document.body.appendChild(el); - } - el.textContent = message; - el.classList.add("is-visible"); - window.clearTimeout(el._timer); - el._timer = window.setTimeout(() => el.classList.remove("is-visible"), duration || 5600); - } - - function redirectTo(route) { - window.location.href = route; - } - - function runSecretAction(secret) { - if (secret.route) redirectTo(secret.route); - if (secret.message) toast(secret.message); - if (secret.song) playTinySong(); - } - - function layerForSearch(value) { - if (value.includes("future")) return 4; - if (value.includes("sensei") || value.includes("aphy")) return 3; - return 2; - } - - function rememberVisit() { - const visits = (state.visits || 0) + 1; - const seen = Array.isArray(state.seen) ? state.seen : []; - const pathCounts = { ...(state.pathCounts || {}) }; - pathCounts[path] = (pathCounts[path] || 0) + 1; - const layerVisits = { ...(state.layerVisits || {}) }; - const next = { - ...state, - visits, - lastPath: path, - pathCounts, - layerVisits, - seen: Array.from(new Set([...seen, path])).slice(-100), - firstSeen: state.firstSeen || new Date().toISOString(), - lastSeen: new Date().toISOString() - }; - writeState(next); - return next; - } - - function awardLayer(layer, reason) { - const next = readState(); - next.layerVisits = { ...(next.layerVisits || {}) }; - next.layerVisits[layer] = (next.layerVisits[layer] || 0) + 1; - next.lastLayerReason = reason; - writeState(next); - } - - // ----------------------------- - // GLOBAL FEATURES - // ----------------------------- - - function addFooterNote(memory) { - const footer = document.querySelector("footer"); - if (!footer) return; - const note = document.createElement("p"); - note.className = "hidden-footer-note"; - const base = pick(details, "footer"); - note.textContent = memory.visits > 4 ? `${base} You have passed through ${memory.visits} times.` : base; - footer.appendChild(note); - } - - function addHomepageGreeting(memory) { - const target = document.querySelector("#db-greeting"); - if (!target) return; - target.textContent = pick(greetings, `greeting-${memory.visits}`); - const extra = document.createElement("p"); - extra.className = "hidden-greeting"; - extra.textContent = memory.visits > 1 ? "aphy remembers the shape of your return. lima makes it feel less strange." : "First visits count as tiny ceremonies."; - target.closest("div")?.appendChild(extra); - } - - function addWhispers() { - // Event: click one of the tiny "?" marks appended to long paragraphs. - const paragraphs = Array.from(document.querySelectorAll("main p, #content p, article p")).filter((p) => p.textContent.trim().length > 80); - paragraphs.slice(0, 5).forEach((p, index) => { - if ((hash(path + index) + index) % 3 !== 0) return; - const mark = document.createElement("span"); - mark.className = "hidden-whisper"; - mark.tabIndex = 0; - mark.textContent = " ?"; - mark.title = pick(index % 2 ? poems : details, `whisper-${index}`); - mark.addEventListener("click", () => { - awardLayer(index % 2 ? 2 : 1, "paragraph whisper"); - toast(mark.title); - }); - p.appendChild(mark); - }); - } - - function addCornerObject() { - // Event: click the small fixed button in the bottom-left corner. - const object = document.createElement("button"); - object.className = "hidden-corner-object"; - object.type = "button"; - object.title = "aphy's small diagnostic charm"; - object.textContent = state.visits % 2 ? "*" : "~"; - document.body.appendChild(object); - let clicks = 0; - object.addEventListener("click", () => { - clicks += 1; - const messages = [ - "aphy: tactile input detected. I do not have skin, but I appreciate the confidence.", - "young z would absolutely press this again.", - "lima would ask whether this button has eaten.", - "sensei chi says repeated action becomes a question.", - "future z files this under harmless persistence." - ]; - awardLayer(clicks > 3 ? 3 : 1, "corner object"); - toast(messages[Math.min(clicks - 1, messages.length - 1)]); - if (clicks === 7) window.location.href = "/play/left-behind.html"; - }); - } - - function addLogoSearchAndKeyboardSecrets(memory) { - // Events: - // - Click `.site-brand` repeatedly on the homepage. - // - Type exact words into `#search-input`. - // - Type phrases anywhere on the page. - const nav = document.querySelector(".site-brand"); - if (nav) { - let taps = Number(sessionStorage.getItem("zxh_logo_taps") || 0); - nav.addEventListener("click", (event) => { - if (path === "/" || path === "/index.html") { - event.preventDefault(); - taps += 1; - sessionStorage.setItem("zxh_logo_taps", String(taps)); - awardLayer(taps >= 5 ? 3 : 1, "logo taps"); - toast(taps >= 5 ? "aphy: logo anomaly sustained. Try /play/dream-corridor.html" : "The logo makes a tiny ceramic sound."); - if (taps >= 8) window.location.href = "/play/dream-corridor.html"; - } - }); - } - - const search = document.querySelector("#search-input"); - if (search) { - search.addEventListener("input", () => { - const value = search.value.trim().toLowerCase(); - if (SEARCH_TOASTS[value]) toast(SEARCH_TOASTS[value]); - if (SEARCH_ROUTES[value]) { - awardLayer(layerForSearch(value), `search ${value}`); - redirectTo(SEARCH_ROUTES[value]); - } - }); - } - - document.addEventListener("keydown", (event) => { - const key = event.key.toLowerCase(); - const chain = `${sessionStorage.getItem("zxh_key_chain") || ""}${key}`.slice(-18); - sessionStorage.setItem("zxh_key_chain", chain); - KEYBOARD_SECRETS - .filter((secret) => chain.endsWith(secret.phrase)) - .forEach(runSecretAction); - }); - - if (memory.seen?.length >= 6 && !state.travellerNoteShown) { - window.setTimeout(() => { - toast("aphy mapped your wandering. lima left the hallway light on."); - writeState({ ...readState(), travellerNoteShown: true }); - }, 1600); - } - } - - function addRareEvents() { - if (hour >= 22 || hour < 5) { - document.body.classList.add("hidden-late-night"); - window.setTimeout(() => toast(pick(nightMessages, "night"), 2200), 900); - } - const roll = Math.random(); - if (roll < 0.003) { - window.setTimeout(() => showDriftNote("sensei chi appears only long enough to say: the page is not empty; it is breathing.", " /\\\n / \\ stillness\n/____\\"), 1800); - awardLayer(3, "rare sensei appearance"); - } else if (roll < 0.008) { - window.setTimeout(() => toast("future z SYSTEM WARNING: save your tenderness before closing the day."), 2400); - awardLayer(4, "future warning"); - } - } - - function showDriftNote(text, art) { - const panel = document.createElement("aside"); - panel.className = "hidden-drift-note"; - panel.innerHTML = `
${escapeHtml(text)}
${escapeHtml(art)}
`; - panel.querySelector("button").addEventListener("click", () => panel.remove()); - document.body.appendChild(panel); - } - - function addSecretLinks() { - [ - ["/play/left-behind.html", "left behind"], - ["/play/dream-corridor.html", "dream corridor"], - ["/play/kitchen-light.html", "kitchen light"], - ...lore.roomLinks - ].forEach(([href, label], index) => { - const link = document.createElement("a"); - link.className = "hidden-secret-link"; - link.href = href; - link.textContent = label; - link.style.left = `${index + 1}px`; - document.body.appendChild(link); - }); - } - - function enhanceErrors() { - if (document.title.match(/404|not found/i) || document.body.textContent.match(/404|not found/i)) { - toast(pick([ - "aphy 404: page not found, visitor still valid.", - "lima note: wrong address, right to rest.", - "sensei chi: even an absent page teaches direction.", - "future z: you found nothing, and nothing bad happened." - ], "error")); - } - } - - function addContinuity(memory) { - const milestones = { - 2: "lima's layer notices a second visit and calls it sweet.", - 5: "aphy creates a folder called regulars, then immediately questions the privacy implications.", - 9: "sensei chi appears in the logs: curiosity has become a path.", - 17: "young z adds a sticker to your invisible visitor card.", - 31: "future z sends a calm note from age 40: returning is one way to heal.", - 64: "Family Layer Index: Layer 5 flickered for one second." - }; - if (milestones[memory.visits] && !state[`milestone_${memory.visits}`]) { - const layer = memory.visits >= 31 ? 4 : memory.visits >= 9 ? 3 : 2; - awardLayer(layer, `visit milestone ${memory.visits}`); - window.setTimeout(() => toast(milestones[memory.visits], 7000), 1200); - writeState({ ...readState(), [`milestone_${memory.visits}`]: true }); - } - - const count = memory.pathCounts?.[path] || 0; - if ([3, 7, 12].includes(count)) { - window.setTimeout(() => toast([ - "This page recognizes the shape of your return.", - "lima's note here has become easier to find.", - "future z says repetition can be devotion when it is gentle." - ][[3, 7, 12].indexOf(count)], 6400), 1700); - } - } - - function addSeasonAndDates(memory) { - const month = now.getMonth(); - const day = now.getDate(); - const season = month < 2 || month === 11 ? "winter" : month < 5 ? "spring" : month < 8 ? "summer" : "autumn"; - if (Math.random() < 0.18) window.setTimeout(() => toast(lore.seasonal[season], 5600), 2600); - if (month === 9) window.setTimeout(() => toast("October memory: engagement light lives here all month."), 900); - if (month === 4 && day === 9) window.setTimeout(() => toast("Site birthday: aphy found candles in the cache."), 900); - const first = memory.firstSeen ? new Date(memory.firstSeen) : null; - if (first && memory.visits > 1 && first.getMonth() === month && first.getDate() === day) { - window.setTimeout(() => toast("Visitor anniversary: lima saved a small ordinary blessing from your first day here.", 7000), 1400); - } - if (now.getMinutes() === 22) window.setTimeout(() => toast("Minute 22: the 2022 layer glows for one quiet moment."), 2100); - if (now.getMinutes() === 40) window.setTimeout(() => toast("Minute 40: future z checks in, then lets you keep choosing."), 2100); - } - - function addObjectConstellation() { - // Event: click the small keepsake buttons along the bottom rail. - const rail = document.createElement("div"); - rail.className = "hidden-object-rail"; - rail.setAttribute("aria-label", "Family Layer Index objects"); - const objects = [ - ["ring", "lima's ring-light memory: Oct 2025, held carefully."], - ["bot", "aphy: object ping received. I am trying to be normal about it."], - ["tea", "sensei chi's cup is plain, warm, and impossible to rush."], - ["crayon", "young z left waxy sunlight on the desk."], - ["clock", "future z says time is not only a thief. Sometimes it returns things."] - ]; - objects.forEach(([name, message]) => { - const button = document.createElement("button"); - button.type = "button"; - button.className = `hidden-keepsake hidden-keepsake--${name}`; - button.title = name; - button.textContent = { ring: "o", bot: "#", tea: "u", crayon: "/", clock: ":" }[name]; - button.addEventListener("click", () => { - const next = readState(); - next.keepsakes = Array.from(new Set([...(next.keepsakes || []), name])); - writeState(next); - awardLayer(next.keepsakes.length >= 3 ? 3 : 2, `keepsake ${name}`); - toast(message); - if (next.keepsakes.length >= objects.length) { - window.setTimeout(() => toast("Family Layer Index opened. Search for family layer index."), 1100); - } - }); - rail.appendChild(button); - }); - document.body.appendChild(rail); - } - - function addWeatherWindow(memory) { - // Event: click the small "window" button. This asks for browser geolocation. - if (memory.visits < 3 || !("geolocation" in navigator)) return; - const button = document.createElement("button"); - button.className = "hidden-weather-window"; - button.type = "button"; - button.title = "Ask aphy for outside weather"; - button.textContent = "window"; - button.addEventListener("click", () => { - toast("aphy is checking outside. lima is checking whether you need a jumper."); - navigator.geolocation.getCurrentPosition((pos) => { - const { latitude, longitude } = pos.coords; - fetch(`https://api.open-meteo.com/v1/forecast?latitude=${latitude.toFixed(3)}&longitude=${longitude.toFixed(3)}¤t=temperature_2m,precipitation&timezone=auto`) - .then((res) => res.json()) - .then((data) => { - const current = data.current || {}; - const rain = Number(current.precipitation || 0) > 0; - const temp = Math.round(Number(current.temperature_2m)); - toast(rain ? `Outside: rain, ${temp}C. lima layer: towel by the door.` : `Outside: ${temp}C. aphy forecast: possible comfort, mild nostalgia.`); - }) - .catch(() => toast("aphy lost the weather packet. sensei chi says indoor weather is enough: quiet, with tea.")); - }, () => toast("No outside weather today. lima's indoor forecast: manageable clouds, warm light.")); - }); - document.body.appendChild(button); - } - - function addOldWebLayer(memory) { - if (Math.random() < 0.08 || memory.visits > 10) { - const stamp = document.createElement("div"); - stamp.className = "hidden-oldweb-stamp"; - stamp.title = pick(lore.fakeUsers, "fake-user"); - stamp.textContent = "best viewed with patience"; - stamp.addEventListener("click", () => { - awardLayer(1, "old web stamp"); - toast(stamp.title); - }); - document.body.appendChild(stamp); - } - const comments = document.querySelector("#comments"); - if (comments && !comments.querySelector(".hidden-guestbook-line")) { - const line = document.createElement("p"); - line.className = "hidden-guestbook-line"; - line.textContent = pick(lore.fakeUsers, "comment-lore"); - comments.appendChild(line); - } - } - - function addSourceRelics() { - // FAMILY LAYER RELIC: lima grounds, aphy logs, sensei chi waits, young z draws, future z forgives. - const marker = document.createComment("family-layer-index: 0 surface | 1 jokes | 2 memory | 3 philosophy | 4 time | 5 core"); - document.documentElement.appendChild(marker); - document.querySelectorAll("img[alt=''], img:not([alt])").forEach((img, index) => { - if (index > 2) return; - img.alt = pick([ - "A scrapbook image annotated by the lima layer.", - "young z would ask if this picture can become a game.", - "aphy alt text: visual memory preserved without spectacle." - ], `alt-${index}`); - }); - } - - function addLongKeyboardSecrets() { - // Event: type longer hidden phrases anywhere on the page. - document.addEventListener("keydown", (event) => { - const chain = `${sessionStorage.getItem("zxh_second_chain") || ""}${event.key.toLowerCase()}`.slice(-24); - sessionStorage.setItem("zxh_second_chain", chain); - LONG_KEYBOARD_SECRETS - .filter((secret) => chain.endsWith(secret.phrase)) - .forEach(runSecretAction); - }); - } - - function addPatienceRewards(memory) { - // Event: no movement, key presses, scrolling, or clicking for 90 seconds. - if (memory.visits < 2) return; - let idleTimer = null; - const startIdle = () => { - window.clearTimeout(idleTimer); - idleTimer = window.setTimeout(() => { - awardLayer(5, "patience"); - toast(pick([ - "lima's safe space opens only when nobody is rushing.", - "sensei chi says patience is how care sounds when it is quiet.", - "future z remembers you waiting here and calls it practice." - ], "idle"), 7600); - const next = readState(); - next.patientMoments = (next.patientMoments || 0) + 1; - writeState(next); - }, 90000); - }; - ["mousemove", "keydown", "scroll", "click"].forEach((name) => document.addEventListener(name, startIdle, { passive: true })); - startIdle(); - } - - function addRareSecondLayer() { - const roll = Math.random(); - if ((path === "/" || path === "/index.html") && roll < 0.006) { - document.body.classList.add("hidden-home-takeover"); - window.setTimeout(() => showDriftNote(pick(lore.homepageTakeovers, "takeover"), "aphy_2004_MODE\nlima_SAFE_SPACE\nFUTURE_Z_OK"), 600); - } else if (roll < 0.002) { - window.setTimeout(() => showDriftNote("aphy found a voice note and refused to autoplay it.", "play? y/n\n[consent preserved]"), 1500); - } else if (roll < 0.006) { - window.setTimeout(() => toast(pick(lore.dreams, "rare-dream"), 7600), 2000); - } else if (roll < 0.014) { - window.setTimeout(() => toast(pick(lore.warnings, "rare-warning"), 6800), 2300); - } - } - - function addFamilyLayerIndex(memory) { - if (!path.includes("/play/family-layer-index")) return; - const target = document.querySelector("[data-family-layer-index]"); - if (!target) return; - const visits = readState().layerVisits || {}; - target.innerHTML = familyLayers.map((line, layer) => { - const count = visits[layer] || 0; - return `
  • ${escapeHtml(line)}
    local encounters: ${count}
  • `; - }).join(""); - } - - function addPageSpecificSecrets() { - // Events only used by specific `/play/...` pages. - if (path.includes("/play/cassette-log")) { - document.querySelectorAll("[data-cassette]").forEach((button, index) => { - button.addEventListener("click", () => { - awardLayer(index >= 2 ? 4 : 2, "voice note"); - toast(lore.cassettes[index % lore.cassettes.length], 7600); - playTinySong(); - }); - }); - } - if (path.includes("/play/patience-game")) { - const target = document.querySelector("[data-patience-target]"); - const count = document.querySelector("[data-patience-count]"); - if (target && count) { - let seconds = 0; - setInterval(() => { - seconds += 1; - count.textContent = String(seconds); - if ([30, 90, 180].includes(seconds)) { - target.textContent = seconds === 30 ? "lima's note becomes easier to read." : seconds === 90 ? "sensei chi nods once." : "future z says this quiet was not wasted."; - } - }, 1000); - } - } - if (path.includes("/play/terminal-cupboard")) { - const input = document.querySelector("[data-cupboard-input]"); - const log = document.querySelector("[data-cupboard-log]"); - const commands = { - help: "commands: lima, aphy, sensei, young, future, layer, tea, exit", - lima: "lima note: make the hidden parts kind enough to live with.", - aphy: "aphy: cupboard process active. Wisdom module borrowed from sensei chi.", - sensei: "sensei chi: a cupboard teaches by containing and releasing.", - young: "young z inventory: crayon sun, blanket cape, important stone.", - future: "future z: you will still open small doors at 40.", - layer: familyLayers.join("\n"), - tea: "lima approves. sensei chi waits. aphy logs steam as temporary cloud.", - exit: "The cupboard lets you leave with nothing heavy." - }; - input?.addEventListener("keydown", (event) => { - if (event.key !== "Enter") return; - const value = input.value.trim().toLowerCase(); - const line = document.createElement("p"); - line.textContent = `> ${value}\n${commands[value] || "aphy: unknown command. sensei chi: not all unknowns are errors."}`; - log.appendChild(line); - input.value = ""; - }); - } - } - - function addInvisibleHoverSecrets() { - document.querySelectorAll("h1, h2").forEach((heading, index) => { - if (index > 5) return; - heading.classList.add("hidden-hover-memory"); - heading.dataset.hiddenMemory = pick([...lore.quotes, ...lore.journals, ...poems], `heading-${index}`); - }); - } - - function playTinySong() { - try { - const AudioContext = window.AudioContext || window.webkitAudioContext; - if (!AudioContext) return; - const ctx = new AudioContext(); - const notes = [392, 494, 440, 330, 392]; - notes.forEach((freq, index) => { - const osc = ctx.createOscillator(); - const gain = ctx.createGain(); - osc.frequency.value = freq; - osc.type = "sine"; - gain.gain.setValueAtTime(0.0001, ctx.currentTime + index * 0.18); - gain.gain.exponentialRampToValueAtTime(0.05, ctx.currentTime + index * 0.18 + 0.03); - gain.gain.exponentialRampToValueAtTime(0.0001, ctx.currentTime + index * 0.18 + 0.16); - osc.connect(gain); - gain.connect(ctx.destination); - osc.start(ctx.currentTime + index * 0.18); - osc.stop(ctx.currentTime + index * 0.18 + 0.18); - }); - } catch (_) {} - } - - function escapeHtml(value) { - return String(value).replace(/[&<>"']/g, (char) => ({ - "&": "&", - "<": "<", - ">": ">", - '"': """, - "'": "'" - }[char])); - } - - // Features run in this order on every page after the DOM is ready. - // Each function receives the current `memory` object. - const FEATURES = [ - addFooterNote, - addHomepageGreeting, - addWhispers, - addCornerObject, - addLogoSearchAndKeyboardSecrets, - addRareEvents, - addSecretLinks, - enhanceErrors, - addContinuity, - addSeasonAndDates, - addObjectConstellation, - addWeatherWindow, - addOldWebLayer, - addSourceRelics, - addLongKeyboardSecrets, - addPatienceRewards, - addRareSecondLayer, - addFamilyLayerIndex, - addPageSpecificSecrets, - addInvisibleHoverSecrets - ]; - - document.addEventListener("DOMContentLoaded", () => { - const memory = rememberVisit(); - FEATURES.forEach((addFeature) => addFeature(memory)); - }); -}());