@mevdragon/vidfarm-devcli 0.15.0 → 0.17.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 (37) hide show
  1. package/.agents/skills/farmville-saas-ux/SKILL.md +156 -0
  2. package/.agents/skills/farmville-saas-ux/assets/starter.html +294 -0
  3. package/.agents/skills/farmville-saas-ux/references/components.md +340 -0
  4. package/.agents/skills/farmville-saas-ux/references/porting-guide.md +121 -0
  5. package/.agents/skills/farmville-saas-ux/references/tokens.md +271 -0
  6. package/.agents/skills/vidfarm-media/SKILL.md +2 -1
  7. package/.agents/skills/vidfarm-media/references/tts.md +20 -1
  8. package/demo/dist/app.css +1 -1
  9. package/demo/dist/app.js +74 -74
  10. package/dist/src/account-pages-legacy.js +2 -2
  11. package/dist/src/app.js +682 -12
  12. package/dist/src/config.js +13 -0
  13. package/dist/src/editor-chat.js +3 -2
  14. package/dist/src/frontend/homepage-client.js +211 -2
  15. package/dist/src/frontend/homepage-shared.js +197 -0
  16. package/dist/src/frontend/homepage-store.js +39 -1
  17. package/dist/src/frontend/homepage-view.js +150 -5
  18. package/dist/src/homepage.js +234 -18
  19. package/dist/src/landing-page.js +367 -0
  20. package/dist/src/primitive-registry.js +278 -2
  21. package/dist/src/reskin/agency-page.js +255 -0
  22. package/dist/src/reskin/calendar-page.js +252 -0
  23. package/dist/src/reskin/chat-page.js +414 -0
  24. package/dist/src/reskin/discover-page.js +380 -0
  25. package/dist/src/reskin/document.js +74 -0
  26. package/dist/src/reskin/help-page.js +318 -0
  27. package/dist/src/reskin/index-page.js +62 -0
  28. package/dist/src/reskin/job-runs-page.js +249 -0
  29. package/dist/src/reskin/library-page.js +515 -0
  30. package/dist/src/reskin/login-page.js +225 -0
  31. package/dist/src/reskin/pricing-page.js +359 -0
  32. package/dist/src/reskin/settings-page.js +353 -0
  33. package/dist/src/reskin/theme.js +265 -0
  34. package/dist/src/services/serverless-records.js +63 -0
  35. package/dist/src/services/swipe-customize.js +434 -0
  36. package/package.json +1 -1
  37. package/public/assets/homepage-client-app.js +23 -23
@@ -0,0 +1,318 @@
1
+ // /reskin/help — the vidfarm Help / Getting-started center, ported into the
2
+ // farmville design system using ONLY shared rk-* primitives + a page-scoped
3
+ // CSS block + a small self-contained module script.
4
+ //
5
+ // Renders with representative SAMPLE help content so it can be viewed without
6
+ // auth. Faithfully mirrors src/help-page.ts → renderHelpPage: a friendly intro,
7
+ // support entry points (video tutorials / report a bug / contact / billing),
8
+ // and a native <details>/<summary> accordion whose items can be deep-linked
9
+ // open via an `?expand=id,id` query param (URL stays in sync on toggle).
10
+ //
11
+ // Content is representative vidfarm help: forking templates, editing on the
12
+ // timeline, rendering, publishing, and billing / credits.
13
+ import { reskinDocument, rkEscape } from "./document.js";
14
+ const SEARCH_ICON = `<svg class="rk-help-search-ic" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>`;
15
+ const TOPICS = [
16
+ { expand: "getting_started", title: "Getting started", blurb: "Fork a template on Discover and make it yours in a click.", count: "6 articles" },
17
+ { expand: "editing", title: "Editing", blurb: "Retime clips, swap media, and edit by chatting on the timeline.", count: "9 articles" },
18
+ { expand: "rendering", title: "Rendering", blurb: "Turn your composition into a finished MP4 on HyperFrames.", count: "4 articles" },
19
+ { expand: "publishing", title: "Publishing", blurb: "Schedule and post approved cuts straight to your channels.", count: "5 articles" },
20
+ { expand: "billing", title: "Billing & credits", blurb: "Understand your wallet, compute charges, and rate limits.", count: "7 articles" }
21
+ ];
22
+ const FAQ = [
23
+ {
24
+ id: "getting_started",
25
+ cat: "Getting started",
26
+ catPill: "rk-pill-green",
27
+ question: "How do I start from a template?",
28
+ answer: `<p>Head to <strong>Discover</strong> and tap any template to preview it. When one clicks, hit <strong>Fork</strong> — vidfarm copies it into your <strong>Library</strong> as an editable project with its own timeline.</p>
29
+ <p>Forking never touches the original, so you're free to remix hooks, captions, and clips however you like. Your forks always show a <em>“Your project”</em> pill so they're easy to spot.</p>`
30
+ },
31
+ {
32
+ id: "editing",
33
+ cat: "Editing",
34
+ catPill: "rk-pill-sky",
35
+ question: "How does the timeline editor work?",
36
+ answer: `<p>Open any project in <strong>/editor</strong> to get a track-based timeline. Drag a clip to retime it, click a layer to edit its text or swap the media, and use the handles on the canvas frame to move or resize anything.</p>
37
+ <p>Prefer words to knobs? The AI chat dock edits for you in plain language — try <code>make the hook bigger</code> or <code>swap scene 2 for a beach clip</code>. Every change is saved to your project and you'll see a confirmation banner.</p>`
38
+ },
39
+ {
40
+ id: "rendering",
41
+ cat: "Rendering",
42
+ catPill: "rk-pill-violet",
43
+ question: "How do I render my video, and what does it cost?",
44
+ answer: `<p>Hit <strong>Render</strong> in the editor. Vidfarm renders your composition to a full-quality MP4 on HyperFrames Lambda, and the finished file lands back in your <strong>Library</strong> when it's done.</p>
45
+ <p>You're only charged for the <strong>compute time</strong> the render actually uses — your own AI keys are never billed by vidfarm. Long renders keep you posted from the <strong>Job runs</strong> page.</p>`
46
+ },
47
+ {
48
+ id: "publishing",
49
+ cat: "Publishing",
50
+ catPill: "rk-pill-red",
51
+ question: "Can I publish straight to social?",
52
+ answer: `<p>Yes. First connect a channel in <strong>Settings → Channels</strong> (via FlockPoster or an email channel). Then, from any finished render, choose <strong>Publish</strong> to post or schedule to TikTok, Instagram, and more.</p>
53
+ <p>Scheduled posts show up on your <strong>Calendar</strong>, so you can plan a week of content and let vidfarm ship it for you.</p>`
54
+ },
55
+ {
56
+ id: "billing",
57
+ cat: "Billing",
58
+ catPill: "rk-pill-gold",
59
+ question: "How do credits and billing work?",
60
+ answer: `<p>Your <strong>wallet</strong> holds a prepaid balance. Renders and clip scans draw from it based on the compute they use — nothing else. Every charge is itemized in <strong>Settings → Wallet</strong>.</p>
61
+ <p>Top up any time, and set <strong>rate limits</strong> like “max spend per hour / per day” so a runaway job can never surprise you.</p>`
62
+ },
63
+ {
64
+ id: "clips",
65
+ cat: "Editing",
66
+ catPill: "rk-pill-sky",
67
+ question: "What are Clips, and how do I use them?",
68
+ answer: `<p>Clips are short, tagged moments mined from your footage. Point vidfarm at a source video and it cuts the video into scenes, tags each one, and drops them into your <strong>Clips</strong> library.</p>
69
+ <p>From there you can search by what's on screen (“founder on camera”, “product close-up”) and drop any clip straight into a project on the timeline.</p>`
70
+ },
71
+ {
72
+ id: "ai_keys",
73
+ cat: "Billing",
74
+ catPill: "rk-pill-gold",
75
+ question: "Do I need my own AI keys?",
76
+ answer: `<p>Bring-your-own keys — OpenAI, Gemini, OpenRouter, or Anthropic — power clip tagging, recaption, and Swipe mode. Add them in <strong>Settings → Developer</strong>; they're stored per workspace.</p>
77
+ <p>These keys are <strong>never billed by vidfarm</strong>. You only ever pay us for render and clip-scan compute.</p>`
78
+ },
79
+ {
80
+ id: "cancel",
81
+ cat: "Billing",
82
+ catPill: "rk-pill-gold",
83
+ question: "How do I cancel or get a refund?",
84
+ answer: `<p>You can cancel any time from the self-serve billing portal — no email required. We also offer <strong>90-day refunds</strong>.</p>
85
+ <p>For a refund, email <a href="mailto:operator@zoom-gtm.com">operator@zoom-gtm.com</a> and mention the email address you used at checkout so we can find your subscription quickly.</p>
86
+ <p><a class="rk-help-inline-cta" href="https://billing.stripe.com/p/login/fZecQubkxcwOgDuaEE" target="_blank" rel="noopener noreferrer">Open the billing portal <span class="rk-arrow">↗</span></a></p>`
87
+ }
88
+ ];
89
+ const SUPPORT_LINKS = [
90
+ { title: "Watch the tutorials", blurb: "A 4-minute tour from fork to published post.", cta: "Play video", href: "#" },
91
+ { title: "Report a bug", blurb: "Something broken? Send us the details in 30 seconds.", cta: "Report it", href: "#" },
92
+ { title: "Billing portal", blurb: "Update payment, download invoices, or cancel.", cta: "Manage billing", href: "#" }
93
+ ];
94
+ function renderTopicCard(t) {
95
+ return `<a class="rk-card rk-card-hover rk-help-topic" href="?expand=${rkEscape(t.expand)}#rk-help-faq">
96
+ <h3>${rkEscape(t.title)}</h3>
97
+ <p class="rk-muted rk-help-topic-blurb">${rkEscape(t.blurb)}</p>
98
+ <span class="rk-help-topic-foot">${rkEscape(t.count)} <span class="rk-arrow">→</span></span>
99
+ </a>`;
100
+ }
101
+ function renderFaqItem(f) {
102
+ return `<details class="rk-help-faq-item" data-expand-id="${rkEscape(f.id)}">
103
+ <summary class="rk-help-faq-summary">
104
+ <span class="rk-help-faq-q">
105
+ <span class="rk-pill ${f.catPill} rk-help-faq-cat">${rkEscape(f.cat)}</span>
106
+ <span class="rk-help-faq-qt">${rkEscape(f.question)}</span>
107
+ </span>
108
+ <span class="rk-help-faq-toggle" aria-hidden="true"></span>
109
+ </summary>
110
+ <div class="rk-help-faq-body">${f.answer}</div>
111
+ </details>`;
112
+ }
113
+ function renderSupportLink(s) {
114
+ return `<a class="rk-item rk-help-support" href="${rkEscape(s.href)}">
115
+ <div class="rk-item-main">
116
+ <div class="rk-item-title">${rkEscape(s.title)}</div>
117
+ <div class="rk-help-support-sub rk-muted">${rkEscape(s.blurb)}</div>
118
+ </div>
119
+ <span class="rk-btn rk-btn-ghost rk-btn-sm">${rkEscape(s.cta)} <span class="rk-arrow">→</span></span>
120
+ </a>`;
121
+ }
122
+ export function renderReskinHelp() {
123
+ const popular = ["Fork a template", "Render an MP4", "Connect TikTok", "Wallet top-up", "What are Clips?"];
124
+ const hero = `
125
+ <div class="rk-hero-band rk-help-band">
126
+ <div class="rk-hero-art rk-help-art"></div>
127
+ <div class="rk-hero rk-help-hero">
128
+ <span class="rk-badge rk-help-badge">Help center</span>
129
+ <h1 class="rk-help-title">How can we help?</h1>
130
+ <p class="rk-help-lede">Guides and answers for making short-form video with vidfarm — from your first fork to your first published post.</p>
131
+ <form class="rk-help-search" role="search" onsubmit="return false">
132
+ ${SEARCH_ICON}
133
+ <input class="rk-help-search-input" type="search" placeholder="Search help articles — “how do I render?”" aria-label="Search help articles" />
134
+ <button class="rk-btn rk-btn-gold rk-help-search-btn" type="submit">Search</button>
135
+ </form>
136
+ <div class="rk-help-popular">
137
+ <span class="rk-help-popular-label">Popular:</span>
138
+ ${popular.map((p) => `<a class="rk-help-chip" href="#rk-help-faq">${rkEscape(p)}</a>`).join("")}
139
+ </div>
140
+ </div>
141
+ </div>`;
142
+ const topics = `
143
+ <section class="rk-help-topics" aria-label="Browse by topic">
144
+ <div class="rk-grid rk-help-topic-grid">
145
+ ${TOPICS.map(renderTopicCard).join("")}
146
+ </div>
147
+ </section>`;
148
+ const faq = `
149
+ <section class="rk-help-faq" id="rk-help-faq" aria-labelledby="rk-help-faq-title">
150
+ <div class="rk-page-head rk-help-faq-head">
151
+ <span class="rk-eyebrow">Frequently asked</span>
152
+ <h2 class="rk-h1 rk-help-faq-h" id="rk-help-faq-title">Questions, answered</h2>
153
+ <p class="rk-sub">The quick answers most folks are looking for. Still stuck? We're one message away.</p>
154
+ </div>
155
+ <div class="rk-help-faq-list">
156
+ ${FAQ.map(renderFaqItem).join("")}
157
+ </div>
158
+ </section>`;
159
+ const contact = `
160
+ <section class="rk-help-contact" aria-label="Contact support">
161
+ <div class="rk-card rk-help-cta-card">
162
+ <div class="rk-help-cta-copy">
163
+ <span class="rk-eyebrow rk-help-cta-eyebrow">Still need help?</span>
164
+ <h2>Talk to a human</h2>
165
+ <p class="rk-help-cta-sub">Real people, quick replies. Tell us what you're building and we'll help you ship it.</p>
166
+ </div>
167
+ <div class="rk-help-cta-actions">
168
+ <a class="rk-btn rk-btn-gold rk-btn-lg" href="mailto:operator@zoom-gtm.com">Contact the team <span class="rk-arrow">→</span></a>
169
+ <span class="rk-pill rk-pill-green">Typically replies in a few hours</span>
170
+ </div>
171
+ </div>
172
+ <div class="rk-list rk-help-support-list">
173
+ ${SUPPORT_LINKS.map(renderSupportLink).join("")}
174
+ </div>
175
+ </section>`;
176
+ const body = `
177
+ ${hero}
178
+ <main class="rk-container rk-help-main">
179
+ ${topics}
180
+ ${faq}
181
+ ${contact}
182
+ </main>`;
183
+ const pageCss = `
184
+ /* ── hero band ── */
185
+ .rk-help-band{padding-bottom:110px}
186
+ .rk-help-art{background:
187
+ radial-gradient(52% 42% at 82% 14%,rgba(255,241,204,.95),transparent 62%),
188
+ radial-gradient(46% 40% at 12% 92%,rgba(255,245,228,.55),transparent 60%),
189
+ linear-gradient(180deg,#3f9fe0 0%,#6cc0f5 52%,#a9dbfb 100%)}
190
+ .rk-help-hero{max-width:780px;margin:0 auto;display:grid;justify-items:center;gap:18px;padding-bottom:36px}
191
+ .rk-help-badge{background:rgba(255,255,255,.9);backdrop-filter:blur(6px)}
192
+ .rk-help-title{font-size:clamp(2.4rem,6vw,3.6rem);letter-spacing:-.035em;color:#fff;text-shadow:0 2px 18px rgba(20,54,90,.28)}
193
+ .rk-help-lede{font-size:var(--rk-text-lg);color:#f3fbff;max-width:560px;line-height:1.55;text-shadow:0 1px 10px rgba(20,54,90,.25)}
194
+ /* decorative search */
195
+ .rk-help-search{position:relative;display:flex;align-items:center;width:min(560px,100%);
196
+ background:#fff;border-radius:var(--rk-r-full);box-shadow:var(--rk-shadow-lg);padding:7px 7px 7px 18px;margin-top:6px}
197
+ .rk-help-search-ic{color:var(--rk-n-400);flex:none}
198
+ .rk-help-search-input{flex:1;border:0;outline:none;font-family:var(--rk-font-body);font-size:15px;
199
+ color:var(--rk-ink);background:transparent;padding:11px 12px}
200
+ .rk-help-search-input::placeholder{color:var(--rk-n-400)}
201
+ .rk-help-search-btn{flex:none;padding:11px 20px}
202
+ .rk-help-popular{display:flex;align-items:center;gap:8px;flex-wrap:wrap;justify-content:center;margin-top:2px}
203
+ .rk-help-popular-label{font-size:13px;font-weight:600;color:#eaf6ff}
204
+ .rk-help-chip{display:inline-flex;align-items:center;padding:6px 13px;border-radius:var(--rk-r-full);
205
+ font-size:13px;font-weight:600;color:#fff;background:rgba(255,255,255,.16);border:1px solid rgba(255,255,255,.3);
206
+ backdrop-filter:blur(6px);transition:background var(--rk-dur) var(--rk-ease),transform var(--rk-dur) var(--rk-ease)}
207
+ .rk-help-chip:hover{background:rgba(255,255,255,.28);transform:translateY(-1px)}
208
+
209
+ /* ── main column ── */
210
+ .rk-help-main{position:relative;z-index:2;padding-bottom:96px}
211
+
212
+ /* ── topic cards (pulled up onto the hero seam) ── */
213
+ .rk-help-topics{margin-top:-72px}
214
+ .rk-help-topic-grid{grid-template-columns:repeat(5,1fr)}
215
+ @media(max-width:1080px){.rk-help-topic-grid{grid-template-columns:repeat(3,1fr)}}
216
+ @media(max-width:720px){.rk-help-topic-grid{grid-template-columns:repeat(2,1fr)}}
217
+ @media(max-width:460px){.rk-help-topic-grid{grid-template-columns:1fr}}
218
+ .rk-help-topic{display:grid;gap:10px;align-content:start;padding:24px;text-align:left}
219
+ .rk-help-topic h3{font-size:var(--rk-text-lg);font-weight:800;font-family:var(--rk-font-display);color:var(--rk-ink)}
220
+ .rk-help-topic-tile{margin-bottom:4px}
221
+ .rk-help-topic-blurb{font-size:13.5px;line-height:1.5}
222
+ .rk-help-topic-foot{display:inline-flex;align-items:center;gap:6px;margin-top:6px;
223
+ font-size:13px;font-weight:700;color:var(--rk-gold-700)}
224
+ .rk-help-topic:hover .rk-help-topic-foot .rk-arrow{transform:translateX(3px)}
225
+ .rk-help-topic-foot .rk-arrow{transition:transform var(--rk-dur) var(--rk-ease)}
226
+
227
+ /* ── FAQ accordion ── */
228
+ .rk-help-faq{margin-top:72px}
229
+ .rk-help-faq-head{justify-items:center;text-align:center;margin-bottom:28px;gap:10px}
230
+ .rk-help-faq-h{font-size:var(--rk-text-3xl)}
231
+ .rk-help-faq-list{display:grid;gap:14px;max-width:820px;margin:0 auto}
232
+ .rk-help-faq-item{background:var(--rk-surface);border:1px solid var(--rk-border);border-radius:var(--rk-r-2xl);
233
+ box-shadow:var(--rk-shadow-card);overflow:hidden;transition:box-shadow var(--rk-dur) var(--rk-ease),border-color var(--rk-dur) var(--rk-ease)}
234
+ .rk-help-faq-item[open]{border-color:var(--rk-gold-tint2);box-shadow:var(--rk-shadow-lg)}
235
+ .rk-help-faq-summary{display:flex;align-items:center;justify-content:space-between;gap:16px;
236
+ padding:20px 22px;cursor:pointer;list-style:none;user-select:none}
237
+ .rk-help-faq-summary::-webkit-details-marker{display:none}
238
+ .rk-help-faq-q{display:flex;align-items:center;gap:12px;flex-wrap:wrap;min-width:0}
239
+ .rk-help-faq-cat{flex:none}
240
+ .rk-help-faq-qt{font-family:var(--rk-font-display);font-weight:700;font-size:var(--rk-text-lg);color:var(--rk-ink);letter-spacing:-.01em}
241
+ .rk-help-faq-toggle{position:relative;flex:none;width:30px;height:30px;border-radius:var(--rk-r-full);
242
+ background:var(--rk-n-100);transition:background var(--rk-dur) var(--rk-ease),transform var(--rk-dur) var(--rk-ease)}
243
+ .rk-help-faq-toggle::before,.rk-help-faq-toggle::after{content:"";position:absolute;top:50%;left:50%;
244
+ width:12px;height:2px;border-radius:2px;background:var(--rk-n-600);transform:translate(-50%,-50%);transition:opacity var(--rk-dur) var(--rk-ease)}
245
+ .rk-help-faq-toggle::after{transform:translate(-50%,-50%) rotate(90deg)}
246
+ .rk-help-faq-item[open] .rk-help-faq-toggle{background:var(--rk-gold-tint2);transform:rotate(180deg)}
247
+ .rk-help-faq-item[open] .rk-help-faq-toggle::after{opacity:0}
248
+ .rk-help-faq-item[open] .rk-help-faq-toggle::before{background:var(--rk-gold-700)}
249
+ .rk-help-faq-summary:hover .rk-help-faq-toggle{background:var(--rk-n-200)}
250
+ .rk-help-faq-body{display:grid;gap:12px;padding:0 22px 22px;max-width:680px;color:var(--rk-text);line-height:1.65;font-size:14.5px}
251
+ .rk-help-faq-body p{margin:0}
252
+ .rk-help-faq-body strong{color:var(--rk-n-800);font-weight:600}
253
+ .rk-help-faq-body a{color:var(--rk-gold-700);font-weight:600;text-decoration:underline;text-underline-offset:2px}
254
+ .rk-help-inline-cta{display:inline-flex;align-items:center;gap:6px;text-decoration:none!important}
255
+ .rk-help-inline-cta .rk-arrow{transition:transform var(--rk-dur) var(--rk-ease)}
256
+ .rk-help-inline-cta:hover .rk-arrow{transform:translateX(3px)}
257
+
258
+ /* ── contact ── */
259
+ .rk-help-contact{margin-top:72px;display:grid;gap:20px}
260
+ .rk-help-cta-card{display:flex;align-items:center;justify-content:space-between;gap:28px;flex-wrap:wrap;
261
+ background:linear-gradient(135deg,#fffaf0 0%,#fff 60%);border-color:var(--rk-gold-tint2)}
262
+ .rk-help-cta-copy{display:grid;gap:8px;max-width:460px}
263
+ .rk-help-cta-card h2{font-size:var(--rk-text-3xl)}
264
+ .rk-help-cta-eyebrow{color:var(--rk-gold-700)}
265
+ .rk-help-cta-sub{color:var(--rk-text-muted);font-size:var(--rk-text-base);line-height:1.55}
266
+ .rk-help-cta-actions{display:grid;gap:12px;justify-items:start}
267
+ .rk-help-support-list{grid-template-columns:repeat(3,1fr);display:grid;gap:14px}
268
+ @media(max-width:860px){.rk-help-support-list{grid-template-columns:1fr}}
269
+ .rk-help-support{gap:16px;padding:18px}
270
+ .rk-help-support-sub{font-size:13px;margin-top:2px;white-space:normal}
271
+ @media(max-width:560px){.rk-help-support{flex-wrap:wrap}}
272
+ `;
273
+ const script = `
274
+ (function(){
275
+ var items=Array.prototype.slice.call(document.querySelectorAll('.rk-help-faq-item[data-expand-id]'));
276
+ var allowed={}; items.forEach(function(d){if(d.dataset.expandId)allowed[d.dataset.expandId]=1;});
277
+
278
+ function readExpandedFromUrl(){
279
+ var params=new URLSearchParams(window.location.search);
280
+ var set={};
281
+ (params.get('expand')||'').split(',').map(function(v){return v.trim();})
282
+ .forEach(function(v){if(allowed[v])set[v]=1;});
283
+ return set;
284
+ }
285
+ function writeExpandedToUrl(){
286
+ var open=items.filter(function(d){return d.open&&d.dataset.expandId;})
287
+ .map(function(d){return d.dataset.expandId;});
288
+ var url=new URL(window.location.href);
289
+ if(open.length){url.searchParams.set('expand',open.join(','));}
290
+ else{url.searchParams.delete('expand');}
291
+ if(window.history.replaceState){
292
+ window.history.replaceState(null,'',url.pathname+url.search+url.hash);
293
+ }
294
+ }
295
+
296
+ var initial=readExpandedFromUrl();
297
+ items.forEach(function(d){
298
+ d.open=Boolean(d.dataset.expandId&&initial[d.dataset.expandId]);
299
+ d.addEventListener('toggle',writeExpandedToUrl);
300
+ });
301
+
302
+ var firstOpen=items.filter(function(d){return d.open;})[0];
303
+ if(firstOpen){
304
+ requestAnimationFrame(function(){
305
+ firstOpen.scrollIntoView({block:'center',behavior:'smooth'});
306
+ });
307
+ }
308
+ })();`;
309
+ return reskinDocument({
310
+ title: "vidfarm reskin — Help",
311
+ description: "Reskinned vidfarm help center — getting started, topics, FAQ, and support.",
312
+ activeSlug: "help",
313
+ pageCss,
314
+ body,
315
+ script
316
+ });
317
+ }
318
+ //# sourceMappingURL=help-page.js.map
@@ -0,0 +1,62 @@
1
+ // /reskin index — a gallery of every page in the migration, with status.
2
+ import { reskinDocument } from "./document.js";
3
+ import { RESKIN_PAGES } from "./theme.js";
4
+ export function renderReskinIndex() {
5
+ const done = RESKIN_PAGES.filter((p) => p.done).length;
6
+ const cards = RESKIN_PAGES.map((p) => {
7
+ const status = p.done
8
+ ? `<span class="rk-pill rk-pill-green">✓ Ported</span>`
9
+ : `<span class="rk-pill">In progress</span>`;
10
+ const href = p.done ? `/reskin/${p.slug}` : `/reskin/${p.slug}`;
11
+ return `<a class="rk-card rk-card-hover rk-idx-card" href="${href}">
12
+ <div class="rk-spread">
13
+ <span class="rk-tile rk-tile-lg rk-tile-gold rk-idx-mark">${p.label.charAt(0)}</span>
14
+ ${status}
15
+ </div>
16
+ <div>
17
+ <h3 class="rk-idx-title">${p.label}</h3>
18
+ <p class="rk-muted rk-idx-blurb">${p.blurb}</p>
19
+ </div>
20
+ <div class="rk-idx-foot rk-muted">
21
+ <span>reskins <code>${p.live}</code></span>
22
+ <span class="rk-idx-go">Open <span class="rk-arrow">→</span></span>
23
+ </div>
24
+ </a>`;
25
+ }).join("");
26
+ const body = `
27
+ <section class="rk-hero-band">
28
+ <div class="rk-hero-art"></div>
29
+ <div class="rk-hero">
30
+ <span class="rk-badge">Design-system migration</span>
31
+ <h1 class="rk-idx-hero-h1">Reskinning vidfarm,<br>one page at a time</h1>
32
+ <p class="rk-idx-hero-sub">A safe sandbox at <code>/reskin/*</code> where each old page is
33
+ rebuilt in the warm, sunny design system — with fully isolated CSS, so nothing touches the live app.</p>
34
+ <div class="rk-idx-progress">${done} of ${RESKIN_PAGES.length} pages ported</div>
35
+ </div>
36
+ </section>
37
+ <main class="rk-container rk-page">
38
+ <div class="rk-grid rk-grid-3 rk-idx-grid">${cards}</div>
39
+ </main>`;
40
+ const pageCss = `
41
+ .rk-idx-hero-h1{font-size:clamp(2.4rem,5vw,3.6rem);color:#fff;letter-spacing:-.03em;text-shadow:0 2px 24px rgba(0,0,0,.12);margin-top:18px}
42
+ .rk-hero .rk-badge{background:rgba(255,255,255,.9)}
43
+ .rk-idx-hero-sub{max-width:620px;margin:18px auto 0;font-size:var(--rk-text-lg);color:rgba(255,255,255,.95)}
44
+ .rk-idx-hero-sub code{background:rgba(255,255,255,.22);color:#fff}
45
+ .rk-idx-progress{display:inline-block;margin-top:22px;padding:8px 16px;border-radius:var(--rk-r-full);background:rgba(255,255,255,.9);color:var(--rk-ink);font-weight:600;font-size:14px}
46
+ .rk-idx-grid{margin-top:-56px;position:relative;z-index:2}
47
+ .rk-idx-card{display:grid;gap:16px;text-decoration:none}
48
+ .rk-idx-title{margin-bottom:4px}
49
+ .rk-idx-blurb{font-size:14px}
50
+ .rk-idx-foot{display:flex;align-items:center;justify-content:space-between;font-size:13px;padding-top:14px;border-top:1px solid var(--rk-border)}
51
+ .rk-idx-go{font-weight:600;color:var(--rk-ink)}
52
+ .rk-idx-mark{font-family:var(--rk-font-display);font-weight:800;font-size:22px;color:var(--rk-ink)}
53
+ `;
54
+ return reskinDocument({
55
+ title: "vidfarm reskin — page gallery",
56
+ description: "The /reskin/* design-system migration surface for vidfarm.",
57
+ activeSlug: null,
58
+ pageCss,
59
+ body
60
+ });
61
+ }
62
+ //# sourceMappingURL=index-page.js.map
@@ -0,0 +1,249 @@
1
+ // /reskin/job-runs — render & job history / health, ported into the farmville
2
+ // design system. Reference: renderSettingsPage (settings-page.ts) for the
3
+ // zero-arg + SAMPLE-data + page-scoped rk-<page>- CSS + self-contained script
4
+ // conventions.
5
+ //
6
+ // Faithfully mirrors src/account-pages-legacy.ts → renderJobRunsPage ("Render
7
+ // runs."): a scrollable history of job runs, each row surfacing job type
8
+ // (render, clip-scan, decompose, tts…), status (queued/running/succeeded/
9
+ // failed), the linked composition/source + tracer, started-at + duration,
10
+ // compute cost, and a logs link. Baked with representative SAMPLE runs so it
11
+ // renders without auth.
12
+ import { reskinDocument, rkEscape } from "./document.js";
13
+ const SAMPLE = {
14
+ totalRuns: 128,
15
+ succeeded: 108,
16
+ failed: 7,
17
+ spendUsd: 27.6,
18
+ spendWindow: "last 30 days",
19
+ runs: [
20
+ { jobId: "job_a91f", type: "Timeline render", emoji: "🎬", status: "succeeded", sub: "Sunny UGC hook v3", operation: "primitive:timeline_render", tracer: "render-final", startedAt: "Jul 7, 4:12 PM", duration: "1m 04s", costUsd: 1.8 },
21
+ { jobId: "job_c740", type: "Clip scan", emoji: "✂️", status: "succeeded", sub: "founder-b-roll.mp4 · 14 clips tagged", operation: "clip_scan_lambda", tracer: "clip-hunt", startedAt: "Jul 7, 3:58 PM", duration: "38s", costUsd: 0.42 },
22
+ { jobId: "job_dd12", type: "Decompose", emoji: "🧬", status: "running", sub: "Costco haul remix", operation: "decompose", tracer: "decompose-dna", startedAt: "Jul 7, 3:40 PM", duration: "", progress: 0.71, costUsd: 0 },
23
+ { jobId: "job_77c2", type: "HyperFrames render", emoji: "🎞️", status: "failed", sub: "Q3 launch reel · ffprobe not found", operation: "primitive:hyperframes_render", tracer: "render-final", startedAt: "Jul 7, 2:15 PM", duration: "12s", costUsd: 0.06 },
24
+ { jobId: "job_9b3e", type: "Text-to-speech", emoji: "🔊", status: "succeeded", sub: "Founder VO · 320 words", operation: "primitive:tts", tracer: "vo-narration", startedAt: "Jul 7, 1:50 PM", duration: "9s", costUsd: 0.03 },
25
+ { jobId: "job_51aa", type: "Scene annotation", emoji: "🗂️", status: "succeeded", sub: "DTC skincare ad · 8 scenes", operation: "scene_annotation", tracer: "scene-dna", startedAt: "Jul 7, 12:30 PM", duration: "54s", costUsd: 0.71 },
26
+ { jobId: "job_2f08", type: "Video download", emoji: "⬇️", status: "succeeded", sub: "youtube.com/watch?v=… · URL import", operation: "primitive:video_download", tracer: "url-import", startedAt: "Jul 6, 6:22 PM", duration: "22s", costUsd: 0.08 },
27
+ { jobId: "job_be61", type: "Swipe customize", emoji: "🃏", status: "running", sub: "Recut: Morning routine → HydraGlow", operation: "swipe_customize", tracer: "swipe-deck", startedAt: "Jul 6, 5:10 PM", duration: "", progress: 0.33, costUsd: 0 },
28
+ { jobId: "job_04c9", type: "Image generate", emoji: "🖼️", status: "succeeded", sub: "hero-frame-02 · 1080×1920", operation: "primitive:image_generate", tracer: "gen-frames", startedAt: "Jul 6, 3:44 PM", duration: "7s", costUsd: 0.04 },
29
+ { jobId: "job_7a20", type: "Cast identification", emoji: "🎭", status: "failed", sub: "Interview cut · provider rate limit", operation: "cast_identification", tracer: "cast-isolate", startedAt: "Jul 6, 11:05 AM", duration: "4s", costUsd: 0 },
30
+ { jobId: "job_3d1c", type: "Speech-to-text", emoji: "📝", status: "succeeded", sub: "podcast-ep12.mp3 · word timestamps", operation: "primitive:stt", tracer: "caption-sync", startedAt: "Jul 5, 8:31 PM", duration: "41s", costUsd: 0.19 },
31
+ { jobId: "job_e5f7", type: "Product placement", emoji: "📦", status: "queued", sub: "SaaS dashboard promo · waiting for provider", operation: "product_placement", tracer: "placement-scout", startedAt: "Jul 5, 7:58 PM", duration: "", costUsd: 0 }
32
+ ]
33
+ };
34
+ const STATUS_META = {
35
+ succeeded: { label: "Succeeded", tile: "rk-tile-green", pill: "rk-pill-green", glyph: "✓" },
36
+ running: { label: "Running", tile: "rk-jobs-tile-run", pill: "rk-pill-gold", glyph: "●" },
37
+ failed: { label: "Failed", tile: "rk-jobs-tile-fail", pill: "rk-pill-red", glyph: "✕" },
38
+ queued: { label: "Queued", tile: "rk-tile-ink", pill: "rk-pill", glyph: "◔" }
39
+ };
40
+ function money(n) {
41
+ if (!n)
42
+ return "—";
43
+ return `$${n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
44
+ }
45
+ function statChip(count, filter, label, tone, active = false) {
46
+ return `<button type="button" class="rk-jobs-chip${tone}${active ? " is-active" : ""}" data-rk-filter="${filter}">
47
+ ${rkEscape(label)}<span class="rk-jobs-chip-n">${count}</span>
48
+ </button>`;
49
+ }
50
+ function runRow(r) {
51
+ const meta = STATUS_META[r.status];
52
+ const pct = Math.round((r.progress ?? 0) * 100);
53
+ const when = r.status === "running"
54
+ ? `<div class="rk-jobs-progress" role="progressbar" aria-valuenow="${pct}" aria-valuemin="0" aria-valuemax="100">
55
+ <span class="rk-jobs-progress-bar" style="width:${pct}%"></span>
56
+ </div>
57
+ <div class="rk-jobs-when-dur is-run">${pct}% done</div>`
58
+ : r.status === "queued"
59
+ ? `<div class="rk-jobs-when-dur is-wait">In queue</div>`
60
+ : `<div class="rk-jobs-when-dur">${rkEscape(r.duration)}</div>`;
61
+ return `<div class="rk-jobs-row" data-rk-status="${r.status}">
62
+ <span class="rk-tile ${meta.tile} rk-jobs-tile" aria-hidden="true">${meta.glyph}</span>
63
+ <div class="rk-jobs-main">
64
+ <div class="rk-jobs-row-title">
65
+ <span class="rk-jobs-type">${rkEscape(r.type)}</span>
66
+ <span class="rk-mono rk-jobs-id">${rkEscape(r.jobId)}</span>
67
+ <span class="rk-pill ${meta.pill}">${meta.label}</span>
68
+ </div>
69
+ <div class="rk-jobs-sub">
70
+ ${rkEscape(r.sub)} · <span class="rk-mono">${rkEscape(r.operation)}</span>${r.tracer ? ` · tracer <span class="rk-mono">${rkEscape(r.tracer)}</span>` : ""}
71
+ </div>
72
+ </div>
73
+ <div class="rk-jobs-tail">
74
+ <div class="rk-jobs-when">
75
+ <div class="rk-jobs-when-at">${rkEscape(r.startedAt)}</div>
76
+ ${when}
77
+ </div>
78
+ <div class="rk-jobs-cost${r.costUsd ? "" : " is-zero"}">${money(r.costUsd)}</div>
79
+ <button type="button" class="rk-btn rk-btn-ghost rk-btn-sm rk-jobs-logs">View logs</button>
80
+ </div>
81
+ </div>`;
82
+ }
83
+ export function renderReskinJobRuns() {
84
+ const s = SAMPLE;
85
+ const running = s.runs.filter((r) => r.status === "running").length;
86
+ const queued = s.runs.filter((r) => r.status === "queued").length;
87
+ const succeeded = s.runs.filter((r) => r.status === "succeeded").length;
88
+ const failed = s.runs.filter((r) => r.status === "failed").length;
89
+ const successRate = Math.round((s.succeeded / s.totalRuns) * 100);
90
+ const rows = s.runs.map(runRow).join("");
91
+ const body = `
92
+ <main class="rk-container rk-page">
93
+ <div class="rk-page-head">
94
+ <span class="rk-badge">Run history &amp; health</span>
95
+ <h1 class="rk-h1">Job runs</h1>
96
+ <p class="rk-sub">Every render, clip scan, decompose and primitive job — with status, timing, and the compute it cost.</p>
97
+ </div>
98
+
99
+ <div class="rk-jobs-summary"><b>${s.totalRuns}</b> runs&nbsp;·&nbsp;<b>${successRate}%</b> success&nbsp;·&nbsp;<b>${s.failed}</b> failed&nbsp;·&nbsp;<b>${money(s.spendUsd)}</b> compute&nbsp;·&nbsp;<span class="rk-jobs-summary-note">last 30 days · BYO AI keys never billed</span></div>
100
+
101
+ <div class="rk-card rk-jobs-panel">
102
+ <div class="rk-spread rk-wrap rk-jobs-panel-head">
103
+ <div class="rk-jobs-filters" role="group" aria-label="Filter runs by status">
104
+ ${statChip(s.runs.length, "all", "All", "", true)}
105
+ ${statChip(succeeded, "succeeded", "Succeeded", " is-green")}
106
+ ${statChip(running, "running", "Running", " is-gold")}
107
+ ${statChip(failed, "failed", "Failed", " is-red")}
108
+ ${statChip(queued, "queued", "Queued", " is-ink")}
109
+ </div>
110
+ <div class="rk-row">
111
+ <span class="rk-pill rk-pill-gold rk-jobs-live"><span class="rk-jobs-live-dot"></span>Live</span>
112
+ <button type="button" class="rk-btn rk-btn-ghost rk-btn-sm" data-rk-refresh>↻ Refresh</button>
113
+ </div>
114
+ </div>
115
+
116
+ <div class="rk-jobs-head" aria-hidden="true">
117
+ <span></span>
118
+ <span>Run</span>
119
+ <span class="rk-jobs-tail rk-jobs-head-tail">
120
+ <span class="rk-jobs-when">Started</span>
121
+ <span class="rk-jobs-cost">Cost</span>
122
+ <span class="rk-jobs-logs-h"></span>
123
+ </span>
124
+ </div>
125
+
126
+ <div class="rk-jobs-list" data-rk-list>${rows}</div>
127
+ <div class="rk-jobs-empty" data-rk-empty hidden>No runs match this filter yet.</div>
128
+
129
+ <div class="rk-row rk-jobs-foot">
130
+ <button type="button" class="rk-btn rk-btn-ghost rk-btn-sm">Load older runs</button>
131
+ <span class="rk-hint">Showing ${s.runs.length} most recent of ${s.totalRuns}</span>
132
+ </div>
133
+ </div>
134
+ </main>`;
135
+ const pageCss = `
136
+ .rk-jobs-summary{font-size:13.5px;color:var(--rk-text-muted);font-weight:500;margin-bottom:22px;line-height:1.7}
137
+ .rk-jobs-summary b{color:var(--rk-ink);font-weight:800;font-family:var(--rk-font-display);font-size:15px;letter-spacing:-.01em}
138
+ .rk-jobs-summary-note{color:var(--rk-text-faint)}
139
+ .rk-jobs-panel{padding:22px 22px 18px}
140
+
141
+ /* status tiles not in the shared set */
142
+ .rk-jobs-tile{font-size:15px;font-weight:800}
143
+ .rk-jobs-tile-run{background:var(--rk-gold-tint2);color:var(--rk-gold-700);animation:rk-jobs-pulse 1.6s var(--rk-ease) infinite}
144
+ .rk-jobs-tile-fail{background:var(--rk-red-tint);color:#b91c1c}
145
+ @keyframes rk-jobs-pulse{0%,100%{box-shadow:0 0 0 0 rgba(246,199,68,.55)}50%{box-shadow:0 0 0 6px rgba(246,199,68,0)}}
146
+
147
+ /* filter chips */
148
+ .rk-jobs-panel-head{margin-bottom:18px}
149
+ .rk-jobs-filters{display:flex;flex-wrap:wrap;gap:8px}
150
+ .rk-jobs-chip{display:inline-flex;align-items:center;gap:8px;padding:7px 14px;border-radius:var(--rk-r-full);
151
+ font-family:var(--rk-font-body);font-size:13px;font-weight:600;color:var(--rk-n-600);cursor:pointer;
152
+ background:#fff;border:1px solid var(--rk-border);box-shadow:var(--rk-shadow-xs);
153
+ transition:border-color var(--rk-dur) var(--rk-ease),background var(--rk-dur) var(--rk-ease),color var(--rk-dur) var(--rk-ease)}
154
+ .rk-jobs-chip:hover{border-color:var(--rk-border-strong)}
155
+ .rk-jobs-chip-n{display:inline-grid;place-items:center;min-width:20px;height:20px;padding:0 6px;border-radius:var(--rk-r-full);
156
+ background:var(--rk-n-100);color:var(--rk-n-600);font-size:11px;font-weight:700}
157
+ .rk-jobs-chip.is-active{background:var(--rk-ink);color:#fff;border-color:var(--rk-ink)}
158
+ .rk-jobs-chip.is-active .rk-jobs-chip-n{background:#ffffff26;color:#fff}
159
+ .rk-jobs-chip.is-green.is-active{background:#15803d;border-color:#15803d}
160
+ .rk-jobs-chip.is-gold.is-active{background:var(--rk-gold-500);border-color:var(--rk-gold-500);color:var(--rk-ink)}
161
+ .rk-jobs-chip.is-gold.is-active .rk-jobs-chip-n{background:#00000012;color:var(--rk-ink)}
162
+ .rk-jobs-chip.is-red.is-active{background:#b91c1c;border-color:#b91c1c}
163
+ .rk-jobs-chip.is-ink.is-active{background:var(--rk-n-700);border-color:var(--rk-n-700)}
164
+
165
+ .rk-jobs-live{gap:7px}
166
+ .rk-jobs-live-dot{width:7px;height:7px;border-radius:50%;background:var(--rk-gold-700);animation:rk-jobs-pulse 1.6s var(--rk-ease) infinite}
167
+
168
+ /* table-ish column header — 5 tracks that the display:contents tail flows into */
169
+ .rk-jobs-head{display:grid;grid-template-columns:44px minmax(0,1fr) 156px 78px auto;gap:16px;align-items:center;
170
+ padding:0 16px 10px;font-size:11px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--rk-text-faint)}
171
+ .rk-jobs-head .rk-jobs-when{justify-items:start;text-align:left}
172
+ .rk-jobs-head .rk-jobs-cost{text-align:right}
173
+
174
+ /* run rows */
175
+ .rk-jobs-list{display:grid;gap:10px}
176
+ .rk-jobs-row{display:grid;grid-template-columns:44px minmax(0,1fr) 156px 78px auto;gap:16px;align-items:center;
177
+ padding:14px 16px;background:#fff;border:1px solid var(--rk-border);border-radius:var(--rk-r-2xl);
178
+ box-shadow:var(--rk-shadow-xs);transition:box-shadow var(--rk-dur) var(--rk-ease),transform var(--rk-dur) var(--rk-ease),border-color var(--rk-dur) var(--rk-ease)}
179
+ .rk-jobs-row:hover{box-shadow:var(--rk-shadow-md);border-color:var(--rk-border-strong)}
180
+ .rk-jobs-row[data-rk-status="running"]{border-color:var(--rk-gold-tint2);background:linear-gradient(180deg,var(--rk-gold-tint) 0%,#fff 42%)}
181
+ .rk-jobs-main{min-width:0}
182
+ .rk-jobs-row-title{display:flex;align-items:center;gap:9px;flex-wrap:wrap;margin-bottom:4px}
183
+ .rk-jobs-emoji{font-size:15px;line-height:1}
184
+ .rk-jobs-type{font-family:var(--rk-font-display);font-weight:700;font-size:15px;color:var(--rk-ink);letter-spacing:-.01em}
185
+ .rk-jobs-id{font-size:12px;color:var(--rk-text-faint)}
186
+ .rk-jobs-sub{font-size:13px;color:var(--rk-text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
187
+
188
+ .rk-jobs-tail{display:contents}
189
+ .rk-jobs-when{width:156px;display:grid;gap:5px;justify-items:end;text-align:right}
190
+ .rk-jobs-when-at{font-size:13px;font-weight:600;color:var(--rk-n-700)}
191
+ .rk-jobs-when-dur{font-size:12px;color:var(--rk-text-muted)}
192
+ .rk-jobs-when-dur.is-run{color:var(--rk-gold-700);font-weight:700}
193
+ .rk-jobs-when-dur.is-wait{color:var(--rk-text-faint)}
194
+ .rk-jobs-progress{width:100%;height:6px;border-radius:var(--rk-r-full);background:var(--rk-gold-tint2);overflow:hidden}
195
+ .rk-jobs-progress-bar{display:block;height:100%;border-radius:var(--rk-r-full);
196
+ background:linear-gradient(90deg,var(--rk-gold-500),var(--rk-gold-700))}
197
+ .rk-jobs-cost{width:78px;text-align:right;font-family:var(--rk-font-display);font-weight:700;font-size:15px;color:var(--rk-n-700)}
198
+ .rk-jobs-cost.is-zero{color:var(--rk-text-faint);font-weight:500}
199
+ .rk-jobs-logs{flex:none}
200
+
201
+ .rk-jobs-empty{padding:40px 16px;text-align:center;color:var(--rk-text-muted);font-size:14px}
202
+ .rk-jobs-foot{margin-top:16px;justify-content:space-between}
203
+
204
+ @media(max-width:780px){
205
+ .rk-jobs-head{display:none}
206
+ .rk-jobs-row{grid-template-columns:44px minmax(0,1fr);row-gap:12px}
207
+ .rk-jobs-tail{display:flex;grid-column:1 / -1;flex-wrap:wrap;align-items:center;gap:14px;padding-left:60px}
208
+ .rk-jobs-when{width:auto;justify-items:start;text-align:left}
209
+ .rk-jobs-progress{width:120px}
210
+ .rk-jobs-cost{width:auto;text-align:left}
211
+ .rk-jobs-logs{margin-left:auto}
212
+ }
213
+ @media(max-width:520px){
214
+ .rk-jobs-foot{flex-direction:column;align-items:flex-start;gap:10px}
215
+ }
216
+ `;
217
+ const script = `
218
+ (function(){
219
+ var root=document;
220
+ var list=root.querySelector('[data-rk-list]');
221
+ var empty=root.querySelector('[data-rk-empty]');
222
+ root.querySelectorAll('[data-rk-filter]').forEach(function(btn){
223
+ btn.addEventListener('click',function(){
224
+ var f=btn.getAttribute('data-rk-filter');
225
+ root.querySelectorAll('[data-rk-filter]').forEach(function(b){b.classList.toggle('is-active',b===btn)});
226
+ var shown=0;
227
+ root.querySelectorAll('[data-rk-status]').forEach(function(row){
228
+ var match=f==='all'||row.getAttribute('data-rk-status')===f;
229
+ row.hidden=!match; if(match)shown++;
230
+ });
231
+ if(empty)empty.hidden=shown!==0;
232
+ });
233
+ });
234
+ var refresh=root.querySelector('[data-rk-refresh]');
235
+ if(refresh)refresh.addEventListener('click',function(){
236
+ var t=refresh.textContent; refresh.textContent='↻ Refreshing…';
237
+ setTimeout(function(){refresh.textContent=t},900);
238
+ });
239
+ })();`;
240
+ return reskinDocument({
241
+ title: "vidfarm reskin — Job runs",
242
+ description: "Reskinned vidfarm job runs page — render & job history, status, timing, and compute cost.",
243
+ activeSlug: "job-runs",
244
+ pageCss,
245
+ body,
246
+ script
247
+ });
248
+ }
249
+ //# sourceMappingURL=job-runs-page.js.map