/*
  Design tokens + modern CSS, hand-authored on platform primitives (handout §9).
  TOKEN VALUES ARE PLACEHOLDERS pending the one-time designer handoff. This pass
  is structural: one token set, no parallel knobs, and the list layer generic so
  every program page inherits it with zero new CSS. Fine spacing is left ad-hoc
  on purpose — it's a designer decision, not a doubling to crush.
*/
:root {
  /* colour ------------------------------------------------------------- */
  --color-accent: #336699;            /* signature brand #369 */
  --color-bg: #ffffff;
  --color-fg: #1a1a1a;
  --color-muted: #6b7280;             /* de-emphasised text: empty states, disabled */
  --color-topbar-bg: #111111;         /* black header bar */
  --color-topbar-fg: #ffcc00;         /* yellow header text */
  --color-topbar-hover: rgba(255, 255, 255, 0.12);
  --color-backdrop: rgba(0, 0, 0, 0.45);   /* modal dialog backdrop */
  --color-ok: #1a7f37;
  --color-warn: #b45309;   /* caution: notable-but-expected (e.g. self-deregister) */
  --color-fail: #b42318;
  --color-border: #999999;
  --color-hover: #eef2f6;             /* interactive surface tint: hover states */
  --color-row-alt: #f4f6f8;           /* zebra stripe: non-interactive, quieter than hover */
  /* type --------------------------------------------------------------- */
  --font: Verdana, "Segoe UI", system-ui, sans-serif;
  --font-lg: 1.3rem;                  /* page / card headings */
  --font-sm: 0.9rem;                  /* secondary / meta text */
  /* geometry ----------------------------------------------------------- */
  --space: 1rem;
  --space-xs: 0.25rem;
  --space-sm: 0.5rem;
  --space-lg: 1.5rem;
  --radius: 6px;
  --radius-pill: 999px;
  --shadow: 0 6px 18px rgba(0, 0, 0, 0.22);
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-hover: #23303a;
    --color-row-alt: #1c1c1c;
    --color-fg: #e8e8e8;
    --color-muted: #9aa1ab;
    --color-border: #555555;
    --color-hover: #23303a;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font);
  color: var(--color-fg);
  background: var(--color-bg);
}

/* environment tint: ambient page background per dataspace class */
body.env-staff { background: color-mix(in srgb, var(--color-fail)  6%, var(--color-bg)); }
body.env-test  { background: color-mix(in srgb, var(--color-ok)    6%, var(--color-bg)); }
body.env-base  { background: color-mix(in srgb, #7b2fbf           18%, var(--color-bg)); }

/* header bar */
.topbar {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space);
  padding: var(--space);
  background: var(--color-topbar-bg);
  color: var(--color-topbar-fg);
}
.topbar .brand {
  font-weight: 700;
  color: var(--color-topbar-fg);
  text-decoration: none;
}

/* top navigation (black bar) */
.topbar .brand {
  font-weight: 700;
  color: var(--color-topbar-fg);
  text-decoration: none;
  margin-right: auto;   /* pushes the usermenu to the right (was on .topnav) */
}

/* user dropdown (native details/summary, no JS) */
.usermenu { position: relative; }
.usermenu > summary {
  list-style: none;
  cursor: pointer;
  font-size: var(--font-sm);
  color: var(--color-topbar-fg);
}
.usermenu > summary::-webkit-details-marker { display: none; }
.usermenu-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 0.35rem);
  min-width: 12rem;
  display: grid;
  background: var(--color-bg);
  color: var(--color-fg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  overflow: hidden;
  z-index: 10;
}
.usermenu-panel a {
  padding: 0.5rem 0.75rem;
  color: var(--color-accent);
  text-decoration: none;
}
.usermenu-panel a:hover { background: var(--color-hover); }

/* login (centered card) */
main { padding: var(--space); }
.card {
  width: 100%;
  max-width: 22rem;
  margin: 0 auto;
  display: grid;
  gap: var(--space);
  padding: var(--space-lg);
  border: 1px solid var(--color-accent);
  border-radius: var(--radius);
}
.card h1 { margin: 0; font-size: var(--font-lg); color: var(--color-accent); }
form { display: grid; gap: calc(var(--space) * 0.75); }
label { display: grid; gap: 0.25rem; font-size: var(--font-sm); }
input {
  padding: 0.5rem;
  font: inherit;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}
button {
  padding: 0.5rem;
  font: inherit;
  color: #ffffff;
  background: var(--color-accent);
  border: 0;
  border-radius: var(--radius);
  cursor: pointer;
}
.result { margin: 0; font-weight: 700; }
.result.ok { color: var(--color-ok); }
.result.fail { color: var(--color-fail); }

/* portal shell (header + left menu + content) */
.portal {
  display: grid;
  grid-template-columns: 14rem 1fr;
  gap: var(--space);
  padding: var(--space);
  align-items: start;
}

/* site menu: collapsible groups with sub-choices (native details, no JS) */
.menu-groups { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.25rem; }
.menu-group > details > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem 0.75rem;
  border-radius: var(--radius);
  font-weight: 600;
  color: var(--color-fg);
}
.menu-group > details > summary::-webkit-details-marker { display: none; }
.menu-group > details > summary::after {
  content: "\25B8";               /* triangle marker */
  font-size: 0.8em;
  transition: transform 0.15s ease;
}
.menu-group > details[open] > summary::after { transform: rotate(90deg); }
.menu-group > details > summary:hover { background: var(--color-hover); }
.menu-group ul {
  list-style: none;
  margin: 0.1rem 0 0.25rem;
  padding: 0 0 0 0.5rem;
  display: grid;
  gap: 0.15rem;
}
.menu-group ul a {
  display: block;
  padding: 0.4rem 0.75rem;
  border-radius: var(--radius);
  color: var(--color-accent);
  text-decoration: none;
  font-size: var(--font-sm);
}
.menu-group ul a:hover { background: var(--color-hover); }
.menu-group ul a.active { background: var(--color-accent); color: #ffffff; }


/* Grid content column must be allowed to shrink below its intrinsic content
   width. Without this, a DyGraphs canvas fixed at the wide (menu-collapsed)
   pixel width holds the 1fr column open — so re-expanding the menu can't narrow
   the column, and the charts overflow the viewport instead of shrinking back.
   min-width:0 overrides the grid track's default min-width:auto. */
.portal main { padding: 0; min-width: 0; }
.portal main h1 { margin-top: 0; font-size: var(--font-lg); color: var(--color-accent); }

/* mobile: single column; the site menu sits full-width above content */
@media (max-width: 820px) {
  .portal { grid-template-columns: 1fr; }
}

/* side-menu collapse toggle (no JS: checkbox + sibling selectors) --------- */

/* the toggle button in the black bar */
.sidebar-toggle {
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  padding: 0.4rem 0.5rem;
  border-radius: var(--radius);
  color: var(--color-topbar-fg);
}
.sidebar-toggle:hover { background: var(--color-topbar-hover); }
.sidebar-icon {
  display: block;
  width: 18px;
  height: 14px;
  border: 2px solid currentColor;
  border-radius: 2px;
  position: relative;
}
/* filled left column = the panel that gets collapsed */
.sidebar-icon::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 5px;
  background: currentColor;
}

/* collapsed: menu removed, content spans the full width (for DyGraphs) */
#menu-collapse:checked ~ .portal { grid-template-columns: 1fr; }
#menu-collapse:checked ~ .portal .menu { display: none; }
/* reflect state in the icon: hollow out the left column when collapsed */
#menu-collapse:checked ~ .topbar .sidebar-icon::before { background: none; }

/* muted empty-state line inside a menu group */
.menu-empty {
  padding: 0.4rem 0.75rem;
  font-size: var(--font-sm);
  font-style: italic;
  color: var(--color-muted);
}

/* ── List component ─────────────────────────────────────────────────────
   Ground model for every program list page: filter row, active-filter chips,
   sortable table, pager. Generic `.list-*` — a new list page reuses these with
   zero new CSS. All values route through :root; no page-local knobs.          */
.list-head   { margin-bottom: var(--space); }

.list-filter { display: flex; flex-wrap: wrap; gap: var(--space-sm); margin-bottom: var(--space); }
.list-filter input { padding: var(--space-sm); }   /* border/radius inherited from global input */

.list-chips  { display: flex; flex-wrap: wrap; gap: var(--space-xs); margin-bottom: var(--space-sm); }
.list-chips .chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-xs);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-pill);
  background: var(--color-hover);
  color: var(--color-fg);
  text-decoration: none;
  font-size: var(--font-sm);
}
.list-chips .chip-x { font-weight: 700; }

.list-table { width: 100%; border-collapse: collapse; }
.list-table th,
.list-table td { padding: var(--space-sm); text-align: left; border-bottom: 1px solid var(--color-border); }
.list-table td.nowrap,
.list-table th.nowrap { white-space: nowrap; }
.list-table th.sortable a { text-decoration: none; color: inherit; }
.list-table th.sortable.asc  a::after { content: " \25B2"; }   /* up triangle   */
.list-table th.sortable.desc a::after { content: " \25BC"; }   /* down triangle */
.list-table tbody tr:nth-child(even) { background: var(--color-row-alt); }
.list-empty { text-align: center; color: var(--color-muted); padding: var(--space-lg); }

.list-pager { display: flex; align-items: center; gap: var(--space); margin-top: var(--space); }
.list-pager .disabled { color: var(--color-muted); }

/* ── Buttons (shared: used by lists, modals, future pages) ──────────────── */
.btn-primary {
  padding: var(--space-sm) var(--space);
  font: inherit;
  color: #ffffff;
  background: var(--color-accent);
  border: 0;
  border-radius: var(--radius);
  cursor: pointer;
}
.btn-cancel {
  padding: var(--space-sm) var(--space);
  font: inherit;
  color: var(--color-fg);
  background: none;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  cursor: pointer;
}
/* text-style action buttons for table rows (Edit / Delete) */
.btn-link {
  padding: var(--space-xs) var(--space-sm);
  font: inherit;
  color: var(--color-accent);
  background: none;
  border: 0;
  border-radius: var(--radius);
  cursor: pointer;
}
.btn-link:hover { background: var(--color-hover); }
.btn-danger { color: var(--color-fail); }

/* ── List head: title left, primary action right ───────────────────────── */
.list-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space);
  margin-bottom: var(--space);
}
.list-head h1 { margin: 0; font-size: var(--font-lg); color: var(--color-accent); }

/* Header-uitlijning volgt de kolominhoud: numerieke kolommen (Prioriteit /
   Volgorde / Capaciteit) en de actiekolom krijgen expliciet een class op de th,
   rechts uitgelijnd om de linkse default van `.list-table th` te overrulen. */
.list-table th.num { text-align: right; }
.list-table th.actions-head
{
  text-align: right;
  padding-right: var(--space-lg);
}

/* right-aligned numeric column (capacity) */
.list-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
/* Action cell stays a REAL table-cell (no display:flex — a flex td leaves the
   table's row-height algorithm: buttons pin to the top and the bottom border
   sits at the cell's own height instead of the row's). Alignment via
   vertical-align + text-align; inter-button spacing via sibling margin,
   replacing the old flex gap. */
.list-table td.row-actions {
  vertical-align: middle;
  text-align: right;
  padding-right: var(--space-lg);
  white-space: nowrap;
}
.list-table td.row-actions .btn-icon + .btn-icon {
  margin-left: var(--space-lg);
}

/* Icon action buttons: glyph-only, character injected via content so the delete
   glyph (can be A/B'd in ONE place. The flex gap on
   .row-actions handles spacing; justify-content:flex-end right-aligns the pair. */
.btn-icon {
  background: none;
  border: 0;
  cursor: pointer;
  padding: 0;
  font-size: 1.1rem;
  line-height: 1;
}
.btn-icon:disabled {
	opacity: 0.35;
	cursor: not-allowed;
	pointer-events: auto; /* keep title tooltip working despite disabled */
}
.btn-edit::before { content: "\270F\FE0F"; }
.btn-delete::before { content: "\274C"; }
.btn-chart::before { content: "\1F4C8"; }   /* U+1F4C8 chart with upwards trend */

/* ── Notice line (list-level feedback: e.g. delete-in-use) ─────────────── */
.list-notice {
  margin-bottom: var(--space-sm);
  padding: var(--space-sm) var(--space);
  border-radius: var(--radius);
}
.list-notice:empty { display: none; }          /* no chrome when there's no message */
.list-notice-error {
  color: var(--color-fail);
  background: color-mix(in srgb, var(--color-fail) 10%, transparent);
}

/* ── Modal (shared <dialog>; the modal-edited-table archetype) ──────────── */
.modal {
  width: min(32rem, calc(100vw - 2 * var(--space)));
  padding: 0;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-bg);
  color: var(--color-fg);
  box-shadow: var(--shadow);
}
.modal::backdrop { background: var(--color-backdrop); }

.modal-form { display: grid; gap: var(--space); padding: var(--space-lg); margin: 0; }
.modal-title { margin: 0; font-size: var(--font-lg); color: var(--color-accent); }
.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
}

/* wide modal: for pages whose modal carries more than a single field stack —
   the farmer-loading editor (form + two context tables). Overrides the default
   .modal width; capped so it never touches the viewport edges. */
.modal-wide {
  width: min(60rem, calc(100vw - 2 * var(--space)));
}

/* medium modal: the trigger-event editor (note thread width) */
.modal-medium {
  width: min(44rem, calc(100vw - 2 * var(--space)));
}

/* ── Form fields (modal + reusable) ─────────────────────────────────────── */
.field { display: grid; gap: var(--space-xs); font-size: var(--font-sm); }
.field > span { color: var(--color-fg); }
.field input,
.field select {
  padding: var(--space-sm);
  font: inherit;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-bg);
  color: var(--color-fg);
}
.field input[readonly] { background: var(--color-hover); color: var(--color-muted); }
.code-input { text-transform: uppercase; width: 5rem; }

/* per-field + general validation messages */
.field-error { color: var(--color-fail); font-size: var(--font-sm); }
.form-error {
  margin: 0;
  padding: var(--space-sm) var(--space);
  color: var(--color-fail);
  background: color-mix(in srgb, var(--color-fail) 10%, transparent);
  border-radius: var(--radius);
}

.code-input-wide { text-transform: uppercase; }

/* ── DB-upgrade: operational review page (prog 9810) ─────────────────────
   Not a list — a version-state review with inspectable DDL and loud, severity-
   coded apply outcomes. The danger style is deliberately the most alarming
   thing in the whole stylesheet: it marks "schema advanced, version did not",
   which needs manual repair.                                                  */
.dbup-intro { color: var(--color-muted); margin: 0 0 var(--space); }
.dbup-summary { font-weight: 600; margin: 0 0 var(--space-sm); }

/* small button variant for the per-row Apply action */
.btn-sm { padding: var(--space-xs) var(--space-sm); font-size: var(--font-sm); }

/* ── state table ── */
.dbup-table { width: 100%; border-collapse: collapse; }
.dbup-table th,
.dbup-table td {
  padding: var(--space-sm);
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--color-border);
}
.dbup-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.dbup-tname { font-weight: 600; }
.dbup-muted { color: var(--color-muted); }

/* rows with a pending step get a faint accent bar; current rows recede */
.dbup-pending td:first-child { box-shadow: inset 3px 0 0 var(--color-accent); }
.dbup-latest { color: var(--color-muted); }

/* ── the DDL statement, shown verbatim for inspection ── */
.dbup-stmt {
  margin: 0;
  padding: var(--space-sm);
  max-width: 40rem;
  overflow-x: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: ui-monospace, "Cascadia Code", Menlo, Consolas, monospace;
  font-size: var(--font-sm);
  background: var(--color-row-alt);
  border-radius: var(--radius);
}

/* ── apply outcome blocks: severity-coded ── */
.dbup-outcome {
  margin-bottom: var(--space);
  padding: var(--space);
  border-radius: var(--radius);
  border-left: 4px solid var(--color-border);
}
.dbup-outcome-msg { margin: 0 0 var(--space-sm); font-weight: 600; }
.dbup-outcome .dbup-stmt { margin-top: var(--space-sm); }

/* raw DB error text, shown verbatim */
.dbup-error-text {
  margin: var(--space-sm) 0 0;
  padding: var(--space-sm);
  font-family: ui-monospace, "Cascadia Code", Menlo, Consolas, monospace;
  font-size: var(--font-sm);
  white-space: pre-wrap;
  word-break: break-word;
  background: color-mix(in srgb, var(--color-fail) 8%, transparent);
  border-radius: var(--radius);
}

/* ok — quiet green, this is the common case */
.dbup-ok {
  border-left-color: var(--color-ok);
  background: color-mix(in srgb, var(--color-ok) 8%, transparent);
}
.dbup-ok .dbup-outcome-msg { color: var(--color-ok); }

/* deregistered — amber: expected, but worth noticing */
.dbup-deregistered {
  border-left-color: var(--color-warn);
  background: color-mix(in srgb, var(--color-warn) 8%, transparent);
}
.dbup-deregistered .dbup-outcome-msg { color: var(--color-warn); }

/* error — red, but not catastrophic (version untouched, retry-safe) */
.dbup-error {
  border-left-color: var(--color-fail);
  background: color-mix(in srgb, var(--color-fail) 8%, transparent);
}
.dbup-error .dbup-outcome-msg { color: var(--color-fail); }

/* danger — the loudest thing here: schema moved, version didn't. Solid red
   fill, heavy border, so it cannot be mistaken for an ordinary error. */
.dbup-danger {
  border: 2px solid var(--color-fail);
  border-left-width: 6px;
  background: color-mix(in srgb, var(--color-fail) 16%, transparent);
}
.dbup-danger .dbup-outcome-msg { color: var(--color-fail); font-size: 1.05em; }

/* banner: full-width alarm (load failure, pass halt) */
.dbup-banner {
  margin-bottom: var(--space);
  padding: var(--space);
  border-radius: var(--radius);
  font-weight: 600;
}
.dbup-banner.dbup-danger { border-left-width: 6px; }

/* ── Trends: the visualization page (prog 4302) ────────────────────────────
   Archetype D. Header controls drive a client-side re-fetch (no reload); each
   .trends-chart is an empty container DyGraphs hydrates from /trends/data. */
.trends-header {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  align-items: center;
  margin-bottom: var(--space);
}
.trends-header select,
.trends-header input[type="date"] {
  padding: var(--space-sm);
  font: inherit;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-bg);
  color: var(--color-fg);
}
/* min-width:0 (here + on the block + column) lets these nested grids shrink below
   the DyGraphs canvas's intrinsic width — the same min-width:auto blowout fixed
   on .portal main, recurring at the two inner grid levels. Without it the wide
   (collapsed) canvas holds the grid items open, so re-expanding the menu can't
   narrow them. */
.trends-charts {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-lg);
}
.trends-chart-block {
  display: grid;
  gap: var(--space-xs);
  min-width: 0;
}
.trends-chart-title { margin: 0; font-size: var(--font-lg); color: var(--color-accent); }
/* DyGraphs draws into this box; width fills the column, height is flat (ysize is
   ignored, matching the old rendering, and one row's ysize is a 4px typo). */
.trends-chart { width: 100%; min-width: 0; }
.trends-empty { color: var(--color-muted); padding: var(--space-lg); text-align: center; }

/* ── Breed growth-curve editor (prog 2020) ────────────────────────────────
   The modal-CRUD archetype plus a computed DyGraphs growth curve (replacing
   picture/grow-curve.php's PNG). The curve is drawn client-side by breed.js;
   height is set inline at creation (DyGraphs fixes it then), width fills the
   modal. .breed-grid pairs the six Gompertz inputs two-up. */
.breed-chart { width: 100%; }
.breed-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space);
}
.breed-curve-modal { display: grid; gap: var(--space); padding: var(--space-lg); }

/* ── DyGraphs legend: compact hover overlay ────────────────────────────────
   The default DyGraphs legend is full-width and opaque, blocking dense charts
   on hover. Constrain it to a small pinned box in the top-left, semi-transparent
   so the plot reads through it. Ported from the PHP site's .dygraph-legend. */
.dygraph-legend {
  display: inline-block;
  width: auto !important;
  left: 65px !important;
  top: 5px !important;
  padding: 0.5em;
  font-size: 8pt !important;
  font-weight: bold;
  text-align: left;
  background-color: color-mix(in srgb, var(--color-hover) 70%, transparent) !important;
  border: 1px solid var(--color-accent);
  border-radius: var(--radius);
}
.dygraph-legend > span {
  display: block;
  margin-bottom: 0.2em;
  padding: 0;
  font-weight: normal;
}
.dygraph-legend > span.highlight {
  font-weight: bold;
  padding: 3px 0;
  font-size: 125%;
}

/* ── Site form: checkbox rows ─────────────────────────────────────────────── */
.field-check {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  font-size: var(--font-sm);
}
.field-check input { width: auto; }

/* Breadcrumb (drill-down trail on graph / graphsensor pages). Separator glyph is
   injected via content-escape so the template source stays pure ASCII (no literal
   non-ASCII character to trip the single-char sweep). \203A = SINGLE RIGHT-
   POINTING ANGLE QUOTATION MARK. */
.crumbs {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space);
  font-size: var(--font-sm);
}
.crumbs a { color: var(--color-accent); text-decoration: none; }
.crumbs a:hover { text-decoration: underline; }
.crumb-sep::before { content: "\203A"; color: var(--color-muted); }
.crumb-current { color: var(--color-fg); font-weight: 600; }

/* Colour swatch in the sensor-line list: a small filled rectangle showing the
   chosen colour (the colour NAME is a CSS keyword, e.g. CornflowerBlue, used
   directly as the background). Sits inline before the colour name. Automatic
   (NULL) rows show the muted "(auto)" marker instead, no swatch. */
.color-swatch {
  display: inline-block;
  width: 1.1em;
  height: 1.1em;
  margin-right: var(--space-xs);
  vertical-align: middle;
  border: 1px solid var(--color-border);
  border-radius: 2px;
}
.color-auto {
  color: var(--color-muted);
  font-style: italic;
}

/* Colour field in the sensor modal: native <select> + live swatch preview beside
   it (JS mirrors the selected option's colour). Always visible: a named colour
   shows a solid swatch; "(automatic)" shows a diagonal-stripe placeholder (the
   view-side cycle colour isn't known here, so we show "no fixed colour" rather
   than guess one). */
.color-field {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}
.color-field select { flex: 1; }
.color-swatch-preview {
  width: 1.4em;
  height: 1.4em;
  flex: none;
}
/* automatic placeholder: diagonal stripes read as "none / auto", and can't be
   mistaken for the real palette colour "Gray". No image asset — pure gradient. */
.color-swatch-auto {
  background: repeating-linear-gradient(
    45deg,
    var(--color-hover),
    var(--color-hover) 3px,
    var(--color-muted) 3px,
    var(--color-muted) 6px
  ) !important;
}

/* --- vehicle-combination matrix (prog 3920) --- */
.combi-grid th.combi-corner   { text-align: left; white-space: nowrap; }
.combi-grid th.combi-col      { text-align: center; white-space: nowrap; }
.combi-grid th.combi-row-head { text-align: left; white-space: nowrap; }
.combi-grid td.combi-cell     { text-align: center; }
.combi-grid td.combi-cell input[type="checkbox"] { cursor: pointer; }

/* ── Print + batch-list working sheet (prog 3155) ─────────────────────────
   First print-targeted page in the Go portal. Screen: normal portal chrome.
   Print: hide chrome + controls, collapse the grid, clean black-on-white sheet.
   Replaces the old print.css. .no-print marks anything that must not print
   (the day selector, the Print button). */
.batch-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space);
  margin-bottom: var(--space);
}
.batch-select { display: flex; align-items: flex-end; gap: var(--space-sm); margin: 0; }
.batch-select .field { min-width: 12rem; }
.batch-title { margin: 0 0 var(--space); font-size: var(--font-lg); color: var(--color-accent); }

.batch-sheet { width: 100%; border-collapse: collapse; }
.batch-sheet th,
.batch-sheet td { padding: var(--space-sm); text-align: left; border-bottom: 1px solid var(--color-border); }
.batch-sheet th.num,
.batch-sheet td.num { text-align: right; font-variant-numeric: tabular-nums; }
.batch-sheet td.nowrap,
.batch-sheet th.nowrap { white-space: nowrap; }
.batch-sheet tbody tr:nth-child(even) { background: var(--color-row-alt); }
.batch-sheet .batch-total td { border-top: 2px solid var(--color-fg); border-bottom: 0; font-weight: 700; }

@media print {
  .topbar,
  .menu,
  .no-print { display: none !important; }

  .portal { grid-template-columns: 1fr; gap: 0; padding: 0; }
  .portal main { padding: 0; }

  /* clean black-on-white: solid borders, no zebra, no brand colour */
  .batch-title { color: #000; }
  .batch-sheet th,
  .batch-sheet td { border: 1px solid #000; color: #000; }
  .batch-sheet tbody tr:nth-child(even) { background: none; }
  .batch-sheet .batch-total td { border: 1px solid #000; border-top: 2px solid #000; }

  .batch-sheet tr { break-inside: avoid; }
}

/* ── Batch-number maintenance (prog 3150): modal-CRUD over a day's lines ───
   Reshaped onto the country editor archetype: the list reuses .list-table, the
   modal reuses .modal-form. The day selector (.batch-select, shared with the
   3155 report) picks which day's lines show; add/edit/delete run through the
   shared <dialog>. The Aantal total sits above the table and refreshes OOB on
   every mutation. No print target here — the 3155 report is the print sheet. */
.batch-head-actions { display: flex; gap: var(--space-sm); }
.batch-total-line {
  margin-bottom: var(--space);
  font-size: var(--font-lg);
  color: var(--color-accent);
}
.batch-total-line strong { font-variant-numeric: tabular-nums; }

/* read-only context rows in the edit modal (farm + batch code: shown, not edited) */
.field-readonly {
  margin: 0;
  padding: var(--space-sm);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-hover);
  color: var(--color-muted);
}

/* ── Farmer prognose (prog 2210): weekly matrix on the report sheet ────────
   Reuses .batch-sheet; cells stack per-group values top-aligned. The prognose
   total is brand-blue (PHP class="bblue"), a negative saldo is fail-red
   (class="bred"). */
.farmer-sheet td { vertical-align: top; }
.prognose-blue { color: var(--color-accent); font-weight: 700; }
.prognose-neg  {
  color: var(--color-fail);
  font-weight: 700;
  padding: 0 var(--space-xs);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--color-fail) 14%, transparent);
}

/* loading-prognose sheet: the red marker must not force bold — weight follows
   the wrapper (plain span in detail rows, <b> in subtotal/total rows) */
.farmer-loading-sheet .prognose-neg { font-weight: inherit; }

/* --- holiday calendar (prog 9050) ------------------------------------------ */
.holiday-content .cal { margin-bottom: 1.5rem; }

.cal-nav {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  margin-bottom: .5rem;
}
.cal-title { font-weight: 600; min-width: 12ch; text-align: center; font-size: var(--font-lg); }
.cal-nav-btn {
  text-decoration: none;
  font-size: 1.4rem;
  line-height: 1;
  padding: 0 .5rem;
  border-radius: var(--radius, 4px);
}
.cal-nav-btn:hover { background: var(--color-surface-2, rgba(0,0,0,.05)); }

.cal-grid { border-collapse: collapse; width: 100%; table-layout: fixed; }
.cal-grid th {
  font-size: var(--font-sm, .85rem);
  font-weight: 500;
  padding: .25rem;
  text-align: center;
}
.cal-grid td {
  border: 1px solid var(--color-border, #ddd);
  height: 3rem;
  vertical-align: top;
  padding: 0;
}
.cal-wk {
  width: 3ch;
  text-align: center;
  font-size: var(--font-sm, .8rem);
  color: var(--color-text-muted, #888);
  border: none !important;
  vertical-align: middle;
}

.cal-cell.cal-weekend { background: color-mix(in srgb, var(--color-accent, #2a7) 15%, transparent); }
.cal-cell.cal-holiday { background: color-mix(in srgb, var(--color-accent, #2a7) 45%, transparent); }
.cal-cell.cal-today   { outline: 2px solid var(--color-accent, #2a7); outline-offset: -2px; }

.cal-day {
  display: block;
  width: 100%;
  min-height: 3rem;
  border: 0;
  background: transparent;
  cursor: pointer;
  text-align: left;
  padding: .25rem .4rem;
  font: inherit;
  color: inherit;
}
.cal-day:hover { background: color-mix(in srgb, var(--color-accent, #2a7) 25%, transparent); }

.date-parts { display: flex; gap: .5rem; }
.date-parts input[name="day"], .date-parts input[name="month"] { width: 4rem; }
.date-parts input[name="year"] { width: 6rem; }

/* --- slaughter calendar (prog 4020) ---------------------------------------- */
.kal-siteform { display: flex; align-items: center; gap: .5rem; margin-bottom: 1rem; }
.kal-siteform select {
  padding: var(--space-sm);
  font: inherit;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-bg);
  color: var(--color-fg);
}
.cal-toolbar { display: flex; justify-content: flex-end; margin-bottom: .5rem; }
4020
.kal-cell { position: relative; }
.kal-day {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 3.5rem;
  border: 0;
  background: transparent;
  cursor: pointer;
  font: inherit;
  color: inherit;
  padding: .2rem;
}
.kal-daynum {
  position: absolute;
  top: 2px;
  right: 4px;
  font-size: var(--font-sm, .75rem);
  color: var(--color-text-muted, #888);
  line-height: 1;
}
.kal-cap { font-size: 1rem; font-weight: 600; }
.kal-day:hover { background: color-mix(in srgb, var(--color-accent, #2a7) 15%, transparent); }

.time-parts, .date-parts { display: flex; gap: .5rem; }
.time-parts input { width: 4rem; }
.date-parts input[name$="day"], .date-parts input[name$="month"] { width: 4rem; }
.date-parts input[name$="year"] { width: 6rem; }
.unit-input { display: flex; align-items: center; gap: .4rem; }
.unit-input input { width: 8rem; }
.unit { color: var(--color-text-muted, #888); }

.dow-grid { display: flex; flex-wrap: wrap; gap: .5rem 1rem; }
.dow-check { display: flex; align-items: center; gap: .3rem; white-space: nowrap; }
.dow-holidays { font-style: italic; }

.form-hint { font-size: var(--font-sm, .85rem); color: var(--color-text-muted, #888); margin: .25rem 0 0; }

/* Process trigger calendar (prog 4480) — compact day rows: label left,
   select right, controls carrying the standard .field select chrome. */
.calendar-form                 { max-width: 28rem; }
.calendar-day                  { display: flex; align-items: center; justify-content: space-between;
                                 gap: var(--space); }
.calendar-day label            { font-size: var(--font-sm); }
.calendar-day .calendar-date   { margin-left: var(--space-sm); color: var(--color-muted); }
.calendar-day select           { min-width: 12rem;
                                 padding: var(--space-sm);
                                 font: inherit;
                                 border: 1px solid var(--color-border);
                                 border-radius: var(--radius);
                                 background: var(--color-bg);
                                 color: var(--color-fg); }

/* ── Farmer forecast (prog 2200): long-term planning matrix ────────────────
   Wide week x farm grid. The scroll container clips; the header row and the
   label column are sticky so both stay visible while scrolling either axis.
   Two sub-columns per week (early/late); capacity + saldo span both. */
.forecast-scroll {
  overflow: auto;
  max-height: calc(100vh - 12rem);   /* leaves room for topbar + filter form */
}
.forecast-matrix {
  border-collapse: separate;         /* separate: sticky cells keep their borders */
  border-spacing: 0;
  font-size: var(--font-sm);
}
.forecast-matrix th,
.forecast-matrix td {
  padding: var(--space-xs) var(--space-sm);
  border-bottom: 1px solid var(--color-border);
  border-right: 1px solid var(--color-border);
  white-space: nowrap;
  background: var(--color-bg);       /* opaque: content mustn't show through sticky cells */
}
.forecast-matrix td.num { text-align: right; font-variant-numeric: tabular-nums; }

/* sticky header row */
.forecast-matrix thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--color-bg);
  text-align: center;
}
/* sticky label column */
.forecast-rowhead {
  position: sticky;
  left: 0;
  z-index: 1;
  text-align: left;
  font-weight: normal;
}
/* top-left corner: sticky on BOTH axes, above everything */
.forecast-corner {
  position: sticky;
  left: 0;
  top: 0;
  z-index: 3;
}
.forecast-week { border-right: 2px solid var(--color-border); }
.forecast-late { border-right: 2px solid var(--color-border); }  /* thicker divide between weeks */
/* uniform sub-column width: every early/late cell carries the same width, so
   the two halves of each week split it 50/50 instead of tracking content */
.forecast-matrix td.forecast-early,
.forecast-matrix td.forecast-late { width: 2.75rem; min-width: 2.75rem; }
.forecast-span { text-align: center; }
.forecast-total th,
.forecast-total td { border-top: 2px solid var(--color-fg); font-weight: 700; }
.forecast-neg { color: var(--color-fail); font-weight: 700; }
/* Totaal values: the prognose total formatting (accent bold; bold comes from
   .forecast-total itself) */
/* .forecast-matrix tr.forecast-total td { color: var(--color-accent); } */

.report-head {
    display: flex;
    align-items: flex-end;
    gap: 1rem;
}

.report-head .report-select {
    display: flex;
    align-items: flex-end;
    gap: 1rem;
}

.report-head .report-excel {
    margin-left: auto;   /* push the Excel link to the right on the same line */
    display: inline-flex;
    align-items: center;
}

.report-head .report-excel img {
    display: block;      /* drop the inline-image baseline gap */
}

/* forecast: the filter form sits directly on the sticky matrix — breathe */
.forecast .report-head { margin-bottom: var(--space); }

/* ── Farmer loading prognose (prog 2220): grouped per-farm loading list ────
   Flat A-shape oversight list on the .batch-sheet chrome. ONE <tbody> per farm:
   a full-width name band leads each block (the landedit house pattern), detail
   rows follow, a subtotal closes it. Print keeps a block whole and repeats the
   column header per page (rules in the @media print block below). Zebra is
   Go-computed (.farmer-alt) because the band + subtotal rows would throw
   :nth-child parity off. */
.farmer-loading-sheet .farmer-block-head td {
  padding-top: var(--space);
  border-bottom: 2px solid var(--color-accent);
}
.farmer-loading-sheet .farmer-block-title {
  margin: 0;
  font-size: var(--font-lg);
  color: var(--color-accent);
}
.farmer-loading-sheet .farmer-subtotal td {
  border-top: 1px solid var(--color-fg);
  border-bottom: 2px solid var(--color-border);
}
/* neutralise the inherited .batch-sheet nth-child zebra (equal specificity,
   later in source -> wins), then stripe only Go-flagged detail rows */
.farmer-loading-sheet tbody tr:nth-child(even) { background: none; }
.farmer-loading-sheet tbody tr.farmer-alt { background: var(--color-row-alt); }

@media print {
  /* keep each farm block on one page where it fits; a block taller than a page
     breaks because it must, with the column header repeating on the next page */
  .farmer-loading-sheet tbody { break-inside: avoid; }
  .farmer-loading-sheet thead { display: table-header-group; }
  .farmer-loading-sheet .farmer-block-title { color: #000; }
  .farmer-loading-sheet .farmer-block-head td { border-bottom: 2px solid #000; }
  /* drill-down links print as plain numbers */
  .farmer-loading-sheet td a { color: inherit; text-decoration: none; }
}

.list-head-controls {
  display: flex;
  align-items: center;
  gap: 1rem;
  margin-right: auto;   /* pull the whole control cluster to the left */
}
.list-head-main {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  align-items: flex-start;
}
.farm-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  white-space: nowrap;
}

.row-inactive-tag { color: var(--color-text-muted); font-style: italic; }

.linklike {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: var(--color-link, #0066cc);
  text-decoration: underline;
  cursor: pointer;
}
.linklike:hover {
  text-decoration: none;
}

/* context tables in the farmer-loading modal */
.ctx-table { width: 100%; border-collapse: collapse; margin-top: var(--space-sm); }
.ctx-table th,
.ctx-table td { padding: var(--space-sm); text-align: left; border-bottom: 1px solid var(--color-border); }
.ctx-table th.num,
.ctx-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
.ctx-table td.nowrap,
.ctx-table th.nowrap { white-space: nowrap; }

/* --- farmer planning (prog 2035) ------------------------------------------ */
.farmerplan .plan-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-sm);
  margin-bottom: var(--space);
}
.farmerplan .plan-controls select {
  padding: var(--space-sm);
  font: inherit;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-bg);
  color: var(--color-fg);
  max-width: 20rem;             /* farmer select carries "code - name"; cap it */
}

.plan-house { margin-bottom: var(--space-lg); }
.plan-house-head {
  display: flex; align-items: baseline; gap: var(--space);
  border-bottom: 2px solid var(--color-accent); margin-bottom: var(--space-xs);
}
.plan-house-head h2 { margin: 0; font-size: var(--font-lg); color: var(--color-accent); }
.plan-house-links { margin-left: auto; font-size: var(--font-sm); }
.plan-house-links a,
.plan-house-links .btn-link { margin-left: var(--space); color: var(--color-accent); text-decoration: none; }
.plan-house-links a:hover,
.plan-house-links .btn-link:hover { text-decoration: underline; }

/* rows: reuse .batch-sheet for the table itself (see template); these are the
   planning-specific extras only */
.plan-check { width: 1.6rem; text-align: center; }
.plan-indent { padding-left: var(--space-lg); }
.plan-flock.grey td   { color: var(--color-muted); }
.plan-flock.vet  td   { font-weight: 700; }
.plan-transport td    { color: var(--color-muted); }
/* .bred cells: reuse the fail colour the rest of the app uses for over-limit */
.plan-table td.bred, .batch-sheet td.bred {
  color: var(--color-fail);
  background: color-mix(in srgb, var(--color-fail) 8%, transparent);
}

.plan-msg {
  padding: var(--space-sm) var(--space); margin-bottom: var(--space);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--color-accent) 12%, transparent);
  border: 1px solid var(--color-accent);
}
.plan-msg-error {
  color: var(--color-fail);
  background: color-mix(in srgb, var(--color-fail) 10%, transparent);
  border-color: var(--color-fail);
}

/* sticky bulk-action pane: two stacked rows (replan, status change), each on
   the .mgroup tinted-panel idiom so they read as separate actions */
.plan-actions {
  position: sticky; bottom: 0; z-index: 5;
  background: var(--color-bg); border-top: 2px solid var(--color-border);
  padding: var(--space-sm) 0; margin-top: var(--space);
  display: grid; gap: var(--space-sm);
}
.plan-row {
  background: color-mix(in srgb, var(--color-accent) 15%, transparent);
  border: 1px solid color-mix(in srgb, var(--color-accent) 45%, transparent);
  border-radius: var(--radius);
  padding: var(--space-sm) var(--space);
}
.plan-row-head {
  margin: 0 0 var(--space-xs);
  font-size: var(--font-sm);
  font-weight: 600;
  color: var(--color-accent);
  text-align: center;
}

/* replan row: earlier button far left, days in the middle, later far right */
.plan-shift-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space);
}
.plan-days { display: flex; align-items: center; gap: var(--space-sm); }
.plan-days input { width: 4rem; }

/* status row: select + apply button, centered; select carries the standard
   app select chrome (trends-header idiom) */
.plan-status-bar {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-sm);
}
.plan-status-bar select {
  padding: var(--space-sm);
  font: inherit;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-bg);
  color: var(--color-fg);
  max-width: 20rem;
}

/* flock-group zebra: the start line + all its loadings share one shade, the
   next flock flips. Go-computed (.plan-alt) because the transport sub-rows
   break :nth-child parity — same reason farmer-loading uses .farmer-alt.
   Both rules paint EVERY row of a group explicitly (alt = row-alt, non-alt =
   base) so the inherited .batch-sheet :nth-child stripe has nothing to show —
   and both outrank it on specificity (table-class + tr.class), so no !important
   and no source-order fragility. */
.batch-sheet.plan-table tbody tr.plan-alt      { background: var(--color-row-alt); }
.batch-sheet.plan-table tbody tr:not(.plan-alt) { background: var(--color-bg); }

/* flock-group separator: a heavier rule on the top of every flock start line
   marks where one flock's group ends and the next begins. The flock <tr> is
   always the first row of its group (its loadings follow as .plan-transport),
   so keying on .plan-flock draws the divider at exactly the group boundaries.
   The first flock in a house gets one too, sitting just under the column
   header — visually fine, and it reinforces "rows below here are grouped". */
.batch-sheet.plan-table tbody tr.plan-flock td {
  border-top: 4px double var(--color-fg);
}

/* ── Framework agreements (prog 2400): weekly buy/sell overview per partner ──
   Read-only report on the .batch-sheet chrome. ONE <tbody> per week: a full-
   width partner/week band leads it, then per weight class the sell (negative,
   red) and buy (positive) lines with a per-class saldo, then a "new line" link.
   Zebra is Go-computed per weight-class block (.framework-alt) because the band,
   saldo and new-line rows would throw :nth-child parity off — same idiom as the
   farmer-loading sheet. */
.framework-sheet .framework-week-head td {
  padding-top: var(--space);
  border-bottom: 2px solid var(--color-accent);
}
.framework-sheet .framework-week-title {
  margin: 0;
  font-size: var(--font-lg);
  color: var(--color-accent);
}
.framework-sheet .framework-total td { border-top: 1px solid var(--color-fg); }
.framework-sheet .framework-newrow td {
  border-bottom: 2px solid var(--color-border);
  font-size: var(--font-sm);
}
.framework-sheet .framework-newrow a { color: var(--color-accent); text-decoration: none; }
.framework-sheet .framework-newrow a:hover { text-decoration: underline; }

/* neutralise the inherited .batch-sheet nth-child zebra (equal specificity,
   later in source -> wins), then stripe only the Go-flagged blocks */
.framework-sheet tbody tr:nth-child(even) { background: none; }
.framework-sheet tbody tr.framework-alt { background: var(--color-row-alt); }

/* sell counts read negative and fail-red (PHP class="red"); a negative saldo is
   fail-red too (PHP class="bred"), a non-negative one is just bold (PHP "vet",
   handled by the <b> in the template). */
.framework-sheet td.fw-sell { color: var(--color-fail); }
.framework-sheet td.fw-neg  { color: var(--color-fail); }

@media print {
  .framework-sheet tbody { break-inside: avoid; }
  .framework-sheet thead { display: table-header-group; }
  .framework-sheet .framework-week-title { color: #000; }
  .framework-sheet .framework-week-head td { border-bottom: 2px solid #000; }
}

/* hatchery planning (prog 2500) — one sheet, one tbody per hatchery (farmer-
   loading idiom); Go-computed stripes, inherited zebra neutralised */
.hp-sheet tbody tr:nth-child(even) { background: none; }
.hp-sheet .hp-odd  > td { background: color-mix(in srgb, var(--color-accent) 8%, transparent); }
.hp-sheet .hp-even > td { background: transparent; }

/* ── Chicken-house editor (prog 2015): two-up field grid in the wide modal ──
   Same idiom as .breed-grid; the ~16 physical fields pair two-up. Width is
   capped by .modal-wide, so no responsive collapse (matching .breed-grid). */
.house-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space);
}

/* farmerhouse: chromeless wrapper so the 999-disable stays one element */
.house-phys {
  border: 0;
  margin: 0;
  padding: 0;
  min-inline-size: 0;
}

/* ── Site editor (prog 8210): two-up field grid in the wide modal ─────────
   Same idiom as .house-grid / .breed-grid; the three classification
   checkboxes sit on one row below the grid. */
.site-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space);
}
.site-checks {
  display: flex;
  gap: var(--space-lg);
  flex-wrap: wrap;
}

/* ── Modal group panels (.mgroup) + compact field grids ───────────────────
   Generic modal-form building blocks (born on the chicken-house editor,
   adopted by the farm editor): tinted group fieldsets (holiday-calendar tint
   levels 15%/45%), 4-up field grid, single-column stack, side-by-side
   sub-panels, postcode+place row, short-input+checkbox cell. */
.mgroup {
  background: color-mix(in srgb, var(--color-accent) 15%, transparent);
  border: 1px solid color-mix(in srgb, var(--color-accent) 45%, transparent);
  border-radius: var(--radius);
  padding: var(--space-sm) var(--space) var(--space);
  margin: 0;
}
/* inside a chromeless wrapper fieldset (e.g. .house-phys) the modal-form grid
   gap doesn't reach; space stacked groups by hand */
.house-phys .mgroup + .mgroup {
  margin-top: var(--space-sm);
}

/* legend = strongest text in the group; floated so it renders INSIDE the
   tinted panel (a non-floated legend straddles the fieldset border) */
.mgroup > legend {
  float: left;
  width: 100%;
  font-weight: 600;
  color: var(--color-accent);
  padding: 0;
  margin: 0 0 var(--space-sm);
}
.mgroup > .mgrid-4,
.mgroup > .mgrid-1 {
  clear: both;
}

/* field labels recede to helper-text level; input values keep full contrast */
.mgroup .field > span {
  color: var(--color-muted);
}

/* 4-up field grid */
.mgrid-4 {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space) var(--space-lg);
}

/* single-column field stack (address panels) */
.mgrid-1 {
  display: grid;
  gap: var(--space);
}

/* two side-by-side sub-panels (invoice / stable address) */
.mgrid-2panel {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space);
}

/* postcode (fixed ~8 chars) + place (rest) on one line */
.addr-pcrow {
  display: grid;
  grid-template-columns: 12ch minmax(0, 1fr);
  gap: var(--space-sm);
}

/* short input + checkbox sharing one field cell (farm code + actief) */
.field-inline {
  display: flex;
  align-items: center;
  gap: var(--space);
}

/* grid items must also shrink below the input's intrinsic width — the
   min-width:auto blowout recurs per nesting level (trends/trig-search idiom):
   track -> .field item -> the input inside .field's own grid. All levels need
   the clamp; number inputs have a fat UA intrinsic width. */
.mgrid-4 > .field,
.mgrid-1 > .field,
.mgrid-2panel > .mgroup,
.addr-pcrow > .field {
  min-width: 0;
}
.mgroup .field input,
.mgroup .field select {
  min-width: 0;
  width: 100%;
}
/* exception: the farm code keeps its short fixed width inside .field-inline */
.field-inline input.code-input {
  width: 5rem;
  flex: 0 0 auto;
}

/* ── Farmer week planning (prog 2033): archetype E, the bulk-edit form ─────
   One tbody per day (band head, editable rows, footer: total / capacity /
   saldo against the product groups, lightest first — the PHP voetje's walk).
   Zebra alternates per DAY, whole day one shade, so it's tbody-level and the
   inherited .batch-sheet nth-child stripe is neutralised (farmer-loading
   idiom). One sticky apply bar (.plan-actions, reused) replaces the PHP's
   per-day Wijzigen buttons. */
.fw-select {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-sm);
  margin-bottom: var(--space);
}
/* standard app select chrome (trends-header idiom) on the week selectors */
.fw-select select {
  padding: var(--space-sm);
  font: inherit;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-bg);
  color: var(--color-fg);
}
.fw-sheet .fw-day-head td {
  padding-top: var(--space);
  border-bottom: 2px solid var(--color-accent);
}
.fw-day-title { margin: 0; font-size: var(--font-lg); color: var(--color-accent); }
.fw-sheet tbody tr:nth-child(even) { background: none; }
.fw-sheet tbody.fw-alt tr { background: var(--color-row-alt); }
.fw-num { width: 5.5rem; }
/* standard app select chrome on the in-table destination / date selects */
.fw-sheet select {
  max-width: 14rem;
  padding: var(--space-sm);
  font: inherit;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-bg);
  color: var(--color-fg);
}
.fw-sheet td.fw-foot-name { padding-left: var(--space-xs); }
.fw-foot td { font-variant-numeric: tabular-nums; }
.fw-neg { color: var(--color-fail); }
.fw-newrow td { border-bottom: 2px solid var(--color-border); font-size: var(--font-sm); }
.fw-newrow a { color: var(--color-accent); text-decoration: none; }
.fw-newrow a:hover { text-decoration: underline; }

/* ── API keys (prog 9035): one-time credential reveal + list extras ─────── */
.apikey-code { font-family: ui-monospace, "Cascadia Code", Menlo, Consolas, monospace; }
.apikey-inactive { color: var(--color-muted); font-style: italic; }
.apikey-reveal {
  margin-bottom: var(--space);
  padding: var(--space);
  border: 2px solid var(--color-warn);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--color-warn) 8%, transparent);
}
.apikey-reveal-title { margin: 0 0 var(--space-sm); font-size: var(--font-lg); color: var(--color-warn); }
.apikey-reveal-line code {
  font-family: ui-monospace, "Cascadia Code", Menlo, Consolas, monospace;
  user-select: all;               /* one click selects the whole credential */
  word-break: break-all;
}

/* ── Production current view (prog 4550): archetype D bar histogram ────────
   Replaces picture/lineproducts.php's polled PNG. Controls persist per-user
   settings (unchanged production_preview_* key names); the chart re-hydrates
   from /production/current-view/data every 5 seconds. Height matches the old
   image's 700px plot area; DyGraphs reads it at creation. */
.cview-header {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-sm);
  align-items: center;
  margin-bottom: var(--space);
}
.cview-header select {
  padding: var(--space-sm);
  font: inherit;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-bg);
  color: var(--color-fg);
}
.cview-chart { width: 100%; min-width: 0; height: 700px; }
.cview-status {
  text-align: center;
  font-variant-numeric: tabular-nums;
  white-space: pre;          /* preserve the triple-space separators */
}

/* ── Production tracepoint counters (prog 4500): live card grid ──────────
   Replaces production-trac.php's hand-rolled XHR poller. #trace-live
   re-fetches /production/trace/data every 30s via htmx; the whole payload
   (cards + updated stamp) swaps in place. One card per sensor. */
.trace-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: var(--space);
  align-items: start;
  margin-bottom: var(--space);
}
.trace-card {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  overflow: hidden;               /* clip row bars to the rounded corners */
}
.trace-card-title {
  margin: 0;
  padding: var(--space-sm) var(--space);
  font-size: 1rem;
  color: #ffffff;
  background: var(--color-accent);
}
.trace-table { width: 100%; border-collapse: collapse; }
.trace-table td { padding: var(--space-xs) var(--space); }
.trace-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
/* distribution bar behind each item row: accent tint up to --trace-pct */
.trace-row {
  background: linear-gradient(to right,
    color-mix(in srgb, var(--color-accent) 14%, transparent) var(--trace-pct, 0%),
    transparent var(--trace-pct, 0%));
}
.trace-total td { border-top: 1px solid var(--color-fg); font-weight: 700; }
.trace-updated { margin: var(--space) 0 0; color: var(--color-muted); font-size: var(--font-sm); }
.trace-note    { color: var(--color-muted); font-size: var(--font-sm); }
.trace-empty   { color: var(--color-muted); padding: var(--space-lg); text-align: center; }
/* soft pulse while a refresh is in flight */
#trace-live.htmx-request { opacity: 0.6; transition: opacity 0.15s ease; }

.geofix-status { margin-top: 0.75rem; font-family: monospace; min-height: 1.25rem; }
.geofix-log { margin-top: 0.5rem; max-height: 30rem; overflow-y: auto;
              font-family: monospace; font-size: 0.85rem;
              border: 1px solid color-mix(in srgb, var(--color-accent) 30%, transparent);
              padding: 0.5rem; }
.geofix-phase   { font-weight: bold; margin-top: 0.5rem; color: var(--color-accent); }
.geofix-err     { color: #b00020; }
.geofix-summary { font-style: italic; }

.geofix-collision { color: color-mix(in srgb, currentColor 55%, transparent); }

/* --- trigger log (prog 4450) --------------------------------------------- */
.trig-filter {
	display: flex;
	gap: var(--space-lg);
	align-items: flex-start;
	flex-wrap: wrap;
	margin-bottom: var(--space);
}
.trig-filter-cols {
	border: 1px solid var(--color-border);
	border-radius: var(--radius);
	padding: var(--space);
	margin: 0;
	display: flex;
	flex-direction: column;
	gap: var(--space-xs);
}
.trig-filter-cols legend {
	padding: 0 var(--space-xs);
	font-size: var(--font-sm);
	color: var(--color-fg);
}
.trig-filter-range {
	display: flex;
	gap: var(--space-sm);
	align-items: center;
}

/* Sticky header replaces the PHP's repeated title row every 24 rows.
   The th background must be opaque or rows show through when scrolled —
   swap the fallback for your table-header token. */
.trig-log thead th {
	position: sticky;
	top: 0;
	z-index: 1;
	background: var(--color-bg, #fff);
}

.trig-log thead tr:first-child th {
	position: sticky;
	top: 0;
	z-index: 1;
	background: var(--color-bg, #fff);
}

.trig-log .trig-search-row th { padding: var(--space-xs) var(--space-sm); }
.trig-search {
	width: 100%;
	min-width: 0;
	box-sizing: border-box;
	padding: var(--space-xs);
	font: inherit;
	font-size: var(--font-sm);
	border: 1px solid var(--color-border);
	border-radius: 0;
	background: var(--color-bg);
	color: var(--color-fg);
}

.trig-log .trig-nowrap { white-space: nowrap; }
.trig-id { text-align: center; }

/* State colours — port of the PHP inline styles (10 red, 20 orange,
   30/40/50 yellow, 80 lawngreen, 99 grey), as translucent mixes so they
   read on both light and dark backgrounds. */
.trig-state-10 { background: color-mix(in srgb, red 35%, transparent); }
.trig-state-20 { background: color-mix(in srgb, orange 40%, transparent); }
.trig-state-30,
.trig-state-40,
.trig-state-50 { background: color-mix(in srgb, yellow 40%, transparent); }
.trig-state-80 { background: color-mix(in srgb, lawngreen 35%, transparent); }
.trig-state-99 { background: color-mix(in srgb, grey 35%, transparent); }

/* ── Trigger event (prog 4460): detail card + note thread ─────────────────
   Event header as a definition grid in a bordered card, message as the card's
   main text. Notes are .dbup-outcome-idiom cards (accent left bar, meta line,
   pre-line body — PHP-era <br> arrives as newlines from Go). */
.trigevent-card {
  max-width: 40rem;
  margin-bottom: var(--space-lg);
  padding: var(--space);
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
}
.trigevent-head {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: var(--space-xs) var(--space);
  margin: 0;
}
.trigevent-head dt { color: var(--color-muted); font-size: var(--font-sm); }
.trigevent-head dd { margin: 0; }
.trigevent-message {
  margin: var(--space) 0 0;
  padding-top: var(--space);
  border-top: 1px solid var(--color-border);
  white-space: pre-line;
}
.trigevent-subtitle { margin: 0 0 var(--space); font-size: var(--font-lg); color: var(--color-accent); }
.trigevent-form { max-width: 40rem; margin-bottom: var(--space-lg); }
.trigevent-form textarea {
  padding: var(--space-sm);
  font: inherit;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-bg);
  color: var(--color-fg);
  resize: vertical;
}
.trigevent-note {
  max-width: 40rem;
  margin-bottom: var(--space);
  padding: var(--space);
  border-radius: var(--radius);
  border-left: 4px solid var(--color-accent);
  background: color-mix(in srgb, var(--color-accent) 6%, transparent);
}
.trigevent-note-meta {
  display: flex;
  gap: var(--space);
  margin-bottom: var(--space-sm);
  font-size: var(--font-sm);
  color: var(--color-muted);
}
.trigevent-note-stamp { font-weight: 600; }
.trigevent-note-body { margin: 0; white-space: pre-line; }
.trigevent-thread { max-height: 20rem; overflow-y: auto; }

.trigevent-message {
  margin: var(--space) 0 0;
  padding-top: var(--space);
  border-top: 1px solid var(--color-border);
  white-space: pre-line;
  font-size: 1.1rem;
  font-weight: 700;
}

/* system configuration (prog 9840): flat global-settings form */
.sysconfig-form { max-width: 28rem; }

/* ── SMS process triggers (prog 4400): modal-CRUD on the wide modal ─────────
   List reuses .list-table; the editor fills .modal-wide. Fields pair two-up
   (.trigsms-fields, the house-grid idiom); the 3x24 hour grid (rows = day
   types, columns = hours) paints by click-drag (trigger-sms.js). The
   checkboxes are the real inputs, so no-JS degrades to a checkbox matrix. */
.trigsms-fields {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space);
}

.trigsms-delay-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}
.trigsms-delay-row input[type="range"] { flex: 1; }
.trigsms-delay-row output {
  min-width: 3ch;
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.trigsms-days {
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  padding: var(--space);
}
.trigsms-days legend {
  padding: 0 var(--space-xs);
  font-size: var(--font-sm);
  color: var(--color-fg);
}

.trigsms-grid {
  border-collapse: collapse;
  user-select: none;          /* drag-painting must not select text */
}
/* Hour labels are FENCE POSTS: each label sits exactly on the divider line
   at the START of its cell (03 above the left edge of the 03:00-04:00 cell),
   with an extra 24 on the right edge of the last cell — 25 labels, 24 cells.
   The th anchors; the span shifts left by half its own width onto the border. */
.trigsms-grid th.trigsms-hour {
  position: relative;
  height: 1.4rem;
  padding: 0;
  font-size: var(--font-sm);
  font-weight: normal;
  color: var(--color-muted);
}
.trigsms-tick {
  position: absolute;
  left: 0;
  bottom: 2px;
  transform: translateX(-50%);
  line-height: 1;
}
.trigsms-tick-end {
  left: auto;
  right: 0;
  transform: translateX(50%);
}
.trigsms-grid th.trigsms-rowhead {
  text-align: left;
  font-weight: 600;
  padding-right: var(--space);
  white-space: nowrap;
}
.trigsms-cell {
  position: relative;
  width: 1.5rem;
  height: 1.5rem;
  text-align: center;
  border: 1px solid var(--color-border);
  cursor: pointer;
}
/* painted-cell feedback; :has keys off the real checkbox state */
.trigsms-cell:has(input:checked) {
  background: color-mix(in srgb, var(--color-accent) 40%, transparent);
}
.trigsms-cell:hover {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}
/* JS mode: hide the raw checkboxes, the cell IS the toggle. The class is
   added by trigger-sms.js, so no-JS keeps visible checkboxes. */
.trigsms-js .trigsms-cell input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

/* Repeat sliders (SMS retrigger only): 0 = repeat DISABLED. The row dims,
   the output shows the localized off-word and the minutes suffix hides.
   Plain delay sliders are untouched — 0 minutes is a valid delay there. */
.trigsms-repeat.trigsms-off .trigsms-delay-row output {
  color: var(--color-muted);
  font-style: italic;
  text-align: left;          /* the off-word reads as a state, not a number */
}
.trigsms-repeat.trigsms-off .trigsms-unit {
  visibility: hidden;        /* keeps its width — no layout jump on toggle */
}
.trigsms-repeat.trigsms-off > span:first-child {
  color: var(--color-muted);
}

/* ── Data reports (prog 4304): daily sensor-value tables ─────────────────
   Reuses .report-head / .batch-sheet / .batch-title wholesale; only the
   inter-block spacing is new. */
.dr-block { margin-bottom: var(--space-lg); }

/* ── Driver planning (prog 3100): SVG Gantt + modal-CRUD trip list ─────────
   The chart is server-rendered SVG (buildDplanGantt computes all geometry);
   width scales to the column via viewBox, tokens make dark mode work. Bars
   carry hx-get (htmx binds hx-* on SVG elements too) → the rit editor. */
.dplan-select {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-sm);
  margin-bottom: var(--space);
}
.dplan-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space);
  margin-bottom: var(--space-sm);
}
.dplan-btn-group { display: flex; gap: var(--space-xs); }

.dplan-gantt { margin-bottom: var(--space-lg); min-width: 0; }
.dplan-gantt svg { display: block; width: 100%; height: auto; }

.dplan-bg        { fill: var(--color-hover); }             /* the PHP's soft #9cc */
.dplan-track     { fill: var(--color-bg); }
.dplan-label     { font-size: 11px; fill: var(--color-fg); }
.dplan-head      { font-size: 11px; fill: var(--color-fg); }
.dplan-head-hour { font-size: 9px;  fill: var(--color-muted); }
.dplan-grid-day  { stroke: var(--color-fg); stroke-width: 2; }
.dplan-grid-hour { stroke: var(--color-border); stroke-width: 1; }
.dplan-now       { stroke: var(--color-fail); stroke-width: 2; }
.dplan-bar       { fill: var(--color-ok); cursor: pointer; }
.dplan-bar:hover,
.dplan-bar:focus { fill: color-mix(in srgb, var(--color-ok) 75%, var(--color-fg)); outline: none; }
.dplan-overlap   { fill: var(--color-fail); pointer-events: none; }  /* clicks fall through to the bar */

.dplan-conflict-cell { text-align: center; }
.dplan-conflict-mark { color: var(--color-fail); font-weight: 700; }
.dplan-noplg     { color: var(--color-muted); padding: var(--space-lg); text-align: center; }

.dplan-form-times {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: var(--space);
}
.dplan-duur { display: flex; align-items: center; gap: var(--space-xs); }
.dplan-duur input { width: 4.5rem; }

/* opaque login-failure message (rendered into #result by login-result) */
.login-error {
  margin: 0;
  padding: var(--space-sm) var(--space);
  color: var(--color-fail);
  background: color-mix(in srgb, var(--color-fail) 10%, transparent);
  border-radius: var(--radius);
  font-size: var(--font-sm);
}

/* ── Menu translations (prog 9021, staff): language-major editor ─────────── */
.mt-select { margin-bottom: var(--space); max-width: 20rem; }
.mt-table input { width: 100%; }
.mt-missing { font-size: var(--font-sm); color: var(--color-warn); }
.mt-saved {
  color: var(--color-ok);
  background: color-mix(in srgb, var(--color-ok) 10%, transparent);
}
.mt-gap td { box-shadow: inset 3px 0 0 var(--color-warn); }

/* ── Customer create (prog 9860): provisioning form + outcome + run log ────
   Form on the .sysconfig-form idiom (capped flat form); outcome panels on the
   .dbup-outcome idiom (severity-coded left-bar cards); the run log is a
   .dbup-stmt-style monospace pre. Buttons size to content, not to the grid
   track. */
.prov-form { max-width: 28rem; }
.prov-form .btn-primary { justify-self: start; }

.prov-result {
  max-width: 40rem;
  margin-bottom: var(--space);
  padding: var(--space);
  border-radius: var(--radius);
  border-left: 4px solid var(--color-border);
}
.prov-result p { margin: 0 0 var(--space-sm); }
.prov-result.prov-ok {
  border-left-color: var(--color-ok);
  background: color-mix(in srgb, var(--color-ok) 8%, transparent);
}
.prov-result.prov-failed,
.prov-result.prov-rolled_back {
  border-left-color: var(--color-fail);
  background: color-mix(in srgb, var(--color-fail) 8%, transparent);
}
.prov-result.prov-rollback_failed {
  border: 2px solid var(--color-fail);
  border-left-width: 6px;
  background: color-mix(in srgb, var(--color-fail) 16%, transparent);
}

.prov-log {
  margin: var(--space-sm) 0 0;
  padding: var(--space-sm);
  max-height: 20rem;
  overflow: auto;
  white-space: pre-wrap;
  word-break: break-word;
  font-family: ui-monospace, "Cascadia Code", Menlo, Consolas, monospace;
  font-size: var(--font-sm);
  background: var(--color-row-alt);
  border-radius: var(--radius);
}

/* ── Customer copy (prog 9861): enqueue form + live queue monitor ─────────
   Form on the .prov-form idiom; queue on .list-table with htmx 5s polling.
   State cell tints reuse the trig-state translucent-mix idiom. */
.ccopy-form { max-width: 28rem; }
.ccopy-form .btn-primary { justify-self: start; }
.ccopy-notice {
  max-width: 40rem;
  padding: var(--space-sm) var(--space);
  border-radius: var(--radius);
  color: var(--color-ok);
  background: color-mix(in srgb, var(--color-ok) 10%, transparent);
}
.ccopy-when { font-size: var(--font-sm); color: var(--color-muted); }
.ccopy-error { color: var(--color-fail); font-size: var(--font-sm); }
.ccopy-stale { color: var(--color-warn); }
.ccopy-state-queued    { background: color-mix(in srgb, grey 20%, transparent); }
.ccopy-state-running   { background: color-mix(in srgb, yellow 35%, transparent); }
.ccopy-state-done      { background: color-mix(in srgb, lawngreen 30%, transparent); }
.ccopy-state-failed    { background: color-mix(in srgb, red 30%, transparent); }
.ccopy-state-cancelled { background: color-mix(in srgb, orange 30%, transparent); }
#ccopy-live.htmx-request { opacity: 0.6; transition: opacity 0.15s ease; }
.ccopy-progress { white-space: pre-line; }

/* ── Flock editor (prog 2031): page-level form on the mgroup panel idiom ──
   Same building blocks as the chicken-house/farm modal editors, hosted on a
   plain page: tinted group panels, .field chrome, capped width. .kop-inline
   pairs an input with its sibling (date + week number, the two uitlaad
   inputs, the sex checkboxes). Sub-blocks (mortality, transports) sit on
   .ctx-table with accent h2 section titles. */
.kop-form { max-width: 48rem; }
.kop-form .btn-primary { justify-self: start; }
.kop-form textarea {
  padding: var(--space-sm);
  font: inherit;
  border: 1px solid var(--color-border);
  border-radius: var(--radius);
  background: var(--color-bg);
  color: var(--color-fg);
  resize: vertical;
}
.kop-inline {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}
.kop-inline input { min-width: 0; }
.farmerkopp h2 {
  margin: var(--space-lg) 0 var(--space-sm);
  font-size: var(--font-lg);
  color: var(--color-accent);
}
.kop-sub { max-width: 60rem; justify-items: start; }
.kop-sub .ctx-table { justify-self: stretch; }
.farmerkopp .ctx-table { max-width: 60rem; }
.farmerkopp .ctx-table input { padding: var(--space-xs) var(--space-sm); }

/* ── UNS tree editor (prog 8440): nested namespace tree + drag re-hang ────
   Tree archetype, not table: one nested <ul> from the root, each node a
   .uns-row line (name, label, meta, icon actions right — the .row-actions
   idiom). Drag-and-drop re-hang (uns-tree.js): dragged node dims, branch rows
   under the cursor tint accent (.uns-drop-hover). Depth reads from the
   indented list borders, the sensor/branch distinction from the leading
   glyph (content-escape idiom: \1F4C1 folder, \1F4CD round pushpin). */
.uns-tree { max-width: 60rem; }
.uns-list {
  list-style: none;
  margin: 0;
  padding: 0 0 0 var(--space-lg);
  border-left: 1px solid var(--color-border);
}
.uns-list-root {
  padding-left: 0;
  border-left: 0;
}
.uns-item { margin: var(--space-xs) 0; }

.uns-row {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius);
}
.uns-row:hover { background: var(--color-hover); }

/* node glyphs, injected via content so the template stays pure ASCII */
.uns-branch > .uns-row .uns-name::before { content: "\1F4C1 "; }
.uns-value  > .uns-row .uns-name::before { content: "\1F4CD "; }

.uns-name { font-weight: 600; }
.uns-label { color: var(--color-muted); font-size: var(--font-sm); }
.uns-meta  { color: var(--color-muted); font-size: var(--font-sm); }

/* action icons right-aligned on the row (the td.row-actions idiom, flex form) */
.uns-row .row-actions {
  margin-left: auto;
  display: flex;
  gap: var(--space);
}
.btn-add::before { content: "\2795"; }   /* U+2795 heavy plus sign */

/* drag states */
.uns-dragging > .uns-row { opacity: 0.4; }
.uns-drop-hover {
  background: color-mix(in srgb, var(--color-accent) 15%, transparent);
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

/* the parent path line in the node form */
.uns-form-path { margin: 0; font-size: var(--font-sm); color: var(--color-muted); }
.uns-form-path code {
  font-family: ui-monospace, "Cascadia Code", Menlo, Consolas, monospace;
  color: var(--color-fg);
}

/* rename warning under the name field */
.field-hint { font-size: var(--font-sm); color: var(--color-warn); }

/* fold: native details/summary per branch (menu-group idiom). The summary IS
   the .uns-row, so hover/drop-tint/actions work unchanged on folded branches. */
.uns-fold > summary { list-style: none; cursor: pointer; }
.uns-fold > summary::-webkit-details-marker { display: none; }
.uns-caret::before {
  content: "\25B8";               /* triangle marker, menu-group idiom */
  display: inline-block;
  font-size: 0.8em;
  color: var(--color-muted);
  transition: transform 0.15s ease;
}
.uns-fold[open] > .uns-caret::before,
.uns-fold[open] > summary .uns-caret::before { transform: rotate(90deg); }
/* folded branches carry no folder glyph double-marking: caret replaces it */
.uns-fold > summary .uns-name::before { content: "\1F4C1 "; }