import Link from "next/link"; import type { Metadata } from "next"; import { Band, CtaBand, SiteNav, body, h2, h3, kicker, small, wrap } from "@/components/site"; import { PRICES } from "@/lib/plan"; import { plansLive } from "@/lib/plans-live"; /* Two pages in one file, and which one renders is decided by the console's plans switch, not by a * deploy — see lib/plans-live.ts for why. The first is the page as it stood while ThreadCount was * free, kept word for word: its promises are the reason the second page reads the way it does. * The founder's sentence under "Why …" is the same on both; only the heading's object moves. */ export async function generateMetadata(): Promise { const live = await plansLive(); return live ? { title: "Pricing", description: `ThreadCount is free to run yourself and free to host for rooms under ${PRICES.freeStaff} staff records. Hosting a facility is $${PRICES.hostedAnnual} a year. Every feature is in every edition.`, alternates: { canonical: "/pricing" } } : { title: "Pricing", description: "ThreadCount is free. No licence, no per-device charge, no per-user charge, and no tier above this one holding the useful parts back.", alternates: { canonical: "/pricing" } }; } const INCLUDED = [ { t: "Every person, every device", b: "Access is per person and costs nothing, so the counter, the ward or wing, and the coordinator’s phone can all be signed in at once." }, { t: "Every feature", b: "Issuing, ordering, receiving, stocktakes, all nine report tabs and the month-end pack. There is no locked tier." }, { t: "Every record, exportable", b: "Every report and register exports as CSV. One backup file carries the whole facility out. Nothing is held back to make leaving hard." }, ]; const COVERED = [ "Unlimited staff records", "Unlimited garments and sizes", "Unlimited issues and receipts", "Scanning by USB scanner, or by phone camera where the browser reads one", "Draft replenishment orders", "Printable purchase orders and slips", "Stocktakes with blind counting", "All nine report tabs", "Journal CSV for finance", "The one-click month-end pack", ]; const HONEST = [ { q: "Is there a paid tier coming?", a: "Nothing is planned. If that ever changes, the rooms using it will hear before the website does." }, { q: "What’s the catch?", a: "It’s maintained by one person alongside a day job. That’s the honest limitation — not a cost, but a support surface worth discussing before you commit a whole site to it." }, { q: "Do we need to buy hardware?", a: "No. It runs on the phones and computers the room already has. A USB scanner speeds up the counter but isn’t required." }, { q: "What about hosting costs?", a: "There is nothing for you to pay. If your facility needs it hosted inside their own environment, that’s a conversation worth having early." }, ]; /* ---------- the page once plans are live ---------- */ const P = PRICES; const money = (n: number) => `$${n.toLocaleString("en-AU")}`; const PLANS = [ { name: "Community", price: "$0", per: "", who: "Run it on your own server. The source is published; every feature is in it.", rows: ["Everything, no ceiling", "Your Postgres, your backups", "Your own single sign-on broker, if you want one", "A public issue tracker"], cta: ["Read the install guide", "/getting-started"], lead: false, }, { name: "Hosted", price: money(P.hostedMonthly), per: "a facility, a month", who: `Or ${money(P.hostedAnnual)} a year. Free for a room under ${P.freeStaff} staff records.`, rows: ["Everything", "Australian hosting, nightly backups kept 35 days", "Point-in-time restore", "Single sign-on set up for you", "Email support, next business day"], cta: ["Start a 60-day trial", "/auth?mode=signup"], lead: true, }, { name: "Health Service", price: money(P.healthServiceAnnual), per: "a year", who: `Up to ${P.healthServiceFacilities} facilities, then ${money(P.healthServiceExtra)} each.`, rows: ["Everything, across every site", "One sign-in, switch between facilities", "Roll-up reports and a shared catalogue", "Named support, same business day", "Invoice, purchase order, security questionnaire"], cta: ["Talk about a pilot", "/contact"], lead: false, }, ]; const HONEST_LIVE = [ { q: "Is there a locked tier?", a: "No. Community, Hosted and Health Service run the same code. The differences are who runs it, how long the backups are kept, and how many sites it covers." }, { q: "We signed up when it was free. What changes?", a: "Nothing. Every facility created before this page changed stays on the free hosted plan, with everything, for as long as it exists. That was promised here, and it is written into the terms." }, { q: `Why ${P.freeStaff} staff records?`, a: "It is roughly where a room stops being one person and a cupboard and starts being a job. A clinic, a dental practice or a small home sits under it and pays nothing; a hospital ward is over it on day one." }, { q: "What happens if we stop paying?", a: "The facility goes read-only after a fortnight’s grace. Every report, export and the full backup keep working. Nothing is deleted, and writing starts again when the invoice is paid." }, { q: "How do we pay?", a: "By invoice, annually, against your purchase order. Prices are in Australian dollars before GST." }, { q: "What’s the catch?", a: "It is still maintained by one person alongside a day job. Hosted buys a response time; it does not buy a team." }, ]; function FreePage() { return ( <>
Pricing

Free

No licence. No per-device charge. No per-user charge. That is the whole page.

What free actually covers.

Everything. There is no tier above this one holding the useful parts back.

{INCLUDED.map((c, i) => (

{c.t}

{c.b}

))}
In the box
{COVERED.map((c) => (
{c}
))}

Why it costs nothing.

It was built by a hospital uniform coordinator to solve their own room’s problem. It already exists, it already works, and charging for it was never the point.

{HONEST.map((h) => (
{h.q}

{h.a}

))}
); } function LivePage() { return ( <>
Pricing

Free to run. Paid to host.

Every feature is in every edition. You pay for someone to keep it running, backed up and supported. A room under {P.freeStaff} staff records pays nothing either way.

{PLANS.map((p, i) => (

{p.name}

{p.price}
{p.per &&
{p.per}
}

{p.who}

    {p.rows.map((r) =>
  • {r}
  • )}
{p.cta[0]}
))}
In every edition
{COVERED.map((c) => (
{c}
))}

“Unlimited staff records” is true of Community, Hosted Facility and Health Service. The free hosted room holds {P.freeStaff}; past that it is a Hosted facility.

Why the software costs nothing.

It was built by a hospital uniform coordinator to solve their own room’s problem. It already exists, it already works, and charging for it was never the point.

What costs money is running it for other people, carefully: the servers, the backups, and being the person who answers.

{HONEST_LIVE.map((h) => (
{h.q}

{h.a}

))}
); } export default async function Pricing() { const live = await plansLive(); return ( <> {live ? : }

Nothing to sign. Open it and look.

The demo runs on demonstration data. Nothing you do in it touches a real record.

Open the working demo Getting started
); }