Files
jorgecuadros-platform/apps/web/src/app/globals.css
T
rmancinasandClaude Opus 5 0332292ae9
Build and Push Images / Build jorgecuadros-web (push) Successful in 1m42s
Build and Push Images / Build jorgecuadros-api (push) Successful in 2m22s
fix(notificaciones): merge renewals into one screen, fix MailModule DI
MailModule's provider used a `useFactory` with no `inject`, so the factory
received `undefined` and `new MailService(config)` threw on `config.get`,
taking the whole API down at boot. The module also wasn't actually
`@Global()` even though both NotificationsModule and RenewalsModule inject
MailService without importing it — that would have failed next. Replaced the
factory with a plain provider (ConfigModule is already `isGlobal`) and marked
the module global.

On the web side, mass email and renewal notices were two menu entries doing
the same job — telling a customer something by email. They are now two tabs
of `/notificaciones` (Servicios and Pólizas), following the Captura pattern:
`/renovaciones` still resolves, opening the same screen on its Pólizas tab so
existing bookmarks keep working.

The notifications page was also the last screen written in raw inline styles,
with blue buttons and filter pills that appear nowhere else in the app. It now
uses the shared design system: btn-primary/btn-outline, the seg segmented
control, card, tx-table, pager, and the servicios/fideicomiso badges.

Two supporting fixes found on the way: NOTIFICATION_STATUS_COLORS hardcoded
hex instead of the theme's positive/negative/muted vars, and `.small` was
referenced in 19 places across the app but never defined in globals.css.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 02:21:51 -07:00

3117 lines
66 KiB
CSS

/* ============================================================================
Jorge Cuadros & Asociados — design system
Warm coastal-professional identity for a Baja California firm.
Fraunces (display serif) + Work Sans (UI) + JetBrains Mono (data).
Palette: cream paper, deep pine brand, terracotta accent,
gold = Servicios, teal = Seguros, aubergine = Fideicomiso.
========================================================================== */
:root {
/* neutrals — warm paper family */
--paper: #f5f1e8;
--paper-2: #efe9dc;
--surface: #fffefb;
--surface-2: #faf6ee;
--line: #e4dccb;
--line-strong: #d3c8b1;
/* ink / text */
--ink: #211d17;
--ink-soft: #4b4438;
--muted: #756c5c;
--muted-2: #98907e;
/* brand — deep pine teal */
--brand-900: #0c322d;
--brand-800: #113f38;
--brand-700: #16544a;
--brand-600: #1c6b5e;
--brand-500: #24897a;
--brand-tint: #e2ede9;
/* accent — terracotta clay (CTA / focus / links) */
--accent: #bf5a34;
--accent-600: #a94c2a;
--accent-tint: #f6e4da;
/* line-of-business + domains */
--servicios: #9a6a12; /* gold / ochre — utilities */
--servicios-tint: #f2e6c8;
--servicios-ink: #6d4a08;
--seguros: #0f6f68; /* teal — insurance */
--seguros-tint: #d3ebe7;
--seguros-ink: #0a4b46;
--fideicomiso: #6d3f60; /* aubergine — trust */
--fideicomiso-tint: #eddced;
--fideicomiso-ink: #4a2a41;
/* semantic */
--positive: #2f6d3c;
--positive-tint: #dcecdd;
--negative: #a23a2c;
--negative-tint: #f4dcd6;
/* shape + motion */
--radius-sm: 6px;
--radius: 10px;
--radius-lg: 16px;
--shadow-sm: 0 1px 2px rgba(33, 29, 23, 0.06),
0 1px 3px rgba(33, 29, 23, 0.05);
--shadow: 0 2px 4px rgba(33, 29, 23, 0.05),
0 8px 24px -12px rgba(33, 29, 23, 0.18);
--shadow-lg: 0 24px 60px -24px rgba(12, 50, 45, 0.35);
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
--ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
--font-display: "Fraunces", "Iowan Old Style", "Palatino Linotype",
Georgia, serif;
--font-sans: "Work Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
--font-mono: "JetBrains Mono", ui-monospace, "SF Mono", "Cascadia Code",
Menlo, monospace;
--shell-max: 1180px;
}
* {
box-sizing: border-box;
}
html,
body {
margin: 0;
padding: 0;
}
/* ----------------------------------------------------------------------------
User-adjustable text size. Every font-size in this stylesheet is expressed in
rem, so the whole UI follows the root size and one variable scales it all.
--ui-scale is written onto <html> by the pre-hydration script in layout.tsx
(read from localStorage, so there is no flash of the wrong size) and updated
live by the Aa control in the appbar. A percentage keeps the browser's own
base font size honoured instead of hard-coding 16px over it.
-------------------------------------------------------------------------- */
html {
font-size: calc(100% * var(--ui-scale, 1));
}
body {
font-family: var(--font-sans);
background-color: var(--paper);
/* subtle paper grain via layered radial dots */
background-image: radial-gradient(
circle at 1px 1px,
rgba(33, 29, 23, 0.035) 1px,
transparent 0
);
background-size: 22px 22px;
color: var(--ink);
font-size: 0.9375rem;
line-height: 1.55;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
h1,
h2,
h3,
h4 {
font-family: var(--font-display);
font-weight: 560;
color: var(--ink);
letter-spacing: -0.01em;
margin: 0;
line-height: 1.12;
}
a {
color: var(--accent-600);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
button {
font-family: inherit;
cursor: pointer;
}
:focus-visible {
outline: 2.5px solid var(--accent);
outline-offset: 2px;
border-radius: 4px;
}
.mono {
font-family: var(--font-mono);
font-feature-settings: "tnum" 1;
}
/* ---- layout helpers ---- */
.shell-main {
max-width: var(--shell-max);
margin: 0 auto;
padding: 2.125rem 1.75rem 6rem;
}
@media (max-width: 640px) {
.shell-main {
padding: 1.375rem 1rem 4.5rem;
}
}
/* Deployed-build line. Quiet by default — it only needs to be legible when
someone is verifying a release or a rollback. */
.shell-footer {
max-width: var(--shell-max);
margin: 0 auto;
padding: 1rem 1.75rem 1.75rem;
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 0.75rem;
font-size: 0.75rem;
color: var(--muted-2);
border-top: 1px solid var(--line);
}
.shell-footer-build {
font-family: var(--font-mono);
font-variant-numeric: tabular-nums;
cursor: help;
/* Full 40-char commit hashes, and two of them when the tiers disagree. Hex
offers no break opportunity, so without this the line pushes the page into
horizontal scroll on a phone. */
min-width: 0;
overflow-wrap: anywhere;
}
.shell-footer-warn {
color: var(--negative);
background: var(--negative-tint);
border-radius: var(--radius-sm);
padding: 0.0625rem 0.375rem;
font-weight: 600;
}
@media (max-width: 640px) {
.shell-footer {
padding: 1rem 1rem 1.5rem;
}
}
.eyebrow {
font-family: var(--font-sans);
text-transform: uppercase;
letter-spacing: 0.16em;
font-size: 0.6875rem;
font-weight: 600;
color: var(--muted);
}
.muted {
color: var(--muted);
}
/* Secondary line inside a row or card — used alongside .muted throughout. */
.small {
font-size: 0.8125rem;
}
/* ============================================================================
App shell / top nav
========================================================================== */
.appbar {
position: sticky;
top: 0;
z-index: 40;
background: linear-gradient(180deg, var(--brand-900), var(--brand-800));
color: #f2efe7;
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
}
.appbar-inner {
max-width: var(--shell-max);
margin: 0 auto;
padding: 0 1.75rem;
/* min-height, not height: the bar has to grow when the user scales text up. */
min-height: 3.875rem;
display: flex;
align-items: center;
gap: 1.625rem;
}
@media (max-width: 640px) {
.appbar-inner {
padding: 0 1rem;
gap: 0.875rem;
}
}
.brand {
display: flex;
align-items: center;
gap: 0.75rem;
color: #f6f3ec;
text-decoration: none;
}
.brand:hover {
text-decoration: none;
}
.brand-mark {
width: 38px;
height: 38px;
border-radius: 9px;
background: #fbf8f0;
border: 1px solid rgba(255, 255, 255, 0.18);
object-fit: contain;
padding: 0.1875rem;
box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.2);
}
.login-brand .brand-mark {
width: 46px;
height: 46px;
border-radius: 10px;
}
.brand-text {
display: flex;
flex-direction: column;
line-height: 1.05;
}
.brand-name {
font-family: var(--font-display);
font-size: 1rem;
font-weight: 560;
letter-spacing: -0.01em;
}
.brand-sub {
font-size: 0.65625rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: rgba(242, 239, 231, 0.62);
}
@media (max-width: 500px) {
.brand-sub {
display: none;
}
}
.appbar-nav {
display: flex;
align-items: center;
gap: 0.25rem;
margin-left: 0.5rem;
}
.appbar-link {
color: rgba(242, 239, 231, 0.78);
padding: 0.4375rem 0.75rem;
border-radius: 7px;
font-size: 0.875rem;
font-weight: 500;
white-space: nowrap;
}
.appbar-link:hover {
color: #fff;
background: rgba(255, 255, 255, 0.08);
text-decoration: none;
}
.appbar-link.active {
color: #fff;
background: rgba(255, 255, 255, 0.12);
}
/* Grouped nav entries ("Cobranza", "Admin") */
.appbar-menu {
position: relative;
}
.appbar-menu-trigger {
display: inline-flex;
align-items: center;
gap: 0.375rem;
border: 0;
background: transparent;
font: inherit;
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
}
.appbar-caret {
width: 0;
height: 0;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4.5px solid currentColor;
opacity: 0.7;
}
.appbar-menu-trigger[aria-expanded="true"] {
color: #fff;
background: rgba(255, 255, 255, 0.12);
}
.appbar-dropdown {
position: absolute;
top: calc(100% + 8px);
left: 0;
min-width: 194px;
padding: 0.375rem;
display: flex;
flex-direction: column;
gap: 0.125rem;
background: var(--surface);
border: 1px solid var(--line);
border-radius: 10px;
box-shadow: 0 12px 30px rgba(12, 50, 45, 0.22);
z-index: 50;
}
.appbar-dropdown-link {
padding: 0.5rem 0.6875rem;
border-radius: 7px;
font-size: 0.875rem;
color: var(--ink);
white-space: nowrap;
}
.appbar-dropdown-link:hover {
background: var(--brand-tint);
color: var(--brand-800);
text-decoration: none;
}
.appbar-dropdown-link.active {
background: var(--brand-tint);
color: var(--brand-800);
font-weight: 600;
box-shadow: inset 2px 0 0 var(--brand-600);
}
.appbar-dropdown-right {
left: auto;
right: 0;
}
.appbar-spacer {
flex: 1;
}
/* Text-size control */
.appbar-scale-trigger {
display: inline-flex;
align-items: baseline;
justify-content: center;
min-width: 2.25rem;
padding: 0.3125rem 0.5rem;
border: 1px solid rgba(255, 255, 255, 0.22);
border-radius: 0.5rem;
background: rgba(255, 255, 255, 0.06);
color: rgba(242, 239, 231, 0.85);
font-family: var(--font-sans);
font-size: 0.75rem;
font-weight: 600;
line-height: 1;
cursor: pointer;
}
.appbar-scale-trigger:hover,
.appbar-scale-trigger[aria-expanded="true"] {
background: rgba(255, 255, 255, 0.14);
color: #fff;
}
.appbar-scale-big {
font-size: 1.25rem;
}
.scale-option {
display: flex;
align-items: baseline;
gap: 0.625rem;
width: 100%;
border: 0;
background: transparent;
font-family: var(--font-sans);
text-align: left;
cursor: pointer;
}
.scale-option > span:first-child {
width: 1.5rem;
flex: none;
font-weight: 700;
line-height: 1;
}
.scale-option-label {
font-size: 0.875rem;
}
.scale-inline {
display: flex;
flex-direction: column;
gap: 0.375rem;
margin-top: 0.75rem;
padding-top: 0.75rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.scale-inline-options {
display: flex;
align-items: baseline;
gap: 0.375rem;
padding: 0 0.75rem;
}
.scale-chip {
min-width: 2.75rem;
padding: 0.5rem 0.375rem;
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: 0.5rem;
background: rgba(255, 255, 255, 0.06);
color: rgba(242, 239, 231, 0.82);
font-family: var(--font-sans);
font-weight: 700;
line-height: 1;
cursor: pointer;
}
.scale-chip.active {
background: rgba(255, 255, 255, 0.18);
border-color: rgba(255, 255, 255, 0.4);
color: #fff;
}
/* Mobile menu button + drawer */
.appbar-burger {
display: none;
width: 38px;
height: 38px;
padding: 0;
border: 1px solid rgba(255, 255, 255, 0.22);
border-radius: 9px;
background: rgba(255, 255, 255, 0.06);
cursor: pointer;
place-items: center;
}
.burger-icon {
position: relative;
}
.burger-icon,
.burger-icon::before,
.burger-icon::after {
display: block;
width: 17px;
height: 1.8px;
border-radius: 2px;
background: #f2efe7;
transition: transform 0.16s ease, opacity 0.16s ease;
}
.burger-icon::before,
.burger-icon::after {
content: "";
position: absolute;
}
.burger-icon::before {
transform: translateY(-5.5px);
}
.burger-icon::after {
transform: translateY(5.5px);
}
.burger-icon.open {
background: transparent;
}
.burger-icon.open::before {
transform: rotate(45deg);
}
.burger-icon.open::after {
transform: rotate(-45deg);
}
.appbar-drawer {
display: none;
flex-direction: column;
gap: 0.125rem;
padding: 0.5rem 1rem 1rem;
border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.appbar-drawer-group {
display: flex;
flex-direction: column;
gap: 0.125rem;
margin-top: 0.5rem;
}
.appbar-drawer-heading {
padding: 0.375rem 0.75rem 0.125rem;
font-size: 0.65625rem;
letter-spacing: 0.14em;
text-transform: uppercase;
color: rgba(242, 239, 231, 0.45);
}
.appbar-drawer-link {
padding: 0.625rem 0.75rem;
border-radius: 8px;
font-size: 0.9375rem;
color: rgba(242, 239, 231, 0.82);
}
.appbar-drawer-link:hover {
background: rgba(255, 255, 255, 0.08);
color: #fff;
text-decoration: none;
}
.appbar-drawer-link.active {
background: rgba(255, 255, 255, 0.13);
color: #fff;
font-weight: 600;
}
.appbar-drawer-user {
margin-top: 0.75rem;
padding: 0.625rem 0.75rem 0;
border-top: 1px solid rgba(255, 255, 255, 0.1);
font-size: 0.78125rem;
color: rgba(242, 239, 231, 0.55);
}
@media (max-width: 980px) {
.appbar-nav {
display: none;
}
.appbar-burger {
display: grid;
}
.appbar-drawer {
display: flex;
}
}
.appbar-user {
display: flex;
align-items: center;
gap: 0.75rem;
font-size: 0.8125rem;
color: rgba(242, 239, 231, 0.7);
}
.appbar-user-name {
display: inline-flex;
flex-direction: column;
line-height: 1.2;
}
.appbar-user-role {
font-size: 0.6875rem;
color: rgba(242, 239, 231, 0.45);
}
/* Below the drawer breakpoint the drawer carries the user's name/role. */
@media (max-width: 980px) {
.appbar-user .appbar-user-name {
display: none;
}
}
/* ============================================================================
Admin form grid (users, and future CRUD forms)
========================================================================== */
.form-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1rem;
}
.form-actions {
display: flex;
gap: 0.625rem;
margin-top: 1.125rem;
flex-wrap: wrap;
}
.row-actions {
display: flex;
gap: 0.5rem;
align-items: center;
justify-content: flex-end;
}
.detail-actionbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.75rem;
flex-wrap: wrap;
margin-bottom: 0.5rem;
}
.inline-form-note {
font-size: 0.8125rem;
color: var(--muted, #6b7280);
margin: 0.25rem 0 0.875rem;
}
.child-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0.625rem;
}
.child-editor {
border-top: 1px solid rgba(0, 0, 0, 0.08);
margin-top: 0.75rem;
padding-top: 0.875rem;
}
.picker {
position: relative;
}
.picker-selected {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.5rem;
padding: 0.5rem 0.75rem;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 8px;
}
.picker-list {
position: absolute;
z-index: 20;
top: 100%;
left: 0;
right: 0;
margin: 0.25rem 0 0;
padding: 0.25rem;
list-style: none;
background: var(--card-bg, #fff);
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 8px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
max-height: 280px;
overflow-y: auto;
}
.picker-item {
display: block;
width: 100%;
text-align: left;
padding: 0.5rem 0.625rem;
border: 0;
background: transparent;
border-radius: 6px;
cursor: pointer;
font: inherit;
}
.picker-item:hover {
background: rgba(0, 0, 0, 0.05);
}
/* ============================================================================
Buttons
========================================================================== */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
border-radius: var(--radius-sm);
border: 1px solid transparent;
padding: 0.5625rem 1rem;
font-size: 0.875rem;
font-weight: 600;
line-height: 1;
transition: transform 0.12s var(--ease-out-quart),
background 0.15s, box-shadow 0.15s, border-color 0.15s;
}
.btn:active {
transform: translateY(1px);
}
.btn-primary {
background: var(--accent);
color: #fff;
box-shadow: 0 1px 0 rgba(0, 0, 0, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.15);
}
.btn-primary:hover {
background: var(--accent-600);
text-decoration: none;
}
.btn-ghost {
background: transparent;
color: var(--ink-soft);
border-color: var(--line-strong);
}
.btn-ghost:hover {
background: var(--surface-2);
color: var(--ink);
border-color: var(--brand-600);
text-decoration: none;
}
/* Dark appbar keeps the light-on-dark ghost button. */
.appbar .btn-ghost {
color: rgba(242, 239, 231, 0.85);
border-color: rgba(255, 255, 255, 0.22);
}
.appbar .btn-ghost:hover {
background: rgba(255, 255, 255, 0.1);
color: #fff;
border-color: rgba(255, 255, 255, 0.35);
}
.btn-outline {
background: var(--surface);
color: var(--ink-soft);
border-color: var(--line-strong);
}
.btn-outline:hover {
border-color: var(--brand-600);
color: var(--brand-700);
text-decoration: none;
}
.btn:disabled {
opacity: 0.55;
cursor: not-allowed;
transform: none;
}
/* ============================================================================
Cards / panels
========================================================================== */
.card {
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
}
/* ============================================================================
Badges / chips
========================================================================== */
.badge {
display: inline-flex;
align-items: center;
gap: 0.375rem;
padding: 0.1875rem 0.5625rem;
border-radius: 999px;
font-size: 0.75rem;
font-weight: 600;
letter-spacing: 0.01em;
line-height: 1.4;
border: 1px solid transparent;
white-space: nowrap;
}
.badge .dot {
width: 7px;
height: 7px;
border-radius: 50%;
}
.badge-servicios {
background: var(--servicios-tint);
color: var(--servicios-ink);
border-color: rgba(154, 106, 18, 0.28);
}
.badge-servicios .dot {
background: var(--servicios);
}
.badge-seguros {
background: var(--seguros-tint);
color: var(--seguros-ink);
border-color: rgba(15, 111, 104, 0.28);
}
.badge-seguros .dot {
background: var(--seguros);
}
.badge-fideicomiso {
background: var(--fideicomiso-tint);
color: var(--fideicomiso-ink);
border-color: rgba(109, 63, 96, 0.28);
}
.badge-fideicomiso .dot {
background: var(--fideicomiso);
}
.badge-neutral {
background: var(--paper-2);
color: var(--muted);
border-color: var(--line-strong);
}
.badge-positive {
background: var(--positive-tint);
color: var(--positive);
border-color: rgba(47, 109, 60, 0.25);
}
.badge-negative {
background: var(--negative-tint);
color: var(--negative);
border-color: rgba(162, 58, 44, 0.25);
}
.badge-count {
font-family: var(--font-mono);
font-feature-settings: "tnum" 1;
}
/* ============================================================================
Utility animation
========================================================================== */
@keyframes rise {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.rise {
animation: rise 0.5s var(--ease-out-expo) both;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.spinner {
width: 18px;
height: 18px;
border-radius: 50%;
border: 2.5px solid currentColor;
border-top-color: transparent;
animation: spin 0.7s linear infinite;
display: inline-block;
}
/* Upload progress (Operaciones ingest) */
.upload-progress {
display: flex;
flex-direction: column;
gap: 6px;
padding: 4px 0 8px;
}
.progress-track {
position: relative;
overflow: hidden;
height: 8px;
border-radius: 999px;
background: var(--paper-2);
}
.progress-fill {
height: 100%;
border-radius: 999px;
background: var(--brand-500);
transition: width 0.2s linear;
}
.progress-indeterminate .progress-fill {
width: 40% !important;
animation: progress-slide 1.2s var(--ease-out-quart) infinite;
}
@keyframes progress-slide {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(250%);
}
}
.upload-progress-stats {
display: flex;
flex-wrap: wrap;
gap: 12px;
font-size: 12px;
color: var(--muted);
}
@keyframes shimmer {
0% {
background-position: -420px 0;
}
100% {
background-position: 420px 0;
}
}
.skeleton {
background: linear-gradient(
90deg,
var(--paper-2) 25%,
#e8e1d2 37%,
var(--paper-2) 63%
);
background-size: 840px 100%;
animation: shimmer 1.4s linear infinite;
border-radius: 6px;
}
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
}
}
/* ============================================================================
Form fields (shared)
========================================================================== */
.field {
display: block;
margin-bottom: 1.125rem;
}
.field-label {
display: block;
font-size: 0.8125rem;
font-weight: 600;
color: var(--ink-soft);
margin-bottom: 0.4375rem;
}
.input {
width: 100%;
font-family: inherit;
font-size: 0.9375rem;
color: var(--ink);
background: var(--surface);
border: 1px solid var(--line-strong);
border-radius: var(--radius-sm);
padding: 0.6875rem 0.8125rem;
transition: border-color 0.15s, box-shadow 0.15s;
}
.input::placeholder {
color: var(--muted-2);
}
.input:focus {
outline: none;
border-color: var(--brand-600);
box-shadow: 0 0 0 3px var(--brand-tint);
}
/* ============================================================================
Login screen — split brand / form layout
========================================================================== */
.login-wrap {
min-height: 100vh;
display: grid;
grid-template-columns: 1.05fr 1fr;
}
@media (max-width: 860px) {
.login-wrap {
grid-template-columns: 1fr;
}
}
.login-aside {
position: relative;
background: linear-gradient(155deg, var(--brand-900) 0%, var(--brand-700) 62%, #17544a 100%);
color: #f2efe7;
padding: 2.75rem 3.25rem;
display: flex;
flex-direction: column;
justify-content: space-between;
overflow: hidden;
}
.login-aside::after {
/* faint concentric coastal arcs */
content: "";
position: absolute;
right: -180px;
bottom: -180px;
width: 560px;
height: 560px;
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 0 0 60px rgba(255, 255, 255, 0.04),
0 0 0 130px rgba(255, 255, 255, 0.03);
pointer-events: none;
}
@media (max-width: 860px) {
.login-aside {
padding: 2rem 1.75rem 2.5rem;
}
.login-aside-mid {
margin: 1.875rem 0;
}
}
.login-brand {
display: flex;
align-items: center;
gap: 0.8125rem;
position: relative;
z-index: 1;
}
.login-aside-mid {
position: relative;
z-index: 1;
max-width: 460px;
}
.login-headline {
font-family: var(--font-display);
color: #fbf8f0;
font-size: clamp(30px, 4.6vw, 46px);
font-weight: 500;
line-height: 1.08;
margin: 0.875rem 0 1.25rem;
letter-spacing: -0.015em;
}
.login-headline em {
font-style: italic;
color: #ecc98d;
}
.login-lede {
font-size: 1rem;
line-height: 1.6;
color: rgba(242, 239, 231, 0.82);
max-width: 420px;
}
.login-aside-foot {
position: relative;
z-index: 1;
display: flex;
align-items: center;
gap: 1rem;
flex-wrap: wrap;
}
.login-lob {
display: flex;
gap: 0.5rem;
}
.login-foot-note {
font-size: 0.78125rem;
color: rgba(242, 239, 231, 0.55);
}
.login-form-panel {
display: grid;
place-items: center;
padding: 2.5rem 1.75rem;
background: var(--paper);
}
.login-form-inner {
width: 100%;
max-width: 380px;
}
.login-form-title {
font-size: 1.875rem;
margin-top: 0.25rem;
}
.login-error {
background: var(--negative-tint);
color: var(--negative);
border: 1px solid rgba(162, 58, 44, 0.28);
border-radius: var(--radius-sm);
padding: 0.625rem 0.8125rem;
font-size: 0.84375rem;
font-weight: 500;
margin-bottom: 1.125rem;
}
.login-submit {
width: 100%;
padding: 0.75rem 1rem;
font-size: 0.9375rem;
}
/* ============================================================================
Page heading + stats (customer list)
========================================================================== */
.page-head {
margin-bottom: 1.625rem;
}
.page-title {
font-size: clamp(28px, 4vw, 38px);
font-weight: 500;
margin: 0.25rem 0 0;
}
.stat-strip {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 0.0625rem;
background: var(--line);
border: 1px solid var(--line);
border-radius: var(--radius-lg);
overflow: hidden;
margin-top: 1.375rem;
}
@media (max-width: 900px) {
.stat-strip {
grid-template-columns: repeat(3, 1fr);
}
}
@media (max-width: 520px) {
.stat-strip {
grid-template-columns: repeat(2, 1fr);
}
}
.stat-cell {
background: var(--surface);
padding: 0.9375rem 1rem 0.875rem;
}
.stat-cell.accent {
background: var(--brand-tint);
}
.stat-value {
font-family: var(--font-display);
font-size: 1.5625rem;
font-weight: 560;
color: var(--ink);
font-feature-settings: "tnum" 1;
letter-spacing: -0.01em;
}
.stat-cell.accent .stat-value {
color: var(--brand-700);
}
.stat-label {
font-size: 0.71875rem;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-top: 0.1875rem;
font-weight: 600;
}
/* ============================================================================
Search + filter toolbar
========================================================================== */
.toolbar {
display: flex;
gap: 0.875rem;
align-items: center;
flex-wrap: wrap;
margin: 1.625rem 0 1.125rem;
}
.search-box {
position: relative;
flex: 1 1 320px;
}
.search-box .search-icon {
position: absolute;
left: 13px;
top: 50%;
transform: translateY(-50%);
color: var(--muted-2);
font-size: 1rem;
pointer-events: none;
}
.search-input {
width: 100%;
padding-left: 2.375rem;
}
.seg {
display: inline-flex;
background: var(--paper-2);
border: 1px solid var(--line-strong);
border-radius: 999px;
padding: 0.1875rem;
gap: 0.125rem;
}
.seg-btn {
border: none;
background: transparent;
color: var(--muted);
font-size: 0.8125rem;
font-weight: 600;
padding: 0.4375rem 0.9375rem;
border-radius: 999px;
transition: color 0.15s, background 0.15s;
white-space: nowrap;
}
.seg-btn:hover {
color: var(--ink);
}
.seg-btn.active {
background: var(--surface);
color: var(--brand-700);
box-shadow: var(--shadow-sm);
}
@media (max-width: 560px) {
.seg {
width: 100%;
justify-content: space-between;
}
.seg-btn {
flex: 1;
padding: 0.5rem 0.375rem;
}
}
.result-meta {
font-size: 0.8125rem;
color: var(--muted);
margin-bottom: 0.75rem;
}
/* ============================================================================
Customer list (rows)
========================================================================== */
.cust-list {
display: flex;
flex-direction: column;
gap: 0.625rem;
}
.cust-row {
display: grid;
grid-template-columns: 1fr auto;
gap: 1rem;
align-items: center;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 0.9375rem 1.125rem;
text-decoration: none;
color: inherit;
transition: transform 0.14s var(--ease-out-quart),
box-shadow 0.14s, border-color 0.14s;
}
.cust-row:hover {
text-decoration: none;
transform: translateY(-1px);
box-shadow: var(--shadow);
border-color: var(--line-strong);
}
.cust-main {
min-width: 0;
}
.cust-name {
font-family: var(--font-display);
font-size: 1.0625rem;
font-weight: 560;
color: var(--ink);
letter-spacing: -0.01em;
display: flex;
align-items: center;
gap: 0.625rem;
}
.cust-name .inactive-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--muted-2);
flex: none;
}
/* Legacy record with no name anywhere in the source — reads as absent data,
not as a customer actually called "(SIN NOMBRE)". */
.cust-name-missing {
color: var(--muted-2);
font-style: italic;
font-weight: 400;
}
/* Name reconstructed from a secondary legacy table, so staff can tell it
apart from a name that was really on the customer record. */
.name-source {
font-family: var(--font-sans);
font-size: 0.625rem;
font-weight: 600;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--muted-2);
border: 1px solid var(--line-strong);
border-radius: 4px;
padding: 0.125rem 0.375rem;
white-space: nowrap;
cursor: help;
}
.cust-sub {
display: flex;
flex-wrap: wrap;
gap: 0.375rem 1rem;
margin-top: 0.3125rem;
font-size: 0.8125rem;
color: var(--muted);
}
.cust-sub .sep {
color: var(--line-strong);
}
.cust-side {
display: flex;
align-items: center;
gap: 0.5rem;
flex-wrap: wrap;
justify-content: flex-end;
}
@media (max-width: 620px) {
.cust-row {
grid-template-columns: 1fr;
}
.cust-side {
justify-content: flex-start;
}
}
/* ============================================================================
Pagination
========================================================================== */
.pager {
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
margin-top: 1.625rem;
}
.pager-info {
font-size: 0.8125rem;
color: var(--muted);
min-width: 130px;
text-align: center;
}
/* ============================================================================
Empty / error / loading states
========================================================================== */
.state-box {
text-align: center;
padding: 3.75rem 1.5rem;
color: var(--muted);
}
.state-box h3 {
color: var(--ink-soft);
font-size: 1.25rem;
margin-bottom: 0.375rem;
}
.state-glyph {
font-size: 2.125rem;
opacity: 0.5;
margin-bottom: 0.625rem;
}
.state-error {
background: var(--negative-tint);
border: 1px solid rgba(162, 58, 44, 0.25);
color: var(--negative);
border-radius: var(--radius);
padding: 1rem 1.125rem;
font-size: 0.875rem;
}
.skel-row {
height: 74px;
border-radius: var(--radius);
}
/* ============================================================================
Customer detail — hero
========================================================================== */
.back-link {
display: inline-flex;
align-items: center;
gap: 0.375rem;
font-size: 0.84375rem;
font-weight: 600;
color: var(--muted);
margin-bottom: 1.125rem;
}
.back-link:hover {
color: var(--brand-700);
text-decoration: none;
}
.detail-hero {
background: linear-gradient(150deg, var(--brand-900), var(--brand-700) 88%);
color: #f2efe7;
border-radius: var(--radius-lg);
padding: 1.625rem 1.75rem;
position: relative;
overflow: hidden;
box-shadow: var(--shadow);
}
.detail-hero::after {
content: "";
position: absolute;
right: -120px;
top: -140px;
width: 340px;
height: 340px;
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 0 0 44px rgba(255, 255, 255, 0.04);
pointer-events: none;
}
.hero-top {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 1.125rem;
flex-wrap: wrap;
position: relative;
z-index: 1;
}
.hero-name {
font-family: var(--font-display);
font-weight: 500;
color: #fbf8f0;
font-size: clamp(24px, 3.4vw, 34px);
letter-spacing: -0.015em;
line-height: 1.1;
}
.hero-name-missing {
font-style: italic;
color: rgba(251, 248, 240, 0.55);
}
.hero-provenance {
font-size: 0.78125rem;
color: rgba(242, 239, 231, 0.62);
margin-top: 0.5rem;
font-family: var(--font-mono);
}
.hero-badges {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
align-items: center;
}
.badge-on-dark {
background: rgba(255, 255, 255, 0.12);
color: #f2efe7;
border-color: rgba(255, 255, 255, 0.2);
}
.hero-facts {
position: relative;
z-index: 1;
display: flex;
flex-wrap: wrap;
gap: 1.625rem;
margin-top: 1.375rem;
padding-top: 1.125rem;
border-top: 1px solid rgba(255, 255, 255, 0.12);
}
.hero-fact-label {
font-size: 0.65625rem;
text-transform: uppercase;
letter-spacing: 0.1em;
color: rgba(242, 239, 231, 0.55);
font-weight: 600;
}
.hero-fact-value {
font-size: 0.9375rem;
color: #f6f3ec;
margin-top: 0.125rem;
font-weight: 500;
}
/* ============================================================================
Section scaffolding
========================================================================== */
.section {
margin-top: 2.125rem;
}
.section-head {
display: flex;
align-items: baseline;
gap: 0.75rem;
margin-bottom: 1rem;
}
.section-rule {
width: 4px;
align-self: stretch;
border-radius: 3px;
min-height: 1.375rem;
}
.section-rule.servicios {
background: var(--servicios);
}
.section-rule.seguros {
background: var(--seguros);
}
.section-rule.cuenta {
background: var(--accent);
}
.section-rule.datos {
background: var(--brand-600);
}
.section-rule.docs {
background: var(--muted-2);
}
.section-title {
font-size: 1.375rem;
font-weight: 560;
}
.section-count {
font-size: 0.8125rem;
color: var(--muted);
font-weight: 600;
font-family: var(--font-mono);
}
/* ---- key/value grid (datos del cliente) ---- */
.kv-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1.125rem 1.5rem;
padding: 1.375rem 1.5rem;
}
.kv-label {
font-size: 0.6875rem;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--muted);
font-weight: 600;
margin-bottom: 0.1875rem;
}
.kv-value {
font-size: 0.90625rem;
color: var(--ink);
}
.kv-value.mono {
font-size: 0.875rem;
}
.kv-block {
grid-column: 1 / -1;
border-top: 1px dashed var(--line-strong);
padding-top: 1rem;
margin-top: -0.125rem;
}
/* ---- property cards ---- */
.prop-card {
padding: 1.25rem 1.375rem;
}
.prop-card + .prop-card {
border-top: 1px solid var(--line);
}
.prop-addr {
font-family: var(--font-display);
font-size: 1.0625rem;
font-weight: 560;
color: var(--ink);
}
.prop-meta {
font-size: 0.8125rem;
color: var(--muted);
margin-top: 0.1875rem;
display: flex;
gap: 0.875rem;
flex-wrap: wrap;
}
.svc-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
gap: 0.625rem;
margin-top: 1rem;
}
.svc-item {
border: 1px solid var(--line);
border-left: 3px solid var(--servicios);
border-radius: var(--radius-sm);
background: var(--surface-2);
padding: 0.6875rem 0.8125rem;
}
.svc-item.inactive {
opacity: 0.62;
border-left-color: var(--muted-2);
}
.svc-head {
display: flex;
align-items: center;
gap: 0.5rem;
justify-content: space-between;
}
.svc-kind {
font-weight: 700;
font-size: 0.84375rem;
color: var(--servicios-ink);
display: flex;
align-items: center;
gap: 0.4375rem;
}
.svc-glyph {
font-size: 0.875rem;
}
.svc-detail {
margin-top: 0.4375rem;
font-size: 0.75rem;
color: var(--muted);
display: flex;
flex-direction: column;
gap: 0.125rem;
}
.svc-detail .mono {
color: var(--ink-soft);
}
.trust-box {
margin-top: 1rem;
border: 1px solid var(--fideicomiso-tint);
background: linear-gradient(0deg, var(--fideicomiso-tint), transparent 140%);
border-left: 3px solid var(--fideicomiso);
border-radius: var(--radius-sm);
padding: 0.8125rem 0.9375rem;
}
.trust-title {
font-size: 0.78125rem;
font-weight: 700;
color: var(--fideicomiso-ink);
text-transform: uppercase;
letter-spacing: 0.04em;
margin-bottom: 0.5rem;
}
.trust-facts {
display: flex;
flex-wrap: wrap;
gap: 0.5rem 1.375rem;
font-size: 0.8125rem;
}
/* ---- policy cards ---- */
.policy-card {
padding: 1.25rem 1.375rem;
}
.policy-card + .policy-card {
border-top: 1px solid var(--line);
}
.policy-head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 1rem;
flex-wrap: wrap;
}
.policy-num {
font-family: var(--font-mono);
font-size: 1.125rem;
font-weight: 600;
color: var(--seguros-ink);
letter-spacing: -0.01em;
}
.policy-type-row {
display: flex;
align-items: center;
gap: 0.5rem;
margin-top: 0.3125rem;
font-size: 0.8125rem;
color: var(--muted);
flex-wrap: wrap;
}
.policy-figures {
text-align: right;
}
.policy-total {
font-family: var(--font-display);
font-size: 1.375rem;
font-weight: 560;
color: var(--ink);
font-feature-settings: "tnum" 1;
}
.policy-total-label {
font-size: 0.6875rem;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--muted);
}
.policy-body {
margin-top: 1rem;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap: 1rem;
}
.subpanel {
background: var(--surface-2);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
padding: 0.75rem 0.875rem;
}
.subpanel-title {
font-size: 0.6875rem;
text-transform: uppercase;
letter-spacing: 0.07em;
color: var(--muted);
font-weight: 700;
margin-bottom: 0.5625rem;
display: flex;
justify-content: space-between;
}
.pay-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.625rem;
font-size: 0.8125rem;
padding: 0.3125rem 0;
border-bottom: 1px dashed var(--line);
}
.pay-row:last-child {
border-bottom: none;
}
.pay-seq {
width: 22px;
height: 22px;
border-radius: 50%;
background: var(--seguros-tint);
color: var(--seguros-ink);
font-size: 0.6875rem;
font-weight: 700;
display: grid;
place-items: center;
flex: none;
}
.mini-list {
font-size: 0.84375rem;
color: var(--ink-soft);
display: flex;
flex-direction: column;
gap: 0.3125rem;
}
.mini-list .mini-sub {
font-size: 0.75rem;
color: var(--muted);
}
/* ============================================================================
Estado de cuenta
========================================================================== */
.summary-grid {
display: grid;
/* Min track holds a 7-figure balance (e.g. TRASPASOS PAYPAL's -$7,028,533.44)
in full — auto-fill packs fixed-width tracks, so a single-currency card
never widens on its own and the min is what has to fit the number. */
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
gap: 0.75rem;
margin-bottom: 1.125rem;
}
.summary-card {
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 0.9375rem 1rem;
background: var(--surface);
border-top: 3px solid var(--muted-2);
}
.summary-card.UTILITY {
border-top-color: var(--servicios);
}
.summary-card.INSURANCE {
border-top-color: var(--seguros);
}
.summary-card.TRUST {
border-top-color: var(--fideicomiso);
}
.summary-domain {
display: flex;
align-items: center;
gap: 0.4375rem;
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--muted);
}
.summary-total {
font-family: var(--font-display);
font-size: 1.375rem;
font-weight: 560;
margin-top: 0.5rem;
font-feature-settings: "tnum" 1;
color: var(--ink);
/* Never truncate a balance — a clipped money figure reads as a wrong number.
The track width above is what keeps it inside the card. */
}
.summary-count {
font-size: 0.75rem;
color: var(--muted);
margin-top: 0.125rem;
}
.tx-table {
width: 100%;
border-collapse: collapse;
font-size: 0.84375rem;
}
.tx-table th {
text-align: left;
font-size: 0.65625rem;
text-transform: uppercase;
letter-spacing: 0.07em;
color: var(--muted);
font-weight: 700;
padding: 0 0.75rem 0.625rem;
border-bottom: 1px solid var(--line);
}
.tx-table th.num,
.tx-table td.num {
text-align: right;
}
.tx-table td {
padding: 0.6875rem 0.75rem;
border-bottom: 1px solid var(--line);
vertical-align: top;
}
.tx-table tr:last-child td {
border-bottom: none;
}
.tx-table tr:hover td {
background: var(--surface-2);
}
/* Drill-down rows (the chequera's yearly summary) read as pressable. */
.tx-table tr.selected td {
background: var(--surface-2);
box-shadow: inset 2px 0 0 var(--brand-600);
}
.tx-domain-cell {
white-space: nowrap;
}
.tx-dot {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 2px;
margin-right: 0.4375rem;
vertical-align: middle;
}
.tx-dot.UTILITY {
background: var(--servicios);
}
.tx-dot.INSURANCE {
background: var(--seguros);
}
.tx-dot.TRUST {
background: var(--fideicomiso);
}
.tx-amount {
font-family: var(--font-mono);
font-weight: 600;
font-feature-settings: "tnum" 1;
white-space: nowrap;
}
.tx-amount.neg {
color: var(--negative);
}
.tx-amount.pos {
color: var(--positive);
}
.tx-cur {
font-size: 0.6875rem;
color: var(--muted);
font-weight: 500;
}
.tx-concept {
color: var(--ink-soft);
}
.tx-ref {
font-family: var(--font-mono);
font-size: 0.78125rem;
color: var(--muted);
}
.tx-scroll {
overflow-x: auto;
}
/* ---- documents ---- */
.doc-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 0.625rem;
padding: 1.25rem 1.375rem;
}
.doc-item {
display: flex;
align-items: center;
gap: 0.6875rem;
border: 1px solid var(--line);
border-radius: var(--radius-sm);
padding: 0.6875rem 0.8125rem;
background: var(--surface-2);
}
.doc-icon {
width: 32px;
height: 32px;
border-radius: 7px;
background: var(--paper-2);
display: grid;
place-items: center;
font-size: 0.9375rem;
flex: none;
color: var(--brand-700);
}
.doc-type {
font-size: 0.84375rem;
font-weight: 600;
color: var(--ink);
}
.doc-key {
font-size: 0.6875rem;
color: var(--muted-2);
font-family: var(--font-mono);
word-break: break-all;
}
.empty-inline {
padding: 1.375rem 1.5rem;
color: var(--muted);
font-size: 0.875rem;
font-style: italic;
}
.section-note {
font-size: 0.75rem;
color: var(--muted);
margin-top: 0.625rem;
}
/* ============================================================================
Policies — vigencia badges
========================================================================== */
.badge.status-active {
background: var(--positive-tint);
color: var(--positive);
border-color: rgba(47, 109, 60, 0.25);
}
.badge.status-expiring {
background: var(--accent-tint);
color: var(--accent-600);
border-color: rgba(191, 90, 52, 0.28);
}
.badge.status-expired {
background: var(--negative-tint);
color: var(--negative);
border-color: rgba(162, 58, 44, 0.25);
}
.badge.status-undated {
background: var(--paper-2);
color: var(--muted);
border-color: var(--line-strong);
}
/* ============================================================================
Policies — header strips
========================================================================== */
/* Stat cells double as vigencia filters on /polizas. */
.stat-cell-btn {
display: block;
width: 100%;
text-align: left;
border: none;
cursor: pointer;
font: inherit;
transition: background 0.15s;
}
.stat-cell-btn:hover {
background: var(--surface-2);
}
.stat-cell-btn.accent:hover {
background: #d7e6e1;
}
.stat-cell-btn.selected {
box-shadow: inset 0 -3px 0 var(--brand-600);
}
.premium-strip {
display: flex;
align-items: center;
gap: 1rem;
flex-wrap: wrap;
margin-top: 0.875rem;
padding: 0 0.125rem;
}
.premium-caption {
font-size: 0.71875rem;
text-transform: uppercase;
letter-spacing: 0.05em;
font-weight: 600;
color: var(--muted);
}
.premium-chip {
display: inline-flex;
align-items: baseline;
gap: 0.5rem;
}
.premium-chip strong {
font-family: var(--font-mono);
font-feature-settings: "tnum" 1;
font-size: 0.9375rem;
color: var(--brand-700);
}
.premium-chip-sub {
font-size: 0.75rem;
color: var(--muted);
}
/* ============================================================================
Policies — secondary filter row
========================================================================== */
.filter-row {
display: flex;
gap: 0.875rem;
align-items: flex-end;
flex-wrap: wrap;
margin-bottom: 1.125rem;
}
.filter-field {
display: flex;
flex-direction: column;
gap: 0.3125rem;
flex: 1 1 210px;
min-width: 0;
}
.filter-label {
font-size: 0.71875rem;
text-transform: uppercase;
letter-spacing: 0.05em;
font-weight: 600;
color: var(--muted);
}
.select {
appearance: none;
cursor: pointer;
padding-right: 2.125rem;
background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%),
linear-gradient(135deg, var(--muted) 50%, transparent 50%);
background-position: calc(100% - 18px) 55%, calc(100% - 13px) 55%;
background-size: 5px 5px, 5px 5px;
background-repeat: no-repeat;
}
.filter-clear {
flex: 0 0 auto;
height: 40px;
}
/* ============================================================================
Policies — list rows
========================================================================== */
.pol-row .cust-name {
flex-wrap: wrap;
gap: 0.5625rem;
}
.pol-number {
font-size: 0.9375rem;
font-weight: 600;
letter-spacing: -0.01em;
}
.pol-side {
text-align: right;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 0.1875rem;
flex: 0 0 auto;
}
.pol-premium {
font-family: var(--font-mono);
font-feature-settings: "tnum" 1;
font-size: 0.9375rem;
font-weight: 600;
color: var(--ink);
}
.pol-dates {
font-size: 0.75rem;
color: var(--muted);
white-space: nowrap;
}
.pol-phrase {
font-size: 0.71875rem;
font-weight: 600;
}
.pol-phrase.expiring {
color: var(--accent-600);
}
.pol-phrase.active {
color: var(--positive);
}
@media (max-width: 620px) {
.pol-side {
align-items: flex-start;
text-align: left;
margin-top: 0.5rem;
}
}
/* ============================================================================
Policy detail — owner card, linked properties, vehicles
========================================================================== */
.owner-link {
display: flex;
align-items: center;
justify-content: space-between;
gap: 1rem;
padding: 1.125rem 1.375rem;
color: inherit;
text-decoration: none;
border-radius: var(--radius-lg);
transition: background 0.15s;
}
.owner-link:hover {
background: var(--surface-2);
}
.owner-name {
font-family: var(--font-display);
font-size: 1.1875rem;
font-weight: 560;
margin-bottom: 0.1875rem;
}
.owner-cta {
font-size: 0.8125rem;
font-weight: 600;
color: var(--brand-600);
white-space: nowrap;
}
.linked-props {
border-top: 1px solid var(--line);
padding: 1rem 1.375rem 1.125rem;
}
.linked-prop {
font-size: 0.875rem;
margin-top: 0.25rem;
}
.veh-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 0.75rem;
padding: 1rem;
}
.veh-card {
background: var(--surface-2);
border: 1px solid var(--line);
border-radius: var(--radius);
padding: 0.8125rem 0.9375rem;
}
.veh-title {
font-weight: 600;
font-size: 0.90625rem;
margin-bottom: 0.375rem;
}
.veh-facts {
display: flex;
flex-direction: column;
gap: 0.1875rem;
font-size: 0.78125rem;
color: var(--muted);
}
/* Policy number on the customer detail card links through to /polizas/[id]. */
.policy-num-link {
color: inherit;
text-decoration: none;
transition: color 0.15s;
}
.policy-num-link:hover {
color: var(--brand-600);
}
/* ============================================================================
Utilities — service mix strip (doubles as the service-kind filter)
========================================================================== */
.mix-strip {
display: flex;
align-items: center;
gap: 0.625rem;
flex-wrap: wrap;
margin-top: 0.875rem;
padding: 0 0.125rem;
}
.mix-chip {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.375rem 0.6875rem;
background: var(--surface-2);
border: 1px solid var(--line);
border-radius: 999px;
cursor: pointer;
font: inherit;
color: inherit;
transition: border-color 0.15s, background 0.15s;
}
.mix-chip:hover {
border-color: var(--brand-600);
}
.mix-chip.selected {
border-color: var(--brand-600);
box-shadow: inset 0 0 0 1px var(--brand-600);
}
.mix-glyph {
font-size: 0.8125rem;
color: var(--brand-600);
}
.mix-body {
display: inline-flex;
align-items: baseline;
gap: 0.375rem;
}
.mix-body strong {
font-family: var(--font-mono);
font-feature-settings: "tnum" 1;
font-size: 0.875rem;
color: var(--brand-700);
}
.mix-label {
font-size: 0.78125rem;
color: var(--muted);
}
.mix-inactive {
font-size: 0.6875rem;
color: var(--muted);
border-left: 1px solid var(--line);
padding-left: 0.5rem;
}
/* ============================================================================
Utilities — property list rows
========================================================================== */
.prop-row .cust-name {
flex-wrap: wrap;
gap: 0.5625rem;
}
.prop-side {
text-align: right;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 0.3125rem;
flex: 0 0 auto;
}
.svc-chips {
display: flex;
gap: 0.3125rem;
flex-wrap: wrap;
justify-content: flex-end;
}
.svc-chip {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
border-radius: 7px;
background: var(--surface-2);
border: 1px solid var(--line);
font-size: 0.75rem;
color: var(--brand-600);
}
.svc-chip.inactive {
opacity: 0.45;
}
@media (max-width: 620px) {
.prop-side {
align-items: flex-start;
text-align: left;
margin-top: 0.5rem;
}
.svc-chips {
justify-content: flex-start;
}
}
/* Sibling properties on the property detail are navigable. */
.linked-prop.link {
display: block;
color: inherit;
text-decoration: none;
transition: color 0.15s;
}
.linked-prop.link:hover {
color: var(--brand-600);
}
.inline-link {
color: var(--brand-600);
text-decoration: none;
font-weight: 600;
}
.inline-link:hover {
text-decoration: underline;
}
/* Visually hidden, still read by screen readers (service kind chips). */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -0.0625rem;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* ================================================================
Estado de cuenta (billing / statements) — plan step 6
================================================================ */
/* A balance is signed: negative means the customer owes the office, positive
means they hold a credit. `flat` is a real state (settled to zero), not a
fallback, so it gets its own muted treatment rather than inheriting either. */
.bal-amount {
font-family: var(--font-mono);
font-weight: 700;
font-feature-settings: "tnum" 1;
white-space: nowrap;
}
.bal-amount.owing {
color: var(--negative);
}
.bal-amount.credit {
color: var(--positive);
}
.bal-amount.flat {
color: var(--muted);
}
.bal-running {
font-family: var(--font-mono);
font-size: 0.78125rem;
font-feature-settings: "tnum" 1;
white-space: nowrap;
color: var(--ink-soft);
}
.bal-running.owing {
color: var(--negative);
}
.bal-running.credit {
color: var(--positive);
}
.bal-row .bal-side {
text-align: right;
display: flex;
flex-direction: column;
gap: 0.125rem;
align-items: flex-end;
min-width: 160px;
}
.bal-side .bal-amount {
font-size: 1rem;
}
.bal-phrase {
font-size: 0.6875rem;
font-weight: 600;
letter-spacing: 0.03em;
text-transform: uppercase;
}
.bal-phrase.owing {
color: var(--negative);
}
.bal-phrase.credit {
color: var(--positive);
}
.bal-phrase.flat {
color: var(--muted);
}
/* The customer's other-currency balance, shown so a peso figure is never
mistaken for the whole picture. */
.bal-other {
font-size: 0.71875rem;
color: var(--muted);
font-family: var(--font-mono);
}
@media (max-width: 640px) {
.bal-row .bal-side {
align-items: flex-start;
text-align: left;
min-width: 0;
}
}
/* Currency cards on the statement double as the movement-table currency
switch, so they are buttons, not divs. */
.bal-card {
text-align: left;
cursor: pointer;
font: inherit;
border: 1px solid var(--line);
transition: border-color 0.15s ease, transform 0.15s ease;
}
.bal-card:hover {
transform: translateY(-1px);
}
.bal-card.selected {
border-color: var(--brand-600);
box-shadow: 0 0 0 1px var(--brand-600);
}
.bal-breakdown {
display: grid;
grid-template-columns: auto 1fr;
gap: 0.0625rem 0.5rem;
margin-top: 0.5rem;
font-size: 0.75rem;
align-items: baseline;
}
.bal-breakdown-label {
color: var(--muted);
font-size: 0.71875rem;
}
/* Ledger-wide charge/credit totals in the page header. */
.ledger-chip {
display: flex;
align-items: center;
gap: 0.875rem;
padding: 0.5rem 0.875rem;
border: 1px solid var(--line);
border-radius: 10px;
background: var(--surface-2);
}
.ledger-chip-cur {
font-weight: 700;
font-size: 0.75rem;
letter-spacing: 0.06em;
color: var(--muted);
}
.ledger-chip-figs {
display: flex;
flex-direction: column;
line-height: 1.3;
}
.ledger-chip-label {
font-size: 0.6875rem;
color: var(--muted);
}
/* Totals for the current movement filter — deliberately above the table, so a
filtered view can't be read as if it were the whole ledger. */
.filtered-totals {
display: flex;
flex-wrap: wrap;
gap: 0.625rem;
margin-bottom: 0.75rem;
}
.filtered-total {
display: flex;
flex-wrap: wrap;
align-items: baseline;
gap: 0.875rem;
padding: 0.5625rem 0.875rem;
border: 1px solid var(--line);
border-radius: 10px;
background: var(--surface-2);
font-size: 0.78125rem;
color: var(--muted);
}
.filtered-total-cur {
font-weight: 700;
letter-spacing: 0.06em;
color: var(--ink-soft);
}
.filtered-total-net strong {
font-family: var(--font-mono);
color: var(--ink);
}
/* Captured-but-unpaid (legacy NOPAGO). Deliberately not the same treatment as
a voided row: the movement is real and still pending, it just doesn't count
toward the balance until a check resolves it. */
.tx-outstanding {
color: var(--warn, #b45309);
font-weight: 600;
}
/* Charges broken out by concept, with a proportional bar. `.card` carries no
padding, so the list pads itself — otherwise the total sits on the border. */
.concept-list {
display: flex;
flex-direction: column;
padding: 0.25rem 1rem;
}
.concept-row {
display: grid;
grid-template-columns: minmax(150px, 1.2fr) minmax(60px, 2fr) auto;
gap: 0.875rem;
align-items: center;
padding: 0.5625rem 0;
border-bottom: 1px solid var(--line);
}
.concept-row:last-child {
border-bottom: none;
}
.concept-name {
font-size: 0.84375rem;
font-weight: 600;
display: flex;
flex-direction: column;
}
.concept-count {
font-size: 0.71875rem;
font-weight: 500;
color: var(--muted);
}
.concept-bar {
height: 7px;
background: var(--surface-2);
border-radius: 4px;
overflow: hidden;
}
.concept-bar span {
display: block;
height: 100%;
border-radius: 4px;
background: var(--negative);
opacity: 0.55;
}
.concept-total {
text-align: right;
font-size: 0.8125rem;
}
@media (max-width: 640px) {
.concept-row {
grid-template-columns: 1fr auto;
}
.concept-bar {
display: none;
}
}
/* Cross-link out of a detail hero (statement -> customer file). */
.hero-links {
margin-top: 1rem;
display: flex;
gap: 0.625rem;
flex-wrap: wrap;
position: relative;
z-index: 1;
}
/* --- Admin DB operations (Operaciones) --- */
.btn-danger {
background: var(--negative);
color: #fff;
}
.btn-danger:hover {
filter: brightness(0.94);
text-decoration: none;
}
.btn-danger:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.ops-log {
margin: 0.75rem 0 0;
padding: 0.75rem 0.875rem;
max-height: 320px;
overflow: auto;
background: var(--surface-2);
border: 1px solid var(--line);
border-radius: var(--radius-sm);
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
font-size: 0.75rem;
line-height: 1.5;
white-space: pre-wrap;
word-break: break-word;
}
.modal-backdrop {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
display: grid;
place-items: center;
padding: 1.25rem;
z-index: 50;
}
/* ============================================================================
Home dashboard (/inicio)
========================================================================== */
.home {
display: flex;
flex-direction: column;
gap: 2.25rem;
padding-top: 0.5rem;
}
.home-last-seen {
margin-top: 0.875rem;
font-size: 0.8125rem;
}
.home-section {
display: flex;
flex-direction: column;
gap: 0.875rem;
}
.section-head {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 1rem;
flex-wrap: wrap;
}
.section-title {
font-family: var(--font-display);
font-size: 1.25rem;
font-weight: 560;
margin: 0;
color: var(--ink);
letter-spacing: -0.005em;
}
.section-sub {
font-size: 0.78125rem;
color: var(--muted);
}
/* KPI cards row — 4 across on wide screens, 2 on tablet, 1 on mobile */
.home-section.kpi-row {
display: grid;
}
.home > .home-section:first-of-type {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.875rem;
}
@media (max-width: 1080px) {
.home > .home-section:first-of-type {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 560px) {
.home > .home-section:first-of-type {
grid-template-columns: 1fr;
}
}
.kpi-card {
display: flex;
flex-direction: column;
padding: 1.125rem 1.125rem 1rem;
text-decoration: none;
color: inherit;
transition: transform 0.18s var(--ease-out-expo), box-shadow 0.18s;
position: relative;
}
.kpi-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.kpi-label {
font-size: 0.71875rem;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--muted);
font-weight: 700;
}
.kpi-primary {
font-family: var(--font-display);
font-size: 2rem;
font-weight: 560;
color: var(--ink);
margin-top: 0.5rem;
font-feature-settings: "tnum" 1;
letter-spacing: -0.015em;
line-height: 1.1;
min-height: 2.25rem;
}
.kpi-sub {
list-style: none;
padding: 0;
margin: 0.75rem 0 0;
display: flex;
flex-direction: column;
gap: 0.25rem;
font-size: 0.78125rem;
color: var(--ink-soft);
}
.kpi-sub li {
line-height: 1.35;
}
.kpi-cta {
margin-top: 0.875rem;
font-size: 0.75rem;
font-weight: 700;
color: var(--brand-700);
letter-spacing: 0.02em;
text-transform: uppercase;
}
.kpi-card:hover .kpi-cta {
color: var(--brand-800);
}
/* Attention grid — auto-fill so the row of cards stays balanced */
.attention-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 0.875rem;
}
.attention-card {
display: flex;
flex-direction: column;
gap: 0.375rem;
padding: 1rem 1.125rem 1.125rem;
text-decoration: none;
color: inherit;
border-left: 3px solid var(--line-strong);
transition: transform 0.18s var(--ease-out-expo), box-shadow 0.18s;
position: relative;
}
.attention-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.attention-card.tone-warn {
border-left-color: var(--seguros);
}
.attention-card.tone-info {
border-left-color: var(--brand-600);
}
.attention-card.tone-muted {
border-left-color: var(--muted-2);
}
.attention-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 0.625rem;
}
.attention-title {
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--muted);
font-weight: 700;
}
.attention-arrow {
color: var(--muted-2);
font-size: 0.875rem;
transition: transform 0.18s var(--ease-out-expo), color 0.18s;
}
.attention-card:hover .attention-arrow {
color: var(--brand-700);
transform: translateX(3px);
}
.attention-primary {
font-family: var(--font-display);
font-size: 1.625rem;
font-weight: 560;
color: var(--ink);
font-feature-settings: "tnum" 1;
letter-spacing: -0.01em;
line-height: 1.1;
min-height: 1.875rem;
margin-top: 0.125rem;
}
.attention-sub {
font-size: 0.78125rem;
color: var(--ink-soft);
line-height: 1.4;
}
.attention-meta {
font-size: 0.71875rem;
color: var(--muted);
margin-top: 0.25rem;
}
/* Currency totals inside an attention card */
.home-currency-totals {
display: inline-flex;
flex-wrap: wrap;
gap: 0.5rem 0.875rem;
align-items: baseline;
}
.home-currency-totals-row {
display: inline-flex;
align-items: baseline;
gap: 0.375rem;
}
.home-currency-totals-num {
font-family: var(--font-display);
font-feature-settings: "tnum" 1;
font-size: 1.25rem;
font-weight: 560;
}
/* Quick links row */
.quick-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
gap: 0.75rem;
}
.quick-link {
display: flex;
flex-direction: column;
padding: 0.875rem 1rem;
text-decoration: none;
color: inherit;
position: relative;
transition: transform 0.18s var(--ease-out-expo), box-shadow 0.18s;
}
.quick-link:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.quick-label {
font-family: var(--font-display);
font-size: 1.0625rem;
font-weight: 560;
color: var(--ink);
}
.quick-sub {
font-size: 0.75rem;
color: var(--muted);
margin-top: 0.125rem;
}
.quick-arrow {
position: absolute;
top: 14px;
right: 16px;
color: var(--muted-2);
font-size: 0.875rem;
transition: transform 0.18s var(--ease-out-expo), color 0.18s;
}
.quick-link:hover .quick-arrow {
color: var(--brand-700);
transform: translateX(3px);
}
/* Money tone helpers (used in chequera card) */
.money-pos { color: var(--positive); }
.money-neg { color: var(--negative); }
/* ============================================================================
Reports module
========================================================================== */
.report-catalog {
display: flex;
flex-direction: column;
gap: 1.75rem;
margin-top: 1.25rem;
}
.report-catalog-group {
display: flex;
flex-direction: column;
gap: 0.625rem;
}
.report-catalog-title {
font-family: var(--font-display);
font-weight: 500;
font-size: 1.25rem;
color: var(--brand-800);
margin: 0;
padding-bottom: 0.375rem;
border-bottom: 1px solid var(--line);
}
.report-catalog-list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 0.75rem;
}
.report-catalog-item {
margin: 0;
}
.report-catalog-link {
display: flex;
flex-direction: column;
gap: 0.375rem;
padding: 0.875rem 1rem;
background: var(--surface);
border: 1px solid var(--line);
border-radius: 8px;
color: inherit;
text-decoration: none;
transition: border-color 0.18s, transform 0.18s var(--ease-out-expo);
}
.report-catalog-link:hover {
border-color: var(--brand-500);
transform: translateY(-1px);
}
.report-catalog-item-title {
font-weight: 600;
font-size: 0.9375rem;
color: var(--ink);
}
.report-catalog-item-desc {
font-size: 0.8125rem;
color: var(--muted);
line-height: 1.4;
}
.report-catalog-item-meta {
display: flex;
flex-wrap: wrap;
gap: 0.375rem;
margin-top: 0.25rem;
}
.report-catalog-tag {
font-size: 0.6875rem;
background: var(--brand-tint);
color: var(--brand-800);
padding: 0.125rem 0.5rem;
border-radius: 999px;
font-family: var(--font-mono);
}
.report-catalog-tag.muted {
background: var(--paper-2);
color: var(--muted);
}
/* Runner */
.report-runner {
display: flex;
flex-direction: column;
gap: 1rem;
margin-top: 1.25rem;
}
.report-filters {
display: flex;
flex-wrap: wrap;
gap: 0.75rem;
align-items: flex-end;
background: var(--surface);
border: 1px solid var(--line);
border-radius: 8px;
padding: 0.875rem 1rem;
}
.report-filters-actions {
display: flex;
align-items: center;
gap: 0.5rem;
margin-left: auto;
}
.report-error {
background: #fbeae3;
color: var(--negative);
border: 1px solid #e6b6a4;
border-radius: 6px;
padding: 0.625rem 0.875rem;
font-size: 0.8125rem;
}
.report-loading {
font-size: 0.75rem;
color: var(--muted);
padding: 0.25rem 0;
}
.report-result {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.report-result-head {
display: flex;
align-items: flex-end;
gap: 1rem;
flex-wrap: wrap;
}
.report-result-meta {
flex: 1;
min-width: 200px;
}
.report-output-buttons {
display: flex;
flex-wrap: wrap;
gap: 0.375rem;
}
.btn-sm {
padding: 0.25rem 0.625rem;
font-size: 0.75rem;
}
.report-table-wrap {
overflow-x: auto;
background: var(--surface);
border: 1px solid var(--line);
border-radius: 8px;
}
.report-table {
width: 100%;
border-collapse: collapse;
font-size: 0.8125rem;
}
.report-table th,
.report-table td {
padding: 0.5rem 0.75rem;
border-bottom: 1px solid var(--line);
}
.report-table th {
background: var(--paper-2);
color: var(--ink-soft);
font-weight: 600;
text-align: left;
position: sticky;
top: 0;
z-index: 1;
}
.report-table tbody tr:hover {
background: var(--paper-2);
}
.report-totals {
background: var(--accent);
color: #f5f1e8;
font-weight: 600;
padding: 0.625rem 0.875rem;
}
.statement {
display: flex;
flex-direction: column;
gap: 1.25rem;
}
.statement-head {
background: var(--surface);
border: 1px solid var(--line);
border-radius: 8px;
padding: 1rem 1.125rem;
}
.statement-name {
font-family: var(--font-display);
font-size: 1.375rem;
font-weight: 600;
color: var(--ink);
margin: 0 0 0.25rem;
}
.statement-section-title {
font-family: var(--font-display);
font-size: 1rem;
font-weight: 500;
color: var(--brand-800);
margin: 0 0 0.5rem;
}
/* Renewal notices (aviso-renovacion) — one card per policy due, see
docs/RENEWAL_NOTICES.md for the legacy report this replaces. */
.renewal-letters {
display: flex;
flex-direction: column;
gap: 1.25rem;
}
.renewal-letter {
background: var(--surface);
border: 1px solid var(--line);
border-radius: 8px;
padding: 1.125rem 1.25rem;
break-inside: avoid;
}
.renewal-letter-head {
display: flex;
justify-content: space-between;
align-items: flex-start;
border-bottom: 1px solid var(--line);
padding-bottom: 0.625rem;
margin-bottom: 0.75rem;
}
.renewal-letter-title {
font-family: var(--font-display);
font-size: 1.125rem;
font-weight: 600;
color: var(--ink);
margin: 0;
}
.renewal-letter-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.75rem;
margin-bottom: 0.75rem;
}
.renewal-letter-vehicle {
background: var(--paper-2);
border-radius: 6px;
padding: 0.625rem 0.75rem;
margin-bottom: 0.75rem;
}
.renewal-letter-coverage {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
gap: 0.75rem;
margin-bottom: 0.75rem;
}
.renewal-letter-coverage-item {
border-left: 2px solid var(--line-strong);
padding-left: 0.625rem;
}
.renewal-letter-value {
font-size: 0.875rem;
font-weight: 600;
color: var(--ink);
margin: 0.125rem 0 0;
}
.renewal-letter-premium {
display: flex;
gap: 1.25rem;
font-size: 0.875rem;
border-top: 1px solid var(--line);
padding-top: 0.625rem;
}
.renewal-letter-total {
font-weight: 700;
color: var(--brand-800);
}
@media print {
.renewal-letter {
page-break-inside: avoid;
page-break-after: always;
}
.renewal-letter:last-child {
page-break-after: auto;
}
}
/* Context buttons (the inline shortcut on existing pages) */
.context-reports {
display: flex;
flex-wrap: wrap;
gap: 0.375rem;
align-items: center;
margin-left: auto;
}
.context-reports-label {
font-size: 0.6875rem;
color: var(--muted-2);
text-transform: uppercase;
letter-spacing: 0.06em;
margin-right: 0.25rem;
font-weight: 500;
}
.context-report-link {
font-size: 0.75rem;
padding: 0.25rem 0.625rem;
border: 1px solid var(--line);
background: var(--surface);
color: var(--ink);
border-radius: 999px;
text-decoration: none;
transition: border-color 0.15s, color 0.15s;
}
.context-report-link:hover {
border-color: var(--brand-500);
color: var(--brand-700);
}