feat: care offer for websites, with its price and its limits
What happens after a website is live was one vague sentence about a monthly fee to be agreed. It is now one clear offer in the website case and in the questions: 89 EUR per month, cancel monthly, the site runs on servers in Germany, small changes by email are included, no editing system and no login. The limits are said as plainly as what is included: the first three months are part of the project price, anything bigger gets a price first, replies within two working days, no on-call, and the client can leave with everything. The decision and the parts that are not on the website (the larger variant, CMS as an exception, how the sites are served) are in the concept. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -76,8 +76,9 @@ App lives in `apps/web`. Social media videos live in `apps/video` (Remotion, sta
|
||||
or person, with pins), `Track` (straight runs, 45 degree bends), `Pad` (solder point), `Bit` (what travels).
|
||||
New drawings are built from these, not from pills, curves and round dots.
|
||||
- Two audiences, two pages (owner, 2026-10-05). The homepage is for people who commission a result and have
|
||||
no software of their own: a website or process automation (plus web presentations). It shows no prices
|
||||
and no hourly terms, only "ask for an offer". Everything for companies that book by the hour is on the
|
||||
no software of their own: a website or process automation (plus web presentations). It shows no project prices
|
||||
and no hourly terms, only "ask for an offer"; the one price there is the care offer for websites (89 EUR
|
||||
per month, see the concept, section 5). Everything for companies that book by the hour is on the
|
||||
freelance page only: the developer case, making an existing app reliable, how working together goes,
|
||||
project fit, tools, safeguards, rates and booking. Do not put any of that back on the homepage.
|
||||
- `DecodeText` is for monospaced text only; in proportional type the changing glyph widths make text jump.
|
||||
|
||||
@@ -88,7 +88,20 @@ Audit of an AI-built codebase, test foundation, mutation baseline, prioritized r
|
||||
|
||||
- Sprint: time-boxed package (example 40 h, 3,800 EUR) with a clear scope list. Extra work is a follow-up block, not open scope. 50% at start, 50% at launch.
|
||||
- Site: static Next.js, no CMS, no server, no database.
|
||||
- Hosting: managed platform (Cloudflare Pages or Vercel) in my account, flat monthly subscription (example 49-99 EUR) including hosting, SSL, domain renewal at cost, 1 h of changes per month. Deployment: git push builds automatically, client approves via a preview URL by email.
|
||||
- Care after the launch (owner decision 2026-10-05, replaces the managed-platform idea): one offer,
|
||||
"Betreuung", 89 EUR per month net, cancel monthly. Hosting on the owner's own servers in Germany (EU
|
||||
hosting matters to German clients; no Vercel or Cloudflare), domain, security and updates, and small
|
||||
changes by email (a text, a picture, opening hours; internally about 30 minutes a month). No CMS and no
|
||||
login for the client. The first three months are included in the project price. Anything bigger (a new
|
||||
page, a new feature) gets a price first. Reply within two working days, no on-call. The client can leave
|
||||
and take everything along.
|
||||
- Larger clients: the same care with all non-editorial changes included, about 250 EUR per month (one
|
||||
existing client pays 3,000 EUR a year). Offered in the quote, not listed on the website.
|
||||
- CMS only as an exception and at extra cost (file-based first; Payload only when the site lives on
|
||||
maintained data such as a product catalogue).
|
||||
- Operations: serve client sites as static files from one shared web server, not one container per client.
|
||||
- Not on the website yet: "green hosting". The owner wants it as an argument; state it only once the
|
||||
provider's proof (renewable energy) is at hand.
|
||||
- Changes: by email or message, no CMS training.
|
||||
- Domain: client is the registrant; transfer-out and source handover on request.
|
||||
- **Email and DNS are out of scope.** Never switch nameservers. Only the two records `A/CNAME` for apex and `www` are changed, after exporting the existing records, and mail delivery is checked afterwards. Mailboxes stay with the client's provider.
|
||||
|
||||
@@ -153,7 +153,7 @@ export const PathPicker: React.FC<{
|
||||
pricing: "offer" | "estimate";
|
||||
}> = ({ ids, pricing }) => {
|
||||
const { services } = useContent();
|
||||
const { estimate } = services;
|
||||
const { estimate, care } = services;
|
||||
const paths = ids.flatMap((id) =>
|
||||
services.paths.filter((path) => path.id === id),
|
||||
);
|
||||
@@ -251,6 +251,53 @@ export const PathPicker: React.FC<{
|
||||
<PathStory path={path} active={selected} />
|
||||
</div>
|
||||
|
||||
{pricing === "offer" && path.id === "website" && (
|
||||
<div className="mt-12 rounded-3xl border border-slate-200 p-6 md:mt-16 md:p-10">
|
||||
<p className={`${MONO} m-0`}>{care.label}</p>
|
||||
<div className="mt-6 grid grid-cols-1 gap-10 md:grid-cols-12 md:gap-12">
|
||||
<div className="md:col-span-7">
|
||||
<p className="m-0 font-sans text-2xl font-bold leading-tight tracking-tight text-slate-900 md:text-4xl">
|
||||
{care.title}
|
||||
</p>
|
||||
<p className="m-0 mt-4 max-w-2xl font-sans text-base leading-relaxed text-slate-500 md:text-lg">
|
||||
{care.text}
|
||||
</p>
|
||||
</div>
|
||||
<div className="md:col-span-5">
|
||||
<p className="m-0 font-sans text-base text-slate-500 md:text-lg">
|
||||
{care.price.label}
|
||||
</p>
|
||||
<p className="m-0 mt-2 font-sans text-3xl font-black tracking-tighter text-slate-900 md:text-5xl">
|
||||
{care.price.amount}
|
||||
</p>
|
||||
<p className={`${MONO} m-0 mt-2`}>{care.price.note}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-10 grid grid-cols-1 gap-10 border-t border-slate-200 pt-8 md:grid-cols-2 md:gap-12">
|
||||
{[
|
||||
{ title: care.includedLabel, items: care.included },
|
||||
{ title: care.termsLabel, items: care.terms },
|
||||
].map((group) => (
|
||||
<div key={group.title}>
|
||||
<p className="m-0 font-sans text-lg font-bold tracking-tight text-slate-900 md:text-xl">
|
||||
{group.title}
|
||||
</p>
|
||||
<ul className="m-0 mt-3 list-none p-0">
|
||||
{group.items.map((item) => (
|
||||
<li
|
||||
key={item}
|
||||
className="border-b border-slate-100 py-3 font-sans text-base leading-relaxed text-slate-600 md:text-lg"
|
||||
>
|
||||
{item}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{pricing === "offer" ? (
|
||||
<div className="mt-12 flex flex-col gap-8 rounded-3xl bg-slate-50 p-6 md:mt-16 md:flex-row md:items-center md:justify-between md:gap-12 md:p-10">
|
||||
<div className="max-w-2xl">
|
||||
|
||||
@@ -78,6 +78,30 @@ export const de: Content = {
|
||||
tablistLabel: "Was brauchen Sie?",
|
||||
youGet: "Das bekommen Sie",
|
||||
howItRuns: "So läuft es ab",
|
||||
care: {
|
||||
label: "Nach dem Start",
|
||||
title: "Ich kümmere mich weiter um Ihre Website, wenn Sie das möchten.",
|
||||
text: "Sie müssen kein System bedienen und nichts selbst pflegen. Wenn etwas zu ändern ist, schreiben Sie mir eine E-Mail.",
|
||||
price: {
|
||||
label: "Betreuung",
|
||||
amount: "89 EUR",
|
||||
note: "im Monat, netto, monatlich kündbar",
|
||||
},
|
||||
includedLabel: "Das ist enthalten",
|
||||
included: [
|
||||
"Die Website läuft auf Servern in Deutschland",
|
||||
"Adresse, Sicherheit und Technik sind erledigt",
|
||||
"Kleine Änderungen: ein Text, ein Bild, neue Öffnungszeiten",
|
||||
"Kein Redaktionssystem, kein Login",
|
||||
],
|
||||
termsLabel: "Gut zu wissen",
|
||||
terms: [
|
||||
"Die ersten drei Monate sind im Projektpreis enthalten.",
|
||||
"Größeres, etwa eine neue Seite, bekommt vorher einen Preis.",
|
||||
"Ich antworte innerhalb von zwei Werktagen. Eine Rufbereitschaft gibt es nicht.",
|
||||
"Wenn Sie gehen möchten, bekommen Sie alles mit. Die Website gehört Ihnen.",
|
||||
],
|
||||
},
|
||||
offer: {
|
||||
title: "Sie bekommen ein Angebot, bevor etwas kostet.",
|
||||
text: "Schreiben Sie mir kurz, was Sie brauchen. Wir sprechen etwa 20 Minuten, danach bekommen Sie ein schriftliches Angebot. Ich fange erst an, wenn Sie zugestimmt haben.",
|
||||
@@ -252,7 +276,7 @@ export const de: Content = {
|
||||
],
|
||||
orientation: "Ab 3.800 EUR, einmalig",
|
||||
orientationNote:
|
||||
"Das gilt für eine typische Unternehmenswebsite. Hosting und kleine Änderungen danach kosten einen monatlichen Betrag, den wir vor dem Start vereinbaren.",
|
||||
"Das gilt für eine typische Unternehmenswebsite. Die Betreuung danach kostet 89 EUR im Monat.",
|
||||
},
|
||||
],
|
||||
scenes: {
|
||||
@@ -769,7 +793,7 @@ export const de: Content = {
|
||||
{
|
||||
question: "Wer kümmert sich danach um die Website?",
|
||||
answer:
|
||||
"Ich, wenn Sie das möchten. Hosting und kleine Änderungen danach kosten einen monatlichen Betrag, den wir vor dem Start vereinbaren. E-Mail-Postfächer bleiben bei Ihrem Anbieter.",
|
||||
"Ich, wenn Sie das möchten. Für 89 EUR im Monat läuft die Website auf Servern in Deutschland, und kleine Änderungen sind enthalten: Sie schreiben mir eine E-Mail. Ein Redaktionssystem brauchen Sie nicht. E-Mail-Postfächer bleiben bei Ihrem Anbieter.",
|
||||
},
|
||||
{
|
||||
question: "Wie erreiche ich Sie?",
|
||||
|
||||
@@ -77,6 +77,30 @@ export const en: Content = {
|
||||
tablistLabel: "What do you need?",
|
||||
youGet: "What you get",
|
||||
howItRuns: "How it runs",
|
||||
care: {
|
||||
label: "After the launch",
|
||||
title: "I keep looking after your website, if you want that.",
|
||||
text: "You do not have to operate a system or maintain anything yourself. When something needs to change, you write me an email.",
|
||||
price: {
|
||||
label: "Care",
|
||||
amount: "89 EUR",
|
||||
note: "per month, net, cancel monthly",
|
||||
},
|
||||
includedLabel: "What is included",
|
||||
included: [
|
||||
"The website runs on servers in Germany",
|
||||
"Address, security and technology are taken care of",
|
||||
"Small changes: a text, a picture, new opening hours",
|
||||
"No editing system, no login",
|
||||
],
|
||||
termsLabel: "Good to know",
|
||||
terms: [
|
||||
"The first three months are included in the project price.",
|
||||
"Anything bigger, such as a new page, gets a price first.",
|
||||
"I reply within two working days. There is no on-call service.",
|
||||
"If you want to leave, you take everything with you. The website is yours.",
|
||||
],
|
||||
},
|
||||
offer: {
|
||||
title: "You get an offer before anything costs money.",
|
||||
text: "Write me briefly what you need. We talk for about 20 minutes, then you get a written offer. I only start once you have agreed.",
|
||||
@@ -248,7 +272,7 @@ export const en: Content = {
|
||||
],
|
||||
orientation: "From 3,800 EUR, one-off",
|
||||
orientationNote:
|
||||
"This is for a typical company website. Hosting and small changes afterwards cost a monthly fee that we agree on before we start.",
|
||||
"This is for a typical company website. Care afterwards costs 89 EUR per month.",
|
||||
},
|
||||
],
|
||||
scenes: {
|
||||
@@ -749,7 +773,7 @@ export const en: Content = {
|
||||
{
|
||||
question: "Who looks after the website afterwards?",
|
||||
answer:
|
||||
"I do, if you want that. Hosting and small changes afterwards cost a monthly fee that we agree on before we start. Email inboxes stay with your provider.",
|
||||
"I do, if you want that. For 89 EUR per month the website runs on servers in Germany, and small changes are included: you write me an email. You do not need an editing system. Email inboxes stay with your provider.",
|
||||
},
|
||||
{
|
||||
question: "How do I reach you?",
|
||||
|
||||
@@ -188,6 +188,18 @@ export type Content = {
|
||||
readonly tablistLabel: string;
|
||||
readonly youGet: string;
|
||||
readonly howItRuns: string;
|
||||
/** What happens to a website after it is live: the care offer, with its price. */
|
||||
readonly care: {
|
||||
readonly label: string;
|
||||
readonly title: string;
|
||||
readonly text: string;
|
||||
readonly price: PathPrice;
|
||||
readonly includedLabel: string;
|
||||
readonly included: readonly string[];
|
||||
readonly termsLabel: string;
|
||||
/** The limits, said plainly. */
|
||||
readonly terms: readonly string[];
|
||||
};
|
||||
/** The homepage's way to a price: ask for an offer. No figures there. */
|
||||
readonly offer: TitledText & { readonly cta: string };
|
||||
/** How a visitor gets to a price: the homepage shows this instead of a price list. */
|
||||
|
||||
Reference in New Issue
Block a user