/*
 * Ported from docs/reference/collab-field-controls.html — the visual source
 * of truth for ui-web (CLAUDE.md). Loaded alongside Bootstrap's CSS (for
 * baseline grid/utilities/forms/modal styling — ADR-0019), not a replacement
 * for it.
 */
:root{
  --cf-surface:#ffffff;
  --cf-page:#f4f6f8;
  --cf-ink:#1b2430;
  --cf-ink-soft:#5c6a7a;
  --cf-rule:#ced6df;
  --cf-mine:#0d8a7d;
  --cf-pending:#8a8f98;
  --cf-remote:#2563eb;
  --cf-conflict:#b45309;
  --cf-system:#aab4c0;
  --cf-mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
}
/*
 * `hidden` has to mean hidden.
 *
 * The attribute works through the UA stylesheet's `[hidden]{display:none}`,
 * and ANY author rule setting `display` outranks a UA rule — so Bootstrap's
 * display utilities, which are `display:… !important`, quietly win. An
 * element carrying `d-flex` or `d-inline-block` therefore stayed on screen
 * when code set `.hidden = true`, with nothing anywhere to say so.
 *
 * Three of those were live when this rule was written: the "not a money leg"
 * dash in the transaction editor, which never went away and pushed the wallet
 * field out of line with its row; the report's tag any/all switch; and the
 * report's "Delete all transactions…" bar, which is meant to appear for
 * administrators only and was appearing for everybody. The server refuses the
 * action regardless, so that was an offered button rather than a granted
 * power — but offering it is its own kind of wrong.
 *
 * Author level and `!important`, because that is what it takes to outrank a
 * utility class that is also `!important`.
 */
[hidden]{display:none!important}

body{background:var(--cf-page);color:var(--cf-ink);font-size:15px}
.cf-shell{max-width:1240px}
.cf-card{background:var(--cf-surface);border:1px solid #dde3ea;border-radius:10px}
/* A card holding a full-width list spends ~15% of a 375px viewport on chrome
   (container gutter + card padding + border) and buys little there: filling
   the screen, it has nothing beside it to be grouped apart from. Below 640px
   the table breaks out to the card's edges and the side border/radius go,
   keeping the surface colour, the vertical rhythm, and the padding that any
   header row or prose inside the card still wants.
   Opt-in, not a blanket `.cf-card` rule: the same class is also the centred
   login panel and the launcher's app tiles, which need their padding and
   their edges. The -1rem matches these cards' own `p-3`. */
@media (max-width:640px){
  .cf-card--bleed{border-left:0;border-right:0;border-radius:0}
  /* `max-width:none` is required, not incidental: the wrapper's own
     `max-width:100%` resolves against the padded card interior and would
     cap it right back to that width, negative margins or not. Bleeding to
     the card's edge is exactly what that cap normally prevents. */
  .cf-card--bleed .cf-table-scroll{margin-left:-1rem;margin-right:-1rem;max-width:none}
}
.cf-h{font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;font-weight:700;color:var(--cf-ink-soft)}

/* ===================================================================
   COMPACT FIELD — notched outline
=================================================================== */
/* Idle default is the same grey as the rest of the border, not transparent —
   a fully transparent left edge is indistinguishable from a missing one; the
   notched-outline design only reads as intentional if the thicker left rule
   is always visible, with colour reserved for signalling a real state change. */
.cf-f{position:relative;margin-bottom:14px;--cf-state:var(--cf-rule)}
.cf-f__box{position:relative;display:block}
.cf-f .form-control{
  height:44px;padding:.55rem .75rem;padding-right:44px;
  border:1px solid var(--cf-rule);border-left-width:3px;border-left-color:var(--cf-state);
  border-radius:8px;background:var(--cf-surface);
  transition:border-color .16s ease,box-shadow .16s ease;
}
.cf-f textarea.form-control{height:auto;min-height:62px;padding-top:.7rem}
.cf-f .form-control:focus{box-shadow:0 0 0 .18rem rgba(90,110,140,.12)}

.cf-f__label{
  position:absolute;left:12px;top:50%;transform:translateY(-50%);transform-origin:left center;
  pointer-events:none;color:#8d97a3;font-size:.92rem;padding:0 5px;background:transparent;
  transition:top .14s ease,font-size .14s ease,color .14s ease,background-color .14s;
  white-space:nowrap;max-width:calc(100% - 60px);overflow:hidden;text-overflow:ellipsis;
}
.cf-f textarea + .cf-f__label{top:1.1rem;transform:none}
.cf-f .form-control:focus + .cf-f__label,
.cf-f .form-control:not(:placeholder-shown) + .cf-f__label{
  top:0;transform:translateY(-50%);font-size:.7rem;font-weight:600;letter-spacing:.02em;
  color:var(--cf-ink-soft);background:var(--cf-surface);
}
.cf-f textarea.form-control:focus + .cf-f__label,
.cf-f textarea.form-control:not(:placeholder-shown) + .cf-f__label{top:0;transform:translateY(-50%)}

.cf-f__state{
  position:absolute;right:10px;top:0;transform:translateY(-50%);display:none;
  align-items:center;gap:.25rem;height:16px;padding:0 5px;background:var(--cf-surface);
  border-radius:8px;font-size:.68rem;font-weight:600;white-space:nowrap;z-index:2;
}
.cf-f:not(.is-idle):not(.is-system) .cf-f__state{display:inline-flex}
.cf-f__state .cf-dot{
  width:13px;height:13px;border-radius:50%;display:grid;place-items:center;
  color:#fff;font-size:.5rem;font-weight:700;flex:0 0 auto;
}
@media (max-width:520px){ .cf-f__state .cf-txt{display:none} }

.cf-f.is-mine    {--cf-state:var(--cf-mine)}
.cf-f.is-mine    .form-control{border-color:var(--cf-mine)}
.cf-f.is-mine    .cf-f__state{color:var(--cf-mine)}
.cf-f.is-pending {--cf-state:var(--cf-pending)}
.cf-f.is-pending .form-control{border-left-style:dashed}
.cf-f.is-locked  {--cf-state:#7c3aed}
.cf-f.is-locked  .form-control{background:#fbfcfd;color:var(--cf-ink-soft);cursor:default}
.cf-f.is-remote  {--cf-state:var(--cf-remote)}
.cf-f.is-remote  .form-control{animation:cf-flash 2.4s ease-out 1}
.cf-f.is-remote  .cf-f__state{color:var(--cf-remote)}
.cf-f.is-conflict{--cf-state:var(--cf-conflict)}
.cf-f.is-conflict .form-control{border-color:var(--cf-conflict)}
.cf-f.is-conflict .cf-f__state{color:var(--cf-conflict)}
.cf-f.is-system  {--cf-state:var(--cf-system)}
.cf-f.is-system  .form-control{background:#f7f8fa;color:var(--cf-ink-soft);border-style:dashed}
.cf-f.is-system  .cf-f__label{color:var(--cf-system)}

@keyframes cf-flash{0%{background:rgba(37,99,235,.16)}100%{background:var(--cf-surface)}}
@keyframes cf-rowflash{0%{background:rgba(37,99,235,.14)}100%{background:transparent}}
@keyframes cf-pulse{0%,100%{opacity:.3}50%{opacity:1}}
@media (prefers-reduced-motion:reduce){
  .cf-f.is-remote .form-control{animation:none;background:rgba(37,99,235,.09)}
  .cf-f__label,.cf-f .form-control{transition:none}
  tr.is-remote{animation:none !important;background:rgba(37,99,235,.08) !important}
}

.cf-f__tools{position:absolute;right:7px;top:22px;transform:translateY(-50%);display:flex;align-items:center;gap:.15rem;z-index:2}
.cf-f textarea + .cf-f__label ~ .cf-f__tools{top:20px}
.cf-hist-btn{
  border:0;background:transparent;color:#aeb8c4;padding:.2rem .3rem;border-radius:6px;
  display:inline-flex;align-items:center;gap:.15rem;font-size:.68rem;line-height:1;
  transition:color .14s,background-color .14s;
}
.cf-hist-btn:hover,.cf-hist-btn:focus-visible{color:var(--cf-ink);background:#eef1f5}
.cf-f.is-conflict .cf-hist-btn{color:var(--cf-conflict);font-weight:700}
.cf-hist-btn__n{font-variant-numeric:tabular-nums;letter-spacing:-.02em}

.cf-f--toggle{
  display:flex;align-items:center;gap:.6rem;
  border:1px solid var(--cf-rule);border-left-width:3px;border-left-color:var(--cf-state);
  border-radius:8px;height:44px;padding:0 .6rem 0 .75rem;position:relative;
}
.cf-f--toggle .cf-f__state{right:10px}
.cf-f--toggle .cf-f__tools{position:static;transform:none;margin-left:auto}
.cf-f--toggle label{font-size:.9rem;margin:0}

/* ===================================================================
   HISTORY PANEL
=================================================================== */
.cf-hist{position:absolute;z-index:1080;right:0;top:calc(100% + 4px);
  width:min(380px,calc(100vw - 32px));background:#fff;border:1px solid #dde3ea;
  border-radius:9px;box-shadow:0 12px 30px rgba(20,30,45,.16);padding:.55rem 0 .3rem;display:none}
.cf-hist.show{display:block}
.cf-hist__title{font-size:.7rem;text-transform:uppercase;letter-spacing:.05em;
  color:var(--cf-ink-soft);font-weight:700;padding:0 .75rem .4rem}
.cf-hist__row{padding:.4rem .75rem;border-top:1px solid #f0f2f5}
.cf-hist__row:hover{background:#f8fafc}
/* Flex, not grid: a grid/flex item defaults to min-width:auto, which sizes
   it to its content's min-content (the longest unbreakable run) regardless
   of word-break — `min-width:0` here is what actually lets long values wrap
   normally instead of being squeezed into a sliver by the dot/badge tracks. */
.cf-hist__top{display:flex;align-items:center;gap:.5rem}
.cf-hist__top .cf-dot{flex:0 0 auto}
.cf-hist__val{font-family:var(--cf-mono);font-size:.78rem;word-break:break-word;
  flex:1 1 auto;min-width:0}
.cf-hist__meta{font-size:.7rem;color:var(--cf-ink-soft);margin-top:.15rem;padding-left:22px}
.cf-hist__origin{font-size:.62rem;text-transform:uppercase;letter-spacing:.04em;
  border:1px solid #dde3ea;border-radius:3px;padding:0 .22rem;color:var(--cf-ink-soft)}
.cf-hist__origin.offline{border-color:#e0b072;color:#96650f;background:#fdf6ec}
.cf-hist__cur{font-size:.62rem;text-transform:uppercase;letter-spacing:.04em;
  color:var(--cf-mine);font-weight:700;flex:0 0 auto}
.cf-hist__restore{font-size:.7rem;--bs-btn-padding-y:.05rem;--bs-btn-padding-x:.4rem}
.cf-review{margin:.15rem .55rem .45rem;border:1px solid #eccb96;background:#fdf8ef;
  border-radius:7px;padding:.5rem .6rem}
.cf-review__h{font-size:.72rem;font-weight:700;color:#8a5b08;margin-bottom:.35rem}
.cf-review__opt{display:flex;align-items:center;justify-content:space-between;gap:.5rem;
  font-family:var(--cf-mono);font-size:.75rem;padding:.2rem 0}

/* ===================================================================
   RECORD TABLE
=================================================================== */
/* Scopes overflow to the table itself, never the page (see CLAUDE.md's
   responsive rule) — a row wide enough to need it gets a local scrollbar
   instead of bleeding past the card into whatever is behind it. */
.cf-table-scroll{overflow-x:auto;max-width:100%}
.cf-table{margin-bottom:0;font-size:.85rem}
.cf-table thead th{font-size:.68rem;text-transform:uppercase;letter-spacing:.05em;font-weight:700;
  color:var(--cf-ink-soft);border-bottom:1px solid #dde3ea;padding:.4rem .5rem;
  white-space:nowrap;background:#fafbfc}
.cf-table td{padding:.45rem .5rem;vertical-align:middle;border-bottom:1px solid #f0f2f5}
.cf-table tbody tr:hover{background:#fafbfd}
.cf-table td.cf-gut{width:6px;padding:0 0 0 3px}
.cf-table td.cf-gut span{display:block;width:4px;height:26px;border-radius:4px;background:transparent}
tr.is-open td.cf-gut span{background:var(--row-color,#7c3aed)}
tr.is-remote{animation:cf-rowflash 2.6s ease-out 1}
tr.is-remote td.cf-gut span{background:var(--cf-remote)}
tr.is-tombstone td{color:#b3bcc6;text-decoration:line-through}
/* Grouping row for a table grouped by application (e.g. role grants) — a
   plain, non-interactive header between groups, not a record of its own. */
tr.cf-role-group td{background:#f7f8fa;font-size:.68rem;text-transform:uppercase;
  letter-spacing:.05em;font-weight:700;color:var(--cf-ink-soft);
  padding:.4rem .5rem;border-top:1px solid #dde3ea}
tr.cf-role-group:first-child td{border-top:0}
.cf-date{font-family:var(--cf-mono);font-size:.78rem;white-space:nowrap}
.cf-date.sys{color:var(--cf-system)}
.cf-title button{border:0;background:none;padding:0;color:inherit;font:inherit;font-weight:500;
  text-align:left;text-decoration:underline;text-decoration-color:#c9d2dc;text-underline-offset:3px}
.cf-title button:hover{text-decoration-color:var(--cf-ink)}
.cf-rowpres{display:inline-flex;gap:2px;margin-left:.35rem;vertical-align:middle}
.cf-dot{width:16px;height:16px;border-radius:50%;display:inline-grid;place-items:center;
  color:#fff;font-size:.58rem;font-weight:700;flex:0 0 auto}

.cf-sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

/* "Locked by other" in a list row (SPEC §6.1) — the holder's avatar sits in
   the row's presence slot, their name and a lock beside it, so the gutter
   colour is never the only thing carrying the state. */
.cf-lockedby{font-size:.7rem;color:var(--cf-ink-soft);white-space:nowrap;margin-left:.15rem}

/* ===================================================================
   TRANSACTION LIST (webledger's register)
=================================================================== */
/* The register's rows carry lease state the way a compact field does: the
   row is an outlined block whose left edge is the holder's colour, and the
   holder rides a chip notched into the top border.
 *
 * It used to be a 4px pill in a gutter column plus a badge inline after the
 * description. That said the same thing in two weaker places at once — the
 * badge competed with the description for the same line and pushed the
 * splits summary around as names of different lengths came and went, and the
 * pill was a colour with no word attached until you reached the badge. The
 * field controls had already solved this: border for state, chip for
 * identity, and the content line left alone.
 *
 * Space is reserved either way (invariant #11): the chip overlays the top
 * border inside the cell's existing padding, so a colleague arriving on a
 * record never reflows the row. */
.wl-tx-list{border-collapse:separate;border-spacing:0 6px}
.wl-tx-list thead th{border-bottom:0;padding-bottom:.15rem}
.wl-tx-list tbody td{
  background:var(--cf-surface);
  border-top:1px solid #dde3ea;
  border-bottom:1px solid #dde3ea;
  padding:.55rem .5rem;
}
.wl-tx-list tbody tr:hover td{background:#fafbfd}
/* The gutter column becomes the block's left edge rather than a pill inside it. */
.wl-tx-list tbody td.cf-gut{
  width:0;padding:0;
  border-left:3px solid var(--row-color,#dde3ea);
  border-top-left-radius:9px;border-bottom-left-radius:9px;
}
.wl-tx-list tbody td.cf-gut span{display:none}
.wl-tx-list tbody td:last-child{
  border-right:1px solid #dde3ea;
  border-top-right-radius:9px;border-bottom-right-radius:9px;
}
.wl-tx-list td.cf-title{position:relative}
.wl-tx-list .wl-state-chip{position:absolute;left:.45rem;top:0;transform:translateY(-50%)}
/* Notched into the row's top border, same trick as the field's state chip:
   an opaque background cutting the line it sits on. */
.wl-state-chip{
  display:inline-flex;align-items:center;gap:.28rem;
  height:16px;padding:0 6px;
  background:var(--cf-surface);border-radius:8px;
  font-size:.66rem;font-weight:600;line-height:1;white-space:nowrap;
  color:var(--cf-ink-soft);
  z-index:1;
}
.wl-state-chip .cf-dot{width:13px;height:13px;font-size:.5rem}
/* Yours, interrupted. Amber outranks the holder's colour on the border:
   a decision of yours waiting on this row matters more to you than who
   happens to be holding it, and the chip still names them. */
.wl-tx-list tbody tr.wl-tx-held td.cf-gut{border-left-color:var(--cf-conflict)}
/* Queued, not contended: the muted "provisional" grey, not an alarm colour. */
.wl-tx-list tbody tr.wl-tx-pending td.cf-gut{border-left-color:var(--cf-pending)}
.wl-state-chip--held{color:#8a5b08}
.wl-state-chip--queued{color:var(--cf-conflict)}
@media (max-width:640px){
  /* The chip would collide with a wrapped description on a narrow row. */
  .wl-state-chip .wl-state-chip__txt{max-width:14ch;overflow:hidden;text-overflow:ellipsis}
}
/* The splits summary is the informative column and the currency code the
   least, but on a phone the currency held a whole column while the splits
   sat off-screen entirely — and, still being laid out out there, wrapped
   into enough lines to balloon every row. Below 640px both dedicated
   columns fold into one muted line under the description (code first, then
   the legs), clamped so a many-split transaction can't grow the row.
   Reduce, don't reflow — the same approach as the state chip's text and the
   split header's currency echo. */
.wl-tx-sub{display:none}
@media (max-width:640px){
  .wl-tx-list .wl-tx-cur,
  .wl-tx-list .wl-tx-legs{display:none}
  .wl-tx-list .wl-tx-sub{
    display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
    font-size:.72rem;line-height:1.35;color:var(--cf-ink-soft);margin-top:.15rem;
  }
}

/* A draft of yours that somebody interrupted, shown beside the value that
   replaced it. The control above keeps belonging to whoever holds the record
   — it updates live as they type — and this strip carries the half that is
   yours, so nothing has to be painted over your work to show you theirs.
   Amber and a word, never colour alone (invariant #11). */
.wl-held{
  display:flex;align-items:center;gap:.4rem;flex-wrap:wrap;
  min-width:0;
  border:1px solid var(--cf-conflict);border-left-width:3px;
  border-radius:0 0 7px 7px;
  background:#fdf8ef;
  padding:.25rem .4rem .25rem .55rem;
  margin-top:-1px;                       /* share the control's bottom edge */
  font-size:.74rem;line-height:1.3;
}
.wl-held + .wl-held{margin-top:4px}
.wl-held b{color:#8a5b08;font-size:.66rem;text-transform:uppercase;letter-spacing:.04em;white-space:nowrap}
.wl-held__who{display:inline-flex;align-items:center;gap:.2rem;color:var(--cf-ink-soft);font-size:.68rem;white-space:nowrap}
.wl-held__v{font-family:var(--cf-mono);font-size:.73rem;color:var(--cf-ink);flex:1 1 auto;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.wl-held .btn{font-size:.66rem;--bs-btn-padding-y:.02rem;--bs-btn-padding-x:.35rem;flex:0 0 auto}
/* The split-table variant: an extra row under the leg it belongs to, so the
   table keeps its columns instead of growing a parallel one. */
tr.wl-held-row td{background:transparent;border-top:0;padding:.1rem .5rem .3rem}
.wl-held--row{border-radius:7px;margin-top:0}

/* ===================================================================
   HELP POPOVER
=================================================================== */
/* Same shape as the history panel above — a borderless icon button owning a
   positioned panel — rather than Bootstrap's Popover plugin, which would
   pull in a second Bootstrap JS component (only Modal is used anywhere in
   this codebase) and need per-open init/dispose. Hover is gated behind
   `hover: hover` so a tap on a touch screen doesn't leave it stuck open
   through emulated hover; touch gets the click toggle (`.show`) instead. */
.cf-help{position:relative;display:inline-flex}
.cf-help__btn{
  border:0;background:transparent;color:#aeb8c4;padding:.15rem .25rem;border-radius:6px;
  display:inline-flex;align-items:center;line-height:1;transition:color .14s,background-color .14s;
}
.cf-help__btn:hover,.cf-help__btn:focus-visible{color:var(--cf-ink);background:#eef1f5}
.cf-help__panel{
  position:absolute;z-index:1080;right:0;top:calc(100% + 6px);
  /* Right-anchored to a button that is itself inset from the viewport edge
     (modal side margin + header padding), so the cap has to leave room for
     that inset as well — sized against the viewport alone, the panel hangs
     off the *left* edge on a phone. 4.5rem covers both sides' worth plus a
     little slack; on a roomy viewport the 340px preference wins. */
  width:min(340px,calc(100vw - 4.5rem));background:var(--cf-surface);border:1px solid #dde3ea;
  border-radius:9px;box-shadow:0 12px 30px rgba(20,30,45,.16);
  padding:.55rem .7rem;font-size:.78rem;font-weight:400;line-height:1.45;
  color:var(--cf-ink-soft);text-align:left;text-transform:none;letter-spacing:normal;display:none;
}
.cf-help__panel.show{display:block}
@media (hover:hover){
  .cf-help:hover .cf-help__panel,
  .cf-help__btn:focus-visible + .cf-help__panel{display:block}
}

/* ===================================================================
   SPLIT TABLE (webledger's transaction editor)
=================================================================== */
/* Six columns of real controls do not fit a phone, and the two failure modes
   are both worse than scrolling: left to shrink, the account select collapses
   to a couple of characters ("Te", "OT") and the acct-currency column falls
   off the edge; with no scroll container it spills straight past the modal in
   landscape. So the table keeps a legible floor and its `.cf-table-scroll`
   wrapper scrolls it, per this file's own rule that wide content scrolls
   inside its own box rather than bleeding past the card. */
/* Column order: account, +, −, acct-currency amount, memo, remove. These are
   positional, so they move with the header and `addRow`'s template. */
/* ---- A field with its label notched into its border ----
   See `notchedField.ts` for why the label interrupts the border rather than
   sitting on top of it. The frame is drawn by the fieldset, so the control
   inside carries no border of its own and the two can never disagree. */
/* `min-width:0` because a fieldset's default `min-inline-size:min-content`
   will not let a table cell shrink, and this lives in one. */
.wl-nf{display:block;min-width:0;margin:0;padding:0 .35rem .25rem;
  border:1px solid var(--cf-rule);border-radius:.375rem;background:transparent}
.wl-nf>legend{float:none;width:auto;max-width:100%;padding:0 .25rem;margin:0 0 0 .1rem;
  font-size:.6rem;line-height:1;white-space:nowrap;overflow:hidden;color:var(--cf-ink-soft);
  text-transform:uppercase;letter-spacing:.04em}
/* An unnamed field — the amount columns before an account is chosen — should
   not leave a notch in the border with nothing in it. */
.wl-nf>legend:empty{display:none}
.wl-nf__ctl{display:block;width:100%;border:0;background:transparent;outline:none;
  padding:.1rem .15rem .15rem;font-size:.82rem;line-height:1.35;color:var(--cf-ink);
  box-shadow:none}
.wl-nf:focus-within{border-color:var(--cf-remote);box-shadow:0 0 0 .15rem rgba(37,99,235,.15)}
/* A locked field is readonly, never disabled (invariant 4) — so it keeps its
   place in the tab order and its text stays selectable. */
.wl-nf__ctl[aria-readonly="true"]{cursor:default;user-select:text}

/* The account field's control is a button, because what it opens is a tree
   rather than a list. It has to read like the inputs beside it, not like an
   action: same type, same weight, same alignment, with only the caret to say
   that something happens when you press it. */
.wl-acct-btn{display:flex;align-items:center;gap:.3rem;text-align:left;font-weight:400;
  overflow:hidden;cursor:pointer}
/* The name is two parts, so the RIGHT one can be the one that survives a
   narrow field. The lead shrinks and ellipsises; the account's own name does
   not shrink at all, because it is the half that differs between accounts —
   the full chain is on the button's `title` either way. */
.wl-acct-btn__name{flex:1;min-width:0;display:flex;align-items:baseline;white-space:nowrap;overflow:hidden}
.wl-acct-lead{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;color:var(--cf-ink-soft)}
.wl-acct-leaf{flex:0 0 auto;overflow:hidden;text-overflow:ellipsis}
.wl-acct-btn__name--empty{color:var(--cf-ink-soft)}
.wl-acct-btn__caret{flex:none;color:var(--cf-ink-soft);font-size:.7rem}
.wl-acct-btn:hover{background:#f7f9fb}
.wl-acct-btn .wl-ico{margin-right:0}

/* Bank sync: one feed account is a small block, not a table row. The mapping
   is three controls of very different shapes — two account trees and a date —
   and five columns of them become unreadable the moment an account name is
   longer than "Cash". Stacked on a phone, side by side from `sm` up. */
.wl-bank-acct{border-top:1px solid #e8edf2;padding:.75rem 0}
.wl-bank-acct:first-child{border-top:0;padding-top:.25rem}
.wl-bank-acct__head{display:flex;justify-content:space-between;align-items:flex-start;
  gap:.75rem;flex-wrap:wrap}
.wl-bank-acct__map{display:grid;grid-template-columns:1fr;gap:.5rem;margin-top:.5rem;
  align-items:end}
@media (min-width:576px){
  .wl-bank-acct__map{grid-template-columns:1fr 1fr 10rem auto}
}
/* A disabled rule is kept but never matches, so it has to read as present and
   inert — dimmed, with the word "Off" in its own column doing the actual
   telling (invariant 11). */
.wl-bank-rule--off>td{opacity:.55}

/* The register's header is the one place a drag means "move this column".
   The grip is a hit area at the right edge; it is not itself draggable, or
   the browser would start a column drag the moment it was pressed and the
   resize would never begin. */
.wl-reg thead th.wl-col{position:relative;cursor:grab;user-select:none}
/* Wide, and straddling the boundary, because missing it is not a near miss —
   it grabs the column and moves it somewhere else. 16px centred on the edge
   is roughly a fingertip and is what people aim at anyway. */
.wl-col__grip{position:absolute;top:0;right:-8px;width:16px;height:100%;cursor:col-resize;
  touch-action:none;z-index:2}
.wl-col__grip::after{content:"";position:absolute;top:20%;right:7px;width:1px;height:60%;
  background:#cbd5e1}
.wl-col__grip:hover::after{background:var(--cf-remote,#2563eb);width:2px}
.wl-col--dragging{opacity:.5}
.wl-col--over{box-shadow:inset 3px 0 0 var(--cf-remote,#2563eb)}

/* `table-layout: fixed` is what makes the register's columns obey the width
   they are given instead of the width their content wants. Without it the
   Balance column can never be dragged narrower than the widest chip in it,
   which is exactly the complaint that produced this rule. The table carries
   its own pixel width — the sum of its columns — and the scroll container
   deals with anything wider than the window. */
.wl-reg{table-layout:fixed}
/* So a long description wraps inside its column rather than pushing through
   the one beside it. */
.wl-reg td,.wl-reg th{overflow-wrap:anywhere}
/* Numeric cells stop being nowrap *between* chips: that is what lets Balance
   get narrow, with its currencies stacking instead of sitting in one
   unbreakable line. A chip itself still never breaks — `.cf-cur-chip` keeps
   its own `white-space:nowrap`. */
.wl-reg .wl-num{white-space:normal}

/* Attachments. A tile is the thumbnail when there is one and the file's type
   when there is not — "there is a file here" and "there is a PDF here" answer
   different questions, and the second is usually the one being asked. */
.wl-att{position:relative;display:inline-block;margin:0 3px 3px 0;vertical-align:middle}
.wl-att__link{display:block;width:34px;height:34px;border:1px solid #dde3ea;border-radius:6px;
  overflow:hidden;background:#f4f6f8;text-decoration:none}
.wl-att__link img{width:100%;height:100%;object-fit:cover;display:block}
.wl-att__kind{display:flex;align-items:center;justify-content:center;width:100%;height:100%;
  font-size:.58rem;font-weight:700;letter-spacing:.04em;color:var(--cf-ink-soft)}
.wl-att__link:hover{border-color:var(--cf-remote,#2563eb)}
.wl-att__x{position:absolute;top:-6px;right:-6px;width:16px;height:16px;line-height:14px;
  border-radius:999px;border:1px solid #dde3ea;background:#fff;color:var(--cf-ink-soft);
  font-size:.7rem;padding:0;cursor:pointer}
.wl-att__x:hover{color:var(--cf-conflict);border-color:var(--cf-conflict)}
/* The editor's row of files: label, tiles, the button, then whatever it has
   to say — laid out so adding a file never moves the form under it. */
.wl-att-bar{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;min-height:2.25rem}
/* The file input is inside its own label, so the label *is* the button. */
.wl-att-bar label.disabled{pointer-events:none;opacity:.6}
/* In the register the tiles are smaller: the column is a glance, not a gallery. */
.wl-reg .wl-att__link{width:26px;height:26px}

/* The bank-origin badge and its hover panel — see webledgerBankInfo.ts.
   The badge sits inline in a description, small enough not to compete with
   the text and large enough to be a pointer target; it is focusable, so it
   needs a visible focus ring of its own rather than inheriting a cell's. */
.wl-bankbadge{display:inline-block;margin-left:.3rem;font-size:.78em;line-height:1;
  cursor:default;opacity:.75;vertical-align:baseline}
.wl-bankbadge:hover,.wl-bankbadge:focus{opacity:1}
.wl-bankbadge:focus-visible{outline:2px solid var(--cf-remote,#2563eb);outline-offset:2px;border-radius:3px}
/* Fixed, not absolute: the panel is placed against the viewport so it stays
   whole next to a row near the bottom of a long, scrolled register. */
.wl-bi{position:fixed;z-index:1080;max-width:28rem;max-height:60vh;overflow:auto;
  padding:.6rem .7rem;border:1px solid #dde3ea;border-radius:8px;background:#fff;
  box-shadow:0 6px 24px rgba(16,24,40,.14);font-size:.8rem;
  /* It follows the pointer's row and must never swallow the hover that opened it. */
  pointer-events:none}
.wl-bi__title{font-weight:600;margin-bottom:.35rem}
.wl-bi__entry{font-weight:600;color:var(--cf-ink-soft);margin:.25rem 0}
.wl-bi__row{display:flex;gap:.75rem;justify-content:space-between;padding:.1rem 0}
.wl-bi__row span:first-child{color:var(--cf-ink-soft);white-space:nowrap}
.wl-bi__row span:last-child{text-align:right;word-break:break-word}
.wl-bi__sep{border-top:1px solid #eef1f5;margin:.35rem 0}
.wl-bi__note{color:var(--cf-ink-soft);margin-top:.35rem}

.wl-splits{min-width:44rem}
.wl-splits th:nth-child(1),.wl-splits td:nth-child(1){min-width:9rem}   /* account */
.wl-splits th:nth-child(2),.wl-splits td:nth-child(2),
.wl-splits th:nth-child(3),.wl-splits td:nth-child(3){min-width:7rem}   /* + / − */
.wl-splits th:nth-child(4),.wl-splits td:nth-child(4){min-width:11rem}  /* acct-currency amount */
.wl-splits th:nth-child(5),.wl-splits td:nth-child(5){min-width:7rem}   /* memo */
.wl-splits th:nth-child(6),.wl-splits td:nth-child(6){width:2.5rem}     /* remove */
/* The per-row +/− labels carry the account's own type wording, which is the
   point of the column; the header's currency echo is the redundant half on a
   narrow screen, so that is what goes (same reduce-don't-reflow approach as
   the state chip's text at 520px). */
@media (max-width:520px){
  .wl-splits .txCurLabel{display:none}
}

/* Money inputs: real `type="number"` (native validation, numeric keypad on
   mobile), spinner arrows suppressed — they're never used for amounts and
   steal width from the field. Legacy scoped this rule to one modal only
   (`#txModal input[type=number]`), which is why other pages there still show
   native spinners; we apply it globally instead of replicating that
   inconsistency (docs/legacy/webledger.ux-audit-shared.md). */
.cf-no-spin::-webkit-outer-spin-button,
.cf-no-spin::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
.cf-no-spin{-moz-appearance:textfield;appearance:textfield}

/* ===================================================================
   CONNECTION INDICATOR
=================================================================== */
/* ADR-0011: "The UI must say so ('editing offline, not reserved') or users
   assume the same protection they get online." A dropped socket used to be
   invisible until a save failed, so this sits in the page chrome permanently.
   Text always accompanies the dot (invariant #11 — colour is never the only
   signal), and the dot is `aria-hidden` in the markup so screen readers get
   the word alone. `is-open` reuses --cf-mine (the same green that means "this
   is yours" on a field) and `is-closed` --cf-conflict rather than a new
   palette entry. */
.cf-conn{display:inline-flex;align-items:center;gap:.35rem;font-size:.75rem;
  color:var(--cf-ink-soft);white-space:nowrap}
.cf-conn__dot{width:.5rem;height:.5rem;border-radius:50%;background:var(--cf-pending);
  flex:0 0 auto}
.cf-conn.is-open .cf-conn__dot{background:var(--cf-mine)}
.cf-conn.is-connecting .cf-conn__dot{background:var(--cf-conflict);
  animation:cf-conn-pulse 1.2s ease-in-out infinite}
.cf-conn.is-closed{color:var(--cf-conflict);font-weight:600}
.cf-conn.is-closed .cf-conn__dot{background:var(--cf-conflict)}
@keyframes cf-conn-pulse{0%,100%{opacity:1}50%{opacity:.3}}
@media (prefers-reduced-motion:reduce){
  .cf-conn.is-connecting .cf-conn__dot{animation:none}
}

/* A transaction created offline and still in the outbox. Muted rather than
   alarming — nothing is wrong, it just has not left the device yet — but
   never colour alone: the row carries a "waiting to sync" label and its own
   glyph (invariant #11). */
/* On the cells, not the row: the register's rows paint their own background
   to make the outlined-block look work, which would hide a stripe set on the
   `tr` behind them. */
.wl-tx-pending td{background:repeating-linear-gradient(135deg,transparent,transparent 6px,rgba(138,143,152,.07) 6px,rgba(138,143,152,.07) 12px)}
.wl-tx-pending .cf-lockedby{color:var(--cf-conflict)}

/* "A new version is ready" — a quiet bar, never a modal. A cache-first shell
   (ADR-0022) means a deploy reaches the user on their next reload, and this
   is how they find out; reloading unasked would discard a half-typed split
   table for a cosmetic update. Text plus a real button, in a polite live
   region (invariant #11). */
/* `width:max-content` matters: a shrink-to-fit fixed box lets its flex
   children collapse to min-content, which wrapped this to one word per line.
   Size to the natural content, and only give way when the viewport is
   genuinely narrower. */
.cf-updatebar{position:fixed;left:50%;transform:translateX(-50%);bottom:1rem;z-index:1080;
  display:flex;align-items:center;gap:.6rem;padding:.5rem .75rem;font-size:.8rem;
  background:var(--cf-surface);border:1px solid var(--cf-rule);border-radius:8px;
  box-shadow:0 4px 16px rgba(27,36,48,.14);
  width:max-content;max-width:calc(100vw - 2rem);flex-wrap:wrap}
.cf-updatebar .btn{flex:0 0 auto;white-space:nowrap}
.cf-updatebar .btn-link{padding:0;font-size:.8rem}

/* ===================================================================
   CHANGE HISTORY (webledger's transaction timeline)
=================================================================== */
/* A slide-over rather than a section inside the transaction modal: the modal
   already carries a six-column split table, and the point of the panel is to
   be readable *beside* what you are editing. Sits above the modal's own
   backdrop, and on a phone becomes a full sheet — there is no room for two
   panes at 375px. */
.cf-timeline{position:fixed;top:0;right:0;bottom:0;width:min(30rem, 100vw);z-index:1070;
  background:var(--cf-surface);border-left:1px solid var(--cf-rule);
  box-shadow:-8px 0 24px rgba(27,36,48,.12);display:flex;flex-direction:column}
.cf-timeline__head{display:flex;align-items:center;gap:.5rem;padding:.6rem .9rem;
  border-bottom:1px solid var(--cf-rule)}
.cf-timeline__body{overflow-y:auto;padding:.5rem .9rem 1.5rem}
@media (max-width:640px){ .cf-timeline{width:100vw;border-left:0} }

/* One commit. The rail and dot make the sequence readable at a glance without
   colour carrying meaning on its own (invariant #11) — every entry also
   states who, when and what in words. */
.cf-tl{position:relative;padding:.55rem 0 .55rem 1.4rem;border-left:2px solid var(--cf-rule);
  margin-left:.35rem}
.cf-tl:last-child{border-left-color:transparent}
.cf-tl__dot{position:absolute;left:-.42rem;top:.85rem;width:.7rem;height:.7rem;border-radius:50%;
  background:var(--cf-rule);border:2px solid var(--cf-surface)}
.cf-tl.is-current .cf-tl__dot{background:var(--cf-mine)}
.cf-tl.is-import .cf-tl__dot{background:var(--cf-system)}
/* The version the form is currently showing. Marked with a tinted rail and a
   "shown in the form" chip, never colour alone (invariant #11) — the form
   looks identical whichever version it holds, so this is the only thing
   saying which one you are looking at. */
.cf-tl.is-viewing{border-left-color:var(--cf-remote);background:rgba(37,99,235,.045)}
.cf-tl.is-viewing .cf-tl__dot{background:var(--cf-remote)}
.cf-tl__who{display:flex;align-items:center;gap:.35rem;flex-wrap:wrap;font-size:.8rem}
.cf-tl__when{color:var(--cf-ink-soft);font-size:.72rem}
.cf-tl__summary{font-size:.78rem;color:var(--cf-ink-soft);margin:.1rem 0 .2rem}
.cf-tl__changes{margin:.35rem 0 0;border:1px solid var(--cf-rule);border-radius:6px;overflow:hidden}
.cf-tl__change{display:grid;grid-template-columns:1fr auto auto auto;gap:.4rem;align-items:baseline;
  padding:.3rem .5rem;font-size:.75rem;border-top:1px solid var(--cf-rule)}
.cf-tl__change:first-child{border-top:0}
.cf-tl__field{color:var(--cf-ink-soft)}
.cf-tl__from{font-family:var(--cf-mono);font-size:.72rem;color:var(--cf-ink-soft);
  text-decoration:line-through;word-break:break-word}
.cf-tl__arrow{color:var(--cf-ink-soft)}
.cf-tl__to{font-family:var(--cf-mono);font-size:.72rem;word-break:break-word}
.cf-tl__warn{display:inline-flex;align-items:center;gap:.25rem;margin-top:.3rem;font-size:.7rem;
  color:var(--cf-conflict);font-weight:600}
.cf-tl__actions{margin-top:.35rem;display:flex;gap:.4rem}

/* A panel's grab area. `touch-action:none` is required, not cosmetic: without
   it a touch drag scrolls the page instead of moving the panel, because the
   browser claims the gesture before `pointermove` ever fires. */
.cf-drag-handle{cursor:grab;touch-action:none;user-select:none}
.cf-drag-handle.is-dragging{cursor:grabbing}
.cf-drag-handle:focus-visible{outline:2px solid var(--cf-remote);outline-offset:-2px}
.cf-drag-grip{color:var(--cf-ink-soft);line-height:1;letter-spacing:.1em;font-size:.8rem}
/* Dragging is a pointer/keyboard affordance with nowhere to go on a phone,
   where the panel is a full-width sheet already. */
@media (max-width:640px){ .cf-drag-handle{cursor:default} .cf-drag-grip{display:none} }

/* ===================================================================
   REPORTS (webledger's three cross-account reports)
=================================================================== */
/* The filter bar stays put while a long report scrolls under it — these
   reports have no row cap by design (legacy's "never show partial truth"),
   so "scroll back up to change the date range" is a real cost. */
.wl-filters{position:sticky;top:0;z-index:3;background:var(--cf-surface)}
.wl-filters .form-label{font-size:.7rem;text-transform:uppercase;letter-spacing:.04em;
  font-weight:700;color:var(--cf-ink-soft)}

/* The totals bar sits under the filters, not at the foot of the page: with no
   row cap the foot can be thousands of rows away, and the total is the thing
   most people came for. */
.wl-totals{position:sticky;top:64px;z-index:2;
  display:flex;flex-wrap:wrap;align-items:center;gap:.5rem;
  background:var(--cf-surface);border:1px solid #dde3ea;border-radius:9px;
  padding:.45rem .6rem;margin-bottom:1rem}


/* The same bar on a page with nothing sticky above it. The reports' 64px
   offset clears their filter bar; the wallets page has no such thing, so
   pinning it there would park the totals in the middle of the viewport. */
.wl-totals--flow{position:static}

/* Date rule between days in the all-transactions report. */
.wl-daterule{font-size:.78rem;font-weight:700;letter-spacing:.03em;
  color:var(--cf-remote);border-bottom:1px solid #dde3ea;
  padding-bottom:.25rem;margin:1rem 0 .5rem}

.wl-tx-card{border:1px solid #dde3ea;border-radius:9px}
.wl-legs{font-size:.82rem}
.wl-legs thead th{font-weight:400;font-size:.7rem;color:var(--cf-ink-soft);
  border-bottom:1px solid #f0f2f5;padding:.2rem .4rem}
.wl-legs td{padding:.28rem .4rem;vertical-align:middle}
.wl-num{text-align:right;white-space:nowrap}
/* The register vocabulary ("Increase"/"Expense"/…) beside the figure it
   describes — the sign alone does not say what a negative means for an
   income account (invariant #11 again: never the one signal). */
.wl-dir{font-size:.66rem;color:var(--cf-ink-soft);margin-right:.4rem;text-transform:uppercase;letter-spacing:.03em}
/* An account name that is also the way into that account's register, at the
   transaction you are looking at — legacy's `transfer-link`, and the thing
   that makes a report readable *from* rather than only readable.

   Quiet on purpose. A report card names two or three accounts and a register
   split line names one per row; a column of blue beside the figures would
   drown them. The underline is always there, so the link is never signalled
   by colour alone (invariant #11), and it firms up on hover and focus. */
/* An account's icon in running text — before a name in a split line, a
   Transfer cell, a trial-balance row. Smaller than the chart of accounts'
   because these sit inside a dense table, and never underlined with the link
   it sits in: an underline running under a picture reads as a mistake. */
/* The path in front of an account name is context, not the name: dimmed so
   the account's own name reads first. Opacity rather than a colour, so it
   works inside a link, on a marked row's amber and on an out-of-scope leg's
   already-muted ink — all of which a hardcoded grey would fight. */
.wl-acct-path{opacity:.6;font-weight:400}
.wl-ico{margin-right:.3rem;text-decoration:none;display:inline-block}
.wl-ico .wl-acct-icon{width:16px;height:16px;vertical-align:-3px}
.wl-acct{color:inherit;text-decoration:underline;text-decoration-color:#c9d2dc;text-underline-offset:3px}
a.wl-acct:hover,a.wl-acct:focus-visible{color:var(--cf-ink);text-decoration-color:currentColor}

/* A leg outside the active purpose or folder scope. Muted and struck from the
   running column, because it is shown for context only and counts towards
   nothing — the whole point of scoping a folder view. */
.wl-outscope td{color:var(--cf-ink-soft);opacity:.75}

@media (max-width:640px){
  .wl-totals{top:0;position:static}
  .wl-filters{position:static}
}

/* Totals-bar mode select and the "this figure is incomplete" marker. */
.wl-totalmode{width:auto;font-size:.75rem}
.wl-fx-warn{color:var(--cf-conflict);font-size:.72rem;font-weight:600;cursor:help}

/* ===================================================================
   WEBLEDGER PAGE HEADER
=================================================================== */
/* Title, tabs and page-level controls on one line, as legacy's navbar has
   them. The tabs carry the bottom rule for the whole row, so the header still
   reads as one band rather than a strip of loose controls — `align-items:
   flex-end` is what lines that rule up with the baseline of everything else. */
.wl-head{
  display:flex;align-items:flex-end;gap:.75rem;flex-wrap:wrap;
  border-bottom:1px solid #dde3ea;
}
.wl-head__brand{font-size:1.1rem;font-weight:600;line-height:1;padding-bottom:.55rem;white-space:nowrap}
.wl-head__tabs{border-bottom:0;margin-bottom:-1px;flex:1 1 auto}
.wl-head__tabs .nav-link{padding:.4rem .75rem;font-size:.88rem}
.wl-head > .cf-conn,
.wl-head > .btn{margin-bottom:.4rem}
@media (max-width:640px){
  /* The brand is the first thing to go: the tabs say where you are. */
  .wl-head__brand{display:none}
  .wl-head__tabs{order:3;flex-basis:100%}
}

/* ===================================================================
   CURRENCY CHIP (legacy's `cur-chip`)
=================================================================== */
/* One amount with its currency, used wherever a signed figure appears. The
   marker is a column — flag over a small code — so the whole thing stays
   narrow enough for a table cell while still naming the currency in words. */
.cf-cur-chips{display:inline-flex;flex-wrap:wrap;gap:4px 6px;align-items:center}
.cf-cur-chip{
  display:inline-flex;align-items:center;gap:6px;
  background:#f4f6f8;border:.5px solid #dde3ea;border-radius:999px;
  padding:2px 9px 2px 6px;
  font-variant-numeric:tabular-nums;font-size:.82rem;line-height:1.2;
  white-space:nowrap;
}
.cf-cur-mark{display:inline-flex;flex-direction:column;align-items:center;line-height:1;width:22px;flex:none}
.cf-cur-code{color:var(--cf-ink-soft);font-size:8.5px;margin-top:1px;letter-spacing:.02em}
.cf-cur-flag{flex:none;border-radius:2px;border:.5px solid rgba(0,0,0,.12);display:block}
.cf-cur-amt{font-weight:500}
/* Negative is the one thing colour alone would carry, so the minus sign stays
   in the formatted number too (invariant #11) — this only reinforces it. */
.cf-cur-amt.is-negative{color:var(--cf-conflict)}
/* A chip on a row that is out of scope counts towards nothing, and should not
   draw the eye like one that does. */
.cf-cur-chip.is-muted{opacity:.55;background:transparent}
/* Chip tally: a picked chip has to be obvious at a glance in a table of
   several hundred, and obvious without colour alone (invariant 11) — hence the
   ring plus the leading sign, which is also the only thing distinguishing an
   added pick from a subtracted one. */
.cf-cur-chip--picked{outline:2px solid var(--cf-mine,#0f766e);outline-offset:1px;border-radius:4px}
.cf-cur-chip--picked::before{content:"+";font-weight:700;color:var(--cf-mine,#0f766e);margin-right:.15rem}
.cf-cur-chip--minus{outline-color:#b91c1c}
.cf-cur-chip--minus::before{content:"\2212";color:#b91c1c}

/* The panel belongs to no page's layout: fixed, so it never moves anything
   when it appears and stays with the reader wherever they have scrolled. */
.wl-tally{position:fixed;right:12px;bottom:12px;z-index:1050;min-width:11rem;max-width:16rem;
  max-height:60vh;overflow:auto;background:var(--cf-surface);border:1px solid #dde3ea;
  border-radius:10px;box-shadow:0 6px 20px rgba(15,23,42,.14);padding:.5rem .6rem}
.wl-tally__head{display:flex;align-items:center;justify-content:space-between;gap:.5rem}
.wl-tally__x{border:0;background:transparent;line-height:1;color:var(--cf-ink-soft);cursor:pointer;
  padding:0 .15rem}
.wl-tally__x:hover,.wl-tally__x:focus-visible{color:var(--cf-ink)}
.wl-tally__rows{margin-top:.25rem}
.wl-tally__line{display:flex;align-items:center;gap:.35rem;width:100%;border:0;background:transparent;
  padding:.15rem 0;cursor:pointer;text-align:left}
.wl-tally__line:hover{background:#f7f9fb}
.wl-tally__n{font-size:.7rem;color:var(--cf-ink-soft);font-variant-numeric:tabular-nums}
.wl-tally__parts{padding:.1rem 0 .25rem .5rem;border-left:2px solid #e8edf2;margin-left:.15rem}
.wl-tally__part{display:flex;gap:.3rem;font-size:.72rem;font-variant-numeric:tabular-nums}
.wl-tally__sub{font-size:.62rem;text-transform:uppercase;letter-spacing:.04em;color:var(--cf-ink-soft);
  margin-top:.15rem}
.wl-tally__hint{font-size:.66rem;color:var(--cf-ink-soft);margin:.35rem 0 0}
/* On a phone the panel would cover the row being read; it sits across the
   bottom instead, where it covers a margin. */
@media (max-width:520px){
  .wl-tally{left:12px;right:12px;max-width:none;max-height:40vh}
  .wl-tally__hint{display:none}
}


.cf-cur-chip.is-muted .cf-cur-amt{font-weight:400;color:var(--cf-ink-soft)}

/* One leg in the register's splits summary: account, then its chip. */
.wl-leg{display:inline-flex;align-items:center;gap:.3rem;margin-right:.6rem;white-space:nowrap}

/* A report card carries the same state vocabulary a register row does: the
   holder's colour on the left edge, and a chip notched into the top border.
   Same shapes as `.wl-tx-list`, because they say the same things. */
.wl-tx-card{position:relative;border-left-width:3px;border-left-color:var(--row-color,#dde3ea)}
.wl-tx-card.wl-tx-held{border-left-color:var(--cf-conflict)}
.wl-tx-card.wl-tx-pending{border-left-color:var(--cf-pending)}
.wl-tx-card > [data-state-slot]{position:absolute;left:.6rem;top:0;transform:translateY(-50%);z-index:1}
.wl-tx-card > [data-state-slot] .wl-state-chip{position:static;transform:none}

/* ===================================================================
   CHART OF ACCOUNTS (webledger's tree)
=================================================================== */
.wl-acct-bar{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}
.wl-acct-bar__sel{width:auto;font-size:.78rem}
.wl-acct-name{display:flex;align-items:center;gap:.35rem;min-width:0}
/* Reserved whether or not this row has children, so names line up down the
   column instead of stepping in and out with the tree's shape. */
.wl-caret{
  border:0;background:none;padding:0;width:16px;flex:none;
  color:var(--cf-ink-soft);font-size:.7rem;line-height:1;
  border-radius:4px;
}
.wl-caret:hover{color:var(--cf-ink);background:#eef1f5}
.wl-caret--empty{display:inline-block;width:16px;flex:none}
.wl-acct-icon{width:18px;height:18px;flex:none;border-radius:3px;object-fit:contain}
/* Edit stays out of the way until the row is touched — every row having a
   visible button turns a 59-row tree into a wall of buttons. It remains in
   the tab order regardless (invariant #4's reasoning: never unreachable). */
.wl-acct-edit{
  border:0;background:none;padding:0 .25rem;border-radius:4px;
  font-size:.68rem;color:var(--cf-ink-soft);opacity:0;transition:opacity .12s;
}
tr:hover .wl-acct-edit,.wl-acct-edit:focus-visible{opacity:1}
.wl-acct-edit:hover{color:var(--cf-ink);background:#eef1f5}
/* The "own" line: what this account holds directly, shown only when it also
   has children (otherwise it is the subtree total said twice). */
.wl-acct-own{display:flex;align-items:center;gap:.4rem;margin-top:2px;padding-left:2.4rem}
.wl-acct-own__lbl{font-size:10px;color:var(--cf-ink-soft);text-transform:uppercase;letter-spacing:.03em}
.wl-purpose-row td{background:#f7f8fa;border-top:1px solid #dde3ea}

/* ===================================================================
   ACCOUNT REGISTER
=================================================================== */
.wl-reg-head{display:flex;align-items:center;gap:.75rem;flex-wrap:wrap}
.wl-reg-title{font-size:.95rem}
.wl-reg tbody tr[data-tx-row]{cursor:grab}
.wl-reg tbody tr[data-tx-row]:active{cursor:grabbing}
/* Where a dragged row would land. Only ever drawn on a row of the same date —
   dragging across days is refused, because the date decides the position and
   `seq` only orders within it. */
.wl-drop-target td{box-shadow:inset 0 2px 0 0 var(--cf-remote)}
/* Legs of a transaction, under its row, when "Show splits" is on. */
.wl-reg-split td{background:#fafbfd;font-size:.78rem;padding-top:.15rem;padding-bottom:.15rem;border-top:0}
.wl-reg-opening td{background:#f4f6f8;font-style:italic}

/* Drag-to-reorder feedback, shared by the account tree and the register. */
.wl-dragging{opacity:.5}
/* A visible grip, where the chart of accounts makes the whole row draggable.
   A short flat list gives no other hint that its order is the reader's to
   change, and a glyph says so without relying on discovery or on colour
   (invariant #11). */
.wl-grip{width:1.5rem;color:var(--cf-ink-soft);cursor:grab;user-select:none;text-align:center}

/* Transaction tags. A pill with a readable name in it — no colour coding,
   because a tag told apart only by hue is a tag some readers cannot tell
   apart at all (invariant #11). */
.wl-tag{display:inline-flex;align-items:center;gap:.25rem;margin:0 .25rem .25rem 0;
  padding:.1rem .45rem;border:1px solid #dde3ea;border-radius:999px;background:#f4f6f8;
  font-size:.78rem;line-height:1.35;white-space:nowrap}
.wl-tag__x{border:0;background:none;padding:0 0 0 .1rem;line-height:1;color:var(--cf-ink-soft);
  font-size:.9rem;cursor:pointer}
.wl-tag__x:hover{color:var(--cf-conflict)}
/* The editor's tag row, laid out so adding one never moves the form under it. */
.wl-tag-bar{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;min-height:2.25rem}
/* Selected in the report filter: the same pill, pressed. */
.wl-tag.is-on{background:var(--cf-remote,#2563eb);border-color:var(--cf-remote,#2563eb);color:#fff}
.wl-tag--pick{cursor:pointer}
.wl-grip:active{cursor:grabbing}

.wl-merge-sources{max-height:16rem;overflow-y:auto;border:1px solid #dde3ea;border-radius:7px;padding:.5rem}

/* ---- Tree account picker ----
   A `<select>` of 59 accounts is a flat list with no hierarchy and no search,
   in a book where three different accounts are called "Cash". The panel is
   `position:fixed` and placed against the window, so it can be taller than
   the modal it is opened from. */
.wl-apk{position:fixed;z-index:1080;display:flex;flex-direction:column;overflow:hidden;
  background:var(--cf-surface);border:1px solid #dde3ea;border-radius:10px;
  box-shadow:0 .5rem 1.5rem rgba(16,24,40,.22);max-height:80vh}
.wl-apk__head{display:flex;align-items:center;gap:.25rem;padding:.35rem .5rem;
  border-bottom:1px solid #eef1f5;flex:none}
.wl-apk__input{flex:1;min-width:0;border:0;background:transparent;color:var(--cf-ink);
  font-size:.85rem;padding:.15rem 0;outline:none}
.wl-apk__btn{border:0;background:transparent;color:var(--cf-ink-soft);line-height:1;
  padding:.15rem .35rem;border-radius:4px;font-size:.8rem}
.wl-apk__btn:hover{background:#eef1f5;color:var(--cf-ink)}
.wl-apk__close{margin-left:.15rem;border-left:1px solid #eef1f5;padding-left:.45rem}
.wl-apk__list{overflow:auto;padding:.25rem 0;flex:1 1 auto}
.wl-apk__sec{font-size:.62rem;text-transform:uppercase;letter-spacing:.05em;
  color:var(--cf-ink-soft);padding:.3rem .6rem .1rem}
.wl-apk__sec--top{border-top:1px solid #eef1f5;margin-top:.2rem}
.wl-apk__row{display:flex;align-items:center;gap:.3rem;padding:.2rem .5rem;font-size:.84rem;
  line-height:1.35;cursor:pointer;white-space:nowrap}
.wl-apk__row:hover,.wl-apk__row--active{background:#f2f5f8}
/* The chosen account carries the same marker as a chosen row anywhere else,
   and the flash settles into it — see "You are here" below. */
.wl-apk__row.wl-current{background:var(--wl-current)}
.wl-apk__row.wl-flash{animation:wl-flash-apk 1.5s ease-out 1}
@keyframes wl-flash-apk{0%{background:var(--wl-flash)}100%{background:var(--wl-current)}}
.wl-apk__row--folder .wl-apk__name{font-weight:600}
.wl-apk__caret{width:12px;flex:none;text-align:center;color:var(--cf-ink-soft);font-size:.7rem}
/* The row is a flex line with its own gap; the icon's inline margin would
   double it. */
.wl-apk__row .wl-ico{margin-right:0}
.wl-apk__name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis}
.wl-apk__cur,.wl-apk__count{font-size:.68rem;color:var(--cf-ink-soft);flex:none}
.wl-apk__count{background:#eef1f5;border-radius:8px;padding:0 .35rem}
.wl-apk__hit{background:#ffe9a8;color:inherit;padding:0;border-radius:2px}
.wl-apk__empty{padding:.6rem;font-size:.82rem;color:var(--cf-ink-soft)}
.wl-apk__foot{display:flex;gap:.75rem;padding:.25rem .6rem;border-top:1px solid #eef1f5;
  font-size:.62rem;color:var(--cf-ink-soft);flex:none}

/* ---- "You are here" marker ----
   Two parts, and the second one is the part that was missing. A bright flash
   draws the eye to the row you came back to, then decays into a lasting tint
   that stays until another row is picked — so you can look away, answer the
   phone, and still find your place. The first port kept only the flash and
   faded it to transparent from 16% alpha: by the time you looked back there
   was nothing there, which is indistinguishable from nothing having
   happened.

   Painted as a full-bleed inset shadow rather than a background, because
   Bootstrap's own striping and hover states are `inset 0 0 0 9999px` too and
   would paint straight over a background. The shadow still sits behind the
   text, and neither part of this reflows the row.

   Amber, and never amber alone: a marked row also carries
   `aria-current="true"`, which is what actually says "this one" to a screen
   reader (invariant 11). Amber rather than the blue of SPEC 6.1 — that
   vocabulary is about a *field*, and blue there means "just changed
   remotely", a claim about the data rather than about where you are. */
:root{--wl-flash:rgba(255,193,7,.80);--wl-current:rgba(255,193,7,.22);
  --wl-outline:rgba(255,193,7,.70)}
@keyframes wl-flash{0%{box-shadow:inset 0 0 0 9999px var(--wl-flash)}
  100%{box-shadow:inset 0 0 0 9999px var(--wl-current)}}
tr.wl-current>td,tr.wl-current>th{box-shadow:inset 0 0 0 9999px var(--wl-current)}
tr.wl-flash>td,tr.wl-flash>th{animation:wl-flash 1.5s ease-out 1}

/* A transaction card in the reports is the same marker on a shape that
   fights back in two ways, both of which legacy hit first:

   - its left border is already spoken for. That 3px edge is the lease
     colour — who holds this record — and a marker that reused it would be
     claiming something about other people rather than about the reader. So
     the card is outlined instead, which is a channel nothing else uses.
   - the split table inside paints opaque cell backgrounds, which cover the
     card's own background completely. Tint the cells with it, not just the
     card. */
.card.wl-current{outline:2px solid var(--wl-outline);outline-offset:-1px}
.card.wl-current,
.card.wl-current .table>:not(caption)>*>*{background-color:var(--wl-current)}
@keyframes wl-flash-card{0%{outline-color:#ffc107;outline-width:4px}
  100%{outline-color:var(--wl-outline);outline-width:2px}}
.card.wl-flash{animation:wl-flash-card 1.5s ease-out 1}

/* No animation, but the lasting tint and outline are already applied by
   `wl-current`, so the marker is still there — it just arrives without the
   sweep. */
@media (prefers-reduced-motion:reduce){
  tr.wl-flash>td,tr.wl-flash>th,.card.wl-flash{animation:none}}

/* ===================================================================
   WALLETS (ADR-0023)
   A shortfall episode is drawn as a rail down the rows it spans, with a
   word at each end — the rail is the shape of the gap, the words are what
   a reader who cannot see the rail gets instead (invariant #11).
=================================================================== */
.wl-inv{
  border:1px solid #9ed8cd;background:#eefaf7;border-radius:9px;padding:10px 14px;
}
.wl-attn{
  border:1px solid #e3b57a;background:#fffaf3;border-radius:9px;padding:10px 12px;margin-bottom:8px;
}
.wl-num{font-family:var(--cf-mono);font-variant-numeric:tabular-nums}
.wl-num--neg{color:var(--cf-conflict);font-weight:600}

/* The section switcher: pills on the left, the one action that belongs to
   every section on the right. Wraps rather than scrolls on a phone. */
.wl-sec{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px}

/* A wallet row is the way into that wallet's log, so it is a button. Reset
   what the UA gives a button and keep the card it already looked like; the
   indent lives on a wrapper, so this can be full width at any depth. */
.wl-wal{
  border:1px solid #dde3ea;border-left:4px solid transparent;border-radius:9px;
  background:#fff;padding:9px 12px;margin-bottom:8px;
}
/* The card and its edit button are siblings: a button inside a button is not
   valid HTML, and the card as a whole is the way into that wallet's log. */
.wl-wal-row{display:flex;align-items:center;gap:6px}
.wl-wal-row > .wl-wal{flex:1 1 auto;min-width:0}
.wl-wal__edit{
  flex:none;align-self:stretch;margin-bottom:8px;
  border:1px solid #dde3ea;border-radius:9px;background:#fff;
  padding:0 .6rem;color:var(--cf-ink-soft);cursor:pointer;line-height:1;
}
.wl-wal__edit:hover{border-color:#b9c4d0;background:#f4f7fa;color:var(--cf-ink)}
button.wl-wal{display:block;width:100%;text-align:left;font:inherit;color:inherit;cursor:pointer}
button.wl-wal>span{width:100%}
button.wl-wal:hover{border-color:#b9c4d0;background:#f4f7fa}
.wl-wal--over{border-left-color:var(--cf-conflict);background:#fffaf3}
/* Listed after the hover rule and more specific, or hovering a wallet that is
   over would wash the one colour that says so straight off it. */
button.wl-wal--over:hover{background:#fff3e4}
button.wl-wal--unassigned:hover{background:#eef2f6}
.wl-wal--unassigned{background:#f7f9fb}
.wl-wal__name{font-weight:600}
.wl-wal__bal{min-width:170px}
.wl-wal__ccy{display:flex;justify-content:space-between;gap:12px;font-size:.88rem;padding:1px 0}
.wl-wal__code{color:var(--cf-ink-soft);font-size:.78rem}

.wl-reg{border:1px solid #e3e8ee;border-radius:8px;overflow:hidden}
.wl-reg__head,.wl-reg__row{
  display:grid;grid-template-columns:88px 1fr 130px 140px;gap:8px;
  padding:5px 10px 5px 7px;align-items:baseline;border-left:3px solid transparent;
}
.wl-reg--all .wl-reg__head,.wl-reg--all .wl-reg__row{grid-template-columns:88px 1fr 130px 130px 140px}
.wl-reg__head{background:#f7f9fb;border-bottom:1px solid #dde3ea;
  font-size:.7rem;letter-spacing:.06em;text-transform:uppercase;font-weight:700;color:var(--cf-ink-soft)}
.wl-reg__row{border-bottom:1px solid #eef2f6;font-size:.87rem}
.wl-reg__row:last-child{border-bottom:0}
.wl-reg__row--gap{border-left-color:var(--cf-conflict);background:#fffaf3}
.wl-reg__row--cover{background:#eef4ff}
.wl-reg__row--hl{outline:2px solid var(--cf-remote);outline-offset:-2px}
/* The word that says a running total is below zero — the rail and the red
   are the same fact twice over, and this is the one a reader who sees
   neither gets (invariant #11). */
.wl-reg__over{margin-left:.35rem}
/* The legs of one multi-split transaction, boxed.
   No `overflow:hidden`, deliberately: the state chip overhangs its row's top
   edge by half its height and clipping would cut the first one in half. The
   first and last rows take the box's corners instead, so a tinted row — a
   gap, a cover, the one you last opened — still stops at the rounded edge. */
.wl-reg__grp{
  margin:5px 4px;border:1px solid #c9d4e0;border-radius:7px;background:var(--cf-surface);
}
.wl-reg__grp > .wl-reg__row:first-child{border-top-left-radius:6px;border-top-right-radius:6px}
.wl-reg__grp > .wl-reg__row:last-child{border-bottom:0;border-bottom-left-radius:6px;border-bottom-right-radius:6px}

.wl-reg__cap{
  font-size:.72rem;font-weight:700;color:var(--cf-conflict);
  text-transform:uppercase;letter-spacing:.04em;
}

/* The marker on a register row — the same "you are here" as the chart of
   accounts and the reports (`markCurrentRow`), on a third shape.
   Background rather than the inset shadow a table row uses, because here that
   channel already belongs to the lease rail below: a row can be the one you
   last opened AND be open in somebody else's editor, and neither fact may
   erase the other. `aria-current` comes with the marker and is what says
   "this one" without colour (invariant #11). */
.wl-reg__row.wl-current{background:var(--wl-current)}
@keyframes wl-flash-reg{0%{background:var(--wl-flash)}100%{background:var(--wl-current)}}
.wl-reg__row.wl-flash{animation:wl-flash-reg 1.5s ease-out 1}
@media (prefers-reduced-motion:reduce){.wl-reg__row.wl-flash{animation:none}}

/* The register on a narrow screen. Five fixed columns cannot be squeezed into
   375px — they overflow sideways, and a ledger you have to scroll horizontally
   to read the amount of is not readable at all. So the same five cells lay
   themselves out in three lines, the header goes (it would be labelling
   columns that no longer exist), and the running total carries its own label
   instead — that is the number whose meaning is not self-evident. */
@media (max-width:640px){
  .wl-reg__head{display:none}
  /* With the head gone the first row is against the container's top edge, and
     the state chip sits half above its row — `.wl-reg` clips to its rounded
     corners, so without this the first row's chip would be cut in half. */
  .wl-reg{padding-top:9px}
  .wl-reg__row,.wl-reg--all .wl-reg__row{
    /* The description gets a line to itself rather than a column beside the
       amount. An amount cannot be wrapped or abbreviated — chip, currency and
       all of "-3,122,401.10" have to fit — so in two columns it takes what it
       needs and the description is left with whatever is over, which at phone
       width is nothing at all. */
    grid-template-columns:minmax(0,1fr) auto;
    grid-template-areas:"what what" "date amt" "wal run";
    row-gap:1px;padding:7px 10px 7px 7px;
  }
  .wl-reg__what{grid-area:what}
  .wl-reg__amt{grid-area:amt}
  .wl-reg__date{grid-area:date}
  .wl-reg__run{grid-area:run}
  .wl-reg__wal{grid-area:wal;color:var(--cf-ink-soft)}
  /* Above the number, not beside it. A currency chip cannot wrap — flag,
     code and every digit of "-3,122,401.10" are one unbreakable run — so an
     inline label is 56px permanently taken out of the date's column. */
  /* Beside the number it is another unbreakable 28px in the widest column. */
  .wl-reg__over{display:block;margin-left:0}
  .wl-reg__run::before{content:attr(data-lab);display:block;font-size:.66rem;font-weight:700;
    text-transform:uppercase;letter-spacing:.05em;color:var(--cf-ink-soft);line-height:1.2}
}

/* `background` stated rather than inherited from the UA's dialog styling: the
   quick-entry one is a plain <div> inside a transparent shell so that it can
   be dragged, and a <div> gets no such default — it rendered see-through. */
.wl-dlg{
  border:1px solid #dde3ea;border-radius:10px;padding:18px;max-width:440px;width:92vw;
  background:var(--cf-surface);color:var(--cf-ink);
}
.wl-dlg::backdrop{background:rgba(27,36,48,.35)}
/* A `<dialog>` that is only a positioning shell: the visible box is the
   `.wl-dlg` inside it, which is also the part that moves when dragged. The
   shell keeps the top layer (so the account picker inside can be seen at all)
   without taking a transform, which would otherwise become the containing
   block for that picker's `position:fixed`. */
.wl-dlg-shell{
  border:0;padding:0;background:transparent;overflow:visible;
  /* `width`/`height` deliberately NOT set. A modal <dialog> is centred by the
     UA with `inset:0; margin:auto` over a `fit-content` box; `width:auto` on
     a fixed element stretched between left:0 and right:0 fills the viewport
     instead, and the panel then sat against the left edge of an invisible
     full-width box. `fit-content` shrinks the shell to the panel, and
     `margin:auto` does the centring it was always going to do. */
  max-width:100vw;
}
.wl-dlg-shell::backdrop{background:rgba(27,36,48,.35)}
.wl-dlg-shell > .wl-dlg{margin:0}

/* Who is in a wallet-register row right now.
   The left BORDER already belongs to the shortfall rail, so this uses an
   inset shadow: a row can be both part of a gap and open in somebody's
   editor, and neither fact should erase the other. The chip beside the
   description carries the name and glyph — this is the peripheral cue,
   never the only one (invariant #11). */
.wl-reg__row{position:relative}
.wl-reg__row.is-open{box-shadow:inset 4px 0 0 var(--row-color,var(--cf-remote))}
.wl-reg__row.wl-tx-held{box-shadow:inset 4px 0 0 var(--cf-conflict)}
.wl-reg__row.wl-tx-pending{box-shadow:inset 4px 0 0 var(--cf-pending)}
/* Notched into the row's top border, exactly as on a register row and a
   report card (`.wl-tx-list`, `.wl-tx-card`): an opaque chip cutting the line
   it sits on. It was inline in the description before, which made it read as
   part of the text rather than as something about the record, and pushed the
   description sideways whenever somebody opened a row. Out of flow, it does
   neither. */
.wl-reg__row > * > [data-state-slot],
.wl-reg__row > [data-state-slot]{position:absolute;left:.45rem;top:0;transform:translateY(-50%);z-index:1}
.wl-reg__row [data-state-slot] .wl-state-chip{position:static;transform:none}

/* Quick entry: the short form on the wallets page. The preview shows BOTH
   legs on purpose — the category leg carries no wallet, and that is easier
   to believe when you can see it. */
.wl-dlg--wide{max-width:560px}
.wl-q-preview{border:1px solid #e3e8ee;border-radius:8px;padding:8px 10px;background:#f7f9fb}
.wl-q-leg{display:grid;grid-template-columns:1fr 130px 150px;gap:8px;align-items:baseline;
  padding:2px 0;border-bottom:1px dotted #e4e9ee;font-size:.88rem}
.wl-q-leg:last-of-type{border-bottom:0}
