{
  "spec": "quick-html/v0",
  "id": "savings-plan",
  "version": "0.1.0",
  "name": "Savings Plan",
  "description": "Multi-year savings plan on one A4 page: per goal the target, the amount saved, the deadline and the required monthly contribution, shown on a progress bar with 25/50/75% milestone ticks, plus summary tiles, a contribution schedule and saving rules.",
  "useWhen": [
    "savings plan",
    "saving for a goal",
    "savings goals tracker",
    "emergency fund plan",
    "how much to save per month",
    "spaarplan",
    "spaardoelen",
    "sparplan",
    "plan d'épargne",
    "plan de ahorro"
  ],
  "format": "printable",
  "theme": "personal-finance",
  "tags": [
    "saving",
    "goals",
    "milestones",
    "tracker",
    "money"
  ],
  "language": "en",
  "page": {
    "size": "A4",
    "orientation": "portrait"
  },
  "modules": [
    "edit-overlay",
    "print-helper"
  ],
  "slots": [
    {
      "name": "doc-title",
      "type": "text",
      "required": true,
      "example": "Savings plan 2026–2028 — Bosma & Veenstra",
      "notes": "Browser tab title; used as the download filename."
    },
    {
      "name": "plan-title",
      "type": "text",
      "required": true,
      "example": "Savings plan 2026 – 2028",
      "notes": "Masthead heading; keep under ~34 characters so it stays on one line."
    },
    {
      "name": "owner-line",
      "type": "text",
      "required": true,
      "example": "Femke Bosma & Joris Veenstra · Groningen · joint account NL24 RABO 0388 1147 62",
      "notes": "Who the plan belongs to; optionally city and the account the pots live in. One line, under ~90 characters."
    },
    {
      "name": "plan-date",
      "type": "date",
      "required": true,
      "example": "19 Aug 2026",
      "notes": "The day the plan was drawn up or last reviewed. Format '19 Aug 2026'."
    },
    {
      "name": "total-target",
      "type": "text",
      "required": true,
      "example": "€ 35.100,00",
      "notes": "Sum of every goal-target. Must equal the goal rows exactly."
    },
    {
      "name": "total-saved",
      "type": "text",
      "required": true,
      "example": "€ 15.450,00",
      "notes": "Sum of every goal-saved. Must equal the goal rows exactly."
    },
    {
      "name": "total-monthly",
      "type": "text",
      "required": true,
      "example": "€ 1.725,00",
      "notes": "Sum of every goal-monthly — the total required each month."
    },
    {
      "name": "schedule-note",
      "type": "text",
      "required": false,
      "example": "Running total starts from the € 15.450,00 already saved. December includes the year-end bonus; nothing leaves the pots before Mar 2027.",
      "notes": "Caption under the schedule table: what the running total counts from, and any month that is not a normal month. Inside the schedule optional."
    },
    {
      "name": "rules-content",
      "type": "html",
      "required": false,
      "example": "<ul><li>Move the money on the 25th, the day after salary — € 480 to the emergency pot first.</li><li>Windfalls split 50/50 between the kitchen and the trip.</li></ul>",
      "notes": "One <ul> with 3–5 short <li> rules the household has agreed. Inside the rules optional."
    },
    {
      "name": "footer-line",
      "type": "text",
      "required": false,
      "example": "Agreed together on 19 Aug 2026 · next review 24 Nov 2026"
    }
  ],
  "repeats": [
    {
      "name": "goal",
      "description": "One card per savings goal: name, deadline, money figures, milestone progress bar and a note.",
      "min": 1,
      "max": 6,
      "slots": [
        {
          "name": "goal-name",
          "type": "text",
          "required": true,
          "example": "Emergency fund",
          "notes": "Short and concrete; keep under ~34 characters."
        },
        {
          "name": "goal-deadline",
          "type": "text",
          "required": true,
          "example": "Jun 2027",
          "notes": "Month the goal must be funded. Printed after a static 'by', so write only 'Jun 2027'."
        },
        {
          "name": "goal-saved",
          "type": "text",
          "required": true,
          "example": "€ 7.200,00",
          "notes": "Amount in this pot today."
        },
        {
          "name": "goal-target",
          "type": "text",
          "required": true,
          "example": "€ 12.000,00",
          "notes": "Amount the goal needs in total."
        },
        {
          "name": "goal-monthly",
          "type": "text",
          "required": true,
          "example": "€ 480,00",
          "notes": "Required monthly contribution: (target − saved) ÷ months to the deadline, rounded up. Use '€ 0,00' for a goal already reached."
        },
        {
          "name": "goal-percent",
          "type": "text",
          "required": true,
          "example": "60%",
          "notes": "saved ÷ target as a whole percentage with a '%' sign. Must match goal-bar-style."
        },
        {
          "name": "goal-bar-style",
          "type": "text",
          "required": true,
          "example": "width:60%",
          "notes": "Attribute slot on the bar fill. Exactly 'width:NN%', NN a whole number clamped to 0–100, equal to goal-percent."
        },
        {
          "name": "goal-bar-class",
          "type": "text",
          "required": true,
          "example": "fill fill-ontrack",
          "notes": "Attribute slot on the bar fill. Exactly one of 'fill fill-ontrack', 'fill fill-behind', 'fill fill-done'."
        },
        {
          "name": "goal-note",
          "type": "text",
          "required": false,
          "example": "Three months of fixed costs. This pot fills first — nothing else is topped up while it is short.",
          "notes": "One or two sentences: why the goal exists, or why it is behind and what would fix it. Under ~200 characters."
        }
      ]
    },
    {
      "name": "contribution",
      "description": "One row per month in the contribution schedule; lives inside the schedule optional, so use 0 rows when that block is dropped.",
      "min": 0,
      "max": 12,
      "slots": [
        {
          "name": "month-label",
          "type": "text",
          "required": true,
          "example": "Sep 2026",
          "notes": "Month of the transfer, format 'Sep 2026'."
        },
        {
          "name": "month-amount",
          "type": "text",
          "required": true,
          "example": "€ 1.725,00",
          "notes": "Total moved into the pots that month, across all goals."
        },
        {
          "name": "month-running-total",
          "type": "text",
          "required": true,
          "example": "€ 17.175,00",
          "notes": "Previous running total + month-amount; the first row starts from total-saved."
        }
      ]
    }
  ],
  "optionals": [
    {
      "name": "schedule",
      "description": "Month-by-month contribution table with running totals; drop it when the plan only needs the goals."
    },
    {
      "name": "rules",
      "description": "The household's own saving rules (transfer day, order of the pots, what happens to windfalls)."
    }
  ],
  "changelog": [
    {
      "version": "0.1.0",
      "date": "2026-08-19",
      "notes": "Initial release: A4 savings plan with milestone progress bars per goal, summary tiles, optional contribution schedule and saving rules."
    }
  ],
  "agentInstructions": "One 'goal' card per goal; delete the data-example cards/rows. RE-CHECK EVERY DERIVED FIGURE. goal-percent = saved ÷ target, rounded to a whole number. goal-monthly = (target − saved) ÷ months from now to the deadline, rounded up ('€ 0,00' once reached). The three tiles must equal the goal-row sums: total-target, total-saved, total-monthly. goal-bar-style is exactly 'width:NN%', NN the same whole number, clamped 0–100. goal-bar-class is exactly one of 'fill fill-ontrack', 'fill fill-behind', 'fill fill-done': fill-done once saved reaches target, fill-behind when the monthly amount actually set aside misses the deadline, otherwise fill-ontrack. If a goal is unreachable at the real contribution, say so in goal-note with the month it would land instead, never round to fit. Money exactly like the placeholders ('€ 1.234,56', two decimals), months as 'Jun 2027', dates as '19 Aug 2026'. Each month-running-total = the previous one + month-amount, starting from total-saved. Drop the schedule and/or rules block when not wanted — with five or six goals drop the schedule, or the sheet runs onto a second page. Unknown balances: leave [PLACEHOLDER] and say to fill them via the built-in Edit button. Never give financial advice — this is a planning sheet, not a recommendation.",
  "license": "MIT",
  "authors": [
    {
      "github": "bvrln"
    }
  ]
}
