{
  "kind": "html",
  "title": "October spending: €2,184 of €2,400",
  "html": "<style>\n*{box-sizing:border-box}\nbody{margin:0;background:radial-gradient(120% 60% at 50% 0%,#164336 0%,#0f2e25 60%);background-color:#0f2e25;color:#eaf6f0;font:16px/1.45 system-ui,-apple-system,\"Segoe UI\",Roboto,sans-serif;min-height:100vh}\n.wrap{max-width:520px;margin:0 auto;padding:20px 18px 28px}\n.eyebrow{font-size:13px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:#5ee6a8;margin:0 0 4px}\nh1{font-size:22px;line-height:1.25;margin:0 0 4px;letter-spacing:-.01em}\nh1.done{color:#5ee6a8}\n.sub{color:#a9c7bb;font-size:15px;margin:0 0 18px}\n.chart{position:relative;width:212px;height:212px;margin:0 auto 20px}\n.chart svg{width:100%;height:100%;transform:rotate(-90deg)}\n.chart circle{transition:stroke-dasharray .2s,stroke-dashoffset .2s}\n.mid{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center}\n.mid b{font-size:32px;font-weight:700;letter-spacing:-.02em;font-variant-numeric:tabular-nums}\n.mid span{font-size:14px;color:#a9c7bb;font-variant-numeric:tabular-nums}\n.mid em{font-style:normal;font-size:13px;font-weight:600;color:#5ee6a8;margin-top:4px;font-variant-numeric:tabular-nums}\n.list{background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.07);border-radius:18px;padding:4px 16px;margin-bottom:18px}\n.row{padding:12px 0;border-bottom:1px solid rgba(255,255,255,.07)}\n.row:last-child{border-bottom:0}\n.rh{display:flex;align-items:center;gap:10px;margin-bottom:8px}\n.dot{width:10px;height:10px;border-radius:50%;flex:none}\n.nm{flex:1;font-weight:600;font-size:15px}\n.amt{font-size:14px;color:#a9c7bb;font-variant-numeric:tabular-nums}\n.amt b{color:#eaf6f0;font-weight:600}\n.amt i{font-style:normal;font-weight:700;color:#5ee6a8;margin-left:6px}\n.amt i:empty{display:none}\n.over .amt b,.flag{color:#ff7a6b}\n.flag{display:block;font-size:13px;font-weight:600;margin-top:6px}\n.flag:empty{display:none}\n.meter{height:6px;border-radius:3px;background:rgba(255,255,255,.08);overflow:hidden}\n.fill{height:100%;border-radius:3px;transition:width .2s,background-color .2s}\n.ask{background:rgba(94,230,168,.08);border:1px solid rgba(94,230,168,.22);border-radius:16px;padding:14px 16px;margin-bottom:12px;font-size:15px;color:#cfe8dd}\n.ask b{color:#eaf6f0}\n.chips{display:flex;flex-direction:column;gap:8px;margin-bottom:18px}\n.chip{display:flex;align-items:center;gap:10px;width:100%;min-height:52px;font:inherit;font-size:15px;text-align:left;color:#eaf6f0;background:rgba(255,255,255,.04);border:1.5px solid rgba(255,255,255,.12);border-radius:14px;padding:10px 14px;cursor:pointer;transition:background-color .18s,border-color .18s,transform .15s}\n.chip:active,.main:active{transform:scale(.98)}\n.chip .r{width:20px;height:20px;border-radius:50%;border:2px solid #6f9586;flex:none;transition:border .18s}\n.chip .t{flex:1}\n.chip small{font-size:12px;font-weight:700;color:#0f2e25;background:#5ee6a8;border-radius:20px;padding:2px 8px}\n.chip.on{border-color:#5ee6a8;background:rgba(94,230,168,.12)}\n.chip.on .r{border:6px solid #5ee6a8}\n.main{width:100%;min-height:54px;font:inherit;font-weight:700;font-size:16px;border:0;border-radius:16px;background:#5ee6a8;color:#0b2a20;cursor:pointer;box-shadow:0 8px 24px rgba(94,230,168,.22);transition:transform .15s,opacity .2s}\n.main:disabled,.chip:disabled{opacity:.55;cursor:default}\n.status{text-align:center;font-size:14px;color:#a9c7bb;min-height:20px;margin:10px 0 0}\n</style>\n<div class=\"wrap\">\n  <p class=\"eyebrow\" id=\"eyebrow\"></p>\n  <h1 id=\"title\"></h1>\n  <p class=\"sub\" id=\"sub\"></p>\n  <div class=\"chart\"><svg viewBox=\"0 0 120 120\" id=\"donut\"><circle cx=\"60\" cy=\"60\" r=\"50\" fill=\"none\" stroke=\"rgba(255,255,255,.06)\" stroke-width=\"12\"/></svg>\n    <div class=\"mid\"><b id=\"spent\"></b><span id=\"of\"></span><em id=\"left\"></em></div></div>\n  <div class=\"list\" id=\"list\"></div>\n  <div class=\"ask\" id=\"ask\"></div>\n  <div class=\"chips\" id=\"chips\"></div>\n  <button class=\"main\" id=\"go\"></button>\n  <p class=\"status\" id=\"status\" role=\"status\"></p>\n</div>\n<script>\n// The card's content. Change only this object.\n// answer.data: {\"action\":\"move\"|\"raise\"|\"leave\",\"from\":\"eating_out\",\"to\":\"groceries\",\"amount_eur\":50}\nvar CARD = {\n  eyebrow: 'October budget',\n  title: 'October spending: €2,184 of €2,400',\n  sub: 'Six days left. Groceries went over; everything else is on track.',\n  currency: '€',\n  categories: [\n    {id: 'home', name: 'Home & bills', spent: 1120, budget: 1150, color: '#7fb7d9'},\n    {id: 'groceries', name: 'Groceries', spent: 466, budget: 420, color: '#e8c468'},\n    {id: 'eating_out', name: 'Eating out', spent: 148, budget: 240, color: '#c49cf0'},\n    {id: 'transport', name: 'Transport', spent: 162, budget: 200, color: '#6fd3c7'},\n    {id: 'fun', name: 'Fun & hobbies', spent: 178, budget: 230, color: '#f2a6c0'},\n    {id: 'health', name: 'Health', spent: 110, budget: 160, color: '#a7cf8c'}\n  ],\n  over_label: 'over budget so far',\n  of_label: 'of',\n  left_label: 'left',\n  suggestion: 'Groceries is €46 over. Eating out still has €92 spare, so I suggest moving €50 across.',\n  from: 'eating_out', to: 'groceries', amount: 50, raise_to: 470,\n  choices: [\n    {action: 'move', label: 'Move €50 from Eating out', badge: 'My pick', button: 'Apply · move €50'},\n    {action: 'raise', label: 'Raise Groceries budget to €470', button: 'Apply · raise to €470'},\n    {action: 'leave', label: 'Leave it as it is', button: 'Leave budgets as they are'}\n  ],\n  sending: 'Sending…',\n  preview: 'Preview only: open this card in Bridgit to answer.',\n  done: 'Budget updated for October'\n};\n\nvar choice = CARD.choices[0], sent = false, C = 2 * Math.PI * 50;\nfunction $(id) { return document.getElementById(id); }\nfunction eur(v) { return CARD.currency + v.toLocaleString('en-GB'); }\n$('eyebrow').textContent = CARD.eyebrow;\n$('title').textContent = CARD.title;\n$('sub').textContent = CARD.sub;\n$('ask').textContent = CARD.suggestion;\n\n// Donut\nvar svg = $('donut'), NS = 'http://www.w3.org/2000/svg';\nvar total = CARD.categories.reduce(function (a, c) { return a + c.spent; }, 0), off = 0, gap = 2.2;\nCARD.categories.forEach(function (c) {\n  var len = C * c.spent / total, el = document.createElementNS(NS, 'circle');\n  el.setAttribute('cx', 60); el.setAttribute('cy', 60); el.setAttribute('r', 50);\n  el.setAttribute('fill', 'none'); el.setAttribute('stroke', c.color); el.setAttribute('stroke-width', 12);\n  el.setAttribute('stroke-dasharray', Math.max(len - gap, 0.5) + ' ' + C);\n  el.setAttribute('stroke-dashoffset', -off);\n  svg.appendChild(el); off += len;\n});\n\n// Rows\nvar rows = {};\nCARD.categories.forEach(function (c) {\n  var r = document.createElement('div'); r.className = 'row';\n  var h = document.createElement('div'); h.className = 'rh';\n  var d = document.createElement('span'); d.className = 'dot'; d.style.background = c.color;\n  var n = document.createElement('span'); n.className = 'nm'; n.textContent = c.name;\n  var f = document.createElement('span'); f.className = 'flag';\n  var a = document.createElement('span'); a.className = 'amt';\n  var b = document.createElement('b'); var s = document.createElement('span'); var t = document.createElement('i');\n  a.appendChild(b); a.appendChild(s); a.appendChild(t); h.appendChild(d); h.appendChild(n); h.appendChild(a);\n  var m = document.createElement('div'); m.className = 'meter';\n  var fl = document.createElement('div'); fl.className = 'fill'; m.appendChild(fl);\n  r.appendChild(h); r.appendChild(m); r.appendChild(f); $('list').appendChild(r);\n  rows[c.id] = {row: r, b: b, s: s, t: t, f: f, fill: fl, c: c};\n});\n\nfunction budgetOf(c) {\n  if (choice.action === 'move' && c.id === CARD.from) return c.budget - CARD.amount;\n  if (choice.action === 'move' && c.id === CARD.to) return c.budget + CARD.amount;\n  if (choice.action === 'raise' && c.id === CARD.to) return CARD.raise_to;\n  return c.budget;\n}\nfunction render() {\n  var budget = 0;\n  CARD.categories.forEach(function (c) {\n    var R = rows[c.id], bud = budgetOf(c), over = c.spent > c.budget;\n    budget += bud;\n    R.b.textContent = eur(c.spent); R.s.textContent = ' / ' + eur(c.budget);\n    R.t.textContent = bud !== c.budget ? '→ ' + eur(bud) : '';\n    R.f.textContent = over ? eur(c.spent - c.budget) + ' ' + CARD.over_label : '';\n    R.row.classList.toggle('over', over);\n    R.fill.style.width = Math.min(100, c.spent / c.budget * 100) + '%';\n    R.fill.style.backgroundColor = over ? '#ff7a6b' : c.color;\n  });\n  $('spent').textContent = eur(total);\n  $('of').textContent = CARD.of_label + ' ' + eur(budget);\n  $('left').textContent = eur(budget - total) + ' ' + CARD.left_label;\n  $('go').textContent = choice.button;\n}\n\n// Choices\nCARD.choices.forEach(function (ch, i) {\n  var b = document.createElement('button'); b.className = 'chip' + (i === 0 ? ' on' : '');\n  var r = document.createElement('span'); r.className = 'r';\n  var t = document.createElement('span'); t.className = 't'; t.textContent = ch.label;\n  b.appendChild(r); b.appendChild(t);\n  if (ch.badge) { var s = document.createElement('small'); s.textContent = ch.badge; b.appendChild(s); }\n  b.onclick = function () {\n    if (sent) return; choice = ch;\n    document.querySelectorAll('.chip').forEach(function (x) { x.classList.remove('on'); });\n    b.classList.add('on'); render();\n  };\n  $('chips').appendChild(b);\n});\n\n$('go').onclick = function () {\n  if (sent) return;\n  var B = window.Bridgit || window.Bridget;\n  if (!B) { $('status').textContent = CARD.preview; return; }\n  var data = {action: choice.action};\n  if (choice.action === 'move') { data.from = CARD.from; data.to = CARD.to; data.amount_eur = CARD.amount; }\n  if (choice.action === 'raise') { data.category = CARD.to; data.new_budget_eur = CARD.raise_to; }\n  sent = true;\n  document.querySelectorAll('button').forEach(function (x) { x.disabled = true; });\n  $('status').textContent = CARD.sending;\n  B.respond(JSON.stringify(data));\n};\ndocument.addEventListener('bridgit:answered', function (e) {\n  e.preventDefault();\n  $('title').textContent = '✓ ' + CARD.done; $('title').classList.add('done');\n  $('status').textContent = '';\n  setTimeout(function () { $('title').scrollIntoView({block: 'center', behavior: 'smooth'}); }, 400);\n});\nrender();\n</script>\n",
  "expires_in_seconds": 86400
}
