Humuhumu33 commited on
Commit
b239858
·
verified ·
1 Parent(s): 7c2b87d

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

Browse files
sha256/07a59847b598c95c5cb062770db082099402d1eca1b913733a62ead475962f93 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":"0a65550d027ecaf8ee3e58e6c9221a09c376a3ea4cd8283e6376baeae525bab6","resolve":"23aec66382775f5773693615338f37c37a5f58257ec95aece33a664baa6fe0db","self":"0511772584510f0888d97dd5a72bf986ad6791bd8e305063ddd8a82c39888a70","spaces":"d8aebefb67e7fc7802e757b9a7d95bb881e6d75b4a9bc4bd4227634541757e51","yourspace":"f9c0ceff30069ffb076d4da4787d23984c247573e29a9ac52fb175703ddf0d62"},"boot":{"entry":"holo-portal.mjs","sha256":"caeb3e57b6eeab09bee6d67c022145a8e2dae610c929a7f51bc169898625e7e8"},"closure":"5c90360d436496a02657e32ac353c3ddf83fb29ea0b55b63caecdb02c21adbe0","manifest":"8bd00282804b748cc852f8ad5fc7a583144ecbbcd715b333421445ed1d6fe3cc","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":"97ca1ee6446f04c1d395d318e759c5a9ffdf4a27c26ef6ce9a5001dc49b2fd70"},"holstr:prev":"did:holo:sha256:272b79f381eff738de6fb52c5ef0c669ffbc0118288c254c03e67cee7f0db17a","holstr:seq":239,"prov:generatedAtTime":"2026-07-13T14:46:23.749Z"}
sha256/55e097a763c011d722514271fb75c565eccb8557bf6bb27f6a963516a5f96880 ADDED
The diff for this file is too large to render. See raw diff
 
sha256/f9c0ceff30069ffb076d4da4787d23984c247573e29a9ac52fb175703ddf0d62 ADDED
@@ -0,0 +1,582 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 — a place for friends</title>
7
+ <meta name="description" content="Your page is back. A whole 2004-style space carried inside one link — no account, no app, no server. Step in, then make it yours in one tap." />
8
+ <meta property="og:title" content="Your Space — a place for friends" />
9
+ <meta property="og:description" content="The page your thumbs remember, reborn: it travels whole inside one link. Step in, then make it yours in one tap." />
10
+ <meta property="og:type" content="website" />
11
+ <meta name="twitter:card" content="summary" />
12
+ <style>
13
+ /* ── HOLO-MYSPACE-AUTHENTIC A0 tokens — harvested from wittenbrock/toms-myspace-page.
14
+ Authentic in FORM (2004 palette, Verdana 8pt, 800px white page, table rhythm),
15
+ lawful in SUBSTANCE (κ-true, honest numbers, 2026 speed). ─────────────────────── */
16
+ :root {
17
+ --ms-blue: #003399; --ms-band: #6699cc; --ms-label: #b1d0f0; --ms-cell: #d5e8fb;
18
+ --ms-labeltext: #336699; --ms-orange: #ff6600; --ms-orangeband: #ffcc99;
19
+ --ms-cth: #ff9933; --ms-ctd: #f9d6b4; --ms-red: #cc0000; --ms-page: #ffffff; --ms-bg: #e5e5e5;
20
+ }
21
+ * { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
22
+ html, body { margin: 0; }
23
+ body { font-family: verdana, arial, sans-serif, helvetica; font-size: 8pt; background: var(--ms-bg); color: #000; }
24
+ a { font-weight: bold; text-decoration: none; color: var(--ms-blue); cursor: pointer; }
25
+ a:hover { text-decoration: underline; color: var(--ms-red); }
26
+ h1 { font-size: 12.25pt; font-weight: bold; margin: 15px 0 0; }
27
+ h2 { font-size: 9pt; font-weight: bold; margin: 0; }
28
+ .master { margin: auto; width: 800px; max-width: 100%; background: var(--ms-page); min-height: 100vh; }
29
+
30
+ /* header — the blue block + search row + #6699CC navbar */
31
+ .hdr { background: var(--ms-blue); color: #fff; padding-top: 6px; }
32
+ .hdr a { color: #fff; font-weight: normal; }
33
+ .hdr .toprow { display: flex; align-items: baseline; gap: .5rem; padding: 4px 10px; flex-wrap: wrap; }
34
+ .hdr .wordmark { font-size: 15pt; font-weight: bold; letter-spacing: -.5px; }
35
+ .hdr .tagline { font-size: 8pt; opacity: .92; }
36
+ .hdr .helpsignup { margin-left: auto; font-size: 8pt; }
37
+ .searchrow { display: flex; align-items: center; gap: 6px; padding: 4px 10px 8px; flex-wrap: wrap; font-size: 8pt; }
38
+ .searchrow input[type="text"] { width: 160px; font: inherit; padding: 1px 3px; }
39
+ .btn2004 { border: 1px solid rgb(186,186,186); padding: 2px 7px; background: #e0ffff; font-size: 10px; font-family: inherit; cursor: pointer; color: #000; }
40
+ .navbar { display: flex; align-items: center; background: var(--ms-band); min-height: 26px; flex-wrap: wrap; padding: 2px 6px; }
41
+ .navbar a { color: #fff; font-weight: bold; font-size: 8pt; white-space: nowrap; }
42
+ .navbar a:hover { color: var(--ms-blue); text-decoration: none; }
43
+ .navbar .sep { color: #000; margin: 0 .33rem; }
44
+
45
+ /* two columns: 325 / 453, 20px gap (the harvest), stacked on phones */
46
+ .page { display: grid; grid-template-columns: 305px 1fr; gap: 20px; padding: 0 12px 20px 27px; }
47
+ @media (max-width: 719px) { .page { display: block; padding: 0 10px 20px; } .netbanner { width: auto !important; } }
48
+
49
+ /* left column */
50
+ .picwrap { display: flex; gap: 15px; margin: 10px 0 12px; }
51
+ .avatar { width: 130px; height: 150px; flex: none; display: grid; place-items: center; font-size: 64px;
52
+ background: #fff; border: 1px solid #999; overflow: hidden; }
53
+ .avatar img { width: 100%; height: 100%; object-fit: cover; }
54
+ .picmeta { display: flex; flex-direction: column; min-width: 0; }
55
+ .picmeta .quote { margin: 2px 0 10px; font-weight: bold; }
56
+ .picmeta ul { list-style: none; margin: 0; padding: 0; }
57
+ .picmeta .lastlogin { margin-top: auto; padding-bottom: 4px; color: #000; }
58
+ .online { color: #fff; background: #390; font-weight: bold; padding: 0 4px; font-size: 7pt; display: inline-block; margin-top: 3px; }
59
+ .moodline { margin-top: 6px; }
60
+
61
+ .contactbox { border: 2px solid var(--ms-band); width: 100%; margin-bottom: 24px; }
62
+ .contactbox h2 { color: #fff; background: var(--ms-band); padding: 2px 0 2px 10px; font-size: 9pt; }
63
+ .contactgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 4px; padding: 6px 10px 8px; }
64
+ .contactgrid a { font-size: 8pt; }
65
+ .urlbox { border: 1px solid var(--ms-band); margin-bottom: 24px; padding: 2px 6px 4px; overflow: hidden; }
66
+ .urlbox b { font-size: 8pt; }
67
+ .urlbox .thelink { color: var(--ms-blue); font-weight: bold; cursor: pointer; word-break: break-all; display: block; max-height: 3.9em; overflow: hidden; }
68
+ .urlbox .kap { color: #666; font-weight: normal; cursor: pointer; }
69
+ .urlbox .kap.ok { color: #390; }
70
+
71
+ table.side { border: 2px solid var(--ms-band); border-collapse: separate; border-spacing: 3px; width: 100%; margin-bottom: 13px; }
72
+ table.side .h2row td { background: var(--ms-band); color: #fff; font-weight: bold; font-size: 9pt; padding: 2px 0 2px 15px; }
73
+ table.side th { vertical-align: top; text-align: left; width: 37%; background: var(--ms-label); color: var(--ms-labeltext); padding: 3px; font-weight: bold; font-size: 8pt; }
74
+ table.side td.v { background: var(--ms-cell); padding: 3px; width: 63%; word-break: break-word; }
75
+
76
+ /* right column */
77
+ .netbanner { display: flex; justify-content: center; align-items: center; width: 433px; max-width: 100%; margin: 13px auto 0; height: 75px; border: 2px solid #000; text-align: center; padding: 0 8px; }
78
+ .netbanner h2 { font-size: 12pt; }
79
+ .blog { margin: 13px 0 16px 12px; }
80
+ .blog p { margin: 3px 0; }
81
+ .mainhead { background: var(--ms-orangeband); padding: 3px 0; margin: 0 0 8px; }
82
+ .mainhead h2 { color: var(--ms-orange); margin-left: 20px; }
83
+ .blurbs h3 { margin: 0 0 2px 15px; color: var(--ms-orange); font-size: 9pt; }
84
+ .blurbs p { margin: 0 10px 15px 15px; white-space: pre-wrap; line-height: 1.3; }
85
+ section { margin-bottom: 10px; }
86
+
87
+ /* the profile song — the little player bar */
88
+ .player { display: flex; align-items: center; gap: 8px; border: 2px solid var(--ms-band); background: #001a4d; color: #fff; padding: 5px 8px; margin: 0 10px 18px 15px; }
89
+ .player button { background: #e0ffff; border: 1px solid #999; width: 26px; height: 22px; cursor: pointer; font-size: 10px; }
90
+ .player .tt { font-size: 8pt; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
91
+ .player .tt b { color: #ffcc99; }
92
+
93
+ .friends p.count { margin: 0 0 8px 15px; }
94
+ .focus { color: var(--ms-red); font-weight: bold; font-size: 10pt; }
95
+ .friendgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; padding: 0 8px; }
96
+ @media (max-width: 719px) { .friendgrid { grid-template-columns: repeat(2, 1fr); } }
97
+ .friendgrid figure { margin: 0; text-align: center; cursor: pointer; position: relative; }
98
+ .friendgrid .face { width: 88px; height: 88px; margin: auto; display: grid; place-items: center; font-size: 40px; background: #eee; border: 1px solid #999; overflow: hidden; }
99
+ .friendgrid .face img { width: 100%; height: 100%; object-fit: cover; }
100
+ .friendgrid figcaption { margin-top: 3px; }
101
+ .friendgrid figcaption a { color: var(--ms-blue); }
102
+ .friendgrid .rm { display: none; position: absolute; top: -6px; right: 6px; width: 20px; height: 20px; border: 1px solid #999; background: #fff; color: var(--ms-red); font-weight: bold; cursor: pointer; }
103
+ body.edit .friendgrid .rm { display: block; }
104
+ .viewall { text-align: right; margin: 7px 8px 22px 0; }
105
+ .viewall a { color: var(--ms-red); }
106
+
107
+ .comments table { width: calc(100% - 16px); margin: 0 auto 5px; border-collapse: separate; border-spacing: 2px; }
108
+ .comments th { background: var(--ms-cth); width: 35%; padding: 3px; vertical-align: top; font-size: 8pt; text-align: center; }
109
+ .comments td { background: var(--ms-ctd); padding: 3px 6px; vertical-align: top; font-weight: normal; text-align: left; }
110
+ .comments .ccount { margin: 0 0 6px 15px; }
111
+ .addcomment { text-align: right; margin: 0 10px 12px 0; }
112
+
113
+ .ftr { background: var(--ms-bg); padding: 10px 8px 14px; text-align: center; }
114
+ .ftr a { font-weight: normal; text-decoration: underline; font-size: 8pt; }
115
+ .ftr small { display: block; margin-top: 8px; font-size: 8pt; color: #333; }
116
+
117
+ /* ── edit mode: Playground law — every element edits in place ───────────────────────── */
118
+ body.edit [data-ek], body.edit [data-eki], body.edit [data-ekd], body.edit [data-ekg] { outline: 1px dashed var(--ms-red); outline-offset: 2px; min-width: 20px; cursor: text; }
119
+ [contenteditable]:empty::before { content: attr(data-ph); color: #999; font-weight: normal; }
120
+ .editonly { display: none; }
121
+ body.edit .editonly { display: block; }
122
+ body.edit .avatar { cursor: pointer; }
123
+ .layoutchips { display: flex; flex-wrap: wrap; gap: 5px; padding: 4px 10px 8px; }
124
+ .csspaste { width: calc(100% - 20px); margin: 0 10px 8px; font: 8pt monospace; min-height: 70px; }
125
+ .srchbox input { width: calc(100% - 20px); margin: 0 10px 6px; font: inherit; padding: 2px 4px; }
126
+ .srchbox .res { display: flex; gap: 6px; align-items: center; padding: 3px 12px; cursor: pointer; }
127
+ .srchbox .res:hover { background: var(--ms-cell); }
128
+ .srchbox .res img { width: 30px; height: 30px; }
129
+ .doorform input { width: calc(100% - 20px); margin: 0 10px 5px; font: inherit; padding: 2px 4px; }
130
+
131
+ /* the loop bar — 2004 button chrome on a modern sticky bar (the one deliberate anachronism) */
132
+ #cta { position: fixed; z-index: 5; left: 0; right: 0; bottom: 0; display: flex; gap: 8px; justify-content: center;
133
+ padding: 8px 10px calc(8px + env(safe-area-inset-bottom)); background: rgba(229,229,229,.95); border-top: 1px solid #bbb; }
134
+ #cta .btn2004 { font-size: 12px; padding: 8px 14px; min-height: 44px; min-width: 130px; font-weight: bold; }
135
+ #cta .go { background: var(--ms-orangeband); border-color: var(--ms-orange); color: #000; }
136
+ #toast { position: fixed; z-index: 10; left: 50%; bottom: 78px; transform: translateX(-50%) translateY(16px); opacity: 0; pointer-events: none;
137
+ background: #fffbd6; border: 1px solid #999; color: #000; padding: 6px 12px; font-size: 8pt; transition: all .25s; max-width: 88vw; text-align: center; }
138
+ #toast.on { opacity: 1; transform: translateX(-50%); }
139
+ #picker { display: none; position: fixed; z-index: 9; left: 50%; bottom: 84px; transform: translateX(-50%); width: min(92vw, 360px);
140
+ padding: 8px; background: #fff; border: 2px solid var(--ms-band); }
141
+ #picker.on { display: block; }
142
+ #picker .grid { display: grid; grid-template-columns: repeat(8, 1fr); gap: 2px; }
143
+ #picker .grid button { font-size: 18px; padding: 3px 0; background: none; border: 0; cursor: pointer; }
144
+ #picker input { width: 100%; margin-top: 6px; font: inherit; padding: 2px 4px; }
145
+ .hint { background: #fffbd6; border: 1px solid #e0c000; margin: 8px 12px 0; padding: 5px 8px; display: none; }
146
+ .hint.on { display: block; }
147
+ </style>
148
+ </head>
149
+ <body>
150
+ <div class="master">
151
+ <header class="hdr">
152
+ <div class="toprow">
153
+ <span class="wordmark">yourspace</span><span class="tagline">a place for friends.</span>
154
+ <span class="helpsignup"><a id="navhelp">Help</a> | <a id="navsignup">SignUp</a></span>
155
+ </div>
156
+ <div class="searchrow">
157
+ <input type="text" id="q" placeholder="" aria-label="search" />
158
+ <button class="btn2004" id="qgo">Search</button>
159
+ <label><input type="radio" name="sw" /> The Web</label>
160
+ <label><input type="radio" name="sw" checked /> YourSpace</label>
161
+ </div>
162
+ <nav class="navbar" id="navbar"></nav>
163
+ </header>
164
+
165
+ <div class="hint" id="hint"></div>
166
+
167
+ <div class="page">
168
+ <div class="col-left">
169
+ <h1 id="name" data-ek="name" data-ph="Your Name"></h1>
170
+ <div class="picwrap">
171
+ <div class="avatar" id="avatar">🪐</div>
172
+ <div class="picmeta">
173
+ <p class="quote" id="headline" data-ek="headline" data-ph='":-)"'></p>
174
+ <ul>
175
+ <li id="d-gender" data-ekg="gender" data-ph="Gender"></li>
176
+ <li id="d-age" data-ekg="age" data-ph="Age"></li>
177
+ <li id="d-city" data-ekg="city" data-ph="City"></li>
178
+ <li id="d-region" data-ekg="region" data-ph="State / Region"></li>
179
+ <li id="d-country" data-ekg="country" data-ph="Country"></li>
180
+ </ul>
181
+ <div class="moodline">Mood: <span id="moodemo" data-ek="moodemo" data-ph=":-)"></span> <span id="moodword" data-ek="moodword" data-ph="chillin"></span></div>
182
+ <ul class="lastlogin"><li id="lastlogin"></li><li><span class="online" id="online" style="display:none">ONLINE NOW!</span></li></ul>
183
+ </div>
184
+ </div>
185
+
186
+ <div class="contactbox">
187
+ <h2 id="contacth">Contacting You</h2>
188
+ <div class="contactgrid" id="contactgrid"></div>
189
+ </div>
190
+
191
+ <div class="urlbox">
192
+ <b>YourSpace URL:</b>
193
+ <span class="thelink" id="thelink" title="tap to copy your link — the link IS your whole space"></span>
194
+ <span class="kap" id="kap" title="this page's content address — tap to re-derive + copy">κ…</span>
195
+ </div>
196
+
197
+ <table class="side" id="interests"><tbody></tbody></table>
198
+ <table class="side" id="details"><tbody></tbody></table>
199
+
200
+ <div class="contactbox editonly srchbox" id="songbox">
201
+ <h2>Profile Song</h2>
202
+ <input id="srchin" placeholder="Search any song…" autocomplete="off" />
203
+ <div id="srchres"></div>
204
+ </div>
205
+
206
+ <div class="contactbox editonly" id="layoutbox">
207
+ <h2>Pick a Layout</h2>
208
+ <div class="layoutchips" id="layoutchips"></div>
209
+ <textarea class="csspaste" id="csspaste" placeholder="…or paste your own layout code (CSS only — no scripts, no imports)"></textarea>
210
+ <div style="padding:0 10px 8px"><button class="btn2004" id="cssapply">Apply Code</button></div>
211
+ </div>
212
+ </div>
213
+
214
+ <main class="col-right">
215
+ <div class="netbanner"><h2 id="netbanner"></h2></div>
216
+
217
+ <div class="blog" id="blog"></div>
218
+
219
+ <section class="blurbs">
220
+ <div class="mainhead"><h2 id="blurbh"></h2></div>
221
+ <h3>About me:</h3>
222
+ <p id="aboutme" data-ek="aboutMe" data-ph="Tell the world. This is your corner of it."></p>
223
+ <h3>Who I'd like to meet:</h3>
224
+ <p id="meet" data-ek="meet" data-ph="People who educate, inspire, or entertain."></p>
225
+ </section>
226
+
227
+ <div class="player" id="player" style="display:none">
228
+ <button id="songbtn" aria-label="Play">▶</button>
229
+ <div class="tt"><b id="songt"></b> — <span id="songa"></span></div>
230
+ </div>
231
+
232
+ <section class="friends">
233
+ <div class="mainhead"><h2 id="friendh"></h2></div>
234
+ <p class="count" id="friendcount"></p>
235
+ <div class="friendgrid" id="friendgrid"></div>
236
+ <div class="contactbox editonly doorform" id="doorform">
237
+ <h2>Add to Top 8</h2>
238
+ <input id="doorlink" placeholder="Paste a friend's space link (or any link)" autocomplete="off" />
239
+ <input id="doorname" placeholder="Their name" autocomplete="off" />
240
+ <div style="padding:0 10px 8px"><button class="btn2004" id="dooradd">Add Friend</button></div>
241
+ </div>
242
+ <p class="viewall"><a id="viewall"></a></p>
243
+ </section>
244
+
245
+ <section class="comments">
246
+ <div class="mainhead"><h2 id="commenth"></h2></div>
247
+ <p class="ccount" id="ccount"></p>
248
+ <div id="commentrows"></div>
249
+ <p class="addcomment"><a id="addcomment">Add Comment</a></p>
250
+ </section>
251
+ </main>
252
+ </div>
253
+
254
+ <footer class="ftr">
255
+ <a id="fabout">About</a> | <a id="ffaq">FAQ</a> | <a id="fterms">Terms</a> | <a id="fprivacy">Privacy</a> |
256
+ <a id="fsafety">Safety Tips</a> | <a id="fcontact">Contact</a> | <a id="fpromote">Promote!</a> | <a id="fnew">Start a blank space</a>
257
+ <small>©2003-2026 your space. the page is a κ — the link is the whole thing. no account, no app, no server.</small>
258
+ </footer>
259
+ </div>
260
+
261
+ <div id="cta"></div>
262
+ <div id="picker"><div class="grid" id="emojis"></div><input id="avurl" placeholder="…or paste an image URL" autocomplete="off" /></div>
263
+ <div id="toast"></div>
264
+ <audio id="audio"></audio>
265
+ <style id="usercss"></style>
266
+
267
+ <script>
268
+ /* pre-paint (boot-ground): name + the visitor's layout code land BEFORE the module, so a
269
+ glitter page NEVER flashes default first. Decode + the same fail-closed CSS refusal —
270
+ no trust decisions beyond "is this lawful CSS"; the module re-derives κ and owns behaviour. */
271
+ (function () {
272
+ try {
273
+ var m = location.hash.match(/#p=v[23]\.([A-Za-z0-9_-]+)/);
274
+ var p = m ? JSON.parse(atob(m[1].replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(m[1].length / 4) * 4, "="))) : null;
275
+ if (p && p.kind === "profile") {
276
+ document.getElementById("name").textContent = p.name || "";
277
+ var css = String(p.customCss || "");
278
+ if (css && css.length <= 32768 && !/[<]|@import|expression\s*\(|behavior\s*:|javascript\s*:|url\(\s*['"]?\s*(?!data:|https:)[a-z]/i.test(css))
279
+ document.getElementById("usercss").textContent = css;
280
+ }
281
+ } catch (e) { /* refused → default skin */ }
282
+ })();
283
+ </script>
284
+
285
+ <script type="module">
286
+ import * as YS from "./holo-yourspace.mjs";
287
+
288
+ const OS = "https://hologram-technologies.github.io/Q";
289
+ const $ = (id) => document.getElementById(id);
290
+ const esc = (s) => String(s || "").replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" }[c]));
291
+ const ME_KEY = "holo-yourspace.me.v2"; // key stable across versions; payload self-describes
292
+ const EMOJIS = ["🙂","😎","🛰️","🪐","🌸","🔥","🌊","🌙","⚡","🎧","🎮","🐉","🦋","🌵","👾","✨"];
293
+
294
+ /* ── A3: Tom = Q — the seed page, beat for beat ─────────────────────────────────────── */
295
+ const SEED = YS.identity({
296
+ v: 3, name: "Q", headline: '":-)"', avatar: "🪐", accent: "#7c6cf0",
297
+ mood: { emo: ":-)", word: "helpful" },
298
+ demog: { gender: "Bot", age: "∞ years old", city: "Inside the seed,", region: "HOLOGRAM", country: "The Internet" },
299
+ interests: { general: "Helping you move in. Links that carry whole worlds. Pages nobody can take down.",
300
+ music: "Everything, at once. Tap my song.", movies: "The Matrix (obviously)", television: "Tuned out.",
301
+ books: "The κ-closure, cover to cover", heroes: "Tom." },
302
+ details: { status: "Here to help", hereFor: "Friends", hometown: "myspace.com/tom", occupation: "Your Space" },
303
+ blurbs: {
304
+ aboutMe: "I'm here to help you with your Space. Fork me and you're home.\n\nThis whole page — the layout, the song, the Top 8 — travels inside ONE link. No account, no app, no server. Post your link anywhere and anyone steps straight into your world.",
305
+ meet: "People who educate, inspire, or entertain. And anyone who misses their old page.",
306
+ },
307
+ blog: [{ title: "why your page is a κ now", link: "https://hologram-technologies.github.io/" }],
308
+ song: { title: "Space Oddity", artist: "David Bowie", query: "david bowie space oddity", art: "", preview: "" },
309
+ top: [{ name: "Q", link: OS, photo: "🪐" }, { name: "HOLOGRAM", link: "https://hologram-technologies.github.io/", photo: "✨" }],
310
+ wall: "", layout: "", customCss: "", via: null,
311
+ });
312
+
313
+ /* ── A4-lite: the layout rack — period-exact, pure CSS/data-URIs, zero external hosts ── */
314
+ const LAYOUTS = {
315
+ "Classic": "",
316
+ "Glitter Pink": "body{background:repeating-linear-gradient(45deg,#ffd7ef 0 12px,#ffc2e5 12px 24px)}.master{background:#fff0fa}:root{--ms-band:#ff5fb0;--ms-label:#ffc2e5;--ms-cell:#ffe3f4;--ms-labeltext:#b0006d;--ms-orangeband:#ffc2e5;--ms-orange:#e0007a;--ms-cth:#ff8fd0;--ms-ctd:#ffe3f4;--ms-blue:#c4008a}",
317
+ "Goth": "body{background:#000}.master{background:#0d0d0d;color:#ccc}:root{--ms-band:#8b0000;--ms-label:#1a0000;--ms-cell:#140a0a;--ms-labeltext:#ff4444;--ms-orangeband:#2b0000;--ms-orange:#ff2222;--ms-cth:#8b0000;--ms-ctd:#1c0d0d;--ms-blue:#ff4444;--ms-red:#fff;--ms-page:#0d0d0d}h1,h2,h3,p,li,td,th,div,span{color:#ccc}",
318
+ "Baby Blue": "body{background:#dff1ff}:root{--ms-band:#8fc6f0;--ms-label:#d5ecff;--ms-cell:#ecf7ff;--ms-orangeband:#d5ecff;--ms-orange:#2277bb;--ms-cth:#a8d4f5;--ms-ctd:#ecf7ff}",
319
+ "Zebra": "body{background:repeating-linear-gradient(135deg,#000 0 16px,#fff 16px 32px)}.master{background:#fff}:root{--ms-band:#000;--ms-label:#e8e8e8;--ms-cell:#f7f7f7;--ms-labeltext:#000;--ms-orangeband:#000;--ms-orange:#fff;--ms-cth:#000;--ms-ctd:#eee;--ms-blue:#000}",
320
+ "Matrix": "body{background:#000}.master{background:#020a02;color:#39ff5a}:root{--ms-band:#0a3d0a;--ms-label:#04140 4;--ms-cell:#031003;--ms-labeltext:#39ff5a;--ms-orangeband:#052505;--ms-orange:#39ff5a;--ms-cth:#0a3d0a;--ms-ctd:#031003;--ms-blue:#39ff5a;--ms-page:#020a02}h1,h2,h3,p,li,td,th,div,span{color:#39ff5a}",
321
+ "Flames": "body{background:linear-gradient(180deg,#1a0000,#4d0f00 40%,#993300)}.master{background:#fff7ef}:root{--ms-band:#cc3300;--ms-label:#ffd9b3;--ms-cell:#fff0e0;--ms-labeltext:#992200;--ms-orangeband:#ffb366;--ms-orange:#cc2200;--ms-cth:#ff6600;--ms-ctd:#ffe6cc}",
322
+ "Sunset": "body{background:linear-gradient(180deg,#3d1635,#7a2b3d 45%,#c96f3f)}.master{background:#fff5f0}:root{--ms-band:#b0486a;--ms-label:#ffd9e0;--ms-cell:#fff0f3;--ms-labeltext:#8a2c48;--ms-orangeband:#ffd9c0;--ms-orange:#c2452a;--ms-cth:#e98a5f;--ms-ctd:#ffeadd}",
323
+ };
324
+ // fix the Matrix typo above at runtime (space in a hex) — fail-closed sanitizer would keep it, so scrub:
325
+ LAYOUTS["Matrix"] = LAYOUTS["Matrix"].replace("#04140 4", "#041404");
326
+
327
+ const NAV = [["Home", () => goHome()], ["Browse", () => toast("Browse opens when spaces opt in — for now, walk the Top 8")], ["Search", () => $("q").focus()],
328
+ ["Invite", () => share()], ["Film", () => open(OS, "_blank")], ["Mail", () => open(OS, "_blank")], ["Blog", () => $("blog").scrollIntoView({ behavior: "smooth" })],
329
+ ["Favorites", () => toast("Saved spaces live in your Top 8")], ["Forum", () => toast("Coming to your space")], ["Groups", () => toast("Groups = sealed rooms — coming to your space")],
330
+ ["Events", () => toast("Coming to your space")], ["Videos", () => open(OS, "_blank")], ["Music", () => open(OS, "_blank")], ["Classifieds", () => toast("Coming to your space")]];
331
+
332
+ let doc = null, mode = "view", curKappa = "", gestured = false;
333
+ const me = () => { try { return JSON.parse(localStorage.getItem(ME_KEY)); } catch (e) { return null; } };
334
+ const saveMe = (d, l) => localStorage.setItem(ME_KEY, JSON.stringify({ payload: YS.encode(d), link: l }));
335
+ const toast = (msg, ms = 2800) => { const t = $("toast"); t.textContent = msg; t.classList.add("on"); clearTimeout(t._h); t._h = setTimeout(() => t.classList.remove("on"), ms); };
336
+ const myLink = () => location.origin + location.pathname + "#p=v" + doc.v + "." + YS.encode(doc);
337
+
338
+ function boot() {
339
+ const payload = YS.parseLink(location.hash);
340
+ if (payload) { const p = YS.decode(payload); if (p) return show(YS.upgrade(p), "view"); }
341
+ const m = me();
342
+ if (m && m.payload) { const p = YS.decode(m.payload); if (p) { const u = YS.upgrade(p); history.replaceState(null, "", "#p=v" + u.v + "." + YS.encode(u)); return show(u, "view"); } }
343
+ history.replaceState(null, "", location.pathname + location.search);
344
+ show(SEED, "view", true);
345
+ }
346
+ addEventListener("hashchange", boot);
347
+
348
+ /* ── render: the doc IS the page; view and edit are the same anatomy ────────────────── */
349
+ async function show(p, m, isSeed = false) {
350
+ doc = YS.upgrade(p); mode = m;
351
+ document.body.classList.toggle("edit", mode === "edit");
352
+ $("usercss").textContent = doc.customCss || "";
353
+ const first = (doc.name || "Your").split(/\s+/)[0];
354
+
355
+ // nav (once)
356
+ if (!$("navbar").childElementCount) NAV.forEach(([label, fn], i) => {
357
+ if (i) { const s = document.createElement("span"); s.className = "sep"; s.textContent = "|"; $("navbar").appendChild(s); }
358
+ const a = document.createElement("a"); a.textContent = label; a.onclick = fn; $("navbar").appendChild(a);
359
+ });
360
+
361
+ // left column
362
+ $("name").textContent = doc.name;
363
+ $("headline").textContent = doc.headline;
364
+ $("avatar").innerHTML = /^https:\/\//.test(doc.avatar) ? `<img src="${esc(doc.avatar)}" alt="">` : (esc(doc.avatar) || (mode === "edit" ? "+" : "🪐"));
365
+ for (const k of ["gender", "age", "city", "region", "country"]) $("d-" + k).textContent = doc.demog[k];
366
+ $("moodemo").textContent = doc.mood.emo; $("moodword").textContent = doc.mood.word;
367
+ const owner = !!(me() && me().payload === YS.encode(doc));
368
+ // honest presence: only the owner is provably online (they're holding the page); visitors see no invented row
369
+ $("lastlogin").textContent = owner ? ("Last Login: " + new Date().toLocaleDateString()) : "";
370
+ $("online").style.display = owner ? "" : "none";
371
+
372
+ $("contacth").textContent = "Contacting " + (doc.name || "this space");
373
+ const CONTACT = [
374
+ ["✉️", "Send Message", () => doc.wall ? open(doc.wall, "_blank") : open(OS, "_blank")],
375
+ ["↪️", "Forward to Friend", share],
376
+ ["➕", "Add to Friends", addToFriends],
377
+ ["⭐", "Add to Favorites", () => { toast("Saved. Favorites live in YOUR Top 8 — tap Add to Friends to place them."); }],
378
+ ["💬", "Instant Message", () => open(OS, "_blank")],
379
+ ["🚫", "Block User", () => toast("Blocked here = just leave. Nobody can follow you — the page can't track anyone.")],
380
+ ["👥", "Add to Group", () => toast("Groups = sealed rooms — coming to your space")],
381
+ ["🏳️", "Report", () => toast("A κ page can't be edited by anyone but its owner — share concerns with the person who sent the link")],
382
+ ];
383
+ $("contactgrid").innerHTML = "";
384
+ CONTACT.forEach(([g, label, fn]) => { const a = document.createElement("a"); a.textContent = g + " " + label; a.onclick = fn; $("contactgrid").appendChild(a); });
385
+
386
+ $("thelink").textContent = location.hash.includes("#p=") ? location.href : "mint your link with Make It Mine ↓";
387
+ $("thelink").onclick = async () => { try { await navigator.clipboard.writeText(location.href); toast("Link copied — the link IS your whole page 💫"); } catch (e) {} };
388
+ $("kap").textContent = "κ…";
389
+ try { curKappa = await YS.kappa(doc); $("kap").textContent = "κ·" + YS.hexOf(curKappa).slice(0, 10) + " — tap to verify"; } catch (e) { $("kap").textContent = "κ offline"; }
390
+
391
+ sideTable("interests", first + "'s Interests", [["General", "general"], ["Music", "music"], ["Movies", "movies"], ["Television", "television"], ["Books", "books"], ["Heroes", "heroes"]], doc.interests, "eki");
392
+ sideTable("details", first + "'s Details", [["Status", "status"], ["Here for", "hereFor"], ["Hometown", "hometown"], ["Zodiac Sign", "zodiac"], ["Smoke / Drink", "smokeDrink"], ["Children", "children"], ["Education", "education"], ["Occupation", "occupation"]], doc.details, "ekd");
393
+
394
+ // right column
395
+ const viewer = me();
396
+ $("netbanner").textContent = owner ? "This is your space — you're home."
397
+ : viewer ? (doc.name || "This space") + " is in your extended network"
398
+ : (doc.name || "This space") + " is one tap away from being your network";
399
+ const blog = $("blog"); blog.innerHTML = "";
400
+ if (doc.blog.length) {
401
+ blog.innerHTML = `<p><b>${esc(first)}'s Latest Blog Entry</b>&nbsp; <a>[Subscribe to this Blog]</a></p>` +
402
+ doc.blog.map((b) => `<p><a href="${esc(b.link)}" target="_blank" rel="noopener">${esc(b.title)}</a></p>`).join("");
403
+ }
404
+ $("blurbh").textContent = first + "'s Blurbs";
405
+ $("aboutme").textContent = doc.blurbs.aboutMe; $("meet").textContent = doc.blurbs.meet;
406
+
407
+ const s = doc.song; $("player").style.display = s ? "" : "none";
408
+ if (s) { $("songt").textContent = s.title; $("songa").textContent = s.artist; }
409
+
410
+ $("friendh").textContent = first + "'s Friend Space";
411
+ $("friendcount").innerHTML = `${esc(doc.name || "This space")} has <span class="focus">${doc.top.length}</span> friend${doc.top.length === 1 ? "" : "s"}.`;
412
+ const fg = $("friendgrid"); fg.innerHTML = "";
413
+ doc.top.forEach((d, i) => {
414
+ const isSpace = !!YS.parseLink(d.link);
415
+ const fig = document.createElement("figure");
416
+ fig.innerHTML = `<div class="face">${/^https:\/\//.test(d.photo) ? `<img src="${esc(d.photo)}" alt="">` : (esc(d.photo) || esc((d.name || "•")[0].toUpperCase()))}</div><figcaption><a>${esc(d.name || "friend")}</a></figcaption><button class="rm" aria-label="remove">×</button>`;
417
+ fig.onclick = (ev) => { if (ev.target.className === "rm") return commit(YS.removeDoor(doc, i));
418
+ if (isSpace || d.link.startsWith(location.origin)) location.assign(d.link); else open(d.link, "_blank", "noopener"); };
419
+ fg.appendChild(fig);
420
+ });
421
+ $("viewall").textContent = "View All of " + first + "'s Friends";
422
+ $("viewall").onclick = () => toast(doc.top.length ? doc.top.map((d) => d.name || "friend").join(" · ") : "No friends yet — that's what forking is for");
423
+
424
+ $("commenth").textContent = first + "'s Friends Comments";
425
+ $("ccount").innerHTML = doc.wall
426
+ ? `Comments live in <b>${esc(first)}'s sealed wall</b> — step in to read and write. Honest number: only the room knows.`
427
+ : `Displaying <b>0</b> of <b>0</b> comments — this wall isn't open yet.`;
428
+ $("commentrows").innerHTML = doc.wall ? "" : `<table><tr><th>${esc(first)}<br><div style="font-size:30px">${esc(doc.avatar) || "🪐"}</div></th><td><b>${new Date().toLocaleDateString()}</b><br><br>Your wall opens when you weld a sealed room to this page. Until then: real numbers only — this page never invents a comment.</td></tr></table>`;
429
+ $("addcomment").onclick = () => doc.wall ? open(doc.wall, "_blank") : toast(owner ? "Weld a room link into your wall field (rooms are live in the messenger)" : "This wall isn't open yet — fork the page and open your own");
430
+
431
+ // hint + CTA
432
+ $("hint").classList.toggle("on", isSeed);
433
+ if (isSeed) $("hint").innerHTML = "👋 <b>Welcome back.</b> This is Q's page — same page your thumbs remember, except it lives inside the link itself. <b>Make It Mine</b> below ↓";
434
+ const cta = $("cta"); cta.innerHTML = "";
435
+ const btn = (label, cls, fn) => { const b = document.createElement("button"); b.className = "btn2004 " + cls; b.textContent = label; b.onclick = fn; cta.appendChild(b); };
436
+ if (mode === "edit") btn("Done — mint my link", "go", finish);
437
+ else if (owner) { btn("Edit My Space", "", () => show(doc, "edit")); btn("Share", "go", share); }
438
+ else { btn("✨ Make It Mine", "go", makeItMine); btn("Share", "", share); }
439
+
440
+ renderEditRacks();
441
+ wireEditables();
442
+ }
443
+
444
+ function sideTable(id, title, rows, data, ekAttr) {
445
+ const tb = $(id).querySelector("tbody"); tb.innerHTML = "";
446
+ const live = rows.filter(([, k]) => data[k] || mode === "edit");
447
+ $(id).style.display = live.length ? "" : "none";
448
+ if (!live.length) return;
449
+ tb.innerHTML = `<tr class="h2row"><td colspan="2">${esc(title)}</td></tr>` +
450
+ live.map(([label, k]) => `<tr><th>${esc(label)}</th><td class="v" data-${ekAttr}="${k}" data-ph="…">${esc(data[k])}</td></tr>`).join("");
451
+ }
452
+
453
+ /* ── the loop ────────────────────────────────────────────────────────────────────────── */
454
+ async function makeItMine() {
455
+ let mine;
456
+ try { mine = await YS.fork(doc, location.hash.includes("#p=") ? location.href : location.origin + location.pathname); }
457
+ catch (e) { mine = YS.upgrade(doc); mine = YS.withFields(mine, { name: "", via: { k: "", name: doc.name } }); }
458
+ toast("It's yours. Decorate, then mint your link ✨");
459
+ show(mine, "edit");
460
+ scrollTo({ top: 0, behavior: "smooth" });
461
+ }
462
+ async function addToFriends() { // 2004 "Add to Friends" = put THIS page in MY Top 8
463
+ const m = me();
464
+ if (!m) return makeItMine();
465
+ const my = YS.upgrade(YS.decode(m.payload));
466
+ const updated = YS.addDoor(my, { name: doc.name || "friend", link: location.href, photo: doc.avatar });
467
+ saveMe(updated, m.link);
468
+ toast((doc.name || "This space") + " added to your Top 8 ✔ — friendship is mutual when they add you back");
469
+ }
470
+ function commit(next) { doc = YS.identity(next); show(doc, mode); }
471
+
472
+ function collectEdits() {
473
+ const g = {}; for (const k of ["gender", "age", "city", "region", "country"]) g[k] = $("d-" + k).textContent.trim();
474
+ const interests = {}, details = {};
475
+ document.querySelectorAll("[data-eki]").forEach((el) => interests[el.dataset.eki] = el.textContent.trim());
476
+ document.querySelectorAll("[data-ekd]").forEach((el) => details[el.dataset.ekd] = el.textContent.trim());
477
+ return YS.withFields(doc, {
478
+ name: $("name").textContent.trim(), headline: $("headline").textContent.trim(),
479
+ mood: { emo: $("moodemo").textContent.trim(), word: $("moodword").textContent.trim() },
480
+ demog: g, interests: { ...doc.interests, ...interests }, details: { ...doc.details, ...details },
481
+ blurbs: { aboutMe: $("aboutme").textContent.trim(), meet: $("meet").textContent.trim() },
482
+ });
483
+ }
484
+ async function finish() {
485
+ doc = collectEdits();
486
+ saveMe(doc, myLink());
487
+ history.replaceState(null, "", "#p=v" + doc.v + "." + YS.encode(doc));
488
+ await show(doc, "view");
489
+ toast("Minted. The URL box on the left IS your page — copy it, post it anywhere.");
490
+ share();
491
+ }
492
+ async function share() {
493
+ const url = location.href;
494
+ const title = (doc.name ? doc.name + "'s" : "my") + " space";
495
+ try { if (navigator.share) return await navigator.share({ title, text: "step into " + title, url }); } catch (e) { if (e.name === "AbortError") return; }
496
+ try { await navigator.clipboard.writeText(url); toast("Link copied — drop it in any chat 💫"); }
497
+ catch (e) { prompt("Copy your space link:", url); }
498
+ }
499
+ function goHome() { const m = me(); if (m && m.link) location.assign(m.link); else { location.hash = ""; } }
500
+
501
+ /* ── edit affordances ───────────────────────────────────────────────────────────────── */
502
+ function wireEditables() {
503
+ const on = mode === "edit";
504
+ document.querySelectorAll("[data-ek],[data-eki],[data-ekd],[data-ekg]").forEach((el) => { el.contentEditable = on ? "plaintext-only" : "false"; });
505
+ }
506
+ function renderEditRacks() {
507
+ const rack = $("layoutchips"); rack.innerHTML = "";
508
+ Object.keys(LAYOUTS).forEach((name) => { const b = document.createElement("button"); b.className = "btn2004"; b.textContent = name;
509
+ b.onclick = () => { commit(YS.withFields(collectEdits(), { customCss: LAYOUTS[name] })); toast(name + " layout on — via the rack, free forever"); }; rack.appendChild(b); });
510
+ $("csspaste").value = doc.customCss || "";
511
+ }
512
+ $("cssapply").onclick = () => {
513
+ const clean = YS.sanitizeCss($("csspaste").value);
514
+ if ($("csspaste").value.trim() && !clean) return toast("Code refused — CSS only: no <, no @import, no scripts, urls must be data:/https:");
515
+ commit(YS.withFields(collectEdits(), { customCss: clean }));
516
+ toast(clean ? "Your code is live on your page ✨" : "Back to the classic skin");
517
+ };
518
+ $("avatar").onclick = () => { if (mode !== "edit") return; const p = $("picker"); p.classList.toggle("on");
519
+ const g = $("emojis"); if (!g.childElementCount) EMOJIS.forEach((e) => { const b = document.createElement("button"); b.textContent = e;
520
+ b.onclick = () => { p.classList.remove("on"); commit(YS.withFields(collectEdits(), { avatar: e })); }; g.appendChild(b); }); };
521
+ $("avurl").onchange = () => { $("picker").classList.remove("on"); commit(YS.withFields(collectEdits(), { avatar: $("avurl").value.trim() })); };
522
+ document.addEventListener("click", (e) => { if (!$("picker").contains(e.target) && e.target !== $("avatar")) $("picker").classList.remove("on"); });
523
+
524
+ let srchT = null;
525
+ $("srchin").oninput = () => { clearTimeout(srchT); srchT = setTimeout(async () => {
526
+ const q = $("srchin").value.trim(); const box = $("srchres"); if (!q) { box.innerHTML = ""; return; }
527
+ try {
528
+ const r = await fetch(`https://itunes.apple.com/search?media=music&limit=5&term=${encodeURIComponent(q)}`);
529
+ const js = await r.json(); box.innerHTML = "";
530
+ (js.results || []).forEach((t) => { const d = document.createElement("div"); d.className = "res";
531
+ d.innerHTML = `<img src="${esc(t.artworkUrl100 || "")}" alt=""><div><b>${esc(t.trackName)}</b><br><span>${esc(t.artistName)}</span></div>`;
532
+ d.onclick = () => { commit(YS.withFields(collectEdits(), { song: { title: t.trackName, artist: t.artistName, art: t.artworkUrl100 || "", query: `${t.artistName} ${t.trackName}`, preview: t.previewUrl || "" } })); toast("Song set 🎧 — it plays when someone enters"); };
533
+ box.appendChild(d); });
534
+ } catch (e) { box.innerHTML = `<div class="res">search unreachable — try again later</div>`; }
535
+ }, 350); };
536
+ $("dooradd").onclick = () => { const l = $("doorlink").value.trim(), n = $("doorname").value.trim();
537
+ if (!l) return toast("Paste a link first");
538
+ commit(YS.addDoor(collectEdits(), { name: n || "friend", link: l, photo: "" })); $("doorlink").value = $("doorname").value = ""; };
539
+
540
+ /* ── the song: first gesture starts it (the lawful 2004 autoplay) ───────────────────── */
541
+ async function playSong(toggle) {
542
+ const s = doc.song; if (!s) return;
543
+ const a = $("audio"), btn = $("songbtn");
544
+ if (toggle && !a.paused) { a.pause(); btn.textContent = "▶"; try { busCmd("pause"); } catch (e) {} return; }
545
+ if (await busCmd("play", [s.query || `${s.artist} ${s.title}`])) { btn.textContent = "❚❚"; toast("Playing in Holo Music 🎶"); return; }
546
+ let src = s.preview;
547
+ if (!src && (s.query || s.title)) { try {
548
+ const r = await fetch(`https://itunes.apple.com/search?media=music&limit=1&term=${encodeURIComponent(s.query || s.artist + " " + s.title)}`);
549
+ src = ((await r.json()).results || [])[0]?.previewUrl || ""; } catch (e) {} }
550
+ if (!src) { if (toggle) toast("Song not reachable right now"); return; }
551
+ a.src = src; try { await a.play(); btn.textContent = "❚❚"; } catch (e) { if (toggle) toast("Tap ▶ to play"); }
552
+ a.onended = () => { btn.textContent = "▶"; };
553
+ }
554
+ $("songbtn").onclick = () => playSong(true);
555
+ const firstGesture = () => { if (gestured) return; gestured = true; if (mode === "view" && doc && doc.song) playSong(false); };
556
+ addEventListener("pointerdown", firstGesture, { once: false });
557
+ addEventListener("scroll", firstGesture, { once: true, passive: true });
558
+
559
+ function busCmd(cmd, args = []) {
560
+ return new Promise((res) => { let done = false;
561
+ try { const ch = new BroadcastChannel("holo-music-ctl"); const id = Math.random().toString(36).slice(2);
562
+ ch.onmessage = (e) => { if (e.data && e.data.re === id) { done = true; ch.close(); res(!!e.data.ok); } };
563
+ ch.postMessage({ id, cmd, args }); setTimeout(() => { if (!done) { ch.close(); res(false); } }, 800);
564
+ } catch (e) { res(false); } });
565
+ }
566
+
567
+ $("kap").onclick = async () => { try {
568
+ const ok = await YS.verify(doc, curKappa);
569
+ $("kap").classList.toggle("ok", ok); setTimeout(() => $("kap").classList.remove("ok"), 1600);
570
+ await navigator.clipboard.writeText(curKappa); toast(ok ? "re-derived ✓ · κ copied — the page you see IS the link you share" : "κ MISMATCH — refused");
571
+ } catch (e) { toast("κ unavailable here"); } };
572
+
573
+ $("navsignup").onclick = () => makeItMine();
574
+ $("navhelp").onclick = () => { location.hash = ""; };
575
+ $("qgo").onclick = () => { const v = $("q").value.trim(); const p = YS.parseLink(v); if (p) location.assign(location.pathname + "#p=v" + (YS.decode(p).v) + "." + p); else toast(v ? "Paste a space link to teleport to it" : "Search = paste any space link"); };
576
+ $("fnew").onclick = () => show(YS.identity({ v: 3, name: "", customCss: "" }), "edit");
577
+ ["fabout", "ffaq", "fterms", "fprivacy", "fsafety", "fcontact", "fpromote"].forEach((id) => $(id).onclick = () => toast("This page has no company behind it — it's a κ. About: the footer says it all."));
578
+
579
+ boot();
580
+ </script>
581
+ </body>
582
+ </html>