1(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,116233,e=>{"use strict";e.i(365587),e.i(81522),e.i(602744),e.i(155861),e.i(432497),e.i(541377),e.i(416523),e.i(549118),e.i(636715),e.i(566629),e.i(517854),e.i(98085),e.i(446070),e.i(463796),e.i(213718),e.i(268141),e.s([])},468158,e=>{"use strict";var a=e.i(843476);e.s(["SkipToContent",0,function({label:e="Skip to main content"}){return(0,a.jsx)("a",{href:"#main-content",className:"fixed left-4 top-4 z-[100] -translate-y-24 rounded-lg bg-[color:var(--site-accent)] px-4 py-2 text-sm font-semibold text-[color:var(--site-on-accent,#fff)] shadow-lg transition-transform focus:translate-y-0 focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-[color:var(--site-fg)]",children:e})}])},175522,e=>{"use strict";var a=e.i(843476);e.i(116233);var t=e.i(432497),s=e.i(468158),r=e.i(271645);let n="https://media.softflourish.com/softflourish",i={heroStudio:`${n}/hero_studio.webp`,heroTeam:`${n}/hero_team.webp`,portfolioMobile:`${n}/portfolio_mobile.webp`,portfolioWeb:`${n}/portfolio_web.webp`,portfolioEnterprise:`${n}/portfolio_enterprise.webp`,infographicPlatforms:`${n}/infographic_platforms.webp`,backgroundOffice:`${n}/background_office.webp`},o=[{icon:"layers",value:"6",label:"Platforms â iOS, iPadOS, Android, macOS, Windows, tvOS"},{icon:"code",value:"Web",label:"Apps, dashboards & internal tools"},{icon:"store",value:"Stores",label:"App Store, Google Play & Microsoft Store"},{icon:"book",value:"Books",label:"Paperback, hardcover & ebook publishing"}],l=[{title:"Native apps",description:"Production iPhone, iPad, Android, Mac, Windows, and Apple TV apps â Swift/SwiftUI, Kotlin/Jetpack Compose, and WinUI, with shared cores where it genuinely fits.",icon:"ð±"},{title:"Web applications",description:"Customer portals, admin consoles, marketing sites, and SaaS dashboards on modern stacks with Cloudflare-ready deployment.",icon:"ð"},{title:"Store publishing",description:"End-to-end submission, metadata, screenshots, ASO, and compliance pages so your product ships cleanly on the App Store, Google Play, and the Microsoft Store.",icon:"ð"},{title:"Book publishing",description:"Manuscript to shelf â editing, typesetting, cover design, ISBN assignment, and distribution in paperback, hardcover, and ebook. You keep the rights.",icon:"ð"},{title:"Backend & cloud",description:"Firebase, GCP, auth, APIs, and data pipelines sized for business workloads â not prototype throwaways.",icon:"âï¸"},{title:"Product design",description:"UX flows, visual systems, and prototype-to-production handoff so engineering and design stay aligned.",icon:"â¦"},{title:"Support & iteration",description:"Post-launch fixes, OS updates, store policy changes, and feature cycles with a team that signed the original work.",icon:"ð§"}],c=[{title:"Field operations suite",tagline:"Mobile workforce tools for a regional services company",image:i.portfolioMobile,category:"Mobile · B2B"},{title:"Analytics command center",tagline:"Web dashboard for inventory, sales, and executive reporting",image:i.portfolioWeb,category:"Web · Enterprise"},{title:"Product launch program",tagline:"iOS and Android release with store optimization and support infrastructure",image:i.portfolioEnterprise,category:"Mobile · Launch"},{title:"Internal platform",tagline:"macOS and web admin for operations teams",image:i.heroTeam,category:"macOS · Web"}];var d=e.i(247167),p=e.i(268141);let h=[{legend:"An app â which platforms?",hint:"Tick every platform you want it on. Not sure yet is a fine answer.",options:["iPhone","iPad","Android","Mac","Windows","Apple TV","Web app","Not sure yet"]},{legend:"Publishing & everything else",hint:"",options:["Publish a book","Store submission & compliance","Support an app I already use","Something else"]}];function m({pageSlug:e="/",pageTitle:t="Contact"}){let[s,n]=(0,r.useState)({kind:"idle"}),[i,o]=(0,r.useState)(!1);async function l(a){a.preventDefault();let s=a.currentTarget,r=new FormData(s),i=String(r.get("hp_field")??"").trim(),l=String(r.get("cf-turnstile-response")??"").trim(),c={};for(let e of Array.from(new Set(r.keys()))){if("hp_field"===e||"cf-turnstile-response"===e)continue;let a=r.getAll(e).filter(e=>"string"==typeof e).map(e=>
1e.trim()).filter(Boolean).join(", ");a&&(c[e]=a)}o(!0);try{let a=await fetch("/api/leads",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({siteKey:"softflourish.com",siteName:"Soft Flourish",pageSlug:e,pageTitle:t,sourceUrl:window.location.href,honeypot:i,turnstileToken:l,fields:c})}),r=await a.json().catch(()=>null);if(!a.ok)throw Error(r?.message??"Unable to send your message right now.");s.reset(),n({kind:"success",message:r?.message??"Thank you â we received your message and will reply within one business day."})}catch(e){n({kind:"error",message:e instanceof Error?e.message:"Unable to send your message right now."})}finally{o(!1)}}return(0,a.jsxs)("form",{className:"sf-contact-form",onSubmit:l,children:[(0,a.jsxs)("label",{className:"sf-field",children:[(0,a.jsx)("span",{children:"Name"}),(0,a.jsx)("input",{name:"full_name",type:"text",required:!0,autoComplete:"name"})]}),(0,a.jsxs)("label",{className:"sf-field",children:[(0,a.jsx)("span",{children:"Email"}),(0,a.jsx)("input",{name:"email",type:"email",required:!0,autoComplete:"email"})]}),(0,a.jsxs)("label",{className:"sf-field",children:[(0,a.jsx)("span",{children:"Phone"}),(0,a.jsx)("input",{name:"phone",type:"tel",required:!0,autoComplete:"tel"})]}),(0,a.jsxs)("label",{className:"sf-field",children:[(0,a.jsx)("span",{children:"Website"}),(0,a.jsx)("input",{name:"website",type:"url",autoComplete:"url",placeholder:"https://yourcompany.com"})]}),h.map(e=>(0,a.jsxs)("fieldset",{className:"sf-choice-set",children:[(0,a.jsx)("legend",{children:e.legend}),e.hint?(0,a.jsx)("p",{className:"sf-choice-hint",children:e.hint}):null,(0,a.jsx)("div",{className:"sf-choice-grid",children:e.options.map(e=>(0,a.jsxs)("label",{className:"sf-choice",children:[(0,a.jsx)("input",{type:"checkbox",name:"looking_for",value:e}),(0,a.jsx)("span",{children:e})]},e))})]},e.legend)),(0,a.jsxs)("label",{className:"sf-field sf-field-full",children:[(0,a.jsx)("span",{children:"Tell us about it"}),(0,a.jsx)("textarea",{name:"message",rows:4,required:!0,placeholder:"Your app idea, the book you want published, or a questionâ¦"})]}),(0,a.jsxs)("label",{className:"sf-honeypot","aria-hidden":"true",children:[(0,a.jsx)("span",{children:"Leave blank"}),(0,a.jsx)("input",{name:"hp_field",type:"text",tabIndex:-1,autoComplete:"off"})]}),d.default.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY?(0,a.jsx)(p.TurnstileWidget,{siteKey:d.default.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY}):null,(0,a.jsx)("button",{className:"sf-btn sf-btn-primary sf-btn-full",type:"submit",disabled:i,children:i?"Sendingâ¦":"Send message"}),(0,a.jsx)("p",{className:"sf-form-note",children:"We reply within one business day. Your details stay with us â we never sell or share them."}),(0,a.jsx)("p",{role:"error"===s.kind?"alert":"status","aria-live":"polite","aria-atomic":"true",className:"idle"===s.kind?"sr-only":`sf-form-status sf-form-status-${s.kind}`,children:"idle"===s.kind?"":s.message})]})}let u=[{slug:"app-store-review-rejections",title:"App Store Review Guidelines: The Rejection Reasons That Catch Founders Off Guard",dek:"Apple rejects a meaningful share of first-time submissions, and it's rarely the exotic reasons founders worry about. Here's what actually trips people up, and how to submit clean.",category:"App Store",readTime:"9 min read",published:"Feb 3, 2026",body:[{type:"p",text:"Every app submitted to the App Store goes through App Review â a mix of automated checks and a human reviewer who actually installs and uses your build. First-time founders often imagine review as a formality, then get a rejection notice referencing a guideline number they've never seen. It's not arbitrary. Apple's App Review Guidelines are organized into five numbered sections, and the overwhelming majority of rejections we see trace back to the same handful of subsections."},{type:"h2",text:"The five sections, in plain language"},{type:"ul",items:["1. Safety â user-generated content moderation, kids' category rules, physical harm and abuse prevention.","2. Performance â the app has to actually work: no crashes, complete features, accurate device requirements, no beta or placeholder builds.","3. Business â how you charge people. This is where In-App Purchase requirements and subscription rules live.","4. Design â spam, cloned templates, minimum functionality, and general UX expectations.","5. Legal â privacy policy, data collection disclosure, intellectual property, and legal compliance in the app's operating region."]},{type:"h2",text:"Guideline 2.1 â App Completeness (the most common rejection)"},{type:"p",text:"2.1 is the workhorse rejection reason. It covers crashes, broken links, placeholder text (\"Lorem ipsum\" screenshots do happen), and â this one catches people constantly â a login-gated app with no working demo account in the review notes. If your app requires sign-in before a reviewer can see anything, and you haven't supplied a test account with the App Review Information section of App Store Connect, the reviewer can't get past your login screen and will reject on completeness grounds. It's an easy fix and an easy miss."},{type:"h2",text:"Guideline 4.2 â Minimum Functionality"},{type:"p",text:'4.2 exists to keep the App Store from filling up with thin wrappers around a website. If your app is a WebView pointed at your marketing site with no native functionality, or if it duplicates dozens of other submissions using the same template with only branding swapped, expect a 4.2 rejection. This is also where "your app should feel at home on the platform" lives â an app that ignores standard iOS navigation and interaction patterns entirely can get flagged here, separate from any bug.'},{type:"h2",text:"Guideline 3.1.1 â In-App Purchase"},{type:"p",text:'If your app unlocks digital content or services â a subscription, extra credits, premium features, digital goods of any kind â that transaction has to go through Apple\'s In-App Purchase system, not a link to an external
1checkout page. This is the rule that surprises founders coming from the web, where "just add a Stripe button" is normal. There are real exceptions (the Reader app exception for content consumed elsewhere, and physical goods/services consumed outside the app, like ordering food or booking a hotel room, are exempt), but digital content and functionality inside the app itself has to use IAP. Routing around it with a hidden web checkout is one of the fastest ways to get an app pulled entirely, not just rejected.'},{type:"h2",text:"Guideline 5.1.1 â Data Collection and Storage"},{type:"p",text:"Your privacy policy has to be live at a public URL before you submit â a placeholder or a policy that 404s is an automatic hold. Since App Store Connect requires an App Privacy \"nutrition label\" â the questionnaire that declares which data types you collect and whether they're linked to identity or used for tracking â that questionnaire has to match what the app actually does. Apple has pulled apps post-launch when a later review found the declared label didn't match observed behavior. Since 2022, guideline 5.1.1(v) also requires that if your app lets users create an account, it has to let them delete that account (and the associated data) from inside the app, not just via an email to support."},{type:"h2",text:"Guideline 2.3.1 â Accurate Metadata"},{type:"p",text:"Screenshots, the app description, and the app name have to reflect what the app actually does today, not what's on your roadmap. Screenshots of a feature that isn't shipped yet, or comparison language against a competitor, will get flagged during metadata review â which is a separate pass from the functional review and can bounce your submission back even after the build itself passes."},{type:"h2",text:"Practical steps that avoid a first rejection"},{type:"ul",items:["Add a working demo account (with sample data already populated) in the Notes field of App Review Information â an empty demo account is only marginally better than none.","Test your In-App Purchase flow with a real sandbox tester account before submitting, including restore purchases.","Publish the privacy policy URL and make sure the App Privacy questionnaire matches the SDKs you actually ship (ad networks and analytics tools count).","Write review notes for anything non-obvious â hardware requirements, region locks, a feature that only activates under specific conditions.","Run a TestFlight beta with a handful of real users first; most crash-related rejections are caught here before a human reviewer ever sees the build."]},{type:"h2",text:"What a rejection notice actually looks like"},{type:"p",text:"A rejection arrives in App Store Connect's Resolution Center as a specific guideline citation plus a short explanation, sometimes with a screenshot of exactly where the reviewer got stuck. It is not a form letter â reviewers reference the specific screen, flow, or piece of metadata that triggered the note. Reply in the same thread rather than resubmitting blind; you can ask a clarifying question if the note is genuinely ambiguous, and reviewers do respond. Resubmitting the identical build with no changes, hoping for a different reviewer, is a bad strategy â the same issue is still there, and it wastes a review cycle you could have used to actually fix it."},{type:"h2",text:"Guideline 2.5 â deprecated APIs and private frameworks"},{type:"p",text:"2.5 covers software requirements below the surface level: apps calling private, undocumented APIs, or built against SDKs old enough that Apple has stopped accepting them for new submissions. Static analysis catches most of this automatically before a human ever looks at your build, which means it's one of the few rejection categories you can usually rule out entirely just by building against a current SDK and avoiding anything you had to work around Apple's public APIs to access."},{type:"p",text:"None of this is exotic. The pattern across almost every rejection we've helped clients resolve is the same: something that works fine on the developer's own phone, with the developer's own account, in the developer's own network conditions, fails the moment someone outside that c
1ontext tries it. Review your submission the way an impatient stranger would use it, and most of Apple's rejection reasons stop being a mystery."}]},{slug:"planning-an-app-launch",title:"Planning a Product Launch on the App Store: A Realistic Timeline",dek:"Launch day is the easy part. The work that determines whether it goes well happens weeks earlier â here's the sequence we actually follow.",category:"Launch",readTime:"8 min read",published:"Feb 17, 2026",body:[{type:"p",text:'Founders often treat "submit to Apple" as the finish line of a launch. In practice it\'s closer to the midpoint. The store listing, the compliance pages, the beta test, and the review buffer all have their own lead times, and skipping any of them to hit a date usually costs more time later in the form of a rejection or a bad first-week rating.'},{type:"h2",text:"Work backward from your target date"},{type:"p",text:'We plan launch timelines backward from the date the app needs to be live, not forward from "whenever the code is done." A realistic runway from a feature-complete build to a live App Store listing is four to eight weeks, covering store assets, legal pages, a beta test cycle, submission, and a buffer for at least one round of review feedback. Compressing that window is possible, but it removes your margin for the inevitable first-submission note back from Apple.'},{type:"h2",text:"Store listing assets (2â3 weeks out)"},{type:"p",text:"The App Store Connect record needs more than an icon and a description. You need screenshots sized for each device class you support, an optional but high-converting app preview video, a 30-character subtitle, a 100-character keyword field (invisible to users, but the primary lever Apple's search algorithm reads), promotional text you can update without a new build, and a primary plus optional secondary category. The age rating questionnaire also feeds directly into what content and permissions Apple expects to see, so get it right the first time â changing it later can trigger a fresh review."},{type:"h2",text:"Legal and compliance pages"},{type:"p",text:'Your privacy policy URL and support URL both need to be live and reachable before submission â not "coming soon." The App Privacy nutrition label questionnaire in App Store Connect has to match what your app and its SDKs actually collect. There\'s also an export compliance question: most apps that only use standard HTTPS/TLS qualify for an encryption exemption, but you still have to answer the questionnaire accurately on every submission, and getting it wrong can hold up your build in "Waiting for Export Compliance."'},{type:"h2",text:"Beta testing with TestFlight"},{type:"p",text:"Internal testing (up to 100 people on your team, no review required) should catch obvious breakage. External testing (up to 10,000 tester
1s, requires a lightweight Apple review of the build itself) is where you find the crashes and confusing flows real users hit that your own team never would. Build processing after upload typically takes anywhere from a few minutes to over an hour depending on Apple's load â plan for that when you're testing close to a deadline."},{type:"h2",text:"Submission and review"},{type:"p",text:"Review turnaround varies and Apple makes no guarantees, though most submissions get a first response within a day or two. Give yourself buffer for at least one rejection-and-resubmit cycle regardless â even clean apps sometimes get a metadata note that needs a quick fix. You can also choose manual release, which lets an approved build sit ready until you flip the switch, useful if your launch is tied to a specific date, press embargo, or marketing push."},{type:"h2",text:"Day one and week one"},{type:"p",text:"The work doesn't stop at \"Ready for Sale.\" The first week is when you find out whether onboarding actually works for people who've never seen the app before, whether crash rates hold up under real device diversity, and whether App Store Connect's analytics (impressions, product page views, conversion rate, and early retention) are telling you anything you need to react to quickly."},{type:"h2",text:"A launch-week checklist"},{type:"ul",items:["Crash and error monitoring dashboard open and actually watched, not just installed","A fast path to ship a bug
1-fix build if something obvious surfaces on day one","Support inbox monitored â reviews and support emails are both early-warning signals","App Store Connect analytics checked daily for the first week, not just glanced at once","A plan for responding to public reviews, especially the first few â they set the tone for everyone who reads them after"]},{type:"h2",text:"Pricing, territories, and availability"},{type:"p",text:"Decide your price tier (or free-with-IAP structure) and which territories you're launching in well before submission â both are configured in App Store Connect and Play Console, and changing pricing globally after launch means navigating currency conversion tables Apple manages for you, or, on Android, adjusting per-territory prices manually if you've set them individually. If you're launching in a specific country first to test before a wider release, both stores support territory-limited availability, which is worth using deliberately rather than defaulting to \"all territories\" and discovering a market you weren't ready to support."},{type:"h2",text:"Coordinating marketing with an uncertain approval date"},{type:"p",text:'Because Apple doesn\'t guarantee a review turnaround time, committing to a hard public launch date before you have an approved build is a real risk. Manual release solves this cleanly: submit early, let the build sit in "Pending Developer Release" once approved, and only flip it live when your marketing, press, or partner timing is actually ready. This decouples "when Apple says yes" from "when the world finds out," which removes a surprising amount of stress from launch week.'},{type:"p",text:"Treat submission as one milestone in a longer sequence, not the deadline itself, and the actual launch tends to go a lot more smoothly."}]},{slug:"react-native-vs-native",title:"React Native vs. Native Swift/Kotlin: How We Choose for a New App",dek:"Cross-platform frameworks have closed most of the historical gap with native â but not all of it. Here's the actual decision framework, not a philosophy war.",category:"Engineering",readTime:"9 min read",published:"Mar 2, 2026",body:[{type:"p",text:'"Should we build native or React Native?" is one of the first questions almost every founder asks, and it usually gets an opinionated, one-size-fits-all answer from whoever\'s in the room. The honest answer depends on what the app actually needs to do, not on which stack a given engineer prefers.'},{type:"h2",text:"What React Native actually is today"},{type:"p",text:"React Native lets you write one JavaScript or TypeScript codebase that ships to both iOS and Android. The framework has matured significantly: the New Architecture replaces the old asynchronous JavaScript bridge with Fabric (a rewritten renderer) and TurboModules (synchronous native module access), and the Hermes JavaScript engine improves startup time and memory use over the old JavaScriptCore path. Combined with Expo's tooling, spinning up a cross-platform app and getting it building on both stores is faster than it was even two or three years ago."},{type:"h2",text:"What native still gives you"},{type:"p",text:'Native Swift/SwiftUI on iOS and Kotlin/Jetpack Compose on Android still win in a specific set of situations: day-one access to brand-new OS APIs (new ARKit capabilities, Live Activities, widgets, visionOS support) without waiting on a community bridge library to catch up; heavy camera, AR, or on-device machine-learning workloads where you want direct, unmediated access to AVFoundation, ARKit, ARCore, or Core ML/NNAPI; complex custom animation and gesture work where a JavaScript layer adds real overhead; and platform-native design idioms that make an app feel genuinely "at home" rather than merely functional on each OS.'},{type:"h2",text:"Where React Native wins"},{type:"p",text:"For a business app whose core value is CRUD workflows, content display, forms, and standard navigation â the shape of most internal tools, marketplaces, and content apps â React Native lets one team ship both platforms from a single codebase, with a much larger pool of hire-able generalist engineers than native iOS-and-Android specialists combined. If you're also building a web app and want to share business logic, that overlap compounds the advantage."},{type:"h2",text:"Flutter, briefly"},{type:"p",text:"Flutter is a legitimate third option: it uses Dart and renders through its own engine (historically Skia, moving to Impeller) rather than mapping to native platform widgets. The tradeoffs land in a similar place to React Native â strong cross-platform velocity, a smaller native-feeling ceiling than fully native â with a smaller hiring pool for Dart specifically compared to JavaScript/TypeScript."},{type:"h2",text:"Our actual decision framework"},{type:"ul",items:["Heavy camera, AR, depth sensing, or on-device ML as the core feature â native. When we built SurfaceIQ, a surface-scanning app using ARKit/ARCore depth capture and real-time image analysis, we wrote the capture and analysis pipeline natively per platform (with the pure math ported line-for-line between Swift and Kotlin) specifically because that workload needed direct hardware access.","A straightforward business or content app on a tight timeline â React Native, one team, both platforms at once.","You're also shipping a web app and want shared business logic â React Native (or a shared TypeScript layer) to avoid maintaining the same logic three times.","You're launching on one platform first and want to move as fast as possible on that platform specifically, without cross-platform overhead â
1 native for that platform, with the second platform as a later, separately-scoped phase."]},{type:"h2",text:"Hiring realities most founders underweight"},{type:"p",text:"Stack choice is also a hiring decision, and it's worth being honest about how that market actually looks. React Native and TypeScript draw from the much larger pool of web-adjacent engineers, which generally means faster hiring and a wider range of contractor and agency options. Native iOS and Android specialists are a smaller, more specialized pool â strong ones are excellent, but harder to source quickly, and a team that hires one strong Swift engineer and one strong Kotlin engineer is effectively running two parallel hiring searches instead of one."},{type:"h2",text:"Can you switch later?"},{type:"p",text:"Starting cross-platform and moving specific features to native later is realistic â React Native supports native modules precisely so you can drop into Swift or Kotlin for the one screen or workload that needs it, without rewriting the whole app. Starting native and later adding a cross-platform layer is a heavier lift, since you're retrofitting shared logic onto two codebases that have already diverged. If there's real uncertainty about which platform will matter most, starting cross-platform and hardening specific native modules as needs emerge is usually the lower-risk path."},{type:"p",text:'Neither stack is categorically "better." The honest framing is: what does the app need to do in its hardest, most demanding feature, and does the team have (or can it hire) the skill set that stack requires? Answer that first, and the framework question mostly answers itself.'}]},{slug:"mvp-scoping-first-time-founders",title:"MVP Scoping for First-Time Founders: What to Build First (and What to Cut)",dek:"Most first-time founders overscope their MVP by two or three features. Here's how to find the smallest version that actually tests your idea.",category:"Strategy",readTime:"8 min read",published:"Mar 16, 2026",body:[{type:"p",text:"The single most common mistake we see in first product conversations is scope, not technology. Founders arrive with a full feature list â social login, an admin dashboard, push notifications, in-app chat, multi-language support â for a product that hasn't yet proven anyone wants the core idea. An MVP's job is to test the core idea as cheaply and quickly as possible, not to arrive fully formed."},{type:"h2",text:"Start with one user, one journey"},{type:"p",text:"Pick a single primary user and trace the one path that delivers your product's core value, start to finish. Everything that isn't on that path is a candidate for v2. If your product has two very different user types (say, a marketplace with buyers and sellers), you often still need to build for both to make the loop work â but keep each side's version of that flow as narrow as possible."},{type:"h2",text:"The three-feature test"},{type:"p",text:"If you can't describe your MVP in three features or fewer, it isn't scoped yet. This isn't a rule about counting â it's a forcing function. Try to state your MVP that way, and the features that don't fit reveal themselves as things you're building because they feel important, not because the launch depends on them."},{type:"h2",text:"Common things to cut from v1"},{type:"ul",items:["Every social login provider â pick one or two (Apple is required if you offer any third-party login on iOS; add Google as the common second option) instead of five","A custom admin dashboard â a tool like Retool, or even direct access to Firebase/Supabase's own console, covers early operational needs","Push notifications, unless the core loop depends on real-time alerts","In-app messaging or chat between users","Multi-language support before you know which markets matter","Building both iOS and Android simultaneously, unless your audience is genuinely split â most early apps skew heavily to one platform"]},{type:"h2",text:"What not to cut"},{type:"ul",items:["Onboarding that actually explains the product's value â a confusing first five minutes kills more MVPs than missing features do","A working payment or subscription flow, if the product is meant to make money â testing willingness to pay is often the entire point of the MVP","Basic analytics instrumentation from day one, so you have real usage data instead of guesses when deciding what's next","Account deleti
1on, if you support accounts at all â Apple requires it for App Store approval, and Android increasingly expects it too","Crash reporting â you cannot iterate on a product you can't see failing"]},{type:"h2",text:"Build vs. buy for MVP infrastructure"},{type:"p",text:"Almost nothing about authentication, payments, or basic backend infrastructure is worth building from scratch at MVP stage. Managed auth (Firebase Auth, Clerk), subscription/entitlement management (RevenueCat), and a managed backend (Firebase, Supabase) let a small team spend its limited engineering time on the parts of the product that are actually differentiated, instead of reimplementing solved problems."},{type:"h2",text:"A scope example"},{type:"p",text:"As an illustration: a founder wants a booking app for independent fitness instructors. The full vision includes payments, reviews, recurring class packages, waitlists, instructor payouts, and a marketing website with SEO-optimized instructor profiles. A defensible MVP scope is: instructor creates a class with a time and price, a client books and pays for one class, both get a confirmation. That's the entire loop. Reviews, packages, waitlists, and payouts are all real features â for v2, once the core loop has proven that instructors and clients will actually use it."},{type:"h2",text:"Signs you've overscoped"},{type:"ul",items:['You\'re describing your MVP with an "and" more than twice â a real MVP description is usually one sentence, not a list',"You're designing an admin dashboard before you have a single real user to administer","You're building settings and customization options for preferences no user has actually asked for yet","You're planning both platforms simultaneously with no clear reason your audience needs both on day one","Your team can't agree on what the MVP even is â that's a scoping problem, not a communication problem"]},{type:"h2",text:"How long should this actually take"},{type:"p",text:"A properly scoped MVP â one core journey, managed infrastructure for auth and payments, a single primary platform â is realistically a six-to-ten week build from a finished design, not a six-month one. If your MVP timeline is stretching toward six months, that's usually a sign the scope has quietly grown past \"minimum\" rather than a sign the product is unusually complex. It's worth revisiting the three-feature test at that point rather than pushing forward on the original plan."},{type:"p",text:"The goal of an MVP isn't to be small for its own sake â it's to get real usage data as fast as possible, so the next round of decisions is based on what people actually do instead of what you assumed they'd want."}]},{slug:"app-store-optimization-basics",title:"App Store Optimization Basics: Getting Found on the App Store and Google Play",dek:"ASO is the closest thing app stores have to SEO â and Apple and Google's algorithms work differently enough that the same listing strategy doesn't work on both.",category:"Marketing",readTime:"8 min read",published:"Mar 30, 2026",body:[{type:"p",text:"App Store Optimization is search engine optimization for app stores â the discipline of getting your app to surface for the searches your future users actually run, and then converting that visibility into installs. It's easy to underinvest in because it doesn't require an engineering sprint, but the fields you fill in on your store listing meaningfully change how discoverable your app is."},{type:"h2",text:"The ranking fields you actually control"},{type:"p",text:"On the App Store: the app name (30 characters), the subtitle (30 characters), the hidden keyword field (100 characters, comma-separated, never shown to users but read directly by search), and your primary/secondary category. On Google Play: the title (30 characters), a short description (80 characters), and a full description (up to 4,000 characters) â all of which factor into search relevance."},{type:"h2",text:"Apple vs. Google â genuinely different algorithms"},{type:"p",text:"This is the part people get wrong by treating both stores the same way. Apple's search weights the app name, subtitle, and that invisible keyword field heavily, and does not meaningfully index the long-form description for search purposes â so stuffing keywords into your Ap
1p Store description does close to nothing. Google Play's algorithm behaves much more like traditional web search: it does read and weight the full description text, so relevant keyword usage there â written naturally, not stuffed â actually affects ranking."},{type:"h2",text:"Screenshots and preview video are conversion, not discovery"},{type:"p",text:"Once someone lands on your product page, screenshots do the selling. The first two or three matter disproportionately, since that's what's visible without scrolling on most devices. If you have meaningful markets outside English, localizing screenshots (not just translating the description) measurably improves conversion in those markets. An App Store preview video autoplays muted in the listing â treat the first few seconds as if sound is off, because for most viewers it is."},{type:"h2",text:"Ratings and reviews"},{type:"p",text:"Both platforms offer a native in-app rating prompt (SKStoreReviewController on iOS, the Play In-App Review API on Android) that doesn't take the user out of the app. Trigger it after a genuinely positive moment â right after a task succeeds, not on first launch â and note that Apple limits how often your app is allowed to show the system prompt to a given user, so you can't spam it even if you wanted to. Responding to reviews, especially critical ones, is visible to every future visitor reading them and is worth the time."},{type:"h2",text:"Iterate with the data both stores give you"},{type:"p",text:"App Store Connect shows impressions, product page views, and conversion rate as a funnel, so you can see whether a listing change actually moved the needle. Google Play Console goes further with pre-launch reports and native store listing experiments â genuine A/B tests of icon, screenshots, and description variants against real traffic. Apple's closest equivalent, Product Page Optimization, lets you test alternate icons and screenshot sets. Both are worth running before assuming a listing change helped or hurt."},{type:"h2",text:"Common ASO mistakes"},{type:"ul",items:["Repeating the app name inside the Apple keyword field â the name is already indexed, so that space is wasted on words search already finds you for","Writing an App Store description as if it's read by the algorithm, when Apple mostly isn't reading it â that effort is better spent on Google Play's description instead","Ignoring the subtitle on iOS, which is prime, heavily-weighted real estate directly under the app name","Never revisiting keywords after launch, even after search-term data in App Store Connect shows which terms are actually driving impressions","Treating localization as translation only, without adapting screenshots and preview video for each market's actual visual and cultural expectations"]},{type:"h2",text:"Backlinks and off-store discovery"},{type:"p",text:"Some of what drives installs happens outside the store entirely â Universal Links and App Links let a regular web link open directly into your app instead of a browser if it's installed, which matters for anyone landing on your marketing site or a shared link from an existing user. A smart App Banner (iOS) or Play Instant experience can convert web visitors who don't have the app yet, and neither of these substitutes for in-store ASO, but both compound with it."},{type:"p",text:"Category selection deserves more attention than it usually gets, too. A crowded primary category (Productivity, Health & Fitness) means more competition for the same searches, while a well-chosen secondary category can put you in front of a smaller, more targeted audience with less competition. Neither store lets you change primary category casually without it affecting your ranking history in the old one, so this is worth getting right at launch rather than treating it as easily reversible later."},{type:"p",text:"ASO isn't a one-time setup â it's a listing you keep tuning as you learn what search terms and screenshots actually convert, the same way you'd tune a landing page."}]},{slug:"subscription-pricing-paywall-design",title:"Subscription Pricing and Paywall Design Patterns That Convert",dek:"The mechanics of trials, tiers, and entitlement checks matter as much as the pricing numbers themselves. Here's how the pieces actually fit together.",category:"Monetization",readTime:"10 min read",published:"Apr 13, 2026",body:[{type:"p",text:"Subscription pricing gets discussed as if it's purely a numbers problem â what to charge â but most of the conversion difference between apps comes from the mechanics around the price: when you show the paywall, how the trial works, and whether your entitlement checks are actually trustworthy. Those are engineering and design decisions, not just pricing decisions."},{type:"h2",text:"Pick a monetization shape first"},{type:"p",text:"Before designing screens, decide the shape: freemium with a metered limit (a certain
1number of uses free, then pay), a hard paywall (no meaningful use without paying), a trial-gated subscription (full access for a set period, then billing starts), or a one-time purchase with an optional subscription add-on for ongoing features. Each shape implies a different onboarding flow, so this decision has to come before UI design, not after."},{type:"h2",text:"Trial design mechanics"},{type:"p",text:'The standard pattern on both platforms is a trial that requires a payment method up front and converts automatically unless cancelled â StoreKit 2 introductory offers on iOS, base plans with offers in Play Billing on Android. A no-card trial (full access with no payment method collected) is far less common because it requires you to build your own manual conversion flow rather than relying on the store\'s billing system, but it does remove a common point of user hesitation. Seven days is the most common trial length we see, long enough to reach a real "aha" moment in most products without giving away so much value that paying feels unnecessary.'},{type:"h2",text:"Anchoring and plan structure"},{type:"p",text:"A three-tier structure (monthly, annual, sometimes lifetime) is the most common pattern, with the annual plan visually highlighted as the default choice â it's usually both the best deal for the user and the best retention outcome for you, since annual subscribers churn far less often than monthly ones. Displaying the annual price broken down to a per-week or per-month equivalent, next to the monthly price, is standard because it reframes an annual commitment as a small recurring number instead of one large charge."},{type:"h2",text:"When to show the paywall"},{type:"p",text:"A soft paywall lets someone browse and explore before hitting a wall at the moment they try to act (save, export, unlock a feature). A hard paywall blocks the core feature immediately. Generally, showing the paywall after the user has experienced a real taste of value â not before they've seen anything â converts better than gating the entire app from launch, though there are legitimate hard-paywall products where the value is obvious enough (a utility that does one specific thing) that an immediate paywall with a trial works fine."},{type:"h2",text:"Server-side entitlement, not just a client-side flag"},{type:"p",text:'Checking "is this user subscribed" only on the client is not secure â a modified client can simply lie about it. Real subscription products validate purchases server-side, either through a service like RevenueCat or a custom receipt-validation pipeline, and listen for App Store Server Notifications (v2) and Google Play Real-time Developer Notifications so your backend knows immediately about renewals, cancellations, refunds, and billing issues rather than trusting a stale client-reported state.'},{type:"h2",text:"Price increases and win-back offers"},{type:"p",text:"Apple's guideline 3.1.2 requires a specific in-app consent flow before an existing subscriber can be charged a higher price â you can't simply raise the price silently for current subscribers. Both platforms support win-back and promotional offers aimed at subscribers who've cancelled or lapsed, which is often a cheaper way to recover revenue than acquiring a brand-new subscriber."},{type:"h2",text:"Free-tier design as part of the funnel"},{type:"p",text:"The free tier isn't just \"what's left over after the paid features\" â it's the mechanism that gets someone to the paywall with context. A free tier that's too generous removes any reason to upgrade; one that's too thin means most people never experience enough of the product to want to. Metered limits (a fixed number of uses, exports, or saved items) tend to communicate value more clearly than time-based trials alone, because the limit is tied to something the user actually did, not just a clock running in the background."},{type:"p",text:"It's also worth deciding early who owns pricing changes once the product is live. Because Apple and Google both cache and roll out price changes on their own timelines, and because guideline 3.1.2 gates any increase for existing subscribers behind an explicit consent flow, pricing isn't something you can adjust as casually as a line in a config file â treat it as a deliberate, occasional decision with real lead time, not a lever to pull reactively."},{type:"p",text:"We run this entitlement pattern â store billing plus server-side validation through RevenueCat â across our own subscription products, including SurfaceIQ and Flourish PDF, precisely because client-only checks don't hold up once an app has real usage. It's not more work worth skipping; it's the part of the paywall that actually has to be correct."}]},{slug:"privacy-manifests-att-compliance",title:"Privacy Manifests and App Tracking Transparency: A Compliance Primer",dek:"Two related but distinct Apple requirements trip up almost every app that uses ads or a handful of common third-party SDKs. Here's what each one actually requires.",category:"Privacy & Compliance",readTime:"9 min read",published:"Apr 27, 2026",body:[{type:"p",text:"Two separate Apple requirements get conflated constantly because both are about privacy and both showed up around the same time in the App Store's history: App Tracking Transparency, which governs cross-app tracking for advertising, and privacy manifests, which govern a specific technical category of API usage. They require different work, and missing either one can hold up a submission."},{type:"h2",text:"App Tracking Transparency (ATT) â iOS 14.5 and later"},{type:"p",text:"ATT governs \"tracking\" in Apple's specific sense: linking data collected in your app with data from other companies' apps or websites for advertising purposes, or sharing user or device data with a data broker. If your app does this â most commonly through an ad SDK using the device's advertising identifier (IDFA) â you must request permission through the AppTrackingTransparency framework before accessing the IDFA, using a system prompt whose accompanying explanation string (NSUserTrackingUsageDescription) you write yourself. Best practice is to ask at a moment where the value of allowing tracking is clear, rather than as the very first thing the app does, and the app must remain fully usable for its non-advertising functionality even if the user declines."},{type:"h2",text:'The App Privacy "nutrition label"'},{type:"p",text:"Separately, every app publishes a structured privacy disclosure in App Store Connect covering data categories â contact info, health, financial info, location, sensitive info, contacts, user content, browsing and search history, identifiers, purchases, usage data, and diagnostics. For each category you collected, you declare whether it's linked to the user's identity and whether it's used for tracking. This has to match actual behavior, including the behavior of every third-party SDK you ship â Apple has pulled apps post-launch over a mismatch between the declared label and observed data flows."},{type:"h2",text:"Privacy manifests (PrivacyInfo.xcprivacy)"},{type:"p",text:'Since spring 2024, apps using certain "required reason APIs" â file timestamp APIs, UserDefaults, system boot time, and available disk space, among others â must declare, in a PrivacyInfo.xcprivacy file, why they use that API, from a fixed list of Apple-approved reasons. On top of that, many common third-party SDKs above a certain size threshold are now required to ship their own signed privacy manifest; if a dependency you use hasn\'t updated to include one, Apple will flag it, and eventually reject builds that include it. Checking your dependency list for privacy-manifest compliance is now a standard part of pre-submission review, not an edge c
1ase.'},{type:"h2",text:"Android's parallel: Data safety and runtime permissions"},{type:"p",text:"Google Play has its own structured disclosure â the Data safety section in Play Console â with a similar spirit to Apple's label, though a different form. Android's permission model is enforced at runtime rather than through a single declared string: dangerous permissions (camera, location, contacts, and so on) are requested individually when the feature is first used, scoped storage since Android 10 limits blanket file-system access, and POST_NOTIFICATIONS became its own runtime-requested permission starting with Android 13, rather than being granted automatically."},{type:"h2",text:"A practical compliance checklist"},{type:"ul",items:["Audit every third-party SDK for tracking behavior and privacy-manifest compliance before you assume your own code is the only thing that matters","Write ATT and permission usage-description strings that are specific and honest about why you need the data, not generic boilerplate","Fill out the App Privacy questionnaire from the actual data flows in the app, ideally cross-checked against a network traffic capture, not from memory","Re-check the nutrition label any time you add a new SDK or analytics tool â it drifts out of date quietly","Build the account-deletion and data-export paths your privacy policy promises, not just the policy text itself"]},{type:"h2",text:"What actually happens if you get it wrong"},{type:"p",text:"The consequences aren't hypothetical. A mismatched privacy label is grounds for rejection during review, and Apple has removed already-live apps after a later audit found a mismatch between the declared label and observed network behavior. A missing privacy manifest on a bundled SDK increasingly blocks submission outright rather than producing a warning. And beyond App Review entirely, inaccurate privacy disclosures can create real legal exposure under state and federal privacy law â this is compliance work with actual teeth, not a formality to get past a reviewer."},{type:"p",text:"Timing matters operationally, too â both requirements have shown up as new obligations mid-lifecycle for existing apps, not just for new submissions. Treat privacy compliance as something you revisit at every release, especially any release that adds a new SDK, not as a box checked once at initial launch and forgotten."},{type:"p",text:"None of this is exotic legal work â it's closer to a checklist that has to be run through deliberately, once per release, rather than assumed to still be accurate from the last time someone looked at it."}]},{slug:"onboarding-flow-design",title:"Onboarding Flow Design: Principles That Actually Reduce Drop-off",dek:"The first ninety seconds decide whether someone becomes a user. Here's what separates onboarding that works from onboarding that just looks polished.",category:"Product Design",readTime:"8 min read",published:"May 11, 2026",body:[{type:"p",text:"Onboarding gets treated as a design exercise â pick a nice illustration style, write some friendly copy â when it's really a sequencing problem. The order you ask for things, and how quickly you get to something the user actually cares about, matters more than how polished any individual screen looks."},{type:"p",text:"This matters more than it seems, because onboarding is the one part of the product every single user experiences, without exception â you can build a great feature that half your users never find, but every user who installs the app goes through onboarding. A small improvement there compounds across your entire user base in a way that a niche feature improvement never does."},{type:"h2",text:"Get to the core value before asking for anything"},{type:"p",text:"Every screen between install and the moment a user experiences your product's actual value is a chance to lose them. A long welcome carousel explaining features nobody's used yet is friction, not education â most users learn a feature better by touching it than by reading a slide about it. Where possible, let people reach a real, working version of the core action before asking them to sign up, set preferences, or watch an intro."},{type:"h2",text:"Ask permission for permissions"},{type:"p",text:"System permission prompts (camera, location, notifications, tracking) are one-shot on iOS in a meaningful sense â if a user declines, you generally can't re-trigger the native dialog; you have to send them to Settings, which almost nobody does. A \"priming\" screen that explains why you're about to ask, shown right before the system prompt, meaningfully improves the accept rate compared to firing the system dialog cold on first launch."},{type:"h2",text:"Progressive account creation"},{type:"p",text:"Where the product allows it, let people explore before forcing an account. When an account genuinely is required for the core loop to work â most social or transactional products â explain why right before you ask, rather than presenting sign-up as the very first screen with no context."},{type:"h2",text:"Personalization without interrogation"},{type:"p",text:"A handful of onboarding questions (two to four, not ten) can meaningfully improve relevance if the app actually uses the answers visibly and quickly â a personalized home screen, a tailored recommendation. Questions that don't visibly change anything downstream just read as friction, and should always have a skip option so an impatient user isn't blocked from proceeding."},{type:"h2",text:"Empty states are part of onboarding"},{type:"p",text:"A brand-new account with a genuinely blank screen â no data, no content, no example â reads as broken, even when it's working correctly. Sample content, a clear first action, or a short guided task in that empty state does more for first-session retention than almost any other single change."},{type:"h2",text:"Instrument before you redesign"},{type:"p",text:"The most common mistake in onboarding work is redesigning based on a guess about where people drop off. Instrument each onboarding step as its own event, look at the actual funnel, and find the specific screen where the drop happens before changing anything. It's common for the real problem to be a single confusing step rather than the whole flow â and redesigning the wrong ten screens
1wastes a sprint that fixing the one real problem wouldn't have."},{type:"h2",text:"Skip options and the anxious user"},{type:"p",text:"Some users want to move fast and skip every optional step; others read every word before proceeding, worried about getting a setting wrong. Good onboarding design serves both without penalizing either â a visible, honest skip option (not a hidden tiny link in the corner) respects the first group, while sensible defaults and the ability to change any onboarding choice later in Settings reassures the second group that nothing they pick during setup is a permanent, high-stakes decision."},{type:"h2",text:"Onboarding doesn't end at the first session"},{type:"p",text:'The first-session flow gets most of the design attention, but a returning user who hasn\'t come back in a while needs a different, lighter kind of re-onboarding â a reminder of what changed, not the full introduction again. Treating "day one" and "day seven, after a gap" as the same problem is a common miss; a returning user already knows the basics and just needs to be reoriented quickly, not walked through the product from scratch.'},{type:"h2",text:"Testing changes properly"},{type:"p",text:"Once you're instrumented, resist the urge to ship a full onboarding redesign in one shot and hope it helped. Where you have the traffic to support it, an A/B test of a single change â reordering two steps, adding a priming screen before one specific permission â isolates what actually moved the funnel from what just happened to coincide with a broader release. At lower traffic volumes where a formal test isn't statistically meaningful, at least change one variable at a time and watch the same funnel step before and after, rather than redesigning everything simultaneously and losing the ability to tell what worked."},{type:"p",text:"Good onboarding isn't about making every screen prettier. It's about removing screens, reordering the ones that remain, and using real data instead of intuition to find out which one is actually costing you users."}]},{slug:"backend-stack-for-small-teams",title:"Choosing a Backend Stack for a Small Team",dek:"For a one-to-three-person team, the right backend choice is rarely the most technically impressive one â it's the one that gets you to real users fastest without boxing you in.",category:"Engineering",readTime:"9 min read",published:"May 25, 2026",body:[{type:"p",text:"Backend architecture decisions get made in a vacuum more often than any other early technical choice â an engineer picks what they know best, or what's trending, without weighing it against the team's actual size and the product's actual data shape. For a small team, the right answer is usually narrower than the options make it look."},{type:"h2",text:"Firebase â Auth, Firestore, Functions, Storage"},{type:"p",text:"Firebase remains the fastest path from zero to a working authenticated app with real-time data sync, tight first-party SDKs on iOS and Android, and a genuinely generous free tier for early-stage traffic. Its tradeoffs are real: Firestore's NoSQL document model makes complex relational queries (joins across collections, in particular) awkward compared to SQL, and cost curves can climb quickly once you're at meaningful scale with heavy read/write patterns. Security rules take real care to get right â an over-permissive rule set is a common, quietly serious mistake."},{type:"h2",text:"Supabase"},{type:"p",text:"Supabase pairs a real Postgres database with Auth, Storage, and Edge Functions on top, open-source and self-hostable if you ever need to leave the managed service. When your data model is genuinely relational â orders that reference customers that reference products, with real joins and constraints â Postgres and row-level security policies are a more natural fit than a document database, and you keep the option to write actual SQL for reporting and analytics later."},{type:"h2",text:"Custom Node/Express or FastAPI with managed Postgres"},{type:"p",text:"Rolling your own backend gives full control and zero platform lock-in, at the cost of owning authentication, infrastru
1cture, scaling, and operations yourself. This is the right call when your requirements are genuinely unusual â an existing system to integrate deeply with, compliance requirements a managed platform doesn't cover well, or a team that already has backend engineers and infrastructure experience sitting idle. It's rarely the right call purely because it feels more \"real\" than a managed backend."},{type:"h2",text:"Serverless vs. always-on"},{type:"p",text:"Cloud Functions, Cloud Run, and Lambda-style serverless compute scale to zero and back up automatically, which is ideal for early-stage, spiky, or low-traffic workloads â you don't pay for idle capacity, at the cost of occasional cold-start latency. A small, always-on server is more predictable in latency but you're paying for capacity around the clock whether it's used or not. For most early products, serverless wins on cost; for latency-sensitive real-time features, an always-on process is often worth the tradeoff."},{type:"h2",text:"What actually matters for a one-to-three person team"},{type:"ul",items:["Time to first real user beats architectural purity â a working product with a managed backend today outperforms a perfectly architected one that ships two months later","Pick the stack your team already knows well over the one that's theoretically more powerful","Avoid microservices at MVP stage â a single well-organized backend is easier for a small team to reason about and deploy","Use managed authentication instead of rolling your own â session handling, password resets, and social login all have sharp edges that aren't worth relearning","Plan for a migration path, not a rewrite â pick a stack you can grow inside of for a while, not one you'll have to replace at the first sign of real traffic"]},{type:"h2",text:"Observability from day one, not after the first incident"},{type:"p",text:"Small teams routinely skip logging, error tracking, and basic uptime monitoring until something breaks in production and there's no record of what happened. This is one of the cheapest investments available â a managed backend's built-in logging plus a lightweight error-tracking tool costs little in setup time and pays for itself the first time a user reports a problem you'd otherwise have no way to diagnose. Treat it as part of the initial build, not a follow-up task for \"later.\""},{type:"h2",text:"Authentication complexity grows faster than people expect"},{type:"p",text:"A single-tenant app with email/password login is simple regardless of backend choice. The complexity shows up later â adding social login providers, supporting multiple organizations or teams per account, role-based permissions, or single sign-on for an enterprise customer. Managed auth providers (Firebase Auth, Supabase Auth, Clerk) handle most of this progression without a rewrite; a hand-rolled auth system built for the simple case often needs real surgery once a customer asks for SSO or team accounts, which is one more reason to default to managed auth even on a stack you otherwise built yourself."},{type:"p",text:"We run Firebase/GCP for most client products where the data is naturally document-shaped and the team wants managed infrastructure, and Postgres-backed services when the data model is genuinely relational or the client already has backend engineering in-house. Neither is the \"correct\" default â the product's data shape and the team's existing skills should decide it."}]},{slug:"app-development-cost-timeline",title:"Custom App Development: Realistic Cost and Timeline Expectations",dek:'"How much will this cost" and "how long will this take" are the first two questions every founder asks â and the honest answer is always a range driven by a specific set of variables, not a fixed number.',category:"Planning",readTime:"9 min read",published:"Jun 8, 2026",body:[{type:"p",text:'Every custom app development conversation eventually gets to cost and timeline, and the honest answer is always "it depends" â but it depends on a specific, knowable set of variables, not on vague complexity. Understanding those variables is more useful to a founder than any single number, because it tells you which decisions actually move your budget and which don\'t.'},{type:"h2",text:"The variables that actually drive cost"},{type:"p",text:"Number of platforms (one vs. two vs. web-plus-mobile) multiplies engineering surface area directly. Backend complexity â a simple CRUD API versus real-time features, complex business logic, or heavy data processing â is often a bigger cost driver than the UI itself. Third-party integrations (payments, maps, video calling, AI features) each bring their own setup time, ongoing cost, and edge cases. How much design work exists before engineering starts matters enormously: a team building from finished, detailed designs moves far faster than one figuring out layout and interaction decisions mid-build. And regulatory requirements â healthcare data, financial services, anything with a compliance framework attached â add real, non-optional scope."},{type:"h2",text:"Typical timeline bands"},{type:"p",text:"These are general ranges, not guarantees for any specific project: a simple, single-platform MVP with a managed backend typically runs six to ten weeks from a finished design to a submittable build. A two-platform app with real backend logic and a couple of integrations more typically runs ten to sixteen weeks. A complex product â multiple integrations, custom AI features, non-trivial compliance requirements â commonly runs four to six months or more. Every one of these bands assumes design is largely settled before engineering starts; unresolved design decisions mid-build are one of the most reliable ways to blow past any of these ranges."},{type:"p",text:"It's also worth separating the cost of the initial build from the cost of validating the idea. A founder who needs to know whether people want the product at all is often better served by a narrower, cheaper first version than by a fully-realized one â spending toward the top of a timeline band before there's any real usage data is a bet, and it's worth being deliberate about how large a bet you're making before real users have told you anything."},{type:"h2",text:"Fixed bid vs. time-and-materials"},{type:"p",text:"A fixed-bid engagement gives budget certainty but only works when scope is genuinely well-defined up front â any ambiguity gets priced as risk by whoever's quoting it, which usually means a higher number than the work would cost under clearer scope. Time-and-materials is more flexible as the product evolves but requires more active budget management from the client. A short, paid discovery or scoping phase before committing to either structure reduces risk on both sides: it turns vague requirements into a real spec before real money is committed to a build."},{type:"h2",text:"Where budgets quietly blow up"},{type:"ul",items:['Scope creep after seeing v1 â seeing a real, working product makes new ideas feel urgent, and "just one more thing" adds up fast',"Unbudgeted third-party API costs at scale â a mapping, AI, or messaging API that was nearly free in testing can become a real line item at production volume","App Review rejection cycles that weren't planned for as buffer time","A backend data model that didn't anticipate a feature added later, requiring a partial redesign instead of a simple addition","Skipping a real design phase and making layout decisions in code, which is almost always slower and more expensive than deciding them on paper first"]},{type:"h2",text:"A phased approach that controls risk"},{type:"p",text:'The sequence that tends to hold budgets closest to their estimate: a short discovery and scoping phase to nail down the actual MVP feature set, a design phase that resolves the UI before code is written against it, engineering delivered in vertical slice
1s (a complete, testable feature at a time, not "all the backend, then all the frontend"), an internal beta before public release, and a submission buffer that assumes at least one round of App Review feedback rather than a same-day approval.'},{type:"h2",text:"Maintenance is a real, ongoing cost â budget for it"},{type:"p",text:"The cost conversation often stops at launch, but OS updates, store policy changes, and third-party SDK updates are a recurring maintenance surface, not a one-time expense. Apple and Google both ship annual OS updates that periodically require code changes to stay compliant (a new minimum SDK requirement, a deprecated permission model, a new privacy disclosure). Budgeting a modest ongoing maintenance allocation â even a few hours a month for a simple app â avoids the common trap of a product that worked perfectly at launch quietly breaking a year later when nobody's watched it."},{type:"p",text:"A vague estimate isn't dishonesty â it's often the most honest answer available before scope, design, and integrations are actually locked. Getting those three things nailed down early is what turns a range into a
1number you can actually plan around."}]},{slug:"post-launch-analytics-setup",title:"Post-Launch: Setting Up Analytics and Iterating on Real Usage",dek:"Launch is when the guessing should stop. Here's the minimum analytics setup that turns real usage into an actual product roadmap.",category:"Growth",readTime:"8 min read",published:"Jun 22, 2026",body:[{type:"p",text:"Before launch, every product decision is a bet on what users will want. After launch, it doesn't have to be â if you've instrumented the app well enough to see what's actually happening. A surprising number of teams ship without doing this, then spend months making roadmap decisions on the same guesswork they were using before anyone had used the product."},{type:"p",text:"The good news is that most of this setup is a one-time investment, not an ongoing burden â once the core events and dashboards exist, checking them becomes a habit rather than a project. The teams that skip this step aren't saving time; they're deferring the same work to a later, more stressful moment, usually right after something has already gone wrong and they need answers they don't have."},{type:"h2",text:"Pick one north star metric before instrumenting anything"},{type:"p",text:'Before you wire up event tracking, decide what single metric best represents your product actually working for a user â a completed booking, a saved item, a message sent, a report generated. Every other event you track should ultimately help explain movement in that one number. Without this, teams end up tracking dozens of events with no clear hierarchy and no clear answer to "is the product working."'},{type:"h2",text:"The minimum event taxonomy"},{type:"ul",items:["install and first_open â did the app actually get used after download","signup_complete â did the user finish account creation, not just start it","your product's specific activation event â the first moment a user experiences real value (this is the one that varies most by product and matters most)","trial_start and subscription_start â for monetized products, the entire top of the revenue funnel","subscription_cancel â churn is as important to see clearly as growth","one or two key retention actions â behaviors that correlate with someone coming back"]},{type:"h2",text:"Tooling that covers most apps"},{type:"p",text:"Firebase Analytics (which is GA4 under the hood for mobile) is free, ties directly into Google Ads and BigQuery export, and is usually the right default for basic event tracking. For deeper funnel and retention analysis â cohort curves, path analysis, feature-adoption breakdowns â a dedicated product analytics tool like Mixpanel or Amplitude goes further than GA4's mobile reporting. If you're running subscriptions, don't rebuild MRR, trial conversion rate, and churn from raw receipt data â a tool like RevenueCat's dashboard already computes these correctly, including the edge cases (grace periods, billing retries, refunds) that are easy to get subtly wrong by hand. Crash and error reporting (Crashlytics, Sentry) rounds out the minimum stack â you can't iterate on stability you can't see."},{type:"h2",text:"Funnels and retention, not just totals"},{type:"p",text:"A total install count or total signups number tells you almost nothing about product health on its own. Day-1, day-7, and day-30 retention curves â what fraction of a cohort is still active that many days after their first session â are far more informative, because they show whether the product actually holds people rather than just attracting them once. Thinking in cohorts (people who joined this week vs. last week) rather than cumulative totals makes it much easier to see whether a recent change actually helped."},{type:"h2",text:"Closing the loop"},{type:"p",text:"Trigger review prompts after a genuinely positive activation event, not on a timer, and respect the platform's limits on how often the system prompt can appear for a given user. Triage top crashes weekly rather than letting a backlog build silently. And treat the roadmap as something the drop-off data should meaningfully influence â not override entirely, but inform â rather than something decided purely by internal opinion about what's interesting to build next."}
1,{type:"h2",text:"Privacy-conscious analytics without flying blind"},{type:"p",text:"Good instrumentation and privacy compliance aren't in tension if you set them up deliberately. Aggregate, non-identifying event data (which screen, which action, without a name or email attached) covers most product-analytics needs without touching sensitive personal data at all. Where you do collect identifiable data, make sure your App Privacy nutrition label and Play Data safety declaration actually reflect the analytics SDKs you've added â this is one of the most common places a privacy disclosure quietly drifts out of date, since a new analytics event often gets added without anyone revisiting the store disclosure that's supposed to describe it."},{type:"h2",text:"Common analytics mistakes worth avoiding"},{type:"ul",items:["Instrumenting dozens of events with no clear hierarchy, so nobody can say which ones actually matter","Looking only at cumulative totals instead of cohort-based retention, which hides whether recent users are actually sticking around",'Treating a dashboard as "done" once it\'s built, rather than checking it on a regular cadence',"Letting the privacy disclosure drift out of sync with the analytics SDKs actually shipping in the app","Making roadmap decisions purely from analytics with no user interviews at all â the data shows what's happening, not always why"]},{type:"p",text:"The apps that improve fastest after launch aren't the ones with the most features shipped â they're the ones whose teams can actually see, with real data, where users are getting stuck."}]},{slug:"ios-app-permissions-explained",title:"iOS App Permissions Explained: What to Ask For, and When",dek:"Every permission prompt is a small negotiation with the user. Understanding what each one actually unlocks â and what happens after a decline â changes how you should ask.",category:"Engineering",readTime:"8 min read",published:"Jul 6, 2026",body:[{type:"p",text:"Permission prompts are one of the few moments where iOS puts a system-level interruption directly between your app and the user, in language you write. Getting the timing and the explanation right measurably affects acceptance rates â and getting the mechanics wrong can mean a permission you genuinely need gets declined and then becomes very hard to recover."},{type:"h2",text:"The purpose-string requirement"},{type:"p",text:'Every permission that requires a system prompt on iOS also requires a matching "usage description" string in your Info.plist, written by you, explaining specifically why your app needs it. Apple\'s App Review checks that these strings are present, specific, and consistent with what the app actually does â a generic string like "this app needs your location" for a feature that clearly requires precise, continuous tracking is the kind of mismatch that draws review scrutiny.'},{type:"h2",text:"Common permission types and what they unlock"},{type:"ul",items:["NSCameraUsageDescription â access to the camera for photo or video capture","NSMicrophoneUsageDescription â audio recording, including as part of video capture","NSPhotoLibraryUsageDescription vs. NSPhotoLibraryAddUsageDescription â the first grants read access to the photo library, the second only lets you save new photos into it without reading existing ones, a meaningfully narrower and often sufficient ask","NSLocationWhenInUseUsageDescription vs. NSLocationAlwaysAndWhenInUseUsageDescription â foreground-only location versus background location tracking, which requires additional justification and is scrutinized more heavily in review","NSContactsUsageDescription â access to the address book","NSUserTrackingUsageDescription â the App Tracking Transparency prompt required before accessing the advertising identifier for cross-app tracking","NSBluetoothAlwaysUsageDescription â required for apps connecting to Bluetooth accessories","NSHealthShareUsageDescription and NSHealthUpdateUsageDescription â reading from and writing to HealthKit, requested separately","NSFaceIDUsageDescription â using Face ID for authentication within the app"]},{type:"h2",text:"Ask in context, not at launch"},{type:"p",text:"Requesting every permission your app might ever need during the first launch is a common and avoidable mistake. A priming screen explaining why you're about to ask, shown immediately before the system dialog and tied to the specific feature the user just tried to use, converts meaningfully better than a wall of unexplained system prompts on first open."},{type:"h2",text:"What happens after a decline"},{type:"p",text:"Once a user declines a system permission prompt, you cannot simply show it again â iOS won't re-trigger the native dialog. The only path back is deep-linking the user to your app's page in the Settings app (via UIApplication.openSettingsURLString), which most users never follow through on. Design a real, graceful degraded state for a declined permission rather than a dead end â a feature that's unavailable but explained, not a screen that just stops working."},{type:"h2",text:"Android's equivalent: runtime permissions"},{type:"p",text:'Android groups permissions into "dangerous" categories requested individually at runtime, similar in spirit to iOS. The shouldShowRequestPermissionRationale API lets you detect when a user has previously declined and show your own explanation before asking again, which iOS doesn\'t offer directly. Scoped storage, introduced in Android 10, limits broad file-system access to app-specific directories by default, and POST_NOTIFICATIONS became its own explicit runtime permission starting in Android 13, rather than being granted automatically the way notifications previously were.'},{type:"h2",text:"Testing the denial path, not just the happy path"},{type:"p",text:"Most teams thoroughly test what happens when a user grants a permission and rarely test what happens when they don't â and yet decline is a completely normal, expected outcome that App Review itself will exercise. Before submitting, walk through every permission-gated feature in your app with each permission explicitly denied, and confirm the app degrades gracefully rather than silently failing or crashing. A reviewer who declines a permission and hits a broken screen will treat that as a completeness bug (guideline 2.1), not a permissions edge c
1ase."},{type:"h2",text:"A note on special-case permissions"},{type:"p",text:"A handful of permissions carry extra review scrutiny beyond the standard usage-string requirement. Background location, Critical Alerts, and NFC-related entitlements all require additional justification in your App Review notes explaining specifically why the standard, more limited alternative isn't sufficient â Apple wants to see that you've considered the narrower option first, not just defaulted to the broadest permission available because it was easier to implement."},{type:"p",text:"It's worth remembering, too, that a granted permission isn't permanent consent to use it however you like â both platforms expect the data accessed through a permission to be used only for the purpose described in your usage string, and that expectation is enforced through App Review's ongoing scrutiny of app behavior, not just at the moment of the first prompt."},{type:"p",text:"Treat every permission request as something the user has to actively decide to trust you with, not a formality to get through as fast as possible â the apps that ask well tend to get granted access more often, and lose less goodwill when they don't."}]}],f=u.map(({body:e,...a})=>a);function g({blocks:e}){return(0,a.jsx)(a.Fragment,{children:e.map((e,t)=>"h2"===e.type?(0,a.jsx)("h2",{children:e.text},t):"p"===e.type?(0,a.jsx)("p",{children:e.text},t):"ul"===e.type?(0,a.jsx)("ul",{children:e.items.map((e,t)=>(0,a.jsx)("li",{children:e},t))},t):(0,a.jsx)("ol",{children:e.items.map((e,t)=>(0,a.jsx)("li",{children:e},t))},t))})}function x(){return(0,a.jsx)("section",{className:"sf-page",children:(0,a.jsxs)("div",{className:"sf-page-inner",children:[(0,a.jsx)("p",{className:"sf-section-eyebrow",children:"Journal"}),(0,a.jsx)("h1",{className:"sf-section-h2 sf-display",children:"From the studio"}),(0,a.jsx)("p",{className:"sf-section-lead",style:{marginBottom:40},children:"Notes on shipping app and web products â App Store review, platform choice, scoping, monetization, compliance, and everything else we think about before, during, and after a launch."}),(0,a.jsx)("div",{className:"sf-blog-grid",children:f.map(e=>(0,a.jsxs)("a",{href:`/blog/${e.slug}`,className:"sf-blog-card sf-glass",children:[(0,a.jsx)("p",{className:"sf-blog-cat",children:e.category}),(0,a.jsx)("h3",{children:e.title}),(0,a.jsx)("p",{children:e.dek}),(0,a.jsxs)("p",{className:"sf-blog-meta",children:[e.published," · ",e.readTime]})]},e.slug))})]})})}function b({slug:e}){let t=u.find(a=>a.slug===e)??null;if(!t)return(0,a.jsx)("section",{className:"sf-page",children:(0,a.jsxs)("div",{className:"sf-page-narrow sf-prose",children:[(0,a.jsx)("p",{className:"sf-section-eyebrow",children:"Journal"}),(0,a.jsx)("h1",{className:"sf-section-h2 sf-display",children:"Article not found"}),(0,a.jsxs)("p",{children:["We couldn't find that article. Browse everything on the ",(0,a.jsx)("a",{href:"/blog",children:"Soft Flourish journal"}),"."]})]})});let s=f.filter(e=>e.slug!==t.slug).slice(0,3);return(0,a.jsx)("section",{className:"sf-page",children:(0,a.jsxs)("div",{className:"sf-page-narrow",children:[(0,a.jsx)("a",{href:"/blog",className:"sf-article-back",children:"â Back to the journal"}),(0,a.jsxs)("div",{className:"sf-article-header",children:[(0,a.jsx)("p",{className:"sf-section-eyebrow",children:t.category}),(0,a.jsx)("h1",{className:"sf-section-h2 sf-display",children:t.title}),(0,a.jsxs)("p",{className:"sf-article-meta",children:[t.published," · ",t.readTime]})]}),(0,a.jsx)("div",{className:"sf-prose",children:(0,a.jsx)(g,{blocks:t.body})}),s.length>0?(0,a.jsxs)("div",{style:{marginTop:56,paddingTop:32,borderTop:"1px solid var(--sf-border)"},children:[(0,a.jsx)("p",{className:"sf-section-eyebrow",children:"More from the journal"}),(0,a.jsx)("div",{className:"sf-blog-grid",style:{marginTop:20},children:s.map(e=>(0,a.jsxs)("a",{href:`/blog/${e.slug}`,className:"sf-blog-card sf-glass",children:[(0,a.jsx)("p",{className:"sf-blog-cat",children:e.category}),(0,a.jsx)("h3",{children:e.title}),(0,a.jsx)("p",{children:e.dek})]},e.slug))})]}):null]})})}let y=[{kicker:"Productivity · macOS & Windows",name:"MeetWing",tagline:"A real-time AI co-pilot that listens to meetings and never stores what it hears.",problem:"People walk into calls â sales, interviews, client work â and lose the thread of what they meant to say the moment the conversation moves fast. The value of an assistant here lives entirely in latency: suggestions that arrive a few seconds late are useless.",approach:"We built a native menu-bar app that transcribes live audio locally and streams it to Google's Speech-to-Text (Chirp) and Gemini for c
1ontext-aware suggestions, with playbooks that tune the AI's behavior for sales calls, interviews, or client meetings. Privacy was a hard constraint from day one, not a policy written after the fact: audio is processed and discarded, never recorded or stored, and that design decision shaped the architecture at every layer.",shipped:"MeetWing ships on macOS via the Mac App Store and on Windows via a native WinUI3 app, with live transcription, AI-generated suggestions, an optional live web-grounding mode for real-time facts, and one-click export to PDF, TXT, Markdown, or CSV. Windows billing runs on RevenueCat Web Billing so the subscription flow works without a Microsoft Store IAP dependency.",tags:["macOS","Windows (WinUI3)","Google Speech-to-Text","Gemini","RevenueCat"],href:"/meetwing",linkLabel:"View the MeetWing product page â"},{kicker:"Productivity · iOS, iPad, Mac, Windows, Android",name:"Flourish PDF",tagline:"A full PDF editor â reading, editing, OCR, and AI â that never uploads a document.",problem:'Most "free" PDF tools route your document through a server to do anything beyond basic viewing â OCR, AI summarization, and editing typically require an upload. For anyone handling contracts, medical records, or client files, that\'s a real barrier, not a minor inconvenience.',approach:"We scoped the entire product around a single constraint: every feature, including OCR and AI, had to run on-device. That ruled out several off-the-shelf approaches and meant building a local OCR and document-processing pipeline per platform rather than calling a hosted API, which is the harder but more defensible path for a document tool built around user trust.",shipped:"Flourish PDF is live across Mac, Windows, iPhone, iPad, and Android with reading, editing, organizing, scanning, signing, and protecting PDFs, all processed locally. Windows uses RevenueCat Web Billing for subscription management, following the same web-billing pattern we built for MeetWing.",tags:["Swift/SwiftUI","Kotlin","Windows (.NET/WinUI)","On-device OCR","RevenueCat"],href:"/flourishpdf",linkLabel:"View the Flourish PDF product page â"},{kicker:"Productivity · macOS",name:"Message Exporter",tagline:"Getting your own message history off your own Mac, in a format you can keep.",problem:"Messages history is one of the few archives people genuinely need out of a device â for a legal dispute, an insurance claim, or simply to keep a record of conversations with someone who is gone â and macOS offers no export at all. The usual workarounds are hundreds of screenshots, or handing a copy of your entire chat database to a stranger's website.",approach:"The whole product is built around not being that website: it reads the local Messages database on the machine, resolves contact names and attachments, and renders the export locally, so there is no server that could leak a conversation even in principle. The harder engineering is in the rendering â a chat transcript has to survive becoming a paginated PDF or a spreadsheet row without losing who said what, when, or which image they attached, and each of the five output formats wants that structure a different way.",shipped:"Message Exporter is on the Mac App Store. It lists every conversation, searches names and message text, exports an exact date range to PDF, Excel, CSV, HTML, or plain text, collects shared media alongside the transcript, and can read a local iPhone backup for history that is no longer on the Mac.",tags:["Swift/SwiftUI","macOS","SQLite","PDFKit","StoreKit"],href:"/messageexporter",linkLabel:"View the Message Exporter product page â"},{kicker:"Utilities · Mac, Windows, iPhone, iPad & Android",name:"SystemZen",tagline:"Storage cleanup that tells you the truth about where the space went.",problem:'"Other" or "System Data" quietly swallowing tens of gigabytes is the most common complaint about a full device, and the category of app that promises to fix it has an earned reputation for the opposite â inflated numbers, one-click cleans that remove things you wanted, and a subscription attached to a progress bar.',approach:"We made the scan explainable before we made it fast: every figure traces back to real files or caches you can inspect, and nothing is removed without you seeing what it is first. Five surfaces share one design system, one account, and one entitlement, but each cleanup engine is deliberately platform-specific â what is safe to delete on Android has almost nothing in common with what is safe on macOS, and flattening that difference is exactly how these apps break machines. Scanning happens entirely on the device: file names, photos, and the installed-app list never reach a server.",shipped:"SystemZen runs on Mac, Windows, iPhone, iPad, and Android with a storage breakdown, cleanup runs recorded with their real reclaimed-s
1pace figures, and a shared sign-in across all five surfaces. The mobile free tier is ad-supported; the paid tier is billed through whichever store the user came from.",tags:["Swift/SwiftUI","Kotlin","WinUI","Firebase","RevenueCat"],href:"/systemzen",linkLabel:"View the SystemZen product page â"},{kicker:"Inspection & field tools · iOS & Android",name:"SurfaceIQ",tagline:"A camera-and-depth surface scanner that flags cracks, stains, dents, and wear.",problem:"Visual inspection of surfaces â walls, floors, equipment, vehicles â is normally a fully manual, subjective process. The goal was a mobile tool that could highlight anomalies objectively and consistently, without pretending to replace professional judgment where it actually matters (structural or safety-critical calls).",approach:"This is a case where native development wasn't optional: real-time depth capture needs direct access to ARKit's scene depth on iOS and ARCore's depth API on Android, and those frameworks can't share a camera session with a standard capture pipeline, so the two modes are a hardware-level swap, not an add-on. We ported the core anomaly-detection math â grid-based color and texture comparison against a stored baseline, followed by clustering and classification â line-for-line between Swift and Kotlin, so both platforms produce identical results from the same algorithm, and kept that math fully unit-testable and separate from the platform-specific camera code.",shipped:"SurfaceIQ runs real live-camera scanning (not canned demo output) with on-device RGB and depth anomaly detection, baseline comparisons, and PDF report export. The free tier includes ad-supported scanning with a five-scan history limit; SurfaceIQ Pro adds unlimited history, baseline compare, cloud sync, and export, enforced both client-side and server-side so entitlement checks can't be bypassed by a patched client.",tags:["Swift/ARKit","Kotlin/ARCore","Firebase","RevenueCat","Google AdMob"],href:"/surfaceiq",linkLabel:"View the SurfaceIQ product page â"},{kicker:"Sports · iOS, iPad, Mac, Apple TV & Android",name:"The Pulse sports franchise",tagline:"Three live-sports apps â Football, Cricket, multi-sport â one shared engine.",problem:"Live-sports apps live and die on data freshness and reliability during exactly the moments traffic spikes hardest â a goal, a wicket, a medal round. Building each competition as a fully separate app meant re-solving the same live-data, notification, and subscription problems over and over â and early on, we did exactly that, with seven apps for what is really three products. A single-event app also goes dormant for years between tournaments, which is a bad deal for everyone: the user deletes it, and the listing rots.",approach:"We consolidated to three evergreen apps on one shared engine for live scores, tables, fixtures, and results â Football Insight Hub for international tournaments, continental championships and domestic league seasons; Cricket Pulse for ODI and T20 internationals plus the major domestic T20 league seasons; and Sport Orbit for the summer and winter multi-sport games alongside year-round athletics, aquatics, and winter sports. Each tournament became a competition behind an in-app switcher rather than its own download, so coverage grew while the number of things to maintain shrank.",shipped:"The franchise runs across iPhone, iPad, Mac, and Apple TV, with live scores, live league tables, fixtures and results, a competition switcher, and archived past seasons. Ads are non-personalized via Google AdMob, and each of the three apps publishes one privacy policy and one set of terms covering every competition it carries.",tags:["Swift/SwiftUI","tvOS","Kotlin","Firebase","Google AdMob"],href:"/football-pulse",linkLabel:"View the Football Insight Hub product page â"},{kicker:"Publishing · Paperback, hardcover & Kindle",name:"Architecting Agentic AI",tagline:"A seventeen-volume technical reference, published in all three editions.",problem:"A reference work of this size is where self-publishing normally falls apart. Print stores enforce hard page ceilings per trim size, hardcover and paperback want different geometry, ebook readers want reflowable text rather than page images, and every one of those constraints has to be settled before the writing is laid out â not discovered at upload time when the only remedy is splitting a finished volume in half.",approach:"We built the publishing side as a pipeline rather than a manual process: one authored source per volume, rendered into paperback, hardcover, and ebook through format-aware typesetting, with covers composited to the exact spine width the page count implies. ISBNs are assigned from our own block â three per volume, one per format â so every edition is properly distinct and none of them are tied to a single retailer. Then we read every page of every rendered volume before it ships, because the defects that matter at this scale are semantic, not structural, and n
1o automated check catches them.",shipped:"All seventeen volumes are published in paperback, hardcover, and Kindle. The same pipeline now carries our other titles, and it is the pipeline we run a client's manuscript through.",tags:["Typesetting","Cover design","ISBN","Paperback","Hardcover","Ebook"],href:"/contact",linkLabel:"Talk to us about publishing your book â"}];function v(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("p",{className:"sf-section-eyebrow",children:"Portfolio"}),(0,a.jsx)("h1",{className:"sf-section-h2 sf-display",children:"Work"}),(0,a.jsx)("p",{className:"sf-section-lead",style:{marginBottom:8},children:"Below are Soft Flourish's own shipped, published products â real apps you can install today and real books you can buy, not illustrations. They're the clearest evidence of how we scope, build, and ship, because you can go verify every claim on the App Store, Play Store, Microsoft Store, or a bookshelf yourself."}),(0,a.jsx)("div",{style:{marginTop:40},children:y.map(e=>(0,a.jsxs)("article",{className:"sf-case-study sf-glass",children:[(0,a.jsx)("p",{className:"sf-case-kicker",children:e.kicker}),(0,a.jsx)("h3",{children:e.name}),(0,a.jsx)("p",{className:"sf-case-tagline",children:e.tagline}),(0,a.jsxs)("div",{className:"sf-case-grid",children:[(0,a.jsxs)("div",{className:"sf-case-col",children:[(0,a.jsx)("h4",{children:"The problem"}),(0,a.jsx)("p",{children:e.problem})]}),(0,a.jsxs)("div",{className:"sf-case-col",children:[(0,a.jsx)("h4",{children:"Our approach"}),(0,a.jsx)("p",{children:e.approach})]}),(0,a.jsxs)("div",{className:"sf-case-col",children:[(0,a.jsx)("h4",{children:"What shipped"}),(0,a.jsx)("p",{children:e.shipped})]})]}),(0,a.jsx)("div",{className:"sf-case-tags",children:e.tags.map(e=>(0,a.jsx)("span",{className:"sf-case-tag",children:e},e))}),e.href?(0,a.jsx)("a",{href:e.href,className:"sf-article-back",style:{marginTop:20,display:"inline-block"},children:e.linkLabel}):null]},e.name))}),(0,a.jsxs)("div",{style:{marginTop:40},children:[(0,a.jsx)("h3",{style:{marginBottom:4},children:"More from Soft Flourish"}),(0,a.jsx)("p",{style:{fontSize:".92rem",color:"var(--sf-muted)",marginBottom:16},children:"Smaller, shipped apps that didn't get the full case-study treatment above â still real, still live."}),(0,a.jsx)("ul",{style:{listStyle:"none",padding:0,margin:0,display:"grid",gap:10},children:w.map(e=>(0,a.jsxs)("li",{children:[(0,a.jsx)("a",{href:e.href,className:"sf-article-back",style:{display:"inline-block"},children:e.name}),(0,a.jsxs)("span",{style:{fontSize:".88rem",color:"var(--sf-muted)"},children:[" â ",e.tagline]})]},e.href))})]}),(0,a.jsx)("div",{style:{marginTop:8,padding:"28px 0 0"},children:(0,a.jsxs)("p",{style:{fontSize:".92rem",color:"var(--sf-muted)",lineHeight:1.75,maxWidth:"70ch"},children:["Beyond our own products, most of our engineering work is client-confidential â field operations tools, internal dashboards, and store-ready launches built under NDA for businesses that don't publish their vendor relationships. If you'd like references in your industry, ",(0,a.jsx)("a",{href:"/contact",children:"get in touch"}),"and we'll share what we can. If you have an app idea or a manuscript of your own, that's the same form â we take on both."]})})]})}let w=[{name:"Magic Rename",tagline:"AI file renamer for Mac.",href:"/magicrename"},{name:"Fitness 20",tagline:"20-minute daily workouts with full weekly muscle coverage.",href:"/fitness20"},{name:"Nature Wonders",tagline:"Plant identifier and family field guide.",href:"/naturewonders"},{name:"Parent Sphere",tagline:"Shared planning and calmer handoffs for both parents.",href:"/parentsphere"},{name:"DJFlow",tagline:"Your personal DJ for every playlist.",href:"/djflow"},{name:"Audio Master Studio",tagline:"AI-mastered audio in four studio-quality versions.",href:"/audiomasterstudio"}],j=[{n:1,title:"Architecting Agentic AI: Fundamentals & Core Patterns",premise:"Foundation of agentic design: the anatomy of an agent and the full sense, plan, act, reflect lifecycle, end to end.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v01/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H8DD5243",amazonHardcover:"https://www.amazon.com/dp/B0H8HZCLRK",amazonKindle:"https://www.amazon.com/dp/B0GX2VZGGD",apple:"https://books.apple.com/us/book/architecting-agentic-ai-fundamentals-core-patterns/id6799294820",google:"https://play.google.com/store/books/details?id=dEMBEgAAQBAJ",bn:"https://www.barnesandnoble.com/w/architecting-agentic-ai-tayeb-ayat/1150750283?ean=9798906040008"},{n:2,title:"Architecting Agentic AI: Architectures, Governance & Frameworks",premise:"Blueprints for designing, governing, and scaling autonomous systems responsibly, for leaders who need to know what to ask their teams.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v02/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H8HV1KMG",amazonHardcover:"https://www.amazon.com/dp/B0H8LH86SV",amazonKindle:"https://www.amazon.com/dp/B0H8FK6QQF",apple:"https://books.apple.com/us/book/architecting-agentic-ai-architectures-governance-frameworks/id6799497277",google:"https://play.google.com/store/books/details?id=JIYBEgAAQBAJ",bn:"https://www.barnesandnoble.com/w/architecting-agentic-ai-tayeb-ayat/1150763317?ean=9798906040497"},{n:3,title:"Agentic AI on Google Gemini Enterprise: Platform, Models & Data",premise:"How Google's Gemini Enterprise platform puts agentic AI to work, and what that actually means for a business.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v03/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H8J27941",amazonHardcover:"https://www.amazon.com/dp/B0H8JHXCMZ",amazonKindle:"https://www.amazon.com/dp/B0H8GF9WXM",apple:"https://books.apple.com/us/book/agentic-ai-on-google-gemini-enterprise-platform-models/id6799795425",google:"https://play.google.com/store/books/details?id=KoYBEgAAQBAJ"},{n:4,title:"Agentic AI on Google Gemini Enterprise: Agent Platform & Enterprise Rollout",premise:"A practical rollout guide: getting agentic AI live on Gemini Enterprise across a real organization.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v04/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H8J7KCZ7",amazonHardcover:"https://www.amazon.com/dp/B0H8JDKXYL",amazonKindle:"https://www.amazon.com/dp/B0H8G4Q6SF",apple:"https://books.apple.com/us/book/agentic-ai-on-google-gemini-enterprise-agent-platform/id6799795709",google:"https://play.google.com/store/books/details?id=LoYBEgAAQBAJ",bn:"https://www.barnesandnoble.com/w/agentic-ai-on-google-gemini-enterprise-agent-platform-enterprise-rollout-tayeb-ayat/1151060219?ean=9798906040022"},{n:5,title:"Agentic AI in Healthcare & Life Sciences",premise:"Where autonomous agents are already changing healthcare delivery, pharma research, and everyday wellness work.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v05/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H8J8RY6X",amazonHardcover:"https://www.amazon.com/dp/B0H8J
16X93M",amazonKindle:"https://www.amazon.com/dp/B0H8GJM2QS",apple:"https://books.apple.com/us/book/agentic-ai-in-healthcare-life-sciences/id6800056654",google:"https://play.google.com/store/books/details?id=W4YBEgAAQBAJ"},{n:6,title:"Agentic AI in Financial Services",premise:"Autonomous agents reshaping retail banking, investing, and how financial decisions get made.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v06/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H8HTMQP5",amazonHardcover:"https://www.amazon.com/dp/B0H8JC1F1V",amazonKindle:"https://www.amazon.com/dp/B0H8FY7BM6",apple:"https://books.apple.com/us/book/agentic-ai-in-financial-services/id6800065123",google:"https://play.google.com/store/books/details?id=XYYBEgAAQBAJ"},{n:7,title:"Agentic AI in Retail, Commerce & Hospitality",premise:"How agentic AI is changing retail operations, e-commerce, and hospitality, from the shop floor to the front desk.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v07/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H8LCJRD3",amazonHardcover:"https://www.amazon.com/dp/B0H8L3RS7Q",amazonKindle:"https://www.amazon.com/dp/B0H8KF66KV",apple:"https://books.apple.com/us/book/agentic-ai-in-retail-commerce-hospitality/id6800076102",google:"https://play.google.com/store/books/details?id=X4YBEgAAQBAJ"},{n:8,title:"Agentic AI in Energy & Environment",premise:"Agentic AI across the energy sector, from extraction to the renewable grid.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v08/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H8Y1YNRK",amazonHardcover:"https://www.amazon.com/dp/B0H8W7JNBQ",amazonKindle:"https://www.amazon.com/dp/B0H8KH52F1",apple:"https://books.apple.com/us/book/agentic-ai-in-energy-environment/id6800076899",google:"https://play.google.com/store/books/details?id=aYYBEgAAQBAJ"},{n:9,title:"Agentic AI in Manufacturing & Industry 4.0",premise:"What agentic AI looks like on a real factory floor, and in the broader Industry 4.0 shift.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v09/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H8XSRMHD",amazonHardcover:"https://www.amazon.com/dp/B0H8XRSY9K",amazonKindle:"https://www.amazon.com/dp/B0H1RYBX76",apple:"https://books.apple.com/us/book/agentic-ai-in-manufacturing-industry-4-0/id6800079150",google:"https://play.google.com/store/books/details?id=b4YBEgAAQBAJ",bn:"https://www.barnesandnoble.com/w/agentic-ai-in-manufacturing-industry-40-tayeb-ayat/1151060232?ean=9798906040077"},{n:10,title:"Agentic AI in Construction & Real Estate",premise:"Autonomous AI reshaping how buildings get planned, built, and managed.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v10/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H8Y9L6TB",amazonHardcover:"https://www.amazon.com/dp/B0H8YH7JG4",amazonKindle:"https://www.amazon.com/dp/B0H8X6QJNP",apple:"https://books.apple.com/us/book/agentic-ai-in-construction-real-estate/id6800083018",google:"https://play.google.com/store/books/details?id=eYYBEgAAQBAJ"},{n:11,title:"Agentic AI in Government, Public Sector & Education",premise:"How agentic AI is showing up in government services and classrooms, K-12 through corporate training.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v11/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H8YRCSRF",amazonHardcover:"https://www.amazon.com/dp/B0H8YJ5GGJ",amazonKindle:"https://www.amazon.com/dp/B0H8W7HP9Z",apple:"https://books.apple.com/us/book/agentic-ai-in-government-public-sector-education/id6800085697",google:"https://play.google.com/store/books/details?id=fYYBEgAAQBAJ",bn:"https://www.barnesandnoble.com/w/agentic-ai-in-government-public-sector-education-tayeb-ayat/1151060234?ean=9798906040091"},{n:12,title:"Agentic AI in Mobility, Transportation, Aerospace & Defense",premise:"Agentic AI across mobility, transportation, aerospace, and defense, wherever things need to move safely.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v12/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H8YKC8R1",amazonHardcover:"https://www.amazon.com/dp/B0H8WZ458Q",amazonKindle:"https://www.amazon.com/dp/B0H8WFGWZK",apple:"https://books.apple.com/us/book/agentic-ai-in-mobility-transportation-aerospace-defense/id6800107330",google:"https://play.google.com/store/books/details?id=f4YBEgAAQBAJ"},{n:13,title:"Agentic AI in Technology, Telecom & Media",premise:"How agentic AI is remaking technology, telecom, and media businesses.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v13/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H8YCDG82",amazonHardcover:"https://www.amazon.com/dp/B0H8YNVHTP",amazonKindle:"https://www.amazon.com/dp/B0H3BYRKSB",apple:"https://books.apple.com/us/book/agentic-ai-in-technology-telecom-media/id6800116022",google:"https://play.google.com/store/books/details?id=hoYBEgAAQBAJ",bn:"https://www.barnesandnoble.com/w/agentic-ai-in-technology-telecom-media-tayeb-ayat/1151060248?ean=9798906040114"}
1,{n:14,title:"Agentic AI in Agriculture & Food",premise:"Agentic AI's growing role in modern agriculture and the food supply chain.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v14/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H8YJBFBT",amazonHardcover:"https://www.amazon.com/dp/B0H8YLQGLZ",amazonKindle:"https://www.amazon.com/dp/B0H8X44689",apple:"https://books.apple.com/us/book/agentic-ai-in-agriculture-food/id6800120347",google:"https://play.google.com/store/books/details?id=iIYBEgAAQBAJ"},{n:15,title:"Agentic AI in Professional & Workforce Services",premise:"Autonomous agents changing legal, HR, and other professional and workforce services.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v15/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H8YNBS71",amazonHardcover:"https://www.amazon.com/dp/B0H8YL31RW",amazonKindle:"https://www.amazon.com/dp/B0H8WNYXZS",apple:"https://books.apple.com/us/book/agentic-ai-in-professional-workforce-services/id6800123115",google:"https://play.google.com/store/books/details?id=joYBEgAAQBAJ",bn:"https://www.barnesandnoble.com/w/agentic-ai-in-professional-workforce-services-tayeb-ayat/1151060207?ean=9798906040138"},{n:16,title:"Agentic AI: Cross-Industry Architecture & Governance",premise:"A cross-industry playbook: architecture and governance patterns that hold up no matter which industry you're in.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v16/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H8YJGH4P",amazonHardcover:"https://www.amazon.com/dp/B0H8YKH54P",amazonKindle:"https://www.amazon.com/dp/B0H8WSXFW3",apple:"https://books.apple.com/us/book/agentic-ai-cross-industry-architecture-governance/id6800132474",google:"https://play.google.com/store/books/details?id=kIYBEgAAQBAJ",bn:"https://www.barnesandnoble.com/w/agentic-ai-tayeb-ayat/1151060209?ean=9798906040145"},{n:17,title:"The Future of Agentic AI & Reference",premise:"A forward look: where agentic AI is headed, and a reference you'll come back to as it gets there.",cover:"https://media.softflourish.com/softflourish/books/00001_architecting_agentic_ai/v17/cover.jpg",amazonPaperback:"https://www.amazon.com/dp/B0H996HW25",amazonHardcover:"https://www.amazon.com/dp/B0H98XY8ZV",amazonKindle:"https://www.amazon.com/dp/B0H8WHV6Y3",apple:"https://books.apple.com/us/book/the-future-of-agentic-ai-reference/id6800114238",google:"https://play.google.com/store/books/details?id=koYBEgAAQBAJ",bn:"https://www.barnesandnoble.com/w/the-future-of-agentic-ai-reference-tayeb-ayat/1151020128?ean=9798906040152"}],k=` 2.sfbk-hero{max-width:820px;margin:0 auto 56px;text-align:center} 3.sfbk-eyebrow{font-size:.72rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--sf-accent);margin-bottom:14px} 4.sfbk-title{font-family:var(--font-fraunces),Georgia,serif;font-size:clamp(1.7rem,3.4vw,2.5rem);font-weight:650;line-height:1.2;color:var(--sf-fg);margin-bottom:18px} 5.sfbk-lead{font-size:1.02rem;line-height:1.75;color:var(--sf-muted)} 6.sfbk-grid{display:grid;gap:24px;grid-template-columns:1fr} 7@media(min-width:760px){.sfbk-grid{grid-template-columns:repeat(2,1fr)}} 8.sfbk-card{display:flex;gap:18px;padding:22px;border-radius:var(--sf-radius);scroll-margin-top:100px} 9.sfbk-cover{flex:none;width:96px;border-radius:6px;overflow:hidden;box-shadow:0 4px 16px rgba(0,0,0,0.18)} 10.sfbk-cover img{display:block;width:100%;height:auto} 11.sfbk-num{font-size:.68rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--sf-muted);margin-bottom:4px} 12.sfbk-card h3{font-size:1rem;font-weight:650;line-height:1.35;color:var(--sf-fg);margin-bottom:8px} 13.sfbk-premise{font-size:.86rem;line-height:1.6;color:var(--sf-muted);margin-bottom:12px} 14.sfbk-label{font-size:.66rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--sf-muted);margin:8px 0 4px} 15.sfbk-pills{display:flex;flex-wrap:wrap;gap:6px} 16.sfbk-pill{font-size:.72rem;font-weight:600;color:var(--sf-accent);background:var(--sf-accent-soft);padding:5px 10px;border-radius:999px;text-decoration:none} 17.sfbk-pill:hover{background:var(--sf-accent);color:#fff} 18`;function N({v:e}){let t=e.amazonPaperback??e.amazonKindle??e.amazonHardcover,s=[e.amazonKindle&&{label:"Kindle",href:e.amazonKindle},e.apple&&{label:"Apple Books",href:e.apple},e.google&&{label:"Google Play",href:e.google}
18].filter(Boolean),r=[e.amazonPaperback&&{label:"Paperback",href:e.amazonPaperback},e.amazonHardcover&&{label:"Hardcover",href:e.amazonHardcover},e.bn&&{label:"Barnes & Noble",href:e.bn}].filter(Boolean);return(0,a.jsxs)("article",{id:`volume-${e.n}`,className:"sfbk-card sf-glass",children:[(0,a.jsx)("div",{className:"sfbk-cover",children:t?(0,a.jsx)("a",{href:t,target:"_blank",rel:"noopener noreferrer",children:(0,a.jsx)("img",{src:e.cover,alt:`${e.title} cover`,loading:"lazy"})}):(0,a.jsx)("img",{src:e.cover,alt:`${e.title} cover`,loading:"lazy"})}),(0,a.jsxs)("div",{children:[(0,a.jsxs)("p",{className:"sfbk-num",children:["Volume ",e.n]}),(0,a.jsx)("h3",{children:t?(0,a.jsx)("a",{href:t,target:"_blank",rel:"noopener noreferrer",style:{color:"inherit",textDecoration:"none"},children:e.title}):e.title}),(0,a.jsx)("p",{className:"sfbk-premise",children:e.premise}),s.length>0&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("p",{className:"sfbk-label",children:"Ebook"}),(0,a.jsx)("div",{className:"sfbk-pills",children:s.map(e=>(0,a.jsx)("a",{href:e.href,target:"_blank",rel:"noopener noreferrer",className:"sfbk-pill",children:e.label},e.label))})]}),r.length>0&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("p",{className:"sfbk-label",children:"Print"}),(0,a.jsx)("div",{className:"sfbk-pills",children:r.map(e=>(0,a.jsx)("a",{href:e.href,target:"_blank",rel:"noopener noreferrer",className:"sfbk-pill",children:e.label},e.label))})]})]})]})}function S(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:k}}),(0,a.jsxs)("div",{className:"sfbk-hero",children:[(0,a.jsx)("p",{className:"sfbk-eyebrow",children:"The Agentic AI Handbook â 17 Volumes"}),(0,a.jsx)("h1",{className:"sfbk-title",children:"Software is starting to do the work. Find the volume written for your field."}),(0,a.jsx)("p",{className:"sfbk-lead",children:"Agentic AI is software that doesnât just answer a question â it plans and takes the next step for you. Itâs already changing ordinary jobs, not just tech companies. This 17-volume series answers one question for almost every field of work: what does this actually change for someone doing your job? Every volume stands on its own â find yours below."})]}),(0,a.jsx)("div",{className:"sfbk-grid",children:j.map(e=>(0,a.jsx)(N,{v:e},e.n))}),(0,a.jsxs)("a",{href:"/apps",className:"sf-glass",style:{display:"block",marginTop:48,padding:22,borderRadius:"var(--sf-radius)",textAlign:"center",textDecoration:"none"},children:[(0,a.jsx)("strong",{style:{color:"var(--sf-fg)"},children:"We also build apps."}),(0,a.jsx)("span",{style:{display:"block",marginTop:4,fontSize:".86rem",color:"var(--sf-muted)"},children:"Everything weâve shipped, in one place. See every app â"})]})]})}let A=[{name:"MeetWing",appStoreUrl:"https://apps.apple.com/app/id6779822069",tagline:"AI co-pilot for your meetings.",description:"Real-time AI assistant for Mac and Windows that listens to live meetings and gives instant, context-aware suggestions. Runs privately â audio is never stored. Supports transcription, AI answers, playbooks, screen sharing, and one-click export.",category:"Productivity",platforms:["macOS","Windows"],bundleId:"com.fenul.meetpilot",pageUrl:"/meetwing",iconSrc:"/meetwing/icon.png",accent:"#d9820a"},{name:"Flourish PDF",appStoreUrl:"https://apps.apple.com/app/id6794034781",playStoreUrl:"https://play.google.com/store/apps/details?id=com.softflourish.flourishpdf",microsoftStoreUrl:"https://apps.microsoft.com/detail/9nk1x0qr2vz6",tagline:"A full PDF editor that never uploads your document.",description:"Reading, editing, organizing, scanning, signing, OCR, forms, protection, and AI summarization â all processed on-device. Ships on Mac, Windows, iPhone, iPad, and Android.",category:"Productivity",platforms:["macOS","Windows","iOS","iPadOS","Android"],bundleId:"com.softflourish.flourishpdf",pageUrl:"/flourishpdf",iconSrc:"/flourishpdf/icon.png",accent:"#a23ce0"},{name:"Message Exporter",appStoreUrl:"https://apps.apple.com/app/id6776814909",tagline:"Save Mac Messages conversations you can actually keep.",description:"Exports iMessage and SMS history from a Mac to PDF, Excel, CSV, HTML, or plain text, with attachments, date filtering, and per-conversation selection.",category:"Productivity",platforms:["macOS"],pageUrl:"/messageexporter",iconSrc:"/messageexporter/icon.png",accent:"#d94436"},{name:"Magic Rename",appStoreUrl:"https://apps.apple.com/app/id6798810152",tagline:"Files named for what is actually in them.",description:"Reads every file in a folder â photos, screenshots, PDFs, Word and Excel documents, video â and proposes a name that describes it, in the case, separator, and tone you choose. Runs entirely on the Mac or PC it is installed on, with a full preview before anything changes and one-click undo after.",category:"Utilities",platforms:["macOS","Windows"],bundleId:"com.fenul.MagicRename",pageUrl:"/magicrename",iconSrc:"/magicrename/icon.png",accent:"#6247e0"},{name:"SystemZen",appStoreUrl:"https://apps.apple.com/app/id6794717513",playStoreUrl:"https://play.google.com/store/apps/details?id=com.softflourish.systemzen",microsoftStoreUrl:"https://apps.microsoft.com/detail/9p4c18nft1cn",tagline:"Reclaim storage and keep the device quick.",description:"Device cleanup and storage insight across Mac, Windows, iPhone, iPad, and Android â finds what is actually taking up space and clears it safely.",category:"Utilities",platforms:["macOS","Windows","iOS","iPadOS","Android"],bundleId:"com.softflourish.systemzen",pageUrl:"/systemzen",iconSrc:"/systemzen/icon.png",accent:"#7c5cff"},{name:"SurfaceIQ",appStoreUrl:"https://apps.apple.com/app/id6787247553",playStoreUrl:"https://play.google.com/store/apps/details?id=com.softflourish.surfaceiq",tagline:"Point the camera at a surface and see what is wrong with it.",description:"Find studs and wiring behind a wall, check a floor is level, and ph
18otograph cracks, damp, stains, dents and wear with each problem marked on the picture. Compare against an earlier photo to show what changed, and export the whole inspection as a PDF.",category:"Utilities",platforms:["iOS","Android"],bundleId:"com.fenul.surfaceiq",pageUrl:"/surfaceiq",iconSrc:"/surfaceiq/icon.png",accent:"#1a6b5c"},{name:"Football Insight Hub",appStoreUrl:"https://apps.apple.com/app/id6777162828",playStoreUrl:"https://play.google.com/store/apps/details?id=com.fenul.worldcup",tagline:"Two fair sides in seconds â then the whole match.",description:"Turn up with fourteen people and no teams: the Smart Team Balancer deals two sides level on strength and sensible in shape, suggests a formation for both, and tells you the gap it could not close. Matchday keeps the clock, the goals and the cards; the formation sheet, tactics board and offside visualiser cover the rest of a coach's Sunday. The professional game sits alongside it â live scores, tables, fixtures and results across international tournaments, continental championships, domestic league seasons and continental club competitions, with finished seasons archived and one tap away.",category:"Sports",platforms:["iOS","iPadOS","Android","macOS","tvOS"],bundleId:"com.fenul.worldcup",pageUrl:"/football-pulse",iconSrc:"/football-pulse/icon.png",accent:"#da1e28"},{name:"Cricket Pulse",tagline:"ODI and T20 cricket â scores, series standings, and tournament tables.",description:"A multi-format cricket app: live scores, points tables, series standings, and knockout paths across One Day International and Twenty20 internationals, the Cricket World Cup and T20 World Cup, the Indian Premier League, and the Big Bash League.",category:"Sports",platforms:["iOS","iPadOS","Android","macOS","tvOS"],bundleId:"com.fenul.cricket.t20",pageUrl:"/cricket-pulse",iconSrc:"/cricket-pulse/icon.png",accent:"#0f8a4d"},{name:"Sport Orbit",appStoreUrl:"https://apps.apple.com/app/id6777163988",tagline:"Every season, every sport â one app.",description:"A multi-sport app covering both the Summer and Winter Games (medal counts, daily schedules, sport-by-sport results, and country standings) plus year-round coverage of athletics, swimming, cycling, alpine skiing, figure skating, speed skating, biathlon, and more.",category:"Sports",platforms:["iOS","iPadOS","Android","macOS","tvOS"],bundleId:"com.fenul.olympics.summer",pageUrl:"/sport-orbit",iconSrc:"/sport-orbit/icon.png",accent:"#1a5fd0"}],P=[...A];A.map(e=>e.bundleId).filter(e=>!!e);let z="/contact",T="[email protected]",F={fitness20:{slug:"fitness20",name:"Fitness 20",storeTitle:"Fitness 20: Daily Workouts",platforms:["iOS","iPadOS","macOS","tvOS","Android","Windows"],category:"Health & Fitness",emoji:"ðª",iconSrc:"/app-icons/fitness20.png",palette:{accent:"#2ce5c3",accentDark:"#075345",soft:"#dff8f2",bg:"#f7f4ed",fg:"#071d1a",muted:"#50625d"},badge:"20 minutes today. Every major muscle this week.",headline:["Twenty minutes a day,","a whole body every week"],sub:"Fitness 20 makes consistency simple: one focused 20-minute session each day, and complete muscle coverage across the week. Six strength days and one restore day, with the session chosen around how recovered you actually are.",availability:"Coming to iPhone, iPad, Mac, Apple TV, Android and Windows. No account, no ads, no analytics.",featuresH2:"Built for the person who keeps starting over",featuresLead:"The plan is ordered so the week covers everything, and the daily check-in decides which session you actually get â so a bad night's sleep changes the workout instead of ending the habit.",features:[{icon:"â±ï¸",title:"Exactly twenty minutes",body:"Seven sessions, each built to finish in twenty minutes. Short enough to do on the day you do not want to."},{icon:"ðï¸",title:"Every muscle, every week",body:"Six focused strength days and one restore day, ordered so nothing is quietly skipped week after week."},{icon:"ð",title:"Recovery-aware",body:"Check in with energy, soreness, sleep and recovery signals, and the app caps the work when you are already highly active."},{icon:"ð§",title:"Body Signal Decoder",body:"Separates early adaptation, scale-versus-shape mismatch, digestive patterns and 12-week calibration from things worth taking to a doctor."},{icon:"ð¯",title:"Build or maintain",body:"Your shape goal changes hard-set volume. It makes no spot-reduction promise, because that is not a thing."},{icon:"ð",title:"More than the scale",body:"Fortnightly measurements and clothes-fit context, so progress is not judged on one number that moves for a dozen reasons."}],limitsH2:"What it is not",limits:"Fitness 20 provides general fitness guidance. It is not medical advice, not physiotherapy, and not a diagnosis. If something hurts, if you are pregnant or postpartum, or if you have a condition that exercise could affect, talk to a clinician before starting and stop if a movement causes pain.",privacyUpdated:"August 21, 2026",privacyIntro:"Fitness 20 keeps everything on your device. There is no account to create, no server to sync to, and no analytics or advertising SDK in the app.",privacyCollects:[{what:"Your check-ins, completed sessions, goals and measurements",why:"Stored on your device so the app can plan the week and show your progress. They are never transmitted."}],privacyNever:["Health or fitness data from Apple Health, Google Fit or any other health service â the app does not request access","Location, camera, microphone or contacts â none of these capabilities are requested","An account, email address, or any identifier tied to you","Advert
18ising identifiers, and there are no ads","Analytics, crash telemetry or usage tracking of any kind"],privacyExtra:[{h:"Deleting your data",p:"Because nothing leaves the device, deleting the app deletes everything it stored. There is no server-side copy to request, and no account to close."},{h:"Children",p:"Fitness 20 is not directed to children under 13 and collects nothing from anyone, of any age."}],termsUpdated:"August 21, 2026",termsExtra:[{h:"Use it sensibly",p:"You are responsible for exercising within your own limits. Warm up, use a weight you can control, and stop if a movement causes pain. Soft Flourish is not liable for injury arising from use of the app."}],manualUpdated:"August 21, 2026",manualLead:"Everything Fitness 20 does, in the order you will meet it.",manualSections:[{h:"Your first session",items:["Open the app and pick your shape goal â build or maintain. This sets how many hard sets each session asks for, and you can change it later.","Do the daily check-in: energy, soreness, sleep, and how active you have already been.","The app picks today's session from that. Tap Start.","Each movement shows its target and a lighter option. Take the lighter option whenever the target is not available to you today."]},{h:"The weekly plan",p:"Six focused strength days cover the major muscle groups in an order that does not double up on a sore group, plus one restore day. If your recovery signals say you need it, the restore session can swap in on a strength day without breaking the streak â coverage catches up across the week rather than the day."},{h:"Reading the Body Signal Decoder",p:"When your measurements and the scale disagree, or progress feels stalled, the Decoder explains which of the ordinary causes fits â early adaptation, a scale-versus-shape mismatch, a digestive pattern, or simply that twelve weeks have not passed yet. It also names the small set of signals that are worth a doctor rather than an app. It never diagnoses."},{h:"Tracking progress",items:["Completed days and weekly muscle coverage appear on the progress view.","Add measurements and a clothes-fit note every two weeks â not daily. Daily scale weight moves for reasons that have nothing to do with training.","Nothing here is uploaded. It lives on the device."]},{h:"On Apple TV",p:"The TV surface is the workout player: start the session and follow along on the big screen. Detailed check-ins and editing stay on your phone, tablet or computer where typing is practical."},{h:"If something goes wrong",p:`Email ${T} or use the contact form. Include your device and OS version. Because there is no account, there is nothing we can look up on our side â tell us what you saw and what you expected.`}]},naturewonders:{slug:"naturewonders",name:"Nature Wonders",storeTitle:"Nature Wonders: Plant Scanner",platforms:["iOS","iPadOS","Android"],category:"Education",emoji:"ð¿",iconSrc:"/app-icons/naturewonders.png",palette:{accent:"#2ac486",accentDark:"#075345",soft:"#e2f5eb",bg:"#fbf7ed",fg:"#02231f",muted:"#4d6259"},badge:"Photograph a plant. Learn something real.",headline:["Turn an ordinary walk","into a closer look"],sub:"Photograph a leaf, a flower or the bark of a tree, compare the likely matches, and open a Wonder Card written three ways â for kids, for families, and for people who want the taxonomy.",availability:"Coming to iPhone, iPad and Android. No location requested, no ads, no child account.",featuresH2:"Identification that shows its working",featuresLead:"A photograph can be misread. Nature Wonders is built to show you the alternatives and the confidence behind a match rather than announce one answer and hope.",features:[{icon:"ð",title:"Name, family and alternatives",body:"Common name, scientific name, plant family, a confidence figure, and the other plants it could plausibly be."},{icon:"ð¸",title:"Photo coaching by part",body:"Choose the part that is most visible â leaf, flower, fruit, bark â and get practical tips for a shot that identifies well."},{icon:"ð",title:"The Wonder Card",body:"One discovery, three audiences: memorable observations for kids, activities for families, and habitat, ecology and identification clues for nature lovers."},{icon:"ð",title:"A private field journal",body:"Save a discovery with your own note. It stays on the device â no account, no cloud, no location attached."},{icon:"ð",title:"Works without signal",body:"An eight-species offline learning guide, so a walk out of coverage is still a walk worth taking."},{icon:"ð¥¾",title:"Leave-no-trace missions",body:"Leaf-shape safaris, pollinator pauses and tree-detective challenges that reward staying on the trail and leaving things where they are."}],limitsH2:"Read this before you touch anything",limits:"Plant recognition from a photograph can be wrong, and a confident-looking match is still a guess. Nature Wonders is never an edibility, touch-safety, medical, veterinary or foraging decision. Never touch, taste, burn, prepare, or give a plant to a person or a pet based on what this app says. If you suspect poisoning or a serious reaction, contact your local emergency services or poison control immediately.",privacyUpdated:"August 21, 2026",privacyIntro:"Nature Wonders keeps your journal on your device and asks for as little as an identification app can ask for. It does not request your location, does not create a child profile, and does not sell anything about you.",privacyCollects:[{what:"The photograph you choose to identify",why:"Sent to the plant-recognition provider to produce a match, and not retained by us afterwards."},{what:"Your saved discoveries and notes",why:"Stored on your device so your field journal persists. They are not uploaded."}],privacyNever:["Your location â the app does not request location permission at all, and no coordinates are attached to a discovery","A child account, profile, or anything identifying a child","Advert
18ising identifiers, and there are no ads","Contacts, microphone, or health data","Any sale or sharing of your data with data brokers"],privacyExtra:[{h:"The identification provider",p:"Live identification is performed by Pl@ntNet. The photograph you submit is sent to their service to produce a ranked list of matches, and is handled under their terms. We attach no account, no device identifier and no location to that request."},{h:"Rare plants",p:"The app does not request, store, or publish precise locations of rare plants. Publishing exact coordinates of a rare species is how populations get stripped, so the feature does not exist."},{h:"Deleting your data",p:"Your journal is on the device: deleting the app removes it. There is no account to close and no server-side copy to request."}],termsUpdated:"August 21, 2026",termsExtra:[{h:"Identification is a possibility, not a verdict",p:"You agree not to rely on Nature Wonders for any decision about eating, touching, burning, preparing, or administering a plant to a person or animal. Soft Flourish accepts no liability for harm arising from treating an identification as a certainty."},{h:"Respect the places you visit",p:"Use the app in line with the rules of the land you are on. Do not trespass, do not collect where collecting is prohibited, and leave plants where they are growing."}],manualUpdated:"August 21, 2026",manualLead:"How to get a good identification, and how to read the one you get.",manualSections:[{h:"Taking a photograph that identifies well",items:["Choose the plant part first â leaf, flower, fruit, or bark. The app coaches the shot differently for each.","Fill the frame with the single part. A whole bush at three metres identifies badly.","Get the background as plain as you can â a hand behind a leaf works.","Even, indirect light. Harsh sun blows out the vein pattern that does most of the work.","If the first result looks unlikely, photograph a different part of the same plant and compare."]},{h:"Reading the result",p:"You get a ranked list, not a single answer. The top match has a confidence figure and the alternatives are shown beneath it. A low confidence with two plausible alternatives means exactly that â it is not a shy way of saying the first one is right. Compare the visual clues listed against the plant in front of you before you accept a match."},{h:"The Wonder Card",p:"Open a match to get its Wonder Card. Switch between the kids, families and nature-lover views: the same plant, described for a six-year-old, for a family planning a walk, and for someone who wants habitat, ecology and taxonomy."},{h:"Your field journal",items:["Save a discovery to keep it, and add your own note â where you were, what the weather was, what you noticed.","The journal is on your device only. Back up your phone to keep it.","No location is stored with an entry."]},{h:"Offline",p:"The eight-species learning guide and everything already saved to your journal work with no connection. Live identification needs one, because the recognition runs on the provider's service."},{h:'"Live ID needs setup"',p:"That message means the build you are running has no identification credential configured. A store build will not show it; a locally built development copy will until a key is supplied."},{h:"If something goes wrong",p:`Email ${T} or use the contact form, with your device, OS version, and â if it is about a bad match â the plant part you photographed.`}]},parentsphere:{slug:"parentsphere",name:"Parent Sphere",storeTitle:"Parent Sphere: Family Planner",platforms:["iOS","iPadOS","macOS","tvOS","Android","Windows"],category:"Family",emoji:"ð¡",iconSrc:"/app-icons/parentsphere.png",palette:{accent:"#34d4e8",accentDark:"#25165c",soft:"#ece9fb",bg:"#f7f5fa",fg:"#160f35",muted:"#5b5570"},badge:"One calm place to coordinate the care.",headline:["Both parents,","one shared week"],sub:"Parent Sphere gives both parents one place to plan the week, hand over calmly, agree decisions and follow through â without turning the children into the messaging service.",availability:"Coming to iPhone, iPad, Mac, Apple TV, Android and Windows. No ads, no location tracking, no sale of family data.",featuresH2:"Plan, hand off, observe, decide, follow through",featuresLead:"The whole app is that one loop. Both parents hold the same role â nothing here is gender-coded, and neither parent is the assistant.",features:[{icon:"ð
",title:"A shared family day",body:"Custody time, appointments, school, activities and the next handoff in one view â with proposed changes kept separate from the agreed calendar."}
18,{icon:"ð",title:"Handoff briefs",body:"Sleep, food, mood, medicine and what is in the bag, prepared before the handover instead of remembered halfway through it."},{icon:"ð¬",title:"One topic at a time",body:"Updates are written per topic, with a pause-and-reframe check that asks whether the child would be fine reading it."},{icon:"ð¤",title:"Decisions that stay found",body:"Shared decisions, each parent's response and the next action, recorded where both of you can find them later."},{icon:"ð§¾",title:"Expenses and splits",body:"Costs, how they split, receipts, and whether reimbursement actually happened."},{icon:"ð±",title:"Development, not scoring",body:"Age-relevant milestones as supportive observation, short play ideas using ordinary household things, and notes ready for the next well visit."}],limitsH2:"What it does not do",limits:"Parent Sphere is not legal advice and makes no promise that anything recorded in it is admissible in any court. Developmental milestones are observations, never a score, a deadline, or a diagnosis â take concerns to your child's clinician. The app contains no covert location tracking and no child-device surveillance, and it never will.",privacyUpdated:"August 25, 2026",privacyIntro:"Parent Sphere is local-first. Family sync is optional and is available only to active parents who joined the same private household with an expiring invite.",privacyCollects:[{what:"Account identity and invited-parent email",why:"To create the private household, verify membership, and make an invite usable only by the intended parent."},{what:"Your family's calendar, handoffs, messages, decisions, expenses and observations",why:"The content of the app. Stored so both parents can see the same week."},{what:"Sync revisions and last-change metadata",why:"To prevent one parent's offline edit from silently overwriting the other's change."}],privacyNever:["Any location of a parent or a child â there is no location tracking, covert or otherwise","Anything from a child's own device; the app does not monitor children's phones","Advertising identifiers, and there are no ads","Any sale or sharing of family data with advertisers, data brokers, or anyone else"],privacyExtra:[{h:"The family TV board",p:"The Apple TV surface is deliberately reduced: private messages and sensitive health details never appear on it. A shared living-room screen is not a private one, so the app treats it as public within the household."},{h:"Where your data lives",p:"The app works locally without an account. If you turn on family sync, records travel over TLS to Parent Sphere's dedicated Firebase tenant and are readable only by active members of that household. Provider credentials are never stored in family records."},{h:"Getting your data out, and deleting it",p:`Export is available inside Family circle. Delete account removes your identity, email, invitations and member metadata immediately. If another parent remains, shared family records remain for that parent without your identity; if you are the last active parent, the whole household is deleted. Local device data remains until you clear it or remove the app. If you cannot sign in, write to ${T}.`}],termsUpdated:"August 21, 2026",termsExtra:[{h:"Not legal advice, and not evidence",p:"Parent Sphere is an organisational tool. Nothing in it constitutes legal advice, and we make no representation that records kept in it will be accepted by any court, mediator or authority. Take legal questions to a lawyer in your jurisdiction."},{h:"Not a medical service",p:"Milestones and play ideas are general information for observation, not assessment or diagnosis. Concerns about a child's development or health go to that child's clinician."},{h:"Both parents, equal standing",p:"The app grants both parents the same role by design. You agree not to use it to monitor, coerce, or surveil the other parent or a child. Accounts used that way may be terminated."}],manualUpdated:"August 25, 2026",manualLead:"The care loop, and how each part of it works.",manualSections:[{h:"Setting up",items:["You can use Parent Sphere locally without an account.","For family sync, open Family cir
18cle and create an account with your email and a password of at least eight characters.","Create an invite for the other parent's exact email. They create their account with that email, enter the expiring invite code, and join the same household.","Only active household parents can read shared records. Keep the invite private and sign out on a shared device."]},{h:"The shared family day",p:"One view holds custody time, appointments, school, activities and the next handoff. A change either parent proposes stays in a separate proposed state until the other agrees â so the agreed calendar is always the one both of you actually signed off, and a disagreement never quietly overwrites the week."},{h:"Preparing a handoff",items:["Open the next handoff and fill the brief: sleep, food, mood, medicine, packed items.","Add anything the other parent will need in the first hour rather than everything you can think of.","The receiving parent sees it before pickup, not after."]},{h:"Writing an update",p:"Updates are one topic each, which keeps a disagreement about Tuesday from swallowing the note about the dentist. The pause-and-reframe check reads what you wrote and asks whether it stays child-focused. It never sends anything for you and never edits your words â you decide whether to change it."},{h:"Decisions and follow-through",p:"Record the decision, each parent's response and the next action with an owner. Ownership is explicit so 'I thought you were doing it' has somewhere to be resolved. Reminders chase the action, not the person."},{h:"Expenses",items:["Log the cost, attach the receipt, set how it splits.","Mark reimbursement when it actually arrives â the status is the point, not the ledger."]},{h:"Development notes",p:"Milestones are prompts to notice something, not a checklist to pass. Record what you observed in your own words; the app keeps it ready for the next well visit. If something concerns you, the app will say so plainly and tell you to contact your clinician â it will not offer a verdict."},{h:"The TV board",p:"Family board on Apple TV shows the shared day and routines for everyone in the room. Private messages and sensitive health details are excluded by design and cannot be turned on."},{h:"Sync, export and deletion",items:["Use Sync now after an offline change. Revision checks stop an older copy from silently replacing a newer one.","Export shows the household, active members and shared records available to your account.","Delete account removes your identity and membership. Shared records stay for the other parent; the final parent's deletion removes the household."]},{h:"If something goes wrong",p:`Email ${T} or use the contact form. Tell us the platform and what you expected to happen.`}],hasAccount:!0,deleteAccount:{warning:"Deleting your Parent Sphere account is permanent. Your identity, invited-parent email, invitations and membership metadata are removed immediately.",inAppSteps:["Open Parent Sphere and choose Family circle.","Choose Delete account.","Confirm. Parent Sphere removes cloud data first, then removes the sign-in account."],emailSubject:"Delete Parent Sphere account",extra:[{h:"Shared family records",p:"If another active parent remains, shared calendar, handoff, message, decision, expense and child records remain available to that parent, with your account identity removed. If you are the last active parent, the entire cloud household is deleted."},{h:"Local copies and retention",p:"Deleting the cloud account does not erase data already stored on a device. Clear local app data or remove the app on each device. Invitation codes expire after seven days and are removed by the service's expiration policy."}]}},audiomasterstudio:{slug:"audiomasterstudio",name:"Audio Master Studio",storeTitle:"Audio Master Studio: Mastering",platforms:["Web","iOS","iPadOS","macOS","Android","Windows"],category:"Music & Audio",earlyAccessUrl:"/audiomasterstudio",emoji:"ðï¸",iconSrc:"/app-icons/audiomasterstudio.png",palette:{accent:"#5B32F0",accentDark:"#3B22A8",soft:"#EAE3FF",bg:"#F7F5FC",fg:"#140F22",muted:"#57506E"},badge:"Make your song sound finished.",headline:["Upload a track,","keep the one you love"],sub:"Upload a song and get four studio-quality masters in about a minute â Streaming Ready, Club & Party, Warm & Natural, and Voice & Podcast. Listen to all four, adjust with two plain-word dials, and save the one that fits.",availability:"Free on the web now. iPhone, iPad, Mac, Android and Windows apps are in development.",featuresH2:"Four masters, not one guess",featuresLead:"Mastering is a judgment call, so Audio Master Studio gives you four different judgment calls to compare against your own ears, plus a fifth: its own opinion on which one fits.",features:[{icon:"ð§",title:"Four finished versions",body:"Streaming Ready, Club & Party, Warm & Natural, and Voice & Podcast â each tuned for what it's actually for, not four flavors of the same thing."},{icon:"ð¤",title:"Pick for me",body:"It compares loudness, punch, clarity and stereo width across all four and tells you which one suits your song. You still hear every version and can choose a different one."},{icon:"ðï¸",title:"Two dials, plain words",body:"Tone (warm to open) and a second adjustment sit on top of whichever master you picked â no EQ jargon, no hidden second engine."},{icon:"ð",title:"Mastered on your device",body:"Processing runs locally. Your music is never uploaded to a server to be mastered."},{icon:"ð¾",title:"Real files, every tier",body:"MP3 and WAV are free on every plan â quality is never paywalled, only how many masters you save each month."},{icon:"ð",title:"A/B against your original",body:"Compare any master to the file you uploaded, at any point, so you can hear exactly what changed."}
18],limitsH2:"What it can't do",limits:"Mastering cannot fix a recording problem â if something sounds off in your original, it will usually still be there after. This is a tool you drive, not a mastering studio with a person in it. Listening and adjusting is always free; credits are spent only when you save a file.",privacyUpdated:"August 21, 2026",privacyIntro:"Audio Master Studio processes your music on your own device. It is never uploaded to our servers to be mastered.",privacyCollects:[{what:"The audio file you choose to master",why:"Processed locally on your device to produce the four versions. It is not transmitted to us."},{what:"Your account and subscription status",why:"If you sign in or subscribe, used to track your monthly credits and entitlement across devices."}],privacyNever:["Your uploaded audio file â it is never sent to our servers","Advertising identifiers beyond what the ad network on your platform requires to show a free-tier ad","Sale of your data to advertisers or data brokers"],privacyExtra:[{h:"Ads on the free tier",p:"The free tier on web, iPhone, iPad and Android shows ads through AdSense or AdMob. Ads never appear while audio is playing. There is no ad SDK on Mac or Windows â the free tier there is limited by monthly credits only."},{h:"Deleting your data",p:"Your uploaded files are processed and discarded â there is nothing stored server-side to delete. Closing your account removes your subscription record and credit history."}],termsUpdated:"August 21, 2026",termsExtra:[{h:"Credits and downloads",p:"Free-tier credits reset monthly and are spent when you save a file, never when you master or preview one. Re-downloading the same master in the same format never costs a second credit. Unused credits do not roll over."},{h:"Your music, your rights",p:"You retain all rights to the audio you upload and to the masters produced from it. We claim no ownership or license over your music beyond what is needed to process it on your device."}],manualUpdated:"August 21, 2026",manualLead:"How to get from a rough mix to a master you'll keep.",manualSections:[{h:"Mastering your first track",items:["Choose a file from your device â songs up to 12 minutes on the free tier.","Audio Master Studio produces all four masters: Streaming Ready, Club & Party, Warm & Natural, Voice & Podcast.","Tap any version to hear it, and tap Original to compare. Listening is unlimited and free.","Save the one you want â that's when a credit is spent."]},{h:"Which master to pick",p:"Streaming Ready is the default and the right start if you're not sure â tuned for how Spotify, Apple Music and YouTube normalize loudness. Club & Party is loud and punchy for a big room. Warm & Natural keeps the dynamics gentle rather than squashed. Voice & Podcast keeps every word clear even through a phone speaker. Pick for me compares all four against your track and tells you which one it thinks fits, but you can always choose a different one â its opinion is a suggestion, not a lock."},{h:"The two dials",p:"Tone and a second adjustment sit on top of whichever master you chose, including a Pick for me result. They shift the same chain rather than run a second hidden engine, and Back to recommended returns you to the master's original settings exactly."},{h:"Credits and formats",items:["Free: 6 credits a month. MP3 costs 2, WAV costs 3, HD WAV or FLAC costs 6.","Listening, comparing and adjusting never cost a credit â only saving does.","Already saved a master as MP3 and want the WAV too? You pay only the difference, not the full price again.","Re-downloading the exact same master in the exact same format is always free.","Watch a rewarded video for one extra credit, up to 3 a day and 20 a month."]},{h:"Pro and Plus",p:"Pro ($4.99/month) removes the ads and gives unlimited MP3 and WAV masters plus 3 HD WAV masters a month, on songs up to an hour. Plus ($9.99/month) makes every format unlimited, including HD WAV and FLAC, on songs up to 3 hours, and adds folder mastering and priority access to new engines."},{h:"If something goes wrong",p:`Email ${T} or use the contact form. Tell us your platform and, if a master sounded wrong, which of the four versions and what you expected.`}],hasAccount:!0,deleteAccount:{warning:"Deleting your account is permanent. It removes your sign-in, your remaining monthly credits, your download history and your subscription record. It does not touch the audio files on your device â those never left it in the first place.",inAppSteps:["Open Audio Master Studio and sign in, if you are not already.","Go to Account.","Choose Delete account and data, then confirm."],emailSubject:"Delete Audio Master Studio account",extra:[{h:"Your subscription is separate",p:"Deleting the account does not cancel a Pro or Plus subscription â the store that billed you owns that. Cancel it in the App Store, Google Play or Microsoft Store account settings first, or you will keep being charged for an account that no longer exists."},{h:"What was never stored",p:"Your music is mastered on your device and is never uploaded, so there is no audio of yours on our servers to delete. What is deleted is the account record: identity, credit balance, download ledger and entitlement."}]},supportSections:[{h:"Before you write in",items:["A master sounded wrong: tell us which of the four versions, and what you expected instead.","A download did not arrive: tell us the format (MP3, WAV, HD WAV, FLAC) and whether a credit was spent.","Credits look wrong: they reset on the 1st of the month in your own timezone, and are spent on save, never on listening.","You subscribed and the app still shows Free: sign in with the same account you subscribed on, then reopen the app."]}]},djflow:{slug:"djflow",name:"DJFlow",storeTitle:"DJFlow: AI Music Mixer",platforms:["iOS","iPadOS","macOS","tvOS","Android","Windows"],category:"Music & Audio",emoji:"ã½ï¸",iconSrc:"/app-icons/djflow.png",palette:{accent:"#7a47ff",accentDark:"#5830cf",soft:"#eee9ff",bg:"#090812",fg:"#f7f4ff",muted:"#c4bdd2"},badge:"Your personal DJ for every playlist.",headline:["One playlist.","One continuous flow."],sub:"Connect your music, choose a vibe, and press play. DJFlow plans a deliberate listening session around tempo, key, energy and what your provider legally allows â without putting a professional DJ console in your way.",availability:"In development for iPhone, iPad, Mac, Apple TV, Android and Windows. Store links appear only after each listing is live.",featuresH2:"Better than pressing Shuffle",featuresLead:"The measure is simple: how often one button creates a noticeably better continuous session than a random queue.",features:[{icon:"ð",title:"Flow Mode",body:"Tasteful handoffs for driving, work, dinner, relaxing and long everyday playlists."},{icon:"â¨",title:"Party Mode",body:"Build and preserve energy with compatible tempo, key, genre and transition timing."},{icon:"ð",title:"Smart Shuffle",body:"A curated order shaped by music and preference signals â never random."},{icon:"ð",title:"Workout Mode",body:"Warm up, build, peak, sustain and cool down instead of holding one energy level."},{icon:"ð¬",title:"Describe the room",body:"Say âupbeat, but not clubbyâ and DJFlow converts the intent into clear mix parameters."},{icon:"ð¡ï¸",title:"Provider-honest",body:"Spotify stays in Spotify, YouTube stays visible, and full local mixing is reserved for audio you own or a provider explicitly licenses."}],limitsH2:"Connection is not permission to alter audio",limits:"DJFlow never downloads, decrypts, extracts, caches, analyzes or modifies protected streaming audio. Spotify's public connection leaves streaming and play accounting in Spotify; full catalog mixing needs Spotify's separate DJ-software partnership. YouTube plays only in a visible official player without background audio. Features vary by provider, subscription, device, region and account eligibility.",privacyUpdated:"August 24, 2026",privacyIntro:"DJFlow collects only the identity, provider authorization, mix state and product signals needed to run and improve a session. Provider credentials are encrypted server-side and protected audio never enters DJFlow storage.",privacyCollects:[{what:"DJFlow account identity",why:"To save preferences and resume eligible sessions across devices. Guest/local listening is available without an account."},{what:"Music-service authorization",why:"To read eligible playlists and ask that service to play the music you choose. Tokens are encrypted and never placed in client logs."},{what:"Playlist and track metadata",why:"To plan sequence and compatibility. DJFlow stores stable identifiers and derived plans, not protected audio."},{what:"Session and interaction events",why:"To recover playback and learn from skips, likes, ratings and completion. Fine history is minimized and expires unless you save it."},{what:"Purchase entitlement",why:"To unlock DJFlow Pro on devices signed into the same account. Pricing remains controlled by the store."}],privacyNever:["Protected music or video files from Spotify, Apple Music, YouTube, TIDAL or SoundCloud","OAuth tokens in analytics, crash reports or client-visible records","Location, contacts, photos, microphone or health data unless a future feature clearly asks first","Sale of listening history or personal data","Hidden YouTube background audio or separated YouTube audio"],privacyExtra:[{h:"Provider playback",p:"Apple Music uses MusicKit. Spotify performs and accounts for its own streams. YouTube remains inside its official visible player. Provider terms and privacy policies also apply."},{h:"Owned music",p:"Files you select remain platform file resources. DJFlow can process those files locally for transitions; it does not upload or share the audio."},{h:"Retention",p:"OAuth state expires after ten minutes. Detailed session history is aggregated or removed after 30 days unless you save the session. Disconnecting a service removes its stored token reference."},{h:"Deleting your account",p:"Use Account â Delete account and data in the app, or follow the public deletion steps. DJFlow removes provider connections, sessions, preferences, feedback and entitlement records before the authentication account is removed."}],termsUpdated:"August 24, 2026",termsExtra:[{h:"Music services",p:"You must use each provider under its own subscription, region, account and platform rules. DJFlow cannot promise that every track, control or transition is available on every provider."},{h:"Your music rights",p:"Only import local files you have the right to play. DJFlow is a personal listening product, not a tool for copying, redistributing, broadcasting or evading DRM."},{h:"DJFlow Pro",p:"Paid features may include additional local-file modes, advanced planning, vibe instructions, multiple provider connections and synced preferences. Apple Music access and playback are never sold as a DJFlow entitlement."}],manualUpdated:"August 24, 2026",manualLead:"From first connection to a provider-safe continuous session.",manualSections:[{h:"Start your first flow",items:["Open DJFlow and continue as a guest for local listening, or sign in to sync preferences.","Choose Apple Music, Spotify, YouTube, or Owned music. The capability label tells you what DJFlow can actually control.","Pick a playlist, choose Flow or Party, then press Start DJFlow.","The Now Playing view shows the current track, what comes next, and transition detail when DJFlow is allowed to execute it."]},{h:"Modes",items:["Flow keeps handoffs quiet and suitable for everyday listening.","Party builds energy and prefers longer compatible blends where the audio source permits them.","Smart Shuffle creates an intentional order from compatibility and preference signals.","Original Order keeps every playlist position and improves only permitted handoffs.","Workout follows a warm-up, build, peak, sustained-energy and cooldown curve."]},{h:"Spotify",p:"Sign into DJFlow first, connect Spotify, and choose an eligible playlist. DJFlow sends official playback and control requests; Spotify fetches, streams and accounts for the tracks. If Spotify says no active device, open Spotify, start any track once, return and retry. No Spotify audio is overlapped or altered until Spotify grants DJFlow direct DJ-partner approval."},{h:"YouTube",p:"Paste a public YouTube or YouTube Music playlist link. The video and official player remain visible, including standard controls and attribution. Closing the player ends the foreground session; DJFlow does not continue hidden audio."},{h:"Owned music",p:"Choose audio files you own. DJFlow can reorder, fade, normalize and use system background controls because the files are locally accessible. Protected or unavailable files are skipped without ending the rest of the session."},{h:"Disconnect or delete",p:"Open Account or Settings. Disconnecting a provider removes its authorization from DJFlow. Delete account and data removes saved DJFlow records and then the login identity. You may also revoke DJFlow from the provider's own account settings."},{h:"Support",p:`Email ${T} or use the contact form. Include platform, OS version, provider, mode, and the exact message you saw. Never send an OAuth token or password.`}],hasAccount:!0,deleteAccount:{warning:"Deleting your account is permanent. It removes saved mixes, preferences, feedback, entitlements, and every connected music-service token held by DJFlow.",inAppSteps:["Open DJFlow and choose Account.","Choose Delete account and data.","Confirm. DJFlow removes server data first, then removes the authentication account."],emailSubject:"Delete DJFlow account",extra:[{h:"Music services",p:"Deletion removes DJFlow's stored authorization. You can also revoke DJFlow immediately in each provider's account settings. Your provider account and playlists are not deleted."}]},supportSections:[{h:"Before you write in",items:["Include the platform, OS version, provider and mode, and the exact message you saw.","Never send an OAuth token or a password â we will never ask for either.","âNo active deviceâ on Spotify: open Spotify, play any track once, then return to DJFlow and retry."]}]}},C=["privacy","privacypolicy"];function M(e){let a=/^\/([a-z0-9]+)(?:\/(privacy|privacypolicy|terms|manual|support|deleteaccount))?$/.exec(e);if(!a)return null;let t=F[a[1]];return t&&("deleteaccount"!==a[2]||t.hasAccount)?{cfg:t,page:"privacypolicy"===a[2]?"privacy":a[2]||"landing"}:null}function I({cfg:e,children:t}){let s,r=e.slug;return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:(s=e.palette,` 19.ap-wrap{ 20 --ap-accent:${s.accent};--ap-accent-dark:${s.accentDark};--ap-soft:${s.soft}; 21 --ap-bg:${s.bg};--ap-fg:${s.fg};--ap-muted:${s.muted}; 22 --ap-border:rgba(0,0,0,0.10);--ap-surface:rgba(255,255,255,0.82);--ap-radius:16px; 23 font-family:-apple-system,BlinkMacSystemFont,"Inter",system-ui,sans-serif; 24 background:var(--ap-bg);color:var(--ap-fg);-webkit-font-smoothing:antialiased; 25} 26.ap-wrap *,.ap-wrap *::before,.ap-wrap *::after{box-sizing:border-box} 27.ap-nav{ 28 position:sticky;top:0;z-index:100;height:64px;padding:0 clamp(20px,4vw,48px); 29 display:flex;align-items:center;justify-content:space-between; 30 background:color-mix(in srgb,var(--ap-bg) 84%,transparent);backdrop-filter:blur(18px); 31 border-bottom:1px solid var(--ap-border); 32} 33.ap-nav-logo{display:flex;align-items:center;gap:10px;text-decoration:none;color:var(--ap-fg);font-weight:650} 34.ap-nav-logo-icon{ 35 width:30px;height:30px;border-radius:8px;flex:none;font-size:16px; 36 background:var(--ap-soft);display:flex;align-items:center;justify-content:center; 37 overflow:hidden; 38}
39.ap-nav-logo-icon img{width:100%;height:100%;object-fit:cover;display:block} 40.ap-nav-links{display:flex;align-items:center;gap:4px;flex-wrap:wrap} 41.ap-nav-links a{font-size:.85rem;color:var(--ap-muted);text-decoration:none;padding:8px 12px;border-radius:999px} 42.ap-nav-links a:hover{color:var(--ap-fg);background:var(--ap-soft)} 43 44.ap-hero{padding:clamp(56px,9vw,104px) clamp(20px,4vw,48px) clamp(40px,6vw,72px);text-align:center} 45.ap-hero-inner{max-width:880px;margin:0 auto} 46.ap-hero-icon{width:112px;height:112px;display:block;object-fit:cover;margin:0 auto 20px;border-radius:27px; 47 box-shadow:0 24px 60px color-mix(in srgb,var(--ap-accent) 34%,transparent),0 1px 0 rgba(255,255,255,.45) inset} 48.ap-badge{display:inline-block;padding:6px 14px;border-radius:999px;font-size:.78rem;font-weight:600; 49 background:var(--ap-soft);color:var(--ap-accent-dark);margin-bottom:20px} 50.ap-h1{font-size:clamp(2.2rem,5.5vw,3.6rem);font-weight:750;letter-spacing:-.03em;line-height:1.05;margin:0 0 18px} 51.ap-h1 em{font-style:normal;color:var(--ap-accent-dark)} 52.ap-sub{font-size:clamp(1rem,2vw,1.16rem);color:var(--ap-muted);line-height:1.7;max-width:60ch;margin:0 auto 28px} 53.ap-cta-row{display:flex;flex-wrap:wrap;gap:12px;justify-content:center} 54.ap-btn{display:inline-flex;align-items:center;gap:8px;padding:13px 26px;border-radius:999px; 55 font-size:.9rem;font-weight:620;text-decoration:none;transition:transform .18s} 56.ap-btn:hover{transform:translateY(-1px)} 57.ap-btn-primary{background:var(--ap-accent-dark);color:#fff} 58.ap-btn-outline{background:var(--ap-surface);color:var(--ap-fg);border:1.5px solid var(--ap-border)} 59.ap-availability{font-size:.85rem;color:var(--ap-muted);margin-top:22px} 60 61.ap-section{padding:clamp(48px,7vw,84px) clamp(20px,4vw,48px)} 62.ap-section-inner{max-width:1080px;margin:0 auto} 63.ap-label{font-size:.72rem;font-weight:680;letter-spacing:.18em;text-transform:uppercase;color:var(--ap-accent-dark);margin:0 0 12px} 64.ap-h2{font-size:clamp(1.7rem,3.4vw,2.4rem);font-weight:720;letter-spacing:-.02em;margin:0 0 14px} 65.ap-lead{font-size:1.02rem;color:var(--ap-muted);line-height:1.7;max-width:62ch;margin:0 0 36px} 66.ap-grid{display:grid;gap:18px} 67@media(min-width:640px){.ap-grid{grid-template-columns:repeat(2,1fr)}} 68@media(min-width:960px){.ap-grid{grid-template-columns:repeat(3,1fr)}} 69.ap-card{background:var(--ap-surface);border:1px solid var(--ap-border);border-radius:var(--ap-radius);padding:22px} 70.ap-card h3{font-size:1rem;font-weight:650;margin:0 0 8px} 71.ap-card p{font-size:.9rem;color:var(--ap-muted);line-height:1.6;margin:0} 72.ap-card-icon{font-size:1.35rem;margin-bottom:10px} 73.ap-limits-band{background:linear-gradient(180deg,var(--ap-soft) 0%,var(--ap-bg) 100%)} 74 75.ap-doc{padding:clamp(40px,6vw,72px) clamp(20px,4vw,48px) clamp(56px,8vw,96px)} 76.ap-doc-inner{max-width:760px;margin:0 auto} 77.ap-doc-inner h1{font-size:clamp(1.9rem,4vw,2.6rem);font-weight:740;letter-spacing:-.025em;line-height:1.12;margin:0 0 10px} 78.ap-doc-inner h2{font-size:1.14rem;font-weight:680;margin:36px 0 10px} 79.ap-doc-inner p,.ap-doc-inner li{font-size:.965rem;line-height:1.72;color:var(--ap-muted)} 80.ap-doc-inner p{margin:0 0 14px} 81.ap-doc-inner ul,.ap-doc-inner ol{margin:0 0 16px;padding-left:22px} 82.ap-doc-inner li{margin:0 0 7px} 83.ap-doc-inner strong{color:var(--ap-fg);font-weight:620} 84.ap-doc-inner a{color:var(--ap-accent-dark);text-decoration:underline;text-underline-offset:2px} 85.ap-updated{font-size:.83rem;color:var(--ap-muted);margin:0 0 28px !important} 86.ap-box{background:var(--ap-soft);border:1px solid var(--ap-border);border-radius:var(--ap-radius);padding:18px 22px;margin:0 0 26px} 87.ap-box p:last-child{margin-bottom:0} 88.ap-table{width:100%;border-collapse:collapse;margin:0 0 20px;font-size:.9rem} 89.ap-table th,.ap-table td{text-align:left;padding:10px 12px;border-bottom:1px solid var(--ap-border);color:var(--ap-muted);line-height:1.55;vertical-align:top} 90.ap-table th{color:var(--ap-fg);font-weight:640;font-size:.82rem} 91 92.ap-footer{padding:32px clamp(20px,4vw,48px);border-top:1px solid var(--ap-border);background:var(--ap-surface)} 93.ap-footer-inner{max-width:1080px;margin:0 auto;display:flex;flex-wrap:wrap;gap:14px; 94 align-items:center;justify-content:space-between;font-size:.85rem;color:var(--ap-muted)} 95.ap-footer-links{display:flex;flex-wrap:wrap;gap:16px} 96.ap-footer-links a{color:var(--ap-muted);text-decoration:none} 97.ap-footer-links a:hover{color:var(--ap-accent-dark)} 98`)}}),(0,a.jsxs)("div",{className:"ap-wrap",children:[(0,a.jsxs)("nav",{className:"ap-nav",children:[(0,a.jsxs)("a",{href:`/${r}`,className:"ap-nav-logo",children:[(0,a.jsx)("span",{className:"ap-nav-logo-icon","aria-hidden":!0,
98children:e.iconSrc?(0,a.jsx)("img",{src:e.iconSrc,alt:""}):e.emoji}),(0,a.jsx)("span",{children:e.name})]}),(0,a.jsxs)("div",{className:"ap-nav-links",children:[(0,a.jsx)("a",{href:`/${r}/manual`,children:"Manual"}),(0,a.jsx)("a",{href:`/${r}/privacy`,children:"Privacy"}),(0,a.jsx)("a",{href:`/${r}/terms`,children:"Terms"}),(0,a.jsx)("a",{href:`/${r}/support`,children:"Support"})]})]}),t,(0,a.jsx)("footer",{className:"ap-footer",children:(0,a.jsxs)("div",{className:"ap-footer-inner",children:[(0,a.jsxs)("span",{children:["© ",new Date().getFullYear()," Soft Flourish"]}),(0,a.jsxs)("div",{className:"ap-footer-links",children:[(0,a.jsx)("a",{href:`/${r}`,children:e.name}),(0,a.jsx)("a",{href:`/${r}/manual`,children:"Manual"}),(0,a.jsx)("a",{href:`/${r}/privacy`,children:"Privacy Policy"}),(0,a.jsx)("a",{href:`/${r}/terms`,children:"Terms of Use"}),(0,a.jsx)("a",{href:`/${r}/support`,children:"Support"}),(0,a.jsx)("a",{href:z,children:"Contact"}),(0,a.jsx)("a",{href:"/",children:"softflourish.com"})]})]})})]})]})}function D({cfg:e,title:t,updated:s,children:r}){return(0,a.jsx)(I,{cfg:e,children:(0,a.jsx)("div",{className:"ap-doc",children:(0,a.jsxs)("div",{className:"ap-doc-inner",children:[(0,a.jsx)("h1",{children:t}),(0,a.jsxs)("p",{className:"ap-updated",children:["Last updated: ",s]}),r]})})})}function W({items:e}){return(0,a.jsx)(a.Fragment,{children:e.map(e=>(0,a.jsxs)("section",{children:[(0,a.jsx)("h2",{children:e.h}),e.p?(0,a.jsx)("p",{children:e.p}):null,e.items?(0,a.jsx)("ul",{children:e.items.map(e=>(0,a.jsx)("li",{children:e},e))}):null]},e.h))})}function O({cfg:e}){return(0,a.jsxs)(I,{cfg:e,children:[(0,a.jsx)("section",{className:"ap-hero",children:(0,a.jsxs)("div",{className:"ap-hero-inner",children:[e.iconSrc?(0,a.jsx)("img",{className:"ap-hero-icon",src:e.iconSrc,alt:`${e.name} app icon`}):null,(0,a.jsxs)("div",{className:"ap-badge",children:[e.emoji," ",e.badge]}),(0,a.jsxs)("h1",{className:"ap-h1",children:[e.headline[0],(0,a.jsx)("br",{}),(0,a.jsx)("em",{children:e.headline[1]})]}),(0,a.jsx)("p",{className:"ap-sub",children:e.sub}),(0,a.jsxs)("div",{className:"ap-cta-row",children:[(0,a.jsx)("a",{href:`/${e.slug}/manual`,className:"ap-btn ap-btn-primary",children:"Read the manual"}),(0,a.jsx)("a",{href:z,className:"ap-btn ap-btn-outline",children:"Get in touch"})]}),(0,a.jsx)("p",{className:"ap-availability",children:e.availability})]})}),(0,a.jsx)("section",{className:"ap-section",children:(0,a.jsxs)("div",{className:"ap-section-inner",children:[(0,a.jsx)("p",{className:"ap-label",children:"What it does"}),(0,a.jsx)("h2",{className:"ap-h2",children:e.featuresH2}),(0,a.jsx)("p",{className:"ap-lead",children:e.featuresLead}),(0,a.jsx)("div",{className:"ap-grid",children:e.features.map(e=>(0,a.jsxs)("div",{className:"ap-card",children:[(0,a.jsx)("div",{className:"ap-card-icon","aria-hidden":!0,children:e.icon}),(0,a.jsx)("h3",{children:e.title}),(0,a.jsx)("p",{children:e.body})]},e.title))})]})}),(0,a.jsx)("section",{className:"ap-section ap-limits-band",children:(0,a.jsxs)("div",{className:"ap-section-inner",children:[(0,a.jsx)("p",{className:"ap-label",children:"Honest limits"}),(0,a.jsx)("h2",{className:"ap-h2",children:e.limitsH2}),(0,a.jsx)("p",{className:"ap-lead",children:e.limits}),(0,a.jsxs)("p",{className:"ap-lead",style:{marginBottom:0},children:["Full detail in the ",(0,a.jsx)("a",{href:`/${e.slug}/terms`,children:"terms of use"})," and the"," ",(0,a.jsx)("a",{href:`/${e.slug}/privacy`,children:"privacy policy"}),"."]})]})})]})}function E({cfg:e}){return(0,a.jsxs)(D,{cfg:e,title:`${e.name} â Privacy Policy`,updated:e.privacyUpdated,children:[(0,a.jsxs)("p",{children:["This policy explains how ",(0,a.jsx)("strong",{children:e.storeTitle})," handles information."," ",e.name," is published by Soft Flourish, an independent software studio and publishing house based in California, USA. It supplements the general"," ",(0,a.jsx)("a",{href:"/privacy",children:"Soft Flourish Privacy Policy"})," and controls where the two differ for this app."]}),(0,a.jsx)("div",{className:"ap-box",children:(0,a.jsx)("p",{children:e.privacyIntro})}),(0,a.jsx)("h2",{children:"What the app holds, and why"}),(0,a.jsxs)("table",{className:"ap-table",children:[(0,a.jsx)("thead",{children:(0,a.jsxs)("tr",{children:[(0,a.jsx)("th",{children:"What"}),(0,a.jsx)("th",{children:"Why"})]})}),(0,a.jsx)("tbody",{children:e.privacyCollects.map(e=>(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:e.what}),(0,a.jsx)("td",{children:e.why})]},e.what))})]}),(0,a.jsx)("h2",{children:"What it never collects"}),(0,a.jsx)("ul",{children:e.privacyNever.map(e=>(0,a.jsx)("li",{children:e},e))}),(0,a.jsx)(W,{items:e.privacyExtra}),(0,a.jsx)("h2",{children:"Changes to this policy"}),(0,a.jsxs)("p",{children:["If this policy changes materially we will update the date at the top and describe the change here. Continuing to use ",e.name," after a change means you accept it."]}),(0,a.jsx)("h2",{children:"Contact"}),(0,a.jsxs)("p",{children:["Questions about this policy, or a request about your data:"," ",(0,a.jsx)("a",{href:`mailto:${T}`,children:T}),", or the"," ",(0,a.jsx)("a",{href:z,children:"contact form"}),"."]})]})}function R({cfg:e}){return(0,a.jsxs)(D,{cfg:e,title:`${e.name} â Terms of Use`,updated:e.termsUpdated,children:[(0,a.jsxs)("p",{children:["These terms govern your use of ",(0,a.jsx)("strong",{children:e.storeTitle}),", published by Soft Flourish. By installing or using the app you accept them. They supplement the general"," ",(0,a.jsx)("a",{href:"/terms",children:"Soft Flourish Terms of Use"})," and control where the two differ."]}),(0,a.jsx)("h2",{children:"Your licence"}),(0,a.jsxs)("p",{children:["You get a personal, non-exclusive, non-transferable licence to use ",e.name," on devices you own or c
98ontrol. You may not resell it, rent it, reverse-engineer it, or strip its notices. The licence ends if you stop complying with these terms."]}),(0,a.jsx)("h2",{children:"Purchases"}),(0,a.jsxs)("p",{children:["Where ",e.name,"offers a paid tier, it is billed by whichever store you installed from, under that storeâs rules. Manage or cancel a subscription in that storeâs account settings â we cannot cancel it for you, and refunds are handled by the store, not by us."]}),(0,a.jsx)(W,{items:e.termsExtra}),(0,a.jsx)("h2",{children:"Availability"}),(0,a.jsxs)("p",{children:["We aim to keep ",e.name," working, but we do not guarantee it will be uninterrupted or error-free, and features may change between versions."]}),(0,a.jsx)("h2",{children:"Liability"}),(0,a.jsxs)("p",{children:["To the fullest extent permitted by law, Soft Flourish is not liable for indirect or consequential loss arising from use of ",e.name,". Nothing here limits liability that cannot be limited by law."]}),(0,a.jsx)("h2",{children:"Governing law"}),(0,a.jsx)("p",{children:"These terms are governed by the laws of the State of California, USA."}),(0,a.jsx)("h2",{children:"Contact"}),(0,a.jsxs)("p",{children:[(0,a.jsx)("a",{href:`mailto:${T}`,children:T})," or the"," ",(0,a.jsx)("a",{href:z,children:"contact form"}),"."]})]})}function $({cfg:e}){return(0,a.jsxs)(D,{cfg:e,title:`${e.name} â User Manual`,updated:e.manualUpdated,children:[(0,a.jsx)("p",{children:e.manualLead}),(0,a.jsx)("div",{className:"ap-box",children:(0,a.jsxs)("p",{children:[(0,a.jsxs)("strong",{children:[e.limitsH2,"."]})," ",e.limits]})}),(0,a.jsx)(W,{items:e.manualSections})]})}function _({cfg:e}){let t=e.deleteAccount;return t?(0,a.jsxs)(D,{cfg:e,title:`Delete your ${e.name} account`,updated:e.privacyUpdated,children:[(0,a.jsx)("div",{className:"ap-box",children:(0,a.jsx)("p",{children:t.warning})}),(0,a.jsx)("h2",{children:"Delete inside the app"}),(0,a.jsx)("ol",{children:t.inAppSteps.map(e=>(0,a.jsx)("li",{children:e},e))}),(0,a.jsx)("h2",{children:"If you cannot open the app"}),(0,a.jsxs)("p",{children:["Email ",(0,a.jsx)("a",{href:`mailto:${T}`,children:T})," from the address linked to the account, with subject ",(0,a.jsx)("strong",{children:t.emailSubject}),". We may ask you to verify control of that address; never send a password or an access token."]}),(0,a.jsx)(W,{items:t.extra??[]})]}):null}function U({cfg:e}){return(0,a.jsxs)(D,{cfg:e,title:`${e.name} â Support`,updated:e.manualUpdated,children:[(0,a.jsx)("div",{className:"ap-box",children:(0,a.jsxs)("p",{children:["Email ",(0,a.jsx)("a",{href:`mailto:${T}`,children:T})," or use the"," ",(0,a.jsx)("a",{href:z,children:"contact form"}),". A real person reads both. Include your platform and app version so the first reply can be an answer rather than a question."]})}),(0,a.jsx)("h2",{children:"Try the manual first"}),(0,a.jsxs)("p",{children:["The ",(0,a.jsxs)("a",{href:`/${e.slug}/manual`,children:[e.name," manual"]})," covers how the app is meant to be used, step by step, and answers most of what arrives in the inbox."]}),(0,a.jsx)(W,{items:e.supportSections??[]}),(0,a.jsx)("h2",{children:"Billing and subscriptions"}),(0,a.jsx)("p",{children:"Subscriptions are billed by the store you installed from, so cancellations and refunds are handled there â App Store, Google Play or Microsoft Store account settings. We cannot cancel or refund a store subscription on your behalf."}),e.hasAccount?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("h2",{children:"Deleting your account"}),(0,a.jsxs)("p",{children:["Steps are on the"," ",(0,a.jsx)("a",{href:`/${e.slug}/deleteaccount`,children:"delete your account"})," page."]})]}):null,(0,a.jsx)("h2",{children:"Legal"}),(0,a.jsxs)("p",{children:[(0,a.jsx)("a",{href:`/${e.slug}/privacy`,children:"Privacy policy"})," ·"," ",(0,a.jsx)("a",{href:`/${e.slug}/terms`,children:"Terms of use"})]})]})}function L({slug:e}){let t=M(e);if(!t)return null;let{cfg:s,page:r}=t;return"privacy"===r?(0,a.jsx)(E,{cfg:s}):"terms"===r?(0,a.jsx)(R,{cfg:s}):"manual"===r?(0,a.jsx)($,{cfg:s}):"support"===r?(0,a.jsx)(U,{cfg:s}):"deleteaccount"===r?(0,a.jsx)(_,{cfg:s}):(0,a.jsx)(O,{cfg:s})}Object.values(F).flatMap(e=>[`/${e.slug}`,...C.map(a=>`/${e.slug}/${a}`),`/${e.slug}/terms`,`/${e.slug}/manual`,`/${e.slug}/support`,...e.hasAccount?[`/${e.slug}/deleteaccount`]:[]]);let q={src:"/store-badges/app-store.svg",alt:"Download on the App Store",label:"App Store"},B={src:"/store-badges/mac-app-store.svg",alt:"Download on the Mac App Store",label:"Mac App Store"},G={src:"/store-badges/google-play.png",alt:"Get it on Google Play",label:"Google Play"},H={src:"/store-badges/microsoft-store.svg",alt:"Get it from Microsoft",label:"Microsoft Store"};function Y(e){let a=function(e){let a=[];if(e.appStoreUrl){let t=e.platforms.includes("iOS")||e.platforms.includes("iPadOS");a.push({key:"apple",href:e.appStoreUrl,...t?q:B})}return e.playStoreUrl&&a.push({key:"play",href:e.playStoreUrl,...G}),e.microsoftStoreUrl&&a.push({key:"microsoft",href:e.microsoftStoreUrl,...H}),a}(e);return{key:`p-${e.name}`,name:e.name,tagline:e.tagline,category:e.category,platforms:e.platforms,iconSrc:e.iconSrc,accent:e.accent??"#1a6b5c",pageUrl:e.pageUrl,kind:"released",isLive:a.length>0||!!e.webUrl,stores:a,webUrl:e.webUrl}}function V(e){return{key:`c-${e.slug}`,name:e.name,tagline:e.badge,category:e.category,platforms:e.platforms,iconSrc:e.iconSrc,emoji:e.emoji,accent:e.palette.accent,pageUrl:`/${e.slug}`,kind:"comingSoon",isLive:!!e.earlyAccessUrl,stores:[],earlyAccessUrl:e.earlyAccessUrl}}let K=[{key:"all",label:"All Apps",test:()=>!0},{key:"ios",label:"iPhone & iPad",test:e=>e.includes("iOS")||e.includes("iPadOS")},{key:"mac",label:"Mac",test:e=>e.includes("macOS")},{key:"android",label:"Android",test:e=>e.includes("Android")},{key:"windows",label:"Windows",test:e=>e.includes("Windows")},{key:"tv",label:"Apple TV",test:e=>e.includes("tvOS")},{key:"web",label:"Web",test:e=>e.includes("Web")}],Q=` 99.sfap-hero{max-width:860px;margin:0 auto 40px;text-align:center} 100.sfap-eyebrow{font-size:.72rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--sf-accent);margin-bottom:14px} 101.sfap-title{font-family:var(--font-fraunces),Georgia,serif;font-size:clamp(1.9rem,4vw,2.9rem);font-weight:660;line-height:1.14;color:var(--sf-fg);margin-bottom:18px;letter-spacing:-.02em} 102.sfap-lead{font-size:1.03rem;line-height:1.75;color:var(--sf-muted);max-width:62ch;margin:0 auto 32px} 103.sfap-stats{max-width:560px;margin:0 auto} 104 105.sfap-filters{display:flex;flex-wrap:wrap;gap:10px;justify-content:center;margin:0 0 48px} 106.sfap-filter-chip{ 107 display:inline-flex;align-items:center;gap:8px;padding:10px 18px;border-radius:999px; 108 font-size:.84rem;font-weight:600;color:var(--sf-muted);background:rgba(255,255,255,.5); 109 border:1.5px solid var(--sf-border);cursor:pointer;transition:background .2s var(--sf-ease),color .2s var(--sf-ease),border-color .2s var(--sf-ease),transform .2s var(--sf-ease); 110} 111.sfap-filter-chip:hover{color:var(--sf-fg);border-color:rgba(20,24,32,.22);background:#fff;transform:translateY(-1px)} 112.sfap-filter-chip.is-active{background:var(--sf-fg);color:#fff;border-color:var(--sf-fg);box-shadow:0 12px 28px -12px rgba(14,18,25,.4)} 113.sfap-filter-count{font-size:.7rem;font-weight:700;opacity:.55;background:rgba(20,24,32,.08);padding:1px 7px;border-radius:999px} 114.sfap-filter-chip.is-active .sfap-filter-count{background:rgba(255,255,255,.22);opacity:1} 115 116.sfap-section{margin-bottom:8px} 117.sfap-section-title{display:flex;align-items:center;gap:9px;font-size:.72rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--sf-fg);margin:0 0 22px} 118.sfap-section-title-muted{color:var(--sf-muted)}
119.sfap-live-dot{width:8px;height:8px;border-radius:50%;background:#1a6b5c;box-shadow:0 0 0 4px rgba(26,107,92,.18);flex:none} 120 121.sfap-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:22px;margin-bottom:60px} 122 123.sfap-card{ 124 position:relative;overflow:hidden;display:flex;flex-direction:column; 125 padding:26px;border-radius:calc(var(--sf-radius) + 2px); 126 opacity:0;animation:sfap-rise .6s var(--sf-ease) forwards; 127 transition:transform .3s var(--sf-ease),box-shadow .3s var(--sf-ease),border-color .3s var(--sf-ease); 128} 129.sfap-card::after{ 130 content:'';position:absolute;inset:0;pointer-events:none;border-radius:inherit; 131 background:linear-gradient(120deg,transparent 32%,rgba(255,255,255,.4) 48%,transparent 64%); 132 transform:translateX(-130%);transition:transform .8s var(--sf-ease); 133} 134.sfap-card:hover{ 135 transform:translateY(-6px); 136 box-shadow:0 30px 70px -32px color-mix(in srgb, var(--tile-accent) 45%, rgba(14,18,25,.35)); 137 border-color:color-mix(in srgb, var(--tile-accent) 45%, var(--sf-glass-border)); 138} 139.sfap-card:hover::after{transform:translateX(130%)} 140@keyframes sfap-rise{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:translateY(0)}} 141@media (prefers-reduced-motion: reduce){.sfap-card{animation:none;opacity:1}.sfap-card::after{display:none}} 142 143.sfap-ribbon-row{display:flex;justify-content:flex-end;margin:-4px 0 12px} 144.sfap-ribbon{ 145 font-size:.66rem;font-weight:700;letter-spacing:.02em; 146 padding:5px 11px;border-radius:999px;white-space:nowrap; 147 background:color-mix(in srgb, var(--tile-accent) 16%, #fff); 148 color:color-mix(in srgb, var(--tile-accent) 65%, #000); 149} 150 151.sfap-card-top{display:flex;align-items:flex-start;gap:14px;margin-bottom:14px} 152.sfap-card-top-text{flex:1;min-width:0} 153.sfap-icon{width:56px;height:56px;border-radius:16px;object-fit:cover;flex:none;display:block; 154 box-shadow:0 10px 24px -12px color-mix(in srgb, var(--tile-accent) 55%, transparent),0 0 0 1px rgba(255,255,255,.5) inset} 155.sfap-icon-mono{display:flex;align-items:center;justify-content:center;color:#fff;font-size:1.35rem;font-weight:700} 156.sfap-category{font-size:.66rem;font-weight:700;letter-spacing:.09em;text-transform:uppercase;color:var(--tile-accent);margin:2px 0 4px} 157.sfap-card h3{font-size:1.08rem;font-weight:650;line-height:1.3;color:var(--sf-fg);margin:0} 158.sfap-name-link{color:inherit;text-decoration:none} 159.sfap-name-link:hover{color:var(--tile-accent)} 160 161.sfap-tagline{font-size:.9rem;line-height:1.62;color:var(--sf-muted);margin:0 0 16px} 162.sfap-platform-row{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:18px} 163.sfap-platform-pill{font-size:.7rem;font-weight:600;color:var(--sf-muted);background:rgba(20,24,32,0.05);padding:4px 10px;border-radius:999px} 164 165.sfap-card-foot{margin-top:auto;display:flex;flex-direction:column;gap:12px} 166.sfap-stores{display:flex;flex-wrap:wrap;align-items:center;gap:12px} 167.sfap-store-link{display:inline-flex;transition:transform .18s var(--sf-ease)} 168.sfap-store-link:hover{transform:translateY(-2px)} 169.sfap-store-link img{height:36px;width:auto;display:block} 170.sfap-try-btn{padding:10px 20px;font-size:.82rem} 171.sfap-partial-note{font-size:.78rem;color:var(--sf-muted);margin:0} 172.sfap-learn-more{font-size:.84rem;font-weight:600;color:var(--tile-accent);text-decoration:none;align-self:flex-start} 173.sfap-learn-more:hover{text-decoration:underline} 174 175.sfap-books-link{display:block;margin-top:16px;padding:22px;border-radius:var(--sf-radius);text-align:center;text-decoration:none} 176.sfap-books-link strong{color:var(--sf-fg)} 177.sfap-books-link span{display:block;margin-top:4px;font-size:.86rem;color:var(--sf-muted)} 178`;function J({tile:e}){return e.iconSrc?(0,a.jsx)("img",{src:e.iconSrc,alt:"",className:"sfap-icon",loading:"lazy",decoding:"async"}):(0,a.jsx)("div",{className:"sfap-icon sfap-icon-mono",style:{background:`linear-gradient(155deg, ${e.accent}, color-mix(in srgb, ${e.accent} 55%, #000))`},"aria-hidden":!0,children:e.emoji??e.name.charAt(0)})}function Z({tile:e,index:t}){let s,r,n=(s=new Set(e.platforms),r=[],s.has("macOS")&&r.push("Mac"),s.has("iOS")&&s.has("iPadOS")?r.push("iPhone & iPad"):s.has("iOS")?r.push("iPhone"):s.has("iPadOS")&&r.push("iPad"),s.has("Windows")&&r.push("Windows"),s.has("Android")&&r.push("Android"),s.has("tvOS")&&r.push("Apple TV"),s.has("Web")&&r.push("Web"),r),i={"--tile-accent":e.accent,animationDelay:`${45*Math.min(t,9)}ms`};return(0,a.jsxs)("article",{className:"sfap-card sf-glass",style:i,children:[!e.isLive&&(0,a.jsx)("div",{className:"sfap-ribbon-row",children:(0,a.jsx)("span",{className:"sfap-ribbon",children:"comingSoon"===e.kind?"Coming Soon":"Store link coming soon"})}),(0,a.jsxs)("div",{className:"sfap-card-top",children:[(0,a.jsx)(J,{tile:e}),(0,a.jsxs)("div",{className:"sfap-card-top-text",children:[(0,a.jsx)("p",{className:"sfap-category",children:e.category}),(0,a.jsx)("h3",{children:e.pageUrl?(0,a.jsx)("a",{href:e.pageUrl,className:"sfap-name-link",children:e.name}):e.name})]})]}),(0,a.jsx)("p",{className:"sfap-tagline",children:e.tagline}),(0,a.jsx)("div",{className:"sfap-platform-row",children:n.map(e=>(0,a.jsx)("span",{className:"sfap-platform-pill",children:e},e))}),(0,a.jsxs)("div",{className:"sfap-card-foot",children:[e.isLive&&(e.earlyAccessUrl||e.webUrl||e.stores.length>0)&&(0,a.jsxs)("div",{className:"sfap-stores",children:[e.earlyAccessUrl&&(0,a.jsx)("a",{href:e.earlyAccessUrl,className:"sf-btn sf-btn-accent sfap-try-btn",children:"Try it free on web â"}),e.webUrl&&(0,a.jsx)("a",{href:e.webUrl,target:"_blank",rel:"noopener noreferrer",className:"sf-btn sf-btn-accent sfap-try-btn",children:"Open on web â"}),e.stores.map(e=>(0,a.jsx)("a",{href:e.href,target:"_blank",rel:"noopener noreferrer",className:"sfap-store-link","aria-label":e.alt,children:(0,a.jsx)("img",{src:e.src,alt:e.alt})},e.key))]}),"comingSoon"===e.kind&&e.isLive&&(0,a.jsx)("p",{className:"sfap-partial-note",children:"Native apps coming soon"}),e.pageUrl&&(0,a.jsxs)("a",{href:e.pageUrl,className:"sfap-learn-more",children:[e.isLive?"Learn more":"comingSoon"===e.kind?"See what's coming":"Learn more"," â"]})]})]})}function X(){let[e,t]=(0,r.useState)("all"),s=(0,r.useMemo)(()=>[...P.map(Y),...Object.values(F).map(V)],[]),n=(0,r.useMemo)(()=>s.filter(e=>e.isLive),[s]),i=(0,r.useMemo)(()=>s.filter(e=>!e.isLive),[s]),o=(0,r.useMemo)(()=>new Set(s.flatMap(e=>e.platforms)).size,[s]),l=(0,r.useMemo)(()=>{let e=new Map;for(let a of K)e.set(a.key,s.filter(e=>a.test(e.platforms)).length);return e},[s]),c=K.find(a=>a.key===e)?.test??(()=>!0),d=n.filter(e=>c(e.platforms)),p=i.filter(e=>c(e.platforms));return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:Q}}),(0,a.jsxs)("div",{className:"sfap-hero",children:[(0,a.jsx)("p",{className:"sfap-eyebrow",children:"Our Apps"}),(0,a.jsx)("h1",{className:"sfap-title",children:"Everything we build, in one place"}),(0,a.jsxs)("p",{className:"sfap-lead",children:["Soft Flourish designs, ships, and maintains its own apps across iPhone, iPad, Android, Mac, Windows, and Apple TV. ",n.length," are live today â find yours and open it in the store you use. The rest are already in development."]}),(0,a.jsxs)("div",{className:"sf-stats-strip sfap-stats sf-glass",children:[(0,a.jsxs)("div",{className:"sf-stat",children:[(0,a.jsx)("p",{className:"sf-stat-val",children:n.length}),(0,a.jsx)("p",{className:"sf-stat-label",children:"Apps shipped"})]}),(0,a.jsxs)("div",{className:"sf-stat",children:[(0,a.jsx)("p",{className:"sf-stat-val",children:o}),(0,a.jsx)("p",{className:"sf-stat-label",children:"Platforms supported"})]}),(0,a.jsxs)("div",{className:"sf-stat",children:[(0,a.jsx)("p",{className:"sf-stat-val",children:i.length}),(0,a.jsx)("p",{className:"sf-stat-label",children:"In development"})]})]})]}),(0,a.jsx)("div",{className:"sfap-filters",role:"group","aria-label":"Filter apps by platform",children:K.map(s=>{let r=l.get(s.key)??0;return"all"!==s.key&&0===r?null:(0,a.jsxs)("button",{type:"button",className:`sfap-filter-chip${e===s.key?" is-active":""}`,onClick:()=>t(s.key),"aria-pressed":e===s.key,children:[s.label,(0,a.jsx)("span",{className:"sfap-filter-count",children:r})]},s.key)})}),d.length>0&&(0,a.jsxs)("section",{className:"sfap-section",children:[(0,a.jsxs)("p",{className:"sfap-section-title",children:[(0,a.jsx)("span",{className:"sfap-live-d
178ot","aria-hidden":!0}),"Available now"]}),(0,a.jsx)("div",{className:"sfap-grid",children:d.map((e,t)=>(0,a.jsx)(Z,{tile:e,index:t},e.key))})]}),p.length>0&&(0,a.jsxs)("section",{className:"sfap-section",children:[(0,a.jsx)("p",{className:"sfap-section-title sfap-section-title-muted",children:"In active development"}),(0,a.jsx)("div",{className:"sfap-grid",children:p.map((e,t)=>(0,a.jsx)(Z,{tile:e,index:t},e.key))})]}),(0,a.jsxs)("a",{href:"/books",className:"sfap-books-link sf-glass",children:[(0,a.jsx)("strong",{children:"We also publish books."}),(0,a.jsx)("span",{children:"The Agentic AI Handbook â a 17-volume series. See every volume â"})]})]})}let ee=["AI & Technology","Finance","News & Media","Entertainment","Consumer Products","Home & Construction","Lifestyle & Travel"],ea={"AI & Technology":"#1a6b5c",Finance:"#3b5c8f","News & Media":"#9c491d",Entertainment:"#7c3f8a","Consumer Products":"#b8862b","Home & Construction":"#4a5568","Lifestyle & Travel":"#2f7a63"},et=[{name:"Fenul",domain:"fenul.com",url:"https://fenul.com",tagline:"Wealth, automation, and data â connected as one system.",description:"Fenul is Soft Flourish's flagship intelligence hub, pairing sharp editorial on wealth, automation, and real-asset strategy with interactive calculators and planning tools â no sales pressure, no hype.",category:"AI & Technology",status:"live"},{name:"Fenul Wealth Management",domain:"fenulwealthmanagement.com",url:"https://fenulwealthmanagement.com",tagline:"Financial planning tools and guidance that respect your time.",description:"A modern wealth-planning platform combining clear education, practical calculators, and optional access to licensed advisors â built for people who want real answers, not a sales pitch.",category:"Finance",status:"live"},{name:"Fenul Capital Investment",domain:"fenulcapitalinvestment.com",url:"https://fenulcapitalinvestment.com",tagline:"Advanced investment intelligence for serious capital.",description:"A research-driven investment platform for sophisticated investors â data-heavy analysis, scenario-based tools, and market intelligence built for repeat use, not one-off lead generation.",category:"Finance",status:"live"},{name:"Pacific Wealth Desk",domain:"cafinancialadvisor.com",url:"https://cafinancialadvisor.com",tagline:"California money moves, explained without the sales pitch.",description:"Pacific Wealth Desk covers taxes, housing costs, equity comp, and retirement for California households â practical guides and calculators first, licensed advice only if you want it.",category:"Finance",status:"live"},{name:"Alta Capital Desk",domain:"cafinancialadvisor.net",url:"https://cafinancialadvisor.net",tagline:"Capital-side clarity for founders and finance leads.",description:"Alta Capital Desk gives operators, CFOs, and founders decision-grade articles and models on runway, growth financing, and risk â built for people who already think in spreadsheets.",category:"Finance",status:"live"},{name:"Golden State Planner",domain:"financialplannercalifornia.com",url:"https://financialplannercalifornia.com",tagline:"Know what a financial planner actually does â before you hire one.",description:"Golden State Planner helps Californians compare planning approaches, understand fee models, and handle the basics themselves, with licensed guidance available only when it's actually needed.",category:"Finance",status:"live"},{name:"Altitude Wealth Desk",domain:"aviationfinancialadvisor.com",url:"https://aviationfinancialadvisor.com",tagline:"Money advice that understands seniority lists and layovers.",description:"Altitude Wealth Desk translates the volatile pay, benefits, and schedules of aviation careers into planning guides and calculators built specifically for pilots and crew.",category:"Finance",status:"live"},{name:"Apex Wealth Desk",domain:"clevelfinancial.com",url:"https://clevelfinancial.com",tagline:"Wealth decisions that survive board week.",description:"Apex Wealth Desk helps executives navigate equity concentration, deferred comp, and personal balance sheets alongside board-level demands, with peer-level tools instead of generic advice.",category:"Finance",status:"live"},{name:"Stratum Finance",domain:"engineerfinancial.com",url:"https://engineerfinancial.com",tagline:"Model your money like you model systems.",description:"Stratum Finance speaks engineer: RSUs, ISOs, AMT exposure, and FIRE paths explained with explicit assumptions and stress-testable calculators, not hand-wavy finance copy.",category:"Finance",status:"live"},{name:"Axiom Wealth",domain:"lawyerfinancialadvisor.com",url:"https://lawyerfinancialadvisor.com",tagline:"Money strategy that fits billable reality.",description:"Axiom Wealth addresses student debt, partnership economics, and income volatility across the legal career arc, with guides and calculators built for how lawyers actually get paid.",category:"Finance",status:"live"},{name:"Vantage MD Wealth",domain:"medicalfinancialadvisor.com",url:"https://medicalf
178inancialadvisor.com",tagline:"Catch up on wealth without catching the hype.",description:"Vantage MD Wealth covers contract literacy, insurance, and retirement catch-up for physicians and clinicians â evidence-first guidance built around training length and shift work.",category:"Finance",status:"live"},{name:"Empirical Finance",domain:"scientistfinancialadvisor.com",url:"https://scientistfinancialadvisor.com",tagline:"Finance models you can peer-review.",description:"Empirical Finance explains grant-driven income, postdoc stipends, and the academic-to-industry jump with transparent assumptions â personal finance for people who publish methods sections.",category:"Finance",status:"live"},{name:"Factlen",domain:"factlen.com",url:"https://factlen.com",tagline:"See every side of the story, clearly.",description:"Factlen breaks down consequential topics â news, health, technology, finance, and more â into structured, balanced analysis: arguments for, against, what's agreed on, and what's still uncertain.",category:"News & Media",status:"live"},{name:"Koohestun",domain:"koohestun.com",url:"https://koohestun.com",tagline:"Structured, balanced news and analysis â in Persian.",description:"Koohestun delivers Persian-language news and analysis on Iran, the world, technology, health, and more â every topic weighed with arguments for and against, and a clear bottom line.",category:"News & Media",status:"live"},{name:"EchoVira",domain:"echovira.com",url:"https://echovira.com",tagline:"A global record label network, twelve traditions strong.",description:"EchoVira represents original artists across twelve cultural music traditions â Persian, Korean, Arabic, Latin, Chinese, French, Turkish, and more â plus in-house mastering and music video production.",category:"Entertainment",status:"live"},{name:"Hich",domain:"hich.echovira.com",url:"https://hich.echovira.com",tagline:"Persian indie music, tradition meets tomorrow.",description:"Hich is EchoVira's Persian music label, where Iranian classical roots meet contemporary indie sound â original, bilingual releases streaming everywhere.",category:"Entertainment",status:"live"},{name:"Pulseborn",domain:"pulseborn.echovira.com",url:"https://pulseborn.echovira.com",tagline:"American indie and electronic music, built for streaming.",description:"Pulseborn is EchoVira's American label, blending analog warmth with digital precision across original indie and electronic releases.",category:"Entertainment",status:"live"},{name:"Luoxi",domain:"luoxi.echovira.com",url:"https://luoxi.echovira.com",tagline:"Chinese indie music, ethereal and rooted in tradition.",description:"Luoxi is EchoVira's Chinese music label, pairing traditional instruments with contemporary songwriting in Mandarin-language releases.",category:"Entertainment",status:"live"},{name:"Syahi",domain:"syahi.echovira.com",url:"https://syahi.echovira.com",tagline:"South Asian music, classical roots and modern production.",description:"Syahi is EchoVira's South Asian label, fusing classical Indian musical tradition with modern indie production across Hindi and Urdu releases.",category:"Entertainment",status:"live"},{name:"Amanecer",domain:"amanecer.echovira.com",url:"https://amanecer.echovira.com",tagline:"Latin indie-pop with real emotional depth.",description:"Amanecer is EchoVira's Latin music label, bringing indie-pop warmth and Spanish-language songwriting to a global audience.",category:"Entertainment",status:"live"},{name:"Miroir",domain:"miroir.echovira.com",url:"https://miroir.echovira.com",tagline:"French indie-electro, rooted in chanson tradition.",description:"Miroir is EchoVira's French music label, blending indie-electro production with contemporary chanson for French and bilingual audiences.",category:"Entertainment",status:"live"},{name:"Bahira",domain:"bahira.echovira.com",url:"https://bahira.echovira.com",tagline:"Arabic world music for a global audience.",description:"Bahira is EchoVira's Arabic music label, drawing on Gulf and Levantine traditions for original, bilingual Arabic-English releases.",category:"Entertainment",status:"live"},{name:"Cortica",domain:"cortica.echovira.com",url:"https://cortica.echovira.com",tagline:"Portuguese indie music, carrying the depth of fado.",description:"Cortica is EchoVira's Portuguese music label, pairing indie sound with fado's emotional undertones across original Portuguese-language releases.",category:"Entertainment",status:"live"},{name:"Morvina",domain:"morvina.echovira.com",url:"https://morvina.echovira.com",tagline:"Slavic and Eastern European indie, carried into the modern era.",description:"Morvina is EchoVira's Slavic music label, bringing contemporary Russian and Eastern European songwriting to bilingual, Cyrillic-script releases.",category:"Entertainment",status:"live"},{name:"Frekans",domain:"frekans.echovira.com",url:"https://frekans.echovira.com",tagline:"Turkish alternative rock, rooted in Anatolian tradition.",description:"Frekans is EchoVira's Turkish music label, connecting Istanbul's modern scene to its deep musical roots through original Turkish-language releases.",category:"Entertainment",status:"live"},{name:"Byeolbit",domain:"byeolbit.echovira.com",url:"https://byeolbit.echovira.com",tagline:"K-indie, beyond the mainstream.",description:"Byeolbit is EchoVira's Korean music label, bringing contemporary K-indie sound and bilingual releases to the global independent music scene.",category:"Entertainment",status:"live"},{name:"Echoes",domain:"echoes.echovira.com",url:"https://echoes.echovira.com",tagline:"A 32-piece world ensemble that needs no lyrics.",description:"Echoes is EchoVira's instrumental label â original compositions performed by a 32-piece world ensemble, drawing on musical traditions from around the globe.",category:"Entertainment",status:"live"},{name:"EchoVira Mastering",domain:"mastering.echovira.com",url:"https://mastering.echovira.com",tagline:"Professional mastering, open to any artist.",description:"EchoVira Mastering provides professional stereo, stem, and album mastering optimized for every major streaming platform â a 5â7 day turnaround, no label membership required.",category:"Entertainment",status:"live"},{name:"EchoVira Music Video",domain:"musicvideo.echovira.com",url:"https://musicvideo.echovira.com",tagline:"Concept-to-delivery music videos, made in LA.",description:"EchoVira Music Video produces full music video content for independent artists worldwide â concept development, LA studio and location shoots, and short-form content for every platform.",category:"Entertainment",status:"live"},{name:"Everacity",domain:"everacity.com",url:"https://everacity.com",tagline:"Find the best products instantly.",description:"Everacity ranks and reviews products across electronics, home, health, and fashion â clear scoring, live data, and a direct link to buy, so you can decide with confidence.",category:"Consumer Products",status:"live"},{name:"Omenira",domain:"omenira.com",url:"https://omenira.com",tagline:"A curated online store, run the honest way.",description:"Omenira is Soft Flourish's direct-to-consumer storefront â a curated catalog with real customer support and human-reviewed order fulfillment behind every purchase.",category:"Consumer Products",status:"live"},{name:"LA Premier Builders",domain:"lapremierbuilders.com",url:"https://lapremierbuilders.com",tagline:"Find the right builder â not just any builder.",description:"LA Premier Builders helps Los Angeles homeowners shortlist the right contractor with transparent comparisons and readiness scoring, covering the whole LA market.",category:"Home & Construction",status:"live"},{name:"Oceanfront Builders â Orange County",domain:"oceanfrontbuilders.com",url:"https://oceanfrontbuilders.com",tagline:"Serious projects require serious builders.",description:"Oceanfront Builders is Orange County's luxury construction authority â high-end remodels and new builds across Newport Beach, Irvine, and Laguna Beach, backed by feasibility tools that respect your budget.",category:"Home & Construction",status:"live"},{name:"Oceanfront Builders â Los Angeles",domain:"oceanfront.builders",url:"https://oceanfront.builders",tagline:"Build bold. Build right.",description:"Oceanfront Builders' Los Angeles studio covers design-forward, high-end construction across Beverly Hills, Santa Monica, and Malibu â feasibility tools grounded in real neighborhoods.",category:"Home & Construction",status:"live"},{name:"Palos Verdes Builders",domain:"palosverdesbuilders.com",url:"https://palosverdesbuilders.com",tagline:"The most beautiful coastline in Southern California.",description:"Palos Verdes Builders is the Peninsula's home builder â deep local knowledge of Rolling Hills, Palos Verdes Estates, and Rancho Palos Verdes, built by people who live there.",category:"Home & Construction",status:"live"},{name:"PV Premier Builders",domain:"pvpremierbuilders.com",url:"https://pvpremierbuilders.com",tagline:"The straightforward way to hire well in Palos Verdes.",description:"PV Premier Builders offers simple, honest contractor matching for Palos Verdes homeowners planning kitchens, exteriors, ADUs, and structural upgrades.",category:"Home & Construction",status:"live"},{name:"Minoo Life Diary",domain:"minoolifediary.com",url:"https://minoolifediary.com",tagline:"Real travel stories, from someone who's actually been there.",description:"Minoo Life Diary shares personal travel storytelling and recommendations from around the world â global experiences, honest insights, real photos.",category:"Lifestyle & Travel",status:"live"}],es=` 179.sfws-hero{max-width:820px;margin:0 auto 40px;text-align:center} 180.sfws-eyebrow{font-size:.72rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:var(--sf-accent);margin-bottom:14px} 181.sfws-title{font-family:var(--font-fraunces),Georgia,serif;font-size:clamp(1.7rem,3.4vw,2.5rem);font-weight:650;line-height:1.2;color:var(--sf-fg);margin-bottom:18px} 182.sfws-lead{font-size:1.02rem;line-height:1.75;color:var(--sf-muted)} 183.sfws-filters{display:flex;flex-wrap:wrap;justify-content:center;gap:8px;margin:0 0 40px} 184.sfws-filter-pill{ 185 font-size:.82rem;font-weight:600;color:var(--sf-muted);background:rgba(255,255,255,0.5); 186 border:1px solid var(--sf-border);padding:8px 16px;border-radius:999px;cursor:pointer; 187 transition:background .2s var(--sf-ease),color .2s var(--sf-ease),transform .2s var(--sf-ease); 188} 189.sfws-filter-pill:hover{transform:translateY(-1px);color:var(--sf-fg)} 190.sfws-filter-pill.active{background:var(--sf-fg);color:#fff;border-color:var(--sf-fg)} 191.sfws-grid{display:grid;gap:20px;grid-template-columns:1fr} 192@media(min-width:640px){.sfws-grid{grid-template-columns:repeat(2,1fr)}} 193@media(min-width:1080px){.sfws-grid{grid-template-columns:repeat(3,1fr)}} 194.sfws-card{border-radius:var(--sf-radius);overflow:hidden;display:flex;flex-direction:column;transition:transform .25s var(--sf-ease)} 195.sfws-card:hover{transform:translateY(-3px)} 196.sfws-chrome{display:flex;align-items:center;gap:8px;padding:11px 14px} 197.sfws-dot{width:7px;height:7px;border-radius:50%;background:rgba(255,255,255,0.55)} 198.sfws-chrome-url{margin-left:6px;font-size:.72rem;font-weight:600;color:rgba(255,255,255,0.92);letter-spacing:.01em;overflow:hidden;text-overflow:ellipsis;white-space:nowrap} 199.sfws-card-body{padding:20px 22px 22px;display:flex;flex-direction:column;flex:1} 200.sfws-category{font-size:.68rem;font-weight:700;letter-spacing:.09em;text-transform:uppercase;margin-bottom:10px} 201.sfws-card h3{font-size:1.05rem;font-weight:650;line-height:1.3;color:var(--sf-fg);margin-bottom:6px} 202.sfws-tagline{font-size:.88rem;font-weight:600;line-height:1.5;color:var(--sf-fg);margin-bottom:10px} 203.sfws-description{font-size:.86rem;line-height:1.65;color:var(--sf-muted);margin-bottom:18px;flex:1} 204.sfws-visit{ 205 align-self:flex-start;font-size:.8rem;font-weight:650;color:#fff;text-decoration:none; 206 padding:9px 16px;border-radius:999px;transition:opacity .2s var(--sf-ease); 207} 208.sfws-visit:hover{opacity:.85} 209.sfws-count{text-align:center;font-size:.82rem;color:var(--sf-muted);margin:-20px 0 32px} 210.sfws-card-soon{padding:20px 22px;justify-content:center} 211.sfws-soon-badge{align-self:flex-start;font-size:.68rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--sf-accent-warm);background:rgba(156,73,29,0.12);padding:4px 10px;border-radius:999px;margin-bottom:10px} 212`;function er({site:e}){if("coming-soon"===e.status)return(0,a.jsxs)("article",{className:"sfws-card sfws-card-soon sf-glass",children:[(0,a.jsx)("span",{className:"sfws-soon-badge",children:"Coming Soon"}),(0,a.jsx)("h3",{children:e.name}),(0,a.jsx)("p",{className:"sfws-tagline",children:e.tagline})]});let t=ea[e.category];return(0,a.jsxs)("article",{className:"sfws-card sf-glass",children:[(0,a.jsxs)("div",{className:"sfws-chrome",style:{background:t},children:[(0,a.jsx)("span",{className:"sfws-dot"}),(0,a.jsx)("span",{className:"sfws-dot"}),(0,a.jsx)("span",{className:"sfws-dot"}),(0,a.jsx)("span",{className:"sfws-chrome-url",children:e.domain})]}),(0,a.jsxs)("div",{className:"sfws-card-body",children:[(0,a.jsx)("span",{className:"sfws-category",style:{color:t},children:e.category}),(0,a.jsx)("h3",{children:e.name}),(0,a.jsx)("p",{className:"sfws-tagline",children:e.tagline}),(0,a.jsx)("p",{className:"sfws-description",children:e.description}),(0,a.jsx)("a",{href:e.url,target:"_blank",rel:"noopener noreferrer",className:"sfws-visit",style:{background:t},children:"Visit Website â"})]})]})}function en(){let e=(0,r.useMemo)(()=>ee.filter(e=>et.some(a=>a.category===e)),[]),[t,s]=(0,r.useState)("All"),n=(0,r.useMemo)(()=>et.filter(e=>"live"===e.status).length,[]),i="All"===t?et:et.filter(e=>e.category===t);return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:es}}),(0,a.jsxs)("div",{className:"sfws-hero",children:[(0,a.jsx)("p",{className:"sfws-eyebrow",children:"Our Websites"}),(0,a.jsx)("h1",{className:"sfws-title",children:"A growing ecosystem of digital brands"}),(0,a.jsxs)("p",{className:"sfws-lead",children:["Soft Flourish designs, builds, and operates ",n," live websites and platforms â spanning AI-powered product discovery, independent finance desks, a global record label network, and structured news. Every one below is real and live; open any of them and look around."]})]}),(0,a.jsxs)("div",{className:"sfws-filters",role:"tablist","aria-label":"Filter websites by category",children:[(0,a.jsx)("button",{type:"button",className:`sfws-filter-pill${"All"===t?" active":""}`,onClick:()=>s("All"),"aria-pressed":"All"===t,children:"All"}),e.map(e=>(0,a.jsx)("button",{type:"button",className:`sfws-filter-pill${t===e?" active":""}`,onClick:()=>s(e),"aria-pressed":t===e,children:e},e))]}),(0,a.jsx)("div",{className:"sfws-grid",children:i.map(e=>(0,a.jsx)(er,{site:e},e.domain))})]})}e.i(151718);var ei=e.i(594684),eo=e.i(839849),el=e.i(898109),ec=e.i(127254),ed=e.i(276494),ed=ed,ep=ed,eh=ed,em=e.i(959640),eu=e.i(603249);function ef(){let e=(0,ei.getAuth)((0,eu.getFirebaseApp)());
212return e.tenantId="flourishpdf-kh1bu",e}function eg(e){let a=e.rcBillingProduct,t=a?.freeTrialPhase?.period;if(!a||!t)return null;let s=String(t.unit),r="week"===s?7*t.number:t.number,n=`${r} ${"week"===s?"day":s}${1===r?"":"s"} free`,i=a.currentPrice?.formattedPrice,o=a.period?`/${String(a.period.unit)}`:"";return i?`${n}, then ${i}${o}`:n}function ex(){let[t,s]=(0,r.useState)(null),[n,i]=(0,r.useState)(!1),[o,l]=(0,r.useState)(""),[c,d]=(0,r.useState)(""),[p,h]=(0,r.useState)(!1),[m,u]=(0,r.useState)([]),[f,g]=(0,r.useState)(!1),[x,b]=(0,r.useState)(null),[y,v]=(0,r.useState)(!1),[w,j]=(0,r.useState)(null);(0,r.useEffect)(()=>{let e=(0,eo.onAuthStateChanged)(ef(),e=>{s(e),i(!0)});return()=>e()},[]);let k=(0,r.useCallback)(async a=>{g(!1);try{let{Purchases:t}=await e.A(323738),s=t.isConfigured()?t.getSharedInstance():t.configure({apiKey:"rcb_cPDMLPXhxxHmdbBRclbqlmsDuFrs",appUserId:a});t.isConfigured()&&await s.changeUser(a),b(await s.getCustomerInfo());let r=await s.getOfferings(),n=r.all?.["flourishpdf.default"]??r.current;u(n?.availablePackages??[])}catch(e){j(e.message)}finally{g(!0)}},[]);(0,r.useEffect)(()=>{t&&k(t.uid)},[t,k]);let N=e=>{v(!0),j(null),e().catch(e=>j(e.message)).finally(()=>v(!1))},S=x?.entitlements.active.flourishpdf_pro?"Pro":x?.entitlements.active.flourishpdf_plus?"Plus":"Free",A="Free"!==S,P=Object.values(x?.entitlements.active??{}).some(e=>"trial"===e.periodType);return(0,a.jsxs)("main",{style:{maxWidth:560,margin:"0 auto",padding:"48px 20px",color:"#F7F7F8"},children:[(0,a.jsxs)("h1",{style:{fontSize:28,fontWeight:800},children:["Flourish PDF ",(0,a.jsx)("span",{style:{color:"#C95DFF"},children:"account"})]}),(0,a.jsx)("p",{style:{color:"#B8BAC1",marginTop:8},children:"Manage your Windows subscription. Sign in here, subscribe, then sign in inside the Windows app with the same account to unlock. Your documents never leave your device."}),n?t?(0,a.jsxs)("section",{style:{marginTop:24,display:"grid",gap:12},children:[(0,a.jsxs)("div",{style:{padding:14,borderRadius:10,background:"#27282D",border:"1px solid #42434A"},children:["Signed in as ",(0,a.jsx)("strong",{children:t.email??"your account"})," â current plan: ",(0,a.jsx)("strong",{children:S})]}),A?(0,a.jsxs)("p",{style:{color:"#47D18C"},children:[P?`Your ${S} free trial is running â you won't be charged until it ends.`:`You're on ${S}.`," ","Open the Windows app and sign in with this account to unlock."]}):f?0===m.length?(0,a.jsxs)("div",{style:{padding:14,borderRadius:10,background:"#3A2A2E",border:"1px solid #FF667A"},children:[(0,a.jsx)("p",{style:{color:"#FF667A",fontWeight:600},children:"Plans are temporarily unavailable."}),(0,a.jsxs)("p",{style:{color:"#B8BAC1",marginTop:6,fontSize:14},children:["This is on us, not you â nothing was charged. Please try again shortly, or email"," ",(0,a.jsx)("a",{href:"mailto:[email protected]",style:{color:"#5DB2FF"},children:"[email protected]"})," ","and we'll sort it out. Flourish PDF stays fully usable on the Free tier meanwhile."]})]}):(0,a.jsxs)(a.Fragment,{children:[m.map(t=>{let s=eg(t);return(0,a.jsxs)("button",{disabled:y,onClick:()=>N(async()=>{let{Purchases:a}=await e.A(323738);b((await a.getSharedInstance().purchase({rcPackage:t})).customerInfo)}),style:{...eb,textAlign:"left"},children:[s?"Start free trial":"Subscribe"," â ",t.rcBillingProduct?.displayName??t.identifier,t.rcBillingProduct?.currentPrice?.formattedPrice?` \xb7 ${t.rcBillingProduct.currentPrice.formattedPrice}`:"",s&&(0,a.jsx)("span",{style:{display:"block",marginTop:4,color:"#47D18C",fontSize:13},children:s})]},t.identifier)}),m.some(e=>eg(e))&&(0,a.jsx)("p",{style:{color:"#858790",fontSize:13},children:"Each plan's free trial covers that plan only â start a Plus trial and you get Plus for the trial period, start a Pro trial and you get Pro. A trial converts to a paid subscription at the price shown when it ends, unless you cancel before then. You can cancel any time from this page."})]}):(0,a.jsx)("p",{style:{color:"#858790"},children:"Loading plansâ¦"}),(0,a.jsx)("button",{onClick:()=>N(async()=>{await (0,em.signOut)(ef())}),style:ev,children:"Sign out"})]}
212):(0,a.jsxs)("section",{style:{marginTop:24,display:"grid",gap:10},children:[(0,a.jsx)("button",{disabled:y,onClick:()=>N(async()=>{await (0,el.signInWithPopup)(ef(),new ec.GoogleAuthProvider)}),style:eb,children:"Continue with Google"}),(0,a.jsx)("button",{disabled:y,onClick:()=>N(async()=>{await (0,el.signInWithPopup)(ef(),new ed._("apple.com"))}),style:eb,children:"Continue with Apple"}),(0,a.jsx)("div",{style:{height:1,background:"#42434A",margin:"8px 0"}}),(0,a.jsx)("input",{placeholder:"Email",value:o,onChange:e=>l(e.target.value),style:ey}),(0,a.jsx)("input",{placeholder:"Password",type:"password",value:c,onChange:e=>d(e.target.value),style:ey}),(0,a.jsx)("button",{disabled:y||!o||!c,onClick:()=>N(async()=>{let e=ef();p?await (0,eh.ab)(e,o,c):await (0,ep.ac)(e,o,c)}),style:{...eb,background:"#C95DFF",color:"#1D1E22"},children:p?"Create account":"Sign in"}),(0,a.jsx)("button",{onClick:()=>h(e=>!e),style:{...ev},children:p?"Have an account? Sign in":"New here? Create an account"})]}):(0,a.jsx)("p",{style:{marginTop:24,color:"#858790"},children:"Loadingâ¦"}),w&&(0,a.jsx)("p",{style:{color:"#FF667A",marginTop:16},children:w})]})}let eb={padding:"11px 16px",borderRadius:10,border:"1px solid #42434A",background:"#303137",color:"#F7F7F8",fontWeight:600,cursor:"pointer"},ey={padding:"10px 12px",borderRadius:8,border:"1px solid #42434A",background:"#1D1E22",color:"#F7F7F8"},ev={background:"none",border:"none",color:"#5DB2FF",cursor:"pointer",fontSize:13,padding:4};e.i(436180);var ew=e.i(276807),ew=ew,ej=ew,ek=ew,eN=e.i(263802),eS=e.i(457879);let eA="ops_manual_walls",eP="_settings",ez="ops_oauth_logins",eT=[{id:"kdp",label:"Amazon KDP"},{id:"ingram",label:"IngramSpark"}];function eF(){let e=(0,ei.getAuth)((0,eu.getFirebaseApp)());return e.tenantId="owner-portal-w358j",e}function eC(){return(0,ew.aX)((0,eu.getFirebaseApp)())}let eM=[{id:"web",label:"Web"},{id:"apps",label:"Apps"},{id:"books",label:"Books"},{id:"other",label:"Everything else"}],eI='<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg>',eD='<svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12.5 9.5 18 20 6.5"/></svg>';async function eW(e){try{return await navigator.clipboard.writeText(e),!0}catch{try{let a=document.createElement("textarea");a.value=e,a.setAttribute("readonly",""),a.style.cssText="position:fixed;top:0;left:0;opacity:0",document.body.appendChild(a),a.select();let t=document.execCommand("copy");return a.remove(),t}catch{return!1}}}function eO({text:e,wide:t}){let[s,n]=(0,r.useState)("idle");(0,r.useEffect)(()=>{if("idle"===s)return;let e=setTimeout(()=>n("idle"),1500);return()=>clearTimeout(e)},[s]);let i="done"===s;return(0,a.jsxs)("button",{type:"button",className:`mw-copy${i?" mw-copy-done":""}${"failed"===s?" mw-copy-failed":""}${t?" mw-copy-wide":""}`,title:i?"Copied":"Copy","aria-label":i?"Copied":"Copy",onClick:async a=>{a.preventDefault(),n(await eW(e)?"done":"failed")},children:[(0,a.jsx)("span",{className:"mw-copy-glyph",dangerouslySetInnerHTML:{__html:i?eD:eI}}),t&&(0,a.jsx)("span",{children:i?"Copied":"failed"===s?"Copy failed":"Copy"})]})}function eE({html:e}){let t=(0,r.useRef)(null);return(0,r.useEffect)(()=>{let e=t.current;if(!e)return;let a=[];return e.querySelectorAll("code").forEach(e=>{if(e.nextElementSibling?.classList.contains("mw-copy"))return;let t=document.createElement("button");t.type="button",t.className="mw-copy",t.title="Copy",t.setAttribute("aria-label","Copy"),t.innerHTML=`<span class="mw-copy-glyph">${eI}</span>`,t.addEventListener("click",async()=>{let s=await eW(e.textContent??"");t.innerHTML=`<span class="mw-copy-glyph">${s?eD:eI}</span>`,t.classList.toggle("mw-copy-done",s),t.classList.toggle("mw-copy-failed",!s),t.title=s?"Copied":"Copy failed",a.push(window.setTimeout(()=>{t.innerHTML=`<span class="mw-copy-glyph">${eI}</span>`,t.classList.remove("mw-copy-done","mw-copy-failed"),t.title="Copy"},1500))}),e.after(t)}),()=>
212a.forEach(clearTimeout)},[e]),(0,a.jsx)("div",{className:"mw-sub-detail",ref:t,dangerouslySetInnerHTML:{__html:e}})}function eR({id:e,checked:t,indeterminate:s,disabled:n,label:i,onChange:o}){let l=(0,r.useRef)(null);return(0,r.useEffect)(()=>{l.current&&(l.current.indeterminate=s)},[s]),(0,a.jsx)("input",{ref:l,id:e,type:"checkbox",className:"mw-check mw-check-block",checked:t,disabled:n,"aria-label":i,title:i,onChange:e=>o(e.target.checked)})}function e$(e){let a=e.walls.reduce((e,a)=>e+a.subtasks.length,0),t=e.walls.reduce((a,t)=>a+t.subtasks.filter(a=>e.checked[`${t.id}.${a.id}`]).length,0),s=e.walls.filter(a=>a.subtasks.length>0&&a.subtasks.every(t=>e.checked[`${a.id}.${t.id}`])).length;return{total:a,done:t,wallsDone:s,wallsOpen:e.walls.length-s}}function e_(){let[e,t]=(0,r.useState)(null),[s,n]=(0,r.useState)(!1),[i,o]=(0,r.useState)([]),[l,c]=(0,r.useState)({}),[d,p]=(0,r.useState)({}),[h,m]=(0,r.useState)(!1),[u,f]=(0,r.useState)(null),[g,x]=(0,r.useState)(null),b=/permission|insufficient/i.test(u??"");(0,r.useEffect)(()=>(0,eo.onAuthStateChanged)(eF(),e=>{t(e),n(!0)}),[]),(0,r.useEffect)(()=>{if(!e){o([]),m(!1);return}return(0,eN.onSnapshot)((0,ej.aO)(eC(),eA),e=>{let a=e.docs.find(e=>e.id===eP);c(a?.data()??{});let t=e.docs.filter(e=>!e.id.startsWith("_")),s=t.filter(e=>!Array.isArray(e.data().walls)).map(e=>e.id);if(s.length){let e=`Malformed ${eA} docs (no walls array): ${s.join(", ")}`;console.error(e),f(e),m(!0);return}let r=t.map(e=>{let a,t=e.data();return{id:e.id,title:"string"==typeof t.title&&t.title?t.title:e.id,group:"web"===(a=t.group)||"apps"===a||"books"===a||"other"===a?t.group:"other",path:"string"==typeof t.path?t.path:"",walls:t.walls,checked:t.checked??{},feedback:"string"==typeof t.feedback?t.feedback:"",feedbackUpdatedAt:"string"==typeof t.feedbackUpdatedAt?t.feedbackUpdatedAt:"",feedbackLog:Array.isArray(t.feedbackLog)?t.feedbackLog:[]}});r.sort((e,a)=>e.title.localeCompare(a.title)),o(r),m(!0),f(null)},e=>{let a=`Could not read ${eA} from Firestore: ${e.message}`;console.error(a),f(a),m(!0)})},[e]),(0,r.useEffect)(()=>e?(0,eN.onSnapshot)((0,ej.aO)(eC(),ez),e=>{let a={};e.docs.forEach(e=>{a[e.id]=e.data()}),p(a)},()=>{}):void p({}),[e]);let y=(0,r.useCallback)(async e=>{try{await (0,eN.setDoc)((0,ek.a6)(eC(),ez,e),{loginRequestedAt:new Date().toISOString()},{merge:!0})}catch(e){f(e instanceof Error?e.message:String(e))}},[]),v=(0,r.useCallback)(async e=>{try{await (0,eN.setDoc)((0,ek.a6)(eC(),eA,eP),{macUiBlocked:e,macUiUpdatedAt:new Date().toISOString()},{merge:!0})}catch(e){f(e instanceof Error?e.message:String(e))}},[]),w=(0,r.useCallback)(async(e,a,t)=>{let s=e.checked,r={...s,[a]:t};t||delete r[a],o(a=>a.map(a=>a.id===e.id?{...a,checked:r}:a));try{await (0,eN.setDoc)((0,ek.a6)(eC(),eA,e.id),{checked:r,updatedAt:new Date().toISOString()},{merge:!0})}catch(r){o(a=>a.map(a=>a.id===e.id?{...a,checked:s}:a));let t=`Could not save that tick (${e.id}/${a}): ${r.message}`;console.error(t),f(t)}},[]),j=(0,r.useCallback)(async(e,a,t)=>{let s=e.checked,r={...s};for(let e of a.subtasks){let s=`${a.id}.${e.id}`;t?r[s]=!0:delete r[s]}o(a=>a.map(a=>a.id===e.id?{...a,checked:r}:a));try{await (0,eN.setDoc)((0,ek.a6)(eC(),eA,e.id),{checked:r,updatedAt:new Date().toISOString()},{merge:!0})}catch(r){o(a=>a.map(a=>a.id===e.id?{...a,checked:s}:a));let t=`Could not save that block (${e.id}/${a.id}): ${r.message}`;console.error(t),f(t)}},[]),k=(0,r.useCallback)(async(e,a)=>{let t=new Date().toISOString();o(s=>s.map(s=>s.id===e.id?{...s,feedback:a,feedbackUpdatedAt:t}:s)),await (0,eN.setDoc)((0,ek.a6)(eC(),eA,e.id),{feedback:a,feedbackUpdatedAt:t,updatedAt:t},{merge:!0})},[]),N=(0,r.useMemo)(()=>i.find(e=>e.id===g)??null,[i,g]);return s?e?b?(0,a.jsx)(eV,{onSignOut:()=>(0,em.signOut)(eF())}):(0,a.jsxs)("main",{className:"mw-root",children:[(0,a.jsx)("style",{children:eK}),(0,a.jsxs)("div",{className:"mw-bar",children:[N?(0,a.jsx)("button",{className:"mw-btn mw-btn-quiet",onClick:()=>x(null),children:"â All services"}):(0,a.jsx)("span",{}),(0,a.jsxs)("span",{className:"mw-who",children:[e.email,(0,a.jsx)("button",{className:"mw-btn mw-btn-quiet",onClick:()=>(0,em.signOut)(eF()),children:"Sign out"})]})]}),(0,a.jsx)(eS.OwnerNav,{active:"manualwalls"}),(0,a.jsx)(eL,{kdpAuth:l.kdpAuth}),(0,a.jsx)(eq,{logins:d,onRequest:y}),(0,a.jsx)(eB,{settings:l,onChange:v}),u&&(0,a.jsx)("p",{className:"mw-error",children:u}),!u&&!h&&(0,a.jsx)("p",{className:"mw-muted",children:"Loadingâ¦"}),!u&&h&&!N&&(0,a.jsx)(eG,{projects:i,onOpen:x}),!u&&h&&N&&(0,a.jsx)(eY,{project:N,onToggle:(e,a)=>w(N,e,a),onToggleWall:(e,a)=>j(N,e,a),onSaveFeedback:e=>k(N,e)})]}):(0,a.jsx)(eV,{error:u,onSignIn:async()=>{f(null);try{let e=new ec.GoogleAuthProvider;e.setCustomParameters({prompt:"select_account"}),await (0,el.signInWithPopup)(eF(),e)}catch(e){f(e.message)}}}):(0,a.jsx)(eV,{})}function eU(e){if(!e)return"";let a=new Date(e);
212return Number.isNaN(a.getTime())?"":a.toLocaleString()}function eL({kdpAuth:e}){if(!e||!e.checkedAt)return null;let t=!0===e.loggedIn,s=(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("span",{className:`mw-kdp-dot${t?" mw-kdp-dot-ok":" mw-kdp-dot-bad"}`,"aria-hidden":"true"}),(0,a.jsxs)("span",{className:"mw-kdp-text",children:[(0,a.jsxs)("strong",{children:["KDP ",t?"signed in":"sign-in needed"]}),(0,a.jsxs)("span",{className:"mw-kdp-sub",children:[t?"book revenue refreshing daily":"tap to sign in â takes under a minute"," · checked ",eU(e.checkedAt)]})]})]});return t?(0,a.jsx)("div",{className:"mw-kdp mw-kdp-ok",children:s}):(0,a.jsx)("a",{className:"mw-kdp mw-kdp-bad",href:e.loginUrl||"#",target:"_blank",rel:"noopener noreferrer",children:s})}function eq({logins:e,onRequest:t}){let[s,n]=(0,r.useState)(null);(0,r.useEffect)(()=>{if(!s)return;let e=setTimeout(()=>n(null),8e3);return()=>clearTimeout(e)},[s]);let i=eT.filter(a=>e[a.id]);return 0===i.length?null:(0,a.jsx)("section",{className:"mw-login",children:i.map(({id:r,label:i})=>{let o=e[r],l=o?.status==="live";return(0,a.jsxs)("div",{className:`mw-login-row${l?" mw-login-ok":" mw-login-bad"}`,children:[(0,a.jsx)("span",{className:`mw-login-dot${l?" mw-login-dot-ok":" mw-login-dot-bad"}`,"aria-hidden":"true"}),(0,a.jsxs)("span",{className:"mw-login-text",children:[(0,a.jsx)("strong",{children:i}),(0,a.jsxs)("span",{className:"mw-login-sub",children:[o?.statusReason||(l?"signed in":"needs a sign-in"),o?.lastCheckedAt&&(0,a.jsxs)(a.Fragment,{children:[" · checked ",eU(o.lastCheckedAt)]})]})]}),(0,a.jsx)("button",{type:"button",className:"mw-btn mw-login-btn",disabled:s===r,onClick:()=>{n(r),t(r)},children:s===r?"Opening on your Macâ¦":"Sign in on my Mac"})]},r)})})}function eB({settings:e,onChange:t}){let s=!0===e.macUiBlocked;return(0,a.jsxs)("section",{className:`mw-switch${s?" mw-switch-on":""}`,children:[(0,a.jsxs)("label",{className:"mw-switch-main",children:[(0,a.jsx)("input",{type:"checkbox",checked:s,onChange:e=>t(e.target.checked)}),(0,a.jsxs)("span",{children:[(0,a.jsx)("strong",{children:"Donât take over my Mac"}),(0,a.jsx)("span",{className:"mw-switch-sub",children:s?"On â agents skip macOS UI tests and desktop capture. Simulators still run.":"Off â agents may drive the desktop inside the allowed hours."})]})]}),s&&e.macUiUpdatedAt&&(0,a.jsxs)("span",{className:"mw-switch-since",children:["since ",eU(e.macUiUpdatedAt)]})]})}function eG({projects:e,onOpen:t}){let s=e.reduce((e,a)=>{let t=e$(a);return e.open+=t.wallsOpen,e.subOpen+=t.total-t.done,a.feedback.trim()&&(e.notes+=1),e},{open:0,subOpen:0,notes:0});return 0===e.length?(0,a.jsx)("p",{className:"mw-muted",children:"Nothing open right now."}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("p",{className:"mw-lede",children:[s.open," open ",1===s.open?"wall":"walls"," across ",e.length," ","services · ",s.subOpen," steps remaining. Everything here needs a console click or a decision only you can make â anything scriptable runs automatically instead.",s.notes>0&&` ${s.notes} ${1===s.notes?"note is":"notes are"} waiting to be picked up.`]}),eM.map(({id:s,label:r})=>{let n=e.filter(e=>e.group===s);return 0===n.length?null:(0,a.jsxs)("section",{className:"mw-group",children:[(0,a.jsxs)("h2",{className:"mw-group-head",children:[r," ",(0,a.jsx)("span",{className:"mw-group-count",children:n.length})]}),(0,a.jsx)("ul",{className:"mw-tiles",children:n.map(e=>{let s=e$(e),r=0===s.wallsOpen,n=!!e.feedback.trim();return(0,a.jsx)("li",{children:(0,a.jsxs)("button",{className:`mw-tile${r&&!n?" mw-tile-clear":""}`,onClick:()=>t(e.id),children:[(0,a.jsxs)("span",{className:"mw-tile-top",children:[(0,a.jsx)("span",{className:"mw-tile-name",children:e.title}),n&&(0,a.jsx)("span",{className:"mw-tile-note",children:"note"}),(0,a.jsx)("span",{className:`mw-badge${r?" mw-badge-clear":""}`,children:r?"clear":s.wallsOpen})]}),e.path&&(0,a.jsx)("span",{className:"mw-tile-path",children:e.path}),(0,a.jsxs)("span",{className:"mw-tile-meta",children:[s.done,"/",s.total," steps done"]}),(0,a.jsx)("span",{className:"mw-track",children:(0,a.jsx)("span",{className:"mw-fill",style:{width:s.total?`${s.done/s.total*100}%`:"0%"}})})]})},e.id)})})]},s)})]})}function eH({project:e,onSave:t}){let[s,n]=(0,r.useState)(e.feedback),[i,o]=(0,r.useState)(!1),[l,c]=(0,r.useState)(!1),[d,p]=(0,r.useState)(null);(0,r.useEffect)(()=>{i||n(e.feedback)},[e.id,e.feedback,i]),(0,r.useEffect)(()=>{o(!1),p(null)},[e.id]);let h=e.feedbackLog[0],m=!!e.feedback.trim();return(0,a.jsxs)("section",{className:"mw-fb",children:[(0,a.jsxs)("div",{className:"mw-fb-head",children:[(0,a.jsx)("span",{className:"mw-fb-title",children:"Note for the agent"}),m&&!i&&(0,a.jsx)("span",{className:"mw-fb-flag",children:"waiting"})]}),(0,a.jsx)("p",{className:"mw-fb-hint",children:"Bugs, ideas, c
212orrections, anything you want changed. The next run reads this first, turns it into tasks, and empties the box."}),(0,a.jsx)("textarea",{className:"mw-fb-text",value:s,rows:5,spellCheck:!0,onChange:e=>{n(e.target.value),o(!0)}}),(0,a.jsxs)("div",{className:"mw-fb-foot",children:[(0,a.jsx)("button",{type:"button",className:"mw-btn",disabled:!i||l,onClick:async()=>{c(!0),p(null);try{await t(s),o(!1)}catch(e){p(e.message)}finally{c(!1)}},children:l?"Savingâ¦":i?"Save":"Saved"}),(0,a.jsx)("span",{className:"mw-fb-state",children:d?`Not saved â ${d}`:i?"Unsaved":m?`Waiting to be picked up${e.feedbackUpdatedAt?` \xb7 saved ${eU(e.feedbackUpdatedAt)}`:""}`:h?.clearedAt?`Last note picked up ${eU(h.clearedAt)}`:"Empty"})]}),!m&&h?.note&&(0,a.jsxs)("p",{className:"mw-fb-last",children:[(0,a.jsx)("span",{className:"mw-fb-last-label",children:"Became"})," ",h.note]})]})}function eY({project:e,onToggle:t,onToggleWall:s,onSaveFeedback:r}){let n=e$(e);return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("header",{className:"mw-head",children:[(0,a.jsx)("h1",{children:e.title}),e.path&&(0,a.jsx)("p",{className:"mw-path",children:e.path}),(0,a.jsxs)("p",{className:"mw-lede",children:[n.wallsOpen," open ",1===n.wallsOpen?"wall":"walls"," · ",n.done,"/",n.total," steps done"]})]}),(0,a.jsx)(eH,{project:e,onSave:r}),0===e.walls.length&&(0,a.jsx)("p",{className:"mw-muted",children:"Nothing open for this service."}),(0,a.jsx)("ul",{className:"mw-list",children:e.walls.map(r=>{let n=r.subtasks.filter(a=>e.checked[`${r.id}.${a.id}`]).length,i=r.subtasks.length>0&&n===r.subtasks.length,o=n>0&&!i;return(0,a.jsxs)("li",{className:`mw-wall${i?" mw-complete":""}`,children:[(0,a.jsxs)("div",{className:"mw-wall-head",children:[(0,a.jsx)(eR,{id:`${r.id}.__block`,checked:i,indeterminate:o,disabled:0===r.subtasks.length,label:`Mark every step in ${r.title} done`,onChange:e=>s(r,e)}),r.code&&(0,a.jsx)("span",{className:"mw-code",children:r.code}),(0,a.jsx)("span",{className:`mw-chip mw-chip-${"decision"===r.kind?"decision":"link"}`,children:"decision"===r.kind?"Decision":"Link"}),(0,a.jsx)("span",{className:"mw-title",children:r.title}),(0,a.jsxs)("span",{className:`mw-prog${i?" mw-prog-done":""}`,children:[n,"/",r.subtasks.length]})]}),r.url&&(0,a.jsxs)("span",{className:"mw-open",children:[(0,a.jsx)("a",{className:"mw-btn",href:r.url,target:"_blank",rel:"noopener noreferrer",children:"Open â"}),(0,a.jsx)(eO,{text:r.url,wide:!0})]}),r.unblocks&&(0,a.jsxs)("p",{className:"mw-unblocks",children:[(0,a.jsx)("span",{className:"mw-unblocks-label",children:"Unblocks"})," ",r.unblocks]}),(0,a.jsx)("ul",{className:"mw-subs",children:r.subtasks.map(s=>{let n=`${r.id}.${s.id}`,i=!!e.checked[n];return(0,a.jsxs)("li",{className:`mw-sub${i?" mw-sub-done":""}`,children:[(0,a.jsx)("input",{id:n,type:"checkbox",className:"mw-check",checked:i,onChange:e=>t(n,e.target.checked)}),(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"mw-sub-label",htmlFor:n,children:s.label}),s.copy&&(0,a.jsx)(eO,{text:s.copy}),s.detail&&(0,a.jsx)(eE,{html:s.detail})]})]},s.id)})}),i&&(0,a.jsx)("p",{className:"mw-cleared",children:"Cleared â an agent can run what this unblocks, then remove it."})]},r.id)})})]})}function eV({onSignIn:e,onSignOut:t,error:s}){return(0,a.jsxs)("main",{className:"mw-root mw-gate",children:[(0,a.jsx)("style",{children:eK}),e&&(0,a.jsx)("button",{className:"mw-btn",onClick:e,children:"Sign in with Google"}),t&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("a",{className:"mw-btn",href:"/ownerportal",children:"â Back to the owner portal"}),(0,a.jsx)("button",{className:"mw-btn mw-btn-quiet",onClick:t,children:"Sign out"})]}),s&&(0,a.jsx)("p",{className:"mw-error",children:s})]})}let eK=` 213.mw-root { 214 /* Factlen brand tokens (packages/config/src/tokens/factlen.tokens.json) â owner 215 directive 2026-09-11: the owner dashboard reads black/white/editorial like the 216 site itself, not a separate purple theme. Color is reserved for the accent 217 (site-accent, red) and real status (green/red), never for chrome or decoration. */ 218 --mw-bg: #ffffff; --mw-surface: #ffffff; --mw-alt: #f6f6f6; --mw-border: #e6e6e6; 219 --mw-text: #161616; --mw-muted: #5a5a5a; --mw-faint: #8a8a8a; 220 --mw-accent: #C8102E; --mw-accent-strong: #a30d26; --mw-accent-soft: #fde2e6; 221 --mw-warn: #92400e; --mw-warn-soft: #fffbeb; --mw-warn-border: #fbbf24; 222 --mw-dec: #404040; --mw-dec-soft: #f6f6f6; --mw-dec-border: #e6e6e6; 223 --mw-done: #1d7a37; --mw-done-soft: #eaf6ec; --mw-err: #C8102E; 224 max-width: 820px; margin: 0 auto; padding: 32px 20px 80px; 225 color: var(--mw-text); background: var(--mw-bg); 226 font-family: -apple-system, "SF Pro Text", ui-sans-serif, system-ui, sans-serif; 227} 228@media (prefers-color-scheme: dark) { 229 .mw-root { 230 --mw-bg: #0d1117; --mw-surface: #161b22; --mw-alt: #161b22; --mw-border: #30363d; 231 --mw-text: #d4d4d4; --mw-muted: #9a9a9a; --mw-faint: #6e7681; 232 --mw-accent: #ea5a72; --mw-accent-strong: #f2879a; --mw-accent-soft: #2a1216; 233 --mw-warn: #fcd34d; --mw-warn-soft: #2a2005; --mw-warn-border: #5b4520; 234 --mw-dec: #c9c9c9; --mw-dec-soft: #161b22; --mw-dec-border: #30363d; 235 --mw-done: #3fb950; --mw-done-soft: #0f1f12; --mw-err: #f85149; 236 } 237} 238.mw-kdp { display: flex; align-items: center; gap: 11px; margin: 0 0 12px; padding: 12px 14px; 239 border-radius: 12px; border: 1px solid var(--mw-border); text-decoration: none; } 240a.mw-kdp { cursor: pointer; } 241a.mw-kdp:hover { border-color: var(--mw-err); } 242a.mw-kdp:focus-visible { outline: 2px solid var(--mw-err); outline-offset: 2px; } 243.mw-kdp-ok { background: var(--mw-done-soft); } 244.mw-kdp-bad { background: var(--mw-accent-soft); } 245.mw-kdp-dot { flex: none; width: 11px; height: 11px; border-radius: 50%; } 246.mw-kdp-dot-ok { background: var(--mw-done); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mw-done) 22%, transparent); } 247.mw-kdp-dot-bad { background: var(--mw-err); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mw-err) 22%, transparent); } 248.mw-kdp-text { display: flex; flex-direction: column; gap: 2px; color: var(--mw-text); } 249.mw-kdp-text strong { font-size: .92rem; } 250.mw-kdp-sub { font-size: .76rem; color: var(--mw-muted); } 251.mw-login { display: flex; flex-direction: column; gap: 8px; margin: 0 0 12px; } 252.mw-login-row { display: flex; align-items: center; gap: 11px; padding: 10px 14px; 253 border-radius: 12px; border: 1px solid var(--mw-border); flex-wrap: wrap; } 254.mw-login-ok { background: var(--mw-done-soft); } 255.mw-login-bad { background: var(--mw-alt); } 256.mw-login-dot { flex: none; width: 9px; height: 9px; border-radius: 50%; } 257.mw-login-dot-ok { background: var(--mw-done); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mw-done) 22%, transparent); } 258.mw-login-dot-bad { background: var(--mw-faint); } 259.mw-login-text { display: flex; flex-direction: column; gap: 1px; color: var(--mw-text); flex: 1 1 160px; } 260.mw-login-text strong { font-size: .88rem; } 261.mw-login-sub { font-size: .74rem; color: var(--mw-muted); } 262.mw-login-btn { flex: none; white-space: nowrap; } 263.mw-switch { display: flex; align-items: center; justify-content: space-between; gap: 12px; 264 flex-wrap: wrap; margin: 0 0 20px; padding: 12px 14px; border-radius: 12px; 265 background: var(--mw-surface); border: 1px solid var(--mw-border); } 266.mw-switch-on { background: var(--mw-warn-soft); border-color: var(--mw-warn-border); } 267.mw-switch-main { display: flex; align-items: flex-start; gap: 10px; cursor: pointer; } 268.mw-switch-main input { margin-top: 3px; width: 17px; height: 17px; accent-color: var(--mw-accent); flex: none; } 269.mw-switch-main strong { display: block; font-size: .92rem; } 270.mw-switch-sub { display: block; font-size: .78rem; color: var(--mw-muted); margin-top: 2px; } 271.mw-switch-on .mw-switch-sub { color: var(--mw-warn); } 272.mw-switch-since { font-size: .74rem; color: var(--mw-warn); white-space: nowrap; } 273.mw-gate { min-height: 60vh; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; } 274.mw-bar { display: flex; justify-content: space-between; align-items: center; gap: 12px; 275 flex-wrap: wrap; padding-bottom: 14px; border-bottom: 1px solid var(--mw-border); margin-bottom: 20px; } 276.mw-bar-title { font-size: .8rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase; color: var(--mw-faint); } 277.mw-who { display: flex; align-items: center; gap: 10px; font-size: .78rem; color: var(--mw-faint); } 278.mw-head { margin-bottom: 20px; } 279.mw-head h1 { font-size: clamp(1.4rem,3vw,1.85rem); font-weight: 800; letter-spacing: -.015em; margin: 0 0 4px; text-wrap: balance; } 280.mw-path { font-family: ui-monospace, monospace; font-size: .76rem; color: var(--mw-faint); margin: 0 0 8px; } 281.mw-lede { color: var(--mw-muted); font-size: .88rem; line-height: 1.6; margin: 0 0 18px; max-width: 62ch; } 282.mw-muted { color: var(--mw-muted); font-size: .9rem; } 283.mw-error { color: var(--mw-err); font-size: .88rem; line-height: 1.55; } 284.mw-btn { display: inline-flex; align-items: center; gap: 6px; font: inherit; font-size: .8rem; font-weight: 700; 285 padding: 7px 13px; border-radius: 8px; border: 1px solid transparent; cursor: pointer; 286 background: var(--mw-accent-soft); color: var(--mw-accent-strong); text-decoration: none; } 287.mw-btn:hover { border-color: var(--mw-accent); } 288.mw-btn:focus-visible { outline: 2px solid var(--mw-accent); outline-offset: 2px; } 289.mw-btn-quiet { background: transparent; color: var(--mw-faint); border-color: var(--mw-border); } 290.mw-open { display: inline-flex; align-items: center; gap: 7px; margin-top: 9px; } 291.mw-copy { display: inline-flex; align-items: center; justify-content: center; gap: 5px; 292 vertical-align: middle; margin-left: 6px; padding: 0 5px; min-width: 22px; height: 22px; 293 font: inherit; font-size: .7rem; font-weight: 700; line-height: 1; cursor: pointer;
294 color: var(--mw-faint); background: var(--mw-surface); 295 border: 1px solid var(--mw-border); border-radius: 6px; } 296.mw-copy-wide { margin-left: 0; padding: 0 9px; height: 26px; } 297.mw-copy:hover { color: var(--mw-accent-strong); border-color: var(--mw-accent); } 298.mw-copy:focus-visible { outline: 2px solid var(--mw-accent); outline-offset: 2px; } 299.mw-copy-done { color: var(--mw-done); border-color: var(--mw-done); } 300.mw-copy-failed { color: var(--mw-err); border-color: var(--mw-err); } 301.mw-copy-glyph { display: inline-flex; } 302.mw-group { margin: 0 0 26px; } 303.mw-group-head { display: flex; align-items: baseline; gap: 8px; margin: 0 0 10px; 304 font-size: .8rem; font-weight: 800; letter-spacing: .07em; text-transform: uppercase; 305 color: var(--mw-faint); } 306.mw-group-count { font-size: .72rem; font-weight: 700; letter-spacing: .04em; 307 color: var(--mw-muted); } 308.mw-tiles { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; 309 grid-template-columns: repeat(auto-fill, minmax(228px, 1fr)); } 310.mw-tile { width: 100%; text-align: left; font: inherit; cursor: pointer; 311 display: flex; flex-direction: column; gap: 6px; 312 background: var(--mw-surface); border: 1px solid var(--mw-border); border-radius: 12px; padding: 14px 15px; } 313.mw-tile:hover { border-color: var(--mw-accent); } 314.mw-tile:focus-visible { outline: 2px solid var(--mw-accent); outline-offset: 2px; } 315.mw-tile-clear { opacity: .62; } 316.mw-tile-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; } 317.mw-tile-name { font-size: .95rem; font-weight: 800; letter-spacing: -.01em; flex: 1 1 auto; } 318.mw-tile-note { font-size: .62rem; font-weight: 800; text-transform: uppercase; letter-spacing: .07em; 319 color: var(--mw-accent-strong); background: var(--mw-accent-soft); 320 border: 1px solid var(--mw-accent); border-radius: 20px; padding: 2px 8px; } 321.mw-badge { font-family: ui-monospace, monospace; font-size: .72rem; font-weight: 700; color: #fff; 322 background: var(--mw-accent); border-radius: 20px; padding: 2px 9px; min-width: 26px; text-align: center; } 323.mw-badge-clear { background: transparent; color: var(--mw-done); border: 1px solid var(--mw-done); } 324.mw-tile-path { font-family: ui-monospace, monospace; font-size: .7rem; color: var(--mw-faint); overflow-wrap: anywhere; } 325.mw-tile-meta { font-size: .76rem; color: var(--mw-muted); } 326.mw-track { height: 4px; border-radius: 4px; background: var(--mw-alt); overflow: hidden; } 327.mw-fill { display: block; height: 100%; background: var(--mw-accent); border-radius: 4px; } 328.mw-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 11px; } 329.mw-wall { background: var(--mw-surface); border: 1px solid var(--mw-border); border-radius: 12px; padding: 14px 16px 15px; } 330.mw-complete { opacity: .6; } 331.mw-wall-head { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; } 332.mw-code { font-family: ui-monospace, monospace; font-size: .74rem; font-weight: 700; color: #fff; 333 background: var(--mw-accent); border-radius: 6px; padding: 2px 7px; } 334.mw-chip { font-size: .64rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; 335 padding: 2px 7px; border-radius: 20px; border: 1px solid transparent; } 336.mw-chip-link { color: var(--mw-warn); background: var(--mw-warn-soft); border-color: var(--mw-warn-border); } 337.mw-chip-decision { color: var(--mw-dec); background: var(--mw-dec-soft); border-color: var(--mw-dec-border); } 338.mw-title { font-size: .95rem; font-weight: 700; } 339.mw-complete .mw-title { text-decoration: line-through; } 340.mw-prog { margin-left: auto; font-family: ui-monospace, monospace; font-size: .7rem; font-weight: 700; 341 color: var(--mw-faint); background: var(--mw-alt); border: 1px solid var(--mw-border); border-radius: 20px; padding: 2px 8px; } 342.mw-prog-done { color: var(--mw-done); border-color: var(--mw-done); } 343.mw-unblocks { margin: 9px 0 0; font-size: .78rem; color: var(--mw-muted); line-height: 1.5; 344 padding-left: 10px; border-left: 2px solid var(--mw-accent-soft); } 345.mw-unblocks-label { font-size: .64rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; 346 color: var(--mw-accent-strong); margin-right: 4px; } 347.mw-subs { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-direction: column; gap: 7px; } 348.mw-sub { display: grid; grid-template-columns: 19px 1fr; gap: 10px; background: var(--mw-alt); 349 border: 1px solid var(--mw-border); border-radius: 8px; padding: 8px 10px; } 350.mw-sub-done { opacity: .55; } 351.mw-sub-done .mw-sub-label { text-decoration: line-through; } 352.mw-check { width: 17px; height: 17px;
352 margin-top: 1px; accent-color: var(--mw-accent); cursor: pointer; } 353.mw-check:focus-visible { outline: 2px solid var(--mw-accent); outline-offset: 2px; } 354.mw-check-block { margin-top: 0; flex-shrink: 0; } 355.mw-check-block:disabled { cursor: default; opacity: .35; } 356.mw-sub-label { font-size: .84rem; font-weight: 700; cursor: pointer; } 357.mw-sub-detail { margin-top: 2px; font-size: .8rem; color: var(--mw-muted); line-height: 1.5; overflow-wrap: anywhere; } 358.mw-sub-detail code { font-family: ui-monospace, monospace; font-size: .95em; 359 background: var(--mw-surface); border: 1px solid var(--mw-border); border-radius: 4px; padding: 0 4px; } 360.mw-sub-detail a { color: var(--mw-accent-strong); } 361.mw-cleared { margin: 11px 0 0; font-size: .78rem; font-weight: 700; color: var(--mw-done); } 362.mw-fb { background: var(--mw-surface); border: 1px solid var(--mw-border); border-radius: 12px; 363 padding: 13px 15px 14px; margin-bottom: 16px; } 364.mw-fb-head { display: flex; align-items: center; gap: 9px; } 365.mw-fb-title { font-size: .8rem; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--mw-faint); } 366.mw-fb-flag { font-size: .62rem; font-weight: 800; text-transform: uppercase; letter-spacing: .07em; 367 color: var(--mw-accent-strong); background: var(--mw-accent-soft); 368 border: 1px solid var(--mw-accent); border-radius: 20px; padding: 2px 8px; } 369.mw-fb-hint { margin: 6px 0 9px; font-size: .78rem; color: var(--mw-muted); line-height: 1.5; max-width: 62ch; } 370.mw-fb-text { display: block; width: 100%; box-sizing: border-box; resize: vertical; 371 font: inherit; font-size: .85rem; line-height: 1.55; color: var(--mw-text); 372 background: var(--mw-alt); border: 1px solid var(--mw-border); border-radius: 8px; padding: 9px 11px; } 373.mw-fb-text:focus-visible { outline: 2px solid var(--mw-accent); outline-offset: 1px; } 374.mw-fb-foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 9px; } 375.mw-fb-foot .mw-btn:disabled { opacity: .5; cursor: default; } 376.mw-fb-state { font-size: .74rem; color: var(--mw-faint); } 377.mw-fb-last { margin: 9px 0 0; font-size: .78rem; color: var(--mw-muted); line-height: 1.5; 378 padding-left: 10px; border-left: 2px solid var(--mw-accent-soft); } 379.mw-fb-last-label { font-size: .64rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; 380 color: var(--mw-accent-strong); margin-right: 4px; } 381`;var ew=ew,ej=ew;function eQ(){let e=(0,ei.getAuth)((0,eu.getFirebaseApp)());return e.tenantId="owner-portal-w358j",e}let eJ={general_instrumental:"General · Instrumental",iran:"Iran"},eZ=["Heartbroken","Joyful"];function eX(){let[e,t]=(0,r.useState)(null),[s,n]=(0,r.useState)(!1),[i,o]=(0,r.useState)({kids:[],cinema:[],proposed:[],dual
381:[],video:[],videoKids:[]}),[l,c]=(0,r.useState)(!1),[d,p]=(0,r.useState)(null),[h,m]=(0,r.useState)("image"),u=/permission|insufficient/i.test(d??"");(0,r.useEffect)(()=>(0,eo.onAuthStateChanged)(eQ(),e=>{t(e),n(!0)}),[]),(0,r.useEffect)(()=>{if(!e){o({kids:[],cinema:[],proposed:[],dual:[],video:[],videoKids:[]}),c(!1);return}return(0,eN.onSnapshot)((0,ej.aO)((0,ew.aX)((0,eu.getFirebaseApp)()),"ops_manual_walls"),e=>{let a=e.docs.find(e=>"_art_kids"===e.id),t=e.docs.find(e=>"_art_cinema"===e.id),s=e.docs.find(e=>"_art_proposed"===e.id),r=e.docs.find(e=>"_art_dual"===e.id),n=e.docs.find(e=>"_art_video"===e.id),i=e.docs.find(e=>"_art_video_kids"===e.id);o({kids:a?.data()?.items??[],cinema:t?.data()?.items??[],proposed:s?.data()?.items??[],dual:r?.data()?.items??[],video:n?.data()?.items??[],videoKids:i?.data()?.items??[]}),c(!0),p(null)},e=>{console.error("Art: Firestore subscription failed",e),p(e.message??String(e)),c(!0)})},[e]);let f=()=>(0,em.signOut)(eQ());return s?e?u?(0,a.jsx)(e6,{onSignOut:f,error:"Not authorized for this page."}):l?(0,a.jsxs)("main",{className:"art-root",children:[(0,a.jsx)("style",{children:e5}),(0,a.jsx)(eS.OwnerNav,{active:"art"}),(0,a.jsxs)("header",{className:"art-head",children:[(0,a.jsx)("p",{className:"art-eyebrow",children:"music-media-generator · visualizer engine"}),(0,a.jsx)("h1",{children:"Visual Style Catalog"}),(0,a.jsx)("p",{className:"art-sub",children:"Real reference images per Art Director style, rendered through the pipelineâs own Gemini engine. The shared cinematic catalog shows each style twice â a dark, heartbroken song on the left, a bright, happy song on the right â since the same style has to serve both moods across real songs."}),(0,a.jsx)("button",{className:"art-btn art-btn-quiet",onClick:f,children:"Sign out"})]}),d&&(0,a.jsx)("p",{className:"art-error",children:d}),(0,a.jsxs)("div",{className:"art-media-picker",children:[(0,a.jsx)("label",{htmlFor:"art-media-select",children:"Menu"}),(0,a.jsxs)("select",{id:"art-media-select",className:"art-select",value:h,onChange:e=>m(e.target.value),children:[(0,a.jsxs)("option",{value:"image",children:["Image (",i.kids.length+i.cinema.length+i.proposed.length+i.dual.length," styles)"]}),(0,a.jsxs)("option",{value:"video",children:["Video (",i.video.length+i.videoKids.length," styles)"]})]})]}),"image"===h&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(e1,{items:i.dual}),(0,a.jsx)(e2,{title:"Kids â Storybook Illustration",tag:"kids",items:i.kids}),(0,a.jsx)(e2,{title:"General · Iran · Instrumental â Shared Cinematic Catalog",tag:"cinema",items:i.cinema}),(0,a.jsx)(e2,{title:"Proposed â Not Yet Live",tag:"proposed",items:i.proposed,note:"Preview only â these aren't in the pipeline's real style menus yet. The Art Director can't pick any of these for an actual song until they're added to the code."})]}),"video"===h&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(e2,{title:"Video â Recognizable Video Styles",tag:"video",items:i.video,note:"The official-video Director's own dial: one camera/grade/stock identity per video, dealt fresh per render. Mood, energy, wardrobe, and every other creative axis stay independent of this list."}),(0,a.jsx)(e2,{title:"Kids â Video Styles (Preview Only)",tag:"proposed",items:i.videoKids,note:"Preview only â a comprehensive draft catalog, not yet wired into the kids official-video pipeline (which currently runs its own mascot/character system). Nothing here is live until that decision is made."})]})]}):(0,a.jsx)("main",{className:"art-root"}):(0,a.jsx)(e6,{onSignIn:()=>{p(null),(0,el.signInWithPopup)(eQ(),new ec.GoogleAuthProvider).catch(e=>p(e?.message??String(e)))}}):(0,a.jsx)("main",{className:"art-root"})}let e0=[{mode:"Photographic",mood:"Heartbroken"},{mode:"Photographic",mood:"Joyful"},{mode:"Painterly",mood:"Heartbroken"},{mode:"Painterly",mood:"Joyful"}];function e1({items:e}){return e.length?(0,a.jsxs)("section",{className:"art-board",children:[(0,a.jsxs)("div",{className:"art-board-head",children:[(0,a.jsxs)("span",{className:"art-tag art-tag-dual",children:[e.length," styles"]}),(0,a.jsx)("h2",{className:"art-board-title art-board-title-dual",children:"Dual-Mode â Both Photographic & Painterly"})]}),(0,a.jsx)("p",{className:"art-board-note",children:"Genuinely recognized in both forms by real photographers and painters (e.g. Baroque chiaroscuro in a Caravaggio painting and in film-noir photography) â not a forced pairing. Each shows all 4: photographic heartbroken/joyful, painterly heartbroken/joyful."}),(0,a.jsx)("div",{className:"art-grid-dual",children:e.map(e=>(0,a.jsxs)("figure",{className:"art-card art-card-dual",children:[(0,a.jsx)("div",{className:"art-img-wrap-dual",children:e.urls.map((t,s)=>(0,a.jsxs)("div",{className:"art-img-cell",children:[(0,a.jsx)("img",{src:t,alt:`${e.name} â ${e0[s]?.mode} ${e0[s]?.mood}`,loading:"lazy"}),e0[s]&&(0,a.jsxs)("span",{className:"art-mood-label",children:[e0[s].mode," · ",e0[s].mood]})]},t))}),(0,a.jsxs)("figcaption",{children:[(0,a.jsx)("h3",{className:"art-card-title art-card-title-dual",children:e.name}),(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"Photo:"})," ",e.description_photo]}),(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"Painterly:"})," ",e.description_painterly]}),e.badges&&(0,a.jsxs)("div",{className:"art-badges",children:[(0,a.jsxs)("span",{className:"art-badge",children:["G·",String(e.badges.general).padStart(2,"0")]}),(0,a.jsxs)("span",{className:"art-badge",children:["IR·",String(e.badges.iran).padStart(2,"0")]}),(0,a.jsxs)("span",{className:"art-badge",children:["IN·",String(e.badges.instrumental).padStart(2,"0")]})]}),e.pool&&(0,a.jsx)("div",{className:"art-badges",children:(0,a.jsx)("span",{className:"art-badge",children:eJ[e.pool]??e.pool})})]})]},e.name))})]}):null}function e2({title:e,tag:t,items:s,note:r}){return s.length?(0,a.jsxs)("section",{className:"art-board",children:[(0,a.jsxs)("div",{className:"art-board-head",children:[(0,a.jsxs)("span",{className:`art-tag art-tag-${t}`,children:[s.length," styles"]}),(0,a.jsx)("h2",{className:`art-board-title art-board-title-${t}`,children:e})]}),r&&(0,a.jsx)("p",{className:"art-board-note",children:r}),(0,a.jsx)("div",{className:`art-grid art-grid-${t}`,children:s.map(e=>(0,a.jsxs)("figure",{className:"art-card",children:[(0,a.jsx)("div",{className:`art-img-wrap ${e.urls.length>1?"art-img-wrap-multi":""}`,children:e.urls.length?e.urls.map((t,s)=>(0,a.jsxs)("div",{className:"art-img-cell",children:[(0,a.jsx)("img",{src:t,alt:`${e.name} â ${eZ[s]??`sample ${s+1}`}`,loading:"lazy"}),e.urls.length>1&&(0,a.jsx)("span",{className:"art-mood-label",children:eZ[s]})]},t)):(0,a.jsx)("div",{className:"art-img-missing",children:"pending"})}),(0,a.jsxs)("figcaption",{children:[(0,a.jsx)("h3",{className:`art-card-title art-card-title-${t}`,children:e.name}),e.description&&(0,a.jsx)("p",{children:e.description}),e.badges&&(0,a.jsxs)("div",{className:"art-badges",children:[(0,a.jsxs)("span",{className:"art-badge",children:["G·",String(e.badges.general).padStart(2,"0")]}),(0,a.jsxs)("span",{className:"art-badge",children:["IR·",String(e.badges.iran).padStart(2,"0")]}),(0,a.jsxs)("span",{className:"art-badge",children:["IN·",String(e.badges.instrumental).padStart(2,"0")]})]}),e.pool&&(0,a.jsx)("div",{className:"art-badges",children:(0,a.jsx)("span",{className:"art-badge",children:eJ[e.pool]??e.pool})})]})]},e.name))})]}):null}function e6({onSignIn:e,onSignOut:t,error:s}){return(0,a.jsxs)("main",{className:"art-root art-gate",children:[(0,a.jsx)("style",{children:e5}),e&&(0,a.jsx)("button",{className:"art-btn",onClick:e,children:"Sign in with Google"}),t&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("a",{className:"art-btn",href:"/ownerportal",children:"â Back to the owner portal"}),(0,a.jsx)("button",{className:"art-btn art-btn-quiet",onClick:t,children:"Sign out"})]}),s&&(0,a.jsx)("p",{className:"art-error",children:s})]})}let e5=` 382.art-root { 383 /* Factlen tokens â owner directive 2026-09-11: black/white/red for chrome. 384 --art-kids/cinema/proposed/dual untouched â those distinguish real content
385 categories, not decoration. */ 386 --art-bg: #ffffff; --art-surface: #fff; --art-alt: #f6f6f6; --art-border: #e6e6e6; 387 --art-text: #161616; --art-muted: #5a5a5a; --art-faint: #8a8a8a; 388 --art-kids: #b8523a; --art-kids-soft: #f3ddd3; 389 --art-cinema: #1f6e64; --art-cinema-soft: #d8ece7; 390 --art-proposed: #8a5cb8; --art-proposed-soft: #e6dcf3; 391 --art-dual: #b8622f; --art-dual-soft: #f5e2d3; 392 --art-video: #2d5fa8; --art-video-soft: #dbe6f5; 393 --art-err: #C8102E; 394 max-width: 1180px; margin: 0 auto; padding: 32px 20px 100px; 395 color: var(--art-text); background: var(--art-bg); 396 font-family: -apple-system, "SF Pro Text", ui-sans-serif, system-ui, sans-serif; 397} 398@media (prefers-color-scheme: dark) { 399 .art-root { 400 --art-bg: #0d1117; --art-surface: #161b22; --art-alt: #161b22; --art-border: #30363d; 401 --art-text: #d4d4d4; --art-muted: #9a9a9a; --art-faint: #6e7681; 402 --art-kids: #e8896c; --art-kids-soft: #3d281f; 403 --art-cinema: #5fc9bd; --art-cinema-soft: #17332e; 404 --art-proposed: #bb9ce0; --art-proposed-soft: #2e2340; 405 --art-dual: #e29a63; --art-dual-soft: #3d2c1e; 406 --art-video: #7ba7e3; --art-video-soft: #1c2b40; 407 --art-err: #f85149; 408 } 409} 410.art-gate { display: flex; flex-direction: column; align-items: center; justify-content: center; 411 min-height: 60vh; gap: 12px; } 412.art-btn { font: inherit; font-size: .86rem; font-weight: 700; padding: 10px 18px; border-radius: 10px; 413 border: 1px solid var(--art-border); background: var(--art-surface); color: var(--art-text); cursor: pointer; } 414.art-btn-quiet { background: transparent; font-weight: 500; color: var(--art-muted); } 415.art-error { color: var(--art-err); font-size: .82rem; } 416 417.art-head { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; margin-bottom: 28px; position: relative; } 418.art-eyebrow { font-size: .74rem; letter-spacing: .08em; text-transform: uppercase; color: var(--art-faint); margin: 0; } 419.art-head h1 { font-size: 1.7rem; margin: 2px 0 0; letter-spacing: -.01em; } 420.art-sub { color: var(--art-muted); font-size: .92rem; margin: 4px 0 0; max-width: 34rem; } 421.art-head .art-btn { position: absolute; right: 0; top: 0; } 422 423.art-board { margin-bottom: 40px; } 424.art-board-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 4px; } 425.art-tag { font-size: .7rem; font-weight: 700; padding: 3px 9px; border-radius: 20px; white-space: nowrap; } 426.art-tag-kids { background: var(--art-kids-soft); color: var(--art-kids); } 427.art-tag-cinema { background: var(--art-cinema-soft); color: var(--art-cinema); } 428.art-tag-proposed { background: var(--art-proposed-soft); color: var(--art-proposed); } 429.art-tag-dual { background: var(--art-dual-soft); color: var(--art-dual); } 430.art-tag-video { background: var(--art-video-soft); color: var(--art-video); } 431.art-board-title { font-size: 1.05rem; font-weight: 700; margin: 0; } 432.art-board-title-kids { color: var(--art-kids); } 433.art-board-title-cinema { color: var(--art-cinema); } 434.art-board-title-proposed { color: var(--art-proposed); } 435.art-board-title-dual { color: var(--art-dual); } 436.art-board-title-video { color: var(--art-video); } 437.art-board-note { color: var(--art-muted); font-size: .8rem; margin: 6px 0 0; max-width: 40rem; } 438 439.art-media-picker { display: flex; align-items: center; gap: 8px; margin-bottom: 24px; } 440.art-media-picker label { font-size: .74rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; 441 color: var(--art-faint); } 442.art-select { font: inherit; font-size: .84rem; font-weight: 600; padding: 7px 12px; border-radius: 8px; 443 border: 1px solid var(--art-border); background: var(--art-surface); color: var(--art-text); cursor: pointer; } 444 445.art-grid { display: grid; gap: 14px; margin-top: 14px; } 446.art-grid-kids { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); } 447.art-grid-cinema { grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); } 448.art-grid-proposed { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); margin-top: 12px; } 449.art-grid-video { grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); margin-top: 12px; } 450.art-grid-dual { display: grid; gap: 18px; margin-top: 14px; 451 grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); } 452.art-card-dual { border-color: var(--art-dual); border-width: 1.5px; } 453.art-img-wrap-dual { display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; 454 aspect-ratio: 1 / 1; background: var(--art-alt); } 455.art-img-wrap-dual .art-img-cell:nth-child(1), .art-img-wrap-dual .art-img-cell:nth-child(2) { 456 border-bottom: 1px solid var(--art-surface); } 457.art-img-wrap-dual .art-img-cell:nth-child(1), .art-img-wrap-dual .art-img-cell:nth-child(3) { 458 border-right: 1px solid var(--art-surface); } 459.art-img-wrap-dual .art-mood-label { font-size: .58rem; padding: 1px 5px; } 460 461.art-card { margin: 0; background: var(--art-surface); border: 1px solid var(--art-border); 462 border-radius: 12px; overflow: hidden; display: flex; flex-direction: column; } 463.art-img-wrap { aspect-ratio: 1 / 1; background: var(--art-alt); display: flex; } 464.art-img-cell { position: relative; flex: 1; min-width: 0; } 465.art-img-wrap img { width: 100%; height: 100%; object-fit: cover; display: block; } 466.art-img-wrap-multi .art-img-cell:not(:last-child) { border-right: 1px solid var(--art-surface); } 467.art-mood-label { position: absolute; left: 6px; bottom: 6px; font-size: .64rem; font-weight: 700; 468 letter-spacing: .02em; padding: 2px 7px; border-radius: 20px; background: rgba(0,0,0,.55); color: #fff; } 469.art-img-missing { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; 470 color: var(--art-faint); font-size: .75rem; } 471.art-card figcaption { padding: 10px 12px 12px; } 472.art-card-title { font-size: .86rem; font-weight: 700; margin: 0 0 3px; line-height: 1.2; } 473.art-card-title-kids { color: var(--art-kids); } 474.art-card-title-cinema { color: var(--art-cinema); } 475.art-card-title-proposed { color: var(--art-proposed); } 476.art-card-title-dual { color: var(--art-dual); } 477.art-card-title-video { color: var(--art-video); } 478.art-card p { font-size: .76rem; color: var(--art-muted); margin: 0; line-height: 1.35; } 479.art-badges { display: flex; gap: 5px; margin-top: 7px; flex-wrap: wrap; } 480.art-badge { font-size: .68rem; font-family: ui-monospace, "SF Mono", monospace; border: 1px solid var(--art-border); 481 border-radius: 4px; padding: 1px 6px; color: var(--art-muted); } 482`;var ew=ew,ej=ew;function e8(){let e=(0,ei.getAuth)((0,eu.getFirebaseApp)());return e.tenantId="owner-portal-w358j",e}let e4=["loop","teaser","short"],e3={before:"Before",progress_1:"Progress 1",progress_2:"Progress 2",after:"After",halfway:"Halfway",final:"Final"},e7=["8s","16s","24s"],e9={"8s":"8s · 1 act","16s":"15s · 2 acts (8s + 7s extension)","24s":"22s · 3 acts (8s + 7s + 7s extension)"},ae={oceanfront_builders:"Oceanfront Builders",ava:"Ava",echovira_cinema:"Echovira Cinema",dream_feed:"Dream Feed",bubblemon:"Bubblemon",eghtesad_online:"Eghtesad Online",fenul_capital_investment:"Fenul
482Capital Investment",fenul_wealth_management:"Fenul Wealth Management",foreverfit_daily:"ForeverFit Daily",funbook:"Funbook",koodakaneh:"Koodakaneh",science_of_mind:"Science of Mind",short_travel_videos:"Short Travel Videos",simply_life_and_technology:"Simply Life and Technology",the_football_insight_hub:"The Football Insight Hub",factlen:"Factlen",koohestun:"Koohestun"};function aa(){let[e,t]=(0,r.useState)(null),[s,n]=(0,r.useState)(!1),[i,o]=(0,r.useState)([]),[l,c]=(0,r.useState)(!1),[d,p]=(0,r.useState)(null),[h,m]=(0,r.useState)(null),u=/permission|insufficient/i.test(d??"");(0,r.useEffect)(()=>(0,eo.onAuthStateChanged)(e8(),e=>{t(e),n(!0)}),[]),(0,r.useEffect)(()=>{if(!e){o([]),c(!1);return}return(0,eN.onSnapshot)((0,ej.aO)((0,ew.aX)((0,eu.getFirebaseApp)()),"ops_manual_walls"),e=>{o(e.docs.filter(e=>e.id.startsWith("_business_media__")).map(e=>e.data())),c(!0),p(null)},e=>{console.error("BusinessMedia: Firestore subscription failed",e),p(e.message??String(e)),c(!0)})},[e]);let f=()=>(0,em.signOut)(e8());if(!s)return(0,a.jsx)("main",{className:"bm-root"});if(!e)return(0,a.jsx)(ac,{onSignIn:()=>{p(null),(0,el.signInWithPopup)(e8(),new ec.GoogleAuthProvider).catch(e=>p(e?.message??String(e)))}});if(u)return(0,a.jsx)(ac,{onSignOut:f,error:"Not authorized for this page."});if(!l)return(0,a.jsx)("main",{className:"bm-root"});let g=new Map;for(let e of i){let a=g.get(e.band_id)??[];a.push(e),g.set(e.band_id,a)}for(let e of g.values())e.sort((e,a)=>e.chapter.localeCompare(a.chapter));let x=[...g.keys()].sort((e,a)=>(ae[e]??e).localeCompare(ae[a]??a)),b=h&&g.has(h)?h:x[0]??null,y=b?g.get(b)??[]:[];return(0,a.jsxs)("main",{className:"bm-root",children:[(0,a.jsx)("style",{children:ad}),(0,a.jsx)(eS.OwnerNav,{active:"businessmedia"}),(0,a.jsxs)("header",{className:"bm-head",children:[(0,a.jsx)("p",{className:"bm-eyebrow",children:"business-video-generator · before â process â after"}),(0,a.jsx)("h1",{children:"Business Media QC"}),(0,a.jsx)("p",{className:"bm-sub",children:"Real keyframes per business, chapter and triplet length, rendered through the pipelineâs own Gemini engine and exact prompts â images only, no video generated for this page."}),(0,a.jsx)("button",{className:"bm-btn bm-btn-quiet",onClick:f,children:"Sign out"})]}),d&&(0,a.jsx)("p",{className:"bm-error",children:d}),!i.length&&!d&&(0,a.jsx)("p",{className:"bm-empty",children:"Nothing published yet."}),x.length>0&&(0,a.jsxs)("div",{className:"bm-band-picker",children:[(0,a.jsx)("label",{htmlFor:"bm-band-select",children:"Business"}),(0,a.jsx)("select",{id:"bm-band-select",value:b??"",onChange:e=>m(e.target.value),children:x.map(e=>(0,a.jsxs)("option",{value:e,children:[ae[e]??e," (",g.get(e).length,")"]},e))})]}),b&&(0,a.jsx)("section",{className:"bm-band",children:y.map(e=>(0,a.jsx)(at,{entry:e},`${e.band_id}__${e.chapter}`))})]})}function at({entry:e}){if("film"===e.mode){let t=[...e.cuts].sort((e,a)=>e7.indexOf(e.cut)-e7.indexOf(a.cut));return(0,a.jsxs)("section",{className:"bm-chapter",children:[(0,a.jsxs)("div",{className:"bm-chapter-head",children:[(0,a.jsxs)("h3",{children:[e.chapter," ",(0,a.jsxs)("span",{className:"bm-chapter-sub",children:["· ",e.subchapter]})]}),e.generated_ts&&(0,a.jsxs)("span",{className:"bm-ts",children:["generated ",e.generated_ts]})]}),t.map(e=>(0,a.jsx)(an,{cut:e},e.cut))]})}if("reel"===e.mode){let t=new Set(e.reels.map(e=>e.category).filter(Boolean));return(0,a.jsxs)("section",{className:"bm-chapter",children:[(0,a.jsxs)("div",{className:"bm-chapter-head",children:[(0,a.jsx)("h3",{children:e.chapter}),e.generated_ts&&(0,a.jsxs)("span",{className:"bm-ts",children:["generated ",e.generated_ts]})]}),(0,a.jsx)(ao,{reels:e.reels}),e.roster&&e.roster.length>0&&(0,a.jsx)(al,{roster:e.roster,sampledCategories:t})]})}if("art"===e.mode){let t=[...e.cuts].sort((e,a)=>e4.indexOf(e.cut)-e4.indexOf(a.cut));return(0,a.jsxs)("section",{className:"bm-chapter",children:[(0,a.jsxs)("div",{className:"bm-chapter-head",children:[(0,a.jsxs)("h3",{children:[e.chapter," ",(0,a.jsxs)("span",{className:"bm-chapter-sub",children:["· ",e.subchapter]})]}),e.generated_ts&&(0,a.jsxs)("span",{className:"bm-ts",children:["generated ",e.generated_ts]})]}),t.map(e=>(0,a.jsx)(ai,{cut:e},e.cut))]})}let t=[...e.cuts].sort((e,a)=>e4.indexOf(e.cut)-e4.indexOf(a.cut));return(0,a.jsxs)("section",{className:"bm-chapter",children:[(0,a.jsxs)("div",{className:"bm-chapter-head",children:[(0,a.jsxs)("h3",{children:[e.chapter,e.subchapter&&(0,a.jsxs)("span",{className:"bm-chapter-sub",children:["· ",e.subchapter]})]}),e.generated_ts&&(0,a.jsxs)("span",{className:"bm-ts",children:["generated ",e.generated_ts]})]}),t.map(e=>(0,a.jsx)(ar,{cut:e},e.cut))]})}function as({label:e,text:t,mono:s}){return(0,a.jsxs)("details",{className:"bm-field",children:[(0,a.jsx)("summary",{children:e}),s?(0,a.jsx)("pre",{children:t}):(0,a.jsx)("p",{children:t})]})}function ar({cut:e}){let t=e.keyframes.map(e=>e3[e.stage]??e.stage).join(" â "),s=[e.format&&`Format: ${e.format}`,e.visual_form&&`Visual form: ${e.visual_form}`,e.chart_surface&&`Surface: ${e.chart_surface}
482`].filter(Boolean);return(0,a.jsxs)("article",{className:"bm-cut",children:[(0,a.jsxs)("div",{className:"bm-cut-head",children:[(0,a.jsxs)("span",{className:"bm-cut-tag",children:[e.duration_seconds,"s · ",t]}),(0,a.jsx)("h4",{children:e.headline})]}),e.subchapter&&(0,a.jsxs)("p",{className:"bm-cut-meta",children:[(0,a.jsx)("strong",{children:"Subchapter:"})," ",e.subchapter]}),s.length>0&&(0,a.jsx)("p",{className:"bm-cut-meta",children:s.join(" · ")}),(0,a.jsxs)("div",{className:"bm-cut-fields",children:[e.scene_lock&&(0,a.jsx)(as,{label:"Scene lock",text:e.scene_lock}),e.cinematography&&(0,a.jsx)(as,{label:"Cinematography",text:e.cinematography}),e.lesson&&(0,a.jsx)(as,{label:"What the viewer learns",text:e.lesson}),e.narration&&(0,a.jsx)(as,{label:"Narration",text:e.narration})]}),(0,a.jsx)("div",{className:"bm-frames",children:e.keyframes.map(t=>(0,a.jsxs)("figure",{className:"bm-frame",children:[(0,a.jsxs)("div",{className:"bm-frame-img",children:[(0,a.jsx)("img",{src:t.url,alt:`${e.headline} â ${e3[t.stage]??t.stage}`,loading:"lazy"}),(0,a.jsx)("span",{className:"bm-frame-label",children:e3[t.stage]??t.stage})]}),(0,a.jsxs)("figcaption",{children:[(0,a.jsx)(as,{label:"Content",text:t.content}),t.transition_in&&(0,a.jsx)(as,{label:"Motion",text:t.transition_in}),(0,a.jsx)(as,{label:"Image prompt",text:t.prompt,mono:!0}),t.veo_prompt&&(0,a.jsx)(as,{label:"Veo prompt",text:t.veo_prompt,mono:!0})]})]},t.stage))})]})}function an({cut:e}){return(0,a.jsxs)("article",{className:"bm-cut",children:[(0,a.jsxs)("div",{className:"bm-cut-head",children:[(0,a.jsx)("span",{className:"bm-cut-tag",children:e9[e.cut]??e.cut}),(0,a.jsx)("h4",{children:e.headline})]}),(0,a.jsxs)("p",{className:"bm-cut-meta",children:[(0,a.jsx)("strong",{children:"Continuity:"})," ","cut_to_new_act"===e.continuity_mode?"hard cut between acts":"continuous â later acts resume from the previous act's rendered clip"," · ",(0,a.jsx)("strong",{children:"Resolution:"})," ",e.acts.length>1?"720p (Veo extension is 720p-only)":"1080p"]}),(0,a.jsxs)("div",{className:"bm-cut-fields",children:[(0,a.jsx)(as,{label:"Premise",text:e.premise}),(0,a.jsx)(as,{label:"Cast lock",text:e.cast_lock}),(0,a.jsx)(as,{label:"World lock",text:e.world_lock}),(0,a.jsx)(as,{label:"Director lens",text:e.director_lens})]}),(0,a.jsx)("div",{className:"bm-frames",children:e.acts.map(e=>(0,a.jsxs)("figure",{className:"bm-frame",children:[(0,a.jsxs)("div",{className:"bm-frame-img bm-frame-unpreviewable",children:[(0,a.jsxs)("span",{className:"bm-frame-label",children:["Act ",e.act]}),(0,a.jsx)("p",{children:"Direct video mode â no image, Veo renders from the prompt below."}),e.veo_voice_wanted&&(0,a.jsxs)("span",{className:"bm-frame-predicted-badge",children:["Veo voice: â",e.veo_voice_line,"â"]})]}),(0,a.jsx)("figcaption",{children:(0,a.jsx)(as,{label:"Veo prompt",text:e.video_prompt,mono:!0})})]},e.act))})]})}function ai({cut:e}){return(0,a.jsxs)("article",{className:"bm-cut",children:[(0,a.jsxs)("div",{className:"bm-cut-head",children:[(0,a.jsxs)("span",{className:"bm-cut-tag",children:[e.duration_seconds,"s · ",e.frames.length," frame",1===e.frames.length?"":"s",", loops to frame A"]}),(0,a.jsx)("h4",{children:e.headline})]}),(0,a.jsxs)("div",{className:"bm-cut-fields",children:[(0,a.jsx)(as,{label:"Concept",text:e.concept}),(0,a.jsx)(as,{label:"World lock",text:e.world_lock}),(0,a.jsx)(as,{label:"Morph engine",text:e.morph_engine})]}),(0,a.jsx)("div",{className:"bm-frames",children:e.frames.map(t=>(0,a.jsxs)("figure",{className:"bm-frame",children:[(0,a.jsxs)("div",{className:"bm-frame-img",children:[(0,a.jsx)("img",{src:t.url,alt:`${e.headline} â frame ${t.frame}`,loading:"lazy"}),(0,a.jsxs)("span",{className:"bm-frame-label",children:["Frame ",String.fromCharCode(65+t.frame)]})]}),(0,a.jsxs)("figcaption",{children:[(0,a.jsx)(as,{label:"Content",text:t.content}),t.prompt&&(0,a.jsx)(as,{label:"Image prompt",text:t.prompt,mono:!0})]})]},t.frame))}),(0,a.jsx)("div",{className:"bm-cut-fields bm-art-legs",children:e.legs.map(e=>(0,a.jsx)(as,{label:`Transition: frame ${String.fromCharCode(65+e.from_frame)} â frame ${String.fromCharCode(65+e.to_frame)}${e.closes_loop?" (closes the loop)":""}`,text:e.veo_prompt,mono:!0},e.leg))})]})}function ao({reels:e}){return(0,a.jsx)("div",{className:"bm-reels",children:e.map(e=>(0,a.jsx)("article",{className:"bm-cut bm-reel",children:(0,a.jsxs)("div",{className:"bm-reel-body",children:[(0,a.jsx)("video",{className:"bm-reel-video",src:e.url,controls:!0,preload:"metadata",playsInl
482ine:!0}),(0,a.jsxs)("div",{className:"bm-reel-meta",children:[(0,a.jsxs)("div",{className:"bm-cut-head",children:[(0,a.jsxs)("span",{className:"bm-cut-tag",children:[e.duration_seconds?`${e.duration_seconds.toFixed(0)}s`:"",e.category?` \xb7 ${e.category}`:""]}),(0,a.jsx)("h4",{children:e.title})]}),(0,a.jsxs)("p",{className:"bm-cut-meta",children:[(0,a.jsx)("strong",{children:"Voice:"})," ",e.voice_name,e.voice_persona_id?` (${e.voice_persona_id})`:""]}),(0,a.jsxs)("div",{className:"bm-cut-fields",children:[(0,a.jsx)(as,{label:"Narration script",text:e.script.join("\n"),mono:!0}),e.caption&&(0,a.jsx)(as,{label:"Post caption",text:e.caption})]})]})]})},e.story_id))})}function al({roster:e,sampledCategories:t}){return(0,a.jsxs)("div",{className:"bm-roster",children:[(0,a.jsx)("h4",{className:"bm-roster-title",children:"Every topic & its voice"}),(0,a.jsx)("p",{className:"bm-cut-meta",children:"One voice per topic, straight from the data â the rest below have no sample video (not generated, no spend), just the taxonomy and the voice that will read it."}),(0,a.jsxs)("table",{className:"bm-roster-table",children:[(0,a.jsx)("thead",{children:(0,a.jsxs)("tr",{children:[(0,a.jsx)("th",{children:"Topic"}),(0,a.jsx)("th",{children:"Voice(s)"}),(0,a.jsx)("th",{children:"Sample video"})]})}),(0,a.jsx)("tbody",{children:e.map(e=>(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:e.category}),(0,a.jsx)("td",{children:e.voices.map(e=>(0,a.jsxs)("div",{children:[e.voice_name,e.voice_persona_id?` (${e.voice_persona_id})`:""]},e.voice_name+(e.voice_persona_id??"")))}),(0,a.jsx)("td",{children:t.has(e.category)?"â":"â"})]},e.category))})]})]})}function ac({onSignIn:e,onSignOut:t,error:s}){return(0,a.jsxs)("main",{className:"bm-root bm-gate",children:[(0,a.jsx)("style",{children:ad}),e&&(0,a.jsx)("button",{className:"bm-btn",onClick:e,children:"Sign in with Google"}),t&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("a",{className:"bm-btn",href:"/ownerportal",children:"â Back to the owner portal"}),(0,a.jsx)("button",{className:"bm-btn bm-btn-quiet",onClick:t,children:"Sign out"})]}),s&&(0,a.jsx)("p",{className:"bm-error",children:s})]})}let ad=` 483.bm-root { 484 /* Factlen tokens â owner directive 2026-09-11: black/white/red. */ 485 --bm-bg: #ffffff; --bm-surface: #fff; --bm-alt: #f6f6f6; --bm-border: #e6e6e6; 486 --bm-text: #161616; --bm-muted: #5a5a5a; --bm-faint: #8a8a8a; 487 --bm-accent: #C8102E; --bm-accent-soft: #fde2e6; 488 --bm-warn: #92400e; --bm-warn-soft: #fffbeb; 489 --bm-err: #C8102E; 490 max-width: 1180px; margin: 0 auto; padding: 32px 20px 100px; 491 color: var(--bm-text); background: var(--bm-bg); 492 font-family: -apple-system, "SF Pro Text", ui-sans-serif, system-ui, sans-serif; 493} 494@media (prefers-color-scheme: dark) { 495 .bm-root { 496 --bm-bg: #0d1117; --bm-surface: #161b22; --bm-alt: #161b22; --bm-border: #30363d; 497 --bm-text: #d4d4d4; --bm-muted: #9a9a9a; --bm-faint: #6e7681; 498 --bm-accent: #ea5a72; --bm-accent-soft: #2a1216; 499 --bm-warn: #fcd34d; --bm-warn-soft: #2a2005; 500 --bm-err: #f85149; 501 } 502} 503.bm-gate { display: flex; flex-direction: column; align-items: center; justify-content: center; 504 min-height: 60vh; gap: 12px; } 505.bm-btn { font: inherit; font-size: .86rem; font-weight: 700; padding: 10px 18px; border-radius: 10px; 506 border: 1px solid var(--bm-border); background: var(--bm-surface); color: var(--bm-text); cursor: pointer; } 507.bm-btn-quiet { background: transparent; font-weight: 500; color: var(--bm-muted); } 508.bm-error { color: var(--bm-err); font-size: .82rem; } 509.bm-empty { color: var(--bm-muted); font-size: .88rem; } 510 511.bm-head { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; margin-bottom: 28px; position: relative; } 512.bm-eyebrow { font-size: .74rem; letter-spacing: .08em; text-transform: uppercase; color: var(--bm-faint); margin: 0; } 513.bm-head h1 { font-size: 1.7rem; margin: 2px 0 0; letter-spacing: -.01em; } 514.bm-sub { color: var(--bm-muted); font-size: .92rem; margin: 4px 0 0; max-width: 38rem; } 515.bm-head .bm-btn { position: absolute; right: 0; top: 0; } 516 517.bm-band-picker { display: flex; align-items: center; gap: 10px; margin-bottom: 22px; } 518.bm-band-picker label { font-size: .76rem; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; 519 color: var(--bm-faint); } 520.bm-band-picker select { font: inherit; font-size: .92rem; font-weight: 700; padding: 8px 12px; 521 border-radius: 10px; border: 1px solid var(--bm-border); background: var(--bm-surface); color: var(--bm-text); 522 cursor: pointer; } 523 524.bm-band { margin-bottom: 44px; } 525 526.bm-chapter { margin-bottom: 28px; padding-bottom: 8px; border-bottom: 1px solid var(--bm-border); } 527.bm-chapter-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 10px; flex-wrap: wrap; } 528.bm-chapter-head h3 { font-size: 1.02rem; font-weight: 700; margin: 0; } 529.bm-chapter-sub { font-weight: 500; color: var(--bm-muted); } 530.bm-ts { font-size: .7rem; color: var(--bm-faint); } 531 532.bm-cut { background: var(--bm-surface); border: 1px solid var(--bm-border); border-radius: 12px; 533 padding: 14px 16px; margin-bottom: 14px; } 534.bm-cut-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 4px; } 535.bm-cut-tag { font-size: .68rem; font-weight: 700; padding: 3px 9px; border-radius: 20px; 536 background: var(--bm-accent-soft); color: var(--bm-accent); white-space: nowrap; } 537.bm-cut-head h4 { font-size: .92rem; font-weight: 700; margin: 0; } 538.bm-cut-meta { font-size: .8rem; color: var(--bm-muted); margin: 2px 0 10px; } 539 540.bm-cut-fields { display: flex; flex-direction: column; gap: 4px; margin-bottom: 12px; } 541.bm-art-legs { margin-top: 12px; padding-top: 10px; border-top: 1px dashed var(--bm-border); margin-bottom: 0; } 542 543/* One consistent disclosure row for every piece of generated text on the page â 544 content, motion, image prompt, veo prompt, scene lock, cinematography â so 545 the page reads as a short stack of labels, not a wall of prose. */ 546.bm-field { font-size: .78rem; color: var(--bm-muted); } 547.bm-field + .bm-field { margin-top: 4px; } 548.bm-field summary { 549 cursor: pointer; font-weight: 600; color: var(--bm-text); list-style: none; 550 display: flex; align-items: center; gap: 5px; 551} 552.bm-field summary::-webkit-details-marker { display: none; } 553.bm-field summary::before { content: "â¸"; font-size: .68em; color: var(--bm-faint); transition: transform .12s ease; } 554.bm-field[open] summary::before { transform: rotate(90deg); } 555.bm-field p { margin: 6px 0 0; line-height: 1.5; } 556.bm-field pre { 557 white-space: pre-wrap; word-break: break-word; 558 font-family: ui-monospace, "SF Mono", monospace; 559 font-size: .68rem; line-height: 1.45; margin: 6px 0 0; color: var(--bm-muted); 560 max-height: 320px; overflow-y: auto; 561} 562 563.bm-frames { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); } 564.bm-frame { margin: 0; background: var(--bm-alt); border: 1px solid var(--bm-border); border-radius: 10px; 565 overflow: hidden; display: flex; flex-direction: column; } 566.bm-frame-img { position: relative; aspect-ratio: 9 / 16; background: var(--bm-border); } 567.bm-frame-img img { width: 100%; height: 100%; object-fit: cover; display: block; } 568.bm-frame-label { position: absolute; left: 6px; bottom: 6px; font-size: .64rem; font-weight: 700; 569 letter-spacing: .02em; padding: 2px 7px; border-radius: 20px; background: rgba(0,0,0,.6); color: #fff; } 570.bm-frame figcaption { padding: 9px 10px 10px; display: flex; flex-direction: column; gap: 2px; } 571 572.bm-frame-unpreviewable { display: flex; flex-direction: column; align-items: center; justify-content: center; 573 gap: 8px; padding: 16px; text-align: center; background: repeating-linear-gradient(135deg, 574 var(--bm-alt), var(--bm-alt) 8px, var(--bm-border) 8px, var(--bm-border) 9px); } 575.bm-frame-unpreviewable .bm-frame-label { position: static; background: var(--bm-accent); color: #fff; } 576.bm-frame-unpreviewable p { margin: 0; font-size: .72rem; color: var(--bm-muted); line-height: 1.4; } 577 578.bm-reels { display: flex; flex-direction: column; gap: 14px; } 579.bm-reel-body { display: flex; gap: 16px; flex-wrap: wrap; } 580.bm-reel-video { width: 220px; max-width: 100%; aspect-ratio: 9 / 16; border-radius: 10px; 581 background: #000; flex: none; } 582.bm-reel-meta { flex: 1 1 260px; min-width: 0; } 583 584.bm-roster { margin-top: 20px; padding-top: 16px; border-top: 1px dashed var(--bm-border); } 585.bm-roster-title { font-size: .92rem; font-weight: 700; margin: 0 0 4px; } 586.bm-roster-table { width: 100%; border-collapse: collapse; margin-top: 10px; font-size: .8rem; } 587.bm-roster-table th { text-align: left; font-size: .7rem; text-transform: uppercase; letter-spacing: .04em; 588 color: var(--bm-faint); padding: 0 10px 6px 0; border-bottom: 1px solid var(--bm-border); } 589.bm-roster-table td { padding: 7px 10px 7px 0; border-bottom: 1px solid var(--bm-border); vertical-align: top; } 590.bm-roster-table tr:last-child td { border-bottom: none; } 591 592.bm-frame-predicted { outline: 2px dashed var(--bm-warn); outline-offset: -2px; } 593.bm-frame-predicted-badge { position: absolute; right: 6px; top: 6px; font-size: .6rem; font-weight: 700; 594 letter-spacing: .02em; padding: 2px 7px; border-radius: 20px; background: var(--bm-warn-soft); 595 color: var(--bm-warn); } 596`;var ew=ew,ej=ew;let ap="ops_ad_performance";function ah(){let e=(0,ei.getAuth)((0,eu.getFirebaseApp)());return e.tenantId="owner-portal-w358j",e}let am=e=>"number"==typeof e&&Number.isFinite(e);function au(e,a="USD"){return am(e)?new Intl.NumberFormat("en-US",{style:"currency",currency:a,minimumFractionDigits:2,maximumFractionDigits:2}).format(e):"â"}function af(e){return am(e)?new Intl.NumberFormat("en-US").format(e):"â"}function ag(e){if(!e)return"â";let a=new Date(e);
596return Number.isNaN(a.getTime())?"â":a.toLocaleString()}function ax(e){return e.platforms.reduce((e,a)=>e+(am(a.spend)?a.spend:0),0)}function ab(e){let a=e.platforms.reduce((e,a)=>e+(am(a.budgetCapUsd)?a.budgetCapUsd:0),0);return a>0?a:am(e.budgetCapUsd)?e.budgetCapUsd:0}function ay(e){return e.platforms.reduce((e,a)=>e+(am(a.clicks)?a.clicks:0),0)}function av(e){return e.platforms.filter(e=>!e.error&&am(e.cpc)&&e.cpc>0&&am(e.clicks)&&e.clicks>0).sort((e,a)=>e.cpc-a.cpc)[0]??null}function aw(e){return e.platforms.filter(e=>e.error).length}function aj(){let[e,t]=(0,r.useState)(null),[s,n]=(0,r.useState)(!1),[i,o]=(0,r.useState)([]),[l,c]=(0,r.useState)(!1),[d,p]=(0,r.useState)(null),[h,m]=(0,r.useState)(null),u=/permission|insufficient/i.test(d??"");(0,r.useEffect)(()=>(0,eo.onAuthStateChanged)(ah(),e=>{t(e),n(!0)}),[]),(0,r.useEffect)(()=>{if(!e){o([]),c(!1);return}return(0,eN.onSnapshot)((0,ej.aO)((0,ew.aX)((0,eu.getFirebaseApp)()),ap),e=>{let a=e.docs.filter(e=>!Array.isArray(e.data().platforms)).map(e=>e.id);if(a.length){let e=`Malformed ${ap} docs (no platforms array): ${a.join(", ")}`;console.error(e),p(e),c(!0);return}let t=e.docs.map(e=>{let a=e.data();return{id:e.id,title:"string"==typeof a.title&&a.title?a.title:e.id,subtitle:"string"==typeof a.subtitle?a.subtitle:"",landing:"string"==typeof a.landing?a.landing:"",currency:"string"==typeof a.currency&&a.currency?a.currency:"USD",budgetCapUsd:a.budgetCapUsd,platforms:a.platforms,lastSyncAt:"string"==typeof a.lastSyncAt?a.lastSyncAt:""}});t.sort((e,a)=>e.title.localeCompare(a.title)),o(t),c(!0),p(null)},e=>{let a=`Could not read ${ap} from Firestore: ${e.message}`;console.error(a),p(a),c(!0)})},[e]);let f=(0,r.useMemo)(()=>i.find(e=>e.id===h)??null,[i,h]);return s?e?u?(0,a.jsx)(aA,{onSignOut:()=>(0,em.signOut)(ah())}):(0,a.jsxs)("main",{className:"ap-root",children:[(0,a.jsx)("style",{children:aP}),(0,a.jsxs)("div",{className:"ap-bar",children:[f?(0,a.jsx)("button",{className:"ap-btn ap-btn-quiet",onClick:()=>m(null),children:"â Everything"}):(0,a.jsx)("span",{}),(0,a.jsxs)("span",{className:"ap-who",children:[e.email,(0,a.jsx)("button",{className:"ap-btn ap-btn-quiet",onClick:()=>(0,em.signOut)(ah()),children:"Sign out"})]})]}),(0,a.jsx)(eS.OwnerNav,{active:"adperformance"}),d&&(0,a.jsx)("p",{className:"ap-error",children:d}),!d&&!l&&(0,a.jsx)("p",{className:"ap-muted",children:"Loadingâ¦"}),!d&&l&&!f&&(0,a.jsx)(ak,{items:i,onOpen:m}),!d&&l&&f&&(0,a.jsx)(aN,{item:f})]}):(0,a.jsx)(aA,{error:d,onSignIn:async()=>{p(null);try{let e=new ec.GoogleAuthProvider;e.setCustomParameters({prompt:"select_account"}),await (0,el.signInWithPopup)(ah(),e)}catch(e){p(e.message)}}}):(0,a.jsx)(aA,{})}function ak({items:e,onOpen:t}){if(0===e.length)return(0,a.jsx)("p",{className:"ap-muted",children:"Nothing is being advertised yet."});let s=e.reduce((e,a)=>e+ax(a),0),r=e.reduce((e,a)=>e+ab(a),0),n=e.reduce((e,a)=>e+ay(a),0),i=e.reduce((e,a)=>e+aw(a),0);return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("p",{className:"ap-lede",children:[au(s)," spent of ",au(r)," committed across ",e.length," ",1===e.length?"item":"items"," · ",af(n)," clicks."]}),i>0&&(0,a.jsxs)("p",{className:"ap-error",children:[i," ",1===i?"platform is":"platforms are"," not reporting â open the item to see why. Those rows are excluded from every comparison below rather than counted as zero."]}),(0,a.jsx)("ul",{className:"ap-tiles",children:e.map(e=>{let s=ax(e),r=ab(e),n=av(e),i=aw(e);return(0,a.jsx)("li",{children:(0,a.jsxs)("button",{className:"ap-tile",onClick:()=>t(e.id),children:[(0,a.jsxs)("span",{className:"ap-tile-top",children:[(0,a.jsx)("span",{className:"ap-tile-name",children:e.title}),i>0?(0,a.jsx)("span",{className:"ap-badge ap-badge-err",children:i}):(0,a.jsx)("span",{className:"ap-badge",children:e.platforms.length})]}),e.subtitle&&(0,a.jsx)("span",{className:"ap-tile-sub",children:e.subt
596itle}),(0,a.jsxs)("span",{className:"ap-tile-meta",children:[au(s,e.currency)," of ",au(r,e.currency)," · ",af(ay(e))," clicks"]}),(0,a.jsx)("span",{className:"ap-track",children:(0,a.jsx)("span",{className:"ap-fill",style:{width:r>0?`${Math.min(100,s/r*100)}%`:"0%"}})}),(0,a.jsx)("span",{className:"ap-tile-lead",children:n?(0,a.jsxs)(a.Fragment,{children:["Cheapest clicks: ",(0,a.jsx)("strong",{children:n.label})," at"," ",au(n.cpc,e.currency)]}):"No clicks bought yet"})]})},e.id)})})]})}function aN({item:e}){let t=ax(e),s=ab(e),r=av(e),n=e.currency??"USD",i=ay(e),o=e.platforms.reduce((e,a)=>e+(am(a.conversions)?a.conversions:0),0);return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("header",{className:"ap-head",children:[(0,a.jsx)("h1",{children:e.title}),e.subtitle&&(0,a.jsx)("p",{className:"ap-sub",children:e.subtitle}),e.landing&&(0,a.jsx)("p",{className:"ap-path",children:(0,a.jsx)("a",{href:e.landing,target:"_blank",rel:"noopener noreferrer",children:e.landing})}),(0,a.jsxs)("p",{className:"ap-lede",children:[au(t,n)," spent of ",au(s,n)," committed · ",af(i)," clicks · synced ",ag(e.lastSyncAt)]})]}),(0,a.jsxs)("p",{className:"ap-caveat",children:["At this budget ",(0,a.jsx)("strong",{children:"CPC, CTR and CPM are the reliable signals"}),". Conversions and CPA are shown for completeness but are under-powered â ",af(o)," recorded so far, where ranking platforms on cost-per-subscription would need dozens each."]}),0===e.platforms.length&&(0,a.jsx)("p",{className:"ap-muted",children:"No platforms configured for this item yet."}),(0,a.jsx)("ul",{className:"ap-list",children:e.platforms.map(e=>{var t;let s,i=!!r&&r.key===e.key;return(0,a.jsxs)("li",{className:`ap-plat${e.error?" ap-plat-err":""}`,children:[(0,a.jsxs)("div",{className:"ap-plat-head",children:[(0,a.jsx)("span",{className:"ap-plat-name",children:e.label}),e.status&&(0,a.jsx)("span",{className:`ap-chip ap-chip-${"live"===(s=e.status.toLowerCase())||"enabled"===s||"active"===s?"live":"blocked"===s||"error"===s?"err":"idle"}`,children:e.status}),i&&(0,a.jsx)("span",{className:"ap-chip ap-chip-best",children:"cheapest clicks"}),am(e.budgetCapUsd)&&(0,a.jsxs)("span",{className:"ap-plat-cap",children:[au(e.spend??0,n)," / ",au(e.budgetCapUsd,n)]})]}),e.campaignName&&(0,a.jsxs)("p",{className:"ap-plat-campaign",children:[e.campaignName,e.campaignId?` \xb7 ${e.campaignId}`:""]}),e.error?(0,a.jsx)("p",{className:"ap-plat-error",children:e.error}):(0,a.jsxs)("dl",{className:"ap-metrics",children:[(0,a.jsx)(aS,{label:"Spend",value:au(e.spend,n)}),(0,a.jsx)(aS,{label:"Impressions",value:af(e.impressions)}),(0,a.jsx)(aS,{label:"Clicks",value:af(e.clicks)}),(0,a.jsx)(aS,{label:"CTR",value:am(t=e.ctr)?`${(100*t).toFixed(2)}%`:"â",strong:!0}),(0,a.jsx)(aS,{label:"CPC",value:au(e.cpc,n),strong:!0}),(0,a.jsx)(aS,{label:"CPM",value:au(e.cpm,n),strong:!0}),(0,a.jsx)(aS,{label:"Conversions",value:af(e.conversions),weak:!0}),(0,a.jsx)(aS,{label:"CPA",value:au(e.cpa,n),weak:!0})]}),e.note&&(0,a.jsx)("p",{className:"ap-plat-note",children:e.note}),(0,a.jsxs)("p",{className:"ap-plat-sync",children:["synced ",ag(e.lastSyncAt)]})]},e.key)})})]})}function aS({label:e,value:t,strong:s,weak:r}){return(0,a.jsxs)("div",{className:`ap-metric${s?" ap-metric-strong":""}${r?" ap-metric-weak":""}`,children:[(0,a.jsx)("dt",{children:e}),(0,a.jsx)("dd",{children:t})]})}function aA({onSignIn:e,onSignOut:t,error:s}){return(0,a.jsxs)("main",{className:"ap-root ap-gate",children:[(0,a.jsx)("style",{children:aP}),e&&(0,a.jsx)("button",{className:"ap-btn",onClick:e,children:"Sign in with Google"}),t&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("a",{className:"ap-btn",href:"/ownerportal",children:"â Back to the owner portal"}),(0,a.jsx)("button",{className:"ap-btn ap-btn-quiet",onClick:t,children:"Sign out"})]}),s&&(0,a.jsx)("p",{className:"ap-error",children:s})]})}let aP=` 597.ap-root { 598 /* Factlen tokens â owner directive 2026-09-11: black/white/red. */ 599 --ap-bg: #ffffff; --ap-surface: #fff; --ap-alt: #f6f6f6; --ap-border: #e6e6e6; 600 --ap-text: #161616; --ap-muted: #5a5a5a; --ap-faint: #8a8a8a; 601 --ap-accent: #C8102E; --ap-accent-strong: #a30d26; --ap-accent-soft: #fde2e6; 602 --ap-warn: #92400e; --ap-warn-soft: #fffbeb; --ap-warn-border: #fbbf24; 603 --ap-idle: #404040; --ap-idle-soft: #f6f6f6; --ap-idle-border: #e6e6e6; 604 --ap-done: #1d7a37; --ap-done-soft: #eaf6ec; --ap-err: #C8102E; --ap-err-soft: #fde2e6; 605 max-width: 820px; margin: 0 auto; padding: 32px 20px 80px; 606 color: var(--ap-text); 607 font-family: -apple-system, "SF Pro Text", ui-sans-serif, system-ui, sans-serif; 608} 609@media (prefers-color-scheme: dark) { 610 .ap-root { 611 --ap-bg: #0d1117; --ap-surface: #161b22; --ap-alt: #161b22; --ap-border: #30363d; 612 --ap-text: #d4d4d4; --ap-muted: #9a9a9a; --ap-faint: #6e7681; 613 --ap-accent: #ea5a72; --ap-accent-strong: #f2879a; --ap-accent-soft: #2a1216; 614 --ap-warn: #fcd34d; --ap-warn-soft: #2a2005; --ap-warn-border: #5b4520; 615 --ap-idle: #c9c9c9; --ap-idle-soft: #161b22; --ap-idle-border: #30363d; 616 --ap-done: #3fb950; --ap-done-soft: #0f1f12; --ap-err: #f85149; --ap-err-soft: #2a1216; 617 } 618} 619.ap-gate { min-height: 60vh; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; } 620.ap-bar { display: flex; justify-content: space-between; align-items: center; gap: 12px; 621 flex-wrap: wrap; padding-bottom: 14px; border-bottom: 1px solid var(--ap-border); margin-bottom: 20px; } 622.ap-who { display: flex; align-items: center; gap: 10px; font-size: .78rem; color: var(--ap-faint); } 623.ap-head { margin-bottom: 18px; } 624.ap-head h1 { font-size: clamp(1.4rem,3vw,1.85rem); font-weight: 800; letter-spacing: -.015em; margin: 0 0 4px; text-wrap: balance; } 625.ap-sub { font-size: .88rem; color: var(--ap-muted); margin: 0 0 6px; } 626.ap-path { font-family: ui-monospace, monospace; font-size: .76rem; color: var(--ap-faint); margin: 0 0 8px;
626 overflow-wrap: anywhere; } 627.ap-path a { color: var(--ap-faint); } 628.ap-lede { color: var(--ap-muted); font-size: .88rem; line-height: 1.6; margin: 0 0 16px; max-width: 62ch; } 629.ap-muted { color: var(--ap-muted); font-size: .9rem; } 630.ap-error { color: var(--ap-err); font-size: .88rem; line-height: 1.55; } 631.ap-caveat { font-size: .8rem; line-height: 1.55; color: var(--ap-warn); background: var(--ap-warn-soft); 632 border: 1px solid var(--ap-warn-border); border-radius: 10px; padding: 10px 13px; margin: 0 0 18px; max-width: 70ch; } 633.ap-btn { display: inline-flex; align-items: center; gap: 6px; font: inherit; font-size: .8rem; font-weight: 700; 634 padding: 7px 13px; border-radius: 8px; border: 1px solid transparent; cursor: pointer; 635 background: var(--ap-accent-soft); color: var(--ap-accent-strong); text-decoration: none; } 636.ap-btn:hover { border-color: var(--ap-accent); } 637.ap-btn:focus-visible { outline: 2px solid var(--ap-accent); outline-offset: 2px; } 638.ap-btn-quiet { background: transparent; color: var(--ap-faint); border-color: var(--ap-border); } 639.ap-tiles { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; 640 grid-template-columns: repeat(auto-fill, minmax(244px, 1fr)); } 641.ap-tile { width: 100%; text-align: left; font: inherit; cursor: pointer; 642 display: flex; flex-direction: column; gap: 6px; 643 background: var(--ap-surface); border: 1px solid var(--ap-border); border-radius: 12px; padding: 14px 15px; } 644.ap-tile:hover { border-color: var(--ap-accent); } 645.ap-tile:focus-visible { outline: 2px solid var(--ap-accent); outline-offset: 2px; } 646.ap-tile-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; } 647.ap-tile-name { font-size: .95rem; font-weight: 800; letter-spacing: -.01em; } 648.ap-tile-sub { font-size: .76rem; color: var(--ap-faint); } 649.ap-tile-meta { font-size: .76rem; color: var(--ap-muted); } 650.ap-tile-lead { font-size: .74rem; color: var(--ap-muted); } 651.ap-badge { font-family: ui-monospace, monospace; font-size: .72rem; font-weight: 700; color: #fff; 652 background: var(--ap-accent); border-radius: 20px; padding: 2px 9px; min-width: 26px; text-align: center; } 653.ap-badge-err { background: var(--ap-err); } 654.ap-track { height: 4px; border-radius: 4px; background: var(--ap-alt); overflow: hidden; } 655.ap-fill { display: block; height: 100%; background: var(--ap-accent); border-radius: 4px; } 656.ap-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 11px; } 657.ap-plat { background: var(--ap-surface); border: 1px solid var(--ap-border); border-radius: 12px; padding: 14px 16px 13px; } 658.ap-plat-err { border-color: var(--ap-err); } 659.ap-plat-head { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; } 660.ap-plat-name { font-size: .95rem; font-weight: 800; } 661.ap-plat-cap { margin-left: auto; font-family: ui-monospace, monospace; font-size: .72rem; font-weight: 700; 662 color: var(--ap-faint); background: var(--ap-alt); border: 1px solid var(--ap-border); border-radius: 20px; padding: 2px 9px; } 663.ap-chip { font-size: .64rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; 664 padding: 2px 7px; border-radius: 20px; border: 1px solid transparent; } 665.ap-chip-live { color: var(--ap-done); background: var(--ap-done-soft); border-color: var(--ap-done); } 666.ap-chip-idle { color: var(--ap-idle); background: var(--ap-idle-soft); border-color: var(--ap-idle-border); } 667.ap-chip-err { color: var(--ap-err); background: var(--ap-err-soft); border-color: var(--ap-err); } 668.ap-chip-best { color: var(--ap-accent-strong); background: var(--ap-accent-soft); border-color: var(--ap-accent); } 669.ap-plat-campaign { font-family: ui-monospace, monospace; font-size: .72rem; color: var(--ap-faint); margin: 7px 0 0; overflow-wrap: anywhere; } 670.ap-plat-error { margin: 10px 0 0; font-size: .8rem; line-height: 1.5; color: var(--ap-err); 671 background: var(--ap-err-soft); border-radius: 8px; padding: 9px 11px; overflow-wrap: anywhere; } 672.ap-plat-note { margin: 10px 0 0; font-size: .78rem; color: var(--ap-muted); line-height: 1.5; 673 padding-left: 10px; border-left: 2px solid var(--ap-accent-soft); } 674.ap-plat-sync { margin: 9px 0 0; font-size: .68rem; color: var(--ap-faint); } 675.ap-metrics { display: grid; gap: 8px; grid-template-columns: repeat(auto-fit, minmax(84px, 1fr)); 676 margin: 12px 0 0; padding: 0; } 677.ap-metric { background: var(--ap-alt); border: 1px solid var(--ap-border); border-radius: 8px; padding: 7px 9px; } 678.ap-metric dt { font-size: .64rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--ap-faint); } 679.ap-metric dd { margin: 2px 0 0; font-family: ui-monospace, monospace; font-size: .84rem; font-weight: 700; } 680.ap-metric-strong { border-color: var(--ap-accent); } 681.ap-metric-strong dt { color: var(--ap-accent-strong); } 682.ap-metric-weak { opacity: .62; } 683`;var ew=ew,ek=ew;let az="ops_owner_portal",aT="latest";function aF(){let e=(0,ei.getAuth)((0,eu.getFirebaseApp)());return e.tenantId="owner-portal-w358j",e}let aC=["var(--op-c1)","var(--op-c2)","var(--op-c3)","var(--op-c4)","var(--op-c5)","var(--op-c6)","var(--op-c7)","var(--op-c8)","var(--op-c9)","var(--op-c10)"],aM={app_studio_and_publisher:"Studio & imprint",record_label_and_arts_network:"Record label",ecommerce_store:"Store",app_franchise:"App franchise",news_media:"News",affiliate:"Affiliate",financial_advisory:"Advisory",financial_advisory_niche_sites:"Adv
683isory sites",construction:"Construction",lifestyle_blog:"Blog",corporate_parent_brand:"Parent brand",content_channel_bucket:"Channel group",native_app:"App",book_publisher:"Books",artist:"Artist",platform:"Channel",bucket:"Group",band:"Channel",title:"Book",volume:"Volume",format:"Edition",service_app:"Service",content_channel:"Channel",business:"Business",line:"Line"},aI={revenuecat:"Subscriptions (RevenueCat)",shopify_omenira:"Omenira store",shopify_echovira:"Echovira Gallery store",admob:"In-app ads (AdMob)"};function aD(e){return e.replace(/_/g," ").replace(/\b\w/g,e=>e.toUpperCase())}let aW={revenue:{label:"Revenue",tint:"var(--op-good)",dim:"total_revenue"},adspend:{label:"Ad spend",tint:"var(--op-bad)",dim:null},net:{label:"Net",tint:"var(--op-accent)",dim:null},audience:{label:"Audience",tint:"var(--op-c2)",dim:"total_audience"},traffic:{label:"Visitors / 7d",tint:"var(--op-c3)",dim:"total_traffic"}};function aO(e){return aM[e]??e.replace(/_/g," ").replace(/\b\w/g,e=>e.toUpperCase())}let aE=e=>"number"==typeof e&&Number.isFinite(e);function aR(e,a="USD"){return aE(e)?new Intl.NumberFormat("en-US",{style:"currency",currency:a,minimumFractionDigits:2,maximumFractionDigits:2}).format(e):"â"}function a$(e){return aE(e)?new Intl.NumberFormat("en-US").format(e):"â"}function a_(e){if(!e)return"â";let a=new Date(e);return Number.isNaN(a.getTime())?"â":a.toLocaleString()}let aU={subscriptions_lifetime:"Subscriptions (all time)",active_subscriptions:"Subscriptions (active now)",revenue_gross_lifetime:"Revenue, gross (all time)",revenue_proceeds_lifetime:"Revenue, proceeds (all time)",views_lifetime:"Views (all time)"},aL={total_revenue:"revenue",total_audience:"audience",total_traffic:"traffic"};function aq(){let[e,t]=(0,r.useState)(null),[s,n]=(0,r.useState)(!1),[i,o]=(0,r.useState)(null),[l,c]=(0,r.useState)(!1),[d,p]=(0,r.useState)(null),[h,m]=(0,r.useState)([]),u=/permission|insufficient/i.test(d??"");(0,r.useEffect)(()=>(0,eo.onAuthStateChanged)(aF(),e=>{t(e),n(!0)}),[]),(0,r.useEffect)(()=>{if(!e){o(null),c(!1);return}return(0,eN.onSnapshot)((0,ek.a6)((0,ew.aX)((0,eu.getFirebaseApp)()),az,aT),e=>{if(!e.exists()){p(az+"/"+aT+" does not exist yet â run owner_portal_sync.py"),c(!0);return}o(e.data()),c(!0),p(null)},e=>{let a="Could not read "+az+"/"+aT+" from Firestore: "+e.message;console.error(a),p(a),c(!0)})},[e]);let f=h[0]?.startsWith("kpi:")?h[0].slice(4):null,{chain:g,children:x,node:b}=(0,r.useMemo)(()=>{if(!i||f)return{chain:[],children:[],node:null};let e=[],a=i.tree,t=null;for(let s of h){let r=a.find(e=>e.id===s);if(!r)break;t=r,e.push(r),a=r.children}return{chain:e,children:a,node:t}},[i,h,f]);return s?e?u?(0,a.jsx)(a0,{onSignOut:()=>(0,em.signOut)(aF())}):(0,a.jsxs)("main",{className:"op-root",children:[(0,a.jsx)("style",{children:a1}),(0,a.jsxs)("div",{className:"op-shell",children:[(0,a.jsxs)("div",{className:"op-bar",children:[(0,a.jsxs)("nav",{className:"op-crumbs","aria-label":"Drill-down path",children:[(0,a.jsx)("button",{className:"op-crumb"+(0===h.length?" op-crumb-now":""),onClick:()=>m([]),children:"Everything"}),g.map((e,t)=>(0,a.jsxs)("span",{style:{display:"inline-flex",alignItems:"center"},children:[(0,a.jsx)("span",{className:"op-crumb-dot"}),(0,a.jsx)("button",{className:"op-crumb"+(t===g.length-1?" op-crumb-now":""),onClick:()=>m(h.slice(0,t+1)),children:e.name})]},e.id)),f&&(0,a.jsxs)("span",{style:{display:"inline-flex",alignItems:"center"},children:[(0,a.jsx)("span",{className:"op-crumb-dot"}),(0,a.jsx)("button",{className:"op-crumb op-crumb-now",onClick:()=>m(h),children:aW[f].label})]})]}),(0,a.jsxs)("span",{className:"op-who",children:[e.email,(0,a.jsx)("button",{className:"op-btn op-btn-quiet",onClick:()=>(0,em.signOut)(aF()),children:"Sign out"})]})]}),(0,a.jsx)(eS.OwnerNav,{active:"ownerportal"}),d&&!u&&(0,a.jsx)("p",{className:"op-error",children:d}),!d&&!l&&(0,a.jsx)("p",{className:"op-muted",children:"Loadingâ¦"}),!d&&l&&i&&(0,a.jsx)("div",{className:"op-stage",children:f?(0,a.jsx)(aG,{portal:i,kpiId:f,onOpen:e=>m([e])}):(0,a.jsx)(aB,{portal:i,node:b,children_:x,onOpen:e=>m([...h,e]),onOpenKpi:e=>m(["kpi:"+e])})},h.join("/"))]})]}):(0,a.jsx)(a0,{error:d,onSignIn:async()=>{p(null);try{let e=new ec.GoogleAuthProvider;e.setCustomParameters({prompt:"select_account"}),await (0,el.signInWithPopup)(aF(),e)}catch(e){p(e.message)}}}):(0,a.jsx)(a0,{})}function aB({portal:e,node:t,children_:s,onOpen:n,onOpenKpi:i}){let o,l,c,d=e.currency,p=null===t,h=(l=[["total_revenue",(o=e=>s.filter(a=>a.rollup[e].tracked_count>0).length)("total_revenue")],["total_audience",o("total_audience")],["total_traffic",o("total_traffic")]].filter(([,e])=>e>0).sort((e,a)=>a[1]-e[1])[0])?l[0]:null,m=e=>"total_revenue"===h?aR(e,d):a$(e),u=h?aL[h]:"",f=(0,r.useMemo)(()=>h?[...s].sort((e,a)=>(a.rollup[h].value??-1)-(e.rollup[h].value??-1)):s,[s,h]),g=h?f.filter(e=>e.rollup[h].value):[],x=Object.entries(e.notes||{});return(0,a.jsxs)(a.Fragment,{children:[p?(0,a.jsxs)("div",{className:"op-hero",children:[(0,a.jsx)("p",{className:"op-eyebrow",children:"Portfolio"}),(0,a.jsxs)("h1",{className:"op-title",children:[aR(e.totals.net,d)," net across ",e.tree.length," businesses"]}),(0,a.jsxs)("p",{className:"op-lede",children:[aR(e.totals.revenue,d)," revenue against ",aR(e.totals.adSpend,d)," of ad spend, and ",a$(e.portfolioAudience.value)," people following the portfolio across ",e.portfolioAudience.tracked_count," measured channels."]}),(0,a.jsxs)("p",{className:"op-sync",children:["as of ",e.asOf??"â"," · ",e.entityMetricsAttached," live metrics from"," ",(e.liveSources??[]).length," sources · updated ",a_(e.lastSyncAt)]})]}):(0,a.jsx)(aH,{node:t}),x.length>0&&p&&(0,a.jsx)("ul",{className:"op-notes",children:x.map(([e,t])=>(0,a.jsx)("li",{className:"op-error",children:t},e))}),p?(0,a.jsxs)("div",{className:"op-kpis",children:[(0,a.jsx)(aY,{label:"Revenue",tint:"var(--op-good)",value:aR(e.totals.revenue,d),onClick:()=>i("revenue"),children:(0,a.jsx)(aK,{points:e.trend,k:"revenue",tint:"var(--op-good)"})}),(0,a.jsx)(aY,{label:"Ad spend",tint:"var(--op-bad)",value:aR(e.totals.adSpend,d),onClick:()=>i("adspend"),children:(0,a.jsx)(aK,{points:e.trend,k:"adSpend",tint:"var(--op-bad)"})}),(0,a.jsx)(aY,{label:"Net",tint:"var(--op-accent)",value:aR(e.totals.net,d),sub:"revenue minus ad spend",onClick:()=>i("net")}),(0,a.jsx)(aY,{label:"Audience",tint:"var(--op-c2)",value:a$(e.portfolioAudience.value),sub:e.portfolioAudience.tracked_count+" of "+e.portfolioAudience.total_count+" channels measured",onClick:()=>i("audience")}),(0,a.jsx)(aY,{label:"Visitors / 7d",tint:"var(--op-c3)",value:a$(e.portfolioTraffic.value),sub:e.portfolioTraffic.tracked_count+" web properties",onClick:()=>i("traffic")})]}):(0,a.jsxs)("div",{className:"op-kpis",children:[t.rollup.total_revenue.total_count>0&&(0,a.jsx)(aY,{label:"Revenue here",tint:"var(--op-good)",value:aR(t.rollup.total_revenue.value,d),sub:t.rollup.total_revenue.tracked_count+" of "+t.rollup.total_revenue.total_count+" measured"}),t.rollup.total_audience.total_count>0&&(0,a.jsx)(aY,{label:"Audience here",tint:"var(--op-c2)",value:a$(t.rollup.total_audience.value),sub:t.rollup.total_audience.tracked_count+" of "+t.rollup.total_audience.total_count+" measured"}),t.rollup.total_traffic.total_count>0&&(0,a.jsx)(aY,{label:"Visitors / 7d",tint:"var(--op-c3)",value:a$(t.rollup.total_traffic.value),sub:t.rollup.total_traffic.tracked_count+" web "+(1===t.rollup.total_traffic.tracked_count?"property":"properties")}),(0,a.jsx)(aY,{label:"Inside",tint:"var(--op-accent)",value:String(s.length),sub:(1===s.length?"thing":"things")+" one level down"})]}),p&&(e.revenue.length>0||e.adIncome.length>0)&&(0,a.jsxs)("div",{className:"op-panel",children:[(0,a.jsxs)("div",{className:"op-panel-head",children:[(0,a.jsx)("p",{className:"op-panel-title",children:"Revenue breakdown"}),(0,a.jsxs)("p",{className:"op-panel-note",children:["every source that adds up to ",aR(e.totals.revenue,d)," above"]})]}),(0,a.jsx)("dl",{className:"op-stats",children:[...e.revenue,...e.adIncome].map((e,t)=>(0,a.jsxs)("div",{className:"op-stat",children:[(0,a.jsx)("dt",{children:aI[e.source]??aD(e.source)}),(0,a.jsx)("dd",{children:aR(e.value,d)}),(0,a.jsx)("p",{className:"op-stat-src",children:e.window?e.window.replace(/_/g," "):e.metric})]},e.source+e.metric+t))})]}),p&&e.books.length>0&&(0,a.jsxs)("div",{className:"op-panel",children:[(0,a.jsxs)("div",{className:"op-panel-head",children:[(0,a.jsx)("p",{className:"op-panel-title",children:"Book sales"}
683),(0,a.jsxs)("p",{className:"op-panel-note",children:["last ",30," days · one row per marketplace currency â never summed together"]})]}),(0,a.jsx)("dl",{className:"op-stats",children:e.books.map(e=>(0,a.jsxs)("div",{className:"op-stat",children:[(0,a.jsxs)("dt",{children:[e.platform," · ",e.currency]}),(0,a.jsx)("dd",{children:aR(e.net,e.currency)}),(0,a.jsxs)("p",{className:"op-stat-src",children:[a$(e.units)," ",1===e.units?"unit":"units"," sold"]})]},e.platform+e.currency))})]}),p&&e.trend.length>1&&(0,a.jsxs)("div",{className:"op-panel",children:[(0,a.jsx)("div",{className:"op-panel-head",children:(0,a.jsxs)("p",{className:"op-panel-title",children:["Revenue vs ad spend · ",e.trend.length," days"]})}),(0,a.jsx)(aQ,{points:e.trend}),(0,a.jsxs)("div",{className:"op-legend",children:[(0,a.jsxs)("span",{className:"op-legend-item",children:[(0,a.jsx)("span",{className:"op-swatch",style:{background:"var(--op-good)"}}),"Revenue"]}),(0,a.jsxs)("span",{className:"op-legend-item",children:[(0,a.jsx)("span",{className:"op-swatch",style:{background:"var(--op-bad)"}}),"Ad spend"]})]})]}),!p&&Object.keys(t.metrics||{}).length>0&&(0,a.jsxs)("div",{className:"op-panel",children:[(0,a.jsx)("div",{className:"op-panel-head",children:(0,a.jsx)("p",{className:"op-panel-title",children:"Measured here"})}),(0,a.jsx)("dl",{className:"op-stats",children:Object.entries(t.metrics).map(([e,t])=>(0,a.jsxs)("div",{className:"op-stat"+(t.tracked?"":" op-stat-untracked"),title:t.source,children:[(0,a.jsx)("dt",{children:aU[e]??e.replace(/_/g," ").replace(/\b\w/g,e=>e.toUpperCase())}),(0,a.jsx)("dd",{children:t.tracked?/revenue|spend|price|cpa|cpc|cpm/i.test(e)?aR(t.value,d):a$(t.value):"not measured yet"}),t.as_of&&(0,a.jsxs)("p",{className:"op-stat-src",children:["as of ",t.as_of]})]},e))})]}),!p&&t.transactions&&t.transactions.length>0&&(0,a.jsxs)("div",{className:"op-panel",children:[(0,a.jsxs)("div",{className:"op-panel-head",children:[(0,a.jsx)("p",{className:"op-panel-title",children:"Recent subscriptions"}),(0,a.jsxs)("p",{className:"op-panel-note",children:[t.transactions.length," most recent · production only"]})]}),(0,a.jsx)("div",{className:"op-tx-scroll",children:(0,a.jsxs)("table",{className:"op-tx-table",children:[(0,a.jsx)("thead",{children:(0,a.jsxs)("tr",{children:[(0,a.jsx)("th",{children:"Customer"}),(0,a.jsx)("th",{children:"Product"}),(0,a.jsx)("th",{children:"Store"}),(0,a.jsx)("th",{children:"Started"}),(0,a.jsx)("th",{children:"Renews / expires"}),(0,a.jsx)("th",{children:"Revenue"}),(0,a.jsx)("th",{children:"Status"})]})}),(0,a.jsx)("tbody",{children:t.transactions.map((e,t)=>{var s;return(0,a.jsxs)("tr",{className:e.active?"":"op-tx-inactive",children:[(0,a.jsx)("td",{className:"op-tx-mono",children:(s=e.customer_id).length<=8?s:`${s.slice(0,4)}â¢â¢â¢â¢${s.slice(-4)}`}),(0,a.jsx)("td",{children:e.product_id}),(0,a.jsx)("td",{children:aD(e.store)}),(0,a.jsx)("td",{children:a_(e.purchased_at)}),(0,a.jsx)("td",{children:a_(e.expires_at)}),(0,a.jsx)("td",{children:aR(e.revenue_gross,d)}),(0,a.jsx)("td",{children:aD(e.status)})]},e.customer_id+e.product_id+t)})})]})})]}),!p&&(!!("factlen.com"===(c=(t.site??"").toLowerCase())||"factlen"===c||t.domains?.some(e=>"factlen.com"===e.toLowerCase()))||"factlen"===t.name.trim().toLowerCase())&&(0,a.jsxs)("div",{className:"op-panel",children:[(0,a.jsxs)("div",{className:"op-panel-head",children:[(0,a.jsx)("p",{className:"op-panel-title",children:"Factlen"}),(0,a.jsx)("p",{className:"op-panel-note",children:"recomputed live from the published feed"})]}),(0,a.jsx)("ul",{className:"op-tiles",children:(0,a.jsx)("li",{children:(0,a.jsxs)("a",{className:"op-tile",href:"/ownerportal/factlen",style:{"--tile-tint":aC[1],textDecoration:"none",color:"inherit"},children:[(0,a.jsxs)("span",{className:"op-tile-top",children:[(0,a.jsx)("span",{className:"op-tile-type",children:"Content"}),(0,a.jsx)("span",{className:"op-tile-name",children:"Publishing Cadence"})]}),(0,a.jsx)("span",{className:"op-tile-sub",children:"Articles per hour of day and per day, measured live from Factlen's own published feed â not a snapshot."}),(0,a.jsxs)("span",{className:"op-tile-foot",children:[(0,a.jsx)("span",{className:"op-tile-idle",children:"hourly rhythm, daily totals"}),(0,a.jsx)("span",{className:"op-tile-arrow",children:"â"})]})]})})})]}),h&&g.length>=2&&(0,a.jsxs)("div",{className:"op-panel",children:[(0,a.jsxs)("div",{className:"op-panel-head",children:[(0,a.jsxs)("p",{className:"op-panel-title",children:["Where the ",u," sits"]}),(0,a.jsxs)("p",{className:"op-panel-note",children:[g.length," of ",s.length," have measured ",u]})]}),(0,a.jsxs)("div",{className:"op-grid-2",children:[(0,a.jsx)(aJ,{items:g,dim:h,fmt:m,label:u,onOpen:n}),(0,a.jsxs)("div",{children:[(0,a.jsx)("div",{className:"op-bars",children:g.slice(0,14).map((e,t)=>(0,a.jsx)(aZ,{name:e.name,value:e.rollup[h].value,max:g[0].rollup[h].value??1,display:m(e.rollup[h].value),tint:aC[t%aC.length],onClick:()=>n(e.id)},e.id))}),s.length-g.length>0&&(0,a.jsxs)("p",{className:"op-panel-note",style:{marginTop:10},children:[s.length-g.length," more with nothing measured here yet â they are in the tiles below."]})]})]})]}),p&&(0,a.jsxs)("div",{className:"op-panel",children:[(0,a.jsxs)("div",{className:"op-panel-head",children:[(0,a.jsx)("p",{className:"op-panel-title",children:"Web"}),(0,a.jsx)("p",{className:"op-panel-note",children:"every registered website, end to end"})]}),(0,a.jsx)("ul",{className:"op-tiles",children:(0,a.jsx)("li",{children:(0,a.jsxs)("a",{className:"op-tile",href:"/ownerportal/web",style:{"--tile-tint":aC[1],textDecoration:"none",color:"inherit"},children:[(0,a.jsxs)("span",{className:"op-tile-top",children:[(0,a.jsx)("span",{className:"op-tile-type",children:"Websites"}),(0,a.jsx)("span",{className:"op-tile-name",children:"Website Analytics"})]}),(0,a.jsx)("span",{className:"op-tile-sub",children:"Published â sitemap â discovered â crawled â indexed â impressions â clicks â readers, per site, with the drop-off named."}),(0,a.jsxs)("span",{className:"op-tile-foot",children:[(0,a.jsx)("span",{className:"op-tile-idle",children:"search, indexing, crawl and content"}),(0,a.jsx)("span",{className:"op-tile-arrow",children:"â"})]})]})})})]}),s.length>0?(0,a.jsxs)("div",{className:"op-panel",children:[(0,a.jsxs)("div",{className:"op-panel-head",children:[(0,a.jsx)("p",{className:"op-panel-title",children:p?"Every business":"Inside "+t.name}),(0,a.jsx)("p",{className:"op-panel-note",children:"click any tile, bar or slice to dive deeper"})]}),(0,a.jsx)("ul",{className:"op-tiles",children:f.map((e,t)=>(0,a.jsx)(aV,{node:e,tint:aC[t%aC.length],currency:d,onOpen:()=>n(e.id)},e.id))})]}):!p&&(0,a.jsx)("p",{className:"op-muted",children:"Nothing deeper here â this is a leaf."}),p&&e.unwired.length>0&&(0,a.jsxs)("div",{className:"op-panel",children:[(0,a.jsx)("div",{className:"op-panel-head",children:(0,a.jsx)("p",{className:"op-panel-title",children:"Not yet counted"})}),(0,a.jsx)("ul",{className:"op-gaps",children:e.unwired.map(e=>(0,a.jsx)("li",{children:e},e))})]})]})}function aG({portal:e,kpiId:t,onOpen:s}){let n=e.currency,i=aW[t],o=e=>"total_revenue"===i.dim?aR(e,n):a$(e),l=(0,r.useMemo)(()=>
683{if(!i.dim)return[];let a=i.dim;return[...e.tree].sort((e,t)=>(t.rollup[a].value??-1)-(e.rollup[a].value??-1))},[e.tree,i.dim]),c=i.dim?l.filter(e=>e.rollup[i.dim].tracked_count>0):[],d="revenue"===t?aR(e.totals.revenue,n):"adspend"===t?aR(e.totals.adSpend,n):"net"===t?aR(e.totals.net,n):"audience"===t?a$(e.portfolioAudience.value):a$(e.portfolioTraffic.value),p="revenue"===t?[...e.revenue,...e.adIncome]:"adspend"===t?e.adSpend:[];return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"op-hero",children:[(0,a.jsxs)("p",{className:"op-eyebrow",children:["Portfolio · ",i.label]}),(0,a.jsx)("h1",{className:"op-title",children:d}),"net"===t&&(0,a.jsxs)("p",{className:"op-lede",children:[aR(e.totals.revenue,n)," revenue minus ",aR(e.totals.adSpend,n)," ad spend"]}),("audience"===t||"traffic"===t)&&e.audience&&(0,a.jsxs)("p",{className:"op-lede",children:[a$(e.audience.users)," unique visitors, ",a$(e.audience.sessions)," sessions,"," ",a$(e.audience.events)," events in the last ",e.audience.windowDays," days (web properties only â social/streaming followers roll up separately below)"]})]}),("revenue"===t||"net"===t||"adspend"===t)&&e.trend.length>1&&(0,a.jsxs)("div",{className:"op-panel",children:[(0,a.jsx)("div",{className:"op-panel-head",children:(0,a.jsxs)("p",{className:"op-panel-title",children:["Revenue vs ad spend · ",e.trend.length," days"]})}),(0,a.jsx)(aQ,{points:e.trend}),(0,a.jsxs)("div",{className:"op-legend",children:[(0,a.jsxs)("span",{className:"op-legend-item",children:[(0,a.jsx)("span",{className:"op-swatch",style:{background:"var(--op-good)"}}),"Revenue"]}),(0,a.jsxs)("span",{className:"op-legend-item",children:[(0,a.jsx)("span",{className:"op-swatch",style:{background:"var(--op-bad)"}}),"Ad spend"]})]})]}),p.length>0&&(0,a.jsxs)("div",{className:"op-panel",children:[(0,a.jsxs)("div",{className:"op-panel-head",children:[(0,a.jsx)("p",{className:"op-panel-title",children:"Every source"}),(0,a.jsxs)("p",{className:"op-panel-note",children:["what ",i.label.toLowerCase()," above adds up from"]})]}),(0,a.jsx)("dl",{className:"op-stats",children:p.map((e,t)=>(0,a.jsxs)("div",{className:"op-stat",children:[(0,a.jsx)("dt",{children:aI[e.source]??aD(e.source)}),(0,a.jsx)("dd",{children:aR(e.value,n)}),(0,a.jsx)("p",{className:"op-stat-src",children:e.window?e.window.replace(/_/g," "):e.metric})]},e.source+e.metric+t))})]}),"revenue"===t&&e.books.length>0&&(0,a.jsxs)("div",{className:"op-panel",children:[(0,a.jsxs)("div",{className:"op-panel-head",children:[(0,a.jsx)("p",{className:"op-panel-title",children:"Book sales"}),(0,a.jsx)("p",{className:"op-panel-note",children:"one row per marketplace currency â never summed together"})]}),(0,a.jsx)("dl",{className:"op-stats",children:e.books.map(e=>(0,a.jsxs)("div",{className:"op-stat",children:[(0,a.jsxs)("dt",{children:[e.platform," · ",e.currency]}),(0,a.jsx)("dd",{children:aR(e.net,e.currency)}),(0,a.jsxs)("p",{className:"op-stat-src",children:[a$(e.units)," ",1===e.units?"unit":"units"," sold"]})]},e.platform+e.currency))})]}),i.dim&&c.length>=2&&(0,a.jsxs)("div",{className:"op-panel",children:[(0,a.jsxs)("div",{className:"op-panel-head",children:[(0,a.jsxs)("p",{className:"op-panel-title",children:["Where the ",aL[i.dim]," sits"]}),(0,a.jsxs)("p",{className:"op-panel-note",children:[c.length," of ",e.tree.length," businesses have measured ",aL[i.dim]]})]}),(0,a.jsxs)("div",{className:"op-grid-2",children:[(0,a.jsx)(aJ,{items:c,dim:i.dim,fmt:o,label:aL[i.dim],onOpen:s}),(0,a.jsx)("div",{children:(0,a.jsx)("div",{className:"op-bars",children:c.slice(0,14).map((e,t)=>(0,a.jsx)(aZ,{name:e.name,value:e.rollup[i.dim].value,max:c[0].rollup[i.dim].value??1,display:o(e.rollup[i.dim].value),tint:aC[t%aC.length],onClick:()=>s(e.id)},e.id))})})]})]}),null===i.dim&&"net"!==t&&"adspend"!==t&&0===p.length&&(0,a.jsx)("p",{className:"op-muted",children:"Nothing to break down here yet."})]})}function aH({node:e}){let t=[];return e.handle&&t.push(["Handle",e.handle]),e.channel_id&&t.push(["Channel",e.channel_id]),e.artist_id&&t.push(["Artist",e.artist_id]),e.app_id&&t.pu
683sh(["App",e.app_id]),e.package&&t.push(["Package",e.package]),e.site&&t.push(["Site",e.site]),e.status&&t.push(["Status",e.status]),(0,a.jsxs)("div",{className:"op-hero",children:[(0,a.jsx)("p",{className:"op-eyebrow",children:aO(e.type)}),(0,a.jsx)("h1",{className:"op-title",children:e.name}),e.description&&(0,a.jsx)("p",{className:"op-lede",children:e.description}),e.url&&(0,a.jsx)("p",{style:{margin:"8px 0 0"},children:(0,a.jsx)("a",{className:"op-link",href:e.url,target:"_blank",rel:"noopener noreferrer",children:e.url})}),t.length>0&&(0,a.jsx)("div",{className:"op-idchips",children:t.map(([e,t])=>(0,a.jsxs)("span",{className:"op-idchip",children:[(0,a.jsx)("b",{children:e})," ",t]},e))})]})}function aY({label:e,value:t,sub:s,tint:r,children:n,onClick:i}){let o=(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("p",{className:"op-kpi-label",children:e}),(0,a.jsx)("p",{className:"op-kpi-val",children:t}),s&&(0,a.jsx)("p",{className:"op-kpi-sub",children:s}),n,i&&(0,a.jsx)("span",{className:"op-kpi-arrow",children:"â"})]}),l={"--kpi-tint":r};return i?(0,a.jsx)("button",{type:"button",className:"op-kpi op-kpi-clickable",style:l,onClick:i,children:o}):(0,a.jsx)("div",{className:"op-kpi",style:l,children:o})}function aV({node:e,tint:t,currency:s,onOpen:r}){let n,i=e.rollup,o=(n=e.rollup).total_revenue.tracked_count>0?{value:aR(n.total_revenue.value,s),label:"revenue"}:n.total_audience.tracked_count>0?{value:a$(n.total_audience.value),label:"audience"}:n.total_traffic.tracked_count>0?{value:a$(n.total_traffic.value),label:"visitors / 7d"}:null,l=i.total_revenue.tracked_count+i.total_audience.tracked_count+i.total_traffic.tracked_count,c=i.total_revenue.total_count+i.total_audience.total_count+i.total_traffic.total_count;return(0,a.jsx)("li",{children:(0,a.jsxs)("button",{className:"op-tile",style:{"--tile-tint":t},onClick:r,children:[(0,a.jsxs)("span",{className:"op-tile-top",children:[(0,a.jsx)("span",{className:"op-tile-type",children:aO(e.type)}),(0,a.jsx)("span",{className:"op-tile-name",children:e.name})]}),e.description&&(0,a.jsx)("span",{className:"op-tile-sub",children:e.description}),(0,a.jsxs)("span",{className:"op-tile-foot",children:[o?(0,a.jsxs)("span",{className:"op-tile-metric",children:[(0,a.jsx)("span",{className:"op-tile-metric-val",children:o.value}),(0,a.jsx)("span",{className:"op-tile-metric-lbl",children:o.label})]}):(0,a.jsxs)("span",{className:"op-tile-idle",children:[e.children.length," inside · not measured yet"]}),c>0&&(0,a.jsx)(aX,{tracked:l,total:c}),(0,a.jsx)("span",{className:"op-tile-arrow",children:"â"})]})]})})}function aK({points:e,k:t,tint:s}){if(e.length<2)return null;let r=e.map(e=>e[t]),n=Math.min(...r),i=Math.max(...r)-n||1,o=r.map((e,a)=>[a/(r.length-1)*170,27-(e-n)/i*24]).map(([e,a],t)=>(t?"L":"M")+e.toFixed(1)+","+a.toFixed(1)).join(" "),l="sp-"+t;return(0,a.jsxs)("svg",{className:"op-kpi-spark",viewBox:"0 0 170 30",preserveAspectRatio:"none",children:[(0,a.jsx)("defs",{children:(0,a.jsxs)("linearGradient",{id:l,x1:"0",y1:"0",x2:"0",y2:"1",children:[(0,a.jsx)("stop",{offset:"0%",stopColor:s,stopOpacity:".35"}),(0,a.jsx)("stop",{offset:"100%",stopColor:s,stopOpacity:"0"})]})}),(0,a.jsx)("path",{d:o+" L170,30 L0,30 Z",fill:"url(#"+l+")"}),(0,a.jsx)("path",{d:o,fill:"none",stroke:s,strokeWidth:"1.6"})]})}function aQ({points:e}){let t=Math.max(1,...e.map(e=>Math.max(e.revenue,e.adSpend))),s=e.length,r=e=>44+e/Math.max(1,s-1)*848,n=e=>176-e/t*164,i=(t,i,o)=>{let l=e.map((e,a)=>(a?"L":"M")+r(a).toFixed(1)+","+n(e[t]).toFixed(1)).join(" ");return(0,a.jsxs)("g",{children:[(0,a.jsx)("defs",{children:(0,a.jsxs)("linearGradient",{id:o,x1:"0",y1:"0",x2:"0",y2:"1",children:[(0,a.jsx)("stop",{offset:"0%",stopColor:i,stopOpacity:".28"}),(0,a.jsx)("stop",{offset:"100%",stopColor:i,stopOpacity:"0"})]})}),(0,a.jsx)("path",{d:l+" L"+r(s-1).toFixed(1)+","+176+" L"+r(0).toFixed(1)+","+176+" Z",fill:"url(#"+o+")"}),(0,a.jsx)("path",{d:l,fill:"none",stroke:i,strokeWidth:"2.1",strokeLinejoin:"round"}),(0,a.jsx)("circle",{cx:r(s-1),cy:n(e[s-1][t]),r:"3.4",fill:i})]},o)};return(0,a.jsxs)("svg",{className:"op-area",viewBox:"0 0 900 200",preserveAspectRatio:"none",children:[[0,.25,.5,.75,1].map(e=>{let s=176-164*e;return(0,a.jsxs)("g",{children:[(0,a.jsx)("line",{className:"op-grid-line",x1:44,y1:s,x2:892,y2:s}),(0,a.jsx)("text",{className:"op-axis-txt",x:37,y:s+3,textAnchor:"end",children:"$"+Math.round(t*e).toLocaleStr
683ing()})]},e)}),i("adSpend","var(--op-bad)","op-g-spend"),i("revenue","var(--op-good)","op-g-rev"),[0,Math.floor(s/2),s-1].map(t=>(0,a.jsx)("text",{className:"op-axis-txt",x:r(t),y:194,textAnchor:"middle",children:e[t].date.slice(5)},t))]})}function aJ({items:e,dim:t,fmt:s,label:r,onOpen:n}){let i=2*Math.PI*66,o=e.reduce((e,a)=>e+(a.rollup[t].value??0),0)||1,l=0,c=e.map((e,s)=>{let r=(e.rollup[t].value??0)/o*i,c=(0,a.jsx)("circle",{className:"op-donut-seg",cx:90,cy:90,r:66,fill:"none",stroke:aC[s%aC.length],strokeWidth:22,strokeDasharray:(r-1.6).toFixed(2)+" "+(i-r+1.6).toFixed(2),strokeDashoffset:(-l).toFixed(2),transform:"rotate(-90 90 90)",onClick:()=>n(e.id),children:(0,a.jsx)("title",{children:e.name})},e.id);return l+=r,c});return(0,a.jsxs)("div",{className:"op-donut-wrap",children:[(0,a.jsxs)("svg",{className:"op-donut",viewBox:"0 0 180 180",children:[c,(0,a.jsx)("text",{className:"op-donut-center-val",x:90,y:92,textAnchor:"middle",children:s(o)}),(0,a.jsx)("text",{className:"op-donut-center-lbl",x:90,y:110,textAnchor:"middle",children:r})]}),(0,a.jsxs)("ul",{className:"op-dlegend",children:[e.slice(0,6).map((e,s)=>(0,a.jsx)("li",{children:(0,a.jsxs)("button",{onClick:()=>n(e.id),children:[(0,a.jsx)("span",{className:"op-swatch",style:{background:aC[s%aC.length]}}),(0,a.jsx)("span",{className:"op-dlegend-name",children:e.name}),(0,a.jsxs)("span",{className:"op-dlegend-val",children:[Math.round((e.rollup[t].value??0)/o*100),"%"]})]})},e.id)),e.length>6&&(0,a.jsx)("li",{children:(0,a.jsxs)("button",{style:{cursor:"default"},children:[(0,a.jsx)("span",{className:"op-swatch",style:{background:"var(--op-faint)"}}),(0,a.jsxs)("span",{className:"op-dlegend-name",children:["+",e.length-6," more"]}),(0,a.jsxs)("span",{className:"op-dlegend-val",children:[Math.round(e.slice(6).reduce((e,a)=>e+(a.rollup[t].value??0),0)/o*100),"%"]})]})})]})]})}function aZ({name:e,value:t,max:s,display:r,tint:n,onClick:i}){let o=t?Math.max(2,t/(s||1)*100):0;return(0,a.jsxs)("button",{className:"op-bar-row"+(t?"":" op-bar-row-empty"),style:{"--bar-tint":n},onClick:i,children:[(0,a.jsx)("span",{className:"op-bar-label",children:e}),(0,a.jsx)("span",{className:"op-bar-track",children:(0,a.jsx)("span",{className:"op-bar-fill",style:{width:o.toFixed(1)+"%"}})}),(0,a.jsx)("span",{className:"op-bar-val",children:r})]})}function aX({tracked:e,total:t,size:s=42}){let r=(s-7)/2,n=2*Math.PI*r,i=t?e/t:0;return(0,a.jsxs)("svg",{className:"op-ring",width:s,height:s,viewBox:"0 0 "+s+" "+s,children:[(0,a.jsx)("title",{children:e+" of "+t+" metrics measured"}),(0,a.jsx)("circle",{className:"op-ring-bg",cx:s/2,cy:s/2,r:r,fill:"none",strokeWidth:"4"}),(0,a.jsx)("circle",{className:"op-ring-fg",cx:s/2,cy:s/2,r:r,fill:"none",strokeWidth:"4",strokeDasharray:n.toFixed(2),strokeDashoffset:(n*(1-i)).toFixed(2),transform:"rotate(-90 "+s/2+" "+s/2+")"}),(0,a.jsxs)("text",{className:"op-ring-txt",x:s/2,y:s/2+3.5,textAnchor:"middle",children:[e,"/",t]})]})}function a0({onSignIn:e,onSignOut:t,error:s}){return(0,a.jsxs)("main",{className:"op-root op-gate",children:[(0,a.jsx)("style",{children:a1}),e&&(0,a.jsx)("button",{className:"op-btn",onClick:e,children:"Sign in with Google"}),t&&(0,a.jsx)("button",{className:"op-btn op-btn-quiet",onClick:t,children:"Sign out"}),s&&(0,a.jsx)("p",{className:"op-error",children:s})]})}let a1=` 684.op-root { 685 /* Factlen tokens â owner directive 2026-09-11: black/white/red, matching the 686 site itself. --op-c1..c10 (per-source chart series) are untouched â 687 functional data encoding, not decoration; already flagged CVD-debt 688 elsewhere, out of scope for a recolor pass. */ 689 --op-canvas: #0d1117; 690 --op-canvas-2: #0d1117; 691 --op-surface: rgba(255, 255, 255, 0.045); 692 --op-surface-2: rgba(255, 255, 255, 0.075); 693 --op-border: rgba(255, 255, 255, 0.09); 694 --op-border-str: rgba(255, 255, 255, 0.18); 695 --op-text: #d4d4d4; 696 --op-muted: #9a9a9a; 697 --op-faint: #6e7681; 698 --op-accent: #ea5a72; 699 --op-accent-2: #f2879a; 700 --op-good: #3fb950; 701 --op-bad: #f85149; 702 --op-warn: #fcd34d; 703 --op-c1: #8b5cf6; --op-c2: #22d3ee; --op-c3: #34d399; --op-c4: #fbbf24; 704 --op-c5: #fb7185; --op-c6: #60a5fa; --op-c7: #f472b6; --op-c8: #2dd4bf; 705 --op-c9: #c084fc; --op-c10: #facc15; 706 --op-glow: 0 0 0 1px rgba(255,255,255,.05), 0 18px 48px -22px rgba(0,0,0,.9); 707 min-height: 100vh; 708 margin: 0; 709 padding: 0 0 96px; 710 color: var(--op-text); 711 background-color: var(--op-canvas-2); 712 font-family: -apple-system, "SF Pro Text", ui-sans-serif, system-ui, "
712Segoe UI", sans-serif; 713 -webkit-font-smoothing: antialiased; 714 font-variant-numeric: tabular-nums; 715} 716@media (prefers-color-scheme: light) { 717 .op-root { 718 --op-canvas: #ffffff; --op-canvas-2: #ffffff; 719 --op-surface: rgba(16, 24, 40, 0.03); --op-surface-2: #ffffff; 720 --op-border: #e6e6e6; --op-border-str: rgba(16, 24, 40, 0.20); 721 --op-text: #161616; --op-muted: #5a5a5a; --op-faint: #8a8a8a; 722 --op-accent: #C8102E; --op-accent-2: #a30d26; 723 --op-good: #1d7a37; --op-bad: #C8102E; 724 --op-glow: 0 0 0 1px rgba(16,24,40,.04), 0 18px 44px -26px rgba(16,24,40,.35); 725 background-color: var(--op-canvas-2); 726 } 727} 728.op-shell { max-width: 1180px; margin: 0 auto; padding: 0 clamp(16px, 3vw, 32px); } 729.op-bar { 730 position: sticky; top: 0; z-index: 30; 731 display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; 732 padding: 14px 0 12px; 733 backdrop-filter: blur(18px) saturate(1.4); -webkit-backdrop-filter: blur(18px) saturate(1.4); 734 border-bottom: 1px solid var(--op-border); margin-bottom: 26px; 735} 736.op-who { display: flex; align-items: center; gap: 10px; font-size: .76rem; color: var(--op-faint); } 737.op-crumbs { display: flex; align-items: center; flex-wrap: wrap; gap: 0; } 738.op-crumb { 739 font: inherit; font-size: .78rem; font-weight: 650; letter-spacing: -.005em; 740 color: var(--op-muted); background: transparent; border: none; cursor: pointer; 741 padding: 5px 11px; border-radius: 999px; transition: color .18s, background .18s; white-space: nowrap; 742} 743.op-crumb:hover { color: var(--op-text); background: var(--op-surface-2); } 744.op-crumb-now { color: var(--op-text); background: var(--op-surface); box-shadow: inset 0 0 0 1px var(--op-border); } 745.op-crumb-dot { width: 14px; height: 1px; background: var(--op-border-str); flex: none; } 746@keyframes op-dive-in { 747 from { opacity: 0; transform: scale(.955) translateY(14px); filter: blur(3px); } 748 to { opacity: 1; transform: none; filter: none; } 749} 750@keyframes op-rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } } 751.op-stage { animation: op-dive-in .42s cubic-bezier(.22,.9,.28,1) both; } 752.op-stage > * { animation: op-rise .5s cubic-bezier(.22,.9,.28,1) both; } 753.op-stage > *:nth-child(1) { animation-delay: .02s } 754.op-stage > *:nth-child(2) { animation-delay: .07s } 755.op-stage > *:nth-child(3) { animation-delay: .12s } 756.op-stage > *:nth-child(4) { animation-delay: .17s } 757.op-stage > *:nth-child(5) { animation-delay: .22s } 758@media (prefers-reduced-motion: reduce) { 759 .op-stage, .op-stage > * { animation: none !important; } 760 .op-tile, .op-bar-row, .op-kpi { transition: none !important; } 761} 762.op-hero { margin-bottom: 26px; } 763.op-eyebrow { 764 font-size: .68rem; font-weight: 750; letter-spacing: .14em; text-transform: uppercase; 765 color: var(--op-accent-2); margin: 0 0 10px; 766} 767.op-title { 768 font-size: clamp(1.9rem, 4.4vw, 3rem); font-weight: 800; letter-spacing: -.035em; 769 line-height: 1.04; margin: 0 0 8px; text-wrap: balance; 770} 771.op-lede { 772 font-size: .95rem; line-height: 1.6; color: var(--op-muted); margin: 0 0 4px; max-width: 68ch; 773 display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; 774} 775.op-sync { font-size: .7rem; color: var(--op-faint); margin: 8px 0 0; } 776.op-idchips { display: flex; flex-wrap: wrap; gap: 7px; margin: 12px 0 0; } 777.op-idchip { 778 font-family: ui-monospace, "SF Mono", monospace; font-size: .69rem; color: var(--op-muted); 779 background: var(--op-surface); border: 1px solid var(--op-border); border-radius: 8px; padding: 4px 9px; 780} 781.op-idchip b { color: var(--op-faint); font-weight: 650; } 782.op-link { color: var(--op-accent-2); text-decoration: none; font-weight: 600; font-size: .8rem; overflow-wrap: anywhere; } 783.op-link:hover { text-decoration: underline; } 784.op-kpis { display: grid; gap: 12px; margin: 0 0 24px; grid-template-columns: repeat(auto-fit, minmax(196px, 1fr)); } 785.op-kpi { 786 position: relative; overflow: hidden; background: var(--op-surface); border: 1px solid var(--op-border); 787 border-radius: 18px; padding: 16px 17px 14px; box-shadow: var(--op-glow); 788 transition: transform .2s cubic-bezier(.22,.9,.28,1), border-color .2s; 789} 790.op-kpi:hover { transform: translateY(-2px); border-color: var(--op-border-str); } 791.op-kpi::after {
792 content: ""; position: absolute; inset: 0 0 auto 0; height: 2px; 793 background: linear-gradient(90deg, var(--kpi-tint, var(--op-accent)), transparent 78%); opacity: .85; 794} 795.op-kpi-label { 796 font-size: .68rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; 797 color: var(--op-faint); margin: 0 0 7px; 798} 799.op-kpi-val { font-size: clamp(1.45rem, 3vw, 1.95rem); font-weight: 800; letter-spacing: -.03em; line-height: 1; margin: 0; } 800.op-kpi-sub { font-size: .72rem; color: var(--op-muted); margin: 7px 0 0; } 801.op-kpi-spark { margin-top: 10px; display: block; width: 100%; height: 30px; } 802.op-kpi-clickable { 803 display: block; width: 100%; text-align: left; font: inherit; color: inherit; cursor: pointer; 804} 805.op-kpi-clickable:hover .op-kpi-arrow { opacity: 1; transform: translateX(0); } 806.op-kpi-arrow { 807 position: absolute; right: 14px; bottom: 12px; font-size: .85rem; color: var(--op-faint); 808 opacity: 0; transform: translateX(-4px); transition: opacity .2s, transform .2s; 809} 810.op-panel { 811 background: var(--op-surface); border: 1px solid var(--op-border); border-radius: 20px; 812 padding: 18px 19px 16px; box-shadow: var(--op-glow); margin: 0 0 18px; 813} 814.op-panel-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin: 0 0 14px; } 815.op-panel-title { font-size: .74rem; font-weight: 750; letter-spacing: .1em; text-transform: uppercase; color: var(--op-faint); margin: 0; } 816.op-panel-note { font-size: .72rem; color: var(--op-faint); margin: 0; } 817.op-grid-2 { display: grid; gap: 18px; grid-template-columns: minmax(0, 1fr); } 818@media (min-width: 880px) { .op-grid-2 { grid-template-columns: 292px minmax(0, 1fr); align-items: center; } } 819.op-area { width: 100%; height: clamp(150px, 22vw, 210px); display: block; overflow: visible; } 820.op-grid-line { stroke: var(--op-border); stroke-width: 1; } 821.op-axis-txt { fill: var(--op-faint); font-size: 9px; font-family: ui-monospace, monospace; } 822.op-legend { display: flex; flex-wrap: wrap; gap: 16px; margin: 12px 0 0; font-size: .75rem; color: var(--op-muted); } 823.op-legend-item { display: inline-flex; align-items: center; gap: 7px; } 824.op-swatch { width: 9px; height: 9px; border-radius: 3px; flex: none; } 825.op-donut-wrap { display: flex; flex-direction: column; align-items: center; gap: 12px; } 826.op-donut { width: 100%; max-width: 232px; height: auto; display: block; } 827.op-donut-seg { transition: opacity .18s; cursor: pointer; } 828.op-donut-seg:hover { opacity: .8; } 829.op-donut-center-val { font-size: 26px; font-weight: 800; letter-spacing: -.03em; fill: var(--op-text); } 830.op-donut-center-lbl { font-size: 8.5px; font-weight: 700; letter-spacing: .12em; fill: var(--op-faint); text-transform: uppercase; } 831.op-dlegend { list-style: none; margin: 0; padding: 0; width: 100%; display: flex; flex-direction: column; gap: 3px; } 832.op-dlegend button { 833 display: flex; align-items: center; gap: 8px; width: 100%; font: inherit; font-size: .76rem; 834 background: none; border: none; color: var(--op-muted); cursor: pointer; padding: 3px 5px; 835 border-radius: 7px; text-align: left; transition: background .16s, color .16s; 836} 837.op-dlegend button:hover { background: var(--op-surface-2); color: var(--op-text); } 838.op-dlegend-name { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } 839.op-dlegend-val { font-family: ui-monospace, monospace; font-size: .72rem; font-weight: 700; color: var(--op-text); } 840.op-bars { display: flex; flex-direction: column; gap: 3px; } 841.op-bar-row { 842 display: grid; grid-template-columns: minmax(88px, 168px) minmax(0, 1fr) auto; 843 align-items: center; gap: 12px; background: none; border: none; font: inherit; cursor: pointer; 844 padding: 7px 9px; border-radius: 11px; text-align: left; transition: background .16s; 845} 846.op-bar-row:hover { background: var(--op-surface-2); } 847.op-bar-row:focus-visible { outline: 2px solid var(--op-accent); outline-offset: 1px; } 848.op-bar-row-empty { opacity: .42; } 849.op-bar-row-empty .op-bar-track { background: transparent; box-shadow: inset 0 0 0 1px var(--op-border); } 850.op-bar-label { font-size: .81rem; font-weight: 650; color: var(--op-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } 851.op-bar-track { height: 9px; border-radius: 6px; background: var(--op-surface-2); overflow: hidden; box-shadow: inset 0 0 0 1px var(--op-border); } 852.op-bar-fill { 853 display: block; height: 100%; border-radius: 6px; 854 background: linear-gradient(90deg, var(--bar-tint, var(--op-accent)), color-mix(in srgb, var(--bar-tint, var(--op-accent)) 55%, transparent)); 855 animation: op-bar-grow .7s cubic-bezier(.22,.9,.28,1) both; 856} 857@keyframes op-bar-grow { from { width: 0 !important; } } 858.op-bar-val { font-family: ui-monospace, monospace; font-size: .76rem; font-weight: 700; color: var(--op-muted); white-space: nowrap; min-width: 72px; text-align: right; } 859.op-tiles { list-style: none; margin: 0; padding: 0; display: grid; gap: 13px; grid-template-columns: repeat(auto-fill, minmax(248px, 1fr)); grid-auto-rows: 1fr; } 860.op-tile { 861 position: relative; overflow: hidden; width: 100%; height: 100%; text-align: left; font: inherit; cursor: pointer; 862 display: flex; flex-direction: column; gap: 9px; 863 background: var(--op-surface); border: 1px solid var(--op-border); border-radius: 18px; 864 padding: 15px 16px 14px; box-shadow: var(--op-glow); 865 transition: transform .22s cubic-bezier(.22,.9,.28,1), border-color .22s, background .22s; 866} 867.op-tile:hover { transform: translateY(-3px); border-color: var(--op-border-str); background: var(--op-surface-2); } 868.op-tile:focus-visible { outline: 2px solid var(--op-accent); outline-offset: 2px; } 869.op-tile::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--tile-tint, var(--op-accent)); opacity: .9; } 870.op-tile-top { display: flex; flex-direction: column; gap: 5px; } 871.op-tile-type { 872 font-size: .6rem; font-weight: 750; letter-spacing: .1em; text-transform: uppercase; 873 color: var(--tile-tint, var(--op-accent-2)); opacity: .95; 874 white-space: nowrap; overflow: hidden; text-overflow: ellipsis; 875} 876.op-tile-name { 877 font-size: .96rem; font-weight: 750; letter-spacing: -.015em; line-height: 1.25; 878 display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; 879} 880.op-tile-sub { 881 font-size: .74rem; color: var(--op-faint); line-height: 1.45; 882 display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; 883} 884.op-tile-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: auto; } 885.op-tile-metric { display: flex; flex-direction: column; gap: 2px; } 886.op-tile-metric-val { font-size: 1.12rem; font-weight: 800; letter-spacing: -.025em; line-height: 1; } 887.op-tile-metric-lbl { font-size: .63rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--op-faint); } 888.op-tile-idle { font-size: .74rem; color: var(--op-faint); } 889.op-tile-arrow { font-size: .95rem; color: var(--op-faint); transition: transform .22s, color .22s; flex: none; } 890.op-tile:hover .op-tile-arrow { transform: translateX(3px); color: var(--op-accent-2); } 891.op-ring { flex: none; } 892.op-ring-bg { stroke: var(--op-surface-2); } 893.op-ring-fg { stroke: var(--op-accent); stroke-linecap: round; transition: stroke-dashoffset .6s cubic-bezier(.22,.9,.28,1); } 894.op-ring-txt { fill: var(--op-muted); font-size: 10px; font-weight: 700; font-family: ui-monospace, monospace; } 895.op-stats { display: grid; gap: 10px; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); margin: 0; } 896.op-stat { background: var(--op-surface); border: 1px solid var(--op-border); border-radius: 14px; padding: 12px 13px; } 897.op-stat-untracked { opacity: .5; border-style: dashed; } 898.op-stat dt { font-size: .63rem; font-weight: 750; letter-spacing: .07em; text-transform: uppercase; color: var(--op-faint); margin: 0 0 5px; } 899.op-stat dd { margin: 0; font-size: 1.06rem; font-weight: 800; letter-spacing: -.02em; } 900.op-stat-src { font-size: .64rem; color: var(--op-faint); margin: 5px 0 0; line-height: 1.4; } 901.op-tx-scroll { overflow-x: auto; } 902.op-tx-table { width: 100%; border-collapse: collapse; font-size: .78rem; white-space: nowrap; } 903.op-tx-table th { 904 text-align: left; font-size: .63rem; font-weight: 750; letter-spacing: .07em; text-transform: uppercase; 905 color: var(--op-faint); padding: 0 12px 8px 0; border-bottom: 1px solid var(--op-border); 906} 907.op-tx-table td { padding: 8px 12px 8px 0; border-bottom: 1px solid var(--op-border); } 908.op-tx-table tr:last-child td { border-bottom: none; } 909.op-tx-inactive { opacity: .55; } 910.op-tx-mono { font-family: ui-monospace, monospace; } 911.op-muted { color: var(--op-muted); font-size: .88rem; } 912.op-error { color: var(--op-bad); font-size: .84rem; line-height: 1.55; } 913.op-notes { 914 list-style: none; margin: 0 0 20px; padding: 12px 15px; 915 background: color-mix(in srgb, var(--op-bad) 10%, transparent); 916 border: 1px solid color-mix(in srgb, var(--op-bad) 40%, transparent); 917 border-radius: 14px; display: flex; flex-direction: column; gap: 6px; 918} 919.op-gaps { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 7px; } 920.op-gaps li { font-size: .78rem; color: var(--op-muted); line-height: 1.5; padding-left: 16px; position: relative; } 921.op-gaps li::before { content: ""; position: absolute; left: 0; top: .52em; width: 5px; height: 5px; border-radius: 50%; background: var(--op-warn); opacity: .7; } 922.op-btn { 923 display: inline-flex; align-items: center; gap: 7px; font: inherit; font-size: .79rem; font-weight: 700; 924 padding: 9px 16px; border-radius: 11px; border: 1px solid var(--op-border); cursor: pointer; 925 background: var(--op-surface); color: var(--op-text); text-decoration: none; 926 transition: border-color .18s, background .18s, transform .18s; 927} 928.op-btn:hover { border-color: var(--op-border-str); background: var(--op-surface-2); transform: translateY(-1px); } 929.op-btn:focus-visible { outline: 2px solid var(--op-accent); outline-offset: 2px; } 930.op-btn-quiet { background: transparent; color: var(--op-faint); font-weight: 600; padding: 6px 11px; } 931.op-gate { min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; } 932`;var ew=ew,ej=ew;let a2="ops_email_marketing",a6="_sendgrid_account";function a5(){let e=(0,ei.getAuth)((0,eu.getFirebaseApp)());return e.tenantId="owner-portal-w358j",e}let a8=[{key:"openRate",label:"Open rate",short:"Opens",num:e=>e.uniqueOpens,den:e=>e.delivered,goodHigh:!0,soft:!0},{key:"clickRate",label:"Click rate",short:"Clicks",num:e=>e.uniqueClicks,den:e=>e.delivered,goodHigh:!0},{key:"conversionRate",label:"Conversion rate",short:"Conversions",num:e=>e.conversions,den:e=>e.delivered,goodHigh:!0},{key:"clickToOpenRate",label:"Click-to-open",short:"CTOR",num:e=>e.uniqueClicks,den:e=>e.uniqueOpens,goodHigh:!0,soft:!0},{key:"unsubRate",label:"Unsubscribe rate",short:"Unsubs",num:e=>e.unsubscribes,den:e=>e.delivered,goodHigh:!1}
932,{key:"bounceRate",label:"Bounce rate",short:"Bounces",num:e=>e.bounced,den:e=>e.sent,goodHigh:!1}],a4=e=>a8.find(a=>a.key===e)??a8[1];function a3(e){return!(e.isTest||/-test$/i.test(e.campaignId)||/^brake-test$/i.test(e.campaignId))&&(e.accepted>0||e.sent>0)}function a7(){let[e,t]=(0,r.useState)(null),[s,n]=(0,r.useState)(!1),[i,o]=(0,r.useState)([]),[l,c]=(0,r.useState)(null),[d,p]=(0,r.useState)(!1),[h,m]=(0,r.useState)(null),[u,f]=(0,r.useState)({kind:"overview"}),[g,x]=(0,r.useState)("all"),[b,y]=(0,r.useState)(0),[v,w]=(0,r.useState)("all"),[j,k]=(0,r.useState)("clickRate"),N=/permission|insufficient/i.test(h??"");(0,r.useEffect)(()=>(0,eo.onAuthStateChanged)(a5(),e=>{t(e),n(!0)}),[]),(0,r.useEffect)(()=>{if(!e){o([]),c(null),p(!1);return}return(0,eN.onSnapshot)((0,ej.aO)((0,ew.aX)((0,eu.getFirebaseApp)()),a2),e=>{var a;let t,s=e.docs.filter(e=>!Array.isArray(e.data().campaigns)).map(e=>e.id);if(s.length){let e=`Malformed ${a2} docs (no campaigns array): ${s.join(", ")}`;console.error(e),m(e),p(!0);return}let r=e.docs.find(e=>e.id===a6);c(r?(a=r.data(),t=e=>"string"==typeof e?e:null,{provider:"string"==typeof a.provider?a.provider:"SendGrid",reputation:ta(a.reputation),reputationWarnBelow:a9(a.reputationWarnBelow)?a.reputationWarnBelow:90,reputationSeries:te(a.reputationSeries),daily:te(a.daily),alerts:te(a.alerts),readingCount:ta(a.readingCount),firstReadingAt:t(a.firstReadingAt),delta1d:tt(a.delta1d),delta7d:tt(a.delta7d),delta30d:tt(a.delta30d),asOf1d:t(a.asOf1d),asOf7d:t(a.asOf7d),asOf30d:t(a.asOf30d),capturedAt:t(a.capturedAt),lastSyncAt:t(a.lastSyncAt)}):null);let n=e.docs.filter(e=>e.id!==a6).map(e=>{let a=e.data();return{id:e.id,domain:"string"==typeof a.domain&&a.domain?a.domain:e.id,businessName:"string"==typeof a.businessName&&a.businessName?a.businessName:e.id,campaigns:a.campaigns.map(ts),experimentRules:a.experimentRules,lastSyncAt:"string"==typeof a.lastSyncAt?a.lastSyncAt:"",schedule:Array.isArray(a.schedule)?a.schedule.map(ti).filter(Boolean):[],scheduleSyncAt:"string"==typeof a.scheduleSyncAt?a.scheduleSyncAt:""}});n.sort((e,a)=>e.businessName.localeCompare(a.businessName)),o(n),p(!0),m(null)},e=>{let a=`Could not read ${a2} from Firestore: ${e.message}`;console.error(a),m(a),p(!0)})},[e]);let S=(0,r.useMemo)(()=>i.find(e=>e.experimentRules)?.experimentRules??{z:1.96,minDelivered:400},[i]),A=(0,r.useMemo)(()=>i.flatMap(e=>e.campaigns.filter(a3).map(a=>({...a,businessId:e.id,businessName:e.businessName}))),[i]),P=(0,r.useMemo)(()=>{let e=b>0?Date.now()-864e5*b:0;return A.filter(e=>"all"===g||e.businessId===g).filter(e=>"sending"===v?e.inFlight>0:"finished"!==v||0===e.inFlight).filter(a=>!e||(a.firstSentAt?Date.parse(a.firstSentAt):0)>=e).sort((e,a)=>(a.firstSentAt??"").localeCompare(e.firstSentAt??""))},[A,g,b,v]),z=(0,r.useMemo)(()=>tm(P),[P]),T=(0,r.useMemo)(()=>"campaign"!==u.kind&&"experiment"!==u.kind?null:i.find(e=>e.id===u.businessId)??null,[i,u]),F=(0,r.useMemo)(()=>"campaign"===u.kind&&T?T.campaigns.find(e=>e.campaignId===u.campaignId)??null:null,[T,u]),C=(0,r.useMemo)(()=>"experiment"!==u.kind?null:tm(A).find(e=>e.group===u.group)??null,[A,u]);if(!s)return(0,a.jsx)(tB,{});if(!e)return(0,a.jsx)(tB,{error:h,onSignIn:async()=>{m(null);try{let e=new ec.GoogleAuthProvider;e.setCustomParameters({prompt:"select_account"}),await (0,el.signInWithPopup)(a5(),e)}catch(e){m(e.message)}}});if(N)return(0,a.jsx)(tB,{onSignOut:()=>(0,em.signOut)(a5())});let M="overview"!==u.kind;return(0,a.jsxs)("main",{className:"em-root",children:[(0,a.jsx)("style",{children:tG}),(0,a.jsxs)("div",{className:"em-bar",children:[(0,a.jsxs)("span",{className:"em-crumbs",children:[M&&(0,a.jsx)("button",{className:"em-btn em-btn-quiet",onClick:()=>f({kind:"overview"}),children:"â All campaigns"}),"campaign"===u.kind&&F?.variantGroup&&(0,a.jsx)("button",{className:"em-btn em-btn-quiet",onClick:()=>f({kind:"experiment",businessId:u.businessId,group:F.variantGroup}),children:"â Compare variants"})]}),(0,a.jsxs)("span",{className:"em-who",children:[e.email,(0,a.jsx)("button",{className:"em-btn em-btn-quiet",onClick:()=>(0,em.signOut)(a5()),children:"Sign out"})]})]}),(0,a.jsx)(eS.OwnerNav,{active:"emailmarketing"}),h&&(0,a.jsx)("p",{className:"em-error",children:h}),!h&&!d&&(0,a.jsx)("p",{className:"em-muted",children:"Loadingâ¦"}),!h&&d&&"overview"===u.kind&&(0,a.jsxs)(a.Fragment,{children:[l?(0,a.jsx)(tb,{a:l,onOpen:()=>f({kind:"account"})}):(0,a.jsx)("p",{className:"em-muted",children:"No sender-reputation snapshot recorded yet."}),(0,a.jsx)(ty,{businesses:i,syncedAt:i.map(e=>e.scheduleSyncAt).filter(e=>!!e).sort().at(-1)}),(0,a.jsx)(tj,{businesses:i.filter(e=>A.some(a=>a.businessId===e.id)),fBusiness:g,setFBusiness:x,fDays:b,setFDays:y,fStatus:v,setFStatus:w,metric:j,setMetric:k,shown:P.length,total:A.length}),z.length>0&&(0,a.jsxs)("section",{className:"em-section",children:[(0,a.jsx)("h2",{className:"em-h2",children:"Experiments"}),z.map(e=>(0,a.jsx)(tA,{g:e,metric:j,rules:S,onOpen:()=>f({kind:"experiment",businessId:e.arms[0].businessId,group:e.group}),onOpenArm:e=>f({kind:"campaign",businessId:e.businessId,campaignId:e.campaignId})},e.group))]}),(0,a.jsxs)("section",{className:"em-section",children:[(0,a.jsx)("h2",{className:"em-h2",children:"Campaigns"}),0===P.length?(0,a.jsx)("p",{className:"em-muted",children:"No campaign matches these filters."}):(0,a.jsx)("ul",{className:"em-cards",children:P.map(e=>(0,a.jsx)(tk,{c:e,metric:j,onOpen:()=>f({kind:"campaign",businessId:e.businessId,campaignId:e.campaignId})},`${e.businessId}/${e.campaignId}`))})]})]}),!h&&d&&"account"===u.kind&&l&&(0,a.jsx)(tw,{a:l}),!h&&d&&"account"===u.kind&&!l&&(0,a.jsx)("p",{className:"em-muted",children:"No reputation snapshot recorded yet."}),!h&&d&&"campaign"===u.kind&&F&&(0,a.jsx)(tT,{c:F,businessName:T?.businessName??""}),!h&&d&&"campaign"===u.kind&&!F&&(0,a.jsx)("p",{className:"em-muted",children:"That campaign is no longer in the dashboard."}),!h&&d&&"experiment"===u.kind&&C&&(0,a.jsx)(tz,{g:C,rules:S,onOpenArm:e=>f({kind:"campaign",businessId:e.businessId,campaignId:e.campaignId})}),!h&&d&&"experiment"===u.kind&&!C&&(0,a.jsx)("p",{className:"em-muted",children:"That experiment is no longer in the dashboard."})]})}let a9=e=>"number"==typeof e&&Number.isFinite(e),te=e=>Array.isArray(e)?e:[],ta=e=>a9(e)?e:0,tt=e=>a9(e)?e:null;function ts(e){let a;return{...e,campaignId:"string"==typeof e.campaignId?e.campaignId:"",name:"string"==typeof e.name&&e.name?e.name:String(e.campaignId??""),variantGroup:"string"==typeof e.variantGroup?e.variantGroup:null,variant:"string"==typeof e.variant?e.variant:null,targeted:ta(e.targeted),accepted:ta(e.accepted),sent:ta(e.sent),inFlight:ta(e.inFlight),delivered:ta(e.delivered),bounced:ta(e.bounced),dropped:ta(e.dropped),blocked:ta(e.blocked),deferred:ta(e.deferred),opens:ta(e.opens),uniqueOpens:ta(e.uniqueOpens),clicks:ta(e.clicks),uniqueClicks:ta(e.uniqueClicks),conversions:ta(e.conversions),formFills:ta(e.formFills),unsubscribes:ta(e.unsubscribes),spamReports:ta(e.spamReports),scanners:ta(e.scanners),deliveryRate:tt(e.deliveryRate),openRate:tt(e.openRate),clickRate:tt(e.clickRate),clickToOpenRate:tt(e.clickToOpenRate),bounceRate:tt(e.bounceRate),dropRate:tt(e.dropRate),spamRate:tt(e.spamRate),unsubRate:tt(e.unsubRate),conversionRate:tt(e.conversionRate),topLinks:te(e.topLinks),daily:te(e.daily),hourly:te(e.hourly),topDomains:te(e.topDomains),interests:te(e.interests),devices:{since:"string"==typeof(a=e.devices??{}).since?a.since:void 0,firstSeenAt:"string"==typeof a.firstSeenAt?a.firstSeenAt:null,clicksWithUa:ta(a.clicksWithUa),clicksTotal:ta(a.clicksTotal),peopleWithUa:ta(a.peopleWithUa),devices:te(a.devices),clients:te(a.clients)}}}let tr={clicksWithUa:0,clicksTotal:0,peopleWithUa:0,devices:[],clients:[]},tn=new Set(["scheduled","sent","cancelled"]);function ti(e){let a=e??{};if("string"!=typeof a.batchId||!a.batchId)return null;let t="string"==typeof a.status&&tn.has(a.status)?a.status:"scheduled";return{batchId:a.batchId,campaignId:"string"==typeof a.campaignId?a.campaignId:null,arm:"string"==typeof a.arm?a.arm:null,wave:"number"==typeof a.wave?a.wave:null,sendAt:"string"==typeof a.sendAt?a.sendAt:null,sendDatePt:"string"==typeof a.sendDatePt?a.sendDatePt:null,recipientCount:ta(a.recipientCount),status:t,reason:"string"==typeof a.reason?a.reason:null,createdAt:"string"==typeof a.createdAt?a.createdAt:null}}let to=(e,a=1)=>null==e?"â":`${(100*e).toFixed(a)}%`;function tl(e){let[,a,t]=e.slice(0,10).split("-");return`${a}/${t}`}function tc(e){if(!e)return"â";let[a,t]=e.split("T"),s=(t??"").slice(0,5);return s?`${a} ${s} UTC`:a}let td=e=>e.replace(/[_-]+/g," ").replace(/\b\w/g,e=>e.toUpperCase());function tp(e,a,t,s){if(a<=0||s<=0)return null;let r=(e+t)/(a+s),n=Math.sqrt(r*(1-r)*(1/a+1/s));return n>0?(e/a-t/s)/n:null}let th=e=>{var a;let t,s;return 1-(s=1/(1+.3275911*(t=Math.abs(a=Math.abs(e)/Math.SQRT2))),(a<0?-1:1)*(1-((((1.061405429*s-1.453152027)*s+1.421413741)*s-.284496736)*s+.254829592)*s*Math.exp(-t*t)))};function tm(e){let a=new Map;for(let t of e){if(!t.variantGroup||!t.variant)continue;let e=a.get(t.variantGroup)??[];e.push(t),a.set(t.variantGroup,e)}let t=[];return a.forEach((e,a)=>{if(e.length<2)return;e.sort((e,a)=>(e.variant??"").localeCompare(a.variant??""));let s=e.map(e=>
932e.firstSentAt??"").filter(Boolean).sort(),r=e.map(e=>e.lastEventAt??"").filter(Boolean).sort();t.push({group:a,label:td(a.replace(/-test$/,"")),businessName:e[0].businessName,arms:e,firstSentAt:s[0]??null,lastEventAt:r[r.length-1]??null})}),t.sort((e,a)=>(a.firstSentAt??"").localeCompare(e.firstSentAt??"")),t}function tu(e,a,t){return e.arms.map(e=>{let s=a.num(e),r=a.den(e);return{c:e,x:s,n:r,p:r>0?s/r:null,ci:function(e,a,t){if(a<=0)return null;let s=e/a,r=t*t,n=1+r/a,i=(s+r/(2*a))/n,o=t/n*Math.sqrt(s*(1-s)/a+r/(4*a*a));return{lo:Math.max(0,i-o),hi:Math.min(1,i+o)}}(s,r,t)}})}function tf(e,a,t){let s=e.filter(e=>e.n>0);if(s.length<2)return{state:"nodata",best:null,runnerUp:null,z:null,p:null,needed:null,underpowered:!0};let r=a.goodHigh?1:-1,n=[...s].sort((e,a)=>r*((a.p??0)-(e.p??0))),i=n[0],o=n[1],l=s.some(e=>e.n<t.minDelivered),c=tp(i.x,i.n,o.x,o.n),d=function(e,a){let t=Math.abs(e-a);if(!(t>0))return null;let s=(e+a)/2;return Math.ceil(Math.pow(1.959964*Math.sqrt(2*s*(1-s))+.841621*Math.sqrt(e*(1-e)+a*(1-a)),2)/(t*t))}(i.p??0,o.p??0);if(null===c||(i.p??0)===(o.p??0))return{state:"tied",best:i,runnerUp:o,z:c,p:null===c?null:th(c),needed:d,underpowered:l};let p=Math.abs(c)>=t.z;return{state:l?"insufficient":p?"significant":"leading",best:i,runnerUp:o,z:c,p:th(c),needed:d,underpowered:l}}function tg(e){return e.reputation<e.reputationWarnBelow?{cls:"em-bad-band",word:"At risk"}:e.reputation<e.reputationWarnBelow+5?{cls:"em-warn-band",word:"Watch"}:{cls:"em-good-band",word:"Healthy"}}function tx(e){let a=e.daily.slice(-30),t=e=>a.reduce((a,t)=>a+(e(t)||0),0),s=t(e=>e.delivered),r=t(e=>e.bounces),n=t(e=>e.blocks),i=t(e=>e.spamReports),o=t(e=>
932e.invalidEmails);return{delivered:s,bounces:r,blocks:n,spam:i,invalid:o,requests:t(e=>e.requests),total:s+r+n+i+o,days:a.length}}function tb({a:e,onOpen:t}){let s=tg(e),r=tx(e),n=e.reputationSeries,i=r.total>0?(r.bounces+r.blocks+r.spam+r.invalid)/r.total:null;return(0,a.jsxs)("section",{className:`em-health ${s.cls}`,children:[(0,a.jsxs)("div",{className:"em-health-head",children:[(0,a.jsxs)("h2",{className:"em-h2 em-h2-tight",children:[e.provider," account health"]}),(0,a.jsx)("button",{className:"em-btn em-btn-quiet",onClick:t,children:"Full history â"})]}),(0,a.jsxs)("div",{className:"em-health-grid",children:[(0,a.jsxs)("button",{className:"em-health-gauge",onClick:t,title:"Open the full reputation history",children:[(0,a.jsx)(tR,{value:e.reputation,max:100,warn:e.reputationWarnBelow}),(0,a.jsx)("span",{className:"em-gauge-word",children:s.word}),(0,a.jsxs)("span",{className:"em-gauge-sub",children:["reputation · warns below ",e.reputationWarnBelow]})]}),(0,a.jsxs)("div",{className:"em-health-trend",children:[(0,a.jsxs)("div",{className:"em-health-deltas",children:[(0,a.jsx)(tv,{label:"24h",v:e.delta1d}),(0,a.jsx)(tv,{label:"7d",v:e.delta7d}),(0,a.jsx)(tv,{label:"30d",v:e.delta30d})]}),n.length>=2?(0,a.jsx)(tU,{points:n.map(e=>({x:e.date,y:e.reputation})),warnBelow:e.reputationWarnBelow,height:92,suffix:""}):(0,a.jsxs)("p",{className:"em-note em-note-tight",children:["First reading ",e.firstReadingAt??"â",". SendGrid keeps no reputation history, so the line starts the day this began being recorded and fills in one point per morning."]})]}),(0,a.jsxs)("div",{className:"em-health-mix",children:[(0,a.jsxs)("span",{className:"em-mix-title",children:["Where the last ",r.days," days went"]}),r.total>0?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(t$,{slices:[{label:"Delivered",value:r.delivered,cls:"em-s1"},{label:"Bounced",value:r.bounces,cls:"em-s3"},{label:"Blocked",value:r.blocks,cls:"em-s2"}
932,{label:"Invalid",value:r.invalid,cls:"em-s5"},{label:"Spam reports",value:r.spam,cls:"em-s4"}],centreTop:to(r.total?r.delivered/r.total:null,1),centreSub:"delivered"}),(0,a.jsxs)("span",{className:"em-mix-foot",children:[r.total.toLocaleString("en-US")," attempted · ",to(i,2)," did not land"]})]}):(0,a.jsx)("p",{className:"em-note em-note-tight",children:"No account volume recorded in the window yet."})]})]}),e.alerts.length>0&&(0,a.jsx)("ul",{className:"em-alert-list",children:e.alerts.map(e=>(0,a.jsx)("li",{className:"em-alert",children:e},e))}),(0,a.jsxs)("p",{className:"em-note em-note-tight",children:["Captured ",tc(e.capturedAt)," · ",e.readingCount," reading",1===e.readingCount?"":"s"," on record"]})]})}function ty({businesses:e,syncedAt:t}){let s=(0,r.useMemo)(()=>(function(e){let a=new Map;for(let t of e)for(let e of t.schedule??[]){let s=e.sendDatePt??"â",r=a.get(s)??[];r.push({...e,businessName:t.businessName}),a.set(s,r)}let t=Array.from(a.entries()).map(([e,a])=>({date:e,rows:a.sort((e,a)=>(e.sendAt??"").localeCompare(a.sendAt??"")||(e.arm??"").localeCompare(a.arm??""))}));return t.sort((e,a)=>a.date.localeCompare(e.date)),t})(e),[e]),[n,i]=(0,r.useState)(()=>new Set(s.slice(0,1).map(e=>e.date)));return 0===s.length?null:(0,a.jsxs)("section",{className:"em-section",children:[(0,a.jsx)("h2",{className:"em-h2",children:"Send schedule"}),(0,a.jsx)("ul",{className:"em-sched-days",children:s.map(e=>{let t=n.has(e.date),s=e.rows.filter(e=>"cancelled"===e.status),r=e.rows.reduce((e,a)=>e+a.recipientCount,0);return(0,a.jsxs)("li",{className:"em-sched-day",children:[(0,a.jsxs)("button",{className:"em-sched-day-head",onClick:()=>i(a=>{let t=new Set(a);return t.has(e.date)?t.delete(e.date):t.add(e.date),t}),children:[(0,a.jsx)("span",{className:"em-sched-date",children:e.date}),(0,a.jsxs)("span",{className:"em-sched-summary",children:[r.toLocaleString("en-US")," planned",s.length>0&&(0,a.jsxs)("span",{className:"em-sched-bad",children:[" · ",s.length," cancelled"]})]}),(0,a.jsx)("span",{className:"em-sched-caret",children:t?"â¾":"â¸"})]}),t&&(0,a.jsxs)("table",{className:"em-sched-table",children:[(0,a.jsx)("thead",{children:(0,a.jsxs)("tr",{children:[(0,a.jsx)("th",{children:"Time (PT)"}),(0,a.jsx)("th",{children:"Business"}),(0,a.jsx)("th",{children:"Arm"}),(0,a.jsx)("th",{children:"Recipients"}),(0,a.jsx)("th",{children:"Status"}),(0,a.jsx)("th",{children:"Reason"})]})}),(0,a.jsx)("tbody",{children:e.rows.map(e=>{var t;let s="cancelled"===(t=e.status)?{cls:"em-sched-bad",word:"Cancelled"}
932:"sent"===t?{cls:"em-sched-good",word:"Sent"}:{cls:"em-sched-pending",word:"Scheduled"};return(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:function(e){if(!e)return"â";let a=new Date(e);return Number.isNaN(a.getTime())?"â":a.toLocaleString("en-US",{timeZone:"America/Los_Angeles",month:"numeric",day:"numeric",hour:"numeric",minute:"2-digit"})+" PT"}(e.sendAt)}),(0,a.jsx)("td",{children:e.businessName}),(0,a.jsx)("td",{children:e.arm??"â"}),(0,a.jsx)("td",{children:e.recipientCount.toLocaleString("en-US")}),(0,a.jsx)("td",{children:(0,a.jsx)("span",{className:`em-sched-badge ${s.cls}`,children:s.word})}),(0,a.jsx)("td",{className:"em-sched-reason",children:e.reason??"â"})]},e.batchId)})})]})]},e.date)})}),(0,a.jsxs)("p",{className:"em-note em-note-tight",children:["Updated ",tc(t)," · refreshed frequently, independent of the daily campaign rollup below"]})]})}function tv({label:e,v:t}){if(null===t)return(0,a.jsxs)("span",{className:"em-delta em-delta-flat",children:[(0,a.jsx)("span",{className:"em-delta-v",children:"first"}),(0,a.jsx)("span",{className:"em-delta-l",children:e})]});let s=t>0?"up":t<0?"down":"flat",r=t>0?"+":"";return(0,a.jsxs)("span",{className:`em-delta em-delta-${s}`,children:[(0,a.jsxs)("span",{className:"em-delta-v",children:[r,t.toFixed(1)]}),(0,a.jsx)("span",{className:"em-delta-l",children:e})]})}function tw({a:e}){let t=tg(e),s=tx(e),r=e.daily;return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("header",{className:"em-head",children:[(0,a.jsxs)("h1",{children:[e.provider," account health"]}),(0,a.jsxs)("p",{className:"em-lede",children:["The account's standing with SendGrid, and the daily volume that moves it. This is the gate on every campaign below â a reputation under ",e.reputationWarnBelow," costs delivery on mail that has already been paid for and written."]})]}),(0,a.jsxs)("section",{className:`em-panel ${t.cls}`,children:[(0,a.jsxs)("div",{className:"em-health-grid",children:[(0,a.jsxs)("div",{className:"em-health-gauge",children:[(0,a.jsx)(tR,{value:e.reputation,max:100,warn:e.reputationWarnBelow}),(0,a.jsx)("span",{className:"em-gauge-word",children:t.word}),(0,a.jsx)("span",{className:"em-gauge-sub",children:"reputation now"})]}),(0,a.jsxs)("div",{className:"em-health-trend",children:[(0,a.jsxs)("div",{className:"em-health-deltas",children:[(0,a.jsx)(tv,{label:"24h",v:e.delta1d}),(0,a.jsx)(tv,{label:"7d",v:e.delta7d}),(0,a.jsx)(tv,{label:"30d",v:e.delta30d})]}),e.reputationSeries.length>=2?(0,a.jsx)(tU,{points:e.reputationSeries.map(e=>({x:e.date,y:e.reputation})),warnBelow:e.reputationWarnBelow,height:150,suffix:""}):(0,a.jsxs)("p",{className:"em-note em-note-tight",children:["One reading so far (",e.firstReadingAt??"â","). A trend needs two, and SendGrid publishes no history to backfill from â the second point arrives tomorrow morning."]})]})]}),e.alerts.length>0&&(0,a.jsx)("ul",{className:"em-alert-list",children:e.alerts.map(e=>(0,a.jsx)("li",{className:"em-alert",children:e},e))})]}),(0,a.jsxs)("section",{className:"em-panel",children:[(0,a.jsxs)("h2",{className:"em-h2",children:["Outcome mix, last ",s.days," days"]}),(0,a.jsxs)("div",{className:"em-split",children:[(0,a.jsx)(t$,{slices:[{label:"Delivered",value:s.delivered,cls:"em-s1"},{label:"Bounced",value:s.bounces,cls:"em-s3"},{label:"Blocked",value:s.blocks,cls:"em-s2"},{label:"Invalid",value:s.invalid,cls:"em-s5"},{label:"Spam reports",value:s.spam,cls:"em-s4"}],centreTop:to(s.total?s.delivered/s.total:null,1),centreSub:"delivered",size:190,showLegend:!0}),(0,a.jsxs)("dl",{className:"em-metrics",children:[(0,a.jsx)(tO,{label:"Requests",value:s.requests.toLocaleString("en-US")}),(0,a.jsx)(tO,{label:"Delivered",value:s.delivered.toLocaleString("en-US"),strong:!0}),(0,a.jsx)(tO,{label:"Bounce rate",value:to(s.total?s.bounces/s.total:null,2),bad:s.total>0&&s.bounces/s.total>=.05}),(0,a.jsx)(tO,{label:"Spam rate",value:to(s.delivered?s.spam/s.delivered:null,3),bad:s.delivered>0&&s.spam/s.delivered>=.001}),(0,a.jsx)(tO,{label:"Blocked",value:s.blocks.toLocaleString("en-US"),bad:s.blocks>0}
932),(0,a.jsx)(tO,{label:"Invalid",value:s.invalid.toLocaleString("en-US"),bad:s.invalid>0})]})]})]}),r.length>0&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("section",{className:"em-panel",children:[(0,a.jsx)("h2",{className:"em-h2",children:"Daily send volume"}),(0,a.jsx)(tL,{rows:r.map(e=>({label:tl(e.date),values:[{v:e.delivered,cls:"em-s1",name:"Delivered"},{v:e.bounces,cls:"em-s3",name:"Bounced"},{v:e.blocks+e.invalidEmails,cls:"em-s2",name:"Blocked / invalid"}]})),height:140})]}),(0,a.jsxs)("section",{className:"em-panel",children:[(0,a.jsx)("h2",{className:"em-h2",children:"Bounce rate against the throttling threshold"}),(0,a.jsx)(tU,{points:r.map(e=>({x:e.date,y:(e.bounceRate??0)*100})),warnAbove:5,height:130,suffix:"%"}),(0,a.jsxs)("p",{className:"em-note",children:["Above ",to(.05,0)," a sending account gets throttled. This is the same line book_campaign_plan.py refuses to raise daily volume through."]})]})]})]})}function tj(e){let{businesses:t,fBusiness:s,setFBusiness:r,fDays:n,setFDays:i,fStatus:o,setFStatus:l,metric:c,setMetric:d,shown:p,total:h}=e;return(0,a.jsxs)("section",{className:"em-filters",children:[(0,a.jsxs)("label",{className:"em-field",children:[(0,a.jsx)("span",{className:"em-field-l",children:"Business"}),(0,a.jsxs)("select",{className:"em-select",value:s,onChange:e=>r(e.target.value),children:[(0,a.jsx)("option",{value:"all",children:"All businesses"}),t.map(e=>(0,a.jsx)("option",{value:e.id,children:e.businessName},e.id))]})]}),(0,a.jsxs)("label",{className:"em-field",children:[(0,a.jsx)("span",{className:"em-field-l",children:"Period"}),(0,a.jsxs)("select",{className:"em-select",value:String(n),onChange:e=>i(Number(e.target.value)),children:[(0,a.jsx)("option",{value:"7",children:"Last 7 days"}),(0,a.jsx)("option",{value:"30",children:"Last 30 days"}),(0,a.jsx)("option",{value:"90",children:"Last 90 days"}),(0,a.jsx)("option",{value:"0",children:"All time"})]})]}),(0,a.jsxs)("span",{className:"em-field",children:[(0,a.jsx)("span",{className:"em-field-l",children:"Status"}),(0,a.jsx)("span",{className:"em-toggle",children:[["all","All"],["sending","Still sending"],["finished","Finished"]].map(([e,t])=>(0,a.jsx)("button",{className:`em-toggle-b ${o===e?"em-toggle-on":""}`,onClick:()=>l(e),children:t},e))})]}),(0,a.jsxs)("label",{className:"em-field",children:[(0,a.jsx)("span",{className:"em-field-l",children:"Compare by"}),(0,a.jsx)("select",{className:"em-select",value:c,onChange:e=>d(e.target.value),children:a8.map(e=>(0,a.jsx)("option",{value:e.key,children:e.label},e.key))})]}),(0,a.jsxs)("span",{className:"em-filter-count",children:[p," of ",h]})]})}function tk({c:e,metric:t,onOpen:s}){var r;let n=a4(t),i=(r=e).inFlight>0?{label:`${r.inFlight.toLocaleString("en-US")} still to send`,cls:"em-chip-live"}:null,o=e.daily??[],l=(e.bounceRate??0)>=.05||(e.spamRate??0)>=.001||(e.unsubRate??0)>=.005;return(0,a.jsx)("li",{className:`em-card ${l?"em-card-alarm":""}`,children:(0,a.jsxs)("button",{className:"em-card-btn",onClick:s,children:[(0,a.jsxs)("span",{className:"em-card-head",children:[(0,a.jsx)("span",{className:"em-card-name",children:e.name}),e.variant&&(0,a.jsx)("span",{className:"em-chip em-chip-var",children:e.variant}),i&&(0,a.jsx)("span",{className:`em-chip ${i.cls}`,children:i.label}),(0,a.jsx)("span",{className:"em-open-hint",children:"Open â"})]}),(0,a.jsxs)("span",{className:"em-card-sub",children:[e.businessName," · ",tc(e.firstSentAt)]}),(0,a.jsx)(tN,{c:e}),(0,a.jsxs)("span",{className:"em-card-foot",children:[(0,a.jsxs)("span",{className:"em-card-metric",children:[(0,a.jsx)("span",{className:"em-card-metric-v",children:to(e[n.key],2)}),(0,a.jsx)("span",{className:"em-card-metric-l",children:n.label.toLowerCase()})]}),o.length>=2&&(0,a.jsx)(t_,{points:o.map(e=>"openRate"===n.key?e.opens:e.clicks)}),l&&(0,a.jsx)("span",{className:"em-chip em-chip-alarm",children:"Check signals"})]})]})})}function tN({c:e}){let t=Math.max(e.sent,1),s=[{v:e.delivered,cls:"em-s1",name:"Delivered"},{v:e.uniqueOpens,cls:"em-s4",name:"Opened"},{v:e.uniqueClicks,cls:"em-s2",name:"Clicked"}];return(0,a.jsxs)("span",{className:"em-mini",children:[s.map(e=>(0,a.jsxs)("span",{className:"em-mini-row",children:[(0,a.jsx)("span",{className:"em-mini-l",children:e.name}),(0,a.jsx)("span",{className:"em-mini-track",children:(0,a.jsx)("span",{className:`em-mini-fill ${e.cls}`,style:{width:`${Math.min(100,e.v/t*100)}%`}})}),(0,a.jsx)("span",{className:"em-mini-v",children:e.v.toLocaleStr
932ing("en-US")})]},e.name)),e.inFlight>0&&(0,a.jsxs)("span",{className:"em-mini-row",children:[(0,a.jsx)("span",{className:"em-mini-l em-faint",children:"Queued"}),(0,a.jsx)("span",{className:"em-mini-track",children:(0,a.jsx)("span",{className:"em-mini-fill em-mini-queued",style:{width:`${Math.min(100,e.inFlight/Math.max(e.accepted,1)*100)}%`}})}),(0,a.jsx)("span",{className:"em-mini-v em-faint",children:e.inFlight.toLocaleString("en-US")})]})]})}function tS(e,a,t){let s=e=>e?.c.variant??"?";switch(e.state){case"nodata":return{cls:"em-verdict-none",text:"Not enough delivered mail to compare anything yet."};case"tied":return{cls:"em-verdict-none",text:`${a.label} is identical across the leading arms â no difference to test.`};case"significant":return{cls:"em-verdict-win",text:`${s(e.best)} beats ${s(e.runnerUp)} on ${a.label.toLowerCase()} (p = ${e.p.toFixed(3)}). Statistically significant.`};case"insufficient":return{cls:"em-verdict-wait",text:`${s(e.best)} is ahead, but every arm needs ${t.minDelivered.toLocaleString("en-US")} delivered before this counts`+(e.needed?` â a gap this size needs roughly ${e.needed.toLocaleString("en-US")} per arm to confirm.`:".")};default:return{cls:"em-verdict-wait",text:`${s(e.best)} leads, but the gap is inside the noise (p = ${e.p.toFixed(3)})`+(e.needed?` â about ${e.needed.toLocaleString("en-US")} delivered per arm would settle it.`:".")}}}function tA({g:e,metric:t,rules:s,onOpen:r,onOpenArm:n}){let i=a4(t),o=tu(e,i,s.z),l=tf(o,i,s),c=tS(l,i,s);return(0,a.jsxs)("section",{className:"em-panel em-exp",children:[(0,a.jsxs)("div",{className:"em-exp-head",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("h3",{className:"em-exp-title",children:e.label}),(0,a.jsxs)("p",{className:"em-exp-sub",children:[e.arms.length," variants · ",e.businessName," · started ",tc(e.firstSentAt)]})]}),(0,a.jsx)("button",{className:"em-btn em-btn-quiet",onClick:r,children:"Compare in detail â"})]}),(0,a.jsx)(tP,{stats:o,m:i,best:"significant"===l.state?l.best:null,onPick:n}),(0,a.jsx)("p",{className:`em-verdict ${c.cls}`,children:c.text})]})}function tP({stats:e,m:t,best:s,onPick:r}){let n=Math.max(34*e.length+22,60),i=Math.max(...e.map(e=>e.ci?.hi??e.p??0),1e-4);return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("svg",{className:"em-chart",viewBox:`0 0 640 ${n}`,role:"img","aria-label":`${t.label} by variant`,children:e.map((e,n)=>{let o=34*n+8,l=e.p??0,c=s?.c.campaignId===e.c.campaignId;return(0,a.jsxs)("g",{className:r?"em-ci-row":void 0,onClick:()=>r?.(e.c),children:[(0,a.jsx)("title",{children:`${e.c.name} 933${t.label}: ${to(e.p,2)} (${e.x.toLocaleString("en-US")} of ${e.n.toLocaleString("en-US")})`+(e.ci?` 93495% interval: ${to(e.ci.lo,2)} â ${to(e.ci.hi,2)}`:"")}),(0,a.jsx)("text",{className:"em-ci-label",x:108,y:o+15,children:e.c.variant??e.c.campaignId}),(0,a.jsx)("rect",{className:"em-ci-track",x:118,y:o+5,width:426,height:14,rx:3}),(0,a.jsx)("rect",{className:`em-ci-bar ${c?"em-ci-win":""} ${t.goodHigh?"":"em-ci-neg"}`,x:118,y:o+5,width:Math.max(l/i*426,2*(e.n>0)),height:14,rx:3}),e.ci&&e.n>0&&(0,a.jsxs)("g",{className:"em-ci-whisk",children:[(0,a.jsx)("line",{x1:118+e.ci.lo/i*426,y1:o+12,x2:118+e.ci.hi/i*426,y2:o+12}),(0,a.jsx)("line",{x1:118+e.ci.lo/i*426,y1:o+6,x2:118+e.ci.lo/i*426,y2:o+18}),(0,a.jsx)("line",{x1:118+e.ci.hi/i*426,y1:o+6,x2:118+e.ci.hi/i*426,y2:o+18})]}),(0,a.jsxs)("text",{className:"em-ci-value",x:634,y:o+15,children:[to(e.p,2)," · ",e.x.toLocaleString("en-US"),"/",e.n.toLocaleString("en-US")]})]},e.c.campaignId)})}),(0,a.jsxs)("p",{className:"em-note em-note-tight",children:["Bars are ",t.label.toLowerCase(),"; the line through each is its 95% confidence interval. Overlapping intervals mean the difference is not established.",t.soft?" Open and click tracking is soft â Apple Mail Privacy Protection opens mail on the reader's behalf.":""]})]})}function tz({g:e,rules:t,onOpenArm:s}){let[n,i]=(0,r.useState)("clickRate"),o=a4(n),l=tu(e,o,t.z),c=tf(l,o,t),d=tS(c,o,t),p=o.goodHigh?1:-1,h=[...l].filter(e=>e.n>0).sort((e,a)=>p*((a.p??0)-(e.p??0))),m=h[0];return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("header",{className:"em-head",children:[(0,a.jsx)("h1",{children:e.label}),(0,a.jsxs)("p",{className:"em-lede",children:[e.arms.length," variants, ",e.businessName,". Test period ",tc(e.firstSentAt)," â"," ",tc(e.lastEventAt),"."]})]}),(0,a.jsxs)("section",{className:"em-panel",children:[(0,a.jsxs)("div",{className:"em-exp-head",children:[(0,a.jsx)("h2",{className:"em-h2 em-h2-tight",children:"Variant comparison"}),(0,a.jsx)("span",{className:"em-toggle",children:a8.map(e=>(0,a.jsx)("button",{className:`em-toggle-b ${n===e.key?"em-toggle-on":""}`,onClick:()=>i(e.key),children:e.short},e.key))})]}),(0,a.jsx)(tP,{stats:l,m:o,best:"significant"===c.state?c.best:null,onPick:s}),(0,a.jsx)("p",{className:`em-verdict ${d.cls}`,children:d.text})]}),m&&h.length>1&&(0,a.jsxs)("section",{className:"em-panel",children:[(0,a.jsxs)("h2",{className:"em-h2",children:["Every variant against ",m.c.variant]}),(0,a.jsx)("div",{className:"em-table-wrap",children:(0,a.jsxs)("table",{className:"em-table",children:[(0,a.jsx)("thead",{children:(0,a.jsxs)("tr",{children:[(0,a.jsx)("th",{children:"Variant"}),(0,a.jsx)("th",{className:"em-num",children:o.label}),(0,a.jsx)("th",{className:"em-num",children:"Sample"}),(0,a.jsx)("th",{className:"em-num",children:"Difference"}),(0,a.jsx)("th",{className:"em-num",children:"z"}),(0,a.jsx)("th",{className:"em-num",children:"p"}),(0,a.jsx)("th",{children:"Reading"})]})}),(0,a.jsx)("tbody",{children:h.map(e=>{let r=e.c.campaignId===m.c.campaignId,n=r?null:tp(m.x,m.n,e.x,e.n),i=null===n?null:th(n),o=(m.p??0)-(e.p??0),l=null!==n&&Math.abs(n)>=t.z&&!c.underpowered;return(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:(0,a.jsxs)("button",{className:"em-linkbtn",onClick:()=>s(e.c),children:[e.c.variant," ",r&&(0,a.jsx)("span",{className:"em-chip em-chip-lead",children:"best"})]})}),(0,a.jsx)("td",{className:"em-num",children:to(e.p,2)}
934),(0,a.jsxs)("td",{className:"em-num",children:[e.x.toLocaleString("en-US"),"/",e.n.toLocaleString("en-US")]}),(0,a.jsx)("td",{className:"em-num",children:r?"â":`${o>=0?"+":""}${(100*o).toFixed(2)}pp`}),(0,a.jsx)("td",{className:"em-num",children:null===n?"â":n.toFixed(2)}),(0,a.jsx)("td",{className:"em-num",children:null===i?"â":i.toFixed(3)}),(0,a.jsx)("td",{className:l?"em-good":"em-faint",children:r?"reference":l?"real difference":"within noise"})]},e.c.campaignId)})})]})}),(0,a.jsxs)("p",{className:"em-note",children:["Two-proportion z-test, two-sided. A difference counts as real only when |z| ⥠",t.z," ","AND every arm has at least ",t.minDelivered.toLocaleString("en-US")," delivered â the same bar book_campaign_plan.py uses before it moves volume, so nothing here is a verdict the allocator would ignore."]})]}),(0,a.jsxs)("section",{className:"em-panel",children:[(0,a.jsx)("h2",{className:"em-h2",children:"Where each variant differs"}),(0,a.jsx)(tq,{groups:e.arms.map(e=>({label:e.variant??e.campaignId,values:[{v:(e.openRate??0)*100,cls:"em-s4",name:"Open %"},{v:(e.clickRate??0)*100,cls:"em-s1",name:"Click %"},{v:(e.conversionRate??0)*100,cls:"em-s2",name:"Conversion %"},{v:(e.unsubRate??0)*100,cls:"em-s3",name:"Unsub %"}]})),height:170,suffix:"%"})]}),(0,a.jsxs)("section",{className:"em-panel",children:[(0,a.jsx)("h2",{className:"em-h2",children:"What each variant said"}),(0,a.jsx)("div",{className:"em-table-wrap",children:(0,a.jsxs)("table",{className:"em-table",children:[(0,a.jsx)("thead",{children:(0,a.jsxs)("tr",{children:[(0,a.jsx)("th",{children:"Variant"}),(0,a.jsx)("th",{children:"Subject line"}),(0,a.jsx)("th",{className:"em-num",children:"Sent"}),(0,a.jsx)("th",{className:"em-num",children:"Queued"}),(0,a.jsx)("th",{children:"First sent"})]})}),(0,a.jsx)("tbody",{children:e.arms.map(e=>(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:(0,a.jsx)("button",{className:"em-linkbtn",onClick:()=>s(e),children:e.variant})}),(0,a.jsx)("td",{children:e.subject??"â"}),(0,a.jsx)("td",{className:"em-num",children:e.sent.toLocaleString("en-US")}),(0,a.jsx)("td",{className:"em-num",children:e.inFlight?e.inFlight.toLocaleString("en-US"):"â"}),(0,a.jsx)("td",{className:"em-mono",children:tc(e.firstSentAt)})]},e.campaignId))})]})})]})]})}function tT({c:e,businessName:t}){let[s,n]=(0,r.useState)("hour"),i=e.topLinks??[],o=e.topDomains??[],l=e.interests??[],c=e.hourly??[],d=e.daily??[],p=e.devices??tr,h=e.conversionKind??"none";return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("header",{className:"em-head",children:[(0,a.jsxs)("h1",{children:[e.name,e.variant&&(0,a.jsxs)("span",{className:"em-chip em-chip-var",children:["Variant ",e.variant]})]}),e.subject&&(0,a.jsxs)("p",{className:"em-subject",children:["â",e.subject,"â"]}),(0,a.jsxs)("p",{className:"em-lede",children:[t," · first sent ",tc(e.firstSentAt)," · last event ",tc(e.lastEventAt)]})]}),(0,a.jsxs)("section",{className:"em-panel",children:[(0,a.jsx)("h2",{className:"em-h2",children:"Send pipeline"}),(0,a.jsx)(tI,{c:e}),e.inFlight>0&&(0,a.jsxs)("p",{className:"em-note",children:[e.inFlight.toLocaleString("en-US")," of ",e.accepted.toLocaleString("en-US")," accepted messages are scheduled and have not gone out. They are not counted as sent, and no rate below is divided by them."]})]}),(0,a.jsxs)("section",{className:"em-panel",children:[(0,a.jsxs)("h2",{className:"em-h2",children:["What happened to the ",e.sent.toLocaleString("en-US")," that went out"]}),(0,a.jsx)(tD,{c:e}),(0,a.jsxs)("dl",{className:"em-metrics",children:[(0,a.jsx)(tO,{label:"Delivery",value:to(e.deliveryRate),strong:!0}),(0,a.jsx)(tO,{label:"Open",value:to(e.openRate),soft:!0}),(0,a.jsx)(tO,{label:"Click",value:to(e.clickRate),strong:!0}),(0,a.jsx)(tO,{label:"Click-to-open",value:to(e.clickToOpenRate),soft:!0}),(0,a.jsx)(tO,{label:"form"===h?"Form fills":"retailer"===h?"To retailer":"Conversion",value:"none"===h?"n/a":to(e.conversionRate),weak:"none"===h})]}),(0,a.jsxs)("p",{className:"em-note",children:["Open rate is indicative, not measured: Apple Mail Privacy Protection opens messages on the reader's behalf."," ",e.scanners>
9340&&`${e.scanners.toLocaleString("en-US")} recipient${1===e.scanners?"":"s"} clicked like a security scanner (every link at once) and ${1===e.scanners?"is":"are"} excluded from click counts.`," ","retailer"===h&&"A conversion here is a click that reached a bookstore â the last step this pipeline can observe, since retailers send no purchase callback.","form"===h&&"A conversion here is a UTM-attributed contact-form fill.","none"===h&&"This campaign has no conversion destination to measure."]})]}),(0,a.jsxs)("section",{className:`em-panel ${tM(e)?"em-bad-band":""}`,children:[(0,a.jsx)("h2",{className:"em-h2",children:"Negative signals"}),(0,a.jsxs)("div",{className:"em-split",children:[(0,a.jsx)(tW,{c:e}),(0,a.jsxs)("dl",{className:"em-metrics",children:[(0,a.jsx)(tO,{label:"Bounced",value:`${e.bounced.toLocaleString("en-US")} \xb7 ${to(e.bounceRate,2)}`,bad:(e.bounceRate??0)>=.05}),(0,a.jsx)(tO,{label:"Dropped",value:`${e.dropped.toLocaleString("en-US")} \xb7 ${to(e.dropRate,2)}`,weak:0===e.dropped}),(0,a.jsx)(tO,{label:"Spam reports",value:`${e.spamReports.toLocaleString("en-US")} \xb7 ${to(e.spamRate,3)}`,bad:(e.spamRate??0)>=.001}),(0,a.jsx)(tO,{label:"Unsubscribed",value:`${e.unsubscribes.toLocaleString("en-US")} \xb7 ${to(e.unsubRate,2)}`,bad:(e.unsubRate??0)>=.005}),(0,a.jsx)(tO,{label:"Blocked",value:e.blocked.toLocaleString("en-US"),weak:0===e.blocked}),(0,a.jsx)(tO,{label:"Deferred",value:e.deferred.toLocaleString("en-US"),weak:0===e.deferred})]})]}),(0,a.jsxs)("p",{className:"em-note",children:["Thresholds shown in red: bounce ",to(.05,0),", spam ",to(.001,1),", unsubscribe ",to(.005,1),". Unsubscribes are attributed by last touch from the contacts suppression list â the footer link, the one-click header and SendGrid's own suppression all land there, and none of them carry a campaign id."]})]}),(c.length>1||d.length>1)&&(0,a.jsxs)("section",{className:"em-panel",children:[(0,a.jsxs)("div",{className:"em-exp-head",children:[(0,a.jsx)("h2",{className:"em-h2 em-h2-tight",children:"Engagement over time"}),(0,a.jsxs)("span",{className:"em-toggle",children:[(0,a.jsx)("button",{className:`em-toggle-b ${"hour"===s?"em-toggle-on":""}`,onClick:()=>n("hour"),disabled:c.length<2,children:"Hours since send"}),(0,a.jsx)("button",{className:`em-toggle-b ${"day"===s?"em-toggle-on":""}`,onClick:()=>n("day"),disabled:d.length<2,children:"By day"})]})]}),"hour"===s&&c.length>1?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(tL,{rows:c.map(e=>({label:`${e.hour}h`,values:[{v:e.opens,cls:"em-s4",name:"Opened"},{v:e.clicks,cls:"em-s1",name:"Clicked"}]})),height:150,grouped:!0}),(0,a.jsx)("p",{className:"em-note",children:"Hours since this campaign's first delivery. Calendar days split a late-night send across two bars and make one spike look like two quiet days."})]}):(0,a.jsx)(tL,{rows:d.map(e=>({label:tl(e.date),values:[{v:e.delivered,cls:"em-s1",name:"Delivered"},{v:e.opens,cls:"em-s4",name:"Opened"},{v:e.clicks,cls:"em-s2",name:"Clicked"},{v:e.unsubscribes,cls:"em-s3",name:"Unsubscribed"}]})),height:150,grouped:!0})]}),o.length>0&&(0,a.jsxs)("section",{className:"em-panel",children:[(0,a.jsx)("h2",{className:"em-h2",children:"Mailbox providers"}),(0,a.jsx)(tq,{groups:o.slice(0,8).map(e=>({label:e.domain,values:[{v:e.delivered&&e.sent?e.delivered/e.sent*100:0,cls:"em-s1",name:"Delivered %"},{v:e.delivered?e.opens/e.delivered*100:0,cls:"em-s4",name:"Opened %"},{v:e.delivered?e.clicks/e.delivered*100:0,cls:"em-s2",name:"Clicked %"},{v:e.sent?e.bounces/e.sent*100:0,cls:"em-s3",name:"Bounced %"}]})),height:200,suffix:"%"}),(0,a.jsx)("p",{className:"em-note",children:"Deliverability is not uniform. The same send can be waved through by one provider and filtered by another, and an account-level average hides which one is which."}),(0,a.jsx)("div",{className:"em-table-wrap",children:(0,a.jsxs)("table",{className:"em-table",children:[(0,a.jsx)("thead",{children:(0,a.jsxs)("tr",{children:[(0,a.jsx)("th",{children:"Provider"}),(0,a.jsx)("th",{className:"em-num",children:"Sent"}),(0,a.jsx)("th",{className:"em-num",children:"Delivered"}),(0,a.jsx)("th",{className:"em-num",children:"Opened"}),(0,a.jsx)("th",{className:"em-num",children:"Clicked"}),(0,a.jsx)("th",{className:"em-num",children:"Bounced"})]})}),(0,a.jsx)("tbody",{children:o.map(e=>(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{className:"em-mono",children:e.domain}),(0,a.jsx)("td",{className:"em-num",children:(e.sent??0).toLocaleStr
934ing("en-US")}),(0,a.jsx)("td",{className:"em-num",children:e.delivered.toLocaleString("en-US")}),(0,a.jsx)("td",{className:"em-num",children:e.opens.toLocaleString("en-US")}),(0,a.jsx)("td",{className:"em-num",children:e.clicks.toLocaleString("en-US")}),(0,a.jsx)("td",{className:`em-num ${e.bounces>0?"em-bad":""}`,children:e.bounces.toLocaleString("en-US")})]},e.domain))})]})})]}),i.length>0&&(0,a.jsxs)("section",{className:"em-panel",children:[(0,a.jsx)("h2",{className:"em-h2",children:"Which links earned the clicks"}),(0,a.jsx)("ul",{className:"em-links-list",children:i.map(e=>{var t,s;let r,n,o;return(0,a.jsx)(tE,{label:`${(t=e.url,(r=/^https?:\/\/([^/?#]+)/i.exec(t))?r[1].replace(/^www\./,""):t)}${(s=e.url,(o=(n=/^https?:\/\/[^/?#]+([^?#]*)/i.exec(s))?n[1]:"")&&"/"!==o?o:"/")}`,value:e.uniqueClicks,max:Math.max(...i.map(e=>e.uniqueClicks),1),note:`${e.uniqueClicks.toLocaleString("en-US")} people \xb7 ${e.clicks.toLocaleString("en-US")} clicks`,cls:"store"===e.kind?"em-s2":"em-s1",href:e.url},e.url)})}),(0,a.jsxs)("ul",{className:"em-legend",children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("span",{className:"em-swatch em-s2"})," Retailer"]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("span",{className:"em-swatch em-s1"})," Our site"]})]}),(0,a.jsx)("p",{className:"em-note",children:"Human, delivered-to clicks only â the same population as the headline click rate, so this list and the number above it can never disagree."})]}),l.length>0&&(0,a.jsxs)("section",{className:"em-panel",children:[(0,a.jsx)("h2",{className:"em-h2",children:"What the readers were interested in"}),(0,a.jsxs)("div",{className:"em-split",children:[(0,a.jsx)(t$,{slices:l.slice(0,6).map((e,a)=>({label:td(e.interest),value:e.people,cls:`em-s${a%5+1}`})),centreTop:l.reduce((e,a)=>e+a.people,0).toLocaleString("en-US"),centreSub:"clickers",size:180,showLegend:!0}),(0,a.jsx)("ul",{className:"em-links-list em-grow",children:l.map(e=>(0,a.jsx)(tE,{label:td(e.interest),value:e.people,max:Math.max(...l.map(e=>e.people),1),note:`${e.people.toLocaleString("en-US")} people \xb7 ${e.clicks.toLocaleString("en-US")} clicks`,cls:"em-s1"},e.interest))})]}),(0,a.jsx)("p",{className:"em-note",children:"Topic is tagged on the redirect, so this says what a reader wanted rather than only that they wanted something."})]}),p.clicksTotal>0&&(0,a.jsx)(tC,{d:p})]})}let tF={phone:"Phone",tablet:"Tablet",computer:"Computer",machine:"Automated",unknown:"Unclassified"};function tC({d:e}){let t=e.clicksTotal>0?e.clicksWithUa/e.clicksTotal:0,s=e.clicksWithUa>0&&t>=.2;return(0,a.jsxs)("section",{className:"em-panel",children:[(0,a.jsx)("h2",{className:"em-h2",children:"What they read it on"}),s?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"em-split",children:[(0,a.jsx)(t$,{slices:e.devices.map((e,a)=>{let t;return{label:tF[t=e.label]??td(t),value:e.people,cls:`em-s${a%5+1}`}}),centreTop:e.peopleWithUa.toLocaleString("en-US"),centreSub:"clickers",size:180,showLegend:!0}),e.clients.length>0&&(0,a.jsx)("ul",{className:"em-links-list em-grow",children:e.clients.map(t=>(0,a.jsx)(tE,{label:t.label,value:t.people,max:Math.max(...e.clients.map(e=>e.people),1),note:`${t.people.toLocaleString("en-US")} people \xb7 ${t.clicks.toLocaleString("en-US")} clicks`,cls:"em-s1"},t.label))})]}),(0,a.jsxs)("p",{className:"em-note",children:["Clicks only â an open tells you about Apple's privacy proxy or Google's image cache, not about a person. Based on ",e.clicksWithUa.toLocaleString("en-US")," of ",e.clicksTotal.toLocaleString("en-US")," clicks (",to(t,0),"); the rest predate device capture",e.firstSeenAt?`, which started ${tc(e.firstSeenAt)}`:"","."]})]}):(0,a.jsx)("p",{className:"em-note",children:0===e.clicksWithUa?`Not recorded for this campaign. Device is read off the click redirect and only stored from ${e.since??"recently"} onward; none of these ${e.clicksTotal.toLocaleString("en-US")} clicks carry one.`:`Only ${e.clicksWithUa.toLocaleString("en-US")} of ${e.clicksTotal.toLocaleString("en-US")} clicks (${to(t,0)}) carry a device, so no breakdown is drawn â the shape of ${e.clicksWithUa.toLocaleStr
934ing("en-US")} clicks would not be the shape of the campaign. Device is stored from ${e.since??"recently"} onward.`})]})}let tM=e=>(e.bounceRate??0)>=.05||(e.spamRate??0)>=.001||(e.unsubRate??0)>=.005;function tI({c:e}){let t=Math.max(e.targeted,e.accepted,e.sent,1),s=[{name:"Targeted",v:e.targeted,cls:"em-s5",hint:"recipients we asked SendGrid to send to"},{name:"Accepted",v:e.accepted,cls:"em-s4",hint:"SendGrid took the message into its queue"},{name:"Sent",v:e.sent,cls:"em-s1",hint:"SendGrid actually attempted delivery"},{name:"Still queued",v:e.inFlight,cls:"em-s2",hint:"scheduled, not yet gone out"}];return(0,a.jsx)("ul",{className:"em-pipe",children:s.map(e=>(0,a.jsxs)("li",{className:"em-pipe-row",title:e.hint,children:[(0,a.jsx)("span",{className:"em-pipe-l",children:e.name}),(0,a.jsx)("span",{className:"em-pipe-track",children:(0,a.jsx)("span",{className:`em-pipe-fill ${e.cls}`,style:{width:`${e.v/t*100}%`}})}),(0,a.jsx)("span",{className:"em-pipe-v",children:e.v.toLocaleString("en-US")})]},e.name))})}function tD({c:e}){let t=[{name:"Sent",v:e.sent},{name:"Delivered",v:e.delivered},{name:"Opened",v:e.uniqueOpens},{name:"Clicked",v:e.uniqueClicks},{name:"form"===e.conversionKind?"Filled the form":"Reached a store",v:e.conversions}].filter((a,t)=>t<4||"none"!==e.conversionKind),s=Math.max(e.sent,1),r=40*t.length+8;return(0,a.jsx)("svg",{className:"em-chart",viewBox:`0 0 640 ${r}`,role:"img","aria-label":"Campaign funnel",children:t.map((e,r)=>{let n=40*r+4,i=0===r?null:t[r-1].v,o=i&&i>0?e.v/i:null;return(0,a.jsxs)("g",{children:[(0,a.jsx)("title",{children:`${e.name}: ${e.v.toLocaleString("en-US")}`+(null===o?"":` 935${to(o,1)} of ${t[r-1].name.toLowerCase()}`)}),(0,a.jsx)("text",{className:"em-fn-label",x:118,y:n+20,children:e.name}),(0,a.jsx)("rect",{className:"em-fn-track",x:128,y:n+5,width:404,height:22,rx:4}),(0,a.jsx)("rect",{className:"em-fn-bar",x:128,y:n+5,width:Math.max(e.v/s*404,3*(e.v>0)),height:22,rx:4}),(0,a.jsx)("text",{className:"em-fn-val",x:540,y:n+20,children:e.v.toLocaleString("en-US")}),null!==o&&(0,a.jsx)("text",{className:"em-fn-step",x:634,y:n+20,children:to(o,1)})]},e.name)})})}function tW({c:e}){let t=[{name:"Bounce",v:e.bounceRate,limit:.05},{name:"Spam",v:e.spamRate,limit:.001},{name:"Unsubscribe",v:e.unsubRate,limit:.005}],s=40*t.length+6;return(0,a.jsxs)("svg",{className:"em-chart em-chart-narrow",viewBox:`0 0 380 ${s}`,role:"img","aria-label":"Negative signals against their thresholds",children:[t.map((e,t)=>{let s=40*t+4,r=2*e.limit,n=e.v??0,i=n>=e.limit;return(0,a.jsxs)("g",{children:[(0,a.jsx)("title",{children:`${e.name}: ${to(e.v,3)} (threshold ${to(e.limit,3)})`}),(0,a.jsx)("text",{className:"em-fn-label",x:86,y:s+19,children:e.name}),(0,a.jsx)("rect",{className:"em-fn-track",x:96,y:s+6,width:212,height:16,rx:3}),(0,a.jsx)("rect",{className:`em-sig-bar ${i?"em-sig-over":""}`,x:96,y:s+6,width:Math.max(212*Math.min(n/r,1),2*(n>0)),height:16,rx:3}),(0,a.jsx)("line",{className:"em-band-line",x1:202,y1:s+2,x2:202,y2:s+26}),(0,a.jsx)("text",{className:`em-fn-val ${i?"em-svg-bad":""}`,x:316,y:s+19,children:to(e.v,e.limit<.01?3:2)})]},e.name)}),(0,a.jsx)("text",{className:"em-axis em-axis-x",x:202,y:s-1,children:"threshold"})]})}function tO({label:e,value:t,strong:s,weak:r,bad:n,soft:i}){return(0,a.jsxs)("div",{className:`em-metric ${n?"em-metric-bad":s?"em-metric-strong":r?"em-metric-weak":""}`,children:[(0,a.jsxs)("dt",{children:[e,i&&(0,a.jsx)("span",{className:"em-soft-mark",title:"Indicative â inflated by privacy proxies",children:"~"})]}),(0,a.jsx)("dd",{children:t})]})}function tE({label:e,value:t,max:s,note:r,cls:n="em-s1",href:i}){return(0,a.jsxs)("li",{className:"em-rank-row",children:[(0,a.jsx)("span",{className:"em-rank-label",title:i??e,children:i?(0,a.jsx)("a",{className:"em-rank-a",href:i,target:"_blank",rel:"noreferrer noopener",children:e}):e}),(0,a.jsx)("span",{className:"em-rank-track",children:(0,a.jsx)("span",{className:`em-rank-fill ${n}`,style:{width:`${s>0?t/s*100:0}%`}})}),(0,a.jsx)("span",{className:"em-rank-val",children:r})]})}function tR({value:e,max:t,warn:s}){let r=.82*Math.PI,n=2.18*Math.PI,i=e=>{let a=r+(n-r)*e;return[66+52*Math.cos(a),72+52*Math.sin(a)]},o=(e,a)=>{let[t,s]=i(e),[r,n]=i(a),o=+(a-e>.5);return`M ${t.toFixed(2)} ${s.toFixed(2)} A 52 52 0 ${o} 1 ${r.toFixed(2)} ${n.toFixed(2)}`},l=Math.max(0,Math.min(1,e/t)),c=Math.max(0,Math.min(1,s/t));return(0,a.jsxs)("svg",{className:"em-gauge",viewBox:"0 0 132 132",role:"img","aria-label":`Reputation ${e} of ${t}`,children:[(0,a.jsx)("path",{className:"em-gauge-track",d:o(0,1)}),(0,a.jsx)("path",{className:"em-gauge-danger",d:o(0,c)}),(0,a.jsx)("path",{className:"em-gauge-value",d:o(0,l)}),(0,a.jsx)("text",{className:"em-gauge-num",x:66,y:76,children:e.toFixed(0)}),(0,a.jsxs)("text",{className:"em-gauge-max",x:66,y:94,children:["/ ",t]})]})}function t$({slices:e,centreTop:t,centreSub:s,size:r=150,showLegend:n}){let i=e.filter(e=>e.value>0),o=i.reduce((e,a)=>e+a.value,0),l=r/2-10,c=.62*l,d=r/2,p=r/2,h=-Math.PI/2,m=i.map(e=>{let a=e.value/o*Math.PI*2,t=h,s=h+a;if(h=s,1===i.length)return{s:e,d:`M ${d} ${p-l} A ${l} ${l} 0 1 1 ${d-.01} ${p-l} L ${d-.01} ${p-c} A ${c} ${c} 0 1 0 ${d} ${p-c} Z`};let r=+(a>Math.PI),n=(e,a)=>`${(d+e*Math.cos(a)).toFixed(2)} ${(p+e*Math.sin(a)).toFixed(2)}`;return{s:e,d:`M ${n(l,t)} A ${l} ${l} 0 ${r} 1 ${n(l,s)} L ${n(c,s)} A ${c} ${c} 0 ${r} 0 ${n(c,t)} Z`}});return(0,a.jsxs)("div",{className:"em-donut-wrap",children:[(0,a.jsxs)("svg",{className:"em-donut",viewBox:`0 0 ${r} ${r}`,role:"img","aria-label":s,children:[o>0?m.map(({s:e,d:t})=>(0,a.jsx)("path",{className:`em-slice ${e.cls}`,d:t,children:(0,a.jsx)("title",{children:`${e.label}
935: ${e.value.toLocaleString("en-US")} (${to(e.value/o,1)})`})},e.label)):(0,a.jsx)("circle",{className:"em-donut-empty",cx:d,cy:p,r:(l+c)/2,strokeWidth:l-c}),(0,a.jsx)("text",{className:"em-donut-top",x:d,y:p+1,children:t}),(0,a.jsx)("text",{className:"em-donut-sub",x:d,y:p+16,children:s})]}),n&&(0,a.jsx)("ul",{className:"em-legend em-legend-col",children:i.map(e=>(0,a.jsxs)("li",{children:[(0,a.jsx)("span",{className:`em-swatch ${e.cls}`})," ",e.label,(0,a.jsxs)("span",{className:"em-legend-v",children:[e.value.toLocaleString("en-US")," · ",to(e.value/o,1)]})]},e.label))})]})}function t_({points:e}){if(e.length<2)return null;let t=Math.max(...e,1),s=120/(e.length-1),r=e.map((e,a)=>`${(a*s).toFixed(1)},${(28-e/t*25-1).toFixed(1)}`).join(" ");return(0,a.jsx)("svg",{className:"em-spark",viewBox:"0 0 120 28",preserveAspectRatio:"none","aria-hidden":"true",children:(0,a.jsx)("polyline",{points:r,fill:"none",strokeWidth:1.8})})}function tU({points:e,warnBelow:t,warnAbove:s,height:r=130,suffix:n=""}){if(e.length<2)return null;let i=r-10-22,o=e.map(e=>e.y),l=Math.min(...o,t??1/0),c=Math.max(...o,s??-1/0),d=(c-l)*.12||Math.max(.1*c,1),p=Math.max(0,l-d),h=c+d,m=e=>10+i-(e-p)/(h-p||1)*i,u=a=>40+a/(e.length-1)*590,f=e.map((e,a)=>`${u(a).toFixed(1)},${m(e.y).toFixed(1)}`).join(" "),g=Math.max(1,Math.ceil(e.length/6));return(0,a.jsxs)("svg",{className:"em-chart",viewBox:`0 0 640 ${r}`,role:"img","aria-label":"Trend",children:[[p,(p+h)/2,h].map(e=>(0,a.jsxs)("g",{children:[(0,a.jsx)("line",{className:"em-grid",x1:40,y1:m(e),x2:630,y2:m(e)}),(0,a.jsxs)("text",{className:"em-axis em-axis-y",x:34,y:m(e)+3,children:[e.toFixed(+(e<10)),n]})]},e)),void 0!==t&&m(t)<10+i&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("rect",{className:"em-band",x:40,y:m(t),width:590,height:10+i-m(t)}),(0,a.jsx)("line",{className:"em-band-line",x1:40,y1:m(t),x2:630,y2:m(t)})]}),void 0!==s&&m(s)>10&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("rect",{className:"em-band",x:40,y:10,width:590,height:m(s)-10}),(0,a.jsx)("line",{className:"em-band-line",x1:40,y1:m(s),x2:630,y2:m(s)})]}),(0,a.jsx)("polyline",{className:"em-line",points:f,fill:"none"}),e.map((e,t)=>(0,a.jsx)("circle",{className:"em-dot",cx:u(t),cy:m(e.y),r:2.4,children:(0,a.jsx)("title",{children:`${e.x}: ${e.y.toFixed(1)}${n}`})},e.x)),e.map((t,s)=>s%g==0||s===e.length-1?(0,a.jsx)("text",{className:"em-axis em-axis-x",x:u(s),y:r-6,children:tl(t.x)},`t${t.x}`):null)]})}function tL({rows:e,height:t=140,grouped:s}){if(0===e.length)return null;let r=t-20-8,n=s?Math.max(...e.flatMap(e=>e.values.map(e=>e.v)),1):Math.max(...e.map(e=>e.values.reduce((e,a)=>e+a.v,0)),1),i=598/e.length,o=Math.max(.7*i,2),l=Math.max(1,Math.ceil(e.length/8)),c=e[0].values.map(e=>({name:e.name,cls:e.cls}));return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("svg",{className:"em-chart",viewBox:`0 0 640 ${t}`,role:"img","aria-label":"Volume",children:[[0,n/2,n].map(e=>(0,a.jsxs)("g",{children:[(0,a.jsx)("line",{className:"em-grid",x1:34,y1:8+r-e/n*r,x2:632,y2:8+r-e/n*r}),(0,a.jsx)("text",{className:"em-axis em-axis-y",x:29,y:8+r-e/n*r+3,children:Math.round(e)})]},e)),e.map((c,d)=>{let p=34+d*i+(i-o)/2,h=0,m=s?o/c.values.length:o;return(0,a.jsxs)("g",{children:[c.values.map((e,t)=>{let i=e.v/n*r,l=s?8+r-i:8+r-i-h;return s||(h+=i),(0,a.jsx)("rect",{className:`em-bar ${e.cls}`,x:s?p+t*m:p,y:l,width:Math.max(s?m-.6:o,.8),height:Math.max(i,+(e.v>0)),children:(0,a.jsx)("title",{children:`${c.label} \xb7 ${e.name}: ${e.v.toLocaleString("en-US")}`})},e.name)}),(d%l==0||d===e.length-1)&&(0,a.jsx)("text",{className:"em-axis em-axis-x",x:p+o/2,y:t-6,children:c.label})]},`${c.label}-${d}`)})]}),(0,a.jsx)("ul",{className:"em-legend",children:c.map(e=>(0,a.jsxs)("li",{children:[(0,a.jsx)("span",{className:`em-swatch ${e.cls}`})," ",e.name]},e.name))})]})}function tq({groups:e,height:t=180,suffix:s=""}){if(0===e.length)return null;let r=Math.max(Math.floor((t-10)/e.length),26),n=e.length*r+10,i=Math.max(...e.flatMap(e=>e.values.map(e=>e.v)),1),o=e[0].values.map(e=>({name:e.name,cls:e.cls}));return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("svg",{className:"em-chart",viewBox:`0 0 640 ${n}`,role:"img","aria-label":"Comparison",children:e.map((e,t)=>{let n=t*r+5,o=Math.max((r-8)/e.values.length,2);return(0,a.jsxs)("g",{children:[(0,a.jsx)("text",{className:"em-ci-label",x:112,y:n+r/2,children:e.label.length>18?`${e.label.slice(0,17)}â¦`:e.label}),e.values.map((t,r)=>(0,a.jsx)("rect",{className:`em-bar ${t.cls}`,x:120,y:n+r*o,width:Math.max(t.v/i*466,1.5*(t.v>0)),height:Math.max(o-1.4,1.5),rx:1.5,children:(0,a.jsx)("title",{children:`${e.label} \xb7 ${t.name}: ${t.v.toFixed(2)}${s}`})},t.name)),(0,a.jsxs)("text",{className:"em-ci-value",x:636,y:n+r/2,children:[e.values[0].v.toFixed(1),s]})]},e.label)})}),(0,a.jsx)("ul",{className:"em-legend",children:o.map(e=>(0,a.jsxs)("li",{children:[(0,a.jsx)("span",{className:`em-swatch ${e.cls}`})," ",e.name]},e.name))})]})}function tB({onSignIn:e,onSignOut:t,error:s}){return(0,a.jsxs)("main",{className:"em-root em-gate",children:[(0,a.jsx)("style",{children:tG}),e&&(0,a.jsx)("button",{className:"em-btn",onClick:e,children:"Sign in with Google"}),t&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("a",{className:"em-btn",href:"/ownerportal",children:"â Back to the owner portal"}),(0,a.jsx)("button",{className:"em-btn em-btn-quiet",onClick:t,children:"Sign out"})]}),s&&(0,a.jsx)("p",{className:"em-error",children:s})]})}
935let tG=` 936.em-root { 937 /* Factlen tokens â owner directive 2026-09-11: black/white/red. --em-s1..s5 938 (per-campaign series) untouched â functional data encoding. */ 939 --em-bg: #ffffff; --em-surface: #fff; --em-alt: #f6f6f6; --em-border: #e6e6e6; 940 --em-text: #161616; --em-muted: #5a5a5a; --em-faint: #8a8a8a; 941 --em-accent: #C8102E; --em-accent-strong: #a30d26; --em-accent-soft: #fde2e6; 942 --em-done: #1d7a37; --em-done-soft: #eaf6ec; --em-err: #C8102E; --em-err-soft: #fde2e6; 943 --em-test: #92400e; --em-test-soft: #fffbeb; --em-test-border: #fbbf24; 944 --em-s1: #0f9b8e; --em-s2: #d98324; --em-s3: #a3282f; --em-s4: #3f7fd1; --em-s5: #7b52c9; 945 max-width: 980px; margin: 0 auto; padding: 32px 20px 80px; 946 color: var(--em-text); 947 font-family: -apple-system, "SF Pro Text", ui-sans-serif, system-ui, sans-serif; 948} 949@media (prefers-color-scheme: dark) { 950 .em-root { 951 --em-bg: #0d1117; --em-surface: #161b22; --em-alt: #161b22; --em-border: #30363d; 952 --em-text: #d4d4d4; --em-muted: #9a9a9a; --em-faint: #6e7681; 953 --em-accent: #ea5a72; --em-accent-strong: #f2879a; --em-accent-soft: #2a1216; 954 --em-done: #3fb950; --em-done-soft: #0f1f12; --em-err: #f85149; --em-err-soft: #2a1216; 955 --em-test: #fcd34d; --em-test-soft: #2a2005; --em-test-border: #5b4520; 956 --em-s1: #34d6c1; --em-s2: #f0b167; --em-s3: #ff9ba1; --em-s4: #7fb2f0; --em-s5: #b79bee; 957 } 958} 959.em-gate { min-height: 60vh; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; } 960.em-bar { display: flex; justify-content: space-between; align-items: center; gap: 12px; 961 flex-wrap: wrap; padding-bottom: 14px; border-bottom: 1px solid var(--em-border); margin-bottom: 20px; } 962.em-crumbs { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } 963.em-who { display: flex; align-items: center; gap: 10px; font-size: .78rem; color: var(--em-faint); } 964.em-head { margin-bottom: 18px; } 965.em-head h1 { font-size: clamp(1.4rem,3vw,1.85rem); font-weight: 800; letter-spacing: -.015em; 966 margin: 0 0 4px; text-wrap: balance; display: flex; align-items: center; gap: 9px; flex-wrap: wrap; } 967.em-h2 { font-size: .8rem; font-weight: 800; text-transform: uppercase; letter-spacing: .07em; 968 color: var(--em-faint); margin: 0 0 12px; } 969.em-h2-tight { margin: 0; } 970.em-section { margin-top: 26px; } 971.em-lede { color: var(--em-muted); font-size: .88rem; line-height: 1.6; margin: 0 0 16px; max-width: 72ch; } 972.em-subject { font-size: .92rem; color: var(--em-muted); font-style: italic; margin: 2px 0 6px; } 973.em-note { color: var(--em-faint); font-size: .75rem; line-height: 1.6; margin: 10px 0 0; max-width: 78ch; } 974.em-note-tight { margin: 6px 0 0; } 975.em-muted { color: var(--em-muted); font-size: .9rem; } 976.em-faint { color: var(--em-faint); } 977.em-good { color: var(--em-done); font-weight: 700; } 978.em-error { color: var(--em-err); font-size: .88rem; line-height: 1.55; } 979.em-btn { display: inline-flex; align-items: center; gap: 6px; font: inherit; font-size: .8rem; font-weight: 700; 980 padding: 7px 13px; border-radius: 8px; border: 1px solid transparent; cursor: pointer; 981 background: var(--em-accent-soft); color: var(--em-accent-strong); text-decoration: none; } 982.em-btn:hover { border-color: var(--em-accent); } 983.em-btn:focus-visible { outline: 2px solid var(--em-accent); outline-offset: 2px; } 984.em-btn-quiet { background: transparent; color: var(--em-faint); border-color: var(--em-border); } 985.em-linkbtn { font: inherit; font-weight: 700; background: none; border: 0; padding: 0; cursor: pointer; 986 color: var(--em-accent-strong); display: inline-flex; align-items: center; gap: 6px; } 987.em-linkbtn:hover { text-decoration: underline; } 988 989/* account health */ 990.em-health { background: var(--em-surface); border: 1px solid var(--em-border); border-left-width: 5px; 991 border-radius: 14px; padding: 16px 18px 14px; margin-bottom: 8px; } 992.em-good-band { border-left-color: var(--em-accent); } 993.em-warn-band { border-left-color: var(--em-test); } 994.em-bad-band { border-left-color: var(--em-err); } 995.em-health-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 12px; } 996.em-health-grid { display: grid; gap: 20px; align-items: start; 997 grid-template-columns: minmax(140px, 168px) minmax(220px, 1fr) minmax(170px, 230px); } 998.em-health-gauge { display: flex; flex-direction: column; align-items: center; gap: 2px; 999 background: none; border: 0; font: inherit; color: inherit; cursor: pointer; padding: 0; } 1000.em-health-gauge:focus-visible { outline: 2px solid var(--em-accent); outline-offset: 4px; border-radius: 10px; } 1001.em-gauge { width: 132px; height: 132px; display: block; } 1002.em-gauge-track { fill: none; stroke: var(--em-alt); stroke-width: 12; stroke-linecap: round; } 1003.em-gauge-danger { fill: none; stroke: var(--em-err); stroke-width: 12; opacity: .22; stroke-linecap: round; } 1004.em-gauge-value { fill: none; stroke: var(--em-accent); stroke-width: 12; stroke-linecap: round; } 1005.em-bad-band .em-gauge-value { stroke: var(--em-err); } 1006.em-warn-band .em-gauge-value { stroke: var(--em-test); } 1007.em-gauge-num { text-anchor: middle; font-family: ui-monospace, monospace; font-size: 30px; font-weight: 800; fill: var(--em-text); } 1008.em-gauge-max { text-anchor: middle; font-size: 11px; fill: var(--em-faint); font-family: ui-monospace, monospace; } 1009.em-gauge-word { font-size: .82rem; font-weight: 800; letter-spacing: -.01em; } 1010.em-gauge-sub { font-size: .66rem; color: var(--em-faint); text-align: center; } 1011.em-health-trend { min-width: 0; } 1012.em-health-deltas { display: flex; gap: 18px; margin-bottom: 4px; } 1013.em-health-mix { display: flex; flex-direction: column; align-items: center; gap: 4px; min-width: 0; } 1014.em-mix-title { font-size: .66rem; font-weight: 800; text-transform: uppercase; letter-spacing: .05em; color: var(--em-faint); } 1015.em-mix-foot { font-size: .68rem; color: var(--em-faint); text-align: center; } 1016.em-alert-list { list-style: none; margin: 12px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; } 1017.em-alert { font-size: .76rem; line-height: 1.55; color: var(--em-err); 1018 background: var(--em-err-soft); border-radius: 7px; padding: 6px 9px; } 1019.em-delta { display: flex; flex-direction: column; gap: 1px; } 1020.em-delta-v { font-family: ui-monospace, monospace; font-size: .86rem; font-weight: 800; } 1021.em-delta-l { font-size: .6rem; text-transform: uppercase; letter-spacing: .05em; color: var(--em-faint); } 1022.em-delta-up .em-delta-v { color: var(--em-done); } 1023.em-delta-down .em-delta-v { color: var(--em-err); } 1024.em-delta-flat .em-delta-v { color: var(--em-faint); } 1025 1026.em-sched-days { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; } 1027.em-sched-day { background: var(--em-surface); border: 1px solid var(--em-border); border-radius: 10px; overflow: hidden; } 1028.em-sched-day-head { width: 100%; display: flex; align-items: center; gap: 10px; padding: 10px 14px; 1029 background: transparent; border: none; cursor: pointer; text-align: left; color: var(--em-text); font: inherit; } 1030.em-sched-day-head:hover { background: var(--em-alt); } 1031.em-sched-date { font-weight: 800; font-size: .82rem; } 1032.em-sched-summary { color: var(--em-faint); font-size: .76rem; flex: 1; } 1033.em-sched-caret { color: var(--em-faint); font-size: .7rem; } 1034.em-sched-table { width: 100%; border-collapse: collapse; font-size: .78rem; } 1035.em-sched-table th { text-align: left; font-size: .64rem; text-transform: uppercase; letter-spacing: .04em; 1036 color: var(--em-faint); padding: 6px 14px; border-top: 1px solid var(--em-border); } 1037.em-sched-table td { padding: 6px 14px; border-top: 1px solid var(--em-border); } 1038.em-sched-reason { color: var(--em-muted); max-width: 32ch; } 1039.em-sched-badge { display: inline-block; padding: 1px 8px; border-radius: 999px; font-size: .68rem; font-weight: 700; } 1040.em-sched-good { background: var(--em-done-soft); color: var(--em-done); } 1041.em-sched-bad { background: var(--em-err-soft); color: var(--em-err); } 1042.em-sched-pending { background: var(--em-accent-soft); color: var(--em-accent-strong); } 1043 1044.em-panel { background: var(--em-surface); border: 1px solid var(--em-border); 1045 border-radius: 12px; padding: 16px 18px; margin-bottom: 14px; } 1046.em-panel.em-bad-band { border-left: 5px solid var(--em-err); } 1047.em-split { display: flex; gap: 22px; align-items: flex-start; flex-wrap: wrap; } 1048.em-grow { flex: 1 1 260px; min-width: 0; } 1049 1050/* filters */ 1051.em-filters { display: flex; gap: 14px; align-items: flex-end; flex-wrap: wrap; 1052 background: var(--em-alt); border: 1px solid var(--em-border); border-radius: 12px; 1053 padding: 11px 14px; margin: 16px 0 4px; } 1054.em-field { display: flex; flex-direction: column; gap: 3px; } 1055.em-field-l { font-size: .6rem; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--em-faint); } 1056.em-select { font: inherit; font-size: .8rem; font-weight: 600; padding: 5px 8px; border-radius: 7px; 1057 border: 1px solid var(--em-border); background: var(--em-surface); color: var(--em-text); } 1058.em-toggle { display: inline-flex; border: 1px solid var(--em-border); border-radius: 7px; overflow: hidden; background: var(--em-surface); } 1059.em-toggle-b { font: inherit; font-size: .74rem; font-weight: 700; padding: 5px 9px; border: 0; 1060 background: transparent; color: var(--em-faint); cursor: pointer; } 1061.em-toggle-b:disabled { opacity: .4; cursor: default; } 1062.em-toggle-on { background: var(--em-accent-soft); color: var(--em-accent-strong); } 1063.em-filter-count { margin-left: auto; font-size: .72rem; color: var(--em-faint); font-family: ui-monospace, monospace; } 1064 1065/* campaign cards */ 1066.em-cards { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; 1067 grid-template-columns: repeat(auto-fill, minmax(298px, 1fr)); } 1068.em-card { background: var(--em-surface); border: 1px solid var(--em-border); border-radius: 12px; } 1069.em-card-alarm { border-left: 4px solid var(--em-err); } 1070.em-card:hover { border-color: var(--em-accent); } 1071.em-card-btn { width: 100%; text-align: left; font: inherit; color: inherit; cursor: pointer; 1072 background: none; border: 0; padding: 13px 15px; display: flex; flex-direction: column; gap: 7px; } 1073.em-card-btn:focus-visible { outline: 2px solid var(--em-accent); outline-offset: 2px; border-radius: 12px; } 1074.em-card-head { display: flex; align-items: center; gap: 7px; flex-wrap: wrap; } 1075.em-card-name { font-size: .92rem; font-weight: 800; letter-spacing: -.01em; } 1076.em-card-sub { font-size: .7rem; color: var(--em-faint); font-family: ui-monospace, monospace; } 1077.em-open-hint { margin-left: auto; font-size: .7rem; font-weight: 700; color: var(--em-accent-strong); } 1078.em-card-foot { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; } 1079.em-card-metric { display: flex; flex-direction: column; } 1080.em-card-metric-v { font-family: ui-monospace, monospace; font-size: 1.25rem; font-weight: 800; line-height: 1.1; } 1081.em-card-metric-l { font-size: .6rem; text-transform: uppercase; letter-spacing: .05em; color: var(--em-faint); } 1082.em-mini { display: flex; flex-direction: column; gap: 3px; } 1083.em-mini-row { display: grid; grid-template-columns: 62px 1fr 46px; align-items: center; gap: 7px; } 1084.em-mini-l { font-size: .66rem; color: var(--em-muted); } 1085.em-mini-track { height: 7px; border-radius: 4px; background: var(--em-alt); overflow: hidden; } 1086.em-mini-fill { display: block; height: 100%; border-radius: 4px; background: var(--em-accent); } 1087.em-mini-fill.em-s1 { background: var(--em-s1); } 1088.em-mini-fill.em-s2 { background: var(--em-s2); } 1089.em-mini-fill.em-s4 { background: var(--em-s4); } 1090.em-mini-queued { background: repeating-linear-gradient(45deg, var(--em-faint), var(--em-faint) 3px, transparent 3px, transparent 6px); } 1091.em-mini-v { font-family: ui-monospace, monospace; font-size: .68rem; text-align: right; } 1092 1093.em-chip { font-size: .62rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; 1094 padding: 2px 7px; border-radius: 20px; border: 1px solid transparent; white-space: nowrap; } 1095.em-chip-live { color: var(--em-accent-strong); background: var(--em-accent-soft); border-color: var(--em-accent); } 1096.em-chip-var { color: var(--em-text); background: var(--em-alt); border-color: var(--em-border); font-family: ui-monospace, monospace; } 1097.em-chip-alarm { color: var(--em-err); background: var(--em-err-soft); } 1098.em-chip-lead { color: var(--em-done); background: var(--em-done-soft); } 1099 1100/* experiments */ 1101.em-exp-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 14px; flex-wrap: wrap; margin-bottom: 10px; } 1102.em-exp-title { font-size: 1rem; font-weight: 800; margin: 0; display: flex; align-items: center; gap: 8px; } 1103.em-exp-sub { font-size: .72rem; color: var(--em-faint); margin: 2px 0 0; } 1104.em-verdict { font-size: .82rem; line-height: 1.55; margin: 10px 0 0; padding: 8px 11px; border-radius: 8px; } 1105.em-verdict-win { color: var(--em-done); background: var(--em-done-soft); font-weight: 700; } 1106.em-verdict-wait { color: var(--em-muted); background: var(--em-alt); } 1107.em-verdict-none { color: var(--em-faint); background: var(--em-alt); } 1108.em-ci-row { cursor: pointer; } 1109.em-ci-row:hover .em-ci-bar { opacity: .85; } 1110.em-ci-label { text-anchor: end; font-size: 12px; font-weight: 800; fill: var(--em-text); font-family: ui-monospace, monospace; dominant-baseline: middle; } 1111.em-ci-value { text-anchor: end; font-size: 11px; fill: var(--em-muted); font-family: ui-monospace, monospace; dominant-baseline: middle; } 1112.em-ci-track { fill: var(--em-alt); } 1113.em-ci-bar { fill: var(--em-s1); } 1114.em-ci-neg { fill: var(--em-s3); } 1115.em-ci-win { fill: var(--em-done); } 1116.em-ci-whisk line { stroke: var(--em-text); stroke-width: 1.4; opacity: .65; } 1117 1118/* pipeline + funnel */ 1119.em-pipe { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; } 1120.em-pipe-row { display: grid; grid-template-columns: 108px 1fr 64px; align-items: center; gap: 10px; } 1121.em-pipe-l { font-size: .76rem; color: var(--em-muted); } 1122.em-pipe-track { height: 12px; border-radius: 5px; background: var(--em-alt); overflow: hidden; } 1123.em-pipe-fill { display: block; height: 100%; border-radius: 5px; } 1124.em-pipe-fill.em-s1 { background: var(--em-s1); } 1125.em-pipe-fill.em-s2 { background: var(--em-s2); } 1126.em-pipe-fill.em-s4 { background: var(--em-s4); } 1127.em-pipe-fill.em-s5 { background: var(--em-s5); } 1128.em-pipe-v { font-family: ui-monospace, monospace; font-size: .8rem; font-weight: 700; text-align: right; } 1129.em-fn-label { text-anchor: end; font-size: 11.5px; fill: var(--em-muted); font-weight: 600; dominant-baseline: middle; } 1130.em-fn-track { fill: var(--em-alt); } 1131.em-fn-bar { fill: var(--em-accent); } 1132.em-fn-val { font-size: 11.5px; fill: var(--em-text); font-weight: 700; font-family: ui-monospace, monospace; dominant-baseline: middle; } 1133.em-fn-step { text-anchor: end; font-size: 11px; fill: var(--em-faint); font-family: ui-monospace, monospace; dominant-baseline: middle; } 1134.em-sig-bar { fill: var(--em-s1); } 1135.em-sig-over { fill: var(--em-err); } 1136.em-svg-bad { fill: var(--em-err); } 1137 1138/* metrics */ 1139.em-metrics { display: grid; gap: 8px; grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); 1140 margin: 12px 0 0; padding: 0; flex: 1 1 300px; } 1141.em-metric { background: var(--em-alt); border: 1px solid var(--em-border); border-radius: 8px; padding: 7px 9px; } 1142.em-metric dt { font-size: .62rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--em-faint); } 1143.em-metric dd { margin: 2px 0 0; font-family: ui-monospace, monospace; font-size: .84rem; font-weight: 700; } 1144.em-metric-strong { border-color: var(--em-accent); } 1145.em-metric-strong dt { color: var(--em-accent-strong); } 1146.em-metric-weak { opacity: .55; } 1147.em-metric-bad { border-color: var(--em-err); background: var(--em-err-soft); } 1148.em-metric-bad dd { color: var(--em-err); } 1149.em-soft-mark { margin-left: 3px; cursor: help; } 1150 1151/* rank bars */ 1152.em-links-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; } 1153.em-rank-row { display: grid; grid-template-columns: minmax(110px,1.4fr) 2fr auto; align-items: center; gap: 9px; } 1154.em-rank-label { font-size: .74rem; color: var(--em-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } 1155.em-rank-a { color: inherit; text-decoration: none; } 1156.em-rank-a:hover { text-decoration: underline;
1156 color: var(--em-accent-strong); } 1157.em-rank-track { height: 9px; border-radius: 4px; background: var(--em-alt); overflow: hidden; } 1158.em-rank-fill { display: block; height: 100%; border-radius: 4px; background: var(--em-accent); } 1159.em-rank-fill.em-s1 { background: var(--em-s1); } 1160.em-rank-fill.em-s2 { background: var(--em-s2); } 1161.em-rank-val { font-size: .68rem; color: var(--em-faint); white-space: nowrap; font-family: ui-monospace, monospace; } 1162 1163/* charts */ 1164.em-spark { width: 120px; height: 28px; display: block; } 1165.em-spark polyline { stroke: var(--em-accent); } 1166.em-chart { width: 100%; height: auto; display: block; } 1167.em-chart-narrow { max-width: 380px; } 1168.em-grid { stroke: var(--em-border); stroke-width: 1; } 1169.em-axis { font-size: 9.5px; fill: var(--em-faint); font-family: ui-monospace, monospace; } 1170.em-axis-y { text-anchor: end; } 1171.em-axis-x { text-anchor: middle; } 1172.em-band { fill: var(--em-err); opacity: .07; } 1173.em-band-line { stroke: var(--em-err); stroke-width: 1; stroke-dasharray: 3 3; opacity: .5; } 1174.em-line { stroke: var(--em-accent); stroke-width: 2; } 1175.em-dot { fill: var(--em-accent); } 1176.em-bar.em-s1 { fill: var(--em-s1); } 1177.em-bar.em-s2 { fill: var(--em-s2); } 1178.em-bar.em-s3 { fill: var(--em-s3); } 1179.em-bar.em-s4 { fill: var(--em-s4); } 1180.em-bar.em-s5 { fill: var(--em-s5); } 1181.em-donut-wrap { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; } 1182.em-donut { display: block; } 1183.em-slice.em-s1 { fill: var(--em-s1); } 1184.em-slice.em-s2 { fill: var(--em-s2); } 1185.em-slice.em-s3 { fill: var(--em-s3); } 1186.em-slice.em-s4 { fill: var(--em-s4); } 1187.em-slice.em-s5 { fill: var(--em-s5); } 1188.em-donut-empty { fill: none; stroke: var(--em-alt); } 1189.em-donut-top { text-anchor: middle; font-family: ui-monospace, monospace; font-size: 19px; font-weight: 800; fill: var(--em-text); } 1190.em-donut-sub { text-anchor: middle; font-size: 9.5px; fill: var(--em-faint); } 1191.em-legend { list-style: none; display: flex; gap: 14px; flex-wrap: wrap; margin: 8px 0 0; padding: 0; 1192 font-size: .7rem; color: var(--em-muted); } 1193.em-legend-col { flex-direction: column; gap: 5px; margin: 0; } 1194.em-legend li { display: flex; align-items: center; gap: 5px; } 1195.em-legend-v { color: var(--em-faint); font-family: ui-monospace, monospace; margin-left: 4px; } 1196.em-swatch { width: 9px; height: 9px; border-radius: 2px; display: inline-block; flex: none; } 1197.em-swatch.em-s1 { background: var(--em-s1); } 1198.em-swatch.em-s2 { background: var(--em-s2); } 1199.em-swatch.em-s3 { background: var(--em-s3); } 1200.em-swatch.em-s4 { background: var(--em-s4); } 1201.em-swatch.em-s5 { background: var(--em-s5); } 1202 1203/* tables */ 1204.em-table-wrap { overflow-x: auto; margin-top: 10px; } 1205.em-table { width: 100%; border-collapse: collapse; font-size: .76rem; } 1206.em-table th { text-align: left; font-size: .62rem; text-transform: uppercase; letter-spacing: .05em; 1207 color: var(--em-faint); font-weight: 800; padding: 5px 8px; border-bottom: 1px solid var(--em-border); white-space: nowrap; } 1208.em-table td { padding: 5px 8px; border-bottom: 1px solid var(--em-border); white-space: nowrap; } 1209.em-table tr:last-child td { border-bottom: 0; } 1210.em-num { text-align: right; font-family: ui-monospace, monospace; } 1211.em-mono { font-family: ui-monospace, monospace; } 1212.em-bad { color: var(--em-err); font-weight: 700; } 1213 1214@media (max-width: 760px) { 1215 .em-health-grid { grid-template-columns: 1fr; justify-items: center; } 1216 .em-health-trend { width: 100%; } 1217 .em-filters { gap: 10px; } 1218 .em-filter-count { margin-left: 0; } 1219} 1220`;var ew=ew,ej=ew;let tH="ops_leads";function tY(){let e=(0,ei.getAuth)((0,eu.getFirebaseApp)());return e.tenantId="owner-portal-w358j",e}function tV(e){return"number"==typeof e&&Number.isFinite(e)?e:0}function tK(e){let a=e??{};if("string"!=typeof a.submittedAt)return null;let t=a.fields&&"object"==typeof a.fields?a.fields:{};return{leadId:"string"==typeof a.leadId?a.leadId:null,campaignId:"string"==typeof a.campaignId?a.campaignId:null,siteName:"string"==typeof a.siteName?a.siteName:null,email:"string"==typeof a.email?a.email:null,fullName:"string"==typeof a.fullName?a.fullName:null,phone:"string"==typeof a.phone?a.phone:null,fields:t,pageSlug:"string"==typeof a.pageSlug?a.pageSlug:null,pageTitle:"string"==typeof a.pageTitle?a.pageTitle:null,sourceUrl:"string"==typeof a.sourceUrl?a.sourceUrl:null,submittedAt:a.submittedAt,source:"string"==typeof a.source?a.source:null,notified:"boolean"==typeof a.notified?a.notified:null}}let tQ=new Set(["full_name","name","email","email_address","phone","phone_number"]);function tJ(){let[e,t]=(0,r.useState)(null),[s,n]=(0,r.useState)(!1),[i,o]=(0,r.useState)([]),[l,c]=(0,r.useState)(!1),[d,p]=(0,r.useState)(null),[h,m]=(0,r.useState)("all"),[u,f]=(0,r.useState)(""),[g,x]=(0,r.useState)(new Set),b=/permission|insufficient/i.test(d??"");(0,r.useEffect)(()=>(0,eo.onAuthStateChanged)(tY(),e=>{t(e),n(!0)}),[]),(0,r.useEffect)(()=>
1220{if(!e){o([]),c(!1);return}return(0,eN.onSnapshot)((0,ej.aO)((0,ew.aX)((0,eu.getFirebaseApp)()),tH),e=>{let a=e.docs.map(e=>{let a=e.data(),t=(Array.isArray(a.leads)?a.leads:[]).map(tK).filter(e=>null!==e);return{id:e.id,domain:"string"==typeof a.domain&&a.domain?a.domain:e.id,businessName:"string"==typeof a.businessName&&a.businessName?a.businessName:e.id,leads:t,totalLeads:tV(a.totalLeads)||t.length,notNotifiedCount:tV(a.notNotifiedCount),lastSyncAt:"string"==typeof a.lastSyncAt?a.lastSyncAt:""}});a.sort((e,a)=>e.businessName.localeCompare(a.businessName)),o(a),c(!0),p(null)},e=>{let a=`Could not read ${tH} from Firestore: ${e.message}`;console.error(a),p(a),c(!0)})},[e]);let y=(0,r.useMemo)(()=>i.flatMap(e=>e.leads.map(a=>({...a,businessId:e.id,businessName:e.businessName}))),[i]),v=(0,r.useMemo)(()=>i.reduce((e,a)=>e+a.notNotifiedCount,0),[i]),w=(0,r.useMemo)(()=>{let e=u.trim().toLowerCase();return y.filter(e=>"all"===h||e.businessId===h).filter(a=>!e||[a.fullName,a.email,a.phone,a.pageTitle,a.businessName,...Object.values(a.fields)].filter(Boolean).join(" ").toLowerCase().includes(e)).sort((e,a)=>(a.submittedAt??"").localeCompare(e.submittedAt??""))},[y,h,u]);return s?e?b?(0,a.jsx)(tZ,{onSignOut:()=>(0,em.signOut)(tY())}):(0,a.jsxs)("main",{className:"ld-root",children:[(0,a.jsx)("style",{children:tX}),(0,a.jsx)("div",{className:"ld-bar",children:(0,a.jsxs)("span",{className:"ld-who",children:[e.email,(0,a.jsx)("button",{className:"ld-btn ld-btn-quiet",onClick:()=>(0,em.signOut)(tY()),children:"Sign out"})]})}),(0,a.jsx)(eS.OwnerNav,{active:"leads"}),d&&(0,a.jsx)("p",{className:"ld-error",children:d}),!d&&!l&&(0,a.jsx)("p",{className:"ld-muted",children:"Loadingâ¦"}),!d&&l&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"ld-summary",children:[(0,a.jsxs)("div",{className:"ld-stat",children:[(0,a.jsx)("span",{className:"ld-stat-n",children:y.length}),(0,a.jsx)("span",{className:"ld-stat-l",children:"leads, last 90 days"})]}),(0,a.jsxs)("div",{className:`ld-stat ${v>0?"ld-stat-bad":""}`,children:[(0,a.jsx)("span",{className:"ld-stat-n",children:v}),(0,a.jsx)("span",{className:"ld-stat-l",children:"notification failed â check these first"})]})]}),(0,a.jsxs)("div",{className:"ld-filters",children:[(0,a.jsxs)("select",{value:h,onChange:e=>m(e.target.value),className:"ld-select",children:[(0,a.jsxs)("option",{value:"all",children:["All businesses (",i.length,")"]}),i.map(e=>(0,a.jsxs)("option",{value:e.id,children:[e.businessName," (",e.totalLeads,")"]},e.id))]}),(0,a.jsx)("input",{className:"ld-search",placeholder:"Search name, email, phone, messageâ¦",value:u,onChange:e=>f(e.target.value)})]}),0===w.length?(0,a.jsx)("p",{className:"ld-muted",children:"No leads match these filters."}):(0,a.jsx)("ul",{className:"ld-list",children:w.map((e,t)=>{let s=e.leadId??`${e.businessId}-${e.submittedAt}-${t}`,r=g.has(s),n=Object.entries(e.fields).filter(([e])=>!tQ.has(e)),i=!e.fullName&&!e.phone&&0===Object.keys(e.fields).length;return(0,a.jsxs)("li",{className:"ld-card",children:[(0,a.jsxs)("button",{className:"ld-card-head",onClick:()=>x(e=>{let a=new Set(e);return a.has(s)?a.delete(s):a.add(s),a}),children:[(0,a.jsx)("span",{className:"ld-card-name",children:e.fullName||e.email||"(no name captured)"}),(0,a.jsx)("span",{className:"ld-card-biz",children:e.businessName}),(0,a.jsx)("span",{className:"ld-card-when",children:function(e){if(!e)return"â";let a=new Date(e);return Number.isNaN(a.getTime())?e:a.toLocaleString("en-US",{month:"short",day:"numeric",year:"numeric",hour:"numeric",minute:"2-digit"})}(e.submittedAt)}),!1===e.notified&&(0,a.jsx)("span",{className:"ld-badge ld-badge-bad",children:"not delivered"}),(0,a.jsx)("span",{className:"ld-caret",children:r?"â¾":"â¸"})]}),r&&(0,a.jsxs)("div",{className:"ld-card-body",children:[i&&(0,a.jsx)("p",{className:"ld-note",children:"Recorded before 2026-09-11 â only email/page/time were captured then, not name/phone/message. Full content may still be in your spam folder for this date."}),(0,a.jsxs)("dl",{className:"ld-fields",children:[e.email&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("dt",{children:"Email"}),(0,a.jsx)("dd",{children:(0,a.jsx)("a",{href:`mailto:${e.email}`,children:e.email})})]}),e.phone&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("dt",{children:"Phone"}),(0,a.jsx)("dd",{children:(0,a.jsx)("a",{href:`tel:${e.phone}`,children:e.phone})})]}),n.map(([e,t])=>(0,a.jsxs)("div",{className:"ld-field-row",children:[(0,a.jsx)("dt",{children:e.split("_").map(e=>e?e[0].toUpperCase()+e.slice(1):"").join(" ")}),(0,a.jsx)("dd",{children:t})]},e)),(0,a.jsx)("dt",{children:"Page"}),(0,a.jsx)("dd",{children:e.pageTitle||e.pageSlug||"â"}),e.sourceUrl&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("dt",{children:"Source URL"}),(0,a.jsx)("dd",{children:(0,a.jsx)("a",{href:e.sourceUrl,target:"_blank",rel:"noreferrer",children:e.sourceUrl})})]}),e.campaignId&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("dt",{children:"Campaign"}),(0,a.jsx)("dd",{children:e.campaignId})]})]})]})]},s)})})]})]}):(0,a.jsx)(tZ,{error:d,onSignIn:async()=>{p(null);try{let e=new ec.GoogleAuthProvider;e.setCustomParameters({prompt:"select_account"}),await (0,el.signInWithPopup)(tY(),e)}catch(e){p(e.message)}}}):(0,a.jsx)(tZ,{})}function tZ({onSignIn:e,onSignOut:t,error:s}){return(0,a.jsxs)("main",{className:"ld-root ld-gate",children:[(0,a.jsx)("style",{children:tX}),e&&(0,a.jsx)("button",{className:"ld-btn",onClick:e,children:"Sign in with Google"}),t&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("a",{className:"ld-btn",href:"/ownerportal",children:"â Back to the owner portal"}),(0,a.jsx)("button",{className:"ld-btn ld-btn-quiet",onClick:t,children:"Sign out"})]}),s&&(0,a.jsx)("p",{className:"ld-error",children:s})]})}let tX=` 1221.ld-root { 1222 /* Factlen tokens â owner directive 2026-09-11: black/white/red. */ 1223 --ld-bg: #ffffff; --ld-surface: #fff; --ld-alt: #f6f6f6; --ld-border: #e6e6e6; 1224 --ld-text: #161616; --ld-muted: #5a5a5a; --ld-faint: #8a8a8a; 1225 --ld-accent: #C8102E; --ld-accent-strong: #a30d26; --ld-accent-soft: #fde2e6; 1226 --ld-err: #C8102E; --ld-err-soft: #fde2e6; 1227 max-width: 900px; margin: 0 auto; padding: 32px 20px 80px; 1228 color: var(--ld-text); 1229 font-family: -apple-system, "SF Pro Text", ui-sans-serif, system-ui, sans-serif; 1230} 1231@media (prefers-color-scheme: dark) { 1232 .ld-root { 1233 --ld-bg: #0d1117; --ld-surface: #161b22; --ld-alt: #161b22; --ld-border: #30363d; 1234 --ld-text: #d4d4d4; --ld-muted: #9a9a9a; --ld-faint: #6e7681; 1235 --ld-accent: #ea5a72; --ld-accent-strong: #f2879a; --ld-accent-soft: #2a1216; 1236 --ld-err: #f85149; --ld-err-soft: #2a1216; 1237 } 1238} 1239.ld-gate { display: flex; flex-direction: column; align-items: center; gap: 10px; padding-top: 80px; } 1240.ld-bar { display: flex; justify-content: flex-end; padding-bottom: 10px; } 1241.ld-who { display: flex; align-items: center; gap: 10px; font-size: .78rem; color: var(--ld-faint); } 1242.ld-muted { color: var(--ld-faint); font-size: .85rem; } 1243.ld-error { color: var(--ld-err); font-size: .88rem; background: var(--ld-err-soft); padding: 8px 12px; border-radius: 8px; } 1244.ld-btn { font: inherit; font-size: .78rem; font-weight: 700; padding: 7px 14px; border-radius: 8px; 1245 border: 1px solid var(--ld-border); background: var(--ld-accent-soft); color: var(--ld-accent-strong); 1246 text-decoration: none; cursor: pointer; } 1247.ld-btn-quiet { background: transparent; color: var(--ld-faint); } 1248.ld-summary { display: flex; gap: 12px; margin: 14px 0; flex-wrap: wrap; } 1249.ld-stat { flex: 1; min-width: 180px; background: var(--ld-surface); border: 1px solid var(--ld-border); 1250 border-radius: 10px; padding: 12px 16px; display: flex; flex-direction: column; } 1251.ld-stat-bad { border-color: var(--ld-err); background: var(--ld-err-soft); } 1252.ld-stat-n { font-size: 1.6rem; font-weight: 800; } 1253.ld-stat-l { font-size: .72rem; color: var(--ld-faint); }
1254.ld-stat-bad .ld-stat-l { color: var(--ld-err); } 1255.ld-filters { display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; } 1256.ld-select, .ld-search { font: inherit; font-size: .8rem; padding: 7px 10px; border-radius: 8px; 1257 border: 1px solid var(--ld-border); background: var(--ld-surface); color: var(--ld-text); } 1258.ld-search { flex: 1; min-width: 200px; } 1259.ld-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; } 1260.ld-card { background: var(--ld-surface); border: 1px solid var(--ld-border); border-radius: 10px; overflow: hidden; } 1261.ld-card-head { width: 100%; display: flex; align-items: center; gap: 10px; padding: 10px 14px; 1262 background: transparent; border: none; cursor: pointer; text-align: left; color: var(--ld-text); font: inherit; } 1263.ld-card-head:hover { background: var(--ld-alt); } 1264.ld-card-name { font-weight: 800; font-size: .85rem; } 1265.ld-card-biz { color: var(--ld-faint); font-size: .74rem; flex: 1; } 1266.ld-card-when { color: var(--ld-faint); font-size: .74rem; } 1267.ld-badge { font-size: .66rem; font-weight: 700; padding: 2px 8px; border-radius: 999px; } 1268.ld-badge-bad { background: var(--ld-err-soft); color: var(--ld-err); } 1269.ld-caret { color: var(--ld-faint); font-size: .7rem; } 1270.ld-card-body { padding: 4px 16px 14px; border-top: 1px solid var(--ld-border); } 1271.ld-note { font-size: .74rem; color: var(--ld-faint); margin: 10px 0; } 1272.ld-fields { display: grid; grid-template-columns: max-content 1fr; gap: 4px 12px; font-size: .8rem; margin: 8px 0 0; } 1273.ld-fields dt { color: var(--ld-faint); } 1274.ld-fields dd { margin: 0; } 1275.ld-fields a { color: var(--ld-accent-strong); } 1276.ld-field-row { display: contents; } 1277`;var ew=ew,ek=ew;let t0="ops_profile_map",t1="latest";function t2(){let e=(0,ei.getAuth)((0,eu.getFirebaseApp)());return e.tenantId="owner-portal-w358j",e}let t6={ios:"iOS",android:"Android",windows:"Windows",web:"Web"},t5={youtube:"YouTube",facebook:"Facebook",instagram:"Instagram",tiktok:"TikTok",site:"Site"},t8={music:"Music",business:"Business"};function t4(){let[e,t]=(0,r.useState)(null),[s,n]=(0,r.useState)(!1),[i,o]=(0,r.useState)(null),[l,c]=(0,r.useState)(!1),[d,p]=(0,r.useState)(null),h=/permission|insufficient/i.test(d??"");return((0,r.useEffect)(()=>(0,eo.onAuthStateChanged)(t2(),e=>{t(e),n(!0)}),[]),(0,r.useEffect)(()=>{if(!e){o(null),c(!1);return}return(0,eN.onSnapshot)((0,ek.a6)((0,ew.aX)((0,eu.getFirebaseApp)()),t0,t1),e=>{let a=e.data();if(!a||!Array.isArray(a.apps)||!Array.isArray(a.channels)){let e=`Malformed ${t0}/${t1}: missing apps/channels array. Run profile_map_sync.py.`;console.error(e),p(e),c(!0);return}o(a),c(!0),p(null)},e=>{let a=`Could not read ${t0} from Firestore: ${e.message}`;console.error(a),p(a),c(!0)})},[e]),s)?e?h?(0,a.jsx)(sa,{onSignOut:()=>(0,em.signOut)(t2())}):(0,a.jsxs)("main",{className:"pm-root",children:[(0,a.jsx)("style",{children:st}),(0,a.jsxs)("div",{className:"pm-bar",children:[(0,a.jsx)("span",{}),(0,a.jsxs)("span",{className:"pm-who",children:[e.email,(0,a.jsx)("button",{className:"pm-btn pm-btn-quiet",onClick:()=>(0,em.signOut)(t2()),children:"Sign out"})]})]}),(0,a.jsx)(eS.OwnerNav,{active:"profilemap"}),d&&(0,a.jsx)("p",{className:"pm-error",children:d}),!d&&!l&&(0,a.jsx)("p",{className:"pm-muted",children:"Loadingâ¦"}),!d&&l&&i&&(0,a.jsx)(t3,{map:i})]}):(0,a.jsx)(sa,{error:d,onSignIn:async()=>{p(null);try{let e=new ec.GoogleAuthProvider;e.setCustomParameters({prompt:"select_account"}),await (0,el.signInWithPopup)(t2(),e)}catch(e){p(e.message)}}}):(0,a.jsx)(sa,{})}function t3({map:e}){let t=e.summary??{};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("p",{className:"pm-lede",children:[t.totalApps??e.apps.length," apps · ",t.totalChannels??e.channels.length," ","channels · ",t.channelsFullyCovered??0," channels with every platform linked · derived ",function(e){if(!e)return"â";let a=new Date(e);return Number.isNaN(a.getTime())?"â":a.toLocaleString()}(e.lastSyncAt)]}),(0,a.jsx)("h2",{className:"pm-h2",children:"Apps"}),(0,a.jsx)(t9,{apps:e.apps}),(0,a.jsx)("h2",{className:"pm-h2",children:"Channels â Music"}),(0,a.jsx)(se,{channels:e.channels.filter(e=>"music"===e.category)}),(0,a.jsx)("h2",{className:"pm-h2",children:"Channels â Business"}),(0,a.jsx)(se,{channels:e.channels.filter(e=>"business"===e.category)})]})}function t7({href:e}){return e?(0,a.jsx)("a",{className:"pm-link",href:e,target:"_blank",rel:"noreferrer noopener",children:"Open"}):(0,a.jsx)("span",{className:"pm-off",children:"â"})}function t9({apps:e}){let t=Object.keys(t6);return 0===e.length?(0,a.jsx)("p",{className:"pm-muted",children:"No apps in the map yet."}):(0,a.jsx)("div",{className:"pm-scroll",children:(0,a.jsxs)("table",{className:"pm-table",children:[(0,a.jsx)("thead",{children:(0,a.jsxs)("tr",{children:[(0,a.jsx)("th",{children:"App"}),t.map(e=>(0,a.jsx)("th",{children:t6[e]},e))]})}),(0,a.jsx)("tbody",{children:e.map(e=>(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{className:"pm-name",children:e.name}),t.map(t=>(0,a.jsx)("td",{children:(0,a.jsx)(t7,{href:e.platform_links[t]})},t))]},e.name))})]})})}function se({channels:e}){let t=Object.keys(t5);return 0===e.length?(0,a.jsx)("p",{className:"pm-muted",children:"No channels in this category."}):(0,a.jsx)("div",{className:"pm-scroll",children:(0,a.jsxs)("table",{className:"pm-table",children:[(0,a.jsx)("thead",{children:(0,a.jsxs)("tr",{children:[(0,a.jsx)("th",{children:"Channel"}),t.map(e=>(0,a.jsx)("th",{children:t5[e]},e))]})}),(0,a.jsx)("tbody",{children:e.map(e=>(0,a.jsxs)("tr",{children:[(0,a.jsxs)("td",{className:"pm-name",children:[e.name,(0,a.jsx)("span",{className:"pm-cat",children:t8[e.category]})]}),t.map(t=>(0,a.jsx)("td",{children:(0,a.jsx)(t7,{href:e.platform_links[t]})},t))]},e.name))})]})})}function sa({onSignIn:e,onSignOut:t,error:s}){return(0,a.jsxs)("main",{className:"pm-root pm-gate",children:[(0,a.jsx)("style",{children:st}),e&&(0,a.jsx)("button",{className:"pm-btn",onClick:e,children:"Sign in with Google"}),t&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("a",{className:"pm-btn",href:"/ownerportal",children:"â Back to the owner portal"}),(0,a.jsx)("button",{className:"pm-btn pm-btn-quiet",onClick:t,children:"Sign out"})]}),s&&(0,a.jsx)("p",{className:"pm-error",children:s})]})}let st=` 1278.pm-root { 1279 /* Factlen tokens â owner directive 2026-09-11: black/white/red. Same values 1280 as AppMap.tsx's --am-* tokens, prefixed --pm- to avoid collision. */ 1281 --pm-bg: #ffffff; --pm-surface: #fff; --pm-alt: #f6f6f6; --pm-border: #e6e6e6; 1282 --pm-text: #161616; --pm-muted: #5a5a5a; --pm-faint: #8a8a8a; 1283 --pm-accent: #C8102E; --pm-accent-strong: #a30d26; --pm-accent-soft: #fde2e6; 1284 max-width: 980px; margin: 0 auto; padding: 32px 20px 80px; 1285 color: var(--pm-text); 1286 font-family: -apple-system, "SF Pro Text", ui-sans-serif, system-ui, sans-serif; 1287} 1288@media (prefers-color-scheme: dark) { 1289 .pm-root { 1290 --pm-bg: #0d1117; --pm-surface: #161b22; --pm-alt: #161b22; --pm-border: #30363d; 1291 --pm-text: #d4d4d4; --pm-muted: #9a9a9a; --pm-faint: #6e7681; 1292 --pm-accent: #ea5a72; --pm-accent-strong: #f2879a; --pm-accent-soft: #2a1216; 1293 } 1294} 1295.pm-gate { min-height: 60vh; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 14px; } 1296.pm-bar { display: flex; justify-content: space-between; align-items: center; gap: 12px; 1297 flex-wrap: wrap; padding-bottom: 14px; border-bottom: 1px solid var(--pm-border); margin-bottom: 20px; } 1298.pm-who { display: flex; align-items: center; gap: 10px; font-size: .78rem; color: var(--pm-faint); } 1299.pm-h2 { font-size: .74rem; font-weight: 800; text-transform: uppercase; letter-spacing: .07em; 1300 color: var(--pm-faint); margin: 24px 0 9px; } 1301.pm-lede { color: var(--pm-muted); font-size: .88rem; line-height: 1.6; margin: 0 0 16px; max-width: 74ch; } 1302.pm-muted { color: var(--pm-muted); font-size: .9rem; } 1303.pm-error { color: var(--pm-accent); font-size: .88rem; line-height: 1.55; } 1304.pm-btn { display: inline-flex; align-items: center; gap: 6px; font: inherit; font-size: .8rem; font-weight: 700; 1305 padding: 7px 13px; border-radius: 8px; border: 1px solid transparent; cursor: pointer; 1306 background: var(--pm-accent-soft); color: var(--pm-accent-strong); text-decoration: none; } 1307.pm-btn:hover { border-color: var(--pm-accent); } 1308.pm-btn:focus-visible { outline: 2px solid var(--pm-accent); outline-offset: 2px; } 1309.pm-btn-quiet { background: transparent; color: var(--pm-faint); border-color: var(--pm-border); } 1310 1311.pm-scroll { overflow-x: auto; border: 1px solid var(--pm-border); border-radius: 12px; background: var(--pm-surface); margin-bottom: 8px; } 1312.pm-table { width: 100%; border-collapse: collapse; font-size: .82rem; min-width: 560px; } 1313.pm-table thead th { text-align: left; font-size: .64rem; font-weight: 800; text-transform: uppercase; 1314 letter-spacing: .06em; color: var(--pm-faint); padding: 11px 10px; border-bottom: 1px solid var(--pm-border); white-space: nowrap; } 1315.pm-table tbody td { padding: 11px 10px; border-bottom: 1px solid var(--pm-border); vertical-align: middle; } 1316.pm-table tbody tr:last-child td { border-bottom: none; } 1317.pm-table tbody tr:hover { background: var(--pm-alt); } 1318.pm-name { font-weight: 800; letter-spacing: -.01em; white-space: nowrap; } 1319.pm-cat { display: inline-block; margin-left: 8px; font-size: .62rem; font-weight: 700; letter-spacing: .05em; 1320 text-transform: uppercase; color: var(--pm-faint); } 1321.pm-link { font-weight: 700; color: var(--pm-accent-strong); text-decoration: none; } 1322.pm-link:hover { text-decoration: underline; } 1323.pm-link:focus-visible { outline: 2px solid var(--pm-accent); outline-offset: 2px; } 1324.pm-off { color: var(--pm-faint); } 1325`;var ew=ew,ek=ew;let ss="ops_owner_portal",sr="latest";function sn(){let e=(0,ei.getAuth)((0,eu.getFirebaseApp)());return e.tenantId="owner-portal-w358j",e}let si=e=>"number"==typeof e&&Number.isFinite(e);function so(e){return si(e)?new Intl.NumberFormat("en-US",{style:"currency",currency:"USD",minimumFractionDigits:2,maximumFractionDigits:2}).format(e):"â"}function sl(e){return si(e)?new Intl.NumberFormat("en-US").format(e):"â"}function sc(e){return e.replace(/_/g," ").replace(/\b\w/g,e=>e.toUpperCase())}function sd(e){return e.split("/").map(sc).join(" ⺠")}let sp={text:"Gemini text",image:"Gemini image",video:"Veo video",tts:"TTS",audio_analysis:"Gemini audio analysis",vision_analysis:"Gemini vision analysis",claude:"Claude",deepseek:"DeepSeek",llama:"Llama",openai_oss:"GPT-OSS",qwen:"Qwen",kimi:"Kimi",embedding:"Embedding"};function sh(e){return sp[e]??sc(e)}let sm=new Set(["claude","llama","openai_oss","qwen","kimi","deepseek"]),su=["meta/","qwen/","moonshotai/","openai/","deepseek-ai/"],sf=new Set(["gemini-pro-latest","gemini-3.1-pro-preview","gemini-flash-latest","gemini-3.8-flash","gemini-3.7-flash","gemini-flash-lite-latest","gemini-3.5-flash-lite","gemini-3-pro-image","gemini-3.1-flash-image","gemini-3.1-flash-lite-image","gemini-2.5-flash-image","veo-3.1-generate-001","veo-3.1-fast-generate-001","veo-3.1-lite-generate-001","veo-3.0-generate-001","veo-3.0-fast-generate-001","veo-2.0-generate-001","gemini-omni-flash-preview","gemini-2.5-pro-preview-tts","gemini-2.5-pro-tts","gemini-3.1-flash-tts-preview","gemini-2.5-flash-preview-tts","gemini-2.5-flash-tts","chirp_3","chirp_2","chirp","claude-sonnet-5"]);function sg(){let[e,t]=(0,r.useState)(null),[s,n]=(0,r.useState)(!1),[i,o]=(0,r.useState)(null),[l,c]=(0,r.useState)(!1),[d,p]=(0,r.useState)(null),[h,m]=(0,r.useState)(null),[u,f]=(0,r.useState)(null),[g,x]=(0,r.useState)(null),b=(0,r.useRef)(null),y=/permission|insufficient/i.test(d??"");if((0,r.useEffect)(()=>(0,eo.onAuthStateChanged)(sn(),e=>{t(e),n(!0)}),[]),(0,r.useEffect)(()=>
1325{if(!e){o(null),c(!1);return}return(0,eN.onSnapshot)((0,ek.a6)((0,ew.aX)((0,eu.getFirebaseApp)()),ss,sr),e=>{if(!e.exists()){p(ss+"/"+sr+" does not exist yet â run owner_portal_sync.py"),c(!0);return}o(e.data()),c(!0),p(null)},e=>{let a="Could not read "+ss+"/"+sr+" from Firestore: "+e.message;console.error(a),p(a),c(!0)})},[e]),!s)return(0,a.jsx)(sk,{});if(!e)return(0,a.jsx)(sk,{error:d,onSignIn:async()=>{p(null);try{let e=new ec.GoogleAuthProvider;e.setCustomParameters({prompt:"select_account"}),await (0,el.signInWithPopup)(sn(),e)}catch(e){p(e.message)}}});if(y)return(0,a.jsx)(sk,{onSignOut:()=>(0,em.signOut)(sn())});let v=i?.genaiCost??null;return(0,a.jsxs)("main",{className:"gc-root",children:[(0,a.jsx)("style",{children:sN}),(0,a.jsxs)("div",{className:"gc-shell",children:[(0,a.jsxs)("div",{className:"gc-bar",children:[(0,a.jsx)("p",{className:"gc-eyebrow",children:"Cost â three sections, never blended"}),(0,a.jsxs)("span",{className:"gc-who",children:[e.email,(0,a.jsx)("button",{className:"gc-btn gc-btn-quiet",onClick:()=>(0,em.signOut)(sn()),children:"Sign out"})]})]}),(0,a.jsx)(eS.OwnerNav,{active:"genaicost"}),d&&!y&&(0,a.jsx)("p",{className:"gc-error",children:d}),!d&&!l&&(0,a.jsx)("p",{className:"gc-muted",children:"Loadingâ¦"}),!d&&l&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("p",{className:"gc-note",children:["Three cost sections, kept strictly separate â never summed together, never blended in a query. Synced ",function(e){if(!e)return"â";let a=new Date(e);return Number.isNaN(a.getTime())?"â":a.toLocaleString()}(i?.lastSyncAt),"."]}),(0,a.jsx)(sx,{num:1,title:"fenul-general",tint:"var(--gc-tint-a)",note:"General infra only. Vertex AI is disabled here (verified live) â this section is structurally Vertex-free, not just Vertex-free by convention. Also the $15/day GCP cap.",data:i?.fenulGeneralCost}),(0,a.jsx)(sx,{num:2,title:"Google / Soft Flourish",tint:"var(--gc-tint-b)",note:"Apps with direct AI usage (MeetWing's Gemini + Chirp), billed to the soft-flourish project. Real $, same shared billing export as section 1, filtered by project.id â never fenul-general's.",data:i?.s
1325oftFlourishCost}),(0,a.jsxs)("div",{className:"gc-section-head",children:[(0,a.jsx)("p",{className:"gc-section-num",children:"3"}),(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"gc-section-title",children:"n8n-billing (Vertex / GenAI)"}),(0,a.jsx)("p",{className:"gc-section-note",children:"Essentially every internal generation pipeline (content, music/video, articles) runs through the lvs n8n SA, billed to its own project root-catfish-206221 â off-limits to query directly (hard rule). Shown as COUNTS, not dollars â real numbers straight from the call log, no price table involved. No dollar alert on this section."})]})]}),v?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"gc-kpis",children:[(0,a.jsxs)("div",{className:"gc-kpi",children:[(0,a.jsx)("p",{className:"gc-kpi-label",children:"Calls today"}),(0,a.jsx)("p",{className:"gc-kpi-val",children:sl(v.callsToday)}),(0,a.jsx)("p",{className:"gc-kpi-sub",children:"same figure the daily digest emails"})]}),(0,a.jsxs)("div",{className:"gc-kpi",children:[(0,a.jsx)("p",{className:"gc-kpi-label",children:"Calls, trailing 7 days"}),(0,a.jsx)("p",{className:"gc-kpi-val",children:sl(v.calls7d)})]}),(0,a.jsxs)("div",{className:"gc-kpi",children:[(0,a.jsx)("p",{className:"gc-kpi-label",children:"No model identified (7d)"}),(0,a.jsx)("p",{className:"gc-kpi-val",children:null!==v.noModelPct7d?v.noModelPct7d+"%":"â"}),(0,a.jsx)("p",{className:"gc-kpi-sub",children:"call sites still missing model= â a real gap, not a price gap"})]})]}),v.trend30dCalls.length>0&&(0,a.jsxs)("div",{className:"gc-panel",children:[(0,a.jsxs)("div",{className:"gc-panel-head",children:[(0,a.jsx)("p",{className:"gc-panel-title",children:"30-day trend"}),(0,a.jsx)("p",{className:"gc-panel-note",children:"calls per day"})]}),(0,a.jsx)(sj,{points:v.trend30dCalls.map(e=>({date:e.date,costUsd:e.calls})),tint:"var(--gc-tint-c)",valueFmt:sl})]}),(0,a.jsxs)("div",{className:"gc-panel",children:[(0,a.jsxs)("div",{className:"gc-panel-head",children:[(0,a.jsx)("p",{className:"gc-panel-title",children:"Calls today, by category"}),(0,a.jsx)("p",{className:"gc-panel-note",children:"coarse grouping (same as the daily digest email) â click a row to deep-dive below"})]}),0===v.callsTodayByKind.length?(0,a.jsx)("p",{className:"gc-muted",children:"No calls yet today."}):(0,a.jsx)(sb,{tint:"var(--gc-tint-c)",rows:v.callsTodayByKind.slice().sort((e,a)=>a.n-e.n).map(e=>({key:e.kind,label:sh(e.kind),value:e.n,display:sl(e.n)})),onRowClick:e=>{m(e),f(null),x(null),b.current?.scrollIntoView({behavior:"smooth",block:"start"})}})]}),(0,a.jsx)(sw,{data:v.byKindServiceModel7d,kind:h,root:u,lane:g,onSetKind:e=>{m(e),f(null),x(null)},onSetRoot:e=>{f(e),x(null)},onSetLane:x,panelRef:b})]}):(0,a.jsxs)("p",{className:"gc-muted",children:["No genaiCost data yet in ",ss,"/",sr," â run owner_portal_sync.py."]})]})]})]})}function sx({num:e,title:t,note:s,tint:r,data:n}){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"gc-section-head",children:[(0,a.jsx)("p",{className:"gc-section-num",children:e}),(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"gc-section-title",children:t}),(0,a.jsx)("p",{className:"gc-section-note",children:s})]})]}),n?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"gc-kpis",children:[(0,a.jsxs)("div",{className:"gc-kpi",children:[(0,a.jsx)("p",{className:"gc-kpi-label",children:"Today"}),(0,a.jsx)("p",{className:"gc-kpi-val",children:so(n.today)})]}),(0,a.jsxs)("div",{className:"gc-kpi",children:[(0,a.jsx)("p",{className:"gc-kpi-label",children:"Trailing 7 days"}),(0,a.jsx)("p",{className:"gc-kpi-val",children:so(n.trailing7d)})]})]}),n.trend30d.length>0&&(0,a.jsxs)("div",{className:"gc-panel",children:[(0,a.jsxs)("div",{className:"gc-panel-head",children:[(0,a.jsx)("p",{className:"gc-panel-title",children:"30-day trend"}),(0,a.jsx)("p",{className:"gc-panel-note",children:"real cost per day"})]}),(0,a.jsx)(sj,{points:n.trend30d,tint:r})]}),(0,a.jsxs)("div",{className:"gc-panel",children:[(0,a.jsxs)("div",{className:"gc-panel-head",children:[(0,a.jsx)("p",{className:"gc-panel-title",children:"By GCP service (7d)"}),(0,a.jsx)("p",{className:"gc-panel-note",children:"highest cost first"})]}),0===n.byService7d.length?(0,a.jsx)("p",{className:"gc-muted",children:"No spend in this window."}):(0,a.jsx)(sb,{tint:r,rows:n.byService7d.map(e=>({key:e.service,label:e.service,value:e.costUsd,display:so(e.costUsd)}))})]})]}):(0,a.jsx)("p",{className:"gc-muted",children:"No data yet â run owner_portal_sync.py."})]})}
1325function sb({rows:e,tint:t,onRowClick:s}){let r=Math.max(1,...e.map(e=>e.value));return(0,a.jsx)("ul",{className:"gc-barlist",children:e.map(e=>(0,a.jsx)("li",{className:"gc-bar-row-li",children:(0,a.jsxs)(s?"button":"div",{className:s?"gc-bar-row gc-bar-row-clickable":"gc-bar-row",style:{"--gc-bar-tint":t},...s?{onClick:()=>s(e.key),type:"button"}:{},children:[(0,a.jsxs)("title",{children:[e.label,": ",e.display,e.sub?` (${e.sub})`:""]}),(0,a.jsx)("span",{className:"gc-bar-label",children:e.label}),(0,a.jsx)("span",{className:"gc-bar-track",children:(0,a.jsx)("span",{className:"gc-bar-fill",style:{width:Math.max(2,100*e.value/r)+"%"}})}),(0,a.jsxs)("span",{className:"gc-bar-vals",children:[(0,a.jsx)("span",{className:"gc-bar-val",children:e.display}),e.sub&&(0,a.jsx)("span",{className:"gc-bar-sub",children:e.sub})]})]})},e.key))})}function sy(e){let a=e.indexOf("/");return -1===a?{root:e,lane:""}:{root:e.slice(0,a),lane:e.slice(a+1)}}let sv="\0general";function sw({data:e,kind:t,root:s,lane:r,onSetKind:n,onSetRoot:i,onSetLane:o,panelRef:l}){let c,d,p=null===t?e:e.filter(e=>e.kind===t),h=new Map;for(let a of e)h.set(a.kind,(h.get(a.kind)??0)+a.calls);let m=[...h.entries()].sort((e,a)=>a[1]-e[1]),u=[{label:"All",onClick:()=>{n(null),i(null)}}];if(null!==t&&u.push({label:sh(t),onClick:()=>i(null)}),null!==s&&u.push({label:sc(s),onClick:()=>o(null)}),null!==r&&u.push({label:""===r?"(general)":sd(r),onClick:()=>{}}),null===s){let e=new Map;for(let a of p){let{root:t}=sy(a.service);e.set(t,(e.get(t)??0)+a.calls)}let t=[...e.entries()].sort((e,a)=>a[1]-e[1]).map(([e,a])=>({key:e,label:sc(e),value:a,display:sl(a)}));c="By service",d=0===t.length?(0,a.jsx)("p",{className:"gc-muted",children:"No GenAI calls in this window."}):(0,a.jsx)(sb,{tint:"var(--gc-tint-c)",rows:t,onRowClick:i})}else{let e=p.filter(e=>sy(e.service).root===s),t=e.some(e=>""!==sy(e.service).lane);if(null===r&&t){let t=new Map;for(let a of e){let e=sy(a.service).lane,s=""===e?sv:e;t.set(s,(t.get(s)??0)+a.calls)}let s=[...t.entries()].sort((e,a)=>a[1]-e[1]).map(([e,a])=>({key:e,label:e===sv?"(general)":sd(e),value:a,display:sl(a)}));c="By sub-category",d=(0,a.jsx)(sb,{tint:"var(--gc-tint-c)",rows:s,onRowClick:e=>o(e===sv?"":e)})}else{let s=t?e.filter(e=>sy(e.service).lane===(r??"")):e,n=new Map;for(let e of s)n.set(e.model,(n.get(e.model)??0)+e.calls);let i=[...n.entries()].sort((e,a)=>a[1]-e[1]);c="By model",d=0===i.length?(0,a.jsx)("p",{className:"gc-muted",children:"No GenAI calls in this window."}):(0,a.jsx)("ul",{className:"gc-svc-models gc-svc-models-leaf",children:i.map(([e,t])=>{let s=!(sm.has(e)||sf.has(e));return(0,a.jsxs)("li",{title:s?`${e} â not in a DEV_GUIDE fallback chain`:e,children:[(0,a.jsx)("span",{className:s?"gc-svc-model-name gc-svc-model-offchain":"gc-svc-model-name",children:function(e){if(sm.has(e))return sh(e)+" (exact model not logged)";let a=e;for(let e of su)if(a.startsWith(e)){a=a.slice(e.length);break}a=a.replace(/-maas$/,"");let t=/-latest$/.test(a);return a=(a=a.replace(/-latest$/,"")).replace(/[-_]/g," ").replace(/\b\w/g,e=>e.toUpperCase()),t?a+" (latest â Google-managed alias, exact dated version not logged)":a}(e)}),(0,a.jsx)("span",{className:"gc-svc-model-n",children:sl(t)})]},e)})})}}return(0,a.jsxs)("div",{className:"gc-panel",ref:l,children:[(0,a.jsxs)("div",{className:"gc-panel-head",children:[(0,a.jsxs)("p",{className:"gc-panel-title",children:["Deep dive (7d) â ",c]}),(0,a.jsxs)("p",{className:"gc-panel-note",children:["click a row to drill in · ",(0,a.jsx)("span",{className:"gc-svc-model-offchain",children:"yellow"})," = not in a DEV_GUIDE fallback chain"]})]}),(0,a.jsx)("div",{className:"gc-chipbar",children:m.map(([e,s])=>(0,a.jsxs)("button",{type:"button",className:e===t?"gc-chip gc-chip-active":"gc-chip",onClick:()=>n(e===t?null:e),children:[sh(e)," ",(0,a.jsx)("span",{className:"gc-chip-n",children:sl(s)})]},e))}),(0,a.jsx)("nav",{className:"gc-crumbs","aria-label":"Drill-down path",children:u.map((e,t)=>(0,a.jsxs)("span",{className:"gc-crumb-wrap",children:[t>0&&(0,a.jsx)("span",{className:"gc-crumb-sep",children:"âº"}),t===u.length-1?(0,a.jsx)("span",{className:"gc-crumb gc-crumb-current",children:e.label}):(0,a.jsx)("button",{type:"button",className:"gc-crumb gc-crumb-link",onClick:e.onClick,children:e.label})]},t))}),d]})}function sj({points:e,tint:t="var(--gc-tint-a)",valueFmt:s=so}){let n="gc-trend-fill-"+(0,r.useId)().replace(/[:]/g,""),i=Math.max(1,...e.map(e=>e.costUsd)),o=e.length,l=e=>52+e/Math.max(1,o-1)*840,c=e=>176-e/i*164,d=e.map((e,a)=>(a?"L":"M")+l(a).toFixed(1)+","+c(e.costUsd).toFixed(1)).join(" "),p=d+" L"+l(o-1).toFixed(1)+","+176+" L"+l(0).toFixed(1)+","+176+" Z";return(0,a.jsxs)("svg",{className:"gc-area",viewBox:"0 0 900 200",preserveAspectRatio:"none",children:[[0,.25,.5,.75,1].map(e=>{let t=176-164*e;return(0,a.jsxs)("g",{children:[(0,a.jsx)("line",{className:"gc-grid-line",x1:52,y1:t,x2:892,y2:t}),(0,a.jsx)("text",{className:"gc-axis-txt",x:45,y:t+3,textAnchor:"end",children:s===so?"$"+Math.round(i*e).toLocaleStr
1325ing():Math.round(i*e).toLocaleString()})]},e)}),(0,a.jsx)("defs",{children:(0,a.jsxs)("linearGradient",{id:n,x1:"0",y1:"0",x2:"0",y2:"1",children:[(0,a.jsx)("stop",{offset:"0%",stopColor:t,stopOpacity:".28"}),(0,a.jsx)("stop",{offset:"100%",stopColor:t,stopOpacity:"0"})]})}),(0,a.jsx)("path",{d:p,fill:"url(#"+n+")"}),(0,a.jsx)("path",{d:d,fill:"none",stroke:t,strokeWidth:"2.1",strokeLinejoin:"round"}),e.map((e,r)=>(0,a.jsx)("circle",{cx:l(r),cy:c(e.costUsd),r:r===o-1?3.4:2,fill:t,opacity:+(r===o-1),children:(0,a.jsxs)("title",{children:[e.date,": ",s(e.costUsd)]})},e.date)),[0,Math.floor(o/2),o-1].map(t=>(0,a.jsx)("text",{className:"gc-axis-txt",x:l(t),y:194,textAnchor:"middle",children:e[t].date.slice(5)},t))]})}function sk({onSignIn:e,onSignOut:t,error:s}){return(0,a.jsxs)("main",{className:"gc-root gc-gate",children:[(0,a.jsx)("style",{children:sN}),e&&(0,a.jsx)("button",{className:"gc-btn",onClick:e,children:"Sign in with Google"}),t&&(0,a.jsx)("button",{className:"gc-btn gc-btn-quiet",onClick:t,children:"Sign out"}),s&&(0,a.jsx)("p",{className:"gc-error",children:s})]})}let sN=` 1326.gc-root { 1327 /* Factlen tokens â owner directive 2026-09-11: black/white/red. Chart tints 1328 (a/b/c) untouched â one hue per chart, dataviz-skill-validated, functional. */ 1329 --gc-canvas: #0d1117; --gc-canvas-2: #0d1117; 1330 --gc-surface: rgba(255, 255, 255, 0.045); --gc-surface-2: rgba(255, 255, 255, 0.075); 1331 --gc-border: rgba(255, 255, 255, 0.09); --gc-border-str: rgba(255, 255, 255, 0.18); 1332 --gc-text: #d4d4d4; --gc-muted: #9a9a9a; --gc-faint: #6e7681; 1333 --gc-accent: #ea5a72; --gc-bad: #f85149; --gc-warn: #f5a524; 1334 --gc-tint-a: #3987e5; --gc-tint-b: #9085e9; --gc-tint-c: #199e70; 1335 --gc-glow: 0 0 0 1px rgba(255,255,255,.05), 0 18px 48px -22px rgba(0,0,0,.9); 1336 min-height: 100vh; margin: 0; padding: 0 0 96px; color: var(--gc-text); 1337 background-color: var(--gc-canvas-2); 1338 background-repeat: no-repeat; 1339 font-family: -apple-system, "SF Pro Text", ui-sans-serif, system-ui, "Segoe UI", sans-serif; 1340 -webkit-font-smoothing: antialiased; 1341 font-variant-numeric: tabular-nums; 1342} 1343@media (prefers-color-scheme: light) { 1344 .gc-root { 1345 --gc-canvas: #ffffff; --gc-canvas-2: #ffffff; 1346 --gc-surface: rgba(16, 24, 40, 0.03); --gc-surface-2: #ffffff; 1347 --gc-border: #e6e6e6; --gc-border-str: rgba(16, 24, 40, 0.20); 1348 --gc-text: #161616; --gc-muted: #5a5a5a; --gc-faint: #8a8a8a; 1349 --gc-accent: #C8102E; --gc-bad: #C8102E; --gc-warn: #a15c00; 1350 --gc-tint-a: #2a78d6; --gc-tint-b: #4a3aa7; --gc-tint-c: #1baf7a; 1351 --gc-glow: 0 0 0 1px rgba(16,24,40,.04), 0 18px 44px -26px rgba(16,24,40,.35); 1352 } 1353} 1354.gc-shell { max-width: 1180px; margin: 0 auto; padding: 0 clamp(16px, 3vw, 32px); } 1355.gc-bar { 1356 display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap; 1357 padding: 14px 0 12px; border-bottom: 1px solid var(--gc-border); margin-bottom: 6px; 1358} 1359.gc-eyebrow { font-size: .95rem; font-weight: 800; letter-spacing: -.01em; margin: 0; } 1360.gc-who { display: flex; align-items: center; gap: 10px; font-size: .76rem; color: var(--gc-faint); } 1361.gc-note { font-size: .76rem; color: var(--gc-faint); line-height: 1.5; margin: 14px 0 20px; max-width: 720px; } 1362.gc-section-head { display: flex; align-items: flex-start; gap: 14px; margin: 32px 0 16px; padding-top: 20px; border-top: 1px solid var(--gc-border); } 1363.gc-section-head:first-of-type { margin-top: 0; padding-top: 0; border-top: none; } 1364.gc-section-num { 1365 flex: none; width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center; 1366 justify-content: center; font-size: .9rem; font-weight: 800; background: var(--gc-surface-2); 1367 border: 1px solid var(--gc-border-str); color: var(--gc-text); margin: 0; 1368} 1369.gc-section-title { font-size: 1.05rem; font-weight: 800; letter-spacing: -.01em; margin: 2px 0 4px; } 1370.gc-section-note { font-size: .74rem; color: var(--gc-faint); line-height: 1.5; margin: 0; max-width: 640px; } 1371.gc-kpis { display: grid; gap: 12px; margin: 0 0 24px; grid-template-columns: repeat(auto-fit, minmax(196px, 1fr)); } 1372.gc-kpi { 1373 background: var(--gc-surface); border: 1px solid var(--gc-border); border-radius: 18px; 1374 padding: 16px 17px 14px; box-shadow: var(--gc-glow); 1375} 1376.gc-kpi-label { font-size: .68rem; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--gc-faint); margin: 0 0 7px; } 1377.gc-kpi-val { font-size: clamp(1.45rem, 3vw, 1.95rem); font-weight: 800; letter-spacing: -.03em; line-height: 1; margin: 0;
1377 font-variant-numeric: normal; } 1378.gc-kpi-sub { font-size: .72rem; color: var(--gc-muted); margin: 7px 0 0; } 1379.gc-panel { 1380 background: var(--gc-surface); border: 1px solid var(--gc-border); border-radius: 20px; 1381 padding: 18px 19px 16px; box-shadow: var(--gc-glow); margin: 0 0 18px; 1382} 1383.gc-panel-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin: 0 0 14px; } 1384.gc-panel-title { font-size: .74rem; font-weight: 750; letter-spacing: .1em; text-transform: uppercase; color: var(--gc-faint); margin: 0; } 1385.gc-panel-note { font-size: .72rem; color: var(--gc-faint); margin: 0; } 1386.gc-area { width: 100%; height: clamp(150px, 22vw, 210px); display: block; overflow: visible; } 1387.gc-grid-line { stroke: var(--gc-border); stroke-width: 1; } 1388.gc-axis-txt { fill: var(--gc-faint); font-size: 9px; font-family: ui-monospace, monospace; } 1389.gc-barlist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 3px; } 1390.gc-bar-row-li { list-style: none; } 1391.gc-bar-row { 1392 display: grid; grid-template-columns: minmax(96px, 190px) minmax(0, 1fr) auto; 1393 align-items: center; gap: 12px; padding: 7px 9px; border-radius: 11px; width: 100%; 1394} 1395.gc-bar-row:hover { background: var(--gc-surface-2); } 1396.gc-bar-row-clickable { 1397 all: unset; box-sizing: border-box; display: grid; cursor: pointer; 1398 grid-template-columns: minmax(96px, 190px) minmax(0, 1fr) auto; 1399 align-items: center; gap: 12px; padding: 7px 9px; border-radius: 11px; width: 100%; 1400 font: inherit; color: inherit; 1401} 1402.gc-bar-row-clickable:hover { background: var(--gc-surface-2); } 1403.gc-bar-row-clickable:focus-visible { outline: 2px solid var(--gc-accent); outline-offset: 1px; } 1404.gc-bar-row-clickable:active .gc-bar-fill { filter: brightness(1.15); } 1405.gc-bar-label { font-size: .81rem; font-weight: 650; color: var(--gc-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } 1406.gc-bar-track { height: 9px; border-radius: 6px; background: var(--gc-surface-2); overflow: hidden; box-shadow: inset 0 0 0 1px var(--gc-border); } 1407.gc-bar-fill { 1408 display: block; height: 100%; border-radius: 6px; 1409 background: linear-gradient(90deg, var(--gc-bar-tint, var(--gc-accent)), color-mix(in srgb, var(--gc-bar-tint, var(--gc-accent)) 55%, transparent)); 1410} 1411.gc-bar-vals { display: flex; flex-direction: column; align-items: flex-end; min-width: 96px; } 1412.gc-bar-val { font-family: ui-monospace, monospace; font-size: .76rem; font-weight: 700; color: var(--gc-text); white-space: nowrap; } 1413.gc-bar-sub { font-size: .62rem; color: var(--gc-faint); white-space: nowrap; } 1414.gc-svc-grid { display: grid; gap: 12px; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); } 1415.gc-svc-card { background: var(--gc-surface-2); border: 1px solid var(--gc-border); border-radius: 14px; padding: 12px 13px; } 1416.gc-svc-name { font-size: .82rem; font-weight: 700; color: var(--gc-text); margin: 0 0 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } 1417.gc-svc-total { font-size: .68rem; color: var(--gc-faint); margin: 0 0 8px; } 1418.gc-svc-models { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; } 1419.gc-svc-models li { display: flex; justify-content: space-between; gap: 8px; font-size: .72rem; } 1420.gc-svc-model-name { color: var(--gc-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } 1421.gc-svc-model-offchain { color: var(--gc-warn); font-weight: 700; } 1422.gc-svc-model-n { font-family: ui-monospace, monospace; font-weight: 700; color: var(--gc-text); white-space: nowrap; } 1423.gc-svc-model-more { color: var(--gc-faint); font-style: italic; } 1424.gc-svc-lanes { list-style: none; margin: 10px 0 0; padding: 8px 0 0; border-top: 1px dashed var(--gc-border); display: flex; flex-direction: column; gap: 8px; } 1425.gc-svc-lane-head { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; } 1426.gc-svc-lane-name { font-size: .71rem; font-weight: 700; color: var(--gc-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } 1427.gc-svc-lane-n { font-family: ui-monospace, monospace; font-size: .68rem; font-weight: 700; color: var(--gc-muted); white-space: nowrap; } 1428.gc-svc-models-nested { margin: 3px 0 0 8px; } 1429.gc-svc-models-leaf li { padding: 6px 2px; border-bottom: 1px solid var(--gc-border); } 1430.gc-svc-models-leaf li:last-child { border-bottom: none; } 1431.gc-chipbar { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 14px; } 1432.gc-chip { 1433 font: inherit; font-size: .73rem; font-weight: 700; color: var(--gc-muted); 1434 background: var(--gc-surface-2); border: 1px solid var(--gc-border); border-radius: 999px; 1435 padding: 6px 12px; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; 1436} 1437.gc-chip:hover { border-color: var(--gc-border-str); color: var(--gc-text); } 1438.gc-chip-active { background: var(--gc-accent); border-color: var(--gc-accent); color: #fff; } 1439.gc-chip-n { font-family: ui-monospace, monospace; font-weight: 700; opacity: .8; } 1440.gc-crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; margin: 0 0 14px; } 1441.gc-crumb-wrap { display: inline-flex; align-items: center; gap: 4px; } 1442.gc-crumb-sep { color: var(--gc-faint); font-size: .78rem; } 1443.gc-crumb { font-size: .78rem; } 1444.gc-crumb-link { 1445 font: inherit; font-size: .78rem; font-weight: 700; color: var(--gc-tint-a); 1446 background: none; border: none; padding: 2px 4px; cursor: pointer; border-radius: 6px; 1447} 1448.gc-crumb-link:hover { background: var(--gc-surface-2); text-decoration: underline; } 1449.gc-crumb-current { font-weight: 700; color: var(--gc-text); padding: 2px 4px; } 1450.gc-muted { color: var(--gc-muted); font-size: .88rem; } 1451.gc-error { color: var(--gc-bad); font-size: .84rem; line-height: 1.55; } 1452.gc-btn { 1453 font: inherit; font-size: .8rem; font-weight: 700; color: var(--gc-text); 1454 background: var(--gc-surface); border: 1px solid var(--gc-border); border-radius: 10px; 1455 padding: 9px 15px; cursor: pointer; 1456} 1457.gc-btn:hover { border-color: var(--gc-border-str); } 1458.gc-btn-quiet { background: transparent; color: var(--gc-faint); font-weight: 600; padding: 6px 11px; } 1459.gc-gate { min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; } 1460`;var sS=e.i(715537);let sA="/contact",sP="[email protected]",sz="/flourishpdf",sT=[{key:"mac",platform:"Mac",requirement:"macOS 14 Sonoma or later · Apple silicon and Intel",url:"https://apps.apple.com/app/id6794034781?mt=12",display:"apps.apple.com/app/id6794034781",badge:`${sz}/mac-app-store-badge.svg`,badgeAlt:"Download on the Mac App Store",badgeWidth:187,qr:"qr-macappstore"},{key:"ios",platform:"iPhone & iPad",requirement:"iOS and iPadOS 17 or later",url:"https://apps.apple.com/app/id6794034781",display:"apps.apple.com/app/id6794034781",badge:`${sz}/app-store-badge.svg`,badgeAlt:"Download on the App Store",badgeWidth:120,qr:"qr-appstore"},{key:"android",platform:"Android",requirement:"Phones and tablets",url:"https://play.google.com/store/apps/details?id=com.softflourish.flourishpdf",display:"play.google.com/store/apps/details?id=com.softflourish.flourishpdf",badge:`${sz}/google-play-badge.png`,badgeAlt:"Get it on Google Play",badgeWidth:135,qr:"qr-googleplay"},{key:"windows",platform:"Windows",requirement:"Windows 10 version 2004 (build 19041) or later, and Windows 11 · x64 and ARM64",url:"https://apps.microsoft.com/detail/9nk1x0qr2vz6",display:"apps.microsoft.com/detail/9nk1x0qr2vz6",badge:`${sz}/microsoft-store-badge.svg`,badgeAlt:"Get it from Microsoft",badgeWidth:176,qr:"qr-microsoftstore"}],sF=` 1461/* One stylesheet for every /flourishpdf/* page. Orchid-on-cream light theme (the app's palette on 1462 a light web surface); dark follows the OS, and [data-theme] overrides it, per the web standard. */ 1463.fp-wrap{ 1464 --fp-orchid:#a23ce0;--fp-orchid-deep:#7d24b8;--fp-orchid-soft:#f3e6ff; 1465 --fp-accent:#7d24b8; 1466 --fp-bg:#faf7fd;--fp-bg-alt:#f3ecfa;--fp-fg:#17141c;--fp-muted:#5b5566; 1467 --fp-border:rgba(23,20,28,0.10);--fp-surface:rgba(255,255,255,0.78);--fp-solid:#ffffff; 1468 --fp-ok:#17693f;--fp-soon-fg:#7a4b00;--fp-soon-bg:#fff1cf; 1469 --fp-shadow:0 18px 44px -24px rgba(60,20,90,.35); 1470 --fp-radius:16px; 1471 font-family:-apple-system,BlinkMacSystemFont,"Inter",system-ui,sans-serif; 1472 background:var(--fp-bg);color:var(--fp-fg); 1473 -webkit-font-smoothing:antialiased; 1474} 1475@media (prefers-color-scheme:dark){ 1476 :root:not([data-theme="light"]) .fp-wrap{ 1477 --fp-orchid-soft:#2a1a3a;--fp-accent:#d3a6f5; 1478 --fp-bg:#131018;--fp-bg-alt:#1a1622;--fp-fg:#f3eff7;--fp-muted:#b9b1c4; 1479 --fp-border:rgba(255,255,255,0.13);--fp-surface:rgba(255,255,255,0.05);--fp-solid:#1d1826; 1480 --fp-ok:#6fd39c;--fp-soon-fg:#ffd98a;--fp-soon-bg:#3a2b0a; 1481 --fp-shadow:0 18px 44px -24px rgba(0,0,0,.7); 1482 } 1483} 1484:root[data-theme="dark"] .fp-wrap{ 1485 --fp-orchid-soft:#2a1a3a;--fp-accent:#d3a6f5; 1486 --fp-bg:#131018;--fp-bg-alt:#1a1622;--fp-fg:#f3eff7;--fp-muted:#b9b1c4; 1487 --fp-border:rgba(255,255,255,0.13);--fp-surface:rgba(255,255,255,0.05);--fp-solid:#1d1826; 1488 --fp-ok:#6fd39c;--fp-soon-fg:#ffd98a;--fp-soon-bg:#3a2b0a; 1489 --fp-shadow:0 18px 44px -24px rgba(0,0,0,.7); 1490} 1491.fp-wrap *,.fp-wrap *::before,.fp-wrap *::after{box-sizing:border-box} 1492.fp-wrap img{max-width:100%;height:auto} 1493.fp-wrap a:focus-visible,.fp-wrap summary:focus-visible,.fp-wrap button:focus-visible{outline:3px solid var(--fp-orchid);outline-offset:2px;border-radius:8px} 1494.fp-sr{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0} 1495.fp-skip{position:absolute;
1495left:12px;top:-60px;z-index:200;background:var(--fp-orchid-deep);color:#fff;padding:10px 16px;border-radius:0 0 10px 10px;font-weight:600;text-decoration:none} 1496.fp-skip:focus{top:0} 1497 1498/* ââ header âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 1499.fp-nav{ 1500 position:sticky;top:0;z-index:100;min-height:64px;padding:0 clamp(16px,4vw,48px); 1501 display:flex;align-items:center;justify-content:space-between;gap:16px; 1502 background:color-mix(in srgb,var(--fp-bg) 86%,transparent);backdrop-filter:blur(18px); 1503 border-bottom:1px solid var(--fp-border); 1504} 1505.fp-nav-logo{display:flex;align-items:center;gap:10px;text-decoration:none;color:var(--fp-fg);font-weight:650;flex:none} 1506.fp-nav-logo-icon{ 1507 width:30px;height:30px;border-radius:8px;flex:none; 1508 background:linear-gradient(160deg,var(--fp-orchid),var(--fp-orchid-deep)); 1509 display:flex;align-items:center;justify-content:center;color:#fff;font-size:15px;font-weight:700; 1510} 1511.fp-nav-links{display:none;align-items:center;gap:2px;margin-left:auto} 1512.fp-nav-links a,.fp-nav-links .fp-dd > a{ 1513 font-size:.86rem;color:var(--fp-muted);text-decoration:none;padding:9px 12px;border-radius:999px;display:inline-block; 1514} 1515.fp-nav-links a:hover,.fp-nav-links .fp-dd:hover > a,.fp-nav-links .fp-dd:focus-within > a{color:var(--fp-fg);background:rgba(162,60,224,0.10)} 1516.fp-nav-links a[aria-current="page"]{color:var(--fp-fg);font-weight:620} 1517.fp-nav-cta{ 1518 background:var(--fp-orchid-deep);color:#fff !important;font-weight:620;margin-left:6px; 1519} 1520.fp-nav-cta:hover{background:var(--fp-orchid) !important;color:#fff !important} 1521.fp-dd{position:relative} 1522.fp-dd-panel{ 1523 display:none;position:absolute;left:-40px;top:100%;width:min(560px,90vw);padding:14px;margin-top:6px; 1524 background:var(--fp-solid);border:1px solid var(--fp-border);border-radius:var(--fp-radius);box-shadow:var(--fp-shadow); 1525 grid-template-columns:repeat(2,1fr);gap:4px; 1526} 1527.fp-dd:hover .fp-dd-panel,.fp-dd:focus-within .fp-dd-panel{display:grid} 1528.fp-dd-panel a{display:block;padding:10px 12px;border-radius:10px;font-size:.86rem;color:var(--fp-fg);line-height:1.35} 1529.fp-dd-panel a small{display:block;color:var(--fp-muted);font-size:.76rem;margin-top:2px} 1530.fp-menu{position:relative;margin-left:auto} 1531.fp-menu summary{list-style:none;cursor:pointer;padding:9px 16px;border:1px solid var(--fp-border);border-radius:999px;font-size:.86rem;font-weight:600;background:var(--fp-surface)} 1532.fp-menu summary::-webkit-details-marker{display:none} 1533.fp-menu-panel{ 1534 position:absolute;right:0;top:calc(100% + 8px);width:min(320px,calc(100vw - 32px));padding:10px; 1535 background:var(--fp-solid);border:1px solid var(--fp-border);border-radius:var(--fp-radius);box-shadow:var(--fp-shadow); 1536 max-height:calc(100vh - 96px);overflow:auto; 1537} 1538.fp-menu-panel a{display:block;padding:11px 12px;border-radius:10px;color:var(--fp-fg);text-decoration:none;font-size:.92rem} 1539.fp-menu-panel a:hover{background:rgba(162,60,224,0.10)} 1540.fp-menu-panel .fp-menu-h{font-size:.68rem;letter-spacing:.16em;text-transform:uppercase;color:var(--fp-muted);padding:10px 12px 4px;font-weight:680} 1541@media(min-width:900px){.fp-nav-links{display:flex}.fp-menu{display:none}} 1542 1543/* ââ hero âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 1544.fp-hero{padding:clamp(44px,7vw,88px) clamp(16px,4vw,48px) clamp(28px,5vw,56px)} 1545.fp-hero-grid{max-width:1180px;margin:0 auto;display:grid;gap:40px;align-items:center} 1546@media(min-width:960px){.fp-hero-grid{grid-template-columns:1.05fr .95fr}} 1547.fp-hero-copy{text-align:left} 1548.fp-hero-inner{max-width:880px;margin:0 auto;text-align:center} 1549.fp-badge{ 1550 display:inline-block;padding:6px 14px;border-radius:999px;font-size:.78rem;font-weight:620; 1551 background:var(--fp-orchid-soft);color:var(--fp-accent);margin-bottom:18px; 1552} 1553.fp-h1{font-size:clamp(2.1rem,5.2vw,3.6rem);font-weight:760;letter-spacing:-.03em;line-height:1.06;margin:0 0 18px} 1554.fp-h1 em{font-style:normal;color:var(--fp-accent)} 1555.fp-sub{font-size:clamp(1rem,2vw,1.16rem);color:var(--fp-muted);line-height:1.7;max-width:60ch;margin:0 0 28px} 1556.fp-hero-inner .fp-sub{margin-left:auto;margin-right:auto} 1557.fp-cta-row{display:flex;flex-wrap:wrap;gap:12px} 1558.fp-hero-inner .fp-cta-row{justify-content:center} 1559.fp-btn{ 1560 display:inline-flex;align-items:center;justify-content:center;gap:8px;
1560padding:13px 26px;border-radius:999px; 1561 font-size:.92rem;font-weight:640;text-decoration:none;transition:transform .18s,box-shadow .18s;border:1.5px solid transparent; 1562 min-height:46px;cursor:pointer;font-family:inherit; 1563} 1564.fp-btn:hover{transform:translateY(-1px)} 1565.fp-btn-primary{background:var(--fp-orchid-deep);color:#fff;box-shadow:0 10px 26px -12px rgba(125,36,184,.6)} 1566.fp-btn-outline{background:var(--fp-surface);color:var(--fp-fg);border-color:var(--fp-border)} 1567.fp-btn-block{width:100%} 1568.fp-platforms{font-size:.85rem;color:var(--fp-muted);margin-top:20px} 1569.fp-crumbs{max-width:1180px;margin:0 auto;padding:18px clamp(16px,4vw,48px) 0;font-size:.82rem;color:var(--fp-muted)} 1570.fp-crumbs ol{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:6px} 1571.fp-crumbs li+li::before{content:"/";margin-right:6px;opacity:.6} 1572.fp-crumbs a{color:var(--fp-muted);text-decoration:underline;text-underline-offset:3px} 1573 1574/* Product frames â real captures, never drawn. */ 1575.fp-shots{position:relative;display:flex;justify-content:center;align-items:flex-end;gap:0} 1576.fp-device{background:#0d0b11;border:7px solid #23202b;box-shadow:var(--fp-shadow);overflow:hidden} 1577.fp-device img{display:block;width:100%;height:auto} 1578.fp-device-ipad{border-radius:24px;width:min(100%,520px)} 1579.fp-device-iphone{border-radius:32px;width:min(38%,200px);margin-left:-9%;margin-bottom:-4%;position:relative;z-index:2} 1580.fp-shot-cap{font-size:.78rem;color:var(--fp-muted);margin:12px 0 0;text-align:center} 1581 1582/* ââ sections âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 1583#download,#pricing,#compare,#faq{scroll-margin-top:76px} 1584.fp-section{padding:clamp(44px,6.5vw,80px) clamp(16px,4vw,48px)} 1585.fp-section-inner{max-width:1120px;margin:0 auto} 1586.fp-band{background:var(--fp-bg-alt)} 1587.fp-privacy-band{background:linear-gradient(180deg,var(--fp-orchid-soft) 0%,var(--fp-bg) 100%)} 1588.fp-label{font-size:.72rem;font-weight:680;letter-spacing:.18em;text-transform:uppercase;color:var(--fp-accent);margin:0 0 12px} 1589.fp-h2{font-size:clamp(1.6rem,3.3vw,2.35rem);font-weight:730;letter-spacing:-.02em;line-height:1.15;margin:0 0 14px} 1590.fp-lead{font-size:1.02rem;color:var(--fp-muted);line-height:1.7;max-width:64ch;margin:0 0 34px} 1591.fp-center{text-align:center}.fp-center .fp-lead{margin-left:auto;margin-right:auto} 1592.fp-grid{display:grid;gap:18px} 1593@media(min-width:640px){.fp-grid{grid-template-columns:repeat(2,1fr)}} 1594@media(min-width:960px){.fp-grid{grid-template-columns:repeat(3,1fr)}.fp-grid-4{grid-template-columns:repeat(4,1fr)}} 1595.fp-card{background:var(--fp-surface);border:1px solid var(--fp-border);border-radius:var(--fp-radius);padding:22px} 1596.fp-card h3{font-size:1.02rem;font-weight:660;margin:0 0 8px} 1597.fp-card p{font-size:.92rem;color:var(--fp-muted);line-height:1.6;margin:0} 1598.fp-card-link{text-decoration:none;color:inherit;display:block;transition:transform .18s,border-color .18s,box-shadow .18s} 1599.fp-card-link:hover{transform:translateY(-2px);border-color:var(--fp-orchid);box-shadow:var(--fp-shadow)} 1600.fp-card-icon{font-size:1.35rem;margin-bottom:10px} 1601.fp-card .fp-more{display:inline-block;margin-top:12px;font-size:.84rem;font-weight:620;color:var(--fp-accent)} 1602.fp-list{margin:14px 0 0;padding:0;list-style:none} 1603.fp-list li{position:relative;padding-left:22px;font-size:.9rem;color:var(--fp-muted);line-height:1.55;margin-bottom:8px} 1604.fp-list li::before{content:"";position:absolute;left:2px;top:.5em;width:8px;height:8px;border-radius:50%;background:var(--fp-orchid)} 1605 1606/* ââ pricing ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 1607.fp-billing{display:flex;flex-wrap:wrap;justify-content:center;gap:6px;margin:0 auto 28px;padding:5px;width:max-content;max-width:100%;background:var(--fp-surface);border:1px solid var(--fp-border);border-radius:999px} 1608.fp-billing label{cursor:pointer;padding:9px 20px;border-radius:999px;font-size:.88rem;font-weight:620;color:var(--fp-muted)} 1609.fp-billing label small{font-weight:660;color:var(--fp-ok);margin-left:6px} 1610#fp-bill-y:checked ~ .fp-billing label[for="fp-bill-y"],#fp-bill-m:checked ~ .fp-billing label[for="fp-bill-m"]{background:var(--fp-orchid-deep);color:#fff} 1611#fp-bill-y:checked ~ .fp-billing label[for="fp-bill-y"] small{color:#fff} 1612#fp-bill-y:focus-visible ~ .fp-billing label[for="fp-bill-y"],#fp-bill-m:focus-visible ~ .fp-billing label[for="fp-bill-m"]{outline:3px solid var(--fp-orchid);outline-offset:2px} 1613.fp-plans{display:grid;gap:18px}
1614@media(min-width:900px){.fp-plans{grid-template-columns:repeat(3,1fr);align-items:stretch}} 1615.fp-plan{position:relative;display:flex;flex-direction:column;background:var(--fp-solid);border:1px solid var(--fp-border);border-radius:var(--fp-radius);padding:26px} 1616.fp-plan.featured{border:2px solid var(--fp-orchid);box-shadow:var(--fp-shadow)} 1617.fp-plan-flag{position:absolute;top:-13px;left:24px;background:var(--fp-orchid-deep);color:#fff;font-size:.72rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:5px 12px;border-radius:999px} 1618.fp-plan h3{margin:0 0 4px;font-size:1.15rem;font-weight:700} 1619.fp-plan .fp-tag{font-size:.88rem;color:var(--fp-muted);margin:0 0 14px;line-height:1.5;min-height:2.6em} 1620.fp-price{font-size:2.1rem;font-weight:760;letter-spacing:-.025em;margin:4px 0 2px;line-height:1.1} 1621.fp-price small{font-size:.86rem;font-weight:520;color:var(--fp-muted);letter-spacing:0} 1622.fp-price-sub{font-size:.82rem;color:var(--fp-muted);min-height:1.3em;margin:0 0 18px} 1623#fp-bill-m:checked ~ .fp-plans .fp-p-year,#fp-bill-y:checked ~ .fp-plans .fp-p-month{display:none} 1624.fp-plan .fp-list{margin:20px 0 22px;flex:1} 1625.fp-plan .fp-note{font-size:.78rem;color:var(--fp-muted);margin:10px 0 0;text-align:center;line-height:1.45} 1626.fp-fine{font-size:.82rem;color:var(--fp-muted);text-align:center;margin:22px auto 0;max-width:70ch;line-height:1.6} 1627 1628/* ââ feature table ââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 1629.fp-table{width:100%;border-collapse:separate;border-spacing:0;font-size:.9rem;background:var(--fp-solid);border:1px solid var(--fp-border);border-radius:var(--fp-radius)} 1630.fp-table caption{text-align:left;padding:0 0 12px;font-size:.84rem;color:var(--fp-muted)} 1631.fp-table thead th{position:sticky;top:64px;z-index:5;background:var(--fp-solid);padding:14px 8px;font-size:.84rem;font-weight:700;text-align:center;border-bottom:1px solid var(--fp-border)} 1632.fp-table thead th:first-child{text-align:left;padding-left:18px;border-top-left-radius:var(--fp-radius)} 1633.fp-table thead th:last-child{border-top-right-radius:var(--fp-radius)} 1634.fp-table thead th small{display:block;font-weight:520;color:var(--fp-muted);font-size:.72rem} 1635.fp-table tbody th[scope="rowgroup"]{background:var(--fp-bg-alt);text-align:left;padding:12px 18px;font-size:.76rem;letter-spacing:.14em;text-transform:uppercase;color:var(--fp-accent);font-weight:700} 1636.fp-table tbody th[scope="row"]{text-align:left;font-weight:560;padding:13px 8px 13px 18px;border-top:1px solid var(--fp-border);vertical-align:top;line-height:1.4} 1637.fp-table td{text-align:center;padding:13px 6px;border-top:1px solid var(--fp-border);vertical-align:top;font-weight:700} 1638.fp-table .fp-yes{color:var(--fp-ok)} 1639.fp-table .fp-no{color:var(--fp-muted);font-weight:400} 1640.fp-table .fp-soon{display:inline-block;font-size:.66rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--fp-soon-fg);background:var(--fp-soon-bg);padding:3px 7px;border-radius:999px} 1641.fp-row-note{display:block;margin-top:4px;font-size:.74rem;font-weight:400;color:var(--fp-muted);line-height:1.4} 1642.fp-avail{display:flex;flex-wrap:wrap;gap:4px;list-style:none;margin:6px 0 0;padding:0} 1643.fp-avail li{font-size:.68rem;font-weight:600;padding:2px 8px;border-radius:999px;border:1px solid var(--fp-border);color:var(--fp-muted);line-height:1.5;white-space:nowrap} 1644.fp-avail .a-yes{background:var(--fp-orchid-soft);color:var(--fp-accent);border-color:transparent} 1645.fp-avail .a-partial{border-style:solid;border-color:var(--fp-orchid);color:var(--fp-accent)} 1646.fp-avail .a-soon{border-style:dashed} 1647@media(max-width:560px){ 1648 .fp-table{font-size:.82rem}.fp-table thead th{padding:10px 2px;font-size:.74rem} 1649 .fp-table thead th:first-child,.fp-table tbody th[scope="row"],.fp-table tbody th[scope="rowgroup"]{padding-left:12px} 1650 .fp-table td{padding:12px 2px}.fp-table .fp-soon{font-size:.58rem;padding:2px 5px} 1651} 1652 1653/* ââ feature cards on area / platform pages âââââââââââââââââââââââââââââââ */ 1654.fp-feat{background:var(--fp-surface);border:1px solid var(--fp-border);border-radius:var(--fp-radius);padding:20px} 1655.fp-feat h3{font-size:1rem;font-weight:660;margin:0 0 6px;display:flex;flex-wrap:wrap;align-items:center;gap:8px} 1656.fp-feat p{font-size:.9rem;color:var(--fp-muted);line-height:1.6;margin:0} 1657.fp-pill{font-size:.66rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:3px 8px;border-radius:999px;background:var(--fp-orchid-soft);color:var(--fp-accent)} 1658.fp-pill-soon{background:var(--fp-soon-bg);color:var(--fp-soon-fg)} 1659 1660/* ââ FAQ ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 1661.fp-faq{max-width:820px;margin:0 auto;display:grid;gap:10px} 1662.fp-faq details{background:var(--fp-solid);border:1px solid var(--fp-border);border-radius:14px;padding:0} 1663.fp-faq summary{cursor:pointer;list-style:none;padding:18px 20px;font-weight:640;font-size:1rem;display:flex;justify-content:space-between;gap:14px;align-items:center} 1664.fp-faq summary::-webkit-details-marker{display:none} 1665.fp-faq summary::after{content:"+";font-size:1.4rem;line-height:1;color:var(--fp-accent);flex:none} 1666.fp-faq details[open] summary::after{content:"â"} 1667.fp-faq details p{margin:0;padding:0 20px 20px;color:var(--fp-muted);line-height:1.7;font-size:.95rem} 1668 1669/* ââ downloads ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 1670.fp-dl-grid{display:grid;gap:18px} 1671@media(min-width:820px){.fp-dl-grid{grid-template-columns:repeat(2,1fr)}} 1672.fp-dl-card{background:var(--fp-surface);border:1px solid var(--fp-border);border-radius:var(--fp-radius);padding:24px;display:flex;flex-direction:column;gap:18px} 1673.fp-dl-head{display:flex;align-items:center;gap:12px} 1674.fp-dl-icon{width:44px;height:44px;border-radius:10px;flex:none} 1675.fp-dl-head h3{margin:0;font-size:1.05rem;font-weight:680} 1676.fp-dl-head p{margin:2px 0 0;font-size:.83rem;color:var(--fp-muted)}
1677.fp-dl-body{display:flex;flex-wrap:wrap;align-items:center;gap:18px} 1678.fp-dl-badge{display:inline-flex;text-decoration:none;transition:transform .18s} 1679.fp-dl-badge:hover{transform:translateY(-1px)} 1680.fp-dl-qr{display:flex;align-items:center;gap:12px;text-decoration:none;color:var(--fp-muted)} 1681/* White plate behind the code: a QR needs a light quiet zone or a phone will not lock on. */ 1682.fp-dl-qr img{width:112px;height:112px;flex:none;border-radius:10px;background:#fff;border:1px solid var(--fp-border)} 1683.fp-dl-qr span{font-size:.8rem;line-height:1.5;max-width:15ch} 1684.fp-dl-qr strong{display:block;font-size:.86rem;font-weight:640;color:var(--fp-fg)} 1685.fp-dl-url{font-size:.78rem;color:var(--fp-muted);word-break:break-all;margin:0} 1686.fp-dl-note{font-size:.85rem;color:var(--fp-muted);margin:26px 0 0} 1687.fp-dl-card-link{font-size:.84rem;font-weight:620;color:var(--fp-accent)} 1688 1689.fp-cta-band{background:linear-gradient(135deg,var(--fp-orchid-deep),#4d1577);color:#fff;text-align:center;border-radius:calc(var(--fp-radius) + 8px);padding:clamp(34px,6vw,60px) 24px} 1690.fp-cta-band h2{font-size:clamp(1.5rem,3vw,2.1rem);margin:0 0 10px;font-weight:740} 1691.fp-cta-band p{margin:0 auto 24px;max-width:56ch;opacity:.92;line-height:1.65} 1692.fp-cta-band .fp-btn-primary{background:#fff;color:#4d1577;box-shadow:none} 1693.fp-cta-band .fp-btn-outline{background:transparent;color:#fff;border-color:rgba(255,255,255,.55)} 1694.fp-cta-band .fp-cta-row{justify-content:center} 1695 1696/* ââ legal / long text ââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 1697.fp-legal{padding:clamp(40px,6vw,72px) clamp(16px,4vw,48px)} 1698.fp-legal-inner{max-width:760px;margin:0 auto} 1699.fp-legal h1{font-size:clamp(1.9rem,4vw,2.5rem);font-weight:730;letter-spacing:-.02em;margin:0 0 8px} 1700.fp-updated{font-size:.85rem;color:var(--fp-muted);margin:0 0 28px} 1701.fp-legal h2{font-size:1.12rem;font-weight:680;margin:30px 0 10px} 1702.fp-legal p,.fp-legal li{font-size:.95rem;line-height:1.78;color:var(--fp-muted)} 1703.fp-legal p{margin:0 0 14px} 1704.fp-legal ul{margin:0 0 16px;padding-left:1.2rem} 1705.fp-legal li{margin-bottom:7px} 1706.fp-legal a{color:var(--fp-accent);text-decoration:underline;text-underline-offset:3px;font-weight:560} 1707.fp-legal-box{background:var(--fp-orchid-soft);border:1px solid rgba(162,60,224,.24);border-radius:var(--fp-radius);padding:18px 20px;margin:0 0 26px} 1708.fp-legal-box p{margin:0;color:var(--fp-fg)} 1709 1710.fp-footer{border-top:1px solid var(--fp-border);padding:34px clamp(16px,4vw,48px) 26px;background:var(--fp-surface)} 1711.fp-footer-inner{max-width:1120px;margin:0 auto;display:grid;gap:28px;font-size:.86rem;color:var(--fp-muted)} 1712@media(min-width:760px){.fp-footer-inner{grid-template-columns:1.3fr repeat(3,1fr)}} 1713.fp-footer h4{margin:0 0 10px;font-size:.72rem;letter-spacing:.14em;text-transform:uppercase;color:var(--fp-fg);font-weight:700} 1714.fp-footer ul{list-style:none;margin:0;padding:0;display:grid;gap:8px} 1715.fp-footer a{color:var(--fp-muted);text-decoration:none} 1716.fp-footer a:hover{color:var(--fp-accent);text-decoration:underline} 1717.fp-footer-base{max-width:1120px;margin:26px auto 0;padding-top:18px;border-top:1px solid var(--fp-border);font-size:.8rem;color:var(--fp-muted)} 1718@media(prefers-reduced-motion:reduce){.fp-wrap *{transition:none !important}} 1719`;function sC(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("a",{className:"fp-skip",href:"#fp-main",children:"Skip to content"}),(0,a.jsxs)("header",{className:"fp-nav",children:[(0,a.jsxs)("a",{href:"/flourishpdf",className:"fp-nav-logo","aria-label":"Flourish PDF home",children:[(0,a.jsx)("span",{className:"fp-nav-logo-icon","aria-hidden":"true",children:"F"}),(0,a.jsx)("span",{children:"Flourish PDF"})]}),(0,a.jsxs)("nav",{className:"fp-nav-links","aria-label":"Flourish PDF",children:[(0,a.jsxs)("div",{className:"fp-dd",children:[(0,a.jsx)("a",{href:"/flourishpdf/features",children:"Features"}),(0,a.jsx)("div",{className:"fp-dd-panel",children:sS.FP_AREAS.map(e=>(0,a.jsxs)("a",{href:`/flourishpdf/${e.slug}`,children:[e.name,(0,a.jsx)("small",{children:e.blurb})]},e.id))})]}),(0,a.jsxs)("div",{className:"fp-dd",children:[(0,a.jsx)("a",{href:"/flourishpdf/download",children:"Platforms"}),(0,a.jsx)("div",{className:"fp-dd-panel",children:sS.FP_PLATFORMS.map(e=>(0,a.jsxs)("a",{href:`/flourishpdf/${e.slug}`,children:[e.name,(0,a.jsx)("small",{children:e.requirement})]},e.id))})]}),(0,a.jsx)("a",{href:"/flourishpdf/pricing",children:"Pricing"}),(0,a.jsx)("a",{href:"/flourishpdf/support",children:"Support"}),(0,a.jsx)("a",{href:"/flourishpdf/account",children:"Account"}),(0,a.jsx)("a",{href:"/flourishpdf/download",className:"fp-nav-cta",children:"Get Flourish PDF"})]}),(0,a.jsxs)("details",{className:"fp-menu",children:[(0,a.jsx)("summary",{children:"Menu"}),(0,a.jsxs)("div",{className:"fp-menu-panel",children:[(0,a.jsx)("p",{className:"fp-menu-h",children:"Product"}),(0,a.jsx)("a",{href:"/flourishpdf/features",children:"All features"}),(0,a.jsx)("a",{href:"/flourishpdf/pricing",children:"Pricing"}),(0,a.jsx)("a",{href:"/flourishpdf/download",children:"Download"}),(0,a.jsx)("p",{className:"fp-menu-h",children:"Features"}),sS.FP_AREAS.map(e=>(0,a.jsx)("a",{href:`/flourishpdf/${e.slug}`,children:e.name},e.id)),(0,a.jsx)("p",{className:"fp-menu-h",children:"Platforms"}),sS.FP_PLATFORMS.map(e=>(0,a.jsx)("a",{href:`/flourishpdf/${e.slug}`,children:e.name},e.id)),(0,a.jsx)("p",{className:"fp-menu-h",children:"Help"}),(0,a.jsx)("a",{href:"/flourishpdf/support",children:"Support"}),(0,a.jsx)("a",{href:"/flourishpdf/manual",children:"User manual"}),(0,a.jsx)("a",{href:"/flourishpdf/account",children:"Account"})]})]})]})]})}function sM(){return(0,a.jsxs)("footer",{className:"fp-footer",children:[(0,a.jsxs)("div",{className:"fp-footer-inner",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("h4",{children:"Flourish PDF"}),(0,a.jsx)("p",{style:{margin:0,lineHeight:1.65},children:"The PDF editor that keeps your documents on your device. Mac, Windows, iPhone, iPad and Android."})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h4",{children:"Features"}),(0,a.jsx)("ul",{children:sS.FP_AREAS.map(e=>(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:`/flourishpdf/${e.slug}`,children:e.name})},e.id))})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h4",{children:"Get it"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"/flourishpdf/pricing",children:"Pricing"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"/flourishpdf/download",children:"Download"})}),sS.FP_PLATFORMS.map(e=>(0,a.jsx)("li",{children:(0,a.jsxs)("a",{href:`/flourishpdf/${e.slug}`,children:["PDF editor for ",e.name]})},e.id))]})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h4",{children:"Help"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"/flourishpdf/support",children:"Support"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"/flourishpdf/faq",children:"FAQ"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"/flourishpdf/manual",children:"User manual"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"/flourishpdf/account",children:"Account"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"/flourishpdf/privacy",children:"Privacy Policy"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"/flourishpdf/terms",children:"Terms of Use"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:sA,children:"Contact"})})]})]})]}),(0,a.jsxs)("div",{className:"fp-footer-base",children:["© ",new Date().getFullYear()," Soft Flourish · ",(0,a.jsx)("a",{href:"/",children:"softflourish.com"})]})]})}function sI(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:sF}}),(0,a.jsxs)("div",{className:"fp-wrap",children:[(0,a.jsx)(sC,{}),(0,a.jsx)("main",{id:"fp-main",className:"fp-legal",children:(0,a.jsxs)("div",{className:"fp-legal-inner",children:[(0,a.jsx)("h1",{children:"Privacy Policy"}),(0,a.jsx)("p",{className:"fp-updated",children:"Last updated: September 2026 · Applies to: Flourish PDF for macOS, Windows, iPhone, iPad, and Android"}),(0,a.jsx)("div",{className:"fp-legal-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"The short version:"}),"your documents never leave your device. Flourish PDF has no document-upload endpoint. Editing, scanning, OCR, and AI all run locally on your device. The only data we hold is what's needed to run an account and a subscription â your email address and your plan status. The free tier of the iPhone, iPad, and Android apps shows ads, which means Google receives the usual advertising signals described below; the Mac and Windows apps show no ads at all."]})}),(0,a.jsx)("h2",{children:"Who we are"}),(0,a.jsxs)("p",{children:["Flourish PDF is developed and published by Soft Flourish, an independent software studio and publishing house based in California, USA. Questions: ",(0,a.jsx)("a",{href:sA,children:"contact us"}),"."]}),(0,a.jsx)("h2",{children:"Your documents"}),(0,a.jsx)("p",{children:"We never receive, store, transmit, or process the contents of your PDFs. All document work â opening, editing, page operations, annotation, redaction, form filling, signing, and export â happens entirely on your device. There is no server-side document pipeline in Flourish PDF."}),(0,a.jsx)("h2",{children:"Scanning and OCR"}),(0,a.jsx)("p",{children:"Camera scanning and text recognition (OCR) run on-device using your operating system's built-in capabilities â Apple's VisionKit and Vision frameworks on Mac, iPhone, and iPad, and Google ML Kit's on-device models on Android. Page images and recognized text are never uploaded."}),(0,a.jsx)("h2",{children:"AI features"}),(0,a.jsx)("p",{children:"Summarize, ask-about-your-document, and translate run on your device's own on-device AI â Apple's Foundation Models on Mac, iPhone, and iPad, and Google ML Kit / Gemini Nano on Android. Your document text is never sent to us, to a cloud AI provider, or to any third party. If your device doesn't support on-device AI, the feature is simply unavailable; we never fall back to a cloud service."}),(0,a.jsx)("h2",{children:"What we collect"}),(0,a.jsx)("p",{children:"Only if you choose to create an account or subscribe:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Email address"})," â to identify your account and let you sign in on other devices."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Name"})," â only if you sign in with Apple or Google and choose to share it."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"A user identifier"})," â an anonymous ID that links your account to your subscription."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Subscription status"}),"â which plan you're on and whether it is active, so the app can unlock the right features."]})]}),(0,a.jsx)("p",{children:"You can use Flourish PDF without an account. Signing in is only required to use a paid subscription across devices."}),(0,a.jsx)("h2",{children:"What we never collect"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Your PDFs, or anything inside them"}),(0,a.jsx)("li",{children:"Page images, scans, or OCR output"}),(0,a.jsx)("li",{children:"AI prompts, questions, summaries, or translations"}),(0,a.jsx)("li",{children:"Your file names, folders, or document history"}),(0,a.jsx)("li",{children:"Cross-app tracking data â nothing we send is combined with your activity in other companies' apps or websites"})]}),(0,a.jsx)("h2",{children:"Payments"}),(0,a.jsx)("p",{children:"Subscriptions are processed by the platform you bought them on â Apple (App Store), Google (Google Play), or our web checkout for Windows. We never see or store your card details. We use RevenueCat as our subscription-management provider to tell the app whether your plan is active."}),(0,a.jsx)("h2",{children:"Service providers"}),(0,a.jsx)("p",{children:"We use a small number of processors, strictly to run accounts and subscriptions:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Google Firebase Authentication"})," â account sign-in and identity."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"RevenueCat"})," â subscription and entitlement status."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Google Cloud"}),"â hosts the small service that tells your app which plan you're on."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Google AdMob"})," â serves the ads on the free tier of the iPhone, iPad, and Android apps (see Advertising below)."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Google Firebase Analytics"})," â basic usage and performance signals in the iPhone, iPad, Mac, and Android apps (see Analytics below)."]})]}),(0,a.jsx)("p",{children:"None of these receive your documents or their contents."}),(0,a.jsx)("h2",{children:"Advertising â free tier, mobile only"}),(0,a.jsxs)("p",{children:["The iPhone, iPad, and Android apps are supported by ads on the free tier, served by"," ",(0,a.jsx)("strong",{children:"Google AdMob"}),". Any paid plan removes them. The Mac and Windows apps contain no advertising code whatsoever."]}),(0,a.jsx)("p",{children:"When an ad is requested, Google collects and receives:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Approximate location"})," â a coarse region derived from your IP address. The app holds no location permission and never reads your GPS."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"App interactions"})," â which ads were shown, viewed, or tapped."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Diagnostics"})," â app performance signals such as launch time and energy use."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Device or other identifiers"})," â on Android, the advertising ID and app set ID. iPhone and iPad supply no advertising identifier at all, because Flourish PDF never asks permission to track."]})]}),(0,a.jsxs)("p",{children:["These go to Google as an advertising partner and are used for serving ads, measurement, and fraud prevention. Google's handling is governed by its own"," ",(0,a.jsx)("a",{href:"https://policies.google.com/technologies/partner-sites",children:"privacy policy"}),"."]}),(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"Flourish PDF does not track you."}),"Every ad request the app makes is marked non-personalized, on every platform, and nothing it sends is combined with your activity in other companies' apps or websites. On iPhone and iPad there is no App Tracking Transparency prompt because the app never requests permission to track and never reads your device's advertising identifier. On Android, you can reset or delete your advertising ID under ",(0,a.jsx)("strong",{children:"Settings â Privacy â Ads"}),". Your documents are never part of an ad request."]}),(0,a.jsx)("h2",{children:"Analytics"}),(0,a.jsx)("p",{children:"The iPhone, iPad, Mac, and Android apps include Google Firebase Analytics, which automatically records app opens, screen views, session length, and basic device and performance information. We use it to see which features are used and where the app is slow. We do not record custom events describing your documents, their contents, or your file names. We do not sell personal information."}),(0,a.jsx)("h2",{children:"Data retention and deletion"}),(0,a.jsxs)("p",{children:["We keep your account record for as long as your account exists. You can ask us to delete your account and associated data at any time via"," ",(0,a.jsx)("a",{href:sA,children:"our contact form"}),", and we will remove it. Deleting the app alone does not delete your account."]}),(0,a.jsx)("h2",{children:"Your rights"}),(0,a.jsxs)("p",{children:["Depending on where you live (including under the GDPR and the CCPA/CPRA), you may have the right to access, correct, export, or delete the personal information we hold, and to withdraw consent. Because we hold so little, these requests are usually simple â just ",(0,a.jsx)("a",{href:sA,children:"contact us"}),"."]}),(0,a.jsx)("h2",{children:"Children"}),(0,a.jsx)("p",{children:"Flourish PDF is a general-purpose productivity tool and is not directed to children under 13, and we do not knowingly collect perso
1719nal information from them."}),(0,a.jsx)("h2",{children:"International transfers"}),(0,a.jsx)("p",{children:"Our account and subscription infrastructure is operated in the United States. Because your documents never leave your device, no document data is transferred anywhere."}),(0,a.jsx)("h2",{children:"Changes to this policy"}),(0,a.jsx)("p",{children:"If we make a material change, we will update this page and revise the âlast updatedâ date above. Continued use after a change means you accept the updated policy."}),(0,a.jsx)("h2",{children:"Contact"}),(0,a.jsxs)("p",{children:["Questions about this policy or your data: ",(0,a.jsx)("a",{href:sA,children:"contact us"}),". Publisher: Soft Flourish, California, USA."]})]})}),(0,a.jsx)(sM,{})]})]})}function sD(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:sF}}),(0,a.jsxs)("div",{className:"fp-wrap",children:[(0,a.jsx)(sC,{}),(0,a.jsx)("main",{id:"fp-main",className:"fp-legal",children:(0,a.jsxs)("div",{className:"fp-legal-inner",children:[(0,a.jsx)("h1",{children:"Delete Your Flourish PDF Account & Data"}),(0,a.jsx)("p",{className:"fp-updated",children:"Last updated: July 2026"}),(0,a.jsxs)("p",{children:["This page explains how to delete your account and associated data in"," ",(0,a.jsx)("strong",{children:"Flourish PDF"}),", published by ",(0,a.jsx)("strong",{children:"Soft Flourish"}),". It applies to the macOS, Windows, iPhone, iPad, and Android apps."]}),(0,a.jsxs)("div",{className:"fp-legal-box",children:[(0,a.jsx)("p",{children:(0,a.jsx)("strong",{children:"Delete your account from inside the app â no request needed."})}),(0,a.jsxs)("ol",{children:[(0,a.jsxs)("li",{children:["Open ",(0,a.jsx)("strong",{children:"Flourish PDF"})," and sign in if you are not already."]}),(0,a.jsxs)("li",{children:["Go to ",(0,a.jsx)("strong",{children:"Account"})," (macOS: the app menu â Accountâ¦; Windows/iOS/Android: the Account tab)."]}),(0,a.jsxs)("li",{children:["Choose ",(0,a.jsx)("strong",{children:"Delete Account"})," and confirm on the warning prompt."]})]}),(0,a.jsx)("p",{children:"Deletion is immediate and cannot be undone."})]}),(0,a.jsx)("h2",{children:"If you cannot sign in"}),(0,a.jsxs)("p",{children:["Email"," ",(0,a.jsx)("a",{href:`mailto:${sP}?subject=Delete%20Flourish%20PDF%20account`,children:sP})," ","from the address on the account, with the subject âDelete Flourish PDF accountâ. We verify ownership of the address and complete the deletion within 30 days, and confirm by email when it is done."]}),(0,a.jsx)("h2",{children:"What is deleted"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Your Flourish PDF sign-in account itself (email/password, Google, or Apple sign-in)."}),(0,a.jsx)("li",{children:"Your email address and account identifier."}),(0,a.jsx)("li",{children:"Your entitlement record and stored subscription record."}),(0,a.jsx)("li",{children:"Your personal LaTeX source key â the AES-GCM key that decrypts any PDF whose LaTeX source you locked. Once it is destroyed with the account, those documents' embedded source becomes permanently unreadable; the visible PDF pages themselves are untouched, because they never left your device."})]}),(0,a.jsx)("p",{children:"These are erased immediately â there is no soft-delete or recovery window, and they cannot be restored afterwards."}),(0,a.jsx)("h2",{children:"What is not deleted"}),(0,a.jsxs)("p",{children:["Account deletion does not cancel your subscription. Cancel it separately â App Store Subscriptions, Play Store Subscriptions, or"," ",(0,a.jsx)("a",{href:"/flourishpdf/account",children:"your account page"})," for Windows â or you will keep being billed by the store that took the payment."]}),(0,a.jsx)("h2",{children:"What is kept, and for how long"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Purchase and subscription records"})," â retained by Apple and Google under their own policies, and by us in de-identified form for up to"," ",(0,a.jsx)("strong",{children:"7 years"})," where tax, accounting, and fraud-prevention law requires it. Not linked back to a deleted account for any other purpose."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Encrypted operational backups"})," â deleted records may persist for up to"," ",(0,a.jsx)("strong",{children:"30 days"})," before those backups roll off, after which they are gone."]})]}),(0,a.jsxs)("p",{children:["Your documents are never included in any of this: Flourish PDF is 100% on-device, so your files, page images, and OCR/AI output never reach our servers in the first place. See the ",(0,a.jsx)("a",{href:"/flourishpdf/privacy",children:"Privacy Policy"}),"."]}),(0,a.jsx)("h2",{children:"Legal"}),(0,a.jsxs)("p",{children:[(0,a.jsx)("a",{href:"/flourishpdf/privacy",children:"Privacy Policy"})," ·"," ",(0,a.jsx)("a",{href:"/flourishpdf/terms",children:"Terms of Use"})," ·"," ",(0,a.jsx)("a",{href:"/flourishpdf/support",children:"Support"})]})]})}),(0,a.jsx)(sM,{})]})]})}function sW(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:sF}}),(0,a.jsxs)("div",{className:"fp-wrap",children:[(0,a.jsx)(sC,{}),(0,a.jsx)("main",{id:"fp-main",className:"fp-legal",children:(0,a.jsxs)("div",{className:"fp-legal-inner",children:[(0,a.jsx)("h1",{children:"Flourish PDF â Support"}),(0,a.jsx)("p",{className:"fp-updated",children:"Last updated: July 2026 · Covers Flourish PDF for macOS, Windows, iPhone, iPad, and Android"}),(0,a.jsx)("div",{className:"fp-legal-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"Ask us anything:"})," ",(0,a.jsx)("a",{href:`mailto:${sP}?subject=Flourish%20PDF%20support`,children:sP})," ","â or use the ",(0,a.jsx)("a",{href:sA,children:"contact form"}),".",(0,a.jsx)("br",{}
1719),"We answer within one business day (MonâFri, U.S. Pacific Time)."]})}),(0,a.jsx)("h2",{children:"What to include"}),(0,a.jsx)("p",{children:"None of this is required, but it usually saves a round trip:"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Your platform and OS version â macOS, Windows, iPhone/iPad, or Android"}),(0,a.jsx)("li",{children:"The app version (Flourish PDF â Settings â About)"}),(0,a.jsx)("li",{children:"Whether you signed in with Apple, Google, or email"}),(0,a.jsx)("li",{children:"For a document problem, what you did and what happened instead"})]}),(0,a.jsx)("p",{children:"Please don't attach a confidential document. Flourish PDF works entirely on your device and we never receive your files â we'd rather keep it that way. A screenshot with sensitive parts covered is usually enough."}),(0,a.jsx)("h2",{children:"Common questions"}),(0,a.jsx)("h2",{id:"restore-purchases",children:"I paid, but the app still shows the free tier"}),(0,a.jsxs)("p",{children:["Open the paywall and choose ",(0,a.jsx)("strong",{children:"Restore Purchases"}),". Make sure you are signed in to the same Apple Account, Google account, or Flourish PDF account you bought with. A subscription bought on any platform unlocks all of them once you sign in with the same Flourish PDF account."]}),(0,a.jsx)("h2",{id:"cancel",children:"How do I cancel my subscription?"}),(0,a.jsx)("p",{children:"Cancelling is handled by whoever took the payment:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Mac, iPhone, iPad:"})," System Settings / Settings â Apple Account â Subscriptions."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Android:"})," Play Store â Subscriptions."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Windows:"})," ",(0,a.jsx)("a",{href:"/flourishpdf/account",children:"your account page"}),"."]})]}),(0,a.jsx)("p",{children:"You keep paid features until the end of the period you already paid for. Refunds are issued by the store that processed the payment, under its own policy."}),(0,a.jsx)("h2",{id:"delete-account",children:"Delete my account and data"}),(0,a.jsxs)("p",{children:["See ",(0,a.jsx)("a",{href:"/flourishpdf/deleteaccount",children:"Delete Your Account & Data"})," for the in-app steps, the email fallback, and exactly what is deleted versus retained."]}),(0,a.jsx)("h2",{id:"privacy",children:"Where do my documents go?"}),(0,a.jsxs)("p",{children:["Nowhere. Editing, OCR, scanning, conversion, and the AI features all run on your own device â there is no upload endpoint to send them to. The only thing our servers hold is your account and which plan it is on. See the"," ",(0,a.jsx)("a",{href:"/flourishpdf/privacy",children:"Privacy Policy"}),"."]}),(0,a.jsx)("h2",{id:"ocr-latex",children:"OCR or LaTeX says I've hit my limit"}),(0,a.jsxs)("p",{children:["Free and Plus include a monthly allowance of on-device OCR pages and LaTeX builds; Pro is unlimited. Allowances reset at the start of each calendar month. The"," ",(0,a.jsx)("a",{href:"/flourishpdf",children:"product page"})," lists what each plan includes."]}),(0,a.jsx)("h2",{id:"bug",children:"Reporting a bug"}),(0,a.jsx)("p",{children:"Tell us the steps that trigger it, what you expected, and what happened â plus the platform and app version. If it involves a specific PDF, a description of the document (scanned or digital, page count, has a form, is encrypted) helps more than the file itself, which we don't want."}),(0,a.jsx)("h2",{children:"Legal"}),(0,a.jsxs)("p",{children:[(0,a.jsx)("a",{href:"/flourishpdf/privacy",children:"Privacy Policy"})," ·"," ",(0,a.jsx)("a",{href:"/flourishpdf/terms",children:"Terms of Use"}),". Flourish PDF is published by Soft Flourish, California, USA."]})]})}),(0,a.jsx)(sM,{})]})]})}function sO(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:sF}}),(0,a.jsxs)("div",{className:"fp-wrap",children:[(0,a.jsx)(sC,{}),(0,a.jsx)("main",{id:"fp-main",className:"fp-legal",children:(0,a.jsxs)("div",{className:"fp-legal-inner",children:[(0,a.jsx)("h1",{children:"Flourish PDF â User Manual"}),(0,a.jsx)("p",{className:"fp-updated",children:"Last updated: August 2026 · Covers Flourish PDF for macOS, Windows, iPhone, iPad, and Android"}),(0,a.jsx)("div",{className:"fp-legal-box",children:(0,a.jsxs)("p",{children:["Every feature is visible from the first launch â nothing is hidden until you upgrade. Free, Plus, and Pro differ only in monthly OCR/LaTeX allowance and which page-editing tools are unlocked. See ",(0,a.jsx)("a",{href:"/flourishpdf",children:"pricing"})," for the split."]})}),(0,a.jsx)("h2",{children:"Opening a document"}),(0,a.jsx)("p",{children:"Open any PDF from your device, or scan a paper document with the camera â edge detection and auto-capture turn it into a clean PDF, and on-device OCR makes it fully searchable in the same step."}),(0,a.jsx)("h2",{children:"Reading and annotating"}),(0,a.jsx)("p",{children:"Highlight, draw with Apple Pencil, add notes and stamps. Drop in a saved signature and a date to sign a document in seconds â no printing, scanning, or emailing it elsewhere first."}),(0,a.jsx)("h2",{children:"Editing pages"}),(0,a.jsx)("p",{children:"Reorder, rotate, merge, split, crop, insert, and extract pages. Fill in forms, and add page numbers, headers, or Bates numbering for a document set that needs it."}),(0,a.jsx)("h2",{children:"Protecting and redacting"}),(0,a.jsx)("p",{children:"Password-protect a file and set what a viewer can do with it â print, copy, edit. Redaction removes the content underneath, not just a black box drawn over it, so a redacted page is actually gone from the file."}),(0,a.jsx)("h2",{children:"On-device AI"}),(0,a.jsx)("p",{children:"On Mac, iPhone, iPad, and supported Android ph
1719ones you can summarize a document, ask questions about what it says, or translate it â all run by your deviceâs own AI (Apple Intelligence, or Googleâs on-device models). Nothing about the document is sent anywhere to do this. Windows does not have the AI features yet."}),(0,a.jsx)("h2",{children:"Converting and exporting"}),(0,a.jsxs)("p",{children:["Export pages as images, and create a PDF from photos and scans. Word, Excel, and PowerPoint export are not available yet â the ",(0,a.jsx)("a",{href:"/flourishpdf/features",children:"features page"})," shows what runs on which platform today. As with everything else, the conversion happens on your device."]}),(0,a.jsx)("h2",{children:"One subscription, every platform"}),(0,a.jsxs)("p",{children:["Buy Plus or Pro once and it covers Mac, Windows, iPhone, iPad, and Android â sign in with the same Flourish PDF account on each and it unlocks there too. See"," ",(0,a.jsx)("a",{href:"/flourishpdf/support#restore-purchases",children:"restoring a purchase"}),"if a device still shows the free tier after youâve paid."]}),(0,a.jsx)("h2",{children:"If something isnât working"}),(0,a.jsxs)("p",{children:["See ",(0,a.jsx)("a",{href:"/flourishpdf/support",children:"support"})," for subscription management, OCR/ LaTeX allowance questions, and account deletion."]})]})}),(0,a.jsx)(sM,{})]})]})}function sE(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:sF}}),(0,a.jsxs)("div",{className:"fp-wrap",children:[(0,a.jsx)(sC,{}),(0,a.jsx)("main",{id:"fp-main",className:"fp-legal",children:(0,a.jsxs)("div",{className:"fp-legal-inner",children:[(0,a.jsx)("h1",{children:"Terms of Use"}),(0,a.jsx)("p",{className:"fp-updated",children:"Last updated: July 2026 · Applies to: Flourish PDF for macOS, Windows, iPhone, iPad, and Android"}),(0,a.jsx)("div",{className:"fp-legal-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"The short version:"})," Flourish PDF is licensed to you, not sold. Paid plans are auto-renewing subscriptions you can cancel any time. Your documents stay on your device â which also means keeping your own backups is your responsibility."]})}),(0,a.jsx)("h2",{children:"1. Agreement"}),(0,a.jsx)("p",{children:"These Terms govern your use of the Flourish PDF applications and this website, provided by Soft Flourish (âweâ, âusâ). By installing or using the app, you agree to these Terms. If you don't agree, please don't use the app."}),(0,a.jsx)("h2",{children:"2. Licence"}),(0,a.jsx)("p",{children:"We grant you a personal, non-exclusive, non-transferable licence to install and use Flourish PDF on devices you own or control, for personal or internal business use. You may not resell, redistribute, reverse-engineer, or attempt to circumvent the app's licensing or plan restrictions, except where such restriction is prohibited by law."}),(0,a.jsx)("h2",{children:"3. Plans and free tier"}),(0,a.jsxs)("p",{children:["Flourish PDF offers a free tier and paid ",(0,a.jsx)("strong",{children:"Plus"})," and ",(0,a.jsx)("strong",{children:"Pro"})," ","plans. The free tier includes reading, annotation, signing, and a monthly allowance of on-device OCR pages; documents exported on the free tier carry a small âCreated with Flourish PDFâ footer. Paid plans unlock additional tools and higher or unlimited allowances, as described on the"," ",(0,a.jsx)("a",{href:"/flourishpdf",children:"product page"}),"."]}),(0,a.jsx)("h2",{children:"4. Subscriptions, billing, and cancellation"}),(0,a.jsxs)("p",{children:["Paid plans are ",(0,a.jsx)("strong",{children:"auto-renewing subscriptions"}),". Depending on your platform, billing is handled by Apple (App Store), Google (Google Play), or our web checkout for Windows. Your subscription renews automatically at the end of each period unless you cancel at least 24 hours before the period ends. Prices are shown before purchase and may vary by region."]}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Apple:"})," manage or cancel in System Settings / Settings â Apple Account â Subscriptions."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Google Play:"})," manage or cancel in the Play Store â Subscriptions."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Windows / web:"})," manage or cancel at"," ",(0,a.jsx)("a",{href:"/flourishpdf/account",children:"your account page"}),"."]})]}),(0,a.jsx)("h2",{children:"5. Refunds"}),(0,a.jsx)("p",{children:"Refunds are handled by the store that processed your payment, under its policy â Apple or Google for in-app purchases, or our payment provider for web purchases. If a refund is granted, the corresponding paid features stop being available."}),(0,a.jsx)("h2",{children:"6. Acceptable use"}),(0,a.jsx)("p",{children:"You are responsible for the documents you process and must have the right to use them. Don't use Flourish PDF to infringe intellectual property, violate privacy or c
1719onfidentiality obligations, remove security or access controls from documents you aren't authorised to modify, or break applicable law."}),(0,a.jsx)("h2",{children:"7. Your documents and backups"}),(0,a.jsx)("p",{children:"Because Flourish PDF works locally, we never hold a copy of your files â so we cannot recover them for you. Editing is powerful and some operations (for example redaction and flattening) are intentionally irreversible. Keep your own backups of anything important, and verify results before relying on them."}),(0,a.jsx)("h2",{children:"8. Availability of features"}),(0,a.jsx)("p",{children:"Some features depend on your device and operating system. On-device AI requires a device and OS version that supports it, and LaTeX compilation is available on the desktop apps. Where a feature isn't supported, the app will tell you plainly rather than substituting a cloud service."}),(0,a.jsx)("h2",{children:"9. Intellectual property"}),(0,a.jsx)("p",{children:"Flourish PDF, its name, logo, and design are owned by us. These Terms don't transfer any of our intellectual property to you. You retain all rights to your own documents."}),(0,a.jsx)("h2",{children:"10. Disclaimer and limitation of liability"}),(0,a.jsx)("p",{children:"The app is provided âas isâ without warranties of any kind to the fullest extent permitted by law. We don't warrant that it will be uninterrupted or error-free, or that any output will be fit for a particular purpose. To the maximum extent permitted by law, our total liability arising out of or relating to the app is limited to the amount you paid us in the twelve months before the claim."}),(0,a.jsx)("h2",{children:"11. Changes"}),(0,a.jsx)("p",{children:"We may update these Terms or the app. Material changes will be reflected here with a new âlast updatedâ date. Continued use after a change means you accept the updated Terms."}),(0,a.jsx)("h2",{children:"12. Termination"}),(0,a.jsx)("p",{children:"You may stop using the app at any time. We may suspend or terminate access if these Terms are breached. Sections that by their nature should survive termination will do so."}),(0,a.jsx)("h2",{children:"13. Governing law"}),(0,a.jsx)("p",{children:"These Terms are governed by the laws of the State of California, USA, without regard to conflict-of-laws rules, and the courts located in California will have jurisdiction â except where mandatory local consumer law grants you other rights."}),(0,a.jsx)("h2",{children:"14. Contact"}),(0,a.jsxs)("p",{children:["Questions about these Terms: ",(0,a.jsx)("a",{href:sA,children:"contact us"}),". Publisher: Soft Flourish, California, USA. See also our"," ",(0,a.jsx)("a",{href:"/flourishpdf/privacy",children:"Privacy Policy"}),"."]})]})}),(0,a.jsx)(sM,{})]})]})}let sR="https://softflourish.com",s$="/flourishpdf",s_=`${sz}/shots`,sU=["mac","ios","windows","android"],sL=Object.fromEntries(sS.FP_PLATFORMS.map(e=>[e.id,e])),sq={free:"Free",plus:"Plus",pro:"Pro"},sB=e=>`$${e.toFixed(2)}`;function sG({data:e}){return(0,a.jsx)("script",{type:"application/ld+json",dangerouslySetInnerHTML:{__html:JSON.stringify(e).replace(/</g,"\\u003c")}})}function sH(){return{"@context":"https://schema.org","@type":"SoftwareApplication",name:"Flourish PDF",applicationCategory:"BusinessApplication",operatingSystem:"macOS, iOS, iPadOS, Android, Windows",url:`${sR}${s$}`,description:"A PDF editor for Mac, Windows, iPhone, iPad and Android that never uploads your document. Edit, organize, OCR, sign, redact and convert â on the device.",publisher:{"@type":"Organization",name:"Soft Flourish",url:sR},offers:sS.FP_PLANS.flatMap(e=>null==e.annual?[{"@type":"Offer",name:e.name,price:"0",priceCurrency:"USD",url:`${sR}${s$}/pricing`}]:[{"@type":"Offer",name:`${e.name} (monthly)`,price:e.monthly.toFixed(2),priceCurrency:"USD",url:`${sR}${s$}/pricing`},{"@type":"Offer",name:`${e.name} (yearly)`,price:e.annual.toFixed(2),priceCurrency:"USD",url:`${sR}${s$}/pricing`}])}}function sY(e){return{"@context":"https://schema.org","@type":"FAQPage",mainEntity:e.map(e=>
1719({"@type":"Question",name:e.q,acceptedAnswer:{"@type":"Answer",text:e.a}}))}}function sV(e){return{"@context":"https://schema.org","@type":"BreadcrumbList",itemListElement:e.map((e,a)=>({"@type":"ListItem",position:a+1,name:e.name,item:`${sR}${e.path}`}))}}function sK({children:e}){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:sF}}),(0,a.jsxs)("div",{className:"fp-wrap",children:[(0,a.jsx)(sC,{}),(0,a.jsx)("main",{id:"fp-main",children:e}),(0,a.jsx)(sM,{})]})]})}function sQ({trail:e}){return(0,a.jsx)("nav",{className:"fp-crumbs","aria-label":"Breadcrumb",children:(0,a.jsx)("ol",{children:e.map((t,s)=>(0,a.jsx)("li",{children:s===e.length-1?(0,a.jsx)("span",{"aria-current":"page",children:t.name}):(0,a.jsx)("a",{href:t.path,children:t.name})},t.path))})})}function sJ({shot:e,priority:t,overlap:s}){let r="ipad"===e.device;return(0,a.jsx)("div",{className:`fp-device ${r?"fp-device-ipad":"fp-device-iphone"}`,style:r||s?void 0:{width:"min(46%,240px)",margin:0},children:(0,a.jsx)("img",{src:`${s_}/${e.src}`,alt:e.alt,width:900,height:r?1200:1951,loading:t?"eager":"lazy",decoding:"async",...t?{fetchPriority:"high"}:{}})})}function sZ({title:e,body:t}){return(0,a.jsx)("section",{className:"fp-section",children:(0,a.jsx)("div",{className:"fp-section-inner",children:(0,a.jsxs)("div",{className:"fp-cta-band",children:[(0,a.jsx)("h2",{children:e}),(0,a.jsx)("p",{children:t}),(0,a.jsxs)("div",{className:"fp-cta-row",children:[(0,a.jsx)("a",{href:`${s$}/download`,className:"fp-btn fp-btn-primary",children:"Download Flourish PDF"}),(0,a.jsx)("a",{href:`${s$}/pricing`,className:"fp-btn fp-btn-outline",children:"See pricing"})]})]})})})}function sX({items:e,open:t}){return(0,a.jsx)("div",{className:"fp-faq",children:e.map((e,s)=>(0,a.jsxs)("details",{...t&&0===s?{open:!0}:{},children:[(0,a.jsx)("summary",{children:e.q}),(0,a.jsx)("p",{children:e.a})]},e.q))})}function s0({f:e}){let t=sU.flatMap(a=>{let t=e.avail[a],s=sL[a].short;if("no"===t)return[];let r="yes"===t?s:"partial"===t?`${s} (partial)`:`${s} â soon`;return[{id:a,state:t,text:r}]});return 0===t.length?null:(0,a.jsx)("ul",{className:"fp-avail","aria-label":"Where it works",children:t.map(e=>(0,a.jsx)("li",{className:`a-${e.state}`,children:e.text},e.id))})}function s1({p:e}){let t=null!=e.annual;return(0,a.jsxs)("article",{className:`fp-plan${e.featured?" featured":""}`,children:[e.featured?(0,a.jsx)("span",{className:"fp-plan-flag",children:"Most popular"}):null,(0,a.jsx)("h3",{children:e.name}),(0,a.jsx)("p",{className:"fp-tag",children:e.tagline}),t?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"fp-price",children:[(0,a.jsxs)("span",{className:"fp-p-year",children:[sB(e.annual),(0,a.jsx)("small",{children:" /year"})]}),(0,a.jsxs)("span",{className:"fp-p-month",children:[sB(e.monthly),(0,a.jsx)("small",{children:" /month"})]})]}),(0,a.jsxs)("p",{className:"fp-price-sub",children:[(0,a.jsxs)("span",{className:"fp-p-year",children:[sB(e.annual/12),"/month, billed yearly"]}),(0,a.jsx)("span",{className:"fp-p-month",children:"Billed monthly · cancel any time"})]})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"fp-price",children:"$0"}),(0,a.jsx)("p",{className:"fp-price-sub",children:"Free to download. No account needed."})]}),(0,a.jsx)("a",{href:t?`${s$}/account`:`${s$}/download`,className:`fp-btn fp-btn-block ${e.featured?"fp-btn-primary":"fp-btn-outline"}`,children:t?`Choose ${e.name}`:"Download free"}),(0,a.jsx)("ul",{className:"fp-list",children:e.bullets.map(e=>(0,a.jsx)("li",{children:e},e))}),t?(0,a.jsx)("p",{className:"fp-note",children:"New subscribers: 7-day free trial. Terms are shown before you pay."}):null]})}function s2(){let e=Math.max(...sS.FP_PLANS.map(sS.fpAnnualSavingPercent));return(0,a.jsxs)("div",{className:"fp-planwrap",children:[(0,a.jsx)("input",{type:"radio",name:"fp-bill",id:"fp-bill-y",className:"fp-sr",defaultChecked:!0}),(0,a.jsx)("input",{type:"radio",name:"fp-bill",id:"fp-bill-m",className:"fp-sr"}),(0,a.jsxs)("div",{className:"fp-billing",children:[(0,a.jsxs)("label",{htmlFor:"fp-bill-y",children:["Yearly",(0,a.jsxs)("small",{children:["Save ",e,"%"]})]}),(0,a.jsx)("label",{htmlFor:"fp-bill-m",children:"Monthly"})]}),(0,a.jsx)("div",{className:"fp-plans",children:sS.FP_PLANS.map(e=>(0,a.jsx)(s1,{p:e},e.id))}),(0,a.jsxs)("p",{className:"fp-fine",children:["One subscription unlocks Mac, Windows, iPhone, iPad and Android â sign in with the same Flourish PDF account. Buy in the App Store, on Google Play, or on the ",(0,a.jsx)("a",{href:`${s$}/account`,children:"web account page"}
1719)," (the way Windows subscribes). Some tools ship on some platforms first; the comparison below shows where each one works today."]})]})}function s6({f:e,plan:t}){let s=(0,sS.fpIncludedIn)(e,t),r=["ios","android"].some(a=>"yes"===e.avail[a]||"partial"===e.avail[a]),n=!s&&r&&((0,sS.fpIncludedIn)(e,t,"ios")||(0,sS.fpIncludedIn)(e,t,"android")),i=(0,sS.fpOverall)(e);return s||n?"soon"===i?(0,a.jsx)("td",{children:(0,a.jsx)("span",{className:"fp-soon",children:"Soon"})}):(0,a.jsxs)("td",{children:[(0,a.jsx)("span",{className:"fp-yes","aria-label":n?"Included on iPhone, iPad and Android":"Included",children:"â"}),n?(0,a.jsx)("span",{className:"fp-row-note",children:"mobile"}):null]}):(0,a.jsx)("td",{children:(0,a.jsx)("span",{className:"fp-no","aria-label":"Not included",children:"â"})})}function s5({areas:e}){return(0,a.jsxs)("table",{className:"fp-table",children:[(0,a.jsx)("caption",{children:"Features by plan. A check means included; âSoonâ means not shipped yet."}),(0,a.jsx)("thead",{children:(0,a.jsxs)("tr",{children:[(0,a.jsx)("th",{scope:"col",children:"Feature"}),sS.FP_PLANS.map(e=>(0,a.jsxs)("th",{scope:"col",children:[e.name,(0,a.jsx)("small",{children:null==e.annual?"$0":`${sB(e.annual)}/yr`})]},e.id))]})}),e.map(e=>{let t=sS.FP_FEATURES.filter(a=>a.area===e.id);return 0===t.length?null:(0,a.jsxs)("tbody",{children:[(0,a.jsx)("tr",{children:(0,a.jsx)("th",{scope:"rowgroup",colSpan:1+sS.FP_PLANS.length,children:e.name})}),t.map(e=>(0,a.jsxs)("tr",{children:[(0,a.jsxs)("th",{scope:"row",children:[e.name,e.note?(0,a.jsx)("span",{className:"fp-row-note",children:e.note}):null,(0,a.jsx)(s0,{f:e})]}),sS.FP_PLANS.map(t=>(0,a.jsx)(s6,{f:e,plan:t.id},t.id))]},e.id))]},e.id)})]})}function s8({storeKey:e}){let t=sT.find(a=>a.key===e);return t?(0,a.jsxs)("div",{className:"fp-dl-card",children:[(0,a.jsxs)("div",{className:"fp-dl-head",children:[(0,a.jsx)("img",{src:`${sz}/icon.png`,alt:"",className:"fp-dl-icon",width:44,height:44}),(0,a.jsxs)("div",{children:[(0,a.jsxs)("h3",{children:["Flourish PDF for ",t.platform]}),(0,a.jsx)("p",{children:t.requirement})]})]}),(0,a.jsxs)("div",{className:"fp-dl-body",children:[(0,a.jsx)("a",{href:t.url,className:"fp-dl-badge",rel:"noopener","aria-label":`${t.badgeAlt} â Flourish PDF for ${t.platform}`,children:(0,a.jsx)("img",{src:t.badge,alt:t.badgeAlt,height:48,width:t.badgeWidth})}),(0,a.jsxs)("a",{href:t.url,className:"fp-dl-qr",rel:"noopener","aria-label":`Open the ${t.platform} store listing for Flourish PDF`,children:[(0,a.jsx)("img",{src:`${sz}/${t.qr}.svg`,alt:`QR code linking to the Flourish PDF listing for ${t.platform}`,width:112,height:112}),(0,a.jsxs)("span",{children:[(0,a.jsx)("strong",{children:"Scan to install"}),"Point your camera at the code to open the listing."]})]})]}),(0,a.jsx)("p",{className:"fp-dl-url",children:t.display})]}):null}function s4({id:e="download",heading:t=!0}){return(0,a.jsx)("section",{id:e,className:"fp-section",children:(0,a.jsxs)("div",{className:"fp-section-inner",children:[t?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("p",{className:"fp-label",children:"Download"}),(0,a.jsx)("h2",{className:"fp-h2",children:"Available on Mac, iPhone, iPad, Android and Windows"}),(0,a.jsx)("p",{className:"fp-lead",children:"Install from the store your device already trusts. Flourish PDF is a free download â every tool is visible from the first launch, and a Plus or Pro plan unlocks the premium ones when you need them."})]}):null,(0,a.jsx)("div",{className:"fp-dl-grid",children:sT.map(e=>(0,a.jsx)(s8,{storeKey:e.key},e.key))}),(0,a.jsx)("p",{className:"fp-dl-note",children:"One Plus or Pro plan covers every platform â buy it once and sign in anywhere."})]})})}function s3(){return(0,a.jsx)("section",{className:"fp-section fp-privacy-band",children:(0,a.jsxs)("div",{className:"fp-section-inner",children:[(0,a.jsx)("p",{className:"fp-label",children:"Why Flourish"}),(0,a.jsx)("h2",{className:"fp-h2",children:"Private by architecture, not by promise"}),(0,a.jsx)("p",{className:"fp-lead",children:"Most PDF apps quietly send your file to a server to run OCR, conversion, or AI. Flourish PDF has no document upload endpoint at all â which makes it a genuine fit for legal, medical, financial and other sensitive documents."}),(0,a.jsx)("div",{className:"fp-grid fp-grid-4",children:sS.FP_PRIVACY_POINTS.map(e=>(0,a.jsxs)("div",{className:"fp-card",children:[(0,a.jsx)("h3",{children:e.title}),(0,a.jsx)("p",{children:e.body})]},e.title))})]})})}function s7({areas:e}){return(0,a.jsx)("div",{className:"fp-grid",children:e.map(e=>{let t=sS.FP_FEATURES.filter(a=>a.area===e.id&&"soon"!==(0,sS.fpOverall)(a)).length;return(0,a.jsxs)("a",{href:`${s$}/${e.slug}`,className:"fp-card fp-card-link",children:[(0,a.jsx)("h3",{children:e.name}),(0,a.jsx)("p",{children:e.blurb}),(0,a.jsxs)("span",{className:"fp-more",children:[t," ",1===t?"tool":"tools"," today â"]})]},e.id)})})}function s9(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(sG,{data:sH()}),(0,a.jsx)(sG,{data:sY(sS.FP_FAQ.slice(0,6))}),(0,a.jsxs)(sK,{children:[(0,a.jsx)("section",{className:"fp-hero",children:(0,a.jsxs)("div",{className:"fp-hero-grid",children:[(0,a.jsxs)("div",{className:"fp-hero-copy",children:[(0,a.jsx)("div",{className:"fp-badge",children:"ð Local-first · nothing is uploaded"}),(0,a.jsxs)("h1",{className:"fp-h1",children:["Do more with your PDFs â",(0,a.jsx)("br",{}),(0,a.jsx)("em",{children:"without uploading them"})]}),(0,a.jsx)("p",{className:"fp-sub",children:"Read, edit, organize, scan, sign and protect PDFs on Mac, Windows, iPhone, iPad and Android. OCR, scanning and AI run on your device, so your documents never leave it. Free to start."}),(0,a.jsxs)("div",{className:"fp-cta-row",children:[(0,a.jsx)("a",{href:`${s$}/download`,className:"fp-btn fp-btn-primary",children:"Download free"}),(0,a.jsx)("a",{href:`${s$}/pricing`,className:"fp-btn fp-btn-outline",children:"See plans & pricing"})]}),(0,a.jsx)("p",{className:"fp-platforms",children:"Mac App Store · App Store · Google Play · Microsoft Store"})]}),(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"fp-shots",children:[(0,a.jsx)(sJ,{shot:{src:"ipad-comment.jpg",alt:"Flourish PDF on iPad: the Comment tab with Note, Add Text, Highlight, Draw, Stamp, Redact and Sign, page thumbnails on the left and a Processed locally badge",device:"ipad"},priority:!0}),(0,a.jsx)(sJ,{shot:{src:"iphone-editor.jpg",alt:"Flourish PDF on iPhone showing a PDF with the Draw, Add Text, Sign and Read Aloud toolbar",device:"iphone"},priority:!0,overlap:!0})]}),(0,a.jsx)("p",{className:"fp-shot-cap",children:"Flourish PDF on iPad and iPhone â real captures from the App Store listing."})]})]})}),(0,a.jsx)("section",{id:"pricing",className:"fp-section fp-band",children:(0,a.jsxs)("div",{className:"fp-section-inner",children:[(0,a.jsxs)("div",{className:"fp-center",children:[(0,a.jsx)("p",{className:"fp-label",children:"Plans"}),(0,a.jsx)("h2",{className:"fp-h2",children:"Simple plans. One subscription, every device."}),(0,a.jsx)("p",{className:"fp-lead",children:"Start free. Upgrade when you need merging, forms, redaction and the rest."})]}),(0,a.jsx)(s2,{}),(0,a.jsx)("p",{className:"fp-fine",children:(0,a.jsx)("a",{href:`${s$}/pricing`,children:"Compare every feature â"})})]})}),(0,a.jsx)(s3,{}),(0,a.jsx)("section",{className:"fp-section",children:(0,a.jsxs)("div",{className:"fp-section-inner",children:[(0,a.jsx)("p",{className:"fp-label",children:"Features"}),(0,a.jsx)("h2",{className:"fp-h2",children:"Everything you do to a PDF, in one app"}),(0,a.jsxs)("p",{className:"fp-lead",children:[sS.FP_AREAS.length," feature areas, each with its own page: what it does, which plan unlocks it, and which platforms it runs on today â including the parts we have not shipped yet."]}),(0,a.jsx)(s7,{areas:sS.FP_AREAS})]})}),(0,a.jsx)("section",{className:"fp-section fp-band",children:(0,a.jsxs)("div",{className:"fp-section-inner",children:[(0,a.jsx)("p",{className:"fp-label",children:"Platforms"}),(0,a.jsx)("h2",{className:"fp-h2",children:"One account. Five kinds of device."}),(0,a.jsx)("p",{className:"fp-lead",children:"Native on each platform â not a web wrapper. The same Flourish PDF account unlocks all of them."}),(0,a.jsx)("div",{className:"fp-grid fp-grid-4",children:sS.FP_PLATFORMS.map(e=>(0,a.jsxs)("a",{href:`${s$}/${e.slug}`,className:"fp-card fp-card-link",children:[(0,a.jsx)("h3",{children:e.name}),(0,a.jsx)("p",{children:e.requirement}),(0,a.jsxs)("span",{className:"fp-more",children:["PDF editor for ",e.short," â"]})]},e.id))})]})}),(0,a.jsx)(s4,{}),(0,a.jsx)("section",{id:"faq",className:"fp-section fp-band",children:(0,a.jsxs)("div",{className:"fp-section-inner",children:[(0,a.jsxs)("div",{className:"fp-center",children:[(0,a.jsx)("p",{className:"fp-label",children:"FAQ"}),(0,a.jsx)("h2",{className:"fp-h2",children:"Questions, answered"})]}),(0,a.jsx)(sX,{items:sS.FP_FAQ.slice(0,6)}),(0,a.jsx)("p",{className:"fp-fine",children:(0,a.jsx)("a",{href:`${s$}/faq`,children:"All questions â"})})]})}),(0,a.jsx)(sZ,{title:"Keep your documents yours",body:"Download Flourish PDF free. Every tool is visible from the first launch; a paid plan unlocks the premium ones."})]})]})}function re(){let e=[{name:"Flourish PDF",path:s$},{name:"Pricing",path:`${s$}/pricing`}];return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(sG,{data:sH()}),(0,a.jsx)(sG,{data:sY(sS.FP_FAQ)}),(0,a.jsx)(sG,{data:sV(e)}),(0,a.jsxs)(sK,{children:[(0,a.jsx)(sQ,{trail:e}),(0,a.jsx)("section",{className:"fp-hero",children:(0,a.jsxs)("div",{className:"fp-hero-inner",children:[(0,a.jsx)("div",{className:"fp-badge",children:"Free · Plus · Pro"}),(0,a.jsxs)("h1",{className:"fp-h1",children:["Pick a plan. ",(0,a.jsx)("em",{children:"Use it everywhere."})]}),(0,a.jsx)("p",{className:"fp-sub",children:"Free covers reading, marking up, signing and page basics. Plus adds the everyday document tools. Pro adds editing, redaction and the professional set. One subscription unlocks every platform."})]})}),(0,a.jsx)("section",{className:"fp-section",style:{paddingTop:0},children:(0,a.jsx)("div",{className:"fp-section-inner",children:(0,a.jsx)(s2,{})})}),(0,a.jsx)("section",{id:"compare",className:"fp-section fp-band",children:(0,a.jsxs)("div",{className:"fp-section-inner",children:[(0,a.jsx)("p",{className:"fp-label",children:"Compare"}),(0,a.jsx)("h2",{className:"fp-h2",children:"Every feature, every plan"}),(0,a.jsx)("p",{className:"fp-lead",children:"The chips under each feature show where it works today. Free is a real tier, not a trial: the tools marked Plus or Pro are visible in the app and unlock with a plan."}),(0,a.jsx)(s5,{areas:sS.FP_AREAS})]})}),(0,a.jsx)("section",{id:"faq",className:"fp-section",children:(0,a.jsxs)("div",{className:"fp-section-inner",children:[(0,a.jsxs)("div",{className:"fp-center",children:[(0,a.jsx)("p",{className:"fp-label",children:"FAQ"}),(0,a.jsx)("h2",{className:"fp-h2",children:"Pricing questions"})]}),(0,a.jsx)(sX,{items:sS.FP_FAQ})]})}),(0,a.jsx)(sZ,{title:"Start free",body:"Download Flourish PDF and try every tool. Upgrade from inside the app, or on the web, when you are ready."})]})]})}function ra(){let e=[{name:"Flourish PDF",path:s$},{name:"Features",path:`${s$}/features`}];return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(sG,{data:sV(e)}),(0,a.jsxs)(sK,{children:[(0,a.jsx)(sQ,{trail:e}),(0,a.jsx)("section",{className:"fp-hero",children:(0,a.jsxs)("div",{className:"fp-hero-inner",children:[(0,a.jsx)("div",{className:"fp-badge",children:"All features"}),(0,a.jsxs)("h1",{className:"fp-h1",children:["Every tool, ",(0,a.jsx)("em",{children:"and where it runs today"})]}),(0,a.jsx)("p",{className:"fp-sub",children:"An honest feature list: what ships on Mac, Windows, iPhone, iPad and Android right now, which plan unlocks it, and what is still on the way."})]})}),(0,a.jsx)("section",{className:"fp-section",style:{paddingTop:0},children:(0,a.jsx)("div",{className:"fp-section-inner",children:(0,a.jsx)(s7,{areas:sS.FP_AREAS})})}),(0,a.jsx)("section",{id:"compare",className:"fp-section fp-band",children:(0,a.jsxs)("div",{className:"fp-section-inner",children:[(0,a.jsx)("h2",{className:"fp-h2",children:"The full comparison"}),(0,a.jsx)(s5,{areas:sS.FP_AREAS})]})}),(0,a.jsx)(sZ,{title:"Try it free",body:"Every tool is visible from the first launch. A paid plan unlocks the premium ones."})]})]})}function rt({f:e,compact:t}){let s,r="soon"===(0,sS.fpOverall)(e);return(0,a.jsxs)("div",{className:"fp-feat",children:[(0,a.jsxs)("h3",{children:[e.name,(0,a.jsx)("span",{className:`fp-pill${r?" fp-pill-soon":""}`,children:r?"Soon":(s=sq[e.plan],e.mobilePlan&&e.mobilePlan!==e.plan?`${s} \xb7 ${sq[e.mobilePlan]} on mobile`:s)})]}),e.detail?(0,a.jsx)("p",{children:e.detail}):null,e.note?(0,a.jsx)("p",{className:"fp-row-note",children:e.note}):null,t?null:(0,a.jsx)(s0,{f:e})]})}function rs({area:e}){let t=sS.FP_FEATURES.filter(a=>a.area===e.id),s=t.filter(e=>
1719"soon"!==(0,sS.fpOverall)(e)),r=t.filter(e=>"soon"===(0,sS.fpOverall)(e)),n=sS.FP_AREAS.filter(a=>a.id!==e.id).slice(0,3),i=[{name:"Flourish PDF",path:s$},{name:"Features",path:`${s$}/features`},{name:e.name,path:`${s$}/${e.slug}`}];return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(sG,{data:sV(i)}),(0,a.jsxs)(sK,{children:[(0,a.jsx)(sQ,{trail:i}),(0,a.jsx)("section",{className:"fp-hero",children:(0,a.jsxs)("div",{className:"fp-hero-grid",children:[(0,a.jsxs)("div",{className:"fp-hero-copy",children:[(0,a.jsx)("div",{className:"fp-badge",children:e.name}),(0,a.jsx)("h1",{className:"fp-h1",children:e.headline}),(0,a.jsx)("p",{className:"fp-sub",children:e.sub}),(0,a.jsxs)("div",{className:"fp-cta-row",children:[(0,a.jsx)("a",{href:`${s$}/download`,className:"fp-btn fp-btn-primary",children:"Download free"}),(0,a.jsx)("a",{href:`${s$}/pricing`,className:"fp-btn fp-btn-outline",children:"See plans & pricing"})]})]}),e.shot?(0,a.jsxs)("div",{children:[(0,a.jsx)("div",{className:"fp-shots",children:(0,a.jsx)(sJ,{shot:e.shot,priority:!0})}),(0,a.jsx)("p",{className:"fp-shot-cap",children:"Real capture from the App Store listing."})]}):(0,a.jsx)("div",{})]})}),(0,a.jsx)("section",{className:"fp-section fp-band",children:(0,a.jsxs)("div",{className:"fp-section-inner",children:[(0,a.jsx)("p",{className:"fp-label",children:"What you can do"}),(0,a.jsxs)("h2",{className:"fp-h2",children:[e.name,", tool by tool"]}),(0,a.jsx)("p",{className:"fp-lead",children:"The plan that unlocks each tool is on its label; the chips show which platforms have it today."}),(0,a.jsx)("div",{className:"fp-grid",children:s.map(e=>(0,a.jsx)(rt,{f:e},e.id))})]})}),r.length>0?(0,a.jsx)("section",{className:"fp-section",children:(0,a.jsxs)("div",{className:"fp-section-inner",children:[(0,a.jsx)("p",{className:"fp-label",children:"On the way"}),(0,a.jsx)("h2",{className:"fp-h2",children:"Not shipped yet"}),(0,a.jsx)("p",{className:"fp-lead",children:"We list these so you know what to expect â and we do not sell them until they work."}),(0,a.jsx)("div",{className:"fp-grid",children:r.map(e=>(0,a.jsx)(rt,{f:e},e.id))})]})}):null,(0,a.jsx)(s3,{}),(0,a.jsx)("section",{className:"fp-section",children:(0,a.jsxs)("div",{className:"fp-section-inner",children:[(0,a.jsx)("p",{className:"fp-label",children:"More"}),(0,a.jsx)("h2",{className:"fp-h2",children:"Other things Flourish PDF does"}),(0,a.jsx)(s7,{areas:n})]})}),(0,a.jsx)(sZ,{title:`Try ${e.name.toLowerCase()} free`,body:"Download Flourish PDF. Every tool is visible from the first launch; a paid plan unlocks the premium ones."})]})]})}function rr({platform:e}){let t=e.id,s=sS.FP_FEATURES.filter(e=>"yes"===e.avail[t]),r=sS.FP_FEATURES.filter(e=>"partial"===e.avail[t]),n=sS.FP_FEATURES.filter(e=>"soon"===e.avail[t]&&"soon"!==(0,sS.fpOverall)(e)),i=[{name:"Flourish PDF",path:s$},{name:e.name,path:`${s$}/${e.slug}`}];return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(sG,{data:sV(i)}),(0,a.jsxs)(sK,{children:[(0,a.jsx)(sQ,{trail:i}),(0,a.jsx)("section",{className:"fp-hero",children:(0,a.jsxs)("div",{className:"fp-hero-grid",children:[(0,a.jsxs)("div",{className:"fp-hero-copy",children:[(0,a.jsx)("div",{className:"fp-badge",children:e.requirement}),(0,a.jsx)("h1",{className:"fp-h1",children:e.headline}),(0,a.jsx)("p",{className:"fp-sub",children:e.sub}),(0,a.jsxs)("div",{className:"fp-cta-row",children:[(0,a.jsxs)("a",{href:"#download",className:"fp-btn fp-btn-primary",children:["Get it for ",e.short]}),(0,a.jsx)("a",{href:`${s$}/pricing`,className:"fp-btn fp-btn-outline",children:"See plans & pricing"})]})]}),(0,a.jsx)(s8,{storeKey:t})]})}),(0,a.jsx)("section",{className:"fp-section fp-band",children:(0,a.jsxs)("div",{className:"fp-section-inner",children:[(0,a.jsxs)("p",{className:"fp-label",children:["On ",e.short]}),(0,a.jsxs)("h2",{className:"fp-h2",children:["What works on ",e.short," today"]}),(0,a.jsx)("ul",{className:"fp-list",children:e.points.map(e=>(0,a.jsx)("li",{children:e},e))}),(0,a.jsx)("div",{className:"fp-grid",style:{marginTop:28},children:s.map(e=>(0,a.jsx)(rt,{f:e,compact:!0},e.id))})]})}),r.length>0?(0,a.jsx)("section",{className:"fp-section",children:(0,a.jsxs)("div",{className:"fp-section-inner",children:[(0,a.jsx)("p",{className:"fp-label",children:"Partly there"}),(0,a.jsxs)("h2",{className:"fp-h2",children:["Works in part on ",e.short]}),(0,a.jsx)("div",{className:"fp-grid",children:r.map(e=>(0,a.jsx)(rt,{f:e,compact:!0},e.id))})]})}):null,n.length>0?(0,a.jsx)("section",{className:"fp-section fp-band",children:(0,a.jsxs)("div",{className:"fp-section-inner",children:[(0,a.jsx)("p",{className:"fp-label",children:"Coming"}),(0,a.jsxs)("h2",{className:"fp-h2",children:["On other platforms, on the way to ",e.short]}),(0,a.jsx)("p",{className:"fp-lead",children:"Each of these already ships somewhere. We are bringing them across â and will not advertise one here until it works here."}),(0,a.jsx)("ul",{className:"fp-list",children:n.map(e=>(0,a.jsx)("li",{children:e.name},e.id))})]})}):null,(0,a.jsx)(s4,{heading:!1}),(0,a.jsx)(sZ,{title:`Flourish PDF for ${e.short}`,body:"Free to download. Your documents stay on your device."})]})]})}function rn(){let e=[{name:"Flourish PDF",path:s$},{name:"Download",path:`${s$}/download`}];return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(sG,{data:sV(e)}),(0,a.jsxs)(sK,{children:[(0,a.jsx)(sQ,{trail:e}),(0,a.jsx)("section",{className:"fp-hero",children:(0,a.jsxs)("div",{className:"fp-hero-inner",children:[(0,a.jsx)("div",{className:"fp-badge",children:"Free to download"}),(0,a.jsxs)("h1",{className:"fp-h1",children:["Download ",(0,a.jsx)("em",{children:"Flourish PDF"})]}),(0,a.jsx)("p",{className:"fp-sub",children:"Mac, iPhone, iPad, Android and Windows. Install from your device's own store. Free to start â a paid plan unlocks the premium tools on every device you sign in on."})]})}),(0,a.jsx)(s4,{heading:!1}),(0,a.jsx)("section",{className:"fp-section fp-band",children:(0,a.jsxs)("div",{className:"fp-section-inner",children:[(0,a.jsx)("p",{className:"fp-label",children:"By platform"}),(0,a.jsx)("h2",{className:"fp-h2",children:"Details for your device"}),(0,a.jsx)("div",{className:"fp-grid fp-grid-4",children:sS.FP_PLATFORMS.map(e=>(0,a.jsxs)("a",{href:`${s$}/${e.slug}`,className:"fp-card fp-card-link",children:[(0,a.jsx)("h3",{children:e.name}),(0,a.jsx)("p",{children:e.requirement}),(0,a.jsxs)("span",{className:"fp-more",children:["What works on ",e.short," â"]})]},e.id))})]})})]})]})}function ri(){let e=[{name:"Flourish PDF",path:s$},{name:"FAQ",path:`${s$}/faq`}];return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(sG,{data:sY(sS.FP_FAQ)}),(0,a.jsx)(sG,{data:sV(e)}),(0,a.jsxs)(sK,{children:[(0,a.jsx)(sQ,{trail:e}),(0,a.jsx)("section",{className:"fp-hero",children:(0,a.jsxs)("div",{className:"fp-hero-inner",children:[(0,a.jsx)("div",{className:"fp-badge",children:"FAQ"}),(0,a.jsxs)("h1",{className:"fp-h1",children:["Flourish PDF ",(0,a.jsx)("em",{children:"questions"})]}),(0,a.jsxs)("p",{className:"fp-sub",children:["Plans, the free tier, on-device AI, where your documents go, and how billing works. Something missing? Email"," ",(0,a.jsx)("a",{href:"mailto:[email protected]",children:"[email protected]"})," or use the ",(0,a.jsx)("a",{href:sA,children:"contact form"}),"."]})]})}),(0,a.jsx)("section",{className:"fp-section",style:{paddingTop:0},children:(0,a.jsx)("div",{className:"fp-section-inner",children:(0,a.jsx)(sX,{items:sS.FP_FAQ,open:!0})})}),(0,a.jsx)(sZ,{title:"Still deciding?",body:"Download Flourish PDF free and try every tool before you pay for anything."})]})]})}let ro="/contact",rl="[email protected]",rc="https://apps.apple.com/app/id6776814909",rd="/messageexporter",rp=` 1720.me-wrap{ 1721 --me-red:#d94436;--me-red-deep:#a92f24;--me-red-soft:#fdeeec; 1722 --me-green:#13a05a;--me-green-deep:#0c7a44; 1723 --me-bg:#faf8f7;--me-fg:#191416;--me-muted:#5f5860; 1724 --me-border:rgba(25,20,22,0.10);--me-surface:rgba(255,255,255,0.80); 1725 --me-ink:#15171c; 1726 --me-radius:16px; 1727 font-family:-apple-system,BlinkMacSystemFont,"Inter",system-ui,sans-serif; 1728 background:var(--me-bg);color:var(--me-fg); 1729 -webkit-font-smoothing:antialiased; 1730} 1731.me-wrap *,.me-wrap *::before,.me-wrap *::after{box-sizing:border-box} 1732.me-wrap img{max-width:100%;display:block} 1733 1734/* nav */ 1735.me-nav{ 1736 position:sticky;top:0;z-index:100;height:64px;padding:0 clamp(16px,4vw,48px); 1737 display:flex;align-items:center;justify-content:space-between;gap:12px; 1738 background:rgba(250,248,247,0.86);backdrop-filter:blur(18px); 1739 border-bottom:1px solid var(--me-border); 1740} 1741.me-nav-logo{display:flex;align-items:center;gap:10px;text-decoration:none;color:var(--me-fg);font-weight:650;flex:none} 1742.me-nav-logo img{width:30px;height:30px;border-radius:7px} 1743.me-nav-links{display:flex;align-items:center;gap:2px;flex-wrap:wrap;justify-content:flex-end} 1744.me-nav-links a{font-size:.85rem;color:var(--me-muted);text-decoration:none;padding:8px 11px;border-radius:999px;white-space:nowrap} 1745.me-nav-links a:hover{color:var(--me-fg);background:rgba(217,68,54,0.08)} 1746.me-nav-links a.me-nav-cta{background:var(--me-green-deep);color:#fff;font-weight:600;padding:9px 16px} 1747.me-nav-links a.me-nav-cta:hover{background:var(--me-green);color:#fff} 1748@media(max-width:560px){.me-nav-links a:not(.me-nav-cta){display:none}} 1749 1750/* hero */ 1751.me-hero{padding:clamp(48px,8vw,92px) clamp(20px,4vw,48px) clamp(28px,4vw,44px);text-align:center; 1752 background:radial-gradient(120% 90% at 50% 0%,#fff 0%,var(--me-bg) 62%)} 1753.me-hero-inner{max-width:900px;margin:0 auto} 1754.me-badge{display:inline-flex;align-items:center;gap:7px;padding:6px 14px;border-radius:999px; 1755 font-size:.78rem;font-weight:600;background:var(--me-red-soft);color:var(--me-red-deep);margin-bottom:20px} 1756.me-h1{font-size:clamp(2.15rem,5.6vw,3.7rem);font-weight:760;letter-spacing:-.035em;line-height:1.04;margin:0 0 18px} 1757.me-h1 em{font-style:normal;color:var(--me-red-deep)} 1758.me-sub{font-size:clamp(1rem,2vw,1.16rem);color:var(--me-muted);line-height:1.7;max-width:62ch;margin:0 auto 28px} 1759.me-cta-row{display:flex;flex-wrap:wrap;gap:12px;justify-content:center} 1760.me-btn{display:inline-flex;align-items:center;gap:8px;
1760padding:13px 26px;border-radius:999px; 1761 font-size:.92rem;font-weight:620;text-decoration:none;transition:transform .18s,box-shadow .18s,background .18s} 1762.me-btn:hover{transform:translateY(-1px)} 1763.me-btn-primary{background:var(--me-green-deep);color:#fff;box-shadow:0 10px 26px -12px rgba(12,122,68,.65)} 1764.me-btn-primary:hover{background:var(--me-green)} 1765.me-btn-outline{background:var(--me-surface);color:var(--me-fg);border:1.5px solid var(--me-border)} 1766.me-meta{font-size:.85rem;color:var(--me-muted);margin-top:22px} 1767 1768/* download row â Apple's official Mac App Store badge beside a scannable QR. 1769 Both point at the same listing: click the badge on a Mac, scan the code from 1770 a phone or from a screenshot of this page used in an ad. */ 1771.me-dl{display:flex;flex-wrap:wrap;gap:16px;align-items:stretch;justify-content:center} 1772.me-dl-card{display:flex;align-items:center;gap:14px;padding:14px 20px; 1773 text-decoration:none;color:inherit;background:var(--me-surface); 1774 border:1px solid var(--me-border);border-radius:16px; 1775 box-shadow:0 10px 26px -20px rgba(20,16,18,.5); 1776 transition:transform .18s,box-shadow .18s,border-color .18s} 1777.me-dl-card:hover{transform:translateY(-2px);border-color:rgba(25,20,22,.20); 1778 box-shadow:0 16px 34px -20px rgba(20,16,18,.55)} 1779/* Apple requires the badge artwork be used unaltered, with clear space around 1780 it â hence the padded card rather than a tight crop. */ 1781.me-dl-badge img{height:48px;width:auto;display:block} 1782/* 111px = exactly 3 device pixels per module across all 37, so no module lands 1783 on a fractional boundary and softens. Verified to decode at this size; do not 1784 shrink it without re-checking, and see the QR note on DownloadRow. */ 1785.me-dl-qr img{width:111px;height:111px;display:block;border-radius:8px;background:#fff} 1786.me-dl-qr-text{text-align:left;max-width:184px} 1787.me-dl-qr-text strong{display:block;font-size:.92rem;font-weight:660;margin-bottom:4px} 1788.me-dl-qr-text span{display:block;font-size:.8rem;color:var(--me-muted);line-height:1.5} 1789.me-cta-sub{margin-top:18px} 1790 1791/* mac window frame around real UI captures */ 1792.me-shot{ 1793 border-radius:13px;overflow:hidden;border:1px solid rgba(25,20,22,.16); 1794 background:var(--me-ink);box-shadow:0 34px 70px -34px rgba(20,16,18,.55),0 4px 14px -6px rgba(20,16,18,.22); 1795} 1796.me-shot-bar{height:30px;display:flex;align-items:center;gap:7px;padding:0 13px; 1797 background:linear-gradient(180deg,#33353d,#26282e);border-bottom:1px solid rgba(0,0,0,.4)} 1798.me-dot{width:11px;height:11px;border-radius:50%} 1799.me-dot.r{background:#ff5f57}.me-dot.y{background:#febc2e}.me-dot.g{background:#28c840} 1800.me-shot-title{margin-left:auto;margin-right:auto;padding-right:34px;font-size:.72rem;color:rgba(255,255,255,.55);letter-spacing:.01em} 1801.me-shot img{width:100%;height:auto} 1802.me-hero-shot{max-width:1020px;margin:clamp(28px,5vw,52px) auto 0;padding:0 clamp(16px,4vw,48px)} 1803.me-caption{font-size:.8rem;color:var(--me-muted);text-align:center;margin:12px 0 0} 1804 1805/* sections */ 1806.me-section{padding:clamp(46px,7vw,86px) clamp(20px,4vw,48px)} 1807.me-section-inner{max-width:1080px;margin:0 auto} 1808.me-label{font-size:.72rem;font-weight:680;letter-spacing:.18em;text-transform:uppercase;color:var(--me-red-deep);margin:0 0 12px} 1809.me-h2{font-size:clamp(1.65rem,3.4vw,2.4rem);font-weight:730;letter-spacing:-.025em;margin:0 0 14px} 1810.me-lead{font-size:1.02rem;color:var(--me-muted);line-height:1.72;max-width:64ch;margin:0 0 36px} 1811.me-tint{background:linear-gradient(180deg,#fdeeec 0%,var(--me-bg) 100%)} 1812.me-dark{background:var(--me-ink);color:#f2eff0} 1813.me-dark .me-h2{color:#fff} 1814.me-dark .me-label{color:#ff8a7a} 1815.me-dark .me-lead{color:rgba(255,255,255,.68)} 1816 1817/* steps */ 1818.me-steps{display:grid;gap:18px;counter-reset:step} 1819@media(min-width:820px){.me-steps{grid-template-columns:repeat(3,1fr)}} 1820.me-step{background:var(--me-surface);border:1px solid var(--me-border);border-radius:var(--me-radius);padding:24px} 1821.me-step-n{width:30px;height:30px;border-radius:9px;background:var(--me-red);color:#fff; 1822 display:flex;align-items:center;justify-content:center;font-weight:700;font-size:.92rem;margin-bottom:14px} 1823.me-step h3{font-size:1.02rem;font-weight:660;margin:0 0 8px} 1824.me-step p{font-size:.91rem;color:var(--me-muted);line-height:1.65;margin:0} 1825 1826/* feature grid */ 1827.me-grid{display:grid;gap:18px} 1828@media(min-width:640px){.me-grid{grid-template-columns:repeat(2,1fr)}} 1829@media(min-width:980px){.me-grid{grid-template-columns:repeat(3,1fr)}} 1830.me-card{background:var(--me-surface);border:1px solid var(--me-border);border-radius:var(--me-radius);padding:22px} 1831.me-card h3{font-size:1rem;font-weight:650;margin:0 0 8px} 1832.me-card p{font-size:.9rem;color:var(--me-muted);line-height:1.62;margin:0} 1833.me-card-icon{font-size:1.35rem;margin-bottom:10px} 1834.me-dark .me-card{background:rgba(255,255,255,.05);border-color:rgba(255,255,255,.12)} 1835.me-dark .me-card p{color:rgba(255,255,255,.66)} 1836 1837/* alternating showcase rows */ 1838.me-row{display:grid;gap:clamp(24px,4vw,52px);align-items:center;margin-top:clamp(40px,6vw,72px)}
1839@media(min-width:900px){ 1840 .me-row{grid-template-columns:0.92fr 1.08fr} 1841 .me-row.flip .me-row-text{order:2} 1842 .me-row.flip .me-row-shot{order:1} 1843} 1844/* permission-flow panel â an explanatory diagram, deliberately not styled as a 1845 screenshot, because the real macOS open panel lists the machine owner's 1846 cloud accounts by name and has no place on a marketing page */ 1847.me-flow{background:var(--me-surface);border:1px solid var(--me-border); 1848 border-radius:var(--me-radius);padding:clamp(22px,3vw,32px)} 1849.me-flow-step{display:flex;gap:14px;align-items:flex-start} 1850.me-flow-step + .me-flow-step{margin-top:18px;padding-top:18px;border-top:1px solid var(--me-border)} 1851.me-flow-num{flex:none;width:26px;height:26px;border-radius:50%;background:var(--me-red-soft); 1852 color:var(--me-red-deep);font-size:.8rem;font-weight:700;display:flex; 1853 align-items:center;justify-content:center} 1854.me-flow-step h4{margin:2px 0 5px;font-size:.96rem;font-weight:650} 1855.me-flow-step p{margin:0;font-size:.88rem;color:var(--me-muted);line-height:1.6} 1856.me-flow-foot{margin-top:22px;padding:13px 15px;border-radius:10px; 1857 background:rgba(19,160,90,.09);border:1px solid rgba(19,160,90,.22)} 1858.me-flow-foot p{margin:0;font-size:.86rem;color:var(--me-green-deep);line-height:1.55} 1859.me-row-text h3{font-size:clamp(1.3rem,2.4vw,1.65rem);font-weight:700;letter-spacing:-.02em;margin:0 0 12px} 1860.me-row-text p{font-size:.98rem;color:var(--me-muted);line-height:1.72;margin:0 0 14px} 1861.me-row-text ul{margin:0;padding-left:1.1rem} 1862.me-row-text li{font-size:.93rem;color:var(--me-muted);line-height:1.6;margin-bottom:7px} 1863 1864/* format chips */ 1865.me-chips{display:flex;flex-wrap:wrap;gap:10px;margin-top:8px} 1866.me-chip{display:inline-flex;align-items:center;gap:8px;padding:10px 16px;border-radius:12px; 1867 background:var(--me-surface);border:1px solid var(--me-border);font-size:.9rem;font-weight:600} 1868.me-chip span{font-weight:500;color:var(--me-muted);font-size:.82rem} 1869.me-dark .me-chip{background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.13);color:#fff} 1870.me-dark .me-chip span{color:rgba(255,255,255,.6)} 1871 1872/* plans */ 1873.me-plans{display:grid;gap:18px;margin-top:12px} 1874@media(min-width:760px){.me-plans{grid-template-columns:repeat(2,1fr)}} 1875.me-plan{background:var(--me-surface);border:1px solid var(--me-border);border-radius:var(--me-radius);padding:26px} 1876.me-plan.featured{border-color:var(--me-green);box-shadow:0 12px 32px -20px rgba(19,160,90,.6)} 1877.me-plan h3{margin:0 0 4px;font-size:1.06rem;font-weight:690} 1878.me-price{font-size:1.75rem;font-weight:750;letter-spacing:-.02em;margin:6px 0 2px} 1879.me-price small{font-size:.8rem;font-weight:500;color:var(--me-muted)} 1880.me-plan ul{margin:14px 0 0;padding-left:1.1rem} 1881.me-plan li{font-size:.9rem;color:var(--me-muted);line-height:1.6;margin-bottom:7px} 1882.me-plan-note{font-size:.82rem;color:var(--me-muted);margin:18px 0 0;line-height:1.6} 1883 1884/* faq */ 1885.me-faq{display:grid;gap:14px} 1886@media(min-width:860px){.me-faq{grid-template-columns:repeat(2,1fr)}} 1887.me-faq-item{background:var(--me-surface);border:1px solid var(--me-border);border-radius:var(--me-radius);padding:20px 22px} 1888.me-faq-item h3{font-size:.98rem;font-weight:650;margin:0 0 7px} 1889.me-faq-item p{font-size:.9rem;color:var(--me-muted);line-height:1.65;margin:0} 1890.me-faq-item a{color:var(--me-red-deep);text-decoration:underline;text-underline-offset:3px} 1891 1892/* closing cta */ 1893.me-final{padding:clamp(56px,8vw,96px) clamp(20px,4vw,48px);text-align:center; 1894 background:linear-gradient(180deg,var(--me-bg) 0%,#f3ece9 100%)} 1895 1896/* long-form / legal / manual */ 1897.me-doc{padding:clamp(36px,6vw,68px) clamp(20px,4vw,48px)} 1898.me-doc-inner{max-width:800px;margin:0 auto} 1899.me-doc h1{font-size:clamp(1.9rem,4vw,2.5rem);font-weight:740;letter-spacing:-.025em;margin:0 0 8px} 1900.me-updated{font-size:.85rem;color:var(--me-muted);margin:0 0 26px} 1901.me-doc h2{font-size:1.16rem;font-weight:690;margin:34px 0 10px;scroll-margin-top:80px} 1902.me-doc h3{font-size:1rem;font-weight:660;margin:22px 0 8px;scroll-margin-top:80px} 1903.me-doc p,.me-doc li{font-size:.95rem;line-height:1.78;color:var(--me-muted)} 1904.me-doc p{margin:0 0 14px} 1905.me-doc ul,.me-doc ol{margin:0 0 16px;padding-left:1.25rem} 1906.me-doc li{margin-bottom:7px} 1907.me-doc strong{color:var(--me-fg);font-weight:620} 1908.me-doc a{color:var(--me-red-deep);text-decoration:underline;text-underline-offset:3px;font-weight:560} 1909.me-doc kbd{font:inherit;font-size:.88em;background:#fff;border:1px solid var(--me-border); 1910 border-bottom-width:2px;border-radius:6px;padding:1px 6px;color:var(--me-fg)} 1911.me-box{background:var(--me-red-soft);border:1px solid rgba(217,68,54,.24); 1912 border-radius:var(--me-radius);padding:18px 20px;margin:0 0 26px} 1913.me-box p{margin:0;color:#5b241e} 1914.me-box p + p{margin-top:10px} 1915.me-note{background:#fff;border:1px solid var(--me-border);border-left:3px solid var(--me-green); 1916 border-radius:10px;padding:14px 18px;margin:0 0 20px} 1917.me-note p{margin:0;font-size:.91rem} 1918.me-doc-shot{margin:20px 0 28px} 1919.me-toc{background:#fff;border:1px solid var(--me-border);border-radius:var(--me-radius);padding:20px 24px;margin:0 0 30px} 1920.me-toc p{margin:0 0 10px;font-size:.74rem;font-weight:680;letter-spacing:.16em;text-transform:uppercase;color:var(--me-red-deep)} 1921.me-toc ol{margin:0;padding-left:1.15rem;columns:2;column-gap:28px} 1922@media(max-width:640px){.me-toc ol{columns:1}} 1923.me-toc li{margin-bottom:6px} 1924.me-toc a{font-weight:520} 1925 1926/* footer */ 1927.me-footer{border-top:1px solid var(--me-border);padding:26px clamp(20px,4vw,48px);background:rgba(255,255,255,.6)} 1928.me-footer-inner{max-width:1080px;margin:0 auto;display:flex;flex-wrap:wrap;gap:12px; 1929 align-items:center;justify-content:space-between;font-size:.84rem;color:var(--me-muted)} 1930.me-footer-links{display:flex;flex-wrap:wrap;gap:16px} 1931.me-footer-links a{color:var(--me-muted);text-decoration:none} 1932.me-footer-links a:hover{color:var(--me-red-deep)} 1933`;function rh(){return(0,a.jsxs)("nav",{className:"me-nav",children:[(0,a.jsxs)("a",{href:"/messageexporter",className:"me-nav-logo",children:[(0,a.jsx)("img",{src:`${rd}/icon.png`,alt:"",width:30,height:30}),(0,a.jsx)("span",{children:"Message Exporter"})]}),(0,a.jsxs)("div",{className:"me-nav-links",children:[(0,a.jsx)("a",{href:"/messageexporter",children:"Overview"}),(0,a.jsx)("a",{href:"/messageexporter/guide",children:"User Guide"}),(0,a.jsx)("a",{href:"/messageexporter/support",children:"Support"}),(0,a.jsx)("a",{href:rc,className:"me-nav-cta",rel:"noopener",children:"Mac App Store"})]})]})}function rm(){return(0,a.jsx)("footer",{className:"me-footer",children:(0,a.jsxs)("div",{className:"me-footer-inner",children:[(0,a.jsxs)("span",{children:["© ",new Date().getFullYear()," Soft Flourish"]}),(0,a.jsxs)("div",{className:"me-footer-links",children:[(0,a.jsx)("a",{href:"/messageexporter",children:"Overview"}),(0,a.jsx)("a",{href:"/messageexporter/guide",children:"User Guide"}),(0,a.jsx)("a",{href:"/messageexporter/support",children:"Support"}),(0,a.jsx)("a",{href:"/messageexporter/privacy",children:"Privacy Policy"}),(0,a.jsx)("a",{href:"/messageexporter/terms",children:"Terms of Use"}),(0,a.jsx)("a",{href:ro,children:"Contact"}),(0,a.jsx)("a",{href:"/",children:"softflourish.com"})]})]})})}function ru({src:e,alt:t,title:s}){return(0,a.jsxs)("div",{className:"me-shot",children:[(0,a.jsxs)("div",{className:"me-shot-bar",children:[(0,a.jsx)("span",{className:"me-dot r"}),(0,a.jsx)("span",{className:"me-dot y"}),(0,a.jsx)("span",{className:"me-dot g"}),(0,a.jsx)("span",{className:"me-shot-title",children:s})]}),(0,a.jsx)("img",{src:e,alt:t,loading:"lazy"})]})}function rf(){return(0,a.jsxs)("div",{className:"me-dl",children:[(0,a.jsx)("a",{href:rc,className:"me-dl-card me-dl-badge",rel:"noopener","aria-label":"Download Message Exporter on the Mac App Store",children:(0,a.jsx)("img",{src:`${rd}/mac-app-store-badge.svg`,alt:"Download on the Mac App Store",width:187,height:48})}),(0,a.jsxs)("a",{href:rc,className:"me-dl-card me-dl-qr",rel:"noopener","aria-label":"Open the Mac App Store listing for Message Exporter",children:[(0,a.jsx)("img",{src:`${rd}/qr-appstore.svg`,alt:"QR code linking to the Message Exporter listing on the Mac App Store",width:111,height:111}),(0,a.jsxs)("span",{className:"me-dl-qr-text",children:[(0,a.jsx)("strong",{children:"Scan to download"}),(0,a.jsx)("span",{children:"Point your camera at the code to open the App Store listing."})]})]})]})}let rg=[{icon:"ðï¸",title:"Every conversation, in one list",body:"Your Mac Messages contacts and group chats, with message counts, sorted and searchable. Pick a thread and read it before you export anything."},{icon:"ð",title:"Search names and message text",body:"One search f
1933ield looks through contact names and the words inside messages, so you can find the thread you half-remember."},{icon:"ð
",title:"Export exactly the range you need",body:"Last 7 days, last 30 days, all time, or a custom window. The export sheet tells you how many messages are in the range before you commit."},{icon:"ð",title:"Five real file formats",body:"PDF for reading and filing, HTML for sharing, CSV and Excel for analysis, plain text for anything else. Not screenshots â actual documents."},{icon:"ð¼ï¸",title:"Shared media, collected for you",body:"Manage Media finds every photo, video, PDF and file shared in a thread. Select what you want, or export the whole set as a ZIP."},{icon:"ð±",title:"Reads local iPhone backups",body:"Point it at an encrypted iPhone backup already on your Mac and browse those conversations too â useful for messages your Mac never synced."}],rx=[{q:"Does it upload my messages anywhere?",a:(0,a.jsxs)(a.Fragment,{children:["No. Reading, previewing, and exporting all happen on your Mac. Message text and attachments are never sent to us. See the"," ",(0,a.jsx)("a",{href:"/messageexporter/privacy",children:"Privacy Policy"}),"."]})},{q:"Do I need Full Disk Access?",a:(0,a.jsx)(a.Fragment,{children:"No. Message Exporter is sandboxed and asks you to pick your Messages folder once, through the standard macOS open panel. You can revoke it any time."})},{q:"What does the free version do?",a:(0,a.jsx)(a.Fragment,{children:"The last 3 days of any conversation, exported as plain text, plus the photos, videos, PDFs and files shared in those 3 days. Enough to see exactly how it works on your own messages before paying."})},{q:"Can I export a group chat?",a:(0,a.jsx)(a.Fragment,{children:"Yes. Group conversations appear in the contacts list like any other thread, and export with sender names included."})},{q:"Will the export include attachments?",a:(0,a.jsx)(a.Fragment,{children:"With Pro, yes â attachments are copied out alongside the transcript and bundled in a ZIP. You can also hand-pick which files to include."})},{q:"How do I cancel?",a:(0,a.jsx)(a.Fragment,{children:"System Settings â Apple Account â Subscriptions, like any App Store subscription. You keep Pro until the end of the period you paid for."})}];function rb(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:rp}}),(0,a.jsxs)("div",{className:"me-wrap",children:[(0,a.jsx)(rh,{}),(0,a.jsx)("section",{className:"me-hero",children:(0,a.jsxs)("div",{className:"me-hero-inner",children:[(0,a.jsx)("div",{className:"me-badge",children:"ð Runs entirely on your Mac"}),(0,a.jsxs)("h1",{className:"me-h1",children:["Turn your Mac Messages into",(0,a.jsx)("br",{}),(0,a.jsx)("em",{children:"files you actually own"})]}),(0,a.jsx)("p",{className:"me-sub",children:"Message Exporter reads the conversations already on your Mac and saves them as PDF, HTML, CSV, Excel, or plain text â with the shared photos, videos and documents collected alongside them. Nothing is uploaded. Nothing leaves your computer unless you export it."}),(0,a.jsx)(rf,{}),(0,a.jsx)("div",{className:"me-cta-row me-cta-sub",children:(0,a.jsx)("a",{href:"/messageexporter/guide",className:"me-btn me-btn-outline",children:"Read the user guide"})}),(0,a.jsx)("p",{className:"me-meta",children:"macOS 14 Sonoma or later · Free to try · Pro $4.99/month"})]})}),(0,a.jsxs)("div",{className:"me-hero-shot",children:[(0,a.jsx)(ru,{src:`${rd}/ui-browse.png`,alt:"Message Exporter on macOS: contact list on the left, the selected conversation on the right, with Media and Export buttons in the toolbar.",title:"Message Exporter"}),(0,a.jsx)("p",{className:"me-caption",children:"The actual app. Contacts on the left, the conversation on the right."})]}),(0,a.jsx)("section",{className:"me-section",children:(0,a.jsxs)("div",{className:"me-section-inner",children:[(0,a.jsx)("p",{className:"me-label",children:"How it works"}),(0,a.jsx)("h2",{className:"me-h2",children:"Three steps, about a minute"}),(0,a.jsx)("p",{className:"me-lead",children:"There is no import, no sync, and no account required to start. Message Exporter reads the Messages database that already exists on your Mac."}),(0,a.jsxs)("div",{className:"me-steps",children:[(0,a.jsxs)("div",{className:"me-step",children:[(0,a.jsx)("div",{className:"me-step-n",children:"1"}),(0,a.jsx)("h3",{children:"Grant Messages access"}),(0,a.jsx)("p",{children:"On first launch the app asks you to choose your Messages folder in a
1933normal macOS open panel. One click, no Full Disk Access, revocable any time."})]}),(0,a.jsxs)("div",{className:"me-step",children:[(0,a.jsx)("div",{className:"me-step-n",children:"2"}),(0,a.jsx)("h3",{children:"Pick a conversation"}),(0,a.jsx)("p",{children:"Browse or search your contacts and group chats, then read the thread in the app to be sure it's the right one before you export."})]}),(0,a.jsxs)("div",{className:"me-step",children:[(0,a.jsx)("div",{className:"me-step-n",children:"3"}),(0,a.jsx)("h3",{children:"Choose range and format"}),(0,a.jsx)("p",{children:"Set the time range, pick a format, decide whether to include attachments, and save the file wherever you want it."})]})]})]})}),(0,a.jsx)("section",{className:"me-section me-tint",children:(0,a.jsxs)("div",{className:"me-section-inner",children:[(0,a.jsx)("p",{className:"me-label",children:"Features"}),(0,a.jsx)("h2",{className:"me-h2",children:"Built for keeping records, not screenshots"}),(0,a.jsx)("p",{className:"me-lead",children:"Message threads are evidence, receipts, project history, and family memory. They deserve to exist as something better than a phone photo of a screen."}),(0,a.jsx)("div",{className:"me-grid",children:rg.map(e=>(0,a.jsxs)("div",{className:"me-card",children:[(0,a.jsx)("div",{className:"me-card-icon",children:e.icon}),(0,a.jsx)("h3",{children:e.title}),(0,a.jsx)("p",{children:e.body})]},e.title))})]})}),(0,a.jsx)("section",{className:"me-section",children:(0,a.jsxs)("div",{className:"me-section-inner",children:[(0,a.jsx)("p",{className:"me-label",children:"A closer look"}),(0,a.jsx)("h2",{className:"me-h2",children:"Every screen below is the real app"}),(0,a.jsx)("p",{className:"me-lead",children:"No mockups, no illustrations of features that don't exist â these are captures of Message Exporter running on macOS."}),(0,a.jsxs)("div",{className:"me-row",children:[(0,a.jsxs)("div",{className:"me-row-text",children:[(0,a.jsx)("h3",{children:"The export sheet"}),(0,a.jsx)("p",{children:"One sheet holds every decision: time range, file format, and whether to bring the attachments along. It shows the message count for the range you picked, so you know what you're about to save."}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Time range â 3 days, 7, 30, all time, or custom"}),(0,a.jsx)("li",{children:"Format â PDF, HTML, CSV, Excel, or plain text"}),(0,a.jsx)("li",{children:"Attachments â all files in range, or a selection you choose"}),(0,a.jsx)("li",{children:"A live count of the messages included"})]})]}),(0,a.jsx)("div",{className:"me-row-shot",children:(0,a.jsx)(ru,{src:`${rd}/ui-export.png`,alt:"The Export Options sheet in Message Exporter showing Time Range, Format, and Attachments controls.",title:"Export Options"})})]}),(0,a.jsxs)("div",{className:"me-row flip",children:[(0,a.jsxs)("div",{className:"me-row-text",children:[(0,a.jsx)("h3",{children:"Manage Media"}),(0,a.jsx)("p",{children:"Photos, videos, PDFs, voice memos and documents shared in a conversation are scattered across your disk with meaningless filenames. Manage Media lists them all with real names, sizes and dates â select all, clear, or hand-pick, then export the set as a ZIP."}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Every attachment in the thread, in one grid"}),(0,a.jsx)("li",{children:"Filter by the same time ranges as the export"}),(0,a.jsx)("li",{children:"Select all, clear, or choose individual files"}),(0,a.jsx)("li",{children:"Free plan includes media from the last 3 days"})]})]}),(0,a.jsx)("div",{className:"me-row-shot",children:(0,a.jsx)(ru,{src:`${rd}/ui-media.png`,alt:"The Manage Media sheet in Message Exporter listing shared photos, videos, PDFs and files with sizes and dates.",title:"Manage Media"})})]}),(0,a.jsxs)("div",{className:"me-row",children:[(0,a.jsxs)("div",{className:"me-row-text",children:[(0,a.jsx)("h3",{children:"Local iPhone backups"}),(0,a.jsx)("p",{children:"If you keep an encrypted iPhone backup on this Mac, Message Exporter can read the conversations inside it. Switch the source with a toggle in the toolbar and browse them exactly like your Mac messages."}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Add a backup folder from the sidebar"}),(0,a.jsx)("li",{children:"Switch between Mac and iPhone with the Source picker"}),(0,a.jsx)("li",{children:"Encrypted backups are decrypted locally, on your Mac"}),(0,a.jsx)("li",{children:"Same search, preview and export tools apply"})]})]}),(0,a.jsx)("div",{className:"me-row-shot",children:(0,a.jsx)(ru,{src:`${rd}/ui-backups.png`,alt:"Message Exporter showing an iPhone backup added as a message source, with the Source picker in the toolbar.",title:"iPhone Backups"})})]}),(0,a.jsxs)("div",{className:"me-row flip",children:[(0,a.jsxs)("div",{className:"me-row-text",children:[(0,a.jsx)("h3",{children:"Access on your terms"}),(0,a.jsx)("p",{children:"Message Exporter is sandboxed. It cannot read your Messages folder until you point it there yourself, and it asks in the standard macOS open panel rather than sending you to a settings pane to hand over your whole disk."}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"No Full Disk Access requirement"}),(0,a.jsx)("li",{children:"Contacts access is optional â decline and you'll see numbers"}),(0,a.jsx)("li",{children:"Revoke access whenever you like"}),(0,a.jsx)("li",{children:"Distributed and notarised through the Mac App Store"})]})]}),(0,a.jsx)("div",{className:"me-row-shot",children:(0,a.jsxs)("div",{className:"me-flow",children:[(0,a.jsxs)("div",{className:"me-flow-step",children:[(0,a.jsx)("span",{className:"me-flow-num",children:"1"}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h4",{children:"Open the app"}),(0,a.jsx)("p",{children:"Message Exporter starts with no access to anything. It explains what it needs before it asks."})]})]}),(0,a.jsxs)("div",{className:"me-flow-step",children:[(0,a.jsx)("span",{className:"me-flow-num",children:"2"}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h4",{children:"Point it at your Messages folder"}),(0,a.jsxs)("p",{children:["The standard macOS open panel appears at"," ",(0,a.jsx)("code",{children:"~/Library/Messages"}),". Click Allow and that one folder becomes readable â nothing else on your disk does."]})]})]}),(0,a.jsxs)("div",{className:"me-flow-step",children:[(0,a.jsx)("span",{className:"me-flow-num",children:"3"}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h4",{children:"Export whenever you like"}),(0,a.jsx)("p",{children:"macOS remembers the choice, so you only do this once. Revoke it any time by resetting the app's permissions or moving it to the Trash."})]})]}),(0,a.jsx)("div",{className:"me-flow-foot",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"Full Disk Access is never requested."})," Other Messages tools ask you to hand over your entire drive in System Settings. This one does not."]})})]})})]})]})}),(0,a.jsx)("section",{className:"me-section me-dark",children:(0,a.jsxs)("div",{className:"me-section-inner",children:[(0,a.jsx)("p",{className:"me-label",children:"Formats"}),(0,a.jsx)("h2",{className:"me-h2",children:"Save it as the thing you actually need"}),(0,a.jsx)("p",{className:"me-lead",children:"Different jobs need different files. A transcript you'll hand to a lawyer isn't the same artefact as one you'll pivot in a spreadsheet."}),(0,a.jsxs)("div",{className:"me-chips",children:[(0,a.jsxs)("div",{className:"me-chip",children:["PDF ",(0,a.jsx)("span",{children:"paginated, readable, printable"})]}),(0,a.jsxs)("div",{className:"me-chip",children:["HTML ",(0,a.jsx)("span",{children:"bubbles preserved, opens anyw
1933here"})]}),(0,a.jsxs)("div",{className:"me-chip",children:["CSV ",(0,a.jsx)("span",{children:"date, direction, message, attachments"})]}),(0,a.jsxs)("div",{className:"me-chip",children:["Excel (.xlsx) ",(0,a.jsx)("span",{children:"ready to filter and pivot"})]}),(0,a.jsxs)("div",{className:"me-chip",children:["Plain text ",(0,a.jsx)("span",{children:"simple, tiny, universal"})]}),(0,a.jsxs)("div",{className:"me-chip",children:["ZIP ",(0,a.jsx)("span",{children:"transcript plus every shared file"})]})]})]})}),(0,a.jsx)("section",{className:"me-section",children:(0,a.jsxs)("div",{className:"me-section-inner",children:[(0,a.jsx)("p",{className:"me-label",children:"Privacy"}),(0,a.jsx)("h2",{className:"me-h2",children:"Your messages never reach us"}),(0,a.jsx)("p",{className:"me-lead",children:"There is no upload step in Message Exporter, because there is nowhere to upload to. The app reads your local Messages database and writes files to the folder you choose. That's the whole data path."}),(0,a.jsxs)("div",{className:"me-grid",children:[(0,a.jsxs)("div",{className:"me-card",children:[(0,a.jsx)("div",{className:"me-card-icon",children:"ð»"}),(0,a.jsx)("h3",{children:"Local reads, local writes"}),(0,a.jsx)("p",{children:"Messages, attachments and iPhone backups are read from your disk and exported to your disk. No copy is sent anywhere."})]}),(0,a.jsxs)("div",{className:"me-card",children:[(0,a.jsx)("div",{className:"me-card-icon",children:"ð"}),(0,a.jsx)("h3",{children:"No analytics, no ads"}),(0,a.jsx)("p",{children:"There is no third-party analytics SDK, no advertising, and no tracking identifier in the app."})]}),(0,a.jsxs)("div",{className:"me-card",children:[(0,a.jsx)("div",{className:"me-card-icon",children:"ð"}),(0,a.jsx)("h3",{children:"Sign-in is only for Pro"}),(0,a.jsx)("p",{children:"An account exists so your subscription follows you to another Mac. It holds an email address and a plan status â nothing about your messages."})]})]}),(0,a.jsxs)("p",{className:"me-plan-note",children:["Full detail in the ",(0,a.jsx)("a",{href:"/messageexporter/privacy",style:{color:"var(--me-red-deep)"},children:"Privacy Policy"}),"."]})]})}),(0,a.jsx)("section",{className:"me-section me-tint",children:(0,a.jsxs)("div",{className:"me-section-inner",children:[(0,a.jsx)("p",{className:"me-label",children:"Pricing"}),(0,a.jsx)("h2",{className:"me-h2",children:"Try it on your own messages first"}),(0,a.jsx)("p",{className:"me-lead",children:"The free plan isn't a demo with fake data â it exports your real conversations, limited to the last three days. If it does what you need, Pro removes the limits."}),(0,a.jsxs)("div",{className:"me-plans",children:[(0,a.jsxs)("div",{className:"me-plan",children:[(0,a.jsx)("h3",{children:"Free"}),(0,a.jsx)("div",{className:"me-price",children:"$0"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Browse and search every conversation"}),(0,a.jsx)("li",{children:"Read full threads in the app"}),(0,a.jsx)("li",{children:"Export the last 3 days as plain text"}),(0,a.jsx)("li",{children:"Shared photos, videos, PDFs and files from the last 3 days"}),(0,a.jsx)("li",{children:"Add and browse local iPhone backups"})]})]}),(0,a.jsxs)("div",{className:"me-plan featured",children:[(0,a.jsx)("h3",{children:"Pro"}),(0,a.jsxs)("div",{className:"me-price",children:["$4.99 ",(0,a.jsx)("small",{children:"/ month"})]}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Everything in Free"}),(0,a.jsx)("li",{children:"Any time range â 7 days, 30 days, all time, custom"}),(0,a.jsx)("li",{children:"PDF, HTML, CSV, and Excel export"}),(0,a.jsx)("li",{children:"Full attachment export as a ZIP, any range"}),(0,a.jsx)("li",{children:"Your subscription follows your account to another Mac"})]})]})]}),(0,a.jsx)("p",{className:"me-plan-note",children:"Billed through your Ap
1933ple Account and renews monthly until cancelled. Cancel any time in System Settings â Apple Account â Subscriptions. Prices shown in USD; your local price appears in the App Store before you buy."})]})}),(0,a.jsx)("section",{className:"me-section",children:(0,a.jsxs)("div",{className:"me-section-inner",children:[(0,a.jsx)("p",{className:"me-label",children:"Questions"}),(0,a.jsx)("h2",{className:"me-h2",children:"The things people ask first"}),(0,a.jsxs)("p",{className:"me-lead",children:["More in the ",(0,a.jsx)("a",{href:"/messageexporter/guide",style:{color:"var(--me-red-deep)"},children:"user guide"})," and on the"," ",(0,a.jsx)("a",{href:"/messageexporter/support",style:{color:"var(--me-red-deep)"},children:"support page"}),"."]}),(0,a.jsx)("div",{className:"me-faq",children:rx.map(e=>(0,a.jsxs)("div",{className:"me-faq-item",children:[(0,a.jsx)("h3",{children:e.q}),(0,a.jsx)("p",{children:e.a})]},e.q))})]})}),(0,a.jsxs)("section",{className:"me-final",children:[(0,a.jsx)("h2",{className:"me-h2",children:"Keep the conversations that matter"}),(0,a.jsx)("p",{className:"me-sub",children:"Free to install, free to try on your own threads, and everything stays on your Mac."}),(0,a.jsx)(rf,{}),(0,a.jsx)("div",{className:"me-cta-row me-cta-sub",children:(0,a.jsx)("a",{href:"/messageexporter/support",className:"me-btn me-btn-outline",children:"Get support"})}),(0,a.jsx)("p",{className:"me-meta",children:"macOS 14 Sonoma or later · Apple silicon and Intel"})]}),(0,a.jsx)(rm,{})]})]})}function ry(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:rp}}),(0,a.jsxs)("div",{className:"me-wrap",children:[(0,a.jsx)(rh,{}),(0,a.jsx)("div",{className:"me-doc",children:(0,a.jsxs)("div",{className:"me-doc-inner",children:[(0,a.jsx)("h1",{children:"Message Exporter â User Guide"}),(0,a.jsx)("p",{className:"me-updated",children:"Last updated: July 2026 · Applies to Message Exporter 1.2 and later on macOS 14 or newer"}),(0,a.jsx)("div",{className:"me-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"In one paragraph:"})," install from the Mac App Store, grant access to your Messages folder when the open panel appears, choose a conversation, click"," ",(0,a.jsx)("strong",{children:"Export"}),", pick a time range and format, and save the file. Every step happens on your Mac."]})}),(0,a.jsxs)("div",{className:"me-toc",children:[(0,a.jsx)("p",{children:"Contents"}),(0,a.jsxs)("ol",{children:[(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"#install",children:"Installing and first launch"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"#access",children:"Granting Messages access"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"#contacts",children:"Contacts access (optional)"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"#window",children:"The main window"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"#browse",children:"Browsing and searching"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"#export",children:"Exporting a conversation"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"#formats",children:"Which format to choose"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"#media",children:"Manage Media"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"#backups",children:"Importing an iPhone backup"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"#account",children:"Accounts and Pro"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"#free",children:"What the free plan includes"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"#troubleshooting",children:"Troubleshooting"})}),(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"#updates",children:"Updates"})})]})]}),(0,a.jsx)("h2",{id:"install",children:"1. Installing and first launch"}),(0,a.jsxs)("p",{children:["Message Exporter is distributed through the"," ",(0,a.jsx)("a",{href:rc,rel:"noopener",children:"Mac App Store"}),", so it is signed, notarised, and updated by macOS. It requires macOS 14 Sonoma or later and runs natively on Apple silicon and Intel Macs."]}),(0,a.jsx)("p",{children:"Launch it from Applications or Spotlight. On first launch you'll be asked to sign in â with Apple, Google, or an email address. An account is how a Pro subscription follows you to a second Mac; it is not used for anything to do with your messages."}),(0,a.jsx)("h2",{id:"access",children:"2. Granting Messages access"}),(0,a.jsxs)("p",{children:["Message Exporter is a sandboxed app, which means macOS blocks it from reading your Messages database until you explicitly point it there. The app explains what it needs, then opens the standard macOS open panel at your Messages folder â usually"," ",(0,a.jsx)("kbd",{children:"~/Library/Messages"}),"."]}),(0,a.jsxs)("p",{children:["The panel is titled ",(0,a.jsx)("strong",{children:"Messages Access"}),"and reads âClick Allow to grant access. Nothing leaves your Mac.â The folder is already selected, so you do not need to navigate anywhere â just click ",(0,a.jsx)("strong",{children:"Allow"}),". If you land in a different folder, press ",(0,a.jsx)("kbd",{children:"â§âG"}),", type"," ",(0,a.jsx)("kbd",{children:"~/Library/Messages"}),", press Return, then click Allow."]}),(0,a.jsx)("div",{className:"me-note",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"You do not need Full Disk Access."})," If another Messages tool has asked you to enable it in System Settings, Message Exporter does not â the folder you pick in the open panel is the only thing it can read. macOS remembers the choice, and you can revoke it by moving the app to the Trash or by resetting its sandbox permissions."]})}),(0,a.jsx)("h2",{id:"contacts",children:"3. Contacts access (optional)"}),(0,a.jsx)("p",{children:"macOS stores message threads against phone numbers and email addresses, not names. If you allow access to Contacts, Message Exporter can show âAlex Johnsonâ instead of â+1 555 0142â. This is entirely optional:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Open Privacy Settings"})," â take you to the macOS pane where you can allow it."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Continue with Numbers"})," â carry on without names."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Don't Ask Again"})," â dismiss the prompt for good."]})]}),(0,a.jsx)("p",{children:"Contact names are used only for display and inside the files you export. They are never transmitted."}),(0,a.jsx)("h2",{id:"window",children:"4. The main window"}),(0,a.jsx)("div",{className:"me-doc-shot",children:(0,a.jsx)(ru,{src:`${rd}/ui-browse.png`,alt:"The Message Exporter main window: contacts sidebar on the left, selected conversation on the right, toolbar with Source picker, Media and Export.",title:"Message Exporter"})}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Sidebar"}),"â every conversation Message Exporter can see, with each one's message count and service (iMessage or SMS). The search f
1933ield at the top filters this list."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Add iPhone Backupâ¦"})," â at the bottom of the sidebar, for adding a local encrypted backup as an extra source."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Source picker"})," â in the toolbar, once you have more than one source. Switches between your Mac and any backup you added."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Media"})," â opens Manage Media for the selected conversation."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Export"})," â opens the export sheet for the selected conversation."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Upgrade to Pro"}),"â shown until you subscribe; replaced by a âYou're on Proâ indicator afterwards."]})]}),(0,a.jsxs)("p",{children:["If you close the window, reopen it from the Window menu or with ",(0,a.jsx)("kbd",{children:"â0"}),"."]}),(0,a.jsx)("h2",{id:"browse",children:"5. Browsing and searching"}),(0,a.jsx)("p",{children:"Select any contact or group in the sidebar to read the thread on the right, with sent and received messages in bubbles and dates in order. This is a preview â the app never modifies your Messages data."}
1933),(0,a.jsxs)("p",{children:["The search field matches both contact names and the text inside messages, so searching for ",(0,a.jsx)("em",{children:"invoice"}),"will surface the conversations where that word was used even if you can't remember who you were talking to."]}),(0,a.jsx)("h2",{id:"export",children:"6. Exporting a conversation"}),(0,a.jsxs)("p",{children:["Select a conversation, click ",(0,a.jsx)("strong",{children:"Export"}),", and the export sheet opens with four controls."]}),(0,a.jsx)("div",{className:"me-doc-shot",children:(0,a.jsx)(ru,{src:`${rd}/ui-export.png`,alt:"The Export Options sheet showing Time Range, Format and Attachments controls and a count of messages to be exported.",title:"Export Options"})}),(0,a.jsxs)("ol",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Time Range"})," â the window of history to include. The free plan is limited to the last 3 days; Pro adds 7 days, 30 days, all time, and custom ranges."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Format"})," â PDF, HTML, CSV, Excel, or plain text. Plain text is available on the free plan; the rest are Pro."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Attachments"})," â off by default. Turn it on to bundle the shared files with the transcript in a ZIP. Choose ",(0,a.jsx)("em",{children:"all files in range"}),", or click"," ",(0,a.jsx)("strong",{children:"Chooseâ¦"})," to hand-pick."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"The count"})," â the sheet tells you how many messages fall in the range before you click Export, so an empty range never surprises you."]})]}),(0,a.jsxs)("p",{children:["Click ",(0,a.jsx)("strong",{children:"Export"}),", choose where to save, and a progress bar appears â PDFs render page by page, attachments are copied and zipped. Large exports can be cancelled while they run."]}),(0,a.jsx)("h2",{id:"formats",children:"7. Which format to choose"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"PDF"})," â the one to pick for filing, printing, or handing to someone else. Paginated and readable, with the conversation title at the top."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"HTML"})," â keeps the chat-bubble look and opens in any browser. Good for sharing a readable copy without a PDF viewer."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"CSV"})," â one row per message with date, direction, message text, and attachment names. The right choice for filtering or importing elsewhere."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Excel (.xlsx)"})," â the same columns as CSV, as a real workbook, for sorting and pivoting in Numbers or Excel."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Plain text"})," â the smallest, most portable option, and the format the free plan uses."]})]}),(0,a.jsx)("h2",{id:"media",children:"8. Manage Media"}),(0,a.jsxs)("p",{children:["Attachments in Messages live in a folder of randomly named subdirectories, which makes recovering a photo someone sent you last spring genuinely painful."," ",(0,a.jsx)("strong",{children:"Media"})," in the toolbar opens a browser for everything shared in the selected conversation."]}),(0,a.jsx)("div",{className:"me-doc-shot",children:(0,a.jsx)(ru,{src:`${rd}/ui-media.png`,alt:"The Manage Media sheet with a grid of shared photos, videos, PDFs and files, and Select All / Clear controls.",title:"Manage Media"})}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Filter by the same time ranges as the export sheet."}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Select All"})," or ",(0,a.jsx)("strong",{children:"Clear"}),", or click individual files. The header keeps a running count of files shown and files selected."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Export Selected"})," saves the chosen files. Opened from inside the export sheet instead, the button reads ",(0,a.jsx)("strong",{children:"Use Selection"})," and hands your picks back to the export."]}
1933)]}),(0,a.jsx)("p",{children:"The free plan shows and exports media from the last 3 days; Pro covers the full history."}),(0,a.jsx)("h2",{id:"backups",children:"9. Importing an iPhone backup"}),(0,a.jsxs)("p",{children:["Conversations that never synced to your Mac can still be read from a local iPhone backup. Make the backup first, in Finder: connect the iPhone, select it in the sidebar, tick ",(0,a.jsx)("em",{children:"Encrypt local backup"}),", set a password, and back up."]}),(0,a.jsx)("p",{children:"Then in Message Exporter:"}),(0,a.jsxs)("ol",{children:[(0,a.jsxs)("li",{children:["Click ",(0,a.jsx)("strong",{children:"Add iPhone Backupâ¦"})," at the bottom of the sidebar."]}),(0,a.jsxs)("li",{children:["Choose the backup folder â normally inside"," ",(0,a.jsx)("kbd",{children:"~/Library/Application Support/MobileSync/Backup/"}),"."]}),(0,a.jsx)("li",{children:"Enter the backup password if it's encrypted."}),(0,a.jsxs)("li",{children:["Use the ",(0,a.jsx)("strong",{children:"Source"})," picker in the toolbar to switch between your Mac and the backup."]})]}),(0,a.jsx)("div",{className:"me-doc-shot",children:(0,a.jsx)(ru,{src:`${rd}/ui-backups.png`,alt:"Message Exporter with an iPhone backup added as a source and selected in the toolbar Source picker.",title:"iPhone Backups"})}),(0,a.jsx)("div",{className:"me-note",children:(0,a.jsx)("p",{children:"Decryption happens on your Mac. The backup password is used locally to open the backup and is never transmitted. Backups made to iCloud rather than to this Mac cannot be read â Message Exporter only reads backups stored on your own disk."})}),(0,a.jsx)("h2",{id:"account",children:"10. Accounts and Pro"}),(0,a.jsx)("p",{children:"Sign in with Apple, Google, or email. The account exists so that a Pro subscription unlocks the app on any Mac you sign in to; it holds your email address, optionally your name, and whether your plan is active."}),(0,a.jsxs)("p",{children:["Pro is ",(0,a.jsx)("strong",{children:"$4.99 per month"}),", purchased through the App Store and billed to your Apple Account. It renews monthly until cancelled. To cancel, open System Settings â Apple Account â Subscriptions; you keep Pro features until the end of the period already paid for."]}),(0,a.jsxs)("p",{children:["If you have paid but the app still shows the free tier, open the upgrade sheet and choose ",(0,a.jsx)("strong",{children:"Restore Purchases"}),", making sure you're signed in to the same Apple Account you bought with."]}),(0,a.jsx)("h2",{id:"free",children:"11. What the free plan includes"}),(0,a.jsx)("p",{children:"The free plan is deliberately usable on real data rather than a locked demo. You get:"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Browsing, searching, and reading every conversation, with no limit"}),(0,a.jsx)("li",{children:"Export of the last 3 days of any thread, as plain text"}),(0,a.jsx)("li",{children:"Shared photos, videos, PDFs and files from those same 3 days"}),(0,a.jsx)("li",{children:"Adding and browsing local iPhone backups"})]}),(0,a.jsx)("p",{children:"Pro adds the longer time ranges, the four other formats, and full attachment export."}),(0,a.jsx)("h2",{id:"troubleshooting",children:"12. Troubleshooting"}),(0,a.jsx)("h3",{children:"No conversations appear"}),(0,a.jsxs)("p",{children:["Access to the Messages folder probably wasn't granted, or the wrong folder was chosen in the open panel. Quit and relaunch the app and pick"," ",(0,a.jsx)("kbd",{children:"~/Library/Messages"})," â in the open panel you can press"," ",(0,a.jsx)("kbd",{children:"ââ§G"})," and type the path. Also confirm Messages itself is signed in and has history on this Mac."]}),(0,a.jsx)("h3",{children:"Contacts show as phone numbers"}),(0,a.jsx)("p",{children:"Contacts access is off. Allow Message Exporter in System Settings â Privacy & Security â Contacts, then relaunch. Numbers with no matching contact card stay as numbers."}),(0,a.jsx)("h3",{children:"A shared file is missing from Manage Media"}),(0,a.jsx)("p",{children:"Attachments that were never downloaded to this Mac â for example, media in a conversation your Mac only partially synced â have no file on disk to collect. Open the conversation in Messages, let the attachment download, then re-scan. Also check the time-range filter; on the free plan it is limited to the last 3 days."}),(0,a.jsx)("h3",{children:"The backup won't open"}),(0,a.jsxs)("p",{children:["Confirm it is a backup made ",(0,a.jsx)("em",{children:"to this Mac"}
1933)," and not to iCloud, that you picked the backup folder itself (the one named with a long identifier), and that the password matches the one set when encryption was enabled. A backup made on a different Mac and copied over works, as long as you know its password."]}),(0,a.jsx)("h3",{children:"A PDF export is taking a long time"}),(0,a.jsx)("p",{children:"PDF rendering is per-page, so a multi-year thread with thousands of messages takes a while. The progress sheet shows the page count and can be cancelled. If you only need the data, CSV or Excel finishes far faster."}),(0,a.jsx)("h2",{id:"updates",children:"13. Updates"}),(0,a.jsxs)("p",{children:["Updates arrive through the Mac App Store. If the app hasn't updated itself, open the App Store, go to ",(0,a.jsx)("strong",{children:"Updates"}),", and press ",(0,a.jsx)("kbd",{children:"âR"})," to refresh the list â the store caches for a while after a new version goes live."]}),(0,a.jsxs)("p",{children:["Still stuck? Email"," ",(0,a.jsx)("a",{href:`mailto:${rl}?subject=Message%20Exporter%20help`,children:rl})," ","or read the ",(0,a.jsx)("a",{href:"/messageexporter/support",children:"support page"}),"."]})]})}),(0,a.jsx)(rm,{})]})]})}function rv(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:rp}}),(0,a.jsxs)("div",{className:"me-wrap",children:[(0,a.jsx)(rh,{}),(0,a.jsx)("div",{className:"me-doc",children:(0,a.jsxs)("div",{className:"me-doc-inner",children:[(0,a.jsx)("h1",{children:"Message Exporter â Support"}),(0,a.jsx)("p",{className:"me-updated",children:"Last updated: July 2026 · Message Exporter for macOS"}),(0,a.jsxs)("div",{className:"me-box",children:[(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"Ask us anything:"})," ",(0,a.jsx)("a",{href:`mailto:${rl}?subject=Message%20Exporter%20support`,children:rl})," ","â or use the ",(0,a.jsx)("a",{href:ro,children:"contact form"}),". We answer within one business day (MonâFri, U.S. Pacific Time)."]}),(0,a.jsxs)("p",{children:["Looking for step-by-step instructions instead? The"," ",(0,a.jsx)("a",{href:"/messageexporter/guide",children:"user guide"})," covers every screen in the app."]})]}),(0,a.jsx)("h2",{children:"What to include"}),(0,a.jsx)("p",{children:"None of this is required, but it usually saves a round trip:"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Your macOS version (Apple menu â About This Mac)"}),(0,a.jsx)("li",{children:"The app version (Message Exporter â About Message Exporter)"}),(0,a.jsx)("li",{children:"Whether you signed in with Apple, Google, or email"}),(0,a.jsx)("li",{children:"Whether the conversation came from your Mac or an iPhone backup"}),(0,a.jsx)("li",{children:"What you did, and what happened instead"})]}),(0,a.jsx)("p",{children:"Please don't attach a message transcript or a conversation screenshot with real content in it. Message Exporter works entirely on your Mac and we never receive your messages â we'd rather keep it that way. A description, or a screenshot with the content covered, is enough."}),(0,a.jsx)("h2",{children:"Common questions"}),(0,a.jsx)("h2",{id:"no-messages",children:"The app doesn't show any conversations"}),(0,a.jsxs)("p",{children:["Message Exporter is sandboxed and can only read the folder you point it at. Quit and relaunch, and when the open panel appears choose"," ",(0,a.jsx)("kbd",{children:"~/Library/Messages"})," â press ",(0,a.jsx)("kbd",{children:"ââ§G"}),"in the panel and paste that path if you can't see the Library folder. You do ",(0,a.jsx)("em",{children:"not"})," need to grant Full Disk Access."]}),(0,a.jsx)("h2",{id:"names",children:"Everyone shows up as a phone number"}),(0,a.jsx)("p",{children:"That's Contacts access being off â macOS stores threads against numbers, not names. Allow Message Exporter in System Settings â Privacy & Security â Contacts and relaunch. The app works fine without it; you'll just see numbers."}),(0,a.jsx)("h2",{id:"restore-purchases",children:"I paid, but the app still shows the free plan"}),(0,a.jsxs)("p",{children:["Open the upgrade sheet and choose ",(0,a.jsx)("strong",{children:"Restore Purchases"}),". Make sure you're signed in to the same Apple Account you purchased with, and to the same Message Exporter account. If it still doesn't unlock, email us the Apple receipt and we'll sort it out."]}),(0,a.jsx)("h2",{id:"cancel",children:"How do I cancel my subscription?"}),(0,a.jsxs)("p",{children:["Pro is billed by Apple, so cancelling happens there: System Settings â Apple Account â Subscriptions â Message Exporter â Cancel Subscription. You keep Pro features until the end of the peri
1933od you already paid for. Refunds are issued by Apple under its own policy, at"," ",(0,a.jsx)("a",{href:"https://reportaproblem.apple.com",rel:"noopener nofollow",children:"reportaproblem.apple.com"}),"."]}),(0,a.jsx)("h2",{id:"delete-account",children:"Delete my account and data"}),(0,a.jsxs)("p",{children:["Email"," ",(0,a.jsx)("a",{href:`mailto:${rl}?subject=Delete%20Message%20Exporter%20account`,children:rl})," ","from the address on the account with the subject âDelete Message Exporter accountâ, and we delete the account record and its subscription status within 30 days."]}),(0,a.jsxs)("p",{children:["Two things it does ",(0,a.jsx)("em",{children:"not"})," do. It does not cancel your subscription â cancel that with Apple separately, or you will keep being billed. And it does not touch anything on your Mac: your messages and any files you exported are yours and were never ours to delete."]}),(0,a.jsx)("h2",{id:"media-missing",children:"A shared photo or file isn't listed"}),(0,a.jsx)("p",{children:"Manage Media can only collect attachments that exist on this Mac. If an attachment was never downloaded â common in threads your Mac synced only partially â open the conversation in Messages, let it download, and scan again. Also check the time-range filter: on the free plan media is limited to the last 3 days."}),(0,a.jsx)("h2",{id:"backup",children:"My iPhone backup won't load"}),(0,a.jsxs)("p",{children:["It must be a backup stored on this Mac (Finder â your iPhone â Back up now), not an iCloud backup. Select the backup folder itself â the one named with a long identifier inside"," ",(0,a.jsx)("kbd",{children:"~/Library/Application Support/MobileSync/Backup/"})," â and enter the password you set when you enabled ",(0,a.jsx)("em",{children:"Encrypt local backup"}),". Without that password an encrypted backup cannot be opened by anyone, including us."]}),(0,a.jsx)("h2",{id:"free-limits",children:"What exactly is limited on the free plan?"}),(0,a.jsxs)("p",{children:["Browsing, searching, and reading are unlimited. Exporting is limited to the last 3 days, in plain text, plus the shared photos, videos, PDFs and files from those same 3 days. Pro unlocks every time range, PDF/HTML/CSV/Excel, and full attachment export. See ",(0,a.jsx)("a",{href:"/messageexporter",children:"the plan comparison"}),"."]}),(0,a.jsx)("h2",{id:"update",children:"I'm not getting the latest version"}),(0,a.jsxs)("p",{children:["Open the App Store, click ",(0,a.jsx)("strong",{children:"Updates"}),", and press ",(0,a.jsx)("kbd",{children:"âR"})," to force a refresh â the store caches its update list for a while after a release. An"," ",(0,a.jsx)("em",{children:"Open"}),"button next to the app in search results doesn't mean you're up to date; only the Updates tab does."]}),(0,a.jsx)("h2",{id:"privacy",children:"Where do my messages go?"}),(0,a.jsxs)("p",{children:["Nowhere. Reading, previewing, and exporting all happen on your Mac; there is no upload endpoint in the app. The only thing our servers hold is your account and whether it has an active subscription. See the"," ",(0,a.jsx)("a",{href:"/messageexporter/privacy",children:"Privacy Policy"}),"."]}),(0,a.jsx)("h2",{id:"bug",children:"Reporting a bug"}),(0,a.jsx)("p",{children:"Tell us the steps that trigger it, what you expected, and what happened, plus your macOS and app version. If it involves a specific conversation, a description of it (group or one-to-one, roughly how many messages, whether it has attachments) helps more than the transcript itself, which we don't want."}),(0,a.jsx)("h2",{children:"Legal"}),(0,a.jsxs)("p",{children:[(0,a.jsx)("a",{href:"/messageexporter/privacy",children:"Privacy Policy"})," ·"," ",(0,a.jsx)("a",{href:"/messageexporter/terms",children:"Terms of Use"}),". Message Exporter is published by Soft Flourish, California, USA."]})]})}),(0,a.jsx)(rm,{})]})]})}function rw(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:rp}}),(0,a.jsxs)("div",{className:"me-wrap",children:[(0,a.jsx)(rh,{}),(0,a.jsx)("div",{className:"me-doc",children:(0,a.jsxs)("div",{className:"me-doc-inner",children:[(0,a.jsx)("h1",{children:"Privacy Policy"}),(0,a.jsx)("p",{className:"me-updated",children:"Last updated: July 2026 · Applies to: Message Exporter for macOS"}),(0,a.jsx)("div",{className:"me-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"The short version:"}),"your messages, attachments, and iPhone backups never leave your Mac. Message Exporter has no upload endpoint for them. Reading, previewing, and exporting all happen locally. The only data we hold is what's needed to run an account and a subscription â an email address and a plan status."]})}),(0,a.jsx)("h2",{children:"Who we are"}),(0,a.jsxs)("p",{children:["Message Exporter is developed and published by Soft Flourish, an independent software studio and publishing house based in California, USA. Questions: ",(0,a.jsx)("a",{href:ro,children:"contact us"})," or"," ",(0,a.jsx)("a",{href:`mailto:${rl}`,children:rl}),"."]}),(0,a.jsx)("h2",{children:"Your messages and files"}),(0,a.jsx)("p",{children:"We never receive, store, transmit, or process your message content, your attachments, your contact list, or the contents of an iPhone backup. All of that work â reading the local Messages database, decrypting a local backup, rendering a PDF, copying attachments, building a ZIP â happens entirely on your Mac. There is no server-side message pipeline in Message Exporter."}),(0,a.jsx)("h2",{children:"Access permissions"}),(0,a.jsx)("p",{children:"The app is sandboxed. It cannot read your Messages folder until you select it yourself in the standard macOS open panel, and macOS â not us â controls that grant. Contacts access is optional and used only to display names instead of phone numbers; decline it and the app still works. Full Disk Access is never requested."}),(0,a.jsx)("h2",{children:"iPhone backup passwords"}),(0,a.jsx)("p",{children:"If you open an encrypted local iPhone backup, the password you enter is used on your Mac to decrypt it and is never transmitted to us or to anyone else."}),(0,a.jsx)("h2",{children:"What we collect"}),(0,a.jsx)("p",{children:"Only in connection with your account and subscription:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Email address"})," â to identify your account and let you sign in on another Mac."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Name"}),"â only if you sign in with Apple or Google and choose to share it. Apple's Hide My Email is fully supported."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"A user identifier"})," â an anonymous ID that links your account to your subscription."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Subscription status"}),"â which plan you're on and whether it is active, so the app can unlock the right features."]})]}),(0,a.jsx)("h2",{children:"What we never collect"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Message text, message metadata, or conversation history"}),(0,a.jsx)("li",{children:"Attachments, photos, videos, or documents shared in a conversation"}),(0,a.jsx)("li",{children:"Your contacts, phone numbers, or the addresses you message"}),(0,a.jsx)("li",{children:"iPhone backup contents or backup passwords"}),(0,a.jsx)("li",{children:"The files you export, or where you saved them"}),(0,a.jsx)("li",{children:"Advertising identifiers or cross-app tracking data"})]}),(0,a.jsx)("h2",{children:"Network requests the app makes"}),(0,a.jsx)("p",{children:"For transparency, these are the only things Message Exporter talks to, and none of them carry message data:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Firebase Authentication (Google)"})," â signing in and keeping your session valid."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"RevenueCat"})," â checking whether your subscription is active."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Apple's App Store"})," â purchasing and restoring the subscription."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"A Soft Flourish configuration endpoint"})," â a small public file telling the app the minimum supported version, so a broken release can be retired. The request sends no personal data."]})]}),(0,a.jsx)("h2",{children:"Payments"}),(0,a.jsx)("p",{children:"Subscriptions are processed by Apple through the App Store. We never see or store your card details. We use RevenueCat as our subscription-management provider to tell the app whether your plan is active."}),(0,a.jsx)("h2",{children:"Service providers"}),(0,a.jsx)("p",{children:"We use a small number of processors, strictly to run accounts and subscriptions:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Google Firebase Authentication"})," â account sign-in and identity."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"RevenueCat"})," â subscription and entitlement status."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Google Cloud"}),"â hosts the small service that tells your app which plan you're on."]})]}),(0,a.jsx)("p",{children:"None of these receive your messages, attachments, or contacts."}),(0,a.jsx)("h2",{children:"Analytics and advertising"}
1933),(0,a.jsx)("p",{children:"Message Exporter contains no advertising, no third-party analytics SDKs, and no tracking. We do not sell or share personal information, and we do not use your data for cross-context behavioral advertising."}),(0,a.jsx)("h2",{children:"Data retention and deletion"}),(0,a.jsxs)("p",{children:["We keep your account record for as long as your account exists. You can ask us to delete your account and associated data at any time via"," ",(0,a.jsx)("a",{href:ro,children:"our contact form"})," or by emailing"," ",(0,a.jsx)("a",{href:`mailto:${rl}?subject=Delete%20Message%20Exporter%20account`,children:rl}),", and we will remove it within 30 days. Deleting the app alone does not delete your account, and it does not cancel an Apple subscription â cancel that in System Settings â Apple Account â Subscriptions."]}),(0,a.jsx)("h2",{children:"Your rights"}),(0,a.jsxs)("p",{children:["Depending on where you live (including under the GDPR and the CCPA/CPRA), you may have the right to access, correct, export, or delete the personal information we hold, and to withdraw consent. Because we hold so little, these requests are usually simple â just ",(0,a.jsx)("a",{href:ro,children:"contact us"}),"."]}),(0,a.jsx)("h2",{children:"Children"}),(0,a.jsx)("p",{children:"Message Exporter is a general-purpose productivity tool, is not directed to children under 13, and we do not knowingly collect personal information from them."}),(0,a.jsx)("h2",{children:"International transfers"}),(0,a.jsx)("p",{children:"Our account and subscription infrastructure is operated in the United States. Because your messages never leave your Mac, no message data is transferred anywhere."}),(0,a.jsx)("h2",{children:"Changes to this policy"}),(0,a.jsx)("p",{children:"If we make a material change, we will update this page and revise the âlast updatedâ date above. Continued use after a change means you accept the updated policy."}),(0,a.jsx)("h2",{children:"Contact"}),(0,a.jsxs)("p",{children:["Questions about this policy or your data: ",(0,a.jsx)("a",{href:ro,children:"contact us"})," or email ",(0,a.jsx)("a",{href:`mailto:${rl}`,children:rl}),". Publisher: Soft Flourish, California, USA."]})]})}),(0,a.jsx)(rm,{})]})]})}function rj(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:rp}}),(0,a.jsxs)("div",{className:"me-wrap",children:[(0,a.jsx)(rh,{}),(0,a.jsx)("div",{className:"me-doc",children:(0,a.jsxs)("div",{className:"me-doc-inner",children:[(0,a.jsx)("h1",{children:"Terms of Use"}),(0,a.jsx)("p",{className:"me-updated",children:"Last updated: July 2026 · Applies to: Message Exporter for macOS"}),(0,a.jsx)("div",{className:"me-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"The short version:"})," Message Exporter is licensed to you, not sold. Pro is an auto-renewing monthly subscription you can cancel any time through Apple. Your messages stay on your Mac â which also means keeping your own backups, and having the right to export the conversations you export, is your responsibility."]})}),(0,a.jsx)("h2",{children:"1. Agreement"}),(0,a.jsx)("p",{children:"These Terms govern your use of the Message Exporter application and this website, provided by Soft Flourish (âweâ, âusâ). By installing or using the app, you agree to these Terms. If you don't agree, please don't use the app."}),(0,a.jsx)("h2",{children:"2. Licence"}),(0,a.jsx)("p",{children:"We grant you a personal, non-exclusive, non-transferable licence to install and use Message Exporter on Macs you own or control, for personal or internal business use. You may not resell, redistribute, reverse-engineer, or attempt to circumvent the app's licensing or plan restrictions, except where such restriction is prohibited by law. Your use is also subject to Apple's standard End User Licence Agreement for App Store applications."}),(0,a.jsx)("h2",{children:"3. Plans and the free tier"}),(0,a.jsxs)("p",{children:["Message Exporter offers a free tier and a paid ",(0,a.jsx)("strong",{children:"Pro"})," plan. The free tier includes unlimited browsing, searching, and reading, plus export of the last 3 days of a conversation as plain text and the shared photos, videos, PDFs and files from those same 3 days. Pro unlocks all time ranges, the PDF, HTML, CSV and Excel formats, and full attachment export, as described on the"," ",(0,a.jsx)("a",{href:"/messageexporter",children:"product page"}),"."]}),(0,a.jsx)("h2",{children:"4. Subscription, billing, and cancellation"}),(0,a.jsxs)("p",{children:["Pro is an ",(0,a.jsx)("strong",{children:"auto-renewing subscription"})," billed at $4.99 per month (or the equivalent in your local currency, shown before purchase). Billing is handled by Apple through the App Store and charged to your Ap
1933ple Account. It renews automatically at the end of each period unless you cancel at least 24 hours before the period ends."]}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Manage or cancel in System Settings â Apple Account â Subscriptions."}),(0,a.jsx)("li",{children:"Cancelling stops future charges; you keep Pro features until the end of the current period."}),(0,a.jsx)("li",{children:"Prices may change; we will tell you before a change affects you."})]}),(0,a.jsx)("h2",{children:"5. Refunds"}),(0,a.jsxs)("p",{children:["Purchases are processed by Apple, so refunds are handled by Apple under its own policy â request one at"," ",(0,a.jsx)("a",{href:"https://reportaproblem.apple.com",rel:"noopener nofollow",children:"reportaproblem.apple.com"}),". If a refund is granted, the corresponding Pro features stop being available."]}),(0,a.jsx)("h2",{children:"6. Acceptable use"}),(0,a.jsx)("p",{children:"You are responsible for the conversations you export and must have the right to do so. Don't use Message Exporter to access messages on a device you do not own or are not authorised to use, to violate someone's privacy or a confidentiality obligation, to harass or surveil another person, or to break applicable law â including laws on recording, retaining, or disclosing communications, which vary by jurisdiction."}),(0,a.jsx)("h2",{children:"7. Your data and backups"}),(0,a.jsx)("p",{children:"Because Message Exporter works locally, we never hold a copy of your messages or exports â so we cannot recover them for you. Keep your own backups of anything important, and check an export before relying on it. The app reads your Messages data and does not modify or delete it, but you remain responsible for your own files."}),(0,a.jsx)("h2",{children:"8. Availability of features"}
1933),(0,a.jsx)("p",{children:"Some capabilities depend on your Mac and its configuration. Reading messages requires that you grant access to your Messages folder; contact names require Contacts access; and iPhone import requires an encrypted backup stored on that Mac and its password. Where something isn't available, the app will tell you plainly rather than substituting a cloud service."}),(0,a.jsx)("h2",{children:"9. Intellectual property"}),(0,a.jsx)("p",{children:"Message Exporter, its name, icon, and design are owned by us. These Terms don't transfer any of our intellectual property to you. You retain all rights to your own messages and to the files you export."}),(0,a.jsx)("h2",{children:"10. Disclaimer and limitation of liability"}),(0,a.jsx)("p",{children:"The app is provided âas isâ without warranties of any kind to the fullest extent permitted by law. We don't warrant that it will be uninterrupted or error-free, or that any export will be complete, accurate, or fit for a particular purpose â including any legal or evidentiary purpose. To the maximum extent permitted by law, our total liability arising out of or relating to the app is limited to the amount you paid us in the twelve months before the claim."}),(0,a.jsx)("h2",{children:"11. Changes"}),(0,a.jsx)("p",{children:"We may update these Terms or the app. Material changes will be reflected here with a new âlast updatedâ date. Continued use after a change means you accept the updated Terms."}),(0,a.jsx)("h2",{children:"12. Termination"}),(0,a.jsx)("p",{children:"You may stop using the app at any time. We may suspend or terminate access if these Terms are breached. Sections that by their nature should survive termination will do so."}),(0,a.jsx)("h2",{children:"13. Governing law"}),(0,a.jsx)("p",{children:"These Terms are governed by the laws of the State of California, USA, without regard to conflict-of-laws rules, and the courts located in California will have jurisdiction â except where mandatory local consumer law grants you other rights."}),(0,a.jsx)("h2",{children:"14. Contact"}),(0,a.jsxs)("p",{children:["Questions about these Terms: ",(0,a.jsx)("a",{href:ro,children:"contact us"})," or email"," ",(0,a.jsx)("a",{href:`mailto:${rl}`,children:rl}),". Publisher: Soft Flourish, California, USA. See also our"," ",(0,a.jsx)("a",{href:"/messageexporter/privacy",children:"Privacy Policy"}),"."]})]})}),(0,a.jsx)(rm,{})]})]})}var ep=ed,eh=ed;let rk="#C8102E",rN={pro_monthly:{title:"Pro â Monthly",blurb:"100,000 credits / month â 8 hours of meetings",group:"sub"},pro_annual:{title:"Pro â Annual",blurb:"100,000 credits / month â 8 hours of meetings · billed yearly (save 25%)",group:"sub"},max_monthly:{title:"Max â Monthly",blurb:"450,000 credits / month â 35 hours of meetings",group:"sub"},max_annual:{title:"Max â Annual",blurb:"450,000 credits / month â 35 hours of meetings · billed yearly (save 33%)",group:"sub"},credits_25k:{title:"25,000 credits",blurb:"â 2 hours of meetings · one-time, stacks on your balance",group:"pack"},credits_50k:{title:"50,000 credits",blurb:"â 4 hours of meetings · one-time, stacks on your balance",group:"pack"},credits_100k:{title:"100,000 credits",blurb:"â 8 hours of meetings · one-time, stacks on your balance",group:"pack"}};function rS(){let e=(0,ei.getAuth)((0,eu.getFirebaseApp)());return e.tenantId="meetpilot-yhdzh",e}function rA(){var t;let[s,n]=(0,r.useState)(null),[i,o]=(0,r.useState)(!1),[l,c]=(0,r.useState)(null),[d,p]=(0,r.useState)(!1),[h,m]=(0,r.useState)(""),[u,f]=(0,r.useState)(""),[g,x]=(0,r.useState)(!1),[b,y]=(0,r.useState)([]),[v,w]=(0,r.useState)(!1),[j,k]=(0,r.useState)(null),[N,S]=(0,r.useState)(null),[A,P]=(0,r.useState)(!1),[z,T]=(0,r.useState)(!1),[F,C]=(0,r.useState)(null);(0,r.useEffect)(()=>{c(new URLSearchParams(window.location.search).get("uid"));let e=(0,eo.onAuthStateChanged)(rS(),e=>{n(e),o(!0)});return()=>e()},[]);let M=(0,r.useCallback)(async a=>{try{let{Purchases:t}=await e.A(323738),s=t.isConfigured()?t.getSharedInstance():t.configure({apiKey:"rcb_JBCTeGNfXRyYufjsDhPcqMCNfHFB",appUserId:a});t.isConfigured()&&await s.changeUser(a),k(await s.getCustomerInfo());let r=await s.getOfferings(),n=r.all["com.fenul.meetpilot.default"]??r.current;y(n?.availablePackages??[]),w(!0)}catch(e){C(e.message)}},[]),I=(0,r.useCallback)(async e=>{try{let a=await e.getIdToken(),t=await fetch("https://meeting-assistant-793989633230.us-central1.run.app/credits",{headers:{Authorization:`Bearer ${a}`}});if(!t.ok)throw Error(`Account status failed (HTTP ${t.status})`);S(await t.json())}catch(e){C(e.message)}},[]);(0,r.useEffect)(()=>{s?(M(s.uid),I(s)):d&&l&&M(l)}
1933,[s,d,l,M,I]);let D=e=>{T(!0),C(null),e().catch(e=>C(e.message)).finally(()=>T(!1))},W=null!==s||d&&null!==l,O=null!==s&&null!==l&&s.uid!==l,E=N?"pro"===(t=N.tier)?"Pro":"max"===t?"Max":"Free":j?.entitlements.active.meetwing_max?"Max":j?.entitlements.active.meetwing_pro?"Pro":"Free",R=b.filter(e=>rN[e.identifier]?.group!=="pack"),$=b.filter(e=>rN[e.identifier]?.group==="pack"),_=t=>{let r=rN[t.identifier],n=t.webBillingProduct?.currentPrice?.formattedPrice,i=function(e){let a=e.webBillingProduct?.freeTrialPhase?.period;if(!a)return null;let t=a.number;return`First ${t} ${a.unit}${1===t?"":"s"} free`}(t);return(0,a.jsxs)("button",{disabled:z,onClick:()=>D(async()=>{let{Purchases:a}=await e.A(323738);k((await a.getSharedInstance().purchase({rcPackage:t})).customerInfo),P(!0),s&&await I(s)}),style:rT,children:[(0,a.jsxs)("span",{style:{display:"block",fontWeight:700},children:[r?.title??t.webBillingProduct?.displayName??t.identifier,n?(0,a.jsxs)("span",{style:{color:rk},children:[" · ",n]}):null]}),i?(0,a.jsx)("span",{style:{display:"block",fontSize:12,fontWeight:700,color:rk,marginTop:4},children:i}):null,r?(0,a.jsx)("span",{style:{display:"block",fontSize:13,color:"#6B7280",marginTop:4},children:r.blurb}):null]},t.identifier)};return(0,a.jsxs)("main",{style:{maxWidth:620,margin:"0 auto",padding:"48px 20px",color:"#141820",background:"#fff",minHeight:"100svh"},children:[(0,a.jsx)("p",{style:{marginBottom:24},children:(0,a.jsx)("a",{href:"/meetwing",style:{color:"#6B7280",textDecoration:"none",fontSize:14},children:"â MeetWing"})}),(0,a.jsxs)("h1",{style:{fontSize:28,fontWeight:800},children:["MeetWing ",(0,a.jsx)("span",{style:{color:rk},children:"account"})]}),(0,a.jsx)("p",{style:{color:"#5c6478",marginTop:8,lineHeight:1.6},children:"Manage your MeetWing plan and credits. Buy here, then keep using the app â purchases appear in the app automatically. Credits meter your live meeting time: about 210 credits per minute of transcription and AI answers."}),i?W?(0,a.jsxs)("section",{style:{marginTop:24,display:"grid",gap:12},children:[(0,a.jsx)("div",{style:{padding:14,borderRadius:10,background:"#F8F9FB",border:"1px solid #E5E7EB"},children:s?(0,a.jsxs)(a.Fragment,{children:["Signed in as ",(0,a.jsx)("strong",{children:s.email??s.uid})," â current plan: ",(0,a.jsx)("strong",{children:E}),N?(0,a.jsxs)("span",{children:[" · ",N.unlimited?"Unlimited credits":`${N.credits.toLocaleString()} credits`]}):null]}):(0,a.jsxs)(a.Fragment,{children:["Continuing as ",(0,a.jsx)("strong",{children:"guest"}),"â purchases credit this device's app session (",(0,a.jsx)("code",{style:{fontSize:12},children:l}),")."]})}),O?(0,a.jsxs)("div",{style:{padding:14,borderRadius:10,background:"rgba(200,16,46,0.06)",border:"1px solid rgba(200,16,46,0.35)",fontSize:14,lineHeight:1.6},children:[(0,a.jsx)("strong",{style:{color:rk},children:"Heads up:"})," you arrived from the app with a different session than the account you just signed in with. Purchases credit the",(0,a.jsx)("strong",{children:" signed-in account"}),"â to use them, sign into this same account inside the app. To credit the app's current session instead, sign out here and choose âContinue as guestâ."]}):null,A?(0,a.jsx)("p",{style:{color:"#188A52",fontWeight:600},children:"Purchase complete â it appears in the app automatically."}):null,v?0===b.length?(0,a.jsxs)("p",{style:{color:rk},children:["Web purchase isn't available right now. Purchases made inside the app still work â or contact ",(0,a.jsx)("a",{href:"/contact",style:{color:rk},children:"support"}),"."]}):(0,a.jsxs)(a.Fragment,{children:[R.length>0?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("h2",{style:rP,children:"Subscriptions"}),R.map(_)]}):null,$.length>0?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("h2",{style:rP,children:"Credit packs"}),$.map(_)]}):null,(0,a.jsx)("p",{style:{fontSize:13,color:"#6B7280",marginTop:4},children:"Purchases appear in the app automatically."})]}):(0,a.jsx)("p",{style:{color:"#9AA0AA"},children:"Loading plansâ¦"}),s?(0,a.jsx)("button",{onClick:()=>D(async()=>{await (0,em.signOut)(rS()),S(null),k(null),y([]),w(!1),P(!1)}),style:rC,children:"Sign out"}):(0,a.jsx)("button",{onClick:()=>{p(!1),k(null),y([]),w(!1),P(!1)},style:rC,children:"Back to sign in"})]}
1933):(0,a.jsxs)("section",{style:{marginTop:24,display:"grid",gap:10},children:[(0,a.jsx)("button",{disabled:z,onClick:()=>D(async()=>{await (0,el.signInWithPopup)(rS(),new ec.GoogleAuthProvider)}),style:rz,children:"Continue with Google"}),(0,a.jsx)("div",{style:{height:1,background:"#E5E7EB",margin:"8px 0"}}),(0,a.jsx)("input",{placeholder:"Email",value:h,onChange:e=>m(e.target.value),style:rF}),(0,a.jsx)("input",{placeholder:"Password",type:"password",value:u,onChange:e=>f(e.target.value),style:rF}),(0,a.jsx)("button",{disabled:z||!h||!u,onClick:()=>D(async()=>{let e=rS();g?await (0,eh.ab)(e,h,u):await (0,ep.ac)(e,h,u)}),style:{...rz,background:rk,borderColor:rk,color:"#fff"},children:g?"Create account":"Sign in"}),(0,a.jsx)("button",{onClick:()=>x(e=>!e),style:rC,children:g?"Have an account? Sign in":"New here? Create an account"}),l?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{style:{height:1,background:"#E5E7EB",margin:"8px 0"}}),(0,a.jsx)("button",{disabled:z,onClick:()=>p(!0),style:rz,children:"Continue as guest â credit this device's app session"}),(0,a.jsx)("p",{style:{fontSize:13,color:"#6B7280",margin:0},children:"No account needed: your purchase is tied directly to the app session that opened this page and unlocks in the app automatically."})]}):null]}):(0,a.jsx)("p",{style:{marginTop:24,color:"#9AA0AA"},children:"Loadingâ¦"}),F&&(0,a.jsx)("p",{style:{color:rk,marginTop:16},children:F})]})}let rP={fontSize:15,fontWeight:800,marginTop:12,letterSpacing:"0.02em",textTransform:"uppercase",color:"#141820"},rz={padding:"11px 16px",borderRadius:10,border:"1px solid #D3D7DE",background:"#fff",color:"#141820",fontWeight:600,cursor:"pointer"},rT={...rz,textAlign:"left",padding:"13px 16px"},rF={padding:"10px 12px",borderRadius:8,border:"1px solid #D3D7DE",background:"#fff",color:"#141820"},rC={background:"none",border:"none",color:rk,cursor:"pointer",fontSize:13,padding:4},rM=` 1934.sf-legal{padding:120px clamp(20px,4vw,48px) clamp(64px,8vw,96px);max-width:760px;margin:0 auto} 1935.sf-legal h1{font-family:var(--font-fraunces),Georgia,serif;font-size:clamp(2rem,4vw,2.6rem);font-weight:700;letter-spacing:-.025em;margin-bottom:12px} 1936.sf-legal .sf-legal-updated{font-size:.88rem;color:var(--sf-muted);margin-bottom:32px} 1937.sf-legal h2{font-size:1.15rem;font-weight:650;margin:32px 0 12px;color:var(--sf-fg)} 1938.sf-legal p,.sf-legal li{font-size:.95rem;line-height:1.75;color:var(--sf-muted);margin-bottom:14px} 1939.sf-legal ul{padding-left:1.25rem;margin-bottom:20px} 1940.sf-legal a{color:var(--sf-accent);text-decoration:underline;text-underline-offset:3px} 1941.sf-legal .sf-legal-box{ 1942 padding:20px 24px;border-radius:var(--sf-radius);background:var(--sf-accent-soft); 1943 border:1px solid var(--sf-border);margin:24px 0; 1944} 1945.sf-legal .sf-legal-box p{margin:0;color:var(--sf-fg)} 1946.sf-support-cards{display:grid;gap:16px;margin-top:32px} 1947@media(min-width:540px){.sf-support-cards{grid-template-columns:1fr 1fr}} 1948.sf-support-card{ 1949 padding:24px;border-radius:var(--sf-radius);background:#fff;border:1px solid var(--sf-border); 1950} 1951.sf-support-card h3{font-size:1rem;font-weight:650;margin-bottom:8px} 1952.sf-support-card p{font-size:.9rem;margin:0} 1953`;function rI({title:e,updated:t,children:s}){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:rM}}),(0,a.jsxs)("article",{className:"sf-legal",children:[(0,a.jsx)("h1",{children:e}),(0,a.jsxs)("p",{className:"sf-legal-updated",children:["Last updated: ",t]}),s]})]})}function rD(){return(0,a.jsxs)(rI,{title:"Privacy Policy",updated:"September 18, 2026",children:[(0,a.jsx)("p",{children:"Soft Flourish (âweâ, âusâ, or âourâ) develops applications for iPhone, iPad, Android, Mac, Windows, and Apple TV, web applications, and the website at softflourish.com, and publishes books. This Privacy Policy explains how we collect, use, and share information when you use our software or visit our website. Soft Flourish is an independent software studio and publishing house based in California, USA."}),(0,a.jsx)("p",{children:"By using our applications or website, you agree to this policy. If you do not agree, please do not use our services."}),(0,a.jsx)("h2",{children:"Scope"}),(0,a.jsx)("p",{children:"This policy applies to mobile and web applications published or operated by Soft Flourish, client products we host on behalf of businesses where we act as processor, and this website. Specific apps may include additional in-app disclosures."}),(0,a.jsx)("h2",{children:"Information we collect"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Account information:"})," Email address, display name, and authentication identifiers when you create an account or sign in with Apple, Google, or email."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Usage data:"})," Features used, screens viewed, crash reports, and performance diagnostics to keep software stable and improve experience."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Device information:"})," Device type, operating system version, app version, language, and general region derived from IP address. Precise location is collected only when a feature requires it and
1953you grant permission."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Contact forms:"})," Name, email, phone, website, and message content submitted through softflourish.com."]})]}),(0,a.jsx)("h2",{children:"How we use information"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Provide, maintain, and improve our applications and website"}),(0,a.jsx)("li",{children:"Authenticate accounts and sync preferences across devices"}),(0,a.jsx)("li",{children:"Respond to support requests and business inquiries"}),(0,a.jsx)("li",{children:"Monitor crashes, fix bugs, and measure aggregate usage trends"}),(0,a.jsx)("li",{children:"Comply with legal obligations"})]}),(0,a.jsx)("p",{children:"We do not sell your personal information to third parties."}),(0,a.jsx)("h2",{children:"Third-party services"}),(0,a.jsx)("p",{children:"Our applications and website may use trusted service providers, including:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Google Firebase"})," â authentication, cloud database, analytics, and crash reporting"]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Apple Sign In / Google Sign In"})," â optional account sign-in"]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Google Analytics"})," â aggregated website usage on softflourish.com"]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Google AdSense"})," â display advertising on softflourish.com"]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Yandex Metrica"})," â records which pages of softflourish.com are visited, so Yandex Search can find them; session recording off, and only with your analytics cookie choice"]})]}),(0,a.jsx)("h2",{children:"Cookies & advertising"}),(0,a.jsx)("p",{children:"softflourish.com uses cookies and similar technologies. Some cookies are essential for the website to function; others are used for analytics and advertising as described below."}),(0,a.jsx)("p",{children:"Third-party vendors, including Google, use cookies to serve ads on this website based on a user's prior visits to this website or other websites. Google's use of advertising cookies (including the DoubleClick cookie) enables it and its partners to serve ads to you based on your visits to this site and/or other sites on the Internet."}),(0,a.jsxs)("p",{children:["You may opt out of personalized advertising by visiting"," ",(0,a.jsx)("a",{href:"https://www.google.com/settings/ads",target:"_blank",rel:"noopener noreferrer",children:"Google Ads Settings"}),". You can also opt out of some third-party vendors' use of cookies for personalized advertising by visiting"," ",(0,a.jsx)("a",{href:"https://optout.aboutads.info",target:"_blank",rel:"noopener noreferrer",children:"optout.aboutads.info"}),"."]}),(0,a.jsxs)("p",{children:["We also use Google Analytics on softflourish.com to understand aggregate website usage (pages viewed, device and browser type, and approximate region). You can prevent your data from being used by Google Analytics with the"," ",(0,a.jsx)("a",{href:"https://tools.google.com/dlpage/gaoptout",target:"_blank",rel:"noopener noreferrer",children:"Google Analytics opt-out browser add-on"}),". You can also block or delete cookies in your browser settings; some site features may then work less smoothly."]}),(0,a.jsxs)("p",{children:["For clarity: this section applies to your ",(0,a.jsx)("em",{children:"browsing of the softflourish.com website"}),". It does not change how we handle data received from Google APIs when you sign in with Google in our applications â that data is never used for advertising, as described in the next section."]}),(0,a.jsx)("h2",{children:"Signing in with Google or Apple"}),(0,a.jsxs)("p",{children:["When you choose to sign in with ",(0,a.jsx)("strong",{children:"Google"}),", we receive your name, email address, and profile picture from your Google Account through Google's secure OAuth service. When you sign in with ",(0,a.jsx)("strong",{children:"Apple"}),", we receive your name and email address (or Apple's private relay address if you choose to hide your email). We use this information only to create and secure your account, authenticate you, and sync y
1953our preferences across your devices. We never post on your behalf and do not request access to any other data from these providers."]}),(0,a.jsxs)("p",{children:["Soft Flourish's use and transfer of information received from Google APIs adheres to the"," ",(0,a.jsx)("a",{href:"https://developers.google.com/terms/api-services-user-data-policy",target:"_blank",rel:"noopener noreferrer",children:"Google API Services User Data Policy"}),", including its Limited Use requirements. We do not use Google user data for advertising, and we do not sell it or share it with third parties except as needed to provide the service, to comply with law, or in connection with a merger or acquisition."]}),(0,a.jsx)("h2",{children:"Data retention"}),(0,a.jsx)("p",{children:"Account data is kept while your account is active. If you delete your account, we delete or anonymize associated personal data within 30 days, except where retention is required by law. Support emails are retained up to two years for operational purposes."}),(0,a.jsx)("h2",{children:"Your rights"}),(0,a.jsx)("p",{children:"Depending on where you live, you may have the right to access, correct, delete, or export your personal data. California residents have rights under the CCPA; EEA/UK residents have rights under the GDPR."}),(0,a.jsx)("div",{className:"sf-legal-box",children:(0,a.jsxs)("p",{children:["To exercise your privacy rights or request account deletion, use our"," ",(0,a.jsx)("a",{href:"/contact",children:"contact form"}),"with the subject âPrivacy Requestâ and the email address associated with your account. We respond within 30 days."]})}),(0,a.jsx)("h2",{children:"Children's privacy"}),(0,a.jsx)("p",{children:"Our services are not directed to children under 13 (or under 16 in the EEA). We do not knowingly collect personal information from children. If you believe a child has provided us data, contact us and we will delete it promptly."}),(0,a.jsx)("h2",{children:"Security"}),(0,a.jsx)("p",{children:"We use HTTPS encryption, industry-standard authentication, and access controls on our infrastructure."}),(0,a.jsx)("h2",{children:"Changes to this policy"}),(0,a.jsx)("p",{children:"We may update this Privacy Policy from time to time. We will post the revised policy on this page with an updated date."}),(0,a.jsx)("h2",{children:"Contact us"}),(0,a.jsxs)("p",{children:["Questions: ",(0,a.jsx)("a",{href:"/contact",children:"Contact form"}),(0,a.jsx)("br",{}),"Support: ",(0,a.jsx)("a",{href:"/support",children:"/support"}),(0,a.jsx)("br",{}),"Terms: ",(0,a.jsx)("a",{href:"/terms",children:"/terms"})]})]})}function rW(){return(0,a.jsxs)(rI,{title:"Terms of Use",updated:"June 23, 2026",children:[(0,a.jsx)("p",{children:"These Terms of Use (âTermsâ) govern your use of applications published by Soft Flourish and the website at softflourish.com (âweâ, âusâ, or âourâ). By downloading, installing, or using our software, or by using our website, you agree to these Terms."}),(0,a.jsx)("h2",{children:"Our services"}),(0,a.jsx)("p",{children:"Soft Flourish develops and publishes mobile and web software for businesses and end users. Content in our applications is provided for general information. We strive for accuracy but do not guarantee completeness or timeliness."}),(0,a.jsx)("h2",{children:"Accounts"}),(0,a.jsxs)("p",{children:["Some applications require an account. You are responsible for safeguarding your credentials and for activity under your account. Notify us of unauthorized access via our"," ",(0,a.jsx)("a",{href:"/contact",children:"contact form"}),"."]}),(0,a.jsxs)("p",{children:["If you sign in using a third-party provider such as Google or Apple, your use of that provider is also subject to its own terms and privacy policy. We receive only your basic profile information (name, email, and, for Google, profile picture) to authenticate your account â see our"," ",(0,a.jsx)("a",{href:"/privacy",children:"Privacy Policy"})," for details."]}),(0,a.jsx)("h2",{children:"Acceptable use"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Use our software and website only for lawful purposes"}),(0,a.jsx)("li",{children:"Do not reverse engineer, scrape, or attempt to extract source code or data at scale"}),(0,a.jsx)("li",{children:"Do not interfere with operation, security, or other users' experience"}),(0,a.jsx)("li",{children:"Do not use our services to spam, harass, or distribute malware"})]}),(0,a.jsx)("h2",{children:"Intellectual property"}),(0,a.jsx)("p",{children:"All content, design, trademarks, and software are owned by Soft Flourish or licensed to us. You receive a limited, non-exclusive license to use our applications as permitted by applicable store terms."}),(0,a.jsx)("h2",{children:"Subscriptions and purchases"}),(0,a.jsx)("p",{children:"If an application offers in-app purchases or subscriptions, billing is handled by Apple or Google under their respective terms. Refund requests follow the store's refund policies."}),(0,a.jsx)("h2",{children:"Disclaimers"}),(0,a.jsx)("p",{children:"OUR SOFTWARE AND WEBSITE ARE PROVIDED âAS ISâ WITHOUT WARRANTIES OF ANY KIND. CONTENT IS NOT PROFESSIONAL ADVICE â including financial, medical, or legal advice."}),(0,a.jsx)("h2",{children:"Limitation of liability"}),(0,a.jsx)("p",{children:"TO THE MAXIMUM EXTENT PERMITTED BY LAW, SOFT FLOURISH IS NOT LIABLE FOR INDIRECT, INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OF OUR SOFTWARE OR WEBSITE."}),(0,a.jsx)("h2",{children:"Governing law"}),(0,a.jsx)("p",{children:"These Terms are governed by the laws of the State of California, USA. Disputes shall be resolved
1953in courts located in Los Angeles County, California."}),(0,a.jsx)("h2",{children:"Contact"}),(0,a.jsxs)("p",{children:[(0,a.jsx)("a",{href:"/contact",children:"Contact form"}),(0,a.jsx)("br",{}),(0,a.jsx)("a",{href:"/support",children:"/support"}),(0,a.jsx)("br",{}),(0,a.jsx)("a",{href:"/privacy",children:"Privacy Policy"})]})]})}function rO(){return(0,a.jsxs)(rI,{title:"Support",updated:"June 4, 2026",children:[(0,a.jsx)("p",{children:"Need help with a Soft Flourish product? This page is the official support URL for applications published under the Soft Flourish brand on the App Store and Google Play."}),(0,a.jsx)("div",{className:"sf-legal-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"Contact support:"})," ",(0,a.jsx)("a",{href:"/contact",children:"softflourish.com/contact"}),(0,a.jsx)("br",{}),(0,a.jsx)("span",{style:{fontSize:".88rem",color:"var(--sf-muted)"},children:"We typically respond within one business day (MonâFri, U.S. Pacific Time)."})]})}),(0,a.jsx)("h2",{children:"Before you write"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Include the product or application name and your device model"}),(0,a.jsx)("li",{children:"Include iOS/Android version and app version from Settings â About"}),(0,a.jsx)("li",{children:"For sign-in issues, mention whether you use Apple, Google, or email login"}),(0,a.jsx)("li",{children:"For crashes, describe what you were doing when the app closed"})]}),(0,a.jsxs)("div",{className:"sf-support-cards",children:[(0,a.jsxs)("div",{className:"sf-support-card",children:[(0,a.jsx)("h3",{children:"Delete my account"}),(0,a.jsxs)("p",{children:["Use our ",(0,a.jsx)("a",{href:"/contact",children:"contact form"}),"with subject âDelete Accountâ from the address linked to your account. We confirm deletion within 30 days."]})]}),(0,a.jsxs)("div",{className:"sf-support-card",children:[(0,a.jsx)("h3",{children:"Restore purchases"}),(0,a.jsx)("p",{children:"Use âRestore Purchasesâ in the app settings, or contact Apple/Google support for billing issues."})]}),(0,a.jsxs)("div",{className:"sf-support-card",children:[(0,a.jsx)("h3",{children:"Report a bug"}),(0,a.jsx)("p",{children:"Send steps to reproduce, screenshots if possible, and your app version."})]}),(0,a.jsxs)("div",{className:"sf-support-card",children:[(0,a.jsx)("h3",{children:"New business inquiries"}),(0,a.jsxs)("p",{children:["For custom development projects, use our ",(0,a.jsx)("a",{href:"/contact",children:"contact form"}),"."]})]})]}),(0,a.jsx)("h2",{children:"Policies"}),(0,a.jsxs)("p",{children:[(0,a.jsx)("a",{href:"/privacy",children:"Privacy Policy"})," · ",(0,a.jsx)("a",{href:"/terms",children:"Terms of Use"})]})]})}let rE="/contact",rR=` 1954.mw-wrap{ 1955 --mw-amber:#d9820a;--mw-amber-dark:#b8650a;--mw-taupe:#c9bba8; 1956 --mw-bg:#f8f5f0;--mw-fg:#141820;--mw-muted:#5c6478; 1957 --mw-border:rgba(20,24,32,0.10);--mw-surface:rgba(255,255,255,0.70); 1958 --mw-radius:16px; 1959 font-family:-apple-system,BlinkMacSystemFont,"Inter",system-ui,sans-serif; 1960 background:var(--mw-bg);color:var(--mw-fg); 1961 -webkit-font-smoothing:antialiased; 1962} 1963.mw-wrap *,.mw-wrap *::before,.mw-wrap *::after{box-sizing:border-box} 1964 1965/* nav */ 1966.mw-nav{ 1967 position:sticky;top:0;z-index:100;height:64px;padding:0 clamp(20px,4vw,48px); 1968 display:flex;align-items:center;justify-content:space-between; 1969 background:rgba(248,245,240,0.82);backdrop-filter:blur(18px); 1970 border-bottom:1px solid var(--mw-border); 1971} 1972.mw-nav-logo{display:flex;align-items:center;gap:10px;text-decoration:none;color:var(--mw-fg)} 1973.mw-nav-logo-icon{ 1974 width:30px;height:30px;border-radius:8px; 1975 background:linear-gradient(135deg,var(--mw-amber) 0%,var(--mw-amber-dark) 100%); 1976 display:flex;align-items:center;justify-content:center; 1977 font-size:16px;line-height:1; 1978} 1979.mw-nav-logo span{font-size:1rem;font-weight:700;letter-spacing:-.02em} 1980.mw-nav-links{display:flex;align-items:center;gap:16px;font-size:.84rem} 1981.mw-nav-links a{color:var(--mw-muted);text-decoration:none;transition:color .15s} 1982.mw-nav-links a:hover{color:var(--mw-fg)} 1983@media(max-width:600px){.mw-nav-links{display:none}} 1984 1985/* hero */ 1986.mw-hero{ 1987 padding:72px clamp(20px,4vw,48px) 64px; 1988 background:linear-gradient(160deg,#fdf6ee 0%,#f3ece0 55%,#ede3d4 100%); 1989 border-bottom:1px solid var(--mw-border); 1990} 1991.mw-hero-inner{max-width:860px;margin:0 auto;text-align:center} 1992/* Official Mac App Store artwork beside a scannable code. Apple requires the badge 1993 be used unaltered with clear space around it, so it gets its own row rather than 1994 being squeezed into the button row above. */ 1995.mw-store-row{display:flex;flex-wrap:wrap;gap:18px;align-items:center;justify-content:center;margin-top:24px} 1996.mw-store-row a{display:inline-flex;transition:transform .18s} 1997.mw-store-row a:hover{transform:translateY(-1px)} 1998.mw-store-qr{width:104px;
1998height:104px;background:#fff;border-radius:10px;border:1px solid rgba(0,0,0,.10)} 1999.mw-hero-badge{ 2000 display:inline-flex;align-items:center;gap:8px;padding:5px 14px;border-radius:999px; 2001 background:rgba(217,130,10,0.12);border:1px solid rgba(217,130,10,0.25); 2002 font-size:.72rem;font-weight:650;letter-spacing:.08em;text-transform:uppercase; 2003 color:var(--mw-amber-dark);margin-bottom:20px; 2004} 2005.mw-hero-h1{font-size:clamp(2.1rem,5vw,3.2rem);font-weight:780;letter-spacing:-.03em;line-height:1.05;margin-bottom:18px} 2006.mw-hero-sub{font-size:clamp(.97rem,2vw,1.1rem);color:var(--mw-muted);line-height:1.75;max-width:58ch;margin:0 auto 32px} 2007.mw-cta-row{display:flex;flex-wrap:wrap;gap:12px;justify-content:center} 2008.mw-btn{ 2009 display:inline-flex;align-items:center;gap:8px;padding:13px 26px;border-radius:999px; 2010 font-size:.88rem;font-weight:650;text-decoration:none;transition:transform .18s,box-shadow .18s; 2011} 2012.mw-btn:hover{transform:translateY(-2px)} 2013.mw-btn-primary{background:var(--mw-amber);color:#fff;box-shadow:0 8px 24px -8px rgba(217,130,10,0.45)} 2014.mw-btn-outline{background:rgba(255,255,255,0.55);color:var(--mw-fg);border:1.5px solid var(--mw-border);backdrop-filter:blur(8px)} 2015 2016/* features */ 2017.mw-features{padding:clamp(56px,8vw,80px) clamp(20px,4vw,48px)} 2018.mw-features-inner{max-width:1000px;margin:0 auto} 2019.mw-section-label{font-size:.68rem;font-weight:650;letter-spacing:.2em;text-transform:uppercase;color:var(--mw-amber);margin-bottom:12px} 2020.mw-section-h2{font-size:clamp(1.7rem,3.5vw,2.3rem);font-weight:750;letter-spacing:-.025em;margin-bottom:14px} 2021.mw-section-lead{font-size:1rem;color:var(--mw-muted);max-width:56ch;line-height:1.75;margin-bottom:40px} 2022.mw-grid{display:grid;gap:18px} 2023@media(min-width:640px){.mw-grid{grid-template-columns:repeat(2,1fr)}} 2024@media(min-width:900px){.mw-grid{grid-template-columns:repeat(3,1fr)}} 2025.mw-card{ 2026 padding:26px;border-radius:var(--mw-radius);background:var(--mw-surface); 2027 border:1px solid var(--mw-border);backdrop-filter:blur(8px); 2028} 2029.mw-card-icon{font-size:1.5rem;margin-bottom:12px} 2030.mw-card h3{font-size:1rem;font-weight:680;margin-bottom:6px} 2031.mw-card p{font-size:.88rem;color:var(--mw-muted);line-height:1.65;margin:0} 2032 2033/* privacy strip */ 2034.mw-privacy-strip{ 2035 padding:clamp(48px,6vw,64px) clamp(20px,4vw,48px); 2036 background:linear-gradient(160deg,#1a1208 0%,#0e0a04 100%);color:rgba(255,255,255,.88); 2037} 2038.mw-privacy-inner{max-width:1000px;margin:0 auto} 2039.mw-privacy-grid{display:grid;gap:16px;margin-top:32px} 2040@media(min-width:540px){.mw-privacy-grid{grid-template-columns:repeat(2,1fr)}} 2041@media(min-width:800px){.mw-privacy-grid{grid-template-columns:repeat(4,1fr)}} 2042.mw-privacy-card{padding:22px;border-radius:12px;background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.10)} 2043.mw-privacy-card h4{font-size:.9rem;font-weight:650;margin-bottom:6px;color:#fff} 2044.mw-privacy-card p{font-size:.82rem;color:rgba(255,255,255,.58);line-height:1.6;margin:0} 2045 2046/* legal body */ 2047.mw-legal{padding:clamp(48px,7vw,72px) clamp(20px,4vw,48px) clamp(56px,8vw,80px)} 2048.mw-legal-inner{max-width:740px;margin:0 auto} 2049.mw-legal h1{font-size:clamp(1.8rem,4vw,2.5rem);font-weight:750;letter-spacing:-.025em;margin-bottom:6px} 2050.mw-legal .mw-updated{font-size:.82rem;color:var(--mw-muted);margin-bottom:36px} 2051.mw-legal h2{font-size:1.05rem;font-weight:680;margin:28px 0 10px;color:var(--mw-fg)} 2052.mw-legal p,.mw-legal li{font-size:.93rem;line-height:1.8;color:var(--mw-muted);margin-bottom:12px} 2053.mw-legal ul{padding-left:1.25rem;margin-bottom:16px} 2054.mw-legal a{color:var(--mw-amber);text-decoration:underline;text-underline-offset:3px} 2055.mw-legal-box{ 2056 padding:18px 22px;border-radius:12px;background:rgba(217,130,10,0.08); 2057 border:1px solid rgba(217,130,10,0.22);margin:24px 0; 2058} 2059.mw-legal-box p{margin:0;color:var(--mw-fg);font-size:.9rem} 2060 2061/* footer */ 2062.mw-footer{ 2063 background:#0e0a04;color:rgba(255,255,255,.55); 2064 padding:36px clamp(20px,4vw,48px) 24px;font-size:.82rem; 2065 border-top:1px solid rgba(255,255,255,.06); 2066} 2067.mw-footer-inner{max-width:1000px;margin:0 auto;display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between;align-items:center} 2068.mw-footer-links{display:flex;flex-wrap:wrap;gap:12px} 2069.mw-footer-links a{color:rgba(255,255,255,.45);text-decoration:none;transition:color .15s} 2070.mw-footer-links a:hover{color:#fff} 2071`;function r$(){return(0,a.jsxs)("nav",{className:"mw-nav","aria-label":"MeetWing navigation",children:[(0,a.jsxs)("a",{href:"/meetwing",className:"mw-nav-logo","aria-label":"MeetWing home",children:[(0,a.jsx)("div",{className:"mw-nav-logo-icon","aria-hidden":!0,
2071children:"ðª¶"}),(0,a.jsx)("span",{children:"MeetWing"})]}),(0,a.jsxs)("div",{className:"mw-nav-links",children:[(0,a.jsx)("a",{href:"/meetwing/manual",children:"Manual"}),(0,a.jsx)("a",{href:"/meetwing/account",children:"Account"}),(0,a.jsx)("a",{href:"/meetwing/privacypolicy",children:"Privacy"}),(0,a.jsx)("a",{href:"/meetwing/terms",children:"Terms"}),(0,a.jsx)("a",{href:"/meetwing/support",children:"Support"})]})]})}function r_(){return(0,a.jsx)("footer",{className:"mw-footer",children:(0,a.jsxs)("div",{className:"mw-footer-inner",children:[(0,a.jsxs)("span",{children:["© ",new Date().getFullYear()," Soft Flourish"]}),(0,a.jsxs)("div",{className:"mw-footer-links",children:[(0,a.jsx)("a",{href:"/meetwing/manual",children:"Manual"}),(0,a.jsx)("a",{href:"/meetwing/account",children:"Account"}),(0,a.jsx)("a",{href:"/meetwing/privacypolicy",children:"Privacy Policy"}),(0,a.jsx)("a",{href:"/meetwing/terms",children:"Terms of Use"}),(0,a.jsx)("a",{href:rE,children:"Contact"}),(0,a.jsx)("a",{href:"/",children:"softflourish.com"})]})]})})}function rU(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:rR}}),(0,a.jsxs)("div",{className:"mw-wrap",children:[(0,a.jsx)(r$,{}),(0,a.jsx)("section",{className:"mw-hero",children:(0,a.jsxs)("div",{className:"mw-hero-inner",children:[(0,a.jsx)("div",{className:"mw-hero-badge",children:"𪶠Now on Mac"}),(0,a.jsxs)("h1",{className:"mw-hero-h1",children:["Your AI co-pilot",(0,a.jsx)("br",{}),"for every meeting"]}),(0,a.jsx)("p",{className:"mw-hero-sub",children:"MeetWing listens to your meetings in real time and gives you live, context-aware suggestions â so you can stay focused on the conversation instead of scrambling for the right words."}),(0,a.jsxs)("div",{className:"mw-cta-row",children:[(0,a.jsx)("a",{href:"https://apps.apple.com/app/meetwing/id6779822069",className:"mw-btn mw-btn-primary",target:"_blank",rel:"noopener noreferrer",children:"Download on the Mac App Store"}),(0,a.jsx)("a",{href:rE,className:"mw-btn mw-btn-outline",children:"Get support"})]}),(0,a.jsxs)("div",{className:"mw-store-row",children:[(0,a.jsx)("a",{href:"https://apps.apple.com/app/id6779822069?mt=12",target:"_blank",rel:"noopener noreferrer","aria-label":"Download MeetW
2071ing on the Mac App Store",children:(0,a.jsx)("img",{src:"/meetwing/mac-app-store-badge.svg",alt:"Download on the Mac App Store",width:187,height:48})}),(0,a.jsx)("a",{href:"https://apps.apple.com/app/id6779822069?mt=12",target:"_blank",rel:"noopener noreferrer","aria-label":"Open the MeetWing Mac App Store listing",children:(0,a.jsx)("img",{className:"mw-store-qr",src:"/meetwing/qr-macappstore.svg",alt:"",width:104,height:104})})]})]})}),(0,a.jsx)("section",{className:"mw-features",children:(0,a.jsxs)("div",{className:"mw-features-inner",children:[(0,a.jsx)("p",{className:"mw-section-label",children:"Features"}),(0,a.jsx)("h2",{className:"mw-section-h2",children:"Everything you need to ace the call"}),(0,a.jsx)("p",{className:"mw-section-lead",children:"MeetWing runs quietly alongside any meeting â no integrations, no bot joining the call. Just open it and start."}),(0,a.jsx)("div",{className:"mw-grid",children:[{icon:"ðï¸",title:"Live transcription",body:"Real-time speech-to-text of what everyone is saying, so nothing gets lost."},{icon:"ð¤",title:"AI answers",body:"Context-aware suggestions appear the moment someone asks a question or makes a point."},{icon:"ð",title:"Playbooks",body:"Sales, interviews, client calls, recruiting â one tap tunes the AI for your meeting type."},{icon:"ð",title:"Privacy first",body:"Audio is never stored. Your session lives only in memory and is wiped when you close the app."},{icon:"ð",title:"Google Search Live",body:"Optional live web grounding â the AI finds real-time facts before it answers."},{icon:"ð¤",title:"Export",body:"One-click export to PDF, TXT, Markdown, or CSV â only when you choose to save anything."}].map(e=>(0,a.jsxs)("article",{className:"mw-card",children:[(0,a.jsx)("p",{className:"mw-card-icon","aria-hidden":!0,children:e.icon}),(0,a.jsx)("h3",{children:e.title}),(0,a.jsx)("p",{children:e.body})]},e.title))})]})}),(0,a.jsx)("section",{className:"mw-privacy-strip","aria-label":"Privacy promises",children:(0,a.jsxs)("div",{className:"mw-privacy-inner",children:[(0,a.jsx)("p",{className:"mw-section-label",style:{color:"#d9820a"},children:"Privacy"}),(0,a.jsx)("h2",{className:"mw-section-h2",style:{color:"#fff",marginBottom:8},children:"Your meetings stay yours"}),(0,a.jsx)("p",{style:{fontSize:".95rem",color:"rgba(255,255,255,.6)",maxWidth:"54ch",lineHeight:1.7,marginBottom:0},children:"MeetWing is built on a single rule: we keep nothing."}),(0,a.jsx)("div",{className:"mw-privacy-grid",children:[{icon:"ðï¸",title:"Audio never stored",body:"Processed in the moment, discarded as soon as the response arrives."},{icon:"ðï¸",title:"No server logs",body:"Transcript and answers are never written to any database or log."},{icon:"ð§¹",title:"Wiped on close",body:"Session lives in memory only. Gone the moment you quit the app."},{icon:"ð¤",title:"You export, you own",body:"The only copy is the one you create â on your own device."}].map(e=>(0,a.jsxs)("div",{className:"mw-privacy-card",children:[(0,a.jsxs)("h4",{children:[e.icon," ",e.title]}),(0,a.jsx)("p",{children:e.body})]},e.title))})]})}),(0,a.jsx)(r_,{})]})]})}function rL(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:rR}}),(0,a.jsxs)("div",{className:"mw-wrap",children:[(0,a.jsx)(r$,{}),(0,a.jsx)("main",{className:"mw-legal",children:(0,a.jsxs)("div",{className:"mw-legal-inner",children:[(0,a.jsx)("h1",{children:"MeetWing support"}),(0,a.jsx)("p",{className:"mw-updated",children:"Mac and Windows · Soft Flourish"}),(0,a.jsxs)("div",{className:"mw-legal-box",children:[(0,a.jsx)("h2",{children:"Get help"}),(0,a.jsxs)("p",{children:["Email ",(0,a.jsx)("a",{href:"mailto:[email protected]",children:"[email protected]"})," and include your operating system version and what you were doing when the problem happened. That is usually enough to reproduce it on the first reply."]}),(0,a.jsx)("h2",{children:"Common questions"}),(0,a.jsx)("h3",{children:"Live transcription is empty"}),(0,a.jsx)("p",{children:"MeetWing listens to your Macâs own audio. Check that system audio capture is permitted, and that the meeting is actually playing through this machine rather than a headset paired to another device. The status bar names the specific fault when capture fails."}),(0,a.jsx)("h3",{children:"Where are my transcripts stored?"}),(0,a.jsx)("p",{children:"On your machine. Transcripts and summaries are yours to export â nothing is kept on our servers beyond what is needed to generate an answer you asked for."}),(0,a.jsx)("h3",{children:"Managing your subscription"}),(0,a.jsxs)("p",{children:["MeetWing Pro and Max are auto-renewable subscriptions billed through your Ap
2071ple Account. Manage or cancel in App Store settings, or from your"," ",(0,a.jsx)("a",{href:"/meetwing/account",children:"account page"}),". Cancelling at least 24 hours before the period ends stops the next renewal."]}),(0,a.jsx)("h3",{children:"Requesting deletion of your data"}),(0,a.jsxs)("p",{children:["Write to ",(0,a.jsx)("a",{href:"mailto:[email protected]",children:"[email protected]"})," from the address on the account. See the"," ",(0,a.jsx)("a",{href:"/meetwing/privacypolicy",children:"privacy policy"})," for what is held and for how long."]})]})]})}),(0,a.jsx)(r_,{})]})]})}function rq(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:rR}}),(0,a.jsxs)("div",{className:"mw-wrap",children:[(0,a.jsx)(r$,{}),(0,a.jsx)("main",{className:"mw-legal",children:(0,a.jsxs)("div",{className:"mw-legal-inner",children:[(0,a.jsx)("h1",{children:"MeetWing user manual"}),(0,a.jsx)("p",{className:"mw-updated",children:"Mac and Windows · Soft Flourish"}),(0,a.jsxs)("div",{className:"mw-legal-box",children:[(0,a.jsx)("h2",{children:"Starting a session"}),(0,a.jsx)("p",{children:"Open MeetWing before your meeting starts, or any time during it. There is no bot to invite and nothing to integrate â MeetWing listens to your Macâs own audio, so it works with any meeting app running on the same machine."}),(0,a.jsx)("h2",{children:"Live transcription"}),(0,a.jsxs)("p",{children:["Speech from everyone on the call appears as real-time text while the meeting runs. Nothing is stored as it happens â see the"," ",(0,a.jsx)("a",{href:"/meetwing/privacypolicy",children:"privacy policy"})," for exactly what that means."]}),(0,a.jsx)("h2",{children:"AI answers"}),(0,a.jsx)("p",{children:"When someone asks a question or raises a point worth responding to, a context-aware suggestion appears â pulled from what has been said in the meeting so far, not a generic script."}),(0,a.jsx)("h2",{children:"Playbooks"}),(0,a.jsx)("p",{children:"Pick the playbook that matches your meeting â sales, interview, client call, recruiting â before you start, and MeetWing tunes its suggestions to that kind of conversation instead of a one-size-fits-all response."}),(0,a.jsx)("h2",{children:"Google Search Live"}),(0,a.jsx)("p",{children:"An optional setting that lets the AI check real-time facts on the web before it answers, for the moments a suggestion needs something more current than the conversation itself. Off by default; turn it on in Settings."}),(0,a.jsx)("h2",{children:"Exporting a transcript"}),(0,a.jsxs)("p",{children:["One click exports the session to PDF, TXT, Markdown, or CSV. That export is the only copy that persists anywhere â see"," ",(0,a.jsx)("a",{href:"/meetwing/privacypolicy",children:"how data is handled"})," during and after a session."]}),(0,a.jsx)("h2",{children:"If something isnât working"}),(0,a.jsxs)("p",{children:["See ",(0,a.jsx)("a",{href:"/meetwing/support",children:"support"})," for live-transcription troubleshooting, subscription management, and data deletion requests."]})]})]})}),(0,a.jsx)(r_,{})]})]})}function rB(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:rR}}),(0,a.jsxs)("div",{className:"mw-wrap",children:[(0,a.jsx)(r$,{}),(0,a.jsx)("div",{className:"mw-legal",children:(0,a.jsxs)("div",{className:"mw-legal-inner",children:[(0,a.jsx)("h1",{children:"Privacy Policy"}),(0,a.jsx)("p",{className:"mw-updated",children:"Last updated: June 2026 · Applies to: MeetWing for macOS"}),(0,a.jsx)("div",{className:"mw-legal-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"The short version:"})," MeetWing never records your audio, never stores your transcript, and never logs your meeting content. Everything is processed in real time and discarded immediately. The only data we keep is your account identifier and credit balance â nothing meeting-related."]})}),(0,a.jsx)("h2",{children:"Who we are"}),(0,a.jsxs)("p",{children:["MeetWing is developed and published by Soft Flourish, an independent software studio and publishing house based in California, USA. Questions: ",(0,a.jsx)("a",{href:rE,children:"contact us"}),"."]}),(0,a.jsx)("h2",{children:"Our core promise"}),(0,a.jsx)("p",{children:"MeetWing is built so that your meetings stay yours. We designed the app around a single rule: we keep nothing. We do not record your conversations, we do not store your transcripts, and we do not keep a history of anything you say or anything the assistant suggests."}),(0,a.jsx)("h2",{children:"How the assistant works"}),(0,a.jsx)("p",{children:"To generate live answers, MeetWing sends short segments of meeting audio, the running transcript, and â only if you turn on screen sharing â screenshots of the screen you selected, over an encrypted connection to our AI service. This data is used solely to produce the response you see, and is discarded as soon as that response is returned. It is never written to a database, a file, or a log on our side."}),(0,a.jsx)("h2",{children:"Third-party AI providers"}),(0,a.jsx)("p",{children:"Your meeting data (audio segments, transcripts, and optional screenshots) is processed by third-party AI providers depen
2071ding on the model tier you select. These providers currently include:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Anthropic"})," (Claude) â AI answer generation"]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Google"})," (Gemini, Chirp 3) â AI answer generation and live transcription"]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"OpenAI"})," (Whisper, GPT-4o) â transcription and AI answer generation"]})]}),(0,a.jsx)("p",{children:"None of these providers retain your meeting data to train their models. We may change or add providers at any time; the current list is always reflected in the app's in-app Privacy Policy (Account menu â Terms & Privacy)."}),(0,a.jsx)("h2",{children:"Screen recording data"}),(0,a.jsx)("p",{children:"MeetWing uses macOS ScreenCaptureKit to capture system audio (the other participants' voices). Optionally, you may select a screen or window to share with the AI. When you do, MeetWing takes a JPEG screenshot whenever the visible content changes significantly. This screenshot is included as context in the prompt sent to our AI backend, consumed once per response, and then immediately set to nil. No screenshot is ever stored server-side."}),(0,a.jsx)("h2",{children:"What we never do"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"We never save or replay your audio."}),(0,a.jsx)("li",{children:"We never retain your transcript or the assistant's answers."}),(0,a.jsx)("li",{children:"We never log the contents of your meetings."}),(0,a.jsx)("li",{children:"We never sell, share, or use your meeting content to train AI models."})]}),(0,a.jsx)("h2",{children:"What lives on your Mac"}),(0,a.jsx)("p",{children:"During a session, your transcript and the assistant's answers exist only in your Mac's memory. They are not saved to disk. When the session ends or you quit MeetWing, this history is erased automatically. The only settings we remember between launches are your appearance theme, text size, and the optional meeting-context fields you fill in yourself."}),(0,a.jsx)("h2",{children:"The only data we store"}),(0,a.jsx)("p",{children:"To run your account we store the minimum needed for sign-in and billing: your account identifier and your remaining credit balance / subscription tier. This contains no meeting content of any kind. Payments are handled by Apple; we never see your card details."}),(0,a.jsx)("h2",{children:"Your responsibility in a meeting"}),(0,a.jsx)("p",{children:"Laws on recording and transcribing conversations vary by region. Because MeetWing processes what is said in your meetings, you are responsible for obtaining any consent required from other participants and for using the app in line with the rules of your workplace and your local laws."}),(0,a.jsx)("h2",{children:"Account deletion"}),(0,a.jsxs)("p",{children:["You can delete your account in-app at any time: Account menu (person icon) â Delete Account. This permanently removes your Firebase Auth account and all billing data within the app. To request data deletion another way, use our"," ",(0,a.jsx)("a",{href:rE,children:"contact form"}),' with "Delete Account" and the email address linked to your account. We process requests within 30 days.']}),(0,a.jsx)("h2",{children:"Your rights"}),(0,a.jsx)("p",{children:"Depending on where you live, you may have the right to access, correct, delete, or export your personal data. California residents have rights under the CCPA; EEA/UK residents have rights under the GDPR."}),(0,a.jsx)("div",{className:"mw-legal-box",children:(0,a.jsxs)("p",{children:["To exercise privacy rights: use our ",(0,a.jsx)("a",{href:rE,children:"contact form"}),' with "Privacy Request" and the email address linked to your account. We respond within 30 days.']})}),(0,a.jsx)("h2",{children:"Children's privacy"}),(0,a.jsx)("p",{children:"MeetWing is not directed to children under 13. We do not knowingly collect personal information from children."}),(0,a.jsx)("h2",{children:"Changes to this policy"}),(0,a.jsx)("p",{children:"We may update this Privacy Policy as the app evolves. We will post the revised policy on this page with an updated date."}),(0,a.jsx)("h2",{children:"Contact"}),(0,a.jsxs)("p",{children:[(0,a.jsx)("a",{href:rE,children:"Contact"})," · ",(0,a.jsx)("a",{href:"/meetwing/terms",children:"Terms of Use"})," · ",(0,a.jsx)("a",{href:"/meetwing",children:"MeetWing"})]})]})}),(0,a.jsx)(r_,{})]})]})}function rG(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:rR}}),(0,a.jsxs)("div",{className:"mw-wrap",children:[(0,a.jsx)(r$,{}),(0,a.jsx)("div",{className:"mw-legal",children:(0,a.jsxs)("div",{className:"mw-legal-inner",children:[(0,a.jsx)("h1",{children:"Terms of Use"}),(0,a.jsx)("p",{className:"mw-updated",children:"Last updated: June 2026 · Applies to: MeetWing for macOS"}),(0,a.jsx)("h2",{children:"Acceptance of these Terms"}),(0,a.jsxs)("p",{children:["These Terms of Use form a binding agreement between you and Soft Flourish (âwe,â âusâ). By checking the acceptance box, signing in, creating an account, or using MeetWing in any way, you confirm that you have read, understood, and agree to these Terms and the"," ",(0,a.jsx)("a",{href:"/meetwing/privacypolicy",children:"Privacy Policy"}),". If you do not agree, do not use MeetWing."]}),(0,a.jsx)("h2",{children:"What MeetWing is â and is not"}),(0,a.jsxs)("p",{children:["MeetWing is a real-time assistant that generates AI suggestions to help you during live conversations. It is a ",(0,a.jsx)("strong",{children:"productivity aid only"}),". It is NOT advice. It does not provide legal, financial, medical, tax, employment, investment, or any other professional advice, and n
2071othing it produces should be relied upon as such. For decisions that matter, consult a qualified professional."]}),(0,a.jsx)("h2",{children:"AI output may be wrong â you decide"}),(0,a.jsx)("p",{children:"Artificial intelligence can and does produce output that is inaccurate, incomplete, outdated, biased, fabricated, or otherwise wrong (including âhallucinationsâ â confident statements that are simply false). MeetWing's suggestions are starting points for your own thinking, not statements of fact."}),(0,a.jsx)("div",{className:"mw-legal-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"YOU ARE THE SOLE AND ULTIMATE DECISION-MAKER."}),"Any words you speak, send, or act on are your own decisions and your sole responsibility, regardless of whether they were prompted, suggested, or generated by MeetWing. We are not responsible for any decision you make or any consequence that follows from relying on the app's output."]})}),(0,a.jsx)("h2",{children:"Third-party AI providers"}),(0,a.jsx)("p",{children:"MeetWing uses third-party AI providers â currently including Anthropic (Claude), Google (Gemini / Chirp 3), and OpenAI (Whisper / GPT-4o) â to generate responses and transcriptions. We do not control these providers, and we are not responsible for their availability, accuracy, output, errors, or downtime. We may change providers at any time without notice."}),(0,a.jsx)("h2",{children:"Your responsibilities & acceptable use"}),(0,a.jsx)("p",{children:"You agree to use MeetWing lawfully and to obtain any consent legally required to record, transcribe, or process other participants' speech in your meetings. You are solely responsible for the meetings you run the assistant on and for complying with your employer's policies, confidentiality obligations, and all applicable laws (including recording-consent and privacy laws in your jurisdiction). You will not use MeetWing for any unlawful, harmful, or infringing purpose."}),(0,a.jsx)("h2",{children:"Your account & credits"}),(0,a.jsx)("p",{children:"You must create an account and accept these Terms to use MeetWing. You are responsible for activity under your account. Some features consume credits or require a subscription; credits and tiers are tied to your account, are non-transferable, and have no cash value. Subscriptions auto-renew and are managed through Apple under their terms."}),(0,a.jsx)("h2",{children:"Auto-renewable subscriptions"}),(0,a.jsx)("p",{children:"MeetWing Pro and MeetWing Max are auto-renewable subscriptions billed through the App Store. Payment is charged to your Apple ID at confirmation of purchase. Subscriptions automatically renew unless cancelled at least 24 hours before the end of the current period. You can manage and cancel subscriptions in your Ap
2071ple ID account settings. Prices are displayed before purchase and may vary by region."}),(0,a.jsx)("h2",{children:'Service "as is" â no warranties'}),(0,a.jsx)("p",{children:"MEETWING IS PROVIDED âAS ISâ AND âAS AVAILABLE,â WITHOUT WARRANTIES OF ANY KIND, WHETHER EXPRESS, IMPLIED, OR STATUTORY, INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, ACCURACY, AND NON-INFRINGEMENT. We do not warrant that the app will be uninterrupted, timely, secure, error-free, or that any output will be accurate or reliable."}),(0,a.jsx)("h2",{children:"Limitation of liability"}),(0,a.jsx)("p",{children:"TO THE FULLEST EXTENT PERMITTED BY LAW, IN NO EVENT WILL SOFT FLOURISH OR ITS OFFICERS, EMPLOYEES, OR SUPPLIERS BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL, EXEMPLARY, OR PUNITIVE DAMAGES, OR FOR ANY LOSS OF PROFITS, REVENUE, DATA, GOODWILL, OR BUSINESS, ARISING OUT OF OR RELATED TO YOUR USE OF â OR INABILITY TO USE â MEETWING OR ITS OUTPUT. OUR TOTAL AGGREGATE LIABILITY WILL NOT EXCEED THE GREATER OF (A) THE AMOUNT YOU PAID US IN THE 3 MONTHS BEFORE THE CLAIM, OR (B) US $50."}),(0,a.jsx)("h2",{children:"Indemnification"}),(0,a.jsx)("p",{children:"You agree to indemnify and hold harmless Soft Flourish from any claims, damages, liabilities, and expenses arising out of your use of MeetWing, your reliance on its output, the content of your meetings, or your violation of these Terms or any law or third-party rights."}),(0,a.jsx)("h2",{children:"Governing law & changes"}),(0,a.jsx)("p",{children:"These Terms are governed by the laws of the State of California, USA, and any dispute will be resolved in the courts of California. We may update these Terms as the app evolves; continued use after an update means you accept the current version."}),(0,a.jsx)("h2",{children:"Contact"}),(0,a.jsxs)("p",{children:[(0,a.jsx)("a",{href:rE,children:"Contact"})," · ",(0,a.jsx)("a",{href:"/meetwing/privacypolicy",children:"Privacy Policy"})," · ",(0,a.jsx)("a",{href:"/meetwing",children:"MeetWing"})]})]})}),(0,a.jsx)(r_,{})]})]})}let rH=[{slug:"football-pulse",name:"Football Insight Hub",bundleId:"com.fenul.worldcup",sport:"football (soccer)",competitions:"major international tournaments, continental championships, domestic league seasons and continental club competitions",hasAds:!0,hasRewardedAds:!0,manualLeadSections:[{heading:"Balancing two fair sides",body:"Open the Smart Team Balancer and add whoever turned up. It deals two sides that are level on strength and sensible in shape â a keeper on each side, the defenders spread rather than stacked â and then tells you how big a gap it could not close, instead of pretending there isn't one. Nobody has to be picked last, and nobody has to be the one doing the picking."},{heading:"Running matchday",body:"Start the clock and keep the game: goals, cards, substitutions, half-time and the shootout if it goes that far. It is built for fourteen people in a park â big controls, one hand, works with the screen in the sun â not for a broadcast gantry."},{heading:"The formation sheet",body:"Drag your shape out on the pitch and name the roles. It saves alongside the squad, so the next game starts from the last one rather than from a blank sheet, and it can be handed round before kick-off."},{heading:"The tactics board",body:"Draw runs, mark where to press, freeze a moment and talk over it. The whiteboard that normally lives in the boot of somebody's car, in your pocket instead."},{heading:"The offside visualiser",body:"Set the defensive line, place the pass and the run, and see the decision drawn. It settles the weekend's argument with the rule rather than with volume â and it is the fastest way to explain offside to someone who has never had it explained."},{heading:"Your squad",body:"The people you play with, kept on your device. Add them once and they are there for every balance, every formation sheet and every matchday after that. No account is needed for any of it."}]},{slug:"cricket-pulse",name:"Cricket Pulse",bundleId:"com.fenul.cricket.t20",sport:"cricket",competitions:"One Day International and Twenty20 internationals, major global tournaments, and domestic Twenty20 league seasons",hasAds:!0,hasRewardedAds:!1}
2071,{slug:"sport-orbit",name:"Sport Orbit",bundleId:"com.fenul.olympics.summer",sport:"multi-sport competition",competitions:"both the Summer and Winter Games, plus year-round athletics, swimming, cycling, alpine skiing, figure skating, speed skating, and biathlon",hasAds:!0,hasRewardedAds:!1}],rY=["privacy","privacypolicy","privacy-policy"],rV=["terms","terms-of-use","terms-of-service"],rK=["deleteaccount","delete-account"],rQ=["support","help"],rJ=["manual","guide"];rH.map(e=>`/${e.slug}`);let rZ="/contact",rX=[{slug:"football-pulse",name:"Football Insight Hub",emoji:"â½",palette:{accent:"#da1e28",accentDark:"#b3141d",hero:["#fff4f4","#f6e9e9","#efdedf"],strip:["#1a0709","#0c0405"]},badge:"Your Sunday game. And every other one.",headline:["Two fair sides in four seconds,","then the whole game"],sub:"Fourteen turned up and nobody wants to be picked last. Football Insight Hub balances the squad, suggests a shape, and runs matchday from the first whistle â then keeps the professional game beside it, with live scores, tables, fixtures and results across the competitions you follow.",ctas:[{href:"https://factlen.com/football",label:"Open the web app",primary:!0},{href:rZ,label:"Get support"}],stores:[{url:"https://apps.apple.com/app/id6777162828",badge:"/pulse/app-store-badge.svg",alt:"Download on the App Store",width:120},{url:"https://play.google.com/store/apps/details?id=com.fenul.worldcup",badge:"/pulse/google-play-badge.png",alt:"Get it on Google Play",width:135}],platforms:"Free, with an optional ad-free Pro upgrade",featuresH2:"Built for the person who organises the game",featuresLead:"Everything you need between \"who's in?\" and the final whistle â and the professional game in the same app when you're done playing it.",features:[{icon:"âï¸",title:"Smart Team Balancer",body:"Add whoever came and it deals two sides level on strength and sensible in shape â a keeper each, and an honest report of the gap it couldn't close."},{icon:"ð",title:"Formation sheet",body:"Drag your shape out on the pitch, name the roles, and hand it round. It saves with the squad, so next week starts where last week finished."},{icon:"âï¸",title:"Tactics board",body:"Draw runs, mark the press, freeze a moment. A whiteboard that lives in your pocket instead of the boot of a car."},{icon:"ð©",title:"Offside visualiser",body:"Set the line, place the pass, and settle the argument you had at the weekend â with the rule drawn instead of shouted."},{icon:"â±ï¸",title:"Matchday",body:"Run the clock, log the goals, keep the score. Built for fourteen people in a park, not a broadcast gantry."},{icon:"â¡",title:"The professional game",body:"Live scores, live tables, fixtures, results and finished seasons across the competitions you follow â switch between them in a tap."}],compsH2:"Coverage that rolls forward on its own",compsLead:"A competition switches on the moment its season kicks off and stays reachable in the archive once it ends â nothing to download or reinstall for a new edition. Each one ships with at least a live table and full fixtures.",primaryChips:["The world tournament","Continental championships","Women's international tournaments","National team qualifying"],secondaryChips:["Domestic league seasons","Continental club competitions","Domestic cup competitions","Finished seasons, archived"]},{slug:"cricket-pulse",name:"Cricket Forge",emoji:"ð",palette:{accent:"#155840",accentDark:"#0f4230",hero:["#f4f9f6","#e8f0ea","#dbe7de"],strip:["#0a1c15","#04100b"]},badge:"Every match. Every angle.",headline:["Score the whole match,","ball by ball"],sub:"Put the phone on the boundary rope and score a real match with one thumb â a single, a boundary, a wide, a no ball, a wicket, with full undo. Cricket Forge builds the scorecards, the Manhattan and the wagon wheel from what you tap, then turns every match into a lasting career record. Live scores and points tables for the professional one-day and T20 calendar sit alongside it.",ctas:[{href:"https://factlen.com/sports/cricket",label:"Open the web app",primary:!0},{href:rZ,label:"Get support"}],stores:[{url:"https://apps.apple.com/app/id6777165784",badge:"/pulse/app-store-badge.svg",alt:"Download on the App Store",width:120},{url:"https://play.google.com/store/apps/details?id=com.fenul.cricket.t20",badge:"/pulse/google-play-badge.png",alt:"Get it on Google Play",width:135}],platforms:"Free, with an optional ad-free Pro upgrade · iPhone, iPad, Mac and Android â Apple TV in review",featuresH2:"A scorebook first, the professional game beside it",featuresLead:"Everything between \"how's that\" and the full scorecard â plus the one-day and T20 calendar in the same app when you're done scoring.",features:[{icon:"ð",title:"Score ball by ball",body:"A single, a boundary, a wide, a no ball, a bye, a leg bye, a wicket â one tap each, with full undo for when the umpire changes their mind."},{icon:"ð",title:"Full scorecards",body:"Batting and bowling cards, extras, the partnership after every stand and the fall of wickets â all built from the deliveries you tapped."},{icon:"ð",title:"Manhattan & wagon wheel",body:"The Manhattan draws runs per over as a skyline. The wagon wheel draws where the shots actually went."},{icon:"ð",title:"The career you didn't know you had",body:"Every match feeds a permanent record for every player â average, strike rate, economy, best figures, dism
2071issals by type."},{icon:"ð§®",title:"Run rate, chase & partnerships",body:"Current and required rate, balls left, an over-by-over chase table, and every stand split between the two batters."},{icon:"â¡",title:"The professional game",body:"Live scores, points tables, fixtures, results and an archive across the one-day and T20 calendar, switcher included."}],compsH2:"The professional calendar, rolling forward on its own",compsLead:"Each competition switches on when its season starts and stays in the archive when it ends â nothing to download, nothing to reinstall.",primaryChips:["The 50-over world tournament","The T20 world tournament"],secondaryChips:["Men's international series","Women's international series","Domestic T20 leagues","Finished editions, archived"]},{slug:"sport-orbit",name:"Sport Orbit",emoji:"ð ",palette:{accent:"#1a5fd0",accentDark:"#1449a3",hero:["#f2f6fe","#e6edfa","#dae4f5"],strip:["#060d1c","#030711"]},badge:"Every season. Every sport.",headline:["Every season, every sport,","one live app"],sub:"Sport Orbit covers both the Summer and Winter Games â medal counts, daily schedules, sport-by-sport results and country standings â plus year-round athletics, swimming, cycling, alpine skiing, figure skating, speed skating and biathlon. One app, no gap years.",ctas:[{href:"https://factlen.com/sports",label:"Open the web app",primary:!0},{href:rZ,label:"Get support"}],stores:[{url:"https://apps.apple.com/app/id6777163988?mt=12",badge:"/pulse/mac-app-store-badge.svg",alt:"Download on the Mac App Store",width:187}],platforms:"Free, with an optional ad-free Pro upgrade · iPhone, iPad and Apple TV in review",featuresH2:"The multi-sport calendar without the four-year wait",featuresLead:"A single-event app sits dormant between Games. Sport Orbit carries the Summer and Winter seasons and keeps going through the world championships and World Cup circuits in between.",features:[{icon:"ð",title:"Season switcher",body:"Summer and Winter in one app â switch season and every schedule, result and standing follows."},{icon:"â¡",title:"Live results",body:"Finals, heats and finishes as they happen, across every sport on the day's programme."},{icon:"ð¥",title:"Medal table",body:"Country standings updating with each final, sortable by gold or by total."},{icon:"ðï¸",title:"Daily schedule",body:"What is on today in your own timezone, sport by sport, with finals flagged."},{icon:"ð½",title:"Sport-by-sport results",body:"Drill into athletics, swimming, alpine skiing or any other sport for its full result set."},{icon:"ð",title:"Predictions & rewards",body:"Sign in to call podium finishes and earn rewards. Everything else works without an account."}],compsH2:"Both Games, plus the years in between",compsLead:"Each season switches on as it starts and stays in the archive when it closes, and the year-round circuits run continuously alongside them.",primaryChips:["Summer Games","Winter Games"],secondaryChips:["Athletics","Swimming","Cycling","Alpine skiing","Figure skating","Speed skating","Biathlon"]}];function r0(e,a){let t=parseInt(e.slice(1),16);return`rgba(${t>>16&255},${t>>8&255},${255&t},${a})`}function r1({config:e}){let{slug:t,name:s,emoji:r}=e;return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:function(e){let[a,t,s]=e.hero,[r,n]=e.strip;return` 2072.sp-wrap{ 2073 --sp-accent:${e.accent};--sp-accent-dark:${e.accentDark}; 2074 --sp-bg:#f7f6f4;--sp-fg:#12141a;--sp-muted:#5c6270; 2075 --sp-border:rgba(18,20,26,0.10);--sp-surface:rgba(255,255,255,0.72); 2076 --sp-radius:16px; 2077 font-family:-apple-system,BlinkMacSystemFont,"Inter",system-ui,sans-serif; 2078 background:var(--sp-bg);color:var(--sp-fg); 2079 -webkit-font-smoothing:antialiased; 2080} 2081.sp-wrap *,.sp-wrap *::before,.sp-wrap *::after{box-sizing:border-box} 2082 2083/* nav */ 2084.sp-nav{ 2085 position:sticky;top:0;z-index:100;height:64px;padding:0 clamp(20px,4vw,48px); 2086 display:flex;align-items:center;justify-content:space-between; 2087 background:rgba(247,246,244,0.82);backdrop-filter:blur(18px); 2088 border-bottom:1px solid var(--sp-border); 2089} 2090.sp-nav-logo{display:flex;align-items:center;gap:10px;text-decoration:none;color:var(--sp-fg)} 2091.sp-nav-logo-icon{ 2092 width:30px;height:30px;border-radius:8px; 2093 background:linear-gradient(135deg,var(--sp-accent) 0%,var(--sp-accent-dark) 100%); 2094 display:flex;align-items:center;justify-content:center;font-size:16px;line-height:1; 2095} 2096.sp-nav-logo span{font-size:1rem;font-weight:700;letter-spacing:-.02em} 2097.sp-nav-links{display:flex;align-items:center;gap:16px;font-size:.84rem} 2098.sp-nav-links a{color:var(--sp-muted);text-decoration:none;transition:color .15s} 2099.sp-nav-links a:hover{color:var(--sp-fg)} 2100@media(max-width:600px){.sp-nav-links{display:none}} 2101 2102/* hero */ 2103.sp-hero{ 2104 padding:72px clamp(20px,4vw,48px) 64px; 2105 background:linear-gradient(160deg,${a} 0%,${t} 55%,${s} 100%); 2106 border-bottom:1px solid var(--sp-border); 2107} 2108.sp-hero-inner{max-width:880px;margin:0 auto;text-align:center} 2109.sp-hero-badge{ 2110 display:inline-flex;align-items:center;gap:8px;padding:5px 14px;border-radius:999px; 2111 background:${r0(e.accent,.1)};border:1px solid ${r0(e.accent,.24)}; 2112 font-size:.72rem;font-weight:650;letter-spacing:.08em;text-transform:uppercase; 2113 color:var(--sp-accent-dark);margin-bottom:20px; 2114} 2115.sp-hero-h1{font-size:clamp(2.1rem,5vw,3.3rem);font-weight:800;letter-spacing:-.03em;line-height:1.05;margin-bottom:18px} 2116.sp-hero-sub{font-size:clamp(.97rem,2vw,1.1rem);color:var(--sp-muted);line-height:1.75;max-width:60ch;margin:0 auto 32px} 2117.sp-cta-row{display:flex;flex-wrap:wrap;gap:12px;justify-content:center} 2118.sp-btn{ 2119 display:inline-flex;align-items:center;gap:8px;
2119padding:13px 26px;border-radius:999px; 2120 font-size:.88rem;font-weight:650;text-decoration:none;transition:transform .18s,box-shadow .18s; 2121} 2122.sp-btn:hover{transform:translateY(-2px)} 2123.sp-btn-primary{background:var(--sp-accent);color:#fff;box-shadow:0 8px 24px -8px ${r0(e.accent,.5)}} 2124.sp-btn-outline{background:rgba(255,255,255,0.6);color:var(--sp-fg);border:1.5px solid var(--sp-border);backdrop-filter:blur(8px)} 2125.sp-store-row{display:flex;flex-wrap:wrap;gap:14px;justify-content:center;align-items:center;margin-top:22px} 2126.sp-store-row a{display:inline-flex;transition:transform .18s} 2127.sp-store-row a:hover{transform:translateY(-1px)} 2128.sp-platforms{margin-top:14px;font-size:.78rem;color:var(--sp-muted);letter-spacing:.02em} 2129 2130/* features */ 2131.sp-features{padding:clamp(56px,8vw,80px) clamp(20px,4vw,48px)} 2132.sp-features-inner{max-width:1000px;margin:0 auto} 2133.sp-section-label{font-size:.68rem;font-weight:650;letter-spacing:.2em;text-transform:uppercase;color:var(--sp-accent);margin-bottom:12px} 2134.sp-section-h2{font-size:clamp(1.7rem,3.5vw,2.3rem);font-weight:760;letter-spacing:-.025em;margin-bottom:14px} 2135.sp-section-lead{font-size:1rem;color:var(--sp-muted);max-width:58ch;line-height:1.75;margin-bottom:40px} 2136.sp-grid{display:grid;gap:18px} 2137@media(min-width:640px){.sp-grid{grid-template-columns:repeat(2,1fr)}} 2138@media(min-width:900px){.sp-grid{grid-template-columns:repeat(3,1fr)}} 2139.sp-card{padding:26px;border-radius:var(--sp-radius);background:var(--sp-surface);border:1px solid var(--sp-border);backdrop-filter:blur(8px)} 2140.sp-card-icon{font-size:1.5rem;margin-bottom:12px} 2141.sp-card h3{font-size:1rem;font-weight:680;margin-bottom:6px} 2142.sp-card p{font-size:.88rem;color:var(--sp-muted);line-height:1.65;margin:0} 2143 2144/* competitions strip */ 2145.sp-comps{ 2146 padding:clamp(48px,6vw,64px) clamp(20px,4vw,48px); 2147 background:linear-gradient(160deg,${r} 0%,${n} 100%);color:rgba(255,255,255,.9); 2148} 2149.sp-comps-inner{max-width:1000px;margin:0 auto} 2150.sp-comps .sp-section-h2{color:#fff;margin-bottom:8px} 2151.sp-comps-lead{font-size:.95rem;color:rgba(255,255,255,.6);max-width:56ch;line-height:1.7;margin-bottom:0} 2152.sp-chip-row{display:flex;flex-wrap:wrap;gap:10px;margin-top:26px} 2153.sp-chip{ 2154 padding:9px 16px;border-radius:999px;font-size:.84rem;font-weight:600; 2155 background:rgba(255,255,255,0.07);border:1px solid rgba(255,255,255,0.12);color:#fff; 2156} 2157.sp-chip-marquee{background:${r0(e.accent,.16)};border-color:${r0(e.accent,.38)}} 2158 2159/* footer */ 2160.sp-footer{ 2161 background:${n};color:rgba(255,255,255,.55); 2162 padding:36px clamp(20px,4vw,48px) 24px;font-size:.82rem; 2163 border-top:1px solid rgba(255,255,255,.06); 2164} 2165.sp-footer-inner{max-width:1000px;margin:0 auto;display:flex;flex-wrap:wrap;gap:16px;justify-content:space-between;align-items:center} 2166.sp-footer-links{display:flex;flex-wrap:wrap;gap:12px} 2167.sp-footer-links a{color:rgba(255,255,255,.45);text-decoration:none;transition:color .15s} 2168.sp-footer-links a:hover{color:#fff} 2169`}(e.palette)}}),(0,a.jsxs)("div",{className:"sp-wrap",children:[(0,a.jsxs)("nav",{className:"sp-nav","aria-label":`${s} navigation`,children:[(0,a.jsxs)("a",{href:`/${t}`,className:"sp-nav-logo","aria-label":`${s} home`,children:[(0,a.jsx)("div",{className:"sp-nav-logo-icon","aria-hidden":!0,children:r}),(0,a.jsx)("span",{children:s})]}),(0,a.jsxs)("div",{className:"sp-nav-links",children:[(0,a.jsx)("a",{href:`/${t}/manual`,children:"Manual"}),(0,a.jsx)("a",{href:`/${t}/privacypolicy`,children:"Privacy"}),(0,a.jsx)("a",{href:`/${t}/terms`,children:"Terms"}),(0,a.jsx)("a",{href:rZ,children:"Support"})]})]}),(0,a.jsx)("section",{className:"sp-hero",children:(0,a.jsxs)("div",{className:"sp-hero-inner",children:[(0,a.jsxs)("div",{className:"sp-hero-badge",children:[r," ",e.badge]}),(0,a.jsxs)("h1",{className:"sp-hero-h1",children:[e.headline[0],(0,a.jsx)("br",{}),e.headline[1]]}),(0,a.jsx)("p",{className:"sp-hero-sub",children:e.sub}),(0,a.jsx)("div",{className:"sp-cta-row",children:e.ctas.map(e=>(0,a.jsx)("a",{href:e.href,className:`sp-btn ${e.primary?"sp-btn-primary":"sp-btn-outline"}`,children:e.label},e.href))}),e.stores?.length?(0,a.jsx)("div",{className:"sp-store-row",children:e.stores.map(e=>(0,a.jsx)("a",{href:e.url,target:"_blank",rel:"noopener noreferrer",children:(0,a.jsx)("img",{src:e.badge,alt:e.alt,width:e.width,height:40})},e.url))}):null,(0,a.jsx)("p",{className:"sp-platforms",children:e.platforms})]})}),(0,a.jsx)("section",{className:"sp-features",children:(0,a.jsxs)("div",{className:"sp-features-inner",children:[(0,a.jsx)("p",{className:"sp-section-label",children:"Features"}),(0,a.jsx)("h2",{className:"sp-section-h2",children:e.featuresH2}),(0,a.jsx)("p",{className:"sp-section-lead",children:e.featuresLead}),(0,a.jsx)("div",{className:"sp-grid",children:e.features.map(e=>(0,a.jsxs)("article",{className:"sp-card",children:[(0,a.jsx)("p",{className:"sp-card-icon","aria-hidden":!0,
2169children:e.icon}),(0,a.jsx)("h3",{children:e.title}),(0,a.jsx)("p",{children:e.body})]},e.title))})]})}),(0,a.jsx)("section",{className:"sp-comps","aria-label":"Competitions covered",children:(0,a.jsxs)("div",{className:"sp-comps-inner",children:[(0,a.jsx)("p",{className:"sp-section-label",children:"Competitions"}),(0,a.jsx)("h2",{className:"sp-section-h2",children:e.compsH2}),(0,a.jsx)("p",{className:"sp-comps-lead",children:e.compsLead}),(0,a.jsxs)("div",{className:"sp-chip-row",children:[e.primaryChips.map(e=>(0,a.jsx)("span",{className:"sp-chip sp-chip-marquee",children:e},e)),e.secondaryChips.map(e=>(0,a.jsx)("span",{className:"sp-chip",children:e},e))]})]})}),(0,a.jsx)("footer",{className:"sp-footer",children:(0,a.jsxs)("div",{className:"sp-footer-inner",children:[(0,a.jsxs)("span",{children:["© ",new Date().getFullYear()," Soft Flourish"]}),(0,a.jsxs)("div",{className:"sp-footer-links",children:[(0,a.jsx)("a",{href:`/${t}/privacypolicy`,children:"Privacy Policy"}),(0,a.jsx)("a",{href:`/${t}/terms`,children:"Terms of Use"}),(0,a.jsx)("a",{href:rZ,children:"Contact"}),(0,a.jsx)("a",{href:"/",children:"softflourish.com"})]})]})})]})]})}function r2({app:e}){let t=`https://softflourish.com/${e.slug}`;return(0,a.jsxs)(rI,{title:`${e.name} Privacy Policy`,updated:"August 8, 2026",children:[(0,a.jsxs)("p",{children:[e.name,"(âthe appâ) is developed and published by Soft Flourish, an independent software studio and publishing house based in California, USA. This page explains what ",e.name,"collects, why, and how it's used â it supplements, and is consistent with, Soft Flourish's"," ",(0,a.jsx)("a",{href:"/privacy",children:"general privacy policy"}),"."]}),(0,a.jsx)("h2",{children:"What this policy covers"}),(0,a.jsxs)("p",{children:[e.name," is a single app covering ",e.competitions,". Each of those is a competition inside ",e.name," â they are not separate apps and do not have separate privacy policies. This one page applies to all of them, on every platform ",e.name," ships on."]}),(0,a.jsx)("h2",{children:"Sign-in is optional"}),(0,a.jsxs)("p",{children:["All live ",e.sport," scores, standings, brackets, and news in ",e.name," are available without creating an account, for every competition it covers. Signing in only unlocks predictions, personalized alerts, and the rewards program."]}),(0,a.jsx)("h2",{children:"Information we collect"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Account information:"})," Email address and a unique user ID when you sign in with Apple, Google, or email/password (via Firebase Authentication). Google and Apple provide only your name, email, and â for Google â profile picture; we never request access to anything else from those accounts."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Purchase history:"})," Your subscription status (active/expired/plan) so the app can",e.hasAds?" remove ads for Pro subscribers and unlock Pro features":" unlock Pro features",". Billing itself is handled entirely by Apple/Google â we never see your payment details."]}),e.hasAds?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Advertising (free tier only):"})," ",e.name," shows banner",e.hasRewardedAds?", interstitial, and rewarded ads":" and interstitial ads"," via Google AdMob to free-tier users; ads are removed entirely for Pro subscribers. All ad requests are non-personalized â we do not use your advertising identifier (IDFA) or any tracking for ad targeting, and you will not see an App Tracking Transparency prompt because of this."]}),e.hasRewardedAds?(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Rewards program:"}),"If you watch a rewarded ad to earn in-app points, completion is verified through Google AdMob's server-to-server callback to our backend â never trusted from the device alone â and the point balance is tied to your account."]}):(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Rewards program:"})," Points you earn from predictions, brackets and daily check-ins are tied to your account and awarded by our backend, never by the device. ",e.name," has no watch-an-ad-for-points feature, so no ad
2169you see ever credits your balance."]})]}):(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Rewards program:"})," Points you earn from predictions, brackets and daily check-ins are tied to your account and awarded by our backend, never by the device. ",e.name," carries no advertising SDK and shows no ads, so there is no ad identifier to collect and no App Tracking Transparency prompt."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Push notifications:"}),"If you allow notifications, we store a device token (via Firebase Cloud Messaging) to send match alerts you've opted into. You can disable this anytime in system Settings."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Analytics and diagnostics:"})," Firebase Analytics records which screens and features are used, plus an app-instance identifier and the coarse, city-level location Google derives from your IP address. ",e.name," links no crash-reporting SDK, so the only crash data that exists is what the App Store and Google Play collect themselves, under their own policies."]})]}),(0,a.jsxs)("p",{children:["We do ",(0,a.jsx)("strong",{children:"not"})," collect device location (no location permission is requested â the city-level estimate above is derived from your IP address by Google, not read from your device), health, contacts, or browsing history, and we do not sell your personal information to anyone."]}),(0,a.jsx)("h2",{children:"Signing in with Google or Apple"}),(0,a.jsxs)("p",{children:["Google sign-in for ",e.name,"uses Firebase's federated sign-in flow â you'll see a secure system web sheet reading âto continue to Soft Flourishâ, the shared consent screen used across every Soft Flourish app. We receive only your basic profile (name, email, and, for Google, profile picture) to create and secure your account. Our use of data from Google Sign-In adheres to the"," ",(0,a.jsx)("a",{href:"https://developers.google.com/terms/api-services-user-data-policy",target:"_blank",rel:"noopener noreferrer",children:"Google API Services User Data Policy"}),", including its Limited Use requirements â we never use it for advertising and never sell or share it with third parties except as needed to run the service or comply with law."]}),(0,a.jsx)("h2",{children:"Data retention"}),(0,a.jsx)("p",{children:"Account data is kept while your account is active. If you delete your account, we delete or anonymize associated personal data within 30 days, except where retention is required by law."}),(0,a.jsx)("h2",{children:"Your rights"}),(0,a.jsx)("p",{children:"You may access, correct, delete, or export your personal data at any time. California residents have rights under the CCPA; EEA/UK residents have rights under the GDPR."}),(0,a.jsx)("div",{className:"sf-legal-box",children:(0,a.jsxs)("p",{children:["To delete your account and its data, follow the steps on"," ",(0,a.jsxs)("a",{href:`${t}/deleteaccount`,children:[e.slug,"/deleteaccount"]})," â you can do it inside the app, and you do not need to contact us first. For any other privacy request, use our ",(0,a.jsx)("a",{href:"/contact",children:"contact form"})," ","with the subject âPrivacy Request â ",e.name,"â and the email address associated with your account. We respond within 30 days."]})}),(0,a.jsx)("h2",{children:"Children's privacy"}),(0,a.jsxs)("p",{children:[e.name," is not directed to children under 13 (or under 16 in the EEA). We do not knowingly collect personal information from children. If you believe a child has provided us data, contact us and we will delete it promptly."]}),(0,a.jsx)("h2",{children:"Changes to this policy"}),(0,a.jsx)("p",{children:"We may update this page from time to time. We'll post the revised version here with a new date."}),(0,a.jsx)("h2",{children:"Contact us"}),(0,a.jsxs)("p",{children:["Questions: ",(0,a.jsx)("a",{href:"/contact",children:"Contact form"}),(0,a.jsx)("br",{}),"Terms of Use: ",(0,a.jsxs)("a",{href:`${t}/terms`,children:[e.slug,"/terms"]}),(0,a.jsx)("br",{}),"General policy: ",(0,a.jsx)("a",{href:"/privacy",children:"softflourish.com/privacy"})]})]})}function r6({app:e}){return(0,a.jsxs)(rI,{title:`${e.name} Support`,updated:"August 15, 2026",children:[(0,a.jsxs)("p",{children:[e.name," is developed and published by Soft Flourish. This page is the fastest route to a human â email"," ",(0,a.jsx)("a",{href:"mailto:[email protected]",children:"[email protected]"})," with your device, your OS version, and what you were doing when the problem happened."]}),(0,a.jsx)("h2",{children:"Common questions"}),(0,a.jsx)("h3",{children:"Scores or standings look wrong"}),(0,a.jsx)("p",{children:"Live data is pulled from the competition feeds and refreshes on a
2169schedule; a match that has just finished can take a few minutes to settle. If a figure is still wrong after that, send the fixture and what you expected to see â that is usually enough to trace it."}),(0,a.jsx)("h3",{children:"The app is free â what is the catch?"}),(0,a.jsxs)("p",{children:["There is none. ",e.name," is free, and an account is optional â everything the app does day to day works without one. It shows ads, and it never sells personal data. What is collected and why is set out in the"," ",(0,a.jsx)("a",{href:`/${e.slug}/privacypolicy`,children:"privacy policy"}),"."]}),(0,a.jsx)("h3",{children:"Notifications"}),(0,a.jsxs)("p",{children:["Turn them on or off in your device settings for ",e.name,". The app does not require them to work."]}),(0,a.jsx)("h3",{children:"Deleting your data"}),(0,a.jsxs)("p",{children:["If you never signed in, there is nothing tied to your identity to delete. If you did, ",(0,a.jsx)("a",{href:`/${e.slug}/deleteaccount`,children:"delete your account and data"}),"â that page lists the in-app steps, the email route, and exactly what is erased. Either way, ad and analytics identifiers are reset from your deviceâs own privacy settings; the ",(0,a.jsx)("a",{href:`/${e.slug}/privacypolicy`,children:"privacy policy"})," names each one."]}),(0,a.jsx)("h2",{children:"Terms"}),(0,a.jsxs)("p",{children:["Use of ",e.name," is covered by its ",(0,a.jsx)("a",{href:`/${e.slug}/terms`,children:"terms of use"}),"."]})]})}function r5({app:e}){return(0,a.jsxs)(rI,{title:`${e.name} â User Manual`,updated:"September 2, 2026",children:[e.manualLeadSections?.length?(0,a.jsxs)("p",{children:["This manual covers both halves of ",e.name,": the tools for the game you play yourself, first, and then the professional game it follows â ",e.competitions,", each a competition you switch between inside the app rather than a separate download."]}):(0,a.jsxs)("p",{children:[e.name," covers ",e.competitions," â each is a competition you switch between inside the app, not a separate download."]}),e.manualLeadSections?.map(e=>(0,a.jsxs)(r.Fragment,{children:[(0,a.jsx)("h2",{children:e.heading}),(0,a.jsx)("p",{children:e.body})]},e.heading)),(0,a.jsx)("h2",{children:"Picking a competition"}),(0,a.jsxs)("p",{children:["Use the competition switcher to move between the cups, leagues, and series ",e.name," ","covers. A new competition switches on automatically the moment its season starts, and stays available in the archive once it ends â nothing to download or reinstall for a new edition."]}),(0,a.jsx)("h2",{children:"Live scores"}),(0,a.jsxs)("p",{children:["Scores update in real time as ",e.sport," is played. Live data is pulled from competition feeds on a refresh schedule, so a result that just finished can take a few minutes to settle before it is final."]}),(0,a.jsx)("h2",{children:"Standings and tables"}),(0,a.jsx)("p",{children:"Group and league tables update as results land, with qualification and relegation zones highlighted where the competition has them."}),(0,a.jsx)("h2",{children:"Fixtures and results"}),(0,a.jsx)("p",{children:"The full schedule for every competition you follow, with completed results kept in the same place â no separate results archive to hunt through."}),(0,a.jsx)("h2",{children:"Past editions"}),(0,a.jsx)("p",{children:"A finished competition does not disappear. Past editions stay one tap away in the archive, ready to browse any time."}),(0,a.jsx)("h2",{children:"Predictions and rewards"}),(0,a.jsxs)("p",{children:["Sign in to call outcomes and earn rewards. Everything else in ",e.name," works without an account."]}),e.hasRewardedAds?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("h2",{children:"Earning extra rewards"}),(0,a.jsxs)("p",{children:[e.name," has a rewards screen where you can watch a short ad in exchange for extra points. It only ever plays when you choose to tap it â never automatically."]})]}):null,(0,a.jsx)("h2",{children:"Notifications"}),(0,a.jsxs)("p",{children:["Turn match or result notifications on or off in your deviceâs settings for"," ",e.name,". The app works without them."]}),(0,a.jsx)("h2",{children:"If something isnât working"}),(0,a.jsxs)("p",{children:["See ",(0,a.jsx)("a",{href:`/${e.slug}/support`,children:"support"})," for wrong-looking scores, notifications, and data questions."]})]})}function r8({app:e}){let t=`https://softflourish.com/${e.slug}`;return(0,a.jsxs)(rI,{title:`${e.name} Terms of Use`,updated:"August 8, 2026",children:[(0,a.jsxs)("p",{children:["These Terms of Use govern your use of ",e.name,", published by Soft Flourish (âweâ, âusâ, or âourâ). By downloading, installing, or using ",e.name,", you agree to these Terms and to Soft Flourish's"," ",(0,a.jsx)("a",{href:`${t}/privacypolicy`,children:"Privacy Policy"}),"."]}),(0,a.jsxs)("h2",{children:["What ",e.name," provides"]}),(0,a.jsxs)("p",{children:[e.name," delivers live scores, standings, brackets, schedules, news, and AI-assisted match analysis for"," ",e.sport,". One app covers ",e.competitions," â each is a competition you switch between inside"," ",e.name,", not a separate download, and these Terms apply to all of them equally."]}),(0,a.jsxs)("p",{children:["We source data from third-party providers and strive for accuracy, but scores and stats are provided for informational purposes only and are not an official record of any competition. ",e.name," is an independent app: it is not affiliated with, endorsed by, or sponsored by any league, federation, or governing body whose competitions it covers."]}),(0,a.jsx)("h2",{children:"Accounts"}
2169),(0,a.jsx)("p",{children:"An account is optional â required only for predictions, personalized alerts, and the rewards program. You are responsible for safeguarding your credentials and for activity under your account. If you sign in with Google or Apple, your use of that provider is also governed by its own terms."}),(0,a.jsx)("h2",{children:"Rewards program"}),(0,a.jsxs)("p",{children:["In-app points earned through predictions",e.hasRewardedAds?" or watching rewarded ads":", brackets and daily check-ins"," have no cash value, cannot be exchanged for currency, and are not a security or financial instrument. We may change point values, redemption options, or discontinue the program at any time."]}),(0,a.jsx)("h2",{children:"Subscriptions"}),(0,a.jsxs)("p",{children:[e.name," offers an optional auto-renewing Pro subscription that",e.hasAds?" removes ads and unlocks Pro features":" unlocks Pro features",". Billing, renewal, and cancellation are handled entirely by Apple or Google under their respective terms; refund requests follow the relevant store's refund policy."]}),e.hasAds?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("h2",{children:"Advertising"}),(0,a.jsxs)("p",{children:["Free-tier use of ",e.name," includes ads served via Google AdMob. Ads are non-personalized â no cross-app or cross-site tracking is used to select them."]})]}):null,(0,a.jsx)("h2",{children:"Acceptable use"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:["Use ",e.name," only for lawful purposes"]}),(0,a.jsx)("li",{children:"Do not reverse engineer, scrape, or attempt to extract data at scale"}),(0,a.jsx)("li",{children:"Do not interfere with the app's operation, security, or other users' experience"})]}),(0,a.jsx)("h2",{children:"Disclaimers"}),(0,a.jsxs)("p",{children:[e.name.toUpperCase(),"IS PROVIDED âAS ISâ WITHOUT WARRANTIES OF ANY KIND. MATCH DATA, PREDICTIONS, AND AI-GENERATED ANALYSIS ARE NOT OFFICIAL AND MAY CONTAIN ERRORS OR DELAYS."]}),(0,a.jsx)("h2",{children:"Limitation of liability"}),(0,a.jsxs)("p",{children:["TO THE MAXIMUM EXTENT PERMITTED BY LAW, SOFT FLOURISH IS NOT LIABLE FOR INDIRECT, INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OF ",e.name.toUpperCase(),"."]}),(0,a.jsx)("h2",{children:"Governing law"}),(0,a.jsx)("p",{children:"These Terms are governed by the laws of the State of California, USA. Disputes shall be resolved in courts located in Los Angeles County, California."}),(0,a.jsx)("h2",{children:"Contact"}),(0,a.jsxs)("p",{children:[(0,a.jsx)("a",{href:"/contact",children:"Contact form"}),(0,a.jsx)("br",{}),"Privacy Policy: ",(0,a.jsxs)("a",{href:`${t}/privacypolicy`,children:[e.slug,"/privacypolicy"]})]})]})}function r4({app:e}){return(0,a.jsxs)(rI,{title:`Delete Your ${e.name} Account & Data`,updated:"August 8, 2026",children:[(0,a.jsxs)("p",{children:["This page explains how to delete your account and its data in ",(0,a.jsx)("strong",{children:e.name}),", published by"," ",(0,a.jsx)("strong",{children:"Soft Flourish"}),". It applies to ",e.name," on Google Play and the Apple App Store, on every device the app runs on."]}),(0,a.jsxs)("p",{children:["Deleting an account is only relevant if you made one. Live ",e.sport," scores, standings, schedules, results and news in ",e.name," are free and need no account at all â signing in adds predictions, alerts for what you follow, and the rewards club."]}),(0,a.jsxs)("div",{className:"sf-legal-box",children:[(0,a.jsx)("p",{children:(0,a.jsx)("strong",{children:"Delete it from inside the app â the fastest way, and nothing to request."})}),(0,a.jsxs)("ol",{children:[(0,a.jsxs)("li",{children:["Open ",(0,a.jsx)("strong",{children:e.name})," and sign in with the account you want deleted, if you are not already signed in."]}),(0,a.jsxs)("li",{children:["Go to the ",(0,a.jsx)("strong",{children:"Home"})," tab and scroll to the account block â the one containing"," ",(0,a.jsx)("strong",{children:"Sign out"}),"."]}),(0,a.jsxs)("li",{children:["Tap ",(0,a.jsx)("strong",{children:"Delete Account"}),"."]}),(0,a.jsx)("li",{children:"Confirm on the warning prompt. Deleti
2169on happens immediately and cannot be undone."})]})]}),(0,a.jsx)("h2",{children:"If you cannot sign in"}),(0,a.jsxs)("p",{children:["Email ",(0,a.jsx)("a",{href:"mailto:[email protected]",children:"[email protected]"})," from the address linked to your"," ",e.name," account, with the subject ",(0,a.jsxs)("strong",{children:["âDelete ",e.name," Accountâ"]}),". We verify that you own the address, complete the deletion within 30 days, and confirm by email when it is done."]}),(0,a.jsx)("h2",{children:"What is deleted"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Your sign-in credential itself â email/password or federated Google sign-in."}),(0,a.jsx)("li",{children:"Your email address, display name, and account identifier (user ID)."}),(0,a.jsx)("li",{children:"Your rewards wallet and its full points ledger."}),(0,a.jsx)("li",{children:"Your predictions, brackets, and any prize claims."}),(0,a.jsx)("li",{children:"The teams, athletes and competitions you follow."}),(0,a.jsx)("li",{children:"Your push notification tokens and alert preferences."})]}),(0,a.jsx)("p",{children:"In-app deletion erases these straight away. There is no soft-delete, no recovery window, and no way to restore the account afterwards â signing up again with the same email creates a new, empty account."}),(0,a.jsx)("h2",{children:"What is kept, and for how long"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Purchase and subscription records"})," â held by Apple and Google, and by us in de-identified form, for up to ",(0,a.jsx)("strong",{children:"7 years"})," where tax, accounting, audit and fraud-prevention law requires it. They are not linked back to a deleted account for any other purpose."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Aggregated, anonymous analytics"})," â up to ",(0,a.jsx)("strong",{children:"14 months"}),". This carries no account identifier and cannot be traced back to you."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Encrypted operational backups"})," â deleted records may survive in backups for up to"," ",(0,a.jsx)("strong",{children:"30 days"})," before those backups roll off."]})]}),(0,a.jsx)("h2",{children:"Cancel your subscription first"}),(0,a.jsxs)("p",{children:["Deleting your account does ",(0,a.jsx)("strong",{children:"not"})," cancel a ",e.name," Pro subscription â billing belongs to the app store, not to us. Cancel it in ",(0,a.jsx)("strong",{children:"Google Play â Payments & subscriptions â Subscriptions"})," ","or ",(0,a.jsx)("strong",{children:"iPhone Settings â your name â Subscriptions"})," before you delete, or it keeps renewing."]}),(0,a.jsx)("h2",{children:"Deleting some data without deleting your account"}),(0,a.jsxs)("p",{children:["You can also keep the account and have specific data erased. Email"," ",(0,a.jsx)("a",{href:"mailto:[email protected]",children:"[email protected]"})," from the address linked to your account with the subject ",(0,a.jsxs)("strong",{children:["âDelete ",e.name," Dataâ"]}),", saying what to remove â your predictions, your follows, your rewards history, or all of them â and we complete it within 30 days and confirm by email."]}),(0,a.jsx)("h2",{children:"Contact"}),(0,a.jsxs)("p",{children:[(0,a.jsx)("a",{href:"mailto:[email protected]",children:"[email protected]"})," ·"," ",(0,a.jsxs)("a",{href:`/${e.slug}/privacypolicy`,children:[e.name," Privacy Policy"]})," ·"," ",(0,a.jsxs)("a",{href:`/${e.slug}/terms`,children:[e.name," Terms of Use"]})]})]})}function r3(){return(0,a.jsxs)(rI,{title:"SurfaceIQ â Wall & Floor Scan",updated:"August 15, 2026",children:[(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"SurfaceIQ turns your phone into a precision surface scanner."})," Point it at a wall, floor, panel, appliance or vehicle and it finds cracks, stains, dents, discoloration and wear â then marks each anomaly right on your photo with a severity rating."]}),(0,a.jsxs)("div",{className:"sf-legal-box",children:[(0,a.jsx)("p",{style:{marginBottom:14},children:(0,a.jsx)("strong",{children:"Free to download on iPhone and Android."})}),(0,a.jsxs)("p",{style:{display:"flex",flexWrap:"wrap",gap:14,alignItems:"center"},children:[(0,a.jsx)("a",{href:"https://apps.apple.com/app/id6787247553",target:"_blank",rel:"noopener noreferrer",style:{display:"inline-flex"},children:(0,a.jsx)("img",{src:"/surfaceiq/app-store-badge.svg",alt:"Download on the App Store",width:120,height:40})}),(0,a.jsx)("a",{href:"https://play.google.com/store/apps/details?id=com.softflourish.surfaceiq",target:"_blank",rel:"noopener noreferrer",style:{display:"inline-flex"},children:(0,a.jsx)("img",{src:"/surfaceiq/google-play-badge.png",alt:"Get it on Google Play",width:135,height:40})})]})]}),(0,a.jsx)("h2",{children:"What it does"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Findings on the real photo."})," Every flaw is pinned to the image you captured, not described
2169in the abstract."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Dents and bulges you can measure."})," On newer iPhones the app measures how far a surface has moved instead of estimating it from the picture."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Before/after baselines."})," Compare a surface against an earlier scan to show what changed â move-in versus move-out, before versus after a repair."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Exportable reports."})," A PDF per inspection, in a template that suits the job."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"A phone-only Toolbox."})," Stud and wire finder, tap sounding, moisture screening, leak listener, vibration check, and a spirit level with multi-point floor survey â all from the phone itself, with nothing to buy or plug in."]})]}),(0,a.jsx)("h2",{children:"Private by design"}),(0,a.jsxs)("p",{children:["All image analysis runs on your device. Your photos are not uploaded to be processed and are not used to train anything. See the"," ",(0,a.jsx)("a",{href:"/surfaceiq/privacypolicy",children:"privacy policy"})," for the detail."]}),(0,a.jsx)("h2",{children:"Honest about its limits"}),(0,a.jsx)("p",{children:"SurfaceIQ is an indicative screening tool, not a certified instrument. It is built to say what it measured and how confident it is â and to say plainly when a mode is not available on your device rather than return a confident-looking guess."}),(0,a.jsx)("h2",{children:"Pricing"}),(0,a.jsxs)("p",{children:["Free to scan, with monthly credits and no account needed to try it. SurfaceIQ Pro is an auto-renewable monthly subscription; see the"," ",(0,a.jsx)("a",{href:"/surfaceiq/terms",children:"terms of use"}),". Full walkthrough:"," ",(0,a.jsx)("a",{href:"/surfaceiq/manual",children:"user manual"}),". Questions:"," ",(0,a.jsx)("a",{href:"/surfaceiq/support",children:"support"}),"."]})]})}function r7(){return(0,a.jsxs)(rI,{title:"SurfaceIQ â Privacy Policy",updated:"July 23, 2026",children:[(0,a.jsxs)("p",{children:["This Privacy Policy explains how ",(0,a.jsx)("strong",{children:"SurfaceIQ"}),"(âthe appâ) collects, uses, and shares information. SurfaceIQ is published by Soft Flourish, an independent software studio and publishing house based in California, USA. It supplements the general"," ",(0,a.jsx)("a",{href:"/privacy",children:"Soft Flourish Privacy Policy"})," and controls where the two differ for this app."]}),(0,a.jsx)("p",{children:"By using SurfaceIQ you agree to this policy. If you do not agree, please do not use the app."}),(0,a.jsx)("h2",{children:"What SurfaceIQ does"}),(0,a.jsx)("p",{children:"SurfaceIQ uses your device camera (and, on supported devices, LiDAR/ARCore depth) to scan surfaces â walls, floors, panels, equipment, vehicles â and highlight visual anomalies such as cracks, stains, dents, and wear."}),(0,a.jsx)("h2",{children:"Your photos stay on your device"}),(0,a.jsx)("div",{className:"sf-legal-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"Raw scan imagery never leaves your device."})," Anomaly detection runs entirely on-device. We do not upload, store, or have access to your captured photos or camera feed. If you subscribe to SurfaceIQ Pro and enable cloud sync, only your ",(0,a.jsx)("em",{children:"findings and report metadata"})," (e.g. anomaly type, severity, measurements) are synced to your account â not the underlying images, unless you explicitly export/share a report yourself."]})}),(0,a.jsx)("h2",{children:"Information we collect"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Account information:"})," your email address and an account identifier (user ID) when you create an account or sign in, via Firebase Authentication. Used only to operate your account and sync your data."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Purchase information:"})," your subscription status and purchase history for SurfaceIQ Pro, from Apple or Google. We never receive your full payment card details â billing is handled by the app store."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Findings & reports (Pro cloud sync):"})," scan metadata you choose to save to your account."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Advertising & diagnostics (free tier):"})," to show ads to free users, the Google AdMob SDK collects an advertising device identifier and related advertising/usage and diagnostic data. See below."]})]}),(0,a.jsx)("h2",{children:"Advertising and tracking"}),(0,a.jsxs)("p",{children:["The free version of SurfaceIQ shows ads through Google AdMob. To do this, AdMob collects a device advertising identifier and information about the ads you see and interact with, and may derive coarse (city-level) location from your IP address, in order to serve and measure advertising. This identifier may be used to track y
2169ou across apps and websites owned by other companies. On iOS, we ask for your permission first via Apple's App Tracking Transparency prompt â you can decline and continue using every feature. ",(0,a.jsx)("strong",{children:"SurfaceIQ Pro is ad-free"})," and performs no advertising tracking."]}),(0,a.jsxs)("p",{children:["AdMob's use of data is governed by"," ",(0,a.jsx)("a",{href:"https://policies.google.com/technologies/partner-sites",target:"_blank",rel:"noopener noreferrer",children:"Google's Privacy & Terms"}),". You can reset or limit your advertising identifier in your device settings."]}),(0,a.jsx)("h2",{children:"How we use information"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Authenticate your account and sync your saved findings across your devices"}),(0,a.jsx)("li",{children:"Provide and manage the SurfaceIQ Pro subscription and free-tier limits"}),(0,a.jsx)("li",{children:"Show and measure ads to free-tier users"}),(0,a.jsx)("li",{children:"Diagnose crashes and improve reliability"}),(0,a.jsx)("li",{children:"Comply with legal obligations"})]}),(0,a.jsx)("p",{children:"We do not sell your personal information."}),(0,a.jsx)("h2",{children:"Third-party services"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Google Firebase"})," (Authentication, Firestore, Storage) â account, sync, and backend."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Google AdMob"})," â advertising to free-tier users."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"RevenueCat"})," and the ",(0,a.jsx)("strong",{children:"Apple App Store / Google Play"})," â subscription validation and billing."]})]}),(0,a.jsx)("h2",{children:"Data retention & your rights"}),(0,a.jsxs)("p",{children:["You can delete your account and associated data at any time from inside the app, or by request â see"," ",(0,a.jsx)("a",{href:"/surfaceiq/deleteaccount",children:"Delete your account & data"}),", which lists exactly what is erased, what is retained, and for how long. In-app deletion is immediate; emailed requests complete within 30 days. You may request access to or correction of your data by contacting us."]}),(0,a.jsx)("h2",{children:"Children's privacy"}),(0,a.jsx)("p",{children:"SurfaceIQ is rated 4+ but is intended for a general audience and is not directed to children under 13."}),(0,a.jsx)("h2",{children:"Changes"}),(0,a.jsx)("p",{children:"We may update this policy; material changes will be reflected by the âLast updatedâ date above."}),(0,a.jsx)("h2",{children:"Contact"}),(0,a.jsxs)("p",{children:["Questions: ",(0,a.jsx)("a",{href:"mailto:[email protected]",children:"[email protected]"})," · Support:"," ",(0,a.jsx)("a",{href:"/surfaceiq/support",children:"softflourish.com/surfaceiq/support"})]})]})}function r9(){return(0,a.jsxs)(rI,{title:"SurfaceIQ â Support",updated:"July 23, 2026",children:[(0,a.jsxs)("p",{children:["This is the official support page for ",(0,a.jsx)("strong",{children:"SurfaceIQ"}),", the surface-anomaly scanner published by Soft Flourish. We're happy to help with scanning, subscriptions, or your account."]}),(0,a.jsx)("div",{className:"sf-legal-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"Contact support:"})," ",(0,a.jsx)("a",{href:"mailto:[email protected]",children:"[email protected]"}),(0,a.jsx)("br",{}),(0,a.jsx)("span",{style:{fontSize:".88rem",color:"var(--sf-muted)"},children:"We typically respond within one business day (MonâFri, U.S. Pacific Time)."})]})}),(0,a.jsx)("h2",{children:"Before you write"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Your device model and iOS/Android version"}),(0,a.jsx)("li",{children:"The SurfaceIQ app version (Settings â About)"}),(0,a.jsx)("li",{children:"For sign-in issues, whether you use Apple, Google, or email login"}),(0,a.jsx)("li",{children:"For a scan problem, the surface type and what you expected vs. what happened"})]}),(0,a.jsxs)("div",{className:"sf-support-cards",children:[(0,a.jsxs)("div",{className:"sf-support-card",children:[(0,a.jsx)("h3",{children:"Getting a good scan"}),(0,a.jsx)("p",{children:"Hold steady in good light and let the capture coach's ring turn green. Keep the surface roughly filling the frame; the on-screen guides help you frame and stay in range."})]}),(0,a.jsxs)("div",{className:"sf-support-card",children:[(0,a.jsx)("h3",{children:"SurfaceIQ Pro & free trial"}),(0,a.jsx)("p",{children:"Pro unlocks unlimited saved scans, baseline compare, PDF exp
2169ort, cloud sync, and removes ads â with a 7-day free trial, then monthly billing unless cancelled. Manage or cancel anytime in your App Store or Google Play subscription settings."})]}),(0,a.jsxs)("div",{className:"sf-support-card",children:[(0,a.jsx)("h3",{children:"Restore purchases"}),(0,a.jsx)("p",{children:"Use âRestore Purchasesâ in the app's settings. One subscription covers your account across your iPhone and Android devices when you sign in with the same account."})]}),(0,a.jsxs)("div",{className:"sf-support-card",id:"delete-account",children:[(0,a.jsx)("h3",{children:"Delete my SurfaceIQ account & data"}),(0,a.jsxs)("p",{children:["Sign in, then tap ",(0,a.jsx)("strong",{children:"Delete Account"}),"next to Sign Out on the app's home screen â deletion is immediate. If you cannot sign in, email"," ",(0,a.jsx)("a",{href:"mailto:[email protected]",children:"[email protected]"}),"with subject âDelete SurfaceIQ Accountâ from the address linked to your account and we complete it within 30 days. Full details of what is deleted and what is retained: ",(0,a.jsx)("a",{href:"/surfaceiq/deleteaccount",children:"Delete your account & data"}),"."]})]}),(0,a.jsxs)("div",{className:"sf-support-card",children:[(0,a.jsx)("h3",{children:"Privacy"}),(0,a.jsxs)("p",{children:["Raw photos stay on your device. See the"," ",(0,a.jsx)("a",{href:"/surfaceiq/privacypolicy",children:"SurfaceIQ Privacy Policy"})," for details."]})]}),(0,a.jsxs)("div",{className:"sf-support-card",children:[(0,a.jsx)("h3",{children:"Accessories & depth"}),(0,a.jsx)("p",{children:"Real-world depth measurement uses LiDAR/ARCore where available. Thermal/UV/moisture options appear only when supported hardware is connected."})]})]})]})}function ne(){return(0,a.jsxs)(rI,{title:"SurfaceIQ â User Manual",updated:"August 21, 2026",children:[(0,a.jsx)("p",{children:"Everything SurfaceIQ can do, and how to get a scan you can trust."}),(0,a.jsx)("div",{className:"sf-legal-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"Indicative, not certified."})," SurfaceIQ is a screening tool. It tells you what it measured and how confident it is, and it says plainly when a mode is not available on your device rather than guess. For a structural or safety-critical call, use a qualified professional."]})}),(0,a.jsx)("h2",{children:"Your first scan"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Choose a surface â wall, floor, panel, appliance, or vehicle."}),(0,a.jsx)("li",{children:"Hold the phone steady in good light. The capture coach's ring turns green when framing and distance are right."}),(0,a.jsx)("li",{children:"Fill the frame with the surface rather than the room around it."}),(0,a.jsx)("li",{children:"Capture. Findings appear pinned to your actual photo, each with a severity rating."})]}),(0,a.jsx)("h2",{children:"Reading a finding"}),(0,a.jsx)("p",{children:"Each flaw â a crack, stain, dent, patch of discoloration, or wear â is marked directly on the photo you took, not described separately. Where your device has LiDAR, a dent or bulge is measured in real distance rather than estimated from the picture; where it does not, the app says so rather than showing a number it cannot back up."}),(0,a.jsx)("h2",{children:"Before/after baselines"}),(0,a.jsx)("p",{children:"Save a scan as a baseline, then scan the same surface again later â move-in versus move-out, before a repair versus after â and SurfaceIQ shows exactly what changed between the two."}),(0,a.jsx)("h2",{children:"The Toolbox"}),(0,a.jsx)("p",{children:"A set of phone-only tools that need nothing extra to buy or plug in:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Stud and wire finder"})," â locate what is behind a wall before you drill."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Tap sounding"})," â listen for the hollow sound that flags a void or delamination."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Moisture screening"})," â a quick read on whether a surface is unexpectedly damp."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Leak listener"})," â picks up the sound pattern of a running leak."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Vibration check"})," â flags unusual vibration on equipment or a vehicle."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Spirit level with floor survey"})," â a multi-point level for checking a floor across several spots, not just one."]})]}),(0,a.jsx)("h2",{children:"Exporting a report"}),(0,a.jsx)("p",{children:"Turn any inspection into a PDF, in a template suited to the job â a single-room scan reads differently from a full move-in walkthrough. Export from the scan's detail screen."}),(0,a.jsx)("h2",{children:"Free vs. Pro"}),(0,a.jsxs)("p",{children:["Scanning itself is free, with a monthly credit balance and no account required to try it.",(0,a.jsx)("strong",{children:" SurfaceIQ Pro"}
2169)," unlocks unlimited saved scans, baseline compare, PDF export, cloud sync across your devices, and removes ads â with a 7-day free trial, then billed monthly until cancelled in your App Store or Google Play subscription settings. Full terms: ",(0,a.jsx)("a",{href:"/surfaceiq/terms",children:"terms of use"}),"."]}),(0,a.jsx)("h2",{children:"If something goes wrong"}),(0,a.jsxs)("p",{children:["Start with ",(0,a.jsx)("a",{href:"/surfaceiq/support",children:"support"})," â it covers getting a good scan, restoring a purchase across devices, and deleting your account. For anything else, email"," ",(0,a.jsx)("a",{href:"mailto:[email protected]",children:"[email protected]"})," with your device model, OS version, and what you expected to happen."]})]})}function na(){return(0,a.jsxs)(rI,{title:"SurfaceIQ â Terms of Use",updated:"July 23, 2026",children:[(0,a.jsxs)("p",{children:["These Terms govern your use of ",(0,a.jsx)("strong",{children:"SurfaceIQ"}),", published by Soft Flourish. They supplement the general ",(0,a.jsx)("a",{href:"/terms",children:"Soft Flourish Terms of Use"}),". By using SurfaceIQ you agree to these Terms."]}),(0,a.jsx)("h2",{children:"The service"}),(0,a.jsxs)("p",{children:["SurfaceIQ is a scanning and inspection aid. It highlights ",(0,a.jsx)("em",{children:"visible or geometric"}),"surface anomalies to assist your own judgment. It is not a substitute for a professional inspection, and findings are indicative, not guaranteed. Hidden conditions (moisture, mold, odor, chemical or structural issues) are âsuspectedâ signals that require appropriate professional verification."]}),(0,a.jsx)("h2",{children:"Accounts"}),(0,a.jsx)("p",{children:"You are responsible for your account credentials and for activity under your account. Provide accurate information and keep it current."}),(0,a.jsx)("h2",{children:"Subscriptions & purchases"}),(0,a.jsxs)("p",{children:["SurfaceIQ is free to download with an optional ",(0,a.jsx)("strong",{children:"SurfaceIQ Pro"})," auto-renewing subscription. Pro begins with a 7-day free trial, then bills monthly through Apple or Google until cancelled. Manage or cancel in your App Store / Google Play settings; store refund policies apply. Free-tier features include ads and usage limits."]}),(0,a.jsx)("h2",{children:"Acceptable use"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Do not use the app unlawfully or to infringe others' rights"}),(0,a.jsx)("li",{children:"Do not reverse-engineer, resell, or misrepresent the app or its output"}),(0,a.jsx)("li",{children:"Do not rely solely on the app where safety or code compliance requires a professional"})]}),(0,a.jsx)("h2",{children:"Disclaimers & liability"}),(0,a.jsx)("p",{children:"SurfaceIQ is provided âas isâ without warranties. To the maximum extent permitted by law, Soft Flourish is not liable for indirect or consequential damages, or for decisions made in reliance on the app's findings."}),(0,a.jsx)("h2",{children:"Governing law"}),(0,a.jsx)("p",{children:"These Terms are governed by the laws of the State of California, USA."}),(0,a.jsx)("h2",{children:"Contact"}),(0,a.jsxs)("p",{children:[(0,a.jsx)("a",{href:"mailto:[email protected]",children:"[email protected]"})," ·"," ",(0,a.jsx)("a",{href:"/surfaceiq/support",children:"SurfaceIQ Support"})]})]})}function nt(){return(0,a.jsxs)(rI,{title:"SurfaceIQ â Delete Your Account & Data",updated:"July 30, 2026",children:[(0,a.jsxs)("p",{children:["This page explains how to delete your account and associated data in ",(0,a.jsx)("strong",{children:"SurfaceIQ"}),", the surface-anomaly scanner published by ",(0,a.jsx)("strong",{children:"Soft Flourish"}),". It applies to the SurfaceIQ apps on Google Play and the Apple App Store."]}),(0,a.jsxs)("div",{className:"sf-legal-box",children:[(0,a.jsx)("p",{children:(0,a.jsx)("strong",{children:"Delete your account from inside the app â the fastest way, no request needed."})}),(0,a.jsxs)("ol",{children:[(0,a.jsxs)("li",{children:["Open ",(0,a.jsx)("strong",{children:"SurfaceIQ"})," on your phone."]}),(0,a.jsx)("li",{children:"Sign in with the account you want to delete, if you are not already signed in."}),(0,a.jsxs)("li",{children:["Go to the account area on the home screen (where ",(0,a.jsx)("strong",{children:"Sign Out"})," appears) and tap"," ",(0,a.jsx)("strong",{children:"Delete Account"}),"."]}),(0,a.jsx)("li",{children:"Confirm on the warning prompt. Deleti
2169on is immediate and cannot be undone."})]})]}),(0,a.jsx)("h2",{children:"If you cannot sign in"}),(0,a.jsxs)("p",{children:["Email ",(0,a.jsx)("a",{href:"mailto:[email protected]",children:"[email protected]"})," from the address linked to your SurfaceIQ account, with the subject ",(0,a.jsx)("strong",{children:"âDelete SurfaceIQ Accountâ"}),". We verify ownership of the address and complete the deletion within 30 days. We will confirm by email when it is done."]}),(0,a.jsx)("h2",{children:"What is deleted"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Your SurfaceIQ sign-in account itself (email/password, Google, or Apple sign-in)."}),(0,a.jsx)("li",{children:"Your email address and account identifier (user ID)."}),(0,a.jsx)("li",{children:"Your user profile record."}),(0,a.jsx)("li",{children:"Your workspace and everything in it: saved scan sessions, findings, baselines, and generated reports synced to your account."})]}),(0,a.jsx)("p",{children:"These are erased immediately when you use in-app deletion â they are not soft-deleted or held in a recovery window, and they cannot be restored afterwards."}),(0,a.jsx)("h2",{children:"What is kept, and for how long"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Purchase and subscription records"})," â retained by Apple and Google, and by us in de-identified form, for up to ",(0,a.jsx)("strong",{children:"7 years"})," where tax, accounting, audit, and fraud-prevention law requires it. These are not linked back to a deleted account for any other purpose."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Aggregated, anonymous analytics and crash diagnostics"})," â retained up to"," ",(0,a.jsx)("strong",{children:"14 months"}),". This data contains no account identifier and cannot be traced back to you."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Backups"})," â deleted records may persist in encrypted operational backups for up to"," ",(0,a.jsx)("strong",{children:"30 days"})," before those backups roll off, after which they are gone."]})]}),(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"Raw scan photos and camera imagery are never uploaded at all"}),", so there is nothing server-side to delete for them. Anything stored only on your phone is removed when you delete the app."]}),(0,a.jsx)("h2",{children:"Cancel your subscription first"}),(0,a.jsxs)("p",{children:["Deleting your SurfaceIQ account does ",(0,a.jsx)("strong",{children:"not"})," cancel a SurfaceIQ Pro subscription â billing is handled by the app store, not by us. Cancel it in ",(0,a.jsx)("strong",{children:"Google Play â Payments & subscriptions â Subscriptions"}),", or ",(0,a.jsx)("strong",{children:"iPhone Settings â your name â Subscriptions"}),", before deleting."]}),(0,a.jsx)("h2",{children:"Deleting some data without deleting your account"}),(0,a.jsxs)("p",{children:["You can also have specific data erased while keeping your account. Email"," ",(0,a.jsx)("a",{href:"mailto:[email protected]",children:"[email protected]"})," from the address linked to your account with the subject ",(0,a.jsx)("strong",{children:"âDelete SurfaceIQ Dataâ"}),", telling us which saved scans, findings, or reports to remove â or ask us to clear all synced scan data while leaving your sign-in intact. We complete these requests within 30 days and confirm by email."]}),(0,a.jsx)("h2",{children:"Contact"}),(0,a.jsxs)("p",{children:[(0,a.jsx)("a",{href:"mailto:[email protected]",children:"[email protected]"})," ·"," ",(0,a.jsx)("a",{href:"/surfaceiq/privacypolicy",children:"SurfaceIQ Privacy Policy"})," ·"," ",(0,a.jsx)("a",{href:"/surfaceiq/support",children:"SurfaceIQ Support"})]})]})}var ep=ed,eh=ed;let ns="#6247e0",nr="#15131c",nn="#585266",ni={$rc_monthly:{title:"Pro â Monthly",blurb:"Unlimited renaming, every folder · all of it still on your own machine"}};function no(){let e=(0,ei.getAuth)((0,eu.getFirebaseApp)());return e.tenantId=null,e}function nl(){let[t,s]=(0,r.useState)(null),[n,i]=(0,r.useState)(!1),[o,l]=(0,r.useState)(null),[c,d]=(0,r.useState)(""),[p,h]=(0,r.useState)(""),[m,u]=(0,r.useState)(!1),[f,g]=(0,r.useState)([]),[x,b]=(0,r.useState)(!1),[y,v]=(0,r.useState)(null),[w,j]=(0,r.useState)(!1),[k,N]=(0,r.useState)(!1),[S,A]=(0,r.useState)(null);(0,r.useEffect)(()=>
2169{l(new URLSearchParams(window.location.search).get("uid"));let e=(0,eo.onAuthStateChanged)(no(),e=>{s(e),i(!0)});return()=>e()},[]);let P=(0,r.useCallback)(async a=>{try{let{Purchases:t}=await e.A(323738),s=t.isConfigured()?t.getSharedInstance():t.configure({apiKey:"rcb_BFiMnWwWILPZlWMdqFRbNYaxqjQf",appUserId:a});t.isConfigured()&&await s.changeUser(a),v(await s.getCustomerInfo());let r=await s.getOfferings(),n=r.all["com.fenul.MagicRename.default"]??r.current;g(n?.availablePackages??[]),b(!0)}catch(e){A(e.message)}},[]);(0,r.useEffect)(()=>{t&&P(t.uid)},[t,P]);let z=e=>{N(!0),A(null),e().catch(e=>A(e.message)).finally(()=>N(!1))},T=null!==t&&null!==o&&t.uid!==o,F=y?.entitlements.active.pro!==void 0,C=function(e){let a=e?.entitlements.active.pro;if(!a?.expirationDate)return null;let t=a.expirationDate.toLocaleDateString();return a.willRenew?`Renews on ${t}`:`Access ends on ${t}`}(y),M=y?.managementURL??null;return(0,a.jsxs)("main",{style:{maxWidth:620,margin:"0 auto",padding:"48px 20px",color:nr,background:"#f8f7fd",minHeight:"100svh"},children:[(0,a.jsx)("p",{style:{marginBottom:24},children:(0,a.jsx)("a",{href:"/magicrename/support",style:{color:nn,textDecoration:"none",fontSize:14},children:"â Magic Rename"})}),(0,a.jsxs)("h1",{style:{fontSize:28,fontWeight:800},children:["Magic Rename ",(0,a.jsx)("span",{style:{color:ns},children:"account"})]}),(0,a.jsx)("p",{style:{color:nn,marginTop:8,lineHeight:1.6},children:"Manage Magic Rename Pro. Renaming three files a calendar month is free, with every feature unlocked; Pro removes the limit. One subscription covers every machine you sign into â Mac and Windows. Your files are read on your own computer either way, and nothing about them is ever uploaded."}),n?t?(0,a.jsxs)("section",{style:{marginTop:24,display:"grid",gap:12},children:[(0,a.jsxs)("div",{style:{padding:14,borderRadius:12,background:"rgba(255,255,255,0.80)",border:"1px solid rgba(21,19,28,0.10)"},children:["Signed in as ",(0,a.jsx)("strong",{children:t.email??t.uid})," â current plan:"," ",(0,a.jsx)("strong",{children:F?"Pro":"Free"}),C?(0,a.jsxs)("span",{style:{color:nn},children:[" · ",C]}):null]}),T?(0,a.jsxs)("div",{style:{padding:14,borderRadius:12,background:"rgba(98,71,224,0.08)",border:"1px solid rgba(98,71,224,0.35)",fontSize:14,lineHeight:1.6},children:[(0,a.jsx)("strong",{style:{color:ns},children:"Heads up:"})," the app that opened this page is signed into a different account than the browser is. Pro unlocks on the"," ",(0,a.jsx)("strong",{children:"account you buy it with"})," â either sign in here with the account the app is using, or sign into this account inside the app afterwards."]}):null,w?(0,a.jsx)("p",{style:{color:"#1c946c",fontWeight:600},children:"You're on Pro â choose âRestore purchasesâ in the app and the limit lifts."}):null,F?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("p",{style:{color:nn,lineHeight:1.6},children:"Pro is active. Open Magic Rename on any machine and sign in with this account â the three-files-a-month limit is lifted there too."}),M?(0,a.jsx)("a",{href:M,target:"_blank",rel:"noreferrer",style:{...np,display:"block",textDecoration:"none",fontWeight:700},children:"Manage or cancel subscription"}):null]}):x?0===f.length?(0,a.jsxs)("p",{style:{color:ns},children:["Buying Pro on the web isn't available right now. A subscription bought in the Mac app still works and unlocks here â or contact"," ",(0,a.jsx)("a",{href:"/contact",style:{color:ns},children:"support"}),"."]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("h2",{style:nc,children:"Magic Rename Pro"}),f.map(t=>{let s=ni[t.identifier],r=t.webBillingProduct?.currentPrice?.formattedPrice;return(0,a.jsxs)("button",{disabled:k,onClick:()=>z(async()=>{let{Purchases:a}=await e.A(323738);v((await a.getSharedInstance().purchase({rcPackage:t})).customerInfo),j(!0)}),style:np,children:[(0,a.jsxs)("span",{style:{display:"block",fontWeight:700},children:[s?.title??t.webBillingProduct?.displayName??t.identifier,r?(0,a.jsxs)("span",{style:{color:ns},children:[" · ",r]}):null]}),s?(0,a.jsx)("span",{style:{display:"block",fontSize:13,color:nn,marginTop:4},children:s.blurb}):null]},t.identifier)}),(0,a.jsx)("p",{style:{fontSize:13,color:nn,marginTop:4,lineHeight:1.6},children:"The subscription renews automatically until cancelled, and
2169you can cancel any time from this page. Pro unlocks in the app as soon as RevenueCat confirms it."})]}):(0,a.jsx)("p",{style:{color:"#9a94a8"},children:"Loading plansâ¦"}),(0,a.jsx)("button",{onClick:()=>z(async()=>{await (0,em.signOut)(no()),v(null),g([]),b(!1),j(!1)}),style:nm,children:"Sign out"})]}):(0,a.jsxs)("section",{style:{marginTop:24,display:"grid",gap:10},children:[(0,a.jsx)("button",{disabled:k,onClick:()=>z(async()=>{await (0,el.signInWithPopup)(no(),new ec.GoogleAuthProvider)}),style:nd,children:"Continue with Google"}),(0,a.jsx)("div",{style:{height:1,background:"rgba(21,19,28,0.10)",margin:"8px 0"}}),(0,a.jsx)("input",{placeholder:"Email",value:c,onChange:e=>d(e.target.value),style:nh}),(0,a.jsx)("input",{placeholder:"Password",type:"password",value:p,onChange:e=>h(e.target.value),style:nh}),(0,a.jsx)("button",{disabled:k||!c||!p,onClick:()=>z(async()=>{let e=no();m?await (0,eh.ab)(e,c,p):await (0,ep.ac)(e,c,p)}),style:{...nd,background:ns,borderColor:ns,color:"#fff"},children:m?"Create account":"Sign in"}),(0,a.jsx)("button",{onClick:()=>u(e=>!e),style:nm,children:m?"Have an account? Sign in":"New here? Create an account"}),(0,a.jsx)("p",{style:{fontSize:13,color:nn,margin:0,lineHeight:1.6},children:"Sign in with the same account you use inside the app â that is what lets a subscription follow you from your PC to your Mac."})]}):(0,a.jsx)("p",{style:{marginTop:24,color:"#9a94a8"},children:"Loadingâ¦"}),S&&(0,a.jsx)("p",{style:{color:"#b3261e",marginTop:16},children:S})]})}let nc={fontSize:15,fontWeight:800,marginTop:12,letterSpacing:"0.02em",textTransform:"uppercase",color:nr},nd={padding:"11px 16px",borderRadius:12,border:"1px solid rgba(21,19,28,0.14)",background:"#fff",color:nr,fontWeight:600,cursor:"pointer"},np={...nd,textAlign:"left",padding:"13px 16px"},nh={padding:"10px 12px",borderRadius:10,border:"1px solid rgba(21,19,28,0.14)",background:"#fff",color:nr},nm={background:"none",border:"none",color:ns,cursor:"pointer",fontSize:13,padding:4},nu="/contact",nf="[email protected]",ng="Last updated: August 2026",nx="macOS and Windows",nb=` 2170.mr-wrap{ 2171 --mr-violet:#6247e0;--mr-violet-deep:#4a34b8;--mr-green:#1c946c;--mr-violet-soft:#eeeaff; 2172 --mr-bg:#f8f7fd;--mr-fg:#15131c;--mr-muted:#585266; 2173 --mr-border:rgba(21,19,28,0.10);--mr-surface:rgba(255,255,255,0.80); 2174 --mr-radius:16px; 2175 font-family:-apple-system,BlinkMacSystemFont,"Inter",system-ui,sans-serif; 2176 background:var(--mr-bg);color:var(--mr-fg); 2177 -webkit-font-smoothing:antialiased; 2178} 2179.mr-wrap *,.mr-wrap *::before,.mr-wrap *::after{box-sizing:border-box} 2180 2181.mr-nav{ 2182 position:sticky;top:0;z-index:100;height:64px;padding:0 clamp(20px,4vw,48px); 2183 display:flex;align-items:center;justify-content:space-between; 2184 background:rgba(248,247,253,0.82);backdrop-filter:blur(18px); 2185 border-bottom:1px solid var(--mr-border); 2186} 2187.mr-nav-logo{display:flex;align-items:center;gap:10px;text-decoration:none;color:var(--mr-fg);font-weight:650} 2188.mr-nav-logo-icon{ 2189 width:30px;height:30px;border-radius:8px;flex:none; 2190 background:linear-gradient(160deg,var(--mr-violet),#9b6cf0); 2191 display:flex;align-items:center;justify-content:center;color:#fff;font-size:15px;font-weight:700; 2192} 2193.mr-nav-links{display:flex;align-items:center;gap:4px} 2194.mr-nav-links a{ 2195 font-size:.85rem;color:var(--mr-muted);text-decoration:none;padding:8px 12px;border-radius:999px; 2196} 2197.mr-nav-links a:hover{color:var(--mr-fg);background:rgba(98,71,224,0.09)} 2198 2199.mr-legal{padding:clamp(40px,6vw,72px) clamp(20px,4vw,48px) clamp(56px,8vw,96px)} 2200.mr-legal-inner{max-width:760px;margin:0 auto} 2201.mr-legal-inner h1{font-size:clamp(1.9rem,4vw,2.6rem);font-weight:740;letter-spacing:-.025em;line-height:1.12;margin:0 0 10px} 2202.mr-legal-inner h2{font-size:1.14rem;font-weight:680;letter-spacing:-.01em;margin:36px 0 10px} 2203.mr-legal-inner p,.mr-legal-inner li{font-size:.965rem;line-height:1.72;color:var(--mr-muted)} 2204.mr-legal-inner p{margin:0 0 14px} 2205.mr-legal-inner ul{margin:0 0 16px;padding-left:22px} 2206.mr-legal-inner li{margin:0 0 7px} 2207.mr-legal-inner strong{color:var(--mr-fg);font-weight:620} 2208.mr-legal-inner a{color:var(--mr-violet-deep);text-decoration:underline;text-underline-offset:2px} 2209.mr-updated{font-size:.83rem;color:var(--mr-muted);margin:0 0 28px !important} 2210.mr-legal-box{ 2211 background:var(--mr-violet-soft);border:1px solid rgba(98,71,224,0.22); 2212 border-radius:var(--mr-radius);padding:18px 22px;margin:0 0 26px; 2213} 2214.mr-legal-box p:last-child,.mr-legal-box ul:last-child{margin-bottom:0} 2215 2216.mr-table{width:100%;border-collapse:collapse;margin:0 0 20px;font-size:.9rem} 2217.mr-table th,.mr-table td{ 2218 text-align:left;padding:10px 12px;border-bottom:1px solid var(--mr-border); 2219 color:var(--mr-muted);line-height:1.55;vertical-align:top; 2220} 2221.mr-table th{color:var(--mr-fg);font-weight:640;font-size:.82rem;letter-spacing:.02em} 2222.mr-table td:first-child{color:var(--mr-fg);font-weight:560} 2223 2224/* ââ landing page (added 2026-08-15) âââââââââââââââââââââââââââââââââââââââ */ 2225.mr-hero{padding:clamp(56px,9vw,104px) clamp(20px,4vw,48px) clamp(40px,6vw,72px);text-align:center} 2226.mr-hero-inner{max-width:760px;margin:0 auto} 2227.mr-store-row{display:flex;flex-wrap:wrap;gap:18px;align-items:center;justify-content:center;margin-top:22px} 2228.mr-store-row a{display:inline-flex;transition:transform .18s} 2229.mr-store-row a:hover{transform:translateY(-1px)} 2230.mr-store-qr{width:104px;
2230height:104px;background:#fff;border-radius:10px;border:1px solid rgba(0,0,0,.10)} 2231.mr-badge{ 2232 display:inline-block;font-size:.78rem;font-weight:600;color:var(--mr-violet-deep); 2233 background:var(--mr-violet-soft);border-radius:999px;padding:6px 14px;margin-bottom:20px; 2234} 2235.mr-h1{ 2236 font-size:clamp(2rem,5.2vw,3.2rem);line-height:1.08;letter-spacing:-.02em; 2237 font-weight:750;margin:0 0 16px;text-wrap:balance; 2238} 2239.mr-h1 em{font-style:normal;color:var(--mr-violet)} 2240.mr-sub{font-size:clamp(1rem,2vw,1.14rem);line-height:1.6;color:var(--mr-muted);margin:0 auto 28px;max-width:60ch} 2241.mr-cta-row{display:flex;flex-wrap:wrap;gap:12px;justify-content:center;margin-bottom:14px} 2242.mr-btn{ 2243 display:inline-flex;align-items:center;justify-content:center;gap:8px; 2244 font-size:.95rem;font-weight:650;text-decoration:none;padding:13px 24px;border-radius:999px; 2245 border:1px solid transparent;transition:transform .12s ease; 2246} 2247.mr-btn:hover{transform:translateY(-1px)} 2248.mr-btn-primary{background:var(--mr-violet);color:#fff} 2249.mr-btn-outline{background:var(--mr-surface);color:var(--mr-fg);border-color:var(--mr-border)} 2250.mr-note{font-size:.83rem;color:var(--mr-muted);margin:0} 2251.mr-section{padding:clamp(40px,6vw,72px) clamp(20px,4vw,48px)} 2252.mr-section-inner{max-width:1000px;margin:0 auto} 2253.mr-label{ 2254 font-size:.75rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase; 2255 color:var(--mr-violet);margin:0 0 8px; 2256} 2257.mr-h2{font-size:clamp(1.45rem,3vw,2rem);letter-spacing:-.015em;font-weight:700;margin:0 0 12px;text-wrap:balance} 2258.mr-lead{font-size:1.02rem;line-height:1.65;color:var(--mr-muted);max-width:64ch;margin:0 0 28px} 2259.mr-grid{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(240px,1fr))} 2260.mr-card{ 2261 background:var(--mr-surface);border:1px solid var(--mr-border);border-radius:var(--mr-radius); 2262 padding:20px 22px; 2263} 2264.mr-card h3{font-size:1rem;font-weight:650;margin:0 0 6px} 2265.mr-card p{font-size:.9rem;line-height:1.55;color:var(--mr-muted);margin:0} 2266.mr-before-after{ 2267 display:grid;gap:10px;grid-template-columns:1fr auto 1fr;align-items:center; 2268 background:var(--mr-surface);border:1px solid var(--mr-border);border-radius:var(--mr-radius); 2269 padding:18px 20px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.86rem; 2270} 2271.mr-before-after .mr-from{color:var(--mr-muted);overflow-wrap:anywhere} 2272.mr-before-after .mr-arrow{color:var(--mr-violet);font-weight:700} 2273.mr-before-after .mr-to{color:var(--mr-green);font-weight:600;overflow-wrap:anywhere} 2274@media (max-width:560px){.mr-before-after{grid-template-columns:1fr;text-align:center}} 2275 2276.mr-footer{padding:32px clamp(20px,4vw,48px);border-top:1px solid var(--mr-border);background:var(--mr-surface)} 2277.mr-footer-inner{ 2278 max-width:1080px;margin:0 auto;display:flex;flex-wrap:wrap;gap:14px; 2279 align-items:center;justify-content:space-between;font-size:.85rem;color:var(--mr-muted); 2280} 2281.mr-footer-links{display:flex;flex-wrap:wrap;gap:16px} 2282.mr-footer-links a{color:var(--mr-muted);text-decoration:none} 2283.mr-footer-links a:hover{color:var(--mr-violet-deep)} 2284`;function ny(){return(0,a.jsxs)("nav",{className:"mr-nav",children:[(0,a.jsxs)("a",{href:"/magicrename/support",className:"mr-nav-logo",children:[(0,a.jsx)("span",{className:"mr-nav-logo-icon",children:"M"}),(0,a.jsx)("span",{children:"Magic Rename"})]}),(0,a.jsxs)("div",{className:"mr-nav-links",children:[(0,a.jsx)("a",{href:"/magicrename/manual",children:"Manual"}),(0,a.jsx)("a",{href:"/magicrename/support",children:"Support"}),(0,a.jsx)("a",{href:"/magicrename/account",children:"Account"}),(0,a.jsx)("a",{href:"/magicrename/privacy",children:"Privacy"}),(0,a.jsx)("a",{href:"/magicrename/terms",children:"Terms"})]})]})}function nv(){return(0,a.jsx)("footer",{className:"mr-footer",children:(0,a.jsxs)("div",{className:"mr-footer-inner",children:[(0,a.jsxs)("span",{children:["© ",new Date().getFullYear()," Soft Flourish"]}),(0,a.jsxs)("div",{className:"mr-footer-links",children:[(0,a.jsx)("a",{href:"/magicrename/manual",children:"Manual"}),(0,a.jsx)("a",{href:"/magicrename/support",children:"Support"}),(0,a.jsx)("a",{href:"/magicrename/account",children:"Account"}),(0,a.jsx)("a",{href:"/magicrename/privacy",children:"Privacy Policy"}),(0,a.jsx)("a",{href:"/magicrename/terms",children:"Terms of Use"}),(0,a.jsx)("a",{href:nu,children:"Contact"}),(0,a.jsx)("a",{href:"/",children:"softflourish.com"})]})]})})}function nw({children:e}){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:nb}}),(0,a.jsxs)("div",{className:"mr-wrap",children:[(0,a.jsx)(ny,{}),(0,a.jsx)("div",{className:"mr-legal",children:(0,a.jsx)("div",{className:"mr-legal-inner",children:e})}),(0,a.jsx)(nv,{})]})]})}function nj(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:nb}}),(0,a.jsxs)("div",{className:"mr-wrap",children:[(0,a.jsx)(ny,{}),(0,a.jsx)("section",{className:"mr-hero",children:(0,a.jsxs)("div",{className:"mr-hero-inner",children:[(0,a.jsx)("div",{className:"mr-badge",children:"ð Every file analysed on your Mac"}),(0,a.jsxs)("h1",{className:"mr-h1",children:["Folders full of ",(0,a.jsx)("em",{children:"IMG_3674.jpg"}),(0,a.jsx)("br",{}),"become names you can read"]}),(0,a.jsx)("p",{className:"mr-sub",children:"Magic Rename looks at what is actually inside each file â photos by what they show, videos frame by frame, PDFs and documents by their real title â then renames the whole folder in one consistent style."}),(0,a.jsxs)("div",{className:"mr-cta-row",children:[(0,a.jsx)("a",{className:"mr-btn mr-btn-primary",href:"https://apps.apple.com/app/id6798810152",children:"Get it on the Mac App Store"}),(0,a.jsx)("a",{className:"mr-btn mr-btn-outline",href:"/magicrename/support",children:"Support"})]}),(0,a.jsxs)("div",{className:"mr-store-row",children:[(0,a.jsx)("a",{href:"https://apps.apple.com/app/id6798810152?mt=12",target:"_blank",rel:"noopener noreferrer","aria-label":"Download Magic Rename on the Mac App Store",children:(0,a.jsx)("img",{src:"/magicrename/mac-app-store-badge.svg",alt:"Download on the Mac App Store",width:187,height:48})}),(0,a.jsx)("a",{href:"https://apps.apple.com/app/id6798810152?mt=12",target:"_blank",rel:"noopener noreferrer","aria-label":"Open the Magic Rename Mac App Store listing",children:(0,a.jsx)("img",{className:"mr-store-qr",src:"/magicrename/qr-macappstore.svg",alt:"",width:104,height:104})})]}),(0,a.jsx)("p",{className:"mr-note",children:"Free to rename 3 files a month, every feature unlocked · Pro $4.99/month with a 7-day free trial"})]})}),(0,a.jsx)("section",{className:"mr-section",children:(0,a.jsx)("div",{className:"mr-section-inner",children:(0,a.jsxs)("div",{className:"mr-before-after",children:[(0,a.jsx)("span",{className:"mr-from",children:"IMG_3674.jpg"}),(0,a.jsx)("span",{className:"mr-arrow",children:"â"}),(0,a.jsx)("span",{className:"mr-to",children:"bedroom_luxury.jpg"})]})})}),(0,a.jsx)("section",{className:"mr-section",children:(0,a.jsxs)("div",{className:"mr-section-inner",children:[(0,a.jsx)("p",{className:"mr-label",children:"How it works"}),(0,a.jsx)("h2",{className:"mr-h2",children:"Preview everything before a single file changes"}),(0,a.jsx)("p",{className:"mr-lead",children:"Pick a whole folder or just the files you want â on this Mac, an external drive, or a synced folder like iCloud Drive, Dropbox or Google Drive."}),(0,a.jsxs)("div",{className:"mr-grid",children:[(0,a.jsxs)("div",{className:"mr-card",children:[(0,a.jsx)("h3",{children:"Reads the contents"}),(0,a.jsx)("p",{children:"Photos are recognised by what they show, videos sampled frame by frame, PDFs and documents read for their real title and subject."})]}),(0,a.jsxs)("div",{className:"mr-card",children:[(0,a.jsx)("h3",{children:"Your style, applied consistently"}),(0,a.jsx)("p",{children:"Case, separators, tone, date stamps, sequence numbers and prefixes â or templates so a whole shoot follows one pattern."})]}),(0,a.jsxs)("div",{className:"mr-card",children:[(0,a.jsx)("h3",{children:"Preview, edit, undo"}),(0,a.jsx)("p",{children:"Review every proposed name, edit any of them by hand, skip the ones you want left alone â and undo the whole batch just as easily."})]})]})]})}),(0,a.jsx)("section",{className:"mr-section",children:(0,a.jsxs)("div",{className:"mr-section-inner",children:[(0,a.jsx)("p",{className:"mr-label",children:"Private by design"}),(0,a.jsx)("h2",{className:"mr-h2",children:"Nothing is uploaded, ever"}),(0,a.jsx)("p",{className:"mr-lead",children:"Every file is analysed on your Mac using Appleâs built-in on-device intelligence. Your photos and documents are never sent to a server and never used to train anything. Magic Rename only ever sees the folder you choose."})]})}),(0,a.jsx)(nv,{})]})]})}function nk(){return(0,a.jsxs)(nw,{children:[(0,a.jsx)("h1",{children:"Magic Rename â Support"}),(0,a.jsxs)("p",{className:"mr-updated",children:[ng," · Covers Magic Rename for ",nx]}),(0,a.jsx)("div",{className:"mr-legal-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"Ask us anything:"})," ",(0,a.jsx)("a",{href:`mailto:${nf}?subject=Magic%20Rename%20support`,children:nf})," â or use the ",(0,a.jsx)("a",{href:nu,children:"contact form"}),".",(0,a.jsx)("br",{}
2284),"We answer within one business day (MonâFri, U.S. Pacific Time)."]})}),(0,a.jsx)("h2",{children:"What to include"}),(0,a.jsx)("p",{children:"None of this is required, but it usually saves a round trip:"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Your platform and OS version â macOS or Windows"}),(0,a.jsx)("li",{children:"The app version, from the About panel"}),(0,a.jsx)("li",{children:"Whether you signed in with Google or with an email address and password"}),(0,a.jsx)("li",{children:"What you did, what you expected, and what happened instead"})]}),(0,a.jsx)("p",{children:"Please don't send us the files you were renaming. Magic Rename reads them on your own computer and we never receive them â we'd rather keep it that way. A screenshot of the preview list, with anything sensitive covered, is usually enough."}),(0,a.jsx)("h2",{id:"how-it-works",children:"How Magic Rename decides on a name"}),(0,a.jsx)("p",{children:"It reads the file. A photo goes through image recognition, a scan or screenshot through OCR, a PDF through its embedded text layer, a Word or Excel document through the text inside it, and a video through its metadata. What comes out of that is turned into a short descriptive name in the style you picked. On machines with on-device AI â Apple Intelligence on a supported Mac, or a Copilot+ PC on Windows â a local language model writes the phrasing; everywhere else a deterministic namer does, and the app works exactly the same."}),(0,a.jsx)("p",{children:"All of it happens on your machine. No file, no file name, and no extracted text is ever uploaded, and there is no server that could receive them."}),(0,a.jsx)("h2",{id:"wrong-name",children:"A proposed name is wrong or unhelpful"}),(0,a.jsx)("p",{children:"Nothing is renamed until you say so â that is what the preview is for. Edit any proposed name in place before applying, or uncheck the row to leave that file alone. If a whole batch reads badly, try a different tone or a shorter name length in Options: a âdescriptiveâ run on a folder of near-identical photos will repeat itself, where âminimalâ plus numbering usually will not. Files the app cannot read anything useful out of â an empty document, a scan with no legible text â fall back to their metadata rather than inventing something."}),(0,a.jsx)("h2",{id:"undo",children:"I renamed the wrong folder"}),(0,a.jsxs)("p",{children:["Click ",(0,a.jsx)("strong",{children:"Undo Last Rename"}),". It restores every original name from the batch you just applied, in one step. The undo record lives with the app, so it survives closing the window but not moving or renaming the files again by other means â if something else has touched a file in the meantime, Magic Rename tells you which ones it could not put back rather than guessing."]}),(0,a.jsx)("h2",{id:"free-tier",children:"What the free version does"}),(0,a.jsxs)("p",{children:["Everything. There is no locked feature, no watermark, and no ads â the free tier renames"," ",(0,a.jsx)("strong",{children:"3 files per calendar month"}),", and the counter resets on the 1st. Magic Rename Pro is ",(0,a.jsx)("strong",{children:"$4.99 per month"})," and removes the limit. Previewing, analyzing, and undoing are always free and unlimited; only applying a rename counts."]}),(0,a.jsx)("h2",{id:"buy",children:"How do I buy Pro?"}),(0,a.jsxs)("table",{className:"mr-table",children:[(0,a.jsx)("thead",{children:(0,a.jsxs)("tr",{children:[(0,a.jsx)("th",{children:"Where you use it"}),(0,a.jsx)("th",{children:"Where you buy it"})]})}),(0,a.jsxs)("tbody",{children:[(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:"Mac"}),(0,a.jsx)("td",{children:"In the app, through the Mac App Store."})]}),(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:"Windows"}),(0,a.jsxs)("td",{children:["On the web, on your ",(0,a.jsx)("a",{href:"/magicrename/account",children:"account page"})," â the Windows app opens it for you. The Microsoft Store permits this for non-game apps, and the listing says so."]})]})]})]}),(0,a.jsx)("p",{children:"Either way it is one subscription on one account. Buy it on your Mac and sign in on your PC and Pro is already there, and the other way round."}),(0,a.jsx)("h2",{id:"restore-purchases",children:"I paid, but the app still shows the free limit"}),(0,a.jsxs)("p",{children:["Choose ",(0,a.jsx)("strong",{children:"Restore Purchases"})," in the app, and make sure you are signed in with the same account you bought with â a purchase belongs to the account, not to the machine. On a Mac, also confirm you are using the same Apple Account that made the purchase. You can see what your account currently holds on the"," ",(0,a.jsx)("a",{href:"/magicrename/account",children:"account page"}),". If it still does not unlock, email us the receipt and we will sort it out."]}),(0,a.jsx)("h2",{id:"cancel",children:"How do I cancel my subscription?"}),(0,a.jsx)("p",{children:"Cancelling is handled by whoever took the payment:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Bought on a Mac:"})," System Settings â Apple Account â Subscriptions."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Bought on the web (Windows):"})," your"," ",(0,a.jsx)("a",{href:"/magicrename/account",children:"account page"}),", using the manage link there."]})]}),(0,a.jsx)("p",{children:"Pro keeps working until the end of the peri
2284od you have already paid for, and then the app returns to the free tier. Nothing is deleted and no file is touched when a subscription ends."}),(0,a.jsx)("h2",{id:"account",children:"Do I need an account?"}),(0,a.jsx)("p",{children:"Only to buy Pro or to carry it to another machine. Renaming â including the free monthly allowance â works with no account and with no internet connection at all. The app contacts the network for exactly two things: signing you in, and asking whether your subscription is active."}),(0,a.jsx)("h2",{id:"delete-account",children:"Delete my account and data"}),(0,a.jsxs)("p",{children:["Email"," ",(0,a.jsx)("a",{href:`mailto:${nf}?subject=Delete%20Magic%20Rename%20account`,children:nf})," ","from the address on the account, with the subject âDelete Magic Rename accountâ, and we delete the account record and its subscription status within 30 days."]}),(0,a.jsx)("p",{children:"There is very little to delete: the account holds your email address and whether Pro is active. Your files, their names, and everything Magic Rename read out of them only ever existed on your own computer, so they were never ours to delete. Deleting the account does not cancel a subscription bought through the Mac App Store â cancel that under Subscriptions first, or it will keep renewing."}),(0,a.jsx)("h2",{id:"privacy",children:"Privacy and terms"}),(0,a.jsxs)("p",{children:["The full ",(0,a.jsx)("a",{href:"/magicrename/privacy",children:"Privacy Policy"})," and"," ",(0,a.jsx)("a",{href:"/magicrename/terms",children:"Terms of Use"})," apply to Magic Rename. The short version is on this page already: the app analyzes your files locally, has no telemetry and no analytics, and sends nothing about your files anywhere."]})]})}function nN(){return(0,a.jsxs)(nw,{children:[(0,a.jsx)("h1",{children:"Magic Rename â User Manual"}),(0,a.jsxs)("p",{className:"mr-updated",children:[ng," · Covers Magic Rename for ",nx]}),(0,a.jsx)("div",{className:"mr-legal-box",children:(0,a.jsx)("p",{children:"Nothing changes until you say so. Magic Rename always shows a full preview first, and the whole batch can be undone in one step after it runs."})}),(0,a.jsx)("h2",{children:"Choosing what to rename"}),(0,a.jsx)("p",{children:"Pick a whole folder or just the files you want â on this Mac, an external drive, or a synced folder like iCloud Drive, Dropbox, or Google Drive. Magic Rename only ever reads the folder you choose."}),(0,a.jsx)("h2",{children:"How it reads each file"}),(0,a.jsx)("p",{children:"Photos are recognised by what they actually show, videos are sampled frame by frame, and PDFs and documents are read for their real title and subject â not guessed from the existing filename or metadata."}),(0,a.jsx)("h2",{children:"Setting your naming style"}),(0,a.jsx)("p",{children:"Choose the case, separator, and tone you want, along with optional date stamps, sequence numbers, and prefixes â or use a template so an entire shoot or project follows one consistent pattern."}),(0,a.jsx)("h2",{children:"Reviewing before anything changes"}),(0,a.jsx)("p",{children:"Every proposed name appears in a preview before any file is touched. Edit any name by hand, skip the ones you want left alone, then apply the batch."}),(0,a.jsx)("h2",{children:"Undo"}),(0,a.jsx)("p",{children:"A rename can be undone as a whole batch, just as easily as it was applied â there is no need to rename files back one at a time."}),(0,a.jsx)("h2",{children:"Free vs. Pro"}),(0,a.jsxs)("p",{children:["Free renames 3 files a month with every feature unlocked, so there is nothing locked behind an upgrade except the monthly count. Pro removes that limit for $4.99/month, with a 7-day free trial. See ",(0,a.jsx)("a",{href:"/magicrename",children:"pricing"}),"."]}),(0,a.jsx)("h2",{children:"If something isnât working"}),(0,a.jsxs)("p",{children:["See ",(0,a.jsx)("a",{href:"/magicrename/support",children:"support"}),"for subscription management and troubleshooting a rename that didnât come out the way you expected."]})]})}function nS(){return(0,a.jsxs)(nw,{children:[(0,a.jsx)("h1",{children:"Magic Rename â Privacy Policy"}),(0,a.jsxs)("p",{className:"mr-updated",children:[ng," · Applies to Magic Rename for ",nx]}),(0,a.jsx)("div",{className:"mr-legal-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"The short version:"})," your files never leave your computer. Magic Rename reads them where they sit, and there is no server that c
2284ould receive a file, a file name, or anything read out of one. The only data we hold is what an account and a subscription need: your email address and whether Pro is active."]})}),(0,a.jsx)("h2",{children:"Who we are"}),(0,a.jsxs)("p",{children:["Magic Rename is developed and published by Soft Flourish, an independent software studio based in California, USA. Questions: ",(0,a.jsx)("a",{href:nu,children:"contact us"})," or email"," ",(0,a.jsx)("a",{href:`mailto:${nf}?subject=Magic%20Rename%20privacy`,children:nf}),"."]}),(0,a.jsx)("h2",{children:"Your files"}),(0,a.jsx)("p",{children:"We never receive, store, transmit, or process your files or their contents. Every part of the analysis runs on the machine the app is installed on: image recognition and OCR for photos, screenshots, and scans; the embedded text layer of a PDF; the text inside Word, Excel, and PowerPoint documents; and the metadata of a video or audio file. The proposed names are composed there too."}),(0,a.jsx)("p",{children:"This is not a promise we ask you to take on faith. The apps' own test suites fail the build if any file-reading service acquires a network dependency, so a future version cannot quietly start uploading what this one does not."}),(0,a.jsx)("h2",{children:"On-device AI"}),(0,a.jsx)("p",{children:"On a Mac that supports Apple Intelligence, and on a Copilot+ PC, a language model that ships with the operating system writes the phrasing of a name. It runs locally, like everything else. Nothing is sent to us, to Apple or Microsoft, or to any AI provider. On machines without that hardware the app uses a deterministic namer instead and works the same way â there is no cloud fallback."}),(0,a.jsx)("h2",{children:"What we collect"}),(0,a.jsx)("p",{children:"Only if you choose to create an account or buy Pro:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Email address"})," â identifies your account so Pro follows you to another machine."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Name"})," â only if you sign in with Google and it comes with the sign-in."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"A user identifier"})," â an opaque id that links your account to your subscription."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Subscription status"})," â whether Pro is active, so the app knows what to unlock."]})]}),(0,a.jsx)("p",{children:"You can use Magic Rename without an account at all. Renaming, including the free monthly allowance, needs no sign-in and no internet connection."}),(0,a.jsx)("h2",{children:"What we never collect"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Your files, or anything inside them"}),(0,a.jsx)("li",{children:"Your file names â neither the old ones nor the ones the app proposes"}),(0,a.jsx)("li",{children:"Text read out of a document, a scan, or a photo"}),(0,a.jsx)("li",{children:"The folders you point the app at, or a history of what you renamed"}),(0,a.jsx)("li",{children:"Analytics, usage telemetry, crash reports, or advertising identifiers"})]}),(0,a.jsx)("p",{children:"Magic Rename contains no advertising SDK, no analytics SDK, and no tracking of any kind. We do not sell or share personal information, and we do not use anything for cross-context behavioral advertising."}),(0,a.jsx)("h2",{children:"Payments"}),(0,a.jsx)("p",{children:"On a Mac, Pro is bought through the Mac App Store and Apple handles the payment. On Windows, Pro is bought on this website through our payment provider. Either way we never see or store your card details â we only learn that a subscription exists and whether it is active."}),(0,a.jsx)("h2",{children:"Service providers"}),(0,a.jsx)("p",{children:"Three, and only to run accounts and subscriptions:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Google Firebase Authentication"})," â sign-in and account identity."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"RevenueCat"})," â subscription and entitlement status, on both platforms."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Stripe"})," â card processing for the web checkout used by Windows."]})]}),(0,a.jsx)("p",{children:"None of them receives your files, your file names, or anything read out of them."}),(0,a.jsx)("h2",{children:"Data retention and deletion"}),(0,a.jsxs)("p",{children:["We keep the account record for as long as the account exists. Email"," ",(0,a.jsx)("a",{href:`mailto:${nf}
2284?subject=Delete%20Magic%20Rename%20account`,children:nf})," ","from the address on the account and we delete it, and its subscription status, within 30 days. Deleting the app by itself does not delete the account, and deleting the account does not cancel a subscription bought on the Mac App Store â cancel that under Subscriptions first. See ",(0,a.jsx)("a",{href:"/magicrename/support#delete-account",children:"Support"})," for the full steps."]}),(0,a.jsx)("h2",{children:"Your rights"}),(0,a.jsxs)("p",{children:["Depending on where you live â including under the GDPR and the CCPA/CPRA â you may have the right to access, correct, export, or delete the personal information we hold, and to withdraw consent. Because we hold an email address and a yes-or-no subscription flag, these requests are usually a single reply. ",(0,a.jsx)("a",{href:nu,children:"Contact us"})," and we will action it."]}),(0,a.jsx)("h2",{children:"Children"}),(0,a.jsx)("p",{children:"Magic Rename is a general-purpose utility, is not directed to children under 13, and we do not knowingly collect personal information from them."}),(0,a.jsx)("h2",{children:"International transfers"}),(0,a.jsx)("p",{children:"The account and subscription infrastructure runs in the United States. Because your files never leave your computer, no file data is transferred anywhere at all."}),(0,a.jsx)("h2",{children:"Changes to this policy"}),(0,a.jsx)("p",{children:"If we make a material change we will update this page and the date above. Continued use after a change means you accept the updated policy."}),(0,a.jsx)("h2",{children:"Contact"}),(0,a.jsxs)("p",{children:["Questions about this policy or your data: ",(0,a.jsx)("a",{href:nu,children:"contact us"}),". Publisher: Soft Flourish, California, USA. See also our"," ",(0,a.jsx)("a",{href:"/magicrename/terms",children:"Terms of Use"}),"."]})]})}function nA(){return(0,a.jsxs)(nw,{children:[(0,a.jsx)("h1",{children:"Magic Rename â Terms of Use"}),(0,a.jsxs)("p",{className:"mr-updated",children:[ng," · Applies to Magic Rename for ",nx]}),(0,a.jsx)("div",{className:"mr-legal-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"The short version:"})," Magic Rename is licensed to you, not sold. Pro is an auto-renewing subscription you can cancel any time. Renaming changes files on your own computer â there is a preview before and an undo after, but keeping backups of anything irreplaceable is still your job."]})}),(0,a.jsx)("h2",{children:"1. Agreement"}),(0,a.jsx)("p",{children:"These Terms govern your use of the Magic Rename applications and these pages, provided by Soft Flourish (âweâ, âusâ). Installing or using the app means you agree to them. If you do not, please do not use the app."}),(0,a.jsx)("h2",{children:"2. Licence"}),(0,a.jsx)("p",{children:"We grant you a personal, non-exclusive, non-transferable licence to install and use Magic Rename on machines you own or control, for personal or internal business use. You may not resell it, redistribute it, reverse-engineer it, or work around its plan limits, except where the law says such a restriction cannot be enforced."}),(0,a.jsx)("h2",{children:"3. The free tier and Pro"}),(0,a.jsxs)("p",{children:["Magic Rename is free to use, with every feature unlocked, for"," ",(0,a.jsx)("strong",{children:"3 renamed files per calendar month"}),"; the counter resets on the 1st. Analyzing, previewing, and undoing are always free and unlimited â only applying a rename counts against the allowance. ",(0,a.jsx)("strong",{children:"Magic Rename Pro"})," is $4.99 per month and removes the limit. There is no free trial, no watermark, and no advertising."]}),(0,a.jsx)("h2",{children:"4. Subscriptions, billing, and cancellation"}),(0,a.jsxs)("p",{children:["Pro is an ",(0,a.jsx)("strong",{children:"auto-renewing subscription"}),". It renews at the end of each monthly period unless cancelled at least 24 hours before that period ends. Who takes the payment depends on where you bought it:"]}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Bought on a Mac:"})," Apple bills you through the Mac App Store. Manage or cancel in System Settings â Apple Account â Subscriptions."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Bought on Windows:"})," our web checkout bills you, through our payment provider â the Microsoft Store is not involved, which it permits for apps of this kind. Manage or cancel on your ",(0,a.jsx)("a",{href:"/magicrename/account",children:"account page"}),"."]})]}),(0,a.jsx)("p",{children:"One subscription covers every machine you sign in on, Mac and Windows together. When a subscription ends the app returns to the free tier; nothing is deleted and no file is touched."}),(0,a.jsx)("h2",{children:"5. Refunds"}),(0,a.jsx)("p",{children:"Refunds are handled by whoever took the payment, under their policy â Apple for a Mac App Store purchase, our payment provider for a web purchase. If a refund is granted, Pro stops being available."}),(0,a.jsx)("h2",{children:"6. Acceptable use"}),(0,a.jsx)("p",{children:"You are responsible for the files you process and must have the right to modify them. Do not use Magic Rename to infringe anyone's rights, to breach a confidentiality obligation, or to break applicable law."}),(0,a.jsx)("h2",{children:"7. Your files and backups"}),(0,a.jsxs)("p",{children:["Renaming changes files on your own computer, and we never hold a copy â so we cannot recover anything for you. The app shows every proposed name before it changes anything, and"," ",(0,a.jsx)("strong",{children:"Undo L
2284ast Rename"})," restores the batch you just applied. That undo covers one batch, and it cannot put back a file that something else has since moved, renamed, or deleted. Keep your own backups of anything irreplaceable."]}),(0,a.jsx)("h2",{children:"8. What the app can and cannot read"}),(0,a.jsx)("p",{children:"Proposed names are generated from what the app can read on your machine, and it will be wrong sometimes â a photo of an unusual subject, a scan with no legible text, an empty document. That is why nothing is renamed until you approve it. Some capabilities depend on your hardware: on-device AI phrasing needs a Mac that supports Apple Intelligence or a Copilot+ PC, and where it is unavailable the app says so and uses a deterministic namer instead of substituting a cloud service."}),(0,a.jsx)("h2",{children:"9. Intellectual property"}),(0,a.jsx)("p",{children:"Magic Rename, its name, icon, and design are ours. These Terms transfer none of that to you. Your files, and the names you apply to them, remain entirely yours."}),(0,a.jsx)("h2",{children:"10. Disclaimer and limitation of liability"}),(0,a.jsx)("p",{children:"The app is provided âas isâ, without warranties of any kind, to the fullest extent the law allows. We do not warrant that it will be uninterrupted or error-free, or that any proposed name will be fit for a particular purpose. To the maximum extent permitted by law, our total liability arising out of or relating to the app is limited to what you paid us in the twelve months before the claim."}),(0,a.jsx)("h2",{children:"11. Changes"}),(0,a.jsx)("p",{children:"We may update these Terms or the app. Material changes appear here with a new date above. Continued use after a change means you accept the updated Terms."}),(0,a.jsx)("h2",{children:"12. Termination"}),(0,a.jsx)("p",{children:"You may stop using the app at any time. We may suspend or end access if these Terms are breached. Sections that should by their nature survive termination will do so."}),(0,a.jsx)("h2",{children:"13. Governing law"}),(0,a.jsx)("p",{children:"These Terms are governed by the laws of the State of California, USA, without regard to conflict-of-laws rules, and the courts of California have jurisdiction â except where mandatory local consumer law gives you other rights."}),(0,a.jsx)("h2",{children:"14. Contact"}),(0,a.jsxs)("p",{children:["Questions about these Terms: ",(0,a.jsx)("a",{href:nu,children:"contact us"}),". Publisher: Soft Flourish, California, USA. See also our"," ",(0,a.jsx)("a",{href:"/magicrename/privacy",children:"Privacy Policy"}),"."]})]})}var ep=ed,eh=ed;let nP="systemzen_pro",nz="#5533d6",nT={$rc_monthly:{title:"Pro â Monthly",blurb:"Unlimited cleanups, deep scans and duplicate removal · no ads · every device you sign into"},$rc_annual:{title:"Pro â Annual",blurb:"Everything in Pro, billed yearly · the cheaper way to keep a machine tidy all year"}};function nF(){let e=(0,ei.getAuth)((0,eu.getFirebaseApp)());return e.tenantId="evertune-rmy40",e}function nC(){let[t,s]=(0,r.useState)(null),[n,i]=(0,r.useState)(!1),[o,l]=(0,r.useState)(null),[c,d]=(0,r.useState)(!1),[p,h]=(0,r.useState)(""),[m,u]=(0,r.useState)(""),[f,g]=(0,r.useState)(!1),[x,b]=(0,r.useState)([]),[y,v]=(0,r.useState)(!1),[w,j]=(0,r.useState)(null),[k,N]=(0,r.useState)(!1),[S,A]=(0,r.useState)(!1),[P,z]=(0,r.useState)(null);(0,r.useEffect)(()=>
2284{l(new URLSearchParams(window.location.search).get("uid"));let e=(0,eo.onAuthStateChanged)(nF(),e=>{s(e),i(!0)});return()=>e()},[]);let T=(0,r.useCallback)(async a=>{try{let{Purchases:t}=await e.A(323738),s=t.isConfigured()?t.getSharedInstance():t.configure({apiKey:"rcb_PoZkhuRgVSvquUWwgaAlAuSDXEkU",appUserId:a});t.isConfigured()&&await s.changeUser(a),j(await s.getCustomerInfo());let r=await s.getOfferings(),n=r.all["systemzen.default"]??r.current;b(n?.availablePackages??[]),v(!0)}catch(e){z(e.message)}},[]);(0,r.useEffect)(()=>{t?T(t.uid):c&&o&&T(o)},[t,c,o,T]);let F=e=>{A(!0),z(null),e().catch(e=>z(e.message)).finally(()=>A(!1))},C=null!==t||c&&null!==o,M=null!==t&&null!==o&&t.uid!==o,I=w?.entitlements.active[nP]!==void 0,D=function(e){let a=e?.entitlements.active[nP];if(!a?.expirationDate)return null;let t=a.expirationDate.toLocaleDateString();return a.willRenew?`Renews on ${t}`:`Access ends on ${t}`}(w),W=w?.managementURL??null;return(0,a.jsxs)("main",{style:{maxWidth:620,margin:"0 auto",padding:"48px 20px",color:"#15131c",background:"#f8f7fd",minHeight:"100svh"},children:[(0,a.jsx)("p",{style:{marginBottom:24},children:(0,a.jsx)("a",{href:"/systemzen/support",style:{color:"#585266",textDecoration:"none",fontSize:14},children:"â SystemZen"})}),(0,a.jsxs)("h1",{style:{fontSize:28,fontWeight:800},children:["SystemZen ",(0,a.jsx)("span",{style:{color:nz},children:"account"})]}),(0,a.jsx)("p",{style:{color:"#585266",marginTop:8,lineHeight:1.6},children:"Manage SystemZen Pro. Monitoring, scanning and one basic cache clean stay free (with ads); Pro removes the ads and the cleanup limits. One subscription covers every device you sign into â Mac, Windows, iPhone, iPad and Android."}),n?C?(0,a.jsxs)("section",{style:{marginTop:24,display:"grid",gap:12},children:[(0,a.jsx)("div",{style:{padding:14,borderRadius:12,background:"rgba(255,255,255,0.80)",border:"1px solid rgba(21,19,28,0.10)"},children:t?(0,a.jsxs)(a.Fragment,{children:["Signed in as ",(0,a.jsx)("strong",{children:t.email??t.uid})," â current plan:"," ",(0,a.jsx)("strong",{children:I?"Pro":"Free"}),D?(0,a.jsxs)("span",{style:{color:"#585266"},children:[" · ",D]}):null]}):(0,a.jsxs)(a.Fragment,{children:["Continuing as ",(0,a.jsx)("strong",{children:"guest"}),"â purchases unlock this device's app (",(0,a.jsx)("code",{style:{fontSize:12},children:o}),"). Current plan:"," ",(0,a.jsx)("strong",{children:I?"Pro":"Free"}),"."]})}),M?(0,a.jsxs)("div",{style:{padding:14,borderRadius:12,background:"rgba(124,92,255,0.08)",border:"1px solid rgba(124,92,255,0.35)",fontSize:14,lineHeight:1.6},children:[(0,a.jsx)("strong",{style:{color:nz},children:"Heads up:"})," you arrived from the app with a different session than the account you just signed in with. Pro unlocks on the",(0,a.jsx)("strong",{children:" signed-in account"}),"â to use it, sign into this same account inside the app. To unlock the app's current session instead, sign out here and choose âContinue as guestâ."]}):null,k?(0,a.jsx)("p",{style:{color:"#188A52",fontWeight:600},children:"You're on Pro â it unlocks in the app automatically."}):null,I?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("p",{style:{color:"#585266",lineHeight:1.6},children:"Pro is active. Open SystemZen on any device and sign in with this account â the ads go away and the cleanup limits are lifted there too."}),W?(0,a.jsx)("a",{href:W,target:"_blank",rel:"noreferrer",style:{...nD,display:"block",textDecoration:"none",fontWeight:700},children:"Manage or cancel subscription"}):null]}):y?0===x.length?(0,a.jsxs)("p",{style:{color:nz},children:["Buying Pro on the web isn't available right now. Purchases made inside the Mac, iPhone or Android app still work and unlock here â or contact"," ",(0,a.jsx)("a",{href:"/contact",style:{color:nz},children:"support"}),"."]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("h2",{style:nM,children:"SystemZen Pro"}),x.map(t=>{let s=nT[t.identifier],r=t.webBillingProduct?.currentPrice?.formattedPrice,n=function(e){let a=e.webBillingProduct?.freeTrialPhase?.period;if(!a)return null;let t=a.number;return`First ${t} ${a.unit}${1===t?"":"s"} free`}(t);return(0,a.jsxs)("button",{disabled:S,onClick:()=>F(async()=>{let{Purchases:a}=await e.A(323738);j((await a.getSharedInstance().purchase({rcPackage:t})).customerInfo),N(!0)}),style:nD,children:[(0,a.jsxs)("span",{style:{display:"block",fontWeight:700},children:[s?.title??t.webBillingProduct?.displayName??t.identifier,r?(0,a.jsxs)("span",{style:{color:nz},children:[" · ",r]}):null]}),n?(0,a.jsx)("span",{style:{display:"block",fontSize:12,fontWeight:700,color:"#d946ef",marginTop:4},children:n}
2284):null,s?(0,a.jsx)("span",{style:{display:"block",fontSize:13,color:"#585266",marginTop:4},children:s.blurb}):null]},t.identifier)}),(0,a.jsx)("p",{style:{fontSize:13,color:"#585266",marginTop:4,lineHeight:1.6},children:"Subscriptions renew automatically until cancelled, and you can cancel any time from this page. Any free trial converts to a paid period unless you cancel before it ends. Pro unlocks in the app automatically."})]}):(0,a.jsx)("p",{style:{color:"#9a94a8"},children:"Loading plansâ¦"}),t?(0,a.jsx)("button",{onClick:()=>F(async()=>{await (0,em.signOut)(nF()),j(null),b([]),v(!1),N(!1)}),style:nO,children:"Sign out"}):(0,a.jsx)("button",{onClick:()=>{d(!1),j(null),b([]),v(!1),N(!1)},style:nO,children:"Back to sign in"})]}):(0,a.jsxs)("section",{style:{marginTop:24,display:"grid",gap:10},children:[(0,a.jsx)("button",{disabled:S,onClick:()=>F(async()=>{await (0,el.signInWithPopup)(nF(),new ec.GoogleAuthProvider)}),style:nI,children:"Continue with Google"}),(0,a.jsx)("div",{style:{height:1,background:"rgba(21,19,28,0.10)",margin:"8px 0"}}),(0,a.jsx)("input",{placeholder:"Email",value:p,onChange:e=>h(e.target.value),style:nW}),(0,a.jsx)("input",{placeholder:"Password",type:"password",value:m,onChange:e=>u(e.target.value),style:nW}),(0,a.jsx)("button",{disabled:S||!p||!m,onClick:()=>F(async()=>{let e=nF();f?await (0,eh.ab)(e,p,m):await (0,ep.ac)(e,p,m)}),style:{...nI,background:nz,borderColor:nz,color:"#fff"},children:f?"Create account":"Sign in"}),(0,a.jsx)("button",{onClick:()=>g(e=>!e),style:nO,children:f?"Have an account? Sign in":"New here? Create an account"}),o?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{style:{height:1,background:"rgba(21,19,28,0.10)",margin:"8px 0"}}),(0,a.jsx)("button",{disabled:S,onClick:()=>d(!0),style:nI,children:"Continue as guest â unlock this device's app"}),(0,a.jsx)("p",{style:{fontSize:13,color:"#585266",margin:0},children:"No account needed: your subscription is tied to the app that opened this page and unlocks there automatically. Sign in instead if you want Pro on your phone and Mac too."})]}):null]}):(0,a.jsx)("p",{style:{marginTop:24,color:"#9a94a8"},children:"Loadingâ¦"}),P&&(0,a.jsx)("p",{style:{color:"#b3261e",marginTop:16},children:P})]})}let nM={fontSize:15,fontWeight:800,marginTop:12,letterSpacing:"0.02em",textTransform:"uppercase",color:"#15131c"},nI={padding:"11px 16px",borderRadius:12,border:"1px solid rgba(21,19,28,0.14)",background:"#fff",color:"#15131c",fontWeight:600,cursor:"pointer"},nD={...nI,textAlign:"left",padding:"13px 16px"},nW={padding:"10px 12px",borderRadius:10,border:"1px solid rgba(21,19,28,0.14)",background:"#fff",color:"#15131c"},nO={background:"none",border:"none",color:nz,cursor:"pointer",fontSize:13,padding:4};var ep=ed,eh=ed;let nE="djflow_pro",nR="#7a47ff",n$="#1fd6fa",n_="#f7f4ff",nU="#c4bdd2",nL="rgba(247,244,255,0.14)",nq="rgba(247,244,255,0.05)",nB="#ff527f",nG={$rc_annual:{title:"Pro â Annual",blurb:"Party and Workout modes on music you own, advanced local analysis, AI vibe instructions and synced preferences · billed yearly, the cheapest way to keep Pro all year"},$rc_monthly:{title:"Pro â Monthly",blurb:"The same Pro modes, month to month · cancel any time from this page"}},nH=["$rc_annual","$rc_monthly"];function nY(){let e=(0,ei.getAuth)((0,eu.getFirebaseApp)());return e.tenantId="softflourish-djflow-zsiyw",e}function nV(){let t,[s,n]=(0,r.useState)(null),[i,o]=(0,r.useState)(!1),[l,c]=(0,r.useState)(null),[d,p]=(0,r.useState)(!1),[h,m]=(0,r.useState)(""),[u,f]=(0,r.useState)(""),[g,x]=(0,r.useState)(!1),[b,y]=(0,r.useState)([]),[v,w]=(0,r.useState)(!1),[j,k]=(0,r.useState)(null),[N,S]=(0,r.useState)(!1),[A,P]=(0,r.useState)(!1),[z,T]=(0,r.useState)(null);(0,r.useEffect)(()=>{c(new URLSearchParams(window.location.search).get("uid"));let e=(0,eo.onAuthStateChanged)(nY(),e=>{n(e),o(!0)});return()=>e()},[]);let F=(0,r.useCallback)(async a=>{try{let{Purchases:t}=await e.A(323738),s=t.isConfigured()?t.getSharedInstance():t.configure({apiKey:"rcb_SpBkQFBvpYElFRZCSlExkfZuOxYH",appUserId:a});t.isConfigured()&&await s.changeUser(a),k(await s.getCustomerInfo());let r=(await s.getOfferings()).all["djflow.default"],n=r?.availablePackages??[],i=e=>{let a=nH.indexOf(e);return a<0?nH.length:a};y([...n].sort((e,a)=>i(e.identifier)-i(a.identifier))),w(!0)}catch(e){T(e.message)}},[]);(0,r.useEffect)(()=>{s?F(s.uid):d&&l&&F(l)},[s,d,l,F]);let C=e=>{P(!0),T(null),e().catch(e=>T(e.message)).finally(()=>P(!1))},M=null!==s||d&&null!==l,I=null!==s&&null!==l&&s.uid!==l,D=j?.entitlements.active[nE],W=void 0!==D,O
2284="trial"===String(D?.periodType??""),E=function(e){let a=e?.entitlements.active[nE];if(!a?.expirationDate)return null;let t=a.expirationDate.toLocaleDateString();return a.willRenew?`Renews on ${t}`:`Access ends on ${t}`}(j),R=(t=j?.entitlements.active[nE]?.store)?({app_store:"the App Store",mac_app_store:"the Mac App Store",play_store:"Google Play",rc_billing:"the web",stripe:"the web",promotional:"a promotional grant"})[String(t)]??null:null,$=j?.managementURL??null;return(0,a.jsxs)("main",{style:{maxWidth:620,margin:"0 auto",padding:"48px 20px",color:n_,background:"#090812",minHeight:"100svh"},children:[(0,a.jsx)("p",{style:{marginBottom:24},children:(0,a.jsx)("a",{href:"/djflow",style:{color:nU,textDecoration:"none",fontSize:14},children:"â DJFlow"})}),(0,a.jsxs)("h1",{style:{fontSize:28,fontWeight:800},children:["DJFlow ",(0,a.jsx)("span",{style:{color:nR},children:"account"})]}),(0,a.jsx)("p",{style:{color:nU,marginTop:8,lineHeight:1.6},children:"Manage DJFlow Pro. Flow Mode, Smart Shuffle and Apple Music playback are free and always will be; Pro adds Party and Workout modes on music you own, advanced local analysis, AI vibe instructions and preferences synced across devices â and removes the ads on iPhone and Android. On Windows you subscribe here; on iPhone, iPad, Mac and Android you can subscribe in the app. One subscription covers every device you sign into."}),i?M?(0,a.jsxs)("section",{style:{marginTop:24,display:"grid",gap:12},children:[(0,a.jsx)("div",{style:{padding:14,borderRadius:12,background:nq,border:`1px solid ${nL}`,lineHeight:1.6},children:s?(0,a.jsxs)(a.Fragment,{children:["Signed in as ",(0,a.jsx)("strong",{children:s.email??s.uid})," â current plan:"," ",(0,a.jsx)("strong",{style:{color:W?n$:n_},children:W?O?"Pro (free trial)":"Pro":"Free"}),E?(0,a.jsxs)("span",{style:{color:nU},children:[" · ",E]}):null]}):(0,a.jsxs)(a.Fragment,{children:["Continuing as ",(0,a.jsx)("strong",{children:"guest"}),"â purchases unlock this device's app (",(0,a.jsx)("code",{style:{fontSize:12},children:l}),"). Current plan:"," ",(0,a.jsx)("strong",{style:{color:W?n$:n_},children:W?"Pro":"Free"}),"."]})}),I?(0,a.jsxs)("div",{style:{padding:14,borderRadius:12,background:"rgba(122,71,255,0.12)",border:"1px solid rgba(122,71,255,0.45)",fontSize:14,lineHeight:1.6},children:[(0,a.jsx)("strong",{style:{color:nR},children:"Heads up:"})," you arrived from the app with a different session than the account you just signed in with. Pro unlocks on the ",(0,a.jsx)("strong",{children:"signed-in account"}),"â to use it, sign into this same account inside the app. To unlock the app's current session instead, sign out here and choose âContinue as guestâ."]}):null,N?(0,a.jsx)("p",{style:{color:n$,fontWeight:700},children:"You're on Pro â it unlocks in the app automatically."}):null,W?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("p",{style:{color:nU,lineHeight:1.6},children:[O?"Your free trial is running â nothing is charged until it ends, and cancelling before then costs you nothing.":"Pro is active."," ","Open DJFlow on any device and sign in with this account â Party and Workout modes on your own files, AI vibe instructions and synced preferences are unlocked there too, and the ads are gone.",R?` Bought on ${R}.`:""]}),$?(0,a.jsx)("a",{href:$,target:"_blank",rel:"noreferrer",style:{...nJ,display:"block",textDecoration:"none",fontWeight:700},children:"Manage or cancel subscription"}):(0,a.jsxs)("p",{style:{fontSize:13,color:nU,lineHeight:1.6},children:["To cancel, use the subscription settings of the store you bought it from, or email"," ",(0,a.jsx)("a",{href:"mailto:[email protected]",style:{color:n$},children:"[email protected]"}),"."]})]}):v?0===b.length?(0,a.jsxs)("div",{style:{padding:14,borderRadius:12,background:"rgba(255,82,127,0.10)",border:"1px solid rgba(255,82,127,0.45)",lineHeight:1.6},children:[(0,a.jsx)("p",{style:{color:nB,fontWeight:700,margin:0},children:"Buying Pro on the web isn't available right now."}),(0,a.jsxs)("p",{style:{color:nU,marginTop:6,fontSize:14},children:["This is on us, not you â nothing was charged. Subscriptions bought inside the iPhone, Mac or Android app still work and unlock here. Try again shortly, or email"," ",(0,a.jsx)("a",{href:"mailto:[email protected]",style:{color:n$},children:"[email protected]"}),". DJFlow stays fully usable free meanwhile â Flow Mode, Smart Shuffle and Apple Music playback are never gated."]})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("h2",{style:nK,children:"DJFlow Pro"}),b.map((t,s)=>{var r;let n,i,o,l;return r=0===s,n=nG[t.identifier],i=t.webBillingProduct?.currentPrice?.formattedPrice,o=t.webBillingProduct?.period?`/${Str
2284ing(t.webBillingProduct.period.unit)}`:"",l=function(e){let a=e.webBillingProduct?.freeTrialPhase?.period;if(!a)return null;let t=String(a.unit),s="week"===t?7*a.number:a.number;return`First ${s} ${"week"===t?"day":t}${1===s?"":"s"} free`}(t),(0,a.jsxs)("button",{disabled:A,onClick:()=>C(async()=>{let{Purchases:a}=await e.A(323738);k((await a.getSharedInstance().purchase({rcPackage:t})).customerInfo),S(!0)}),style:r?nZ:nJ,children:[(0,a.jsxs)("span",{style:{display:"block",fontWeight:800},children:[n?.title??t.webBillingProduct?.displayName??t.identifier,i?(0,a.jsxs)("span",{style:{color:r?n_:nR},children:[" ","· ",i,o]}):null]}),l?(0,a.jsx)("span",{style:{display:"block",width:"fit-content",marginTop:6,fontSize:12,fontWeight:800,letterSpacing:"0.04em",textTransform:"uppercase",color:r?"#0b0a16":n$,background:r?n$:"transparent",borderRadius:999,padding:r?"3px 10px":0},children:l}):null,n?(0,a.jsx)("span",{style:{display:"block",fontSize:13,lineHeight:1.55,color:r?"rgba(247,244,255,0.82)":nU,marginTop:6},children:n.blurb}):null]},t.identifier)}),(0,a.jsxs)("p",{style:{fontSize:13,color:nU,marginTop:4,lineHeight:1.6},children:["Pay with ",(0,a.jsx)("strong",{style:{color:n_},children:"Apple Pay"}),","," ",(0,a.jsx)("strong",{style:{color:n_},children:"Google Pay"})," or any credit or debit card â the wallets appear on the checkout when your device and browser support them."]}),(0,a.jsx)("p",{style:{fontSize:13,color:nU,lineHeight:1.6},children:"Subscriptions renew automatically until cancelled, and
2284you can cancel any time from this page. A free trial converts to a paid period at the price shown unless you cancel before it ends. Pro unlocks in the app automatically. Apple Music access and playback are never part of Pro â they are free on every platform."})]}):(0,a.jsx)("p",{style:{color:"rgba(247,244,255,0.45)"},children:"Loading plansâ¦"}),s?(0,a.jsx)("button",{onClick:()=>C(async()=>{await (0,em.signOut)(nY()),k(null),y([]),w(!1),S(!1)}),style:n0,children:"Sign out"}):(0,a.jsx)("button",{onClick:()=>{p(!1),k(null),y([]),w(!1),S(!1)},style:n0,children:"Back to sign in"})]}):(0,a.jsxs)("section",{style:{marginTop:24,display:"grid",gap:10},children:[(0,a.jsx)("button",{disabled:A,onClick:()=>C(async()=>{await (0,el.signInWithPopup)(nY(),new ec.GoogleAuthProvider)}),style:nQ,children:"Continue with Google"}),(0,a.jsx)("div",{style:{height:1,background:nL,margin:"8px 0"}}),(0,a.jsx)("input",{placeholder:"Email",value:h,onChange:e=>m(e.target.value),style:nX}),(0,a.jsx)("input",{placeholder:"Password",type:"password",value:u,onChange:e=>f(e.target.value),style:nX}),(0,a.jsx)("button",{disabled:A||!h||!u,onClick:()=>C(async()=>{let e=nY();g?await (0,eh.ab)(e,h,u):await (0,ep.ac)(e,h,u)}),style:{...nQ,background:nR,borderColor:nR,color:"#ffffff"},children:g?"Create account":"Sign in"}),(0,a.jsx)("button",{onClick:()=>x(e=>!e),style:n0,children:g?"Have an account? Sign in":"New here? Create an account"}),(0,a.jsx)("p",{style:{fontSize:13,color:nU,margin:0,lineHeight:1.6},children:"Sign in with Apple is available inside the iPhone, iPad, Mac and Apple TV apps."}),l?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{style:{height:1,background:nL,margin:"8px 0"}}),(0,a.jsx)("button",{disabled:A,onClick:()=>p(!0),style:nQ,children:"Continue as guest â unlock this device's app"}),(0,a.jsx)("p",{style:{fontSize:13,color:nU,margin:0,lineHeight:1.6},children:"No account needed: your subscription is tied to the app that opened this page and unlocks there automatically. Sign in instead if you want Pro on your phone and Mac too."})]}):null]}):(0,a.jsx)("p",{style:{marginTop:24,color:"rgba(247,244,255,0.45)"},children:"Loadingâ¦"}),z&&(0,a.jsx)("p",{style:{color:nB,marginTop:16},children:z}),(0,a.jsxs)("p",{style:{marginTop:32,fontSize:13,color:"rgba(247,244,255,0.45)",lineHeight:1.7},children:[(0,a.jsx)("a",{href:"/djflow/terms",style:{color:nU},children:"Terms"})," ","·"," ",(0,a.jsx)("a",{href:"/djflow/privacy",style:{color:nU},children:"Privacy"})," ","·"," ",(0,a.jsx)("a",{href:"/djflow/support",style:{color:nU},children:"Support"})," ","·"," ",(0,a.jsx)("a",{href:"/djflow/deleteaccount",style:{color:nU},children:"Delete account"})]})]})}let nK={fontSize:15,fontWeight:800,marginTop:12,letterSpacing:"0.02em",textTransform:"uppercase",color:n_},nQ={padding:"11px 16px",borderRadius:12,border:`1px solid ${nL}`,background:nq,color:n_,fontWeight:600,cursor:"pointer"},nJ={...nQ,textAlign:"left",padding:"13px 16px"},nZ={...nJ,background:"linear-gradient(135deg, rgba(122,71,255,0.30), rgba(31,214,250,0.16))",border:`1px solid ${nR}`},nX={padding:"10px 12px",borderRadius:10,border:`1px solid ${nL}`,background:"rgba(0,0,0,0.35)",color:n_},n0={background:"none",border:"none",color:n$,cursor:"pointer",fontSize:13,padding:4},n1="/contact",n2="[email protected]",n6="Last updated: July 2026",n5="macOS, Windows, iPhone, iPad, and Android",n8=` 2285.sz-wrap{ 2286 --sz-violet:#7c5cff;--sz-violet-deep:#5533d6;--sz-fuchsia:#d946ef;--sz-violet-soft:#efeaff; 2287 --sz-bg:#f8f7fd;--sz-fg:#15131c;--sz-muted:#585266; 2288 --sz-border:rgba(21,19,28,0.10);--sz-surface:rgba(255,255,255,0.80); 2289 --sz-radius:16px; 2290 font-family:-apple-system,BlinkMacSystemFont,"Inter",system-ui,sans-serif; 2291 background:var(--sz-bg);color:var(--sz-fg); 2292 -webkit-font-smoothing:antialiased; 2293} 2294.sz-wrap *,.sz-wrap *::before,.sz-wrap *::after{box-sizing:border-box} 2295 2296.sz-nav{ 2297 position:sticky;top:0;z-index:100;height:64px;padding:0 clamp(20px,4vw,48px); 2298 display:flex;align-items:center;justify-content:space-between; 2299 background:rgba(248,247,253,0.82);backdrop-filter:blur(18px); 2300 border-bottom:1px solid var(--sz-border); 2301} 2302.sz-nav-logo{display:flex;align-items:center;gap:10px;text-decoration:none;color:var(--sz-fg);font-weight:650} 2303.sz-nav-logo-icon{ 2304 width:30px;height:30px;border-radius:8px;flex:none; 2305 background:linear-gradient(160deg,var(--sz-violet),var(--sz-fuchsia)); 2306 display:flex;align-items:center;justify-content:center;color:#fff;font-size:15px;font-weight:700; 2307} 2308.sz-nav-links{display:flex;align-items:center;gap:4px} 2309.sz-nav-links a{ 2310 font-size:.85rem;color:var(--sz-muted);text-decoration:none;padding:8px 12px;border-radius:999px; 2311} 2312.sz-nav-links a:hover{color:var(--sz-fg);background:rgba(124,92,255,0.09)} 2313 2314.sz-legal{padding:clamp(40px,6vw,72px) clamp(20px,4vw,48px) clamp(56px,8vw,96px)} 2315.sz-legal-inner{max-width:760px;margin:0 auto} 2316.sz-legal-inner h1{font-size:clamp(1.9rem,4vw,2.6rem);font-weight:740;letter-spacing:-.025em;line-height:1.12;margin:0 0 10px} 2317.sz-legal-inner h2{font-size:1.14rem;font-weight:680;letter-spacing:-.01em;
2317margin:36px 0 10px} 2318.sz-legal-inner h3{font-size:1rem;font-weight:660;margin:24px 0 8px} 2319.sz-legal-inner p,.sz-legal-inner li{font-size:.965rem;line-height:1.72;color:var(--sz-muted)} 2320.sz-legal-inner p{margin:0 0 14px} 2321.sz-legal-inner ul,.sz-legal-inner ol{margin:0 0 16px;padding-left:22px} 2322.sz-legal-inner li{margin:0 0 7px} 2323.sz-legal-inner strong{color:var(--sz-fg);font-weight:620} 2324.sz-legal-inner a{color:var(--sz-violet-deep);text-decoration:underline;text-underline-offset:2px} 2325.sz-updated{font-size:.83rem;color:var(--sz-muted);margin:0 0 28px !important} 2326.sz-legal-box{ 2327 background:var(--sz-violet-soft);border:1px solid rgba(124,92,255,0.22); 2328 border-radius:var(--sz-radius);padding:18px 22px;margin:0 0 26px; 2329} 2330.sz-legal-box p:last-child,.sz-legal-box ol:last-child,.sz-legal-box ul:last-child{margin-bottom:0} 2331 2332.sz-table{width:100%;border-collapse:collapse;margin:0 0 20px;font-size:.9rem} 2333.sz-table th,.sz-table td{ 2334 text-align:left;padding:10px 12px;border-bottom:1px solid var(--sz-border); 2335 color:var(--sz-muted);line-height:1.55;vertical-align:top; 2336} 2337.sz-table th{color:var(--sz-fg);font-weight:640;font-size:.82rem;letter-spacing:.02em} 2338.sz-table td:first-child{color:var(--sz-fg);font-weight:560;white-space:nowrap} 2339 2340.sz-footer{padding:32px clamp(20px,4vw,48px);border-top:1px solid var(--sz-border);background:var(--sz-surface)} 2341.sz-footer-inner{ 2342 max-width:1080px;margin:0 auto;display:flex;flex-wrap:wrap;gap:14px; 2343 align-items:center;justify-content:space-between;font-size:.85rem;color:var(--sz-muted); 2344} 2345.sz-footer-links{display:flex;flex-wrap:wrap;gap:16px} 2346.sz-footer-links a{color:var(--sz-muted);text-decoration:none} 2347.sz-footer-links a:hover{color:var(--sz-violet-deep)} 2348 2349/* âââ landing page (/systemzen) âââââââââââââââââââââââââââââââââââââââââââââââ 2350 Same shell, nav and palette as the legal pages above so the marketing page and 2351 the pages every store listing links to cannot drift apart visually. */ 2352.sz-hero{padding:clamp(56px,9vw,104px) clamp(20px,4vw,48px) clamp(40px,6vw,72px);text-align:center} 2353.sz-hero-inner{max-width:880px;margin:0 auto} 2354.sz-badge{ 2355 display:inline-block;padding:6px 14px;border-radius:999px;font-size:.78rem;font-weight:600; 2356 background:var(--sz-violet-soft);color:var(--sz-violet-deep);margin-bottom:20px; 2357} 2358.sz-h1{font-size:clamp(2.2rem,5.5vw,3.6rem);font-weight:750;letter-spacing:-.03em;line-height:1.05;margin:0 0 18px} 2359.sz-h1 em{font-style:normal;color:var(--sz-violet-deep)} 2360.sz-sub{font-size:clamp(1rem,2vw,1.16rem);color:var(--sz-muted);line-height:1.7;max-width:60ch;margin:0 auto 28px} 2361.sz-cta-row{display:flex;flex-wrap:wrap;gap:12px;justify-content:center} 2362.sz-btn{ 2363 display:inline-flex;align-items:center;gap:8px;padding:13px 26px;border-radius:999px; 2364 font-size:.9rem;font-weight:620;text-decoration:none;transition:transform .18s,box-shadow .18s; 2365} 2366.sz-btn:hover{transform:translateY(-1px)} 2367.sz-btn-primary{background:var(--sz-violet-deep);color:#fff;box-shadow:0 10px 26px -12px rgba(85,51,214,.6)} 2368.sz-btn-outline{background:var(--sz-surface);color:var(--sz-fg);border:1.5px solid var(--sz-border)} 2369.sz-platforms{font-size:.85rem;color:var(--sz-muted);margin-top:22px} 2370 2371/* The nav is sticky at 64px, so #download needs its own scroll offset or the 2372 heading lands underneath it when the hero CTA jumps here. */ 2373#download{scroll-margin-top:72px} 2374.sz-dl-grid{display:grid;gap:18px} 2375@media(min-width:820px){.sz-dl-grid{grid-template-columns:repeat(2,1fr)}} 2376@media(min-width:1180px){.sz-dl-grid{grid-template-columns:repeat(3,1fr)}} 2377.sz-dl-card{ 2378 background:var(--sz-surface);border:1px solid var(--sz-border);border-radius:var(--sz-radius); 2379 padding:24px;display:flex;flex-direction:column;gap:18px; 2380} 2381.sz-dl-head{display:flex;align-items:center;gap:12px} 2382.sz-dl-icon{width:44px;height:44px;border-radius:10px;flex:none} 2383.sz-dl-head h3{margin:0;font-size:1.05rem;font-weight:680} 2384.sz-dl-head p{margin:2px 0 0;font-size:.83rem;color:var(--sz-muted)} 2385.sz-dl-body{display:flex;flex-wrap:wrap;align-items:center;gap:18px} 2386.sz-dl-badge{display:inline-flex;text-decoration:none;transition:transform .18s} 2387.sz-dl-badge:hover{transform:translateY(-1px)} 2388.sz-dl-qr{display:flex;align-items:center;gap:12px;text-decoration:none;color:var(--sz-muted)} 2389/* White plate behind the code: the page background is tinted, and a QR needs a 2390 light quiet zone around it or a phone will not lock onto the finder patterns. */ 2391.sz-dl-qr img{ 2392 width:112px;height:112px;flex:none;border-radius:10px; 2393 background:#fff;border:1px solid var(--sz-border); 2394} 2395.sz-dl-qr span{font-size:.8rem;line-height:1.5;max-width:15ch} 2396.sz-dl-qr strong{display:block;font-size:.86rem;font-weight:640;color:var(--sz-fg)} 2397.sz-dl-url{font-size:.78rem;color:var(--sz-muted);word-break:break-all;margin:0} 2398.sz-dl-note{font-size:.85rem;color:var(--sz-muted);margin:26px 0 0} 2399 2400.sz-section{padding:clamp(48px,7vw,84px) clamp(20px,4vw,48px)} 2401.sz-section-inner{max-width:1080px;margin:0 auto} 2402.sz-label{font-size:.72rem;font-weight:680;letter-spacing:.18em;text-transform:uppercase;color:var(--sz-violet-deep);margin:0 0 12px} 2403.sz-h2{font-size:clamp(1.7rem,3.4vw,2.4rem);font-weight:720;letter-spacing:-.02em;margin:0 0 14px} 2404.sz-lead{font-size:1.02rem;color:var(--sz-muted);line-height:1.7;max-width:62ch;margin:0 0 36px} 2405.sz-grid{display:grid;gap:18px} 2406@media(min-width:640px){.sz-grid{grid-template-columns:repeat(2,1fr)}} 2407@media(min-width:960px){.sz-grid{grid-template-columns:repeat(3,1fr)}} 2408.sz-card{ 2409 background:var(--sz-surface);border:1px solid var(--sz-border);border-radius:var(--sz-radius); 2410 padding:22px; 2411} 2412.sz-card h3{font-size:1rem;font-weight:650;margin:0 0 8px} 2413.sz-card p{font-size:.9rem;color:var(--sz-muted);line-height:1.6;margin:0} 2414.sz-card-icon{font-size:1.35rem;margin-bottom:10px} 2415.sz-honest-band{background:linear-gradient(180deg,var(--sz-violet-soft) 0%,var(--sz-bg) 100%)} 2416.sz-plans{display:grid;gap:18px;margin-top:12px} 2417@media(min-width:820px){.sz-plans{grid-template-columns:repeat(2,1fr)}} 2418.sz-plan{background:var(--sz-surface);border:1px solid var(--sz-border);border-radius:var(--sz-radius);padding:24px} 2419.sz-plan.featured{border-color:var(--sz-violet);box-shadow:0 12px 30px -18px rgba(124,92,255,.5)} 2420.sz-plan h3{margin:0 0 4px;font-size:1.05rem;font-weight:680} 2421.sz-price{font-size:1.6rem;font-weight:720;margin:6px 0 14px} 2422.sz-price small{font-size:.82rem;font-weight:500;color:var(--sz-muted)} 2423.sz-plan ul{margin:0;padding-left:20px} 2424.sz-plan li{font-size:.9rem;color:var(--sz-muted);line-height:1.6;margin:0 0 7px} 2425`;function n4(){return(0,a.jsxs)("nav",{className:"sz-nav",children:[(0,a.jsxs)("a",{href:"/systemzen",className:"sz-nav-logo",children:[(0,a.jsx)("span",{className:"sz-nav-logo-icon",children:"S"}),(0,a.jsx)("span",{children:"SystemZen"})]}),(0,a.jsxs)("div",{className:"sz-nav-links",children:[(0,a.jsx)("a",{href:"/systemzen#download",children:"Download"}),(0,a.jsx)("a",{href:"/systemzen/advanced",children:"Advanced"}),(0,a.jsx)("a",{href:"/systemzen/manual",children:"Manual"}),(0,a.jsx)("a",{href:"/systemzen/support",children:"Support"}),(0,a.jsx)("a",{href:"/systemzen/privacy",children:"Privacy"}),(0,a.jsx)("a",{href:"/systemzen/terms",children:"Terms"})]})]})}function n3(){return(0,a.jsx)("footer",{className:"sz-footer",children:(0,a.jsxs)("div",{className:"sz-footer-inner",children:[(0,a.jsxs)("span",{children:["© ",new Date().getFullYear()," Soft Flourish"]}),(0,a.jsxs)("div",{className:"sz-footer-links",children:[(0,a.jsx)("a",{href:"/systemzen/manual",children:"Manual"}),(0,a.jsx)("a",{href:"/systemzen/support",children:"Support"}),(0,a.jsx)("a",{href:"/systemzen/account",children:"Account"}),(0,a.jsx)("a",{href:"/systemzen/privacy",children:"Privacy Policy"}),(0,a.jsx)("a",{href:"/systemzen/terms",children:"Terms of Use"}),(0,a.jsx)("a",{href:"/systemzen/deleteaccount",children:"Delete Account"}),(0,a.jsx)("a",{href:n1,children:"Contact"}),(0,a.jsx)("a",{href:"/",children:"softflourish.com"})]})]})})}function n7({children:e}){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:n8}}),(0,a.jsxs)("div",{className:"sz-wrap",children:[(0,a.jsx)(n4,{}),(0,a.jsx)("div",{className:"sz-legal",children:(0,a.jsx)("div",{className:"sz-legal-inner",children:e})}),(0,a.jsx)(n3,{})]})]})}let n9="/systemzen",ie="SystemZen-Advanced-1.0.7.dmg",ia=[{key:"mac",platform:"Mac",requirement:"Apple silicon and Intel",url:"https://apps.apple.com/app/id6794717513?mt=12",display:"apps.apple.com/app/id6794717513",badge:`${n9}/mac-app-store-badge.svg`,badgeAlt:"Download on the Mac App Store",badgeWidth:187,qr:`${n9}/qr-macappstore.svg`,qrLabel:"Scan to open the Mac App Store"},{key:"android",platform:"Android",requirement:"Phones and tablets",url:"https://play.google.com/store/apps/details?id=com.softflourish.systemzen",display:"play.google.com/store/apps/details?id=com.softflourish.systemzen",badge:`${n9}/google-play-badge.png`,badgeAlt:"Get it on Google Play",badgeWidth:180,qr:`${n9}/qr-googleplay.svg`,qrLabel:"Scan to open Google Play"},{key:"windows",platform:"Windows",requirement:"Windows 10 and 11 · x64 and ARM64",url:"https://apps.microsoft.com/detail/9p4c18nft1cn",display:"apps.microsoft.com/detail/9p4c18nft1cn",badge:`${n9}/microsoft-store-badge.svg`,badgeAlt:"Get it from Microsoft",badgeWidth:176,qr:`${n9}/qr-microsoftstore.svg`,qrLabel:"Scan to open the Microsoft Store"}],it=[{icon:"ð",title:"See what is actually using the space",body:"One scan lists app caches, duplicate photos, big downloads, and which apps are draining the battery or the data plan. Every number is measured, never invented to sell you a fix."},{icon:"ð§¹",title:"Clear only what is yours",body:"SystemZen touches its own cache and any folder you give it access to â with the size shown before you tap Clean, and the system's normal delete confirmation in front of every removal."},{icon:"ð¼ï¸",title:"Find duplicate photos for real",body:"Duplicates are found by comparing the files themselves, not by guessing from names or dates, so what it offers to remove really is the same picture twice."},{icon:"ð",title:"See where the storage went",body:"Photos, videos, downloads and SystemZen's own footprint, broken out against the total space on the device."},{icon:"ð¶",title:"Watch the data plan",body:"Real per-app data totals alongside live throughput, so you can see which app is spending the plan rather than guess."}
2425,{icon:"â¤ï¸",title:"A health score that means something",body:"A live score plus a ranked list of what to fix first, each item tied to something that was actually measured on the device."}];function is(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:n8}}),(0,a.jsxs)("div",{className:"sz-wrap",children:[(0,a.jsx)(n4,{}),(0,a.jsx)("section",{className:"sz-hero",children:(0,a.jsxs)("div",{className:"sz-hero-inner",children:[(0,a.jsx)("div",{className:"sz-badge",children:"ð Scans on your device · nothing is uploaded"}),(0,a.jsxs)("h1",{className:"sz-h1",children:["Reclaim your storage â",(0,a.jsx)("br",{}),(0,a.jsx)("em",{children:"with numbers you can trust"})]}),(0,a.jsx)("p",{className:"sz-sub",children:"SystemZen finds what is really taking up space on your Mac, PC, phone and tablet â app caches, duplicate photos, forgotten downloads â and clears it safely, with your confirmation on every removal. No fake scan results, no invented problems."}),(0,a.jsxs)("div",{className:"sz-cta-row",children:[(0,a.jsx)("a",{href:"#download",className:"sz-btn sz-btn-primary",children:"Get SystemZen"}),(0,a.jsx)("a",{href:"/systemzen/account",className:"sz-btn sz-btn-outline",children:"Manage account"})]}),(0,a.jsx)("p",{className:"sz-platforms",children:"Free to download · on the Mac App Store, Google Play and the Microsoft Store now"})]})}),(0,a.jsx)("section",{className:"sz-section",id:"download",children:(0,a.jsxs)("div",{className:"sz-section-inner",children:[(0,a.jsx)("p",{className:"sz-label",children:"Download"}),(0,a.jsx)("h2",{className:"sz-h2",children:"Pick your device"}),(0,a.jsx)("p",{className:"sz-lead",children:"The same app on every platform, and one Pro subscription covers all of them."}),(0,a.jsx)("div",{className:"sz-dl-grid",children:ia.map(e=>(0,a.jsxs)("div",{className:"sz-dl-card",children:[(0,a.jsxs)("div",{className:"sz-dl-head",children:[(0,a.jsx)("img",{className:"sz-dl-icon",src:`${n9}/icon.png`,alt:"",width:44,height:44}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h3",{children:e.platform}),(0,a.jsx)("p",{children:e.requirement})]})]}),(0,a.jsxs)("div",{className:"sz-dl-body",children:[(0,a.jsx)("a",{className:"sz-dl-badge",href:e.url,target:"_blank",rel:"noopener noreferrer",children:(0,a.jsx)("img",{src:e.badge,alt:e.badgeAlt,width:e.badgeWidth,height:40})}),(0,a.jsxs)("a",{className:"sz-dl-qr",href:e.url,target:"_blank",rel:"noopener noreferrer",children:[(0,a.jsx)("img",{src:e.qr,alt:"",width:112,height:112}),(0,a.jsxs)("span",{children:[(0,a.jsx)("strong",{children:"Scan"}),e.qrLabel]})]})]}),(0,a.jsx)("p",{className:"sz-dl-url",children:e.display})]},e.key))}),(0,a.jsx)("p",{className:"sz-dl-note",children:"iPhone and iPad are in App Store review. This page gets its download button the day that listing goes live â we do not publish a store link before it works."})]})}),(0,a.jsx)("section",{className:"sz-section sz-honest-band",children:(0,a.jsxs)("div",{className:"sz-section-inner",children:[(0,a.jsx)("p",{className:"sz-label",children:"What it does"}),(0,a.jsx)("h2",{className:"sz-h2",children:"Every number comes from your device"}),(0,a.jsx)("p",{className:"sz-lead",children:"Cleaner apps have a reputation for inventing problems: a scan bar that always lands on â847 issues foundâ, whatever the device. SystemZen reports what it measured, and where the operating system makes a fix impossible it says so instead of pretending â freeing memory is listed as unavailable on a phone, not faked with a spinner."}),(0,a.jsx)("div",{className:"sz-grid",children:it.map(e=>(0,a.jsxs)("div",{className:"sz-card",children:[(0,a.jsx)("div",{className:"sz-card-icon",children:e.icon}),(0,a.jsx)("h3",{children:e.title}),(0,a.jsx)("p",{children:e.body})]},e.title))})]})}),(0,a.jsx)("section",{className:"sz-section",children:(0,a.jsxs)("div",{className:"sz-section-inner",children:[(0,a.jsx)("p",{className:"sz-label",children:"Pricing"}),(0,a.jsx)("h2",{className:"sz-h2",children:"One subscription, every device"}),(0,a.jsx)("p",{className:"sz-lead",children:"Buy it once and sign in anyw
2425here â Mac, Windows, Android, and iPhone when it lands. Prices shown are US; your store shows your own currency."}),(0,a.jsxs)("div",{className:"sz-plans",children:[(0,a.jsxs)("div",{className:"sz-plan",children:[(0,a.jsx)("h3",{children:"Free"}),(0,a.jsx)("div",{className:"sz-price",children:"$0"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"A full scan, any time"}),(0,a.jsx)("li",{children:"Live health score"}),(0,a.jsx)("li",{children:"Storage and data breakdowns"}),(0,a.jsx)("li",{children:"One starter cache clean"})]})]}),(0,a.jsxs)("div",{className:"sz-plan featured",children:[(0,a.jsx)("h3",{children:"Pro"}),(0,a.jsxs)("div",{className:"sz-price",children:["$9.99 ",(0,a.jsx)("small",{children:"/month · or $79.99/year"})]}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Unlimited cleanup"}),(0,a.jsx)("li",{children:"Duplicate photo removal"}),(0,a.jsx)("li",{children:"The full tools list"}),(0,a.jsx)("li",{children:"Scheduled automatic maintenance"}),(0,a.jsx)("li",{children:"Scan history synced across your devices"})]})]})]})]})}),(0,a.jsx)(n3,{})]})]})}function ir(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:n8}}),(0,a.jsxs)("div",{className:"sz-wrap",children:[(0,a.jsx)(n4,{}),(0,a.jsx)("section",{className:"sz-hero",children:(0,a.jsxs)("div",{className:"sz-hero-inner",children:[(0,a.jsx)("div",{className:"sz-badge",children:"ð Notarized by Apple · not sandboxed"}),(0,a.jsxs)("h1",{className:"sz-h1",children:["SystemZen Advanced â",(0,a.jsx)("br",{}),(0,a.jsx)("em",{children:"the whole disk, not just a corner of it"})]}),(0,a.jsx)("p",{className:"sz-sub",children:"The Mac App Store version runs in Appleâs sandbox, which means it can only read folders you hand it one at a time. Advanced asks for Full Disk Access instead and scans your Mac the way a maintenance app is supposed to. Same app, same subscription â the only difference is what macOS lets it touch."}),(0,a.jsxs)("div",{className:"sz-cta-row",children:[(0,a.jsxs)("a",{href:`${n9}/${ie}`,className:"sz-btn sz-btn-primary",children:["Download for Mac · ","19.0 MB"]}),(0,a.jsx)("a",{href:"/systemzen#download",className:"sz-btn sz-btn-outline",children:"Or get it from the Mac App Store"})]}),(0,a.jsxs)("p",{className:"sz-platforms",children:["Version ","1.0.7"," · macOS ","13"," or later · Apple silicon and Intel"]})]})}),(0,a.jsx)("section",{className:"sz-section",children:(0,a.jsxs)("div",{className:"sz-section-inner",children:[(0,a.jsx)("p",{className:"sz-label",children:"Which one do I want?"}),(0,a.jsx)("h2",{className:"sz-h2",children:"The honest comparison"}),(0,a.jsx)("p",{className:"sz-lead",children:"Most people should take the App Store version. Choose Advanced if it told you âNothing foundâ on a Mac you know is full, or if you want it to reach system caches and other usersâ leftovers."}),(0,a.jsxs)("table",{className:"sz-table",children:[(0,a.jsx)("thead",{children:(0,a.jsxs)("tr",{children:[(0,a.jsx)("th",{}),(0,a.jsx)("th",{children:"App Store"}),(0,a.jsx)("th",{children:"Advanced"})]})}),(0,a.jsxs)("tbody",{children:[(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:"How you get it"}),(0,a.jsx)("td",{children:"Mac App Store"}),(0,a.jsx)("td",{children:"Downloaded here"})]}),(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:"Updates"}),(0,a.jsx)("td",{children:"Automatic"}),(0,a.jsx)("td",{children:"You download the new version"})]}),(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:"What it can read"}),(0,a.jsx)("td",{children:"Folders you pick in a chooser"}),(0,a.jsx)("td",{children:"Everything Full Disk Access allows"})]}),(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:"System caches and logs"}),(0,a.jsx)("td",{children:"No â the sandbox refuses"}),(0,a.jsx)("td",{children:"Yes"})]}),(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:"Buying Pro"}),(0,a.jsx)("td",{children:"In the app"}),(0,a.jsx)("td",{children:"On this website"})]}),(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:"Pro bought on another device"}),(0,a.jsx)("td",{colSpan:2,children:"Works in both â sign in and it follows you"})]})]})]})]})}),(0,a.jsx)("section",{className:"sz-section",children:(0,a.jsxs)("div",{className:"sz-section-inner",children:[(0,a.jsx)("p",{className:"sz-label",children:"Installing"}),(0,a.jsx)("h2",{className:"sz-h2",children:"Three steps, and the third one matters"}),(0,a.jsxs)("div",{className:"sz-grid",children:[(0,a.jsxs)("div",{className:"sz-card",children:[(0,a.jsx)("div",{className:"sz-card-icon",children:"1"}),(0,a.jsx)("h3",{children:"Open the download"}
vendor: 1,288 bytes, line 2425
2425),(0,a.jsx)("p",{children:"Drag SystemZen Advanced into your Applications folder. It is notarized by Apple, so it opens normally â no right-click, no warning to click past."})]}),(0,a.jsxs)("div",{className:"sz-card",children:[(0,a.jsx)("div",{className:"sz-card-icon",children:"2"}),(0,a.jsx)("h3",{children:"Grant Full Disk Access"}),(0,a.jsx)("p",{children:"The app shows you the switch and opens System Settings at the right page. Without it, Advanced sees no more than the App Store version does."})]}),(0,a.jsxs)("div",{className:"sz-card",children:[(0,a.jsx)("div",{className:"sz-card-icon",children:"3"}),(0,a.jsx)("h3",{children:"Quit and open it again"}),(0,a.jsx)("p",{children:"macOS only applies Full Disk Access to an app that has restarted. Skip this and it will still report almost nothing â this is the step people miss."})]})]})]})}),(0,a.jsx)("section",{className:"sz-section",children:(0,a.jsxs)("div",{className:"sz-section-inner",children:[(0,a.jsx)("p",{className:"sz-label",children:"Verify it yourself"}),(0,a.jsx)("h2",{className:"sz-h2",children:"What you are running"}),(0,a.jsxs)("p",{className:"sz-lead",children:["Signed by Fenul LLC (team ","A5SQ29M7WW",") and notarized by Apple, which means Apple scanned this exact file for malware and issued it a t
2425icket. macOS checks that ticket on first launch even with no internet connection."]}),(0,a.jsxs)("p",{className:"sz-dl-url",children:[(0,a.jsx)("strong",{children:"SHA-256"}),(0,a.jsx)("br",{}),(0,a.jsx)("code",{children:"967930f43923ca4d036aca5dda7f24d381763de723fdb6be41b6b69d86c2ef5a"})]}),(0,a.jsxs)("p",{className:"sz-dl-note",children:["To check it before opening:"," ",(0,a.jsxs)("code",{children:["shasum -a 256 ~/Downloads/",ie]})]})]})}),(0,a.jsx)("section",{className:"sz-section",children:(0,a.jsxs)("div",{className:"sz-section-inner",children:[(0,a.jsx)("p",{className:"sz-label",children:"Pro"}),(0,a.jsx)("h2",{className:"sz-h2",children:"One subscription, every device"}),(0,a.jsx)("p",{className:"sz-lead",children:"Advanced is free to download, and the free tier does the same work here as everywhere else. Pro is bought on this website rather than inside the app, because a build that does not come from the App Store cannot take payment through it. Buy it once and sign in on your Mac, iPhone, Android or PC â it is the same subscription."}),(0,a.jsxs)("div",{className:"sz-cta-row",children:[(0,a.jsx)("a",{href:"/systemzen/account",className:"sz-btn sz-btn-primary",children:"Manage or buy Pro"}),(0,a.jsx)("a",{href:"/systemzen/support",className:"sz-btn sz-btn-outline",children:"Support"})]})]})}),(0,a.jsx)(n3,{})]})]})}function ii(){return(0,a.jsxs)(n7,{children:[(0,a.jsx)("h1",{children:"SystemZen Privacy Policy"}),(0,a.jsxs)("p",{className:"sz-updated",children:[n6," · Covers SystemZen for ",n5]}),(0,a.jsx)("div",{className:"sz-legal-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"The short version."})," SystemZen scans your device on your device. The names of your files, your photos, and the apps you have installed are never sent to us or to anyone else. If you sign in, we store your email address and a short one-line summary of each maintenance run so your history follows you between devices. The free tier of the iPhone, iPad, and Android apps shows ads, which means Google receives the usual advertising signals described below; the Mac and Windows apps show no ads at all."]})}),(0,a.jsxs)("p",{children:["SystemZen is published by ",(0,a.jsx)("strong",{children:"Soft Flourish"})," (California, USA). This policy explains what the app collects, why, and what you can do about it."]}),(0,a.jsx)("h2",{children:"What stays on your device"}),(0,a.jsx)("p",{children:"Scanning is entirely local. The following never leave your device, and there is no endpoint on our servers that could receive them:"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"File and folder names, paths, and contents â including anything SystemZen offers to clean."}),(0,a.jsx)("li",{children:"Your photos and videos, and the fingerprints used to detect duplicates among them."}),(0,a.jsx)("li",{children:"The list of apps installed on your device, and their individual storage, battery, or data figures."}),(0,a.jsx)("li",{children:"Anything you type into the in-app search field, which is a local filter and nothing more."})]}),(0,a.jsx)("h2",{children:"What we collect when you sign in"}),(0,a.jsx)("p",{children:"Signing in is optional â every scan and every tool works without an account. It exists so your maintenance history is available on your other devices. When you do sign in, we store:"}),(0,a.jsxs)("table",{className:"sz-table",children:[(0,a.jsx)("thead",{children:(0,a.jsxs)("tr",{children:[(0,a.jsx)("th",{children:"Data"}),(0,a.jsx)("th",{children:"Why"})]})}),(0,a.jsxs)("tbody",{children:[(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:"Email address"}),(0,a.jsx)("td",{children:"Identifies your account for both email/password and Google sign-in."})]}),(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:"Account identifier"}),(0,a.jsx)("td",{children:"The internal user ID your history is filed under."})]}),(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:"Name"}),(0,a.jsx)("td",{children:"Only if you sign in with Google, which supplies your display name. Signing up with an email address and password never asks for a name."})]}),(0,a.jsxs)("tr",{children:[(0,a.jsx)("td",{children:"Maintenance history"}),(0,a.jsxs)("td",{children:["For each completed run: what kind it was, roughly how much space it reclaimed, how many items it touched, when, and a device label such as âGoogle Pixel 8â. The stored summary looks like ",(0,a.jsx)("em",{children:"âReclaimed 1.2 GB · 34 itemsâ"})," â never a list of what those items were."]})]})]})]}),(0,a.jsx)("p",{children:"Accounts and history are held in Google Firebase (Authentication and Cloud Firestore) on our behalf. All of it is encrypted in transit."}),(0,a.jsx)("h2",{children:"Advertising â free tier, mobile only"}),(0,a.jsxs)("p",{children:["The iPhone, iPad, and Android apps are supported by ads on the free tier, served by"," ",(0,a.jsx)("strong",{children:"Google AdMob"}),". SystemZen Pro removes them. The Mac and Windows apps contain no advertising code whatsoever."]}),(0,a.jsx)("p",{children:"When an ad is requested, Google collects and receives:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Approximate location"})," â a coarse region derived from your IP address. The app holds no location permission and never reads your GPS."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"App interactions"})," â which ads were shown, viewed, or tapped."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Diagnostics"})," â app performance signals such as launch time and energy use."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Device or other identifiers"})," â the app instance identifier, and on Android the advertising ID. iPhone and iPad supply no advertising identifier at all, because SystemZen never asks permission to track."]})]}),(0,a.jsxs)("p",{children:["These go to Google as an advertising partner and are used for serving ads, measurement, and fraud prevention. Google's handling is governed by its own"," ",(0,a.jsx)("a",{href:"https://policies.google.com/technologies/partner-sites",children:"privacy policy"}),"."]}),(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"SystemZen does not track you."}),"Every ad request the app makes is marked non-personalized, on every platform. The ads you see are not chosen from a profile of you, and n
2425othing SystemZen sends is combined with your activity in other companies' apps or websites. On iPhone and iPad there is no App Tracking Transparency prompt because there is nothing to ask for: the app never requests permission to track and never reads your device's advertising identifier. On Android, Google may still use the advertising ID for frequency capping and fraud prevention, and you can reset or delete it under"," ",(0,a.jsx)("strong",{children:"Settings â Privacy â Ads"}),"."]}),(0,a.jsx)("h2",{children:"Analytics"}),(0,a.jsx)("p",{children:"The mobile apps include Google Firebase Analytics, which automatically records app opens, screen views, session length, and basic device and performance information. We use it to see which features are actually used and where the app is slow. We do not upload custom events describing your files or scan contents."}),(0,a.jsx)("h2",{children:"Retention"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Account and history"})," â kept until you delete your account, then erased immediately. See"," ",(0,a.jsx)("a",{href:"/systemzen/deleteaccount",children:"Delete Your Account & Data"}),"."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Encrypted operational backups"})," â deleted records may survive in backups for up to ",(0,a.jsx)("strong",{children:"30 days"})," before those roll off."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Advertising and analytics data"})," â retained by Google under its own retention schedules, not ours."]})]}),(0,a.jsx)("h2",{children:"Your choices"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Use SystemZen without signing in â nothing about you is stored at all."}),(0,a.jsxs)("li",{children:["Delete your account and history from inside the app at any time, or by email if you cannot sign in: ",(0,a.jsx)("a",{href:"/systemzen/deleteaccount",children:"Delete Your Account & Data"}),"."]}),(0,a.jsxs)("li",{children:["On Android, reset or delete your advertising ID under"," ",(0,a.jsx)("strong",{children:"Settings â Privacy â Ads"}),". On iPhone and iPad there is nothing to reset â SystemZen never reads one."]}),(0,a.jsxs)("li",{children:["Depending on where you live, you may have rights to access, correct, export, or erase your personal data, and to object to its processing. Write to"," ",(0,a.jsx)("a",{href:`mailto:${n2}?subject=SystemZen%20privacy%20request`,children:n2})," ","and we will respond within 30 days. We do not sell personal information."]})]}),(0,a.jsx)("h2",{children:"Permissions the app may ask for"}),(0,a.jsx)("p",{children:"Each is requested only when you use the feature that needs it, and declining leaves the rest of the app working:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Storage / folder access"})," â so a scan can measure and clean the folders you point it at. SystemZen only ever sees the locations you grant."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Usage Access (Android)"})," â so per-app data and battery totals are real numbers rather than estimates."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Notifications"})," â for scheduled maintenance results, if you enable them."]})]}),(0,a.jsx)("h2",{children:"Children"}),(0,a.jsx)("p",{children:"SystemZen is not directed to children and is rated for general audiences aged 18 and over. We do not knowingly collect personal information from children. If you believe a child has given us data, write to us and we will delete it."}),(0,a.jsx)("h2",{children:"Changes"}),(0,a.jsx)("p",{children:"If this policy changes materially we will update the date at the top and, where the change affects what we collect, note it in the app's release notes."}),(0,a.jsx)("h2",{children:"Contact"}),(0,a.jsxs)("p",{children:["Soft Flourish, California, USA â"," ",(0,a.jsx)("a",{href:`mailto:${n2}?subject=SystemZen%20privacy`,children:n2})," or the"," ",(0,a.jsx)("a",{href:n1,children:"contact form"}),". See also"," ",(0,a.jsx)("a",{href:"/systemzen/terms",children:"Terms of Use"})," and ",(0,a.jsx)("a",{href:"/systemzen/support",children:"Support"}),"."]})]})}function io(){return(0,a.jsxs)(n7,{children:[(0,a.jsx)("h1",{children:"Delete Your SystemZen Account & Data"}),(0,a.jsx)("p",{className:"sz-updated",children:n6}),(0,a.jsxs)("p",{children:["This page explains how to delete your account and its associated data in"," ",(0,a.jsx)("strong",{children:"SystemZen"}),", published by ",(0,a.jsx)("strong",{children:"Soft Flourish"}),". It applies to the ",n5," apps."]}),(0,a.jsxs)("div",{className:"sz-legal-box",children:[(0,a.jsx)("p",{children:(0,a.jsx)("strong",{children:"Delete your account from inside the app â no request needed."})}),(0,a.jsxs)("ol",{children:[(0,a.jsxs)("li",{children:["Open ",(0,a.jsx)("strong",{children:"SystemZen"})," and sign in if you are not already."]}),(0,a.jsxs)("li",{children:["Open the ",(0,a.jsx)("strong",{children:"account menu"}),"(your name or the account icon in the app's navigation)."]}),(0,a.jsxs)("li",{children:["Choose ",(0,a.jsx)("strong",{children:"Delete Account"})," and confirm on the warning prompt."]})]}),(0,a.jsx)("p",{children:"Deletion is immediate and cannot be undone. If you have been signed in for a long time the app may ask you to sign in once more first â that is a security check, not an extra step you can skip."})]}),(0,a.jsx)("h2",{children:"If you cannot sign in"}),(0,a.jsxs)("p",{children:["Email"," ",(0,a.jsx)("a",{href:`mailto:${n2}?subject=Delete%20SystemZen%20account`,children:n2})," ","from the address on the account, with the subject âDelete SystemZen accountâ. We verify ownership of the address, complete the deletion within 30 days, and confirm b
2425y email when it is done."]}),(0,a.jsx)("h2",{children:"What is deleted"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Your SystemZen sign-in account itself â email/password or Google sign-in."}),(0,a.jsx)("li",{children:"Your email address, your account identifier, and your name if Google supplied one."}),(0,a.jsx)("li",{children:"Your entire maintenance history: every recorded run, its reclaimed-space figure, item count, timestamp, and device label."})]}),(0,a.jsx)("p",{children:"These are erased immediately. There is no soft-delete and no recovery window â once it is done we cannot restore any of it."}),(0,a.jsx)("h2",{children:"What is kept, and for how long"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Encrypted operational backups"})," â deleted records may persist for up to"," ",(0,a.jsx)("strong",{children:"30 days"})," before those backups roll off, after which they are gone."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Advertising and analytics data"}),"â the free mobile tier's ad and analytics signals are held by Google under its own retention schedule and are not tied to your SystemZen account. On Android you can reset your advertising ID in device settings to break the link on Google's side."]})]}),(0,a.jsxs)("p",{children:["Nothing about your files is involved in any of this. SystemZen scans on your device, so file names, photos, and your installed-app list never reach our servers in the first place. See the ",(0,a.jsx)("a",{href:"/systemzen/privacy",children:"Privacy Policy"}),"."]}),(0,a.jsx)("h2",{children:"What deletion does not do"}),(0,a.jsxs)("p",{children:["Deleting your account does not uninstall the app, and it does not cancel a subscription if you have one. Cancel that separately with whoever took the payment â App Store Subscriptions, Play Store Subscriptions, or, for a subscription bought on the web (which is how Windows buys), your ",(0,a.jsx)("a",{href:"/systemzen/account",children:"account page"})," â or billing continues."]}),(0,a.jsx)("h2",{children:"Legal"}),(0,a.jsxs)("p",{children:[(0,a.jsx)("a",{href:"/systemzen/privacy",children:"Privacy Policy"})," ·"," ",(0,a.jsx)("a",{href:"/systemzen/terms",children:"Terms of Use"})," · ",(0,a.jsx)("a",{href:"/systemzen/support",children:"Support"})]})]})}function il(){return(0,a.jsxs)(n7,{children:[(0,a.jsx)("h1",{children:"SystemZen â Support"}),(0,a.jsxs)("p",{className:"sz-updated",children:[n6," · Covers SystemZen for ",n5]}),(0,a.jsx)("div",{className:"sz-legal-box",children:(0,a.jsxs)("p",{children:[(0,a.jsx)("strong",{children:"Ask us anything:"})," ",(0,a.jsx)("a",{href:`mailto:${n2}?subject=SystemZen%20support`,children:n2})," â or use the ",(0,a.jsx)("a",{href:n1,children:"contact form"}),".",(0,a.jsx)("br",{}),"We answer within one business day (MonâFri, U.S. Pacific Time)."]})}),(0,a.jsx)("h2",{children:"What to include"}),(0,a.jsx)("p",{children:"None of this is required, but it usually saves a round trip:"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"Your platform and OS version â macOS, Windows, iPhone/iPad, or Android"}),(0,a.jsx)("li",{children:"The app version, from Settings â About"}),(0,a.jsx)("li",{children:"Whether you signed in with Google or with an email address and password"}),(0,a.jsx)("li",{children:"What you did, what you expected, and what happened instead"})]}),(0,a.jsx)("p",{children:"Please don't send us a file you were trying to clean. SystemZen works on your device and we never receive your files â we'd rather keep it that way. A screenshot with sensitive parts covered is usually enough."}),(0,a.jsx)("h2",{id:"scan-results",children:"A scan says I have less to clean than I expected"}),(0,a.jsx)("p",{children:"That is usually correct, and it is deliberate. SystemZen only reports space it can actually reclaim in places you have granted it access to. It will never inflate a number to make a cleanup look more impressive â if a competing app claims many times more, it is generally counting things it cannot safely delete. Granting access to more folders will legitimately raise the figure."}),(0,a.jsx)("h2",{id:"permissions",children:"The app is asking for Usage Access or folder access"}),(0,a.jsxs)("p",{children:["Per-app data and battery totals need ",(0,a.jsx)("strong",{children:"Usage Access"})," on Android to be real measurements rather than estimates, and cleaning a folder needs access to that folder. SystemZen only ever sees what you grant, and declining leaves the rest of the app working."]}),(0,a.jsx)("h2",{id:"ads",children:"Why am I seeing ads?"}),(0,a.jsxs)("p",{children:["The free tier of the iPhone, iPad, and Android apps is ad-supported; SystemZen Pro is ad-free. The Mac and Windows apps never show ads. On iPhone and iPad you can decline tracking when asked and still use the app â you will simply see non-personalized ads. What the ads collect is set out in the ",(0,a.jsx)("a",{href:"/systemzen/privacy",children:"Privacy Policy"}),"."]}),(0,a.jsx)("h2",{id:"restore-purchases",children:"I paid, but the app still shows the free tier"}),(0,a.jsxs)("p",{children:["Open the paywall and choose ",(0,a.jsx)("strong",{children:"Restore Purchases"}),", making sure you are signed in to the same Apple Account or Google account you bought with. Pro follows your SystemZen account, so signing in with the same email on another device is usually all it takes â
2425check what your account currently holds on the"," ",(0,a.jsx)("a",{href:"/systemzen/account",children:"account page"}),". If it still does not unlock, email us the receipt and we will sort it out."]}),(0,a.jsx)("h2",{id:"cancel",children:"How do I cancel my subscription?"}),(0,a.jsx)("p",{children:"Cancelling is handled by whoever took the payment:"}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Mac, iPhone, iPad:"})," System Settings / Settings â Apple Account â Subscriptions."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Android:"})," Play Store â Subscriptions."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("strong",{children:"Windows:"})," the Windows app buys on the web rather than through the Microsoft Store, so cancel on your"," ",(0,a.jsx)("a",{href:"/systemzen/account",children:"account page"}),"."]})]}),(0,a.jsx)("p",{children:"You keep paid features until the end of the period you already paid for. Refunds are issued by the store that processed the payment, under its own policy."}),(0,a.jsx)("h2",{id:"sync",children:"My history is not showing on my other device"}),(0,a.jsx)("p",{children:"History syncs per account, so sign in with the same account on both devices. Runs completed while signed out are recorded on that device only and are not backfilled after you sign in."}),(0,a.jsx)("h2",{id:"delete-account",children:"Delete my account and data"}),(0,a.jsxs)("p",{children:["See ",(0,a.jsx)("a",{href:"/systemzen/deleteaccount",children:"Delete Your Account & Data"})," for the in-app steps, the email fallback, and exactly what is deleted versus retained."]}),(0,a.jsx)("h2",{id:"bug",children:"Reporting a bug"}),(0,a.jsx)("p",{children:"Tell us the steps that trigger it, what you expected, and what happened â plus the platform and app version. If a cleanup behaved unexpectedly, the section you were in and the figure it showed you help far more than the files themselves, which we don't want."}),(0,a.jsx)("h2",{children:"Legal"}),(0,a.jsxs)("p",{children:[(0,a.jsx)("a",{href:"/systemzen/privacy",children:"Privacy Policy"})," ·"," ",(0,a.jsx)("a",{href:"/systemzen/terms",children:"Terms of Use"}),". SystemZen is published by Soft Flourish, California, USA."]})]})}function ic(){return(0,a.jsxs)(n7,{children:[(0,a.jsx)("h1",{children:"SystemZen â User Manual"}),(0,a.jsxs)("p",{className:"sz-updated",children:[n6," · Covers SystemZen for ",n5]}),(0,a.jsx)("div",{className:"sz-legal-box",children:(0,a.jsx)("p",{children:"Every number SystemZen shows traces back to real files or caches you can inspect. Nothing is removed without you seeing what it is first, and nothing is inflated to make a cleanup look bigger than it is."})}),(0,a.jsx)("h2",{children:"Running your first scan"}),(0,a.jsx)("p",{children:"Open the app and run a full scan â free on every platform, any time. It lists exactly what is using your storage: app caches, duplicate photos, large downloads, and which apps are draining your battery or your data plan."}),(0,a.jsx)("h2",{children:"Cleaning up"}),(0,a.jsx)("p",{children:"As a junk cleaner, SystemZen only touches what is yours: its own cache and any folder you have specifically granted access to, with the size shown before you tap Clean. Files move through your operating systemâs normal delete confirmation â nothing is removed without your say-so."}),(0,a.jsx)("h2",{children:"Finding duplicate photos"}),(0,a.jsx)("p",{children:"My Clutter scans your photo and download library for duplicates and large files using real file hashing, not guesswork by filename or date â so what it offers to remove really is the same file twice."}),(0,a.jsx)("h2",{children:"Space Lens"}),(0,a.jsx)("p",{children:"Breaks down your photos, videos, downloads, and SystemZenâs own footprint against your total device storage, so you can see at a glance where the space actually went."}),(0,a.jsx)("h2",{children:"Watching your data usage"}),(0,a.jsx)("p",{children:"Optimize Internet Speed shows real per-app data totals â with Usage Access granted on Android â plus live aggregate throughput, so you can see exactly what is using your data plan."}),(0,a.jsx)("h2",{children:"Performance"}),(0,a.jsx)("p",{children:"Reads live RAM and battery figures straight from the operating system â total memory, what is free, and whether the system is under pressure â next to per-app data totals. Where the platformâs own sandbox makes a fix impossible, SystemZen says so rather than faking a spinner â freeing memory is listed as unavailable on a phone, for example, not pretended at."}),(0,a.jsx)("h2",{children:"Smart Care"}),(0,a.jsx)("p",{children:"A live health score for your device plus a ranked list of what to fix first, based on what the scan actually measured â never a generic checklist."}),(0,a.jsx)("h2",{children:"Signing in and history sync"}),(0,a.jsxs)("p",{children:["Sign in and your scan history syncs across every device you use SystemZen on. Runs completed while signed out stay on that device only and are not backfilled after you sign in â see ",(0,a.jsx)("a",{href:"/systemzen/support#sync",children:"syncing"})," if a run is missing."]}),(0,a.jsx)("h2",{children:"Free vs. Pro"}),(0,a.jsxs)("p",{children:["Free includes a full scan, a live health score, and one starter cache clean. Pro unlocks unlimited cleanup, duplicate removal, the full tools list, and scheduled automatic maintenance. One subscription covers every device you sign into â see"," ",(0,a.jsx)("a",{href:"/systemzen",children:"pricing"}),"."]}),(0,a.jsx)("h2",{children:"If something isnât working"}),(0,a.jsxs)("p",{children:["See ",(0,a.jsx)("a",{href:"/systemzen/support",children:"support"})," for permissions questions, restoring a purchase, and deleting your account."]})]})}function id(){return(0,a.jsxs)(n7,{children:[(0,a.jsx)("h1",{children:"SystemZen Terms of Use"}),(0,a.jsxs)("p",{className:"sz-updated",children:[n6," · Covers SystemZen for ",n5]}),(0,a.jsxs)("p",{children:["These terms are the agreement between you and ",(0,a.jsx)("strong",{children:"Soft Flourish"})," (California, USA), for the SystemZen apps. Installing or using SystemZen means you accept them. If you do not, please do not use the app."]}),(0,a.jsx)("h2",{children:"1. Licence"}),(0,a.jsx)("p",{children:"We grant you a personal, non-exclusive, non-transferable, revocable licence to use SystemZen on devices you own or c
2425ontrol, for personal or internal business use. You may not resell, rent, sublicense, reverse-engineer, or attempt to defeat the app's licensing, except to the extent your local law expressly permits it regardless of contract."}),(0,a.jsx)("h2",{children:"2. Free tier, ads, and Pro"}),(0,a.jsx)("p",{children:"Scanning, the health score, and monitoring are free. Cleanup and the advanced tools are part of SystemZen Pro, with a limited free allowance so you can try them first. The free tier of the iPhone, iPad, and Android apps displays advertising supplied by Google AdMob; Pro is ad-free, and the Mac and Windows apps show no ads at any tier. We may change what each tier includes for future releases, but not retroactively for a period you have already paid for."}),(0,a.jsx)("h2",{children:"3. Purchases, billing, and refunds"}),(0,a.jsxs)("p",{children:["If you buy Pro inside the iPhone, iPad, Mac, or Android app, the purchase is processed by Apple or Google under that store's terms. The Windows app instead sends you to your"," ",(0,a.jsx)("a",{href:"/systemzen/account",children:"account page"}),", where the payment is processed by Stripe through our provider RevenueCat. Either way we never see your card details. One subscription covers every device you sign in on. Subscriptions renew automatically until cancelled; cancel through whoever took the payment, at least 24 hours before the renewal date, and you keep access until the end of the period you have paid for. Refunds are issued under the policy of the store that processed the payment; for a web purchase, contact us. Any free trial converts to a paid period unless cancelled before it ends."]}),(0,a.jsx)("h2",{children:"4. Your responsibility for what you delete"}),(0,a.jsxs)("p",{children:["SystemZen shows you what it proposes to remove, with sizes, and nothing is deleted without your confirmation. ",(0,a.jsx)("strong",{children:"You are responsible for that confirmation."})," Deleting caches, duplicates, or large files is normally safe, but it is irreversible, and only you know which of your own files matter. Keep a current backup before any bulk cleanup. We are not liable for files you chose to delete."]}),(0,a.jsx)("h2",{children:"5. Accounts"}),(0,a.jsxs)("p",{children:["An account is optional and exists to sync your maintenance history. Keep your credentials secure; you are responsible for activity under your account. We may suspend an account used to abuse the service or break these terms. You can delete your account at any time â see"," ",(0,a.jsx)("a",{href:"/systemzen/deleteaccount",children:"Delete Your Account & Data"}),"."]}),(0,a.jsx)("h2",{children:"6. Acceptable use"}),(0,a.jsx)("p",{children:"Do not use SystemZen to interfere with a device you do not control, to circumvent security or licensing on your system, or in any way that breaks applicable law or the rules of the store you installed it from. Do not attempt to disrupt our services or extract data from them beyond your own."}),(0,a.jsx)("h2",{children:"7. Privacy"}),(0,a.jsxs)("p",{children:["The ",(0,a.jsx)("a",{href:"/systemzen/privacy",children:"Privacy Policy"})," forms part of these terms and describes what the app collects and what stays on your device."]}),(0,a.jsx)("h2",{children:"8. Availability and changes"}),(0,a.jsx)("p",{children:"We aim to keep the service running but do not guarantee uninterrupted availability. Features that depend on our servers â chiefly history sync â may be changed, limited, or discontinued with reasonable notice. Operating-system changes may affect what the app can measure or clean on a given platform."}),(0,a.jsx)("h2",{children:"9. Disclaimer of warranties"}),(0,a.jsx)("p",{children:"To the fullest extent the law allows, SystemZen is provided âas isâ and âas availableâ, without warranties of any kind, express or implied, including merchantability, fitness for a particular purpose, and non-infringement. We do not warrant that the app will be error-free, that every measurement is exact, or that any specific amount of space will be reclaimed."}),(0,a.jsx)("h2",{children:"10. Limitation of liability"}),(0,a.jsx)("p",{children:"To the fullest extent the law allows, we are not liable for indirect, incidental, special, consequential, or punitive damages, or for lost data, lost profits, or lost goodwill. Our total liability for any claim relating to SystemZen is limited to the greater of the amount you paid us for it in the twelve months before the claim, or US$50. Nothing here excludes liability that cannot be excluded by law, and some jurisdictions do not allow these limits, in which case they apply only as far as permitted."}),(0,a.jsx)("h2",{children:"11. Termination"}),(0,a.jsx)("p",{children:"You may stop using SystemZen and delete it at any time. We may terminate this licence if you materially breach these terms. Sections 4, 9, 10, and 12 survive termination."}),(0,a.jsx)("h2",{children:"12. Governing law"}),(0,a.jsx)("p",{children:"These terms are governed by the laws of the State of California, USA, without regard to its conflict-of-laws rules, and the state and federal courts located in California have exclusive jurisdiction â except where your local consumer law gives you the right to bring proceedings where you live."}),(0,a.jsx)("h2",{children:"13. Apple, Google, and Microsoft"}),(0,a.jsx)("p",{children:"The platform store you installed from is not a party to these terms and has no obligation to provide support for SystemZen. Where a store's own end-user licence terms conflict with these, that store's terms prevail for that platform, and its operators are third-party beneficiaries entitled to enforce them."}),(0,a.jsx)("h2",{children:"14. Changes to these terms"}),(0,a.jsx)("p",{children:"We may update these terms; the date at the top shows the current version, and continuing to use the app after a material change means you accept it. If you do not, stop using the app and delete your account."}),(0,a.jsx)("h2",{children:"Contact"}),(0,a.jsxs)("p",{children:["Soft Flourish, California, USA â"," ",(0,a.jsx)("a",{href:`mailto:${n2}?subject=SystemZen%20terms`,children:n2})," or the"," ",(0,a.jsx)("a",{href:n1,children:"contact form"}),". See also"," ",(0,a.jsx)("a",{href:"/systemzen/support",children:"Support"}),"."]})]})}let ip=` 2426.sf-wrap{ 2427 --sf-bg:#f4f1ea;--sf-fg:#141820;--sf-muted:#5c6478; 2428 --sf-accent:#1a6b5c;--sf-accent-warm:#9c491d;
2428--sf-accent-soft:rgba(26,107,92,0.10); 2429 --sf-border:rgba(20,24,32,0.10);--sf-glass:rgba(255,255,255,0.52); 2430 --sf-glass-border:rgba(255,255,255,0.65);--sf-dark:#0e1219;--sf-radius:20px; 2431 --sf-ease:cubic-bezier(.4,0,.2,1); 2432 font-family:var(--font-inter),system-ui,sans-serif;background:var(--sf-bg);color:var(--sf-fg); 2433 -webkit-font-smoothing:antialiased;overflow-x:hidden; 2434} 2435.sf-wrap *,.sf-wrap *::before,.sf-wrap *::after{box-sizing:border-box} 2436.sf-display{font-family:var(--font-fraunces),Georgia,serif} 2437.sf-page{padding:120px clamp(20px,4vw,48px) clamp(64px,8vw,96px)} 2438.sf-page-inner{max-width:1180px;margin:0 auto} 2439.sf-page-narrow{max-width:760px;margin:0 auto} 2440.sf-prose p,.sf-prose li{font-size:.98rem;line-height:1.85;color:var(--sf-muted);margin-bottom:16px} 2441.sf-prose h2{font-family:var(--font-fraunces),Georgia,serif;font-size:1.35rem;font-weight:650;margin:32px 0 12px;color:var(--sf-fg)} 2442.sf-prose h3{font-size:1.08rem;font-weight:650;margin:22px 0 10px;color:var(--sf-fg)} 2443.sf-prose ul,.sf-prose ol{padding-left:1.25rem;margin-bottom:20px} 2444.sf-prose a,.sf-contact-info a{color:var(--sf-accent);text-decoration:underline;text-underline-offset:3px;font-weight:600} 2445.sf-prose a:hover{color:#0f4a40} 2446/* A CTA button inside prose: .sf-prose a (0,1,1) otherwise beats .sf-btn-primary 2447 (0,1,0) and repaints the button's label teal-and-underlined. */ 2448.sf-prose a.sf-btn{text-decoration:none} 2449.sf-prose a.sf-btn-primary,.sf-prose a.sf-btn-primary:hover{color:#fff} 2450.sf-prose a.sf-btn-accent,.sf-prose a.sf-btn-accent:hover{color:#fff} 2451 2452/* Blog / journal */ 2453.sf-blog-grid{display:grid;gap:20px;margin-top:8px} 2454@media(min-width:640px){.sf-blog-grid{grid-template-columns:repeat(2,1fr)}} 2455@media(min-width:960px){.sf-blog-grid{grid-template-columns:repeat(3,1fr)}} 2456.sf-blog-card{display:flex;flex-direction:column;padding:26px;border-radius:var(--sf-radius);text-decoration:none;color:inherit;transition:transform .25s var(--sf-ease)} 2457.sf-blog-card:hover{transform:translateY(-4px)} 2458.sf-blog-cat{font-size:.68rem;font-weight:650;letter-spacing:.12em;text-transform:uppercase;color:var(--sf-accent);margin-bottom:10px} 2459.sf-blog-card h3{font-size:1.05rem;font-weight:650;margin-bottom:8px;line-height:1.35;color:var(--sf-fg)} 2460.sf-blog-card p{font-size:.88rem;color:var(--sf-muted);line-height:1.6;margin:0 0 14px;flex:1} 2461.sf-blog-meta{font-size:.76rem;color:var(--sf-muted);margin-bottom:0} 2462.sf-article-header{max-width:760px;margin:0 auto 8px} 2463.sf-article-meta{font-size:.78rem;color:var(--sf-muted);margin-bottom:16px} 2464.sf-article-back{display:inline-block;margin-bottom:24px;font-size:.84rem;color:var(--sf-accent);text-decoration:none;font-weight:600} 2465.sf-article-back:hover{color:#0f4a40} 2466 2467/* Case studies (/work) */ 2468.sf-case-study{padding:clamp(24px,4vw,36px);border-radius:calc(var(--sf-radius) + 4px);margin-bottom:24px} 2469.sf-case-kicker{font-size:.68rem;font-weight:650;letter-spacing:.12em;text-transform:uppercase;color:var(--sf-accent);margin-bottom:10px} 2470.sf-case-study h3{font-family:var(--font-fraunces),Georgia,serif;font-size:1.5rem;font-weight:650;margin-bottom:6px} 2471.sf-case-tagline{font-size:.98rem;color:var(--sf-accent-warm);font-weight:600;margin-bottom:20px;line-height:1.5} 2472.sf-case-grid{display:grid;gap:24px;margin-top:8px} 2473@media(min-width:760px){.sf-case-grid{grid-template-columns:repeat(3,1fr)}} 2474.sf-case-col h4{font-size:.74rem;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--sf-fg);margin-bottom:8px} 2475.sf-case-col p{font-size:.88rem;color:var(--sf-muted);line-height:1.65;margin:0} 2476.sf-case-tags{display:flex;flex-wrap:wrap;gap:8px;margin-top:22px} 2477.sf-case-tag{font-size:.72rem;padding:4px 10px;border-radius:999px;background:var(--sf-accent-soft);color:var(--sf-accent);font-weight:600} 2478 2479.sf-glass{ 2480 background:var(--sf-glass);backdrop-filter:blur(22px) saturate(1.35); 2481 -webkit-backdrop-filter:blur(22px) saturate(1.35); 2482 border:1px solid var(--sf-glass-border); 2483 box-shadow:0 24px 64px -32px rgba(14,18,25,0.18),inset 0 1px 0 rgba(255,255,255,0.55); 2484} 2485 2486.sf-nav{ 2487 position:fixed;top:0;left:0;right:0;z-index:400;height:76px;padding:0 clamp(20px,4vw,48px); 2488 display:flex;align-items:center;justify-content:space-between; 2489 background:rgba(244,241,234,0.55);backdrop-filter:blur(20px) saturate(1.3); 2490 border-bottom:1px solid var(--sf-border); 2491} 2492.sf-nav-logo{display:flex;align-items:center;gap:12px;text-decoration:none;color:var(--sf-fg)} 2493.sf-nav-logo-mark{height:34px;width:auto;display:block} 2494.sf-nav-logo span{font-size:1.06rem;font-weight:650;letter-spacing:-.02em} 2495.sf-nav-links{display:flex;align-items:center;gap:2px} 2496.sf-nav-links a{ 2497 font-size:.84rem;font-weight:500;color:var(--sf-muted);text-decoration:none; 2498 padding:8px 14px;border-radius:999px;transition:color .2s,background .2s; 2499} 2500.sf-nav-links a:hover{color:var(--sf-fg);
2500background:rgba(26,107,92,0.08)} 2501.sf-nav-cta{ 2502 display:inline-flex;align-items:center;gap:8px;padding:10px 22px;border-radius:999px; 2503 background:var(--sf-fg);color:#fff;font-size:.82rem;font-weight:600;text-decoration:none; 2504 transition:transform .2s,box-shadow .2s; 2505} 2506.sf-nav-cta:hover{transform:translateY(-1px);box-shadow:0 8px 24px -8px rgba(14,18,25,0.35)} 2507 2508.sf-hero{ 2509 position:relative;min-height:96svh;display:flex;align-items:center; 2510 padding:120px clamp(20px,4vw,48px) 80px;overflow:hidden; 2511} 2512.sf-hero-bg{position:absolute;inset:0;z-index:0} 2513.sf-hero-bg img{width:100%;height:100%;object-fit:cover;opacity:0.38} 2514.sf-hero-bg::after{ 2515 content:'';position:absolute;inset:0; 2516 background:linear-gradient(135deg,rgba(244,241,234,0.92) 0%,rgba(244,241,234,0.55) 45%,rgba(244,241,234,0.82) 100%); 2517} 2518.sf-hero-inner{position:relative;z-index:2;max-width:1180px;margin:0 auto;width:100%} 2519.sf-hero-grid{display:grid;gap:40px;align-items:center} 2520@media(min-width:960px){.sf-hero-grid{grid-template-columns:1.15fr 0.85fr;gap:56px}} 2521.sf-hero-card{padding:clamp(32px,5vw,48px);border-radius:calc(var(--sf-radius) + 4px)} 2522.sf-hero-eyebrow{ 2523 font-size:.68rem;font-weight:650;letter-spacing:.22em;text-transform:uppercase; 2524 color:var(--sf-accent);margin-bottom:18px;display:flex;align-items:center;gap:10px; 2525} 2526.sf-hero-eyebrow::before{content:'';width:28px;height:1px;background:var(--sf-accent)} 2527.sf-hero-h1{ 2528 font-size:clamp(2.5rem,6.5vw,4.2rem);font-weight:700;letter-spacing:-.035em;line-height:1.02;margin-bottom:20px; 2529} 2530.sf-hero-h1 em{font-style:italic;color:var(--sf-accent-warm)} 2531.sf-hero-sub{font-size:clamp(1.02rem,2vw,1.18rem);color:var(--sf-muted);line-height:1.75;margin-bottom:28px;max-width:52ch} 2532.sf-hero-btns{display:flex;flex-wrap:wrap;gap:12px} 2533.sf-btn{ 2534 display:inline-flex;align-items:center;gap:8px;padding:14px 28px;border-radius:999px; 2535 font-size:.88rem;font-weight:600;text-decoration:none;border:none;cursor:pointer;transition:transform .2s,box-shadow .2s; 2536} 2537.sf-btn:hover{transform:translateY(-1px)} 2538.sf-btn-primary{background:var(--sf-fg);color:#fff;box-shadow:0 12px 32px -12px rgba(14,18,25,0.35)} 2539.sf-btn-outline{background:rgba(255,255,255,0.35);color:var(--sf-fg);border:1.5px solid var(--sf-border)} 2540.sf-btn-accent{background:var(--sf-accent);color:#fff} 2541 2542.sf-stats-strip{ 2543 display:grid;grid-template-columns:repeat(2,1fr);gap:1px;margin-top:8px; 2544 border-radius:var(--sf-radius);overflow:hidden;border:1px solid var(--sf-border); 2545} 2546@media(min-width:640px){.sf-stats-strip{grid-template-columns:repeat(4,1fr)}} 2547.sf-stat{padding:22px 20px;background:rgba(255,255,255,0.42);backdrop-filter:blur(12px)} 2548.sf-stat-val{font-family:var(--font-fraunces),Georgia,serif;font-size:1.65rem;font-weight:700;color:var(--sf-accent);line-height:1} 2549.sf-stat-label{font-size:.72rem;color:var(--sf-muted);margin-top:8px;line-height:1.45;letter-spacing:.02em} 2550 2551.sf-section{padding:clamp(72px,10vw,108px) clamp(20px,4vw,48px)} 2552.sf-section-muted{background:linear-gradient(180deg,#ece8df 0%,var(--sf-bg) 100%)} 2553.sf-section-dark{background:var(--sf-dark);color:rgba(240,242,245,.88)} 2554.sf-section-inner{max-width:1180px;margin:0 auto} 2555.sf-section-eyebrow{ 2556 font-size:.68rem;font-weight:650;letter-spacing:.2em;text-transform:uppercase; 2557 color:var(--sf-accent);margin-bottom:14px; 2558} 2559.sf-section-dark .sf-section-eyebrow{color:#6ecfb8} 2560.sf-section-h2{ 2561 font-size:clamp(1.9rem,4vw,2.75rem);font-weight:700;letter-spacing:-.025em;line-height:1.08;margin-bottom:16px; 2562} 2563.sf-section-lead{font-size:1.06rem;color:var(--sf-muted);max-width:640px;line-height:1.75} 2564.sf-section-dark .sf-section-lead{color:rgba(240,242,245,.62)} 2565 2566.sf-services-grid{display:grid;gap:20px;margin-top:48px} 2567@media(min-width:640px){.sf-services-grid{grid-template-columns:repeat(2,1fr)}} 2568@media(min-width:960px){.sf-services-grid{grid-template-columns:repeat(3,1fr)}} 2569.sf-service-card{padding:28px;border-radius:var(--sf-radius);transition:transform .25s var(--sf-ease)} 2570.sf-service-card:hover{transform:translateY(-4px)} 2571.sf-service-icon{font-size:1.4rem;margin-bottom:14px} 2572.sf-service-card h3{font-size:1.05rem;font-weight:650;margin-bottom:8px} 2573.sf-service-card p{font-size:.9rem;color:var(--sf-muted);line-height:1.65;margin:0} 2574 2575.sf-portfolio-wrap{margin-top:48px;position:relative} 2576.sf-portfolio-track{ 2577 display:flex;gap:20px;overflow-x:auto;scroll-snap-type:x mandatory; 2578 scrollbar-width:none;padding-bottom:8px; 2579} 2580.sf-portfolio-track::-webkit-scrollbar{display:none} 2581.sf-portfolio-card{ 2582 flex:0 0 min(340px,85vw);scroll-snap-align:start;border-radius:var(--sf-radius);overflow:hidden; 2583} 2584.sf-portfolio-card img{width:100%;aspect-ratio:4/3;object-fit:cover;display:block} 2585.sf-portfolio-body{padding:22px 24px} 2586.sf-portfolio-cat{font-size:.68rem;font-weight:650;letter-spacing:.12em;text-transform:uppercase;color:var(--sf-accent);margin-bottom:8px} 2587.sf-portfolio-card h3{font-size:1.08rem;font-weight:650;margin-bottom:6px} 2588.sf-portfolio-card p{font-size:.88rem;color:var(--sf-muted);line-height:1.55;margin:0}
2589.sf-carousel-btns{display:flex;gap:8px;margin-top:20px} 2590.sf-carousel-btn{ 2591 width:44px;height:44px;border-radius:50%;border:1px solid var(--sf-border); 2592 background:rgba(255,255,255,0.5);cursor:pointer;font-size:1.1rem; 2593 display:flex;align-items:center;justify-content:center;transition:background .2s; 2594} 2595.sf-carousel-btn:hover{background:#fff} 2596 2597.sf-split{display:grid;gap:48px;align-items:center;margin-top:48px} 2598@media(min-width:900px){.sf-split{grid-template-columns:1fr 1fr}} 2599.sf-split img{width:100%;border-radius:var(--sf-radius);object-fit:cover;aspect-ratio:16/10} 2600 2601.sf-cta-band{ 2602 position:relative;padding:clamp(72px,10vw,100px) clamp(20px,4vw,48px);overflow:hidden; 2603} 2604.sf-cta-band-inner{max-width:1180px;margin:0 auto;display:grid;gap:48px;align-items:start} 2605@media(min-width:900px){.sf-cta-band-inner{grid-template-columns:1fr 1.1fr}} 2606.sf-cta-form{padding:36px;border-radius:calc(var(--sf-radius) + 4px)} 2607 2608.sf-contact-grid{display:grid;gap:28px;margin-top:40px} 2609@media(min-width:960px){.sf-contact-grid{grid-template-columns:.85fr 1.15fr;align-items:start;gap:40px}} 2610.sf-contact-info{display:flex;flex-direction:column;gap:16px} 2611.sf-contact-card{padding:26px;border-radius:var(--sf-radius)} 2612/* Bare emoji rendered small and inconsistently against the card; the tinted 2613 badge gives all four the same optical weight. */ 2614.sf-contact-card-icon{ 2615 display:grid;place-content:center;width:42px;height:42px;border-radius:14px; 2616 background:var(--sf-accent-soft);font-size:1.15rem;line-height:1;margin-bottom:14px; 2617} 2618.sf-contact-info h3{font-size:1.02rem;font-weight:650;margin:0 0 8px;color:var(--sf-fg)} 2619.sf-contact-info p{font-size:.89rem;color:var(--sf-muted);line-height:1.7;margin:0} 2620.sf-contact-steps{list-style:none;padding:0;margin:0;counter-reset:sfstep} 2621.sf-contact-steps li{position:relative;padding-left:34px;margin-bottom:11px;font-size:.86rem;color:var(--sf-muted);line-height:1.6} 2622.sf-contact-steps li:last-child{margin-bottom:0} 2623.sf-contact-steps li::before{ 2624 counter-increment:sfstep;content:counter(sfstep);position:absolute;left:0;top:0; 2625 width:23px;height:23px;border-radius:50%;background:var(--sf-accent-soft);color:var(--sf-accent); 2626 font-size:.72rem;font-weight:700;display:grid;place-content:center; 2627} 2628/* Scoped past '.sf-contact-info p' (0,1,1), which would otherwise win. */ 2629.sf-contact-info .sf-contact-policies{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:.84rem} 2630 2631/* ââ Contact form âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 2632 None of these rules existed. .sf-contact-form / .sf-field / .sf-honeypot / 2633 .sf-btn-full / .sf-form-status were class names with no stylesheet behind 2634 them, so the form rendered as raw browser-default inputs in a single column 2635 â and the honeypot, which is only a trap if nobody can see it, was showing 2636 every visitor a "Leave blank" text box. */ 2637.sf-form-card{padding:clamp(24px,3.5vw,38px);border-radius:calc(var(--sf-radius) + 4px)} 2638.sf-form-title{font-family:var(--font-fraunces),Georgia,serif;font-size:1.4rem;font-weight:650;margin:0 0 6px} 2639.sf-form-sub{font-size:.88rem;color:var(--sf-muted);line-height:1.6;margin:0 0 26px} 2640.sf-contact-form{display:grid;grid-template-columns:1fr;gap:18px} 2641@media(min-width:560px){.sf-contact-form{grid-template-columns:1fr 1fr}} 2642.sf-field{display:flex;flex-direction:column;gap:7px;min-width:0} 2643.sf-field-full{grid-column:1/-1} 2644.sf-field>span{font-size:.72rem;font-weight:700;letter-spacing:.07em;text-transform:uppercase;color:var(--sf-fg);opacity:.7} 2645.sf-field input,.sf-field textarea{ 2646 width:100%;font:inherit;font-size:.92rem;color:var(--sf-fg); 2647 background:rgba(255,255,255,.72);border:1.5px solid var(--sf-border);border-radius:12px;padding:12px 14px; 2648 transition:border-color .18s var(--sf-ease),box-shadow .18s var(--sf-ease),background .18s var(--sf-ease); 2649} 2650.sf-field textarea{resize:vertical;min-height:124px;line-height:1.65} 2651.sf-field input::placeholder,.sf-field textarea::placeholder{color:var(--sf-muted);opacity:.62} 2652.sf-field input:hover,.sf-field textarea:hover{border-color:rgba(20,24,32,.2)} 2653.sf-field input:focus,.sf-field textarea:focus{ 2654 outline:none;background:#fff;border-color:var(--sf-accent);box-shadow:0 0 0 4px var(--sf-accent-soft); 2655} 2656 2657/* Checkbox groups: one 'looking_for' name, the label text is the value. */ 2658.sf-choice-set{grid-column:1/-1;min-width:0;border:0;margin:0;padding:0} 2659.sf-choice-set legend{ 2660 padding:0;margin-bottom:8px;font-size:.72rem;font-weight:700;letter-spacing:.07em; 2661 text-transform:uppercase;color:var(--sf-fg);opacity:.7; 2662} 2663.sf-choice-hint{font-size:.82rem;color:var(--sf-muted);line-height:1.55;margin:0 0 12px} 2664.sf-choice-grid{display:flex;flex-wrap:wrap;gap:9px} 2665.sf-choice{ 2666 display:inline-flex;align-items:center;gap:9px;
2666cursor:pointer;padding:9px 15px;border-radius:999px; 2667 font-size:.85rem;font-weight:600;color:var(--sf-fg);background:rgba(255,255,255,.55); 2668 border:1.5px solid var(--sf-border); 2669 transition:background .18s var(--sf-ease),border-color .18s var(--sf-ease),color .18s var(--sf-ease); 2670} 2671.sf-choice:hover{background:#fff;border-color:rgba(20,24,32,.22)} 2672.sf-choice input{ 2673 appearance:none;-webkit-appearance:none;margin:0;flex:none;width:16px;height:16px; 2674 border:1.5px solid rgba(20,24,32,.28);border-radius:5px;background:#fff;display:grid;place-content:center; 2675 transition:background .18s var(--sf-ease),border-color .18s var(--sf-ease); 2676} 2677.sf-choice input::after{ 2678 content:"";width:9px;height:9px;background:#fff;transform:scale(0);transition:transform .15s var(--sf-ease); 2679 clip-path:polygon(14% 44%,0 60%,39% 100%,100% 18%,84% 3%,39% 69%); 2680} 2681.sf-choice input:checked{background:var(--sf-accent);border-color:var(--sf-accent)} 2682.sf-choice input:checked::after{transform:scale(1)} 2683.sf-choice input:focus-visible{outline:2px solid var(--sf-accent);outline-offset:3px} 2684.sf-choice:has(input:checked){background:var(--sf-accent-soft);border-color:var(--sf-accent);color:var(--sf-accent)} 2685 2686/* aria-hidden + tabIndex={-1} on the markup keep assistive tech out of this. */ 2687.sf-honeypot{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0} 2688.sf-contact-form .cf-turnstile{grid-column:1/-1} 2689.sf-btn-full{grid-column:1/-1;width:100%;justify-content:center;padding:15px 28px} 2690.sf-btn-full:disabled{opacity:.6;cursor:progress;transform:none} 2691.sf-form-note{grid-column:1/-1;font-size:.78rem;color:var(--sf-muted);line-height:1.6;margin:-6px 0 0;text-align:center} 2692.sf-form-status{ 2693 grid-column:1/-1;margin:0;padding:13px 16px;border-radius:12px;border:1.5px solid transparent; 2694 font-size:.88rem;font-weight:600;line-height:1.6; 2695} 2696.sf-form-status-success{background:var(--sf-accent-soft);border-color:rgba(26,107,92,.28);color:#12564a} 2697.sf-form-status-error{background:rgba(196,92,38,.1);border-color:rgba(196,92,38,.3);color:#8f3d13} 2698 2699.sf-footer{background:var(--sf-dark);color:rgba(240,242,245,.72);padding:56px clamp(20px,4vw,48px) 32px} 2700.sf-footer-inner{max-width:1180px;margin:0 auto;display:grid;gap:32px} 2701@media(min-width:768px){.sf-footer-inner{grid-template-columns:1.5fr 1fr 1fr}} 2702.sf-footer-brand span{font-weight:650;color:#fff;font-size:1.05rem} 2703.sf-footer-links{display:flex;flex-direction:column;gap:8px} 2704.sf-footer-links a{color:rgba(240,242,245,.65);text-decoration:none;font-size:.88rem} 2705.sf-footer-links a:hover{color:#fff} 2706.sf-footer-bottom{ 2707 max-width:1180px;margin:32px auto 0;padding-top:24px;border-top:1px solid rgba(255,255,255,.08); 2708 font-size:.78rem;text-align:center;color:rgba(240,242,245,.55); 2709} 2710@media(max-width:768px){.sf-nav-links{display:none}} 2711`,ih=new Set(["/privacy","/privacy-policy","/terms","/terms-of-service","/support"]);function im({eyebrow:e,title:t,lead:s}){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("p",{className:"sf-section-eyebrow",children:e}),(0,a.jsx)("h1",{className:"sf-section-h2 sf-display",children:t}),(0,a.jsx)("p",{className:"sf-section-lead",style:{marginBottom:40},children:s})]})}function iu(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(s.SkipToContent,{}),(0,a.jsxs)("nav",{className:"sf-nav","aria-label":"Main navigation",children:[(0,a.jsxs)("a",{href:"/",className:"sf-nav-logo","aria-label":"Soft Flourish home",children:[(0,a.jsx)("img",{src:"/softflourish/logo.png",alt:"",className:"sf-nav-logo-mark"}),(0,a.jsx)("span",{className:"sf-display",children:"Soft Flourish"})]}),(0,a.jsxs)("div",{className:"sf-nav-links",children:[(0,a.jsx)("a",{href:"/services",children:"Services"}),(0,a.jsx)("a",{href:"/work",children:"Work"}),(0,a.jsx)("a",{href:"/websites",children:"Websites"}),(0,a.jsx)("a",{href:"/blog",children:"Blog"}),(0,a.jsx)("a",{href:"/about",children:"About"}),(0,a.jsx)("a",{href:"/support",children:"Support"})]}),(0,a.jsx)("a",{href:"/contact",className:"sf-nav-cta",children:"Get in touch"})]}),(0,a.jsx)("div",{id:"main-content",tabIndex:-1})]})}function ig(){return(0,a.jsxs)("footer",{className:"sf-footer",children:[(0,a.jsxs)("div",{className:"sf-footer-inner",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("div",{className:"sf-footer-brand",style:{marginBottom:12},children:(0,a.jsx)("span",{className:"sf-display",children:"Soft Flourish"})}),(0,a.jsx)("p",{style:{fontSize:".88rem",lineHeight:1.65,maxWidth:320},children:"Independent software studio and publishing house. We build and maintain apps for iPhone, iPad, Android, Mac, Windows, Apple TV, and the web â and we publish books."})]}),(0,a.jsxs)("div",{className:"sf-footer-links",children:[(0,a.jsx)("strong",{style:{color:"#fff",fontSize:".82rem",marginBottom:4},children:"Studio"}),(0,a.jsx)("a",{href:"/services",children:"Services"}),(0,a.jsx)("a",{href:"/work",children:"Work"}),(0,a.jsx)("a",{href:"/websites",children:"Websites"}),(0,a.jsx)("a",{href:"/blog",children:"Blog"}),(0,a.jsx)("a",{href:"/about",children:"About"}),(0,a.jsx)("a",{href:"/contact",children:"Contact"})]}),(0,a.jsxs)("div",{className:"sf-footer-links",children:[(0,a.jsx)("strong",{style:{color:"#fff",fontSize:".82rem",marginBottom:4},children:"Policies"}),(0,a.jsx)("a",{href:"/contact",children:"Contact"}),(0,a.jsx)("a",{href:"/support",children:"Support"}),(0,a.jsx)("a",{href:"/privacy",children:"Privacy Policy"}),(0,a.jsx)("a",{href:"/terms",children:"Terms of Use"})]})]}),(0,a.jsxs)("p",{className:"sf-footer-bottom",children:["© ",new Date().getFullYear()," Soft Flourish. All rights reserved."]})]})}function ix(){let e=(0,r.useRef)(null),t=(0,r.useCallback)(a=>{e.current?.scrollBy({left:360*a,behavior:"smooth"})},[]);return(0,a.jsxs)("div",{className:"sf-portfolio-wrap",children:[(0,a.jsx)("div",{ref:e,className:"sf-portfolio-track",tabIndex:0,role:"region","aria-label":"Portfolio projects",children:c.map(e=>(0,a.jsxs)("article",{className:"sf-portfolio-card sf-glass",children:[(0,a.jsx)("img",{src:e.image,alt:"",loading:"lazy",decoding:"async"}),(0,a.jsxs)("div",{className:"sf-portfolio-body",children:[(0,a.jsx)("p",{className:"sf-portfolio-cat",children:e.category}),(0,a.jsx)("h3",{children:e.title}),(0,a.jsx)("p",{children:e.tagline})]})]},e.title))}
2711),(0,a.jsxs)("div",{className:"sf-carousel-btns",children:[(0,a.jsx)("button",{type:"button",className:"sf-carousel-btn","aria-label":"Previous project",onClick:()=>t(-1),children:"â"}),(0,a.jsx)("button",{type:"button",className:"sf-carousel-btn","aria-label":"Next project",onClick:()=>t(1),children:"â"})]})]})}function ib(){return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("section",{className:"sf-hero",children:[(0,a.jsx)("div",{className:"sf-hero-bg","aria-hidden":!0,children:(0,a.jsx)("img",{src:i.backgroundOffice,alt:"",fetchPriority:"high"})}),(0,a.jsx)(t.FluidCanvas,{colorA:"#1a6b5c",colorB:"#f4f1ea",intensity:.22,className:"absolute inset-0 z-[1] mix-blend-multiply opacity-70 pointer-events-none"}),(0,a.jsx)("div",{className:"sf-hero-inner",children:(0,a.jsxs)("div",{className:"sf-hero-grid",children:[(0,a.jsxs)("div",{className:"sf-hero-card sf-glass",children:[(0,a.jsx)("p",{className:"sf-hero-eyebrow sf-display",children:"App & web development studio"}),(0,a.jsxs)("h1",{className:"sf-hero-h1 sf-display",children:["Craftsmen of the ",(0,a.jsx)("em",{children:"digital"})," world"]}),(0,a.jsx)("p",{className:"sf-hero-sub",children:"An independent studio and publishing house. We design, engineer, publish, and maintain apps on iPhone, iPad, Android, Mac, Windows, Apple TV, and the web â and we publish books in print and digital. Bring us your idea and we'll do the same for you."}),(0,a.jsxs)("div",{className:"sf-hero-btns",children:[(0,a.jsx)("a",{href:"/contact",className:"sf-btn sf-btn-primary",children:"Get in touch"}),(0,a.jsx)("a",{href:"/work",className:"sf-btn sf-btn-outline",children:"View our work"})]})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("img",{src:i.heroStudio,alt:"Soft Flourish product studio",className:"sf-glass",style:{width:"100%",borderRadius:"var(--sf-radius)",aspectRatio:"16/10",objectFit:"cover"},fetchPriority:"high"}),(0,a.jsx)("div",{className:"sf-stats-strip sf-glass",style:{marginTop:16},children:o.map(e=>(0,a.jsxs)("div",{className:"sf-stat",children:[(0,a.jsx)("p",{className:"sf-stat-val",children:e.value}),(0,a.jsx)("p",{className:"sf-stat-label",children:e.label})]},e.label))})]})]})})]}),(0,a.jsx)("section",{className:"sf-section",children:(0,a.jsxs)("div",{className:"sf-section-inner",children:[(0,a.jsx)("p",{className:"sf-section-eyebrow",children:"Capabilities"}),(0,a.jsx)("h2",{className:"sf-section-h2 sf-display",children:"We build, ship, and keep supporting what we make"}),(0,a.jsx)("p",{className:"sf-section-lead",children:"From greenfield products to rescue missions â native apps for every major platform, internal tools, customer portals, store-ready releases, and books taken from manuscript to print. If you have an idea, we can build it or publish it."}),(0,a.jsx)("div",{className:"sf-services-grid",children:l.map(e=>(0,a.jsxs)("article",{className:"sf-service-card sf-glass",children:[(0,a.jsx)("p",{className:"sf-service-icon","aria-hidden":!0,children:e.icon}),(0,a.jsx)("h3",{children:e.title}),(0,a.jsx)("p",{children:e.description})]},e.title))})]})}),(0,a.jsx)("section",{className:"sf-section sf-section-muted",children:(0,a.jsx)("div",{className:"sf-section-inner",children:(0,a.jsxs)("div",{className:"sf-split",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"sf-section-eyebrow",children:"Platforms"}),(0,a.jsx)("h2",{className:"sf-section-h2 sf-display",children:"One team across every surface"}),(0,a.jsx)("p",{className:"sf-section-lead",children:"Apple platforms, Android, and modern web â unified backend, consistent design language, and publishing workflows that satisfy store review and enterprise IT."}),(0,a.jsx)("a",{href:"/services",className:"sf-btn sf-btn-accent",style:{marginTop:24},children:"Explore services â"})]}),(0,a.jsx)("img",{src:i.infographicPlatforms,alt:"Platform capabilities overview",className:"sf-glass",loading:"lazy"})]})})}),(0,a.jsx)("section",{className:"sf-section",id:"work",children:(0,a.jsxs)("div",{className:"sf-section-inner",children:[(0,a.jsx)("p",{className:"sf-section-eyebrow",children:"Selected work"}),(0,a.jsx)("h2",{className:"sf-section-h2 sf-display",children:"Products we've shipped for business"}),(0,a.jsx)("p",{className:"sf-section-lead",children:"Representative engagements across mobile, web, and launch programs. Client names with
2711held â ask us for references in your industry."}),(0,a.jsx)(ix,{})]})}),(0,a.jsx)("section",{className:"sf-section sf-section-dark",children:(0,a.jsxs)("div",{className:"sf-section-inner",style:{textAlign:"center"},children:[(0,a.jsx)("p",{className:"sf-section-eyebrow",children:"Trusted delivery"}),(0,a.jsx)("h2",{className:"sf-section-h2 sf-display",children:"Built for teams that re-engage"}),(0,a.jsx)("p",{className:"sf-section-lead",style:{margin:"0 auto 32px"},children:"We sign our name next to every release. Store submissions, production incidents, and OS migrations â the same engineers who shipped v1 handle what comes next."}),(0,a.jsx)("a",{href:"/contact",className:"sf-btn sf-btn-accent",children:"Start a conversation"})]})}),(0,a.jsxs)("section",{className:"sf-cta-band",id:"contact",children:[(0,a.jsx)(t.FluidCanvas,{colorA:"#c45c26",colorB:"#0e1219",intensity:.15,className:"absolute inset-0 pointer-events-none opacity-40"}),(0,a.jsxs)("div",{className:"sf-cta-band-inner",style:{position:"relative",zIndex:1},children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"sf-section-eyebrow",children:"Contact"}),(0,a.jsx)("h2",{className:"sf-section-h2 sf-display",children:"Let's get in touch"}),(0,a.jsx)("p",{className:"sf-section-lead",children:"An app you want built, a book you want published, or an idea you haven't scoped yet â tell us what it is. A paragraph is plenty. We respond within one business day."})]}),(0,a.jsx)("div",{className:"sf-cta-form sf-glass",children:(0,a.jsx)(m,{pageSlug:"/",pageTitle:"Home"})})]})]})]})}e.s(["SoftFlourishPage",0,function({slug:e="/"}){let t;if("/manualwalls"===e)return(0,a.jsx)(e_,{});if("/art"===e)return(0,a.jsx)(eX,{});if("/ownerportal/businessmedia"===e)return(0,a.jsx)(aa,{});if("/adperformance"===e)return(0,a.jsx)(aj,{});if("/ownerportal"===e)return(0,a.jsx)(aq,{});if("/emailmarketing"===e)return(0,a.jsx)(a7,{});if("/leads"===e)return(0,a.jsx)(tJ,{});if("/profilemap"===e)return(0,a.jsx)(t4,{});if("/genaicost"===e)return(0,a.jsx)(sg,{});if("/flourishpdf"===e)return(0,a.jsx)(s9,{});if("/flourishpdf/privacy"===e)return(0,a.jsx)(sI,{});if("/flourishpdf/terms"===e)return(0,a.jsx)(sE,{});if("/flourishpdf/support"===e)return(0,a.jsx)(sW,{});if("/flourishpdf/account"===e)return(0,a.jsx)(ex,{});if("/flourishpdf/deleteaccount"===e)return(0,a.jsx)(sD,{});if("/flourishpdf/manual"===e)return(0,a.jsx)(sO,{});{let t=(0,sS.matchFlourishPdfSlug)(e);if(t?.kind==="pricing")return(0,a.jsx)(re,{});if(t?.kind==="features")return(0,a.jsx)(ra,{});if(t?.kind==="download")return(0,a.jsx)(rn,{});if(t?.kind==="faq")return(0,a.jsx)(ri,{});if(t?.kind==="area")return(0,a.jsx)(rs,{area:t.area});if(t?.kind==="platform")return(0,a.jsx)(rr,{platform:t.platform})}if(M(e))return(0,a.jsx)(L,{slug:e});if("/djflow/account"===e)return(0,a.jsx)(nV,{});if("/systemzen"===e)return(0,a.jsx)(is,{});if("/systemzen/privacy"===e)return(0,a.jsx)(ii,{});if("/systemzen/terms"===e)return(0,a.jsx)(id,{});if("/systemzen/support"===e)return(0,a.jsx)(il,{});if("/systemzen/deleteaccount"===e)return(0,a.jsx)(io,{});if("/systemzen/manual"===e)return(0,a.jsx)(ic,{});if("/systemzen/advanced"===e)return(0,a.jsx)(ir,{});if("/systemzen/account"===e)return(0,a.jsx)(nC,{});if("/messageexporter"===e)return(0,a.jsx)(rb,{});if("/messageexporter/guide"===e||"/messageexporter/manual"===e)return(0,a.jsx)(ry,{});if("/messageexporter/support"===e)return(0,a.jsx)(rv,{});if("/messageexporter/privacy"===e)return(0,a.jsx)(rw,{});if("/messageexporter/terms"===e)return(0,a.jsx)(rj,{});if("/magicrename"===e)return(0,a.jsx)(nj,{});if("/magicrename/support"===e)return(0,a.jsx)(nk,{});if("/magicrename/privacy"===e)return(0,a.jsx)(nS,{});if("/magicrename/terms"===e)return(0,a.jsx)(nA,{});if("/magicrename/manual"===e)return(0,a.jsx)(nN,{});if("/magicrename/account"===e)return(0,a.jsx)(nl,{});if("/meetwing"===e)return(0,a.jsx)(rU,{});if("/meetwing/account"===e)return(0,a.jsx)(rA,{});if("/meetwing/privacy"===e||"/meetwing/privacypolicy"===e||"/meetwing/privacy-policy"===e)return(0,a.jsx)(rB,{});if("/meetwing/terms"===e||"/meetwing/terms-of-use"===e||"/meetwing/term-consition"===e)return(0,a.jsx)(rG,{});if("/meetwing/support"===e||"/meetwing/help"===e)return(0,a.jsx)(rL,{});if("/meetwing/manual"===e)return(0,a.jsx)(rq,{});let s=(t=e.replace(/\/+$/,"")||"/",rX.find(e=>`/${e.slug}`===t)??null);if(s)return(0,a.jsx)(r1,{config:s});
2711let r=function(e){let a=e.split("/").filter(Boolean);if(2!==a.length)return null;let[t,s]=a,r=rH.find(e=>e.slug===t);return r?rY.includes(s)?{app:r,page:"privacy"}:rV.includes(s)?{app:r,page:"terms"}:rK.includes(s)?{app:r,page:"deleteaccount"}:rQ.includes(s)?{app:r,page:"support"}:rJ.includes(s)?{app:r,page:"manual"}:null:null}(e),n=ih.has(e)?"/privacy"===e||"/privacy-policy"===e?"/privacy":"/terms"===e||"/terms-of-service"===e?"/terms":"/support"===e?"/support":null:null;return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:ip}}),(0,a.jsxs)("div",{className:"sf-wrap",children:[(0,a.jsx)(iu,{}),"/surfaceiq"===e?(0,a.jsx)(r3,{}):"/surfaceiq/privacypolicy"===e?(0,a.jsx)(r7,{}):"/surfaceiq/support"===e?(0,a.jsx)(r9,{}):"/surfaceiq/terms"===e?(0,a.jsx)(na,{}):"/surfaceiq/deleteaccount"===e?(0,a.jsx)(nt,{}):"/surfaceiq/manual"===e?(0,a.jsx)(ne,{}):r?.page==="privacy"?(0,a.jsx)(r2,{app:r.app}):r?.page==="terms"?(0,a.jsx)(r8,{app:r.app}):r?.page==="support"?(0,a.jsx)(r6,{app:r.app}):r?.page==="deleteaccount"?(0,a.jsx)(r4,{app:r.app}):r?.page==="manual"?(0,a.jsx)(r5,{app:r.app}):"/privacy"===n?(0,a.jsx)(rD,{}):"/terms"===n?(0,a.jsx)(rW,{}):"/support"===n?(0,a.jsx)(rO,{}):"/"===e?(0,a.jsx)(ib,{}):"/work"===e?(0,a.jsx)("section",{className:"sf-page",children:(0,a.jsx)("div",{className:"sf-page-inner",children:(0,a.jsx)(v,{})})}):"/books"===e?(0,a.jsx)("section",{className:"sf-page",children:(0,a.jsx)("div",{className:"sf-page-inner",children:(0,a.jsx)(S,{})})}):"/apps"===e?(0,a.jsx)("section",{className:"sf-page",children:(0,a.jsx)("div",{className:"sf-page-inner",children:(0,a.jsx)(X,{})})}):"/websites"===e?(0,a.jsx)("section",{className:"sf-page",children:(0,a.jsx)("div",{className:"sf-page-inner",children:(0,a.jsx)(en,{})})}):"/blog"===e?(0,a.jsx)(x,{}):e.startsWith("/blog/")?(0,a.jsx)(b,{slug:e.replace("/blog/","")}):"/about"===e?(0,a.jsx)("section",{className:"sf-page",children:(0,a.jsxs)("div",{className:"sf-page-narrow sf-prose",children:[(0,a.jsx)(im,{eyebrow:"About",title:"We build things, and we publish them",lead:"Soft Flourish is an independent software studio and publishing house. We design, ship, and maintain our own apps across iPhone, iPad, Android, Mac, Windows, Apple TV, and the web â and we publish books in print and digital. When someone brings us an idea, we do the same thing for them."}),(0,a.jsx)("p",{children:"Most studios hand a product over at launch and move on. We never got to. Every app on this page is one we publish under our own name, which means we are also the ones who answer the support email, rewrite the paywall when a store changes its rules, and rebuild the thing each autumn when a new OS lands. That is the whole difference in how we work, and it is not a philosophy we adopted â it is just what owning your own catalogue does to you."}),(0,a.jsx)("h2",{children:"What we publish and maintain"}),(0,a.jsx)("p",{children:"These are live products, not case-study illustrations. You can install them today from the App Store, Google Play, or the Microsoft Store, and every one of them is ours end to end: the code, the backend, the billing, the store listing, and the privacy and support pages you are reading right now."}),(0,a.jsxs)("ul",{children:[(0,a.jsxs)("li",{children:[(0,a.jsx)("a",{href:"/meetwing",children:"MeetWing"})," â a real-time AI meeting assistant for"," ",(0,a.jsx)("strong",{children:"Mac and Windows"}),". Listens live, suggests in the moment, stores no audio."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("a",{href:"/flourishpdf",children:"Flourish PDF"})," â a full PDF editor with on-device OCR and AI, on ",(0,a.jsx)("strong",{children:"Mac, Windows, iPhone, iPad, and Android"}),". No document ever leaves the device."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("a",{href:"/messageexporter",children:"Message Exporter"})," â saves Mac Messages conversations as PDF, Excel, CSV, HTML, or text. ",(0,a.jsx)("strong",{children:"macOS."})]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("a",{href:"/systemzen/support",children:"SystemZen"})," â device cleanup and storage insight across ",(0,a.jsx)("strong",{children:"Mac, Windows, iPhone, iPad, and Android"}),"."]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("a",{href:"/surfaceiq/support",children:"SurfaceIQ"})," â camera-and-depth surface scanning that flags cracks, stains, dents, and wear, with PDF reports. ",(0,a.jsx)("strong",{children:"iPhone and Android."})]}),(0,a.jsxs)("li",{children:[(0,a.jsx)("a",{href:"/football-pulse",children:"Football Insight Hub"}),", Cricket Pulse, and Sport Orbit â three separate live-sports apps, each with its own scores, tables, and fixtures, on ",(0,a.jsx)("strong",{children:"iPhone, iPad, Mac, Apple TV, and Android"}),"."]}),(0,a.jsx)("li",{children:"Web platforms we run ourselves â structured news analysis at scale, and explainable product rankings."})]}),(0,a.jsxs)("p",{children:["Longer write-ups of how each was scoped and built are on our ",(0,a.jsx)("a",{href:"/work",children:"work page"}),"."]}),(0,a.jsx)("h2",{children:"We are also a book publisher"}),(0,a.jsxs)("p",{children:["The publishing side is a real operation, not a sideline: manuscript through editing, typesetting, cover design, ISBN assignment, and distribution into print and digital retail. We run our own typesetting and cover pipeline, which is why a Soft Flourish book comes out in ",(0,a.jsx)("strong",{children:"paperback, hardcover, and Kindle"})," rather than whichever single format was cheapest to produce."]}),(0,a.jsxs)("p",{children:["Our flagship title is ",(0,a.jsx)("em",{children:"Architecting Agentic AI"}),", a seventeen-volume technical reference published in all three editions. Behind it is a growing catalogue across fiction and illustrated non-fiction. The same standard applies as on the softw
2711are side â we read every page of every book we put our name on before it ships."]}),(0,a.jsx)("h2",{children:"Have an idea? We will build it or publish it"}),(0,a.jsx)("p",{children:"Everything above started as a sentence someone said out loud. If you have one of those, bring it to us â there is no minimum size, and you do not need a spec, a design, or a manuscript that is finished."}),(0,a.jsx)("h3",{children:"If it is an app"}),(0,a.jsx)("p",{children:"We take it from idea to scope to design to build to store submission, on any surface you need: iPhone, iPad, Android, Mac, Windows, Apple TV, or the web. Then we stay on for the part most people forget â OS updates, store policy changes, crash triage, and the next version. If you already have a stalled build from another team, that is a normal way for us to start too."}),(0,a.jsx)("h3",{children:"If it is a book"}),(0,a.jsx)("p",{children:"We take a manuscript through editing, typesetting, cover, ISBN, and into print and ebook retail. You keep your rights and your royalties; we handle the machinery. If the manuscript is still half-written, tell us anyway â knowing the format early changes how it should be written."}),(0,a.jsxs)("p",{children:["Both start the same way: ",(0,a.jsx)("a",{href:"/contact",children:"the contact form"}),". A paragraph is plenty. We read every message ourselves and reply within one business day."]}),(0,a.jsx)("p",{style:{marginTop:28},children:(0,a.jsx)("a",{href:"/contact",className:"sf-btn sf-btn-primary",children:"Tell us your idea"})}),(0,a.jsx)("h2",{children:"How we work"}),(0,a.jsxs)("ul",{children:[(0,a.jsx)("li",{children:"A small senior team. The people who scope your project are the people who build it and the people who are still there a year later."}),(0,a.jsx)("li",{children:"Shipped in vertical slices â a complete, testable feature at a time â so there is always something real to react to instead of a status update."}),(0,a.jsx)("li",{children:"Deep Apple, Google, and Microsoft publishing experience, including the privacy, data-safety, and support-URL compliance that quietly sinks first submissions."}),(0,a.jsx)("li",{children:"Modern stack: Swift/SwiftUI, Kotlin, WinUI, Next.js, Firebase, and Google Cloud."}),(0,a.jsx)("li",{children:"Transparent milestones and direct access to the people doing the work."})]}),(0,a.jsxs)("p",{children:["For new business, support, or privacy inquiries, use our"," ",(0,a.jsx)("a",{href:"/contact",children:"contact form"}),". Our ",(0,a.jsx)("a",{href:"/blog",children:"journal"})," covers how we think about App Store review, platform choice, pricing, and scoping."]})]})}):"/services"===e?(0,a.jsx)("section",{className:"sf-page",children:(0,a.jsxs)("div",{className:"sf-page-inner",children:[(0,a.jsx)(im,{eyebrow:"Services",title:"What we build â and what we publish",lead:"End-to-end product development across every major platform, plus a full book-publishing pipeline. Not template sites, not offshore handoffs."}),(0,a.jsx)("div",{className:"sf-services-grid",style:{marginTop:0},children:l.map(e=>(0,a.jsxs)("article",{className:"sf-service-card sf-glass",children:[(0,a.jsx)("p",{className:"sf-service-icon","aria-hidden":!0,children:e.icon}),(0,a.jsx)("h3",{children:e.title}),(0,a.jsx)("p",{children:e.description})]},e.title))}),(0,a.jsxs)("div",{className:"sf-page-narrow sf-prose",style:{marginTop:64,marginLeft:0},children:[(0,a.jsx)("h2",{children:"How an engagement typically runs"}),(0,a.jsx)("p",{children:"We start most projects with a short, paid discovery phase â scoping the MVP, resolving the data model, and settling design direction â before committing to a full build timeline. From there, engineering ships in vertical slices (a complete, testable feature at a time) rather than all-backend-then-all-frontend, so there is always a working build to react to. An internal beta and, where it fits, a TestFlight or Play Console closed test round happen before store submission, with review-cycle buffer built into the timeline rather than assumed away."}),(0,a.jsx)("h2",{children:"Publishing a book with us"}),(0,a.jsx)("p",{children:"The book side runs on the same shape of process. We read the manuscript first and say honestly whether it is ready; from there it is editing, typesetting, cover design, ISBN assignment, and distribution into paperback, hardcover, and ebook retail. We produce all three formats rather than the one that was cheapest, because a book that only exists as an ebook quietly gives up most of its readers. You keep your rights and your royalties. If your manuscript is half-finished, talk to us anyway â the format decisions are much cheaper to make before the writing is done than after."}),(0,a.jsx)("h2",{children:"After launch"}),(0,a.jsxs)("p",{children:["Shipping v1 is not the end of the relationship for most clients. OS updates, store policy changes, crash triage, and iteration based on real usage data are ongoing work, and we prefer to stay on as the team that keeps a product healthy rather than handing off to whoever picks up the ticket next. Read more on how we approach specific parts of this work â App Store review, subscription and paywall design, backend architecture, and post-launch analytics â on our ",(0,a.jsx)("a",{href:"/blog",children:"journal"}),"."]})]})]})}):"/contact"===e?(0,a.jsx)("section",{className:"sf-page",children:(0,a.jsxs)("div",{className:"sf-page-inner",children:[(0,a.jsx)(im,{eyebrow:"Contact",title:"Get in touch",lead:"An app you want built, a book you want published, support for something you already use, privacy requests, or partnership inquiries. We respond within one business day."}),(0,a.jsxs)("div",{className:"sf-contact-grid",children:[(0,a.jsxs)("div",{className:"sf-contact-info",children:[(0,a.jsxs)("div",{className:"sf-contact-card sf-glass",children:[(0,a.jsx)("span",{className:"sf-contact-card-icon","aria-hidden":"true",children:"ð±"}),(0,a.jsx)("h3",{children:"A new app"}),(0,a.jsxs)("p",{children:["Tick the platforms you need and share your timeline. A stalled build from another team is a perfectly normal starting point. See our"," ",(0,a.jsx)("a",{href:"/services",children:"services page"})," for capabilities."]})]}),(0,a.jsxs)("div",{className:"sf-contact-card sf-glass",children:[(0,a.jsx)("span",{className:"sf-contact-card-icon","aria-hidden":"true",children:"ð"}),(0,a.jsx)("h3",{children:"A book to publish"}),(0,a.jsx)("p",{children:"Tell us the title, roughly how long it is, and whether the manuscript is finished. We come back on editing, typesetting, cover, ISBN, and getting it into paperback, hardcover, and ebook retail."})]}),(0,a.jsxs)("div",{className:"sf-contact-card sf-glass",children:[(0,a.jsx)("span",{className:"sf-contact-card-icon","aria-hidden":"true",children:"â±"}),(0,a.jsx)("h3",{children:"What happens next"}),(0,a.jsxs)("ol",{className:"sf-contact-steps",children:[(0,a.jsx)("li",{children:"We read it ourselves â no bot, no ticket queue."}),(0,a.jsx)("li",{children:"A reply within one business day with first questions."}),(0,a.jsx)("li",{children:"A short call, then scope, timeline, and a fixed quote."})]})]}),(0,a.jsxs)("div",{className:"sf-contact-card sf-glass",children:[(0,a.jsx)("span",{className:"sf-contact-card-icon","aria-hidden":"true",children:"ð"}),(0,a.jsx)("h3",{children:"Already using one of our apps?"}),(0,a.jsxs)("p",{children:["Support requests, privacy requests, and account deleti
2711on all start here too â or go straight to ",(0,a.jsx)("a",{href:"/support",children:"support"}),"."]}),(0,a.jsxs)("p",{className:"sf-contact-policies",style:{marginTop:12},children:[(0,a.jsx)("a",{href:"/privacy",children:"Privacy Policy"}),(0,a.jsx)("a",{href:"/terms",children:"Terms of Use"}),(0,a.jsx)("a",{href:"/support",children:"Support"})]})]})]}),(0,a.jsxs)("div",{className:"sf-form-card sf-glass",children:[(0,a.jsx)("h2",{className:"sf-form-title sf-display",children:"Tell us what you need"}),(0,a.jsx)("p",{className:"sf-form-sub",children:"Tick everything that applies â one app, several platforms, a book, or all of it. A paragraph of detail is plenty to start."}),(0,a.jsx)(m,{pageSlug:"/contact",pageTitle:"Contact"})]})]})]})}):null,(0,a.jsx)(ig,{})]})]})}],175522)}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.