/*
 * De Websignaal-laag: vorm, typografie en dichtheid.
 *
 * Kleuren staan in variables.css, dit bestand gebruikt ze. Samen volgen ze
 * DESIGN.md in de projectroot; staat hier iets wat daar niet staat, dan is dit
 * bestand fout en niet dat.
 *
 * Alle maten komen van websignaal.nl, gemeten in de browser met
 * getComputedStyle op 31 oktober 2026, niet geschat. De waarden staan bij de
 * regel waar ze horen.
 *
 * DE REGEL DIE ALLES DRAAGT: één accent, verder monochroom, en diepte uit een
 * haarlijn van één pixel in plaats van uit een kleurvlak of een schaduw.
 *
 * Dit bestand hangt aan het blok stylesheets_last in base.html.twig, ná alle
 * paginastijlen. Die ene link weghalen zet het CRM terug op de standaardvorm
 * van het thema, zonder dat er een template verandert.
 */

/* ================================================================ 1. Vlakken
   De site zet zijn hele pagina op #eef8fc en houdt wit voor de inhoud. */
body,
.view-page,
.view-content {
    background: var(--ws-primary-bg);
}

.content-section,
.kpi-block,
.card {
    background: var(--ws-canvas);
}

/* ================================================================ 2. Haarlijnen
   Het ontwerp kent geen schaduw: een vlak wordt begrensd door een lijn van één
   pixel. Dat is rustiger op een scherm met twintig kaarten dan twintig keer een
   zachte slagschaduw. Ronding en lijnkleur van de rest van het thema staan in
   variables.css als --bs-variabelen. */
.content-section,
.kpi-block,
.card {
    border: 1px solid var(--ws-hair);
    border-radius: var(--ws-radius-card);
    box-shadow: none;
}

.card .card,
.card-body > .card {
    border: 0;
}

.section-header,
.card-header {
    background: var(--ws-canvas);
    border-bottom: 1px solid var(--ws-hair);
    padding: .75rem 1.25rem;
}

/* ================================================================ 3. Typografie
   Gemeten: kop Archivo 900 op -0,045em in #000000, lopende tekst Inter 400 met
   regelhoogte 1,65 in #545c61, label IBM Plex Mono 500 op 11 px met 0,18em.

   De familie staat expliciet op body en niet alleen als --bs-variabele: in
   assets/styles/app.scss staat `body { font-family: Arial, sans-serif }`, een
   directe regel die van elke variabele wint. Het hele CRM stond daardoor in
   Arial en Inter werd nooit opgehaald. */
body {
    font-family: var(--ws-font-body);
    font-weight: 400;
    font-size: .9375rem;
    line-height: 1.6;
    color: var(--ws-article);
}

p {
    color: var(--ws-body);
    line-height: 1.65;
}

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
    font-family: var(--ws-font-head);
    font-weight: 700;
    letter-spacing: var(--ws-head-tracking);
    color: var(--ws-ink);
}

/* De paginatitel is de enige kop op displaygewicht, zoals de hero op de site.
   De koppen in een kaart blijven 700, anders schreeuwen tien kaarten tegelijk. */
/* De kop van de hero, geschaald. Gemeten op websignaal.nl: Archivo 900 op
   52 px, regelhoogte 0,98, tracking -0,045em, puur zwart. De grootte is hier
   30 px in plaats van 52, want een CRM-kop staat naast knoppen en niet in een
   hero; de regelhoogte, het gewicht en de tracking zijn wel die van de site, en
   dat is wat de kop zijn karakter geeft. */
.ws-page-header h1,
.view-header h1 {
    font-weight: var(--ws-title-weight);
    font-size: 1.875rem;
    line-height: .98;
    letter-spacing: var(--ws-title-tracking);
    color: var(--ws-ink);
}

/* Alles wat label is en geen inhoud, met het blokje uit het logo ervoor. Dat
   bolletje is 7 px en de enige plek waar de 55%-vorm nog gebruikt wordt. */
.section-title,
.kpi-block-label,
.card-title,
.card-header h4,
.card-header h5,
.card-header h6 {
    font-family: var(--ws-label-font);
    font-weight: var(--ws-label-weight);
    font-size: .6875rem;
    letter-spacing: var(--ws-label-tracking);
    text-transform: uppercase;
    color: var(--ws-body);
    margin-bottom: 0;
}

.section-title::before,
.card-title::before,
.card-header h4::before,
.card-header h5::before,
.card-header h6::before,
.ws-mark-dot::before {
    content: "";
    display: inline-block;
    width: .4375rem;
    height: .4375rem;
    margin-right: .5rem;
    vertical-align: baseline;
    background: var(--ws-accent);
    border-radius: var(--ws-mark-radius);
}

/* ================================================================ 4. Het blokje
   Op kleine UI-elementen is de logovorm geen 55% maar 14px 4px: linksboven en
   rechtsonder rond, de andere twee bijna vierkant. Gemeten op .ws-pill ("Voor
   Bureaus"). Zo blijft het een merkteken in plaats van een blaadje. */
.contact-icon,
.btn-icon,
.ws-user-tag,
.ws-mark {
    border-radius: 14px 4px;
}

/* ================================================================ 5. Knoppen
   Eén op één de knop uit de navigatie van de site, uit .ws-header__cta in
   theme.css: IBM Plex Mono 500 op 12,5 px met 0,1em letterafstand, padding
   0 18, minimale hoogte 46, ronding 10. De gevulde variant is zwart met witte
   tekst en krijgt het accent bij hover; die staat in base.html.twig. */
.ws-btn,
.btn {
    font-family: var(--ws-btn-font);
    font-weight: var(--ws-btn-weight);
    font-size: var(--ws-btn-size);
    letter-spacing: var(--ws-btn-tracking);
    text-transform: var(--ws-btn-transform);
    border-radius: var(--ws-radius-control);
}

.ws-btn {
    display: inline-flex;
    align-items: center;
    min-height: 46px;
    padding: 0 18px;
}

/* Alleen de rustige knop, die zonder variant. Zonder die uitsluiting won deze
   regel van .ws-btn--primary:hover en .ws-btn--edit:hover in base.html.twig:
   gelijke specificiteit, en dit bestand laadt later. De zwarte knop werd daardoor
   wit met een blauwe rand in plaats van blauw. */
.ws-btn:not([class*="ws-btn--"]):hover {
    background: var(--ws-primary-bg);
    border-color: var(--ws-primary);
    color: var(--ws-ink);
}

/* Een knop die alleen een icoon is heeft niets aan letterafstand. */
.ws-view,
.ws-edit,
.ws-delete,
.btn-icon,
.btn-close {
    letter-spacing: normal;
    text-transform: none;
}

/* De pijl terug is navigatie, geen actie: rustig, met het accent bij hover. */
/* De pijl en zijn vervanger zijn even breed, en het gat ernaast is op beide
   koptypes 1rem. Zo begint elke paginatitel op x=88, met of zonder pijl.
   Gemeten op een detailpagina: pijl op 32, breed 40, gat 16. */
/* De terugweg: een kleine regel boven de titel in plaats van een pijl ernaast.
   In dezelfde mono kapitalen als de eyebrow, zodat hij bij de kop hoort zonder
   ermee te concurreren. */
.ws-back {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
    margin-bottom: .25rem;
    background: transparent;
    border: 0;
    padding: 0;
    font-family: var(--ws-label-font);
    font-weight: var(--ws-label-weight);
    font-size: .6875rem;
    letter-spacing: var(--ws-label-tracking);
    text-transform: uppercase;
    line-height: 1.4;
    color: var(--ws-muted);
    text-decoration: none;
}

.ws-back i {
    font-size: 1rem;
    line-height: 1;
    /* De pijl schuift mee bij hover, zoals de links op websignaal.nl. */
    transition: transform .15s ease;
}

.ws-back:hover i {
    transform: translateX(-3px);
}

.ws-page-header__row > div:first-child {
    gap: 1rem;
}

.ws-back:hover {
    background: transparent;
    color: var(--ws-primary);
}

/* ================================================================ 6. De bovenbalk
   Wit, zoals de navigatie op de site. Zwart is daar alleen de footer en de knop
   "plan een gesprek"; een zwarte balk bovenin staat er nergens.

   Met de id erbij, want app.min.css zet de achtergrond op #page-topbar en een
   id wint van een klasse. De klasse bg-white moest uit de markup: die staat in
   bootstrap.min.css én in de gebouwde app-CSS met !important. */
#page-topbar.ws-topbar {
    background: var(--ws-canvas);
    border-bottom: 1px solid var(--ws-hair);
}

.ws-topbar a,
.ws-topbar button {
    color: var(--ws-ink);
}

.ws-topbar a:hover,
.ws-topbar button:hover {
    background-color: var(--ws-primary-bg);
    color: var(--ws-primary);
}

/* Wat uit de balk klapt is een paneel op de pagina en geen onderdeel van de
   balk. Hogere specificiteit dan de regels hierboven, dus deze winnen. */
.ws-topbar .absolute a,
.ws-topbar .absolute button,
.ws-topbar .bg-white a,
.ws-topbar .bg-white button {
    color: var(--ws-article);
    background-color: transparent;
}

.ws-topbar .absolute a:hover,
.ws-topbar .absolute button:hover,
.ws-topbar .bg-white a:hover,
.ws-topbar .bg-white button:hover {
    color: var(--ws-ink);
    background-color: var(--ws-primary-bg);
}

/* ================================================================ 7. Tabbladen
   Geen vulling: een gevulde tab vecht met de inhoud eronder. Een rand van 2 px
   in het accent zegt hetzelfde en laat het scherm heel. */
.view-tab,
.acct-tab {
    font-family: var(--ws-font-body);
    font-weight: 500;
    font-size: .8125rem;
    letter-spacing: normal;
    text-transform: none;
    color: var(--ws-body);
}

.view-tab.view-tab--active,
.view-tab.active,
.acct-tab.acct-tab--active {
    background-color: transparent;
    color: var(--ws-ink);
}

.view-tab::after,
.acct-tab::after {
    background: var(--ws-accent);
}

/* Binnen een kaart is er geen onderliggende rand om een streep aan op te
   hangen, dus daar is het wél een strook met een witte pil. */
.view-subtabs {
    background: var(--ws-surface-alt);
    border-radius: var(--ws-radius-control);
}

.view-subtab {
    font-family: var(--ws-font-body);
    font-weight: 500;
    font-size: .8125rem;
    letter-spacing: normal;
    text-transform: none;
    border-radius: var(--ws-radius-badge);
    color: var(--ws-body);
}

.view-subtab.view-subtab--active,
.view-subtab.active {
    background: var(--ws-canvas);
    color: var(--ws-ink);
    box-shadow: rgba(28, 40, 64, .08) 0 1px 2px 0;
}

/* ================================================================ 8. Tabellen */
.content-section .table th,
.table th {
    font-family: var(--ws-font-body);
    font-weight: 500;
    font-size: .6875rem;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--ws-body);
    background: var(--ws-surface-alt);
    border-bottom: 1px solid var(--ws-hair);
}

.content-section .table td,
.table td {
    border-bottom-color: var(--ws-hair);
}

.content-section .table tbody tr:hover,
.table tbody tr:hover {
    background: var(--ws-surface-alt);
}

/* De naam in een rij is op de site een echte kop (Archivo 800). In het CRM is
   dat een link met font-medium, de Tailwind-klasse, en geen <strong>. */
.table td a.font-medium,
.table td .font-semibold,
.table td strong,
.table td b {
    font-family: var(--ws-font-head);
    font-weight: 600;
    letter-spacing: -.015em;
    color: var(--ws-ink);
}

.table td a.font-medium:hover {
    color: var(--ws-primary);
}

/* ================================================================ 9. Kerngetallen */
.kpi-block-value {
    font-family: var(--ws-font-head);
    font-weight: 700;
    font-size: 2rem;
    line-height: 1.1;
    letter-spacing: -.02em;
    color: var(--ws-ink);
}

/* Een waarde die eigenlijk tekst is hoort niet in de displayvorm, anders
   schreeuwt een leeg veld het hardst. */
.kpi-block-value--tekst,
.kpi-block-value--unknown {
    font-family: var(--ws-font-body);
    font-weight: 500;
    font-size: 1rem;
    letter-spacing: normal;
}

.kpi-block-label {
    font-size: .625rem;
}

/* ================================================================ 10. Badges
   De pil van de site, gemeten op "Voor Bureaus": Archivo 700 op 14,5 px met
   -0,015em, vlak #eef8fc, rand #bfe4f5, tekst #1d76a0, ronding 14px 4px.

   .status-badge staat 39 keer in de templates, .badge 291 keer en rounded-full
   180 keer; de statuskolom van elke lijst gebruikt die laatste. Vandaar alle
   drie. Op `span` met `text-xs`, zodat een schakelaar met dezelfde
   Tailwind-klasse er niet in meegaat. */
.status-badge,
.badge,
span.rounded-full.text-xs {
    font-family: var(--ws-font-head);
    font-weight: 700;
    font-size: .75rem;
    letter-spacing: -.015em;
    text-transform: none;
    border-radius: 11px 3px;
}

.status-badge {
    display: inline-flex;
    align-items: center;
    padding: .25rem .6875rem;
    background: var(--ws-primary-bg);
    color: var(--ws-primary);
    border: 1px solid var(--ws-primary-line);
}

/* De badges die wél iets betekenen houden hun eigen kleur en krijgen hun rand
   uit diezelfde kleur, zodat de vorm gelijk blijft maar de betekenis niet. */
.status-badge.status-badge--muted,
.status-badge.status-badge--green,
.status-badge.status-badge--amber,
.status-badge.status-badge--red {
    border-color: color-mix(in srgb, currentColor 28%, transparent);
}

/* ================================================================ 11. Links
   Het accent in zijn donkere trap, want het accent als tekst op wit haalt 2,8 staat
   tot 1. Alleen in de inhoud: een menu-item is bediening, geen link in tekst. */
.section-header-link,
.section-body a:not(.btn):not(.ws-btn),
.content-section .table a:not(.btn):not(.ws-btn) {
    color: var(--ws-primary);
}

:focus-visible {
    outline: 2px solid var(--ws-accent);
    outline-offset: 2px;
}

/* ================================================================ 12. De paginakop
   components/_page_header.html.twig draagt 83 schermen; die erven niet van
   view_base en hadden dus geen kopband.

   De band is een pseudo-element dat links en rechts ruim buiten het kader
   steekt, niet een negatieve marge: die zou moeten weten hoeveel padding de
   container heeft, en dat verschilt onder en boven 1200 px. */
.ws-page-header {
    position: relative;
    padding-bottom: 1.25rem;
    margin-bottom: 1.5rem;
}

.ws-page-header::before {
    content: "";
    position: absolute;
    top: -2rem;
    bottom: 0;
    left: -50vw;
    right: -50vw;
    background: var(--ws-primary-bg);
    border-bottom: 1px solid var(--ws-primary-line);
    z-index: -1;
}

/* Het blokje achter de titel. Op de site is dat geen CSS-ronding maar het
   merkteken als SVG op 14% dekking, 400 bij 414 in een hero van 547
   (ws-hero__text::before). Een border-radius benadert die curve wel, maar niet
   precies, en de tint kwam lichter uit.

   Hij loopt vrij door en wordt niet afgeknipt op de hoogte van de kop. Dat is
   op 31 okt 2026 één keer geprobeerd, omdat de ideeënpagina toen haar tabbalk
   kaal had en de vorm daar overheen liep. Maar een kop is meestal veel korter
   dan 414 px, dus overal elders brak de vorm halverwege af met een harde
   streep. Afdekken is de goede route, niet afknippen: alles wat eronder komt
   heeft een eigen achtergrond en een z-index (zie .view-tabs-bar hieronder). */
.ws-page-header__shape {
    position: absolute;
    top: -4px;
    left: -10px;
    width: 400px;
    height: 414px;
    background: var(--ws-page-shape);
    border-radius: var(--ws-page-shape-radius);
    opacity: .14;
    z-index: -1;
    pointer-events: none;
}

/* De uitleg onder de kop, zoals de intro onder de hero op de site: Inter in
   de rustige tekstkleur, met een leesbreedte. Niet in de eyebrow, want die zin
   kan twee regels lang zijn en mono kapitalen zijn dan onleesbaar. */
/* De intro onder de hero: Inter 400 op 18 px met regelhoogte 1,65 in #545c61,
   en een leesbreedte. Hier 16 px, meeschalend met de kop. Die ruime
   regelafstand is wat de tekst op de site lucht geeft; dat is belangrijker dan
   de grootte en blijft dus staan. */
.ws-page-header__intro,
.view-header .text-sm.text-muted {
    margin: .5rem 0 0;
    max-width: 68ch;
    font-family: var(--ws-font-body);
    font-weight: 400;
    font-size: 1rem;
    line-height: 1.65;
    color: var(--ws-body);
}

/* De rij feiten onder de kop. Zelfde chips als in een tabel, zodat een status
   er overal hetzelfde uitziet. */
.ws-page-header__badges {
    display: flex;
    flex-wrap: wrap;
    gap: .375rem;
    margin-top: .625rem;
}

/* De eyebrow: klein accentblokje met mono kapitalen, boven de titel. Alleen
   voor een paar woorden, zoals "OVER WEBSIGNAAL" op de site. */
.ws-page-header .ws-eyebrow {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: .375rem;
    font-family: var(--ws-label-font);
    font-weight: var(--ws-label-weight);
    font-size: .6875rem;
    letter-spacing: var(--ws-label-tracking);
    text-transform: uppercase;
    color: var(--ws-primary);
}

.ws-page-header .ws-eyebrow::before {
    content: "";
    width: .4375rem;
    height: .4375rem;
    flex: none;
    background: var(--ws-accent);
    border-radius: 55% 0 55% 0;
}

/* ================================================================ 13. De detailkop
   Detailpagina's hebben hun kop in view_base: kop, kerngetallen en inhoud als
   drie banden onder elkaar.

   De vorm hangt aan .view-header en niet aan .view-page, want die laatste is
   een blok dat elke pagina mag overschrijven (account/show zet er account-page
   neer). En het is ::after met z-index 0 en niet ::before met -1: een kind met
   een negatieve z-index valt achter de achtergrond van zijn eigen ouder, en de
   kop heeft er een. Zo ligt de vorm boven die achtergrond en onder de inhoud,
   en loopt hij door achter de kerngetallen omdat die band geen eigen vulling
   heeft. */
.view-header {
    position: relative;
    background: var(--ws-primary-bg);
    border-bottom: 0;
}

.view-header::after {
    content: "";
    position: absolute;
    top: 2rem;
    left: calc(2rem - 10px);
    width: 400px;
    height: 414px;
    background: var(--ws-page-shape);
    border-radius: var(--ws-page-shape-radius);
    opacity: .14;
    z-index: 0;
    pointer-events: none;
}

.view-header > * {
    position: relative;
    z-index: 1;
}

/* De kerngetallen horen bij de kop: samen één band van de bovenbalk tot aan de
   tabbalk. Geen eigen vulling, zodat de vorm eronder doorloopt. */
.view-kpis {
    position: relative;
    z-index: 1;
    background: transparent;
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--ws-primary-line);
}

/* De tabbalk en de inhoud moeten de vorm juist wél afdekken. Zonder een eigen
   z-index schildert de achtergrondlaag van de kop over de achtergrond van deze
   gewone blokken heen, en loopt de vorm door tot halverwege het scherm. Hij
   hoort te stoppen waar de kop ophoudt. */
.view-tabs-bar {
    position: relative;
    z-index: 2;
    background: var(--ws-canvas);
    border-bottom: 1px solid var(--ws-hair);
}

.view-content {
    position: relative;
    z-index: 2;
}

/* ================================================================ 14. Tailwind
   rounded-lg staat op 8 px, precies de verboden standaardronding; hier wordt
   het de 12 uit het ontwerp. */
.rounded-lg {
    border-radius: var(--ws-radius-card);
}

.rounded-md {
    border-radius: var(--ws-radius-control);
}

/* Een gevulde knop in het accent draagt geen witte tekst. */
.bg-primary-400,
.bg-primary-300 {
    color: var(--ws-ink);
}

/* ================================================================ 15. De typemachine
   De kop die zichzelf schrijft, overgenomen van websignaal.nl (theme.js, het
   blok "typemachine", en de .ws-type-regels in theme.css). 38 ms per teken.

   De volledige kop staat onzichtbaar achter de getypte laag als maatvoering:
   zonder dat verspringt de hoogte van de band terwijl er getypt wordt, en
   schuift de hele pagina mee. */
.ws-type {
    position: relative;
    display: block;
}

.ws-type__ghost {
    visibility: hidden;
}

/* De cursor staat in de getypte laag en telt daar mee in de breedte, maar niet
   in de onzichtbare kopie die de maat bepaalt. Bij een titel die net past
   scheelt dat 0,14em, en brak het laatste woord af terwijl de kopie eronder op
   één regel stond. De kopie reserveert die ruimte nu ook. */
.ws-type__ghost::after {
    content: "";
    display: inline-block;
    width: .14em;
}

.ws-type__live {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
}

/* Het gemarkeerde deel is op de site cursief in het accent, een halve tint
   donkerder dan het accent zelf. Gemeten: #2390c3, italic, -0,03em. */
.ws-type__accent {
    font-style: italic;
    letter-spacing: -.03em;
    color: var(--ws-accent-h);
}

.ws-type__caret {
    display: inline-block;
    width: .08em;
    height: .82em;
    margin-left: .06em;
    vertical-align: baseline;
    background: var(--ws-accent);
    animation: ws-caret 1s steps(1) infinite;
}

.ws-type.is-done .ws-type__caret {
    display: none;
}

@keyframes ws-caret {
    0%, 50% { opacity: 1; }
    50.01%, 100% { opacity: 0; }
}

/* Wie beweging heeft uitgezet krijgt de kop in één keer; het script slaat het
   typen dan ook over, dit is het vangnet voor de cursor. */
@media (prefers-reduced-motion: reduce) {
    .ws-type__caret {
        display: none;
    }
}

/* ================================================================ 16. Knoppen met losse klassen
   Niet elke knop in het CRM is een .ws-btn. Op 95 plekken staat er in plaats
   daarvan "bg-primary-600 hover:bg-primary-700 text-white" met Tailwind-klassen,
   bijvoorbeeld Nieuwe planning op het capaciteitsscherm. Die kregen daardoor het
   donkerblauw van de primary-schaal en niet de zwarte knop van de site.

   Alleen op een <a> of <button>, want dezelfde klasse staat ook op balkjes en
   badges die wél gewoon blauw horen te zijn. Een element met een tag ervoor is
   specifieker dan de Tailwind-regel, dus hier is geen !important nodig. */
a.bg-primary-600,
button.bg-primary-600 {
    background-color: var(--ws-ink);
    color: #fff;
    font-family: var(--ws-btn-font);
    font-weight: var(--ws-btn-weight);
    font-size: var(--ws-btn-size);
    letter-spacing: var(--ws-btn-tracking);
    text-transform: var(--ws-btn-transform);
    border-radius: var(--ws-radius-control);
}

a.bg-primary-600:hover,
button.bg-primary-600:hover {
    background-color: var(--ws-primary);
    color: #fff;
}

/* ================================================================ 17. Rij-acties
   Bekijken, bewerken en verwijderen dragen hun kleur meteen, niet pas bij
   hover. Grijs tot je erop staat betekent dat je op een volle lijst moet zoeken
   welke van de drie de rode is, en bij verwijderen wil je dat juist zien
   voordat je klikt.

   De iconen zijn de gevulde Font Awesome-set die het CRM al gebruikte
   (fas fa-eye, fas fa-pen, fas fa-trash); de outline-varianten van Material
   Design Icons waren de uitzondering. */
.ws-view {
    color: var(--ws-body);
}

.ws-view:hover {
    color: var(--ws-ink);
    background: var(--ws-primary-bg);
}

.ws-edit {
    color: var(--ws-primary);
}

.ws-edit:hover {
    color: var(--ws-primary-h);
    background: var(--ws-primary-bg);
}

.ws-delete {
    color: var(--ws-danger);
}

.ws-delete:hover {
    color: var(--ws-danger-hover);
    background: var(--ws-primary-bg);
}

/* ================================================================ 18. Kerngetallen op een gevuld accentvlak
   Gevuld met --ws-primary en witte cijfers. Het label eronder in --ws-on-primary, het
   token dat de site zelf gebruikt voor tekst op een gevuld accentvlak.

   Positief en negatief houden hun betekenis, maar in een tint die op dat vlak leest:
   groen en rood uit de statuskleuren zijn daar te donker voor. */
.kpi-block {
    background: var(--ws-primary);
    border-color: var(--ws-primary);
}

.kpi-block-value {
    color: #fff;
}

.kpi-block-label,
.kpi-block-sub {
    color: var(--ws-on-primary);
}

.kpi-block-value--tekst,
.kpi-block-value--licht {
    color: #fff;
}

.kpi-block-value.kpi-block-value--unknown {
    color: rgba(255, 255, 255, .6);
}

.kpi-block-value.kpi-block-value--positive {
    color: #a7f3d0;
}

.kpi-block-value.kpi-block-value--negative {
    color: #fecdd3;
}

/* Een link of chip binnen een kerngetal moet ook op dat vlak leesbaar blijven. */
.kpi-block a,
.kpi-block .ws-user-tag {
    color: #fff;
}

/* De tegel met een grafiek blijft wit: die heeft zijn eigen kleuren en lijnen,
   en die leggen het af tegen een gevuld accentvlak. */
.kpi-block.kpi-block--chart {
    background: var(--ws-canvas);
    border-color: var(--ws-hair);
}

/* ================================================================ 19. Het icoon bij de titel
   Elk scherm toont het icoon van zijn menu-item, zodat je aan de kop ziet in
   welk deel van het CRM je zit. Overgenomen van de afspraakpagina, die het als
   enige al had, inclusief de ronde vorm. */
.ws-page-icon,
.meeting-icon,
.contact-avatar,
.time-icon,
.kind-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 44px;
    height: 44px;
    /* Uitlijnen met de titelregel en niet met het hele blok: staat er een
       omschrijving onder de titel, dan zakt een gecentreerd icoon mee naar het
       midden daarvan en hangt het los van de kop waar het bij hoort. De marge
       is het verschil tussen de hoogte van de titelregel (40, zie sectie 20) en
       het icoon (44), gedeeld door twee. */
    align-self: flex-start;
    margin-top: -2px;
    border-radius: 50%;
    background: var(--ws-primary-bg);
    border: 1px solid var(--ws-primary-line);
    color: var(--ws-primary);
    font-size: 1.125rem;
}

/* De vier blokken bovenaan het dashboard zijn kerngetallen, maar hun inhoud is
   met Tailwind-klassen in grijstinten opgemaakt. Die zijn op een gevuld accentvlak
   niet te lezen, dus binnen zo'n blok wint wit van elke grijstint. */
.kpi-block--link,
.kpi-block--link .text-gray-500,
.kpi-block--link .text-gray-600,
.kpi-block--link .text-gray-900,
.kpi-block--link strong {
    color: #fff;
}

.kpi-block--link .text-gray-500,
.kpi-block--link .text-gray-600 {
    color: var(--ws-on-primary);
}

/* Het icoonvlakje rechtsboven in zo'n blok: lichter dan het blok zelf, zodat
   het leesbaar blijft zonder een tweede kleur te introduceren. */
.kpi-block--link .bg-primary-50,
.kpi-block--link [class*="bg-green-"],
.kpi-block--link [class*="bg-amber-"],
.kpi-block--link [class*="bg-blue-"] {
    background-color: rgba(255, 255, 255, .15) !important;
    color: #fff !important;
}

.kpi-block--link:hover {
    background: var(--ws-primary-h);
    border-color: var(--ws-primary-h);
}

/* ================================================================ 20. Alles in
   de paginakop staat op de titelregel

   De kop is een flexrij met items-center, dus de knoppen rechts hingen in het
   midden van het hele blok. Met een omschrijving onder de titel zakken ze
   daardoor mee naar beneden en staan ze schuin onder de kop in plaats van
   ernaast. Hetzelfde geldt voor de terugpijl en het pagina-icoon links.

   Boven uitlijnen alleen is niet genoeg: een knop is 40 px hoog en de titel
   29 px, dus dan hangt de knop 11 px onder de tekst. Daarom krijgt de
   titelregel de hoogte van een knop en staat alles daar netjes op gecentreerd.
   Dat werkt ook als er een periodekiezer naast de titel staat, want die is zelf
   40 px en maakt de regel dus niet hoger. */
.ws-page-header__title-row {
    min-height: 40px;
}

.ws-page-header__actions {
    align-self: flex-start;
}

/* ================================================================ 21. De kaart
   Het witte vlak waar elke lijst, elk paneel en elke tegel in staat. Dit stond
   als vier Tailwind-utilities in de markup van 88 templates
   (bg-white rounded-lg shadow-sm border border-gray-100), dus een wijziging
   aan de kaart was 88 bewerkingen. Nu is het één regel.

   De waarden zijn exact wat die utilities opleverden, gemeten op 1 nov 2026,
   zodat de omzetting zelf niets aan het beeld verandert. Let op de dubbele
   haarlijn: er staat én een rand én een ring van 1px. Dat is bestaand gedrag
   (Bootstrap's .shadow-sm leest --bs-box-shadow-sm, die bij ons de ring is),
   geen keuze. Wie dat wil opruimen haalt --ws-card-ring weg en ziet het
   meteen overal.

   Plaatsing hoort niet hier: overflow-hidden, mb-4 en p-4 blijven utilities
   in de markup, want die verschillen per scherm. */
.ws-card {
    background: var(--ws-card-bg);
    border: 1px solid var(--ws-card-border);
    border-radius: var(--ws-card-radius);
    box-shadow: 0 0 0 1px var(--ws-card-ring);
}

/* Een kaart die een probleem signaleert, bijvoorbeeld een inkoopregel zonder
   leverancier. Alleen de rand verandert; de rest blijft de gewone kaart. */
.ws-card--alert {
    border-color: var(--ws-card-border-alert);
    box-shadow: 0 0 0 1px var(--ws-card-border-alert);
}

/* ================================================================ 22. Meldingen
   Een toelichting, waarschuwing of fout op een pagina, en ook de flashes.

   Dit kan niet met Tailwind-utilities. Bootstrap's `.border` is een shorthand
   mét !important en Vuetify (app.js-bundel) zet er nog een regel overheen, dus
   `border-amber-200` verloor altijd en elke gekleurde melding in het CRM had
   een grijze rand. Gemeten 1 nov 2026 door de stylesheets één voor één uit te
   zetten. Vandaar een eigen klasse met tokens.

   De rand is een tint van de tekstkleur, dezelfde truc als bij de
   statusbadges, zodat een nieuw type alleen twee kleuren hoeft te kiezen. */
.ws-notice {
    display: flex;
    align-items: flex-start;
    gap: .5rem;
    padding: .75rem;
    border-radius: var(--ws-radius-control);
    border: 1px solid color-mix(in srgb, currentColor 28%, transparent);
    font-size: .875rem;
    line-height: 1.5;
}

.ws-notice--info    { background: var(--ws-primary-bg);        color: var(--ws-primary-deep); }
.ws-notice--warning { background: var(--ws-badge-amber-bg); color: var(--ws-badge-amber); }
.ws-notice--success { background: var(--ws-badge-green-bg); color: var(--ws-badge-green); }
.ws-notice--error   { background: var(--ws-badge-red-bg);   color: var(--ws-badge-red); }

.ws-notice__icon { flex: none; margin-top: .1rem; }
.ws-notice__text { flex: 1 1 auto; }
/* De actie van een melding staat rechts en krimpt niet mee: de tekst vult de
   rest. Kleine knop, zie .ws-btn--sm hieronder. */
.ws-notice__action { flex: none; align-self: center; }

.ws-notice__close {
    flex: none;
    border: 0;
    background: none;
    color: inherit;
    opacity: .6;
    padding: 0;
    line-height: 1;
}

.ws-notice__close:hover { opacity: 1; }

/* Een icoonknop zonder tekst: bellen, mailen, een snelkoppeling naast de titel.
   36 bij 36 en gedempt, zodat hij naast een gewone .ws-btn leest als een
   hulpmiddel en niet als een knop waarvan de tekst is weggevallen. Een actie met
   gevolgen (kopiëren, verwijderen) hoort een .ws-btn mét tekst te zijn.
   Stond tot 3 nov 2026 in het style-blok van layouts/view_base.html.twig en was
   daardoor onbereikbaar voor pagina's die die layout niet gebruiken. */
.btn-icon {
    width: 36px;
    height: 36px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 6px;
    color: var(--ws-muted);
    background: var(--ws-page);
    border: 1px solid var(--ws-hair);
    text-decoration: none;
    cursor: pointer;
    transition: all 0.15s ease;
}

.btn-icon:hover {
    background: var(--ws-hair);
    color: var(--ws-ink);
}

.btn-icon.text-success { color: #198754; }
.btn-icon.text-warning { color: #ffc107; }
.btn-icon.text-danger:hover { background: #fee; color: #dc3545; }

/* Een kleine knop voor een actie op een rij. De gewone .ws-btn is 46px hoog en
   dat domineert een tabelrij van 40px; deze past ertussen zonder dat de actie
   wegvalt als een stuk tekst. Gemaakt voor Verzamelen en Vervalt op de
   bestellijst, die daar als grijze en rode tekstlinks stonden. */
.ws-btn--sm {
    min-height: 30px;
    padding: 0 10px;
    font-size: .6875rem;
}

/* ================================================================ 23. Uitklapbare blokken
   Bootstrap verbergt een dichtgeklapt blok met `.collapse:not(.show){display:none}`
   en animeert de hoogte. Tailwind heeft toevallig óók een utility die `.collapse`
   heet, en die zet `visibility: collapse`. Tailwind laadt later, dus die won:
   het blok klapte wel open maar de inhoud bleef onzichtbaar, en op een tabel
   haalt `visibility: collapse` de rijen zelfs helemaal weg.

   Gevonden op 1 nov 2026 bij "Dagelijks detail" op het beschikbaarheidsscherm,
   dat opende met een hoogte van 41px en een lege tabel erin. Raakt elk blok met
   class="collapse", dus ook de twee die op `collapse show` staan.

   De Tailwind-utility wordt nergens bewust gebruikt; die geven we hiermee op. */
.collapse {
    visibility: visible;
}

/* ================================================================ 24. Lijstregel
   De regels van de stappenlijst en de contactenlijst van een campagne waren
   twee keer met de hand opgemaakt, met hetzelfde doel en net andere waarden:
   de stappen hadden een eigen achtergrond uit een <style>-blok in het sjabloon
   en een bolletje in Bootstrap-blauw, de contacten een wit kader. Ze horen
   hetzelfde te zijn, dus staan ze nu in één component,
   templates/components/_list_item.html.twig, en hier de opmaak daarvan.

   Een lijstregel is geen kaart: hij staat ín een kaart, dus hij heeft de
   vlakke vulling en de rustige ronding van een bedieningselement. */
.ws-list-item {
    background: var(--ws-surface-alt);
    border: 1px solid var(--ws-hair);
    border-radius: var(--ws-radius-control);
    padding: 1rem;
    margin-bottom: .75rem;
    transition: background-color .15s ease;
}

.ws-list-item:last-child {
    margin-bottom: 0;
}

.ws-list-item:hover {
    background: var(--ws-primary-bg);
}

/* Het bolletje links. Wat erin staat verschilt per lijst, de vorm niet: bij de
   stappen het volgnummer, bij de contacten de initialen. */
.ws-list-item__marker {
    flex: none;
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--ws-primary);
    color: #fff;
    font-family: var(--ws-font-head);
    font-weight: 700;
    font-size: .8125rem;
    line-height: 1;
}

.ws-list-item__title {
    font-size: .9rem;
    font-weight: 700;
    margin: 0 0 .25rem;
}

.ws-list-item__sub {
    display: block;
    color: var(--ws-muted);
    font-size: .75rem;
}

.ws-list-item__handle {
    flex: none;
    cursor: move;
    color: var(--ws-muted);
}

/* Bijzaak naast de titel: het bedrijf, een e-mailadres, een datum. Zelfde tint
   en maat als de toelichting eronder, zodat de titel zelf de titel blijft. */
.ws-list-item__aside {
    margin-left: .5rem;
    color: var(--ws-muted);
    font-family: var(--ws-font-body);
    font-size: .75rem;
    font-weight: 400;
}

/* ================================================================ 25. Velden
   Eén vorm voor elk invoerveld, of het nu uit een Symfony-formulier komt
   (templates/form/tailwind_theme.html.twig) of met de hand in een sjabloon
   staat (components/_field.html.twig).

   De maten stonden tot 3 nov 2026 als losse Tailwind-klassen in het formthema
   en nog eens in tientallen sjablonen, met `rounded-md` naast `rounded`, en een
   focusrand in een kleur die niet in de huisstijl zat. */
.ws-field {
    margin-bottom: 1rem;
}

.ws-field__label {
    display: block;
    margin-bottom: .25rem;
    font-size: .8125rem;
    font-weight: 500;
    color: var(--ws-article);
}

.ws-field__label .ws-field__required {
    color: var(--ws-danger);
}

.ws-field__control {
    display: block;
    width: 100%;
    padding: .5rem .75rem;
    font-family: var(--ws-font-body);
    font-size: .875rem;
    color: var(--ws-article);
    background: var(--ws-canvas);
    border: 1px solid var(--ws-hair);
    border-radius: var(--ws-radius-control);
}

.ws-field__control::placeholder {
    color: var(--ws-muted);
}

.ws-field__control:focus {
    outline: none;
    border-color: var(--ws-accent);
    box-shadow: 0 0 0 3px rgba(var(--ws-accent-rgb), .18);
}

.ws-field__control:disabled,
.ws-field__control[readonly] {
    background: var(--ws-surface-alt);
    color: var(--ws-body);
}

/* Een veld met een fout draagt dat zelf, zodat je het ziet zonder de melding
   eronder te lezen. */
.ws-field--invalid .ws-field__control {
    border-color: var(--ws-danger);
}

.ws-field__help {
    margin-top: .25rem;
    font-size: .75rem;
    color: var(--ws-muted);
}

.ws-field__error {
    margin: .25rem 0 0;
    padding: 0;
    list-style: none;
    font-size: .8125rem;
    color: var(--ws-danger);
}

/* Een vinkvakje of keuzerondje is geen vlak maar een merkteken: zelfde
   accentkleur als de rest, en niet de blauwe van de browser. */
.ws-field__check {
    width: 1rem;
    height: 1rem;
    accent-color: var(--ws-primary);
    border: 1px solid var(--ws-hair);
}
