-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMainWindow.cpp
More file actions
642 lines (510 loc) · 28.5 KB
/
Copy pathMainWindow.cpp
File metadata and controls
642 lines (510 loc) · 28.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
//main function
#include "MainWindow.h"
#include <QVBoxLayout>
#include <QGridLayout>
#include <QMessageBox>
#include <QListWidgetItem>
#include <algorithm>
using std::vector;
using std::string;
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
QWidget *central = new QWidget(this);
setCentralWidget(central);
setWindowTitle("Credit Card Optimizer 💳");
resize(980, 720);
cardTypeBox = new QComboBox();
cardTypeBox->addItems({"Travel", "Cashback", "Low Interest", "Student"});
creditTierBox = new QComboBox();
creditTierBox->addItems({
"Poor (300–579)",
"Fair (580–669)",
"Good (670–739)",
"Very Good (740–799)",
"Excellent (800–850)"
});
creditTierBox->setCurrentIndex(2);
feeTierBox = new QComboBox();
feeTierBox->addItems({"$0", "Up to $100", "Up to $500", "Up to $1000"});
feeTierBox->setCurrentIndex(1);
spendBox = new QComboBox();
spendBox->addItems({"Groceries", "Dining", "Travel", "Gas", "Online"});
travelCheck = new QCheckBox("Travel 2+ times per year");
loungeCheck = new QCheckBox("Lounge access is important");
showButton = new QPushButton("Show Cards");
compareButton = new QPushButton("Compare Selected (2–3)");
detailsHintLabel = new QLabel("(Double click on the name of the card to see details)");
detailsHintLabel->setWordWrap(true);
resultsList = new QListWidget();
resultsList->setSelectionMode(QAbstractItemView::MultiSelection);
disclaimerLabel = new QLabel(
"Disclaimer: Results are estimates only. Offers/benefits change and vary by person. "
"Point transfers may increase value. This app is not affiliated with any issuer."
);
disclaimerLabel->setWordWrap(true);
auto *grid = new QGridLayout();
int r = 0;
grid->addWidget(new QLabel("Card Type:"), r, 0);
grid->addWidget(cardTypeBox, r++, 1);
grid->addWidget(new QLabel("Credit Tier:"), r, 0);
grid->addWidget(creditTierBox, r++, 1);
grid->addWidget(new QLabel("Max Annual Fee:"), r, 0);
grid->addWidget(feeTierBox, r++, 1);
grid->addWidget(new QLabel("Biggest Spend:"), r, 0);
grid->addWidget(spendBox, r++, 1);
grid->addWidget(travelCheck, r++, 0, 1, 2);
grid->addWidget(loungeCheck, r++, 0, 1, 2);
auto *layout = new QVBoxLayout();
layout->addLayout(grid);
layout->addWidget(showButton);
layout->addWidget(detailsHintLabel);
layout->addWidget(resultsList);
layout->addWidget(compareButton);
layout->addWidget(disclaimerLabel);
central->setLayout(layout);
connect(showButton, &QPushButton::clicked, this, &MainWindow::showAllCards);
connect(compareButton, &QPushButton::clicked, this, &MainWindow::compareCards);
connect(resultsList, &QListWidget::itemDoubleClicked,
this, &MainWindow::viewDetailsFromItem);
loadCards();
resultsList->clear();
}
int MainWindow::userCreditScore() const {
static int scores[] = {560, 640, 700, 760, 820};
return scores[creditTierBox->currentIndex()];
}
double MainWindow::userMaxAnnualFee() const {
static double fees[] = {0, 100, 500, 1000};
return fees[feeTierBox->currentIndex()];
}
CardType MainWindow::userGoal() const {
return static_cast<CardType>(cardTypeBox->currentIndex());
}
int MainWindow::userMainSpend() const {
return spendBox->currentIndex();
}
bool MainWindow::userTravelsOften() const {
return travelCheck->isChecked();
}
bool MainWindow::userWantsLounge() const {
return loungeCheck->isChecked();
}
bool MainWindow::isTypeRelevant(const CreditCard &card) const {
return card.type == userGoal();
}
bool MainWindow::isOptimalForUser(const CreditCard &card) const {
if (!isTypeRelevant(card)) return false;
if (card.annualFee > userMaxAnnualFee()) return false;
if (userCreditScore() < card.creditRange.minScore) return false;
return true;
}
QString MainWindow::cardTypeToString(CardType t) const {
switch (t) {
case TRAVEL: return "Travel";
case CASHBACK: return "Cashback";
case LOW_INTEREST: return "Low Interest";
case STUDENT: return "Student";
default: return "Unknown";
}
}
QString MainWindow::buildCardDetailsText(const CreditCard &card) const {
QString text;
text += "💳 " + QString::fromStdString(card.name) + "\n";
text += "Type: " + cardTypeToString(card.type) + "\n";
text += "Annual Fee: $" + QString::number(card.annualFee, 'f', 0) + "\n";
text += "Typical Credit Range: " + QString::number(card.creditRange.minScore)
+ "–" + QString::number(card.creditRange.maxScore) + "\n";
if (!card.minCreditLimit.empty())
text += "Min Credit Limit: " + QString::fromStdString(card.minCreditLimit) + "\n";
else
text += "Min Credit Limit: —\n";
text += "\nSignup Bonus: " + QString::fromStdString(card.signupBonus) + "\n";
text += "Cash Equivalent: ~$" + QString::number(card.bonusCashValue, 'f', 0)
+ " (can be higher via transfers)\n\n";
text += "Rewards Summary: " + QString::fromStdString(card.rewardsSummary) + "\n\n";
text += "Lounge Access: " + QString(card.loungeAccess ? "Yes" : "No") + "\n";
text += "Foreign Transaction Fee: " + QString(card.foreignTransactionFee ? "Yes" : "No") + "\n\n";
text += "Perks:\n";
for (const auto &perk : card.perks)
text += " - " + QString::fromStdString(perk) + "\n";
text += "\nDisclaimer: Verify current terms/benefits on the issuer’s website.\n";
return text;
}
int MainWindow::scoreCard(const CreditCard &card) const {
int score = 0;
const int userScore = userCreditScore();
const double maxFee = userMaxAnnualFee();
if (isTypeRelevant(card)) score += 500;
else score -= 500;
if (userScore >= card.creditRange.minScore) score += 40;
else score -= 70;
if (card.annualFee <= maxFee) score += 35;
else score -= 60;
score += static_cast<int>(card.bonusCashValue / 100.0);
const int spend = userMainSpend();
auto contains = [&](const string &kw) { return card.rewardsSummary.find(kw) != string::npos; };
if (spend == 0 && (contains("grocery") || contains("Grocer"))) score += 14;
if (spend == 1 && (contains("dining") || contains("Dining") || contains("restaurant"))) score += 14;
if (spend == 2 && (contains("travel") || contains("Travel") || contains("hotel") || contains("flight"))) score += 14;
if (spend == 3 && (contains("gas") || contains("Gas"))) score += 12;
if (spend == 4 && (contains("online") || contains("Online") || contains("Amazon"))) score += 10;
if (card.type == TRAVEL) {
if (userTravelsOften() && !card.foreignTransactionFee) score += 10;
if (userTravelsOften() && card.loungeAccess) score += 6;
if (userWantsLounge()) {
if (card.loungeAccess) score += 14;
else score -= 10;
}
}
return score;
}
void MainWindow::showAllCards() {
resultsList->clear();
vector<std::pair<int,int>> ranked;
ranked.reserve(cards.size());
for (int i = 0; i < (int)cards.size(); i++) {
ranked.push_back({scoreCard(cards[i]), i});
}
std::sort(ranked.begin(), ranked.end(),
[](const auto &a, const auto &b) { return a.first > b.first; });
for (const auto &p : ranked) {
const CreditCard &card = cards[p.second];
bool relevant = isTypeRelevant(card);
bool optimal = isOptimalForUser(card);
QString label = QString::fromStdString(card.name)
+ " | Type: " + cardTypeToString(card.type)
+ " | Fee: $" + QString::number(card.annualFee, 'f', 0);
if (!relevant) label += " (Irrelevant)";
else if (!optimal) label += " (Not optimal)";
auto *item = new QListWidgetItem(label);
item->setData(Qt::UserRole, p.second);
if (!relevant || !optimal) item->setForeground(Qt::red);
resultsList->addItem(item);
}
}
void MainWindow::viewDetailsFromItem(QListWidgetItem* item) {
if (!item) return;
int idx = item->data(Qt::UserRole).toInt();
if (idx < 0 || idx >= (int)cards.size()) return;
QMessageBox::information(this, "Card Details", buildCardDetailsText(cards[idx]));
}
void MainWindow::compareCards() {
auto selected = resultsList->selectedItems();
if (selected.size() < 2 || selected.size() > 3) {
QMessageBox::warning(this, "Selection Error", "Please select 2 or 3 cards to compare.");
return;
}
QString text;
text += "Selected Card Type: " + cardTypeToString(userGoal()) + "\n";
text += "Credit Tier: " + creditTierBox->currentText() + "\n";
text += "Max Fee: " + feeTierBox->currentText() + "\n";
text += "Biggest Spend: " + spendBox->currentText() + "\n\n";
for (auto *it : selected) {
int idx = it->data(Qt::UserRole).toInt();
if (idx < 0 || idx >= (int)cards.size()) continue;
text += buildCardDetailsText(cards[idx]);
text += "\n----------------------------------------\n\n";
}
text += "Thank you for choosing us.\n";
QMessageBox::information(this, "Card Comparison", text);
}
void MainWindow::loadCards() {
cards.clear();
cards.emplace_back("Chase Sapphire Preferred®", TRAVEL, 95, CreditRange{700,850},
"$5,000+ (often Visa Signature tier)",
"Points bonus (offer varies)", 750,
"Travel + dining rewards", false, false,
vector<string>{"Primary rental car coverage", "Trip protections", "Transfer partners"});
cards.emplace_back("Chase Sapphire Reserve®", TRAVEL, 550, CreditRange{720,850},
"$10,000+ (often Visa Infinite tier)",
"Points bonus (offer varies)", 900,
"Premium travel rewards", true, false,
vector<string>{"Travel credit (varies)", "Priority Pass", "Premium protections"});
cards.emplace_back("Capital One Venture Rewards", TRAVEL, 95, CreditRange{700,850},
"",
"Miles bonus (offer varies)", 750,
"Simple travel miles", false, false,
vector<string>{"Flexible redemptions", "Transfer options (may apply)"});
cards.emplace_back("Capital One Venture X", TRAVEL, 395, CreditRange{720,850},
"$10,000+ (often Visa Infinite tier)",
"Miles bonus (offer varies)", 900,
"Premium travel miles + credits", true, false,
vector<string>{"Travel credits (varies)", "Lounge access", "Premium perks"});
cards.emplace_back("American Express® Gold Card", TRAVEL, 325, CreditRange{700,850},
"N/A (charge card)",
"Points bonus (offer varies)", 900,
"Dining + groceries (points)", false, true,
vector<string>{"Credits (varies)", "Strong earn on food categories"});
cards.emplace_back("American Express Platinum Card®", TRAVEL, 895, CreditRange{700,850},
"N/A (charge card)",
"Points bonus (offer varies)", 1400,
"Premium travel perks + lounge focus", true, true,
vector<string>{"Airport lounge access", "Credits (varies)", "Premium protections"});
cards.emplace_back("Wells Fargo Autograph®", TRAVEL, 0, CreditRange{670,850},
"",
"Cash/points bonus (offer varies)", 200,
"Broad bonus categories (includes travel)", false, true,
vector<string>{"No annual fee", "Broad category rewards"});
cards.emplace_back("Bilt Mastercard®", TRAVEL, 0, CreditRange{670,850},
"",
"Points on rent (terms vary)", 250,
"Rent + travel partners", false, true,
vector<string>{"Earn on rent (rules apply)", "Transfer partners (may apply)"});
cards.emplace_back("Citi Premier® Card", TRAVEL, 95, CreditRange{700,850},
"",
"Points bonus (offer varies)", 600,
"Travel + dining + gas rewards", false, true,
vector<string>{"Transfer partners (may apply)", "Good all-round travel card"});
cards.emplace_back("Bank of America® Premium Rewards®", TRAVEL, 95, CreditRange{700,850},
"",
"Points bonus (offer varies)", 500,
"Simple travel points + credits", false, true,
vector<string>{"Travel credit (varies)", "Flexible redemption options"});
cards.emplace_back("Bank of America® Travel Rewards", TRAVEL, 0, CreditRange{670,850},
"",
"Points bonus (offer varies)", 250,
"No annual fee travel points", false, false,
vector<string>{"No annual fee", "No foreign transaction fee"});
cards.emplace_back("U.S. Bank Altitude® Reserve", TRAVEL, 400, CreditRange{720,850},
"$10,000+ (often Visa Infinite tier)",
"Points bonus (offer varies)", 750,
"Travel + mobile wallet rewards", true, false,
vector<string>{"Premium perks", "Strong mobile wallet earning"});
cards.emplace_back("U.S. Bank Altitude® Connect", TRAVEL, 0, CreditRange{700,850},
"",
"Points bonus (offer varies)", 250,
"Travel + gas oriented rewards", false, true,
vector<string>{"Travel perks vary", "Good for some travel categories"});
cards.emplace_back("Capital One VentureOne", TRAVEL, 0, CreditRange{670,850},
"",
"Miles bonus (offer varies)", 200,
"No annual fee travel miles", false, true,
vector<string>{"No annual fee", "Simple miles structure"});
cards.emplace_back("Wells Fargo Autograph Journey℠", TRAVEL, 95, CreditRange{700,850},
"",
"Points bonus (offer varies)", 600,
"Travel + dining rewards", false, true,
vector<string>{"Travel protections may vary", "Good mid-fee travel option"});
cards.emplace_back("Chase United℠ Explorer Card", TRAVEL, 0, CreditRange{670,850},
"",
"Miles bonus (offer varies)", 500,
"United miles + travel perks", false, true,
vector<string>{"Airline perks vary", "Better for loyal flyers"});
cards.emplace_back("Chase United℠ Quest Card", TRAVEL, 250, CreditRange{700,850},
"",
"Miles bonus (offer varies)", 650,
"United-focused travel perks", false, true,
vector<string>{"Better for loyal flyers", "Credits/perks vary"});
cards.emplace_back("Chase United Club℠ Infinite Card", TRAVEL, 525, CreditRange{720,850},
"$10,000+ (often Visa Infinite tier)",
"Miles bonus (offer varies)", 800,
"Premium airline perks + lounge style benefits", true, true,
vector<string>{"Premium perks", "Best for heavy airline loyalty"});
cards.emplace_back("Delta SkyMiles® Gold American Express", TRAVEL, 150, CreditRange{700,850},
"",
"Miles bonus (offer varies)", 450,
"Airline miles rewards", false, true,
vector<string>{"Airline perks vary", "Good for loyal flyers"});
cards.emplace_back("Delta SkyMiles® Platinum American Express", TRAVEL, 350, CreditRange{700,850},
"",
"Miles bonus (offer varies)", 650,
"Airline perks + miles", false, true,
vector<string>{"Benefits vary", "Better for frequent flyers"});
cards.emplace_back("Citi® / AAdvantage® Platinum Select®", TRAVEL, 99, CreditRange{700,850},
"",
"Miles bonus (offer varies)", 450,
"Airline miles rewards", false, true,
vector<string>{"Airline perks vary", "Good if loyal to that airline"});
cards.emplace_back("Barclays AAdvantage® Aviator Red", TRAVEL, 99, CreditRange{700,850},
"",
"Miles bonus (offer varies)", 500,
"Airline miles oriented", false, true,
vector<string>{"Airline perks vary", "Good for loyal flyers"});
cards.emplace_back("JetBlue Plus Card", TRAVEL, 99, CreditRange{700,850},
"",
"Points bonus (offer varies)", 350,
"Airline points rewards", false, true,
vector<string>{"Airline perks vary", "Best for loyal flyers"});
cards.emplace_back("Chase Southwest Rapid Rewards® Plus", TRAVEL, 69, CreditRange{670,850},
"",
"Points bonus (offer varies)", 400,
"Airline co-branded rewards", false, true,
vector<string>{"Airline perks vary", "Best if loyal to that airline"});
cards.emplace_back("Chase Marriott Bonvoy Boundless®", TRAVEL, 95, CreditRange{670,850},
"",
"Hotel bonus (offer varies)", 450,
"Hotel co-branded rewards", false, true,
vector<string>{"Hotel perks vary", "Best if you use that brand"});
cards.emplace_back("Chase World of Hyatt®", TRAVEL, 95, CreditRange{670,850},
"",
"Hotel bonus (offer varies)", 450,
"Hotel co-branded rewards", false, true,
vector<string>{"Hotel perks vary", "Best if loyal to that brand"});
cards.emplace_back("IHG One Rewards Premier", TRAVEL, 99, CreditRange{700,850},
"",
"Hotel bonus (offer varies)", 450,
"Hotel points + perks", false, true,
vector<string>{"Better for hotel loyalists", "Benefits vary"});
cards.emplace_back("Hilton Honors American Express Card", TRAVEL, 0, CreditRange{670,850},
"",
"Hotel points bonus (offer varies)", 300,
"Hotel points rewards", false, true,
vector<string>{"No annual fee", "Good entry hotel card"});
cards.emplace_back("Hilton Honors American Express Surpass®", TRAVEL, 150, CreditRange{700,850},
"",
"Hotel points bonus (offer varies)", 450,
"Hotel points rewards", false, true,
vector<string>{"Hotel perks vary", "Better for that ecosystem"});
// CASHBACK
cards.emplace_back("Chase Freedom Unlimited®", CASHBACK, 0, CreditRange{670,850},
"",
"Cash bonus (offer varies)", 200,
"Everyday cashback + bonus categories", false, true,
vector<string>{"No annual fee", "Simple daily driver"});
cards.emplace_back("Chase Freedom Flex®", CASHBACK, 0, CreditRange{670,850},
"",
"Cash bonus (offer varies)", 200,
"Rotating cashback categories", false, true,
vector<string>{"No annual fee", "Strong when categories match"});
cards.emplace_back("Discover it® Cash Back", CASHBACK, 0, CreditRange{670,850},
"",
"Cashback match/bonus (varies)", 250,
"Rotating 5% categories", false, true,
vector<string>{"No annual fee", "Rotating categories"});
cards.emplace_back("Wells Fargo Active Cash®", CASHBACK, 0, CreditRange{670,850},
"",
"Cash bonus (offer varies)", 200,
"Flat-rate cashback style", false, true,
vector<string>{"No annual fee", "Simple flat rewards"});
cards.emplace_back("Citi® Double Cash", CASHBACK, 0, CreditRange{670,850},
"",
"No bonus (often)", 0,
"Flat-rate cashback style", false, true,
vector<string>{"No annual fee", "Simple flat rewards"});
cards.emplace_back("Citi Custom Cash®", CASHBACK, 0, CreditRange{670,850},
"",
"Cash bonus (offer varies)", 200,
"5% back on top eligible category", false, true,
vector<string>{"No annual fee", "Great if one category dominates"});
cards.emplace_back("Capital One SavorOne", CASHBACK, 0, CreditRange{670,850},
"",
"Cash bonus (offer varies)", 200,
"Dining/entertainment cashback", false, true,
vector<string>{"No annual fee", "Good for dining/entertainment"});
cards.emplace_back("Capital One Quicksilver", CASHBACK, 0, CreditRange{670,850},
"",
"Cash bonus (offer varies)", 200,
"Simple flat cashback", false, true,
vector<string>{"No annual fee", "Easy flat rewards"});
cards.emplace_back("Blue Cash Everyday® (Amex)", CASHBACK, 0, CreditRange{670,850},
"",
"Cash bonus (offer varies)", 200,
"Everyday cashback categories", false, true,
vector<string>{"No annual fee", "Everyday category rewards"});
cards.emplace_back("Blue Cash Preferred® (Amex)", CASHBACK, 95, CreditRange{700,850},
"",
"Cash bonus (offer varies)", 250,
"Strong grocery/streaming cashback", false, true,
vector<string>{"Grocery earn cap may apply", "Popular cashback card"});
cards.emplace_back("U.S. Bank Cash+®", CASHBACK, 0, CreditRange{670,850},
"$5,000+ (Visa Signature typical if approved at that tier)",
"Cash bonus (offer varies)", 200,
"Choose-your-category cashback (terms vary)", false, true,
vector<string>{"No annual fee", "Customizable categories (terms vary)"});
cards.emplace_back("Bank of America® Customized Cash Rewards", CASHBACK, 0, CreditRange{670,850},
"",
"Cash bonus (offer varies)", 200,
"Choose-a-category cashback (terms vary)", false, true,
vector<string>{"No annual fee", "Good if one category dominates"});
cards.emplace_back("Bank of America® Unlimited Cash Rewards", CASHBACK, 0, CreditRange{670,850},
"",
"Cash bonus (offer varies)", 200,
"Flat cashback style", false, true,
vector<string>{"No annual fee", "Simple flat rewards"});
cards.emplace_back("Fidelity® Rewards Visa Signature®", CASHBACK, 0, CreditRange{670,850},
"$5,000+ (Visa Signature typical)",
"Bonus (offer varies)", 150,
"Flat cashback (often 2% style)", false, true,
vector<string>{"No annual fee", "Simple flat rewards"});
cards.emplace_back("PayPal Cashback Mastercard", CASHBACK, 0, CreditRange{670,850},
"",
"Bonus (offer varies)", 100,
"Flat cashback style", false, true,
vector<string>{"No annual fee", "Simple cashback approach"});
cards.emplace_back("Apple Card", CASHBACK, 0, CreditRange{660,850},
"",
"No typical bonus", 0,
"Apple Pay oriented cashback", false, true,
vector<string>{"Simple UX if in Apple ecosystem", "Cashback varies by purchase type"});
cards.emplace_back("Amazon Prime Visa", CASHBACK, 0, CreditRange{670,850},
"",
"Gift card/bonus (offer varies)", 150,
"Online/Amazon-focused rewards", false, true,
vector<string>{"Best for Amazon shoppers", "Rewards depend on program terms"});
cards.emplace_back("PNC Cash Unlimited®", CASHBACK, 0, CreditRange{670,850},
"",
"Cash bonus (offer varies)", 200,
"Flat cashback style", false, true,
vector<string>{"No annual fee", "Simple flat rewards"});
cards.emplace_back("Venmo Credit Card", CASHBACK, 0, CreditRange{670,850},
"",
"Bonus (offer varies)", 100,
"Top category cashback (terms vary)", false, true,
vector<string>{"No annual fee", "Best if one category dominates"});
cards.emplace_back("Discover it® Chrome", CASHBACK, 0, CreditRange{670,850},
"",
"Cashback match/bonus (varies)", 200,
"Gas + dining oriented cashback", false, true,
vector<string>{"No annual fee", "Simple category rewards"});
cards.emplace_back("U.S. Bank Shopper Cash Rewards®", CASHBACK, 95, CreditRange{700,850},
"",
"Cash bonus (offer varies)", 250,
"Retail-focused cashback (terms vary)", false, true,
vector<string>{"Better for specific shopping patterns", "Category terms vary"});
// LOW INTEREST
cards.emplace_back("Chase Slate Edge®", LOW_INTEREST, 0, CreditRange{670,850},
"",
"Intro APR (varies)", 0,
"Low interest / intro APR focused", false, true,
vector<string>{"Intro APR terms vary", "Useful for balance management"});
cards.emplace_back("Citi Simplicity®", LOW_INTEREST, 0, CreditRange{670,850},
"",
"Intro APR (varies)", 0,
"Balance transfer / intro APR focus", false, true,
vector<string>{"Intro APR terms vary", "Balance transfer terms may apply"});
cards.emplace_back("Wells Fargo Reflect®", LOW_INTEREST, 0, CreditRange{670,850},
"",
"Intro APR (varies)", 0,
"Long intro APR focus", false, true,
vector<string>{"Intro APR terms vary", "Balance transfer may apply"});
cards.emplace_back("BankAmericard®", LOW_INTEREST, 0, CreditRange{670,850},
"",
"Intro APR (varies)", 0,
"Intro APR + balance transfer focus", false, true,
vector<string>{"Intro APR terms vary", "Simple low-interest design"});
cards.emplace_back("Discover it® Balance Transfer", LOW_INTEREST, 0, CreditRange{670,850},
"",
"Intro APR (varies)", 0,
"Balance transfer + intro APR focus", false, true,
vector<string>{"Intro APR terms vary", "Balance transfer fees may apply"});
// STUDENT
cards.emplace_back("Discover it® Student Cash Back", STUDENT, 0, CreditRange{620,850},
"",
"Student bonus/match (varies)", 150,
"Student-friendly cashback", false, true,
vector<string>{"Student focused", "Free score tools (varies)"});
cards.emplace_back("Discover it® Student Chrome", STUDENT, 0, CreditRange{620,850},
"",
"Student match/bonus (varies)", 120,
"Student gas + dining cashback", false, true,
vector<string>{"Student oriented", "No annual fee"});
cards.emplace_back("Capital One Quicksilver Student", STUDENT, 0, CreditRange{620,850},
"",
"Student bonus (varies)", 120,
"Student-friendly cashback", false, true,
vector<string>{"Student oriented", "Simple rewards"});
cards.emplace_back("Chase Freedom Rise®", STUDENT, 0, CreditRange{620,850},
"",
"Bonus (offer varies)", 100,
"Entry-level cashback style", false, true,
vector<string>{"Good starter card", "No annual fee"});
}