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,350 +1,342 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <script type="application/json" data-fy="props">
6
- {
7
- "kicker": "Frameyard / Timeline Overlay 001",
8
- "title": "Timeline",
9
- "titleAccent": "Overlay",
10
- "subtitle": "The clip lives on a timeline track. This HTML layer composites over it with alpha.",
11
- "detail": "Detail / second pass",
12
- "format": "30 FPS",
13
- "accent": "#ffc37a"
14
- }
15
- </script>
16
- <style>
17
- @keyframes fade-in {
18
- from {
19
- opacity: 0;
20
- }
21
- to {
22
- opacity: 1;
23
- }
24
- }
25
- @keyframes rise {
26
- from {
27
- opacity: 0;
28
- transform: translateY(42px);
29
- }
30
- to {
31
- opacity: 1;
32
- transform: translateY(0);
33
- }
34
- }
35
- @keyframes draw {
36
- from {
37
- opacity: 0;
38
- transform: scaleX(0);
39
- }
40
- to {
41
- opacity: 1;
42
- transform: scaleX(1);
43
- }
44
- }
45
- @keyframes detail-cycle {
46
- 0% {
47
- opacity: 0;
48
- }
49
- 6% {
50
- opacity: 1;
51
- }
52
- 94% {
53
- opacity: 1;
54
- }
55
- 100% {
56
- opacity: 0;
57
- }
58
- }
59
- @keyframes outro-fade {
60
- from {
61
- opacity: 0;
62
- }
63
- to {
64
- opacity: 1;
65
- }
66
- }
67
- html,
68
- body,
69
- fy-composition {
70
- background: transparent;
71
- }
72
- #scene {
73
- position: relative;
74
- width: 100%;
75
- height: 100%;
76
- overflow: hidden;
77
- isolation: isolate;
78
- color: #eef4f1;
79
- font-family: Inter, "Helvetica Neue", ui-sans-serif, system-ui, sans-serif;
80
- }
81
- #scrim {
82
- position: absolute;
83
- inset: 0;
84
- background: linear-gradient(
85
- 90deg,
86
- rgba(4, 8, 11, 0.94) 0%,
87
- rgba(4, 8, 11, 0.82) 22%,
88
- rgba(4, 8, 11, 0.35) 46%,
89
- rgba(4, 8, 11, 0) 66%
90
- );
91
- pointer-events: none;
92
- }
93
- #footer-scrim {
94
- position: absolute;
95
- left: 0;
96
- right: 0;
97
- bottom: 0;
98
- height: 150px;
99
- background: linear-gradient(0deg, rgba(4, 8, 11, 0.88) 0%, rgba(4, 8, 11, 0) 100%);
100
- pointer-events: none;
101
- }
102
- #frame-border {
103
- position: absolute;
104
- inset: 36px;
105
- border: 1px solid rgba(238, 244, 241, 0.16);
106
- pointer-events: none;
107
- }
108
- #header {
109
- position: absolute;
110
- top: 80px;
111
- left: 148px;
112
- right: 148px;
113
- display: flex;
114
- justify-content: space-between;
115
- align-items: center;
116
- gap: 32px;
117
- animation: fade-in 1s linear both;
118
- }
119
- #kicker {
120
- display: flex;
121
- align-items: center;
122
- gap: 15px;
123
- font:
124
- 600 14px/1 ui-monospace,
125
- monospace;
126
- letter-spacing: 0.24em;
127
- text-transform: uppercase;
128
- color: var(--fy-prop-accent);
129
- }
130
- #kicker::before {
131
- content: "";
132
- width: 8px;
133
- height: 8px;
134
- background: var(--fy-prop-accent);
135
- box-shadow: 0 0 16px var(--fy-prop-accent);
136
- }
137
- #format {
138
- font:
139
- 500 12px/1 ui-monospace,
140
- monospace;
141
- letter-spacing: 0.16em;
142
- text-transform: uppercase;
143
- color: #b9c6c8;
144
- text-shadow: 0 2px 18px rgba(0, 0, 0, 0.65);
145
- }
146
- #copy {
147
- position: absolute;
148
- top: 278px;
149
- left: 148px;
150
- width: 860px;
151
- }
152
- #title {
153
- margin: 0;
154
- font-size: 174px;
155
- line-height: 0.84;
156
- letter-spacing: -0.064em;
157
- font-weight: 660;
158
- }
159
- #title span,
160
- #title em {
161
- display: block;
162
- animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
163
- }
164
- #title em {
165
- margin-top: 0.06em;
166
- font-family: "Iowan Old Style", "Palatino Linotype", Georgia, ui-serif, serif;
167
- font-style: italic;
168
- font-weight: 400;
169
- letter-spacing: -0.035em;
170
- color: var(--fy-prop-accent);
171
- }
172
- #subtitle {
173
- width: 720px;
174
- margin: 38px 0 0;
175
- font-size: 29px;
176
- line-height: 1.5;
177
- font-weight: 420;
178
- color: #b3c1c4;
179
- animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
180
- }
181
- #detail {
182
- position: absolute;
183
- left: 1332px;
184
- top: 646px;
185
- width: 440px;
186
- animation: detail-cycle 1s linear both;
187
- }
188
- #detail-bar {
189
- display: flex;
190
- justify-content: space-between;
191
- align-items: center;
192
- gap: 16px;
193
- height: 22px;
194
- margin-bottom: 10px;
195
- padding: 0 10px;
196
- background: rgba(4, 8, 11, 0.55);
197
- font:
198
- 600 11px/1 ui-monospace,
199
- monospace;
200
- letter-spacing: 0.18em;
201
- text-transform: uppercase;
202
- }
203
- #detail-bar > span:first-child {
204
- color: var(--fy-prop-accent);
205
- }
206
- #detail-time {
207
- color: #c0cdcf;
208
- font-weight: 500;
209
- }
210
- #detail-window {
211
- position: relative;
212
- width: 440px;
213
- height: 248px;
214
- box-sizing: border-box;
215
- overflow: hidden;
216
- border: 1px solid rgba(255, 195, 122, 0.75);
217
- background: transparent;
218
- box-shadow: 0 24px 90px rgba(0, 0, 0, 0.45);
219
- }
220
- #detail-tint {
221
- position: absolute;
222
- inset: 0;
223
- background: linear-gradient(0deg, rgba(4, 8, 11, 0.32) 0%, rgba(4, 8, 11, 0) 45%);
224
- pointer-events: none;
225
- }
226
- #footer {
227
- position: absolute;
228
- left: 148px;
229
- right: 148px;
230
- bottom: 70px;
231
- contain: layout paint style;
232
- animation: fade-in 1s linear both;
233
- }
234
- #rule {
235
- position: relative;
236
- height: 1px;
237
- background: rgba(238, 244, 241, 0.24);
238
- transform-origin: left center;
239
- animation: draw 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
240
- }
241
- #progress {
242
- position: absolute;
243
- inset: 0 auto 0 0;
244
- width: calc(var(--fy-progress) * 100%);
245
- background: var(--fy-prop-accent);
246
- box-shadow: 0 0 14px var(--fy-prop-accent);
247
- }
248
- #footer-row {
249
- display: flex;
250
- justify-content: space-between;
251
- align-items: baseline;
252
- gap: 24px;
253
- margin-top: 18px;
254
- font:
255
- 500 12px/1 ui-monospace,
256
- monospace;
257
- letter-spacing: 0.14em;
258
- color: #a4b1b3;
259
- text-transform: uppercase;
260
- text-shadow: 0 2px 14px rgba(0, 0, 0, 0.7);
261
- }
262
- #footer-row strong {
263
- color: #edf5f2;
264
- font-weight: 600;
265
- }
266
- #footer-row .accent {
267
- color: var(--fy-prop-accent);
268
- }
269
- #frame-counter {
270
- display: inline-block;
271
- min-width: 3ch;
272
- text-align: right;
273
- }
274
- #frame-counter::after {
275
- counter-reset: fy-frame var(--fy-frame);
276
- content: counter(fy-frame);
277
- }
278
- #outro-fade {
279
- position: absolute;
280
- inset: 0;
281
- background: #04080b;
282
- pointer-events: none;
283
- opacity: 0;
284
- animation: outro-fade 1s linear both;
285
- }
286
- </style>
287
- </head>
288
- <body>
289
- <fy-composition width="1920" height="1080" fps="30" duration-in-frames="360">
290
- <div id="scene">
291
- <div id="scrim"></div>
292
- <div id="footer-scrim"></div>
293
- <div id="frame-border"></div>
1
+ <script type="application/json" data-fy="props">
2
+ {
3
+ "kicker": "Frameyard / Timeline Overlay 001",
4
+ "title": "Timeline",
5
+ "titleAccent": "Overlay",
6
+ "subtitle": "The clip lives on a timeline track. This HTML layer composites over it with alpha.",
7
+ "detail": "Detail / second pass",
8
+ "format": "30 FPS",
9
+ "accent": "#ffc37a"
10
+ }
11
+ </script>
12
+ <style>
13
+ @keyframes fade-in {
14
+ from {
15
+ opacity: 0;
16
+ }
17
+ to {
18
+ opacity: 1;
19
+ }
20
+ }
21
+ @keyframes rise {
22
+ from {
23
+ opacity: 0;
24
+ transform: translateY(42px);
25
+ }
26
+ to {
27
+ opacity: 1;
28
+ transform: translateY(0);
29
+ }
30
+ }
31
+ @keyframes draw {
32
+ from {
33
+ opacity: 0;
34
+ transform: scaleX(0);
35
+ }
36
+ to {
37
+ opacity: 1;
38
+ transform: scaleX(1);
39
+ }
40
+ }
41
+ @keyframes detail-cycle {
42
+ 0% {
43
+ opacity: 0;
44
+ }
45
+ 6% {
46
+ opacity: 1;
47
+ }
48
+ 94% {
49
+ opacity: 1;
50
+ }
51
+ 100% {
52
+ opacity: 0;
53
+ }
54
+ }
55
+ @keyframes outro-fade {
56
+ from {
57
+ opacity: 0;
58
+ }
59
+ to {
60
+ opacity: 1;
61
+ }
62
+ }
63
+ html,
64
+ body,
65
+ fy-composition {
66
+ background: transparent;
67
+ }
68
+ #scene {
69
+ position: relative;
70
+ width: 100%;
71
+ height: 100%;
72
+ overflow: hidden;
73
+ isolation: isolate;
74
+ color: #eef4f1;
75
+ font-family: Inter, "Helvetica Neue", ui-sans-serif, system-ui, sans-serif;
76
+ }
77
+ #scrim {
78
+ position: absolute;
79
+ inset: 0;
80
+ background: linear-gradient(
81
+ 90deg,
82
+ rgba(4, 8, 11, 0.94) 0%,
83
+ rgba(4, 8, 11, 0.82) 22%,
84
+ rgba(4, 8, 11, 0.35) 46%,
85
+ rgba(4, 8, 11, 0) 66%
86
+ );
87
+ pointer-events: none;
88
+ }
89
+ #footer-scrim {
90
+ position: absolute;
91
+ left: 0;
92
+ right: 0;
93
+ bottom: 0;
94
+ height: 150px;
95
+ background: linear-gradient(0deg, rgba(4, 8, 11, 0.88) 0%, rgba(4, 8, 11, 0) 100%);
96
+ pointer-events: none;
97
+ }
98
+ #frame-border {
99
+ position: absolute;
100
+ inset: 36px;
101
+ border: 1px solid rgba(238, 244, 241, 0.16);
102
+ pointer-events: none;
103
+ }
104
+ #header {
105
+ position: absolute;
106
+ top: 80px;
107
+ left: 148px;
108
+ right: 148px;
109
+ display: flex;
110
+ justify-content: space-between;
111
+ align-items: center;
112
+ gap: 32px;
113
+ animation: fade-in 1s linear both;
114
+ }
115
+ #kicker {
116
+ display: flex;
117
+ align-items: center;
118
+ gap: 15px;
119
+ font:
120
+ 600 14px/1 ui-monospace,
121
+ monospace;
122
+ letter-spacing: 0.24em;
123
+ text-transform: uppercase;
124
+ color: var(--fy-prop-accent);
125
+ }
126
+ #kicker::before {
127
+ content: "";
128
+ width: 8px;
129
+ height: 8px;
130
+ background: var(--fy-prop-accent);
131
+ box-shadow: 0 0 16px var(--fy-prop-accent);
132
+ }
133
+ #format {
134
+ font:
135
+ 500 12px/1 ui-monospace,
136
+ monospace;
137
+ letter-spacing: 0.16em;
138
+ text-transform: uppercase;
139
+ color: #b9c6c8;
140
+ text-shadow: 0 2px 18px rgba(0, 0, 0, 0.65);
141
+ }
142
+ #copy {
143
+ position: absolute;
144
+ top: 278px;
145
+ left: 148px;
146
+ width: 860px;
147
+ }
148
+ #title {
149
+ margin: 0;
150
+ font-size: 174px;
151
+ line-height: 0.84;
152
+ letter-spacing: -0.064em;
153
+ font-weight: 660;
154
+ }
155
+ #title span,
156
+ #title em {
157
+ display: block;
158
+ animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
159
+ }
160
+ #title em {
161
+ margin-top: 0.06em;
162
+ font-family: "Iowan Old Style", "Palatino Linotype", Georgia, ui-serif, serif;
163
+ font-style: italic;
164
+ font-weight: 400;
165
+ letter-spacing: -0.035em;
166
+ color: var(--fy-prop-accent);
167
+ }
168
+ #subtitle {
169
+ width: 720px;
170
+ margin: 38px 0 0;
171
+ font-size: 29px;
172
+ line-height: 1.5;
173
+ font-weight: 420;
174
+ color: #b3c1c4;
175
+ animation: rise 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
176
+ }
177
+ #detail {
178
+ position: absolute;
179
+ left: 1332px;
180
+ top: 646px;
181
+ width: 440px;
182
+ animation: detail-cycle 1s linear both;
183
+ }
184
+ #detail-bar {
185
+ display: flex;
186
+ justify-content: space-between;
187
+ align-items: center;
188
+ gap: 16px;
189
+ height: 22px;
190
+ margin-bottom: 10px;
191
+ padding: 0 10px;
192
+ background: rgba(4, 8, 11, 0.55);
193
+ font:
194
+ 600 11px/1 ui-monospace,
195
+ monospace;
196
+ letter-spacing: 0.18em;
197
+ text-transform: uppercase;
198
+ }
199
+ #detail-bar > span:first-child {
200
+ color: var(--fy-prop-accent);
201
+ }
202
+ #detail-time {
203
+ color: #c0cdcf;
204
+ font-weight: 500;
205
+ }
206
+ #detail-window {
207
+ position: relative;
208
+ width: 440px;
209
+ height: 248px;
210
+ box-sizing: border-box;
211
+ overflow: hidden;
212
+ border: 1px solid rgba(255, 195, 122, 0.75);
213
+ background: transparent;
214
+ box-shadow: 0 24px 90px rgba(0, 0, 0, 0.45);
215
+ }
216
+ #detail-tint {
217
+ position: absolute;
218
+ inset: 0;
219
+ background: linear-gradient(0deg, rgba(4, 8, 11, 0.32) 0%, rgba(4, 8, 11, 0) 45%);
220
+ pointer-events: none;
221
+ }
222
+ #footer {
223
+ position: absolute;
224
+ left: 148px;
225
+ right: 148px;
226
+ bottom: 70px;
227
+ contain: layout paint style;
228
+ animation: fade-in 1s linear both;
229
+ }
230
+ #rule {
231
+ position: relative;
232
+ height: 1px;
233
+ background: rgba(238, 244, 241, 0.24);
234
+ transform-origin: left center;
235
+ animation: draw 1s cubic-bezier(0.215, 0.61, 0.355, 1) both;
236
+ }
237
+ #progress {
238
+ position: absolute;
239
+ inset: 0 auto 0 0;
240
+ width: calc(var(--fy-progress) * 100%);
241
+ background: var(--fy-prop-accent);
242
+ box-shadow: 0 0 14px var(--fy-prop-accent);
243
+ }
244
+ #footer-row {
245
+ display: flex;
246
+ justify-content: space-between;
247
+ align-items: baseline;
248
+ gap: 24px;
249
+ margin-top: 18px;
250
+ font:
251
+ 500 12px/1 ui-monospace,
252
+ monospace;
253
+ letter-spacing: 0.14em;
254
+ color: #a4b1b3;
255
+ text-transform: uppercase;
256
+ text-shadow: 0 2px 14px rgba(0, 0, 0, 0.7);
257
+ }
258
+ #footer-row strong {
259
+ color: #edf5f2;
260
+ font-weight: 600;
261
+ }
262
+ #footer-row .accent {
263
+ color: var(--fy-prop-accent);
264
+ }
265
+ #frame-counter {
266
+ display: inline-block;
267
+ min-width: 3ch;
268
+ text-align: right;
269
+ }
270
+ #frame-counter::after {
271
+ counter-reset: fy-frame var(--fy-frame);
272
+ content: counter(fy-frame);
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
+ <fy-composition width="1920" height="1080" fps="30" duration-in-frames="360">
284
+ <div id="scene">
285
+ <div id="scrim"></div>
286
+ <div id="footer-scrim"></div>
287
+ <div id="frame-border"></div>
294
288
 
295
- <header id="header" data-fy-anim-start="8" data-fy-anim-duration="28">
296
- <div id="kicker" data-fy-text="kicker"></div>
297
- <div id="format">Timeline video / <span data-fy-text="format"></span></div>
298
- </header>
289
+ <header id="header" data-fy-anim-start="8" data-fy-anim-duration="28">
290
+ <div id="kicker" data-fy-text="kicker"></div>
291
+ <div id="format">Timeline video / <span data-fy-text="format"></span></div>
292
+ </header>
299
293
 
300
- <div id="copy">
301
- <h1 id="title">
302
- <span
303
- data-fy-text="title"
304
- data-fy-anim-start="14"
305
- data-fy-anim-duration="44"
306
- data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
307
- ></span>
308
- <em
309
- data-fy-text="titleAccent"
310
- data-fy-anim-start="26"
311
- data-fy-anim-duration="44"
312
- data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
313
- ></em>
314
- </h1>
315
- <p
316
- id="subtitle"
317
- data-fy-text="subtitle"
318
- data-fy-anim-start="40"
319
- data-fy-anim-duration="40"
320
- data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
321
- ></p>
322
- </div>
294
+ <div id="copy">
295
+ <h1 id="title">
296
+ <span
297
+ data-fy-text="title"
298
+ data-fy-anim-start="14"
299
+ data-fy-anim-duration="44"
300
+ data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
301
+ ></span>
302
+ <em
303
+ data-fy-text="titleAccent"
304
+ data-fy-anim-start="26"
305
+ data-fy-anim-duration="44"
306
+ data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
307
+ ></em>
308
+ </h1>
309
+ <p
310
+ id="subtitle"
311
+ data-fy-text="subtitle"
312
+ data-fy-anim-start="40"
313
+ data-fy-anim-duration="40"
314
+ data-fy-anim-easing="cubic-bezier(0.215, 0.61, 0.355, 1)"
315
+ ></p>
316
+ </div>
323
317
 
324
- <div id="detail" data-fy-anim-start="112" data-fy-anim-duration="170">
325
- <div id="detail-bar">
326
- <span data-fy-text="detail"></span>
327
- <span id="detail-time">SRC 5.8–10.8 S</span>
328
- </div>
329
- <div id="detail-window">
330
- <div id="detail-tint"></div>
331
- </div>
332
- </div>
333
-
334
- <div id="footer" data-fy-anim-start="46" data-fy-anim-duration="30">
335
- <div id="rule" data-fy-anim-start="52" data-fy-anim-duration="50">
336
- <div id="progress"></div>
337
- </div>
338
- <div id="footer-row">
339
- <span>Hero / v1 · SRC <strong>0.0–12.0 S</strong></span>
340
- <span>Detail / v2 · SRC <strong>5.8–10.8 S</strong></span>
341
- <span>Frame <strong id="frame-counter"></strong> / 359</span>
342
- <span class="accent">HTML overlay / v3 · 30 FPS</span>
343
- </div>
344
- </div>
318
+ <div id="detail" data-fy-anim-start="112" data-fy-anim-duration="170">
319
+ <div id="detail-bar">
320
+ <span data-fy-text="detail"></span>
321
+ <span id="detail-time">SRC 5.8–10.8 S</span>
322
+ </div>
323
+ <div id="detail-window">
324
+ <div id="detail-tint"></div>
325
+ </div>
326
+ </div>
345
327
 
346
- <div id="outro-fade" data-fy-anim-start="329" data-fy-anim-duration="30"></div>
328
+ <div id="footer" data-fy-anim-start="46" data-fy-anim-duration="30">
329
+ <div id="rule" data-fy-anim-start="52" data-fy-anim-duration="50">
330
+ <div id="progress"></div>
347
331
  </div>
348
- </fy-composition>
349
- </body>
350
- </html>
332
+ <div id="footer-row">
333
+ <span>Hero / v1 · SRC <strong>0.0–12.0 S</strong></span>
334
+ <span>Detail / v2 · SRC <strong>5.8–10.8 S</strong></span>
335
+ <span>Frame <strong id="frame-counter"></strong> / 359</span>
336
+ <span class="accent">HTML overlay / v3 · 30 FPS</span>
337
+ </div>
338
+ </div>
339
+
340
+ <div id="outro-fade" data-fy-anim-start="329" data-fy-anim-duration="30"></div>
341
+ </div>
342
+ </fy-composition>