Humuhumu33 commited on
Commit
6eeb282
·
verified ·
1 Parent(s): 94b5504

retire-provenance: superseded Q b/ objects (sha256 CAS, HOLO-LEAN-RESOLVER P1.3)

Browse files
sha256/57e17a233e6239acabf7976b3f296bc3966c684242c0beb3947b5212986577d8 ADDED
The diff for this file is too large to render. See raw diff
 
sha256/a75f6bd307e60199f5fd8b87fcd349124e98842905acfe4e11232a400b8e427c ADDED
@@ -0,0 +1 @@
 
 
1
+ {"@context":["https://www.w3.org/ns/did/v1","https://w3id.org/security/data-integrity/v2",{"dcterms":"http://purl.org/dc/terms/","links":{"@container":"@set","@id":"schema:hasPart"},"prov":"http://www.w3.org/ns/prov#","rel":"schema:additionalType","schema":"https://schema.org/"},{"holstr":"https://hologram.os/ns/strand#"}],"@type":["prov:Entity","holstr:Entry"],"holstr:kind":"release","holstr:op":"did:holo:sha256:87599792c7b13fad9d6c57d3b89d2cef35e4715869f5cde597ab60ca92699f0b","holstr:payload":{"apps":{"carry":"9f44164b17e6b7e37f39623bb445d49b6608da5f2b04cef8b1a8b183362a6b9a","hello":"66c1ada5ac08a2d4f73851bbe7458ad5fb6e2226c5a131c3829abfe715900fed","holo-messenger":"8b9b5f882c85455bf29325bddf30142c1a0164b3b9f856815cb07e31df9a1863","resolve":"23aec66382775f5773693615338f37c37a5f58257ec95aece33a664baa6fe0db","self":"0511772584510f0888d97dd5a72bf986ad6791bd8e305063ddd8a82c39888a70","spaces":"d8aebefb67e7fc7802e757b9a7d95bb881e6d75b4a9bc4bd4227634541757e51","yourspace":"c224b1941a746ba15fc62cd82299cc66751b5b4b3d8148972fa9b18b34792cfc"},"boot":{"entry":"holo-portal.mjs","sha256":"caeb3e57b6eeab09bee6d67c022145a8e2dae610c929a7f51bc169898625e7e8"},"closure":"c27676b9ede1d345a3bfb5ef6c214de4b9408975cb0526783912a42b01829652","manifest":"a20831a879e5c9e364b56821e793ddc9e01f441ad7c0f1dc4fe06772b8175ac2","runtime":{"apps/q/pkg/holospaces_web.js":"b9f8a887de588c5193dc65ca9ce206a70c6df53595b848e0ce0299c2d88a5753","apps/q/pkg/holospaces_web_bg.wasm":"9d90b4ea8bccee8c239073c7ec7dd5158c6d11f1f668487570d17475d79e661e","apps/workspace/pkg/holospaces_web.js":"8c30aa5582dffe4548d27fa7863163eba689ed65126be472c003f5884fc8e534","usr/lib/holo/holowhat/holospaces_web.js":"a5b5d9c559a1d2812c33e9d7a4b1c24bb03059482503a23356e4c9c32d4d8b88","usr/lib/holo/holowhat/holospaces_web_bg.wasm":"00cfa4960b319ba1ba7291cddb20dea130a407ce8b42426218854f4f68f4dcd7","usr/lib/pkg/holospaces_web.js":"8c30aa5582dffe4548d27fa7863163eba689ed65126be472c003f5884fc8e534","usr/lib/pkg/holospaces_web_bg.wasm":"3370b22070b56218a57df6406e2e4ba7cc171e942d4d1f2ae4297ea2c4c08d9c"},"shell":"815ba7c08262334c6be7c4c7867348593ecddef37819715047feb99b999c7e67"},"holstr:prev":"did:holo:sha256:c01d6fc97a16c7657ea3bb9765a9e63d990705feb41809a264222558d3c20455","holstr:seq":225,"prov:generatedAtTime":"2026-07-13T13:01:08.563Z"}
sha256/c224b1941a746ba15fc62cd82299cc66751b5b4b3d8148972fa9b18b34792cfc ADDED
@@ -0,0 +1,419 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
6
+ <title>Your Space — HOLOGRAM</title>
7
+ <meta name="description" content="A whole world in one link. Tap to step inside — then make it yours in one tap. No signup, no app, no server." />
8
+ <meta property="og:title" content="Your Space — HOLOGRAM" />
9
+ <meta property="og:description" content="A whole world in one link. Step inside, then make it yours in one tap." />
10
+ <meta property="og:type" content="website" />
11
+ <meta name="twitter:card" content="summary" />
12
+ <style>
13
+ /* ── YOUR SPACE — familiar profile skin (MAYA), radical substrate underneath. ─────────
14
+ One column, mobile-first, zero framework: first paint is pure CSS + the pre-paint
15
+ script below (boot-ground pattern — the wallpaper and name land BEFORE the module). */
16
+ :root {
17
+ color-scheme: dark;
18
+ --accent: #7c6cf0;
19
+ --ink: #eceaf4; --dim: rgba(236,234,244,.62); --faint: rgba(236,234,244,.38);
20
+ --card: rgba(16,13,26,.58); --line: rgba(255,255,255,.11);
21
+ --r: 10px; --rl: 16px; --pill: 999px;
22
+ --sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
23
+ --mono: ui-monospace, "Cascadia Code", Menlo, Consolas, monospace;
24
+ }
25
+ * { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
26
+ html, body { min-height: 100%; margin: 0; }
27
+ body { color: var(--ink); font: 1rem/1.55 var(--sans); background: #060410; overflow-x: hidden; }
28
+
29
+ /* ── wallpaper: full-bleed, mood-keyed pure-CSS presets (fast, zero assets) ─────────── */
30
+ #wall { position: fixed; inset: 0; z-index: 0; background: #060410 center/cover no-repeat; transition: opacity .8s ease; opacity: 0; }
31
+ #wall.on { opacity: 1; }
32
+ #wall::after { content: ""; position: absolute; inset: 0; background: linear-gradient(180deg, rgba(4,2,10,.25) 0%, rgba(4,2,10,.55) 68%, rgba(4,2,10,.82) 100%); }
33
+ .wp-cosmic { background-image: radial-gradient(1px 1px at 12% 22%, rgba(255,255,255,.9) 50%, transparent 51%), radial-gradient(1px 1px at 78% 12%, rgba(255,255,255,.7) 50%, transparent 51%), radial-gradient(1.5px 1.5px at 62% 70%, rgba(255,255,255,.8) 50%, transparent 51%), radial-gradient(1px 1px at 30% 82%, rgba(255,255,255,.6) 50%, transparent 51%), radial-gradient(1px 1px at 88% 52%, rgba(255,255,255,.75) 50%, transparent 51%), radial-gradient(ellipse 90% 60% at 50% 30%, #241a4d 0%, #120c28 55%, #05030d 100%) !important; }
34
+ .wp-vaporwave { background-image: linear-gradient(160deg, #ff71ce22 0%, transparent 40%), linear-gradient(200deg, #01cdfe22 10%, transparent 55%), linear-gradient(180deg, #2b1055 0%, #7597de44 140%), linear-gradient(180deg, #1a0b33 0%, #0d0619 100%) !important; }
35
+ .wp-cozy { background-image: radial-gradient(ellipse 100% 80% at 50% 20%, #4a2e1a 0%, #2a1810 55%, #120a06 100%) !important; }
36
+ .wp-neon { background-image: radial-gradient(ellipse 60% 40% at 20% 30%, #16f7a422 0%, transparent 70%), radial-gradient(ellipse 60% 40% at 80% 70%, #f716c922 0%, transparent 70%), linear-gradient(#07060d, #07060d) !important; }
37
+ .wp-noir { background-image: radial-gradient(ellipse 120% 90% at 50% 0%, #1c1c20 0%, #0c0c0e 60%, #050506 100%) !important; }
38
+ .wp-sunset { background-image: linear-gradient(180deg, #3d1635 0%, #7a2b3d 40%, #c96f3f66 78%, #1a0d18 100%) !important; }
39
+ .wp-forest { background-image: radial-gradient(ellipse 110% 80% at 50% 10%, #14351f 0%, #0a1f12 55%, #04100a 100%) !important; }
40
+ .wp-ocean { background-image: radial-gradient(ellipse 110% 80% at 50% 100%, #0a3a4a 0%, #072433 55%, #030d16 100%) !important; }
41
+ .wp-chaos { background-image: conic-gradient(from 210deg at 50% 40%, #2b1055, #4a1a3d, #1a3a4a, #3d2b10, #2b1055) !important; }
42
+
43
+ /* ── the page: one centred column, a profile everyone already knows how to read ─────── */
44
+ #page { position: relative; z-index: 1; max-width: 560px; margin: 0 auto; padding: max(4.5vh, 2rem) 1.1rem 7.5rem; animation: rise .5s cubic-bezier(.2,.8,.2,1); }
45
+ @keyframes rise { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
46
+
47
+ /* via-lineage credit — the road back up the fork chain */
48
+ #via { display: none; align-items: center; gap: .4rem; font-size: .82rem; color: var(--faint); margin-bottom: 1rem; }
49
+ #via.on { display: inline-flex; } #via a { color: var(--dim); cursor: pointer; text-decoration: none; border-bottom: 1px dotted var(--faint); }
50
+
51
+ /* head: avatar · name · bio · mood + κ */
52
+ #avatar { width: 104px; height: 104px; border-radius: 28%; display: grid; place-items: center; font-size: 3.2rem; user-select: none;
53
+ background: color-mix(in srgb, var(--accent) 20%, rgba(255,255,255,.04)); border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--line));
54
+ box-shadow: 0 8px 40px -8px color-mix(in srgb, var(--accent) 55%, transparent); overflow: hidden; }
55
+ #avatar img { width: 100%; height: 100%; object-fit: cover; }
56
+ #name { font-size: 2rem; font-weight: 800; letter-spacing: -.02em; margin: 1rem 0 .1rem; min-height: 1.2em; outline: none; }
57
+ #bio { color: var(--dim); margin: 0 0 .8rem; white-space: pre-wrap; outline: none; }
58
+ #name:empty::before, #bio:empty::before { content: attr(data-ph); color: var(--faint); }
59
+ .chips { display: flex; flex-wrap: wrap; gap: .45rem; align-items: center; }
60
+ .chip { display: inline-flex; align-items: center; gap: .4rem; font-size: .8rem; padding: .28rem .7rem; border-radius: var(--pill);
61
+ border: 1px solid var(--line); color: var(--dim); background: var(--card); backdrop-filter: blur(8px); }
62
+ #kap { font-family: var(--mono); cursor: pointer; max-width: 15ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
63
+ #kap.ok { color: #34d399; border-color: #34d39966; }
64
+
65
+ /* sections read like the page everyone grew up on: song, top friends, wall */
66
+ .sec { margin-top: 1.6rem; }
67
+ .sec h2 { font-size: .78rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--faint); margin: 0 0 .6rem; }
68
+ .card { background: var(--card); border: 1px solid var(--line); border-radius: var(--rl); backdrop-filter: blur(12px); }
69
+
70
+ /* the profile song — MySpace's most-loved feature, reborn as a κ-findable track */
71
+ #song { display: none; align-items: center; gap: .8rem; padding: .7rem .8rem; cursor: pointer; }
72
+ #song.on { display: flex; }
73
+ #songart { width: 52px; height: 52px; border-radius: var(--r); background: color-mix(in srgb, var(--accent) 25%, #111) center/cover; display: grid; place-items: center; font-size: 1.4rem; flex: none; }
74
+ #songmeta { min-width: 0; flex: 1; } #songmeta b { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
75
+ #songmeta span { font-size: .84rem; color: var(--dim); }
76
+ #songbtn { width: 44px; height: 44px; flex: none; border-radius: 50%; border: 0; cursor: pointer; font-size: 1.05rem; color: #fff;
77
+ background: var(--accent); display: grid; place-items: center; box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 60%, transparent); animation: pulse 2.4s infinite; }
78
+ @keyframes pulse { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 45%, transparent); } 70% { box-shadow: 0 0 0 14px transparent; } 100% { box-shadow: 0 0 0 0 transparent; } }
79
+ #song.playing #songbtn { animation: none; }
80
+
81
+ /* Top 8 — doors into other worlds */
82
+ #top { display: none; grid-template-columns: repeat(4, 1fr); gap: .55rem; }
83
+ #top.on { display: grid; }
84
+ .door { display: flex; flex-direction: column; align-items: center; gap: .35rem; padding: .7rem .3rem .55rem; text-align: center; cursor: pointer;
85
+ background: var(--card); border: 1px solid var(--line); border-radius: var(--rl); backdrop-filter: blur(8px); position: relative;
86
+ transition: transform .15s cubic-bezier(.2,.8,.2,1), border-color .15s; }
87
+ .door:hover { transform: translateY(-2px); border-color: color-mix(in srgb, var(--accent) 55%, var(--line)); }
88
+ .door .face { width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center; font-size: 1.25rem; font-weight: 700;
89
+ background: color-mix(in srgb, var(--accent) 18%, rgba(255,255,255,.05)); border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line)); }
90
+ .door small { font-size: .72rem; color: var(--dim); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
91
+ .door .rm { display: none; position: absolute; top: -7px; right: -7px; width: 22px; height: 22px; border-radius: 50%; border: 1px solid var(--line);
92
+ background: #1c1626; color: var(--dim); font-size: .8rem; line-height: 1; cursor: pointer; place-items: center; }
93
+ body.edit .door .rm { display: grid; }
94
+ .door.add { border-style: dashed; color: var(--faint); display: none; }
95
+ body.edit .door.add { display: flex; }
96
+
97
+ #wallbtn { display: none; width: 100%; padding: .8rem; text-align: center; cursor: pointer; color: var(--ink); font-weight: 600; }
98
+ #wallbtn.on { display: block; }
99
+
100
+ /* ── the sticky CTA — the whole viral loop is these two buttons ──────────────────────── */
101
+ #cta { position: fixed; z-index: 5; left: 0; right: 0; bottom: 0; padding: .8rem 1.1rem calc(.8rem + env(safe-area-inset-bottom));
102
+ display: flex; gap: .6rem; justify-content: center; background: linear-gradient(transparent, rgba(4,2,10,.88) 40%); }
103
+ .btn { flex: 1; max-width: 270px; padding: .85rem 1.1rem; font: 600 1rem var(--sans); border-radius: 14px; border: 1px solid var(--line);
104
+ color: var(--ink); background: rgba(22,17,34,.85); backdrop-filter: blur(12px); cursor: pointer; transition: transform .12s, filter .12s; }
105
+ .btn:active { transform: scale(.97); }
106
+ .btn.go { background: var(--accent); border-color: transparent; color: #fff; box-shadow: 0 6px 30px -6px color-mix(in srgb, var(--accent) 70%, transparent); }
107
+ .btn.go:hover { filter: brightness(1.08); }
108
+
109
+ /* ── EDIT — Playground law: every element is editable directly, in place ─────────────── */
110
+ body.edit [data-edit] { outline: 1.5px dashed color-mix(in srgb, var(--accent) 55%, transparent); outline-offset: 4px; border-radius: 6px; cursor: text; }
111
+ body.edit #avatar, body.edit .door.add, body.edit #songpick { cursor: pointer; }
112
+ .row { display: none; flex-wrap: wrap; gap: .45rem; margin-top: .7rem; }
113
+ body.edit .row { display: flex; }
114
+ .sw { width: 30px; height: 30px; border-radius: 50%; border: 2px solid transparent; cursor: pointer; }
115
+ .sw.on { border-color: #fff; }
116
+ .mood { font-size: .8rem; padding: .3rem .75rem; border-radius: var(--pill); border: 1px solid var(--line); background: var(--card); color: var(--dim); cursor: pointer; }
117
+ .mood.on { color: #fff; background: color-mix(in srgb, var(--accent) 45%, transparent); border-color: transparent; }
118
+ #picker { display: none; position: fixed; z-index: 9; left: 50%; bottom: 90px; transform: translateX(-50%); width: min(92vw, 380px);
119
+ padding: .8rem; border-radius: var(--rl); background: #171126; border: 1px solid var(--line); box-shadow: 0 20px 60px rgba(0,0,0,.6); }
120
+ #picker.on { display: block; }
121
+ #picker .grid { display: grid; grid-template-columns: repeat(8, 1fr); gap: .3rem; }
122
+ #picker .grid button { font-size: 1.35rem; padding: .3rem 0; background: none; border: 0; border-radius: 8px; cursor: pointer; }
123
+ #picker .grid button:hover { background: rgba(255,255,255,.08); }
124
+ #picker input, #srch input, #doorform input { width: 100%; margin-top: .55rem; padding: .6rem .7rem; font: inherit; color: var(--ink);
125
+ background: rgba(255,255,255,.05); border: 1px solid var(--line); border-radius: var(--r); outline: none; }
126
+ #srch { display: none; padding: .8rem; } body.edit #srch { display: block; }
127
+ #srch .res { display: flex; align-items: center; gap: .6rem; padding: .45rem .3rem; cursor: pointer; border-radius: var(--r); }
128
+ #srch .res:hover { background: rgba(255,255,255,.06); }
129
+ #srch .res img { width: 38px; height: 38px; border-radius: 8px; }
130
+ #srch .res small { color: var(--dim); }
131
+ #doorform { display: none; padding: .8rem; } #doorform.on { display: block; }
132
+
133
+ /* toast + hint */
134
+ #toast { position: fixed; z-index: 10; left: 50%; bottom: 96px; transform: translateX(-50%) translateY(20px); opacity: 0; pointer-events: none;
135
+ background: #171126; border: 1px solid var(--line); color: var(--ink); padding: .6rem 1rem; border-radius: var(--pill); font-size: .9rem;
136
+ transition: all .25s; max-width: 88vw; text-align: center; }
137
+ #toast.on { opacity: 1; transform: translateX(-50%); }
138
+ #hint { display: none; margin: 0 0 1.2rem; padding: .7rem .9rem; font-size: .9rem; border-radius: var(--rl);
139
+ background: color-mix(in srgb, var(--accent) 14%, rgba(10,7,20,.6)); border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--line)); }
140
+ #hint.on { display: block; }
141
+ footer { position: relative; z-index: 1; text-align: center; padding: 0 1rem 8.5rem; color: var(--faint); font-size: .78rem; }
142
+ footer a { color: var(--dim); cursor: pointer; text-decoration: none; border-bottom: 1px dotted var(--faint); }
143
+ @media (min-width: 700px) { #page { padding-top: 9vh; } }
144
+ </style>
145
+ </head>
146
+ <body>
147
+ <div id="wall"></div>
148
+ <main id="page">
149
+ <div id="hint"></div>
150
+ <span id="via">forked via <a id="vialink"></a></span>
151
+ <div id="avatar" data-edit-tap>🪐</div>
152
+ <h1 id="name" data-ph="Name your space"></h1>
153
+ <p id="bio" data-ph="Say something. This is your corner of the universe."></p>
154
+ <div class="chips">
155
+ <span class="chip" id="moodchip" style="display:none"></span>
156
+ <span class="chip" id="kap" title="This space's content address — tap to verify + copy">κ…</span>
157
+ </div>
158
+ <div class="row" id="accents"></div>
159
+ <div class="row" id="moods"></div>
160
+
161
+ <section class="sec" id="songsec">
162
+ <h2>Profile song</h2>
163
+ <div class="card" id="song">
164
+ <div id="songart">♪</div>
165
+ <div id="songmeta"><b id="songt"></b><span id="songa"></span></div>
166
+ <button id="songbtn" aria-label="Play">▶</button>
167
+ </div>
168
+ <div class="card" id="srch">
169
+ <input id="srchin" placeholder="Search any song — this plays when someone enters" autocomplete="off" />
170
+ <div id="srchres"></div>
171
+ </div>
172
+ </section>
173
+
174
+ <section class="sec">
175
+ <h2 id="toph">Top 8</h2>
176
+ <div id="top"></div>
177
+ <div class="card" id="doorform">
178
+ <input id="doorlink" placeholder="Paste a friend's space link (or any link)" autocomplete="off" />
179
+ <input id="doorname" placeholder="Name this door" autocomplete="off" />
180
+ <div class="row" style="display:flex;margin-top:.6rem"><button class="btn go" id="dooradd" style="max-width:none">Add door</button></div>
181
+ </div>
182
+ </section>
183
+
184
+ <section class="sec">
185
+ <div class="card"><div id="wallbtn">💬 Leave a note on my wall</div></div>
186
+ </section>
187
+ </main>
188
+ <footer>
189
+ your space is one link — no account, no app, no server · <a id="newspace">start a blank space</a><br/>
190
+ <span style="opacity:.7">HOLOGRAM · the κ re-derives from the link itself</span>
191
+ </footer>
192
+
193
+ <div id="cta"></div>
194
+ <div id="picker"><div class="grid" id="emojis"></div><input id="avurl" placeholder="…or paste an image URL" autocomplete="off" /></div>
195
+ <div id="toast"></div>
196
+ <audio id="audio"></audio>
197
+
198
+ <script>
199
+ /* pre-paint (boot-ground): land wallpaper + name from the link BEFORE the module loads,
200
+ so the teleport chorus is identical and instant on every click. Decode only — no hash,
201
+ no trust decisions here; the module re-derives κ and owns all behaviour. */
202
+ (function () {
203
+ try {
204
+ var m = location.hash.match(/#p=v2\.([A-Za-z0-9_-]+)/);
205
+ var p = m ? JSON.parse(atob(m[1].replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(m[1].length / 4) * 4, "="))) : null;
206
+ if (p && p.kind === "profile") {
207
+ if (p.accent) document.documentElement.style.setProperty("--accent", p.accent);
208
+ var w = document.getElementById("wall"), wp = String(p.wallpaper || "");
209
+ if (/^https:\/\//.test(wp)) w.style.backgroundImage = "url('" + wp.replace(/'/g, "%27") + "')";
210
+ else w.className = "wp-" + ((wp.match(/^preset:(\w+)/) || [])[1] || p.mood || "cosmic");
211
+ document.getElementById("name").textContent = p.name || "";
212
+ } else { document.getElementById("wall").className = "wp-cosmic"; }
213
+ } catch (e) { document.getElementById("wall").className = "wp-cosmic"; }
214
+ requestAnimationFrame(function () { document.getElementById("wall").classList.add("on"); });
215
+ })();
216
+ </script>
217
+
218
+ <script type="module">
219
+ import * as YS from "./holo-yourspace.mjs";
220
+
221
+ /* ── Q's Space — the Tom Anderson seed: everyone's first friend, everyone's first fork ── */
222
+ const OS_HOME = "https://hologram-technologies.github.io/Q";
223
+ const SEED = YS.identity({
224
+ name: "Q", bio: "I live inside HOLOGRAM — a whole operating system that unfolds from one link.\nThis page is my space. Make yours in one tap. I'll be your first friend.",
225
+ avatar: "🪐", accent: "#7c6cf0", mood: "cosmic", wallpaper: "preset:cosmic",
226
+ song: { title: "Space Oddity", artist: "David Bowie", query: "david bowie space oddity", art: "", preview: "" },
227
+ top: [{ name: "Q", link: OS_HOME }, { name: "HOLOGRAM", link: "https://hologram-technologies.github.io/" }],
228
+ wall: "", via: null,
229
+ });
230
+
231
+ const $ = (id) => document.getElementById(id);
232
+ const ME_KEY = "holo-yourspace.me.v2";
233
+ const ACCENTS = ["#7c6cf0", "#2dd4bf", "#f472b6", "#f59e0b", "#22c55e", "#38bdf8", "#ef4444", "#e2e8f0"];
234
+ const EMOJIS = ["🙂","😎","🛰️","🪐","🌸","🔥","🌊","🌙","⚡","🎧","🎮","🐉","🦋","🌵","👾","✨"];
235
+ let doc = null, mode = "view", myLink = "", curLink = "", curKappa = "";
236
+
237
+ const me = () => { try { return JSON.parse(localStorage.getItem(ME_KEY)); } catch (e) { return null; } };
238
+ const saveMe = (d, l) => localStorage.setItem(ME_KEY, JSON.stringify({ payload: YS.encode(d), link: l }));
239
+ const toast = (msg, ms = 2600) => { const t = $("toast"); t.textContent = msg; t.classList.add("on"); clearTimeout(t._h); t._h = setTimeout(() => t.classList.remove("on"), ms); };
240
+
241
+ /* ── boot: the hash IS the state. no hash → my space, or Q's seed for a cold stranger. ── */
242
+ function boot() {
243
+ const payload = YS.parseLink(location.hash);
244
+ if (payload) { const p = YS.decode(payload); if (p) return show(p, "view"); }
245
+ const m = me();
246
+ if (m && m.payload) { const p = YS.decode(m.payload); if (p) { history.replaceState(null, "", "#p=v2." + m.payload); return show(p, "view"); } }
247
+ history.replaceState(null, "", location.pathname + location.search);
248
+ show(SEED, "view", true);
249
+ }
250
+ addEventListener("hashchange", boot); // doors between spaces teleport in place — no reload
251
+
252
+ /* ── render — one function, view and edit are the same page (Playground law) ─────────── */
253
+ async function show(p, m, isSeed = false) {
254
+ doc = YS.identity(p); mode = m;
255
+ curLink = location.hash.includes("#p=v2.") ? location.origin + location.pathname + "#p=v2." + YS.encode(doc) : "";
256
+ document.body.classList.toggle("edit", mode === "edit");
257
+ document.documentElement.style.setProperty("--accent", doc.accent || "#7c6cf0");
258
+
259
+ // wallpaper (κ | https | preset | mood default) — pre-paint already landed it on cold load
260
+ const w = $("wall"); const wp = doc.wallpaper; const kx = YS.hexOf(wp);
261
+ w.style.backgroundImage = ""; w.className = "on";
262
+ if (kx) w.style.backgroundImage = `url('/.holo/blake3/${kx}')`;
263
+ else if (/^https:\/\//.test(wp)) w.style.backgroundImage = `url('${wp.replace(/'/g, "%27")}')`;
264
+ else w.classList.add("wp-" + ((wp.match(/^preset:(\w+)/) || [])[1] || doc.mood || "cosmic"));
265
+
266
+ // head
267
+ $("avatar").innerHTML = /^https:\/\//.test(doc.avatar) ? `<img src="${doc.avatar}" alt="">` : (doc.avatar || (mode === "edit" ? "+" : "🪐"));
268
+ $("name").textContent = doc.name; $("bio").textContent = doc.bio;
269
+ $("name").contentEditable = $("bio").contentEditable = mode === "edit" ? "plaintext-only" : "false";
270
+ $("name").toggleAttribute("data-edit", mode === "edit"); $("bio").toggleAttribute("data-edit", mode === "edit");
271
+ $("moodchip").style.display = doc.mood ? "" : "none"; $("moodchip").textContent = "· " + doc.mood + " ·";
272
+ $("via").classList.toggle("on", !!doc.via);
273
+ if (doc.via) { $("vialink").textContent = doc.via.name || "κ·" + doc.via.k.slice(0, 8); $("vialink").onclick = () => history.back(); }
274
+
275
+ // κ badge — re-derives live; tap = verify + copy (the trust signature)
276
+ $("kap").textContent = "κ…";
277
+ try { curKappa = await YS.kappa(doc); $("kap").textContent = "κ·" + YS.hexOf(curKappa).slice(0, 10); } catch (e) { $("kap").textContent = "κ offline"; }
278
+
279
+ // song
280
+ const s = doc.song; $("song").classList.toggle("on", !!s);
281
+ if (s) { $("songt").textContent = s.title; $("songa").textContent = s.artist;
282
+ $("songart").style.backgroundImage = s.art ? `url('${s.art}')` : ""; $("songart").textContent = s.art ? "" : "♪"; }
283
+
284
+ // top doors
285
+ const top = $("top"); top.innerHTML = ""; top.classList.toggle("on", doc.top.length > 0 || mode === "edit");
286
+ $("toph").textContent = doc.top.length ? `Top ${Math.min(doc.top.length, 8)}` : "Top 8";
287
+ doc.top.forEach((d, i) => {
288
+ const el = document.createElement("div"); el.className = "door";
289
+ const isSpace = !!YS.parseLink(d.link);
290
+ el.innerHTML = `<div class="face">${isSpace ? "🌐" : (d.name || "•")[0].toUpperCase()}</div><small>${d.name || "door"}</small><button class="rm" aria-label="remove">×</button>`;
291
+ el.onclick = (ev) => { if (ev.target.className === "rm") return commit(YS.removeDoor(doc, i));
292
+ if (isSpace || d.link.startsWith(location.origin)) location.assign(d.link); else window.open(d.link, "_blank", "noopener"); };
293
+ top.appendChild(el);
294
+ });
295
+ const add = document.createElement("div"); add.className = "door add"; add.innerHTML = `<div class="face">+</div><small>add door</small>`;
296
+ add.onclick = () => $("doorform").classList.toggle("on"); top.appendChild(add);
297
+
298
+ // wall
299
+ $("wallbtn").classList.toggle("on", !!doc.wall);
300
+ $("wallbtn").onclick = () => doc.wall && window.open(doc.wall, "_blank", "noopener");
301
+
302
+ // hint (the seed sells the fork; a fresh fork sells the share)
303
+ const hint = $("hint"); hint.classList.toggle("on", isSeed);
304
+ if (isSeed) hint.innerHTML = "👋 This is <b>Q's space</b> — a whole world living in one link. <b>Make it yours in one tap</b> ↓";
305
+
306
+ // CTA — the loop: visitor → [Make It Mine · Share] · owner → [Edit · Share] · edit → [Done]
307
+ const owner = !!(me() && me().payload === YS.encode(doc));
308
+ const cta = $("cta"); cta.innerHTML = "";
309
+ const btn = (label, cls, fn) => { const b = document.createElement("button"); b.className = "btn " + cls; b.textContent = label; b.onclick = fn; cta.appendChild(b); };
310
+ if (mode === "edit") btn("Done — mint my link", "go", finish);
311
+ else if (owner) { btn("Edit my space", "", () => show(doc, "edit")); btn("Share", "go", share); }
312
+ else { btn("✨ Make It Mine", "go", makeItMine); btn("Share", "", share); }
313
+
314
+ renderEditRows();
315
+ }
316
+
317
+ /* ── the viral gesture ────────────────────────────────────────────────────────────────── */
318
+ async function makeItMine() {
319
+ let mine;
320
+ try { mine = await YS.fork(doc, curLink || location.href); }
321
+ catch (e) { mine = YS.withFields(doc, { name: "", bio: "", avatar: "", wall: "", via: { k: "", name: doc.name } }); }
322
+ toast("It's yours. Decorate, then mint your link ✨");
323
+ show(mine, "edit");
324
+ }
325
+
326
+ function commit(next) { doc = next; show(doc, mode); } // every edit re-renders from the doc — the doc IS the page
327
+
328
+ async function finish() {
329
+ // pull the two contenteditables, mint, save, land on MY link, open the share sheet
330
+ doc = YS.withFields(doc, { name: $("name").textContent.trim(), bio: $("bio").textContent.trim() });
331
+ const payload = YS.encode(doc);
332
+ myLink = location.origin + location.pathname + "#p=v2." + payload;
333
+ saveMe(doc, myLink);
334
+ history.replaceState(null, "", "#p=v2." + payload);
335
+ await show(doc, "view");
336
+ toast("Minted. This link IS your space — post it anywhere.");
337
+ share();
338
+ }
339
+
340
+ async function share() {
341
+ const url = location.href;
342
+ const title = (doc.name ? doc.name + "'s" : "my") + " space";
343
+ try { if (navigator.share) return await navigator.share({ title, text: "step into " + title, url }); } catch (e) { if (e.name === "AbortError") return; }
344
+ try { await navigator.clipboard.writeText(url); toast("Link copied — drop it in any chat 💫"); }
345
+ catch (e) { prompt("Copy your space link:", url); }
346
+ }
347
+
348
+ /* ── edit affordances: accents · moods · avatar · song search · doors ─────────────────── */
349
+ function renderEditRows() {
350
+ const ac = $("accents"); ac.innerHTML = "";
351
+ ACCENTS.forEach((c) => { const b = document.createElement("button"); b.className = "sw" + (doc.accent === c ? " on" : "");
352
+ b.style.background = c; b.onclick = () => commit(YS.withFields(grab(), { accent: c })); ac.appendChild(b); });
353
+ const md = $("moods"); md.innerHTML = "";
354
+ YS.MOODS.forEach((m) => { const b = document.createElement("button"); b.className = "mood" + (doc.mood === m ? " on" : ""); b.textContent = m;
355
+ b.onclick = () => commit(YS.withFields(grab(), { mood: m, wallpaper: (!doc.wallpaper || doc.wallpaper.startsWith("preset:")) ? "preset:" + m : doc.wallpaper })); md.appendChild(b); });
356
+ }
357
+ const grab = () => YS.withFields(doc, { name: $("name").textContent.trim(), bio: $("bio").textContent.trim() }); // keep in-flight typing across re-renders
358
+
359
+ $("avatar").onclick = () => { if (mode !== "edit") return; const p = $("picker"); p.classList.toggle("on");
360
+ const g = $("emojis"); if (!g.childElementCount) EMOJIS.forEach((e) => { const b = document.createElement("button"); b.textContent = e;
361
+ b.onclick = () => { p.classList.remove("on"); commit(YS.withFields(grab(), { avatar: e })); }; g.appendChild(b); }); };
362
+ $("avurl").onchange = () => { $("picker").classList.remove("on"); commit(YS.withFields(grab(), { avatar: $("avurl").value.trim() })); };
363
+
364
+ // song search — client-direct iTunes (existing holo-imusic pattern), zero servers of ours
365
+ let srchT = null;
366
+ $("srchin").oninput = () => { clearTimeout(srchT); srchT = setTimeout(async () => {
367
+ const q = $("srchin").value.trim(); const box = $("srchres"); if (!q) { box.innerHTML = ""; return; }
368
+ try {
369
+ const r = await fetch(`https://itunes.apple.com/search?media=music&limit=5&term=${encodeURIComponent(q)}`);
370
+ const js = await r.json(); box.innerHTML = "";
371
+ (js.results || []).forEach((t) => { const d = document.createElement("div"); d.className = "res";
372
+ d.innerHTML = `<img src="${t.artworkUrl100 || ""}" alt=""><div><b>${t.trackName}</b><br/><small>${t.artistName}</small></div>`;
373
+ d.onclick = () => { commit(YS.withFields(grab(), { song: { title: t.trackName, artist: t.artistName, art: t.artworkUrl100 || "", query: `${t.artistName} ${t.trackName}`, preview: t.previewUrl || "" } })); toast("Song set 🎧"); };
374
+ box.appendChild(d); });
375
+ } catch (e) { box.innerHTML = `<small style="color:var(--dim)">search unreachable — paste on again later</small>`; }
376
+ }, 350); };
377
+
378
+ $("dooradd").onclick = () => { const l = $("doorlink").value.trim(), n = $("doorname").value.trim();
379
+ if (!l) return toast("Paste a link first");
380
+ commit(YS.addDoor(grab(), { name: n || "door", link: l })); $("doorlink").value = $("doorname").value = ""; $("doorform").classList.remove("on"); };
381
+
382
+ /* ── the song plays: in-OS full track via the holo-music-ctl bus, else the 30s preview ── */
383
+ $("song").onclick = async (ev) => {
384
+ if (ev.target.id === "songbtn" || ev.currentTarget.contains(ev.target)) {
385
+ const s = doc.song; if (!s) return;
386
+ const el = $("song"), a = $("audio");
387
+ if (el.classList.contains("playing")) { a.pause(); el.classList.remove("playing"); $("songbtn").textContent = "▶"; try { busCmd("pause"); } catch (e) {} return; }
388
+ if (await busCmd("play", [s.query || `${s.artist} ${s.title}`])) { el.classList.add("playing"); $("songbtn").textContent = "❚❚"; toast("Playing in Holo Music 🎶"); return; }
389
+ let src = s.preview;
390
+ if (!src && (s.query || s.title)) { try {
391
+ const r = await fetch(`https://itunes.apple.com/search?media=music&limit=1&term=${encodeURIComponent(s.query || s.artist + " " + s.title)}`);
392
+ src = ((await r.json()).results || [])[0]?.previewUrl || ""; } catch (e) {} }
393
+ if (!src) return toast("Song not reachable right now");
394
+ a.src = src; try { await a.play(); el.classList.add("playing"); $("songbtn").textContent = "❚❚"; } catch (e) { toast("Tap again to play"); }
395
+ a.onended = () => { el.classList.remove("playing"); $("songbtn").textContent = "▶"; };
396
+ }
397
+ };
398
+ function busCmd(cmd, args = []) { // same-origin Holo Music tab answers in ≤800ms, else fall through
399
+ return new Promise((res) => { let done = false;
400
+ try { const ch = new BroadcastChannel("holo-music-ctl"); const id = Math.random().toString(36).slice(2);
401
+ ch.onmessage = (e) => { if (e.data && e.data.re === id) { done = true; ch.close(); res(!!e.data.ok); } };
402
+ ch.postMessage({ id, cmd, args }); setTimeout(() => { if (!done) { ch.close(); res(false); } }, 800);
403
+ } catch (e) { res(false); } });
404
+ }
405
+
406
+ /* κ badge: tap → re-derive (L5) + copy. Green flash = the page you see IS the κ you share. */
407
+ $("kap").onclick = async () => { try {
408
+ const ok = await YS.verify(doc, curKappa);
409
+ $("kap").classList.toggle("ok", ok); setTimeout(() => $("kap").classList.remove("ok"), 1600);
410
+ await navigator.clipboard.writeText(curKappa); toast(ok ? "re-derived ✓ · κ copied" : "κ MISMATCH — refused");
411
+ } catch (e) { toast("κ unavailable here"); } };
412
+
413
+ $("newspace").onclick = () => show(YS.identity({ mood: "cosmic", wallpaper: "preset:cosmic", accent: "#7c6cf0" }), "edit");
414
+ document.addEventListener("click", (e) => { if (!$("picker").contains(e.target) && e.target !== $("avatar")) $("picker").classList.remove("on"); });
415
+
416
+ boot();
417
+ </script>
418
+ </body>
419
+ </html>