lanre-ds 0.0.0-stage → 0.2.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 (240) hide show
  1. package/LICENSE +21 -0
  2. package/components/buttons/Button.d.ts +28 -0
  3. package/components/buttons/Button.jsx +50 -0
  4. package/components/buttons/Button.prompt.md +16 -0
  5. package/components/buttons/IconButton.d.ts +23 -0
  6. package/components/buttons/IconButton.jsx +53 -0
  7. package/components/buttons/IconButton.prompt.md +12 -0
  8. package/components/buttons/SegmentedTabs.d.ts +21 -0
  9. package/components/buttons/SegmentedTabs.jsx +15 -0
  10. package/components/buttons/SegmentedTabs.prompt.md +10 -0
  11. package/components/contributor/AudioRecorder.d.ts +27 -0
  12. package/components/contributor/AudioRecorder.jsx +79 -0
  13. package/components/contributor/AudioRecorder.prompt.md +13 -0
  14. package/components/contributor/RadioCard.d.ts +17 -0
  15. package/components/contributor/RadioCard.jsx +30 -0
  16. package/components/contributor/RadioCard.prompt.md +12 -0
  17. package/components/contributor/RecorderPanel.d.ts +30 -0
  18. package/components/contributor/RecorderPanel.jsx +49 -0
  19. package/components/contributor/RecorderPanel.prompt.md +11 -0
  20. package/components/contributor/StepBar.d.ts +17 -0
  21. package/components/contributor/StepBar.jsx +55 -0
  22. package/components/contributor/StepBar.prompt.md +12 -0
  23. package/components/contributor/TextField.d.ts +25 -0
  24. package/components/contributor/TextField.jsx +29 -0
  25. package/components/contributor/TextField.prompt.md +7 -0
  26. package/components/contributor/UploadTile.d.ts +39 -0
  27. package/components/contributor/UploadTile.jsx +116 -0
  28. package/components/contributor/UploadTile.prompt.md +21 -0
  29. package/components/contributor/WorldChip.d.ts +18 -0
  30. package/components/contributor/WorldChip.jsx +16 -0
  31. package/components/contributor/WorldChip.prompt.md +9 -0
  32. package/components/contributor/WorldOption.d.ts +19 -0
  33. package/components/contributor/WorldOption.jsx +19 -0
  34. package/components/contributor/WorldOption.prompt.md +11 -0
  35. package/components/contributor/WorldSign.d.ts +25 -0
  36. package/components/contributor/WorldSign.jsx +32 -0
  37. package/components/contributor/WorldSign.prompt.md +14 -0
  38. package/components/contributor/WorldSigns.d.ts +13 -0
  39. package/components/contributor/WorldSigns.jsx +12 -0
  40. package/components/contributor/WorldSigns.prompt.md +8 -0
  41. package/components/effects/Confetti.d.ts +14 -0
  42. package/components/effects/Confetti.jsx +38 -0
  43. package/components/effects/Confetti.prompt.md +12 -0
  44. package/components/effects/Sparkles.d.ts +9 -0
  45. package/components/effects/Sparkles.jsx +13 -0
  46. package/components/effects/Sparkles.prompt.md +11 -0
  47. package/components/effects/Sunburst.d.ts +23 -0
  48. package/components/effects/Sunburst.jsx +26 -0
  49. package/components/effects/Sunburst.prompt.md +12 -0
  50. package/components/feedback/Badge.d.ts +17 -0
  51. package/components/feedback/Badge.jsx +16 -0
  52. package/components/feedback/Badge.prompt.md +10 -0
  53. package/components/feedback/ErrorText.d.ts +11 -0
  54. package/components/feedback/ErrorText.jsx +8 -0
  55. package/components/feedback/ErrorText.prompt.md +9 -0
  56. package/components/feedback/LoadingScreen.d.ts +14 -0
  57. package/components/feedback/LoadingScreen.jsx +24 -0
  58. package/components/feedback/LoadingScreen.prompt.md +9 -0
  59. package/components/feedback/Notice.d.ts +15 -0
  60. package/components/feedback/Notice.jsx +16 -0
  61. package/components/feedback/Notice.prompt.md +10 -0
  62. package/components/feedback/SaveAlert.d.ts +15 -0
  63. package/components/feedback/SaveAlert.jsx +24 -0
  64. package/components/feedback/SaveAlert.prompt.md +9 -0
  65. package/components/feedback/SavePill.d.ts +13 -0
  66. package/components/feedback/SavePill.jsx +29 -0
  67. package/components/feedback/SavePill.prompt.md +8 -0
  68. package/components/feedback/SoundToggle.d.ts +16 -0
  69. package/components/feedback/SoundToggle.jsx +15 -0
  70. package/components/feedback/SoundToggle.prompt.md +6 -0
  71. package/components/feedback/Sticker.d.ts +15 -0
  72. package/components/feedback/Sticker.jsx +12 -0
  73. package/components/feedback/Sticker.prompt.md +9 -0
  74. package/components/feedback/Tag.d.ts +15 -0
  75. package/components/feedback/Tag.jsx +15 -0
  76. package/components/feedback/Tag.prompt.md +10 -0
  77. package/components/feedback/Toast.d.ts +21 -0
  78. package/components/feedback/Toast.jsx +19 -0
  79. package/components/feedback/Toast.prompt.md +7 -0
  80. package/components/forms/Slider.d.ts +24 -0
  81. package/components/forms/Slider.jsx +28 -0
  82. package/components/forms/Slider.prompt.md +6 -0
  83. package/components/forms/Switch.d.ts +19 -0
  84. package/components/forms/Switch.jsx +20 -0
  85. package/components/forms/Switch.prompt.md +6 -0
  86. package/components/games/MatchCard.d.ts +18 -0
  87. package/components/games/MatchCard.jsx +27 -0
  88. package/components/games/MatchCard.prompt.md +11 -0
  89. package/components/hud/Countdown.d.ts +33 -0
  90. package/components/hud/Countdown.jsx +74 -0
  91. package/components/hud/Countdown.prompt.md +15 -0
  92. package/components/hud/CurrencyPill.d.ts +24 -0
  93. package/components/hud/CurrencyPill.jsx +34 -0
  94. package/components/hud/CurrencyPill.prompt.md +12 -0
  95. package/components/hud/HudBar.d.ts +18 -0
  96. package/components/hud/HudBar.jsx +14 -0
  97. package/components/hud/HudBar.prompt.md +15 -0
  98. package/components/hud/HudChip.d.ts +25 -0
  99. package/components/hud/HudChip.jsx +30 -0
  100. package/components/hud/HudChip.prompt.md +13 -0
  101. package/components/hud/ProgressBar.d.ts +27 -0
  102. package/components/hud/ProgressBar.jsx +39 -0
  103. package/components/hud/ProgressBar.prompt.md +11 -0
  104. package/components/hud/TimerChip.d.ts +19 -0
  105. package/components/hud/TimerChip.jsx +17 -0
  106. package/components/hud/TimerChip.prompt.md +7 -0
  107. package/components/icons/GameIcon.d.ts +25 -0
  108. package/components/icons/GameIcon.jsx +43 -0
  109. package/components/icons/GameIcon.prompt.md +13 -0
  110. package/components/identity/Avatar.d.ts +26 -0
  111. package/components/identity/Avatar.jsx +31 -0
  112. package/components/identity/Avatar.prompt.md +11 -0
  113. package/components/identity/AvatarButton.d.ts +19 -0
  114. package/components/identity/AvatarButton.jsx +13 -0
  115. package/components/identity/AvatarButton.prompt.md +8 -0
  116. package/components/identity/Gemstone.d.ts +28 -0
  117. package/components/identity/Gemstone.jsx +46 -0
  118. package/components/identity/Gemstone.prompt.md +15 -0
  119. package/components/identity/PersonTile.d.ts +22 -0
  120. package/components/identity/PersonTile.jsx +27 -0
  121. package/components/identity/PersonTile.prompt.md +10 -0
  122. package/components/levels/ChoiceButton.d.ts +20 -0
  123. package/components/levels/ChoiceButton.jsx +31 -0
  124. package/components/levels/ChoiceButton.prompt.md +10 -0
  125. package/components/map/DiscoveryMarker.d.ts +19 -0
  126. package/components/map/DiscoveryMarker.jsx +31 -0
  127. package/components/map/DiscoveryMarker.prompt.md +11 -0
  128. package/components/map/LevelNode.d.ts +19 -0
  129. package/components/map/LevelNode.jsx +37 -0
  130. package/components/map/LevelNode.prompt.md +11 -0
  131. package/components/map/PortraitMedallion.d.ts +27 -0
  132. package/components/map/PortraitMedallion.jsx +29 -0
  133. package/components/map/PortraitMedallion.prompt.md +14 -0
  134. package/components/map/StarRating.d.ts +22 -0
  135. package/components/map/StarRating.jsx +21 -0
  136. package/components/map/StarRating.prompt.md +6 -0
  137. package/components/map/Tile.d.ts +22 -0
  138. package/components/map/Tile.jsx +32 -0
  139. package/components/map/Tile.prompt.md +8 -0
  140. package/components/map/WorldGate.d.ts +21 -0
  141. package/components/map/WorldGate.jsx +27 -0
  142. package/components/map/WorldGate.prompt.md +13 -0
  143. package/components/map/worlds.d.ts +18 -0
  144. package/components/map/worlds.js +9 -0
  145. package/components/media/Album.d.ts +33 -0
  146. package/components/media/Album.jsx +157 -0
  147. package/components/media/Album.prompt.md +16 -0
  148. package/components/media/Cassette.d.ts +23 -0
  149. package/components/media/Cassette.jsx +64 -0
  150. package/components/media/Cassette.prompt.md +13 -0
  151. package/components/media/Film.d.ts +20 -0
  152. package/components/media/Film.jsx +39 -0
  153. package/components/media/Film.prompt.md +14 -0
  154. package/components/media/MemoryText.d.ts +20 -0
  155. package/components/media/MemoryText.jsx +14 -0
  156. package/components/media/MemoryText.prompt.md +9 -0
  157. package/components/media/Unavailable.d.ts +13 -0
  158. package/components/media/Unavailable.jsx +13 -0
  159. package/components/media/Unavailable.prompt.md +10 -0
  160. package/components/media/Words.d.ts +21 -0
  161. package/components/media/Words.jsx +57 -0
  162. package/components/media/Words.prompt.md +12 -0
  163. package/components/media/shared.js +19 -0
  164. package/components/panels/Modal.d.ts +25 -0
  165. package/components/panels/Modal.jsx +68 -0
  166. package/components/panels/Modal.prompt.md +20 -0
  167. package/components/panels/Panel.d.ts +29 -0
  168. package/components/panels/Panel.jsx +109 -0
  169. package/components/panels/Panel.prompt.md +13 -0
  170. package/components/quiz/LeaderboardRow.d.ts +26 -0
  171. package/components/quiz/LeaderboardRow.jsx +24 -0
  172. package/components/quiz/LeaderboardRow.prompt.md +10 -0
  173. package/components/rewards/ItemCell.d.ts +24 -0
  174. package/components/rewards/ItemCell.jsx +24 -0
  175. package/components/rewards/ItemCell.prompt.md +9 -0
  176. package/components/rewards/RewardCard.d.ts +24 -0
  177. package/components/rewards/RewardCard.jsx +34 -0
  178. package/components/rewards/RewardCard.prompt.md +8 -0
  179. package/components/rewards/ShopRow.d.ts +23 -0
  180. package/components/rewards/ShopRow.jsx +26 -0
  181. package/components/rewards/ShopRow.prompt.md +7 -0
  182. package/components/text/Heading.d.ts +19 -0
  183. package/components/text/Heading.jsx +15 -0
  184. package/components/text/Heading.prompt.md +10 -0
  185. package/components/text/OutlineText.d.ts +24 -0
  186. package/components/text/OutlineText.jsx +20 -0
  187. package/components/text/OutlineText.prompt.md +11 -0
  188. package/components/toolbar/FastTravelDock.d.ts +50 -0
  189. package/components/toolbar/FastTravelDock.jsx +36 -0
  190. package/components/toolbar/FastTravelDock.prompt.md +12 -0
  191. package/components/toolbar/Toolbar.d.ts +16 -0
  192. package/components/toolbar/Toolbar.jsx +8 -0
  193. package/components/toolbar/Toolbar.prompt.md +14 -0
  194. package/components/toolbar/ToolbarNext.d.ts +21 -0
  195. package/components/toolbar/ToolbarNext.jsx +21 -0
  196. package/components/toolbar/ToolbarNext.prompt.md +13 -0
  197. package/components/toolbar/ToolbarTab.d.ts +15 -0
  198. package/components/toolbar/ToolbarTab.jsx +12 -0
  199. package/components/toolbar/ToolbarTab.prompt.md +9 -0
  200. package/components/viewer/ClueCard.d.ts +18 -0
  201. package/components/viewer/ClueCard.jsx +14 -0
  202. package/components/viewer/ClueCard.prompt.md +9 -0
  203. package/components/viewer/ClueTab.d.ts +16 -0
  204. package/components/viewer/ClueTab.jsx +12 -0
  205. package/components/viewer/ClueTab.prompt.md +8 -0
  206. package/components/viewer/RevealTray.d.ts +54 -0
  207. package/components/viewer/RevealTray.jsx +58 -0
  208. package/components/viewer/RevealTray.prompt.md +21 -0
  209. package/components/viewer/RoadsideViewer.d.ts +60 -0
  210. package/components/viewer/RoadsideViewer.jsx +96 -0
  211. package/components/viewer/RoadsideViewer.prompt.md +20 -0
  212. package/components/viewer/ViewerKeys.d.ts +26 -0
  213. package/components/viewer/ViewerKeys.jsx +22 -0
  214. package/components/viewer/ViewerKeys.prompt.md +14 -0
  215. package/components/viewer/ViewerKnob.d.ts +20 -0
  216. package/components/viewer/ViewerKnob.jsx +12 -0
  217. package/components/viewer/ViewerKnob.prompt.md +11 -0
  218. package/components/viewer/WhoButton.d.ts +21 -0
  219. package/components/viewer/WhoButton.jsx +15 -0
  220. package/components/viewer/WhoButton.prompt.md +10 -0
  221. package/package.json +62 -4
  222. package/readme.md +145 -0
  223. package/styles.css +15 -0
  224. package/tokens/colors.css +140 -0
  225. package/tokens/effects.css +40 -0
  226. package/tokens/fonts.css +3 -0
  227. package/tokens/fx.css +186 -0
  228. package/tokens/gems.css +8 -0
  229. package/tokens/hud.css +510 -0
  230. package/tokens/icons.css +7 -0
  231. package/tokens/media.css +774 -0
  232. package/tokens/signs.css +459 -0
  233. package/tokens/spacing.css +38 -0
  234. package/tokens/typography.css +34 -0
  235. package/tokens/ui.css +432 -0
  236. package/tokens/utilities.css +34 -0
  237. package/tokens/viewer.css +1176 -0
  238. package/tokens/worlds.css +73 -0
  239. package/tools/ds-loader.js +60 -0
  240. package/README.md +0 -3
@@ -0,0 +1,774 @@
1
+ /* Memory media: what plays inside a viewer screen. Words, a photo album with a full-screen lightbox, a cassette for a
2
+ voice note, a film strip for video, an "unavailable" card, and the shimmer that holds a photo's place while it loads.
3
+ Used by the Words, Album, Cassette, Film and Unavailable components (components/media). Each piece fills the box it
4
+ sits in, and the box is a size container, so photos and video fit themselves to it with cq units. */
5
+
6
+ .mp-unavailable {
7
+ display: flex;
8
+ align-items: center;
9
+ justify-content: center;
10
+ flex-wrap: wrap;
11
+ gap: 12px;
12
+ margin: 16px;
13
+ padding: 18px;
14
+ border-radius: var(--radius-lg);
15
+ background: var(--cream-200);
16
+ font-weight: 700;
17
+ color: var(--ink-700);
18
+ }
19
+
20
+ .mp-unavailable i {
21
+ font-size: 28px;
22
+ color: var(--ink-500);
23
+ }
24
+
25
+ /* ------------------------------------------------------------------ words */
26
+
27
+ .mp-words {
28
+ width: 100%;
29
+ height: 100%;
30
+ box-sizing: border-box;
31
+ overflow-y: auto;
32
+ overscroll-behavior: contain;
33
+ -webkit-overflow-scrolling: touch;
34
+ padding: clamp(18px, 4vw, 40px);
35
+ display: grid;
36
+ align-content: safe center;
37
+ justify-items: center;
38
+ outline: none;
39
+ }
40
+
41
+ .mp-words:focus-visible {
42
+ box-shadow: inset 0 0 0 4px var(--blue-500);
43
+ }
44
+
45
+ .mp-words-inner {
46
+ max-width: 62ch;
47
+ display: grid;
48
+ gap: 0.9em;
49
+ font-weight: 600;
50
+ font-size: clamp(17px, 2.1vw, 22px);
51
+ line-height: 1.6;
52
+ color: var(--ink-900);
53
+ text-wrap: pretty;
54
+ overflow-wrap: anywhere;
55
+ }
56
+
57
+ .mp-words-inner p {
58
+ margin: 0;
59
+ white-space: pre-line;
60
+ }
61
+
62
+ .mp-words-short .mp-words-inner {
63
+ font-size: clamp(22px, 3.4vw, 34px);
64
+ line-height: 1.4;
65
+ text-align: center;
66
+ text-wrap: balance;
67
+ }
68
+
69
+ .mp-words-unwritten {
70
+ opacity: 0;
71
+ }
72
+
73
+ /* The cursor takes no room, so the words after it stay exactly where they will land. */
74
+ .mp-words-cursor {
75
+ display: inline-block;
76
+ width: 3px;
77
+ height: 0.9em;
78
+ margin: 0 -6px 0 3px;
79
+ vertical-align: -0.12em;
80
+ border-radius: 2px;
81
+ background: var(--gold-700);
82
+ animation: mp-blink 1s steps(2) infinite;
83
+ }
84
+
85
+ @keyframes mp-blink {
86
+ 50% { opacity: 0; }
87
+ }
88
+
89
+ .mp-words-more {
90
+ position: absolute;
91
+ left: 0;
92
+ right: 0;
93
+ bottom: 0;
94
+ height: 64px;
95
+ display: grid;
96
+ place-items: end center;
97
+ padding-bottom: 8px;
98
+ pointer-events: none;
99
+ background: linear-gradient(180deg, transparent, var(--cream-50) 70%);
100
+ font-weight: 700;
101
+ font-size: 12px;
102
+ letter-spacing: 0.1em;
103
+ text-transform: uppercase;
104
+ color: var(--ink-500);
105
+ }
106
+
107
+ /* ------------------------------------------------------------------ photos */
108
+
109
+ .mp-album {
110
+ position: absolute;
111
+ inset: 0;
112
+ display: grid;
113
+ grid-template-columns: minmax(0, 1fr);
114
+ grid-template-rows: minmax(0, 1fr) auto;
115
+ }
116
+
117
+ .mp-album-frame {
118
+ position: relative;
119
+ min-height: 0;
120
+ display: grid;
121
+ place-items: center;
122
+ container-type: size;
123
+ --gutter: 20px;
124
+ }
125
+
126
+ .mp-polaroid {
127
+ margin: 0;
128
+ padding: 8px 8px 0;
129
+ background: #fff;
130
+ box-shadow: 0 5px 0 var(--cream-300), 0 12px 24px oklch(0.22 0.05 60 / 0.25);
131
+ rotate: var(--tilt, -1.5deg);
132
+ animation: mp-drop 380ms var(--ease-bounce) both;
133
+ /* Older browsers: a sensible fixed box. Newer ones fit the photo's own shape to the screen. */
134
+ width: min(100% - 40px, 360px);
135
+ box-sizing: border-box;
136
+ }
137
+
138
+ @supports (width: 1cqw) {
139
+ .mp-polaroid {
140
+ width: min(
141
+ 100cqw - var(--gutter) * 2,
142
+ (100cqh - 44px - var(--cap)) * var(--w) / var(--h) + 16px
143
+ );
144
+ }
145
+ }
146
+
147
+ @keyframes mp-drop {
148
+ from { opacity: 0; transform: translateY(-30px) rotate(-6deg); }
149
+ to { opacity: 1; transform: none; }
150
+ }
151
+
152
+ .mp-photo-button {
153
+ position: relative;
154
+ display: block;
155
+ width: 100%;
156
+ padding: 0;
157
+ border: 0;
158
+ background: var(--cream-100);
159
+ border-radius: 3px;
160
+ cursor: zoom-in;
161
+ -webkit-tap-highlight-color: transparent;
162
+ }
163
+
164
+ .mp-photo-button img {
165
+ display: block;
166
+ width: 100%;
167
+ aspect-ratio: var(--w) / var(--h);
168
+ object-fit: contain;
169
+ border-radius: 3px;
170
+ }
171
+
172
+ .mp-photo-button:focus-visible,
173
+ .mp-album-arrow:focus-visible,
174
+ .mp-album-thumb:focus-visible,
175
+ .mp-cassette-play:focus-visible,
176
+ .mp-light-close:focus-visible {
177
+ outline: 4px solid var(--blue-500);
178
+ outline-offset: 3px;
179
+ }
180
+
181
+ .mp-photo-zoom {
182
+ position: absolute;
183
+ right: 6px;
184
+ bottom: 6px;
185
+ width: 32px;
186
+ height: 32px;
187
+ border-radius: 50%;
188
+ display: grid;
189
+ place-items: center;
190
+ background: var(--surface-hud);
191
+ color: #fff;
192
+ font-size: 16px;
193
+ }
194
+
195
+ .mp-polaroid figcaption {
196
+ padding: 6px 4px 8px;
197
+ min-height: 12px;
198
+ font-family: var(--font-display);
199
+ font-size: 14px;
200
+ line-height: 1.3;
201
+ color: var(--ink-700);
202
+ text-align: center;
203
+ white-space: nowrap;
204
+ overflow: hidden;
205
+ text-overflow: ellipsis;
206
+ }
207
+
208
+ .mp-polaroid:not(:has(figcaption)) {
209
+ padding-bottom: 8px;
210
+ }
211
+
212
+ .mp-album-arrow {
213
+ position: absolute;
214
+ bottom: 8px;
215
+ z-index: 2;
216
+ width: 44px;
217
+ height: 44px;
218
+ padding: 0;
219
+ border-radius: 50%;
220
+ border: 3px solid #fff;
221
+ background: linear-gradient(180deg, #fff, var(--cream-100));
222
+ box-shadow: 0 4px 0 var(--cream-300), 0 6px 12px oklch(0 0 0 / 0.25);
223
+ display: grid;
224
+ place-items: center;
225
+ font-size: 20px;
226
+ color: var(--ink-900);
227
+ cursor: pointer;
228
+ -webkit-tap-highlight-color: transparent;
229
+ }
230
+
231
+ .mp-album-arrow:active:not(:disabled) {
232
+ translate: 0 3px;
233
+ box-shadow: 0 1px 0 var(--cream-300);
234
+ }
235
+
236
+ .mp-album-arrow:disabled {
237
+ opacity: 0.35;
238
+ cursor: default;
239
+ }
240
+
241
+ .mp-album-arrow-prev { left: 8px; }
242
+ .mp-album-arrow-next { right: 8px; }
243
+
244
+ .mp-strip-row {
245
+ display: flex;
246
+ align-items: center;
247
+ gap: 8px;
248
+ padding: 0 8px;
249
+ background: oklch(0.3 0.05 60 / 0.06);
250
+ }
251
+
252
+ /* Phones: the arrows sit either end of the strip, clear of the photo. Roomier screens put them beside the photo. */
253
+ .mp-album-frame > .mp-album-arrow {
254
+ display: none;
255
+ }
256
+
257
+ .mp-strip-row > .mp-album-arrow {
258
+ position: static;
259
+ flex: none;
260
+ width: 40px;
261
+ height: 40px;
262
+ }
263
+
264
+ .mp-strip-row > .mp-album-arrow-next {
265
+ order: 3;
266
+ }
267
+
268
+ .mp-strip-row > .mp-album-count {
269
+ order: 2;
270
+ }
271
+
272
+ .mp-album-strip {
273
+ position: relative;
274
+ flex: 1;
275
+ min-width: 0;
276
+ display: flex;
277
+ gap: 6px;
278
+ padding: 10px 12px;
279
+ overflow-x: auto;
280
+ scrollbar-width: none;
281
+ }
282
+
283
+ .mp-album-strip::-webkit-scrollbar {
284
+ display: none;
285
+ }
286
+
287
+ .mp-album-thumb {
288
+ flex: none;
289
+ width: 42px;
290
+ height: 46px;
291
+ padding: 0;
292
+ overflow: hidden;
293
+ border-radius: 8px;
294
+ border: 3px solid #fff;
295
+ background: var(--cream-200);
296
+ box-shadow: 0 2px 0 var(--cream-300);
297
+ cursor: pointer;
298
+ transition: translate var(--dur-fast), scale var(--dur-fast);
299
+ }
300
+
301
+ .mp-album-thumb img {
302
+ display: block;
303
+ width: 100%;
304
+ height: 100%;
305
+ object-fit: cover;
306
+ }
307
+
308
+ .mp-album-thumb:active {
309
+ scale: 0.92;
310
+ }
311
+
312
+ .mp-album-thumb-on {
313
+ border-color: var(--blue-500);
314
+ box-shadow: 0 3px 0 var(--blue-700);
315
+ translate: 0 -3px;
316
+ }
317
+
318
+ .mp-album-count {
319
+ flex: none;
320
+ font-family: var(--font-display);
321
+ font-size: 13px;
322
+ color: var(--ink-700);
323
+ font-variant-numeric: tabular-nums;
324
+ }
325
+
326
+ /* Full screen, for tall screenshots and small faces. Rendered on the body, above everything. */
327
+ .mp-lightbox {
328
+ position: fixed;
329
+ inset: 0;
330
+ z-index: 70;
331
+ display: grid;
332
+ place-items: center;
333
+ background: oklch(0.16 0.03 260 / 0.94);
334
+ cursor: zoom-out;
335
+ animation: mp-fade 200ms var(--ease-out) both;
336
+ }
337
+
338
+ .mp-lightbox-leaving {
339
+ animation: mp-fade-out 180ms var(--ease-in-out) both;
340
+ pointer-events: none;
341
+ }
342
+
343
+ @keyframes mp-fade-out {
344
+ from { opacity: 1; }
345
+ to { opacity: 0; }
346
+ }
347
+
348
+ .mp-lightbox img {
349
+ animation: mp-zoom-in 260ms var(--ease-out) both;
350
+ display: block;
351
+ width: 100vw;
352
+ height: 100dvh;
353
+ box-sizing: border-box;
354
+ padding: max(12px, env(safe-area-inset-top)) 12px max(12px, env(safe-area-inset-bottom));
355
+ object-fit: contain;
356
+ }
357
+
358
+ .mp-light-caption {
359
+ position: absolute;
360
+ left: 50%;
361
+ bottom: max(18px, env(safe-area-inset-bottom));
362
+ translate: -50% 0;
363
+ max-width: min(84vw, 560px);
364
+ margin: 0;
365
+ padding: 8px 16px;
366
+ border-radius: 999px;
367
+ background: var(--surface-hud);
368
+ font-weight: 700;
369
+ font-size: 15px;
370
+ color: #fff;
371
+ text-align: center;
372
+ }
373
+
374
+ .mp-light-close {
375
+ position: absolute;
376
+ top: max(14px, env(safe-area-inset-top));
377
+ right: 14px;
378
+ width: 48px;
379
+ height: 48px;
380
+ padding: 0;
381
+ border-radius: 50%;
382
+ border: 3px solid #fff;
383
+ background: linear-gradient(180deg, var(--coral-300), var(--coral-500) 50%);
384
+ box-shadow: 0 4px 0 var(--coral-700), 0 6px 10px oklch(0 0 0 / 0.25);
385
+ color: #fff;
386
+ font-size: 22px;
387
+ display: grid;
388
+ place-items: center;
389
+ cursor: pointer;
390
+ }
391
+
392
+ .mp-lightbox .mp-album-arrow {
393
+ bottom: auto;
394
+ top: 50%;
395
+ translate: 0 -50%;
396
+ }
397
+
398
+ .mp-lightbox .mp-album-arrow:active:not(:disabled) {
399
+ translate: 0 calc(-50% + 3px);
400
+ }
401
+
402
+ @keyframes mp-zoom-in {
403
+ from { opacity: 0; transform: scale(0.94); }
404
+ to { opacity: 1; transform: none; }
405
+ }
406
+
407
+ @keyframes mp-fade {
408
+ from { opacity: 0; }
409
+ to { opacity: 1; }
410
+ }
411
+
412
+ /* ------------------------------------------------------------------ voice note */
413
+
414
+ .mp-tape-deck {
415
+ width: min(88%, 520px);
416
+ display: grid;
417
+ gap: 14px;
418
+ justify-items: center;
419
+ text-align: center;
420
+ }
421
+
422
+ .mp-cassette {
423
+ width: 100%;
424
+ box-sizing: border-box;
425
+ padding: 14px 16px;
426
+ border-radius: 16px;
427
+ border: 3px solid var(--stone-700);
428
+ background: linear-gradient(180deg, var(--stone-100), var(--stone-300));
429
+ box-shadow: 0 5px 0 var(--stone-700), 0 10px 18px oklch(0.2 0.05 60 / 0.25);
430
+ display: grid;
431
+ gap: 12px;
432
+ }
433
+
434
+ .mp-cassette-label {
435
+ display: flex;
436
+ justify-content: space-between;
437
+ gap: 12px;
438
+ padding: 6px 10px;
439
+ border-radius: 8px;
440
+ border: 2px solid var(--cream-300);
441
+ background: var(--cream-50);
442
+ font-family: var(--font-display);
443
+ font-size: 13px;
444
+ letter-spacing: 0.04em;
445
+ text-transform: uppercase;
446
+ color: var(--ink-900);
447
+ }
448
+
449
+ .mp-cassette-label span:first-child {
450
+ min-width: 0;
451
+ overflow: hidden;
452
+ text-overflow: ellipsis;
453
+ white-space: nowrap;
454
+ }
455
+
456
+ .mp-reels {
457
+ display: flex;
458
+ align-items: center;
459
+ justify-content: space-between;
460
+ padding: 0 10px;
461
+ }
462
+
463
+ .mp-reel {
464
+ width: 54px;
465
+ height: 54px;
466
+ flex: none;
467
+ border-radius: 50%;
468
+ background: conic-gradient(var(--stone-700) 0 12%, var(--cream-50) 0 25%, var(--stone-700) 0 37%, var(--cream-50) 0 50%, var(--stone-700) 0 62%, var(--cream-50) 0 75%, var(--stone-700) 0 87%, var(--cream-50) 0);
469
+ box-shadow: inset 0 0 0 4px var(--ink-900), inset 0 0 0 14px var(--stone-100);
470
+ }
471
+
472
+ .mp-playing .mp-reel {
473
+ animation: mp-spin 2.4s linear infinite;
474
+ }
475
+
476
+ @keyframes mp-spin {
477
+ to { rotate: 360deg; }
478
+ }
479
+
480
+ .mp-wave {
481
+ flex: 1;
482
+ min-width: 0;
483
+ height: 44px;
484
+ overflow: hidden;
485
+ display: flex;
486
+ align-items: center;
487
+ justify-content: center;
488
+ gap: 3px;
489
+ padding: 0 12px;
490
+ }
491
+
492
+ .mp-wave i {
493
+ width: 4px;
494
+ flex: none;
495
+ height: var(--bar);
496
+ border-radius: 3px;
497
+ background: var(--teal-500);
498
+ }
499
+
500
+ .mp-playing .mp-wave i {
501
+ animation: mp-bounce 900ms ease-in-out infinite;
502
+ animation-delay: var(--delay);
503
+ }
504
+
505
+ @keyframes mp-bounce {
506
+ 50% { transform: scaleY(0.35); }
507
+ }
508
+
509
+ .mp-transport {
510
+ display: flex;
511
+ align-items: center;
512
+ gap: 12px;
513
+ }
514
+
515
+ .mp-cassette-play {
516
+ width: 56px;
517
+ height: 56px;
518
+ flex: none;
519
+ padding: 0;
520
+ border: 0;
521
+ border-radius: 50%;
522
+ display: grid;
523
+ place-items: center;
524
+ font-size: 26px;
525
+ color: #fff;
526
+ background: linear-gradient(180deg, var(--teal-300), var(--teal-500) 45%);
527
+ box-shadow: 0 5px 0 var(--teal-700), inset 0 2px 0 oklch(1 0 0 / 0.45);
528
+ text-shadow: 0 2px 0 var(--teal-700);
529
+ cursor: pointer;
530
+ transition: transform var(--dur-fast), box-shadow var(--dur-fast);
531
+ -webkit-tap-highlight-color: transparent;
532
+ }
533
+
534
+ .mp-cassette-play:active {
535
+ transform: translateY(4px);
536
+ box-shadow: 0 1px 0 var(--teal-700), inset 0 2px 0 oklch(1 0 0 / 0.45);
537
+ }
538
+
539
+ .mp-seek {
540
+ position: relative;
541
+ flex: 1;
542
+ height: 28px;
543
+ display: grid;
544
+ align-items: center;
545
+ }
546
+
547
+ .mp-seek input {
548
+ position: absolute;
549
+ inset: 0;
550
+ width: 100%;
551
+ height: 100%;
552
+ margin: 0;
553
+ opacity: 0;
554
+ cursor: pointer;
555
+ }
556
+
557
+ .mp-seek:has(input:focus-visible) .mp-seek-bar {
558
+ outline: 4px solid var(--blue-500);
559
+ outline-offset: 3px;
560
+ }
561
+
562
+ .mp-seek-bar {
563
+ display: block;
564
+ height: 10px;
565
+ border-radius: 999px;
566
+ overflow: hidden;
567
+ background: var(--cream-50);
568
+ box-shadow: inset 0 2px 3px oklch(0 0 0 / 0.15);
569
+ }
570
+
571
+ .mp-seek-bar b {
572
+ display: block;
573
+ height: 100%;
574
+ border-radius: 999px;
575
+ background: var(--teal-500);
576
+ }
577
+
578
+ .mp-cassette-time {
579
+ min-width: 36px;
580
+ font-family: var(--font-display);
581
+ font-size: 13px;
582
+ color: var(--ink-700);
583
+ font-variant-numeric: tabular-nums;
584
+ text-align: right;
585
+ }
586
+
587
+ .mp-tape-heading {
588
+ margin: 0;
589
+ font-family: var(--font-display);
590
+ font-weight: 400;
591
+ font-size: 22px;
592
+ line-height: 1.15;
593
+ color: var(--ink-900);
594
+ text-wrap: balance;
595
+ }
596
+
597
+ .mp-tape-hint {
598
+ margin: -6px 0 0;
599
+ font-weight: 600;
600
+ font-size: 14px;
601
+ color: var(--ink-700);
602
+ }
603
+
604
+ /* ------------------------------------------------------------------ video */
605
+
606
+ .mp-film {
607
+ margin: 0;
608
+ display: grid;
609
+ gap: 6px;
610
+ width: min(100% - 28px, 380px);
611
+ }
612
+
613
+ @supports (width: 1cqw) {
614
+ .mp-film {
615
+ width: min(
616
+ 100cqw - 28px,
617
+ (100cqh - 28px - var(--cap)) * var(--w) / var(--h) + 28px
618
+ );
619
+ }
620
+ }
621
+
622
+ /* A strip of film: the sprocket holes run down the sides, outside the picture. */
623
+ .mp-film-strip {
624
+ position: relative;
625
+ padding: 0 14px;
626
+ border-radius: 12px;
627
+ overflow: hidden;
628
+ background: var(--ink-900);
629
+ box-shadow: 0 8px 18px oklch(0.2 0.05 60 / 0.3);
630
+ }
631
+
632
+ .mp-film-strip::before,
633
+ .mp-film-strip::after {
634
+ content: '';
635
+ position: absolute;
636
+ top: 0;
637
+ bottom: 0;
638
+ width: 14px;
639
+ background: repeating-linear-gradient(180deg, transparent 0 6px, var(--cream-50) 6px 12px, transparent 12px 16px);
640
+ background-size: 6px 100%;
641
+ background-position: center;
642
+ background-repeat: no-repeat;
643
+ opacity: 0.85;
644
+ }
645
+
646
+ .mp-film-strip::before { left: 0; }
647
+ .mp-film-strip::after { right: 0; }
648
+
649
+ .mp-film-strip video {
650
+ display: block;
651
+ width: 100%;
652
+ aspect-ratio: var(--w) / var(--h);
653
+ object-fit: contain;
654
+ background: #000;
655
+ }
656
+
657
+ .mp-film figcaption {
658
+ font-weight: 700;
659
+ font-size: 14px;
660
+ line-height: 1.3;
661
+ color: var(--ink-700);
662
+ text-align: center;
663
+ white-space: nowrap;
664
+ overflow: hidden;
665
+ text-overflow: ellipsis;
666
+ }
667
+
668
+ /* ------------------------------------------------------------------ roomier screens */
669
+
670
+ @media (min-width: 600px) {
671
+ .mp-album-frame {
672
+ --gutter: 66px;
673
+ }
674
+
675
+ .mp-album-frame > .mp-album-arrow {
676
+ display: grid;
677
+ bottom: auto;
678
+ top: 50%;
679
+ translate: 0 -50%;
680
+ }
681
+
682
+ .mp-album-frame > .mp-album-arrow:active:not(:disabled) {
683
+ translate: 0 calc(-50% + 3px);
684
+ }
685
+
686
+ .mp-strip-row > .mp-album-arrow {
687
+ display: none;
688
+ }
689
+
690
+ .mp-strip-row {
691
+ padding: 0 12px 0 0;
692
+ }
693
+ }
694
+
695
+ @media (prefers-reduced-motion: reduce) {
696
+ .mp-words-cursor,
697
+ .mp-polaroid,
698
+ .mp-lightbox,
699
+ .mp-lightbox-leaving,
700
+ .mp-lightbox img,
701
+ .mp-playing .mp-reel,
702
+ .mp-playing .mp-wave i {
703
+ animation: none;
704
+ }
705
+ }
706
+
707
+ /* ---- Loading: a soft shine sweeps the empty frame, then the picture fades in over it. ---- */
708
+
709
+ .mp-media {
710
+ position: relative;
711
+ }
712
+
713
+ .mp-media > img,
714
+ .mp-media > video {
715
+ transition: opacity 320ms var(--ease-out);
716
+ }
717
+
718
+ .mp-media[data-loaded='false'] > img,
719
+ .mp-media[data-loaded='false'] > video {
720
+ opacity: 0;
721
+ }
722
+
723
+ .mp-shimmer {
724
+ position: absolute;
725
+ inset: 0;
726
+ z-index: 1;
727
+ border-radius: inherit;
728
+ pointer-events: none;
729
+ background: linear-gradient(100deg, transparent 25%, var(--shine, oklch(1 0 0 / 0.6)) 50%, transparent 75%) 0 0 / 220% 100%, var(--base, var(--cream-200));
730
+ animation: mp-shine 1.4s linear infinite;
731
+ transition: opacity 320ms var(--ease-out);
732
+ }
733
+
734
+ .mp-media[data-loaded='true'] > .mp-shimmer {
735
+ opacity: 0;
736
+ animation: none;
737
+ }
738
+
739
+ /* Tones for the different frames. */
740
+ .mp-on-cream {
741
+ --base: var(--cream-200);
742
+ --shine: oklch(1 0 0 / 0.65);
743
+ }
744
+
745
+ .mp-on-dark {
746
+ --base: oklch(0.3 0.05 55);
747
+ --shine: oklch(1 0 0 / 0.1);
748
+ }
749
+
750
+ .mp-on-lilac {
751
+ --base: #e6d9ef;
752
+ --shine: oklch(1 0 0 / 0.7);
753
+ }
754
+
755
+ .mp-on-night {
756
+ --base: #2b2150;
757
+ --shine: oklch(1 0 0 / 0.1);
758
+ }
759
+
760
+ /* The film strip keeps its sprocket holes showing down the sides. */
761
+ .mp-inset {
762
+ inset: 0 14px;
763
+ }
764
+
765
+ @keyframes mp-shine {
766
+ from { background-position: 120% 0, 0 0; }
767
+ to { background-position: -120% 0, 0 0; }
768
+ }
769
+
770
+ @media (prefers-reduced-motion: reduce) {
771
+ .mp-shimmer { animation: none; }
772
+ .mp-media > img,
773
+ .mp-media > video { transition: none; }
774
+ }