/* ============================================================
 * SocialGain — app.css
 * Alle applicatie-stijlen. Geen preprocessors, geen frameworks.
 * ============================================================ */

/* DE BROWSER MOET WETEN WELKE WEERGAVE DRAAIT. Zonder color-scheme tekent hij
   checkboxes, selects, scrollbalken en de standaard knopachtergrond altijd
   LICHT — ook in donkere weergave. Gemeten in donkere weergave: .ql-emoji-btn
   zwart op #14141f (contrast 1.15) en .btn-danger-ghost op de UA-knopkleur
   #efefef (1.65). Beide krijgen hun kleur van de browser, niet van dit bestand,
   dus geen enkele CSS-regel hier repareerde dat.
   `light dark` volgt de systeemvoorkeur; de twee regels erna volgen de
   expliciete keuze uit assets/js/mode.js (<html data-mode>), zodat "donker" op
   een licht systeem óók donkere besturingen geeft. */
:root { color-scheme: light dark; }
html[data-mode="light"] { color-scheme: light; }
html[data-mode="dark"]  { color-scheme: dark; }

/* -----  Design tokens  ---------------------------------- */
:root {
  --c-bg:        #f5f7fb;
  --c-surface:   #ffffff;
  --c-border:    #e4e7ef;
  --c-text:      #1b1f2a;
  --c-muted:     #6b7280;
  --c-primary:   #2563eb;
  --c-primary-600: #1d4ed8;
  --c-primary-soft: #eef2ff;
  --c-success:   #16a34a;
  --c-warning:   #d97706;
  --c-error:     #dc2626;
  --c-focus:     #3b82f6;

  --r-sm: 4px;
  --r-md: 8px;
  --r-lg: 12px;

  --shadow-sm: 0 1px 2px rgba(16, 24, 40, .06);
  --shadow-md: 0 4px 12px rgba(16, 24, 40, .08);
  --shadow-lg: 0 16px 40px rgba(16, 24, 40, .12);

  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* -----  Reset & base  ----------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--font);
  font-size: 15px;
  line-height: 1.5;
  color: var(--c-text);
  background: var(--c-bg);
  -webkit-font-smoothing: antialiased;
}
a { color: var(--c-primary); text-decoration: none; }
a:hover { text-decoration: underline; }
h1 { font-size: 24px; margin: 0 0 8px; }
h2 { font-size: 18px; margin: 0 0 8px; }
.muted { color: var(--c-muted); }
.hint  { color: var(--c-muted); font-size: 13px; display: block; margin-top: 4px; }

/* -----  Layout  ----------------------------------------- */
.page { min-height: calc(100vh - 60px); padding: 24px 0; }
.container { max-width: 1080px; margin: 0 auto; padding: 0 20px; }

/* -----  Topbar / nav (authenticated pages)  ------------- */
.impersonation-banner {
  background: #7c3aed;
  color: #fff;
  padding: 8px 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  font-size: 14px;
  position: sticky;
  top: 0;
  z-index: 30;
}
.impersonation-banner strong { font-weight: 600; }
.btn-warning {
  background: #fff;
  color: #7c3aed;
  border: none;
  padding: 4px 12px;
  border-radius: 6px;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.btn-warning:hover { background: #ede9fe; }
.topbar {
  background: var(--c-surface);
  border-bottom: 1px solid var(--c-border);
  position: sticky; top: 0; z-index: 20;
}
.topbar-inner {
  max-width: 1280px; margin: 0 auto;
  padding: 0 20px; height: 60px;
  display: flex; align-items: center; gap: 24px;
}
.brand {
  display: flex; align-items: center; gap: 10px;
  color: var(--c-text); text-decoration: none; font-weight: 600;
}
.brand:hover { text-decoration: none; }
.brand-mark {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 6px;
  background: var(--c-primary); color: #fff;
  font-weight: 700; font-size: 12px; letter-spacing: .5px;
}
.brand-mark-lg { width: 48px; height: 48px; font-size: 18px; border-radius: 10px; }
.topbar-nav {
  display: flex; gap: 4px; flex: 1;
}
.topbar-nav a {
  color: var(--c-text); padding: 8px 12px; border-radius: var(--r-sm);
  font-weight: 500;
}
.topbar-nav a:hover {
  background: var(--c-primary-soft); color: var(--c-primary-600);
  text-decoration: none;
}
.topbar-right {
  display: flex; align-items: center; gap: 12px;
}
.user-chip {
  color: var(--c-muted); font-size: 14px; max-width: 200px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.inline-form { display: inline; margin: 0; padding: 0; }

/* Kleurstip van een contentcategorie. `border` en niet alleen de vulkleur: een
   categorie die wit of bijna-wit kiest zou anders onzichtbaar zijn op de kaart,
   en dan lijkt er geen kleur te staan. */
.cat-dot {
  display: inline-block;
  width: .75rem; height: .75rem;
  border-radius: 50%;
  border: 1px solid var(--c-border, rgba(0,0,0,.2));
  vertical-align: middle;
  margin-right: .35rem;
}

/* -----  Buttons  ---------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 9px 16px; border-radius: var(--r-md);
  font: inherit; font-weight: 500;
  border: 1px solid transparent; cursor: pointer;
  transition: background-color .15s, border-color .15s, color .15s;
}
.btn:focus-visible {
  outline: 2px solid var(--c-focus); outline-offset: 2px;
}
.btn-primary {
  background: var(--c-primary); color: #fff;
}
.btn-primary:hover { background: var(--c-primary-600); }
.btn-ghost {
  background: transparent; color: var(--c-text);
  border-color: var(--c-border);
}
.btn-ghost:hover { background: var(--c-bg); }
.btn-block { width: 100%; }

/* -----  Forms  ------------------------------------------ */
.field { display: block; margin-bottom: 16px; }
.field-label {
  display: block; font-weight: 500; font-size: 14px;
  margin-bottom: 6px; color: var(--c-text);
}
.field input[type="text"],
.field input[type="email"],
.field input[type="password"],
.field input[type="date"],
.field input[type="datetime-local"],
.field input[type="number"],
.field input[type="url"],
.field input[type="search"],
.field input[type="time"],
.field textarea,
.field select {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  background: #fff;
  font: inherit;
  color: inherit;
  transition: border-color .15s, box-shadow .15s;
}
.field input:focus,
.field textarea:focus,
.field select:focus {
  outline: none;
  border-color: var(--c-primary);
  box-shadow: 0 0 0 3px rgba(37, 99, 235, .15);
}
.checkbox {
  display: flex; align-items: center; gap: 8px;
  margin-bottom: 16px; font-size: 14px; color: var(--c-muted);
  cursor: pointer;
}
.checkbox input { width: 16px; height: 16px; }

/* -----  Aanraakdoelen  ---------------------------------- */
/* WCAG 2.2 AA, Target Size (Minimum): een aanwijsdoel is minstens 24x24 CSS px,
   of het label eromheen haalt die maat. Gemeten op 375px stond hier een bonte
   reeks onder die grens: tekstvelden en <select> van 19px hoog, het bitly-veld
   van 21px, terugkeerlinks van 21px, en vinkvakjes van 13x13 in labels die zélf
   maar 17px hoog waren.
   DE MAAT HANGT AAN HET LABEL, NIET AAN HET VAKJE. Eenenveertig vinkvakjes op
   /notifications/preferences en /settings/api zaten al in een ruim label en
   waren daarmee gewoon in orde; die vakjes opblazen tot 24px naast tekst van
   14px maakt het formulier lelijker zonder iets te repareren. Alleen de labels
   die te kort waren krijgen hun ondergrens. */
input:not([type="checkbox"]):not([type="radio"]):not([type="hidden"]),
select, textarea { min-height: 24px; }
/* EEN <select> IS ZO BREED ALS ZIJN LANGSTE OPTIE, en niets dwingt hem binnen
   zijn ouder. Op /conversations stond één optie van een hele zin ("Nog geen
   sjablonen. Maak er een aan bij …"): de keuzelijst werd 426px en trok het
   DOCUMENT op een telefoon naar 467px (gemeten 21-09-2026, 320/375/390px, alle
   drie de site-thema's). `.field select` heeft `width: 100%` en had dit dus
   niet; een select buiten `.field` wel — en die view droeg `form-control`, een
   klasse die in deze codebase geen enkele regel heeft. Deze ene regel kan
   niets BREDER maken, alleen afkappen op de ouder; de browser kort de getoonde
   tekst dan zelf in. `.org-switcher select` (max 200px) blijft specifieker. */
select { max-width: 100%; }
.checkbox { min-height: 24px; }
/* EEN KNOP DIE ALS LINK OOGT IS NOG STEEDS EEN DOEL. `.linklike` heeft
   `padding: 0`, dus zijn hoogte is die van één tekstregel: 21px bij 14px
   tekst, gemeten op /campaigns/plan (22-09-2026) toen de knop "zonder taak" van
   het dode `btn-link` naar deze klasse ging. Een `<button>` is inline-block en
   draagt een min-height gewoon. */
.linklike { min-height: 24px; }
/* PADDING EN NIET `display: inline-flex`. Die eerste poging gaf wél de 24px,
   maar maakte van elk label één onbreekbaar blok: de kanaal-allowlist
   ("LinkedIn — Ontwikkelpagina (linkedin · read/write/publish)") werd zo 259px
   breed en duwde /settings/api op 768px weer 26px open. Een inline element kan
   geen min-height dragen, maar verticale padding vergroot zijn hitvlak wél —
   en de tekst blijft gewoon over regels wikkelen. */
label.checkbox-inline { padding-block: 4px; }

/* -----  Auth pages  ------------------------------------- */
/* Alleen de hoogte. De ACHTERGROND staat in assets/media/ui.css, dat na dit
   bestand laadt en `var(--ui-auth-bg)` van het actieve thema pakt. Hier stonden
   tot 14-09-2026 twee hardgecodeerde lichtblauwe radial-gradients overheen die
   dus al nergens meer tekenden — dode regels, en in donkere weergave zouden ze
   ook niet geklopt hebben. */
body.is-auth-page { min-height: 100vh; }
.auth-wrap {
  min-height: calc(100vh - 48px);
  display: flex; align-items: center; justify-content: center;
  padding: 24px 20px;
}
.auth-card {
  width: 100%; max-width: 420px;
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  padding: 32px 28px;
}
.auth-brand {
  text-align: center; margin-bottom: 24px;
}
.auth-brand .brand-mark { margin-bottom: 12px; }
.auth-brand h1 { font-size: 22px; margin: 0 0 4px; }
.auth-brand .muted { font-size: 14px; }
.auth-links {
  margin-top: 16px; text-align: center; font-size: 14px;
}

/* -----  Alerts / flash  --------------------------------- */
.alert {
  border-radius: var(--r-md);
  padding: 10px 14px;
  margin-bottom: 16px;
  font-size: 14px;
  border: 1px solid transparent;
}
.alert-success { background: #ecfdf5; border-color: #a7f3d0; color: #065f46; }
.alert-error   { background: #fef2f2; border-color: #fecaca; color: #991b1b; }
.alert-warning { background: #fffbeb; border-color: #fde68a; color: #92400e; }
.alert-info    { background: var(--c-primary-soft); border-color: #c7d2fe; color: #3730a3; }

/* -----  Cards  ------------------------------------------ */
.card {
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  padding: 20px;
  box-shadow: var(--shadow-sm);
  margin: 16px 0;
}
.plain-list { list-style: none; padding: 0; margin: 8px 0; }

/* Statusregel van de passkey-module (assets/js/passkey.js). Zonder de
   foutvariant is een geweigerde registratie niet te onderscheiden van een
   gewone melding — en dan leest "er is niets gewijzigd" hetzelfde als
   "je apparaat leverde geen bruikbare sleutel".
   `--tint-err-fg` is de TEKSTvariant; `--c-error` is een VULkleur en heeft als
   tekst te weinig contrast (gemeten 2.77:1 op wit). */
.passkey-status.is-error { color: var(--tint-err-fg, #b91c1c); }
.plain-list li {
  padding: 8px 0; border-bottom: 1px solid var(--c-border);
}
.plain-list li:last-child { border-bottom: 0; }

/* -----  Publieke-paginablokken (views/blocks/*.php)  ---- */
.page-hero-image { max-width: 100%; border-radius: var(--r-lg); margin-bottom: 16px; }
/* .card.page-hero--bg (niet .page-hero--bg alleen): assets/media/ui.css laadt
   NA dit bestand en herdefinieert .card met een `background:`-shorthand, die
   anders background-size/-position van gelijke bronvolgorde-specificiteit
   zou terugzetten. De dubbele klasse geeft deze regel voorrang ongeacht
   laadvolgorde. */
.card.page-hero--bg {
  position: relative;
  background-size: cover;
  background-position: center;
  color: #fff;
}
.page-hero--bg::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, .45);
  border-radius: inherit;
}
.page-hero--bg > * { position: relative; z-index: 1; }

.page-image-grid {
  display: grid;
  grid-template-columns: repeat(var(--cols, 3), 1fr);
  gap: var(--gap, 16px);
}
/* Vaste vierkante cel + cover-crop: zonder een vaste doos heeft
   object-position (het focuspunt dat de beheerder per foto kiest) geen
   zichtbaar effect, want de hele foto past dan toch altijd op zijn plek. */
.page-image-grid img {
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: var(--r-lg);
  display: block;
}
@media (max-width: 640px) {
  .page-image-grid { grid-template-columns: 1fr; }
}

/* -----  Toastify overrides (lokale stijlen)  ------------ */
.toastify.toast-success { background: var(--c-success); }
.toastify.toast-error   { background: var(--c-error); }
.toastify.toast-warning { background: var(--c-warning); }
.toastify.toast-info    { background: var(--c-primary); }

/* -----  Utilities  -------------------------------------- */
.text-center { text-align: center; }
.mt-0  { margin-top: 0; }
.mt-8  { margin-top: 8px; }
.mt-16 { margin-top: 16px; }
.mt-24 { margin-top: 24px; }
.mb-0  { margin-bottom: 0; }
.mb-8  { margin-bottom: 8px; }
.mb-16 { margin-bottom: 16px; }
.hidden { display: none !important; }

/* Het hidden-ATTRIBUUT moet net zo hard verbergen als de .hidden-klasse.
   De browserstandaard (`[hidden] { display: none }`) staat in de UA-stylesheet
   en verliest daardoor van élke eigen display-regel: `.btn` is inline-flex, dus
   `<button class="btn" hidden>` bleef zichtbaar en gaf een knop die niets doet.
   Alle JS in assets/js/ verbergt via `prop('hidden', …)`, dus deze regel is de
   enige plek waar dat contract wordt afgedwongen. */
[hidden] { display: none !important; }

/* -----  Page header (list pages)  ----------------------- */
.page-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; margin-bottom: 16px; flex-wrap: wrap;
}
.page-header h1 { margin: 0; }
/* EEN FLEX- OF GRIDKIND HEEFT STANDAARD `min-width: auto` en krimpt daardoor
   nooit onder zijn min-content-breedte: één lange knoppenrij of titel duwt de
   hele pagina open. Gemeten op 375px: de kopregel van /calendar werd 364px in
   een kolom van 335 (documentbreedte 385) en de dashboardkaart "Aankomende
   planning" werd 437px (documentbreedte 457). Met `min-width: 0` mag het kind
   krimpen en wikkelt of scrollt de inhoud binnen zijn eigen kader. */
.page-header > *, .nv-grid > *, .form-grid > * { min-width: 0; }
.filter-row {
  display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
}
.filter-row select,
.filter-row input[type="text"],
.search-input {
  padding: 8px 10px; border: 1px solid var(--c-border);
  border-radius: var(--r-md); background: var(--c-surface-2); font: inherit;
}
/* Die achtergrond was een HARDGECODEERDE `#fff` naast twee tokens in dezelfde
   regel, en in donkere modus won hij van de tokenregel voor kale velden
   (ui.css). Gemeten in Forum-donker: tekst #f1efe9 op #ffffff is 1.08:1 —
   onleesbaar, op /team/stats en /reports allebei. `--c-surface-2` is precies
   wat ui.css een kale `select` al geeft, en alle vier de schillen definiëren
   hem in licht én donker. */

/* Het rode accent van het beheerpaneel staat sinds de themasplitsing in
   themes/admin/supernova/theme.css: het verschijnt in precies één schil en
   hoort dus bij dat thema. `.auth-card-wide` blijft hier — die wordt óók
   gebruikt door views/invitations/accept.php, een publieke pagina. */
.auth-card-wide {
  max-width: 560px;
}

/* -----  Setup 2FA  -------------------------------------- */
.setup-steps {
  margin: 0; padding: 0 0 0 20px;
}
.setup-steps li {
  margin-bottom: 20px;
}
.totp-qr-wrap {
  margin-top: 12px;
  display: flex;
  justify-content: center;
}
.totp-qr-wrap > div, .totp-qr-wrap canvas {
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  padding: 8px;
  background: #fff;
}
.totp-secret-box, .totp-uri-box {
  margin-top: 8px;
  padding: 12px 14px;
  background: #f8fafc;
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
.totp-secret-box code {
  font-size: 16px; font-weight: 600; letter-spacing: 1px;
  word-break: break-all;
}
.totp-uri-box code {
  font-size: 12px; word-break: break-all; flex: 1; min-width: 0;
}
.btn-small {
  padding: 5px 10px; font-size: 13px;
}
.totp-input {
  font-size: 24px !important; letter-spacing: 8px;
  text-align: center; font-family: "SF Mono", Menlo, Consolas, monospace;
}

/* -----  Stat grid  -------------------------------------- */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 16px; margin: 16px 0 24px;
}
.stat-card {
  background: var(--c-surface); border: 1px solid var(--c-border);
  border-radius: var(--r-lg); padding: 18px;
  display: flex; flex-direction: column; gap: 4px;
  box-shadow: var(--shadow-sm);
}
.stat-label { color: var(--c-muted); font-size: 13px; text-transform: uppercase; letter-spacing: .5px; }
.stat-value { font-size: 28px; font-weight: 700; }
.stat-sub   { color: var(--c-muted); font-size: 12px; }

/* -----  Card header  ------------------------------------ */
.card-header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 12px; gap: 12px;
}
.card-header h2 { margin: 0; }

/* -----  Data table  ------------------------------------- */
/* EEN BREDE TABEL LIET DE HELE PAGINA SCHUIVEN. Gemeten op 375px:
   /team/stats werd 939px breed, /campaigns 697, /team 640, /team/audit-log 658
   en /channels 492 — en /team/stats schoof zelfs op 768px nog. Horizontaal
   scrollen van het DOCUMENT betekent dat de kopbalk, de knoppen en alle andere
   inhoud meebewegen; scrollen van de TABEL raakt alleen de tabel.
   Een tabel kan zichzelf niet scrollen (daarvoor moet hij display:block
   worden, en dan vallen de kolommen uit elkaar), dus doet de omhullende kaart
   het. `:has(> .data-table)` houdt dat beperkt tot kaarten die er echt één
   dragen: gemeten bevat geen enkele daarvan een absoluut gepositioneerd
   element, dus er valt niets af te knippen. Kaarten met een uitklapper houden
   hun `overflow: visible`. */
:is(.card, .nv-panel):has(> :is(.data-table, .perm-table)) {
  overflow-x: auto;
  overscroll-behavior-x: contain;
}
/* `.table-scroll` IS HET OMHULSEL VOOR EEN TABEL DIE GEEN DIRECT KIND VAN DE
   KAART IS — en die klasse had geen enkele regel. `views/conversations/` droeg
   hem kaal, `views/campaigns/plan_edit.php` plakte er zelf een inline
   `overflow-x:auto` bij: de klasse bestond in de opmaak maar niet in de CSS.
   Gemeten op 21-09-2026: de tabel "Wat kan er per kanaal" is minimaal 385px
   breed, het omhulsel op een telefoon 238-308px, en zonder deze regel schoof
   het hele DOCUMENT naar 467px — op 320, 375 én 390px, in alle drie de
   site-thema's. De `:has()`-regel hierboven ving dit niet: de tabel zit een
   niveau dieper en heet niet `.data-table`. */
.table-scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
}
.data-table {
  width: 100%; border-collapse: collapse; font-size: 14px;
}
.data-table th, .data-table td {
  text-align: left; padding: 10px 8px;
  border-bottom: 1px solid var(--c-border);
  vertical-align: middle;
}
.data-table th {
  font-weight: 600; color: var(--c-muted); font-size: 12px;
  text-transform: uppercase; letter-spacing: .5px;
  background: #fafbfc;
}
.data-table tbody tr:hover { background: #fafbfc; }
.data-table code {
  background: #f1f5f9; padding: 1px 6px; border-radius: 3px;
  font-size: 12px;
}
.data-table-dense th, .data-table-dense td { padding: 6px 8px; }
.data-table .nowrap { white-space: nowrap; }
.data-table .text-right { text-align: right; }

/* `.data-table--stack` — EEN TABEL MET EEN LANGE TEKSTKOLOM WORDT OP EEN
   TELEFOON EEN LIJST. Schuiven binnen het eigen kader is goed voor cijfers en
   korte cellen, niet voor lopende tekst. Gemeten op 21-09-2026 met de tabel
   "Wat kan er per kanaal" op /conversations (390px, omhulsel 308px): als
   gewone tabel kreeg de toelichting 113px, brak ze per woord, was één rij
   1352px hoog en de pagina 6,7 schermen lang. Een minimumbreedte (44rem)
   maakt de kolom 346px, maar zet hem BUITEN beeld: je schuift naar de uitleg
   en raakt de kanaalnaam kwijt, en die uitleg is waar de tabel om draait.
   Gestapeld krijgt de tekst de volle 308px en blijft de naam erboven staan.
   Onder dezelfde 720px als het basisblok hieronder; daarboven is het een
   gewone .data-table, met de cellen bovenaan uitgelijnd omdat een rij met een
   alinea erin anders zijn label halverwege de hoogte zet.
   DE VIEW LEVERT TWEE DINGEN MEE. `data-label` op elke cel die zonder zijn
   kolomkop niets zegt (twee badges onder elkaar: welke is "Reacties"?), en de
   ARIA-tabelrollen: `display: block` op tabelelementen kost in een deel van de
   browsers de tabelsemantiek, een expliciete rol niet. De kop blijft om die
   reden in de toegankelijkheidsboom (weggeknipt, niet `display: none`) en het
   label krijgt een lege alt-tekst, zodat een schermlezer de kolomnaam niet
   twee keer voorleest; een browser die die schrijfwijze niet kent houdt de
   regel erboven. */
.data-table--stack th, .data-table--stack td { vertical-align: top; }
/* Een URL of sleutel in zo'n tabel mag overal breken: gestapeld is de kolom
   een telefoonbreedte, en een callback-URL van 60 tekens schoof anders uit
   de kaart (/superadmin/system, 22-09-2026). */
.data-table--stack code { overflow-wrap: anywhere; }
@media (max-width: 720px) {
  .data-table--stack,
  .data-table--stack tbody,
  .data-table--stack tr,
  .data-table--stack th,
  .data-table--stack td { display: block; }
  .data-table--stack thead {
    position: absolute; width: 1px; height: 1px; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
  }
  .data-table--stack tbody tr {
    padding: 12px 0; border-bottom: 1px solid var(--c-border);
  }
  .data-table--stack tbody th,
  .data-table--stack tbody td {
    padding: 2px 0; border-bottom: 0; background: none;
  }
  .data-table--stack td[data-label] {
    display: flex; gap: 8px; align-items: baseline;
  }
  .data-table--stack td[data-label]::before {
    content: attr(data-label);
    content: attr(data-label) / "";
    flex: 0 0 5.5rem; color: var(--c-muted); font-size: 12px;
  }
}

/* -----  Badges  ----------------------------------------- */
.badge {
  display: inline-block; padding: 2px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 500;
}
.badge-success { background: #dcfce7; color: #166534; }
.badge-muted   { background: #f1f5f9; color: #475569; }
.badge-warning { background: #fef3c7; color: #92400e; }
.badge-danger  { background: #fee2e2; color: #991b1b; }
.badge-info    { background: #e0e7ff; color: #3730a3; }

/* -----  Form actions  ----------------------------------- */
.form-actions {
  display: flex; gap: 8px; margin-top: 8px;
}

/* -----  Pagination  ------------------------------------- */
.pagination {
  display: flex; flex-wrap: wrap; gap: 4px; margin-top: 16px;
  padding-top: 12px; border-top: 1px solid var(--c-border);
}
.pagination a {
  padding: 6px 12px; border-radius: var(--r-sm);
  color: var(--c-text); background: #fff;
  border: 1px solid var(--c-border); font-size: 13px;
}
.pagination a:hover { background: var(--c-bg); text-decoration: none; }
.pagination a.is-current {
  background: var(--c-primary); color: #fff; border-color: var(--c-primary);
}
.pag-ellipsis { padding: 6px 8px; color: var(--c-muted); }

/* -----  Details / extra JSON blocks  -------------------- */
.extra-json {
  background: #0f172a; color: #e2e8f0;
  padding: 10px 12px; border-radius: var(--r-sm);
  font-size: 11px; overflow: auto; max-height: 200px;
  margin-top: 6px; white-space: pre-wrap; word-break: break-word;
}
details summary { cursor: pointer; }

/* -----  Permission editor  ------------------------------ */
.back-link {
  /* inline-flex + min-height: was 21px hoog, onder de 24 van WCAG 2.2 AA. */
  display: inline-flex; align-items: center; min-height: 24px;
  margin-bottom: 12px;
  color: var(--c-muted); font-size: 14px;
}
.back-link:hover { color: var(--c-text); }

.perm-card { margin-bottom: 20px; }
/* Drie knoppen naast de kaarttitel. Ze mogen niet krimpen en de kop zet ze met
   space-between tegen de rechterrand: zonder wrap stak de rij op een telefoon
   16px buiten de kaart en schoof het DOCUMENT naar 406px (docs/themas.md §9g). */
.perm-card > .card-header { flex-wrap: wrap; }
.perm-actions { display: flex; flex-wrap: wrap; gap: 6px; }

.perm-section-title {
  font-size: 13px; text-transform: uppercase; letter-spacing: .5px;
  color: var(--c-muted); margin: 20px 0 8px;
  padding-bottom: 4px; border-bottom: 1px solid var(--c-border);
}

.perm-matrix { overflow-x: auto; }
.perm-table {
  width: 100%; border-collapse: collapse; font-size: 14px;
  max-width: 640px;
}
.perm-table th, .perm-table td {
  padding: 8px 12px; border-bottom: 1px solid var(--c-border);
}
.perm-table th {
  font-weight: 600; color: var(--c-muted); font-size: 12px;
  text-transform: uppercase; letter-spacing: .5px;
  vertical-align: bottom;
}
.perm-table input[type="checkbox"] {
  width: 18px; height: 18px; cursor: pointer;
}
/* TEKST in de linkkleur, niet in --c-primary: die token is de VULLING onder
   witte tekst en kan als tekst op de eigen achtergrond niet óók 4.5:1 halen —
   in het donker van Supernova las "alles/geen" als #c9372c op #14141f, 3.54:1
   (gemeten 22-09-2026, docs/themas.md §11a). --c-link is per schil de kleur
   die op de achtergrond leesbaar is: gemeten 9.16:1 donker, 6.29:1 licht. */
.col-toggle {
  display: block; margin-top: 4px;
  background: none; border: 0; cursor: pointer;
  color: var(--c-link); font-size: 11px;
  text-transform: none; letter-spacing: 0; font-weight: 500;
  padding: 2px 4px;
  /* EEN TEKSTKNOP IS NOG STEEDS EEN DOEL (WCAG 2.2, 24×24): bij 11px tekst en
     2px padding mat hij 31×16px op de gebruikerspagina van het beheer
     (22-09-2026). Zelfde ondergrens als `.linklike` en `.checkbox`; de breedte
     is al 31px. Geen accolades in dit commentaar: de broncontrole in
     bin/test_theme.php sectie M leest het blok tot de eerste sluitaccolade. */
  min-height: 24px;
}
.col-toggle:hover { text-decoration: underline; }

.perm-features {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 8px 16px;
}
.perm-feature {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 8px 12px; border-radius: var(--r-sm);
  cursor: pointer; line-height: 1.35;
  border: 1px solid transparent;
}
.perm-feature:hover { background: #fafbfc; border-color: var(--c-border); }
.perm-feature input { margin-top: 3px; width: 16px; height: 16px; flex: 0 0 auto; }
/* `min-width: 0` en `overflow-wrap`: een flex-kind krimpt standaard niet onder
   de breedte van zijn inhoud, en de featurecode is één woord zonder spaties.
   Vier features misten hun vertaling, waarna t() de SLEUTEL teruggaf
   ("superadmin.user.features.manage_gamification") — 44 tekens die niet
   afbraken en dwars door de buurcel liepen (GitLab #22). Die vertalingen
   staan er nu, maar de volgende ontbrekende sleutel hoort het raster niet
   opnieuw open te breken: dan is het label lelijk, niet de pagina stuk. */
.perm-feature span,
.perm-feature small { min-width: 0; overflow-wrap: anywhere; }
.perm-feature span { font-weight: 500; display: block; }
.perm-feature small { display: block; font-size: 11px; font-family: monospace; }

.text-center { text-align: center; }

/* -----  Inline add form (voor lidmaatschap toevoegen)  -- */
.inline-add-form {
  display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap;
}
.inline-field { margin-bottom: 0; }
.inline-field select {
  padding: 10px 12px;
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  background: #fff; font: inherit; min-width: 200px;
}

/* -----  Responsive (base)  ------------------------------ */
@media (max-width: 720px) {
  .topbar-nav { display: none; }
  .topbar-inner { gap: 12px; }
  .user-chip { display: none; }
  .data-table { font-size: 13px; }
  .perm-features { grid-template-columns: 1fr; }
  .inline-add-form { flex-direction: column; align-items: stretch; }
  .inline-field select { min-width: 0; width: 100%; }
}

/* -----  Org switcher in topbar  ------------------------- */
.org-switcher {
  border-left: 1px solid var(--c-border);
  border-right: 1px solid var(--c-border);
  padding: 0 16px;
  margin: 0 4px;
  display: flex; align-items: center;
}
.org-switcher-form { margin: 0; }
.org-switcher select {
  padding: 6px 28px 6px 10px;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: transparent;
  font: inherit;
  font-weight: 500;
  color: var(--c-text);
  cursor: pointer;
  max-width: 200px;
  text-overflow: ellipsis;
}
.org-switcher select:hover { background: var(--c-bg); }
.org-switcher select:focus {
  border-color: var(--c-primary);
  background: var(--c-surface);   /* was #fff: wit vlak in donkere weergave */
}
.org-current {
  font-weight: 500; color: var(--c-text);
  max-width: 200px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}

.user-chip {
  color: var(--c-muted); font-size: 14px; max-width: 200px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  padding: 6px 10px; border-radius: var(--r-md);
}
a.user-chip:hover { background: var(--c-bg); text-decoration: none; color: var(--c-text); }

/* -----  Profile page grid  ------------------------------ */
.profile-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
  gap: 16px;
}

/* -----  Form grid (2-kolom velden)  --------------------- */
.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 12px;
}
.form-grid .field { margin-bottom: 0; }

/* -----  Invitation summary  ----------------------------- */
.invite-summary {
  background: #f8fafc;
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  padding: 12px 16px;
  margin-bottom: 20px;
  display: grid; gap: 8px;
}
.invite-summary div { display: flex; justify-content: space-between; gap: 12px; }
.invite-label { color: var(--c-muted); font-size: 13px; }

/* -----  Profile: mark current membership row  ----------- */
.data-table tr.is-current { background: var(--c-primary-soft); }

/* -----  Disabled input look  ---------------------------- */
.field input:disabled {
  background: #f8fafc; color: var(--c-muted); cursor: not-allowed;
}

/* -----  Responsive tweaks  ------------------------------ */
@media (max-width: 900px) {
  .org-switcher { padding: 0 10px; margin: 0; border: none; }
  .org-switcher select { max-width: 140px; }
}
/* HIER STOND `@media (max-width: 720px) { .org-switcher { display: none } }`,
   sinds de eerste commit. Het verborg de organisatiekiezer op ELKE telefoon, in
   alle drie de site-schillen, en niemand zag het: een gedeelde regel die een
   element weghaalt faalt nergens op. Waar hij op een telefoon staat is een
   vraag van de schil, dus die beslist het nu zelf: Nova in zijn theme.css,
   Atlas en Forum naast hun merk — zie docs/themas.md §9j. */

/* -----  Platformkleuren: één bron  ----------------------- */
/* Deze waarden stonden op twee plekken (.platform-dot-* hier en .rv-tab-dot-* /
   .rv-badge-* bij de reviewpagina), en die twee lijsten waren ongelijk: de
   eerste kende vijf platforms, de tweede elf. Een derde patroon —
   `.platform-icon platform-<naam>`, in vijf views — had NOOIT css en rendert
   dus al die tijd een leeg <span> (git log -S bevestigt: nooit bestaan, niet
   verwijderd). Vandaar hier de kleuren als tokens en de drie patronen als
   consumenten; wie een platform toevoegt zet hem één keer.

   Merkkleuren zijn vaste huisstijlwaarden en horen NIET in een licht/donker-
   palet: LinkedIn-blauw is in beide weergaven LinkedIn-blauw. */
:root {
  --pf-linkedin:  #0A66C2;
  --pf-facebook:  #1877F2;
  --pf-instagram: linear-gradient(45deg, #F58529, #DD2A7B, #8134AF);
  --pf-x:         #000000;
  --pf-youtube:   #FF0000;
  --pf-threads:   #000000;
  --pf-bluesky:   #0085FF;
  --pf-tiktok:    #000000;
  --pf-pinterest: #E60023;
  --pf-wordpress: #21759B;
  --pf-joomla:    #5091CD;
}

/* -----  Channels: platform colour dots  ----------------- */
.platform-dot {
  display: inline-block;
  width: 10px; height: 10px; border-radius: 50%;
  margin-right: 8px; vertical-align: middle;
}
.platform-dot-linkedin  { background: var(--pf-linkedin); }
.platform-dot-instagram { background: var(--pf-instagram); }
.platform-dot-facebook  { background: var(--pf-facebook); }
.platform-dot-x         { background: var(--pf-x); }
.platform-dot-youtube   { background: var(--pf-youtube); }

/* `.platform-icon platform-<naam>` — het derde patroon, nu pas met opmaak.
   Iets groter dan .platform-dot omdat het naast een label of legend staat en
   niet in een lijstrij. Een onbekend platform krijgt --c-muted en blijft dus
   een zichtbare stip in plaats van te verdwijnen: een vorm die er soms wel en
   soms niet is, leest als een renderfout. */
.platform-icon {
  display: inline-block;
  width: 12px; height: 12px; border-radius: 50%;
  margin-right: 8px; vertical-align: middle;
  background: var(--c-muted);
  flex: 0 0 auto;
}
.platform-icon.platform-linkedin  { background: var(--pf-linkedin); }
.platform-icon.platform-facebook  { background: var(--pf-facebook); }
.platform-icon.platform-instagram { background: var(--pf-instagram); }
.platform-icon.platform-x         { background: var(--pf-x); }
.platform-icon.platform-youtube   { background: var(--pf-youtube); }
.platform-icon.platform-threads   { background: var(--pf-threads); }
.platform-icon.platform-bluesky   { background: var(--pf-bluesky); }
.platform-icon.platform-tiktok    { background: var(--pf-tiktok); }
.platform-icon.platform-pinterest { background: var(--pf-pinterest); }
.platform-icon.platform-wordpress { background: var(--pf-wordpress); }
.platform-icon.platform-joomla    { background: var(--pf-joomla); }

/* Zwart op een donkere achtergrond is geen kleur maar een gat. X, Threads en
   TikTok voeren zwart als merkkleur; in donkere weergave krijgen ze een rand
   zodat de stip een stip blijft. */
:root:not([data-mode="light"]) .platform-icon.platform-x,
:root:not([data-mode="light"]) .platform-icon.platform-threads,
:root:not([data-mode="light"]) .platform-icon.platform-tiktok,
:root:not([data-mode="light"]) .platform-dot-x {
  box-shadow: inset 0 0 0 1px var(--c-muted);
}

.card-header h2 {
  display: flex; align-items: center;
  font-weight: 600;
}

/* -----  Genummerde formulierblokken (merkprofiel)  ------ */
/* De kop draagt een volgnummer, een icoon en zijn titel. `gap` en niet marges:
   de hint achter de titel is een eigen <span> en moet gewoon mee wrappen. */
.step-heading {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
/* De pil houdt de accentkleur, het CIJFER niet. `--c-primary` op
   `--c-primary-soft` haalde 3.23:1 in donker en 4.26:1 in licht — beide onder
   de 4.5 van WCAG 1.4.3, en 13px vet telt niet als grote tekst (dat begint bij
   18.66px vet). `--c-primary-soft` is semi-transparant, dus meten vraagt eerst
   alpha-compositie over de kaart: naïef vergelijken geeft 1.0 en lijkt op een
   onzichtbaar cijfer dat er wél staat. Met `--c-text` is het contrast in beide
   weergaven ruim, en het werkt in alle vier de schillen zonder per thema een
   eigen waarde te meten. */
.step-number {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--c-primary-soft); color: var(--c-text);
  font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums;
}
/* sg_nav_icon() levert een <svg viewBox="0 0 24 24"> ZONDER width/height — de
   maat komt van de wikkelaar. In de schillen doet `.nav-ico svg` dat per thema;
   hier moet dit blok het zeggen, anders rendert het icoon 0×0 en zie je een
   kop met een nummer en geen glyph. In de browser gemeten. */
.step-icon { flex: 0 0 auto; color: var(--c-primary); display: inline-flex; }
.step-icon svg { display: block; width: 20px; height: 20px; }

/* -----  Invulvoortgang  --------------------------------- */
.fill-progress { margin: 0 0 20px; }
.fill-progress-head {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px; margin-bottom: 6px;
}
.fill-progress-count { font-weight: 600; font-variant-numeric: tabular-nums; }
/* `progress` in plaats van een eigen div: de browser levert de rol, de
   waarde en de voorleesbaarheid al. Een eigen balk zou role="progressbar" plus
   drie aria-attributen nodig hebben om hetzelfde te doen. Firefox en WebKit
   vragen elk hun eigen pseudo-element voor de kleur; vandaar de drie regels. */
.fill-progress progress {
  appearance: none; -webkit-appearance: none;
  display: block; width: 100%; height: 8px; border: 0;
  border-radius: 999px; overflow: hidden;
  background: var(--c-surface-2);
}
.fill-progress progress::-webkit-progress-bar { background: var(--c-surface-2); }
.fill-progress progress::-webkit-progress-value {
  background: var(--c-primary); border-radius: 999px;
  transition: width .25s ease;
}
.fill-progress progress::-moz-progress-bar { background: var(--c-primary); }

/* -----  Kanaalregels per platform (merkprofiel)  -------- */
/* Stond als inline style op de fieldset met een vaste #e5e7eb — een lichte
   rand die in donkere weergave als streep op een donker vlak bleef staan. */
.channel-rule-set {
  border: 1px solid var(--c-border); border-radius: 6px;
  padding: 12px; margin-bottom: 12px;
}
.channel-rule-legend {
  display: flex; align-items: center;
  padding: 0 6px; font-weight: 600;
}

/* -----  Status pills (posts)  --------------------------- */
.status-pill {
  display: inline-block;
  padding: 2px 10px; border-radius: 999px;
  font-size: 12px; font-weight: 500;
  text-transform: capitalize;
  border: 1px solid transparent;
}
.status-draft     { background: #f1f5f9; color: #475569; }
.status-review    { background: #fef3c7; color: #92400e; }
.status-approved  { background: #dcfce7; color: #166534; }
.status-scheduled { background: #dbeafe; color: #1e40af; }
.status-live      { background: #16a34a; color: #ffffff; }
.status-rejected  { background: #fee2e2; color: #991b1b; }

/* -----  Post editor layout  ----------------------------- */
.post-editor { max-width: 1100px; }
.post-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 20px;
}
.post-sidebar .card { margin: 0 0 16px; }
.post-sidebar h3 {
  margin: 0 0 12px; font-size: 14px;
  text-transform: uppercase; letter-spacing: .5px;
  color: var(--c-muted);
}

/* Subkop BINNEN een kaart, voor een kaart die twee blokken draagt (nu: de
   kaart "Externe beoordeling" met klantakkoord en meekijklink).

   ZONDER DEZE REGEL VALT EEN <h4> TERUG OP DE BROWSERSTANDAARD, en die is
   15px terwijl de kaartkop `.post-sidebar h3` er 14px is — de subkop
   overstemt dan zijn eigen kaart, mét de 19.95px UA-marges erbovenop.
   Gemeten, niet vermoed: elke bestaande h4-regel in deze codebase is
   gescopeerd op een andere container (`.article-rich`, `.meten-uitleg-body`,
   `.media-modal-usage`, `.ai-label-why-body`) en raakt deze dus niet.

   Staat in app.css en niet in een thema: die wordt onvoorwaardelijk geladen
   vóór de themastylesheet, dus Nova, Atlas én Forum krijgen hem in één keer —
   en een thema kan hem daarna nog overschrijven. De tokens bestaan in alle
   drie de thema's; een `var()` naar niets zou hier stil op een literal
   terugvallen. */
.card-subhead {
  margin: 0 0 8px; font-size: 13px; font-weight: 600;
  color: var(--c-text); text-transform: none; letter-spacing: 0;
}

/* DE MINIMUMHOOGTE HOORT OP `.ql-editor`, NIET OP DE CONTAINER.
 *
 * Quill zet zijn eigen `.ql-container` op dit element en maakt daarbinnen een
 * `.ql-editor` — dát is het bewerkbare vlak, en dat is alleen zo hoog als de
 * tekst. Stond de 220px op de container, dan was bij één regel tekst het
 * bewerkbare vlak 42px en de rest 178px dode ruimte: klikken daar raakte de
 * container en zette de cursor niet. Je moest de eerste regel zelf aanwijzen
 * (GitLab #15). In de browser gemeten, met de quill.min.js uit deze repo.
 *
 * Op `.ql-editor` groeit de container gewoon mee, want de editor duwt hem op. */
.quill-editor {
  background: #fff;
  border-radius: 0 0 var(--r-md) var(--r-md);
}
.quill-editor .ql-editor { min-height: 220px; }

/* Reactievak: verving een <textarea rows="3">, dus niet de 220px van de
   postbody-editor hierboven. Zelfde reden voor de plek van de min-height. */
.comment-editor {
  background: #fff;
  border-radius: 0 0 var(--r-md) var(--r-md);
}
.comment-editor .ql-editor { min-height: 96px; }
/* De generieke tekenteller uit app.js deelt de opmaak van de
   reactie-teller: rechts uitgelijnd onder het veld, verder een gewone
   `.hint` — dus kleur en grootte volgen het actieve thema vanzelf. */
.comment-editor-count,
.field-counter { text-align: right; }
.comment-editor-count.is-over { color: var(--c-error); font-weight: 600; }
.ql-toolbar.ql-snow {
  border-radius: var(--r-md) var(--r-md) 0 0;
  border-color: var(--c-border) !important;
}
.ql-container.ql-snow { border-color: var(--c-border) !important; }

/* Placeholder-chips in de mailsjabloon-editor (/superadmin/mail-templates) */
.mailtpl-placeholders {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 4px;
}
.mailtpl-placeholders li { display: flex; align-items: center; gap: 8px; }
.mailtpl-placeholders .mailtpl-ph { padding: 2px 8px; }
.mailtpl-placeholders code { font-size: 12px; }

.post-body-readonly {
  padding: 14px 16px;
  background: #fafbfc;
  border: 1px solid var(--c-border);
  border-radius: var(--r-md);
  line-height: 1.6;
  min-height: 120px;
}
.post-body-readonly p:first-child { margin-top: 0; }
.post-body-readonly p:last-child { margin-bottom: 0; }

.version-list {
  list-style: none; padding: 0; margin: 0;
}
.version-list li {
  padding: 10px 0; border-bottom: 1px solid var(--c-border);
}
.version-list li:last-child { border-bottom: 0; }
.version-head {
  display: flex; justify-content: space-between;
  align-items: baseline; gap: 8px;
  font-size: 13px;
}
.version-note {
  margin-top: 4px; font-size: 13px; color: var(--c-text);
}

/* -----  Review URL box  --------------------------------- */
.review-url {
  display: flex; gap: 6px; align-items: center;
  margin-top: 4px;
}
.review-url input {
  flex: 1; min-width: 0;
  padding: 6px 8px; font-size: 12px;
  border: 1px solid var(--c-border); border-radius: var(--r-sm);
  font-family: "SF Mono", Menlo, Consolas, monospace;
  background: #f8fafc;
}

/* -----  Samenwerkkaart: feedback vragen naast reacties  -- */
/* Issue #17: "Reacties & feedback" is EEN kaart met twee functies erin —
   iemand om feedback vragen, en de reactiedraad. Zonder lijn lezen die als
   een blok en ging de kop van de tweede verloren in de eerste. De regel staat
   op de OPVOLGER (`+`), dus de eerste zichtbare sectie houdt zijn bovenrand
   vrij en een verborgen verzoekenblok laat geen zwevende streep achter. Kleur
   uit de token, dus alle vier de schillen volgen vanzelf. */
.collab-block + .collab-block {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--c-border);
}
.collab-block > h3 { margin: 0 0 8px; }

/* -----  Review preview page  ---------------------------- */
.review-preview {
  border-top: 1px solid var(--c-border);
  padding-top: 20px;
}
.review-preview h2 {
  font-size: 22px;
}

/* -----  kv list  ---------------------------------------- */
.kv-list {
  display: grid; grid-template-columns: auto 1fr;
  gap: 6px 16px; margin: 12px 0 0;
}
.kv-list dt { color: var(--c-muted); font-size: 13px; }
.kv-list dd { margin: 0; font-size: 14px; }

.text-small { font-size: 13px; }

/* -----  Mobile posts layout  ---------------------------- */
@media (max-width: 900px) {
  /* minmax(0, 1fr), niet 1fr: een kale 1fr is minmax(auto, 1fr), en `auto`
     is de min-content van de kolom — de breedste tabel in de zijkolom. De
     kaart met de publicatiestatistieken (zeven kolommen, 450px) rekte zo
     op een telefoon de HELE kolom op en het document schoof naar 470px; de
     :has()-schuifregel op de kaart helpt daar niet, want de kaart is niet
     het grid-item. Gemeten op 22-09-2026 (docs/themas.md §9g). */
  .post-layout { grid-template-columns: minmax(0, 1fr); }
}

/* ============================================================
 * Kalender — maandgrid met drag & drop
 * ============================================================ */

.calendar-container { max-width: 1280px; }

.calendar-nav {
  display: flex; align-items: center; gap: 8px;
  flex-wrap: wrap; justify-content: center;
}
.calendar-title {
  margin: 0 8px; font-size: 18px; font-weight: 600;
  /* `min(180px, 100%)` en niet kaal 180px: die ondergrens hield de maandnaam
     op een vaste breedte zodat de pijlen niet verspringen, maar duwde op 375px
     de hele kopregel naar 364px en daarmee de pagina 10px open. Met min() blijft
     de 180px staan zodra er ruimte is en wijkt hij pas als het niet past. */
  min-width: min(180px, 100%); text-align: center;
}

.calendar-legend {
  display: flex; gap: 8px; flex-wrap: wrap;
  margin-bottom: 12px;
}

.calendar-card {
  padding: 0;      /* grid loopt tot aan de rand */
  overflow: hidden;
}

.calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  background: var(--c-border);
  gap: 1px;
  padding: 1px;
}

.cal-weekday {
  background: #fafbfc;
  padding: 10px 8px;
  font-size: 12px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .5px;
  color: var(--c-muted);
  text-align: center;
}
/* Was hardgezet #b91c1c: in donkere weergave donkerrood op donkergrijs,
   contrast 2.65. --tint-err-fg draagt per weergave de leesbare variant. */
.cal-weekday.cal-weekend { color: var(--tint-err-fg, #b91c1c); }

.cal-cell {
  background: #fff;
  min-height: 110px;
  padding: 6px;
  position: relative;
  display: flex; flex-direction: column;
}
.cal-cell.cal-empty { background: #fafbfc; }
.cal-cell.cal-weekend { background: #fcfcfc; }

/* Bufferdagen van de volgende maand: iets gedimd, wel volledig bruikbaar
   als drop-doel voor drag & drop over de maandgrens. */
.cal-cell.cal-next-month { opacity: 0.72; }
.cal-day-month { font-size: 10px; text-transform: uppercase; letter-spacing: .04em; }

/* "+" per dag: nieuwe post met die datum al ingevuld (#65). Bewust altijd
   zichtbaar, ingetogen, en niet alleen bij hover: op een touchscherm
   bestaat hover niet, en een knop die pas verschijnt vind je niet. */
.cal-day-add {
  position: absolute; top: 4px; right: 4px;
  width: 22px; height: 22px; border-radius: 6px;
  display: flex; align-items: center; justify-content: center;
  font-size: 16px; line-height: 1; text-decoration: none;
  color: var(--c-muted);
}
.cal-day:hover .cal-day-add,
.cal-day-add:focus-visible {
  color: var(--c-primary);
  background: var(--c-primary-soft);
}

.cal-day.is-today {
  background: var(--c-primary-soft);
}
.cal-day.is-today .cal-day-number {
  color: var(--c-primary);
  font-weight: 700;
}

.cal-day-number {
  font-size: 13px; font-weight: 500;
  color: var(--c-muted);
  margin-bottom: 4px;
}

.cal-day-events {
  display: flex; flex-direction: column; gap: 3px;
  flex: 1; overflow: hidden;
  min-height: 20px;             /* droppable hit-area */
}

.cal-event {
  padding: 4px 6px;
  border-radius: var(--r-sm);
  font-size: 11.5px;
  line-height: 1.3;
  background: #fff;
  border-left: 3px solid var(--c-muted);
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: background .12s;
}
.cal-event:hover { background: var(--c-bg); }
.cal-event.is-draggable { cursor: grab; }
.cal-event.is-draggable:active { cursor: grabbing; }
.cal-event.is-locked { cursor: pointer; opacity: .85; }

.cal-event-title {
  display: block;
  font-weight: 500;
  overflow: hidden; text-overflow: ellipsis;
}
.cal-event-meta {
  display: block;
  font-size: 10.5px;
  color: var(--c-muted);
  margin-top: 1px;
}
.cal-event-badge {
  display: inline-block;
  margin-left: 4px;
  padding: 0 5px;
  background: #fff3cd;
  color: #856404;
  border-radius: 2px;
  font-size: 10px;
}

/* Status-kleur via linkerrand */
.cal-event.status-draft     { border-left-color: #64748b; }
.cal-event.status-review    { border-left-color: #d97706; background: #fffbeb; }
.cal-event.status-approved  { border-left-color: #16a34a; background: #f0fdf4; }
.cal-event.status-scheduled { border-left-color: #2563eb; background: #eff6ff; }
.cal-event.status-live      { border-left-color: #16a34a; background: #dcfce7; }
.cal-event.status-rejected  { border-left-color: #dc2626; background: #fef2f2; text-decoration: line-through; }

/* Droppable hover state */
.cal-day.is-drop-hover {
  background: #e0f2fe !important;
  box-shadow: inset 0 0 0 2px var(--c-primary);
}

/* Dragging state */
.cal-event.is-dragging { opacity: .5; }

/* Drag-helper (appendTo body) — komt buiten grid */
body > .cal-event.ui-draggable-dragging {
  box-shadow: var(--shadow-lg);
  background: #fff;
  z-index: 9999;
  max-width: 220px;
}

.calendar-loading, .calendar-empty {
  padding: 24px; text-align: center; color: var(--c-muted);
}

/* ============================================================
 * Mobile: kalender wordt lijst-weergave
 * ============================================================ */
@media (max-width: 720px) {
  .calendar-grid {
    grid-template-columns: 1fr;
    gap: 0;
  }
  .cal-weekday, .cal-empty { display: none; }
  .cal-cell {
    min-height: auto;
    flex-direction: row;
    align-items: flex-start;
    padding: 10px;
    border-bottom: 1px solid var(--c-border);
  }
  .cal-day-number {
    min-width: 40px;
    margin: 0 12px 0 0;
  }
  .cal-day-events { flex: 1; }
}

/* ============================================================
 * Taken
 * ============================================================ */

.priority-pill {
  display: inline-block;
  padding: 1px 8px; border-radius: 999px;
  font-size: 11px; font-weight: 500;
  text-transform: capitalize;
}
.priority-low     { background: #e0e7ff; color: #3730a3; }
.priority-medium  { background: #dbeafe; color: #1e40af; }
.priority-high    { background: #fef3c7; color: #92400e; }
.priority-urgent  { background: #fee2e2; color: #991b1b; }

/* Additional statusen die Post niet heeft */
.status-pending   { background: #f1f5f9; color: #475569; }
.status-submitted { background: #dbeafe; color: #1e40af; }
.status-overdue   { background: #fef3c7; color: #92400e; }
.status-escalated { background: #fee2e2; color: #991b1b; }
.status-completed { background: #dcfce7; color: #166534; }

tr.is-overdue {
  background: #fff8f8;
}
tr.is-overdue td:first-child {
  box-shadow: inset 3px 0 0 #dc2626;
}

.text-error {
  /* HET TEKST-TOKEN, niet de hex en niet `--c-error`.
     #991b1b stond hier hardgecodeerd en dat is een LICHT-modus-kleur: op een
     donkere kaart haalde hij 2.20:1, ruim onder de 4.5:1 die leesbare tekst
     nodig heeft. Gemeten in Supernova.
     `--c-error` lijkt de voor de hand liggende vervanger maar is het NIET: dat
     is een VULkleur (toastachtergrond, badge) en alle vier de schillen zetten
     hem op #f87171 in élke modus — 2.77:1 op een witte kaart. Als tekst is hij
     dus precies andersom kapot.
     `--tint-err-fg` is de tekstvariant en staat in alle vier de schillen per
     modus: #b91c1c licht, #fca5a5 donker (Forum heeft zijn eigen paar). Zeven
     andere plekken gebruiken deze klasse — het gat zat in de klasse, niet in
     één scherm. */
  color: var(--tint-err-fg, #991b1b);
}

.text-warning {
  /* DEZE KLASSE BESTOND NIET, en werd wél gebruikt: `avatar-model-advice.js`
     zet hem op twee plekken om een modeladvies als waarschuwing te tonen. Zonder
     regel is dat gewone tekst — een stille no-op, precies het geval dat
     `.text-error` hierboven ooit was.

     `--tint-warn-fg` en niet `--c-warning`, om dezelfde reden als daar: die
     tweede is een VULkleur (#fbbf24 in élke modus) en als tekst op een witte
     kaart onleesbaar. De tekstvariant staat per modus in alle vier de schillen:
     #b45309 licht, #fcd34d donker. */
  color: var(--tint-warn-fg, #b45309);
}

/* Taak-lijst in post-sidebar */
.task-list {
  list-style: none; padding: 0; margin: 0;
}
.task-item {
  padding: 10px 0;
  border-bottom: 1px solid var(--c-border);
  font-size: 13px;
}
.task-item:last-child { border-bottom: 0; }
.task-item.is-overdue {
  background: #fff8f8;
  margin: 0 -14px;
  padding-left: 14px; padding-right: 14px;
}
.task-head {
  display: flex; gap: 6px; align-items: center;
  margin-bottom: 4px;
}
.task-meta { font-size: 13px; }

.task-new-details summary {
  display: block; cursor: pointer;
  list-style: none;
}
.task-new-details summary::-webkit-details-marker { display: none; }

/* ============================================================
 * Notification bell in topbar
 * ============================================================ */

.notif-bell {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 6px 8px;
  border-radius: var(--r-md);
  color: var(--c-muted);
}
.notif-bell:hover { background: var(--c-bg); text-decoration: none; }
.notif-bell-icon { display: inline-flex; line-height: 1; }
.notif-bell-count {
  position: absolute;
  top: -2px; right: -4px;
  min-width: 18px; height: 18px;
  border-radius: 999px;
  background: var(--c-error, #dc2626);
  color: #fff;
  font-size: 11px; font-weight: 600;
  display: flex; align-items: center; justify-content: center;
  padding: 0 5px;
  box-shadow: 0 0 0 2px var(--c-surface);
}

/* ============================================================
 * Notifications page
 * ============================================================ */

.notif-list { list-style: none; padding: 0; margin: 0; }
.notif-item {
  display: flex; gap: 12px; align-items: flex-start;
  padding: 12px 8px;
  border-bottom: 1px solid var(--c-border);
}
.notif-item:last-child { border-bottom: 0; }
.notif-item.is-unread { background: var(--c-primary-soft); border-radius: var(--r-sm); }

.notif-icon {
  width: 32px; height: 24px; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--c-muted);
}
.notif-icon svg { width: 20px; height: 20px; }
.notif-content { flex: 1; min-width: 0; }
.notif-title { font-weight: 500; margin-bottom: 2px; }
.notif-body  { font-size: 13px; line-height: 1.4; }
.notif-meta  { margin-top: 4px; }

/* ============================================================
 * E-mail replies in post sidebar
 * ============================================================ */

.email-reply-list {
  list-style: none; padding: 0; margin: 0;
}
.email-reply {
  padding: 10px 0;
  border-bottom: 1px solid var(--c-border);
  font-size: 13px;
}
.email-reply:last-child { border-bottom: 0; }
.email-reply.is-unprocessed { background: #fffbeb; margin: 0 -14px; padding-left: 14px; padding-right: 14px; }
.email-reply-head {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 8px;
  margin-bottom: 4px;
}
.email-reply-body {
  color: var(--c-text); line-height: 1.45;
  padding: 6px 10px;
  background: #fafbfc;
  border-left: 3px solid var(--c-border);
  border-radius: 2px;
  font-size: 12.5px;
  max-height: 200px; overflow-y: auto;
  word-wrap: break-word;
}

/* ============================================================
 * AI-assistent paneel
 * ============================================================ */

#ai-panel h3 { margin-bottom: 6px; }

.ai-model-row {
  display: flex; align-items: center;
  margin: 8px 0 12px;
  font-size: 13px;
}
.ai-model-select {
  flex: 1;
  padding: 6px 10px;
  border: 1px solid var(--c-border);
  border-radius: var(--r-sm);
  background: #fff;
  font: inherit; font-size: 13px;
}

.ai-actions {
  display: flex; flex-wrap: wrap; gap: 6px;
}
.ai-actions .btn { font-size: 12px; padding: 5px 9px; }
.ai-tones { margin-top: 12px; }
/* Snelle kleuren voor het AI-label (#44). */
.ai-label-swatches { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 6px 0; }
.ai-label-swatch {
  width: 28px; height: 28px; padding: 0; border-radius: 50%;
  border: 1px solid var(--c-border); cursor: pointer;
}
.ai-tones .field-label { display: block; margin-bottom: 6px; }
.ai-tones p { margin: 6px 0 0; }

#ai-custom-wrap textarea {
  font-size: 13px;
}

.ai-output {
  margin-top: 14px;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  background: #f8fafc;
  border-left: 3px solid var(--c-primary);
  position: relative;
}
.ai-output.is-loading {
  border-left-color: var(--c-muted);
}
.ai-output.is-loading::after {
  content: '';
  position: absolute;
  top: 12px; right: 12px;
  width: 12px; height: 12px;
  border: 2px solid var(--c-border);
  border-top-color: var(--c-primary);
  border-radius: 50%;
  animation: ai-spin 0.8s linear infinite;
}
@keyframes ai-spin {
  to { transform: rotate(360deg); }
}
.ai-output-meta {
  font-size: 11px;
  color: var(--c-muted);
  text-transform: uppercase;
  letter-spacing: .5px;
  margin-bottom: 6px;
}
.ai-output-text {
  margin: 0 0 10px;
  padding: 0;
  background: none;
  font-family: inherit;
  font-size: 13px;
  line-height: 1.5;
  white-space: pre-wrap;
  word-wrap: break-word;
  max-height: 260px;
  overflow-y: auto;
}
.ai-output-actions {
  display: flex; gap: 6px; flex-wrap: wrap;
}

/* -----  Herstelbalk conceptbuffer (GitLab #28)  --------- */
/* Waarschuwingskleur en niet groen: er staat iets klaar dat je nog moet
   BEKIJKEN. Een succeskleur zou zeggen dat het al goed is, en dan drukt
   niemand op "Herstellen". */
.draft-restore {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  margin: 0 0 16px; padding: 10px 12px;
  border-radius: var(--r-md);
  background: var(--tint-warn-bg); color: var(--tint-warn-fg);
  font-size: 14px;
}
.draft-restore-text { flex: 1 1 240px; min-width: 0; }

/* -----  Toepasbare punten uit een check (GitLab #25)  ---- */
.ai-suggestions {
  list-style: none; margin: 0 0 10px; padding: 0;
  display: flex; flex-direction: column; gap: 6px;
  max-height: 260px; overflow-y: auto;
}
.ai-sug {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px;
  padding: 8px 10px; border-radius: var(--r-sm);
  background: var(--c-surface-2);
  font-size: 13px; line-height: 1.45;
}
.ai-sug-text { font-weight: 600; flex: 1 1 100%; }
/* `min-width: 0` + break-word: citaat en vervanging zijn vrije tekst uit een
   taalmodel en kunnen één lang woord zijn; zonder dit duwt zo'n punt het hele
   AI-paneel open. */
.ai-sug-diff {
  flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere;
  color: var(--c-muted);
}
.ai-sug-diff del { text-decoration: line-through; }
.ai-sug-diff ins { text-decoration: none; color: var(--c-text); font-weight: 500; }
.ai-sug-apply { flex: 0 0 auto; }

/* Een punt zonder toepasbaar citaat: wel lezen, niets te klikken. */
.ai-sug.is-info { background: none; padding-left: 0; padding-right: 0; }

/* Al doorgevoerd, of het citaat is intussen weggetypt. Beide gedimd, maar
   `is-done` is een BEVESTIGING en `is-stale` een waarschuwing — dus niet
   dezelfde opmaak, anders lijkt "verlopen" op "gelukt". */
.ai-sug.is-done  { opacity: .55; }
.ai-sug.is-done .ai-sug-text::after { content: ' ✓'; color: var(--tint-ok-fg); }
.ai-sug.is-stale .ai-sug-diff { opacity: .5; font-style: italic; }

/* ============================================================
 * Notificatie tray dropdown (in topbar)
 * ============================================================ */

.notif-bell-wrap {
  position: relative;
}

.notif-bell {
  position: relative;
  display: inline-flex; align-items: center; justify-content: center;
  padding: 6px 8px;
  border-radius: var(--r-md);
  color: var(--c-muted);
  background: transparent;
  border: 0;
  font: inherit;
  cursor: pointer;
}
.notif-bell:hover { background: var(--c-bg); }
.notif-bell:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 2px;
}

.notif-tray {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: 360px;
  max-width: calc(100vw - 20px);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  z-index: 100;
  overflow: hidden;
  animation: tray-fade-in 0.12s ease-out;
}
@keyframes tray-fade-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: translateY(0); }
}

.notif-tray-head {
  display: flex; justify-content: space-between; align-items: center;
  padding: 12px 16px;
  border-bottom: 1px solid var(--c-border);
  background: #fafbfc;
}
.notif-tray-head strong { font-size: 14px; }

.notif-tray-body {
  max-height: 420px;
  overflow-y: auto;
}

.notif-tray-foot {
  padding: 10px 16px;
  border-top: 1px solid var(--c-border);
  background: #fafbfc;
  text-align: center;
}

.tray-list {
  list-style: none; padding: 0; margin: 0;
}
.tray-item {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 10px 16px;
  border-bottom: 1px solid var(--c-border);
  transition: background .12s;
}
.tray-item:last-child { border-bottom: 0; }
/* Hoverkleuren als transparante overlay zodat titel-tekst in zowel licht als
   donker thema leesbaar blijft (was: vaste lichte kleuren onder lichte tekst). */
.tray-item:hover { background: rgba(127, 127, 127, 0.10); }
.tray-item.is-unread {
  background: var(--c-primary-soft);
}
.tray-item.is-unread:hover { background: rgba(127, 127, 127, 0.16); }

.tray-mark-read {
  flex-shrink: 0;
  border: 0; background: transparent; cursor: pointer;
  color: var(--c-muted); font-size: 13px; line-height: 1;
  padding: 2px 6px; border-radius: 6px;
}
.tray-mark-read:hover { color: var(--c-primary); background: rgba(127, 127, 127, 0.12); }

.notif-tray-head-actions { display: inline-flex; gap: 10px; align-items: center; }
.linklike {
  border: 0; background: transparent; padding: 0; cursor: pointer;
  text-decoration: underline; font: inherit;
}

.tray-content {
  flex: 1; min-width: 0;
}
.tray-title {
  font-size: 13px; font-weight: 500;
  line-height: 1.3;
  word-wrap: break-word;
  margin-bottom: 2px;
}
.tray-title a { color: var(--c-text); }
.tray-title a:hover { color: var(--c-primary); }
.tray-body {
  font-size: 12px; color: var(--c-muted);
  line-height: 1.35;
  margin-bottom: 2px;
  word-wrap: break-word;
}
.tray-meta {
  font-size: 11px;
}

/* ============================================================
 * Preferences page
 * ============================================================ */

.channel-row {
  padding: 14px 0;
  border-bottom: 1px solid var(--c-border);
}
.channel-row:first-child { padding-top: 0; }
.channel-row:last-child  { border-bottom: 0; padding-bottom: 0; }

.channel-head {
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 10px;
}

.channel-logo {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px;
  border-radius: var(--r-sm);
  color: #fff;
  font-weight: 700; font-size: 14px;
  letter-spacing: .5px;
  flex-shrink: 0;
}

.channel-toggle {
  display: flex; align-items: center; gap: 8px;
  cursor: pointer;
  margin: 0;
  font-size: 15px;
}
.channel-toggle input[type="checkbox"] {
  width: 18px; height: 18px;
  cursor: pointer;
}

.channel-body {
  padding-left: 44px;        /* inlijn met text naast logo */
}
.channel-body .field { margin-bottom: 10px; }

/* Types grid */
.notif-types {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 8px 16px;
  margin-top: 8px;
}
.notif-type {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 8px 10px;
  border-radius: var(--r-sm);
  cursor: pointer;
  border: 1px solid transparent;
}
.notif-type:hover {
  background: #fafbfc;
  border-color: var(--c-border);
}
.notif-type input[type="checkbox"] {
  margin-top: 2px;
  width: 16px; height: 16px;
  cursor: pointer;
  flex-shrink: 0;
}
.notif-type span {
  display: flex; flex-direction: column; gap: 2px;
  font-size: 13px; line-height: 1.3;
}
.notif-type span strong { font-weight: 500; }
.notif-type span small {
  font-size: 11px;
  font-family: "SF Mono", Menlo, Consolas, monospace;
  color: var(--c-muted);
}

/* ============================================================
 * Mobile
 * ============================================================ */
@media (max-width: 720px) {
  /* De uitklapperbehandeling voor de plakbalk staat sinds de themasplitsing in
     assets/media/ui.css, gekoppeld aan `data-sg-bar` in plaats van aan `.topbar`:
     zo geldt hij voor élke schil en niet alleen voor die van Nova. */
  .channel-body { padding-left: 0; }
  .notif-types { grid-template-columns: 1fr; }
}

/* ============================================================
 * UI Polish — loading, empty states, mobile nav, shortcuts
 * ============================================================ */

/* ----- Top loading bar ---------------------------------- */
body.is-loading::before {
  content: '';
  position: fixed; top: 0; left: 0;
  height: 3px;
  background: linear-gradient(90deg, var(--c-primary), var(--c-focus));
  z-index: 9999;
  animation: top-progress 1.2s ease-in-out infinite;
  pointer-events: none;
}
@keyframes top-progress {
  0%   { width: 0%;   opacity: 1; }
  50%  { width: 70%;  opacity: 1; }
  100% { width: 100%; opacity: .4; }
}

/* ----- AJAX-loader met CSS-vangnet ----------------------
 *
 * Verschijnt bij een `data-ajax`-submit en blijft staan tot het antwoord er is
 * — of, als de uitkomst een paginawissel is, tot de nieuwe pagina verft.
 *
 * HET VANGNET IS BEWUST CSS EN GEEN setTimeout. Juist in de gevallen waarvoor
 * een vangnet bestaat kun je niet op JavaScript rekenen: een vastgelopen
 * script, een request die nooit terugkomt, of een browser die tijdens een
 * navigatie zijn timers bevriest. Een animatie loopt dan door. Na 15 seconden
 * verdwijnen de achtergrond en de spinner, en verschijnt de foutmelding.
 *
 * De wrapper vangt zelf geen klikken; alleen de zichtbare delen doen dat. Zodra
 * de achtergrond op `visibility: hidden` staat blokkeert hij dus niets meer en
 * heeft de gebruiker zijn pagina terug — zonder dat daar een regel JS aan te
 * pas komt.
 */
.sg-ajax-loader {
  position: fixed; inset: 0; z-index: 10000;
  display: none; align-items: center; justify-content: center;
  pointer-events: none;
}
.sg-ajax-loader.is-on { display: flex; }

.sg-ajax-backdrop {
  position: absolute; inset: 0;
  background: rgba(0, 0, 0, .35);
  pointer-events: auto;
}
.sg-ajax-spin {
  position: relative;
  width: 42px; height: 42px;
  border: 3px solid rgba(255, 255, 255, .35);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spinner-rotate .8s linear infinite;
}
.sg-ajax-timeout {
  position: relative;
  max-width: 360px; padding: 16px 20px;
  border-radius: var(--r-md, 8px);
  background: var(--c-surface, #fff); color: var(--c-text, #111);
  box-shadow: 0 8px 32px rgba(0, 0, 0, .28);
  text-align: center;
  pointer-events: auto;
  visibility: hidden; opacity: 0;
}
.sg-ajax-timeout p { margin: 0 0 12px; }
.sg-ajax-acties { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }


/* Het vangnet zelf. `99.99%` in plaats van een tweede keyframe op 100%: zo
 * springt de waarde pas aan het einde om en is er geen zichtbare fade die de
 * gebruiker als "hij doet iets" leest. */
.sg-ajax-loader.is-on .sg-ajax-backdrop { animation: sg-ajax-vangnet-uit 15s linear forwards; }
.sg-ajax-loader.is-on .sg-ajax-timeout  { animation: sg-ajax-vangnet-aan 15s linear forwards; }
/* Twee animaties tegelijk: blijven draaien én na 15 seconden verdwijnen. */
.sg-ajax-loader.is-on .sg-ajax-spin {
  animation: spinner-rotate .8s linear infinite,
             sg-ajax-vangnet-uit 15s linear forwards;
}

@keyframes sg-ajax-vangnet-uit {
  0%, 99.99% { opacity: 1; visibility: visible; }
  100%       { opacity: 0; visibility: hidden; }
}
@keyframes sg-ajax-vangnet-aan {
  0%, 99.99% { opacity: 0; visibility: hidden; }
  100%       { opacity: 1; visibility: visible; }
}

/* De draaiing is versiering en mag uit; het VANGNET is functioneel en blijft
 * staan — anders zit iemand met minder beweging juist vast achter een
 * overlay die nooit weggaat. */
@media (prefers-reduced-motion: reduce) {
  .sg-ajax-loader.is-on .sg-ajax-spin { animation: sg-ajax-vangnet-uit 15s linear forwards; }
}

/* ----- Hetzelfde paneel als MEDEDELING (sessie) ----------
 *
 * Het vangnet hierboven is een TIMER die na 15 seconden vermoedt dat er iets
 * misging. Dit is een VASTSTELLING: de server heeft net 401 gezegd, of de
 * afteller staat op de waarschuwingsgrens. Dus geen spinner en geen animatie —
 * `animation: none` is hier nodig omdat het paneel anders eerst 15 seconden
 * onzichtbaar blijft wachten op de vangnet-keyframe.
 *
 * De backdrop houdt `pointer-events: auto` en blokkeert dus de pagina eronder.
 * Dat is bedoeld: doorklikken op een pagina waarvan elke actie gaat falen
 * levert alleen maar meer meldingen op. De tekst eronder blijft leesbaar en
 * selecteerbaar zodra het paneel sluit.
 */
.sg-ajax-loader.is-sessie .sg-ajax-spin     { display: none; }
.sg-ajax-loader.is-sessie .sg-ajax-backdrop { animation: none; }
.sg-ajax-loader.is-sessie .sg-ajax-timeout {
  animation: none; visibility: visible; opacity: 1;
  max-width: 420px; text-align: left;
}
.sg-ajax-loader.is-sessie .sg-ajax-acties { justify-content: flex-start; }

/* De aftelling. tabular-nums zodat "9:07" niet smaller is dan "9:44" en de
 * knoppen niet elke seconde een pixel verschuiven. */
.sg-sessie-klok { font-variant-numeric: tabular-nums; font-weight: 600; }

/* ----- Inline spinner (in buttons) ---------------------- */
.spinner-inline {
  display: inline-block;
  width: 12px; height: 12px;
  border: 2px solid rgba(255, 255, 255, 0.35);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spinner-rotate 0.7s linear infinite;
  vertical-align: -2px;
  margin-right: 4px;
}
.btn-ghost .spinner-inline {
  border-color: rgba(37, 99, 235, 0.2);
  border-top-color: var(--c-primary);
}
/* BUITEN EEN KNOP. De regels hierboven zijn wit-op-gekleurd: precies goed in een
   `.btn-primary`, en op een kaart een witte ring op een witte achtergrond —
   onzichtbaar. Dat trof de standkaart van de avatar-opname, waar hij juist moet
   laten zien DAT de cron bezig is.

   `currentColor` en geen eigen token: zo erft hij de tekstkleur van de regel
   waarin hij staat en klopt hij vanzelf in alle vier de schillen, in licht én
   donker. Iets groter dan de knopvariant, want hier staat geen label naast dat
   de aandacht al trekt. */
.spinner-inline.is-los {
  width: 14px; height: 14px;
  border-color: color-mix(in srgb, currentColor 25%, transparent);
  border-top-color: currentColor;
  vertical-align: -3px;
}
@keyframes spinner-rotate {
  to { transform: rotate(360deg); }
}

/* ----- Mobile nav hamburger ----------------------------- */
.mobile-nav-btn {
  display: none;
  background: none;
  border: 0;
  padding: 10px;
  cursor: pointer;
  margin-left: auto;
}
.mobile-nav-btn span {
  display: block;
  width: 22px; height: 2px;
  background: var(--c-text);
  border-radius: 1px;
  transition: transform 0.2s, opacity 0.2s;
}
.mobile-nav-btn span + span { margin-top: 5px; }
.mobile-nav-btn[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}
.mobile-nav-btn[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.mobile-nav-btn[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

/* ----- Empty state ------------------------------------- */
.empty-state {
  text-align: center;
  padding: 48px 20px;
  color: var(--c-muted);
}
.empty-state-icon {
  display: block;
  width: 40px; height: 40px;
  margin: 0 auto 14px;
  color: var(--c-muted);
  opacity: .55;
}
.empty-state-icon svg { width: 100%; height: 100%; stroke-width: 1.4; }
.empty-state h2 {
  margin: 0 0 8px;
  color: var(--c-text);
  font-size: 18px;
}
.empty-state p {
  margin: 0 0 16px;
  max-width: 420px;
  margin-left: auto;
  margin-right: auto;
}

/* ----- Skeleton loader (voor lijsten) ------------------ */
.skeleton {
  background: linear-gradient(90deg, #f0f2f5 25%, #e4e7ef 50%, #f0f2f5 75%);
  background-size: 200% 100%;
  animation: skeleton-shimmer 1.4s ease-in-out infinite;
  border-radius: var(--r-sm);
}
.skeleton-line {
  height: 12px;
  margin: 8px 0;
}
.skeleton-line.short { width: 40%; }
.skeleton-line.medium { width: 70%; }
@keyframes skeleton-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* ----- Shortcut help modal ----------------------------- */
.sg-modal {
  position: fixed; inset: 0;
  background: rgba(15, 23, 42, 0.5);
  display: flex; align-items: center; justify-content: center;
  z-index: 1000;
  padding: 20px;
  animation: sg-modal-fade 0.15s ease-out;
}
@keyframes sg-modal-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.sg-modal-card {
  background: var(--c-surface);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  padding: 24px;
  max-width: 480px;
  width: 100%;
  animation: sg-modal-pop 0.18s cubic-bezier(0.2, 0, 0.1, 1);
}
@keyframes sg-modal-pop {
  from { opacity: 0; transform: scale(0.95) translateY(10px); }
  to   { opacity: 1; transform: scale(1)    translateY(0); }
}
.sg-modal-card h2 { margin: 0 0 16px; }
/* display:flex zou het hidden-attribuut anders overrulen. Sinds de globale
   `[hidden]`-regel bij de utilities is dit dubbelop; laten staan als lokaal
   vangnet, maar nieuwe componenten hebben zo'n eigen patch niet meer nodig. */
.sg-modal[hidden] { display: none; }

/* ----- Beelddossier in de mediamodal --------------------- */
.media-dossier            { display: grid; grid-template-columns: max-content 1fr;
                            gap: 4px 12px; margin: 0 0 12px; }
.media-dossier-row        { display: contents; }
.media-dossier-label      { color: var(--c-muted); font-size: .85em; }
.media-dossier-value      { font-size: .9em; word-break: break-word; }
/* Het <details> met de prompt overspant beide kolommen van de grid. */
.media-dossier-prompt     { grid-column: 1 / -1; margin-top: 4px; }
.media-dossier-prompt p   { margin: 6px 0 0; font-size: .85em; white-space: pre-wrap;
                            word-break: break-word; }
.media-dossier-refs       { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 8px; }
.media-dossier-refs img   { width: 64px; height: 64px; object-fit: cover;
                            border-radius: var(--r-sm); }

/* ----- Geavanceerd: AI-contextblokken --------------------
   De vinkjesstijl is bewust gescoped op .ai-context-blocks. `.checkbox-inline`
   is een bestaande klasse die ook in /settings/gamification, publishing en
   content-sources staat; die schermen hoeven hier niet mee te veranderen. */
.ai-context-host              { margin: 10px 0; }
.ai-context-warning           { margin: 0 0 8px; }
.ai-context-advanced          { border: 1px solid var(--c-border); border-radius: var(--r-md);
                                padding: 8px 12px; }
.ai-context-advanced summary  { cursor: pointer; font-weight: 600; }
.ai-context-advanced > p      { margin: 6px 0 10px; }
.ai-context-blocks            { display: flex; flex-direction: column; gap: 6px; }
.ai-context-blocks .checkbox-inline { display: flex; align-items: flex-start; gap: 8px; margin: 0; }
.ai-context-blocks .checkbox-inline input { margin-top: 3px; flex: none; }

/* "nog niet ingevuld" naast een aangevinkt blok. Geen alarmkleur: dit is
   informatie, geen fout — het blok mag gewoon aan blijven staan. De link
   opent een nieuw tabblad, dus het concept in de composer blijft staan.
   De globale [hidden]-regel verbergt hem als het blok wél inhoud heeft. */
.ai-context-empty             { display: inline-flex; align-items: center; gap: 6px;
                                margin-left: 6px; white-space: nowrap; }
.ai-context-empty-tag         { font-size: 11px; line-height: 1.6; padding: 0 6px;
                                border-radius: 10px; background: var(--c-border);
                                color: var(--c-muted); }
.ai-context-empty a           { font-size: 12px; }

/* Het bevestigingsvenster heeft geen [hidden]-toestand: het bestaat alleen
   zolang de vraag openstaat en wordt daarna uit de DOM verwijderd. */
.ai-context-confirm-card      { max-width: 460px; }
/* Vaste hoogte voor de wisselende waarschuwing: zonder deze regel springen de
   knoppen mee met de tekstlengte, en dan klikt iemand op de verkeerde. */
.ai-context-confirm-text      { margin: 0 0 20px; line-height: 1.5; min-height: 4.5em; }
.ai-context-confirm-actions   { display: flex; gap: 8px; justify-content: flex-end;
                                flex-wrap: wrap; }
.ai-context-confirm-accept[disabled] { opacity: .55; cursor: not-allowed; }

/* ----- Handmatig doorzetten ------------------------------ */
.manual-publish            { margin: 10px 0; }
.manual-publish-row        { display: flex; gap: 8px; align-items: center; margin-top: 8px; }
.manual-publish-row input[type="url"] { flex: 1; min-width: 0; }
/* Doorzetten naar een LinkedIn-groep (#61). De kaart staat in de smalle
   zijkolom: twee velden en een knop op één regel duwden de knop de kaart uit
   (gemeten in de browser). Velden dus elk op een eigen regel, de groepsknop
   krimpt mee en breekt zijn tekst af. */
.li-groups-row              { flex-wrap: wrap; }
.li-groups-row input        { flex: 1 1 100%; min-width: 0; }
.li-groups-row > a.btn      { flex: 1 1 0; min-width: 0; white-space: normal; text-align: left; overflow-wrap: anywhere; }
.li-groups-row > form       { flex: 0 0 auto; }

/* ----- Rondleidingen (tour.js) --------------------------- */
.sg-tour-overlay    { position: fixed; inset: 0; z-index: 1200; display: none;
                      background: rgba(15, 23, 42, 0.55); }
.sg-tour-spot       { position: fixed; border-radius: 8px; pointer-events: none;
                      box-shadow: 0 0 0 9999px rgba(15, 23, 42, 0.55);
                      transition: top .2s, left .2s, width .2s, height .2s; }
.sg-tour-pop        { position: fixed; z-index: 1201; display: none; width: 340px; max-width: calc(100vw - 24px);
                      background: var(--c-surface); border-radius: var(--r-lg);
                      box-shadow: var(--shadow-lg); padding: 16px;
                      transition: top .2s, left .2s; }
.sg-tour-pop-head   { display: flex; justify-content: space-between; align-items: flex-start; gap: 8px; }
.sg-tour-pop p      { margin: 8px 0 12px; font-size: .92rem; line-height: 1.5; }
.sg-tour-close      { background: none; border: none; cursor: pointer; font-size: 1rem;
                      color: var(--c-muted); padding: 0 2px; }
.sg-tour-pop-foot   { display: flex; justify-content: space-between; align-items: center; }
.sg-tour-count      { font-size: .8rem; color: var(--c-muted); }
.sg-tour-btns       { display: inline-flex; gap: 6px; }

/* ❓-menu in de topbar */
.tour-help-wrap     { position: relative; }
.sg-tour-menu       { position: absolute; right: 0; top: calc(100% + 8px); z-index: 900;
                      min-width: 280px; max-width: min(360px, calc(100vw - 24px));
                      overflow-y: auto; overscroll-behavior: contain;
                      background: var(--c-surface); border-radius: var(--r-lg);
                      box-shadow: var(--shadow-lg); padding: 6px; }
.sg-tour-menu button { display: flex; flex-direction: column; gap: 2px; width: 100%;
                      text-align: left; padding: 10px 12px; border: none; background: none;
                      cursor: pointer; border-radius: var(--r-sm); color: inherit; }
.sg-tour-menu button:hover { background: rgba(79, 70, 229, .10); }
.sg-tour-done       { font-size: .75rem; color: #16a34a; }
/* Weggeklikt: wél zichtbaar, maar zwakker dan afgerond — anders leest
   "later bekeken" als een prestatie. */
.sg-tour-later      { font-size: .75rem; color: var(--c-muted); }
.sg-tour-group      { font-size: .72rem; text-transform: uppercase; letter-spacing: .04em;
                      color: var(--c-muted); padding: 8px 12px 4px; }
.sg-tour-group + .sg-tour-group { padding-top: 8px; }
/* Teller op de ❓-knop. Positioneert tegen .tour-help-wrap (het enige
   position:relative in de keten, zie boven). */
.sg-tour-badge      { position: absolute; top: -2px; right: -2px; min-width: 16px; height: 16px;
                      padding: 0 4px; border-radius: 8px; background: var(--c-primary);
                      color: #fff; font-size: .65rem; line-height: 16px; text-align: center;
                      font-weight: 600; pointer-events: none; }

/* Superadmin: stappen-editor */
.tour-step-editor          { border: 1px solid var(--c-border, rgba(127,127,127,.25));
                             border-radius: var(--r-md); padding: 10px 12px; margin-bottom: 10px; }
.tour-step-editor summary  { cursor: pointer; }
.tour-step-editor form     { margin-top: 10px; }

/* ----- Kanaal feed-raster (fase 4) ----------------------- */
.channel-grid       { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; }
.channel-grid-cell  { position: relative; aspect-ratio: 1; display: block; overflow: hidden;
                      border-radius: 4px; background: var(--c-surface-2, rgba(127,127,127,.12));
                      text-decoration: none; color: inherit; }
.channel-grid-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }
.channel-grid-noimg { display: flex; align-items: center; justify-content: center; height: 100%;
                      padding: 10px; font-size: .8rem; text-align: center; color: var(--c-muted); }
.channel-grid-meta  { position: absolute; left: 6px; bottom: 6px; right: 6px; display: flex;
                      justify-content: space-between; align-items: center; gap: 4px; }
.channel-grid-meta .text-small { background: rgba(0,0,0,.55); color: #fff; padding: 1px 6px;
                      border-radius: 4px; font-size: .7rem; }

/* ----- Carrousel-studio (C1/C2) -------------------------- */
.cstudio-layout        { display: grid; grid-template-columns: minmax(0, 1fr) 380px; gap: 16px; }
@media (max-width: 900px) { .cstudio-layout { grid-template-columns: 1fr; } }
.cstudio-tabs          { margin-bottom: 12px; }
.cstudio-slide-editor  { border: 1px solid var(--c-border, rgba(127,127,127,.25));
                         border-radius: var(--r-md); padding: 10px 12px; margin-bottom: 10px; }
.cstudio-slide-editor input,
.cstudio-slide-editor textarea { width: 100%; margin-top: 6px; }
.cstudio-slide-head    { display: flex; justify-content: space-between; align-items: center; }
.cstudio-slide-tools   { display: inline-flex; gap: 2px; }
.cstudio-presets       { display: flex; gap: 10px; margin: 8px 0 14px; flex-wrap: wrap; }
.cstudio-preset        { width: 84px; height: 56px; border: 2px solid transparent; border-radius: var(--r-md);
                         cursor: pointer; position: relative; display: flex; align-items: flex-end;
                         justify-content: center; padding: 4px; }
.cstudio-preset:hover  { border-color: #93c5fd; }
.cstudio-preset span   { position: absolute; top: 6px; left: 6px; width: 18px; height: 8px; border-radius: 4px; }
.cstudio-preset small  { font-size: .68rem; }
/* Stapnavigatie onder elk paneel: de tabs beloven een route, dit loopt hem.
   `space-between` zodat Vorige links en Volgende rechts staat, ook wanneer er
   maar één van de twee is (stap 1 heeft geen Vorige). */
.cstudio-nav           { display: flex; justify-content: space-between; gap: 10px;
                         margin-top: 18px; padding-top: 14px;
                         border-top: 1px solid var(--c-border, rgba(127,127,127,.2)); }
.cstudio-nav .cstudio-nav-next { margin-left: auto; }

/* Eigen sjablonen (migratie 157): zelfde tegel als een preset, met de
   verwijderknop erop in plaats van ernaast. */
/* Volgorde van de media in de composer: item 1 is de cover van een carrousel. */
.media-item-order       { position: absolute; left: 4px; bottom: 4px; display: flex; gap: 3px; }
.media-move             { width: 22px; height: 22px; border: none; border-radius: var(--r-sm);
                          cursor: pointer; line-height: 1; font-size: .8rem;
                          background: rgba(0,0,0,.55); color: #fff; }
.media-move:hover       { background: rgba(0,0,0,.8); }
.media-item-pos         { position: absolute; top: 4px; left: 4px; min-width: 18px; height: 18px;
                          border-radius: 50%; background: rgba(0,0,0,.6); color: #fff;
                          font-size: .68rem; line-height: 18px; text-align: center; }

.cstudio-templates     { display: flex; gap: 10px; margin: 8px 0 14px; flex-wrap: wrap; }
.cstudio-template      { position: relative; }
.cstudio-template-del  { position: absolute; top: -6px; right: -6px; width: 20px; height: 20px;
                         border-radius: 50%; border: none; cursor: pointer; line-height: 1;
                         background: var(--c-error, #dc2626); color: #fff; font-size: .8rem; }
.cstudio-colors        { display: grid; grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 10px; }
.cstudio-colors input[type="color"] { width: 100%; height: 38px; border: 1px solid var(--c-border, rgba(127,127,127,.25));
                         border-radius: var(--r-sm); background: none; cursor: pointer; }
.cstudio-preview-card  { position: sticky; top: 80px; text-align: center; }
#cstudio-canvas        { width: 100%; max-width: 340px; height: auto; border-radius: var(--r-md);
                         box-shadow: var(--shadow-lg); }
.cstudio-preview-nav   { display: flex; justify-content: center; align-items: center; gap: 10px; margin-top: 10px; }

/* ----- Command palette (D1, ⌘K) -------------------------- */
.cmdk-overlay        { align-items: flex-start; padding-top: 12vh; }
.cmdk-card           { max-width: 560px; padding: 12px; }
.cmdk-input          { width: 100%; font-size: 1.05rem; padding: 10px 12px;
                       border: none; outline: none; background: transparent; color: inherit;
                       border-bottom: 1px solid var(--c-border, rgba(127,127,127,.25)); }
.cmdk-list           { list-style: none; margin: 8px 0 0; padding: 0; max-height: 46vh; overflow-y: auto; }
.cmdk-list li        { display: flex; justify-content: space-between; align-items: center;
                       gap: 12px; padding: 9px 12px; border-radius: var(--r-sm); cursor: pointer; }
.cmdk-list li.is-active,
.cmdk-list li:hover  { background: rgba(79,70,229,.10); }
.cmdk-list li.cmdk-empty { cursor: default; background: none; }
.cmdk-help           { margin: 8px 4px 0; }

/* ----- Setup-wizard (A5) --------------------------------- */
.setup-wizard          { padding: 0; overflow: hidden; }
/* Vlak in de merkkleur van het thema. Stond hier als
   linear-gradient(135deg, #4f46e5, #7c3aed): een diagonaal indigo-naar-violet
   dat noch Atlas noch Forum ooit gekozen had, en dat dus in twee van de drie
   schillen simpelweg de verkeerde kleur was. */
.setup-wizard-head     { background: var(--c-primary);
                         color: #fff; text-align: center; padding: 28px 20px; }
.setup-wizard-head h1  { margin: 0 0 4px; color: #fff; }
.setup-wizard-head p   { margin: 0; opacity: .85; }
.setup-steps-bar       { display: flex; align-items: center; justify-content: center;
                         gap: 8px; padding: 20px 16px 4px; flex-wrap: wrap; }
.setup-step-dot        { width: 30px; height: 30px; border-radius: 50%; display: inline-flex;
                         align-items: center; justify-content: center; font-weight: 600;
                         background: var(--c-surface-2, rgba(127,127,127,.12)); color: var(--c-muted); }
.setup-step-dot.is-active { background: var(--c-primary); color: #fff; }
.setup-step-dot.is-done   { background: #16a34a; color: #fff; }
.setup-step-label      { font-size: .85rem; color: var(--c-muted); }
/* Dot en label zijn één groep: de balk wrapt (vijf stappen sinds de
   campagnestap), en zonder deze wrapper kan de breuk TUSSEN een dot en zijn
   eigen label vallen — gemeten op 1280px, waar "Campagne" als losse regel
   onder de balk hing. De lijn mag krimpen zodat het wrappen later begint. */
.setup-step            { display: inline-flex; align-items: center; gap: 8px; }
.setup-step-line       { flex: 1 1 24px; min-width: 12px; max-width: 40px; height: 2px;
                         background: var(--c-border, rgba(127,127,127,.25)); }
/* Smal: de verbindingslijnen weg. Ze verbinden bij vijf gewrapte stappen toch
   niets meer — er bleven losse streepjes aan het begin en eind van elke regel
   hangen — en zonder lijnen passen de stappen in twee regels in plaats van
   drie. GEMETEN op 375px. */
@media (max-width: 720px) {
  .setup-step-line     { display: none; }
  .setup-steps-bar     { gap: 10px 14px; }
}
.setup-body            { padding: 20px 24px 28px; }
.setup-intent-grid     { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
                         gap: 10px; margin: 14px 0; }
.setup-intent-card     { display: flex; flex-direction: column; align-items: center; gap: 6px;
                         padding: 16px 10px; border: 2px solid var(--c-border, rgba(127,127,127,.25));
                         border-radius: var(--r-lg); cursor: pointer; text-align: center; }
.setup-intent-card:hover { border-color: var(--c-primary); }
.setup-intent-card:has(input:checked) { border-color: var(--c-primary); background: var(--c-primary-soft); }
.setup-intent-card input { position: absolute; opacity: 0; }
.setup-next-icon       { display: inline-flex; color: var(--c-muted); }
.setup-next-icon svg   { width: 22px; height: 22px; }
.setup-platform-grid   { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 6px; }
.setup-actions         { display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; flex-wrap: wrap; }
.setup-done            { text-align: center; }
.setup-done-icon       { width: 56px; height: 56px; margin: 8px auto 12px; border-radius: 50%;
                         background: rgba(22,163,74,.15); color: #16a34a; font-size: 1.8rem;
                         display: flex; align-items: center; justify-content: center; }
.setup-next-grid       { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
                         gap: 12px; margin: 18px 0; text-align: left; }
.setup-next-card       { display: flex; flex-direction: column; gap: 6px; padding: 16px;
                         border: 1px solid var(--c-border, rgba(127,127,127,.25));
                         border-radius: var(--r-lg); text-decoration: none; color: inherit; }
.setup-next-card:hover { border-color: var(--c-primary); }

/* ----- Topbar: AI-verbruiksteller (D2) ------------------- */
.topbar-ai-usage {
  font-size: 0.82rem; color: var(--c-muted);
  padding: 4px 8px; border-radius: var(--r-sm);
  background: var(--c-surface-2, rgba(127,127,127,.08));
  white-space: nowrap; cursor: default;
  display: inline-flex; align-items: center; gap: 6px;
}
.topbar-ai-usage svg { width: 14px; height: 14px; flex: 0 0 14px; }

/* ----- Mediabibliotheek: AI-generatie modal ------------- */
.media-ai-card                    { max-width: 720px; max-height: 88vh; overflow-y: auto; }
.media-ai-tabs                    { display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
.media-ai-refs .ai-image-cell img { max-width: 90px; }
.media-ai-input                   { width: 100%; }

.shortcut-list {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 16px;
}
.shortcut-list td {
  padding: 6px 0;
  font-size: 14px;
}
.shortcut-list td:first-child {
  width: 140px;
  white-space: nowrap;
}

kbd {
  display: inline-block;
  padding: 2px 6px;
  background: #f1f5f9;
  border: 1px solid #cbd5e1;
  border-radius: 3px;
  box-shadow: 0 1px 0 #cbd5e1;
  font-family: "SF Mono", Menlo, Consolas, monospace;
  font-size: 11px;
  font-weight: 600;
  color: var(--c-text);
  min-width: 16px;
  text-align: center;
}

/* ----- Form focus ring consistency --------------------- */
/* DEZE REGEL HAALDE DE FOCUS WEG EN GAF NIETS TERUG. Alleen velden binnen
   `.field` kregen hierboven een box-shadow als vervanging; elke andere select,
   elk zoekveld, elke losse checkbox en de Quill-editor stonden zonder énig
   focusteken. Gemeten met echte Tab-aanslagen: 40 van de 46 tabstops op
   /notifications/preferences en de complete editor op /posts/new (WCAG 2.4.7
   Focus Visible, AA).
   `:focus` blijft dus kaal — dat is de bedoeling, want een muisklik hoort geen
   ring te tonen — maar `:focus-visible` krijgt dezelfde ring als .btn en
   .notif-bell hierboven. Eén patroon voor de hele applicatie. */
select:focus, textarea:focus, input:focus {
  outline: none;
}
select:focus-visible, textarea:focus-visible, input:focus-visible,
[contenteditable="true"]:focus-visible, summary:focus-visible, a:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 2px;
}

/* ----- Pointer cursor on buttons (consistency) --------- */
button:not(:disabled), [role="button"]:not([aria-disabled="true"]) {
  cursor: pointer;
}
button:disabled { cursor: not-allowed; opacity: 0.6; }

/* ============================================================
 * Mobile: hamburger menu + nav drawer
 * ============================================================ */

@media (max-width: 840px) {
  .mobile-nav-btn { display: flex; flex-direction: column; justify-content: center; align-items: center; }

  /* WRAP MET EEN VASTE HOOGTE IS EEN OVERLAP. `.topbar-inner` staat hierboven
     op `height: 60px`; deze mediaquery zette daar `flex-wrap: wrap` bij zonder
     die hoogte los te laten. De tweede regel viel daardoor BUITEN de balk:
     gemeten op 375px stond `.topbar-right` op top 62 met een hoogte van 39,
     dus 40px over de pagina-inhoud heen — en hij ving daar de kliks van wat
     eronder lag (de "← Terug"-link op /posts/new en /settings/connections was
     niet aan te tikken). Een balk die wrapt moet meegroeien. */
  .topbar-inner {
    flex-wrap: wrap;
    height: auto;
    min-height: 60px;
    padding-top: 8px; padding-bottom: 8px;
  }

  .topbar-nav {
    display: none;
    position: absolute;
    top: 60px; left: 0; right: 0;
    background: var(--c-surface);
    border-bottom: 1px solid var(--c-border);
    box-shadow: var(--shadow-md);
    flex-direction: column;
    gap: 0;
    padding: 8px 0;
    z-index: 50;
  }
  .topbar-nav.is-open { display: flex; }
  .topbar-nav a {
    padding: 12px 20px;
    border-radius: 0;
    width: 100%;
  }

  .org-switcher {
    order: 10;
    width: 100%;
    margin: 8px 0 0;
    padding: 0;
    border: none;
  }
  .org-switcher select {
    max-width: 100%;
    width: 100%;
  }
  .org-switcher-form { width: 100%; }

  /* EN HIJ MOET KUNNEN KRIMPEN. Zonder wrap bleef deze rij 469px breed op een
     scherm van 375 en schoof de HELE pagina horizontaal mee (documentbreedte
     493). Het meldingenklokje stond daardoor op x=455: alleen bereikbaar
     doordat de mobiele browser de pagina uitzoomt, niet met een echte tik.
     Zelfde vangrail als `.atlas-actions` in atlas.css. */
  .topbar-right {
    margin-left: auto;
    flex-wrap: wrap;
    justify-content: flex-end;
    min-width: 0;
  }
  .topbar-right .nav-search { flex: 0 1 auto; min-width: 0; }
  .topbar-right .nav-search input { width: 100%; min-width: 0; }
}

@media (max-width: 720px) {
  .topbar-nav { top: 60px; }

  /* Verberg brand-text bij heel smal */
  .brand-text { display: none; }

  .user-chip { display: none; }

  /* Page header stapelt ipv naast elkaar */
  .page-header {
    flex-direction: column;
    align-items: stretch;
  }
}

/* ========================================================================
   Post editor: multi-channel + live preview
   ======================================================================== */
.post-editor-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 360px;
  gap: 20px;
  align-items: start;
}
@media (max-width: 1024px) {
  .post-editor-layout { grid-template-columns: 1fr; }
}

.channel-checkboxes {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.channel-checkbox-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  border: 1px solid var(--c-border, #d0d5dd);
  border-radius: 6px;
  cursor: pointer;
  /* WAS #fff. In donkere weergave stond daar lichte tekst op: contrast 1.14,
     de kanaalkiezer was praktisch onleesbaar. `--border` bestaat trouwens niet
     als token — dat was al een stille terugval op #d0d5dd. */
  background: var(--c-surface);
  font-size: 14px;
}
.channel-checkbox-item input[type="checkbox"] { margin: 0; }
/* Een aangevinkt kanaal droeg hier `background: #f3efff` — een LITERAL in de
   gedeelde laag, en die kan geen enkel thema bereiken. In donkere modus bleef
   de tekstkleur licht en werd de achtergrond bijna wit: GEMETEN 1.01:1, ver
   onder de 4.5:1 van WCAG 2.2 AA, waardoor "LinkedIn — Dev LinkedIn" op een
   aangevinkt kanaal onleesbaar was. Dat trof /posts/new en /posts/{id}/edit,
   niet alleen de wizard. De achtergrond hoort bij een kleurpáár: alleen de
   `bg` vertalen laat de tekst op de erfkleur staan en dan komt hetzelfde
   probleem in een ander thema terug. */
.channel-checkbox-item:has(input:checked) {
  border-color: var(--c-primary, #5b21b6);
  background: var(--tint-prim-bg);
  color: var(--tint-prim-fg);
}

.post-preview-panel { position: sticky; top: 16px; }
.preview-header { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 10px; }
.preview-card {
  border: 1px solid var(--c-border, #d0d5dd);
  border-radius: 8px;
  padding: 12px;
  margin-bottom: 10px;
  background: #fff;
}
.preview-card-head { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
.preview-title { font-weight: 600; margin-bottom: 6px; }
.preview-body { font-size: 14px; line-height: 1.45; white-space: pre-wrap; word-wrap: break-word; }
.preview-footer { margin-top: 8px; font-size: 12px; color: #667085; }
.preview-footer.is-over { color: #b42318; }

.calendar-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: flex-end;
  margin-bottom: 12px;
}
.calendar-filters .inline-field { min-width: 200px; }
.calendar-filters select { padding: 6px 8px; }

/* ========================================================================
   Dashboard permissions overview
   ======================================================================== */
.perm-table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 10px;
}
.perm-table th, .perm-table td {
  padding: 10px 12px;
  border-bottom: 1px solid #eef1f5;
  text-align: left;
  vertical-align: top;
}
.perm-table tr.perm-denied { color: #667085; }
/* Eén kolom per rol (#49); de eigen rol gemarkeerd. */
.perm-table--rollen th.perm-rol,
.perm-table--rollen td.perm-rol { text-align: center; white-space: nowrap; }
.perm-table--rollen .perm-own { background: var(--c-primary-soft); }
.perm-own-label { display: block; font-size: 10px; text-transform: none; letter-spacing: 0; }
.perm-table tr.perm-allowed td:first-child { border-left: 3px solid #12b76a; }
.perm-badge {
  display: inline-block;
  padding: 3px 8px;
  border-radius: 10px;
  font-size: 12px;
  font-weight: 600;
}
.perm-badge-yes { background: #d1fadf; color: #027a48; }
.perm-badge-no  { background: #f2f4f7; color: #667085; }

/* Alleen de vorm. De KLEUR van .role-pill en van de vier rolvarianten staat in
   assets/media/ui.css op tint-tokens; dat bestand laadt na dit bestand en bij
   gelijke specificiteit won het dus altijd al. De literals die hier stonden
   (#f4ebff/#6941c6 en drie zusters) tekenden nergens meer. */
.role-pill {
  display: inline-block;
  padding: 3px 8px;
  border-radius: 10px;
  font-size: 12px;
  font-weight: 600;
}

.task-subject { margin: 6px 0 4px; }
.task-description { margin-bottom: 6px; }

/* ========================================================================
   Media uploader
   ======================================================================== */
.media-uploader { display: block; }
.media-upload-btn { display: inline-block; }
.media-hint { margin-left: 10px; }
.media-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 8px;
  margin-top: 10px;
}
.media-item {
  position: relative;
  border: 1px solid #e4e7ec;
  border-radius: 6px;
  overflow: hidden;
  background: #f9fafb;
  aspect-ratio: 1 / 1;
}
.media-item img, .media-item video {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.media-item.is-uploading {
  display: flex; align-items: center; justify-content: center;
  color: #667085; font-size: 12px;
}
.media-remove {
  position: absolute; top: 4px; right: 4px;
  width: 22px; height: 22px; border-radius: 50%;
  border: none; background: rgba(0,0,0,0.65); color: #fff;
  cursor: pointer; line-height: 1; font-size: 16px;
}
.media-remove:hover { background: #b42318; }

/* ========================================================================
   Platform-authentieke preview-cards
   ======================================================================== */
.pv-wrap { margin-bottom: 16px; }
.pv-wrap-head {
  display: flex; align-items: center; gap: 6px;
  font-size: 12px; margin-bottom: 6px; color: #344054;
}
.pv-counter { margin-top: 4px; font-size: 11px; color: #667085; }
.pv-counter.is-over { color: #b42318; font-weight: 600; }

.pv-card {
  border: 1px solid #e4e7ec;
  border-radius: 10px;
  background: #fff;
  /* Expliciete donkere tekstkleur: de kaart is altijd wit (platform-look),
     dus de lichte tekstkleur van het dark theme mag hier niet doorerven. */
  color: #0f172a;
  overflow: hidden;
  font-size: 13px;
  line-height: 1.4;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.pv-head {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px;
}
.pv-name { font-weight: 600; font-size: 13px; color: #0f172a; }
.pv-sub  { color: #667085; font-size: 11px; font-weight: 400; }
.pv-body { padding: 4px 12px 10px; white-space: pre-wrap; word-wrap: break-word; }
.pv-avatar {
  width: 36px; height: 36px; border-radius: 50%;
  /* Vlak. Was linear-gradient(135deg, #6941c6, #12b76a) — paars naar groen,
     een combinatie die geen enkel platform op een avatar zet. Deze kaart doet
     een echte feed na; een verloop verraadt meteen dat het een imitatie is. */
  background: #5b6472;
  color: #fff; font-weight: 700; font-size: 15px;
  display: flex; align-items: center; justify-content: center;
}

.pv-media { display: grid; gap: 2px; background: #000; }
.pv-media-single { grid-template-columns: 1fr; }
.pv-media-grid.pv-media-count-2 { grid-template-columns: 1fr 1fr; }
.pv-media-grid.pv-media-count-3 { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
.pv-media-grid.pv-media-count-3 .pv-media-item:first-child { grid-row: span 2; }
.pv-media-grid.pv-media-count-4 { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
.pv-media-item {
  width: 100%;
  aspect-ratio: 4 / 3;
  background-size: cover; background-position: center;
  background-color: #111;
  position: relative;
}
.pv-media-single .pv-media-item { aspect-ratio: 16 / 9; }
.pv-media-item video { width: 100%; height: 100%; object-fit: cover; display: block; }
.pv-media-more {
  position: absolute; right: 0; bottom: 0;
  background: rgba(0,0,0,0.7); color: #fff;
  padding: 4px 8px; font-size: 12px; border-top-left-radius: 6px;
}

/* LinkedIn */
.pv-linkedin { border-color: #d0d5dd; }
.pv-linkedin .pv-li-title { padding: 0 12px 6px; font-weight: 600; color: #0f172a; }
.pv-li-actions {
  display: flex; justify-content: space-around;
  padding: 6px 4px; border-top: 1px solid #eef1f5;
  color: #475467; font-size: 12px;
}

/* Facebook */
.pv-facebook { font-family: 'Segoe UI', Helvetica, Arial, sans-serif; }
.pv-fb-actions {
  display: flex; justify-content: space-around;
  padding: 8px 4px; border-top: 1px solid #eef1f5;
  color: #65676b; font-size: 13px; font-weight: 600;
}

/* Instagram */
.pv-instagram .pv-media { aspect-ratio: 1 / 1; }
.pv-instagram .pv-media-item { aspect-ratio: 1 / 1; }
.pv-ig-placeholder {
  aspect-ratio: 1 / 1; background: #fafafa;
  display: flex; align-items: center; justify-content: center;
  color: #8e8e8e; font-size: 12px; text-align: center; padding: 20px;
  border-top: 1px solid #efefef; border-bottom: 1px solid #efefef;
}
.pv-ig-icons {
  padding: 8px 12px; font-size: 18px; letter-spacing: 6px;
  display: flex; justify-content: space-between;
}
.pv-ig-icons .pv-right { letter-spacing: 0; }
.pv-instagram .pv-body strong:first-child { margin-right: 4px; }

/* X (Twitter) */
.pv-x { background: #fff; color: #0f1419; }
.pv-x .pv-head { padding-bottom: 4px; }
.pv-x .pv-body { font-size: 15px; line-height: 1.3; }
.pv-x-actions {
  display: flex; justify-content: space-around;
  padding: 8px 4px; color: #536471; font-size: 14px;
}
.pv-x .pv-media-item { aspect-ratio: 16 / 9; border-radius: 12px; overflow: hidden; }
.pv-x .pv-media { padding: 0 12px 8px; background: transparent; }

/* YouTube */
.pv-youtube { padding: 0; }
.pv-yt-frame { background: #000; }
.pv-yt-frame video, .pv-yt-thumb {
  width: 100%; aspect-ratio: 16 / 9; background-size: cover; background-position: center; background-color: #000;
  display: block;
}
.pv-yt-placeholder {
  display: flex; align-items: center; justify-content: center;
  color: #aaa; font-size: 12px;
}
.pv-yt-meta { display: flex; gap: 10px; padding: 10px 12px; }
.pv-yt-text { flex: 1; min-width: 0; }
.pv-yt-title { font-weight: 600; color: #0f172a; margin-bottom: 3px; }
.pv-yt-desc {
  padding: 6px 0 0; font-size: 12px; color: #475467;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}

/* --- Platform-spec preview controls & frame -------------------------- */
.pv-controls {
  display: flex; flex-wrap: wrap; gap: 8px 12px; align-items: center;
  padding: 8px 10px; margin: 6px 0;
  background: #f8fafc; border: 1px solid #e4e7ec; border-radius: 6px;
  font-size: 12px; color: #344054;
}
.pv-controls label { display: inline-flex; align-items: center; gap: 4px; }
.pv-controls select {
  font-size: 12px; padding: 2px 6px; border: 1px solid #d0d5dd; border-radius: 4px;
  background: #fff; color: #0f172a;
}
.pv-safe-toggle { cursor: pointer; }
.pv-safe-toggle input { margin: 0 2px 0 0; }

.pv-render-rule {
  padding: 4px 2px 8px; font-size: 11px; line-height: 1.4;
}

/* Wat het platform met DIT beeld doet. De verhoudingskiezer die hier stond
   besliste niets — het platform krijgt de originele bytes — en is vervangen
   door de uitkomst: blijft het beeld heel, of snijdt de feed het bij?

   VIA DE TINT-TOKENS en niet met een vaste kleur. `--tint-ok-fg` en
   `--tint-warn-fg` dragen per weergave al de leesbare variant; een
   hardgecodeerde #15803d haalt op donker 2.2:1 en dat is precies het defect
   dat `.text-error` ooit had. De terugvalwaarden zijn de lichte modus, zodat
   een schil zonder deze tokens niet zwart-op-zwart eindigt. */
.pv-frame-note {
  margin: 6px 0 0; padding: 0; line-height: 1.45;
}
.pv-frame-note.pv-frame-ok   { color: var(--tint-ok-fg,   #15803d); }
.pv-frame-note.pv-frame-crop { color: var(--tint-warn-fg, #b45309); }

.pv-frame {
  position: relative; width: 100%; background: #000;
  overflow: hidden; display: block;
}
.pv-frame img, .pv-frame video {
  width: 100%; height: 100%; display: block;
}
.pv-frame.pv-empty {
  display: flex; align-items: center; justify-content: center;
  background: #f2f4f7; color: #667085; font-size: 12px;
  border: 1px dashed #d0d5dd;
}

.pv-grid { display: grid; gap: 2px; width: 100%; height: 100%; background: #000; }
.pv-grid-2 { grid-template-columns: 1fr 1fr; }
.pv-grid-3 { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
.pv-grid-3 .pv-grid-item:first-child { grid-row: span 2; }
.pv-grid-4 { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
.pv-grid-item { position: relative; overflow: hidden; }
.pv-grid-item img, .pv-grid-item video { width: 100%; height: 100%; display: block; }

/* Carrousel in de composer: dezelfde bolletjes en teller als op de
   reviewkaart (.rv-carousel-*), zodat auteur en reviewer hetzelfde zien. */
.pv-carousel-count      { position: absolute; top: 8px; right: 8px; padding: 2px 9px;
                          border-radius: 999px; background: rgba(0,0,0,.55); color: #fff;
                          font-size: .72rem; line-height: 1.5; }
.pv-carousel-dots       { position: absolute; left: 0; right: 0; bottom: 8px;
                          display: flex; justify-content: center; gap: 5px; }
.pv-carousel-dots i     { width: 6px; height: 6px; border-radius: 50%;
                          background: rgba(255,255,255,.45); }
.pv-carousel-dots i.is-active { background: #fff; }
.pv-warn                { margin: 6px 0 0; font-size: .78rem; color: var(--c-warning, #b45309); }

.pv-more {
  position: absolute; right: 8px; bottom: 8px;
  background: rgba(0,0,0,0.6); color: #fff;
  padding: 2px 8px; border-radius: 12px; font-size: 12px; font-weight: 600;
}

.pv-safe {
  position: absolute; pointer-events: none;
  outline: 1px dashed rgba(255,255,255,0.85);
  outline-offset: -1px;
  box-shadow: 0 0 0 9999px rgba(0,0,0,0.08) inset;
}
.pv-safe span {
  position: absolute; top: 4px; left: 6px;
  font-size: 10px; font-weight: 600; letter-spacing: 0.04em;
  color: #fff; text-shadow: 0 1px 2px rgba(0,0,0,0.6);
  text-transform: uppercase;
}

/* --- Review: afbeelding-annotaties & comment-threads --------------- */
.review-media-item          { margin: 0 0 20px 0; }
.annotate-stage             { position: relative; display: inline-block; max-width: 100%; }
.annotate-stage img         { max-width: 100%; display: block; user-select: none; -webkit-user-drag: none; }
.annotate-canvas            { position: absolute; top: 0; left: 0; cursor: crosshair; touch-action: none; }
.annotate-toolbar           { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 6px; }
.annotate-color             { width: 22px; height: 22px; border-radius: 50%; border: 2px solid transparent;
                              cursor: pointer; padding: 0; }
.annotate-color.is-active   { border-color: #111827; box-shadow: 0 0 0 2px #fff inset; }
.annotate-tool.is-active    { background: #eef2ff; font-weight: 600; }

.annotation-lightbox        { position: fixed; inset: 0; z-index: 1000; background: rgba(17,24,39,.75);
                              display: flex; align-items: center; justify-content: center; padding: 24px; }
.annotation-lightbox-inner  { position: relative; }
.annotation-lightbox-inner img    { max-width: 90vw; max-height: 82vh; display: block; border-radius: 6px; }
.annotation-lightbox-inner canvas { position: absolute; top: 0; left: 0; pointer-events: none; }
.annotation-lightbox-close  { position: absolute; top: -14px; right: -14px; }

.review-comments            { margin-top: 32px; }
.review-comment-list        { list-style: none; padding: 0; margin: 0 0 24px 0; }
.review-comment             { padding: 10px 0; border-bottom: 1px solid #e5e7eb; }
.review-comment-reply       { margin-left: 28px; border-left: 3px solid #e5e7eb; padding-left: 12px; }
.review-comment-head        { display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
/* GEEN white-space: pre-wrap. Hier stond die wél, terwijl de body al door
   nl2br() ging: nl2br() VERWIJDERT de \n niet, het zet er een <br /> vóór.
   Met pre-wrap telde elke regelovergang dus twee keer en stond elke reactie op
   de publieke reviewpagina met dubbele regelafstand. De interne kant had het
   niet, want die gebruikte e() zonder nl2br. */
.review-comment-body        { margin-top: 4px; }
.review-comment .btn-reply,
.review-comment .btn-view-annotation { margin-top: 6px; margin-right: 6px; }

/* Honeypot: onzichtbaar voor mensen, invulbaar voor bots */
.hp-field                   { position: absolute; left: -9999px; top: -9999px; height: 1px;
                              overflow: hidden; }

/* Comment-threads in de posteditor */
.comment-reply-indent       { margin-left: 28px; border-left: 3px solid #e5e7eb; padding-left: 12px; }

/* --- Inspiratie-paneel (posteditor) -------------------------------- */
.inspiration-list           { list-style: none; padding: 0; margin: 0; }
.inspiration-item           { padding: 10px 0; border-bottom: 1px solid #e5e7eb; }
.inspiration-item:last-child{ border-bottom: none; }
.inspiration-snippet        { margin: 4px 0 6px 0; }
.inspiration-actions        { display: flex; flex-wrap: wrap; gap: 4px; }
.inspiration-thumb          { max-width: 100%; max-height: 120px; border-radius: 6px; margin-top: 6px; }
.inspiration-lightbox-inner { background: #fff; border-radius: 8px; padding: 20px;
                              max-width: min(680px, 92vw); max-height: 85vh; overflow: auto; }
.inspiration-fulltext       { white-space: pre-wrap; word-break: break-word; font-family: inherit;
                              font-size: 14px; margin: 12px 0; }

/* --- Per-lid rechten-editor (team) --------------------------------- */
.perms-row > td             { background: #f9fafb; }
.perms-form                 { padding: 4px 2px 8px; }
.perms-grid                 { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
                              gap: 6px 16px; margin: 8px 0 12px; }
.perms-item                 { display: flex; align-items: center; gap: 8px; cursor: pointer; }
.perms-item span            { font-size: 14px; }

/* --- Externe beoordeling (review-link) ----------------------------- */
.review-decision            { margin-top: 28px; padding-top: 20px; border-top: 2px solid #e5e7eb; }
.review-decision-list       { list-style: none; padding: 0; margin: 0 0 16px 0; }
.review-decision-list li    { padding: 8px 0; border-bottom: 1px solid #f0f0f0; }

/* Overzicht van een campagnebrede reviewlink (views/posts/campaign-review.php).
   De titel links, de knop of badge rechts; wrap omdat een posttitel lang is en
   de knop anders op smalle schermen buiten beeld loopt. */
.sg-campagne-review         { list-style: none; padding: 0; margin: 0 0 24px; }
.sg-campagne-review-rij     { display: flex; align-items: center; justify-content: space-between;
                              gap: 12px; flex-wrap: wrap; padding: 14px 16px;
                              border-bottom: 1px solid var(--c-border); }
.sg-campagne-review-rij:last-child { border-bottom: 0; }
.sg-campagne-review-titel   { font-weight: 600; flex: 1; min-width: 180px; }

/* --- Mediabibliotheek ----------------------------------------------- */
/* wrap: zonder deze regel stonden het bronfilter en de zoekknop op 375px op
   x=287 en x=410 — buiten beeld, met een documentbreedte van 496. */
.media-lib-filter           { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }
.media-lib-filter input[type="text"] { flex: 1; }
.media-lib-grid             { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; }
.media-lib-item             { display: flex; flex-direction: column; gap: 8px; }
.media-lib-thumb            { display: block; }
.media-lib-thumb img,
.media-lib-thumb video      { width: 100%; height: 140px; object-fit: cover; border-radius: 6px; display: block; }
.media-lib-alt              { display: flex; gap: 6px; }
.media-lib-alt input        { flex: 1; font-size: 13px; }

/* --- "Uit bibliotheek"-picker (posteditor) --------------------------- */
.media-picker               { background: #fff; border-radius: 8px; padding: 16px;
                              width: min(720px, 92vw); max-height: 85vh; overflow: auto; }
.media-picker-head          { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; }
.media-picker-search        { flex: 1; }
.media-picker-grid          { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 10px; }
.media-picker-cell          { position: relative; border: 2px solid transparent; border-radius: 6px;
                              padding: 0; cursor: pointer; background: none; overflow: hidden; }
.media-picker-cell:hover    { border-color: #2563eb; }
.media-picker-cell img,
.media-picker-cell video    { width: 100%; height: 90px; object-fit: cover; display: block; }
.media-picker-cell.is-added { opacity: .5; cursor: default; }
.media-picker-added         { position: absolute; top: 4px; right: 4px; background: #16a34a; color: #fff;
                              border-radius: 50%; width: 20px; height: 20px; line-height: 20px;
                              font-size: 12px; text-align: center; }

/* --- Onboarding-wizard ---------------------------------------------- */
/* Tokens, geen literals. Stond hier als #e5e7eb (spoor) en #2563eb (vulling):
   het spoor bleef daardoor lichtgrijs in donkere weergave, en de vulling was
   een blauw dat geen van de drie thema's voert. Het widget op het dashboard
   deed dit al goed via .lb-level-bar; deze balk op /onboarding niet. */
.onboarding-progress        { height: 8px; background: var(--c-surface-2); border-radius: 4px; margin-bottom: 16px; overflow: hidden; }
.onboarding-progress-bar    { height: 100%; background: var(--c-primary); border-radius: 4px; transition: width .3s; }
.onboarding-list            { list-style: none; padding: 0; margin: 0; }
/* wrap: de actieknoppen stonden op 375px op x=246 met een breedte van 144 en
   liepen 15px buiten beeld. `#e5e7eb` was bovendien hardgezet en volgde de
   weergave niet. */
.onboarding-step            { display: flex; align-items: flex-start; gap: 12px; padding: 12px 0;
                              flex-wrap: wrap; border-bottom: 1px solid var(--c-border, #e5e7eb); }
.onboarding-step:last-child { border-bottom: none; }
.onboarding-check           { flex: 0 0 28px; width: 28px; height: 28px; line-height: 28px; text-align: center;
                              border-radius: 50%; background: #e5e7eb; font-weight: 600; font-size: 14px; }
.onboarding-step.is-done .onboarding-check { background: #16a34a; color: #fff; }
.onboarding-step.is-done .onboarding-body strong { text-decoration: line-through; opacity: .7; }
.onboarding-body            { flex: 1; }
.onboarding-body p          { margin: 2px 0 0; }
.onboarding-action          { flex: 0 0 auto; display: flex; align-items: center; gap: 6px; }
.onboarding-action form     { margin: 0; }
/* Overgeslagen is een DERDE toestand, niet "bijna gedaan": geen groen vinkje
   en geen open bolletje, maar zichtbaar terzijde geschoven mét een weg terug. */
.onboarding-step.is-skipped .onboarding-body strong { text-decoration: line-through; opacity: .55; }
.onboarding-step.is-skipped .onboarding-body p      { opacity: .55; }

/* --- Globale zoekfunctie -------------------------------------------- */
.nav-search input           { width: 160px; padding: 6px 10px; border: 1px solid #d1d5db;
                              border-radius: 6px; font-size: 14px; transition: width .2s; }
.nav-search input:focus     { width: 220px; }
.search-result-list         { list-style: none; padding: 0; margin: 0; }
.search-result-list li      { padding: 8px 0; border-bottom: 1px solid #f0f0f0; }
.search-result-list li:last-child { border-bottom: none; }

/* -------------------- Native koppelingen (LinkedIn/Meta) -------------------- */
/* Veiligheidsuitleg boven "Account koppelen". Alleen ritme en regellengte:
   GEEN kleuren, want die erft dit blok van .card en dus van het actieve thema.
   Een hardgecodeerde kleur hier zou precies de licht-modusval zijn waar
   .text-error op 2.20:1 in het donker aan onderdoor ging. */
.security-list              { margin: 0; padding-left: 1.15rem; max-width: 52rem; }
.security-list li           { margin: .45rem 0; line-height: 1.55; }

.connect-grid               { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 24px; }
.connect-grid h3            { margin: 0 0 4px; }
.connection-row             { display: flex; justify-content: space-between; align-items: flex-start;
                              gap: 16px; padding: 12px 0; border-bottom: 1px solid #f0f0f0; }
.connection-row:last-child  { border-bottom: none; }
.connection-actions         { display: flex; gap: 8px; flex-shrink: 0; }
.target-list                { list-style: none; padding: 0; margin: 6px 0 0; }
.target-list li             { padding: 2px 0; }
.channel-variant-item       { display: inline-flex; align-items: center; gap: 6px; margin: 0 0 6px 26px; }
.connection-save-status.is-ok  { color: #067647; font-weight: 600; }
.connection-save-status.is-err { color: #b42318; font-weight: 600; }

/* -------------------- Capaciteiten per platform (koppelingen) -------------------- */
.capabilities-grid          { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 20px; margin: 8px 0 16px; }
/* De hardgezette kleuren hieronder volgden de weergave niet: in donkere
   weergave stond #067647 op #14141f (contrast 3.21) en #b42318 op 2.78. De
   --tint-*-fg-familie draagt per weergave de leesbare variant. */
.capability-card            { border: 1px solid var(--c-border, #ececec); border-radius: 8px; padding: 14px 16px; }
.capability-card h3         { margin: 0 0 8px; display: flex; align-items: center; gap: 8px; }
.capability-heading         { margin: 10px 0 4px; font-size: 12px; font-weight: 700; text-transform: uppercase;
                              letter-spacing: .03em; color: var(--c-muted, #667085); }
.capability-heading.capability-yes { color: var(--tint-ok-fg, #067647); }
.capability-heading.capability-no  { color: var(--tint-err-fg, #b42318); }
.capability-list            { list-style: none; padding: 0; margin: 0; font-size: 14px; }
.capability-list li         { padding: 2px 0 2px 20px; position: relative; }
.capability-list-yes li::before   { content: "✓"; position: absolute; left: 0; color: var(--tint-ok-fg, #067647); font-weight: 700; }
.capability-list-no li::before    { content: "✕"; position: absolute; left: 0; color: var(--tint-err-fg, #b42318); font-weight: 700; }
.capability-list-needs li::before { content: "•"; position: absolute; left: 4px; color: var(--c-muted, #98a2b3); }

/* -------------------- Koppel-instructies (setup guides) -------------------- */
/* `#fafafa` was hardgezet: in donkere weergave stond de lichte tekst van
   --c-text daarop, contrast 1.09 — de hele koppelinstructie was onleesbaar. */
.setup-guide                { border: 1px solid var(--c-border, #ececec); border-radius: 8px; padding: 4px 14px;
                              margin-top: 12px; background: var(--c-surface-2, #fafafa); }
.setup-guide > summary      { cursor: pointer; padding: 10px 0; font-size: 14px; }
.setup-guide[open] > summary { border-bottom: 1px solid var(--c-border, #ececec); margin-bottom: 10px; }
.setup-steps                { margin: 4px 0 10px; padding-left: 22px; }
.setup-steps li             { padding: 3px 0; font-size: 14px; }

/* -------------------- Handmatige statistieken -------------------- */
.manual-metrics            { margin-top: 14px; padding-top: 12px; border-top: 1px solid #ececec; }
.manual-metrics-form       { margin-top: 10px; }
.manual-metrics-grid       { display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: 8px; margin-bottom: 8px; }
.manual-metrics-grid label { display: flex; flex-direction: column; font-size: 12px; color: #667085; gap: 3px; }
.manual-metrics-grid input { padding: 5px 7px; border: 1px solid #d0d5dd; border-radius: 6px; font-size: 14px; }

/* -------------------- Sluitbare meldingen (× wegklikken) -------------------- */
.alert-dismissible         { position: relative; padding-right: 40px; }
.alert-close               { position: absolute; top: 6px; right: 8px; width: 24px; height: 24px;
                             padding: 0; line-height: 22px; font-size: 20px; text-align: center;
                             background: transparent; border: 0; cursor: pointer; color: inherit;
                             opacity: .55; border-radius: 4px; }
.alert-close:hover         { opacity: 1; background: rgba(0, 0, 0, .06); }

/* -----  AI-beeldgeneratie (posteditor)  ------------------ */
.ai-image                   { margin-top: 8px; }
.ai-image-panel             { margin-top: 8px; padding: 14px; }
.ai-image-prompt            { width: 100%; resize: vertical; }
.ai-image-suggest           { margin-top: 6px; }
.ai-image-opts              { display: flex; gap: 10px; flex-wrap: wrap; margin: 8px 0; align-items: flex-end; }
.ai-image-opt               { margin: 0; }
.ai-image-opt-btn           { align-self: flex-end; }
.ai-image-grid              { display: flex; gap: 10px; flex-wrap: wrap; }
.ai-image-cell              { padding: 0; border: 2px solid transparent; border-radius: 8px;
                              cursor: pointer; background: none; }
.ai-image-cell:hover        { border-color: #93c5fd; }
.ai-image-cell.is-selected  { border-color: #2563eb; }
.ai-image-cell img          { max-width: 150px; display: block; border-radius: 6px; }
.ai-image-refine            { margin-top: 10px; }
.ai-image-steer             { width: 100%; }
.ai-image-refine-actions    { margin-top: 6px; display: flex; gap: 8px; flex-wrap: wrap; }
.ai-image-usage             { margin: 8px 0 0; }
.ai-image-source-toggle     { display: block; margin-bottom: 8px; }
.ai-image-composed          { margin: 8px 0; }
.ai-image-composed textarea { width: 100%; margin-top: 8px; }
.ai-image-references        { margin: 10px 0; }
.ai-image-reference-list    { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 8px; }
.ai-image-reference-item    { display: inline-flex; align-items: center; gap: 3px; }
.ai-image-reference-item img { width: 72px; height: 72px; object-fit: cover; border-radius: 6px; }
.post-ai-disclosure         { margin-top: 10px; }
.post-ai-disclosure-toggle  { margin-left: 10px; }
.media-item-alt             { width: 100%; margin-top: 6px; }

/* AI-label: plaatsingshulp. Het label is een <img> dat van de server komt,
   dus `width: auto` bij een gezette hoogte houdt de verhouding vanzelf —
   uitrekken kan hier niet, en dat is precies wat de Code of Practice eist. */
/* De var()-namen hieronder waren --border/--muted/--text/--surface: die
   tokens bestaan niet in dit project (ze heten --c-*). Elke verwijzing viel
   dus stil terug op zijn hardgezette lichte literal en volgde de weergave
   niet. Zelfde fout als in subprocessors.css; overal rechtgezet. */
.ai-image-label             { margin-top: 12px; border-top: 1px solid var(--c-border, #e5e7eb); padding-top: 10px; }
.ai-image-label-head        { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ai-image-label-switch      { display: inline-flex; align-items: center; gap: 6px; }
.ai-label-opts              { align-items: flex-end; }
.ai-label-color             { width: 48px; height: 32px; padding: 2px; }
.ai-label-size              { width: 160px; }
.ai-label-stage             { position: relative; display: inline-block; max-width: 100%;
                              margin-top: 6px; touch-action: none; }
.ai-label-stage-img         { display: block; max-width: 320px; width: 100%; border-radius: 6px; }
/* Het gebied dat op álle gekozen kanalen binnen beeld blijft. */
.ai-label-zone              { position: absolute; border: 1px dashed rgba(255,255,255,.85);
                              box-shadow: 0 0 0 1px rgba(0,0,0,.35) inset; pointer-events: none; }
.ai-label-chip              { position: absolute; width: auto; cursor: grab;
                              user-select: none; touch-action: none; }
.ai-label-chip:active       { cursor: grabbing; }
.ai-label-verdict           { margin: 6px 0 0; }
.ai-label-why > summary     { cursor: pointer; font-size: .85em; color: var(--c-muted, #6b7280); }
.ai-label-why-body          { max-width: 46em; font-size: .9em; }
.ai-label-why-body h4       { margin: 8px 0 4px; }
.ai-label-why-body ul       { margin: 4px 0 0 1.1em; }

/* ============================================================
 * Emoji-picker in de Quill-toolbar (quill-emoji.js)
 * ============================================================ */
.ql-emoji-btn {
  border: 0; background: transparent; cursor: pointer;
  font-size: 15px; line-height: 1; padding: 2px 4px;
}
.ql-emoji-btn:hover { transform: scale(1.15); }
.ql-emoji-panel {
  position: absolute; top: 100%; right: 0; z-index: 50;
  width: 300px; max-height: 280px; overflow-y: auto;
  background: var(--c-surface, #fff);
  border: 1px solid var(--c-border, #e4e7ec);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  padding: 8px 10px;
}
/* Sticky: het paneel scrollt (max-height 280px), en een zoekveld dat mee naar
   boven verdwijnt is er geen. De achtergrond moet dekkend zijn, anders scrollen
   de emoji er zichtbaar achterlangs.
 *
 * De RAND is `--c-muted` en niet `--c-border`. Dit veld staat op een popover
 * die zelf al `--c-surface` is, en daar verdwijnt de gewone veldcombinatie:
 * `--c-surface-2` op `--c-surface` geeft 1.06:1 vulling en `--c-border` geeft
 * 1.23:1 rand — in donker én licht, in de browser gemeten op nova. WCAG 2.2
 * SC 1.4.11 vraagt 3:1 voor de omtrek die een component herkenbaar maakt, en
 * bij die waarden staat er zichtbaar geen invoerveld. `--c-muted` haalt het
 * in beide weergaven met ruime marge en is een bestaand token.
 *
 * DE SELECTOR DRAAGT HET PANEEL MEE, en dat is geen sierlijkheid. ui.css zet
 * `input[type="search"] { border: 1px solid var(--c-border) }` — specificiteit
 * (0,1,1) — en die stylesheet laadt ná app.css, want de site-schillen halen
 * hem op via `Theme::THEMES[...]['css']` en niet via een <link> in de header.
 * Een kale `.ql-emoji-search` is (0,1,0) en verliest dus; met het paneel erbij
 * is het (0,2,0) en wint deze regel. De eerste versie hiervan mat 5.92:1 op een
 * proefpagina waar ui.css ontbrak — de fix was er, maar in de echte cascade
 * stond gewoon weer 1.23:1.
 *
 * Elders in de app valt dezelfde combinatie minder op omdat een veld daar op
 * `--c-bg` staat in plaats van op een surface; dat bredere patroon is hier
 * NIET aangepast. Géén `#fff` zoals .search-input en .media-picker hierboven
 * — dat is precies wat in donkere weergave omvalt. */
.ql-emoji-panel .ql-emoji-search {
  position: sticky; top: -8px; z-index: 1;
  display: block; width: 100%; box-sizing: border-box;
  margin: -8px 0 4px; padding: 6px 8px;
  appearance: none; -webkit-appearance: none;
  font-size: 13px; line-height: 1.3;
  color: var(--c-text);
  background: var(--c-surface-2);
  border: 1px solid var(--c-muted);
  border-radius: 6px;
}
.ql-emoji-panel .ql-emoji-search::placeholder { color: var(--c-muted); }
.ql-emoji-panel .ql-emoji-search:focus-visible {
  outline: 2px solid var(--c-primary); outline-offset: 1px;
}
.ql-emoji-none {
  font-size: 12px; color: var(--c-muted);
  padding: 10px 2px; text-align: center;
}
.ql-emoji-group-label {
  font-size: 11px; font-weight: 600; color: var(--c-muted);
  text-transform: uppercase; letter-spacing: .04em;
  margin: 6px 0 2px;
}
.ql-emoji-group { display: flex; flex-wrap: wrap; gap: 2px; }
.ql-emoji-item {
  border: 0; background: transparent; cursor: pointer;
  font-size: 18px; line-height: 1; padding: 4px; border-radius: 6px;
}
.ql-emoji-item:hover { background: rgba(127, 127, 127, 0.15); transform: scale(1.2); }

/* ============================================================
 * @-vermeldingen (quill-mention.js, issue #62). Zoals LinkedIn hem toont: de
 * naam zonder @, vet, in linkkleur — in de editor (een ondeelbaar chipje) én
 * in de alleen-lezen-tekst (`PostFormat::mentionsMarkeren()`). `--c-link`
 * bestaat in alle vier de schillen, licht en donker.
 * ============================================================ */
.ql-mention-btn {
  border: 0; background: transparent; cursor: pointer;
  font-weight: 700; font-size: 15px; line-height: 1; padding: 2px 4px;
  color: var(--c-text);
}
.sg-mention { color: var(--c-link); font-weight: 600; white-space: nowrap; }
/* Buiten de editor linkt een vermelding door naar LinkedIn (mentionsMarkeren()).
 * Zoals op LinkedIn: geen streep, wel bij aanwijzen en bij toetsenbordfocus.
 * De streep hoort op de span — op de <a> kreeg hij de kleur van de link. */
.article-rich a:has(> .sg-mention),
.rv-text a:has(> .rv-tag) { color: inherit; text-decoration: none; }
a:hover > .sg-mention, a:focus-visible > .sg-mention,
a:hover > .rv-tag,     a:focus-visible > .rv-tag { text-decoration: underline; }
/* Een persoon is in te korten: klikken opent de keuze. */
.ql-editor .sg-mention[data-urn^="urn:li:person:"] { cursor: pointer; }
.ql-editor .sg-mention[data-urn^="urn:li:person:"]:hover { text-decoration: underline; }
.sg-mention-list {
  position: absolute; z-index: 50; min-width: 220px; max-width: 360px;
  margin: 0; padding: 4px; list-style: none;
  background: var(--c-surface); color: var(--c-text);
  border: 1px solid var(--c-muted); border-radius: 8px;
  box-shadow: var(--shadow-md);
}
.sg-mention-list li[role="option"] { padding: 6px 8px; border-radius: 6px; cursor: pointer; }
.sg-mention-list li.is-actief { background: var(--tint-prim-bg); color: var(--tint-prim-fg); }
.sg-mention-list li.is-actief .muted { color: inherit; }
.sg-mention-melding { padding: 6px 8px; font-size: 12px; color: var(--c-muted); }

/* ============================================================
 * Beeldbewerker (media-crop.js): uitsnede, draaien, aanpassen, tekst, logo
 * ============================================================ */
.media-crop {
  position: absolute; top: 4px; right: 30px;
  width: 22px; height: 22px; border-radius: 50%;
  border: none; background: rgba(0,0,0,0.65); color: #fff;
  cursor: pointer; line-height: 1; font-size: 12px;
}
.media-crop:hover { background: var(--c-primary); }

/* Instagram-tags op het beeld (post-media.js, issue #62). Zelfde knopvorm als
   .media-crop, één plek verder naar links. */
.media-igtag {
  position: absolute; top: 4px; right: 56px;
  min-width: 22px; height: 22px; border-radius: 11px; padding: 0 5px;
  border: none; background: rgba(0,0,0,0.65); color: #fff;
  cursor: pointer; line-height: 1; font-size: 12px; font-weight: 700;
}
.media-igtag:hover, .media-igtag.has-tags { background: var(--c-primary); }
.sg-modal-card.igtag-modal { max-width: min(640px, 96vw); }
.igtag-stage { position: relative; display: inline-block; max-width: 100%; margin-bottom: 10px; }
.igtag-stage img { display: block; max-width: 100%; max-height: 55vh; cursor: crosshair; }
.igtag-dot {
  position: absolute; transform: translate(-50%, -50%); pointer-events: none;
  background: var(--c-surface); color: var(--c-text); border: 1px solid var(--c-muted);
  border-radius: 10px; padding: 1px 6px; font-size: 12px; white-space: nowrap;
}
.igtag-dot.is-nieuw { background: var(--tint-prim-bg); color: var(--tint-prim-fg); }
.igtag-row { display: flex; gap: 8px; }
.igtag-row input { flex: 1 1 auto; }
.igtag-list { list-style: none; margin: 8px 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.igtag-list li { display: inline-flex; align-items: center; gap: 2px; }

/* De kaart is breder dan de standaard .sg-modal-card: canvas én paneel. */
.sg-modal-card.crop-modal { max-width: min(960px, 96vw); padding: 16px; }
.crop-head {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin-bottom: 10px;
}
.crop-head h2 { margin: 0; font-size: 1.05rem; flex: 1 1 auto; }
.crop-ctl { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--c-muted); }
.crop-ctl select { font: inherit; padding: 4px 8px; border: 1px solid var(--c-border); border-radius: var(--r-sm);
                   background: var(--c-surface-2); color: var(--c-text); max-width: 260px; }
.crop-zoom { width: 110px; }
.crop-btns { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.crop-ai-note { margin: 0 0 8px; color: var(--c-muted); }

.crop-body { display: grid; grid-template-columns: minmax(0, 1fr) 270px; gap: 16px; align-items: start; }
@media (max-width: 720px) { .crop-body { grid-template-columns: 1fr; } }
.crop-stage { min-width: 0; }
/* De bron dekt het kader altijd (zoom ≥ cover), dus deze kleur is alleen
   zichtbaar tijdens het laden — en dan hoort hij bij het thema. */
.crop-viewport {
  position: relative; overflow: hidden; margin: 0 auto;
  background: var(--c-surface-2); border-radius: var(--r-md);
  touch-action: none; user-select: none;
}
.crop-canvas { display: block; cursor: grab; }
.crop-canvas:active { cursor: grabbing; }
.crop-canvas:focus-visible { outline: 2px solid var(--c-focus); outline-offset: 2px; }
.crop-note { margin: 8px 0 0; text-align: center; color: var(--c-muted); min-height: 1.2em; }
.crop-hint { margin: 4px 0 0; text-align: center; }

.crop-panel { max-height: 70vh; overflow: auto; display: flex; flex-direction: column; gap: 8px; }
.crop-target { border: 1px solid var(--c-border); border-radius: var(--r-md); padding: 8px 10px; margin: 0; font-size: 13px; }
.crop-target legend { padding: 0 4px; color: var(--c-muted); }
.crop-target label { display: inline-flex; align-items: center; gap: 4px; margin-right: 10px; }
.crop-target p { margin: 6px 0 0; }
.crop-section { border: 1px solid var(--c-border); border-radius: var(--r-md); padding: 0 10px 8px; }
.crop-section summary { cursor: pointer; padding: 8px 0; font-weight: 600; font-size: 13px; }
.crop-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 13px; margin: 6px 0; }
.crop-row > span { color: var(--c-muted); flex: 0 0 auto; }
.crop-row input[type="range"] { flex: 1 1 auto; min-width: 0; }
.crop-row input[type="text"] { flex: 1 1 auto; min-width: 0; font: inherit; padding: 4px 8px;
                               border: 1px solid var(--c-border); border-radius: var(--r-sm);
                               background: var(--c-surface-2); color: var(--c-text); }
.crop-row select { font: inherit; padding: 4px 8px; border: 1px solid var(--c-border); border-radius: var(--r-sm);
                   background: var(--c-surface-2); color: var(--c-text); max-width: 60%; }
.crop-check { justify-content: flex-start; }
.crop-logo-row { flex-wrap: wrap; justify-content: flex-start; }
.crop-logo-thumb { width: 48px; height: 48px; object-fit: contain; border-radius: var(--r-sm);
                   background: var(--c-surface-2); border: 1px solid var(--c-border); }

.crop-foot { display: flex; align-items: center; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
.crop-spacer { flex: 1 1 auto; }

/* ============================================================
   Taalswitcher (meertaligheid) — zwevend op publieke/auth-pagina's
   ============================================================ */
.lang-switcher-floating {
  position: absolute;
  top: 16px;
  right: 16px;
  z-index: 50;
  /* `width: auto` IS HIER GEEN RUIS. De mobiele mediaquery hierboven zet
     `.org-switcher-form { width: 100% }` voor de kiezer in de topbar, en dit
     zwevende formulier draagt diezelfde klasse. Het werd dus vensterbreed
     terwijl het op `right: 16px` hangt, en begon daardoor op left −16: de
     <select> viel 16px links buiten beeld op /login, /voorwaarden, /welcome,
     /pakketten en /forgot-password, in beide thema's. */
  width: auto;
}

/* Publiek impressum (views/layout/footer.php) — permanente bedrijfsidentificatie
   op publieke pagina's (Dienstenrichtlijn / e-commerce). */
.site-footer {
  border-top: 1px solid var(--c-border, #e2e8f0);
  margin-top: 2rem;
  padding: 1rem 1.25rem;
  font-size: .8125rem;
  color: var(--c-muted, #64748b);
}
.site-footer-inner {
  max-width: 1080px;
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1rem;
  align-items: center;
  justify-content: space-between;
}
.site-footer-links { display: flex; gap: 1rem; flex-wrap: wrap; }
.site-footer-links a { color: inherit; text-decoration: underline; }
.site-footer-links a:hover { color: var(--c-text, #1e293b); }

/* ── Trending topics: bron-catalogus + per-bron instellingen ─────────────
   (Slice 6 introduceerde de .trend-source*-markup zonder eigen CSS; deze
   regels geven de kaarten een nette raster-layout en stylen het
   instellingen-<details> dat bij de keyword-/regio-filter hoort.) */
.trend-source-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: .75rem;
  margin: .5rem 0 1rem;
}
.trend-source {
  border: 1px solid var(--c-border, #e2e8f0);
  border-radius: 8px;
  padding: .75rem .875rem;
  background: var(--c-surface, #fff);
}
.trend-source-disabled { opacity: .7; }
.trend-source-head {
  display: flex;
  align-items: center;
  gap: .5rem;
  justify-content: space-between;
  margin-bottom: .5rem;
}
.trend-source-settings { margin-top: .625rem; }
.trend-source-settings > summary {
  cursor: pointer;
  color: var(--c-muted, #64748b);
  user-select: none;
}
.trend-source-settings[open] > summary { margin-bottom: .5rem; }
.trend-source-settings .field { margin-bottom: .5rem; }

/* ==================================================================
 * Publieke reviewpagina (views/posts/review.php) — prefix .rv-
 *
 * De previewkaarten imiteren bewust de ECHTE feeds: licht-op-donker,
 * platformkleuren en -radii, ongeacht het gekozen thema. Dat is geen
 * bug om met var(--c-surface) te "repareren" — een LinkedIn-post die
 * er donkerpaars uitziet vertelt de reviewer niets over de publicatie.
 * De pagina eromheen volgt wél het thema.
 * ================================================================== */

/* Alleen voor schermlezers: draagt de uitleg die visueel al uit kleur,
   vorm of een compacte notatie blijkt. Het project had nog geen eigen
   hulpklasse hiervoor. */
.sr-only                    { position: absolute; width: 1px; height: 1px; padding: 0;
                              margin: -1px; overflow: hidden; clip: rect(0 0 0 0);
                              white-space: nowrap; border: 0; }

.rv-page                    { max-width: 860px; margin: 0 auto; padding: 24px 16px 64px; }

/* --- Kop ----------------------------------------------------------- */
.rv-hero                    { text-align: center; padding: 24px 0 32px; }
.rv-hero-brand              { margin-bottom: 14px; }
.rv-hero-title              { margin: 0 0 8px; font-size: 30px; line-height: 1.25;
                              letter-spacing: -.02em; overflow-wrap: anywhere; }
.rv-hero-meta               { margin: 0; color: var(--c-muted); font-size: 14px; }
.rv-hero-sep                { margin: 0 8px; opacity: .5; }

.rv-section-head            { margin: 0 0 16px; }
.rv-section-head h2         { margin: 0 0 4px; font-size: 17px; letter-spacing: -.01em; }
.rv-section-head p          { margin: 0; }

/* --- Tabstrip per kanaal ------------------------------------------- */
/* De strip scrollt horizontaal bij veel kanalen. Bewust GEEN mask-fade
   rechts: `mask-origin` is de border-box en beweegt niet mee met de
   scroll-offset, dus die zou de láátste tab permanent half wegknippen —
   juist wanneer de reviewer erheen is gescrold. */
/* Zonder JavaScript zijn de tabknoppen dood (er wordt niets omgeschakeld) en
   staan alle kanaalvoorbeelden gewoon onder elkaar. De strip verschijnt dus
   pas zodra review-preview.js `.rv-js` heeft gezet. */
.rv-tabs                    { display: none; }
.rv-js .rv-tabs             { display: flex; gap: 8px; overflow-x: auto; padding: 2px 2px 12px;
                              scrollbar-width: thin; scroll-behavior: smooth; }
.rv-tab                     { flex: 0 0 auto; display: flex; align-items: center; gap: 8px;
                              padding: 9px 14px; border-radius: 999px; cursor: pointer;
                              border: 1px solid var(--c-border); background: var(--c-surface);
                              color: var(--c-text); font: inherit; font-size: 13px;
                              transition: border-color .18s ease, background .18s ease,
                                          transform .18s ease; }
.rv-tab:hover               { border-color: var(--c-primary); transform: translateY(-1px); }
.rv-tab.is-active           { border-color: var(--c-primary); background: var(--c-primary-soft);
                              font-weight: 600; }
.rv-tab-account             { color: var(--c-muted); font-weight: 400; max-width: 140px;
                              overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rv-tab-flag                { display: inline-flex; align-items: center; justify-content: center;
                              width: 16px; height: 16px; border-radius: 50%; font-size: 11px;
                              font-weight: 700; background: var(--tint-warn-bg); color: var(--tint-warn-fg); }
.rv-tab-dot                 { width: 9px; height: 9px; border-radius: 50%; background: var(--c-muted);
                              flex: 0 0 auto; }

/* Platformkleuren uit de tokens bovenin (zoek op "Platformkleuren: één bron").
   De waarden zijn byte-identiek aan wat hier stond; dit is dezelfde lijst die
   .platform-dot en .platform-icon lezen. */
.rv-tab-dot-linkedin,  .rv-badge-linkedin  { background: var(--pf-linkedin); }
.rv-tab-dot-facebook,  .rv-badge-facebook  { background: var(--pf-facebook); }
.rv-tab-dot-instagram, .rv-badge-instagram { background: var(--pf-instagram); }
.rv-tab-dot-x,         .rv-badge-x         { background: var(--pf-x); }
.rv-tab-dot-youtube,   .rv-badge-youtube   { background: var(--pf-youtube); }
.rv-tab-dot-threads,   .rv-badge-threads   { background: var(--pf-threads); }
.rv-tab-dot-bluesky,   .rv-badge-bluesky   { background: var(--pf-bluesky); }
.rv-tab-dot-tiktok,    .rv-badge-tiktok    { background: var(--pf-tiktok); }
.rv-tab-dot-pinterest, .rv-badge-pinterest { background: var(--pf-pinterest); }
.rv-tab-dot-wordpress, .rv-badge-wordpress { background: var(--pf-wordpress); }
.rv-tab-dot-joomla,    .rv-badge-joomla    { background: var(--pf-joomla); }

/* Met JS actief staat er één paneel tegelijk; zonder JS blijft alles staan. */
.rv-deck.rv-has-tabs .rv-panel            { display: none; }
.rv-deck.rv-has-tabs .rv-panel.is-active  { display: block; animation: rv-panel-in .28s ease both; }
.rv-deck:not(.rv-has-tabs) .rv-panel      { margin-bottom: 24px; }

/* In tabmodus animeert het PANEEL; de kaart erin staat meteen in
   eindtoestand. Anders blijft een kaart onzichtbaar: een element in een
   display:none-paneel wordt nooit door de IntersectionObserver gezien, en
   na het wisselen van tab is er geen tweede kans. */
.rv-deck.rv-has-tabs .rv-card             { opacity: 1; transform: none; transition: none; }

/* --- De kaart ------------------------------------------------------- */
/* LET OP — de begintoestand van de entree-animatie staat ACHTER `.rv-anim`,
   een klasse die assets/js/review-preview.js pas zet als hij draait. Zet je
   `opacity: 0` hier onvoorwaardelijk, dan is de hele beoordeling onzichtbaar
   voor een reviewer zonder JavaScript (bedrijfsproxy, NoScript, jQuery faalt)
   — en dat is precies het oppervlak waar dat niet mag. */
.rv-card                    { background: var(--c-surface); border: 1px solid var(--c-border);
                              border-radius: var(--r-lg); overflow: hidden;
                              box-shadow: var(--shadow-sm); }
.rv-anim .rv-card           { opacity: 0; transform: translateY(14px);
                              transition: opacity .5s ease, transform .5s ease;
                              transition-delay: var(--rv-delay, 0ms); }
.rv-anim .rv-card.rv-in     { opacity: 1; transform: none; }

.rv-card-bar                { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
                              padding: 10px 14px; border-bottom: 1px solid var(--c-border);
                              background: var(--c-surface-2); }
/* De achtergrond is de terugval voor een platform zonder eigen kleur; zonder
   deze regel zou een nieuw platform witte tekst op niets tonen. */
.rv-badge                   { display: inline-block; padding: 3px 9px; border-radius: 999px;
                              font-size: 11px; font-weight: 700; color: #fff; letter-spacing: .02em;
                              background: #475569; }
.rv-card-account            { font-size: 13px; color: var(--c-muted); overflow: hidden;
                              text-overflow: ellipsis; white-space: nowrap; }
.rv-chip                    { padding: 2px 8px; border-radius: 999px; font-size: 11px;
                              background: var(--tint-info-bg); color: var(--tint-info-fg); }
.rv-count                   { margin-left: auto; font-size: 11px; color: var(--c-muted);
                              font-variant-numeric: tabular-nums; }
.rv-count.is-over           { color: var(--tint-err-fg); font-weight: 700; }

.rv-stage                   { padding: 18px 14px; background:
                              linear-gradient(180deg, rgba(0,0,0,.05), transparent 120px); }

/* --- Feed-imitatie (licht, platform-authentiek) ---------------------- */
.rv-feed                    { background: #fff; color: #0f172a; border-radius: 10px;
                              border: 1px solid #e2e5ea; overflow: hidden;
                              max-width: 520px; margin: 0 auto;
                              box-shadow: 0 8px 26px rgba(0,0,0,.14); }
.rv-feed .rv-head           { display: flex; align-items: center; gap: 10px; padding: 12px 14px 8px; }
.rv-head-text               { display: flex; flex-direction: column; min-width: 0; }
.rv-name                    { font-size: 14px; font-weight: 600; color: #0f172a; }
.rv-sub                     { font-size: 12px; font-weight: 400; color: #64748b; }
.rv-more                    { margin-left: auto; color: #64748b; }
.rv-avatar                  { width: 40px; height: 40px; border-radius: 50%; flex: 0 0 auto;
                              display: inline-flex; align-items: center; justify-content: center;
                              font-weight: 700; color: #fff; font-size: 15px;
                              /* Vlak, zie .pv-avatar hierboven. */
                              background: #5b6472; }
.rv-avatar-sm               { width: 26px; height: 26px; font-size: 12px; }
.rv-avatar-ring             { box-shadow: 0 0 0 2px #fff, 0 0 0 4px #DD2A7B; }

.rv-lead                    { margin: 0; padding: 0 14px 6px; font-size: 15px; color: #0f172a; }
.rv-text                    { padding: 0 14px 12px; font-size: 14px; line-height: 21px;
                              color: #0f172a; word-break: break-word; overflow: hidden; }
.rv-text.is-clamped         { -webkit-mask-image: linear-gradient(180deg, #000 60%, transparent); 
                              mask-image: linear-gradient(180deg, #000 60%, transparent); }
.rv-link                    { color: #0a66c2; }
.rv-tag                     { color: #0a66c2; }
.rv-more-btn                { display: block; margin: -6px auto 12px; padding: 4px 12px;
                              border: none; background: none; cursor: pointer;
                              color: var(--c-link); font: inherit; font-size: 13px; font-weight: 600; }
.rv-more-btn:hover          { text-decoration: underline; }

.rv-actions                 { display: flex; flex-wrap: wrap; gap: 6px 10px;
                              justify-content: space-around;
                              padding: 8px 10px; border-top: 1px solid #eceef2;
                              font-size: 12px; color: #475569; }
.rv-actions-icons           { font-size: 14px; }
.rv-ig-actions              { display: flex; gap: 14px; padding: 10px 14px 4px; font-size: 20px;
                              color: #0f172a; }
.rv-ig-save                 { margin-left: auto; }
.rv-ig-tags                 { padding: 6px 14px 0; }   /* issue #62: zelfde inspringing als de actierij */

/* Mediakader in platformverhouding */
.rv-frame                   { position: relative; width: 100%; background: #0b1020; overflow: hidden;
                              display: grid; gap: 2px; }
.rv-frame-single            { grid-template-columns: 1fr; grid-template-rows: 1fr; }
/* Expliciete rijen: zonder deze regels groeien de cellen op hun inhoud en
   loopt het raster buiten de aspect-ratio van het kader. */
.rv-frame-n2                { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr; }
.rv-frame-n3                { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
.rv-frame-n3 .rv-frame-cell:first-child { grid-row: span 2; }
.rv-frame-n4                { grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr; }
.rv-frame-cell              { position: relative; overflow: hidden; min-height: 0; min-width: 0; }
.rv-frame img,
.rv-frame video             { width: 100%; height: 100%; object-fit: cover; display: block; }
.rv-frame-more              { position: absolute; right: 8px; bottom: 8px; padding: 2px 9px;
                              border-radius: 999px; background: rgba(15,23,42,.78); color: #fff;
                              font-size: 12px; font-weight: 600; }
.rv-play                    { position: absolute; inset: 0; margin: auto; width: 54px; height: 54px;
                              border-radius: 50%; background: rgba(15,23,42,.6); }
.rv-play::after             { content: ''; position: absolute; inset: 0; margin: auto;
                              width: 0; height: 0; border-left: 17px solid #fff;
                              border-top: 11px solid transparent; border-bottom: 11px solid transparent;
                              transform: translateX(3px); }

/* Telefoonlijst voor de verticale formats */
.rv-phone                   { max-width: 300px; margin: 0 auto; padding: 10px;
                              border-radius: 30px; background: #11121a;
                              box-shadow: 0 14px 40px rgba(0,0,0,.45), inset 0 0 0 1px #2a2b3a; }
.rv-phone .rv-feed          { border-radius: 22px; box-shadow: none; border: none; }
.rv-phone-tall .rv-feed     { background: #000; }

.rv-feed-x .rv-link,
.rv-feed-x .rv-tag          { color: #1d9bf0; }
.rv-feed-facebook .rv-link,
.rv-feed-facebook .rv-tag   { color: #1877F2; }
.rv-feed-instagram .rv-link,
.rv-feed-instagram .rv-tag  { color: #00376b; }
.rv-feed-bluesky .rv-link,
.rv-feed-bluesky .rv-tag    { color: #0085FF; }
.rv-feed-youtube .rv-yt-meta{ display: flex; gap: 10px; padding: 12px 14px 4px; }
.rv-yt-title                { font-size: 15px; font-weight: 700; color: #0f172a;
                              overflow-wrap: anywhere; }
.rv-yt-desc                 { color: #475569; }
.rv-feed-tiktok             { position: relative; }
.rv-tt-overlay              { position: absolute; left: 0; right: 0; bottom: 0; padding: 12px;
                              background: linear-gradient(transparent, rgba(0,0,0,.75)); }
.rv-tt-overlay .rv-name,
.rv-tt-overlay .rv-text     { color: #fff; padding-left: 0; padding-right: 0; }
/* De uitklapknop staat hier op een donkere overlay, niet op wit. */
.rv-tt-overlay .rv-more-btn { color: #fff; margin-bottom: 0; }

/* Website/CMS: browservenster met artikel */
.rv-browser                 { border-radius: 10px; overflow: hidden; background: #fff; color: #0f172a;
                              border: 1px solid #e2e5ea; box-shadow: 0 8px 26px rgba(0,0,0,.14); }
.rv-browser-bar             { display: flex; align-items: center; gap: 6px; padding: 9px 12px;
                              background: #eef0f4; border-bottom: 1px solid #e2e5ea; }
.rv-dot                     { width: 10px; height: 10px; border-radius: 50%; background: #cbd2dc; }
.rv-browser-url             { flex: 1; margin-left: 8px; padding: 3px 10px; border-radius: 999px;
                              background: #fff; color: #64748b; font-size: 11px;
                              overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rv-article                 { padding: 22px 24px 26px; }
.rv-article-title           { margin: 0 0 6px; font-size: 24px; line-height: 1.25; color: #0f172a;
                              font-weight: 700; overflow-wrap: anywhere; }
.rv-article-meta            { margin-bottom: 16px; font-size: 12px; color: #64748b; }
.rv-article .rv-frame       { border-radius: 8px; margin-bottom: 16px; }
.rv-article-body            { font-size: 15px; line-height: 25px; color: #1f2937;
                              word-break: break-word; }

/* Generieke artikeltypografie voor het CMS-voorbeeld.
   Het échte uiterlijk hangt af van het thema dat op de WordPress- of
   Joomla-site staat, en dat kennen wij hier niet. Dit is daarom bewust een
   NEUTRAAL blogartikel: het laat zien welke opmaak overleeft (koppen,
   lijsten, echte links, vet) zonder een lettertype of kleur te beloven die
   de site straks misschien niet gebruikt. Alle regels zijn gescoped op
   .article-rich, zodat ze nooit een social kaart raken. */
/* Opmaakwaarschuwing direct onder de editor. Bewust rustig van toon: dit is
   informatie, geen fout — de auteur mag vet blijven typen voor zijn
   WordPress-kanaal, ook als LinkedIn er speciale tekens van maakt. */
.editor-format-warning      { margin: 8px 0 0; padding: 8px 12px 8px 28px;
                              border-radius: 8px; list-style: disc;
                              background: var(--c-surface-2); border: 1px solid var(--c-border);
                              font-size: 12px; line-height: 18px; color: var(--c-muted); }
.editor-format-warning li   { margin-bottom: 2px; }
.editor-format-warning li:last-child { margin-bottom: 0; }

/* Notities onder een composer-kaart: dezelfde regels die de externe reviewer
   ziet, zodat auteur en reviewer nooit een verschillend verhaal krijgen. */
.pv-notes                   { margin: 6px 0 0; padding: 0 0 0 16px; list-style: disc;
                              font-size: 12px; line-height: 18px; color: var(--c-muted); }
.pv-notes li                { margin-bottom: 2px; }
/* Een CMS-kaart is geen feedkaartje maar een artikel. */
.pv-card-article            { padding: 16px 18px; }

.article-rich > :first-child { margin-top: 0; }
.article-rich > :last-child  { margin-bottom: 0; }
.article-rich p              { margin: 0 0 14px; }
.article-rich h1,
.article-rich h2,
.article-rich h3,
.article-rich h4             { margin: 22px 0 8px; line-height: 1.3; color: #0f172a;
                                  font-weight: 700; }
.article-rich h1             { font-size: 21px; }
.article-rich h2             { font-size: 19px; }
.article-rich h3             { font-size: 17px; }
.article-rich h4             { font-size: 15px; }
.article-rich ol,
.article-rich ul             { margin: 0 0 14px; padding-left: 22px; }
.article-rich li             { margin-bottom: 4px; }
.article-rich a              { color: #1d4ed8; text-decoration: underline; }
.article-rich blockquote     { margin: 0 0 14px; padding: 2px 0 2px 14px;
                                  border-left: 3px solid #cbd5e1; color: #475569; }
.article-rich pre,
.article-rich code           { font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
                                  font-size: 13px; background: #f1f5f9; border-radius: 4px; }
.article-rich code           { padding: 1px 5px; }
.article-rich pre            { margin: 0 0 14px; padding: 10px 12px; overflow-x: auto; }
.article-rich img            { max-width: 100%; height: auto; border-radius: 6px; }

/* Eerste reactie */
.rv-firstcomment            { margin: 0 14px 16px; padding: 12px 14px; border-radius: 10px;
                              background: var(--c-surface-2); border: 1px dashed var(--c-border); }
/* Alleen de INHOUD dempen; de chip draagt juist de uitleg waarom het blok
   gedempt is en moet op contrast blijven. */
.rv-firstcomment.is-muted .rv-text,
.rv-firstcomment.is-muted .rv-firstcomment-head { opacity: .62; }
.rv-firstcomment-head       { display: flex; align-items: center; gap: 8px; margin-bottom: 6px;
                              font-size: 13px; }
.rv-firstcomment .rv-text   { padding: 0; color: var(--c-text); }

/* Notitiechips: wat gebeurt er bij publicatie */
.rv-notes                   { display: flex; flex-wrap: wrap; gap: 6px; padding: 0 14px 14px; }
/* Focus zichtbaar houden: dit oppervlak is expliciet toetsenbord-bedienbaar
   (pijltjes door de tabstrip) en volgt daarmee de projectconventie. */
.rv-tab:focus-visible,
.rv-more-btn:focus-visible,
.rv-details > summary:focus-visible { outline: 2px solid var(--c-focus); outline-offset: 2px; }

.rv-note                    { display: inline-block; padding: 3px 10px; border-radius: 999px;
                              font-size: 11.5px; background: var(--tint-info-bg); color: var(--tint-info-fg); }
.rv-note-token_filled       { background: var(--tint-prim-bg); color: var(--tint-prim-fg); }
.rv-note-token_pending,
.rv-note-truncated          { background: var(--tint-warn-bg); color: var(--tint-warn-fg); }
.rv-note-over_limit,
.rv-note-media_required,
.rv-note-inactive           { background: var(--tint-err-bg); color: var(--tint-err-fg); }
.rv-note-no_first_comment   { background: var(--tint-warn-bg); color: var(--tint-warn-fg); }

/* --- Brontekst, media, beslissing, reacties -------------------------- */
.rv-source                  { margin: 28px 0; }
.rv-details                 { border: 1px solid var(--c-border); border-radius: var(--r-md);
                              background: var(--c-surface); }
.rv-details > summary       { padding: 12px 16px; cursor: pointer; font-weight: 600; font-size: 14px; }
.rv-details[open] > summary { border-bottom: 1px solid var(--c-border); }
.rv-details-body            { padding: 16px; }
.rv-source-title            { margin-top: 0; }

.rv-media                   { margin-top: 32px; }
.rv-decision, .rv-comments  { margin-top: 36px; padding-top: 24px;
                              border-top: 1px solid var(--c-border); }
/* Zie de waarschuwing bij .rv-card: begintoestand alleen mét JS. */
.rv-anim .rv-decision,
.rv-anim .rv-comments       { opacity: 0; transform: translateY(10px);
                              transition: opacity .45s ease, transform .45s ease; }
.rv-anim .rv-decision.rv-in,
.rv-anim .rv-comments.rv-in { opacity: 1; transform: none; }
.rv-anim .rv-section-head   { opacity: 0; transform: translateY(8px);
                              transition: opacity .4s ease, transform .4s ease; }
.rv-anim .rv-section-head.rv-in { opacity: 1; transform: none; }
.rv-form-title              { margin: 24px 0 10px; font-size: 15px; }
.rv-powered                 { margin-top: 40px; text-align: center; font-size: 12px;
                              color: var(--c-muted); }

.rv-pop                     { animation: rv-pop .4s cubic-bezier(.2, .9, .3, 1.2) both; }

@keyframes rv-panel-in {
  from { opacity: 0; transform: translateY(8px) scale(.995); }
  to   { opacity: 1; transform: none; }
}
@keyframes rv-pop {
  0%   { opacity: 0; transform: scale(.94); }
  60%  { transform: scale(1.02); }
  100% { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------------
 * Bewegingsvoorkeur. Een reviewlink gaat naar willekeurige externe
 * personen; wie in het besturingssysteem minder beweging vraagt, krijgt
 * hier de eindtoestand zonder animatie. Alles blijft zichtbaar en
 * bedienbaar — nooit inhoud verbergen achter een animatie.
 * ------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  .rv-card, .rv-decision, .rv-comments, .rv-section-head,
  .rv-anim .rv-card, .rv-anim .rv-decision,
  .rv-anim .rv-comments, .rv-anim .rv-section-head {
    opacity: 1 !important; transform: none !important; transition: none !important;
  }
  .rv-deck.rv-has-tabs .rv-panel.is-active,
  .rv-pop                                   { animation: none; }
  .rv-tab                                   { transition: none; }
  /* scroll-behavior hoort hier óók: een JS-aanroep met behavior:'auto'
     erft alsnog de CSS-waarde van de scroll-container. */
  .rv-tabs, html                            { scroll-behavior: auto; }
}

@media (max-width: 640px) {
  .rv-page       { padding: 12px 12px 48px; }
  .rv-hero-title { font-size: 23px; }
  .rv-stage      { padding: 12px 8px; }
  .rv-firstcomment, .rv-notes { margin-left: 8px; margin-right: 8px; padding-left: 10px; }
}

/* Carrousel-indicator: Instagram/Threads tonen één beeld met bolletjes,
   geen mozaïek — zie de toelichting bij $pvFrame in de previewpartial. */
.rv-carousel-count          { position: absolute; top: 8px; right: 8px; padding: 2px 9px;
                              border-radius: 999px; background: rgba(15,23,42,.72); color: #fff;
                              font-size: 11px; font-weight: 600; }
.rv-carousel-dots           { position: absolute; left: 0; right: 0; bottom: 8px;
                              display: flex; gap: 5px; justify-content: center; }
.rv-carousel-dots i         { width: 6px; height: 6px; border-radius: 50%;
                              background: rgba(255,255,255,.45); }
.rv-carousel-dots i.is-active { background: #fff; }

/* Teamstatistieken: markering bij een deels GESCHAT historisch gemiddelde
   (besluit D-019 §5/§8). Geen kleurcodering alleen — de tekst/het sterretje
   draagt de betekenis, zodat hij ook zonder kleurwaarneming leesbaar is. */
/* Compacte trechter in een SMALLE kolom (blok "Externe beoordeling" in de
   rechterzijbalk van de posteditor). Bewust geen .stat-grid: die is gebouwd
   voor volle paginabreedte en werd hier GEMETEN 670px hoog in één kolom van
   278px — vijf tegels boven elkaar die het uitnodigingsformulier van het
   scherm duwen. Dezelfde cijfers, een twintigste van de hoogte.
   De brede variant blijft .stat-grid en staat op /reviews/stats. */
.review-funnel { list-style: none; padding: 0; margin: 10px 0 14px;
  border: 1px solid var(--c-border); border-radius: var(--r-md); }
.review-funnel li { display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; padding: 6px 10px; }
.review-funnel li + li { border-top: 1px solid var(--c-border); }
.rf-label { color: var(--c-muted); font-size: 13px; }
.rf-label small { display: block; font-size: 11px; }
/* tabular-nums houdt de kolom recht als de aantallen in cijferbreedte verschillen */
.rf-value { font-weight: 700; font-size: 18px; line-height: 1.2; font-variant-numeric: tabular-nums; }
.stat-estimate { font-size: .85em; color: var(--c-muted, #6b7280); margin-left: .25rem; }
abbr.stat-estimate { text-decoration: none; cursor: help; border-bottom: 1px dotted currentColor; }

/* Zij-aan-zij-vergelijking bij een editconflict (besluit D-018 B1).
   `<details>` doet het open-/dichtklappen — de CSP is `script-src 'self'` en
   een eigen toggle zou een scriptbestand kosten voor iets wat de browser zelf
   kan. De brede cellen scrollen binnen hun eigen kader; de PAGINA mag niet
   horizontaal gaan scrollen door een lange post-body. */
.conflict-compare { margin-top: .75rem; }
.conflict-compare > summary { cursor: pointer; font-weight: 600; }
.conflict-compare-scroll { overflow-x: auto; margin-top: .5rem; }
.conflict-compare-table { width: 100%; table-layout: fixed; }
.conflict-compare-table th[scope="row"] { width: 8rem; vertical-align: top; text-align: left; }
.conflict-compare-cell {
  vertical-align: top;
  max-height: 14rem;
  overflow-y: auto;
  word-break: break-word;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: .85em;
}

/* ============================================================
   Blok-editor voor publieke pagina's (/superadmin/pages/{id}).
   Zie assets/js/superadmin-pages.js en docs/publieke-paginas.md.
   ============================================================ */

/* De blok-editor zelf (het sleepbare paneel in views/superadmin/page-edit.php)
   staat in themes/admin/supernova/theme.css. Hij verschijnt uitsluitend in het
   beheerpaneel; de PUBLIEKE uitvoer van de paginabouwer komt uit
   views/blocks/*.php en wordt hier en in ui.css gekleurd. Zie docs/themas.md. */

/* Bulkinvoer handmatige statistieken: zeven kolommen met invoervelden
   passen op een telefoon niet naast elkaar — horizontaal scrollen binnen de
   tabel in plaats van de pagina laten uitrekken. */
.metric-entry-scroll { overflow-x: auto; }
.metric-entry-scroll .data-table input[type="number"] { width: 5.5rem; }

/* Campagneprestaties (core/CampaignPerformance.php). Geen chart-library:
   de CSP is `script-src 'self'` en er staat geen lokale grafiekbibliotheek
   in assets/. Een balk is een div met een breedte. */
.perf-tiles { display: flex; flex-wrap: wrap; gap: .75rem; margin: .5rem 0 1rem; }
.perf-tile { flex: 1 1 8rem; min-width: 8rem; padding: .75rem; background: #f8fafc;
             border-radius: 6px; }
.perf-tile-label { font-size: 11px; text-transform: uppercase; color: #666; }
.perf-tile-value { font-size: 24px; font-weight: 700; line-height: 1.2; }
.perf-tile-note { font-size: 11px; }
.perf-subhead { margin: 1.25rem 0 .5rem; font-size: 14px; }
.perf-bar { background: #eef1f5; border-radius: 3px; height: .6rem; min-width: 4rem; }
.perf-bar-fill { background: #2563eb; border-radius: 3px; height: 100%; }
.perf-list { margin: 0; padding-left: 1.25rem; }
.perf-list li { margin-bottom: .25rem; }
.perf-list li span { margin-left: .5rem; }

/* Best-time-heatmap (core/BestTimes.php) EN de activiteitsheatmap van
   /superadmin/analytics -- beide via views/layout/_heatmap.php. Geen
   grafiekbibliotheek: de CSP is `script-src 'self'`. Een heatmap is een raster
   van divs met een dekking.

   De labels gebruiken var(--c-muted) en niet #666: dat laatste haalde op de
   donkere schil ~3,35:1 tegen de achtergrond en zakte daarmee onder de WCAG 2.2
   AA-eis van 4,5:1. Gemeten in de browser, niet berekend uit de bron. */
.heatmap { display: flex; flex-direction: column; gap: 2px; overflow-x: auto; }
.heatmap-hours { display: grid; grid-template-columns: repeat(24, 12px);
                 margin-left: 2.2rem; font-size: 10px; color: var(--c-muted); }
.heatmap-row { display: flex; align-items: center; gap: .25rem; }
.heatmap-day { width: 2rem; font-size: 11px; color: var(--c-muted); text-align: right; }
.heatmap-cells { display: grid; grid-template-columns: repeat(24, 12px); gap: 2px; }
.heatmap-cell { display: block; width: 12px; height: 12px; border-radius: 2px;
                background: #2563eb; }

/* "Hoe komen we aan deze cijfers?" (views/layout/_meten-uitleg.php).
   <details> is native HTML — geen JS, dus geen CSP-uitzondering. Dichtgeklapt
   is het één regel; wie er niet om vraagt heeft er geen last van. */
.meten-uitleg { margin-top: .75rem; font-size: 12px; }
.meten-uitleg > summary { cursor: pointer; color: #666; list-style: none;
                          display: inline-flex; align-items: center; gap: .4rem; }
.meten-uitleg > summary::-webkit-details-marker { display: none; }
.meten-uitleg-mark { display: inline-flex; align-items: center; justify-content: center;
                     width: 1.1rem; height: 1.1rem; border-radius: 50%;
                     border: 1px solid currentColor; font-size: 10px; font-weight: 700; }
.meten-uitleg > summary:hover { color: #2563eb; }
.meten-uitleg-body { margin-top: .5rem; padding: .75rem 1rem; background: #f8fafc;
                     border-radius: 6px; max-width: 46rem; }
.meten-uitleg-body h4 { margin: .75rem 0 .25rem; font-size: 12px; }
.meten-uitleg-body h4:first-child { margin-top: 0; }
.meten-uitleg-body p, .meten-uitleg-body li { margin: .25rem 0; line-height: 1.5; }
.meten-uitleg-body ul { margin: .25rem 0; padding-left: 1.1rem; }

/* ----- Avatar-assistent (avatar.js) -----------------------
   z-index 400 ligt in de vrije band 300-800: boven .notif-tray (100), onder
   .sg-tour-menu (900), de tour-overlay (1200) en de modals (1000). Een avatar
   die over een rondleiding heen loopt is geen hulp maar een obstakel.

   POSITIE VIA left, NIET VIA transform. De transform is helemaal van de CSS:
   de spiegeling (welke kant hij op kijkt) en het indrukken zijn allebei een
   transform, en JS die daar ook nog een translateX in schrijft, wist er telkens
   een van. De kijkrichting komt daarom als --sg-flip binnen. */
/* DE MAAT KOMT UIT ÉÉN VARIABELE (migratie 187). Hij stond hardgecodeerd op 96
   in DRIE bestanden: hier, in `AvatarSprite::TEGEL` en als constante in
   `avatar.js`. De tegel IN de sheet blijft 96 — die zit in de PNG — maar de
   maat op het scherm is nu instelbaar.

   DE CLAMP BESCHERMT GEDRAG, geen smaak. `avatar.js` toetst klimankers tegen de
   tegelmaat (`r.width < TEGEL * 1.5`, `pr.top >= TEGEL`): wordt het poppetje te
   groot, dan kwalificeert geen enkele richel meer en klimt hij nergens; te klein
   en hij is niet aan te klikken. 72-160 px houdt beide kanten heel, ook op een
   telefoon in landschap of een 4K-scherm.

   `--sg-avatar-vh` zet de server per merk; de fallback 9 is de waarde die op een
   scherm van 1080 uitkomt op 97 px — vrijwel exact de 96 van vóór deze
   instelling, zodat een bestaande installatie er niet anders uit gaat zien. */
.sg-avatar { /* DE BEGINWAARDE STAAT HIER, niet alleen in JS. Zelfde reden als
                --sg-flip een paar regels lager: een token dat uitsluitend uit
                JavaScript komt bestaat voor de tokenkruiscontrole in
                bin/test_theme.php niet, en — belangrijker — een `var()` naar
                niets valt STIL terug op de literal. Dan klopt het beeld
                toevallig tot iemand die fallback weghaalt. */
             --sg-avatar-vh: 9;
             --sg-avatar-h: clamp(72px, calc(var(--sg-avatar-vh) * 1vh), 240px);
             position: fixed; left: 0; bottom: 0;
             width: var(--sg-avatar-h); height: var(--sg-avatar-h);
             /* De sheet is acht tegels breed en tien hoog; zonder deze regel
                zie je bij een andere maat een stuk van de buurtegel. */
             background-size: calc(var(--sg-avatar-h) * 8) calc(var(--sg-avatar-h) * 10);
             z-index: 400; background-repeat: no-repeat; cursor: pointer;
             /* Kijkrichting: 1 = naar rechts, -1 = naar links. avatar.js zet hem
                per tik. Hier staat de BEGINWAARDE, zodat het element ook klopt
                voordat de lus voor het eerst getekend heeft — en zodat de
                tokenkruiscontrole in bin/test_theme.php hem kan vinden; die
                meldde --sg-flip terecht als onbekend toen hij alleen uit JS kwam. */
             --sg-flip: 1;
             transform-origin: bottom center; will-change: left, bottom, scale;
             transform: scaleX(var(--sg-flip, 1));
             transition: scale .12s cubic-bezier(.34, 1.56, .64, 1); }

/* Indrukken: breder en platter, alsof hij even inzakt. Nul extra frames.
   Blijft ook staan bij prefers-reduced-motion — een reactie op een bewuste
   aanraking is geen beweging die je overkomt.

   WAAROM scale EN NIET transform. Eerst stonden spiegeling én indrukken allebei
   op transform, met één transitie eroverheen. Gevolg: elke keer dat hij omdraait
   animeerde hij 120 ms lang dwars door scaleX(0) heen — hij vouwde zichzelf dicht
   en weer open. Gemeten in de browser, niet bedacht: de transitie bleef als
   CSSTransition:transform hangen en de computed transform bleef de
   eenheidsmatrix, terwijl transition:none meteen matrix(-1.06, 0, 0, 0.9) gaf.
   De losse scale-eigenschap (Transforms Level 2) scheidt de twee: omdraaien is
   direct — daar heeft de sprite eigen draaiframes voor — en alleen het indrukken
   veert. */
.sg-avatar.is-ingedrukt { scale: 1.06 .9; }
/* SCHERPE PIXELS bij vergroten — een keuze, geen standaard. De sheet is een
   raster van 96 pixels per tegel; boven ongeveer anderhalf keer die maat wordt
   dat zichtbaar. `pixelated` maakt daar bewust een retro-look van, zonder de
   klasse blijven de randen zacht. Dat laatste is hoe hij er altijd uitzag. */
.sg-avatar.is-scherp { image-rendering: pixelated; }

/* ----- Avatar: de weergaveproef op het beheerscherm -------
   Dezelfde clamp en dezelfde background-size als `.sg-avatar` hierboven, want
   een proef die anders rekent dan het echte poppetje is geen proef. */
.av-weergave        { display: flex; gap: 1.5rem; align-items: flex-start; flex-wrap: wrap; }
.av-weergave-velden { flex: 1 1 16rem; min-width: 14rem; }
.av-weergave-proef  { flex: 0 0 auto; display: flex; flex-direction: column; align-items: center;
                      gap: .4rem; padding: 1rem; border-radius: var(--r-md);
                      background: var(--c-surface-2); }
.av-weergave-figuur { --sg-avatar-vh: 9;
                      --sg-avatar-h: clamp(72px, calc(var(--sg-avatar-vh) * 1vh), 240px);
                      width: var(--sg-avatar-h); height: var(--sg-avatar-h);
                      background-repeat: no-repeat;
                      background-size: calc(var(--sg-avatar-h) * 8) calc(var(--sg-avatar-h) * 10);
                      background-position: 0 0; }
.av-weergave-figuur.is-scherp { image-rendering: pixelated; }
.av-weergave-maat   { display: flex; align-items: baseline; gap: .5rem; margin-top: .25rem; }
.av-weergave-maat output { font-variant-numeric: tabular-nums; font-weight: 600; }

.sg-avatar-ballon { position: fixed; bottom: 104px; left: 8px; z-index: 401;
                    max-width: min(22rem, calc(100vw - 32px));
                    background: var(--c-surface); color: inherit;
                    border-radius: var(--r-lg); box-shadow: var(--shadow-lg);
                    padding: 10px 14px; font-size: .92rem; line-height: 1.45; }
/* Het puntje naar de avatar toe. Los pseudo-element in plaats van een gedraaid
   vierkant op de ballon zelf: die laatste zou de schaduw meedraaien. */
.sg-avatar-ballon::after { content: ""; position: absolute; bottom: -6px;
                           /* avatar.js zet `--sg-ballon-punt` op het midden van
                              het poppetje, relatief aan de ballon. Vast op 28px
                              wees het puntje naar niets zodra de ballon tegen
                              een schermrand geklemd werd. De beginwaarde staat
                              hier, net als bij --sg-flip: een token dat alleen
                              uit JS komt bestaat niet voor de tokencontrole, en
                              een `var()` naar niets valt STIL terug. */
                           --sg-ballon-punt: 28px;
                           left: var(--sg-ballon-punt);
                           width: 12px; height: 12px; background: var(--c-surface);
                           transform: rotate(45deg); }
/* ONDER DE AVATAR. Past de ballon niet boven hem — hij zit hoog op een
   klimanker — dan kantelt hij eronder, en dan hoort het puntje omhoog te
   wijzen. Zonder deze regel steekt het aan de verkeerde kant uit. */
.sg-avatar-ballon.is-onder::after { bottom: auto; top: -6px; }

/* Op papier heeft niemand iets aan een poppetje. */
@media print { .sg-avatar, .sg-avatar-ballon { display: none; } }

/* ----- Avatar: de praatlaag (avatar-chat.js) --------------
   De ballon groeit mee met zijn inhoud; alleen de breedte is begrensd, want
   een tekstballon over de halve pagina leest niet meer als een ballon. */
.sg-avatar-ballon .sg-avatar-antwoord { margin: 0 0 .5rem; white-space: pre-wrap; }
.sg-avatar-ballon .sg-avatar-helpdesk { margin: 0 0 .5rem; font-size: .86rem;
                                        color: var(--c-muted); }
.sg-avatar-vorm    { display: flex; gap: .4rem; align-items: center; }
.sg-avatar-veld    { flex: 1; min-width: 12rem; padding: .35rem .5rem;
                     border: 1px solid var(--c-border); border-radius: var(--r-sm);
                     background: var(--c-bg); color: inherit; font: inherit; }
.sg-avatar-acties  { display: flex; flex-wrap: wrap; gap: .35rem; }

/* Wachten in trappen: onder 150 ms verschijnt dit helemaal niet, want een
   loader die even flitst is erger dan geen loader. */
.sg-avatar-wacht       { display: flex; align-items: center; gap: .3rem; }
.sg-avatar-wacht-tekst { margin-left: .35rem; font-size: .86rem; color: var(--c-muted); }
.sg-avatar-punt        { width: 6px; height: 6px; border-radius: 50%;
                         background: var(--c-muted);
                         animation: sg-avatar-pulse 1.2s ease-in-out infinite; }
.sg-avatar-punt:nth-child(2) { animation-delay: .2s; }
.sg-avatar-punt:nth-child(3) { animation-delay: .4s; }
@keyframes sg-avatar-pulse { 0%, 60%, 100% { opacity: .25; } 30% { opacity: 1; } }

/* Bij minder beweging staan de puntjes stil; de tekst ernaast blijft vertellen
   dat er iets gebeurt, dus er gaat geen informatie verloren. */
@media (prefers-reduced-motion: reduce) {
  .sg-avatar-punt { animation: none; opacity: .6; }
}

/* ----- Avatar: het opnamescherm (avatar-capture.js) -------
   Alleen in de superadmin-schil, maar app.css wordt daar ook geladen, dus hier
   is de plek waar de rest van de avatarstijlen ook staat. */
.av-voorbeelden { display: flex; flex-wrap: wrap; gap: .75rem; margin: .75rem 0 1rem; }
.av-voorbeeld   { margin: 0; width: 150px; }
.av-voorbeeld img { width: 100%; height: auto; border-radius: var(--r-sm); display: block; }
.av-voorbeeld figcaption { margin-top: .35rem; font-size: .78rem; line-height: 1.35;
                           color: var(--c-muted); }
.av-karakterblad-voorbeeld { margin: .75rem 0 0; max-width: 760px; }
.av-karakterblad-voorbeeld img { width: 100%; height: auto; display: block;
                                border: 1px solid var(--c-border); border-radius: var(--r-md); }
.av-karakterblad-voorbeeld figcaption { margin-top: .4rem; color: var(--c-muted);
                                       font-size: .82rem; line-height: 1.45; }

.av-regels { margin: .5rem 0 0; padding-left: 1.1rem; font-size: .88rem; line-height: 1.6; }
.av-regels li { margin: .15rem 0; }

/* Het doek toont een frame op ware verhouding maar nooit breder dan de kaart.
   Een vaste hoogte zou een staand telefoonfilmpje uitrekken. */
.av-doekwrap { max-width: 420px; margin: .5rem 0; }
.av-doek     { width: 100%; height: auto; display: block; border-radius: var(--r-sm);
               background: var(--c-bg); }

.av-schuif   { width: 100%; max-width: 420px; }

/* Het vinkje per pose: naast de kleur ook een teken, zodat het onderscheid
   niet alleen op kleur rust. */
.av-status   { display: inline-block; min-width: 1.2rem; margin-left: .4rem;
               font-weight: 700; color: var(--c-muted); }
.av-status.is-klaar { color: #1f9d55; }

.av-toestemming { margin-top: 1rem; padding: .75rem; border-radius: var(--r-sm);
                  background: var(--c-bg); }

/* De drie stappen bovenaan het opnamescherm. Genummerd en met de huidige stap
   aangezet: zonder dit is het scherm een reeks kaarten waarvan je pas onderaan
   merkt welke nu aan de beurt is — en juist stap 2 wacht op een mens. */
.av-stappen { display: flex; flex-wrap: wrap; gap: .5rem; list-style: none;
              margin: 0 0 1rem; padding: 0; }
.av-stap    { display: flex; gap: .55rem; align-items: flex-start; flex: 1 1 14rem;
              padding: .6rem .75rem; border-radius: var(--r-md);
              border: 1px solid var(--c-border); background: var(--c-surface); }
.av-stap span { display: block; }
.av-stap-nr { flex: 0 0 auto; width: 1.6rem; height: 1.6rem; border-radius: 50%;
              display: grid; place-items: center; font-weight: 700; font-size: .8rem;
              background: var(--c-surface-2); color: var(--c-muted); }
.av-stap.is-nu     { border-color: var(--c-primary); }
.av-stap.is-nu .av-stap-nr { background: var(--c-primary); color: #fff; }
/* Naast de kleur ook een teken: kleur alleen werkt niet voor iedereen. */
.av-stap.is-gedaan .av-stap-nr { background: var(--c-success); color: #fff; font-size: .9rem; }
.av-stap.is-gedaan .av-stap-nr::after { content: "\2713"; }
.av-stap.is-gedaan .av-stap-nr        { font-size: 0; }

/* Het karakterblad zelf: groot genoeg om echt te beoordelen. */
.av-blad { max-width: 100%; border-radius: var(--r-md); border: 1px solid var(--c-border); }
.av-blad-acties { display: flex; flex-wrap: wrap; gap: .5rem; margin: .75rem 0 .35rem; }

/* Zelf een blad aanleveren: dichtgeklapt, want het is de UITZONDERING. De
   gewone weg is genereren; wie een illustrator heeft, weet dat hij hier moet
   zijn. Een open formulier zou elke andere beheerder een keuze voorschotelen
   die hij niet heeft. */
.av-blad-upload { margin-top: 1rem; border-top: 1px solid var(--c-border); padding-top: .75rem; }
.av-blad-upload > summary { cursor: pointer; font-weight: 600; }
.av-blad-upload > summary:focus-visible { outline: 2px solid var(--c-primary); outline-offset: 2px; }
.av-blad-upload .field { margin-top: .75rem; }

/* Eén regel per seizoen: links de stand, rechts de knop. Wrapt op smal, want
   de leverancierskeuze en het modelveld staan in dezelfde regel. */
.av-seizoen { display: flex; flex-wrap: wrap; gap: .6rem; align-items: center;
              justify-content: space-between; padding: .6rem 0;
              border-top: 1px solid var(--c-border); }
.av-seizoen:first-of-type { border-top: 0; }
.av-seizoen form { display: flex; flex-wrap: wrap; gap: .35rem; align-items: center; }
.av-seizoen input[type="text"] { width: 10rem; }
.av-modeladvies-acties { display: flex; flex-wrap: wrap; align-items: center;
                         gap: .5rem; margin-top: .4rem; }
.av-modeladvies-resultaat { flex-basis: 100%; }

/* De prompt zoals hij naar het beeldmodel gaat. Dichtgeklapt, want het is
   naslag en geen invoer — maar wel op de plek waar de keuze gemaakt wordt, en
   niet in een document dat niemand er naast openslaat. */
.av-prompt { margin-top: 1rem; }
.av-prompt > summary { cursor: pointer; font-weight: 600; font-size: .9rem; }
.av-prompt h3, .av-prompt h4 { margin: .8rem 0 .25rem; color: var(--c-muted);
                               text-transform: uppercase; letter-spacing: .04em; }
/* pre-wrap en niet scrollen: de prompt is proza met regelafbrekingen, geen
   code. Een horizontale schuifbalk zou de helft verstoppen. */
.av-prompt-tekst { margin: 0; padding: .6rem .7rem; border-radius: var(--r-sm);
                   background: var(--c-bg); border: 1px solid var(--c-border);
                   font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
                   font-size: .78rem; line-height: 1.5;
                   white-space: pre-wrap; overflow-wrap: anywhere; }

/* Voorbeeldpost-kandidaten (views/profile/_voorbeeld_kandidaten.php).
   De preview is 90 tekens en `.btn` is inline-flex, dus zonder deze regels
   wikkelt hij over twee regels en verspringt de lijst per post. `min-width: 0`
   staat erbij omdat een flex-kind anders niet ONDER zijn inhoud krimpt — zonder
   die regel doet `text-overflow: ellipsis` helemaal niets. Gemeten op 1280px:
   knoppen van ~590×43, één regel per post. */
.kandidaten { margin-top: .5rem; }
.kandidaten-lijst { list-style: none; margin: .35rem 0 0; padding: 0; display: grid; gap: .25rem; }
.kandidaat { display: flex; align-items: center; gap: .5rem; min-width: 0; }
.kandidaat-knop { flex: 1 1 auto; min-width: 0; justify-content: flex-start; text-align: left; }
.kandidaat-tekst { display: block; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kandidaat-meta { flex: 0 0 auto; white-space: nowrap; font-size: .8125rem; }
.kandidaat-knop.is-toegevoegd { opacity: .55; }

/* ============================================================
   Gesprekken — de werkstroomlijst (docs/inbox.md slice 1c).

   Uitsluitend bestaande tokens: geen literal kleur, geen eigen
   donkere-modus-blok. Een tokenverwijzing die naar niets wijst valt
   STIL terug op de literal ernaast, dus staat er geen literal.
   (Schrijf hier geen voorbeeld met haakjes: bin/test_theme.php sectie J
   leest de BRONTEKST en zou zo'n voorbeeld als echt spooktoken melden.)
   ============================================================ */
.sg-gesprek-lijst,
.sg-gesprek-berichten,
.sg-gesprek-notities { list-style: none; margin: .75rem 0 0; padding: 0; }

.sg-gesprek-rij {
  display: flex; align-items: center; gap: .625rem; flex-wrap: wrap;
  padding: .625rem .75rem;
  border: 1px solid var(--c-border);
  border-radius: 8px;
  margin-bottom: .375rem;
  cursor: pointer;
  background: var(--c-surface);
}
.sg-gesprek-rij:hover  { border-color: var(--c-primary); }
.sg-gesprek-rij:focus-visible { outline: 2px solid var(--c-focus); outline-offset: 2px; }

/* Ongelezen is een RANDaccent, geen vulkleur: een gevulde rij zou met de
   status-badge om aandacht vechten en de tekst erop onleesbaar maken. */
.sg-gesprek-rij.is-ongelezen { border-left: 3px solid var(--c-primary); font-weight: 600; }

.sg-gesprek-preview {
  flex: 1 1 12rem; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.sg-gesprek-filter.is-actief { background: var(--tint-prim-bg); color: var(--tint-prim-fg); }

.sg-gesprek-berichten li {
  padding: .5rem .75rem; margin-bottom: .375rem;
  border-radius: 8px; background: var(--c-bg);
}
.sg-gesprek-berichten li.is-eigen { background: var(--tint-prim-bg); color: var(--tint-prim-fg); }
.sg-gesprek-berichten li p { margin: .25rem 0; white-space: pre-wrap; }

.sg-gesprek-notities li {
  padding: .375rem .5rem; border-left: 2px solid var(--c-border);
  margin-bottom: .25rem;
}
.sg-gesprek-notities li strong { margin-right: .375rem; }

/* ============================================================
   Link in bio (docs/publieke-paginas.md).

   MOBIEL EERST en dat is hier geen slogan: deze pagina staat in een
   profielbio en wordt vrijwel uitsluitend op een telefoon geopend.
   Eén kolom, grote raakvlakken, geen navigatie.

   Uitsluitend bestaande tokens; geen literal kleur en geen eigen
   donkere-modus-blok.
   ============================================================ */
.bio-pagina {
  max-width: 34rem;
  margin: 0 auto;
  padding: 1.5rem 1rem 3rem;
}
.bio-pagina h1,
.bio-pagina h2 { text-align: center; }

.bio-links-lijst { list-style: none; margin: 1rem 0 0; padding: 0; }

/* Kop (#51): kopafbeelding, rond logo dat erover valt, naam, iconenrij. */
.bio-kop { text-align: center; margin-bottom: 1rem; }
.bio-cover {
  display: block; width: 100%; aspect-ratio: 3 / 1; object-fit: cover;
  border-radius: 12px; background: var(--c-surface-2);
}
.bio-logo {
  display: block; width: 96px; height: 96px; margin: 0 auto .5rem;
  border-radius: 50%; object-fit: cover;
  border: 3px solid var(--c-surface); background: var(--c-surface);
}
.bio-kop--cover .bio-logo { position: relative; margin-top: -48px; }
.bio-naam { font-size: 1.25rem; margin: .25rem 0 .5rem; }
.bio-socials {
  list-style: none; margin: .5rem 0 0; padding: 0;
  display: flex; flex-wrap: wrap; justify-content: center; gap: .5rem;
}
/* 44px: het kleinste raakvlak dat op een telefoon betrouwbaar te raken is. */
.bio-socials a {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; border-radius: 50%;
  color: var(--c-text); background: var(--c-surface-2);
}
.bio-socials a:hover { color: var(--c-primary); }
.bio-socials a:focus-visible { outline: 2px solid var(--c-focus); outline-offset: 2px; }
.bio-socials svg { width: 20px; height: 20px; }
.bio-links-lijst li { margin-bottom: .625rem; }

/* Minstens 44px hoog: dat is de kleinste raakvlak die op een telefoon
   betrouwbaar te raken is. */
.bio-link {
  display: block;
  min-height: 44px;
  padding: .75rem 1rem;
  border: 1px solid var(--c-border);
  border-radius: 10px;
  background: var(--c-surface);
  color: var(--c-text);
  text-align: center;
  text-decoration: none;
  font-weight: 600;
}
.bio-link:hover        { border-color: var(--c-primary); }
.bio-link:focus-visible { outline: 2px solid var(--c-focus); outline-offset: 2px; }

/* Beheer */
.bio-link-rij {
  display: flex; gap: .5rem; align-items: center; margin-bottom: .5rem;
}
.bio-link-rij .bio-link-label { flex: 1 1 12rem; }
.bio-link-rij .bio-link-href  { flex: 2 1 18rem; }
.bio-link-rij { flex-wrap: wrap; }
.bio-link-rij .bio-link-kliks { flex: 1 0 100%; margin-top: -.25rem; }
.bio-beeld { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem; margin-bottom: .5rem; }
.bio-beeld .field-label { flex: 1 0 100%; }
.bio-beeld-voorbeeld { width: 56px; height: 56px; object-fit: cover; border-radius: 8px; border: 1px solid var(--c-border); }
.bio-social-rij { display: flex; align-items: center; gap: .5rem; margin-bottom: .375rem; }
.bio-social-rij span { flex: 0 0 8rem; }
.bio-social-rij input { flex: 1 1 auto; }
