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 @@
- Press โช (top-right, or F2) to wake OMNI โ the whole audio graph builds on the first press.
-- 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).
+- 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.
- 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.
- 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.
- 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.
@@ -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); }