/* ================================================================
   UI KIT WORK — kit-work.css
   The three surface-shapers. Pairs with kit-work.js.
   Requires tokens.css + kit-ext.css loaded first (the lifecycle
   palette and .lc-* / .lc-sw live there).
   Components: record shell (.fc) · typed table (.kt) · commit
   sheet (.cs).  Tokens only — no hex, no invented radii,
   durations or shadows. Nothing here animates on an idle loop.
   ================================================================ */

/* ---------- shared plates & marks ----------
   Self-contained copies of the shell's plate/mark vocabulary so the
   components render identically in the gallery (which has no shell.css)
   and in the product. Same geometry, same tokens. */
.kw-plate{display:inline-block;font-family:var(--mono);font-size:var(--fs-mark,10px);font-weight:700;letter-spacing:.08em;color:var(--ink-3);border:1.5px solid var(--hairline);border-radius:4px;padding:2.5px 7px;white-space:nowrap}
.kw-plate.miss{color:var(--lc-missing-ink);border-color:color-mix(in srgb,var(--lc-missing) 55%,transparent)}
.kw-mk{display:inline-block;width:11px;height:11px;border-radius:3px;flex:none;vertical-align:-1px}
.kw-mk.ok{background:var(--lc-use)}
.kw-mk.prov{background:repeating-linear-gradient(45deg,var(--lc-use) 0 2.5px,color-mix(in srgb,var(--lc-use) 22%,var(--surface)) 2.5px 5px);border:1px solid color-mix(in srgb,var(--lc-use) 60%,var(--surface))}
.kw-mk.gap{background:var(--lc-missing-bg);border:1.5px dashed var(--lc-missing)}
/* a state, as a dot and a word -- see the `signal` cell in kit-work.js */
.kw-sig{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}
.kw-sig>i{display:inline-block;width:8px;height:8px;border-radius:50%;flex:none;background:var(--ink-4,#9aa0a6)}
.kw-sig.ok>i{background:var(--lc-use,#1a7f37)}
.kw-sig.warn>i{background:var(--warn,#b26a00)}
.kw-sig.bad>i{background:var(--lc-missing,#c0392b)}
.kw-sig.none>i{background:transparent;border:1.5px solid var(--ink-4,#9aa0a6)}
.kw-lc{display:inline-flex;align-items:center;gap:6px;font-size:11.5px;font-weight:600;color:var(--lc-ink);white-space:nowrap}
.kw-lnk{cursor:pointer;border-bottom:1px dotted transparent;transition:color var(--t-fast) ease,border-color var(--t-fast) ease}
.kw-lnk:hover{color:var(--accent-ink);border-bottom-color:var(--accent-ink)}

/* ================================================================
   1 · RECORD SHELL  (.fc) — verdict first, faces beneath
   ================================================================ */
/* identity is a strip, not a card — one dense line, mono identifiers */
.fc-subject{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;padding:2px 2px 0}
.fc-title{font-size:20px;font-weight:600;letter-spacing:-.02em;font-family:var(--mono)}
/* subject.kind:'name' — a non-mono subject (a person, a desk, a policy) */
.fc.fc-name .fc-title{font-family:var(--sans);letter-spacing:-.022em}
.fc-tag{font-family:var(--mono);font-size:11px;font-weight:600;color:var(--ink-3);letter-spacing:.03em}
.fc-sub{font-size:12.5px;color:var(--ink-2)}
.fc-marks{display:inline-flex;gap:6px;align-items:center}
.fc-mark{display:inline-flex;align-items:center;gap:5px;font-size:10.5px;font-weight:600;color:var(--ink-2);border:1px solid var(--hairline);border-radius:99px;padding:1.5px 9px;white-space:nowrap;background:var(--surface);transition:border-color var(--t-fast) ease,transform var(--t-fast) var(--ease-spring)}
.fc-mark:hover{border-color:color-mix(in srgb,var(--ink) 22%,var(--hairline));transform:translateY(-1px)}
.fc-mark.hold{color:var(--lc-hold-ink);border-color:color-mix(in srgb,var(--lc-hold) 40%,var(--hairline))}
.fc-mark.miss{color:var(--lc-missing-ink);border-color:color-mix(in srgb,var(--lc-missing) 45%,var(--hairline))}
.fc-mark.warn{color:var(--warn-ink);border-color:color-mix(in srgb,var(--warn) 45%,var(--hairline))}

/* the verdict — the answer, stated in the first screenful, with the mark
   that proves it drawn beside it. Chrome per tone, prose from the caller. */
.fc-verdict{--fv-c:var(--ink-3);--fv-ink:var(--ink-2);position:relative;margin-top:12px;padding:13px 16px 12px;border:1px solid var(--hairline);border-left:3px solid var(--fv-c);border-radius:10px;background:color-mix(in srgb,var(--fv-c) 4%,var(--surface));transition:border-color var(--t-med) ease,background .35s ease}
.fc-verdict[data-tone="ok"]{--fv-c:var(--s-good);--fv-ink:var(--s-good-ink)}
.fc-verdict[data-tone="warn"]{--fv-c:var(--warn);--fv-ink:var(--warn-ink)}
.fc-verdict[data-tone="bad"]{--fv-c:var(--lc-missing);--fv-ink:var(--lc-missing-ink)}
.fc-verdict[data-tone="unknown"]{border-style:dashed;border-left-style:dashed;background:var(--surface)}
.fc-vline{display:flex;align-items:center;gap:10px;flex-wrap:wrap}
.fc-vdot{width:9px;height:9px;border-radius:50%;flex:none;background:var(--fv-c);box-shadow:0 0 0 3px color-mix(in srgb,var(--fv-c) 16%,transparent)}
.fc-verdict[data-tone="unknown"] .fc-vdot{background:transparent;border:1.5px dashed var(--ink-3);box-shadow:none}
.fc-vtext{font-size:14.5px;font-weight:600;letter-spacing:-.008em;color:var(--ink)}
.fc-vmark{display:inline-flex;align-items:center;flex:0 1 auto;min-width:0;max-width:100%}
.fc-vmark:empty{display:none}
.fc-vactions{margin-left:auto;display:flex;gap:7px;align-items:center}
.fc-vdetail{margin-top:5px;padding-left:19px;font-size:12px;color:var(--ink-2);max-width:88ch}
.fc-vdetail:empty{display:none}
/* an `ok` verdict is not drawn — no tint, no rule, no dot, no words. Only
   what it carried survives, on a plain row: the mark and the actions. */
.fc-verdict.quiet{padding:0;border:0;border-radius:0;background:none}
.fc-verdict.quiet .fc-vline{gap:8px}
.fc-verdict.quiet .fc-vactions{margin-left:0}
.fc-verdict.quiet .fc-vactions:empty{display:none}
/* carried nothing, so it takes no room at all */
.fc-verdict.bare{margin:0}

/* the faces rail — a quiet hairline row; count and tone dot only where
   something needs attention. One shared underline that SLIDES
   (.seg-thumb's exact physics), so both selection controls move alike. */
.fc-rail{position:relative;display:flex;gap:2px;border-bottom:1px solid var(--hairline);margin-top:16px;overflow-x:auto;scrollbar-width:none}
.fc-rail::-webkit-scrollbar{display:none}
.fc-thumb{position:absolute;bottom:0;left:0;height:2px;width:60px;background:var(--ink);border-radius:2px 2px 0 0;transition:transform var(--t-med) var(--ease-spring),width var(--t-med) var(--ease-spring)}
.fc-thumb.still{transition:none}
.fc-face{display:inline-flex;align-items:center;gap:7px;padding:9px 12px 10px;font-size:12.5px;font-weight:550;color:var(--ink-3);white-space:nowrap;border-radius:7px 7px 0 0;cursor:pointer;transition:color var(--t-fast) ease,background var(--t-fast) ease}
.fc-face:hover{color:var(--ink-2);background:var(--surface-2)}
.fc-face.on{color:var(--ink);font-weight:600}
.fc-face .fc-n{font-size:10.5px;font-weight:650;font-variant-numeric:tabular-nums;color:var(--ink-3);background:var(--surface-2);border:1px solid var(--hairline-2);border-radius:99px;padding:0 7px;line-height:16px;transition:color var(--t-fast) ease,background var(--t-fast) ease}
.fc-face.on .fc-n{color:var(--ink-2);background:var(--surface);border-color:var(--hairline)}
/* a failed count is a dash, never a zero — with the warn dot naming it */
.fc-face .fc-n.fail{color:var(--warn-ink);background:transparent;border-style:dashed}
.fc-face .fc-dot{width:6px;height:6px;border-radius:50%;flex:none}
.fc-face .fc-dot.warn{background:var(--warn)}
.fc-face .fc-dot.bad{background:var(--lc-missing)}
.fc-face .fc-dot.ok{background:var(--s-good)}
/* sequencing without a wizard: done · current · locked, all routable */
.fc-face .fc-done{flex:none;color:var(--s-good-ink)}
.fc-face.current .fc-cur{width:6px;height:6px;border-radius:50%;flex:none;background:var(--accent)}
.fc-face.locked{color:var(--ink-3)}
.fc-face.locked .fc-lk{flex:none;opacity:.6;transition:opacity var(--t-fast) ease,transform var(--t-med) var(--ease-spring)}
.fc-face.locked:hover .fc-lk{opacity:.95;transform:translateY(-1px)}

.fc-panel{padding-top:16px}
@keyframes fc-in{0%{opacity:0;transform:translateY(7px)}100%{opacity:1;transform:none}}
.fc-panel.fc-swap>*{animation:fc-in var(--t-med) var(--ease-out) both}
/* a locked face, opened — it explains itself and routes to what unlocks it */
.fc-locked{display:flex;flex-direction:column;align-items:center;text-align:center;padding:44px 20px;color:var(--ink-3)}
.fc-locked .fc-lock-ic{width:38px;height:38px;border-radius:10px;background:var(--surface-2);border:1.5px dashed var(--hairline);display:grid;place-items:center;margin-bottom:12px;color:var(--ink-3);transition:transform var(--t-med) var(--ease-spring)}
.fc-locked:hover .fc-lock-ic{transform:translateY(-2px)}
.fc-locked b{color:var(--ink-2);font-size:13px;font-weight:600}
.fc-locked p{font-size:12px;margin-top:3px;max-width:340px;line-height:1.55}

/* ================================================================
   2 · TYPED TABLE  (.kt)
   ================================================================ */
.kt{position:relative}
/* overflow-x:auto would trap position:sticky inside the wrapper — so the
   scroll container is engaged (by JS) only when the table actually
   overflows; a fitting table keeps its header stuck to the topbar.
   ⚠ AND THE RESTING STATE IS `clip`, NOT `visible`. A table's header cells
   carry an opaque background, and that background is a SQUARE box drawn at
   the top of the card. `visible` let it paint over the card's own 12px
   rounded corner and a few pixels of its border, on both top corners, at
   every width — which is what the customer's screenshot of "the header
   carries on past the edge of the card" actually shows. `clip` is the one
   value that clips one axis and leaves the other genuinely `visible`
   (`hidden` would compute the other axis to `auto` and make this wrap the
   scrollport, which is the very trap the note above is about), so the
   header is cut to the card's rounded edge and still sticks to the topbar
   as the page scrolls. */
.kt-wrap{overflow-x:clip;overflow-y:visible}
.kt-wrap.scroll{overflow-x:auto}

/* ================================================================
   A TABLE WIDER THAN ITS CARD — the one treatment, for every table in
   this product that can outgrow the card it sits in.

   ⚠ WHAT WAS ACTUALLY WRONG. Measured on a register page at 1400px with
   all twelve optional columns on: the table was 2,469px inside a wrap of
   1,048px. The wrap really was the scroll container and the page really
   did not move sideways — so the only thing a check looking for a
   sideways page scroll could see was a pass. What the person got was:

     · columns sliced through the middle of a word at the right edge,
       with nothing at all to say there was more to the right;
     · NO SCROLLBAR. `offsetHeight - clientHeight` measured 0, because a
       scroll container that is never styled gets the platform's OVERLAY
       scrollbar, which is drawn only while a finger or a trackpad is
       already scrolling. On a mouse, the way to more columns was
       invisible and undiscoverable;
     · the identity column gone. Scrolled to the right-hand end, the Tag
       column sat 1,091px off the left of the screen, so every row was a
       line of facts about a machine nobody could name;
     · and the header painting over the card's rounded corner (see the
       note on .kt-wrap above).

   THE TREATMENT, in the vocabulary this product already has:
     .tw-scroll   one positioned box around the scroller, so an edge
                  fade can be drawn over the scrolling content without
                  scrolling with it;
     .tw-bar      a real, always-drawn scrollbar. Styling
                  ::-webkit-scrollbar is what turns the overlay one off,
                  which is the whole point: a control has to be there
                  before it can be reached;
     .tw-pin      the identifying first column (and the select box in
                  front of it) stay put while the rest scrolls.
   The fade and the pin are both switched on only while the table is
   actually wider than its card — see UI.wideScroll in kit-work.js —
   so a table that fits is untouched, down to the pixel.
   ================================================================ */
.tw-scroll{position:relative}
/* more to the right, said as an edge rather than as a word. It stops
   short of the scrollbar, and it follows the card's corner. */
.tw-scroll::after{content:"";position:absolute;top:0;right:0;bottom:var(--tw-bar-h,0px);width:28px;pointer-events:none;
  border-radius:inherit;opacity:0;z-index:8;transition:opacity var(--t-fast) ease;
  background:linear-gradient(to left,var(--surface),color-mix(in srgb,var(--surface) 0%,transparent))}
.tw-scroll.more-r::after{opacity:1}
/* the card's own rounded edge, kept: where the table is the first thing
   in a card, the scroller is clipped to that corner and nothing inside it
   can paint past it. One pixel in, because the card's border is one pixel. */
.card>.tw-scroll:first-child,
.card>.kt-mount:first-child>.tw-scroll:first-child{border-top-left-radius:calc(var(--r-card) - 1px);border-top-right-radius:calc(var(--r-card) - 1px)}
.tw-scroll>.kt-wrap,.tw-scroll>.tbl-wrap{border-radius:inherit}
/* A scrollbar a mouse can see and drag, in the ink already on the page.
   ⚠ AND NO `scrollbar-width` / `scrollbar-color` HERE, deliberately. Either
   of those two properties makes the browser IGNORE every ::-webkit-scrollbar
   rule below and keep the platform's overlay scrollbar — measured: with
   overlay scrollbars on, a scroller carrying only the pseudo-elements lays
   out 11px, and the same scroller with `scrollbar-width:thin` added lays out
   0. Nought reserved pixels is the whole defect: there is no bar to see and
   nothing to drag until the person is already scrolling, which they cannot
   start doing without a wheel that tilts. */
.tw-bar::-webkit-scrollbar{height:11px;width:11px}
.tw-bar::-webkit-scrollbar-track{background:transparent}
.tw-bar::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--ink) 24%,transparent);border-radius:99px;border:3px solid transparent;background-clip:padding-box}
.tw-bar::-webkit-scrollbar-thumb:hover{background:color-mix(in srgb,var(--ink) 40%,transparent);background-clip:padding-box}
/* ⚠ THE PINNED CELLS CARRY THE ROW'S OWN BACKGROUND, and they have to
   carry it OPAQUE: a see-through pinned cell shows the columns sliding
   underneath it, which reads as a rendering fault rather than as a
   column that stays put. Hover and selection are restated here for the
   same reason, in the same two mixes the rows already use. */
.tw-scroll .scroll .tw-pin{position:sticky;left:0;z-index:5;background:var(--surface)}
.tw-scroll .scroll th.tw-pin{z-index:7}
.tw-scroll .scroll .tw-pin-2{left:var(--tw-pin-x,34px)}
/* one hairline where the pinned block ends, so the join is a seam and not
   a smear. A border, never a box-shadow: the first cell's box-shadow is
   already the selected row's left edge. */
.tw-scroll .scroll .tw-pin-last{border-right:1px solid var(--hairline)}
.tw-scroll .scroll tbody tr:hover td.tw-pin{background:var(--surface-2)}
.tw-scroll .scroll tbody tr.selrow td.tw-pin{background:color-mix(in srgb,var(--accent) 14%,var(--surface))}
.tw-scroll .scroll tbody tr.selrow:hover td.tw-pin{background:color-mix(in srgb,var(--accent) 19%,var(--surface))}
.tw-scroll .scroll tfoot td.tw-pin{background:var(--surface)}
.tw-scroll .scroll tfoot tr.click:hover td.tw-pin{background:var(--surface-2)}
@media (prefers-reduced-motion:reduce){.tw-scroll::after{transition:none}}

.kt-tbl{width:100%;border-collapse:collapse;font-size:12.5px}
.kt-tbl th{text-align:left;font-size:10.5px;font-weight:650;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3);padding:8px 10px;border-bottom:1px solid var(--hairline);white-space:nowrap}
.kt-tbl.kt-sticky th{position:sticky;top:var(--topbar-h,54px);z-index:6;background:color-mix(in srgb,var(--surface) 94%,transparent);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}
/* ⚠ That offset is for a PAGE: it parks the header just below the app topbar so
   rows scroll under it. A drawer has no topbar — it carries its own header and
   its body is the scroll container — so the same offset pushes the header DOWN
   onto the first rows instead. The header is deliberately opaque, so it does not
   look misplaced, it looks like the rows are missing: a one-row drilldown showed
   a header, a sliver of its only row, and "Showing 1-1 of 1" under it.
   Inside an overlay the header sticks to the top of that overlay. */
#drawer .kt-tbl.kt-sticky th,
.sheet .kt-tbl.kt-sticky th,
.drawer .kt-tbl.kt-sticky th{top:0}
/* ⚠ THE SAME PUSH ON A PAGE, ONCE THE WRAP SCROLLS SIDEWAYS. A wrap with
   overflow-x:auto is the scrollport the header measures itself against, so the
   topbar offset drew the header 54px down, over the second row. On a phone every
   wrap scrolls (see the 720px rule below), which put that on the Assets page's
   By category table the moment it became the page people land on. The register's
   own table has carried this same rule since (.at-card .tbl-wrap.scroll). */
.kt-wrap.scroll .kt-tbl.kt-sticky th{top:0}
@media (max-width:720px){.kt-tbl.kt-sticky th{top:0}}
/* ⚠ …AND THE HORIZONTAL SCROLLER THEN SWALLOWS IT.
   `.kt-wrap.scroll` sets overflow-x:auto, and CSS computes the other axis from
   `visible` to `auto` the moment one axis is not visible — so the WRAP, not the
   overlay body, becomes the scrollport the sticky header measures itself
   against. The wrap is exactly as tall as its own content, so `top:0` can never
   engage: scroll a wide drilldown and the header does not sit in the wrong
   place, it leaves altogether, which is the worse of the two failures.
   Inside an overlay the scrolling table therefore owns a height, so the header
   sticks to the top of the table it belongs to and the pager stays in view
   under it. Only the wide case is touched — a table that fits keeps no
   scroll container at all and keeps the rule above. */
#drawer .kt-wrap.scroll,
.sheet .kt-wrap.scroll,
.drawer .kt-wrap.scroll{max-height:calc(100vh - 230px)}
/* ⚠ A FULL PAGE CAN NEED THIS TOO, AND THE CATEGORY RECORD IS THE ONE THAT DOES.
   Same reasoning as the overlays above, on a page rather than in a sheet: give the wrap a
   height and it becomes the scrollport, so `top:0` engages and the column headings stay
   with the rows they name. Without it the whole page scrolled and a reader looking at row
   90 of 130 had lost the heading, the tabs, the filters and the pager.
   Scoped to the marked grid, so no other table's scrolling changes. */
.cat-grid .kt-wrap{max-height:calc(100vh - 340px);min-height:220px;overflow-y:auto}
.cat-grid .kt-tbl.kt-sticky th{top:0}
@media (max-width:720px){.cat-grid .kt-wrap{max-height:none;overflow-y:visible}}
.kt-tbl th.sortable{cursor:pointer;user-select:none;transition:color var(--t-fast) ease}
.kt-tbl th.sortable:hover{color:var(--ink)}
.kt-tbl th .sa{display:inline-block;vertical-align:-1px;margin-left:4px;opacity:0;transform:translateY(2px);transition:opacity var(--t-fast) ease,transform var(--t-med) var(--ease-spring)}
.kt-tbl th.sorted .sa,.kt-tbl th.sortable:hover .sa{opacity:1;transform:none}
.kt-tbl th.sorted{color:var(--ink)}
.kt-hwarn{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--warn);margin-left:5px;vertical-align:1px}
.kt-tbl td{padding:8px 10px;border-bottom:1px solid var(--hairline-2);vertical-align:middle}
.kt-tbl tbody tr{transition:background var(--t-fast) ease,opacity var(--t-fast) ease;will-change:transform}
.kt-tbl tbody tr:hover{background:var(--surface-2)}
.kt-tbl tbody tr:hover td:first-child{box-shadow:inset 2px 0 0 var(--accent)}
.kt-tbl tbody:hover tr:not(:hover):not(.kt-group){opacity:.6}
.kt-tbl tbody tr.click{cursor:pointer}
.kt-tbl .num{text-align:right;font-variant-numeric:tabular-nums}
/* typed cells */
.kt-mono{font-family:var(--mono);font-size:12px;font-weight:600;letter-spacing:.01em;white-space:nowrap}
.kt-tx b{font-weight:550;font-size:12.5px;display:block}
.kt-tx small,.kt-cell small{display:block;font-size:10.5px;color:var(--ink-3);margin-top:1px}
.kt-money{font-variant-numeric:tabular-nums;white-space:nowrap}
.kt-age{font-variant-numeric:tabular-nums;white-space:nowrap;font-size:12px}
.kt-age.capped{color:var(--ink-2);font-weight:550}
.kt-fail{color:var(--ink-3)}
.kt-none{color:var(--ink-3)}
/* new-to-page rows after a re-sort settle in; moved rows FLIP (JS-driven) */
@keyframes kt-in{0%{opacity:0}100%{opacity:1}}
.kt-tbl tbody tr.kt-new{animation:kt-in var(--t-med) ease both}
/* group header: shared context said once */
.kt-tbl tr.kt-group td{padding:12px 10px 6px;border-bottom:1px solid var(--hairline);background:transparent;font-size:10.5px;font-weight:650;letter-spacing:.07em;text-transform:uppercase;color:var(--ink-2)}
.kt-tbl tr.kt-group td b{font-variant-numeric:tabular-nums;color:var(--ink-3);font-weight:600;margin-left:7px;text-transform:none;letter-spacing:0}
.kt-tbl tr.kt-group:hover td{box-shadow:none}
/* totals: an earned footer, not another row */
.kt-tbl tfoot td{padding:9px 10px;border-top:1px solid var(--hairline);border-bottom:0;font-weight:650;font-variant-numeric:tabular-nums;font-size:12.5px}
.kt-tbl tfoot td:first-child{font-size:10.5px;font-weight:650;letter-spacing:.06em;text-transform:uppercase;color:var(--ink-3)}
/* a tail line (what the rows leave out) wears the totals footer and, when it opens something, the row hover */
.kt-tbl tfoot tr.kt-tail td{white-space:nowrap}
.kt-tbl tfoot tr.click{cursor:pointer;transition:background var(--t-fast) ease}
.kt-tbl tfoot tr.click:hover{background:var(--surface-2)}
/* selection: the check draws itself (same anatomy as the register's) */
.kt-ck{width:16px;height:16px;border-radius:5px;border:1.5px solid var(--hairline);background:var(--surface);display:grid;place-items:center;cursor:pointer;flex:none;transition:border-color var(--t-fast) ease,background var(--t-fast) ease,transform var(--t-fast) var(--ease-spring)}
.kt-ck:hover{border-color:color-mix(in srgb,var(--accent) 55%,var(--hairline));transform:scale(1.1)}
.kt-ck:active{transform:scale(.92)}
.kt-ck svg{opacity:0}
.kt-ck.on{background:var(--accent);border-color:var(--accent)}
.kt-ck.on svg{opacity:1}
@keyframes kw-tick{to{stroke-dashoffset:0}}
.kt-ck.on svg path{stroke-dasharray:24;stroke-dashoffset:24;animation:kw-tick .25s var(--ease-out) forwards}
.kt-ck.part{background:var(--accent);border-color:var(--accent)}
.kt-ck.part::after{content:"";width:8px;height:2px;border-radius:2px;background:var(--surface)}
/* ⚠ A SELECTED ROW WAS FAINTER THAN A HOVERED ONE, WHICH IS BACKWARDS.
   This was one rule: a background of --accent-bg, which is the accent at 8%
   opacity. Two lines above it, tr:hover paints --surface-2 AND a 2px accent
   left edge. So resting the mouse on a row gave a reader more signal than
   actually selecting it did, and the customer's reviewer could not tell which
   row he had picked while the bulk-action bar sat open underneath.
   A selected row now carries the two marks this product already uses for
   selected everywhere else -- see .mt-row.on, the match sheet's picked row:
   a real background and a left edge. It outranks hover in both directions,
   so the state holds while the pointer moves and while the bar is open. */
.kt-tbl tbody tr.selrow td{background:color-mix(in srgb,var(--accent) 14%,var(--surface))}
.kt-tbl tbody tr.selrow td:first-child{box-shadow:inset 3px 0 0 var(--accent)}
.kt-tbl tbody tr.selrow:hover td{background:color-mix(in srgb,var(--accent) 19%,var(--surface))}
.kt-tbl tbody tr.selrow:hover td:first-child{box-shadow:inset 3px 0 0 var(--accent)}
/* pager — THE paging strategy, with first/last one click away */
.kt-foot{display:flex;align-items:center;gap:10px;padding:10px 14px;border-top:1px solid var(--hairline);font-size:12px;color:var(--ink-3)}
.kt-foot b{color:var(--ink);font-variant-numeric:tabular-nums;font-weight:600}
.kt-foot .pgs{margin-left:auto;display:flex;align-items:center;gap:3px}
.kt-pg{min-width:26px;height:26px;padding:0 7px;border-radius:7px;display:grid;place-items:center;font-size:11.5px;font-weight:550;font-variant-numeric:tabular-nums;color:var(--ink-2);cursor:pointer;border:1px solid transparent;transition:background var(--t-fast) ease,border-color var(--t-fast) ease,color var(--t-fast) ease,transform var(--t-fast) var(--ease-spring)}
.kt-pg:hover{background:var(--surface-2);color:var(--ink)}
.kt-pg:active{transform:scale(.92)}
.kt-pg.on{background:var(--surface);border-color:var(--hairline);color:var(--ink);font-weight:650;box-shadow:0 1px 2px rgba(var(--shadow-c),.05)}
.kt-pg.dis{opacity:.35;pointer-events:none}
.kt-pg.end{font-weight:650}

/* ⚠ ON A NARROW SCREEN THE VERDICT'S ACTS TAKE A LINE OF THEIR OWN.
   .fc-vactions is one nowrap row pushed right by margin-left:auto, and .btn
   has a set height — so at 400px "Download agent" and "Add asset" wrapped onto
   a second line INSIDE a 24px button and spilled out of the bottom of it.
   Nothing measuring the button could see it: its height stayed 24px and its
   scrollWidth stayed inside its clientWidth. Only scrollHeight knew, which is
   what tools/verify-registers.js checks now.
   Full width under 720px lets the row sit below the count it belongs to and
   its buttons flow onto as many lines as they need, each at its natural width.
   This rule is drawn by UI.faces on every record surface in the product and
   every one of them was clipped at this width. */
@media (max-width:720px){
  .fc-vline{align-items:flex-start}
  .fc-vactions{margin-left:0;width:100%;flex-wrap:wrap}
  .fc-vactions .btn{white-space:nowrap}
}

@media (max-width:720px){
  .kt-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch}
  .kt-tbl{min-width:max-content}
  .kt-foot{align-items:flex-start;flex-direction:column;padding:10px 8px}
  .kt-foot .pgs{margin-left:0;max-width:100%;overflow-x:auto;padding:2px 0}
  .kt-pg{min-width:34px;height:34px}
}
.kt-gap{color:var(--ink-3);padding:0 2px}
.kt-empty:empty{display:none}

/* ================================================================
   3 · COMMIT SHEET  (.cs) — the only centred overlay in the product
   ================================================================ */
.cs-veil{position:fixed;inset:0;background:color-mix(in srgb,var(--ink) 34%,transparent);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);opacity:0;transition:opacity var(--t-fast) ease;z-index:120}
.cs-veil.on{opacity:1}
.cs{position:fixed;left:50%;top:50%;transform:translate(-50%,-47%) scale(.97);width:560px;max-width:94vw;max-height:88vh;display:flex;flex-direction:column;background:var(--surface);border:1px solid var(--hairline);border-radius:14px;box-shadow:0 24px 70px -12px rgba(var(--shadow-c),.35);z-index:121;opacity:0;transition:opacity var(--t-fast) ease,transform var(--t-med) var(--ease-spring)}
.cs.on{opacity:1;transform:translate(-50%,-50%) scale(1)}
@keyframes cs-shake{0%,100%{transform:translate(-50%,-50%)}22%{transform:translate(calc(-50% - 5px),-50%)}55%{transform:translate(calc(-50% + 4px),-50%)}80%{transform:translate(calc(-50% - 2px),-50%)}}
.cs.err-hit{animation:cs-shake .38s var(--ease-out)}
.cs-head{display:flex;align-items:center;gap:10px;padding:15px 18px 12px;border-bottom:1px solid var(--hairline)}
.cs-title{font-size:14.5px;font-weight:600;letter-spacing:-.008em}
.cs-x{margin-left:auto;width:26px;height:26px;border-radius:7px;display:grid;place-items:center;color:var(--ink-3);cursor:pointer;transition:background var(--t-fast) ease,color var(--t-fast) ease}
.cs-x:hover{background:var(--surface-2);color:var(--ink)}
.cs-subject{display:flex;align-items:baseline;gap:9px;padding:11px 18px 0;flex-wrap:wrap}
.cs-subject .kw-tag{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.05em;color:var(--ink-2);border:1px solid var(--hairline);border-radius:5px;padding:2px 7px;background:var(--surface-2)}
.cs-subject span:not(.kw-tag){font-size:12.5px;color:var(--ink-2);line-height:1.55}
.cs-body{flex:1;overflow-y:auto;padding:6px 18px 4px}
/* the consequence ledger — computed from the estate, and it ARRIVES:
   rows settle in once on open, then hold still */
.cs-ledger{margin-top:8px}
.cs-lrow{display:flex;align-items:baseline;gap:12px;padding:7.5px 2px;border-bottom:1px solid var(--hairline-2)}
.cs-ledger .cs-lrow:last-child{border-bottom:0}
@keyframes cs-settle{0%{opacity:0;transform:translateY(-4px)}100%{opacity:1;transform:none}}
.cs-lrow{animation:cs-settle var(--t-med) var(--ease-out) both}
.cs-ll{font-size:12.5px;color:var(--ink-2)}
/* ⚠ `white-space:nowrap` was right for what this is FOR — a short value, a
   count, a sum, kept on one line against its label. It is silent damage for
   what actually gets passed: two separate builders wrote a sentence into a
   ledger value, and it ran off the edge of the sheet with no ellipsis, no
   wrap and no error. A commit sheet is the last thing a person reads before
   an irreversible act; a clipped word there is the worst place in the product
   to lose text.
   A value that is short still sits on one line, because it has nothing to
   wrap. One that is a sentence now wraps and stays readable, right-aligned
   with its label, rather than disappearing past the edge. */
.cs-lv{margin-left:auto;font-size:12.5px;font-weight:650;font-variant-numeric:tabular-nums;color:var(--ink);text-align:right;max-width:62ch;overflow-wrap:anywhere}
.cs-lrow.bad .cs-lv{color:var(--lc-missing-ink)}
.cs-lrow.warn .cs-lv{color:var(--warn-ink)}
.cs-lrow.ok .cs-lv{color:var(--s-good-ink)}
/* extra evidence area (tag lists, prose) — the tail stays reachable INSIDE the sheet */
.cs-detail{margin-top:12px}
.cs-detail-h{font-size:10px;font-weight:650;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3);padding:4px 0 7px}
/* ⚠ This capped EVERY commit-sheet detail body at 168px and scrolled the rest.
   Right for what it was written for — a 43-tag evidence list you skim — and
   wrong for the other thing that lands here, a form. On the policies sheet it
   hid the cadence, the recipient, the mode and the impact line behind a scroll
   nobody discovers, which are exactly the controls its refusals are about. A
   reader skims evidence; a reader must SEE a control before they can be
   refused for not setting it.
   So the cap now applies only to a detail body that is a list of things, and a
   body containing form controls is never clipped. */
.cs-detail-b{max-height:168px;overflow-y:auto;scrollbar-width:thin;border:1px solid var(--hairline-2);border-radius:9px;padding:10px 11px;background:var(--surface-2)}
.cs-detail-b:has(input,select,textarea,.fld,[role="radiogroup"],.pu-chips){max-height:none;overflow:visible}

/* ---- matching a reporting machine to a register row ----
   Lives inside a commit sheet's detail area. The list scrolls on its own rather
   than growing the sheet, so the two evidence fields underneath stay reachable
   without hunting for them — a picker that pushes its own confirm off-screen is
   how people end up committing the first row they see. */
.mt-pick{display:flex;flex-direction:column;gap:9px}
.mt-list{max-height:196px;overflow-y:auto;scrollbar-width:thin;border:1px solid var(--hairline-2);border-radius:9px;background:var(--surface)}
.mt-row{display:flex;align-items:center;gap:11px;padding:9px 12px;cursor:pointer;transition:background var(--t-fast) ease}
.mt-row+.mt-row{border-top:1px solid var(--hairline-2)}
.mt-row:hover{background:var(--surface-2)}
.mt-row:focus-visible{outline:2px solid var(--accent);outline-offset:-2px}
.mt-row.on{background:var(--accent-bg);box-shadow:inset 2px 0 0 var(--accent)}
.mt-r-m{flex:1;min-width:0}
.mt-r-n{font-size:13px;font-weight:620}
.mt-r-s{font-size:11.5px;color:var(--ink-3);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.mt-r-v{flex:none;font-size:11.5px;color:var(--ink-2);font-variant-numeric:tabular-nums}
.mt-none{padding:14px 12px;font-size:12.5px;color:var(--ink-3)}
.mt-picked{font-size:12.5px;line-height:1.6;color:var(--ink-2);min-height:1.2em}
.mt-picked b{color:var(--ink);font-weight:640}
.cs-tags{display:flex;gap:6px;flex-wrap:wrap}
/* what the policy demands before commit is possible */
.cs-req{margin-top:14px;display:flex;flex-direction:column;gap:11px}
.cs-req-h{font-size:10px;font-weight:650;letter-spacing:.09em;text-transform:uppercase;color:var(--ink-3)}
.cs-r{border:1px solid var(--hairline-2);border-radius:9px;padding:10px 12px;transition:border-color var(--t-fast) ease,background var(--t-fast) ease}
.cs-r:focus-within{border-color:color-mix(in srgb,var(--accent) 40%,var(--hairline))}
.cs-r.met{border-color:color-mix(in srgb,var(--s-good) 32%,var(--hairline))}
.cs-r.nudge{animation:cs-shake .38s var(--ease-out);border-color:color-mix(in srgb,var(--warn) 55%,var(--hairline))}
.cs-r .why{font-size:11px;color:var(--ink-3);margin-top:3px;line-height:1.5}
.cs-r-row{display:flex;align-items:center;gap:10px}
.cs-r-row label{font-size:12.5px;font-weight:600;color:var(--ink);cursor:pointer}
.cs-r .inp,.cs-r .sel{margin-top:7px;width:100%}
.cs-r .met-mk{margin-left:auto;flex:none;color:var(--s-good-ink);opacity:0;transform:scale(.6);transition:opacity var(--t-fast) ease,transform var(--t-med) var(--ease-spring)}
.cs-r.met .met-mk{opacity:1;transform:none}
.cs-attach{display:inline-flex;align-items:center;gap:7px;margin-top:7px;height:30px;padding:0 12px;border:1.5px dashed var(--hairline);border-radius:8px;font-size:12px;font-weight:550;color:var(--ink-2);cursor:pointer;background:var(--surface);transition:border-color var(--t-fast) ease,color var(--t-fast) ease}
.cs-attach:hover{border-color:color-mix(in srgb,var(--accent) 45%,var(--hairline));color:var(--accent-ink)}
.cs-attach.has{border-style:solid;border-color:color-mix(in srgb,var(--s-good) 40%,var(--hairline));color:var(--s-good-ink)}
/* an error stays open and keeps the typed values */
.cs-err{display:none;margin-top:12px;padding:10px 13px;border:1px solid color-mix(in srgb,var(--danger) 45%,var(--hairline));border-left-width:3px;border-radius:9px;background:color-mix(in srgb,var(--danger) 5%,var(--surface));font-size:12px;color:var(--lc-missing-ink);line-height:1.5}
.cs-err.on{display:block;animation:cs-settle var(--t-med) var(--ease-out)}
/* denied: locked with its owner, never masked */
.cs-denied{display:flex;align-items:center;gap:10px;margin-top:14px;padding:11px 13px;border:1.5px dashed var(--hairline);border-radius:9px;font-size:12px;color:var(--ink-2);line-height:1.5}
.cs-denied svg{flex:none;color:var(--ink-3)}
.cs-refused{margin-top:4px;padding:12px 14px;border-radius:9px;background:var(--surface-2);font-size:13.5px;font-weight:550;color:var(--ink);line-height:1.5}
/* foot: the safe choice reads first */
.cs-foot{display:flex;align-items:center;gap:8px;row-gap:10px;padding:13px 18px;border-top:1px solid var(--hairline);flex-wrap:wrap}
.cs-foot .btn{white-space:nowrap}
/* the safe choice and the commit travel as one unit when the foot wraps */
.cs-btns{display:flex;gap:8px;align-items:center;margin-left:auto}
.cs-note{font-size:11px;color:var(--ink-3)}
.cs-note.hard{color:var(--warn-ink);font-weight:550}
.cs-foot .sp{flex:1}
.cs-need{font-size:11px;color:var(--ink-3);text-align:right;margin-right:2px;transition:color var(--t-fast) ease}
.cs-need.hot{color:var(--warn-ink);font-weight:550}
.cs-commit.danger{background:var(--danger);border-color:var(--danger);color:var(--surface)}
.cs-commit.danger:hover{opacity:.88;color:var(--surface);box-shadow:none}
.cs-commit.dis{opacity:.45;cursor:not-allowed}
.cs-commit.dis:hover{box-shadow:none;border-color:var(--danger)}
.cs-commit.busy{pointer-events:none;opacity:.7}

@media (prefers-reduced-motion:reduce){
  .fc-thumb{transition:none}
  .cs-lrow{animation:none}
}

/* ⚠ AN ACTIONS COLUMN. The row's own controls, kept on one line and pinned to
   the right-hand edge of the table so the last column is where a reader looks
   for it. The column is sized to its content: `width:1%` with nowrap is how a
   table is told "take exactly what these buttons need", which leaves every
   remaining pixel to the columns that carry the reading. */
.kt-tbl td.kt-acts{white-space:nowrap;text-align:right;width:1%}
.kt-tbl td.kt-acts .btn{margin-left:6px}
.kt-tbl td.kt-acts .btn:first-child{margin-left:0}
.kt-tbl th.kt-acts-h{width:1%;white-space:nowrap}
