Technical proposal
What we would build for you instead
A technical proposal for fluentwithstories.com. Written for Gabriel. Prepared 1 November 2026.
This document is based entirely on a read-only technical teardown of your public
site, recorded in docs/evidence/findings.md. Every number and product name below
comes from that teardown. Nothing in this document reproduces your stories, your
audio or your illustrations, and nothing in it is a recipe for anyone to take them.
The short version: the platform is replaceable. The content is yours. We build the platform, you keep the material.
Section 1 of 14
1. What the teardown found
Your site is not a custom application. It is five rented parts assembled on top of a site builder. Each part is a commodity you could replace; none of them is doing something a well-built product of your own could not do better.
| Layer | What you use today | What it does for you | What it means for you |
|---|---|---|---|
| Site + CMS + page rendering | Webflow | every marketing page, every story page, all page templates | your page templates, your publishing workflow and your content live inside someone else's editor |
| Membership, auth, billing, plan gating | Outseta | accounts, the $97 course checkout, the member dashboard, plan-gated collections | auth and billing are not implemented by you; your paid gate is a display attribute on a page block |
| Interactive exercises | BookWidgets | every quiz, flashcard deck and challenge you have, embedded as iframes | you do not own the exercise runtime, its user experience, its offline behaviour or its data |
| Media delivery | BunnyCDN | story audio (MP3) and illustrations | your media host is doing the delivery correctly and protecting nothing |
| Edge / DNS / TLS / bot protection | Cloudflare | CDN, TLS, bot protection in front of everything | the one layer that is pure infrastructure, not product lock-in |
Measured facts from the teardown that shape everything below:
- The site is 343 URLs: 294 member dashboard pages, 32 free story pages, 2 story listings, 1 live course page, and 14 marketing/support pages.
- 32 free stories, by level: A1 — 10, A2 — 10, B1 — 8, B2 — 4.
- Two courses of 30 stories each. The A1 course page is live and shows $97 in six places in its HTML; one search-indexed snippet shows the same price.
- A story record has seven fields: level, Spanish title, English title, body with key vocabulary marked up, illustration, audio, and practice widgets. Free story bodies run about 125–150 words; course stories 300–350 words.
- The page HTML is rendered by a serverless function at Webflow's origin in
us-east-1, then cached at Cloudflare's edge — one response carried anage:of about 98,000 seconds, and the marketing pages carrysurrogate-control: max-age=432000(five days). - Webflow's own internal manifest file is publicly readable, and it discloses your CMS collection structure and a redirect map. It is not a PWA manifest.
- Your exercises all live in one BookWidgets teacher account; free story pages embed 2 widgets, the course page embeds 5 or more. Every exercise is a third-party iframe.
- The one audio file we measured returned
200,audio/mpeg, 908,012 bytes (about 0.9 MB), withAccess-Control-Allow-Origin: *, and with no Referer header sent — the URLs are unsigned. There is no token, no expiry and no hotlink protection. - The 294 member URLs are published in your public sitemap. Search engines are being invited to index your members' private pages.
- There is no app. No PWA, no service worker, no native client. Nothing on your site is offline-capable.
Two facts decide everything else in this proposal.
One: the platform is replaceable. Five commodity layers, each with a well-understood replacement, each mapping onto a small data model that the teardown read straight off the page.
Two: the content is the asset, and it is the only thing that is not reproducible. 32 free stories, sixty course stories, and your own audio recordings — written and recorded by you. Copyright protects that material legally. Nothing protects it technically today. That is a problem to fix, and fixing it is part of this plan.
Section 2 of 14
2. The target architecture in one picture
The shape we recommend: one content core, two clients.
Cloudflare
Edge, DNS, TLS and WAF.
Static site
Marketing pages, free stories, the reading experience.
The app
Offline library, review queue, audio player, speaking practice.
Accounts and access
A small service you control: accounts, entitlement and signed asset URLs.
Payment provider
Merchant of record.
Content core
Story and exercise data.
Media CDN
Audio and illustrations, served only via signed URLs.
Three consequences worth stating plainly, because they are the parts that surprise people:
- The content core is the whole point. Author a story once, in one structured form; the website and the app both render from it. Without this, you are maintaining your library twice forever.
- A pure static site cannot hold a paywall. Anything served as HTML is readable by anyone who asks for it. Static is exactly right for your marketing pages and your free stories, and exactly wrong for the paid ones. Your current setup is the demonstration of that: the paid gate is a display attribute, and the media behind it is unsigned. Phase 2 therefore includes one small service, deliberately kept small.
- The app is not a port. It is a different product with a different job — habit and retention — and it needs a data model that the website never needed.
Section 3 of 14
3. Layer 1 — Site and page rendering
Replaces: Webflow.
What you have today
Webflow renders every page, serves your templates, hosts your CMS and generates your
shared stylesheet from cdn.prod.website-files.com. The rendering happens in a
serverless function at their origin and is then cached at Cloudflare for up to five
days. Your CMS structure (stories/es, story-collections, collection-stories,
learn-spanish, dashboard, collection-complete) is disclosed in a publicly
readable manifest file, along with your redirect map.
What we would use instead
Stories and pages as structured files in a Git repository, built into static HTML. One story = one file with seven fields (level, Spanish title, English title, body with marked vocabulary, illustration, audio, exercise set). One marketing page = one file. A build step turns those into the published site. Use a well-supported static site generator, or the plainest tool that does the job — this is a small content model and it does not deserve a framework.
Add a headless CMS as an editing interface later, only if you want a point-and-click editor rather than a text file. The storage format stays the same either way, which is the point: the editor is a convenience, not the source of truth.
Why this and not the alternatives
- Versus staying on Webflow: it works today, and it will keep working — until the app needs your content in a machine-readable form. An iframe and a rendered page are not a content API. Every week you add stories to a builder you do not own is a week added to the eventual migration.
- Versus another no-code builder: you swap one dependency for another and inherit the same structural problem. Your exercises stay third-party iframes, your media stays wherever you put it, and the app still cannot read your content.
- Versus a hosted course platform: you would trade your site for someone else's marketplace, lose your own domain as the primary experience, and still not own the data model.
Trade-offs, honestly.
- You gain: your content in a form you own; a site that costs almost nothing to host; no page-render function, no per-page cache rules; free stories that could be served as plain files with no application behind them; and a content core that the app can read.
- You lose: drag-and-drop editing. Editing becomes "change the text, publish". That is a real workflow change for you, not a footnote.
- The risk is not the code. The code is a few weeks. The risk is migration fidelity: 32 free stories, sixty course stories and 14 marketing pages have to arrive intact, with the markup for your key vocabulary preserved, and your existing story URLs have to keep working.
What stays the same
Your page templates, your visual identity, your story structure, your vocabulary markup, your reading order, your URLs (with redirects where they change), and your writing. Nothing about how a story reads has to change.
What it costs
- Recurring: static hosting is close to free, plus one small domain/CDN line. This layer is the cheapest thing in the document and the one that removes a per-seat/per-project vendor.
- Hidden: the migration is billed in authoring hours, not engineering hours — someone has to move 92 stories and check every one. And every future edit now needs whoever can publish a build, or a CMS interface you have to pay for.
Effort
3–5 weeks for one competent developer: about 1.5–2.5 weeks to build the content model, the build pipeline, the templates and the reading experience; about 1.5–2.5 weeks for migration and redirect verification.
Section 4 of 14
4. Layer 2 — Membership, auth, billing and plan gating
Replaces: Outseta.
What you have today
Accounts, checkout, the member portal, the support knowledge base and plan-gated content all come from Outseta. The gate is an HTML attribute naming the plan that may see a page block. Your dashboard is a Webflow page that embeds their portal. Auth and billing are not implemented by you.
What we would use instead
Rent the money. Own the gate.
- Rented: a hosted billing/membership product — preferably one that acts as merchant of record, so that sales tax, invoicing and the regulatory side sit with the vendor, which is the model you already benefit from. Checkout, subscriptions, one-time purchases and receipts stay with them. Do not hand-roll payments.
- Owned: a small service of your own that answers one question — may this account see this story or this course? It holds accounts, products and entitlements (account + product + state + dates), and it issues the short-lived, signed asset URLs for gated media. The billing vendor tells it when someone pays, is refunded or cancels; it decides access.
- Owned: the gated delivery path. Paid stories are not published as public HTML files. They are served through the entitlement check, with the media behind signed URLs.
The website's paid pages and the app both call this one service. That is what makes "buy once, use on both" possible without building two businesses.
Why this and not the alternatives
- Versus staying entirely on the vendor: workable, and the least work today. The problem is where the gate lives: it is an attribute on a content block inside a page, which is what we observed on your member dashboard. Whether the vendor enforces that attribute on the server or applies it in the browser is something we did not test and cannot state — ask them, and ask what happens to the content of an account that stops paying. But whichever the answer, it cannot gate an app at all, because the app does not render your pages. It also can never answer "this learner has three days of review queue waiting", which is the app's core loop.
- Versus writing your own billing: the tempting answer, and the wrong one. Payment correctness, tax, invoicing, chargebacks and password security are not where a product like yours should spend its engineering. Renting this and owning the small deterministic part is the better split.
- Versus free-but-gated-by-obscurity: your current media situation, and it protects nothing. Obscure paths are not access control.
Trade-offs, honestly.
- You gain: control of access on both web and app; a single source of truth for who has paid; the ability to change plans, add a trial, add a yearly option or bundle the app without asking a vendor; and the ability to leave the billing vendor without losing your member base.
- You lose: the one-attribute simplicity. Access is now real code, with real states — active, expired, refunded, cancelled, in grace period — and you own those edge cases.
- New risk: webhooks fail silently. A refund that does not revoke access is a leak; a successful payment that does not grant access is a support ticket. This needs an explicit reconciliation job, not hope.
What stays the same
Your pricing decisions, your product definitions, your member relationships, and the fact that you never personally touch a card number. If you prefer, you can keep the current vendor as the billing front end and only replace the gate — that is a legitimate, lower-risk first step and it is compatible with everything below.
What it costs
- Recurring: percentage-based payment fees (the main real cost of this layer), plus the membership/billing product's own subscription, plus a small always-on service to host the gate.
- Hidden: every support question that starts with "I paid and I can't get in" is now routed to you, not to a vendor's support desk. Those are recurring operating hours, not a one-off. And if you later sell digital content inside the app, app store rules may require the store's own billing path for that purchase, which affects both your margin and your price — verify this before you set app pricing.
- Not measured: the teardown did not authenticate anywhere, so this document contains no vendor prices. Get current quotes; the fee structure matters more than the headline number.
Effort
3–5 weeks for one competent developer: accounts, the entitlement service, the billing integration and webhooks, the gated delivery path with signed URLs, the account screens, and a reconciliation job. Add roughly a week if you also build trials, yearly plans or bundles.
Section 5 of 14
5. Layer 3 — Interactive exercises
Replaces: BookWidgets.
What you have today
Every quiz, flashcard deck and challenge is a widget in a single third-party account, embedded by id. Two widgets on a free story page, five or more on the course page. The exercise designs are yours; the runtime is not.
What we would use instead
Your exercises as data, rendered by components you own.
- One exercise schema covering the types you already teach: comprehension questions, flashcards, pattern drills, listening sets, writing prompts, speaking prompts, plus per-story culture notes.
- The exercise set is a field of the story record, so a story and its practice travel together — to the website and to the app.
- One renderer per exercise type, shared between the web experience and the app.
- Spaced repetition is then possible, because a flashcard is now a record you own with a history attached to a learner, rather than a widget inside someone else's iframe.
Why this and not the alternatives
This is the layer where the honest answer is not "the vendor is bad". BookWidgets is a good authoring tool, and re-authoring your existing exercises is real work. The reason to leave is structural, and the teardown states it: an iframe running on someone else's account cannot deliver offline study, spaced-repetition scheduling, native input such as speech, or progress that belongs to your product. It cannot work in an app. And your paid tier's practice is the part you most want to own, because it is what people pay for.
Alternatives considered and rejected:
- Keep BookWidgets on the website, build separate exercises for the app. Two authoring formats, two maintenance paths, one story content that drifts. This is the most expensive option on a three-year view.
- Keep BookWidgets for the paid web course only. Reasonable as a temporary state, and it does reduce the Phase 2 scope — but it leaves the app without practice, which is the app's reason to exist.
Trade-offs, honestly.
- You gain: exercise data you own, in a format the app can use; progress and answers that belong to your product; the ability to run exercises offline; and one fewer third-party account with your content inside it.
- You lose: the authoring interface you are used to. Building a comfortable internal authoring tool is a project of its own; structured files plus a preview page is the pragmatic first answer.
- Long tail: your existing exercises have to be re-entered into the new schema. That is measured in evenings, not hours, and it is the single most underestimated item in this document.
What stays the same
Your exercise designs and your teaching. The exercise types the teardown found in your markup — comprehension quizzes, vocabulary flashcards, pattern drills, listening and speaking challenges, culture sections — are exactly the vocabulary of the schema we would build. You are not being asked to invent a new pedagogy, only to move your existing one into a form that travels.
What it costs
- Recurring: removes a third-party account and its per-seat cost. That is the direction of travel — the savings are real but small.
- Hidden: re-authoring the existing exercises is a content cost, and the authoring tooling is a build cost. Do not model this as a one-week job.
Effort
3–6 weeks: 2–3 weeks for the schema, the renderers and the progress model; 2–4 weeks for re-authoring. If you keep BookWidgets on the web course for now, Phase 2 drops by roughly 2 weeks and the debt lands in Phase 3 instead.
Section 6 of 14
6. Layer 4 — Media delivery
Replaces: BunnyCDN (as configured today).
What you have today
Audio (MP3) and illustrations are on a CDN with a fully consistent, predictable path
scheme organised by level. The delivery part is fine — media should not come from your
application server. The protection part is absent: the one file we measured returned
200, audio/mpeg, 908,012 bytes, Access-Control-Allow-Origin: *, to a request with
no Referer at all. Unsigned URLs, no token, no expiry, no hotlink protection.
Your own FAQ states the content is copyrighted and must not be downloaded or reproduced. That is a legal statement, and it is the correct one. It is also, on its own, not a technical control. A catalogue of ~0.9 MB audio files behind unsigned URLs is an open bandwidth tap, and bandwidth is the cost that grows when someone else's script finds it.
What we would use instead
Keep a CDN. Change the delivery model around it.
- Signed, short-lived URLs for every audio file and illustration, issued by the entitlement service, tied to an account where the content is paid.
- Tighten CORS from
*to your own origins and the app. - Origin access control so the storage bucket itself cannot be read directly, bypassing the CDN.
- Hotlink and leech protection on, with an alert if egress jumps unexpectedly.
- Normalised asset names and a version/manifest record, because both the app's offline cache and your own maintenance need to know what a file is and whether it changed. Your current filenames contain spaces, parentheses and accents — fine for a human, awkward for a build pipeline.
- Illustrations delivered in modern formats at sensible sizes — a reading app is mostly text, and illustration weight is pure waste on a phone.
The CDN product itself can stay as it is, or move to any equivalent. It is a commodity and should be treated as one: the fix is in how URLs are issued, not in which vendor sells the bytes.
Be honest about what this fix does and does not achieve
It stops bulk automated retrieval and casual hotlinking, and it makes the free/paid split real instead of cosmetic. It does not make a file undownloadable. Any audio that plays on someone's device has been delivered to that device; that is true for every streaming service in the world. The goal is to make your catalogue expensive and impractical to take in bulk, to tie access to an account, and to know when someone tries. Anyone promising you more than that is selling you something that does not exist.
We are deliberately not publishing a step-by-step description of the current exposure anywhere in this material, and we recommend you do not describe it publicly either. Fix it, and say that it is fixed.
What stays the same
Your audio recordings, your illustrations, your path organisation by level, and the fact that your media is on a CDN. Nothing has to be re-recorded.
What it costs
- Recurring: storage plus egress. Egress is the line item that grows with success and with offline downloads, and it is the one to watch: an app that lets learners download the whole library multiplies your traffic in a way the website never did. Model the worst case (every paying learner downloads everything) before you launch, not after.
- Hidden: signed URLs have an awkward edge for an app that must work offline — a downloaded file outlives its signature. That is expected and acceptable, but it needs a deliberate policy for what gets cached, for how long, and what happens when a subscription lapses.
Effort
1–2 weeks: signing, origin access control, CORS, asset normalisation, and a verification pass that confirms nothing is publicly retrievable any more.
Section 7 of 14
7. Layer 5 — The mobile app
This layer does not exist today. No PWA, no service worker, no native client.
This is the most important section of this document, and the one to read twice.
The app is not a port of your site
A port of your site puts your pages in a wrapper, requires a network connection, and delivers the same product with a worse experience. It would fail on the platform it is built for, because the stores and, more importantly, learners expect a native app to do things an app can do.
An app is a product decision about habit and retention. The website is where people find you, understand what you teach, and study a story when they have a laptop open. The app is the thing a learner opens on the bus, at breakfast, and for the eleven minutes before sleep. Those are different products with different jobs, and the app's job is to bring someone back tomorrow — which is the only metric that matters for a subscription or a course business.
What the app can do that the website cannot
- Offline study. Stories, vocabulary and audio downloaded to the device, usable with the network off: on a plane, on the metro, in the countryside. There is no version of this on the web that is equally reliable.
- Spaced-repetition scheduling. Your stories already mark their key vocabulary. That markup is a ready-made seed set of review items — for every free story and every course story. An app can hold per-item review state and put a short daily queue in front of the learner: these twelve words are due today, from the stories you have read. Nothing on your site can do this, because the state has to live with the learner and survive between sessions and devices.
- Speaking practice that is actually native. A microphone prompt, a recording and a score against the model text is a natural app interaction and an unnatural web one. Be realistic about scope: an honest first version scores whether the expected words were said, clearly enough, at a reasonable pace. A genuinely good pronunciation score is a research problem measured in months, not weeks. Ship the honest rough version first, then decide whether it is worth more.
- Listening practice with real controls. Variable speed, per-sentence repeat, background playback, lock-screen controls, and continuing to play while the screen is off. Your course already advertises adjustable audio speed; the app is where that becomes a native player rather than a web control.
- Progress that follows the learner. One account, one streak, one word list, on every device. Read three stories on the train, pick up on the laptop at home.
- Streaks and a daily goal. A defined amount of study per day, a visible count, and a gentle reminder. This is the single most effective retention mechanic in language learning, and it needs notifications and a device the learner already carries.
- Downloads managed as a library. What is on the device, what is new, what can be removed to free space — a real screen with real state, not a browser cache.
- A word list that belongs to the learner. Tap a word, keep it, review it, hear it. On the web that interaction exists inside a page; in an app it becomes a personal collection that persists.
The data model the app needs
The app cannot consume page HTML or an iframe. It needs the content core in a machine-readable form. The model below is a small extension of the story record the teardown already read off your pages — seven fields, plus the learner side.
Content (authored once, used by web and app):
- Story — id, level (A1–B2), Spanish title, English title, body with the key vocabulary marked as structured items rather than only styling, word count, sequence position, illustration reference, audio reference, exercise set, and whether it is free or belongs to a product.
- Course / collection — id, title, level, ordered list of story ids, the product that unlocks it, and its completion rule.
- Vocabulary item — the word as it appears, its dictionary form, the English meaning, the story it came from, its position in that story, and its example sentence. This is the review unit, and it is why the SRS queue can be built without writing new content.
- Exercise — id, story id, type, items, correct answers, feedback text.
- Asset — id, kind (audio or illustration), size, checksum/version. The app's download manager and the build pipeline both need this.
Learner (per account, synced):
- Account and entitlement — who the learner is, which products they hold, and the state and dates of each. Same service that gates the paid web stories.
- Progress event — an append-only record: learner, story, position, timestamp, which words were tapped, which audio was played, which answers were given. Append-only is the key property; it makes offline sync tractable.
- Review state — per learner, per vocabulary item: due date, interval, ease, number of lapses, recent history. This is the SRS memory. It is small, it is the most valuable data in the product, and it must never be lost.
- Streak / daily goal state — days met, current count, last activity, and the learner's timezone, because "today" has to be defined explicitly or streaks break for anyone who travels.
- Device sync cursor — what this device has already sent and received, so two devices can converge without re-sending the world.
The one hard problem, named: offline-first sync. Storing data locally is easy; deciding what wins when two devices change the same thing while offline is not. Progress events are append-only, so they merge cleanly. Review state does not — a naive "last write wins" silently discards a learner's reviews and corrupts their schedule. This needs a deliberate merge policy from day one; it is a design decision, not a bug you fix later.
What "a product decision, not a port" actually means in practice
- Decide the free/paid split you actually want. Today your paid tier is more and longer stories — a content split. The app makes a genuine product split possible for the first time: offline library, spaced repetition, speaking practice and streak tracking are things the website cannot offer at any price. That is a much better reason to pay, and it does not require you to write another story.
- Decide the daily loop. What does a learner do in five minutes? The answer should be one screen, not four.
- Decide the first platform. One platform done properly beats two done thinly, and the second platform is mostly cheap once the content core is right.
- Decide what success means before launch: day-1 and day-7 return rates, lessons completed, review queue kept up. Without this, the app becomes an expensive experiment you cannot evaluate.
What it costs
- Recurring: app store developer accounts, a push notification path, the sync service, and CDN egress from downloads. Plus continued engineering — an app in two stores is a product you maintain, not a project you finish.
- Hidden: store review cycles and rejections, which cost calendar weeks and cannot be scheduled away. Privacy declarations and permission prompts for microphone and notifications. Store billing rules if you sell inside the app. And the honest one: you now own availability. If your service is down at breakfast, the learner notices, and there is no vendor to call.
Effort
10–16 weeks for a v1 on one platform, by one competent developer, assuming the content core and exercise data already exist: offline storage and download management, the reading and player experience, the SRS engine and review screen, progress sync, streaks and notifications, speaking v1, and store submission. Add 4–8 weeks for a second platform. Budget 2–4 weeks per quarter for maintenance thereafter.
Section 8 of 14
8. Engineering effort and the long tail
Estimates below are for one competent developer working at this full time. They are estimates, not measurements — this document measured your site, not the work.
| Workstream | Estimate | Note |
|---|---|---|
| Layer 1 — content core, site build, templates, reading experience | 3–5 weeks | migration and redirect verification included |
| Layer 2 — entitlement service, billing integration, gated delivery | 3–5 weeks | plus ~1 week for trials/years/bundles |
| Layer 3 — exercise schema, renderers, progress model | 3–6 weeks | 2–3 weeks engine, 2–4 weeks re-authoring |
| Layer 4 — signed media delivery, origin control, asset normalisation | 1–2 weeks | |
| Layer 5 — app v1, one platform | 10–16 weeks | second platform +4–8 weeks |
| To a paid web product on your own platform (layers 1–4) | ≈ 10–18 weeks | |
| To a shipped app | ≈ 6–9 months end to end | phases overlap; app work starts before the web work finishes |
The long tail — the parts that take longer than people expect. Every one of these is a schedule risk on the project, not a footnote:
- Content migration fidelity. 92 stories, each with marked vocabulary, titles in two languages, illustrations and audio. The engineering is a script; the checking is human and cannot be skipped.
- Exercise re-authoring. Every quiz, deck and challenge re-entered into a new schema. This is the most commonly underestimated item in this entire plan.
- Redirects and old URLs. Your existing story URLs and your short link map must keep working, or you lose search traffic and shared links.
- Asset normalisation. Filenames with spaces, parentheses and accented characters, across audio and illustrations, need a deterministic naming and versioning scheme before an app can cache them.
- Offline sync merge policy. Small code, subtle design, no room for guessing.
- Speech scoring quality. Weeks for an honest rough score, months for a good one.
- Store review and rejection cycles. Calendar time you cannot compress.
- Privacy, consent and analytics. Measurement is presently a third-party tag; an app needs a deliberate position, and the stores require declarations.
- Accessibility. A reading product should be readable, and this is design work across web and app.
- Operational ownership. Monitoring, alerts, backups, an incident path, and the question nobody writes down: who fixes it at 23:00.
Section 9 of 14
9. Three phases
Each phase ends with something you can use, and each depends on the one before it. Phases 2 and 3 overlap deliberately.
Phase 1 — Your content, in your hands, on your own site
Weeks 0–6 (approximately).
What you get at the end of this phase: your public site — the marketing pages and all 32 free stories, level by level, with the same reading experience — rebuilt on a platform you own, with your content stored as structured data instead of living inside a site builder. Your audio and illustrations are no longer publicly retrievable without authorisation. Your member pages are out of the public sitemap. There is no login, no billing and nothing to pay a builder for.
Depends on: you deciding who owns the content in writing, choosing where it is hosted, and confirming your story levels and reading order. And on the data handover session.
Phase 2 — Paid access that you control
Weeks 6–14 (approximately).
What you get at the end of this phase: a working paid web product. Real accounts, real checkout, your own access gate, and courses delivered on your own platform — 30 stories per course, in sequence, with progress and adjustable audio speed. Your exercises are no longer third-party iframes: they are components you own, running from your own data, and the practice that people pay for is finally yours. The same service now answers "may this learner see this?" for the website and, later, for the app.
Depends on: Phase 1's content core. Requires the exercise re-authoring effort, and a decision on how you want to take money (rented merchant of record, or your own processor with the tax consequences that follow).
Phase 3 — The app
Weeks 12–30 (approximately, starting before Phase 2 ends).
What you get at the end of this phase: the product your website cannot be. A learner downloads a story and its audio, studies with the network off, gets a short review queue every day built from the vocabulary in the stories they have read, hears the audio with real player controls including while the screen is off, practices speaking, and keeps a streak. Their progress follows them between phone and laptop. And it is the first piece of your business that nobody can copy by reading your page source.
Depends on: Phase 2's accounts and entitlements, the exercise data from Phase 2, and store accounts. It also depends on one more thing that is not engineering at all: your decision about what the free experience is and what the paid one is.
Section 10 of 14
10. What you keep
This is the part of the proposal we would put first if we were being honest about priorities.
- Your content. Every story you have written, free and paid, stays yours. It moves into a form you control, on infrastructure you control. The words do not change hands, and the platform is yours to leave again if you ever want to.
- Your brand. Your name, your domain, your look, your voice, your level structure, your reading order. Nothing about this plan requires you to become someone else's brand on someone else's marketplace.
- Your audio recordings. Your own recordings, unchanged, kept as masters and re-delivered through a protected path. Nothing is re-recorded and nothing is replaced by a synthetic voice.
- Your exercise designs. The quizzes, decks and challenges you built are your teaching. They move into an owned format; they are not discarded or replaced.
- Your audience. Your learners keep their accounts and their access. Exporting your member list from your current vendor is part of the migration — treat it as a condition of the move.
- Your pricing decisions. What you charge, whether there is a free tier, whether the app costs more, whether there is a yearly option: yours. The price we measured — $97 on the live A1 course page — is a fact about what you charge today, not a constraint.
- Your exit. Every layer in this plan can be left. A rented payment vendor can be swapped. A CDN is a commodity. Static files and a structured content core can be moved anywhere by anyone.
And the framing that matters more than any of the above: your content, your app. We build the platform. You own the material, the accounts and the decisions. If we build this for you, you should be able to take it away from us as easily as you can take it away from Webflow.
(One thing to check in that list: confirm you hold the rights to every illustration in your library. The teardown measured how your images are delivered, not who drew them, and illustration rights are the kind of detail that only surfaces years later.)
Section 11 of 14
11. What you must decide before any code is written
None of these are technical questions. All of them change the build.
- Who owns the content, in writing. Yours, unambiguously, with the platform separate from the material. This should be settled before the first file is migrated.
- App, or a much better website first. Both are defensible. A better website on your own platform fixes the structural problems and costs less. The app is where the growth is, and it needs the website work done first anyway. If you must pick one to finish first, finish the platform, then the app.
- What the paid tier is. More stories (today's split), or a product difference the website cannot offer (offline, review queue, speaking, streaks). The second is stronger, and it does not require writing new content.
- How you take money. Rent a merchant of record — simpler, and the tax burden sits with the vendor — or run your own processor and own the tax, invoicing and dunning.
- Who edits the content, and how. A text file per story in a repository is durable and cheap but not friendly. A CMS interface is friendly but is another dependency. Decide whether you want to be the editor or to hand this to someone.
- Where the media lives, and who pays for bandwidth. Egress grows with downloads, and offline downloads change that arithmetic.
- One platform or two, and which one first.
- Who answers support. Access problems, broken streaks, refund questions. This becomes your job the day the gate becomes yours.
- What happens if you stop paying each vendor today. Worth answering now, vendor by vendor, while it is a thought experiment rather than an incident. The answer to "what happens to my content if I stop paying" is the strongest argument for the whole of this proposal.
- What "working" means. Pick the numbers before launch — members, completion, return rate — or you will not be able to tell whether this was worth doing.
Section 12 of 14
12. Costs, honestly
This section contains no vendor prices, because the teardown measured your site's delivery, not anyone's price list, and we will not invent figures. What we can give you is the shape of the cost, which is what actually decides whether this is a good idea.
One-off:
- Engineering, layers 1–4: roughly 10–18 developer-weeks.
- Engineering, the app: roughly 10–16 more developer-weeks, plus 4–8 for a second platform.
- Content work: migrating 92 stories, re-authoring your exercises, illustration and audio tidy-up. Billed in human hours, not code hours, and it is the item people forget.
- Store accounts and submission.
Recurring, per layer:
| Layer | What you pay for | How it behaves as you grow |
|---|---|---|
| Site + content | hosting for static files | flat and negligible |
| Access gate + accounts | a small always-on service, plus billing fees | billing fees scale with revenue; the service stays small |
| Exercises | nothing to a third party | internal maintenance only |
| Media | storage + egress | grows with success, and grows faster with app downloads |
| App | store accounts, notifications, sync service, maintenance | grows with users and with every platform you support |
| Edge | Cloudflare | flat |
The hidden costs, named. These are the ones that turn a sensible plan into an expensive one:
- Ownership transfers risk to you. Today a vendor's outage is their problem. After this, it is yours. Budget for monitoring and an incident path, and for someone to be on call in a small way.
- Two clients, one content core — or double everything. If the content core is right, the app is cheap to keep fed. If it is wrong, every new story is entered twice, forever. This is the single decision with the longest shadow.
- Support is a recurring cost, not a launch cost.
- Vendor prices change and vendor accounts have minimums. Your current stack's price is not a fact about the future, and per-account pricing punishes exactly the growth you want.
- Your attention is the scarcest resource. Every week spent migrating is a week not spent writing the stories that are the actual product. This is why the phases are ordered the way they are: get the content into a form you own once, then build on it twice.
Where the easy answer is wrong. Three temptations worth naming:
- "Just save the pages from the current site and put them somewhere else." You would capture rendered HTML — including 294 member dashboard URLs — and inherit no content model, no exercises and no path to an app. You would own a snapshot, not a system.
- "Just move to a different no-code builder." Same structure, same rents, same iframes, same unsigned media eventually. The dependencies change name.
- "Just wrap the site in an app." An app whose every screen depends on the network, that cannot do offline study, review scheduling or real audio controls, delivers none of the reasons to install it. It is the most expensive way to get the least benefit.
Section 13 of 14
13. What we are not proposing
- We are not proposing that your stories, audio or illustrations be re-used by anyone else, and nothing in this document is a means to obtain them.
- We are not proposing that your content be copied, scraped, rehosted or republished. The proposal is the opposite: move the content into a structure you own, and put a real access control in front of it.
- The teardown's finding that your media is currently retrievable by anyone who asks is reported here as a defect to fix, and this document deliberately stops at describing the fix rather than the technique.
- Nothing in this document contains your story text, your audio, your illustrations or your page copy, and any sample Spanish elsewhere in this material was written for the purpose or is public domain.
Section 14 of 14
14. Assumptions, and the limits of this document
Everything above that is a fact about your site traces to docs/evidence/findings.md,
which was produced by read-only, unauthenticated HTTP requests on 1 November 2026. The
following are assumptions, labelled as such, and should be checked before they are
relied on:
- Vendor prices are absent by design. The teardown did not authenticate anywhere, so no subscription, seat or transaction price could be measured. Get current quotes.
- Engineering estimates are estimates. Week ranges are professional judgement for one competent full-time developer, not measurements.
- Course structure. The dossier records two courses of 30 stories each; only the A1 course page was verified live. The A2 course's live state is assumed to match.
- Membership export. Assume your member accounts and entitlements can be exported from your current membership vendor. The teardown could not verify this; confirm it before committing to the move.
- App store billing. Assume that selling digital content inside a mobile app may require the store's own billing path. Verify the current rules; it affects your price and margin.
- Speaking practice. Assume an honest first version scores whether the expected words were said with reasonable clarity. Assume a high-quality pronunciation score is a research effort of a different magnitude.
- Illustration rights. The teardown measured delivery, not authorship. Confirm you hold the rights to every illustration.
- Analytics. The only measurement signal the teardown noted was the presence of Google Tag Manager. Traffic, revenue and conversion figures were not measured and do not appear anywhere in this document.
- "Weeks" means one developer, full time, with the content work being done in parallel by you or a helper. Serialise them and the calendar roughly doubles.
What the teardown did not look at
Server-side code, databases, internal APIs, your membership vendor's plan configuration, your exercise account's settings, analytics beyond the presence of Google Tag Manager, and any revenue or traffic figures. Statements about those would be guesses, so there are none.