Files
jorgecuadros-platform/apps/web/src/app/globals.css
T
rmancinasandClaude Opus 5 48e01ddd21
Build and Push Images / Build jorgecuadros-api (push) Successful in 2m19s
Build and Push Images / Build jorgecuadros-web (push) Successful in 2m1s
feat(policies): capture the full premium breakdown
The capture form only ever had prima neta, derecho de póliza and comisión.
The Access form it replaces has seven figures, and the four that were missing
are the ones that make a policy paid in installments add up.

Adds recargo, IVA, prima total and forma de pago to the policy header, the
same breakdown per installment, and a per-line-of-business IVA rate.

IVA and prima total are the only derived figures:

    base  = prima neta + recargo + derecho de póliza
    IVA   = round(base * tasa)
    total = base + IVA

The recargo is inside the taxable base. That is not a guess — policy 7006785
prints IVA 52.03 on 610.86 + 8.55 + 31.00, and leaving the recargo out gives
51.35, which matches nothing on the page. Both of its money rows are asserted
in premium.spec.ts. The recargo itself is never derived: the carrier quotes it,
so staff key it in, and the field is disabled on ANNUAL/SINGLE. Both derived
figures are stored rather than recomputed on read, and stay editable, because
the printed policy is the record of truth and a later rate change must not
silently restate what was issued.

The rate lives on PolicyType (seeded to 0.08, editable in Catálogos), which is
the legacy one-row IMPUESTOS / IMPUESTOS_AUTOS tables made configurable. The
rate applied is stamped on the policy so an old one reads back at its original
rate.

Per-installment, not two fixed slots on the header: a policy split into several
exhibiciones prices each payment separately — that is why the Access form drew
the money row twice — and a trimestral policy needs four, which the Access
layout could not hold.

Also fixes two losses in the ETL, which is how these went missing:

  - `forma_pago` was marked consumed by the coverage sweep and then never
    written to any column, so FORMA PAGO existed nowhere in the platform.
  - `recargo` and the whole second money row fell into `coveragesJson` as
    loose strings, mislabeled as coverage amounts.

transform_policies.py now writes all of it directly;
backfill_policy_premium_breakdown.py recovers it on a database that must not be
re-imported, and strips the migrated keys back out of coveragesJson. Both are
COALESCE-only, so a figure a human has corrected in the app wins.

IVA and TOTAL are NOT backfilled: they were unbound calculated controls on the
Access form, never columns, so there is nothing to recover and every migrated
policy reads null until it is edited.

The backfill warns on 5 annual policies that carry a non-zero recargo — a
contradiction that predates this change and is left for a human, not silently
corrected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-18 00:24:22 -07:00

3190 lines
68 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;
}
/* Sub-label under an input: the computed figure behind an override field, or
why a field is disabled. Quiet enough not to compete with .field-label. */
.field-hint {
display: block;
font-size: 0.75rem;
line-height: 1.35;
color: var(--muted-2);
margin-top: 0.3125rem;
}
.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:disabled,
.select:disabled {
background: var(--surface-2, var(--surface));
color: var(--muted-2);
cursor: not-allowed;
}
.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);
}
/* ============================================================================
Layout + text utilities the screens already assumed
Several components were written against these names before any rule
defined them, so they rendered as bare inline spans. The visible symptom
was the policy OCR review header running together —
"Para revisarPágina 1700489616· PAMELA DENISE WAGONERLICENCIASANA" —
because JSX drops the newline between sibling elements and the `gap` those
call sites pass does nothing without a flex container.
========================================================================== */
.row {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 0.5rem;
}
.stack {
display: flex;
flex-direction: column;
gap: 1rem;
}
/* The muted line under a page title, and the same voice reused inline. Only
the block form takes a margin — as a flex child it would shift the item
off the row's centre line. */
.page-sub {
color: var(--muted);
font-size: 0.875rem;
}
p.page-sub {
margin: 0.25rem 0 0;
}
/* A neutral chip. Same shape as `.badge` so the OCR statuses, policy type and
carrier read as the labels they are rather than as running prose. */
.tag {
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;
white-space: nowrap;
background: var(--paper-2);
color: var(--muted);
border: 1px solid var(--line-strong);
}
/* The warning sibling of `.state-error`, used where a page needs a human to
choose between candidates rather than reporting a failure. */
.state-warn {
background: var(--servicios-tint);
border: 1px solid rgba(154, 106, 18, 0.25);
color: var(--servicios-ink);
border-radius: var(--radius);
padding: 1rem 1.125rem;
font-size: 0.875rem;
}