/* Bonnetjes — site.css
 * Plain CSS, no preprocessors, no build step.
 * GENERIC styles only: design tokens, element defaults, app chrome (topbar/nav/search/
 * account), and patterns shared by multiple components (tables, row-link, forms, buttons,
 * chips, tags, cards, filter bar, doc-split, line-grid base, Tom Select shims).
 * Anything specific to one page or component lives in that component's .razor.css
 * (Blazor CSS isolation, bundled as Bonnetjes.Web.styles.css — linked in App.razor).
 * Rule of thumb: a selector that names one page's markup does not belong here.
 */

/* Self-hosted Space Grotesk (OFL), gesubset (±13 kB per gewicht).
 * Draagt het merk: lockup, koppen, display-getallen. Body blijft
 * system-ui voor dichte tabellen en formulieren. */
@font-face {
    font-family: "Space Grotesk";
    src: url("fonts/spacegrotesk-400.woff2") format("woff2");
    font-weight: 400;
    font-display: swap;
}
@font-face {
    font-family: "Space Grotesk";
    src: url("fonts/spacegrotesk-500.woff2") format("woff2");
    font-weight: 500;
    font-display: swap;
}

:root {
    /* ── Merk ─────────────────────────────────────────────
     * --green is semantisch gereserveerd: "klopt" — in balans,
     * gematcht, geld-in. Nooit decoratief of voor interactie.
     * Monochroom: interactie ís inkt — gevulde inktknoppen,
     * onderstreepte links. --accent bestaat als rol maar wijst
     * naar de inkt; groen blijft de enige echte kleur. */
    --ink: #1C333D;
    --paper: #FAF8F3;
    --green: #2E7D5B;

    /* ── Rollen (bestaande namen; de rest van dit bestand
     *    hangt hieraan) ──────────────────────────────────── */
    --fg: var(--ink);
    --fg-muted: color-mix(in srgb, var(--ink) 64%, var(--paper));
    --bg: var(--paper);
    --bg-alt: #FFFEFB;
    /* Achtergrond van alle tabelkoppen (th). Opaak: nodig voor sticky-head,
     * waar rijen onder de kop doorschuiven. */
    --table-head-bg: #EEF3F8;
    --hairline: color-mix(in srgb, var(--ink) 14%, var(--paper));
    --accent: var(--ink);
    --accent-hover: #0F212B;
    --warn: #A8514B;
    --radius: 8px;
    --radius-lg: 14px;
    /* Diepte i.p.v. kaders: kaarten/tabellen dragen een zachte schaduw,
     * zwevende lagen (dropdown/popover) een sterkere. Focus is een
     * zachte ring in de accentkleur. */
    --shadow-1: 0 1px 2px rgba(28, 51, 61, 0.05), 0 2px 10px rgba(28, 51, 61, 0.05);
    --shadow-2: 0 10px 30px rgba(28, 51, 61, 0.16);
    --ring: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
    --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --display: "Space Grotesk", var(--font);
    --mono: ui-monospace, "Cascadia Mono", "SF Mono", Menlo, Consolas, monospace;

    /* ── Diagramkleuren ──────────────────────────────────────
     * Alleen voor grafiekmarkeringen — nooit voor tekst, randen of
     * vlakken in de UI. Twee categorieën die naast elkaar staan
     * (baten/lasten) moeten ook zonder kleurwaarneming te scheiden
     * zijn; de sitegroen tegen --warn haalt dat niet (deutan ΔE 4.1).
     * Dit paar is gevalideerd op lichtheidsband, chroma, CVD-afstand
     * en contrast tegen --bg-alt: protan ΔE 11.7, alle checks PASS.
     * Wijzig ze niet los van elkaar — hervalideer het paar.
     * Een afgeleid totaal (resultaat) is géén derde categorie en
     * blijft --ink; het onderscheid zit in de marker (lijn vs staaf). */
    --chart-baten: #0C7A52;
    --chart-lasten: #CC8422;

    /* Hoogte van de plakkende topbalk. Alles wat zelf onder de balk plakt
     * (tabelkoppen, documentpaneel) of waar de browser naartoe scrollt
     * (anchors) rekent hiermee. site.js meet de echte hoogte en overschrijft
     * deze waarde; dit is de desktopwaarde als fallback (min-height 3rem +
     * 1px hairline) — zonder JS staan de gestapelde balken op smalle
     * schermen iets te hoog, wat niets breekt. */
    --topbar-h: 3.0625rem;
}

* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }

/* Anchors (#id) niet onder de plakkende topbalk laten landen. */
html { scroll-padding-top: var(--topbar-h); }

body {
    font-family: var(--font);
    font-size: 15px;
    line-height: 1.5;
    color: var(--fg);
    background: var(--bg);
}

/* Monochroom: links zijn inktkleurig, dus de onderstreping ís de
 * affordance. Blok- en chrome-links (nav, merk, knoppen, tags,
 * rij-links, zoekresultaten, notitielinks) dragen die al via vorm
 * en hover en blijven zonder streep. */
a {
    color: var(--fg);
    text-decoration: underline;
    text-underline-offset: 2px;
    text-decoration-color: color-mix(in srgb, var(--ink) 35%, transparent);
}
a:hover { text-decoration-color: currentColor; }
.nav a, .subnav a, .brand, .btn, a.btn, .tag,
.search-hits a, .note-list .note-link,
table.row-link tbody td > a { text-decoration: none; }

h1, h2, h3, h4 { font-family: var(--display); font-weight: 500; line-height: 1.2; margin: 1em 0 0.5em; letter-spacing: 0.005em; }
h1 { font-size: 1.75rem; letter-spacing: -0.01em; margin-top: 0; }
h1:focus { outline: none; } /* FocusOnNavigate programmatically focuses h1 after each nav */
h2 { font-size: 1.3rem; }
h3 { font-size: 1.1rem; }

code { font-family: var(--mono); font-size: 0.9em; }

/* ── Top bar ─────────────────────────────────────────────── *
 * Fluid: flex-wrap lets nav items reflow onto multiple rows
 * on narrow screens. No media queries, no disclosure widget.
 *
 * Plakt aan de bovenkant van het venster: het hoofdmenu blijft bij elke
 * scrollpositie bereikbaar. De balk heeft al een dekkende achtergrond
 * (--bg-alt), dus de content schuift er netjes onder door.
 *
 * Lagen: boven de in-pagina plaklagen (tabelkoppen, z-index 1), onder de
 * Tom Select-dropdowns die naar <body> verhuizen (z-index 30, zie onder) en
 * onder #blazor-error-ui (1000). De subnav plakt bewust niet mee — die hoort
 * bij de pagina en mag wegscrollen.
 */
.topbar {
    position: sticky;
    top: 0;
    z-index: 20;
    background: var(--bg-alt);
    border-bottom: 1px solid var(--hairline);
    padding: 0 1rem;
}
.topbar-inner {
    max-width: 1200px;
    margin: 0 auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem 1.5rem;
    padding: 0.5rem 0;
    min-height: 3rem;
}
/* Merk-lockup: [icoon] tenant bonnetjes. De tenantslug is echte tekst
 * naast het merk: gewicht 400 op 72% inkt; "bonnetjes" 500 op volle inkt.
 * Lange slugs (max 40 tekens) krijgen ellipsis; icoon en merk wijken nooit.
 * Het icoon (inline svg, stroke=currentColor) kleurt mee met de context. */
.brand {
    display: inline-flex;
    align-items: center;
    gap: 0.45em;
    min-width: 0;
    color: var(--fg);
    font-family: var(--display);
    font-weight: 500;
    font-size: 1.15rem;
    letter-spacing: 0.005em;
    white-space: nowrap;
}
.brand:hover { text-decoration: none; }
.brand > svg { height: 1.45em; width: auto; flex: none; }
.brand .tenant {
    font-weight: 400;
    color: color-mix(in srgb, currentColor 72%, transparent);
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}
.nav {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem;
}
/* Menu-items in onderkast, zoals het merk zelf — afgedwongen in CSS zodat
 * de Razor-markup gewoon "Mutaties" mag blijven zeggen. Iets kleiner en
 * rustiger dan de content, conform het tenant-lockup voorbeeld. */
.nav a {
    color: var(--fg-muted);
    font-size: 0.9rem;
    text-transform: lowercase;
    padding: 0.4rem 0.6rem;
}
.nav a:hover { color: var(--fg); text-decoration: none; }
.nav a.active { color: var(--fg); font-weight: 500; }
/* Actief-indicator: een strak lijntje in het merkgroen — dezelfde kleur
 * als het isgelijkteken in het logo. Hover toont het zacht (35%),
 * actief vol. Bewust het enige groene chrome-element. */
.nav a { position: relative; }
.nav a::after {
    content: "";
    position: absolute;
    left: 0.6rem;
    right: 0.6rem;
    bottom: 0.15rem;
    height: 2px;
    border-radius: 1px;
    background: var(--green);
    opacity: 0;
    transition: opacity 0.12s;
}
.nav a:hover::after { opacity: 0.35; }
.nav a.active::after { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
    .nav a::after { transition: none; }
}

/* ── Secondary (section) navigation ──────────────────────── */
/* A second row under the topbar, shown only on pages in a multi-page section (Documenten,
   Rapportages, Instellingen). Uses the page background so it reads as a sub-level of the
   topbar, which uses --bg-alt. */
.subnav {
    background: var(--bg);
    border-bottom: 1px solid var(--hairline);
    padding: 0 1rem;
}
.subnav-inner {
    max-width: 1200px;
    margin: 0 auto;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.25rem;
    padding: 0.35rem 0;
}
.subnav a {
    color: var(--fg-muted);
    font-size: 0.85rem;
    text-transform: lowercase;
    padding: 0.3rem 0.6rem;
}
.subnav a:hover { color: var(--fg); text-decoration: none; }
.subnav a.active { color: var(--fg); font-weight: 500; }
/* Zelfde groene indicator als het topmenu, iets smaller gezet. */
.subnav a { position: relative; }
.subnav a::after {
    content: "";
    position: absolute;
    left: 0.6rem;
    right: 0.6rem;
    bottom: 0.05rem;
    height: 2px;
    border-radius: 1px;
    background: var(--green);
    opacity: 0;
    transition: opacity 0.12s;
}
.subnav a:hover::after { opacity: 0.35; }
.subnav a.active::after { opacity: 1; }

/* Wide desktop: pin the brand to the left and center the primary nav links within the bar, so the
   secondary nav (also centered in the same box) lines up directly beneath the menu items rather
   than drifting into the empty space to the right. The absolutely-pinned brand and right group
   (see .topbar-right) don't participate in the flex flow, so this layout only engages once the
   bar is wide enough for centered nav + both pinned groups — below that the bar stays in normal
   flow (brand, links, then the stacked search/account group at the right; the nav links wrap
   within .nav when space runs out) so nothing can overlap. Keep this breakpoint in sync with
   the .topbar-right ones. */
@media (min-width: 1200px) {
    .topbar-inner {
        position: relative;
        justify-content: center;
    }
    .brand {
        position: absolute;
        top: 0;
        bottom: 0;
        left: 0;
        display: inline-flex;
        align-items: center;
    }
}

/* ── Page container ──────────────────────────────────────── */
main {
    max-width: 1200px;
    margin: 0 auto;
    padding: 1rem;
}
@media (min-width: 820px) {
    main { padding: 1.5rem; }
}

/* ── Tables ──────────────────────────────────────────────── */
/* Modern vlak: geen kader maar een zachte schaduw; binnenlijnen lichter
 * dan de hairline zodat rijen ademen. */
table {
    border-collapse: collapse;
    width: 100%;
    background: var(--bg-alt);
    border: none;
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-1);
    overflow: hidden;
}
th, td {
    text-align: left;
    padding: 0.6rem 0.9rem;
    border-bottom: 1px solid color-mix(in srgb, var(--ink) 8%, var(--bg-alt));
}
tr:last-child td { border-bottom: none; }
th {
    background: var(--table-head-bg);
    font-weight: 500;
    font-size: 0.8rem;
    color: var(--fg-muted);
    letter-spacing: 0.01em;
}
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
td.code, th.code { font-family: var(--mono); font-size: 0.9em; }
/* Dates must never wrap onto two lines (the inner <a> in row-link tables too). */
td.date, th.date, td.date > a { white-space: nowrap; }

/* Sortable column headers: clickable, but keep the muted header look. */
th a { color: inherit; text-decoration: none; }
th a:hover { text-decoration: underline; }

/* Slightly emphasise the money column so it's the scan anchor. */
td.amount, td.amount > a { font-weight: 500; }

/* Truncate a long free-text cell to one line (full text lives in the title attribute). */
td.ellipsis, td.ellipsis > a { max-width: 32rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Sticky header for long overview tables. Needs overflow:visible — the default table
   overflow:hidden (used to clip the rounded corners) would trap position:sticky. The header
   keeps its own opaque background so rows scroll underneath it. Parks below the sticky topbar
   (--topbar-h) instead of at top:0, where the bar would hide it. */
table.sticky-head { overflow: visible; }
table.sticky-head thead th { position: sticky; top: var(--topbar-h); z-index: 1; }

/* User-resizable columns (opt-in via data-colw="key" on the table): a slim grab strip inside each
   header cell — no markup, the divider is the strip's own 1px border, lit up while the pointer is
   in that cell. Every handle resizes the cell it sits in, so it sits on the cell's right edge —
   except in the last cell, where it mirrors to the left edge (see below). Dragging resizes that
   column; the other columns share the freed or claimed space (site.js), which writes the widths
   into a <style> in <head> — HTMX swaps never touch <head>, so the widths survive sort/filter
   clicks.
   Double-click a strip resets the table to the stylesheet defaults. The position rule is kept at
   lower specificity than table.sticky-head thead th, so sticky headers keep position:sticky
   (which anchors the ::after strip just as well as relative does). */
table[data-colw] th { position: relative; }
table[data-colw] thead th::after {
    content: "";
    position: absolute;
    top: 0;
    right: 0;
    width: 8px; /* keep equal to EDGE in site.js: the strip IS the hit zone */
    height: 100%;
    cursor: col-resize;
    /* The divider is this strip's own 1px border, so what is painted stays independent of how wide
       the hit zone is. Transparent at rest: the table keeps its borderless look until hovered. */
    border-right: 1px solid transparent;
    transition: border-color 120ms ease;
}
/* The last cell's right edge is the table's own edge — nothing to trade against — so its handle
   mirrors to its LEFT edge, keeping the rule "a handle resizes the cell it sits in" true for every
   column. It shares that boundary with the previous cell's handle: grab it from the right side and
   the previous column takes the delta, from the left side and the last column does (site.js flips
   the drag sign there). */
table[data-colw] thead th:last-child::after {
    right: auto;
    left: 0;
    border-right: 0;
    border-left: 1px solid transparent;
}
/* Only the hovered cell's divider shows — the line always sits in the column the drag would
   resize, so what lights up is what will move. */
table[data-colw] thead th:hover::after { border-color: var(--accent); }
@media (prefers-reduced-motion: reduce) {
    table[data-colw] thead th::after { transition: none; }
}

/* Nothing paints outside its own column. A dragged column can end up narrower than its content,
   and a nowrap date, a pill or a single long word cannot wrap out of the way — without this they
   paint over the neighbouring column's text. The clip sits on the box that holds the text, never
   on the cell itself: in row-link tables the anchor fills the whole cell, so clipping the td would
   swallow that anchor's focus ring (and the action buttons' ring in the bank tables). An anchor's
   own overflow does not clip its own outline, so focus stays visible. .chip truncates inside its
   pill (see the chip block below); the free-text columns keep their full value in a title. */
table[data-colw] th,
table[data-colw] tbody td > a {
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Cells that hold bare text (no anchor to clip on, e.g. the bank-transaction tables) get a wrap
   opportunity instead: an unbreakable word (an IBAN) folds within the column rather than
   painting over the next one. */
table[data-colw] td { overflow-wrap: break-word; }

/* Type chips: small low-saturation pills, toned by money direction (in / out / neutral). */
.chip {
    display: inline-block;
    padding: 0.05rem 0.5rem;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 500;
    line-height: 1.5;
    white-space: nowrap;
}
.chip-in { background: color-mix(in srgb, var(--green) 13%, transparent); color: var(--green); }
.chip-out { background: color-mix(in srgb, var(--warn) 12%, transparent); color: var(--warn); }
.chip-neutral { background: var(--bg); color: var(--fg-muted); border: 1px solid var(--hairline); }
/* In a table cell the pill may never outgrow its column: columns are user-resizable (see the
   data-colw block above) and a nowrap pill would otherwise paint straight over the next column.
   It truncates inside the pill, so the rounded end and the tone stay intact; the full type sits in
   the cell's title attribute. vertical-align:bottom is load-bearing — overflow:hidden moves an
   inline-block's baseline to its bottom edge, which without it adds descender space under every
   chip row and makes those rows taller than the rest. */
td .chip {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: bottom;
}

/* Row-as-link table: each cell wraps its content in <a>, padding moved to the
   anchor so the entire row is a click target. No JS needed. */
table.row-link tbody tr:hover { background: var(--bg); }
table.row-link tbody td { padding: 0; }
table.row-link tbody td > a {
    display: block;
    padding: 0.6rem 0.9rem;
    color: inherit;
    text-decoration: none;
}
table.row-link tbody td.num > a { text-align: right; }
/* Small muted link to a related mutation, shown as an extra line under the description in the
   ledger lines table. It leaves the row for the other mutation, so it sits below the (block)
   description anchor; tighten the stacked top padding so the two lines read as one cell. */
table.row-link tbody td > a.row-relation { padding-top: 0; font-size: 0.78rem; }
/* A row in a row-link table with no detail page to point at — a whole derived tbody (e.g. the
   Balans Resultaat line) or a single interleaved row (e.g. a journaalregel whose ledger lookup
   missed): restore the cell padding that row-link zeroes for anchor-less cells. */
table.row-link tbody.no-link td,
table.row-link tbody tr.no-link td { padding: 0.6rem 0.9rem; }

/* Edit (pencil) icon link — the SVG lives here as a mask so the markup stays a single class
   instead of repeating the inline <svg> on every row. Inherits text colour via currentColor. */
.icon-edit {
    display: inline-block;
    width: 18px;
    height: 18px;
    color: var(--fg-muted); /* was var(--muted): bestond niet */
    background-color: currentColor;
    -webkit-mask: var(--icon-edit-src) center / contain no-repeat;
    mask: var(--icon-edit-src) center / contain no-repeat;
}
table.row-link tbody td > a:hover .icon-edit { color: var(--accent); }

/* Download (tray-arrow) icon, same mask trick as .icon-edit above. Used standalone in a link — see
   .icon-link for the hover/focus affordance, which .icon-edit gets from its row instead. */
.icon-download {
    display: inline-block;
    width: 18px;
    height: 18px;
    color: var(--fg-muted);
    background-color: currentColor;
    -webkit-mask: var(--icon-download-src) center / contain no-repeat;
    mask: var(--icon-download-src) center / contain no-repeat;
}

/* Paperclip (bijlage) marker, same mask trick as .icon-edit above. Inline next to cell text
   (e.g. de Factuur-kolom op /mutaties), so slightly smaller and baseline-aligned with a gap. */
.icon-paperclip {
    display: inline-block;
    width: 14px;
    height: 14px;
    margin-right: 0.35rem;
    vertical-align: -0.15em;
    color: var(--fg-muted);
    background-color: currentColor;
    -webkit-mask: var(--icon-paperclip-src) center / contain no-repeat;
    mask: var(--icon-paperclip-src) center / contain no-repeat;
}

/* Mutation has notes ("opmerkingen"): a speech bubble before the number (lists) or hit title (search).
   .note-hit = the text filter / search term matched a note, so the bubble is marked like a <mark>. */
.has-notes::before {
    content: "";
    display: inline-block;
    width: 13px;
    height: 13px;
    margin-right: 0.3rem;
    vertical-align: -0.1em;
    background-color: var(--fg-muted);
    -webkit-mask: var(--icon-note-src) center / contain no-repeat;
    mask: var(--icon-note-src) center / contain no-repeat;
}
/* A mutation row with notes gets a barely-there warm tint; with an open follow-up ("opvolging nodig")
   a clearer light yellow. Both a shade deeper on hover, like plain rows, and both kept well below the
   <mark> yellow (#FBE7A1) so filter hits still stand out on these rows. */
table tbody tr.has-notes-row { background: #FFFDF6; }
table.row-link tbody tr.has-notes-row:hover { background: #FAF6E8; }
table tbody tr.has-notes-row.follow-up-row { background: #FFFCEB; }
table.row-link tbody tr.has-notes-row.follow-up-row:hover { background: #FDF5D6; }
/* In a table the bubble and the number stay on one line (the column grows / is sized for it). */
td > a.has-notes { white-space: nowrap; }
.has-notes.note-hit::before {
    width: 15px;
    height: 15px;
    background: var(--icon-note-hit-src) center / contain no-repeat;
    -webkit-mask: none;
    mask: none;
}

/* An icon-only link outside a table: give it a real hit area and the same hover/focus cues a text
   link gets, since there is no label to underline. */
a.icon-link {
    display: inline-flex;
    align-items: center;
    padding: 0.25rem;
    border-radius: var(--radius);
    line-height: 0;
}
a.icon-link:hover > [class^="icon-"] { color: var(--accent); }
a.icon-link:focus-visible { outline: none; box-shadow: var(--ring); }

/* The same affordance for an icon-only <button> (e.g. copy-to-clipboard): no button chrome, just
   the icon with a hit area and hover/focus cues. */
button.icon-button {
    display: inline-flex;
    align-items: center;
    padding: 0.25rem;
    margin: 0 0 0 0.25rem;
    border: 0;
    border-radius: var(--radius);
    background: none;
    line-height: 0;
    vertical-align: middle;
    cursor: pointer;
}
/* Beats the global button:hover accent fill further down (same specificity would lose on order). */
button.icon-button:hover { background: none; border-color: transparent; }
button.icon-button:hover > [class^="icon-"] { color: var(--accent); }
button.icon-button:focus-visible { outline: none; box-shadow: var(--ring); }

/* Copy (two sheets) icon, same mask trick as .icon-edit, a size smaller so it sits quietly next to
   the text it copies. After a successful copy site.js sets .copied and it briefly shows a check. */
.icon-copy {
    display: inline-block;
    width: 14px;
    height: 14px;
    color: var(--fg-muted);
    background-color: currentColor;
    -webkit-mask: var(--icon-copy-src) center / contain no-repeat;
    mask: var(--icon-copy-src) center / contain no-repeat;
}
button.copied > .icon-copy {
    color: var(--green);
    -webkit-mask-image: var(--icon-check-src);
    mask-image: var(--icon-check-src);
}

:root {
    --icon-copy-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='9' y='9' width='13' height='13' rx='2'/%3E%3Cpath d='M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1'/%3E%3C/svg%3E");
    --icon-check-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
    --icon-edit-src:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 20h9'/%3E%3Cpath d='M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4Z'/%3E%3C/svg%3E");
    --icon-download-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3v12'/%3E%3Cpath d='m7 10 5 5 5-5'/%3E%3Cpath d='M3 20h18'/%3E%3C/svg%3E");
    --icon-note-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z'/%3E%3C/svg%3E");
    /* Marked variant: the <mark> yellow as fill, ink outline — a full-colour image, not a mask. */
    --icon-note-hit-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23FBE7A1' stroke='%231C333D' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z'/%3E%3C/svg%3E");
    --icon-paperclip-src: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48'/%3E%3C/svg%3E");
}

/* ── Forms ───────────────────────────────────────────────── */
label {
    display: block;
    font-size: 0.85rem;
    color: var(--fg-muted);
    margin-bottom: 0.25rem;    
}
label > *:has(+ :is(input, select,textarea)) {
  font-weight: bold;
}

input[type="text"], input[type="number"], input[type="email"], input[type="password"],
input[type="date"], input:not([type]), select, textarea {
    font: inherit;
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--bg-alt);
    min-width: 12rem;
}
input:focus, select:focus, textarea:focus {
    outline: 2px solid transparent;
    border-color: var(--accent);
    box-shadow: var(--ring);
}
/* User-resizable description box — vertical only, so it can't overflow the form width. */
textarea.resize-y { resize: vertical; min-height: 2.4rem; }
/* File input: restyle the native "Bestand kiezen" button to match .btn.secondary; the
   "geen bestand gekozen" suffix renders muted like other helper text. */
input[type="file"] {
    font: inherit;
    color: var(--fg-muted);
    min-width: 12rem;
}
input[type="file"]::file-selector-button {
    font: inherit;
    padding: 0.4rem 1.05rem;
    margin-right: 0.75rem;
    border: 1px solid var(--hairline);
    background: var(--bg-alt);
    color: var(--fg);
    border-radius: 999px;
    cursor: pointer;
    transition: background-color 0.1s, border-color 0.1s;
}
input[type="file"]::file-selector-button:hover {
    background: var(--bg);
    border-color: var(--fg-muted);
}
/* Upload form: no submit button until a file is picked. The file input is `required`, so
   it stays :invalid while empty — no JS needed. */
form[action="/documenten/upload"]:has(input[type="file"]:invalid) button[type="submit"] {
    display: none;
}
button, .btn {
    font: inherit;
    font-weight: 500;
    padding: 0.45rem 1.15rem;
    border: 1px solid var(--accent);
    background: var(--accent);
    color: white;
    border-radius: 999px;
    cursor: pointer;
}
button:hover, .btn:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
button.secondary, .btn.secondary {
    background: var(--bg-alt);
    color: var(--fg);
    border-color: var(--hairline);
}
button.secondary:hover, .btn.secondary:hover {
    background: var(--bg);
    border-color: var(--fg-muted);
    color: var(--fg);
}
/* Links styled as buttons (a.btn) don't get the browser's default link focus ring once
   they look like buttons — give all button-likes the same visible keyboard focus. */
/* Zachte focus-ring; de transparante outline blijft zichtbaar in
 * forced-colors mode, waar box-shadow wegvalt. */
button:focus-visible, .btn:focus-visible {
    outline: 2px solid transparent;
    outline-offset: 2px;
    box-shadow: var(--ring);
}
/* ── Line grid (Nieuwe mutatie) ──────────────────────────── */
.line-grid { table-layout: fixed; }
.line-grid thead th { background: transparent; padding: 0.4rem 0.5rem; font-size: 0.78rem; }
.line-grid tbody td,
.line-grid tfoot th { padding: 0.3rem 0.5rem; vertical-align: middle; }
.line-grid tbody tr:last-child td { border-bottom: 1px solid var(--hairline); }
.line-grid tbody td:first-child { width: 2.5rem; }
.line-grid tbody td:nth-child(6) { width: 2.5rem; }
.line-grid input, .line-grid select {
    min-width: 0; padding: 0.25rem 0.4rem; font-size: 0.95rem;
}
.line-grid input[type="number"] { font-variant-numeric: tabular-nums; }

/* Tom Select copies the select's classes onto its wrapper — stretch it to fill the cell.
   The widget's .ts-control is a div, so the `.line-grid input, select` sizing above doesn't
   reach it — repeat the font-size/padding so it matches the other line inputs. */
.line-grid .ts-wrapper.account-select { width: 100%; }
.line-grid .ts-wrapper.account-select .ts-control,
.line-grid .ts-wrapper.account-select .ts-dropdown {
    font-size: 0.95rem;
}
.line-grid .ts-wrapper.account-select .ts-control { padding: 0.25rem 0.4rem; }

/* De account-/relatiedropdowns verhuizen naar <body> (dropdownParent, zie site.js) om uit de
   overflow van de tabel te komen. Daarmee liggen ze niet langer binnen de stapelcontext van de
   pagina, dus ze moeten expliciet boven de plakkende topbalk (z-index 20) uitkomen — Tom Select
   zet zelf maar 10. Voor de dropdowns die wél in hun wrapper blijven is dit onschadelijk. */
.ts-dropdown { z-index: 30; }

/* Voorraad cross-reference: collapsible panel so it doesn't overwhelm the booking form. */
details.stock-panel {
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    background: var(--bg-alt);
    padding: 0 1rem;
}
details.stock-panel > summary {
    cursor: pointer;
    padding: 0.6rem 0;
    font-size: 0.8rem;
    color: var(--fg-muted);
    letter-spacing: 0.01em;
    font-weight: 500;
}
details.stock-panel[open] { padding-bottom: 1rem; }
details.stock-panel > .stack { margin-top: 0.5rem; }

/* Count badge in a collapsible-panel summary: signals the panel holds N items while it's closed. */
.count-badge {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 0 0.4rem;
    min-width: 1.3rem;
    border-radius: 999px;
    background: var(--accent);
    color: #fff;
    font-size: 0.72rem;
    font-weight: 600;
    line-height: 1.4;
    text-align: center;
    letter-spacing: normal;
    text-transform: none;
}

.line-grid tfoot th { background: var(--bg); font-weight: 600; font-size: 0.9rem; color: var(--fg); text-transform: none; letter-spacing: normal; }
.line-remove {
    padding: 0.2rem 0.5rem; line-height: 1; font-size: 1.1rem;
    color: var(--warn); border-color: var(--hairline); background: transparent;
}
.line-remove:hover { background: var(--bg); border-color: var(--warn); }
/* ── Definition list (detail views) ──────────────────────── */
dl.detail {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: 0.4rem 1.5rem;
    margin: 1rem 0;
    max-width: 36rem;
}
dl.detail dt { color: var(--fg-muted); font-size: 0.9rem; align-self: baseline; }
dl.detail dd { margin: 0; }

/* ── Tags (product organization) ─────────────────────────── */
/* Small pill; as a link it doubles as a filter shortcut to /voorraad?tag=. */
.tag {
    display: inline-block;
    padding: 0.1rem 0.5rem;
    margin: 0 0.25rem 0.25rem 0;
    border: 1px solid var(--hairline);
    border-radius: 999px;
    background: var(--bg);
    font-size: 0.82rem;
    color: var(--fg-muted);
    text-decoration: none;
    line-height: 1.5;
}
a.tag:hover { border-color: var(--accent); color: var(--accent); }
.tag.active { background: var(--accent); color: #fff; border-color: var(--accent); }

/* ── Hypermedia filter bar ───────────────────────────────── */
/* Link-based switches + segmented groups (no submit button). Active/selected uses the app's
   accent (ink), mirroring .tag.active — not a separate accent color. */
/* One responsive row: controls flow left-to-right (Filters → Jaar → Periode → tekstfilter → Cumulatief) and
   wrap to the next line only when the width runs out. Balans keeps a nested .filter-row form. */
.filter-bar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.75rem; margin: 0 0 1rem; }
.filter-bar .filter-row { display: flex; align-items: flex-end; gap: 0.75rem; flex-wrap: wrap; }

/* Segmented group of links: one .active current item, optional .step prev/next arrows. */
.seg {
    display: inline-flex; align-items: stretch; border: 1px solid var(--hairline);
    border-radius: var(--radius); overflow: hidden; background: var(--bg-alt);
}
.seg > a, .seg > span {
    display: inline-flex; align-items: center; padding: 0.4rem 0.8rem;
    font-size: 0.9rem; text-decoration: none; color: var(--fg);
}
.seg > * + * { border-left: 1px solid var(--hairline); }
.seg a:hover { background: var(--bg); }
.seg a:focus-visible { outline: none; box-shadow: var(--ring); z-index: 1; }
.seg .active { background: var(--accent); color: #fff; font-weight: 500; }
.seg .step { color: var(--fg-muted); padding: 0.4rem 0.7rem; }

/* Text filter: same height/radius as a .seg group; grows to fill the row, wraps when narrow. */
.filter-search { display: flex; flex: 1 1 14rem; max-width: 24rem; margin: 0; }
.filter-search input[type="search"] {
    width: 100%; padding: 0.4rem 0.7rem; font-size: 0.9rem; line-height: 1.5;
    border: 1px solid var(--hairline); border-radius: var(--radius); background: var(--bg-alt); color: var(--fg);
}
.filter-search input[type="search"]:focus-visible { outline: none; box-shadow: var(--ring); }
/* Text-filter hits in the filtered list (<Highlight>, AccountTokens). Keeps the cell's own text
   color (muted, chip tone) so only the background signals the match. */
mark { background: #FBE7A1; color: inherit; border-radius: 2px; padding: 0 1px; }
/* A hit can sit past the ellipsis cut-off; a cell with a hit wraps instead so the match is visible. */
td.ellipsis:has(mark), td.ellipsis:has(mark) > a { white-space: normal; }

/* The row above a data table (mutaties, grootboek detail): the record count on the left, list-level actions (CSV download)
   pinned right so they line up with the table's last column. Same shape as .page-header, but for
   actions that belong to the *current view* of the data rather than to the page. The <p> loses its
   margin because the enclosing .stack already provides the spacing. */
.list-actions {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
}
.list-actions > p { margin: 0; }

/* Infinite-scroll trigger row (mutaties, grootboek detail): htmx loads the next batch as soon as
   it scrolls into view, so it normally flashes by; it reads as a quiet status line, not a data row. */
table.row-link tbody tr.load-more td > a { text-align: center; color: var(--fg-muted); font-size: 0.9rem; }

/* ── Cards & notes (shared: Home dashboard + DashboardChart + mutation detail) ─ *
 * Stat tiles and chart sizing live in the owning components' .razor.css
 * (DashboardContent, DashboardChart); the card surface, the dashboard grid and the
 * note-list base are shared between components and stay here.
 */
/* Dashboard: a responsive card grid — cards flow into as many columns as fit (min 20rem each)
   and stack on narrow screens. Shared by Home's skeleton placeholder and the DashboardContent
   fragment htmx swaps in its place, so it cannot live in either component's scoped css. */
.dashboard-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
    gap: 1rem;
    align-items: start;
    margin-top: 0.5rem;
}
/* A wide card claims a full row (chart cards, grootste open posten, skeleton chart cards). */
.dashboard-grid > .card-wide { grid-column: 1 / -1; }
.card {
    border: 1px solid transparent; /* DashboardContent's .card-attention kleurt deze rand */
    border-radius: var(--radius-lg);
    background: var(--bg-alt);
    box-shadow: var(--shadow-1);
    padding: 1rem 1.35rem 1.15rem;
}
.card > h2 { font-size: 1.05rem; margin: 0.25rem 0; }
.card > p.muted { margin: 0 0 0.75rem; font-size: 0.9rem; }
.card table { margin: 0; }
.card-foot { margin: 0.75rem 0 0; font-size: 0.9rem; }
/* Note list base — the dashboard renders items as links (DashboardContent.razor.css), the
   mutation detail as static blocks with a resolve form (MutatieDetail.razor.css). */
.note-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.25rem; }
.note-list .note-text { display: block; }
.note-list .note-meta { display: block; font-size: 0.8rem; }

/* ── Utility ─────────────────────────────────────────────── */
.muted { color: var(--fg-muted); }
/* "Klopt"-semantiek: in balans, gematcht, sluitend. Zelfde betekenis als
   het groen in het merkicoon; alleen hiervoor gebruiken. */
.ok { color: var(--green); }
/* Helper text under/next to a form field. Labels are already muted at 0.85rem, so plain
   .muted would look identical to a label; a step smaller sets hints apart. */
.hint { color: var(--fg-muted); font-size: 0.78rem; margin-left:0.2em;}
/* Inside a stacked form label the flex gap (1rem) pushes the hint a full line away from
   its input; pull it back so hint and input read as one group. Only when the hint
   directly follows the control — with an error message in between it stays put. */
label.stack > input + .hint,
label.stack > select + .hint,
label.stack > textarea + .hint { margin-top: -0.7rem; }
/* Determinate progress bar for the XAF-import commit: the page POSTs, then an HTMX fragment
   self-polls and re-renders this bar with a climbing width until the import is done. */
.progress {
    width: 100%;
    max-width: 32rem;
    height: 0.75rem;
    background: color-mix(in srgb, var(--fg-muted) 20%, var(--bg-alt));
    border-radius: 999px;
    overflow: hidden;
}
.progress-bar {
    height: 100%;
    background: var(--accent);
    border-radius: inherit;
    transition: width 0.3s ease;
}
@media (prefers-reduced-motion: reduce) {
    .progress-bar { transition: none; }
}
/* Indeterminate variant (postvak mail check): no known duration, so a segment sweeps across. */
.progress-indeterminate .progress-bar {
    width: 40%;
    animation: progress-sweep 1.2s ease-in-out infinite;
}
@keyframes progress-sweep {
    from { transform: translateX(-100%); }
    to { transform: translateX(250%); }
}
@media (prefers-reduced-motion: reduce) {
    .progress-indeterminate .progress-bar { width: 100%; animation: none; opacity: 0.6; }
}

.error { color: var(--warn); font-size: 0.9rem; }
.error p { margin: 0.25rem 0; }
.warn-box {
    padding: 0.6rem 0.85rem;
    border: 1px solid var(--warn);
    border-radius: var(--radius);
    background: color-mix(in srgb, var(--warn) 8%, var(--bg-alt));
    font-size: 0.9rem;
}
.warn-box p { margin: 0.25rem 0; }
.stack { display: flex; flex-direction: column; gap: 1rem; }
.row { display: flex; gap: 1rem; align-items: baseline; }

/* Side-by-side: page content on the left, the source-document preview on the right so the
   bookkeeper can read/copy values across. Breaks out of the 1200px-capped <main> to use the
   full viewport — otherwise the preview pane is too narrow to read a PDF. The preview sticks
   while the left column scrolls and fills the viewport height. Used by the "Boek als mutatie"
   form and the mutation detail view. */
.doc-split {
    display: flex;
    /* Narrow gap: the resize gutter between the panes supplies the visual separation
       (0.75rem + 6px bar + 0.75rem ≈ the old 2rem gap). */
    gap: 0.75rem;
    align-items: flex-start;
    width: 100vw;
    margin-left: calc(50% - 50vw);
    padding: 0 1.5rem;
    box-sizing: border-box;
}
/* The booking form column is narrow (form fields); the mutation detail column is wider
   (it carries the journal table). Both leave the rest of the viewport to the preview.
   site.js's doc-split resizer overrides this with an inline flex-basis when dragged. */
.doc-split > form { flex: 0 1 38rem; min-width: 0; }
.doc-split > .doc-split-main { flex: 1 1 55%; min-width: 0; }
/* Vertical grab bar: drag to trade width between the panes (e.g. widen the form). Spans the
   full row height so it's grabbable wherever the user has scrolled to. Wired by site.js;
   without JS it degrades to a static divider. */
.doc-split-gutter {
    flex: 0 0 6px;
    align-self: stretch;
    cursor: col-resize;
    touch-action: none; /* pointer-event dragging, no scroll gesture fighting */
    border-radius: 3px;
    background: var(--hairline);
}
.doc-split-gutter:hover, .doc-split-gutter:focus-visible { background: var(--accent); }
/* Plakt onder de topbalk mee (--topbar-h), zodat het document in beeld blijft en niet half
   achter de balk verdwijnt; de hoogte houdt daar rekening mee. */
.doc-split-doc { flex: 1 1 auto; min-width: 0; position: sticky; top: calc(var(--topbar-h) + 1rem); }
.doc-split-doc .doc-preview { height: calc(100vh - var(--topbar-h) - 2rem); }
@media (max-width: 900px) {
    .doc-split { flex-direction: column; gap: 1rem; }
    .doc-split-gutter { display: none; }
    /* The resizer's inline flex-basis is a horizontal width; in the stacked layout it would
       become a bogus height, so neutralize it (stylesheet !important beats inline style). */
    .doc-split > form, .doc-split > .doc-split-main { flex: 0 1 auto !important; }
    .doc-split-doc { position: static; width: 100%; }
    .doc-split-doc .doc-preview { height: 75vh; }
}
.page-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1rem;
}

/* ── Blazor error UI (default) ───────────────────────────── */
#blazor-error-ui {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}
#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}

/* ── Global search (omnibox) ─────────────────────────────── */
.search {
    position: relative;
}
.search input[type="search"] {
    min-width: 0;
    border-color: transparent;
    border-radius: 999px;
    background: color-mix(in srgb, var(--ink) 6%, var(--bg-alt));
    /* Shrinks on very small screens: the topbar row is nowrap below the desktop breakpoint,
       so a fixed 12rem box would force horizontal overflow there. */
    width: min(12rem, 40vw);
    padding: 0.3rem 0.6rem;
    font-size: 0.9rem;
}
/* The search box and the account area sit together on the right of the bar. */
.topbar-right {
    display: inline-flex;
    align-items: center;
    gap: 1rem;
}
/* Below the pinned-desktop breakpoint: stay at the bar's right edge and stack account over
   search box (column-reverse: the search form is first in the DOM). The bar row itself goes
   nowrap so this group can never drop below the menu items — when space runs out the nav links
   wrap within .nav (which is flex-wrap) while the group keeps its spot on the right. */
@media (max-width: 1199.98px) {
    .topbar-inner { flex-wrap: nowrap; }
    .topbar-right {
        margin-left: auto;
        flex-direction: column-reverse;
        align-items: flex-end;
        gap: 0.25rem;
    }
}
/* Very narrow: no room for the nav beside the logo — move the menu items onto their own
   full-width row below the logo, with the stacked account/search group staying top-right. */
@media (max-width: 700px) {
    .topbar-inner { flex-wrap: wrap; }
    .nav { order: 1; flex-basis: 100%; }
}
/* Wide desktop: mirror the absolutely-pinned brand — park the whole right group at the bar's
   edge. Same breakpoint as the brand-pin block above: pinned means out of the flex flow, so on
   narrower screens the group must stay in normal flow or it paints over the nav. */
@media (min-width: 1200px) {
    .topbar-right {
        position: absolute;
        top: 0;
        bottom: 0;
        right: 0;
    }
}
.search-dropdown {
    position: absolute;
    top: 100%;
    right: 0;
    margin-top: 0.25rem;
    width: min(28rem, 90vw);
    max-height: 70vh;
    overflow-y: auto;
    background: var(--bg-alt);
    border: 1px solid var(--hairline);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-2);
    z-index: 50;
    padding: 0.5rem;
}
/* Hide until there are results and the box is focused — no JS needed. */
.search-dropdown:empty { display: none; }
.search:not(:focus-within) .search-dropdown { display: none; }

/* ── Account area (signed-in user + logout popover) ──────── */
.account { position: relative; }
.account-trigger {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    color: var(--fg-muted);
    font-size: 0.9rem;
    white-space: nowrap;
    cursor: default;
    padding: 0.3rem 0;
}
.account:hover .account-trigger,
.account:focus-within .account-trigger { color: var(--fg); }
.account-icon { flex: none; }
/* Cap a long display name so the pinned right group can't grow into the centered nav. */
.account-name { max-width: 11rem; overflow: hidden; text-overflow: ellipsis; }
.account:focus-visible { outline: none; }
.account:focus-visible .account-trigger {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius);
}
/* Popover holding the logout form. The transparent padding-top bridges the visual gap so the
   pointer can travel from the name to the button without the (:hover) popover collapsing. */
.account-popover {
    position: absolute;
    top: 100%;
    right: 0;
    display: none;
    padding-top: 0.4rem;
    z-index: 50;
}
.account:hover .account-popover,
.account:focus-within .account-popover { display: block; }
.account-menu {
    background: var(--bg-alt);
    border: 1px solid var(--hairline);
    border-radius: var(--radius);
    box-shadow: var(--shadow-2);
    padding: 0.5rem;
}
.account-menu .btn { white-space: nowrap; }

