/* ICBeheer — app-specific overrides on top of Tabler dark theme */

/* Font Awesome Pro — override CDN Free fonts with self-hosted Pro */
@font-face {
    font-family: 'Font Awesome 5 Free';
    src: url('../fonts/fa-solid-900.ttf') format('truetype');
    font-weight: 900;
    font-style: normal;
    font-display: block;
}
@font-face {
    font-family: 'Font Awesome 5 Free';
    src: url('../fonts/fa-regular-400.ttf') format('truetype');
    font-weight: 400;
    font-style: normal;
    font-display: block;
}
@font-face {
    font-family: 'Font Awesome 5 Brands';
    src: url('../fonts/fa-brands-400.ttf') format('truetype');
    font-weight: 400;
    font-style: normal;
    font-display: block;
}

/* ===========================================================
   Design tokens + colour schemes

   The palette is defined here; this file is the source of truth.
   (It previously mirrored AITools/FOBO.Admin by hand — that navy/cyan
   system was retired on 2026-09-12 for the near-black duotone below.)

   Two accents, and the split between them is the whole system:
     --c-accent   (cyan)  = anything you can touch. Links, buttons,
                            focus rings, active nav, toggles.
     --c-emphasis (pink)  = anything you should look at but cannot
                            touch. Active tab rule, selected row, the
                            headline figure on a stat card, hero wash.
   Never give pink to a control, never leave a control without cyan;
   that one rule is what keeps a two-accent UI legible.

   The --c-* values stay declared inside each scheme rather than on
   :root. The client portal is *also* [data-bs-theme="light"], so admin
   light tokens are scoped to .app-body or they would repaint it.
   =========================================================== */
/* --- The dot screen ---
   Pink is printed, not poured. Anywhere it covers an area rather than
   a line or a label, it prints as a fine dot grid the way a banknote's
   second colour is engraved — the cyan under it stays a smooth wash,
   so the two read as two press runs laid over one another.
   The geometry lives here so every screen in the app shares one pitch;
   the ink colour is written at each use site, because it has to resolve
   against the scheme the element is actually in. */
:root {
    --c-dot-pitch: 4px;        /* grid spacing */
    --c-dot-r: 0.95px;         /* dot radius — inks ~18% of the area */
    --c-dot-feather: 1.25px;   /* fully faded by here: one short step past
                                  the radius keeps the dot round instead of
                                  aliasing into a square */
}

[data-bs-theme="dark"] {
    --c-bg: #0d0e11;           /* page background */
    --c-surface: #15171c;      /* card surface */
    --c-surface-2: #1e2129;    /* elevated: table head, inputs, chips */
    --c-border: #23262e;       /* used sparingly; surfaces step instead */
    --c-border-strong: #2e323c;
    --c-text: #eceff3;
    --c-text-muted: #8e97a3;
    --c-text-dim: #737d8a;
    --c-accent: #04c8ff;       /* interactive */
    --c-accent-hover: #45d7ff;
    --c-accent-ink: #062230;   /* text on cyan fills */
    --c-accent-rgb: 4, 200, 255;
    --c-emphasis: #ff2e93;     /* emphasis, never interactive */
    --c-emphasis-hover: #ff5aa8;
    --c-emphasis-ink: #2b0417; /* text on pink fills */
    --c-emphasis-rgb: 255, 46, 147;
    --c-sheen: rgba(255, 255, 255, 0.028);
    --c-sheen-edge: rgba(255, 255, 255, 0.045);
    --c-shadow: rgba(0, 0, 0, 0.5);
    --c-shadow-ambient: rgba(0, 0, 0, 0.75);
    --c-header-tint: rgba(255, 255, 255, 0.022);
    --c-footer-tint: rgba(0, 0, 0, 0.16);
    --c-input-bg: #0f1115;
    --c-hero-tone: #131a26;    /* duotone base under hero photography */

    --tblr-success: #22e39a;
    --tblr-warning: #ffb020;
    --tblr-danger: #ff4d5e;

    /* Status hues feed .bg-green/.text-red/etc. Tabler's stock set was
       tuned for a grey ramp and goes muddy on near-black. */
    --tblr-green: #22e39a;
    --tblr-teal: #2ad4c4;
    --tblr-blue: #04c8ff;   /* blue is the brand cyan — see note below */
    --tblr-purple: #b07bff;
    --tblr-yellow: #ffb020;
    --tblr-orange: #ff8a3d;
    --tblr-red: #ff4d5e;
    --tblr-pink: #ff2e93;
    --tblr-cyan: #04c8ff;

    /* The hue at text strength. On paper these deepen; on near-black they
       lift, so a label on a tinted chip clears AA without the chip
       having to become a slab. */
    --c-ink-green: #5cf0b8;
    --c-ink-teal: #6ae4d8;
    --c-ink-blue: #57dcff;
    --c-ink-purple: #c9a5ff;
    --c-ink-yellow: #ffc75c;
    --c-ink-orange: #ffa972;
    --c-ink-red: #ff8a94;
    --c-ink-pink: #ff6fb4;
    --c-ink-cyan: #57dcff;

    --tblr-green-rgb: 34, 227, 154;
    --tblr-teal-rgb: 42, 212, 196;
    --tblr-blue-rgb: 4, 200, 255;
    --tblr-purple-rgb: 176, 123, 255;
    --tblr-yellow-rgb: 255, 176, 32;
    --tblr-orange-rgb: 255, 138, 61;
    --tblr-red-rgb: 255, 77, 94;
    --tblr-pink-rgb: 255, 46, 147;
    --tblr-cyan-rgb: 4, 200, 255;
}

/* Light scheme. A warm paper white rather than a cool grey, in the same
   family as the client portal so the two never look like different
   products. Both accents are too bright to sit on paper — cyan #04c8ff
   is 1.9:1 and pink #ff2e93 is 3.0:1 — so each deepens: #0a6f8c
   measures 5.7:1 and #be1263 measures 6.1:1, white-on-fill likewise. */
[data-bs-theme="light"] .app-body {
    --c-bg: #f4f2ee;
    --c-surface: #ffffff;
    --c-surface-2: #eceae4;
    --c-border: #e4e1d9;
    --c-border-strong: #d2cec3;
    --c-text: #14161a;
    --c-text-muted: #5c6570;
    --c-text-dim: #78818d;
    --c-accent: #0a6f8c;
    --c-accent-hover: #075f78;
    --c-accent-ink: #ffffff;
    --c-accent-rgb: 10, 111, 140;
    --c-emphasis: #be1263;
    --c-emphasis-hover: #a10f54;
    --c-emphasis-ink: #ffffff;
    --c-emphasis-rgb: 190, 18, 99;
    --c-sheen: rgba(255, 255, 255, 0.9);
    --c-sheen-edge: rgba(255, 255, 255, 0.9);
    --c-shadow: rgba(43, 37, 26, 0.06);
    --c-shadow-ambient: rgba(43, 37, 26, 0.16);
    --c-header-tint: #faf8f4;
    --c-footer-tint: #f7f5f0;
    --c-input-bg: #ffffff;
    --c-hero-tone: #1b2433;

    --tblr-danger: #be123c;
    --tblr-success: #047857;
    --tblr-warning: #b45309;

    /* Each hue has TWO jobs and they pull in opposite directions on white:
       a badge or chart FILL wants to be bright, while the same hue used as
       TEXT on white has to be dark to clear 4.5:1. So --tblr-* is the fill
       and --c-ink-* is the darkened partner used wherever it becomes text.

       Blue is an alias of cyan, not a hue of its own. A status blue that
       sits a few degrees off the brand accent reads as a clash rather
       than a distinction, so the two collapsed into one — which leaves
       the palette eight hues wide, and statuses that used .bg-blue come
       out on-brand without touching the views.

       The fills split again by luminosity, because the label sitting *on*
       them cannot be one colour for all nine:
         green/teal/yellow/orange/cyan  bright, near-black label
         blue/purple/red/pink           deeper, white label
       The second group is pulled down just far enough that white clears
       AA on it — white on a vivid red is only 3.6:1, which is why those
       four cannot simply keep the bright fill. */
    --tblr-green: #34d399;
    --tblr-green-rgb: 52, 211, 153;
    --c-ink-green: #0b7a53;
    --tblr-teal: #2dd4bf;
    --tblr-teal-rgb: 45, 212, 191;
    --c-ink-teal: #0c6b63;
    --tblr-yellow: #f5a524;
    --tblr-yellow-rgb: 245, 165, 36;
    --c-ink-yellow: #8a5a00;
    --tblr-orange: #f97316;
    --tblr-orange-rgb: 249, 115, 22;
    --c-ink-orange: #a8450f;
    --tblr-cyan: #06b6d4;
    --tblr-cyan-rgb: 6, 182, 212;
    --c-ink-cyan: #0a6f8c;
    --tblr-blue: #06b6d4;   /* blue is the brand cyan — see note below */
    --tblr-blue-rgb: 6, 182, 212;
    --c-ink-blue: #0a6f8c;
    --tblr-purple: #7c3aed;
    --tblr-purple-rgb: 124, 58, 237;
    --c-ink-purple: #6b21a8;
    --tblr-red: #d0263c;
    --tblr-red-rgb: 208, 38, 60;
    --c-ink-red: #a81228;
    --tblr-pink: #d81b76;
    --tblr-pink-rgb: 216, 27, 118;
    --c-ink-pink: #a10f54;
}

/* Geometry. Not colour, so it is shared by every skin including the
   portal — which is light-themed but deliberately outside .app-body. */
.app-body,
.portal-body {
    /* The chrome colour: the surface the sidebar rail and the top bar
       share. It is the scheme's own surface in every skin — named
       separately because the hero's backing plate has to sit flush
       against the chrome specifically, not against whatever a card
       happens to be. */
    --c-chrome: var(--c-surface);

    --c-radius-sm: 10px;
    --c-radius: 16px;
    --c-radius-lg: 22px;

    --tblr-border-radius: 12px;
    --tblr-border-radius-sm: 10px;
    --tblr-border-radius-lg: 16px;
    --tblr-border-radius-xl: 20px;
    --tblr-border-radius-2xl: 24px;
}

/* Shared mapping: the scheme blocks above feed the same Tabler variables. */
[data-bs-theme="dark"],
[data-bs-theme="light"] .app-body {
    --tblr-body-bg: var(--c-bg);
    --tblr-body-color: var(--c-text);
    --tblr-bg-surface: var(--c-surface);
    --tblr-bg-surface-secondary: var(--c-bg);
    --tblr-bg-surface-tertiary: var(--c-surface-2);
    --tblr-border-color: var(--c-border);
    --tblr-border-color-translucent: var(--c-border);
    --tblr-secondary: var(--c-text-muted);
    --tblr-muted: var(--c-text-muted);
    --tblr-primary: var(--c-accent);
    --tblr-primary-rgb: var(--c-accent-rgb);
    --tblr-link-color: var(--c-accent);
    --tblr-link-hover-color: var(--c-accent-hover);
}

/* Accent fills take the scheme's ink: near-black under the neon pair on
   dark, white under the deepened pair on paper. */
.app-body .btn-primary {
    --tblr-btn-color: var(--c-accent-ink);
    --tblr-btn-bg: var(--c-accent);
    --tblr-btn-border-color: var(--c-accent);
    --tblr-btn-hover-color: var(--c-accent-ink);
    --tblr-btn-hover-bg: var(--c-accent-hover);
    --tblr-btn-hover-border-color: var(--c-accent-hover);
    --tblr-btn-active-color: var(--c-accent-ink);
    --tblr-btn-active-bg: var(--c-accent-hover);
    --tblr-btn-active-border-color: var(--c-accent-hover);
    --tblr-btn-disabled-color: var(--c-accent-ink);
    --tblr-btn-disabled-bg: var(--c-accent);
    --tblr-btn-disabled-border-color: var(--c-accent);
    font-weight: 700;
}

/* --- Modal: darken backdrop instead of blur --- */
.modal-backdrop {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
}
.modal-backdrop.show {
    background-color: rgba(0, 0, 0, 0.7) !important;
    opacity: 1 !important;
}

/* --- Typography ---
   Manrope is the interface face: a geometric grotesk that holds up at
   800 where the big figures live, and stays legible at 13px in a dense
   table. Small labels are set in sentence case at weight rather than
   tracked-out caps — caps labels read as template chrome and cost
   legibility at this size for nothing. */
:root {
    --tblr-font-sans-serif: 'Manrope', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --tblr-body-font-family: var(--tblr-font-sans-serif);
}

body {
    font-family: var(--tblr-font-sans-serif);
    font-size: 0.875rem;
    letter-spacing: 0;
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
    letter-spacing: -0.02em;
    font-weight: 700;
}

.page-header {
    padding: 1.5rem 0;
}

.page-title {
    font-size: clamp(1.35rem, 1.1rem + 0.9vw, 1.75rem);
    font-weight: 800;
    letter-spacing: -0.03em;
    line-height: 1.1;
}

.page-pretitle {
    font-size: 0.8125rem;
    font-weight: 600;
    text-transform: none;
    letter-spacing: 0;
    color: var(--tblr-secondary);
}

/* Tabler sets datagrid labels in tracked-out caps. Same call as the
   table heads and stat-card labels: sentence case at weight reads
   faster at 13px and stops the field labels shouting over their own
   values. */
.app-body .datagrid-title,
.portal-body .datagrid-title {
    font-size: 0.8125rem;
    text-transform: none;
    letter-spacing: 0;
    font-weight: 600;
    color: var(--tblr-secondary);
}

/* Pill buttons need the icon and label held apart, and noticeably more
   horizontal padding than Tabler's rectangle default — a capsule with
   square-button padding reads as pinched, because the rounded ends eat
   into the space the text needs. */
.app-body .btn:not(.btn-icon),
.portal-body .btn:not(.btn-icon) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5em;
    padding: 0.5rem 1.25rem;
}

.app-body .btn.btn-sm:not(.btn-icon),
.portal-body .btn.btn-sm:not(.btn-icon) {
    padding: 0.4rem 1rem;
}

.app-body .btn.btn-lg:not(.btn-icon),
.portal-body .btn.btn-lg:not(.btn-icon) {
    padding: 0.65rem 1.75rem;
}
.app-body .btn:not(.btn-icon) > i,
.portal-body .btn:not(.btn-icon) > i {
    margin: 0;
}

/* --- Stat cards ---
   The figure is the content; the label is a caption for it. Size and
   weight carry that relationship, so the figure goes genuinely large
   and tabular (columns of euros have to align) and the label stays
   small and quiet. */
.card .subheader {
    font-size: 0.8125rem;
    text-transform: none;
    letter-spacing: 0;
    color: var(--tblr-secondary);
    font-weight: 600;
}

.app-body .card-body > .h1,
.portal-body .card-body > .h1 {
    font-size: clamp(1.75rem, 1.3rem + 1.6vw, 2.5rem);
    font-weight: 800;
    letter-spacing: -0.035em;
    line-height: 1.02;
    font-variant-numeric: tabular-nums;
}

/* --- Tables: legibility --- */
/* Two classes deep on purpose: Tabler's own `.table thead th` sets the
   uppercase, and a bare `.table th` loses to it on specificity. */
.app-body .table th,
.portal-body .table th {
    font-size: 0.8125rem;
    text-transform: none;
    letter-spacing: 0;
    font-weight: 600;
    color: var(--tblr-secondary);
    white-space: nowrap;
}

.table td {
    vertical-align: middle;
    padding: 0.5rem 0.75rem;
}

.table-vcenter td,
.table-vcenter th {
    vertical-align: middle;
}

/* --- Clickable table rows --- */
.table-clickable tbody tr {
    cursor: pointer;
}
.table-clickable tbody tr:hover {
    background-color: rgba(var(--c-accent-rgb), 0.08);
}

/* --- Dossier tab bar: solid strip between hero and content --- */
.dossier-tabbar {
    background-color: var(--tblr-bg-surface);
    border-bottom: 0;
}

.dossier-tabbar + .page-body {
    margin-top: 1rem;
}

.dossier-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    border-bottom: 0;
    /* Pull back by the full container gutter (1rem), not the tab's own
       0.75rem padding. The old -0.75rem left a 4px residue, so the first
       tab's highlight started 4px clear of the sidebar's right edge
       instead of meeting it. The first tab takes the extra 0.25rem as
       padding so its *label* still lands on the content gutter. */
    margin-left: -1rem;
    margin-right: -1rem;
}

.dossier-tabs .nav-item:first-child .nav-link {
    padding-left: 1rem;
}
.dossier-tabs::-webkit-scrollbar {
    height: 0;
}

.dossier-tabs .nav-item {
    flex: 0 0 auto;
}

.dossier-tabs .nav-link {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.75rem;
    font-size: 0.8125rem;
    font-weight: 500;
    line-height: 1.2;
    color: var(--c-text-muted);
    background-color: transparent;
    border: 0;
    border-bottom: 2px solid transparent;
    border-radius: 0;
    white-space: nowrap;
    transition: color 0.15s, border-color 0.15s, background-color 0.15s;
}
.dossier-tabs .nav-link i {
    font-size: 0.875em;
    opacity: 0.75;
}
.dossier-tabs .nav-link:hover,
.dossier-tabs .nav-link:focus-visible {
    color: var(--c-text);
    background-color: rgba(var(--c-accent-rgb), 0.08);
    border-bottom-color: var(--c-border-strong);
}
/* Pink marks the tab you are on. It is the one place in the chrome
   where "where am I" matters more than "what can I click", so it gets
   the emphasis accent while every other control keeps cyan. */
.dossier-tabs .nav-link.active {
    color: var(--c-text);
    font-weight: 700;
    /* The same gradient fill the status pills take, in pink. The tab is a
       small solid shape rather than an open field, so the dot screen
       reads as noise on it — screening is for the large pink areas (the
       rail, the hero corner, the logo tile). */
    background-image: linear-gradient(180deg,
        rgba(var(--c-emphasis-rgb), 0.24) 0%,
        rgba(var(--c-emphasis-rgb), 0.04) 100%);
    border-bottom-color: var(--c-emphasis);
}
.dossier-tabs .nav-link.active i {
    color: var(--c-emphasis);
    opacity: 1;
}

/* --- Cards: consistent spacing --- */
.card-header {
    display: flex;
    align-items: center;
    min-height: 3rem;
    flex-wrap: wrap;
    gap: 0.5rem;
}

.card-header .card-title {
    margin-bottom: 0;
}

.card-header .card-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-left: auto;
    flex-wrap: wrap;
    justify-content: flex-end;
}

/* Search box + action button stay on one line */
.card-header .form-control,
.card-header .form-select {
    width: auto;
    flex: 0 1 300px;
    min-width: 0;
}

.card-header .btn,
.card-header .btn-group {
    flex: 0 0 auto;
    white-space: nowrap;
}

/* Inline filter/search forms inside a card header */
.card-header form {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    min-width: 0;
    margin-bottom: 0;
}

/* --- Form labels: required marker --- */
.form-label.required::after {
    content: " *";
    color: var(--tblr-danger);
}

/* --- Form inputs: focus ring for tabbing --- */
.form-control:focus,
.form-select:focus {
    border-color: var(--c-accent);
    box-shadow: 0 0 0 3px rgba(var(--c-accent-rgb), 0.28);
}

/* Keyboard focus is visible on every control, not just fields. */
.app-body :focus-visible,
.portal-body :focus-visible {
    outline: 2px solid var(--c-accent);
    outline-offset: 2px;
}

/* --- Modal: title icon (rounded square) --- */
.modal-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 10px;
    background-color: rgba(var(--c-accent-rgb), 0.12);
    color: var(--c-accent);
    margin-right: 0.5rem;
    font-size: 0.875rem;
    flex-shrink: 0;
}

/* --- Modal: form layout --- */
.modal .form-label {
    font-size: 0.8125rem;
    font-weight: 500;
    margin-bottom: 0.25rem;
}

.modal .modal-body .mb-3:last-child {
    margin-bottom: 0 !important;
}

.modal-footer {
    border-top-color: var(--tblr-border-color);
}

/* --- Badge enhancements --- */
.badge {
    font-weight: 600;
    letter-spacing: 0;
    border-radius: 999px;
    padding: 0.3em 0.7em;
}

[data-bs-theme="dark"] .avatar.bg-secondary {
    color: #0d0e11 !important;
}
/* --- Status pills ---
   A status is stamped, not painted: a solid rule in the hue holds the
   shape, the fill is a soft gradient of the same hue, and the label is
   the hue at text strength. Nine statuses stay tellable apart without
   nine slabs of colour shouting over the table they sit in.
   The -lt variants map to the same hue: a pill is already a light
   chip, so Tabler's tinted badge and its solid one are now one thing,
   and a badge that only carried .bg-cyan-lt would otherwise fall
   through to the grey default.
   One rule does the work; each hue only sets --pill-rgb and --pill-ink,
   and --c-ink-* already means "this hue as text in this scheme", so the
   pill reads correctly on paper and on near-black without a second set
   of values. */
:is(.app-body, .portal-body) :is(.badge, .action-status-select) {
    --pill-rgb: 142, 151, 163;
    --pill-ink: var(--c-text);
    background-color: transparent !important;
    background-image: linear-gradient(180deg,
        rgba(var(--pill-rgb), 0.24) 0%,
        rgba(var(--pill-rgb), 0.04) 100%);
    border: 1.5px solid rgb(var(--pill-rgb));
    color: var(--pill-ink) !important;
}
:is(.app-body, .portal-body) :is(.badge, .action-status-select):is(.bg-green, .bg-green-lt) { --pill-rgb: var(--tblr-green-rgb);   --pill-ink: var(--c-ink-green); }
:is(.app-body, .portal-body) :is(.badge, .action-status-select):is(.bg-teal, .bg-teal-lt) { --pill-rgb: var(--tblr-teal-rgb);    --pill-ink: var(--c-ink-teal); }
:is(.app-body, .portal-body) :is(.badge, .action-status-select):is(.bg-blue, .bg-blue-lt) { --pill-rgb: var(--tblr-blue-rgb);    --pill-ink: var(--c-ink-blue); }
:is(.app-body, .portal-body) :is(.badge, .action-status-select):is(.bg-purple, .bg-purple-lt) { --pill-rgb: var(--tblr-purple-rgb);  --pill-ink: var(--c-ink-purple); }
:is(.app-body, .portal-body) :is(.badge, .action-status-select):is(.bg-yellow, .bg-yellow-lt) { --pill-rgb: var(--tblr-yellow-rgb);  --pill-ink: var(--c-ink-yellow); }
:is(.app-body, .portal-body) :is(.badge, .action-status-select):is(.bg-orange, .bg-orange-lt) { --pill-rgb: var(--tblr-orange-rgb);  --pill-ink: var(--c-ink-orange); }
:is(.app-body, .portal-body) :is(.badge, .action-status-select):is(.bg-red, .bg-red-lt) { --pill-rgb: var(--tblr-red-rgb);     --pill-ink: var(--c-ink-red); }
:is(.app-body, .portal-body) :is(.badge, .action-status-select):is(.bg-pink, .bg-pink-lt) { --pill-rgb: var(--tblr-pink-rgb);    --pill-ink: var(--c-ink-pink); }
:is(.app-body, .portal-body) :is(.badge, .action-status-select):is(.bg-cyan, .bg-cyan-lt) { --pill-rgb: var(--tblr-cyan-rgb);    --pill-ink: var(--c-ink-cyan); }
:is(.app-body, .portal-body) :is(.badge, .action-status-select):is(.bg-secondary, .bg-secondary-lt) { --pill-rgb: 142, 151, 163; --pill-ink: var(--c-text-muted); }
[data-bs-theme="dark"] .badge.bg-secondary { --pill-ink: #b6bec9; }

/* --- Search inputs in card headers --- */
.card-header input[type="text"],
.card-header .form-control-sm {
    max-width: 280px;
    font-size: 0.8125rem;
}

/* --- Hues used as text on paper ---
   A bright fill is unreadable as text on white, so every .text-* utility
   and every .bg-*-lt tint (whose label takes the hue as its colour) swaps
   to the darkened partner. Fills keep the bright value. */
[data-bs-theme="light"] .app-body .text-green,
[data-bs-theme="light"] .app-body .bg-green-lt,
.portal-body .text-green,
.portal-body .bg-green-lt {
    color: var(--c-ink-green) !important;
}

[data-bs-theme="light"] .app-body .text-teal,
[data-bs-theme="light"] .app-body .bg-teal-lt,
.portal-body .text-teal,
.portal-body .bg-teal-lt {
    color: var(--c-ink-teal) !important;
}

[data-bs-theme="light"] .app-body .text-blue,
[data-bs-theme="light"] .app-body .bg-blue-lt,
.portal-body .text-blue,
.portal-body .bg-blue-lt {
    color: var(--c-ink-blue) !important;
}

[data-bs-theme="light"] .app-body .text-purple,
[data-bs-theme="light"] .app-body .bg-purple-lt,
.portal-body .text-purple,
.portal-body .bg-purple-lt {
    color: var(--c-ink-purple) !important;
}

[data-bs-theme="light"] .app-body .text-yellow,
[data-bs-theme="light"] .app-body .bg-yellow-lt,
.portal-body .text-yellow,
.portal-body .bg-yellow-lt {
    color: var(--c-ink-yellow) !important;
}

[data-bs-theme="light"] .app-body .text-orange,
[data-bs-theme="light"] .app-body .bg-orange-lt,
.portal-body .text-orange,
.portal-body .bg-orange-lt {
    color: var(--c-ink-orange) !important;
}

[data-bs-theme="light"] .app-body .text-red,
[data-bs-theme="light"] .app-body .bg-red-lt,
.portal-body .text-red,
.portal-body .bg-red-lt {
    color: var(--c-ink-red) !important;
}

[data-bs-theme="light"] .app-body .text-pink,
[data-bs-theme="light"] .app-body .bg-pink-lt,
.portal-body .text-pink,
.portal-body .bg-pink-lt {
    color: var(--c-ink-pink) !important;
}

[data-bs-theme="light"] .app-body .text-cyan,
[data-bs-theme="light"] .app-body .bg-cyan-lt,
.portal-body .text-cyan,
.portal-body .bg-cyan-lt {
    color: var(--c-ink-cyan) !important;
}

/* --- Currency amounts: tabular numbers --- */
/* `.amount` carries the same treatment to euros that are not colour-coded:
   a column of plain amounts has to align just as much as a green one. */
.text-green, .text-red, .text-orange, .text-teal, .amount {
    font-variant-numeric: tabular-nums;
}

/* --- Avatar initials --- */
.avatar {
    font-weight: 600;
}

/* --- Sidebar: no seam against the header bar ---
   Tabler draws the sidebar's right edge as `inset -1px 0 0 0` in
   --tblr-navbar-border-color. Both the sidebar and the top header bar
   take their background from --tblr-bg-surface, so in the dark scheme
   the two are the same navy and that 1px is the only thing between
   them: a hairline floating in one continuous plane. Drop it — the
   sidebar still reads as its own surface against the darker page body
   below (#162030 on #0e1520), and against the paper page in light
   mode the contrast is larger still. Both skins, admin and portal: the
   portal spent a while with the hairline still on, which is what made its
   rail read as a panel bolted beside the page rather than part of it. */
.app-body .navbar.navbar-vertical,
.portal-body .navbar.navbar-vertical {
    box-shadow: none;
}

/* --- Top bar matches the menu rail ---
   The bar carries the same surface as the sidebar so the two read as one
   piece of chrome wrapping the page — in the portal too, which was left
   on Tabler's default bar and so carried a border under it. */
.app-body .page-wrapper > .navbar,
.portal-body .page-wrapper > .navbar {
    background-color: var(--c-chrome);
    border-bottom: 0;
    box-shadow: none;
}

/* --- Sidebar: the rail ends in the brand gradient ---
   Cyan into pink across the foot of the menu, masked so it fades up out
   of the surface rather than sitting there as a block. It bookends the
   gradient logo tile at the top of the same rail.
   The wash is absolutely positioned, so it would otherwise paint over
   the menu items; z-index 0 on it and 1 on the content keeps the links
   on top, and pointer-events: none keeps them clickable.
   Do NOT set position: relative on the rail — Tabler lays it out as
   position: fixed, and overriding that collapses the sidebar to content
   height and drops the page-wrapper's offset. Fixed is already a
   containing block, so the wash anchors correctly as-is. */
.app-body .navbar-vertical::after,
.portal-body .navbar-vertical::after {
    content: '';
    position: absolute;
    inset: auto 0 0 0;
    z-index: 0;
    height: 320px;
    max-height: 50%;
    pointer-events: none;
    /* Cyan pours, pink prints — see the dot screen note at the top. The
       element mask below fades both together, which is what still leaves
       the pink edge sitting higher up the rail than the cyan one. */
    background-image:
        radial-gradient(circle at 50% 50%,
            rgba(var(--c-emphasis-rgb), 0.9) 0 var(--c-dot-r),
            rgba(var(--c-emphasis-rgb), 0) var(--c-dot-feather)),
        linear-gradient(100deg,
            rgba(var(--c-accent-rgb), 0.55) 0%,
            rgba(var(--c-accent-rgb), 0) 68%);
    background-size: var(--c-dot-pitch) var(--c-dot-pitch), 100% 100%;
    /* The mask is tilted rather than vertical (160deg, not 180deg), so the
       fade line slopes up to the right: the pink edge reaches higher up
       the rail than the cyan one. */
    -webkit-mask-image: linear-gradient(160deg, transparent 22%, #000 100%);
    mask-image: linear-gradient(160deg, transparent 22%, #000 100%);
}

.app-body .navbar-vertical > .container-fluid,
.portal-body .navbar-vertical > .container-fluid {
    position: relative;
    z-index: 1;
}

/* --- Sidebar: menu items sit as inset pills ---
   The nav-link used to span the rail edge to edge (0 -> 240), so the
   active fill touched both sides. It gains an 8px margin and gives back
   the same 8px of padding, which insets the pill while leaving the icon
   at x=16 — exactly where the brand's 16px padding puts the logo. That
   alignment is deliberate; changing only the margin would break it. */
.app-body .navbar-vertical.navbar-expand-lg .navbar-nav .nav-link,
.app-body .navbar-vertical .nav-link,
.portal-body .navbar-vertical.navbar-expand-lg .navbar-nav .nav-link,
.portal-body .navbar-vertical .nav-link {
    margin: 0.125rem 0.5rem;
    padding-left: 0.5rem;
    padding-right: 0.5rem;
    border-radius: 12px;
}

.app-body .navbar-vertical .navbar-nav,
.portal-body .navbar-vertical .navbar-nav {
    padding-bottom: 0.5rem;
}

/* The submenu renders inline in the rail, not as a floating card, so it
   sheds the dropdown surface treatment and takes the same 0.5rem inset
   as the parent pills — otherwise the children sit wider than the
   parent they belong to. */
.app-body .navbar-vertical.navbar-expand-lg .dropdown-menu,
.app-body .navbar-vertical .dropdown-menu {
    background-color: transparent;
    background-image: none;
    box-shadow: none;
    border: 0;
    border-radius: 0;
    padding: 0 0.5rem 0.25rem;
}

.app-body .navbar-vertical .dropdown-item {
    border-radius: 10px;
}

/* --- Sidebar: active state ---
   Cyan, because the nav item is a control. The inset bar marks position
   without adding margin to the link, which would pull the menu icons
   out of alignment with the brand above them. */
.app-body .navbar-vertical .nav-link.active,
.portal-body .navbar-vertical .nav-link.active {
    color: var(--c-accent);
    font-weight: 700;
    background-color: rgba(var(--c-accent-rgb), 0.10);
    box-shadow: inset 3px 0 0 var(--c-accent);
}
.app-body .navbar-vertical .nav-link.active .nav-link-icon,
.portal-body .navbar-vertical .nav-link.active .nav-link-icon {
    color: var(--c-accent);
}

/* --- Sidebar: logo alignment with menu items ---
   Tabler's `.navbar-vertical.navbar-expand-lg .navbar-brand` (0,3,0) centres the
   brand with zero side padding and outranked the previous (0,2,0) override, so
   the logo never picked up the page padding. Match that specificity — this file
   loads after tabler.min.css, so an equal-specificity rule wins on order — and
   reuse the nav-link's exact padding expression so the logo and the menu icons
   resolve to one shared left inset. Vertical padding is left to Tabler. */
.navbar.navbar-vertical .navbar-brand {
    justify-content: flex-start;
    padding-left: calc(calc(var(--tblr-page-padding) * 2) / 2);
    padding-right: calc(calc(var(--tblr-page-padding) * 2) / 2);
    margin: 0;
    width: 100%;
}

/* --- Brand logo: hold its size at every breakpoint ---
   logo.svg carries a viewBox but no width/height, so it has a ratio and no
   intrinsic size, and Tabler's `img { max-width: 100%; height: auto }` beats
   the markup's `height="36"` attribute (a CSS declaration always outranks a
   presentational attribute). Left to itself the image then resolves against
   the available width — which is the brand box above, and below `lg` the
   sidebar is no longer a fixed 15rem column but a full-width top bar, so the
   logo inflated to the whole viewport. Pin the height the markup asked for
   and let the ratio drive the width, with max-width as the floor for very
   narrow screens. Tabler solves this for its own markup with
   `.navbar-brand-image`; this applies the same shape to ours. */
/* The mark is capped by the rail's width, not this height — the viewBox
   is trimmed to the wordmark so the cap lands as late as possible. */
.navbar-brand img {
    height: 3.75rem;
    width: auto;
    max-width: 100%;
}

/* --- Sidebar on mobile: logo and hamburger share one row ---
   Tabler lays the sidebar's container out as `flex-direction: column`
   unconditionally, not just inside the desktop media query, so below `lg` —
   where the sidebar is a full-width top bar rather than a fixed column — the
   toggler and the brand stack on two rows. Put the container back to a row
   for that range only: brand first (order -1, ahead of its source position),
   toggler pushed to the far edge, and the collapsing menu still wrapping to
   its own row underneath on its flex-basis: 100%. The toggler's right margin
   reuses the brand's left-padding expression so both ends sit on the same
   inset. */
@media (max-width: 991.98px) {
    /* Collapsed to a top bar, the rail has no foot to end in — drop the
       gradient so it does not bleed across the hamburger row. */
    .app-body .navbar-vertical::after,
    .portal-body .navbar-vertical::after {
        display: none;
    }

    .app-body .navbar.navbar-vertical > .container-fluid,
    .portal-body .navbar.navbar-vertical > .container-fluid {
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
    }

    .app-body .navbar.navbar-vertical .navbar-brand,
    .portal-body .navbar.navbar-vertical .navbar-brand {
        order: -1;
        width: auto;
    }

    .app-body .navbar.navbar-vertical .navbar-toggler,
    .portal-body .navbar.navbar-vertical .navbar-toggler {
        margin-left: auto;
        margin-right: calc(calc(var(--tblr-page-padding) * 2) / 2);
    }
}

/* --- Lookup input component --- */
.lookup-wrap {
    position: relative;
}
.lookup-wrap .lookup-display {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.4375rem 0.75rem;
    background: var(--tblr-bg-surface);
    border: 1px solid var(--tblr-border-color);
    border-radius: var(--tblr-border-radius);
    min-height: calc(1.4285714286em + 0.875rem + 2px);
    cursor: pointer;
    transition: border-color 0.15s;
}
.lookup-wrap .lookup-display:hover {
    border-color: var(--tblr-primary);
}
.lookup-wrap .lookup-display .lookup-text {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.lookup-wrap .lookup-display .lookup-placeholder {
    color: var(--tblr-secondary);
}
.lookup-wrap .lookup-display .lookup-clear {
    flex-shrink: 0;
    cursor: pointer;
    color: var(--tblr-secondary);
    padding: 0 0.25rem;
}
.lookup-wrap .lookup-display .lookup-clear:hover {
    color: var(--tblr-danger);
}
.lookup-results {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 1060;
    background: var(--tblr-bg-surface);
    border: 1px solid var(--tblr-border-color);
    border-top: 0;
    border-radius: 0 0 var(--tblr-border-radius) var(--tblr-border-radius);
    max-height: 240px;
    overflow-y: auto;
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
.lookup-results .lookup-item {
    padding: 0.5rem 0.75rem;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    transition: background-color 0.1s;
}
.lookup-results .lookup-item:hover,
.lookup-results .lookup-item.active {
    background-color: rgba(32, 107, 196, 0.15);
}
.lookup-results .lookup-item .lookup-item-sub {
    font-size: 0.75rem;
    color: var(--tblr-secondary);
}
.lookup-results .lookup-empty {
    padding: 0.75rem;
    text-align: center;
    color: var(--tblr-secondary);
    font-size: 0.8125rem;
}
.lookup-results .lookup-quick-add-trigger {
    border-top: 1px solid var(--tblr-border-color);
    color: var(--c-accent);
    font-weight: 500;
    font-size: 0.8125rem;
}
.lookup-results .lookup-quick-add-trigger:hover {
    color: var(--c-accent-hover);
}
.lookup-quick-add {
    background: var(--c-surface);
    border: 1px solid var(--c-border-strong);
    border-radius: var(--tblr-border-radius);
    padding: 0.75rem;
    margin-top: 0.25rem;
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
}
.lookup-quick-add-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 0.5rem;
    font-size: 0.8125rem;
}
.lookup-quick-add-close {
    background: none;
    border: none;
    color: var(--c-text-muted);
    font-size: 1.25rem;
    cursor: pointer;
    padding: 0 0.25rem;
    line-height: 1;
}
.lookup-quick-add-close:hover {
    color: var(--tblr-danger);
}
.lookup-quick-add-body .form-select-sm,
.lookup-quick-add-body .form-control-sm {
    font-size: 0.8125rem;
}

/* ===========================
   RESPONSIVE — Mobile first
   =========================== */

/* Mobile: full-width modals */
@media (max-width: 575.98px) {
    .modal-dialog {
        margin: 0.5rem;
        max-width: calc(100% - 1rem);
    }
    .modal-lg {
        max-width: calc(100% - 1rem);
    }
}

/* Small screens */
@media (max-width: 768px) {
    .row-deck > [class*="col-"] {
        margin-bottom: 0.75rem;
    }
    .dossier-tabs {
        margin-left: -0.625rem;
        margin-right: -0.625rem;
    }
    .dossier-tabs .nav-link {
        padding: 0.625rem;
        font-size: 0.75rem;
    }

    /* Card headers: stack title and actions */
    .card-header {
        flex-direction: column;
        align-items: flex-start;
        gap: 0.5rem;
    }
    .card-header .card-actions {
        margin-left: 0;
        width: 100%;
    }
    .card-header .form-control,
    .card-header .form-select,
    .card-header input[type="text"],
    .card-header .form-control-sm {
        max-width: none;
        flex: 1 1 auto;
    }

    /* Page header: tighter spacing */
    .page-header {
        padding: 0.75rem 0;
    }
    .page-header .page-title {
        font-size: 1.25rem;
    }

    /* Stat cards: smaller numbers on mobile */
    .card-body .h1 {
        font-size: 1.5rem;
    }

    /* Hide less important columns on small screens */
    .table .d-none-mobile {
        display: none !important;
    }

    /* Tighter table cells */
    .table td {
        padding: 0.375rem 0.5rem;
        font-size: 0.8125rem;
    }
    .table th {
        padding: 0.375rem 0.5rem;
        font-size: 0.6875rem;
    }

    /* Action buttons: smaller on mobile */
    .btn-icon.btn-sm {
        width: 1.5rem;
        height: 1.5rem;
    }

    /* Datagrid: full width items */
    .datagrid {
        grid-template-columns: 1fr !important;
    }

    /* Header: show search on mobile */
    .navbar.d-none.d-lg-flex {
        display: none !important;
    }

    /* Contact cards: tighter */
    .col-lg-6 .border.rounded {
        padding: 0.75rem !important;
    }
}

/* Medium screens: tables stay readable */
@media (min-width: 769px) and (max-width: 1024px) {
    .table td {
        font-size: 0.8125rem;
    }
    .card-header input[type="text"] {
        max-width: 200px;
    }
}

/* Large screens: optimal spacing */
@media (min-width: 1200px) {
    .page-body .container-xl {
        max-width: 1320px;
    }
}

/* Touch-friendly: larger tap targets on touch devices */
@media (hover: none) and (pointer: coarse) {
    .btn-icon.btn-sm {
        width: 2.25rem;
        height: 2.25rem;
    }
    .table td {
        padding: 0.625rem 0.75rem;
    }
    .nav-link {
        padding: 0.75rem 1rem;
    }
    .dropdown-item {
        padding: 0.625rem 1rem;
    }
}

/* --- Inline action status dropdown --- */
/* The status you can change takes the same stamped pill as the ones you
   cannot — the caret is what says it is a control. It is drawn from two
   half-square gradients rather than an inline SVG, because a data URI
   cannot read currentColor and the caret has to follow the hue's ink. */
.action-status-select {
    font-size: 0.75rem;
    font-weight: 600;
    padding: 0.15rem 1.75rem 0.15rem 0.7rem;
    border-radius: 999px;
    cursor: pointer;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
}
:is(.app-body, .portal-body) .action-status-select {
    background-image:
        linear-gradient(45deg, transparent 50%, currentColor 50%),
        linear-gradient(135deg, currentColor 50%, transparent 50%),
        linear-gradient(180deg,
            rgba(var(--pill-rgb), 0.24) 0%,
            rgba(var(--pill-rgb), 0.04) 100%);
    background-repeat: no-repeat, no-repeat, repeat;
    background-position: right 0.85rem center, right 0.55rem center, 0 0;
    background-size: 5px 5px, 5px 5px, 100% 100%;
}
.action-status-select option { background-color: var(--c-surface-2); color: var(--c-text); }

/* --- Theme picker (Beheer > Profiel) --- */
.theme-option {
    display: block;
    cursor: pointer;
    border: 1px solid var(--c-border-strong);
    border-radius: var(--tblr-border-radius);
    padding: 0.75rem;
    margin: 0;
    transition: border-color 0.15s, background-color 0.15s;
}
.theme-option:hover {
    border-color: var(--c-accent);
}
.theme-option.is-active {
    border-color: var(--c-accent);
    background-color: rgba(var(--c-accent-rgb), 0.08);
}
.theme-option .form-check-input {
    float: none;
    margin: 0 0 0.5rem 0;
}
.theme-option-body {
    display: block;
}
.theme-option-label {
    display: block;
    margin-top: 0.5rem;
    font-weight: 600;
}

/* Miniature of each scheme: page bar + card, drawn from literal values so
   the preview stays correct whichever scheme the page itself is rendering. */
.theme-swatch {
    display: block;
    height: 56px;
    border-radius: 6px;
    border: 1px solid var(--c-border-strong);
    padding: 7px;
    overflow: hidden;
}
.theme-swatch-bar {
    display: block;
    height: 8px;
    border-radius: 2px;
    margin-bottom: 6px;
}
.theme-swatch-card {
    display: block;
    height: 24px;
    border-radius: 3px;
}
.theme-swatch-dark { background-color: #0d0e11; }
.theme-swatch-dark .theme-swatch-bar { background: linear-gradient(90deg, #04c8ff, #ff2e93); width: 45%; }
.theme-swatch-dark .theme-swatch-card { background-color: #15171c; border: 0; }
.theme-swatch-light { background-color: #f4f2ee; }
.theme-swatch-light .theme-swatch-bar { background: linear-gradient(90deg, #0a6f8c, #be1263); width: 45%; }
.theme-swatch-light .theme-swatch-card { background-color: #ffffff; border: 0; }

/* --- Print: hide nav --- */
@media print {
    .navbar, .page-header .btn { display: none !important; }
}

/* --- Surfaces: elevation instead of outlines ---
   Previously every card carried a 1px outline plus a sheen plus a
   two-part shadow. Outlines are the thing that dates an admin panel:
   they chop the page into boxes and compete with the data inside them.
   A card now reads as a surface step plus a soft ambient shadow, and
   nothing else. The one hairline kept anywhere is under a table head,
   because that rule genuinely separates labels from values.
   Both admin schemes and the portal share these rules; only the --c-*
   values differ between them. */
.app-body .card,
.portal-body .card {
    background-color: var(--c-surface);
    background-image: none;
    border: 0;
    border-radius: var(--c-radius);
    box-shadow:
        0 1px 2px var(--c-shadow),
        0 18px 40px -26px var(--c-shadow-ambient);
}

.app-body .card-header,
.portal-body .card-header {
    background-color: transparent;
    border-bottom: 0;
    padding-top: 1.125rem;
    padding-bottom: 0.5rem;
}

.app-body .card-footer,
.portal-body .card-footer {
    background-color: transparent;
    border-top: 0;
}

/* The one hairline worth keeping: it separates column labels from data. */
.app-body .card .table thead th,
.portal-body .card .table thead th {
    background-color: transparent;
    border-bottom: 1px solid var(--c-border);
}

/* Modals and dropdowns sit above the page, so they lift further. */
.app-body .modal-content,
.app-body .dropdown-menu,
.portal-body .modal-content,
.portal-body .dropdown-menu {
    background-color: var(--c-surface);
    background-image: none;
    border: 0;
    border-radius: var(--c-radius);
    box-shadow:
        0 2px 8px var(--c-shadow),
        0 32px 70px -28px var(--c-shadow-ambient);
}

.app-body .dropdown-menu,
.portal-body .dropdown-menu {
    border-radius: var(--c-radius-sm);
    padding: 0.375rem;
}

.app-body .dropdown-item,
.portal-body .dropdown-item {
    border-radius: 8px;
}

/* Tabler's active dropdown item is grey-on-grey (3.68:1 on paper). Give
   it the same cyan treatment the active sidebar item uses. */
.app-body .navbar .dropdown-menu .dropdown-item.active,
.app-body .dropdown-menu .dropdown-item.active,
.portal-body .navbar .dropdown-menu .dropdown-item.active,
.portal-body .dropdown-menu .dropdown-item.active {
    /* Tabler pins the active item's fill to rgba(0,0,0,.2) with a rule
       that survives a five-class override (the colour lands, the fill
       does not), and on paper that grey reads 3.57:1. Forced, narrowly. */
    background-color: rgba(var(--c-accent-rgb), 0.14) !important;
    color: var(--c-accent);
    font-weight: 700;
}

/* Inputs sit *into* the surface rather than on it. With card outlines
   gone, the field outline is now the only one on the page — which is
   exactly the cue you want it to be. */
.app-body .form-control,
.app-body .form-select,
.portal-body .form-control,
.portal-body .form-select {
    background-color: var(--c-input-bg);
    border-color: var(--c-border-strong);
    border-radius: var(--c-radius-sm);
    box-shadow: inset 0 1px 2px var(--c-shadow);
}

/* Controls go pill-shaped; groups square off their inner edges so the
   segments still read as one control. */
.app-body .btn,
.portal-body .btn {
    border-radius: 999px;
    font-weight: 600;
}

.app-body .btn-group > .btn:not(:first-child),
.portal-body .btn-group > .btn:not(:first-child) {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
}

.app-body .btn-group > .btn:not(:last-child),
.portal-body .btn-group > .btn:not(:last-child) {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

.app-body .avatar,
.portal-body .avatar {
    border-radius: 999px;
}

/* Row actions read as one-tap targets rather than bare glyphs: a round
   surface chip, a larger icon, and colour arriving on hover. */
.app-body .btn.btn-icon,
.portal-body .btn.btn-icon {
    width: 2.25rem;
    height: 2.25rem;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background-color: var(--c-surface-2);
    font-size: 1rem;
    line-height: 1;
    transition: background-color 0.15s, color 0.15s;
}

.app-body .btn.btn-icon.btn-sm,
.portal-body .btn.btn-icon.btn-sm {
    width: 2.125rem;
    height: 2.125rem;
    font-size: 0.9375rem;
}

.app-body .btn-icon.btn-ghost-primary,
.portal-body .btn-icon.btn-ghost-primary {
    color: var(--c-accent);
}
.app-body .btn-icon.btn-ghost-primary:hover,
.portal-body .btn-icon.btn-ghost-primary:hover {
    background-color: rgba(var(--c-accent-rgb), 0.18);
    color: var(--c-accent);
}

.app-body .btn-icon.btn-ghost-danger,
.portal-body .btn-icon.btn-ghost-danger {
    color: var(--tblr-red);
}
.app-body .btn-icon.btn-ghost-danger:hover,
.portal-body .btn-icon.btn-ghost-danger:hover {
    background-color: rgba(var(--tblr-red-rgb), 0.18);
    color: var(--tblr-red);
}

.app-body .btn-icon.btn-ghost-secondary,
.portal-body .btn-icon.btn-ghost-secondary {
    color: var(--c-text-muted);
}
.app-body .btn-icon.btn-ghost-secondary:hover,
.portal-body .btn-icon.btn-ghost-secondary:hover {
    background-color: rgba(var(--c-accent-rgb), 0.14);
    color: var(--c-text);
}

/* Adjacent row actions need air between the chips. The delete action is
   wrapped in its own form, so it is not a direct child of the cell —
   only the genuinely first chip loses its offset. */
.app-body .table td .btn-icon,
.portal-body .table td .btn-icon {
    margin-left: 0.375rem;
}
.app-body .table td > .btn-icon:first-child,
.portal-body .table td > .btn-icon:first-child {
    margin-left: 0;
}

/* Sidebar sheds its outline along with everything else. */
.app-body .navbar-vertical,
.portal-body .navbar-vertical {
    border-right: 0;
}

/* Pink carries urgency — an overdue count is the one number on the
   dashboard that should interrupt you — so it is the single status that
   stays filled. Every other badge is an outlined pill. */
.app-body .badge.bg-pink,
.portal-body .badge.bg-pink {
    background-image: none;
    background-color: var(--c-emphasis) !important;
    border-color: var(--c-emphasis);
    color: var(--c-emphasis-ink) !important;
}

.text-emphasis {
    color: var(--c-emphasis) !important;
}

/* ===========================
   PORTAL — Light client theme

   The portal is light-themed but deliberately outside .app-body, so it
   declares its own tokens rather than inheriting the admin light ones.
   Values match the admin light scheme on purpose: clients and
   caseworkers should recognise one product. The portal is light only —
   it has no dark counterpart, so every rule here assumes paper.
   =========================== */
.portal-body {
    --c-bg: #f4f2ee;
    --c-surface: #ffffff;
    --c-surface-2: #eceae4;
    --c-border: #e4e1d9;
    --c-border-strong: #d2cec3;
    --c-text: #14161a;
    --c-text-muted: #5c6570;
    --c-text-dim: #78818d;
    --c-accent: #0a6f8c;
    --c-accent-hover: #075f78;
    --c-accent-ink: #ffffff;
    --c-accent-rgb: 10, 111, 140;
    --c-emphasis: #be1263;
    --c-emphasis-hover: #a10f54;
    --c-emphasis-ink: #ffffff;
    --c-emphasis-rgb: 190, 18, 99;
    --c-sheen: rgba(255, 255, 255, 0.9);
    --c-sheen-edge: rgba(255, 255, 255, 0.9);
    --c-shadow: rgba(43, 37, 26, 0.06);
    --c-shadow-ambient: rgba(43, 37, 26, 0.16);
    --c-header-tint: #faf8f4;
    --c-footer-tint: #f7f5f0;
    --c-input-bg: #ffffff;
    --c-hero-tone: #1b2433;

    --tblr-body-bg: var(--c-bg);
    --tblr-body-color: var(--c-text);
    --tblr-bg-surface: var(--c-surface);
    --tblr-bg-surface-secondary: var(--c-bg);
    --tblr-bg-surface-tertiary: var(--c-surface-2);
    --tblr-border-color: var(--c-border);
    --tblr-border-color-translucent: var(--c-border);
    --tblr-secondary: var(--c-text-muted);
    --tblr-muted: var(--c-text-muted);
    --tblr-primary: var(--c-accent);
    --tblr-primary-rgb: var(--c-accent-rgb);
    --tblr-link-color: var(--c-accent);
    --tblr-link-hover-color: var(--c-accent-hover);
    --tblr-danger: #be123c;
    --tblr-success: #047857;
    --tblr-warning: #b45309;

    /* The portal is outside .app-body, so it never inherited the remapped
       hues. Same fill / ink pairs as admin light. */
    --tblr-green: #34d399;
    --tblr-green-rgb: 52, 211, 153;
    --c-ink-green: #0b7a53;
    --tblr-teal: #2dd4bf;
    --tblr-teal-rgb: 45, 212, 191;
    --c-ink-teal: #0c6b63;
    --tblr-yellow: #f5a524;
    --tblr-yellow-rgb: 245, 165, 36;
    --c-ink-yellow: #8a5a00;
    --tblr-orange: #f97316;
    --tblr-orange-rgb: 249, 115, 22;
    --c-ink-orange: #a8450f;
    --tblr-cyan: #06b6d4;
    --tblr-cyan-rgb: 6, 182, 212;
    --c-ink-cyan: #0a6f8c;
    --tblr-blue: #06b6d4;   /* blue is the brand cyan — see note below */
    --tblr-blue-rgb: 6, 182, 212;
    --c-ink-blue: #0a6f8c;
    --tblr-purple: #7c3aed;
    --tblr-purple-rgb: 124, 58, 237;
    --c-ink-purple: #6b21a8;
    --tblr-red: #d0263c;
    --tblr-red-rgb: 208, 38, 60;
    --c-ink-red: #a81228;
    --tblr-pink: #d81b76;
    --tblr-pink-rgb: 216, 27, 118;
    --c-ink-pink: #a10f54;

    background-color: var(--c-bg);
}

.portal-body .page-wrapper {
    background-color: var(--c-bg);
}

.portal-body .btn-primary {
    --tblr-btn-color: var(--c-accent-ink);
    --tblr-btn-bg: var(--c-accent);
    --tblr-btn-border-color: var(--c-accent);
    --tblr-btn-hover-color: var(--c-accent-ink);
    --tblr-btn-hover-bg: var(--c-accent-hover);
    --tblr-btn-hover-border-color: var(--c-accent-hover);
    --tblr-btn-active-color: var(--c-accent-ink);
    --tblr-btn-active-bg: var(--c-accent-hover);
    --tblr-btn-active-border-color: var(--c-accent-hover);
    font-weight: 700;
}

/* The rail is paper, like the rest of the portal and like the admin light
   skin — the portal is a light-only product, so it needs no rail colour of
   its own: --tblr-bg-surface already resolves to the same chrome both
   skins use. Every rail rule above names .portal-body alongside .app-body
   for the same reason. */
/* --- Page header hero banners ---
   The photography stays, but it is treated rather than dropped in raw.
   background-blend-mode: luminosity keeps only the luminance of the
   photo and takes its colour from --c-hero-tone underneath, collapsing
   every hero to one duotone; the ::after then lays a cyan wash in from
   one corner and a pink wash in from the other, over a bottom-weighted
   scrim that guarantees the title's contrast wherever the photo is
   bright. All of it is driven from the CSS, so the seventeen views that
   set their own background-image need no changes. */
.page-header-hero {
    background-size: cover;
    background-position: center;
    background-color: var(--c-hero-tone);
    background-blend-mode: luminosity;
    position: relative;
    padding: 2.5rem 0 2.75rem;
    margin: 0 !important;
    /* Left edge runs straight into the rail at the bottom, so that corner
       stays square; the two corners away from the chrome are rounded. */
    border-top-left-radius: var(--c-radius-lg);
    border-bottom-left-radius: 0;
    border-bottom-right-radius: var(--c-radius-lg);
    border-top-right-radius: 0;
}

/* A backing plate in the chrome colour sits behind the hero, so the
   rounded corners reveal the header/menu surface rather than the darker
   page ground — the corner reads as filled in rather than cut out.
   It needs z-index -1 (behind the hero's own clipped background), which
   is why the hero cannot use overflow:hidden; ::after inherits the radius
   instead so the wash still follows the rounded edge.
   The plate is square at the top, where it backs the rounded top-left
   corner against the header bar, but it copies the hero's rounded
   bottom-right: that corner faces the page body, not the chrome, so a
   square plate there left a wedge of chrome colour sitting on the page
   ground. Invisible while the two were both white; a dark wedge on paper
   once the portal's chrome went dark. */
.page-header-hero::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    background-color: var(--c-chrome);
    border-bottom-right-radius: inherit;
}
/* The wash uses the bright pair literally rather than the scheme's
   tokens. A hero is a dark photographic surface in every scheme, so the
   deepened paper-mode accents only muddy it — these sit on the photo,
   not on the page. */
.page-header-hero::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background:
        linear-gradient(115deg, rgba(4, 200, 255, 0.45) 0%, rgba(4, 200, 255, 0) 48%),
        linear-gradient(295deg, rgba(255, 46, 147, 0.62) 0%, rgba(255, 46, 147, 0) 40%),
        linear-gradient(180deg, rgba(7, 8, 11, 0.30) 0%, rgba(7, 8, 11, 0.80) 100%);
    /* The pink corner is screened rather than washed. A dot grid and a
       ramp are unioned (mask layers composite as `add` by default): the
       ramp is solid across the cyan half, so that half stays a smooth
       wash, and drops to a 0.62 floor into the pink corner, where the
       dots carry it back up to full. The floor is what keeps the scrim
       under the corner — take it lower and the photo blows out there. Screening the
       whole layer instead of just the pink would break the cyan up too;
       screening a separate element would cost a wrapper on 19 views. */
    -webkit-mask-image:
        radial-gradient(circle at 50% 50%, #000 0 var(--c-dot-r), rgba(0, 0, 0, 0) var(--c-dot-feather)),
        linear-gradient(295deg, rgba(0, 0, 0, 0.62) 0%, #000 40%);
    -webkit-mask-size: var(--c-dot-pitch) var(--c-dot-pitch), auto;
    mask-image:
        radial-gradient(circle at 50% 50%, #000 0 var(--c-dot-r), rgba(0, 0, 0, 0) var(--c-dot-feather)),
        linear-gradient(295deg, rgba(0, 0, 0, 0.62) 0%, #000 40%);
    mask-size: var(--c-dot-pitch) var(--c-dot-pitch), auto;
}
.page-header-hero .container-xl {
    position: relative;
    z-index: 1;
}
.page-header-hero .page-title,
.page-header-hero .page-pretitle {
    color: #fff;
}

/* An avatar's tint is meant for a flat surface; over a photo it drops to
   2:1. Give it a solid ground of its own instead. Scoped two classes deep
   so it outranks the paper-mode .bg-*-lt text rule, which would otherwise
   paint this dark-blue on a dark photo. */
.app-body .page-header-hero .avatar,
.portal-body .page-header-hero .avatar {
    background-color: rgba(7, 8, 11, 0.55) !important;
    color: #ffffff !important;
}

.page-header-hero .page-title {
    font-size: clamp(1.5rem, 1.15rem + 1.4vw, 2.25rem);
    font-weight: 800;
    letter-spacing: -0.035em;
    line-height: 1.08;
}
.page-header-hero .page-pretitle {
    opacity: 0.75;
}
.page-header-hero .btn-outline-secondary {
    color: #fff;
    border-color: rgba(255, 255, 255, 0.35);
}
.page-header-hero .btn-outline-secondary:hover {
    background-color: rgba(255, 255, 255, 0.12);
    border-color: rgba(255, 255, 255, 0.6);
}

/* A hero with the dossier tab bar under it is shorter and squares off,
   so the tabs read as attached to it rather than floating below. */
/* The dossier hero runs straight into its tab bar, so it keeps hard
   corners all round. */
.page-header-hero.has-tabbar {
    padding: 1.5rem 0 1.75rem;
    border-radius: 0;
}

/* --- EasyMDE: theme-aware editor --- */
.EasyMDEContainer .CodeMirror {
    background-color: var(--c-input-bg);
    color: var(--c-text);
    border-color: var(--c-border-strong);
}
.EasyMDEContainer .CodeMirror-cursor {
    border-left-color: var(--c-text);
}
.EasyMDEContainer .editor-toolbar {
    background-color: var(--c-surface-2);
    border-color: var(--c-border-strong);
}
.EasyMDEContainer .editor-toolbar button {
    color: var(--c-text-muted) !important;
}
.EasyMDEContainer .editor-toolbar button:hover,
.EasyMDEContainer .editor-toolbar button.active {
    background-color: rgba(var(--c-accent-rgb), 0.15);
    color: var(--c-accent) !important;
    border-color: transparent;
}
.EasyMDEContainer .editor-toolbar i.separator {
    border-left-color: var(--c-border);
    border-right-color: var(--c-border);
}
.EasyMDEContainer .CodeMirror .CodeMirror-selected {
    background: rgba(var(--c-accent-rgb), 0.2);
}
.EasyMDEContainer .CodeMirror .cm-header {
    color: var(--c-text);
}
.EasyMDEContainer .CodeMirror .cm-link,
.EasyMDEContainer .CodeMirror .cm-url {
    color: var(--c-accent);
}
.EasyMDEContainer .CodeMirror .cm-formatting {
    color: var(--c-text-dim);
}
.EasyMDEContainer .editor-preview {
    background-color: var(--c-surface);
    color: var(--c-text);
}
.EasyMDEContainer .editor-preview a {
    color: var(--c-accent);
}

/* --- Berichten: leesbare berichtinhoud (dark theme) --- */
.message-item .message-toggle {
    border-radius: var(--tblr-border-radius);
    padding: 0.25rem 0.5rem;
    margin: -0.25rem -0.5rem;
    transition: background-color 0.15s ease;
}
.message-item .message-toggle:hover {
    background-color: rgba(var(--c-accent-rgb), 0.06);
}
.message-item .message-subject {
    color: var(--tblr-body-color);
}
.message-item .message-caret {
    color: var(--tblr-secondary);
    font-size: 0.7rem;
    transition: transform 0.15s ease;
}
.message-item .message-toggle[aria-expanded="true"] .message-caret {
    transform: rotate(90deg);
}
.message-body {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    font-size: 0.875rem;
    line-height: 1.6;
    color: var(--c-text);
    background-color: var(--c-surface-2);
    border: 1px solid var(--tblr-border-color);
    border-left: 3px solid var(--tblr-primary);
    border-radius: var(--tblr-border-radius);
    padding: 0.875rem 1rem;
}
