@hraness/slopcamera 3.5.0 → 3.7.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 (52) hide show
  1. package/README.md +42 -26
  2. package/apps/desktop/README.md +1 -1
  3. package/apps/desktop/application/html-overlay-renderer.ts +6 -0
  4. package/apps/desktop/application/operation.ts +1 -1
  5. package/apps/desktop/cli/args.ts +63 -1
  6. package/apps/desktop/cli/command-host-resources.ts +5 -0
  7. package/apps/desktop/cli/commands.ts +27 -1
  8. package/apps/desktop/cli/help.ts +43 -21
  9. package/apps/desktop/cli/html-film-init.ts +158 -0
  10. package/apps/desktop/cli/html-film-names.ts +17 -0
  11. package/apps/desktop/cli/html-film.ts +471 -0
  12. package/apps/desktop/cli/html-overlay-renderer.ts +29 -2
  13. package/apps/desktop/cli/html-scene.ts +3 -3
  14. package/apps/desktop/cli/main.ts +1 -0
  15. package/apps/desktop/cli/menubar-status.ts +156 -0
  16. package/apps/desktop/cli/menubar.ts +88 -136
  17. package/apps/desktop/cli/portable-surface.ts +55 -1
  18. package/apps/desktop/dist/cli/main.js +353 -328
  19. package/apps/desktop/html-overlay/film.ts +418 -0
  20. package/dist/cli.js +19 -9
  21. package/dist/generate.js +1 -1
  22. package/dist/{index-8bhp7cj2.js → index-842t02pp.js} +1 -1
  23. package/dist/{index-jab2mg0s.js → index-nv3byqa1.js} +20 -4
  24. package/dist/index-p74xy76d.js +3 -0
  25. package/dist/{index-24w75s6a.js → index-y8spxs77.js} +10 -7
  26. package/dist/index.js +1 -1
  27. package/dist/operations.js +1 -1
  28. package/dist/workflow.js +1 -1
  29. package/docs/README.md +5 -5
  30. package/examples/html/launch-film/README.md +51 -0
  31. package/examples/html/launch-film/build.ts +158 -0
  32. package/examples/html/launch-film/film.css +593 -0
  33. package/examples/html/launch-film/film.html +85 -0
  34. package/examples/html/launch-film/film.js +410 -0
  35. package/examples/html/launch-film/film.json +63 -0
  36. package/examples/html/launch-film/mockups.tsx +74 -0
  37. package/examples/html/launch-film/timeline.ts +57 -0
  38. package/package.json +14 -8
  39. package/skills/slopcamera/SKILL.md +3 -2
  40. package/skills/slopcamera/references/install.md +3 -3
  41. package/skills/slopcamera/references/launch-films.md +154 -0
  42. package/skills/slopcamera/references/visual-communication.md +1 -1
  43. package/src/cli.ts +138 -19
  44. package/src/generate.ts +44 -1
  45. package/src/generation-pricing.ts +237 -0
  46. package/src/icon-set.ts +1086 -0
  47. package/src/icon.ts +396 -59
  48. package/src/index.ts +1 -0
  49. package/src/operations.ts +52 -0
  50. package/src/support.ts +6 -0
  51. package/src/version.ts +1 -1
  52. package/dist/index-77fjfg9f.js +0 -3
@@ -0,0 +1,593 @@
1
+ /* Launch film stage. Sizes use --u, one unit per pixel of a 1080 px short
2
+ * side, so the same film renders at 16:9, 1:1 and 9:16. */
3
+
4
+ :root {
5
+ --ink: #f3f1ec;
6
+ --ink-2: rgb(243 241 236 / 0.62);
7
+ --paper: #f6f4ef;
8
+ --paper-ink: #16171a;
9
+ --paper-ink-2: #5d5f66;
10
+ --line: rgb(255 255 255 / 0.09);
11
+ }
12
+
13
+ * {
14
+ box-sizing: border-box;
15
+ }
16
+
17
+ html,
18
+ body {
19
+ margin: 0;
20
+ width: var(--cw);
21
+ height: var(--ch);
22
+ overflow: hidden;
23
+ background: #111214;
24
+ }
25
+
26
+ /* The film always lays out at full size; --zoom scales a draft render down
27
+ * as a whole, so mockups with fixed pixel sizes keep their proportions. */
28
+ .film {
29
+ position: relative;
30
+ width: var(--w);
31
+ height: var(--h);
32
+ zoom: var(--zoom);
33
+ overflow: hidden;
34
+ color: var(--ink);
35
+ font-family: "Film Sans", ui-sans-serif, system-ui, sans-serif;
36
+ font-kerning: normal;
37
+ -webkit-font-smoothing: antialiased;
38
+ letter-spacing: -0.01em;
39
+ }
40
+
41
+ .bg {
42
+ position: absolute;
43
+ inset: 0;
44
+ }
45
+
46
+ .bg-night {
47
+ background:
48
+ radial-gradient(120% 90% at 50% 0%, rgb(255 255 255 / 0.06), transparent 60%),
49
+ #111214;
50
+ }
51
+
52
+ .bg-paper {
53
+ background:
54
+ radial-gradient(110% 80% at 50% 10%, #ffffff, transparent 70%),
55
+ var(--paper);
56
+ visibility: hidden;
57
+ }
58
+
59
+ .scene {
60
+ position: absolute;
61
+ inset: 0;
62
+ visibility: hidden;
63
+ }
64
+
65
+ /* Kinetic type: each word rises inside its own mask */
66
+ .kin {
67
+ visibility: hidden;
68
+ }
69
+
70
+ .kin .w {
71
+ display: inline-block;
72
+ overflow: hidden;
73
+ padding: 0.08em 0.02em 0.26em;
74
+ margin: -0.08em -0.02em -0.26em;
75
+ vertical-align: top;
76
+ }
77
+
78
+ .kin .wi {
79
+ display: inline-block;
80
+ will-change: transform;
81
+ }
82
+
83
+ /* 1. Cold open */
84
+ .open-src {
85
+ display: none;
86
+ }
87
+
88
+ .open-grid {
89
+ position: absolute;
90
+ inset: 0;
91
+ perspective: calc(var(--u) * 1800px);
92
+ }
93
+
94
+ .open-tile {
95
+ position: absolute;
96
+ width: calc(var(--u) * 520px);
97
+ will-change: transform, opacity;
98
+ }
99
+
100
+ .open-scrim {
101
+ position: absolute;
102
+ inset: 0;
103
+ background: radial-gradient(70% 60% at 50% 50%, rgb(17 18 20 / 0.92), rgb(17 18 20 / 0.55));
104
+ opacity: 0;
105
+ }
106
+
107
+ .open-lines {
108
+ position: absolute;
109
+ inset: 0;
110
+ display: grid;
111
+ place-items: center;
112
+ padding: 0 calc(var(--u) * 120px);
113
+ }
114
+
115
+ .open-line {
116
+ grid-area: 1 / 1;
117
+ margin: 0;
118
+ max-width: calc(var(--u) * 1400px);
119
+ font-size: calc(var(--u) * 92px);
120
+ font-weight: 700;
121
+ line-height: 1.04;
122
+ text-align: center;
123
+ letter-spacing: -0.035em;
124
+ }
125
+
126
+ /* 2. Title */
127
+ #s-title {
128
+ display: grid;
129
+ place-content: center;
130
+ justify-items: center;
131
+ gap: calc(var(--u) * 34px);
132
+ text-align: center;
133
+ }
134
+
135
+ .title-word {
136
+ margin: 0;
137
+ font-size: calc(var(--u) * 210px);
138
+ font-weight: 700;
139
+ line-height: 0.95;
140
+ letter-spacing: -0.05em;
141
+ }
142
+
143
+ .title-promise {
144
+ margin: 0;
145
+ max-width: calc(var(--u) * 1100px);
146
+ padding: 0 calc(var(--u) * 80px);
147
+ font-size: calc(var(--u) * 46px);
148
+ line-height: 1.2;
149
+ color: var(--ink-2);
150
+ }
151
+
152
+ /* The mark: a stroked outline that later becomes the product window edge */
153
+ .mark {
154
+ position: absolute;
155
+ visibility: hidden;
156
+ pointer-events: none;
157
+ }
158
+
159
+ .mark-svg {
160
+ position: absolute;
161
+ inset: 0;
162
+ width: 100%;
163
+ height: 100%;
164
+ overflow: visible;
165
+ }
166
+
167
+ .mark-svg rect {
168
+ fill: none;
169
+ stroke: var(--accent);
170
+ stroke-linecap: round;
171
+ stroke-dasharray: 1 1;
172
+ }
173
+
174
+ /* 3. Product walk */
175
+ .walk-copy {
176
+ position: absolute;
177
+ }
178
+
179
+ .walk-step {
180
+ position: absolute;
181
+ inset: 0 auto auto 0;
182
+ width: 100%;
183
+ }
184
+
185
+ .walk-num {
186
+ display: block;
187
+ margin-bottom: calc(var(--u) * 22px);
188
+ font-size: calc(var(--u) * 26px);
189
+ font-weight: 700;
190
+ color: var(--accent);
191
+ letter-spacing: 0.02em;
192
+ font-variant-numeric: tabular-nums;
193
+ }
194
+
195
+ .walk-h {
196
+ margin: 0 0 calc(var(--u) * 22px);
197
+ font-size: calc(var(--u) * 88px);
198
+ font-weight: 700;
199
+ line-height: 1;
200
+ color: var(--paper-ink);
201
+ letter-spacing: -0.045em;
202
+ }
203
+
204
+ .walk-p {
205
+ margin: 0;
206
+ font-size: calc(var(--u) * 36px);
207
+ line-height: 1.3;
208
+ color: var(--paper-ink-2);
209
+ }
210
+
211
+ .walk-dots {
212
+ position: absolute;
213
+ display: flex;
214
+ gap: calc(var(--u) * 10px);
215
+ }
216
+
217
+ .walk-dot {
218
+ height: calc(var(--u) * 6px);
219
+ width: calc(var(--u) * 26px);
220
+ border-radius: 99px;
221
+ background: rgb(22 23 26 / 0.14);
222
+ overflow: hidden;
223
+ }
224
+
225
+ .walk-dot i {
226
+ display: block;
227
+ height: 100%;
228
+ width: 100%;
229
+ background: var(--accent);
230
+ transform-origin: 0 50%;
231
+ transform: scaleX(0);
232
+ }
233
+
234
+ .win {
235
+ position: absolute;
236
+ overflow: hidden;
237
+ border-radius: calc(var(--u) * 18px);
238
+ background: #fff;
239
+ box-shadow:
240
+ 0 calc(var(--u) * 50px) calc(var(--u) * 120px) rgb(20 22 30 / 0.22),
241
+ 0 calc(var(--u) * 8px) calc(var(--u) * 24px) rgb(20 22 30 / 0.1),
242
+ 0 0 0 1px rgb(20 22 30 / 0.08);
243
+ will-change: transform;
244
+ }
245
+
246
+ .win-surface {
247
+ position: absolute;
248
+ left: 0;
249
+ top: 0;
250
+ display: flex;
251
+ flex-direction: column;
252
+ width: 1280px;
253
+ transform-origin: 0 0;
254
+ will-change: transform;
255
+ }
256
+
257
+ /* film.js sets the surface's min-height to the window at rest, so the mockup fills it. */
258
+ .win-surface > * {
259
+ flex: 1 0 auto;
260
+ }
261
+
262
+ .win-cursor {
263
+ position: absolute;
264
+ left: 0;
265
+ top: 0;
266
+ width: 28px;
267
+ height: 28px;
268
+ transform-origin: 5px 3px;
269
+ visibility: hidden;
270
+ z-index: 3;
271
+ }
272
+
273
+ .win-cursor svg {
274
+ display: block;
275
+ filter: drop-shadow(0 2px 3px rgb(0 0 0 / 0.3));
276
+ }
277
+
278
+ .win-cursor path {
279
+ fill: #111;
280
+ stroke: #fff;
281
+ stroke-width: 1.6;
282
+ stroke-linejoin: round;
283
+ }
284
+
285
+ .win-ripple {
286
+ position: absolute;
287
+ left: -13px;
288
+ top: -15px;
289
+ width: 36px;
290
+ height: 36px;
291
+ border-radius: 50%;
292
+ border: 2px solid var(--accent);
293
+ opacity: 0;
294
+ }
295
+
296
+ .win .mark {
297
+ z-index: 2;
298
+ }
299
+
300
+ .hl-fill {
301
+ position: absolute;
302
+ inset: 0;
303
+ }
304
+
305
+ /* 4. Proof */
306
+ #s-proof {
307
+ display: grid;
308
+ place-content: center;
309
+ justify-items: center;
310
+ gap: calc(var(--u) * 60px);
311
+ color: var(--paper-ink);
312
+ }
313
+
314
+ .proof-row {
315
+ display: flex;
316
+ gap: calc(var(--u) * 110px);
317
+ }
318
+
319
+ [data-aspect="9:16"] .proof-row {
320
+ flex-direction: column;
321
+ gap: calc(var(--u) * 70px);
322
+ }
323
+
324
+ .proof-item {
325
+ text-align: center;
326
+ }
327
+
328
+ .proof-value {
329
+ display: block;
330
+ font-size: calc(var(--u) * 190px);
331
+ font-weight: 700;
332
+ line-height: 1;
333
+ letter-spacing: -0.05em;
334
+ font-variant-numeric: tabular-nums;
335
+ }
336
+
337
+ .proof-label {
338
+ display: block;
339
+ max-width: calc(var(--u) * 520px);
340
+ margin-top: calc(var(--u) * 18px);
341
+ font-size: calc(var(--u) * 32px);
342
+ line-height: 1.25;
343
+ color: var(--paper-ink-2);
344
+ }
345
+
346
+ .proof-caption {
347
+ margin: 0;
348
+ font-size: calc(var(--u) * 26px);
349
+ color: var(--paper-ink-2);
350
+ }
351
+
352
+ /* 5. Limits */
353
+ #s-limits {
354
+ display: grid;
355
+ place-items: center;
356
+ }
357
+
358
+ .limits-card {
359
+ max-width: calc(var(--u) * 1180px);
360
+ margin: 0 calc(var(--u) * 80px);
361
+ padding: calc(var(--u) * 70px) calc(var(--u) * 80px);
362
+ border-radius: calc(var(--u) * 28px);
363
+ background: rgb(255 255 255 / 0.04);
364
+ box-shadow: inset 0 0 0 1px var(--line);
365
+ }
366
+
367
+ .limits-h {
368
+ margin: 0 0 calc(var(--u) * 26px);
369
+ font-size: calc(var(--u) * 30px);
370
+ font-weight: 700;
371
+ color: var(--accent);
372
+ letter-spacing: 0.01em;
373
+ }
374
+
375
+ .limits-p {
376
+ margin: 0;
377
+ font-size: calc(var(--u) * 64px);
378
+ font-weight: 700;
379
+ line-height: 1.12;
380
+ letter-spacing: -0.035em;
381
+ }
382
+
383
+ /* 6. End card */
384
+ #s-end {
385
+ display: grid;
386
+ place-content: center;
387
+ justify-items: center;
388
+ gap: calc(var(--u) * 26px);
389
+ text-align: center;
390
+ }
391
+
392
+ .end-word {
393
+ margin: 0;
394
+ font-size: calc(var(--u) * 170px);
395
+ font-weight: 700;
396
+ line-height: 0.95;
397
+ letter-spacing: -0.05em;
398
+ }
399
+
400
+ .end-url {
401
+ margin: 0;
402
+ font-size: calc(var(--u) * 50px);
403
+ color: var(--accent);
404
+ }
405
+
406
+ .end-line {
407
+ margin: 0;
408
+ font-size: calc(var(--u) * 34px);
409
+ color: var(--ink-2);
410
+ }
411
+
412
+ /* Film grain: a fixed noise tile, so it never flickers between renders */
413
+ .grain {
414
+ position: absolute;
415
+ inset: 0;
416
+ pointer-events: none;
417
+ opacity: 0.06;
418
+ mix-blend-mode: overlay;
419
+ background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' seed='7' stitchTiles='stitch'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");
420
+ }
421
+
422
+ /* Placeholder product mockup (mockups.tsx). Replace with your own. */
423
+ .fm-browser {
424
+ display: flex;
425
+ flex-direction: column;
426
+ width: 1280px;
427
+ font-family: "Film Sans", ui-sans-serif, system-ui, sans-serif;
428
+ color: #16171a;
429
+ background: #fff;
430
+ letter-spacing: 0;
431
+ }
432
+
433
+ .fm-chrome {
434
+ display: flex;
435
+ align-items: center;
436
+ gap: 16px;
437
+ height: 52px;
438
+ padding: 0 18px;
439
+ background: #f1f0ec;
440
+ border-bottom: 1px solid #e2e0da;
441
+ }
442
+
443
+ .fm-dots {
444
+ display: flex;
445
+ gap: 8px;
446
+ }
447
+
448
+ .fm-dots i {
449
+ width: 12px;
450
+ height: 12px;
451
+ border-radius: 50%;
452
+ background: #d6d3cc;
453
+ }
454
+
455
+ .fm-url {
456
+ flex: 1;
457
+ max-width: 520px;
458
+ margin: 0 auto;
459
+ padding: 7px 14px;
460
+ border-radius: 8px;
461
+ background: #fff;
462
+ font-size: 15px;
463
+ color: #5d5f66;
464
+ text-align: center;
465
+ }
466
+
467
+ .fm-page {
468
+ flex: 1 0 auto;
469
+ display: grid;
470
+ grid-template-columns: 200px 1fr 440px;
471
+ min-height: 860px;
472
+ }
473
+
474
+ .fm-side {
475
+ display: flex;
476
+ flex-direction: column;
477
+ gap: 14px;
478
+ padding: 28px 22px;
479
+ border-right: 1px solid #eceae4;
480
+ font-size: 16px;
481
+ color: #5d5f66;
482
+ }
483
+
484
+ .fm-side b {
485
+ margin-bottom: 8px;
486
+ color: #16171a;
487
+ font-size: 18px;
488
+ }
489
+
490
+ .fm-side-on {
491
+ color: #16171a;
492
+ font-weight: 700;
493
+ }
494
+
495
+ .fm-list {
496
+ padding: 28px 30px;
497
+ }
498
+
499
+ .fm-h {
500
+ margin: 0 0 20px;
501
+ font-size: 24px;
502
+ letter-spacing: -0.01em;
503
+ }
504
+
505
+ .fm-list ol {
506
+ margin: 0;
507
+ padding: 0;
508
+ list-style: none;
509
+ }
510
+
511
+ .fm-row {
512
+ display: flex;
513
+ align-items: center;
514
+ gap: 14px;
515
+ padding: 16px 12px;
516
+ border-bottom: 1px solid #eceae4;
517
+ font-size: 17px;
518
+ }
519
+
520
+ .fm-row[data-film-state="done"] .fm-row-text {
521
+ color: #8a8c93;
522
+ text-decoration: line-through;
523
+ }
524
+
525
+ .fm-row-text {
526
+ flex: 1;
527
+ }
528
+
529
+ .fm-row-meta {
530
+ font-size: 14px;
531
+ color: #8a8c93;
532
+ }
533
+
534
+ .fm-avatar {
535
+ display: grid;
536
+ place-items: center;
537
+ width: 34px;
538
+ height: 34px;
539
+ flex: none;
540
+ border-radius: 50%;
541
+ background: #e8e6f5;
542
+ color: #3d3a7a;
543
+ font-size: 14px;
544
+ font-weight: 700;
545
+ }
546
+
547
+ .fm-detail {
548
+ padding: 28px 30px;
549
+ border-left: 1px solid #eceae4;
550
+ background: #fbfaf7;
551
+ }
552
+
553
+ .fm-eyebrow {
554
+ margin: 0 0 10px;
555
+ font-size: 14px;
556
+ color: #8a8c93;
557
+ }
558
+
559
+ .fm-body {
560
+ margin: 0 0 26px;
561
+ font-size: 17px;
562
+ line-height: 1.5;
563
+ color: #3b3d44;
564
+ }
565
+
566
+ .fm-button {
567
+ padding: 12px 20px;
568
+ border: 0;
569
+ border-radius: 10px;
570
+ background: var(--accent);
571
+ color: #fff;
572
+ font: inherit;
573
+ font-size: 16px;
574
+ font-weight: 700;
575
+ }
576
+
577
+ .fm-card {
578
+ display: flex;
579
+ gap: 14px;
580
+ padding: 22px 24px;
581
+ border-radius: 16px;
582
+ background: #1c1d21;
583
+ box-shadow: 0 0 0 1px rgb(255 255 255 / 0.07), 0 24px 50px rgb(0 0 0 / 0.4);
584
+ color: #f3f1ec;
585
+ font-size: 19px;
586
+ line-height: 1.35;
587
+ letter-spacing: 0;
588
+ }
589
+
590
+ .fm-card p {
591
+ margin: 4px 0 0;
592
+ color: rgb(243 241 236 / 0.7);
593
+ }
@@ -0,0 +1,85 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width={{CANVAS_WIDTH}}, initial-scale=1">
6
+ <title>{{TITLE}}</title>
7
+ <style>:root { --w: {{WIDTH}}px; --h: {{HEIGHT}}px; --cw: {{CANVAS_WIDTH}}px; --ch: {{CANVAS_HEIGHT}}px; --zoom: {{ZOOM}}; --u: {{UNIT}}; --accent: {{ACCENT}}; }</style>
8
+ <style>{{KIT_CSS}}</style>
9
+ <style>{{PRODUCT_CSS}}</style>
10
+ <style>{{FILM_CSS}}</style>
11
+ </head>
12
+ <body>
13
+ <div id="film" class="film" data-aspect="{{ASPECT}}">
14
+ <div class="bg bg-night" id="bg-night"></div>
15
+ <div class="bg bg-paper" id="bg-paper"></div>
16
+
17
+ <!-- 1. Cold open: a collage of the problem, then two lines -->
18
+ <section class="scene" id="s-open">
19
+ <div class="open-src" id="open-src" aria-hidden="true">{{OPEN_CARDS}}</div>
20
+ <div class="open-grid" id="open-grid"></div>
21
+ <div class="open-scrim" id="open-scrim"></div>
22
+ <div class="open-lines">
23
+ <h2 class="kin open-line" data-copy="open.0"></h2>
24
+ <h2 class="kin open-line" data-copy="open.1"></h2>
25
+ </div>
26
+ </section>
27
+
28
+ <!-- 2. Title -->
29
+ <section class="scene" id="s-title">
30
+ <h1 class="kin title-word" id="title-word" data-copy="name"></h1>
31
+ <p class="kin title-promise" id="title-promise" data-copy="promise"></p>
32
+ </section>
33
+
34
+ <!-- The mark draws at the title, then opens into the product window -->
35
+ <div class="mark" id="mark">
36
+ <svg class="mark-svg" id="mark-svg" viewBox="0 0 120 120" preserveAspectRatio="none" aria-hidden="true">
37
+ <rect id="mark-rect" pathLength="1"></rect>
38
+ </svg>
39
+ </div>
40
+
41
+ <!-- 3. Product walk: copy beside a framed product with camera, cursor and highlight -->
42
+ <section class="scene" id="s-walk">
43
+ <div class="walk-copy" id="walk-copy"></div>
44
+ <div class="walk-dots" id="walk-dots"></div>
45
+ <div class="win" id="win">
46
+ <div class="win-surface" id="win-surface">{{PRODUCT}}</div>
47
+ <div class="mark hl" id="hl">
48
+ <svg class="mark-svg" id="hl-svg" viewBox="0 0 120 120" preserveAspectRatio="none" aria-hidden="true">
49
+ <rect id="hl-rect" pathLength="1"></rect>
50
+ </svg>
51
+ </div>
52
+ <div class="win-cursor" id="win-cursor" aria-hidden="true">
53
+ <svg viewBox="0 0 28 28" width="28" height="28"><path d="M5 3 L5 22 L10 17.5 L13.6 25 L17 23.4 L13.5 16.2 L20 16 Z"></path></svg>
54
+ <span class="win-ripple" id="win-ripple"></span>
55
+ </div>
56
+ </div>
57
+ </section>
58
+
59
+ <!-- 4. Proof: numbers from the facts file -->
60
+ <section class="scene" id="s-proof">
61
+ <div class="proof-row" id="proof-row"></div>
62
+ <p class="kin proof-caption" data-copy="proof.caption"></p>
63
+ </section>
64
+
65
+ <!-- 5. Limits: what it does not do -->
66
+ <section class="scene" id="s-limits">
67
+ <div class="limits-card" id="limits-card">
68
+ <h2 class="kin limits-h" data-copy="limits.heading"></h2>
69
+ <p class="kin limits-p" data-copy="limits.body"></p>
70
+ </div>
71
+ </section>
72
+
73
+ <!-- 6. End card -->
74
+ <section class="scene" id="s-end">
75
+ <h2 class="kin end-word" id="end-word" data-copy="name"></h2>
76
+ <p class="kin end-url" data-copy="url"></p>
77
+ <p class="kin end-line" data-copy="end.line"></p>
78
+ </section>
79
+
80
+ <div class="grain" aria-hidden="true"></div>
81
+ </div>
82
+ <script>window.__FILM__ = {{CONFIG}};</script>
83
+ <script>{{FILM_JS}}</script>
84
+ </body>
85
+ </html>