@navipartner/demo-video 0.7.0 → 0.7.2

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.
@@ -1,103 +1,166 @@
1
1
  # The look
2
2
 
3
- Every NaviPartner demo video should look and feel the same, in order of importance for the visuals:
3
+ Every NaviPartner demo video should look and feel the same. They are made
4
+ for serious buyers: operators and owners choosing a system for their shops,
5
+ restaurants and venues. They watch to understand, so a video is brisk where
6
+ nothing is happening and gives information the time it needs. In order of
7
+ importance:
4
8
 
5
- 1. **The product tells the story; text only frames it.** No caption pills
6
- over the UI. Words appear on their own cards *between* the chapters, never
7
- on top of the app.
8
- 2. **Calm, purposeful camera.** It moves between shots, never during the
9
- action: one smooth push-in on the area where the chapter's action and its
10
- result happen, held steady through the clicks, then back out. It never
11
- chases the cursor, and there are no hard cuts inside a chapter.
12
- 3. **Soft, light, branded.** A pale brand-tinted background, a floating app
13
- window, serif statements, and one brand colour used boldly once or twice.
14
- 4. **Real-looking data.** Believable names, orders and products in the
15
- customer's language and market. Never "Test item 1".
9
+ 1. **The product tells the story; text frames it.** Words live on their own
10
+ cards *between* the chapters, never over the live app. A card may hold a
11
+ snapshot of what its chapter achieves beside the words: the app, captured,
12
+ not a mock-up.
13
+ 2. **No dead time.** Setup happens behind cards, only values that show
14
+ something are typed, and every beat is there so the viewer sees
15
+ something. Never rushed: results and cards stay up long enough to read.
16
+ 3. **Purposeful camera.** Every page opens wide, on the whole window. The
17
+ camera pushes in only where the action would be too small to read, and
18
+ then only after the viewer has seen where they are. It moves between
19
+ shots, never during the action, never chases the cursor, and there are no
20
+ hard cuts inside a chapter.
21
+ 4. **Soft, light, branded.** A pale brand-tinted background, a floating app
22
+ window, big serif statements, and the brand colour used boldly.
23
+ 5. **Believable data.** The most real-looking records the e2e mocks have.
24
+ Never "Test item 1" on screen when the story can type a better value.
16
25
 
17
- ## Structure
26
+ ## Two kinds of video
18
27
 
19
- A video is a sequence of **chapters**. Each chapter is one idea the viewer
20
- should remember.
28
+ A story says which it is (`"kind"`, default `showcase`). The beats are the
29
+ same in both; the shape differs.
30
+
31
+ | | Showcase | Feature clip |
32
+ |---|---|---|
33
+ | Shows | the app's main journey, from start to the moment of value (the primary story is always one) | one feature: "see the new split-bill button" |
34
+ | For | the website, a sales meeting, a buyer comparing systems | release news, a LinkedIn post, a customer update |
35
+ | Length | 45–90 s | 15–30 s |
36
+ | Shape | open card, 2–4 chapters of 10–20 s, a payoff, end card | open card naming the feature, one chapter, end card |
37
+ | Open and end cards | up to 3.5 s | up to 2.5 s |
38
+
39
+ `check` keeps a feature clip to one chapter and a showcase to four, and
40
+ prints how long the story will run.
41
+
42
+ ### Showcase
21
43
 
22
44
  ```
23
- open card 2–4 s eyebrow "Introducing" + wordmark/logo; tagline fades in
24
- ┌ statement 2–3 s "A customer *scans* their basket."
25
- │ demo 10–25 s the app doing exactly that, camera following
26
- └ payoff 3–5 s optional: what just happened, as a fact
45
+ open card ≤ 3.5 s logo + what the product does for the buyer + a snapshot of the result
46
+ ┌ chapter card ~2–4 s "Pay *by card*." + the benefit in one line, beside a snapshot of the result
47
+ │ demo 10–20 s the app doing exactly that, ending on the result
48
+ └ payoff ~3–4 s optional: the result just shown, as a fact
27
49
  … 2–4 chapters …
28
- tagline 2–3 s one line on brand colour: "Checkout that runs itself."
29
- end card 3–5 s logo + tagline, still
50
+ end card ≤ 3.5 s logo + the promise, or the next step
51
+ ```
52
+
53
+ ### Feature clip
54
+
55
+ ```
56
+ open card ≤ 2.5 s logo + "New: …" + a snapshot of the feature
57
+ ┌ chapter card ~2–3 s the feature's statement + what it does for the customer
58
+ │ demo 8–15 s straight to the feature; all setup behind the card
59
+ end card ≤ 2.5 s logo + where it is available
30
60
  ```
31
61
 
32
- - 30 s is enough for one chapter, 60–95 s for three or four. Length follows
33
- the chapters. Don't pad, don't cram.
34
- - The first chapter's statement lands by second 6. The hook is the
35
- open card plus the first statement, and nothing else.
36
- - A chapter ends wide (the whole window), so the next card has something to
37
- dissolve from.
62
+ - **The first seconds carry brand and product.** The open card shows the
63
+ logo and a snapshot of the result (a showcase) or of the feature (a clip),
64
+ so a viewer who stops after two seconds has seen both.
65
+ - Setup the viewer needn't watch (a language dialog, a cookie banner,
66
+ opening a tab) happens behind the card before it, never on screen.
67
+ - Length follows the chapters: don't pad, don't cram.
38
68
 
39
- ## Text cards
69
+ ## Cards
40
70
 
41
71
  | Card | Text | Look |
42
72
  |---|---|---|
43
- | Open | eyebrow `Introducing` (small sans, grey) · brand wordmark/logo · tagline | pale background, centred |
44
- | Statement | ≤ 7 words naming what the next chapter shows, sentence case, ends with a full stop, **one word or phrase in italic** (the action or its result): "Pay *by card*." | dark serif on pale background |
45
- | Payoff | the result just shown as heading; sub-line states the concrete fact the app showed: "64,50 kr. by card." | white serif on full-bleed brand colour |
46
- | Stat | a number + what it counts: "60+ integrations" | pale, optional logo grid |
47
- | Tagline | the product's promise in ≤ 5 words | brand colour or pale, alternating |
48
- | End | logo + one-line tagline | pale, held still |
49
-
50
- - **Typeface:** the brand's fonts. For NaviPartner that's **Merriweather** for
51
- headings (its italic is the emphasis word) and **Work Sans** for eyebrows and
52
- sub-lines.
53
- At 1080p: logo ~110 px tall equivalent, statements ~70 px, sub-lines
54
- ~18–22 px.
55
- - **Tone:** plain, confident, present tense. No exclamation marks except
56
- inside a quoted customer line. No "seamless", "revolutionary" or
57
- "effortless".
58
- - **Truth:** every fact on a card must be visible in the video itself (the
59
- price shown, the receipt that appears), checked by an `expectText`. No
60
- invented durations or customer statistics. Stat cards only state real
61
- product facts, such as the number of payment methods the app supports.
73
+ | Open | eyebrow (`Introducing`, or `New` for a clip) · brand logo · one line on what it does for the buyer · a snapshot of the result | pale |
74
+ | Chapter | the statement and the benefit (below), beside a snapshot of the result when there is one | pale and brand colour alternate, chapter by chapter |
75
+ | Payoff | the result just shown; the sub-line states the fact the app showed: "64,50 kr. by card." | full-bleed brand colour |
76
+ | End | logo + the product's promise, or the next step: "Book a demo at navipartner.com." | pale, held still |
77
+
78
+ A **chapter card** has two lines of words, and no eyebrow: chapter numbers
79
+ and topic labels don't help in a film this short.
80
+
81
+ 1. **Statement:** ≤ 7 words naming what the chapter shows, in its own verb,
82
+ sentence case, ending with a full stop, **one word or phrase in italic**
83
+ (the action or its result): "Pay *by card*."
84
+ 2. **Benefit:** one sub-line, ideally ≤ 12 words, saying what it does for
85
+ the customer as something the chapter then shows. An outcome, not a
86
+ feature name, and a specific value when the app shows one: "Tap, insert
87
+ or phone: the terminal takes it all."
88
+
89
+ **Text and UI on one card.** A card shows a snapshot of the result beside
90
+ the words when there is something visual to show: the finished pass, the
91
+ receipt, the "Paid" screen. The words go left, the snapshot right, in the
92
+ same rounded window and soft shadow as the app. It is the app, captured by
93
+ `check`, never a mock-up. A chapter that ends on a toast or a number stays
94
+ text only, centred.
95
+
96
+ - **Typeface:** the brand's fonts. For NaviPartner: **Merriweather** for
97
+ statements (its italic is the emphasis word), **Work Sans** for eyebrows
98
+ and sub-lines. At 1080p: statements 116 px (92 px beside a snapshot),
99
+ sub-lines 40 px (36 px), the open card's eyebrow 28 px spaced capitals,
100
+ logo 720 px wide.
101
+ - **Tone:** plain, confident, present tense, for a professional audience.
102
+ No exclamation marks except inside a quoted customer line. No "seamless",
103
+ "revolutionary" or "effortless".
104
+ - **Truth:** every fact on a card is visible in the video (the price shown,
105
+ the receipt that appears), checked by an `expectText`. No invented
106
+ durations or customer statistics.
62
107
  - **Never on a card:** testids, barcodes, internal names or any other code.
63
- - **Reading time:** a card stays up at least 1 s plus ~0.3 s a word.
108
+ - **Reading time:** a card stays up 0.5 s plus ~0.25 s a word of its
109
+ statement and ~0.2 s a word of its sub-line (an eyebrow's words 0.12 s),
110
+ at least 1.6 s. The open and end cards stay at most 3.5 s in a showcase
111
+ and 2.5 s in a clip, unless the story asks for more. Setup behind a card
112
+ that takes longer holds it up: `check` says so.
64
113
 
65
114
  ## Transitions
66
115
 
67
- - **Blur dissolve** everywhere. Text blurs in from ~12 px to sharp over
68
- ~500 ms. A card hands over to the app by blurring out while the app blurs in
69
- behind it (~700 ms).
70
- - Into a payoff card: the brand colour washes over the wide app (~600 ms),
71
- then the heading blurs in.
72
- - Card to card: the old words blur out first (~300 ms), then the new ones
73
- blur in, so two headings never show at once.
116
+ - **Blur dissolve** everywhere. A card's words blur in from ~12 px to sharp
117
+ over ~500 ms, one after the other: statement, snapshot, sub-line. A
118
+ card hands over to the app by fading out (~600 ms) while the app sharpens
119
+ behind it.
120
+ - Card to card: the old words blur out first (~240 ms), then the new ones
121
+ blur in, so two statements never show at once.
74
122
  - No slides, wipes, spins, or zoom-punch transitions.
75
123
 
76
124
  ## Camera
77
125
 
78
- - **Wide shot:** the app floats in the middle of the frame at ~90 % width.
79
- Small corner radius, soft long shadow, no browser chrome.
80
- - **Push in once per shot** on the area that holds the control *and* what it
81
- changes, to scale **1.4–1.8** (never past 2: tighter loses the context and
82
- softens the picture). The UI should fill the frame and read on a phone.
126
+ - **Wide is the default.** The app floats in the middle of the frame at
127
+ ~90 % width, with a small corner radius, a soft long shadow and no browser
128
+ chrome. A wide shot whose text reads (16 px or more on the 1080p frame)
129
+ needs no push-in.
130
+ - **A page never opens zoomed.** Every chapter opens wide when its card
131
+ dissolves, and so does every new screen. When a push-in is needed, the
132
+ whole window is seen first (the engine holds it ~0.8 s), then the camera
133
+ moves in.
134
+ - **Push in only where it's needed:** where the text is too small to read
135
+ wide (`check` asks for it), or to read a result. Frame the control *and*
136
+ what it changes, and no tighter than it takes to read (scale 1.3–1.8,
137
+ never past 2: tighter loses the context and softens the picture). A live
138
+ preview that the action changes belongs in the shot.
83
139
  - **Hold steady through the action.** The clicks happen inside the shot; the
84
140
  camera doesn't move with them. At most two push-ins a chapter.
85
141
  - **Show the result.** When an action changes something outside the shot (a
86
142
  row added at the top, a new screen), pull out right after it. A push-in
87
143
  that shows a button pressed and nothing happening is the worst shot.
88
- - **Each move eases in and out** (engine: 1200 ms,
89
- `cubic-bezier(0.65, 0, 0.35, 1)`), then settles 300 ms before anything
90
- happens. Hold ≥ 1.5 s on anything the viewer must read.
91
- - Every chapter opens wide: the engine resets the camera behind each card.
144
+ - **End on the result.** The last shot of a chapter frames what it achieved
145
+ (the pass with its wallet badge, the receipt), held long enough to read,
146
+ then the card covers it.
147
+ - **Each move eases in and out** (engine: 900 ms,
148
+ `cubic-bezier(0.65, 0, 0.35, 1)`), then settles 150 ms before anything
149
+ happens.
92
150
 
93
151
  ## Cursor
94
152
 
95
- - A small standard arrow, used sparingly. It appears for the clicks and drags
96
- a person would make. It glides on a gentle curve, easing in and out
97
- (550–1100 ms by distance), and doesn't pulse, ripple or jerk.
98
- - **A beat around every click:** it rests on the control (220 ms, its hover
153
+ - A small standard arrow, used for the clicks and drags a person would make.
154
+ It glides on a gentle curve, easing in and out (400–800 ms by distance),
155
+ and doesn't pulse, ripple or jerk.
156
+ - **A beat around every click:** it rests on the control (160 ms, its hover
99
157
  state shows), presses (110 ms, the pressed state shows), and the app's
100
- reaction plays (650 ms) before anything else happens.
158
+ reaction plays: 450 ms, then until the app has settled (a toast after a
159
+ save, a preview redrawn), up to a second more.
160
+ - **Every control the viewer sees used gets the cursor**, a colour picker or
161
+ an upload as much as a button: an adapter action names its control, and
162
+ the cursor goes there and rests before it acts.
163
+ - Typing runs at ~45 ms a character.
101
164
  - Between actions it rests where it doesn't cover what the viewer should
102
165
  read.
103
166
  - If the UI animates by itself (an order total updating, a status changing),
@@ -108,10 +171,10 @@ end card 3–5 s logo + tagline, still
108
171
  - **Background:** white tinted ~5–8 % with the brand's primary colour, with
109
172
  one or two very soft radial blooms. Never a busy wallpaper, and never
110
173
  dark mode.
111
- - **Brand colour:** the full-bleed payoff and tagline cards are the brand
112
- colour. The brand's logo and wordmark are on the open and end cards. The
113
- app itself is shown in the customer's branding (their kiosk looks like
114
- their kiosk).
174
+ - **Brand colour:** payoffs and every other chapter card are full-bleed
175
+ brand colour. The brand's logo is on the open and end cards. The app
176
+ itself is shown in its tenant's branding (their kiosk looks like their
177
+ kiosk).
115
178
  - **Brand:** NaviPartner's (below). A pitch draft (linear.md) swaps in a
116
179
  customer's researched logo, colours and fonts, following the same roles.
117
180
 
@@ -122,23 +185,23 @@ These values are taken from navipartner.com and the official logo
122
185
 
123
186
  | Role | Value |
124
187
  |---|---|
125
- | Primary: full-bleed payoff and tagline cards, italic emphasis word, background tint | `#1F5494` |
126
- | Navy: logo wordmark, statement headings on pale cards | `#123A7E` |
188
+ | Primary: brand cards, italic emphasis word, the open card's eyebrow, background tint | `#1F5494` |
189
+ | Navy: logo wordmark, statements on pale cards | `#123A7E` |
127
190
  | Light blue: the logo's "NAVI" mark, soft background bloom, sparingly | `#729FD3` |
128
- | Text: sub-lines and eyebrows | `#232323`; muted `#707070` |
191
+ | Text: sub-lines | `#232323`; muted `#707070` |
129
192
  | Pale background base | `#F8F8F8` / `#FFFFFF` |
130
193
  | Heading font | Merriweather 400/700, italic for the emphasis word |
131
194
  | Body font | Work Sans 400/600/700 |
132
195
  | Logo | official two-colour SVG: `#729FD3` mark + `#123A7E` wordmark, on pale backgrounds only. On `#1F5494`, use it all white. |
133
196
 
134
197
  A NaviPartner pale card is `#F8F8F8` with a `#1F5494` tint at ~5 % and one
135
- `#729FD3` bloom, with the heading in navy and the italic word in primary. A
136
- brand card is full-bleed `#1F5494` with the heading in white Merriweather and
137
- the sub-line in white Work Sans at 80 % opacity.
198
+ `#729FD3` bloom, with the statement in navy and the italic word in primary.
199
+ A brand card is full-bleed `#1F5494` with the statement in white
200
+ Merriweather and the sub-line in white Work Sans at ~85 % opacity.
138
201
 
139
202
  Keep the two brand layers apart. The cards and background use the video's
140
203
  brand (NaviPartner, or the customer's). What's inside the app window is the
141
- kiosk's tenant branding from the adapter's brand file.
204
+ app's tenant branding from the adapter.
142
205
 
143
206
  ## Sound
144
207
 
@@ -152,5 +215,42 @@ the whole story.
152
215
  This is used for the website and the LinkedIn feed.
153
216
  - **Portrait kiosk apps** (food order 1080×1920) float centred in the 16:9
154
217
  frame, and the camera pushes in the same way.
155
- - 1:1 / 9:16 cuts for SoMe come later. Keep card text inside the central
156
- 1080×1080 area so a square crop still works.
218
+ - 1:1 / 9:16 cuts for SoMe come later. Text-only cards keep their words
219
+ inside the central 1080×1080 area; a card with a snapshot will stack it
220
+ under the words there.
221
+
222
+ ## Where these rules come from
223
+
224
+ Researched October 2026. Apple-style ads composite their UI in afterwards;
225
+ we film the real app, so we borrow their discipline (one message per shot,
226
+ the product as the hero), not their technique.
227
+
228
+ - **Showcase length and pace.** Wistia's data on 1.1 million videos: product
229
+ and explainer videos keep over half their viewers up to a few minutes,
230
+ with the steepest drop after two; put what matters early. B2B launch
231
+ videos run 30–60 s, explainers 60–90 s. A buyer can follow a slower video
232
+ that carries real information, so cut dead time, not information.
233
+ ([Wistia](https://wistia.com/blog/optimal-video-length),
234
+ [Flowjam](https://www.flowjam.com/blog/b2b-saas-launch-video))
235
+ - **Feature clips and the first seconds.** In feeds, attention is decided in
236
+ the first 2–3 seconds; brand in the first 5 (Google's ABCD) or 2
237
+ (LinkedIn: a visible logo lifts click-through 17%), and lead with the
238
+ product in use, not only a logo (Meta). Short ads do best around 15–30 s.
239
+ ([Think with Google](https://thinkwithgoogle.com/_qs/documents/8472/ABCD_Complete_V7b_HR_1.pdf),
240
+ [LinkedIn via Storykit](https://storykit.io/blog/linkedin-video-ads-best-practices),
241
+ [Meta via Cinerads](https://www.cinerads.com/blog/meta-ads-video-creative-best-practices))
242
+ - **Silent by design.** About 80 % of LinkedIn video and 85 % of Facebook
243
+ video is watched without sound, so the cards carry every claim.
244
+ - **What goes on a card.** One idea per scene with one supporting line; the
245
+ benefit a viewer grasps fastest beats the feature's name; specific numbers
246
+ beat vague claims; short labels hold viewers.
247
+ ([Nemo](https://www.nemovideo.com/blog/kinetic-typography-product-ads),
248
+ [Arcade, 14 million demo sessions](https://www.arcade.software/post/2026-arcade-benchmarks))
249
+ - **Reading time.** Netflix allows at most 20 characters a second, text up
250
+ 0.83–7 s; the BBC works to about 0.3 s a word. Our cards are read faster
251
+ than subtitles (0.25 s a statement word, 0.2 s a sub-line word): the viewer
252
+ isn't also following speech.
253
+ ([Netflix](https://backlothelp.netflix.com/hc/en-us/articles/219375728))
254
+ - **Camera.** Screen Studio's "Focused" motion settles quickly so UI can be
255
+ read; that is the model: quick moves, then stillness.
256
+ ([Screen Studio](https://screen.studio/guide/animations))
@@ -1,4 +1,4 @@
1
- # Reference: adapter, story and brand fields
1
+ # Reference: adapter, story, brand and check
2
2
 
3
3
  ## Adapter (`<app>/demo/demo.config.ts`)
4
4
 
@@ -7,9 +7,10 @@ the mocks booted the app), `viewport` (default 1728×972; a portrait kiosk is
7
7
  scaled to fit) and `setup(page, { brand })`, which runs the app's e2e mocks
8
8
  **unchanged** and lays the brand over the response that carries it.
9
9
  Optional: `actions` (app steps such as a barcode scan), `idleTimeoutSeconds`,
10
- `locale`, `timezone`, and `brandNeeds`: the `config` keys and asset slots `setup` uses, each a
11
- description (where it shows, what shape) or `{ what, optional: true }`. A
12
- brand without a need that isn't optional fails `check`.
10
+ `locale`, `timezone`, and `brandNeeds`: the `config` keys and asset slots
11
+ `setup` uses, each a description (where it shows, what shape) or
12
+ `{ what, optional: true }`. A brand without a need that isn't optional fails
13
+ `check`.
13
14
 
14
15
  ```ts
15
16
  import { defineDemo, overlayResponse } from "@navipartner/demo-video";
@@ -42,30 +43,88 @@ export default defineDemo({
42
43
 
43
44
  ## Story (`<app>/demo/stories/<name>.json`)
44
45
 
45
- `app`, `description`, optional `primary`, `title`, `closing`, `steps`.
46
-
47
- - **Steps:** `click` `type` `press` `zoom` `frame` `zoomOut` `park` `waitFor`
48
- `expectText` `hold` `card`, plus the adapter's actions as
49
- `{ "do": "<action>", …params }`. `target` is a testid or any Playwright
50
- selector (`role=button[name=/pay/i]`); `nth` picks among matches (-1 =
51
- last). Clicks wait until their target stops moving.
52
- - **Cards** (`card`, `title`, `closing`): `eyebrow` (≤ 4 words), `heading`
53
- (≤ 8, `*word*` in italic, once), `sub` (≤ 16), `logo`, `style` (`pale` |
54
- `brand`), `seconds` (a minimum: a card stays up long enough to read).
55
- Cards that follow each other hand over directly. A number on a card must be
56
- checked by an `expectText` in the story.
57
- - **Camera:** `zoom` (`target`, `scale` 1.3–2) pushes in on that element's
58
- area; `frame` (`targets`: 2–4 testids or `{ target, nth }`, optional
59
- `maxScale` 1.3–2, default 1.8) pushes in as far as all of them fit, and
60
- fails if they only fit wide; `zoomOut` (`park`) pulls back. A `click` keeps the camera unless it
61
- has its own `"zoom"`. At most two push-ins a chapter, and a zoom needs
62
- something done or read in its shot. A click target must be inside the
63
- current shot, and what a zoomed click changes must be in the shot or shown
64
- by the next step (a `zoomOut`, or a zoom that frames it). Every chapter
65
- opens wide.
66
- - **Timing:** `hold` ≤ 6 s; `type` `perCharMs` ≥ 40. The engine adds the
67
- beats around clicks, the camera's settle and a linger before each card.
68
- Holds may not add up past the app's idle timeout.
46
+ `app`, `description`, optional `kind` (`showcase`, the default, or
47
+ `feature`), `primary` (a showcase), `title`, `closing`, `steps`.
48
+
49
+ | Step | Fields |
50
+ |---|---|
51
+ | `click` | `target`, `nth`, `zoom` (push in on it first, 1.3–2), `behindCard` |
52
+ | `type` | `target`, `nth`, `text`, `perCharMs` (≥ 40, default 45), `replace` (clear the field first; without it the text goes at its end), `behindCard` |
53
+ | `press` | `key`, `behindCard` |
54
+ | `zoom` | `target`, `nth`, `scale` (1.3–2) |
55
+ | `frame` | `targets` (2–4 testids or `{ target, nth }`), `maxScale` (1.3–2, default 1.8) |
56
+ | `zoomOut` | `park` (rest the cursor too) |
57
+ | `park` | rests the cursor in empty space |
58
+ | `waitFor` | `target`, `nth`, `hidden`, `timeout` (s), `behindCard` |
59
+ | `expectText` | `target`, `nth`, `text`, `contains`, `timeout` (s) |
60
+ | `hold` | `seconds` (≤ 6) |
61
+ | `card` | see Cards |
62
+
63
+ Plus the adapter's actions as `{ "do": "<action>", …params }`. `target` is a
64
+ testid or any Playwright selector (`role=button[name=/pay/i]`); `nth` picks
65
+ among matches (-1 = last). Clicks wait until their target stops moving.
66
+
67
+ **Cards** (`card`, `title`, `closing`): `heading` (≤ 7 words, `*word*` in
68
+ italic, once), `sub` (≤ 16 words; a chapter card's benefit ≤ 10), `logo`,
69
+ `style` (`pale` | `brand`; chapter cards without one alternate, pale first),
70
+ `seconds` (a minimum: a card stays up long enough to read), and on `title`
71
+ and `closing` only, `eyebrow` (≤ 4 words). `title` may `show` an element
72
+ too: a snapshot of it as it last looks in the story, beside the logo. A `card` step may `show` an element
73
+ (a testid or `{ target, nth }`): a snapshot of it beside the words, as it
74
+ last looks in the chapter after the card (on a payoff, as the card comes up).
75
+ Cards that follow each other hand over directly. A number on a card must be
76
+ checked by an `expectText` in the story.
77
+
78
+ **Behind a card:** `behindCard` steps right after a card (or at the very
79
+ start, behind the title card) run while the card covers the app, with no
80
+ cursor and no camera. `click`, `type`, `press`, `waitFor` and adapter
81
+ actions take it; never a push-in, since every chapter opens wide. A card stays up until what's behind
82
+ it is done: `check` names a card whose setup outlasts its reading time.
83
+
84
+ **Camera rules** (`check` enforces them): every page opens wide, and a
85
+ push-in from the wide shot comes after the whole window has been seen. At
86
+ most two push-ins a chapter, and a push-in needs something done or read in
87
+ its shot. A control a step uses must be inside the current shot. What it
88
+ changes (watched from the press to the app settling: typing's preview, a
89
+ toast, a restyle) must be in the shot or shown by the next step (a
90
+ `zoomOut`, or a zoom that frames it). A wide shot whose text is under 16 px
91
+ on the frame, where the controls and what they change would fit a push-in
92
+ of 1.4 or more, needs one.
93
+
94
+ **Adapter actions** may name the control they use (`target(params)`); the
95
+ cursor then goes there and rests, and the camera rules apply as for a
96
+ click. Without one, an action has no cursor and only a short beat after
97
+ it.
98
+
99
+ **Timing:** the engine adds the beats around clicks, the camera's settle and
100
+ a linger before each card (look.md has the numbers). Holds may not add up
101
+ past the app's idle timeout. A passing `check` prints the story's estimated
102
+ length against its kind's range (`seconds` and `kind` in `--json`).
103
+
104
+ ## What `check` says, and the fix
105
+
106
+ | It says | Fix |
107
+ |---|---|
108
+ | `"x" is outside the shot …` | `zoomOut` first, or frame an area that holds everything the shot uses |
109
+ | `clicking / typing in / using "x" changes y, which is outside the shot …` | `zoomOut` right after, or frame the control with what it changes |
110
+ | `"x" is used in the wide shot, where its text is N px, too small to read …` | `frame` the controls and what they change (the engine pushes in after a wide beat); or, if it's setup, `behindCard` |
111
+ | `N camera push-ins in one chapter` | Push in once on the chapter's area and hold steady |
112
+ | `… only fit in the wide shot` | Stay wide, or frame things that sit together |
113
+ | `a zoom with nothing in its shot` | Remove it, or act or read something in it |
114
+ | `zoom 2.4; use 1.3–2` | Zoom less: context reads better and stays sharp |
115
+ | `behindCard only right after a card` | Move the step to just after a card, or drop `behindCard` |
116
+ | `"x" (show) never appears in the chapter …` | Show an element the chapter ends on |
117
+ | `cards between chapters have no eyebrow` | Remove it: the statement and sub-line carry the chapter |
118
+ | `… shows the same snapshot` | Show each result once: on the open card or on a chapter card |
119
+ | `title.show: "x" never appears in the story` | Show an element the story reaches: its result, or the feature |
120
+ | `kind: a feature clip shows one feature, in one chapter` | Cut it to the feature's one chapter, or make it a showcase |
121
+ | `N chapters; a showcase has at most 4` | Make a second story |
122
+ | `kind: the primary story … is a showcase` | Drop `"kind": "feature"` from the primary story |
123
+ | `"…" looks like a testid-like name / barcode / code` | Cards are for people: say it in words |
124
+ | `"38" isn't shown by an expectText` | Quote only values an `expectText` checks |
125
+ | `the card announces a chapter in which nothing is done` | Follow it with the steps it describes (setup behind the card doesn't count) |
126
+ | `… names a brand or extends a story` | Remove it: a customer's branding is @Linear's, a translation is a story of its own |
127
+ | `…/demo/brands isn't used` | Delete the folder: repos keep no brands |
69
128
 
70
129
  ## Brand (the engine's `navipartner`, or one a pitch draft builds)
71
130
 
@@ -73,8 +132,8 @@ export default defineDemo({
73
132
  - `config`: opaque to the engine, the keys the app's branding API reads
74
133
  (`primaryColor`, …). An adapter may also read `config.demoData`: names
75
134
  for the mock's products, keyed by the mock's barcodes (prices and flows
76
- stay the mock's). It takes `demoData` out before it
77
- overlays the branding.
135
+ stay the mock's). It takes `demoData` out before it overlays the
136
+ branding.
78
137
  - `assets`: slot → file, e.g. `{ "logo": "navipartner/logo.svg" }`
79
138
  - `video`: `primary`, `ink`, `accent`, `text`, `headingFont`, `bodyFont`,
80
139
  optional `fontsUrl`, `logo` (asset slot)