/* Summary: styling van het klantportaal — thema "Nachtdienst", dezelfde huisstijl als de publieke
   site (donker als basis, licht als keuze) maar rustiger: een klant ziet hier een handvol schermen
   en moet niet het gevoel krijgen in een beheerpaneel te zijn beland.
   Alle kleuren staan als custom properties op :root; een component-regel verwijst nooit naar een
   letterlijke kleur, zodat het lichte thema puur een andere set tokens is. Het actieve thema staat
   als data-theme op <html>: voor een ingelogde klant zet de server dat uit
   PortalAccount.PrefersLightTheme (dus geen flits), op de inlogschermen doet theme.js het uit
   localStorage.
   Geen inline styles in de views — de CSP hier staat ze niet toe. De enige uitzondering is het
   genoncede <style>-blok in Timeline/Project.cshtml met de per-project berekende posities.
   Keywords: portaal, styling, klant, thema, dark mode, light mode, data-theme */

/* ============================================================================================
   1. Thematokens — gelijk aan site.css van de publieke site; houd ze in de pas.
   ============================================================================================ */

:root {
    color-scheme: dark;

    /* Breedte van de paginakolom: de kop en de inhoud houden dezelfde maat, dus dit staat op één plek.
       Geen kleur, dus deze hoort niet in het lichte-thema-blok hieronder — die twee blokken dekken de
       kleurtokens. Sinds de menu-items een icoontje hebben paste de kop niet meer prettig op 900px. */
    --page-width: 1160px;

    --bg: #0A1220;
    --bg-alt: #0E1929;
    --surface: #131F32;
    --surface-2: #18273D;
    --surface-input: #0C1524;

    --line: #24344E;
    --line-strong: #35507A;

    --brand: #F5A524;
    --brand-hover: #FFB944;
    --brand-text: #F5A524;
    --on-brand: #16202F;

    --accent: #38D6C4;
    --accent-soft: rgba(56, 214, 196, .08);

    --text: #C3D0E2;
    --text-bright: #F2F6FC;
    --muted: #7E90AA;
    --placeholder: #55688A;

    --ok-bg: rgba(16, 185, 129, .10);
    --ok-border: rgba(52, 211, 153, .38);
    --ok-text: #6EE7B7;

    --warn-bg: rgba(245, 165, 36, .10);
    --warn-border: rgba(245, 165, 36, .38);
    --warn-text: #FBC46A;

    --bad-bg: rgba(239, 68, 68, .10);
    --bad-border: rgba(248, 113, 113, .38);
    --bad-text: #FCA5A5;

    /* Statuskleuren op de tijdlijn. Bewust eigen tokens en niet hergebruik van ok/warn/bad: een
       mijlpaal-stip moet vol en zichtbaar zijn, geen zachte achtergrondtint. */
    --dot-planned: #60A5FA;
    --dot-reached: #34D399;
    --dot-progress: #FBBF24;
    --dot-blocked: #A78BFA;
    --dot-cancelled: #64748B;
    --dot-overdue: #F87171;
    /* Een gedateerde melding: geen status, dus een eigen kleur naast het stoplicht. */
    --dot-note: #38BDF8;
    --note-bg: rgba(56, 189, 248, .14);
    --bar-tint: rgba(96, 165, 250, .16);
    --today: #F87171;

    /* Boekingskalender — dezelfde betekenis als op de publieke site: dezelfde kleur moet voor een
       klant hetzelfde betekenen, waar 'ie 'm ook ziet. */
    --cal-open: #0E9F8B;
    --cal-open-text: #04231F;
    --cal-full: #B4332F;
    --cal-full-text: #FFE8E6;
    --cal-split: #1B2B42;
    --cal-closed: #182740;
    --cal-closed-text: #5D708C;

    --radius: 10px;
    --radius-sm: 7px;
    --shadow: none;
    /* Slide-in melding (liveproject.js): een zwevende kaart heeft iets van hoogte nodig, ook in het
       donkere thema waar --shadow bewust op none staat. Eigen token, want een letterlijke kleur in
       een component-regel klopt per definitie in maar één van de twee thema's. */
    --toast-shadow: 0 12px 32px rgba(0, 0, 0, .5);

    --sans: system-ui, "Segoe UI", Roboto, Arial, sans-serif;
    --mono: ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;
}

:root[data-theme="light"] {
    color-scheme: light;

    --bg: #F7F9FC;
    --bg-alt: #ECF1F8;
    --surface: #FFFFFF;
    --surface-2: #F4F7FB;
    --surface-input: #FFFFFF;

    --line: #DEE6F0;
    --line-strong: #BFCDDF;

    --brand: #F0A01B;
    --brand-hover: #DB8E0C;
    --brand-text: #96590A;
    --on-brand: #1B1305;

    --accent: #0F766E;
    --accent-soft: rgba(15, 118, 110, .07);
    --accent-border: rgba(15, 118, 110, .22);

    --text: #43536A;
    --text-bright: #101E33;
    --muted: #64748B;
    --placeholder: #94A3B8;

    --ok-bg: #ECFDF5;
    --ok-border: #A7F3D0;
    --ok-text: #065F46;

    --warn-bg: #FFFBEB;
    --warn-border: #FDE68A;
    --warn-text: #92400E;

    --bad-bg: #FEF2F2;
    --bad-border: #FECACA;
    --bad-text: #991B1B;

    --dot-planned: #2563EB;
    --dot-reached: #059669;
    --dot-progress: #D97706;
    --dot-blocked: #7C3AED;
    --dot-cancelled: #94A3B8;
    --dot-overdue: #DC2626;
    --dot-note: #0EA5E9;
    --note-bg: #F0F9FF;
    --bar-tint: #DBEAFE;
    --today: #DC2626;

    --cal-open: #0E9F8B;
    --cal-open-text: #FFFFFF;
    --cal-full: #DC2626;
    --cal-full-text: #FFFFFF;
    --cal-split: #FFFFFF;
    --cal-closed: #E4EAF2;
    --cal-closed-text: #94A3B8;

    --shadow: 0 1px 2px rgba(15, 30, 51, .04), 0 10px 26px -14px rgba(15, 30, 51, .16);
    --toast-shadow: 0 12px 32px rgba(15, 30, 51, .20);
}

/* ============================================================================================
   2. Basis
   ============================================================================================ */

* { box-sizing: border-box; }

/* Een element met het hidden-attribuut hoort weg te zijn, ook als een klasse-regel er een display op
   zet. De browserregel [hidden]{display:none} is minder specifiek dan zo'n klasse, dus zonder dit
   blijft bijvoorbeeld een lege teller of een dichtgeklapt chatpaneel alsnog in beeld staan — precies
   het soort fout dat je pas ziet als je ernaar kijkt. */
[hidden] {
    display: none !important;
}

body {
    margin: 0;
    font-family: var(--sans);
    font-size: 16px;
    line-height: 1.65;
    background: var(--bg);
    color: var(--text);
    -webkit-font-smoothing: antialiased;
}

body.portal-centered {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: 20px;
}

h1, h2, h3 { color: var(--text-bright); line-height: 1.25; letter-spacing: -.015em; }

a { color: var(--accent); }

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    * { transition: none !important; animation: none !important; }
}

/* ============================================================================================
   3. Kop, navigatie en themaknop
   ============================================================================================ */

.portal-header {
    background: var(--bg-alt);
    border-bottom: 1px solid var(--line);
}

.portal-header-inner {
    max-width: var(--page-width);
    margin: 0 auto;
    padding: 14px 20px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}

.portal-brand {
    font-weight: 650;
    color: var(--text-bright);
    text-decoration: none;
    display: flex;
    align-items: center;
    gap: 10px;
}

/* Zelfde merkteken als op de publieke site: amber blokje met een cyaan hoek. */
.portal-brand::before {
    content: "";
    width: 22px;
    height: 22px;
    border-radius: 6px;
    flex: none;
    background: var(--brand);
    box-shadow: inset -5px -5px 0 0 var(--accent);
}

/* Navigatie in de kop. Chips in plaats van kale links: op een telefoon is een tekstlink van 12px
   geen fatsoenlijk tikdoel, en zo is meteen te zien dat het knoppen zijn. */
.portal-nav {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-wrap: wrap;
}

.portal-nav a {
    /* inline-flex zodat het icoontje op dezelfde regel blijft en netjes op de tekst uitlijnt. */
    display: inline-flex;
    align-items: center;
    gap: 7px;
    color: var(--text);
    text-decoration: none;
    font-weight: 600;
    font-size: 0.9rem;
    padding: 6px 12px;
    border-radius: 999px;
    white-space: nowrap;
}

/* Decoratieve menu-icoontjes: erven de kleur van de link (currentColor), dus ze kleuren mee bij hover
   en kloppen in beide thema's zonder eigen kleur. */
.nav-icon {
    width: 18px;
    height: 18px;
    flex: 0 0 auto;
    /* Iets rustiger dan de tekst: het icoon wijst aan, het woord vertelt. */
    opacity: .75;
}

.portal-nav a:hover .nav-icon,
.portal-nav a:focus-visible .nav-icon,
.portal-user a:hover .nav-icon {
    opacity: 1;
}

/* Ook in de rechterkolom (Beveiliging) staat de tekst naast het icoon. */
.portal-user a {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.portal-nav a:hover,
.portal-nav a:focus-visible {
    background: var(--surface);
    color: var(--brand-text);
}

.portal-user {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    color: var(--muted);
    font-size: 0.9rem;
}

.portal-user form { margin: 0; }
.portal-user a { color: var(--accent); }

/* De naam mag wijken als de ruimte krap wordt — een afgekapt e-mailadres is beter dan een kop die
   buiten het scherm loopt. */
.portal-user-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 40vw;
}

.portal-linkbutton {
    background: none;
    border: none;
    color: var(--accent);
    cursor: pointer;
    font: inherit;
    padding: 0;
}

/* Themaknop. In de kop een echt <form> dat naar de server post: de keuze hoort bij het account en
   niet bij deze browser, dus 'ie moet ook werken zonder JavaScript. Op de inlogschermen (waar er
   nog geen account is) staat dezelfde knop als <button> en doet theme.js het werk. */
.portal-theme form { margin: 0; display: flex; }

.portal-theme-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    background: transparent;
    color: var(--muted);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: color .18s ease, border-color .18s ease, background .18s ease;
}

.portal-theme-button:hover {
    color: var(--brand-text);
    border-color: var(--line-strong);
    background: var(--surface);
}

.portal-theme-button svg { width: 17px; height: 17px; display: block; }

/* Donker actief → toon de zon (klik = naar licht). Licht actief → toon de maan. */
.portal-theme-button .theme-icon-light { display: none; }
:root[data-theme="light"] .portal-theme-button .theme-icon-light { display: block; }
:root[data-theme="light"] .portal-theme-button .theme-icon-dark { display: none; }

/* Op de anonieme schermen kan het thema alleen met JavaScript gewisseld worden; zonder JS hoort de
   knop er niet te staan. theme.js haalt deze klasse weg. */
.portal-theme-button.theme-toggle-unavailable { display: none; }

/* Losse themaknop op een pagina zonder kop (inloggen, uitnodiging): rechtsboven in de kaart. */
.portal-theme-floating {
    display: flex;
    justify-content: flex-end;
    margin-bottom: -6px;
}

/* Onder 760px wordt de kop een paar regels: merk, navigatie, en wie je bent. Alles blijft
   zichtbaar, niets schuift buiten beeld. */
@media (max-width: 760px) {
    .portal-header-inner {
        padding: 12px 14px;
        gap: 10px;
    }

    .portal-brand,
    .portal-nav,
    .portal-user {
        flex-basis: 100%;
    }

    .portal-nav {
        /* Negatieve marge zodat de chips links uitlijnen met de merknaam erboven, ondanks hun
           eigen padding. */
        margin-left: -12px;
    }

    .portal-user {
        justify-content: space-between;
        border-top: 1px solid var(--line);
        padding-top: 10px;
    }

    .portal-user-name {
        max-width: 100%;
        flex: 1 1 auto;
    }
}

/* ============================================================================================
   4. Kaarten en tekstelementen
   ============================================================================================ */

.portal-main {
    max-width: var(--page-width);
    margin: 0 auto;
    padding: 28px 20px 60px;
}

.portal-card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    padding: 22px;
    margin-bottom: 18px;
}

.portal-card-narrow {
    width: min(420px, 100%);
    margin: 0;
}

.portal-card h1, .portal-card h2 { margin-top: 0; }

.portal-note { color: var(--muted); font-size: .95rem; }

.portal-error {
    color: var(--bad-text);
    background: var(--bad-bg);
    border: 1px solid var(--bad-border);
    border-radius: var(--radius-sm);
    padding: 10px 14px;
    margin-bottom: 14px;
}

/* Een lege validatiesamenvatting rendert alsnog een <ul>, dus :empty vangt 'm niet. */
.portal-error:empty,
.portal-error.validation-summary-valid { display: none; }

.portal-success {
    background: var(--ok-bg);
    border: 1px solid var(--ok-border);
    color: var(--ok-text);
    border-radius: var(--radius-sm);
    padding: 10px 14px;
    margin-bottom: 16px;
}

/* ============================================================================================
   5. Formulieren
   ============================================================================================ */

label {
    display: block;
    font-weight: 600;
    margin: 14px 0 6px;
    color: var(--text-bright);
}

input[type="text"], input[type="email"], input[type="password"], input[type="date"], select, textarea {
    width: 100%;
    padding: 10px 12px;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-input);
    color: var(--text-bright);
    font: inherit;
}

input::placeholder, textarea::placeholder { color: var(--placeholder); }

/* Optioneel datumbereik onder een formulier (de statusupdate met een periode). Als fieldset zodat
   de twee velden ook voor een schermlezer bij elkaar horen. */
.portal-daterange {
    margin: 18px 0 0;
    padding: 12px 14px 14px;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface-2);
}

.portal-daterange legend {
    padding: 0 6px;
    font-weight: 600;
    color: var(--text-bright);
}

.portal-daterange .portal-note { margin: 0; }

.portal-daterange-fields {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 0 14px;
}

.portal-daterange label { margin-top: 10px; }

input:focus, select:focus, textarea:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

textarea { resize: vertical; }

.portal-checkbox {
    display: flex;
    align-items: center;
    gap: 8px;
    font-weight: 400;
    color: var(--text);
}

.portal-checkbox input { width: auto; accent-color: var(--brand); }

.portal-button {
    margin-top: 18px;
    padding: 12px 24px;
    background: var(--brand);
    color: var(--on-brand);
    border: none;
    border-radius: var(--radius-sm);
    font-size: 1rem;
    font-weight: 700;
    cursor: pointer;
    transition: background .18s ease;
}

.portal-button:hover { background: var(--brand-hover); }

/* Knop op slot tijdens de wachttijd na een wachtwoordaanvraag. */
.portal-button:disabled {
    background: var(--surface-2);
    color: var(--muted);
    cursor: not-allowed;
}

.portal-button-quiet {
    background: transparent;
    color: var(--bad-text);
    border: 1px solid var(--bad-border);
}

.portal-button-quiet:hover { background: var(--bad-bg); }

/* Terugverwijzing onderaan een los formulier (inloggen, wachtwoord vergeten). */
.portal-back-link {
    margin-top: 18px;
    margin-bottom: 0;
}

/* ============================================================================================
   6. Projectenlijst, badges en lijsten
   ============================================================================================ */

.portal-project-list {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 16px;
}

.portal-project {
    display: block;
    text-decoration: none;
    color: inherit;
    margin-bottom: 0;
    transition: border-color .2s ease, transform .2s ease;
}

.portal-project:hover { border-color: var(--line-strong); transform: translateY(-3px); }
.portal-project h2 { margin-bottom: 4px; font-size: 1.15rem; }

.portal-badge {
    display: inline-block;
    background: var(--surface-2);
    color: var(--muted);
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 2px 10px;
    font-size: .8rem;
    margin: 4px 4px 0 0;
}

/* Iets dat op de klant wacht moet eruit springen — dat is de reden dat ze hier kijken. */
.portal-badge-action {
    background: var(--warn-bg);
    border-color: var(--warn-border);
    color: var(--warn-text);
    font-weight: 600;
}

.portal-badge-ok {
    background: var(--ok-bg);
    border-color: var(--ok-border);
    color: var(--ok-text);
}

.portal-badge-warn {
    background: var(--bad-bg);
    border-color: var(--bad-border);
    color: var(--bad-text);
}

.portal-actions {
    list-style: none;
    padding: 0;
    margin: 0;
}

.portal-actions li {
    border-top: 1px solid var(--line);
    padding: 12px 0;
}

.portal-actions li:first-child { border-top: none; }
.portal-actions li.is-closed { opacity: .55; }

.portal-action-head {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

.portal-timeline {
    list-style: none;
    padding: 0;
    margin: 0;
}

.portal-timeline li {
    display: flex;
    gap: 14px;
    border-top: 1px solid var(--line);
    padding: 12px 0;
}

.portal-timeline li:first-child { border-top: none; }

.portal-timeline-date {
    flex: 0 0 92px;
    color: var(--muted);
    font-family: var(--mono);
    font-size: .84rem;
}

.portal-timeline p { margin: 4px 0 0; }

.portal-footer {
    text-align: center;
    padding: 24px 20px;
    color: var(--muted);
    font-size: .9rem;
    border-top: 1px solid var(--line);
}

@media (max-width: 560px) {
    .portal-timeline li { flex-direction: column; gap: 4px; }
    .portal-timeline-date { flex: none; }
}

/* Impact van een risico — zelfde 1..3-schaal als RiskSeverity in de CRM. */
.severity-1 { background: var(--ok-bg); border-color: var(--ok-border); color: var(--ok-text); }
.severity-2 { background: var(--warn-bg); border-color: var(--warn-border); color: var(--warn-text); }
.severity-3 { background: var(--bad-bg); border-color: var(--bad-border); color: var(--bad-text); }

/* Inklapbaar formulier ("Nieuw wijzigingsverzoek indienen"). */
.portal-card > details > summary {
    cursor: pointer;
    font-weight: 600;
    color: var(--accent);
    padding: 10px 0;
}

/* Los icoontje in lopende tekst (bv. de opmerkingenteller). */
.portal-icon {
    width: 16px;
    height: 16px;
    vertical-align: -3px;
}

/* ============================================================================================
   7. Planning: RAG-status, tijdlijn en mijlpalen
   De posities (left/width/top) staan bewust NIET hier maar in een genonced <style>-blok in
   Timeline/Project.cshtml — ze zijn per project berekend.
   ============================================================================================ */

.rag-badge {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 600;
    border: 1px solid transparent;
    white-space: nowrap;
    margin-right: 8px;
}

.rag-green { background: var(--ok-bg); color: var(--ok-text); border-color: var(--ok-border); }
.rag-amber { background: var(--warn-bg); color: var(--warn-text); border-color: var(--warn-border); }
.rag-red { background: var(--bad-bg); color: var(--bad-text); border-color: var(--bad-border); }

/* RAG-stoplicht: een gevulde SVG-stip in plaats van een emoji. De cirkel erft de tekstkleur van de
   badge (currentColor), dus rag-green/amber/red bepaalt pil én stip in één keer. */
.rag-dot {
    width: 11px;
    height: 11px;
    fill: currentColor;
    flex: 0 0 auto;
}

.timeline-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    font-size: 0.8rem;
    color: var(--muted);
    margin-bottom: 6px;
}

.timeline-progress-label { font-weight: 600; color: var(--text-bright); }

.timeline-progress {
    height: 6px;
    border-radius: 999px;
    background: var(--surface-2);
    overflow: hidden;
}

.timeline-progress-fill { height: 100%; background: var(--brand); }

/* Hoogte en posities komen als custom property uit de view (--tl-height/--tl-left/--tl-top/
   --tl-width, gezet in het genoncede <style>-blok): op een telefoon wordt de lijn verticaal en
   moet de media query onderaan die waarden kunnen negeren. */
.timeline-track {
    position: relative;
    height: var(--tl-height);
    border-top: 2px solid var(--line-strong);
    padding-top: 10px;
    /* De marge boven is de strook waar de "vandaag"-vlag in hangt; zonder die ruimte landt het
       label bovenop de voortgangsbalk. */
    margin: 30px 60px 8px 0;
}

.timeline-today {
    position: absolute;
    left: var(--tl-left);
    top: -22px;
    bottom: 2px;
    width: 2px;
    background: var(--today);
    /* Boven de maandstreepjes maar ónder de mijlpalen: de lijn mag geen label doorsnijden. */
    z-index: 1;
}

.timeline-today::before {
    content: "vandaag";
    position: absolute;
    top: -4px;
    left: 50%;
    transform: translateX(-50%);
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--today);
    color: var(--bg);
    font-size: 0.68rem;
    font-weight: 700;
    line-height: 1.4;
    white-space: nowrap;
}

/* Maand-/kwartaal-/jaarschaal onder de mijlpalen. */
.timeline-tick {
    position: absolute;
    top: 0;
    bottom: 18px;
    width: 1px;
    background: var(--line);
    z-index: 0;
}

.timeline-tick > span {
    position: absolute;
    bottom: -17px;
    left: 0;
    transform: translateX(-50%);
    font-size: 0.68rem;
    color: var(--muted);
    white-space: nowrap;
}

/* Eén marker per datum: op de lijn staat wanneer, de namen zitten in het paneel eronder dat bij
   hover verschijnt en bij een klik als dialoog opengaat (timelinemarkers.js). Zelfde opzet als in
   admin.css — houd ze gelijk. */
/* De hele marker is het klikdoel, niet alleen de knop erin: op een telefoon staat het paneel met
   de namen open en dáár tikt iedereen op. De knop blijft voor toetsenbord en focus. */
.timeline-marker {
    position: absolute;
    left: var(--tl-left);
    top: var(--tl-top);
    height: 26px;
    display: flex;
    align-items: center;
    cursor: pointer;
    z-index: 2;
}

/* Zie admin.css: zonder dit valt het hover-paneel weg achter de markers die er in de DOM ná komen —
   z-index binnen een marker telt alleen in diens eigen stapelcontext. Houd beide blokken gelijk. */
.timeline-marker:hover,
.timeline-marker:focus-within {
    z-index: 10;
}

.timeline-marker.is-point { transform: translateX(-5px); }

.timeline-marker.is-bar { width: var(--tl-width); }

.timeline-marker-hit {
    display: flex;
    align-items: center;
    gap: 6px;
    height: 100%;
    max-width: 100%;
    padding: 0 4px;
    border: none;
    border-radius: 6px;
    background: none;
    font: inherit;
    color: inherit;
    cursor: pointer;
}

.timeline-marker-hit:focus-visible {
    outline: 2px solid var(--brand);
    outline-offset: 2px;
}

.timeline-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--dot-planned);
    border: 2px solid var(--surface);
    box-shadow: 0 0 0 1px var(--dot-planned);
    flex: 0 0 auto;
}

.timeline-marker.is-bar .timeline-marker-hit {
    width: 100%;
    padding: 0 8px;
    background: var(--bar-tint);
    border: 1px solid var(--dot-planned);
    overflow: hidden;
}

.timeline-marker.is-bar .timeline-dot { display: none; }

.timeline-label {
    font-size: 0.78rem;
    color: var(--text-bright);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Het aantal achter de datum: het enige teken op de lijn dat er meer dan één ding achter zit. */
.timeline-count {
    min-width: 16px;
    padding: 0 4px;
    border-radius: 999px;
    background: var(--text-bright);
    color: var(--bg);
    font-size: 0.65rem;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
    flex: 0 0 auto;
}

.timeline-marker.is-reached .timeline-dot,
.timeline-pop-item.is-reached .timeline-dot { background: var(--dot-reached); box-shadow: 0 0 0 1px var(--dot-reached); }
.timeline-marker.is-bar.is-reached .timeline-marker-hit { background: var(--ok-bg); border-color: var(--dot-reached); }

.timeline-marker.is-progress .timeline-dot,
.timeline-pop-item.is-progress .timeline-dot { background: var(--dot-progress); box-shadow: 0 0 0 1px var(--dot-progress); }
.timeline-marker.is-bar.is-progress .timeline-marker-hit { background: var(--warn-bg); border-color: var(--dot-progress); }

.timeline-marker.is-cancelled .timeline-dot,
.timeline-pop-item.is-cancelled .timeline-dot { background: var(--dot-cancelled); box-shadow: 0 0 0 1px var(--dot-cancelled); }
.timeline-marker.is-bar.is-cancelled .timeline-marker-hit { background: var(--surface-2); border-color: var(--dot-cancelled); }
.timeline-marker.is-cancelled .timeline-label { color: var(--muted); text-decoration: line-through; }

.timeline-marker.is-blocked .timeline-dot,
.timeline-pop-item.is-blocked .timeline-dot { background: var(--dot-blocked); box-shadow: 0 0 0 1px var(--dot-blocked); }
.timeline-marker.is-bar.is-blocked .timeline-marker-hit { background: rgba(167, 139, 250, .14); border-color: var(--dot-blocked); }

/* Een melding (gedateerde statusupdate) is geen planning en heeft geen status: eigen, rustige
   kleur zodat 'ie niet meedoet in het stoplicht van de mijlpalen. */
.timeline-marker.is-note .timeline-dot,
.timeline-pop-item.is-note .timeline-dot { background: var(--dot-note); box-shadow: 0 0 0 1px var(--dot-note); }
.timeline-marker.is-bar.is-note .timeline-marker-hit { background: var(--note-bg); border-color: var(--dot-note); }

.timeline-marker.is-overdue .timeline-dot,
.timeline-pop-item.is-overdue .timeline-dot { background: var(--dot-overdue); box-shadow: 0 0 0 1px var(--dot-overdue); }
.timeline-marker.is-bar.is-overdue .timeline-marker-hit { background: var(--bad-bg); border-color: var(--dot-overdue); }
.timeline-marker.is-overdue .timeline-label { color: var(--bad-text); font-weight: 600; }

/* Actiepunt op de lijn: een ruit in plaats van een rondje, zodat een deadline niet als mijlpaal
   gelezen wordt. Identiek aan admin.css — houd ze gelijk. */
.timeline-marker.is-action .timeline-dot,
.timeline-pop-item.is-action .timeline-dot {
    border-radius: 2px;
    rotate: 45deg;
    width: 9px;
    height: 9px;
}

.timeline-marker.is-action .timeline-label { border-bottom: 1px dotted currentColor; }

/* Een melding krijgt een vierkantje: derde soort op de lijn, dus ook een derde vorm. */
.timeline-marker.is-note .timeline-dot,
.timeline-pop-item.is-note .timeline-dot {
    border-radius: 2px;
    width: 9px;
    height: 9px;
}

/* Een risico of probleem: een driehoek, de vorm die iedereen als waarschuwing leest. Geen border
   of box-shadow, want clip-path knipt die er toch af. Identiek aan admin.css — houd ze gelijk. */
.timeline-marker.is-risk .timeline-dot,
.timeline-pop-item.is-risk .timeline-dot {
    width: 12px;
    height: 11px;
    border: none;
    border-radius: 0;
    box-shadow: none;
    clip-path: polygon(50% 0, 100% 100%, 0 100%);
}

/* Het paneel achter een marker. Dezelfde markup wordt naar de dialoog gekopieerd, dus wat hier
   staat geldt alleen voor de hover-variant. */
.timeline-pop {
    display: none;
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: 5;
    width: max-content;
    max-width: min(320px, 70vw);
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--surface);
    box-shadow: 0 12px 30px rgba(0, 0, 0, .35);
    text-align: left;
    white-space: normal;
}

/* Rechts op de lijn zou het paneel buiten de track hangen. */
.timeline-marker.is-right .timeline-pop { left: auto; right: 0; }

.timeline-marker:hover .timeline-pop,
.timeline-marker:focus-within .timeline-pop { display: block; }

/* Hover is de snelle blik: alleen namen en status. De toelichting en de link staan in de dialoog —
   onder de muis is een link niet te bereiken zonder het paneel te verlaten. */
.timeline-marker .timeline-pop .timeline-pop-desc,
.timeline-marker .timeline-pop .timeline-pop-actions { display: none; }

.timeline-marker .timeline-pop::after {
    content: "Klik voor de details";
    display: block;
    margin-top: 8px;
    font-size: 0.7rem;
    color: var(--muted);
}

.timeline-pop-date {
    margin: 0 0 6px;
    font-size: 0.72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--muted);
}

.timeline-pop-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.timeline-pop-item + .timeline-pop-item {
    margin-top: 8px;
    padding-top: 8px;
    border-top: 1px solid var(--line);
}

.timeline-pop-title {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0;
    font-size: 0.85rem;
    color: var(--text-bright);
}

.timeline-pop-meta {
    margin: 2px 0 0 16px;
    font-size: 0.72rem;
    color: var(--muted);
}

.timeline-pop-desc {
    margin: 4px 0 0 16px;
    font-size: 0.82rem;
}

.timeline-pop-item.is-overdue .timeline-pop-meta { color: var(--bad-text); font-weight: 600; }

.timeline-pop-item.is-cancelled .timeline-pop-title strong {
    color: var(--muted);
    text-decoration: line-through;
}

/* In de dialoog is het paneel gewoon inhoud: geen zwevend kaartje meer, en alles zichtbaar. */
.timeline-dialog-body .timeline-pop {
    display: block;
    position: static;
    max-width: none;
    padding: 0;
    border: none;
    box-shadow: none;
}

.timeline-dialog-body .timeline-pop-item + .timeline-pop-item {
    margin-top: 14px;
    padding-top: 14px;
}

.timeline-dialog-body .timeline-pop-actions {
    display: block;
    margin: 8px 0 0 16px;
    font-size: 0.82rem;
}

/* De enige <dialog> in het portaal. Bewust hier en niet uit de CRM overgenomen: admin.css hoort
   niet in het portaal en de kaartopmaak verschilt. */
.portal-modal {
    width: min(560px, calc(100vw - 32px));
    max-height: calc(100vh - 64px);
    padding: 0;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--surface);
    color: var(--text);
    box-shadow: 0 24px 60px rgba(0, 0, 0, .45);
    overflow: auto;
}

.portal-modal::backdrop { background: rgba(0, 0, 0, .55); }

.portal-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 14px 18px;
    border-bottom: 1px solid var(--line);
}

.portal-modal-header h2 {
    margin: 0;
    font-size: 1.05rem;
}

.portal-modal-close {
    padding: 0 4px;
    border: none;
    background: none;
    color: var(--muted);
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
}

.portal-modal-close:hover { color: var(--text-bright); }

.portal-modal-body { padding: 16px 18px 18px; }

/* Legenda onder de lijn: dezelfde markers, meelopend in de tekst in plaats van absoluut op de track. */
.timeline-legend {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    color: var(--muted);
    font-size: 0.8rem;
}

.timeline-legend .timeline-marker {
    position: static;
    height: auto;
    transform: none;
    margin-left: 8px;
}

.timeline-legend .timeline-marker:first-child { margin-left: 0; }

.timeline-legend-hint { color: var(--muted); }

/* Leesbare lijst onder de getekende lijn — op een telefoon is dit de echte weergave. */
.portal-milestones {
    list-style: none;
    margin: 16px 0 0;
    padding: 0;
}

.portal-milestones li {
    display: flex;
    gap: 16px;
    padding: 10px 0;
    border-top: 1px solid var(--line);
}

.portal-milestones li.is-closed { opacity: .6; }

.portal-milestones li.is-overdue .portal-timeline-date {
    color: var(--bad-text);
    font-weight: 600;
}

.portal-milestones p { margin: 4px 0 0; }

/* Op een telefoon wordt de lijn verticaal — identiek aan het blok in admin.css, houd ze gelijk.
   Horizontaal is een half jaar op 360px onleesbaar: markers vallen op elkaar en een punt van een
   paar procent breed is met een vinger niet te raken. Verticaal is het een lijst op datum, met de
   namen meteen zichtbaar in plaats van achter een hover die op een telefoon niet bestaat.
   Alles staat onder .timeline-track zodat de legenda eronder ongemoeid blijft. */
@media (max-width: 768px) {
    .timeline-track {
        display: flex;
        flex-direction: column;
        gap: 12px;
        /* De laanhoogte geldt alleen horizontaal; hier bepaalt de inhoud de hoogte. */
        height: auto;
        margin: 26px 0 8px;
        padding: 0 0 0 18px;
        border-top: none;
        border-left: 2px solid var(--line-strong);
    }

    /* De maand-/kwartaalschaal hoort bij een horizontale as. */
    .timeline-track .timeline-tick { display: none; }

    /* "Vandaag" wordt een regel op z'n eigen plek in de tijd (--tl-order uit de view). */
    .timeline-track .timeline-today {
        position: static;
        order: var(--tl-order, 0);
        width: auto;
        height: auto;
        background: none;
    }

    .timeline-track .timeline-today::before {
        position: static;
        top: auto;
        left: auto;
        transform: none;
        display: inline-block;
        margin-left: -12px;
    }

    /* Het hele blok is één tikdoel (zie data-timeline-marker op de marker zelf). De negatieve
       marge heft de padding op, zodat de achtergrond bij het indrukken iets uitsteekt maar de
       stip op de lijn blijft staan. */
    .timeline-track .timeline-marker {
        position: static;
        order: var(--tl-order, 0);
        display: block;
        width: auto;
        height: auto;
        padding: 4px 6px;
        margin-left: -6px;
        border-radius: 10px;
        transform: none;
        -webkit-tap-highlight-color: transparent;
    }

    .timeline-track .timeline-marker:active {
        background: var(--surface-2);
    }

    /* De stip van een punt hangt op de verticale lijn: 18px padding + 2px rand, stip 10px + 2px. */
    .timeline-track .timeline-marker.is-point .timeline-marker-hit { margin-left: -26px; }

    .timeline-track .timeline-marker-hit {
        height: auto;
        padding: 2px 0;
    }

    .timeline-track .timeline-marker.is-bar .timeline-marker-hit {
        width: auto;
        padding: 2px 8px;
    }

    .timeline-track .timeline-pop {
        display: block;
        position: static;
        max-width: none;
        margin: 2px 0 0 2px;
        padding: 0;
        border: none;
        background: none;
        box-shadow: none;
    }

    /* De datum staat al in de kop van de regel. */
    .timeline-track .timeline-pop-date { display: none; }

    .timeline-track .timeline-pop-item + .timeline-pop-item {
        margin-top: 5px;
        padding-top: 5px;
    }

    /* Onderstreept en in de accentkleur: het is geen link, maar de hele regel eromheen is wel
       aanraakbaar, dus het wijst naar iets dat er echt is. */
    .timeline-track .timeline-marker .timeline-pop::after {
        content: "Tik voor de details";
        margin-top: 6px;
        color: var(--brand);
        text-decoration: underline;
    }

    .timeline-label {
        white-space: normal;
        font-weight: 600;
    }
}

/* ============================================================================================
   8. Gesprek: opmerkingen bij een actiepunt en statusupdates
   ============================================================================================ */

.comment-thread {
    margin-top: 12px;
    border-top: 1px solid var(--line);
    padding-top: 10px;
}

.comment-thread > summary {
    cursor: pointer;
    font-size: 0.85rem;
    color: var(--muted);
    /* Het standaard driehoekje botst met het 💬-icoon. */
    list-style: none;
}

.comment-thread > summary::-webkit-details-marker { display: none; }

.comment {
    border-radius: var(--radius-sm);
    padding: 8px 12px;
    margin-top: 8px;
}

/* Twee kanten, twee kleuren — wie iets zei moet je niet hoeven lezen. */
.comment.is-us { background: var(--surface-2); }

.comment.is-client {
    background: var(--accent-soft);
    border-left: 3px solid var(--accent);
}

.comment-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-items: baseline;
    font-size: 0.75rem;
    color: var(--muted);
    margin-bottom: 4px;
}

.comment p { margin: 0; white-space: pre-wrap; }

.comment-form { margin-top: 10px; }
.comment-form textarea { width: 100%; }

/* Conceptbalk van formdraft.js — dezelfde JS als in de CRM, dus dezelfde klassen. */
.draft-banner {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    align-items: center;
    background: var(--warn-bg);
    border: 1px solid var(--warn-border);
    color: var(--warn-text);
    border-radius: var(--radius-sm);
    padding: 8px 12px;
    margin-bottom: 10px;
    font-size: 0.85rem;
}

.draft-banner .button {
    background: var(--brand);
    color: var(--on-brand);
    border: 0;
    border-radius: 6px;
    padding: 6px 12px;
    cursor: pointer;
    font: inherit;
    font-weight: 600;
}

.draft-banner .link-button {
    background: none;
    border: 0;
    padding: 0;
    color: var(--warn-text);
    text-decoration: underline;
    cursor: pointer;
    font: inherit;
}

/* ============================================================================================
   9. Documenten en digitale goedkeuring
   ============================================================================================ */

.portal-documents {
    list-style: none;
    margin: 0;
    padding: 0;
}

.portal-documents > li {
    padding: 12px 0;
    border-top: 1px solid var(--line);
}

.portal-documents > li:first-child { border-top: 0; }

/* Iets dat op een handtekening wacht mag niet wegvallen tussen de rest. */
.portal-documents > li.needs-signature {
    background: var(--warn-bg);
    border-left: 3px solid var(--brand);
    border-top-color: var(--warn-border);
    padding-left: 12px;
    border-radius: 6px;
}

.portal-document-head {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
}

.portal-approval {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    align-items: flex-start;
    margin-top: 8px;
}

.portal-approval > details > summary {
    cursor: pointer;
    font-size: 0.85rem;
    color: var(--warn-text);
    padding: 8px 0;
}

.portal-approval textarea { width: 100%; min-width: 260px; }

/* ============================================================================================
   10. Beveiligingspagina: 2FA instellen, wachtwoord wijzigen, vertrouwde apparaten
   ============================================================================================ */

.portal-setup-steps { margin: 0; padding-left: 20px; }
.portal-setup-steps > li { margin-bottom: 18px; }

/* De QR-code houdt in béide thema's een witte ondergrond met wat lucht eromheen: de code zelf is
   zwart-op-wit en wordt onleesbaar voor een telefooncamera zodra je 'm op donker zet of
   inverteert. */
.portal-qr {
    display: block;
    margin: 12px 0;
    padding: 10px;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    background: #FFFFFF;
    max-width: 100%;
    height: auto;
}

/* Het geheim moet overtypbaar zijn, dus lettertype met vaste breedte en genoeg lucht. */
.portal-secret {
    display: inline-block;
    font-family: var(--mono);
    font-size: 1rem;
    letter-spacing: .08em;
    word-break: break-all;
    background: var(--surface-2);
    border: 1px solid var(--line);
    color: var(--text-bright);
    border-radius: 6px;
    padding: 8px 12px;
}

/* Onthouden apparaten (2FA "dit apparaat 30 dagen onthouden"): omschrijving links, vergeetknop
   rechts, en op een smal scherm gewoon onder elkaar. */
.portal-device-list {
    list-style: none;
    padding: 0;
    margin: 0 0 16px;
}

.portal-device-list li {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    padding: 10px 0;
    border-bottom: 1px solid var(--line);
}

.portal-device-list li:last-child { border-bottom: none; }
.portal-device-list .portal-note { display: block; margin-top: 2px; }

/* ============================================================================================
   11. Zelf een afspraak inplannen (Booking/Index)
   Dezelfde maandkalender met kleurcodering als de publieke boekingspagina — dezelfde kleur moet
   voor een klant hetzelfde betekenen, waar 'ie 'm ook ziet. Bewust puur CSS: de portaal-CSP staat
   geen inline styles toe, en een kalender die alleen met JavaScript werkt is een kalender die op
   een dwarse browser niet werkt.
   ============================================================================================ */

.booking-options fieldset {
    border: none;
    padding: 0;
    margin: 0 0 14px;
}

.booking-options legend {
    font-weight: 600;
    color: var(--text-bright);
    padding: 0;
    margin-bottom: 6px;
}

.booking-month-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 10px;
}

.booking-month-name { text-transform: capitalize; color: var(--text-bright); }

.booking-calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 6px;
    margin-bottom: 10px;
}

.booking-calendar-weekday {
    text-align: center;
    font-family: var(--mono);
    font-weight: 600;
    font-size: 0.72rem;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--muted);
    padding: 4px 0;
}

label.booking-day {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 1;
    border: 1px solid var(--line);
    border-radius: 8px;
    font-weight: 600;
    color: var(--text-bright);
    margin: 0;
    overflow: hidden;
}

/* De radio bedekt het hele vlak maar is onzichtbaar: de dag zelf is het klikdoel, en met een
   vinger is een kaal radiorondje niet te raken. */
.booking-day input[type="radio"] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: not-allowed;
}

.booking-day input[type="radio"]:not(:disabled) { cursor: pointer; }

/* :has(:checked) markeert de gekozen dag zonder een regel JavaScript. Toetsenbordbediening moet
   net zo zichtbaar zijn als de muis: de radio zelf is onzichtbaar, dus de focusring hoort op het
   vlak eromheen. */
.booking-day:has(input:checked),
.booking-day:has(input:focus-visible) {
    outline: 3px solid var(--brand);
    outline-offset: 2px;
}

.booking-day-number { pointer-events: none; position: relative; z-index: 1; }

.booking-day-full { background: var(--cal-open); color: var(--cal-open-text); border-color: var(--cal-open); }
.booking-day-none { background: var(--cal-full); color: var(--cal-full-text); border-color: var(--cal-full); }

/* Linksboven open = ochtend beschikbaar; rechtsonder open = middag. */
.booking-day-morning { background: linear-gradient(135deg, var(--cal-open) 50%, var(--cal-split) 50%); }
.booking-day-afternoon { background: linear-gradient(135deg, var(--cal-split) 50%, var(--cal-open) 50%); }

.booking-day-closed { background: var(--cal-closed); color: var(--cal-closed-text); border-color: var(--cal-closed); }
.booking-day-other-month { opacity: 0.35; }

.booking-legend {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
    font-size: 0.85rem;
    color: var(--muted);
}

.booking-legend-dot {
    display: inline-block;
    width: 12px;
    height: 12px;
    border-radius: 3px;
    margin-right: 4px;
    vertical-align: middle;
    border: 1px solid var(--line-strong);
}

/* Label dat alleen voor een schermlezer bestaat — zichtbaar staat de vraag al in de legend. */
.portal-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ============================================================================================
   12. Contract ondertekenen (Contracts/Sign)
   ============================================================================================ */

.signature-block { margin-top: 14px; }

/* De tekenmat blijft in béide thema's wit: contractsignature.js tekent met donkere inkt en die
   afbeelding gaat zo het ondertekende exemplaar in. Een handtekening op een donker vlak zou in de
   PDF onzichtbaar zijn.
   touch-action:none is niet optioneel: zonder dat scrollt de pagina mee terwijl je met een vinger
   probeert te tekenen en komt er geen streep op het canvas. */
.signature-pad {
    display: block;
    width: 100%;
    max-width: 600px;
    height: auto;
    aspect-ratio: 3 / 1;
    background: #FFFFFF;
    border: 1px dashed var(--line-strong);
    border-radius: var(--radius-sm);
    touch-action: none;
    cursor: crosshair;
}

/* ============================================================================================
   13. Werkrapport (WorkLogs) — de Markdown-weergave van een rapport
   ============================================================================================ */

/* Zelfde opmaak als de CRM's .markdown-preview, maar volledig in thematokens: admin.css kan met
   vaste lichte kleuren werken omdat de CRM één thema heeft, hier moet elke regel het in donker
   én licht doen. Voeg dus nooit een letterlijke kleur toe in dit blok. */

.markdown-preview > *:first-child { margin-top: 0; }
.markdown-preview > *:last-child { margin-bottom: 0; }

.markdown-preview h1,
.markdown-preview h2,
.markdown-preview h3,
.markdown-preview h4 {
    margin: 18px 0 8px;
    color: var(--text-bright);
}

/* Een regel van 150 tekens leest niemand. De paginakolom is breder geworden voor de kop en de
   overzichten; lópende tekst houdt een leesbare maat. Bewust alleen op alinea's en lijstregels, zodat
   een tabel of een diagram de volle breedte houdt. */
.markdown-preview p,
.markdown-preview li {
    max-width: 92ch;
}

.markdown-preview p { margin: 0 0 10px; }

.markdown-preview ul,
.markdown-preview ol {
    margin: 0 0 10px 20px;
}

.markdown-preview a { color: var(--brand-text); }

.markdown-preview blockquote {
    border-left: 3px solid var(--line-strong);
    margin: 10px 0;
    padding: 2px 14px;
    color: var(--muted);
}

.markdown-preview hr {
    border: none;
    border-top: 1px solid var(--line);
    margin: 16px 0;
}

/* Wiki.js-callouts: {.is-info}/{.is-success}/{.is-warning}/{.is-danger}. */
.markdown-preview blockquote.is-info,
.markdown-preview blockquote.is-success,
.markdown-preview blockquote.is-warning,
.markdown-preview blockquote.is-danger {
    color: inherit;
    border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
    padding: 8px 14px;
}

.markdown-preview blockquote.is-info { border-left-color: var(--dot-note); background: var(--note-bg); }
.markdown-preview blockquote.is-success { border-left-color: var(--ok-border); background: var(--ok-bg); }
.markdown-preview blockquote.is-warning { border-left-color: var(--warn-border); background: var(--warn-bg); }
.markdown-preview blockquote.is-danger { border-left-color: var(--bad-border); background: var(--bad-bg); }

/* Tabellen kunnen breder zijn dan de kaart; laat ze binnen hun eigen kader schuiven in plaats van
   de hele pagina horizontaal te laten scrollen. */
.markdown-preview table {
    border-collapse: collapse;
    margin: 12px 0;
    width: 100%;
    display: block;
    overflow-x: auto;
}

.markdown-preview th,
.markdown-preview td {
    border: 1px solid var(--line);
    padding: 6px 10px;
    text-align: left;
}

.markdown-preview thead th {
    background: var(--surface-2);
    color: var(--text-bright);
}

.markdown-preview table.dense th,
.markdown-preview table.dense td {
    padding: 2px 6px;
    font-size: 0.85em;
}

.markdown-preview code {
    background: var(--surface-2);
    padding: 1px 5px;
    border-radius: 4px;
    font-size: 0.9em;
}

.markdown-preview pre {
    background: var(--surface-input);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 10px 12px;
    overflow-x: auto;
}

.markdown-preview pre code {
    background: none;
    padding: 0;
}

.markdown-preview kbd {
    background: var(--surface-2);
    color: var(--text-bright);
    border: 1px solid var(--line-strong);
    border-radius: 4px;
    padding: 1px 6px;
    font-size: 0.85em;
    font-family: monospace;
}

.markdown-preview ul.contains-task-list {
    list-style: none;
    margin-left: 4px;
}

.markdown-preview ul.grid-list {
    list-style: none;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 10px;
    margin-left: 0;
}

.markdown-preview ul.grid-list li {
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 10px;
    text-align: center;
}

.markdown-preview ul.links-list {
    list-style: none;
    margin-left: 0;
}

.markdown-preview ul.links-list li {
    border-bottom: 1px solid var(--line);
    padding: 8px 0;
}

/* Mermaid/PlantUML: de dienst levert een plaatje met witte achtergrond, dus een eigen wit vlak met
   wat lucht eromheen leest beter dan een hard rechthoekig gat in een donkere kaart. */
.markdown-preview .markdown-diagram {
    max-width: 100%;
    display: block;
    margin: 12px 0;
    background: #FFFFFF;
    border-radius: var(--radius-sm);
    padding: 8px;
}

/* Tabwidget, client-side opgebouwd door markdownenhance.js. */
.markdown-preview .tabset-tabs {
    display: flex;
    gap: 2px;
    border-bottom: 2px solid var(--line);
    margin: 12px 0 0;
    flex-wrap: wrap;
}

.markdown-preview .tabset-tab {
    background: none;
    border: none;
    padding: 6px 14px;
    cursor: pointer;
    color: var(--text);
    border-bottom: 2px solid transparent;
    margin-bottom: -2px;
    font: inherit;
}

.markdown-preview .tabset-tab.is-active {
    border-bottom-color: var(--brand);
    color: var(--brand-text);
    font-weight: 600;
}

.markdown-preview .tabset-panel {
    display: none;
    padding: 12px 2px;
}

.markdown-preview .tabset-panel.is-active { display: block; }

/* Een werkrapport in een lijst of op de tijdlijn: opgemaakt, maar begrensd op hoogte met een
   uitvloeiende onderkant. Zo blijft een lange rapportage één regel in het overzicht en staat de
   volledige tekst op z'n eigen pagina — zonder de klant ruwe Markdown-tekens te tonen. */
.markdown-preview.is-compact {
    position: relative;
    max-height: 8.5em;
    overflow: hidden;
    margin-top: 6px;
}

.markdown-preview.is-compact::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    height: 2.2em;
    background: linear-gradient(to bottom, transparent, var(--surface));
    pointer-events: none;
}

/* Koppen mogen in een voorproefje niet als volwaardige koppen schreeuwen. */
.markdown-preview.is-compact h1,
.markdown-preview.is-compact h2,
.markdown-preview.is-compact h3,
.markdown-preview.is-compact h4 {
    font-size: 1em;
    margin: 0 0 4px;
}

/* ============================================================================================
   Realtime projectpagina (liveproject.js)
   Zelfde markup als in de CRM — het script is een byte-identieke kopie — maar met de tokens van
   dit thema, zodat alles in licht én donker klopt.
   ============================================================================================ */

.live-toast-stack {
    position: fixed;
    right: 18px;
    bottom: 18px;
    z-index: 1200;
    display: flex;
    flex-direction: column;
    gap: 10px;
    max-width: min(360px, calc(100vw - 36px));
}

.live-toast {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 8px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-left: 4px solid var(--accent);
    border-radius: var(--radius);
    box-shadow: var(--toast-shadow);
    padding: 12px 14px;
    /* Begint buiten beeld rechts; .is-visible schuift 'm binnen. */
    transform: translateX(calc(100% + 24px));
    opacity: 0;
    transition: transform .25s ease, opacity .25s ease;
}

.live-toast.is-visible {
    transform: translateX(0);
    opacity: 1;
}

.live-toast-error {
    border-left-color: var(--bad-border);
}

.live-toast-body {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1;
    min-width: 0;
    /* Ook als <button>: geen knopuiterlijk, wel een klikbaar vlak naar het juiste paneel. */
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    font: inherit;
    color: var(--text);
    text-align: left;
    cursor: pointer;
}

.live-toast-body strong {
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--accent);
}

.live-toast-error .live-toast-body strong {
    color: var(--bad-text);
}

.live-toast-text {
    /* Een lange regel mag de kaart niet oprekken. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.live-toast-hint {
    font-size: 0.78rem;
    color: var(--muted);
}

.live-toast-close {
    background: none;
    border: none;
    font-size: 1.2rem;
    line-height: 1;
    color: var(--muted);
    cursor: pointer;
    padding: 0 2px;
}

.live-toast-close:hover {
    color: var(--text-bright);
}

/* "Kijkt mee" onder de projecttitel: informatie, geen waarschuwing. */
.live-presence {
    display: flex;
    align-items: center;
    gap: 7px;
    margin: -4px 0 14px;
    font-size: 0.88rem;
    color: var(--muted);
}

.live-presence-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--dot-reached);
    box-shadow: 0 0 0 3px var(--accent-soft);
    flex: 0 0 auto;
}

/* Een paneel dat is bijgewerkt terwijl je er zelf in bezig was: niet onder je handen vandaan
   getrokken, maar wel gemeld — met een knop om het alsnog te doen. */
.live-stale-note {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    background: var(--warn-bg);
    border: 1px solid var(--warn-border);
    border-radius: var(--radius-sm);
    padding: 8px 12px;
    margin-bottom: 12px;
    font-size: 0.9rem;
    color: var(--warn-text);
}

/* Kort oplichten waar de melding over ging, na een klik op de banner. */
.is-live-highlight {
    animation: live-highlight 2s ease;
}

@keyframes live-highlight {
    0% { background-color: var(--accent-soft); }
    100% { background-color: transparent; }
}

form.is-live-busy {
    opacity: .75;
}

@media (prefers-reduced-motion: reduce) {
    .live-toast {
        transition: opacity .2s ease;
        transform: none;
    }

    .is-live-highlight {
        animation: none;
    }
}

/* ============================================================================================
   Chat met de consultant (chatwidget.js)
   Alleen tokens, geen letterlijke kleuren: hij moet in het donkere én het lichte thema kloppen.
   De uitzondering is het stoplichtje zelf — groen en rood betekenen in beide thema's hetzelfde.
   ============================================================================================ */

.chat-widget {
    position: fixed;
    right: 18px;
    bottom: 18px;
    z-index: 1100;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 10px;
    max-width: min(380px, calc(100vw - 36px));
}

.chat-launcher {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 10px 16px;
    background: var(--surface);
    color: var(--text-bright);
    border: 1px solid var(--line-strong);
    border-radius: 999px;
    box-shadow: var(--toast-shadow);
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}

.chat-launcher:hover {
    border-color: var(--accent);
}

/* Er staat iets ongelezen: dan mag de knop even om aandacht vragen. */
.chat-widget.has-news .chat-launcher {
    border-color: var(--accent);
    box-shadow: var(--toast-shadow), 0 0 0 3px var(--accent-soft);
}

.chat-dot {
    display: inline-block;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: var(--muted);
    flex: 0 0 auto;
}

/* Twee dragers voor dezelfde toestand: het widget in de hoek en de statusregel op de
   Gesprekken-pagina (die buiten het widget staat). */
.chat-widget.is-online .chat-dot,
.chat-status.is-online .chat-dot {
    background: #22C55E;
    box-shadow: 0 0 0 3px rgba(34, 197, 94, .2);
}

.chat-widget.is-offline .chat-dot,
.chat-status.is-offline .chat-dot {
    background: #EF4444;
    box-shadow: 0 0 0 3px rgba(239, 68, 68, .18);
}

/* Het rondje met onze initialen naast een inkomend bericht en in de kop van het paneel. Afgeleid van
   de bedrijfsnaam, dus geen tweede plek waar die naam staat. */
.chat-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: var(--brand);
    color: var(--on-brand);
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: .02em;
}

/* In de kop is de achtergrond zelf al brand-kleurig; daar moet het rondje omkeren. */
.chat-panel-header .chat-avatar {
    background: var(--on-brand);
    color: var(--brand);
    width: 34px;
    height: 34px;
    font-size: 0.78rem;
}

.chat-badge {
    display: inline-block;
    min-width: 18px;
    padding: 1px 6px;
    border-radius: 999px;
    background: var(--brand);
    color: var(--on-brand);
    font-size: 0.72rem;
    font-weight: 700;
    line-height: 1.5;
    text-align: center;
}

.chat-panel {
    display: flex;
    flex-direction: column;
    width: min(400px, calc(100vw - 36px));
    max-height: min(76vh, 620px);
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--toast-shadow);
    overflow: hidden;
    /* De marge van .portal-button hoort niet in een paneel dat zelf z'n ruimte bepaalt. */
    margin: 0;
}

/* Een gekleurde kop met wie je aan de lijn hebt: dat maakt in één blik duidelijk dat dit een gesprek
   met een mens is en niet zomaar een formulier in de hoek. */
.chat-panel-header {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    background: var(--brand);
    color: var(--on-brand);
}

.chat-panel-titles {
    display: flex;
    flex-direction: column;
    gap: 1px;
    min-width: 0;
    flex: 1;
}

.chat-panel-title {
    font-size: 0.98rem;
    line-height: 1.25;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.chat-status {
    display: flex;
    align-items: center;
    gap: 7px;
    font-size: 0.82rem;
    font-weight: 600;
}

/* In de kop erft de statusregel de donkere tekstkleur van de balk; op de Gesprekken-pagina staat hij
   op een gewone achtergrond en hoort hij de normale tekstkleur te hebben. */
.chat-panel-header .chat-status {
    color: inherit;
}

.chat-panel-link,
.chat-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    background: none;
    border: none;
    color: inherit;
    opacity: .75;
    cursor: pointer;
    padding: 2px;
    line-height: 1;
}

.chat-close {
    font-size: 1.4rem;
}

.chat-panel-link:hover,
.chat-close:hover {
    opacity: 1;
}

/* Waarom je nu geen antwoord hoeft te verwachten, of waarom versturen niet lukte. */
.chat-panel-note {
    margin: 0;
    padding: 8px 14px;
    background: var(--warn-bg);
    border-bottom: 1px solid var(--warn-border);
    color: var(--warn-text);
    font-size: 0.85rem;
}

.chat-panel-link {
    text-decoration: none;
}

/* De draad staat op een eigen, iets afwijkende ondergrond zodat de bericht-bolletjes er los op
   liggen — hetzelfde idee als in een mailprogramma. */
.chat-log {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 14px;
    overflow-y: auto;
    flex: 1;
    background: var(--bg-alt);
}

.chat-empty {
    margin: 0;
    color: var(--muted);
    font-size: 0.88rem;
}

/* Een regel is het rondje plus het bolletje met de tekst; eigen berichten hebben geen rondje. */
.chat-line {
    display: flex;
    align-items: flex-end;
    gap: 8px;
    max-width: 100%;
}

.chat-line.is-mine {
    justify-content: flex-end;
}

.chat-bubble {
    display: flex;
    flex-direction: column;
    gap: 2px;
    max-width: 84%;
    padding: 9px 12px;
    border-radius: 14px;
    background: var(--surface);
    box-shadow: 0 1px 2px rgba(0, 0, 0, .12);
}

.chat-line.is-theirs .chat-bubble {
    border-bottom-left-radius: 4px;
}

.chat-line.is-mine .chat-bubble {
    background: var(--surface-2);
    border: 1px solid var(--line-strong);
    border-bottom-right-radius: 4px;
}

.chat-line-meta {
    font-size: 0.72rem;
    color: var(--muted);
}

.chat-line-context {
    font-size: 0.72rem;
    color: var(--accent);
}

.chat-line-body {
    /* Regelafbrekingen van de afzender blijven staan; het is platte tekst. */
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* Ruim tekstvak, kleine knop: hier past een verhaal van een paar regels in. Ctrl+Enter verstuurt,
   dus Enter mag gewoon een nieuwe regel zijn. */
.chat-compose {
    display: flex;
    align-items: flex-end;
    gap: 8px;
    padding: 12px 14px 6px;
    border-top: 1px solid var(--line);
}

.chat-compose textarea {
    flex: 1;
    min-height: 84px;
    max-height: 40vh;
    resize: vertical;
    margin: 0;
}

.chat-send {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: var(--brand);
    color: var(--on-brand);
    cursor: pointer;
}

.chat-send:hover:not(:disabled) {
    background: var(--brand-hover);
}

.chat-send:disabled {
    opacity: .5;
    cursor: default;
}

.chat-hint {
    margin: 0;
    padding: 0 14px 12px;
    font-size: 0.74rem;
    color: var(--muted);
}

.chat-hint kbd {
    padding: 1px 5px;
    border: 1px solid var(--line-strong);
    border-radius: 4px;
    background: var(--surface-2);
    font-family: inherit;
    font-size: 0.72rem;
}

/* ---------------------------------------------------------------- pagina 'Gesprekken'
   Eén doorlopende draad, per dag gegroepeerd. Dezelfde bericht-opbouw als in het widget: de bolletjes
   hierboven zijn niet per pagina anders. */
.chat-thread-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

.chat-thread-head .portal-button {
    /* .portal-button brengt een marge mee die bedoeld is voor een formulier onder elkaar. */
    margin-top: 0;
}

.chat-thread {
    display: flex;
    flex-direction: column;
    gap: 12px;
    padding: 16px;
    border-radius: var(--radius);
    background: var(--bg-alt);
    border: 1px solid var(--line);
    overflow-y: auto;
}

/* De gesprekkenpagina vult het venster: de draad scrolt vanbinnen, de kop en de statusregel blijven
   staan. Bij een lang gesprek zou je anders door de hele pagina moeten scrollen om bij het laatste
   bericht te komen. body.is-chat-page komt uit ViewData["BodyClass"] (zie _Layout). */
body.is-chat-page {
    height: 100dvh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

body.is-chat-page .portal-main {
    flex: 1;
    /* min-height:0 is wat een flex-kind laat krimpen in plaats van z'n inhoud te laten overlopen. */
    min-height: 0;
    display: flex;
    flex-direction: column;
    width: 100%;
    padding-bottom: 24px;
}

.chat-screen {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
}

.chat-screen .chat-thread {
    flex: 1;
    min-height: 0;
}

/* Doorpraten onder de draad, net als in de CRM. Vaste hoogte binnen het venstervullende scherm: de
   draad krimpt, het tekstvak blijft staan. */
.chat-page-reply {
    flex: none;
    margin: 12px 0 0;
}

.chat-page-reply .chat-compose {
    padding: 0;
    border-top: none;
}

.chat-page-reply .chat-hint {
    padding: 6px 0 0;
}

/* Op de gesprekkenpagina is de knop in de hoek overbodig — je zit al in het gesprek, en hij zou over
   het tekstvak heen vallen. Het widget zelf blijft bestaan (de verbinding en de meldingen lopen erlangs). */
body.is-chat-page .chat-launcher {
    display: none;
}

/* Op een laag venster gaat er anders te veel naar de kop; het gesprek is waar je voor komt. Alleen op
   een echt scherm — op een telefoon scrolt de pagina toch al. */
@media (min-width: 769px) and (max-height: 850px) {
    body.is-chat-page .portal-main {
        padding-top: 14px;
        padding-bottom: 14px;
    }

    body.is-chat-page h1 {
        font-size: 1.5rem;
        margin: 0 0 4px;
    }

    /* Alles wat niet het gesprek is, wordt hier krapper: de kaartrand, de marge boven het tekstvak en
       het tekstvak zelf. Bij een venster van 650px bleef er anders ruim honderd pixels gesprek over. */
    body.is-chat-page .portal-card {
        padding: 12px;
        margin-bottom: 0;
    }

    .chat-page-reply {
        margin-top: 8px;
    }

    .chat-page-reply .chat-compose textarea {
        height: 62px;
        min-height: 0;
    }
}

.chat-day {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

/* Waar het ongelezen deel begon — dezelfde vorm als in de CRM. Alleen tokens, dus hij klopt in beide
   thema's; verdwijnt zodra je er voorbij scrolt (chatwidget.js). */
.chat-unread-line {
    display: flex;
    align-items: center;
    gap: 10px;
    color: var(--brand-text);
    font-size: 0.74rem;
    font-weight: 700;
    letter-spacing: .02em;
    text-transform: uppercase;
}

.chat-unread-line::before,
.chat-unread-line::after {
    content: "";
    flex: 1;
    height: 1px;
    background: var(--line-strong);
}

.chat-day-label {
    margin: 6px 0 0;
    text-align: center;
    font-size: 0.76rem;
    font-weight: 600;
    color: var(--muted);
}

@media (max-width: 768px) {
    .chat-widget {
        right: 12px;
        bottom: 12px;
        max-width: calc(100vw - 24px);
    }

    .chat-panel {
        width: calc(100vw - 24px);
        max-height: 76vh;
    }

    .chat-compose textarea {
        min-height: 64px;
    }

    /* Op een telefoon is de pagina zelf scrollen juist normaal; de draad houdt wel een maximum. */
    body.is-chat-page {
        height: auto;
        overflow: visible;
    }

    .chat-screen .chat-thread {
        flex: none;
        max-height: 65dvh;
    }
}


/* ============================================================================================
   Offertes (Quotes/Sign) — de prijsopgave-tabel. Het tekenvlak hergebruikt .signature-pad
   hierboven; de tabelkleuren zijn tokens, dus dit klopt in beide thema's.
   ============================================================================================ */

.quote-table-scroll { overflow-x: auto; }

.quote-lines-table {
    width: 100%;
    border-collapse: collapse;
}

.quote-lines-table th,
.quote-lines-table td {
    text-align: left;
    padding: 8px 12px;
    border-bottom: 1px solid var(--line);
}

.quote-lines-table th:nth-child(n+2),
.quote-lines-table td:nth-child(n+2) { text-align: right; white-space: nowrap; }

.quote-lines-table thead th { color: var(--text-bright); border-bottom: 1px solid var(--line-strong); }

.quote-lines-table tfoot td { border-bottom: none; color: var(--muted); }
.quote-lines-table tfoot .quote-total-row td { color: var(--text-bright); font-weight: 700; }
