@navipartner/demo-video 0.6.0 → 0.7.1

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,82 +1,133 @@
1
1
  # The look
2
2
 
3
- Every NaviPartner demo video should look and feel the same, in order of importance for the visuals:
4
-
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".
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:
8
+
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.** It moves between shots, never during the action:
17
+ the chapter opens on the shot that holds the action and its result,
18
+ holds steady through the clicks, and ends on the result. It never chases
19
+ the cursor, and there are no hard cuts inside a chapter.
20
+ 4. **Soft, light, branded.** A pale brand-tinted background, a floating app
21
+ window, big serif statements, and the brand colour used boldly.
22
+ 5. **Believable data.** The most real-looking records the e2e mocks have.
23
+ Never "Test item 1" on screen when the story can type a better value.
24
+
25
+ ## Two kinds of video
26
+
27
+ A story says which it is (`"kind"`, default `showcase`). The beats are the
28
+ same in both; the shape differs.
29
+
30
+ | | Showcase | Feature clip |
31
+ |---|---|---|
32
+ | 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" |
33
+ | For | the website, a sales meeting, a buyer comparing systems | release news, a LinkedIn post, a customer update |
34
+ | Length | 45–90 s | 15–30 s |
35
+ | Shape | open card, 2–4 chapters of 10–20 s, a payoff, end card | open card naming the feature, one chapter, end card |
36
+ | Open and end cards | up to 3.5 s | up to 2.5 s |
16
37
 
17
- ## Structure
38
+ `check` keeps a feature clip to one chapter and a showcase to four, and
39
+ prints how long the story will run.
18
40
 
19
- A video is a sequence of **chapters**. Each chapter is one idea the viewer
20
- should remember.
41
+ ### Showcase
21
42
 
22
43
  ```
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
44
+ open card ≤ 3.5 s logo + what the product does for the buyer + a snapshot of the result
45
+ ┌ chapter card ~2–4 s "Pay *by card*." + the benefit in one line, beside a snapshot of the result
46
+ │ demo 10–20 s the app doing exactly that, ending on the result
47
+ └ payoff ~3–4 s optional: the result just shown, as a fact
27
48
  … 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
49
+ end card ≤ 3.5 s logo + the promise, or the next step
50
+ ```
51
+
52
+ ### Feature clip
53
+
54
+ ```
55
+ open card ≤ 2.5 s logo + "New: …" + a snapshot of the feature
56
+ ┌ chapter card ~2–3 s the feature's statement + what it does for the customer
57
+ │ demo 8–15 s straight to the feature; all setup behind the card
58
+ end card ≤ 2.5 s logo + where it is available
30
59
  ```
31
60
 
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.
61
+ - **The first seconds carry brand and product.** The open card shows the
62
+ logo and a snapshot of the result (a showcase) or of the feature (a clip),
63
+ so a viewer who stops after two seconds has seen both.
64
+ - Setup the viewer needn't watch (a language dialog, a cookie banner,
65
+ opening a tab) happens behind the card before it, never on screen.
66
+ - Length follows the chapters: don't pad, don't cram.
38
67
 
39
- ## Text cards
68
+ ## Cards
40
69
 
41
70
  | Card | Text | Look |
42
71
  |---|---|---|
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.
72
+ | 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 |
73
+ | 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 |
74
+ | Payoff | the result just shown; the sub-line states the fact the app showed: "64,50 kr. by card." | full-bleed brand colour |
75
+ | End | logo + the product's promise, or the next step: "Book a demo at navipartner.com." | pale, held still |
76
+
77
+ A **chapter card** has two lines of words, and no eyebrow: chapter numbers
78
+ and topic labels don't help in a film this short.
79
+
80
+ 1. **Statement:** ≤ 7 words naming what the chapter shows, in its own verb,
81
+ sentence case, ending with a full stop, **one word or phrase in italic**
82
+ (the action or its result): "Pay *by card*."
83
+ 2. **Benefit:** one sub-line, ideally ≤ 12 words, saying what it does for
84
+ the customer as something the chapter then shows. An outcome, not a
85
+ feature name, and a specific value when the app shows one: "Tap, insert
86
+ or phone: the terminal takes it all."
87
+
88
+ **Text and UI on one card.** A card shows a snapshot of the result beside
89
+ the words when there is something visual to show: the finished pass, the
90
+ receipt, the "Paid" screen. The words go left, the snapshot right, in the
91
+ same rounded window and soft shadow as the app. It is the app, captured by
92
+ `check`, never a mock-up. A chapter that ends on a toast or a number stays
93
+ text only, centred.
94
+
95
+ - **Typeface:** the brand's fonts. For NaviPartner: **Merriweather** for
96
+ statements (its italic is the emphasis word), **Work Sans** for eyebrows
97
+ and sub-lines. At 1080p: statements 116 px (92 px beside a snapshot),
98
+ sub-lines 40 px (36 px), the open card's eyebrow 28 px spaced capitals,
99
+ logo 720 px wide.
100
+ - **Tone:** plain, confident, present tense, for a professional audience.
101
+ No exclamation marks except inside a quoted customer line. No "seamless",
102
+ "revolutionary" or "effortless".
103
+ - **Truth:** every fact on a card is visible in the video (the price shown,
104
+ the receipt that appears), checked by an `expectText`. No invented
105
+ durations or customer statistics.
62
106
  - **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.
107
+ - **Reading time:** a card stays up 0.6 s plus ~0.28 s a word (an eyebrow's
108
+ words count half), at least 1.8 s. The open and end cards stay at most
109
+ 3.5 s in a showcase and 2.5 s in a clip, unless the story asks for more.
64
110
 
65
111
  ## Transitions
66
112
 
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.
113
+ - **Blur dissolve** everywhere. A card's words blur in from ~12 px to sharp
114
+ over ~500 ms, one after the other: statement, snapshot, sub-line. A
115
+ card hands over to the app by fading out (~600 ms) while the app sharpens
116
+ behind it.
117
+ - Card to card: the old words blur out first (~240 ms), then the new ones
118
+ blur in, so two statements never show at once.
74
119
  - No slides, wipes, spins, or zoom-punch transitions.
75
120
 
76
121
  ## Camera
77
122
 
78
123
  - **Wide shot:** the app floats in the middle of the frame at ~90 % width.
79
- Small corner radius, soft long shadow, no browser chrome.
124
+ Small corner radius, soft long shadow, no browser chrome. Wide is for
125
+ moments that need the whole window (a new screen arriving). A desktop app
126
+ shown wide is too small to read on a phone, so clicks whose controls and
127
+ results would fit a push-in get one (`check` asks for it).
128
+ - **A chapter opens on its first shot.** When a chapter starts with a
129
+ push-in, the camera is already there when its card dissolves: no push-in
130
+ after every card.
80
131
  - **Push in once per shot** on the area that holds the control *and* what it
81
132
  changes, to scale **1.4–1.8** (never past 2: tighter loses the context and
82
133
  softens the picture). The UI should fill the frame and read on a phone.
@@ -85,19 +136,22 @@ end card 3–5 s logo + tagline, still
85
136
  - **Show the result.** When an action changes something outside the shot (a
86
137
  row added at the top, a new screen), pull out right after it. A push-in
87
138
  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.
139
+ - **End on the result.** The last shot of a chapter frames what it achieved
140
+ (the pass with its wallet badge, the receipt), held long enough to read,
141
+ then the card covers it.
142
+ - **Each move eases in and out** (engine: 900 ms,
143
+ `cubic-bezier(0.65, 0, 0.35, 1)`), then settles 150 ms before anything
144
+ happens.
92
145
 
93
146
  ## Cursor
94
147
 
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
148
+ - A small standard arrow, used for the clicks and drags a person would make.
149
+ It glides on a gentle curve, easing in and out (400–800 ms by distance),
150
+ and doesn't pulse, ripple or jerk.
151
+ - **A beat around every click:** it rests on the control (160 ms, its hover
99
152
  state shows), presses (110 ms, the pressed state shows), and the app's
100
- reaction plays (650 ms) before anything else happens.
153
+ reaction plays (450 ms) before anything else happens.
154
+ - Typing runs at ~45 ms a character.
101
155
  - Between actions it rests where it doesn't cover what the viewer should
102
156
  read.
103
157
  - If the UI animates by itself (an order total updating, a status changing),
@@ -108,10 +162,10 @@ end card 3–5 s logo + tagline, still
108
162
  - **Background:** white tinted ~5–8 % with the brand's primary colour, with
109
163
  one or two very soft radial blooms. Never a busy wallpaper, and never
110
164
  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).
165
+ - **Brand colour:** payoffs and every other chapter card are full-bleed
166
+ brand colour. The brand's logo is on the open and end cards. The app
167
+ itself is shown in its tenant's branding (their kiosk looks like their
168
+ kiosk).
115
169
  - **Brand:** NaviPartner's (below). A pitch draft (linear.md) swaps in a
116
170
  customer's researched logo, colours and fonts, following the same roles.
117
171
 
@@ -122,23 +176,23 @@ These values are taken from navipartner.com and the official logo
122
176
 
123
177
  | Role | Value |
124
178
  |---|---|
125
- | Primary: full-bleed payoff and tagline cards, italic emphasis word, background tint | `#1F5494` |
126
- | Navy: logo wordmark, statement headings on pale cards | `#123A7E` |
179
+ | Primary: brand cards, italic emphasis word, the open card's eyebrow, background tint | `#1F5494` |
180
+ | Navy: logo wordmark, statements on pale cards | `#123A7E` |
127
181
  | Light blue: the logo's "NAVI" mark, soft background bloom, sparingly | `#729FD3` |
128
- | Text: sub-lines and eyebrows | `#232323`; muted `#707070` |
182
+ | Text: sub-lines | `#232323`; muted `#707070` |
129
183
  | Pale background base | `#F8F8F8` / `#FFFFFF` |
130
184
  | Heading font | Merriweather 400/700, italic for the emphasis word |
131
185
  | Body font | Work Sans 400/600/700 |
132
186
  | Logo | official two-colour SVG: `#729FD3` mark + `#123A7E` wordmark, on pale backgrounds only. On `#1F5494`, use it all white. |
133
187
 
134
188
  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.
189
+ `#729FD3` bloom, with the statement in navy and the italic word in primary.
190
+ A brand card is full-bleed `#1F5494` with the statement in white
191
+ Merriweather and the sub-line in white Work Sans at ~85 % opacity.
138
192
 
139
193
  Keep the two brand layers apart. The cards and background use the video's
140
194
  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.
195
+ app's tenant branding from the adapter.
142
196
 
143
197
  ## Sound
144
198
 
@@ -152,5 +206,41 @@ the whole story.
152
206
  This is used for the website and the LinkedIn feed.
153
207
  - **Portrait kiosk apps** (food order 1080×1920) float centred in the 16:9
154
208
  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.
209
+ - 1:1 / 9:16 cuts for SoMe come later. Text-only cards keep their words
210
+ inside the central 1080×1080 area; a card with a snapshot will stack it
211
+ under the words there.
212
+
213
+ ## Where these rules come from
214
+
215
+ Researched October 2026. Apple-style ads composite their UI in afterwards;
216
+ we film the real app, so we borrow their discipline (one message per shot,
217
+ the product as the hero), not their technique.
218
+
219
+ - **Showcase length and pace.** Wistia's data on 1.1 million videos: product
220
+ and explainer videos keep over half their viewers up to a few minutes,
221
+ with the steepest drop after two; put what matters early. B2B launch
222
+ videos run 30–60 s, explainers 60–90 s. A buyer can follow a slower video
223
+ that carries real information, so cut dead time, not information.
224
+ ([Wistia](https://wistia.com/blog/optimal-video-length),
225
+ [Flowjam](https://www.flowjam.com/blog/b2b-saas-launch-video))
226
+ - **Feature clips and the first seconds.** In feeds, attention is decided in
227
+ the first 2–3 seconds; brand in the first 5 (Google's ABCD) or 2
228
+ (LinkedIn: a visible logo lifts click-through 17%), and lead with the
229
+ product in use, not only a logo (Meta). Short ads do best around 15–30 s.
230
+ ([Think with Google](https://thinkwithgoogle.com/_qs/documents/8472/ABCD_Complete_V7b_HR_1.pdf),
231
+ [LinkedIn via Storykit](https://storykit.io/blog/linkedin-video-ads-best-practices),
232
+ [Meta via Cinerads](https://www.cinerads.com/blog/meta-ads-video-creative-best-practices))
233
+ - **Silent by design.** About 80 % of LinkedIn video and 85 % of Facebook
234
+ video is watched without sound, so the cards carry every claim.
235
+ - **What goes on a card.** One idea per scene with one supporting line; the
236
+ benefit a viewer grasps fastest beats the feature's name; specific numbers
237
+ beat vague claims; short labels hold viewers.
238
+ ([Nemo](https://www.nemovideo.com/blog/kinetic-typography-product-ads),
239
+ [Arcade, 14 million demo sessions](https://www.arcade.software/post/2026-arcade-benchmarks))
240
+ - **Reading time.** Netflix allows at most 20 characters a second, text up
241
+ 0.83–7 s; the BBC works to about 0.3 s a word. Our 0.6 s + 0.28 s a word
242
+ sits between them.
243
+ ([Netflix](https://backlothelp.netflix.com/hc/en-us/articles/219375728))
244
+ - **Camera.** Screen Studio's "Focused" motion settles quickly so UI can be
245
+ read; that is the model: quick moves, then stillness.
246
+ ([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,80 @@ 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 is added), `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. The chapter's first `zoom` or `frame` is set behind the
81
+ card too, so the chapter opens on it. A card stays up until what's behind it
82
+ is done.
83
+
84
+ **Camera rules** (`check` enforces them): at most two push-ins a chapter,
85
+ and a push-in needs something done or read in its shot. A click target must
86
+ be inside the current shot. What a zoomed click changes must be in the shot
87
+ or shown by the next step (a `zoomOut`, or a zoom that frames it). Clicks in
88
+ the wide shot whose controls and results would fit a push-in of 1.4 or more
89
+ need one.
90
+
91
+ **Timing:** the engine adds the beats around clicks, the camera's settle and
92
+ a linger before each card (look.md has the numbers). Holds may not add up
93
+ past the app's idle timeout. A passing `check` prints the story's estimated
94
+ length against its kind's range (`seconds` and `kind` in `--json`).
95
+
96
+ ## What `check` says, and the fix
97
+
98
+ | It says | Fix |
99
+ |---|---|
100
+ | `"x" is outside the shot …` | The shot frames the wrong area: frame what this shot clicks, or `zoomOut` first |
101
+ | `clicking "x" changes y, which is outside the shot …` | Frame what the click changes, or `zoomOut` right after the click |
102
+ | `"x" is clicked in the wide shot, but … fit a push-in` | `frame` the controls and what they change; or, if it's setup, `behindCard` |
103
+ | `N camera push-ins in one chapter` | Push in once on the chapter's area and hold steady |
104
+ | `… only fit in the wide shot` | `frame` things that sit together, or stay wide |
105
+ | `a zoom with nothing in its shot` | Remove it, or act or read something in it |
106
+ | `zoom 2.4; use 1.3–2` | Zoom less: context reads better and stays sharp |
107
+ | `behindCard only right after a card` | Move the step to just after a card, or drop `behindCard` |
108
+ | `"x" (show) never appears in the chapter …` | Show an element the chapter ends on |
109
+ | `cards between chapters have no eyebrow` | Remove it: the statement and sub-line carry the chapter |
110
+ | `… shows the same snapshot` | Show each result once: on the open card or on a chapter card |
111
+ | `title.show: "x" never appears in the story` | Show an element the story reaches: its result, or the feature |
112
+ | `kind: a feature clip shows one feature, in one chapter` | Cut it to the feature's one chapter, or make it a showcase |
113
+ | `N chapters; a showcase has at most 4` | Make a second story |
114
+ | `kind: the primary story … is a showcase` | Drop `"kind": "feature"` from the primary story |
115
+ | `"…" looks like a testid-like name / barcode / code` | Cards are for people: say it in words |
116
+ | `"38" isn't shown by an expectText` | Quote only values an `expectText` checks |
117
+ | `the card announces a chapter in which nothing is done` | Follow it with the steps it describes (setup behind the card doesn't count) |
118
+ | `… names a brand or extends a story` | Remove it: a customer's branding is @Linear's, a translation is a story of its own |
119
+ | `…/demo/brands isn't used` | Delete the folder: repos keep no brands |
69
120
 
70
121
  ## Brand (the engine's `navipartner`, or one a pitch draft builds)
71
122
 
@@ -73,8 +124,8 @@ export default defineDemo({
73
124
  - `config`: opaque to the engine, the keys the app's branding API reads
74
125
  (`primaryColor`, …). An adapter may also read `config.demoData`: names
75
126
  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.
127
+ stay the mock's). It takes `demoData` out before it overlays the
128
+ branding.
78
129
  - `assets`: slot → file, e.g. `{ "logo": "navipartner/logo.svg" }`
79
130
  - `video`: `primary`, `ink`, `accent`, `text`, `headingFont`, `bodyFont`,
80
131
  optional `fontsUrl`, `logo` (asset slot)