diff --git a/.claude/launch.json b/.claude/launch.json deleted file mode 100644 index ae73933..0000000 --- a/.claude/launch.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "version": "0.0.1", - "configurations": [ - { - "name": "choreless", - "runtimeExecutable": "python3", - "runtimeArgs": ["-m", "http.server", "4600"], - "port": 4600, - "autoPort": false - } - ] -} diff --git a/App.tsx b/App.tsx deleted file mode 100644 index 39086d6..0000000 --- a/App.tsx +++ /dev/null @@ -1,1767 +0,0 @@ -import { useEffect, useMemo, useRef, useState } from "react"; - -/* ============ DATA ============ */ - -type Lane = "Business" | "Creator" | "Life"; - -const LANE_COLOR: Record = { - Business: "bg-[#0A5C36] text-white", - Creator: "bg-[#FF4D00] text-white", - Life: "bg-[#1B3A6B] text-white", -}; - -const SERVICES: { - name: string; - lane: Lane; - tagline: string; - deliverable: string; - whyNow: string; - credits: number; - turnaround: string; - autopilot?: boolean; - keywords: string[]; -}[] = [ - { - name: "Reputation Autopilot", - lane: "Business", - tagline: "Every review answered. Every week.", - deliverable: "Monitored reviews across Google, Yelp & Facebook, tone-matched responses posted after your one-tap approval, plus a monthly reputation report.", - whyNow: "88% of customers read responses to reviews before choosing a local business.", - credits: 3, - turnaround: "Runs weekly", - autopilot: true, - keywords: ["review", "reputation", "yelp", "google", "rating", "respond"], - }, - { - name: "Social Autopilot", - lane: "Business", - tagline: "A month of posts. Designed, written, scheduled.", - deliverable: "A 30-day content calendar with fully designed posts and captions in your brand kit, scheduled to your accounts after approval.", - whyNow: "Consistency beats virality — and no owner has time for daily posting.", - credits: 8, - turnaround: "48h", - autopilot: true, - keywords: ["social", "post", "instagram", "facebook", "content", "calendar", "tiktok"], - }, - { - name: "Clip Factory", - lane: "Creator", - tagline: "One video in. Ten shorts out.", - deliverable: "Your long-form video cut into captioned, hook-optimized shorts with thumbnails — each scored for predicted retention before delivery.", - whyNow: "Short-form is where audiences grow; editing is where creators burn out.", - credits: 5, - turnaround: "24h", - keywords: ["video", "clip", "shorts", "podcast", "edit", "youtube", "reels", "cut"], - }, - { - name: "Refund & Comp Recovery", - lane: "Life", - tagline: "We get your money back — automatically.", - deliverable: "Purchases, flights, deliveries and outages watched non-stop; price-adjustment claims, delay compensation, outage credits and wrong-charge disputes filed for you. You spend credits only when it actually recovers money.", - whyNow: "Every company owes refunds they bank on you never claiming.", - credits: 2, - turnaround: "Runs continuously", - autopilot: true, - keywords: ["refund", "price drop", "money back", "compensation", "delay", "outage", "chargeback", "reimburse", "credit", "claim"], - }, - { - name: "Digital Footprint Cleaner", - lane: "Life", - tagline: "Disappear from the databases.", - deliverable: "Removed from data-broker sites, deletion requests filed, zombie accounts closed, mass-unsubscribes done — then monitored monthly so you don't quietly get re-listed.", - whyNow: "Your personal data is bought and sold daily, and almost no one cleans it up.", - credits: 6, - turnaround: "Sweep in 72h, then monthly", - autopilot: true, - keywords: ["data broker", "delete", "privacy", "remove me", "unsubscribe", "footprint", "disappear", "opt out", "personal information", "spam"], - }, - { - name: "Hard Conversation Ghostwriter", - lane: "Life", - tagline: "The email you're dreading — written for you.", - deliverable: "The message you can't bring yourself to write — firing a client, chasing money owed, a complaint, a raise request, a vendor breakup — written in exactly the right tone, with the follow-up ready if they push back.", - whyNow: "The messages that cost you sleep are the ones most worth outsourcing.", - credits: 2, - turnaround: "6h", - keywords: ["email", "write", "dreading", "hard", "fire", "raise", "complaint", "conversation", "message", "quit", "breakup", "confront", "apology", "text", "awkward"], - }, -]; - -const AD_KEYWORDS = ["ad", "ads", "advertis", "campaign", "promote", "marketing", "meta ads", "google ads", "grow sales", "leads"]; - -/* Deep-dive dossiers per service */ -const DEEP: Record = { - "Reputation Autopilot": { - scene: "It's 11:47 on a Tuesday night and someone just left you a one-star review over a parking situation you don't control. You're asleep. Good — stay asleep. By the time you're pouring coffee, a response is drafted in your voice: gracious, specific, and quietly persuasive to the 4,000 strangers who'll read it before deciding whether to walk through your door.", - steps: [ - ["Listen", "Every review on Google, Yelp and Facebook is picked up within the hour — star rating, text, and the reviewer's history."], - ["Read the room", "The pipeline studies your past replies, your tone rules, and the reviewer's actual complaint. A missed reservation gets a different response than a rant."], - ["Draft & flag", "A response is written for your one-tap approval — and if three reviews mention the same broken thing, that pattern gets flagged as an operations alert, not just answered politely."], - ["Report", "A monthly reputation report: rating trends, response times, and the complaint heatmap that tells you what to actually fix."], - ], - lands: ["Every review answered within a week, in your voice", "Operations alerts when complaints cluster", "Monthly reputation report with complaint heatmap"], - finePrint: "Needs: your Google Business / Yelp / Facebook connections and 10 minutes of tone questions at setup.", - }, - "Social Autopilot": { - scene: "The businesses that win on social aren't the cleverest — they're the ones still posting in week forty. You know this. You also know you will not be designing a carousel about your seasonal special at midnight on a Sunday. So the calendar builds itself: thirty days of posts that look like you hired someone, because in a sense you did.", - steps: [ - ["Mine what's working", "Your sales data, seasonality, and past post performance decide what gets pushed — winners promoted, slow movers cleared."], - ["Write the month", "A 30-day calendar with hooks, captions and hashtags in your voice — mixed formats, not thirty variations of the same post."], - ["Design every tile", "Each post rendered in your locked brand kit: your colors, your type, your logo placement. No lavender-gradient AI mush."], - ["Schedule on approval", "You review the month in one sitting, tap approve, and it schedules across Instagram and Facebook."], - ], - lands: ["30 designed, captioned, scheduled posts", "A content strategy note explaining why each week pushes what it pushes", "Monthly performance recap feeding the next calendar"], - finePrint: "Needs: your social accounts connected and a brand kit (we build it at onboarding if you don't have one).", - }, - "Clip Factory": { - scene: "Somewhere in your 74-minute episode is the 40-second moment that would stop a million thumbs. You know it's in there. You also know finding it means scrubbing timelines until 2am. So don't: upload once, and the factory finds the peaks, cuts them, captions them, frames them vertical, and tells you which one to post first — with a number attached.", - steps: [ - ["Map the peaks", "Full transcription plus an emotional map: laughter, tension, hot takes, story turns — the moments that hold attention."], - ["Cut for the hook", "Each clip opens on its strongest three seconds. No 'so anyway, as I was saying' cold starts."], - ["Dress it", "Animated captions in your style, vertical reframing that keeps faces centered, and a thumbnail per clip."], - ["Score it", "Every clip gets a predicted-retention score and a recommended posting order. You post the winners, skip the rest."], - ], - lands: ["8–12 captioned vertical shorts per upload", "A thumbnail for each", "Retention scores + recommended posting order"], - finePrint: "Needs: a video link or upload. Works from YouTube, Drive, or a raw file.", - }, - "Refund & Comp Recovery": { - scene: "You paid $340 for the flight. It dropped to $280 the next morning, and the airline is delighted you'll never notice. The delivery came four days late — that's a refund you're owed and will never claim. The internet was down six hours — that's a bill credit sitting there unasked. This service's whole job is to notice, and to ask, so a slow trickle of your own money finds its way back without you lifting a finger.", - steps: [ - ["Watch the receipts", "Purchases, flights, packages and services are monitored against prices, delivery promises and uptime — the moment one slips, it's flagged as recoverable money."], - ["Know the rule", "Airlines owe delay comp under specific rules; retailers honor price-adjustment windows; ISPs credit outages. Each claim is filed citing the exact policy that forces a yes."], - ["File on your say-so", "Anything account-touching comes back for one-tap approval; the claim goes to the channel that actually pays, with the paper trail retained."], - ["Bank it", "A running ledger of what's been recovered this month — real dollars back, and credits spent only on the wins."], - ], - lands: ["Price-drop, delay, outage and wrong-charge claims filed for you", "One-tap approval on anything account-touching", "A monthly ledger of money actually recovered"], - finePrint: "Results-priced: credits are spent only when a claim actually recovers money. Needs read access to the receipts/accounts you want watched.", - }, - "Digital Footprint Cleaner": { - scene: "Type your own name into Google with a private window open. The home address. The old phone number. The relatives listed like a family tree you didn't publish. Data brokers assembled that for anyone with $2 and a grudge — and they'll rebuild it the week after you remove it, which is why this isn't a one-time scrub but a standing watch that makes you quietly, durably harder to find.", - steps: [ - ["Map your exposure", "A sweep across the major data-broker networks, people-search sites and breach databases surfaces everywhere you're listed — with links."], - ["File the removals", "Opt-out and deletion requests submitted to each, using the specific legal request each one is required to honor — not the polite form they hope you'll use."], - ["Close the zombies", "Dormant accounts you forgot, newsletters you never read, logins tied to old breaches — closed and unsubscribed at the source."], - ["Keep watch", "Brokers re-list you constantly. Monthly re-scans catch it and re-file, so the cleanup holds instead of quietly undoing itself."], - ], - lands: ["Removal from the major data-broker and people-search sites", "Zombie accounts closed, mass-unsubscribes done", "Monthly monitoring + re-removal when you get re-listed"], - finePrint: "Results-priced after the initial sweep: monitoring credits spend only when a new listing is found and removed. Needs the name/aliases and details you want scrubbed.", - }, - "Hard Conversation Ghostwriter": { - scene: "It's the message that's been sitting in your drafts for a week — the client you have to let go, the friend who still owes you $400, the landlord who won't fix the heat, the raise you've earned and can't figure out how to ask for. The words aren't hard because you don't know what you mean. They're hard because tone is everything and you're too close to it. Describe the situation once; get back something that says the hard thing cleanly, keeps the door open where it should be, and closes it where it must.", - steps: [ - ["Hear the whole situation", "The relationship, the history, the outcome you actually want, and the lines you're afraid to cross — captured in a couple of questions."], - ["Find the register", "Firm without cruelty, warm without weakness, final without burning the bridge — the exact register the moment needs, matched to your natural voice."], - ["Draft the message", "The email, text, or letter — ready to send, with the one hard sentence handled instead of hedged into meaninglessness."], - ["Arm the follow-up", "The reply for when they push back, guilt-trip, or negotiate — drafted in advance so you're never caught flat in the moment that counts."], - ], - lands: ["The message you're dreading, written and send-ready", "A follow-up drafted for when they push back", "The right register — firm, warm, or final — matched to your voice"], - finePrint: "Needs: the situation in your words and any thread it's replying to. Nothing sends without you — the words are yours to use or edit.", - }, -}; - -/* ============ SUBSCRIPTION GATING ============ */ - -type Plan = "Starter" | "Pro" | "Business"; -const PLAN_RANK: Record = { Starter: 0, Pro: 1, Business: 2 }; -const PLAN_CREDITS: Record = { Starter: 10, Pro: 30, Business: 100 }; - -/* Which plan unlocks each service */ -const MIN_TIER: Record = { - "Hard Conversation Ghostwriter": "Starter", - "Refund & Comp Recovery": "Starter", - "Clip Factory": "Pro", - "Digital Footprint Cleaner": "Pro", - "Social Autopilot": "Pro", - "Reputation Autopilot": "Business", -}; -const unlocked = (svc: string, plan: Plan) => PLAN_RANK[plan] >= PLAN_RANK[MIN_TIER[svc]]; - -/* ============ SERVICE RUNTIMES (the 6 functioners) ============ */ - -type Field = { key: string; label: string; type: "text" | "textarea" | "select"; placeholder?: string; options?: string[] }; -type Block = { h: string; lines: string[] }; -type Runtime = { fields: Field[]; generate: (v: Record) => Block[] }; - -const pick = (arr: T[], seed: string): T => arr[Math.abs(seed.split("").reduce((n, c) => n + c.charCodeAt(0), 0)) % arr.length]; -const excerpt = (t: string, n = 8) => { const w = (t || "").trim().split(/\s+/); return w.slice(0, n).join(" ") + (w.length > n ? "…" : ""); }; - -const RUNTIME: Record = { - "Reputation Autopilot": { - fields: [ - { key: "biz", label: "Your business name", type: "text", placeholder: "Bluebird Café" }, - { key: "stars", label: "Star rating of the review", type: "select", options: ["1", "2", "3", "4", "5"] }, - { key: "review", label: "Paste the review", type: "textarea", placeholder: "Waited 40 minutes and the food was cold…" }, - { key: "tone", label: "Your response tone", type: "select", options: ["Warm & personal", "Polished & professional", "Playful"] }, - ], - generate: (v) => { - const s = +v.stars; - const r = (v.review || "").toLowerCase(); - const issue = - /wait|slow|line|forever/.test(r) ? "wait times" : - /rude|staff|service|attitude/.test(r) ? "service" : - /dirty|clean|mess/.test(r) ? "cleanliness" : - /price|expensive|charge|cost/.test(r) ? "pricing" : - /cold|stale|quality|broken/.test(r) ? "quality" : ""; - const opener = s <= 2 - ? `Thank you for telling us straight — this isn't the visit we want anyone to have at ${v.biz}, and I'm sorry we missed the mark.` - : s === 3 - ? `Thanks for the honest, balanced review — we'll take the three stars, but we want the other two back.` - : `This made our morning — thank you for taking the time to say it.`; - const middle = issue - ? `What you described${v.review ? ` ("${excerpt(v.review)}")` : ""} points at our ${issue}, and it's already been raised with the team — that's a fix, not a platitude.` - : s <= 3 - ? `We've shared your note with the whole team so the next visit reads very differently.` - : `We've pinned this one up for the team — it's exactly what we're trying to do every day.`; - const close = - v.tone === "Playful" ? `Come back soon — the next one's on its best behavior. 🧡` : - v.tone === "Warm & personal" ? `If you'll give us another chance, ask for me — I'd like to make this right personally.` : - `We'd welcome the chance to serve you again, and to a higher standard.`; - const blocks: Block[] = [ - { h: "Drafted response (awaiting your one-tap approval)", lines: [`${opener} ${middle} ${close}`, `— ${v.biz}`] }, - ]; - if (issue && s <= 3) - blocks.push({ h: "⚠ Operations signal", lines: [`This is the kind of review we cluster: if 2 more mention ${issue} this month, you'll get an ops alert with the pattern, dates, and suggested fix — not just polite replies.`] }); - blocks.push({ h: "This week, on autopilot", lines: ["• 4 platforms monitored hourly (Google, Yelp, Facebook, TripAdvisor)", "• Every response drafted in this voice, posted only after your tap", "• Month-end: rating trend + complaint heatmap report"] }); - return blocks; - }, - }, - - "Social Autopilot": { - fields: [ - { key: "biz", label: "Your business", type: "text", placeholder: "Kettle & Co. — handmade candles" }, - { key: "push", label: "What should this month push?", type: "text", placeholder: "The new autumn collection" }, - { key: "vibe", label: "Brand vibe", type: "select", options: ["Cozy & warm", "Bold & punchy", "Clean & premium"] }, - ], - generate: (v) => { - const cap = (h: string, c: string) => `${h} — "${c}"`; - const vibeWord = v.vibe === "Bold & punchy" ? "Loud" : v.vibe === "Clean & premium" ? "Quiet luxury" : "Warm"; - return [ - { h: "Week 1 of your 30-day calendar (sample)", lines: [ - cap("MON · Reel", `POV: ${v.push} just dropped and your whole feed can smell it. (hook: 0.8s product macro)`), - cap("TUE · Story poll", `Two of ${v.push} head-to-head — 'which one lives on your shelf?' (engagement bait, zero production)`), - cap("WED · Carousel", `The making of ${v.push}: 5 slides, hands + process + one imperfection kept in. People buy the maker.`), - cap("FRI · Static", `${vibeWord} flat-lay of ${v.push} with one line of copy and nothing else. Let it breathe.`), - cap("SUN · UGC repost", `Customer photo + your one-line reply. Cheapest trust you'll ever buy.`), - ]}, - { h: "Strategy note (why this mix)", lines: [ - `• ${v.push} carries the month, but only 40% of posts sell — the rest build the habit of watching ${v.biz}`, - "• Reels for reach, carousels for saves, stories for votes — each format has one job", - "• Every tile is rendered in your locked brand kit before you see it; you approve the month in one sitting", - ]}, - { h: "What the full run delivers", lines: ["• 30 designed posts, captioned + hashtagged", "• Scheduled across Instagram & Facebook after your approval", "• Next month's calendar auto-learns from this month's numbers"] }, - ]; - }, - }, - - "Clip Factory": { - fields: [ - { key: "topic", label: "What's the video about?", type: "textarea", placeholder: "Episode 42: I interviewed a burnout coach about why high performers crash…" }, - { key: "len", label: "Video length", type: "select", options: ["Under 20 min", "20–60 min", "60+ min"] }, - { key: "aud", label: "Who watches you?", type: "text", placeholder: "Ambitious 25-40yo professionals" }, - ], - generate: (v) => { - const t = excerpt(v.topic || "your topic", 6); - const n = v.len === "60+ min" ? "10–12" : v.len === "20–60 min" ? "7–9" : "4–6"; - return [ - { h: `Clip sheet (${n} clips from this upload — top 5 shown)`, lines: [ - `01 · [12:40] "The thing nobody tells you about ${t}" — opens mid-sentence on the boldest claim. Retention score 87 · post FIRST`, - `02 · [31:05] The story moment — personal, specific, slightly uncomfortable. Score 82 · post 3rd, stories reward mid-week`, - `03 · [04:18] The contrarian take your ${v.aud || "audience"} will argue about in comments. Score 79 · comments = distribution`, - `04 · [47:52] The practical bit — '3 things to do tonight.' Saves magnet. Score 74`, - `05 · [22:11] The laugh — 28 seconds, no context needed. Score 71 · palate cleanser between heavy posts`, - ]}, - { h: "Every clip arrives dressed", lines: ["• Animated captions in your style (word-by-word, keyword-highlighted)", "• Vertical reframe with faces auto-centered", "• A thumbnail per clip, in your locked brand kit", "• Hook rewritten as the first line of the caption"] }, - { h: "Posting order logic", lines: ["• Highest score ≠ always first — the sheet sequences for momentum: bold claim → argument-starter → story → utility", "• Scores are predictions, not promises: the next upload's scores learn from this one's real numbers"] }, - ]; - }, - }, - - "Refund & Comp Recovery": { - fields: [ - { key: "what", label: "What are we recovering on?", type: "select", options: ["A price drop after I bought", "A late flight / delivery", "An internet or service outage", "A wrong or double charge"] }, - { key: "item", label: "What was it & how much?", type: "text", placeholder: "Flight to Denver — $340" }, - { key: "when", label: "When did it happen?", type: "select", options: ["This week", "This month", "1–3 months ago"] }, - ], - generate: (v) => { - const kind = v.what || ""; - const isPrice = kind.startsWith("A price"); - const isLate = kind.includes("late"); - const isOutage = kind.includes("outage"); - return [ - { h: "The rule that forces a yes", lines: [ - isPrice ? `• ${v.item || "This purchase"} likely falls inside a price-adjustment window most retailers honor but never advertise. We cite their own policy back to them and request the difference refunded to your original payment.` - : isLate ? `• ${v.item || "This trip/delivery"} — carriers owe compensation for delays past defined thresholds under specific regulations. We file citing the exact rule and delay length, not a vague complaint.` - : isOutage ? `• ${v.item || "This service"} — providers credit outages on request but bank on you not asking. We calculate the pro-rated credit and demand it in writing.` - : `• ${v.item || "This charge"} — a formal dispute citing the card network's rules goes to the billing channel that must respond, with your issuer ready to CC if they stall.`, - ]}, - { h: "Drafted claim (files on your one tap)", lines: [ - `Re: ${isPrice ? "Price adjustment request" : isLate ? "Delay compensation claim" : isOutage ? "Service outage credit" : "Formal charge dispute"} — ${v.item || "[item]"}`, - isPrice ? `"I purchased the above ${(v.when || "recently").toLowerCase()} and the price has since dropped. Per your price-adjustment policy, I request the difference refunded to my original payment method within your stated window."` - : isLate ? `"The above was delayed beyond the threshold at which compensation is owed. I am claiming the compensation due under the applicable regulation and request written confirmation and payment within the mandated period."` - : isOutage ? `"Service was interrupted for a material period ${(v.when || "recently").toLowerCase()}. I request a pro-rated credit for the outage applied to my next statement, per your service terms."` - : `"I am formally disputing the above charge under the card network's dispute rules and applicable billing law, and request written resolution within 30 days."`, - ]}, - { h: "Then it just… watches", lines: [ - "• The claim is filed to the channel that actually pays and tracked to resolution", - "• Silent past their window → automatic escalation (regulator, card issuer, or supervisor as fits)", - "• You're charged credits only if it recovers money — a running ledger shows what came back", - ]}, - ]; - }, - }, - - "Digital Footprint Cleaner": { - fields: [ - { key: "name", label: "Name to scrub", type: "text", placeholder: "Jaden Green" }, - { key: "city", label: "City / state (helps find listings)", type: "text", placeholder: "Phoenix, AZ" }, - { key: "goal", label: "What's driving this?", type: "select", options: ["General privacy", "Reduce spam & robocalls", "A specific person I want to lose me", "Professional / safety reasons"] }, - ], - generate: (v) => { - const n = v.name || "your name"; - return [ - { h: `Exposure sweep for ${n} (sample)`, lines: [ - `🔴 12 data-broker profiles found — home address, ${v.city ? v.city + ", " : ""}age, and 3 listed relatives on the top people-search sites`, - `🔴 2 old breaches include your email + a reused password — flagged for a reset, not just removal`, - `🟡 41 marketing lists holding your address; 8 you opened this year, 33 you never did`, - `🟢 6 dormant accounts tied to a breached email — closable on your say-so`, - ]}, - { h: "Removal plan (files on your approval)", lines: [ - `• Deletion / opt-out requests submitted to all 12 brokers, each citing the specific request it's legally required to honor`, - v.goal === "A specific person I want to lose me" ? "• The address-revealing sites first — the ones that make you findable — before the low-risk marketing lists" - : v.goal === "Reduce spam & robocalls" ? "• Marketing lists and number-selling brokers first — the pipeline feeding the robocalls gets cut at the source" - : v.goal === "Professional / safety reasons" ? "• People-search and address sites escalated, plus a suppression request wherever the law allows one" - : "• Full sweep across brokers, people-search sites and stale accounts, worst-exposure first", - "• 33 unread lists unsubscribed at the source; 6 zombie accounts closed", - ]}, - { h: "Why this is a subscription, not a one-off", lines: [ - "• Brokers re-list you within weeks — a one-time scrub quietly undoes itself", - "• Monthly re-scans catch new listings and re-file automatically", - "• Results-priced after the sweep: you spend credits only when a new listing is actually found and removed", - ]}, - ]; - }, - }, - - "Hard Conversation Ghostwriter": { - fields: [ - { key: "situation", label: "What's the conversation?", type: "select", options: ["Firing / parting ways with a client", "Chasing money a friend owes", "A complaint (landlord, company, service)", "Asking for a raise", "Ending a vendor / partnership", "Setting a hard boundary"] }, - { key: "detail", label: "The situation, in your words", type: "textarea", placeholder: "They've been late three months running and I've covered for them, but I can't keep doing it…" }, - { key: "tone", label: "How should it land?", type: "select", options: ["Firm but kind", "Warm, door left open", "Final — no ambiguity"] }, - ], - generate: (v) => { - const sit = v.situation || ""; - const warm = v.tone === "Warm, door left open"; - const final = v.tone === "Final — no ambiguity"; - const opener = - sit.startsWith("Firing") ? `Hi [name] — I've valued working together, and I want to be straight with you rather than let this drift.` - : sit.startsWith("Chasing") ? `Hey [name] — I hate that money is the thing between us right now, so I'd rather just say it plainly.` - : sit.startsWith("A complaint") ? `Hi — I'm writing about an issue that's gone on long enough that a friendly nudge isn't enough anymore.` - : sit.startsWith("Asking") ? `Hi [name] — I'd like to talk about my compensation, and I want to make the case directly.` - : sit.startsWith("Ending") ? `Hi [name] — after a lot of thought, I've decided to end our arrangement, and I want to do it cleanly and fairly.` - : `Hi [name] — I need to be clear about something, because being vague hasn't been fair to either of us.`; - const hardLine = - sit.startsWith("Firing") ? `After this project wraps, I'm going to move on from our work together. It isn't about any one thing — it's the right call for where I'm headed.` - : sit.startsWith("Chasing") ? `The $[amount] from [when] is still outstanding, and I need it settled by [date]. I'd rather sort this now than let it sit between us any longer.` - : sit.startsWith("A complaint") ? `The [issue] has not been resolved despite [attempts], and I'm now asking for [specific remedy] by [date].` - : sit.startsWith("Asking") ? `Based on [what I've delivered], I'm asking for a raise to [number]. I've laid out the reasoning below and I'm confident it's earned.` - : sit.startsWith("Ending") ? `We'll wind down over [notice period], I'll [handoff commitment], and I want to leave this on good terms.` - : `Going forward, [the boundary]. I'm telling you directly so there's no confusion later.`; - const close = - final ? `I've made my decision, so I'm not looking to reopen it — but I'm glad to sort out the practical details whenever works.` - : warm ? `I really do value what we've built, and I hope we can [stay in touch / work together again down the line]. Happy to talk it through.` - : `I'd rather handle this like adults than let it fester, so let me know a good time to talk if you'd prefer.`; - return [ - { h: "Drafted message (ready to send or edit)", lines: [ - opener, - hardLine, - close, - `— [you]`, - ]}, - { h: "Why it's built this way", lines: [ - `• The one hard sentence is stated once, cleanly — not buried, not repeated, not hedged into mush`, - `• Tone dialed to "${v.tone || "Firm but kind"}": ${final ? "no openings that invite negotiation" : warm ? "the door is explicitly left open" : "firm on the ask, kind on the person"}`, - v.detail ? `• Grounded in your specifics ("${excerpt(v.detail, 10)}") so it reads like you, not a template` : `• Add the details above and the draft grounds itself in your specifics`, - ]}, - { h: "The follow-up, already written", lines: [ - `• When they push back, guilt-trip, or counter — a reply is drafted in advance in the same register`, - `• You're never caught flat in the moment that actually counts`, - `• Nothing sends without you. The words are yours to use, soften, or sharpen.`, - ]}, - ]; - }, - }, -}; - -const TIERS = [ - { - name: "Starter", - price: 29, - credits: 10, - turnaround: "48h turnaround", - features: ["10 task credits / month", "2 starter services — Hard Conversation Ghostwriter + Refund & Comp Recovery", "1 Autopilot", "Credits roll over 1 month"], - cta: "Go choreless", - featured: false, - }, - { - name: "Pro", - price: 79, - credits: 30, - turnaround: "24h turnaround", - features: ["30 task credits / month", "5 services — adds Clip Factory, Social Autopilot & Digital Footprint Cleaner", "3 Autopilots", "1 free revision per task", "Voice & brand profile training", "Priority queue"], - cta: "Go Pro", - featured: true, - }, - { - name: "Business", - price: 199, - credits: 100, - turnaround: "12h turnaround", - features: ["100 task credits / month", "All 6 services — adds Reputation Autopilot", "Unlimited Autopilots", "Human QA on request", "Dedicated success manager", "Team seats (up to 5)"], - cta: "Scale up", - featured: false, - }, -]; - -const OUTCOMES = [ - { stat: "312", label: "data-broker listings removed", quote: "I searched my own name and there was so much — address, relatives, all of it. Now there's almost nothing, and it keeps them from re-adding me.", who: "Marcus T. — Pro member" }, - { stat: "+212%", label: "short-form views in 60 days", quote: "I upload one podcast. Ten scored clips come back. I haven't opened an editor since March.", who: "Dana R. — 340k subscribers" }, - { stat: "1 email", label: "I'd dreaded for a month", quote: "It wrote the 'we're done' message to a client with exactly the right spine. I sent it in one tap and finally slept.", who: "Dana L. — freelance designer" }, - { stat: "$486", label: "in refunds it claimed for me", quote: "It found the price drops, filed the claims, and tracked them. I did literally nothing.", who: "Priya S. — Pro member" }, - { stat: "4.9★", label: "average rating maintained", quote: "Every review answered within a week, in our voice. Our reputation runs itself now.", who: "Bluebird Café — Portland" }, - { stat: "3.2x", label: "ROAS on first managed campaign", quote: "Choreless Ads built 14 creative variants, killed the losers weekly, and tripled our return.", who: "Kettle & Co. — DTC brand" }, -]; - -const DEMO_TASKS = [ - { name: "October social calendar", service: "Social Autopilot", status: "Awaiting approval", eta: "Ready now", pct: 95 }, - { name: "Cut podcast #42 into shorts", service: "Clip Factory", status: "In QA", eta: "~2h", pct: 80 }, - { name: "Remove me from 14 data-broker sites", service: "Digital Footprint Cleaner", status: "Running", eta: "~1 day", pct: 45 }, - { name: "Claim price-drop refund — Denver flight", service: "Refund & Comp Recovery", status: "Delivered", eta: "$60 back ✓", pct: 100 }, - { name: "Draft the raise-request email", service: "Hard Conversation Ghostwriter", status: "Delivered", eta: "Sent ✓", pct: 100 }, -]; - -const STATUS_STYLE: Record = { - "Awaiting approval": "bg-[#FF4D00] text-white", - "In QA": "bg-[#141414] text-white", - Running: "bg-[#E8E1D4] text-[#141414]", - Delivered: "bg-[#0A5C36] text-white", -}; - -/* ============ SHARED ============ */ - -const serif = { fontFamily: "Georgia, 'Times New Roman', serif" }; - -function Wordmark({ light }: { light?: boolean }) { - return ( - - CHORELESS. - - ); -} - -/* ============ TASK DROP (live routing demo) ============ */ - -function routeTask(text: string) { - const t = text.toLowerCase(); - if (AD_KEYWORDS.some((k) => t.includes(k))) - return { type: "ads" as const }; - let best: { s: (typeof SERVICES)[number]; score: number } | null = null; - for (const s of SERVICES) { - const score = s.keywords.reduce((n, k) => n + (t.includes(k) ? 1 : 0), 0); - if (score > 0 && (!best || score > best.score)) best = { s, score }; - } - return best ? { type: "service" as const, service: best.s } : { type: "custom" as const }; -} - -function TaskDrop() { - const [text, setText] = useState(""); - const [result, setResult] = useState | null>(null); - - const examples = [ - "Turn my podcast into shorts", - "Write the email I've been dreading to a client", - "Get my money back on that price drop", - "Remove me from data-broker sites", - ]; - - return ( -
-
- Task Drop — try the router live -
-
-