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