Diff -- /var/www/vhosts/3dshawn.com/abforge.3dshawn.com/TEMPLATES/abforge_billing.html
Diff
/var/www/vhosts/3dshawn.com/abforge.3dshawn.com/TEMPLATES/abforge_billing.html
added on local at 2026-07-01 16:01:47
Added
+442
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to 5fe2368f0a29
to 5fe2368f0a29
Unified diff
Naive LCS-based line diff. Additions in emerald, deletions in rose. Both sides decompressed live from BLOB_STORE.| 1 | <!-- ===================================================================== | |
| 2 | Seller Billing. | |
| 3 | 1. Page head + tutorial button | |
| 4 | 2. Schema-not-ready banner (early-adopter accounts) | |
| 5 | 3. Current subscription card + credit balance card | |
| 6 | 4. Plan picker (upgrade / downgrade) | |
| 7 | 5. Recent invoices table | |
| 8 | 6. Payment method card | |
| 9 | Data comes from billing.cgi / MODS::ABForge::Billing. | |
| 10 | ===================================================================== --> | |
| 11 | ||
| 12 | <style> | |
| 13 | .bil-grid { | |
| 14 | display: grid; | |
| 15 | grid-template-columns: 1fr 1fr; | |
| 16 | gap: 14px; | |
| 17 | margin-bottom: 22px; | |
| 18 | } | |
| 19 | @media (max-width: 900px) { .bil-grid { grid-template-columns: 1fr; } } | |
| 20 | ||
| 21 | .bil-pricetag { | |
| 22 | font-size: 32px; font-weight: 700; color: #fff; | |
| 23 | font-family: var(--font-mono, monospace); | |
| 24 | margin-bottom: 4px; | |
| 25 | } | |
| 26 | .bil-pricetag .sub { font-size: 14px; color: var(--col-text-3); font-weight: 500; } | |
| 27 | ||
| 28 | .bil-next { | |
| 29 | background: var(--col-surface-2); | |
| 30 | border: 1px solid var(--col-border); | |
| 31 | border-radius: 10px; | |
| 32 | padding: 12px 14px; | |
| 33 | margin-top: 14px; | |
| 34 | } | |
| 35 | .bil-next-row { display:flex; justify-content:space-between; font-size:13px; padding:4px 0; } | |
| 36 | .bil-next-row.bil-next-total { border-top: 1px dashed var(--col-border); margin-top:6px; padding-top:8px; font-weight:700; color:#fff; } | |
| 37 | .bil-next-row .bil-credit { color: #a78bfa; } | |
| 38 | ||
| 39 | .bil-creditbox { | |
| 40 | background: linear-gradient(135deg, rgba(124,58,237,0.16) 0%, rgba(255,122,61,0.10) 100%); | |
| 41 | border: 1px solid rgba(124,58,237,0.30); | |
| 42 | border-radius: 12px; | |
| 43 | padding: 18px 22px; | |
| 44 | } | |
| 45 | .bil-creditbox .lbl { font-size: 10px; letter-spacing:1.5px; text-transform:uppercase; color:#c4b5fd; font-weight:700; } | |
| 46 | .bil-creditbox .val { font-size: 30px; font-weight: 700; color: #fff; font-family: var(--font-mono, monospace); } | |
| 47 | .bil-creditbox .note { font-size: 12px; color: var(--col-text-2); margin-top: 6px; line-height: 1.5; } | |
| 48 | ||
| 49 | .bil-ledger { display:flex; flex-direction:column; gap:8px; margin-top:14px; } | |
| 50 | .bil-ledger-row { | |
| 51 | display:flex; justify-content:space-between; gap:10px; | |
| 52 | padding:8px 10px; background: rgba(255,255,255,0.03); | |
| 53 | border-radius: 8px; font-size: 12px; | |
| 54 | } | |
| 55 | .bil-ledger-row .amt { font-family: var(--font-mono, monospace); font-weight: 700; flex-shrink:0; } | |
| 56 | .bil-ledger-row .amt.pos { color: #4ade80; } | |
| 57 | .bil-ledger-row .amt.neg { color: #fbbf24; } | |
| 58 | ||
| 59 | .bil-plans { | |
| 60 | display: grid; | |
| 61 | grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); | |
| 62 | gap: 14px; | |
| 63 | margin-bottom: 24px; | |
| 64 | } | |
| 65 | .bil-plan-card { | |
| 66 | background: var(--col-surface-1); | |
| 67 | border: 1px solid var(--col-border); | |
| 68 | border-radius: 14px; | |
| 69 | padding: 18px; | |
| 70 | position: relative; | |
| 71 | transition: border-color 0.15s, transform 0.15s, box-shadow 0.15s; | |
| 72 | /* Flex column so the action footer (form / "current" note) hugs the | |
| 73 | bottom edge regardless of how many feature bullets the card has. | |
| 74 | Without this the PRO card's extra bullet pushes its button below | |
| 75 | the others. */ | |
| 76 | display: flex; | |
| 77 | flex-direction: column; | |
| 78 | } | |
| 79 | .bil-plan-card.is-current { | |
| 80 | border-color: var(--col-accent); | |
| 81 | box-shadow: 0 0 0 2px var(--col-accent) inset, 0 8px 32px rgba(99,102,241,0.18); | |
| 82 | background: linear-gradient(180deg, rgba(99,102,241,0.10), rgba(99,102,241,0.02) 60%); | |
| 83 | } | |
| 84 | .bil-plan-card:hover { transform: translateY(-2px); } | |
| 85 | .bil-plan-card .badge { | |
| 86 | position: absolute; top: 14px; right: 14px; | |
| 87 | font-size: 9px; font-weight: 700; | |
| 88 | letter-spacing: 1.5px; text-transform: uppercase; | |
| 89 | color: var(--col-accent-bright); | |
| 90 | background: rgba(99,102,241,0.18); | |
| 91 | border: 1px solid rgba(99,102,241,0.45); | |
| 92 | padding: 3px 8px; border-radius: 999px; | |
| 93 | } | |
| 94 | .bil-plan-card .tier { font-size: 12px; font-weight: 700; letter-spacing:1.5px; text-transform:uppercase; color: var(--col-text-3); } | |
| 95 | .bil-plan-card.is-current .tier { color: var(--col-accent-bright); } | |
| 96 | .bil-plan-card .price { font-size: 28px; font-weight: 700; color: #fff; font-family: var(--font-mono, monospace); margin-top: 8px; } | |
| 97 | .bil-plan-card .price .sub { font-size: 13px; color: var(--col-text-3); font-weight: 500; } | |
| 98 | .bil-plan-card .annual { font-size: 11px; color: var(--col-text-3); margin-top: 2px; } | |
| 99 | .bil-plan-card ul { list-style: none; padding: 0; margin: 14px 0; display: flex; flex-direction: column; gap: 6px; } | |
| 100 | .bil-plan-card li { font-size: 12px; color: var(--col-text-2); display: flex; gap: 6px; align-items: flex-start; } | |
| 101 | .bil-plan-card li.in { color: var(--col-text); } | |
| 102 | .bil-plan-card li.out { color: var(--col-text-3); text-decoration: line-through; } | |
| 103 | .bil-plan-card .check { color: #4ade80; flex-shrink: 0; } | |
| 104 | .bil-plan-card .cross { color: var(--col-text-3); flex-shrink: 0; } | |
| 105 | /* margin-top:auto pushes the action footer to the bottom of the | |
| 106 | flex-column so every card's button sits at the same Y. */ | |
| 107 | .bil-plan-card form, | |
| 108 | .bil-plan-card .bil-plan-current-note { | |
| 109 | display: flex; flex-direction: column; gap: 6px; | |
| 110 | margin-top: auto; | |
| 111 | } | |
| 112 | ||
| 113 | .bil-itable { width: 100%; border-collapse: separate; border-spacing: 0; | |
| 114 | background: var(--col-surface-1); | |
| 115 | border: 1px solid var(--col-border); | |
| 116 | border-radius: 12px; overflow: hidden; } | |
| 117 | .bil-itable thead th { | |
| 118 | background: var(--col-surface-2); padding: 11px 14px; | |
| 119 | text-align: left; font-size: 10px; letter-spacing: 1.5px; | |
| 120 | text-transform: uppercase; color: var(--col-text-3); font-weight: 700; | |
| 121 | border-bottom: 1px solid var(--col-border); | |
| 122 | } | |
| 123 | .bil-itable tbody td { padding: 12px 14px; border-bottom: 1px solid var(--col-border); font-size: 13px; color: var(--col-text-2); vertical-align: middle; } | |
| 124 | .bil-itable tbody tr:last-child td { border-bottom: none; } | |
| 125 | .bil-itable .num { text-align: right; font-family: var(--font-mono, monospace); } | |
| 126 | .bil-itable .num.credit { color: #a78bfa; } | |
| 127 | .bil-itable .num.cash { color: #4ade80; } | |
| 128 | .bil-itable .pill { font-size: 9px; font-weight: 700; padding: 3px 8px; border-radius: 6px; letter-spacing: 1px; text-transform: uppercase; } | |
| 129 | .bil-itable .pill.paid { background: rgba(34,197,94,0.18); color: #4ade80; } | |
| 130 | .bil-itable .pill.open { background: rgba(245,158,11,0.18); color: #fbbf24; } | |
| 131 | .bil-itable .pill.failed { background: rgba(239,68,68,0.20); color: #f87171; } | |
| 132 | .bil-itable .pill.refunded { background: rgba(124,58,237,0.18); color: #a78bfa; } | |
| 133 | .bil-itable .pill.void { background: rgba(100,116,139,0.20); color: #94a3b8; } | |
| 134 | </style> | |
| 135 | ||
| 136 | <div class="page-head"> | |
| 137 | <div> | |
| 138 | <span class="page-eyebrow"><span class="dot"></span> Account · Billing</span> | |
| 139 | <h1 class="page-title">Billing & credits</h1> | |
| 140 | <p class="page-subtitle">Your subscription, credit balance, invoice history, and payment method. Refunds are issued as account credit and applied automatically to your next invoice before any card is charged.</p> | |
| 141 | </div> | |
| 142 | <div class="page-actions"> | |
| 143 | [if:$not_tutorial_mode]<a href="/billing.cgi?tutorial=1" class="btn btn-secondary"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>Sample Data View</a>[/if] | |
| 144 | [if:$tutorial_mode]<a href="/billing.cgi" class="btn btn-secondary"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>Exit sample view</a>[/if] | |
| 145 | </div> | |
| 146 | </div> | |
| 147 | ||
| 148 | [if:$has_resume_plan] | |
| 149 | <div class="banner mb-3" style="display:flex;gap:12px;align-items:center;padding:14px 16px;border-radius:10px;background:linear-gradient(135deg, rgba(99,102,241,0.12), rgba(139,92,246,0.08));border:1px solid rgba(99,102,241,0.4)"> | |
| 150 | <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="#a5b4fc" stroke-width="2" style="flex:0 0 auto"><polyline points="20 6 9 17 4 12"/></svg> | |
| 151 | <div style="flex:1"> | |
| 152 | <div class="fw-700" style="color:#fff;font-size:14px">Card saved. Ready to finish your upgrade?</div> | |
| 153 | <div class="text-sm" style="color:var(--col-text-2);margin-top:2px">Click below to switch to <strong>$resume_plan_label</strong> and charge <strong>$resume_charge_str</strong> now.</div> | |
| 154 | </div> | |
| 155 | <form method="POST" action="/billing_action.cgi" style="margin:0"> | |
| 156 | <input type="hidden" name="act" value="change_plan"> | |
| 157 | <input type="hidden" name="plan_id" value="$resume_plan_id"> | |
| 158 | <button type="submit" class="btn btn-primary btn-sm">Complete upgrade</button> | |
| 159 | </form> | |
| 160 | </div> | |
| 161 | [/if] | |
| 162 | ||
| 163 | [if:$has_plan_flash] | |
| 164 | <div class="banner [if:$plan_flash_is_ok]success[/if][if:$plan_flash_is_err]error[/if] mb-3" style="display:flex;gap:10px;align-items:flex-start;padding:12px 14px;border-radius:10px"> | |
| 165 | <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" style="flex:0 0 auto;margin-top:1px"> | |
| 166 | [if:$plan_flash_is_ok]<polyline points="20 6 9 17 4 12"/>[/if] | |
| 167 | [if:$plan_flash_is_err]<circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/>[/if] | |
| 168 | </svg> | |
| 169 | <div class="text-sm fw-600">$plan_flash_msg</div> | |
| 170 | </div> | |
| 171 | [/if] | |
| 172 | ||
| 173 | [if:$tutorial_mode] | |
| 174 | <div class="tutorial-banner"> | |
| 175 | <div class="tutorial-banner-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg></div> | |
| 176 | <div class="tutorial-banner-body"> | |
| 177 | <div class="tutorial-banner-title">Tutorial · Sample data only</div> | |
| 178 | <div class="tutorial-banner-text">Every panel below is filled with example data — an imaginary Pro-tier account with <strong>\$12.00 of credit</strong> on file, four past invoices, and a credit-applied next charge. Use this to see how billing reads when you have history.</div> | |
| 179 | </div> | |
| 180 | <a href="/billing.cgi" class="btn btn-secondary btn-sm">Exit sample view</a> | |
| 181 | </div> | |
| 182 | ||
| 183 | <div class="tutorial-intro"> | |
| 184 | <div class="tutorial-intro-eyebrow">A walkthrough of Account · Billing</div> | |
| 185 | <h2 class="tutorial-intro-title">Here is how billing and credits work on ABForge.</h2> | |
| 186 | <p class="tutorial-intro-text">Every subscription charge runs through one simple rule: <strong>your credit balance is applied before your card is charged</strong>. If you have \$12 of credit and your monthly plan is \$29, the next invoice charges \$17 in cash and consumes the \$12 of credit. If your credit covers the whole invoice, your card is never touched that month. The <em>Credit balance</em> panel on the right shows your running balance and every grant or consumption that affected it.</p> | |
| 187 | <p class="tutorial-intro-text">When you ask for a refund, ABForge issues it as <strong>account credit</strong> rather than returning money to your card. The credit goes straight into your balance and is auto-applied to your next bill. This keeps the math clean for your business taxes (no card refunds to reconcile) and gives you faster relief than a card refund (which takes 5-10 business days to settle). The recent-invoices table below uses the <em>Refunded as credit</em> status to call out which invoices triggered credit grants.</p> | |
| 188 | <p class="tutorial-intro-text tutorial-intro-foot">When you are ready to see your live billing, click <a href="/billing.cgi" class="text-brand fw-700">Exit sample view</a>.</p> | |
| 189 | </div> | |
| 190 | [/if] | |
| 191 | ||
| 192 | [if:$schema_missing] | |
| 193 | <div class="an-banner" style="background:rgba(245,158,11,0.10);border-color:rgba(245,158,11,0.30)"> | |
| 194 | <strong>Billing schema not yet installed.</strong> Run the latest <code>db_schema.sql</code> against your database to bring the billing tables online; once installed, this page populates with your real subscription, credit balance, and invoices. | |
| 195 | </div> | |
| 196 | [/if] | |
| 197 | ||
| 198 | [if:$schema_ready] | |
| 199 | <!-- Current subscription + credit balance --> | |
| 200 | <div class="bil-grid"> | |
| 201 | <div class="module"> | |
| 202 | <div class="module-head"> | |
| 203 | <div class="left"> | |
| 204 | <div class="module-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="5" width="20" height="14" rx="2"/><line x1="2" y1="10" x2="22" y2="10"/></svg></div> | |
| 205 | <div> | |
| 206 | <div class="module-title">Current plan</div> | |
| 207 | <div class="module-sub">$sub_plan_label · $sub_cadence_label cadence</div> | |
| 208 | </div> | |
| 209 | </div> | |
| 210 | <span class="pill brand">$sub_status_label</span> | |
| 211 | </div> | |
| 212 | <div class="module-body"> | |
| 213 | <div class="bil-pricetag">$sub_price <span class="sub">/ $sub_cadence_label</span></div> | |
| 214 | <div class="text-xs text-dim">Next charge: <strong style="color:#fff">$sub_next_charge</strong></div> | |
| 215 | ||
| 216 | [if:$sub_is_canceling] | |
| 217 | <div class="text-xs" style="color:#fbbf24;margin-top:8px">Cancels at period end — you will not be charged again.</div> | |
| 218 | [/if] | |
| 219 | [if:$sub_is_past_due] | |
| 220 | <div class="text-xs" style="color:#f87171;margin-top:8px">Past due — we will retry the card automatically. Update your payment method below if the issue persists.</div> | |
| 221 | [/if] | |
| 222 | [if:$sub_is_trialing] | |
| 223 | <div class="text-xs" style="color:#a78bfa;margin-top:8px">Free trial in progress. The first paid invoice generates when your trial ends.</div> | |
| 224 | [/if] | |
| 225 | ||
| 226 | <div class="bil-next"> | |
| 227 | <div class="text-xs text-dim" style="letter-spacing:1.5px;text-transform:uppercase;margin-bottom:6px">Next invoice preview</div> | |
| 228 | <div class="bil-next-row"><span>Plan charge</span><span>$next_charge_display</span></div> | |
| 229 | [if:$has_next_credit_applied] | |
| 230 | <div class="bil-next-row"><span class="bil-credit">Credit applied</span><span class="bil-credit">-$next_credit_display</span></div> | |
| 231 | [/if] | |
| 232 | <div class="bil-next-row bil-next-total"><span>Cash due to card</span><span>$next_cash_display</span></div> | |
| 233 | [if:$next_cash_due_zero] | |
| 234 | <div class="text-xs" style="color:#4ade80;margin-top:6px">Your credit covers the full charge — no card will be billed this period.</div> | |
| 235 | [/if] | |
| 236 | </div> | |
| 237 | </div> | |
| 238 | </div> | |
| 239 | ||
| 240 | <div class="module"> | |
| 241 | <div class="module-head"> | |
| 242 | <div class="left"> | |
| 243 | <div class="module-icon" style="background:rgba(124,58,237,0.18);color:var(--col-violet-bright)"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 1v22M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg></div> | |
| 244 | <div> | |
| 245 | <div class="module-title">Credit balance</div> | |
| 246 | <div class="module-sub">Applied automatically to your next invoice</div> | |
| 247 | </div> | |
| 248 | </div> | |
| 249 | </div> | |
| 250 | <div class="module-body"> | |
| 251 | <div class="bil-creditbox"> | |
| 252 | <div class="lbl">Available credit</div> | |
| 253 | <div class="val">$credit_balance_display</div> | |
| 254 | [if:$has_credit_balance] | |
| 255 | <div class="note">This credit is applied to your <strong>next invoice</strong> before your card is charged. If your credit covers the bill in full, your card is never touched that period.</div> | |
| 256 | [/if] | |
| 257 | [if:!$has_credit_balance] | |
| 258 | <div class="note">You have no account credit right now. If you ever request a refund it will be added here automatically.</div> | |
| 259 | [/if] | |
| 260 | </div> | |
| 261 | ||
| 262 | [if:$has_credit_history] | |
| 263 | <div class="text-xs text-dim mt-3" style="letter-spacing:1.5px;text-transform:uppercase">Recent activity</div> | |
| 264 | <div class="bil-ledger"> | |
| 265 | [loop:@credit_rows] | |
| 266 | <div class="bil-ledger-row"> | |
| 267 | <div> | |
| 268 | <div style="color:var(--col-text)">$loop1.reason_label</div> | |
| 269 | [if:$loop1.has_note]<div class="text-xs text-dim">$loop1.note</div>[/if] | |
| 270 | <div class="text-xs text-dim">$loop1.created_at</div> | |
| 271 | </div> | |
| 272 | <div class="amt [if:$loop1.is_positive]pos[/if][if:$loop1.is_negative]neg[/if]">$loop1.amount_display</div> | |
| 273 | </div> | |
| 274 | [/loop] | |
| 275 | </div> | |
| 276 | [/if] | |
| 277 | </div> | |
| 278 | </div> | |
| 279 | </div> | |
| 280 | ||
| 281 | <!-- Promo code --> | |
| 282 | <h2 style="font-size:18px;margin:18px 0 14px;color:var(--col-text)">Have a promo code?</h2> | |
| 283 | <div class="module" style="margin-bottom:18px"> | |
| 284 | <div class="module-body"> | |
| 285 | [if:$has_active_promo] | |
| 286 | <!-- Live offer banner. Driven by Promotions.pm::active_plan_promo | |
| 287 | on the seller's billing page so the deal is impossible to miss. --> | |
| 288 | <div style="display:flex;align-items:center;gap:12px;padding:10px 14px;margin-bottom:14px;border-radius:10px;background:linear-gradient(135deg,rgba(34,197,94,0.10),rgba(124,58,237,0.10));border:1px solid rgba(34,197,94,0.35);flex-wrap:wrap"> | |
| 289 | <span style="display:inline-flex;align-items:center;gap:6px;padding:3px 10px;border-radius:999px;background:rgba(34,197,94,0.18);color:#86efac;font-size:10.5px;font-weight:800;letter-spacing:1.5px;text-transform:uppercase">Limited-time</span> | |
| 290 | <div style="color:#fff;font-size:13px;font-weight:600">$active_promo_label with code <code style="background:rgba(255,255,255,0.10);padding:2px 7px;border-radius:5px;font-family:var(--font-mono,monospace);font-weight:700">$active_promo_code</code></div> | |
| 291 | </div> | |
| 292 | [/if] | |
| 293 | [if:$has_promo_flash] | |
| 294 | <div class="banner $promo_flash_kind" style="margin-bottom:14px"> | |
| 295 | <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="8" x2="12" y2="12"/><line x1="12" y1="16" x2="12.01" y2="16"/></svg> | |
| 296 | <div class="text-xs">$promo_flash_msg</div> | |
| 297 | </div> | |
| 298 | [/if] | |
| 299 | <form method="POST" action="/billing_action.cgi" style="display:flex;gap:10px;align-items:center;flex-wrap:wrap"> | |
| 300 | <input type="hidden" name="act" value="apply_promo_code"> | |
| 301 | <input class="input" type="text" name="promo_code" value="$active_promo_code" placeholder="ENTER CODE" autocomplete="off" maxlength="64" style="text-transform:uppercase;max-width:260px;flex:1"> | |
| 302 | <button type="submit" class="btn btn-primary">Apply code</button> | |
| 303 | </form> | |
| 304 | <div class="text-xs text-dim" style="margin-top:8px;line-height:1.5"> | |
| 305 | Codes apply to your next invoice automatically by adding promotional credit equal to the discount. The discount is calculated against your current plan's price — if you are on Free, upgrade first to benefit. | |
| 306 | </div> | |
| 307 | </div> | |
| 308 | </div> | |
| 309 | ||
| 310 | <!-- Pending-change banner: surfaces a scheduled downgrade (the seller | |
| 311 | chose a lower tier; we wait until the current paid cycle ends | |
| 312 | before flipping plan_id). Always above the plan picker so the | |
| 313 | state is obvious before they try to pick another plan. --> | |
| 314 | [if:$has_pending_change] | |
| 315 | <div style="display:flex;align-items:flex-start;gap:12px;padding:12px 14px;margin:18px 0 0;border-radius:10px;background:rgba(251,191,36,0.12);border:1px solid rgba(251,191,36,0.35)"> | |
| 316 | <svg viewBox="0 0 24 24" fill="none" stroke="#fbbf24" stroke-width="2" style="width:18px;height:18px;flex:0 0 auto;margin-top:1px"><circle cx="12" cy="12" r="10"/><polyline points="12,6 12,12 16,14"/></svg> | |
| 317 | <div style="font-size:13px;line-height:1.55;color:#fde68a"> | |
| 318 | Scheduled to switch to <strong style="color:#fff">$pending_plan_label</strong> ($pending_cadence_label) on <strong style="color:#fff">$pending_change_at</strong>. You will keep your current plan until then since you have already paid for this cycle. | |
| 319 | <form method="POST" action="/billing_action.cgi" style="display:inline-block;margin-left:6px"> | |
| 320 | <input type="hidden" name="act" value="clear_pending_change"> | |
| 321 | <button type="submit" class="btn btn-secondary btn-sm" style="padding:3px 10px">Keep current plan</button> | |
| 322 | </form> | |
| 323 | </div> | |
| 324 | </div> | |
| 325 | [/if] | |
| 326 | ||
| 327 | <!-- Plan picker --> | |
| 328 | [if:$has_plans] | |
| 329 | <h2 id="change-plan" style="font-size:18px;margin:18px 0 14px;color:var(--col-text)">Change plan</h2> | |
| 330 | <div class="bil-plans"> | |
| 331 | [loop:@plan_cards] | |
| 332 | <div class="bil-plan-card[if:$loop1.is_current] is-current[/if]"> | |
| 333 | [if:$loop1.is_current]<div class="badge">Current</div>[/if] | |
| 334 | <div class="tier">$loop1.tier_label</div> | |
| 335 | <div class="price">$loop1.price_monthly <span class="sub">/ mo</span></div> | |
| 336 | <div class="annual">or $loop1.price_annual / year</div> | |
| 337 | <ul> | |
| 338 | [loop:@loop1.features] | |
| 339 | <li class="[if:$loop2.included]in[/if][if:!$loop2.included]out[/if]"> | |
| 340 | [if:$loop2.included]<span class="check">✓</span>[/if] | |
| 341 | [if:!$loop2.included]<span class="cross">✕</span>[/if] | |
| 342 | <span>$loop2.name</span> | |
| 343 | </li> | |
| 344 | [/loop] | |
| 345 | </ul> | |
| 346 | [if:!$loop1.is_current] | |
| 347 | [ptag:manage_billing] | |
| 348 | <form method="POST" action="/billing_action.cgi" | |
| 349 | data-confirm-title="$loop1.confirm_title" | |
| 350 | data-confirm-message="$loop1.confirm_message" | |
| 351 | data-confirm-label="$loop1.confirm_label" | |
| 352 | data-confirm-cancel="Keep current plan" | |
| 353 | data-confirm-style="$loop1.confirm_style"> | |
| 354 | <input type="hidden" name="act" value="change_plan"> | |
| 355 | <input type="hidden" name="plan_id" value="$loop1.plan_id_monthly"> | |
| 356 | <button class="btn btn-primary btn-sm" type="submit">Switch to $loop1.tier_label</button> | |
| 357 | </form> | |
| 358 | [/ptag:manage_billing] | |
| 359 | [/if] | |
| 360 | [if:$loop1.is_current] | |
| 361 | <div class="bil-plan-current-note"> | |
| 362 | <div class="btn btn-secondary btn-sm" style="cursor:default;text-align:center;background:rgba(99,102,241,0.14);border:1px solid rgba(99,102,241,0.45);color:var(--col-accent-bright);font-weight:700">Your current plan</div> | |
| 363 | </div> | |
| 364 | [/if] | |
| 365 | </div> | |
| 366 | [/loop] | |
| 367 | </div> | |
| 368 | [/if] | |
| 369 | ||
| 370 | <!-- Recent invoices --> | |
| 371 | <h2 style="font-size:18px;margin:18px 0 14px;color:var(--col-text)">Invoice history</h2> | |
| 372 | [if:$has_invoices] | |
| 373 | <table class="bil-itable"> | |
| 374 | <thead> | |
| 375 | <tr> | |
| 376 | <th>Invoice</th> | |
| 377 | <th>Period</th> | |
| 378 | <th class="num">Amount due</th> | |
| 379 | <th class="num">Credit applied</th> | |
| 380 | <th class="num">Cash paid</th> | |
| 381 | <th>Status</th> | |
| 382 | </tr> | |
| 383 | </thead> | |
| 384 | <tbody> | |
| 385 | [loop:@invoices] | |
| 386 | <tr> | |
| 387 | <td><strong style="color:var(--col-text)">$loop1.invoice_number</strong> | |
| 388 | [if:$loop1.has_failure]<div class="text-xs" style="color:#f87171">$loop1.failure_reason</div>[/if] | |
| 389 | </td> | |
| 390 | <td>$loop1.period_label</td> | |
| 391 | <td class="num">$loop1.amount_due_display</td> | |
| 392 | <td class="num credit">$loop1.credit_applied_display</td> | |
| 393 | <td class="num cash">$loop1.cash_paid_display</td> | |
| 394 | <td> | |
| 395 | [if:$loop1.is_paid]<span class="pill paid">Paid</span>[/if] | |
| 396 | [if:$loop1.is_open]<span class="pill open">Open</span>[/if] | |
| 397 | [if:$loop1.is_failed]<span class="pill failed">Failed</span>[/if] | |
| 398 | [if:$loop1.is_refunded]<span class="pill refunded">Refunded as credit</span>[/if] | |
| 399 | [if:$loop1.is_void]<span class="pill void">Void</span>[/if] | |
| 400 | </td> | |
| 401 | </tr> | |
| 402 | [/loop] | |
| 403 | </tbody> | |
| 404 | </table> | |
| 405 | [/if] | |
| 406 | [if:!$has_invoices] | |
| 407 | <div style="text-align:center;padding:36px 18px;background:var(--col-surface-1);border:1px solid var(--col-border);border-radius:12px"> | |
| 408 | <div class="text-sm fw-600" style="color:#fff;margin-bottom:6px">No invoices yet</div> | |
| 409 | <div class="text-xs text-dim">Your first invoice issues on your billing anchor date once you are on a paid plan.</div> | |
| 410 | </div> | |
| 411 | [/if] | |
| 412 | ||
| 413 | <!-- Payment method --> | |
| 414 | <h2 style="font-size:18px;margin:24px 0 14px;color:var(--col-text)">Payment method</h2> | |
| 415 | <div class="module"> | |
| 416 | <div class="module-body" style="display:flex;align-items:center;justify-content:space-between;gap:14px"> | |
| 417 | [if:$has_pm] | |
| 418 | <div style="display:flex;align-items:center;gap:14px"> | |
| 419 | <div style="width:48px;height:32px;border-radius:6px;background:linear-gradient(135deg,#1e293b,#334155);display:grid;place-items:center;font-size:11px;font-weight:700;color:#fff">CARD</div> | |
| 420 | <div> | |
| 421 | <div class="text-sm fw-600" style="color:var(--col-text)">$pm_brand_label ···· $pm_last4</div> | |
| 422 | <div class="text-xs text-dim">Expires $pm_exp</div> | |
| 423 | </div> | |
| 424 | </div> | |
| 425 | [ptag:manage_billing] | |
| 426 | <a href="/billing_payment.cgi" class="btn btn-secondary btn-sm">Manage cards</a> | |
| 427 | [/ptag:manage_billing] | |
| 428 | [/if] | |
| 429 | [if:!$has_pm] | |
| 430 | <div> | |
| 431 | <div class="text-sm fw-600" style="color:var(--col-text);margin-bottom:4px">No payment method on file</div> | |
| 432 | <div class="text-xs text-dim">Required for paid plans. Free plan does not need a card.</div> | |
| 433 | </div> | |
| 434 | [ptag:manage_billing] | |
| 435 | <a href="/billing_payment.cgi" class="btn btn-primary btn-sm">Add a card</a> | |
| 436 | [/ptag:manage_billing] | |
| 437 | [/if] | |
| 438 | </div> | |
| 439 | </div> | |
| 440 | [/if] | |
| 441 | ||
| 442 | <div class="adm-foot-spacer" data-end="billing"> </div> |