/* App CSS lives strictly *around* the NLDD components: spacing between them and
   the few plain elements we own. Values come from --primitives-* tokens and
   light-dark() so the app follows the viewer's colour scheme, per the design
   system's rules. Never reach into a component's shadow DOM from here. */

/* One scrollbar, not two.
   nldd-page is the app's scroller: the design system gives it overflow-y:auto
   and the split view sizes it to the viewport, so the content scrolls inside it
   and every box from body up measures exactly the viewport height.
   The root still reported a 35 000px scroll area on top of that -- a second
   vertical scrollbar next to the first, scrolling the same list. Nothing lives
   out there: body's own scrollHeight equals its height, so hiding the root
   overflow removes a scrollbar and no content.
   Why the root sees it at all is the design system's business: it derives the
   scroll mode from the split view (nldd-app-view._evaluateScrollMode) and bails
   while isSingleColumn is not yet a boolean, so data-scroll is never set on
   anything and nobody claims the root. If a later release starts setting it,
   this rule becomes redundant rather than wrong. */
html {
  overflow: hidden;
}

/* NLDD applies fonts per component, not to the document, so any text that ends
   up outside one renders in the browser's default serif. This is a safety net
   using the public token, not an override of anything the system sets. */
body {
  font-family: var(--primitives-font-family-body);
  margin: 0;
}

/* ── Pioniers ───────────────────────────────────────────────────────────── */

.meta,
.back-link {
  color: var(--primitives-color-neutral-600);
}

.back-link {
  text-decoration: none;
}
.back-link:hover {
  text-decoration: underline;
}

.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--primitives-space-8);
}

.filter-label {
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--primitives-color-neutral-600);
}

/* The design system has no toggle-chip component: nldd-token is for displaying
   an active filter (dismissible), not switching one. These are plain buttons
   carrying aria-pressed, styled with public tokens — which is also how screen
   readers learn the on/off state the old dashboard only ever showed visually. */
.epic-filter,
.dep-filter,
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--primitives-space-6);
  padding: var(--primitives-space-4) var(--primitives-space-12);
  border-radius: 999px;
  border: 2px solid var(--primitives-color-neutral-300);
  background: transparent;
  color: var(--primitives-color-neutral-700);
  font: inherit;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1.3;
  cursor: pointer;
}

.epic-filter:hover,
.dep-filter:hover,
.chip:hover {
  border-color: var(--primitives-color-neutral-500);
}

.epic-filter.is-on,
.dep-filter.is-on,
.chip.is-on {
  border-color: var(--primitives-color-accent-600);
  background: var(--primitives-color-accent-50);
}

.epic-dot,
.chip-dot,
.cat-dot,
.tally-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--dot);
  flex-shrink: 0;
}

.epic-group[hidden] {
  display: none;
}

/* A row the departement filter puts away. nldd-list-item is a custom element
   with display: block from the design system, and that beats the browser's own
   [hidden] behaviour, so it has to be explicit here. */
nldd-list-item[hidden] {
  display: none;
}

/* The departement code on a pioniers row. The same shape as .status beside it,
   and deliberately not an nldd-tag: that is for the issue's own labels, and a
   derived code that looks like a label reads as one. */
.dep-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--primitives-space-6);
  font-size: 0.85rem;
  color: var(--primitives-color-neutral-700);
}

.epic-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--primitives-space-12);
  padding: var(--primitives-space-10) var(--primitives-space-16);
  border-left: 4px solid var(--epic-colour);
  background: var(--primitives-color-neutral-100);
  border-radius: 4px 4px 0 0;
}

.epic-title {
  font-weight: 700;
  color: inherit;
  text-decoration: none;
}
a.epic-title:hover {
  text-decoration: underline;
}

.epic-count {
  font-size: 0.8rem;
  white-space: nowrap;
  color: var(--primitives-color-neutral-600);
}

.row-main,
.traject {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--primitives-space-6);
}

.issue-id {
  font-size: 0.8rem;
  color: var(--primitives-color-neutral-500);
}

.issue-title {
  color: var(--primitives-color-accent-700);
  text-decoration: none;
}
.issue-title:hover {
  text-decoration: underline;
}

.is-task .row-main {
  padding-left: var(--primitives-space-24);
}

/* The column headers above the trajectory tree on the departement dashboard.

   Loose text and not slot="header": that is a table thing, and this panel is an
   nldd-list type="tree". The widths match the cells in the rows below (see
   traject-rij), so the headers sit above their column. To assistive software it
   is nothing -- the tree names its rows itself -- hence aria-hidden on the
   block. */
.traject-kop {
  display: flex;
  gap: var(--primitives-space-12);
  padding: 0 var(--primitives-space-16) var(--primitives-space-8);
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--primitives-color-neutral-600);
}

/* The first column fills, the other two are as wide as their cell. The chevron
   in front does not count: it sits inside the row, not in this header. */
.traject-kop-cel.is-traject {
  flex: 1 1 auto;
  padding-left: var(--primitives-space-32);
}

.traject-kop-cel.is-status {
  flex: 0 0 140px;
}

.traject-kop-cel.is-wie {
  flex: 0 0 180px;
}

.task-badge {
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 1px 5px;
  border-radius: 3px;
  background: var(--primitives-color-neutral-200);
  color: var(--primitives-color-neutral-700);
}

.status {
  display: inline-flex;
  align-items: center;
  gap: var(--primitives-space-4);
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
}

.status-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--dot);
  flex-shrink: 0;
}

.assignees {
  font-size: 0.8rem;
  color: var(--primitives-color-neutral-600);
}

.assignees.none {
  color: var(--primitives-color-neutral-400);
}

.empty {
  padding: var(--primitives-space-16);
  font-style: italic;
  color: var(--primitives-color-neutral-500);
}

/* ── Sectorregistraties ─────────────────────────────────────────────────── */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.filters {
  display: flex;
  flex-direction: column;
  gap: var(--primitives-space-10);
}

.tally {
  display: flex;
  flex-wrap: wrap;
  gap: var(--primitives-space-16);
  font-size: 0.82rem;
  color: var(--primitives-color-neutral-700);
}

.tally-stat {
  display: inline-flex;
  align-items: center;
  gap: var(--primitives-space-6);
}

.chip {
  font-size: 0.76rem;
  padding: 3px var(--primitives-space-10);
}

.result-count {
  font-size: 0.78rem;
  color: var(--primitives-color-neutral-600);
}

.reg-link {
  color: var(--primitives-color-accent-700);
  text-decoration: none;
}
.reg-link:hover {
  text-decoration: underline;
}

.ext {
  font-size: 0.7em;
  margin-left: 3px;
  opacity: 0.7;
}

.cat,
.aanbieder {
  display: inline-flex;
  align-items: center;
  gap: var(--primitives-space-6);
  font-size: 0.85rem;
}

.min-badge {
  display: inline-block;
  margin-left: var(--primitives-space-6);
  padding: 0 5px;
  border-radius: 3px;
  font-size: 0.65rem;
  font-weight: 700;
  color: #fff;
  background: var(--min);
  white-space: nowrap;
}

/* A filtered-out row must leave the grid entirely: nldd-table lays its rows out
   with subgrid, so `visibility` or opacity would still occupy a track. */
nldd-table-row[hidden] {
  display: none;
}

/* While a tick is in flight. htmx adds .htmx-request to the hx-indicator target
   for the duration of the request. */
nldd-table-row.htmx-request {
  opacity: 0.55;
}

#toggle-fout:empty {
  display: none;
}

#toggle-fout {
  margin-bottom: var(--primitives-space-12);
}

/* ── Pioniers edit ──────────────────────────────────────────────────────── */

.edit-btn {
  font-size: 0.78rem;
  font-weight: 600;
  padding: 3px var(--primitives-space-10);
  border-radius: 4px;
  border: 1px solid var(--primitives-color-neutral-300);
  background: transparent;
  color: var(--primitives-color-accent-700);
  cursor: pointer;
}
.edit-btn:hover {
  border-color: var(--primitives-color-accent-600);
}

/* The form fills the sheet's content height (set by sizeEditForm in app.js) and
   lays out as a column: fixed fields at the top, the description or preview
   taking the remaining space and scrolling internally, and the actions fixed at
   the bottom. The sheet itself never gets a scrollbar. The calc() is only a
   fallback if the JS height cannot be set. The width comes from the sheet's own
   `width` attribute, so the form does not cap it. */
.edit-form {
  /* border-box so the set height includes the padding; otherwise the form
     renders 40px taller than the sheet and the buttons clip again. */
  box-sizing: border-box;
  padding: var(--primitives-space-20);
  height: calc(100dvh - 4rem);
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
}

/* Fixed rows never shrink; the description block gives up space instead,
   scrolling internally. */
.edit-title,
.edit-form nldd-form-field,
.edit-form nldd-spacer,
#edit-fout {
  flex: 0 0 auto;
}

.desc-wrap {
  /* The only flexible row, so it takes all the leftover height. min-height:0
     lets it still yield on short windows so the buttons never get pushed out. */
  flex: 1 1 0;
  min-height: 0;
  /* The anchor for the preview's loader, which floats over the pane rather
     than taking a row of its own -- a row would push the editor as it came
     and went. */
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--primitives-space-4);
}

/* nldd-text-editor is already a flex column that grows, so it only needs to be
   told to take the leftover height rather than its content's. */
.desc-wrap #edit-desc {
  flex: 1 1 0;
  min-height: 0;
}

/* One row above the editor: label, toolbar, and the Bewerken/Voorbeeld switch
   pinned right. Label and switch keep their natural width; the toolbar takes
   whatever is left and decides for itself what still fits. */
.desc-head {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--primitives-space-8);
}

.desc-head > .field-label,
.desc-head > #edit-mode {
  flex: 0 0 auto;
}

/* The toolbar measures its own width to decide what goes into its overflow
   menu, so it has to be told what that width is: min-width 0 lets it shrink
   below its content instead of pushing the switch off the row. */
.md-toolbar {
  flex: 1 1 auto;
  min-width: 0;
}

/* Exactly one of editor and preview is on screen, so whichever it is gets the
   whole leftover height. app.js flips data-mode from the segmented control. */
.desc-wrap[data-mode="preview"] > #edit-desc,
.desc-wrap[data-mode="edit"] > .preview {
  display: none;
}

/* The toolbar is hidden rather than removed: display:none collapses its box, and
   on the way back it re-measures against a width that is not settled yet and
   pulls everything back out of its overflow menu. visibility keeps the box, so
   it never re-measures — and it still leaves the tab order and the a11y tree.
   It also keeps the switch from jumping sideways between the two modes. */
.desc-wrap[data-mode="preview"] .md-toolbar {
  visibility: hidden;
}

.field-label {
  flex: 0 0 auto;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--primitives-color-neutral-800);
}

.edit-title {
  font-size: 1.15rem;
  font-weight: 700;
  margin-bottom: var(--primitives-space-16);
}

/* GitLab renders the preview; give it a plain readable frame. It takes the space
   left over in the column and scrolls its own overflow, so a long rendered
   description never grows the sheet. */
.preview {
  flex: 1 1 0;
  min-height: 0;
  overflow-y: auto;
  border: 1px solid light-dark(
    var(--primitives-color-neutral-200),
    var(--primitives-color-neutral-750)
  );
  border-radius: 4px;
  padding: var(--primitives-space-12);
  font-size: 0.9rem;
  line-height: 1.5;
  overflow-wrap: anywhere;
}
.preview .rendered > :first-child { margin-top: 0; }
.preview .rendered > :last-child { margin-bottom: 0; }
.preview-fout {
  font-style: italic;
  color: var(--primitives-color-neutral-500);
}

/* The last row of the column: fixed at the bottom of the sheet, never shrinking,
   so Annuleren/Opslaan stay in view however long the description or preview is.
   The scrolling regions above (description, preview) give up space instead. */
.edit-actions {
  flex: 0 0 auto;
  display: flex;
  justify-content: flex-end;
  gap: var(--primitives-space-8);
  padding-top: var(--primitives-space-12);
  margin-top: var(--primitives-space-8);
  border-top: 1px solid light-dark(
    var(--primitives-color-neutral-150),
    var(--primitives-color-neutral-200)
  );
}

/* ── Dashboard per organisatie ───────────────────────────────────────────── */

/* The picker holds ~88 organisations, which is a table rather than a wall of
   pills: the ministerie moved into the filter chips above it, where a dozen
   values is the size chips are meant for. Everything around the table —
   .filters, .filter-bar, .chip, .result-count, .min-badge — is shared with the
   Sectorregistraties screen. */
a.org-link {
  color: var(--primitives-color-accent-700);
  text-decoration: none;
}
a.org-link:hover {
  text-decoration: underline;
}

/* Standing on its own in a column, not trailing a provider name. */
.min-badge.min-solo {
  margin-left: 0;
}

/* The badge is sometimes a link to that departement's dashboard. It keeps its
   own shape -- the colour is the code, and that must not flip to link blue --
   so only the underline is added, and only on hover, as with .org-link. */
a.min-badge {
  color: #fff;
  text-decoration: none;
}
a.min-badge:hover {
  text-decoration: underline;
}

.org-telling {
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  color: var(--primitives-color-neutral-700);
}

/* A zero is worth reporting — an organisation nobody has started on is exactly
   what an account manager may be looking for — but it should not read as
   loudly as a count that means work. */
.org-telling.is-nul {
  color: var(--primitives-color-neutral-500);
}

/* ── Kerncijfers ─────────────────────────────────────────────────────────── */

.stat-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--primitives-space-12);
}

.stat-tile {
  display: flex;
  flex-direction: column;
  gap: var(--primitives-space-4);
  min-width: 9rem;
  padding: var(--primitives-space-12) var(--primitives-space-16);
  border-radius: 6px;
  border: 1px solid light-dark(
    var(--primitives-color-neutral-200),
    var(--primitives-color-neutral-750)
  );
  background: var(--primitives-color-neutral-50);
}

/* A tile that clicks through -- the ministry tile on the organisation dashboard
   goes to the departement dashboard. It keeps its own shape and text colour: the
   tile is the button, and link-blue figures would make it read as a text link.
   What is added is a reason to think it can be clicked: the border lights up and
   the tile comes forward a little. */
a.stat-tile {
  color: inherit;
  text-decoration: none;
  transition: border-color 120ms ease, background-color 120ms ease;
}

a.stat-tile:hover,
a.stat-tile:focus-visible {
  border-color: var(--primitives-color-accent-600);
  background: light-dark(
    var(--primitives-color-neutral-100),
    var(--primitives-color-neutral-700)
  );
}

/* The ministry's name is what the link is about, so that gets the underline and
   not the whole tile. */
a.stat-tile:hover .stat-value,
a.stat-tile:focus-visible .stat-value {
  text-decoration: underline;
}

.stat-label {
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--primitives-color-neutral-600);
}

/* The figure is the point of the tile, so it gets the size. Tabular numerals
   keep the four tiles from jittering against each other. */
.stat-value {
  display: inline-flex;
  align-items: center;
  gap: var(--primitives-space-6);
  font-size: 1.5rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  color: var(--primitives-color-neutral-900);
}

.stat-sub {
  font-size: 0.78rem;
  line-height: 1.25;
  color: var(--primitives-color-neutral-600);
}

/* A tile whose figure we do not have. It keeps its place in the row -- the gap
   is the point, it is what starts the conversation about recording the data --
   but it must not read as a measured value, so it drops to the weight and
   colour of the labels around it. */
.stat-value.is-onbekend {
  font-size: 1.15rem;
  font-weight: 600;
  color: var(--primitives-color-neutral-500);
}

/* Narrower than the 22rem that would keep the name on one line: that single
   wide tile pushed the fifth off the row. At this width "Ministerie van
   Infrastructuur en Waterstaat" wraps to two lines -- which the line below is
   already built for -- and all five key figures sit side by side. */
.stat-tile-min {
  max-width: 15rem;
}

/* This tile's value is a sentence rather than a number, so it takes a size that
   can wrap to two lines inside the tile instead of the numeric 1.5rem. */
.stat-tile-min .stat-value {
  align-items: baseline;
  font-size: 1.05rem;
  line-height: 1.25;
}

/* The one key-figure tile with a control in it: the organisation's fase is
   changed here. Wider than the others because "Kandidaat deelnemer" has to fit
   the dropdown without being cut off. */
.stat-tile-fase {
  min-width: 13rem;
}

/* The dropdown fills the tile but does not inherit .stat-value's 1.5rem/700:
   that size is for a number, and a dropdown at it reads as a heading. The
   read-only value does stay .stat-value, so the tile looks like the four beside
   it. */
.stat-tile-fase .fase-keuze {
  margin-top: var(--primitives-space-4);
}

.stat-tile-fase .stat-value {
  font-size: 1.05rem;
  line-height: 1.25;
}

.stat-of {
  font-size: 1rem;
  font-weight: 400;
  color: var(--primitives-color-neutral-600);
}

/* ── Overzichtsstrook ────────────────────────────────────────────────────── */

/* Three panels above the filters on Sectorregistraties. auto-fit rather than a
   fixed three, so on a narrow screen they stack instead of squeezing the pies
   below the size where the small wedges are still distinguishable. */
.overzicht {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  align-items: start;
  gap: var(--primitives-space-24);
}

.overzicht-paneel {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--primitives-space-12);
}

.overzicht-kop {
  margin: 0;
  font-size: 0.78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--primitives-color-neutral-600);
}

/* The funnel once sat in a row beside a pie chart, where the 16rem from the
   flex shorthand was a width. Here the panel is a column, so that same basis
   becomes a height and leaves a 16rem gap under the bars. */
.overzicht-paneel .funnel {
  flex: 0 0 auto;
  width: 100%;
}

/* ── The bulk action on the organisation dashboard ───────────────────────── */

/* The registration panel's header, with the bulk button beside it: the button
   belongs to the table below and not to the tiles above, so it sits on the edge
   of what it changes. Wraps when narrow, because the choice and the button
   together are wider than a phone. */
.paneel-kop {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--primitives-space-8) var(--primitives-space-16);
  width: 100%;
}

.bulk-keuze {
  display: flex;
  align-items: center;
  gap: var(--primitives-space-8);
}

.bulk-vraag {
  margin: 0 0 var(--primitives-space-16);
  font-size: 0.95rem;
}

/* The preview's groups. The group that actually writes gets the emphasis; the
   other three are there to show what will *not* happen, which is just as
   important an answer -- but it is not an action. */
.bulk-groep {
  margin: 0 0 var(--primitives-space-16);
  padding-left: var(--primitives-space-12);
  border-left: 3px solid var(--primitives-color-neutral-200);
}

.bulk-groep.is-actie {
  border-left-color: var(--primitives-color-hemelblauw-500, #007bc7);
}

.bulk-groep-kop {
  margin: 0;
  font-size: 0.85rem;
  font-weight: 700;
}

.bulk-groep-aantal {
  font-weight: 400;
  color: var(--primitives-color-neutral-600);
}

.bulk-groep-uitleg {
  margin: 0 0 var(--primitives-space-8);
  font-size: 0.8rem;
  color: var(--primitives-color-neutral-600);
}

/* The registrations themselves, name on the left and fase on the right, so a
   column can be read instead of thirteen lines. It would rather scroll than let
   the dialog become the whole page. */
.bulk-lijst {
  margin: 0;
  padding: 0;
  max-height: 40vh;
  overflow-y: auto;
  list-style: none;
}

.bulk-lijst li {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--primitives-space-12);
  padding: var(--primitives-space-4) 0;
  font-size: 0.85rem;
  border-bottom: 1px solid var(--primitives-color-neutral-100);
}

.bulk-naam {
  min-width: 0;
  overflow-wrap: anywhere;
}

.bulk-fase {
  flex: 0 0 auto;
  white-space: nowrap;
  color: var(--primitives-color-neutral-600);
}

.bulk-lijst li.is-mislukt .bulk-fase {
  color: var(--primitives-color-rood-600, #d52b1e);
}

.bulk-acties {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--primitives-space-8);
  margin-top: var(--primitives-space-16);
}

.bulk-resultaat > .bulk-lijst {
  margin-top: var(--primitives-space-16);
}

/* A bar stretched over the full width of the page would put its label and its
   value a screen apart. Cap it at a readable measure. */
.voortgang-blok {
  max-width: 44rem;
}

/* The funnel across the full width, above the two tables. .overzicht-paneel is
   a column with align-items: flex-start, so without this the bar group inside
   would shrink to its own content instead of filling the line. */
.funnel-blok > .overzicht-paneel {
  align-items: stretch;
}

/* The two tables side by side on the sectorregistraties dashboard. The same
   auto-fit as .overzicht above rather than two fixed columns: as soon as they no
   longer fit beside each other they fall below one another, and then each table
   keeps its full width instead of being squeezed into three cramped columns. */
.cijfers {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr));
  align-items: start;
  gap: var(--primitives-space-24);
}

/* .overzicht-paneel is a column with align-items: flex-start, so everything in
   it shrinks to its own content. For the table that is wrong: it should fill the
   column the grid gives it. */
.cijfers .overzicht-paneel > nldd-table {
  width: 100%;
}

.paneel-hint {
  margin: 0;
  font-size: 0.78rem;
  color: var(--primitives-color-neutral-600);
}

/* The kennisproducten panel on the organisation dashboard. .overzicht-paneel is
   a column with align-items: flex-start, so the table would shrink to its own
   content instead of filling the panel. */
#kennisproducten .overzicht-paneel > nldd-table {
  width: 100%;
}

/* The add row: three choices and a button side by side, falling below one
   another on a narrow screen instead of being cramped.

   nldd-dropdown is a block element at 100% width, so without a flex basis of its
   own every choice fills the line and pushes the next one below it -- the row
   would be a column after all. The basis is the readable minimum width of the
   longest option ("Stappenplan Gegevenskwaliteit"); growing is allowed, so is
   shrinking, and only when that runs out does the line break. */
.tool-toevoegen {
  display: flex;
  flex-flow: row wrap;
  align-items: center;
  gap: var(--primitives-space-8);
  width: 100%;
}

.tool-toevoegen > nldd-dropdown {
  flex: 1 1 12rem;
  min-width: 0;
}

.tool-toevoegen > nldd-button {
  flex: 0 0 auto;
}

/* ── App shell: no divider lines between the panes ───────────────────────── */

/* The split view draws an nldd-split-view-divider of its own on the seam
   between the menu and the screen. Consumers do not manage that divider, and
   there is no attribute to switch them off; what the divider does read is the
   public token --semantics-dividers-color. Setting that token to transparent on
   the split views removes the lines without reaching into a shadow DOM.

   Custom properties inherit through the shadow boundary, so this would also take
   away the lines in lists, tables and menus inside. Hence the panes put it back
   at once: everything *in* a pane keeps its own dividers, only the seams between
   the panes disappear.

   The original value is recorded on :root rather than overwritten here, so a
   change in the design system comes along by itself. */
:root {
  --app-divider-color: var(--semantics-dividers-color);
}

nldd-navigation-split-view {
  --semantics-dividers-color: transparent;
}

/* The panes put it back at once; everything in them -- the top bar with its
   account menu, the lists, the tables -- keeps its own lines that way. */
nldd-split-view-pane {
  --semantics-dividers-color: var(--app-divider-color);
}

/* ── Zoeken in de topbalk ────────────────────────────────────────────────── */

/* The utility slot is a flex row aligned to the right; the search field sits to
   the left of the account menu. It may shrink but not vanish, and it does not
   push the account menu away: that keeps its own width. */
/* Centred in the top bar, and not on the right beside the account menu.

   Taken out of the flow rather than pushed with margins: the utility slot
   shrinks around its content, so there is no free space for auto margins to
   share out -- tried, and the field moved eight pixels. Positioned absolutely,
   the centre of the field lands exactly on the centre of the bar (measured: both
   903px in a 1500px window), and that stays true whether the account name is
   longer or shorter, because it no longer counts.

   Without a top the vertical place stays where it would be in the flow, so the
   field lines up with the account menu beside it. */
.topzoek {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  margin-top: 0.3rem;
}

/* The field is out of the flow and so does not know the account menu is beside
   it. Measured, they start overlapping below roughly 1150px, so below that the
   field disappears -- with margin, because a longer account name pushes the menu
   left. Every screen has its own search and filter bar, so no function is lost
   that is not already there. */
@media (max-width: 1279px) {
  .topzoek {
    display: none;
  }
}

/* ── Zijmenu ─────────────────────────────────────────────────────────────── */

/* The logo at the top of the side pane, in the same band as the top bar on the
   right.

   The 2.75rem is the height of that top bar: 44px, measured in the browser. That
   height comes from the account menu in it and sits in no token we can read from
   outside, so it stands here as a measurement -- if the top bar's content
   changes, this has to follow. There used to be a 3rem margin-top on .zijmenu
   here that pushed the menu below the top bar; now the logo fills that space and
   the menu no longer needs the margin.

   The inline padding is .zijmenu's, so the logo lines up on the left with the
   menu items below it instead of with the edge of the pane. */
.zijmenu-logo {
  display: flex;
  align-items: center;
  gap: var(--primitives-space-8);
  min-height: 2.75rem;

  /* The pane is a flex column and sets ::slotted(*) to flex-grow: 1. Without
     this rule the logo band stretches to halfway down the screen and the
     align-items: center above drops the logo to the middle of that. */
  flex: none;
  padding-inline: var(--primitives-space-12);
  text-decoration: none;

  /* In the light scheme hemelblauw-750 is almost exactly the blue of logo.svg,
     and the token already carries a light-dark() pair of its own: in the dark
     scheme it becomes the light counterpart by itself. Hence no light-dark()
     of our own around it -- that would invert the pair. */
  color: var(--primitives-color-hemelblauw-750);
}

/* The mark is one path with holes (see logo-mark.svg), so it needs only one
   colour and follows currentColor -- which is why it can move with the colour
   scheme above. */
.zijmenu-logo-merk {
  width: 1.75rem;
  height: 1.75rem;
  flex: none;
}

.zijmenu-logo-naam {
  font-family: var(--primitives-font-family-display);
  font-size: 1.125rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

/* The groups below one another, with air around the lists. The split view
   supplies the pane and its width; this is only the inside. */
.zijmenu {
  display: flex;
  flex-direction: column;
  gap: var(--primitives-space-4);
  padding: var(--primitives-space-16) var(--primitives-space-12);
}

/* The group heading with its icon beside it. The heading names the navigation
   landmark below it (aria-labelledby), so it is functional and not decorative;
   the icon beside it is decorative and sets aria-hidden itself.

   The colour sits on the row and not on the component: nldd-title sets it on
   slotted content with !important, and color="inherit" on the element is the way
   in the system offers for that -- the heading then follows the text colour set
   here. nldd-icon inherits without further ado, so with the colour on the row
   the icon and the heading keep the same tint by themselves. */
.zijmenu-kop-rij {
  display: flex;
  align-items: center;
  gap: var(--primitives-space-8);
  color: var(--primitives-color-neutral-600);
}

/* ── Tellingen ───────────────────────────────────────────────────────────── */

.tel-naam {
  display: inline-flex;
  align-items: center;
  gap: var(--primitives-space-6);
}

/* One uitvoeringsorganisatie's fase on the departement dashboard. The design
   fills the whole cell with the fase's colour here; a dot with a label keeps the
   same colour-to-status coupling as the rest of the app, and a table with eleven
   coloured blocks side by side reads as a warning that is not meant. */
.fase {
  display: inline-flex;
  align-items: center;
  gap: var(--primitives-space-6);
  font-size: 0.85rem;
  font-weight: 600;
}

/* "Nog geen actie" is the absence of progress and should not shout as loudly as
   a fase that was actually reached. */
.fase.is-leeg {
  font-weight: 400;
  color: var(--primitives-color-neutral-600);
}

/* A state that comes from a sectorregistratie and not from the organisation
   itself. Distinguished without a second colour, because the colours are already
   the states: a dotted line says "this belongs somewhere else" without muddying
   the four status colours. The explanation appears once, below the table. */
.is-van-registratie {
  text-decoration: underline dotted;
  text-underline-offset: 3px;
  text-decoration-color: var(--primitives-color-neutral-400);
}

/* ── Kennisproducten: de kaarten ─────────────────────────────────────────── */

/* One card per kennisproduct, and at the same time the filter on the table
   below -- hence .chip alongside: the aria-pressed behaviour and the on state
   come from there and are only styled differently here. */
.kaarten {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--primitives-space-12);
}

.kaart.chip {
  flex-direction: column;
  align-items: stretch;
  gap: var(--primitives-space-8);
  padding: var(--primitives-space-12) var(--primitives-space-16);
  border-radius: 6px;
  text-align: left;
}

.kaart-naam {
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--primitives-color-neutral-900);
}

.kaart-cijfers {
  display: flex;
  flex-wrap: wrap;
  gap: var(--primitives-space-8);
  font-size: 0.78rem;
  font-weight: 400;
  color: var(--primitives-color-neutral-700);
}

.kaart-stand {
  display: inline-flex;
  align-items: center;
  gap: var(--primitives-space-4);
}

.kaart-cijfers .is-leeg {
  color: var(--primitives-color-neutral-500);
}

/* The spread over the states as one bar. What is not filled is the track: those
   are the organisations this product is recorded nowhere for, and they should
   read as emptiness rather than as a fourth colour. */
.stapelbalk {
  display: flex;
  height: 8px;
  border-radius: 999px;
  overflow: hidden;
  background: var(--primitives-color-neutral-200);
}

.stapeldeel {
  width: var(--breedte);
  background: var(--deel);
}

/* The totals line belongs with the table visually, but not among its rows: a
   line above it and heavier text is enough to read it as a closing line. */
.tel-totaal {
  border-top: 2px solid var(--primitives-color-neutral-300);
}

/* ── Funnel: het treintje ────────────────────────────────────────────────── */

/* Two grids, not one: the six fases of the casus board and the five fases of a
   sectorregistratie. They deliberately do not line up -- see the head of
   internal/web/funnel.go and docs/design.md decision 4. One shared grid claimed
   that a column above and a column below are the same fase, and that is exactly
   the equivalence that was withdrawn.

   So do not merge them again, not even with a lowest common multiple of thirty
   columns: the alignment then becomes coincidence instead of absent, and that
   reads as a claim where there is now no claim.

   Explicit columns, and not grid-auto-flow: column, because the band labels have
   to be able to span the grid and that only works with named columns. The count
   comes from --kolommen, so both bands share one definition.

   The seventh bucket -- casussen without a fase -- deliberately sits below the
   funnel: it is not a fase anyone reaches, and as a column it made the row too
   wide to read.

   When too little room is left, a grid slides sideways rather than shrinking to
   unreadable slivers. The scrolling is on this element alone; the page itself
   never scrolls horizontally. At small widths the two bands scroll independently
   of each other, which is allowed: they are no longer meant to line up. */
.treintje {
  display: grid;
  grid-template-columns: repeat(var(--kolommen, 6), minmax(190px, 1fr));
  /* stretch, not start: within one band all six columns become as tall as the
     tallest. Otherwise a fase with one case gets a short block beside a fase
     with eight, and then the columns read as loose blocks instead of as one row
     you compare across the fases. The bands each keep their own height: this is
     per grid row. */
  align-items: stretch;
  gap: var(--primitives-space-8) var(--primitives-space-12);
  overflow-x: auto;
  padding-bottom: var(--primitives-space-8);
}

/* The registration band: five fases instead of six. */
.treintje-registraties {
  --kolommen: 5;
}

/* The two halves of the funnel, above the columns they span. --span comes from
   casussen.Groepen and is counted from the fases there. */
.treintje-groep {
  grid-column: span var(--span);
  margin: 0;
  padding: var(--primitives-space-6) var(--primitives-space-12);
  border-radius: 4px;
  font-size: 0.78rem;
  font-weight: 700;
  text-align: center;
  background: var(--primitives-color-neutral-100);
  color: var(--primitives-color-neutral-700);
}

/* The support half gets a tint of its own: whose turn it is changes there, and
   that is the one division the grid itself does not already show. */
.treintje-groep-ondersteuning {
  background: var(--primitives-color-hemelblauw-50);
}

.treintje-groep-uitleg {
  font-weight: 400;
}

/* A band's label, across the full width of the grid. */
.treintje-band {
  grid-column: 1 / -1;
  margin: var(--primitives-space-8) 0 0;
  font-size: 0.78rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--primitives-color-neutral-600);
}

.treintje-band-uitleg {
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
}


/* The number of registrations is what this band exists for, so that gets the
   size. Tabular figures so six columns do not jitter against each other. */
.treintje-getal {
  margin: 0;
  font-size: 2rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  text-align: center;
  color: var(--fase);
}

.treintje-voorbeelden {
  margin: 0;
  font-size: 0.75rem;
  line-height: 1.3;
  text-align: center;
  color: var(--primitives-color-neutral-600);
}

/* The remainder line below the grid, on one quiet line as in the design. It now
   sits outside both grids -- it is about both bands -- so no grid-column any
   more: a column span depending on a grid that is no longer around it is a rule
   that quietly does nothing. */
.treintje-voet {
  margin: var(--primitives-space-8) 0 0;
  padding: var(--primitives-space-8) var(--primitives-space-12);
  border-radius: 4px;
  font-size: 0.82rem;
  background: var(--primitives-color-neutral-50);
  color: var(--primitives-color-neutral-700);
}

/* Below the funnel, and deliberately not as a column: a wide grid that moves
   with the space instead of one long narrow stack. */
.geen-fase-lijst {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--primitives-space-8);
}

.funnel-kolom {
  display: flex;
  flex-direction: column;
  gap: var(--primitives-space-8);
  padding: var(--primitives-space-12);
  border-top: 3px solid var(--fase);
  border-radius: 4px;
  background: var(--primitives-color-neutral-100);
}

.funnel-kop {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--primitives-space-8);
}

.funnel-titel {
  margin: 0;
  font-size: 0.9rem;
}

.funnel-telling {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--primitives-color-neutral-600);
}

.funnel-uitleg {
  margin: 0;
  font-size: 0.75rem;
  color: var(--primitives-color-neutral-600);
}

.funnel-lijst {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--primitives-space-8);
}

.casus-kaart {
  display: flex;
  flex-direction: column;
  gap: var(--primitives-space-4);
  padding: var(--primitives-space-10);
  border-radius: 4px;
  /* neutral-0 is white in the light scheme and black in the dark one, so the
     card stands out both ways against the column's surface below it. This used
     to say --primitives-color-neutral-white, a token that does not exist: one
     invalid value makes the whole light-dark() invalid, so the cards in fact had
     no background at all. */
  background: var(--primitives-color-neutral-0);
  font-size: 0.82rem;
}

.casus-labels {
  display: flex;
  flex-wrap: wrap;
  gap: var(--primitives-space-4);
}

/* The empty-state line sits inside a column, where the page-level padding would
   push it out of its box. */
.funnel-kolom .empty {
  padding: var(--primitives-space-8) 0;
  font-size: 0.8rem;
}

/* ── Organisatiestappen (#89) ────────────────────────────────────────────── */

/* One dropdown per step, six of them per row. They have to fill their column:
   left at their content width the six would sit ragged, and "in uitvoering" is
   twice as wide as "nvt". */
.stap-keuze,
.stap-keuze select,
.fase-keuze,
.fase-keuze select {
  width: 100%;
}

/* An organisation the registry does not know still lists, but has nowhere to
   link to; it must not look like a dead link. */
.org-link.is-inert {
  color: inherit;
}

/* ── Voortgangstrechter ──────────────────────────────────────────────────── */

/* Cumulative reach per step, beside the pie. The bars themselves are
   nldd-progress-bar; this only stacks them and gives the group its width. Each
   bar carries its own label, value and track, so there is nothing here to
   style — reaching into the component would be fighting it. */
.funnel {
  display: flex;
  flex-direction: column;
  gap: var(--primitives-space-12);
  min-width: 16rem;
  flex: 1 1 16rem;
}

/* ── Mijn account ───────────────────────────────────────────────────────── */

.account-kop {
  display: flex;
  align-items: center;
  gap: var(--primitives-space-16);
}

.account-avatar {
  border-radius: 50%;
  /* GitLab serves square avatars, but a non-square one would otherwise stretch
     rather than crop. */
  object-fit: cover;
}

.account-naam {
  margin: 0;
  font-size: 1.25rem;
  font-weight: 600;
}

.account-kop .meta {
  margin: var(--primitives-space-4) 0 0;
}

/* ── Overall ──────────────────────────────────────────────────────────────
   The table reuses the Sectorregistraties chrome (.filters, .chip, .tally,
   .result-count, nldd-table-row[hidden]); only the things a GitLab issue has
   and a registration does not are new. */

/* This screen shows a bar per label scope, so it has six of them where
   Sectorregistraties has three. A caption column of one width lines the chips
   up instead of starting them at six offsets. */
#overall-filters .filter-label {
  flex-shrink: 0;
  min-width: 6.5rem;
}

.issue-cell {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--primitives-space-6);
  min-width: 0;
}

/* The project an issue came from. Quiet by design: it answers "why is this
   row here", which only comes up when a board spans more than one project. */
.proj-badge {
  padding: 0 5px;
  border-radius: 3px;
  font-size: 0.65rem;
  font-weight: 600;
  white-space: nowrap;
  color: var(--primitives-color-neutral-600);
  background: var(--primitives-color-neutral-100);
}

.milestone-badge {
  padding: 0 5px;
  border-radius: 3px;
  font-size: 0.65rem;
  font-weight: 600;
  white-space: nowrap;
  border: 1px solid var(--primitives-color-neutral-300);
  color: var(--primitives-color-neutral-700);
}

.label-list {
  display: inline-flex;
  flex-wrap: wrap;
  gap: var(--primitives-space-4);
}

/* A label carries its colour as a left bar rather than a fill: the palette is
   assigned by name and is not chosen for contrast against text, so a filled
   badge would be unreadable for some of the labels in either theme. */
.label-badge {
  padding: 0 5px 0 4px;
  border-left: 3px solid var(--dot);
  border-radius: 2px;
  font-size: 0.7rem;
  font-weight: 600;
  white-space: nowrap;
  color: var(--primitives-color-neutral-700);
  background: var(--primitives-color-neutral-100);
}

/* ── Wachten op GitLab ────────────────────────────────────────────────────
   Every screen here is assembled from GitLab calls that can take seconds, so
   the wait needs to be visible. It also needs to not be visible when the 60s
   cache answers in milliseconds, which is the common case. */

/* Held back a second — the design system's own threshold for a wait worth
   mentioning. Nothing moves: a zero-length animation on a one-second delay
   only flips visibility at the end, so there is no motion for
   prefers-reduced-motion to suppress and no shimmer to draw the eye to the
   waiting rather than to what is arriving.

   The clock starts when the element is first laid out, which for an
   htmx-indicator is the moment its request starts. */
/* The empty state of the search suggestions, in three faces: loading, nothing
   found, and fetch failed. All three are in the markup and app.js only flips
   data-status -- that keeps the Dutch texts in the template, with the rest of
   the user-facing wording, instead of in JavaScript.

   This is not the same question as "are there items": the menu shows this slot
   as soon as nothing is visible, and that is the case while fetching too. */
.zoekstatus > * {
  display: none;
}

.zoekstatus[data-status="laden"] .is-laden,
.zoekstatus[data-status="klaar"] .is-leeg,
.zoekstatus[data-status="mislukt"] .is-mislukt {
  display: block;
}

.laadblok {
  visibility: hidden;
  animation: fds-laadblok 0s linear 1s forwards;
}

@keyframes fds-laadblok {
  to {
    visibility: visible;
  }
}

/* htmx's own indicator styles are switched off in the layout: they use opacity,
   which leaves the indicator holding its space while invisible — a blank gap
   the width of the table that is about to arrive. display takes it out of the
   flow instead. */
.htmx-indicator {
  display: none;
}

.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  display: block;
}

/* The preview pane keeps its previous render while a new one is fetched, the
   same trade the sectorregistraties row makes: dimmed, so it is visibly stale
   rather than either blank or apparently current. */
#edit-preview.htmx-request {
  opacity: 0.55;
}

/* The wait inside the edit sheet fills the space the form will occupy, so the
   sheet does not resize under the user when the form lands. */
#edit-sheet-body .laadblok {
  padding: var(--primitives-space-24) 0;
}

/* Over the preview pane, centred, above the stale render it dims. */
#preview-laden {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  translate: -50% -50%;
  z-index: 1;
}

/* The save spinner sits between the two buttons; the row is a flex row, so it
   only needs to keep out of the way of their alignment. */
.edit-actions #opslaan-laden {
  flex: 0 0 auto;
}

/* The confirmation that replaces the form after a save. Where the form filled
   the sheet, this is a short panel, so it gets the same breathing room the
   wait indicator has rather than sitting flush against the sheet's edge.
   sizeEditForm no-ops here -- it measures .edit-form, which this replaced --
   so the sheet sizes itself to the panel. */
#edit-sheet-body .opgeslagen {
  padding: var(--primitives-space-24);
}

/* ── Tijdslijn ──────────────────────────────────────────────────────────── */

/* The trailing column of a timeline row: what the change was about, and where
   it came from. A column rather than a line, because a subject is often long
   enough to wrap and the tag would then float away from it. */
.tijdslijn-onderwerp {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--primitives-space-4);
  font-size: 0.85rem;
}

/* The row that closes off a slice: the "meer laden" button, or the line saying
   the feed ends here. It carries the end of the vertical track, so it keeps the
   row height of the entries above it rather than collapsing to its content. */
.tijdslijn-sluit {
  min-height: 48px;
}
