@arcforgelabs/flow 2026.10.37 → 2026.10.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +2 -1
  2. package/npm-shrinkwrap.json +2 -2
  3. package/package.json +4 -2
  4. package/server/api/custom-fields.js +27 -9
  5. package/server/api/invoicing.js +198 -51
  6. package/server/app.js +5 -2
  7. package/server/core/alerts.js +1 -1
  8. package/server/core/automations.js +1 -1
  9. package/server/core/custom-fields.js +89 -3
  10. package/server/core/job-board.js +2 -1
  11. package/server/core/job-match.js +241 -0
  12. package/server/core/ledger.js +109 -8
  13. package/server/core/price-list.js +27 -2
  14. package/server/core/read-before-act.js +3 -2
  15. package/server/core/sending.js +36 -18
  16. package/server/db/doctor.js +18 -19
  17. package/server/db/index.js +74 -3
  18. package/server/db/migrate-all.js +3 -4
  19. package/server/db/migrations/0020_custom_field_colours.sql +21 -0
  20. package/server/db/migrations/meta/0020_snapshot.json +4377 -0
  21. package/server/db/migrations/meta/_journal.json +7 -0
  22. package/server/db/schema.js +14 -0
  23. package/server/integrations/call-guard.js +148 -0
  24. package/server/integrations/port.js +3 -2
  25. package/server/integrations/schema.js +9 -5
  26. package/server/integrations/seazona/feed.js +4 -1
  27. package/server/integrations/seazona/settings.js +8 -4
  28. package/server/integrations/xero/drafts.js +4 -4
  29. package/server/integrations/xero/fit.js +108 -0
  30. package/server/integrations/xero/import.js +6 -1
  31. package/server/integrations/xero/index.js +6 -2
  32. package/server/integrations/xero/ledger.js +3 -0
  33. package/server/integrations/xero/links.js +293 -0
  34. package/server/integrations/xero/migrations/0008_xero_invoice_links.sql +26 -0
  35. package/server/integrations/xero/migrations/0009_xero_sent_to_contact.sql +3 -0
  36. package/server/integrations/xero/migrations/meta/_journal.json +14 -0
  37. package/server/integrations/xero/mirror.js +4 -3
  38. package/server/integrations/xero/reconcile.js +42 -12
  39. package/server/integrations/xero/routes.js +66 -7
  40. package/server/integrations/xero/settings.js +6 -5
  41. package/server/ui/app-page.js +1 -1
  42. package/server/ui/app.js +89 -3
  43. package/server/ui/bulk-bar.js +4 -3
  44. package/server/ui/custom-fields.js +252 -94
  45. package/server/ui/flow.css +326 -29
  46. package/server/ui/invoice-notes.js +2 -2
  47. package/server/ui/invoicing.js +92 -137
  48. package/server/ui/job-board.js +9 -7
  49. package/server/ui/job-change.js +23 -12
  50. package/server/ui/job-view.js +189 -31
  51. package/server/ui/jobs.js +738 -70
  52. package/server/ui/price-list.js +43 -86
  53. package/server/ui/price-match.js +93 -0
  54. package/server/ui/price-reconcile.js +357 -0
  55. package/server/ui/reconcile.js +275 -0
  56. package/server/ui/routes.js +1 -1
  57. package/server/ui/xero-import.js +329 -0
@@ -202,6 +202,10 @@ body:is([data-state="off"], [data-state="error"], [data-state="no-access"]) .fx-
202
202
  .fx-change__head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px 16px; }
203
203
  .fx-change__acts { display: flex; flex-wrap: wrap; gap: 8px; }
204
204
  .fx-change__note { font-size: 13px; color: var(--t-dim); }
205
+ .fx-change__recheck { width: 30px; }
206
+ .fx-change__recheck .fx-lucide { width: 14px; height: 14px; }
207
+ .fx-change__recheck.is-busy .fx-lucide { animation: fx-spin 1s linear infinite; }
208
+ @media (prefers-reduced-motion: reduce) { .fx-change__recheck.is-busy .fx-lucide { animation: none; opacity: .5; } }
205
209
  .fx-change__scroll { overflow-x: auto; }
206
210
  .fx-change__table { width: 100%; border-collapse: collapse; background: var(--t-paper); border: 1px solid var(--t-brd); font-size: 13.5px; }
207
211
  .fx-change__table th, .fx-change__table td { padding: 8px 12px; border-bottom: 1px solid var(--t-brd); text-align: left; vertical-align: top; }
@@ -235,10 +239,12 @@ a.fx-link:hover, a.fx-link:focus-visible { border-color: var(--t-strong); }
235
239
  .fx-link__dot[data-tone="success"] { background: var(--t-success); }
236
240
  .fx-link__dot[data-tone="warn"] { background: var(--t-warn); }
237
241
  .fx-link__dot[data-tone="danger"] { background: var(--t-danger); }
238
- .fx-linked { display: grid; }
239
- .fx-linked__row { display: flex; align-items: center; gap: 8px; white-space: nowrap; min-height: 44px; padding: 6px 0; border-top: 1px solid var(--fx-line-soft); }
242
+ .fx-linked { display: grid; grid-template-columns: minmax(0, 1fr); }
243
+ .fx-linked__row { display: flex; align-items: center; gap: 8px; min-width: 0; white-space: nowrap; min-height: 44px; padding: 6px 0; border-top: 1px solid var(--fx-line-soft); }
244
+ .fx-linked__row > * { flex: none; }
245
+ .fx-linked__row .fx-dim { flex: 1 1 auto; min-width: 0; white-space: normal; }
240
246
  .fx-linked__row:first-child { border-top: 0; }
241
- .fx-linked__ref { font-family: var(--t-data); font-variant-numeric: tabular-nums; color: var(--t-strong); }
247
+ .fx-linked__ref { flex: 0 1 auto !important; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-family: var(--t-data); font-variant-numeric: tabular-nums; color: var(--t-strong); }
242
248
  .fx-linked__open { margin-left: auto; font-size: 12.5px; color: var(--t-dim); white-space: nowrap; }
243
249
  .fx-linked__open:hover { color: var(--t-strong); }
244
250
  .fx-tag {
@@ -417,11 +423,11 @@ a.fx-strong:hover { text-decoration: underline; }
417
423
  .fx-crumbs a:hover { color: var(--t-strong); text-decoration: underline; }
418
424
  .fx-invoice { display: grid; gap: 20px; }
419
425
  .fx-invoice__grid { display: grid; grid-template-columns: minmax(0, 1fr) 260px; gap: 20px; align-items: start; }
420
- .fx-invoice__main, .fx-invoice__aside { display: grid; gap: 16px; }
426
+ .fx-invoice__main, .fx-invoice__aside { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; min-width: 0; }
421
427
  .fx-invoice__aside:empty { display: none; }
422
428
  .fx-invoice__grid:has(.fx-invoice__aside:empty) { grid-template-columns: minmax(0, 1fr); }
423
429
  .fx-invoice__head { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 16px; padding: 18px 20px; }
424
- .fx-side-card { display: grid; gap: 10px; padding: 16px; }
430
+ .fx-side-card { display: grid; grid-template-columns: minmax(0, 1fr); gap: 10px; min-width: 0; padding: 16px; }
425
431
  .fx-facts { display: grid; grid-template-columns: max-content 1fr; gap: 6px 12px; margin: 0; font-size: 13.5px; }
426
432
  .fx-facts dt { color: var(--t-dim); }
427
433
  .fx-facts dd { margin: 0; color: var(--t-strong); overflow-wrap: anywhere; }
@@ -508,10 +514,89 @@ a.fx-strong:hover { text-decoration: underline; }
508
514
  .fx-pop__tick { display: inline-grid; place-items: center; flex: none; width: 16px; height: 16px; color: var(--t-accent); }
509
515
  .fx-pop__sep { height: 1px; margin: 6px 4px; background: var(--fx-line-soft); }
510
516
  .fx-pop__foot { display: flex; justify-content: flex-end; padding: 6px 4px 2px; border-top: 1px solid var(--fx-line-soft); margin-top: 6px; }
517
+ /* A chip with an × that clears it (as ClickUp's "Clear all filters"). */
518
+ .fx-chipset { display: inline-flex; align-items: stretch; }
519
+ .fx-chipset[hidden] { display: none; }
520
+ .fx-chipset > .fx-chip:not(:last-child):has(+ .fx-chip__clear:not([hidden])) { border-top-right-radius: 0; border-bottom-right-radius: 0; border-right-width: 0; padding-right: 8px; }
521
+ .fx-chip__clear {
522
+ display: inline-grid; place-items: center; width: 28px; padding: 0 6px 0 2px; border: 1px solid var(--fx-line); border-left: 0;
523
+ border-radius: 0 999px 999px 0; background: transparent; color: var(--t-dim); cursor: pointer;
524
+ }
525
+ .fx-chip__clear[hidden] { display: none; }
526
+ .fx-chip__clear .fx-lucide { width: 13px; height: 13px; }
527
+ .fx-chip__clear:hover { background: var(--fx-hover); color: var(--t-strong); }
528
+ .fx-chip__clear:focus-visible { outline: 2px solid var(--t-ring, var(--t-accent)); outline-offset: 2px; }
529
+ .fx-chipset--on .fx-chip__clear { border-color: var(--t-accent-brd, var(--t-accent)); background: var(--t-accent-bg); color: var(--t-accent); }
530
+ /* Group by, Sort and Filters: rows of selects with a bin at the end, as
531
+ ClickUp lays them out, in Flow's own look. */
532
+ .fx-pop.fx-pop--rows { width: max-content; min-width: 360px; max-width: min(680px, calc(100vw - 16px)); max-height: min(520px, calc(100vh - 32px)); padding: 12px; }
533
+ .fx-rows__title { margin: 0 0 10px; color: var(--t-strong); font: 600 14px/1.3 var(--t-body); }
534
+ .fx-rows { display: grid; gap: 8px; }
535
+ .fx-rows__row { display: grid; grid-template-columns: 78px minmax(130px, 1fr) minmax(120px, auto) minmax(150px, 1.2fr) 32px; gap: 8px; align-items: center; }
536
+ .fx-rows__row--sort { grid-template-columns: 78px minmax(150px, 1fr) minmax(140px, auto) 32px; }
537
+ .fx-rows__row--group { grid-template-columns: minmax(170px, 1fr) minmax(130px, auto) 32px; }
538
+ .fx-rows .fx-select, .fx-rows .fx-input { width: 100%; min-width: 0; min-height: 32px; font-size: 13px; }
539
+ .fx-rows__lead { color: var(--t-dim); font: 500 13px/1.2 var(--t-body); }
540
+ .fx-rows__value { overflow: hidden; text-align: left; text-overflow: ellipsis; white-space: nowrap; }
541
+ .fx-rows__gone { grid-column: span 3; color: var(--t-dim); font-size: 13px; font-style: italic; }
542
+ .fx-rows__bin {
543
+ display: inline-grid; place-items: center; width: 32px; height: 32px; border: 0; border-radius: var(--t-radius);
544
+ background: none; color: var(--t-dim); cursor: pointer;
545
+ }
546
+ .fx-rows__bin .fx-lucide { width: 15px; height: 15px; }
547
+ .fx-rows__bin:hover { background: var(--fx-hover); color: var(--t-danger); }
548
+ .fx-rows__bin:focus-visible { outline: 2px solid var(--t-ring, var(--t-accent)); outline-offset: 1px; }
549
+ .fx-rows__empty { margin: 2px 0 4px; color: var(--t-dim); font-size: 13px; }
550
+ .fx-rows__foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 12px; }
551
+ .fx-rows__add { display: inline-flex; align-items: center; gap: 6px; }
552
+ .fx-rows__add .fx-lucide { width: 14px; height: 14px; }
553
+ .fx-pop.fx-pop--choices { min-width: 200px; max-width: 300px; padding: 6px; }
554
+ .fx-pop--choices .fx-input { min-height: 32px; margin-bottom: 4px; font-size: 13px; }
555
+ .fx-pop__end { display: inline-grid; margin-left: auto; color: var(--t-accent); }
556
+ @media (max-width: 640px) {
557
+ .fx-pop.fx-pop--rows { min-width: 0; width: calc(100vw - 16px); }
558
+ .fx-rows__row, .fx-rows__row--sort { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 32px; }
559
+ .fx-rows__row > .fx-rows__lead, .fx-rows__row > .fx-select:first-child { grid-column: 1 / -1; }
560
+ .fx-rows__row > .fx-rows__value { grid-column: 1 / 3; }
561
+ .fx-rows__row > .fx-rows__value--none { display: none; }
562
+ }
511
563
  .fx-table__group td { padding: 14px 16px 8px; background: var(--t-s1); }
512
564
  .fx-table tbody tr.fx-table__group:hover td { background: var(--t-s1); }
513
565
  .fx-table__group-row { display: flex; align-items: center; gap: 12px; }
514
566
  .fx-table__group-row .fx-board__sum { margin-left: 0; }
567
+ /* The Jobs list's group head folds its rows (jobs.js, list): a click on the
568
+ head or its chevron. */
569
+ .fx-table--jobs .fx-table__group { cursor: pointer; }
570
+ .fx-table--jobs .fx-table__group td.fx-tick-cell { padding-right: 0; }
571
+ .fx-table__fold { display: inline-flex; align-items: center; gap: 8px; min-width: 0; padding: 2px 4px 2px 0; border: 0; background: none; color: var(--t-dim); font: inherit; cursor: pointer; }
572
+ .fx-table__fold > svg { flex: none; transition: transform .15s; }
573
+ .fx-table__fold[aria-expanded="true"] > svg { transform: rotate(90deg); }
574
+ .fx-table__fold:hover, .fx-table__fold:focus-visible { color: var(--t-strong); outline: none; }
575
+ .fx-table__fold:focus-visible .fx-stage, .fx-table__fold:focus-visible .fx-cf__opt { box-shadow: 0 0 0 2px var(--t-accent); }
576
+ .fx-table__group--folded td { padding-bottom: 14px; }
577
+ /* A Jobs list column head opens the column's menu (a click or a
578
+ right-click); + at the end shows a hidden column. */
579
+ .fx-th { margin: -4px -6px; padding: 4px 6px; border: 0; border-radius: var(--t-radius); background: none; color: inherit; font: inherit; letter-spacing: inherit; text-transform: inherit; cursor: pointer; }
580
+ .fx-th:hover, .fx-th:focus-visible { background: var(--fx-hover); color: var(--t-strong); outline: none; }
581
+ .fx-th-add { width: 36px; text-align: right; }
582
+ .fx-th--add { font-size: 16px; line-height: 1; }
583
+ .fx-th--fixed, .fx-th--fixed:hover { cursor: default; background: none; color: inherit; }
584
+ /* A column head's sort button (as ClickUp's): shown on hover, and always
585
+ while the column sorts, as a small filled circle with the arrow. */
586
+ .fx-th-cell { display: inline-flex; align-items: center; gap: 6px; }
587
+ .fx-th-sort {
588
+ display: inline-grid; grid-auto-flow: column; place-items: center; gap: 1px; min-width: 22px; height: 22px; padding: 0 4px;
589
+ border: 0; border-radius: 999px; background: none; color: var(--t-dim); cursor: pointer; opacity: 0;
590
+ }
591
+ th:hover .fx-th-sort, .fx-th-sort:focus-visible, .fx-th-sort--on { opacity: 1; }
592
+ .fx-th-sort:hover { background: var(--fx-hover); color: var(--t-strong); }
593
+ .fx-th-sort:focus-visible { outline: 2px solid var(--t-ring, var(--t-accent)); outline-offset: 1px; }
594
+ .fx-th-sort--on, .fx-th-sort--on:hover { background: var(--t-accent-bg); color: var(--t-accent); }
595
+ .fx-th-sort .fx-lucide { width: 13px; height: 13px; }
596
+ .fx-th-sort__n { font: 700 10px/1 var(--t-body); }
597
+ @media (hover: none) { .fx-th-sort { opacity: 1; } }
598
+ .fx-avatar--sm { width: 24px; height: 24px; font-size: 10px; }
599
+ .fx-c-date time { font-variant-numeric: tabular-nums; }
515
600
  .fx-stage[data-rank="plain"] { --stage: var(--t-dim); }
516
601
  /* The board is one row of lanes (one per group), each about a phone wide.
517
602
  Lanes share the width when they fit; more lanes scroll sideways inside
@@ -613,6 +698,70 @@ a.fx-strong:hover { text-decoration: underline; }
613
698
  .fx-task__fix h3 { padding: 12px 14px 4px; color: var(--t-warn); }
614
699
  .fx-task__fixrow { padding: 9px 14px; border-top: 1px solid var(--fx-line-soft); color: var(--t-strong); font-size: 13.5px; }
615
700
  .fx-task__fixrow:first-of-type { border-top: 0; }
701
+ .fx-task__choose { scroll-margin-top: 16px; margin-top: 22px; display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 12px 14px; border: 1px solid var(--t-warn-brd, var(--t-warn)); border-radius: var(--t-radius); background: var(--t-warn-bg); color: var(--t-strong); font-size: 13.5px; }
702
+ .fx-task__choose > svg { flex: none; width: 20px; height: 20px; color: var(--t-warn); }
703
+ .fx-task__choose > span { flex: 1 1 200px; }
704
+ .fx-task__choices { display: flex; flex-wrap: wrap; gap: 10px; }
705
+ .fx-task__none { display: inline-flex; align-items: center; gap: 6px; color: var(--t-warn); }
706
+ .fx-task__none svg { width: 16px; height: 16px; }
707
+
708
+ /* Reconcile: the queue left, one job and its possible invoices right. */
709
+ .fx-rec { display: grid; grid-template-columns: 280px minmax(0, 1fr); border: 1px solid var(--fx-line); border-radius: var(--t-radius-lg); background: var(--t-paper); min-height: 420px; }
710
+ .fx-rec > .fx-empty, .fx-rec > .fx-dim { grid-column: 1 / -1; }
711
+ .fx-rec__queue { display: flex; flex-direction: column; border-right: 1px solid var(--fx-line); max-height: 75vh; overflow-y: auto; }
712
+ .fx-rec__tabs { display: flex; flex-wrap: wrap; gap: 6px; padding: 12px 16px; border-bottom: 1px solid var(--fx-line); }
713
+ .fx-rec__tab { display: inline-flex; align-items: center; min-height: 32px; padding: 0 10px; border: 1px solid var(--fx-line); border-radius: 999px; color: var(--t-text); font-size: 12.5px; font-weight: 600; text-decoration: none; }
714
+ .fx-rec__tab[aria-selected="true"] { border-color: var(--t-primary); background: var(--t-primary); color: var(--t-on-primary); }
715
+ .fx-rec__search { width: 100%; min-width: 0; }
716
+ .fx-rec__results { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
717
+ .fx-rec__item { display: grid; gap: 3px; min-height: 44px; padding: 12px 16px; border-bottom: 1px solid var(--fx-line-soft); border-left: 3px solid transparent; color: var(--t-text); text-decoration: none; }
718
+ .fx-rec__item:hover { background: var(--fx-hover); }
719
+ .fx-rec__item[aria-current="true"] { border-left-color: var(--t-accent); background: var(--t-s1); }
720
+ .fx-rec__item-top { display: flex; justify-content: space-between; gap: 8px; }
721
+ .fx-rec__item .fx-num { font-family: var(--t-data); font-variant-numeric: tabular-nums; }
722
+ .fx-rec__detail { display: flex; flex-direction: column; gap: 16px; min-width: 0; padding: 20px 24px; }
723
+ .fx-rec__head { display: flex; justify-content: space-between; align-items: flex-end; gap: 16px; flex-wrap: wrap; }
724
+ .fx-rec__head > div { display: grid; gap: 2px; }
725
+ .fx-rec__back { display: none; color: var(--t-accent); text-decoration: none; font-size: 13px; }
726
+ .fx-rec__title { margin: 0; font-family: var(--t-display); font-weight: var(--t-display-weight); color: var(--t-strong); font-size: 24px; }
727
+ .fx-rec__table { display: grid; grid-template-columns: 110px minmax(150px, 1fr) repeat(var(--cols), minmax(170px, 1fr)); border: 1px solid var(--fx-line); border-radius: var(--t-radius); overflow-x: auto; }
728
+ .fx-rec__row { display: contents; }
729
+ .fx-rec__row > span { display: flex; flex-direction: column; gap: 2px; padding: 10px 12px; border-top: 1px solid var(--fx-line-soft); font-size: 13.5px; color: var(--t-strong); }
730
+ .fx-rec__row:first-child > span { border-top: 0; background: var(--t-s1); }
731
+ .fx-rec__row > span[role="cell"]:not(.fx-rec__job) { flex-direction: row; align-items: flex-start; gap: 6px; }
732
+ .fx-rec__row > span[role="cell"].fx-rec__acts { flex-direction: column; align-items: stretch; gap: 8px; }
733
+ .fx-rec__acts > .fx-task__link, .fx-rec__card > .fx-task__link { align-self: flex-start; }
734
+ .fx-rec__job { background: color-mix(in srgb, var(--t-accent) 5%, transparent); }
735
+ .fx-rec__row:first-child > .fx-rec__job { background: color-mix(in srgb, var(--t-accent) 10%, var(--t-s1)); }
736
+ .fx-rec__lines { display: flex; flex-direction: column; font-family: var(--t-data); font-variant-numeric: tabular-nums; font-size: 13px; line-height: 1.6; }
737
+ .fx-rec__mark { flex: none; font-weight: 800; }
738
+ [data-mark="same"] > .fx-rec__mark { color: var(--t-success); }
739
+ [data-mark="differs"], [data-mark="differs"] > .fx-rec__mark { color: var(--t-warn); }
740
+ .fx-rec__best { color: var(--t-success); font-size: 12px; font-weight: 700; }
741
+ .fx-rec__foot { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }
742
+ .fx-rec__foot-acts { display: flex; gap: 10px; flex-wrap: wrap; }
743
+ .fx-rec__cards { display: none; }
744
+ .fx-rec__card { display: flex; flex-direction: column; gap: 6px; padding: 12px; border: 1px solid var(--fx-line); border-radius: var(--t-radius); font-size: 13.5px; color: var(--t-strong); }
745
+ .fx-rec__card.fx-rec__job { border-color: transparent; }
746
+ .fx-rec__card--best { border-color: var(--t-success); }
747
+ .fx-rec__card-top { display: flex; justify-content: space-between; gap: 8px; }
748
+ .fx-rec__label { color: var(--t-dim); font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
749
+ .fx-rec__checks { display: flex; flex-wrap: wrap; gap: 4px 12px; }
750
+ @media (max-width: 1100px) {
751
+ .fx-rec { grid-template-columns: minmax(0, 1fr); min-height: 0; }
752
+ .fx-rec__queue { border-right: 0; max-height: none; }
753
+ .fx-rec--open .fx-rec__queue { display: none; }
754
+ .fx-rec:not(.fx-rec--open) .fx-rec__detail { display: none; }
755
+ .fx-rec__back { display: inline; }
756
+ }
757
+ @media (max-width: 820px) {
758
+ .fx-rec__detail { padding: 16px; }
759
+ .fx-rec__table { display: none; }
760
+ .fx-rec__cards { display: flex; flex-direction: column; gap: 12px; }
761
+ .fx-rec__cards .fx-btn, .fx-rec__foot .fx-btn { min-height: 44px; }
762
+ .fx-rec__foot-acts { width: 100%; }
763
+ .fx-rec__foot-acts .fx-btn { flex: 1 1 auto; }
764
+ }
616
765
  .fx-task__custom { margin-top: 24px; }
617
766
  .fx-task__custom h3 { margin: 0 0 4px; color: var(--t-dim); font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
618
767
  .fx-task__input { width: 100%; min-width: 0; }
@@ -838,6 +987,20 @@ body[data-state="loading"] .fx-loading { display: grid; justify-items: center; }
838
987
  .fx-people td:nth-child(2) .fx-select { width: 100%; }
839
988
  .fx-people td:nth-child(3) { grid-area: 3 / 1 / 4 / -1; }
840
989
  .fx-people td:nth-child(3):empty { display: none; }
990
+ /* Jobs list: the tick at the left of each row and group head; the client
991
+ and total on the first line, then each other column on its own line,
992
+ with its name. */
993
+ .fx-table--jobs tr { display: grid; grid-template-columns: 28px minmax(0, 1fr) auto; align-items: center; gap: 4px 8px; }
994
+ .fx-table--jobs td, .fx-table--jobs td:nth-child(2), .fx-table--jobs td:nth-child(3) { display: block; width: auto; margin: 0; padding: 0; }
995
+ .fx-table--jobs tr > td.fx-tick-cell { grid-column: 1; grid-row: 1; align-self: start; padding-top: 2px; }
996
+ .fx-table--jobs tr.fx-row-link > td:not(.fx-tick-cell) { grid-column: 2 / -1; }
997
+ .fx-table--jobs tr.fx-row-link > td.fx-c-client { grid-area: 1 / 2; }
998
+ .fx-table--jobs tr.fx-row-link > td.fx-c-total { grid-area: 1 / 3; }
999
+ .fx-table--jobs td.fx-c-end { display: none; }
1000
+ .fx-table--jobs tr.fx-row-link > td[data-label]:not(.fx-c-total, .fx-c-stage)::before { content: attr(data-label) " "; color: var(--t-dim); font-size: 12px; }
1001
+ .fx-table--jobs tr.fx-table__group { grid-template-columns: 28px minmax(0, 1fr); background: var(--t-s1); }
1002
+ .fx-table--jobs tr.fx-table__group > td.fx-tick-cell { grid-row: 1; align-self: center; padding: 0; }
1003
+ .fx-table--jobs tr.fx-table__group > td { background: none; }
841
1004
  }
842
1005
 
843
1006
  @keyframes fx-rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@@ -862,43 +1025,75 @@ body[data-state="loading"] .fx-loading { display: grid; justify-items: center; }
862
1025
  .fx-set__control .fx-input[type="number"] { width: 96px; }
863
1026
  .fx-set__control .fx-select { min-width: 220px; }
864
1027
  .fx-set + .fx-set { margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--fx-line); }
865
- /* Price list (price-list.js): a code's price and its systems' chips. */
866
- .fx-price__hint { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; }
867
- .fx-price__head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 16px; }
868
- .fx-price__head .fx-search { margin: 0; }
1028
+ /* Price list (price-list.js): one plain table of Flow's codes. */
1029
+ .fx-price { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
1030
+ .fx-price__head { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: 8px 16px; }
1031
+ .fx-price__head .fx-search { margin: 0 0 6px; width: 260px; }
869
1032
  @media (max-width: 560px) { .fx-price__head .fx-search { width: 100%; } }
870
- .fx-price__systems { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 10px; }
871
- .fx-set__control .fx-price__link { width: 260px; min-width: 0; }
872
- @media (max-width: 560px) { .fx-set__control .fx-price__link { width: 150px; } }
1033
+ .fx-price__scroll { overflow-x: auto; border: 1px solid var(--fx-line); background: var(--t-paper); }
1034
+ .fx-price__table th { position: sticky; top: 0; z-index: 1; }
1035
+ .fx-price__table td { padding-top: 9px; padding-bottom: 9px; }
1036
+ .fx-price__table td.fx-num, .fx-price__table th.fx-num { text-align: right; white-space: nowrap; }
1037
+ .fx-price__code { width: 1%; white-space: nowrap; font-family: var(--t-data); font-size: 13px; font-weight: 600; color: var(--t-strong); }
1038
+ .fx-price__settings { margin-top: 12px; }
1039
+ .fx-ph__actions .fx-tabs__count[data-tone="warn"], .fx-btn .fx-tabs__count[data-tone="warn"] { margin-left: 8px; background: var(--t-warn-bg); color: var(--t-warn); }
1040
+ /* Price list › Reconcile (price-reconcile.js): Xero › Import's two views. */
1041
+ .fx-prc .fx-pager { margin-bottom: 4px; }
1042
+ .fx-prc__finder { display: flex; flex-direction: column; gap: 4px; }
1043
+ .fx-prc__found { display: flex; flex-direction: column; }
1044
+ .fx-prc__found:empty { display: none; }
1045
+ .fx-prc__found .fx-pop__item { display: grid; grid-template-columns: 90px 1fr auto; gap: 12px; text-align: left; }
1046
+ .fx-prc { padding-bottom: 72px; }
1047
+ .fx-prc-list td { white-space: nowrap; }
1048
+ .fx-prc-list td.fx-prc-list__name { white-space: normal; min-width: 160px; }
1049
+ .fx-prc-list__flow { display: inline-flex; align-items: center; gap: 8px; }
1050
+ .fx-prc-list__sys { width: 1%; white-space: nowrap; }
1051
+ .fx-prc-list__bad { color: var(--t-warn); font-weight: 600; }
1052
+ .fx-prc-list tr[data-state="sure"] .fx-xi-list__mid { color: var(--t-success); }
1053
+ .fx-prc-list tr[data-state="differs"] .fx-xi-list__mid { color: var(--t-warn); }
873
1054
  /* Custom fields (custom-fields.js): option chips with a colour dot, and the
874
1055
  dialog's option lines. The option colours are fixed, like ClickUp's. */
875
- .fx-cf__opt { display: inline-flex; align-items: center; gap: 6px; padding: 2px 9px; border: 1px solid var(--fx-line); border-radius: 999px; font-size: 12.5px; color: var(--t-text); white-space: nowrap; }
1056
+ .fx-cf__opt { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; padding: 2px 9px; border: 1px solid color-mix(in srgb, var(--cf) 45%, transparent); border-radius: 999px; background: color-mix(in srgb, var(--cf) 16%, transparent); font-size: 12.5px; font-weight: 600; color: var(--t-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
876
1057
  .fx-cf__opt::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--cf); flex: none; }
877
1058
  [data-color="gray"] { --cf: #8a8f98; } [data-color="blue"] { --cf: #3b82f6; } [data-color="teal"] { --cf: #14b8a6; }
878
1059
  [data-color="green"] { --cf: #22a35a; } [data-color="amber"] { --cf: #e0a019; } [data-color="red"] { --cf: #e5484d; }
879
1060
  [data-color="purple"] { --cf: #8b5cf6; } [data-color="pink"] { --cf: #e5469a; }
880
- .fx-cf__options { display: grid; gap: 8px; }
881
- .fx-cf__line { display: grid; grid-template-columns: minmax(0, 1fr) 120px auto auto; gap: 8px; align-items: center; }
882
- .fx-cf__line .fx-input, .fx-cf__line .fx-select { width: 100%; min-width: 0; }
883
- .fx-cf__color { border-left: 4px solid var(--cf); }
884
- @media (max-width: 560px) { .fx-cf__line { grid-template-columns: minmax(0, 1fr) 96px; } }
1061
+ /* A colour picker (colorPicker): the button shows the real pill in the
1062
+ chosen colour; the palette shows the same pill in every colour. */
1063
+ .fx-cf__swatch { display: inline-flex; align-items: center; justify-content: space-between; gap: 6px; min-width: 0; max-width: 100%; min-height: 36px; padding: 4px 8px; border: 1px solid var(--fx-line); border-radius: var(--t-radius); background: var(--t-paper); color: var(--t-dim); font: inherit; cursor: pointer; }
1064
+ .fx-cf__swatch:hover:not(:disabled) { border-color: var(--t-dim); }
1065
+ .fx-cf__swatch:focus-visible { outline: none; box-shadow: var(--fx-focus); }
1066
+ .fx-cf__swatch:disabled { cursor: default; }
1067
+ .fx-cf__swatch > .fx-lucide { width: 14px; height: 14px; }
1068
+ .fx-cf__swatch > :first-child { min-width: 0; }
1069
+ .fx-cfm__group .fx-cf__swatch, .fx-cfm__type .fx-cf__swatch { min-height: 0; padding: 2px; border-color: transparent; background: none; }
1070
+ .fx-cfm__group .fx-cf__swatch > .fx-lucide, .fx-cfm__type .fx-cf__swatch > .fx-lucide { display: none; }
1071
+ .fx-cfm__group .fx-cf__swatch:hover, .fx-cfm__type .fx-cf__swatch:hover { border-color: var(--fx-line); background: var(--fx-hover); }
1072
+ .fx-pop.fx-cf__palette { min-width: 0; width: 248px; padding: 4px; }
1073
+ .fx-cf__grid { display: grid; gap: 2px; }
1074
+ .fx-cf__choice { display: grid; grid-template-columns: minmax(0, 1fr) auto 16px; align-items: center; gap: 8px; width: 100%; padding: 5px 8px; border: 0; border-radius: var(--t-radius); background: none; color: var(--t-dim); font: 500 12.5px/1.3 var(--t-body); text-align: left; cursor: pointer; }
1075
+ .fx-cf__choice:hover, .fx-cf__choice:focus-visible { background: var(--fx-hover); outline: none; }
1076
+ .fx-cf__choice[aria-checked="true"] { background: var(--fx-hover); color: var(--t-strong); }
1077
+ .fx-cf__choice > :first-child { justify-self: start; }
1078
+ .fx-cf__choice .fx-lucide { width: 15px; height: 15px; color: var(--t-accent); }
885
1079
  /* Custom fields manager (custom-fields.js), like ClickUp's Custom Field
886
1080
  Manager: a toolbar, then one table of type groups that fold. */
887
1081
  .fx-lucide { display: inline-block; flex: none; width: 16px; height: 16px; background: currentColor; -webkit-mask: var(--icon) center / contain no-repeat; mask: var(--icon) center / contain no-repeat; }
888
- .fx-cfm__card { padding: 0; overflow: hidden; }
1082
+ .fx-cfm__card { padding: 0; overflow: clip; }
889
1083
  .fx-cfm__bar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 12px 16px; border-bottom: 1px solid var(--fx-line-soft); }
890
1084
  .fx-cfm__search { width: 220px; min-width: 0; height: 32px; }
891
1085
  .fx-cfm__tool { min-height: 32px; }
892
1086
  .fx-cfm__tool--dashed { border-style: dashed; }
893
1087
  .fx-cfm__gap { flex: 1; }
894
- .fx-cfm { display: grid; grid-template-columns: minmax(160px, 1fr) 64px 96px 120px minmax(90px, .6fr) 44px; }
1088
+ .fx-cfm { display: grid; grid-template-columns: minmax(200px, 1fr) 56px 88px 104px minmax(96px, 160px) 36px; column-gap: 16px; }
895
1089
  .fx-cfm > [role="row"], .fx-cfm > .fx-notice, .fx-cfm > .fx-cfm__create, .fx-cfm > .fx-cfm__empty { grid-column: 1 / -1; }
896
1090
  .fx-cfm__head, .fx-cfm__row { display: grid; grid-template-columns: subgrid; align-items: center; }
897
- .fx-cfm__head { height: 36px; padding: 0 8px 0 36px; color: var(--t-dim); font-size: 12.5px; border-bottom: 1px solid var(--fx-line-soft); }
1091
+ .fx-cfm__head { height: 36px; padding: 0 8px 0 40px; color: var(--t-dim); font-size: 12.5px; border-bottom: 1px solid var(--fx-line-soft); white-space: nowrap; }
898
1092
  .fx-cfm__sort { all: unset; cursor: pointer; display: inline-flex; gap: 4px; align-items: center; }
899
1093
  .fx-cfm__sort:focus-visible { box-shadow: var(--fx-focus); border-radius: 4px; }
900
1094
  .fx-cfm__sort .fx-lucide { width: 13px; height: 13px; }
901
1095
  .fx-cfm__group { display: flex; align-items: center; gap: 8px; height: 44px; margin-top: 12px; padding: 0 16px 0 8px; border-bottom: 1px solid var(--fx-line-soft); }
1096
+ .fx-cfm__group .fx-cfm__fold { margin-right: 0; }
902
1097
  .fx-cfm__group:first-of-type { margin-top: 4px; }
903
1098
  .fx-cfm__fold { all: unset; cursor: pointer; display: inline-grid; place-items: center; width: 24px; height: 24px; border-radius: 6px; color: var(--t-dim); }
904
1099
  .fx-cfm__fold:hover { background: var(--fx-hover); color: var(--t-strong); }
@@ -908,19 +1103,21 @@ body[data-state="loading"] .fx-loading { display: grid; justify-items: center; }
908
1103
  .fx-cfm__pill { display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px; border-radius: 6px; font-size: 12.5px; font-weight: 600; color: var(--cf); background: color-mix(in srgb, var(--cf) 14%, transparent); }
909
1104
  .fx-cfm__pill .fx-lucide { width: 14px; height: 14px; }
910
1105
  .fx-cfm__count { color: var(--t-dim); font-size: 12.5px; font-weight: 600; }
911
- .fx-cfm__row { min-height: 36px; padding: 0 8px 0 36px; border-bottom: 1px solid var(--fx-line-soft); font-size: 14px; }
912
- .fx-cfm__row:hover { background: var(--fx-hover); }
1106
+ .fx-cfm__row { min-height: 40px; padding: 4px 8px 4px 40px; border-bottom: 1px solid var(--fx-line-soft); font-size: 14px; }
1107
+ .fx-cfm__row:hover, .fx-cfm__row[data-open] { background: var(--fx-hover); }
1108
+ .fx-cfm__row[data-open] { box-shadow: inset 2px 0 0 var(--t-accent); }
913
1109
  .fx-cfm__row[data-archived] .fx-cfm__label { color: var(--t-dim); }
914
- .fx-cfm__name { display: flex; align-items: center; gap: 8px; min-width: 0; padding-right: 8px; color: var(--t-strong); }
915
- .fx-cfm__name > .fx-lucide { width: 13px; height: 13px; color: var(--t-dim); }
1110
+ .fx-cfm__name { min-width: 0; color: var(--t-strong); }
1111
+ .fx-cfm__title { display: flex; align-items: center; gap: 8px; min-width: 0; min-height: 28px; }
1112
+ .fx-cfm__title > .fx-lucide { width: 13px; height: 13px; color: var(--t-dim); }
916
1113
  .fx-cfm__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
917
1114
  .fx-cfm__edit { display: none; align-items: center; gap: 5px; flex: none; height: 24px; padding: 0 7px; border: 1px solid var(--fx-line); border-radius: 6px; background: var(--t-paper); color: var(--t-text); font: inherit; font-size: 12px; cursor: pointer; }
918
1115
  .fx-cfm__edit .fx-lucide { width: 13px; height: 13px; }
919
1116
  .fx-cfm__row:hover .fx-cfm__edit, .fx-cfm__edit:focus-visible { display: inline-flex; }
920
1117
  .fx-cfm__edit:focus-visible { box-shadow: var(--fx-focus); outline: none; }
921
1118
  .fx-cfm__rename { height: 28px; width: 100%; max-width: 320px; }
922
- .fx-cfm__type { color: var(--cf); }
923
- .fx-cfm__type .fx-lucide { width: 17px; height: 17px; }
1119
+ .fx-cfm__icon { display: inline-grid; place-items: center; width: 28px; height: 28px; border-radius: 6px; color: var(--cf); background: color-mix(in srgb, var(--cf) 14%, transparent); }
1120
+ .fx-cfm__icon .fx-lucide { width: 16px; height: 16px; }
924
1121
  .fx-cfm__avatar { display: inline-grid; place-items: center; width: 24px; height: 24px; border-radius: 50%; background: var(--t-primary); color: var(--t-on-primary); font-size: 10.5px; font-weight: 700; letter-spacing: .02em; }
925
1122
  .fx-cfm__avatar .fx-lucide { width: 13px; height: 13px; }
926
1123
  .fx-cfm__date, .fx-cfm__where { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@@ -929,7 +1126,7 @@ body[data-state="loading"] .fx-loading { display: grid; justify-items: center; }
929
1126
  .fx-cfm__more { all: unset; cursor: pointer; display: inline-grid; place-items: center; width: 30px; height: 28px; border-radius: 6px; color: var(--t-dim); }
930
1127
  .fx-cfm__more:hover, .fx-cfm__more[aria-expanded="true"] { background: var(--fx-hover); color: var(--t-strong); }
931
1128
  .fx-cfm__more:focus-visible { box-shadow: var(--fx-focus); }
932
- .fx-cfm__create { all: unset; cursor: pointer; display: flex; align-items: center; gap: 6px; height: 36px; padding: 0 16px 0 36px; color: var(--t-dim); font-size: 13px; }
1129
+ .fx-cfm__create { all: unset; cursor: pointer; display: flex; align-items: center; gap: 6px; height: 36px; padding: 0 16px 0 40px; color: var(--t-dim); font-size: 13px; }
933
1130
  .fx-cfm__create:hover, .fx-cfm__create:focus-visible { color: var(--t-strong); background: var(--fx-hover); }
934
1131
  .fx-cfm__create .fx-lucide { width: 14px; height: 14px; }
935
1132
  .fx-cfm__empty { margin: 0; padding: 20px 16px; color: var(--t-dim); }
@@ -938,8 +1135,63 @@ body[data-state="loading"] .fx-loading { display: grid; justify-items: center; }
938
1135
  .fx-pop__item:disabled { opacity: .5; cursor: default; }
939
1136
  .fx-pop__item[data-tone="danger"], .fx-pop__item[data-tone="danger"] .fx-pop__tick { color: var(--t-danger); }
940
1137
  .fx-pop__item .fx-lucide { width: 15px; height: 15px; }
1138
+ /* The sidecar (custom-fields.js edit), like ClickUp's: a panel that slides
1139
+ in from the right of the manager and leaves the table usable. */
1140
+ .fx-cfm__card { position: relative; }
1141
+ .fx-cfm__card:has(> .fx-cfs:not([hidden])) { min-height: min(640px, 100vh); }
1142
+ .fx-cfs { position: absolute; inset: 0 0 0 auto; z-index: 5; display: flex; width: min(420px, 100%); border-left: 1px solid var(--fx-line); background: var(--t-paper); color: var(--t-text); box-shadow: -16px 0 40px rgba(0, 0, 0, .22); animation: fx-cfs-in .18s var(--fx-ease); }
1143
+ .fx-cfs[hidden] { display: none; }
1144
+ @keyframes fx-cfs-in { from { transform: translateX(24px); opacity: 0; } }
1145
+ @media (prefers-reduced-motion: reduce) { .fx-cfs { animation: none; } }
1146
+ .fx-cfs__form { position: sticky; top: 0; display: flex; flex-direction: column; width: 100%; height: min(100%, 100vh); max-height: 100vh; min-height: 0; }
1147
+ .fx-cfs__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 52px; padding: 0 12px 0 16px; border-bottom: 1px solid var(--fx-line-soft); }
1148
+ .fx-cfs__typename, .fx-cfs__typepick { display: inline-flex; align-items: center; gap: 8px; color: var(--t-text); font-weight: 500; font-size: 14px; }
1149
+ .fx-cfs__typepick { position: relative; padding: 4px 8px; margin-left: -8px; border-radius: 6px; cursor: pointer; }
1150
+ .fx-cfs__typepick:hover, .fx-cfs__typepick:focus-within { background: var(--fx-hover); }
1151
+ .fx-cfs__typepick .fx-lucide { width: 15px; height: 15px; }
1152
+ .fx-cfs__type { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
1153
+ .fx-cfs__close, .fx-cfs__delete, .fx-cfs__tool { all: unset; cursor: pointer; display: inline-grid; place-items: center; width: 30px; height: 30px; border-radius: 6px; color: var(--t-dim); }
1154
+ .fx-cfs__close:hover, .fx-cfs__tool:hover:not(:disabled) { background: var(--fx-hover); color: var(--t-strong); }
1155
+ .fx-cfs__close:focus-visible, .fx-cfs__delete:focus-visible, .fx-cfs__tool:focus-visible { box-shadow: var(--fx-focus); }
1156
+ .fx-cfs__body { flex: 1; min-height: 0; overflow-y: auto; display: grid; align-content: start; gap: 0; }
1157
+ .fx-cfs__section { display: grid; gap: 8px; padding: 14px 16px 4px; }
1158
+ .fx-cfs__section:last-child { margin-top: 14px; padding-top: 16px; border-top: 1px solid var(--fx-line-soft); }
1159
+ .fx-cfs__section[hidden] { display: none; }
1160
+ .fx-cfs__label { color: var(--t-text); font-size: 12.5px; font-weight: 500; }
1161
+ .fx-cfs__req { margin-left: 2px; color: var(--t-danger); }
1162
+ .fx-cfs__hint { font-size: 12.5px; }
1163
+ .fx-cfs__namebox, .fx-cfs__opt { display: flex; align-items: center; gap: 8px; min-height: 32px; padding: 0 8px; border: 1px solid var(--fx-line); border-radius: var(--t-radius); background: var(--t-paper); }
1164
+ .fx-cfs__namebox:focus-within, .fx-cfs__opt:focus-within { border-color: var(--t-accent); box-shadow: var(--fx-focus); }
1165
+ .fx-cfs__nameicon { display: inline-grid; place-items: center; color: var(--cf); }
1166
+ .fx-cfs__nameicon .fx-lucide { width: 16px; height: 16px; }
1167
+ .fx-cfs__name, .fx-cfs__optname { flex: 1; min-width: 0; height: 30px; padding: 0; border: 0; border-radius: 0; background: none; color: var(--t-text); font: 400 14px/30px var(--t-body); outline: none; box-shadow: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1168
+ .fx-cfs__name:focus, .fx-cfs__optname:focus { outline: none; box-shadow: none; border: 0; }
1169
+ .fx-cfs__list, .fx-cfs__options { display: grid; gap: 6px; }
1170
+ .fx-cfs__opt--locked { padding-left: 12px; }
1171
+ .fx-cfs__dot[data-rank] { --cf: var(--t-accent); }
1172
+ .fx-cfs__dot[data-rank="0"] { --cf: var(--t-mute); }
1173
+ .fx-cfs__dot[data-rank="3"], .fx-cfs__dot[data-rank="4"] { --cf: color-mix(in srgb, var(--t-accent) 45%, var(--t-success)); }
1174
+ .fx-cfs__dot[data-rank="5"], .fx-cfs__dot[data-rank="6"] { --cf: var(--t-success); }
1175
+ .fx-cfs__dot[data-rank="7"] { --cf: var(--t-dim); }
1176
+ .fx-cfs__colour.fx-cf__swatch { min-height: 0; padding: 4px; border: 0; background: none; border-radius: 50%; }
1177
+ .fx-cfs__colour.fx-cf__swatch:hover:not(:disabled) { background: var(--fx-hover); }
1178
+ .fx-cfs__dot { display: block; width: 12px; height: 12px; border-radius: 50%; background: var(--cf); }
1179
+ .fx-cfs__tools { display: inline-flex; opacity: 0; transition: opacity .12s var(--fx-ease); }
1180
+ .fx-cfs__opt:hover .fx-cfs__tools, .fx-cfs__opt:focus-within .fx-cfs__tools { opacity: 1; }
1181
+ .fx-cfs__tool { width: 26px; height: 26px; }
1182
+ .fx-cfs__tool:disabled { opacity: .35; cursor: default; }
1183
+ .fx-cfs__tool .fx-lucide { width: 14px; height: 14px; }
1184
+ .fx-cfs__add { all: unset; cursor: pointer; display: flex; align-items: center; gap: 6px; min-height: 32px; padding: 0 10px; border: 1px dashed var(--fx-line); border-radius: var(--t-radius); color: var(--t-dim); font-size: 13.5px; }
1185
+ .fx-cfs__add:hover, .fx-cfs__add:focus-visible { color: var(--t-strong); border-color: var(--t-dim); }
1186
+ .fx-cfs__add .fx-lucide { width: 14px; height: 14px; }
1187
+ .fx-cfs__where { display: flex; align-items: center; gap: 8px; color: var(--t-text); font-size: 14px; }
1188
+ .fx-cfs__where .fx-lucide { width: 16px; height: 16px; color: var(--t-dim); }
1189
+ .fx-cfs__foot { display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--fx-line-soft); }
1190
+ .fx-cfs__delete { color: var(--t-danger); background: color-mix(in srgb, var(--t-danger) 12%, transparent); }
1191
+ .fx-cfs__delete:hover { background: color-mix(in srgb, var(--t-danger) 22%, transparent); }
1192
+ .fx-cfs__delete .fx-lucide { width: 16px; height: 16px; }
941
1193
  @media (max-width: 720px) {
942
- .fx-cfm { grid-template-columns: minmax(0, 1fr) 40px 40px; }
1194
+ .fx-cfm { grid-template-columns: minmax(0, 1fr) 36px 32px; column-gap: 8px; }
943
1195
  .fx-cfm .fx-cfm__by, .fx-cfm .fx-cfm__date, .fx-cfm .fx-cfm__where { display: none; }
944
1196
  .fx-cfm__head, .fx-cfm__row { padding-left: 16px; }
945
1197
  .fx-cfm__create { padding-left: 16px; }
@@ -1092,3 +1344,48 @@ body[data-state="loading"] .fx-loading { display: grid; justify-items: center; }
1092
1344
  .fx-bulk__sep { margin: 0 2px; }
1093
1345
  .fx-bulk__act:not(.fx-bulk__act--primary) .fx-bulk__word { display: none; }
1094
1346
  }
1347
+
1348
+ /* Xero › Import (server/ui/xero-import.js): Review compares one fact per row
1349
+ with ✓ or ≠ between the sides; the list and history tick rows for the bulk
1350
+ bar. */
1351
+ .fx-xi { display: flex; flex-direction: column; gap: 16px; min-width: 0; max-width: 980px; }
1352
+ .fx-xi .fx-lucide { width: 16px; height: 16px; }
1353
+ .fx-xi__views { display: inline-flex; border: 1px solid var(--fx-line); }
1354
+ .fx-xi__view { display: inline-grid; place-items: center; width: 40px; height: 36px; border: 0; background: transparent; color: var(--t-dim); cursor: pointer; }
1355
+ .fx-xi__view[aria-selected="true"] { background: var(--t-primary); color: var(--t-on-primary); }
1356
+ .fx-xi__count { display: inline-flex; align-items: center; gap: 10px; font-variant-numeric: tabular-nums; }
1357
+ .fx-xi__progress { display: inline-block; width: 120px; height: 4px; background: var(--fx-line); }
1358
+ .fx-xi__progress > span { display: block; height: 100%; background: var(--t-primary); }
1359
+ .fx-xi__bulk { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 10px 14px; border: 1px dashed var(--t-primary); background: color-mix(in srgb, var(--t-primary) 6%, transparent); color: var(--t-strong); font-size: 13.5px; }
1360
+ .fx-xi__bulk-text { margin-right: auto; }
1361
+ .fx-xi__bulk .fx-btn { display: inline-flex; align-items: center; gap: 6px; }
1362
+ .fx-xi__bulk .fx-xi__search { flex: 1; min-width: 0; margin-right: 0; }
1363
+ .fx-xi-cmp { width: 100%; border-collapse: collapse; border: 1px solid var(--fx-line); background: var(--t-s1); }
1364
+ .fx-xi-cmp th, .fx-xi-cmp td { padding: 11px 16px; border-top: 1px solid var(--fx-line-soft); text-align: left; font-size: 14px; color: var(--t-strong); }
1365
+ .fx-xi-cmp thead th { border-top: 0; padding: 14px 16px; }
1366
+ .fx-xi-cmp tbody th { width: 44px; color: var(--t-dim); }
1367
+ .fx-xi-cmp td:nth-child(2), .fx-xi-cmp th:nth-child(2) { width: 42%; }
1368
+ .fx-xi-cmp__mid { width: 48px; text-align: center !important; }
1369
+ .fx-xi__mark { height: 22px; width: auto; vertical-align: middle; }
1370
+ .fx-xi-cmp thead th:last-child { display: flex; align-items: center; gap: 10px; color: var(--t-dim); }
1371
+ .fx-xi__agree { display: inline-grid; place-items: center; width: 24px; height: 24px; }
1372
+ .fx-xi__agree[data-state="same"] { color: var(--t-success); }
1373
+ .fx-xi__agree[data-state="differs"] { color: var(--t-warn); }
1374
+ .fx-xi__acts { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; }
1375
+ .fx-xi__acts .fx-btn { display: inline-flex; align-items: center; gap: 6px; }
1376
+ .fx-xi__done { display: flex; align-items: center; gap: 12px; padding: 24px; border: 1px solid var(--fx-line); color: var(--t-success); }
1377
+ .fx-xi__done p { margin: 0; color: var(--t-strong); }
1378
+ .fx-xi__scroll { overflow-x: auto; }
1379
+ .fx-xi-list .fx-xi__mark { height: 18px; }
1380
+ .fx-xi-list__mid { width: 56px; text-align: center; color: var(--t-dim); white-space: nowrap; }
1381
+ .fx-xi-list__mid .fx-dim { margin-left: 4px; font-size: 12px; }
1382
+ .fx-xi-list tr[data-state="linked"] .fx-xi-list__mid { color: var(--t-success); }
1383
+ .fx-xi-list tr[data-state="invoice_fits"] .fx-xi-list__mid a, .fx-xi-list tr[data-state="job_fits"] .fx-xi-list__mid a { color: var(--t-warn); }
1384
+ @media (max-width: 820px) {
1385
+ .fx-xi-cmp th, .fx-xi-cmp td { padding: 10px 8px; font-size: 13px; }
1386
+ .fx-xi-cmp tbody th { width: 28px; }
1387
+ .fx-xi-cmp__mid { width: 28px; }
1388
+ .fx-xi__acts .fx-btn { flex: 1 1 auto; min-height: 44px; justify-content: center; }
1389
+ }
1390
+ .fx-xi-count { font-size: 18px; }
1391
+ .fx-xi-list td.fx-num:not(.fx-strong) { text-align: left; }
@@ -20,7 +20,7 @@ export function sendModeNote(to, sendMode, what = 'each invoice') {
20
20
 
21
21
  // The line under a draft's title, one fact per sentence.
22
22
  export function draftNote(to, invoice, ready) {
23
- if (!to) return ready ? 'A person checked it. A saved change keeps it ready.' : 'Flow gives it a number when you issue it.';
24
- if (!ready) return `Finalise it, then send it to ${to}.`;
23
+ if (!to) return ready ? 'It has no problem. Approve gives it a number.' : 'Fix the problem, then approve it.';
24
+ if (!ready) return `Fix the problem, then send it to ${to}.`;
25
25
  return sendModeNote(to, invoice?.sendMode, 'this invoice');
26
26
  }