feat(election): compact candidate analysis UI + savings card enhancements

- Reduce all spacing/padding/font sizes for denser layout (hero, KPI strip, section headers, cards, donuts, legends, influence metrics, loan stats)
- Fix Savings Account Depth card: remove empty space (justify-content flex-start), fix donut notch on single segment, add 3-stat grid (active/no-savings/penetration%) and insight note
- Remove Chart.js borderWidth/borderRadius on single-segment donuts to eliminate white gap artefact

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jaanu423
2026-06-30 12:29:17 +05:30
co-authored by Claude Sonnet 4.6
parent dac169ae15
commit 78b87fb6ce
3 changed files with 156 additions and 153 deletions
@@ -487,7 +487,7 @@
<div class="section-header"> <div class="section-header">
<mat-icon>savings</mat-icon> <mat-icon>savings</mat-icon>
<h2>Financial & Family Engagement</h2> <h2>Financial & Family Engagement</h2>
<span class="section-badge">6 Analysis Dimensions</span> <span class="section-badge">5 Analysis Dimensions</span>
</div> </div>
<div class="a-grid"> <div class="a-grid">
@@ -584,6 +584,7 @@
<div class="ac-head"> <div class="ac-head">
<div class="ac-icon"><mat-icon>savings</mat-icon></div> <div class="ac-icon"><mat-icon>savings</mat-icon></div>
<span class="ac-title">Savings Account Depth</span> <span class="ac-title">Savings Account Depth</span>
<span class="ac-tag">{{ activeSavingsPct(data) }}% Penetration</span>
</div> </div>
<div class="chart-body flex-col"> <div class="chart-body flex-col">
@if (data.savingsBreakdown?.length > 0) { @if (data.savingsBreakdown?.length > 0) {
@@ -605,6 +606,29 @@
</div> </div>
} }
</div> </div>
<div class="sav-stats">
<div class="sav-stat">
<div class="sav-num" style="color: #0ea5e9">{{ data.activeSavingsHolders }}</div>
<div class="sav-lbl">Active Savings</div>
</div>
<div class="sav-stat">
<div class="sav-num" style="color: #94a3b8">
{{ data.totalActiveShareholders - data.activeSavingsHolders }}
</div>
<div class="sav-lbl">No Savings</div>
</div>
<div class="sav-stat">
<div class="sav-num" style="color: #16a34a">{{ activeSavingsPct(data) }}%</div>
<div class="sav-lbl">Penetration Rate</div>
</div>
</div>
<div class="loan-note" style="margin-top: 10px; width: 100%">
<mat-icon>info_outline</mat-icon>
<span
>High savings penetration means members are financially active and have stronger ties to the
institution — a positive signal for candidate support.</span
>
</div>
} @else { } @else {
<p class="no-data">No savings data available.</p> <p class="no-data">No savings data available.</p>
} }
@@ -655,61 +679,6 @@
</div> </div>
</div> </div>
</div> </div>
<!-- Family Member Network (full width) -->
<div class="a-card a-full" style="--ac: #0d9488; --ac-bg: rgba(13, 148, 136, 0.08)">
<div class="ac-head">
<div class="ac-icon"><mat-icon>family_restroom</mat-icon></div>
<span class="ac-title">Family Member Network Influence</span>
</div>
<div class="fam-metrics-layout">
<div class="fam-metrics">
<div class="fam-metric">
<div class="fm-icon-badge" style="color: #0d9488; background: rgba(13, 148, 136, 0.08)">
<mat-icon>group</mat-icon>
</div>
<div class="fm-details">
<div class="fm-val">{{ data.shareholdersWithFamily }}</div>
<div class="fm-lbl">Shareholders with Family Records</div>
</div>
</div>
<div class="fam-sep"></div>
<div class="fam-metric">
<div class="fm-icon-badge" style="color: #0f766e; background: rgba(15, 118, 110, 0.08)">
<mat-icon>diversity_1</mat-icon>
</div>
<div class="fm-details">
<div class="fm-val">{{ data.totalFamilyMembers }}</div>
<div class="fm-lbl">Total Family Members Registered</div>
</div>
</div>
<div class="fam-sep"></div>
<div class="fam-metric">
<div class="fm-icon-badge" style="color: #115e59; background: rgba(17, 94, 89, 0.08)">
<mat-icon>calculate</mat-icon>
</div>
<div class="fm-details">
<div class="fm-val">
{{
data.shareholdersWithFamily > 0
? (data.totalFamilyMembers / data.shareholdersWithFamily | number: '1.1-1')
: '—'
}}
</div>
<div class="fm-lbl">Avg Registered Members / Household</div>
</div>
</div>
</div>
<div class="insight-note">
<mat-icon>insights</mat-icon>
<span
>Family records indicate broader community networks. A single voting shareholder with multiple family
records acts as a proxy shapeholder, often scaling voting decisions across their entire
household.</span
>
</div>
</div>
</div>
</div> </div>
</div> </div>
@@ -41,7 +41,7 @@ $accent-green: #10b981;
min-height: 100%; min-height: 100%;
font-family: $font; font-family: $font;
color: $text-main; color: $text-main;
padding-bottom: 40px; padding-bottom: 24px;
position: relative; position: relative;
overflow-x: hidden; overflow-x: hidden;
} }
@@ -89,7 +89,7 @@ $accent-green: #10b981;
// ─── Hero Header (Redesigned - Light Mode Infographic Style) ────────────────── // ─── Hero Header (Redesigned - Light Mode Infographic Style) ──────────────────
.hero { .hero {
background: linear-gradient(135deg, #eef2ff 0%, #e0f2fe 50%, #f0fdf4 100%); background: linear-gradient(135deg, #eef2ff 0%, #e0f2fe 50%, #f0fdf4 100%);
padding: 24px 32px 36px; padding: 14px 24px 18px;
position: relative; position: relative;
overflow: hidden; overflow: hidden;
border-bottom: 1px solid #cbd5e1; border-bottom: 1px solid #cbd5e1;
@@ -132,9 +132,9 @@ $accent-green: #10b981;
gap: 4px; gap: 4px;
color: #475569; color: #475569;
text-decoration: none; text-decoration: none;
font-size: 13px; font-size: 12px;
font-weight: 600; font-weight: 600;
margin-bottom: 24px; margin-bottom: 12px;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
position: relative; position: relative;
z-index: 2; z-index: 2;
@@ -160,14 +160,14 @@ $accent-green: #10b981;
.hero-body { .hero-body {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 28px; gap: 18px;
position: relative; position: relative;
z-index: 2; z-index: 2;
@media (width <= 768px) { @media (width <= 768px) {
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
gap: 20px; gap: 12px;
} }
} }
@@ -177,13 +177,13 @@ $accent-green: #10b981;
} }
.hero-avatar { .hero-avatar {
width: 80px; width: 58px;
height: 80px; height: 58px;
border-radius: 50%; border-radius: 50%;
background: linear-gradient(135deg, #6366f1 0%, #3b82f6 100%); background: linear-gradient(135deg, #6366f1 0%, #3b82f6 100%);
border: 2px solid #fff; border: 2px solid #fff;
color: #fff; color: #fff;
font-size: 38px; font-size: 26px;
font-weight: 800; font-weight: 800;
display: flex; display: flex;
align-items: center; align-items: center;
@@ -249,21 +249,21 @@ $accent-green: #10b981;
} }
.hero-name { .hero-name {
font-size: 32px; font-size: 22px;
font-weight: 800; font-weight: 800;
color: #0f172a; color: #0f172a;
margin: 0 0 12px; margin: 0 0 6px;
letter-spacing: -0.8px; letter-spacing: -0.5px;
line-height: 1.15; line-height: 1.15;
} }
.hero-criteria { .hero-criteria {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 8px; gap: 6px;
flex-wrap: wrap; flex-wrap: wrap;
margin-top: 14px; margin-top: 8px;
padding-top: 12px; padding-top: 8px;
border-top: 1px solid #cbd5e1; border-top: 1px solid #cbd5e1;
} }
@@ -376,8 +376,8 @@ $accent-green: #10b981;
.kpi-row { .kpi-row {
display: grid; display: grid;
grid-template-columns: 1.4fr repeat(5, 1fr); grid-template-columns: 1.4fr repeat(5, 1fr);
gap: 16px; gap: 10px;
padding: 24px 32px; padding: 12px 24px;
position: relative; position: relative;
z-index: 2; z-index: 2;
@@ -387,8 +387,8 @@ $accent-green: #10b981;
@media (width <= 768px) { @media (width <= 768px) {
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
padding: 16px 20px; padding: 10px 16px;
gap: 12px; gap: 8px;
} }
@media (width <= 480px) { @media (width <= 480px) {
@@ -398,11 +398,11 @@ $accent-green: #10b981;
.kpi { .kpi {
background: white; background: white;
border-radius: $radius; border-radius: $radius-sm;
padding: 18px 20px; padding: 10px 14px;
display: flex; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: 10px;
box-shadow: $shadow-sm; box-shadow: $shadow-sm;
border: 1px solid $border-color; border: 1px solid $border-color;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
@@ -425,18 +425,18 @@ $accent-green: #10b981;
} }
.kpi-icon { .kpi-icon {
width: 48px; width: 34px;
height: 48px; height: 34px;
border-radius: 12px; border-radius: 8px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-shrink: 0; flex-shrink: 0;
mat-icon { mat-icon {
font-size: 24px; font-size: 18px;
height: 24px; height: 18px;
width: 24px; width: 18px;
} }
} }
@@ -445,54 +445,54 @@ $accent-green: #10b981;
} }
.kpi-val { .kpi-val {
font-size: 28px; font-size: 20px;
font-weight: 800; font-weight: 800;
color: $text-main; color: $text-main;
line-height: 1; line-height: 1;
margin-bottom: 4px; margin-bottom: 2px;
letter-spacing: -0.8px; letter-spacing: -0.5px;
} }
.featured-val { .featured-val {
color: #2563eb; color: #2563eb;
font-size: 32px; font-size: 24px;
} }
.kpi-lbl { .kpi-lbl {
font-size: 11px; font-size: 10px;
font-weight: 700; font-weight: 700;
color: $text-sub; color: $text-sub;
line-height: 1.35; line-height: 1.3;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.3px; letter-spacing: 0.3px;
} }
.kpi-sub { .kpi-sub {
font-size: 11px; font-size: 10px;
font-weight: 700; font-weight: 700;
color: #2563eb; color: #2563eb;
margin-top: 3px; margin-top: 2px;
} }
.kpi-sub-text { .kpi-sub-text {
font-size: 10px; font-size: 9.5px;
font-weight: 500; font-weight: 500;
color: $text-sub; color: $text-sub;
margin-top: 2px; margin-top: 1px;
} }
// ─── Dashboard Sections Layout ─────────────────────────────────────────────── // ─── Dashboard Sections Layout ───────────────────────────────────────────────
.dashboard-sections { .dashboard-sections {
padding: 0 32px; padding: 0 20px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 36px; gap: 20px;
position: relative; position: relative;
z-index: 2; z-index: 2;
@media (width <= 768px) { @media (width <= 768px) {
padding: 0 20px; padding: 0 14px;
gap: 28px; gap: 16px;
} }
} }
@@ -521,20 +521,20 @@ $accent-green: #10b981;
.section-header { .section-header {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: 10px;
margin-bottom: 20px; margin-bottom: 12px;
padding-bottom: 8px; padding-bottom: 6px;
border-bottom: 2px solid #e2e8f0; border-bottom: 2px solid #e2e8f0;
mat-icon { mat-icon {
color: $primary; color: $primary;
font-size: 26px; font-size: 20px;
height: 26px; height: 20px;
width: 26px; width: 20px;
} }
h2 { h2 {
font-size: 18px; font-size: 15px;
font-weight: 800; font-weight: 800;
color: $text-main; color: $text-main;
margin: 0; margin: 0;
@@ -543,11 +543,11 @@ $accent-green: #10b981;
.section-badge { .section-badge {
margin-left: auto; margin-left: auto;
font-size: 11px; font-size: 10px;
font-weight: 700; font-weight: 700;
color: $primary; color: $primary;
background: rgb(79 70 229 / 8%); background: rgb(79 70 229 / 8%);
padding: 3px 10px; padding: 2px 8px;
border-radius: 99px; border-radius: 99px;
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.3px; letter-spacing: 0.3px;
@@ -558,7 +558,7 @@ $accent-green: #10b981;
.a-grid { .a-grid {
display: grid; display: grid;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
gap: 20px; gap: 12px;
@media (width <= 1024px) { @media (width <= 1024px) {
grid-template-columns: 1fr; grid-template-columns: 1fr;
@@ -568,7 +568,7 @@ $accent-green: #10b981;
.a-card { .a-card {
background: white; background: white;
border-radius: $radius; border-radius: $radius;
padding: 24px; padding: 16px;
box-shadow: box-shadow:
0 4px 20px -2px rgb(0 0 0 / 3%), 0 4px 20px -2px rgb(0 0 0 / 3%),
0 1px 3px rgb(0 0 0 / 2%); 0 1px 3px rgb(0 0 0 / 2%);
@@ -577,7 +577,7 @@ $accent-green: #10b981;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: flex-start;
&:hover { &:hover {
box-shadow: box-shadow:
@@ -596,14 +596,14 @@ $accent-green: #10b981;
.ac-head { .ac-head {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 12px; gap: 10px;
margin-bottom: 20px; margin-bottom: 12px;
} }
.ac-icon { .ac-icon {
width: 40px; width: 32px;
height: 40px; height: 32px;
border-radius: 10px; border-radius: 8px;
background: var(--ac-bg, #f1f5f9); background: var(--ac-bg, #f1f5f9);
display: flex; display: flex;
align-items: center; align-items: center;
@@ -611,15 +611,15 @@ $accent-green: #10b981;
flex-shrink: 0; flex-shrink: 0;
mat-icon { mat-icon {
font-size: 20px; font-size: 16px;
height: 20px; height: 16px;
width: 20px; width: 16px;
color: var(--ac, #64748b); color: var(--ac, #64748b);
} }
} }
.ac-title { .ac-title {
font-size: 15px; font-size: 13px;
font-weight: 700; font-weight: 700;
color: $text-main; color: $text-main;
flex: 1; flex: 1;
@@ -674,11 +674,11 @@ $accent-green: #10b981;
// ─── Donut Center Overlay ──────────────────────────────────────────────────── // ─── Donut Center Overlay ────────────────────────────────────────────────────
.donut-wrap { .donut-wrap {
position: relative; position: relative;
width: 150px; width: 120px;
height: 150px; height: 120px;
flex-shrink: 0; flex-shrink: 0;
margin-bottom: 20px; margin-bottom: 12px;
filter: drop-shadow(0 6px 12px rgb(0 0 0 / 4%)); filter: drop-shadow(0 4px 8px rgb(0 0 0 / 4%));
} }
.donut-center { .donut-center {
@@ -698,7 +698,7 @@ $accent-green: #10b981;
} }
.dc-val { .dc-val {
font-size: 24px; font-size: 18px;
font-weight: 800; font-weight: 800;
color: $text-main; color: $text-main;
line-height: 1; line-height: 1;
@@ -719,14 +719,14 @@ $accent-green: #10b981;
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 8px; gap: 5px;
} }
.cl-row { .cl-row {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 10px; gap: 8px;
padding: 8px 12px; padding: 5px 8px;
background: #f8fafc; background: #f8fafc;
border-radius: 10px; border-radius: 10px;
border: 1px solid #f1f5f9; border: 1px solid #f1f5f9;
@@ -946,18 +946,18 @@ $accent-green: #10b981;
.loan-stats { .loan-stats {
display: flex; display: flex;
gap: 16px; gap: 10px;
margin-bottom: 16px; margin-bottom: 10px;
flex-wrap: wrap; flex-wrap: wrap;
} }
.loan-stat { .loan-stat {
flex: 1; flex: 1;
min-width: 180px; min-width: 160px;
background: #f8fafc; background: #f8fafc;
border: 1px solid #e2e8f0; border: 1px solid #e2e8f0;
border-radius: 12px; border-radius: 12px;
padding: 16px; padding: 10px;
transition: all 0.2s ease; transition: all 0.2s ease;
&.loan-stat-active { &.loan-stat-active {
@@ -997,12 +997,12 @@ $accent-green: #10b981;
} }
.ls-num { .ls-num {
font-size: 34px; font-size: 26px;
font-weight: 800; font-weight: 800;
color: $text-main; color: $text-main;
line-height: 1; line-height: 1;
margin-bottom: 4px; margin-bottom: 3px;
letter-spacing: -1px; letter-spacing: -0.8px;
} }
.ls-pct { .ls-pct {
@@ -1137,8 +1137,8 @@ $accent-green: #10b981;
.influence-insight { .influence-insight {
display: grid; display: grid;
grid-template-columns: repeat(4, 1fr); grid-template-columns: repeat(4, 1fr);
gap: 16px; gap: 12px;
margin-top: 8px; margin-top: 6px;
@media (width <= 1100px) { @media (width <= 1100px) {
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
@@ -1154,11 +1154,11 @@ $accent-green: #10b981;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
text-align: center; text-align: center;
padding: 20px 16px; padding: 12px;
background: #f8fafc; background: #f8fafc;
border-radius: 14px; border-radius: 12px;
border: 1px solid #e2e8f0; border: 1px solid #e2e8f0;
gap: 6px; gap: 4px;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
&.im-highlight { &.im-highlight {
@@ -1175,41 +1175,75 @@ $accent-green: #10b981;
} }
.im-icon { .im-icon {
width: 44px; width: 34px;
height: 44px; height: 34px;
border-radius: 12px; border-radius: 9px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin-bottom: 6px; margin-bottom: 4px;
flex-shrink: 0; flex-shrink: 0;
mat-icon { mat-icon {
font-size: 22px; font-size: 18px;
height: 22px; height: 18px;
width: 22px; width: 18px;
} }
} }
.im-val { .im-val {
font-size: 2.4rem; font-size: 1.8rem;
font-weight: 800; font-weight: 800;
line-height: 1; line-height: 1;
color: $text-main; color: $text-main;
letter-spacing: -1.5px; letter-spacing: -1px;
} }
.im-pct { .im-pct {
font-size: 1rem; font-size: 0.85rem;
font-weight: 700; font-weight: 700;
} }
.im-label { .im-label {
font-size: 11px; font-size: 10.5px;
color: $text-sub; color: $text-sub;
font-weight: 500; font-weight: 500;
line-height: 1.4; line-height: 1.4;
max-width: 160px; max-width: 140px;
}
// ─── Savings Stats Grid ──────────────────────────────────────────────────────
.sav-stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
width: 100%;
margin-top: 10px;
}
.sav-stat {
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 10px 8px;
text-align: center;
}
.sav-num {
font-size: 22px;
font-weight: 800;
line-height: 1;
margin-bottom: 3px;
letter-spacing: -0.5px;
}
.sav-lbl {
font-size: 9.5px;
color: #64748b;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.3px;
line-height: 1.3;
} }
// ─── Helpers ────────────────────────────────────────────────────────────────── // ─── Helpers ──────────────────────────────────────────────────────────────────
@@ -313,9 +313,9 @@ export class CandidateAnalysisComponent implements OnInit, OnDestroy {
{ {
data: data, data: data,
backgroundColor: colors, backgroundColor: colors,
borderWidth: 3, borderWidth: breakdown.length === 1 ? 0 : 3,
borderColor: '#ffffff', borderColor: '#ffffff',
borderRadius: 5, // Rounded corners on doughnut segments for clean graphic style borderRadius: breakdown.length === 1 ? 0 : 5,
hoverOffset: 6 hoverOffset: 6
} }
] ]