@vanillaskyai/video 0.8.7 → 0.9.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.
Files changed (175) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/PUBLIC-API.md +5 -8
  3. package/dist/{builtin-server-CUYPIDE7.js → builtin-server-HW6HGXCZ.js} +2 -2
  4. package/dist/{catalog-types-BIhSpOWK.d.ts → catalog-types-WTbLP6Jh.d.ts} +5 -1
  5. package/dist/chapter-title-YRLIQY3V.js +7 -0
  6. package/dist/check-runtime.js +6 -15
  7. package/dist/{chunk-2USVB3CY.js → chunk-3ZTEFJIR.js} +285 -178
  8. package/dist/chunk-4YM2M62S.js +13 -0
  9. package/dist/chunk-77VY4O7A.js +390 -0
  10. package/dist/{chunk-MMUXVA47.js → chunk-7M56IUUX.js} +9 -8
  11. package/dist/chunk-7NZOMTAL.js +63 -0
  12. package/dist/{chunk-2DOLF4QN.js → chunk-AUPC6MDK.js} +12 -14
  13. package/dist/{chunk-352ZZCET.js → chunk-EGVQODKU.js} +2 -2
  14. package/dist/chunk-EWWCQFTI.js +18 -0
  15. package/dist/{chunk-MXZSDGZQ.js → chunk-G7IZBBWF.js} +14 -9
  16. package/dist/{chunk-KT3M7KDL.js → chunk-HKUI5BTB.js} +3 -3
  17. package/dist/{chunk-LMG3GHLL.js → chunk-IQMYK5DX.js} +29 -3
  18. package/dist/chunk-LH4BPALA.js +564 -0
  19. package/dist/{chunk-OWBT45EN.js → chunk-MQILEEXC.js} +1 -1
  20. package/dist/chunk-MXEYPK7M.js +552 -0
  21. package/dist/{chunk-AITKH6QT.js → chunk-OR52UMSF.js} +13 -39
  22. package/dist/{chunk-5SLENAJW.js → chunk-PAJ44TFW.js} +1 -2
  23. package/dist/{chunk-L7QGGKJ2.js → chunk-PDQFQIQW.js} +1 -1
  24. package/dist/{chunk-WJ6FLKNC.js → chunk-R3XAOMKP.js} +14 -0
  25. package/dist/{chunk-2OKB27TG.js → chunk-RXTN2CW6.js} +2 -2
  26. package/dist/{chunk-XWWLKRNU.js → chunk-TVIU23OM.js} +38 -49
  27. package/dist/{chunk-224QNWRA.js → chunk-UDIODEDK.js} +1 -5
  28. package/dist/{chunk-3VRMKFM7.js → chunk-ZDJNFA52.js} +11 -24
  29. package/dist/cinema-media-YQHSIFDA.js +11 -0
  30. package/dist/cli.js +18 -9
  31. package/dist/comparison-RZMMHZR2.js +27 -0
  32. package/dist/compose-video-TBFTONUC.js +12 -0
  33. package/dist/editorial-timeline-OZX7OS6G.js +36 -0
  34. package/dist/{events-BQ6z264n.d.ts → events-B4YCc4vc.d.ts} +1 -1
  35. package/dist/focus-cards-TJ72BT7U.js +20 -0
  36. package/dist/index.d.ts +4 -6
  37. package/dist/index.js +3 -7
  38. package/dist/key-figure-GYENXKH3.js +20 -0
  39. package/dist/{kit-tQnCPMOM.d.ts → kit-DrRpdn0p.d.ts} +2 -2
  40. package/dist/mobile-message-UZUPIKHD.js +49 -0
  41. package/dist/{preload-media-LJXWKTWG.js → preload-media-CFTZIMZL.js} +1 -1
  42. package/dist/quote-EW5HQA6H.js +23 -0
  43. package/dist/react.d.ts +21 -11
  44. package/dist/react.js +608 -851
  45. package/dist/scene-video-backdrop-PCTARAAJ.js +7 -0
  46. package/dist/server.d.ts +7 -8
  47. package/dist/server.js +91 -74
  48. package/dist/{system-prompt-RRXIWDDD.js → system-prompt-SYRT5DWM.js} +1 -1
  49. package/dist/template-catalog.d.ts +257 -16
  50. package/dist/template-catalog.js +1 -1
  51. package/dist/templates.d.ts +5 -5
  52. package/dist/test.d.ts +3 -3
  53. package/dist/test.js +18 -18
  54. package/dist/text-stream-LD364KBI.js +9 -0
  55. package/dist/{types-DWFO6qca.d.ts → types-CdBmNTGD.d.ts} +1 -1
  56. package/dist/{types-CEh9eUFu.d.ts → types-DdZw4GRQ.d.ts} +17 -48
  57. package/dist/validate-4OUD2CLX.js +10 -0
  58. package/docs/architecture.md +1 -1
  59. package/docs/concepts.md +1 -1
  60. package/docs/custom-templates.md +13 -28
  61. package/docs/customization.md +11 -59
  62. package/docs/getting-started.md +2 -2
  63. package/docs/immersive-interface.md +3 -4
  64. package/docs/maintainers/cinematic-migration.md +34 -0
  65. package/docs/media-and-audio.md +14 -14
  66. package/docs/motion-and-effects.md +6 -14
  67. package/docs/persistence.md +3 -3
  68. package/docs/production.md +11 -9
  69. package/docs/provider-integration.md +9 -16
  70. package/docs/reference/design-system.html +3 -3
  71. package/docs/reference/protocol.md +14 -4
  72. package/docs/testing.md +1 -1
  73. package/package.json +6 -3
  74. package/registry/items/backgrounds.json +1 -1
  75. package/registry/items/chapterTitle.json +80 -0
  76. package/registry/items/cinemaMedia.json +136 -0
  77. package/registry/items/comparison.json +101 -0
  78. package/registry/items/editorialTimeline.json +105 -0
  79. package/registry/items/focusCards.json +90 -0
  80. package/registry/items/keyFigure.json +97 -0
  81. package/registry/items/mobileMessage.json +153 -0
  82. package/registry/items/quote.json +94 -0
  83. package/registry/items/template-context.json +1 -1
  84. package/registry/items/theme.json +1 -1
  85. package/starters/video-chat/README.md +24 -26
  86. package/starters/video-chat/package.json +1 -1
  87. package/starters/video-chat/providers/video.ts +6 -5
  88. package/starters/video-chat/stock.ts +35 -151
  89. package/styles/fonts/OFL.txt +93 -0
  90. package/styles/fonts/manifest.json +66 -0
  91. package/styles/fonts/roboto-v51-cyrillic-ext.woff2 +0 -0
  92. package/styles/fonts/roboto-v51-cyrillic.woff2 +0 -0
  93. package/styles/fonts/roboto-v51-greek-ext.woff2 +0 -0
  94. package/styles/fonts/roboto-v51-greek.woff2 +0 -0
  95. package/styles/fonts/roboto-v51-latin-ext.woff2 +0 -0
  96. package/styles/fonts/roboto-v51-latin.woff2 +0 -0
  97. package/styles/fonts/roboto-v51-math.woff2 +0 -0
  98. package/styles/fonts/roboto-v51-symbols.woff2 +0 -0
  99. package/styles/fonts/roboto-v51-vietnamese.woff2 +0 -0
  100. package/styles/fonts/roboto.css +91 -0
  101. package/styles/video-chat.css +30 -23
  102. package/dist/bg-confetti-HITCGLGD.js +0 -89
  103. package/dist/bg-emoji-VU4UZYZN.js +0 -150
  104. package/dist/bg-media-L34PDQXJ.js +0 -106
  105. package/dist/brand-message-FUJ4STFO.js +0 -256
  106. package/dist/chart-bar-EGPHJATL.js +0 -310
  107. package/dist/chart-counter-5FFWQNZO.js +0 -306
  108. package/dist/chart-progress-ring-JMXQLMLC.js +0 -302
  109. package/dist/chunk-4UNKZEAT.js +0 -3575
  110. package/dist/chunk-5CDAM24P.js +0 -31
  111. package/dist/chunk-AOWSXU2K.js +0 -18
  112. package/dist/chunk-CJ7F2CE6.js +0 -104
  113. package/dist/chunk-FRN6WKHA.js +0 -185
  114. package/dist/chunk-G5JPHBX7.js +0 -336
  115. package/dist/chunk-L5BTTCZV.js +0 -97
  116. package/dist/chunk-P22HHFDK.js +0 -43
  117. package/dist/chunk-PP3MCPN4.js +0 -1998
  118. package/dist/chunk-QAAO7OAY.js +0 -19
  119. package/dist/chunk-S7HFLYNN.js +0 -35
  120. package/dist/chunk-W3VDIBGR.js +0 -217
  121. package/dist/chunk-XGRA2MUV.js +0 -242
  122. package/dist/chunk-XTOXFMLM.js +0 -105
  123. package/dist/chunk-YJJC4N4D.js +0 -761
  124. package/dist/compose-video-2NFH4DZY.js +0 -13
  125. package/dist/cta-logo-YR7LYUR5.js +0 -212
  126. package/dist/cta-media-DQJBKUO2.js +0 -221
  127. package/dist/incoming-call-54KQ7O5A.js +0 -323
  128. package/dist/infographic-before-after-ROS52GOW.js +0 -460
  129. package/dist/infographic-feature-list-E7MGUDAN.js +0 -272
  130. package/dist/infographic-problem-solution-3S6NGO5N.js +0 -343
  131. package/dist/infographic-stat-row-SYB7AB7H.js +0 -247
  132. package/dist/infographic-steps-KCE6JWE2.js +0 -406
  133. package/dist/prompt-input-MHX4O42G.js +0 -239
  134. package/dist/reaction-SLEGR3BE.js +0 -122
  135. package/dist/scene-video-backdrop-XFPX4O3I.js +0 -10
  136. package/dist/showcase-code-2HLAVKBM.js +0 -357
  137. package/dist/showcase-phone-Q4HEO4LH.js +0 -278
  138. package/dist/showcase-terminal-DL45LUFG.js +0 -312
  139. package/dist/showcase-web-2QZO73FZ.js +0 -396
  140. package/dist/social-conversation-75SD2637.js +0 -695
  141. package/dist/social-milestone-GEFUQSBF.js +0 -296
  142. package/dist/social-notification-FJ5VHVLK.js +0 -247
  143. package/dist/social-review-stack-KXQCKFRP.js +0 -242
  144. package/dist/social-testimonial-JZ7CHC7S.js +0 -287
  145. package/dist/social-tweet-UCDXM25F.js +0 -298
  146. package/dist/text-stream-54LXABJW.js +0 -10
  147. package/dist/validate-T7GBU2YF.js +0 -8
  148. package/registry/items/barChart.json +0 -245
  149. package/registry/items/beforeAfter.json +0 -180
  150. package/registry/items/bigNumber.json +0 -232
  151. package/registry/items/brandMessage.json +0 -164
  152. package/registry/items/cardList.json +0 -214
  153. package/registry/items/chatMessenger.json +0 -151
  154. package/registry/items/chatWhatsapp.json +0 -151
  155. package/registry/items/codeEditor.json +0 -191
  156. package/registry/items/confetti.json +0 -107
  157. package/registry/items/ctaLogo.json +0 -167
  158. package/registry/items/ctaMedia.json +0 -200
  159. package/registry/items/emojiBurst.json +0 -101
  160. package/registry/items/incomingCall.json +0 -177
  161. package/registry/items/media.json +0 -187
  162. package/registry/items/milestone.json +0 -212
  163. package/registry/items/notification.json +0 -189
  164. package/registry/items/phoneMockup.json +0 -261
  165. package/registry/items/problemSolution.json +0 -193
  166. package/registry/items/progressRing.json +0 -206
  167. package/registry/items/promptInput.json +0 -155
  168. package/registry/items/reaction.json +0 -176
  169. package/registry/items/reviewStack.json +0 -231
  170. package/registry/items/steps.json +0 -215
  171. package/registry/items/terminal.json +0 -219
  172. package/registry/items/testimonial.json +0 -171
  173. package/registry/items/tripleStats.json +0 -235
  174. package/registry/items/tweet.json +0 -210
  175. package/registry/items/webMockup.json +0 -280
@@ -14,7 +14,6 @@ heading and a root class when the application needs its own copy or chrome color
14
14
  <VideoChat
15
15
  className="acme-chat"
16
16
  welcomeTitle={<>Ask Acme<br />See the answer</>}
17
- options={{ brand: acmeBrand }}
18
17
  />
19
18
  ```
20
19
 
@@ -32,10 +31,10 @@ that instance:
32
31
  ```
33
32
 
34
33
  The built-in navigation carries the VanillaSky logo. `welcomeTitle` changes
35
- the welcome heading; it does not replace the navigation logo. `options.brand`
36
- styles generated video content independently of the surrounding controls.
34
+ the welcome heading; it does not replace the navigation logo. Graphic scenes
35
+ use fixed black backgrounds and white/neutral system typography.
37
36
 
38
- Use `options` for the endpoint, templates, orientation, visual brand, request
37
+ Use `options` for the endpoint, templates, orientation, request
39
38
  headers, and an optional custom voice. Provider capabilities are discovered
40
39
  from the server. Use `useVideoChat()` only when the application needs to own the
41
40
  entire interface.
@@ -44,63 +43,16 @@ Pass the following visual settings through `VideoChat` or `useVideoChat` options
44
43
  Keep viewer context in the prompt and completed conversation turns; use the
45
44
  server handler’s `instructions` for trusted product guidance.
46
45
 
47
- ## Background and semantic brand
46
+ ## Cinematic visual direction
48
47
 
49
- Omit brand configuration to use the standard `cosmic` background. Prefer a
50
- named curated choice over raw color work:
48
+ Graphics use black backgrounds and white/neutral typography. Full-bleed media
49
+ and Reach out can show naturally colored footage; the other six templates
50
+ explain with their own composition and motion. There is no brand-kit option.
51
51
 
52
- ```ts
53
- const brand = {
54
- name: "Acme",
55
- logoUrl: "https://cdn.acme.com/logo.svg",
56
- font: "Inter",
57
- scriptFont: "Caveat",
58
- background: "twilight",
59
- colors: {
60
- primary: "#6D5EF5",
61
- secondary: "#3D2A78",
62
- foreground: "#FFFFFF",
63
- surface: "#17122F",
64
- surfaceElevated: "#231B42",
65
- muted: "#A7A6B0",
66
- },
67
- };
68
- ```
69
-
70
- Gradient presets: `cosmic`, `horizon`, `twilight`, `meadow`, `velvet`,
71
- `flamingo`, `peach`, `saffron`. Solid presets: `black`, `midnight`,
72
- `aubergine`, `coal`, `navy`. When a named choice genuinely cannot express the
73
- brand, use `{ colors: ["#112233", "#334455"] }` for a custom gradient or
74
- `{ color: "#070B20" }` for a custom solid.
75
-
76
- `colors` may be partial; the resolver fills every semantic token before the
77
- video is emitted. With no foreground, named and custom backgrounds
78
- deterministically select black or white for at least 4.5:1 contrast across the
79
- full rendered sRGB ramp, including gradient interiors. A ramp that neither can
80
- cover is rejected. An explicit foreground is preserved and validated by the
81
- same invariant during input resolution and replay; low-contrast values are
82
- rejected with the failing path and minimum ratio. Elevated surfaces derive an
83
- accessible internal text treatment without changing the semantic foreground.
84
- Use an approved public or signed URL for logos, and never put a private storage
85
- credential in the config.
86
-
87
- ## Global visual direction
88
-
89
- Leave visual direction unset to use VanillaSky's defaults, or set one coherent
90
- look for the completed video:
91
-
92
- ```ts
93
- style: {
94
- density: "airy", // airy | normal | packed
95
- motion: "calm", // calm | normal | punchy
96
- textArchetype: "cinematic",
97
- backgroundEffect: "slow-zoom-out",
98
- }
99
- ```
100
-
101
- These are defaults, not generated CSS. A validated scene may still select a
102
- more appropriate text or background treatment when its trusted template allows
103
- it.
52
+ The host can provide a shared `generatedLook` description for media preparation.
53
+ Custom source-owned templates can define their own visual language in code.
54
+ Do not rely on old global brand, text-effect, or gradient controls to restyle
55
+ the eight cinematic templates.
104
56
 
105
57
  ## Opening
106
58
 
@@ -96,8 +96,8 @@ spoken hook from the response stream before the full plan is complete, holds its
96
96
  scene is ready, reaches its final frame, and leaves the composer ready for
97
97
  another turn. With stock media enabled, click a welcome or follow-up card and
98
98
  confirm its footage carries directly into that opening. With generated video
99
- enabled, confirm the only choices are Templates and Full AI video and that the
100
- first generated shot continues the spoken hook without repeating it.
99
+ enabled, confirm that relevant generated shots and editorial templates form one
100
+ cinematic response, and the first shot continues the hook without repeating it.
101
101
 
102
102
  The generated local authorization accepts localhost only. Replace it with your
103
103
  real session check, rate limits, and usage policy before deploying.
@@ -67,8 +67,7 @@ hover movement remain inside the rail's available space.
67
67
  ## Layout and access
68
68
 
69
69
  The foreground video preserves its scene ratio, so diagrams and text remain
70
- visible without cropping or distortion. Ambient opening media fills the
71
- surrounding viewport, with a gradient fallback when media is unavailable.
70
+ visible without cropping or distortion. The surrounding viewport remains black during playback.
72
71
  Display layout does not rewrite the saved response's orientation. See
73
72
  [responsive orientation](responsive-orientation.md) for saved videos and custom
74
73
  players.
@@ -79,9 +78,9 @@ radio behavior, and popovers support Escape and outside-click dismissal.
79
78
  Reduced motion removes movement and fades; increased contrast strengthens
80
79
  surface opacity and borders.
81
80
 
82
- ## Branding
81
+ ## Customization
83
82
 
84
83
  Use the existing `welcomeTitle`, `className` and `options` props to customize
85
- copy, scoped CSS properties and generated video branding. There is no separate
84
+ copy, scoped CSS properties and generated footage direction. There is no separate
86
85
  light/dark appearance picker. See [customization](customization.md) for examples.
87
86
  Use `useVideoChat` when the application needs to own the whole interface.
@@ -0,0 +1,34 @@
1
+ # Cinematic contract migration
2
+
3
+ The eight-template replacement is an approved breaking pre-1.0 change. Persisted Video schema is now `0.2`; event protocol is `0.6`.
4
+
5
+ ## Breaking changes
6
+
7
+ - Removed `VideoInput.brand`, `VideoStyle.brand`, `VideoBrand`, `VideoBrandInput`, `VideoBackground`, and `resolveVideoBrand`. Graphics use black, white/neutral text and system typography. Naturally colored media is unaffected.
8
+ - Removed all 28 old built-in IDs. Eight new IDs describe different schemas: `cinemaMedia`, `chapterTitle`, `focusCards`, `editorialTimeline`, `mobileMessage`, `comparison`, `quote`, `keyFigure`.
9
+ - `parseVideo` rejects the old persisted `0.1` fixture with `unsupported_video_version`. The parser also rejects `style.brand` if a caller merely stamps the new version onto an old object.
10
+ - Scene source intent uses `mediaKeyword` (up to 80 characters) and optional `mediaSource`. The host owns `mediaUrl`, `mediaPoster` and resolved `mediaType`. No gradient fallback mode. Full-bleed requires a resolvable intent or asset; Reach out can remain on black when media is unavailable.
11
+
12
+ ## Adoption
13
+
14
+ Regenerate videos from the retained source and narration using the new catalog. Do not automatically relabel old IDs: before/after emojis, count-ups and factual quote/stat schemas have different meanings. Retain an older published SDK in a separate legacy playback boundary if historical exports must continue to render; do not pass those payloads into the new parser.
15
+
16
+ Before (old persisted format):
17
+
18
+ ```json
19
+ {"schemaVersion":"0.1","scenes":[{"id":"one","templateId":"media","variables":{"texts":"A new perspective","mediaType":"gradient"},"timing":{"fixedDuration":4}}],"style":{"brand":{"font":"Inter"}}}
20
+ ```
21
+
22
+ After re-authoring that title as a chapter:
23
+
24
+ ```json
25
+ {"schemaVersion":"0.2","scenes":[{"id":"one","templateId":"chapterTitle","variables":{"title":"A new perspective"},"timing":{"fixedDuration":4}}],"style":{}}
26
+ ```
27
+
28
+ Evidence: persistence tests preserve the untouched old release fixture and assert rejection, accept current-style round trips, reject hidden/non-JSON input as before, and pin new checksums. Template tests cover both orientations, deterministic seeking, exact evidence text and single-decoder backdrop ownership. Final packed consumer and mobile playback checks remain release gates.
29
+
30
+ ## Render fonts
31
+
32
+ The cinematic templates use `-apple-system, BlinkMacSystemFont, "Helvetica Neue", Roboto, Arial, sans-serif` at regular and medium weights. Apple devices keep native system typography. The existing `@vanillaskyai/video/video-chat.css` entry registers packaged Roboto v51 WOFF2 subsets as the fallback for environments without those fonts. Standalone player and source-owned template integrations should also import that stylesheet. No font is fetched from Google at runtime; browsers fetch local packaged subsets only when Roboto is selected for the rendered glyphs. The font assets include the SIL Open Font License and a source/hash manifest.
33
+
34
+ Live native fonts and a Linux renderer’s Roboto have slightly different metrics. For repeatable exports, keep the browser, installed fonts, viewport and package version fixed, and await `document.fonts.ready` after mounting the final scene before capturing frames. This is a host export responsibility; the SDK does not add a separate export API or force downloaded fonts onto Apple devices.
@@ -6,20 +6,19 @@ VanillaSky keeps provider choice in the application. The SDK defines small
6
6
  server callbacks, advertises only the capabilities you configure, and keeps
7
7
  all credentials out of React and the browser bundle.
8
8
 
9
- ## Two visual modes
9
+ ## One cinematic mode
10
10
 
11
- The chat exposes only two clear choices:
11
+ The director combines relevant footage and eight editorial templates. Graphics
12
+ use black and white; footage carries atmosphere and concrete action. Each media
13
+ scene declares `mediaSource: "stock"` or `"generate"`. Provider availability and
14
+ host budgets constrain those choices. There is no all-stock or all-AI switch.
12
15
 
13
- | Mode | Visual source | Required callback |
14
- | --- | --- | --- |
15
- | `templates` | Trusted rendered templates, optionally with stock footage | none; `searchMedia` is optional |
16
- | `full` | A generated clip for every visual beat | `generateVideo` |
16
+ The canonical templates are `cinemaMedia`, `chapterTitle`, `focusCards`,
17
+ `editorialTimeline`, `mobileMessage`, `comparison`, `quote`, and `keyFigure`.
18
+ Only `cinemaMedia` and `mobileMessage` accept media backgrounds. Full-bleed
19
+ footage has no headline; narration and subtitles carry the explanation.
17
20
 
18
- Templates are always available and are the fast, inexpensive fallback. The
19
- `full` mode is advertised only when `generateVideo` exists. VanillaSky does not
20
- offer a mixed mode that generates only some scenes.
21
-
22
- ## Stock media for templates
21
+ ## Reviewed stock
23
22
 
24
23
  Add `searchMedia` when template answers, the welcome screen, and follow-up
25
24
  cards should use approved photography or footage:
@@ -46,15 +45,16 @@ createVideoChatHandler({
46
45
  The planner emits a short semantic keyword, not a URL. The callback returns an
47
46
  application-approved image or video URL, and the SDK validates it before it
48
47
  reaches a scene. Return `null` when no licensed, safe, relevant asset exists;
49
- the template falls back to its built-in treatment.
48
+ `cinemaMedia` becomes a chapter using its grounded `fallbackText`; a message can
49
+ keep its content on black. Missing fallback copy produces an explicit error.
50
50
 
51
51
  For Pexels, keep `PEXELS_API_KEY` on the server, enforce a deadline, filter for
52
52
  orientation, and return only validated Pexels asset domains. Licensing,
53
53
  attribution, caching, MIME checks, and byte limits remain application-owned.
54
54
 
55
- ## Full generated video
55
+ ## Planned generated shots
56
56
 
57
- Add `generateVideo` to enable full AI video. It receives the planned visual
57
+ Add `generateVideo` to enable generated shots within cinematic responses. It receives the planned visual
58
58
  subject plus the generated look so every clip can follow the same direction:
59
59
 
60
60
  ```ts
@@ -47,22 +47,14 @@ only when two ranges are contiguous (allowing floating-point arithmetic noise)
47
47
  and both templates declare `usesGlobalTransition: true` with valid
48
48
  `transitionTiming` metadata. It is also conditional on the effective backdrop:
49
49
  the 300 ms outer crossfade runs only when the resolved background media changes.
50
- Scenes that share the brand gradient, or the same resolved media backdrop, do
50
+ Scenes that share the black base, or the same resolved media backdrop, do
51
51
  not crossfade. This keeps one stable background visible while each template
52
52
  plays its own entrance, hold, and exit choreography.
53
53
 
54
- Animated brand gradients use a closed, eased loop per scene. Every gradient
55
- family reaches the exact same zero-velocity frame at progress `0` and `1`, and
56
- content-seeded variation converges before the boundary. A same-gradient cut
57
- therefore changes only the foreground template; the backdrop cannot jump or
58
- flash between scene-specific phases.
59
-
60
- The player owns a persistent brand-color backdrop beneath every scene. Built-in
61
- renderers preload through the same component state used for playback, so their
62
- first frame does not suspend when a new template type appears. If a genuinely
63
- cold custom renderer does suspend, its transparent loading frame reveals the
64
- brand backdrop rather than a black canvas. Scene media still belongs to the
65
- scene and covers that base only while the media scene is active.
54
+ The player owns a fixed black backdrop beneath the scenes. Built-in renderers
55
+ preload through the same component state used for playback. A cold custom
56
+ renderer reveals this black base while loading. Resolved scene media covers it
57
+ only while the media scene is active.
66
58
 
67
59
  During a changed-media overlap, the current scene continues to its exact end.
68
60
  The incoming component may be pre-mounted for media readiness, but remains
@@ -98,7 +90,7 @@ also protects intentional hard cuts; it does not pause or otherwise alter the
98
90
  visual or soundtrack clocks.
99
91
 
100
92
  Undefined or unknown transition names preserve a hard cut and unmodified local
101
- motion. Overlapping ranges also hard-cut. A timeline gap renders the owned brand
93
+ motion. Overlapping ranges also hard-cut. A timeline gap renders the fixed black
102
94
  background instead of replaying an earlier scene.
103
95
 
104
96
  ## Reduced motion
@@ -10,7 +10,7 @@ SDK serializer. An interrupted turn can remain visible without qualifying as
10
10
  completed conversation history. The default `VideoChat` keeps history in memory;
11
11
  use the headless hook when the application needs durable storage.
12
12
 
13
- Every stored video has `schemaVersion: "0.1"`. This storage version is separate
13
+ Every stored video has `schemaVersion: "0.2"`. This storage version is separate
14
14
  from streaming protocol `0.5`. The 0.1 policy supports the current storage
15
15
  schema only: there are no compatibility aliases or implicit coercions.
16
16
 
@@ -39,12 +39,12 @@ The release verifier compiles this exact documented snippet against the packed
39
39
  SDK artifact, including its root and React subpath imports.
40
40
 
41
41
  `parseVideo(value: unknown)` validates the complete shape, known fields,
42
- resolved brand and style, audio, metadata, unique scenes, timing, and JSON-safe
42
+ style, audio, metadata, unique scenes, timing, and JSON-safe
43
43
  template variables. It returns a detached, deeply frozen `Video`, so later
44
44
  changes to the loaded object cannot mutate player state.
45
45
 
46
46
  Invalid data throws `VideoValidationError` with `code: "invalid_video"`.
47
- Unknown or future storage versions throw the same error class with
47
+ Previous, unknown, or future storage versions throw the same error class with
48
48
  `code: "unsupported_video_version"`. `<VideoPlayer video={value} />` repeats
49
49
  this boundary validation and rejects the entire value before any renderer
50
50
  runs; it never renders a partial future document.
@@ -20,12 +20,14 @@ The handler rejects unknown templates and fields, invalid variables, unsafe
20
20
  media, and fabricated quote-template content before a scene reaches the
21
21
  player. Read the [security guide](security.md) for the complete controls.
22
22
 
23
- ## Visual modes and providers
23
+ ## Cinematic direction and providers
24
24
 
25
- Keep templates available on every deployment. They are the reliable fallback
26
- when stock, speech, or generated-video providers are missing or late. Add
27
- `searchMedia` for approved stock footage and `generateVideo` for the separate
28
- full AI video mode. Do not expose a partial generated-video mode.
25
+ One cinematic mode combines footage and editorial graphics according to the
26
+ story. Keep templates available as grounded fallbacks. Add `searchMedia` for
27
+ reviewed stock and `generateVideo` for selected illustrative shots. The planner
28
+ sets `mediaSource` per media scene; the host enforces the generation budget.
29
+ A stock candidate must match the subject, action and permitted crop. Return
30
+ `null` for uncertainty rather than broadening an essential detail.
29
31
 
30
32
  Use explicit provider deadlines. Generated video should use idempotency keys
31
33
  and `maxRetries: 0` so one visible action cannot silently create several
@@ -36,13 +38,13 @@ licensing before use.
36
38
 
37
39
  The planner's first streamed object supplies the spoken hook and media keyword.
38
40
  Start speech immediately, resolve stock in parallel, and keep the opening
39
- playing until the first narrated scene and its media are ready. Welcome cards
41
+ playing until the contiguous preparation cushion and first frame are ready. Welcome cards
40
42
  should carry a prepared hook and preloaded footage so they can start without a
41
43
  model round trip.
42
44
 
43
45
  Do not wait for the complete plan before showing the first validated scene.
44
46
  Preload upcoming assets and keep the current visual if the next one is late.
45
- For full AI video, reserve the first shot in the opening object so generation
47
+ When generated footage is useful, reserve the first shot in the opening object so generation
46
48
  can begin while the planner streams later scenes.
47
49
 
48
50
  ## Data and privacy
@@ -101,8 +103,8 @@ npm test
101
103
  - [ ] Keys exist only in the server secret store.
102
104
  - [ ] Authentication, tenant policy, rate limits, and origin allowlist are live.
103
105
  - [ ] Cancellation, timeouts, fallbacks, and safe errors are tested.
104
- - [ ] Template mode completes when every optional provider is unavailable.
105
- - [ ] Full mode appears only when generated video is configured.
106
+ - [ ] Grounded graphic recovery works when every optional provider is unavailable.
107
+ - [ ] Per-scene media choices obey provider availability and spending limits.
106
108
  - [ ] Both orientations render and narration stays synchronized.
107
109
  - [ ] A packed-artifact consumer and deterministic browser chat pass.
108
110
  - [ ] One bounded real-provider run meets the product's latency and quality target.
@@ -61,11 +61,10 @@ dependencies and never enter the browser bundle.
61
61
  The planner emits a 6-9 word hook first, then continues into the scenes in the
62
62
  same stream. The stock lookup is a separate cancellable request, so it cannot
63
63
  delay speech or planning. A welcome card can carry a prewritten `opening`, which
64
- starts immediately with its already-loaded media. For a full AI response, the
64
+ starts immediately with its already-loaded media. When generated shots are available, the
65
65
  same first streamed object reserves the exact first body scene. Its clip starts
66
- generating while the planner continues with scenes two through five. Template
67
- responses stream their first resolved scene without waiting for the rest of the
68
- plan, and normal narration is written in that same planner call rather than
66
+ generating while the planner continues the story. Playback begins after its
67
+ contiguous preparation cushion, without requiring the entire plan, and normal narration is written in that same planner call rather than
69
68
  through a second model round trip.
70
69
 
71
70
  The matching complete React interface is one component and one scoped style
@@ -121,14 +120,10 @@ streamText: ({ systemPrompt, userPrompt, signal }) => streamText({
121
120
  }),
122
121
  ```
123
122
 
124
- Measured on one grounded chat answer with the 28 built-in templates, leaving
125
- the Anthropic default in place cost roughly twenty seconds before the first
126
- scene; disabling reasoning brought the same plan to a few seconds. Other
127
- providers expose equivalent controls under their own names. Treat the exact
128
- values as host-owned tuning: the lowest effort setting is the fastest, but a
129
- weaker plan misses schema limits more often, which shows up as rejected scenes
130
- in `onComplete`. Compare `timeToFirstSceneMs` and `rejectedSceneCount` across
131
- settings before fixing one.
123
+ Reasoning settings can substantially affect startup latency. Measure them with
124
+ your installed catalog and representative requests. Compare first-scene timing,
125
+ rejected scenes and factual accuracy; the fastest token stream is not useful if
126
+ its scenes cannot be rendered. Keep these settings in the provider adapter.
132
127
 
133
128
  VanillaSky never sets these controls. Provider selection, sampling parameters,
134
129
  and credentials stay with the application.
@@ -185,10 +180,8 @@ is intentionally substantial. It is stable for the same SDK version, template
185
180
  kit, media policy, and base prompt. Record input-token usage, keep the selected
186
181
  kit no broader than the product needs, and enable provider-side prompt caching
187
182
  where the chosen provider/model supports it. VanillaSky does not assume one
188
- provider's cache controls in its provider-neutral adapter. With the 28 built-in
189
- templates, the current catalog prompt is roughly 28,000 characters (about
190
- 7,000 tokens before user input; tokenizer-dependent); provider-reported usage
191
- is the authoritative measurement.
183
+ provider's cache controls in its provider-neutral adapter. The cinematic catalog contains eight templates; use provider-reported token
184
+ usage as the authoritative measurement rather than a character estimate.
192
185
 
193
186
  Provider finish reasons `error` and `tool-calls` are terminal failures.
194
187
  `length` and `content-filter` may complete with already accepted scenes; a
@@ -87,10 +87,10 @@ body { background: #080b16; }
87
87
  <div class="doc-intro"><div class="doc-kicker">The immersive interface · Component reference</div><h1>The picture comes first.<br><span>Conversation stays close.</span></h1><p class="doc-lede">One video canvas. A quiet layer of controls. Every surface, state and detail of the current VanillaSky experience, together in one place.</p></div>
88
88
  <nav class="doc-index" aria-label="Reference sections"><a href="#surfaces">Surfaces & navigation</a><a href="#panels">Settings & history</a><a href="#conversation">Conversation</a><a href="#captions">Subtitles</a><a href="#suggestions">Suggestions</a><a href="#foundations">Foundations</a></nav>
89
89
  <section class="doc-section" id="surfaces"><div class="section-heading"><span class="number">01</span><h2>Present without competing</h2></div><p class="section-copy">Dark glass, soft edges and restrained lavender. Navigation sits above the picture, with the logo and a quiet cluster of actions.</p>
90
- <div class="doc-preview"><header class="chrome"><div class="session-brand"><img class="brand-logo" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI4LjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjE1NSAxMTMgMTY5MCA0MjIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMTU1IDExMyAxNjkwIDQyMjsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MHtmaWxsOnVybCgjU1ZHSURfMV8pO30KCS5zdDF7ZmlsbDp1cmwoI1NWR0lEXzAwMDAwMDEzMTc5MjQ3NzkzMzEzNjczNjYwMDAwMDA5NTAyNDY0NTI0MDQ3MTc5Njg2Xyk7fQoJLnN0MntmaWxsOnVybCgjU1ZHSURfMDAwMDAxMTQwNzU2MTAxNzA2MjA3NDYzMDAwMDAwMTMwNzY4NTczMjc1MDMwNTk4NTJfKTt9Cgkuc3Qze2ZpbGw6dXJsKCNTVkdJRF8wMDAwMDAzOTg0Mzk1Mzc4NTIyOTI0Mjg4MDAwMDAwODQwMzMzMzkyMDEyMjcwNTMyOF8pO30KCS5zdDR7ZmlsbDp1cmwoI1NWR0lEXzAwMDAwMDI3NTkyNTEzNTc2NDg3NDgzMzAwMDAwMDE2NzQwODQ2NzM4OTM4NjE5NzkyXyk7fQoJLnN0NXtmaWxsOnVybCgjU1ZHSURfMDAwMDAwNzM2ODI1Njc2NTkxMzUzNzk0NjAwMDAwMDQyOTE4NTk2MTM4MzIzNjcyNDZfKTt9Cgkuc3Q2e2ZpbGw6dXJsKCNTVkdJRF8wMDAwMDA0MzQyOTI4Mzg1Nzk2OTQ0NDE5MDAwMDAwNzg0MjM5Nzg0MTEwMTYxNjAxN18pO30KCS5zdDd7ZmlsbDojRkZGRkZGO30KPC9zdHlsZT4KPGc+Cgk8Zz4KCQk8Zz4KCQkJCgkJCQk8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzFfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjE0NC43Mzg4IiB5MT0iMzYxLjU0MjIiIHgyPSIxMTQ1LjM4NjYiIHkyPSIzMDguNTkwNyIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAwIDY1MCkiPgoJCQkJPHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6I0UwNEY4QSIvPgoJCQkJPHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6I0VDOUEyQyIvPgoJCQk8L2xpbmVhckdyYWRpZW50PgoJCQk8cGF0aCBjbGFzcz0ic3QwIiBkPSJNNDMxLjkzLDI5OC42N2MtMC42Mi0wLjYtMS4zNi0xLjExLTIuMTktMS41N2MtMi40Mi0xLjMyLTUuNjQtMS45OC05LjctMS45OAoJCQkJYy0xNi4xMiwwLTMwLjQ0LTEuMDYtNDIuOTctMy4xOWMxMS44My0zMi4yLDE3Ljc2LTYxLjkyLDE3Ljc2LTg5LjE1YzAtMTguOTMtNC40NS0zMy44NC0xMy4zMi00NC43NAoJCQkJYy04Ljg5LTEwLjg3LTIwLjg5LTE2LjMyLTM2LjA1LTE2LjMyYy0xMi4zMiwwLTIzLjUsMy4xOS0zMy41NSw5LjU5Yy0xMC4wOCw2LjQtMTgsMTYuMDQtMjMuOCwyOC45NQoJCQkJYy01LjgxLDEyLjg5LTguNywyOC43LTguNyw0Ny40MWMwLDE4LjcsMy42NiwzNS4yMSwxMSw0OS41NGM3LjM0LDE0LjMyLDE4LjIzLDI1Ljk5LDMyLjY3LDM0Ljk3CgkJCQljLTEyLjA2LDI1LjgyLTI1LjMzLDQ3LjI0LTM5Ljc2LDY0LjI4Yy0xNC40NiwxNy4wNi0yNy44MiwyNS41Ny00MC4xNCwyNS41N2MtMTIuMDYsMC0yMS4xMi05LjQtMjcuMTYtMjguMjMKCQkJCWMtNi4wNC0xOC44Mi05LjA2LTQ2LjMzLTkuMDYtODIuNTZjMC0zNC41NSwzLjU1LTczLjE1LDEwLjY2LTExNS43NmMwLjQ3LTMuNzksMC43LTYuNCwwLjctNy44MWMwLTEzLjUxLTcuODEtMjAuMjUtMjMuNDQtMjAuMjUKCQkJCWMtOS40NywwLTE2LjE1LDIuMTktMjAuMDYsNi41N3MtNi45MywxMi43NC05LjA2LDI1LjA0Yy01LjkzLDMzLjE0LTguODcsNzAuNTQtOC44NywxMTIuMjFjMCw0OC43OCw2LjA0LDg3LjQ5LDE4LjEsMTE2LjEyCgkJCQljMTIuMDgsMjguNjcsMzAuNDIsNDIuOTcsNTUuMDUsNDIuOTdjMjMuOTEsMCw0Ny43Ni0xMS42NCw3MS41Ni0zNC45N2MyMy44LTIzLjMzLDQ0LjA4LTUyLjYxLDYwLjktODcuODkKCQkJCWMxMC42MSwyLjQsMjIuMzMsMy43OSwzNS4xNCw0LjIxYzQuOTMsMC4xNywxMC4wMiwwLjE3LDE1LjI5LDAuMDRjNy44MS0wLjIzLDEzLjU1LTIuNzIsMTcuMjMtNy40NQoJCQkJYzMuNjYtNC43NCw1LjUxLTEwLjE3LDUuNTEtMTYuMzJDNDM1LjY3LDMwNC4xMiw0MzQuNDIsMzAxLjA0LDQzMS45MywyOTguNjd6IE0zMzcuNjUsMjc2LjY2CgkJCQljLTE0LjkxLTExLjEzLTIyLjM4LTI4LjI5LTIyLjM4LTUxLjVjMC0xMy43MiwyLjAyLTI0LjMzLDYuMDQtMzEuNzhjNC4wNC03LjQ1LDkuMjUtMTEuMTksMTUuNjMtMTEuMTkKCQkJCWM0LjQ5LDAsOC4wNiwyLjMsMTAuNjYsNi45M2MyLjYsNC42MiwzLjkxLDExLjg5LDMuOTEsMjEuODNDMzUxLjUsMjI4LjI0LDM0Ni44NywyNTAuMTUsMzM3LjY1LDI3Ni42NnoiLz4KCQkJCgkJCQk8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzAwMDAwMDg5NTUyODM1Mzg3NjQwNjA5ODkwMDAwMDA0ODExOTA0NjIyNjQ2MDYyNzMyXyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIxMzcuMjUzNSIgeTE9IjMwOC44OTY2IiB4Mj0iMTEzNy45MDE0IiB5Mj0iMjU1Ljk0NTEiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMCA2NTApIj4KCQkJCTxzdG9wICBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNFMDRGOEEiLz4KCQkJCTxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNFQzlBMkMiLz4KCQkJPC9saW5lYXJHcmFkaWVudD4KCQkJPHBhdGggc3R5bGU9ImZpbGw6dXJsKCNTVkdJRF8wMDAwMDA4OTU1MjgzNTM4NzY0MDYwOTg5MDAwMDAwNDgxMTkwNDYyMjY0NjA2MjczMl8pOyIgZD0iTTc0Ny45LDM3My42MQoJCQkJYy0xLjc3LTIuODUtNC4yMS00LjI3LTcuMjgtNC4yN2MtMy41NSwwLTYuNzQsMS42Ni05LjU5LDQuOTZjLTQuMSw0LjkxLTcuODcsOS4yNy0xMS4zMiwxMy4xM2MtNS43Nyw2LjQ1LTEwLjcsMTEuNDctMTQuNzgsMTUuMQoJCQkJYy02LjUxLDUuODEtMTIuMTMsOC43LTE2Ljg3LDguN2MtNi44NywwLTEwLjMtNC43Mi0xMC4zLTE0LjE5YzAtNi42MiwyLjI2LTIwLjI1LDYuNzYtNDAuODRjNS4yMS0yNC4xNiw3LjgxLTQwLjg0LDcuODEtNTAuMDkKCQkJCWMwLTE1LjM4LTcuNy0yMy4wOC0yMy4wOC0yMy4wOGMtNy44MSwwLTE2LjQsMi43NC0yNS43NCw4LjE3Yy05LjM2LDUuNDUtMTguODksMTQuNTctMjguNTksMjcuMzUKCQkJCWMtOS43MiwxMi43OC0xOC40OCwyOS4zNi0yNi4yOSw0OS43MWMwLjk0LTEwLjQsMi4xMy0xOS4xNywzLjU1LTI2LjI3YzEuNDMtNy4xLDMuMzItMTUuMzgsNS43LTI0Ljg1CgkJCQljMy41NS0xMy4yNyw1LjMyLTIxLjc4LDUuMzItMjUuNTdjMC0zLjMyLTAuOTYtNS41Ny0yLjg1LTYuNzZjLTEuODktMS4xNy01LjIxLTEuNzctOS45NS0xLjc3Yy04Ljc2LDAtMTUuNTcsMS42LTIwLjQyLDQuNzkKCQkJCWMtNC44NSwzLjIxLTguMzQsOC4yMy0xMC40NywxNS4xYy04LjM0LDI2LjM4LTEyLjcsNTQuMTYtMTMuMSw4My4zNGMtMC4wMiwxLjQ1LTAuMDQsMi45MS0wLjA0LDQuMzhjMCwxNiwxLjA4LDI4LjY3LDMuMjYsMzcuOTcKCQkJCWMwLjY0LDIuNzksMS40LDUuMywyLjI1LDcuNDljMy42Niw5LjQ3LDkuOTgsMTQuMTksMTguOTgsMTQuMTljNy4zNCwwLDEyLjc0LTEuOTQsMTYuMTctNS44NWMzLjQyLTMuOTEsNi40NC0xMS40Miw5LjA0LTIyLjU1CgkJCQljMy41Ny0xNC40NCw4LjIzLTI4LjE4LDE0LjA0LTQxLjJjNS43OS0xMy4wMiwxMS40Ny0yMy41LDE3LjA0LTMxLjQyYzUuNTctNy45Myw5Ljg5LTExLjkxLDEyLjk2LTExLjkxCgkJCQljMi42LDAsMy45MSwxLjkxLDMuOTEsNS42OGMwLDUuNy0xLjU1LDE1Ljk4LTQuNjIsMzAuOTFjLTMuMzIsMTcuMDQtNC45NiwyOC44Ny00Ljk2LDM1LjVjMCwxMy4yNywzLjA4LDIzLjM4LDkuMjMsMzAuMzYKCQkJCWM2LjE1LDYuOTgsMTUuMjcsMTAuNDcsMjcuMzUsMTAuNDdjMTQuOTEsMCwyOC4yMy00LjAyLDM5Ljk1LTEyLjA2YzUuMTctMy41NywxMC4xOS03LjU1LDE1LjA4LTExLjk2CgkJCQljNi4xNS01LjYyLDEyLjA4LTExLjkzLDE3Ljc4LTE4Ljk1YzQuNDktNS4xOSw2Ljc0LTEyLjUzLDYuNzQtMjJDNzUwLjU2LDM4MC4zNCw3NDkuNjcsMzc2LjQ0LDc0Ny45LDM3My42MXoiLz4KCQkJCgkJCQk8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzAwMDAwMDM2OTY2Mjg4NDUyMDI4MTAzOTgwMDAwMDA2MTQ5NTUyMTE1MTU0NzIxOTQ4XyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIxMzkuNzQyOCIgeTE9IjM1NS45MzgyIiB4Mj0iMTE0MC4zOTA3IiB5Mj0iMzAyLjk4NjciIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMCA2NTApIj4KCQkJCTxzdG9wICBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNFMDRGOEEiLz4KCQkJCTxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNFQzlBMkMiLz4KCQkJPC9saW5lYXJHcmFkaWVudD4KCQkJPHBhdGggc3R5bGU9ImZpbGw6dXJsKCNTVkdJRF8wMDAwMDAzNjk2NjI4ODQ1MjAyODEwMzk4MDAwMDAwNjE0OTU1MjExNTE1NDcyMTk0OF8pOyIgZD0iTTgzMy40OCwzNzMuNjEKCQkJCWMtMS43Ny0yLjg1LTQuMjEtNC4yNy03LjI4LTQuMjdjLTMuNTUsMC02Ljc2LDEuNjYtOS41OSw0Ljk2Yy0yLjEzLDIuNTctNC4xNyw0Ljk4LTYuMTMsNy4yN2MtNy44NSw5LjEzLTE0LjMyLDE2LjA4LTE5LjQ0LDIwLjgKCQkJCWMtNi40LDUuOTMtMTEuNzIsOC44Ny0xNS45OCw4Ljg3Yy00LjQ5LDAtNy43LTEuODktOS41OS01LjY4Yy0xLjg5LTMuNzctMi44My05LjIzLTIuODMtMTYuMzJjMC05LjI1LDIuODMtMjYuODcsOC41MS01Mi45MgoJCQkJYzQuNDktMjEuNTMsNi43Ni0zNS4yNyw2Ljc2LTQxLjJjMC04LjA0LTYuMDQtMTIuMDgtMTguMTItMTIuMDhjLTcuODEsMC0xMy40OSwxLjQzLTE3LjA0LDQuMjdjLTMuNTUsMi44NS02LjQsOC4wNi04LjUzLDE1LjYzCgkJCQljLTQuMjcsMTQuNDQtNy43NiwzMC4wMS0xMC40Nyw0Ni43MWMtMi40NSwxNS4wNi0zLjc5LDI3LjY1LTQuMDQsMzcuOGMtMC4wMiwxLjA5LTAuMDQsMi4xNy0wLjA0LDMuMjEKCQkJCWMwLDE0LjA0LDIuMTMsMjUuOTEsNi4zOCwzNS42M2MxLjE5LDIuNzQsMi41Nyw1LjMsNC4xLDcuN2M2Ljk4LDEwLjg5LDE4LjE1LDE2LjMyLDMzLjU1LDE2LjMyYzE3Ljg3LDAsMzYuOTEtMTEuMDIsNTcuMTYtMzMuMQoJCQkJYzIuODEtMy4wOCw1LjY2LTYuMzYsOC41My05Ljg3YzQuNTEtNS4xOSw2Ljc2LTEyLjUzLDYuNzYtMjJDODM2LjE0LDM4MC4zNCw4MzUuMjYsMzc2LjQ0LDgzMy40OCwzNzMuNjF6IE03OTIuMTIsMjEwLjYxCgkJCQljLTUuNDUtNC4yNy0xMi41Ny02LjQtMjEuMzItNi40Yy05LjcsMC0xNy44MiwyLjc3LTI0LjMzLDguMzRjLTYuNTEsNS41Ny05Ljc2LDEyLjQ5LTkuNzYsMjAuNzhjMCw4LjI4LDIuNDksMTQuNzQsNy40NSwxOS4zNAoJCQkJYzQuOTgsNC42MiwxMi40NCw2LjkzLDIyLjM4LDYuOTNjMTAuMTcsMCwxOC4zNC0yLjc3LDI0LjUtOC4zNGM2LjE1LTUuNTUsOS4yMy0xMy4wOCw5LjIzLTIyLjU1CgkJCQlDODAwLjI3LDIyMC45LDc5Ny41NSwyMTQuODYsNzkyLjEyLDIxMC42MXoiLz4KCQkJCgkJCQk8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzAwMDAwMTY5NTQ0MDU3OTMxOTgyMTQwNjIwMDAwMDAzNjMwOTA4MjUzMDQ2NjA0NDcxXyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIxNDIuNDM4MiIgeTE9IjQwNi44NzQ3IiB4Mj0iMTE0My4wODYxIiB5Mj0iMzUzLjkyMzIiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMCA2NTApIj4KCQkJCTxzdG9wICBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNFMDRGOEEiLz4KCQkJCTxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNFQzlBMkMiLz4KCQkJPC9saW5lYXJHcmFkaWVudD4KCQkJPHBhdGggc3R5bGU9ImZpbGw6dXJsKCNTVkdJRF8wMDAwMDE2OTU0NDA1NzkzMTk4MjE0MDYyMDAwMDAwMzYzMDkwODI1MzA0NjYwNDQ3MV8pOyIgZD0iTTk0Ny40NywzNzMuNjEKCQkJCWMtMS43Ny0yLjg1LTQuMjEtNC4yNy03LjI4LTQuMjdjLTMuNTUsMC02Ljc0LDEuNjYtOS41OSw0Ljk2Yy0yLjExLDIuNTMtNC4xNyw0LjkxLTYuMTUsNy4xMwoJCQkJYy04LjYyLDkuNzktMTUuOTMsMTYuOTUtMjEuODksMjEuNDZjLTcuMzQsNS41Ny0xNS44Nyw4LjM0LTI1LjU3LDguMzRjLTYuMTcsMC0xMS4xMy0yLjA2LTE0LjkzLTYuMjEKCQkJCWMtMy43OS00LjEzLTYuNTEtMTEuMDYtOC4xNy0yMC43OGMyNi43Ni0zNi40NCw0Ny45NS03My4yNiw2My41OC0xMTAuNDRjMTUuNjMtMzcuMTYsMjMuNDQtNjkuODQsMjMuNDQtOTguMDIKCQkJCWMwLTE5LjY1LTMuMjYtMzQuNzQtOS43OC00NS4yN2MtNi41MS0xMC41My0xNS40NC0xNS44LTI2LjgtMTUuOGMtMjAuMTQsMC0zNy40OCwxMy4yNS01Mi4wMywzOS43NgoJCQkJYy0xNC41NywyNi41MS0yNS41Nyw1OC4yNC0zMy4wMyw5NS4xN2MtNy40NSwzNi45My0xMS4xOSw2OS44NC0xMS4xOSw5OC43NGMwLDExLjk1LDAuNzksMjMsMi40LDMzLjE4CgkJCQljMi4wNiwxMy40LDUuNTMsMjUuMjcsMTAuMzgsMzUuNjNjMC43NCwxLjYsMS41MSwzLjE3LDIuMzIsNC43YzEwLjA2LDE4Ljk1LDI1LjUsMjguNCw0Ni4zNSwyOC40CgkJCQljMTUuMTQsMCwyOC42OS00LjAyLDQwLjY1LTEyLjA2YzguNjgtNS44NSwxNi44OS0xMi44NywyNC42NS0yMS4xYzIuOTEtMy4wOSw1Ljc3LTYuMzgsOC41Ny05LjgxYzQuNDktNS4xOSw2Ljc0LTEyLjUzLDYuNzQtMjIKCQkJCUM5NTAuMTMsMzgwLjM0LDk0OS4yNCwzNzYuNDQsOTQ3LjQ3LDM3My42MXogTTg1OS45MiwyNTIuNTFjNS4yMS0yOS4xMiwxMS4yNS01My41LDE4LjEyLTczLjE1CgkJCQljNi44Ny0xOS42NiwxMy4wMi0yOS40OCwxOC40OC0yOS40OGM2LjYyLDAsOS45NSw5LDkuOTUsMjYuOTljMCwyMi45Ny00Ljk4LDQ4LjM1LTE0LjkzLDc2LjE3cy0yMy4zMyw1NS41OC00MC4xMiw4My4yOAoJCQkJQzg1MS44OCwzMDkuNTUsODU0LjcxLDI4MS42Miw4NTkuOTIsMjUyLjUxeiIvPgoJCQkKCQkJCTxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfMDAwMDAwOTYwMzk0OTc1NDgzNzkyMzk0NjAwMDAwMTc2ODAwODc0NTg3MzgyMjU1NjVfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjE0Mi43NTY1IiB5MT0iNDEyLjg4ODMiIHgyPSIxMTQzLjQwNDMiIHkyPSIzNTkuOTM2OCIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAwIDY1MCkiPgoJCQkJPHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6I0UwNEY4QSIvPgoJCQkJPHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6I0VDOUEyQyIvPgoJCQk8L2xpbmVhckdyYWRpZW50PgoJCQk8cGF0aCBzdHlsZT0iZmlsbDp1cmwoI1NWR0lEXzAwMDAwMDk2MDM5NDk3NTQ4Mzc5MjM5NDYwMDAwMDE3NjgwMDg3NDU4NzM4MjI1NTY1Xyk7IiBkPSJNMTA2MS40NSwzNzMuNjEKCQkJCWMtMS43Ny0yLjg1LTQuMjEtNC4yNy03LjI3LTQuMjdjLTMuNTcsMC02Ljc2LDEuNjYtOS42MSw0Ljk2Yy03LjE3LDguNTMtMTMuNTUsMTUuNDctMTkuMTIsMjAuODMKCQkJCWMtMy4yNSwzLjEzLTYuMjMsNS43Mi04LjkzLDcuNzZjLTcuMzQsNS41Ny0xNS44Nyw4LjM0LTI1LjU3LDguMzRjLTYuMTUsMC0xMS4xMy0yLjA2LTE0LjkxLTYuMjEKCQkJCWMtMy43OS00LjEzLTYuNTMtMTEuMDYtOC4xNy0yMC43OGMyNi43NC0zNi40NCw0Ny45My03My4yNiw2My41Ni0xMTAuNDRjMTUuNjMtMzcuMTYsMjMuNDQtNjkuODQsMjMuNDQtOTguMDIKCQkJCWMwLTE5LjY1LTMuMjUtMzQuNzQtOS43Ni00NS4yN2MtNi41My0xMC41My0xNS40Ni0xNS44LTI2LjgyLTE1LjhjLTIwLjEyLDAtMzcuNDYsMTMuMjUtNTIuMDMsMzkuNzYKCQkJCWMtMTQuNTUsMjYuNTEtMjUuNTcsNTguMjQtMzMuMDMsOTUuMTdjLTcuNDUsMzYuOTMtMTEuMTksNjkuODQtMTEuMTksOTguNzRjMCwxMS44OSwwLjc5LDIyLjkxLDIuMzgsMzMuMDQKCQkJCWMyLjA2LDEzLjQyLDUuNTMsMjUuMzMsMTAuMzgsMzUuNzFjMC43NCwxLjYyLDEuNTMsMy4yMSwyLjM0LDQuNzZjMTAuMDYsMTguOTUsMjUuNTEsMjguNCw0Ni4zNSwyOC40CgkJCQljMTUuMTUsMCwyOC43LTQuMDIsNDAuNjUtMTIuMDZjMy43OS0yLjU3LDcuNTEtNS4zNCwxMS4xMi04LjM2YzcuNzgtNi40NSwxNS4xNC0xMy45NywyMi4xLTIyLjU1YzQuNDktNS4xOSw2Ljc0LTEyLjUzLDYuNzQtMjIKCQkJCUMxMDY0LjExLDM4MC4zNCwxMDYzLjIzLDM3Ni40NCwxMDYxLjQ1LDM3My42MXogTTk3My45MywyNTIuNTFjNS4xOS0yOS4xMiwxMS4yMy01My41LDE4LjEtNzMuMTUKCQkJCWM2Ljg3LTE5LjY2LDEzLjAyLTI5LjQ4LDE4LjQ4LTI5LjQ4YzYuNjIsMCw5Ljk1LDksOS45NSwyNi45OWMwLDIyLjk3LTQuOTgsNDguMzUtMTQuOTEsNzYuMTcKCQkJCWMtOS45NSwyNy44Mi0yMy4zMyw1NS41OC00MC4xNCw4My4yOEM5NjUuODcsMzA5LjU1LDk2OC43MiwyODEuNjIsOTczLjkzLDI1Mi41MXoiLz4KCQkJCgkJCQk8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzAwMDAwMDQ0ODY0NDU5ODE4NDk5ODM0NDgwMDAwMDE0NzUwODc5MzcyMTAyNDYzMzk3XyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIxMzguNTg5NCIgeTE9IjMzNC4xNDA1IiB4Mj0iMTEzOS4yMzcyIiB5Mj0iMjgxLjE4OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAwIDY1MCkiPgoJCQkJPHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6I0UwNEY4QSIvPgoJCQkJPHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6I0VDOUEyQyIvPgoJCQk8L2xpbmVhckdyYWRpZW50PgoJCQk8cGF0aCBzdHlsZT0iZmlsbDp1cmwoI1NWR0lEXzAwMDAwMDQ0ODY0NDU5ODE4NDk5ODM0NDgwMDAwMDE0NzUwODc5MzcyMTAyNDYzMzk3Xyk7IiBkPSJNMTIwMi41MiwzODIuNzQKCQkJCWMtNC40Myw1Ljc2LTEwLjMsMTIuNTctMTcuMjMsMTguN2MwLDAtMTEuMTIsOS44NS0yMS4zOCw5Ljc5Yy0yLjMsMC00LjA5LTAuOTEtNC4wOS0wLjkxYy0xLjA2LTAuNTMtMi0xLjI4LTIuODMtMi4yOAoJCQkJYy0xLjc3LTIuMTMtMi42Ni00Ljk2LTIuNjYtOC41M2MwLTEwLjY0LDIuNzItMjkuNTksOC4xNy01Ni44YzQuMDYtMjAuMjcsNy40NC0zMC4xMSw1LjMyLTM1LjUyCgkJCQljLTcuMjYtMTguNTctMzcuNDUtMjcuMDctNjAuNy0yMy43N2MtMTUuNTgsMi4yMS0yNi40Myw5LjMtMzMuNTgsMTQuMzZjLTE0LjU3LDEwLjMtMjYuMjMsMjMuOTEtMzQuOTksNDAuODQKCQkJCXMtMTMuMTMsMzQuODgtMTMuMTMsNTMuOGMwLDAuOTEsMC4wMiwxLjgxLDAuMDYsMi43MmMwLjM2LDEzLjc4LDMuNjIsMjUuMzYsOS44MSwzNC43NGMxLDEuNTUsMi4wOSwzLjAyLDMuMjYsNC40NQoJCQkJYzguNzYsMTAuNjQsMjAuNDgsMTUuOTcsMzUuMTYsMTUuOTdjMTguNywwLDM0LjIxLTkuOTUsNDYuNTItMjkuODJjMy4zMiw5LjIzLDguMjMsMTYuNTEsMTQuNzQsMjEuODMKCQkJCWM2LjUxLDUuMzIsMTMuNjgsNy45OCwyMS40OSw3Ljk4YzEwLjE3LDAsMjAuNy00LjEzLDMxLjYxLTEyLjQyYzguNC02LjM4LDE2LjQyLTEzLjg5LDI0LjEtMjIuNTMKCQkJCUMxMjA1LjYyLDQwMy4xNiwxMjAzLjI4LDM5MS4yMywxMjAyLjUyLDM4Mi43NHogTTExMTEuNzEsMzgwLjdjLTIuMTMsMTAuMTktNS41NywxOC4zNC0xMC4zLDI0LjUxCgkJCQljLTQuNzQsNi4xNS05LjcsOS4yMy0xNC45MSw5LjIzYy01LjIxLDAtOS4zLTEuNzctMTIuMjUtNS4zMmMtMi45Ni0zLjU3LTQuNDUtOS00LjQ1LTE2LjM0YzAtMTMuMjcsMi40OS0yNS44LDcuNDUtMzcuNjUKCQkJCWM0Ljk4LTExLjgzLDExLjY2LTIxLjM2LDIwLjA4LTI4LjU5YzguNC03LjIxLDE3LjU3LTEwLjk1LDI3LjUxLTExLjE3TDExMTEuNzEsMzgwLjd6Ii8+CgkJCQoJCQkJPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF8wMDAwMDAxMzg4NDE0OTU1MTMwMDcxMDUxMDAwMDAwOTQ1NjEzMzk1NjE1Mjg5NDYxMl8iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMTMyLjA3ODgiIHkxPSIyOTkuOTE3OCIgeDI9IjExMzIuNzI2NyIgeTI9IjI0Ni45NjY0IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDAgNjUwKSI+CgkJCQk8c3RvcCAgb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojRTA0RjhBIi8+CgkJCQk8c3RvcCAgb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojRUM5QTJDIi8+CgkJCTwvbGluZWFyR3JhZGllbnQ+CgkJCTxwYXRoIHN0eWxlPSJmaWxsOnVybCgjU1ZHSURfMDAwMDAwMTM4ODQxNDk1NTEzMDA3MTA1MTAwMDAwMDk0NTYxMzM5NTYxNTI4OTQ2MTJfKTsiIGQ9Ik01NDkuMjksMzgyLjc0CgkJCQljLTQuNDMsNS43Ni0xMC4zLDEyLjU3LTE3LjIzLDE4LjdjMCwwLTExLjEyLDkuODUtMjEuMzgsOS43OWMtMi4zLDAtNC4wOS0wLjkxLTQuMDktMC45MWMtMS4wNi0wLjUzLTItMS4yOC0yLjgzLTIuMjgKCQkJCWMtMS43Ny0yLjEzLTIuNjYtNC45Ni0yLjY2LTguNTNjMC0xMC42NCwyLjcyLTI5LjU5LDguMTctNTYuOGM0LjA2LTIwLjI3LDcuNDQtMzAuMTEsNS4zMi0zNS41MgoJCQkJYy03LjI2LTE4LjU3LTM3LjQ1LTI3LjA3LTYwLjctMjMuNzdjLTE1LjU4LDIuMjEtMjYuNDMsOS4zLTMzLjU4LDE0LjM2Yy0xNC41NywxMC4zLTI2LjIzLDIzLjkxLTM0Ljk5LDQwLjg0CgkJCQljLTguNzYsMTYuOTMtMTMuMTMsMzQuODgtMTMuMTMsNTMuOGMwLDAuOTEsMC4wMiwxLjgxLDAuMDYsMi43MmMwLjM2LDEzLjc4LDMuNjIsMjUuMzYsOS44MSwzNC43NGMxLDEuNTUsMi4wOSwzLjAyLDMuMjYsNC40NQoJCQkJYzguNzYsMTAuNjQsMjAuNDgsMTUuOTcsMzUuMTYsMTUuOTdjMTguNywwLDM0LjIxLTkuOTUsNDYuNTItMjkuODJjMy4zMiw5LjIzLDguMjMsMTYuNTEsMTQuNzQsMjEuODMKCQkJCWM2LjUxLDUuMzIsMTMuNjgsNy45OCwyMS40OSw3Ljk4YzEwLjE3LDAsMjAuNy00LjEzLDMxLjYxLTEyLjQyYzguNC02LjM4LDE2LjQyLTEzLjg5LDI0LjEtMjIuNTMKCQkJCUM1NTIuMzksNDAzLjE2LDU1MC4wNSwzOTEuMjMsNTQ5LjI5LDM4Mi43NHogTTQ1OC40OCwzODAuN2MtMi4xMywxMC4xOS01LjU3LDE4LjM0LTEwLjMsMjQuNTFjLTQuNzQsNi4xNS05LjcsOS4yMy0xNC45MSw5LjIzCgkJCQlzLTkuMy0xLjc3LTEyLjI1LTUuMzJjLTIuOTYtMy41Ny00LjQ1LTktNC40NS0xNi4zNGMwLTEzLjI3LDIuNDktMjUuOCw3LjQ1LTM3LjY1YzQuOTgtMTEuODMsMTEuNjYtMjEuMzYsMjAuMDgtMjguNTkKCQkJCWM4LjQtNy4yMSwxNy41Ny0xMC45NSwyNy41MS0xMS4xN0w0NTguNDgsMzgwLjd6Ii8+CgkJPC9nPgoJPC9nPgoJPGc+CgkJPHBhdGggY2xhc3M9InN0NyIgZD0iTTEyODguNTksMjMwLjcyYzMuNjQtMi45MSw3LjktNS4xLDEyLjc2LTYuNTZjNC44Ni0xLjQ2LDkuOTktMi4xOSwxNS4zMi0yLjE5YzguNTMsMCwxNS44OCwxLjE1LDIyLjA5LDMuNDUKCQkJYzYuMiwyLjMzLDExLjg0LDUuNDMsMTYuOTcsOS4zYzUuMSwzLjksOS43Nyw4LjQ2LDE0LjAzLDEzLjY4YzQuMjUsNS4yNCw4LjY5LDEwLjksMTMuMzIsMTYuOTdsMzcuMjItMzIuODMKCQkJYy03LjMxLTguNTEtMTQuNTktMTYuMDUtMjEuOS0yMi42M2MtNy4yOC02LjU2LTE1LjA5LTEyLjE1LTIzLjMzLTE2Ljc4Yy04LjI3LTQuNjEtMTcuMTEtOC4xMy0yNi40Ni0xMC41NwoJCQljLTkuMzgtMi40Mi0xOS44OC0zLjY0LTMxLjU2LTMuNjRjLTExLjkxLDAtMjMuNTksMS42OS0zNS4wNCw1LjFjLTExLjQyLDMuNDEtMjEuNjQsOC4zOS0zMC42NCwxNC45NwoJCQljLTksNi41Ni0xNi4yNCwxNC44My0yMS43MSwyNC43OWMtNS40NSw5Ljk5LTguMiwyMS41Mi04LjIsMzQuNjZjMCwxMy42MywzLjQxLDI1LjA1LDEwLjIyLDM0LjMxCgkJCWM2LjgxLDkuMjMsMTUuMzcsMTcuMDgsMjUuNzEsMjMuNTJjMTAuMzQsNi40NiwyMS41NSwxMS45MSwzMy41OCwxNi40MmMxMi4wMyw0LjQ5LDIzLjIyLDguOTMsMzMuNTUsMTMuMzIKCQkJYzEwLjM0LDQuMzcsMTguOTIsOS4wNSwyNS43MywxNC4wM2M2Ljc5LDUsMTAuMjIsMTEuMjYsMTAuMjIsMTguOGMwLDUuNTktMS40MSwxMC4zOS00LjIxLDE0LjRjLTIuOCw0LjAyLTYuMjcsNy4zNS0xMC4zOSwxMC4wMwoJCQljLTQuMTYsMi42OC04Ljc2LDQuNy0xMy44Niw2LjAyYy01LjEyLDEuMzQtMTAuMSwyLjAyLTE0Ljk3LDIuMDJjLTksMC0xNy4wOC0xLjI5LTI0LjI3LTMuODNjLTEuMzYtMC40OS0xMC41NS0zLjY5LTE5LjUtMTAuMjIKCQkJYy0xOS42OS0xNC4zOC0yNi45Ny0zNi42My0yOS4xMS00NC4xM2MtMTAuOTcsOS40LTIxLjkyLDE4LjgtMzIuOSwyOC4yYzEuMzYsOS44NSw1LjQsMjYuNjcsMTguNTksNDAuNzQKCQkJYzguMjcsOC44MSwxNy41MywxMy42LDI0LjYzLDE3LjUxYzguNjUsNC43NSwxNy45Myw4LjI3LDI3LjkyLDEwLjU3YzkuOTYsMi4zLDIxLjE1LDMuNDgsMzMuNTUsMy40OAoJCQljMTAuNzEsMCwyMS45LTEuNzIsMzMuNTgtNS4xMmMxMS42OC0zLjM4LDIyLjM3LTguMzksMzIuMS0xNC45NGM5LjczLTYuNTgsMTcuNzYtMTQuNjYsMjQuMDgtMjQuMjcKCQkJYzYuMzItOS41OSw5LjQ3LTIwLjYxLDkuNDctMzMuMDFjMC0xMy42MS0zLjQxLTI1LjA1LTEwLjItMzQuMjhjLTYuODEtOS4yNi0xNS4zOS0xNy4wNC0yNS43My0yMy4zNgoJCQljLTEwLjM0LTYuMzItMjEuNDgtMTEuNjgtMzMuMzctMTYuMDVjLTExLjk0LTQuMzctMjMuMDUtOC42OS0zMy4zOS0xMi45NWMtMTAuMzQtNC4yNS0xOC45Mi04Ljk1LTI1LjczLTE0LjA1CgkJCWMtNi44MS01LjEtMTAuMi0xMS40Mi0xMC4yLTE4Ljk2YzAtNS41OSwxLjA4LTEwLjYsMy4yNy0xNC45N0MxMjgyLjAxLDIzNy4zLDEyODQuOTUsMjMzLjY1LDEyODguNTksMjMwLjcyeiIvPgoJCTxwYXRoIGNsYXNzPSJzdDciIGQ9Ik0xNzg3LjY1LDI1NC44bC01My4yNywxMTkuM2wtNTMuNjItMTE5LjNoLTM1LjM5aC0yMC44aC00NS4yM2wtNzguMDYsNjIuMzljMCwwLTAuODItMTQ2LjU1LTEuMTUtMTU0LjE3CgkJCWMtOC43Miw3LjYxLTE3LjQxLDE1LjIzLTI2LjEzLDIyLjg0Yy04LjY3LDcuNTktMTcuMzIsMTUuMTYtMjUuOTksMjIuNzJ2MzAuNTJ2MjA3Ljk1aDUzLjI3VjM3NC4xbDEyLjM4LTEwLjIybDYzLjQ5LDgzLjE4aDYxLjI4CgkJCWwtODUuNzItMTE1LjI4bDc3LjQ3LTY0LjM2bDc1LjczLDE3MC41MmwtNDEuNTksOTQuODZoNTUuNDVsMTIzLjMxLTI3OEwxNzg3LjY1LDI1NC44TDE3ODcuNjUsMjU0Ljh6Ii8+Cgk8L2c+CjwvZz4KPC9zdmc+Cg==" alt="VanillaSky"></div><div class="group"><button class="pill" type="button" data-go="history-demo"><svg data-icon="sessions"></svg><span class="nav-label">Sessions</span></button></div><div class="group"><button class="round" type="button" id="voice-demo" aria-label="Turn voice off" aria-pressed="true"><svg data-icon="sound"></svg></button><button class="round" type="button" data-go="settings-demo" aria-label="Settings"><svg data-icon="settings"></svg></button></div></header></div>
90
+ <div class="doc-preview"><header class="chrome"><div class="session-brand"><img class="brand-logo" src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI4LjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4IgoJIHZpZXdCb3g9IjE1NSAxMTMgMTY5MCA0MjIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMTU1IDExMyAxNjkwIDQyMjsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MHtmaWxsOnVybCgjU1ZHSURfMV8pO30KCS5zdDF7ZmlsbDp1cmwoI1NWR0lEXzAwMDAwMDEzMTc5MjQ3NzkzMzEzNjczNjYwMDAwMDA5NTAyNDY0NTI0MDQ3MTc5Njg2Xyk7fQoJLnN0MntmaWxsOnVybCgjU1ZHSURfMDAwMDAxMTQwNzU2MTAxNzA2MjA3NDYzMDAwMDAwMTMwNzY4NTczMjc1MDMwNTk4NTJfKTt9Cgkuc3Qze2ZpbGw6dXJsKCNTVkdJRF8wMDAwMDAzOTg0Mzk1Mzc4NTIyOTI0Mjg4MDAwMDAwODQwMzMzMzkyMDEyMjcwNTMyOF8pO30KCS5zdDR7ZmlsbDp1cmwoI1NWR0lEXzAwMDAwMDI3NTkyNTEzNTc2NDg3NDgzMzAwMDAwMDE2NzQwODQ2NzM4OTM4NjE5NzkyXyk7fQoJLnN0NXtmaWxsOnVybCgjU1ZHSURfMDAwMDAwNzM2ODI1Njc2NTkxMzUzNzk0NjAwMDAwMDQyOTE4NTk2MTM4MzIzNjcyNDZfKTt9Cgkuc3Q2e2ZpbGw6dXJsKCNTVkdJRF8wMDAwMDA0MzQyOTI4Mzg1Nzk2OTQ0NDE5MDAwMDAwNzg0MjM5Nzg0MTEwMTYxNjAxN18pO30KCS5zdDd7ZmlsbDojRkZGRkZGO30KPC9zdHlsZT4KPGc+Cgk8Zz4KCQk8Zz4KCQkJCgkJCQk8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzFfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjE0NC43Mzg4IiB5MT0iMzYxLjU0MjIiIHgyPSIxMTQ1LjM4NjYiIHkyPSIzMDguNTkwNyIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAwIDY1MCkiPgoJCQkJPHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6I0UwNEY4QSIvPgoJCQkJPHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6I0VDOUEyQyIvPgoJCQk8L2xpbmVhckdyYWRpZW50PgoJCQk8cGF0aCBjbGFzcz0ic3QwIiBkPSJNNDMxLjkzLDI5OC42N2MtMC42Mi0wLjYtMS4zNi0xLjExLTIuMTktMS41N2MtMi40Mi0xLjMyLTUuNjQtMS45OC05LjctMS45OAoJCQkJYy0xNi4xMiwwLTMwLjQ0LTEuMDYtNDIuOTctMy4xOWMxMS44My0zMi4yLDE3Ljc2LTYxLjkyLDE3Ljc2LTg5LjE1YzAtMTguOTMtNC40NS0zMy44NC0xMy4zMi00NC43NAoJCQkJYy04Ljg5LTEwLjg3LTIwLjg5LTE2LjMyLTM2LjA1LTE2LjMyYy0xMi4zMiwwLTIzLjUsMy4xOS0zMy41NSw5LjU5Yy0xMC4wOCw2LjQtMTgsMTYuMDQtMjMuOCwyOC45NQoJCQkJYy01LjgxLDEyLjg5LTguNywyOC43LTguNyw0Ny40MWMwLDE4LjcsMy42NiwzNS4yMSwxMSw0OS41NGM3LjM0LDE0LjMyLDE4LjIzLDI1Ljk5LDMyLjY3LDM0Ljk3CgkJCQljLTEyLjA2LDI1LjgyLTI1LjMzLDQ3LjI0LTM5Ljc2LDY0LjI4Yy0xNC40NiwxNy4wNi0yNy44MiwyNS41Ny00MC4xNCwyNS41N2MtMTIuMDYsMC0yMS4xMi05LjQtMjcuMTYtMjguMjMKCQkJCWMtNi4wNC0xOC44Mi05LjA2LTQ2LjMzLTkuMDYtODIuNTZjMC0zNC41NSwzLjU1LTczLjE1LDEwLjY2LTExNS43NmMwLjQ3LTMuNzksMC43LTYuNCwwLjctNy44MWMwLTEzLjUxLTcuODEtMjAuMjUtMjMuNDQtMjAuMjUKCQkJCWMtOS40NywwLTE2LjE1LDIuMTktMjAuMDYsNi41N3MtNi45MywxMi43NC05LjA2LDI1LjA0Yy01LjkzLDMzLjE0LTguODcsNzAuNTQtOC44NywxMTIuMjFjMCw0OC43OCw2LjA0LDg3LjQ5LDE4LjEsMTE2LjEyCgkJCQljMTIuMDgsMjguNjcsMzAuNDIsNDIuOTcsNTUuMDUsNDIuOTdjMjMuOTEsMCw0Ny43Ni0xMS42NCw3MS41Ni0zNC45N2MyMy44LTIzLjMzLDQ0LjA4LTUyLjYxLDYwLjktODcuODkKCQkJCWMxMC42MSwyLjQsMjIuMzMsMy43OSwzNS4xNCw0LjIxYzQuOTMsMC4xNywxMC4wMiwwLjE3LDE1LjI5LDAuMDRjNy44MS0wLjIzLDEzLjU1LTIuNzIsMTcuMjMtNy40NQoJCQkJYzMuNjYtNC43NCw1LjUxLTEwLjE3LDUuNTEtMTYuMzJDNDM1LjY3LDMwNC4xMiw0MzQuNDIsMzAxLjA0LDQzMS45MywyOTguNjd6IE0zMzcuNjUsMjc2LjY2CgkJCQljLTE0LjkxLTExLjEzLTIyLjM4LTI4LjI5LTIyLjM4LTUxLjVjMC0xMy43MiwyLjAyLTI0LjMzLDYuMDQtMzEuNzhjNC4wNC03LjQ1LDkuMjUtMTEuMTksMTUuNjMtMTEuMTkKCQkJCWM0LjQ5LDAsOC4wNiwyLjMsMTAuNjYsNi45M2MyLjYsNC42MiwzLjkxLDExLjg5LDMuOTEsMjEuODNDMzUxLjUsMjI4LjI0LDM0Ni44NywyNTAuMTUsMzM3LjY1LDI3Ni42NnoiLz4KCQkJCgkJCQk8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzAwMDAwMDg5NTUyODM1Mzg3NjQwNjA5ODkwMDAwMDA0ODExOTA0NjIyNjQ2MDYyNzMyXyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIxMzcuMjUzNSIgeTE9IjMwOC44OTY2IiB4Mj0iMTEzNy45MDE0IiB5Mj0iMjU1Ljk0NTEiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMCA2NTApIj4KCQkJCTxzdG9wICBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNFMDRGOEEiLz4KCQkJCTxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNFQzlBMkMiLz4KCQkJPC9saW5lYXJHcmFkaWVudD4KCQkJPHBhdGggc3R5bGU9ImZpbGw6dXJsKCNTVkdJRF8wMDAwMDA4OTU1MjgzNTM4NzY0MDYwOTg5MDAwMDAwNDgxMTkwNDYyMjY0NjA2MjczMl8pOyIgZD0iTTc0Ny45LDM3My42MQoJCQkJYy0xLjc3LTIuODUtNC4yMS00LjI3LTcuMjgtNC4yN2MtMy41NSwwLTYuNzQsMS42Ni05LjU5LDQuOTZjLTQuMSw0LjkxLTcuODcsOS4yNy0xMS4zMiwxMy4xM2MtNS43Nyw2LjQ1LTEwLjcsMTEuNDctMTQuNzgsMTUuMQoJCQkJYy02LjUxLDUuODEtMTIuMTMsOC43LTE2Ljg3LDguN2MtNi44NywwLTEwLjMtNC43Mi0xMC4zLTE0LjE5YzAtNi42MiwyLjI2LTIwLjI1LDYuNzYtNDAuODRjNS4yMS0yNC4xNiw3LjgxLTQwLjg0LDcuODEtNTAuMDkKCQkJCWMwLTE1LjM4LTcuNy0yMy4wOC0yMy4wOC0yMy4wOGMtNy44MSwwLTE2LjQsMi43NC0yNS43NCw4LjE3Yy05LjM2LDUuNDUtMTguODksMTQuNTctMjguNTksMjcuMzUKCQkJCWMtOS43MiwxMi43OC0xOC40OCwyOS4zNi0yNi4yOSw0OS43MWMwLjk0LTEwLjQsMi4xMy0xOS4xNywzLjU1LTI2LjI3YzEuNDMtNy4xLDMuMzItMTUuMzgsNS43LTI0Ljg1CgkJCQljMy41NS0xMy4yNyw1LjMyLTIxLjc4LDUuMzItMjUuNTdjMC0zLjMyLTAuOTYtNS41Ny0yLjg1LTYuNzZjLTEuODktMS4xNy01LjIxLTEuNzctOS45NS0xLjc3Yy04Ljc2LDAtMTUuNTcsMS42LTIwLjQyLDQuNzkKCQkJCWMtNC44NSwzLjIxLTguMzQsOC4yMy0xMC40NywxNS4xYy04LjM0LDI2LjM4LTEyLjcsNTQuMTYtMTMuMSw4My4zNGMtMC4wMiwxLjQ1LTAuMDQsMi45MS0wLjA0LDQuMzhjMCwxNiwxLjA4LDI4LjY3LDMuMjYsMzcuOTcKCQkJCWMwLjY0LDIuNzksMS40LDUuMywyLjI1LDcuNDljMy42Niw5LjQ3LDkuOTgsMTQuMTksMTguOTgsMTQuMTljNy4zNCwwLDEyLjc0LTEuOTQsMTYuMTctNS44NWMzLjQyLTMuOTEsNi40NC0xMS40Miw5LjA0LTIyLjU1CgkJCQljMy41Ny0xNC40NCw4LjIzLTI4LjE4LDE0LjA0LTQxLjJjNS43OS0xMy4wMiwxMS40Ny0yMy41LDE3LjA0LTMxLjQyYzUuNTctNy45Myw5Ljg5LTExLjkxLDEyLjk2LTExLjkxCgkJCQljMi42LDAsMy45MSwxLjkxLDMuOTEsNS42OGMwLDUuNy0xLjU1LDE1Ljk4LTQuNjIsMzAuOTFjLTMuMzIsMTcuMDQtNC45NiwyOC44Ny00Ljk2LDM1LjVjMCwxMy4yNywzLjA4LDIzLjM4LDkuMjMsMzAuMzYKCQkJCWM2LjE1LDYuOTgsMTUuMjcsMTAuNDcsMjcuMzUsMTAuNDdjMTQuOTEsMCwyOC4yMy00LjAyLDM5Ljk1LTEyLjA2YzUuMTctMy41NywxMC4xOS03LjU1LDE1LjA4LTExLjk2CgkJCQljNi4xNS01LjYyLDEyLjA4LTExLjkzLDE3Ljc4LTE4Ljk1YzQuNDktNS4xOSw2Ljc0LTEyLjUzLDYuNzQtMjJDNzUwLjU2LDM4MC4zNCw3NDkuNjcsMzc2LjQ0LDc0Ny45LDM3My42MXoiLz4KCQkJCgkJCQk8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzAwMDAwMDM2OTY2Mjg4NDUyMDI4MTAzOTgwMDAwMDA2MTQ5NTUyMTE1MTU0NzIxOTQ4XyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIxMzkuNzQyOCIgeTE9IjM1NS45MzgyIiB4Mj0iMTE0MC4zOTA3IiB5Mj0iMzAyLjk4NjciIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMCA2NTApIj4KCQkJCTxzdG9wICBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNFMDRGOEEiLz4KCQkJCTxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNFQzlBMkMiLz4KCQkJPC9saW5lYXJHcmFkaWVudD4KCQkJPHBhdGggc3R5bGU9ImZpbGw6dXJsKCNTVkdJRF8wMDAwMDAzNjk2NjI4ODQ1MjAyODEwMzk4MDAwMDAwNjE0OTU1MjExNTE1NDcyMTk0OF8pOyIgZD0iTTgzMy40OCwzNzMuNjEKCQkJCWMtMS43Ny0yLjg1LTQuMjEtNC4yNy03LjI4LTQuMjdjLTMuNTUsMC02Ljc2LDEuNjYtOS41OSw0Ljk2Yy0yLjEzLDIuNTctNC4xNyw0Ljk4LTYuMTMsNy4yN2MtNy44NSw5LjEzLTE0LjMyLDE2LjA4LTE5LjQ0LDIwLjgKCQkJCWMtNi40LDUuOTMtMTEuNzIsOC44Ny0xNS45OCw4Ljg3Yy00LjQ5LDAtNy43LTEuODktOS41OS01LjY4Yy0xLjg5LTMuNzctMi44My05LjIzLTIuODMtMTYuMzJjMC05LjI1LDIuODMtMjYuODcsOC41MS01Mi45MgoJCQkJYzQuNDktMjEuNTMsNi43Ni0zNS4yNyw2Ljc2LTQxLjJjMC04LjA0LTYuMDQtMTIuMDgtMTguMTItMTIuMDhjLTcuODEsMC0xMy40OSwxLjQzLTE3LjA0LDQuMjdjLTMuNTUsMi44NS02LjQsOC4wNi04LjUzLDE1LjYzCgkJCQljLTQuMjcsMTQuNDQtNy43NiwzMC4wMS0xMC40Nyw0Ni43MWMtMi40NSwxNS4wNi0zLjc5LDI3LjY1LTQuMDQsMzcuOGMtMC4wMiwxLjA5LTAuMDQsMi4xNy0wLjA0LDMuMjEKCQkJCWMwLDE0LjA0LDIuMTMsMjUuOTEsNi4zOCwzNS42M2MxLjE5LDIuNzQsMi41Nyw1LjMsNC4xLDcuN2M2Ljk4LDEwLjg5LDE4LjE1LDE2LjMyLDMzLjU1LDE2LjMyYzE3Ljg3LDAsMzYuOTEtMTEuMDIsNTcuMTYtMzMuMQoJCQkJYzIuODEtMy4wOCw1LjY2LTYuMzYsOC41My05Ljg3YzQuNTEtNS4xOSw2Ljc2LTEyLjUzLDYuNzYtMjJDODM2LjE0LDM4MC4zNCw4MzUuMjYsMzc2LjQ0LDgzMy40OCwzNzMuNjF6IE03OTIuMTIsMjEwLjYxCgkJCQljLTUuNDUtNC4yNy0xMi41Ny02LjQtMjEuMzItNi40Yy05LjcsMC0xNy44MiwyLjc3LTI0LjMzLDguMzRjLTYuNTEsNS41Ny05Ljc2LDEyLjQ5LTkuNzYsMjAuNzhjMCw4LjI4LDIuNDksMTQuNzQsNy40NSwxOS4zNAoJCQkJYzQuOTgsNC42MiwxMi40NCw2LjkzLDIyLjM4LDYuOTNjMTAuMTcsMCwxOC4zNC0yLjc3LDI0LjUtOC4zNGM2LjE1LTUuNTUsOS4yMy0xMy4wOCw5LjIzLTIyLjU1CgkJCQlDODAwLjI3LDIyMC45LDc5Ny41NSwyMTQuODYsNzkyLjEyLDIxMC42MXoiLz4KCQkJCgkJCQk8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzAwMDAwMTY5NTQ0MDU3OTMxOTgyMTQwNjIwMDAwMDAzNjMwOTA4MjUzMDQ2NjA0NDcxXyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIxNDIuNDM4MiIgeTE9IjQwNi44NzQ3IiB4Mj0iMTE0My4wODYxIiB5Mj0iMzUzLjkyMzIiIGdyYWRpZW50VHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMCA2NTApIj4KCQkJCTxzdG9wICBvZmZzZXQ9IjAiIHN0eWxlPSJzdG9wLWNvbG9yOiNFMDRGOEEiLz4KCQkJCTxzdG9wICBvZmZzZXQ9IjEiIHN0eWxlPSJzdG9wLWNvbG9yOiNFQzlBMkMiLz4KCQkJPC9saW5lYXJHcmFkaWVudD4KCQkJPHBhdGggc3R5bGU9ImZpbGw6dXJsKCNTVkdJRF8wMDAwMDE2OTU0NDA1NzkzMTk4MjE0MDYyMDAwMDAwMzYzMDkwODI1MzA0NjYwNDQ3MV8pOyIgZD0iTTk0Ny40NywzNzMuNjEKCQkJCWMtMS43Ny0yLjg1LTQuMjEtNC4yNy03LjI4LTQuMjdjLTMuNTUsMC02Ljc0LDEuNjYtOS41OSw0Ljk2Yy0yLjExLDIuNTMtNC4xNyw0LjkxLTYuMTUsNy4xMwoJCQkJYy04LjYyLDkuNzktMTUuOTMsMTYuOTUtMjEuODksMjEuNDZjLTcuMzQsNS41Ny0xNS44Nyw4LjM0LTI1LjU3LDguMzRjLTYuMTcsMC0xMS4xMy0yLjA2LTE0LjkzLTYuMjEKCQkJCWMtMy43OS00LjEzLTYuNTEtMTEuMDYtOC4xNy0yMC43OGMyNi43Ni0zNi40NCw0Ny45NS03My4yNiw2My41OC0xMTAuNDRjMTUuNjMtMzcuMTYsMjMuNDQtNjkuODQsMjMuNDQtOTguMDIKCQkJCWMwLTE5LjY1LTMuMjYtMzQuNzQtOS43OC00NS4yN2MtNi41MS0xMC41My0xNS40NC0xNS44LTI2LjgtMTUuOGMtMjAuMTQsMC0zNy40OCwxMy4yNS01Mi4wMywzOS43NgoJCQkJYy0xNC41NywyNi41MS0yNS41Nyw1OC4yNC0zMy4wMyw5NS4xN2MtNy40NSwzNi45My0xMS4xOSw2OS44NC0xMS4xOSw5OC43NGMwLDExLjk1LDAuNzksMjMsMi40LDMzLjE4CgkJCQljMi4wNiwxMy40LDUuNTMsMjUuMjcsMTAuMzgsMzUuNjNjMC43NCwxLjYsMS41MSwzLjE3LDIuMzIsNC43YzEwLjA2LDE4Ljk1LDI1LjUsMjguNCw0Ni4zNSwyOC40CgkJCQljMTUuMTQsMCwyOC42OS00LjAyLDQwLjY1LTEyLjA2YzguNjgtNS44NSwxNi44OS0xMi44NywyNC42NS0yMS4xYzIuOTEtMy4wOSw1Ljc3LTYuMzgsOC41Ny05LjgxYzQuNDktNS4xOSw2Ljc0LTEyLjUzLDYuNzQtMjIKCQkJCUM5NTAuMTMsMzgwLjM0LDk0OS4yNCwzNzYuNDQsOTQ3LjQ3LDM3My42MXogTTg1OS45MiwyNTIuNTFjNS4yMS0yOS4xMiwxMS4yNS01My41LDE4LjEyLTczLjE1CgkJCQljNi44Ny0xOS42NiwxMy4wMi0yOS40OCwxOC40OC0yOS40OGM2LjYyLDAsOS45NSw5LDkuOTUsMjYuOTljMCwyMi45Ny00Ljk4LDQ4LjM1LTE0LjkzLDc2LjE3cy0yMy4zMyw1NS41OC00MC4xMiw4My4yOAoJCQkJQzg1MS44OCwzMDkuNTUsODU0LjcxLDI4MS42Miw4NTkuOTIsMjUyLjUxeiIvPgoJCQkKCQkJCTxsaW5lYXJHcmFkaWVudCBpZD0iU1ZHSURfMDAwMDAwOTYwMzk0OTc1NDgzNzkyMzk0NjAwMDAwMTc2ODAwODc0NTg3MzgyMjU1NjVfIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjE0Mi43NTY1IiB5MT0iNDEyLjg4ODMiIHgyPSIxMTQzLjQwNDMiIHkyPSIzNTkuOTM2OCIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAwIDY1MCkiPgoJCQkJPHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6I0UwNEY4QSIvPgoJCQkJPHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6I0VDOUEyQyIvPgoJCQk8L2xpbmVhckdyYWRpZW50PgoJCQk8cGF0aCBzdHlsZT0iZmlsbDp1cmwoI1NWR0lEXzAwMDAwMDk2MDM5NDk3NTQ4Mzc5MjM5NDYwMDAwMDE3NjgwMDg3NDU4NzM4MjI1NTY1Xyk7IiBkPSJNMTA2MS40NSwzNzMuNjEKCQkJCWMtMS43Ny0yLjg1LTQuMjEtNC4yNy03LjI3LTQuMjdjLTMuNTcsMC02Ljc2LDEuNjYtOS42MSw0Ljk2Yy03LjE3LDguNTMtMTMuNTUsMTUuNDctMTkuMTIsMjAuODMKCQkJCWMtMy4yNSwzLjEzLTYuMjMsNS43Mi04LjkzLDcuNzZjLTcuMzQsNS41Ny0xNS44Nyw4LjM0LTI1LjU3LDguMzRjLTYuMTUsMC0xMS4xMy0yLjA2LTE0LjkxLTYuMjEKCQkJCWMtMy43OS00LjEzLTYuNTMtMTEuMDYtOC4xNy0yMC43OGMyNi43NC0zNi40NCw0Ny45My03My4yNiw2My41Ni0xMTAuNDRjMTUuNjMtMzcuMTYsMjMuNDQtNjkuODQsMjMuNDQtOTguMDIKCQkJCWMwLTE5LjY1LTMuMjUtMzQuNzQtOS43Ni00NS4yN2MtNi41My0xMC41My0xNS40Ni0xNS44LTI2LjgyLTE1LjhjLTIwLjEyLDAtMzcuNDYsMTMuMjUtNTIuMDMsMzkuNzYKCQkJCWMtMTQuNTUsMjYuNTEtMjUuNTcsNTguMjQtMzMuMDMsOTUuMTdjLTcuNDUsMzYuOTMtMTEuMTksNjkuODQtMTEuMTksOTguNzRjMCwxMS44OSwwLjc5LDIyLjkxLDIuMzgsMzMuMDQKCQkJCWMyLjA2LDEzLjQyLDUuNTMsMjUuMzMsMTAuMzgsMzUuNzFjMC43NCwxLjYyLDEuNTMsMy4yMSwyLjM0LDQuNzZjMTAuMDYsMTguOTUsMjUuNTEsMjguNCw0Ni4zNSwyOC40CgkJCQljMTUuMTUsMCwyOC43LTQuMDIsNDAuNjUtMTIuMDZjMy43OS0yLjU3LDcuNTEtNS4zNCwxMS4xMi04LjM2YzcuNzgtNi40NSwxNS4xNC0xMy45NywyMi4xLTIyLjU1YzQuNDktNS4xOSw2Ljc0LTEyLjUzLDYuNzQtMjIKCQkJCUMxMDY0LjExLDM4MC4zNCwxMDYzLjIzLDM3Ni40NCwxMDYxLjQ1LDM3My42MXogTTk3My45MywyNTIuNTFjNS4xOS0yOS4xMiwxMS4yMy01My41LDE4LjEtNzMuMTUKCQkJCWM2Ljg3LTE5LjY2LDEzLjAyLTI5LjQ4LDE4LjQ4LTI5LjQ4YzYuNjIsMCw5Ljk1LDksOS45NSwyNi45OWMwLDIyLjk3LTQuOTgsNDguMzUtMTQuOTEsNzYuMTcKCQkJCWMtOS45NSwyNy44Mi0yMy4zMyw1NS41OC00MC4xNCw4My4yOEM5NjUuODcsMzA5LjU1LDk2OC43MiwyODEuNjIsOTczLjkzLDI1Mi41MXoiLz4KCQkJCgkJCQk8bGluZWFyR3JhZGllbnQgaWQ9IlNWR0lEXzAwMDAwMDQ0ODY0NDU5ODE4NDk5ODM0NDgwMDAwMDE0NzUwODc5MzcyMTAyNDYzMzk3XyIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiIHgxPSIxMzguNTg5NCIgeTE9IjMzNC4xNDA1IiB4Mj0iMTEzOS4yMzcyIiB5Mj0iMjgxLjE4OSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCgxIDAgMCAtMSAwIDY1MCkiPgoJCQkJPHN0b3AgIG9mZnNldD0iMCIgc3R5bGU9InN0b3AtY29sb3I6I0UwNEY4QSIvPgoJCQkJPHN0b3AgIG9mZnNldD0iMSIgc3R5bGU9InN0b3AtY29sb3I6I0VDOUEyQyIvPgoJCQk8L2xpbmVhckdyYWRpZW50PgoJCQk8cGF0aCBzdHlsZT0iZmlsbDp1cmwoI1NWR0lEXzAwMDAwMDQ0ODY0NDU5ODE4NDk5ODM0NDgwMDAwMDE0NzUwODc5MzcyMTAyNDYzMzk3Xyk7IiBkPSJNMTIwMi41MiwzODIuNzQKCQkJCWMtNC40Myw1Ljc2LTEwLjMsMTIuNTctMTcuMjMsMTguN2MwLDAtMTEuMTIsOS44NS0yMS4zOCw5Ljc5Yy0yLjMsMC00LjA5LTAuOTEtNC4wOS0wLjkxYy0xLjA2LTAuNTMtMi0xLjI4LTIuODMtMi4yOAoJCQkJYy0xLjc3LTIuMTMtMi42Ni00Ljk2LTIuNjYtOC41M2MwLTEwLjY0LDIuNzItMjkuNTksOC4xNy01Ni44YzQuMDYtMjAuMjcsNy40NC0zMC4xMSw1LjMyLTM1LjUyCgkJCQljLTcuMjYtMTguNTctMzcuNDUtMjcuMDctNjAuNy0yMy43N2MtMTUuNTgsMi4yMS0yNi40Myw5LjMtMzMuNTgsMTQuMzZjLTE0LjU3LDEwLjMtMjYuMjMsMjMuOTEtMzQuOTksNDAuODQKCQkJCXMtMTMuMTMsMzQuODgtMTMuMTMsNTMuOGMwLDAuOTEsMC4wMiwxLjgxLDAuMDYsMi43MmMwLjM2LDEzLjc4LDMuNjIsMjUuMzYsOS44MSwzNC43NGMxLDEuNTUsMi4wOSwzLjAyLDMuMjYsNC40NQoJCQkJYzguNzYsMTAuNjQsMjAuNDgsMTUuOTcsMzUuMTYsMTUuOTdjMTguNywwLDM0LjIxLTkuOTUsNDYuNTItMjkuODJjMy4zMiw5LjIzLDguMjMsMTYuNTEsMTQuNzQsMjEuODMKCQkJCWM2LjUxLDUuMzIsMTMuNjgsNy45OCwyMS40OSw3Ljk4YzEwLjE3LDAsMjAuNy00LjEzLDMxLjYxLTEyLjQyYzguNC02LjM4LDE2LjQyLTEzLjg5LDI0LjEtMjIuNTMKCQkJCUMxMjA1LjYyLDQwMy4xNiwxMjAzLjI4LDM5MS4yMywxMjAyLjUyLDM4Mi43NHogTTExMTEuNzEsMzgwLjdjLTIuMTMsMTAuMTktNS41NywxOC4zNC0xMC4zLDI0LjUxCgkJCQljLTQuNzQsNi4xNS05LjcsOS4yMy0xNC45MSw5LjIzYy01LjIxLDAtOS4zLTEuNzctMTIuMjUtNS4zMmMtMi45Ni0zLjU3LTQuNDUtOS00LjQ1LTE2LjM0YzAtMTMuMjcsMi40OS0yNS44LDcuNDUtMzcuNjUKCQkJCWM0Ljk4LTExLjgzLDExLjY2LTIxLjM2LDIwLjA4LTI4LjU5YzguNC03LjIxLDE3LjU3LTEwLjk1LDI3LjUxLTExLjE3TDExMTEuNzEsMzgwLjd6Ii8+CgkJCQoJCQkJPGxpbmVhckdyYWRpZW50IGlkPSJTVkdJRF8wMDAwMDAxMzg4NDE0OTU1MTMwMDcxMDUxMDAwMDAwOTQ1NjEzMzk1NjE1Mjg5NDYxMl8iIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIiB4MT0iMTMyLjA3ODgiIHkxPSIyOTkuOTE3OCIgeDI9IjExMzIuNzI2NyIgeTI9IjI0Ni45NjY0IiBncmFkaWVudFRyYW5zZm9ybT0ibWF0cml4KDEgMCAwIC0xIDAgNjUwKSI+CgkJCQk8c3RvcCAgb2Zmc2V0PSIwIiBzdHlsZT0ic3RvcC1jb2xvcjojRTA0RjhBIi8+CgkJCQk8c3RvcCAgb2Zmc2V0PSIxIiBzdHlsZT0ic3RvcC1jb2xvcjojRUM5QTJDIi8+CgkJCTwvbGluZWFyR3JhZGllbnQ+CgkJCTxwYXRoIHN0eWxlPSJmaWxsOnVybCgjU1ZHSURfMDAwMDAwMTM4ODQxNDk1NTEzMDA3MTA1MTAwMDAwMDk0NTYxMzM5NTYxNTI4OTQ2MTJfKTsiIGQ9Ik01NDkuMjksMzgyLjc0CgkJCQljLTQuNDMsNS43Ni0xMC4zLDEyLjU3LTE3LjIzLDE4LjdjMCwwLTExLjEyLDkuODUtMjEuMzgsOS43OWMtMi4zLDAtNC4wOS0wLjkxLTQuMDktMC45MWMtMS4wNi0wLjUzLTItMS4yOC0yLjgzLTIuMjgKCQkJCWMtMS43Ny0yLjEzLTIuNjYtNC45Ni0yLjY2LTguNTNjMC0xMC42NCwyLjcyLTI5LjU5LDguMTctNTYuOGM0LjA2LTIwLjI3LDcuNDQtMzAuMTEsNS4zMi0zNS41MgoJCQkJYy03LjI2LTE4LjU3LTM3LjQ1LTI3LjA3LTYwLjctMjMuNzdjLTE1LjU4LDIuMjEtMjYuNDMsOS4zLTMzLjU4LDE0LjM2Yy0xNC41NywxMC4zLTI2LjIzLDIzLjkxLTM0Ljk5LDQwLjg0CgkJCQljLTguNzYsMTYuOTMtMTMuMTMsMzQuODgtMTMuMTMsNTMuOGMwLDAuOTEsMC4wMiwxLjgxLDAuMDYsMi43MmMwLjM2LDEzLjc4LDMuNjIsMjUuMzYsOS44MSwzNC43NGMxLDEuNTUsMi4wOSwzLjAyLDMuMjYsNC40NQoJCQkJYzguNzYsMTAuNjQsMjAuNDgsMTUuOTcsMzUuMTYsMTUuOTdjMTguNywwLDM0LjIxLTkuOTUsNDYuNTItMjkuODJjMy4zMiw5LjIzLDguMjMsMTYuNTEsMTQuNzQsMjEuODMKCQkJCWM2LjUxLDUuMzIsMTMuNjgsNy45OCwyMS40OSw3Ljk4YzEwLjE3LDAsMjAuNy00LjEzLDMxLjYxLTEyLjQyYzguNC02LjM4LDE2LjQyLTEzLjg5LDI0LjEtMjIuNTMKCQkJCUM1NTIuMzksNDAzLjE2LDU1MC4wNSwzOTEuMjMsNTQ5LjI5LDM4Mi43NHogTTQ1OC40OCwzODAuN2MtMi4xMywxMC4xOS01LjU3LDE4LjM0LTEwLjMsMjQuNTFjLTQuNzQsNi4xNS05LjcsOS4yMy0xNC45MSw5LjIzCgkJCQlzLTkuMy0xLjc3LTEyLjI1LTUuMzJjLTIuOTYtMy41Ny00LjQ1LTktNC40NS0xNi4zNGMwLTEzLjI3LDIuNDktMjUuOCw3LjQ1LTM3LjY1YzQuOTgtMTEuODMsMTEuNjYtMjEuMzYsMjAuMDgtMjguNTkKCQkJCWM4LjQtNy4yMSwxNy41Ny0xMC45NSwyNy41MS0xMS4xN0w0NTguNDgsMzgwLjd6Ii8+CgkJPC9nPgoJPC9nPgoJPGc+CgkJPHBhdGggY2xhc3M9InN0NyIgZD0iTTEyODguNTksMjMwLjcyYzMuNjQtMi45MSw3LjktNS4xLDEyLjc2LTYuNTZjNC44Ni0xLjQ2LDkuOTktMi4xOSwxNS4zMi0yLjE5YzguNTMsMCwxNS44OCwxLjE1LDIyLjA5LDMuNDUKCQkJYzYuMiwyLjMzLDExLjg0LDUuNDMsMTYuOTcsOS4zYzUuMSwzLjksOS43Nyw4LjQ2LDE0LjAzLDEzLjY4YzQuMjUsNS4yNCw4LjY5LDEwLjksMTMuMzIsMTYuOTdsMzcuMjItMzIuODMKCQkJYy03LjMxLTguNTEtMTQuNTktMTYuMDUtMjEuOS0yMi42M2MtNy4yOC02LjU2LTE1LjA5LTEyLjE1LTIzLjMzLTE2Ljc4Yy04LjI3LTQuNjEtMTcuMTEtOC4xMy0yNi40Ni0xMC41NwoJCQljLTkuMzgtMi40Mi0xOS44OC0zLjY0LTMxLjU2LTMuNjRjLTExLjkxLDAtMjMuNTksMS42OS0zNS4wNCw1LjFjLTExLjQyLDMuNDEtMjEuNjQsOC4zOS0zMC42NCwxNC45NwoJCQljLTksNi41Ni0xNi4yNCwxNC44My0yMS43MSwyNC43OWMtNS40NSw5Ljk5LTguMiwyMS41Mi04LjIsMzQuNjZjMCwxMy42MywzLjQxLDI1LjA1LDEwLjIyLDM0LjMxCgkJCWM2LjgxLDkuMjMsMTUuMzcsMTcuMDgsMjUuNzEsMjMuNTJjMTAuMzQsNi40NiwyMS41NSwxMS45MSwzMy41OCwxNi40MmMxMi4wMyw0LjQ5LDIzLjIyLDguOTMsMzMuNTUsMTMuMzIKCQkJYzEwLjM0LDQuMzcsMTguOTIsOS4wNSwyNS43MywxNC4wM2M2Ljc5LDUsMTAuMjIsMTEuMjYsMTAuMjIsMTguOGMwLDUuNTktMS40MSwxMC4zOS00LjIxLDE0LjRjLTIuOCw0LjAyLTYuMjcsNy4zNS0xMC4zOSwxMC4wMwoJCQljLTQuMTYsMi42OC04Ljc2LDQuNy0xMy44Niw2LjAyYy01LjEyLDEuMzQtMTAuMSwyLjAyLTE0Ljk3LDIuMDJjLTksMC0xNy4wOC0xLjI5LTI0LjI3LTMuODNjLTEuMzYtMC40OS0xMC41NS0zLjY5LTE5LjUtMTAuMjIKCQkJYy0xOS42OS0xNC4zOC0yNi45Ny0zNi42My0yOS4xMS00NC4xM2MtMTAuOTcsOS40LTIxLjkyLDE4LjgtMzIuOSwyOC4yYzEuMzYsOS44NSw1LjQsMjYuNjcsMTguNTksNDAuNzQKCQkJYzguMjcsOC44MSwxNy41MywxMy42LDI0LjYzLDE3LjUxYzguNjUsNC43NSwxNy45Myw4LjI3LDI3LjkyLDEwLjU3YzkuOTYsMi4zLDIxLjE1LDMuNDgsMzMuNTUsMy40OAoJCQljMTAuNzEsMCwyMS45LTEuNzIsMzMuNTgtNS4xMmMxMS42OC0zLjM4LDIyLjM3LTguMzksMzIuMS0xNC45NGM5LjczLTYuNTgsMTcuNzYtMTQuNjYsMjQuMDgtMjQuMjcKCQkJYzYuMzItOS41OSw5LjQ3LTIwLjYxLDkuNDctMzMuMDFjMC0xMy42MS0zLjQxLTI1LjA1LTEwLjItMzQuMjhjLTYuODEtOS4yNi0xNS4zOS0xNy4wNC0yNS43My0yMy4zNgoJCQljLTEwLjM0LTYuMzItMjEuNDgtMTEuNjgtMzMuMzctMTYuMDVjLTExLjk0LTQuMzctMjMuMDUtOC42OS0zMy4zOS0xMi45NWMtMTAuMzQtNC4yNS0xOC45Mi04Ljk1LTI1LjczLTE0LjA1CgkJCWMtNi44MS01LjEtMTAuMi0xMS40Mi0xMC4yLTE4Ljk2YzAtNS41OSwxLjA4LTEwLjYsMy4yNy0xNC45N0MxMjgyLjAxLDIzNy4zLDEyODQuOTUsMjMzLjY1LDEyODguNTksMjMwLjcyeiIvPgoJCTxwYXRoIGNsYXNzPSJzdDciIGQ9Ik0xNzg3LjY1LDI1NC44bC01My4yNywxMTkuM2wtNTMuNjItMTE5LjNoLTM1LjM5aC0yMC44aC00NS4yM2wtNzguMDYsNjIuMzljMCwwLTAuODItMTQ2LjU1LTEuMTUtMTU0LjE3CgkJCWMtOC43Miw3LjYxLTE3LjQxLDE1LjIzLTI2LjEzLDIyLjg0Yy04LjY3LDcuNTktMTcuMzIsMTUuMTYtMjUuOTksMjIuNzJ2MzAuNTJ2MjA3Ljk1aDUzLjI3VjM3NC4xbDEyLjM4LTEwLjIybDYzLjQ5LDgzLjE4aDYxLjI4CgkJCWwtODUuNzItMTE1LjI4bDc3LjQ3LTY0LjM2bDc1LjczLDE3MC41MmwtNDEuNTksOTQuODZoNTUuNDVsMTIzLjMxLTI3OEwxNzg3LjY1LDI1NC44TDE3ODcuNjUsMjU0Ljh6Ii8+Cgk8L2c+CjwvZz4KPC9zdmc+Cg==" alt="VanillaSky"></div><div class="group"><button class="pill" type="button" data-go="history-demo"><svg data-icon="sessions"></svg><span class="nav-label">Sessions</span></button><button class="round" type="button" data-go="settings-demo" aria-label="Settings"><svg data-icon="settings"></svg></button><button class="round" type="button" id="voice-demo" aria-label="Turn voice off" aria-pressed="true"><svg data-icon="sound"></svg></button></div></header></div>
91
91
  <div class="surface-grid"><div class="surface" style="background:var(--vs-media-ground)"><strong>Canvas</strong><code>--vs-media-ground</code></div><div class="surface" style="background:var(--vs-media-glass)"><strong>Glass</strong><code>--vs-media-glass</code></div><div class="surface" style="background:var(--vs-media-hover)"><strong>Hover</strong><code>--vs-media-hover</code></div><div class="surface" style="background:var(--vs-media-selected)"><strong>Selected</strong><code>--vs-media-selected</code></div></div></section>
92
92
  <p class="doc-note">The default welcome film is <a href="https://www.pexels.com/video/timelapse-of-clouds-11335959/">Timelapse of Clouds by Dmitry Marchenkov</a>. A poster appears while the muted cloud film loads; custom host welcome searches remain supported.</p>
93
- <section class="doc-section" id="panels"><div class="section-heading"><span class="number">02</span><h2>A place for the details</h2></div><p class="section-copy">Settings and sessions share the same glass surface. Section headings sit above their choice containers with consistent spacing and alignment. Switches describe the current experience; visuals and style apply to the next question. New session and history live together in Sessions, represented by overlapping conversation bubbles. Generated follow-up cards use one idea in at most eight words and sixty characters; oversized suggestions are omitted instead of clipped. The input returns when playback ends and stays available for a follow-up, without opening the mobile keyboard. Try the controls below.</p><div class="doc-grid">
93
+ <section class="doc-section" id="panels"><div class="section-heading"><span class="number">02</span><h2>A place for the details</h2></div><p class="section-copy">Settings and sessions share the same glass surface. Section headings sit above their choice containers with consistent spacing and alignment. Switches describe the current experience; visuals and style apply to the next question. Header controls share one row with equal gaps: 6px on mobile and 10px on desktop. New session and history live together in Sessions, represented by overlapping conversation bubbles. Generated follow-up cards use one idea in at most eight words and sixty characters; oversized suggestions are omitted instead of clipped. The input returns when playback ends and stays available for a follow-up, without opening the mobile keyboard. Try the controls below.</p><div class="doc-grid">
94
94
  <div class="doc-preview popover-demo" id="settings-demo"><div class="sheet-popover settings"><header class="popover-heading"><h2>Settings</h2><button class="round" type="button" data-go="panels" aria-label="Back to section heading"><svg data-icon="close"></svg></button></header><fieldset class="playback-options"><legend>Watching</legend><label class="switch-row"><span><strong>Subtitles</strong><small>Read along with the answer</small></span><input type="checkbox" role="switch" checked id="show-captions"></label><label class="switch-row"><span><strong>Keep controls visible</strong><small>Keep the input bar on screen</small></span><input type="checkbox" role="switch"></label></fieldset><fieldset class="visual-options"><legend>Video creation</legend><div class="visual-choices"><label class="choice-row"><input type="radio" name="visuals" checked><span><strong>Templates only</strong><small>Animated text, diagrams and charts</small></span></label><label class="choice-row"><input type="radio" name="visuals"><span><strong>Full AI video</strong><small>AI-generated footage · billed per clip</small></span></label></div></fieldset><fieldset class="style-options"><legend>Video style</legend><div class="style-choices"><label><input type="radio" name="style" checked><span>Documentary</span></label><label><input type="radio" name="style"><span>Illustrated</span></label><label><input type="radio" name="style"><span>Blueprint</span></label></div></fieldset><p class="settings-note">Creation and style changes apply to your next question.</p>
95
95
  <nav class="developer-links" aria-label="Build with VanillaSky">
96
96
  <p class="section-label">Build with VanillaSky</p>
@@ -104,7 +104,7 @@ body { background: #080b16; }
104
104
  <section class="doc-section" id="conversation"><div class="section-heading"><span class="number">03</span><h2>One field. Every follow-up.</h2></div><p class="section-copy">Speak or type directly over the picture. Beginning a question pauses playback. Sending starts the next answer; canceling returns to the previous playback state.</p><div class="doc-preview"><div class="demo-toolbar" aria-label="Composer preview states"><button type="button" data-state="ready" aria-pressed="true">Ready</button><button type="button" data-state="listening" aria-pressed="false">Listening</button><button type="button" data-state="thinking" aria-pressed="false">Transcribing</button></div><div class="conversation-composer"><div class="composer-meta"><span id="composer-status" role="status">Interactive preview · no microphone or model call</span></div><form class="composer" id="composer-demo"><button class="ghost transport" type="button" aria-label="Play preview" id="play-demo"><svg data-icon="play"></svg></button><label class="sr-only" for="demo-question">Question</label><textarea id="demo-question" rows="1" placeholder="Ask a follow-up…"></textarea><button class="ghost" type="button" id="mic-demo" aria-label="Preview listening state" aria-pressed="false"><svg data-icon="mic"></svg></button><button class="send" type="submit" aria-label="Preview send" disabled><svg data-icon="send"></svg></button></form></div></div><p class="doc-note">44px targets. 10px between microphone and send. The field grows to 120px and keeps the controls within reach.</p></section>
105
105
  <section class="doc-section" id="captions"><div class="section-heading"><span class="number">04</span><h2>Words stay with the picture</h2></div><p class="section-copy">The first subtitle takes the input’s bottom baseline. The complete cue remains readable without truncation. Reveal the input to move subtitles upward; expand for the transcript or restore hidden subtitles in Settings.</p><div class="doc-preview"><div class="demo-toolbar"><button type="button" id="restore-caption">Show subtitles</button><button type="button" id="toggle-transcript">Expand transcript</button></div><div class="line-row" id="caption-demo" data-expanded="false"><div class="caption-actions"><button class="caption-action" type="button" id="expand-action" aria-label="Expand subtitles" aria-expanded="false">Expand <svg data-icon="up"></svg></button><button class="caption-action" type="button" id="hide-action" aria-label="Hide subtitles"><svg data-icon="close"></svg></button></div><p class="line" id="compact-caption">Water absorbs the warm colors of sunlight,<br>leaving more blue light to reach your eyes.</p><div class="expanded-captions" id="transcript-demo" tabindex="0" aria-label="Expanded subtitles" hidden><span class="eyebrow">Full answer</span><p>The ocean’s color begins with sunlight.</p><p>Water absorbs the warm colors of sunlight, leaving more blue light to reach your eyes.</p><p>Depth, particles and the sky change what we see. That is why a shallow lagoon and the open ocean can look so different.</p></div></div></div></section>
106
106
  <section class="doc-section" id="suggestions"><div class="section-heading"><span class="number">05</span><h2>A little more curiosity</h2></div><p class="section-copy">A horizontal rail offers the next question. Video cards show their poster image before playback, including when autoplay is blocked. Hover or keyboard focus selects a card. Its outline and lift have room to breathe.</p><div class="doc-preview"><ul class="cards" aria-label="Example suggested questions"><li><button type="button" data-active><span class="card-art"></span><span class="card-wash"></span><span class="card-prompt">What is hiding in the deep ocean?</span></button></li><li><button type="button"><span class="card-art space"></span><span class="card-wash"></span><span class="card-prompt">Why do planets stay in orbit?</span></button></li><li><button type="button"><span class="card-art mind"></span><span class="card-wash"></span><span class="card-prompt">How does a memory begin?</span></button></li></ul></div><p class="doc-note">Reference artwork uses abstract color fields. The live rail uses the answer’s resolved media. Select a card here to place its question in the preview composer.</p></section>
107
- <section class="doc-section" id="foundations"><div class="section-heading"><span class="number">06</span><h2>Small decisions, shared everywhere</h2></div><p class="section-copy">System typography, generous touch targets and a compact spacing scale. The video carries the color; the interface carries meaning.</p><div class="doc-preview"><div class="type-row"><span style="font-size:40px;letter-spacing:-.055em;line-height:1.08">An answer worth watching.</span><code>Display · responsive / 1.08</code></div><div class="type-row"><span style="font-size:22px;letter-spacing:-.02em;line-height:1.45">A clear line over a moving picture.</span><code>Subtitles · 17–22px / 1.45</code></div><div class="type-row"><span style="font-size:16px;letter-spacing:-.015em;line-height:1.625">Ask a follow-up…</span><code>Input · 16px / 26px</code></div><div class="type-row"><span style="font-size:13px;color:var(--vs-media-muted)">One answer leads to another.</span><code>Context · 13px</code></div><div class="metrics"><div class="metric"><strong>44<span style="display:inline;font-size:15px"> px</span></strong><span>Minimum primary control target</span></div><div class="metric"><strong>180<span style="display:inline;font-size:15px"> ms</span></strong><span>Navigation and opacity fade</span></div><div class="metric"><strong>280<span style="display:inline;font-size:15px"> ms</span></strong><span>Input and subtitle movement</span></div><div class="metric"><strong>2<span style="display:inline;font-size:15px"> s</span></strong><span>Idle delay after revealing controls</span></div></div></div><p class="doc-note">First subtitles hide the input immediately unless the viewer is editing, listening or using keyboard focus. Reduced motion removes movement and fades. Increased contrast strengthens the surfaces and borders. The input respects bottom safe areas. Subtitle actions fade while inactive and return on hover, touch or keyboard focus. The foreground preserves the scene ratio and readable text. Ambient opening media or a gradient fills the surrounding viewport.</p></section>
107
+ <section class="doc-section" id="foundations"><div class="section-heading"><span class="number">06</span><h2>Small decisions, shared everywhere</h2></div><p class="section-copy">System typography, generous touch targets and a compact spacing scale. The video carries the color; the interface carries meaning.</p><div class="doc-preview"><div class="type-row"><span style="font-size:40px;letter-spacing:-.055em;line-height:1.08">An answer worth watching.</span><code>Display · responsive / 1.08</code></div><div class="type-row"><span style="font-size:22px;letter-spacing:-.02em;line-height:1.45">A clear line over a moving picture.</span><code>Subtitles · 17–22px / 1.45</code></div><div class="type-row"><span style="font-size:16px;letter-spacing:-.015em;line-height:1.625">Ask a follow-up…</span><code>Input · 16px / 26px</code></div><div class="type-row"><span style="font-size:13px;color:var(--vs-media-muted)">One answer leads to another.</span><code>Context · 13px</code></div><div class="metrics"><div class="metric"><strong>44<span style="display:inline;font-size:15px"> px</span></strong><span>Minimum primary control target</span></div><div class="metric"><strong>180<span style="display:inline;font-size:15px"> ms</span></strong><span>Navigation and opacity fade</span></div><div class="metric"><strong>280<span style="display:inline;font-size:15px"> ms</span></strong><span>Input and subtitle movement</span></div><div class="metric"><strong>2<span style="display:inline;font-size:15px"> s</span></strong><span>Idle delay after revealing controls</span></div></div></div><p class="doc-note">First subtitles hide the input immediately unless the viewer is editing, listening or using keyboard focus. Reduced motion removes movement and fades. Increased contrast strengthens the surfaces and borders. The input respects bottom safe areas. Subtitle actions fade while inactive and return on hover, touch or keyboard focus. The foreground preserves the scene ratio and readable text. The surrounding viewport remains black during playback.</p></section>
108
108
  <footer class="doc-footer"><span>VanillaSky · VideoChat immersive interface</span><span>Rendered with the same tokens and component CSS as the experience.</span></footer>
109
109
  </div></main>
110
110
  <script>
@@ -1,10 +1,10 @@
1
- # Video Response Protocol 0.5
1
+ # Video Response Protocol 0.6
2
2
 
3
3
  ## Transport
4
4
 
5
5
  The public transport is UTF-8 Server-Sent Events returned from a `POST` request.
6
6
  Responses use `Content-Type: text/event-stream`,
7
- `x-vanillasky-video-stream: 0.5`, `Cache-Control: no-cache, no-transform`, and
7
+ `x-vanillasky-video-stream: 0.6`, `Cache-Control: no-cache, no-transform`, and
8
8
  `X-Accel-Buffering: no`. Each block has an SSE `id`, the event name
9
9
  `video`, and one JSON envelope in `data`. A final `data: [DONE]`
10
10
  closes the transport after a terminal protocol event. Comment heartbeats do not
@@ -14,7 +14,7 @@ change protocol state.
14
14
 
15
15
  ```ts
16
16
  type VideoEvent<T extends string, D> = {
17
- protocolVersion: "0.5";
17
+ protocolVersion: "0.6";
18
18
  runId: string;
19
19
  sequence: number;
20
20
  eventId: string; // exactly `${runId}:${sequence}`
@@ -42,7 +42,7 @@ finish reason, and a deterministic checksum. The reducer verifies that the
42
42
  snapshot equals the state produced by all prior events. The checksum detects
43
43
  accidental drift; it is not a cryptographic signature.
44
44
 
45
- The terminal snapshot carries persisted `schemaVersion: "0.1"`. That storage
45
+ The terminal snapshot carries persisted `schemaVersion: "0.2"`. That storage
46
46
  version is independent from this streaming protocol version. Load stored
47
47
  snapshots through the universal `parseVideo` boundary described in the
48
48
  [persistence guide](../persistence.md).
@@ -100,3 +100,13 @@ SDK does not prescribe or operate a persistence service.
100
100
  Validate adapter output and persisted replay logs against this protocol before
101
101
  accepting them. A replay log must preserve ordering, checksums, and a terminal
102
102
  event.
103
+
104
+ ## Host-authored narration across cuts
105
+
106
+ `VideoScene.narrationGroup` optionally joins adjacent shots to one prepared spoken paragraph. A host supplies the shared `id`, `text`, measured `totalSeconds`, and each segment's `offsetSeconds` and `durationSeconds`. Each scene retains its narration fragment and a matching `timing.fixedDuration`; fragments must exactly cover the paragraph in order. The planner cannot emit this field or guess speech timings.
107
+
108
+ Grouped playback requires prepared speech with `supportsOffsets: true`. The generated-audio voice supports offsets; browser speech synthesis does not. The chat hook prepares and validates a complete group before showing its first scene, including saved replay. Hosts using a standalone player must prepare the paragraph and coordinate narration themselves before starting playback. Unsupported voices and mismatched measured durations fail explicitly. Default planner responses still prepare narration per scene.
109
+
110
+ For standalone playback, provide a synchronous `narrationReady` callback alongside your `onSceneChange` narration handler. Return false while a new grouped paragraph awaits actual audio onset, then true from the voice's `onStart` callback; also release readiness on completion, failure, or interruption. Abort pending narration from the player's `onError` handler. `VideoChat` wires this automatically through its internal narration hook. For grouped paragraphs, the voice must invoke `onStart` when audio actually begins, not when audio is prepared or `play()` is requested. The first visual cue starts narration, then the playhead waits for that onset without pausing the voice. A missing onset stops the player with an error after eight seconds of active waiting. For prepared audio, also provide `narrationTime(scene)` using the active voice's optional `getCurrentTime()`: return paragraph-relative seconds (including the seek offset) for a group, or scene-relative seconds otherwise. This makes the actual audio clock authoritative through cold-start delays and mid-speech stalls. Return `undefined` for silent scenes or unavailable clocks; after ordinary narration completes, release to wall time so the authored reading hold can finish. `VideoChat` coordinates these callbacks automatically. A clock that stops advancing for eight active seconds produces an error; visual-readiness holds and deliberate pauses do not consume that timeout. Voices without an observable playback clock retain wall-time playback.
111
+
112
+ Readiness holds pause narration together with the picture. The same audio continues across adjacent group scenes; replay starts a new playback session. This preserves words through delayed media rather than promising uninterrupted playback on every network.
package/docs/testing.md CHANGED
@@ -34,7 +34,7 @@ describe("POST /api/video-chat", () => {
34
34
  body: JSON.stringify({
35
35
  prompt: "Explain why the Moon shows one face",
36
36
  opening: "The Moon turns, perfectly matching its orbit.",
37
- mode: "templates",
37
+ mode: "cinematic",
38
38
  orientation: "landscape",
39
39
  }),
40
40
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vanillaskyai/video",
3
- "version": "0.8.7",
3
+ "version": "0.9.0",
4
4
  "description": "Open-source voice-and-video chat SDK for AI applications.",
5
5
  "keywords": [
6
6
  "video-chat",
@@ -16,7 +16,8 @@
16
16
  ],
17
17
  "type": "module",
18
18
  "sideEffects": [
19
- "./styles/video-chat.css"
19
+ "./styles/video-chat.css",
20
+ "./styles/fonts/roboto.css"
20
21
  ],
21
22
  "license": "Apache-2.0",
22
23
  "repository": {
@@ -31,6 +32,7 @@
31
32
  "bin",
32
33
  "dist",
33
34
  "styles/video-chat.css",
35
+ "styles/fonts",
34
36
  "starters/video-chat/.env.example",
35
37
  "starters/video-chat/README.md",
36
38
  "starters/video-chat/index.html",
@@ -54,7 +56,8 @@
54
56
  "docs/reference/protocol.md",
55
57
  "CHANGELOG.md",
56
58
  "docs/*.md",
57
- "docs/reference"
59
+ "docs/reference",
60
+ "docs/maintainers/cinematic-migration.md"
58
61
  ],
59
62
  "bin": {
60
63
  "vanillasky": "bin/vanillasky.js"