@rizom/site-rizom-ai 0.2.0-alpha.261 → 0.2.0-alpha.263
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +2688 -2631
- package/dist/index.js.map +19 -17
- package/package.json +1 -1
- package/src/opening.tsx +76 -39
- package/src/rizom/runtime/plugin.ts +2 -0
- package/src/story/runtime.ts +18 -125
- package/src/story.css +47 -41
package/package.json
CHANGED
package/src/opening.tsx
CHANGED
|
@@ -7,6 +7,13 @@ import {
|
|
|
7
7
|
import { ASK_BOX_SCRIPT_PATH } from "@brains/contracts";
|
|
8
8
|
import { StructuredContentFormatter } from "@brains/content-formatters";
|
|
9
9
|
import { AskBoxHost } from "@brains/site-atlas";
|
|
10
|
+
import {
|
|
11
|
+
ASK_COLUMN_ATTRIBUTE,
|
|
12
|
+
ASK_DRAWING_ATTRIBUTE,
|
|
13
|
+
ASK_LEADS_ATTRIBUTE,
|
|
14
|
+
ASK_MARK_ATTRIBUTE,
|
|
15
|
+
ASK_ROOM_ATTRIBUTE,
|
|
16
|
+
} from "@brains/contracts";
|
|
10
17
|
import { createTemplate, type Template } from "@brains/templates";
|
|
11
18
|
import { z } from "@rizom/site";
|
|
12
19
|
import { placeNetwork, type PlacedBrain } from "./story/network";
|
|
@@ -48,11 +55,19 @@ const replyDelay = (reach: number, after = 0): string =>
|
|
|
48
55
|
export function NetworkLayer({
|
|
49
56
|
brains,
|
|
50
57
|
kin,
|
|
51
|
-
|
|
58
|
+
lendable = false,
|
|
59
|
+
}: ReturnType<typeof placeNetwork> & {
|
|
60
|
+
/** The Ask room lends this drawing to a phone's open conversation (the opening's; a chapter's stays put). */
|
|
61
|
+
lendable?: boolean;
|
|
62
|
+
}): JSX.Element | null {
|
|
52
63
|
if (brains.length === 0) return null;
|
|
53
64
|
const byId = new Map(brains.map((brain) => [brain.id, brain]));
|
|
54
65
|
return (
|
|
55
|
-
<div
|
|
66
|
+
<div
|
|
67
|
+
className="net-layer"
|
|
68
|
+
aria-hidden="true"
|
|
69
|
+
{...(lendable ? { [ASK_DRAWING_ATTRIBUTE]: "" } : {})}
|
|
70
|
+
>
|
|
56
71
|
<svg className="net-svg" viewBox="0 0 100 100">
|
|
57
72
|
{[14.7, 29.3, 44].map((r) => (
|
|
58
73
|
<circle key={r} className="net-ring" cx="50" cy="50" r={r} />
|
|
@@ -125,6 +140,7 @@ export function NetworkLayer({
|
|
|
125
140
|
key={brain.id}
|
|
126
141
|
className="net-mark"
|
|
127
142
|
data-brain={brain.id}
|
|
143
|
+
{...{ [ASK_MARK_ATTRIBUTE]: brain.id }}
|
|
128
144
|
style={{ left: `${brain.x}%`, top: `${brain.y}%` }}
|
|
129
145
|
>
|
|
130
146
|
<a href={`/agents/${brain.id}`} aria-label={brain.name}>
|
|
@@ -153,45 +169,66 @@ export function Opening(data: OpeningData): JSX.Element {
|
|
|
153
169
|
const headingLead = data.headingLead ?? DEFAULT_COPY.headingLead;
|
|
154
170
|
const lede = data.lede ?? DEFAULT_COPY.lede;
|
|
155
171
|
return (
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
</li>
|
|
179
|
-
))}
|
|
180
|
-
</ul>
|
|
172
|
+
// With the box, the opening is an Ask room (@brains/contracts ask-box):
|
|
173
|
+
// the words are its column, which scrolls within the screen beside the
|
|
174
|
+
// drawing on a wide screen; the drawing is lent to a phone's open
|
|
175
|
+
// conversation; the room draws leads from an answer's sources to the dots.
|
|
176
|
+
<section
|
|
177
|
+
id="hero"
|
|
178
|
+
className="chapter chapter--opening"
|
|
179
|
+
data-title="Top"
|
|
180
|
+
{...(data.askBox ? { [ASK_ROOM_ATTRIBUTE]: "" } : {})}
|
|
181
|
+
>
|
|
182
|
+
<div
|
|
183
|
+
className="opening__words"
|
|
184
|
+
{...(data.askBox ? { [ASK_COLUMN_ATTRIBUTE]: "" } : {})}
|
|
185
|
+
>
|
|
186
|
+
{data.kicker && <p className="eyebrow">{data.kicker}</p>}
|
|
187
|
+
<h1>
|
|
188
|
+
{headingLead}
|
|
189
|
+
{data.headingAccent && (
|
|
190
|
+
<>
|
|
191
|
+
{" "}
|
|
192
|
+
<em>{data.headingAccent}</em>
|
|
193
|
+
</>
|
|
181
194
|
)}
|
|
182
|
-
|
|
183
|
-
</
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
195
|
+
</h1>
|
|
196
|
+
<p className="lede">{lede}</p>
|
|
197
|
+
{data.askBox ? (
|
|
198
|
+
<div className="ask">
|
|
199
|
+
<AskBoxHost
|
|
200
|
+
prefix="opening"
|
|
201
|
+
placeholder={data.prompt ?? undefined}
|
|
202
|
+
/>
|
|
203
|
+
{data.topics.length > 0 && (
|
|
204
|
+
<ul className="ask__topics" aria-label="Suggested questions">
|
|
205
|
+
{data.topics.map((topic) => (
|
|
206
|
+
<li key={topic}>
|
|
207
|
+
<button type="button" data-atlas-fill={topic}>
|
|
208
|
+
{topic}
|
|
209
|
+
</button>
|
|
210
|
+
</li>
|
|
211
|
+
))}
|
|
212
|
+
</ul>
|
|
213
|
+
)}
|
|
214
|
+
<script src={ASK_BOX_SCRIPT_PATH} defer />
|
|
215
|
+
</div>
|
|
216
|
+
) : (
|
|
217
|
+
data.ctaHref &&
|
|
218
|
+
data.ctaLabel && (
|
|
219
|
+
<a className="opening__door" href={data.ctaHref}>
|
|
220
|
+
{data.ctaLabel}
|
|
221
|
+
</a>
|
|
222
|
+
)
|
|
223
|
+
)}
|
|
224
|
+
</div>
|
|
225
|
+
<NetworkLayer {...placeNetwork(data)} lendable={data.askBox} />
|
|
193
226
|
{data.askBox && (
|
|
194
|
-
<svg
|
|
227
|
+
<svg
|
|
228
|
+
className="net-leads"
|
|
229
|
+
{...{ [ASK_LEADS_ATTRIBUTE]: "" }}
|
|
230
|
+
aria-hidden="true"
|
|
231
|
+
/>
|
|
195
232
|
)}
|
|
196
233
|
</section>
|
|
197
234
|
);
|
|
@@ -15,6 +15,7 @@ import { foundationOrganism } from "../../story/foundation-organism";
|
|
|
15
15
|
import { livingOrganism } from "../../story/living-organism";
|
|
16
16
|
import { workOrganism } from "../../story/work-organism";
|
|
17
17
|
import { storyRuntimeScript } from "../../story/runtime";
|
|
18
|
+
import { ASK_ROOM_STYLES } from "@brains/site-atlas";
|
|
18
19
|
|
|
19
20
|
export type { RizomRuntimeConfig } from "../contracts";
|
|
20
21
|
|
|
@@ -61,6 +62,7 @@ export const rizomRuntimeStaticAssets: Record<string, string> = {
|
|
|
61
62
|
// The story pages: the page shape, the drawings and the reading thread.
|
|
62
63
|
"/styles/story.css":
|
|
63
64
|
storyStyles +
|
|
65
|
+
ASK_ROOM_STYLES +
|
|
64
66
|
livingOrganism.css() +
|
|
65
67
|
brainOrganism.css() +
|
|
66
68
|
workOrganism.css() +
|
package/src/story/runtime.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* function is exported for tests and inlined into the shipped script, so
|
|
5
5
|
* there is one source for the reading line.
|
|
6
6
|
*/
|
|
7
|
+
import { ASK_ROOM_SCRIPT } from "@brains/site-atlas";
|
|
7
8
|
|
|
8
9
|
/** The index of the last chapter whose top has passed the reading line, clamped to the stages a drawing has. */
|
|
9
10
|
export function currentChapter(
|
|
@@ -33,7 +34,9 @@ export function readingLine(
|
|
|
33
34
|
return stripBottom === null ? viewportHeight * beside : stripBottom + 1;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
export const storyRuntimeScript: string =
|
|
37
|
+
export const storyRuntimeScript: string =
|
|
38
|
+
ASK_ROOM_SCRIPT +
|
|
39
|
+
`(function () {
|
|
37
40
|
${currentChapter.toString()}
|
|
38
41
|
${readingLine.toString()}
|
|
39
42
|
function init() {
|
|
@@ -109,11 +112,14 @@ export const storyRuntimeScript: string = `(function () {
|
|
|
109
112
|
addEventListener("resize", function () { placeRail(); read(); });
|
|
110
113
|
addEventListener("load", function () { placeRail(); read(); });
|
|
111
114
|
}
|
|
112
|
-
// The homepage's
|
|
113
|
-
// the brains whose published memory they came from (the
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
//
|
|
115
|
+
// The homepage's drawings listen to the Ask room: an answer's sources name
|
|
116
|
+
// the brains whose published memory they came from (the room matches them
|
|
117
|
+
// to the opening's dots, keyed by brain, and tells the page), and those
|
|
118
|
+
// brains light in every drawing while the rest dim. A source the room
|
|
119
|
+
// matched to no brain is this brain's own and lights the center. Pointing
|
|
120
|
+
// works both ways: a listed source lights its brain, a brain flags its
|
|
121
|
+
// listed sources. Leads, the lending of the drawing to a phone's open
|
|
122
|
+
// conversation and the tap on a lit dot are the room's.
|
|
117
123
|
function listen() {
|
|
118
124
|
// The opening draws the network, and a chapter may draw it again beside
|
|
119
125
|
// its own words: every drawing answers the same events.
|
|
@@ -127,20 +133,6 @@ export const storyRuntimeScript: string = `(function () {
|
|
|
127
133
|
var replies = all(".net-reply[data-brain]");
|
|
128
134
|
var names = all(".net-name[data-brain]");
|
|
129
135
|
var sourceBrain = {};
|
|
130
|
-
function brainOf(source) {
|
|
131
|
-
var brain = source && source.brain;
|
|
132
|
-
if (!brain) return "";
|
|
133
|
-
var host = "";
|
|
134
|
-
if (brain.url) { try { host = new URL(brain.url).host; } catch (e) { host = ""; } }
|
|
135
|
-
var byHost = host && marks.filter(function (m) { return m.getAttribute("data-brain") === host; })[0];
|
|
136
|
-
if (byHost) return host;
|
|
137
|
-
var name = String(brain.name || "").toLowerCase();
|
|
138
|
-
var byName = marks.filter(function (m) {
|
|
139
|
-
var a = m.querySelector("[aria-label]");
|
|
140
|
-
return (a ? a.getAttribute("aria-label") : "").toLowerCase() === name;
|
|
141
|
-
})[0];
|
|
142
|
-
return byName ? byName.getAttribute("data-brain") : "";
|
|
143
|
-
}
|
|
144
136
|
function light(id, on) {
|
|
145
137
|
[marks, threads, replies, names].forEach(function (list) {
|
|
146
138
|
list.forEach(function (el) { if (el.getAttribute("data-brain") === id) el.classList.toggle("is-lit", on); });
|
|
@@ -149,20 +141,19 @@ export const storyRuntimeScript: string = `(function () {
|
|
|
149
141
|
function hot(id, on) {
|
|
150
142
|
marks.concat(threads).forEach(function (el) { if (el.getAttribute("data-brain") === id) el.classList.toggle("is-hot", on); });
|
|
151
143
|
}
|
|
152
|
-
document.addEventListener("ask:
|
|
144
|
+
document.addEventListener("ask:cited", function (event) {
|
|
153
145
|
var detail = event.detail || {};
|
|
154
|
-
var sources = Array.isArray(detail.sources) ? detail.sources : [];
|
|
155
146
|
var lit = {};
|
|
156
|
-
var rizom = false;
|
|
157
147
|
sourceBrain = {};
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
148
|
+
(detail.cited || []).forEach(function (entry) {
|
|
149
|
+
lit[entry.key] = true;
|
|
150
|
+
sourceBrain[entry.source.id] = entry.key;
|
|
161
151
|
});
|
|
162
152
|
marks.forEach(function (m) { light(m.getAttribute("data-brain"), !!lit[m.getAttribute("data-brain")]); });
|
|
153
|
+
var rizom = (detail.unmatched || []).length > 0 && (detail.sources || []).length > 0;
|
|
163
154
|
layers.forEach(function (layer) {
|
|
164
155
|
layer.classList.toggle("has-replies", Object.keys(lit).length > 0);
|
|
165
|
-
layer.classList.toggle("is-rizom", rizom
|
|
156
|
+
layer.classList.toggle("is-rizom", rizom);
|
|
166
157
|
});
|
|
167
158
|
});
|
|
168
159
|
function listed(target) {
|
|
@@ -196,104 +187,6 @@ export const storyRuntimeScript: string = `(function () {
|
|
|
196
187
|
if (!target || !target.hasAttribute || !target.hasAttribute("data-ask-answer")) return;
|
|
197
188
|
document.dispatchEvent(new CustomEvent("ask:sources", { detail: { sources: askedSources() } }));
|
|
198
189
|
}, true);
|
|
199
|
-
// The drawing belongs to the page while an answer is open. On desktop a
|
|
200
|
-
// dotted lead runs from each listed source to its brain in the opening's
|
|
201
|
-
// drawing; on a phone the drawing joins the open conversation as the
|
|
202
|
-
// first item of its scroll, and a tap on a lit dot brings its source
|
|
203
|
-
// into view. Only the opening's drawing is lent; a chapter's stays put.
|
|
204
|
-
var home = layers[0];
|
|
205
|
-
var homeMarks = marks.filter(function (m) { return home.contains(m); });
|
|
206
|
-
var leadsLayer = document.querySelector("[data-net-leads]");
|
|
207
|
-
var narrow = typeof matchMedia === "function" ? matchMedia("(max-width: 60rem)") : { matches: false };
|
|
208
|
-
var askHost = document.querySelector("[data-ask-box]");
|
|
209
|
-
var SVG = "http://www.w3.org/2000/svg", GAP = 6, GLYPH = 9;
|
|
210
|
-
function listedRow(scope, key) {
|
|
211
|
-
if (!scope) return null;
|
|
212
|
-
var rows = Array.prototype.filter.call(scope.querySelectorAll("[data-ask-source]"), function (row) {
|
|
213
|
-
return row.getAttribute("data-ask-source") === key;
|
|
214
|
-
});
|
|
215
|
-
return rows.length ? rows[rows.length - 1] : null;
|
|
216
|
-
}
|
|
217
|
-
function drawLeads() {
|
|
218
|
-
if (!leadsLayer) return;
|
|
219
|
-
while (leadsLayer.firstChild) leadsLayer.removeChild(leadsLayer.firstChild);
|
|
220
|
-
if (narrow.matches || !askHost) return;
|
|
221
|
-
// The layer's own box is the frame: coordinates are its, whatever positions it.
|
|
222
|
-
var frame = leadsLayer.getBoundingClientRect();
|
|
223
|
-
leadsLayer.setAttribute("viewBox", "0 0 " + frame.width + " " + frame.height);
|
|
224
|
-
Object.keys(sourceBrain).forEach(function (key) {
|
|
225
|
-
var mark = homeMarks.filter(function (m) { return m.getAttribute("data-brain") === sourceBrain[key]; })[0];
|
|
226
|
-
var row = listedRow(askHost, key);
|
|
227
|
-
if (!mark || !row) return;
|
|
228
|
-
var from = row.getBoundingClientRect();
|
|
229
|
-
if (!from.height) return;
|
|
230
|
-
var to = mark.getBoundingClientRect();
|
|
231
|
-
var x1 = from.right + GAP - frame.left;
|
|
232
|
-
var y1 = from.top + from.height / 2 - frame.top;
|
|
233
|
-
var toward = to.left + to.width / 2 - frame.left;
|
|
234
|
-
var x2 = toward > x1 ? toward - GLYPH : toward + GLYPH;
|
|
235
|
-
var y2 = to.top + to.height / 2 - frame.top;
|
|
236
|
-
var bend = Math.max(40, Math.abs(x2 - x1) / 2);
|
|
237
|
-
var lead = document.createElementNS(SVG, "path");
|
|
238
|
-
lead.setAttribute("data-lead", key);
|
|
239
|
-
lead.setAttribute("d", "M" + x1 + " " + y1 + " C" + (x1 + bend) + " " + y1 + " " + (x2 - bend) + " " + y2 + " " + x2 + " " + y2);
|
|
240
|
-
leadsLayer.appendChild(lead);
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
var leadsPending = false;
|
|
244
|
-
function scheduleLeads() {
|
|
245
|
-
if (leadsPending || !leadsLayer) return;
|
|
246
|
-
leadsPending = true;
|
|
247
|
-
requestAnimationFrame(function () { leadsPending = false; drawLeads(); });
|
|
248
|
-
}
|
|
249
|
-
if (leadsLayer) {
|
|
250
|
-
addEventListener("resize", scheduleLeads);
|
|
251
|
-
// Scrolling the page or the conversation moves the listed sources.
|
|
252
|
-
document.addEventListener("scroll", scheduleLeads, true);
|
|
253
|
-
document.addEventListener("toggle", scheduleLeads, true);
|
|
254
|
-
if (askHost && typeof ResizeObserver === "function") new ResizeObserver(scheduleLeads).observe(askHost);
|
|
255
|
-
}
|
|
256
|
-
document.addEventListener("ask:sources", function () { drawLeads(); });
|
|
257
|
-
function sheetOpen() { return !!askHost && askHost.hasAttribute("data-ask-sheet"); }
|
|
258
|
-
var slot = null;
|
|
259
|
-
function lend() {
|
|
260
|
-
var dock = askHost.querySelector("[data-ask-dock]");
|
|
261
|
-
if (!home || !dock || home.parentElement === dock) return;
|
|
262
|
-
slot = document.createElement("div");
|
|
263
|
-
slot.className = "net-slot";
|
|
264
|
-
slot.setAttribute("aria-hidden", "true");
|
|
265
|
-
home.parentNode.insertBefore(slot, home);
|
|
266
|
-
dock.appendChild(home);
|
|
267
|
-
}
|
|
268
|
-
function giveBack() {
|
|
269
|
-
if (!slot) return;
|
|
270
|
-
slot.parentNode.insertBefore(home, slot);
|
|
271
|
-
slot.parentNode.removeChild(slot);
|
|
272
|
-
slot = null;
|
|
273
|
-
}
|
|
274
|
-
if (askHost && home && typeof MutationObserver === "function") {
|
|
275
|
-
// The box mounts, and so its dock appears, after the sheet has opened.
|
|
276
|
-
new MutationObserver(function () { if (sheetOpen()) lend(); else giveBack(); })
|
|
277
|
-
.observe(askHost, { attributes: true, attributeFilter: ["data-ask-sheet"], childList: true, subtree: true });
|
|
278
|
-
}
|
|
279
|
-
document.addEventListener("click", function (event) {
|
|
280
|
-
if (!sheetOpen() || !home) return;
|
|
281
|
-
var mark = event.target && event.target.closest ? event.target.closest(".net-mark[data-brain]") : null;
|
|
282
|
-
if (!mark || !home.contains(mark)) return;
|
|
283
|
-
var brain = mark.getAttribute("data-brain");
|
|
284
|
-
var key = Object.keys(sourceBrain).filter(function (k) { return sourceBrain[k] === brain; })[0];
|
|
285
|
-
var area = askHost.querySelector(".brain-box-scroll");
|
|
286
|
-
var row = key ? listedRow(area, key) : null;
|
|
287
|
-
if (!row || !area) return;
|
|
288
|
-
event.preventDefault();
|
|
289
|
-
var at = row.getBoundingClientRect(), box = area.getBoundingClientRect();
|
|
290
|
-
var top = area.scrollTop + at.top - box.top - area.clientHeight / 2 + at.height / 2;
|
|
291
|
-
if (area.scrollTo) area.scrollTo({ top: top, behavior: "smooth" }); else area.scrollTop = top;
|
|
292
|
-
row.removeAttribute("data-ask-flash");
|
|
293
|
-
void row.offsetWidth;
|
|
294
|
-
row.setAttribute("data-ask-flash", "");
|
|
295
|
-
setTimeout(function () { row.removeAttribute("data-ask-flash"); }, 1500);
|
|
296
|
-
}, true);
|
|
297
190
|
document.addEventListener("mouseover", function (e) { point(e, true); });
|
|
298
191
|
document.addEventListener("mouseout", function (e) { point(e, false); });
|
|
299
192
|
document.addEventListener("focusin", function (e) { point(e, true); });
|
package/src/story.css
CHANGED
|
@@ -784,6 +784,45 @@
|
|
|
784
784
|
width: min(100%, calc(100svh - var(--bar-height, 4.6rem) - 2rem));
|
|
785
785
|
aspect-ratio: 1;
|
|
786
786
|
}
|
|
787
|
+
/* On a wide screen the opening's drawing keeps its place beside the words
|
|
788
|
+
while the opening is read, an answer included, and leaves with the opening;
|
|
789
|
+
it fades once the reading has moved the figure on, as on a phone. In flow
|
|
790
|
+
with no height, so the words do not move; its width is the room right of
|
|
791
|
+
the words, as the absolute layer's was. */
|
|
792
|
+
@media not all and (max-width: 60rem) {
|
|
793
|
+
/* The words are the room's column (the kit scrolls it within the screen);
|
|
794
|
+
the bar and the opening's 4rem of padding above and below stay clear, so
|
|
795
|
+
the composer is on the first screen. */
|
|
796
|
+
.chapter--opening .opening__words {
|
|
797
|
+
--ask-column-clear: calc(8rem + var(--bar-height, 4.6rem));
|
|
798
|
+
}
|
|
799
|
+
:where(.chapter--opening) > .net-layer {
|
|
800
|
+
position: sticky;
|
|
801
|
+
order: -1;
|
|
802
|
+
top: calc(
|
|
803
|
+
var(--bar-height, 4.6rem) + (100svh - var(--bar-height, 4.6rem)) / 2
|
|
804
|
+
);
|
|
805
|
+
height: 0;
|
|
806
|
+
margin-left: calc(var(--words) + var(--words-gap));
|
|
807
|
+
width: calc(
|
|
808
|
+
100cqi - max(0px, (100cqi - 80rem) / 2) - clamp(1rem, 5vw, 5rem) -
|
|
809
|
+
var(--words) - var(--words-gap) - clamp(1rem, 6vw, 7rem)
|
|
810
|
+
);
|
|
811
|
+
transition: opacity 0.6s ease;
|
|
812
|
+
}
|
|
813
|
+
:where(.chapter--opening) > .net-layer > * {
|
|
814
|
+
translate: 0 -50%;
|
|
815
|
+
}
|
|
816
|
+
.story:has(.figure:not([data-stage="0"]))
|
|
817
|
+
:where(.chapter--opening)
|
|
818
|
+
> .net-layer,
|
|
819
|
+
.story:has(.figure:not([data-stage="0"])) .net-leads {
|
|
820
|
+
opacity: 0;
|
|
821
|
+
}
|
|
822
|
+
.net-leads {
|
|
823
|
+
transition: opacity 0.6s ease;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
787
826
|
.net-svg {
|
|
788
827
|
overflow: visible;
|
|
789
828
|
}
|
|
@@ -1416,46 +1455,21 @@
|
|
|
1416
1455
|
opacity: 0;
|
|
1417
1456
|
transition: opacity 0.6s ease;
|
|
1418
1457
|
}
|
|
1419
|
-
/* The
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
the first item of its scroll, a slot holding its place
|
|
1423
|
-
tapped dot flashes its source. */
|
|
1458
|
+
/* The Ask room's lead layer (the room script draws into it; the kit styles
|
|
1459
|
+
the leads): leads join words to the drawing across the page, so the
|
|
1460
|
+
viewport is their frame. On a phone the opening's drawing joins the open
|
|
1461
|
+
conversation as the first item of its scroll, a slot holding its place. */
|
|
1424
1462
|
.net-leads {
|
|
1425
|
-
position:
|
|
1463
|
+
position: fixed;
|
|
1426
1464
|
inset: 0;
|
|
1427
1465
|
z-index: 2;
|
|
1428
|
-
width:
|
|
1429
|
-
height:
|
|
1430
|
-
/* The chapter is the words' width; a lead runs past it to the drawing. */
|
|
1466
|
+
width: 100vw;
|
|
1467
|
+
height: 100vh;
|
|
1431
1468
|
overflow: visible;
|
|
1432
1469
|
pointer-events: none;
|
|
1433
1470
|
}
|
|
1434
|
-
.net-leads path {
|
|
1435
|
-
fill: none;
|
|
1436
|
-
stroke: var(--color-accent);
|
|
1437
|
-
stroke-width: 1.4;
|
|
1438
|
-
stroke-linecap: round;
|
|
1439
|
-
stroke-dasharray: 0.1 6;
|
|
1440
|
-
opacity: 0.85;
|
|
1441
|
-
}
|
|
1442
|
-
.opening__ask [data-ask-source][data-ask-flash] {
|
|
1443
|
-
animation: ask-flash 1.5s ease-out;
|
|
1444
|
-
}
|
|
1445
|
-
@keyframes ask-flash {
|
|
1446
|
-
0%,
|
|
1447
|
-
40% {
|
|
1448
|
-
background: rgb(from var(--color-accent) r g b / 0.14);
|
|
1449
|
-
}
|
|
1450
|
-
100% {
|
|
1451
|
-
background: transparent;
|
|
1452
|
-
}
|
|
1453
|
-
}
|
|
1454
1471
|
@media (max-width: 60rem) {
|
|
1455
|
-
|
|
1456
|
-
display: none;
|
|
1457
|
-
}
|
|
1458
|
-
.net-slot {
|
|
1472
|
+
[data-ask-slot] {
|
|
1459
1473
|
height: var(--strip-h);
|
|
1460
1474
|
}
|
|
1461
1475
|
.opening__ask[data-ask-sheet] [data-ask-dock]:has(.net-layer) {
|
|
@@ -1473,14 +1487,6 @@
|
|
|
1473
1487
|
border-bottom: 1px solid var(--color-border);
|
|
1474
1488
|
}
|
|
1475
1489
|
}
|
|
1476
|
-
@media (prefers-reduced-motion: reduce) {
|
|
1477
|
-
.net-leads {
|
|
1478
|
-
display: none;
|
|
1479
|
-
}
|
|
1480
|
-
.opening__ask [data-ask-source][data-ask-flash] {
|
|
1481
|
-
animation: none;
|
|
1482
|
-
}
|
|
1483
|
-
}
|
|
1484
1490
|
/* The chapter's drawing is the network at rest: no pulse travels it and no
|
|
1485
1491
|
brain blinks on its own; only the open question's brains answer. */
|
|
1486
1492
|
.asked .net-pulse,
|