@gentbajko/slopify 2.0.2 → 2.1.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 (224) hide show
  1. package/SUBTITLES.md +1 -1
  2. package/dist/adapters/alignment/protocol.js +11 -1
  3. package/dist/adapters/alignment/runner.js +4 -1
  4. package/dist/adapters/alignment/threads.js +11 -0
  5. package/dist/adapters/alignment/worker.js +66 -9
  6. package/dist/adapters/explain.js +93 -0
  7. package/dist/adapters/ffmpeg.js +3 -3
  8. package/dist/adapters/host-cli/index.js +34 -3
  9. package/dist/adapters/host-cli/transport.js +4 -4
  10. package/dist/adapters/image/bytes.js +2 -2
  11. package/dist/adapters/image/codex-output.js +12 -12
  12. package/dist/adapters/image/codex.js +24 -8
  13. package/dist/adapters/image/fal.js +12 -6
  14. package/dist/adapters/image/google.js +18 -8
  15. package/dist/adapters/image/models.js +8 -2
  16. package/dist/adapters/image/openai.js +15 -6
  17. package/dist/adapters/image/replicate.js +16 -7
  18. package/dist/adapters/llm/claude-code-models.js +1 -1
  19. package/dist/adapters/llm/claude-code.js +16 -7
  20. package/dist/adapters/llm/cli-login-error.js +2 -1
  21. package/dist/adapters/llm/codex-models.js +1 -1
  22. package/dist/adapters/llm/codex.js +5 -6
  23. package/dist/adapters/llm/document-workspace.js +2 -2
  24. package/dist/adapters/llm/gemini-models.js +1 -1
  25. package/dist/adapters/llm/gemini-workspace.js +1 -1
  26. package/dist/adapters/llm/gemini.js +15 -11
  27. package/dist/adapters/llm/openrouter.js +21 -9
  28. package/dist/adapters/llm/run-cli.js +14 -11
  29. package/dist/adapters/tts/cartesia.js +10 -3
  30. package/dist/adapters/tts/elevenlabs.js +18 -6
  31. package/dist/adapters/tts/inworld-async.js +47 -16
  32. package/dist/adapters/tts/inworld.js +25 -15
  33. package/dist/adapters/tts/openai.js +18 -6
  34. package/dist/assets/document/SOURCE.txt +7 -0
  35. package/dist/assets/document/background.jpg +0 -0
  36. package/dist/assets/document/fonts/Cinzel-Black.ttf +0 -0
  37. package/dist/assets/document/fonts/Cinzel-Bold.ttf +0 -0
  38. package/dist/assets/document/fonts/Cinzel-Medium.ttf +0 -0
  39. package/dist/assets/document/fonts/Cinzel-Regular.ttf +0 -0
  40. package/dist/assets/document/fonts/OFL.txt +93 -0
  41. package/dist/assets/document/fonts/SOURCE.txt +11 -0
  42. package/dist/catalog/registry.js +13 -10
  43. package/dist/catalog/store.js +1 -1
  44. package/dist/catalog/validate.js +16 -6
  45. package/dist/edge/cli.js +33 -7
  46. package/dist/edge/docker-launch.js +6 -6
  47. package/dist/edge/docker-projects/engine.js +34 -13
  48. package/dist/edge/docker-projects/install.js +8 -8
  49. package/dist/edge/docker-projects/state.js +11 -7
  50. package/dist/edge/docker-projects/tree.js +6 -6
  51. package/dist/edge/docker.js +13 -13
  52. package/dist/edge/host-cli.js +14 -0
  53. package/dist/edge/http/actions.js +27 -25
  54. package/dist/edge/http/app.js +3 -3
  55. package/dist/edge/http/audio-preview.js +1 -1
  56. package/dist/edge/http/checkpoints.js +5 -1
  57. package/dist/edge/http/diagnostics.js +3 -1
  58. package/dist/edge/http/draft-files.js +2 -2
  59. package/dist/edge/http/draft-problem.js +11 -1
  60. package/dist/edge/http/files.js +4 -4
  61. package/dist/edge/http/folder-location-schema.js +5 -8
  62. package/dist/edge/http/folder-location.js +13 -8
  63. package/dist/edge/http/fonts.js +22 -10
  64. package/dist/edge/http/host-cli.js +61 -14
  65. package/dist/edge/http/multipart.js +7 -5
  66. package/dist/edge/http/open-folder.js +2 -2
  67. package/dist/edge/http/planning.js +4 -4
  68. package/dist/edge/http/problem.js +2 -2
  69. package/dist/edge/http/project-templates.js +7 -3
  70. package/dist/edge/http/projects.js +8 -8
  71. package/dist/edge/http/prompts.js +9 -4
  72. package/dist/edge/http/providers.js +15 -7
  73. package/dist/edge/http/revision-files.js +8 -4
  74. package/dist/edge/http/revisions.js +30 -4
  75. package/dist/edge/http/schedules.js +9 -9
  76. package/dist/edge/http/settings.js +15 -9
  77. package/dist/edge/http/staging.js +5 -5
  78. package/dist/edge/http/storage.js +4 -4
  79. package/dist/edge/http/subtitles.js +1 -1
  80. package/dist/edge/http/tutorial.js +2 -2
  81. package/dist/edge/http/update.js +3 -3
  82. package/dist/host-cli/install.js +28 -6
  83. package/dist/host-cli/open-folder.js +95 -0
  84. package/dist/host-cli/paths.js +6 -6
  85. package/dist/host-cli/runtime.js +2 -0
  86. package/dist/host-cli/server.js +1 -1
  87. package/dist/host-cli/service.js +11 -11
  88. package/dist/host-cli/status.js +7 -3
  89. package/dist/kernel/config/index.js +6 -4
  90. package/dist/kernel/db/migrate.js +27 -2
  91. package/dist/kernel/db/migrations/0013-schedule-topic-queue.sql +4 -0
  92. package/dist/kernel/db/migrations/0014-document-stage.sql +44 -0
  93. package/dist/kernel/db/migrations/0015-description-prompts.sql +13 -0
  94. package/dist/kernel/lock.js +4 -3
  95. package/dist/kernel/pipeline.js +11 -1
  96. package/dist/kernel/ports/host-cli.js +14 -0
  97. package/dist/kernel/ports/subtitles.js +15 -1
  98. package/dist/kernel/runner/attempt.js +26 -2
  99. package/dist/kernel/runner/graph.js +6 -1
  100. package/dist/main.js +8 -4
  101. package/dist/slices/admission/model.js +7 -1
  102. package/dist/slices/admission/rules.js +98 -22
  103. package/dist/slices/admission/schema.js +16 -3
  104. package/dist/slices/admission/start.js +3 -2
  105. package/dist/slices/article/continuation.js +4 -2
  106. package/dist/slices/article/plain.js +2 -2
  107. package/dist/slices/article/provided-entries.js +3 -3
  108. package/dist/slices/article/run.js +2 -2
  109. package/dist/slices/article/segments.js +5 -3
  110. package/dist/slices/control/providers.js +20 -7
  111. package/dist/slices/document/blocks.js +129 -0
  112. package/dist/slices/document/flow.js +117 -0
  113. package/dist/slices/document/fonts.js +54 -0
  114. package/dist/slices/document/model.js +11 -0
  115. package/dist/slices/document/pages.js +198 -0
  116. package/dist/slices/document/render.js +242 -0
  117. package/dist/slices/document/sources.js +35 -0
  118. package/dist/slices/document/theme.js +158 -0
  119. package/dist/slices/document/writer.js +66 -0
  120. package/dist/slices/estimate/index.js +14 -2
  121. package/dist/slices/fonts/catalog.js +1 -1
  122. package/dist/slices/fonts/upload.js +1 -1
  123. package/dist/slices/images/run.js +5 -5
  124. package/dist/slices/library/lint.js +13 -7
  125. package/dist/slices/library/model.js +1 -1
  126. package/dist/slices/library/slots.js +6 -3
  127. package/dist/slices/narration/concat.js +1 -1
  128. package/dist/slices/narration/pronunciation-chunks.js +3 -3
  129. package/dist/slices/narration/pronunciation.js +2 -2
  130. package/dist/slices/narration/run.js +7 -7
  131. package/dist/slices/narration/steering.js +3 -3
  132. package/dist/slices/play-drafts/convert.js +35 -5
  133. package/dist/slices/play-drafts/readiness.js +15 -8
  134. package/dist/slices/play-drafts/review-inputs.js +7 -4
  135. package/dist/slices/play-drafts/review.js +3 -3
  136. package/dist/slices/play-drafts/schema.js +22 -5
  137. package/dist/slices/play-drafts/service.js +3 -1
  138. package/dist/slices/play-drafts/uploads.js +1 -1
  139. package/dist/slices/project-templates/from-project.js +10 -0
  140. package/dist/slices/project-templates/setup.js +1 -0
  141. package/dist/slices/rebuild/admission-repo.js +2 -2
  142. package/dist/slices/rebuild/preview-details.js +11 -1
  143. package/dist/slices/rebuild/recipe-audio.js +3 -0
  144. package/dist/slices/rebuild/recipe-build.js +6 -1
  145. package/dist/slices/rebuild/recipe-document.js +36 -0
  146. package/dist/slices/rebuild/recipe-exports.js +14 -2
  147. package/dist/slices/rebuild/recipe-fixture.js +4 -0
  148. package/dist/slices/rebuild/recipe-legacy.js +6 -1
  149. package/dist/slices/rebuild/recipe-model.js +2 -0
  150. package/dist/slices/rebuild/recipe-narration-text.js +1 -1
  151. package/dist/slices/rebuild/recipe-provider-choice.js +3 -0
  152. package/dist/slices/rebuild/recipe-save.js +15 -3
  153. package/dist/slices/rebuild/recipe-validation.js +15 -2
  154. package/dist/slices/rebuild/recipe-visual.js +11 -2
  155. package/dist/slices/rebuild/recipe-work.js +15 -0
  156. package/dist/slices/rebuild/recipe-youtube.js +33 -0
  157. package/dist/slices/rebuild/recovery-selection.js +2 -0
  158. package/dist/slices/rebuild/recovery.js +12 -7
  159. package/dist/slices/rebuild/repo.js +3 -3
  160. package/dist/slices/rebuild/runtime-actions.js +2 -0
  161. package/dist/slices/rebuild/runtime-admission.js +4 -4
  162. package/dist/slices/rebuild/runtime-article.js +5 -5
  163. package/dist/slices/rebuild/runtime-checkpoints.js +4 -4
  164. package/dist/slices/rebuild/runtime-document.js +85 -0
  165. package/dist/slices/rebuild/runtime-export-inputs.js +7 -6
  166. package/dist/slices/rebuild/runtime-export.js +39 -23
  167. package/dist/slices/rebuild/runtime-local.js +6 -6
  168. package/dist/slices/rebuild/runtime-narration-text.js +46 -7
  169. package/dist/slices/rebuild/runtime-piece-label.js +66 -0
  170. package/dist/slices/rebuild/runtime-provider.js +6 -4
  171. package/dist/slices/rebuild/runtime-publication.js +4 -3
  172. package/dist/slices/rebuild/runtime-run.js +22 -4
  173. package/dist/slices/rebuild/runtime-store.js +7 -2
  174. package/dist/slices/rebuild/runtime-subtitles.js +82 -16
  175. package/dist/slices/rebuild/runtime-youtube.js +84 -0
  176. package/dist/slices/rebuild/transition-repo.js +8 -6
  177. package/dist/slices/reruns/cascade.js +6 -4
  178. package/dist/slices/reruns/index.js +1 -1
  179. package/dist/slices/research/run.js +9 -7
  180. package/dist/slices/research/synthesis.js +4 -2
  181. package/dist/slices/revisions/mutation-assets.js +26 -8
  182. package/dist/slices/revisions/mutation-cues.js +4 -2
  183. package/dist/slices/revisions/projection.js +2 -1
  184. package/dist/slices/revisions/rules.js +18 -9
  185. package/dist/slices/revisions/schema.js +1 -1
  186. package/dist/slices/schedules/repo.js +55 -11
  187. package/dist/slices/schedules/scheduler.js +25 -8
  188. package/dist/slices/schedules/schema.js +19 -4
  189. package/dist/slices/schedules/service.js +2 -0
  190. package/dist/slices/settings/cli-paths.js +7 -4
  191. package/dist/slices/settings/models.js +4 -4
  192. package/dist/slices/settings/playback.js +2 -2
  193. package/dist/slices/storage/assets.js +5 -5
  194. package/dist/slices/storage/downloads.js +1 -0
  195. package/dist/slices/storage/layout.js +7 -1
  196. package/dist/slices/storage/model.js +3 -0
  197. package/dist/slices/subtitles/captions.js +1 -1
  198. package/dist/slices/subtitles/prepare.js +7 -6
  199. package/dist/slices/subtitles/transcript.js +1 -1
  200. package/dist/slices/telemetry/model.js +1 -1
  201. package/dist/slices/thumbnail/run.js +8 -8
  202. package/dist/slices/video/audio-export.js +11 -3
  203. package/dist/slices/video/audio-inputs.js +2 -2
  204. package/dist/slices/video/edit-list.js +73 -0
  205. package/dist/slices/video/ffmpeg.js +129 -61
  206. package/dist/slices/video/motion.js +51 -0
  207. package/dist/slices/video/plan.js +62 -28
  208. package/dist/slices/video/reuse-audio.js +2 -0
  209. package/dist/slices/video/run.js +23 -12
  210. package/dist/slices/video/slideshow.js +48 -0
  211. package/dist/slices/video/write-export.js +17 -25
  212. package/dist/slices/youtube/answer.js +199 -0
  213. package/dist/slices/youtube/model.js +27 -0
  214. package/dist/slices/youtube/timestamps.js +24 -0
  215. package/dist/slices/youtube/transcript.js +47 -0
  216. package/dist/updater/plan.js +1 -1
  217. package/dist/updater/service.js +4 -3
  218. package/dist/updater/worker.js +6 -4
  219. package/dist/web/assets/index-B6NbhpjD.css +1 -0
  220. package/dist/web/assets/index-ComF5gwP.js +142 -0
  221. package/dist/web/index.html +2 -2
  222. package/package.json +4 -2
  223. package/dist/web/assets/index-CZNiHm8P.css +0 -1
  224. package/dist/web/assets/index-DPgGLX5m.js +0 -137
@@ -5,7 +5,6 @@ import { setStageProgress } from "../admission/repo.js";
5
5
  import { outputPath } from "../storage/layout.js";
6
6
  import { deleteOutput, insertOutput, outputsOf } from "../storage/repo.js";
7
7
  import { subtitleRoles } from "../subtitles/prepare.js";
8
- import { runFfmpeg } from "./ffmpeg.js";
9
8
  const progressIntervalMs = 500;
10
9
  const renderRoles = [
11
10
  "video",
@@ -26,30 +25,23 @@ export async function writeExport(deps, context, output) {
26
25
  let announced = 0;
27
26
  try {
28
27
  // No retry and no timeout. A render that fails is terminal.
29
- await runFfmpeg({
30
- bin: deps.ffmpeg,
31
- args: output.args(part),
32
- cwd: output.subtitles?.directory,
33
- signal: context.signal,
34
- log: deps.log,
35
- onProgress: (elapsedMs) => {
36
- const at = deps.clock.now().getTime();
37
- if (at - announced < progressIntervalMs) {
38
- return;
39
- }
40
- announced = at;
41
- const current = output.subtitles === undefined
42
- ? Math.min(elapsedMs, totalMs)
43
- : Math.round((0.35 + 0.65 * Math.min(elapsedMs / totalMs, 1)) * totalMs);
44
- setStageProgress(deps.db, context.stage.id, current, totalMs);
45
- context.emit({
46
- type: "stage.progress",
47
- projectId,
48
- stage: "video",
49
- current,
50
- total: totalMs,
51
- });
52
- },
28
+ await output.render(part, (elapsedMs) => {
29
+ const at = deps.clock.now().getTime();
30
+ if (at - announced < progressIntervalMs) {
31
+ return;
32
+ }
33
+ announced = at;
34
+ const current = output.subtitles === undefined
35
+ ? Math.min(elapsedMs, totalMs)
36
+ : Math.round((0.35 + 0.65 * Math.min(elapsedMs / totalMs, 1)) * totalMs);
37
+ setStageProgress(deps.db, context.stage.id, current, totalMs);
38
+ context.emit({
39
+ type: "stage.progress",
40
+ projectId,
41
+ stage: "video",
42
+ current,
43
+ total: totalMs,
44
+ });
53
45
  });
54
46
  // A cancel protects an output that was already stored, not one about to be. One
55
47
  // landing between ffmpeg exiting and the rows below discards the render rather than
@@ -0,0 +1,199 @@
1
+ import { z } from "zod";
2
+ import { descriptionMaxCharacters, hashtagsMax, minChapterSeconds, minChapters, tagMaxCharacters, tagsMaxCharacters, } from "./model.js";
3
+ import { parseTimestamp, youtubeTimestamp } from "./timestamps.js";
4
+ export function descriptionMessages(brief) {
5
+ const end = youtubeTimestamp(brief.durationSeconds);
6
+ return [
7
+ {
8
+ role: "system",
9
+ content: [
10
+ "You write YouTube descriptions. Answer with one JSON object and nothing else, in this shape:",
11
+ '{"summary": "...", "chapters": [{"start": "0:00", "title": "..."}], "hashtags": ["#Example"], "tags": ["example tag"]}',
12
+ "",
13
+ "Rules YouTube enforces, which the answer must follow:",
14
+ '- The first chapter starts at exactly "0:00".',
15
+ `- At least ${String(minChapters)} chapters, in order, each starting later than the one before.`,
16
+ `- Every chapter lasts at least ${String(minChapterSeconds)} seconds, and every chapter starts before the video ends at ${end}.`,
17
+ "- Write chapter times as M:SS, or H:MM:SS from the first hour on, and take them from the transcript's times.",
18
+ "- A chapter title is one line and does not start with a time.",
19
+ `- Hashtags are single words starting with #, at most ${String(hashtagsMax)}.`,
20
+ `- Tags are plain search terms without # or commas, each at most ${String(tagMaxCharacters)} characters, no tag repeated, and all tags together at most ${String(tagsMaxCharacters)} characters.`,
21
+ "- No < or > anywhere.",
22
+ "- The summary is plain text without chapters, hashtags or links.",
23
+ ].join("\n"),
24
+ },
25
+ {
26
+ role: "user",
27
+ content: [
28
+ brief.instruction,
29
+ "",
30
+ `Video title: ${brief.title}`,
31
+ `Video length: ${end}`,
32
+ "",
33
+ "Transcript, each passage led by the time it starts in the video:",
34
+ "",
35
+ brief.transcript,
36
+ ].join("\n"),
37
+ },
38
+ ];
39
+ }
40
+ const answerSchema = z.object({
41
+ summary: z.string(),
42
+ chapters: z.array(z.object({ start: z.union([z.string(), z.number()]), title: z.string() })),
43
+ hashtags: z.array(z.string()),
44
+ tags: z.array(z.string()),
45
+ });
46
+ const fix = "Retry stage, or choose another model in Edit project → Providers.";
47
+ // Reads and checks the model's answer. The reason is the sentence the stage shows; the
48
+ // provider wrapper's `check` asks the model again while attempts remain.
49
+ export function checkDescriptionAnswer(text, durationSeconds) {
50
+ const parsed = answerSchema.safeParse(jsonOf(text));
51
+ if (!parsed.success)
52
+ return {
53
+ ok: false,
54
+ reason: `The AI model's YouTube description didn't come back in the expected format (a JSON object with a summary, chapters, hashtags and tags). ${fix}`,
55
+ };
56
+ const summary = parsed.data.summary.trim();
57
+ if (summary === "")
58
+ return { ok: false, reason: `The AI model's YouTube description has no summary. ${fix}` };
59
+ const chapters = checkChapters(parsed.data.chapters, durationSeconds);
60
+ if (!chapters.ok)
61
+ return chapters;
62
+ const hashtags = checkHashtags(parsed.data.hashtags);
63
+ if (!hashtags.ok)
64
+ return hashtags;
65
+ const tags = checkTags(parsed.data.tags);
66
+ if (!tags.ok)
67
+ return tags;
68
+ const value = { summary, chapters: chapters.value, hashtags: hashtags.value, tags: tags.value };
69
+ const description = assembleDescription(value);
70
+ if (/[<>]/.test(description))
71
+ return {
72
+ ok: false,
73
+ reason: `The AI model's YouTube description contains < or >, which YouTube doesn't allow. ${fix}`,
74
+ };
75
+ if (description.length > descriptionMaxCharacters)
76
+ return {
77
+ ok: false,
78
+ reason: `The AI model's YouTube description is ${String(description.length)} characters, over YouTube's ${String(descriptionMaxCharacters)}. ${fix}`,
79
+ };
80
+ return { ok: true, value };
81
+ }
82
+ // Summary, a blank line, one "M:SS Title" per chapter, a blank line, then the hashtags,
83
+ // which YouTube shows above the title when they come last.
84
+ export function assembleDescription(answer) {
85
+ return [
86
+ answer.summary,
87
+ "",
88
+ ...answer.chapters.map((chapter) => `${youtubeTimestamp(chapter.start)} ${chapter.title}`),
89
+ "",
90
+ answer.hashtags.join(" "),
91
+ ].join("\n");
92
+ }
93
+ // What goes in YouTube's Tags field: comma-separated.
94
+ export function tagsText(tags) {
95
+ return tags.join(", ");
96
+ }
97
+ // YouTube counts the Tags field as typed: the commas between tags, and the quotes it adds
98
+ // around a tag with a space in it.
99
+ export function tagsLength(tags) {
100
+ return (tags.reduce((sum, tag) => sum + tag.length + (/\s/.test(tag) ? 2 : 0), 0) +
101
+ Math.max(0, tags.length - 1));
102
+ }
103
+ function checkChapters(raw, durationSeconds) {
104
+ const broke = (rule) => ({
105
+ ok: false,
106
+ reason: `The AI model's chapters broke YouTube's rules (${rule}). ${fix}`,
107
+ });
108
+ if (raw.length < minChapters)
109
+ return broke(`there must be at least ${String(minChapters)} chapters, and it wrote ${String(raw.length)}`);
110
+ const chapters = [];
111
+ for (const [index, row] of raw.entries()) {
112
+ const start = typeof row.start === "number"
113
+ ? Number.isInteger(row.start) && row.start >= 0
114
+ ? row.start
115
+ : undefined
116
+ : parseTimestamp(row.start);
117
+ if (start === undefined)
118
+ return broke(`chapter ${String(index + 1)} starts at "${String(row.start)}", not a time like 2:15`);
119
+ const title = row.title.trim();
120
+ if (title === "" ||
121
+ /[\r\n]/.test(title) ||
122
+ parseTimestamp(title.split(/\s/)[0] ?? "") !== undefined)
123
+ return broke(`chapter ${String(index + 1)} needs a one-line title that doesn't start with a time`);
124
+ chapters.push({ start, title });
125
+ }
126
+ if (chapters[0]?.start !== 0)
127
+ return broke("the first chapter must start at 0:00");
128
+ for (const [index, chapter] of chapters.entries()) {
129
+ const previous = chapters[index - 1];
130
+ if (previous !== undefined && chapter.start <= previous.start)
131
+ return broke(`chapter ${String(index + 1)} at ${youtubeTimestamp(chapter.start)} must start after chapter ${String(index)} at ${youtubeTimestamp(previous.start)}`);
132
+ if (chapter.start >= durationSeconds)
133
+ return broke(`chapter ${String(index + 1)} starts at ${youtubeTimestamp(chapter.start)}, after the video ends at ${youtubeTimestamp(durationSeconds)}`);
134
+ }
135
+ for (const [index, chapter] of chapters.entries()) {
136
+ const end = chapters[index + 1]?.start ?? durationSeconds;
137
+ if (end - chapter.start < minChapterSeconds)
138
+ return broke(`chapter ${String(index + 1)} "${chapter.title}" lasts ${String(Math.floor(end - chapter.start))} seconds, and each must last at least ${String(minChapterSeconds)}`);
139
+ }
140
+ return { ok: true, value: chapters };
141
+ }
142
+ function checkHashtags(raw) {
143
+ const broke = (rule) => ({
144
+ ok: false,
145
+ reason: `The AI model's hashtags broke YouTube's rules (${rule}). ${fix}`,
146
+ });
147
+ const hashtags = raw
148
+ .map((tag) => tag.trim())
149
+ .filter((tag) => tag !== "")
150
+ .map((tag) => (tag.startsWith("#") ? tag : `#${tag}`));
151
+ if (hashtags.length === 0)
152
+ return broke("there must be at least one hashtag");
153
+ if (hashtags.length > hashtagsMax)
154
+ return broke(`YouTube ignores them all past ${String(hashtagsMax)}, and it wrote ${String(hashtags.length)}`);
155
+ const bad = hashtags.find((tag) => !/^#[\p{L}\p{N}_]+$/u.test(tag));
156
+ if (bad !== undefined)
157
+ return broke(`"${bad}" is not one word after the #`);
158
+ return { ok: true, value: hashtags };
159
+ }
160
+ function checkTags(raw) {
161
+ const broke = (rule) => ({
162
+ ok: false,
163
+ reason: `The AI model's tags broke YouTube's rules (${rule}). ${fix}`,
164
+ });
165
+ const tags = raw.map((tag) => tag.trim().replace(/\s+/g, " ")).filter((tag) => tag !== "");
166
+ if (tags.length === 0)
167
+ return broke("there must be at least one tag");
168
+ const long = tags.find((tag) => tag.length > tagMaxCharacters);
169
+ if (long !== undefined)
170
+ return broke(`"${long.slice(0, 40)}…" is over ${String(tagMaxCharacters)} characters`);
171
+ const odd = tags.find((tag) => /[,<>]/.test(tag));
172
+ if (odd !== undefined)
173
+ return broke(`"${odd}" contains a comma, < or >`);
174
+ const seen = new Set();
175
+ for (const tag of tags) {
176
+ const key = tag.toLowerCase();
177
+ if (seen.has(key))
178
+ return broke(`"${tag}" is listed twice`);
179
+ seen.add(key);
180
+ }
181
+ const length = tagsLength(tags);
182
+ if (length > tagsMaxCharacters)
183
+ return broke(`the tags come to ${String(length)} characters, over YouTube's ${String(tagsMaxCharacters)}`);
184
+ return { ok: true, value: tags };
185
+ }
186
+ // The object in the answer, wherever the model put it: bare, in a ```json fence, or after a
187
+ // sentence of its own.
188
+ function jsonOf(text) {
189
+ const start = text.indexOf("{");
190
+ const end = text.lastIndexOf("}");
191
+ if (start === -1 || end <= start)
192
+ return undefined;
193
+ try {
194
+ return JSON.parse(text.slice(start, end + 1));
195
+ }
196
+ catch {
197
+ return undefined;
198
+ }
199
+ }
@@ -0,0 +1,27 @@
1
+ // The optional YouTube description step of the Video stage: a short summary, a chapter list
2
+ // YouTube turns into chapters, hashtags, and a separate list for YouTube's Tags field.
3
+ // Browser-safe: Play, Edit project and the project page read these names and limits too.
4
+ // YouTube's own rules for chapters in a description: the first starts at 0:00, there are at
5
+ // least three, and each lasts at least ten seconds.
6
+ export const minChapters = 3;
7
+ export const minChapterSeconds = 10;
8
+ // YouTube's limits on the Tags field: 500 characters in all, 100 for one tag.
9
+ export const tagsMaxCharacters = 500;
10
+ export const tagMaxCharacters = 100;
11
+ // YouTube ignores every hashtag of a description that has more than 15.
12
+ export const hashtagsMax = 15;
13
+ // YouTube's limit on a description.
14
+ export const descriptionMaxCharacters = 5000;
15
+ // What a project uses when no Description prompt from the library is picked. No keywords,
16
+ // so it never asks Play for a field.
17
+ export const defaultDescriptionPrompt = [
18
+ "Write a YouTube description for this video.",
19
+ "",
20
+ "- Summary: a 2-3 sentence hook that says what the viewer gets from watching, in plain words, without clickbait.",
21
+ "- Chapters: split the video where the topic changes, 5-12 chapters for a long video and fewer for a short one. Chapter titles are 2-6 words, in title case, and name what that part covers.",
22
+ "- Hashtags: 3-5 hashtags about the subject, most specific last.",
23
+ "- Tags: 15-25 search terms a viewer might type to find this video, from broad to specific.",
24
+ ].join("\n");
25
+ // The name Play, Edit project and the review summary show for the prompt used when none is
26
+ // picked.
27
+ export const defaultDescriptionPromptName = "Built-in";
@@ -0,0 +1,24 @@
1
+ // Times the way YouTube reads them in a description: M:SS under an hour, H:MM:SS from the
2
+ // first hour on. Whole seconds, rounded down, so a chapter never starts after its words.
3
+ export function youtubeTimestamp(seconds) {
4
+ const whole = Math.max(0, Math.floor(seconds));
5
+ const hours = Math.floor(whole / 3600);
6
+ const minutes = Math.floor((whole % 3600) / 60);
7
+ const rest = String(whole % 60).padStart(2, "0");
8
+ return hours > 0
9
+ ? `${String(hours)}:${String(minutes).padStart(2, "0")}:${rest}`
10
+ : `${String(minutes)}:${rest}`;
11
+ }
12
+ // The seconds a model's M:SS, MM:SS or H:MM:SS stands for; undefined for anything else, so
13
+ // the caller can say which chapter time it could not read.
14
+ export function parseTimestamp(text) {
15
+ const match = /^(?:(\d+):)?(\d{1,2}):(\d{2})$/.exec(text.trim());
16
+ if (match === null)
17
+ return undefined;
18
+ const hours = Number(match[1] ?? "0");
19
+ const minutes = Number(match[2]);
20
+ const seconds = Number(match[3]);
21
+ if (seconds > 59 || (match[1] !== undefined && minutes > 59))
22
+ return undefined;
23
+ return hours * 3600 + minutes * 60 + seconds;
24
+ }
@@ -0,0 +1,47 @@
1
+ import { youtubeTimestamp } from "./timestamps.js";
2
+ // ceiling: a passage closes at the first sentence end after 20 s, so a chapter can start at
3
+ // most one passage late, and a 108-minute narration comes to about 300 lines instead of a
4
+ // thousand sentences. A pause of 1.5 s (the gap between intro, body and outro, or a long
5
+ // breath) always closes one; a passage with no sentence end closes at 60 s.
6
+ const passageSeconds = 20;
7
+ const pauseSeconds = 1.5;
8
+ const longestSeconds = 60;
9
+ export function transcriptPassages(words) {
10
+ const passages = [];
11
+ let pending = [];
12
+ const flush = () => {
13
+ const first = pending[0];
14
+ if (first !== undefined)
15
+ passages.push({
16
+ start: first.start,
17
+ text: pending
18
+ .map((word) => word.text.replace(/\s+/g, " ").trim())
19
+ .filter((text) => text !== "")
20
+ .join(" "),
21
+ });
22
+ pending = [];
23
+ };
24
+ for (const word of words) {
25
+ const first = pending[0];
26
+ const last = pending.at(-1);
27
+ if (first !== undefined &&
28
+ last !== undefined &&
29
+ (word.start - last.end >= pauseSeconds || word.end - first.start > longestSeconds))
30
+ flush();
31
+ pending.push(word);
32
+ const opening = pending[0];
33
+ if (opening !== undefined &&
34
+ word.end - opening.start >= passageSeconds &&
35
+ /[.!?]["'’”)]*$/.test(word.text))
36
+ flush();
37
+ }
38
+ flush();
39
+ return passages.filter((passage) => passage.text !== "");
40
+ }
41
+ // One passage per line, each led by its start as YouTube writes it: the model copies these
42
+ // times into its chapters.
43
+ export function transcriptText(passages) {
44
+ return passages
45
+ .map((passage) => `[${youtubeTimestamp(passage.start)}] ${passage.text}`)
46
+ .join("\n");
47
+ }
@@ -147,7 +147,7 @@ export async function npmCommand() {
147
147
  /* PATH can include directories without npm. */
148
148
  }
149
149
  }
150
- throw new Error("npm is unavailable. Install Node.js with npm to use in-app updates.");
150
+ throw new Error("npm was not found, so in-app updates are off. Install Node.js with npm (https://nodejs.org), or update from the terminal: npx @gentbajko/slopify@latest");
151
151
  }
152
152
  export async function updateCommitted(dataDir, version, token) {
153
153
  try {
@@ -7,7 +7,7 @@ export function createUpdater(deps) {
7
7
  let status = deps.candidate?.pending === true ? "restarting" : "idle";
8
8
  let activated = deps.candidate?.pending !== true;
9
9
  let installError = deps.previousUpdateFailed
10
- ? "The update did not start. The previous version and database were restored. You can try again."
10
+ ? "The new version did not start, so Slopify put back the previous version and database. You can try again; the reason is in logs/updates.log inside your Slopify data folder."
11
11
  : undefined;
12
12
  let error = installError;
13
13
  let checking;
@@ -56,7 +56,8 @@ export function createUpdater(deps) {
56
56
  }
57
57
  catch {
58
58
  status = "error";
59
- error = "Could not check for updates. Check your connection and try again.";
59
+ error =
60
+ "Could not check for updates: the npm registry (registry.npmjs.org) did not answer properly. Check your internet connection and try again.";
60
61
  deps.report(error);
61
62
  }
62
63
  finally {
@@ -143,7 +144,7 @@ export function createUpdater(deps) {
143
144
  .catch(() => {
144
145
  status = "error";
145
146
  error =
146
- "The update could not be completed. Your current installation is still available. Try again or restart Slopify.";
147
+ "The update could not be installed, so Slopify kept your current version. Check your internet connection and try again; the reason is in logs/updates.log inside your Slopify data folder.";
147
148
  installError = error;
148
149
  deps.report(error);
149
150
  });
@@ -111,15 +111,17 @@ function installPackage(plan, directory) {
111
111
  timeout: 15 * 60_000,
112
112
  env: { ...process.env, npm_config_update_notifier: "false" },
113
113
  });
114
- child.once("error", () => reject(new Error("npm could not be started.")));
115
- child.once("close", (code) => code === 0 ? resolve() : reject(new Error("npm could not install the update.")));
114
+ child.once("error", () => reject(new Error(`npm (${plan.npm.file}) could not be started. Check that Node.js and npm are installed.`)));
115
+ child.once("close", (code) => code === 0
116
+ ? resolve()
117
+ : reject(new Error(`npm could not install Slopify ${plan.version} (exit code ${code}). Check your internet connection (npm must reach registry.npmjs.org) and try again.`)));
116
118
  });
117
119
  }
118
120
  async function waitForServer(plan, version, exited) {
119
121
  const until = Date.now() + 60_000;
120
122
  while (Date.now() < until) {
121
123
  if (exited())
122
- throw new Error("Slopify stopped during startup.");
124
+ throw new Error(`Slopify ${version} stopped while starting up, so the previous version was kept.`);
123
125
  try {
124
126
  if (await candidateReady(serverOrigin(plan), version, plan.token, exited, globalThis.fetch))
125
127
  return;
@@ -129,7 +131,7 @@ async function waitForServer(plan, version, exited) {
129
131
  }
130
132
  await delay(250);
131
133
  }
132
- throw new Error("Slopify did not restart in time.");
134
+ throw new Error(`Slopify ${version} did not answer within 60 seconds of starting.`);
133
135
  }
134
136
  function serverOrigin(plan) {
135
137
  const host = plan.host === "0.0.0.0" ? "127.0.0.1" : plan.host === "::" ? "::1" : plan.host;
@@ -0,0 +1 @@
1
+ @font-face{font-family:Barlow;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/barlow-vietnamese-400-normal-Dpl4UHAZ.woff2)format("woff2"),url(/assets/barlow-vietnamese-400-normal-BFeobeCK.woff)format("woff");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Barlow;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/barlow-latin-ext-400-normal-HxX4XjxC.woff2)format("woff2"),url(/assets/barlow-latin-ext-400-normal-CvBsJvxq.woff)format("woff");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Barlow;font-style:normal;font-display:swap;font-weight:400;src:url(/assets/barlow-latin-400-normal-qiz4-Cze.woff2)format("woff2"),url(/assets/barlow-latin-400-normal-fsAxiSwU.woff)format("woff");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Barlow;font-style:normal;font-display:swap;font-weight:500;src:url(/assets/barlow-vietnamese-500-normal-zTViEIzf.woff2)format("woff2"),url(/assets/barlow-vietnamese-500-normal-GNfB7rCE.woff)format("woff");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Barlow;font-style:normal;font-display:swap;font-weight:500;src:url(/assets/barlow-latin-ext-500-normal-DOaysfXq.woff2)format("woff2"),url(/assets/barlow-latin-ext-500-normal-CJPcKP2Q.woff)format("woff");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Barlow;font-style:normal;font-display:swap;font-weight:500;src:url(/assets/barlow-latin-500-normal-BPAOfeC8.woff2)format("woff2"),url(/assets/barlow-latin-500-normal-C1h8hMer.woff)format("woff");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Barlow;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/barlow-vietnamese-600-normal-DcjprdFV.woff2)format("woff2"),url(/assets/barlow-vietnamese-600-normal-CA_GiK2e.woff)format("woff");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Barlow;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/barlow-latin-ext-600-normal-B8NK_A3D.woff2)format("woff2"),url(/assets/barlow-latin-ext-600-normal-DMVRjfRT.woff)format("woff");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Barlow;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/barlow-latin-600-normal-DILqtrty.woff2)format("woff2"),url(/assets/barlow-latin-600-normal-CNwfPWQD.woff)format("woff");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Barlow;font-style:normal;font-display:swap;font-weight:700;src:url(/assets/barlow-vietnamese-700-normal-D6euyNzi.woff2)format("woff2"),url(/assets/barlow-vietnamese-700-normal-4Jt4k04K.woff)format("woff");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Barlow;font-style:normal;font-display:swap;font-weight:700;src:url(/assets/barlow-latin-ext-700-normal-BLuWmldJ.woff2)format("woff2"),url(/assets/barlow-latin-ext-700-normal-CctuGmmz.woff)format("woff");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Barlow;font-style:normal;font-display:swap;font-weight:700;src:url(/assets/barlow-latin-700-normal-A9pxMQ4z.woff2)format("woff2"),url(/assets/barlow-latin-700-normal-__SGTsZ1.woff)format("woff");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Barlow;font-style:normal;font-display:swap;font-weight:800;src:url(/assets/barlow-vietnamese-800-normal-Cl1Mc_Dv.woff2)format("woff2"),url(/assets/barlow-vietnamese-800-normal-D0VWpbij.woff)format("woff");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Barlow;font-style:normal;font-display:swap;font-weight:800;src:url(/assets/barlow-latin-ext-800-normal-BiucknKG.woff2)format("woff2"),url(/assets/barlow-latin-ext-800-normal-D7I3yvUw.woff)format("woff");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Barlow;font-style:normal;font-display:swap;font-weight:800;src:url(/assets/barlow-latin-800-normal-s1sAMnoV.woff2)format("woff2"),url(/assets/barlow-latin-800-normal-BdVooDN4.woff)format("woff");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Barlow Condensed;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/barlow-condensed-vietnamese-600-normal-A5AYRdjN.woff2)format("woff2"),url(/assets/barlow-condensed-vietnamese-600-normal-CNlPk46_.woff)format("woff");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Barlow Condensed;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/barlow-condensed-latin-ext-600-normal-18ESti3H.woff2)format("woff2"),url(/assets/barlow-condensed-latin-ext-600-normal-Clv9cIcR.woff)format("woff");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Barlow Condensed;font-style:normal;font-display:swap;font-weight:600;src:url(/assets/barlow-condensed-latin-600-normal-DepVgxBB.woff2)format("woff2"),url(/assets/barlow-condensed-latin-600-normal-BFJEwTuo.woff)format("woff");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@font-face{font-family:Barlow Condensed;font-style:normal;font-display:swap;font-weight:700;src:url(/assets/barlow-condensed-vietnamese-700-normal-DYeBwlKR.woff2)format("woff2"),url(/assets/barlow-condensed-vietnamese-700-normal-DhIzd8Tb.woff)format("woff");unicode-range:U+102-103,U+110-111,U+128-129,U+168-169,U+1A0-1A1,U+1AF-1B0,U+300-301,U+303-304,U+308-309,U+323,U+329,U+1EA0-1EF9,U+20AB}@font-face{font-family:Barlow Condensed;font-style:normal;font-display:swap;font-weight:700;src:url(/assets/barlow-condensed-latin-ext-700-normal-CwuXbfVR.woff2)format("woff2"),url(/assets/barlow-condensed-latin-ext-700-normal-BIHFfxf0.woff)format("woff");unicode-range:U+100-2BA,U+2BD-2C5,U+2C7-2CC,U+2CE-2D7,U+2DD-2FF,U+304,U+308,U+329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:Barlow Condensed;font-style:normal;font-display:swap;font-weight:700;src:url(/assets/barlow-condensed-latin-700-normal-v1xN8_Wq.woff2)format("woff2"),url(/assets/barlow-condensed-latin-700-normal-Dmwat-ge.woff)format("woff");unicode-range:U+??,U+131,U+152-153,U+2BB-2BC,U+2C6,U+2DA,U+2DC,U+304,U+308,U+329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-duration:initial;--tw-ease:initial;--tw-content:""}}}@layer theme{:root,:host{--font-sans:"Barlow", system-ui, sans-serif;--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--leading-relaxed:1.625;--ease-out:cubic-bezier(0, 0, .2, 1);--animate-spin:spin 1s linear infinite;--aspect-video:16 / 9;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-bg:#141416;--color-panel:#1b1b1f;--color-panel2:#202026;--color-line:#2a2a30;--color-line2:#35353d;--color-ink:#e6e3dc;--color-ink2:#9a978f;--color-ink3:#8a887f;--color-accent:#9bcb4f;--color-accent-ink:#17200b;--color-accent-edge:#6d9333;--color-done:#6f9a3a;--color-red:#e86960;--color-red-tint:#e8696014;--color-amber:#e2a247;--color-lamp-run:#9bcb4f;--color-lamp-halo:#9bcb4f2e;--color-lamp-off:#2c2c33;--color-lamp-ring:#3a3a42;--color-run-text:#9bcb4f;--color-focus:#9bcb4f;--color-shadow:#08080a3d;--color-scrim:#14141699;--color-screen:#0b0b0d;--font-condensed:"Barlow Condensed", "Barlow", system-ui, sans-serif;--text-label:12px;--text-label--line-height:1.2;--text-small:13px;--text-small--line-height:1.45;--text-body:14px;--text-body--line-height:1.45;--text-row:16px;--text-row--line-height:1.35;--text-title:18px;--text-title--line-height:1.3;--text-wordmark:20px;--text-wordmark--line-height:1.2;--text-counter:32px;--text-counter--line-height:1.1;--radius-control:4px;--radius-panel:6px;--animate-lamp-pulse:lamp-pulse 1.2s ease-in-out infinite;--animate-dialog-in:dialog-in .2s ease-out;--animate-tick-in:tick-in .15s ease-out}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring:where(:not(iframe)){outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}:root{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}body{background:var(--color-bg);color:var(--color-ink);font-family:var(--font-sans);font-size:var(--text-body);line-height:var(--text-body--line-height);margin:0}:focus-visible{outline:2px solid var(--color-focus);outline-offset:2px}[disabled],[aria-disabled=true]{opacity:.45;pointer-events:none}}@layer components;@layer utilities{.pointer-events-auto{pointer-events:auto}.pointer-events-none{pointer-events:none}.collapse{visibility:collapse}.invisible{visibility:hidden}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{inset:0}.inset-x-0{inset-inline:0}.top-0{top:0}.top-1{top:var(--spacing)}.top-1\/2{top:50%}.top-12{top:calc(var(--spacing) * 12)}.top-14{top:calc(var(--spacing) * 14)}.top-\[3px\]{top:3px}.right-0{right:0}.right-1{right:var(--spacing)}.right-4{right:calc(var(--spacing) * 4)}.right-\[8px\]{right:8px}.right-\[10px\]{right:10px}.bottom-0{bottom:0}.bottom-4{bottom:calc(var(--spacing) * 4)}.bottom-\[-16px\]{bottom:-16px}.left-1{left:var(--spacing)}.left-1\/2{left:50%}.left-4{left:calc(var(--spacing) * 4)}.z-10{z-index:10}.z-20{z-index:20}.z-30{z-index:30}.z-40{z-index:40}.z-50{z-index:50}.z-\[60\]{z-index:60}.z-\[100\]{z-index:100}.z-\[101\]{z-index:101}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-full{grid-column:1/-1}.col-start-1{grid-column-start:1}.col-start-2{grid-column-start:2}.col-start-3{grid-column-start:3}.col-start-4{grid-column-start:4}.row-start-1{grid-row-start:1}.row-start-2{grid-row-start:2}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.m-0{margin:0}.-mx-4{margin-inline:calc(var(--spacing) * -4)}.-mx-\[18px\]{margin-inline:-18px}.mx-5{margin-inline:calc(var(--spacing) * 5)}.mx-auto{margin-inline:auto}.my-2{margin-block:calc(var(--spacing) * 2)}.my-3{margin-block:calc(var(--spacing) * 3)}.-mt-3{margin-top:calc(var(--spacing) * -3)}.-mt-\[6px\]{margin-top:-6px}.mt-1{margin-top:var(--spacing)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-5{margin-top:calc(var(--spacing) * 5)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-7{margin-top:calc(var(--spacing) * 7)}.mt-\[10px\]{margin-top:10px}.mt-\[22px\]{margin-top:22px}.-mr-1{margin-right:calc(var(--spacing) * -1)}.-mb-4{margin-bottom:calc(var(--spacing) * -4)}.-mb-\[18px\]{margin-bottom:-18px}.-mb-px{margin-bottom:-1px}.mb-1{margin-bottom:var(--spacing)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-5{margin-bottom:calc(var(--spacing) * 5)}.mb-\[5px\]{margin-bottom:5px}.-ml-px{margin-left:-1px}.ml-\[6px\]{margin-left:6px}.ml-auto{margin-left:auto}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.table{display:table}.aspect-\[9\/16\]{aspect-ratio:9/16}.aspect-video{aspect-ratio:var(--aspect-video)}.size-2{width:calc(var(--spacing) * 2);height:calc(var(--spacing) * 2)}.size-3{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.size-6{width:calc(var(--spacing) * 6);height:calc(var(--spacing) * 6)}.size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.size-\[10px\]{width:10px;height:10px}.size-\[13px\]{width:13px;height:13px}.size-\[14px\]{width:14px;height:14px}.size-\[15px\]{width:15px;height:15px}.size-\[22px\]{width:22px;height:22px}.size-\[26px\]{width:26px;height:26px}.h-1{height:var(--spacing)}.h-3{height:calc(var(--spacing) * 3)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-7{height:calc(var(--spacing) * 7)}.h-8{height:calc(var(--spacing) * 8)}.h-9{height:calc(var(--spacing) * 9)}.h-10{height:calc(var(--spacing) * 10)}.h-11{height:calc(var(--spacing) * 11)}.h-12{height:calc(var(--spacing) * 12)}.h-14{height:calc(var(--spacing) * 14)}.h-28{height:calc(var(--spacing) * 28)}.h-\[2px\]{height:2px}.h-\[3px\]{height:3px}.h-\[10px\]{height:10px}.h-\[18px\]{height:18px}.h-\[78px\]{height:78px}.h-\[520px\]{height:520px}.h-\[560px\]{height:560px}.h-full{height:100%}.max-h-32{max-height:calc(var(--spacing) * 32)}.max-h-40{max-height:calc(var(--spacing) * 40)}.max-h-48{max-height:calc(var(--spacing) * 48)}.max-h-56{max-height:calc(var(--spacing) * 56)}.max-h-72{max-height:calc(var(--spacing) * 72)}.max-h-\[360px\]{max-height:360px}.max-h-\[420px\]{max-height:420px}.max-h-\[480px\]{max-height:480px}.max-h-\[min\(58vh\,560px\)\]{max-height:min(58vh,560px)}.max-h-\[min\(58vh\,640px\)\]{max-height:min(58vh,640px)}.min-h-0{min-height:0}.min-h-8{min-height:calc(var(--spacing) * 8)}.min-h-9{min-height:calc(var(--spacing) * 9)}.min-h-10{min-height:calc(var(--spacing) * 10)}.min-h-12{min-height:calc(var(--spacing) * 12)}.min-h-14{min-height:calc(var(--spacing) * 14)}.min-h-32{min-height:calc(var(--spacing) * 32)}.min-h-48{min-height:calc(var(--spacing) * 48)}.min-h-64{min-height:calc(var(--spacing) * 64)}.min-h-\[520px\]{min-height:520px}.min-h-screen{min-height:100vh}.w-2\/5{width:40%}.w-6{width:calc(var(--spacing) * 6)}.w-7{width:calc(var(--spacing) * 7)}.w-10{width:calc(var(--spacing) * 10)}.w-12{width:calc(var(--spacing) * 12)}.w-14{width:calc(var(--spacing) * 14)}.w-16{width:calc(var(--spacing) * 16)}.w-20{width:calc(var(--spacing) * 20)}.w-24{width:calc(var(--spacing) * 24)}.w-28{width:calc(var(--spacing) * 28)}.w-40{width:calc(var(--spacing) * 40)}.w-48{width:calc(var(--spacing) * 48)}.w-56{width:calc(var(--spacing) * 56)}.w-64{width:calc(var(--spacing) * 64)}.w-72{width:calc(var(--spacing) * 72)}.w-\[19\%\]{width:19%}.w-\[24\%\]{width:24%}.w-\[26\%\]{width:26%}.w-\[38\%\]{width:38%}.w-\[40ch\]{width:40ch}.w-\[52px\]{width:52px}.w-\[64px\]{width:64px}.w-\[72px\]{width:72px}.w-\[76px\]{width:76px}.w-\[80px\]{width:80px}.w-\[90px\]{width:90px}.w-\[100px\]{width:100px}.w-\[104px\]{width:104px}.w-\[180px\]{width:180px}.w-\[340px\]{width:340px}.w-\[calc\(100\%-2rem\)\]{width:calc(100% - 2rem)}.w-\[min\(340px\,calc\(100vw-24px\)\)\]{width:min(340px,100vw - 24px)}.w-\[min\(360px\,calc\(100vw-32px\)\)\]{width:min(360px,100vw - 32px)}.w-\[min\(560px\,calc\(100vw-24px\)\)\]{width:min(560px,100vw - 24px)}.w-auto{width:auto}.w-fit{width:fit-content}.w-full{width:100%}.max-w-\[75ch\]{max-width:75ch}.max-w-\[180px\]{max-width:180px}.max-w-\[260px\]{max-width:260px}.max-w-\[280px\]{max-width:280px}.max-w-\[320px\]{max-width:320px}.max-w-\[360px\]{max-width:360px}.max-w-\[400px\]{max-width:400px}.max-w-\[480px\]{max-width:480px}.max-w-\[520px\]{max-width:520px}.max-w-\[620px\]{max-width:620px}.max-w-\[640px\]{max-width:640px}.max-w-\[720px\]{max-width:720px}.max-w-\[1200px\]{max-width:1200px}.max-w-\[1440px\]{max-width:1440px}.max-w-full{max-width:100%}.max-w-md{max-width:var(--container-md)}.min-w-0{min-width:0}.min-w-8{min-width:calc(var(--spacing) * 8)}.min-w-16{min-width:calc(var(--spacing) * 16)}.min-w-\[88px\]{min-width:88px}.min-w-\[112px\]{min-width:112px}.min-w-\[120px\]{min-width:120px}.min-w-\[160px\]{min-width:160px}.min-w-\[200px\]{min-width:200px}.min-w-\[740px\]{min-width:740px}.min-w-\[min\(100\%\,260px\)\]{min-width:min(100%,260px)}.min-w-\[var\(--radix-select-trigger-width\)\]{min-width:var(--radix-select-trigger-width)}.flex-1{flex:1}.shrink-0{flex-shrink:0}.basis-full{flex-basis:100%}.table-fixed{table-layout:fixed}.border-collapse{border-collapse:collapse}.origin-left{transform-origin:0}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.-translate-y-1\/2{--tw-translate-y:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.animate-lamp-pulse{animation:var(--animate-lamp-pulse)}.animate-none{animation:none}.animate-spin{animation:var(--animate-spin)}.animate-tick-in{animation:var(--animate-tick-in)}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.resize{resize:both}.resize-y{resize:vertical}.\[scrollbar-width\:none\]{scrollbar-width:none}.\[scrollbar-width\:thin\]{scrollbar-width:thin}.\[scrollbar-gutter\:stable\]{scrollbar-gutter:stable}.list-inside{list-style-position:inside}.list-decimal{list-style-type:decimal}.list-disc{list-style-type:disc}.list-none{list-style-type:none}.appearance-none{appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.grid-cols-\[10px_minmax\(0\,1fr\)_auto\]{grid-template-columns:10px minmax(0,1fr) auto}.grid-cols-\[14px_minmax\(0\,1fr\)_auto_32px\]{grid-template-columns:14px minmax(0,1fr) auto 32px}.grid-cols-\[24px_1fr_auto\]{grid-template-columns:24px 1fr auto}.grid-cols-\[60px_minmax\(0\,1fr\)_auto\]{grid-template-columns:60px minmax(0,1fr) auto}.grid-cols-\[90px_minmax\(0\,1fr\)_auto\]{grid-template-columns:90px minmax(0,1fr) auto}.grid-cols-\[112px_repeat\(7\,minmax\(0\,1fr\)\)\]{grid-template-columns:112px repeat(7,minmax(0,1fr))}.grid-cols-\[minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,1fr)}.grid-cols-\[minmax\(0\,1fr\)_32px\]{grid-template-columns:minmax(0,1fr) 32px}.grid-cols-\[minmax\(0\,1fr\)_auto_32px\]{grid-template-columns:minmax(0,1fr) auto 32px}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.items-stretch{align-items:stretch}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-5{gap:calc(var(--spacing) * 5)}.gap-6{gap:calc(var(--spacing) * 6)}.gap-8{gap:calc(var(--spacing) * 8)}.gap-\[5px\]{gap:5px}.gap-\[6px\]{gap:6px}.gap-\[10px\]{gap:10px}.gap-\[14px\]{gap:14px}.gap-\[18px\]{gap:18px}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(var(--spacing) * var(--tw-space-y-reverse));margin-block-end:calc(var(--spacing) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-5>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)))}.gap-x-2{column-gap:calc(var(--spacing) * 2)}.gap-x-3{column-gap:calc(var(--spacing) * 3)}.gap-x-4{column-gap:calc(var(--spacing) * 4)}.gap-x-5{column-gap:calc(var(--spacing) * 5)}.gap-x-6{column-gap:calc(var(--spacing) * 6)}.gap-x-\[14px\]{column-gap:14px}.gap-y-1{row-gap:var(--spacing)}.gap-y-2{row-gap:calc(var(--spacing) * 2)}.gap-y-4{row-gap:calc(var(--spacing) * 4)}.gap-y-\[4px\]{row-gap:4px}.gap-y-\[6px\]{row-gap:6px}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-line>:not(:last-child)){border-color:var(--color-line)}.self-start{align-self:flex-start}.self-stretch{align-self:stretch}.justify-self-end{justify-self:flex-end}.justify-self-start{justify-self:flex-start}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-visible{overflow:visible}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-\[2px\]{border-radius:2px}.rounded-control{border-radius:var(--radius-control)}.rounded-full{border-radius:2147483647px}.rounded-panel{border-radius:var(--radius-panel)}.rounded-l-none{border-top-left-radius:0;border-bottom-left-radius:0}.rounded-r-none{border-top-right-radius:0;border-bottom-right-radius:0}.rounded-b-panel{border-bottom-right-radius:var(--radius-panel);border-bottom-left-radius:var(--radius-panel)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-y{border-block-style:var(--tw-border-style);border-block-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-t-0{border-top-style:var(--tw-border-style);border-top-width:0}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-b-2{border-bottom-style:var(--tw-border-style);border-bottom-width:2px}.border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.border-l-0{border-left-style:var(--tw-border-style);border-left-width:0}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.\!border-lamp-run{border-color:var(--color-lamp-run)!important}.border-accent{border-color:var(--color-accent)}.border-current{border-color:currentColor}.border-done{border-color:var(--color-done)}.border-lamp-run{border-color:var(--color-lamp-run)}.border-line{border-color:var(--color-line)}.border-line2{border-color:var(--color-line2)}.border-red{border-color:var(--color-red)}.border-red\/30{border-color:#e869604d}@supports (color:color-mix(in lab, red, red)){.border-red\/30{border-color:color-mix(in oklab, var(--color-red) 30%, transparent)}}.border-transparent{border-color:#0000}.bg-accent{background-color:var(--color-accent)}.bg-amber{background-color:var(--color-amber)}.bg-bg\/40{background-color:#14141666}@supports (color:color-mix(in lab, red, red)){.bg-bg\/40{background-color:color-mix(in oklab, var(--color-bg) 40%, transparent)}}.bg-current{background-color:currentColor}.bg-done{background-color:var(--color-done)}.bg-lamp-off{background-color:var(--color-lamp-off)}.bg-lamp-run{background-color:var(--color-lamp-run)}.bg-panel{background-color:var(--color-panel)}.bg-panel\/90{background-color:#1b1b1fe6}@supports (color:color-mix(in lab, red, red)){.bg-panel\/90{background-color:color-mix(in oklab, var(--color-panel) 90%, transparent)}}.bg-panel2{background-color:var(--color-panel2)}.bg-red{background-color:var(--color-red)}.bg-red-tint{background-color:var(--color-red-tint)}.bg-screen{background-color:var(--color-screen)}.bg-scrim{background-color:var(--color-scrim)}.bg-transparent{background-color:#0000}.object-contain{object-fit:contain}.object-cover{object-fit:cover}.p-0{padding:0}.p-1{padding:var(--spacing)}.p-2{padding:calc(var(--spacing) * 2)}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-8{padding:calc(var(--spacing) * 8)}.p-\[10px\]{padding:10px}.p-\[18px\]{padding:18px}.px-1{padding-inline:var(--spacing)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-4{padding-inline:calc(var(--spacing) * 4)}.px-5{padding-inline:calc(var(--spacing) * 5)}.px-\[4px\]{padding-inline:4px}.px-\[7px\]{padding-inline:7px}.px-\[10px\]{padding-inline:10px}.px-\[14px\]{padding-inline:14px}.px-\[18px\]{padding-inline:18px}.py-1{padding-block:var(--spacing)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-5{padding-block:calc(var(--spacing) * 5)}.py-6{padding-block:calc(var(--spacing) * 6)}.py-7{padding-block:calc(var(--spacing) * 7)}.py-\[1px\]{padding-block:1px}.py-\[2px\]{padding-block:2px}.py-\[3px\]{padding-block:3px}.py-\[5px\]{padding-block:5px}.py-\[6px\]{padding-block:6px}.py-\[10px\]{padding-block:10px}.py-\[14px\]{padding-block:14px}.pt-1{padding-top:var(--spacing)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-5{padding-top:calc(var(--spacing) * 5)}.pt-6{padding-top:calc(var(--spacing) * 6)}.pr-3{padding-right:calc(var(--spacing) * 3)}.pr-4{padding-right:calc(var(--spacing) * 4)}.pr-8{padding-right:calc(var(--spacing) * 8)}.pr-\[26px\]{padding-right:26px}.pb-1{padding-bottom:var(--spacing)}.pb-2{padding-bottom:calc(var(--spacing) * 2)}.pb-3{padding-bottom:calc(var(--spacing) * 3)}.pb-10{padding-bottom:calc(var(--spacing) * 10)}.pb-\[max\(8px\,env\(safe-area-inset-bottom\)\)\]{padding-bottom:max(8px, env(safe-area-inset-bottom))}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-5{padding-left:calc(var(--spacing) * 5)}.pl-6{padding-left:calc(var(--spacing) * 6)}.pl-7{padding-left:calc(var(--spacing) * 7)}.pl-\[10px\]{padding-left:10px}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.align-top{vertical-align:top}.engraved{font-family:var(--font-condensed);font-size:var(--text-label);line-height:var(--text-label--line-height);letter-spacing:.09em;text-transform:uppercase;font-weight:600}.font-mono{font-family:var(--font-mono)}.font-sans{font-family:var(--font-sans)}.text-body{font-size:var(--text-body);line-height:var(--tw-leading,var(--text-body--line-height))}.text-counter{font-size:var(--text-counter);line-height:var(--tw-leading,var(--text-counter--line-height))}.text-label{font-size:var(--text-label);line-height:var(--tw-leading,var(--text-label--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-row{font-size:var(--text-row);line-height:var(--tw-leading,var(--text-row--line-height))}.text-small{font-size:var(--text-small);line-height:var(--tw-leading,var(--text-small--line-height))}.text-title{font-size:var(--text-title);line-height:var(--tw-leading,var(--text-title--line-height))}.text-wordmark{font-size:var(--text-wordmark);line-height:var(--tw-leading,var(--text-wordmark--line-height))}.text-\[10px\]{font-size:10px}.leading-\[1\.5\]{--tw-leading:1.5;line-height:1.5}.leading-\[1\.45\]{--tw-leading:1.45;line-height:1.45}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[-0\.01em\]{--tw-tracking:-.01em;letter-spacing:-.01em}.tracking-\[-0\.02em\]{--tw-tracking:-.02em;letter-spacing:-.02em}.tracking-\[0\.02em\]{--tw-tracking:.02em;letter-spacing:.02em}.text-pretty{text-wrap:pretty}.\[overflow-wrap\:anywhere\]{overflow-wrap:anywhere}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.\!text-ink{color:var(--color-ink)!important}.text-accent-ink{color:var(--color-accent-ink)}.text-amber{color:var(--color-amber)}.text-done{color:var(--color-done)}.text-ink{color:var(--color-ink)}.text-ink2{color:var(--color-ink2)}.text-ink3{color:var(--color-ink3)}.text-lamp-run{color:var(--color-lamp-run)}.text-red{color:var(--color-red)}.text-run-text{color:var(--color-run-text)}.text-transparent{color:#0000}.text-white{color:var(--color-white)}.capitalize{text-transform:capitalize}.uppercase{text-transform:uppercase}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,)}.underline{text-decoration-line:underline}.decoration-red{-webkit-text-decoration-color:var(--color-red);-webkit-text-decoration-color:var(--color-red);-webkit-text-decoration-color:var(--color-red);text-decoration-color:var(--color-red)}.decoration-2{text-decoration-thickness:2px}.underline-offset-2{text-underline-offset:2px}.underline-offset-4{text-underline-offset:4px}.underline-offset-\[3px\]{text-underline-offset:3px}.caret-ink{caret-color:var(--color-ink)}.accent-accent{accent-color:var(--color-accent)}.opacity-0{opacity:0}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\[-12px_0_28px_-12px_var\(--color-shadow\)\]{--tw-shadow:-12px 0 28px -12px var(--tw-shadow-color,var(--color-shadow));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\[0_-6px_14px_-10px_var\(--color-shadow\)\]{--tw-shadow:0 -6px 14px -10px var(--tw-shadow-color,var(--color-shadow));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\[0_0_0_3px_var\(--color-lamp-halo\)\]{--tw-shadow:0 0 0 3px var(--tw-shadow-color,var(--color-lamp-halo));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\[0_4px_0_var\(--color-accent-edge\)\]{--tw-shadow:0 4px 0 var(--tw-shadow-color,var(--color-accent-edge));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\[0_8px_24px_var\(--color-shadow\)\]{--tw-shadow:0 8px 24px var(--tw-shadow-color,var(--color-shadow));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\[inset_0_-2px_0_var\(--color-lamp-run\)\]{--tw-shadow:inset 0 -2px 0 var(--tw-shadow-color,var(--color-lamp-run));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\[inset_0_0_0_1px_var\(--color-lamp-ring\)\]{--tw-shadow:inset 0 0 0 1px var(--tw-shadow-color,var(--color-lamp-ring));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-\[inset_2px_0_0_var\(--color-lamp-run\)\]{--tw-shadow:inset 2px 0 0 var(--tw-shadow-color,var(--color-lamp-run));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline-hidden{--tw-outline-style:none;outline-style:none}@media (forced-colors:active){.outline-hidden{outline-offset:2px;outline:2px solid #0000}}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.transition-\[width\]{transition-property:width;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-150{--tw-duration:.15s;transition-duration:.15s}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-\[250ms\]{--tw-duration:.25s;transition-duration:.25s}.ease-out{--tw-ease:var(--ease-out);transition-timing-function:var(--ease-out)}.outline-none{--tw-outline-style:none;outline-style:none}.select-none{-webkit-user-select:none;user-select:none}.select-text{-webkit-user-select:text;user-select:text}@media (hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}}.file\:mr-2::file-selector-button{margin-right:calc(var(--spacing) * 2)}.file\:mr-3::file-selector-button{margin-right:calc(var(--spacing) * 3)}.file\:h-8::file-selector-button{height:calc(var(--spacing) * 8)}.file\:rounded-control::file-selector-button{border-radius:var(--radius-control)}.file\:border::file-selector-button{border-style:var(--tw-border-style);border-width:1px}.file\:border-line2::file-selector-button{border-color:var(--color-line2)}.file\:bg-panel2::file-selector-button{background-color:var(--color-panel2)}.file\:px-2::file-selector-button{padding-inline:calc(var(--spacing) * 2)}.file\:px-3::file-selector-button{padding-inline:calc(var(--spacing) * 3)}.file\:py-1::file-selector-button{padding-block:var(--spacing)}.file\:text-ink::file-selector-button{color:var(--color-ink)}.placeholder\:text-ink3::placeholder{color:var(--color-ink3)}.after\:absolute:after{content:var(--tw-content);position:absolute}.after\:inset-0:after{content:var(--tw-content);inset:0}.after\:content-\[\'\'\]:after{--tw-content:"";content:var(--tw-content)}.first\:border-t-0:first-child{border-top-style:var(--tw-border-style);border-top-width:0}.last\:border-r:last-child{border-right-style:var(--tw-border-style);border-right-width:1px}.last\:border-r-0:last-child{border-right-style:var(--tw-border-style);border-right-width:0}.last\:border-b-0:last-child{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.focus-within\:opacity-100:focus-within{opacity:1}@media (hover:hover){.hover\:border-ink3:hover{border-color:var(--color-ink3)}.hover\:bg-accent-edge:hover{background-color:var(--color-accent-edge)}.hover\:bg-panel2:hover{background-color:var(--color-panel2)}.hover\:text-ink:hover{color:var(--color-ink)}.hover\:underline:hover{text-decoration-line:underline}}.focus\:bg-panel2:focus{background-color:var(--color-panel2)}.focus-visible\:outline-offset-\[-2px\]:focus-visible{outline-offset:-2px}.focus-visible\:outline-offset-\[-3px\]:focus-visible{outline-offset:-3px}.active\:translate-y-\[3px\]:active{--tw-translate-y:3px;translate:var(--tw-translate-x) var(--tw-translate-y)}.active\:shadow-\[0_1px_0_var\(--color-accent-edge\)\]:active{--tw-shadow:0 1px 0 var(--tw-shadow-color,var(--color-accent-edge));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-40:disabled{opacity:.4}.disabled\:opacity-50:disabled{opacity:.5}.disabled\:opacity-60:disabled{opacity:.6}.aria-invalid\:border-red[aria-invalid=true]{border-color:var(--color-red)}.data-disabled\:opacity-45[data-disabled]{opacity:.45}.data-highlighted\:bg-panel2[data-highlighted]{background-color:var(--color-panel2)}.data-\[placeholder\]\:text-ink3[data-placeholder]{color:var(--color-ink3)}.data-\[state\=on\]\:border-focus[data-state=on]{border-color:var(--color-focus)}.data-\[state\=on\]\:bg-panel2[data-state=on]{background-color:var(--color-panel2)}.data-\[state\=on\]\:text-ink[data-state=on]{color:var(--color-ink)}.data-\[state\=on\]\:text-run-text[data-state=on]{color:var(--color-run-text)}.data-\[state\=on\]\:shadow-\[inset_0_-2px_0_var\(--color-lamp-run\)\][data-state=on]{--tw-shadow:inset 0 -2px 0 var(--tw-shadow-color,var(--color-lamp-run));box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.data-\[state\=on\]\:shadow-none[data-state=on]{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.data-\[state\=open\]\:animate-dialog-in[data-state=open]{animation:var(--animate-dialog-in)}.data-\[state\=open\]\:animate-tick-in[data-state=open]{animation:var(--animate-tick-in)}@media (prefers-reduced-motion:reduce){.motion-reduce\:animate-none{animation:none}.motion-reduce\:transition-none{transition-property:none}.motion-reduce\:active\:translate-y-0:active{--tw-translate-y:0px;translate:var(--tw-translate-x) var(--tw-translate-y)}}@media not all and (width>=1099px){.max-\[1099px\]\:min-h-11{min-height:calc(var(--spacing) * 11)}}@media not all and (width>=700px){.max-\[700px\]\:col-span-3{grid-column:span 3/span 3}.max-\[700px\]\:justify-self-start{justify-self:flex-start}}@media not all and (width>=480px){.max-\[480px\]\:sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}}@media (width>=700px){.min-\[700px\]\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (width>=900px){.min-\[900px\]\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.min-\[900px\]\:grid-cols-\[200px_minmax\(0\,1fr\)\]{grid-template-columns:200px minmax(0,1fr)}}@media (width>=1100px){.min-\[1100px\]\:not-sr-only{clip-path:none;white-space:normal;width:auto;height:auto;margin:0;padding:0;position:static;overflow:visible}.min-\[1100px\]\:sticky{position:sticky}.min-\[1100px\]\:top-16{top:calc(var(--spacing) * 16)}.min-\[1100px\]\:grid-cols-\[minmax\(0\,1fr\)_300px\]{grid-template-columns:minmax(0,1fr) 300px}}@media (width>=40rem){.sm\:not-sr-only{clip-path:none;white-space:normal;width:auto;height:auto;margin:0;padding:0;position:static;overflow:visible}.sm\:col-span-2{grid-column:span 2/span 2}.sm\:-mx-6{margin-inline:calc(var(--spacing) * -6)}.sm\:flex{display:flex}.sm\:w-\[min\(440px\,100vw\)\]{width:min(440px,100vw)}.sm\:w-\[min\(560px\,100vw\)\]{width:min(560px,100vw)}.sm\:w-auto{width:auto}.sm\:w-full{width:100%}.sm\:flex-1{flex:1}.sm\:flex-none{flex:none}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-\[112px_minmax\(0\,1fr\)\]{grid-template-columns:112px minmax(0,1fr)}.sm\:grid-cols-\[145px_minmax\(0\,1fr\)_auto\]{grid-template-columns:145px minmax(0,1fr) auto}.sm\:grid-cols-\[240px_1fr\]{grid-template-columns:240px 1fr}.sm\:flex-col{flex-direction:column}.sm\:items-start{align-items:flex-start}.sm\:justify-center{justify-content:center}.sm\:gap-1{gap:var(--spacing)}.sm\:gap-3{gap:calc(var(--spacing) * 3)}.sm\:gap-6{gap:calc(var(--spacing) * 6)}.sm\:border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.sm\:border-b-0{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.sm\:px-6{padding-inline:calc(var(--spacing) * 6)}}@media (width>=48rem){.md\:sticky{position:sticky}.md\:top-16{top:calc(var(--spacing) * 16)}.md\:col-span-1{grid-column:span 1/span 1}.md\:col-start-2{grid-column-start:2}.md\:col-start-3{grid-column-start:3}.md\:col-start-4{grid-column-start:4}.md\:col-start-5{grid-column-start:5}.md\:row-start-1{grid-row-start:1}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:grid-cols-\[14px_minmax\(0\,1fr\)_140px_100px_32px\]{grid-template-columns:14px minmax(0,1fr) 140px 100px 32px}.md\:grid-cols-\[170px_150px_minmax\(0\,1fr\)_auto\]{grid-template-columns:170px 150px minmax(0,1fr) auto}.md\:grid-cols-\[180px_minmax\(0\,1fr\)\]{grid-template-columns:180px minmax(0,1fr)}.md\:grid-cols-\[200px_minmax\(0\,1fr\)\]{grid-template-columns:200px minmax(0,1fr)}.md\:grid-cols-\[260px_70px_minmax\(0\,1fr\)_32px\]{grid-template-columns:260px 70px minmax(0,1fr) 32px}.md\:grid-cols-\[260px_minmax\(0\,1fr\)_32px\]{grid-template-columns:260px minmax(0,1fr) 32px}.md\:flex-col{flex-direction:column}.md\:self-start{align-self:flex-start}.md\:border-b-0{border-bottom-style:var(--tw-border-style);border-bottom-width:0}.md\:pb-0{padding-bottom:0}}@media (width>=64rem){.lg\:sticky{position:sticky}.lg\:top-16{top:calc(var(--spacing) * 16)}.lg\:grid-cols-\[minmax\(0\,1fr\)_360px\]{grid-template-columns:minmax(0,1fr) 360px}}.\[\&_\[data-slot\=toggle-group\]\]\:max-w-full [data-slot=toggle-group]{max-width:100%}.\[\&_\[data-slot\=toggle-group\]\]\:flex-wrap [data-slot=toggle-group]{flex-wrap:wrap}.\[\&_a\]\:font-semibold a{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.\[\&_a\]\:text-accent-ink a{color:var(--color-accent-ink)}.\[\&_button\]\:min-h-8 button{min-height:calc(var(--spacing) * 8)}.\[\&_button\]\:min-w-8 button{min-width:calc(var(--spacing) * 8)}.\[\&_button\]\:text-accent-ink button{color:var(--color-accent-ink)}@media not all and (width>=1099px){.max-\[1099px\]\:\[\&_button\]\:min-h-11 button{min-height:calc(var(--spacing) * 11)}}.\[\&_code\]\:text-ink code{color:var(--color-ink)}@media not all and (width>=1099px){.max-\[1099px\]\:\[\&_input\:not\(\[type\=checkbox\]\)\]\:min-h-11 input:not([type=checkbox]){min-height:calc(var(--spacing) * 11)}}.\[\&_p\+p\]\:mt-2 p+p{margin-top:calc(var(--spacing) * 2)}@media not all and (width>=1099px){.max-\[1099px\]\:\[\&_select\]\:min-h-11 select,.max-\[1099px\]\:\[\&_summary\]\:min-h-11 summary{min-height:calc(var(--spacing) * 11)}}.\[\&\>div\:empty\]\:hidden>div:empty{display:none}.\[\&\>span\]\:w-full>span{width:100%}}@media (prefers-color-scheme:light){:root:not([data-theme=dark]){--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--color-bg:#f4f2ec;--color-panel:#fbfaf7;--color-panel2:#eceae3;--color-line:#dad8d0;--color-line2:#c9c7be;--color-ink:#1c1c1e;--color-ink2:#5c5b56;--color-ink3:#6b6a64;--color-accent-edge:#6e9a33;--color-done:#55673f;--color-red:#b8332c;--color-red-tint:#b8332c14;--color-amber:#8f5e12;--color-lamp-run:#5f8a2b;--color-lamp-halo:#5f8a2b2e;--color-lamp-off:#dedcd4;--color-lamp-ring:#c9c7be;--color-run-text:#3f6b1c;--color-focus:#5f8a2b;--color-shadow:#3c37281f;--color-scrim:#3c3a3273;--color-screen:#1c1c1e}}:root[data-theme=light]{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light;--color-bg:#f4f2ec;--color-panel:#fbfaf7;--color-panel2:#eceae3;--color-line:#dad8d0;--color-line2:#c9c7be;--color-ink:#1c1c1e;--color-ink2:#5c5b56;--color-ink3:#6b6a64;--color-accent-edge:#6e9a33;--color-done:#55673f;--color-red:#b8332c;--color-red-tint:#b8332c14;--color-amber:#8f5e12;--color-lamp-run:#5f8a2b;--color-lamp-halo:#5f8a2b2e;--color-lamp-off:#dedcd4;--color-lamp-ring:#c9c7be;--color-run-text:#3f6b1c;--color-focus:#5f8a2b;--color-shadow:#3c37281f;--color-scrim:#3c3a3273;--color-screen:#1c1c1e}:root[data-theme=dark]{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}@keyframes lamp-pulse{50%{box-shadow:0 0 0 6px #0000}}@keyframes tick-in{0%{opacity:0}}@keyframes dialog-in{0%{opacity:0;transform:translate(-50%,calc(4px - 50%))}}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@property --tw-content{syntax:"*";inherits:false;initial-value:""}@keyframes spin{to{transform:rotate(360deg)}}