/* ==========================================================================
   DBB Self-Service Portal — design system
   Warm-paper background, DBB navy #002c74, Bricolage Grotesque for headings,
   Hanken Grotesk for body text and Spline Sans Mono for numbers; rounded 18px
   cards, soft shadows, tinted icon chips, subtle motion.
   Self-contained: no CSS framework. Fonts are self-hosted woff2 because the
   CSP only allows fonts from our own origin (font-src 'self'). Components read
   the :root tokens below, so change a colour or radius there, not per rule.
   ========================================================================== */

/* ── Self-hosted fonts (OFL — see /assets/fonts/OFL.txt) ─────────────────── */
@font-face { font-family: "Bricolage Grotesque"; font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/fonts/bricolage-grotesque-400.woff2") format("woff2"); }
@font-face { font-family: "Bricolage Grotesque"; font-style: normal; font-weight: 600; font-display: swap; src: url("/assets/fonts/bricolage-grotesque-600.woff2") format("woff2"); }
@font-face { font-family: "Bricolage Grotesque"; font-style: normal; font-weight: 700; font-display: swap; src: url("/assets/fonts/bricolage-grotesque-700.woff2") format("woff2"); }
@font-face { font-family: "Bricolage Grotesque"; font-style: normal; font-weight: 800; font-display: swap; src: url("/assets/fonts/bricolage-grotesque-800.woff2") format("woff2"); }
@font-face { font-family: "Hanken Grotesk"; font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/fonts/hanken-grotesk-400.woff2") format("woff2"); }
@font-face { font-family: "Hanken Grotesk"; font-style: normal; font-weight: 500; font-display: swap; src: url("/assets/fonts/hanken-grotesk-500.woff2") format("woff2"); }
@font-face { font-family: "Hanken Grotesk"; font-style: normal; font-weight: 600; font-display: swap; src: url("/assets/fonts/hanken-grotesk-600.woff2") format("woff2"); }
@font-face { font-family: "Hanken Grotesk"; font-style: normal; font-weight: 700; font-display: swap; src: url("/assets/fonts/hanken-grotesk-700.woff2") format("woff2"); }
@font-face { font-family: "Hanken Grotesk"; font-style: normal; font-weight: 800; font-display: swap; src: url("/assets/fonts/hanken-grotesk-800.woff2") format("woff2"); }
@font-face { font-family: "Spline Sans Mono"; font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/fonts/spline-sans-mono-400.woff2") format("woff2"); }
@font-face { font-family: "Spline Sans Mono"; font-style: normal; font-weight: 500; font-display: swap; src: url("/assets/fonts/spline-sans-mono-500.woff2") format("woff2"); }

:root {
  /* Colour palette. Emails and PDFs can't read CSS variables and copy some of
     these as hex literals - update views/emails and views/pdf too. */
  --c-bg: #F1EDE6;          /* app background — warm paper */
  --c-bg-2: #E9E3D9;        /* recessed areas */
  --c-surface: #FFFFFF;     /* cards */
  --c-surface-2: #F8F5F0;   /* inset rows, inputs, chips */
  --c-border: #E7E1D7;      /* borders */
  --c-border-strong: #DAD2C4;
  --c-divider: #F0EBE2;     /* subtle dividers, track fills */
  --c-text: #20262A;        /* primary text */
  --c-text-2: #586168;      /* secondary text */
  --c-text-3: #61696F;      /* tertiary / meta; still 4.5:1 on --c-bg (WCAG AA) */
  --c-muted: #8A9298;       /* input placeholders only - too light for real text */

  --c-brand: #002c74;       /* DBB navy — primary actions */
  --c-brand-hover: #0A3E8C; /* hover / gradient step */
  --c-brand-soft: #E6EEF5;  /* navy tint — icon chips, selected */
  --c-brand-ink: #FFFFFF;   /* text on navy */
  --c-brand-tint: #002c74;  /* navy text on soft tint */

  --c-accent: #93590F;      /* amber accent (due-soon) */
  --c-accent-soft: #F7EBD6;

  /* Status colours are used as text on their own -soft tint (badges, chips),
     so each pair keeps at least 4.5:1 contrast. */
  --c-success: #16774C;     --c-success-soft: #E0F0E7;
  --c-warning: #93590F;     --c-warning-soft: #F7EBD6;
  --c-danger: #B23D2A;      --c-danger-soft: #F6E3DD;

  --c-overlay: rgba(16, 26, 34, 0.45);

  /* Type scale: use these, not raw px. Bigger display figures (the hero
     amount, error codes, .fs-36/.fs-40) are set where they are used. */
  --fs-xs: 11.5px;   /* meta, table headers, eyebrows */
  --fs-sm: 12.5px;   /* secondary text, hints, badges */
  --fs-md: 13.5px;   /* dense UI: tables, menus, alerts */
  --fs-base: 14.5px; /* body, buttons, inputs */
  --fs-lg: 16px;     /* card titles, auth subtitles */
  --fs-xl: 18px;     /* topbar title, modal title */
  --fs-2xl: 22px;    /* stat values, section headings */
  --fs-3xl: 28px;    /* page and auth titles */

  /* Radii: pills 999px; cards 18px; menus/tiles/rows 14px; buttons/inputs 12px */
  --r-1: 6px; --r-2: 12px; --r-3: 14px; --r-4: 16px; --r-5: 18px;

  --sh-1: 0 1px 2px rgba(20, 40, 60, 0.05);
  --sh-2: 0 1px 2px rgba(20, 40, 60, 0.04), 0 8px 20px -14px rgba(20, 40, 60, 0.25);
  --sh-3: 0 1px 2px rgba(20, 40, 60, 0.04), 0 16px 36px -26px rgba(20, 40, 60, 0.28);
  --sh-brand: 0 12px 24px -14px rgba(0, 44, 116, 0.7);
  --sh-navy: 0 22px 44px -26px rgba(0, 26, 70, 0.55);

  /* Display = Bricolage Grotesque; body/UI = Hanken Grotesk; numerals/codes =
     Spline Sans Mono. Self-hosted above; system fallbacks if a face is missing.
     --f-serif stays an alias of --f-display so existing .h-serif markup works. */
  --f-display: "Bricolage Grotesque", ui-rounded, "Segoe UI", system-ui, -apple-system, sans-serif;
  --f-serif: var(--f-display);
  --f-sans: "Hanken Grotesk", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --f-mono: "Spline Sans Mono", ui-monospace, "SF Mono", Menlo, monospace;
}

*, *::before, *::after { box-sizing: border-box; }

html, body { height: 100%; }
body {
  margin: 0;
  font-family: var(--f-sans);
  color: var(--c-text);
  background: var(--c-bg);
  font-size: var(--fs-base);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  font-variant-numeric: tabular-nums;
}

a { color: var(--c-brand); text-decoration: none; }
a:hover { text-decoration: underline; }
button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
h1, h2, h3, h4 { margin: 0; }
::selection { background: rgba(0, 44, 116, 0.16); }

.h-serif { font-family: var(--f-display); font-weight: 600; letter-spacing: -0.01em; line-height: 1.15; }
.num { font-family: var(--f-display); font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.mono { font-family: var(--f-mono); }

/* ── Layout utilities ────────────────────────────────────────────────── */
.row { display: flex; align-items: center; }
.col { display: flex; flex-direction: column; }
.spread { justify-content: space-between; }
.center { justify-content: center; align-items: center; }
.items-start { align-items: flex-start; }
.items-baseline { align-items: baseline; }
.wrap { flex-wrap: wrap; }
.grow { flex: 1 1 auto; min-width: 0; }
.gap-1 { gap: 4px; } .gap-2 { gap: 8px; } .gap-3 { gap: 12px; }
.gap-4 { gap: 16px; } .gap-5 { gap: 20px; } .gap-6 { gap: 22px; }
.stack > * + * { margin-top: 16px; }
.m-0 { margin: 0; }
.mt-0 { margin-top: 0; } .mt-1 { margin-top: 4px; } .mt-2 { margin-top: 8px; }
.mt-3 { margin-top: 12px; } .mt-4 { margin-top: 16px; } .mt-5 { margin-top: 20px; } .mt-6 { margin-top: 24px; }
.mb-1 { margin-bottom: 4px; } .mb-2 { margin-bottom: 8px; } .mb-3 { margin-bottom: 12px; }
.mb-4 { margin-bottom: 16px; } .mb-5 { margin-bottom: 20px; } .mb-6 { margin-bottom: 24px; }
.mb-0 { margin-bottom: 0; }

.grid { display: grid; gap: 16px; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid-split { grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); }
.dash-grid { display: grid; gap: 22px; align-items: start; grid-template-columns: minmax(0, 1.7fr) minmax(0, 1fr); }

.muted { color: var(--c-text-3); }
.text-2 { color: var(--c-text-2); }
.text-3 { color: var(--c-text-3); }
.text-danger { color: var(--c-danger); }
.small { font-size: var(--fs-sm); }
.tiny { font-size: var(--fs-xs); }
.right { text-align: right; }
.center-text { text-align: center; }
.fw-500 { font-weight: 500; }
.fw-600 { font-weight: 600; }
.fw-700 { font-weight: 700; }
.uppercase { text-transform: uppercase; letter-spacing: 0.05em; font-size: var(--fs-xs); }
.nowrap { white-space: nowrap; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.w-full { width: 100%; }

/* ── Buttons ─────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border: 1px solid transparent; border-radius: var(--r-2);
  padding: 0 16px; height: 40px; font-weight: 700; font-size: var(--fs-base);
  letter-spacing: 0.005em; white-space: nowrap; text-decoration: none;
  transition: background 0.18s ease, border-color 0.18s ease, color 0.18s ease, transform 0.12s ease, box-shadow 0.18s ease, filter 0.18s ease;
}
.btn:hover { text-decoration: none; transform: translateY(-1px); }
.btn:active { transform: translateY(1px); transition-duration: 0.04s; }
.btn-primary { background: var(--c-brand); color: var(--c-brand-ink); border-color: var(--c-brand); box-shadow: var(--sh-brand); }
.btn-primary:hover { filter: brightness(1.08); color: var(--c-brand-ink); }
.btn-secondary { background: var(--c-surface); color: var(--c-text); border-color: var(--c-border); }
.btn-secondary:hover { background: var(--c-surface); border-color: var(--c-brand); color: var(--c-text); }
.btn-ghost { background: transparent; color: var(--c-text-2); }
.btn-ghost:hover { background: var(--c-surface-2); color: var(--c-text); }
.btn-danger { background: var(--c-surface); color: var(--c-danger); border-color: var(--c-border); }
.btn-danger:hover { background: var(--c-danger-soft); border-color: var(--c-danger); }
.btn-sm { height: 32px; padding: 0 12px; font-size: var(--fs-sm); border-radius: var(--r-2); }
.btn-lg { height: 48px; padding: 0 22px; font-size: var(--fs-base); }
.btn-full { width: 100%; }
.btn:disabled, .btn.is-disabled { opacity: 0.45; pointer-events: none; }

/* ── Forms ───────────────────────────────────────────────────────────── */
.field { display: flex; flex-direction: column; gap: 7px; margin-bottom: 16px; }
.field-label { font-size: var(--fs-md); font-weight: 600; color: var(--c-text); letter-spacing: 0.01em; }
.input, .select, .textarea {
  width: 100%; height: 46px; padding: 0 14px;
  background: var(--c-surface-2); border: 1px solid var(--c-border);
  border-radius: var(--r-2); font-size: var(--fs-base); color: var(--c-text);
  transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
}
.textarea { height: auto; padding: 12px 14px; resize: vertical; min-height: 90px; }
.input:focus, .select:focus, .textarea:focus {
  outline: none; border-color: var(--c-brand); background: var(--c-surface);
  box-shadow: 0 0 0 3px var(--c-brand-soft);
}
.input::placeholder, .textarea::placeholder { color: var(--c-muted); }
/* iOS Safari zooms the page into any focused field under 16px. */
@media (max-width: 900px) { .input, .select, .textarea, .cselect-btn { font-size: var(--fs-lg); } }
.input.is-invalid, .select.is-invalid { border-color: var(--c-danger); }
.field-hint { font-size: var(--fs-sm); color: var(--c-text-3); }
.field-error { font-size: var(--fs-sm); color: var(--c-danger); }
.input[type="date"]::-webkit-calendar-picker-indicator { cursor: pointer; opacity: 0.55; }
.input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 0.9; }
/* Read-only (e.g. the name from DBB records): no fill, so it reads as a value, not a field to type in. */
.input[readonly] { background: transparent; border-color: var(--c-divider); color: var(--c-text-2); cursor: default; }
.input[readonly]:focus { background: transparent; border-color: var(--c-border-strong); box-shadow: none; }

/* File drop zone (views/admin/verify.php, wired by app.js [data-dropzone]).
   The real <input type="file"> is hidden visually but stays focusable, so the
   keyboard focus ring is drawn on the zone through :focus-within. */
.dropzone-area {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  position: relative; padding: 26px 18px; text-align: center; cursor: pointer;
  background: var(--c-surface-2); border: 1.5px dashed var(--c-border-strong);
  border-radius: var(--r-3);
  transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
}
.dropzone-area:hover { border-color: var(--c-brand); background: var(--c-surface); }
.dropzone-area:focus-within { border-color: var(--c-brand); box-shadow: 0 0 0 3px var(--c-brand-soft); }
.dropzone-area.is-dragover { border-style: solid; border-color: var(--c-brand); background: var(--c-brand-soft); }
.dropzone-area.is-invalid { border-color: var(--c-danger); }
/* Children ignore the pointer so dragging across them does not flicker the highlight. */
.dropzone-area > span { pointer-events: none; }
.dropzone-input {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.dropzone-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; margin-bottom: 6px; border-radius: 50%;
  background: var(--c-brand-soft); color: var(--c-brand-tint);
}
.dropzone-title { font-size: var(--fs-base); font-weight: 600; color: var(--c-text); }
.dropzone-browse { font-size: var(--fs-base); color: var(--c-brand); text-decoration: underline; text-underline-offset: 3px; }
.dropzone-meta { font-size: var(--fs-sm); color: var(--c-text-3); margin-top: 2px; }
.dropzone-file {
  display: flex; align-items: center; gap: 12px; padding: 12px 12px 12px 14px;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--r-3);
  box-shadow: var(--sh-1);
}
.dropzone-file[hidden], .dropzone-area[hidden] { display: none; }
.dropzone-file-icon {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 40px; height: 40px; border-radius: var(--r-2);
  background: var(--c-brand-soft); color: var(--c-brand-tint);
}
.dropzone-file-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.dropzone-file-name { font-size: var(--fs-base); font-weight: 600; color: var(--c-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dropzone-file-size { font-size: var(--fs-sm); color: var(--c-text-3); }
.dropzone-clear {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 34px; height: 34px; border: 0; border-radius: 50%; cursor: pointer;
  background: transparent; color: var(--c-text-2);
  transition: background 0.15s, color 0.15s;
}
.dropzone-clear:hover, .dropzone-clear:focus-visible { background: var(--c-danger-soft); color: var(--c-danger); outline: none; }

/* ── Card ────────────────────────────────────────────────────────────── */
.card { background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--r-5); box-shadow: var(--sh-3); }
.card-pad { padding: 22px; }
.card-pad-lg { padding: 28px; }
.card-title { font-weight: 700; font-size: var(--fs-lg); letter-spacing: -0.005em; }

/* ── Badge / status pill ─────────────────────────────────────────────── */
.badge {
  display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px;
  border-radius: 999px; font-size: var(--fs-sm); font-weight: 700; letter-spacing: 0.005em;
  border: 1px solid transparent; white-space: nowrap;
}
.badge-current { background: var(--c-success-soft); color: var(--c-success); }
.badge-due { background: var(--c-warning-soft); color: var(--c-warning); }
.badge-past { background: var(--c-danger-soft); color: var(--c-danger); }
.badge-neutral { background: var(--c-surface-2); color: var(--c-text-2); border-color: var(--c-border); }
.badge-brand { background: var(--c-brand-soft); color: var(--c-brand); }
.badge-dot::before { content: ""; width: 6px; height: 6px; border-radius: 999px; background: currentColor; }

/* ── Progress ────────────────────────────────────────────────────────── */
.progress { height: 8px; background: var(--c-divider); border-radius: 999px; overflow: hidden; }
.progress-slim { height: 6px; }
/* The native <progress> element styled as .progress: its value sets the
   width, so templates need no style="width:…" (keeps the CSP strict). */
progress.progress { display: block; width: 100%; border: 0; appearance: none; -webkit-appearance: none; color: var(--c-brand); }
progress.progress::-webkit-progress-bar { background: var(--c-divider); border-radius: 999px; }
progress.progress::-webkit-progress-value { background: var(--c-brand); border-radius: 999px; }
progress.progress::-moz-progress-bar { background: var(--c-brand); border-radius: 999px; }

/* ── Divider / Table ─────────────────────────────────────────────────── */
.divider { height: 1px; background: var(--c-divider); border: 0; margin: 0; }
.tbl { width: 100%; border-collapse: collapse; font-size: var(--fs-md); }
.tbl th {
  text-align: left; font-weight: 600; color: var(--c-text-3); font-size: var(--fs-xs);
  letter-spacing: 0.04em; text-transform: uppercase; padding: 12px;
  border-bottom: 1px solid var(--c-divider); background: var(--c-surface-2);
}
.tbl td { padding: 13px 12px; border-bottom: 1px solid var(--c-divider); vertical-align: middle; }
.tbl tr:last-child td { border-bottom: 0; }
.tbl tbody tr:hover td { background: var(--c-surface-2); }
.tbl .right { text-align: right; }
/* Equal-width columns (fixed layout); the min-width makes narrow screens scroll the table instead of squeezing it. */
.tbl-even { table-layout: fixed; min-width: 560px; }
.tbl-wrap { overflow-x: auto; }
.tbl .tbl-empty { padding: 28px; }
/* Sortable headings (app.js wraps each th[data-sort] label in a button). The
   arrow comes first so a right-aligned label still lines up with its column. */
.th-sort { display: inline-flex; align-items: center; gap: 5px; padding: 0; border: 0; background: none;
  font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit; cursor: pointer; }
.th-sort::before { content: "\2195"; opacity: 0.45; }
.th-sort:hover, th[aria-sort] .th-sort { color: var(--c-text); }
th[aria-sort="ascending"] .th-sort::before { content: "\2191"; opacity: 1; }
th[aria-sort="descending"] .th-sort::before { content: "\2193"; opacity: 1; }
/* .tbl-right: every column right-aligned on wider screens, so the columns
   don't zig-zag left/right; the ghost button's own padding is pulled back so
   its text lines up with the others. Phones use the card layout below. */
@media (min-width: 601px) {
  .tbl-right th, .tbl-right td { text-align: center; }
  .tbl-right td .btn-ghost:last-child { margin-right: -12px; }
}
/* Phones: .tbl-stack turns each row into a card of "label  value" lines
   (labels from the cells' data-label). .tbl-stack-2col instead lays a short
   row out as two lines of two cells, without labels (payment history). */
@media (max-width: 600px) {
  .tbl-stack, .tbl-stack tbody, .tbl-stack tr, .tbl-stack td { display: block; }
  .tbl-stack.tbl-even { min-width: 0; table-layout: auto; }
  .tbl-stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .tbl-stack tr { padding: 12px 16px; border-bottom: 1px solid var(--c-divider); }
  .tbl-stack tr:last-child { border-bottom: 0; }
  .tbl-stack td { display: flex; justify-content: space-between; align-items: center; gap: 12px; white-space: normal;
    padding: 4px 0; border: 0; text-align: right; overflow-wrap: anywhere; }
  .tbl-stack td[data-label]::before { content: attr(data-label); flex: none; text-align: left;
    color: var(--c-text-3); font-size: var(--fs-sm); }
  .tbl-stack tbody tr:hover td { background: transparent; }
  .tbl-stack .tbl-empty { justify-content: center; padding: 20px 0; }
  .tbl-stack-2col tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; row-gap: 6px; }
  .tbl-stack-2col td:nth-child(odd) { justify-content: flex-start; text-align: left; }
  .tbl-stack-2col td:nth-child(even) { justify-content: flex-end; }
  .tbl-stack-2col .tbl-empty { grid-column: 1 / -1; }
}
/* Rows and footers separated by a divider line. */
.kv-line { padding: 7px 0; border-bottom: 1px solid var(--c-divider); }
.list-row { padding: 12px 0; border-bottom: 1px solid var(--c-divider); }
.divider-top { padding-top: 14px; border-top: 1px solid var(--c-divider); }
.pager-row { padding-top: 16px; margin-top: 4px; border-top: 1px solid var(--c-divider); }
.card-head { padding-bottom: 14px; }
.empty-state { padding: 36px 0; }
.empty-state-sm { padding: 30px 0; }
.empty-note { padding: 14px 0 4px; }
.is-dim { opacity: 0.4; pointer-events: none; }
.btn-back { margin-left: -12px; }
.narrow { max-width: 520px; margin: 0 auto; }
.narrow-sm { max-width: 480px; margin: 0 auto; }
.code-box { display: block; padding: 10px; background: var(--c-surface-2); border: 1px solid var(--c-border); border-radius: var(--r-2); letter-spacing: 0.08em; user-select: all; }
/* Error pages (404, 403, 500, 503). */
.error-page { padding: 40px 0; }
.error-code { font-size: 64px; color: var(--c-brand); }
.error-title { font-size: var(--fs-2xl); margin-top: 6px; }
/* Dashboard hero: two panels tinted by the loan's state. */
.hero-card { overflow: hidden; display: flex; flex-wrap: wrap; }
/* The main panel takes nearly all spare width; when the two wrap onto
   separate lines (phones), the button panel then fills its whole line so
   its tint spans the card. */
.hero-main { flex: 999 1 250px; min-width: 250px; padding: 24px 26px; }
.hero-side { flex: 1 1 190px; padding: 24px 26px; min-width: 190px; }
/* The amount owed, large, with the currency code smaller beside it. */
.hero-amount { font-size: 3.5rem; line-height: 1; overflow-wrap: anywhere; }
.hero-ccy { font-size: 0.4em; font-weight: 600; letter-spacing: 0.04em; color: var(--c-text-2); margin-left: 8px; }
@media (max-width: 480px) { .hero-amount { font-size: 2.6rem; } }
.hero-danger { background: linear-gradient(180deg, var(--c-danger-soft), transparent 92%); }
.hero-success { background: linear-gradient(180deg, var(--c-success-soft), transparent 92%); }
.hero-warning { background: linear-gradient(180deg, var(--c-warning-soft), transparent 92%); }

/* ── Alerts / flash ──────────────────────────────────────────────────── */
.alert {
  display: flex; gap: 10px; align-items: flex-start; padding: 13px 15px;
  border-radius: var(--r-3); font-size: var(--fs-md); border: 1px solid transparent; margin-bottom: 16px;
}
.alert-success { background: var(--c-success-soft); color: var(--c-success); }
.alert-danger  { background: var(--c-danger-soft);  color: var(--c-danger); }
.alert-warning { background: var(--c-warning-soft); color: var(--c-warning); }
.alert-info    { background: var(--c-brand-soft);   color: var(--c-brand); }
/* Status chips in a wrapping row (loan details): same colours as the
   alerts above, sized to their content. */
.note-row { display: flex; flex-wrap: wrap; gap: 8px; }
/* One-line status chip: icon, bold title, then each detail after a dot.
   On a narrow screen the text wraps inside the chip. */
.alert-chip { display: inline-flex; align-items: center; flex-wrap: wrap; column-gap: 6px; row-gap: 2px;
  margin: 0; padding: 6px 12px; border-radius: var(--r-4); font-size: var(--fs-sm); line-height: 1.35; }
.alert-chip-sep { opacity: 0.55; }

/* ── Brand mark / logo ───────────────────────────────────────────────── */
.dbb-mark { display: inline-flex; align-items: center; gap: 11px; font-weight: 700; color: var(--c-text); }
.dbb-mark:hover { text-decoration: none; }

/* ── App shell (authenticated layout): top bar with the navigation ────── */
.app-shell { display: flex; flex-direction: column; height: 100vh; overflow: hidden; }
.app-topbar {
  position: relative; z-index: 30; display: flex; align-items: center; gap: 14px; padding: 0 28px; height: 64px;
  background: var(--c-surface); border-bottom: 1px solid var(--c-border); flex: 0 0 auto;
}
.app-topbar .brand { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-right: 10px; }
.top-nav { display: flex; align-items: center; gap: 4px; }
.nav-item {
  display: flex; align-items: center; gap: 8px; padding: 8px 12px; border-radius: var(--r-2);
  border: 0; background: transparent; color: var(--c-text-2); font-size: var(--fs-md); font-weight: 600;
  white-space: nowrap; text-decoration: none; transition: background 0.12s, color 0.12s;
}
.nav-item:hover { background: var(--c-surface-2); color: var(--c-text); text-decoration: none; }
.nav-item.active { background: var(--c-brand-soft); color: var(--c-brand); font-weight: 700; }
.nav-item .nav-ico { width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; }
/* The area's title and subtitle (the page's <h1>), above the page. */
.area-head { margin-bottom: 22px; }
.area-title { font-family: var(--f-display); font-weight: 700; font-size: var(--fs-xl); color: var(--c-text); line-height: 1.15; }
.area-sub { font-size: var(--fs-sm); color: var(--c-text-3); margin-top: 2px; }
.app-content { flex: 1 1 auto; overflow-y: auto; padding: 30px 32px 48px; scroll-behavior: smooth; }
.app-content-inner { max-width: 1200px; margin: 0 auto; }
.scroll-y { overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--c-border-strong) transparent; }

.avatar {
  width: 36px; height: 36px; border-radius: 999px; background: var(--c-brand); color: var(--c-brand-ink);
  display: inline-flex; align-items: center; justify-content: center; font-weight: 700; font-size: var(--fs-md); flex: 0 0 auto;
}

/* Icon chip: tinted rounded square behind an icon. .icon-tile is an older
   name for the same thing, still used by the auth, MFA and profile pages. */
.icon-chip, .icon-tile {
  width: 40px; height: 40px; border-radius: var(--r-2); background: var(--c-brand-soft); color: var(--c-brand);
  display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
.icon-chip-sm { width: 38px; height: 38px; border-radius: var(--r-2); }
/* Centred chip above a heading (auth, MFA and profile notices). */
.icon-chip-center { width: 48px; height: 48px; margin: 0 auto 14px; }
.icon-chip-center-sm { width: 44px; height: 44px; margin: 0 auto 12px; }
.icon-chip-lg { width: 54px; height: 54px; border-radius: var(--r-4); }
.icon-chip-xl { width: 64px; height: 64px; border-radius: 50%; }
.icon-chip-success, .icon-tile-success { background: var(--c-success-soft); color: var(--c-success); }
.icon-chip-warning { background: var(--c-warning-soft); color: var(--c-warning); }
.icon-chip-danger, .icon-tile-danger { background: var(--c-danger-soft); color: var(--c-danger); }
.ico { display: inline-block; vertical-align: middle; flex: 0 0 auto; }

/* ── Dropdown menu (CSP-safe, toggled by app.js) ─────────────────────── */
.menu { position: relative; }
.menu-panel {
  position: absolute; top: calc(100% + 6px); right: 0; min-width: 220px;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--r-3);
  box-shadow: var(--sh-3); z-index: 40; overflow: hidden; padding: 6px; display: none;
}
.menu.open .menu-panel { display: block; transform-origin: top right; animation: menu-in 0.16s cubic-bezier(0.2, 0.7, 0.3, 1) both; }
.menu-item {
  display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 10px;
  border-radius: var(--r-2); border: 0; background: transparent; color: var(--c-text);
  font-size: var(--fs-md); text-align: left; text-decoration: none;
}
.menu-item:hover { background: var(--c-surface-2); text-decoration: none; color: var(--c-text); }
.menu-sep { height: 1px; background: var(--c-divider); margin: 6px 0; border: 0; }
/* Who is signed in, at the top of the account menu. */
.menu-head { padding: 8px 10px; }

/* ── Page header ─────────────────────────────────────────────────────── */
.page-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; margin-bottom: 22px; }
.page-title { font-family: var(--f-display); font-weight: 600; font-size: var(--fs-3xl); letter-spacing: -0.01em; line-height: 1.1; }
.page-sub { color: var(--c-text-2); font-size: var(--fs-base); margin-top: 6px; }
.eyebrow { font-size: var(--fs-sm); color: var(--c-text-3); letter-spacing: 0.07em; text-transform: uppercase; font-weight: 600; }

/* ── Stat cards / tiles ──────────────────────────────────────────────── */
.stat { padding: 18px 20px; }
.stat-tile { background: var(--c-surface-2); border: 1px solid var(--c-border); border-radius: var(--r-3); padding: 16px; }
.stat-tile-label { font-size: var(--fs-xs); color: var(--c-text-3); letter-spacing: 0.04em; text-transform: uppercase; }
.stat-tile-value { font-family: var(--f-display); font-size: var(--fs-2xl); font-weight: 600; letter-spacing: -0.01em; margin-top: 4px; }

/* ── Loan tile ───────────────────────────────────────────────────────── */
/* partials/loan_card.php: dashboard "Your loans" and the /loans list. */
.loan-tile {
  display: flex; align-items: center; gap: 16px;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--r-4);
  padding: 18px 20px; box-shadow: var(--sh-1);
  transition: border-color 0.18s ease, box-shadow 0.18s ease;
}
.loan-tile:hover { border-color: var(--c-brand); box-shadow: var(--sh-2); }
.loan-tile-main { flex: 1 1 auto; min-width: 0; }
.loan-tile-amount { flex: none; text-align: right; }
.loan-tile-actions { flex: none; display: flex; flex-direction: column; gap: 8px; min-width: 96px; }
.loan-tile-actions .btn { width: 100%; }
@media (max-width: 760px) {
  .loan-tile { flex-wrap: wrap; }
  .loan-tile-amount { text-align: left; order: 3; }
  .loan-tile-actions { flex-direction: row; width: 100%; order: 4; }
}

/* ── Client-side pager ───────────────────────────────────────────────── */
/* Payment history, dashboard loans and the /loans list. */
.pager-pages { display: flex; gap: 6px; }
.pager-pages .btn { min-width: 34px; justify-content: center; padding-left: 8px; padding-right: 8px; }
.pager-ellipsis { align-self: center; color: var(--c-text-3); padding: 0 2px; }

/* ── Quick actions grid ──────────────────────────────────────────────── */
.quick-grid { display: grid; grid-template-columns: 1fr; gap: 8px; }
.quick-tile {
  display: flex; align-items: center; gap: 12px; padding: 12px 14px;
  border: 1px solid var(--c-border); border-radius: var(--r-3); background: var(--c-surface-2);
  text-decoration: none; color: inherit; transition: border-color 0.15s, background 0.15s, transform 0.15s;
}
.quick-tile:hover { border-color: var(--c-brand); background: var(--c-surface); color: inherit; text-decoration: none; transform: translateY(-1px); }
.quick-tile-label { font-weight: 700; font-size: var(--fs-md); color: var(--c-text); }
.quick-tile-sub { font-size: var(--fs-xs); color: var(--c-text-3); }

/* ── Payment page: stepper, option rows, method panels, copy rows ─────── */
.pay-page { margin: 0 auto; }
.step-bar { display: flex; gap: 6px; }
.step-seg { height: 4px; flex: 1; border-radius: 2px; background: var(--c-border); transition: background 0.25s ease; }
.step-seg.done { background: var(--c-brand); }
.pay-step { display: none; }
.pay-step.active { display: block; animation: fade-in 0.22s ease both; }

.option-row {
  display: flex; align-items: center; gap: 14px; width: 100%; text-align: left; padding: 15px 16px;
  border-radius: var(--r-3); border: 1px solid var(--c-border); background: var(--c-surface-2);
  color: inherit; cursor: pointer; transition: border-color 0.15s, background 0.15s;
}
.option-row:hover { border-color: var(--c-brand); }
.option-row.selected { border: 1.5px solid var(--c-brand); background: var(--c-brand-soft); padding: 14.5px 15.5px; }
.option-radio { width: 20px; height: 20px; border-radius: 50%; border: 2px solid var(--c-brand); display: flex; align-items: center; justify-content: center; flex: 0 0 auto; }
.option-row.selected .option-radio::after { content: ""; width: 10px; height: 10px; border-radius: 50%; background: var(--c-brand); }
.option-title { font-weight: 700; font-size: var(--fs-base); color: var(--c-text); }
.option-desc { font-size: var(--fs-sm); color: var(--c-text-3); }
.option-amount { font-weight: 700; font-size: var(--fs-base); color: var(--c-text); margin-left: auto; }

.method-panel { display: none; margin-top: 10px; padding: 16px; border: 1px solid var(--c-border); border-radius: var(--r-3); background: var(--c-surface); }
.method-panel.active { display: block; animation: fade-in 0.2s ease both; }

.copy-row { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border: 1px solid var(--c-border); border-radius: var(--r-2); background: var(--c-surface-2); }
.copy-row + .copy-row { margin-top: 8px; }
.cr-label { font-size: var(--fs-xs); color: var(--c-text-3); text-transform: uppercase; letter-spacing: 0.04em; }
.cr-value { font-weight: 600; font-size: var(--fs-base); color: var(--c-text); word-break: break-word; }
.copy-btn { width: 34px; height: 34px; border-radius: var(--r-2); border: 1px solid var(--c-border); background: var(--c-surface); color: var(--c-text-2); display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto; margin-left: auto; }
.copy-btn:hover { color: var(--c-brand); border-color: var(--c-brand); }
.copy-btn.copied { color: var(--c-success); border-color: var(--c-success); }

.kv-stack { display: flex; flex-direction: column; gap: 1px; border: 1px solid var(--c-border); border-radius: var(--r-3); overflow: hidden; }
.kv-row { display: flex; align-items: center; justify-content: space-between; padding: 13px 16px; background: var(--c-surface); }
.kv-row:nth-child(even) { background: var(--c-surface-2); }
.kv-key { font-size: var(--fs-md); color: var(--c-text-3); font-weight: 500; }
.kv-val { font-size: var(--fs-md); color: var(--c-text); font-weight: 700; }

/* ── Auth layout: one centred card on the plain background ───────────── */
.auth-shell { min-height: 100vh; display: flex; flex-direction: column; }
.auth-top { display: flex; justify-content: flex-end; padding: 22px 28px 0; }
.auth-body { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 22px; padding: 24px 16px; }
.auth-card { width: 100%; max-width: 440px; padding: 36px 32px; }
.auth-foot { text-align: center; color: var(--c-text-3); font-size: var(--fs-sm); }
.auth-panel-head { margin-bottom: 26px; }
.auth-panel-title { font-family: var(--f-display); font-weight: 700; font-size: var(--fs-3xl); letter-spacing: -0.01em; margin: 0 0 6px; }
.auth-panel-sub { color: var(--c-text-2); font-size: var(--fs-base); line-height: 1.5; margin: 0; }
/* Shared heading for the other auth screens (forgot / reset / confirm / mfa) */
.auth-head { margin-bottom: 22px; }
.auth-title { font-family: var(--f-display); font-weight: 700; font-size: var(--fs-3xl); letter-spacing: -0.01em; line-height: 1.2; }

/* Iconed inputs + password reveal */
.field-control { position: relative; display: flex; align-items: center; }
.field-ico { position: absolute; top: 50%; transform: translateY(-50%); display: inline-flex;
  align-items: center; justify-content: center; color: var(--c-text-3); pointer-events: none; }
.field-ico-left { left: 14px; }
.field-ico-right { right: 7px; }
.input-iconed { padding-left: 44px; }
.input-iconed-r { padding-right: 44px; }
.pw-toggle { width: 34px; height: 34px; border: 0; background: transparent; border-radius: var(--r-2);
  color: var(--c-text-3); cursor: pointer; pointer-events: auto; }
.pw-toggle:hover { color: var(--c-text); background: var(--c-surface-2); }
.pw-eye-off { display: none; }
.pw-toggle.revealed .pw-eye { display: none; }
.pw-toggle.revealed .pw-eye-off { display: inline-flex; }
.field-row-end { display: flex; justify-content: flex-end; margin: -6px 0 18px; }

.auth-divider { display: flex; align-items: center; gap: 14px; margin: 22px 0; color: var(--c-text-3); font-size: var(--fs-sm); }
.auth-divider::before, .auth-divider::after { content: ""; flex: 1; height: 1px; background: var(--c-border); }

.link-btn { border: 0; background: transparent; color: var(--c-brand); font-weight: 600;
  font-size: inherit; font-family: inherit; padding: 0; cursor: pointer; }
.link-btn:hover { text-decoration: underline; }
/* EN · ES switcher: the current language in brand colour, the other plain. */
.lang-btn { color: inherit; }
.lang-btn.is-current { color: var(--c-brand); }
.lang-form { margin-right: 6px; }
.user-menu-btn { background: transparent; border: 0; padding: 4px 6px 4px 4px; border-radius: var(--r-2); }

@media (max-width: 480px) {
  .auth-card { padding: 28px 20px; }
}

/* ── Tab panels ──────────────────────────────────────────────────────── */
/* Login/register; app.js toggles .active from the [data-tab] buttons. */
.tabs { display: flex; background: var(--c-surface-2); border: 1px solid var(--c-border); border-radius: var(--r-2); padding: 3px; margin-bottom: 20px; }
.tab {
  flex: 1; height: 36px; border: 0; background: transparent; border-radius: var(--r-1);
  font-size: var(--fs-md); font-weight: 600; color: var(--c-text-2);
  display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 0 10px; white-space: nowrap;
}
/* A count beside a tab's label (the dashboard's tabs). */
.tab .badge { padding: 1px 7px; }
.tab.active { background: var(--c-surface); color: var(--c-brand); box-shadow: var(--sh-1); }
.tab-panel { display: none; }
.tab-panel.active { display: block; animation: fade-in 0.22s ease backwards; }

/* ── OTP / recovery codes ────────────────────────────────────────────── */
.otp-input { letter-spacing: 0.5em; font-size: var(--fs-2xl); text-align: center; height: 52px; font-family: var(--f-mono); }
.recovery-codes {
  font-family: var(--f-mono); background: var(--c-surface-2); border: 1px solid var(--c-border);
  border-radius: var(--r-3); padding: 16px; display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px 20px;
}
.recovery-codes span { font-size: var(--fs-base); letter-spacing: 0.06em; }
.qr-frame { display: inline-flex; padding: 12px; background: #fff; border: 1px solid var(--c-border); border-radius: var(--r-3); }
.qr-frame svg { display: block; width: 200px; height: 200px; }

/* ── Focus ring ──────────────────────────────────────────────────────── */
:focus-visible { outline: 2px solid var(--c-brand); outline-offset: 2px; border-radius: var(--r-1); }

/* ── Motion ──────────────────────────────────────────────────────────── */
/* Cross-page fade via the View Transitions API; browsers without it ignore these rules. */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: vt-out 0.16s ease both; }
::view-transition-new(root) { animation: vt-in 0.32s cubic-bezier(0.2, 0.7, 0.3, 1) both; }
@keyframes vt-out { to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
@keyframes rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes menu-in { from { opacity: 0; transform: translateY(-6px) scale(0.98); } to { opacity: 1; transform: none; } }
@keyframes pop-check { 0% { transform: scale(0.4); opacity: 0; } 60% { transform: scale(1.12); } 100% { transform: scale(1); opacity: 1; } }

/* "backwards", not "both": a transform still applied after the animation
   would trap position:fixed children (page modals) inside the container. */
.app-content-inner { animation: rise 0.34s cubic-bezier(0.2, 0.7, 0.3, 1) backwards; }
.auth-card { animation: rise 0.32s cubic-bezier(0.2, 0.7, 0.3, 1) backwards; }
.alert { animation: rise 0.28s ease both; }
.pop-check { animation: pop-check 0.4s cubic-bezier(0.2, 0.9, 0.3, 1.2) both; }

/* ── Mobile ──────────────────────────────────────────────────────────── */
.hamburger { display: none; flex: none; width: 40px; height: 40px; align-items: center; justify-content: center; border: 1px solid var(--c-border); background: var(--c-surface-2); border-radius: var(--r-2); color: var(--c-text-2); }
/* Under the top bar (z-index 30), over the page, while the drop-down is open. */
.nav-backdrop { display: none; position: fixed; inset: 0; background: var(--c-overlay); z-index: 25; }

/* Narrower screens: the navigation folds into a drop-down under the top bar,
   opened by the menu button (app.js toggles .nav-open on .app-shell). */
@media (max-width: 1024px) {
  .hamburger { display: inline-flex; }
  .top-nav {
    display: none; position: absolute; top: 100%; left: 0; right: 0; flex-direction: column; align-items: stretch;
    padding: 10px 16px 14px; background: var(--c-surface); border-bottom: 1px solid var(--c-border); box-shadow: var(--sh-3);
  }
  .top-nav .nav-item { padding: 11px 12px; font-size: var(--fs-base); }
  .app-shell.nav-open .top-nav { display: flex; }
  .app-shell.nav-open .nav-backdrop { display: block; }
}

@media (max-width: 900px) {
  .app-topbar { padding: 0 16px; }
  .app-content { padding: 20px 16px 40px; }
  .grid-split, .grid-2, .grid-3, .dash-grid { grid-template-columns: 1fr; }
  /* Four figures (loan details) stay 2 x 2 rather than one tall column. */
  .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* The page itself scrolls on phones and tablets (not an inner box), so the
     browser's address bar can collapse, Back restores the scroll position,
     and nothing sits behind the browser's own toolbars. */
  .app-shell { height: auto; min-height: 100dvh; overflow: visible; }
  .app-content { overflow: visible; }
  .app-topbar { position: sticky; top: 0; z-index: 30; }
  .page-title { font-size: var(--fs-2xl); }
}

/* Touch screens: every tap target at least 44px (WCAG 2.5.5 / platform
   guidelines). Icons stay the same size; only the hit area grows. */
@media (pointer: coarse) {
  .btn, .btn-sm { min-height: 44px; }
  .pager-pages .btn { min-width: 44px; }
  .hamburger, .copy-btn, .pw-toggle, .dropzone-clear { width: 44px; height: 44px; }
  .lang-btn { min-width: 44px; min-height: 44px; }
  .toast-close { padding: 12px; margin: -9px -9px -9px 0; }
  .input-iconed-r { padding-right: 54px; }
  .user-menu-btn { min-height: 44px; }
  .menu-item { min-height: 44px; }
}

@media (max-width: 560px) {
  .recovery-codes { grid-template-columns: 1fr; }
  /* Tabs that don't fit on one line wrap to a second, so none is hidden. */
  .tabs { flex-wrap: wrap; }
  .tab { flex: 1 1 auto; }
  .stat-tile { padding: 12px 14px; }
  .stat-tile-value { font-size: var(--fs-lg); }
  .stat-only-desktop { display: none; }
}

/* ── Confirm modal ───────────────────────────────────────────────────── */
/* Built by showConfirm() in app.js. */
body.modal-open { overflow: hidden; }
.modal-root { position: fixed; inset: 0; z-index: 100; display: flex; align-items: center; justify-content: center; padding: 20px; }
.modal-root.is-leaving { opacity: 0; transition: opacity 0.15s ease; }
.modal-backdrop { position: absolute; inset: 0; background: var(--c-overlay); -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px); animation: modal-fade 0.18s ease both; }
.modal-dialog {
  position: relative; width: 100%; max-width: 416px; background: var(--c-surface);
  border: 1px solid var(--c-border); border-radius: var(--r-5); box-shadow: var(--sh-navy);
  padding: 26px 24px 20px; text-align: center; animation: modal-pop 0.2s cubic-bezier(0.2, 0.8, 0.3, 1) both;
}
.modal-icon { width: 54px; height: 54px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; margin-bottom: 14px; }
.modal-icon.is-warning { background: var(--c-warning-soft); color: var(--c-warning); }
.modal-icon.is-danger { background: var(--c-danger-soft); color: var(--c-danger); }
.modal-icon.is-info { background: var(--c-brand-soft); color: var(--c-brand); }
.modal-title { font-family: var(--f-display); font-weight: 600; font-size: var(--fs-xl); color: var(--c-text); margin-bottom: 6px; letter-spacing: -0.01em; }
.modal-text { font-size: var(--fs-base); color: var(--c-text-2); line-height: 1.5; margin: 0 0 22px; }
/* wrap-reverse: when the buttons don't fit side by side, the main action
   (last in the markup, on the right) goes on top. */
.modal-actions { display: flex; flex-wrap: wrap-reverse; gap: 10px; justify-content: center; }
.modal-actions .btn { min-width: 120px; }
.btn-danger-solid { background: var(--c-danger); color: #fff; border-color: var(--c-danger); box-shadow: 0 12px 24px -14px rgba(178, 61, 42, 0.7); }
.btn-danger-solid:hover { filter: brightness(1.06); color: #fff; }
@keyframes modal-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes modal-pop { from { opacity: 0; transform: translateY(8px) scale(0.96); } to { opacity: 1; transform: none; } }

/* ── Toasts (flash messages, enhanced by app.js) ─────────────────────── */
.toast-stack { position: fixed; top: 16px; right: 16px; z-index: 110; display: flex; flex-direction: column; gap: 10px; width: min(360px, calc(100vw - 32px)); pointer-events: none; }
.toast {
  pointer-events: auto; position: relative; display: flex; gap: 11px; align-items: flex-start;
  background: var(--c-surface); border: 1px solid var(--c-border); border-left-width: 3px;
  border-radius: var(--r-3); box-shadow: var(--sh-3); padding: 13px 13px 14px; overflow: hidden;
  animation: toast-in 0.28s cubic-bezier(0.2, 0.8, 0.3, 1) both;
}
.toast.is-leaving { animation: toast-out 0.22s ease forwards; }
.toast-ico { flex: 0 0 auto; margin-top: 1px; line-height: 0; }
.toast-body { flex: 1 1 auto; font-size: var(--fs-md); color: var(--c-text); line-height: 1.45; }
.toast-close { flex: 0 0 auto; border: 0; background: transparent; color: var(--c-text-3); cursor: pointer; padding: 3px; border-radius: var(--r-1); line-height: 0; }
.toast-close:hover { background: var(--c-surface-2); color: var(--c-text); }
.toast-bar { position: absolute; left: 0; bottom: 0; height: 2px; width: 100%; background: currentColor; opacity: 0.35; transform-origin: left; animation: toast-bar linear forwards; }
.toast-success { border-left-color: var(--c-success); }
.toast-success .toast-ico, .toast-success .toast-bar { color: var(--c-success); }
.toast-danger { border-left-color: var(--c-danger); }
.toast-danger .toast-ico, .toast-danger .toast-bar { color: var(--c-danger); }
.toast-warning { border-left-color: var(--c-warning); }
.toast-warning .toast-ico, .toast-warning .toast-bar { color: var(--c-warning); }
.toast-info { border-left-color: var(--c-brand); }
.toast-info .toast-ico, .toast-info .toast-bar { color: var(--c-brand); }
@keyframes toast-in { from { opacity: 0; transform: translateX(16px); } to { opacity: 1; transform: none; } }
@keyframes toast-out { to { opacity: 0; transform: translateX(16px); } }
@keyframes toast-bar { from { transform: scaleX(1); } to { transform: scaleX(0); } }

/* ── Custom select (enhances <select class="select">, CSP-safe) ──────── */
.cselect { position: relative; }
.cselect-native { display: none; }
.cselect-btn {
  width: 100%; height: 46px; display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 0 12px 0 14px; background: var(--c-surface-2); border: 1px solid var(--c-border);
  border-radius: var(--r-2); font-size: var(--fs-base); color: var(--c-text); cursor: pointer; text-align: left;
  transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
}
.cselect-btn:hover { border-color: var(--c-border-strong); }
.cselect.open .cselect-btn, .cselect-btn:focus-visible {
  outline: none; border-color: var(--c-brand); background: var(--c-surface); box-shadow: 0 0 0 3px var(--c-brand-soft);
}
.cselect-btn-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cselect-caret { flex: 0 0 auto; color: var(--c-text-3); line-height: 0; transition: transform 0.18s ease; }
.cselect.open .cselect-caret { transform: rotate(180deg); }
.cselect-panel {
  position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 50;
  background: var(--c-surface); border: 1px solid var(--c-border); border-radius: var(--r-3);
  box-shadow: var(--sh-3); padding: 6px; max-height: 288px; overflow-y: auto; display: none;
}
.cselect.open .cselect-panel { display: block; animation: menu-in 0.16s cubic-bezier(0.2, 0.7, 0.3, 1) both; }
.cselect-option {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 9px 11px; border-radius: var(--r-2); font-size: var(--fs-base); color: var(--c-text); cursor: pointer;
}
.cselect-option:hover, .cselect-option.active { background: var(--c-surface-2); }
.cselect-option[aria-selected="true"] { color: var(--c-brand-tint); font-weight: 600; }
.cselect-option[aria-disabled="true"] { color: var(--c-text-3); cursor: default; }
.cselect-check { flex: 0 0 auto; color: var(--c-brand); line-height: 0; opacity: 0; }
.cselect-option[aria-selected="true"] .cselect-check { opacity: 1; }

/* ── Reduced motion ──────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

/* ── Loading overlay (partials/loading_overlay.php, shown by app.js) ──────
   Dim backdrop + spinner while a form submits or an internal link navigates
   (DPAC-backed pages, registration). The next page load clears it; a JS
   safety timeout makes sure it never sticks. */
.loading-overlay {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 9999;
    align-items: center;
    justify-content: center;
    background: rgba(15, 23, 32, 0.45);
}
.loading-overlay.is-active { display: flex; }
.loading-spinner {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    border: 4px solid rgba(255, 255, 255, 0.35);
    border-top-color: #ffffff;
    animation: loading-spin 0.8s linear infinite;
}
@keyframes loading-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .loading-spinner { animation-duration: 1.6s; }
}

/* ── Submit-button loading dots ──────────────────────────────────────── */
/* app.js adds them to [data-loading-label] buttons. */
.loading-dots { display: inline-flex; gap: 2px; }
.loading-dots span { animation: loading-dot 1.2s infinite ease-in-out both; }
.loading-dots span:nth-child(2) { animation-delay: 0.15s; }
.loading-dots span:nth-child(3) { animation-delay: 0.3s; }
@keyframes loading-dot { 0%, 70%, 100% { opacity: 0.25; } 35% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
    .loading-dots span { animation: none; opacity: 0.7; }
}

/* ── Utilities ─────────────────────────────────────────────────────────
   Last in the file so a utility class wins over the component it is used
   with (e.g. .card-title.fs-15). */
.alert.mb-0 { margin-bottom: 0; }
.tabs.mb-0 { margin-bottom: 0; }
.text-left { text-align: left; }
.text-brand { color: var(--c-brand); }
.text-success { color: var(--c-success); }
.d-block { display: block; }
.min-w-0 { min-width: 0; }
.flex-none { flex: none; }
.items-end { align-items: flex-end; }
.self-center { align-self: center; }
.mx-auto { margin-left: auto; margin-right: auto; }
.my-3 { margin-top: 12px; margin-bottom: 12px; }
.my-5 { margin-top: 20px; margin-bottom: 20px; }
.p-5 { padding: 20px; }
.p-6 { padding: 24px; }
.lh-1 { line-height: 1; }
.select-all { user-select: all; }
/* Font sizes from the type scale beyond .small / .tiny; .fs-36/.fs-40 are display figures. */
.fs-base { font-size: var(--fs-base); } .fs-lg { font-size: var(--fs-lg); }
.fs-xl { font-size: var(--fs-xl); } .fs-2xl { font-size: var(--fs-2xl); }
.fs-36 { font-size: 36px; } .fs-40 { font-size: 40px; }
/* Fixed widths for filter fields (admin lists, loan filter). */
.w-120 { width: 120px; } .w-150 { width: 150px; } .max-w-220 { max-width: 220px; }
.min-w-150 { min-width: 150px; } .min-w-160 { min-width: 160px; } .min-w-170 { min-width: 170px; }
.min-w-180 { min-width: 180px; } .min-w-200 { min-width: 200px; }
/* The hidden attribute always wins, even over .row / .col display rules. */
[hidden] { display: none !important; }
