Design a change-plan panel with Tailwind CSS for an in-app billing settings page. Use a max-w-2xl card. The header states "Change plan" with a muted line naming the current plan and renewal date. Below, three selectable plan rows as radio-style cards: each shows the plan name, a short line of positioning copy, the price on the right, and the current plan carries a "Current" pill while the selected one gets a neutral-900 ring and a filled radio dot. Under the list, a neutral-50 breakdown card titled "Due today" listing the new plan charge for the remaining period, an unused-time credit as a negative green line, and a bold total, followed by a muted line explaining the next full charge and its date. Close with a right-aligned pair of buttons, Cancel and a dark Confirm upgrade. Then add JavaScript that switches the selection styling and recalculates the proration figures from the days left in the billing period.
Paste it into Claude, Cursor, v0 or any AI coding tool. Prefer the
finished markup? .
Design a change-plan panel with Tailwind CSS for an in-app billing settings page. Use a max-w-2xl card. The header states "Change plan" with a muted line naming the current plan and renewal date. Below, three selectable plan rows as radio-style cards: each shows the plan name, a short line of positioning copy, the price on the right, and the current plan carries a "Current" pill while the selected one gets a neutral-900 ring and a filled radio dot. Under the list, a neutral-50 breakdown card titled "Due today" listing the new plan charge for the remaining period, an unused-time credit as a negative green line, and a bold total, followed by a muted line explaining the next full charge and its date. Close with a right-aligned pair of buttons, Cancel and a dark Confirm upgrade. Then add JavaScript that switches the selection styling and recalculates the proration figures from the days left in the billing period.
var DAYS_LEFT = 18;
var CYCLE = 30;
var CURRENT = 19;
var rows = Array.prototype.slice.call(document.querySelectorAll(".plan-row"));
var label = document.getElementById("prorateLabel");
var charge = document.getElementById("prorateCharge");
var credit = document.getElementById("prorateCredit");
var total = document.getElementById("prorateTotal");
var next = document.getElementById("prorateNext");
function money(n) {
return "$" + n.toFixed(2);
}
function select(row) {
rows.forEach(function (r) {
var on = r === row;
r.classList.toggle("border-neutral-900", on);
r.classList.toggle("ring-1", on);
r.classList.toggle("ring-neutral-900", on);
r.querySelector(".dot").classList.toggle("border-neutral-900", on);
r.querySelector(".dot span").classList.toggle("opacity-0", !on);
});
var price = Number(row.dataset.price);
var share = DAYS_LEFT / CYCLE;
var newCharge = price * share;
var unused = CURRENT * share;
label.textContent = row.querySelector("span span").textContent.trim() + ", " + DAYS_LEFT + " days remaining";
charge.textContent = money(newCharge);
credit.textContent = "−" + money(unused);
// Downgrades leave a credit rather than a charge; never show a negative total.
total.textContent = money(Math.max(newCharge - unused, 0));
next.textContent = "Then " + money(price) + " on 14 March, and monthly after that.";
}
rows.forEach(function (row) {
row.addEventListener("click", function () {
select(row);
});
});
select(rows[1]);