/* components.css — /ui/v1 · the page building blocks k-* of every trinsik app (Claude Design board 13
   "Muster: so ist jede HR-Seite gebaut"; inventory: _docs/designsystem/components.md). Load after shell.css.
   v1 changelog: 08.10.2026 split from static/css/app.css (design system stage DS1); spacing, type sizes, layers
   and focus as tokens; app-only blocks moved to static/css/app-portal.css (g-*), app-hr.css (o-*) and
   app-reporting.css (rp-*). Values off the 4 px grid / off the --fs-* scale are unchanged on purpose (no visual
   change in DS); tools/ui_lint.py and the migration M4 take them down.
   08.10.2026 (design panel): k-plan* added for <trinsik-plan>, k-cal rebuilt on the same day boxes (Marcel: no
   "unaligned" rows; k-cal is not used by any page yet).
   08.10.2026 (deferred items of the design panel): k-period, k-pages, k-pick/k-bulkbar, k-upload added (kit.js, ui_kit.py).
   09.10.2026: k-filters/k-filters-more („Mehr Filter“) and k-steps (status sequence) added (M4 notes).
   11.10.2026 density (Marcel: „wir verschwenden brutal viel Platz … man sieht nie etwas auf einen Blick“): every
   control is --target high (32 px with a mouse, 44 on touch), table rows about 32 px (--row-y) with a sticky head from
   1024 px, the page head in one row (title + badges + actions), the page title 24 px, props as a dense strip, cards
   and stats tighter, k-cols.side (main + a side column of fixed width) added; class names unchanged.

   Since UI review B1 (08.10.2026) the portal pages are built from these blocks too.

   One rule for actions (UI review B6):
     k-btn        Senf — the one action per page that starts or completes something: anlegen, speichern, senden.
                  At most one per view; on a page with a table editor, the editor's Speichern is it.
     k-btn.dark   confirming inside a sheet or a dialog (the sheet's own Speichern / Anlegen).
     k-btn.line   everything else that is a button: Bearbeiten, Filtern, Exportieren, secondary steps.
     k-link       tertiary, in running text and lists; k-link.bad deletes.
   Forms show that they are being sent (portal/base.html): the button is disabled and says „Speichert …“. */
/* page and head */
.k-page { box-sizing: border-box; width: 100%; max-width: var(--page-max); padding: var(--s6) var(--page-x) var(--s8); display: flex; flex-direction: column; gap: var(--s6); }
/* table pages (software, devices, planning …): the whole width next to the side bar, a narrower margin */
.k-page.wide { max-width: none; padding-left: var(--page-x); padding-right: var(--page-x); }
.k-page > * { min-width: 0; }
.k-page { overflow-wrap: anywhere; }
/* hyphenation only in titles and labels with long compounds, never in running text, values, names or addresses (guidelines §9) */
.k-title, .k-ph-title, .k-li-title, .k-kpi-l, .k-facts dt { hyphens: auto; }
/* k-head, k-head-text, k-title, k-sub: the old page head, kept for markup outside the kit (Brain, login) only. Since
   P4 „Ein Seitenkopf“ (13.10.2026) k.head renders k.pagehead (k-ph below); tools/ui_lint.py blocks a new
   hand-written class="k-head" in templates and <h1 className="k-title"> in Feedback. */
.k-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--s3) var(--s6); }
/* density 11.10.2026: one compact row — the breadcrumb small above, then title, „?“ and badges on one line (they wrap
   when they do not fit), the actions on the right of that line; a lead sentence, „Mehr“ or anything else takes a line
   of its own. Same markup as before (k.head, the hand-written heads of Projekte, Faktura, Zeit …). */
.k-head-text { display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; gap: var(--s1) var(--s3); min-width: 0; }
.k-head-text > :where(:not(.k-title, .k-title-row, .k-badges)) { flex-basis: 100%; }
.k-head-text > .k-title { min-width: 0; }
.k-eyebrow { margin: 0; font-size: var(--fs-caps); font-weight: 500; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.k-eyebrow a { color: var(--muted); text-decoration: none; } .k-eyebrow a:hover { color: var(--ink); text-decoration: underline; }
.k-title { margin: 0; font-family: var(--serif); font-weight: 300; font-size: var(--fs-title); line-height: 1.2; letter-spacing: -.01em; text-wrap: balance; }  /* 300: GT Alpina Thin (200) is too faint at 24 px */
.k-sub { margin: 0; font-size: var(--fs-lead); color: var(--muted); max-width: 767px; text-wrap: pretty; }
/* the page lead is one sentence; the rest folds away behind „Mehr“ (UI review B3 / D3) */
.k-help { max-width: 767px; font-size: var(--fs-text); color: var(--muted); }
.k-help > summary { display: inline-flex; align-items: center; gap: var(--s1); min-height: var(--target); cursor: pointer; font-size: var(--fs-table); color: var(--ink);
  text-decoration: underline; text-underline-offset: 3px; }
.k-help > summary::after { content: ''; width: 6px; height: 6px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg); }
.k-help[open] > summary::after { transform: translateY(1px) rotate(-135deg); }
.k-help > div, .k-help > p { margin: var(--s1) 0 0; display: flex; flex-direction: column; gap: var(--s2); }
.k-help p { margin: 0; }
.k-head-text .k-help { margin-top: -2px; }
.k-sec .k-help, .k-block .k-help { margin-bottom: var(--s2); }
@media (max-width: 767px) { .k-help.edit { display: none; } }
@media (max-width: 1023px) { .k-help > summary, .k-combo-opt { min-height: var(--target); } .k-combo-opt { justify-content: center; } }  /* touch targets (M9) */
.k-actions { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s3); align-items: center; }
/* „?“ help (Welle 0, 10.10.2026): the explanation of a page or block behind a round „?“ next to its title — a panel, not a
   paragraph that pushes the content down. k-help without .q stays the underlined „Mehr“. kit.js: Escape and a click
   outside close it, opening one closes the others. */
.k-title-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s3); min-width: 0; }
.k-help.q { position: relative; display: inline-block; max-width: none; flex: none; vertical-align: middle; }
.k-help.q > summary { list-style: none; position: relative; justify-content: center; width: 24px; height: 24px; min-height: 24px; padding: 0;
  border: 1px solid var(--mark); border-radius: var(--radius-pill); font-family: var(--mono); font-size: var(--fs-label); line-height: 1; color: var(--muted); text-decoration: none; }
.k-help.q > summary::-webkit-details-marker { display: none; }
.k-help.q > summary::after { content: none; }
.k-help.q > summary::before { content: ''; position: absolute; inset: -10px; }  /* 44 px to touch, 24 px to see */
.k-help.q > summary:hover, .k-help.q[open] > summary { border-color: var(--ink); color: var(--ink); }
.k-help.q[open] > summary { background: var(--ink); color: var(--bg); }
.k-help.q > div { position: absolute; z-index: var(--z-popover); top: calc(100% + var(--s2)); left: 0; width: min(420px, calc(100vw - 2 * var(--s4)));
  margin: 0; padding: var(--s3) var(--s4); background: var(--surface); border: 1px solid var(--line-strong); box-shadow: var(--shadow);
  color: var(--ink); font-family: var(--sans); font-size: var(--fs-table); font-weight: 400; letter-spacing: 0; text-transform: none; text-align: left; }
.k-head-text .k-help.q, .k-sec .k-help.q, .k-block .k-help.q { margin: 0; }
.k-sec-h .k-help.q { align-self: center; }
@media (max-width: 1023px) { .k-help.q > summary { min-height: 24px; } }
@media (max-width: 767px) { .k-help.q > div { position: fixed; left: var(--s4); right: var(--s4); top: auto; width: auto; margin-top: var(--s2); } }

/* sections: number + caps title under an ink rule */
.k-sec { display: flex; flex-direction: column; gap: var(--s1); min-width: 0; }
.k-sec-h { display: flex; align-items: baseline; gap: var(--s3); border-top: 1px solid var(--ink); padding-top: var(--s3); margin-bottom: var(--s2); }
.k-sec-h h2, .k-sec-h h3 { margin: 0; font-size: var(--fs-label); font-weight: 500; letter-spacing: .12em; text-transform: uppercase; text-wrap: balance; }
.k-no { flex: none; font: var(--fs-label) var(--mono); color: var(--muted); }  /* none: a long title never squeezes „01“ into two lines */
.k-sec-a { margin-left: auto; font-size: var(--fs-table); display: inline-flex; align-items: center; min-height: var(--target); margin-block: calc((1lh - var(--target)) / 2); }
.k-block { display: flex; flex-direction: column; min-width: 0; }
.k-block-h { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: var(--s2) var(--s4); padding-bottom: var(--s2); }
.k-block-h h3 { margin: 0; font-size: var(--fs-block); font-weight: 500; text-wrap: balance; }
.k-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(420px, 100%), 1fr)); gap: var(--s7) var(--s7); }
/* tiles: a grid of equal small cards/lines (Organigramm, Skills, Abgleich; M4); narrowest column 260 px, .narrow 220, .wide 280 */
.k-grid.tiles { grid-template-columns: repeat(auto-fill, minmax(min(260px, 100%), 1fr)); gap: 0 var(--s6); }
.k-grid.tiles.narrow { grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr)); }
.k-grid.tiles.wide { grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr)); }
.k-split { display: flex; flex-wrap: wrap; gap: var(--s7) var(--s7); align-items: flex-start; }
.k-split > .k-main { flex: 999 1 520px; min-width: 0; display: flex; flex-direction: column; gap: var(--s6); }
.k-split > .k-aside { flex: 1 1 300px; min-width: 0; display: flex; flex-direction: column; gap: var(--s6); }
.k-split > .k-sec { gap: var(--s1); }

/* ── Kacheln (08.10.2026, Marcel: "die Seiten sind besser strukturiert durch die Kacheln", as in the Brain) ──────────
   k-ico      an icon of the registry (ui_icons.icon(key), _docs/designsystem/icons.md): 20 px line, currentColor
   k-tiles    the tile grid: 1 column on phones, 2 from 768 px, 3 from 1024 px, 4 from 1440 px (.three/.two cap it;
              .fit keeps 5–6 key figures in one row; a fixed key-figure grid: .k-tiles.kpis in a .k-kpi-wrap)
   k-tile     a tile: optional icon + title, a key figure or status, one short sentence, the action at the foot; the
              whole tile is the link (<a class="k-tile">, or a div with one a.k-tile-a stretched over it); a hairline
              box on the surface, square like everything else (radius 0 — the Brain's tiles are square too)
   k-card     a section card: title (+ icon, + link) over a list, lines or a chart — the boxed k-sec of a start page
   The entry page (Einstiegsseite) = k-head, then key-figure tiles, then the area tiles, then the cards. */
.k-ico { display: block; flex: none; }
.k-tiles { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s4); }
@media (min-width: 768px) { .k-tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1024px) { .k-tiles:where(:not(.two)) { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1440px) { .k-tiles:where(:not(.two, .three)) { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
/* .fit: a key-figure row of 5–6 tiles stays one row where it fits (narrowest tile 180 px) */
.k-tiles.fit { grid-template-columns: repeat(auto-fit, minmax(min(180px, 100%), 1fr)); }
/* no orphan rows (09.10.2026, Feedback Team Lead at 1024 px wrapped 3+1): 4 tiles go 4 → 2×2, 5 → 3+2, 6 → 3+3 instead
   of leaving one tile alone in the last row. .fit: one row of n where n × 180 px fits, else --fit-h columns, one column
   below 2 × 180 px; the column minimum switches with the *999 clamp trick (no container query needed). The grid: 4 at
   the 3-column width → 2×2, 5 at the 4-column width → 3+2. Specificity stays that of the base rules (:where), so an
   app's own grid (.k-tiles.kpis) still wins; browsers without :has() keep the rules above. */
.k-tiles.fit:where(:has(> :nth-child(4):last-child)) { --fit-n: 4; --fit-h: 2; }
.k-tiles.fit:where(:has(> :nth-child(5):last-child)) { --fit-n: 5; --fit-h: 3; }
.k-tiles.fit:where(:has(> :nth-child(6):last-child)) { --fit-n: 6; --fit-h: 3; }
.k-tiles.fit:where(:has(> :nth-child(4):last-child), :has(> :nth-child(5):last-child), :has(> :nth-child(6):last-child)) {
  grid-template-columns: repeat(auto-fill, minmax(max(
    calc((100% - (var(--fit-n) - 1) * var(--s4)) / var(--fit-n) - 0.1px),
    min(calc((100% - (var(--fit-h) - 1) * var(--s4)) / var(--fit-h) - 0.1px),
        calc((var(--fit-n) * 180px + (var(--fit-n) - 1) * var(--s4) - 100%) * 999)),
    min(100%, calc((2 * 180px + var(--s4) - 100%) * 999))), 1fr)); }
@media (min-width: 1024px) and (max-width: 1439px) {
  .k-tiles:where(:not(.two, .fit):has(> :nth-child(4):last-child)) { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1440px) {
  .k-tiles:where(:not(.two, .three, .fit):has(> :nth-child(5):last-child)) { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.k-tile { position: relative; display: flex; flex-direction: column; gap: var(--s2); min-width: 0; min-height: var(--target); box-sizing: border-box;
  padding: var(--s5); background: var(--surface); border: 1px solid var(--line-strong); color: var(--ink); text-decoration: none;
  transition: border-color var(--dur-fast); }
a.k-tile:hover, .k-tile:has(.k-tile-a:hover) { border-color: var(--ink); }
a.k-tile:focus-visible, .k-tile:has(.k-tile-a:focus-visible) { outline: var(--focus-width) solid var(--ink); outline-offset: var(--focus-offset); }
.k-tile-a { color: inherit; text-decoration: none; }
.k-tile-a::after { content: ''; position: absolute; inset: 0; }
.k-tile-a:focus-visible { outline: none; }
.k-tile-h { display: flex; align-items: center; gap: var(--s2); margin: 0; font-size: var(--fs-lead); font-weight: 500; line-height: 1.3; text-wrap: balance; hyphens: manual; }
.k-tile-h .k-ico { color: var(--muted); }
a.k-tile:hover .k-tile-h .k-ico, .k-tile:has(.k-tile-a:hover) .k-tile-h .k-ico { color: var(--ink); }
.k-tile-n { font-family: var(--serif); font-weight: 300; font-size: var(--fs-kpi); line-height: 1; font-variant-numeric: tabular-nums; }
.k-tile-d { margin: 0; font-size: var(--fs-table); color: var(--muted); text-wrap: pretty; }
.k-tile-f { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s2) var(--s3); margin-top: auto; padding-top: var(--s2);
  font-size: var(--fs-table); }
.k-tile-go { display: inline-flex; align-items: center; gap: var(--s1); color: var(--muted); }
/* the action sits on the tile's left edge (guidelines §9), right of a status when there is one */
.k-tile-f > * + .k-tile-go { margin-left: auto; }
@media (max-width: 1023px) { .k-tile-f > * + .k-tile-go { margin-left: 0; } }
.k-tile-go .k-ico { transition: transform var(--dur-fast); }
a.k-tile:hover .k-tile-go, .k-tile:has(.k-tile-a:hover) .k-tile-go { color: var(--ink); }
a.k-tile:hover .k-tile-go .k-ico, .k-tile:has(.k-tile-a:hover) .k-tile-go .k-ico { transform: translateX(3px); }
/* a key-figure tile (the Brain's KPI row): the label small on top, the number, one line what it counts */
.k-tile.kpi .k-tile-h { font-size: var(--fs-text); font-weight: 400; color: var(--muted); }
.k-tile.kpi .k-tile-n { margin-top: var(--s1); }
.k-card { display: flex; flex-direction: column; gap: var(--s3); min-width: 0; box-sizing: border-box; padding: var(--s5);
  background: var(--surface); border: 1px solid var(--line-strong); }
.k-card-h { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s2) var(--s4); }
.k-card-h h2, .k-card-h h3 { display: flex; align-items: center; gap: var(--s2); margin: 0; font-size: var(--fs-block); font-weight: 500; text-wrap: balance; }
.k-card-h .k-ico { color: var(--muted); align-self: center; }
.k-card-h .k-link { min-height: var(--target); margin-block: calc((1lh - var(--target)) / 2); }
.k-card > .k-list > .k-li:last-child, .k-card > div > .k-line:last-child { border-bottom: 0; }
@media (max-width: 767px) {
  .k-tile, .k-card { padding: var(--s4); }
  .k-tile-n { font-size: var(--fs-kpi-sm); }
}

/* facts: label | value rows */
.k-facts { margin: 0; display: grid; grid-template-columns: minmax(120px, 180px) minmax(0, 1fr); font-size: var(--fs-text); }
.k-facts dt, .k-facts dd { margin: 0; padding: var(--s3) 0; border-top: 1px solid var(--line); }
.k-facts dt { color: var(--muted); padding-right: var(--s4); }
.k-facts dt:nth-last-of-type(1), .k-facts dd:last-child { border-bottom: 1px solid var(--line); }
.k-facts .k-dim { color: var(--muted); }
@media (max-width: 767px) {
  .k-facts { grid-template-columns: minmax(0, 1fr); }
  .k-facts dt { padding-bottom: 0; font-size: var(--fs-hint); }
  .k-facts dd { border-top: 0; padding-top: 2px; }
  .k-facts dt:nth-last-of-type(1) { border-bottom: 0; }
}

/* tables */
.k-scroll { overflow-x: auto; position: relative; }  /* relative: a k-sr (absolute) inside stays in the scroller instead of widening the page (layout check 08.10.2026) */
/* a table that does not fit fades at the cut edge, so a cut number never reads as the whole number (design panel M3;
   classes set in portal/base.html like k-tabs) */
.k-scroll.more-r { -webkit-mask-image: linear-gradient(to right, var(--mask-solid) calc(100% - 40px), transparent); mask-image: linear-gradient(to right, var(--mask-solid) calc(100% - 40px), transparent); }
.k-scroll.more-l { -webkit-mask-image: linear-gradient(to left, var(--mask-solid) calc(100% - 40px), transparent); mask-image: linear-gradient(to left, var(--mask-solid) calc(100% - 40px), transparent); }
.k-scroll.more-l.more-r { -webkit-mask-image: linear-gradient(to right, transparent, var(--mask-solid) 40px, var(--mask-solid) calc(100% - 40px), transparent);
  mask-image: linear-gradient(to right, transparent, var(--mask-solid) 40px, var(--mask-solid) calc(100% - 40px), transparent); }
.k-table { width: 100%; border-collapse: collapse; font-size: var(--fs-table); line-height: var(--lh); }
/* break-word, not the page's anywhere: only words that cannot fit break, so a column never shrinks to a few letters ("Senio r Devel oper", layout check 08.10.2026) */
.k-table { overflow-wrap: break-word; }
.k-table th[scope="col"] { text-align: left; padding: 0 var(--s3) var(--s2); font-size: var(--fs-caps); font-weight: 500; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted); border-bottom: 1px solid var(--ink); white-space: nowrap; }
.k-table td, .k-table th[scope="row"] { padding: var(--row-y) var(--s3); border-bottom: 1px solid var(--line); vertical-align: top; text-align: left; }
.k-table th[scope="row"] { font-weight: 500; }
/* the column heads stay in view while a long table scrolls with the page (density 11.10.2026; from 1024 px — on phones
   the top bar would cover them); in a scrolling box (k-scroll) they stay put, which changes nothing */
@media (min-width: 1024px) {
  .k-table > thead th[scope="col"] { position: sticky; top: 0; z-index: var(--z-raised); background: var(--bg); }
  :is(.k-card, .k-sheet, .k-tile, .k-callout) .k-table > thead th[scope="col"] { background: var(--surface); }
}
/* a text link in a cell keeps its 44 px target but takes one line of the row: the first lines of a row on one height
   (layout check "cell-top", 10.10.2026) */
.k-table td .k-link, .k-table th .k-link, .k-table td .k-help > summary { margin-block: calc((1lh - var(--target)) / 2); }
.k-table a.k-link { text-decoration: underline; }  /* like the button links beside it (Bearbeiten · Belege senden · Bank lesen) */
.k-table th[scope="row"] > a { white-space: nowrap; }
.k-table tr > :first-child { padding-left: 0; } .k-table tr > :last-child { padding-right: 0; }
.k-table .num { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.k-table th[scope="col"].num { font-family: var(--sans); text-align: right; }
.k-table .act, .k-table th[scope="col"].act { text-align: right; white-space: nowrap; }  /* the head of an action column sits over its links */
.k-table .nowrap { white-space: nowrap; }
/* many number columns (months): tighter cells so a year fits next to the side bar */
.k-table.dense td, .k-table.dense th { padding-left: var(--s2); padding-right: var(--s2); }
.k-table.dense tr > :first-child { padding-left: 0; } .k-table.dense tr > :last-child { padding-right: 0; }
.k-table a { text-decoration: none; } .k-table a:hover { text-decoration: underline; text-underline-offset: 3px; }
.k-table tbody tr.k-row { position: relative; cursor: pointer; }
.k-table tbody tr.k-row:hover { background: var(--hover); }
.k-table tbody tr.k-row a.k-stretch::after { content: ''; position: absolute; inset: 0; }
.k-sm { display: block; font-size: var(--fs-hint); color: var(--muted); font-weight: 400; }
.k-mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.k-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; }  /* visually hidden; the kit scrollers (k-scroll, k-tabs, k-chips, k-ph-kpis) are position:relative so it never escapes a horizontal scroller and widens the page (Zeit /auszug, Abnahme 13.10.2026) */
.k-table tr.k-dim td, .k-table tr.k-dim th { color: var(--muted); }

/* key figures: numbers in GT Alpina separated by rules */
.k-kpis { display: flex; flex-wrap: wrap; column-gap: var(--s6); }
.k-kpi { flex: 1 1 124px; display: flex; flex-direction: column; gap: var(--s1); padding: var(--s4) 0 var(--s4); border-top: 1px solid var(--line); color: var(--ink); text-decoration: none; min-width: 0; }
a.k-kpi:hover .k-kpi-l { color: var(--ink); text-decoration: underline; }
.k-kpi-n { font-family: var(--serif); font-weight: 300; font-size: var(--fs-kpi); line-height: 1; font-variant-numeric: tabular-nums; }
.k-kpi-l { font-size: var(--fs-caps); font-weight: 500; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.k-big { margin: 0; font-family: var(--serif); font-weight: 300; font-size: var(--fs-display); line-height: 1; }

/* status: a square plus a word */
.k-st { display: inline-flex; align-items: baseline; gap: var(--s2); }
.k-table .k-st { white-space: nowrap; }
.k-st::before { content: ''; width: 8px; height: 8px; flex: none; box-sizing: border-box; background: var(--mark); transform: translateY(-1px); }
.k-st.act::before { background: var(--alert); }
.k-st.wait::before { background: transparent; border: 1px solid var(--ink); }
.k-st.ok::before { background: var(--ok); }
.k-st.off { color: var(--muted); }
.k-tag { display: inline-flex; align-items: center; min-height: 20px; padding: 0 var(--s2); border: 1px solid var(--mark); border-radius: 999px; font-size: var(--fs-label); color: var(--muted); white-space: nowrap; }

/* lists of things to do / items */
.k-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
/* a row reads left to right on one line: [mark] · title + context · end (date, amount, badge, actions). The columns
   come from the content, not from a class (Marcel 10.10.2026, "nicht sehr aligned": a .plain row with a mark put the
   title at the far right and the date under the mark): a 16 px column only when there is a k-mark, the title in the
   last column of the template, every part after it a column of its own on the same line (grid-auto-flow: column).
   .plain changes nothing any more (kept for old markup). */
.k-li { display: grid; grid-template-columns: minmax(0, 1fr); grid-auto-columns: auto; grid-auto-flow: column; gap: var(--s1) var(--s3); align-items: baseline; padding: var(--s4) 0; border-bottom: 1px solid var(--line); }
.k-li:has(> .k-mark) { grid-template-columns: 16px minmax(0, 1fr); }
.k-li > .k-mark { grid-area: 1 / 1; }
.k-li > .k-li-main { grid-row: 1; grid-column: -2 / -1; }
/* narrow (phone, the side column of k-cols, a sheet): an end that holds a badge, a button or actions moves under the
   title, on its left edge, so the title keeps its width; a date or an amount stays at the right on the first line */
@media (max-width: 767px) {
  .k-li > :is(.k-actions, .z-acts, form), .k-li > .k-li-when:has(.k-badge, .k-btn, .k-actions, form) { grid-row: 2; grid-column: -2 / -1; justify-self: start; white-space: normal; }
}
@media (min-width: 1024px) {
  .k-cols > :last-child .k-li > :is(.k-actions, .z-acts, form), .k-cols > :last-child .k-li > .k-li-when:has(.k-badge, .k-btn, .k-actions, form) { grid-row: 2; grid-column: -2 / -1; justify-self: start; white-space: normal; }
}
.k-sheet .k-li > :is(.k-actions, .z-acts, form), .k-sheet .k-li > .k-li-when:has(.k-badge, .k-btn, .k-actions, form) { grid-row: 2; grid-column: -2 / -1; justify-self: start; white-space: normal; }
.k-li-main { display: flex; flex-direction: column; gap: var(--s1); min-width: 0; }
.k-li-title { font-size: var(--fs-lead); font-weight: 500; text-decoration: none; color: var(--ink); }
a.k-li-title:hover { text-decoration: underline; text-underline-offset: 3px; }
.k-li.k-row { position: relative; }
.k-li.k-row:hover { background: var(--hover); box-shadow: -12px 0 0 var(--hover), 12px 0 0 var(--hover); }
.k-li.k-row:hover a.k-stretch { text-decoration: underline; text-underline-offset: 3px; }
.k-li.k-row a.k-stretch::after { content: ''; position: absolute; inset: 0; }
.k-li.k-row a.k-stretch:focus-visible { outline: none; } .k-li.k-row:focus-within { outline: var(--focus-width) solid var(--ink); outline-offset: -2px; }
.k-go { display: inline-block; margin-left: var(--s1); color: var(--muted); transition: transform var(--dur-fast); }
.k-li.k-row:hover .k-go { color: var(--ink); transform: translateX(3px); }
.k-li-ctx { font-size: var(--fs-table); color: var(--muted); }
.k-li-when { font: var(--fs-hint) var(--mono); color: var(--muted); white-space: nowrap; }
.k-mark { display: block; width: 8px; height: 8px; margin-top: var(--s2); box-sizing: border-box; background: var(--mark); }
.k-mark.act { background: var(--alert); } .k-mark.off { background: transparent; border: 1px dashed var(--mark); } .k-mark.wait { background: transparent; border: 1px solid var(--ink); } .k-mark.ok { background: var(--ok); }
.k-line { display: flex; justify-content: space-between; gap: var(--s3); padding: var(--s3) 0; border-bottom: 1px solid var(--line); font-size: var(--fs-text); color: var(--ink); text-decoration: none; }
a.k-line:hover span:first-child { text-decoration: underline; text-underline-offset: 3px; }
.k-line > :first-child { flex-shrink: 0; max-width: 50%; }  /* the label never squeezes to letters beside a long value (M4) */
.k-line .k-dim, .k-dim { color: var(--muted); }
/* phones (guidelines §9, Marcel 08.10.2026 "auf mobile bitte die textausrichtung optimieren"): a label | value line stacks —
   label above, value below, both on the content edge (like k-facts); a link line (a.k-line: a row of a list) keeps its
   short meta at the end of the row, flush right also when it wraps, never as ragged text in the right half */
@media (max-width: 767px) {
  div.k-line, span.k-line { flex-direction: column; gap: 2px; }
  div.k-line > :first-child, span.k-line > :first-child { max-width: none; }
  a.k-line > :last-child:not(:first-child) { min-width: 0; text-align: right; }
}

/* actions */
.k-btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--s2); min-height: var(--target); padding: 0 var(--s4); border: 0;
  background: var(--accent); color: var(--accent-ink); font: 500 var(--fs-table) var(--sans); text-decoration: none; cursor: pointer; white-space: nowrap; }
.k-btn:hover { background: var(--accent-hover); }
.k-btn.dark { background: var(--ink); color: var(--bg); } .k-btn.dark:hover { opacity: .9; }
.k-btn.line { background: transparent; color: var(--ink); border: 1px solid var(--mark); font-weight: 400; padding: 0 var(--s4); }
.k-btn.line:hover { border-color: var(--ink); }
.k-btn.small { min-height: var(--target); padding: 0 var(--s3); font-size: var(--fs-hint); }
.k-btn:active { opacity: .85; } .k-btn[disabled] { opacity: .4; cursor: default; }
.k-link { display: inline-flex; align-items: center; min-height: var(--target); padding: 0; background: none; border: 0; color: var(--ink); font: var(--fs-table) var(--sans);
  text-decoration: underline; text-underline-offset: 3px; cursor: pointer; }
.k-link.quiet { color: var(--muted); } .k-link.quiet:hover { color: var(--ink); }
.k-link.bad { color: var(--alert-ink); }
.k-inline { display: inline; margin: 0; }

/* fields: an underline on paper, label above */
.k-form { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(220px, 100%), 1fr)); gap: var(--s4) var(--s5); }
.k-form .wide { grid-column: 1 / -1; }
.k-field { display: flex; flex-direction: column; gap: var(--s1); font-size: var(--fs-hint); font-weight: 500; color: var(--ink); min-width: 0; }
.k-field input:not([type="checkbox"]):not([type="radio"]), .k-field select, .k-field textarea {
  box-sizing: border-box; width: 100%; min-width: 0; height: var(--target); padding: 0; border: 0; border-bottom: 1px solid var(--ink); border-radius: 0;
  background: transparent; color: var(--ink); font: var(--fs-table) var(--sans); }  /* P2 13.10.2026: control text 14 (16 below 1024, base.css) */
.k-field textarea { height: auto; min-height: 64px; padding: var(--s2) 0; resize: vertical; }
.k-field select { cursor: pointer; }
.k-field input:focus, .k-field select:focus, .k-field textarea:focus { outline: none; border-bottom-width: 2px; }
.k-field input:focus-visible, .k-field select:focus-visible, .k-field textarea:focus-visible { outline: var(--focus-width) solid var(--ink); outline-offset: 4px; }
.k-field .mono, .k-field input[type="date"], .k-field input[inputmode="decimal"] { font-family: var(--mono); }
.k-field input[disabled], .k-field select[disabled] { border-bottom-style: dashed; color: var(--muted); }
.k-hint { font-size: var(--fs-hint); font-weight: 400; color: var(--muted); }
.k-field.bad input, .k-field.bad select, .k-field.bad textarea { border-bottom: 2px solid var(--alert); }
.k-field.bad .k-hint { color: var(--alert-ink); }
.k-check { display: flex; gap: var(--s2); align-items: center; min-height: var(--target); font-size: var(--fs-table); font-weight: 400; }
.k-chips > .k-check { white-space: nowrap; }  /* a row of short choices (50 % · 80 % · 100 %): box and word stay together (P2) */
.k-fieldset { margin: 0; padding: 0; border: 0; min-width: 0; display: flex; flex-direction: column; gap: var(--s1); }  /* min-width 0: a fieldset is min-content wide by default, a scrolling k-chips row inside widened the page (Abnahme 13.10.2026) */
.k-fieldset legend { font-size: var(--fs-hint); font-weight: 500; padding: 0 0 var(--s1); }

/* <trinsik-dialog> (/ui/v1/dialog.js): the one dialog — a sheet (k-sheet) or a question (k-dialog); the element itself
   draws nothing (no gap in flex/grid parents), dialog.js checks this rule to know components.css is loaded */
trinsik-dialog { display: contents; }

/* the sheet: the one frame — opens from the right; without script via ?blatt= */
.k-sheet { position: fixed; inset: 0 0 0 auto; width: min(560px, 100vw); height: 100%; max-height: 100%; max-width: 100vw; margin: 0; padding: 0;
  border: 0; border-left: 1px solid var(--ink); background: var(--surface); color: var(--ink); box-sizing: border-box; z-index: var(--z-sheet); }
.k-sheet[open] { display: flex; flex-direction: column; }
.k-sheet::backdrop { background: var(--scrim); }
.k-sheet-h { display: flex; justify-content: space-between; align-items: center; gap: var(--s3); padding: var(--s4) var(--s4) var(--s4) var(--s5); border-bottom: 1px solid var(--line); }
.k-sheet-h h2 { margin: 0; font-size: var(--fs-block); font-weight: 500; text-wrap: balance; }
.k-sheet-x { width: var(--target); height: var(--target); display: inline-flex; align-items: center; justify-content: center; color: var(--ink); text-decoration: none; font-size: 24px; line-height: 1; background: none; border: 0; cursor: pointer; }
.k-sheet-b { flex: 1; overflow-y: auto; padding: var(--s5) var(--s5); display: flex; flex-direction: column; gap: var(--s4); }
.k-sheet-f { display: flex; flex-wrap: wrap; gap: var(--s3); align-items: center; padding: var(--s4) var(--s5) var(--s5); border-top: 1px solid var(--line); }
.k-sheet form { display: contents; }
.k-sheet-err { padding: var(--s3) var(--s5) 0; }  /* the error of a failed save, in the modal that opened again */
@media (max-width: 767px) { .k-sheet-err { padding-left: var(--s4); padding-right: var(--s4); } }
@media (max-width: 767px) { .k-sheet { width: 100vw; border-left: 0; } .k-sheet-b, .k-sheet-f { padding-left: var(--s4); padding-right: var(--s4); } .k-sheet-h { padding-left: var(--s4); } }

/* the question (trinsikDialog.ask, instead of confirm()): a hairline frame in the middle, the scrim of the sheets; optional
   text (p) and a typed confirmation (k-field) */
.k-dialog { box-sizing: border-box; width: min(440px, calc(100vw - 32px)); margin: auto; padding: var(--s5); border: 1px solid var(--ink);
  background: var(--surface); color: var(--ink); }
.k-dialog::backdrop { background: var(--scrim); }
.k-dialog h2 { margin: 0; font-size: var(--fs-block); font-weight: 500; line-height: 1.4; text-wrap: balance; }
.k-dialog .k-actions { justify-content: flex-end; margin-top: var(--s5); }
.k-dialog p { margin: var(--s2) 0 0; font-size: var(--fs-table); color: var(--muted); text-wrap: pretty; white-space: pre-line; }
/* phones: the two answers share the whole width, the safe one left (guidelines §9) */
@media (max-width: 767px) { .k-dialog .k-actions { flex-wrap: nowrap; } .k-dialog .k-actions > * { flex: 1 1 0; min-width: 0; } }
.k-dialog .k-field { margin-top: var(--s4); } .k-dialog .k-field input { font-family: var(--mono); }
.k-btn.bad { background: var(--alert); color: var(--surface); } .k-btn.bad:hover { background: var(--alert-ink); text-decoration: underline; text-underline-offset: 3px; }  /* was opacity .9: 4,2:1 in light */

/* the error page k-error (portal/error.html, M4; 403/404/500 in every app): code as eyebrow, what happened as title, a
   hint, start page + back; bare pages (signed out, the Brain) carry the app mark s-name above the head */
.k-error { min-height: 60vh; } .k-error-mark { align-self: flex-start; }

/* messages, empty */
.k-msg { margin: 0; display: flex; align-items: baseline; gap: var(--s2); font-size: var(--fs-text); padding: var(--s3) 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }
.k-msg::before { content: ''; width: 8px; height: 8px; flex: none; background: var(--ok); transform: translateY(-1px); }
.k-msg:empty { display: none; }
.k-msg.bad { color: var(--alert-ink); } .k-msg.bad::before { background: var(--alert); }
.k-empty { margin: 0; padding: var(--s3) 0; color: var(--muted); font-size: var(--fs-text); display: flex; flex-wrap: wrap; gap: var(--s1) var(--s4); align-items: baseline; text-align: left; text-wrap: pretty; }
.k-note { margin: 0; font-size: var(--fs-table); color: var(--muted); max-width: 767px; text-wrap: pretty; }
.k-agent { display: grid; gap: var(--s1); padding: var(--s3) 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); font-size: var(--fs-text); }
.k-plain { margin: var(--s1) 0 0; padding-left: var(--s4); }
.k-table td.k-pre-cell { white-space: pre-wrap; }
.k-agent ul { margin: 0; padding-left: var(--s4); color: var(--muted); font-size: var(--fs-table); }

/* tabs and filters */
.k-tabs { display: flex; gap: var(--s1); border-bottom: 1px solid var(--line); overflow-x: auto; position: relative; }
.k-tabs a { display: inline-flex; align-items: center; min-height: var(--target); padding: 0 var(--s3); white-space: nowrap; text-decoration: none; color: var(--muted);
  border-bottom: 2px solid transparent; margin-bottom: -1px; font-size: var(--fs-table); }
.k-tabs a:first-child { padding-left: 0; }
.k-tabs a:hover { color: var(--ink); } .k-tabs a.on { color: var(--ink); border-bottom-color: var(--ink); font-weight: 500; }
/* tabs and chips that do not fit scroll; one fade at the cut edge says that more follow (classes set in portal/base.html,
   P5 13.10.2026: the same for tab rows, sub-tabs and chip rows that scroll on a phone) */
.k-tabs { scrollbar-width: thin; }
:is(.k-tabs, .k-chips).more-r { -webkit-mask-image: linear-gradient(to right, var(--mask-solid) calc(100% - 56px), transparent); mask-image: linear-gradient(to right, var(--mask-solid) calc(100% - 56px), transparent); }
:is(.k-tabs, .k-chips).more-l { -webkit-mask-image: linear-gradient(to left, var(--mask-solid) calc(100% - 56px), transparent); mask-image: linear-gradient(to left, var(--mask-solid) calc(100% - 56px), transparent); }
:is(.k-tabs, .k-chips).more-l.more-r { -webkit-mask-image: linear-gradient(to right, transparent, var(--mask-solid) 56px, var(--mask-solid) calc(100% - 56px), transparent);
  mask-image: linear-gradient(to right, transparent, var(--mask-solid) 56px, var(--mask-solid) calc(100% - 56px), transparent); }
/* the record's tabs in groups (Vertrag & Gehalt, Entwicklung …): a hairline between groups, the group name above */
.k-tabs.k-tg-row { align-items: flex-end; gap: 0; }
.k-tg { display: flex; flex-direction: column; margin-left: var(--s1); padding-left: var(--s1); border-left: 1px solid var(--line); }
.k-tg-l { padding: var(--s1) var(--s2) 0; font-size: var(--fs-caps); letter-spacing: .04em; text-transform: uppercase; color: var(--muted); white-space: nowrap; }
.k-tg-a { display: flex; }
.k-tg-row a { padding: 0 var(--s2); }
.k-tabs .k-tg-a a:first-child { padding-left: var(--s2); }
.k-tabs.k-tg-row > a:first-child { padding-left: 0; }
/* below 1400 px the group names give way to the tabs (only the hairline stays); the groups stay for screen readers */
@media (max-width: 1439px) { .k-tg-l { display: none; } .k-tg { margin-left: var(--s1); padding-left: var(--s1); } }
.k-tools { display: flex; flex-wrap: wrap; gap: var(--s3) var(--s5); align-items: flex-end; }
.k-search { flex: 0 1 380px; }
.k-chips { display: flex; flex-wrap: wrap; gap: var(--s2); position: relative; }
.k-chip { display: inline-flex; align-items: center; min-height: var(--target); padding: 0 var(--s4); border: 1px solid var(--mark); border-radius: 999px; color: var(--ink); text-decoration: none; font-size: var(--fs-hint); }
.k-chip:hover { border-color: var(--ink); } .k-chip.on, .k-chip[aria-current="true"], .k-chip[aria-current="page"], .k-chip[aria-pressed="true"] { background: var(--ink); color: var(--bg); border-color: var(--ink); }
/* links inside a scrolling row: the focus ring sits inside, so overflow does not cut it (design panel A9) */
/* smart filters with counts (Welle 0, 10.10.2026, Moco „Überfällig (6)“): the chips are the work queue. k-chip-n = how many
   rows the filter shows (mono, muted); .act = they need someone (filled pill: shape, not colour alone); .zero = nothing waits. */
.k-chip-n { margin-left: var(--s2); font-family: var(--mono); font-size: var(--fs-label); font-variant-numeric: tabular-nums; color: var(--muted); }
.k-chip.on .k-chip-n, .k-chip[aria-current] .k-chip-n { color: inherit; }
.k-chip-n.act { min-width: 20px; padding: 0 var(--s1); line-height: 18px; text-align: center; background: var(--ink); color: var(--bg); border-radius: var(--radius-pill); }
.k-chip.on .k-chip-n.act, .k-chip[aria-current] .k-chip-n.act { background: var(--bg); color: var(--ink); }
.k-chip.zero, .k-chip.soft { color: var(--muted); border-style: dashed; }
/* P5 (13.10.2026) — the chips of Reporting and Zeit moved into the kit (their own chip classes are gone, tools/ui_lint.py):
   .ico   a chip with a leading mark or icon (★ favourite, ⟳ „wie gestern“): the gap between them
   .soft  a quiet chip (dashed, muted): a filter that only shows its default value
   .k-chip-x   the × inside a removable chip (an active filter: „Kunde: Acme ×“), the chip keeps a small right padding
   .k-chip-in  a hidden radio/checkbox before a span.k-chip (a choice inside a GET form): the chip shows the state */
.k-chip.ico { gap: var(--s2); }
.k-chip:has(> .k-chip-x) { gap: var(--s1); padding-right: var(--s1); max-width: 100%; white-space: nowrap; }
.k-chip > b { font-weight: 500; min-width: 0; max-width: 220px; overflow: hidden; text-overflow: ellipsis; }
.k-chip-x { display: inline-flex; align-items: center; justify-content: center; flex: none; width: 24px; height: 24px; font-size: var(--fs-lead); line-height: 1; color: var(--muted); text-decoration: none; }
.k-chip-x:hover { color: var(--ink); }
.k-chip-x:focus-visible { outline: var(--focus-width) solid var(--ink); outline-offset: calc(-1 * var(--focus-width)); }
label:has(> .k-chip-in) { position: relative; display: inline-flex; flex: none; }
.k-chip-in { position: absolute; left: 0; top: 0; width: 1px; height: 1px; margin: 0; opacity: 0; pointer-events: none; }
.k-chip-in + .k-chip { cursor: pointer; white-space: nowrap; }
.k-chip-in:checked + .k-chip { background: var(--ink); color: var(--bg); border-color: var(--ink); }
.k-chip-in:focus-visible + .k-chip { outline: var(--focus-width) solid var(--ink); outline-offset: var(--focus-offset); }
.k-tabs a:focus-visible, .k-chips .k-chip:focus-visible { outline-offset: calc(-1 * var(--focus-width)); }
.k-count { margin: 0; font-size: var(--fs-hint); color: var(--muted); }

/* the record head */
.k-rec { display: flex; flex-wrap: wrap; gap: var(--s4) var(--s5); align-items: center; }
.k-ava { position: relative; width: 48px; height: 48px; flex: none; overflow: hidden; display: flex; align-items: center; justify-content: center;
  background: var(--line); font: 300 20px var(--serif); }
.k-ava img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.k-rec-text { display: flex; flex-direction: column; gap: var(--s1); flex: 1 1 320px; min-width: 0; }
.k-rec-meta { margin: 0; display: flex; flex-wrap: wrap; gap: 2px var(--s4); font-size: var(--fs-text); color: var(--muted); }

/* bars: progress and salary band */
.k-bar { position: relative; display: block; height: 8px; background: var(--line); }
.k-bar > span { position: absolute; left: 0; top: 0; bottom: 0; background: var(--ink); }
.k-band { position: relative; height: 32px; margin: var(--s2) 0 var(--s1); }
.k-band-rail { position: absolute; left: 0; right: 0; top: 14px; height: 4px; background: var(--line); }
.k-band-third { position: absolute; top: 10px; width: 1px; height: 12px; background: var(--mark); }
.k-band-dot.old { background: var(--bg); border: 1px solid var(--ink); box-sizing: border-box; width: 8px; margin-left: -4px; }
.k-band-dot { position: absolute; top: 6px; width: 4px; height: 20px; margin-left: -2px; background: var(--ink); }
.k-band-legend { display: flex; justify-content: space-between; font: var(--fs-hint) var(--mono); color: var(--muted); }

/* day boxes (planning grid and absence calendar; Marcel 08.10.2026: nothing "unaligned"): one box per day,
   height --plan-row − 2 × --plan-inset, columns --plan-gap apart. States change fill, an outline drawn inside
   (outline-offset, never a border) and a word — never the height or the inset. */
/* calendar grid (absences): approved absence = ink box, requested = light box with a dashed outline inside, half a
   day = ink box with "½", sick = ink box with a 2 px alert edge (the word is in k-sr/title), weekend and holiday = the
   muted box, free = the empty box. Today: a 2 px Senf bar under the column head and the head in ink. */
.k-cal { border-collapse: separate; border-spacing: var(--plan-gap) 0; margin-left: calc(-1 * var(--plan-gap)); font-size: var(--fs-label); }
.k-cal th, .k-cal td { padding: 0; text-align: center; }
.k-cal thead th { font: var(--fs-caps) var(--mono); color: var(--muted); padding: 0 0 var(--s2); width: var(--plan-day); min-width: var(--plan-day); }
.k-cal thead th.today, .k-cal thead th[aria-current="date"] { color: var(--ink); font-weight: 700; box-shadow: inset 0 -2px 0 var(--plan-today); }
.k-cal tbody th { text-align: left; font-size: var(--fs-table); font-weight: 400; padding: 0 var(--s4) 0 0; white-space: nowrap; position: sticky; left: 0; z-index: var(--z-raised); background: var(--bg); }
.k-card .k-cal tbody th { background: var(--surface); }  /* QA wave 6: the sticky month column in the card's colour, not the page's */
.k-cal td { position: relative; height: var(--plan-row); }
.k-cal td::before { content: ''; position: absolute; inset: var(--plan-inset) 0; background: var(--plan-slot); box-shadow: inset 0 0 0 1px var(--line); }
.k-cal .we::before, .k-cal .hol::before { background: var(--plan-weekend); box-shadow: none; }
.k-cal .abs::before, .k-cal .half::before, .k-cal .sick::before { background: var(--plan-booked); box-shadow: none; }
.k-cal .sick::before { box-shadow: inset 0 -2px 0 var(--plan-conflict); }
.k-cal .req::before { background: var(--plan-tentative-fill); box-shadow: none; outline: 1px dashed var(--plan-tentative); outline-offset: -1px; }
.k-cal .half::after { content: '½'; position: relative; color: var(--plan-booked-ink); font: 500 var(--fs-table) var(--sans); }

/* planning grid <trinsik-plan> (spec: _docs/designsystem/trinsik-plan.md; plan.js is built in P1 on these classes).
   .k-plan scrolls sideways; the name column sticks. Per row two layers on the same column template: k-plan-slots
   (one box per day: free / we / hol) and k-plan-bars (bookings and absences placed with grid-column: <day> / span <n>,
   one grid row per lane). Default zoom "Monat": --plan-day per day; .fill (zoom 1W/2W) shares the width. */
.k-plan { --days: 31; --plan-col: var(--plan-day); overflow-x: auto; border-top: 1px solid var(--ink); font-size: var(--fs-hint); }
.k-plan.fill { --plan-col: minmax(var(--plan-day), 1fr); }
.k-plan-row { display: grid; grid-template-columns: var(--plan-who) max-content; border-bottom: 1px solid var(--line); min-width: 100%; }
.k-plan.fill .k-plan-row { grid-template-columns: var(--plan-who) minmax(0, 1fr); }
.k-plan-who { position: sticky; left: 0; z-index: var(--z-raised); display: flex; flex-direction: column; justify-content: center; gap: 2px;
  min-width: 0; padding: var(--plan-inset) var(--s3) var(--plan-inset) 0; background: var(--bg); }
.k-plan-who b { font-weight: 500; font-size: var(--fs-table); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.k-plan-who span { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.k-plan-who .k-plan-flag { color: var(--alert-ink); }
.k-plan-days { padding: var(--plan-inset) 0; }
.k-plan-bars, .k-plan-head .k-plan-days, .k-plan-bal .k-plan-days { display: grid; grid-template-columns: repeat(var(--days), var(--plan-col)); column-gap: var(--plan-gap); }
/* every item has grid-column and grid-row (lane): day boxes first, bars after them on top — a second lane repeats
   the day boxes, so no box is ever taller than the others */
.k-plan-bars { grid-auto-rows: calc(var(--plan-row) - 2 * var(--plan-inset)); row-gap: var(--plan-gap); }
.k-plan-slot { background: var(--plan-slot); box-shadow: inset 0 0 0 1px var(--line); }
.k-plan-slot.we, .k-plan-slot.hol { background: var(--plan-weekend); box-shadow: none; }
/* one box for every state: same height, inset and baseline; text truncates with "…", the whole text is in title/aria-label */
.k-plan-bar { container-type: inline-size; position: relative; min-width: 0; overflow: hidden; text-decoration: none; color: var(--ink); }
.k-plan-in { display: flex; align-items: center; gap: var(--s1); height: 100%; padding: 0 var(--s2); white-space: nowrap; line-height: 1.3; }
.k-plan-in > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.k-plan-bar .k-plan-h, .k-plan-bar .k-plan-w { flex: none; font-variant-numeric: tabular-nums; }
.k-plan-bar .k-plan-l + .k-plan-h::before, .k-plan-bar .k-plan-h + .k-plan-w::before { content: '· '; }
.k-plan-bar.fix { background: var(--plan-booked); color: var(--plan-booked-ink); }
.k-plan-bar.tent { background: linear-gradient(var(--plan-tentative-fill), var(--plan-tentative-fill)), var(--plan-slot); outline: 1px dashed var(--plan-tentative); outline-offset: -1px; }
.k-plan-bar.abs { background: repeating-linear-gradient(135deg, var(--plan-absence) 0 1px, transparent 1px 6px), var(--plan-slot); color: var(--muted); box-shadow: inset 0 0 0 1px var(--line); }
.k-plan-bar.abs .k-plan-l { background: var(--plan-slot); padding: 0 var(--s1); }
/* project colours (palette A "Erde und Tinte", Marcel 08.10.2026: Funktion vor Design): only in the planning grid and its legend.
   Markup: class "c1"…"c12" (ui_kit.cats) on a bar or a legend chip. fest = the colour with its label colour;
   vorläufig = the same colour in stripes, the dashed outline inside, the label on a paper patch; absence stays the
   neutral hatching, überbucht the alert edge + hours — the states read the same on every colour. */
.k-plan .c1, .k-plan-key .c1 { --cat: var(--cat-1); --cat-ink: var(--cat-1-ink); }
.k-plan .c2, .k-plan-key .c2 { --cat: var(--cat-2); --cat-ink: var(--cat-2-ink); }
.k-plan .c3, .k-plan-key .c3 { --cat: var(--cat-3); --cat-ink: var(--cat-3-ink); }
.k-plan .c4, .k-plan-key .c4 { --cat: var(--cat-4); --cat-ink: var(--cat-4-ink); }
.k-plan .c5, .k-plan-key .c5 { --cat: var(--cat-5); --cat-ink: var(--cat-5-ink); }
.k-plan .c6, .k-plan-key .c6 { --cat: var(--cat-6); --cat-ink: var(--cat-6-ink); }
.k-plan .c7, .k-plan-key .c7 { --cat: var(--cat-7); --cat-ink: var(--cat-7-ink); }
.k-plan .c8, .k-plan-key .c8 { --cat: var(--cat-8); --cat-ink: var(--cat-8-ink); }
.k-plan .c9, .k-plan-key .c9 { --cat: var(--cat-9); --cat-ink: var(--cat-9-ink); }
.k-plan .c10, .k-plan-key .c10 { --cat: var(--cat-10); --cat-ink: var(--cat-10-ink); }
.k-plan .c11, .k-plan-key .c11 { --cat: var(--cat-11); --cat-ink: var(--cat-11-ink); }
.k-plan .c12, .k-plan-key .c12 { --cat: var(--cat-12); --cat-ink: var(--cat-12-ink); }
.k-plan-bar.fix.cat { background: var(--cat); color: var(--cat-ink); }
.k-plan-bar.tent.cat { background: repeating-linear-gradient(135deg, var(--cat) 0 3px, transparent 3px 8px), var(--plan-slot);
  outline: 1px dashed var(--ink); outline-offset: -3px; }
.k-plan-bar.tent.cat .k-plan-in { box-sizing: border-box; width: max-content; max-width: calc(100% - 8px); height: calc(100% - 8px);
  margin: var(--s1) 0 0 var(--s1); padding: 0 var(--s1); background: var(--plan-slot); color: var(--ink); }
.k-plan-key { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); margin: 0; padding: var(--s2) 0; list-style: none; font-size: var(--fs-hint); }
.k-plan-key li { display: inline-flex; align-items: center; gap: var(--s2); }
.k-plan-key .cat { width: 16px; height: 16px; background: var(--cat); flex: none; }
@media (forced-colors: active) { .k-plan-bar.fix.cat { background: CanvasText; color: Canvas; } .k-plan-key .cat { border: 1px solid CanvasText; } }
/* überbucht: a 2 px alert edge at the foot of the box over the overbooked days, never a fill; the 1 px surface gap above
   keeps it readable on the orange-brown project colours of palette A */
.k-plan-over { align-self: end; height: 2px; background: var(--plan-conflict); box-shadow: 0 -1px 0 var(--plan-slot); z-index: var(--z-raised); }
/* a short bar shows only the hours (the label stays in title and aria-label) */
@container (max-width: 88px) { .k-plan-in { justify-content: center; padding: 0 2px; } .k-plan-l, .k-plan-w { display: none; } .k-plan-h::before { content: none !important; } }
@container (max-width: 24px) { .k-plan-in { visibility: hidden; } }  /* a bar of a day or two in the year zoom: no cut-off „4 h“ (the title has it; mobile pass 15.10.2026) */
@container (max-width: 40px) { .k-plan-h { font-size: var(--fs-caps); } }
.k-plan-bar:focus-visible { outline: var(--focus-width) solid var(--ink); outline-offset: 1px; }
/* head: week row and day row; today = a 2 px Senf bar under its head across the column + the head in ink */
.k-plan-head { border-bottom: 1px solid var(--ink); }
.k-plan-head .k-plan-days { padding: 0; align-items: end; }
.k-plan-dh { display: flex; flex-direction: column; align-items: center; justify-content: flex-end; gap: 2px; padding: var(--s1) 0 var(--s2); font: var(--fs-caps) var(--mono); color: var(--muted); white-space: nowrap; overflow: hidden; }
.k-plan-dh.we, .k-plan-dh.hol { color: var(--muted); }
.k-plan-dh.today, .k-plan-dh[aria-current="date"] { color: var(--ink); font-weight: 700; box-shadow: inset 0 -2px 0 var(--plan-today); }
.k-plan-wk { font: var(--fs-caps) var(--mono); color: var(--muted); padding-top: var(--s2); border-left: 1px solid var(--line); padding-left: var(--s1); white-space: nowrap; overflow: hidden; }
.k-plan-grp { display: flex; align-items: center; gap: var(--s2); min-height: var(--plan-row); border-bottom: 1px solid var(--line); font-weight: 500; font-size: var(--fs-label); letter-spacing: .12em; text-transform: uppercase; }
/* balance per week, under the person: Mono, aligned to the week's columns */
.k-plan-bal .k-plan-days { padding: var(--s1) 0; }
.k-plan-bal .k-plan-days > span { text-align: center; font-variant-numeric: tabular-nums; white-space: nowrap; }
.k-plan-bal .free { color: var(--plan-free); } .k-plan-bal .full { color: var(--plan-full); }
.k-plan-bal .over { color: var(--alert-ink); } .k-plan-bal .over::before { content: ''; display: inline-block; width: 8px; height: 8px; margin-right: var(--s1); background: var(--plan-conflict); }
.k-plan-note { margin: 0 0 var(--s2); font-size: var(--fs-hint); color: var(--muted); }
/* phones: read only, a narrow name column, the note above the grid */
.k-plan.compact { --plan-who: 112px; }
.k-plan.compact .k-plan-who > span:not(.k-plan-flag) { display: none; }
@media (max-width: 767px) { .k-plan { --plan-who: 112px; } .k-plan .k-plan-who > span:not(.k-plan-flag) { display: none; } }

@media (max-width: 1023px) { .k-page, .k-page.wide { padding: var(--s5) var(--s4) var(--s6); gap: var(--s6); } .k-kpis { column-gap: var(--s5); } .k-kpi-n { font-size: var(--fs-kpi-sm); } }

/* checklist tick and the weekday hours row */
.k-tick { width: var(--target); height: var(--target); flex: none; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--ink);
  background: transparent; color: var(--ink); font-size: var(--fs-block); cursor: pointer; }
.k-tick.on, .k-tick[aria-pressed="true"] { background: var(--ink); color: var(--bg); }
.k-check-row { display: grid; grid-template-columns: var(--target) minmax(0, 1fr) auto; gap: var(--s3); align-items: center; padding: var(--s2) 0; border-bottom: 1px solid var(--line); }
@media (max-width: 767px) {  /* QA wave 6: on a phone the status goes under the text (a long badge squeezed the title letter by letter) */
  .k-check-row { grid-template-columns: var(--target) minmax(0, 1fr); row-gap: var(--s1); }
  .k-check-row > :nth-child(3) { grid-column: 2; justify-self: start; } }
.k-done { text-decoration: line-through; color: var(--muted); }
.k-days { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--s3); }
.k-days .k-field input { text-align: center; font-family: var(--mono); }
.k-amend { list-style: none; margin: 0; padding: 0; }
.k-amend li { display: grid; grid-template-columns: 100px minmax(0, 1fr); gap: var(--s1) var(--s4); padding: var(--s3) 0; border-bottom: 1px solid var(--line); font-size: var(--fs-table); }
.k-amend time { font: var(--fs-hint) var(--mono); color: var(--muted); }

/* searchable select (static/ui/combo.js): a text field with a listbox below; the native select stays for the form */
.k-combo { position: relative; min-width: 0; }
.k-combo-native { position: absolute !important; width: 1px !important; height: 1px !important; opacity: 0; pointer-events: none; left: 0; bottom: 0; }
.k-combo-input { padding-right: var(--s6) !important; cursor: text; }
.k-combo::after { content: ''; position: absolute; right: 12px; top: calc(var(--target) / 2 - 2px); width: 7px; height: 7px; border-right: 1.5px solid var(--muted);
  border-bottom: 1.5px solid var(--muted); transform: rotate(45deg); pointer-events: none; }
.k-field .k-combo::after { right: 4px; }
.k-combo.open::after { transform: translateY(4px) rotate(-135deg); }
.k-combo-list { position: absolute; left: 0; right: 0; top: calc(100% + 4px); z-index: var(--z-popover); margin: 0; padding: var(--s1) 0; list-style: none; max-height: 288px;
  overflow-y: auto; background: var(--surface); border: 1px solid var(--ink); }
.k-combo-opt { display: flex; flex-direction: column; gap: 1px; padding: var(--s2) var(--s3); cursor: pointer; font-size: var(--fs-table); color: var(--ink); }
.k-combo-opt.on { background: var(--line); box-shadow: inset var(--focus-width) 0 0 var(--ink); }  /* the active option must be visible to keyboard users (2.4.7; --line alone is 1.2:1) */
.k-combo-opt[aria-selected="true"] { font-weight: 500; }
/* the word is for the eye; screen readers get aria-selected (empty alt text, older browsers keep the first content) */
.k-combo-opt[aria-selected="true"]::after { content: 'gewählt'; content: 'gewählt' / ''; font-size: var(--fs-caps); font-weight: 400; color: var(--muted); }
.k-combo-sub { font-size: var(--fs-label); color: var(--muted); font-weight: 400; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.k-combo-empty { padding: var(--s2) var(--s3); font-size: var(--fs-table); color: var(--muted); }

/* short native selects: same chevron, no system look */
.k-field select { appearance: none; -webkit-appearance: none; padding-right: var(--s5);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 13px) 50%, calc(100% - 8px) 50%; background-size: 5px 5px; background-repeat: no-repeat; }
.k-field select { background-position: calc(100% - 9px) 50%, calc(100% - 4px) 50%; }

/* phone: secondary table columns hide, filter chips scroll in one row */
@media (max-width: 767px) {
  .k-table .hide-sm { display: none; }
  .k-chips { flex-wrap: nowrap; overflow-x: auto; margin: 0 -16px; padding: 0 var(--s4) var(--s1); scrollbar-width: none; }
  .k-chips::-webkit-scrollbar { display: none; }
  .k-chip { flex: none; }
  .k-head .k-actions { width: 100%; }
}

/* the sub-pages of a detail page as a tab row, only on the phone (there the left navigation sits behind „Mehr“) */
.k-phone-tabs { display: none; }
@media (max-width: 767px) { .k-phone-tabs { display: flex; margin-bottom: var(--s4); } }

/* a form that is being sent (portal/base.html): the button waits, the form says it is busy */
form[aria-busy="true"] button[type="submit"], form[aria-busy="true"] .k-btn { cursor: progress; }

/* apps on paper (portal start, admin): an ink rule, a square letter, name and one line — no card boxes */
.k-apps { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr)); gap: var(--s2) var(--s6); }
.k-app { display: grid; grid-template-columns: 44px minmax(0, 1fr); gap: var(--s1) var(--s4); align-items: start; padding: var(--s4) 0 var(--s4);
  border-top: 1px solid var(--ink); color: var(--ink); text-decoration: none; min-width: 0; }
.k-app-icon { grid-row: span 2; width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; background: var(--ink);
  color: var(--bg); font: 19px var(--mono); }
.k-app-name { font-size: var(--fs-block); font-weight: 500; }
.k-app-desc { font-size: var(--fs-table); color: var(--muted); }
a.k-app:hover .k-app-name { text-decoration: underline; text-underline-offset: 3px; }

/* Portal › Admin › Rechte: a matrix of groups (vertical names) × rights */
.k-matrix th.g { padding: 0 var(--s1) var(--s2); vertical-align: bottom; text-transform: none; letter-spacing: normal; font-size: var(--fs-label); }
.k-matrix th.g span { writing-mode: vertical-rl; transform: rotate(180deg); white-space: nowrap; }
.k-matrix td.c { text-align: center; padding: var(--s1) var(--s1); }
.k-matrix tr.area td { padding: var(--s5) 0 var(--s2); font-size: var(--fs-label); font-weight: 500; letter-spacing: .12em; text-transform: uppercase; color: var(--muted); }
.k-matrix th[scope="row"] { font-weight: 400; min-width: 280px; }
.k-matrix .def { display: block; font-size: var(--fs-label); color: var(--muted); }
/* k-matrix.sticky (QA wave 6, Portal › Rechte): the first column stays while the groups scroll sideways; narrower on a phone */
.k-matrix.sticky th[scope="row"], .k-matrix.sticky thead th:first-child { position: sticky; left: 0; z-index: var(--z-raised); background: var(--surface); box-shadow: 1px 0 0 var(--line); }
/* … and the group names stay on top (Marcel 16.10.2026): the matrix scrolls inside its own box, the head sticks to its
   top; the corner cell above both */
.k-scroll:has(> .k-matrix.sticky) { max-height: calc(100dvh - 220px); overflow: auto; }
.k-matrix.sticky thead th { position: sticky; top: 0; z-index: var(--z-raised); background: var(--surface); box-shadow: 0 1px 0 var(--line); }
.k-matrix.sticky thead th:first-child { z-index: calc(var(--z-raised) + 1); box-shadow: 1px 1px 0 var(--line); }
@media (max-width: 767px) { .k-matrix.sticky th[scope="row"] { min-width: 150px; max-width: 170px; overflow-wrap: anywhere; }
  .k-matrix.sticky tr.area td { padding-left: var(--s4); } }
/* P7 (13.10.2026): on a phone the boxes of the matrix are 24 px (WCAG 2.5.8) and a note (.k-matrix-sm, only there) says
   that the matrix is meant for the desktop */
.k-matrix-sm { display: none; }
@media (max-width: 767px) { .k-matrix-sm { display: block; } .k-matrix td.c input[type="checkbox"] { width: 24px; height: 24px; } }
.k-pre { white-space: pre-wrap; overflow-wrap: anywhere; margin: 0; padding: var(--s3) 0; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); font-size: var(--fs-text); }
.k-shot { max-width: 100%; max-height: 520px; border: 1px solid var(--line); display: block; }

/* a whole-page form inside the page (Persönlich) */
.k-pageform { display: flex; flex-direction: column; gap: var(--s6); }
.k-pageform.dirty > .k-actions:last-child { position: sticky; bottom: 0; z-index: var(--z-float); background: var(--bg); border-top: 1px solid var(--ink); padding: var(--s3) 0; }
.k-unsaved { font-size: var(--fs-hint); color: var(--alert-ink); }
@media (max-width: 1023px) { .k-pageform.dirty > .k-actions:last-child { bottom: calc(var(--bar-h) + env(safe-area-inset-bottom, 0px)); } }
.k-field input[readonly] { border-bottom-style: dashed; color: var(--muted); }

/* numbers that explain themselves on mouse-over (Profitabilität, Berichte: title=), negative amounts, sum rows */
.k-tip { cursor: help; }
.k-neg { color: var(--alert-ink); }
.k-table tr.k-sum > th, .k-table tr.k-sum > td { font-weight: 500; }
.k-table tfoot tr.k-sum > * { border-top: 1px solid var(--ink); }
.k-table td strong { display: block; font-weight: 500; }
.k-table tr.k-sub-row > td { padding-top: var(--s1); padding-bottom: var(--s1); font-size: var(--fs-hint); }
.k-table tr.k-sub-row > td:first-child { padding-left: var(--s4); }
.k-tall { max-height: 640px; overflow: auto; }
/* a whole year in one table next to the side bar (Profitabilität): 13 px, tighter cells */
.k-table.small { font-size: var(--fs-hint); }
.k-table.small td, .k-table.small th { padding-left: var(--s1); padding-right: var(--s1); }
.k-table.small tr > :first-child { padding-left: 0; } .k-table.small tr > :last-child { padding-right: 0; }
.k-sticky-head thead th { position: sticky; top: 0; z-index: var(--z-raised); background: var(--bg); }
.k-head-notes { display: flex; flex-direction: column; gap: var(--s1); margin-top: calc(-1 * var(--s6)); }
.k-adjust { display: flex; flex-direction: column; gap: var(--s4); width: min(280px, 80vw); padding: var(--s3) 0; text-align: left; }
/* running text pages (Anleitung): lists and paragraphs on paper */
.k-prose .k-sec p, .k-prose .k-sec > ul { max-width: 760px; margin: 0; font-size: var(--fs-text); }
.k-prose .k-sec > ul { padding-left: var(--s4); display: flex; flex-direction: column; gap: var(--s1); }
.k-prose .k-sec p + ul { margin-top: var(--s2); }


/* ── 08.10.2026 (design panel #14, gaps before P3/P4): Zeitraum, Seitenwechsel, Mehrfachauswahl, Upload ─────────────
   Markup: macros k.period, k.pages, k.pick / k.pick_all / k.bulk_bar, k.upload (portal/_k.html); behaviour without
   script stays usable, /ui/v1/kit.js adds checks, counts and drag & drop; server: _brain/ui_kit.py. */

/* Zeitraum-Auswahl: presets as chips (one row, scroll on phones) + „Von – bis“ folded; the range in words below */
.k-period { display: flex; flex-direction: column; gap: var(--s2); min-width: 0; }
.k-period-row { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
.k-period-free { position: relative; }
.k-period-free > summary { list-style: none; cursor: pointer; }
.k-period-free > summary::-webkit-details-marker { display: none; }
.k-period-free > summary:focus-visible { outline: var(--focus-width) solid var(--ink); outline-offset: 2px; }
.k-period-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--s3) var(--s5); padding: var(--s3) 0 0; }
.k-period-form .k-field { min-width: 150px; }
.k-period-form .k-hint { flex-basis: 100%; }
.k-period-now { margin: 0; font-size: var(--fs-hint); color: var(--muted); font-family: var(--mono); }
/* P7 (13.10.2026): on a phone the presets and „Von – bis“ stay in one row (the presets scroll, „Von – bis“ stays at
   the end); opened, the dates take a line of their own */
@media (max-width: 767px) {
  .k-period-row { flex-wrap: nowrap; }
  .k-period-row > .k-chips { flex: 0 1 auto; min-width: 0; margin-right: 0; }
  .k-period-row > .k-period-free { flex: none; }
  .k-period-row:has(> .k-period-free[open]) { flex-wrap: wrap; }
  .k-period-free[open] { flex-basis: 100%; }
}

/* Seitenwechsel: Zurück · 1 … 4 5 6 … 20 · Weiter, the rows shown in words; 44 px targets */
.k-pages { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s4); padding: var(--s3) 0; border-top: 1px solid var(--line); }
.k-pages ol { display: flex; flex-wrap: wrap; gap: var(--s1); margin: 0; padding: 0; list-style: none; }
.k-pages a, .k-pages [aria-current], .k-pages .gap { display: inline-flex; align-items: center; justify-content: center; min-width: var(--target);
  min-height: var(--target); box-sizing: border-box; font: var(--fs-table) var(--mono); color: var(--ink); text-decoration: none; }
.k-pages ol a:hover { text-decoration: underline; text-underline-offset: 3px; }
.k-pages [aria-current="page"] { background: var(--ink); color: var(--bg); }
.k-pages .gap { color: var(--muted); min-width: var(--s5); }
.k-pages .k-pages-step { font-family: var(--sans); padding: 0 var(--s2); text-decoration: underline; text-underline-offset: 3px; }
.k-pages .k-pages-step[aria-disabled="true"] { color: var(--muted); text-decoration: none; cursor: default; }
.k-pages .k-count { margin-left: auto; }
@media (max-width: 767px) { .k-pages .k-count { margin-left: 0; flex-basis: 100%; } }  /* phones: the count on the content edge (§9) */

/* Mehrfachauswahl: a 44 px checkbox column in front of the rows, a bar with the count and the actions */
.k-pick { display: inline-flex; align-items: center; justify-content: center; width: var(--target); min-height: var(--target); margin: calc(-1 * var(--s2)) 0; cursor: pointer; }
.k-pick input { width: 18px; height: 18px; margin: 0; accent-color: var(--ink); cursor: pointer; }
.k-table th.k-pick-cell, .k-table td.k-pick-cell { width: var(--target); padding-left: 0; padding-right: 0; text-align: center; }
/* the whole row, the row-header cell (th scope=row) too (W8-H: Zeit › Stundenauszug had a white th in a grey row) */
.k-table tr:has(.k-pick input:checked) > :is(td, th) { background: var(--hover); }
.k-bulkbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s4); margin: 0; padding: var(--s2) 0; min-height: var(--target);
  border-top: 1px solid var(--ink); background: var(--bg); position: sticky; bottom: 0; z-index: var(--z-float); }
.k-bulkbar[hidden] { display: none; }
.k-bulk-n { margin: 0 auto 0 0; font-size: var(--fs-table); font-weight: 500; }
@media (max-width: 1023px) { .k-bulkbar { bottom: calc(var(--bar-h) + env(safe-area-inset-bottom, 0px)); } }

/* k-table.cards (P7, 13.10.2026): below 768 px a list table becomes row blocks on hairlines – paper, no box, no radius,
   no shadow (README §1). Line one: the title (k-c-main) left, the action (k-c-act, always visible, also with hide-sm) or
   else the status (k-c-st) right; then the sub line (k-c-sub) and every other cell on a line of its own, its column name
   (td[data-label]) small and muted in front, on the one left edge (§9). Generalised from Projekte's project list
   (static/projekte/project-list.css .pl). From 768 px nothing changes. */
@media (max-width: 767px) {
  .k-table.cards, .k-table.cards > tbody, .k-table.cards > tfoot { display: block; }
  .k-table.cards > thead { display: none; }
  .k-table.cards > tbody > tr, .k-table.cards > tfoot > tr { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: var(--s1) var(--s3);
    padding: var(--s3) 0; border-bottom: 1px solid var(--line); }
  .k-table.cards > tbody > tr > *, .k-table.cards > tfoot > tr > * { grid-column: 1 / -1; display: block; min-width: 0; padding: 0; border: 0; text-align: left; }
  .k-table.cards > tbody > tr > .k-c-main, .k-table.cards > tfoot > tr > .k-c-main { grid-column: 1; grid-row: 1; font-weight: 500; }
  .k-table.cards > tbody > tr > .k-c-sub { font-size: var(--fs-hint); color: var(--muted); }
  .k-table.cards > tbody > tr > .k-c-act { grid-column: 2; grid-row: 1; display: block; text-align: right; }
  .k-table.cards > tbody > tr:not(:has(> .k-c-act:not(:empty))) > .k-c-st, .k-table.cards > tfoot > tr > .k-c-st { grid-column: 2; grid-row: 1; text-align: right; }
  .k-table.cards > tbody > tr > [data-label]::before, .k-table.cards > tfoot > tr > [data-label]::before { content: attr(data-label);
    margin-right: var(--s2); font: 400 var(--fs-hint) var(--sans); color: var(--muted); letter-spacing: normal; text-transform: none; }
  .k-table.cards > tbody > tr > td:empty, .k-table.cards > tfoot > tr > td:empty,
  .k-table.cards > tbody > tr > .hide-sm:not(.k-c-act), .k-table.cards > tfoot > tr > .hide-sm { display: none; }
  .k-table.cards > tfoot > tr { border-top: 1px solid var(--ink); border-bottom: 0; }
  /* in a card edge to edge (k-card.flush) the block takes the card's inner edge, its cells none of their own */
  .k-card.flush .k-table.cards > tbody > tr, .k-card.flush .k-table.cards > tfoot > tr { padding-left: var(--s5); padding-right: var(--s5); }
  .k-card.flush .k-table.cards > tbody > tr > *, .k-card.flush .k-table.cards > tfoot > tr > *,
  .k-card.flush > .k-scroll .k-table.cards > tbody > tr > *, .k-card.flush > .k-scroll .k-table.cards > tfoot > tr > * { padding: 0; }
  .k-card.flush .k-table.cards > tbody > tr:last-child { border-bottom: 0; }
}

/* k-cards-sort (13.10.2026): below 768 px the head of a k-table.cards falls away and with it sorting by a click on a
   column; a sortable list puts this group (label.k-field with a select of the sort columns, a k-chip ↑/↓ for the
   direction) next to its filters – the same sort state as the head. From 768 px the head sorts and the group is hidden. */
.k-cards-sort { display: none; }
@media (max-width: 767px) { .k-cards-sort { display: flex; align-items: flex-end; gap: var(--s2); } }

/* k-table.sticky1 (P7, 13.10.2026): a data grid (balances, months, people × weeks) keeps its first column while the rest
   scrolls sideways in k-scroll (its edge fade stays); a hairline marks the edge, no shadow. Like k-matrix.sticky, on the
   page's paper (--bg), in a card or sheet on --surface. Every width. */
.k-table.sticky1 tr > :first-child { position: sticky; left: 0; z-index: var(--z-raised); background: var(--bg); box-shadow: inset -1px 0 0 var(--line);
  padding-right: var(--s3); }
:is(.k-card, .k-sheet, .k-tile, .k-callout) .k-table.sticky1 tr > :first-child { background: var(--surface); }
.k-table.sticky1 tbody tr.k-row:hover > :first-child { background: var(--hover); }
.k-table.sticky1 > thead tr > :first-child { z-index: calc(var(--z-raised) + 1); }  /* the corner stays above the column below it (sticky head ≥1024) */

/* Upload: the field, a drop zone around it (with script), the chosen files, progress and the message */
.k-upload { display: flex; flex-direction: column; gap: var(--s2); min-width: 0; }
.k-upload-zone { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s4); padding: var(--s4); border: 1px dashed var(--mark); }
.k-upload-zone input[type="file"] { font: var(--fs-table) var(--sans); color: var(--ink); max-width: 100%; border: 0; background: transparent; padding: 0; }
.k-upload-zone input[type="file"]:focus-visible { outline: var(--focus-width) solid var(--ink); outline-offset: var(--focus-offset); }
.k-upload-zone .k-upload-drop { margin: 0; font-size: var(--fs-hint); color: var(--muted); }
.k-upload.over .k-upload-zone { border-style: solid; border-color: var(--ink); background: var(--hover); }
.k-upload.bad .k-upload-zone { border: 2px solid var(--alert); }
.k-upload-files { margin: 0; padding: 0; list-style: none; font: var(--fs-hint) var(--mono); color: var(--ink); }
.k-upload-files:empty { display: none; }
.k-upload-bar { width: 100%; height: 4px; appearance: none; -webkit-appearance: none; border: 0; background: var(--line); }
.k-upload-bar::-webkit-progress-bar { background: var(--line); }
.k-upload-bar::-webkit-progress-value { background: var(--ink); }
.k-upload-bar::-moz-progress-bar { background: var(--ink); }
.k-upload-bar[hidden] { display: none; }
.k-upload-msg { margin: 0; font-size: var(--fs-hint); color: var(--alert-ink); }
.k-upload-msg:empty { display: none; }
@media (forced-colors: active) { .k-pages [aria-current="page"] { background: Highlight; color: HighlightText; } .k-upload.over .k-upload-zone { border-color: Highlight; } }

@media print {
  .s-side, .s-top, .s-bottom, trinsik-report { display: none !important; }
  .k-tall { max-height: none !important; overflow: visible !important; }
  .k-page { padding: 0; }
}

/* spacing helpers (migration M4: they replace the inline style="margin…/gap…" of the shell apps; steps of the --s* scale only,
   !important because they stand in for an inline style and must win over the component rule) */
.k-mt1 { margin-top: var(--s1) !important; } .k-mt2 { margin-top: var(--s2) !important; } .k-mt3 { margin-top: var(--s3) !important; } .k-mt4 { margin-top: var(--s4) !important; } .k-mt5 { margin-top: var(--s5) !important; } .k-mt6 { margin-top: var(--s6) !important; }
.k-mb1 { margin-bottom: var(--s1) !important; } .k-mb2 { margin-bottom: var(--s2) !important; } .k-mb3 { margin-bottom: var(--s3) !important; } .k-mb4 { margin-bottom: var(--s4) !important; } .k-mb5 { margin-bottom: var(--s5) !important; } .k-mb6 { margin-bottom: var(--s6) !important; }
.k-gap1 { gap: var(--s1) !important; } .k-gap2 { gap: var(--s2) !important; } .k-gap3 { gap: var(--s3) !important; } .k-gap4 { gap: var(--s4) !important; } .k-gap5 { gap: var(--s5) !important; } .k-gap6 { gap: var(--s6) !important; }
.k-py2 { padding-top: var(--s2) !important; padding-bottom: var(--s2) !important; } .k-py4 { padding-top: var(--s4) !important; padding-bottom: var(--s4) !important; }
.k-unit { font: var(--fs-text) var(--sans); color: var(--muted); }  /* the unit after a k-big number (M4) */

/* ── 09.10.2026 (M4 notes, additive): filter bar with „Mehr Filter“ k-filters, status sequence k-steps ──────────────
   k-filters: the first chips stay in the row (rule ≤ 5), the rest folds into <details class="k-filters-more">; opened, it
   takes its own line below and its chips wrap (no popover, so nothing is clipped by a scrolling chip row on phones).
   An active chip behind „Mehr Filter“ names itself on the summary (.on). k-steps: Entwurf → Freigabe → versendet as an
   ordered list – done = filled square, now = thick ink frame + medium weight + aria-current="step", open = thin hollow,
   off (verloren, storniert) = struck through; the word always says it (never colour alone), no Senf (one accent). */
.k-filters { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); min-width: 0; }
.k-filters > .k-chips { min-width: 0; }
.k-filters .k-chip { white-space: nowrap; }
.k-filters-more { flex: none; max-width: 100%; }
.k-filters-more > summary { list-style: none; cursor: pointer; gap: var(--s2); white-space: nowrap; width: max-content; max-width: 100%; }
.k-filters-more > summary::-webkit-details-marker { display: none; }
.k-filters-more > summary::after { content: ''; width: 6px; height: 6px; border: solid currentColor; border-width: 0 1px 1px 0; transform: translateY(-2px) rotate(45deg); }
.k-filters-more[open] > summary::after { transform: translateY(1px) rotate(-135deg); }
.k-filters-more > summary:focus-visible { outline: var(--focus-width) solid var(--ink); outline-offset: 2px; }
.k-filters-more[open] { flex-basis: 100%; }
.k-filters-more > .k-chips { padding-top: var(--s2); }
.k-filters-n { color: var(--muted); font-variant-numeric: tabular-nums; }
@media (max-width: 767px) {
  .k-filters > .k-chips { flex: 1 1 100%; }
  .k-filters-more > .k-chips { flex-wrap: wrap; overflow: visible; margin: 0; padding: var(--s2) 0 0; }
}

/* k-filterbar (P7, 13.10.2026): from 768 px the wrapper is display: contents — its filters stand in the row of the
   toolbar as before; below 768 px one chip „Filter · n aktiv“ opens them (a <details>, no script). Browsers without
   ::details-content keep the chip on every width (the filters still open). */
.k-filterbar { min-width: 0; }
.k-filterbar > summary { list-style: none; cursor: pointer; gap: var(--s2); white-space: nowrap; width: max-content; max-width: 100%; }
.k-filterbar > summary::-webkit-details-marker { display: none; }
.k-filterbar > summary::after { content: ''; width: 6px; height: 6px; border: solid currentColor; border-width: 0 1px 1px 0; transform: translateY(-2px) rotate(45deg); }
.k-filterbar[open] > summary::after { transform: translateY(1px) rotate(-135deg); }
.k-filterbar > summary:focus-visible { outline: var(--focus-width) solid var(--ink); outline-offset: 2px; }
.k-filterbar-b { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s3); min-width: 0; }
@supports selector(::details-content) {
  @media (min-width: 768px) {
    .k-filterbar, .k-filterbar::details-content, .k-filterbar-b { display: contents; content-visibility: visible; }
    .k-filterbar > summary { display: none; }
  }
}
@media (max-width: 767px) {
  .k-filterbar { flex: 1 1 100%; }
  .k-filterbar-b { padding-top: var(--s2); }
  .k-filterbar-b .k-chips { flex-wrap: wrap; overflow: visible; margin: 0; }
  .k-filterbar-b > * { max-width: 100%; }
}

.k-steps { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s3); margin: 0; padding: 0; list-style: none; font-size: var(--fs-hint); color: var(--muted); }
.k-steps > li { display: inline-flex; align-items: center; gap: var(--s2); }
.k-steps > li + li::before { content: ''; width: var(--s5); height: 1px; background: var(--line-strong); margin-right: var(--s1); }
.k-step-m { width: 10px; height: 10px; flex: none; box-sizing: border-box; border: 1px solid var(--muted); }
.k-steps > li.done { color: var(--ink); }
.k-steps > li.done .k-step-m { background: var(--ink); border-color: var(--ink); }
.k-steps > li.now { color: var(--ink); font-weight: 500; }
.k-steps > li.now .k-step-m { border: 3px solid var(--ink); }
.k-steps > li.off { text-decoration: line-through; text-decoration-thickness: 1px; }
.k-steps > li.off .k-step-m { border-style: dashed; }
@media (forced-colors: active) { .k-steps > li.done .k-step-m { background: CanvasText; } }
/* k-steps.mini (09.10.2026, HR Vorgänge: the phases of each person in a table cell): squares with short links, only the
   current step's word is visible (the others are k-sr words, the whole sequence is the list's title) */
.k-steps.mini { flex-wrap: nowrap; gap: var(--s1); white-space: nowrap; }
.k-steps.mini > li { gap: var(--s1); }
.k-steps.mini > li + li::before { width: var(--s2); margin-right: 0; }

/* key-figure tiles on a fixed grid (09.10.2026, from Reporting's rp-kpis): k-kpi-wrap is the container (its width decides,
   not the window), k-tiles.kpis 4 per row, 2 below 820 px of container; the figure shrinks with its tile (cqi) and never
   wraps, labels break between words only */
.k-kpi-wrap { container-type: inline-size; display: flex; flex-direction: column; gap: var(--s6); min-width: 0; }
.k-tiles.kpis { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s4); }
@container (max-width: 820px) { .k-tiles.kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s3); } }
.k-tiles.kpis .k-tile { container-type: inline-size; overflow-wrap: normal; }
.k-tiles.kpis .k-tile-n { font-size: var(--fs-kpi-sm); font-size: min(var(--fs-kpi-sm), 15cqi); white-space: nowrap; }
.k-tiles.kpis .k-tile-h { hyphens: manual; overflow-wrap: normal; }

/* ── UX v2 (10.10.2026, Marcel: „viel Text ohne Struktur“; _docs/designsystem/UX_V2_2026-10-10.md) ────────────────
   Structure, not identity: same tokens, fonts and colours; work pages are built from boxed panels on the paper (as the
   Brain), every panel has a head with icon + title + count/actions, figures sit in one stats card, facts as a grid of
   label-over-value, states as badges (tinted pill + dot + word). Additive: nothing of v1 changes.
   k-cards     a column of panels (gap --s5)        k-cols      main (2/3) + side (1/3) column of panels
   k-card      (v1) + .flush (table edge to edge), k-card-n (count), k-card-a (actions on the right), k-card-b, k-card-f
   k-stats     one card with 2–6 figures: k-stat > k-stat-l (label) · k-stat-v (value) · k-stat-d (note) · k-bar
   k-props     the facts of a record as a grid: label small over the value, 2–6 per row
   k-badge     state as pill: .ok .act .wait .off .info (+ dot); always a word
   k-menu      „⋯“ more actions: a disclosure with a panel of links/buttons (kit.js: one open, Escape, click outside)
   k-toolbar   one row: search box, segment, menus; the right part (k-toolbar-r) for the count/sum
   k-empty.box an empty panel: icon + one sentence + one action
   k-callout   a standing note that needs attention (hints from rules), boxed with a left rule
   k-sheet.modal  the same <trinsik-dialog> sheet, centred (short focused tasks; rule set in UX_V2 §3) */
.k-cards { display: flex; flex-direction: column; gap: var(--s5); min-width: 0; }
.k-cols { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: var(--s5); align-items: start; }
.k-cols > * { min-width: 0; }
/* k-cols.side (density 11.10.2026): a detail page with a main column and a side column of fixed width (280–360 px) for
   status, facts and short lists — the main column takes the rest of the full width */
@media (min-width: 1024px) { .k-cols.side { grid-template-columns: minmax(0, 1fr) minmax(280px, 360px); } }
@media (max-width: 1023px) { .k-cols { grid-template-columns: minmax(0, 1fr); } }
.k-page.v2 { gap: var(--s5); }
.k-page.v2 > .k-head, .k-page.v2 > .k-ph--list { margin-bottom: var(--s1); }
.k-card-t { display: flex; align-items: center; gap: var(--s2); min-width: 0; }
.k-card-t .k-help.q { margin: 0; }
.k-card-n { font: var(--fs-hint) var(--mono); color: var(--muted); font-weight: 400; font-variant-numeric: tabular-nums; }
.k-card-a { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s3); margin-left: auto; }
.k-card-a .k-link { font-size: var(--fs-table); min-height: var(--target); display: inline-flex; align-items: center; margin-block: calc((1lh - var(--target)) / 2); }
.k-card.flush { padding: 0; gap: 0; }
.k-card.flush > .k-card-h { padding: var(--s2) var(--s5); min-height: 40px; box-sizing: border-box; border-bottom: 1px solid var(--line); }
.k-card.flush > .k-scroll .k-table tr > :first-child, .k-card.flush > .k-table tr > :first-child { padding-left: var(--s5); }
.k-card.flush > .k-scroll .k-table tr > :last-child, .k-card.flush > .k-table tr > :last-child { padding-right: var(--s5); }
.k-card.flush > .k-scroll .k-table th[scope="col"] { padding-top: var(--s3); }
.k-card.flush > .k-scroll .k-table tbody tr:last-child > *, .k-card.flush > .k-table tbody tr:last-child > * { border-bottom: 0; }
.k-card.flush > .k-list, .k-card.flush > .k-empty, .k-card.flush > .k-card-b, .k-card.flush > .k-card-f, .k-card.flush > .k-facts { padding-left: var(--s5); padding-right: var(--s5); }
.k-card.flush > .k-card-f { padding-top: var(--s3); padding-bottom: var(--s3); border-top: 1px solid var(--line); font-size: var(--fs-hint); color: var(--muted); }
.k-card.flush > .k-card-b { padding-top: var(--s4); padding-bottom: var(--s4); display: flex; flex-direction: column; align-items: flex-start; gap: var(--s3); }
.k-card.flush > .k-card-b > .k-props { align-self: stretch; }
.k-card.flush > .k-empty { padding-top: var(--s4); padding-bottom: var(--s4); }
.k-card > .k-list > .k-li:last-child { border-bottom: 0; }
.k-card .k-li { padding: var(--s3) 0; }
.k-card .k-li-title { font-size: var(--fs-text); }
.k-card > .k-facts dt:nth-last-of-type(1), .k-card > .k-facts dd:last-child { border-bottom: 0; }
.k-card > .k-facts dt:first-of-type, .k-card > .k-facts dd:first-of-type { border-top: 0; }
.k-card > .k-facts dt, .k-card > .k-facts dd { padding-top: var(--s2); padding-bottom: var(--s2); }
@media (max-width: 767px) {
  .k-card.flush > .k-card-h { padding: var(--s3) var(--s4); }
  .k-card.flush > .k-scroll .k-table tr > :first-child, .k-card.flush > .k-table tr > :first-child { padding-left: var(--s4); }
  .k-card.flush > .k-scroll .k-table tr > :last-child, .k-card.flush > .k-table tr > :last-child { padding-right: var(--s4); }
  .k-card.flush > .k-list, .k-card.flush > .k-empty, .k-card.flush > .k-card-b, .k-card.flush > .k-card-f, .k-card.flush > .k-facts { padding-left: var(--s4); padding-right: var(--s4); }
}

/* an open section (Redesign W2 11.10.2026, artboards 02/03/05/08; k.card(…, open=True)): no box, no icon, the CAPS head
   on a 1 px dark rule with the count and the link on the right; rows below on hairlines, edge to edge */
.k-card.k-sec--open, .k-card.k-sec--open.flush { padding: 0; gap: 0; background: none; border: 0; }
.k-sec--open > .k-card-h, .k-card.k-sec--open.flush > .k-card-h { min-height: 0; padding: 0 0 var(--s2); border-bottom: 1px solid var(--ink); flex-wrap: nowrap; align-items: baseline; }
.k-sec--open > .k-card-h h2, .k-sec--open > .k-card-h h3 { flex: 1; min-width: 0; font-size: var(--fs-label); font-weight: 500; letter-spacing: .12em; text-transform: uppercase; }
.k-sec--open > .k-card-h .k-card-n { margin-left: auto; font-size: var(--fs-label); letter-spacing: 0; text-transform: none; }
.k-sec--open > .k-card-h .k-link { min-height: 0; margin-block: 0; font-size: var(--fs-table); }
.k-sec--open > .k-card-h .k-card-t { flex: 1; }
.k-card.k-sec--open > :not(.k-card-h) { padding-left: 0; padding-right: 0; }
.k-card.k-sec--open > .k-card-f { padding-top: var(--s2); border-top: 0; font-size: var(--fs-hint); color: var(--muted); }
.k-card.k-sec--open > .k-card-b, .k-card.k-sec--open > .k-empty { padding-top: var(--s3); padding-bottom: var(--s3); }
.k-sec--open > .k-list > .k-li { padding: var(--s3) 0; border-bottom: 1px solid var(--line); }
.k-sec--open > .k-scroll .k-table > thead th[scope="col"] { background: none; }

/* stats: one card, figures side by side with a hairline between them; label on top (sentence case), value, note */
.k-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(176px, 100%), 1fr)); background: var(--surface); border: 1px solid var(--line-strong); min-width: 0; overflow: hidden; }
.k-stat { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding: var(--s3) var(--s5); border-left: 1px solid var(--line); margin-left: -1px; color: var(--ink); text-decoration: none; }
a.k-stat:hover .k-stat-l { color: var(--ink); text-decoration: underline; text-underline-offset: 3px; }
a.k-stat:focus-visible { outline: var(--focus-width) solid var(--ink); outline-offset: calc(-1 * var(--focus-width)); }
.k-stat-l { font-size: var(--fs-hint); color: var(--muted); hyphens: auto; }
.k-stat-v { font-family: var(--serif); font-weight: 300; font-size: var(--fs-stat); line-height: 1.1; font-variant-numeric: tabular-nums; white-space: nowrap; }
.k-stat-v.none { color: var(--muted); font-family: var(--sans); font-size: var(--fs-lead); line-height: calc(var(--fs-stat) * 1.1); }
/* the source line under a stats card or panel: one muted sentence + the „?“ */
.k-src { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); font-size: var(--fs-hint); color: var(--muted); }
.k-stat-d { font-size: var(--fs-hint); color: var(--muted); text-wrap: pretty; }
.k-stat .k-bar { margin-top: var(--s1); }
.k-stat.bad .k-bar > span { background: var(--alert); }
.k-stat.bad .k-stat-v, .k-stat .k-neg { color: var(--alert-ink); }
@media (max-width: 767px) {
  .k-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .k-stat { padding: var(--s3) var(--s4); border-top: 1px solid var(--line); margin-top: -1px; }
  .k-stat { container-type: inline-size; }
  .k-stat-v { font-size: min(20px, 16cqi); }  /* an amount with cents (217.350,00 €) fits the half-width figure */
  .k-stat:last-child:nth-child(odd) { grid-column: 1 / -1; }
}

/* props: the facts of a record, label small over the value, in a grid (record heads, side cards) */
/* density 11.10.2026: a dense strip — 4–8 columns on desktop (narrowest 132 px), label 12 px over a 13 px value */
.k-props { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(132px, 100%), 1fr)); gap: var(--s3) var(--s5); margin: 0; }
.k-props > div { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.k-props dt { font-size: var(--fs-hint); color: var(--muted); }
.k-props dd { margin: 0; font-size: var(--fs-table); color: var(--ink); line-height: var(--lh); }
.k-props.one { grid-template-columns: minmax(0, 1fr); }
@media (max-width: 767px) { .k-props { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* badge: the state of a record as a tinted pill with a dot and the word (never colour alone, WCAG 1.4.1) */
.k-badge { display: inline-flex; align-items: center; gap: 6px; min-height: 20px; padding: 0 var(--s2); border-radius: var(--radius-pill); box-sizing: border-box;
  font: 500 var(--fs-label) var(--sans); white-space: nowrap; color: var(--ink); background: rgb(var(--mark-rgb) / .16); vertical-align: middle; }  /* sans also in a mono place (k-li-when) */
.k-badge::before { content: ''; width: 7px; height: 7px; border-radius: 50%; box-sizing: border-box; background: var(--mark); flex: none; }
.k-badge.ok { background: rgb(var(--ok-rgb) / .18); } .k-badge.ok::before { background: var(--ok); }
.k-badge.act { background: rgb(var(--alert-rgb) / .16); color: var(--alert-ink); } .k-badge.act::before { background: var(--alert); }
.k-badge.wait { background: rgb(var(--alert-rgb) / .08); } .k-badge.wait::before { background: transparent; border: 1.5px solid var(--alert); }
.k-badge.off { background: transparent; border: 1px dashed var(--mark); color: var(--muted); }
.k-badge.info::before { background: var(--ink); }
.k-badges { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }
@media (forced-colors: active) { .k-badge { border: 1px solid CanvasText; } .k-badge::before { background: CanvasText; } }

/* menu „⋯“: more actions behind one button */
.k-menu { position: relative; display: inline-block; }
.k-menu > summary { list-style: none; cursor: pointer; }
.k-menu > summary::-webkit-details-marker { display: none; }
.k-menu:not([open]) > .k-menu-p { display: none; }
.k-chip:has(.k-chip-n) { white-space: nowrap; }
/* the k-sr word inside a count is absolute: without a positioned box around it, it escapes a scrolling chip row and
   widens the page on phones (layout check 10.10.2026) */
.k-chip-n { position: relative; }
.k-menu > summary.on { border-color: var(--ink); font-weight: 500; }
.k-menu[open] > summary { border-color: var(--ink); }
/* k.menu(row=True): the row menu in a table — icon only, no frame; the 44 px target overlaps the cell padding instead of
   making the row taller (QA wave 6) */
.k-menu > summary.k-row-menu { display: inline-flex; align-items: center; justify-content: center; width: var(--target); height: var(--target);
  margin-block: calc((1lh - var(--target)) / 2); color: var(--muted); border: 1px solid transparent; vertical-align: middle; }
.k-menu > summary.k-row-menu:hover, .k-menu[open] > summary.k-row-menu { color: var(--ink); border-color: var(--mark); }
.k-menu > summary:focus-visible { outline: var(--focus-width) solid var(--ink); outline-offset: var(--focus-offset); }
.k-menu-p { position: absolute; z-index: var(--z-popover); top: calc(100% + var(--s1)); right: 0; min-width: 240px; max-width: min(360px, calc(100vw - 2 * var(--s4)));
  display: flex; flex-direction: column; padding: var(--s1) 0; background: var(--surface); border: 1px solid var(--ink); color: var(--ink); text-align: left; }
.k-menu.left .k-menu-p { right: auto; left: 0; }
/* open, popover.js lifts the panel into the top layer (popover="manual") and places it at its button inside the window,
   so no scroller (k-scroll, a card) cuts it and it never leaves the right edge (13.10.2026); this undoes the browser's
   popover box. Without script the rules above place it as before. */
.k-menu-p[popover], .k-help.q > div[popover] { inset: auto; margin: 0; overflow: visible; }
.k-menu-p > a, .k-menu-p > form > button, .k-menu-p > button { display: flex; align-items: center; gap: var(--s2); min-height: var(--target); padding: 0 var(--s4); width: 100%; box-sizing: border-box;
  font: inherit; font-size: var(--fs-table); color: var(--ink); text-decoration: none; text-align: left; background: none; border: 0; cursor: pointer; white-space: nowrap; }
.k-menu-p > a:hover, .k-menu-p > form > button:hover, .k-menu-p > button:hover { background: var(--hover); }
.k-menu-p > a:focus-visible, .k-menu-p > form > button:focus-visible, .k-menu-p > button:focus-visible { outline: var(--focus-width) solid var(--ink); outline-offset: calc(-1 * var(--focus-width)); }
.k-menu-p .bad { color: var(--alert-ink); }
.k-menu-p > hr { width: 100%; margin: var(--s1) 0; border: 0; border-top: 1px solid var(--line); }
.k-menu-p > form { display: contents; }
.k-menu-p .k-ico { color: var(--muted); flex: none; }
.k-menu-p.wide { min-width: 340px; padding: var(--s4); gap: var(--s3); }
@media (max-width: 1023px) { .k-menu-p > a, .k-menu-p > form > button, .k-menu-p > button { min-height: var(--target); } }
@media (max-width: 767px) { .k-menu-p, .k-menu-p.wide { position: fixed; left: var(--s4); right: var(--s4); top: auto; max-width: none; min-width: 0; margin-top: var(--s2); } }

/* toolbar: one row above a list */
.k-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s3); min-width: 0; }
.k-toolbar > .k-search-box { flex: 0 1 320px; }
.k-toolbar-r { margin-left: auto; font-size: var(--fs-table); color: var(--muted); }
.k-search-box { display: flex; align-items: center; gap: var(--s2); height: var(--target); padding: 0 var(--s3); border: 0; outline: 1px solid var(--line-strong); outline-offset: -1px; background: var(--surface); box-sizing: border-box; min-width: 0; }
.k-search-box:focus-within { outline-color: var(--ink); }  /* P2 13.10.2026: the frame is an inner outline, so the field inside is --target high too (was 30 in a 32 box) */
.k-search-box .k-ico { color: var(--muted); flex: none; }
.k-search-box input { flex: 1; min-width: 0; align-self: stretch; border: 0; background: none; font: inherit; font-size: var(--fs-table); color: var(--ink); padding: 0; }  /* QA wave 6: the box as high as a button (was 39 px next to 32) */
.k-search-box input:focus { outline: none; }
.k-toolbar .s-seg { flex: none; }
@media (max-width: 767px) { .k-toolbar > .k-search-box, .k-toolbar > .k-field { flex: 1 1 100%; } .k-toolbar-r { margin-left: 0; flex-basis: 100%; } .k-search-box input { font-size: var(--fs-input); } }

/* empty panel: icon + one sentence + one action, left-aligned (guidelines §9) */
.k-empty.box { flex-direction: column; align-items: flex-start; gap: var(--s2); padding: var(--s5); border: 1px dashed var(--line-strong); background: var(--surface); }
.k-empty.box .k-ico { color: var(--mark); }
.k-empty.box > a { font-size: var(--fs-table); color: var(--ink); }
.k-card > .k-empty.box { border: 0; background: none; }
/* P6 (13.10.2026): k.empty always draws the box; in a table cell or a list row the row lines already frame it */
:is(td, li) > .k-empty.box { flex-direction: row; flex-wrap: wrap; align-items: center; padding: var(--s3) 0; border: 0; background: none; }

/* callout: a standing note that needs attention (hints from rules, a pending approval) */
.k-callout { display: flex; flex-direction: column; gap: var(--s2); padding: var(--s3) var(--s4); border: 1px solid var(--line-strong); border-left: 3px solid var(--alert); background: var(--surface); font-size: var(--fs-table); }
.k-callout.info { border-left-color: var(--ink); }
.k-callout > ul { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: var(--s1); }
.k-callout > ul > li { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: baseline; }

/* the centred modal: the same sheet markup and behaviour, in the middle; for short focused tasks (≤ ~8 fields) */
.k-sheet.modal { inset: 0; margin: auto; width: min(640px, calc(100vw - 2 * var(--s4))); height: fit-content; max-height: min(86dvh, 820px); border: 1px solid var(--ink); }
.k-sheet.modal.small { width: min(480px, calc(100vw - 2 * var(--s4))); }
.k-sheet.modal .k-sheet-b { flex: 0 1 auto; }
@media (max-width: 767px) {
  .k-sheet.modal, .k-sheet.modal.small { inset: auto 0 0 0; width: 100vw; max-height: 92dvh; margin: 0; border: 0; border-top: 1px solid var(--ink); }
}

/* a meter: bar + words (budget use in a table cell or under a figure) */
.k-meter { display: flex; flex-direction: column; gap: var(--s1); min-width: 140px; }
.k-meter-t { display: flex; justify-content: space-between; gap: var(--s2); white-space: nowrap; font-size: var(--fs-hint); color: var(--muted); font-variant-numeric: tabular-nums; }

/* a panel that folds (rare content behind one click, progressive disclosure ≤ 2 levels): <details class="k-card k-fold"> */
details.k-card.k-fold { max-width: none; padding: 0; gap: 0; color: var(--ink); font-size: var(--fs-text); }
.k-fold > summary { display: flex; align-items: center; gap: var(--s2); min-height: 40px; padding: 0 var(--s5); font-size: var(--fs-block); font-weight: 500; color: var(--ink); text-decoration: none; list-style: none; cursor: pointer; }
.k-fold > summary::-webkit-details-marker { display: none; }
.k-fold > summary .k-ico { color: var(--muted); }
.k-fold > summary::after { margin-left: auto; }
.k-fold[open] > summary { border-bottom: 1px solid var(--line); }
.k-fold > div { padding: var(--s4) var(--s5) var(--s5); gap: var(--s6); margin: 0; }
@media (max-width: 767px) { .k-fold > summary { padding: 0 var(--s4); } .k-fold > div { padding: var(--s3) var(--s4) var(--s4); } }

/* an amount that is billable (green, always next to its word; B0.4) — the old project head (k-phead, k-pfigs, k-btn.bill)
   went with W8-H 12.10.2026: k.pagehead replaced it */
.k-bill { color: var(--bill); }

/* ── Bausteine v2 (W8-0, 12.10.2026; canvas „Projektseite“, _docs/designsystem/BAUSTEINE_V2.md) ──────────────────────
   Colour carries meaning: --good (green, „in Ordnung / kann abgerechnet werden“) and --alert (orange, „jemand muss etwas
   tun“), everything else ink/grey; the meaning sits in a title tooltip (+ a k-sr word), never in a legend.
   k-ph        the page head v2 (k.pagehead): eyebrow, serif title, actions right, people line, key-figure tiles in five
               columns with a 3 px top rule by state (neutral --line, .good, .act)
   k-tabs.v2   the tab ribbon (k.tabs): on --surface, active = 600 + 3 px Senf underline + --bg fill, count pills
   k-cnt       a count pill (tabs, filter chips): neutral · .good · .act · .info
   k-ava.round a round avatar 24/32/40 (k.ava), initials on a --cat-* colour (.c1…c12); k-avas an overlapping stack
   k-badge.good, .bad, .v2   the status pill v2 (k.pill): the green kind, the filled critical kind, 600 weight
   k-rowact    one visible small line button + the row menu „⋯“ (k.rowaction)
   k-lrows / k-lrow  a list row of a person or a thing with trailing figures, pills and „⋯“ (13.10.2026, below)
   k-fig       a figure with its note („62 h“ over „gebucht · 8 W 186 h“), never broken over lines
   k-switch    an on/off setting as a label row: words left, the switch right (k.switch)
   k-selbar    the dark selection bar for bulk actions (k.selbar; a k-bulkbar, kit.js counts) */

/* page head v2 — the one page head of the kit (P4, 13.10.2026): k.pagehead, k.head (= size 'list'), Feedback PageHead.
   Two title sizes only: a record 40 px (--fs-head, 30 on a phone), a list, settings or report page 36 px
   (.k-ph--list: --fs-title, 28 on a phone). The eyebrow (the breadcrumb, guidelines §8) is the one .k-eyebrow above. */
.k-ph { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; }
.k-ph--list { gap: var(--s2); }
.k-ph-top { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: var(--s3) var(--s6); }
.k-ph-text { display: flex; flex-direction: column; gap: var(--s1); min-width: 0; flex: 1 1 320px; }
.k-ph-title { margin: 0; font-family: var(--serif); font-weight: 300; font-size: var(--fs-head); line-height: 1.1; letter-spacing: -.01em; text-wrap: balance; }
.k-ph--list .k-ph-title { font-size: var(--fs-title); line-height: 1.2; }
.k-ph-top > .k-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }
.k-ph-people { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s5); margin: 0; padding: 0; list-style: none; font-size: var(--fs-table); }
.k-ph-people > li { display: inline-flex; align-items: center; gap: var(--s2); min-width: 0; }
.k-ph-role { color: var(--muted); }
.k-ph-kpis { position: relative; display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 10rem), 1fr)); gap: var(--s4); margin: 0; }  /* relative: its k-sr stay inside the phone scroller; auto-fill: a column never gets so narrow that „01.09.2024“ breaks (head beside the Lotse panel, QA 15.10.) */
.k-ph-kpi { display: flex; flex-direction: column; gap: 2px; min-width: 0; padding-top: var(--s2); border-top: 3px solid var(--line); color: var(--ink); text-decoration: none; }
.k-ph-kpi > dd { margin: 0; }
.k-ph-kpi.good { border-top-color: var(--good); }
.k-ph-kpi.act { border-top-color: var(--alert); }
.k-ph-kpi-v a { color: inherit; text-decoration: none; }
.k-ph-kpi-v a:hover { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; }
.k-ph-kpi-l { font-size: var(--fs-caps); letter-spacing: .08em; text-transform: uppercase; color: var(--muted); }
.k-ph-kpi-v { font-family: var(--serif); font-weight: 300; font-size: var(--fs-head-kpi); line-height: 1.15; font-variant-numeric: tabular-nums; overflow-wrap: break-word; }
.k-ph-kpi-d { font-size: var(--fs-label); color: var(--muted); }
/* P7 (13.10.2026): on a phone the key figures are one row that scrolls sideways (the tabs stay above the fold at 812 px);
   „?“ and „⋯“ stay beside the title when they are the only actions — other actions take their own row as before */
@media (max-width: 767px) {
  .k-ph-kpis { display: flex; gap: var(--s4); overflow-x: auto; scrollbar-width: none; overscroll-behavior-x: contain; }
  .k-ph-kpis::-webkit-scrollbar { display: none; }
  .k-ph-kpi { flex: none; min-width: 112px; max-width: 60vw; }
  .k-ph-kpi-v { overflow-wrap: normal; white-space: nowrap; }
  .k-ph-top > .k-actions { width: 100%; }
  .k-ph-top:not(:has(> .k-actions > :not(.k-help.q, .k-menu))) { flex-wrap: nowrap; align-items: flex-start; }
  .k-ph-top:not(:has(> .k-actions > :not(.k-help.q, .k-menu))) > .k-ph-text { flex: 1 1 auto; }
  .k-ph-top:not(:has(> .k-actions > :not(.k-help.q, .k-menu))) > .k-actions { width: auto; flex: none; flex-wrap: nowrap; margin-top: var(--s1); }
}

/* the tab ribbon v2: scrolls sideways on a phone, never wraps (portal/base.html adds the fade classes) */
.k-tabs.v2 { gap: 0; flex-wrap: nowrap; background: var(--surface); border-bottom: 1px solid var(--line); }
.k-tabs.v2 a { gap: var(--s2); padding: 0 var(--s4); border-bottom: 3px solid transparent; margin-bottom: -1px; color: var(--muted); }
.k-tabs.v2 a:first-child { padding-left: var(--s4); }
.k-tabs.v2 a:hover { color: var(--ink); }
.k-tabs.v2 a.on, .k-tabs.v2 a[aria-current="page"] { color: var(--ink); font-weight: 600; background: var(--bg); border-bottom-color: var(--accent); }
.k-tabs-sep { flex: none; align-self: center; width: 1px; height: var(--s5); margin: 0 var(--s1); background: var(--line-strong); }
/* the second level (k.tabs(sub=True), P5 13.10.2026): under a tab row or a page head, smaller and neutral — no surface,
   no Senf; the current page in ink with a 2 px ink underline. Starts on the content edge; scrolls like the first level. */
.k-tabs.v2.sub { background: none; margin-top: var(--s2); }
.k-tabs.v2.sub a { padding: 0 var(--s3); border-bottom-width: 2px; font-size: var(--fs-hint); }
.k-tabs.v2.sub a:first-child { padding-left: 0; }
.k-tabs.v2.sub a.on, .k-tabs.v2.sub a[aria-current="page"] { font-weight: 500; background: none; border-bottom-color: var(--ink); }
.k-tabs.v2.sub .k-tabs-sep { height: var(--s4); }

/* count pill: neutral by default; .good / .act carry the meaning (title + k-sr word), .info ink on grey */
.k-cnt { position: relative; display: inline-flex; align-items: center; justify-content: center; min-width: 20px; height: 20px; padding: 0 6px; box-sizing: border-box;
  border-radius: var(--radius-pill); font: 600 var(--fs-caps) var(--sans); font-variant-numeric: tabular-nums; line-height: 1;
  background: var(--line); color: var(--ink); }
.k-cnt.good { background: var(--good); color: var(--surface); }
.k-cnt.act { background: var(--alert); color: var(--surface); }
.k-cnt.info { background: var(--line); color: var(--ink); }
.k-chip .k-cnt { margin-left: var(--s2); }
@media (forced-colors: active) { .k-cnt { border: 1px solid CanvasText; } }

/* avatar v2: round, three sizes, the photo over the initials (a photo that fails is dropped: static/js/actions.js) */
.k-ava.round { border-radius: 50%; font: 600 var(--fs-label) var(--sans); background: var(--line); color: var(--ink); }
.k-ava.round img { z-index: 1; }
.k-ava.s24 { width: 24px; height: 24px; font-size: var(--fs-caps); }
.k-ava.s32 { width: 32px; height: 32px; font-size: var(--fs-label); }
.k-ava.s40 { width: 40px; height: 40px; font-size: var(--fs-table); }
.k-ava.s64 { width: 64px; height: 64px; font-size: var(--fs-lead); }  /* W8-H: the record head's photo (48 on a phone, below) */
.k-ava.ringed { outline: 2px solid var(--bg); }
.k-ava.c1 { background: var(--cat-1); color: var(--cat-1-ink); }   .k-ava.c2 { background: var(--cat-2); color: var(--cat-2-ink); }
.k-ava.c3 { background: var(--cat-3); color: var(--cat-3-ink); }   .k-ava.c4 { background: var(--cat-4); color: var(--cat-4-ink); }
.k-ava.c5 { background: var(--cat-5); color: var(--cat-5-ink); }   .k-ava.c6 { background: var(--cat-6); color: var(--cat-6-ink); }
.k-ava.c7 { background: var(--cat-7); color: var(--cat-7-ink); }   .k-ava.c8 { background: var(--cat-8); color: var(--cat-8-ink); }
.k-ava.c9 { background: var(--cat-9); color: var(--cat-9-ink); }   .k-ava.c10 { background: var(--cat-10); color: var(--cat-10-ink); }
.k-ava.c11 { background: var(--cat-11); color: var(--cat-11-ink); } .k-ava.c12 { background: var(--cat-12); color: var(--cat-12-ink); }
.k-ava.more { background: var(--surface); color: var(--muted); border: 1px solid var(--line-strong); box-sizing: border-box; }
.k-avas { display: inline-flex; align-items: center; vertical-align: middle; }
.k-avas > .k-ava + .k-ava { margin-left: -6px; }
.k-avas.s32 > .k-ava + .k-ava, .k-avas.s40 > .k-ava + .k-ava { margin-left: -8px; }

/* status pill v2 (k.pill; k.badge with kind 'good'): the green „in Ordnung“, 600 weight */
.k-badge.good { background: rgb(var(--good-rgb) / .14); color: var(--good); } .k-badge.good::before { background: var(--good); }
.k-badge.v2 { font-weight: 600; }
/* bad = „kritisch“ (13.10.2026): filled --alert-ink with --bg text, so it differs from act's pale tint by lightness,
   not hue; 6.4:1 in light and in dark (bg on alert-ink); the dot is the text colour */
.k-badge.bad { background: var(--alert-ink); color: var(--bg); } .k-badge.bad::before { background: var(--bg); }

/* row action: one visible small line button, the rest behind „⋯“ (k.menu(row=True)) */
.k-rowact { display: inline-flex; align-items: center; justify-content: flex-end; gap: var(--s1); white-space: nowrap; }
.k-rowact > form { display: contents; }
.k-table td.act:has(.k-rowact) { width: 1%; vertical-align: middle; }  /* the column as wide as its button + „⋯“, the button mid-row */
@media (min-width: 1024px) { .k-table .k-rowact > .k-btn.small, .k-table .k-rowact > form > .k-btn.small { min-height: 32px; } }  /* mouse 32 px (canvas Tab*), touch keeps 44 */
/* W8-H: controls in a row sit above the stretched row link (a.k-stretch::after covers the row); an open menu above the
   next rows' controls; a pill with a tooltip too, so its title shows on hover */
:is(.k-table tr.k-row, .k-li.k-row) :is(.k-rowact, .k-menu, .k-pick, .k-btn, button, input, select, label, a:not(.k-stretch), .k-badge[title], .k-cnt[title]) { position: relative; z-index: 1; }
:is(.k-table tr.k-row, .k-li.k-row) :is(.k-rowact:has(.k-menu[open]), .k-menu[open]) { z-index: var(--z-popover); }
/* the phone fallback (W8-H): an item of the menu marked .k-menu-sm repeats the row's visible action — on a phone the menu
   shows it and the row keeps „⋯“ only (k.rowaction(…, icon=…) adds it; without one the button stays). .k-tight-act on a
   wide list does the same below 1440 px. */
.k-menu-p > .k-menu-sm { display: none; }
@media (max-width: 767px) {
  .k-rowact:has(.k-menu-sm) > .k-btn, .k-rowact:has(.k-menu-sm) > form > .k-btn { display: none; }
  .k-menu-p > .k-menu-sm { display: flex; } .k-menu-p > form.k-menu-sm { display: contents; }
}
@media (max-width: 1439px) {
  .k-tight-act .k-rowact:has(.k-menu-sm) > .k-btn, .k-tight-act .k-rowact:has(.k-menu-sm) > form > .k-btn { display: none; }
  .k-tight-act .k-menu-p > .k-menu-sm { display: flex; } .k-tight-act .k-menu-p > form.k-menu-sm { display: contents; }
}

/* selection bar v2: dark, the count left, the actions right (Senf for the one primary) */
.k-bulkbar.k-selbar { padding: var(--s2) var(--s4); background: var(--ink); color: var(--bg); border-top: 0; }
.k-selbar .k-bulk-n { color: var(--bg); }
.k-selbar .k-btn.line { color: var(--bg); border-color: rgb(var(--bg-rgb) / .45); }
.k-selbar .k-btn.line:hover { border-color: var(--bg); }
.k-selbar .k-link, .k-selbar .k-link:hover { color: var(--bg); }
.k-selbar :focus-visible { outline-color: var(--bg); }
.k-selbar-a { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }
/* W8-H: the bar sits at the foot — a menu in it opens upwards, anchored to the bar (sticky = the containing block), so it
   stays on screen on a phone too */
.k-selbar .k-menu { position: static; }
.k-selbar .k-menu-p { position: absolute; top: auto; bottom: calc(100% + var(--s1)); left: auto; right: var(--s4); margin: 0; color: var(--ink); }
@media (max-width: 767px) { .k-selbar .k-menu-p, .k-selbar .k-menu-p.wide { left: var(--s4); right: var(--s4); } }

/* page head v2, W8-H additions: the photo beside the head (a record), more items in the people line, the „?“ among the
   actions (its panel opens to the left), the pill line under the head */
.k-ph.has-photo { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--s5); align-items: start; }
.k-ph-main { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; }
.k-ph-plink .k-link { display: inline-flex; align-items: center; gap: var(--s1); min-height: var(--target); }
.k-ph-info { max-width: none; }
@media (min-width: 768px) { .k-ph-info > div { left: auto; right: 0; } }
.k-ph-pills { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2) var(--s3); }
.k-ph-lede { margin: 0; font-size: var(--fs-table); color: var(--muted); max-width: 767px; text-wrap: pretty; }
@media (max-width: 767px) {
  .k-ph.has-photo { grid-template-columns: minmax(0, 1fr); gap: var(--s3); }
  .k-ph.has-photo > .k-ava.s64 { width: 48px; height: 48px; }
}

/* a person in a cell or a list (k.person; W8-H, was HR's .p-who): the avatar, the name, a muted second line; a link in
   it may be the row's stretched link */
.k-who { display: flex; align-items: center; gap: var(--s3); min-width: 0; }
.k-who > .k-ava { flex: none; }
.k-who:has(> .k-ava.s24) { gap: var(--s2); }  /* the small one in a table cell sits closer */
.k-who-t { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.k-li > .k-who { grid-row: 1; grid-column: -2 / -1; }

/* k-lrows / k-lrow — a row of a person or a thing with trailing figures, pills and the row menu „⋯“ (layout fix
   13.10.2026: on a phone the team row of a project fell apart — „⋯“ on a line of its own, the pill under the photo, the
   hours split). Wide: lead (photo) · main (name over a grey line) · info (figures, pills) · act („⋯“) on one line.
   Narrow — the list narrower than 560 px, a container query, so the side column of a desktop page gets it too: two
   lines, lead · main · act on the first („⋯“ always top right), info on the second under the name. Name and grey line
   end in „…“, a figure or a pill never breaks. Rows without „⋯“ keep an empty .k-lrow-act so the columns line up.
     <ul class="k-lrows"><li class="k-lrow">{{ k.ava(p, 40) }}<span class="k-lrow-main"><b>Name</b><span class="k-lrow-sub">Rolle</span></span>
       <span class="k-lrow-info">{{ k.fig('62 h', 'gebucht') }}{{ k.pill('darf buchen', 'good') }}</span>
       <span class="k-lrow-act">{% call k.rowaction() %}…{% endcall %}</span></li></ul> */
.k-lrows { container-type: inline-size; margin: 0; padding: 0; list-style: none;
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto auto auto auto; }  /* lead · main · 4 info slots · act (below) */
.k-lrows > * { grid-column: 1 / -1; }
.k-lrow { display: grid; grid-template-columns: auto minmax(0, 1fr) auto auto; grid-template-areas: "lead main info act";
  align-items: center; gap: var(--s1) var(--s3); padding: var(--s3) 0; border-bottom: 1px solid var(--line); }
.k-lrow:where(:not(:has(> .k-lrow-act))) { grid-template-columns: auto minmax(0, 1fr) auto; grid-template-areas: "lead main info"; }
.k-lrow > .k-ava, .k-lrow > .k-lrow-lead { grid-area: lead; flex: none; }
/* a row without a picture (a letter, a request): no lead column, the name starts on the list's edge (rollout 13.10.2026) */
.k-lrow:where(:not(:has(> .k-ava, > .k-lrow-lead))) { grid-template-columns: minmax(0, 1fr) auto auto; grid-template-areas: "main info act"; }
.k-lrow:where(:not(:has(> .k-ava, > .k-lrow-lead)):not(:has(> .k-lrow-act))) { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "main info"; }
.k-lrow-main { grid-area: main; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.k-lrow-main > :is(b, a, strong) { overflow: hidden; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
.k-lrow-sub { overflow: hidden; font-size: var(--fs-hint); color: var(--muted); text-overflow: ellipsis; white-space: nowrap; }
.k-lrow-info { grid-area: info; display: flex; align-items: center; justify-content: flex-end; gap: var(--s2) var(--s3); min-width: 0; }
.k-lrow-info > * { flex: none; white-space: nowrap; }
.k-lrow-act { grid-area: act; display: flex; align-items: center; justify-content: flex-end; min-width: var(--target); }
/* in a card: the last row without its hairline (like k-list); edge to edge in a k.card(flush=True) with the card's inset */
.k-card > .k-lrows > .k-lrow:last-child { border-bottom: 0; }
.k-card.flush > .k-lrows { padding-left: var(--s5); padding-right: var(--s5); }
.k-card.k-sec--open > .k-lrows { padding-left: 0; padding-right: 0; }
@media (max-width: 767px) { .k-card.flush > .k-lrows { padding-left: var(--s4); padding-right: var(--s4); } .k-card.k-sec--open > .k-lrows { padding-left: 0; padding-right: 0; } }
.k-fig { display: inline-flex; flex-direction: column; align-items: flex-end; text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.k-fig > b { font-family: var(--mono); font-size: var(--fs-hint); font-weight: 500; }
.k-fig > span { font-size: var(--fs-hint); color: var(--muted); }
@container (max-width: 559px) {
  .k-lrow { grid-template-columns: auto minmax(0, 1fr) auto; grid-template-areas: "lead main act" ". info info"; align-items: start; }
  .k-lrow:not(:has(> .k-lrow-act)) { grid-template-columns: auto minmax(0, 1fr); grid-template-areas: "lead main" ". info"; }
  .k-lrow:not(:has(> .k-ava, > .k-lrow-lead)) { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "main act" "info info"; }
  .k-lrow:not(:has(> .k-ava, > .k-lrow-lead)):not(:has(> .k-lrow-act)) { grid-template-columns: minmax(0, 1fr); grid-template-areas: "main" "info"; }
  .k-lrow-main { align-self: center; }
  .k-lrow-info { justify-content: flex-start; flex-wrap: wrap; }
  .k-lrow-info > .k-steps { flex: 1 1 auto; flex-wrap: wrap; white-space: normal; }  /* phases of a cycle (Feedback overview): wrap on the phone */
  .k-lrow-info:empty { display: none; }
  .k-fig { flex-direction: row; align-items: baseline; gap: var(--s1); text-align: left; }
}
/* one set of columns for the whole list (Marcel 15.10.2026, the Brain project list: "Spalten springen von Zeile zu
   Zeile"): wide, every row is a subgrid of its k-lrows — lead · main · four info slots · act — so a slot is as wide as
   its widest entry in the list and a figure stands under the figures above it, whatever pill follows it. The info
   parts fill the slots from the right (the last part in the last slot); a figure sits on its slot's right edge, a pill
   or a date on its left edge (a status column). The gaps sit on the parts (margins), so an empty slot, a list without
   photos or without „⋯“ takes no room. More than four parts: one flex group as before. Narrow (above) is unchanged. */
@container (width > 559px) {
  .k-lrow { grid-template-columns: subgrid; column-gap: 0; }
  .k-lrow > :is(.k-ava, .k-lrow-lead) { grid-row: 1; grid-column: 1; margin-right: var(--s3); }
  .k-lrow > .k-lrow-main { grid-row: 1; grid-column: 2; }
  .k-lrow > .k-lrow-info { grid-row: 1; grid-column: 3 / 7; display: grid; grid-template-columns: subgrid; column-gap: 0; }
  .k-lrow > .k-lrow-info > * { grid-row: 1; justify-self: start; margin-left: var(--s3); }
  .k-lrow > .k-lrow-info > .k-fig { justify-self: end; }
  .k-lrow > .k-lrow-info > :nth-last-child(1) { grid-column: 4; }
  .k-lrow > .k-lrow-info > :nth-last-child(2) { grid-column: 3; }
  .k-lrow > .k-lrow-info > :nth-last-child(3) { grid-column: 2; }
  .k-lrow > .k-lrow-info > :nth-last-child(4) { grid-column: 1; }
  .k-lrow > .k-lrow-info:has(> :nth-child(5)) { display: flex; }
  .k-lrow > .k-lrow-act { grid-row: 1; grid-column: 7; margin-left: var(--s3); }
}

/* k-switch — an on/off setting as a label row (k.switch): the words left (a grey line under them), the switch right;
   a submit button with role="switch" in the setting's POST form, aria-checked = the state now */
.k-switch { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); width: 100%; min-height: var(--target);
  padding: 0; border: 0; background: none; color: var(--ink); font: inherit; font-size: var(--fs-table); text-align: left; cursor: pointer; }
.k-switch-l { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.k-switch-s { font-size: var(--fs-hint); color: var(--muted); }
.k-switch-t { position: relative; flex: none; width: 40px; height: 24px; box-sizing: border-box; border: 1px solid var(--mark);
  border-radius: var(--radius-pill); background: transparent; transition: background var(--dur-fast); }
.k-switch-t::after { content: ''; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: var(--radius-pill);
  background: var(--mark); transition: left var(--dur-fast); }
.k-switch[aria-checked="true"] .k-switch-t { background: var(--ink); border-color: var(--ink); }
.k-switch[aria-checked="true"] .k-switch-t::after { left: 19px; background: var(--bg); }
.k-switch:hover .k-switch-t { border-color: var(--ink); }
.k-switch:focus-visible { outline: var(--focus-width) solid var(--ink); outline-offset: var(--focus-offset); }
.k-switch:disabled { color: var(--muted); cursor: default; }
@media (forced-colors: active) { .k-switch-t { border-color: CanvasText; } .k-switch-t::after { background: CanvasText; } }

/* mobile pass (15.10.2026, Marcel „jede Seite auf mobile … optimieren“): touch targets of 44 px where the kit had text-high
   links, measured at 375 px on every page of Portal, HR, IT, Projekte, Angebote and Staffing. Nothing moves: the extra
   height is taken back with a negative margin (the k-link pattern) or lies in a pseudo-element. */
/* a figure of the page head that links (k.pagehead kpis): the whole figure is the target, not the digits */
.k-ph-kpi:has(> .k-ph-kpi-v a):not(:has(> .k-ph-kpi-d a)) { position: relative; }
.k-ph-kpi:has(> .k-ph-kpi-v a):not(:has(> .k-ph-kpi-d a)) .k-ph-kpi-v a::after { content: ''; position: absolute; inset: 0; }
.k-ph-kpi:has(> .k-ph-kpi-v a):not(:has(> .k-ph-kpi-d a)):hover .k-ph-kpi-v a { text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 4px; }
@media (max-width: 1023px) {
  /* the breadcrumb link above a title, the link of an open section head, a list title that is no stretched row */
  .k-eyebrow a, .k-sec--open > .k-card-h .k-link { display: inline-flex; align-items: center; min-height: var(--target); margin-block: calc((1lh - var(--target)) / 2); }
  .k-li:not(.k-row) a.k-li-title, .k-lrow-main > a { padding-block: calc((var(--target) - 1lh) / 2); margin-block: calc((1lh - var(--target)) / 2); }
  .k-chip-x { position: relative; }
  .k-chip-x::before { content: ''; position: absolute; inset: -10px; }
  /* a plain link in a table cell (a name, „Beleg“): inline padding widens the target, the row keeps its height */
  .k-table :is(td, th) > :is(a:not([class]), a.k-sm) { padding-block: 10px; }
  /* a chip with one sign (‹ ›) and a button link with a short word („ok“) are 44 px wide, too */
  .k-chip { min-width: var(--target); justify-content: center; }
  button.k-link { min-width: var(--target); }
}
/* phones: a row of a k-lrows list wraps its name and its line below instead of cutting them off with „…“ (help cards,
   search results, team rows); the info column keeps its place on the right */
@media (max-width: 767px) {
  .k-lrow-main > :is(b, a, strong), .k-lrow-sub { white-space: normal; overflow-wrap: break-word; }
}

/* Mobile pass B (15.10.2026, 375 px audit of Zeit, Faktura, Reporting, Gesprächspartner): below 1024 px the small text
   links of the kit become 44 px targets (--target) without moving anything — the line stays, the target grows into the
   space around it (the k-table .k-link pattern): the breadcrumb (k-eyebrow), the link of an empty state („Filter
   löschen“, „Zum Monatslauf“), a panel head's link in an open section (Regeln, CSV, Bearbeiten), a key figure that links
   to its list (k-ph-kpi-v), the title link of a list row (k-lrow-main, k-li.plain), a plain link in a table cell (Zeit › Auszug
   clients), a small link (a.k-sm, Faktura › Lauf „zum Beleg“); the placeholder buttons and fields of the document
   editor (d-keys, d-in) and the file field of an upload. */
@media (max-width: 1023px) {
  .k-eyebrow a, .k-empty > a, .k-sec--open > .k-card-h .k-link, .k-ph-kpi-v > a, .k-table :is(td, th) > a:not([class]), a.k-sm {
    display: inline-flex; align-items: center; min-height: var(--target); margin-block: calc((1lh - var(--target)) / 2); }
  .k-ph-kpi-v > a { min-width: var(--target); }
  .k-lrow-main > a, .k-li.plain a.k-li-title { display: flex; align-items: center; min-height: var(--target); margin-block: calc((1lh - var(--target)) / 2); }
  .d-keys .k-tag, input.d-in, .k-upload-zone input[type="file"] { min-height: var(--target); }
}
