@crisp-ui-kit/crisp 0.54.7 → 0.54.9

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 (130) hide show
  1. package/dist/authshell.css +5 -1
  2. package/dist/authshell.layered.css +5 -1
  3. package/dist/badge.css +5 -5
  4. package/dist/badge.layered.css +5 -5
  5. package/dist/board.css +36 -8
  6. package/dist/board.layered.css +36 -8
  7. package/dist/button.css +3 -1
  8. package/dist/button.layered.css +3 -1
  9. package/dist/cjs/components/badge/badge.recipe.js +28 -5
  10. package/dist/cjs/components/button/button.js +33 -2
  11. package/dist/cjs/components/button/button.recipe.js +31 -5
  12. package/dist/cjs/components/contextmenu/contextmenu.js +2 -2
  13. package/dist/cjs/components/contextmenu/contextmenu.recipe.js +24 -0
  14. package/dist/cjs/components/datatable/datatable.js +590 -41
  15. package/dist/cjs/components/datatable/datatable.recipe.js +118 -5
  16. package/dist/cjs/components/dialog/dialog.js +72 -5
  17. package/dist/cjs/components/dialog/dialog.recipe.js +15 -1
  18. package/dist/cjs/components/tabletoolbar/tabletoolbar.chrome.js +49 -5
  19. package/dist/cjs/components/tabletoolbar/tabletoolbar.js +365 -49
  20. package/dist/cjs/components/tabletoolbar/tabletoolbar.recipe.js +78 -0
  21. package/dist/cjs/components/tabs/tabs.recipe.js +11 -2
  22. package/dist/cjs/components/tooltip/tooltip.js +23 -9
  23. package/dist/cjs/components/tooltip/tooltip.recipe.js +10 -9
  24. package/dist/cjs/product/authshell/authshell.js +14 -1
  25. package/dist/cjs/product/authshell/authshell.recipe.js +13 -1
  26. package/dist/cjs/product/board/board.js +175 -46
  27. package/dist/cjs/product/board/board.recipe.js +56 -4
  28. package/dist/cjs/product/comments/comments.js +34 -12
  29. package/dist/cjs/product/comments/comments.recipe.js +31 -7
  30. package/dist/cjs/product/formdialog/formdialog.js +10 -4
  31. package/dist/cjs/product/navitem/navitem.js +7 -2
  32. package/dist/cjs/product/navitem/navitem.recipe.js +16 -0
  33. package/dist/cjs/product/objectsettings/index.js +1 -2
  34. package/dist/cjs/product/objectsettings/objectsettings.js +3 -13
  35. package/dist/cjs/product/recordstable/recordstable.js +1 -17
  36. package/dist/cjs/product/richtext/richtext.js +76 -1
  37. package/dist/cjs/product/savedviews/savedviews.js +55 -24
  38. package/dist/cjs/product/savedviews/savedviews.recipe.js +27 -0
  39. package/dist/cjs/product/sidebar/sidebar.js +68 -15
  40. package/dist/cjs/product/sidebar/sidebar.recipe.js +6 -1
  41. package/dist/cjs/tokens/palette.js +32 -1
  42. package/dist/cjs/tokens/semantic.js +11 -0
  43. package/dist/comments.css +50 -14
  44. package/dist/comments.layered.css +50 -14
  45. package/dist/contextmenu.css +24 -0
  46. package/dist/contextmenu.layered.css +24 -0
  47. package/dist/datatable.css +101 -37
  48. package/dist/datatable.layered.css +101 -37
  49. package/dist/dialog.css +13 -1
  50. package/dist/dialog.layered.css +13 -1
  51. package/dist/esm/components/badge/badge.recipe.js +28 -5
  52. package/dist/esm/components/button/button.js +34 -3
  53. package/dist/esm/components/button/button.recipe.js +31 -5
  54. package/dist/esm/components/contextmenu/contextmenu.js +2 -2
  55. package/dist/esm/components/contextmenu/contextmenu.recipe.js +24 -0
  56. package/dist/esm/components/datatable/datatable.js +590 -42
  57. package/dist/esm/components/datatable/datatable.recipe.js +119 -6
  58. package/dist/esm/components/dialog/dialog.js +72 -5
  59. package/dist/esm/components/dialog/dialog.recipe.js +15 -1
  60. package/dist/esm/components/tabletoolbar/tabletoolbar.chrome.js +49 -5
  61. package/dist/esm/components/tabletoolbar/tabletoolbar.js +366 -50
  62. package/dist/esm/components/tabletoolbar/tabletoolbar.recipe.js +80 -2
  63. package/dist/esm/components/tabs/tabs.recipe.js +11 -2
  64. package/dist/esm/components/tooltip/tooltip.js +23 -9
  65. package/dist/esm/components/tooltip/tooltip.recipe.js +10 -9
  66. package/dist/esm/product/authshell/authshell.js +15 -2
  67. package/dist/esm/product/authshell/authshell.recipe.js +13 -1
  68. package/dist/esm/product/authshell/index.js +1 -1
  69. package/dist/esm/product/board/board.js +176 -47
  70. package/dist/esm/product/board/board.recipe.js +56 -4
  71. package/dist/esm/product/comments/comments.js +35 -13
  72. package/dist/esm/product/comments/comments.recipe.js +31 -7
  73. package/dist/esm/product/formdialog/formdialog.js +10 -4
  74. package/dist/esm/product/navitem/index.js +1 -1
  75. package/dist/esm/product/navitem/navitem.js +7 -2
  76. package/dist/esm/product/navitem/navitem.recipe.js +16 -0
  77. package/dist/esm/product/objectsettings/index.js +1 -1
  78. package/dist/esm/product/objectsettings/objectsettings.js +2 -12
  79. package/dist/esm/product/recordstable/recordstable.js +2 -18
  80. package/dist/esm/product/richtext/richtext.js +76 -1
  81. package/dist/esm/product/savedviews/savedviews.js +57 -26
  82. package/dist/esm/product/savedviews/savedviews.recipe.js +27 -0
  83. package/dist/esm/product/sidebar/sidebar.js +69 -16
  84. package/dist/esm/product/sidebar/sidebar.recipe.js +6 -1
  85. package/dist/esm/tokens/palette.js +32 -1
  86. package/dist/esm/tokens/semantic.js +11 -0
  87. package/dist/native/tokens.d.ts +1 -1
  88. package/dist/native/tokens.js +16 -2
  89. package/dist/navitem.css +7 -0
  90. package/dist/navitem.layered.css +7 -0
  91. package/dist/recordstable.css +34 -32
  92. package/dist/recordstable.layered.css +34 -32
  93. package/dist/savedviews.css +27 -0
  94. package/dist/savedviews.layered.css +27 -0
  95. package/dist/sidebar.css +6 -1
  96. package/dist/sidebar.layered.css +6 -1
  97. package/dist/styles.css +369 -106
  98. package/dist/styles.layered.css +369 -106
  99. package/dist/tabletoolbar.css +47 -3
  100. package/dist/tabletoolbar.layered.css +47 -3
  101. package/dist/tabs.css +1 -1
  102. package/dist/tabs.layered.css +1 -1
  103. package/dist/tokens.css +9 -1
  104. package/dist/tokens.json +16 -2
  105. package/dist/tooltip.css +1 -1
  106. package/dist/tooltip.layered.css +1 -1
  107. package/dist/types/components/badge/badge.recipe.d.ts +5 -1
  108. package/dist/types/components/button/button.recipe.d.ts +18 -5
  109. package/dist/types/components/contextmenu/contextmenu.d.ts +8 -1
  110. package/dist/types/components/datatable/datatable.d.ts +98 -1
  111. package/dist/types/components/dialog/dialog.d.ts +25 -2
  112. package/dist/types/components/tabletoolbar/tabletoolbar.d.ts +113 -3
  113. package/dist/types/components/tooltip/tooltip.d.ts +12 -4
  114. package/dist/types/components/tooltip/tooltip.recipe.d.ts +1 -1
  115. package/dist/types/product/authshell/authshell.d.ts +26 -2
  116. package/dist/types/product/authshell/index.d.ts +1 -1
  117. package/dist/types/product/board/board.d.ts +53 -6
  118. package/dist/types/product/comments/comments.d.ts +16 -3
  119. package/dist/types/product/formdialog/formdialog.d.ts +2 -1
  120. package/dist/types/product/navitem/index.d.ts +1 -1
  121. package/dist/types/product/navitem/navitem.d.ts +23 -0
  122. package/dist/types/product/objectsettings/index.d.ts +1 -1
  123. package/dist/types/product/objectsettings/objectsettings.d.ts +0 -10
  124. package/dist/types/product/recordstable/recordstable.d.ts +0 -1
  125. package/dist/types/product/savedviews/savedviews.d.ts +45 -8
  126. package/dist/types/product/sidebar/sidebar.d.ts +33 -5
  127. package/dist/types/tokens/contract.d.ts +4 -0
  128. package/dist/types/tokens/palette.d.ts +13 -1
  129. package/dist/types/tokens/semantic.d.ts +11 -0
  130. package/package.json +1 -1
@@ -5,14 +5,18 @@
5
5
  --crisp-fg-on-tone: #101112;
6
6
  --crisp-bg-surface: #ffffff;
7
7
  --crisp-bg-raised: #f4f4f5;
8
+ --crisp-bg-surface-muted: #f6f7f7;
8
9
  --crisp-border-default: #e4e4e7;
9
10
  --crisp-border-subtle: #eeeff1;
10
11
  --crisp-border-dropzone: #e6e7ea;
12
+ --crisp-border-divider: #e6e7ea;
11
13
  --crisp-ring-neutral: transparent;
12
14
  --crisp-ring-field: rgba(0,0,0,0.06);
13
15
  --crisp-ring-switch: #d9dade;
14
16
  --crisp-bg-tile-active: #ffffff;
15
17
  --crisp-shadow-tile-active: rgba(0,0,0,0.04);
18
+ --crisp-bg-scrim: rgba(0,0,0,0.25);
19
+ --crisp-fg-alert-description: rgba(0,0,0,0.63);
16
20
  --crisp-bg-overlay-frost: #ffffff;
17
21
  --crisp-shadow-overlay1: rgba(28,40,64,0.12);
18
22
  --crisp-shadow-overlay2: rgba(28,40,64,0.16);
@@ -46,7 +50,7 @@
46
50
  --crisp-fg-success-soft: #186b3d;
47
51
  --crisp-fg-warning-soft: #80520d;
48
52
  --crisp-fg-danger-soft: #a33539;
49
- --crisp-fg-neutral-soft: #686870;
53
+ --crisp-fg-neutral-soft: #505155;
50
54
  --crisp-bg-purple-soft: #f5f0ff;
51
55
  --crisp-fg-purple-soft: #6238b5;
52
56
  --crisp-ring-purple-soft: #e8ddfe;
@@ -59,14 +63,18 @@
59
63
  --crisp-fg-on-tone: #101112;
60
64
  --crisp-bg-surface: #0a0a0a;
61
65
  --crisp-bg-raised: #101112;
66
+ --crisp-bg-surface-muted: #101112;
62
67
  --crisp-border-default: #212224;
63
68
  --crisp-border-subtle: #1c1d1f;
64
69
  --crisp-border-dropzone: #212224;
70
+ --crisp-border-divider: #212224;
65
71
  --crisp-ring-neutral: #212224;
66
72
  --crisp-ring-field: #212224;
67
73
  --crisp-ring-switch: #212224;
68
74
  --crisp-bg-tile-active: #101112;
69
75
  --crisp-shadow-tile-active: rgba(0,0,0,0);
76
+ --crisp-bg-scrim: rgba(0,0,0,0.55);
77
+ --crisp-fg-alert-description: rgba(255,255,255,0.6);
70
78
  --crisp-bg-overlay-frost: #1c1d1f;
71
79
  --crisp-shadow-overlay1: rgba(0,0,0,0.48);
72
80
  --crisp-shadow-overlay2: rgba(0,0,0,0.64);
@@ -217,11 +225,11 @@
217
225
  }
218
226
 
219
227
  .crisp-badge {
220
- display: inline-flex; align-items: center; justify-content: center; gap: var(--crisp-space-1); font-family: var(--crisp-font-sans); font-weight: var(--crisp-weight-medium); letter-spacing: -0.1px; white-space: nowrap;
228
+ display: inline-flex; align-items: center; justify-content: center; gap: var(--crisp-space-1); font-family: var(--crisp-font-sans); font-weight: var(--crisp-weight-medium); letter-spacing: normal; white-space: nowrap;
221
229
  & .crisp-badge-dot { width: 6px; height: 6px; flex-shrink: 0; border-radius: var(--crisp-radius-full); background: currentColor; }
222
230
  }
223
231
  .crisp-badge--tone_neutral {
224
- background: var(--crisp-bg-raised); color: var(--crisp-fg-neutral-soft); box-shadow: inset 0 0 0 1px var(--crisp-border-default);
232
+ background: var(--crisp-bg-raised); color: var(--crisp-fg-neutral-soft); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle);
225
233
  }
226
234
  .crisp-badge--tone_brand {
227
235
  background: color-mix(in srgb, var(--crisp-bg-brand-solid) 14%, transparent); color: var(--crisp-fg-brand-soft); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-brand-solid) 30%, transparent);
@@ -239,13 +247,13 @@
239
247
  background: var(--crisp-bg-purple-soft); color: var(--crisp-fg-purple-soft); box-shadow: inset 0 0 0 1px var(--crisp-ring-purple-soft);
240
248
  }
241
249
  .crisp-badge--size_small {
242
- height: 16px; padding-inline: var(--crisp-space-1-5); font-size: var(--crisp-text-xs); border-radius: var(--crisp-radius-sm);
250
+ height: 16px; padding-inline: var(--crisp-space-1); font-size: var(--crisp-text-sm); line-height: 16px; border-radius: var(--crisp-radius-sm);
243
251
  }
244
252
  .crisp-badge--size_medium {
245
- height: 20px; padding-inline: var(--crisp-space-2); font-size: var(--crisp-text-sm); border-radius: var(--crisp-radius-sm);
253
+ height: 20px; padding-inline: var(--crisp-space-1); font-size: var(--crisp-text-sm); line-height: 16px; gap: 3px; border-radius: var(--crisp-radius-sm);
246
254
  }
247
255
  .crisp-badge--size_large {
248
- height: var(--crisp-size-chip); padding-inline: 9px; font-size: var(--crisp-text-lg); border-radius: var(--crisp-radius-chip);
256
+ height: var(--crisp-size-chip); padding-inline: 9px; font-size: var(--crisp-text-lg); letter-spacing: -0.1px; border-radius: var(--crisp-radius-chip);
249
257
  }
250
258
  .crisp-badge--variant_soft {
251
259
 
@@ -331,7 +339,7 @@
331
339
  }
332
340
 
333
341
  .crisp-button {
334
- display: inline-flex; align-items: center; justify-content: center; gap: var(--crisp-space-1-5); font-family: var(--crisp-font-sans); font-weight: var(--crisp-weight-medium); letter-spacing: -0.14px; line-height: 20px; white-space: nowrap; border: none; cursor: pointer; transition: background-color .2s, box-shadow .2s, color .2s;
342
+ display: inline-flex; align-items: center; justify-content: center; gap: var(--crisp-space-1-5); font-family: var(--crisp-font-sans); font-weight: var(--crisp-weight-medium); letter-spacing: -0.07px; line-height: 20px; white-space: nowrap; border: none; cursor: pointer; transition: background-color .2s, box-shadow .2s, color .2s;
335
343
  &:disabled { opacity: 0.5; cursor: not-allowed; }
336
344
  }
337
345
  .crisp-button--intent_primary {
@@ -357,6 +365,8 @@
357
365
  }
358
366
  .crisp-button--size_md {
359
367
  height: 28px; padding-inline: var(--crisp-space-2); font-size: var(--crisp-text-lg); border-radius: var(--crisp-radius-md);
368
+ &[data-icon-start] { padding-inline-start: var(--crisp-space-1-5); }
369
+ &[data-icon-end] { padding-inline-end: var(--crisp-space-1-5); }
360
370
  }
361
371
  .crisp-button--size_lg {
362
372
  height: 40px; padding-inline: 14px; font-size: var(--crisp-text-lg); border-radius: 10px;
@@ -551,6 +561,30 @@
551
561
  .crisp-contextmenu-item--danger:hover { background: color-mix(in srgb, var(--crisp-bg-danger) 10%, transparent); }
552
562
  .crisp-contextmenu-shortcut { margin-inline-start: auto; padding-inline-start: var(--crisp-space-3); color: var(--crisp-fg-muted); font-size: var(--crisp-text-sm); }
553
563
  .crisp-contextmenu-sep { height: 1px; margin: var(--crisp-space-1) 0; border: none; background: var(--crisp-border-subtle); }
564
+ /* crisp/#598: Attio's OWN cell-level context menu, measured live on
565
+ app.attio.com/techpartners/companies (right-click a data cell) --
566
+ theme-INDEPENDENT dark chrome, not this app's dark THEME (every
567
+ geometry value above -- 12px/8px radius, 4px/8px padding, 32px item
568
+ height, item gap included below -- already matched what was
569
+ measured here; only the colour pair differs, so only colours are
570
+ overridden). Same "#101112 / #eeeff1" dark-pill ink Tooltip
571
+ already hardcodes for the identical reason (that surface is ALSO
572
+ theme-independent). */
573
+ .crisp-contextmenu-content[data-variant="dark"] { background: #101112; box-shadow: none; }
574
+ .crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item { gap: var(--crisp-space-1-5); color: #eeeff1; }
575
+ .crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item svg { color: #898a8d; }
576
+ /* Plain-item hover re-measured live on the same cell menu (right-click
577
+ "Description" on app.attio.com/techpartners/companies, hover the
578
+ "Copy" row): getComputedStyle on the row is rgba(255,255,255,0.1),
579
+ not the 0.08 this used to guess at. The danger row's own hover
580
+ (hovering "Clear value") still didn't get re-measured -- that one
581
+ remains the derived red-on-black translation below, flagged on
582
+ crisp#598 for someone to confirm live. */
583
+ .crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item:hover { background: rgba(255,255,255,0.1); }
584
+ .crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item--danger { color: #ff5454; }
585
+ .crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item--danger svg { color: #ff5454; }
586
+ .crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-item--danger:hover { background: rgba(255,84,84,0.12); }
587
+ .crisp-contextmenu-content[data-variant="dark"] .crisp-contextmenu-sep { background: rgba(255,255,255,0.1); }
554
588
 
555
589
  .crisp-contextmenu {
556
590
  display: contents; font-family: var(--crisp-font-sans);
@@ -575,45 +609,71 @@
575
609
  & .crisp-datatable-count { margin-inline-start: auto; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
576
610
  /* announced, never drawn */
577
611
  & .crisp-datatable-live { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
578
- /* The header cell has no padding of its own -- the button owns the full cell so the whole header is clickable (its own inset is 12px). Mirrors the summary-row fix. */
579
- & th { padding: 0; }
580
- /* Same wrapper trap as AUTHORING.md Lesson 40 (ColumnCalc's summary trigger):
581
- Menu wraps its trigger in a flex div that sizes to its content, so a
582
- long header grew past the column instead of filling it -- with nothing
583
- left to squeeze, the label's own ellipsis CSS (below) had no properly -sized box to truncate against, and the th's own overflow hard-clipped
584
- it with no "...". Fill the wrapper, scoped to th so Menu's default (correct everywhere else) is untouched. */
612
+ /* crisp/#645: visually-hidden-but-focusable, not visually-hidden-and- inert like .crisp-datatable-live above -- same clip-rect shape, this one just needs to actually hold DOM focus so a native copy/paste event has something to fire on (see its own comment in datatable.tsx). resize: none keeps a stray native textarea handle from ever being drawable, even though it's clipped to 1px anyway. */
613
+ & .crisp-datatable-clipboard-proxy {
614
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
615
+ overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
616
+ resize: none;
617
+ }
618
+ /* single active cell (crisp/#645 follow-up -- this had no styling of its
619
+ own at all before, a gap against Golden Rule 0: RecordsTable's own measured value, .crisp-recordstable-td[data-selected], brought across unchanged now that a visible anchor actually matters for the Shift+Click range below). */
620
+ & td[data-selected] { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: -2px; border-radius: var(--crisp-radius-sm); z-index: 3; }
621
+ /* crisp/#645, G-101: a Shift+Click range -- reuses the row-selection tint formula already used twice below (tbody tr[data-selected] td) for the same reason a selected group of rows gets one: a selected GROUP of cells is the same kind of state, just cell-scoped instead of row-scoped. Not independently re-measured against Attio's own range
622
+ highlight (out of scope this slice -- flagged on crisp#645). */
623
+ & td[data-in-range] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface));
624
+ } /* The header cell has no padding of its own -- the button owns the full cell so the whole header is clickable (its own inset is 12px). Mirrors the summary-row fix. `& .crisp-table th`, not bare `& th` (found while re-checking #691's
625
+ own fix live): DataTable's root nests `Table`'s own wrapper, whose
626
+ `table.recipe.ts` carries `& th, & td { padding: 0 12px }
627
+ ` at the SAME specificity (.crisp-table th vs .crisp-datatable th, both one class + one element) -- a bare `& th` here only won by source order, which flipped the moment table.recipe.ts's own CSS file
628
+ happened to land after datatable's in the build (alphabetical: datatable < table), silently reinstating 12px on every header cell in the whole product and making every interactive column's button
629
+ double it to 24px. Scoping through `.crisp-table` adds a second
630
+ class to the selector (0,2,1 beats 0,1,1) so this can never lose a
631
+ specificity tie to source order again. */
632
+ & .crisp-table th { padding: 0; }
633
+ /* Same wrapper trap as AUTHORING.md Lesson 40 (ColumnCalc's summary trigger): Menu wraps its trigger in a flex div that sizes to its content, so a long header grew past the column instead of filling it -- with nothing left to squeeze, the label's own ellipsis CSS (below) had no properly
634
+ -sized box to truncate against, and the th's own overflow hard-clipped it with no "...". Fill the wrapper, scoped to th so Menu's default
635
+ (correct everywhere else) is untouched. */
585
636
  & th .crisp-menu { width: 100%; height: 100%; }
586
637
  & .crisp-datatable-th-inner { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; min-width: 0; height: 100%; }
587
638
  & .crisp-datatable-th-inner > svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
588
- /* The summary cell has NO padding of its own -- the calculation trigger owns the whole 246x37 area so a click anywhere in the cell opens the menu. Zero the cell's inset and let ColumnCalc supply the 12px. */
639
+ /* crisp/#605 follow-up: a header with neither a Menu nor a sort button
640
+ (the primary/entity column's now-inert path, or any column with no columnMenu/sortable at all) renders .crisp-datatable-th-inner as a DIRECT child of th instead of nested inside a button -- and loses the button's own 12px inset, drifting left of the td's 12px base inset. Measured live on Attio's own Companies grid: the header label
641
+ and the cell's own text land at the exact same x (335px from the viewport, both 40px from the column's left edge) via two different
642
+ padding+icon+gap combinations -- Attio does not leave the inert
643
+ primary header flush left either. The `>` combinator targets only
644
+ the button-less case: Menu's own wrapper (`.crisp-menu`) and the plain sort `button` both put >=1 more level between `th` and this span, so their own 12px (already on the button) isn't doubled. */
645
+ & th > .crisp-datatable-th-inner { box-sizing: border-box; padding: 0 var(--crisp-space-3); }
646
+ /* The summary cell has NO padding of its own -- the calculation trigger
647
+ owns the whole 246x37 area so a click anywhere in the cell opens the
648
+ menu. Zero the cell's inset and let ColumnCalc supply the 12px. */
589
649
  & tfoot td { padding: 0; }
590
- /* Group heading row -- a view is grouped under a subtle label with the
591
- bucket's count beside it. */
592
- & .crisp-datatable-viewsettings { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-row); margin: 0; padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); background: transparent; color: var(--crisp-fg-muted); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); white-space: nowrap; cursor: pointer; transition: background-color .12s, color .12s; }
650
+ /* Group heading row -- a view is grouped under a subtle label with the bucket's count beside it. */
651
+ & .crisp-datatable-viewsettings { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); height: var(--crisp-size-row); margin: 0; padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); background: transparent; color: var(--crisp-fg-muted); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); white-space: nowrap; cursor: pointer; transition: background-color .12s, color .12s; }
593
652
  & .crisp-datatable-viewsettings:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
594
653
  & .crisp-datatable-group-label { padding: var(--crisp-space-1-5) var(--crisp-space-2) var(--crisp-space-1); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
595
- & .crisp-datatable-group { height: var(--crisp-size-menuRow); padding: 0 var(--crisp-space-3); background: var(--crisp-bg-subtle); border-bottom: 1px solid var(--crisp-border-subtle); font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
654
+ & .crisp-datatable-group { height: var(--crisp-size-menuRow); padding: 0 var(--crisp-space-3); background: var(--crisp-bg-subtle); border-bottom: 1px solid var(--crisp-border-subtle); font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
596
655
  & .crisp-datatable-group-count { margin-inline-start: var(--crisp-space-1-5); color: var(--crisp-fg-muted); font-variant-numeric: tabular-nums; }
597
656
  & .crisp-datatable-th-rename { width: 100%; min-width: 0; height: var(--crisp-size-row); margin: 0; padding: 0 var(--crisp-space-1); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); outline: none; }
598
657
  & .crisp-datatable-th-rename:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
599
658
  & .crisp-datatable-th-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
600
- /* #465: 12px per the comment above (and Attio's measured header cell
601
- padding: 0 12px) -- was still c.space["2"] (8px), drifted from its
602
- own documented value. */
659
+ /* #465: 12px per the comment above (and Attio's measured header cell padding: 0 12px) -- was still c.space["2"] (8px), drifted from its own documented value. */
603
660
  & .crisp-datatable-sort { display: flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; min-width: 0; height: 100%; margin: 0; padding: 0 var(--crisp-space-3); border: none; background: none; font: inherit; letter-spacing: inherit; color: inherit; text-align: start; cursor: pointer; }
604
- /* A right-aligned column's header keeps its field-icon + label packed at the START, matching its own values only in that the VALUES right-align -- Attio's own right-aligned header (measured live: "Twitter follower
605
- count") keeps the icon+label left and only the trailing column-menu
606
- icon sits pinned to the right edge, independent of the label's width (#406, reversing #393's own justify-content: flex-end here). The
607
- chevron's own margin-inline-start: auto below already pins IT to the end of a flex-start row with no override needed -- that's what pins
608
- the trailing icon on Attio too. */
661
+ /* A right-aligned column's header keeps its field-icon + label packed at
662
+ the START, matching its own values only in that the VALUES right-align
663
+ -- Attio's own right-aligned header (measured live: "Twitter follower
664
+ count") keeps the icon+label left and only the trailing column-menu icon sits pinned to the right edge, independent of the label's width
665
+ (#406, reversing #393's own justify-content: flex-end here). The chevron's own margin-inline-start: auto below already pins IT to the
666
+ end of a flex-start row with no override needed -- that's what pins the trailing icon on Attio too. */
609
667
  & .crisp-datatable-chevron { margin-inline-start: auto; flex-shrink: 0; color: var(--crisp-fg-muted); opacity: 0; transition: opacity .12s; }
610
668
  & .crisp-datatable-sort:hover .crisp-datatable-chevron { opacity: 0.6; }
611
669
  & .crisp-datatable-sort[data-sorted] .crisp-datatable-chevron { opacity: 1; color: var(--crisp-fg-primary); }
612
670
  /* sticky first column(s) + horizontal scroll shadow — RecordsTable's feel */
613
671
  & th[data-sticky], & td[data-sticky] { position: sticky; z-index: 1; background: var(--crisp-bg-surface); }
614
- & th[data-sticky] { z-index: 3; }
615
- /* #554: a footer sticky-column cell sits in the bottom-sticky row (table.recipe.ts's
616
- `tfoot td { z-index: 2 }`), which `td[data-sticky]`'s own higher-specificity `z-index: 1` would otherwise undercut -- keep it level with the row it's in. */
672
+ & th[data-sticky] { z-index: 3;
673
+ } /* #554: a footer sticky-column cell sits in the bottom-sticky row (table.recipe.ts's
674
+ `tfoot td { z-index: 2 }
675
+ `), which `td[data-sticky]`'s own higher-specificity
676
+ `z-index: 1` would otherwise undercut -- keep it level with the row it's in. */
617
677
  & tfoot td[data-sticky] { z-index: 2; }
618
678
  & tbody tr[data-selected] td[data-sticky] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface)); }
619
679
  & tbody tr:hover td[data-sticky] { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-surface)); }
@@ -632,40 +692,77 @@
632
692
  & .crisp-datatable-editable:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-surface)); cursor: text; }
633
693
  & .crisp-datatable-edit { width: 100%; height: 28px; margin: 0; padding: 0 var(--crisp-space-1-5); border: none; border-radius: var(--crisp-radius-sm); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); outline: none; vertical-align: middle; }
634
694
  & .crisp-datatable-edit:focus { box-shadow: inset 0 0 0 1px var(--crisp-bg-brand-solid); }
635
- /* An async onEdit's in-flight state (#385) -- the field/picker dims and
636
- stops accepting input rather than disappearing, so the just-typed or
637
- just-chosen value stays visible while its commit is pending. Same
638
- treatment Input already gives its own disabled state. */
695
+ /* An async onEdit's in-flight state (#385) -- the field/picker dims and stops accepting input rather than disappearing, so the just-typed or just-chosen value stays visible while its commit is pending. Same treatment Input already gives its own disabled state. */
639
696
  & .crisp-datatable-edit:disabled { opacity: 0.5; cursor: not-allowed; }
640
697
  & .crisp-datatable-edit-pending { flex-shrink: 0; margin-inline-start: var(--crisp-space-1); color: var(--crisp-fg-muted); vertical-align: middle; }
641
- /* The rejection's message, beside the cell's own content -- a sibling in
642
- the TableCell, never nested inside a column's own render output, so a hover-only wrapper (a Tooltip, say) around a custom render can't hide
643
- it. Block-level: the row grows to fit it rather than floating over
644
- the row below, so it can never end up clipped by the scroll
645
- container's overflow the way a position:fixed escape would need guarding against. */
698
+ /* The rejection's message, beside the cell's own content -- a sibling in the TableCell, never nested inside a column's own render output, so a
699
+ hover-only wrapper (a Tooltip, say) around a custom render can't hide it. Block-level: the row grows to fit it rather than floating over the row below, so it can never end up clipped by the scroll container's overflow the way a position:fixed escape would need
700
+ guarding against. */
646
701
  & .crisp-datatable-edit-error { display: block; padding: var(--crisp-space-0-5) 0 0; font-size: var(--crisp-text-sm); color: var(--crisp-bg-danger); }
647
- /* editor: "select"/"switch" -- always live, no separate edit mode, so the cell IS the picker: same chromeless-fill treatment as RecordsTable's own --picker cells (measured there already; DataTable
648
- shares RecordsTable's grid, Golden Rule 0). */
702
+ /* editor: "select"/"switch" -- always live, no separate edit mode, so
703
+ the cell IS the picker: same chromeless-fill treatment as
704
+ RecordsTable's own --picker cells (measured there already; DataTable shares RecordsTable's grid, Golden Rule 0). */
649
705
  & .crisp-datatable-td--picker { padding: 0; }
650
706
  & .crisp-datatable-edit-picker { display: flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); }
651
707
  & .crisp-datatable-edit-picker .crisp-select-trigger { flex: 1; min-width: 0; height: 100%; min-height: 0; border-radius: 0; box-shadow: none; padding: 0; }
652
708
  & .crisp-datatable-td--picker .crisp-datatable-edit-error { padding-inline-start: var(--crisp-space-2); }
653
- /* reference picker: a leading 16px avatar per option, plus a trailing "Create X" row when the consumer supplies one (#449) -- same measured values as RecordsTable's own .crisp-recordstable-refoption*. */
709
+ /* reference picker: a leading 16px avatar per option, plus a trailing
710
+ "Create X" row when the consumer supplies one (#449) -- same measured
711
+ values as RecordsTable's own .crisp-recordstable-refoption*. */
654
712
  & .crisp-datatable-refoption { display: flex; align-items: center; gap: var(--crisp-space-2); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
655
713
  & .crisp-datatable-refoption-avatar { flex-shrink: 0; }
656
714
  & .crisp-datatable-refoption--create { color: var(--crisp-fg-muted); }
657
715
  & .crisp-datatable-empty { padding: var(--crisp-space-8) var(--crisp-space-3); text-align: center; color: var(--crisp-fg-muted); font-size: var(--crisp-text-lg); }
716
+ /* #579: click-to-expand overlay for a truncated cell -- a read-only, full-value card, settled live as click (not Attio's usual
717
+ hover-expand) and measured position: absolute, unlike this file's floating chrome (Menu/Select popovers) which portals and escapes the scroll container. Deliberately NOT the edit-error pattern above (grow the row, block-level, never clipped): a virtualized grid assumes a fixed row height for its scroll math (owned by the grid core, not this cell), so growing a row here would fight it. Anchored to the SAME column width the cell itself renders at (measured: Attio's own overlay came out only slightly wider than the
718
+ triggering cell's clientWidth -- the overshoot is this padding), wrapping onto as many lines as the value needs; it CAN be clipped by `.crisp-table-scroll`'s own `overflow: auto` if it grows past the
719
+ visible viewport, same as the edit-error comment above already
720
+ notes for any position:absolute escape -- unmeasured against Attio
721
+ for this exact case, flagged rather than guessed. */
722
+ & td[data-cell-expanded] { overflow: visible; z-index: 7; }
723
+ & .crisp-datatable-cell-overlay {
724
+ position: absolute; inset-inline-start: 0; top: 0; width: 100%; box-sizing: border-box;
725
+ padding: var(--crisp-space-2) var(--crisp-space-3); background: var(--crisp-bg-surface);
726
+ border: 1px solid var(--crisp-bg-brand-solid); border-radius: 4px;
727
+ font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium);
728
+ color: var(--crisp-fg-primary); white-space: normal; word-break: break-word; cursor: default;
729
+ }
658
730
  /* fixed layout so column widths (and resize) are exact — like RecordsTable */
659
731
  & table { table-layout: fixed; }
732
+ /* read-only/computed column marker (#596) -- sits just inside the
733
+ resize handle, same absolute-positioned-against-the-th approach the
734
+ handle itself uses, since th has zero padding and the button/inner
735
+ row fills the whole cell with no room left for a trailing sibling. */
736
+ & .crisp-datatable-readonly { position: absolute; top: 50%; inset-inline-end: 11px; transform: translateY(-50%); color: var(--crisp-fg-muted); pointer-events: none; z-index: 5; }
660
737
  /* column resize grip */
661
738
  & .crisp-datatable-resize { position: absolute; top: 0; inset-inline-end: -3px; width: 9px; height: 100%; cursor: col-resize; z-index: 6; touch-action: none; }
662
739
  & .crisp-datatable-resize::after { content: ""; position: absolute; top: 0; inset-inline-start: 3px; width: 2px; height: 100%; background: var(--crisp-bg-brand-solid); opacity: 0; transition: opacity .12s; }
663
740
  & .crisp-datatable-resize:hover::after, & .crisp-datatable-resize[data-resizing]::after { opacity: 1; }
741
+ /* crisp/#731 -- column drag-to-reorder. Measured live against Attio's own grid (the issue's own comment): the dragged header keeps full
742
+ opacity and the plain `pointer` cursor throughout (Attio draws no
743
+ ghost and no "grabbing" cursor of its own), so this adds nothing to
744
+ `[data-dragging]` itself -- the ONLY new paint is the drop-indicator
745
+ line, a 3px bar in the same measured brand blue the resize handle's own hover state (above) already uses, positioned on whichever edge of the hovered header the pointer is closer to. */
746
+ & th[data-dragging] { cursor: pointer; }
747
+ & th[data-drop-before]::before, & th[data-drop-after]::after { content: ""; position: absolute; top: 0; width: 3px; height: 100%; background: var(--crisp-bg-brand-solid); z-index: 6; pointer-events: none; }
748
+ & th[data-drop-before]::before { inset-inline-start: -1.5px; }
749
+ & th[data-drop-after]::after { inset-inline-end: -1.5px; }
664
750
  /* typed cells — entity (avatar + name), colour-coded tags, numbers, dates */
665
751
  & .crisp-datatable-entity { display: flex; align-items: center; gap: var(--crisp-space-1-5); min-width: 0; color: var(--crisp-fg-primary); font-weight: var(--crisp-weight-medium); }
666
- & .crisp-datatable-entity-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
667
- & .crisp-datatable-entity:hover .crisp-datatable-entity-name { text-decoration: underline; }
752
+ /* crisp/#601, G-050: measured LIVE (getComputedStyle on the name text, plain hover vs rest) that Attio's underline is unconditional, not a
753
+ hover reveal -- identical at rest and on hover: text-decoration-color
754
+ is rgba(0,0,0,0.09), a 9% tint, not the name's own full-contrast text colour. The previous :hover-gated, full-contrast rule here was itself unmeasured and wrong in two ways at once. */
755
+ & .crisp-datatable-entity-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-decoration: underline; text-decoration-color: color-mix(in srgb, var(--crisp-fg-primary) 9%, transparent); }
668
756
  & button.crisp-datatable-entity-name--link { margin: 0; padding: 0; border: none; background: none; font: inherit; color: inherit; letter-spacing: inherit; text-align: start; cursor: pointer; max-width: 100%; }
757
+ /* crisp/#601, G-048: two 20px ghost icons, right-aligned in the primary column, revealed on ROW hover (the issue's own title says "row
758
+ hover", and a live check confirmed hovering well past the name --
759
+ not just the icons' own hit target -- still reveals them) or keyboard focus. Measured live: border-radius 6px, icon colour rgba(0,0,0,0.55) at rest, darkening to the full text colour with a rgba(0,0,0,0.04) background on the icon's OWN hover -- both
760
+ expressed as color-mix against fgPrimary so they stay correct in
761
+ dark mode instead of a literal black that would invert. */
762
+ & .crisp-datatable-entity-actions { display: flex; align-items: center; gap: var(--crisp-space-1-5); margin-inline-start: auto; flex-shrink: 0; opacity: 0; }
763
+ & tr:hover .crisp-datatable-entity-actions, & .crisp-datatable-entity-actions:focus-within { opacity: 1; }
764
+ & .crisp-datatable-entity-action { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; font-family: inherit; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); cursor: pointer; flex-shrink: 0; }
765
+ & .crisp-datatable-entity-action:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
669
766
  & .crisp-datatable-avatar { width: var(--crisp-size-icon); height: var(--crisp-size-icon); border-radius: var(--crisp-radius-sm); flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 10px; line-height: 1; font-weight: var(--crisp-weight-semibold); text-transform: uppercase; color: var(--crisp-fg-on-brand); background: var(--crisp-bg-brand-solid); }
670
767
  & .crisp-datatable-avatar[data-tone="success"] { background: var(--crisp-bg-success); } & .crisp-datatable-avatar[data-tone="warning"] { background: var(--crisp-bg-warning); } & .crisp-datatable-avatar[data-tone="danger"] { background: var(--crisp-bg-danger); } & .crisp-datatable-avatar[data-tone="purple"] { background: var(--crisp-bg-purple); } & .crisp-datatable-avatar[data-tone="cyan"] { background: var(--crisp-bg-cyan); } & .crisp-datatable-avatar[data-tone="orange"] { background: var(--crisp-bg-orange); }
671
768
  & .crisp-datatable-avatar img { width: 100%; height: 100%; border-radius: inherit; object-fit: cover; }
@@ -692,6 +789,7 @@
692
789
  & .crisp-datatable-grip { display: inline-flex; align-items: center; justify-content: center; width: 100%; height: 100%; margin: 0; border: none; border-radius: 0; background: none; font-family: inherit; color: var(--crisp-fg-muted); cursor: grab; }
693
790
  & .crisp-datatable-grip:active { cursor: grabbing; }
694
791
  & tbody tr[data-drop-target] td, & tbody tr[data-drop-target] th { box-shadow: inset 0 2px 0 0 var(--crisp-bg-brand-solid); transition: box-shadow .12s; }
792
+ [dir="rtl"] & [data-rtl-flip] { transform: scaleX(-1); }
695
793
  }
696
794
 
697
795
  @media (prefers-reduced-motion: reduce) {
@@ -836,17 +934,29 @@
836
934
 
837
935
  .crisp-dialog {
838
936
  display: contents; font-family: var(--crisp-font-sans);
839
- & .crisp-dialog-backdrop { position: fixed; inset: 0; z-index: 60; background: transparent; }
937
+ & .crisp-dialog-backdrop { position: fixed; inset: 0; z-index: 60; background: var(--crisp-bg-scrim); }
840
938
  & .crisp-dialog-positioner { position: fixed; inset: 0; z-index: 61; display: flex; align-items: flex-start; justify-content: center; padding: 15vh var(--crisp-space-4) var(--crisp-space-4); }
939
+ & .crisp-dialog-positioner[data-variant="alert"] { align-items: center; padding: var(--crisp-space-4); }
841
940
  & .crisp-dialog-content { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; position: relative; display: flex; flex-direction: column; box-sizing: border-box; width: 90vw; max-height: 74vh; padding: var(--crisp-space-1); border-radius: var(--crisp-radius-2xl); background: color-mix(in srgb, var(--crisp-bg-overlay-frost) 60%, transparent); backdrop-filter: blur(15px); -webkit-backdrop-filter: blur(15px); box-shadow: 0 8px 28px -6px var(--crisp-shadow-overlay1), 0 18px 88px -4px var(--crisp-shadow-overlay2), 0 0 1px var(--crisp-shadow-overlay3); opacity: 1; transform: scale(1); transition: opacity 0.2s ease, transform 0.2s ease; @starting-style { opacity: 0; transform: scale(0.97); } }
941
+ & .crisp-dialog-content[data-variant="alert"] { padding: 0; background: transparent; backdrop-filter: none; -webkit-backdrop-filter: none; box-shadow: none; }
842
942
  & .crisp-dialog-card { display: flex; flex-direction: column; flex: 1; min-height: 0; box-sizing: border-box; border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); box-shadow: 0 0 0 1px color-mix(in srgb, var(--crisp-fg-primary) 8%, transparent); overflow: hidden; }
943
+ & .crisp-dialog-content[data-variant="alert"] .crisp-dialog-card { border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-popover); box-shadow: inset 0 0 0 1px var(--crisp-ring-popover), 0 0 0 1px var(--crisp-shadow-popover1), 0 4px 8px -4px var(--crisp-shadow-popover2), 0 4px 12px -2px var(--crisp-shadow-popover3); }
843
944
  & .crisp-dialog-header { display: flex; align-items: center; gap: var(--crisp-space-2); flex-shrink: 0; height: 52px; padding: 0 var(--crisp-space-2) 0 var(--crisp-space-4); border-bottom: 1px solid var(--crisp-border-subtle); }
844
945
  & .crisp-dialog-header svg { flex-shrink: 0; color: var(--crisp-fg-muted); }
845
946
  & .crisp-dialog-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); margin: 0; }
846
947
  & .crisp-dialog-close { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 28px; height: 28px; margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-md); background: none; color: var(--crisp-fg-muted); font-family: inherit; cursor: pointer; }
847
948
  & .crisp-dialog-close:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: var(--crisp-fg-primary); }
949
+ /* The discard-confirm's own stacked title + sentence (crisp#677) — no
950
+ border, no close button, unlike the panel header above: measured
951
+ live, both themes, 18px/24px 600-weight title (no scale token
952
+ reaches 18px — the scale tops out at text.xl's 16px, so this is raw px per Golden Rule 3's "genuinely intrinsic size" exception, same as
953
+ this file's own submit-key glyphs) then a 14px/20px 500-weight sentence 4px below it. */
954
+ & .crisp-dialog-alert-header { display: flex; flex-direction: column; gap: var(--crisp-space-1); padding: var(--crisp-space-4) var(--crisp-space-4) 0; }
955
+ & .crisp-dialog-alert-title { margin: 0; font-size: 18px; line-height: 24px; font-weight: var(--crisp-weight-semibold); color: var(--crisp-fg-primary); }
956
+ & .crisp-dialog-alert-description { margin: 0; font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-alert-description); }
848
957
  & .crisp-dialog-body { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: var(--crisp-space-4); padding: var(--crisp-space-5) var(--crisp-space-4); }
849
958
  & .crisp-dialog-footer { display: flex; align-items: center; justify-content: flex-end; gap: var(--crisp-space-3); flex-shrink: 0; min-height: 56px; padding: 0 var(--crisp-space-4); border-top: 1px solid var(--crisp-border-subtle); }
959
+ & .crisp-dialog-footer[data-variant="alert"] { min-height: 45px; padding: var(--crisp-space-2); }
850
960
  & .crisp-dialog-field { display: flex; flex-direction: column; gap: var(--crisp-space-1-5); }
851
961
  & .crisp-dialog-field-label { font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 50%, transparent); }
852
962
  & .crisp-dialog-more { display: inline-flex; align-items: center; gap: var(--crisp-space-2); margin-inline-end: auto; border: none; background: none; padding: 0; font-family: inherit; font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); cursor: pointer; }
@@ -1747,7 +1857,7 @@
1747
1857
  & .crisp-tabletoolbar-sort-learn { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); font-size: var(--crisp-text-lg); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); text-decoration: none; }
1748
1858
  & .crisp-tabletoolbar-sort-learn svg { flex-shrink: 0; }
1749
1859
  & .crisp-tabletoolbar-sort-learn:hover { color: var(--crisp-fg-primary); }
1750
- & .crisp-tabletoolbar-menu-item { display: flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; height: var(--crisp-size-menuRow); margin: 0; padding: 0 var(--crisp-space-2); border: none; background: none; border-radius: var(--crisp-radius-md); font-family: inherit; font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); text-align: left; cursor: pointer; }
1860
+ & .crisp-tabletoolbar-menu-item { display: flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; height: var(--crisp-size-menuRow); margin: 0; padding: 0 var(--crisp-space-2); border: none; background: none; border-radius: var(--crisp-radius-md); font-family: inherit; font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); text-align: left; text-decoration: none; cursor: pointer; }
1751
1861
  & .crisp-tabletoolbar-menu-item:hover { background: var(--crisp-bg-raised); }
1752
1862
  & .crisp-tabletoolbar-menu-item:disabled { opacity: 0.4; cursor: default; }
1753
1863
  & .crisp-tabletoolbar-menu-item:disabled:hover { background: none; }
@@ -1755,12 +1865,30 @@
1755
1865
  & .crisp-tabletoolbar-menu-item[data-on] svg { color: var(--crisp-bg-brand-solid); }
1756
1866
  & .crisp-tabletoolbar-menu-item .crisp-tabletoolbar-menu-check { margin-left: auto; }
1757
1867
  & .crisp-tabletoolbar-menu-input { height: var(--crisp-size-row); margin: var(--crisp-space-0-5); padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); background: var(--crisp-bg-surface); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); outline: none; }
1758
- & .crisp-tabletoolbar-menu-sep { height: 1px; background: var(--crisp-border-subtle); margin: var(--crisp-space-0-5) 0; }
1868
+ & .crisp-tabletoolbar-menu-sep { height: 1px; border: none; background: var(--crisp-border-subtle); margin: var(--crisp-space-0-5) 0; }
1869
+ /* #592: the Filter field picker's nested-scope header (back control +
1870
+ search, once drilled into a nestedColumns column) and a search
1871
+ result's own breadcrumb row ("Team › Twitter follower count", surfaced by the SAME query matching a nested column). menu-back reuses the established 28px icon-only control pattern this file already uses twice (sort-remove, the kebab segment) rather than an independent measurement of Attio's own back arrow. The breadcrumb
1872
+ colours ARE measured live: the parent segment muted at the same
1873
+ exact color-mix 63% sort-learn above already uses, the
1874
+ separator chevron and leaf segment both full fgPrimary (the
1875
+ generic menu-item svg rule below defaults every icon to
1876
+ fgMuted, so the separator needs its own override to read
1877
+ correctly here). */
1878
+ & .crisp-tabletoolbar-menu-nesthead { display: flex; align-items: center; gap: var(--crisp-space-1); margin: var(--crisp-space-0-5); }
1879
+ & .crisp-tabletoolbar-menu-nesthead .crisp-tabletoolbar-menu-input { flex: 1; margin: 0; }
1880
+ & .crisp-tabletoolbar-menu-back { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; flex-shrink: 0; padding: 0; border: none; border-radius: var(--crisp-radius-md); background: none; color: var(--crisp-fg-muted); cursor: pointer; }
1881
+ & .crisp-tabletoolbar-menu-back:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
1882
+ & .crisp-tabletoolbar-menu-item--expand { justify-content: space-between; }
1883
+ & .crisp-tabletoolbar-menu-item-path { display: inline-flex; align-items: center; gap: 2px; min-width: 0; overflow: hidden; }
1884
+ & .crisp-tabletoolbar-menu-item-path-parent { flex-shrink: 0; overflow: hidden; text-overflow: ellipsis; max-width: 100px; color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
1885
+ & .crisp-tabletoolbar-menu-item-path-sep { flex-shrink: 0; color: var(--crisp-fg-primary); }
1886
+ & .crisp-tabletoolbar-menu-item-path > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1759
1887
  & .crisp-tabletoolbar-menu--up { top: auto; bottom: calc(100% + var(--crisp-space-1)); transform-origin: bottom; }
1760
1888
  & .crisp-tabletoolbar-menu--right { left: auto; right: 0; }
1761
1889
  & .crisp-tabletoolbar-menu--fixed { position: fixed; top: auto; left: auto; z-index: 40; }
1762
1890
  & .crisp-tabletoolbar-menu--hdr { min-width: 200px; }
1763
- & .crisp-tabletoolbar-menu--op { min-width: 134px; }
1891
+ & .crisp-tabletoolbar-menu--op { min-width: 0; }
1764
1892
  & .crisp-tabletoolbar-menu-item--danger { color: var(--crisp-bg-danger); }
1765
1893
  & .crisp-tabletoolbar-menu-item--danger svg { color: var(--crisp-bg-danger); }
1766
1894
  & .crisp-tabletoolbar-menu-item--danger:hover { background: color-mix(in srgb, var(--crisp-bg-danger) 10%, transparent); }
@@ -1776,6 +1904,32 @@
1776
1904
  & .crisp-tabletoolbar-count { font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
1777
1905
  & .crisp-tabletoolbar-new { display: inline-flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-row); padding: 0 var(--crisp-space-2-5); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-brand-solid); color: var(--crisp-fg-on-brand); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); cursor: pointer; transition: background-color .12s; }
1778
1906
  & .crisp-tabletoolbar-new:hover { background: var(--crisp-bg-brand-solid-pressed); }
1907
+
1908
+ & .crisp-tabletoolbar-fcond { display: inline-flex; align-items: center; height: var(--crisp-size-row); font-size: var(--crisp-text-md); color: var(--crisp-fg-primary); white-space: nowrap; }
1909
+ & .crisp-tabletoolbar-fcond-seg { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); height: 100%; margin: 0; padding: 0 var(--crisp-space-2); border: none; background: var(--crisp-bg-surface-muted); box-shadow: inset 0 0 0 1px var(--crisp-border-divider); font-family: inherit; font-size: inherit; color: inherit; cursor: pointer; }
1910
+ & .crisp-tabletoolbar-fcond-seg svg { flex-shrink: 0; }
1911
+ & .crisp-tabletoolbar-fcond[data-complete] .crisp-tabletoolbar-fcond-seg--field,
1912
+ & .crisp-tabletoolbar-fcond[data-complete] .crisp-tabletoolbar-fcond-seg--val { background: var(--crisp-bg-surface); }
1913
+ & .crisp-tabletoolbar-fcond-seg--field { border-radius: var(--crisp-radius-md) 0 0 var(--crisp-radius-md); font-weight: var(--crisp-weight-medium); max-width: 200px; }
1914
+ & .crisp-tabletoolbar-fcond-seg--field svg { color: var(--crisp-fg-muted); }
1915
+ /* #592: a nested pick's field segment ("Team › Twitter follower
1916
+ count") — measured live: BOTH path segments the same colour (chip context only; the picker's own menu rows keep the parent muted, see
1917
+ tabletoolbar.chrome.ts), each independently truncated. Attio's own layout gives the leaf segment priority (it stays fully readable longer, the parent truncates first) via a two-part min-content measurement this reproduces qualitatively with a flex-shrink differential rather than byte-for-byte (the exact px split Attio computes is content-length-dependent, not a fixed value) — not independently re-verified at other path-segment lengths. */
1918
+ & .crisp-tabletoolbar-fcond-path { display: inline-flex; align-items: center; gap: 2px; min-width: 0; max-width: 178px; }
1919
+ & .crisp-tabletoolbar-fcond-path-seg { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; flex: 1 1 auto; }
1920
+ & .crisp-tabletoolbar-fcond-path-seg:first-child { flex: 0 1 auto; }
1921
+ & .crisp-tabletoolbar-fcond-path-sep { flex-shrink: 0; color: inherit; }
1922
+ & .crisp-tabletoolbar-fcond-seg--op { background: var(--crisp-bg-surface); color: var(--crisp-fg-muted); }
1923
+ & .crisp-tabletoolbar-fcond-seg--val { font-weight: var(--crisp-weight-medium); max-width: 140px; overflow: hidden; text-overflow: ellipsis; }
1924
+ & .crisp-tabletoolbar-fcond-seg--kebab { background: var(--crisp-bg-surface); justify-content: center; width: 28px; padding: 0; border-radius: 0 var(--crisp-radius-md) var(--crisp-radius-md) 0; color: var(--crisp-fg-muted); }
1925
+ & .crisp-tabletoolbar-fcond-seg--kebab:hover { color: var(--crisp-fg-primary); }
1926
+ & .crisp-tabletoolbar-menu--val { min-width: 134px; }
1927
+ & .crisp-tabletoolbar-datepop { width: 300px; }
1928
+ & .crisp-tabletoolbar-datepop-presets { display: flex; flex-wrap: wrap; gap: var(--crisp-space-1); margin-top: var(--crisp-space-2); padding: 0 var(--crisp-space-1); }
1929
+ & .crisp-tabletoolbar-datepop-preset { height: var(--crisp-size-chip); padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-chip); background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); font-family: inherit; font-size: var(--crisp-text-sm); cursor: pointer; }
1930
+ & .crisp-tabletoolbar-datepop-preset:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 8%, transparent); }
1931
+ & .crisp-tabletoolbar-tool--raised { border: none; background: var(--crisp-bg-surface-muted); box-shadow: inset 0 0 0 1px var(--crisp-ring-neutral), 0 0 2px rgba(28,40,64,0.18), 0 1px 3px rgba(0,0,0,0.04); font-family: inherit; cursor: pointer; }
1932
+ [dir="rtl"] & [data-rtl-flip] { transform: scaleX(-1); }
1779
1933
  }
1780
1934
 
1781
1935
  @media (prefers-reduced-motion: reduce) {
@@ -1796,7 +1950,7 @@
1796
1950
  & .crisp-tabs-tab:hover:not([data-selected]) { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
1797
1951
  & .crisp-tabs-tab[data-selected] { color: var(--crisp-fg-primary); background: var(--crisp-bg-subtle); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); }
1798
1952
  & .crisp-tabs-tab:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: 1px; }
1799
- & .crisp-tabs-tab-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 16px; height: 16px; padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); font-size: var(--crisp-text-xs); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); }
1953
+ & .crisp-tabs-tab-badge { display: inline-flex; align-items: center; justify-content: center; min-width: 14px; height: 14px; padding: 0 var(--crisp-space-1); border-radius: 4px; background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); font-size: 10px; line-height: 14px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-neutral-soft); }
1800
1954
  & .crisp-tabs-tab[data-selected] .crisp-tabs-tab-badge { color: var(--crisp-fg-primary); }
1801
1955
  & .crisp-tabs-indicator { position: absolute; bottom: 0; left: 0; z-index: 1; height: 1px; background: var(--crisp-fg-primary); border-radius: 2px 2px 0 0; transition: transform .2s cubic-bezier(.4,0,.2,1), width .2s cubic-bezier(.4,0,.2,1); pointer-events: none; }
1802
1956
  & .crisp-tabs-panel { display: flex; flex-direction: column; flex: 1; min-height: 0; padding-top: var(--crisp-space-8); }
@@ -1896,7 +2050,7 @@
1896
2050
  }
1897
2051
 
1898
2052
  .crisp-tooltip {
1899
- position: fixed; z-index: 70; box-sizing: border-box; pointer-events: none; max-width: 260px; padding: var(--crisp-space-1-5) var(--crisp-space-2); border-radius: var(--crisp-radius-md); background: #101112; color: #eeeff1; font-family: var(--crisp-font-sans); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); line-height: 1.45; box-shadow: 0 4px 14px -2px rgba(28,40,64,0.28), 0 0 0 1px rgba(255,255,255,0.04); opacity: 1; transition: opacity 0.12s ease;
2053
+ position: fixed; z-index: 70; box-sizing: border-box; pointer-events: none; max-width: 260px; padding: var(--crisp-space-1-5) var(--crisp-space-2); border-radius: var(--crisp-radius-md); background: #101112; color: #eeeff1; font-family: var(--crisp-font-sans); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); line-height: 1.45; box-shadow: rgb(33,34,36) 0 0 0 1px inset, rgba(0,0,0,0.04) 0 0 2px 0, rgba(0,0,0,0.12) 0 1px 3px 0; opacity: 1; transition: opacity 0.12s ease;
1900
2054
  @starting-style { opacity: 0; }
1901
2055
  & .crisp-tooltip-reference-row { display: inline-flex; align-items: center; gap: var(--crisp-space-1); }
1902
2056
  }
@@ -1982,7 +2136,11 @@
1982
2136
  &[data-single-pane] { justify-content: center; }
1983
2137
  & .crisp-authshell-progress { position: absolute; top: 0; left: 0; width: 100%; height: 2px; background: var(--crisp-border-default); }
1984
2138
  & .crisp-authshell-progress-fill { height: 2px; background: var(--crisp-fg-primary); border-radius: 4px; transition: width .2s; }
1985
- & .crisp-authshell-logo { margin-top: var(--crisp-space-8); height: 24px; display: flex; align-items: center; }
2139
+ & .crisp-authshell-logo { margin-top: var(--crisp-space-8); min-height: 24px; display: flex; align-items: center; gap: var(--crisp-space-2-5); }
2140
+ & .crisp-authshell-logo-badge { flex-shrink: 0; box-sizing: border-box; width: 32px; height: 32px; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-brand-solid); color: #fff; display: flex; align-items: center; justify-content: center; }
2141
+ & .crisp-authshell-logo-text { display: flex; flex-direction: column; line-height: 1.2; }
2142
+ & .crisp-authshell-logo-title { font-size: var(--crisp-text-xl); font-weight: var(--crisp-weight-bold); letter-spacing: -0.2px; color: var(--crisp-fg-primary); }
2143
+ & .crisp-authshell-logo-subtitle { font-size: var(--crisp-text-sm); color: var(--crisp-fg-muted); }
1986
2144
  & .crisp-authshell-card { margin-top: var(--crisp-space-6); display: flex; height: 692px; border-radius: 20px; border: 1px solid var(--crisp-border-subtle); overflow: hidden; }
1987
2145
  &[data-single-pane] .crisp-authshell-card { height: auto; border: none; border-radius: 0; overflow: visible; }
1988
2146
  & .crisp-authshell-pane { flex: 0 0 564px; padding: 72px; display: flex; flex-direction: column; box-sizing: border-box; }
@@ -2025,23 +2183,49 @@
2025
2183
  }
2026
2184
 
2027
2185
  .crisp-board {
2028
- display: flex; gap: var(--crisp-space-2); height: 100%; box-sizing: border-box; padding: var(--crisp-space-3); overflow-x: auto; align-items: flex-start; font-family: var(--crisp-font-sans);
2029
- & .crisp-board-col { display: flex; flex-direction: column; width: 268px; flex-shrink: 0; max-height: 100%; box-sizing: border-box; }
2186
+ display: flex; flex-direction: column; gap: var(--crisp-space-2); height: 100%; box-sizing: border-box; font-family: var(--crisp-font-sans); /* #718: the density toolbar's own row -- everything that used to be on
2187
+ the root (padding/overflow/the columns themselves) moves to
2188
+ .crisp-board-row below so this can sit above it, unscrolled. */
2189
+ & .crisp-board-toolbar { display: flex; align-items: center; justify-content: flex-end; gap: var(--crisp-space-2); padding: 0 var(--crisp-space-3); flex-shrink: 0; }
2190
+ /* Measured live (Deals pipeline): a 54×28px group, 24×24px buttons,
2191
+ 2px padding/gap -- an 8px gap between the two groups comes from the
2192
+ toolbar's own `gap` above. */
2193
+ & .crisp-board-density { display: flex; align-items: center; gap: 2px; padding: 2px; border-radius: 10px; background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); }
2194
+ & .crisp-board-density button { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; padding: 0; border: none; border-radius: 8px; background: none; color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); font-family: inherit; cursor: pointer; }
2195
+ & .crisp-board-density button[data-state="on"] { background: var(--crisp-bg-surface); color: var(--crisp-fg-primary); }
2196
+ & .crisp-board-row { display: flex; gap: var(--crisp-space-2); flex: 1; min-height: 0; box-sizing: border-box; padding: var(--crisp-space-3); overflow-x: auto; align-items: flex-start; }
2197
+ & .crisp-board-col { display: flex; flex-direction: column; width: 268px; flex-shrink: 0; max-height: 100%; box-sizing: border-box; transition: width 0.15s; }
2198
+ /* #718: a collapsed column is a 52px vertical rail -- header only (dot + count stacked above a vertical-rl title), body/footer hidden entirely. Measured live: 52px width, 8px 0 padding on the header, 14px/600 title text. */
2199
+ & .crisp-board-col[data-density="collapse"] { width: 52px; }
2200
+ & .crisp-board-col[data-density="collapse"] .crisp-board-col-header { flex-direction: column; align-items: center; height: auto; padding: var(--crisp-space-2) 0; gap: var(--crisp-space-1); }
2201
+ /* Measured stacking order (top to bottom): dot, count, THEN the vertical title -- reordered visually via `order`, not by changing the JSX/DOM order, which stays dot/title/count (reading order, unaffected by density). */
2202
+ & .crisp-board-col[data-density="collapse"] .crisp-board-col-title { order: 1; writing-mode: vertical-rl; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-semibold); }
2203
+ & .crisp-board-col[data-density="collapse"] .crisp-board-col-count { order: 0; }
2204
+ & .crisp-board-col[data-density="collapse"] .crisp-board-col-body, & .crisp-board-col[data-density="collapse"] .crisp-board-col-footer { display: none; }
2030
2205
  & .crisp-board-col-header { display: flex; align-items: center; gap: var(--crisp-space-2); height: 36px; padding: 0 var(--crisp-space-1); flex-shrink: 0; }
2031
2206
  & .crisp-board-col-dot { width: 12px; height: 12px; border-radius: var(--crisp-radius-full); flex-shrink: 0; }
2032
2207
  & .crisp-board-col-title { font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-semibold); color: var(--crisp-fg-primary); }
2033
- & .crisp-board-col-count { display: inline-flex; align-items: center; justify-content: center; min-width: 16px; height: 16px; padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
2208
+ /* crisp#697: HANDOFF's #472 note claimed the count pill's color-mix(fgPrimary, 63%, transparent) reproduced the measured rgb(80,81,85) -- it doesn't; 63% of fgPrimary composited on white
2209
+ paints rgb(104,105,105), visibly lighter. Text now reads
2210
+ fgNeutralSoft directly (the same rgb(80,81,85) token Badge's neutral tone uses, #696), and the ring the pill was also missing (inset 0 0 0 1px borderSubtle, measured rgb(238,239,241)) is added. */
2211
+ & .crisp-board-col-count { display: inline-flex; align-items: center; justify-content: center; min-width: 16px; height: 16px; padding: 0 var(--crisp-space-1); border-radius: var(--crisp-radius-sm); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); font-size: var(--crisp-text-sm); line-height: 16px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-neutral-soft); }
2034
2212
  & .crisp-board-col-add { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; margin-inline-start: auto; padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; color: var(--crisp-fg-muted); font-family: inherit; cursor: pointer; }
2035
2213
  & .crisp-board-col-add:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 6%, transparent); color: var(--crisp-fg-primary); }
2036
2214
  & .crisp-board-col-body { display: flex; flex-direction: column; gap: var(--crisp-space-2); flex: 1; min-height: 48px; padding: var(--crisp-space-1); border-radius: var(--crisp-radius-lg); overflow-y: auto; transition: background 0.12s; }
2037
2215
  & .crisp-board-col[data-over] .crisp-board-col-body { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 7%, transparent); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--crisp-bg-brand-solid) 30%, transparent); }
2038
2216
  & .crisp-board-newcard { display: flex; align-items: center; justify-content: center; gap: var(--crisp-space-1-5); height: 32px; flex-shrink: 0; border: none; border-radius: var(--crisp-radius-md); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-muted); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); cursor: pointer; }
2039
2217
  & .crisp-board-newcard:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 7%, transparent); color: var(--crisp-fg-primary); }
2040
- & .crisp-board-card { display: flex; flex-direction: column; gap: var(--crisp-space-2); box-sizing: border-box; padding: var(--crisp-space-2-5) var(--crisp-space-3); border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); cursor: grab; }
2218
+ & .crisp-board-card { display: flex; flex-direction: column; gap: var(--crisp-space-2); box-sizing: border-box; padding: var(--crisp-space-2-5) var(--crisp-space-3); border-radius: var(--crisp-radius-xl); background: var(--crisp-bg-surface); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); cursor: grab; touch-action: none; }
2219
+ /* #718: a collapsed card is a single 40px row -- measured live and independent of `c.size.tableRow`/`tableHeader` (36/40px) even though it happens to match the latter; a plain literal, not a cross-reference to a token measured for a different surface. `renderCard`'s own second argument is what actually decides what a
2220
+ collapsed card SHOWS (Board never sees inside its opaque output);
2221
+ this only supplies the row-shaped box that content sits in. */
2222
+ & .crisp-board-card[data-density="collapse"] { flex-direction: row; align-items: center; height: 40px; padding: 0 var(--crisp-space-3); }
2041
2223
  & .crisp-board-card:active { cursor: grabbing; }
2042
2224
  & .crisp-board-card[data-dragging] { opacity: 0.4; }
2043
2225
  & .crisp-board-card:focus { outline: none; }
2044
2226
  & .crisp-board-card:focus-visible { outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: 1px; }
2227
+ /* #674: the pointer-events drag's own drag image -- the same card surface above, just pinned to the pointer instead of the flex flow. No shadow lift or scale: neither was measured against Attio, and this is the same card, momentarily somewhere else, not a new look. */
2228
+ & .crisp-board-ghost { position: fixed; z-index: 1; pointer-events: none; cursor: grabbing; }
2045
2229
  /* #604: a per-column calc footer, mirroring the table's own (measured
2046
2230
  live, Deals pipeline). Sits below the scrollable card body, not
2047
2231
  inside it or pinned to it -- Attio's own is position: static, not sticky, so it's a plain flex-shrink:0 sibling here too. */
@@ -2051,11 +2235,13 @@
2051
2235
  & .crisp-board-col-calc[data-empty] { opacity: 0.7; }
2052
2236
  & .crisp-board-col-calc-label { text-transform: capitalize; }
2053
2237
  & .crisp-board-col-calc-value { color: var(--crisp-fg-primary); font-variant-numeric: tabular-nums; }
2238
+ /* #718: "Add stage" -- measured live: 52×52px, transparent, a 1px
2239
+ dashed border (rgb(230,231,234), exactly `borderDropzone`/`line150`
2240
+ -- the same dashed-border colour ImportDialog's own dropzone already uses, not borderSubtle/line100, a close but different value), radius.xl (16px), a centered 16px "+". */
2241
+ & .crisp-board-addstage { display: inline-flex; align-items: center; justify-content: center; width: 52px; height: 52px; flex-shrink: 0; border: 1px dashed var(--crisp-border-dropzone); border-radius: var(--crisp-radius-xl); background: none; color: var(--crisp-fg-primary); font-family: inherit; cursor: pointer; }
2242
+ & .crisp-board-addstage:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); }
2054
2243
  /* announced, never drawn */
2055
- & .crisp-board-live {
2056
- position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
2057
- overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
2058
- }
2244
+ & .crisp-board-live { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
2059
2245
  }
2060
2246
 
2061
2247
  @media (prefers-reduced-motion: reduce) {
@@ -2121,11 +2307,21 @@
2121
2307
 
2122
2308
  /* Row badge (CommentCountBadge) -- lives in its own class family so the
2123
2309
  grid lanes can drop it into a cell without pulling in the panel's
2124
- CSS. Persistent state: a 6px grey dot. Hover state: a 12px comment
2125
- icon + 12px count text, both at 55% ink, replacing the dot. */
2126
- .crisp-comments-badge { box-sizing: border-box; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; display: inline-flex; align-items: center; gap: var(--crisp-space-1); border: none; background: none; padding: 0; margin: 0; cursor: pointer; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); }
2310
+ CSS. Re-measured live on a real commented row (crisp/#601
2311
+ supersession, right-aligned in a DataTable primary column): a 6px
2312
+ grey dot at rest (unchanged, already matched); on hover, the trigger
2313
+ button is auto-width with a 12px icon + 12px/500 count text,
2314
+ "padding: 2px 6px 2px 4px", radius 6px -- distinct from the preview
2315
+ button beside it, a fixed 20px square with a 16px icon, same radius.
2316
+ Both share the 55%-ink idle colour and darken on their OWN hover
2317
+ (color-mix 4%, the same formula DataTable's own #601 icons use). */
2318
+ .crisp-comments-badge { box-sizing: border-box; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); }
2127
2319
  .crisp-comments-badge-dot { box-sizing: border-box; width: 6px; height: 6px; border-radius: var(--crisp-radius-full); background: color-mix(in srgb, var(--crisp-fg-primary) 38%, transparent); }
2128
- .crisp-comments-badge-count { font-size: 12px; line-height: 16px; }
2320
+ .crisp-comments-badge-trigger { box-sizing: border-box; display: inline-flex; align-items: center; gap: var(--crisp-space-1); height: 20px; margin: 0; padding: 2px var(--crisp-space-1-5) 2px var(--crisp-space-1); border: none; border-radius: var(--crisp-radius-sm); background: none; font-family: inherit; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); cursor: pointer; }
2321
+ .crisp-comments-badge-trigger:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
2322
+ .crisp-comments-badge-preview { box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; font-family: inherit; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); cursor: pointer; flex-shrink: 0; }
2323
+ .crisp-comments-badge-preview:hover { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); color: var(--crisp-fg-primary); }
2324
+ .crisp-comments-badge-count { font-size: 12px; line-height: 16px; font-weight: var(--crisp-weight-medium); }
2129
2325
  [dir="rtl"] .crisp-comments-badge [data-rtl-flip] { transform: scaleX(-1); }
2130
2326
 
2131
2327
  .crisp-comments {
@@ -2139,31 +2335,50 @@
2139
2335
  & .crisp-comments-breadcrumb { display: flex; align-items: center; gap: var(--crisp-space-1); font-size: 12px; font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2140
2336
  & .crisp-comments-body { flex: 1; min-height: 0; overflow-y: auto; padding: 0 var(--crisp-space-4); display: flex; flex-direction: column; gap: var(--crisp-space-4); }
2141
2337
  & .crisp-comments-body--pad-top { padding-top: var(--crisp-space-3); }
2142
- /* One comment row -- avatar 16px (Avatar size="sm"), 6px gap to the name/time line, 14px/600 name + 12px/500 63%-muted time, 14px/400 body two lines below. Hover actions fade in flush with the name line. */
2338
+ /* One comment row -- avatar 16px (Avatar size="sm"), 6px gap to the name/time line, 14px/600 name + 12px/500 63%-muted time, 14px/400 body two lines below. Hover actions fade in flush with the name line. .crisp-comments-row-head's own height: 20px (above) is a single
2339
+ text-line's worth, on the same assumption .crisp-comments-title/ .crisp-comments-breadcrumb already act on higher up in this same recipe -- a name that doesn't fit wraps to two lines with nothing
2340
+ reserving room for the second one, overflowing straight into the
2341
+ body text underneath (found live: this app's own dev-bypass author name, an email long enough to wrap, sat on top of the comment's own
2342
+ body text). .crisp-comments-author gets the identical nowrap-
2343
+ ellipsis treatment those two already have, plus min-width: 0 so a
2344
+ flex item is actually allowed to shrink below its own text's natural width in the first place; .crisp-comments-time gets flex-shrink: 0 so it's the name that gives, never the timestamp -- the same "one
2345
+ side shrinks, the other one doesn't" split PersonChip's own name/
2346
+ avatar pair already uses (crisp#668/#671). */
2143
2347
  & .crisp-comments-row { position: relative; display: flex; gap: var(--crisp-space-1-5); }
2144
2348
  & .crisp-comments-row-main { flex: 1; min-width: 0; }
2145
2349
  & .crisp-comments-row-head { display: flex; align-items: baseline; gap: var(--crisp-space-1-5); height: 20px; }
2146
- & .crisp-comments-author { font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-semibold); color: var(--crisp-fg-primary); }
2147
- & .crisp-comments-time { font-size: 12px; font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); white-space: nowrap; }
2350
+ & .crisp-comments-author { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-semibold); color: var(--crisp-fg-primary); }
2351
+ & .crisp-comments-time { flex-shrink: 0; font-size: 12px; font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); white-space: nowrap; }
2148
2352
  & .crisp-comments-resolved-label { display: flex; align-items: center; gap: var(--crisp-space-1); margin-bottom: var(--crisp-space-1); font-size: 12px; font-weight: var(--crisp-weight-medium); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
2149
2353
  & .crisp-comments-body-text { font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-regular); color: var(--crisp-fg-primary); white-space: pre-wrap; word-break: break-word; }
2150
- /* Hover actions -- measured: 4 buttons, 28x28, radius 8, flush side-by-side (0 gap), transparent by default, 16px icon at 55% ink. */
2354
+
2355
+ /* Hover actions -- measured: 4 buttons, 28x28, radius 8, flush
2356
+ side-by-side (0 gap), transparent by default, 16px icon at 55% ink. */
2151
2357
  & .crisp-comments-row-actions { position: absolute; top: 0; inset-inline-end: 0; display: flex; opacity: 0; pointer-events: none; background: var(--crisp-bg-surface); }
2152
- & .crisp-comments-row:hover .crisp-comments-row-actions, & .crisp-comments-row-actions:focus-within { opacity: 1; pointer-events: auto; }
2358
+ & .crisp-comments-row:hover .crisp-comments-row-actions,
2359
+ & .crisp-comments-row-actions:focus-within { opacity: 1; pointer-events: auto; }
2153
2360
  & .crisp-comments-action { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-md); background: transparent; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); font-family: inherit; cursor: pointer; transition: background-color .12s, color .12s; }
2154
2361
  & .crisp-comments-action:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
2155
2362
  & .crisp-comments-action[data-active] { color: var(--crisp-bg-brand-solid); }
2156
- /* Reactions -- measured: 28px pill, radius 8, subtle 4% ink fill, padding 4px 8px 4px 6px, gap 4px between the emoji glyph and count. */
2363
+
2364
+ /* Reactions -- measured: 28px pill, radius 8, subtle 4% ink fill,
2365
+ padding 4px 8px 4px 6px, gap 4px between the emoji glyph and count. */
2157
2366
  & .crisp-comments-reactions { display: flex; flex-wrap: wrap; gap: var(--crisp-space-1); margin-top: var(--crisp-space-1-5); }
2158
2367
  & .crisp-comments-reaction { display: inline-flex; align-items: center; gap: var(--crisp-space-1); height: 28px; padding: 4px 8px 4px 6px; border: none; border-radius: var(--crisp-radius-md); background: color-mix(in srgb, var(--crisp-fg-primary) 4%, transparent); font-family: inherit; font-size: 12px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); cursor: pointer; }
2159
2368
  & .crisp-comments-reaction[data-reacted] { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 12%, transparent); color: var(--crisp-bg-brand-solid); }
2160
2369
  & .crisp-comments-reaction-add { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border: none; border-radius: var(--crisp-radius-md); background: transparent; font-family: inherit; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); cursor: pointer; opacity: 0; transition: opacity .12s, background-color .12s; }
2161
2370
  & .crisp-comments-row:hover .crisp-comments-reaction-add { opacity: 1; }
2162
2371
  & .crisp-comments-reaction-add:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
2372
+
2163
2373
  /* Reply-in-thread view -- an indented block of replies below the root. */
2164
2374
  & .crisp-comments-replies { display: flex; flex-direction: column; gap: var(--crisp-space-4); margin-top: var(--crisp-space-3); padding-inline-start: var(--crisp-space-4); border-inline-start: 1px solid var(--crisp-border-subtle); }
2375
+
2165
2376
  & .crisp-comments-empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: var(--crisp-space-1); padding: var(--crisp-space-6) var(--crisp-space-4); text-align: center; color: color-mix(in srgb, var(--crisp-fg-primary) 55%, transparent); font-size: var(--crisp-text-lg); }
2166
- /* Composer -- measured: 14px/20px body text, 16px gap above the reply/mention icon row, Comment button 84x32 padding 6px 10px radius 9 (= radius.lg) brand-solid fill, 14px/500 white label, opacity .4 disabled / 1 enabled (no color/background change between the two). */
2377
+
2378
+ /* Composer -- measured: 14px/20px body text, 16px gap above the
2379
+ reply/mention icon row, Comment button 84x32 padding 6px 10px radius
2380
+ 9 (= radius.lg) brand-solid fill, 14px/500 white label, opacity .4
2381
+ disabled / 1 enabled (no color/background change between the two). */
2167
2382
  & .crisp-comments-composer { flex-shrink: 0; padding: var(--crisp-space-3) var(--crisp-space-4) var(--crisp-space-4); border-top: 1px solid var(--crisp-border-subtle); }
2168
2383
  & .crisp-comments-field { min-height: 20px; max-height: 120px; overflow-y: auto; font-family: inherit; font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-regular); color: var(--crisp-fg-primary); outline: none; white-space: pre-wrap; word-break: break-word; }
2169
2384
  & .crisp-comments-field:focus-visible { outline: 2px solid var(--crisp-stroke-focus-ring); outline-offset: 2px; }
@@ -2173,15 +2388,22 @@
2173
2388
  & .crisp-comments-tool { display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: none; color: color-mix(in srgb, var(--crisp-fg-primary) 45%, transparent); font-family: inherit; cursor: pointer; }
2174
2389
  & .crisp-comments-tool:hover { color: var(--crisp-fg-primary); }
2175
2390
  /* Same fill/shadow as Button's own intent="primary" (crisp/button.recipe.ts) --
2176
- confirmed byte-identical against the live "Comment" button's computed box-shadow, so this reuses that literal rather than re-deriving it. 32px/radius.lg/6px-10px padding has no matching Button `size` yet (24/28/40 are the three that exist), so this stays hand-rolled instead of stretching one of those. */
2391
+ confirmed byte-identical against the live "Comment" button's computed
2392
+ box-shadow, so this reuses that literal rather than re-deriving it.
2393
+ 32px/radius.lg/6px-10px padding has no matching Button `size` yet
2394
+ (24/28/40 are the three that exist), so this stays hand-rolled instead
2395
+ of stretching one of those. */
2177
2396
  & .crisp-comments-post { display: inline-flex; align-items: center; justify-content: center; height: 32px; padding: 6px var(--crisp-space-2-5); border: none; border-radius: var(--crisp-radius-lg); background: var(--crisp-bg-brand-solid); box-shadow: inset 0 0 0 1px rgba(0,0,0,0.06), 0 2px 4px -2px rgba(38,109,240,0.12), 0 3px 6px -2px rgba(38,109,240,0.08); font-family: inherit; font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-semibold); color: var(--crisp-fg-on-brand); cursor: pointer; opacity: 1; transition: opacity .12s, background-color .12s; }
2178
2397
  & .crisp-comments-post:hover:not(:disabled) { background: var(--crisp-bg-brand-solid-pressed); }
2179
2398
  & .crisp-comments-post:disabled { opacity: .4; cursor: default; }
2180
- /* Filter menu -- measured: 220px, radius 12 (radius.xl), 4px padding, elevation.floating -- a plain reuse of crisp's own Menu, so no rules
2399
+
2400
+ /* Filter menu -- measured: 220px, radius 12 (radius.xl), 4px padding,
2401
+ elevation.floating -- a plain reuse of crisp's own Menu, so no rules
2181
2402
  live here; kept as a comment for the record. */
2182
2403
 
2183
2404
  /* An inserted "@mention" chip in the composer -- same shape as
2184
- RichTextEditor's own `.crisp-rte-mention` (crisp#428), reused here rather than re-derived since it's the same atomic-chip affordance. */
2405
+ RichTextEditor's own `.crisp-rte-mention` (crisp#428), reused here
2406
+ rather than re-derived since it's the same atomic-chip affordance. */
2185
2407
  & .crisp-comments-mention { display: inline; padding: 0 2px; border-radius: 4px; background: color-mix(in srgb, var(--crisp-bg-brand-solid) 12%, transparent); color: var(--crisp-bg-brand-solid); font-weight: var(--crisp-weight-medium); user-select: none; }
2186
2408
  [dir="rtl"] & [data-rtl-flip] { transform: scaleX(-1); }
2187
2409
  }
@@ -2356,6 +2578,13 @@
2356
2578
  }
2357
2579
  }
2358
2580
 
2581
+
2582
+ .crisp-navitem-shortcut { display: flex; align-items: baseline; justify-content: space-between; gap: var(--crisp-space-3); }
2583
+ .crisp-navitem-shortcut-desc { font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); line-height: 16px; white-space: nowrap; }
2584
+ .crisp-navitem-shortcut-chord { display: inline-flex; align-items: center; gap: var(--crisp-space-1); flex-shrink: 0; }
2585
+ .crisp-navitem-shortcut-then { font-size: var(--crisp-text-xs); font-weight: var(--crisp-weight-medium); color: #898a8d; }
2586
+ .crisp-navitem-shortcut-chord .crisp-kbd { text-transform: uppercase; font-weight: var(--crisp-weight-regular); color: #a2a4a7; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.2); }
2587
+
2359
2588
  .crisp-navitem {
2360
2589
  box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-1-5); height: 28px; padding-inline: var(--crisp-space-2); padding-inline-end: var(--crisp-space-1); border-radius: var(--crisp-radius-lg); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-medium); letter-spacing: -0.14px; line-height: 20px; color: var(--crisp-fg-primary); text-decoration: none; cursor: pointer; user-select: none; width: 100%; transition: background-color 0.16s;
2361
2590
  & .crisp-navitem-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@@ -2755,7 +2984,7 @@
2755
2984
  & .crisp-recordstable-sort-learn { display: inline-flex; align-items: center; gap: var(--crisp-space-1-5); font-size: var(--crisp-text-lg); color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); text-decoration: none; }
2756
2985
  & .crisp-recordstable-sort-learn svg { flex-shrink: 0; }
2757
2986
  & .crisp-recordstable-sort-learn:hover { color: var(--crisp-fg-primary); }
2758
- & .crisp-recordstable-menu-item { display: flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; height: var(--crisp-size-menuRow); margin: 0; padding: 0 var(--crisp-space-2); border: none; background: none; border-radius: var(--crisp-radius-md); font-family: inherit; font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); text-align: left; cursor: pointer; }
2987
+ & .crisp-recordstable-menu-item { display: flex; align-items: center; gap: var(--crisp-space-1-5); width: 100%; height: var(--crisp-size-menuRow); margin: 0; padding: 0 var(--crisp-space-2); border: none; background: none; border-radius: var(--crisp-radius-md); font-family: inherit; font-size: var(--crisp-text-lg); line-height: 20px; font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-primary); text-align: left; text-decoration: none; cursor: pointer; }
2759
2988
  & .crisp-recordstable-menu-item:hover { background: var(--crisp-bg-raised); }
2760
2989
  & .crisp-recordstable-menu-item:disabled { opacity: 0.4; cursor: default; }
2761
2990
  & .crisp-recordstable-menu-item:disabled:hover { background: none; }
@@ -2763,12 +2992,30 @@
2763
2992
  & .crisp-recordstable-menu-item[data-on] svg { color: var(--crisp-bg-brand-solid); }
2764
2993
  & .crisp-recordstable-menu-item .crisp-recordstable-menu-check { margin-left: auto; }
2765
2994
  & .crisp-recordstable-menu-input { height: var(--crisp-size-row); margin: var(--crisp-space-0-5); padding: 0 var(--crisp-space-2); border: none; border-radius: var(--crisp-radius-md); box-shadow: inset 0 0 0 1px var(--crisp-border-subtle); background: var(--crisp-bg-surface); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); outline: none; }
2766
- & .crisp-recordstable-menu-sep { height: 1px; background: var(--crisp-border-subtle); margin: var(--crisp-space-0-5) 0; }
2995
+ & .crisp-recordstable-menu-sep { height: 1px; border: none; background: var(--crisp-border-subtle); margin: var(--crisp-space-0-5) 0; }
2996
+ /* #592: the Filter field picker's nested-scope header (back control +
2997
+ search, once drilled into a nestedColumns column) and a search
2998
+ result's own breadcrumb row ("Team › Twitter follower count", surfaced by the SAME query matching a nested column). menu-back reuses the established 28px icon-only control pattern this file already uses twice (sort-remove, the kebab segment) rather than an independent measurement of Attio's own back arrow. The breadcrumb
2999
+ colours ARE measured live: the parent segment muted at the same
3000
+ exact color-mix 63% sort-learn above already uses, the
3001
+ separator chevron and leaf segment both full fgPrimary (the
3002
+ generic menu-item svg rule below defaults every icon to
3003
+ fgMuted, so the separator needs its own override to read
3004
+ correctly here). */
3005
+ & .crisp-recordstable-menu-nesthead { display: flex; align-items: center; gap: var(--crisp-space-1); margin: var(--crisp-space-0-5); }
3006
+ & .crisp-recordstable-menu-nesthead .crisp-recordstable-menu-input { flex: 1; margin: 0; }
3007
+ & .crisp-recordstable-menu-back { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; flex-shrink: 0; padding: 0; border: none; border-radius: var(--crisp-radius-md); background: none; color: var(--crisp-fg-muted); cursor: pointer; }
3008
+ & .crisp-recordstable-menu-back:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
3009
+ & .crisp-recordstable-menu-item--expand { justify-content: space-between; }
3010
+ & .crisp-recordstable-menu-item-path { display: inline-flex; align-items: center; gap: 2px; min-width: 0; overflow: hidden; }
3011
+ & .crisp-recordstable-menu-item-path-parent { flex-shrink: 0; overflow: hidden; text-overflow: ellipsis; max-width: 100px; color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); }
3012
+ & .crisp-recordstable-menu-item-path-sep { flex-shrink: 0; color: var(--crisp-fg-primary); }
3013
+ & .crisp-recordstable-menu-item-path > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2767
3014
  & .crisp-recordstable-menu--up { top: auto; bottom: calc(100% + var(--crisp-space-1)); transform-origin: bottom; }
2768
3015
  & .crisp-recordstable-menu--right { left: auto; right: 0; }
2769
3016
  & .crisp-recordstable-menu--fixed { position: fixed; top: auto; left: auto; z-index: 40; }
2770
3017
  & .crisp-recordstable-menu--hdr { min-width: 200px; }
2771
- & .crisp-recordstable-menu--op { min-width: 134px; }
3018
+ & .crisp-recordstable-menu--op { min-width: 0; }
2772
3019
  & .crisp-recordstable-menu-item--danger { color: var(--crisp-bg-danger); }
2773
3020
  & .crisp-recordstable-menu-item--danger svg { color: var(--crisp-bg-danger); }
2774
3021
  & .crisp-recordstable-menu-item--danger:hover { background: color-mix(in srgb, var(--crisp-bg-danger) 10%, transparent); }
@@ -2784,6 +3031,7 @@
2784
3031
  & .crisp-recordstable-count { font-size: var(--crisp-text-md); color: var(--crisp-fg-muted); }
2785
3032
  & .crisp-recordstable-new { display: inline-flex; align-items: center; gap: var(--crisp-space-1); height: var(--crisp-size-row); padding: 0 var(--crisp-space-2-5); border: none; border-radius: var(--crisp-radius-md); background: var(--crisp-bg-brand-solid); color: var(--crisp-fg-on-brand); font-family: inherit; font-size: var(--crisp-text-md); font-weight: var(--crisp-weight-medium); cursor: pointer; transition: background-color .12s; }
2786
3033
  & .crisp-recordstable-new:hover { background: var(--crisp-bg-brand-solid-pressed); }
3034
+
2787
3035
  /* scroll frame + table */
2788
3036
  & .crisp-recordstable-scroll { flex: 1; min-height: 0; overflow: auto; background: var(--crisp-bg-surface); border-top: 1px solid var(--crisp-border-subtle); scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--crisp-fg-primary) 22%, transparent) transparent; }
2789
3037
  & .crisp-recordstable-scroll::-webkit-scrollbar { width: 10px; height: 10px; }
@@ -2801,16 +3049,20 @@
2801
3049
  & .crisp-recordstable-sort { flex-shrink: 0; opacity: 0.4; transition: opacity .12s; color: color-mix(in srgb, var(--crisp-fg-primary) 45%, transparent); }
2802
3050
  & .crisp-recordstable-th:hover .crisp-recordstable-sort, & .crisp-recordstable-th[data-sorted] .crisp-recordstable-sort { opacity: 1; }
2803
3051
  & .crisp-recordstable-th[data-sorted="desc"] .crisp-recordstable-sort svg { transform: rotate(180deg); }
3052
+
2804
3053
  /* column resize handle */
2805
3054
  & .crisp-recordstable-resize { position: absolute; top: 0; inset-inline-end: -3px; width: 9px; height: 100%; cursor: col-resize; z-index: 6; touch-action: none; }
2806
3055
  & .crisp-recordstable-resize::after { content: ""; position: absolute; top: 0; inset-inline-start: 3px; width: 2px; height: 100%; background: var(--crisp-bg-brand-solid); opacity: 0; transition: opacity .12s; }
2807
3056
  & .crisp-recordstable-resize:hover::after, & .crisp-recordstable-resize[data-resizing]::after { opacity: 1; }
3057
+
2808
3058
  /* add-column header */
2809
3059
  & .crisp-recordstable-addcol { min-width: 44px; }
2810
3060
  & .crisp-recordstable-addcol button { display: flex; align-items: center; justify-content: center; width: 44px; height: 100%; border: none; background: none; color: var(--crisp-fg-muted); cursor: pointer; }
2811
3061
  & .crisp-recordstable-addcol button:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
2812
- /* cells */ /* font-weight is explicit because the first cell of each row is a th[scope=row] — the record's name is what the row IS — and a th
2813
- defaults to bold. Measured before the change and unchanged after: 400. */
3062
+
3063
+ /* cells */
3064
+ /* font-weight is explicit because the first cell of each row is a
3065
+ th[scope=row] — the record's name is what the row IS — and a th defaults to bold. Measured before the change and unchanged after: 400. */
2814
3066
  & .crisp-recordstable-td { font-size: var(--crisp-text-lg); font-weight: var(--crisp-weight-regular); color: var(--crisp-fg-primary); background: var(--crisp-bg-surface); cursor: default; }
2815
3067
  & .crisp-recordstable-td--editable { cursor: text; }
2816
3068
  /* keyboard/click cell selection (spreadsheet-style) */
@@ -2826,10 +3078,8 @@
2826
3078
  & .crisp-recordstable-td--picker .crisp-multiselect-chips .crisp-recordstable-tag { flex-shrink: 0; max-width: none; }
2827
3079
  & .crisp-recordstable-td--picker .crisp-multiselect-chevron { display: none; }
2828
3080
  & .crisp-recordstable-td--picker .crisp-multiselect-placeholder { color: var(--crisp-fg-muted); }
2829
- /* reference picker: a leading 16px avatar per option, plus a trailing
2830
- "Create X" row when the consumer supplies one (#449) -- measured live
2831
- against Attio's own picker (16px circular avatar, 16px plain icon for the create row, both sitting in the option's existing 8px gap, no new
2832
- gap value invented). */
3081
+ /* reference picker: a leading 16px avatar per option, plus a trailing "Create X" row when the consumer supplies one (#449) -- measured live against Attio's own picker (16px circular avatar, 16px plain icon for
3082
+ the create row, both sitting in the option's existing 8px gap, no new gap value invented). */
2833
3083
  & .crisp-recordstable-refoption { display: flex; align-items: center; gap: var(--crisp-space-2); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
2834
3084
  & .crisp-recordstable-refoption-avatar { flex-shrink: 0; }
2835
3085
  & .crisp-recordstable-refoption--create { color: var(--crisp-fg-muted); }
@@ -2857,46 +3107,33 @@
2857
3107
  & tbody tr:hover .crisp-recordstable-open { opacity: 1; }
2858
3108
  & .crisp-recordstable-link { color: var(--crisp-fg-primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
2859
3109
  & .crisp-recordstable-link:hover { text-decoration: underline; }
2860
-
2861
3110
  /* inline edit input */
2862
3111
  & .crisp-recordstable-edit { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0 var(--crisp-space-2); border: none; outline: 2px solid var(--crisp-bg-brand-solid); outline-offset: -2px; border-radius: var(--crisp-radius-sm); background: var(--crisp-bg-surface); font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); z-index: 5; }
2863
3112
  & .crisp-recordstable-edit--num { text-align: end; font-variant-numeric: tabular-nums; }
2864
-
2865
3113
  /* selection / hover / sticky */
2866
3114
  & .crisp-recordstable-col-check { width: 40px; min-width: 40px; padding: 0; }
2867
3115
  & .crisp-recordstable-check-inner { display: flex; align-items: center; justify-content: center; height: 100%; }
2868
3116
  & .crisp-recordstable-col-check, & .crisp-recordstable-col-entity { position: sticky; z-index: 3; background: var(--crisp-bg-surface); }
2869
- /* The sticky pin is a LOGICAL offset: with physical `left` these columns
2870
- were not pinned at all under RTL -- scrolled, they left the viewport
2871
- entirely (measured: viewport 13..773, check column at x=1333). Nothing in
2872
- JS measures these, so converting them is safe; the two properties below
2873
- that have no logical form get an explicit RTL rule rather than being
2874
- left to point the wrong way. */
3117
+ /* The sticky pin is a LOGICAL offset: with physical `left` these columns were not pinned at all under RTL -- scrolled, they left the viewport entirely (measured: viewport 13..773, check column at x=1333). Nothing in JS measures these, so converting them is safe; the two properties below that have no logical form get an explicit RTL rule rather than being left to point the wrong way. */
2875
3118
  & .crisp-recordstable-col-check { inset-inline-start: 0; }
2876
3119
  & .crisp-recordstable-col-entity { inset-inline-start: 40px; clip-path: inset(0 -38px 0 0); transition: box-shadow .2s ease; }
2877
3120
  [dir="rtl"] & .crisp-recordstable-col-entity { clip-path: inset(0 0 0 -38px); }
2878
3121
  & thead .crisp-recordstable-col-check, & thead .crisp-recordstable-col-entity { z-index: 4; }
2879
3122
  /* soft shadow on the sticky column edge once the grid is scrolled horizontally */
2880
3123
  & .crisp-recordstable-scroll[data-scrolled] .crisp-recordstable-col-entity { box-shadow: 6px 0 16px 4px rgba(0,0,0,0.12); }
2881
- /* box-shadow has no logical form either: the bleed follows the scrolled
2882
- content, which is the opposite side under RTL. */
3124
+ /* box-shadow has no logical form either: the bleed follows the scrolled content, which is the opposite side under RTL. */
2883
3125
  [dir="rtl"] & .crisp-recordstable-scroll[data-scrolled] .crisp-recordstable-col-entity { box-shadow: -6px 0 16px 4px rgba(0,0,0,0.12); }
2884
- /* AI-enriched values carry a purple wash. Measured live:
2885
- a full-bleed overlay at #9b69ff 4% (== bgPurple), composited OVER
2886
- whatever row-state background the cell already has, which is why it
2887
- is an ::after and not a background. */
3126
+ /* AI-enriched values carry a purple wash. Measured live: a full-bleed overlay at #9b69ff 4% (== bgPurple), composited OVER whatever row-state background the cell already has, which is why it is an ::after and not a background. */
2888
3127
  & .crisp-recordstable-td[data-enriched]::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: color-mix(in srgb, var(--crisp-bg-purple) 4%, transparent); }
2889
3128
  & tbody tr:hover .crisp-recordstable-td { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, var(--crisp-bg-surface)); }
2890
3129
  & tbody tr[data-selected] .crisp-recordstable-td { background: color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface)); }
2891
3130
  & tbody tr[data-selected]:hover .crisp-recordstable-td { background: color-mix(in srgb, var(--crisp-fg-primary) 4%, color-mix(in srgb, var(--crisp-bg-brand-solid) 10%, var(--crisp-bg-surface))); }
2892
3131
  & .crisp-recordstable-th:last-child, & .crisp-recordstable-td:last-child { border-inline-end: none; }
2893
-
2894
3132
  /* add-row footer */
2895
3133
  & .crisp-recordstable-addrow { display: flex; align-items: center; gap: var(--crisp-space-2); width: 100%; height: var(--crisp-size-tableRow); padding: 0 var(--crisp-space-2); border: none; border-top: 1px solid var(--crisp-border-subtle); background: var(--crisp-bg-surface); color: var(--crisp-fg-muted); font-family: inherit; font-size: var(--crisp-text-lg); cursor: pointer; }
2896
3134
  & .crisp-recordstable-addrow:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
2897
3135
  /* zero-row empty state — centred in the body region (headers + summary stay) */
2898
3136
  & .crisp-recordstable-empty { display: flex; align-items: center; justify-content: center; min-height: 320px; padding: var(--crisp-space-8) var(--crisp-space-4); }
2899
-
2900
3137
  /* summary footer — per-column aggregation, sticky to the bottom */
2901
3138
  & .crisp-recordstable-sum { position: sticky; bottom: 0; z-index: 2; height: var(--crisp-size-tableRow); padding: 0; border-top: 1px solid var(--crisp-border-subtle); border-bottom: 1px solid var(--crisp-border-subtle); border-inline-end: 1px solid var(--crisp-border-subtle); background: var(--crisp-bg-surface); }
2902
3139
  & tfoot .crisp-recordstable-col-check, & tfoot .crisp-recordstable-col-entity { position: sticky; bottom: 0; z-index: 3; }
@@ -2909,18 +3146,12 @@
2909
3146
  & .crisp-recordstable-pin { flex-shrink: 0; margin-inline-start: var(--crisp-space-1); color: var(--crisp-bg-warning); fill: color-mix(in srgb, var(--crisp-bg-warning) 35%, transparent); }
2910
3147
  & .crisp-recordstable-sum-btn[data-empty] { opacity: 0; justify-content: flex-start; }
2911
3148
  & .crisp-recordstable-sum-btn[data-empty]:hover { opacity: 1; }
2912
-
2913
3149
  /* bulk action bar lives in its own component now — see components/bulkbar */
2914
3150
  & .crisp-recordstable-bulk-sep { width: 1px; height: 20px; margin: 0 var(--crisp-space-0-5); background: var(--crisp-border-subtle); }
2915
3151
  & .crisp-recordstable-bulk-close { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; padding: 0; border: none; border-radius: var(--crisp-radius-md); background: transparent; color: var(--crisp-fg-muted); cursor: pointer; }
2916
3152
  & .crisp-recordstable-bulk-close:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
2917
-
2918
3153
  /* announced, never drawn */
2919
- & .crisp-recordstable-live {
2920
- position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
2921
- overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
2922
- }
2923
-
3154
+ & .crisp-recordstable-live { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
2924
3155
  [dir="rtl"] & [data-rtl-flip] { transform: scaleX(-1); }
2925
3156
  }
2926
3157
 
@@ -3016,6 +3247,21 @@
3016
3247
  .crisp-savedviews-panel[data-up] { transform-origin: bottom left; }
3017
3248
  .crisp-savedviews-panel { @starting-style { opacity: 0; transform: scale(0.97) translateY(-1px); } }
3018
3249
 
3250
+ /* #618 (P-007): the panel's own "Search views..." box — measured live:
3251
+ 32px tall, 6px/8px padding, NO border/background/leading icon of its
3252
+ own (unlike Select's own bordered searchable-input pill), 14px text.
3253
+ A hairline divider (borderSubtle, 1px, 3px top margin) separates it
3254
+ from the view list, matching the divider Attio's own kebab menu uses
3255
+ between its action items and the "Created by" footer below. */
3256
+ .crisp-savedviews-search {
3257
+ box-sizing: border-box; display: block; width: 100%; height: 32px; flex-shrink: 0;
3258
+ margin: 0; padding: var(--crisp-space-1-5) var(--crisp-space-2); border: none; background: none;
3259
+ font-family: inherit; font-size: var(--crisp-text-lg); color: var(--crisp-fg-primary); outline: none;
3260
+ }
3261
+ .crisp-savedviews-search::placeholder { color: var(--crisp-fg-muted); }
3262
+ .crisp-savedviews-search-sep { height: 1px; flex-shrink: 0; margin: 3px 0 0; background: var(--crisp-border-subtle); }
3263
+ .crisp-savedviews-empty { padding: var(--crisp-space-1-5) var(--crisp-space-2); font-size: var(--crisp-text-lg); color: var(--crisp-fg-muted); }
3264
+
3019
3265
  .crisp-savedviews-row {
3020
3266
  box-sizing: border-box; position: relative; display: flex; align-items: center; gap: var(--crisp-space-1-5);
3021
3267
  height: var(--crisp-size-menuRow); flex-shrink: 0; padding: var(--crisp-space-1-5) var(--crisp-space-2);
@@ -3047,6 +3293,18 @@
3047
3293
  .crisp-savedviews-row:focus-within .crisp-savedviews-kebab,
3048
3294
  .crisp-savedviews-kebab[data-open] { opacity: 1; }
3049
3295
  .crisp-savedviews-kebab:hover, .crisp-savedviews-kebab[data-open] { background: var(--crisp-bg-surface); color: var(--crisp-fg-primary); }
3296
+
3297
+ /* #618 (P-008): the kebab menu's own "Created by X" / date footer —
3298
+ measured live: 2 stacked lines, 6px/8px padding, 2px gap between
3299
+ them, 12px/500 text, BOTH lines the same color-mix 50% muted (a
3300
+ distinct value from the 63% mix #592's chip breadcrumb uses —
3301
+ independently measured, not assumed to match). */
3302
+ .crisp-savedviews-kebab-footer {
3303
+ display: flex; flex-direction: column; gap: 2px;
3304
+ padding: var(--crisp-space-1-5) var(--crisp-space-2);
3305
+ font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium);
3306
+ color: color-mix(in srgb, var(--crisp-fg-primary) 50%, transparent);
3307
+ }
3050
3308
 
3051
3309
  .crisp-savedviews {
3052
3310
  position: relative; display: inline-flex; font-family: var(--crisp-font-sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; /* Trigger fill measured rgb(246,247,247); bgRaised's light value is
@@ -3136,9 +3394,14 @@
3136
3394
  & .crisp-sidebar-section { display: flex; flex-direction: column; gap: var(--crisp-space-px); margin-top: var(--crisp-space-2); }
3137
3395
  & .crisp-sidebar-section[data-collapsed] { display: none; }
3138
3396
  & .crisp-sidebar-label { font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); padding: var(--crisp-space-1-5) var(--crisp-space-2) var(--crisp-space-0-5); }
3139
- & .crisp-sidebar-section-header { display: flex; align-items: center; gap: var(--crisp-space-1); width: 100%; margin: 0; border: none; background: none; padding: var(--crisp-space-1-5) var(--crisp-space-2) var(--crisp-space-0-5); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); cursor: pointer; }
3397
+ & .crisp-sidebar-section-header-row { display: flex; align-items: center; }
3398
+ & .crisp-sidebar-section-header { display: flex; align-items: center; gap: var(--crisp-space-1); flex: 1; min-width: 0; margin: 0; border: none; background: none; padding: var(--crisp-space-1-5) var(--crisp-space-2) var(--crisp-space-0-5); font-family: var(--crisp-font-sans); font-size: var(--crisp-text-sm); font-weight: var(--crisp-weight-medium); color: var(--crisp-fg-muted); cursor: pointer; }
3140
3399
  & .crisp-sidebar-section-header svg { flex-shrink: 0; transition: transform .15s ease; }
3141
3400
  & .crisp-sidebar-section-header[data-collapsed] svg { transform: rotate(-90deg); }
3401
+ & .crisp-sidebar-section-actions { display: flex; align-items: center; gap: var(--crisp-space-1); flex-shrink: 0; padding-inline-end: var(--crisp-space-1); opacity: 0; transition: opacity .12s ease; }
3402
+ & .crisp-sidebar-section-header-row:hover .crisp-sidebar-section-actions, & .crisp-sidebar-section-header-row:focus-within .crisp-sidebar-section-actions { opacity: 1; }
3403
+ & .crisp-sidebar-section-action { box-sizing: border-box; display: inline-flex; align-items: center; justify-content: center; width: var(--crisp-space-5); height: var(--crisp-space-5); margin: 0; padding: 0; border: none; border-radius: var(--crisp-radius-sm); background: transparent; color: color-mix(in srgb, var(--crisp-fg-primary) 63%, transparent); font-family: inherit; cursor: pointer; text-decoration: none; transition: background-color 0.14s, color 0.14s; }
3404
+ & .crisp-sidebar-section-action:hover { background: var(--crisp-bg-raised); color: var(--crisp-fg-primary); }
3142
3405
  & .crisp-sidebar-ws-row { display: flex; align-items: center; gap: var(--crisp-space-2); height: var(--crisp-size-header); padding: 0 var(--crisp-space-2); }
3143
3406
  & .crisp-sidebar-ws-menuroot { display: flex; flex: 1; min-width: 0; }
3144
3407
  & .crisp-sidebar-ws { box-sizing: border-box; display: flex; align-items: center; gap: var(--crisp-space-2); flex: 1; min-width: 0; height: 100%; margin: 0; padding: 0; border: none; background: none; border-radius: var(--crisp-radius-lg); font-family: inherit; font-size: inherit; color: inherit; text-align: start; cursor: pointer; transition: background-color 0.16s; }