wand-kit 0.1.0 → 0.3.0

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/PROMPT.md CHANGED
@@ -37,8 +37,11 @@ strokes) and `art` (which file plays which role). A component asks for a role
37
37
  `tokens.json` and `BRAND.md` differ, `BRAND.md` is right and `tokens.json` has
38
38
  drifted.
39
39
 
40
- The new brand is for internal testing until the FK Roman Standard licence for
41
- documents is settled (`brand-next/fonts/LICENSE-FONTS.md`).
40
+ `current` is the default while the two overlap; a job is on `next` when the
41
+ user asks for the new brand. The default is one constant, `DEFAULT_BRAND` in
42
+ `kit/lib/brand.js`, and `doctor` prints the rule from it. The FK Roman
43
+ Standard licence for documents is still to be confirmed
44
+ (`brand-next/fonts/LICENSE-FONTS.md`).
42
45
 
43
46
  ## 1. Surfaces — locked
44
47
 
@@ -140,6 +143,9 @@ The components (`node wand.js components`; `kit/lib/components.js`):
140
143
  | `stats`, `delta(from, to)` | figures, before-and-after |
141
144
  | `steps(items)` | next steps with arrows |
142
145
  | `callout`, `list`, `chapter`, `img`, `icon`, `tile` | the rest |
146
+ | `enter(kind, i)`, `step(n, html)`, `countup(text)`, `stat({ count })` | motion in a talk (§12) |
147
+ | `video({ src, poster })`, `demo({ src, poster })` | a clip or a live page on a slide (§12) |
148
+ | `storyboard({ title, shots })`, `sketch({ people, apps, agents })` | a board from a script, and a quick frame for it (§13) |
143
149
 
144
150
  **Composition is free.** A job writes the markup it needs from these, adds its
145
151
  own CSS on top (custom properties only) and may write raw markup where no
@@ -181,7 +187,9 @@ new ─▶ argue ─▶ content ─▶ compose ─▶ build ─▶ review ─▶
181
187
  6. **review** — look at `out/sheet.png`, then at any page the gates named.
182
188
  Read the pages as the reader will: does each carry its claim?
183
189
  7. **ship** — `node wand.js ship <job> [--pptx]`: the build, the one-file HTML,
184
- the PowerPoint. The review note says what was changed from the source and
190
+ the PowerPoint. A deck's HTML is a talk (§12), a storyboard's plays (§13),
191
+ and either runtime is driven before it ships. `--brand next` (or `current`)
192
+ builds the same job on the other brand into `out-<brand>/`. The review note says what was changed from the source and
185
193
  why, which gates ran, and what a human still has to decide.
186
194
 
187
195
  ## 7. Gates — every build, every job
@@ -199,7 +207,13 @@ new ─▶ argue ─▶ content ─▶ compose ─▶ build ─▶ review ─▶
199
207
  | pdf text | the PDF's text misses a line, or carries an em dash | |
200
208
  | contrast (warn) | text under WCAG AA against its background, chrome excepted | |
201
209
  | measure (warn) | a paragraph over the surface's characters-per-line | |
210
+ | images | an image did not load | an image that loaded and is the wrong one |
211
+ | media | a demo, a clip or its poster is missing | a demo that loads and misbehaves |
202
212
  | console | a page error | |
213
+ | runtime (ship, decks) | a slide or step the keyboard cannot reach, the overview or notes not opening, the presenter view out of step, a demo that never loads | how the motion feels |
214
+ | timing (warn, boards) | the board is more than 10% off its `target` | the real voiceover's pace |
215
+ | frames (warn, boards) | a frame is still to draw | a frame that is drawn and wrong |
216
+ | player (ship, boards) | the player does not run, scrub, or caption | |
203
217
 
204
218
  The gates read the DOM Chromium laid out for the PDF; nothing is inferred from
205
219
  the spec. What no gate sees: the argument, the pacing, and whether the right
@@ -245,8 +259,78 @@ component was chosen. That is the review.
245
259
  1. **DOCX export.** Declined for now: Word cannot hold these pages. If needed,
246
260
  a semantic export (headings, prose, tables) as an editable-text version,
247
261
  labelled as such.
248
- 2. **The sandbox's browser.** Confirm Chromium is present where sellers work
249
- before the kit replaces `decks` and `docs`.
250
- 3. **The chapter panel and the talk runtime** (presenter view, demos) from the
251
- BofA deck: carry into the slide surface when a talk needs them.
262
+ 2. **The sandbox's browser.** Confirmed 2026-10-19: claude.ai's sandbox carries
263
+ Playwright's Chromium in `/opt/pw-browsers`, which `findChrome()` reads, and
264
+ a page renders there exactly as it does on the Mac.
265
+ 3. **The talk runtime** from the BofA deck: carried into every slide deck's HTML
266
+ in 0.3.0 (§12). The chapter panel is still a component for a job to draw.
267
+ 4. **A scratch voice.** The Wand OS film timed its board against a voice from
268
+ macOS `say`; the chat sandbox has none, so timing here is words at `wpm`.
269
+ A recorded voiceover passed as `audio` leads the player.
252
270
  4. **Email and banner surfaces**: declared in §1's spirit, not yet in the table.
271
+
272
+ ## 12. Talks: motion and the presenter
273
+
274
+ Every slide deck's one-file HTML is a talk, with the runtime ported from the
275
+ BofA deck (`kit/lib/talk.js`): a crossfade between slides, entrances, steps,
276
+ media, the presenter view in a second window kept in step, the talk clock and
277
+ its pace, notes, the overview. The PDF, the PNGs and the PowerPoint never see
278
+ any of it: they show every slide at its end state, every step in, every
279
+ figure final, every poster.
280
+
281
+ - **Entrances, not effects.** By default (`talk: { motion: "auto" }`) a slide
282
+ that marks nothing has its blocks rise in, staggered. Mark a slide yourself
283
+ when one thing should lead: `enter("pop", 0)` on it and the rest follow.
284
+ `talk: { motion: "none" }` stills a deck; `motion: "none"` on a page stills
285
+ one slide. One orchestrated moment a slide; nothing loops, nothing bounces.
286
+ - **Kinds.** `rise` (the default), `fade`, `pop` for a mark or a figure,
287
+ `grow` for a bar going up, `widen` for a bar going across, `draw` for a
288
+ line that tells a trend (an SVG path with `pathLength="1"`), `count` for a
289
+ figure the room should watch arrive (`stat({ count: true })`, `countup()`).
290
+ - **Steps** when the order of the argument is the point: `step(1, html)`, then
291
+ 2, then 3. Never more than four on a slide. Going back undoes the last step.
292
+ - **Notes on every slide of a talk**, and `seconds` on each when the talk has a
293
+ slot: the presenter view shows the slide's window and whether you are on
294
+ pace. `talk: { minutes }` gives the clock a total when slides carry none.
295
+ - **A demo** is one self-contained HTML file (no network, no relative assets),
296
+ mounted in a frame when its slide comes up and the next one preloaded. It
297
+ may expose `window.wandDemo = { play, pause, reset }` for the D and R keys,
298
+ and dress itself from `window.wandEmbed.brand` (bg, card, stroke, ink, text,
299
+ label, accent), which the deck fills from its brand. Its poster is required:
300
+ `node wand.js poster demo.html --play 3 --brand <the deck's>`.
301
+ - **A clip** plays muted and looping while its slide is on; MP4 (H.264) plays
302
+ in every browser. The HTML carries it, so it carries its size: over 60 MB
303
+ warns.
304
+ - **The presenter's keys:** arrows, Space and PgUp/PgDn move (a clicker works),
305
+ P opens the presenter view, N the notes, G the overview, T the clock, B
306
+ blanks the screen, F goes full screen, D and R play and restart media, a
307
+ number then Enter jumps, ? lists them.
308
+
309
+ ## 13. Storyboards
310
+
311
+ A storyboard turns a script into a film's plan (`storyboard()`, generalised
312
+ from the Wand OS film in `runs/2026-10-06-wand-os-video`): a cover, the run
313
+ of show, then a page a shot with its number, name and timecode, the
314
+ voiceover word for word, one to four frames on their cue words, and the
315
+ direction (the line, camera, motion, on screen, sound, into next).
316
+
317
+ - **The script is the content file**, one voiceover line a shot. Split a long
318
+ paragraph where the edit will cut. Coverage then proves every word of the
319
+ script is on the board.
320
+ - **Timing is scratch timing:** each line lasts its words at `wpm` (150 for a
321
+ conversational read, 160 to 170 for a promo), with `lead` before and `tail`
322
+ after; a shot without voiceover `hold`s; `seconds` on a shot overrides the
323
+ estimate once a real read exists. `target` warns when the board is more
324
+ than 10% off. `timing.csv` and `captions.srt` ship with the board.
325
+ - **A frame is drawn four ways:** `sketch` (people as dots, software as
326
+ labelled boxes, agents as sparks: the script's own vocabulary, in the
327
+ brand's colours), the job's own SVG on brand roles, an image (a product
328
+ screen, a demo poster), or left to draw, which the frames gate counts. The
329
+ first frame is on screen from the cut; the others land on their cues.
330
+ - **The HTML plays the board:** the pages to read, and a player (P, or the
331
+ button) that cross-fades the frames on their cues with the voiceover as the
332
+ caption, a track of every shot to scrub, and a title-safe overlay. An
333
+ `audio` file, a scratch or final read, leads the clock when given.
334
+ - **Both brands:** the board is one job; `--brand next` builds it on the
335
+ website's brand beside the first. Frames drawn as sketches or on brand roles
336
+ follow; an image frame is per brand when it shows the brand.
package/SKILL.md CHANGED
@@ -1,19 +1,19 @@
1
1
  ---
2
- name: wand-studio
3
- description: Design any Wand sales or marketing material on either brand - a deck, a document, a one-pager, a case study, social cards - as PDF, PowerPoint and one-file HTML, with gates. Use when a .pptx, .docx or .pdf is attached, or branded output is wanted from a brief.
2
+ name: wand-design
3
+ description: Rebrand or create Wand material on the current or new brand - decks and talks, documents, case studies, storyboards from a script, social cards. Use when a file is attached or branded work is wanted.
4
4
  compatibility: Requires Node.js 18+, code execution enabled in claude.ai (Settings > Features), and a Chromium the kit can find (Playwright's cache or the system). No network needed once installed. Python 3 embeds fonts in the .pptx; without it the deck ships with the faces named, not embedded.
5
5
  license: Proprietary - Wand AI
6
6
  metadata:
7
- version: "0.1.0"
7
+ version: "0.3.0"
8
8
  ---
9
9
 
10
10
 
11
- # Wand studio
11
+ # Wand design
12
12
 
13
13
  ## First: find the kit and stand in it
14
14
 
15
15
  1. **Installed from npm** (the usual way): it is at `node_modules/wand-kit/`.
16
- If it is not there yet: `npm install wand-kit@next --no-audit --no-fund`.
16
+ If it is not there yet: `npm install wand-kit@latest --no-audit --no-fund`.
17
17
  2. **Attached to the chat as a zip**: unzip it and enter the folder it makes.
18
18
  3. **You are already inside it**: `wand.js` is next to this file.
19
19
 
@@ -35,16 +35,28 @@ PDF, the PNGs, the PowerPoint and the one-file HTML are all read from it.
35
35
  `PROMPT.md` is the design system: read it once for design decisions, not per
36
36
  page. `node wand.js components` lists the vocabulary.
37
37
 
38
- Two brands: `current` (Geist, the default) and `next` (the website's; for
39
- internal testing until its font licence is settled). Use `next` only when the
40
- user says *new brand*, *new guidelines* or *website look*.
38
+ ## Which brand
39
+
40
+ Two brands, one kit: `current` (Geist) and `next` (the website's). `doctor`
41
+ ends with the brand rule: which one is the default, and the words that ask
42
+ for the other. Follow it as printed. When the user names no brand, build on
43
+ the default without asking; when their words ask for the other, pass
44
+ `--brand` to `new`. One brand per piece, never a mix, and the review note
45
+ says which. The rule lives in the kit, not here, so it can change without
46
+ anyone reinstalling the skill.
47
+
48
+ **The same piece on the other brand:** `node wand.js ship <job> --brand next`
49
+ (or `current`) builds the job again on that brand, into `out-<brand>/`
50
+ beside the first. Nothing in the job changes.
41
51
 
42
52
  ## Read only what the job needs
43
53
 
44
54
  | Job | Read |
45
55
  |---|---|
46
56
  | A document, a two-pager, a case study | `PROMPT.md` §5 and `templates/page.mjs`; `examples/overview/job.mjs` for a finished one |
47
- | A deck or a talk | §5 and `templates/slide.mjs` |
57
+ | A deck | §5 and `templates/slide.mjs` |
58
+ | An animated deck, a talk, a live demo | §12 and `templates/slide.mjs`; `examples/talk/job.mjs` for a finished one |
59
+ | A storyboard from a script | §13 and `templates/storyboard.mjs`; `examples/storyboard/job.mjs` |
48
60
  | Social cards, a banner | `templates/card.mjs` |
49
61
  | Edit a job this kit built | the job's `job.mjs` only, then `build` |
50
62
 
@@ -57,8 +69,8 @@ user says *new brand*, *new guidelines* or *website look*.
57
69
  text runs in order, or use the decks kit's `tools/extract.py` if present.
58
70
  2. **Write the page list in the chat before any markup**: one line per page
59
71
  with the claim it carries and which source lines it takes. Then compose.
60
- 3. `node wand.js new work/<name> --surface letter|slide --brand current`,
61
- write `pages(c)` with `c.L(n)` for every string, declare every departure
72
+ 3. `node wand.js new work/<name> --surface letter|slide` (plus `--brand` only
73
+ when the brand rule says so), write `pages(c)` with `c.L(n)` for every string, declare every departure
62
74
  in `edits` (no em dashes: swap them for commas or semicolons there) and
63
75
  every line left out in `cut`.
64
76
  4. `node wand.js build work/<name>/job.mjs` until every hard gate is ok. Look
@@ -79,22 +91,64 @@ Author from the client piece: remove the client's figures, names, quotes and
79
91
  vendors; put the client's name and its people in `denylist` so the gate proves
80
92
  they are gone. Say in the note what was cut and what was generalised.
81
93
 
94
+ ### An animated deck or a talk
95
+
96
+ Every deck's HTML already animates: blocks rise in, slides crossfade, and the
97
+ presenter has notes, a clock, an overview and a presenter view (P). What a
98
+ talk adds:
99
+
100
+ 1. Notes on every slide, and `seconds` on each when the talk has a slot.
101
+ 2. Motion where it carries the argument (PROMPT.md §12): `step(n, html)` for
102
+ a reveal in order, `stat({ count: true })` for a figure to watch arrive,
103
+ `enter("draw")` on a trend line, `enter("grow", i)` on bars. Restraint: one
104
+ moment a slide.
105
+ 3. A live demo: one self-contained HTML file, then its poster on the deck's
106
+ brand: `node wand.js poster demo.html --play 3 --brand <brand>`, and
107
+ `c.demo({ src, poster })` on the slide. A clip: `c.video({ src, poster })`.
108
+ 4. `ship` drives the HTML like a presenter before it ships (the runtime row).
109
+ Deliver the HTML as the deck to present, with the PDF and .pptx beside it.
110
+
111
+ ### A storyboard from a script
112
+
113
+ 1. The script into `content.txt`, one voiceover line a shot (split where the
114
+ edit will cut). `node wand.js new work/<name> --surface storyboard`.
115
+ 2. **Write the shot list in the chat first**: act, name, the line, and what
116
+ each frame shows. Then the job: `c.storyboard({ title, wpm, target, shots })`.
117
+ 3. Frames: `sketch` for a quick one in the brand's colours, the job's own SVG,
118
+ an image, or left to draw (the frames gate counts them).
119
+ 4. `ship`: the board as PDF and .pptx, `timing.csv`, `captions.srt`, and the
120
+ HTML with its player. Say in the note that timing is scratch timing at the
121
+ `wpm` given until a real read exists, and which frames are still to draw.
122
+
82
123
  ### Social cards
83
124
 
84
125
  `--surface square` or `og`; one `c.page()` per card; `out/pages/pNN.png` are
85
126
  the deliverables.
86
127
 
128
+ ### Older Wand work
129
+
130
+ A deck or document made before this kit has no `job.mjs`. Treat it as a
131
+ source and rebuild it (the first job above), on the brand the rule gives.
132
+ One exception: a small edit (a date, a word, a figure) to a deck the old
133
+ decks kit built, where they want the file otherwise untouched. For that edit
134
+ only, `npm install wand-decks-kit@0.11.0 --no-audit --no-fund` and follow
135
+ that kit's SKILL.md on editing (`node wand.js show`, then `edit --set`).
136
+ An older copy of this skill may describe that kit, a routing table between
137
+ two kits, or `edit --set`; none of it applies to anything else.
138
+
87
139
  ## The review note, always
88
140
 
89
- What was changed from the source and why (every edit, every cut); the gate
90
- table as printed; what a human still has to decide (a figure to confirm, a
141
+ Which brand it is on; what was changed from the source and why (every edit,
142
+ every cut); the gate table as printed; what a human still has to decide (a figure to confirm, a
91
143
  quote to clear, a claim legal should see). A file delivered without the gate
92
144
  table has been checked by nobody.
93
145
 
94
146
  ## Available scripts
95
147
 
96
- - **`wand.js`** — doctor, new, build, check, ship, watch, gallery, components, brands
148
+ - **`wand.js`** — doctor, new, build, check, ship, watch, poster, gallery, components, brands
97
149
  - **`lib/engine.js`** — the render and the gates
98
150
  - **`lib/components.js`** — the vocabulary and its CSS
99
151
  - **`lib/pptx.js`**, **`tools/embed_fonts.py`** — the PowerPoint export
100
152
  - **`lib/html_export.js`** — the one-file HTML with its runtime
153
+ - **`lib/talk.js`**, **`lib/board.js`** — the talk and the storyboard runtimes
154
+ - **`lib/runtime_check.js`** — the HTML driven like a presenter, at ship
package/START-HERE.md CHANGED
@@ -2,12 +2,18 @@
2
2
 
3
3
  ```bash
4
4
  node wand.js doctor # what works here; chromium must name a binary
5
- node wand.js new work/acme --surface letter --brand current
5
+ node wand.js new work/acme --surface letter # the default brand; doctor prints the rule
6
6
  # write work/acme/job.mjs (pages(c)) and, for a rebuild, work/acme/content.txt
7
7
  node wand.js build work/acme/job.mjs # gates, PDF, a PNG per page, out/sheet.png
8
8
  node wand.js ship work/acme/job.mjs --pptx # + one-file HTML, PowerPoint
9
+ node wand.js ship work/acme/job.mjs --brand next # the same job on the other brand, in out-next/
9
10
  ```
10
11
 
12
+ A deck's HTML is a talk: entrances, steps, a presenter view (P). A demo needs
13
+ a poster: `node wand.js poster demo.html --play 3`. A storyboard starts from
14
+ `node wand.js new work/film --surface storyboard`; its HTML plays the board.
15
+ `examples/talk/` and `examples/storyboard/` are finished ones.
16
+
11
17
  `node wand.js components` lists the vocabulary; `examples/overview/job.mjs` is
12
18
  a finished document and `examples/gallery/job.mjs` every component on one
13
19
  sheet (`node wand.js gallery --brand next` draws it on the website's brand).
@@ -4,7 +4,9 @@
4
4
  * It is the regression for the component library: a change to components.js
5
5
  * or to a brand's tokens is reviewed here once, on one sheet, before any job.
6
6
  */
7
- const brand = process.env.WAND_GALLERY_BRAND || "current";
7
+ import { DEFAULT_BRAND } from "../../lib/brand.js";
8
+
9
+ const brand = process.env.WAND_GALLERY_BRAND || DEFAULT_BRAND;
8
10
 
9
11
  export default {
10
12
  name: `Gallery ${brand}`,
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The storyboard example — a 30-second film from a six-line script, the
3
+ * regression for the storyboard component and lib/board.js.
4
+ *
5
+ * The script is the content file, one voiceover line a shot, so coverage
6
+ * proves every word of it is on the board. Frames show each way a frame can
7
+ * be drawn: a sketch in the brand's colours (people, software, agents), the
8
+ * job's own SVG, an image (the talk example's demo poster), and one left to
9
+ * draw, which the frames gate counts.
10
+ *
11
+ * node wand.js ship examples/storyboard/job.mjs the current brand
12
+ * node wand.js ship examples/storyboard/job.mjs --brand next the website's, in out-next/
13
+ *
14
+ * Outputs: the board as PDF and PNGs, timing.csv, captions.srt, and one HTML
15
+ * file with the board to read and a player that runs it against the clock.
16
+ */
17
+ export default {
18
+ name: "The dual workforce, a 30-second film",
19
+ file: "Wand_Dual_Workforce_Storyboard",
20
+ brand: "current",
21
+ surface: "slide",
22
+ tag: "Wand · Storyboard",
23
+ footer: "Wand AI · Draft for review",
24
+ content: "script.txt",
25
+ css: `
26
+ .frame-art .own .ring{fill:none;stroke:var(--stroke);stroke-width:6}
27
+ .frame-art .own .core{fill:var(--accent)}
28
+ .frame-art .own text{fill:var(--ink);font-family:var(--face-display);font-size:96px}
29
+ `,
30
+ pages: (c) => c.storyboard({
31
+ title: "The dual workforce",
32
+ lede: "A 30-second film for the website and LinkedIn. Six shots, three acts.",
33
+ wpm: 160,
34
+ target: 30,
35
+ shots: [
36
+ { act: "Today", name: "Two things", vo: c.L(1),
37
+ line: "Every company is people plus software; we show both, plainly.",
38
+ camera: "Locked off, wide.", motion: "People dots settle in; three app boxes slide up.", sound: "Low pad comes in.", next: "Cut on 'software'.",
39
+ frames: [
40
+ { sketch: { people: 6, label: "People" }, caption: "The team, as the script draws it: dots." },
41
+ { cue: "software", sketch: { people: 6, apps: ["CRM", "ERP", "Email"], label: "People and software" }, caption: "The systems arrive beside them." },
42
+ ] },
43
+ { act: "Today", name: "Software waits", vo: c.L(2),
44
+ line: "Software only moves when a person moves it.",
45
+ camera: "Slow push in on the boxes.", motion: "An arrow from the people to each box, one at a time.", onscreen: "30 years", sound: "A single click per arrow.", next: "Hold, then dip.",
46
+ frames: [{ cue: "waited", sketch: { people: 3, apps: ["CRM", "ERP"], label: "Every task starts with a person" } }] },
47
+ { act: "The arrival", name: "A new kind of worker", vo: c.L(3),
48
+ line: "The turn of the film: something new enters the frame.",
49
+ camera: "Pull out to reveal the right third.", motion: "A spark draws itself in, then pulses once.", sound: "Rising tone, then silence.", next: "Match cut on the spark.",
50
+ frames: [{ cue: "worker", art: `<svg class="own" viewBox="0 0 1920 1080"><circle class="ring" cx="960" cy="500" r="230"/><circle class="ring" cx="960" cy="500" r="330"/><path class="core" d="M960 380Q990 470 1080 500Q990 530 960 620Q930 530 840 500Q930 470 960 380Z"/><text x="960" y="930" text-anchor="middle">A new worker</text></svg>`, caption: "The job's own SVG, on brand roles." }] },
51
+ { act: "The arrival", name: "Agents at work", vo: c.L(4),
52
+ line: "Agents do the work inside the systems people already use.",
53
+ camera: "Over the shoulder of the boxes.", motion: "Sparks move into the boxes; the boxes light up.", onscreen: "Read · Decide · Act", sound: "Three soft ticks.", next: "Cut.",
54
+ frames: [
55
+ { sketch: { apps: ["CRM", "ERP", "Email"], agents: 3, label: "Agents inside the systems" } },
56
+ { cue: "act", img: c.brand === "next" ? "../talk/agents-demo.poster-next.png" : "../talk/agents-demo.poster.png", caption: "The live product, as it looks on screen." },
57
+ ] },
58
+ { act: "Wand", name: "A place in the organisation", vo: c.L(5),
59
+ line: "Wand is what makes an agent a colleague: identity, manager, budget, record.",
60
+ camera: "Locked off.", motion: "Four labels type in under the spark, one per beat of the voiceover.", onscreen: "Identity · Manager · Budget · Record", sound: "One tick per label.", next: "Dissolve.",
61
+ frames: [
62
+ { cue: "identity", sketch: { agents: 1, apps: ["Identity", "Manager"], label: "An agent, provisioned" } },
63
+ { cue: "budget", sketch: { agents: 1, apps: ["Budget", "Record"], label: "Governed like a person" } },
64
+ { cue: "record", caption: "End card for the governance beat: to draw." },
65
+ ] },
66
+ { act: "Wand", name: "One workforce", vo: c.L(6),
67
+ line: "The close: people and agents in one picture, then the wordmark.",
68
+ camera: "Slow pull out to the full frame.", motion: "Dots and sparks gather into one grid; the wordmark fades up.", sound: "Pad resolves.", next: "End.",
69
+ frames: [{ cue: "workforce", sketch: { people: 6, agents: 4, label: "One workforce", arrows: false } }] },
70
+ ],
71
+ }),
72
+ };
@@ -0,0 +1,6 @@
1
+ Every company runs on two things: people and software.
2
+ For thirty years, software waited for people to tell it what to do.
3
+ Now a new kind of worker has arrived.
4
+ AI agents that read, decide and act, inside the systems you already run.
5
+ Wand gives each one a place in your organisation: an identity, a manager, a budget, a record.
6
+ One workforce. People and agents, working as one.
@@ -0,0 +1,69 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Agents at work</title>
6
+ <!--
7
+ A live demo for a talk deck: requests arrive, agents take them, a person
8
+ approves the ones that need a person. Self-contained, as a demo must be to
9
+ travel inside the deck. It dresses itself in the brand the deck hands it
10
+ (window.wandEmbed.brand) and answers the deck's D and R keys through
11
+ window.wandDemo = { play, pause, reset }.
12
+ -->
13
+ <style>
14
+ :root { --bg:#1A1033; --card:#291D5A; --stroke:#5B4BB0; --ink:#FFFFFF; --text:#F5F5F5; --label:#746B98; --accent:#9481FF; }
15
+ * { box-sizing: border-box; }
16
+ html, body { margin: 0; height: 100%; }
17
+ body { background: var(--bg); color: var(--text); font: 15px/1.4 system-ui, -apple-system, "Segoe UI", sans-serif; overflow: hidden; }
18
+ main { height: 100%; display: grid; grid-template-columns: 1fr 1.25fr 1fr; gap: 18px; padding: 26px; }
19
+ section { display: flex; flex-direction: column; gap: 10px; min-width: 0; padding: 16px; border: 1px solid var(--stroke); border-radius: 14px; background: color-mix(in srgb, var(--card) 55%, transparent); }
20
+ h2 { margin: 0 0 4px; font-size: 12px; letter-spacing: .14em; text-transform: uppercase; color: var(--label); display: flex; justify-content: space-between; }
21
+ h2 b { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: 0; }
22
+ .lane { display: flex; flex-direction: column; gap: 8px; overflow: hidden; flex: 1; }
23
+ .task { padding: 10px 12px; border-radius: 10px; background: var(--card); border: 1px solid var(--stroke); color: var(--ink); font-size: 14px; display: flex; justify-content: space-between; gap: 10px; animation: in 380ms cubic-bezier(.16,1,.3,1) both; }
24
+ .task i { font-style: normal; color: var(--label); font-size: 12px; white-space: nowrap; }
25
+ .task.agent { border-color: var(--accent); }
26
+ .task.agent i { color: var(--accent); }
27
+ .task.human i { color: var(--ink); }
28
+ .bar { height: 3px; border-radius: 2px; background: var(--stroke); overflow: hidden; margin-top: 6px; }
29
+ .bar > span { display: block; height: 100%; width: 0; background: var(--accent); }
30
+ @keyframes in { from { opacity: 0; transform: translateY(8px); } }
31
+ </style>
32
+ </head>
33
+ <body>
34
+ <main>
35
+ <section><h2>Incoming <b id="n-in">0</b></h2><div class="lane" id="in"></div></section>
36
+ <section><h2>Agents working <b id="n-work">0</b></h2><div class="lane" id="work"></div></section>
37
+ <section><h2>Done <b id="n-done">0</b></h2><div class="lane" id="done"></div></section>
38
+ </main>
39
+ <script>
40
+ (() => {
41
+ const E = window.wandEmbed || {}, B = E.brand || {};
42
+ for (const k of ["bg", "card", "stroke", "ink", "text", "label", "accent"]) if (B[k]) document.documentElement.style.setProperty("--" + k, B[k]);
43
+ const KINDS = ["Invoice match", "Refund request", "Address change", "Claim review", "Contract renewal", "Access request", "Vendor onboarding", "Policy question"];
44
+ const lanes = { in: document.getElementById("in"), work: document.getElementById("work"), done: document.getElementById("done") };
45
+ let seq = 0, timer = null, tick = 0, done = 0;
46
+ const count = () => { for (const k of ["in", "work", "done"]) document.getElementById("n-" + k).textContent = k === "done" ? done : lanes[k].children.length; };
47
+ const card = (txt, cls, tag) => { const d = document.createElement("div"); d.className = "task " + cls; d.innerHTML = "<span>" + txt + "</span><i>" + tag + "</i>"; return d; };
48
+ function step() {
49
+ tick++;
50
+ if (lanes.in.children.length < 6) lanes.in.append(card(KINDS[seq++ % KINDS.length] + " #" + (1040 + seq), "", "queued"));
51
+ if (tick % 2 === 0 && lanes.in.firstElementChild && lanes.work.children.length < 4) {
52
+ const t = lanes.in.firstElementChild, name = t.firstElementChild.textContent, human = seq % 5 === 0;
53
+ t.remove(); const w = card(name, human ? "human" : "agent", human ? "needs a person" : "agent"); const bar = document.createElement("div"); bar.className = "bar"; bar.innerHTML = "<span></span>"; w.firstElementChild.append(bar); lanes.work.append(w);
54
+ }
55
+ [...lanes.work.children].forEach((w) => { const s = w.querySelector(".bar > span"); const v = (parseFloat(s.style.width) || 0) + (w.classList.contains("human") ? 9 : 22); s.style.width = Math.min(100, v) + "%"; if (v >= 100) { w.remove(); done++; lanes.done.prepend(card(w.firstElementChild.firstChild.textContent, w.classList.contains("human") ? "human" : "agent", w.classList.contains("human") ? "approved" : "resolved")); if (lanes.done.children.length > 7) lanes.done.lastElementChild.remove(); } });
56
+ count();
57
+ }
58
+ const api = {
59
+ play() { if (!timer) timer = setInterval(step, E.motion === false ? 900 : 450); },
60
+ pause() { clearInterval(timer); timer = null; },
61
+ reset() { api.pause(); for (const l of Object.values(lanes)) l.replaceChildren(); seq = 0; tick = 0; done = 0; for (let i = 0; i < 8; i++) step(); },
62
+ };
63
+ window.wandDemo = api;
64
+ api.reset();
65
+ if (E.motion !== false) api.play();
66
+ })();
67
+ </script>
68
+ </body>
69
+ </html>
@@ -0,0 +1,85 @@
1
+ /**
2
+ * The talk example — an animated deck, the regression for lib/talk.js.
3
+ *
4
+ * Every motion the runtime knows, once: entrances staggered in for a slide
5
+ * that marks nothing (`talk.motion: "auto"`), figures that count up, a chart
6
+ * whose line draws and whose bars grow, steps the presenter brings in one at
7
+ * a time, and a live demo in a frame with its poster for the static exports.
8
+ * `seconds` on each slide gives the presenter's clock a pace to keep.
9
+ *
10
+ * node wand.js ship examples/talk/job.mjs --pptx the current brand
11
+ * node wand.js ship examples/talk/job.mjs --pptx --brand next the website's, in out-next/
12
+ * node wand.js poster examples/talk/agents-demo.html --play 3 [--brand next]
13
+ *
14
+ * The demo's poster is per brand, because the demo dresses itself in the
15
+ * deck's colours: agents-demo.poster.png and agents-demo.poster-next.png.
16
+ */
17
+ import { DEFAULT_BRAND } from "../../lib/brand.js";
18
+
19
+ const brandOf = (c) => c.brand || DEFAULT_BRAND;
20
+
21
+ export default {
22
+ name: "Agents at work",
23
+ file: "Wand_Agents_at_Work_Talk",
24
+ brand: "current",
25
+ surface: "slide",
26
+ tag: "Wand",
27
+ footer: "Wand AI · Example talk",
28
+ talk: { motion: "auto", minutes: 8 },
29
+ css: `
30
+ .chart{width:100%;height:auto;display:block}
31
+ .chart .axis{stroke:var(--rule);stroke-width:2}
32
+ .chart .line{fill:none;stroke:var(--accent);stroke-width:6;stroke-linecap:round;stroke-linejoin:round}
33
+ .chart .bar{fill:var(--emph)}
34
+ .chart text{fill:var(--soft);font-family:var(--face-body);font-size:22px}
35
+ .demo-wrap{margin-top:calc(var(--m-gap) * .6);width:66%}
36
+ `,
37
+ pages: (c) => [
38
+ c.cover({
39
+ label: "Example talk",
40
+ title: `Agents at <em>work</em>`,
41
+ lede: "What changes when software stops waiting to be told.",
42
+ meta: "An animated deck from the Wand studio",
43
+ notes: "Open on the question. Nothing on this slide needs explaining; let the title land.",
44
+ }),
45
+
46
+ c.page({ eyebrow: "The shift", seconds: 60, notes: "Left is every company today. Right is what we deploy. Pause on the word 'colleagues'.", body: `
47
+ ${c.h1(`Two <em>models</em>`)}
48
+ ${c.cols(2, [
49
+ c.card({ tag: "Today", title: "Software waits", level: "h3", body: [`People read, decide and act. Software holds the records and waits to be told.`] }),
50
+ c.card({ tag: "With agents", title: "Software works", level: "h3", emph: true, body: [`Agents read, decide and act inside the same systems, and people manage them like colleagues.`] }),
51
+ ])}
52
+ ` }),
53
+
54
+ c.page({ eyebrow: "What it adds up to", seconds: 50, notes: "The three figures count up as the slide arrives. Read the first one out; the room reads the rest.", body: `
55
+ ${c.h1("In the first year")}
56
+ ${c.stats([{ v: "62%", k: "of routine requests closed by agents", hero: true, count: true }, { v: "4,800", k: "hours a month back to the team", count: true }, { v: "3.5x", k: "faster from request to resolution", count: true }])}
57
+ ` }),
58
+
59
+ c.page({ eyebrow: "How it rolls out", seconds: 90, notes: "Three steps; press once for each. Do not jump ahead: each step is the reason for the next.", body: `
60
+ ${c.h1("One step at a time")}
61
+ ${c.cols(3, [
62
+ c.step(1, c.card({ tag: "Step 1", title: "Give agents an identity", body: ["Every agent is provisioned like a person: an owner, access, a record."] })),
63
+ c.step(2, c.card({ tag: "Step 2", title: "Start where the volume is", body: ["Pick the work that is high in volume and low in judgement."] })),
64
+ c.step(3, c.card({ tag: "Step 3", title: "Widen what they may decide", body: ["Raise the limits as the record earns it."], emph: true })),
65
+ ])}
66
+ ` }),
67
+
68
+ c.page({ eyebrow: "The curve", seconds: 60, notes: "The line draws itself: resolution time falling as agents take the volume. The bars are the agents' share.", body: `
69
+ ${c.h1("Time to resolve, month by month")}
70
+ <svg class="chart" viewBox="0 0 1600 520" role="img" aria-label="Resolution time falls while the agents' share of requests rises">
71
+ <line class="axis" x1="60" y1="460" x2="1560" y2="460"/>
72
+ ${[18, 26, 35, 44, 52, 58, 62].map((v, i) => `<rect class="bar"${c.enter("grow", i)} x="${120 + i * 205}" y="${460 - v * 5}" width="110" height="${v * 5}"/>`).join("")}
73
+ <path class="line" pathLength="1"${c.enter("draw", 2)} d="M175 120 L380 170 L585 230 L790 300 L995 350 L1200 385 L1405 400"/>
74
+ ${["M1", "M2", "M3", "M4", "M5", "M6", "M7"].map((m, i) => `<text x="${175 + i * 205}" y="500" text-anchor="middle">${m}</text>`).join("")}
75
+ </svg>
76
+ ` }),
77
+
78
+ c.page({ eyebrow: "Live", seconds: 120, notes: "The demo is live: press D to pause or play it, R to restart. Esc gives the keys back to the deck.", body: `
79
+ ${c.h1(`Watch them <em>work</em>`, "md")}
80
+ <div class="demo-wrap">${c.demo({ src: "agents-demo.html", poster: brandOf(c) === "next" ? "agents-demo.poster-next.png" : "agents-demo.poster.png", label: "Agents at work, live" })}</div>
81
+ ` }),
82
+
83
+ c.closing({ pair: "Wand", title: "Agents at work", url: "www.wand.ai", eyebrow: "Thank you" }),
84
+ ],
85
+ };