A one page proposal template: four blocks, filled by AI, sent as a link

A one page proposal answers four questions in order: what is wrong, what you will do, what it costs, and what the client does next. Build it as a web page and send the address.

A one page proposal template is four blocks in a fixed order: the problem, your offer, the price, and the next step. Everything else is optional. Build it as one web page and send the address, not a file.

The part that eats time is usually the look. One freelancer wrote that after buying a Canva template they "spent an embarrassing amount of time on something I hoped looked polished enough to send." A plain page with four blocks needs no template.

Here is a finished one. It is a single column that reads on a phone. Edit the numbers at the top of the script and it reruns.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Proposal - Newsletter setup for Fernhill Yoga Studio</title>
<style>
  * { box-sizing: border-box; }
  body { margin: 0; background: #f3f4f6; font-family: system-ui, -apple-system, "Segoe UI", sans-serif; color: #1f2937; line-height: 1.55; }
  .page { max-width: 680px; margin: 0 auto; background: #fff; padding: 24px 20px 30px; min-height: 100vh; }
  .tag { margin: 0; font-size: 13px; letter-spacing: .06em; text-transform: uppercase; color: #6b7280; }
  h1 { margin: 6px 0 4px; font-size: 26px; line-height: 1.2; }
  .meta { margin: 0 0 6px; font-size: 14px; color: #6b7280; }
  h2 { display: flex; align-items: center; gap: 10px; margin: 24px 0 8px; font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: #6b7280; }
  h2 i { font-style: normal; display: inline-grid; place-items: center; width: 24px; height: 24px; border-radius: 50%; background: #1d4ed8; color: #fff; font-size: 13px; letter-spacing: 0; }
  p { margin: 0 0 8px; font-size: 16px; }
  ul { margin: 0; padding-left: 20px; }
  li { margin-bottom: 4px; font-size: 16px; }
  .price { background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 12px; padding: 14px 16px; }
  .price b { display: block; font-size: 28px; color: #1d4ed8; line-height: 1.2; }
  .price span { font-size: 15px; color: #374151; }
  .pay { display: flex; justify-content: space-between; gap: 12px; margin-top: 10px; font-size: 15px; }
  .pay div { flex: 1; padding: 8px 10px; background: #f9fafb; border-radius: 8px; }
  .pay b { display: block; font-size: 18px; }
  .small { font-size: 14px; color: #6b7280; }
  .next { border: 2px solid #1d4ed8; border-radius: 12px; padding: 14px 16px; }
  .next .ask { font-size: 18px; font-weight: 700; margin-bottom: 8px; }
  .steps { list-style: none; padding: 0; margin: 0; }
  .steps li { display: flex; gap: 12px; padding: 6px 0; margin: 0; border-top: 1px solid #eef0f3; font-size: 15px; }
  .steps b { min-width: 64px; color: #1d4ed8; }
</style>
</head>
<body>
<div class="page">
  <p class="tag">Proposal</p>
  <h1>Newsletter setup for Fernhill Yoga Studio</h1>
  <p class="meta">For Noor Haldane · from Jonas Lind · valid until <span id="valid"></span></p>

  <h2><i>1</i>The problem</h2>
  <p>Class changes are posted on the studio's front door and in three chat groups. Members miss them, and you answer the same "is Thursday on?" message every week.</p>

  <h2><i>2</i>My offer</h2>
  <ul>
    <li>One email template in the studio's colours, set up in your mailing tool.</li>
    <li>Your 140 current members imported and tidied.</li>
    <li>A one-hour call showing you how to send an update in five minutes.</li>
  </ul>
  <p class="small">Ready within 10 days of your yes.</p>

  <h2><i>3</i>The price</h2>
  <div class="price">
    <b id="price"></b>
    <span>Fixed. Covers everything in block 2.</span>
    <div class="pay">
      <div><span>Deposit (<span id="dep-pct"></span>)</span><b id="dep"></b></div>
      <div><span>On handover</span><b id="rest"></b></div>
    </div>
  </div>
  <p class="small" style="margin-top:8px">Not included: the mailing tool's own subscription, and copywriting for each update.</p>

  <h2><i>4</i>The next step</h2>
  <div class="next">
    <p class="ask">Reply "yes" to this email by <span id="reply-by"></span>.</p>
    <ul class="steps">
      <li><b>You</b><span>Send "yes" and the member list.</span></li>
      <li><b>Me</b><span>Send the deposit invoice the same day.</span></li>
      <li><b id="start"></b><span>I start. First draft within a week.</span></li>
    </ul>
    <p class="small" style="margin:8px 0 0">Questions first? jonas@example.com</p>
  </div>
</div>

<script>
  // ---- Change the numbers and dates here. The whole page follows. ----
  const PRICE = 780;
  const DEPOSIT_PERCENT = 40;
  const VALID_UNTIL = '31 Oct 2026';
  const REPLY_BY = '23 Oct 2026';
  const START = '26 Oct';
  // ---------------------------------------------------------------------

  const money = (n) => '$' + n.toLocaleString('en-US');
  const deposit = Math.round(PRICE * DEPOSIT_PERCENT / 100);

  document.getElementById('price').textContent = money(PRICE);
  document.getElementById('dep-pct').textContent = DEPOSIT_PERCENT + '%';
  document.getElementById('dep').textContent = money(deposit);
  document.getElementById('rest').textContent = money(PRICE - deposit);
  document.getElementById('valid').textContent = VALID_UNTIL;
  document.getElementById('reply-by').textContent = REPLY_BY;
  document.getElementById('start').textContent = START;
</script>
</body>
</html>
One page, four blocks. Change the price, deposit and dates in the script and every figure follows.

The four blocks, in order

Each block answers one question the client has while reading.

The four blocks and the one question each one answers.
The four blocks and the one question each one answers.
  1. The problem – what is going wrong for them now, in their words. Two sentences.
  2. My offer – what you will deliver, as three short lines, and by when.
  3. The price – one number, what it covers, and what is left out. Add the deposit if you take one.
  4. The next step – one action and a reply-by date, so the client does not have to decide what to do.

The next step is the block people skip. Without it the page ends with a price and a silence. With it, the client has a sentence to answer: "Reply yes by 23 Oct."

One freelancer who writes about proposals says theirs are usually a one-page Google Doc or a 5-7 slide deck. This is the one-page kind, as a web page instead of a document.

What to cut to stay on one page

A one page proposal is short because you left things out, not because the text is small. These are the usual cuts.

  • About me – one line. A link to your work is enough.
  • Process – the timeline fits in the offer block, as one date.
  • Case studies – one sentence, or nothing.
  • Legal terms – leave them for the contract, sent after the yes.
  • A long price table – one number. If the client only asked for a number, send a quote instead.

For the longer version, with background and sections for people who were not on the call, see the proposal link guide.

Make it with AI

Paste this prompt into ChatGPT, Claude or Gemini. Put your own facts at the bottom.

Write ONE self-contained HTML file: a one page freelance proposal, to be read on a phone.
Rules:
- Plain HTML, CSS and JavaScript in one file. No images, no external files, no form that sends data.
- One column. Add a viewport meta tag. Text at least 16px.
- Exactly four blocks, numbered 1 to 4: The problem, My offer, The price, The next step.
- The price block shows the price, the deposit and the balance, and one line about what is not included.
- The next step block asks for ONE action with a reply-by date, then lists who does what.
- Put every number and date (price, deposit percent, valid-until, reply-by, start date) in one block at the top of the script, with the comment "Change the numbers here". Work out the deposit and balance in the script.
- Show my email address as plain text, not as a form.
- If I did not give you a fact, write TODO. Do not invent clients, past work or prices.
My facts:
Client and project: ...
Their problem: ...
What I will deliver and by when: ...
Price, deposit percent, what is not included: ...
Reply-by date and start date: ...
My name and email: ...

Asking for every number in one block means a later price change is one edit, not a hunt through the page.

What to check in the result

An AI is good at layout and loose with facts. Read the page the way the client will.

  • Every number. Price, deposit, balance, dates. Compare them with your notes.
  • Every TODO. Replace it or delete the line.
  • Invented details. Past clients, results and promises you never gave it.
  • The next step. One action, one date. Not two.
  • Your phone. Open the page on it before the client does.

If the page looks wrong, fixing AI-generated HTML covers the usual causes. For a shorter general prompt, see a prompt for an HTML one-pager.

From AI chat to a link, in four steps.
From AI chat to a link, in four steps.

Paste the HTML into a NOS document and choose Create share link. HTML to link walks through it.

The result is a page anyone with the link can open in a browser, on a phone too, with no account. They can read it but not edit it. Scripts run on the page, so a button or a total works for them.

Send the link in the body of your email, with one line saying what it is. Write your email address on the page as plain text. A form that sends data to a server is blocked on a share link.

If you change the code later, the same link shows the new version. Add a short dated line such as "Price updated 12 Oct" so the client knows what moved.

If you want to offer two prices, the second example does it with two buttons. The client taps an option and the offer, price and reply line change. Their tap is theirs alone: it is not saved, so ask them to reply with the letter.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Proposal with two options - Fernhill Yoga Studio</title>
<style>
  * { box-sizing: border-box; }
  body { margin: 0; background: #f3f4f6; font-family: system-ui, -apple-system, "Segoe UI", sans-serif; color: #1f2937; line-height: 1.55; }
  .page { max-width: 680px; margin: 0 auto; background: #fff; padding: 24px 20px 30px; min-height: 100vh; }
  .tag { margin: 0; font-size: 13px; letter-spacing: .06em; text-transform: uppercase; color: #6b7280; }
  h1 { margin: 6px 0 4px; font-size: 26px; line-height: 1.2; }
  .meta { margin: 0 0 14px; font-size: 14px; color: #6b7280; }
  h2 { margin: 22px 0 8px; font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: #6b7280; }
  p { margin: 0 0 8px; font-size: 16px; }
  ul { margin: 0; padding-left: 20px; }
  li { margin-bottom: 4px; font-size: 16px; }
  .pick { display: flex; gap: 10px; }
  .pick button { flex: 1; font: inherit; text-align: left; padding: 12px 14px; border: 2px solid #d1d5db; border-radius: 12px; background: #fff; cursor: pointer; min-height: 44px; }
  .pick button b { display: block; font-size: 16px; }
  .pick button span { font-size: 14px; color: #6b7280; }
  .pick button[aria-pressed="true"] { border-color: #1d4ed8; background: #eff6ff; }
  .price { margin-top: 14px; background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 12px; padding: 14px 16px; }
  .price b { display: block; font-size: 28px; color: #1d4ed8; line-height: 1.2; }
  .price span { font-size: 15px; color: #374151; }
  .small { font-size: 14px; color: #6b7280; }
  .next { border: 2px solid #1d4ed8; border-radius: 12px; padding: 14px 16px; font-size: 18px; font-weight: 700; }
  @media (max-width: 420px) { .pick { flex-direction: column; } }
</style>
</head>
<body>
<div class="page">
  <p class="tag">Proposal · two options</p>
  <h1>Newsletter for Fernhill Yoga Studio</h1>
  <p class="meta">For Noor Haldane · from Jonas Lind · valid until 31 Oct 2026</p>

  <h2>The problem</h2>
  <p>Class changes live on the front door and in three chat groups. Members miss them.</p>

  <h2>Pick an option</h2>
  <div class="pick" id="pick"></div>

  <h2>What you get</h2>
  <ul id="offer"></ul>

  <h2>The price</h2>
  <div class="price"><b id="price"></b><span id="price-note"></span></div>
  <p class="small" style="margin-top:8px">Not included: the mailing tool's own subscription.</p>

  <h2>The next step</h2>
  <div class="next" id="next"></div>
</div>

<script>
  // ---- Change the options here. Tap an option to see the page follow. ----
  const OPTIONS = {
    A: {
      name: 'Setup only', blurb: 'You send the updates',
      price: 780, note: 'Fixed. One payment on handover.',
      offer: ['One email template in the studio\'s colours.', 'Your 140 members imported and tidied.', 'A one-hour call on sending an update in five minutes.']
    },
    B: {
      name: 'Setup + 3 months', blurb: 'I send the updates',
      price: 1350, note: 'Fixed. Setup now, then three monthly payments.',
      offer: ['Everything in option A.', 'I send your weekly class update for three months.', 'A short monthly list of what went out.']
    }
  };
  const REPLY_BY = '23 Oct 2026';
  // ------------------------------------------------------------------------

  const money = (n) => '$' + n.toLocaleString('en-US');
  const pick = document.getElementById('pick');

  for (const key in OPTIONS) {
    const btn = document.createElement('button');
    btn.type = 'button';
    btn.dataset.key = key;
    btn.innerHTML = '<b>Option ' + key + '</b><span>' + OPTIONS[key].name + ' · ' + OPTIONS[key].blurb + '</span>';
    pick.appendChild(btn);
  }

  function show(key) {
    const o = OPTIONS[key];
    pick.querySelectorAll('button').forEach((b) => b.setAttribute('aria-pressed', b.dataset.key === key));
    document.getElementById('offer').innerHTML = o.offer.map((t) => '<li>' + t + '</li>').join('');
    document.getElementById('price').textContent = money(o.price);
    document.getElementById('price-note').textContent = o.note;
    document.getElementById('next').textContent = 'Reply "' + key + '" to this email by ' + REPLY_BY + '. I start the next working day.';
  }

  pick.addEventListener('click', (e) => {
    const b = e.target.closest('button');
    if (b) show(b.dataset.key);
  });
  show('A');
</script>
</body>
</html>
Two options on one page. The client taps A or B. You edit the options in the script.

Ways to send a one page proposal, compared

An attached file against a page sent as a link.
An attached file against a page sent as a link.
Attached file or PDF Text in the email Link to a page
Opens on a phone After a download Yes Yes, in the browser
Change after sending Send a new file Send a new email Edit the page, same link
Two options or a live total No No Yes
Client needs an account No No No
Forwarded inside the client Many copies Pasted text One address

For personal Gmail accounts, Google's help page says the attachment limit is 25 MB, and Gmail adds a larger file as a Drive link (as of October 2026). Work and school accounts have limits set by an administrator.

A one page proposal is far below any limit, so size is rarely the problem. Changing it after you press send is. See email vs link for documents, or sending a proposal to a client for the wider picture.

When it does not work

What you see Cause Fix
The page shows raw code in the chat You copied the chat text, not the code block Copy only the code, from the first doctype line to the last html tag
A logo or picture is missing Images from other sites are blocked Ask for no images, or a logo drawn in SVG
The deposit does not match the price The AI typed the numbers instead of working them out Ask: "Work out the deposit and balance in the script"
An option button does nothing The AI left the script out or broke it Ask: "Make the buttons switch the offer and price"
A contact form does nothing Forms cannot send to a server from the link Write your email address as plain text
The client sees an old price They are reading an old file Send only the link, never the file
The text is tiny on a phone No viewport meta tag Ask for one, and a 16px base size

Questions people ask

What goes on a one page proposal?

Four blocks: the problem in the client's words, your offer with a delivery date, the price with what is left out, and one next step with a reply-by date. Anything that does not answer one of those four questions can be cut or moved to a second link.

Is one page enough for a freelance project?

One freelancer who writes about proposals says theirs are usually a one-page Google Doc or a 5-7 slide deck. For a small, clear project one page is often enough. For a larger one, keep the one page as the summary and link to the detail.

Do I need a Canva or Word template?

No. The order of the four blocks is the template. An AI can write the page in one file, and you spend your time on the facts and the number instead of the layout.

Can I offer two prices on one page?

Yes. The second example on this page shows two options as two buttons. The client taps one and sees that option's offer, price and reply line. Their choice is not saved and you do not see it, so ask them to reply with the option letter.

How do I change the price after sending the link?

Open the page in NOS, change the number in the block at the top of the script, and save. The same link then shows the new price. Add a dated line saying what changed.

Keep reading