diff --git a/viz/index.html b/viz/index.html index 294d33f..fde35ba 100644 --- a/viz/index.html +++ b/viz/index.html @@ -669,6 +669,11 @@ background: rgba(20,28,44,0.82); color: rgba(160,180,215,0.75); border: 1px solid rgba(120,150,200,0.22); border-radius: 9px; padding: 4px 10px; font-size: 11px; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); } #sleepchip:hover { color: #cfe0ff; border-color: rgba(140,170,230,0.5); } + /* ๐Ÿƒ the deal โ€” the redeal chip sits above the sleeper chip */ + #dealchip { position: fixed; left: 14px; bottom: 86px; z-index: 12; display: none; cursor: pointer; + background: rgba(28,24,46,0.85); color: rgba(200,185,235,0.85); border: 1px solid rgba(160,140,220,0.3); + border-radius: 9px; padding: 4px 10px; font-size: 11px; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); } + #dealchip:hover { color: #e8ddff; border-color: rgba(180,160,240,0.6); }
โš™
@@ -1240,6 +1245,7 @@
  • ๐Ÿ•Š PSALM โ€” sing, and the instrument answers. Right-click the sky โ†’ ๐Ÿ•Š PSALM, allow the microphone, and sing. The pitch is heard, folded to the scale you're in (so you're never out of key), and played as notes โ€” a ๐Ÿ•Š chip shows the note leaving your throat. And the duet with โšก GODSPEED: with the arp running, each note you sing latches into the held chord, one at a time โ€” hum four notes and the machine arpeggiates the chord you sang. It listens locally: the analysis happens in the page, in its own silence, and nothing ever leaves the machine โ€” no recording, no upload, just your voice becoming the instrument's.
  • โœฆ CANON โ€” the shelf of worked examples. Open โš™ โ†’ DIMENSIONS and at the top of the list, above your own saved worlds, sit four โœฆ factory dimensions that ship with the instrument โ€” the heartbeat (a boom-bap kit with the swing and the kick's duck), the wheel within the wheel (WHEELS, made audible), the squeeze (the market pumping GODSQUASH), and quiet arrival (the earth echo, slow and sparse). Load one to hear a feature in its element, then save your own version under a new name โ€” the โœฆ ones are read-only, the same for everyone, a place to start rather than a thing to overwrite.
  • ๐Ÿช spheres โ€” the music of the spheres. Press S (or right-click the sky โ†’ ๐Ÿช spheres) and turn it on: the eight planets join the field as modulation sources, the slowest LFOs there are. Each planet offers three signals โ€” phase (its heliocentric longitude, a sawtooth one full turn per orbit), dist (its eccentric breathing, in and out between perihelion and aphelion โ€” Mercury and Mars swing hard, Venus barely), and beat (its synodic dance against Earth โ€” Venus and Earth clasp every 583.9 days, the pentagram cycle). Route any of them like any source, or just say "mars drive filter." One knob rules time: the rate, from 1 to 3650 days of sky per bar โ€” at the default thirty, Venus' year is about seven and a half bars and Jupiter's a hundred and forty-four, real orbits compressed into slow musical arcs. And the โ˜ธ orbital ratios button sets the drum lanes' wheels to the true resonances โ€” 4 : 8 : 16 (Io, Europa and Ganymede, locked at 1 : 2 : 4) and 8 : 13 (Venus to Earth, the pentagram in wheel form), so the polymeter you hear is one the solar system actually keeps. It is all computed here, at the instrument's own clock โ€” the same JPL ephemeris SOLARGOD uses, vendored in, no network and no server, so the sky is deterministic: the same bar always shows the same heaven, and a saved patch reloads the identical sky.
  • +
  • ๐Ÿšช The doors โ€” how the world opens. The front door is a skin like any other. On your first visit a card offers six doors, and the one you walk is remembered โ€” every arrival after that opens the same way (right-click the sky โ†’ ๐Ÿšช the doors to choose again, or say "doors"). The classics: ๐ŸŒฑ start from zero (an empty field, build it yourself), ๐ŸŒ… first light (a gentle arrangement), โ›ˆ the full storm (every hand at once). And the gig doors: ๐ŸŽช the rig โ€” you walk on stage with the beat panel and GODSPEED already open and the field folded to the eight liveliest hands, everything within reach; ๐Ÿƒ the deal โ€” the world deals you five cables from its liveliest feeds onto five voices, one card always pulsing, with a ๐Ÿƒ deal again chip (and โŒ˜Z takes any hand back) โ€” redeal until it sings, and every arrival deals fresh; ๐ŸŒ’ the dawn โ€” the players take the stage one at a time, liveliest first, the field assembling itself over half a minute like an orchestra walking on. Same instrument behind every door โ€” only the meeting changes.
  • โšก Pulse โ€” make the slow world playable. Some of the planet's truest signals barely move โ€” the national debt, the Moon's declination, a glacier of a currency. Right-click a node, open its cables, and tap the โšก chip on any cable to cycle รท16 ยท รท8 ยท รท4 ยท bar: the cable now throbs on the godtime clock, and the slow value becomes the depth of that throb. The debt stops being a frozen offset and becomes a sixteenth-note pressure whose weight is the debt itself โ€” slow data as character, the clock as motion. Off by default; the world's own tempo is still the truth unless you ask for the heartbeat.
  • ๐Ÿ’ค The sleeper fold โ€” and speed at a glance. The field folds away quiet feeds โ€” sources with no cables, no signal and nothing to say for a minute โ€” behind a small ๐Ÿ’ค chip (bottom-left) that shows the count and brings them back with one click. The moment a folded feed speaks, it wakes itself and rejoins the field: the world breathes in and out of view. And every cable now moves at the data's own rate โ€” streaming feeds race, glacial ones crawl โ€” with the words to match: hover a column and each source row reads its speed (streaming ยท โ‰ˆ every 3 bars ยท asleep, still 12m), and a node's right-click readout says the same. What speed is that thing going at? Now it just tells you.
  • ๐Ÿ“ฝ The stage โ€” send the visual to any screen. Right-click the sky โ†’ the stage and a clean black mirror window opens, fed live from the canvas. Drag it to a projector, an AirPlay display, a second monitor โ€” macOS treats it like any window โ€” and double-click for fullscreen. The stage has a camera: the whole view by default (whatever you're in โ€” a skin, the wheel, mind mode's POV), or right-click any node โ†’ project this node and the camera glides after that one sphere through its orbits, or project its station to frame a whole wheel and its satellites. Change cameras live from the menus while the projector runs; the audience sees the camera breathe from subject to subject.
  • @@ -1299,7 +1305,7 @@
    1. Press โ™ช (top-right, or F2) to wake OMNI โ€” the whole audio graph builds on the first press.
    2. -
    3. Your first visit opens gently in ๐ŸŒ… first light. Right-click the sky โ†’ ๐ŸŒ— mood โ€” how much world to move between first light, ambient drift (all atmosphere, no drums) and the full storm (every hand at once).
    4. +
    5. Your first visit offers ๐Ÿšช six doors โ€” from start from zero to the rig, the deal and the dawn โ€” and remembers the one you walk. Right-click the sky โ†’ ๐ŸŒ— mood โ€” how much world to move between first light, ambient drift (all atmosphere, no drums) and the full storm (every hand at once), or ๐Ÿšช the doors to change how the world opens.
    6. Play notes three ways: enter chakra view (C) and click a wheel's core for a 12-key console (play it with the Qโ€ฆP row or a MIDI keyboard); open ๐ŸŽ› tracks (T) to sequence a song; or just wire the world's data to play it hands-free.
    7. Shape the character: swap the pluck voice (๐ŸŽธ string voice / ๐ŸŽ› GODSONIQ), or drag any feed onto crush, wavetable.morph or granular.density.
    @@ -1358,7 +1364,7 @@

    Building from zero โ€” the empty instrument

    -

    The full instrument is a sensory flood by design. Zero is the other door: an empty field with nothing wired, where you build the whole thing by hand, one cable at a time. Same matrix underneath โ€” a calmer face on it. On your first visit the three-door card offers it (๐ŸŒฑ start from zero); after that, press F11, or โš™ โ†’ DIMENSIONS โ†’ โœฆ dimension zero, or the ๐ŸŒ— mood menu โ†’ 0 zero.

    +

    The full instrument is a sensory flood by design. Zero is the other door: an empty field with nothing wired, where you build the whole thing by hand, one cable at a time. Same matrix underneath โ€” a calmer face on it. On your first visit the doors card offers it (๐ŸŒฑ start from zero); after that, press F11, or โš™ โ†’ DIMENSIONS โ†’ โœฆ dimension zero, the ๐ŸŒ— mood menu โ†’ 0 zero, or ๐Ÿšช the doors.

    1. Down each side sit seven vibe-rings โ€” the world grouped into the seven elements (left) and seven domains (right). Each ring breathes with its group's live average. Click one to open its drawer.
    2. @@ -3978,9 +3984,107 @@ document.body.appendChild(wrap); } - // The three doors, shown once on a first-ever visit (replaces the silent drop - // into first light). Backdrop/esc = first light, the gentle default. - function showFirstVisitCard() { + // ---- ๐Ÿšช THE DOORS โ€” how the world opens ----------------------------------- + // The front door is a skin like any other: six openings, each a different way + // to meet the same instrument. Your door is remembered (gs_door) and walked + // again on every arrival; right-click the sky โ†’ ๐Ÿšช the doors to choose again. + // Each door is ONE registry entry + one act โ€” delete the entry, the door is gone. + let doorMode = null; // "rig" | "deal" | "dawn" while that door shapes the field (session-only) + let dawn = null; // the entrance in progress: { queue, next, t } + + function doorRig() { // ๐ŸŽช walk on stage: performance surfaces up, the field folded tight + doorMode = "rig"; + const _m = ungodly.muted; ungodly.muted = true; applyMood("first light"); ungodly.muted = _m; + sleepFold = true; + openBeat(); openGodspeed(); + eventTicker.unshift({ text: "๐ŸŽช the rig โ€” beat + GODSPEED up, the field folded to the liveliest hands. โ™ช to sound, S for the spheres.", tAdded: now(), src: "sys" }); + } + + // ๐Ÿƒ the deal's deck: dests worth being dealt, [key, amount, curve] + const DEAL_DESTS = [ + ["filter.cutoff", 0.6, "scurve"], ["pad.brightness", 0.5, "lin"], ["reverb.size", 0.45, "lin"], + ["delay.feedback", 0.4, "exp"], ["lfo.rate", 0.4, "lin"], ["drone.voices", 0.5, "lin"], + ["wavetable.morph", 0.45, "lin"], ["glitch", 0.25, "exp3"], ["master.space", 0.5, "scurve"], ["saturation", 0.35, "exp"]]; + let dealChipEl = null; + function dealChip(show) { + if (!dealChipEl) { + dealChipEl = document.createElement("div"); dealChipEl.id = "dealchip"; dealChipEl.textContent = "๐Ÿƒ deal again"; + dealChipEl.title = "the world deals a fresh hand โ€” five cables, one pulsing (โŒ˜Z takes it back)"; + dealChipEl.onclick = () => doorDeal(false); + document.body.appendChild(dealChipEl); + } + dealChipEl.style.display = show ? "block" : "none"; + } + function doorDeal(silent) { // ๐Ÿƒ the world deals a hand: five lively sources onto five voices + const pool = [...sources.values()].filter(n => !/^drum\./.test(n.key)); + pool.sort((a, b) => ((b.flowDisp || 0) * 2 + (b.norm || 0)) - ((a.flowDisp || 0) * 2 + (a.norm || 0))); + if (!pool.length) { eventTicker.unshift({ text: "๐Ÿƒ the world hasn't spoken yet โ€” try the deal again in a moment", tAdded: now(), src: "sys" }); return; } + doorMode = "deal"; + if (!silent) ungodlyMark("the deal"); + loadPatch({ routes: [], tweaks: {}, seqs: {} }); + seedBeat(); // a kit waits in the beat panel, off until you press โ–ถ + const picked = [], seenFam = {}; + for (const n of pool) { if (picked.length >= 5) break; if (seenFam[n.family]) continue; seenFam[n.family] = 1; picked.push(n); } + for (const n of pool) { if (picked.length >= 5) break; if (picked.indexOf(n) < 0) picked.push(n); } + const deck = DEAL_DESTS.slice(); + for (let i = deck.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); const sw = deck[i]; deck[i] = deck[j]; deck[j] = sw; } + const pulseCard = Math.floor(Math.random() * picked.length); // one card is always dealt pulsing + picked.forEach((n, i) => { + const d = deck[i]; + registerRoute({ source: n.key, dest: d[0], amount: d[1], curve: d[2], pulse: i === pulseCard ? (Math.random() < 0.5 ? 16 : 8) : 0 }); + }); + layoutDirty = true; + dealChip(true); + eventTicker.unshift({ text: "๐Ÿƒ the deal: " + picked.map(n => n.label || n.key).join(" ยท ") + " โ€” one card pulses. โ™ช to hear it.", tAdded: now(), src: "sys" }); + } + + function doorDawn() { // ๐ŸŒ’ the players take the stage one at a time, liveliest first + doorMode = "dawn"; + const _m = ungodly.muted; ungodly.muted = true; applyMood("first light"); ungodly.muted = _m; + const cabled = new Set(); + for (const r of routesArr) cabled.add(r.source); + const list = [...sources.values()]; + list.sort((a, b) => (cabled.has(b.key) - cabled.has(a.key)) || ((b.flowDisp || 0) - (a.flowDisp || 0))); + for (const n of list) n.hidden = true; + dawn = { queue: list.slice(0, 18), next: 0, t: 0 }; + layoutDirty = true; + eventTicker.unshift({ text: "๐ŸŒ’ the dawn โ€” the world takes the stage, one hand at a time.", tAdded: now(), src: "sys" }); + } + function dawnPass(t) { // one arrival per beat-and-a-bit; then the fold takes over + if (!dawn) return; + if (!dawn.t) dawn.t = t + 1.0; + if (t < dawn.t) return; + const n = dawn.queue[dawn.next++]; + if (n) { + n.hidden = false; layoutDirty = true; + eventTicker.unshift({ text: "๐ŸŒ’ " + (n.label || n.key) + " takes the stage", tAdded: now(), src: "sys" }); + } + dawn.t = t + 1.25; + if (dawn.next >= dawn.queue.length) { + dawn = null; // the entrance is over โ€” sleepFoldPass reconciles the rest + eventTicker.unshift({ text: "๐ŸŒ… the world is on stage.", tAdded: now(), src: "sys" }); + } + } + + const DOORS = [ + { key: "zero", emoji: "๐ŸŒฑ", title: "start from zero", desc: "an empty field. build the instrument yourself, one cable at a time.", act: () => enterZero() }, + { key: "light", emoji: "๐ŸŒ…", title: "first light", desc: "a gentle arrangement โ€” a bass line, soft hats, a small melody. ease in.", act: () => applyMood("first light") }, + { key: "storm", emoji: "โ›ˆ", title: "the full storm", desc: "every one of the world's hands on it at once. the factory instrument.", act: () => applyMood("the full storm") }, + { key: "rig", emoji: "๐ŸŽช", title: "the rig", desc: "walk on stage: beat + GODSPEED open, the field folded to the liveliest hands.", act: () => doorRig() }, + { key: "deal", emoji: "๐Ÿƒ", title: "the deal", desc: "the world deals you five cables โ€” one always pulsing. redeal until it sings.", act: () => doorDeal(false) }, + { key: "dawn", emoji: "๐ŸŒ’", title: "the dawn", desc: "the players take the stage one at a time, liveliest first. watch it assemble.", act: () => doorDawn() }, + ]; + function walkDoor(key, save) { + const d = DOORS.filter(x => x.key === key)[0]; + if (!d) return; + doorMode = null; dawn = null; dealChip(false); // a new door closes the old one's furniture + if (save) try { localStorage.setItem("gs_door", key); } catch (e) {} + d.act(); + } + + // The doors card โ€” first-ever visit, and any time from the sky menu. + // First visit: esc/backdrop = first light (the gentle default). Re-choosing: esc just closes. + function showDoorsCard(again) { if (document.getElementById("firstcard")) return; const wrap = document.createElement("div"); wrap.id = "firstcard"; @@ -3988,10 +4092,10 @@ alignItems: "center", justifyContent: "center", background: "rgba(6,10,20,0.72)", backdropFilter: "blur(8px)", WebkitBackdropFilter: "blur(8px)" }); const card = document.createElement("div"); - Object.assign(card.style, { maxWidth: "640px", width: "88%", textAlign: "center", + Object.assign(card.style, { maxWidth: "760px", width: "92%", textAlign: "center", color: "#d6e2f4", fontFamily: "inherit" }); const h = document.createElement("div"); - h.textContent = "one world, three doors"; + h.textContent = "one world, " + ["", "one", "two", "three", "four", "five", "six", "seven", "eight"][DOORS.length] + " doors"; Object.assign(h.style, { fontWeight: "800", fontSize: "clamp(22px,4vw,32px)", letterSpacing: "2px", marginBottom: "6px" }); const sub = document.createElement("div"); sub.textContent = "the planet is already playing. choose how you meet it."; @@ -3999,32 +4103,28 @@ card.appendChild(h); card.appendChild(sub); const row = document.createElement("div"); Object.assign(row.style, { display: "flex", gap: "14px", flexWrap: "wrap", justifyContent: "center" }); - const doors = [ - ["๐ŸŒฑ", "start from zero", "an empty field. build the instrument yourself, one cable at a time.", () => enterZero()], - ["๐ŸŒ…", "first light", "a gentle arrangement โ€” a bass line, soft hats, a small melody. ease in.", () => applyMood("first light")], - ["โ›ˆ", "the full storm", "every one of the world's hands on it at once. the factory instrument.", () => applyMood("the full storm")], - ]; - for (const [emoji, title, desc, act] of doors) { + for (const d of DOORS) { const b = document.createElement("div"); Object.assign(b.style, { flex: "1 1 170px", minWidth: "160px", maxWidth: "200px", cursor: "pointer", padding: "20px 16px", borderRadius: "16px", background: "rgba(20,28,44,0.7)", border: "1px solid rgba(120,150,200,0.28)", transition: "transform .12s, border-color .12s" }); b.onmouseenter = () => { b.style.transform = "translateY(-3px)"; b.style.borderColor = "rgba(150,200,255,0.7)"; }; b.onmouseleave = () => { b.style.transform = ""; b.style.borderColor = "rgba(120,150,200,0.28)"; }; - b.innerHTML = '
      ' + emoji + '
      ' - + '
      ' + title + '
      ' - + '
      ' + desc + '
      '; - b.onclick = () => { wrap.remove(); document.removeEventListener("keydown", onEsc, true); act(); }; + b.innerHTML = '
      ' + d.emoji + '
      ' + + '
      ' + d.title + '
      ' + + '
      ' + d.desc + '
      '; + b.onclick = () => { wrap.remove(); document.removeEventListener("keydown", onEsc, true); walkDoor(d.key, true); }; row.appendChild(b); } card.appendChild(row); const foot = document.createElement("div"); - foot.textContent = "you can switch any time โ€” right-click the sky โ†’ ๐ŸŒ— mood"; + foot.textContent = "your door is remembered โ€” right-click the sky โ†’ ๐Ÿšช the doors to choose again"; Object.assign(foot.style, { marginTop: "22px", fontSize: "11px", color: "rgba(150,170,205,0.55)" }); card.appendChild(foot); wrap.appendChild(card); - const onEsc = (e) => { if (e.key === "Escape") { wrap.remove(); document.removeEventListener("keydown", onEsc, true); applyMood("first light"); } }; - wrap.onclick = (e) => { if (e.target === wrap) { wrap.remove(); document.removeEventListener("keydown", onEsc, true); applyMood("first light"); } }; + const close = () => { wrap.remove(); document.removeEventListener("keydown", onEsc, true); if (!again) applyMood("first light"); }; + const onEsc = (e) => { if (e.key === "Escape") close(); }; + wrap.onclick = (e) => { if (e.target === wrap) close(); }; document.addEventListener("keydown", onEsc, true); document.body.appendChild(wrap); } @@ -4914,13 +5014,21 @@ if (sleepChipEl._t !== txt) { sleepChipEl._t = txt; sleepChipEl.textContent = txt; sleepChipEl.style.display = txt ? "block" : "none"; } } function sleepFoldPass(t) { + if (dawn) { sleepChip(0); return; } // ๐ŸŒ’ during the entrance, the dawn owns the field const canFold = !chakraMode && !zeroMode && connected; const cabled = new Set(); for (const r of routesArr) cabled.add(r.source); + let onStage = null; // ๐ŸŽช the rig folds harder: only the liveliest uncabled hands stay + if (canFold && doorMode === "rig") { + const flows = [...sources.values()].filter(n => !cabled.has(n.key)).map(n => n.flowDisp || 0).sort((a, b) => b - a); + onStage = flows.length > 8 ? Math.max(flows[7], 0.02) : 0; // the 8th-liveliest sets the bar (never below the quiet line) + } let slept = 0; for (const n of sources.values()) { - const asleep = canFold && !cabled.has(n.key) && !selection.has(n.key) && soloKey !== n.key && - (n.norm || 0) <= 0.02 && (t - (n._lastMoveT != null ? n._lastMoveT : -1e9)) > 45; + const quiet = onStage != null + ? (n.flowDisp || 0) < onStage + : (n.norm || 0) <= 0.02 && (t - (n._lastMoveT != null ? n._lastMoveT : -1e9)) > 45; + const asleep = canFold && !cabled.has(n.key) && !selection.has(n.key) && soloKey !== n.key && quiet; if (asleep) slept++; const hide = asleep && sleepFold; if (hide !== !!n.hidden) { n.hidden = hide; layoutDirty = true; } @@ -5190,6 +5298,8 @@ if (has("stop") || has("pause")) { godspeakBeat(false); godspeakSay("beat stopped"); return; } if (has("play") || has("start") || has("go")) { godspeakBeat(true); godspeakSay("beat playing"); return; } } + if (has("doors") || has("doorway")) { showDoorsCard(true); godspeakSay("๐Ÿšช the doors"); return; } + if (has("deal") && doorMode === "deal") { doorDeal(false); godspeakSay("๐Ÿƒ dealt"); return; } // "deal again" โ€” only while the deal is your door if (has("spheres") || has("planets")) { // ๐Ÿช RESONANCE โ€” the sky by voice if (has("off") || (has("stop") && !has("start"))) { spheresEnable(false); godspeakSay("spheres off"); return; } if (has("faster") || has("quicker")) { spheresSetRate(spheres.rate * 2); godspeakSay("spheres faster โ†’ " + spheres.rate + " d/bar"); return; } @@ -5222,13 +5332,18 @@ // clobber what you're playing. A reload no longer drops you into the wall. if (!moodAppliedThisLoad) { moodAppliedThisLoad = true; - let firstEver = false, saved = "first light"; + let firstEver = false, saved = "first light", door = null; try { firstEver = !localStorage.getItem("gs_seen"); } catch (e) {} try { saved = localStorage.getItem("gs_mood") || "first light"; } catch (e) {} + try { door = localStorage.getItem("gs_door"); } catch (e) {} setTimeout(() => { if (firstEver) { try { localStorage.setItem("gs_seen", "1"); } catch (e) {} - showFirstVisitCard(); // three doors: zero / first light / storm + showDoorsCard(false); // ๐Ÿšช the doors: zero / light / storm / rig / deal / dawn + } else if (door === "rig" || door === "dawn") { + walkDoor(door, false); // ๐Ÿšช walk your remembered door (its mood-mark is muted inside) + } else if (door === "deal") { + doorDeal(true); // a fresh hand every arrival โ€” silent, no undo mark for the load itself } else if (saved === "0 zero") { enterZero(true); // a returning zero-builder comes back to zero, build intact (C2) } else { @@ -5502,6 +5617,7 @@ } } + dawnPass(t); // ๐ŸŒ’ the entrance, if one is playing sleepFoldPass(t); // ๐Ÿ’ค quiet feeds fold; they wake themselves when the world speaks if (layoutDirty) doLayout(); spheresFrame(); // ๐Ÿช RESONANCE โ€” refresh the sphere sources on the great-year clock @@ -5588,7 +5704,7 @@ for (const r of routesArr) { const s = sources.get(r.source); const d = dests.get(r.dest); - if (!s || !d) continue; + if (!s || !d || s.hidden) continue; // ๐ŸŒ’ no wire to a player still in the wings const a = r.activeDisp; const va = Math.max(a, 0.10); // a patched cable NEVER vanishes: a silent source still shows its wire @@ -8256,6 +8372,7 @@ () => { beat.open ? closeBeat() : openBeat(); }, "B"); ctxItem("โช UNGODLY โ€” take it back" + (ungodly.past.length ? " (" + ungodly.past.length + ")" : ""), () => ungodlyUndo(), "โŒ˜Z"); + ctxItem("๐Ÿšช the doors โ€” how the world opens", () => showDoorsCard(true)); const moodSub = ctxSub("๐ŸŒ— mood โ€” how much world"); const z0 = document.createElement("div"); z0.className = "it"; z0.textContent = zeroMode ? "0 zero โ€” you're building it (a mood leaves)" : "0 zero โ€” build from nothing"; diff --git a/viz/manual.html b/viz/manual.html index e542449..d1e522c 100644 --- a/viz/manual.html +++ b/viz/manual.html @@ -673,6 +673,11 @@ background: rgba(20,28,44,0.82); color: rgba(160,180,215,0.75); border: 1px solid rgba(120,150,200,0.22); border-radius: 9px; padding: 4px 10px; font-size: 11px; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); } #sleepchip:hover { color: #cfe0ff; border-color: rgba(140,170,230,0.5); } + /* ๐Ÿƒ the deal โ€” the redeal chip sits above the sleeper chip */ + #dealchip { position: fixed; left: 14px; bottom: 86px; z-index: 12; display: none; cursor: pointer; + background: rgba(28,24,46,0.85); color: rgba(200,185,235,0.85); border: 1px solid rgba(160,140,220,0.3); + border-radius: 9px; padding: 4px 10px; font-size: 11px; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); } + #dealchip:hover { color: #e8ddff; border-color: rgba(180,160,240,0.6); }