frameyard 0.6.0 → 0.8.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 (161) hide show
  1. package/CHANGELOG.md +106 -0
  2. package/GLOSSARY.md +40 -16
  3. package/README.md +64 -55
  4. package/dist/assets/process.js.map +1 -1
  5. package/dist/assets/provenance.js.map +1 -1
  6. package/dist/cli.js +255 -59
  7. package/dist/cli.js.map +1 -1
  8. package/dist/composition/attributes.d.ts.map +1 -1
  9. package/dist/composition/descriptor.d.ts +17 -3
  10. package/dist/composition/descriptor.d.ts.map +1 -1
  11. package/dist/composition/descriptor.js +51 -28
  12. package/dist/composition/descriptor.js.map +1 -1
  13. package/dist/composition/diagnostics.js.map +1 -1
  14. package/dist/composition/format.d.ts +12 -0
  15. package/dist/composition/format.d.ts.map +1 -0
  16. package/dist/composition/format.js +28 -0
  17. package/dist/composition/format.js.map +1 -0
  18. package/dist/composition/index.d.ts +2 -1
  19. package/dist/composition/index.d.ts.map +1 -1
  20. package/dist/composition/index.js +1 -0
  21. package/dist/composition/index.js.map +1 -1
  22. package/dist/contracts.d.ts +55 -6
  23. package/dist/contracts.d.ts.map +1 -1
  24. package/dist/contracts.js +3 -0
  25. package/dist/contracts.js.map +1 -1
  26. package/dist/doctor.d.ts.map +1 -1
  27. package/dist/doctor.js +79 -0
  28. package/dist/doctor.js.map +1 -1
  29. package/dist/errors.d.ts +1 -1
  30. package/dist/errors.d.ts.map +1 -1
  31. package/dist/errors.js.map +1 -1
  32. package/dist/html/document-adapter.d.ts +3 -3
  33. package/dist/html/document-adapter.d.ts.map +1 -1
  34. package/dist/html/document-adapter.js +1 -1
  35. package/dist/html/document-adapter.js.map +1 -1
  36. package/dist/html/index.d.ts +1 -1
  37. package/dist/html/index.d.ts.map +1 -1
  38. package/dist/html/parse.d.ts +10 -2
  39. package/dist/html/parse.d.ts.map +1 -1
  40. package/dist/html/parse.js +14 -5
  41. package/dist/html/parse.js.map +1 -1
  42. package/dist/html/validate.d.ts +12 -1
  43. package/dist/html/validate.d.ts.map +1 -1
  44. package/dist/html/validate.js +233 -61
  45. package/dist/html/validate.js.map +1 -1
  46. package/dist/index.d.ts +4 -4
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +2 -2
  49. package/dist/index.js.map +1 -1
  50. package/dist/preview/main.d.ts +2 -2
  51. package/dist/preview/main.d.ts.map +1 -1
  52. package/dist/preview/main.js +47 -19
  53. package/dist/preview/main.js.map +1 -1
  54. package/dist/preview/timeline-data.d.ts +5 -1
  55. package/dist/preview/timeline-data.d.ts.map +1 -1
  56. package/dist/preview/timeline-host.d.ts +2 -0
  57. package/dist/preview/timeline-host.d.ts.map +1 -1
  58. package/dist/preview/timeline-host.js +31 -10
  59. package/dist/preview/timeline-host.js.map +1 -1
  60. package/dist/preview/timeline-main.js +12 -7
  61. package/dist/preview/timeline-main.js.map +1 -1
  62. package/dist/project.d.ts +3 -5
  63. package/dist/project.d.ts.map +1 -1
  64. package/dist/project.js.map +1 -1
  65. package/dist/renderer/browser.d.ts +2 -2
  66. package/dist/renderer/browser.d.ts.map +1 -1
  67. package/dist/renderer/browser.js +10 -4
  68. package/dist/renderer/browser.js.map +1 -1
  69. package/dist/renderer/ffmpeg.d.ts +69 -2
  70. package/dist/renderer/ffmpeg.d.ts.map +1 -1
  71. package/dist/renderer/ffmpeg.js +381 -69
  72. package/dist/renderer/ffmpeg.js.map +1 -1
  73. package/dist/renderer/index.d.ts +66 -5
  74. package/dist/renderer/index.d.ts.map +1 -1
  75. package/dist/renderer/index.js +226 -51
  76. package/dist/renderer/index.js.map +1 -1
  77. package/dist/renderer/timeline.d.ts +41 -4
  78. package/dist/renderer/timeline.d.ts.map +1 -1
  79. package/dist/renderer/timeline.js +242 -39
  80. package/dist/renderer/timeline.js.map +1 -1
  81. package/dist/runtime/host.d.ts.map +1 -1
  82. package/dist/runtime/host.js +155 -31
  83. package/dist/runtime/host.js.map +1 -1
  84. package/dist/runtime/media.d.ts.map +1 -1
  85. package/dist/runtime/media.js +3 -6
  86. package/dist/runtime/media.js.map +1 -1
  87. package/dist/runtime/video.d.ts +13 -0
  88. package/dist/runtime/video.d.ts.map +1 -0
  89. package/dist/runtime/video.js +41 -0
  90. package/dist/runtime/video.js.map +1 -0
  91. package/dist/scaffold.d.ts.map +1 -1
  92. package/dist/scaffold.js +107 -103
  93. package/dist/scaffold.js.map +1 -1
  94. package/dist/server/composition-html.d.ts +13 -7
  95. package/dist/server/composition-html.d.ts.map +1 -1
  96. package/dist/server/composition-html.js +65 -16
  97. package/dist/server/composition-html.js.map +1 -1
  98. package/dist/server/preview-html.js +1 -1
  99. package/dist/server/server.d.ts.map +1 -1
  100. package/dist/server/server.js +26 -6
  101. package/dist/server/server.js.map +1 -1
  102. package/dist/server/timeline-preview-data.d.ts.map +1 -1
  103. package/dist/server/timeline-preview-data.js +11 -0
  104. package/dist/server/timeline-preview-data.js.map +1 -1
  105. package/dist/server/timeline-preview-html.d.ts.map +1 -1
  106. package/dist/server/timeline-preview-html.js +2 -1
  107. package/dist/server/timeline-preview-html.js.map +1 -1
  108. package/dist/timeline/plan.js.map +1 -1
  109. package/dist/timeline/resolve.d.ts +4 -3
  110. package/dist/timeline/resolve.d.ts.map +1 -1
  111. package/dist/timeline/resolve.js +14 -4
  112. package/dist/timeline/resolve.js.map +1 -1
  113. package/dist/timeline/schema.d.ts +7 -0
  114. package/dist/timeline/schema.d.ts.map +1 -1
  115. package/dist/timeline/schema.js +50 -0
  116. package/dist/timeline/schema.js.map +1 -1
  117. package/dist/transcript.js.map +1 -1
  118. package/dist/version.d.ts +1 -1
  119. package/dist/version.js +1 -1
  120. package/docs/adr/0007-make-html-documents-the-composition-unit.md +1 -1
  121. package/docs/adr/0008-share-the-composition-document-grammar.md +1 -1
  122. package/docs/adr/0011-compose-timeline-frames-in-a-live-browser-host.md +1 -1
  123. package/docs/adr/0012-render-timelines-with-segmented-lossless-clip-intermediates.md +1 -1
  124. package/docs/adr/0013-capture-bounded-multi-frame-stills-for-inspection.md +33 -0
  125. package/docs/adr/0014-compose-timeline-clips-with-css-blend-modes.md +37 -0
  126. package/docs/adr/0015-inherit-composition-format-from-the-project.md +28 -0
  127. package/docs/adr/0016-author-compositions-as-html-fragments.md +32 -0
  128. package/docs/adr/0017-render-timeless-compositions-as-stills.md +99 -0
  129. package/docs/migration-0.4.md +3 -1
  130. package/docs/migration-0.8.md +163 -0
  131. package/docs/preview-testing.md +60 -0
  132. package/docs/v0.1-design.md +52 -42
  133. package/examples/blend/README.md +106 -0
  134. package/examples/blend/assets/ink.mp4 +0 -0
  135. package/examples/blend/compositions/aurora.html +55 -0
  136. package/examples/blend/compositions/backdrop.html +74 -0
  137. package/examples/blend/compositions/labels.html +149 -0
  138. package/examples/blend/scripts/make-ink.sh +26 -0
  139. package/examples/blend/timelines/blend.timeline.json +155 -0
  140. package/examples/blend/video.config.js +11 -0
  141. package/examples/demo/compositions/demo.html +243 -251
  142. package/examples/static/README.md +17 -0
  143. package/examples/static/compositions/still.html +37 -0
  144. package/examples/static/timelines/still.timeline.json +13 -0
  145. package/examples/static/video.config.js +9 -0
  146. package/examples/video/compositions/video-mp4.html +349 -357
  147. package/examples/video/compositions/video-overlay.html +337 -345
  148. package/examples/video/compositions/video.html +349 -357
  149. package/examples/webgl/compositions/webgl.html +205 -213
  150. package/package.json +6 -3
  151. package/skills/frameyard/README.md +7 -5
  152. package/skills/frameyard/SKILL.md +17 -13
  153. package/skills/frameyard/references/common-mistakes.md +30 -6
  154. package/skills/frameyard/references/controllers.md +5 -4
  155. package/skills/frameyard/references/deterministic-authoring.md +42 -36
  156. package/skills/frameyard/references/diagnostics.md +11 -3
  157. package/skills/frameyard/references/failure-recovery.md +28 -7
  158. package/skills/frameyard/references/json-results.md +58 -7
  159. package/skills/frameyard/references/subtitles-karaoke.md +2 -2
  160. package/skills/frameyard/references/timelines.md +21 -15
  161. package/skills/frameyard/scripts/frameyard.mjs +20 -3
@@ -1,362 +1,354 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <script type="application/json" data-fy="props">
6
- {
7
- "kicker": "Frameyard / Clip Study 001",
8
- "title": "Moving",
9
- "titleAccent": "Image",
10
- "subtitle": "A local clip, loaded once and seeked twice from the composition clock.",
11
- "detail": "Detail / second pass",
12
- "accent": "#ffc37a"
13
- }
14
- </script>
15
- <style>
16
- @keyframes fade-in {
17
- from {
18
- opacity: 0;
19
- }
20
- to {
21
- opacity: 1;
22
- }
23
- }
24
- @keyframes rise {
25
- from {
26
- opacity: 0;
27
- transform: translateY(42px);
28
- }
29
- to {
30
- opacity: 1;
31
- transform: translateY(0);
32
- }
33
- }
34
- @keyframes draw {
35
- from {
36
- opacity: 0;
37
- transform: scaleX(0);
38
- }
39
- to {
40
- opacity: 1;
41
- transform: scaleX(1);
42
- }
43
- }
44
- @keyframes outro-fade {
45
- from {
46
- opacity: 0;
47
- }
48
- to {
49
- opacity: 1;
50
- }
51
- }
52
- html,
53
- body,
54
- fy-composition {
55
- background: #04080b;
56
- }
57
- #scene {
58
- position: relative;
59
- width: 100%;
60
- height: 100%;
61
- overflow: hidden;
62
- isolation: isolate;
63
- background: #04080b;
64
- color: #eef4f1;
65
- font-family: Inter, "Helvetica Neue", ui-sans-serif, system-ui, sans-serif;
66
- }
67
- #hero {
68
- position: absolute;
69
- inset: 0;
70
- display: block;
71
- width: 100%;
72
- height: 100%;
73
- object-fit: cover;
74
- transform: scale(calc(1 + var(--fy-progress) * 0.035));
75
- transform-origin: center;
76
- }
77
- #scrim {
78
- position: absolute;
79
- inset: 0;
80
- background:
81
- linear-gradient(
82
- 90deg,
83
- rgba(4, 8, 11, 0.94) 0%,
84
- rgba(4, 8, 11, 0.8) 24%,
85
- rgba(4, 8, 11, 0.24) 52%,
86
- rgba(4, 8, 11, 0) 74%
87
- ),
88
- linear-gradient(0deg, rgba(4, 8, 11, 0.9) 0%, rgba(4, 8, 11, 0) 34%);
89
- }
90
- #vignette {
91
- position: absolute;
92
- inset: 0;
93
- background: radial-gradient(ellipse at 62% 44%, transparent 42%, rgba(0, 0, 0, 0.3) 100%);
94
- pointer-events: none;
95
- }
96
- #frame-border {
97
- position: absolute;
98
- inset: 36px;
99
- border: 1px solid rgba(238, 244, 241, 0.16);
100
- pointer-events: none;
101
- }
102
- #header {
103
- position: absolute;
104
- top: 80px;
105
- left: 148px;
106
- right: 148px;
107
- display: flex;
108
- justify-content: space-between;
109
- align-items: center;
110
- gap: 32px;
111
- animation: fade-in 1s linear both;
112
- }
113
- #kicker {
114
- display: flex;
115
- align-items: center;
116
- gap: 15px;
117
- font:
118
- 600 14px/1 ui-monospace,
119
- monospace;
120
- letter-spacing: 0.24em;
121
- text-transform: uppercase;
122
- color: var(--fy-prop-accent);
123
- }
124
- #kicker::before {
125
- content: "";
126
- width: 8px;
127
- height: 8px;
128
- background: var(--fy-prop-accent);
129
- box-shadow: 0 0 16px var(--fy-prop-accent);
130
- }
131
- #format {
132
- font:
133
- 500 12px/1 ui-monospace,
134
- monospace;
135
- letter-spacing: 0.16em;
136
- text-transform: uppercase;
137
- color: #8b999c;
138
- }
139
- #copy {
140
- position: absolute;
141
- top: 278px;
142
- left: 148px;
143
- width: 860px;
144
- }
145
- #title {
146
- margin: 0;
147
- font-size: 174px;
148
- line-height: 0.84;
149
- letter-spacing: -0.064em;
150
- font-weight: 660;
151
- }
152
- #title span,
153
- #title em {
154
- display: block;
155
- animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
156
- }
157
- #title em {
158
- margin-top: 0.06em;
159
- font-family: "Iowan Old Style", "Palatino Linotype", Georgia, ui-serif, serif;
160
- font-style: italic;
161
- font-weight: 400;
162
- letter-spacing: -0.035em;
163
- color: var(--fy-prop-accent);
164
- }
165
- #subtitle {
166
- width: 720px;
167
- margin: 38px 0 0;
168
- font-size: 29px;
169
- line-height: 1.5;
170
- font-weight: 420;
171
- color: #a6b3b6;
172
- animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
173
- }
174
- #footer {
175
- position: absolute;
176
- left: 148px;
177
- right: 148px;
178
- bottom: 70px;
179
- contain: layout paint style;
180
- animation: fade-in 1s linear both;
181
- }
182
- #rule {
183
- position: relative;
184
- height: 1px;
185
- background: rgba(238, 244, 241, 0.16);
186
- transform-origin: left center;
187
- animation: draw 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
188
- }
189
- #progress {
190
- position: absolute;
191
- inset: 0 auto 0 0;
192
- width: calc(var(--fy-progress) * 100%);
193
- background: var(--fy-prop-accent);
194
- box-shadow: 0 0 14px var(--fy-prop-accent);
195
- }
196
- #footer-row {
197
- display: flex;
198
- justify-content: space-between;
199
- align-items: baseline;
200
- gap: 24px;
201
- margin-top: 18px;
202
- font:
203
- 500 12px/1 ui-monospace,
204
- monospace;
205
- letter-spacing: 0.14em;
206
- color: #879598;
207
- text-transform: uppercase;
208
- }
209
- #footer-row strong {
210
- color: #edf5f2;
211
- font-weight: 600;
212
- }
213
- #footer-row .accent {
214
- color: var(--fy-prop-accent);
215
- }
216
- #frame-counter {
217
- display: inline-block;
218
- min-width: 3ch;
219
- text-align: right;
220
- }
221
- #frame-counter::after {
222
- counter-reset: fy-frame var(--fy-frame);
223
- content: counter(fy-frame);
224
- }
225
- #detail {
226
- position: absolute;
227
- right: 148px;
228
- bottom: 154px;
229
- width: 440px;
230
- }
231
- #detail-bar {
232
- display: flex;
233
- justify-content: space-between;
234
- align-items: baseline;
235
- gap: 24px;
236
- margin-bottom: 10px;
237
- contain: layout paint style;
238
- font:
239
- 600 11px/1 ui-monospace,
240
- monospace;
241
- letter-spacing: 0.18em;
242
- text-transform: uppercase;
243
- }
244
- #detail-bar > span:first-child {
245
- color: var(--fy-prop-accent);
246
- }
247
- #detail-time {
248
- color: #98a5a6;
249
- font-weight: 500;
250
- }
251
- #detail-window {
252
- position: relative;
253
- width: 440px;
254
- height: 248px;
255
- overflow: hidden;
256
- border: 1px solid rgba(255, 195, 122, 0.65);
257
- background: #000;
258
- box-shadow: 0 24px 90px rgba(0, 0, 0, 0.55);
259
- }
260
- #detail-clip {
261
- display: block;
262
- width: 100%;
263
- height: 100%;
264
- object-fit: cover;
265
- object-position: 68% 50%;
266
- transform: scale(1.3);
267
- }
268
- #detail-tint {
269
- position: absolute;
270
- inset: 0;
271
- background: linear-gradient(0deg, rgba(4, 8, 11, 0.28) 0%, rgba(4, 8, 11, 0) 45%);
272
- pointer-events: none;
273
- }
274
- #outro-fade {
275
- position: absolute;
276
- inset: 0;
277
- background: #04080b;
278
- pointer-events: none;
279
- opacity: 0;
280
- animation: outro-fade 1s linear both;
281
- }
282
- </style>
283
- </head>
284
- <body>
285
- <fy-composition width="1920" height="1080" fps="30" duration-in-frames="360">
286
- <div id="scene">
287
- <video
288
- id="hero"
289
- src="assets/clip.webm"
290
- data-fy-start="0"
291
- data-fy-trim="0"
292
- data-fy-duration="12"
293
- data-fy-volume="0.85"
294
- ></video>
295
- <div id="scrim"></div>
296
- <div id="vignette"></div>
297
- <div id="frame-border"></div>
1
+ <script type="application/json" data-fy="props">
2
+ {
3
+ "kicker": "Frameyard / Clip Study 001",
4
+ "title": "Moving",
5
+ "titleAccent": "Image",
6
+ "subtitle": "A local clip, loaded once and seeked twice from the composition clock.",
7
+ "detail": "Detail / second pass",
8
+ "accent": "#ffc37a"
9
+ }
10
+ </script>
11
+ <style>
12
+ @keyframes fade-in {
13
+ from {
14
+ opacity: 0;
15
+ }
16
+ to {
17
+ opacity: 1;
18
+ }
19
+ }
20
+ @keyframes rise {
21
+ from {
22
+ opacity: 0;
23
+ transform: translateY(42px);
24
+ }
25
+ to {
26
+ opacity: 1;
27
+ transform: translateY(0);
28
+ }
29
+ }
30
+ @keyframes draw {
31
+ from {
32
+ opacity: 0;
33
+ transform: scaleX(0);
34
+ }
35
+ to {
36
+ opacity: 1;
37
+ transform: scaleX(1);
38
+ }
39
+ }
40
+ @keyframes outro-fade {
41
+ from {
42
+ opacity: 0;
43
+ }
44
+ to {
45
+ opacity: 1;
46
+ }
47
+ }
48
+ html,
49
+ body,
50
+ fy-composition {
51
+ background: #04080b;
52
+ }
53
+ #scene {
54
+ position: relative;
55
+ width: 100%;
56
+ height: 100%;
57
+ overflow: hidden;
58
+ isolation: isolate;
59
+ background: #04080b;
60
+ color: #eef4f1;
61
+ font-family: Inter, "Helvetica Neue", ui-sans-serif, system-ui, sans-serif;
62
+ }
63
+ #hero {
64
+ position: absolute;
65
+ inset: 0;
66
+ display: block;
67
+ width: 100%;
68
+ height: 100%;
69
+ object-fit: cover;
70
+ transform: scale(calc(1 + var(--fy-progress) * 0.035));
71
+ transform-origin: center;
72
+ }
73
+ #scrim {
74
+ position: absolute;
75
+ inset: 0;
76
+ background:
77
+ linear-gradient(
78
+ 90deg,
79
+ rgba(4, 8, 11, 0.94) 0%,
80
+ rgba(4, 8, 11, 0.8) 24%,
81
+ rgba(4, 8, 11, 0.24) 52%,
82
+ rgba(4, 8, 11, 0) 74%
83
+ ),
84
+ linear-gradient(0deg, rgba(4, 8, 11, 0.9) 0%, rgba(4, 8, 11, 0) 34%);
85
+ }
86
+ #vignette {
87
+ position: absolute;
88
+ inset: 0;
89
+ background: radial-gradient(ellipse at 62% 44%, transparent 42%, rgba(0, 0, 0, 0.3) 100%);
90
+ pointer-events: none;
91
+ }
92
+ #frame-border {
93
+ position: absolute;
94
+ inset: 36px;
95
+ border: 1px solid rgba(238, 244, 241, 0.16);
96
+ pointer-events: none;
97
+ }
98
+ #header {
99
+ position: absolute;
100
+ top: 80px;
101
+ left: 148px;
102
+ right: 148px;
103
+ display: flex;
104
+ justify-content: space-between;
105
+ align-items: center;
106
+ gap: 32px;
107
+ animation: fade-in 1s linear both;
108
+ }
109
+ #kicker {
110
+ display: flex;
111
+ align-items: center;
112
+ gap: 15px;
113
+ font:
114
+ 600 14px/1 ui-monospace,
115
+ monospace;
116
+ letter-spacing: 0.24em;
117
+ text-transform: uppercase;
118
+ color: var(--fy-prop-accent);
119
+ }
120
+ #kicker::before {
121
+ content: "";
122
+ width: 8px;
123
+ height: 8px;
124
+ background: var(--fy-prop-accent);
125
+ box-shadow: 0 0 16px var(--fy-prop-accent);
126
+ }
127
+ #format {
128
+ font:
129
+ 500 12px/1 ui-monospace,
130
+ monospace;
131
+ letter-spacing: 0.16em;
132
+ text-transform: uppercase;
133
+ color: #8b999c;
134
+ }
135
+ #copy {
136
+ position: absolute;
137
+ top: 278px;
138
+ left: 148px;
139
+ width: 860px;
140
+ }
141
+ #title {
142
+ margin: 0;
143
+ font-size: 174px;
144
+ line-height: 0.84;
145
+ letter-spacing: -0.064em;
146
+ font-weight: 660;
147
+ }
148
+ #title span,
149
+ #title em {
150
+ display: block;
151
+ animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
152
+ }
153
+ #title em {
154
+ margin-top: 0.06em;
155
+ font-family: "Iowan Old Style", "Palatino Linotype", Georgia, ui-serif, serif;
156
+ font-style: italic;
157
+ font-weight: 400;
158
+ letter-spacing: -0.035em;
159
+ color: var(--fy-prop-accent);
160
+ }
161
+ #subtitle {
162
+ width: 720px;
163
+ margin: 38px 0 0;
164
+ font-size: 29px;
165
+ line-height: 1.5;
166
+ font-weight: 420;
167
+ color: #a6b3b6;
168
+ animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
169
+ }
170
+ #footer {
171
+ position: absolute;
172
+ left: 148px;
173
+ right: 148px;
174
+ bottom: 70px;
175
+ contain: layout paint style;
176
+ animation: fade-in 1s linear both;
177
+ }
178
+ #rule {
179
+ position: relative;
180
+ height: 1px;
181
+ background: rgba(238, 244, 241, 0.16);
182
+ transform-origin: left center;
183
+ animation: draw 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
184
+ }
185
+ #progress {
186
+ position: absolute;
187
+ inset: 0 auto 0 0;
188
+ width: calc(var(--fy-progress) * 100%);
189
+ background: var(--fy-prop-accent);
190
+ box-shadow: 0 0 14px var(--fy-prop-accent);
191
+ }
192
+ #footer-row {
193
+ display: flex;
194
+ justify-content: space-between;
195
+ align-items: baseline;
196
+ gap: 24px;
197
+ margin-top: 18px;
198
+ font:
199
+ 500 12px/1 ui-monospace,
200
+ monospace;
201
+ letter-spacing: 0.14em;
202
+ color: #879598;
203
+ text-transform: uppercase;
204
+ }
205
+ #footer-row strong {
206
+ color: #edf5f2;
207
+ font-weight: 600;
208
+ }
209
+ #footer-row .accent {
210
+ color: var(--fy-prop-accent);
211
+ }
212
+ #frame-counter {
213
+ display: inline-block;
214
+ min-width: 3ch;
215
+ text-align: right;
216
+ }
217
+ #frame-counter::after {
218
+ counter-reset: fy-frame var(--fy-frame);
219
+ content: counter(fy-frame);
220
+ }
221
+ #detail {
222
+ position: absolute;
223
+ right: 148px;
224
+ bottom: 154px;
225
+ width: 440px;
226
+ }
227
+ #detail-bar {
228
+ display: flex;
229
+ justify-content: space-between;
230
+ align-items: baseline;
231
+ gap: 24px;
232
+ margin-bottom: 10px;
233
+ contain: layout paint style;
234
+ font:
235
+ 600 11px/1 ui-monospace,
236
+ monospace;
237
+ letter-spacing: 0.18em;
238
+ text-transform: uppercase;
239
+ }
240
+ #detail-bar > span:first-child {
241
+ color: var(--fy-prop-accent);
242
+ }
243
+ #detail-time {
244
+ color: #98a5a6;
245
+ font-weight: 500;
246
+ }
247
+ #detail-window {
248
+ position: relative;
249
+ width: 440px;
250
+ height: 248px;
251
+ overflow: hidden;
252
+ border: 1px solid rgba(255, 195, 122, 0.65);
253
+ background: #000;
254
+ box-shadow: 0 24px 90px rgba(0, 0, 0, 0.55);
255
+ }
256
+ #detail-clip {
257
+ display: block;
258
+ width: 100%;
259
+ height: 100%;
260
+ object-fit: cover;
261
+ object-position: 68% 50%;
262
+ transform: scale(1.3);
263
+ }
264
+ #detail-tint {
265
+ position: absolute;
266
+ inset: 0;
267
+ background: linear-gradient(0deg, rgba(4, 8, 11, 0.28) 0%, rgba(4, 8, 11, 0) 45%);
268
+ pointer-events: none;
269
+ }
270
+ #outro-fade {
271
+ position: absolute;
272
+ inset: 0;
273
+ background: #04080b;
274
+ pointer-events: none;
275
+ opacity: 0;
276
+ animation: outro-fade 1s linear both;
277
+ }
278
+ </style>
279
+ <fy-composition width="1920" height="1080" fps="30" duration-in-frames="360">
280
+ <div id="scene">
281
+ <video
282
+ id="hero"
283
+ src="assets/clip.webm"
284
+ data-fy-start="0"
285
+ data-fy-trim="0"
286
+ data-fy-duration="12"
287
+ data-fy-volume="0.85"
288
+ ></video>
289
+ <div id="scrim"></div>
290
+ <div id="vignette"></div>
291
+ <div id="frame-border"></div>
298
292
 
299
- <header id="header" data-fy-anim-start="8" data-fy-anim-duration="28">
300
- <div id="kicker" data-fy-text="kicker"></div>
301
- <div id="format">Embedded video / VP9 + Opus / 30 FPS</div>
302
- </header>
293
+ <header id="header" data-fy-anim-start="8" data-fy-anim-duration="28">
294
+ <div id="kicker" data-fy-text="kicker"></div>
295
+ <div id="format">Embedded video / VP9 + Opus / 30 FPS</div>
296
+ </header>
303
297
 
304
- <div id="copy">
305
- <h1 id="title">
306
- <span
307
- data-fy-text="title"
308
- data-fy-anim-start="14"
309
- data-fy-anim-duration="44"
310
- data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
311
- ></span>
312
- <em
313
- data-fy-text="titleAccent"
314
- data-fy-anim-start="26"
315
- data-fy-anim-duration="44"
316
- data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
317
- ></em>
318
- </h1>
319
- <p
320
- id="subtitle"
321
- data-fy-text="subtitle"
322
- data-fy-anim-start="40"
323
- data-fy-anim-duration="40"
324
- data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
325
- ></p>
326
- </div>
298
+ <div id="copy">
299
+ <h1 id="title">
300
+ <span
301
+ data-fy-text="title"
302
+ data-fy-anim-start="14"
303
+ data-fy-anim-duration="44"
304
+ data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
305
+ ></span>
306
+ <em
307
+ data-fy-text="titleAccent"
308
+ data-fy-anim-start="26"
309
+ data-fy-anim-duration="44"
310
+ data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
311
+ ></em>
312
+ </h1>
313
+ <p
314
+ id="subtitle"
315
+ data-fy-text="subtitle"
316
+ data-fy-anim-start="40"
317
+ data-fy-anim-duration="40"
318
+ data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
319
+ ></p>
320
+ </div>
327
321
 
328
- <div id="detail">
329
- <div id="detail-bar">
330
- <span data-fy-text="detail"></span>
331
- <span id="detail-time">SRC 5.8–10.8 S</span>
332
- </div>
333
- <div id="detail-window">
334
- <video
335
- id="detail-clip"
336
- src="assets/clip.webm"
337
- data-fy-start="120"
338
- data-fy-trim="5.8"
339
- data-fy-duration="5"
340
- data-fy-audio="off"
341
- ></video>
342
- <div id="detail-tint"></div>
343
- </div>
344
- </div>
345
-
346
- <div id="footer" data-fy-anim-start="46" data-fy-anim-duration="30">
347
- <div id="rule" data-fy-anim-start="52" data-fy-anim-duration="50">
348
- <div id="progress"></div>
349
- </div>
350
- <div id="footer-row">
351
- <span>Hero / SRC <strong>0.0–12.0 S</strong></span>
352
- <span>Frame <strong id="frame-counter"></strong> / 359</span>
353
- <span class="accent">Frame-seeked</span>
354
- <span>12.00 S · 30 FPS</span>
355
- </div>
356
- </div>
322
+ <div id="detail">
323
+ <div id="detail-bar">
324
+ <span data-fy-text="detail"></span>
325
+ <span id="detail-time">SRC 5.8–10.8 S</span>
326
+ </div>
327
+ <div id="detail-window">
328
+ <video
329
+ id="detail-clip"
330
+ src="assets/clip.webm"
331
+ data-fy-start="120"
332
+ data-fy-trim="5.8"
333
+ data-fy-duration="5"
334
+ data-fy-audio="off"
335
+ ></video>
336
+ <div id="detail-tint"></div>
337
+ </div>
338
+ </div>
357
339
 
358
- <div id="outro-fade" data-fy-anim-start="329" data-fy-anim-duration="30"></div>
340
+ <div id="footer" data-fy-anim-start="46" data-fy-anim-duration="30">
341
+ <div id="rule" data-fy-anim-start="52" data-fy-anim-duration="50">
342
+ <div id="progress"></div>
359
343
  </div>
360
- </fy-composition>
361
- </body>
362
- </html>
344
+ <div id="footer-row">
345
+ <span>Hero / SRC <strong>0.0–12.0 S</strong></span>
346
+ <span>Frame <strong id="frame-counter"></strong> / 359</span>
347
+ <span class="accent">Frame-seeked</span>
348
+ <span>12.00 S · 30 FPS</span>
349
+ </div>
350
+ </div>
351
+
352
+ <div id="outro-fade" data-fy-anim-start="329" data-fy-anim-duration="30"></div>
353
+ </div>
354
+ </fy-composition>