feat: letter-mgmt module + digital UPI/IMPS/ATM-POS reports + header redesign

letter-mgmt (new module):
- Notice page: overdue loan list with filters, KPI strip, bulk actions,
  call disposition logging (Promise-to-Pay, Not Reachable, etc.), Speed
  Post dispatch dialog with consignment + post-office tracking,
  PDF preview + download, call-contact dialog with primary / alt /
  family / nominee numbers and per-contact disposition history.
- Certificates: Loan Interest, Share, Balance, Form 60 (no-PAN flow),
  Form 15G/H (auto age-based), Hypothecation, NOC - each with shared
  preview dialog (six tone variants) and jsPDF generator using a
  common letterhead / summary-grid / signature-seal helper.

digital:
- IMPS module: report (P2A/P2P/Aadhaar tabs) + entry form with
  MMID/mobile lookup, NPCI switch resolve-to-A/C+IFSC BANL flow,
  sender autocomplete, hide sender field for P2P/Aadhaar, charges +
  18% GST + waive checkbox, per-sender today-only history dialog.
- UPI report with app-mix tab and full transaction filtering.
- ATM/POS/ECOM hub with three ticket cards routing to today/status/cycle.
- NEFT/RTGS entry: sender-account autocomplete, charges + GST + waive,
  field reorder (sender before amount), NEFT cap / RTGS floor
  validation, History button opening per-sender today's transactions.
- NEFT/RTGS report: horizontal-bar trend leaderboard replacing tall bars.

shell + theme:
- Header redesigned with IBM Plex Sans 12/500 typography, 11-column grid
  per row, top-accent hover indicator, bordered cells via grid-gap.
- Live KPI ticker below menu (CASH, TXN TODAY, CLEARING IN/OUT, NPA,
  CASA, CD RATIO, RBI CIRC, REPO) with marquee scroll, pause-on-hover,
  brand-blue gradient, amber bottom strip, pulsing red LIVE chip.
- Sidebar 'KYC Management' -> 'KYC & ReKYC Management'.
- Letter Mgmt / certificate menu items wired with green icon hints for
  items that have a working page.

access-control:
- MFA card layout compacted; method header inlines back button + title.

icons:
- Register faTruck/TruckFast, faEllipsisV/H, faCommentDots.
- Add IBM Plex Sans Google Fonts import to index.html.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-05-19 10:09:06 +05:30
co-authored by Claude Opus 4.7
parent 4322f37c6d
commit 325228c514
138 changed files with 27473 additions and 3997 deletions
+266
View File
@@ -0,0 +1,266 @@
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*
* Shared "client action" page styling — used by Activate / Close / Withdraw /
* Reject / Reactivate etc. screens. Consumer overrides --ca-accent on :host
* to colour the hero card per action.
*/
@mixin client-action-styles {
:host {
--ca-navy: #0b1e3d;
--ca-blue: #1d4ed8;
--ca-blue-l: #eff6ff;
--ca-green: #15803d;
--ca-green-l: #dcfce7;
--ca-amber: #b45309;
--ca-amber-l: #fef3c7;
--ca-red: #c0392b;
--ca-red-l: #fee2e2;
--ca-g50: #f8fafc;
--ca-g100: #f1f5f9;
--ca-g200: #e2e8f0;
--ca-g500: #64748b;
--ca-g700: #334155;
--ca-g900: #0f172a;
--ca-accent: var(--ca-blue);
--ca-accent-l: var(--ca-blue-l);
--ca-accent-fg: #fff;
display: block;
font-family:
'DM Sans',
system-ui,
-apple-system,
sans-serif;
color: var(--ca-g900);
font-size: 13.5px;
}
.ca-root {
padding: 18px 22px;
max-width: 720px;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 16px;
}
/* ───────── HERO ───────── */
.ca-hero {
background: linear-gradient(135deg, var(--ca-navy) 0%, var(--ca-accent) 100%);
border-radius: 14px;
padding: 22px 24px;
display: flex;
align-items: center;
gap: 18px;
box-shadow:
0 10px 24px rgb(11 30 61 / 18%),
0 4px 10px rgb(11 30 61 / 12%);
color: #fff;
position: relative;
overflow: hidden;
}
.ca-hero::before {
content: '';
position: absolute;
top: -60px;
right: -60px;
width: 220px;
height: 220px;
border-radius: 50%;
background: radial-gradient(circle, rgb(255 255 255 / 8%) 0%, transparent 70%);
pointer-events: none;
}
.ca-hero-icon {
width: 56px;
height: 56px;
border-radius: 14px;
background: rgb(255 255 255 / 16%);
border: 1px solid rgb(255 255 255 / 22%);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 26px;
flex-shrink: 0;
}
.ca-hero-text {
flex: 1;
min-width: 0;
}
.ca-hero-eyebrow {
font-size: 10.5px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.12em;
color: rgb(255 255 255 / 70%);
margin-bottom: 4px;
}
.ca-hero-title {
margin: 0;
font-size: 20px;
font-weight: 700;
letter-spacing: 0.01em;
color: #fff;
line-height: 1.2;
}
.ca-hero-sub {
margin: 6px 0 0;
font-size: 13px;
color: rgb(255 255 255 / 82%);
line-height: 1.5;
max-width: 540px;
}
/* ───────── CARD ───────── */
.ca-card {
background: #fff;
border: 1px solid var(--ca-g200);
border-radius: 14px;
overflow: hidden;
box-shadow: 0 1px 2px rgb(15 23 42 / 4%);
}
.ca-card-body {
padding: 22px 24px;
display: flex;
flex-direction: column;
gap: 14px;
}
.ca-form-row {
display: flex;
flex-direction: column;
gap: 4px;
mat-form-field {
width: 100%;
}
}
.ca-row-label {
font-size: 10.5px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--ca-g500);
margin-bottom: 2px;
}
/* Notice / warning strip below form for destructive actions */
.ca-notice {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 12px 14px;
border-radius: 10px;
background: var(--ca-accent-l);
border: 1px solid;
border-color: color-mix(in srgb, var(--ca-accent) 30%, transparent);
color: var(--ca-g700);
font-size: 12.5px;
line-height: 1.5;
fa-icon {
color: var(--ca-accent);
font-size: 14px;
margin-top: 1px;
flex-shrink: 0;
}
strong {
color: var(--ca-accent);
font-weight: 700;
}
}
/* ───────── FOOTER ACTIONS ───────── */
.ca-actions {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 10px;
padding: 14px 24px;
background: var(--ca-g50);
border-top: 1px solid var(--ca-g200);
}
.ca-btn {
display: inline-flex;
align-items: center;
gap: 8px;
height: 38px;
padding: 0 18px;
border-radius: 10px;
font-size: 13px;
font-weight: 700;
letter-spacing: 0.02em;
cursor: pointer;
text-decoration: none;
white-space: nowrap;
font-family: inherit;
transition: all 0.16s ease;
border: 1px solid transparent;
fa-icon {
font-size: 12px;
}
&:disabled {
cursor: not-allowed;
opacity: 0.55;
}
}
.ca-btn--cancel {
background: #fff;
color: var(--ca-g700);
border-color: var(--ca-g200);
&:hover {
background: var(--ca-g100);
border-color: var(--ca-g500);
}
}
.ca-btn--confirm {
background: linear-gradient(135deg, var(--ca-accent) 0%, var(--ca-navy) 100%);
color: var(--ca-accent-fg);
box-shadow:
0 6px 16px color-mix(in srgb, var(--ca-accent) 30%, transparent),
inset 0 1px 0 rgb(255 255 255 / 18%);
&:hover:not(:disabled) {
transform: translateY(-1px);
box-shadow:
0 10px 22px color-mix(in srgb, var(--ca-accent) 40%, transparent),
inset 0 1px 0 rgb(255 255 255 / 22%);
}
&:active {
transform: translateY(0);
}
}
/* Material form-field polish */
::ng-deep .ca-card mat-form-field {
width: 100%;
.mat-mdc-form-field-infix {
padding-top: 10px;
padding-bottom: 10px;
}
}
}
+605
View File
@@ -0,0 +1,605 @@
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*
* Shared "product list" page styling — used by Saving / Loan / FD / RD / Share /
* Current Account product list screens. Components @use this partial so the
* card / KPI / search / table / action-button look is identical across all
* product types.
*
* Theming hook: each consumer may override `--pl-accent` and `--pl-accent-l`
* on the host element to tint the header/icon accent per product type.
*/
@mixin product-list-styles {
:host {
--pl-navy: #0b1e3d;
--pl-blue: #1d4ed8;
--pl-blue-l: #eff6ff;
--pl-teal: #0e7e6b;
--pl-teal-l: #ccfbf1;
--pl-purple: #6d28d9;
--pl-purple-l: #ede9fe;
--pl-amber: #b45309;
--pl-amber-l: #fef3c7;
--pl-green: #15803d;
--pl-green-l: #dcfce7;
--pl-red: #c0392b;
--pl-red-l: #fee2e2;
--pl-g50: #f8fafc;
--pl-g100: #f1f5f9;
--pl-g200: #e2e8f0;
--pl-g500: #64748b;
--pl-g700: #334155;
--pl-g900: #0f172a;
/* Default accent — consumer can override on :host */
--pl-accent: var(--pl-blue);
--pl-accent-l: var(--pl-blue-l);
display: block;
font-family:
'DM Sans',
system-ui,
-apple-system,
sans-serif;
background: var(--pl-g50);
color: var(--pl-g900);
font-size: 13.5px;
}
.pl-root {
padding: 18px 22px;
max-width: 1400px;
margin: 0 auto;
}
/* ───────── HEADER ───────── */
.pl-header {
display: flex;
align-items: center;
gap: 16px;
flex-wrap: wrap;
margin-bottom: 14px;
}
.pl-header-left {
display: flex;
align-items: center;
gap: 14px;
flex: 1;
min-width: 320px;
}
.pl-header-ic {
width: 48px;
height: 48px;
border-radius: 14px;
background: linear-gradient(135deg, var(--pl-accent) 0%, var(--pl-navy) 100%);
color: #fff;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 20px;
flex-shrink: 0;
box-shadow:
0 6px 16px rgb(29 78 216 / 25%),
inset 0 1px 0 rgb(255 255 255 / 22%);
}
.pl-header-text {
h2 {
margin: 0;
font-size: 18px;
font-weight: 700;
color: var(--pl-g900);
letter-spacing: 0.01em;
}
p {
margin: 3px 0 0;
font-size: 12px;
color: var(--pl-g500);
line-height: 1.4;
}
}
.pl-header-right {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
.pl-count-chip {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 6px 14px 6px 12px;
border-radius: 999px;
background: var(--pl-accent-l);
color: var(--pl-accent);
border: 1px solid #dbeafe;
font-size: 12px;
font-weight: 600;
strong {
font-size: 14px;
font-weight: 700;
color: var(--pl-navy);
}
span {
text-transform: uppercase;
letter-spacing: 0.06em;
font-size: 10.5px;
}
fa-icon {
font-size: 11px;
}
}
.pl-create-btn,
.pl-secondary-btn {
display: inline-flex;
align-items: center;
gap: 8px;
height: 38px;
padding: 0 16px;
border-radius: 10px;
font-size: 13px;
font-weight: 700;
letter-spacing: 0.02em;
cursor: pointer;
text-decoration: none;
white-space: nowrap;
font-family: inherit;
transition: all 0.16s ease;
border: 0;
fa-icon {
font-size: 12px;
}
}
.pl-create-btn {
background: linear-gradient(135deg, var(--pl-accent) 0%, var(--pl-navy) 100%);
color: #fff;
box-shadow:
0 6px 16px rgb(29 78 216 / 30%),
inset 0 1px 0 rgb(255 255 255 / 18%);
&:hover {
transform: translateY(-1px);
box-shadow:
0 10px 22px rgb(29 78 216 / 38%),
inset 0 1px 0 rgb(255 255 255 / 22%);
}
&:active {
transform: translateY(0);
}
}
.pl-secondary-btn {
background: #fff;
color: var(--pl-accent);
border: 1px solid var(--pl-g200);
&:hover {
background: var(--pl-accent-l);
border-color: var(--pl-accent);
}
}
/* ───────── KPI STRIP ───────── */
.pl-kpis {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
margin-bottom: 14px;
}
@media (width <= 720px) {
.pl-kpis {
grid-template-columns: 1fr;
}
}
.pl-kpi {
background: #fff;
border: 1px solid var(--pl-g200);
border-radius: 12px;
padding: 12px 16px;
display: flex;
align-items: center;
gap: 14px;
box-shadow: 0 1px 2px rgb(15 23 42 / 4%);
border-left-width: 4px;
}
.pl-kpi-ic {
width: 40px;
height: 40px;
border-radius: 10px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 16px;
flex-shrink: 0;
}
.pl-kpi-n {
font-size: 20px;
font-weight: 700;
color: var(--pl-g900);
line-height: 1.1;
}
.pl-kpi-l {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--pl-g500);
margin-top: 4px;
}
.pl-kpi--blue {
border-left-color: var(--pl-blue);
.pl-kpi-ic {
background: var(--pl-blue-l);
color: var(--pl-blue);
}
}
.pl-kpi--teal {
border-left-color: var(--pl-teal);
.pl-kpi-ic {
background: var(--pl-teal-l);
color: var(--pl-teal);
}
}
.pl-kpi--purple {
border-left-color: var(--pl-purple);
.pl-kpi-ic {
background: var(--pl-purple-l);
color: var(--pl-purple);
}
}
/* ───────── CARD + TOOLBAR ───────── */
.pl-card {
background: #fff;
border: 1px solid var(--pl-g200);
border-radius: 14px;
overflow: hidden;
box-shadow: 0 1px 2px rgb(15 23 42 / 4%);
}
.pl-toolbar {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 16px;
background: #fff;
border-bottom: 1px solid var(--pl-g200);
}
.pl-search {
position: relative;
flex: 1;
max-width: 480px;
input {
width: 100%;
box-sizing: border-box;
padding: 9px 14px 9px 36px;
border: 1px solid var(--pl-g200);
border-radius: 10px;
background: var(--pl-g50);
font-size: 13px;
color: var(--pl-g900);
font-family: inherit;
&::placeholder {
color: #94a3b8;
}
&:focus {
outline: none;
border-color: var(--pl-accent);
background: #fff;
box-shadow: 0 0 0 3px rgb(29 78 216 / 14%);
}
}
}
.pl-search-ic {
position: absolute;
top: 50%;
left: 13px;
transform: translateY(-50%);
color: #94a3b8;
font-size: 13px;
pointer-events: none;
}
/* ───────── TABLE ───────── */
.pl-table-wrap {
overflow-x: auto;
}
.pl-table {
width: 100%;
background: #fff;
th.mat-mdc-header-cell {
background: var(--pl-g50);
color: var(--pl-g700);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 12px 16px;
border-bottom: 1px solid var(--pl-g200);
white-space: nowrap;
&.num {
text-align: right;
}
}
td.mat-mdc-cell {
padding: 12px 16px;
border-bottom: 1px solid var(--pl-g100);
font-size: 13px;
color: var(--pl-g900);
vertical-align: middle;
&.num {
text-align: right;
}
}
}
.pl-row {
cursor: pointer;
transition: background 0.14s ease;
&:hover {
background: var(--pl-accent-l);
}
}
.pl-product-cell {
display: flex;
align-items: center;
gap: 12px;
}
.pl-product-icon {
width: 36px;
height: 36px;
border-radius: 10px;
background: var(--pl-accent-l);
color: var(--pl-accent);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 14px;
flex-shrink: 0;
transition: all 0.14s ease;
}
.pl-row:hover .pl-product-icon {
background: var(--pl-accent);
color: #fff;
transform: scale(1.04);
}
.pl-product-meta {
display: flex;
flex-direction: column;
line-height: 1.25;
}
.pl-product-name {
font-size: 13.5px;
font-weight: 600;
color: var(--pl-g900);
}
.pl-product-sub {
font-size: 11px;
color: var(--pl-g500);
margin-top: 2px;
span {
margin: 0 4px;
}
}
/* Short Name pill */
.pl-tag {
display: inline-flex;
align-items: center;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.05em;
text-transform: uppercase;
padding: 3px 9px;
border-radius: 999px;
background: var(--pl-g100);
color: var(--pl-g700);
font-family: 'JetBrains Mono', ui-monospace, Menlo, Consolas, monospace;
}
/* Currency pill */
.pl-currency-pill {
display: inline-flex;
align-items: center;
font-size: 11px;
font-weight: 700;
padding: 3px 9px;
border-radius: 999px;
background: var(--pl-teal-l);
color: var(--pl-teal);
border: 1px solid #99f6e4;
font-family: 'JetBrains Mono', ui-monospace, Menlo, Consolas, monospace;
}
/* Status pill */
.pl-status-pill {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
padding: 3px 10px;
border-radius: 999px;
fa-icon {
font-size: 9.5px;
}
&--active {
background: var(--pl-green-l);
color: var(--pl-green);
border: 1px solid #bbf7d0;
}
&--inactive {
background: var(--pl-g100);
color: var(--pl-g700);
border: 1px solid var(--pl-g200);
}
}
.muted {
color: var(--pl-g500);
}
/* Action buttons */
.pl-action-btn {
display: inline-flex;
align-items: center;
gap: 5px;
height: 30px;
padding: 0 12px;
border-radius: 8px;
border: 1px solid;
font-size: 11.5px;
font-weight: 600;
cursor: pointer;
text-decoration: none;
font-family: inherit;
margin-left: 6px;
transition: all 0.14s ease;
fa-icon {
font-size: 11px;
}
&:first-child {
margin-left: 0;
}
&--view {
background: var(--pl-blue-l);
color: var(--pl-blue);
border-color: #dbeafe;
&:hover {
background: var(--pl-blue);
color: #fff;
border-color: var(--pl-blue);
}
}
&--edit {
background: var(--pl-amber-l);
color: var(--pl-amber);
border-color: #fde68a;
&:hover {
background: var(--pl-amber);
color: #fff;
border-color: var(--pl-amber);
}
}
}
/* No-match strip */
.pl-no-match {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 28px 16px;
background: var(--pl-g50);
color: var(--pl-g500);
font-size: 13px;
font-weight: 500;
border-top: 1px dashed var(--pl-g200);
fa-icon {
font-size: 13px;
}
}
/* ───────── EMPTY STATE ───────── */
.pl-empty {
text-align: center;
padding: 50px 24px;
}
.pl-empty-ic {
width: 64px;
height: 64px;
margin: 0 auto 16px;
border-radius: 16px;
background: var(--pl-accent-l);
color: var(--pl-accent);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 26px;
}
.pl-empty h3 {
margin: 0 0 8px;
font-size: 16px;
font-weight: 700;
color: var(--pl-g900);
}
.pl-empty p {
margin: 0 0 20px;
font-size: 13px;
color: var(--pl-g500);
line-height: 1.5;
max-width: 420px;
margin-left: auto;
margin-right: auto;
}
/* Paginator polish */
::ng-deep .pl-table-wrap .mat-mdc-paginator {
background: var(--pl-g50);
border-top: 1px solid var(--pl-g200);
font-family: inherit;
}
}
+575
View File
@@ -0,0 +1,575 @@
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/*
* Shared "product view" styling — used by the general-tab views of
* Saving / Loan / FD / RD / Share / Current product types.
*
* Theming hook: each consumer may override `--pv-accent` / `--pv-accent-l`
* on the host to tint the hero gradient per product type.
*/
@mixin product-view-styles {
:host {
--pv-navy: #0b1e3d;
--pv-blue: #1d4ed8;
--pv-blue-l: #eff6ff;
--pv-teal: #0e7e6b;
--pv-teal-l: #ccfbf1;
--pv-purple: #6d28d9;
--pv-purple-l: #ede9fe;
--pv-indigo: #3730a3;
--pv-indigo-l: #e0e7ff;
--pv-amber: #b45309;
--pv-amber-l: #fef3c7;
--pv-green: #15803d;
--pv-green-l: #dcfce7;
--pv-red: #c0392b;
--pv-red-l: #fee2e2;
--pv-pink: #be185d;
--pv-pink-l: #fce7f3;
--pv-g50: #f8fafc;
--pv-g100: #f1f5f9;
--pv-g200: #e2e8f0;
--pv-g500: #64748b;
--pv-g700: #334155;
--pv-g900: #0f172a;
--pv-accent: var(--pv-blue);
--pv-accent-l: var(--pv-blue-l);
display: block;
font-family:
'DM Sans',
system-ui,
-apple-system,
sans-serif;
color: var(--pv-g900);
font-size: 13.5px;
}
.pv-root {
padding: 4px 4px 24px;
display: flex;
flex-direction: column;
gap: 14px;
}
/* ───────── HERO ───────── */
.pv-hero {
background: linear-gradient(135deg, var(--pv-navy) 0%, var(--pv-accent) 100%);
border-radius: 14px;
padding: 22px 22px 22px 18px;
display: flex;
align-items: flex-start;
gap: 18px;
box-shadow:
0 10px 24px rgb(11 30 61 / 18%),
0 4px 10px rgb(11 30 61 / 12%);
color: #fff;
position: relative;
overflow: hidden;
}
.pv-hero::before {
content: '';
position: absolute;
top: -60px;
right: -60px;
width: 240px;
height: 240px;
border-radius: 50%;
background: radial-gradient(circle, rgb(255 255 255 / 8%) 0%, transparent 70%);
pointer-events: none;
}
.pv-hero-left {
display: flex;
align-items: flex-start;
gap: 16px;
flex: 1;
min-width: 0;
}
.pv-hero-icon {
width: 56px;
height: 56px;
border-radius: 14px;
background: rgb(255 255 255 / 16%);
border: 1px solid rgb(255 255 255 / 22%);
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 24px;
flex-shrink: 0;
}
.pv-hero-text {
flex: 1;
min-width: 0;
}
.pv-hero-eyebrow {
font-size: 10.5px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.12em;
color: rgb(255 255 255 / 70%);
margin-bottom: 4px;
}
.pv-hero-title {
margin: 0 0 10px;
font-size: 22px;
font-weight: 700;
letter-spacing: 0.01em;
color: #fff;
line-height: 1.2;
}
.pv-hero-meta {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
.pv-hero-desc {
margin: 12px 0 0;
font-size: 13px;
color: rgb(255 255 255 / 82%);
line-height: 1.5;
max-width: 720px;
}
.pv-edit-btn {
display: inline-flex;
align-items: center;
gap: 8px;
height: 38px;
padding: 0 16px;
border-radius: 10px;
background: rgb(255 255 255 / 14%);
color: #fff;
border: 1px solid rgb(255 255 255 / 28%);
font-size: 13px;
font-weight: 700;
letter-spacing: 0.02em;
cursor: pointer;
text-decoration: none;
white-space: nowrap;
font-family: inherit;
flex-shrink: 0;
transition: all 0.16s ease;
fa-icon {
font-size: 12px;
}
&:hover {
background: rgb(255 255 255 / 24%);
border-color: rgb(255 255 255 / 50%);
}
}
/* ───────── PILLS ───────── */
.pv-pill {
display: inline-flex;
align-items: center;
gap: 5px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
padding: 4px 10px;
border-radius: 999px;
font-family: inherit;
fa-icon {
font-size: 9.5px;
}
&--code {
background: rgb(255 255 255 / 18%);
color: #fff;
border: 1px solid rgb(255 255 255 / 24%);
font-family: 'JetBrains Mono', ui-monospace, Menlo, Consolas, monospace;
}
&--teal {
background: var(--pv-teal-l);
color: var(--pv-teal);
border: 1px solid #99f6e4;
}
&--blue {
background: var(--pv-blue-l);
color: var(--pv-blue);
border: 1px solid #dbeafe;
}
&--green {
background: var(--pv-green-l);
color: var(--pv-green);
border: 1px solid #bbf7d0;
}
&--amber {
background: var(--pv-amber-l);
color: var(--pv-amber);
border: 1px solid #fde68a;
}
&--red {
background: var(--pv-red-l);
color: var(--pv-red);
border: 1px solid #fecaca;
}
&--gray {
background: var(--pv-g100);
color: var(--pv-g700);
border: 1px solid var(--pv-g200);
}
}
/* ───────── SECTION ───────── */
.pv-section {
background: #fff;
border: 1px solid var(--pv-g200);
border-radius: 12px;
padding: 16px 20px;
box-shadow: 0 1px 2px rgb(15 23 42 / 4%);
}
.pv-section-h {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 14px;
padding-bottom: 12px;
border-bottom: 1px dashed var(--pv-g200);
h3 {
margin: 0;
font-size: 14px;
font-weight: 700;
color: var(--pv-g900);
letter-spacing: 0.02em;
}
}
.pv-section-ic {
width: 36px;
height: 36px;
border-radius: 10px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 14px;
flex-shrink: 0;
&--blue {
background: var(--pv-blue-l);
color: var(--pv-blue);
}
&--teal {
background: var(--pv-teal-l);
color: var(--pv-teal);
}
&--purple {
background: var(--pv-purple-l);
color: var(--pv-purple);
}
&--indigo {
background: var(--pv-indigo-l);
color: var(--pv-indigo);
}
&--amber {
background: var(--pv-amber-l);
color: var(--pv-amber);
}
&--green {
background: var(--pv-green-l);
color: var(--pv-green);
}
&--red {
background: var(--pv-red-l);
color: var(--pv-red);
}
&--pink {
background: var(--pv-pink-l);
color: var(--pv-pink);
}
}
.pv-sub-h {
margin: 16px 0 10px;
font-size: 12px;
font-weight: 700;
color: var(--pv-g700);
text-transform: uppercase;
letter-spacing: 0.06em;
}
/* ───────── KEY/VALUE GRID ───────── */
.pv-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px 24px;
}
.pv-grid--3 {
grid-template-columns: repeat(3, 1fr);
}
@media (width <= 900px) {
.pv-grid,
.pv-grid--3 {
grid-template-columns: 1fr 1fr;
}
}
@media (width <= 600px) {
.pv-grid,
.pv-grid--3 {
grid-template-columns: 1fr;
}
}
.pv-kv {
display: flex;
flex-direction: column;
gap: 3px;
padding: 8px 0;
border-bottom: 1px dashed var(--pv-g100);
&--span {
grid-column: 1 / -1;
}
}
.pv-k {
font-size: 10.5px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--pv-g500);
}
.pv-v {
font-size: 13.5px;
color: var(--pv-g900);
font-weight: 500;
word-break: break-word;
strong {
font-weight: 700;
}
}
.pv-v--accent {
font-size: 18px;
font-weight: 700;
color: var(--pv-accent);
letter-spacing: 0.01em;
}
.pv-suffix {
font-size: 12px;
font-weight: 600;
color: var(--pv-g500);
margin-left: 4px;
}
/* ───────── FEATURE GRID ───────── */
.pv-feature {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border-radius: 8px;
background: var(--pv-g50);
border: 1px solid var(--pv-g200);
font-size: 12.5px;
font-weight: 600;
color: var(--pv-g700);
fa-icon {
font-size: 12px;
color: var(--pv-g500);
}
&.is-on {
background: var(--pv-green-l);
border-color: #bbf7d0;
color: var(--pv-green);
fa-icon {
color: var(--pv-green);
}
}
}
/* ───────── LEGACY-MARKUP POLISH ─────────
Polishes the existing Mifos label/value layout (.flex-100.layout-row >
.flex-40 + .flex-60) and section headings (.mat-h3) when wrapped in
.pv-legacy, so view templates can be retro-fitted with the new look
without rewriting every row. */
.pv-legacy {
display: flex;
flex-direction: column;
gap: 14px;
> h2.mat-h2,
.pv-section > h2.mat-h2 {
margin: 0 0 12px;
font-size: 16px;
font-weight: 700;
color: var(--pv-g900);
}
/* Section heading -> behaves like .pv-section-h */
.mat-h3 {
margin: 0 0 12px;
padding: 10px 16px;
font-size: 13px;
font-weight: 700;
color: var(--pv-accent);
letter-spacing: 0.04em;
text-transform: uppercase;
background: var(--pv-accent-l);
border-left: 3px solid var(--pv-accent);
border-radius: 8px;
}
.mat-h4 {
margin: 12px 0 8px;
font-size: 12px;
font-weight: 700;
color: var(--pv-g700);
text-transform: uppercase;
letter-spacing: 0.05em;
}
/* Section card wrapper: every consecutive group of (h3 + rows) is
essentially a section. We wrap each whole content block in a card-like
container by styling .responsive-column / .layout-row-wrap.flex-100 */
.layout-row-wrap.flex-100,
> .flex-100.layout-row-wrap,
> .responsive-column {
background: #fff;
border: 1px solid var(--pv-g200);
border-radius: 12px;
padding: 14px 18px;
box-shadow: 0 1px 2px rgb(15 23 42 / 4%);
}
/* Label / value row */
.flex-100.layout-row,
.layout-row.flex-100 {
display: grid;
grid-template-columns: minmax(220px, 42%) 1fr;
gap: 14px;
align-items: baseline;
padding: 8px 0;
border-bottom: 1px dashed var(--pv-g100);
&:last-child {
border-bottom: 0;
}
> .flex-40 {
font-size: 10.5px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--pv-g500);
margin: 0;
flex: none;
}
> .flex-60 {
font-size: 13.5px;
color: var(--pv-g900);
font-weight: 500;
word-break: break-word;
margin: 0;
flex: none;
strong {
font-weight: 700;
}
}
}
/* Dividers between sections can be removed since cards already separate */
mat-divider {
display: none;
}
/* Inline tables in legacy content */
.mat-elevation-z1 {
border-radius: 10px;
overflow: hidden;
margin-top: 6px;
box-shadow: none;
border: 1px solid var(--pv-g200);
}
@media (width <= 600px) {
.flex-100.layout-row,
.layout-row.flex-100 {
grid-template-columns: 1fr;
gap: 4px;
}
}
}
/* ───────── TABLES ───────── */
.pv-table-wrap {
overflow-x: auto;
border: 1px solid var(--pv-g200);
border-radius: 10px;
margin-top: 4px;
}
.pv-table {
width: 100%;
background: #fff;
th.mat-mdc-header-cell {
background: var(--pv-g50);
color: var(--pv-g700);
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.06em;
padding: 11px 14px;
border-bottom: 1px solid var(--pv-g200);
white-space: nowrap;
}
td.mat-mdc-cell {
padding: 10px 14px;
border-bottom: 1px solid var(--pv-g100);
font-size: 12.5px;
color: var(--pv-g900);
}
tbody tr:last-child td.mat-mdc-cell {
border-bottom: 0;
}
}
}