/* GENERATED by scripts/design-css.mjs from HomeGit/Docs/website-design-system.html: do not edit here.
 * The whole <style> of the design document without its Bootstrap part; the punctuation HomeGit/CLAUDE.md
 * forbids is made plain. The app's own rules are in styles.css, which loads after this file. */

/* ═══════════════════════════════════════════════
   DESIGN TOKENS
═══════════════════════════════════════════════ */

:root {
  --radius-sm:   8px;
  --radius-md:   14px;
  /* --radius-lg is de hoek van ÉLK BUITENSTE kader: kaart, tabelkader, veld,
     widget. Eén waarde, want die kaders staan in beeld naast en om elkaar heen
     en twee radii lezen als een fout. Gelijkgetrokken met AssetApp op
     31-08-2026; hier stond .card nog op --radius-xl (28px) terwijl het
     tabelkader ernaast al op 20px stond. Geneste boxen leiden hun hoek hiervan
     af, zie de sectie Hoeken. */
  --radius-lg:   20px;
  --radius-xl:   28px;   /* ongebruikt sinds 31-08-2026: buitenste kaders staan op --radius-lg */
  --radius-full: 999px;

  --font-base: 'Inter', -apple-system, sans-serif;
  /* Eén bron voor de monospace-stack. Stond tot 31-08-2026 hardgecodeerd in .t-mono en .code,
     terwijl AssetApp en de twee Umbrel-pagina's er alle drie al een token voor hadden. */
  --font-mono: 'SF Mono', 'Fira Code', 'Cascadia Mono', Consolas, monospace;

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  /* ── Maten van de tabel (zie TABLES) ──
     Gelijk aan AssetApp. De rijhoogte is een regelbudget en geen pixelmaat: een rij
     is even hoog als N regels tekst plus de celpadding, en een tekstkolom wordt op
     datzelfde aantal regels afgekapt. Zo is elke rij exact even hoog. */
  --table-vh-offset: 9.5rem;   /* ruimte boven+onder de tabel: main-padding + paginakop + voetnoot */
  --table-row-lines: 2;
  --table-line-h: 1.35em;      /* één regel inclusief regelafstand; `em` rekent in de tabel-fontgrootte */
  --table-cell-py: 0.85rem;    /* celpadding boven en onder; zit in de rijhoogte-berekening, dus één bron */
  --table-actions-w: 6.8rem;   /* vaste breedte van de actiekolom: twee icoon-knoppen plus de celpadding */
  --table-exp-w: 2.2rem;       /* vaste breedte van de uitklap-indicator: één chevron plus wat lucht */
  --table-exp-pad: 0.55rem;    /* x-positie van élke chevron: de rij-indicator én de groepskop erboven */

  /* ── Maten van de zijbalk en zijn belijning ──
     Drie dingen moeten op exact dezelfde x landen: de zijbalk zelf, de scheidingslijn
     (.main::before) en de accentstreep van het actieve menu-item. Daarom zijn het variabelen
     en geen losse getallen op drie plekken. */
  --sidebar-w:       240px;
  --sidebar-pad:     1.5rem;   /* horizontale padding = afstand van nav-item tot de scheidingslijn */
  --nav-line-cap:    10px;     /* lengte van het haakse eindstukje van de scheidingslijn */
  /* Boven- en onder-boogje even ver van de vensterrand, gelijk met de bovenkant van het logo
     en van het eerste kader of de paginatitel (gebruiker, 20-10-2026; zo staat het in AssetApp
     sinds 03-10-2026). Hier stond 2.82rem, de middellijn van de paginatitel. De zijbalk en
     .main nemen hun bovenpadding uit deze variabele, dus ze kunnen niet uit elkaar lopen. */
  --nav-line-top:    1.7rem;
  --nav-line-bottom: 1.7rem;   /* onder-boogje op de onderpadding van .main */

  /* ── De tweede kolom en de kantlijn waar de legenda op hangt ──
     Toegevoegd op 04-09-2026 met de pagina-kolom. Beide staan hier en niet bij
     de componenten omdat er BUITEN die kolom naar gekeken wordt: het
     legenda-icoon en de kantlijn zijn kinderen van de shell, niet van de kolom.
     --hint-line-x is standaard de menulijn; een pagina met een eigen kolom
     schuift hem op via de `:has()`-regel bij .page-layout.
     DE KOLOM IS 1,65 KEER DE MENUBREEDTE (11-09-2026, wens van de gebruiker, en
     dezelfde dag twee keer bijgesteld: eerst twee keer de menubreedte, en daarna
     terug omdat dat op een pagina met kaarten ernaast te veel was). Hier stond de
     menubreedte zelf met een modifier voor de brede stand; die modifier is
     opgeheven, want elke pagina die de kolom kreeg koos de brede. De factor staat
     op precies deze plek, dus 1,6 of 1,7 is één teken.
     EEN PAGINA MAG ERVAN AFWIJKEN, met een modifier op .page-layout die de
     variabele op `html` verzet (zie de `:has()`-regels hieronder). Er is er één,
     en dat is er precies één omdat hij gebruikt wordt: een pagina waar alleen een
     keuzelijst in de kolom staat heeft aan de menubreedte genoeg, en wat de kolom
     niet nodig heeft gaat naar de inhoud. Een tweede stand erbij zetten "voor het
     geval dat" is dode CSS.
     DE ZIJPADDING VAN DE PAGINA IS DEZELFDE MAAT ALS DIE VAN HET MENU, en dat is
     de andere helft van diezelfde ronde: de pagina-kolom haalde de zijpadding met
     een negatieve marge weg, dus zijn inhoud begon op --sidebar-pad van de
     scheidingslijn en de inhoud van elke andere pagina twee keer zo ver. Eén maat
     voor beide, en alles begint op dezelfde x. */
  --page-aside-w:    calc(var(--sidebar-w) * 1.65);
  --main-pad-x:      var(--sidebar-pad);
  --hint-line-x:     var(--sidebar-w);
}

/* ── HET PALET ──────────────────────────────────────────────────────────────
   Negen accenten plus een neutrale startstaat. De eerste vijf zijn gelijkgetrokken met AssetApp op
   31-08-2026. Hier stonden oranje en groen; die twee bestonden nergens meer zo.
   AssetApp draait op oranje, lila, blauw, teal en magenta (ENUMS.ACCENT, standaard
   blauw); de Umbrel-pagina's gebruiken er één, oranje, en dat blijft een geldige
   keuze: een app hoeft niet elk accent aan te bieden, wel dezelfde waarden.

   Op 29-09-2026 kwamen er vier bij: indigo en cyaan als kleur, leisteen en zand
   als gedempte variant naast neutraal. Die vier staan (nog) niet in AssetApp.
   Cyaan ligt dicht bij teal (ongeveer 15 graden tint) en indigo tussen blauw en
   lila; dat is een bewuste keuze voor meer keus, geen vergissing.

   TWEE DINGEN DIE HIER VERANDERD ZIJN EN DIE JE MOET WETEN:

   1. Groen is geen accent meer. Het botste met de betekenis van --pos: in een
      vermogensapp is groen "het staat in de plus" en niet "dit is de merkkleur".
      Wie een groene app wil, zet --accent op de groentint en laat --pos met rust.

   2. --pos en --neg volgen het accent NIET. In het oude oranje-blok stond
      --pos op #FF8124, dus winst kleurde oranje mee. Dat leest als merkkleur en
      niet als richting, en bij vijf accenten wordt het onhoudbaar: winst zou dan
      per instelling van kleur wisselen. Groen en rood zijn nu vast, in alle tien
      de combinaties.

   De neutrale staat is geen kleurkeuze maar een startwaarde: tot de opgeslagen
   accent-instelling is toegepast staat het accent op de menu-grijstint, zodat er
   geen accent-flits over het scherm gaat bij het laden. */

/* ── NEUTRAAL (start- en terugvalstaat) ── */
[data-theme="dark"][data-accent="neutral"] {
  --bg:#080808; --bg-elevated:#181818; --bg-raised:#242424; --bg-overlay:#303030;
  --border:rgba(255,255,255,0.18); --border-mid:rgba(255,255,255,0.28);
  --text-primary:#f2f2f2; --text-sec:#a3a3a3; --text-ter:#6a6a6a;
  --accent:#a3a3a3; --accent-soft:rgba(255,255,255,0.08); --accent-mid:rgba(255,255,255,0.14);
  --accent-glow:rgba(255,255,255,0.10); --accent-text:#a3a3a3;
  --pos:#39FF8A; --pos-bg:rgba(57,255,138,0.16); --neg:#ff5a5a; --neg-bg:rgba(255,69,69,0.18);
  --neutral-bg:rgba(255,255,255,0.08); --input-bg:#1c1c1c; --scrollbar:#2c2c2c;
}
[data-theme="light"][data-accent="neutral"] {
  --bg:#fff; --bg-elevated:#f7f7f7; --bg-raised:#f0f0f0; --bg-overlay:#e8e8e8;
  --border:rgba(0,0,0,0.12); --border-mid:rgba(0,0,0,0.18);
  --text-primary:#0a0a0a; --text-sec:#888; --text-ter:#bbb;
  --accent:#888; --accent-soft:rgba(0,0,0,0.05); --accent-mid:rgba(0,0,0,0.10);
  --accent-glow:rgba(0,0,0,0.08); --accent-text:#888;
  --pos:#007a48; --pos-bg:rgba(0,149,90,0.08); --neg:#cc2222; --neg-bg:rgba(204,34,34,0.08);
  --neutral-bg:rgba(0,0,0,0.04); --input-bg:#f5f5f5; --scrollbar:#ddd;
}

/* ── ORANJE ── */
[data-theme="dark"][data-accent="orange"] {
  --bg:#080808; --bg-elevated:#181818; --bg-raised:#242424; --bg-overlay:#303030;
  --border:rgba(255,255,255,0.18); --border-mid:rgba(255,255,255,0.28);
  --text-primary:#f2f2f2; --text-sec:#a3a3a3; --text-ter:#6a6a6a;
  --accent:#FF6B00; --accent-soft:rgba(255,107,0,0.16); --accent-mid:rgba(255,107,0,0.26);
  --accent-glow:rgba(255,107,0,0.30); --accent-text:#FF8124;
  --pos:#39FF8A; --pos-bg:rgba(57,255,138,0.16); --neg:#ff5a5a; --neg-bg:rgba(255,69,69,0.18);
  --neutral-bg:rgba(255,255,255,0.08); --input-bg:#1c1c1c; --scrollbar:#2c2c2c;
}
[data-theme="light"][data-accent="orange"] {
  --bg:#fff; --bg-elevated:#f7f7f7; --bg-raised:#f0f0f0; --bg-overlay:#e8e8e8;
  --border:rgba(0,0,0,0.12); --border-mid:rgba(0,0,0,0.18);
  --text-primary:#0a0a0a; --text-sec:#888; --text-ter:#bbb;
  --accent:#E05500; --accent-soft:rgba(224,85,0,0.08); --accent-mid:rgba(224,85,0,0.16);
  --accent-glow:rgba(224,85,0,0.22); --accent-text:#E05500;
  --pos:#007a48; --pos-bg:rgba(0,149,90,0.08); --neg:#cc2222; --neg-bg:rgba(204,34,34,0.08);
  --neutral-bg:rgba(0,0,0,0.04); --input-bg:#f5f5f5; --scrollbar:#ddd;
}

/* ── BLAUW (standaard in AssetApp) ── */
[data-theme="dark"][data-accent="blue"] {
  --bg:#080808; --bg-elevated:#181818; --bg-raised:#242424; --bg-overlay:#303030;
  --border:rgba(255,255,255,0.18); --border-mid:rgba(255,255,255,0.28);
  --text-primary:#f2f2f2; --text-sec:#a3a3a3; --text-ter:#6a6a6a;
  --accent:#3B82F6; --accent-soft:rgba(59,130,246,0.16); --accent-mid:rgba(59,130,246,0.26);
  --accent-glow:rgba(59,130,246,0.30); --accent-text:#6AA1FF;
  --pos:#39FF8A; --pos-bg:rgba(57,255,138,0.16); --neg:#ff5a5a; --neg-bg:rgba(255,69,69,0.18);
  --neutral-bg:rgba(255,255,255,0.08); --input-bg:#1c1c1c; --scrollbar:#2c2c2c;
}
[data-theme="light"][data-accent="blue"] {
  --bg:#fff; --bg-elevated:#f7f7f7; --bg-raised:#f0f0f0; --bg-overlay:#e8e8e8;
  --border:rgba(0,0,0,0.12); --border-mid:rgba(0,0,0,0.18);
  --text-primary:#0a0a0a; --text-sec:#888; --text-ter:#bbb;
  --accent:#2563EB; --accent-soft:rgba(37,99,235,0.08); --accent-mid:rgba(37,99,235,0.16);
  --accent-glow:rgba(37,99,235,0.22); --accent-text:#2563EB;
  --pos:#007a48; --pos-bg:rgba(0,149,90,0.08); --neg:#cc2222; --neg-bg:rgba(204,34,34,0.08);
  --neutral-bg:rgba(0,0,0,0.04); --input-bg:#f5f5f5; --scrollbar:#ddd;
}

/* ── LILA ── */
[data-theme="dark"][data-accent="lila"] {
  --bg:#080808; --bg-elevated:#181818; --bg-raised:#242424; --bg-overlay:#303030;
  --border:rgba(255,255,255,0.18); --border-mid:rgba(255,255,255,0.28);
  --text-primary:#f2f2f2; --text-sec:#a3a3a3; --text-ter:#6a6a6a;
  --accent:#B388FF; --accent-soft:rgba(179,136,255,0.16); --accent-mid:rgba(179,136,255,0.26);
  --accent-glow:rgba(179,136,255,0.30); --accent-text:#C7A6FF;
  --pos:#39FF8A; --pos-bg:rgba(57,255,138,0.16); --neg:#ff5a5a; --neg-bg:rgba(255,69,69,0.18);
  --neutral-bg:rgba(255,255,255,0.08); --input-bg:#1c1c1c; --scrollbar:#2c2c2c;
}
[data-theme="light"][data-accent="lila"] {
  --bg:#fff; --bg-elevated:#f7f7f7; --bg-raised:#f0f0f0; --bg-overlay:#e8e8e8;
  --border:rgba(0,0,0,0.12); --border-mid:rgba(0,0,0,0.18);
  --text-primary:#0a0a0a; --text-sec:#888; --text-ter:#bbb;
  --accent:#7C3AED; --accent-soft:rgba(124,58,237,0.08); --accent-mid:rgba(124,58,237,0.16);
  --accent-glow:rgba(124,58,237,0.22); --accent-text:#7C3AED;
  --pos:#007a48; --pos-bg:rgba(0,149,90,0.08); --neg:#cc2222; --neg-bg:rgba(204,34,34,0.08);
  --neutral-bg:rgba(0,0,0,0.04); --input-bg:#f5f5f5; --scrollbar:#ddd;
}

/* ── TEAL ── */
[data-theme="dark"][data-accent="teal"] {
  --bg:#080808; --bg-elevated:#181818; --bg-raised:#242424; --bg-overlay:#303030;
  --border:rgba(255,255,255,0.18); --border-mid:rgba(255,255,255,0.28);
  --text-primary:#f2f2f2; --text-sec:#a3a3a3; --text-ter:#6a6a6a;
  --accent:#14B8A6; --accent-soft:rgba(20,184,166,0.16); --accent-mid:rgba(20,184,166,0.26);
  --accent-glow:rgba(20,184,166,0.30); --accent-text:#2DD4BF;
  --pos:#39FF8A; --pos-bg:rgba(57,255,138,0.16); --neg:#ff5a5a; --neg-bg:rgba(255,69,69,0.18);
  --neutral-bg:rgba(255,255,255,0.08); --input-bg:#1c1c1c; --scrollbar:#2c2c2c;
}
[data-theme="light"][data-accent="teal"] {
  --bg:#fff; --bg-elevated:#f7f7f7; --bg-raised:#f0f0f0; --bg-overlay:#e8e8e8;
  --border:rgba(0,0,0,0.12); --border-mid:rgba(0,0,0,0.18);
  --text-primary:#0a0a0a; --text-sec:#888; --text-ter:#bbb;
  --accent:#0D9488; --accent-soft:rgba(13,148,136,0.08); --accent-mid:rgba(13,148,136,0.16);
  --accent-glow:rgba(13,148,136,0.22); --accent-text:#0D9488;
  --pos:#007a48; --pos-bg:rgba(0,149,90,0.08); --neg:#cc2222; --neg-bg:rgba(204,34,34,0.08);
  --neutral-bg:rgba(0,0,0,0.04); --input-bg:#f5f5f5; --scrollbar:#ddd;
}

/* ── MAGENTA ── */
[data-theme="dark"][data-accent="magenta"] {
  --bg:#080808; --bg-elevated:#181818; --bg-raised:#242424; --bg-overlay:#303030;
  --border:rgba(255,255,255,0.18); --border-mid:rgba(255,255,255,0.28);
  --text-primary:#f2f2f2; --text-sec:#a3a3a3; --text-ter:#6a6a6a;
  --accent:#E24DAE; --accent-soft:rgba(226,77,174,0.16); --accent-mid:rgba(226,77,174,0.26);
  --accent-glow:rgba(226,77,174,0.30); --accent-text:#F072C4;
  --pos:#39FF8A; --pos-bg:rgba(57,255,138,0.16); --neg:#ff5a5a; --neg-bg:rgba(255,69,69,0.18);
  --neutral-bg:rgba(255,255,255,0.08); --input-bg:#1c1c1c; --scrollbar:#2c2c2c;
}
[data-theme="light"][data-accent="magenta"] {
  --bg:#fff; --bg-elevated:#f7f7f7; --bg-raised:#f0f0f0; --bg-overlay:#e8e8e8;
  --border:rgba(0,0,0,0.12); --border-mid:rgba(0,0,0,0.18);
  --text-primary:#0a0a0a; --text-sec:#888; --text-ter:#bbb;
  --accent:#BE2A86; --accent-soft:rgba(190,42,134,0.08); --accent-mid:rgba(190,42,134,0.16);
  --accent-glow:rgba(190,42,134,0.22); --accent-text:#BE2A86;
  --pos:#007a48; --pos-bg:rgba(0,149,90,0.08); --neg:#cc2222; --neg-bg:rgba(204,34,34,0.08);
  --neutral-bg:rgba(0,0,0,0.04); --input-bg:#f5f5f5; --scrollbar:#ddd;
}

/* ── INDIGO ── */
[data-theme="dark"][data-accent="indigo"] {
  --bg:#080808; --bg-elevated:#181818; --bg-raised:#242424; --bg-overlay:#303030;
  --border:rgba(255,255,255,0.18); --border-mid:rgba(255,255,255,0.28);
  --text-primary:#f2f2f2; --text-sec:#a3a3a3; --text-ter:#6a6a6a;
  --accent:#818CF8; --accent-soft:rgba(129,140,248,0.16); --accent-mid:rgba(129,140,248,0.26);
  --accent-glow:rgba(129,140,248,0.30); --accent-text:#A5B4FC;
  --pos:#39FF8A; --pos-bg:rgba(57,255,138,0.16); --neg:#ff5a5a; --neg-bg:rgba(255,69,69,0.18);
  --neutral-bg:rgba(255,255,255,0.08); --input-bg:#1c1c1c; --scrollbar:#2c2c2c;
}
[data-theme="light"][data-accent="indigo"] {
  --bg:#fff; --bg-elevated:#f7f7f7; --bg-raised:#f0f0f0; --bg-overlay:#e8e8e8;
  --border:rgba(0,0,0,0.12); --border-mid:rgba(0,0,0,0.18);
  --text-primary:#0a0a0a; --text-sec:#888; --text-ter:#bbb;
  --accent:#4F46E5; --accent-soft:rgba(79,70,229,0.08); --accent-mid:rgba(79,70,229,0.16);
  --accent-glow:rgba(79,70,229,0.22); --accent-text:#4F46E5;
  --pos:#007a48; --pos-bg:rgba(0,149,90,0.08); --neg:#cc2222; --neg-bg:rgba(204,34,34,0.08);
  --neutral-bg:rgba(0,0,0,0.04); --input-bg:#f5f5f5; --scrollbar:#ddd;
}

/* ── CYAAN ──
   In licht #0E7490 en geen lichtere tint: #0891B2 en #0284C7 halen als tekst op
   wit de 4,5:1 van AA niet. */
[data-theme="dark"][data-accent="cyan"] {
  --bg:#080808; --bg-elevated:#181818; --bg-raised:#242424; --bg-overlay:#303030;
  --border:rgba(255,255,255,0.18); --border-mid:rgba(255,255,255,0.28);
  --text-primary:#f2f2f2; --text-sec:#a3a3a3; --text-ter:#6a6a6a;
  --accent:#22D3EE; --accent-soft:rgba(34,211,238,0.16); --accent-mid:rgba(34,211,238,0.26);
  --accent-glow:rgba(34,211,238,0.30); --accent-text:#67E8F9;
  --pos:#39FF8A; --pos-bg:rgba(57,255,138,0.16); --neg:#ff5a5a; --neg-bg:rgba(255,69,69,0.18);
  --neutral-bg:rgba(255,255,255,0.08); --input-bg:#1c1c1c; --scrollbar:#2c2c2c;
}
[data-theme="light"][data-accent="cyan"] {
  --bg:#fff; --bg-elevated:#f7f7f7; --bg-raised:#f0f0f0; --bg-overlay:#e8e8e8;
  --border:rgba(0,0,0,0.12); --border-mid:rgba(0,0,0,0.18);
  --text-primary:#0a0a0a; --text-sec:#888; --text-ter:#bbb;
  --accent:#0E7490; --accent-soft:rgba(14,116,144,0.08); --accent-mid:rgba(14,116,144,0.16);
  --accent-glow:rgba(14,116,144,0.22); --accent-text:#0E7490;
  --pos:#007a48; --pos-bg:rgba(0,149,90,0.08); --neg:#cc2222; --neg-bg:rgba(204,34,34,0.08);
  --neutral-bg:rgba(0,0,0,0.04); --input-bg:#f5f5f5; --scrollbar:#ddd;
}

/* ── LEISTEEN (gedempt blauwgrijs) ── */
[data-theme="dark"][data-accent="slate"] {
  --bg:#080808; --bg-elevated:#181818; --bg-raised:#242424; --bg-overlay:#303030;
  --border:rgba(255,255,255,0.18); --border-mid:rgba(255,255,255,0.28);
  --text-primary:#f2f2f2; --text-sec:#a3a3a3; --text-ter:#6a6a6a;
  --accent:#94A3B8; --accent-soft:rgba(148,163,184,0.16); --accent-mid:rgba(148,163,184,0.26);
  --accent-glow:rgba(148,163,184,0.30); --accent-text:#B4C0D0;
  --pos:#39FF8A; --pos-bg:rgba(57,255,138,0.16); --neg:#ff5a5a; --neg-bg:rgba(255,69,69,0.18);
  --neutral-bg:rgba(255,255,255,0.08); --input-bg:#1c1c1c; --scrollbar:#2c2c2c;
}
[data-theme="light"][data-accent="slate"] {
  --bg:#fff; --bg-elevated:#f7f7f7; --bg-raised:#f0f0f0; --bg-overlay:#e8e8e8;
  --border:rgba(0,0,0,0.12); --border-mid:rgba(0,0,0,0.18);
  --text-primary:#0a0a0a; --text-sec:#888; --text-ter:#bbb;
  --accent:#475569; --accent-soft:rgba(71,85,105,0.08); --accent-mid:rgba(71,85,105,0.16);
  --accent-glow:rgba(71,85,105,0.22); --accent-text:#475569;
  --pos:#007a48; --pos-bg:rgba(0,149,90,0.08); --neg:#cc2222; --neg-bg:rgba(204,34,34,0.08);
  --neutral-bg:rgba(0,0,0,0.04); --input-bg:#f5f5f5; --scrollbar:#ddd;
}

/* ── ZAND (gedempt warm) ──
   In licht #7A6650: een lichtere zandtint haalt als tekst op wit AA niet. */
[data-theme="dark"][data-accent="sand"] {
  --bg:#080808; --bg-elevated:#181818; --bg-raised:#242424; --bg-overlay:#303030;
  --border:rgba(255,255,255,0.18); --border-mid:rgba(255,255,255,0.28);
  --text-primary:#f2f2f2; --text-sec:#a3a3a3; --text-ter:#6a6a6a;
  --accent:#C8B89E; --accent-soft:rgba(200,184,158,0.16); --accent-mid:rgba(200,184,158,0.26);
  --accent-glow:rgba(200,184,158,0.30); --accent-text:#DDD0BA;
  --pos:#39FF8A; --pos-bg:rgba(57,255,138,0.16); --neg:#ff5a5a; --neg-bg:rgba(255,69,69,0.18);
  --neutral-bg:rgba(255,255,255,0.08); --input-bg:#1c1c1c; --scrollbar:#2c2c2c;
}
[data-theme="light"][data-accent="sand"] {
  --bg:#fff; --bg-elevated:#f7f7f7; --bg-raised:#f0f0f0; --bg-overlay:#e8e8e8;
  --border:rgba(0,0,0,0.12); --border-mid:rgba(0,0,0,0.18);
  --text-primary:#0a0a0a; --text-sec:#888; --text-ter:#bbb;
  --accent:#7A6650; --accent-soft:rgba(122,102,80,0.08); --accent-mid:rgba(122,102,80,0.16);
  --accent-glow:rgba(122,102,80,0.22); --accent-text:#7A6650;
  --pos:#007a48; --pos-bg:rgba(0,149,90,0.08); --neg:#cc2222; --neg-bg:rgba(204,34,34,0.08);
  --neutral-bg:rgba(0,0,0,0.04); --input-bg:#f5f5f5; --scrollbar:#ddd;
}

/* ═══════════════════════════════════════════════
   DIEPTE: VLAK OF DIEP  (data-depth op <html>)
   Nieuw op 31-08-2026. Sinds 04-09-2026 is de gloed er als aparte schakelaar uit
   getild: data-glow="on" of "off", en alleen werkzaam bij data-depth="deep".
   Waarom, en waarom juist die combinatie, staat bij het gloed-blok hieronder.

   WAAROM DE INSTELLING "DIEPTE" HEET EN NIET "GLAS" OF "CONTRAST".
   "Glas" beschrijft één van de vier dingen die de stand omzet, en niet eens de
   belangrijkste: de gloed en de schaduw doen meer werk dan de doorzichtigheid.
   "Contrast" is erger dan onnauwkeurig, want dat woord heeft in toegankelijkheid
   een precieze betekenis (de verhouding tussen tekst en achtergrond) en juist
   díe verandert hier niet: --text-primary en --bg blijven staan waar ze staan.
   Een schuif die Contrast heet belooft dus iets wat hij niet doet, en zou boven-
   dien als hulpmiddel gelezen worden terwijl de terugval onderaan dit blok er
   juist is omdat het er géén is.

   Diepte dekt alle vier de lagen in één woord: de gloed erachter, de schaduw
   eronder, het randlicht erop en de doorzichtigheid ervan. De gloed heeft daarnaast
   zijn eigen schakelaar, want die is als enige over de hele pagina zichtbaar en
   gekleurd; de andere drie zitten op de vlakken zelf. Dat maakt de diepte geen
   twee instellingen: data-glow kan alleen binnen "diep" iets doen.

   Wat dit oplost: de app oogde vlak, en de oorzaak was niet dat er blur ontbrak
   maar dat er NIETS ACHTER DE VLAKKEN ZIT. --bg is een egale #080808 en elke
   kaart een egale #181818; een backdrop-filter op een egale ondergrond levert per
   definitie niets op. Daarom is de gloed het eerste deel en het glas het tweede.

   Het is bewust een TOKENLAAG en geen tweede set componentregels: hieronder in
   dit document kent geen enkele component de stand. Ze lezen --surface,
   --surface-blur en --edge, en die staan hier. Gevolg: de stand kan als
   voorkeur-schuif landen naast modus en accent, zonder dat er ergens een
   component gedupliceerd wordt.

   "off" is de oude toestand en blijft de terugval voor wie doorzichtigheid of
   beweging heeft uitgezet (zie de media-query onderaan dit blok). */

:root {
  --surface:       var(--bg-elevated);   /* vlak van een kaart, zijbalk, tabelkader */
  --surface-inset: var(--bg-raised);     /* vlak dat een niveau dieper ligt: chip, invoer (de tabelkop staat op --bg-elevated) */
  --surface-blur:  0px;
  --surface-sat:   100%;
  --edge:          none;                 /* randlicht bovenlangs, tekent het glas als glas */
  --ambient:       none;                 /* accentgloed achter de pagina */

  /* ── Schaduwschaal ──
     Drie hoogtes, en meer zijn er niet nodig. Toegevoegd op 31-08-2026 omdat er
     in de praktijk vier verschillende met de hand geschreven schaduwen bleken te
     staan: 0 12px 44px op vier popovers, 0 24px 60px op de dialoog, 0 18px 50px
     op een bedieningsbalk en het lichte-thema-paar op de kaart. Dat is precies
     het soort waarde dat een designsysteem hoort te bezitten. */
  --shadow-1: 0 1px 2px rgba(0,0,0,0.06), 0 4px 16px rgba(0,0,0,0.05);   /* kaart, tabelkader */
  --shadow-2: 0 12px 44px rgba(0,0,0,0.35);                              /* popover, keuzemenu */
  --shadow-3: 0 24px 60px rgba(0,0,0,0.40);                              /* dialoog */
}
/* In het donkere thema doet een zachte schaduw op #080808 vrijwel niets; daar
   doet de rand het werk en blijft alleen de dieptehint over. */
[data-theme="dark"] { --shadow-1: 0 1px 2px rgba(0,0,0,0.30); }
/* De schaduw van een kaart die je kiest, bij hover, aanwijzen en selectie
   (28-09-2026). Hier stond --shadow-2, de popover-schaduw van 35% zwart: in donker
   goed, op wit een grijze vlek rond de kaart. Een kaart die oplicht zweeft niet
   zoals een keuzemenu, dus in licht een eigen, veel zachtere waarde. Glas zet zijn
   eigen --hover-shadow en --select-shadow in het profielblok. */
:root { --lift-shadow: var(--shadow-2); --hover-shadow: var(--lift-shadow); }
/* Hover krijgt in licht iets meer dan aanwijzen en selectie (gebruiker, dezelfde
   dag): met de zachte waarde was het oplichten bij hover nauwelijks te zien. Bij
   aanwijzen en selectie doet de accentring dat werk al. */
[data-theme="light"] { --lift-shadow: 0 8px 22px -14px rgba(0,0,0,0.14); --hover-shadow: 0 10px 26px -12px rgba(0,0,0,0.20); }

[data-depth="deep"] { --surface-blur:16px; --surface-sat:135%; }

[data-depth="deep"][data-theme="dark"] {
  --surface:       color-mix(in srgb, #1c1c1c 78%, transparent);
  --surface-inset: color-mix(in srgb, #262626 76%, transparent);
  --edge: linear-gradient(180deg, rgba(255,255,255,0.10), rgba(255,255,255,0) 42%);
  --shadow-1: 0 1px 2px rgba(0,0,0,0.35), 0 10px 30px -18px rgba(0,0,0,0.9);
}
[data-depth="deep"][data-theme="light"] {
  --surface:       color-mix(in srgb, #ffffff 72%, transparent);
  --surface-inset: color-mix(in srgb, #f2f2f2 74%, transparent);
  --edge: linear-gradient(180deg, rgba(255,255,255,0.85), rgba(255,255,255,0) 46%);
}

/* ── De gloed ───────────────────────────────────────────────────────────────
   Twee lagen: één uit de linkerbovenhoek en een tegenlichtje rechtsboven, zodat
   het geheel niet als één vlek leest.

   DE BRON STAAT IN DE HOEK EN NIET OP DE SCHEIDINGSLIJN, en dat is een besluit
   van de gebruiker (31-08-2026). Ik had hem verplaatst naar x = --sidebar-w met
   het argument dat de lijn dan de lichtbron lijkt en de gloed helemaal op de
   inhoud valt. Dat klopte redeneerkundig, maar het zag er minder uit: met de
   bron in de hoek loopt het licht diagonaal over de hele pagina, en dat is het
   effect waar het om begonnen was. De lijn kruist de gloed nu weer, en dat is
   geen probleem meer sinds de zijbalk zelf geen glasvlak meer is (zie hieronder):
   er staat geen tweede rand meer naast de lijn die ermee kon botsen.

   position:fixed via body::before: de gloed hoort bij het venster, net als de
   100vh-sticky zijbalk en de scheidingslijn, en mag dus niet meescrollen. Daar
   en niet in de zijbalk, want alleen op body::before ligt hij écht achter alles;
   .sidebar is position:sticky en maakt een eigen stapelcontext, waarin zelfs een
   z-index:-1 nog vóór de eigen achtergrond van dat element belandt.

   DE GLOED IS EEN EIGEN SCHAKELAAR: data-glow op <html>, "on" of "off".
   Toegevoegd op 04-09-2026 op verzoek van de gebruiker, nadat de gloed in de twee
   Umbrel-apps uit moest terwijl de rest van de diepte daar bleef staan. Dat was
   niet te doen zolang hij aan data-depth vastzat, en het is ook een reële wens:
   dit is de enige dieptelaag die een eigen kleur over de hele pagina legt, en de
   enige waarvan iemand kan vinden dat het te veel is.

   DE SELECTOR EIST BEIDE ATTRIBUTEN, en dat is een eis van de gebruiker met een
   goede grond: de gloed is de ONDERSTE van de vier lagen. Hem aanzetten terwijl
   de pagina vlak staat, legt een verloop achter vlakken die er dekkend voor
   staan; je ziet dan alleen de randen van de pagina oplichten. Dat is geen stand
   die iemand bedoelt, dus die combinatie bestaat niet. Vlak blijft vlak.

   Let op de specificiteit: twee attributen is 0,2,0 en de terugval-query
   hieronder haalt dat niet met [data-depth="deep"]. Dat gaat hier goed omdat die
   query alleen --surface en de blur omzet, en die staan in dit document nog bij
   de diepte. Verhuist er ooit een van beide hierheen, dan moet die selector mee. */
[data-depth="deep"][data-glow="on"] {
  --ambient: radial-gradient(58rem 34rem at 8% -8%, var(--accent-glow), transparent 62%),
             radial-gradient(46rem 30rem at 102% 4%, color-mix(in srgb, var(--accent) 14%, transparent), transparent 66%);
}

/* ── Terugval, en de fout die hier eerst stond ──────────────────────────────
   Deze twee systeemwensen stonden in ÉÉN media-query, die samen de hele stand op
   vlak zette. Dat is fout, en niet theoretisch: Windows heeft een schakelaar
   "Animatie-effecten" die bij veel mensen uit staat, en die zet
   prefers-reduced-motion op reduce. Op zo'n machine deed de schuif Diepte dus
   helemaal niets, in geen van beide standen, zonder dat er iets te zien was dat
   dat verklaarde. Gemeld door de gebruiker op 31-08-2026 en daarna op deze
   machine bevestigd.

   De twee wensen zeggen ook echt iets anders:

   - reduced-TRANSPARENCY betekent "minder doorschijnende vlakken". Dan gaat de
     doorzichtigheid en de blur eruit, en verder niets: de gloed, de schaduw en
     het randlicht zijn geen transparantie-effecten en blijven staan. De vlakken
     worden weer ondoorzichtig, de pagina blijft diep.
   - reduced-MOTION betekent "niet bewegen". Dan gaat de OVERGANG eruit, en de
     stand blijft precies zoals hij is. Een gloed die er gewoon staat, beweegt
     niet; alleen het overvloeien bij het wisselen van accent deed dat, en dat is
     het enige wat hier weg hoort.

   Les die breder geldt dan dit blok: een media-query die twee wensen samenvoegt,
   voegt ook hun gevolgen samen, en dan krijgt de ene wens de gevolgen van de
   andere. */
@media (prefers-reduced-transparency: reduce) {
  [data-depth="deep"] {
    --surface: var(--bg-elevated);
    --surface-inset: var(--bg-raised);
    --surface-blur: 0px;
    --surface-sat: 100%;
  }
}
@media (prefers-reduced-motion: reduce) {
  body::before, .card, .table-frame { transition: none; }
}

/* ═══════════════════════════════════════════════
   PROFIEL: HUIDIG OF GLAS  (data-profile op <html>)
   Nieuw op 27-09-2026 (plan Vormgeving in AssetApp). Zonder attribuut, of met
   data-profile="business", is alles hierboven en hieronder het ontwerp zoals het
   was: dat is profiel 1 en het blijft ongewijzigd. data-profile="glass" legt er
   een tokenlaag overheen. Net als bij Diepte kent geen component het profiel;
   ze lezen tokens, en alleen waar een token niet genoeg is staat hieronder een
   regel met [data-profile="glass"] ervoor.

   WAT GLAS NIET AANRAAKT: de zijbalk met zijn scheidingslijn en afgeronde
   eindstukken, de menu-items en hun hover, de hoeken, de maten, de typografie,
   de sparklines en het palet. Er komt geen kleur bij; elke bol is het accent.

   DIEPTE BLIJFT, MAAR ALLEEN IN HUIDIG. De standen heten Vlak en Reliëf: diep is
   een hoogte-effect (schaduw eronder, randlicht erop, gloed erachter) en geen
   glas. De opgeslagen waarden blijven flat en deep. In Glas winnen de
   glas-tokens hieronder, omdat ze drie attributen eisen (0,3,0) tegen twee.

   DE VIER REGELS VAN GLAS:
   1. Een vlak dat óp glas ligt is een TINT en geen tweede vlak. Twee
      halfdoorzichtige vlakken op elkaar zijn samen bijna dekkend, en dan staat
      er een dichte tabelkop in een open kader.
   2. Wat boven de inhoud zweeft is matglas; wat je moet kunnen vinden (de
      primaire knop) blijft dicht.
   3. Selectie en hover zijn licht en geen verf. Een waas van accent over een
      hele kaart maakt van glas gekleurd plastic.
   4. Sterker glas zit in Matheid en niet in Blur. Achter een kaart ligt alleen de
      gloed, en een zacht verloop vervagen levert vrijwel hetzelfde verloop op.
═══════════════════════════════════════════════ */

/* Geregistreerd, zodat de rand zacht overgaat bij hover en selectie. Een
   ongeregistreerde custom property springt, want de browser weet niet dat het
   een kleur is. */
@property --rim-top    { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --rim-bottom { syntax: '<color>'; inherits: true; initial-value: transparent; }
@property --hairline   { syntax: '<color>'; inherits: true; initial-value: transparent; }

:root {
  --frost:        transparent;               /* melkwitte laag in het glas (Matheid) */
  --surface-tint: var(--surface-inset);      /* ingebed vlak: tabelkop, pil, spoor */
  --row-line:     color-mix(in srgb, var(--border) 50%, transparent);
  --row-hover:    var(--neutral-bg);
  --lit:          none;                      /* lichtval van een selectie */
  --lift:         none;                      /* oplichten bij hover */
  /* De schuif, profiel Zakelijk: dicht en eenvoudig */
  --slider-track:      var(--bg-raised);
  --slider-track-edge: inset 0 0 0 1px var(--border);
  --slider-fill:       linear-gradient(var(--accent), var(--accent));
  --slider-thumb:      var(--text-primary);
  --slider-thumb-edge: 0 0 0 2px var(--bg), 0 1px 3px rgba(0,0,0,0.5);
}
[data-theme="light"] {
  --slider-thumb:      #ffffff;
  --slider-thumb-edge: 0 0 0 1px rgba(0,0,0,0.14), 0 1px 3px rgba(0,0,0,0.2);
}
/* Het spoor van een pil op een verhoogd vlak: een tint donkerder dan dat vlak
   (zie .pill-toggle). Op de pagina zelf blijft het --bg-raised. */
[data-theme="dark"] :is(.dialog, .card) { --pill-track: rgba(0,0,0,0.30); }
[data-theme="light"] :is(.dialog, .card) { --pill-track: rgba(0,0,0,0.06); }

:root[data-profile="glass"][data-theme="dark"] {
  --surface:       color-mix(in srgb, #161616 calc(100% - var(--glass-transparency, 50%)), transparent);
  --surface-inset: color-mix(in srgb, #262626 calc(100% - var(--glass-transparency, 50%)), transparent);
  --surface-tint:  rgba(255,255,255,0.035);
  --surface-blur:  36px;
  /* De schuif Dialoog doorzichtigheid (--dialog-transparency, een kaal getal
     0..100, 23-10-2026): 0 is dicht en 100 helemaal doorzichtig, net als de
     doorzichtigheid van kaarten. De blur zakt mee (0,4px per stap dichtheid), dus
     100 is helder en scherp. Kaal, want blur() kent geen percentage. */
  --float-blur:    calc((100 - var(--dialog-transparency, 30)) * 0.4px);
  --surface-sat:   165%;
  --frost:         color-mix(in srgb, #ffffff var(--glass-frost, 5%), transparent);
  --edge:          linear-gradient(155deg, rgba(255,255,255,0.07), rgba(255,255,255,0.015) 55%, rgba(255,255,255,0));
  --shadow-1:      0 1px 2px rgba(0,0,0,0.4), 0 24px 60px -28px rgba(0,0,0,0.85);
  /* Glanslijn net binnen de rand: in donker niet nodig, de ring is daar zelf het licht.
     Een lege schaduw en geen none, want none mag niet in een lijst met schaduwen. */
  --glans:         inset 0 0 0 transparent;
  --glans-on:      inset 0 0 0 transparent;
  --hover-shadow:  0 18px 40px -22px rgba(0,0,0,0.7);                 /* hover op een kaart */
  --select-shadow: 0 14px 44px -18px var(--accent-glow);              /* geselecteerde kaart */
  --frame-border:  rgba(255,255,255,0.10);
  --frame-top:     rgba(255,255,255,0.12);
  --row-line:      rgba(255,255,255,0.07);
  --row-hover:     rgba(255,255,255,0.05);
  --row-sel:       rgba(255,255,255,0.09);   /* geselecteerde rij in de vorm "tint": lichter glas, geen accent */
  --row-sel-hover: rgba(255,255,255,0.13);
  --rim-top:       rgba(255,255,255,0.18);
  --rim-bottom:    rgba(255,255,255,0.05);
  --rim-hover:     rgba(255,255,255,0.55);
  --rim-hover-bottom: rgba(255,255,255,0.16);
  --hairline:      rgba(255,255,255,0.60);
  --hairline-hover: rgba(255,255,255,0.95);
  --hover-fill:    rgba(255,255,255,0.04);
  --chip-on:       rgba(255,255,255,0.12);                /* de actieve stand in een spoor */
  --chip-on-edge:  inset 0 1px 0 rgba(255,255,255,0.16);
  --float-bg:      color-mix(in srgb, var(--bg-raised) calc(100% - var(--dialog-transparency, 30) * 1%), transparent);   /* matglas: dialoog, popover, tooltip, balk */
  /* De schuif in Glas: een ingelaten spoor, een vulling die naar het accent
     oploopt, en een knop als een glazen kraal met een glimlicht linksboven */
  --slider-track:      rgba(255,255,255,0.07);
  --slider-track-edge: inset 0 1px 2px rgba(0,0,0,0.45), 0 1px 0 rgba(255,255,255,0.06);
  --slider-fill:       linear-gradient(90deg, color-mix(in srgb, var(--accent) 40%, transparent), var(--accent));
  --slider-thumb:      radial-gradient(circle at 35% 30%, #ffffff, rgba(255,255,255,0.8) 50%, rgba(255,255,255,0.55));
  --slider-thumb-edge: inset 0 0 0 1px rgba(255,255,255,0.7), 0 2px 10px rgba(0,0,0,0.55), 0 0 14px -2px var(--accent-glow);
}
:root[data-profile="glass"][data-theme="light"] {
  --surface:       color-mix(in srgb, #ffffff calc(100% - var(--glass-transparency, 50%)), transparent);
  --surface-inset: color-mix(in srgb, #f4f4f4 calc(100% - var(--glass-transparency, 50%)), transparent);
  --surface-tint:  rgba(0,0,0,0.025);
  --surface-blur:  36px;
  --float-blur:    calc((100 - var(--dialog-transparency, 30)) * 0.4px);
  --surface-sat:   160%;
  --frost:         color-mix(in srgb, #ffffff calc(var(--glass-frost, 5%) * 2), transparent);
  --edge:          linear-gradient(155deg, rgba(255,255,255,0.7), rgba(255,255,255,0.12) 60%);
  /* DE RAND IS IN LICHT DONKER EN DE GLANS ZIT ERONDER (28-09-2026, gemeld door
     de gebruiker). Hier stond de donkere-thema-gedachte gespiegeld: een ring die
     bovenin 95% wit was, met een witte haarlijn erin. Wit op een bijna wit vlak is
     onzichtbaar, dus de bovenkant van elk kader viel weg, en bij hover wiste de
     witte haarlijn het midden van de donkere hover-rand. Nu: de ring is zwart en
     bovenin het sterkst, zoals hij in donker bovenin het helderst is; de haarlijn
     is het donkerste stuk in het midden; en het wit is een glanslijn van 1px net
     BINNEN de rand (--glans), zoals het tabelkader al had.
     DE GLANS IS EEN EIGEN TOKEN EN ZIT NIET IN --shadow-1 (gebruiker,
     28-09-2026): in --shadow-1 liep het wit mee in elke toestand, en onder de
     blauwe rand van een selectie lag dan een witte streep. Bij aanwijzen en
     selecteren valt de glans weg (--glans-on), net als het randlicht (zie
     .card-pick), zodat de bovenrand dezelfde lijn is als in donker. Een tint van
     het accent als tweede lijn is geprobeerd en maakte de bovenrand juist anders.
     DE SCHADUWEN ZIJN IN LICHT VEEL LICHTER DAN IN DONKER. Op #080808 moet een
     schaduw hard werken om op te vallen; op wit valt dezelfde waarde op als een
     vlek. Dezelfde dag gemeld: de kaartschaduw was in licht veel te heftig. */
  --shadow-1:      0 1px 2px rgba(0,0,0,0.04), 0 10px 28px -20px rgba(0,0,0,0.10);
  --glans:         inset 0 1px 0 rgba(255,255,255,0.9);
  --glans-on:      inset 0 0 0 transparent;
  --hover-shadow:  0 10px 26px -12px rgba(0,0,0,0.20);   /* gelijk aan Zakelijk licht */
  --select-shadow: 0 8px 24px -14px color-mix(in srgb, var(--accent) 22%, transparent);
  --frame-border:  rgba(0,0,0,0.12);
  --frame-top:     rgba(255,255,255,0.9);
  --row-line:      rgba(0,0,0,0.06);
  --row-hover:     rgba(0,0,0,0.035);
  --row-sel:       rgba(255,255,255,0.6);
  --row-sel-hover: rgba(255,255,255,0.75);
  --rim-top:       rgba(0,0,0,0.14);
  --rim-bottom:    rgba(0,0,0,0.07);
  --rim-hover:     rgba(0,0,0,0.28);
  --rim-hover-bottom: rgba(0,0,0,0.14);
  --hairline:      rgba(0,0,0,0.22);
  --hairline-hover: rgba(0,0,0,0.42);
  --hover-fill:    rgba(255,255,255,0.35);
  --chip-on:       rgba(255,255,255,0.75);
  --chip-on-edge:  inset 0 1px 0 #ffffff, 0 1px 2px rgba(0,0,0,0.08);
  --float-bg:      color-mix(in srgb, #ffffff calc(100% - var(--dialog-transparency, 30) * 1%), transparent);
  --slider-track:      rgba(0,0,0,0.06);
  --slider-track-edge: inset 0 1px 2px rgba(0,0,0,0.12), 0 1px 0 rgba(255,255,255,0.9);
  --slider-fill:       linear-gradient(90deg, color-mix(in srgb, var(--accent) 40%, transparent), var(--accent));
  --slider-thumb:      radial-gradient(circle at 35% 30%, #ffffff, #f5f5f5 55%, #e6e6e6);
  --slider-thumb-edge: inset 0 0 0 1px rgba(0,0,0,0.10), 0 2px 8px rgba(0,0,0,0.18), 0 0 14px -2px var(--accent-glow);
}
/* Minder doorzichtigheid: het glas wordt dicht, de rest blijft. Zelfde les als
   bij Diepte: deze wens zet niets anders uit dan doorzichtigheid. */
@media (prefers-reduced-transparency: reduce) {
  :root[data-profile="glass"][data-theme] {
    --surface: var(--bg-elevated); --surface-inset: var(--bg-raised); --surface-tint: var(--bg-raised);
    --surface-blur: 0px; --surface-sat: 100%; --frost: transparent;
  }
}

/* ── De gloed in Glas: drie bollen die af en toe verschuiven ──
   Vervangt body::before in dit profiel. Bol A en B staan in rust precies waar de
   twee gloedpunten van --ambient staan, in dezelfde sterkte: een element van
   2r x 2r met radial-gradient(closest-side) tekent dezelfde ellips als
   radial-gradient(58rem 34rem at 8% -8%). Bol C hangt in de rechteronderhoek.
   Zelfde regel als de gloed hierboven: alleen bij data-glow="on".
   De beweging is een transform, en die doet de compositor zonder te tekenen.
   Het dure deel is het glas: een backdrop-filter rekent opnieuw zodra wat
   erachter ligt verandert, dus tijdens een verplaatsing elk beeldje. Vandaar
   "af en toe" en niet doorlopend, en vandaar data-render="mimic" hieronder. */
.aurora { display: none; position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
:root[data-profile="glass"][data-glow="on"] .aurora { display: block; }
:root[data-profile="glass"] body::before { display: none; }
.blob {
  position: absolute; border-radius: 50%; translate: -50% -50%;
  will-change: transform;
  transition: transform var(--drift-dur, 24s) cubic-bezier(0.45, 0.05, 0.25, 1), opacity 2.5s;
}
/* ── Twee vormen: Vlek en Kern (28-09-2026, wens van de gebruiker) ──
   Een bol is standaard een VLEK: een zacht verloop zonder midden, de vorm van de
   gloed van Reliëf. Af en toe krijgt één bol een KERN: een rond, helder midden dat
   uitwaaiert, terwijl de vlek eromheen terugzakt. Bij de volgende verplaatsing wordt
   hij weer een vlek. Dat hoort bij de beweging en heeft geen eigen schakelaar: bij
   Beweging Uit, en bij minder beweging, is elke bol een vlek.
   HOOGUIT ÉÉN BOL TEGELIJK heeft een kern, en dat is de eis "niet allemaal
   tegelijk": drie kernen zijn drie lampen, en dan is het geen gloed meer.
   DE VORMEN ZIJN TWEE LAGEN EN GEEN WISSELEND VERLOOP. Een background-image laat
   zich niet overvloeien; de browser springt van het ene verloop naar het andere.
   Twee pseudo-elementen met elk hun opacity kan de compositor wél overvloeien.
   Een kern komt en gaat TIJDENS EEN VERPLAATSING en duurt even lang (--drift-dur).
   Een vormwissel achter glas kost net als een verplaatsing een herberekening per
   beeldje; tegelijk kost het dus één keer, en achter elkaar twee keer. */
.blob::before, .blob::after {
  content: ''; position: absolute; border-radius: 50%; pointer-events: none;
  transition: opacity var(--drift-dur, 24s) ease-in-out;
}
.blob::before { inset: 0; background: var(--vlek); }
.blob::after {
  left: 50%; top: 50%; width: var(--kern-size); height: var(--kern-size); translate: -50% -50%;
  background: radial-gradient(closest-side, color-mix(in srgb, var(--accent) var(--kern-hi), transparent), color-mix(in srgb, var(--accent) var(--kern-lo), transparent) 30%, transparent 70%);
  opacity: 0;
}
.blob.is-core::before { opacity: 0.35; }
.blob.is-core::after  { opacity: 1; }
.blob-a { left: 8%;   top: -8%;  width: 116rem; height: 68rem; --vlek: radial-gradient(closest-side, var(--accent-glow), transparent 62%); --kern-size: 74rem; --kern-hi: 40%; --kern-lo: 13%; }
.blob-b { left: 102%; top: 4%;   width: 92rem;  height: 60rem; --vlek: radial-gradient(closest-side, color-mix(in srgb, var(--accent) 14%, transparent), transparent 66%); --kern-size: 62rem; --kern-hi: 24%; --kern-lo: 8%; }
/* Bol C KOMT UIT de rechteronderhoek (gebruiker, 27-09-2026): in rust hangt hij in
   die hoek, en als hij beweegt loopt hij van daaruit het scherm op, naar links en
   naar boven. Hij stond eerst onder het midden. Groter, zodat het deel dat in
   beeld komt een vlek is en geen rand. */
.blob-c { left: 100%; top: 100%; width: 124rem; height: 84rem; --vlek: radial-gradient(closest-side, color-mix(in srgb, var(--accent) 14%, transparent), transparent 64%); --kern-size: 66rem; --kern-hi: 24%; --kern-lo: 8%; opacity: 0; }
/* Op wit is een helder midden snel een vlek verf: de kern is daar zwakker. */
[data-theme="light"] .blob-a { --kern-hi: 26%; --kern-lo: 9%; }
[data-theme="light"] :is(.blob-b, .blob-c) { --kern-hi: 16%; --kern-lo: 5%; }
[data-motion="step"] .blob-c, [data-motion="always"] .blob-c { opacity: 1; }
/* Altijd: de bollen schuiven doorlopend heen en weer, zoals op de voorbeeldsite.
   De looptijden verschillen (24, 30 en 36 seconden maal het tempo), zodat het
   patroon zelden samenvalt. Mooiste en duurste stand: het glas rekent dan elk
   beeldje opnieuw, zie data-render="mimic". */
[data-motion="always"] .blob { transition: opacity 2.5s; }
[data-motion="always"] .blob-a { animation: drift-a calc(24s * var(--drift-mult, 2)) ease-in-out infinite alternate; }
[data-motion="always"] .blob-b { animation: drift-b calc(30s * var(--drift-mult, 2)) ease-in-out infinite alternate; }
[data-motion="always"] .blob-c { animation: drift-c calc(36s * var(--drift-mult, 2)) ease-in-out infinite alternate; }
/* Een vlek verandert onderweg van vorm: draaien en ongelijk schalen maken van de
   ellips een andere ellips. Allemaal transform, dus nog steeds alleen de
   compositor. In rust (geen transform) is de vorm die van de gloed van Reliëf. */
@keyframes drift-a {
  50%  { transform: translate(7vmax, 3vmax) rotate(10deg) scale(1.15, 0.9); }
  100% { transform: translate(14vmax, 8vmax) rotate(-6deg) scale(0.95, 1.15); }
}
@keyframes drift-b {
  50%  { transform: translate(-6vmax, 8vmax) rotate(-12deg) scale(0.9, 1.15); }
  100% { transform: translate(-12vmax, 14vmax) rotate(8deg) scale(1.1, 0.95); }
}
@keyframes drift-c {
  50%  { transform: translate(-18vmax, -16vh) rotate(8deg) scale(1.15, 0.9); }
  100% { transform: translate(-34vmax, -32vh) rotate(-10deg) scale(0.95, 1.12); }
}
@media (prefers-reduced-motion: reduce) {
  html:not([data-force-motion]) .blob { transition: opacity 2.5s; animation: none; transform: none !important; }
  html:not([data-force-motion]) .blob.is-core::before { opacity: 1; }
  html:not([data-force-motion]) .blob.is-core::after { opacity: 0; }
}

/* ── Banen: de tweede vorm van de gloed (data-glowshape="bands", 23-10-2026) ──
   Lichtbanen in drie lagen die traag uit de rechterbovenhoek naar linksonder
   vallen, over de stille gloed heen: bol A en B blijven staan als in rust, C doet
   niet mee. Elke laag iets schuiner (10, 12 en 14 graden), als een waaier, en het
   masker op .bands maakt ze het sterkst in de hoek. Hoe dichterbij, hoe breder,
   langer, onscherper en zwakker; de voorste laag is meer een lichtval dan een
   baan. Bewust geen regen: geen dunne lijnen en geen heldere onderkant (die leest
   als een druppel), en traag.
   EEN LAAG IS DE ACCENTKLEUR DOOR EEN MASKER, en het masker is een SVG-tegel die
   het script één keer tekent, met de onscherpte erin. Er draait dus geen filter
   per beeldje; de laag schuift per cyclus precies één tegelhoogte op, alleen met
   transform. Het dure deel is net als bij de bollen het glas erboven.
   Beweging: Uit staat stil, Altijd valt doorlopend, Af en toe is een tijd licht
   en daarna een tijd niets (de laag dooft uit en staat dan stil). */
.bands {
  display: none; position: absolute; inset: 0; opacity: 0; transition: opacity 5s ease-in-out;
  -webkit-mask-image: radial-gradient(farthest-corner at 100% 0%, #000 15%, rgba(0,0,0,0.45) 55%, transparent 95%);
  mask-image: radial-gradient(farthest-corner at 100% 0%, #000 15%, rgba(0,0,0,0.45) 55%, transparent 95%);
}
:root[data-glowshape="bands"] .bands { display: block; }
.bands.is-shown { opacity: var(--band-alpha); }
:root[data-theme="dark"] { --band-ink: color-mix(in srgb, var(--accent) 60%, #ffffff); --band-alpha: 0.55; }
/* Op wit verdwijnt een opgelichte kleur: daar het accent zelf, iets donkerder en zwakker. */
:root[data-theme="light"] { --band-ink: color-mix(in srgb, var(--accent) 85%, #000000); --band-alpha: 0.32; }
.band {
  position: absolute; left: 50%; top: 50%;
  /* Ruim genoeg om het scherm tot 14 graden gedraaid te dekken, plus één tegelhoogte om in te schuiven. */
  width: calc(100vw + 30vh + 60px); height: calc(100vh + 30vw + var(--tile-h, 0px) + 60px);
  background: var(--band-ink);
  /* Een leeg masker tot de tegel er is: zonder masker is de laag een egaal vlak accent. */
  -webkit-mask-image: linear-gradient(transparent, transparent); mask-image: linear-gradient(transparent, transparent);
  -webkit-mask-repeat: repeat; mask-repeat: repeat;
  will-change: transform;
  animation: band-fall calc(var(--band-dur) * var(--drift-mult, 2) / 2) linear infinite paused;
}
.bands.is-falling .band { animation-play-state: running; }
@keyframes band-fall {
  from { transform: translate(-50%, -50%) rotate(var(--tilt)) translateY(calc(var(--tile-h) * -0.5)); }
  to   { transform: translate(-50%, -50%) rotate(var(--tilt)) translateY(calc(var(--tile-h) * 0.5)); }
}
:root[data-glowshape="bands"] .blob { animation: none; transform: none !important; }
:root[data-glowshape="bands"] .blob-c { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  html:not([data-force-motion]) .band { animation-play-state: paused !important; }
}

/* Nagebootst glas: geen backdrop-filter op vlakken die alleen op de gloed
   liggen (regel 4 hierboven). Doorzichtigheid, Matheid en de rand blijven; wat
   wegvalt is de extra verzadiging. Welke van de twee de standaard wordt, hangt
   af van een meting (plan Vormgeving, open punt 5). */
:root[data-profile="glass"][data-render="mimic"] :is(.card, .card-flat, .table-frame) {
  backdrop-filter: none; -webkit-backdrop-filter: none;
}

/* ── Vlakken in Glas ──
   --lift en --lit zijn extra lagen boven het vlak, met none als standaard: het
   oplichten bij hover en de lichtval van een selectie. */
:root[data-profile="glass"] :is(.card, .card-flat) {
  position: relative;
  background: var(--lift), var(--lit), linear-gradient(var(--frost), var(--frost)), var(--surface);
  border-color: transparent;
  box-shadow: var(--shadow-1), var(--glans);
  backdrop-filter: blur(var(--surface-blur)) saturate(var(--surface-sat));
  -webkit-backdrop-filter: blur(var(--surface-blur)) saturate(var(--surface-sat));
  transition: background-color 0.25s, box-shadow 0.25s, --rim-top 0.25s, --rim-bottom 0.25s, --hairline 0.25s;
}
/* De rand: een ring die van boven naar onder uitdooft, met de haarlijn IN de
   bovenrand. Een masker op een pseudo-element, want een verloop in de rand zelf
   zou onder het halfdoorzichtige vlak door schijnen. */
:root[data-profile="glass"] :is(.card, .card-flat)::after {
  content: ''; position: absolute; inset: -1px; border-radius: inherit; padding: 1px; pointer-events: none;
  background:
    linear-gradient(90deg, transparent 14%, var(--hairline) 50%, transparent 86%) top / 100% 1px no-repeat,
    linear-gradient(180deg, var(--rim-top), var(--rim-bottom));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
}
/* Het tabelkader krijgt de ring NIET, om dezelfde reden als zijn randlicht een
   achtergrondlaag is (zie TABLES): een pseudo-element wordt door de overflow
   geknipt. Daar doen een rand en een lichtlijn bovenin het werk. */
:root[data-profile="glass"] .table-frame {
  background: var(--edge), linear-gradient(var(--frost), var(--frost)), var(--surface);
  border-color: var(--frame-border);
  box-shadow: var(--shadow-1), inset 0 1px 0 var(--frame-top);
}
/* De kolomnamen dragen dezelfde tint als de controls-balk erboven: één kop, niet
   twee tinten. GEEN BLUR (23-10-2026, zo staat het in AssetApp sinds die dag): de
   kop gaat mee met de rest van de tabel en niet met de schuif Dialoog
   doorzichtigheid, want met blur las hij als een matte band. */
:root[data-profile="glass"] thead th { background: var(--surface-tint); }
/* Een th in de body is een label in een sleutel/waarde-tabel: op glas transparant. */
:root[data-profile="glass"] tbody th { background: transparent; }
:root[data-profile="glass"] thead tr:last-child > th { box-shadow: inset 0 -1px 0 var(--frame-border); }
:root[data-profile="glass"] .table-wrap { --head-fill: var(--surface-tint); --head-line: var(--frame-border); }
:root[data-profile="glass"] .table-controls-bar { background: var(--surface-tint); }
:root[data-profile="glass"] .tbl-count { background: var(--surface-tint); border-color: var(--frame-border); }
:root[data-profile="glass"] .btn-new { border-color: var(--frame-border); }
:root[data-profile="glass"] .btn-new:hover { border-color: var(--accent); }
:root[data-profile="glass"] tbody tr { border-bottom-color: var(--row-line); }
:root[data-profile="glass"] tbody tr:hover { background: var(--row-hover); }
:root[data-profile="glass"] :is(.row-expansion > td, .row-expansion:hover > td) { background: var(--surface-tint); }
/* Ingebedde vlakken worden tint (regel 1) */
:root[data-profile="glass"] :is(.pill-toggle, .tabs) { background: var(--surface-tint); box-shadow: inset 0 0 0 1px var(--row-line); }
:root[data-profile="glass"] .btn-secondary { background: var(--surface-tint); border-color: var(--frame-border); }
:root[data-profile="glass"] .btn-secondary:hover { background: var(--row-hover); border-color: var(--rim-hover); }
:root[data-profile="glass"] .input { background: var(--surface-tint); border-color: var(--frame-border); }
:root[data-profile="glass"] .input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft), 0 0 18px -4px var(--accent-glow); }

/* ── De rest van de componenten in Glas ──
   Nagelopen op 27-09-2026: elke regel die een eigen DICHT vlak tekent
   (--bg-raised, --bg-overlay, --bg-elevated, --input-bg, --surface-inset) staat
   hieronder, met regel 1 (tint) voor wat op glas ligt en regel 2 (matglas) voor
   wat erboven zweeft. Badges en alerts staan al op een doorschijnende tint, en
   de tab-control is een lijn: die doen zonder eigen regel mee. Wie een nieuwe
   component met een dicht vlak toevoegt, hoort hier een regel bij te zetten. */
/* Op glas: tint */
:root[data-profile="glass"] .btn-icon { background: var(--surface-tint); border-color: var(--frame-border); }
:root[data-profile="glass"] .btn-icon:hover { background: var(--row-hover); border-color: var(--rim-hover); }
:root[data-profile="glass"] .mobile-pill { background: var(--surface-tint); box-shadow: inset 0 0 0 1px var(--row-line); }
:root[data-profile="glass"] :is(.tab-btn.on, .mobile-pill button.on) { background: var(--chip-on); box-shadow: var(--chip-on-edge); }
:root[data-profile="glass"] .toggle-switch input:not(:checked) ~ .toggle-track { background: var(--surface-tint); border-color: var(--frame-border); }
:root[data-profile="glass"] .checkbox-box:not(.checked) { background: var(--surface-tint); }
:root[data-profile="glass"] :is(.progress-track, .swatch-info) { background: var(--surface-tint); }
:root[data-profile="glass"] .code { background: var(--surface-tint); border-color: var(--frame-border); }
:root[data-profile="glass"] .skeleton { background-image: linear-gradient(90deg, var(--surface-tint) 25%, var(--neutral-bg) 50%, var(--surface-tint) 75%); }
:root[data-profile="glass"] .list-item { border-bottom-color: var(--row-line); }
/* De primaire knop blijft dicht (regel 2): alleen een lichtlijn bovenin, en in
   donker de gloed die hij in Reliëf ook heeft. */
:root[data-profile="glass"] .btn-primary { box-shadow: inset 0 1px 0 rgba(255,255,255,0.25); }
:root[data-profile="glass"][data-theme="dark"] .btn-primary { box-shadow: 0 0 20px var(--accent-glow), inset 0 1px 0 rgba(255,255,255,0.25); }
/* Boven de inhoud: matglas */
:root[data-profile="glass"] :is(.hint-panel, .tooltip, .new-menu) {
  background: linear-gradient(var(--frost), var(--frost)), var(--float-bg);
  border-color: var(--frame-border);
  box-shadow: var(--shadow-2), inset 0 1px 0 var(--frame-top);
  backdrop-filter: blur(var(--float-blur)) saturate(150%); -webkit-backdrop-filter: blur(var(--float-blur)) saturate(150%);
}
:root[data-profile="glass"] .mobile-topbar {
  background: color-mix(in srgb, var(--bg) 60%, transparent);
  border-bottom-color: var(--frame-border);
  backdrop-filter: blur(var(--float-blur)) saturate(150%); -webkit-backdrop-filter: blur(var(--float-blur)) saturate(150%);
}
/* De Bootstrap-overrides, voor een pagina die daarop draait */
:root[data-profile="glass"] .table thead th { background: var(--surface-tint); }
:root[data-profile="glass"] :is(.form-control, .form-select) { background-color: var(--surface-tint); border-color: var(--frame-border); }

/* ═══════════════════════════════════════════════
   RESET & BASE
═══════════════════════════════════════════════ */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html {
  scroll-behavior: smooth;
  scrollbar-width: thin;
  scrollbar-color: var(--scrollbar) transparent;
}

body {
  font-family: var(--font-base);
  background: var(--bg);
  color: var(--text-primary);
  font-size: 15px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  transition: background 0.25s, color 0.25s;
}

/* ═══════════════════════════════════════════════
   LAYOUT
═══════════════════════════════════════════════ */

/* De gloed ligt achter alles en vangt geen klikken. z-index:0 op de laag en :1 op
   .page, want zonder stapelcontext zou een fixed pseudo-element van body over de
   inhoud heen kunnen komen te liggen. */
body::before {
  content: ''; position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background: var(--ambient);
  transition: background 0.3s;
}
.page { position: relative; z-index: 1; display: flex; min-height: 100vh; }

/* ═══════════════════════════════════════════════
   ZIJBALK EN BELIJNING
   Gelijkgetrokken met AssetApp op 31-08-2026. Wat hier stond was een gewoon
   menu: links uitgelijnde items met een afgerond accentvlak achter het actieve
   item, en een border-right op de zijbalk als scheiding. Dat is vervangen door
   de opzet die in de app is uitgegroeid, en de winst zit in één idee:

   HET MENU-ITEM IS DEZELFDE CONTROL ALS EEN TAB, alleen verticaal.

   Een tab staat op zijn basislijn en krijgt bij activering een accent-onderrand.
   Een menu-item staat tegen de scheidingslijn en krijgt bij activering een
   accentstreep OP die lijn. Zelfde maat, zelfde gewicht, zelfde betekenis. Dat
   is ook waarom het item geen achtergrondvlak en geen radius meer heeft: dat
   maakte er een derde soort control van naast de knop en de tab.

   Daaruit volgen drie dingen die er raar uitzien tot je de reden kent:
   - de items zijn RECHTS uitgelijnd, zodat de tekst tegen de lijn aan staat
     zoals een tab tegen zijn basislijn;
   - de zijbalk heeft GEEN border-right meer; de scheiding is een eigen vorm
     geworden (.main::before, hieronder);
   - de scrollbalk van de zijbalk is verborgen. Een klassieke scrollbalk kort de
     inhoudsbreedte met ongeveer 15px in, en dan landt de accentstreep naast de
     scheidingslijn in plaats van erop: die lijn hangt aan het venster en schuift
     niet mee. Zichtbaar zodra het menu langer is dan het scherm.
═══════════════════════════════════════════════ */

.sidebar {
  width: var(--sidebar-w);
  flex-shrink: 0;
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  scrollbar-width: none;
  padding: var(--nav-line-top) var(--sidebar-pad) 2rem;   /* het logo gelijk met de lijn */
  display: flex;
  flex-direction: column;
  /* GEEN eigen achtergrond en GEEN backdrop-filter, in geen van beide standen.
     Dit stond er één ronde wel, en het was fout (gemeld door de gebruiker,
     31-08-2026). Een glasvlak op de zijbalk is 100vh hoog en heeft dus een
     kaarsrechte rand van boven tot onder, terwijl de scheidingslijn ernaast juist
     op --nav-line-top begint, op --nav-line-bottom ophoudt en aan beide kanten
     afgerond naar binnen buigt. Die twee vechten met elkaar: je ziet een rechte
     kolomrand achter een lijn die nadrukkelijk geen rechte kolomrand is, en dan
     is de zorgvuldige vorm van die lijn weggegooid.

     De scheiding komt dus van de lijn, en van niets anders. Dat is dezelfde regel
     die er al stond voor de vlakke stand; hij bleek gewoon voor allebei te
     gelden. */
  transition: border-color 0.25s, background 0.25s;
}
.sidebar::-webkit-scrollbar { display: none; }
/* Gescrold: de bovenkant vervaagt tot net onder het legenda-icoon, zodat er geen
   menu-item naast het icoon staat (gebruiker, 27-09-2026). Een masker en geen
   vlak, want de zijbalk heeft geen eigen ondergrond (zie hierboven). */
.sidebar.is-scrolled {
  -webkit-mask-image: linear-gradient(to bottom, transparent calc(var(--nav-line-top) + 0.4rem), #000 calc(var(--nav-line-top) + 2.6rem));
  mask-image: linear-gradient(to bottom, transparent calc(var(--nav-line-top) + 0.4rem), #000 calc(var(--nav-line-top) + 2.6rem));
}
/* Het merk: het logo-vak BOVEN de naam, gecentreerd. Gelijkgetrokken met AssetApp
   op 27-09-2026; hier stonden vak en naam naast elkaar. */
.sidebar-logo {
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin-bottom: 2rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.6rem;
  text-align: center;
}
/* De regel onder het merk. In AssetApp staat hier de actieve portefeuille: "waar
   ben ik" hoort bij "welke app is dit", en moet te zien zijn zonder te zoeken. */
.sidebar-portfolio {
  margin-top: -1.4rem; margin-bottom: 2rem;
  text-align: center;
  font-size: 0.7rem; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--text-sec);
}
/* 56px en gecentreerd in de zijbalk; een mobiele topbalk houdt 40px. Het merk
   krijgt hier meer ruimte dan het elders nodig heeft, want dit is de enige plek
   waar het staat. */
.sidebar-logo-icon {
  width: 56px; height: 56px;
  background: var(--accent);
  border-radius: 14px;
  display: grid;
  place-items: center;
  font-size: 1.3rem;
  letter-spacing: -0.03em;
  color: #fff;
  font-weight: 800;
  flex-shrink: 0;
  transition: background 0.45s var(--ease-out);
}
[data-theme="dark"] .sidebar-logo-icon { color: #000; }

/* Groepskop: RECHTS uitgelijnd met de items eronder, en de padding-right is exact
   de 0,6rem inspringing van een item, zodat de rechterrand van kop en items
   dezelfde lijn is. Niet compenseren voor de letter-spacing: die valt binnen de
   tekstbox en verschuift de laatste letter dus niet. */
.sidebar-section-title {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-ter);
  text-align: right;
  padding: 1.4rem 0.6rem 0.5rem 0;
}
.sidebar-section-title:first-of-type { padding-top: 0; }

/* Nav als flex-kolom die de resterende hoogte vult, zodat een .nav-spacer een
   onderste groep naar de onderkant duwt. De min-height op de spacer is de
   minimale scheiding wanneer het menu langer is dan het scherm: de spacer kan
   dan niet groeien, maar blijft een duidelijke witruimte. */
.nav { display: flex; flex-direction: column; flex: 1; padding-bottom: 1rem; }
.nav-spacer { flex: 1 1 auto; min-height: 1.8rem; }

/* Zelfde maat en gewicht als de grote tab-variant (.tabset--lg). Hover is alleen
   kleur; geen vlak, geen radius.
   DE SELECTOR IS .nav a EN NIET .sidebar a (28-09-2026). Met .sidebar a kreeg
   elke link in de zijbalk deze regels, ook een knop onderin die als <a> is
   geschreven: die werd display:block en rechts uitgelijnd, en was geen knop
   meer. Gevonden bij het nabouwen van Diepte v2-2 met alleen dit document. */
.nav a {
  display: block;
  font-size: 0.92rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--text-sec);
  text-decoration: none;
  padding: 0.32rem 0.6rem;
  text-align: right;
  transition: color 0.15s;
  margin: 1px 0;
  cursor: pointer;
}
.nav a:hover { color: var(--text-primary); }
.nav a.active { color: var(--accent-text); }

/* De knop Instellingen onderin de zijbalk. VAST ONDERIN (gebruiker, 27-09-2026):
   is het menu langer dan het scherm, dan schuiven de items eronder door en blijft
   de knop in beeld. Daarom een eigen ondergrond met een vervaging erboven, zodat
   een item niet door de knop heen leest; in Glas is dat matglas, want hij
   zweeft dan boven de items (regel 2 van het profiel). De negatieve marge en de
   padding onderin nemen de onderpadding van de zijbalk over, anders plakt hij
   op 2rem boven de rand en schuiven de items onder hem door in dat gat. */
.controls {
  position: sticky; bottom: -2rem; z-index: 2;   /* = de onderpadding van .sidebar: sticky rekent vanaf de inhoudsrand */
  margin-top: auto;
  margin-bottom: -2rem;
  padding: 1.1rem 0 1.4rem;
  border-top: 1px solid var(--border);
  background: var(--bg);
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.controls::before {
  content: ''; position: absolute; left: 0; right: 0; bottom: 100%; height: 1.4rem; pointer-events: none;
  background: linear-gradient(to bottom, transparent, var(--bg));
}
/* In Glas GEEN matglas, en dat is gemeten: het masker van .sidebar.is-scrolled
   maakt de zijbalk een backdrop root, en dan ziet een blur hierin de items niet
   meer. Ze stonden scherp door de strook heen. Dus bijna dicht, met dezelfde
   overgang erboven; de gloed komt er nog een beetje doorheen. */
:root[data-profile="glass"] .controls { background: color-mix(in srgb, var(--bg) 90%, transparent); }
:root[data-profile="glass"] .controls::before { background: linear-gradient(to bottom, transparent, color-mix(in srgb, var(--bg) 90%, transparent)); }
.control-label {
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-ter);
  margin-bottom: 0.3rem;
}
/* HET SPOOR IS EEN TINT DONKERDER DAN ZIJN ONDERGROND, zonder rand (gebruiker,
   27-09-2026; een rand is die dag geprobeerd en teruggedraaid). Op de pagina en in
   de zijbalk is dat --bg-raised, zoals altijd. In een dialoog of kaart niet: daar
   IS de ondergrond al --bg-raised of iets vergelijkbaars, en dan verdween de pil.
   Daar wordt het spoor een donkere laag over het vlak (--pill-track, gezet op
   .dialog en .card). In Glas krijgt hij wél een rand, want daar is hij doorschijnend. */
.pill-toggle {
  display: flex;
  background: var(--pill-track, var(--bg-raised));
  border-radius: var(--radius-full);
  padding: 3px;
  gap: 2px;
}
.pill-toggle button {
  flex: 1;
  border: none;
  background: transparent;
  color: var(--text-sec);
  font-family: var(--font-base);
  font-size: 0.75rem;
  font-weight: 500;
  padding: 0.3rem 0.5rem;
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: all 0.2s;
  white-space: nowrap;
}
/* Actief = het accent zelf, niet een grijstint. Dat is de stand van AssetApp en
   het is ook consequenter: overal elders in dit document betekent accent-vulling
   "dit is de gekozen optie". */
.pill-toggle button.on {
  background: var(--accent);
  color: #fff;
}
[data-theme="dark"] .pill-toggle button.on { color: #000; }

/* Accentkeuze als stippen en niet als pillen: bij negen opties is de kleur zelf
   het label, en negen woorden naast elkaar passen niet in 240px. Negen stippen
   van 20px met 0.35rem ertussen zijn samen ongeveer 225px. */
.accent-dots { display: flex; gap: 0.35rem; justify-content: flex-end; }
.accent-dots button {
  width: 20px; height: 20px; padding: 0;
  border-radius: 50%; border: 2px solid transparent;
  cursor: pointer; transition: border-color 0.15s, transform 0.15s;
}
.accent-dots button:hover { transform: scale(1.12); }
.accent-dots button.on { border-color: var(--text-primary); }
.accent-dots button[data-a="orange"]  { background: #FF6B00; }
.accent-dots button[data-a="blue"]    { background: #3B82F6; }
.accent-dots button[data-a="lila"]    { background: #B388FF; }
.accent-dots button[data-a="teal"]    { background: #14B8A6; }
.accent-dots button[data-a="magenta"] { background: #E24DAE; }
.accent-dots button[data-a="indigo"]  { background: #818CF8; }
.accent-dots button[data-a="cyan"]    { background: #22D3EE; }
.accent-dots button[data-a="slate"]   { background: #94A3B8; }
.accent-dots button[data-a="sand"]    { background: #C8B89E; }
/* In licht de lichte waarde van elk accent, anders toont de kiezer in licht de
   donkere kleur en niet de kleur die je krijgt. */
[data-theme="light"] .accent-dots button[data-a="orange"]  { background: #E05500; }
[data-theme="light"] .accent-dots button[data-a="blue"]    { background: #2563EB; }
[data-theme="light"] .accent-dots button[data-a="lila"]    { background: #7C3AED; }
[data-theme="light"] .accent-dots button[data-a="teal"]    { background: #0D9488; }
[data-theme="light"] .accent-dots button[data-a="magenta"] { background: #BE2A86; }
[data-theme="light"] .accent-dots button[data-a="indigo"]  { background: #4F46E5; }
[data-theme="light"] .accent-dots button[data-a="cyan"]    { background: #0E7490; }
[data-theme="light"] .accent-dots button[data-a="slate"]   { background: #475569; }
[data-theme="light"] .accent-dots button[data-a="sand"]    { background: #7A6650; }

/* main content
   GEEN max-width (28-09-2026, wens van de gebruiker): kaders lopen over de volle
   breedte naast het menu. Hier stond 860px, en dat was de maat van deze stijlgids
   en niet van een app: een rij van een grote kaart en drie assetkaarten paste er
   niet naast elkaar in. Wat een leesbreedte nodig heeft, lopende tekst, krijgt
   die zelf (max-width op het tekstblok, zoals de secties hieronder doen). */
.main {
  flex: 1;
  min-width: 0;
  padding: var(--nav-line-top) var(--main-pad-x) var(--nav-line-bottom);
}

/* ── Scheidingslijn zijbalk naar pagina ─────────────────────────────────────
   Dezelfde vorm als de tab-lijn: één doorlopende lijn met aan beide uiteinden
   een kort haaks stukje met radius, dat naar het menu toe buigt. Hier verticaal,
   dus de lijn zit rechts in het kadertje en de eindstukjes wijzen naar links.
   Het is dus GEEN border-right op de zijbalk, en dat is het hele punt: een
   border loopt van boven tot onder en heeft geen uiteinden, terwijl deze vorm
   een begin en een eind heeft.

   position:fixed omdat de lijn bij het venster hoort, net als de 100vh-sticky
   zijbalk: hij blijft staan terwijl de pagina scrollt.

   Hij hangt aan .main en niet aan .page of body. In AssetApp is dat nodig omdat
   de onthul-gate van de app-schil op .page zit en er anders een lijn zou flitsen
   vóór het splash-scherm. Hier is er geen gate, maar de plek blijft dezelfde,
   zodat de twee stylesheets naast elkaar te leggen zijn.

   De bovenkant staat gelijk met de bovenkant van het logo en van de inhoud
   (--nav-line-top, 20-10-2026; tot dan de middellijn van de paginatitel).

   Géén z-index: de accentstreep hieronder moet er overheen kunnen.

   DE VORM STAAT ÉÉN KEER EN DE PLEK TWEE KEER. De pagina-kolom (sectie 02c)
   draagt dezelfde lijn, en twee kopieën van deze acht regels zijn de plek waar ze
   ooit uit de pas gaan lopen: dan staan er twee boogjes van verschillende grootte
   naast elkaar. Alleen de `left` verschilt, want dat is precies wat "aan welke
   kolom hangt hij" betekent. */
.main::before, .page-aside::after {
  content: ''; position: fixed; pointer-events: none;
  top: var(--nav-line-top); bottom: var(--nav-line-bottom);
  width: var(--nav-line-cap);
  border: 1px solid var(--border-mid); border-left: none;
  border-top-right-radius: calc(var(--nav-line-cap) - 1px);
  border-bottom-right-radius: calc(var(--nav-line-cap) - 1px);
  transition: border-color 0.25s;
}
.main::before { left: calc(var(--sidebar-w) - var(--nav-line-cap)); }

/* Actieve menu-keuze: dikke accentstreep ÓP de scheidingslijn, precies zo hoog
   als het nav-item. Het verticale equivalent van de accent-onderrand van de
   actieve tab. De streep is 3px en ligt gecentreerd over de 1px-lijn (vandaar de
   -1px naast de zijbalk-padding), zoals een actieve tab met margin-bottom:-1px
   over zijn basislijn valt. z-index:1 zet hem boven .main::before. */
.nav a.active { position: relative; }
.nav a.active::after {
  content: ''; position: absolute; z-index: 1; top: 0; bottom: 0;
  right: calc(-1 * var(--sidebar-pad) - 1px); width: 3px;
  border-radius: 2px; background: var(--accent);
}

/* De paginakop. Zijn bovenkant ligt op de bovenpadding van .main, dus gelijk met
   het boven-boogje en het logo; zonder titel neemt het eerste kader die plek in. */
.page-header { display: flex; align-items: baseline; gap: 1rem; margin-bottom: 2rem; }
.page-title { font-size: 1.4rem; font-weight: 700; letter-spacing: -0.02em; }

/* ── DE PAGINA-KOLOM: een tweede menu, maar van de pagina ───────────────────
   Overgenomen uit AssetApp op 04-09-2026. Daar is hij ontstaan voor één pagina
   en meteen als patroon opgeschreven, op verzoek van de gebruiker: "niet alleen
   voor die pagina maar waarschijnlijk voor meer, dus deels generiek". Deze
   regels zijn dus de generieke helft; wat er ín de kolom staat is van de pagina.

   Een pagina die hem gebruikt schrijft:
       <div class="page-layout">
         <aside class="page-aside">…</aside>
         <div class="page-body">…</div>
       </div>

   HET IS EEN <aside> EN GEEN TWEEDE <nav>. Er staan geen links in maar de
   kengetallen en de bedieningen van díe pagina. Hij staat vóór de inhoud in de
   bronorde omdat hij dat op het scherm ook is, zodat de voorleesvolgorde de
   zichtbare volgorde volgt.

   DE BREEDTE IS 1,65 KEER DE MENUBREEDTE, en dat is een ruil die gemeten is: op
   240px kost de kolom géén kaartkolom in het raster ernaast (die kolommen hebben
   een minimum en verdelen de rest, dus er valt niets onder de drempel), op 396px
   kost hij er een. Dat is sinds 11-09-2026 de maat, en hij is die dag twee keer
   verzet: van de menubreedte naar het dubbele, en daarna terug naar 1,65. Wat in
   de kolom staat heeft breedte nodig (kengetallen naast elkaar, een grafiek, een
   lijst met een bedrag erin), maar op het dubbele bleven er naast een pagina met
   kaarten drie te smalle kolommen over. Dat is de afweging: de kolom en het
   raster ernaast vechten om dezelfde pixels, en welke kant wint hangt af van wat
   er in de kaarten staat.

   ELK RASTER IN DE KOLOM STAAT DAAROM OP `auto-fit` EN NIET OP EEN VAST AANTAL
   KOLOMMEN. Dan is de breedte hierboven één getal en past de inhoud zich aan in
   plaats van af te kappen. Eén valkuil, en die is gemeten: een grid met auto-fit
   in een doos die naar zijn inhoud krimpt (een blok dat centreert, bijvoorbeeld)
   heeft geen beschikbare breedte om tegen af te tellen en valt terug op één
   kolom. Daar is flex met wrap het gereedschap, niet grid.

   DE NEGATIEVE MARGE HAALT HEM TEGEN HET MENU AAN: hij neemt de zijpadding van
   .main weg, zodat de kolom op de scheidingslijn begint en zijn eigen padding de
   afstand tot die lijn bepaalt, net als bij de menu-items. Sinds --main-pad-x
   dezelfde maat is als --sidebar-pad is die marge nul geworden; de regel blijft
   staan omdat hij de relatie vastlegt en niet een getal.

   HIJ IS EXACT ZO HOOG ALS ER IN BEELD PAST: `height` en niet `max-height`, want
   een flex-kind kan alleen ruimte verdelen als de doos een hoogte HEEFT. Met
   max-height groeit hij naar zijn inhoud en valt de onderkant eraf. De overflow
   blijft als noodklep voor een heel laag venster, maar hoort niets te doen: wat
   erin staat krimpt mee.

   position:sticky MAAKT ALTIJD EEN STAPELCONTEXT, ook zonder z-index. Alles in
   deze kolom staat daarmee op één niveau, en de pagina-inhoud komt erna in de
   bronorde: een uitklappend paneel in de kolom verdwijnt dus onder de inhoud
   tenzij de KOLOM een z-index heeft. Op het paneel zelf helpt het niet. */
.page-layout { display: flex; align-items: flex-start; gap: 2rem; }
/* `:has()` op `html` EN NIET op `.page-layout`, en dat is het punt van deze twee
   regels: een custom property geldt vanaf het element waar hij staat, en de
   kantlijn en het legenda-icoon staan BUITEN de kolom. Op .page-layout zou de
   waarde onbereikbaar zijn voor precies de twee dingen die hem nodig hebben.
   Om dezelfde reden is een pagina-afwijking van de breedte óók een `:has()` op
   `html`: zette een pagina --page-aside-w op zijn eigen .page-layout, dan bleef
   de kantlijn op de centrale maat staan en liep de lijn dwars door de inhoud. */
html:has(.page-layout--narrow) { --page-aside-w: var(--sidebar-w); }
html:has(.page-layout > .page-aside) { --hint-line-x: calc(var(--sidebar-w) + var(--page-aside-w)); }
.page-aside {
  flex: 0 0 var(--page-aside-w); width: var(--page-aside-w);
  margin-left: calc(-1 * var(--main-pad-x));   /* = de zijpadding van .main */
  padding: 0 var(--sidebar-pad) 2rem;
  position: sticky; top: 0; height: calc(100vh - var(--nav-line-bottom));
  overflow-y: auto; scrollbar-width: none;
  display: flex; flex-direction: column; gap: 1.6rem;
  z-index: 20;
}
.page-aside::-webkit-scrollbar { display: none; }
/* De kantlijn van de kolom: dezelfde vorm als die van het menu, alleen een
   andere x. De vorm staat één keer (zie .main::before hierboven) en de plek twee
   keer, want "aan welke kolom hangt hij" ís de plek. Twee kopieën van de vorm
   zijn de plek waar ze ooit uit de pas gaan lopen. */
.page-aside::after { left: calc(var(--sidebar-w) + var(--page-aside-w) - var(--nav-line-cap)); }
.page-body { flex: 1; min-width: 0; }
/* ONDER DIT BREEKPUNT IS ER GEEN TWEEDE KOLOM TE VERDELEN. Dan wordt de kolom
   een strook bóven de inhoud, zonder lijn en zonder sticky-gedrag; de legenda
   valt terug op de lijn van het hoofdmenu. */
@media (max-width: 1280px) {
  html:has(.page-layout > .page-aside) { --hint-line-x: var(--sidebar-w); }
  .page-layout { --page-aside-w: auto; display: block; }
  .page-aside { position: relative; width: auto; height: auto; overflow: visible; margin: 0 0 1.8rem; padding: 0; }
  .page-aside::after { display: none; }
}

/* ── DE PAGINA-LEGENDA: één i-icoon per pagina, óp de kantlijn ──────────────
   Overgenomen uit AssetApp op 04-09-2026. Vastgelegd door de gebruiker: "als er
   een pagina-menu is dat de i-icoon op het pagina-menu komt, en anders in het
   hoofd-menu. 1 legenda voor alle zaken op de pagina."

   WAAROM HIJ BESTAAT. Pagina's verzamelen uitleg: wat een kleur betekent, waarom
   een bedrag er niet in zit, waartegen een percentage gemeten is. Elke regel
   wordt één keer gelezen en daarna een regel die je leert overslaan, en samen
   verdringen ze de getallen die ze moesten steunen. Achter één icoon is de
   pagina stil en is de uitleg één beweging weg.

   HIJ HOORT BIJ DE SHELL EN NIET BIJ DE PAGINA, en dat is de regel die de rest
   verklaart. Hij hing in de hoek van een kaart, en dat is om drie redenen
   teruggedraaid: de plek volgde uit waar die kaart toevallig stond, een pagina
   met twee kaarten had twee legenda's, en één ervan zat in een dichtgeklapt
   <details> en was dus onbereikbaar. De regel is nu: één legenda per pagina, met
   een tussenkop per onderdeel, en de pagina MELDT zijn regels aan in plaats van
   ze te tekenen.

   DE PLEK IS DE KANTLIJN EN DUS EEN X DIE DE SHELL KENT (--hint-line-x): die van
   de pagina-kolom als de pagina er een heeft, en anders die van het hoofdmenu.
   Het icoon staat in de binnenhoek van het bovenste haakje van die lijn. Dat is
   waarom er geen cirkel of kader om het icoon zit: het ligt niet óp de lijn maar
   in de lege hoek ernaast, dus er valt niets af te dekken.

   HET PANEEL OPENT NAAR LINKS, OVER DE MENU'S, want het icoon hangt op de
   rechterrand van een menu en dan is de menukant de kant die het níet in de weg
   zit. Zonder pagina-kolom klapt het om naar rechts: links van het hoofdmenu is
   240px en een legenda van 240px breed leest niet.

   position:fixed EN NIET absolute, zodat geen enkele scroll-container hem
   afkapt. Dat is precies wat er misging toen hij in de pagina-kolom hing: die
   heeft overflow-y:auto, en een scroll-container kapt óók horizontaal af, want
   overflow-x:visible bestaat niet naast een auto op de andere as.

   HET IS GEEN title="". Een native tooltip draagt één regel platte tekst; een
   legenda moet KUNNEN LATEN ZIEN hoe een streeplijn eruitziet en wat een open
   stip betekent. Een monster doet dat in één blik waar een zin drie regels
   nodig heeft. Vandaar de vaste monsterbreedte: de tekst lijnt uit of er een
   monster staat of niet.

   HOVER ALLEEN ZOU HEM ONBEREIKBAAR MAKEN met een toetsenbord, dus het is een
   <button> en het paneel opent ook op :focus-visible. */
#page-hint { position: fixed; z-index: 120; top: var(--nav-line-top); left: calc(var(--hint-line-x) - 1.85rem); }
.hint { position: relative; }
.hint-btn {
  display: flex; align-items: center; justify-content: center;
  width: 1.6rem; height: 1.6rem; padding: 0; border: none; border-radius: 50%;
  background: transparent; color: var(--text-sec); opacity: 0.55; cursor: pointer;
  transition: opacity 0.15s, color 0.15s;
}
.hint-btn:hover, .hint-btn:focus-visible { opacity: 1; color: var(--accent-text); }
.hint-btn svg { width: 1rem; height: 1rem; }
/* NOOIT BUITEN BEELD: een hoogtegrens met eigen scroll is het enige dat altijd
   werkt. `visibility` naast `opacity` zodat het gesloten paneel geen
   muisgebeurtenissen opvangt van wat eronder ligt. */
.hint-panel {
  position: fixed; top: var(--nav-line-top);
  right: calc(100% - var(--hint-line-x) + 1.4rem); left: auto;
  width: min(44rem, calc(var(--hint-line-x) - 2.8rem));
  max-height: calc(100vh - var(--nav-line-top) - 2rem); overflow-y: auto;
  padding: 0.9rem 1rem; border-radius: var(--radius-md);
  background: var(--bg-elevated); border: 1px solid var(--border); box-shadow: var(--shadow-2);
  opacity: 0; visibility: hidden; transform: translateY(4px);
  transition: opacity 0.15s, transform 0.15s, visibility 0.15s; pointer-events: none;
}
html:not(:has(.page-layout > .page-aside)) .hint-panel {
  right: auto; left: calc(var(--hint-line-x) + 1.4rem);
  width: min(44rem, calc(100vw - var(--hint-line-x) - 2.8rem));
}
.hint:hover .hint-panel, .hint-btn:focus-visible + .hint-panel { opacity: 1; visibility: visible; transform: translateY(0); }
/* De tussenkop per onderdeel: dat is wat één paneel voor een hele pagina
   leesbaar houdt. Niet vóór de eerste regel, want daar staat de titel al. */
.hint-heading { margin-top: 1rem; }
.hint-heading:first-of-type { margin-top: 0.8rem; }
.hint-row { display: flex; align-items: baseline; gap: 0.6rem; margin-top: 0.55rem; font-size: 0.78rem; color: var(--text-sec); line-height: 1.45; }
/* Alle monsters even breed, zodat de tekst uitlijnt ook als het monster leeg is. */
.hint-sample { flex: 0 0 1.4rem; }
.hint-row > span:last-child > div { margin-top: 0.2rem; }
.hint-line { display: inline-block; width: 1.4rem; height: 0; border-top: 2px solid var(--accent); transform: translateY(-0.25rem); }
.hint-line.is-dashed { border-top-style: dashed; opacity: 0.5; }
.hint-dot { display: inline-block; width: 0.4rem; height: 0.4rem; margin-left: 0.5rem; border-radius: 50%; background: var(--accent); transform: translateY(-0.1rem); }
.hint-dot.is-open { background: var(--bg-elevated); box-shadow: 0 0 0 1.5px var(--accent); }
.hint-ring { display: inline-block; width: 0.75rem; height: 0.75rem; margin-left: 0.3rem; border-radius: 50%; border: 3px solid var(--accent); transform: translateY(0.1rem); }

/* ── Schaalmodel van de drie kolommen (alleen voor dit document) ─────────────
   De echte pagina-kolom hangt aan het venster: zijn kantlijn is position:fixed
   en loopt van boven tot onder in beeld. In een etalagepagina met veertien
   secties kan dat niet echt staan, want dan kruist die lijn de rest van het
   document. Vandaar dit model: dezelfde verhoudingen en dezelfde twee haakjes,
   maar binnen een kader. Het is een tekening en geen namaak-component, dus de
   klassen beginnen met ds- en horen niet in een app. */
.ds-shell { position: relative; display: flex; gap: 0.9rem; height: 11rem; padding: 0.9rem 0; }
.ds-shell-col { flex: 0 0 4.5rem; display: flex; flex-direction: column; gap: 0.4rem; align-items: flex-end; padding-right: 0.9rem; }
.ds-shell-col--body { flex: 1; align-items: stretch; padding-right: 0; }
.ds-shell-line { position: absolute; top: 1.6rem; bottom: 0.9rem; width: var(--nav-line-cap); border: 1px solid var(--border-mid); border-left: none; border-top-right-radius: calc(var(--nav-line-cap) - 1px); border-bottom-right-radius: calc(var(--nav-line-cap) - 1px); }
.ds-shell-bar { height: 0.55rem; border-radius: 3px; background: var(--neutral-bg); }
.ds-shell-bar--on { background: var(--accent-mid); }
.ds-shell-card { flex: 1; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); }
.ds-shell-i { position: absolute; top: 1.6rem; width: 1.1rem; height: 1.1rem; display: flex; align-items: center; justify-content: center; color: var(--accent-text); font-size: 0.7rem; font-weight: 700; }

/* ═══════════════════════════════════════════════
   SECTION HEADERS
═══════════════════════════════════════════════ */

.ds-section {
  margin-bottom: 5rem;
  scroll-margin-top: 2rem;
}
.ds-section-header {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  margin-bottom: 2rem;
}
.ds-section-num {
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--accent);
  letter-spacing: 0.08em;
  font-variant-numeric: tabular-nums;
}
.ds-section-title {
  font-size: 1.1rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}
.ds-subsection { margin-bottom: 2.5rem; }
.ds-subsection-title {
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-sec);
  margin-bottom: 1.2rem;
}
.ds-row { display: flex; flex-wrap: wrap; gap: 1rem; align-items: flex-start; margin-bottom: 1rem; }

/* ═══════════════════════════════════════════════
   COLORS
═══════════════════════════════════════════════ */

.swatch-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 0.75rem;
}
.swatch {
  border-radius: var(--radius-md);
  overflow: hidden;
  border: 1px solid var(--border);
}
.swatch-color { height: 56px; }
.swatch-info { padding: 0.6rem 0.7rem; background: var(--bg-elevated); }
.swatch-name { font-size: 0.72rem; font-weight: 600; }
.swatch-val { font-size: 0.65rem; color: var(--text-sec); font-variant-numeric: tabular-nums; }

/* ═══════════════════════════════════════════════
   TYPOGRAPHY
═══════════════════════════════════════════════ */

.type-specimen { margin-bottom: 1rem; }
.type-label {
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-ter);
  margin-bottom: 0.4rem;
  font-variant-numeric: tabular-nums;
}

.t-display    { font-size: 2.8rem; font-weight: 800; letter-spacing: -0.03em; line-height: 1.05; }
.t-h1         { font-size: 2rem;   font-weight: 700; letter-spacing: -0.025em; line-height: 1.15; }
.t-h2         { font-size: 1.4rem; font-weight: 700; letter-spacing: -0.02em; line-height: 1.2; }
.t-h3         { font-size: 1.1rem; font-weight: 600; letter-spacing: -0.01em; line-height: 1.3; }
.t-h4         { font-size: 0.9rem; font-weight: 600; letter-spacing: 0em; line-height: 1.4; }
.t-body       { font-size: 0.93rem; font-weight: 400; line-height: 1.65; color: var(--text-primary); }
.t-body-sm    { font-size: 0.82rem; font-weight: 400; line-height: 1.6; color: var(--text-sec); }
.t-caption    { font-size: 0.72rem; font-weight: 400; line-height: 1.5; color: var(--text-sec); }
.t-label      { font-size: 0.68rem; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-sec); }
.t-mono       { font-size: 0.82rem; font-family: var(--font-mono); color: var(--accent); }
.t-num-lg     { font-size: 2.4rem; font-weight: 700; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }
.t-num-md     { font-size: 1.4rem; font-weight: 700; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.t-pos        { color: var(--pos); }
.t-neg        { color: var(--neg); }
.t-muted      { color: var(--text-sec); }
.t-accent     { color: var(--accent); }

/* ═══════════════════════════════════════════════
   BUTTONS
═══════════════════════════════════════════════ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  border: none;
  cursor: pointer;
  font-family: var(--font-base);
  font-weight: 600;
  letter-spacing: -0.01em;
  transition: opacity 0.15s, transform 0.1s, box-shadow 0.2s;
  white-space: nowrap;
  text-decoration: none;
}
.btn:active { transform: scale(0.97); }
.btn:disabled { opacity: 0.35; cursor: not-allowed; }
.btn:disabled:active { transform: none; }

/* sizes */
.btn-lg { font-size: 0.9rem; padding: 0.85rem 1.8rem; border-radius: var(--radius-md); }
.btn-md { font-size: 0.82rem; padding: 0.65rem 1.3rem; border-radius: var(--radius-md); }
.btn-sm { font-size: 0.75rem; padding: 0.45rem 0.9rem; border-radius: var(--radius-sm); }
.btn-xs { font-size: 0.68rem; padding: 0.3rem 0.7rem; border-radius: var(--radius-sm); }

/* variants */
.btn-primary  { background: var(--accent); color: #fff; }
[data-theme="dark"] .btn-primary { color: #000; }
.btn-primary:hover { opacity: 0.88; }
/* De gloed hing aan het groene accent, dat op 31-08-2026 is vervallen. Hij hoort
   nu bij de glas-stand en geldt daar voor élk accent: het is een eigenschap van
   de stand en niet van één kleur. Alleen in het donkere thema, want op wit is
   een gekleurde gloed rond een knop vlekkerig in plaats van helder. */
[data-depth="deep"][data-theme="dark"] .btn-primary { box-shadow: 0 0 20px var(--accent-glow); }

.btn-secondary { background: var(--bg-raised); color: var(--text-primary); border: 1px solid var(--border-mid); }
.btn-secondary:hover { background: var(--bg-overlay); }

.btn-ghost { background: transparent; color: var(--text-primary); border: 1px solid var(--border-mid); }
.btn-ghost:hover { background: var(--neutral-bg); }

.btn-accent-soft { background: var(--accent-soft); color: var(--accent-text); }
.btn-accent-soft:hover { background: var(--accent-mid); }

.btn-danger { background: var(--neg-bg); color: var(--neg); }
.btn-danger:hover { opacity: 0.8; }

.btn-icon { padding: 0.65rem; border-radius: var(--radius-md); background: var(--bg-raised); color: var(--text-sec); border: 1px solid var(--border); }
.btn-icon:hover { color: var(--text-primary); background: var(--bg-overlay); }

/* De HOEKKNOP: een klein rond icoon zonder rand of vlak, in de hoek van een kaart of
   dialoog (detail-chevron op een kaart, sluitkruisje van een dialoog). Vastgelegd
   26-09-2026 op verzoek van de gebruiker: één uiterlijk voor elke hoekknop. Rust tot
   je hem aanwijst; dan een zacht accentvlak en accenttekst. In de app verschijnt hij
   pas als je de kaart of dialoog aanwijst (en op toetsfocus). In AssetApp heet hij
   `.wealth-detail-btn` en `.dialog-close`. */
.btn-corner { display: inline-grid; place-items: center; width: 1.7rem; height: 1.7rem; padding: 0; border: 0; border-radius: 50%; background: none; color: var(--text-ter); cursor: pointer; transition: background 0.15s, color 0.15s; }
.btn-corner:hover, .btn-corner:focus-visible { background: var(--accent-soft); color: var(--accent-text); }
.btn-corner svg { display: block; }

/* ═══════════════════════════════════════════════
   DIALOOG
   Overgenomen uit AssetApp (ui/dialog.js) op 27-09-2026. Drie vormen: een
   vraag met knoppen onderin, een mededeling met alleen het kruisje, en een
   breed paneel met eigen scroll (.dialog-body--rich), waarvan de instellingen
   (.dialog--settings) er een zijn.

   DE PADDING STAAT ALS VARIABELE, zodat het kruisje in elke variant op
   dezelfde plek staat: een variant die zijn zijpadding weghaalt en hem op de
   knop met de hand terugzet, typt vroeg of laat een ander getal.

   HET KRUISJE IS DE HOEKKNOP (.btn-corner-vorm) en verschijnt pas bij hover op
   de dialoog, plus op toetsfocus. pointer-events en niet visibility: dan is een
   onzichtbare knop niet aan te klikken, en schakelt hij hard mee met de hover.

   Een dialoog zonder keuze krijgt geen knop onderin maar alleen het kruisje.
   Esc sluit hem altijd. EEN KLIK OP DE OVERLAY ALLEEN ALS HIJ GEEN UITWEG HEEFT
   (geen knop en geen kruisje), bijgewerkt 18-10-2026 naar de gedeelde module
   HomeGit/Shared/client/ui/dialog.js: wie iets leest of invult, verliest anders zijn
   werk bij een muisknop die net naast de rand loslaat. */
.dialog-overlay { position: fixed; inset: 0; z-index: 2000; display: flex; align-items: center; justify-content: center; padding: 1.5rem; background: rgba(0,0,0,0.55); animation: dlg-fade 0.12s ease-out; }
.dialog-overlay[hidden] { display: none; }
.dialog { --dialog-pad-y: 1.5rem; --dialog-pad-x: 1.6rem; position: relative; width: 100%; max-width: 420px; background: var(--bg-elevated); border: 1px solid var(--border-mid); border-radius: var(--radius-lg); padding: var(--dialog-pad-y) var(--dialog-pad-x); box-shadow: var(--shadow-3); }
[data-theme="dark"] .dialog { background: var(--bg-raised); }
.dialog-title { font-size: 1.05rem; font-weight: 700; color: var(--text-primary); margin-bottom: 0.6rem; }
.dialog:has(.dialog-close) .dialog-title { padding-right: 2.2rem; }
.dialog-close {
  position: absolute; top: 0.65rem; right: 0.65rem; z-index: 3;
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.7rem; height: 1.7rem; padding: 0; border: 0; border-radius: 50%;
  background: transparent; color: var(--text-ter); cursor: pointer;
  opacity: 0; pointer-events: none; transition: opacity 0.15s, background 0.15s, color 0.15s;
}
.dialog:hover .dialog-close, .dialog-close:focus-visible { opacity: 1; pointer-events: auto; }
.dialog-close:hover { background: var(--accent-soft); color: var(--accent-text); }
.dialog-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.dialog-body { font-size: 0.9rem; color: var(--text-sec); line-height: 1.5; white-space: pre-line; }
.dialog-body--rich { white-space: normal; max-height: min(78vh, calc(100vh - 12rem)); overflow-y: auto; }
.dialog--settings { max-width: min(46rem, 100%); }
.dialog-actions { display: flex; justify-content: flex-end; gap: 0.6rem; margin-top: 1.4rem; }

/* WAT DE GEDEELDE MODULE NOG MEER SCHRIJFT (HomeGit/Shared/client/ui/dialog.js, uit
   AtreidesAssets, 18-10-2026). Een gewone melding houdt haar regeleinden
   (pre-line hierboven); een paneel met opmaak niet, want op een tabel geeft die
   regel spookruimte tussen de cellen.
   - Een bevestiging BOVENOP een open detailpaneel: erboven, met een lichtere
     sluier, zodat het paneel eronder leesbaar blijft als waar de vraag over gaat.
   - Een vak NAAST de titel (.dialog-title-aside), dat de pagina na het openen
     vult; de titel wordt alleen dan een rij, anders breekt hij als tekst af.
   - Het oog (.dialog-eye) aan het eind van de titelregel, dezelfde ronde knop als
     het kruisje: een app hangt het er via onDetail in (Assets: bedragen tonen).
   - Breed (.dialog--wide) voor een detailpaneel, extra breed (--xl) als er hele
     tabellen in staan.
   - De stappendialoog: VOLLE FOCUS, één maat voor elke stap zodat niets springt,
     over een pagina die donker en vervaagd nog net zichtbaar is; "overslaan"
     staat links, weg van "volgende". */
.dialog-overlay--stacked { z-index: 2001; background: rgba(0,0,0,0.3); }
.dialog-title:has(.dialog-title-aside) { display: flex; align-items: center; gap: 0.7rem; }
.dialog-title:has(.dialog-title-aside) .dialog-title-text { min-width: 0; }
.dialog-eye { display: inline-flex; align-items: center; justify-content: center; flex: none; width: 1.7rem; height: 1.7rem; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--text-ter); cursor: pointer; }
.dialog-eye:hover, .dialog-eye:focus-visible { background: var(--accent-soft); color: var(--accent-text); }
.dialog-eye svg { width: 1rem; height: 1rem; }
.dialog--wide { max-width: min(72rem, 100%); }
.dialog--wide.dialog--xl { max-width: min(110rem, 100%); }
.dialog-overlay--focus { background: rgba(0,0,0,0.78); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.dialog--wide.dialog-stepper { width: min(56rem, 100%); max-width: min(56rem, 100%); height: min(34rem, calc(100vh - 3rem)); display: flex; flex-direction: column; --dialog-pad-y: 2.2rem; --dialog-pad-x: 2.4rem; }
.dialog-stepper .dialog-body { flex: 1; min-height: 0; max-height: none; }
.stepper-title { font-size: 1.5rem; font-weight: 600; letter-spacing: -0.01em; color: var(--text-primary); margin-bottom: 1.6rem; }
.stepper-actions .stepper-skip { margin-right: auto; }
@keyframes dlg-fade { from { opacity: 0; } to { opacity: 1; } }
/* Een dialoog die in de pagina staat in plaats van erboven: alleen voor dit
   document, om de vorm te tonen zonder hem te openen. Het kruisje staat daar
   vast, zodat je hem ook zonder hover ziet. */
.ds-dialog-still { position: relative; }
.ds-dialog-still .dialog-close { opacity: 1; pointer-events: auto; }

/* GLAS: de dialoog is matglas (regel 2 van het profiel: hij zweeft boven tekst,
   en daar doet de blur wél werk), en de pagina erachter dimt én vervaagt. De
   vervaging zegt dat de rest even niet telt. */
/* DE VERVAGING STAAT OP EEN ::before EN NIET OP DE OVERLAY ZELF, en dat is geen
   smaak. Een element met een backdrop-filter is een backdrop root voor alles erin:
   de blur van de dialoog ziet dan alleen de overlay en niet de pagina, en het
   paneel wordt een halfdonker vlak in plaats van matglas. Als laag náást de
   dialoog heeft de overlay geen filter, en ziet de dialoog de pagina wél. */
:root[data-profile="glass"] .dialog-overlay { background: transparent; }
:root[data-profile="glass"] .dialog-overlay::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: rgba(0,0,0,0.32);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
:root[data-profile="glass"][data-theme="light"] .dialog-overlay::before { background: rgba(0,0,0,0.18); }
:root[data-profile="glass"] .dialog {
  background: linear-gradient(var(--frost), var(--frost)), var(--float-bg);
  border-color: var(--frame-border);
  box-shadow: var(--shadow-3), inset 0 1px 0 var(--frame-top);
  backdrop-filter: blur(var(--float-blur)) saturate(150%); -webkit-backdrop-filter: blur(var(--float-blur)) saturate(150%);
}

/* ═══════════════════════════════════════════════
   TAB-CONTROL
   Overgenomen uit AssetApp op 04-09-2026, op verzoek van de gebruiker: hij stond
   daar en niet hier, terwijl het menu-item hierboven zijn verticale tegenhanger
   is. Die twee horen naast elkaar te staan, anders is de gelijkenis een toeval
   dat de volgende wijziging niet overleeft.

   ER IS ÉÉN TAB-CONTROL EN GEEN TWEEDE. In de app stond er tot 18-08-2026 een
   pil-variant naast, die één pagina gebruikte; die is opgeheven. Twee vormen voor
   dezelfde keuze betekent dat de lezer moet leren dat het dezelfde keuze is.

   DE LIJN IS ÉÉN AANEENGESLOTEN VORM, en dat is de hele truc van deze control:
   het rechte middenstuk en de twee uiteinden die omhoog buigen zijn één
   ::before over de volle breedte, met een rechte border-bottom en aan weerszijden
   een border-left/-right met een radius. Hier stond eerst een los recht stuk lijn
   plus twee bochtjes, en dan begint de bocht bij de eerste hertekening ergens
   anders dan het echte uiteinde. De eigen border-bottom van .tabset is
   transparant en bestaat alleen nog voor de layout-hoogte en voor de -1px-truc
   van de tab hieronder.

   DE ACTIEVE TAB VALT MET margin-bottom:-1px OVER ZIJN BASISLIJN, precies zoals
   het actieve menu-item met -1px over de scheidingslijn valt. Dat is dezelfde
   maat en dezelfde gedachte: de accentstreep ligt óp de lijn en niet ernaast.

   TWEE MATEN EN NIET MEER. De kale .tabset is de compacte, voor sub-tabs in een
   uitklap of een dialoog; .tabset--lg is de pagina-maat. Een derde maat is er
   niet, en dat is een grens en geen omissie.

   OP EEN TELEFOON WORDEN HET PILLEN. Een doorlopende sierlijn breekt niet netjes
   over twee regels, dus onder 768px vervalt de lijn en wordt elke tab een pil met
   een accent-omranding als hij aan staat. Dat is de enige plek in dit systeem
   waar de pil-vorm nog voorkomt, en daar is hij functioneel: vijf of zes
   pagina-tabs passen anders niet.

   PAST DE GROTE BALK NIET OP ÉÉN REGEL, DAN TWEE RIJEN (26-10-2026, gebruiker).
   ui/tabs.js meet de balk op één regel en zet .tabset--rows als de laatste tab
   buiten de balk valt: een grid van gelijke tabs over de volle breedte, nooit
   meer dan twee rijen. De lijn blijft één vorm: die van rij 1 buigt links
   omhoog, rechts met dezelfde radius OMLAAG, en loopt over in die van rij 2, die
   links weer omhoog buigt. ::after is de rand om rij 2 (de linkerkant
   weggemaskeerd op het laatste bochtje na), ::before het bochtje van rij 1.
═══════════════════════════════════════════════ */

.tabset { position: relative; display: flex; gap: 0.3rem; margin-bottom: 1.1rem; padding-left: 1.3rem; border-bottom: 1px solid transparent; }
.tabset::before {
  content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 8px;
  border-bottom: 1px solid var(--border-mid);
  border-left: 1px solid var(--border-mid);
  border-right: 1px solid var(--border-mid);
  border-bottom-left-radius: 7px; border-bottom-right-radius: 7px;
}
.tabset-tab {
  appearance: none; border: none; cursor: pointer; background: transparent;
  color: var(--text-sec); font-family: var(--font-base); font-size: 0.78rem;
  font-weight: 600; letter-spacing: 0.01em; padding: 0.45rem 0.9rem;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  margin-bottom: -1px; border-bottom: 2px solid transparent;
  transition: color 0.15s, border-color 0.15s;
}
.tabset-tab:hover { color: var(--text-primary); }
.tabset-tab.is-on { color: var(--accent-text); border-bottom-color: var(--accent); }
/* De pagina-maat. */
.tabset--lg { gap: 0.5rem; margin-bottom: 1.6rem; padding-left: 2.2rem; }
.tabset--lg::before { height: 12px; border-bottom-left-radius: 11px; border-bottom-right-radius: 11px; }
.tabset--lg .tabset-tab { font-size: 0.92rem; padding: 0.7rem 1.4rem; border-bottom-width: 3px; }
/* Een teller of stip achter het label: hij hoort ín de knop, want hij gaat over
   die tab en niet over de balk. */
.tabset-dot { display: inline-block; width: 6px; height: 6px; margin-left: 0.45rem; border-radius: 50%; background: var(--accent); vertical-align: 0.1em; }
/* Twee rijen (.tabset--rows, gezet door ui/tabs.js). Rijen zijn gelijk (1fr), dus
   rij 1 eindigt op (100% - gap) / 2. Het aantal kolommen komt als --tabset-cols. */
@media (min-width: 769px) {
  .tabset--lg.tabset--rows { --tabset-row-gap: 0.6rem; display: grid; grid-template-columns: repeat(var(--tabset-cols), minmax(0, 1fr)); grid-auto-rows: 1fr; gap: var(--tabset-row-gap) 0; padding-right: 2.2rem; }
  .tabset--lg.tabset--rows .tabset-tab { padding-inline: 0.4rem; }
  .tabset--lg.tabset--rows::before { top: calc((100% - var(--tabset-row-gap)) / 2 - 12px); bottom: auto; right: auto; width: 24px; border-right: none; border-bottom-right-radius: 0; }
  .tabset--lg.tabset--rows::after {
    content: ""; position: absolute; top: calc((100% - var(--tabset-row-gap)) / 2 - 1px); bottom: 0; left: 0; right: 0;
    border: 1px solid var(--border-mid); border-radius: 11px; pointer-events: none;
    mask: linear-gradient(transparent calc(100% - 12px), #000 0) 0 0 / 12px 100% no-repeat,
          linear-gradient(#000, #000) 12px 0 / calc(100% - 12px) 100% no-repeat;
  }
}
/* ── EEN TABBALK IN DE PAGINA-KOLOM (11-09-2026) ────────────────────────────
   Staat de balk in de kolom en het paneel in de inhoud, dan is de keuze tussen
   vier tabbladen een tweede menu geworden, en dan hoort hij er ook zo uit te
   zien: verticaal, rechts uitgelijnd tegen de kantlijn, en de actieve keuze
   gemerkt met dezelfde accentstreep ÓP die lijn als in het hoofdmenu. De
   onderrand-bracket gaat uit, want die tekent een basislijn onder een rij en er
   is geen rij meer.
   De streep gebruikt exact dezelfde som als .nav a.active hierboven, en dat
   kan omdat de kolom dezelfde padding heeft als de zijbalk. */
.page-aside .tabset { flex-direction: column; gap: 0.1rem; padding: 0; margin: 0 0 0.2rem; border-bottom: none; }
.page-aside .tabset::before { display: none; }
.page-aside .tabset-tab { position: relative; text-align: right; padding: 0.5rem 0.2rem; margin: 0; border-bottom: none; border-radius: var(--radius-sm); }
.page-aside .tabset-tab.is-on::after {
  content: ""; position: absolute; z-index: 1; top: 0; bottom: 0;
  right: calc(-1 * var(--sidebar-pad) - 1px); width: 3px;
  border-radius: 2px; background: var(--accent);
}
/* ── TWEE NIVEAUS IN DE PAGINA-KOLOM (07-10-2026, eerst op de hulppagina van AssetApp) ──
   Te veel onderdelen voor één kolommenu (de hulp had er 23) krijgen twee niveaus, allebei in de
   kolom. Bovenaan de groepen als korte tabbalk: horizontaal, rechts uitgelijnd zoals alles in de
   kolom, op een dunne basislijn, en de gekozen groep onderstreept in plaats van de streep op de
   kantlijn. Daaronder de onderdelen van die groep als het gewone verticale kolommenu hierboven.
   Drie korte woorden passen in de kolombreedte; meer groepen of langere namen niet, en dan is
   dit niet de vorm. In AssetApp heet de houder .guide-groups, omdat de tabbalk uit de gedeelde
   tab-control komt en daar geen modifier meekrijgt. */
.page-aside .tabset--groups { flex-direction: row; justify-content: flex-end; gap: 1.6rem; margin: 0 0 1.6rem;
  padding-bottom: 0.15rem; border-bottom: 1px solid var(--border-mid); }
.page-aside .tabset--groups .tabset-tab { padding: 0.3rem 0.1rem; font-size: 0.85rem; }
.page-aside .tabset--groups .tabset-tab.is-on::after { top: auto; bottom: calc(-0.15rem - 2px); left: 0; right: 0;
  width: auto; height: 2px; }
/* Alleen voor het voorbeeld in dit document: een kolom zonder de vaste kantlijn van de echte, met
   een eigen lijn rechts zodat de accentstreep ergens op staat. */
.page-aside.ds-aside-demo { position: static; height: auto; margin-left: 0; width: var(--page-aside-w); max-width: 100%;
  padding: 0 var(--sidebar-pad) 0 0; overflow: visible; gap: 0; border-right: 1px solid var(--border-mid); }
.page-aside.ds-aside-demo::after { display: none; }
@media (max-width: 768px) {
  .tabset { flex-wrap: wrap; gap: 0.4rem; padding-left: 0; border-bottom: none; }
  .tabset::before { display: none; }
  .tabset-tab { margin-bottom: 0; border: 1px solid var(--border-mid); border-radius: var(--radius-full); }
  .tabset-tab.is-on { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); }
  .tabset--lg { gap: 0.45rem; padding-left: 0; }
  .tabset--lg .tabset-tab { font-size: 0.85rem; padding: 0.5rem 1.1rem; border-bottom-width: 1px; }
}

/* ═══════════════════════════════════════════════
   BADGES & TAGS
═══════════════════════════════════════════════ */

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.7rem;
  font-weight: 600;
  padding: 0.25rem 0.65rem;
  border-radius: var(--radius-full);
  letter-spacing: 0.02em;
  border: 1px solid color-mix(in srgb, currentColor 30%, transparent);
}
.badge-accent  { background: var(--accent-soft); color: var(--accent-text); }
.badge-pos     { background: var(--pos-bg); color: var(--pos); }
.badge-neg     { background: var(--neg-bg); color: var(--neg); }
.badge-neutral { background: var(--neutral-bg); color: var(--text-sec); }
.badge-outline { background: transparent; color: var(--text-sec); border: 1px solid var(--border-mid); }
/* Een verandering of rendement als pil: de tint van winst of verlies achter het
   percentage, ZONDER rand (28-09-2026, gebruiker; uit de voorbeeldpagina van het
   profiel Glas). De tinten zijn doorschijnend, dus hij leest op glas en op een
   dicht vlak. Niet voor bedragen, niet in een datatabel. */
.delta { display: inline-block; width: max-content; padding: 0.05rem 0.45rem; border-radius: var(--radius-full); font-weight: 600; font-variant-numeric: tabular-nums; }
.delta.t-pos { background: var(--pos-bg); }
.delta.t-neg { background: var(--neg-bg); }
.badge-dot::before {
  content: '';
  display: inline-block;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: currentColor;
  flex-shrink: 0;
}

/* ═══════════════════════════════════════════════
   CARDS
═══════════════════════════════════════════════ */

/* Geneste hoeken (RFC nested-border-radius): container zet --radius + --padding;
   geneste boxen herberekenen max(4px, ouder-radius − ouder-padding) via de cascade. */
.card {
  position: relative;
  background: var(--surface);
  --radius: var(--radius-lg);
  --padding: 1.8rem;
  border-radius: var(--radius);
  padding: var(--padding);
  box-shadow: var(--shadow-1);
  backdrop-filter: blur(var(--surface-blur)) saturate(var(--surface-sat));
  -webkit-backdrop-filter: blur(var(--surface-blur)) saturate(var(--surface-sat));
  transition: background 0.25s, box-shadow 0.25s;
}
/* Het randlicht: een verloop van licht naar niets over de bovenste ~40% van de
   kaart. Dit is het verschil tussen "doorzichtig" en "glas"; zonder deze laag
   ziet een halftransparante kaart er alleen maar bleek uit.
   In de vlakke stand is --edge `none` en tekent dit element dus niets. */
.card::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  background: var(--edge); pointer-events: none;
}
/* De inhoud moet boven het randlicht blijven, anders ligt er een waas over de
   tekst in plaats van erachter. */
.card > * { position: relative; }
/* Rand én schaduw in het lichte thema. Bijgesteld op 27-08-2026, nadat AssetApp
   deze stap in de praktijk al gemaakt had en Electrum Gate hem overnam.

   Wat er stond was alleen een schaduw, en een zachte schaduw op wit geeft wel
   diepte maar geen begrenzing: waar een kader ophoudt bleef een kwestie van
   kijken. De rand doet dat werk. De schaduw is daarom strakker geworden, 2 en 16
   pixels in plaats van 3 en 24, zodat de twee samen niet zwaar worden.

   Hoort bij de opgehoogde --border en --border-mid in de licht-blokken hierboven
   (0,12 en 0,18 in plaats van 0,07 en 0,11): een rand van 7 procent zwart op wit
   verdwijnt vrijwel. In het donkere thema speelt dit niet, want daar staat een
   lichte rand op een donkere ondergrond en die zie je hoe dan ook. */
[data-theme="light"] .card {
  border: 1px solid var(--border);
  box-shadow: 0 1px 2px rgba(0,0,0,0.06), 0 4px 16px rgba(0,0,0,0.05);
}
[data-theme="dark"] .card {
  border: 1px solid var(--border);
}

.card-sm { --radius: var(--radius-lg); --padding: 1.2rem; padding: 1.2rem 1.4rem; border-radius: var(--radius); }
.card-flat { background: var(--surface); border: 1px solid var(--border); --radius: var(--radius-lg); --padding: 1.4rem; border-radius: var(--radius); padding: var(--padding); }

/* nest-kinderen leiden hun hoek af van de container (pill/cirkel uitgezonderd) */
.card > .card, .card > .card-flat, .card > .table-frame,
.card-flat > .card, .card-flat > .table-frame, .r-nest {
  border-radius: max(4px, calc(var(--radius) - var(--padding)));
}
.r-host { border-radius: var(--radius); padding: var(--padding); }
/* De drie lagen van het voorbeeld in sectie 01b. Tot 27-09-2026 stonden de kleuren
   als inline style in de HTML, en dan volgt het voorbeeld geen profiel. In Glas is
   laag 1 een glasvlak en zijn de lagen erin tinten (regel 1 van het profiel). */
.ds-layer-1 { background: var(--bg-elevated); border: 1px solid var(--border); }
.ds-layer-2 { background: var(--bg-raised); border: 1px solid var(--border-mid); }
.ds-layer-3 { background: var(--bg-overlay); }
:root[data-profile="glass"] .ds-layer-1 {
  background: linear-gradient(var(--frost), var(--frost)), var(--surface);
  border-color: var(--frame-border);
  box-shadow: var(--shadow-1), inset 0 1px 0 var(--frame-top);
  backdrop-filter: blur(var(--surface-blur)) saturate(var(--surface-sat));
  -webkit-backdrop-filter: blur(var(--surface-blur)) saturate(var(--surface-sat));
}
:root[data-profile="glass"] .ds-layer-2 { background: var(--surface-tint); border-color: var(--row-line); }
:root[data-profile="glass"] .ds-layer-3 { background: var(--neutral-bg); }

.card-stat { }
.card-stat .stat-label { font-size: 0.68rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-sec); margin-bottom: 0.5rem; }
.card-stat .stat-val { font-size: 2rem; font-weight: 700; letter-spacing: -0.025em; font-variant-numeric: tabular-nums; margin-bottom: 0.25rem; }
.card-stat .stat-change { font-size: 0.78rem; font-weight: 500; color: var(--pos); }

/* card grid */
/* De tussenruimte is 1,5rem en niet 1rem (op verzoek van de gebruiker,
   31-08-2026). Een kaart heeft 1,8rem ruimte BINNENIN, en met 1rem ertussen was
   de ruimte tússen twee kaarten kleiner dan de ruimte erbinnen; dan lezen twee
   kaarten als één blok met een streep erdoor in plaats van als twee kaders.

   1,5rem en niet gewoon 1,8rem, dus nog steeds iets krapper dan de binnenruimte:
   dat is de maat waar zowel AssetApp (--dash-gap) als de Umbrel-pagina's
   onafhankelijk van elkaar op uitkwamen, en drie projecten in de pas houden weegt
   hier zwaarder dan de formule helemaal rond maken. De harde ondergrens is dat de
   tussenruimte niet véél kleiner mag zijn dan de binnenruimte; 1rem was dat wel. */
.card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 1.5rem; }

/* ── De kaart die je kiest (.card-pick) ──
   Een kaart die zelf de knop is, zoals een assetkaart: een <button>, zodat Tab
   en Enter werken. Drie toestanden naast rust: hover, aangewezen (.is-linked,
   vanuit een tabelrij of grafiek) en geselecteerd (.is-selected).
   HUIDIG is de vorm van AssetApp nu: een ring van 2px in het accent, bij de
   selectie met een zweem accent over het vlak. */
.card-pick { width: 100%; font: inherit; color: inherit; text-align: left; cursor: pointer; transition: box-shadow 0.15s ease, background 0.15s ease; }
.card-pick:hover { box-shadow: var(--hover-shadow); }
.card-pick.is-linked { box-shadow: inset 0 0 0 2px var(--accent), var(--lift-shadow); }
.card-pick.is-selected { box-shadow: inset 0 0 0 2px var(--accent), var(--lift-shadow); background: var(--accent-soft); }
/* IN LICHT GEEN RANDLICHT OP EEN AANGEWEZEN OF GESELECTEERDE KAART, in beide
   profielen (28-09-2026, gemeld door de gebruiker). Het randlicht is .card::before,
   en dat ligt als kind BOVEN de inset-schaduw van de kaart: in licht is het 70 tot
   85% wit, en dan waste het de bovenkant van de selectierand weg. In donker is het
   7 tot 10% wit en speelt dit niet. Een geselecteerde kaart heeft zijn eigen licht
   (de zweem accent in Zakelijk, --lit in Glas), dus er gaat niets verloren. */
[data-theme="light"] .card-pick:is(.is-linked, .is-selected)::before { opacity: 0; }
/* GLAS: licht in plaats van verf. Hover licht de rand duidelijk op, zonder
   kleur (kleur is voor aanwijzen en selecteren). Aangewezen kleurt de rand.
   Geselecteerd kleurt de rand sterker, laat licht van boven invallen en zet
   een zachte gloed eronder; de onderste helft blijft glas. */
/* De schaduwen van hover en selectie zijn tokens (--hover-shadow, --select-shadow)
   en de glanslijn ook (--glans, --glans-on), zie het profielblok bovenaan. Hier
   stonden de donkere waarden hard in de regel, en in licht gaf dat een schaduw van
   70% zwart onder een kaart bij hover. */
:root[data-profile="glass"] .card-pick:hover {
  --rim-top: var(--rim-hover); --rim-bottom: var(--rim-hover-bottom); --hairline: var(--hairline-hover);
  --lift: linear-gradient(var(--hover-fill), var(--hover-fill));
  box-shadow: var(--shadow-1), var(--glans), var(--hover-shadow);
}
:root[data-profile="glass"] .card-pick.is-linked {
  --rim-top: color-mix(in srgb, var(--accent) 65%, transparent);
  --rim-bottom: color-mix(in srgb, var(--accent) 18%, transparent);
  --hairline: var(--accent-text);
  --glans: var(--glans-on);
  box-shadow: var(--shadow-1), var(--glans);
}
:root[data-profile="glass"] .card-pick.is-selected {
  --rim-top: color-mix(in srgb, var(--accent) 85%, transparent);
  --rim-bottom: color-mix(in srgb, var(--accent) 30%, transparent);
  --hairline: var(--accent-text);
  --glans: var(--glans-on);
  --lit: radial-gradient(120% 85% at 50% -12%, color-mix(in srgb, var(--accent) 17%, transparent), transparent 68%);
  box-shadow: var(--shadow-1), var(--glans), var(--select-shadow);
}

/* ── Aandacht (.is-attention) ──
   Een lichtpunt in het accent dat rond de rand loopt, voor iets op het
   dashboard dat tijdelijk extra aandacht vraagt. NIET voor een selectie: die
   blijft staan terwijl je leest, en een rand die blijft draaien trekt de blik
   steeds terug. Standaard drie rondes; blijvend kan, voor iets dat openstaat
   tot je het afhandelt. In beide profielen, want het is een functie en geen
   versiering.
   Markup: <span class="attn" aria-hidden="true"><span class="attn-spin"></span></span>
   als eerste kind van de kaart, en --attn-d op de kaart = de diagonaal (script).
   Het draaien is een transform op een vierkant met een kegelverloop achter een
   ringmasker: dat doet de compositor alleen. De hoek van het verloop zelf
   animeren (via @property) tekent elk beeldje opnieuw. */
.attn {
  position: absolute; inset: -1px; border-radius: inherit; padding: 1.5px; pointer-events: none;
  overflow: hidden; opacity: 0; transition: opacity 0.8s;
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
}
.card > .attn { position: absolute; }
.attn-spin {
  position: absolute; left: 50%; top: 50%;
  width: var(--attn-d, 800px); height: var(--attn-d, 800px);
  margin: calc(var(--attn-d, 800px) / -2) 0 0 calc(var(--attn-d, 800px) / -2);
  background: conic-gradient(from 0deg, transparent 0 52%, color-mix(in srgb, var(--accent) 45%, transparent) 70%, var(--accent-text) 82%, transparent 94%);
  animation: attn-spin 3.5s linear infinite; animation-play-state: paused;
}
.is-attention > .attn { opacity: 1; }
.is-attention > .attn .attn-spin { animation-play-state: running; will-change: transform; }
.is-attention { box-shadow: var(--shadow-1), 0 0 0 1px color-mix(in srgb, var(--accent) 25%, transparent), 0 16px 48px -20px var(--accent-glow); }
@keyframes attn-spin { to { transform: rotate(1turn); } }
/* Zonder beweging blijft de aandacht een stilstaande accentrand */
@media (prefers-reduced-motion: reduce) {
  html:not([data-force-motion]) .attn-spin { animation: none; background: var(--accent); }
}

/* ═══════════════════════════════════════════════
   FORMS / INPUTS
═══════════════════════════════════════════════ */

.form-group { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1.2rem; }
.form-label { font-size: 0.78rem; font-weight: 600; color: var(--text-primary); }
.form-hint  { font-size: 0.72rem; color: var(--text-sec); margin-top: 0.3rem; }
.form-error { font-size: 0.72rem; color: var(--neg); margin-top: 0.3rem; }

.input {
  background: var(--input-bg);
  border: 1px solid var(--border-mid);
  border-radius: var(--radius-md);
  padding: 0.65rem 0.9rem;
  font-family: var(--font-base);
  font-size: 0.88rem;
  color: var(--text-primary);
  outline: none;
  transition: border-color 0.15s, box-shadow 0.15s;
  width: 100%;
}
.input::placeholder { color: var(--text-ter); }
.input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.input.error  { border-color: var(--neg); }
.input.error:focus { box-shadow: 0 0 0 3px var(--neg-bg); }
.input-prefix { position: relative; }
.input-prefix .prefix {
  position: absolute; left: 0.9rem; top: 50%; transform: translateY(-50%);
  color: var(--text-sec); font-size: 0.85rem; pointer-events: none;
}
.input-prefix .input { padding-left: 2.2rem; }

select.input { appearance: none; cursor: pointer; }

.toggle-switch {
  position: relative;
  width: 40px; height: 22px;
  flex-shrink: 0;
}
.toggle-switch input { opacity: 0; width: 0; height: 0; position: absolute; }
.toggle-track {
  position: absolute; inset: 0;
  background: var(--bg-overlay);
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: background 0.2s;
  border: 1px solid var(--border-mid);
}
.toggle-switch input:checked ~ .toggle-track { background: var(--accent); border-color: var(--accent); }
.toggle-track::after {
  content: '';
  position: absolute;
  left: 2px; top: 2px;
  width: 16px; height: 16px;
  border-radius: 50%;
  background: #fff;
  transition: transform 0.2s var(--ease-out);
}
.toggle-switch input:checked ~ .toggle-track::after { transform: translateX(18px); }

.checkbox-row { display: flex; align-items: center; gap: 0.7rem; cursor: pointer; }
.checkbox-box {
  width: 18px; height: 18px;
  border: 1.5px solid var(--border-mid);
  border-radius: 5px;
  background: var(--input-bg);
  display: grid; place-items: center;
  flex-shrink: 0;
  transition: all 0.15s;
}
.checkbox-box.checked { background: var(--accent); border-color: var(--accent); }
.checkbox-box.checked::after { content: '✓'; font-size: 0.65rem; color: #fff; font-weight: 700; }
[data-theme="dark"] .checkbox-box.checked::after { color: #000; }
.checkbox-label { font-size: 0.85rem; }

/* ── De schuif (.slider) ──
   Nieuw op 27-09-2026: de apps hadden er geen. Eén opbouw voor beide profielen;
   alleen de tokens verschillen (zie het profielblok bovenaan). Zakelijk is dicht
   en eenvoudig, Glas een ingelaten spoor met een glazen kraal als knop.
   TWEE DINGEN DIE JE MOET WETEN:
   1. De vulling links van de knop kan CSS in Chromium en Safari niet zelf
      tekenen. Het spoor krijgt daarom een tweede laag met de breedte --pct, en
      die zet het script bij elke input. Firefox kan het wel, met
      ::-moz-range-progress.
   2. Elke regel staat er twee keer, voor -webkit- en voor -moz-. Een selector
      met allebei is ongeldig en valt dan in beide browsers weg. */
.slider {
  -webkit-appearance: none; appearance: none;
  width: 100%; height: 22px; margin: 0; background: transparent; cursor: pointer;
  --pct: 50%;
}
.slider::-webkit-slider-runnable-track {
  height: 6px; border-radius: var(--radius-full);
  background: var(--slider-fill) 0 0 / var(--pct) 100% no-repeat, var(--slider-track);
  box-shadow: var(--slider-track-edge);
}
.slider::-moz-range-track { height: 6px; border-radius: var(--radius-full); background: var(--slider-track); box-shadow: var(--slider-track-edge); }
.slider::-moz-range-progress { height: 6px; border-radius: var(--radius-full); background: var(--slider-fill); }
.slider::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 18px; height: 18px; margin-top: -6px; border: 0; border-radius: 50%;
  background: var(--slider-thumb); box-shadow: var(--slider-thumb-edge);
  transition: transform 0.15s var(--ease-out), box-shadow 0.15s;
}
.slider::-moz-range-thumb {
  width: 18px; height: 18px; border: 0; border-radius: 50%;
  background: var(--slider-thumb); box-shadow: var(--slider-thumb-edge);
  transition: transform 0.15s var(--ease-out), box-shadow 0.15s;
}
.slider:hover::-webkit-slider-thumb { transform: scale(1.1); }
.slider:hover::-moz-range-thumb { transform: scale(1.1); }
.slider:active::-webkit-slider-thumb { transform: scale(1.18); box-shadow: var(--slider-thumb-edge), 0 0 0 6px var(--accent-soft); }
.slider:active::-moz-range-thumb { transform: scale(1.18); box-shadow: var(--slider-thumb-edge), 0 0 0 6px var(--accent-soft); }
.slider:focus-visible { outline: none; }
.slider:focus-visible::-webkit-slider-thumb { box-shadow: var(--slider-thumb-edge), 0 0 0 3px var(--bg), 0 0 0 5px var(--accent); }
.slider:focus-visible::-moz-range-thumb { box-shadow: var(--slider-thumb-edge), 0 0 0 3px var(--bg), 0 0 0 5px var(--accent); }
/* Schuif met label links en waarde rechts, voor een instellingenrij */
.slider-row { display: grid; grid-template-columns: 5rem minmax(0, 1fr) 3.2rem; align-items: center; gap: 0.75rem; font-size: 0.8rem; color: var(--text-sec); }
.slider-row output { text-align: right; font-variant-numeric: tabular-nums; color: var(--text-primary); }
.slider-row--wide { grid-template-columns: 10rem minmax(0, 1fr) 3.2rem; }

/* ═══════════════════════════════════════════════
   TABLES
═══════════════════════════════════════════════ */

/* De markup komt uit de gedeelde tabel (HomeGit/Shared/client/ui/table.js), en
   de opmaak hieronder is die van AssetApp (client/css/styles.css, gelijkgetrokken
   op 19-10-2026). Een app die de gedeelde tabel gebruikt, hoeft deze klassen niet
   zelf te definiëren. De opbouw:

     .table-frame                    het kader: rand, hoek, vlak, schaduw, diepte
       .table-controls-bar           de knoppen, buiten het scrollende deel
         .table-controls
       .table-wrap                   de scrollende binnenkant
         table > thead + tbody

   .table-frame is het buitenste kader en .table-wrap de scrollende binnenkant;
   de diepte hoort dus op het kader en niet op .table-wrap. Dat is meteen het
   goedkope geval: het kader scrollt zelf niet, dus de backdrop-filter wordt niet
   per scrollframe opnieuw samengesteld.

   Het randlicht gaat als BACKGROUND-LAAG en niet als ::before, want overflow:hidden
   zou een absoluut geplaatst pseudo-element hier bijten. `none` is een geldige
   background-image, dus deze ene regel dekt beide standen.

   HAPERT EEN GROTE TABEL, zet dan --surface-blur op 0 voor dat kader
   (.table-frame--plain) in plaats van de stand voor de hele app uit te zetten: het
   kader houdt zijn vlak, randlicht en schaduw. */
.table-frame {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--edge), var(--surface);
  box-shadow: var(--shadow-1);
  backdrop-filter: blur(var(--surface-blur)) saturate(var(--surface-sat));
  -webkit-backdrop-filter: blur(var(--surface-blur)) saturate(var(--surface-sat));
  transition: background 0.25s, box-shadow 0.25s;
}
.table-frame--plain { --surface-blur: 0px; }
[data-theme="light"] .table-frame { box-shadow: 0 1px 3px rgba(0,0,0,0.04); }
.table-wrap { width: 100%; max-height: calc(100vh - var(--table-vh-offset)); overflow: auto; }
/* De verticale scrollbalk begint ONDER de kolomnamen: de kop scrollt niet, dus het
   spoor hoort er niet naast. Alleen ::-webkit-scrollbar-track neemt een marge, en
   Chromium negeert die pseudo-elementen zodra scrollbar-color geldt (geërfd van
   html); vandaar de reset. Firefox kent de selector niet en houdt een spoor over
   de volle hoogte.
   De kop loopt wel door tot het kader: de balk zelf draagt tot --sticky-row-top de
   vulling en lijn van de kop, en bestaat alleen als de tabel scrollt.
   --sticky-row-top is de hoogte van thead, per tekening gezet door table.js;
   --head-fill en --head-line volgen de regels van thead th. */
@supports selector(::-webkit-scrollbar) {
  .table-wrap { --head-fill: var(--bg-elevated); --head-line: var(--border-mid); scrollbar-color: auto; scrollbar-width: auto; }
  .table-wrap::-webkit-scrollbar { width: 8px; height: 8px; }
  .table-wrap::-webkit-scrollbar:vertical { background: linear-gradient(var(--head-fill) calc(var(--sticky-row-top, 0px) - 1px), var(--head-line) 0 var(--sticky-row-top, 0px), transparent 0); }
  .table-wrap::-webkit-scrollbar-thumb { background: var(--scrollbar); border-radius: var(--radius-full); }
  .table-wrap::-webkit-scrollbar-track, .table-wrap::-webkit-scrollbar-corner { background: transparent; }
  .table-wrap::-webkit-scrollbar-track:vertical { margin-top: var(--sticky-row-top, 0px); }
}

table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}

/* header: sticky bovenin de scrollende binnenkant. De lijn onder de kop is een
   inset-schaduw en geen rand, want een rand op een sticky kop scrollt mee weg. */
thead { position: sticky; top: 0; z-index: 3; }
thead tr:last-child > th { box-shadow: inset 0 -1px 0 var(--border-mid); }
th {
  padding: 0.75rem 1.1rem;
  text-align: left;
  font-size: 0.67rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--text-sec);
  white-space: nowrap;
  background: var(--bg-elevated);
  transition: background 0.25s, color 0.25s;
}

/* right-align numeric columns */
th.num, td.num { text-align: right; }
th.center, td.center { text-align: center; }
/* De laatste kolom lijnt rechts uit, in elke tabel: hij sluit de rij af tegen het
   kader. De actiekolom en een cel over de hele rij (groepskop, lade) tellen niet
   als laatste. */
.table-wrap :is(th, td):nth-last-child(1 of :not(.actions-cell, [colspan])) { text-align: right; }

/* rows. De rijlijn staat op de halve rand (26-09-2026, op verzoek van de gebruiker):
   tussen elke rij was --border te nadrukkelijk. */
tbody tr {
  position: relative;
  border-bottom: 1px solid color-mix(in srgb, var(--border) 50%, transparent);
  transition: background 0.1s;
}
tbody tr:last-child { border-bottom: none; }
tbody tr:hover { background: var(--neutral-bg); }
tbody tr[data-row-id] { cursor: pointer; }
/* De geselecteerde rij (.row-selected), in Zakelijk zoals AssetApp nu: 16% accent
   en een streep van 3px links. In Glas is een rij geen eigen vlak maar een strook
   in een vlak, dus hij krijgt licht en geen rand rondom. Twee vormen, via
   data-rowsel op <html> (plan Vormgeving, open punt 6):
   - "light" (voorstel): het licht valt van rechtsboven naar linksonder in, 12
     graden uit het lood, zoals de banen van de gloed (gebruiker, 26-10-2026);
   - "tint": lichter glas over de hele rij en een accentstreep van 3px, zonder
     accentvulling (gebruiker, 26-10-2026). Hover maakt de rij zelf lichter en niet
     de cellen. Een vastgezette rij (.sticky-sel) hoort dezelfde --row-sel te tonen.
   Het verloop staat op de RIJ en niet op de cel, anders begint het per cel
   opnieuw. De derde vorm, de pil, staat hieronder. */
tbody tr.row-selected td { background: var(--accent-soft); }
tbody tr.row-selected:hover td { background: var(--accent-mid); }
tbody tr.row-selected td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
:root[data-profile="glass"] tbody tr.row-selected td { background: transparent; }
:root[data-profile="glass"] tbody tr.row-selected:hover td { background: var(--row-hover); }
:root[data-profile="glass"] tbody tr.row-selected td:first-child { box-shadow: inset 2px 0 0 var(--accent), inset 14px 0 18px -14px var(--accent-glow); }
:root[data-profile="glass"] tbody tr.row-selected {
  background: linear-gradient(192deg, color-mix(in srgb, var(--accent) 20%, transparent), color-mix(in srgb, var(--accent) 5%, transparent) 45%, transparent 85%);
}
:root[data-profile="glass"][data-rowsel="tint"] tbody tr.row-selected { background: var(--row-sel); }
:root[data-profile="glass"][data-rowsel="tint"] tbody tr.row-selected:hover { background: var(--row-sel-hover); }
:root[data-profile="glass"][data-rowsel="tint"] tbody tr.row-selected:hover td { background: transparent; }
:root[data-profile="glass"][data-rowsel="tint"] tbody tr.row-selected td:first-child { box-shadow: inset 3px 0 0 var(--accent); }
/* - "pill": de rij wordt een afgeronde strook met een accentrand bovenin. Het
     meest glasachtig, maar hij lijkt op een knop. Afronden kan alleen een CEL,
     en alleen in border-collapse: separate; daar tekent een rand op tbody tr
     niet. In deze stand verhuizen de rijlijnen daarom naar de cellen. */
:root[data-profile="glass"][data-rowsel="pill"] table { border-collapse: separate; border-spacing: 0; }
:root[data-profile="glass"][data-rowsel="pill"] th { border-bottom: 1px solid var(--border-mid); }
:root[data-profile="glass"][data-rowsel="pill"] tbody td { border-bottom: 1px solid var(--row-line); }
:root[data-profile="glass"][data-rowsel="pill"] tbody tr:last-child > td,
:root[data-profile="glass"][data-rowsel="pill"] tbody tr.row-selected > td,
:root[data-profile="glass"][data-rowsel="pill"] tbody tr:has(+ tr.row-selected) > td { border-bottom-color: transparent; }
:root[data-profile="glass"][data-rowsel="pill"] tbody tr.row-selected { background: none; }
:root[data-profile="glass"][data-rowsel="pill"] tbody tr.row-selected td {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--accent) 55%, transparent), inset 0 -1px 0 color-mix(in srgb, var(--accent) 18%, transparent);
}
:root[data-profile="glass"][data-rowsel="pill"] tbody tr.row-selected td:first-child {
  border-radius: 10px 0 0 10px;
  box-shadow: inset 1px 1px 0 color-mix(in srgb, var(--accent) 55%, transparent), inset 0 -1px 0 color-mix(in srgb, var(--accent) 18%, transparent);
}
/* De laatste DATAcel: de actiecel van de gedeelde tabel heeft geen breedte. */
:root[data-profile="glass"][data-rowsel="pill"] tbody tr.row-selected td:nth-last-child(1 of :not(.actions-cell)) {
  border-radius: 0 10px 10px 0;
  box-shadow: inset -1px 1px 0 color-mix(in srgb, var(--accent) 55%, transparent), inset 0 -1px 0 color-mix(in srgb, var(--accent) 18%, transparent);
}

td {
  padding: var(--table-cell-py) 1.1rem;
  vertical-align: middle;
  color: var(--text-primary);
  white-space: nowrap;
  transition: color 0.25s, background 0.25s;
}

/* ── Alleen TEKSTkolommen mogen afbreken ──
   td en th staan in de basisregels op nowrap, dus deze ene regel is de hele
   wrap-policy. Een kolom vraagt het afbreken aan met `col.text` in table.js; dat is
   een uitspraak over de INHOUD (vrije tekst of een lange code) en niet over de CSS.
   overflow-wrap:anywhere verkleint ook de minimale kolombreedte, en dat laat een
   brede tabel binnen de viewport krimpen. min-width houdt de tekstkolom leesbaar:
   zonder ondergrens is hij de enige kolom die kan krimpen en vangt hij alle druk
   op. Daarna scrollt de tabel horizontaal binnen .table-wrap. */
.table-wrap td.text, .table-wrap th.text { white-space: normal; overflow-wrap: anywhere; min-width: 7rem; }

/* ── Gelijke rijhoogte per tabel ──
   Elke rij is exact even hoog, en dat kan alleen als twee dingen tegelijk gelden:
   geen rij mag kleiner worden dan het budget, en geen cel mag er buiten groeien.
   Vandaar deze twee regels samen; los van elkaar doen ze niets. Een tabelrij groeit
   naar zijn hoogste cel, dus een tekstkolom die afbreekt wordt hier op het budget
   afgekapt. --table-row-lines is de knop.
   De prijs: een tabel met alleen eenregelige inhoud krijgt ook rijen van twee
   regels, en een notitie die langer is wordt afgekapt. In de gestapelde modus
   vervalt beide (zie de media-query onderaan). */
.table-wrap td { height: calc(var(--table-row-lines) * var(--table-line-h) + 2 * var(--table-cell-py)); }
.table-wrap td.text > .cell-clamp {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: var(--table-row-lines); overflow: hidden;
}

/* Controls-balk: binnen het tabelkader, maar buiten het scrollende deel; zo oogt het
   als één tabel terwijl alleen de kolommen en de data scrollen. */
.table-controls-bar { padding: 0.6rem 0.7rem; background: var(--bg-elevated); }
.table-controls { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; --tctl-h: 2.5rem; }
.table-controls .input { height: var(--tctl-h); }
.tbl-search { width: auto; min-width: 150px; max-width: 240px; }
/* Twee klassen diep, zodat ze winnen van .input, .form-group, .form-error en .flex
   waar die ook staan. De gedeelde tabel is CSP-strikt en schrijft geen style="". */
.table-controls .tbl-group-select { width: auto; min-width: 140px; }
.row-editing .tbl-textarea, .edit-expand-row .tbl-textarea { resize: vertical; width: 100%; }
.edit-expand-row .edit-expand-fields { flex-wrap: wrap; align-items: flex-end; }
.edit-expand-row .edit-expand-field { margin-bottom: 0; min-width: 200px; flex: 1; }
.edit-expand-row .edit-expand-field.is-full { min-width: auto; flex: 1 0 100%; }
.edit-expand-row .edit-expand-error { margin-top: 0.6rem; }
/* Opslaan en annuleren onder de velden, links (26-10-2026); de rij groeit ervoor. */
.edit-expand-row .edit-actions { justify-content: flex-start; }
.edit-expand-row :is(.edit-expand-fields, .edit-expand-error) + .edit-actions { margin-top: 0.8rem; }
.table-controls label { font-size: 0.72rem; font-weight: 600; color: var(--text-sec); }
/* De plus van de tabel: even hoog als de andere controls. Een vergrendelde plus
   blijft een plus (zichtbaar en op slot, nooit weg). */
.btn-new { width: var(--tctl-h); height: var(--tctl-h); padding: 0; align-self: center; display: inline-flex; align-items: center; justify-content: center; border-radius: var(--radius-md); color: var(--accent); background: var(--neutral-bg); border: 1px solid var(--border); transition: color 0.15s, background 0.15s, border-color 0.15s; }
.btn-new:hover { background: var(--accent); border-color: var(--accent); color: #fff; }
[data-theme="dark"] .btn-new:hover { color: #000; }
.btn-new.is-locked { opacity: 0.55; }
.btn-new.is-locked:hover { opacity: 0.8; }
.tbl-note { align-self: center; font-size: 0.78rem; color: var(--text-ter); }
/* Slot voor de groepsfilter in de controls-balk; hier alleen de plaatsing. */
.tbl-filter { justify-content: center; flex: 1 1 auto; min-width: 0; }
.tbl-empty-spacer td { height: 18px; padding: 0; }
/* Niet-klikbaar telkader rechts in de balk: het totaal aantal records */
.tbl-count { flex: 0 0 auto; margin-left: auto; display: inline-flex; align-items: center; justify-content: center; height: var(--tctl-h); min-width: 2.6rem; padding: 0 0.7rem; border-radius: var(--radius-md); border: 1px solid var(--border-mid); background: var(--bg-elevated); color: var(--text-sec); font-size: 0.8rem; font-weight: 600; font-variant-numeric: tabular-nums; }
th.th-sort { cursor: pointer; user-select: none; }
th.th-sort:hover { color: var(--text-primary); }
.sort-arrow { font-size: 0.55rem; margin-left: 0.3rem; opacity: 0.7; }

/* Groepskoppen */
.group-row > td { background: var(--bg-elevated); padding: 0.5rem 1.1rem; box-shadow: inset 0 1px 0 var(--border), inset 0 -1px 0 var(--border); }
.group-header { display: flex; align-items: center; gap: 0.6rem; font-size: 0.86rem; font-weight: 700; color: var(--accent); margin: 0; }
.group-header .group-count { color: var(--text-sec); font-weight: 500; background: var(--neutral-bg); padding: 0.05rem 0.5rem; border-radius: var(--radius-full); font-size: 0.72rem; }
.group-row td { padding: 0; border: none; background: transparent; }
.group-row:hover td { background: transparent; }
.group-row .group-header { margin: 1.2rem 1.1rem 0.4rem; }
/* In een tabel MET uitklap-indicator schuift de groepskop naar links, tot op de
   x-positie van de chevron van de rijen eronder. Zonder indicator lijnt de kop uit
   met de tekst van de eerste kolom (1.1rem = de celpadding). */
.table-wrap.has-exp .group-row .group-header { margin-left: var(--table-exp-pad); }
/* Inklapbare groepen (opt-in, cfg.collapsibleGroups): kop-chevron is de toggle,
   samenvatting bij ingeklapt. */
.group-header[data-gcollapse] { cursor: pointer; user-select: none; }
.group-header .chev-btn { flex: 0 0 auto; }
.group-header .group-summary { color: var(--text-sec); font-weight: 500; font-variant-numeric: tabular-nums; font-size: 0.78rem; margin-left: 0.2rem; }

/* Actiekolom: VASTE BREEDTE en geen width:1%. De cel is leeg op elke rij behalve de
   geselecteerde, en met 1% bepaalde de inhoud van die ene rij de kolombreedte:
   selecteren verschoof dan de hele laatste kolom. De maat is die van twee
   icoon-knoppen (btn-xs) plus de celpadding; een extra tekstknop rekt hem bij
   selectie alsnog op. */
.actions-cell { text-align: right; width: var(--table-actions-w); white-space: nowrap; }
.row-actions { display: inline-flex; gap: 0.4rem; justify-content: flex-end; }
/* ── De knoppen van de geselecteerde rij zweven midden op de rij (26-10-2026) ──
   In de gedeelde tabel (.table-frame) heeft de actiekolom geen breedte, zodat de
   laatste kolom tegen de rechterrand staat. De balk zweeft gecentreerd over de
   geselecteerde rij; de cellen eronder vallen weg, en dat is een keuze. Opslaan en
   annuleren van de bewerkrij staan in zijn lade, links onder de velden
   (.edit-actions). Alleen in de tabelweergave: onder 1201px is de cel een eigen
   regel.
   De cel plakt aan de rechterrand van het scrollende deel en de balk staat de helft
   van de zichtbare breedte (cqw van .table-wrap) links daarvan: het midden van wat je
   ziet, ook als de tabel opzij scrolt. Een backdrop-filter op de cel (vastgezette
   rij) zou de achtergrond van de balk worden; dan vervaagt de balk niets. */
@media (min-width: 1201px) {
  .table-frame > .table-wrap:has(> table > thead .actions-cell) { container-type: inline-size; }
  .table-frame > .table-wrap :is(th, td).actions-cell { width: 0; padding: 0; }
  .table-frame > .table-wrap tr.row-selected > td.actions-cell { position: sticky; right: 0; z-index: 2; }
  .table-frame > .table-wrap tr.row-selected > td.actions-cell > .row-actions {
    position: absolute; top: 50%; right: 50cqw; transform: translate(50%, -50%);
    padding: 0.3rem; border: 1px solid color-mix(in srgb, var(--accent) 70%, transparent); border-radius: var(--radius-md);
    background: var(--bg-raised); box-shadow: 0 6px 18px rgba(0,0,0,0.35);
  }
  .table-frame > .table-wrap tr.row-selected > td.actions-cell > .row-actions:empty { display: none; }
  :root[data-profile="glass"] .table-frame > .table-wrap tr.row-selected > td.actions-cell > .row-actions {
    background: var(--float-bg);
    backdrop-filter: blur(var(--float-blur)); -webkit-backdrop-filter: blur(var(--float-blur));
  }
  :root[data-profile="glass"] .table-frame > .table-wrap tbody tr.row-selected > td.actions-cell { backdrop-filter: none; -webkit-backdrop-filter: none; }
}

/* Uitklap-indicator voor de eerste kolom (alleen in een tabel met rowExpansion).
   Vaste breedte, om dezelfde reden als de actiekolom. Links uitgelijnd en niet
   gecentreerd, met een variabele als x-positie: de chevron van de groepskop en die
   van de rij staan zo recht onder elkaar. De chevron is grijs en wordt accent zodra
   de lade open staat. */
.exp-cell { width: var(--table-exp-w); text-align: left; padding-left: var(--table-exp-pad); padding-right: 0; }
.exp-cell .chev-btn { vertical-align: middle; }
tbody tr[data-row-id]:hover .exp-cell .chev-btn { color: var(--accent); }

/* De chevron-knop: grijs, draait een halve slag als hij open staat. */
.chev-btn { display: inline-flex; align-items: center; justify-content: center; padding: 0; border: none; background: none; color: var(--text-ter); cursor: pointer; transition: opacity 0.15s, color 0.15s, transform 0.15s; transform-origin: center; }
.chev-btn svg { display: block; }
.chev-btn:hover { color: var(--accent); }
.chev-btn.is-open { transform: rotate(180deg); }
.chev-btn.is-accent { color: var(--accent); }   /* permanent accent */

/* ── Uitklap-lade: tr.row-expansion > td > .src-drawer ──
   De gedeelde tabel schrijft de rij, de pagina vult de lade (cfg.rowExpansion). */
.row-expansion > td { padding: 0; background: var(--bg-raised); box-shadow: inset 3px 0 0 var(--accent); transition: background 0.25s; }
.row-expansion:hover > td { background: var(--bg-raised); }
.src-drawer { padding: 1rem 1.2rem 1.1rem; }
.src-drawer-title { margin-bottom: 0.7rem; }
/* Geneste tabel in een lade: de binnenkant scrollt niet zelf en de kop is niet
   sticky; de lade groeit mee. */
.row-expansion .table-wrap { border: none; border-radius: 0; box-shadow: none; overflow: visible; max-height: none; }
.row-expansion thead { position: static; }
.row-expansion .table-controls { margin-bottom: 0.6rem; }
.row-expansion th { background: transparent; }
.row-expansion tbody tr:last-child { border-bottom: none; }

/* Geselecteerde rij sticky onder de kop houden terwijl een grote lade eronder
   scrollt (alleen tabellen met uitklap; --sticky-row-top = thead-hoogte, gezet door
   table.js). De achtergrond moet ONDOORZICHTIG zijn, anders schemeren de
   scrollende rijen erdoorheen; vandaar een color-mix met --bg-elevated en niet het
   doorschijnende --accent-soft. 20% is een keuze en geen benadering: het anker van
   een openstaande lade mag opvallen, en het getal doet in elk thema hetzelfde. */
.table-wrap.sticky-sel tbody tr.row-selected > td { position: sticky; top: var(--sticky-row-top, 2.7rem); z-index: 2; background: color-mix(in srgb, var(--accent) 20%, var(--bg-elevated)); }
/* De sticky-regel wint op specificiteit van tr.row-selected:hover td; zonder deze
   regel gebeurde er bij hover niets. */
.table-wrap.sticky-sel tbody tr.row-selected:hover > td { background: color-mix(in srgb, var(--accent) 30%, var(--bg-elevated)); }
/* Glas, "tint": vastgezet hetzelfde lichte glas als los. De vulling staat op de
   cellen (een sticky rij plakt per cel), de rij zelf blijft leeg, anders telt hij
   dubbel. */
:root[data-profile="glass"][data-rowsel="tint"] .table-wrap.sticky-sel tbody tr.row-selected { background: none; }
:root[data-profile="glass"][data-rowsel="tint"] .table-wrap.sticky-sel tbody tr.row-selected > td { background: var(--row-sel); }
:root[data-profile="glass"][data-rowsel="tint"] .table-wrap.sticky-sel tbody tr.row-selected:hover > td { background: var(--row-sel-hover); }

/* Het keuzemenu achter de plus van de tabel (table.js newOptions), verankerd aan
   de knop. */
.new-menu { position: fixed; z-index: 1000; min-width: 190px; max-width: 92vw; background: var(--bg-elevated); border: 1px solid var(--border-mid); border-radius: var(--radius-md); box-shadow: var(--shadow-2); padding: 0.4rem; display: flex; flex-direction: column; gap: 0.2rem; }
[data-theme="dark"] .new-menu { background: var(--bg-raised); }
.new-menu .btn { margin: 0; padding: 0.55rem 0.9rem; border-radius: var(--radius-sm); transition: color 0.15s, background 0.15s, border-color 0.15s; }
.new-menu .btn:hover { background: var(--accent); border-color: var(--accent); color: #fff; }
[data-theme="dark"] .new-menu .btn:hover { color: #000; }
.new-menu .new-menu-item { display: block; width: 100%; text-align: left; white-space: nowrap; }

/* Geen native spinknoppen in de bewerkvelden van een tabel: ze kosten ruimte en
   op die plek kan een eigen knop komen. */
.table-wrap input[type="number"] { appearance: textfield; -moz-appearance: textfield; }
.table-wrap input[type="number"]::-webkit-outer-spin-button,
.table-wrap input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* ── Responsive: een kaart per rij onder 1200px ──
   Onder de breekgrens klappen alle tabellen van table.js (en hun geneste tabellen,
   zelfde .table-wrap-structuur) om naar blokken: elke datarij wordt een item, elke
   datacel een "label: waarde"-regel (label uit data-label). De koprij vervalt. Zo
   passen alle kolommen altijd binnen de breedte, zonder horizontale scrollbalk.
   WAAROM 1200: dat ligt net onder de laptops (die houden hun tabel, met wat
   scrollen) en net boven tablet-formaat (dat stapelt). Gemeten op de breedste tabel
   van AssetApp (dertien kolommen): bij 768 zou de helft buiten beeld staan.
   Het getal staat als literal, want een media-query kan geen var() lezen. */
@media (max-width: 1200px) {
  /* Geen eigen scroll-viewport meer: de pagina scrollt, de tabel groeit mee. */
  .table-wrap { max-height: none; overflow: visible; }
  .table-wrap table, .table-wrap tbody, .table-wrap tr, .table-wrap td { display: block; width: auto; }
  .table-wrap thead { display: none; }                       /* kolomkoppen komen per cel via data-label */
  .table-wrap tbody tr { border: none; }

  /* Item = geen kaartje of ronding; een bovenlijn markeert de start van elk item.
     Ruimte eronder scheidt de items en geeft de actieknoppen lucht. */
  .table-wrap tbody tr[data-row-id] {
    border: none; border-top: 1px solid var(--border-mid); background: transparent; margin: 0 0 0.4rem;
  }
  .table-wrap tbody tr[data-row-id]:hover { background: transparent; }
  /* Om en om een tint, zodat je ziet welke regels bij één item horen. De klasse
     .row-alt komt uit table.js en niet uit :nth-child(even): tussen de datarijen
     staan groepskoppen, laden en de bewerkrij, en die tellen in die selector mee.
     De tint mengt met de tekstkleur en niet met een oppervlaktekleur: die
     contrasteert per definitie met de achtergrond, dus dezelfde regel werkt in
     beide thema's. Na de hover-regel, zodat de tint bij hover blijft. */
  .table-wrap tbody tr[data-row-id].row-alt,
  .table-wrap tbody tr[data-row-id].row-alt:hover { background: color-mix(in srgb, var(--text-primary) 6%, transparent); }
  /* Master plakt aan zijn eigen lade; de ruimte komt onder de lade. */
  .table-wrap tbody tr[data-row-id]:has(+ tr.row-expansion) { margin-bottom: 0; }
  .table-wrap tbody tr.row-expansion { margin: 0 0 1.1rem; }
  /* Item direct onder een groepskop: geen eigen bovenlijn (anders een dubbele lijn). */
  .table-wrap tbody tr.group-row + tr[data-row-id] { border-top: none; }
  /* Filterchips in de controls-balk links uitlijnen op smal. */
  .table-controls .tbl-filter { justify-content: flex-start; }
  /* Geselecteerd item: een accentlijn links over de volle hoogte, zoals de lade.
     Een inset-schaduw op elke cel vormt samen een doorlopende lijn. */
  .table-wrap tbody tr.row-selected { box-shadow: none; }
  .table-wrap tbody tr.row-selected > td { background: transparent; box-shadow: inset 3px 0 0 var(--accent); }

  /* Datacel = label links (zwevend), waarde rechts. Float en geen flex, zodat een
     waarde uit meerdere inline-delen als één geheel rechts blijft staan. Afbreken is
     hier de standaard: elke cel is zijn eigen regel, dus er is geen kolombreedte die
     van een andere cel afhangt, en een lange code mag niet buiten beeld lopen. */
  .table-wrap td[data-label] {
    display: flow-root; text-align: right; white-space: normal; overflow-wrap: anywhere;
    padding: 0.5rem 0.9rem; border: none; border-top: 1px solid var(--border);
  }
  /* Geen vaste rijhoogte en geen afkapping in deze modus: elke cel is zijn eigen
     regel, dus een notitie staat hier volledig. */
  .table-wrap td { height: auto; }
  .table-wrap td.text > .cell-clamp { display: block; -webkit-line-clamp: none; overflow: visible; }
  /* De item-bovenlijn volstaat voor de eerste sectie. De tweede selector is voor een
     tabel met uitklap-indicator: die cel staat voor de eerste datacel en is hier
     verborgen. */
  .table-wrap tr > td[data-label]:first-child,
  .table-wrap tr > td.exp-cell + td[data-label] { border-top: none; }
  .table-wrap td[data-label]::before {
    content: attr(data-label); float: left; margin-right: 1rem; text-align: left; line-height: 1.5;
    font-size: 0.66rem; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-sec);
  }
  /* Structureel lege cellen verdwijnen, maar de actiecel niet: die reserveert ruimte. */
  .table-wrap td:not([data-label]):not(.actions-cell):empty { display: none; }
  /* De uitklap-indicator verdwijnt: een eigen blokregel met alleen een pijltje zou de
     kaart openen met een teken dat naast niets staat. Tikken op de kaart doet
     hetzelfde. */
  .table-wrap td.exp-cell, .table-wrap th.exp-cell { display: none; }

  /* Structurele rijen blijven blokken over de volle breedte. */
  .table-wrap .group-row > td,
  .table-wrap .row-expansion > td,
  .table-wrap .edit-expand-row > td { display: block; }
  /* De actiecel reserveert altijd zijn hoogte, ook leeg: elke rij krijgt zo dezelfde
     onderruimte, en de knoppen verschijnen bij selecteren in die ruimte zonder de
     rijhoogte te vergroten. */
  .table-wrap .actions-cell { display: flex; align-items: center; justify-content: flex-end; width: auto; min-height: 2.6rem; padding: 0.35rem 0.9rem; }
  /* De cel van de bewerkrij blijft leeg: zijn knoppen staan in de lade eronder. */
  .table-wrap tr.row-editing > .actions-cell:empty { display: none; }

  /* Bewerkrij: label boven het veld, veld op volle breedte. */
  .table-wrap tr.row-editing td[data-label] { display: block; text-align: left; }
  .table-wrap tr.row-editing td[data-label]::before { float: none; display: block; margin: 0 0 0.25rem; }
  .table-wrap tr.row-editing td[data-label] .input { width: 100%; }

  /* Geen sticky rij in de gestapelde modus (de layout is block, geen table). */
  .table-wrap.sticky-sel tbody tr.row-selected > td { position: static; }
}

/* neutral coin icon variant */
.coin-icon-neutral {
  background: var(--neutral-bg);
  color: var(--text-sec);
}
td.coin { display: flex; align-items: center; gap: 0.65rem; }
td.coin-wrap { vertical-align: middle; }
.coin-icon {
  width: 28px; height: 28px;
  border-radius: 50%;
  background: var(--accent-soft);
  display: grid;
  place-items: center;
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--accent);
  flex-shrink: 0;
}
.coin-name { font-weight: 600; }
.coin-ticker { font-size: 0.72rem; color: var(--text-sec); }

td.change-pos { color: var(--pos); font-weight: 500; }
td.change-neg { color: var(--neg); font-weight: 500; }

/* ═══════════════════════════════════════════════
   ALERTS / BANNERS
═══════════════════════════════════════════════ */

.alert {
  display: flex;
  gap: 0.8rem;
  align-items: flex-start;
  padding: 1rem 1.2rem;
  border-radius: var(--radius-md);
  font-size: 0.84rem;
  margin-bottom: 0.75rem;
}
.alert-icon { font-size: 1rem; flex-shrink: 0; margin-top: 0.05rem; }
.alert-title { font-weight: 600; margin-bottom: 0.1rem; }
.alert-body { color: var(--text-sec); font-size: 0.8rem; }
.alert-info    { background: var(--accent-soft); color: var(--accent-text); }
.alert-success { background: var(--pos-bg); color: var(--pos); }
.alert-error   { background: var(--neg-bg); color: var(--neg); }
.alert-neutral { background: var(--neutral-bg); color: var(--text-sec); border: 1px solid var(--border); }

/* ═══════════════════════════════════════════════
   PROGRESS / BARS
═══════════════════════════════════════════════ */

.progress-wrap { margin-bottom: 1rem; }
.progress-header { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 0.5rem; }
.progress-label { font-size: 0.78rem; font-weight: 500; }
.progress-val { font-size: 0.75rem; color: var(--text-sec); font-variant-numeric: tabular-nums; }
.progress-track {
  height: 6px;
  background: var(--surface-inset);
  border-radius: var(--radius-full);
  overflow: hidden;
}
.progress-fill {
  height: 100%;
  border-radius: var(--radius-full);
  background: var(--accent);
  transition: width 0.6s var(--ease-out);
}
[data-depth="deep"][data-theme="dark"] .progress-fill {
  box-shadow: 0 0 8px var(--accent-glow);
}
.progress-track.thin { height: 3px; }
.progress-track.thick { height: 10px; }

/* ═══════════════════════════════════════════════
   SEGMENTED CONTROL  (.tabs / .tab-btn)
   HEETTE HIER "TABS" EN DAT WAS FOUT (hernoemd 04-09-2026, toen de echte
   tab-control uit AssetApp hierbij kwam; zie "TAB-CONTROL" hierboven). Deze rij
   wisselt geen inhoudspaneel maar kiest een PARAMETER van wat er al staat: een
   periode, een eenheid, een weergave. Vandaar de pil-vorm zonder basislijn: er
   begint niets nieuws, dus er hoort ook geen scheiding onder.

   De klassenamen zijn niet meegehernoemd, en dat is een afweging en geen
   vergeetachtigheid: er hangt bestaande markup aan. Wie er nieuw aan begint,
   leest de sectie hierboven eerst; wie een paneelwissel wil, heeft .tabset nodig.
═══════════════════════════════════════════════ */

.tabs { display: flex; gap: 2px; margin-bottom: 1.5rem; background: var(--bg-raised); padding: 3px; border-radius: var(--radius-lg); width: fit-content; }
.tab-btn {
  border: none;
  background: transparent;
  color: var(--text-sec);
  font-family: var(--font-base);
  font-size: 0.8rem;
  font-weight: 500;
  padding: 0.45rem 1rem;
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: all 0.15s;
}
.tab-btn.on { background: var(--bg-overlay); color: var(--text-primary); }
.tab-btn:hover:not(.on) { color: var(--text-primary); }

/* ═══════════════════════════════════════════════
   LIST ITEMS
═══════════════════════════════════════════════ */

.list-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: 1rem 0;
  border-bottom: 1px solid var(--border);
}
.list-item:last-child { border-bottom: none; }
.list-item-left { display: flex; align-items: center; gap: 0.75rem; flex: 1; min-width: 0; }
.list-item-icon { width: 36px; height: 36px; border-radius: 10px; background: var(--accent-soft); display: grid; place-items: center; font-size: 0.9rem; color: var(--accent); flex-shrink: 0; }
.list-item-title { font-size: 0.88rem; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.list-item-sub { font-size: 0.72rem; color: var(--text-sec); }
.list-item-right { text-align: right; flex-shrink: 0; }
.list-item-val { font-size: 0.88rem; font-weight: 600; font-variant-numeric: tabular-nums; }
.list-item-meta { font-size: 0.72rem; color: var(--text-sec); }

/* De takenrij (AtreidesAssets, meldingen op het dashboard, 23-10-2026): een lijst die je afwerkt.
   Geen eigen vlak: rijlijnen en --row-hover, dus geen glasregel nodig. De rij loopt de kaartpadding in,
   zodat de hover tot de rand gaat en de tekst onder de kaarttitel blijft. Hover ruilt de datum voor de
   knoppen in dezelfde cel, dus er schuift niets; zonder hover (aanraken) staan ze er allebei. */
.task-list { margin: 0.35rem -0.5rem 0; container-type: inline-size; }
.task-row { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; grid-template-areas: "count title side" "count sub sub"; align-items: baseline; column-gap: 0.75rem; row-gap: 0.1rem; padding: 0.55rem 0.5rem; cursor: pointer; transition: background 0.15s; }
.task-row + .task-row { border-top: 1px solid var(--row-line); }
.task-row:hover, .task-row:focus-visible { background: var(--row-hover); outline: none; }
.task-row:focus-visible { box-shadow: inset 2px 0 0 var(--accent); }
.task-row-count { grid-area: count; align-self: center; min-width: 1.6rem; height: 1.6rem; padding: 0 0.35rem; border-radius: var(--radius-sm); display: grid; place-items: center; font-size: 0.76rem; font-weight: 600; font-variant-numeric: tabular-nums; background: var(--surface-tint); box-shadow: inset 0 0 0 1px var(--row-line); transition: color 0.15s; }
.task-row:is(:hover, :focus-visible) .task-row-count { color: var(--accent-text); }
.task-row-title { grid-area: title; font-size: 0.82rem; font-weight: 600; line-height: 1.3; overflow-wrap: break-word; }
.task-row-sub { grid-area: sub; font-size: 0.74rem; color: var(--text-sec); line-height: 1.35; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
.task-row-side { grid-area: side; display: grid; justify-items: end; align-items: center; }
.task-row-side > * { grid-area: 1 / 1; transition: opacity 0.15s; }
.task-row-date { font-size: 0.7rem; color: var(--text-ter); font-variant-numeric: tabular-nums; white-space: nowrap; }
.task-row-actions { display: flex; align-items: center; gap: 0.15rem; opacity: 0; color: var(--text-ter); }
.task-row:is(:hover, :focus-within) .task-row-date { opacity: 0; }
.task-row:is(:hover, :focus-within) .task-row-actions { opacity: 1; }
.task-row-ack { display: inline-flex; align-items: center; justify-content: center; width: 1.6rem; height: 1.6rem; padding: 0; border: 0; border-radius: var(--radius-sm); background: transparent; color: var(--text-sec); cursor: pointer; }
.task-row-ack:hover, .task-row-ack:focus-visible { background: var(--accent); color: #fff; outline: none; }
[data-theme="dark"] .task-row-ack:hover, [data-theme="dark"] .task-row-ack:focus-visible { color: #000; }
@container (max-width: 18rem) {
  .task-row { grid-template-columns: auto minmax(0, 1fr); grid-template-areas: "count title" "count sub" "count side"; }
  .task-row-side { justify-items: start; }
}
@media (hover: none) {
  .task-row { grid-template-columns: auto minmax(0, 1fr) auto; grid-template-areas: "count title side" "count sub sub" "count date date"; }
  .task-row-side { display: contents; }
  .task-row-date { grid-area: date; }
  .task-row-actions { grid-area: side; }
  .task-row-actions, .task-row .task-row-date { opacity: 1; }
}

/* ═══════════════════════════════════════════════
   MISC
═══════════════════════════════════════════════ */

/* Lege staat (sectie 10b): een regel tekst, geen kader en geen icoon. De reden staat gedempt achter
   de melding en de link erachteraan, zodat je in één oogopslag leest wat er niet is en pas daarna
   waarom. Bewust geen min-height: een lege staat mag niet meer ruimte innemen dan zijn tekst. */
.empty-state { font-size: 0.93rem; color: var(--text-sec); line-height: 1.6; padding: 0.9rem 0; }
.empty-state-why { color: var(--text-ter); }
.empty-state a { color: var(--accent); text-decoration: none; }
.empty-state a:hover { text-decoration: underline; }

.divider { height: 1px; background: var(--border); margin: 2rem 0; }
.divider-label { display: flex; align-items: center; gap: 1rem; margin: 1.5rem 0; }
.divider-label::before, .divider-label::after { content: ''; flex: 1; height: 1px; background: var(--border); }
.divider-text { font-size: 0.7rem; color: var(--text-ter); font-weight: 500; white-space: nowrap; }

.skeleton {
  background: linear-gradient(90deg, var(--bg-raised) 25%, var(--bg-overlay) 50%, var(--bg-raised) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.6s infinite;
  border-radius: var(--radius-sm);
}
@keyframes shimmer { to { background-position: -200% 0; } }

/* code block */
.code {
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: 1.2rem 1.4rem;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--accent);
  overflow-x: auto;
  line-height: 1.7;
}
.code .c { color: var(--text-ter); }
.code .k { color: var(--text-sec); }

/* tooltip */
.tooltip-wrap { position: relative; display: inline-block; }
.tooltip {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  background: var(--bg-overlay);
  border: 1px solid var(--border-mid);
  color: var(--text-primary);
  font-size: 0.72rem;
  padding: 0.35rem 0.7rem;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s;
  z-index: 10;
}
.tooltip-wrap:hover .tooltip { opacity: 1; }

/* ═══════════════════════════════════════════════
   UTILITY
═══════════════════════════════════════════════ */
.mt-sm { margin-top: 0.5rem; }
.mt-md { margin-top: 1rem; }
.mt-lg { margin-top: 1.5rem; }
.gap-sm { gap: 0.5rem; }
.gap-md { gap: 1rem; }
.flex { display: flex; align-items: center; }
.flex-between { display: flex; align-items: center; justify-content: space-between; }
.w-full { width: 100%; }
.t-muted-cell { color: var(--text-sec); font-size: 0.8rem; }

/* ═══════════════════════════════════════════════
   RESPONSIVE - MOBILE FIRST
═══════════════════════════════════════════════ */

/* Mobile topbar (hidden on desktop) */
.mobile-topbar {
  display: none;
  position: sticky;
  top: 0;
  z-index: 100;
  background: var(--bg);
  border-bottom: 1px solid var(--border);
  padding: 0.9rem 1.2rem;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  transition: background 0.25s, border-color 0.25s;
}
.mobile-topbar-logo { font-weight: 700; font-size: 0.9rem; display: flex; align-items: center; gap: 0.5rem; }
.mobile-controls { display: flex; gap: 0.5rem; }
.mobile-pill {
  display: flex;
  background: var(--bg-raised);
  border-radius: var(--radius-full);
  padding: 2px;
}
.mobile-pill button {
  border: none; background: transparent;
  color: var(--text-sec);
  font-family: var(--font-base);
  font-size: 0.7rem; font-weight: 500;
  padding: 0.25rem 0.6rem;
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: all 0.2s;
}
.mobile-pill button.on { background: var(--bg-overlay); color: var(--text-primary); }

@media (max-width: 768px) {
  .sidebar { display: none; }
  .mobile-topbar { display: flex; }
  .main { padding: 1.7rem 1.4rem; }
  .page { flex-direction: column; }

  /* Geen zijbalk betekent geen scheidingslijn. Zonder deze regel blijft het
     kadertje staan op x = 230px, midden in de inhoud: het hangt aan het venster
     en weet niet dat het menu weg is. Zelfde reden als bij de tab-lijn, die op
     mobiel ook vervalt omdat één doorlopende lijn niet netjes over meerdere
     regels breekt. De gloed blijft wel staan: die hangt al in de hoek en heeft
     de zijbalk nergens voor nodig. */
  .main::before { display: none; }

  /* tabel: de tabel staat hier al gestapeld (zie TABLES, onder 1200px); de
     topbar en de ruimere padding tellen mee in de ruimte rond de tabel */
  :root { --table-vh-offset: 13rem; }

  /* cards: 1 kolom op mobiel */
  .card-grid { grid-template-columns: 1fr; }

  /* ds-row wrappen */
  .ds-row { flex-wrap: wrap; }

  /* swatch grid smaller */
  .swatch-grid { grid-template-columns: repeat(auto-fill, minmax(90px, 1fr)); }

  /* grote fonts iets kleiner */
  .t-display { font-size: 2rem; }
  .t-h1 { font-size: 1.6rem; }
  .t-num-lg { font-size: 1.8rem; }
}

@media (max-width: 480px) {
  .stats { grid-template-columns: 1fr 1fr; }
  .btn-row { flex-direction: column; }
  .ft-period { display: none; }
}
