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">
<mat-icon>savings</mat-icon>
<h2>Financial & Family Engagement</h2>
<span class="section-badge">6 Analysis Dimensions</span>
<span class="section-badge">5 Analysis Dimensions</span>
</div>
<div class="a-grid">
@@ -584,6 +584,7 @@
<div class="ac-head">
<div class="ac-icon"><mat-icon>savings</mat-icon></div>
<span class="ac-title">Savings Account Depth</span>
<span class="ac-tag">{{ activeSavingsPct(data) }}% Penetration</span>
</div>
<div class="chart-body flex-col">
@if (data.savingsBreakdown?.length > 0) {
@@ -605,6 +606,29 @@
</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 {
<p class="no-data">No savings data available.</p>
}
@@ -655,61 +679,6 @@
</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>
@@ -41,7 +41,7 @@ $accent-green: #10b981;
min-height: 100%;
font-family: $font;
color: $text-main;
padding-bottom: 40px;
padding-bottom: 24px;
position: relative;
overflow-x: hidden;
}
@@ -89,7 +89,7 @@ $accent-green: #10b981;
// ─── Hero Header (Redesigned - Light Mode Infographic Style) ──────────────────
.hero {
background: linear-gradient(135deg, #eef2ff 0%, #e0f2fe 50%, #f0fdf4 100%);
padding: 24px 32px 36px;
padding: 14px 24px 18px;
position: relative;
overflow: hidden;
border-bottom: 1px solid #cbd5e1;
@@ -132,9 +132,9 @@ $accent-green: #10b981;
gap: 4px;
color: #475569;
text-decoration: none;
font-size: 13px;
font-size: 12px;
font-weight: 600;
margin-bottom: 24px;
margin-bottom: 12px;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
z-index: 2;
@@ -160,14 +160,14 @@ $accent-green: #10b981;
.hero-body {
display: flex;
align-items: center;
gap: 28px;
gap: 18px;
position: relative;
z-index: 2;
@media (width <= 768px) {
flex-direction: column;
align-items: flex-start;
gap: 20px;
gap: 12px;
}
}
@@ -177,13 +177,13 @@ $accent-green: #10b981;
}
.hero-avatar {
width: 80px;
height: 80px;
width: 58px;
height: 58px;
border-radius: 50%;
background: linear-gradient(135deg, #6366f1 0%, #3b82f6 100%);
border: 2px solid #fff;
color: #fff;
font-size: 38px;
font-size: 26px;
font-weight: 800;
display: flex;
align-items: center;
@@ -249,21 +249,21 @@ $accent-green: #10b981;
}
.hero-name {
font-size: 32px;
font-size: 22px;
font-weight: 800;
color: #0f172a;
margin: 0 0 12px;
letter-spacing: -0.8px;
margin: 0 0 6px;
letter-spacing: -0.5px;
line-height: 1.15;
}
.hero-criteria {
display: flex;
align-items: center;
gap: 8px;
gap: 6px;
flex-wrap: wrap;
margin-top: 14px;
padding-top: 12px;
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid #cbd5e1;
}
@@ -376,8 +376,8 @@ $accent-green: #10b981;
.kpi-row {
display: grid;
grid-template-columns: 1.4fr repeat(5, 1fr);
gap: 16px;
padding: 24px 32px;
gap: 10px;
padding: 12px 24px;
position: relative;
z-index: 2;
@@ -387,8 +387,8 @@ $accent-green: #10b981;
@media (width <= 768px) {
grid-template-columns: repeat(2, 1fr);
padding: 16px 20px;
gap: 12px;
padding: 10px 16px;
gap: 8px;
}
@media (width <= 480px) {
@@ -398,11 +398,11 @@ $accent-green: #10b981;
.kpi {
background: white;
border-radius: $radius;
padding: 18px 20px;
border-radius: $radius-sm;
padding: 10px 14px;
display: flex;
align-items: center;
gap: 16px;
gap: 10px;
box-shadow: $shadow-sm;
border: 1px solid $border-color;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
@@ -425,18 +425,18 @@ $accent-green: #10b981;
}
.kpi-icon {
width: 48px;
height: 48px;
border-radius: 12px;
width: 34px;
height: 34px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
mat-icon {
font-size: 24px;
height: 24px;
width: 24px;
font-size: 18px;
height: 18px;
width: 18px;
}
}
@@ -445,54 +445,54 @@ $accent-green: #10b981;
}
.kpi-val {
font-size: 28px;
font-size: 20px;
font-weight: 800;
color: $text-main;
line-height: 1;
margin-bottom: 4px;
letter-spacing: -0.8px;
margin-bottom: 2px;
letter-spacing: -0.5px;
}
.featured-val {
color: #2563eb;
font-size: 32px;
font-size: 24px;
}
.kpi-lbl {
font-size: 11px;
font-size: 10px;
font-weight: 700;
color: $text-sub;
line-height: 1.35;
line-height: 1.3;
text-transform: uppercase;
letter-spacing: 0.3px;
}
.kpi-sub {
font-size: 11px;
font-size: 10px;
font-weight: 700;
color: #2563eb;
margin-top: 3px;
margin-top: 2px;
}
.kpi-sub-text {
font-size: 10px;
font-size: 9.5px;
font-weight: 500;
color: $text-sub;
margin-top: 2px;
margin-top: 1px;
}
// ─── Dashboard Sections Layout ───────────────────────────────────────────────
.dashboard-sections {
padding: 0 32px;
padding: 0 20px;
display: flex;
flex-direction: column;
gap: 36px;
gap: 20px;
position: relative;
z-index: 2;
@media (width <= 768px) {
padding: 0 20px;
gap: 28px;
padding: 0 14px;
gap: 16px;
}
}
@@ -521,20 +521,20 @@ $accent-green: #10b981;
.section-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
padding-bottom: 8px;
gap: 10px;
margin-bottom: 12px;
padding-bottom: 6px;
border-bottom: 2px solid #e2e8f0;
mat-icon {
color: $primary;
font-size: 26px;
height: 26px;
width: 26px;
font-size: 20px;
height: 20px;
width: 20px;
}
h2 {
font-size: 18px;
font-size: 15px;
font-weight: 800;
color: $text-main;
margin: 0;
@@ -543,11 +543,11 @@ $accent-green: #10b981;
.section-badge {
margin-left: auto;
font-size: 11px;
font-size: 10px;
font-weight: 700;
color: $primary;
background: rgb(79 70 229 / 8%);
padding: 3px 10px;
padding: 2px 8px;
border-radius: 99px;
text-transform: uppercase;
letter-spacing: 0.3px;
@@ -558,7 +558,7 @@ $accent-green: #10b981;
.a-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
gap: 12px;
@media (width <= 1024px) {
grid-template-columns: 1fr;
@@ -568,7 +568,7 @@ $accent-green: #10b981;
.a-card {
background: white;
border-radius: $radius;
padding: 24px;
padding: 16px;
box-shadow:
0 4px 20px -2px rgb(0 0 0 / 3%),
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);
display: flex;
flex-direction: column;
justify-content: space-between;
justify-content: flex-start;
&:hover {
box-shadow:
@@ -596,14 +596,14 @@ $accent-green: #10b981;
.ac-head {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 20px;
gap: 10px;
margin-bottom: 12px;
}
.ac-icon {
width: 40px;
height: 40px;
border-radius: 10px;
width: 32px;
height: 32px;
border-radius: 8px;
background: var(--ac-bg, #f1f5f9);
display: flex;
align-items: center;
@@ -611,15 +611,15 @@ $accent-green: #10b981;
flex-shrink: 0;
mat-icon {
font-size: 20px;
height: 20px;
width: 20px;
font-size: 16px;
height: 16px;
width: 16px;
color: var(--ac, #64748b);
}
}
.ac-title {
font-size: 15px;
font-size: 13px;
font-weight: 700;
color: $text-main;
flex: 1;
@@ -674,11 +674,11 @@ $accent-green: #10b981;
// ─── Donut Center Overlay ────────────────────────────────────────────────────
.donut-wrap {
position: relative;
width: 150px;
height: 150px;
width: 120px;
height: 120px;
flex-shrink: 0;
margin-bottom: 20px;
filter: drop-shadow(0 6px 12px rgb(0 0 0 / 4%));
margin-bottom: 12px;
filter: drop-shadow(0 4px 8px rgb(0 0 0 / 4%));
}
.donut-center {
@@ -698,7 +698,7 @@ $accent-green: #10b981;
}
.dc-val {
font-size: 24px;
font-size: 18px;
font-weight: 800;
color: $text-main;
line-height: 1;
@@ -719,14 +719,14 @@ $accent-green: #10b981;
width: 100%;
display: flex;
flex-direction: column;
gap: 8px;
gap: 5px;
}
.cl-row {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 12px;
gap: 8px;
padding: 5px 8px;
background: #f8fafc;
border-radius: 10px;
border: 1px solid #f1f5f9;
@@ -946,18 +946,18 @@ $accent-green: #10b981;
.loan-stats {
display: flex;
gap: 16px;
margin-bottom: 16px;
gap: 10px;
margin-bottom: 10px;
flex-wrap: wrap;
}
.loan-stat {
flex: 1;
min-width: 180px;
min-width: 160px;
background: #f8fafc;
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 16px;
padding: 10px;
transition: all 0.2s ease;
&.loan-stat-active {
@@ -997,12 +997,12 @@ $accent-green: #10b981;
}
.ls-num {
font-size: 34px;
font-size: 26px;
font-weight: 800;
color: $text-main;
line-height: 1;
margin-bottom: 4px;
letter-spacing: -1px;
margin-bottom: 3px;
letter-spacing: -0.8px;
}
.ls-pct {
@@ -1137,8 +1137,8 @@ $accent-green: #10b981;
.influence-insight {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
margin-top: 8px;
gap: 12px;
margin-top: 6px;
@media (width <= 1100px) {
grid-template-columns: repeat(2, 1fr);
@@ -1154,11 +1154,11 @@ $accent-green: #10b981;
flex-direction: column;
align-items: center;
text-align: center;
padding: 20px 16px;
padding: 12px;
background: #f8fafc;
border-radius: 14px;
border-radius: 12px;
border: 1px solid #e2e8f0;
gap: 6px;
gap: 4px;
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
&.im-highlight {
@@ -1175,41 +1175,75 @@ $accent-green: #10b981;
}
.im-icon {
width: 44px;
height: 44px;
border-radius: 12px;
width: 34px;
height: 34px;
border-radius: 9px;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 6px;
margin-bottom: 4px;
flex-shrink: 0;
mat-icon {
font-size: 22px;
height: 22px;
width: 22px;
font-size: 18px;
height: 18px;
width: 18px;
}
}
.im-val {
font-size: 2.4rem;
font-size: 1.8rem;
font-weight: 800;
line-height: 1;
color: $text-main;
letter-spacing: -1.5px;
letter-spacing: -1px;
}
.im-pct {
font-size: 1rem;
font-size: 0.85rem;
font-weight: 700;
}
.im-label {
font-size: 11px;
font-size: 10.5px;
color: $text-sub;
font-weight: 500;
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 ──────────────────────────────────────────────────────────────────
@@ -313,9 +313,9 @@ export class CandidateAnalysisComponent implements OnInit, OnDestroy {
{
data: data,
backgroundColor: colors,
borderWidth: 3,
borderWidth: breakdown.length === 1 ? 0 : 3,
borderColor: '#ffffff',
borderRadius: 5, // Rounded corners on doughnut segments for clean graphic style
borderRadius: breakdown.length === 1 ? 0 : 5,
hoverOffset: 6
}
]